From fb057ac80d50e6fc256288ddc5fa52ec40310311 Mon Sep 17 00:00:00 2001 From: Alexander Mahr Date: Tue, 31 Dec 2024 18:09:46 +0100 Subject: [PATCH] adopt rawdrawandroidRepo to make native app --- .gitignore | 1 + README.md | 73 +- .../make--AndroidManifest.xml | 6 +- app/.Makefile.scripts/make--android-sdk.sh | 45 +- app/.Makefile.scripts/make--app-config.sh | 3 +- app/Makefile | 90 +- app/assets/index.html | 69 +- app/res/drawable/appicon.xml | 6 + app/src/nativecode/LICENSE | 21 + app/src/nativecode/android_native_app_glue.c | 526 ++ app/src/nativecode/android_native_app_glue.h | 376 + app/src/nativecode/android_usb_devices.c | 210 + app/src/nativecode/android_usb_devices.h | 17 + app/src/nativecode/cnfa/.gitignore | 3 + app/src/nativecode/cnfa/CNFA.c | 128 + app/src/nativecode/cnfa/CNFA.h | 122 + app/src/nativecode/cnfa/CNFA_alsa.c | 374 + app/src/nativecode/cnfa/CNFA_android.c | 319 + app/src/nativecode/cnfa/CNFA_null.c | 46 + app/src/nativecode/cnfa/CNFA_pulse.c | 308 + app/src/nativecode/cnfa/CNFA_sun.c | 275 + app/src/nativecode/cnfa/CNFA_wasapi.c | 514 ++ app/src/nativecode/cnfa/CNFA_wasapi_utils.h | 636 ++ app/src/nativecode/cnfa/CNFA_winmm.c | 258 + app/src/nativecode/cnfa/LICENSE | 21 + app/src/nativecode/cnfa/Makefile | 34 + app/src/nativecode/cnfa/README.md | 75 + app/src/nativecode/cnfa/example.c | 65 + app/src/nativecode/cnfa/wave_player/Makefile | 27 + app/src/nativecode/cnfa/wave_player/wavDefs.h | 117 + .../nativecode/cnfa/wave_player/wav_player.c | 389 + .../.github/workflows/github-actions.yml | 41 + app/src/nativecode/rawdraw/.gitignore | 17 + app/src/nativecode/rawdraw/CNFG.c | 28 + app/src/nativecode/rawdraw/CNFG.h | 545 ++ app/src/nativecode/rawdraw/CNFG3D.c | 542 ++ app/src/nativecode/rawdraw/CNFGAndroid.h | 217 + app/src/nativecode/rawdraw/CNFGEGLDriver.c | 866 ++ .../nativecode/rawdraw/CNFGEGLLeanAndMean.c | 137 + app/src/nativecode/rawdraw/CNFGFunctions.c | 959 ++ app/src/nativecode/rawdraw/CNFGHTTP.c | 2017 ++++ app/src/nativecode/rawdraw/CNFGNullDriver.c | 70 + app/src/nativecode/rawdraw/CNFGOGLEGLDriver.c | 189 + app/src/nativecode/rawdraw/CNFGRasterizer.c | 341 + app/src/nativecode/rawdraw/CNFGWASMDriver.c | 84 + app/src/nativecode/rawdraw/CNFGWinDriver.c | 681 ++ app/src/nativecode/rawdraw/CNFGXDriver.c | 684 ++ app/src/nativecode/rawdraw/LICENSE | 23 + app/src/nativecode/rawdraw/Makefile | 69 + app/src/nativecode/rawdraw/README.md | 229 + app/src/nativecode/rawdraw/chew.c | 83 + app/src/nativecode/rawdraw/chew.h | 184 + app/src/nativecode/rawdraw/chewtypes.h | 118 + app/src/nativecode/rawdraw/examples/Makefile | 27 + .../nativecode/rawdraw/examples/fontsize.c | 77 + app/src/nativecode/rawdraw/ogltest.c | 56 + app/src/nativecode/rawdraw/os_generic.h | 520 ++ app/src/nativecode/rawdraw/osdtest.c | 153 + app/src/nativecode/rawdraw/rawdraw.c | 293 + app/src/nativecode/rawdraw/rawdraw_sf.h | 8156 +++++++++++++++++ app/src/nativecode/rawdraw/simple.c | 62 + app/src/nativecode/rawdraw/tccbuild.bat | 3 + .../rawdraw/tools/binary_to_buffer.c | 24 + .../tools/rawdraw_http_files/index.html | 546 ++ .../rawdraw/tools/single_file_creator.c | 86 + app/src/nativecode/rawdraw/wasm/.gitignore | 2 + app/src/nativecode/rawdraw/wasm/Makefile | 46 + app/src/nativecode/rawdraw/wasm/README.md | 93 + .../rawdraw/wasm/build-wasm-index.ps1 | 83 + app/src/nativecode/rawdraw/wasm/rawdraw.c | 276 + app/src/nativecode/rawdraw/wasm/subst.c | 206 + app/src/nativecode/rawdraw/wasm/template.ht | 23 + app/src/nativecode/rawdraw/wasm/template.js | 314 + app/src/nativecode/test.c | 663 ++ app/src/nativecode/webview_native_activity.h | 312 + build-android-app.sh | 9 +- 76 files changed, 25162 insertions(+), 146 deletions(-) create mode 100644 app/src/nativecode/LICENSE create mode 100644 app/src/nativecode/android_native_app_glue.c create mode 100644 app/src/nativecode/android_native_app_glue.h create mode 100644 app/src/nativecode/android_usb_devices.c create mode 100644 app/src/nativecode/android_usb_devices.h create mode 100644 app/src/nativecode/cnfa/.gitignore create mode 100644 app/src/nativecode/cnfa/CNFA.c create mode 100644 app/src/nativecode/cnfa/CNFA.h create mode 100644 app/src/nativecode/cnfa/CNFA_alsa.c create mode 100644 app/src/nativecode/cnfa/CNFA_android.c create mode 100644 app/src/nativecode/cnfa/CNFA_null.c create mode 100644 app/src/nativecode/cnfa/CNFA_pulse.c create mode 100644 app/src/nativecode/cnfa/CNFA_sun.c create mode 100644 app/src/nativecode/cnfa/CNFA_wasapi.c create mode 100644 app/src/nativecode/cnfa/CNFA_wasapi_utils.h create mode 100644 app/src/nativecode/cnfa/CNFA_winmm.c create mode 100644 app/src/nativecode/cnfa/LICENSE create mode 100644 app/src/nativecode/cnfa/Makefile create mode 100644 app/src/nativecode/cnfa/README.md create mode 100644 app/src/nativecode/cnfa/example.c create mode 100644 app/src/nativecode/cnfa/wave_player/Makefile create mode 100644 app/src/nativecode/cnfa/wave_player/wavDefs.h create mode 100644 app/src/nativecode/cnfa/wave_player/wav_player.c create mode 100644 app/src/nativecode/rawdraw/.github/workflows/github-actions.yml create mode 100644 app/src/nativecode/rawdraw/.gitignore create mode 100644 app/src/nativecode/rawdraw/CNFG.c create mode 100644 app/src/nativecode/rawdraw/CNFG.h create mode 100644 app/src/nativecode/rawdraw/CNFG3D.c create mode 100644 app/src/nativecode/rawdraw/CNFGAndroid.h create mode 100644 app/src/nativecode/rawdraw/CNFGEGLDriver.c create mode 100644 app/src/nativecode/rawdraw/CNFGEGLLeanAndMean.c create mode 100644 app/src/nativecode/rawdraw/CNFGFunctions.c create mode 100644 app/src/nativecode/rawdraw/CNFGHTTP.c create mode 100644 app/src/nativecode/rawdraw/CNFGNullDriver.c create mode 100644 app/src/nativecode/rawdraw/CNFGOGLEGLDriver.c create mode 100644 app/src/nativecode/rawdraw/CNFGRasterizer.c create mode 100644 app/src/nativecode/rawdraw/CNFGWASMDriver.c create mode 100644 app/src/nativecode/rawdraw/CNFGWinDriver.c create mode 100644 app/src/nativecode/rawdraw/CNFGXDriver.c create mode 100644 app/src/nativecode/rawdraw/LICENSE create mode 100644 app/src/nativecode/rawdraw/Makefile create mode 100644 app/src/nativecode/rawdraw/README.md create mode 100644 app/src/nativecode/rawdraw/chew.c create mode 100644 app/src/nativecode/rawdraw/chew.h create mode 100644 app/src/nativecode/rawdraw/chewtypes.h create mode 100644 app/src/nativecode/rawdraw/examples/Makefile create mode 100644 app/src/nativecode/rawdraw/examples/fontsize.c create mode 100644 app/src/nativecode/rawdraw/ogltest.c create mode 100644 app/src/nativecode/rawdraw/os_generic.h create mode 100644 app/src/nativecode/rawdraw/osdtest.c create mode 100644 app/src/nativecode/rawdraw/rawdraw.c create mode 100644 app/src/nativecode/rawdraw/rawdraw_sf.h create mode 100644 app/src/nativecode/rawdraw/simple.c create mode 100644 app/src/nativecode/rawdraw/tccbuild.bat create mode 100644 app/src/nativecode/rawdraw/tools/binary_to_buffer.c create mode 100644 app/src/nativecode/rawdraw/tools/rawdraw_http_files/index.html create mode 100644 app/src/nativecode/rawdraw/tools/single_file_creator.c create mode 100644 app/src/nativecode/rawdraw/wasm/.gitignore create mode 100644 app/src/nativecode/rawdraw/wasm/Makefile create mode 100644 app/src/nativecode/rawdraw/wasm/README.md create mode 100644 app/src/nativecode/rawdraw/wasm/build-wasm-index.ps1 create mode 100644 app/src/nativecode/rawdraw/wasm/rawdraw.c create mode 100644 app/src/nativecode/rawdraw/wasm/subst.c create mode 100644 app/src/nativecode/rawdraw/wasm/template.ht create mode 100644 app/src/nativecode/rawdraw/wasm/template.js create mode 100644 app/src/nativecode/test.c create mode 100644 app/src/nativecode/webview_native_activity.h diff --git a/.gitignore b/.gitignore index 556cceb..52c570b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ !/app/obj/.gitkeep /app/app.apk /app/Makefile.app-config +/app/output.map diff --git a/README.md b/README.md index 08438b0..6ccea5c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ -# Android App (build via docker/podman) +# Android App Written in C (build via docker/podman) -The goal of this repo, is to create a container that can serve to produce an "empty android Application" (i.e `app.apk` file). -As such the philosophy is to keep the process "simple" as to now make the understanding too difficult. +This repo provides its user with the ability to setup and compile a Android-App that is +based on `android.app.NativeActiviy` and thus can be completely compiled C source-code. + +It is an adoptation (and hopefully somehow simplification of this repo [`rawdrawandroid`](https://github.com/cnlohr/rawdrawandroid) +and this many thanks and credits to most of the c-code goes to CNLohr. ## usage @@ -12,7 +15,7 @@ As such the philosophy is to keep the process "simple" as to now make the unders ./build-android-app.sh ``` -2. follow the configuration +2. follow the configuration (configuration is less than in the normal case of [android-app](https://git.alexmahr.de/lion/android-app) as the API-Level is inserted fixed as "29" ) 3. upon success the apk file created is in app/result/app.apk and can be installed via `adb` ``` adb install -r app/result/app.apk @@ -22,6 +25,7 @@ adb install -r app/result/app.apk * work within container (debian based image) * use Makefile as a build tool +* avoid the "unhappiness" of having to deal with neither KOTLIN nor JAVA ## benefits of this (compared to AndroidStudio) @@ -31,63 +35,4 @@ adb install -r app/result/app.apk * oftentimes faster compile time (as compared with AndroidStudio Gradle builds) * quick "webview" which can serve as starting point for people that can to PWA and websites * Assisted initial configuration provides access to configure almost all types of [Android app permissions](app/.Makefile.scripts/make--app-config.sh#L127) -* no need to have Kotlin, Gradle setup -/ -## basic info - -This repo should allow to generate an empty "android app". by simply cloning this repo and -``` -./build-android-app.sh -``` -It does so via: - -1. building a container (in any of the runtime/daemons it finds: i.e. docker,podman,etc..) -2. running this container having the `./app` folder being mounted within as `/app` -3. executing the [`app/Makefile`](app/Makefile) which will then: -4. either work with the configuration stored in an `app/app-config.sh` in case such file exists or -5. if not go through a `whiptail` text menu wizzard to configure a new empty app. (Makefile recipe: `./app-config.sh`) -6. it will then download the required android sdk files as necessary (Makefile recipe: `./android-sdk/installed`) -7. go through the further steps to setup the blank app. - - - -## files and purpose - -Upon `clone` of this repo the `app` folder is setup with these files: -``` -# The (GNU) Makefile which... -app/Makefile -# ... has recipes that call scripts in folder.... -app/.Makefile.scripts -# .. which creates an `app-config.sh`, a file to keep -# the configuration (app name,lable,api-levels,permissions etc)... -app/.Makefile.scripts/make--app-config.sh -# .. which creates an `AndroidManifest.xml` -app/.Makefile.scripts/make--AndroidManifest.xml -# .. which creates an `AppActivity.java` file (which just setup a Webview and loads `assets/index.html`) -app/.Makefile.scripts/make--AppActivity.java.sh -# .. which installs the necessary Android SDK in the correct versions -app/.Makefile.scripts/make--android-sdk.sh -app/assets -# the index.html file -app/assets/index.html -app/res -app/res/drawable -# the icon of the app -app/res/drawable/appicon.xml -``` - -Upon further `./build-android-app.sh` execution more folders will appear -``` -# a folder in which the Android-sdk stuff (installed via sdkmanager) is stored -android-sdk -# folders used during build... -# ... for temporary files -bin/ -obj/ -result/ -# app configuration resulting from text whiptail menu -app-config.sh -# the Manifest file as resulted from data from app-config.sh -AndroidManifest.xml -``` +* no need to have Kotlin, Gradle setup, nor use JAVA diff --git a/app/.Makefile.scripts/make--AndroidManifest.xml b/app/.Makefile.scripts/make--AndroidManifest.xml index 179d48f..66c1391 100755 --- a/app/.Makefile.scripts/make--AndroidManifest.xml +++ b/app/.Makefile.scripts/make--AndroidManifest.xml @@ -13,11 +13,12 @@ cat > "AndroidManifest.xml" << ANDROIDMANIFEST $(sed 's/^/ /' <<< "$APP_PERMISSIONS") - - + + @@ -26,4 +27,3 @@ $(sed 's/^/ /' <<< "$APP_PERMISSIONS") ANDROIDMANIFEST - diff --git a/app/.Makefile.scripts/make--android-sdk.sh b/app/.Makefile.scripts/make--android-sdk.sh index 1b14330..e3810be 100755 --- a/app/.Makefile.scripts/make--android-sdk.sh +++ b/app/.Makefile.scripts/make--android-sdk.sh @@ -34,46 +34,7 @@ type sdkmanager 2>/dev/null || ( mv -v cmdline-tools "$BUILD_TOOLS_LATEST" || true ) -test -f android-sdk/.installed.buildtools.version.$APP_VERSION_SDK_TARGET || { - EXACTVERSION_BUILDTOOLS="$( - sdkmanager --list 2>/dev/null | - sed 's/^ *//' | - grep -e 'build-tools;'"$APP_VERSION_SDK_TARGET"'\.[0-9]*\.[0-9]*\ ' | - cut -f1 -d' ' | - sort | - tail -n 1 - )" - test -z "$EXACTVERSION_BUILDTOOLS" && { - echo "ERROR cannot get '$EXACTVERSION_BUILDTOOLS'" >&2 - echo "dropping to shell..." >&2 - exec bash - } - sdkmanager --list_installed | grep -q "$EXACTVERSION_BUILDTOOLS" || { - sdkmanager --install "$EXACTVERSION_BUILDTOOLS" - } - echo "$EXACTVERSION_BUILDTOOLS" > android-sdk/.installed.buildtools.version.$APP_VERSION_SDK_TARGET - ln -rvsf android-sdk/.installed.buildtools.version.{$APP_VERSION_SDK_TARGET,current} +test -f android-sdk/installed || { + sdkmanager --install "build-tools;29.0.3" "cmake;3.10.2.4988404" "ndk;21.1.6352462" "platform-tools" "platforms;android-29" "tools" && + touch android-sdk/installed || exit 1 } - -test -f android-sdk/.installed.platforms.version.$APP_VERSION_SDK_TARGET || { - EXACTVERSION_PLATFORM="$( - sdkmanager --list 2>/dev/null | - sed 's/^ *//' | - grep -e 'platforms;android-'"$APP_VERSION_SDK_TARGET"'\ ' | - cut -f1 -d' ' | - sort | - tail -n 1 - )" - test -z "$EXACTVERSION_PLATFORM" && { - echo "ERROR cannot get '$EXACTVERSION_PLATFORM'" >&2 - echo "dropping to shell..." >&2 - exec bash - } - sdkmanager --list_installed | grep -q "$EXACTVERSION_PLATFORM" || { - sdkmanager --install "$EXACTVERSION_PLATFORM" - } - echo "$EXACTVERSION_PLATFORM" > android-sdk/.installed.platforms.version.$APP_VERSION_SDK_TARGET - ln -rvsf android-sdk/.installed.platforms.version.{$APP_VERSION_SDK_TARGET,current} -} - -touch android-sdk/installed diff --git a/app/.Makefile.scripts/make--app-config.sh b/app/.Makefile.scripts/make--app-config.sh index 7a497f0..8732e46 100755 --- a/app/.Makefile.scripts/make--app-config.sh +++ b/app/.Makefile.scripts/make--app-config.sh @@ -3,7 +3,8 @@ test -f app-config.sh && { source app-config.sh } - +APP_VERSION_SDK_TARGET='29' +APP_VERSION_SDK_MIN='29' set -e diff --git a/app/Makefile b/app/Makefile index a8041f1..b7fe88d 100644 --- a/app/Makefile +++ b/app/Makefile @@ -9,48 +9,88 @@ SHELL=/bin/makefile-bash-wrapper.sh # (as explained https://make.mad-scientist.net/papers/advanced-auto-dependency-generation/#include) include Makefile.app-config +# litanay of variabes used +NDK:=./android-sdk/ndk/21.1.6352462 +NDK_TOOLCHAIN:=$(NDK)/toolchains/llvm/prebuilt/linux-x86_64 +NDK_TOOLCHAIN_INCLUDE:=$(NDK_TOOLCHAIN)/sysroot/usr/include +ANDROIDVERSION:=29 +CC_ARM64:=$(NDK_TOOLCHAIN)/bin/aarch64-linux-android29-clang +CFLAGS?=-ffunction-sections -Os -fdata-sections -Wall -fvisibility=hidden +CFLAGS += -fvisibility=hidden +# APP_PACKAGE is set to (-D) i.e preprocessor value and used to set string when logging +CFLAGS+=-Os -DANDROID -DAPP_PACKAGE=\"$(APP_PACKAGE)\" +# the -D will likely set a Variable to be used in the Preprocessor +CFLAGS +=-DANDROID_FULLSCREEN +CFLAGS+= -I./src/nativecode/rawdraw -I./src/nativecode -I$(NDK)/sysroot/usr/include +CFLAGS+= -I$(NDK)/sysroot/usr/include/android -I$(NDK_TOOLCHAIN_INCLUDE) -I$(NDK_TOOLCHAIN_INCLUDE)/android +CFLAGS+= -fPIC -DANDROIDVERSION=$(ANDROIDVERSION) +CFLAGS_ARM64:=-m64 +LDFLAGS?=-Wl,--gc-sections -Wl,-Map=output.map -s +LDFLAGS += -s +# LDFLAGS += -static-libstdc++ +LDFLAGS += -lm -lGLESv3 -lEGL -landroid -llog -lOpenSLES +LDFLAGS += -shared -uANativeActivity_onCreate +# locatoins of "mostly c" source files +SRC?=./src/nativecode/test.c +RAWDRAWANDROIDSRCS=./src/nativecode/android_native_app_glue.c +ANDROIDSRCS:= $(SRC) $(RAWDRAWANDROIDSRCS) + + # symlink ./app.apk: ./result/app.apk ln -sfrv ./result/app.apk ./app.apk # zipalign and sign again (second signing) -./result/app.apk : ./result/signed.apk app-config.sh ./keystore +./result/app.apk : ./result/unsigned.apk app-config.sh ./keystore $(BUILDTOOLS)/zipalign -v -f 4 $< $@ $(BUILDTOOLS)/apksigner sign --ks keystore --key-pass pass:armena --ks-pass pass:armena $@ -# sign the apk file (first sign) -./result/signed.apk : ./result/unsigned.apk ./keystore - jarsigner -verbose -keystore ./keystore -storepass armena -keypass armena -signedjar $@ $< helljniKey +## sign the apk file (first sign) +#./result/signed.apk : ./result/unsigned.apk ./keystore +# jarsigner -verbose -keystore ./keystore -storepass armena -keypass armena -signedjar $@ $< helljniKey # make a "keystore" for the cryptographic signing stuff ./keystore : - keytool -genkeypair -validity 1000 -dname "CN=alexander,O=Android,C=JPN" -keystore $@ \ + keytool -genkeypair -validity 2000 -dname "CN=$(APP_PACKAGE),O=Android,C=IT" -keystore $@ \ -storepass armena -keypass armena -alias helljniKey -keyalg RSA -v # aapt "package" together the dalvik/hex stuff (and "assets" and "res") -./result/unsigned.apk : ./result/bin/classes.dex ./assets ./AndroidManifest.xml +./result/unsigned.apk : ./assets ./AndroidManifest.xml ./result/bin/lib/arm64-v8a/libnativecode.so $(shell find ./res -type f) rm -rvf "$@" $(BUILDTOOLS)/aapt package \ -v -u -f -M ./AndroidManifest.xml -S ./res \ -I $(ANDROID_JAR) -A ./assets -F $@ ./result/bin -# convert "java class"es files (i.e bytecode) to dalvic/d8 android thing -./result/bin/classes.dex : ./obj/$(PACKAGE)/AppActivity.class - mkdir -p ./result/bin - $(BUILDTOOLS)/d8 ./obj/$(PACKAGE)/*.class \ - --lib $(ANDROID_JAR) --output ./result/bin +supi: + echo $(shell find ./res -type f) -# compile (javac) the class from -./obj/$(PACKAGE)/AppActivity.class : ./src/$(PACKAGE)/AppActivity.java ./src/$(PACKAGE)/R.java - mkdir -p ./obj/$(PACKAGE) - javac -d ./obj -classpath $(ANDROID_JAR) -sourcepath ./src $< +alex: ./result/bin/classes.dex + echo $< > $@ + +## convert "java class"es files (i.e bytecode) to dalvic/d8 android thing +#./result/bin/classes.dex : ./obj/$(PACKAGE)/AppActivity.class +# mkdir -p ./result/bin +# $(BUILDTOOLS)/d8 ./obj/$(PACKAGE)/*.class \ +# --lib $(ANDROID_JAR) --output ./result/bin; RESULT=$$?; echo result was $$RESULT; (exit $$RESULT) -# make the resources "R.java" thing -./src/$(PACKAGE)/R.java : $(shell find ./res -type f) app-config.sh ./AndroidManifest.xml ./android-sdk/installed | ./src/$(PACKAGE) - $(BUILDTOOLS)/aapt package \ - -v -f -m -S ./res -J ./src -M ./AndroidManifest.xml \ - -I $(ANDROID_JAR) +./result/bin/lib/arm64-v8a/libnativecode.so: $(ANDROIDSRCS) + mkdir -p "$$(dirname "$@")" + $(CC_ARM64) $(CFLAGS) $(CFLAGS_ARM64) -o $@ $^ -L$(NDK_TOOLCHAIN)/sysroot/usr/lib/aarch64-linux-android/$(ANDROIDVERSION) $(LDFLAGS) + + + +## compile (javac) the class from +#./obj/$(PACKAGE)/AppActivity.class : ./src/$(PACKAGE)/AppActivity.java ./src/$(PACKAGE)/R.java +# mkdir -p ./obj/$(PACKAGE) +# javac -d ./obj -classpath $(ANDROID_JAR) -sourcepath ./src $< +# +# +## make the resources "R.java" thing +#./src/$(PACKAGE)/R.java : $(shell find ./res -type f) app-config.sh ./AndroidManifest.xml ./android-sdk/installed | ./src/$(PACKAGE) +# $(BUILDTOOLS)/aapt package \ +# -v -f -m -S ./res -J ./src -M ./AndroidManifest.xml \ +# -I $(ANDROID_JAR) # generate the AppActivity.java (template # the "|" denotes an "order-only" prerequiste (as in https://stackoverflow.com/a/58040049/1711186) @@ -61,19 +101,21 @@ include Makefile.app-config mkdir -p $@ # install the necessary android sdks -./android-sdk/installed: app-config.sh +./android-sdk/installed: app-config.sh ./.Makefile.scripts/make--android-sdk.sh ./.Makefile.scripts/make--android-sdk.sh # generate the AndroidManifest.xml -./AndroidManifest.xml: app-config.sh +./AndroidManifest.xml: app-config.sh ./.Makefile.scripts/make--AndroidManifest.xml ./.Makefile.scripts/make--AndroidManifest.xml Makefile.app-config: app-config.sh Makefile source app-config.sh; \ tee $@ << MAKEFILE_APP_CONFIG - BUILDTOOLS:=$${ANDROID_SDK_ROOT}/$$(tr ';' '/' < android-sdk/.installed.buildtools.version.current) - ANDROID_JAR:=$${ANDROID_SDK_ROOT}/$$(tr ';' '/' < android-sdk/.installed.platforms.version.current)/android.jar + ANDROIDVERSION:=$$APP_VERSION_SDK_TARGET; + BUILDTOOLS:=$${ANDROID_SDK_ROOT}/build-tools/29.0.3/ + ANDROID_JAR:=$${ANDROID_SDK_ROOT}/platforms/android-29/android.jar PACKAGE:=$$(echo "$$APP_PACKAGE" | tr '.' '/') + APP_PACKAGE=$${APP_PACKAGE} MAKEFILE_APP_CONFIG # use whiptail textgui to make configuration (android API level, app-name, app-label etc...) diff --git a/app/assets/index.html b/app/assets/index.html index 1c1ad7c..b99d44a 100644 --- a/app/assets/index.html +++ b/app/assets/index.html @@ -18,23 +18,72 @@

Webview

+ Test page +
+
+
+
+ + diff --git a/app/res/drawable/appicon.xml b/app/res/drawable/appicon.xml index 2517c9e..b2d4a1b 100644 --- a/app/res/drawable/appicon.xml +++ b/app/res/drawable/appicon.xml @@ -6,4 +6,10 @@ + + diff --git a/app/src/nativecode/LICENSE b/app/src/nativecode/LICENSE new file mode 100644 index 0000000..eb1bef0 --- /dev/null +++ b/app/src/nativecode/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 <>< CNLohr + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/app/src/nativecode/android_native_app_glue.c b/app/src/nativecode/android_native_app_glue.c new file mode 100644 index 0000000..35bda3a --- /dev/null +++ b/app/src/nativecode/android_native_app_glue.c @@ -0,0 +1,526 @@ +/* + * Copyright (C) 2010 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 +#include +#include +#include + +#include "android_native_app_glue.h" +#include +#include + +#include "webview_native_activity.h" +struct android_app * gapp; + +#define LOGI(...) ((void)printf(__VA_ARGS__)) +#define LOGE(...) ((void)printf(__VA_ARGS__)) + +/* For debug builds, always enable the debug traces in this library */ + +#ifndef NDEBUG +# define LOGV(...) ((void)printf(__VA_ARGS__)) +#else +# define LOGV(...) ((void)0) +#endif + + +typedef struct __attribute__((packed)) +{ + void (*callback)( void * ); + void * opaque; +} MainThreadCallbackProps; + +static int pfd[2]; +pthread_t debug_capture_thread; +static void * debug_capture_thread_fn( void * v ) +{ + //struct android_app * app = (struct android_app*)v; + ssize_t readSize; + char buf[2048]; + + while((readSize = read(pfd[0], buf, sizeof buf - 1)) > 0) { + if(buf[readSize - 1] == '\n') { + --readSize; + } + buf[readSize] = 0; // add null-terminator + __android_log_write(ANDROID_LOG_DEBUG, APP_PACKAGE, buf); // Set any log level you want +#ifdef RDALOGFNCB + extern void RDALOGFNCB( int size, char * buf ); + RDALOGFNCB( readSize, buf ); +#endif + //if( debug_capture_hook_function ) debug_capture_hook_function( readSize, buf ); + } + return 0; +} + +static void free_saved_state(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + if (android_app->savedState != NULL) { + free(android_app->savedState); + android_app->savedState = NULL; + android_app->savedStateSize = 0; + } + pthread_mutex_unlock(&android_app->mutex); +} + +int8_t android_app_read_cmd(struct android_app* android_app) { + int8_t cmd; + if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) { + switch (cmd) { + case APP_CMD_SAVE_STATE: + free_saved_state(android_app); + break; + } + return cmd; + } else { + LOGE("No data on command pipe!"); + } + return -1; +} + +static void print_cur_config(struct android_app* android_app) { + //For additional debugging this can be enabled, but for now - no need for the extra space. +/* + char lang[2], country[2]; + AConfiguration_getLanguage(android_app->config, lang); + AConfiguration_getCountry(android_app->config, country); + + LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d " + "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d " + "modetype=%d modenight=%d", + AConfiguration_getMcc(android_app->config), + AConfiguration_getMnc(android_app->config), + lang[0], lang[1], country[0], country[1], + AConfiguration_getOrientation(android_app->config), + AConfiguration_getTouchscreen(android_app->config), + AConfiguration_getDensity(android_app->config), + AConfiguration_getKeyboard(android_app->config), + AConfiguration_getNavigation(android_app->config), + AConfiguration_getKeysHidden(android_app->config), + AConfiguration_getNavHidden(android_app->config), + AConfiguration_getSdkVersion(android_app->config), + AConfiguration_getScreenSize(android_app->config), + AConfiguration_getScreenLong(android_app->config), + AConfiguration_getUiModeType(android_app->config), + AConfiguration_getUiModeNight(android_app->config)); +*/ +} + +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_INPUT_CHANGED: + LOGV("APP_CMD_INPUT_CHANGED\n"); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + android_app->inputQueue = android_app->pendingInputQueue; + if (android_app->inputQueue != NULL) { + LOGV("Attaching input queue to looper"); + AInputQueue_attachLooper(android_app->inputQueue, + android_app->looper, LOOPER_ID_INPUT, NULL, + &android_app->inputPollSource); + } + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_INIT_WINDOW: + LOGV("APP_CMD_INIT_WINDOW\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = android_app->pendingWindow; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_TERM_WINDOW: + LOGV("APP_CMD_TERM_WINDOW\n"); + pthread_cond_broadcast(&android_app->cond); + break; + + case APP_CMD_RESUME: + case APP_CMD_START: + case APP_CMD_PAUSE: + case APP_CMD_STOP: + LOGV("activityState=%d\n", cmd); + pthread_mutex_lock(&android_app->mutex); + android_app->activityState = cmd; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_CONFIG_CHANGED: + LOGV("APP_CMD_CONFIG_CHANGED\n"); + AConfiguration_fromAssetManager(android_app->config, + android_app->activity->assetManager); + print_cur_config(android_app); + break; + + case APP_CMD_DESTROY: + LOGV("APP_CMD_DESTROY\n"); + android_app->destroyRequested = 1; + break; + } +} + +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_TERM_WINDOW: + LOGV("APP_CMD_TERM_WINDOW\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = NULL; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_SAVE_STATE: + LOGV("APP_CMD_SAVE_STATE\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->stateSaved = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_RESUME: + free_saved_state(android_app); + break; + } +} + +void app_dummy() { + +} + +static void android_app_destroy(struct android_app* android_app) { + LOGV("android_app_destroy!"); + free_saved_state(android_app); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + AConfiguration_delete(android_app->config); + android_app->destroyed = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + // Can't touch android_app object after this. +} + +static void process_input(struct android_app* app, struct android_poll_source* source) { + AInputEvent* event = NULL; + while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) { + //LOGV("New input event: type=%d\n", AInputEvent_getType(event)); + if (AInputQueue_preDispatchEvent(app->inputQueue, event)) { + continue; + } + int32_t handled = 0; + if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event); + AInputQueue_finishEvent(app->inputQueue, event, handled); + } +} + +static int process_ui( int dummy1, int dummy2, void * dummy3 ) { + // Can't trust parameters in UI thread callback. + MainThreadCallbackProps rep; + read(gapp->uimsgread, &rep, sizeof(rep)); + rep.callback( rep.opaque ); + return 1; +} + +static void process_cmd(struct android_app* app, struct android_poll_source* source) { + int8_t cmd = android_app_read_cmd(app); + android_app_pre_exec_cmd(app, cmd); + if (app->onAppCmd != NULL) app->onAppCmd(app, cmd); + android_app_post_exec_cmd(app, cmd); +} + +static void* android_app_entry(void* param) { + struct android_app* android_app = (struct android_app*)param; + + android_app->config = AConfiguration_new(); + AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager); + + print_cur_config(android_app); + android_app->cmdPollSource.id = LOOPER_ID_MAIN; + android_app->cmdPollSource.app = android_app; + android_app->cmdPollSource.process = process_cmd; + android_app->inputPollSource.id = LOOPER_ID_INPUT; + android_app->inputPollSource.app = android_app; + android_app->inputPollSource.process = process_input; + + ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); + ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL, &android_app->cmdPollSource); + android_app->looper = looper; + + pthread_mutex_lock(&android_app->mutex); + android_app->running = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + + android_main(android_app); + + android_app_destroy(android_app); + return NULL; +} + +// -------------------------------------------------------------------- +// Native activity interaction (called from main thread) +// -------------------------------------------------------------------- + +static struct android_app* android_app_create(ANativeActivity* activity, + void* savedState, size_t savedStateSize) { + struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app)); + memset(android_app, 0, sizeof(struct android_app)); + android_app->activity = activity; + + pthread_mutex_init(&android_app->mutex, NULL); + pthread_cond_init(&android_app->cond, NULL); + + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + //Capture input + setvbuf(stdout, 0, _IOLBF, 0); // make stdout line-buffered + setvbuf(stderr, 0, _IONBF, 0); // make stderr unbuffered + pipe(pfd); + dup2(pfd[1], 1); + dup2(pfd[1], 2); + pthread_create(&debug_capture_thread, &attr, debug_capture_thread_fn, android_app); + + if (savedState != NULL) { + android_app->savedState = malloc(savedStateSize); + android_app->savedStateSize = savedStateSize; + memcpy(android_app->savedState, savedState, savedStateSize); + } + + int msgpipe[2]; + if (pipe(msgpipe)) { + LOGE("could not create pipe: %s", strerror(errno)); + return NULL; + } + android_app->msgread = msgpipe[0]; + android_app->msgwrite = msgpipe[1]; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Handle calling events on the UI thread. You can get callbacks with RunCallbackOnUIThread. + int msgpipemain[2]; + if (pipe(msgpipemain)) { + LOGE("could not create pipe: %s", strerror(errno)); + return NULL; + } + android_app->uimsgread = msgpipemain[0]; + android_app->uimsgwrite = msgpipemain[1]; + ALooper * looper = ALooper_forThread(); + ALooper_addFd(looper, android_app->uimsgread, LOOPER_ID_MAIN_THREAD, ALOOPER_EVENT_INPUT, process_ui, gapp); //NOTE: Cannot use NULL callback + android_app->looperui = looper; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&android_app->thread, &attr, android_app_entry, android_app); + + // Wait for thread to start. + pthread_mutex_lock(&android_app->mutex); + while (!android_app->running) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + return android_app; +} + +static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { + if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { + LOGE("Failure writing android_app cmd: %s\n", strerror(errno)); + } +} + +static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) { + pthread_mutex_lock(&android_app->mutex); + android_app->pendingInputQueue = inputQueue; + android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); + while (android_app->inputQueue != android_app->pendingInputQueue) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) { + pthread_mutex_lock(&android_app->mutex); + if (android_app->pendingWindow != NULL) { + android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW); + } + android_app->pendingWindow = window; + if (window != NULL) { + android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW); + } + while (android_app->window != android_app->pendingWindow) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, cmd); + while (android_app->activityState != cmd) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_free(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, APP_CMD_DESTROY); + while (!android_app->destroyed) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + close(android_app->msgread); + close(android_app->msgwrite); + pthread_cond_destroy(&android_app->cond); + pthread_mutex_destroy(&android_app->mutex); + free(android_app); +} + +static void onDestroy(ANativeActivity* activity) { + LOGV("Destroy: %p\n", activity); + android_app_free((struct android_app*)activity->instance); +} + +static void onStart(ANativeActivity* activity) { + LOGV("Start: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START); +} + +static void onResume(ANativeActivity* activity) { + LOGV("Resume: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME); +} + +static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { + struct android_app* android_app = (struct android_app*)activity->instance; + void* savedState = NULL; + + LOGV("SaveInstanceState: %p\n", activity); + pthread_mutex_lock(&android_app->mutex); + android_app->stateSaved = 0; + android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); + while (!android_app->stateSaved) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + + if (android_app->savedState != NULL) { + savedState = android_app->savedState; + *outLen = android_app->savedStateSize; + android_app->savedState = NULL; + android_app->savedStateSize = 0; + } + + pthread_mutex_unlock(&android_app->mutex); + + return savedState; +} + +static void onPause(ANativeActivity* activity) { + LOGV("Pause: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE); +} + +static void onStop(ANativeActivity* activity) { + LOGV("Stop: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP); +} + +static void onConfigurationChanged(ANativeActivity* activity) { + struct android_app* android_app = (struct android_app*)activity->instance; + LOGV("ConfigurationChanged: %p\n", activity); + android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED); +} + +static void onLowMemory(ANativeActivity* activity) { + struct android_app* android_app = (struct android_app*)activity->instance; + LOGV("LowMemory: %p\n", activity); + android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY); +} + +static void onWindowFocusChanged(ANativeActivity* activity, int focused) { + LOGV("WindowFocusChanged: %p -- %d\n", activity, focused); + android_app_write_cmd((struct android_app*)activity->instance, + focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); +} + +static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { + LOGV("NativeWindowCreated: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, window); +} + +static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { + LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, NULL); +} + +static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { + LOGV("InputQueueCreated: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, queue); +} + +static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { + LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, NULL); +} + +static void onNativeWindowRedrawNeeded(ANativeActivity* activity, ANativeWindow *window ) { + LOGV("onNativeWindowRedrawNeeded: %p -- %p\n", activity, window); +} + +JNIEXPORT +void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, + size_t savedStateSize) { + LOGV("Creating: %p\n", activity); + activity->callbacks->onDestroy = onDestroy; + activity->callbacks->onStart = onStart; + activity->callbacks->onResume = onResume; + activity->callbacks->onSaveInstanceState = onSaveInstanceState; + activity->callbacks->onPause = onPause; + activity->callbacks->onStop = onStop; + activity->callbacks->onConfigurationChanged = onConfigurationChanged; + activity->callbacks->onLowMemory = onLowMemory; + activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; + activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; + activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; + activity->callbacks->onInputQueueCreated = onInputQueueCreated; + activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; + activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded; + + activity->instance = android_app_create(activity, savedState, savedStateSize); +} + +void RunCallbackOnUIThread( void (*callback)(void *), void * opaque ) +{ + MainThreadCallbackProps gpdata; + gpdata.callback = callback; + gpdata.opaque = opaque; + write(gapp->uimsgwrite, &gpdata, sizeof(gpdata) ); +} + diff --git a/app/src/nativecode/android_native_app_glue.h b/app/src/nativecode/android_native_app_glue.h new file mode 100644 index 0000000..8443194 --- /dev/null +++ b/app/src/nativecode/android_native_app_glue.h @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2010 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_NATIVE_APP_GLUE_H +#define _ANDROID_NATIVE_APP_GLUE_H + +#include +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The native activity interface provided by + * is based on a set of application-provided callbacks that will be called + * by the Activity's main thread when certain events occur. + * + * This means that each one of this callbacks _should_ _not_ block, or they + * risk having the system force-close the application. This programming + * model is direct, lightweight, but constraining. + * + * The 'android_native_app_glue' static library is used to provide a different + * execution model where the application can implement its own main event + * loop in a different thread instead. Here's how it works: + * + * 1/ The application must provide a function named "android_main()" that + * will be called when the activity is created, in a new thread that is + * distinct from the activity's main thread. + * + * 2/ android_main() receives a pointer to a valid "android_app" structure + * that contains references to other important objects, e.g. the + * ANativeActivity object instance the application is running in. + * + * 3/ the "android_app" object holds an ALooper instance that already + * listens to two important things: + * + * - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX + * declarations below. + * + * - input events coming from the AInputQueue attached to the activity. + * + * Each of these correspond to an ALooper identifier returned by + * ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT, + * respectively. + * + * Your application can use the same ALooper to listen to additional + * file-descriptors. They can either be callback based, or with return + * identifiers starting with LOOPER_ID_USER. + * + * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event, + * the returned data will point to an android_poll_source structure. You + * can call the process() function on it, and fill in android_app->onAppCmd + * and android_app->onInputEvent to be called for your own processing + * of the event. + * + * Alternatively, you can call the low-level functions to read and process + * the data directly... look at the process_cmd() and process_input() + * implementations in the glue to see how to do this. + * + * See the sample named "native-activity" that comes with the NDK with a + * full usage example. Also look at the JavaDoc of NativeActivity. + */ + +struct android_app; + +/** + * Data associated with an ALooper fd that will be returned as the "outData" + * when that source has data ready. + */ +struct android_poll_source { + // The identifier of this source. May be LOOPER_ID_MAIN or + // LOOPER_ID_INPUT. + int32_t id; + + // The android_app this ident is associated with. + struct android_app* app; + + // Function to call to perform the standard processing of data from + // this source. + void (*process)(struct android_app* app, struct android_poll_source* source); +}; + +/** + * This is the interface for the standard glue code of a threaded + * application. In this model, the application's code is running + * in its own thread separate from the main thread of the process. + * It is not required that this thread be associated with the Java + * VM, although it will need to be in order to make JNI calls any + * Java objects. + */ +struct android_app { + // The application can place a pointer to its own state object + // here if it likes. + void* userData; + + // Fill this in with the function to process main app commands (APP_CMD_*) + void (*onAppCmd)(struct android_app* app, int32_t cmd); + + // Fill this in with the function to process input events. At this point + // the event has already been pre-dispatched, and it will be finished upon + // return. Return 1 if you have handled the event, 0 for any default + // dispatching. + int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event); + + // The ANativeActivity object instance that this app is running in. + ANativeActivity* activity; + + // The current configuration the app is running in. + AConfiguration* config; + + // This is the last instance's saved state, as provided at creation time. + // It is NULL if there was no state. You can use this as you need; the + // memory will remain around until you call android_app_exec_cmd() for + // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL. + // These variables should only be changed when processing a APP_CMD_SAVE_STATE, + // at which point they will be initialized to NULL and you can malloc your + // state and place the information here. In that case the memory will be + // freed for you later. + void* savedState; + size_t savedStateSize; + + // The ALooper associated with the app's thread. + ALooper* looper; + + + // The ALooper associated with the main thread. + ALooper* looperui; + + // When non-NULL, this is the input queue from which the app will + // receive user input events. + AInputQueue* inputQueue; + + // When non-NULL, this is the window surface that the app can draw in. + ANativeWindow* window; + + // Current content rectangle of the window; this is the area where the + // window's content should be placed to be seen by the user. + ARect contentRect; + + // Current state of the app's activity. May be either APP_CMD_START, + // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. + int activityState; + + // This is non-zero when the application's NativeActivity is being + // destroyed and waiting for the app thread to complete. + int destroyRequested; + + // ------------------------------------------------- + // Below are "private" implementation of the glue code. + + pthread_mutex_t mutex; + pthread_cond_t cond; + + int msgread; + int msgwrite; + + int uimsgread; + int uimsgwrite; + + pthread_t thread; + + struct android_poll_source cmdPollSource; + struct android_poll_source inputPollSource; + + int running; + int stateSaved; + int destroyed; + int redrawNeeded; + AInputQueue* pendingInputQueue; + ANativeWindow* pendingWindow; + ARect pendingContentRect; +}; + +enum { + /** + * Looper data ID of commands coming from the app's main thread, which + * is returned as an identifier from ALooper_pollOnce(). The data for this + * identifier is a pointer to an android_poll_source structure. + * These can be retrieved and processed with android_app_read_cmd() + * and android_app_exec_cmd(). + */ + LOOPER_ID_MAIN = 1, + + /** + * Looper data ID of events coming from the AInputQueue of the + * application's window, which is returned as an identifier from + * ALooper_pollOnce(). The data for this identifier is a pointer to an + * android_poll_source structure. These can be read via the inputQueue + * object of android_app. + */ + LOOPER_ID_INPUT = 2, + + /** + * Start of main ALooper identifiers. + */ + LOOPER_ID_MAIN_THREAD = 3, + + /** + * Start of user-defined ALooper identifiers. + */ + LOOPER_ID_USER = 4, +}; + +enum { + /** + * Command from main thread: the AInputQueue has changed. Upon processing + * this command, android_app->inputQueue will be updated to the new queue + * (or NULL). + */ + APP_CMD_INPUT_CHANGED, + + /** + * Command from main thread: a new ANativeWindow is ready for use. Upon + * receiving this command, android_app->window will contain the new window + * surface. + */ + APP_CMD_INIT_WINDOW, + + /** + * Command from main thread: the existing ANativeWindow needs to be + * terminated. Upon receiving this command, android_app->window still + * contains the existing window; after calling android_app_exec_cmd + * it will be set to NULL. + */ + APP_CMD_TERM_WINDOW, + + /** + * Command from main thread: the current ANativeWindow has been resized. + * Please redraw with its new size. + */ + APP_CMD_WINDOW_RESIZED, + + /** + * Command from main thread: the system needs that the current ANativeWindow + * be redrawn. You should redraw the window before handing this to + * android_app_exec_cmd() in order to avoid transient drawing glitches. + */ + APP_CMD_WINDOW_REDRAW_NEEDED, + + /** + * Command from main thread: the content area of the window has changed, + * such as from the soft input window being shown or hidden. You can + * find the new content rect in android_app::contentRect. + */ + APP_CMD_CONTENT_RECT_CHANGED, + + /** + * Command from main thread: the app's activity window has gained + * input focus. + */ + APP_CMD_GAINED_FOCUS, + + /** + * Command from main thread: the app's activity window has lost + * input focus. + */ + APP_CMD_LOST_FOCUS, + + /** + * Command from main thread: the current device configuration has changed. + */ + APP_CMD_CONFIG_CHANGED, + + /** + * Command from main thread: the system is running low on memory. + * Try to reduce your memory use. + */ + APP_CMD_LOW_MEMORY, + + /** + * Command from main thread: the app's activity has been started. + */ + APP_CMD_START, + + /** + * Command from main thread: the app's activity has been resumed. + */ + APP_CMD_RESUME, + + /** + * Command from main thread: the app should generate a new saved state + * for itself, to restore from later if needed. If you have saved state, + * allocate it with malloc and place it in android_app.savedState with + * the size in android_app.savedStateSize. The will be freed for you + * later. + */ + APP_CMD_SAVE_STATE, + + /** + * Command from main thread: the app's activity has been paused. + */ + APP_CMD_PAUSE, + + /** + * Command from main thread: the app's activity has been stopped. + */ + APP_CMD_STOP, + + /** + * Command from main thread: the app's activity is being destroyed, + * and waiting for the app thread to clean up and exit before proceeding. + */ + APP_CMD_DESTROY, + + /** + * Custom command to execute something from an event queue + */ + APP_CMD_CUSTOM_EVENT, +}; + +/** + * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next + * app command message. + */ +int8_t android_app_read_cmd(struct android_app* android_app); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * initial pre-processing of the given command. You can perform your own + * actions for the command after calling this function. + */ +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * final post-processing of the given command. You must have done your own + * actions for the command before calling this function. + */ +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Dummy function that used to be used to prevent the linker from stripping app + * glue code. No longer necessary, since __attribute__((visibility("default"))) + * does this for us. + */ +__attribute__(( + deprecated("Calls to app_dummy are no longer necessary. See " + "https://github.com/android-ndk/ndk/issues/381."))) void +app_dummy(); + +/** + * This is the function that application code must implement, representing + * the main entry to the app. + */ +extern void android_main(struct android_app* app); + +/** + * Mechanism to run code on main UI thread. + */ +void RunCallbackOnUIThread( void (*callback)(void *), void * opaque ); + +#ifdef __cplusplus +} +#endif + +#endif /* _ANDROID_NATIVE_APP_GLUE_H */ diff --git a/app/src/nativecode/android_usb_devices.c b/app/src/nativecode/android_usb_devices.c new file mode 100644 index 0000000..846ca8a --- /dev/null +++ b/app/src/nativecode/android_usb_devices.c @@ -0,0 +1,210 @@ +//Copyright 2020 <>< Charles Lohr, You may use this file and library freely under the MIT/x11, NewBSD or ColorChord Licenses. + +#include "android_usb_devices.h" +#include "CNFG.h" +#include "os_generic.h" + +double dTimeOfUSBFail; +double dTimeOfLastAsk; +jobject deviceConnection = 0; +int deviceConnectionFD = 0; +extern struct android_app * gapp; + +void DisconnectUSB() +{ + deviceConnectionFD = 0; + dTimeOfUSBFail = OGGetAbsoluteTime(); +} + +int RequestPermissionOrGetConnectionFD( char * ats, uint16_t vid, uint16_t pid ) +{ + //Don't permit + if( OGGetAbsoluteTime() - dTimeOfUSBFail < 1 ) + { + ats+=sprintf(ats, "Comms failed. Waiting to reconnect." ); + return -1; + } + + struct android_app* app = gapp; + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = app->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + // Retrieves NativeActivity. + jobject lNativeActivity = gapp->activity->clazz; + + //https://stackoverflow.com/questions/13280581/using-android-to-communicate-with-a-usb-hid-device + + //UsbManager manager = (UsbManager)getSystemService(Context.USB_SERVICE); + jclass ClassContext = env->FindClass( envptr, "android/content/Context" ); + jfieldID lid_USB_SERVICE = env->GetStaticFieldID( envptr, ClassContext, "USB_SERVICE", "Ljava/lang/String;" ); + jobject USB_SERVICE = env->GetStaticObjectField( envptr, ClassContext, lid_USB_SERVICE ); + + jmethodID MethodgetSystemService = env->GetMethodID( envptr, ClassContext, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;" ); + jobject manager = env->CallObjectMethod( envptr, lNativeActivity, MethodgetSystemService, USB_SERVICE); + //Actually returns an android/hardware/usb/UsbManager + jclass ClassUsbManager = env->FindClass( envptr, "android/hardware/usb/UsbManager" ); + + //HashMap deviceList = mManager.getDeviceList(); + jmethodID MethodgetDeviceList = env->GetMethodID( envptr, ClassUsbManager, "getDeviceList", "()Ljava/util/HashMap;" ); + jobject deviceList = env->CallObjectMethod( envptr, manager, MethodgetDeviceList ); + + //Iterator deviceIterator = deviceList.values().iterator(); + jclass ClassHashMap = env->FindClass( envptr, "java/util/HashMap" ); + jmethodID Methodvalues = env->GetMethodID( envptr, ClassHashMap, "values", "()Ljava/util/Collection;" ); + jobject deviceListCollection = env->CallObjectMethod( envptr, deviceList, Methodvalues ); + jclass ClassCollection = env->FindClass( envptr, "java/util/Collection" ); + jmethodID Methoditerator = env->GetMethodID( envptr, ClassCollection, "iterator", "()Ljava/util/Iterator;" ); + jobject deviceListIterator = env->CallObjectMethod( envptr, deviceListCollection, Methoditerator ); + jclass ClassIterator = env->FindClass( envptr, "java/util/Iterator" ); + + //while (deviceIterator.hasNext()) + jmethodID MethodhasNext = env->GetMethodID( envptr, ClassIterator, "hasNext", "()Z" ); + jboolean bHasNext = env->CallBooleanMethod( envptr, deviceListIterator, MethodhasNext ); + + ats+=sprintf(ats, "Has Devices: %d\n", bHasNext ); + + jmethodID Methodnext = env->GetMethodID( envptr, ClassIterator, "next", "()Ljava/lang/Object;" ); + + jclass ClassUsbDevice = env->FindClass( envptr, "android/hardware/usb/UsbDevice" ); + jclass ClassUsbInterface = env->FindClass( envptr, "android/hardware/usb/UsbInterface" ); + jclass ClassUsbEndpoint = env->FindClass( envptr, "android/hardware/usb/UsbEndpoint" ); + jclass ClassUsbDeviceConnection = env->FindClass( envptr, "android/hardware/usb/UsbDeviceConnection" ); + jmethodID MethodgetDeviceName = env->GetMethodID( envptr, ClassUsbDevice, "getDeviceName", "()Ljava/lang/String;" ); + jmethodID MethodgetVendorId = env->GetMethodID( envptr, ClassUsbDevice, "getVendorId", "()I" ); + jmethodID MethodgetProductId = env->GetMethodID( envptr, ClassUsbDevice, "getProductId", "()I" ); + jmethodID MethodgetInterfaceCount = env->GetMethodID( envptr, ClassUsbDevice, "getInterfaceCount", "()I" ); + jmethodID MethodgetInterface = env->GetMethodID( envptr, ClassUsbDevice, "getInterface", "(I)Landroid/hardware/usb/UsbInterface;" ); + + jmethodID MethodgetEndpointCount = env->GetMethodID( envptr, ClassUsbInterface, "getEndpointCount", "()I" ); + jmethodID MethodgetEndpoint = env->GetMethodID( envptr, ClassUsbInterface, "getEndpoint", "(I)Landroid/hardware/usb/UsbEndpoint;" ); + + jmethodID MethodgetAddress = env->GetMethodID( envptr, ClassUsbEndpoint, "getAddress", "()I" ); + jmethodID MethodgetMaxPacketSize = env->GetMethodID( envptr, ClassUsbEndpoint, "getMaxPacketSize", "()I" ); + + jobject matchingDevice = 0; + jobject matchingInterface = 0; + + while( bHasNext ) + { + // UsbDevice device = deviceIterator.next(); + // Log.i(TAG,"Model: " + device.getDeviceName()); + jobject device = env->CallObjectMethod( envptr, deviceListIterator, Methodnext ); + uint16_t vendorId = env->CallIntMethod( envptr, device, MethodgetVendorId ); + uint16_t productId = env->CallIntMethod( envptr, device, MethodgetProductId ); + int ifaceCount = env->CallIntMethod( envptr, device, MethodgetInterfaceCount ); + const char *strdevname = env->GetStringUTFChars(envptr, env->CallObjectMethod( envptr, device, MethodgetDeviceName ), 0); + ats+=sprintf(ats, "%s,%04x:%04x(%d)\n", strdevname, + vendorId, + productId, ifaceCount ); + + if( vendorId == vid && productId == pid ) + { + if( ifaceCount ) + { + matchingDevice = device; + matchingInterface = env->CallObjectMethod( envptr, device, MethodgetInterface, 0 ); + } + } + + bHasNext = env->CallBooleanMethod( envptr, deviceListIterator, MethodhasNext ); + } + + jobject matchingEp = 0; + + if( matchingInterface ) + { + //matchingInterface is of type android/hardware/usb/UsbInterface + int epCount = env->CallIntMethod( envptr, matchingInterface, MethodgetEndpointCount ); + ats+=sprintf(ats, "Found device %d eps\n", epCount ); + int i; + for( i = 0; i < epCount; i++ ) + { + jobject endpoint = env->CallObjectMethod( envptr, matchingInterface, MethodgetEndpoint, i ); + jint epnum = env->CallIntMethod( envptr, endpoint, MethodgetAddress ); + jint mps = env->CallIntMethod( envptr, endpoint, MethodgetMaxPacketSize ); + if( epnum == 0x02 ) matchingEp = endpoint; + ats+=sprintf(ats, "%p: %02x: MPS: %d (%c)\n", endpoint, epnum, mps, (matchingEp == endpoint)?'*':' ' ); + } + } + + jmethodID MethodopenDevice = env->GetMethodID( envptr, ClassUsbManager, "openDevice", "(Landroid/hardware/usb/UsbDevice;)Landroid/hardware/usb/UsbDeviceConnection;" ); + jmethodID MethodrequestPermission = env->GetMethodID( envptr, ClassUsbManager, "requestPermission", "(Landroid/hardware/usb/UsbDevice;Landroid/app/PendingIntent;)V" ); + jmethodID MethodhasPermission = env->GetMethodID( envptr, ClassUsbManager, "hasPermission", "(Landroid/hardware/usb/UsbDevice;)Z" ); + jmethodID MethodclaimInterface = env->GetMethodID( envptr, ClassUsbDeviceConnection, "claimInterface", "(Landroid/hardware/usb/UsbInterface;Z)Z" ); + jmethodID MethodsetInterface = env->GetMethodID( envptr, ClassUsbDeviceConnection, "setInterface", "(Landroid/hardware/usb/UsbInterface;)Z" ); + jmethodID MethodgetFileDescriptor = env->GetMethodID( envptr, ClassUsbDeviceConnection, "getFileDescriptor", "()I" ); + //jmethodID MethodbulkTransfer = env->GetMethodID( envptr, ClassUsbDeviceConnection, "bulkTransfer", "(Landroid/hardware/usb/UsbEndpoint;[BII)I" ); + + //see https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/hardware/usb/UsbDeviceConnection.java + //Calls: native_bulk_request -> android_hardware_UsbDeviceConnection_bulk_request -> usb_device_bulk_transfer + // UsbEndpoint endpoint, byte[] buffer, int length, int timeout + //bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int length, int timeout) + + //UsbDeviceConnection bulkTransfer + + if( matchingEp && matchingDevice ) + { + //UsbDeviceConnection deviceConnection = manager.openDevice( device ) + deviceConnection = env->CallObjectMethod( envptr, manager, MethodopenDevice, matchingDevice ); + jint epnum = env->CallIntMethod( envptr, matchingEp, MethodgetAddress ); + + if( !deviceConnection ) + { + // hasPermission(UsbDevice device) + + if( OGGetAbsoluteTime() - dTimeOfLastAsk < 5 ) + { + ats+=sprintf(ats, "Asked for permission. Waiting to ask again." ); + } + else if( env->CallBooleanMethod( envptr, manager, MethodhasPermission, matchingDevice ) ) + { + ats+=sprintf(ats, "Has permission - disconnected?" ); + } + else + { + //android.app.PendingIntent currently setting to 0 (null) seems not to cause crashes, but does force lock screen to happen. + //Because the screen locks we need to do a much more complicated operation, generating a PendingIntent. See Below. + // env->CallVoidMethod( envptr, manager, MethodrequestPermission, matchingDevice, 0 ); + + //This part mimiced off of: + //https://www.programcreek.com/java-api-examples/?class=android.hardware.usb.UsbManager&method=requestPermission + // manager.requestPermission(device, PendingIntent.getBroadcast(context, 0, new Intent(MainActivity.ACTION_USB_PERMISSION), 0)); + jclass ClassPendingIntent = env->FindClass( envptr, "android/app/PendingIntent" ); + jclass ClassIntent = env->FindClass(envptr, "android/content/Intent"); + jmethodID newIntent = env->GetMethodID(envptr, ClassIntent, "", "(Ljava/lang/String;)V"); + jstring ACTION_USB_PERMISSION = env->NewStringUTF( envptr, "com.android.recipes.USB_PERMISSION" ); + jobject intentObject = env->NewObject(envptr, ClassIntent, newIntent, ACTION_USB_PERMISSION); + + jmethodID MethodgetBroadcast = env->GetStaticMethodID( envptr, ClassPendingIntent, "getBroadcast", + "(Landroid/content/Context;ILandroid/content/Intent;I)Landroid/app/PendingIntent;" ); + jobject pi = env->CallStaticObjectMethod( envptr, ClassPendingIntent, MethodgetBroadcast, lNativeActivity, 0, intentObject, 0 ); + + //This actually requests permission. + env->CallVoidMethod( envptr, manager, MethodrequestPermission, matchingDevice, pi ); + dTimeOfLastAsk = OGGetAbsoluteTime(); + } + } + else + { + //Because we want to read and write to an interrupt endpoint, we need to claim the interface - it seems setting interfaces is insufficient here. + jboolean claimOk = env->CallBooleanMethod( envptr, deviceConnection, MethodclaimInterface, matchingInterface, 1 ); + //jboolean claimOk = env->CallBooleanMethod( envptr, deviceConnection, MethodsetInterface, matchingInterface ); + //jboolean claimOk = 1; + if( claimOk ) + { + deviceConnectionFD = env->CallIntMethod( envptr, deviceConnection, MethodgetFileDescriptor ); + } + + ats+=sprintf(ats, "DC: %p; Claim: %d; FD: %d\n", deviceConnection, claimOk, deviceConnectionFD ); + } + + } + + jnii->DetachCurrentThread( jniiptr ); + return (!deviceConnectionFD)?-5:0; +} + diff --git a/app/src/nativecode/android_usb_devices.h b/app/src/nativecode/android_usb_devices.h new file mode 100644 index 0000000..0583001 --- /dev/null +++ b/app/src/nativecode/android_usb_devices.h @@ -0,0 +1,17 @@ +//Copyright 2020 <>< Charles Lohr, You may use this file and library freely under the MIT/x11, NewBSD or ColorChord Licenses. + +#ifndef _ANDROID_USB_DEVICES_H +#define _ANDROID_USB_DEVICES_H + +#include +#include +#include + +int RequestPermissionOrGetConnectionFD( char * debug_status, uint16_t vid, uint16_t pid ); +void DisconnectUSB(); //Disconnect from USB + +extern jobject deviceConnection; +extern int deviceConnectionFD; + +#endif + diff --git a/app/src/nativecode/cnfa/.gitignore b/app/src/nativecode/cnfa/.gitignore new file mode 100644 index 0000000..911892b --- /dev/null +++ b/app/src/nativecode/cnfa/.gitignore @@ -0,0 +1,3 @@ +os_generic.h +example + diff --git a/app/src/nativecode/cnfa/CNFA.c b/app/src/nativecode/cnfa/CNFA.c new file mode 100644 index 0000000..296ae15 --- /dev/null +++ b/app/src/nativecode/cnfa/CNFA.c @@ -0,0 +1,128 @@ +//Copyright <>< 2010-2020 Charles Lohr (And other authors as cited) +//CNFA is licensed under the MIT/x11, ColorChord or NewBSD Licenses. You choose. + +#ifndef _CNFA_C +#define _CNFA_C + +#include "CNFA.h" +#include +#include +#include + +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) \ + || defined(_WIN32) || defined(_WIN64) +#ifndef strdup +#define strdup _strdup +#endif +#endif + +static CNFAInitFn * CNFADrivers[MAX_CNFA_DRIVERS]; +static char * CNFADriverNames[MAX_CNFA_DRIVERS]; +static int CNFADriverPriorities[MAX_CNFA_DRIVERS]; + +void RegCNFADriver( int priority, const char * name, CNFAInitFn * fn ) +{ + int j; + + if( priority <= 0 ) + { + return; + } + + printf("[CNFA] Registering Driver: %s\n", name); + + for( j = MAX_CNFA_DRIVERS-1; j >= 0; j-- ) + { + //Cruise along, find location to insert + if( j > 0 && ( !CNFADrivers[j-1] || CNFADriverPriorities[j-1] < priority ) ) + { + CNFADrivers[j] = CNFADrivers[j-1]; + CNFADriverNames[j] = CNFADriverNames[j-1]; + CNFADriverPriorities[j] = CNFADriverPriorities[j-1]; + } + else + { + CNFADrivers[j] = fn; + CNFADriverNames[j] = strdup( name ); + CNFADriverPriorities[j] = priority; + break; + } + } +} + +struct CNFADriver * CNFAInit( const char * driver_name, const char * your_name, CNFACBType cb, int reqSPSPlay, int reqSPSRec, + int reqChannelsPlay, int reqChannelsRec, int sugBufferSize, const char * outputSelect, const char * inputSelect, void * opaque) +{ + +#if defined( ANDROID ) || defined( __android__ ) + //Android can't run static-time code. + void REGISTERAndroidCNFA(); + REGISTERAndroidCNFA(); +#endif + + int i; + struct CNFADriver * ret = 0; + int minprio = 0; + CNFAInitFn * bestinit = 0; + if( driver_name == 0 || strlen( driver_name ) == 0 ) + { + //Search for a driver. + for( i = 0; i < MAX_CNFA_DRIVERS; i++ ) + { + if( CNFADrivers[i] == 0 ) + { + break; + } + if( CNFADriverPriorities[i] > minprio ) + { + minprio = CNFADriverPriorities[i]; + bestinit = CNFADrivers[i]; + } + } + if( bestinit ) + { + ret = (struct CNFADriver *)bestinit( cb, your_name, reqSPSPlay, reqSPSRec, reqChannelsPlay, reqChannelsRec, sugBufferSize, outputSelect, inputSelect, opaque ); + } + if( ret ) + { + return ret; + } + } + else + { + for( i = 0; i < MAX_CNFA_DRIVERS; i++ ) + { + if( CNFADrivers[i] == 0 ) + { + break; + } + if( strcmp( CNFADriverNames[i], driver_name ) == 0 ) + { + return (struct CNFADriver *)CNFADrivers[i]( cb, your_name, reqSPSPlay, reqSPSRec, reqChannelsPlay, reqChannelsRec, sugBufferSize, outputSelect, inputSelect, opaque ); + } + } + } + printf( "CNFA Driver not found.\n" ); + return 0; +} + +int CNFAState( struct CNFADriver * cnfaobject ) +{ + if( cnfaobject ) + { + return cnfaobject->StateFn( cnfaobject ); + } + return -1; +} + +void CNFAClose( struct CNFADriver * cnfaobject ) +{ + if( cnfaobject ) + { + cnfaobject->CloseFn( cnfaobject ); + } +} + +#endif + + diff --git a/app/src/nativecode/cnfa/CNFA.h b/app/src/nativecode/cnfa/CNFA.h new file mode 100644 index 0000000..29866f9 --- /dev/null +++ b/app/src/nativecode/cnfa/CNFA.h @@ -0,0 +1,122 @@ +//Copyright <>< 2010-2020 Charles Lohr (And other authors as cited) +//CNFA is licensed under the MIT/x11, ColorChord or NewBSD Licenses. You choose. +// +// CN's Platform-agnostic, foundational sound driver subsystem. +// Easily output and input sound on a variety of platforms. +// +// Options: +// * #define CNFA_IMPLEMENTATION before this header and it will build all +// definitions in. +// + + +#ifndef _CNFA_H +#define _CNFA_H + + + +//this #define is per-platform. For instance on Linux, you have ALSA, Pulse and null +#define MAX_CNFA_DRIVERS 4 + +struct CNFADriver; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef BUILD_DLL + #ifdef WINDOWS + #define DllExport __declspec( dllexport ) + #else + #define DllExport extern + #endif +#else + #define DllExport +#endif + +//NOTE: Some drivers have synchronous duplex mode, other drivers will use two different callbacks. If ether is unavailable, it will be NULL. +//I.e. if `out` is null, only use in to read. If in is null, only place samples in out. +typedef void(*CNFACBType)( struct CNFADriver * sd, short * out, short * in, int framesp, int framesr ); + +typedef void*(CNFAInitFn)( CNFACBType cb, const char * your_name, int reqSPSPlay, int reqSPSRec, int reqChannelsPlay, int reqChannelsRec, int sugBufferSize, const char * outputSelect, const char * inputSelect, void * opaque ); + +struct CNFADriver +{ + void (*CloseFn)( void * object ); + int (*StateFn)( void * object ); + CNFACBType callback; + short channelsPlay; + short channelsRec; + int spsPlay; + int spsRec; + void * opaque; + + //More fields may exist on a per-sound-driver basis +}; + +//Accepts: +//If DriverName = 0 or empty, will try to find best driver. +// +// our_source_name is an optional argument, but on some platforms controls the name of your endpoint. +// reqSPSPlay = 44100 is guaranteed on many platforms. +// reqSPSRec = 44100 is guaranteed on many platforms. +// NOTE: Some platforms do not allow SPS play and REC to deviate from each other. +// reqChannelsRec = 1 or 2 guaranteed on many platforms. +// reqChannelsPlay = 1 or 2 guaranteedon many platforms. NOTE: Some systems require ChannelsPlay == ChannelsRec! +// sugBufferSize = No promises. +// outputSelect = No standardization, NULL is OK for default. +// inputSelect = No standardization, NULL is OK for default. + +DllExport struct CNFADriver * CNFAInit( const char * driver_name, const char * your_name, CNFACBType cb, int reqSPSPlay, int reqSPSRec, int reqChannelsPlay, + int reqChannelsRec, int sugBufferSize, const char * outputSelect, const char * inputSelect, void * opaque ); + +DllExport int CNFAState( struct CNFADriver * cnfaobject ); //returns bitmask. 1 if mic recording, 2 if play back running, 3 if both running. +DllExport void CNFAClose( struct CNFADriver * cnfaobject ); + + +//Called by various sound drivers. Notice priority must be greater than 0. Priority of 0 or less will not register. +//This is an internal function. Applications shouldnot call it. +void RegCNFADriver( int priority, const char * name, CNFAInitFn * fn ); + +#if defined(_MSC_VER) && !defined(__clang__) +#define REGISTER_CNFA( cnfadriver, priority, name, function ) \ + void REGISTER##cnfadriver() { RegCNFADriver( priority, name, function ); } +#else +#define REGISTER_CNFA( cnfadriver, priority, name, function ) \ + void __attribute__((constructor)) REGISTER##cnfadriver() { RegCNFADriver( priority, name, function ); } +#endif + + +#ifdef __TINYC__ +#ifndef TCC +#define TCC +#endif +#endif + +#ifdef CNFA_IMPLEMENTATION +#include "CNFA.c" +#include "CNFA_null.c" +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) +#include "CNFA_winmm.c" +#include "CNFA_wasapi.c" +#elif defined( ANDROID ) || defined( __android__ ) +#include "CNFA_android.c" +#elif defined(__NetBSD__) || defined(__sun) +#include "CNFA_sun.c" +#elif defined(__linux__) +#include "CNFA_alsa.c" +#if defined(PULSEAUDIO) +#include "CNFA_pulse.c" +#endif +#endif +#endif + + +#ifdef __cplusplus +}; +#endif + + + +#endif + diff --git a/app/src/nativecode/cnfa/CNFA_alsa.c b/app/src/nativecode/cnfa/CNFA_alsa.c new file mode 100644 index 0000000..f273e29 --- /dev/null +++ b/app/src/nativecode/cnfa/CNFA_alsa.c @@ -0,0 +1,374 @@ +//Copyright 2015-2020 <>< Charles Lohr under the MIT/x11, NewBSD or ColorChord License. You choose. + +#include "CNFA.h" +#include "os_generic.h" +#include +#include + +struct CNFADriverAlsa +{ + void (*CloseFn)( void * object ); + int (*StateFn)( void * object ); + CNFACBType callback; + short channelsPlay; + short channelsRec; + int spsPlay; + int spsRec; + void * opaque; + + char * devRec; + char * devPlay; + + snd_pcm_uframes_t bufsize; + og_thread_t threadPlay; + og_thread_t threadRec; + snd_pcm_t *playback_handle; + snd_pcm_t *record_handle; + + char playing; + char recording; +}; + +int CNFAStateAlsa( void * v ) +{ + struct CNFADriverAlsa * r = (struct CNFADriverAlsa *)v; + return ((r->playing)?2:0) | ((r->recording)?1:0); +} + +void CloseCNFAAlsa( void * v ) +{ + struct CNFADriverAlsa * r = (struct CNFADriverAlsa *)v; + if( r ) + { + if( r->playback_handle ) snd_pcm_close (r->playback_handle); + if( r->record_handle ) snd_pcm_close (r->record_handle); + + if( r->threadPlay ) OGJoinThread( r->threadPlay ); + if( r->threadRec ) OGJoinThread( r->threadRec ); + + OGUSleep(2000); + + if( r->devRec ) free( r->devRec ); + if( r->devPlay ) free( r->devPlay ); + free( r ); + } +} + + +static int SetHWParams( snd_pcm_t * handle, int * samplerate, short * channels, snd_pcm_uframes_t * bufsize, struct CNFADriverAlsa * a ) +{ + int err; + int bufs; + int dir; + snd_pcm_hw_params_t *hw_params; + if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { + fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n", + snd_strerror (err)); + return -1; + } + + if ((err = snd_pcm_hw_params_any (handle, hw_params)) < 0) { + fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n", + snd_strerror (err)); + goto fail; + } + + if ((err = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + fprintf (stderr, "cannot set access type (%s)\n", + snd_strerror (err)); + goto fail; + } + + if ((err = snd_pcm_hw_params_set_format (handle, hw_params, SND_PCM_FORMAT_S16_LE )) < 0) { + fprintf (stderr, "cannot set sample format (%s)\n", + snd_strerror (err)); + goto fail; + } + + if ((err = snd_pcm_hw_params_set_rate_near (handle, hw_params, (unsigned int*)samplerate, 0)) < 0) { + fprintf (stderr, "cannot set sample rate (%s)\n", + snd_strerror (err)); + goto fail; + } + + if ((err = snd_pcm_hw_params_set_channels (handle, hw_params, *channels)) < 0) { + fprintf (stderr, "cannot set channel count (%s)\n", + snd_strerror (err)); + goto fail; + } + + dir = 0; + if( (err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, bufsize, &dir)) < 0 ) + { + fprintf( stderr, "cannot set period size. (%s)\n", + snd_strerror(err) ); + goto fail; + } + + //NOTE: This step is critical for low-latency sound. + bufs = *bufsize*3; + if( (err = snd_pcm_hw_params_set_buffer_size(handle, hw_params, bufs)) < 0 ) + { + fprintf( stderr, "cannot set snd_pcm_hw_params_set_buffer_size size. (%s)\n", + snd_strerror(err) ); + goto fail; + } + + + if ((err = snd_pcm_hw_params (handle, hw_params)) < 0) { + fprintf (stderr, "cannot set parameters (%s)\n", + snd_strerror (err)); + goto fail; + } + + snd_pcm_hw_params_free (hw_params); + return 0; +fail: + snd_pcm_hw_params_free (hw_params); + return -2; +} + + +static int SetSWParams( struct CNFADriverAlsa * d, snd_pcm_t * handle, int isrec ) +{ + snd_pcm_sw_params_t *sw_params; + int err; + //Time for software parameters: + + if( !isrec ) + { + if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) { + fprintf (stderr, "cannot allocate software parameters structure (%s)\n", + snd_strerror (err)); + goto failhard; + } + if ((err = snd_pcm_sw_params_current (handle, sw_params)) < 0) { + fprintf (stderr, "cannot initialize software parameters structure (%s) (%p)\n", + snd_strerror (err), handle); + goto fail; + } + + int buffer_size = d->bufsize*3; + int period_size = d->bufsize; + printf( "PERIOD: %d BUFFER: %d\n", period_size, buffer_size ); + + if ((err = snd_pcm_sw_params_set_avail_min (handle, sw_params, period_size )) < 0) { + fprintf (stderr, "cannot set minimum available count (%s)\n", + snd_strerror (err)); + goto fail; + } + //if ((err = snd_pcm_sw_params_set_stop_threshold(handle, sw_params, 512 )) < 0) { + // fprintf (stderr, "cannot set minimum available count (%s)\n", + // snd_strerror (err)); + // goto fail; + //} + if ((err = snd_pcm_sw_params_set_start_threshold(handle, sw_params, buffer_size - period_size )) < 0) { + fprintf (stderr, "cannot set minimum available count (%s)\n", + snd_strerror (err)); + goto fail; + } + if ((err = snd_pcm_sw_params (handle, sw_params)) < 0) { + fprintf (stderr, "cannot set software parameters (%s)\n", + snd_strerror (err)); + goto fail; + } + + + + } + + if ((err = snd_pcm_prepare (handle)) < 0) { + fprintf (stderr, "cannot prepare audio interface for use (%s)\n", + snd_strerror (err)); + goto fail; + } + + return 0; +fail: + if( !isrec ) + { + snd_pcm_sw_params_free (sw_params); + } +failhard: + return -1; +} + +void * RecThread( void * v ) +{ + struct CNFADriverAlsa * r = (struct CNFADriverAlsa *)v; + short samples[r->bufsize * r->channelsRec]; + snd_pcm_start(r->record_handle); + do + { + int err = snd_pcm_readi( r->record_handle, samples, r->bufsize ); + if( err < 0 ) + { + fprintf( stderr, "Warning: ALSA Recording Failed\n" ); + break; + } + if( err != r->bufsize ) + { + fprintf( stderr, "Warning: ALSA Recording Underflow\n" ); + } + r->recording = 1; + r->callback( (struct CNFADriver *)r, 0, samples, 0, err ); + } while( 1 ); + r->recording = 0; + fprintf( stderr, "ALSA Recording Stopped\n" ); + return 0; +} + +void * PlayThread( void * v ) +{ + struct CNFADriverAlsa * r = (struct CNFADriverAlsa *)v; + short samples[r->bufsize * r->channelsPlay]; + int err; + //int total_avail = snd_pcm_avail(r->playback_handle); + + snd_pcm_start(r->playback_handle); + r->callback( (struct CNFADriver *)r, samples, 0, r->bufsize, 0 ); + err = snd_pcm_writei(r->playback_handle, samples, r->bufsize); + + while( err >= 0 ) + { + // int avail = snd_pcm_avail(r->playback_handle); + // printf( "avail: %d\n", avail ); + r->callback( (struct CNFADriver *)r, samples, 0, r->bufsize, 0 ); + err = snd_pcm_writei(r->playback_handle, samples, r->bufsize); + if( err != r->bufsize ) + { + fprintf( stderr, "Warning: ALSA Playback Overflow\n" ); + } + r->playing = 1; + } + r->playing = 0; + fprintf( stderr, "ALSA Playback Stopped\n" ); + return 0; +} + +static struct CNFADriverAlsa * InitALSA( struct CNFADriverAlsa * r ) +{ + printf( "CNFA Alsa Init %p %p (%d %d) %d %d\n", r->playback_handle, r->record_handle, r->spsPlay, r->spsRec, r->channelsPlay, r->channelsRec ); + + int err; + if( r->channelsPlay ) + { + if ((err = snd_pcm_open (&r->playback_handle, r->devPlay?r->devPlay:"default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) { + fprintf (stderr, "cannot open output audio device (%s)\n", + snd_strerror (err)); + goto fail; + } + } + + if( r->channelsRec ) + { + if ((err = snd_pcm_open (&r->record_handle, r->devRec?r->devRec:"default", SND_PCM_STREAM_CAPTURE, 0)) < 0) { + fprintf (stderr, "cannot open input audio device (%s)\n", + snd_strerror (err)); + goto fail; + } + } + + printf( "%p %p\n", r->playback_handle, r->record_handle ); + + if( r->playback_handle ) + { + if( SetHWParams( r->playback_handle, &r->spsPlay, &r->channelsPlay, &r->bufsize, r ) < 0 ) + goto fail; + if( SetSWParams( r, r->playback_handle, 0 ) < 0 ) + goto fail; + } + + if( r->record_handle ) + { + if( SetHWParams( r->record_handle, &r->spsRec, &r->channelsRec, &r->bufsize, r ) < 0 ) + goto fail; + if( SetSWParams( r, r->record_handle, 1 ) < 0 ) + goto fail; + } + +#if 0 + if( r->playback_handle ) + { + snd_async_handler_t *pcm_callback; + //Handle automatically cleaned up when stream closed. + err = snd_async_add_pcm_handler(&pcm_callback, r->playback_handle, playback_callback, r); + if(err < 0) + { + printf("Playback callback handler error: %s\n", snd_strerror(err)); + } + } + + if( r->record_handle ) + { + snd_async_handler_t *pcm_callback; + //Handle automatically cleaned up when stream closed. + err = snd_async_add_pcm_handler(&pcm_callback, r->record_handle, record_callback, r); + if(err < 0) + { + printf("Record callback handler error: %s\n", snd_strerror(err)); + } + } +#endif + + if( r->playback_handle && r->record_handle ) + { + err = snd_pcm_link ( r->playback_handle, r->record_handle ); + if(err < 0) + { + printf("snd_pcm_link error: %s\n", snd_strerror(err)); + } + } + + if( r->playback_handle ) + { + r->threadPlay = OGCreateThread( PlayThread, r ); + } + + if( r->record_handle ) + { + r->threadRec = OGCreateThread( RecThread, r ); + } + + printf( "CNFA Alsa Init Out -> %p %p (%d %d) %d %d\n", r->playback_handle, r->record_handle, r->spsPlay, r->spsRec, r->channelsPlay, r->channelsRec ); + + return r; + +fail: + if( r ) + { + if( r->playback_handle ) snd_pcm_close (r->playback_handle); + if( r->record_handle ) snd_pcm_close (r->record_handle); + free( r ); + } + fprintf( stderr, "Error: ALSA failed to start.\n" ); + return 0; +} + + + +void * InitALSADriver( CNFACBType cb, const char * your_name, int reqSPSPlay, int reqSPSRec, int reqChannelsPlay, int reqChannelsRec, int sugBufferSize, const char * outputSelect, const char * inputSelect, void * opaque ) +{ + struct CNFADriverAlsa * r = (struct CNFADriverAlsa *)malloc( sizeof( struct CNFADriverAlsa ) ); + + r->CloseFn = CloseCNFAAlsa; + r->StateFn = CNFAStateAlsa; + r->callback = cb; + r->opaque = opaque; + r->spsPlay = reqSPSPlay; + r->spsRec = reqSPSRec; + r->channelsPlay = reqChannelsPlay; + r->channelsRec = reqChannelsRec; + + r->devRec = (inputSelect)?strdup(inputSelect):0; + r->devPlay = (outputSelect)?strdup(outputSelect):0; + + r->playback_handle = 0; + r->record_handle = 0; + r->bufsize = sugBufferSize; + + return InitALSA(r); +} + +REGISTER_CNFA( ALSA, 10, "ALSA", InitALSADriver ); + diff --git a/app/src/nativecode/cnfa/CNFA_android.c b/app/src/nativecode/cnfa/CNFA_android.c new file mode 100644 index 0000000..fa3715a --- /dev/null +++ b/app/src/nativecode/cnfa/CNFA_android.c @@ -0,0 +1,319 @@ +//Copyright 2019-2020 <>< Charles Lohr under the ColorChord License, MIT/x11 license or NewBSD Licenses. +// This was originally to be used with rawdrawandroid + +#include "CNFA.h" +#include "os_generic.h" +#include //Using android threads not os_generic threads. +#include +#include +#include +#include + +//based on https://github.com/android/ndk-samples/blob/master/native-audio/app/src/main/cpp/native-audio-jni.c + +// for native audio +#include +#include + +#include +#include +#include + +struct CNFADriverAndroid +{ + //Standard header - must remain. + void (*CloseFn)( void * object ); + int (*StateFn)( void * object ); + CNFACBType callback; + short channelsPlay; + short channelsRec; + int spsPlay; + int spsRec; + void * opaque; + + int buffsz; + + SLObjectItf engineObject; + SLEngineItf engineEngine; + SLRecordItf recorderRecord; + SLObjectItf recorderObject; + + SLPlayItf playerPlay; + SLObjectItf playerObject; + SLObjectItf outputMixObject; + + SLAndroidSimpleBufferQueueItf recorderBufferQueue; + SLAndroidSimpleBufferQueueItf playerBufferQueue; + //unsigned recorderSize; + + int recorderBufferSizeBytes; + int playerBufferSizeBytes; + short * recorderBuffer; + short * playerBuffer; +}; + + +void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context) +{ + struct CNFADriverAndroid * r = (struct CNFADriverAndroid*)context; + r->callback( (struct CNFADriver*)r, 0, r->recorderBuffer, 0, r->buffsz/(sizeof(short)*r->channelsRec) ); + (*r->recorderBufferQueue)->Enqueue( r->recorderBufferQueue, r->recorderBuffer, r->recorderBufferSizeBytes/(r->channelsRec*sizeof(short)) ); +} + +void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) +{ + struct CNFADriverAndroid * r = (struct CNFADriverAndroid*)context; + r->callback( (struct CNFADriver*)r, r->playerBuffer, 0, r->buffsz/(sizeof(short)*r->channelsPlay), 0 ); + (*r->playerBufferQueue)->Enqueue( r->playerBufferQueue, r->playerBuffer, r->playerBufferSizeBytes/(r->channelsPlay*sizeof(short))); +} + +static struct CNFADriverAndroid* InitAndroidDriver( struct CNFADriverAndroid * r ) +{ + SLresult result; + printf( "Starting InitAndroidDriver\n" ); + + // create engine + result = slCreateEngine(&r->engineObject, 0, NULL, 0, NULL, NULL); + assert(SL_RESULT_SUCCESS == result); + (void)result; + + // realize the engine + result = (*r->engineObject)->Realize(r->engineObject, SL_BOOLEAN_FALSE); + assert(SL_RESULT_SUCCESS == result); + (void)result; + + // get the engine interface, which is needed in order to create other objects + result = (*r->engineObject)->GetInterface(r->engineObject, SL_IID_ENGINE, &r->engineEngine); + assert(SL_RESULT_SUCCESS == result); + (void)result; + + + /////////////////////////////////////////////////////////////////////////////////////////////////////// + if( r->channelsPlay ) + { + printf("create output mix"); + + SLDataFormat_PCM format_pcm ={ + SL_DATAFORMAT_PCM, + r->channelsPlay, + r->spsPlay*1000, + SL_PCMSAMPLEFORMAT_FIXED_16, + SL_PCMSAMPLEFORMAT_FIXED_16, + (r->channelsPlay==1)?SL_SPEAKER_FRONT_CENTER:3, + SL_BYTEORDER_LITTLEENDIAN, + }; + SLDataLocator_AndroidSimpleBufferQueue loc_bq_play = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; + SLDataSource source = {&loc_bq_play, &format_pcm}; + const SLInterfaceID ids[1] = {SL_IID_VOLUME}; + const SLboolean req[1] = {SL_BOOLEAN_TRUE}; + const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE}; + + result = (*r->engineEngine)->CreateOutputMix(r->engineEngine, &r->outputMixObject, 0, ids, req); + result = (*r->outputMixObject)->Realize(r->outputMixObject, SL_BOOLEAN_FALSE); + + SLDataLocator_OutputMix loc_outmix = { SL_DATALOCATOR_OUTPUTMIX, r->outputMixObject }; + SLDataSink sink; + sink.pFormat = &format_pcm; + sink.pLocator = &loc_outmix; + + // create audio player + result = (*r->engineEngine)->CreateAudioPlayer(r->engineEngine, &r->playerObject, &source, &sink, 1, id, req); + if (SL_RESULT_SUCCESS != result) { + printf( "CreateAudioPlayer failed\n" ); + return JNI_FALSE; + } + + + // realize the audio player + result = (*r->playerObject)->Realize(r->playerObject, SL_BOOLEAN_FALSE); + if (SL_RESULT_SUCCESS != result) { + printf( "AudioPlayer Realize failed: %d\n", result ); + return JNI_FALSE; + } + + // get the player interface + result = (*r->playerObject)->GetInterface(r->playerObject, SL_IID_PLAY, &r->playerPlay); + assert(SL_RESULT_SUCCESS == result); + (void)result; + + // get the buffer queue interface + result = (*r->playerObject)->GetInterface(r->playerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &r->playerBufferQueue); + assert(SL_RESULT_SUCCESS == result); + (void)result; + + // register callback on the buffer queue + result = (*r->playerBufferQueue)->RegisterCallback(r->playerBufferQueue, bqPlayerCallback, r); + assert(SL_RESULT_SUCCESS == result); + (void)result; + + printf( "===================== Player init ok.\n" ); + } + + if( r->channelsRec ) + { + // configure audio source + SLDataLocator_IODevice loc_devI = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL}; + SLDataSource audioSrc = {&loc_devI, NULL}; + + // configure audio sink + SLDataFormat_PCM format_pcm ={ + SL_DATAFORMAT_PCM, + r->channelsRec, + r->spsRec*1000, + SL_PCMSAMPLEFORMAT_FIXED_16, + SL_PCMSAMPLEFORMAT_FIXED_16, + (r->channelsRec==1)?SL_SPEAKER_FRONT_CENTER:3, + SL_BYTEORDER_LITTLEENDIAN, + }; + SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; + SLDataSink audioSnk = {&loc_bq, &format_pcm}; + + + const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE}; + const SLboolean req[1] = {SL_BOOLEAN_TRUE}; + + result = (*r->engineEngine)->CreateAudioRecorder(r->engineEngine, &r->recorderObject, &audioSrc, &audioSnk, 1, id, req); + if (SL_RESULT_SUCCESS != result) { + printf( "CreateAudioRecorder failed\n" ); + return JNI_FALSE; + } + + // realize the audio recorder + result = (*r->recorderObject)->Realize(r->recorderObject, SL_BOOLEAN_FALSE); + if (SL_RESULT_SUCCESS != result) { + printf( "AudioRecorder Realize failed: %d\n", result ); + return JNI_FALSE; + } + + // get the record interface + result = (*r->recorderObject)->GetInterface(r->recorderObject, SL_IID_RECORD, &r->recorderRecord); + assert(SL_RESULT_SUCCESS == result); + (void)result; + + // get the buffer queue interface + result = (*r->recorderObject)->GetInterface(r->recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &r->recorderBufferQueue); + assert(SL_RESULT_SUCCESS == result); + (void)result; + + // register callback on the buffer queue + result = (*r->recorderBufferQueue)->RegisterCallback(r->recorderBufferQueue, bqRecorderCallback, r); + assert(SL_RESULT_SUCCESS == result); + (void)result; + } + + + if( r->playerPlay ) + { + result = (*r->playerPlay)->SetPlayState(r->playerPlay, SL_PLAYSTATE_STOPPED); + assert(SL_RESULT_SUCCESS == result); (void)result; + result = (*r->playerBufferQueue)->Clear(r->playerBufferQueue); + assert(SL_RESULT_SUCCESS == result); (void)result; + r->playerBuffer = malloc( r->playerBufferSizeBytes ); + memset( r->playerBuffer, 0, r->playerBufferSizeBytes ); + result = (*r->playerBufferQueue)->Enqueue(r->playerBufferQueue, r->playerBuffer, r->playerBufferSizeBytes ); + assert(SL_RESULT_SUCCESS == result); (void)result; + result = (*r->playerPlay)->SetPlayState(r->playerPlay, SL_PLAYSTATE_PLAYING); + assert(SL_RESULT_SUCCESS == result); (void)result; + } + + + if( r->recorderRecord ) + { + result = (*r->recorderRecord)->SetRecordState(r->recorderRecord, SL_RECORDSTATE_STOPPED); + assert(SL_RESULT_SUCCESS == result); (void)result; + result = (*r->recorderBufferQueue)->Clear(r->recorderBufferQueue); + assert(SL_RESULT_SUCCESS == result); (void)result; + // the buffer is not valid for playback yet + + r->recorderBuffer = malloc( r->recorderBufferSizeBytes ); + + // enqueue an empty buffer to be filled by the recorder + // (for streaming recording, we would enqueue at least 2 empty buffers to start things off) + result = (*r->recorderBufferQueue)->Enqueue(r->recorderBufferQueue, r->recorderBuffer, r->recorderBufferSizeBytes ); + // the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT, + // which for this code example would indicate a programming error + assert(SL_RESULT_SUCCESS == result); (void)result; + + // start recording + result = (*r->recorderRecord)->SetRecordState(r->recorderRecord, SL_RECORDSTATE_RECORDING); + assert(SL_RESULT_SUCCESS == result); (void)result; + } + + + printf( "Complete Init Sound Android\n" ); + return r; +} + +int CNFAStateAndroid( void * v ) +{ + struct CNFADriverAndroid * soundobject = (struct CNFADriverAndroid *)v; + return ((soundobject->recorderObject)?1:0) | ((soundobject->playerObject)?2:0); +} + +void CloseCNFAAndroid( void * v ) +{ + struct CNFADriverAndroid * r = (struct CNFADriverAndroid *)v; + // destroy audio recorder object, and invalidate all associated interfaces + if (r->recorderObject != NULL) { + (*r->recorderObject)->Destroy(r->recorderObject); + r->recorderObject = NULL; + r->recorderRecord = NULL; + r->recorderBufferQueue = NULL; + if( r->recorderBuffer ) free( r->recorderBuffer ); + } + + + if (r->playerObject != NULL) { + (*r->playerObject)->Destroy(r->playerObject); + r->playerObject = NULL; + r->playerPlay = NULL; + r->playerBufferQueue = NULL; + if( r->playerBuffer ) free( r->playerBuffer ); + } + + + // destroy engine object, and invalidate all associated interfaces + if (r->engineObject != NULL) { + (*r->engineObject)->Destroy(r->engineObject); + r->engineObject = NULL; + r->engineEngine = NULL; + } + +} + + +int AndroidHasPermissions(const char* perm_name); +void AndroidRequestAppPermissions(const char * perm); + + +void * InitCNFAAndroid( CNFACBType cb, const char * your_name, int reqSPSPlay, int reqSPSRec, int reqChannelsPlay, int reqChannelsRec, int sugBufferSize, const char * outputSelect, const char * inputSelect, void * opaque ) +{ + struct CNFADriverAndroid * r = (struct CNFADriverAndroid *)malloc( sizeof( struct CNFADriverAndroid ) ); + memset( r, 0, sizeof( *r) ); + r->CloseFn = CloseCNFAAndroid; + r->StateFn = CNFAStateAndroid; + r->callback = cb; + r->opaque = opaque; + r->channelsPlay = reqChannelsPlay; + r->channelsRec = reqChannelsRec; + r->spsRec = reqSPSRec; + r->spsPlay = reqSPSPlay; + + r->recorderBufferSizeBytes = sugBufferSize * 2 * r->channelsRec; + r->playerBufferSizeBytes = sugBufferSize * 2 * r->channelsPlay; + + int hasperm = AndroidHasPermissions( "RECORD_AUDIO" ); + if( !hasperm ) + { + AndroidRequestAppPermissions( "RECORD_AUDIO" ); + } + + r->buffsz = sugBufferSize; + + return InitAndroidDriver(r); +} + +//Tricky: On Android, this can't actually run before main. Have to manually execute it. + +REGISTER_CNFA( AndroidCNFA, 10, "ANDROID", InitCNFAAndroid ); + diff --git a/app/src/nativecode/cnfa/CNFA_null.c b/app/src/nativecode/cnfa/CNFA_null.c new file mode 100644 index 0000000..3b3852a --- /dev/null +++ b/app/src/nativecode/cnfa/CNFA_null.c @@ -0,0 +1,46 @@ +//Copyright 2015-2020 <>< Charles Lohr under the ColorChord License. + +#include "CNFA.h" +#include "os_generic.h" +#include + +struct CNFADriverNull +{ + void (*CloseFn)( void * object ); + int (*StateFn)( void * object ); + CNFACBType callback; + short channelsPlay; + short channelsRec; + int spsPlay; + int spsRec; + void * opaque; +}; + +void CloseCNFANull( void * object ) +{ + free( object ); +} + +int CNFAStateNull( void * object ) +{ + return 0; +} + + +void * InitCNFANull( CNFACBType cb, const char * your_name, int reqSPSPlay, int reqSPSRec, int reqChannelsPlay, int reqChannelsRec, int sugBufferSize, const char * outputSelect, const char * inputSelect, void * opaque ) +{ + struct CNFADriverNull * r = (struct CNFADriverNull *)malloc( sizeof( struct CNFADriverNull ) ); + r->CloseFn = CloseCNFANull; + r->StateFn = CNFAStateNull; + r->callback = cb; + r->spsPlay = reqSPSPlay; + r->spsRec = reqSPSRec; + r->opaque = opaque; + r->channelsPlay = reqChannelsPlay; + r->channelsRec = reqChannelsRec; + return r; +} + + +REGISTER_CNFA( NullCNFA, 1, "NULL", InitCNFANull ); + diff --git a/app/src/nativecode/cnfa/CNFA_pulse.c b/app/src/nativecode/cnfa/CNFA_pulse.c new file mode 100644 index 0000000..77b4d3f --- /dev/null +++ b/app/src/nativecode/cnfa/CNFA_pulse.c @@ -0,0 +1,308 @@ +//Copyright 2015-2020 <>< Charles Lohr under the MIT/x11, NewBSD or ColorChord License. You choose. + +//This file is really rough. Full duplex doesn't seem to work hardly at all. + + +#include "CNFA.h" +#include "os_generic.h" +#include + +#include +#include +#include +#include +#include + +#define BUFFERSETS 3 + + +//from http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/Samples/AsyncPlayback/ +//also http://maemo.org/api_refs/5.0/5.0-final/pulseaudio/pacat_8c-example.html + + +struct CNFADriverPulse +{ + void (*CloseFn)( void * object ); + int (*StateFn)( void * object ); + CNFACBType callback; + short channelsPlay; + short channelsRec; + int spsPlay; + int spsRec; + void * opaque; + + char * sourceNamePlay; + char * sourceNameRec; + + og_thread_t thread; + pa_stream * play; + pa_stream * rec; + pa_context * pa_ctx; + pa_mainloop *pa_ml; + int pa_ready; + int buffer; + //More fields may exist on a per-sound-driver basis +}; + + + + +int CNFAStatePulse( void * v ) +{ + struct CNFADriverPulse * soundobject = (struct CNFADriverPulse *)v; + return ((soundobject->play)?2:0) | ((soundobject->rec)?1:0); +} + +void CloseCNFAPulse( void * v ) +{ + struct CNFADriverPulse * r = (struct CNFADriverPulse *)v; + if( r ) + { + if( r->play ) + { + pa_stream_unref (r->play); + r->play = 0; + } + + if( r->rec ) + { + pa_stream_unref (r->rec); + r->rec = 0; + } + OGUSleep(2000); + OGCancelThread( r->thread ); + + + if( r->sourceNamePlay ) free( r->sourceNamePlay ); + if( r->sourceNameRec ) free( r->sourceNameRec ); + free( r ); + } +} + +static void * CNFAThread( void * v ) +{ + struct CNFADriverPulse * r = (struct CNFADriverPulse*)v; + while(1) + { + pa_mainloop_iterate( r->pa_ml, 1, NULL ); + } + return 0; +} + +static void stream_request_cb(pa_stream *s, size_t length, void *userdata) +{ + struct CNFADriverPulse * r = (struct CNFADriverPulse*)userdata; + if( !r->play ) + { + return; + } + short bufp[length*r->channelsPlay/sizeof(short)]; + r->callback( (struct CNFADriver*)r, bufp, 0, length/(sizeof(short)*r->channelsPlay), 0 ); + pa_stream_write(r->play, &bufp[0], length, NULL, 0LL, PA_SEEK_RELATIVE); +} + + +static void stream_record_cb(pa_stream *s, size_t length, void *userdata) +{ + struct CNFADriverPulse * r = (struct CNFADriverPulse*)userdata; + + uint16_t * bufr; + + if (pa_stream_peek(r->rec, (const void**)&bufr, &length) < 0) { + fprintf(stderr, ("pa_stream_peek() failed: %s\n"), pa_strerror(pa_context_errno(r->pa_ctx))); + return; + } + + short * buffer; + buffer = (short*)pa_xmalloc(length); + memcpy(buffer, bufr, length); + pa_stream_drop(r->rec); + r->callback( (struct CNFADriver*)r, 0, buffer, 0, length/(sizeof(short)*r->channelsRec) ); + pa_xfree( buffer ); +} + + + +static void stream_underflow_cb(pa_stream *s, void *userdata) { + printf("underflow\n"); +} + + +void pa_state_cb(pa_context *c, void *userdata) { + pa_context_state_t state; + int *pa_ready = (int*)userdata; + state = pa_context_get_state(c); + switch (state) { + // These are just here for reference + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + default: + break; + case PA_CONTEXT_FAILED: + case PA_CONTEXT_TERMINATED: + *pa_ready = 2; + break; + case PA_CONTEXT_READY: + *pa_ready = 1; + break; + } +} + + +void * InitCNFAPulse( CNFACBType cb, const char * your_name, int reqSPSPlay, int reqSPSRec, int reqChannelsPlay, int reqChannelsRec, int sugBufferSize, const char * outputSelect, const char * inputSelect, void * opaque ) +{ + static pa_buffer_attr bufattr; + static pa_sample_spec ss; + int error; + pa_mainloop_api *pa_mlapi; + const char * title = your_name; + + struct CNFADriverPulse * r = (struct CNFADriverPulse *)malloc( sizeof( struct CNFADriverPulse ) ); + + r->pa_ml = pa_mainloop_new(); + if( !r->pa_ml ) + { + fprintf( stderr, "Failed to initialize pa_mainloop_new()\n" ); + goto fail; + } + + pa_mlapi = pa_mainloop_get_api(r->pa_ml); + if( !pa_mlapi ) + { + fprintf( stderr, "Failed to initialize pa_mainloop_get_api()\n" ); + goto fail; + } + + r->pa_ctx = pa_context_new(pa_mlapi, title ); + pa_context_connect(r->pa_ctx, NULL, PA_CONTEXT_NOFLAGS, NULL); + + //TODO: pa_context_set_state_callback + + r->CloseFn = CloseCNFAPulse; + r->StateFn = CNFAStatePulse; + r->callback = cb; + r->opaque = opaque; + r->spsPlay = reqSPSPlay; + r->spsRec = reqSPSRec; + r->channelsPlay = reqChannelsPlay; + r->channelsRec = reqChannelsRec; + r->sourceNamePlay = outputSelect?strdup(outputSelect):0; + r->sourceNameRec = inputSelect?strdup(inputSelect):0; + + r->play = 0; + r->rec = 0; + r->buffer = sugBufferSize; + + printf ("Pulse: from: [O/I] %s/%s (%s) / (%d,%d)x(%d,%d) (%d)\n", r->sourceNamePlay, r->sourceNameRec, title, r->spsPlay, r->spsRec, r->channelsPlay, r->channelsRec, r->buffer ); + + memset( &ss, 0, sizeof( ss ) ); + + ss.format = PA_SAMPLE_S16NE; + + r->pa_ready = 0; + pa_context_set_state_callback(r->pa_ctx, pa_state_cb, &r->pa_ready); + + while (r->pa_ready == 0) + { + pa_mainloop_iterate(r->pa_ml, 1, NULL); + } + + int bufbytes = r->buffer * sizeof(short) * r->channelsRec; + + if( r->channelsPlay ) + { + ss.channels = r->channelsPlay; + ss.rate = r->spsPlay; + + if (!(r->play = pa_stream_new(r->pa_ctx, "Play", &ss, NULL))) { + error = -3; //XXX ??? TODO + fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); + goto fail; + } + + pa_stream_set_underflow_callback(r->play, stream_underflow_cb, NULL); + pa_stream_set_write_callback(r->play, stream_request_cb, r ); + + bufattr.fragsize = (uint32_t)-1; + bufattr.maxlength = bufbytes*3; //XXX TODO Consider making this -1 + bufattr.minreq = 0; + bufattr.prebuf = (uint32_t)-1; + bufattr.tlength = bufbytes*3; + int ret = pa_stream_connect_playback(r->play, r->sourceNamePlay, &bufattr, + // PA_STREAM_INTERPOLATE_TIMING + // |PA_STREAM_ADJUST_LATENCY //Some servers don't like the adjust_latency flag. + // |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL); + PA_STREAM_NOFLAGS, NULL, NULL ); + if( ret < 0 ) + { + fprintf(stderr, __FILE__": (PLAY) pa_stream_connect_playback() failed: %s\n", pa_strerror(ret)); + goto fail; + } + + } + + if( r->channelsRec ) + { + ss.channels = r->channelsRec; + ss.rate = r->spsRec; + + if (!(r->rec = pa_stream_new(r->pa_ctx, "Record", &ss, NULL))) { + error = -3; //XXX ??? TODO + fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error)); + goto fail; + } + + pa_stream_set_read_callback(r->rec, stream_record_cb, r ); + + bufattr.fragsize = bufbytes; + bufattr.maxlength = (uint32_t)-1;//(uint32_t)-1; //XXX: Todo, should this be low? + bufattr.minreq = bufbytes; + bufattr.prebuf = (uint32_t)-1; + bufattr.tlength = bufbytes*3; + int ret = pa_stream_connect_record(r->rec, r->sourceNameRec, &bufattr, +// PA_STREAM_INTERPOLATE_TIMING + PA_STREAM_ADJUST_LATENCY //Some servers don't like the adjust_latency flag. +// PA_STREAM_AUTO_TIMING_UPDATE +// PA_STREAM_NOFLAGS + ); + +printf( "PA REC RES: %d\n", ret ); + + if( ret < 0 ) + { + fprintf(stderr, __FILE__": (REC) pa_stream_connect_playback() failed: %s\n", pa_strerror(ret)); + goto fail; + } + } + + printf( "Pulse initialized.\n" ); + + + r->thread = OGCreateThread( CNFAThread, r ); + + + if( r->play ) + { + stream_request_cb( r->play, bufbytes, r ); + stream_request_cb( r->play, bufbytes, r ); + } + + return r; + +fail: + if( r ) + { + if( r->play ) pa_xfree (r->play); + if( r->rec ) pa_xfree (r->rec); + free( r ); + } + return 0; +} + + + +REGISTER_CNFA( PulseCNFA, 11, "PULSE", InitCNFAPulse ); + + diff --git a/app/src/nativecode/cnfa/CNFA_sun.c b/app/src/nativecode/cnfa/CNFA_sun.c new file mode 100644 index 0000000..d94913f --- /dev/null +++ b/app/src/nativecode/cnfa/CNFA_sun.c @@ -0,0 +1,275 @@ +//Copyright 2015-2020 <>< Charles Lohr under the MIT/x11, NewBSD or ColorChord License. You choose. + +#include "CNFA.h" +#include "os_generic.h" +#include +#include +#include +#include +#include + +struct CNFADriverSun +{ + void (*CloseFn)( void * object ); + int (*StateFn)( void * object ); + CNFACBType callback; + short channelsPlay; + short channelsRec; + int spsPlay; + int spsRec; + void * opaque; + + char * devRec; + char * devPlay; + + short * samplesRec; + short * samplesPlay; + + og_thread_t threadPlay; + og_thread_t threadRec; + int bufsize; + int playback_handle; + int record_handle; + + char playing; + char recording; +}; + +int CNFAStateSun( void * v ) +{ + struct CNFADriverSun * r = (struct CNFADriverSun *)v; + return ((r->playing)?2:0) | ((r->recording)?1:0); +} + +void CloseCNFASun( void * v ) +{ + struct CNFADriverSun * r = (struct CNFADriverSun *)v; + if( r ) + { + if( r->playback_handle != -1 ) close (r->playback_handle); + if( r->record_handle != -1 ) close (r->record_handle); + + if( r->threadPlay ) OGJoinThread( r->threadPlay ); + if( r->threadRec ) OGJoinThread( r->threadRec ); + + OGUSleep(2000); + + free( r->devRec ); + free( r->devPlay ); + free( r->samplesRec ); + free( r->samplesPlay ); + free( r ); + } +} + + +void * RecThread( void * v ) +{ + struct CNFADriverSun * r = (struct CNFADriverSun *)v; + size_t nbytes = r->bufsize * (2 * r->channelsRec); + do + { + int nread = read( r->record_handle, r->samplesRec, nbytes ); + if( nread < 0 ) + { + fprintf( stderr, "Warning: Sun Recording Failed\n" ); + break; + } + r->recording = 1; + r->callback( (struct CNFADriver *)r, NULL, r->samplesRec, 0, (nread / 2) / r->channelsRec); + } while( 1 ); + r->recording = 0; + fprintf( stderr, "Sun Recording Stopped\n" ); + return 0; +} + +void * PlayThread( void * v ) +{ + struct CNFADriverSun * r = (struct CNFADriverSun *)v; + size_t nbytes = r->bufsize * (2 * r->channelsPlay); + int err; + + r->callback( (struct CNFADriver *)r, r->samplesPlay, NULL, r->bufsize, 0 ); + err = write( r->playback_handle, r->samplesPlay, nbytes ); + + while( err >= 0 ) + { + r->callback( (struct CNFADriver *)r, r->samplesPlay, NULL, r->bufsize, 0 ); + err = write( r->playback_handle, r->samplesPlay, nbytes ); + r->playing = 1; + } + r->playing = 0; + fprintf( stderr, "Sun Playback Stopped\n" ); + return 0; +} + +static struct CNFADriverSun * InitSun( struct CNFADriverSun * r ) +{ + const char * devPlay = r->devPlay; + const char * devRec = r->devRec; + struct audio_info rinfo, pinfo; + + if( devRec == NULL || strcmp ( devRec, "default" ) == 0 ) + { + devRec = "/dev/audio"; + } + + if( devPlay == NULL || strcmp ( devPlay , "default" ) == 0 ) + { + devPlay = "/dev/audio"; + } + + printf( "CNFA Sun Init -> devPlay: %s, channelsPlay: %d, spsPlay: %d, devRec: %s, channelsRec: %d, spsRec: %d\n", devPlay, r->channelsPlay, r->spsPlay, devRec, r->channelsRec, r->spsRec); + + if( r->channelsPlay && r->channelsRec && strcmp (devPlay, devRec) == 0 ) + { + if ( (r->playback_handle = r->record_handle = open (devPlay, O_RDWR)) < 0 ) + { + fprintf (stderr, "cannot open audio device (%s)\n", + strerror (errno)); + goto fail; + } + } + else + { + if( r->channelsPlay ) + { + if ( (r->playback_handle = open (devPlay, O_WRONLY)) < 0 ) + { + fprintf (stderr, "cannot open output audio device %s (%s)\n", + r->devPlay, strerror (errno)); + goto fail; + } + } + + if( r->channelsRec ) + { + if ( (r->record_handle = open (devRec, O_RDONLY)) < 0 ) + { + fprintf (stderr, "cannot open input audio device %s (%s)\n", + r->devRec, strerror (errno)); + goto fail; + } + } + } + + if( r->playback_handle ) + { + AUDIO_INITINFO(&pinfo); + + pinfo.play.precision = 16; + pinfo.play.encoding = AUDIO_ENCODING_LINEAR; + pinfo.play.sample_rate = r->spsPlay; + pinfo.play.channels = r->channelsPlay; + + if ( ioctl(r->playback_handle, AUDIO_SETINFO, &pinfo) < 0 ) + { + fprintf (stderr, "cannot set audio playback format (%s)\n", + strerror (errno)); + goto fail; + } + + if ( ioctl(r->playback_handle, AUDIO_GETINFO, &pinfo) < 0 ) + { + fprintf (stderr, "cannot get audio record format (%s)\n", + strerror (errno)); + goto fail; + } + + r->spsPlay = pinfo.play.sample_rate; + r->channelsPlay = pinfo.play.channels; + + if ( (r->samplesPlay = calloc(2 * r->channelsPlay, r->bufsize)) == NULL ) + { + goto fail; + } + } + + if( r->record_handle ) + { + AUDIO_INITINFO(&rinfo); + + rinfo.record.precision = 16; + rinfo.record.encoding = AUDIO_ENCODING_LINEAR; + rinfo.record.sample_rate = r->spsRec; + rinfo.record.channels = r->channelsRec; + + if ( ioctl(r->record_handle, AUDIO_SETINFO, &rinfo) < 0 ) + { + fprintf (stderr, "cannot set audio record format (%s)\n", + strerror (errno)); + goto fail; + } + + if ( ioctl(r->record_handle, AUDIO_GETINFO, &rinfo) < 0 ) + { + fprintf (stderr, "cannot get audio record format (%s)\n", + strerror (errno)); + goto fail; + } + + r->spsRec = rinfo.record.sample_rate; + r->channelsRec = rinfo.record.channels; + + if ( (r->samplesRec = calloc(2 * r->channelsRec, r->bufsize)) == NULL ) + { + goto fail; + } + } + + if( r->playback_handle ) + { + r->threadPlay = OGCreateThread( PlayThread, r ); + } + + if( r->record_handle ) + { + r->threadRec = OGCreateThread( RecThread, r ); + } + + printf( "CNFA Sun Init Out -> channelsPlay: %d, spsPlay: %d, channelsRec: %d, spsRec: %d\n", r->channelsPlay, r->spsPlay, r->channelsRec, r->spsRec); + + return r; + +fail: + if( r ) + { + if( r->playback_handle != -1 ) close (r->playback_handle); + if( r->record_handle != -1 ) close (r->record_handle); + free( r->samplesPlay ); + free( r->samplesRec ); + free( r ); + } + return 0; +} + + + +void * InitSunDriver( CNFACBType cb, const char * your_name, int reqSPSPlay, int reqSPSRec, int reqChannelsPlay, int reqChannelsRec, int sugBufferSize, const char * outputSelect, const char * inputSelect, void * opaque ) +{ + struct CNFADriverSun * r = (struct CNFADriverSun *)malloc( sizeof( struct CNFADriverSun ) ); + + r->CloseFn = CloseCNFASun; + r->StateFn = CNFAStateSun; + r->callback = cb; + r->opaque = opaque; + r->spsPlay = reqSPSPlay; + r->spsRec = reqSPSRec; + r->channelsPlay = reqChannelsPlay; + r->channelsRec = reqChannelsRec; + + r->devRec = (inputSelect)?strdup(inputSelect):0; + r->devPlay = (outputSelect)?strdup(outputSelect):0; + + r->samplesPlay = NULL; + r->samplesRec = NULL; + + r->playback_handle = -1; + r->record_handle = -1; + r->bufsize = sugBufferSize; + + return InitSun(r); +} + +REGISTER_CNFA( SUN, 10, "Sun", InitSunDriver ); + diff --git a/app/src/nativecode/cnfa/CNFA_wasapi.c b/app/src/nativecode/cnfa/CNFA_wasapi.c new file mode 100644 index 0000000..0eba70b --- /dev/null +++ b/app/src/nativecode/cnfa/CNFA_wasapi.c @@ -0,0 +1,514 @@ +#include "CNFA.h" + +//Needed libraries: -lmmdevapi -lavrt -lole32 +//Or DLLs: C:/windows/system32/avrt.dll C:/windows/system32/ole32.dll + +#ifdef TCC +#define NO_WIN_HEADERS +#endif + +#ifdef NO_WIN_HEADERS +#include "CNFA_wasapi_utils.h" +#else +#include +#include // Render and capturing audio +#include // Audio device handling +#include // Property keys for audio devices +#include // Thread management +#include "windows.h" +#endif + +#include "os_generic.h" + +#if defined(WIN32) && !defined( TCC ) +#pragma comment(lib,"avrt.lib") +#pragma comment(lib,"ole32.lib") +//And maybe mmdevapi.lib +#endif + +#define WASAPIPRINT(message) (printf("[WASAPI] %s\n", message)) +#define WASAPIERROR(error, message) (printf("[WASAPI][ERR] %s HRESULT: 0x%lX\n", message, error)) +#define PRINTGUID(guid) (printf("{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7])) + +#define WASAPI_EXTRA_DEBUG FALSE + +// Forward declarations +void CloseCNFAWASAPI(void* stateObj); +int CNFAStateWASAPI(void* object); +static struct CNFADriverWASAPI* StartWASAPIDriver(struct CNFADriverWASAPI* initState); +static IMMDevice* WASAPIGetDefaultDevice(BOOL isCapture, BOOL isMultimedia); +static void WASAPIPrintAllDeviceLists(); +static void WASAPIPrintDeviceList(EDataFlow dataFlow); +void* ProcessEventAudioIn(void* stateObj); +void* InitCNFAWASAPIDriver( + CNFACBType callback, const char *session_name, + int reqSampleRateOut, int reqSampleRateIn, + int reqChannelsOut, int reqChannelsIn, int sugBufferSize, + const char * inputDevice, const char * outputDevice, + void * opaque +); + +DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395L, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E); +DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2L, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6); +DEFINE_GUID(IID_IMMEndpoint, 0x1BE09788L, 0x6894, 0x4089, 0x85, 0x86, 0x9A, 0x2A, 0x6C, 0x26, 0x5A, 0xC5); +DEFINE_GUID(IID_IAudioClient, 0x1CB9AD4CL, 0xDBFA, 0x4c32, 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2); +DEFINE_GUID(IID_IAudioCaptureClient, 0xC8ADBD64L, 0xE71E, 0x48a0, 0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17); + +// This is a fallback if the client application does not provide a GUID. +DEFINE_GUID(CNFA_GUID, 0x899081C7L, 0x9428, 0x4103, 0x87, 0x93, 0x26, 0x47, 0xE5, 0xEA, 0xA2, 0xB4); + +struct CNFADriverWASAPI +{ + // Common CNFA items + void (*CloseFn)(void* object); + int (*StateFn)(void* object); + CNFACBType Callback; + short ChannelCountOut; // Not yet used. + short ChannelCountIn; // How many cahnnels the input stream has per frame. E.g. stereo = 2. + int SampleRateOut; + int SampleRateIn; + void* Opaque; // Not relevant to us + + // Adjustable WASAPI-specific items + const char* SessionName; // The name to give our audio sessions. Otherwise, defaults to using embedded EXE name, Window title, or EXE file name directly. + const GUID* SessionID; // In order to have different CNFA-based applications individually controllable from the volume mixer, this should be set differently for every client program, but constant across all runs/builds of that application. + + // Everything below here is for internal use only. Do not attempt to interact with these items. + const char* InputDeviceID; // The device to use for getting input from. Can be a render device (operating in loopback), or a capture device. + const char* OutputDeviceID; // Not yet used. + IMMDeviceEnumerator* DeviceEnumerator; // The base object that allows us to look through the system's devices, and from there get everything else. + IMMDevice* Device; // The device we are taking input from. + IAudioClient* Client; // The base client we use for getting input. + IAudioCaptureClient* CaptureClient; // The specific client we use for getting input. + WAVEFORMATEX* MixFormat; // The format of the input stream. + INT32 BytesPerFrame; // The number of bytes of one full frame of audio. AKA (channel count) * (sample bit depth), in Bytes. + BOOL StreamReady; // Whether the input stream is ready for data retrieval. + BOOL KeepGoing; // Whether to continue interacting with the streams, or shutdown the driver. + og_thread_t ThreadOut; // Not yet used. + og_thread_t ThreadIn; // The thread used to grab input data. + HANDLE EventHandleOut; // Not yet used. + HANDLE EventHandleIn; // The handle used to wait for more input data to be ready in the input thread. + HANDLE TaskHandleOut; // The task used to request output thread priority changes. + HANDLE TaskHandleIn; // The task used to request input thread priority changes. +}; + +// This is where the driver's current state is stored. +static struct CNFADriverWASAPI* WASAPIState; + +// Stops streams, ends threads, and cleans up all resources used by the driver. +void CloseCNFAWASAPI(void* stateObj) +{ + struct CNFADriverWASAPI* state = (struct CNFADriverWASAPI*)stateObj; + if(state != NULL) + { + // TODO: See if there are any other items that need cleanup. + state->KeepGoing = FALSE; + if (state->ThreadOut != NULL) { OGJoinThread(state->ThreadOut); } + if (state->ThreadIn != NULL) { OGJoinThread(state->ThreadIn); } + if (state->EventHandleOut != NULL) { CloseHandle(state->EventHandleOut); } + if (state->EventHandleIn != NULL) { CloseHandle(state->EventHandleIn); } + CoTaskMemFree(state->MixFormat); + if (state->CaptureClient != NULL) { state->CaptureClient->lpVtbl->Release(state->CaptureClient); } + if (state->Client != NULL) { state->Client->lpVtbl->Release(state->Client); } + if (state->Device != NULL) { state->Device->lpVtbl->Release(state->Device); } + if (state->DeviceEnumerator != NULL) { state->DeviceEnumerator->lpVtbl->Release(state->DeviceEnumerator); } + free(stateObj); + CoUninitialize(); + printf("[WASAPI] Cleanup completed. Goodbye.\n"); + } +} + +// Gets the current state of the driver. +// 0 = No streams active +// 1 = Input stream active +// 2 = Output stream active +// 3 = Both streams active +int CNFAStateWASAPI(void* stateObj) +{ + struct CNFADriverWASAPI* state = (struct CNFADriverWASAPI*)stateObj; + if(state != NULL) + { + if (state->StreamReady) { return 1; } // TODO: Output the correct status when output is implemented. + } + return 0; +} + +// Reads the desired configuration, interfaces with WASAPI to get the current system information, and starts the input stream. +static struct CNFADriverWASAPI* StartWASAPIDriver(struct CNFADriverWASAPI* initState) +{ + WASAPIState = initState; + WASAPIState->StreamReady = FALSE; + WASAPIState->SessionID = &CNFA_GUID; + + HRESULT ErrorCode; + #ifndef BUILD_DLL + // A library should never call CoInitialize, as it needs to be done from the host program according to its threading model needs. + // NOTE: If you are getting errors, and you are using CNFA as a DLL, you need to call CoInitialize yourself with an appropriate threading model for your needs! + // When the host program is something like ColorChord on the other hand, it cannot be expected to call CoInitialize itself, so we do it on its behalf. + // This restricts the threading model of direct consumers of CNFA, but we can address that if it does ever become an issue. + ErrorCode = CoInitialize(NULL); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "COM INIT FAILED!"); return WASAPIState; } + #endif + + if(WASAPI_EXTRA_DEBUG) + { + printf("[WASAPI] CLSID for MMDeviceEnumerator: "); + PRINTGUID(CLSID_MMDeviceEnumerator); + printf("\n[WASAPI] IID for IMMDeviceEnumerator: "); + PRINTGUID(IID_IMMDeviceEnumerator); + printf("\n[WASAPI] IID for IAudioClient: "); + PRINTGUID(IID_IAudioClient); + printf("\n[WASAPI] IID for IAudioCaptureClient: "); + PRINTGUID(IID_IAudioCaptureClient); + printf("\n[WASAPI] IID for IMMEndpoint: "); + PRINTGUID(IID_IMMEndpoint); + printf("\n"); + } + + ErrorCode = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void**)&(WASAPIState->DeviceEnumerator)); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to get device enumerator. "); return WASAPIState; } + + WASAPIPrintAllDeviceLists(); + + // We need to find the appropriate device to use. + BYTE DeviceDirection = 2; // 0 = Render, 1 = Capture, 2 = Unknown + + if (WASAPIState->InputDeviceID == NULL) + { + WASAPIPRINT("No device specified, attempting to use system default multimedia capture device as input."); + WASAPIState->Device = WASAPIGetDefaultDevice(TRUE, TRUE); + DeviceDirection = 1; + } + else if (strcmp(WASAPIState->InputDeviceID, "defaultRender") == 0) + { + WASAPIPRINT("Attempting to use system default render device as input."); + WASAPIState->Device = WASAPIGetDefaultDevice(FALSE, TRUE); + DeviceDirection = 0; + } + else if (strncmp("defaultCapture", WASAPIState->InputDeviceID, strlen("defaultCapture")) == 0) + { + BOOL IsMultimedia = TRUE; + if (strstr(WASAPIState->InputDeviceID, "Comm") != NULL) { IsMultimedia = FALSE; } + printf("[WASAPI] Attempting to use system default %s capture device as input.\n", (IsMultimedia ? "multimedia" : "communications")); + WASAPIState->Device = WASAPIGetDefaultDevice(TRUE, IsMultimedia); + DeviceDirection = 1; + } + else // A specific device was selected by ID. + { + LPWSTR DeviceIDasLPWSTR; + DeviceIDasLPWSTR = malloc((strlen(WASAPIState->InputDeviceID) + 1) * sizeof(WCHAR)); + mbstowcs(DeviceIDasLPWSTR, WASAPIState->InputDeviceID, strlen(WASAPIState->InputDeviceID) + 1); + printf("[WASAPI] Attempting to find specified device \"%ls\".\n", DeviceIDasLPWSTR); + + ErrorCode = WASAPIState->DeviceEnumerator->lpVtbl->GetDevice(WASAPIState->DeviceEnumerator, DeviceIDasLPWSTR, &(WASAPIState->Device)); + if (FAILED(ErrorCode)) + { + WASAPIERROR(ErrorCode, "Failed to get audio device from the given ID. Using default multimedia capture device instead."); + WASAPIState->Device = WASAPIGetDefaultDevice(TRUE, TRUE); + DeviceDirection = 1; + } + else + { + printf("[WASAPI] Found specified device.\n"); + DWORD DeviceState; + ErrorCode = WASAPIState->Device->lpVtbl->GetState(WASAPIState->Device, &DeviceState); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to get device state."); } + + if ((DeviceState & DEVICE_STATE_DISABLED) == DEVICE_STATE_DISABLED) { WASAPIERROR(E_FAIL, "The specified device is currently disabled."); } + if ((DeviceState & DEVICE_STATE_NOTPRESENT) == DEVICE_STATE_NOTPRESENT) { WASAPIERROR(E_FAIL, "The specified device is not currently present."); } + if ((DeviceState & DEVICE_STATE_UNPLUGGED) == DEVICE_STATE_UNPLUGGED) { WASAPIERROR(E_FAIL, "The specified device is currently unplugged."); } + } + } + + if (DeviceDirection == 2) // We still don't know what type of device we are trying to use. Query the endpoint to find out. + { + IMMEndpoint* Endpoint; + ErrorCode = WASAPIState->Device->lpVtbl->QueryInterface(WASAPIState->Device, &IID_IMMEndpoint, (void**)&Endpoint); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to get endpoint of device."); } + + EDataFlow DataFlow; + ErrorCode = Endpoint->lpVtbl->GetDataFlow(Endpoint, &DataFlow); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Could not determine endpoint type."); } + + DeviceDirection = (DataFlow == eRender) ? 0 : 1; + + if (Endpoint != NULL) { Endpoint->lpVtbl->Release(Endpoint); } + } + + // We should have a device now. + char* DeviceDirectionDesc = (DeviceDirection == 0) ? "render" : ((DeviceDirection == 1) ? "capture" : "UNKNOWN"); + + LPWSTR DeviceID; + ErrorCode = WASAPIState->Device->lpVtbl->GetId(WASAPIState->Device, &DeviceID); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to get audio device ID."); return WASAPIState; } + else { printf("[WASAPI] Using device ID \"%ls\", which is a %s device.\n", DeviceID, DeviceDirectionDesc); } + + // Start an audio client and get info about the stream format. + ErrorCode = WASAPIState->Device->lpVtbl->Activate(WASAPIState->Device, &IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&(WASAPIState->Client)); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to get audio client. "); return WASAPIState; } + + ErrorCode = WASAPIState->Client->lpVtbl->GetMixFormat(WASAPIState->Client, &(WASAPIState->MixFormat)); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to get mix format. "); return WASAPIState; } + printf("[WASAPI] Mix format is %d channel, %luHz sample rate, %db per sample.\n", WASAPIState->MixFormat->nChannels, WASAPIState->MixFormat->nSamplesPerSec, WASAPIState->MixFormat->wBitsPerSample); + printf("[WASAPI] Mix format is format %d, %dB block-aligned, with %dB of extra data in this definition.\n", WASAPIState->MixFormat->wFormatTag, WASAPIState->MixFormat->nBlockAlign, WASAPIState->MixFormat->cbSize); + + // We'll request PCM, 16bpS data from the system. It should be able to do this conversion for us, as long as we are not in exclusive mode. + // TODO: This isn't working, no matter what combination I try to ask it for. Figure this out, so we don't have to do the conversion ourselves. + // Also, we probably don't handle channel counts > 2 with this current setup. + //WASAPIState->MixFormat->wFormatTag = WAVE_FORMAT_PCM; + //WASAPIState->MixFormat->wBitsPerSample = 16 * WASAPIState->MixFormat->nChannels; + //WASAPIState->MixFormat->nBlockAlign = 2 * WASAPIState->MixFormat->nChannels; + //WASAPIState->MixFormat->nAvgBytesPerSec = WASAPIState->MixFormat->nSamplesPerSec * WASAPIState->MixFormat->nBlockAlign; + + WASAPIState->ChannelCountIn = WASAPIState->MixFormat->nChannels; + WASAPIState->SampleRateIn = WASAPIState->MixFormat->nSamplesPerSec; + WASAPIState->BytesPerFrame = WASAPIState->MixFormat->nChannels * (WASAPIState->MixFormat->wBitsPerSample / 8); + + REFERENCE_TIME DefaultInterval, MinimumInterval; + ErrorCode = WASAPIState->Client->lpVtbl->GetDevicePeriod(WASAPIState->Client, &DefaultInterval, &MinimumInterval); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to get device timing info. "); return WASAPIState; } + printf("[WASAPI] Default transaction period is %lld ticks, minimum is %lld ticks.\n", DefaultInterval, MinimumInterval); + + // Configure a capture client. + UINT32 StreamFlags; + if (DeviceDirection == 1) { StreamFlags = AUDCLNT_STREAMFLAGS_NOPERSIST | AUDCLNT_STREAMFLAGS_EVENTCALLBACK; } + else if (DeviceDirection == 0) { StreamFlags = (AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK); } + else { WASAPIPRINT("[ERR] Device type was not determined!"); return WASAPIState; } + + // TODO: Allow the target application to influence the interval we choose. Super realtime apps may require MinimumInterval. + ErrorCode = WASAPIState->Client->lpVtbl->Initialize(WASAPIState->Client, AUDCLNT_SHAREMODE_SHARED, StreamFlags, DefaultInterval, DefaultInterval, WASAPIState->MixFormat, WASAPIState->SessionID); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Could not init audio client."); return WASAPIState; } + + WASAPIState->EventHandleIn = CreateEvent(NULL, FALSE, FALSE, NULL); + if (WASAPIState->EventHandleIn == NULL) { WASAPIERROR(E_FAIL, "Failed to make event handle."); return WASAPIState; } + + ErrorCode = WASAPIState->Client->lpVtbl->SetEventHandle(WASAPIState->Client, WASAPIState->EventHandleIn); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to set event handler."); return WASAPIState; } + + UINT32 BufferFrameCount; + ErrorCode = WASAPIState->Client->lpVtbl->GetBufferSize(WASAPIState->Client, &BufferFrameCount); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Could not get audio client buffer size."); return WASAPIState; } + + ErrorCode = WASAPIState->Client->lpVtbl->GetService(WASAPIState->Client, &IID_IAudioCaptureClient, (void**)&(WASAPIState->CaptureClient)); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Could not get audio capture client."); return WASAPIState; } + + // Begin capturing audio. It will be received on a separate thread. + ErrorCode = WASAPIState->Client->lpVtbl->Start(WASAPIState->Client); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Could not start audio client."); return WASAPIState; } + WASAPIState->StreamReady = TRUE; + + WASAPIState->KeepGoing = TRUE; + WASAPIState->ThreadIn = OGCreateThread(ProcessEventAudioIn, WASAPIState); + + return WASAPIState; +} + +// Gets the default render or capture device. +// isCapture: If true, gets the default capture device, otherwise gets the default render device. +// isMultimedia: If true, gets the system default devide for "multimedia" use, otheriwse for "communication" use. +static IMMDevice* WASAPIGetDefaultDevice(BOOL isCapture, BOOL isMultimedia) +{ + HRESULT ErrorCode; + IMMDevice* Device; + ErrorCode = WASAPIState->DeviceEnumerator->lpVtbl->GetDefaultAudioEndpoint(WASAPIState->DeviceEnumerator, isCapture ? eCapture : eRender, isMultimedia ? eMultimedia : eCommunications, &Device); + if (FAILED(ErrorCode)) + { + WASAPIERROR(ErrorCode, "Failed to get default device."); + return NULL; + } + return Device; +} + +// Prints all available devices to the console. +static void WASAPIPrintAllDeviceLists() +{ + WASAPIPrintDeviceList(eRender); + WASAPIPrintDeviceList(eCapture); +} + +// Prints a list of all available devices of a specified data flow direction to the console. +static void WASAPIPrintDeviceList(EDataFlow dataFlow) +{ + printf("[WASAPI] %s Devices:\n", (dataFlow == eCapture ? "Capture" : "Render")); + IMMDeviceCollection* Devices; + HRESULT ErrorCode = WASAPIState->DeviceEnumerator->lpVtbl->EnumAudioEndpoints(WASAPIState->DeviceEnumerator, dataFlow, (WASAPI_EXTRA_DEBUG ? DEVICE_STATEMASK_ALL : DEVICE_STATE_ACTIVE), &Devices); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to get audio endpoints."); return; } + + UINT32 DeviceCount; + ErrorCode = Devices->lpVtbl->GetCount(Devices, &DeviceCount); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to get audio endpoint count."); return; } + + for (UINT32 DeviceIndex = 0; DeviceIndex < DeviceCount; DeviceIndex++) + { + IMMDevice* Device; + ErrorCode = Devices->lpVtbl->Item(Devices, DeviceIndex, &Device); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to get audio device."); continue; } + + LPWSTR DeviceID; + ErrorCode = Device->lpVtbl->GetId(Device, &DeviceID); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to get audio device ID."); continue; } + + IPropertyStore* Properties; + ErrorCode = Device->lpVtbl->OpenPropertyStore(Device, STGM_READ, &Properties); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to get device properties."); continue; } + + PROPVARIANT Variant; + PropVariantInit(&Variant); + + ErrorCode = Properties->lpVtbl->GetValue(Properties, &PKEY_Device_FriendlyName, &Variant); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to get device friendly name."); } + + LPWSTR DeviceFriendlyName = L"[Name Retrieval Failed]"; + if (Variant.pwszVal != NULL) { DeviceFriendlyName = Variant.pwszVal; } + + printf("[WASAPI] [%d]: \"%ls\" = \"%ls\"\n", DeviceIndex, DeviceFriendlyName, DeviceID); + + CoTaskMemFree(DeviceID); + DeviceID = NULL; + PropVariantClear(&Variant); + if (Properties != NULL) { Properties->lpVtbl->Release(Properties); } + if (Device != NULL) { Device->lpVtbl->Release(Device); } + } + + if (Devices != NULL) { Devices->lpVtbl->Release(Devices); } +} + +// Runs on a thread. Waits for audio data to be ready from the system, then forwards it to the registered callback. +void* ProcessEventAudioIn(void* stateObj) +{ + struct CNFADriverWASAPI* state = (struct CNFADriverWASAPI*)stateObj; + HRESULT ErrorCode; + UINT32 PacketLength; + + // TODO: Set this based on our device period requested. If we are using 10ms or higher, just request "Audio", not "Pro Audio". + DWORD TaskIndex = 0; + state->TaskHandleIn = AvSetMmThreadCharacteristicsW(L"Pro Audio", &TaskIndex); + if (state->TaskHandleIn == NULL) { WASAPIERROR(E_FAIL, "Failed to request thread priority elevation on input task."); } + + while (state->KeepGoing) + { + // Waits up to 500ms to get the next audio buffer from the system. + // The timeout is used because if no audio sessions are active, WASAPI stops sending buffers after a few that indicate silence. + // This means that if the client tries to exit, this loop would not complete, and therefore the thread would not exit, until the next buffer is received. + // This is mostly an issue in loopback mode, where true silence is common, not so much on microphones. + DWORD WaitResult = WaitForSingleObject(state->EventHandleIn, 500); + if (WaitResult == WAIT_TIMEOUT) { continue; } // We are in a period of silence. Keep waiting for audio. + else if (WaitResult != WAIT_OBJECT_0) { WASAPIERROR(E_FAIL, "Something went wrong while waiting for an audio event."); continue; } + + ErrorCode = state->CaptureClient->lpVtbl->GetNextPacketSize(state->CaptureClient, &PacketLength); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to get audio packet size."); continue; } + + BYTE* DataBuffer; + UINT32 FramesAvailable; + DWORD BufferStatus; + BOOL Released = FALSE; + ErrorCode = state->CaptureClient->lpVtbl->GetBuffer(state->CaptureClient, &DataBuffer, &FramesAvailable, &BufferStatus, NULL, NULL); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to get audio buffer."); continue; } + + // "The data in the packet is not correlated with the previous packet's device position; this is possibly due to a stream state transition or timing glitch." + // There's no real way for us to notify the client about this... + if ((BufferStatus & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) == AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) + { + WASAPIPRINT("A data discontinuity was detected."); + } + + if ((BufferStatus & AUDCLNT_BUFFERFLAGS_SILENT) == AUDCLNT_BUFFERFLAGS_SILENT) + { + UINT32 Length = FramesAvailable * state->MixFormat->nChannels; + if (Length == 0) { Length = state->MixFormat->nChannels; } + INT16* AudioData = malloc(Length * 2); + for (int i = 0; i < Length; i++) { AudioData[i] = 0; } + + ErrorCode = state->CaptureClient->lpVtbl->ReleaseBuffer(state->CaptureClient, FramesAvailable); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to release audio buffer."); } + else { Released = TRUE; } + + if (WASAPI_EXTRA_DEBUG) { printf("[WASAPI] SILENCE buffer received. Passing on %d samples.\n", Length); } + + WASAPIState->Callback((struct CNFADriver*)WASAPIState, 0, AudioData, 0, Length / state->MixFormat->nChannels ); + free(AudioData); + } + else + { + // TODO: This assumes that data is coming in at 32b float format. While this appears to be the format that WASAPI uses internally in all cases I've seen, I don't think it's guaranteed. + // We should instead read the MixFormat information and properly handle the data in other cases. + // Ideally, we could request 16b signed PCM data from WASAPI, so we don't even have to do any conversion. But I couldn't get this working yet. + UINT32 Size = FramesAvailable * state->BytesPerFrame; // Size in bytes + FLOAT* DataAsFloat = (FLOAT*)DataBuffer; // The raw input data, reinterpreted as floats. + INT16* AudioData = malloc((FramesAvailable * state->MixFormat->nChannels) * 2); // The data we are passing to the consumer. + for (INT32 i = 0; i < Size / 4; i++) { AudioData[i] = (INT16)(DataAsFloat[i] * 32767.5F); } + + ErrorCode = state->CaptureClient->lpVtbl->ReleaseBuffer(state->CaptureClient, FramesAvailable); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to release audio buffer."); } + else { Released = TRUE; } + + if (WASAPI_EXTRA_DEBUG) { printf("[WASAPI] Got %d bytes of audio data in %d frames. Fowarding to %p.\n", Size, FramesAvailable, (void*) WASAPIState->Callback); } + + WASAPIState->Callback((struct CNFADriver*)WASAPIState, 0, AudioData, 0, FramesAvailable ); + free(AudioData); + } + + if (!Released) + { + ErrorCode = state->CaptureClient->lpVtbl->ReleaseBuffer(state->CaptureClient, FramesAvailable); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to release audio buffer."); } + } + + } + + ErrorCode = state->Client->lpVtbl->Stop(state->Client); + if (FAILED(ErrorCode)) { WASAPIERROR(ErrorCode, "Failed to stop audio client."); } + + if(state->TaskHandleIn != NULL) { AvRevertMmThreadCharacteristics(state->TaskHandleIn); } + + state->StreamReady = FALSE; + return 0; +} + +// Begins preparation of the WASAPI driver. +// callback: The user application's function where audio data is placed when received from the system and/or audio data is retrieved from to give to the system. +// sessionName: How your session will appear to the end user if you play audio. +// reqSampleRateIn/Out: Sample rate you'd like to request. Ignored, as this is determined by the system. See note below. +// reqChannelsIn: Input channel count you'd like to request. Ignored, as this is determined by the system. See note below. +// reqChannelsOut: Output channel count you'd like to request. Ignored, as this is determined by the system. See note below. +// sugBufferSize: Buffer size you'd like to request. Ignored, as this is determined by the system. See note below. +// inputDevice: The device you want to receive audio from. Loopback is supported, so this can be either a capture or render device. +// To get the default render device, specify "defaultRender" +// To get the default multimedia capture device, specify "defaultCapture" +// To get the default communications capture device, specify "defaultCaptureComm" +// A device ID as presented by WASAPI can be specified, regardless of what type it is. If it is invalid, the default capture device is used as fallback. +// If you do not wish to receive audio, specify null. NOT YET IMPLEMENTED +// outputDevice: The device you want to output audio to. OUTPUT IS NOT IMPLEMENTED. +// NOTES: +// Regarding format requests: Sample rate and channel count is determined by the system settings, and cannot be changed. Resampling/mixing will be required in your application if you cannot accept the current system mode. Make sure to check `WASAPIState` for the current system mode. +// Note also that both sample rate and channel count can vary between input and output! +// Currently audio output (playing) is not yet implemented. +void* InitCNFAWASAPIDriver( + CNFACBType callback, const char *sessionName, + int reqSampleRateOut, int reqSampleRateIn, + int reqChannelsOut, int reqChannelsIn, int sugBufferSize, + const char * outputDevice, const char * inputDevice, + void * opaque) +{ + struct CNFADriverWASAPI * InitState = malloc(sizeof(struct CNFADriverWASAPI)); + memset(InitState, 0, sizeof(*InitState)); + InitState->CloseFn = CloseCNFAWASAPI; + InitState->StateFn = CNFAStateWASAPI; + InitState->Callback = callback; + InitState->Opaque = opaque; + // TODO: Waiting for CNFA to support directional sample rates. + InitState->SampleRateIn = reqSampleRateIn; // Will be overridden by the actual system setting. + InitState->SampleRateOut = reqSampleRateOut; // Will be overridden by the actual system setting. + InitState->ChannelCountIn = reqChannelsIn; // Will be overridden by the actual system setting. + InitState->ChannelCountOut = reqChannelsOut; // Will be overridden by the actual system setting. + InitState->InputDeviceID = inputDevice; + InitState->OutputDeviceID = outputDevice; + + InitState->SessionName = sessionName; + + WASAPIPRINT("WASAPI Init"); + + return StartWASAPIDriver(InitState); +} + +REGISTER_CNFA(cnfa_wasapi, 20, "WASAPI", InitCNFAWASAPIDriver); diff --git a/app/src/nativecode/cnfa/CNFA_wasapi_utils.h b/app/src/nativecode/cnfa/CNFA_wasapi_utils.h new file mode 100644 index 0000000..258793e --- /dev/null +++ b/app/src/nativecode/cnfa/CNFA_wasapi_utils.h @@ -0,0 +1,636 @@ +#ifndef _CNFA_WASAPI_UTILS_H +#define _CNFA_WASAPI_UTILS_H + +//#include "ole2.h" + +#ifndef REFPROPERTYKEY +#define REFPROPERTYKEY const PROPERTYKEY * __MIDL_CONST +#endif //REFPROPERTYKEY + +// Necessary definitions +#define _ANONYMOUS_STRUCT +#define BEGIN_INTERFACE +#define END_INTERFACE +#define DEVICE_STATE_ACTIVE 0x00000001 +#define AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000 +#define AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000 +#define AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000 +#define AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000 +#define AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000 +#define AUDCLNT_STREAMFLAGS_PREVENT_LOOPBACK_CAPTURE 0x01000000 +#define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 +#define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 +#define AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000 +#define AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000 +#define AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000 +enum _AUDCLNT_BUFFERFLAGS +{ + AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY = 0x1, + AUDCLNT_BUFFERFLAGS_SILENT = 0x2, + AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR = 0x4 +} ; + + +#ifndef REFIID +#define REFIID const IID * __MIDL_CONST +#endif + +#ifndef PropVariantInit +#define PropVariantInit(pvar) memset ( (pvar), 0, sizeof(PROPVARIANT) ) +#endif + +#if defined (__TINYC__) +#define _COM_Outptr_ +#define _In_ +#define _Out_ +#define _Outptr_ +#define _In_opt_ +#define _Out_opt_ +#define __RPC__in +#define __RPC__out +#define interface struct +#define CONST_VTBL +#define _Outptr_result_buffer_(X) +#define _Inexpressible_(X) +#define REFPROPVARIANT const PROPVARIANT * __MIDL_CONST +typedef struct tagPROPVARIANT PROPVARIANT; +typedef struct tWAVEFORMATEX WAVEFORMATEX; +typedef IID GUID; +typedef void* HANDLE; + +#define CLSCTX_INPROC_SERVER 0x1 +#define CLSCTX_INPROC_HANDLER 0x2 +#define CLSCTX_LOCAL_SERVER 0x4 +#define CLSCTX_REMOTE_SERVER 0x10 +#define STGM_READ 0x00000000L +#define CLSCTX_ALL (CLSCTX_INPROC_SERVER| \ + CLSCTX_INPROC_HANDLER| \ + CLSCTX_LOCAL_SERVER| \ + CLSCTX_REMOTE_SERVER) +typedef unsigned short VARTYPE; + +typedef struct _tagpropertykey { + GUID fmtid; + DWORD pid; +} PROPERTYKEY; + +#ifndef __wtypes_h__ +typedef struct tagDEC { + USHORT wReserved; + BYTE scale; + BYTE sign; + ULONG Hi32; + ULONGLONG Lo64; +} DECIMAL; + +// Property varient struct, used for getting the device name info +typedef BYTE PROPVAR_PAD1; +typedef BYTE PROPVAR_PAD2; +typedef ULONG PROPVAR_PAD3; + +struct tagPROPVARIANT { + union { + struct tag_inner_PROPVARIANT + { + VARTYPE vt; + PROPVAR_PAD1 wReserved1; + PROPVAR_PAD2 wReserved2; + PROPVAR_PAD3 wReserved3; + union + { + double dblVal; // Filler for the largest object we need to store + LPWSTR pwszVal; // This is the only parameter we actually use + }; + } ; + DECIMAL decVal; + }; +}; + +#endif + +#define _Inout_updates_(dwCount) +#define FAR + + +typedef interface IUnknown IUnknown; +typedef IUnknown *LPUNKNOWN; +#endif + +#ifdef NO_WIN_HEADERS +#undef DEFINE_GUID +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + EXTERN_C const GUID DECLSPEC_SELECTANY name \ + = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } +#undef DEFINE_PROPERTYKEY +#define DEFINE_PROPERTYKEY(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8,pid) \ + EXTERN_C const PROPERTYKEY DECLSPEC_SELECTANY name \ + = { { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }, pid } + +// stuff to be able to read device names +DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); + +#ifndef WINOLEAPI +#define WINOLEAPI EXTERN_C DECLSPEC_IMPORT HRESULT STDAPICALLTYPE +#define WINOLEAPI_(type) EXTERN_C DECLSPEC_IMPORT type STDAPICALLTYPE +#endif + +// Define necessary functions +WINOLEAPI_(HANDLE) +AvSetMmThreadCharacteristicsW(LPCWSTR TaskName, LPDWORD TaskIndex); + +WINOLEAPI_(BOOL) +AvRevertMmThreadCharacteristics(HANDLE AvrtHandle); + +WINOLEAPI CoInitialize(LPVOID pvReserved); +WINOLEAPI_(void) CoUninitialize(); +WINOLEAPI_(void) CoTaskMemFree(LPVOID pv); + +WINOLEAPI CoCreateInstance( + REFCLSID rclsid, + LPUNKNOWN pUnkOuter, + DWORD dwClsContext, + REFIID riid, + LPVOID FAR* ppv); + +// WINOLEAPI CoCreateInstanceEx( +// REFCLSID Clsid, +// IUnknown *punkOuter, +// DWORD dwClsCtx, +// COSERVERINFO *pServerInfo, +// DWORD dwCount, +// MULTI_QI *pResults ); + +#endif //NO_WIN_HEADERS + +// forward declarations +typedef struct IMMDevice IMMDevice; +typedef struct IMMDeviceCollection IMMDeviceCollection; +typedef struct IMMDeviceEnumerator IMMDeviceEnumerator; +typedef struct IMMNotificationClient IMMNotificationClient; +typedef struct IPropertyStore IPropertyStore; +typedef struct IAudioClient IAudioClient; +typedef struct IAudioCaptureClient IAudioCaptureClient; + +// So the linker doesn't complain +extern const IID CLSID_MMDeviceEnumerator; +extern const IID IID_IMMDeviceEnumerator; +extern const IID IID_IAudioClient; +extern const IID CNFA_GUID; +extern const IID IID_IAudioCaptureClient; + +typedef enum __MIDL___MIDL_itf_mmdeviceapi_0000_0000_0001 +{ + eRender = 0, + eCapture = ( eRender + 1 ) , + eAll = ( eCapture + 1 ) , + EDataFlow_enum_count = ( eAll + 1 ) +} EDataFlow; + +typedef enum __MIDL___MIDL_itf_mmdeviceapi_0000_0000_0002 +{ + eConsole = 0, + eMultimedia = ( eConsole + 1 ) , + eCommunications = ( eMultimedia + 1 ) , + ERole_enum_count = ( eCommunications + 1 ) +} ERole; + +typedef struct IMMDeviceEnumeratorVtbl +{ + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IMMDeviceEnumerator * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IMMDeviceEnumerator * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IMMDeviceEnumerator * This); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *EnumAudioEndpoints )( + IMMDeviceEnumerator * This, + /* [annotation][in] */ + _In_ EDataFlow dataFlow, + /* [annotation][in] */ + _In_ DWORD dwStateMask, + /* [annotation][out] */ + _Out_ IMMDeviceCollection **ppDevices); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *GetDefaultAudioEndpoint )( + IMMDeviceEnumerator * This, + /* [annotation][in] */ + _In_ EDataFlow dataFlow, + /* [annotation][in] */ + _In_ ERole role, + /* [annotation][out] */ + _Out_ IMMDevice **ppEndpoint); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *GetDevice )( + IMMDeviceEnumerator * This, + /* [annotation][in] */ + _In_ LPCWSTR pwstrId, + /* [annotation][out] */ + _Out_ IMMDevice **ppDevice); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *RegisterEndpointNotificationCallback )( + IMMDeviceEnumerator * This, + /* [annotation][in] */ + _In_ IMMNotificationClient *pClient); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *UnregisterEndpointNotificationCallback )( + IMMDeviceEnumerator * This, + /* [annotation][in] */ + _In_ IMMNotificationClient *pClient); + + END_INTERFACE +} IMMDeviceEnumeratorVtbl; + +interface IMMDeviceEnumerator +{ + CONST_VTBL struct IMMDeviceEnumeratorVtbl *lpVtbl; +}; + +typedef struct IMMDeviceCollectionVtbl +{ + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IMMDeviceCollection * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IMMDeviceCollection * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IMMDeviceCollection * This); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *GetCount )( + IMMDeviceCollection * This, + /* [annotation][out] */ + _Out_ UINT *pcDevices); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Item )( + IMMDeviceCollection * This, + /* [annotation][in] */ + _In_ UINT nDevice, + /* [annotation][out] */ + _Out_ IMMDevice **ppDevice); + + END_INTERFACE +} IMMDeviceCollectionVtbl; + +interface IMMDeviceCollection +{ + CONST_VTBL struct IMMDeviceCollectionVtbl *lpVtbl; +}; + +typedef struct IMMDeviceVtbl +{ + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IMMDevice * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IMMDevice * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IMMDevice * This); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *Activate )( + IMMDevice * This, + /* [annotation][in] */ + _In_ REFIID iid, + /* [annotation][in] */ + _In_ DWORD dwClsCtx, + /* [annotation][unique][in] */ + _In_opt_ PROPVARIANT *pActivationParams, + /* [annotation][iid_is][out] */ + _Out_ void **ppInterface); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *OpenPropertyStore )( + IMMDevice * This, + /* [annotation][in] */ + _In_ DWORD stgmAccess, + /* [annotation][out] */ + _Out_ IPropertyStore **ppProperties); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *GetId )( + IMMDevice * This, + /* [annotation][out] */ + _Outptr_ LPWSTR *ppstrId); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *GetState )( + IMMDevice * This, + /* [annotation][out] */ + _Out_ DWORD *pdwState); + + END_INTERFACE +} IMMDeviceVtbl; + +interface IMMDevice +{ + CONST_VTBL struct IMMDeviceVtbl *lpVtbl; +}; + +typedef struct IMMNotificationClientVtbl +{ + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IMMNotificationClient * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IMMNotificationClient * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IMMNotificationClient * This); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *OnDeviceStateChanged )( + IMMNotificationClient * This, + /* [annotation][in] */ + _In_ LPCWSTR pwstrDeviceId, + /* [annotation][in] */ + _In_ DWORD dwNewState); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *OnDeviceAdded )( + IMMNotificationClient * This, + /* [annotation][in] */ + _In_ LPCWSTR pwstrDeviceId); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *OnDeviceRemoved )( + IMMNotificationClient * This, + /* [annotation][in] */ + _In_ LPCWSTR pwstrDeviceId); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *OnDefaultDeviceChanged )( + IMMNotificationClient * This, + /* [annotation][in] */ + _In_ EDataFlow flow, + /* [annotation][in] */ + _In_ ERole role, + /* [annotation][in] */ + _In_ LPCWSTR pwstrDefaultDeviceId); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *OnPropertyValueChanged )( + IMMNotificationClient * This, + /* [annotation][in] */ + _In_ LPCWSTR pwstrDeviceId, + /* [annotation][in] */ + _In_ const PROPERTYKEY key); + + END_INTERFACE +} IMMNotificationClientVtbl; + +interface IMMNotificationClient +{ + CONST_VTBL struct IMMNotificationClientVtbl *lpVtbl; +}; + +typedef struct IPropertyStoreVtbl +{ + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + __RPC__in IPropertyStore * This, + /* [in] */ __RPC__in REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + __RPC__in IPropertyStore * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + __RPC__in IPropertyStore * This); + + HRESULT ( STDMETHODCALLTYPE *GetCount )( + __RPC__in IPropertyStore * This, + /* [out] */ __RPC__out DWORD *cProps); + + HRESULT ( STDMETHODCALLTYPE *GetAt )( + __RPC__in IPropertyStore * This, + /* [in] */ DWORD iProp, + /* [out] */ __RPC__out PROPERTYKEY *pkey); + + HRESULT ( STDMETHODCALLTYPE *GetValue )( + __RPC__in IPropertyStore * This, + /* [in] */ __RPC__in REFPROPERTYKEY key, + /* [out] */ __RPC__out PROPVARIANT *pv); + + HRESULT ( STDMETHODCALLTYPE *SetValue )( + __RPC__in IPropertyStore * This, + /* [in] */ __RPC__in REFPROPERTYKEY key, + /* [in] */ __RPC__in REFPROPVARIANT propvar); + + HRESULT ( STDMETHODCALLTYPE *Commit )( + __RPC__in IPropertyStore * This); + + END_INTERFACE +} IPropertyStoreVtbl; + +interface IPropertyStore +{ + CONST_VTBL struct IPropertyStoreVtbl *lpVtbl; +}; + +// ----- audioclient.h ----- + +typedef enum _AUDCLNT_SHAREMODE +{ + AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_SHAREMODE_EXCLUSIVE +} AUDCLNT_SHAREMODE; + +typedef LONGLONG REFERENCE_TIME; + +typedef struct IAudioClientVtbl +{ + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IAudioClient * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IAudioClient * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IAudioClient * This); + + HRESULT ( STDMETHODCALLTYPE *Initialize )( + IAudioClient * This, + /* [annotation][in] */ + _In_ AUDCLNT_SHAREMODE ShareMode, + /* [annotation][in] */ + _In_ DWORD StreamFlags, + /* [annotation][in] */ + _In_ REFERENCE_TIME hnsBufferDuration, + /* [annotation][in] */ + _In_ REFERENCE_TIME hnsPeriodicity, + /* [annotation][in] */ + _In_ const WAVEFORMATEX *pFormat, + /* [annotation][in] */ + _In_opt_ LPCGUID AudioSessionGuid); + + HRESULT ( STDMETHODCALLTYPE *GetBufferSize )( + IAudioClient * This, + /* [annotation][out] */ + _Out_ UINT32 *pNumBufferFrames); + + HRESULT ( STDMETHODCALLTYPE *GetStreamLatency )( + IAudioClient * This, + /* [annotation][out] */ + _Out_ REFERENCE_TIME *phnsLatency); + + HRESULT ( STDMETHODCALLTYPE *GetCurrentPadding )( + IAudioClient * This, + /* [annotation][out] */ + _Out_ UINT32 *pNumPaddingFrames); + + HRESULT ( STDMETHODCALLTYPE *IsFormatSupported )( + IAudioClient * This, + /* [annotation][in] */ + _In_ AUDCLNT_SHAREMODE ShareMode, + /* [annotation][in] */ + _In_ const WAVEFORMATEX *pFormat, + /* [unique][annotation][out] */ + _Out_opt_ WAVEFORMATEX **ppClosestMatch); + + HRESULT ( STDMETHODCALLTYPE *GetMixFormat )( + IAudioClient * This, + /* [annotation][out] */ + _Out_ WAVEFORMATEX **ppDeviceFormat); + + HRESULT ( STDMETHODCALLTYPE *GetDevicePeriod )( + IAudioClient * This, + /* [annotation][out] */ + _Out_opt_ REFERENCE_TIME *phnsDefaultDevicePeriod, + /* [annotation][out] */ + _Out_opt_ REFERENCE_TIME *phnsMinimumDevicePeriod); + + HRESULT ( STDMETHODCALLTYPE *Start )( + IAudioClient * This); + + HRESULT ( STDMETHODCALLTYPE *Stop )( + IAudioClient * This); + + HRESULT ( STDMETHODCALLTYPE *Reset )( + IAudioClient * This); + + HRESULT ( STDMETHODCALLTYPE *SetEventHandle )( + IAudioClient * This, + /* [in] */ HANDLE eventHandle); + + HRESULT ( STDMETHODCALLTYPE *GetService )( + IAudioClient * This, + /* [annotation][in] */ + _In_ REFIID riid, + /* [annotation][iid_is][out] */ + _Out_ void **ppv); + + END_INTERFACE +} IAudioClientVtbl; + +interface IAudioClient +{ + CONST_VTBL struct IAudioClientVtbl *lpVtbl; +}; + +typedef struct IAudioCaptureClientVtbl +{ + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IAudioCaptureClient * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IAudioCaptureClient * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IAudioCaptureClient * This); + + HRESULT ( STDMETHODCALLTYPE *GetBuffer )( + IAudioCaptureClient * This, + /* [annotation][out] */ + _Outptr_result_buffer_(_Inexpressible_("*pNumFramesToRead * pFormat->nBlockAlign")) BYTE **ppData, + /* [annotation][out] */ + _Out_ UINT32 *pNumFramesToRead, + /* [annotation][out] */ + _Out_ DWORD *pdwFlags, + /* [annotation][unique][out] */ + _Out_opt_ UINT64 *pu64DevicePosition, + /* [annotation][unique][out] */ + _Out_opt_ UINT64 *pu64QPCPosition); + + HRESULT ( STDMETHODCALLTYPE *ReleaseBuffer )( + IAudioCaptureClient * This, + /* [annotation][in] */ + _In_ UINT32 NumFramesRead); + + HRESULT ( STDMETHODCALLTYPE *GetNextPacketSize )( + IAudioCaptureClient * This, + /* [annotation][out] */ + _Out_ UINT32 *pNumFramesInNextPacket); + + END_INTERFACE +} IAudioCaptureClientVtbl; + +interface IAudioCaptureClient +{ + CONST_VTBL struct IAudioCaptureClientVtbl *lpVtbl; +}; + +typedef interface IMMEndpoint IMMEndpoint; + + typedef struct IMMEndpointVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IMMEndpoint * This, + /* [in] */ REFIID riid, + /* [annotation][iid_is][out] */ + _COM_Outptr_ void **ppvObject); + + ULONG ( STDMETHODCALLTYPE *AddRef )( + IMMEndpoint * This); + + ULONG ( STDMETHODCALLTYPE *Release )( + IMMEndpoint * This); + + /* [helpstring][id] */ HRESULT ( STDMETHODCALLTYPE *GetDataFlow )( + IMMEndpoint * This, + /* [annotation][out] */ + _Out_ EDataFlow *pDataFlow); + + END_INTERFACE + } IMMEndpointVtbl; + + interface IMMEndpoint + { + CONST_VTBL struct IMMEndpointVtbl *lpVtbl; + }; + +#define DEVICE_STATE_ACTIVE 0x00000001 +#define DEVICE_STATE_DISABLED 0x00000002 +#define DEVICE_STATE_NOTPRESENT 0x00000004 +#define DEVICE_STATE_UNPLUGGED 0x00000008 +#define DEVICE_STATEMASK_ALL 0x0000000f + +#endif // _CNFA_WASAPI_UTILS_H \ No newline at end of file diff --git a/app/src/nativecode/cnfa/CNFA_winmm.c b/app/src/nativecode/cnfa/CNFA_winmm.c new file mode 100644 index 0000000..a104c62 --- /dev/null +++ b/app/src/nativecode/cnfa/CNFA_winmm.c @@ -0,0 +1,258 @@ +//Copyright 2015-2020 <>< Charles Lohr under the ColorChord License, MIT/x11 license or NewBSD Licenses. + +#include +#include "CNFA.h" +#include "os_generic.h" +#include +#include +#include +#include + +//Include -lwinmm, or, C:/windows/system32/winmm.dll + +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) \ + || defined(_WIN32) || defined(_WIN64) +#ifndef strdup +#define strdup _strdup +#endif +#endif + +#if defined(WIN32) && !defined( TCC ) +#pragma comment(lib,"winmm.lib") +#endif + +#define BUFFS 3 + +struct CNFADriverWin +{ + //Standard header - must remain. + void (*CloseFn)( void * object ); + int (*StateFn)( void * object ); + CNFACBType callback; + short channelsPlay; + short channelsRec; + int spsPlay; + int spsRec; + void * opaque; + + char * sInputDev; + char * sOutputDev; + + int buffer; + int isEnding; + int GOBUFFRec; + int GOBUFFPlay; + + int recording; + int playing; + + HWAVEIN hMyWaveIn; + HWAVEOUT hMyWaveOut; + WAVEHDR WavBuffIn[BUFFS]; + WAVEHDR WavBuffOut[BUFFS]; +}; + + +static struct CNFADriverWin * w; + +void CloseCNFAWin( void * v ) +{ + struct CNFADriverWin * r = (struct CNFADriverWin *)v; + int i; + + if( r ) + { + if( r->hMyWaveIn ) + { + waveInStop(r->hMyWaveIn); + waveInReset(r->hMyWaveIn); + for ( i=0;ihMyWaveIn,&(r->WavBuffIn[i]),sizeof(WAVEHDR)); + free ((r->WavBuffIn[i]).lpData); + } + waveInClose(r->hMyWaveIn); + } + + if( r->hMyWaveOut ) + { + waveOutPause(r->hMyWaveOut); + waveOutReset(r->hMyWaveOut); + + for ( i=0;ihMyWaveIn,&(r->WavBuffOut[i]),sizeof(WAVEHDR)); + free ((r->WavBuffOut[i]).lpData); + } + waveInClose(r->hMyWaveIn); + waveOutClose(r->hMyWaveOut); + } + free( r ); + } +} + +int CNFAStateWin( void * v ) +{ + struct CNFADriverWin * soundobject = (struct CNFADriverWin *)v; + + return soundobject->recording | (soundobject->playing?2:0); +} + +void CALLBACK HANDLEMIC(HWAVEIN hwi, UINT umsg, DWORD dwi, DWORD hdr, DWORD dwparm) +{ + int ctr; + int ob; + long cValue; + unsigned int maxWave=0; + + if (w->isEnding) return; + + switch (umsg) + { + case MM_WIM_OPEN: + printf( "Mic Open.\n" ); + w->recording = 1; + break; + + case MM_WIM_DATA: + ob = (w->GOBUFFRec+(BUFFS))%BUFFS; + w->callback( (struct CNFADriver*)w, 0, (short*)(w->WavBuffIn[w->GOBUFFRec]).lpData, 0, w->buffer ); + waveInAddBuffer(w->hMyWaveIn,&(w->WavBuffIn[w->GOBUFFRec]),sizeof(WAVEHDR)); + w->GOBUFFRec = ( w->GOBUFFRec + 1 ) % BUFFS; + break; + } +} + + +void CALLBACK HANDLESINK(HWAVEIN hwi, UINT umsg, DWORD dwi, DWORD hdr, DWORD dwparm) +{ + int ctr; + long cValue; + unsigned int maxWave=0; + + if (w->isEnding) return; + + switch (umsg) + { + case MM_WOM_OPEN: + printf( "Sink Open.\n" ); + w->playing = 1; + break; + + case MM_WOM_DONE: + w->callback( (struct CNFADriver*)w, (short*)(w->WavBuffOut[w->GOBUFFPlay]).lpData, 0, w->buffer, 0 ); + waveOutWrite( w->hMyWaveOut, &(w->WavBuffOut[w->GOBUFFPlay]),sizeof(WAVEHDR) ); + w->GOBUFFPlay = ( w->GOBUFFPlay + 1 ) % BUFFS; + break; + } +} + + +static struct CNFADriverWin * InitWinCNFA( struct CNFADriverWin * r ) +{ + int i; + WAVEFORMATEX wfmt; + memset( &wfmt, 0, sizeof(wfmt) ); + printf ("WFMT Size (debugging temp for TCC): %llu\n", sizeof(wfmt) ); + printf( "WFMT: %d %d %d\n", r->channelsRec, r->spsRec, r->spsRec * r->channelsRec ); + w = r; + + wfmt.wFormatTag = WAVE_FORMAT_PCM; + wfmt.nChannels = r->channelsRec; + wfmt.nAvgBytesPerSec = r->spsRec * r->channelsRec; + wfmt.nBlockAlign = r->channelsRec * 2; + wfmt.nSamplesPerSec = r->spsRec; + wfmt.wBitsPerSample = 16; + wfmt.cbSize = 0; + + long dwdeviceR, dwdeviceP; + dwdeviceR = r->sInputDev?atoi(r->sInputDev):WAVE_MAPPER; + dwdeviceP = r->sOutputDev?atoi(r->sOutputDev):WAVE_MAPPER; + + if( r->channelsRec ) + { + printf( "In Wave Devs: %d; WAVE_MAPPER: %d; Selected Input: %ld\n", waveInGetNumDevs(), WAVE_MAPPER, dwdeviceR ); + int p = waveInOpen(&r->hMyWaveIn, dwdeviceR, &wfmt, (intptr_t)(&HANDLEMIC), 0, CALLBACK_FUNCTION); + if( p ) + { + fprintf( stderr, "Error performing waveInOpen. Received code: %d\n", p ); + } + printf( "waveInOpen: %d\n", p ); + for ( i=0;iWavBuffIn[i]), 0, sizeof(r->WavBuffIn[i]) ); + (r->WavBuffIn[i]).dwBufferLength = r->buffer*2*r->channelsRec; + (r->WavBuffIn[i]).dwLoops = 1; + (r->WavBuffIn[i]).lpData=(char*) malloc(r->buffer*r->channelsRec*2); + printf( "buffer gen size: %d: %p\n", r->buffer*r->channelsRec*2, (r->WavBuffIn[i]).lpData ); + p = waveInPrepareHeader(r->hMyWaveIn,&(r->WavBuffIn[i]),sizeof(WAVEHDR)); + printf( "WIPr: %d\n", p ); + waveInAddBuffer(r->hMyWaveIn,&(r->WavBuffIn[i]),sizeof(WAVEHDR)); + printf( "WIAr: %d\n", p ); + } + p = waveInStart(r->hMyWaveIn); + if( p ) + { + fprintf( stderr, "Error performing waveInStart. Received code %d\n", p ); + } + } + + wfmt.nChannels = r->channelsPlay; + wfmt.nAvgBytesPerSec = r->spsPlay * r->channelsPlay; + wfmt.nBlockAlign = r->channelsPlay * 2; + wfmt.nSamplesPerSec = r->spsPlay; + + if( r->channelsPlay ) + { + printf( "Out Wave Devs: %d; WAVE_MAPPER: %d; Selected Input: %ld\n", waveOutGetNumDevs(), WAVE_MAPPER, dwdeviceP ); + int p = waveOutOpen( &r->hMyWaveOut, dwdeviceP, &wfmt, (intptr_t)(void*)(&HANDLESINK), (intptr_t)r, CALLBACK_FUNCTION); + if( p ) + { + fprintf( stderr, "Error performing waveOutOpen. Received code: %d\n", p ); + } + printf( "waveOutOpen: %d\n", p ); + for ( i=0;iWavBuffOut[i]), 0, sizeof(r->WavBuffOut[i]) ); + (r->WavBuffOut[i]).dwBufferLength = r->buffer*2*r->channelsPlay; + (r->WavBuffOut[i]).dwLoops = 1; + int size = r->buffer*r->channelsPlay*2; + char * buf = (r->WavBuffOut[i]).lpData=(char*) malloc(size); + memset( buf, 0, size ); + p = waveOutPrepareHeader(r->hMyWaveOut,&(r->WavBuffOut[i]),sizeof(WAVEHDR)); + waveOutWrite( r->hMyWaveOut, &(r->WavBuffOut[i]),sizeof(WAVEHDR)); + } + } + + return r; +} + + + +void * InitCNFAWin( CNFACBType cb, const char * your_name, int reqSPSPlay, int reqSPSRec, int reqChannelsPlay, int reqChannelsRec, int sugBufferSize, const char * outputSelect, const char * inputSelect, void * opaque ) +{ + struct CNFADriverWin * r = (struct CNFADriverWin *)malloc( sizeof( struct CNFADriverWin ) ); + memset( r, 0, sizeof(*r) ); + r->CloseFn = CloseCNFAWin; + r->StateFn = CNFAStateWin; + r->callback = cb; + r->opaque = opaque; + r->spsPlay = reqSPSPlay; + r->spsRec = reqSPSRec; + r->channelsPlay = reqChannelsPlay; + r->channelsRec = reqChannelsRec; + r->buffer = sugBufferSize; + r->sInputDev = inputSelect?strdup(inputSelect):0; + r->sOutputDev = outputSelect?strdup(outputSelect):0; + + r->recording = 0; + r->playing = 0; + r->isEnding = 0; + r->GOBUFFPlay = 0; + r->GOBUFFRec = 0; + + return InitWinCNFA(r); +} + +REGISTER_CNFA( WinCNFA, 10, "WIN", InitCNFAWin ); + diff --git a/app/src/nativecode/cnfa/LICENSE b/app/src/nativecode/cnfa/LICENSE new file mode 100644 index 0000000..7b1cc56 --- /dev/null +++ b/app/src/nativecode/cnfa/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 <>< Charles Lohr + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/app/src/nativecode/cnfa/Makefile b/app/src/nativecode/cnfa/Makefile new file mode 100644 index 0000000..1622510 --- /dev/null +++ b/app/src/nativecode/cnfa/Makefile @@ -0,0 +1,34 @@ +.PHONY: all shared shared-example wave_player clean + +all: example wav_player + +os_generic.h: + wget https://raw.githubusercontent.com/cntools/rawdraw/master/os_generic.h + +LDFLAGS = -lasound -lpthread +ifeq "$(shell pkgconf --exists pulse && echo 'found' )" "found" +PULSE ?= "YES" +else +PULSE ?= "NO" +endif +ifeq "$(PULSE)" "YES" +LDFLAGS += -lpulse -DPULSEAUDIO +endif + +example : example.c os_generic.h + $(CC) -o $@ $^ $(LDFLAGS) -lm + +wav_player: os_generic.h + make -C wave_player PULSE=$(PULSE) + +shared : os_generic.h + $(CC) CNFA.c -shared -fpic -o libCNFA.so -DCNFA_IMPLEMENTATION -DBUILD_DLL \ + $(LDFLAGS) + +shared-example : example.c shared + $(CC) -L. -Wl,-rpath=. -o example example.c -DUSE_SHARED -lm -lCNFA + +clean : + rm -rf *.o *~ example libCNFA.so + make -C wave_player clean + diff --git a/app/src/nativecode/cnfa/README.md b/app/src/nativecode/cnfa/README.md new file mode 100644 index 0000000..f48f43e --- /dev/null +++ b/app/src/nativecode/cnfa/README.md @@ -0,0 +1,75 @@ +# cnfa +CN's foundational audio drivers + +See CNFA.h for more help and use info. + +This toolset is 100% based around sound callbacks. + +You get three functions: + +```C +struct CNFADriver* CNFAInit(const char* driver_name, + const char* your_name, + CNFACBType cb, + int reqSPSPlay, + int reqSPSRec, + int reqChannelsPlay, + int reqChannelsRec, + int sugBufferSize, + const char* outputSelect, + const char* inputSelect, + void* opaque) + +// Returns bitmask: 1 if mic recording, 2 if playback running, 3 if both running. +int CNFAState(struct CNFADriver* cnfaobject) + +void CNFAClose(struct CNFADriver* cnfaobject) +``` + +Then it goes and calls a callback function, the `CNFACBType cb` parameter. This can feed you new frames, or you can pass frames back in. +`framesp` is the size of one channel of the output buffer in samples. If there are multiple channels they should be interleaved i.e. L:R:L:R... +`framerp` is the size of one channel of the input buffer in samples. Works the same way as the output buffer. +You can obtain the number of input/output channels from `sd->channelsRec` and `sd->channelsPlay` respectively. + +```C +void Callback(struct CNFADriver* sd, short* out, short* in, int framesp, int framesr) +{ + int i; + for( i = 0; i < framesr * sd->channelsRec; i++ ) + short value = in[i]; + for( i = 0; i < framesp * sd->channelsPlay; i++ ) + out[i] = 0; //Send output frames. +} +``` + +There are two examples in this repository, [example.c](example.c) and [wave_player.c](wave_player/wav_player.c). Both of these show examples of using +CNFA to output sound. For use of CNFA for input see [colorchord](https://github.com/cnlohr/colorchord) + +### Building .DLL and .SO files +If you would like to use CNFA in a project where using a DLL or SO file is more practical, you can easily build those files. The below steps are for the Clang & GGC compilers, but others like TCC should work fine as well, just have not been tested. + +NOTE: In order for functions to be exported, you'll need to make sure `-DBUILD_DLL` is specified! + +Parts of CNFA rely on [rawdraw](https://github.com/cntools/rawdraw), so make sure to clone this repo somewhere and insert the path to it in the `[RAWDRAW PATH]` space below: + +**Don't forget to install all libraries' headers!** For Linux, at least these packages: `libasound2-dev`, `libpulse-dev` + +Windows build: +```PS +& "C:\Program Files\LLVM\bin\clang.exe" CNFA.c -shared -o CNFA.dll -DWINDOWS -DCNFA_IMPLEMENTATION -DBUILD_DLL -D_CRT_SECURE_NO_WARNINGS -I"[RAWDRAW PATH]" -lmmdevapi -lavrt -lole32 +``` + +Linux build: +```Bash +make shared-example +``` +This will build the shared library as `libCNFA.so` and will build the example +project to link against it. You can run the example project with `./example`. +The makefile will try and copy the `os_generic.h` header from +[rawdraw](https://github.com/cntools/rawdraw) +automatically using `wget`. + +The command to build simply build the shared library is: +```Bash +gcc CNFA.c -shared -fpic -o CNFA.so -DCNFA_IMPLEMENTATION -DBUILD_DLL -I"[RAWDRAW PATH]" -lasound -lpulse -lpthread +``` diff --git a/app/src/nativecode/cnfa/example.c b/app/src/nativecode/cnfa/example.c new file mode 100644 index 0000000..73f5c4d --- /dev/null +++ b/app/src/nativecode/cnfa/example.c @@ -0,0 +1,65 @@ +#include +#include + +// If using the shared library, don't define CNFA_IMPLEMENTATION +// (it's already in the library). +#ifndef USE_SHARED +#define CNFA_IMPLEMENTATION +#endif +#include "CNFA.h" + +#define RUNTIME 500000 + +double omega = 0; +int totalframesr = 0; +int totalframesp = 0; + +void Callback( struct CNFADriver * sd, short * out, short * in, int framesp, int framesr ) +{ + int i; + + totalframesr += framesr; + totalframesp += framesp; + + int channels = sd->channelsPlay; + for( i = 0; i < framesp; i++ ) + { + // Shift phase, so we run at 440 Hz (A4) + omega += ( 3.14159 * 2 * 440. ) / sd->spsPlay; + + // Make the 440 Hz tone at 10% volume and convert to short. + short value = sin( omega ) * 0.1 * 32767; + + int c; + for( c = 0; c < channels; c++ ) + { + *(out++) = value; + } + } +} + + +struct CNFADriver * cnfa; + +int main( int argc, char ** argv ) +{ + cnfa = CNFAInit( + + //"PULSE", + "ALSA", //You can select a plaback driver, or use 0 for default. + //0, //default + "cnfa_example", Callback, + 48000, //Requested samplerate for playback + 48000, //Requested samplerate for recording + 2, //Number of playback channels. + 2, //Number of record channels. + 1024, //Buffer size in frames. + 0, //Could be a string, for the selected input device - but 0 means default. + 0, //Could be a string, for the selected output device - but 0 means default. + 0 // 'opaque' value if the driver wanted it. + ); + + sleep( RUNTIME ); + printf( "Received %d (%d per sec) frames\nSent %d (%d per sec) frames\n", totalframesr, totalframesr/RUNTIME, totalframesp, totalframesp/RUNTIME ); +} + diff --git a/app/src/nativecode/cnfa/wave_player/Makefile b/app/src/nativecode/cnfa/wave_player/Makefile new file mode 100644 index 0000000..aeb5162 --- /dev/null +++ b/app/src/nativecode/cnfa/wave_player/Makefile @@ -0,0 +1,27 @@ +.PHONY: clean + +C_SRCS = wav_player.c +OUT := wav_player + +CFLAGS = -O2 -g +LDFLAGS = -lasound -lpthread -lm +CC ?= gcc -std=c99 +ifeq "$(shell pkgconf --exists pulse && echo 'found' )" "found" +PULSE ?= "YES" +else +PULSE ?= "NO" +endif +ifeq "$(PULSE)" "YES" +LDFLAGS += -lpulse +endif + +OBJS := $(C_SRCS:.c=.o) + +$(OUT): $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $(OUT) + +clean: + rm $(OBJS) $(OUT) + +.c.o: + $(CC) $(CFLAGS) -c $< -o $@ diff --git a/app/src/nativecode/cnfa/wave_player/wavDefs.h b/app/src/nativecode/cnfa/wave_player/wavDefs.h new file mode 100644 index 0000000..60b715d --- /dev/null +++ b/app/src/nativecode/cnfa/wave_player/wavDefs.h @@ -0,0 +1,117 @@ +/* + * wavDefs.h Samuel Ellicott - 9-3-13 + * defines basic features of PCM encoded wav files + */ + +#ifndef _WAV_DEFS_H_ +#define _WAV_DEFS_H_ + +#include + +//#define ABSOLUTE 1 +#ifdef ABSOLUTE + + //absolute offsets PCM audacity files only + // name offset description + //RIFF chunk descriptor + #define CHUNK_ID 0 //start of the riff chunk + #define CHUNK_SIZE 4 //size of the file minus the first 2 entries + #define FORMAT 8 //describes the format aka. WAVE + + //fmt sub-chunk descriptor + #define FMT_CHUNK_ID 12 //start of the fmt sub chunk + #define FMT_CHUNK_SIZE 16 //length of the rest of the sub-chunk. for PCM should be 16 + #define AUDIO_FORMAT 20 //audio format - for PCM should be 1 (unsigned char) + #define NUM_CHANNELS 22 //the number of channels 1 or 2 (unsigned char) + #define SAMPLE_RATE 24 //sample rate 8000, 44100, or 48000 typical in hz + #define BIT_RATE 28 //bit rate - (SampleRate * NumChannels * BitsPerSample)/8 + #define BLOCK_ALIGN 32 //NumChannels * BitsPerSample/8 (unsigned char) + //1 - 8 bit mono, 2 - 8 bit stereo/16 bit mono, 4 - 16 bit stereo + #define BITS_PER_SAMPLE 34 //bits per channel 8 or 16 (unsigned char) + + + //fmt sub-chunk descriptor + #define CHUNK_ID_1 12 //start of the fmt sub chunk + #define CHUNK_SIZE_1 16 //length of the rest of the sub-chunk. for PCM should be 1 + + //info data sub-chunk descriptor + //if Wav_Data.is_info == 1 than this is info chunk + //if Wav_Data.is_info == 0 than this is data chunk + #define _CHUNK_ID 36 //start of data sub chunk + #define _CHUNK_SIZE 40 //chunk size of sub chunk + #define _CHUNK_DATA 44 //data in chunk + +#else + //general chunk descriptor + #define CHUNK_ID 0 //start of the riff chunk + #define CHUNK_SIZE 4 //size of the file minus the first 2 entries + #define FORMAT 8 //describes the format for RIFF and LIST chunks + #define CHUNK_DATA 8 //start of data for other chunks + #define CHUNK_ID_LEN 5 //length of chunk ID string (including null char) + + //fmt sub-chunk descriptor + #define FMT_CHUNK_ID 0 //start of the fmt sub chunk + #define FMT_CHUNK_SIZE 4 //length of the rest of the sub-chunk. for PCM should be 16 + #define AUDIO_FORMAT 8 //audio format - for PCM should be 1 (unsigned char) + #define NUM_CHANNELS 10 //the number of channels 1 or 2 (unsigned char) + #define SAMPLE_RATE 12 //sample rate 8000, 44100, or 48000 typical in hz + #define BIT_RATE 16 //bit rate - (SampleRate * NumChannels * BitsPerSample)/8 + #define BLOCK_ALIGN 20 //NumChannels * BitsPerSample/8 (unsigned char) + //1 - 8 bit mono, 2 - 8 bit stereo/16 bit mono, 4 - 16 bit stereo + #define BITS_PER_SAMPLE 22 //bits per channel 8 or 16 (unsigned char) + +#endif //ABSOLUTE + +#define MAX_TAG_SIZE 100 //defines the maximum number of characters to be allocated for any info string + +/* + * data on the optional INFO data chunk contains pointers to the following info: + * title + * author + * genre + */ +typedef struct WaveInfoChunk{ + uint8_t is_info; //1 for info data, 0 for no info data + uint32_t info_offset; //the file offset for the info chunk + uint32_t info_len; //length of the info chunk + + //begin heap pointers + char *title; + char *artist; + char *genre; + char *creation_date; +} WaveInfoChunk; +/* + * data structure containing the data neccesarry for PCM audio playback + */ +typedef struct WaveFmtChunk{ + uint32_t fmt_len; //fmt size: 16 for pcm + uint16_t audio_format; //1 = PCM + uint16_t num_channels; //1 for mono, 2 for stereo + uint32_t sample_rate; //44100 (CD), 48000 (DAT) + uint32_t byte_rate; //SampleRate * NumChannels * BitsPerSample/8 + uint16_t block_align; //1 - 8 bit mono + //2 - 8 bit stereo/16 bit mono + //4 - 16 bit stereo + uint16_t bits_per_sample; //8 or 16 + uint8_t bytes_per_sample; +} WaveFmtChunk; + +typedef struct WaveDataChunk{ + uint32_t data_offset; //the file offset for data chunk + uint32_t data_size; //data size in bytes + uint32_t current_offset; //current offset in file in bytes + uint16_t num_samples; //number of samples + uint16_t samples_left; //number of samples left to read +} WaveDataChunk; + +/* + * struct all the pertinent information for the wav file + */ +typedef struct WaveHeaderChunk{ + struct WaveFmtChunk fmt; //the wave format chunk + struct WaveInfoChunk info; //the info metadata chunk + struct WaveDataChunk data; +} WaveHeaderChunk; + +#endif //_WAV_DEFS_H_ \ No newline at end of file diff --git a/app/src/nativecode/cnfa/wave_player/wav_player.c b/app/src/nativecode/cnfa/wave_player/wav_player.c new file mode 100644 index 0000000..d22ee29 --- /dev/null +++ b/app/src/nativecode/cnfa/wave_player/wav_player.c @@ -0,0 +1,389 @@ +/* + * Sam Ellicott - 09-06-22 + * CNFA demo wave file player + */ +#include +#include +#include +#include + +#include "wavDefs.h" + +// wave file player function prototypes +int loadHeader(FILE *file, WaveHeaderChunk *hdr); +int loadInfo(FILE *file, WaveHeaderChunk *hdr); +int printInfo(FILE *file); +int readData(FILE *file, WaveHeaderChunk *hdr, void* buff, int buff_len); +void freeData(WaveHeaderChunk *hdr); + +/* ------------------------------------------ Main Application Code ------------------------------*/ +#define BUFF_SIZE 2048 +// If using the shared library, don't define CNFA_IMPLEMENTATION +// (it's already in the library). +#ifndef USE_SHARED +#define CNFA_IMPLEMENTATION +#endif +#include "../CNFA.h" + +int totalframesr = 0; +int totalframesp = 0; + +FILE* wav_file; +WaveHeaderChunk hdr; +struct CNFADriver * cnfa; +short buff[BUFF_SIZE]; +int is_done; + +void Callback( struct CNFADriver * sd, short * out, short * in, int framesp, int framesr ) +{ + int* is_done_ptr = (int*) sd->opaque; + const int output_channels = sd->channelsPlay; + const int file_channels = hdr.fmt.num_channels; + const int output_buff_sz = framesp * output_channels; + int br = 0; + + // if we have already ended the file, then clear the buffer and exit function + if(*is_done_ptr) { + memset(out, 0, sizeof(short) * output_buff_sz); + return; + } + + totalframesr += framesr; + totalframesp += framesp; + + if (output_channels == file_channels) { + int read_buff_sz = framesp * sd->channelsPlay; + br = readData(wav_file, &hdr, out, read_buff_sz); + } + else if (output_channels > file_channels) { + int read_buff_sz = framesp; + + // exit loop if we are done filling the output buffer or we are at the end of file + int samples_remaining = read_buff_sz; + while (samples_remaining > 0 && br >= 0) { + int read_sz = (samples_remaining > BUFF_SIZE) ? BUFF_SIZE : samples_remaining; + br = readData(wav_file, &hdr, buff, read_sz); + // duplicate data on left and right channels + for (int i = 0; i < read_buff_sz; ++i){ + out[2*i] = buff[i]; + out[2*i+1] = buff[i]; + } + samples_remaining -= br; + } + } + else { + printf("what are you doing? mono sound output?\n"); + } + + // end of file + if (br < 0) { + printf("End of wave file: setting flag\n"); + *is_done_ptr = 1; + } +} + +int main (int nargs, char** args) { + const char* filename; + // if there is a file given on the command line play it. + if(nargs >= 2) { + filename = args[1]; + } + else { + printf("\nError, no input file\nUseage %s \n", args[0]); + return 1; + } + wav_file = fopen(filename, "r"); + + printInfo(wav_file); + printf("\n\n"); + + printf("loading file\n"); + loadHeader(wav_file, &hdr); + + printf("playing file\n"); + + is_done = 0; + cnfa = CNFAInit( + NULL, // String, for the driver "PULSE", "WASAPI" (output only) - NULL means default. + "cnfa_example", // Name of program to audio driver + Callback, // CNFA callback function handle + hdr.fmt.sample_rate, // Requested samplerate for playback + hdr.fmt.sample_rate, // Requested samplerate for record + 2, // Number of playback channels. + 2, // Number of record channels. + 1024, // Buffer size in frames. + NULL, // String, for the selected input device - NULL means default. + NULL, // String, for the selected output device - NULL means default. + &is_done // pass an integer as an "opaque" object so that CNFA can close + ); + + int runtime = 0; + const char* spin_glyph = "-\\|/"; + const char* glyph = spin_glyph; + int i = 0; + while (!is_done){ + sleep(1); + ++runtime; + printf("\r %c ", *glyph++); + fflush(stdout); + if (!*glyph) { + glyph = spin_glyph; + } + } + + CNFAClose(cnfa); + fclose(wav_file); + + printf( "Received %d (%d per sec) frames\nSent %d (%d per sec) frames\n", + totalframesr, totalframesr/runtime, // recorded samples, recorded samples/sec + totalframesp, totalframesp/runtime ); // outputted samples, outputted samples/sec + + return 0; +} + +/* ------------------------------------------ Wave File Player Code ------------------------------*/ +int printInfo(FILE *file){ + WaveHeaderChunk wav_data; + + if(loadHeader(file, &wav_data)!=0){ + printf("file invalid\n\r"); + return 1; + } + //print file data + printf("\n"); + printf("Audio Format: %i \n", wav_data.fmt.audio_format); + printf("Channels: %u \n", wav_data.fmt.num_channels); + printf("Sample Rate: %lu \n", wav_data.fmt.sample_rate); + printf("Block Alignment (bytes): %u \n", wav_data.fmt.block_align); + printf("Bits-per-Sample: %u \n", wav_data.fmt.bits_per_sample); + + if(loadInfo(file, &wav_data)==0){//if there is data show it + printf("\n"); + printf("Track name: %s \n\r", wav_data.info.title); + printf("Artist: %s \n\r", wav_data.info.artist); + printf("Genre: %s \n\r", wav_data.info.genre); + printf("Creation date: %s \n\r", wav_data.info.creation_date); + } + + freeData(&wav_data); + return 0; +} + +int loadHeader(FILE *file, WaveHeaderChunk *hdr){ + char chunk_id[CHUNK_ID_LEN]; + uint32_t chunk_offset=0;//keeps track of position in file - referenced to the chunk id + uint32_t chunk_len=0;//the length of the current chunk + uint32_t br; + + + //check if the file is valid first + if(file==NULL){ + printf("Could not open file \n"); + return 1; + } + // check that the pointer is valid + if (!hdr) { + return 2; + } + // clear pointers + hdr->info.creation_date = NULL; + hdr->info.genre = NULL; + hdr->info.artist = NULL; + hdr->info.title = NULL; + + //look for RIFF/WAVE file header + fseek(file, CHUNK_ID, SEEK_SET); + fgets(chunk_id, CHUNK_ID_LEN, file); + if(strncmp(chunk_id, "RIFF", 4)!=0){ + printf("File is not a wav file\n"); + return 1; + } + fseek(file, FORMAT, SEEK_SET); + fgets(chunk_id, CHUNK_ID_LEN, file); + if(strncmp(chunk_id, "WAVE", 4)!=0){ + printf("File is not a wav file \n"); + return 1; + } + chunk_offset+=CHUNK_DATA+4;//add the file offset of the begining of the first chunk + + //must be WAVE look for chunks + do{ + //get the chunk id + fseek(file, chunk_offset+CHUNK_ID, SEEK_SET); + fgets(chunk_id, CHUNK_ID_LEN, file); + + //get the chunk length + fseek(file, chunk_offset+CHUNK_SIZE, SEEK_SET); + br = fread(&chunk_len, sizeof(uint32_t), 1, file);//get length + + //check for fmt chunk + if(strncmp(chunk_id, "fmt ", 4)==0){//if format section + //get the format section of the file + fseek(file, chunk_offset+AUDIO_FORMAT, SEEK_SET); + br = fread(&hdr->fmt.audio_format, sizeof(uint16_t), 1, file); + + //number of channels + fseek(file, chunk_offset+NUM_CHANNELS, SEEK_SET); + br = fread(&hdr->fmt.num_channels, sizeof(uint16_t), 1, file); + + //sample rate + fseek(file, chunk_offset+SAMPLE_RATE, SEEK_SET); + br = fread(&hdr->fmt.sample_rate, sizeof(uint32_t), 1, file); + + //bits per channel tells if mono, stereo, or hifi stereo + fseek(file, chunk_offset+BLOCK_ALIGN, SEEK_SET); + br = fread(&hdr->fmt.block_align, sizeof(uint16_t), 1, file); + + //bits per channel 8 or 16 + fseek(file, chunk_offset+BITS_PER_SAMPLE, SEEK_SET); + br = fread(&hdr->fmt.bits_per_sample, sizeof(uint16_t), 1, file); + + hdr->fmt.bytes_per_sample=hdr->fmt.bits_per_sample/8; + } + else if(strncmp(chunk_id, "data", 4)==0){//if chunk is of data type + // grab data chunk location and size + hdr->data.data_offset = chunk_offset; + hdr->data.data_size = chunk_len; + hdr->data.num_samples = (uint16_t) chunk_len/hdr->fmt.bytes_per_sample; + hdr->data.samples_left = hdr->data.num_samples; + hdr->data.current_offset = chunk_offset+CHUNK_DATA; + } + else if(strncmp(chunk_id, "LIST", 4)==0){//if chunk is of LIST type + //check if it is of info type + fseek(file, chunk_offset+CHUNK_DATA, SEEK_SET); + fgets(chunk_id, 5, file); + if(strncmp(chunk_id, "INFO", 4)==0){ + // grab info location and size + hdr->info.info_offset = chunk_offset+4; + hdr->info.info_len = chunk_len; + hdr->info.is_info = 1; + } + else{ + // no file info (artist, genre, etc) + hdr->info.is_info = 0; + } + } + else if(strncmp(chunk_id, "data", 4)!=0){ + //get length of chunk + fseek(file, chunk_offset+CHUNK_SIZE, SEEK_SET); + br = fread(&chunk_len, 2, 1, file);//get length + } + + //done reading skip chunk + chunk_offset=chunk_offset+chunk_len+CHUNK_DATA; + fseek(file, chunk_offset, SEEK_SET); + + } while(fgets(chunk_id, 5, file)!=NULL);//loop until the end of the file is reached + + return 0; +} + +/* + * reads the info chunk if available and puts data into the WaveHeader structure provided + */ +int loadInfo(FILE *file, WaveHeaderChunk *hdr){ + char chunk_id[CHUNK_ID_LEN]; + uint32_t chunk_offset=hdr->info.info_offset+CHUNK_DATA;//current file position + uint32_t chunk_len=0;//the length of the current chunk + uint32_t tag_len=0; + uint32_t br; + + if(hdr->info.is_info==0){//if no data + printf("No artist information is available for this wav file \n"); + return 1;//no data + } + + fseek(file, chunk_offset, SEEK_SET);//go to chunk data + while(chunk_offset-4 - hdr->info.info_offset < hdr->info.info_len){//while in info chunk + //get the chunk id + fseek(file, chunk_offset+CHUNK_ID, SEEK_SET); + fgets(chunk_id, 5, file); + //get the chunk length + fseek(file, chunk_offset+CHUNK_SIZE, SEEK_SET); + br = fread(&chunk_len, sizeof(uint32_t), 1, file);//get length + //make sure the length of the tag isn't to big + if(chunk_len > MAX_TAG_SIZE){//if too big + tag_len=MAX_TAG_SIZE; + } + else{ + tag_len=chunk_len; + } + + //go to tag information + fseek(file, chunk_offset+CHUNK_DATA, SEEK_SET); + if(strncmp(chunk_id, "INAM", 4)==0){ // if is name chunk + hdr->info.title=malloc(tag_len+1); // allocate memory for tag + fgets(hdr->info.title, tag_len, file); // read tag + + } + else if(strncmp(chunk_id, "IART", 4)==0){ // if is artist chunk + hdr->info.artist=malloc(tag_len+1); // allocate memory for tag + fgets(hdr->info.artist, tag_len, file); // read tag + + } + else if(strncmp(chunk_id, "IGNR", 4)==0){ // if is genre chunk + hdr->info.genre=malloc(tag_len+1); // allocate memory for tag + fgets(hdr->info.genre, tag_len, file); // read tag + + } + else if(strncmp(chunk_id, "ICRD", 4)==0){ // if is creation date chunk + hdr->info.creation_date=malloc(tag_len+1); // allocate memory for tag + fgets(hdr->info.creation_date, tag_len, file); // read tag + + } + //done reading skip chunk + chunk_offset=chunk_offset+chunk_len+CHUNK_DATA; + fseek(file, chunk_offset, SEEK_SET); + } + return 0; +} + +int readData(FILE *file, WaveHeaderChunk *hdr, void* buff, int buff_len){ + uint32_t br; + int bytes_to_read = hdr->fmt.bytes_per_sample*buff_len; + + if(!hdr) { + printf("Error: No valid header\n"); + return -1; + } + + fseek(file, hdr->data.current_offset, SEEK_SET); + br = fread(buff, hdr->fmt.bytes_per_sample, buff_len, file); + + + int bytes_read = hdr->fmt.bytes_per_sample*br; + + //printf("samples read: %u\n", br); + + hdr->data.samples_left -= br; + hdr->data.current_offset += bytes_read; + + if(feof(file)){ + br = -1; + } + + return br; +} + +void freeData(WaveHeaderChunk *hdr) { + //free the info tag heap data + if(hdr->info.title){ + free(hdr->info.title); + hdr->info.title=NULL; + } + + if(hdr->info.artist){ + free(hdr->info.artist); + hdr->info.artist=NULL; + } + + if(hdr->info.genre){ + free(hdr->info.genre); + hdr->info.genre=NULL; + } + + if(hdr->info.creation_date){ + free(hdr->info.creation_date); + hdr->info.creation_date=NULL; + } +} diff --git a/app/src/nativecode/rawdraw/.github/workflows/github-actions.yml b/app/src/nativecode/rawdraw/.github/workflows/github-actions.yml new file mode 100644 index 0000000..b76d913 --- /dev/null +++ b/app/src/nativecode/rawdraw/.github/workflows/github-actions.yml @@ -0,0 +1,41 @@ +name: rawdraw +run-name: ${{ github.actor }} Executing +on: + push: + branches: + - '**' + pull_request: + branches: + - '**' +jobs: + Explore-GitHub-Actions: + runs-on: ubuntu-latest + steps: + - run: echo "Triggered by ${{ github.event_name }} on ${{ runner.os }}. Branch Ref ${{ github.ref }}." + - name: Install dependencies. + run: sudo apt-get update && sudo apt-get install -y --no-install-recommends + build-essential + openjdk-11-jdk-headless + unzip + wget + zip + git + vim + gettext-base + - name: Check out respository code + uses: actions/checkout@v3 + with: + submodules: recursive + - name: Building rawdraw_sf.h + run: | + rm rawdraw_sf.h + make rawdraw_sf.h + #- name: Commit rawdraw_sf.h + # run: | + # git_hash=$(git rev-parse --short "$GITHUB_SHA") + # git commit -m "Updating rawdraw_sf.h to ${git_hash}" rawdraw_sf.h + - uses: actions/upload-artifact@v3 + with: + name: rawdraw_sf + path: rawdraw_sf.h + diff --git a/app/src/nativecode/rawdraw/.gitignore b/app/src/nativecode/rawdraw/.gitignore new file mode 100644 index 0000000..5dd26fe --- /dev/null +++ b/app/src/nativecode/rawdraw/.gitignore @@ -0,0 +1,17 @@ +.vs/* +Debug/* +Release/* +TextTool/FontBackup.c +TextTool/text +*.exe +ogltest +rawdraw +rawdraw_ogl +simple +examples/fontsize +examples/fontsize_ogl + +tools/binary_to_buffer +tools/single_file_creator +tools/rawdraw_http_page.h +rawdraw_sf.hf diff --git a/app/src/nativecode/rawdraw/CNFG.c b/app/src/nativecode/rawdraw/CNFG.c new file mode 100644 index 0000000..01bb40f --- /dev/null +++ b/app/src/nativecode/rawdraw/CNFG.c @@ -0,0 +1,28 @@ +//Include this file to get all of rawdraw. You usually will not +//want to include this in your build, but instead, #include "CNFG.h" +//after #define CNFG_IMPLEMENTATION in one of your C files. + +// Defined here for universal definition +int CNFGLastCharacter = 0; +int CNFGLastScancode = 0; + +#if defined( CNFGHTTP ) +#include "CNFGHTTP.c" +#elif defined( __wasm__ ) +#include "CNFGWASMDriver.c" +#elif defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) +#include "CNFGWinDriver.c" +#elif defined( EGL_LEAN_AND_MEAN ) +#include "CNFGEGLLeanAndMean.c" +#elif defined( __android__ ) || defined( ANDROID ) +#include "CNFGEGLDriver.c" +#else +#include "CNFGXDriver.c" +#endif + +#include "CNFGFunctions.c" + +#ifdef CNFG3D +#include "CNFG3D.c" +#endif + diff --git a/app/src/nativecode/rawdraw/CNFG.h b/app/src/nativecode/rawdraw/CNFG.h new file mode 100644 index 0000000..36d7ead --- /dev/null +++ b/app/src/nativecode/rawdraw/CNFG.h @@ -0,0 +1,545 @@ +// Copyright 2010-2021 <>< CNLohr, et. al. (Several other authors, many but not all mentioned) +// Licensed under the MIT/x11 or NewBSD License you choose. +// +// CN Foundational Graphics Main Header File. This is the main header you +// should include. See README.md for more details. + + +#ifndef _CNFG_H +#define _CNFG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Rawdraw flags: + CNFG3D -> Enable the weird 3D functionality that rawdraw has to allow you to + write apps which emit basic rawdraw primitives but look 3D! + CNFG_USE_DOUBLE_FUNCTIONS -> Use double-precision floating point for CNFG3D. + CNFGOGL -> Use an OpenGL Backend for all rawdraw functionality. + ->Caveat->If using CNFG_HAS_XSHAPE, then, we do something realy wacky. + CNFGRASTERIZER -> Software-rasterize the rawdraw calls, and, use + CNFGUpdateScreenWithBitmap to send video to webpage. + CNFGCONTEXTONLY -> Don't add any drawing functions, only opening a window to + get an OpenGL context. + + CNFG_IMPLEMENTATION -> #define this and it will make _this_ the file where the cnfg + functions actually live. + +Usually tested combinations: + * TCC On Windows and X11 (Linux) with: + - CNFGOGL on or CNFGOGL off. If CNFGOGL is off you can use + CNFG_WINDOWS_DISABLE_BATCH to disable all batching. + -or- + - CNFGRASTERIZER + + NOTE: Sometimes you can also use CNFGOGL + CNFGRASTERIZER + + * WASM driver supports both: CNFGRASTERIZER and without CNFGRASTERIZER (Recommended turn rasterizer off) + * ANDROID (But this automatically sets CNFGRASTERIZER OFF and CNFGOGL ON) + + Unusual compiler flags: + + * CNFGHTTP - Enable the HTTP server-version of rawdraw, where it renders to a website. + * CNFGHTTPSERVERONLY - if you want to use the HTTP server w/o rawdraw. You will need to implement: + - CloseEvent, HTTPCustomCallback, HTTPCustomStart, NewWebSocket, WebSocketData, WebSocketTick + * CNFG_DISABLE_HTTP_FILES - disable the HTTP file server. + +*/ + + +#include + +//Some per-platform logic. +#if defined( ANDROID ) || defined( __android__ ) + #define CNFGOGL +#endif + +#if ( defined( CNFGOGL ) || defined( __wasm__ ) ) && !defined(CNFG_HAS_XSHAPE) + + #define CNFG_BATCH 8192 //131,072 bytes. + + #if defined( ANDROID ) || defined( __android__ ) || defined( __wasm__ ) || defined( EGL_LEAN_AND_MEAN ) + #define CNFGEWGL //EGL or WebGL + #else + #define CNFGDESKTOPGL + #endif +#endif + +typedef struct { + short x, y; +} RDPoint; + +extern int CNFGPenX, CNFGPenY; +extern uint32_t CNFGBGColor; +extern uint32_t CNFGLastColor; +extern uint32_t CNFGDialogColor; //Only used for DrawBox + +//Draws text at CNFGPenX, CNFGPenY, with scale of `scale`. +void CNFGDrawText( const char * text, short scale ); + +//Determine how large a given test would be to draw. +void CNFGGetTextExtents( const char * text, int * w, int * h, int textsize ); + +//Draws a box, outline as whatever the last CNFGColor was set to but also draws +//a rectangle as a background as whatever CNFGDialogColor is set to. +void CNFGDrawBox( short x1, short y1, short x2, short y2 ); + +//To be provided by driver. Rawdraw uses colors in the format 0xRRGGBBAA +//Note that some backends do not support alpha of any kind. +//Some platforms also support alpha blending. So, be sure to set alpha to 0xFF +uint32_t CNFGColor( uint32_t RGBA ); + +//This both updates the screen, and flips, all as a single operation. +void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h ); + + +//This is only supported on a FEW architectures, but allows arbitrary +//image blitting. Note that the alpha channel behavior is different +//on different systems. +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ); + +// Only supported with CNFGOGL +#ifdef CNFGOGL +void CNFGDeleteTex( unsigned int tex ); +unsigned int CNFGTexImage( uint32_t *data, int w, int h ); +void CNFGBlitTex( unsigned int tex, int x, int y, int w, int h ); +#endif + +void CNFGTackPixel( short x1, short y1 ); +void CNFGTackSegment( short x1, short y1, short x2, short y2 ); +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ); +void CNFGTackPoly( RDPoint * points, int verts ); +void CNFGClearFrame(); +void CNFGSwapBuffers(); + +void CNFGGetDimensions( short * x, short * y ); + + +//This will setup a window. Note that w and h have special meaning. On Windows +//and X11, for instance if you set w and h to be negative, then rawdraw will not +//show the window to the user. This is useful if you just need it for some +//off-screen-rendering purpose. +// +//Return value of 0 indicates success. Nonzero indicates error. +int CNFGSetup( const char * WindowName, int w, int h ); + +void CNFGSetupFullscreen( const char * WindowName, int screen_number ); +int CNFGHandleInput(); + + +//You must provide: +void HandleKey( int keycode, int bDown ); +void HandleButton( int x, int y, int button, int bDown ); +void HandleMotion( int x, int y, int mask ); +int HandleDestroy(); // Return nonzero if you want to cancel destroy. +#ifdef ANDROID_WANT_WINDOW_TERMINATION +void HandleWindowTermination(); +#endif + +// Guaranteed to be for the current key inside HandleKey for drivers that support it +// If drivers don't support it, it is 0 +extern int CNFGLastCharacter; +extern int CNFGLastScancode; + +//Internal function for resizing rasterizer for rasterizer-mode. +void CNFGInternalResize( short x, short y ); //don't call this. + +//Not available on all systems. Use The OGL portion with care. +#ifdef CNFGOGL +void CNFGSetVSync( int vson ); +void * CNFGGetExtension( const char * extname ); +#endif + +//Also not available on all systems. Transparency. +void CNFGPrepareForTransparency(); +void CNFGDrawToTransparencyMode( int transp ); +void CNFGClearTransparencyLevel(); + +//Only available on systems that support it. +void CNFGSetLineWidth( short width ); +void CNFGChangeWindowTitle( const char * windowtitle ); +void CNFGSetWindowIconData( int w, int h, uint32_t * data ); +int CNFGSetupWMClass( const char * WindowName, int w, int h , char * wm_res_name_ , char * wm_res_class_ ); + +// Mouse related functions on systems that support it. + +// enum just in case we want more cursor shapes +typedef enum { + CNFG_CURSOR_HIDDEN, CNFG_CURSOR_ARROW, CNFG_CURSOR_LAST +} CNFGCursorShape; + +// This may put a motion event into the queue, which will call HandleMotion +void CNFGSetMousePosition( int x, int y ); + +void CNFGConfineMouse( int confined ); +void CNFGSetCursor( CNFGCursorShape shape ); + + +//If you're using a batching renderer, for instance on Android or an OpenGL +//You will need to call this function inbetewen swtiching properties of drawing. This is usually +//only needed if you calling OpenGL / OGLES functions directly and outside of CNFG. +// +//Note that these are the functions that are used on the backends which support this +//sort of thing. +#ifdef CNFG_BATCH + +//If you are not using the CNFGOGL driver, you will need to define these in your driver. +void CNFGEmitBackendTriangles( const float * vertices, const uint32_t * colors, int num_vertices ); +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ); + +//These need to be defined for the specific driver. +void CNFGClearFrame(); +void CNFGSwapBuffers(); + +void CNFGFlushRender(); //Emit any geometry (lines, squares, polys) which are slated to be rendered. +void CNFGInternalResize( short x, short y ); //Driver calls this after resize happens. +void CNFGSetupBatchInternal(); //Driver calls this after setup is complete. + +//Useful function for emitting a non-axis-aligned quad. +void CNFGEmitQuad( float cx0, float cy0, float cx1, float cy1, float cx2, float cy2, float cx3, float cy3 ); + +extern int CNFGVertPlace; +extern float CNFGVertDataV[CNFG_BATCH*3]; +extern uint32_t CNFGVertDataC[CNFG_BATCH]; +#endif + +#define CNFG_KEY_FOCUS 0xf000 + +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) + +#define CNFG_KEY_BACKSPACE 0x08 +#define CNFG_KEY_TAB 0x09 +#define CNFG_KEY_CLEAR 0x0C +#define CNFG_KEY_ENTER 0x0D +#define CNFG_KEY_SHIFT 0x10 +#define CNFG_KEY_CTRL 0x11 +#define CNFG_KEY_ALT 0x12 +#define CNFG_KEY_PAUSE 0x13 +#define CNFG_KEY_CAPS_LOCK 0x14 +#define CNFG_KEY_ESCAPE 0x1B +#define CNFG_KEY_SPACE 0x20 +#define CNFG_KEY_PAGE_UP 0x21 +#define CNFG_KEY_PAGE_DOWN 0x22 +#define CNFG_KEY_END 0x23 +#define CNFG_KEY_HOME 0x24 +#define CNFG_KEY_LEFT_ARROW 0x25 +#define CNFG_KEY_TOP_ARROW 0x26 +#define CNFG_KEY_RIGHT_ARROW 0x27 +#define CNFG_KEY_BOTTOM_ARROW 0x28 +#define CNFG_KEY_SELECT 0x29 +#define CNFG_KEY_PRINT 0x2A +#define CNFG_KEY_EXECUTE 0x2B +#define CNFG_KEY_PRINT_SCREEN 0x2C +#define CNFG_KEY_INSERT 0x2D +#define CNFG_KEY_DELETE 0x2E +#define CNFG_KEY_HELP 0x2F +#define CNFG_KEY_LEFT_SUPER 0x5B +#define CNFG_KEY_RIGHT_SUPER 0x5C +#define CNFG_KEY_NUM_0 0x60 +#define CNFG_KEY_NUM_1 0x61 +#define CNFG_KEY_NUM_2 0x62 +#define CNFG_KEY_NUM_3 0x63 +#define CNFG_KEY_NUM_4 0x64 +#define CNFG_KEY_NUM_5 0x65 +#define CNFG_KEY_NUM_6 0x66 +#define CNFG_KEY_NUM_7 0x67 +#define CNFG_KEY_NUM_8 0x68 +#define CNFG_KEY_NUM_9 0x69 +#define CNFG_KEY_NUM_MULTIPLY 0x6A +#define CNFG_KEY_NUM_ADD 0x6B +#define CNFG_KEY_NUM_SEPARATOR 0x6C +#define CNFG_KEY_NUM_SUBTRACT 0x6D +#define CNFG_KEY_NUM_DECIMAL 0x6E +#define CNFG_KEY_NUM_DIVIDE 0x6F +#define CNFG_KEY_F1 0x70 +#define CNFG_KEY_F2 0x71 +#define CNFG_KEY_F3 0x72 +#define CNFG_KEY_F4 0x73 +#define CNFG_KEY_F5 0x74 +#define CNFG_KEY_F6 0x75 +#define CNFG_KEY_F7 0x76 +#define CNFG_KEY_F8 0x77 +#define CNFG_KEY_F9 0x78 +#define CNFG_KEY_F10 0x79 +#define CNFG_KEY_F11 0x7A +#define CNFG_KEY_F12 0x7B +#define CNFG_KEY_F13 0x7C +#define CNFG_KEY_F14 0x7D +#define CNFG_KEY_F15 0x7E +#define CNFG_KEY_F16 0x7F +#define CNFG_KEY_F17 0x80 +#define CNFG_KEY_F18 0x81 +#define CNFG_KEY_F19 0x82 +#define CNFG_KEY_F20 0x83 +#define CNFG_KEY_F21 0x84 +#define CNFG_KEY_F22 0x85 +#define CNFG_KEY_F23 0x86 +#define CNFG_KEY_F24 0x87 +#define CNFG_KEY_NUM_LOCK 0x90 +#define CNFG_KEY_SCROLL_LOCK 0x91 +#define CNFG_KEY_LEFT_SHIFT 0xA0 +#define CNFG_KEY_RIGHT_SHIFT 0xA1 +#define CNFG_KEY_LEFT_CONTROL 0xA2 +#define CNFG_KEY_RIGHT_CONTROL 0xA3 +#define CNFG_KEY_LEFT_ALT 0xA4 +#define CNFG_KEY_RIGHT_ALT 0xA5 + +#elif defined( EGL_LEAN_AND_MEAN ) // doesn't have any keys +#elif defined( __android__ ) || defined( ANDROID ) // ^ +#elif defined( __wasm__ ) + +#define CNFG_KEY_BACKSPACE 8 +#define CNFG_KEY_TAB 9 +#define CNFG_KEY_CLEAR 12 +#define CNFG_KEY_ENTER 13 +#define CNFG_KEY_SHIFT 16 +#define CNFG_KEY_CTRL 17 +#define CNFG_KEY_ALT 18 +#define CNFG_KEY_PAUSE 19 +#define CNFG_KEY_CAPS_LOCK 20 +#define CNFG_KEY_ESCAPE 27 +#define CNFG_KEY_SPACE 32 +#define CNFG_KEY_PAGE_UP 33 +#define CNFG_KEY_PAGE_DOWN 34 +#define CNFG_KEY_END 35 +#define CNFG_KEY_HOME 36 +#define CNFG_KEY_LEFT_ARROW 37 +#define CNFG_KEY_TOP_ARROW 38 +#define CNFG_KEY_RIGHT_ARROW 39 +#define CNFG_KEY_BOTTOM_ARROW 40 +#define CNFG_KEY_SELECT 41 +#define CNFG_KEY_PRINT 42 +#define CNFG_KEY_EXECUTE 43 +#define CNFG_KEY_PRINT_SCREEN 44 +#define CNFG_KEY_INSERT 45 +#define CNFG_KEY_DELETE 46 +#define CNFG_KEY_HELP 47 +#define CNFG_KEY_LEFT_SUPER 91 +#define CNFG_KEY_RIGHT_SUPER 92 +#define CNFG_KEY_NUM_0 96 +#define CNFG_KEY_NUM_1 97 +#define CNFG_KEY_NUM_2 98 +#define CNFG_KEY_NUM_3 99 +#define CNFG_KEY_NUM_4 100 +#define CNFG_KEY_NUM_5 101 +#define CNFG_KEY_NUM_6 102 +#define CNFG_KEY_NUM_7 103 +#define CNFG_KEY_NUM_8 104 +#define CNFG_KEY_NUM_9 105 +#define CNFG_KEY_NUM_MULTIPLY 106 +#define CNFG_KEY_NUM_ADD 107 +#define CNFG_KEY_NUM_SEPARATOR 108 +#define CNFG_KEY_NUM_SUBTRACT 109 +#define CNFG_KEY_NUM_DECIMAL 110 +#define CNFG_KEY_NUM_DIVIDE 111 +#define CNFG_KEY_F1 112 +#define CNFG_KEY_F2 113 +#define CNFG_KEY_F3 114 +#define CNFG_KEY_F4 115 +#define CNFG_KEY_F5 116 +#define CNFG_KEY_F6 117 +#define CNFG_KEY_F7 118 +#define CNFG_KEY_F8 119 +#define CNFG_KEY_F9 120 +#define CNFG_KEY_F10 121 +#define CNFG_KEY_F11 122 +#define CNFG_KEY_F12 123 +#define CNFG_KEY_F13 124 +#define CNFG_KEY_F14 125 +#define CNFG_KEY_F15 126 +#define CNFG_KEY_F16 127 +#define CNFG_KEY_F17 128 +#define CNFG_KEY_F18 129 +#define CNFG_KEY_F19 130 +#define CNFG_KEY_F20 131 +#define CNFG_KEY_F21 132 +#define CNFG_KEY_F22 133 +#define CNFG_KEY_F23 134 +#define CNFG_KEY_F24 135 +#define CNFG_KEY_NUM_LOCK 144 +#define CNFG_KEY_SCROLL_LOCK 145 +#define CNFG_KEY_LEFT_SHIFT 160 +#define CNFG_KEY_RIGHT_SHIFT 161 +#define CNFG_KEY_LEFT_CONTROL 162 +#define CNFG_KEY_RIGHT_CONTROL 163 +#define CNFG_KEY_LEFT_ALT 164 +#define CNFG_KEY_RIGHT_ALT 165 + +#else // most likely x11 + +#define CNFG_KEY_BACKSPACE 65288 +#define CNFG_KEY_TAB 65289 +#define CNFG_KEY_CLEAR 65291 +#define CNFG_KEY_ENTER 65293 +#define CNFG_KEY_SHIFT 65505 +#define CNFG_KEY_CTRL 65507 +#define CNFG_KEY_ALT 65513 +#define CNFG_KEY_PAUSE 65299 +#define CNFG_KEY_CAPS_LOCK 65509 +#define CNFG_KEY_ESCAPE 65505 +#define CNFG_KEY_SPACE 32 +#define CNFG_KEY_PAGE_UP 65365 +#define CNFG_KEY_PAGE_DOWN 65366 +#define CNFG_KEY_END 65367 +#define CNFG_KEY_HOME 65360 +#define CNFG_KEY_LEFT_ARROW 65361 +#define CNFG_KEY_TOP_ARROW 65362 +#define CNFG_KEY_RIGHT_ARROW 65363 +#define CNFG_KEY_BOTTOM_ARROW 65364 +#define CNFG_KEY_SELECT 65376 +#define CNFG_KEY_PRINT 65377 +#define CNFG_KEY_EXECUTE 65378 +#define CNFG_KEY_PRINT_SCREEN 64797 +#define CNFG_KEY_INSERT 65379 +#define CNFG_KEY_DELETE 65535 +#define CNFG_KEY_HELP 65386 +#define CNFG_KEY_LEFT_SUPER 65515 +#define CNFG_KEY_RIGHT_SUPER 65516 +#define CNFG_KEY_NUM_0 65456 +#define CNFG_KEY_NUM_1 65457 +#define CNFG_KEY_NUM_2 65458 +#define CNFG_KEY_NUM_3 65459 +#define CNFG_KEY_NUM_4 65460 +#define CNFG_KEY_NUM_5 65461 +#define CNFG_KEY_NUM_6 65462 +#define CNFG_KEY_NUM_7 65463 +#define CNFG_KEY_NUM_8 65464 +#define CNFG_KEY_NUM_9 65465 +#define CNFG_KEY_NUM_MULTIPLY 65450 +#define CNFG_KEY_NUM_ADD 65451 +#define CNFG_KEY_NUM_SEPARATOR 65452 +#define CNFG_KEY_NUM_SUBTRACT 65453 +#define CNFG_KEY_NUM_DECIMAL 65454 +#define CNFG_KEY_NUM_DIVIDE 65455 +#define CNFG_KEY_F1 65470 +#define CNFG_KEY_F2 65471 +#define CNFG_KEY_F3 65472 +#define CNFG_KEY_F4 65473 +#define CNFG_KEY_F5 65474 +#define CNFG_KEY_F6 65475 +#define CNFG_KEY_F7 65476 +#define CNFG_KEY_F8 65477 +#define CNFG_KEY_F9 65478 +#define CNFG_KEY_F10 65479 +#define CNFG_KEY_F11 65480 +#define CNFG_KEY_F12 65481 +#define CNFG_KEY_F13 65482 +#define CNFG_KEY_F14 65483 +#define CNFG_KEY_F15 65484 +#define CNFG_KEY_F16 65485 +#define CNFG_KEY_F17 65486 +#define CNFG_KEY_F18 65487 +#define CNFG_KEY_F19 65488 +#define CNFG_KEY_F20 65489 +#define CNFG_KEY_F21 65490 +#define CNFG_KEY_F22 65491 +#define CNFG_KEY_F23 65492 +#define CNFG_KEY_F24 65493 +#define CNFG_KEY_NUM_LOCK 65407 +#define CNFG_KEY_SCROLL_LOCK 65300 +#define CNFG_KEY_LEFT_SHIFT 65505 +#define CNFG_KEY_RIGHT_SHIFT 65506 +#define CNFG_KEY_LEFT_CONTROL 65507 +#define CNFG_KEY_RIGHT_CONTROL 65508 +#define CNFG_KEY_LEFT_ALT 65513 +#define CNFG_KEY_RIGHT_ALT 65514 + +#define CNFG_X11_EXPOSE 0xff00 //65280 + +#endif + +#ifdef CNFG3D + +#ifndef __wasm__ +#include +#endif + +#ifdef CNFG_USE_DOUBLE_FUNCTIONS +#define tdCOS cos +#define tdSIN sin +#define tdTAN tan +#define tdSQRT sqrt +#else +#define tdCOS cosf +#define tdSIN sinf +#define tdTAN tanf +#define tdSQRT sqrtf +#endif + +#ifdef __wasm__ +void tdMATCOPY( float * x, const float * y ); //Copy y into x +#else +#define tdMATCOPY(x,y) memcpy( x, y, 16*sizeof(float)) +#endif + +#define tdQ_PI 3.141592653589 +#define tdDEGRAD (tdQ_PI/180.) +#define tdRADDEG (180./tdQ_PI) + + +//General Matrix Functions +void tdIdentity( float * f ); +void tdZero( float * f ); +void tdTranslate( float * f, float x, float y, float z ); //Operates ON f +void tdScale( float * f, float x, float y, float z ); //Operates ON f +void tdRotateAA( float * f, float angle, float x, float y, float z ); //Operates ON f +void tdRotateQuat( float * f, float qw, float qx, float qy, float qz ); //Operates ON f +void tdRotateEA( float * f, float x, float y, float z ); //Operates ON f +void tdMultiply( float * fin1, float * fin2, float * fout ); //Operates ON f +void tdPrint( const float * f ); +void tdTransposeSelf( float * f ); + +//Specialty Matrix Functions +void tdPerspective( float fovy, float aspect, float zNear, float zFar, float * out ); //Sets, NOT OPERATES. (FOVX=degrees) +void tdLookAt( float * m, float * eye, float * at, float * up ); //Operates ON m +//General point functions +#define tdPSet( f, x, y, z ) { f[0] = x; f[1] = y; f[2] = z; } +void tdPTransform( const float * pin, float * f, float * pout ); +void tdVTransform( const float * vin, float * f, float * vout ); +void td4Transform( float * kin, float * f, float * kout ); +void td4RTransform( float * kin, float * f, float * kout ); +void tdNormalizeSelf( float * vin ); +void tdCross( float * va, float * vb, float * vout ); +float tdDistance( float * va, float * vb ); +float tdDot( float * va, float * vb ); +#define tdPSub( x, y, z ) { (z)[0] = (x)[0] - (y)[0]; (z)[1] = (x)[1] - (y)[1]; (z)[2] = (x)[2] - (y)[2]; } +#define tdPAdd( x, y, z ) { (z)[0] = (x)[0] + (y)[0]; (z)[1] = (x)[1] + (y)[1]; (z)[2] = (x)[2] + (y)[2]; } + +//Stack Functionality +#define tdMATRIXMAXDEPTH 32 +extern float * gSMatrix; +void tdPush(); +void tdPop(); +void tdMode( int mode ); +#define tdMODELVIEW 0 +#define tdPROJECTION 1 + +//Final stage tools +void tdSetViewport( float leftx, float topy, float rightx, float bottomy, float pixx, float pixy ); +void tdFinalPoint( float * pin, float * pout ); + +float tdNoiseAt( int x, int y ); +float tdFLerp( float a, float b, float t ); +float tdPerlin2D( float x, float y ); + +#endif + +extern const unsigned char RawdrawFontCharData[1405]; +extern const unsigned short RawdrawFontCharMap[256]; + +#ifdef __cplusplus +}; +#endif + + +#if defined( ANDROID ) || defined( __android__ ) +#include "CNFGAndroid.h" +#endif + +#ifdef CNFG_IMPLEMENTATION +#include "CNFG.c" +#endif + +#endif + diff --git a/app/src/nativecode/rawdraw/CNFG3D.c b/app/src/nativecode/rawdraw/CNFG3D.c new file mode 100644 index 0000000..8c5876c --- /dev/null +++ b/app/src/nativecode/rawdraw/CNFG3D.c @@ -0,0 +1,542 @@ +//Copyright 2012-2017 <>< Charles Lohr +//You may license this file under the MIT/x11, NewBSD, or any GPL license. +//This is a series of tools useful for software rendering. +//Use of this file with OpenGL is untested. + +#ifdef CNFG3D + +#include "CNFG.h" + +#ifdef __wasm__ +double sin( double v ); +double cos( double v ); +double tan( double v ); +double sqrt( double v ); +float sinf( float v ); +float cosf( float v ); +float tanf( float v ); +float sqrtf( float v ); +void tdMATCOPY( float * x, const float * y ) +{ + int i; + for( i = 0; i < 16; i++ ) x[i] = y[i]; +} +#else +#include +#include +#endif + +#ifdef CNFG3D_USE_OGL_MAJOR +#define m00 0 +#define m10 1 +#define m20 2 +#define m30 3 +#define m01 4 +#define m11 5 +#define m21 6 +#define m31 7 +#define m02 8 +#define m12 9 +#define m22 10 +#define m32 11 +#define m03 12 +#define m13 13 +#define m23 14 +#define m33 15 +#else +#define m00 0 +#define m01 1 +#define m02 2 +#define m03 3 +#define m10 4 +#define m11 5 +#define m12 6 +#define m13 7 +#define m20 8 +#define m21 9 +#define m22 10 +#define m23 11 +#define m30 12 +#define m31 13 +#define m32 14 +#define m33 15 +#endif + +void tdIdentity( float * f ) +{ + f[m00] = 1; f[m01] = 0; f[m02] = 0; f[m03] = 0; + f[m10] = 0; f[m11] = 1; f[m12] = 0; f[m13] = 0; + f[m20] = 0; f[m21] = 0; f[m22] = 1; f[m23] = 0; + f[m30] = 0; f[m31] = 0; f[m32] = 0; f[m33] = 1; +} + +void tdZero( float * f ) +{ + f[m00] = 0; f[m01] = 0; f[m02] = 0; f[m03] = 0; + f[m10] = 0; f[m11] = 0; f[m12] = 0; f[m13] = 0; + f[m20] = 0; f[m21] = 0; f[m22] = 0; f[m23] = 0; + f[m30] = 0; f[m31] = 0; f[m32] = 0; f[m33] = 0; +} + +void tdTranslate( float * f, float x, float y, float z ) +{ + float ftmp[16]; + tdIdentity(ftmp); + ftmp[m03] += x; + ftmp[m13] += y; + ftmp[m23] += z; + tdMultiply( f, ftmp, f ); +} + +void tdScale( float * f, float x, float y, float z ) +{ +#if 0 + f[m00] *= x; + f[m01] *= x; + f[m02] *= x; + f[m03] *= x; + + f[m10] *= y; + f[m11] *= y; + f[m12] *= y; + f[m13] *= y; + + f[m20] *= z; + f[m21] *= z; + f[m22] *= z; + f[m23] *= z; +#endif + + float ftmp[16]; + tdIdentity(ftmp); + ftmp[m00] *= x; + ftmp[m11] *= y; + ftmp[m22] *= z; + + tdMultiply( f, ftmp, f ); + +} + +void tdRotateAA( float * f, float angle, float ix, float iy, float iz ) +{ + float ftmp[16]; + + float c = tdCOS( angle*tdDEGRAD ); + float s = tdSIN( angle*tdDEGRAD ); + float absin = tdSQRT( ix*ix + iy*iy + iz*iz ); + float x = ix/absin; + float y = iy/absin; + float z = iz/absin; + + ftmp[m00] = x*x*(1-c)+c; + ftmp[m01] = x*y*(1-c)-z*s; + ftmp[m02] = x*z*(1-c)+y*s; + ftmp[m03] = 0; + + ftmp[m10] = y*x*(1-c)+z*s; + ftmp[m11] = y*y*(1-c)+c; + ftmp[m12] = y*z*(1-c)-x*s; + ftmp[m13] = 0; + + ftmp[m20] = x*z*(1-c)-y*s; + ftmp[m21] = y*z*(1-c)+x*s; + ftmp[m22] = z*z*(1-c)+c; + ftmp[m23] = 0; + + ftmp[m30] = 0; + ftmp[m31] = 0; + ftmp[m32] = 0; + ftmp[m33] = 1; + + tdMultiply( f, ftmp, f ); +} + +void tdRotateQuat( float * f, float qw, float qx, float qy, float qz ) +{ + float ftmp[16]; + //float qw2 = qw*qw; + float qx2 = qx*qx; + float qy2 = qy*qy; + float qz2 = qz*qz; + + ftmp[m00] = 1 - 2*qy2 - 2*qz2; + ftmp[m01] = 2*qx*qy - 2*qz*qw; + ftmp[m02] = 2*qx*qz + 2*qy*qw; + ftmp[m03] = 0; + + ftmp[m10] = 2*qx*qy + 2*qz*qw; + ftmp[m11] = 1 - 2*qx2 - 2*qz2; + ftmp[m12] = 2*qy*qz - 2*qx*qw; + ftmp[m13] = 0; + + ftmp[m20] = 2*qx*qz - 2*qy*qw; + ftmp[m21] = 2*qy*qz + 2*qx*qw; + ftmp[m22] = 1 - 2*qx2 - 2*qy2; + ftmp[m23] = 0; + + ftmp[m30] = 0; + ftmp[m31] = 0; + ftmp[m32] = 0; + ftmp[m33] = 1; + + tdMultiply( f, ftmp, f ); + +} + +void tdRotateEA( float * f, float x, float y, float z ) +{ + float ftmp[16]; + + //x,y,z must be negated for some reason + float X = -x*2*tdQ_PI/360; //Reduced calulation for speed + float Y = -y*2*tdQ_PI/360; + float Z = -z*2*tdQ_PI/360; + float cx = tdCOS(X); + float sx = tdSIN(X); + float cy = tdCOS(Y); + float sy = tdSIN(Y); + float cz = tdCOS(Z); + float sz = tdSIN(Z); + + //Row major (unless CNFG3D_USE_OGL_MAJOR is selected) + //manually transposed + ftmp[m00] = cy*cz; + ftmp[m10] = (sx*sy*cz)-(cx*sz); + ftmp[m20] = (cx*sy*cz)+(sx*sz); + ftmp[m30] = 0; + + ftmp[m01] = cy*sz; + ftmp[m11] = (sx*sy*sz)+(cx*cz); + ftmp[m21] = (cx*sy*sz)-(sx*cz); + ftmp[m31] = 0; + + ftmp[m02] = -sy; + ftmp[m12] = sx*cy; + ftmp[m22] = cx*cy; + ftmp[m32] = 0; + + ftmp[m03] = 0; + ftmp[m13] = 0; + ftmp[m23] = 0; + ftmp[m33] = 1; + + tdMultiply( f, ftmp, f ); +} + +void tdMultiply( float * fin1, float * fin2, float * fout ) +{ + float fotmp[16]; + int i, k; +#ifdef CNFG3D_USE_OGL_MAJOR + fotmp[m00] = fin1[m00] * fin2[m00] + fin1[m01] * fin2[m10] + fin1[m02] * fin2[m20] + fin1[m03] * fin2[m30]; + fotmp[m01] = fin1[m00] * fin2[m01] + fin1[m01] * fin2[m11] + fin1[m02] * fin2[m21] + fin1[m03] * fin2[m31]; + fotmp[m02] = fin1[m00] * fin2[m02] + fin1[m01] * fin2[m12] + fin1[m02] * fin2[m22] + fin1[m03] * fin2[m32]; + fotmp[m03] = fin1[m00] * fin2[m03] + fin1[m01] * fin2[m13] + fin1[m02] * fin2[m23] + fin1[m03] * fin2[m33]; + + fotmp[m10] = fin1[m10] * fin2[m00] + fin1[m11] * fin2[m10] + fin1[m12] * fin2[m20] + fin1[m13] * fin2[m30]; + fotmp[m11] = fin1[m10] * fin2[m01] + fin1[m11] * fin2[m11] + fin1[m12] * fin2[m21] + fin1[m13] * fin2[m31]; + fotmp[m12] = fin1[m10] * fin2[m02] + fin1[m11] * fin2[m12] + fin1[m12] * fin2[m22] + fin1[m13] * fin2[m32]; + fotmp[m13] = fin1[m10] * fin2[m03] + fin1[m11] * fin2[m13] + fin1[m12] * fin2[m23] + fin1[m13] * fin2[m33]; + + fotmp[m20] = fin1[m20] * fin2[m00] + fin1[m21] * fin2[m10] + fin1[m22] * fin2[m20] + fin1[m23] * fin2[m30]; + fotmp[m21] = fin1[m20] * fin2[m01] + fin1[m21] * fin2[m11] + fin1[m22] * fin2[m21] + fin1[m23] * fin2[m31]; + fotmp[m22] = fin1[m20] * fin2[m02] + fin1[m21] * fin2[m12] + fin1[m22] * fin2[m22] + fin1[m23] * fin2[m32]; + fotmp[m23] = fin1[m20] * fin2[m03] + fin1[m21] * fin2[m13] + fin1[m22] * fin2[m23] + fin1[m23] * fin2[m33]; + + fotmp[m30] = fin1[m30] * fin2[m00] + fin1[m31] * fin2[m10] + fin1[m32] * fin2[m20] + fin1[m33] * fin2[m30]; + fotmp[m31] = fin1[m30] * fin2[m01] + fin1[m31] * fin2[m11] + fin1[m32] * fin2[m21] + fin1[m33] * fin2[m31]; + fotmp[m32] = fin1[m30] * fin2[m02] + fin1[m31] * fin2[m12] + fin1[m32] * fin2[m22] + fin1[m33] * fin2[m32]; + fotmp[m33] = fin1[m30] * fin2[m03] + fin1[m31] * fin2[m13] + fin1[m32] * fin2[m23] + fin1[m33] * fin2[m33]; +#else + for( i = 0; i < 16; i++ ) + { + int xp = i & 0x03; + int yp = i & 0x0c; + fotmp[i] = 0; + for( k = 0; k < 4; k++ ) + { + fotmp[i] += fin1[yp+k] * fin2[(k<<2)|xp]; + } + } +#endif + tdMATCOPY( fout, fotmp ); +} + +#ifndef __wasm__ +void tdPrint( const float * f ) +{ + int i; + printf( "{\n" ); +#ifdef CNFG3D_USE_OGL_MAJOR + for( i = 0; i < 4; i++ ) + { + printf( " %f, %f, %f, %f\n", f[0+i], f[4+i], f[8+i], f[12+i] ); + } +#else + for( i = 0; i < 16; i+=4 ) + { + printf( " %f, %f, %f, %f\n", f[0+i], f[1+i], f[2+i], f[3+i] ); + } +#endif + printf( "}\n" ); +} +#endif + +void tdTransposeSelf( float * f ) +{ + float fout[16]; + fout[m00] = f[m00]; fout[m01] = f[m10]; fout[m02] = f[m20]; fout[m03] = f[m30]; + fout[m10] = f[m01]; fout[m11] = f[m11]; fout[m12] = f[m21]; fout[m13] = f[m31]; + fout[m20] = f[m02]; fout[m21] = f[m12]; fout[m22] = f[m22]; fout[m23] = f[m32]; + fout[m30] = f[m03]; fout[m31] = f[m13]; fout[m32] = f[m23]; fout[m33] = f[m33]; + tdMATCOPY( f, fout ); +} + + +void tdPerspective( float fovy, float aspect, float zNear, float zFar, float * out ) +{ + float f = 1./tdTAN(fovy * tdQ_PI / 360.0); + out[m00] = f/aspect; out[m01] = 0; out[m02] = 0; out[m03] = 0; + out[m10] = 0; out[m11] = f; out[m12] = 0; out[m13] = 0; + out[m20] = 0; out[m21] = 0; + out[m22] = (zFar + zNear)/(zNear - zFar); + out[m23] = 2*zFar*zNear /(zNear - zFar); + out[m30] = 0; out[m31] = 0; out[m32] = -1; out[m33] = 0; +} + +void tdLookAt( float * m, float * eye, float * at, float * up ) +{ + float out[16]; + float F[3] = { at[0] - eye[0], at[1] - eye[1], at[2] - eye[2] }; + float fdiv = 1./tdSQRT( F[0]*F[0] + F[1]*F[1] + F[2]*F[2] ); + float f[3] = { F[0]*fdiv, F[1]*fdiv, F[2]*fdiv }; + float udiv = 1./tdSQRT( up[0]*up[0] + up[1]*up[1] + up[2]*up[2] ); + float UP[3] = { up[0]*udiv, up[1]*udiv, up[2]*udiv }; + float s[3]; + float u[3]; + tdCross( f, UP, s ); + tdCross( s, f, u ); + + out[m00] = s[0]; out[m01] = s[1]; out[m02] = s[2]; out[m03] = 0; + out[m10] = u[0]; out[m11] = u[1]; out[m12] = u[2]; out[m13] = 0; + out[m20] = -f[0];out[m21] =-f[1]; out[m22] =-f[2]; out[m23] = 0; + out[m30] = 0; out[m31] = 0; out[m32] = 0; out[m33] = 1; + + tdMultiply( m, out, m ); + tdTranslate( m, -eye[0], -eye[1], -eye[2] ); +} + + + + + + + + + + + + +void tdPTransform( const float * pin, float * f, float * pout ) +{ + float ptmp[2]; + ptmp[0] = pin[0] * f[m00] + pin[1] * f[m01] + pin[2] * f[m02] + f[m03]; + ptmp[1] = pin[0] * f[m10] + pin[1] * f[m11] + pin[2] * f[m12] + f[m13]; + pout[2] = pin[0] * f[m20] + pin[1] * f[m21] + pin[2] * f[m22] + f[m23]; + pout[0] = ptmp[0]; + pout[1] = ptmp[1]; +} + +void tdVTransform( const float * pin, float * f, float * pout ) +{ + float ptmp[2]; + ptmp[0] = pin[0] * f[m00] + pin[1] * f[m01] + pin[2] * f[m02]; + ptmp[1] = pin[0] * f[m10] + pin[1] * f[m11] + pin[2] * f[m12]; + pout[2] = pin[0] * f[m20] + pin[1] * f[m21] + pin[2] * f[m22]; + pout[0] = ptmp[0]; + pout[1] = ptmp[1]; +} + +void td4Transform( float * pin, float * f, float * pout ) +{ + float ptmp[3]; + ptmp[0] = pin[0] * f[m00] + pin[1] * f[m01] + pin[2] * f[m02] + pin[3] * f[m03]; + ptmp[1] = pin[0] * f[m10] + pin[1] * f[m11] + pin[2] * f[m12] + pin[3] * f[m13]; + ptmp[2] = pin[0] * f[m20] + pin[1] * f[m21] + pin[2] * f[m22] + pin[3] * f[m23]; + pout[3] = pin[0] * f[m30] + pin[1] * f[m31] + pin[2] * f[m32] + pin[3] * f[m33]; + pout[0] = ptmp[0]; + pout[1] = ptmp[1]; + pout[2] = ptmp[2]; +} + +void td4RTransform( float * pin, float * f, float * pout ) +{ + float ptmp[3]; + ptmp[0] = pin[0] * f[m00] + pin[1] * f[m10] + pin[2] * f[m20] + pin[3] * f[m30]; + ptmp[1] = pin[0] * f[m01] + pin[1] * f[m11] + pin[2] * f[m21] + pin[3] * f[m31]; + ptmp[2] = pin[0] * f[m02] + pin[1] * f[m12] + pin[2] * f[m22] + pin[3] * f[m32]; + pout[3] = pin[0] * f[m03] + pin[1] * f[m13] + pin[2] * f[m23] + pin[3] * f[m33]; + pout[0] = ptmp[0]; + pout[1] = ptmp[1]; + pout[2] = ptmp[2]; +} + +void tdNormalizeSelf( float * vin ) +{ + float vsq = 1./tdSQRT(vin[0]*vin[0] + vin[1]*vin[1] + vin[2]*vin[2]); + vin[0] *= vsq; + vin[1] *= vsq; + vin[2] *= vsq; +} + +void tdCross( float * va, float * vb, float * vout ) +{ + float vtmp[2]; + vtmp[0] = va[1] * vb[2] - va[2] * vb[1]; + vtmp[1] = va[2] * vb[0] - va[0] * vb[2]; + vout[2] = va[0] * vb[1] - va[1] * vb[0]; + vout[0] = vtmp[0]; + vout[1] = vtmp[1]; +} + +float tdDistance( float * va, float * vb ) +{ + float dx = va[0]-vb[0]; + float dy = va[1]-vb[1]; + float dz = va[2]-vb[2]; + + return tdSQRT(dx*dx + dy*dy + dz*dz); +} + +float tdDot( float * va, float * vb ) +{ + return va[0]*vb[0] + va[1]*vb[1] + va[2]*vb[2]; +} + +//Stack functionality. +static float gsMatricies[2][tdMATRIXMAXDEPTH][16]; +float * gSMatrix = gsMatricies[0][0]; +static int gsMMode; +static int gsMPlace[2]; + +void tdPush() +{ + if( gsMPlace[gsMMode] > tdMATRIXMAXDEPTH - 2 ) + return; + + tdMATCOPY( gsMatricies[gsMMode][gsMPlace[gsMMode] + 1], gsMatricies[gsMMode][gsMPlace[gsMMode]] ); + gsMPlace[gsMMode]++; + + gSMatrix = gsMatricies[gsMMode][gsMPlace[gsMMode]]; +} + +void tdPop() +{ + if( gsMPlace[gsMMode] < 1 ) + return; + + gsMPlace[gsMMode]--; + + gSMatrix = gsMatricies[gsMMode][gsMPlace[gsMMode]]; + +} + +void tdMode( int mode ) +{ + if( mode < 0 || mode > 1 ) + return; + + gsMMode = mode; + + gSMatrix = gsMatricies[gsMMode][gsMPlace[gsMMode]]; + +} + +static float translateX; +static float translateY; +static float scaleX; +static float scaleY; + +void tdSetViewport( float leftx, float topy, float rightx, float bottomy, float pixx, float pixy ) +{ + translateX = leftx; + translateY = bottomy; + scaleX = pixx/(rightx-leftx); + scaleY = pixy/(topy-bottomy); + +} + +void tdFinalPoint( float * pin, float * pout ) +{ + float tdin[4] = { pin[0], pin[1], pin[2], 1. }; + float tmp[4]; + td4Transform( tdin, gsMatricies[0][gsMPlace[0]], tmp ); +// printf( "XFORM1Out: %f %f %f %f\n", tmp[0], tmp[1], tmp[2], tmp[3] ); + td4Transform( tmp, gsMatricies[1][gsMPlace[1]], tmp ); +// printf( "XFORM2Out: %f %f %f %f\n", tmp[0], tmp[1], tmp[2], tmp[3] ); + pout[0] = (tmp[0]/tmp[3] - translateX) * scaleX; + pout[1] = (tmp[1]/tmp[3] - translateY) * scaleY; + pout[2] = tmp[2]/tmp[3]; +// printf( "XFORMFOut: %f %f %f\n", pout[0], pout[1], pout[2] ); +} + + + + + + + + + + +float tdNoiseAt( int x, int y ) +{ + return ((x*13241*y + y * 33455927)%9293) / 4646. - 1.0; +} + +static inline float tdFade( float f ) +{ + float ft3 = f*f*f; + return ft3 * 10 - ft3 * f * 15 + 6 * ft3 * f * f; +} + +float tdFLerp( float a, float b, float t ) +{ + float fr = tdFade( t ); + return a * (1.-fr) + b * fr; +} + +static inline float tdFNoiseAt( float x, float y ) +{ + int ix = x; + int iy = y; + float fx = x - ix; + float fy = y - iy; + + float a = tdNoiseAt( ix, iy ); + float b = tdNoiseAt( ix+1, iy ); + float c = tdNoiseAt( ix, iy+1 ); + float d = tdNoiseAt( ix+1, iy+1 ); + + float top = tdFLerp( a, b, fx ); + float bottom = tdFLerp( c, d, fx ); + + return tdFLerp( top, bottom, fy ); +} + +float tdPerlin2D( float x, float y ) +{ + int ndepth = 5; + + int depth; + float ret = 0; + for( depth = 0; depth < ndepth; depth++ ) + { + float nx = x / (1<<(ndepth-depth-1)); + float ny = y / (1<<(ndepth-depth-1)); + ret += tdFNoiseAt( nx, ny ) / (1<<(depth+1)); + } + return ret; +} + +#endif diff --git a/app/src/nativecode/rawdraw/CNFGAndroid.h b/app/src/nativecode/rawdraw/CNFGAndroid.h new file mode 100644 index 0000000..b889bb6 --- /dev/null +++ b/app/src/nativecode/rawdraw/CNFGAndroid.h @@ -0,0 +1,217 @@ +#ifndef _CNFG_ANDROID_H +#define _CNFG_ANDROID_H + +//This file contains the additional functions that are available on the Android platform. +//In order to build rawdraw for Android, please compile CNFGEGLDriver.c with -DANDROID + +// Tricky: Android headers are confused by c++ if linking statically. +#ifdef __cplusplus +extern "C" { + int __system_property_get(const char* __name, char* __value); +}; +#endif + +extern struct android_app * gapp; +void AndroidMakeFullscreen(); +const char* AndroidGetExternalFilesDir(); +int AndroidHasPermissions(const char* perm_name); +void AndroidRequestAppPermissions(const char * perm); +void AndroidDisplayKeyboard(int pShow); +int AndroidGetUnicodeChar( int keyCode, int metaState ); +void AndroidSendToBack( int param ); + +extern int android_sdk_version; //Derived at start from property ro.build.version.sdk +extern int android_width, android_height; +extern int UpdateScreenWithBitmapOffsetX; +extern int UpdateScreenWithBitmapOffsetY; +extern void (*HandleCustomEventCallback)(); +extern void (*HandleWindowTermination)(); + + +// If you need them, these are the names of raw EGL symbols. +//extern EGLDisplay egl_display; +//extern EGLSurface egl_surface; +//extern EGLContext egl_context; +//extern EGLConfig egl_config; + + +//You must implement these. +void HandleResume(); +void HandleSuspend(); + + +//Departures: + +// HandleMotion's "mask" parameter is actually just an index, not a mask + +// CNFGSetup / CNFGSetupFullScreen only controls whether or not the navigation +// decoration is removed. Fullscreen means *full screen* To choose fullscreen +// or not fullscrene, modify, in your AndroidManifest.xml file, the application +// section to either contain or not contain: +// android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + + +// For debugging: + +#if defined( ANDROID ) && !defined( __cplusplus ) + +#include + +static inline void PrintClassOfObject( jobject bundle ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jclass myclass = env->GetObjectClass( envptr, bundle ); + jmethodID mid = env->GetMethodID( envptr, myclass, "getClass", "()Ljava/lang/Class;"); + jobject clsObj = env->CallObjectMethod( envptr, bundle, mid ); + jclass clazzz = env->GetObjectClass( envptr, clsObj ); + mid = env->GetMethodID(envptr, clazzz, "getName", "()Ljava/lang/String;"); + jstring strObj = (jstring)env->CallObjectMethod( envptr, clsObj, mid); + const char * name = env->GetStringUTFChars( envptr, strObj, 0); + printf( "Class type: %s\n", name ); + + env->DeleteLocalRef( envptr, myclass ); + env->DeleteLocalRef( envptr, clsObj ); + env->DeleteLocalRef( envptr, clazzz ); + env->ReleaseStringUTFChars(envptr, strObj, name); + env->DeleteLocalRef( envptr, strObj ); +} + +static inline void PrintObjectString( jobject bundle ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jclass myclass = env->GetObjectClass( envptr, bundle ); + jmethodID toStringMethod = env->GetMethodID( envptr, myclass, "toString", "()Ljava/lang/String;"); + jstring strObjDescr = (jstring)env->CallObjectMethod( envptr, bundle, toStringMethod); + const char *descr = env->GetStringUTFChars( envptr, strObjDescr, 0); + printf( "String: %s\n", descr ); + + env->DeleteLocalRef( envptr, myclass ); + env->ReleaseStringUTFChars( envptr, strObjDescr, descr ); + env->DeleteLocalRef( envptr, strObjDescr ); +} + + +static inline void DumpObjectClassProperties( jobject objToDump ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jclass mpclass = env->GetObjectClass( envptr, objToDump ); + + jmethodID midGetClass = env->GetMethodID( envptr, mpclass, "getClass", "()Ljava/lang/Class;"); + jclass ClassClass = env->FindClass(envptr, "java/lang/Class"); + jobject clsObj = env->CallObjectMethod( envptr, objToDump, midGetClass); + + jmethodID gnid = env->GetMethodID(envptr, ClassClass, "getName", "()Ljava/lang/String;"); + jstring nameObj = (jstring)env->CallObjectMethod( envptr, clsObj, gnid); + const char *name = env->GetStringUTFChars( envptr, nameObj, 0); + printf( "Class Name: %s\n", name ); + env->ReleaseStringUTFChars(envptr, nameObj, name); + env->DeleteLocalRef( envptr, nameObj ); + + jmethodID getMethodsMethod = env->GetMethodID( envptr, ClassClass, "getMethods","()[Ljava/lang/reflect/Method;"); + jobject jobjArray = env->CallObjectMethod( envptr, clsObj, getMethodsMethod ); + + jclass MethodType = env->FindClass(envptr, "java/lang/reflect/Method"); + jclass TypeType = env->FindClass(envptr, "java/lang/reflect/Type"); + jmethodID tnamemid = env->GetMethodID( envptr, TypeType, "getTypeName", "()Ljava/lang/String;" ); + + jmethodID getFieldsMethod = env->GetMethodID( envptr, ClassClass, "getFields","()[Ljava/lang/reflect/Field;"); + jobject jobjArrayFields = env->CallObjectMethod( envptr, clsObj, getFieldsMethod ); + + jsize len = env->GetArrayLength(envptr, jobjArray); + jsize i; + printf( "Methods:\n" ); + + for (i = 0 ; i < len ; i++) { + jobject _strMethod = env->GetObjectArrayElement( envptr, jobjArray, i ) ; + jclass _methodClazz = env->GetObjectClass(envptr, _strMethod) ; + jmethodID mid = env->GetMethodID(envptr, _methodClazz , "getName", "()Ljava/lang/String;" ); + jmethodID getGenericParameterTypes = env->GetMethodID( envptr, _methodClazz, "getGenericParameterTypes","()[Ljava/lang/reflect/Type;"); + jmethodID getReturnTypeMethod = env->GetMethodID( envptr, _methodClazz, "getGenericReturnType","()Ljava/lang/reflect/Type;"); + jstring _name = (jstring)env->CallObjectMethod( envptr, _strMethod , mid ) ; + const char *str = env->GetStringUTFChars(envptr, _name, 0); + printf(" %s( ", str); + + jobject types = env->CallObjectMethod( envptr, _strMethod, getGenericParameterTypes ); + jsize mlen = env->GetArrayLength(envptr, types); + jsize mi; + for( mi = 0; mi < mlen; mi++ ) + { + jobject typeo = env->GetObjectArrayElement( envptr, types, mi ); + jstring _tn = (jstring)env->CallObjectMethod( envptr, typeo, tnamemid ); + const char * str = env->GetStringUTFChars(envptr, _tn, 0); + printf("%s%s ", str, (mi == mlen-1)?"":"," ); + env->ReleaseStringUTFChars(envptr, _tn, str); + env->DeleteLocalRef( envptr, _tn ); + env->DeleteLocalRef( envptr, typeo ); + } + + jobject rtype = env->CallObjectMethod( envptr, _strMethod, getReturnTypeMethod ); + jstring _tn = (jstring)env->CallObjectMethod( envptr, rtype, tnamemid ); + const char * strret = env->GetStringUTFChars(envptr, _tn, 0); + printf(") -> %s\n", strret); + env->ReleaseStringUTFChars(envptr, _tn, strret); + env->DeleteLocalRef( envptr, _tn ); + env->DeleteLocalRef( envptr, rtype ); + + + env->DeleteLocalRef( envptr, types ); + env->ReleaseStringUTFChars(envptr, _name, str); + env->DeleteLocalRef( envptr, _methodClazz ); + env->DeleteLocalRef( envptr, _strMethod ); + env->DeleteLocalRef( envptr, _name ); + } + len = env->GetArrayLength(envptr, jobjArrayFields); + printf( "Fields:\n" ); + for ( i = 0; i < len; i++) { + jobject _strField = env->GetObjectArrayElement( envptr, jobjArrayFields, i) ; + jclass _methodClazz = env->GetObjectClass(envptr, _strField ); + jmethodID mid = env->GetMethodID(envptr, _methodClazz , "getName", "()Ljava/lang/String;") ; + jstring _name = (jstring)env->CallObjectMethod( envptr, _strField , mid ) ; + const char *str = env->GetStringUTFChars(envptr, _name, 0); + + jmethodID getTypeMethod = env->GetMethodID( envptr, _methodClazz, "getGenericType","()Ljava/lang/reflect/Type;"); + jobject rtype = env->CallObjectMethod( envptr, _strField, getTypeMethod ); + jstring _tn = (jstring)env->CallObjectMethod( envptr, rtype, tnamemid ); + const char * strret = env->GetStringUTFChars(envptr, _tn, 0); + + printf(" %s -> %s\n", str, strret ); + env->ReleaseStringUTFChars(envptr, _name, str); + env->ReleaseStringUTFChars(envptr, _tn, strret); + env->DeleteLocalRef( envptr, _methodClazz ); + env->DeleteLocalRef( envptr, _strField ); + env->DeleteLocalRef( envptr, _name ); + env->DeleteLocalRef( envptr, _tn ); + } + env->DeleteLocalRef( envptr, TypeType ); + env->DeleteLocalRef( envptr, MethodType ); + env->DeleteLocalRef( envptr, clsObj ); + env->DeleteLocalRef( envptr, ClassClass ); + env->DeleteLocalRef( envptr, jobjArrayFields ); + env->DeleteLocalRef( envptr, jobjArray ); + + env->DeleteLocalRef( envptr, mpclass ); +} +#endif + +#endif + diff --git a/app/src/nativecode/rawdraw/CNFGEGLDriver.c b/app/src/nativecode/rawdraw/CNFGEGLDriver.c new file mode 100644 index 0000000..8a72355 --- /dev/null +++ b/app/src/nativecode/rawdraw/CNFGEGLDriver.c @@ -0,0 +1,866 @@ +/* + * Copyright (c) 2011-2013 Luc Verhaegen + * Copyright (c) 2018-2020 <>< Charles Lohr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + + +#if defined( __android__ ) && !defined( ANDROID ) +#define ANDROID +#endif + +//Note: This interface provides the following two things privately. +//you may "extern" them in your code. + + +#ifdef ANDROID + + +#include "CNFGAndroid.h" +extern struct android_app * gapp; +static int OGLESStarted; +void (*HandleCustomEventCallback)(); +int android_width, android_height; +int override_android_screen_dimensons = 0; +int android_sdk_version; + +#include +#include +#include +#define ERRLOG(...) printf( __VA_ARGS__ ); +#else +#define ERRLOG(...) fprintf( stderr, __VA_ARGS__ ); +#endif + + + +#include "CNFG.h" + +#include +#include +#include +#include +#include + +#ifdef ANDROID +#include +#else +#include +#endif + +#define EGL_ZBITS 16 +#define EGL_IMMEDIATE_SIZE 2048 + +#ifdef USE_EGL_X + #error This feature has never been completed or tested. + Display *XDisplay; + Window XWindow; +#else + typedef enum + { + FBDEV_PIXMAP_DEFAULT = 0, + FBDEV_PIXMAP_SUPPORTS_UMP = (1<<0), + FBDEV_PIXMAP_ALPHA_FORMAT_PRE = (1<<1), + FBDEV_PIXMAP_COLORSPACE_sRGB = (1<<2), + FBDEV_PIXMAP_EGL_MEMORY = (1<<3) /* EGL allocates/frees this memory */ + } fbdev_pixmap_flags; + + typedef struct fbdev_window + { + unsigned short width; + unsigned short height; + } fbdev_window; + + typedef struct fbdev_pixmap + { + unsigned int height; + unsigned int width; + unsigned int bytes_per_pixel; + unsigned char buffer_size; + unsigned char red_size; + unsigned char green_size; + unsigned char blue_size; + unsigned char alpha_size; + unsigned char luminance_size; + fbdev_pixmap_flags flags; + unsigned short *data; + unsigned int format; /* extra format information in case rgbal is not enough, especially for YUV formats */ + } fbdev_pixmap; + +#if defined( ANDROID ) +EGLNativeWindowType native_window; +#else +struct fbdev_window native_window; +#endif + +#endif + + +static EGLint const config_attribute_list[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_BUFFER_SIZE, 32, + EGL_STENCIL_SIZE, 0, + EGL_DEPTH_SIZE, EGL_ZBITS, + //EGL_SAMPLES, 1, +#ifdef ANDROID +#if ANDROIDVERSION >= 28 + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, +#else + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, +#endif + +#else + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PIXMAP_BIT, +#endif + EGL_NONE +}; + + +static EGLint window_attribute_list[] = { + EGL_NONE +}; + +static const EGLint context_attribute_list[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE +}; + +EGLDisplay egl_display; +EGLSurface egl_surface; +EGLContext egl_context; +EGLConfig egl_config; + +void CNFGSetVSync( int vson ) +{ + eglSwapInterval(egl_display, vson); +} + +static short iLastInternalW, iLastInternalH; + +void CNFGSwapBuffers() +{ + if ( egl_display == EGL_NO_DISPLAY ) return; + CNFGFlushRender(); + eglSwapBuffers(egl_display, egl_surface); +#ifdef ANDROID + if( !override_android_screen_dimensons ) + { + android_width = ANativeWindow_getWidth( native_window ); + android_height = ANativeWindow_getHeight( native_window ); + } + glViewport( 0, 0, android_width, android_height ); + if( iLastInternalW != android_width || iLastInternalH != android_height ) + CNFGInternalResize( iLastInternalW=android_width, iLastInternalH=android_height ); +#endif +} + +void CNFGGetDimensions( short * x, short * y ) +{ +#ifdef ANDROID + *x = android_width; + *y = android_height; +#else + *x = native_window.width; + *y = native_window.height; +#endif + if( *x != iLastInternalW || *y != iLastInternalH ) + CNFGInternalResize( iLastInternalW=*x, iLastInternalH=*y ); +} + +int CNFGSetup( const char * WindowName, int w, int h ) +{ + EGLint egl_major, egl_minor; + EGLint num_config; + + //This MUST be called before doing any initialization. + int events; + while( !OGLESStarted ) + { + struct android_poll_source* source; + if (ALooper_pollOnce( 0, 0, &events, (void**)&source) >= 0) + { + if (source != NULL) source->process(gapp, source); + } + } + + +#ifdef USE_EGL_X + XDisplay = XOpenDisplay(NULL); + if (!XDisplay) { + ERRLOG( "Error: failed to open X display.\n"); + return -1; + } + + Window XRoot = DefaultRootWindow(XDisplay); + + XSetWindowAttributes XWinAttr; + XWinAttr.event_mask = ExposureMask | PointerMotionMask; + + XWindow = XCreateWindow(XDisplay, XRoot, 0, 0, WIDTH, HEIGHT, 0, + CopyFromParent, InputOutput, + CopyFromParent, CWEventMask, &XWinAttr); + + Atom XWMDeleteMessage = + XInternAtom(XDisplay, "WM_DELETE_WINDOW", False); + + XMapWindow(XDisplay, XWindow); + XStoreName(XDisplay, XWindow, "Mali libs test"); + XSetWMProtocols(XDisplay, XWindow, &XWMDeleteMessage, 1); + + egl_display = eglGetDisplay((EGLNativeDisplayType) XDisplay); +#else + +#ifndef ANDROID + if( w >= 1 && h >= 1 ) + { + native_window.width = w; + native_window.height =h; + } +#endif + + egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); +#endif + if (egl_display == EGL_NO_DISPLAY) { + ERRLOG( "Error: No display found!\n"); + return -1; + } + + if (!eglInitialize(egl_display, &egl_major, &egl_minor)) { + ERRLOG( "Error: eglInitialise failed!\n"); + return -1; + } + + printf("EGL Version: \"%s\"\n", + eglQueryString(egl_display, EGL_VERSION)); + printf("EGL Vendor: \"%s\"\n", + eglQueryString(egl_display, EGL_VENDOR)); + printf("EGL Extensions: \"%s\"\n", + eglQueryString(egl_display, EGL_EXTENSIONS)); + + eglChooseConfig(egl_display, config_attribute_list, &egl_config, 1, + &num_config); + printf( "Config: %d\n", num_config ); + + printf( "Creating Context\n" ); + egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, +// NULL ); + context_attribute_list); + if (egl_context == EGL_NO_CONTEXT) { + ERRLOG( "Error: eglCreateContext failed: 0x%08X\n", + eglGetError()); + return -1; + } + printf( "Context Created %p\n", egl_context ); + +#ifdef USE_EGL_X + egl_surface = eglCreateWindowSurface(egl_display, egl_config, XWindow, + window_attribute_list); +#else + + if( native_window && !gapp->window ) + { + printf( "WARNING: App restarted without a window. Cannot progress.\n" ); + exit( 0 ); + } + + printf( "Getting Surface %p\n", native_window = gapp->window ); + + if( !native_window ) + { + printf( "FAULT: Cannot get window\n" ); + return -5; + } + + if( w <= 0 || h <= 0 ) + { + android_width = ANativeWindow_getWidth( native_window ); + android_height = ANativeWindow_getHeight( native_window ); + } + else + { + override_android_screen_dimensons = 1; + android_width = w; + android_height = h; + } + printf( "Width/Height: %dx%d\n", android_width, android_height ); + egl_surface = eglCreateWindowSurface(egl_display, egl_config, +#ifdef ANDROID + gapp->window, +#else + (EGLNativeWindowType)&native_window, +#endif + window_attribute_list); +#endif + printf( "Got Surface: %p\n", egl_surface ); + + if (egl_surface == EGL_NO_SURFACE) { + ERRLOG( "Error: eglCreateWindowSurface failed: " + "0x%08X\n", eglGetError()); + return -1; + } + +#ifndef ANDROID + int width, height; + if (!eglQuerySurface(egl_display, egl_surface, EGL_WIDTH, &width) || + !eglQuerySurface(egl_display, egl_surface, EGL_HEIGHT, &height)) { + ERRLOG( "Error: eglQuerySurface failed: 0x%08X\n", + eglGetError()); + return -1; + } + printf("Surface size: %dx%d\n", width, height); + + native_window.width = width; + native_window.height = height; +#endif + + if (!eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)) { + ERRLOG( "Error: eglMakeCurrent() failed: 0x%08X\n", + eglGetError()); + return -1; + } + + printf("GL Vendor: \"%s\"\n", glGetString(GL_VENDOR)); + printf("GL Renderer: \"%s\"\n", glGetString(GL_RENDERER)); + printf("GL Version: \"%s\"\n", glGetString(GL_VERSION)); + printf("GL Extensions: \"%s\"\n", glGetString(GL_EXTENSIONS)); + + CNFGSetupBatchInternal(); + + { + short dummyx, dummyy; + CNFGGetDimensions( &dummyx, &dummyy ); + } + + return 0; +} + +void CNFGSetupFullscreen( const char * WindowName, int screen_number ) +{ + //Removes decoration, must be called before setup. + AndroidMakeFullscreen(); + + CNFGSetup( WindowName, -1, -1 ); +} + +int debuga, debugb, debugc; + +int32_t handle_input(struct android_app* app, AInputEvent* event) +{ +#ifdef ANDROID + //Potentially do other things here. + + if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) + { + int action = AMotionEvent_getAction( event ); + int whichsource = action >> 8; + action &= AMOTION_EVENT_ACTION_MASK; + size_t pointerCount = AMotionEvent_getPointerCount(event); + + for (size_t i = 0; i < pointerCount; ++i) + { + int x, y, index; + x = AMotionEvent_getX(event, i); + y = AMotionEvent_getY(event, i); + index = AMotionEvent_getPointerId( event, i ); + + if( action == AMOTION_EVENT_ACTION_POINTER_DOWN || action == AMOTION_EVENT_ACTION_DOWN ) + { + int id = index; + if( action == AMOTION_EVENT_ACTION_POINTER_DOWN && id != whichsource ) continue; + HandleButton( x, y, id, 1 ); + ANativeActivity_showSoftInput( gapp->activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED ); + } + else if( action == AMOTION_EVENT_ACTION_POINTER_UP || action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL ) + { + int id = index; + if( action == AMOTION_EVENT_ACTION_POINTER_UP && id != whichsource ) continue; + HandleButton( x, y, id, 0 ); + } + else if( action == AMOTION_EVENT_ACTION_MOVE ) + { + HandleMotion( x, y, index ); + } + } + return 1; + } + else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) + { + int code = AKeyEvent_getKeyCode(event); +#ifdef ANDROID_USE_SCANCODES + HandleKey( code, AKeyEvent_getAction(event) ); +#else + int unicode = AndroidGetUnicodeChar( code, AMotionEvent_getMetaState( event ) ); + if( unicode ) + HandleKey( unicode, AKeyEvent_getAction(event) ); + else + { + HandleKey( code, !AKeyEvent_getAction(event) ); + return (code == 4)?1:0; //don't override functionality. + } +#endif + + return 1; + } +#endif + return 0; +} + +int CNFGHandleInput() +{ + +#ifdef ANDROID + int events; + struct android_poll_source* source; + while( ALooper_pollOnce( 0, 0, &events, (void**)&source) >= 0 ) + { + if (source != NULL) + { + source->process(gapp, source); + } + } +#endif + +#ifdef USE_EGL_X + while (1) { + XEvent event; + + XNextEvent(XDisplay, &event); + + if ((event.type == MotionNotify) || + (event.type == Expose)) + Redraw(width, height); + else if (event.type == ClientMessage) { + if (event.xclient.data.l[0] == XWMDeleteMessage) + break; + } + } + XSetWMProtocols(XDisplay, XWindow, &XWMDeleteMessage, 0); +#endif + return 1; +} + + + +#ifdef ANDROID + +void (*HandleWindowTermination)(); + +void handle_cmd(struct android_app* app, int32_t cmd) +{ + switch (cmd) + { + case APP_CMD_DESTROY: + //This gets called initially after back. + HandleDestroy(); + ANativeActivity_finish( gapp->activity ); + break; + case APP_CMD_INIT_WINDOW: + //When returning from a back button suspension, this isn't called. + if( !OGLESStarted ) + { + OGLESStarted = 1; + printf( "Got start event\n" ); + } + else + { + CNFGSetup( "", -1, -1 ); + HandleResume(); + } + break; + case APP_CMD_TERM_WINDOW: + //This gets called initially when you click "back" + //This also gets called when you are brought into standby. + //Not sure why - callbacks here seem to break stuff. + if( egl_display != EGL_NO_DISPLAY ) { + eglMakeCurrent( egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); + if( egl_context != EGL_NO_CONTEXT ) { + eglDestroyContext( egl_display, egl_context ); + } + if( egl_surface != EGL_NO_SURFACE ) { + eglDestroySurface( egl_display, egl_surface ); + } + eglTerminate( egl_display ); + } + egl_context = EGL_NO_CONTEXT; + egl_surface = EGL_NO_SURFACE; + egl_display = EGL_NO_DISPLAY; +#ifdef ANDROID_WANT_WINDOW_TERMINATION + if( HandleWindowTermination ) HandleWindowTermination(); +#endif + break; + + case APP_CMD_PAUSE: + HandleSuspend(); + break; + + case APP_CMD_RESUME: + HandleResume(); + break; + case APP_CMD_CUSTOM_EVENT: + if( HandleCustomEventCallback ) HandleCustomEventCallback(); + break; + default: + printf( "event not handled: %d\n", cmd); + } +} + +int __system_property_get(const char* name, char* value); + +void android_main(struct android_app* app) +{ + int main( int argc, char ** argv ); + char mainptr[5] = { 'm', 'a', 'i', 'n', 0 }; + char * argv[] = { mainptr, 0 }; + + { + char sdk_ver_str[92]; + int len = __system_property_get("ro.build.version.sdk", sdk_ver_str); + if( len <= 0 ) + android_sdk_version = 0; + else + android_sdk_version = atoi(sdk_ver_str); + } + + gapp = app; + app->onAppCmd = handle_cmd; + app->onInputEvent = handle_input; + printf( "Starting with Android SDK Version: %d\n", android_sdk_version ); + main( 1, argv ); + printf( "Main Complete\n" ); +} + +#ifdef __cplusplus +#define SETUP_FOR_JAVA_CALL \ + JNIEnv * env = 0; \ + JNIEnv ** envptr = &env; \ + JavaVM * jniiptr = gapp->activity->vm; \ + jniiptr->AttachCurrentThread( (JNIEnv**)&env, 0 ); \ + env = (*envptr); +#define ENVCALL +#define JAVA_CALL_DETACH jniiptr->DetachCurrentThread(); +#else +#define SETUP_FOR_JAVA_CALL \ + const struct JNINativeInterface * env = (struct JNINativeInterface*)gapp->activity->env; \ + const struct JNINativeInterface ** envptr = &env; \ + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; \ + const struct JNIInvokeInterface * jnii = *jniiptr; \ + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); \ + env = (*envptr); +#define ENVCALL envptr, +#define JAVA_CALL_DETACH jnii->DetachCurrentThread( jniiptr ); +#endif + +void AndroidMakeFullscreen() +{ + //Partially based on https://stackoverflow.com/questions/47507714/how-do-i-enable-full-screen-immersive-mode-for-a-native-activity-ndk-app + SETUP_FOR_JAVA_CALL + + //Get android.app.NativeActivity, then get getWindow method handle, returns view.Window type + jclass activityClass = env->FindClass( ENVCALL "android/app/NativeActivity"); + jmethodID getWindow = env->GetMethodID( ENVCALL activityClass, "getWindow", "()Landroid/view/Window;"); + jobject window = env->CallObjectMethod( ENVCALL gapp->activity->clazz, getWindow); + jclass windowClass = env->FindClass( ENVCALL "android/view/Window"); + jmethodID getDecorView = env->GetMethodID( ENVCALL windowClass, "getDecorView", "()Landroid/view/View;"); + jobject decorView = env->CallObjectMethod( ENVCALL window, getDecorView); + + /* + jclass ClassActivity = env->FindClass( ENVCALL "android/app/Activity" ); + const int flag_WindowProp = env->GetStaticIntField( ENVCALL windowClass, env->GetStaticFieldID( ENVCALL windowClass, "FEATURE_NO_TITLE", "I") ); + jmethodID requestWindowFeature = env->GetMethodID( ENVCALL ClassActivity, "requestWindowFeature", "(I)Z" ); + jobject lNativeActivity = gapp->activity->clazz; + env->CallBooleanMethod( ENVCALL lNativeActivity, requestWindowFeature, flag_WindowProp ); + */ + + //Get the flag values associated with systemuivisibility + jclass viewClass = env->FindClass( ENVCALL "android/view/View"); + const int flagLayoutHideNavigation = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION", "I")); + const int flagLayoutFullscreen = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN", "I")); + const int flagLowProfile = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LOW_PROFILE", "I")); + const int flagHideNavigation = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_HIDE_NAVIGATION", "I")); + const int flagFullscreen = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_FULLSCREEN", "I")); + const int flagImmersiveSticky = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY", "I")); + const int flagLayoutStable = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LAYOUT_STABLE", "I")); + jmethodID setSystemUiVisibility = env->GetMethodID( ENVCALL viewClass, "setSystemUiVisibility", "(I)V"); + //Call the decorView.setSystemUiVisibility(FLAGS) + env->CallVoidMethod( ENVCALL decorView, setSystemUiVisibility, + (flagLayoutHideNavigation | flagLayoutFullscreen | flagLowProfile | flagHideNavigation | flagFullscreen | flagImmersiveSticky | flagLayoutStable)); + + //now set some more flags associated with layoutmanager -- note the $ in the class path + //search for api-versions.xml + //https://android.googlesource.com/platform/development/+/refs/tags/android-9.0.0_r48/sdk/api-versions.xml + jclass layoutManagerClass = env->FindClass( ENVCALL "android/view/WindowManager$LayoutParams"); + const int flag_WinMan_Fullscreen = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_FULLSCREEN", "I") )); + const int flag_WinMan_KeepScreenOn = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_KEEP_SCREEN_ON", "I") )); + const int flag_WinMan_hw_acc = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_HARDWARE_ACCELERATED", "I") )); + const int flag_WinMan_NoLimits = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_LAYOUT_NO_LIMITS", "I") )); + // const int flag_WinMan_flag_not_fullscreen = env->GetStaticIntField(layoutManagerClass, (env->GetStaticFieldID(layoutManagerClass, "FLAG_FORCE_NOT_FULLSCREEN", "I") )); + //call window.addFlags(FLAGS) + env->CallVoidMethod( ENVCALL window, (env->GetMethodID (ENVCALL windowClass, "addFlags" , "(I)V")), (flag_WinMan_Fullscreen | flag_WinMan_KeepScreenOn | flag_WinMan_hw_acc | flag_WinMan_NoLimits)); + + + +/* + // Seems to have no impact, and doesn't work with older Android versions. + jmethodID setDecorFitsSystemWindows = env->GetMethodID( ENVCALL windowClass, "setDecorFitsSystemWindows", "(Z)V"); + env->CallVoidMethod( ENVCALL window, setDecorFitsSystemWindows, JNI_FALSE ); + + // "Immersive Mode" (Since Android 11+) + jmethodID getWindowInsetsController = env->GetMethodID( ENVCALL viewClass, "getWindowInsetsController", "()Landroid/view/WindowInsetsController;" ); + if( getWindowInsetsController ) + { + //windowInsetsController.hide(Type.systemBars()) + jobject windowInsetsController = env->CallObjectMethod( ENVCALL decorView, getWindowInsetsController ); + jclass windowInsetsControllerClass = env->FindClass( ENVCALL "android/view/WindowInsetsController" ); + + jclass windowInsetsTypeClass = env->FindClass( ENVCALL "android/view/WindowInsets/Type" ); + jmethodID typeSystemBars = env->GetStaticMethodID( ENVCALL windowInsetsTypeClass, "systemBars", "()I" ); + int systemBarsType = env->CallStaticIntMethod( ENVCALL windowInsetsTypeClass, typeSystemBars ); + + jmethodID hide = env->GetMethodID( ENVCALL windowInsetsControllerClass, "hide", "(I)V" ); + env->CallVoidMethod( ENVCALL windowInsetsController, hide, systemBarsType ); + } +*/ + + JAVA_CALL_DETACH +} + +const char* AndroidGetExternalFilesDir() +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + jclass activityClass = env->FindClass( envptr, "android/app/NativeActivity"); + jobject lNativeActivity = gapp->activity->clazz; + + jmethodID mid_getExtStorage = env->GetMethodID(envptr,activityClass,"getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;"); + jobject obj_File = env->CallObjectMethod(envptr,lNativeActivity, mid_getExtStorage, NULL); + jclass cls_File = env->FindClass(envptr,"java/io/File"); + jmethodID mid_getPath = env->GetMethodID(envptr,cls_File, "getPath", "()Ljava/lang/String;"); + jstring obj_Path = (jstring) env->CallObjectMethod(envptr,obj_File, mid_getPath); + const char* path = env->GetStringUTFChars(envptr,obj_Path, NULL); + //printf("EXTERNAL PATH = %s\n", path); + env->ReleaseStringUTFChars(envptr,obj_Path, path); + jnii->DetachCurrentThread( jniiptr ); + return path; +} + +void AndroidDisplayKeyboard(int pShow) +{ + //Based on https://stackoverflow.com/questions/5864790/how-to-show-the-soft-keyboard-on-native-activity + jint lFlags = 0; + SETUP_FOR_JAVA_CALL + + jclass activityClass = env->FindClass( ENVCALL "android/app/NativeActivity"); + + // Retrieves NativeActivity. + jobject lNativeActivity = gapp->activity->clazz; + + + // Retrieves Context.INPUT_METHOD_SERVICE. + jclass ClassContext = env->FindClass( ENVCALL "android/content/Context"); + jfieldID FieldINPUT_METHOD_SERVICE = env->GetStaticFieldID( ENVCALL ClassContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;" ); + jobject INPUT_METHOD_SERVICE = env->GetStaticObjectField( ENVCALL ClassContext, FieldINPUT_METHOD_SERVICE ); + + // Runs getSystemService(Context.INPUT_METHOD_SERVICE). + jclass ClassInputMethodManager = env->FindClass( ENVCALL "android/view/inputmethod/InputMethodManager" ); + jmethodID MethodGetSystemService = env->GetMethodID( ENVCALL activityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); + jobject lInputMethodManager = env->CallObjectMethod( ENVCALL lNativeActivity, MethodGetSystemService, INPUT_METHOD_SERVICE); + + // Runs getWindow().getDecorView(). + jmethodID MethodGetWindow = env->GetMethodID( ENVCALL activityClass, "getWindow", "()Landroid/view/Window;"); + jobject lWindow = env->CallObjectMethod( ENVCALL lNativeActivity, MethodGetWindow); + jclass ClassWindow = env->FindClass( ENVCALL "android/view/Window"); + jmethodID MethodGetDecorView = env->GetMethodID( ENVCALL ClassWindow, "getDecorView", "()Landroid/view/View;"); + jobject lDecorView = env->CallObjectMethod( ENVCALL lWindow, MethodGetDecorView); + + if (pShow) { + // Runs lInputMethodManager.showSoftInput(...). + jmethodID MethodShowSoftInput = env->GetMethodID( ENVCALL ClassInputMethodManager, "showSoftInput", "(Landroid/view/View;I)Z"); + /*jboolean lResult = */env->CallBooleanMethod( ENVCALL lInputMethodManager, MethodShowSoftInput, lDecorView, lFlags); + } else { + // Runs lWindow.getViewToken() + jclass ClassView = env->FindClass( ENVCALL "android/view/View"); + jmethodID MethodGetWindowToken = env->GetMethodID( ENVCALL ClassView, "getWindowToken", "()Landroid/os/IBinder;"); + jobject lBinder = env->CallObjectMethod( ENVCALL lDecorView, MethodGetWindowToken); + + // lInputMethodManager.hideSoftInput(...). + jmethodID MethodHideSoftInput = env->GetMethodID( ENVCALL ClassInputMethodManager, "hideSoftInputFromWindow", "(Landroid/os/IBinder;I)Z"); + /*jboolean lRes = */env->CallBooleanMethod( ENVCALL lInputMethodManager, MethodHideSoftInput, lBinder, lFlags); + } + + JAVA_CALL_DETACH +} + +int AndroidGetUnicodeChar( int keyCode, int metaState ) +{ + //https://stackoverflow.com/questions/21124051/receive-complete-android-unicode-input-in-c-c/43871301 + + int eventType = AKEY_EVENT_ACTION_DOWN; + + SETUP_FOR_JAVA_CALL + + //jclass activityClass = env->FindClass( envptr, "android/app/NativeActivity"); + // Retrieves NativeActivity. + //jobject lNativeActivity = gapp->activity->clazz; + + jclass class_key_event = env->FindClass( ENVCALL "android/view/KeyEvent"); + int unicodeKey; + + jmethodID method_get_unicode_char = env->GetMethodID( ENVCALL class_key_event, "getUnicodeChar", "(I)I"); + jmethodID eventConstructor = env->GetMethodID( ENVCALL class_key_event, "", "(II)V"); + jobject eventObj = env->NewObject( ENVCALL class_key_event, eventConstructor, eventType, keyCode); + + unicodeKey = env->CallIntMethod( ENVCALL eventObj, method_get_unicode_char, metaState ); + + // Finished with the JVM. + JAVA_CALL_DETACH + + //printf("Unicode key is: %d", unicodeKey); + return unicodeKey; +} + + +//Based on: https://stackoverflow.com/questions/41820039/jstringjni-to-stdstringc-with-utf8-characters +#ifdef __cplusplus +jstring android_permission_name(JNIEnv ** envptr, const char* perm_name) +#else +jstring android_permission_name(const struct JNINativeInterface ** envptr, const char* perm_name) +#endif +{ + // nested class permission in class android.Manifest, + // hence android 'slash' Manifest 'dollar' permission +#ifdef __cplusplus + JNIEnv * env = *envptr; +#else + const struct JNINativeInterface * env = *envptr; +#endif + jclass ClassManifestpermission = env->FindClass( ENVCALL "android/Manifest$permission"); + jfieldID lid_PERM = env->GetStaticFieldID( ENVCALL ClassManifestpermission, perm_name, "Ljava/lang/String;" ); + jstring ls_PERM = (jstring)(env->GetStaticObjectField( ENVCALL ClassManifestpermission, lid_PERM )); + return ls_PERM; +} + +/** + * \brief Tests whether a permission is granted. + * \param[in] app a pointer to the android app. + * \param[in] perm_name the name of the permission, e.g., + * "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE". + * \retval true if the permission is granted. + * \retval false otherwise. + * \note Requires Android API level 23 (Marshmallow, May 2015) + */ +int AndroidHasPermissions( const char* perm_name) +{ + struct android_app* app = gapp; + SETUP_FOR_JAVA_CALL + + if( android_sdk_version < 23 ) + { + printf( "Android SDK version %d does not support AndroidHasPermissions\n", android_sdk_version ); + return 1; + } + + int result = 0; + jstring ls_PERM = android_permission_name(envptr, perm_name); + + jint PERMISSION_GRANTED = (-1); + + { + jclass ClassPackageManager = env->FindClass( ENVCALL "android/content/pm/PackageManager" ); + jfieldID lid_PERMISSION_GRANTED = env->GetStaticFieldID( ENVCALL ClassPackageManager, "PERMISSION_GRANTED", "I" ); + PERMISSION_GRANTED = env->GetStaticIntField( ENVCALL ClassPackageManager, lid_PERMISSION_GRANTED ); + } + { + jobject activity = app->activity->clazz; + jclass ClassContext = env->FindClass( ENVCALL "android/content/Context" ); + jmethodID MethodcheckSelfPermission = env->GetMethodID( ENVCALL ClassContext, "checkSelfPermission", "(Ljava/lang/String;)I" ); + jint int_result = env->CallIntMethod( ENVCALL activity, MethodcheckSelfPermission, ls_PERM ); + result = (int_result == PERMISSION_GRANTED); + } + + JAVA_CALL_DETACH + + return result; +} + +/** + * \brief Query file permissions. + * \details This opens the system dialog that lets the user + * grant (or deny) the permission. + * \param[in] app a pointer to the android app. + * \note Requires Android API level 23 (Marshmallow, May 2015) + */ +void AndroidRequestAppPermissions(const char * perm) +{ + if( android_sdk_version < 23 ) + { + printf( "Android SDK version %d does not support AndroidRequestAppPermissions\n",android_sdk_version ); + return; + } + + struct android_app* app = gapp; + SETUP_FOR_JAVA_CALL + + jobject activity = app->activity->clazz; + + jobjectArray perm_array = env->NewObjectArray( ENVCALL 1, env->FindClass( ENVCALL "java/lang/String"), env->NewStringUTF( ENVCALL "" ) ); + env->SetObjectArrayElement( ENVCALL perm_array, 0, android_permission_name(envptr, perm ) ); + jclass ClassActivity = env->FindClass( ENVCALL "android/app/Activity" ); + + jmethodID MethodrequestPermissions = env->GetMethodID( ENVCALL ClassActivity, "requestPermissions", "([Ljava/lang/String;I)V" ); + + // Last arg (0) is just for the callback (that I do not use) + env->CallVoidMethod( ENVCALL activity, MethodrequestPermissions, perm_array, 0 ); + + JAVA_CALL_DETACH +} + +/* Example: + int hasperm = android_has_permission( "RECORD_AUDIO" ); + if( !hasperm ) + { + android_request_app_permissions( "RECORD_AUDIO" ); + } +*/ + +void AndroidSendToBack( int param ) +{ + struct android_app* app = gapp; + SETUP_FOR_JAVA_CALL + jobject activity = app->activity->clazz; + + //_glfmCallJavaMethodWithArgs(jni, gapp->activity->clazz, "moveTaskToBack", "(Z)Z", Boolean, false); + jclass ClassActivity = env->FindClass( ENVCALL "android/app/Activity" ); + jmethodID MethodmoveTaskToBack = env->GetMethodID( ENVCALL ClassActivity, "moveTaskToBack", "(Z)Z" ); + env->CallBooleanMethod( ENVCALL activity, MethodmoveTaskToBack, param ); + JAVA_CALL_DETACH +} + +#endif + diff --git a/app/src/nativecode/rawdraw/CNFGEGLLeanAndMean.c b/app/src/nativecode/rawdraw/CNFGEGLLeanAndMean.c new file mode 100644 index 0000000..0bcff15 --- /dev/null +++ b/app/src/nativecode/rawdraw/CNFGEGLLeanAndMean.c @@ -0,0 +1,137 @@ +//Copyright (c) 2011, 2017, 2018, 2020 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose. + +//This driver cannot create an OpenGL Surface, but can be used for computing for background tasks. + +//NOTE: This is a truly incomplete driver - if no EGL surface is available, it does not support direct buffer rendering. +//Additionally no input is connected. + +#include "CNFG.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + + +static const EGLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_NONE +}; + +EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE +}; + +static int pbufferWidth = 0; +static int pbufferHeight = 0; + +static EGLint pbufferAttribs[] = { + EGL_WIDTH, 0, + EGL_HEIGHT, 0, + EGL_NONE, +}; + +EGLDisplay eglDpy = 0; +EGLContext eglCtx = 0; +EGLSurface eglSurf = 0; + +void CNFGGetDimensions( short * x, short * y ) +{ + *x = pbufferWidth; + *y = pbufferHeight; +} + +void CNFGChangeWindowTitle( const char * WindowName ) +{ +} + +void CNFGSetupFullscreen( const char * WindowName, int screen_no ) +{ + //Fullscreen is meaningless for this driver, since it doesn't really open a window. + CNFGSetup( WindowName, 1024, 1024 ); +} + +void CNFGTearDown() +{ + if( eglDpy ) + { + eglTerminate( eglDpy ); + } + //Unimplemented. +} + +int CNFGSetup( const char * WindowName, int w, int h ) +{ + eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + atexit( CNFGTearDown ); + printf( "EGL Display: %p\n", eglDpy ); + + pbufferAttribs[1] = pbufferWidth = w; + pbufferAttribs[3] = pbufferHeight = h; + + EGLint major, minor; + eglInitialize(eglDpy, &major, &minor); + + EGLint numConfigs=0; + EGLConfig eglCfg=NULL; + + eglChooseConfig(eglDpy, configAttribs, 0, 0, &numConfigs); //this gets number of configs + if (numConfigs) { + eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs); + printf( " EGL config found\n" ); + } else { + printf( " Error could not find a valid config avail.. \n" ); + } + + printf( "EGL Major Minor: %d %d\n", major, minor ); + eglBindAPI(EGL_OPENGL_API); + eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, context_attribs); + int err = eglGetError(); if(err != EGL_SUCCESS) { printf("1. Error %d\n", err); } + printf( "EGL Got context: %p\n", eglCtx ); + + if( w > 0 && h > 0 ) + { + eglSurf = eglCreatePbufferSurface(eglDpy, eglCfg, pbufferAttribs); + eglMakeCurrent(eglDpy, eglSurf, eglSurf, eglCtx); + printf( "EGL Current, with surface %p\n", eglSurf ); + //Actually have a surface. Need to allocate it. + EGLint surfwid; + EGLint surfht; + eglQuerySurface(eglDpy, eglSurf, EGL_WIDTH, &surfwid); + eglQuerySurface(eglDpy, eglSurf, EGL_HEIGHT, &surfht); + printf("Window dimensions: %d x %d\n", surfwid, surfht); + } + else + { + eglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, eglCtx); + printf( "EGL Current, no surface.\n" ); + } + return 0; +} + +int CNFGHandleInput() +{ + //Stubbed (No input) + return 1; +} + +void CNFGSetVSync( int vson ) +{ + //No-op +} + +void CNFGSwapBuffers() +{ + //No-op +} + diff --git a/app/src/nativecode/rawdraw/CNFGFunctions.c b/app/src/nativecode/rawdraw/CNFGFunctions.c new file mode 100644 index 0000000..1e62228 --- /dev/null +++ b/app/src/nativecode/rawdraw/CNFGFunctions.c @@ -0,0 +1,959 @@ +/* +Copyright (c) 2010-2021 <>< Charles Lohr, and several others! + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef _CNFG_C +#define _CNFG_C + +#include "CNFG.h" + +#ifndef CNFGHTTPSERVERONLY + +#ifdef _CNFG_FANCYFONT +#include "TextTool/FontData.h" +#endif + +int CNFGPenX, CNFGPenY; +uint32_t CNFGBGColor; +uint32_t CNFGLastColor; +//uint32_t CNFGDialogColor; //background for boxes [DEPRECATED] + +// The following two arrays are generated by Fonter/fonter.cpp +const unsigned short RawdrawFontCharMap[256] = { + 65535, 0, 8, 16, 24, 31, 41, 50, 51, 65535, 65535, 57, 66, 65535, 75, 83, + 92, 96, 100, 108, 114, 123, 132, 137, 147, 152, 158, 163, 169, 172, 178, 182, + 65535, 186, 189, 193, 201, 209, 217, 226, 228, 232, 236, 244, 248, 250, 252, 253, + 255, 261, 266, 272, 278, 283, 289, 295, 300, 309, 316, 318, 321, 324, 328, 331, + 337, 345, 352, 362, 368, 375, 382, 388, 396, 402, 408, 413, 422, 425, 430, 435, + 442, 449, 458, 466, 472, 476, 480, 485, 492, 500, 507, 512, 516, 518, 522, 525, + 527, 529, 536, 541, 546, 551, 557, 564, 572, 578, 581, 586, 593, 595, 604, 610, + 615, 621, 627, 632, 638, 642, 648, 653, 660, 664, 670, 674, 680, 684, 690, 694, + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + 700, 703, 711, 718, 731, 740, 744, 754, 756, 760, 766, 772, 775, 777, 785, 787, + 792, 798, 803, 811, 813, 820, 827, 828, 831, 833, 838, 844, 853, 862, 874, 880, + 889, 898, 908, 919, 928, 939, 951, 960, 969, 978, 988, 997, 1005, 1013, 1022, 1030, + 1039, 1047, 1054, 1061, 1070, 1079, 1086, 1090, 1099, 1105, 1111, 1118, 1124, 1133, 1140, 1150, + 1159, 1168, 1178, 1189, 1198, 1209, 1222, 1231, 1239, 1247, 1256, 1264, 1268, 1272, 1277, 1281, + 1290, 1300, 1307, 1314, 1322, 1331, 1338, 1342, 1349, 1357, 1365, 1374, 1382, 1390, 1397, 65535, }; + +const unsigned char RawdrawFontCharData[1405] = { + 0x00, 0x09, 0x20, 0x29, 0x03, 0x23, 0x14, 0x8b, 0x00, 0x09, 0x20, 0x29, 0x04, 0x24, 0x13, 0x8c, + 0x01, 0x21, 0x23, 0x14, 0x03, 0x09, 0x11, 0x9a, 0x11, 0x22, 0x23, 0x14, 0x03, 0x02, 0x99, 0x01, + 0x21, 0x23, 0x09, 0x03, 0x29, 0x03, 0x09, 0x12, 0x9c, 0x03, 0x2b, 0x13, 0x1c, 0x23, 0x22, 0x11, + 0x02, 0x8b, 0x9a, 0x1a, 0x01, 0x21, 0x23, 0x03, 0x89, 0x03, 0x21, 0x2a, 0x21, 0x19, 0x03, 0x14, + 0x23, 0x9a, 0x01, 0x10, 0x21, 0x12, 0x09, 0x12, 0x1c, 0x03, 0xab, 0x02, 0x03, 0x1b, 0x02, 0x1a, + 0x13, 0x10, 0xa9, 0x01, 0x2b, 0x03, 0x29, 0x02, 0x11, 0x22, 0x13, 0x8a, 0x00, 0x22, 0x04, 0x88, + 0x20, 0x02, 0x24, 0xa8, 0x01, 0x10, 0x29, 0x10, 0x14, 0x0b, 0x14, 0xab, 0x00, 0x0b, 0x0c, 0x20, + 0x2b, 0xac, 0x00, 0x28, 0x00, 0x02, 0x2a, 0x10, 0x1c, 0x20, 0xac, 0x01, 0x21, 0x23, 0x03, 0x09, + 0x20, 0x10, 0x14, 0x8c, 0x03, 0x23, 0x24, 0x04, 0x8b, 0x01, 0x10, 0x29, 0x10, 0x14, 0x0b, 0x14, + 0x2b, 0x04, 0xac, 0x01, 0x18, 0x21, 0x10, 0x9c, 0x03, 0x1c, 0x23, 0x1c, 0x10, 0x9c, 0x02, 0x22, + 0x19, 0x22, 0x9b, 0x02, 0x2a, 0x02, 0x19, 0x02, 0x9b, 0x01, 0x02, 0xaa, 0x02, 0x22, 0x11, 0x02, + 0x13, 0xaa, 0x11, 0x22, 0x02, 0x99, 0x02, 0x13, 0x22, 0x8a, 0x10, 0x1b, 0x9c, 0x10, 0x09, 0x20, + 0x99, 0x10, 0x1c, 0x20, 0x2c, 0x01, 0x29, 0x03, 0xab, 0x21, 0x10, 0x01, 0x23, 0x14, 0x0b, 0x10, + 0x9c, 0x00, 0x09, 0x23, 0x2c, 0x04, 0x03, 0x21, 0xa8, 0x21, 0x10, 0x01, 0x12, 0x03, 0x14, 0x2b, + 0x02, 0xac, 0x10, 0x99, 0x10, 0x01, 0x03, 0x9c, 0x10, 0x21, 0x23, 0x9c, 0x01, 0x2b, 0x11, 0x1b, + 0x21, 0x0b, 0x02, 0xaa, 0x02, 0x2a, 0x11, 0x9b, 0x04, 0x9b, 0x02, 0xaa, 0x9c, 0x03, 0xa9, 0x00, + 0x20, 0x24, 0x04, 0x08, 0x9a, 0x01, 0x10, 0x1c, 0x04, 0xac, 0x01, 0x10, 0x21, 0x22, 0x04, 0xac, + 0x00, 0x20, 0x24, 0x0c, 0x12, 0xaa, 0x00, 0x02, 0x2a, 0x20, 0xac, 0x20, 0x00, 0x02, 0x22, 0x24, + 0x8c, 0x20, 0x02, 0x22, 0x24, 0x04, 0x8a, 0x00, 0x20, 0x21, 0x12, 0x9c, 0x00, 0x0c, 0x00, 0x20, + 0x2c, 0x04, 0x2c, 0x02, 0xaa, 0x00, 0x02, 0x22, 0x20, 0x08, 0x22, 0x8c, 0x19, 0x9b, 0x19, 0x13, + 0x8c, 0x20, 0x02, 0xac, 0x01, 0x29, 0x03, 0xab, 0x00, 0x22, 0x8c, 0x01, 0x10, 0x21, 0x12, 0x1b, + 0x9c, 0x21, 0x01, 0x04, 0x24, 0x22, 0x12, 0x13, 0xab, 0x04, 0x01, 0x10, 0x21, 0x2c, 0x02, 0xaa, + 0x00, 0x04, 0x14, 0x23, 0x12, 0x0a, 0x12, 0x21, 0x10, 0x88, 0x23, 0x14, 0x03, 0x01, 0x10, 0xa9, + 0x00, 0x10, 0x21, 0x23, 0x14, 0x04, 0x88, 0x00, 0x04, 0x2c, 0x00, 0x28, 0x02, 0x9a, 0x00, 0x0c, + 0x00, 0x28, 0x02, 0x9a, 0x21, 0x10, 0x01, 0x03, 0x14, 0x23, 0x22, 0x9a, 0x00, 0x0c, 0x20, 0x2c, + 0x02, 0xaa, 0x00, 0x28, 0x10, 0x1c, 0x04, 0xac, 0x00, 0x20, 0x23, 0x14, 0x8b, 0x00, 0x0c, 0x02, + 0x12, 0x21, 0x28, 0x12, 0x23, 0xac, 0x00, 0x04, 0xac, 0x04, 0x00, 0x11, 0x20, 0xac, 0x04, 0x00, + 0x2a, 0x20, 0xac, 0x01, 0x10, 0x21, 0x23, 0x14, 0x03, 0x89, 0x00, 0x0c, 0x00, 0x10, 0x21, 0x12, + 0x8a, 0x01, 0x10, 0x21, 0x23, 0x14, 0x03, 0x09, 0x04, 0x9b, 0x00, 0x0c, 0x00, 0x10, 0x21, 0x12, + 0x02, 0xac, 0x21, 0x10, 0x01, 0x23, 0x14, 0x8b, 0x00, 0x28, 0x10, 0x9c, 0x00, 0x04, 0x24, 0xa8, + 0x00, 0x03, 0x14, 0x23, 0xa8, 0x00, 0x04, 0x2c, 0x14, 0x1b, 0x24, 0xa8, 0x00, 0x01, 0x23, 0x2c, + 0x04, 0x03, 0x21, 0xa8, 0x00, 0x01, 0x12, 0x1c, 0x12, 0x21, 0xa8, 0x00, 0x20, 0x02, 0x04, 0xac, + 0x10, 0x00, 0x04, 0x9c, 0x01, 0xab, 0x10, 0x20, 0x24, 0x9c, 0x01, 0x10, 0xa9, 0x04, 0xac, 0x00, + 0x99, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x8a, 0x00, 0x04, 0x24, 0x22, 0x8a, 0x24, 0x04, 0x03, + 0x12, 0xaa, 0x20, 0x24, 0x04, 0x02, 0xaa, 0x24, 0x04, 0x02, 0x22, 0x23, 0x9b, 0x04, 0x09, 0x02, + 0x1a, 0x01, 0x10, 0xa9, 0x23, 0x12, 0x03, 0x14, 0x23, 0x24, 0x15, 0x8c, 0x00, 0x0c, 0x03, 0x12, + 0x23, 0xac, 0x19, 0x12, 0x9c, 0x2a, 0x23, 0x24, 0x15, 0x8c, 0x00, 0x0c, 0x03, 0x13, 0x2a, 0x13, + 0xac, 0x10, 0x9c, 0x02, 0x0c, 0x02, 0x1b, 0x12, 0x1c, 0x12, 0x23, 0xac, 0x02, 0x0c, 0x03, 0x12, + 0x23, 0xac, 0x02, 0x22, 0x24, 0x04, 0x8a, 0x02, 0x0d, 0x04, 0x24, 0x22, 0x8a, 0x02, 0x04, 0x2c, + 0x25, 0x22, 0x8a, 0x02, 0x0c, 0x03, 0x12, 0xaa, 0x22, 0x02, 0x03, 0x23, 0x24, 0x8c, 0x11, 0x1c, + 0x02, 0xaa, 0x02, 0x04, 0x14, 0x2b, 0x24, 0xaa, 0x02, 0x03, 0x14, 0x23, 0xaa, 0x02, 0x03, 0x14, + 0x1a, 0x13, 0x24, 0xaa, 0x02, 0x2c, 0x04, 0xaa, 0x02, 0x03, 0x1c, 0x22, 0x23, 0x8d, 0x02, 0x22, + 0x04, 0xac, 0x20, 0x10, 0x14, 0x2c, 0x12, 0x8a, 0x10, 0x19, 0x13, 0x9c, 0x00, 0x10, 0x14, 0x0c, + 0x12, 0xaa, 0x01, 0x10, 0x11, 0xa8, 0x03, 0x04, 0x24, 0x23, 0x12, 0x8b, 0x18, 0x11, 0x9c, 0x21, + 0x10, 0x01, 0x02, 0x13, 0x2a, 0x10, 0x9b, 0x11, 0x00, 0x04, 0x24, 0x2b, 0x02, 0x9a, 0x01, 0x0a, + 0x11, 0x29, 0x22, 0x2b, 0x03, 0x1b, 0x02, 0x11, 0x22, 0x13, 0x8a, 0x00, 0x11, 0x28, 0x11, 0x1c, + 0x02, 0x2a, 0x03, 0xab, 0x10, 0x1a, 0x13, 0x9d, 0x20, 0x00, 0x02, 0x11, 0x2a, 0x02, 0x13, 0x22, + 0x24, 0x8c, 0x08, 0xa8, 0x20, 0x10, 0x11, 0xa9, 0x10, 0x29, 0x20, 0x21, 0x11, 0x98, 0x11, 0x02, + 0x1b, 0x21, 0x12, 0xab, 0x01, 0x21, 0xaa, 0x12, 0xaa, 0x10, 0x20, 0x21, 0x19, 0x12, 0x18, 0x11, + 0xaa, 0x00, 0xa8, 0x01, 0x10, 0x21, 0x12, 0x89, 0x02, 0x2a, 0x11, 0x1b, 0x03, 0xab, 0x01, 0x10, + 0x21, 0x03, 0xab, 0x01, 0x10, 0x21, 0x12, 0x0a, 0x12, 0x23, 0x8b, 0x11, 0xa8, 0x02, 0x0d, 0x04, + 0x14, 0x2b, 0x22, 0xac, 0x14, 0x10, 0x01, 0x1a, 0x10, 0x20, 0xac, 0x9a, 0x14, 0x15, 0x8d, 0x20, + 0xa9, 0x10, 0x20, 0x21, 0x11, 0x98, 0x01, 0x12, 0x0b, 0x11, 0x22, 0x9b, 0x00, 0x09, 0x02, 0x28, + 0x12, 0x13, 0x2b, 0x22, 0xac, 0x00, 0x09, 0x02, 0x28, 0x12, 0x22, 0x13, 0x14, 0xac, 0x00, 0x10, + 0x11, 0x09, 0x11, 0x02, 0x28, 0x12, 0x13, 0x2b, 0x22, 0xac, 0x18, 0x11, 0x12, 0x03, 0x14, 0xab, + 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x10, 0xa9, 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, + 0x01, 0x98, 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x01, 0x10, 0xa9, 0x04, 0x02, 0x11, 0x22, + 0x2c, 0x03, 0x2b, 0x01, 0x10, 0x11, 0xa8, 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x08, 0xa8, + 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x00, 0x20, 0x11, 0x88, 0x00, 0x0c, 0x02, 0x2a, 0x00, + 0x19, 0x10, 0x1c, 0x10, 0x28, 0x14, 0xac, 0x23, 0x14, 0x03, 0x01, 0x10, 0x29, 0x14, 0x15, 0x8d, + 0x02, 0x2a, 0x02, 0x04, 0x2c, 0x03, 0x1b, 0x00, 0x99, 0x02, 0x2a, 0x02, 0x04, 0x2c, 0x03, 0x1b, + 0x11, 0xa8, 0x02, 0x2a, 0x02, 0x04, 0x2c, 0x03, 0x1b, 0x01, 0x10, 0xa9, 0x02, 0x2a, 0x02, 0x04, + 0x2c, 0x03, 0x1b, 0x08, 0xa8, 0x02, 0x2a, 0x12, 0x1c, 0x04, 0x2c, 0x00, 0x99, 0x02, 0x2a, 0x12, + 0x1c, 0x04, 0x2c, 0x11, 0xa8, 0x02, 0x2a, 0x12, 0x1c, 0x04, 0x2c, 0x01, 0x10, 0xa9, 0x02, 0x2a, + 0x12, 0x1c, 0x04, 0x2c, 0x28, 0x88, 0x00, 0x10, 0x21, 0x23, 0x14, 0x04, 0x08, 0x02, 0x9a, 0x04, + 0x02, 0x24, 0x2a, 0x01, 0x10, 0x11, 0xa8, 0x02, 0x22, 0x24, 0x04, 0x0a, 0x00, 0x99, 0x02, 0x22, + 0x24, 0x04, 0x0a, 0x11, 0xa8, 0x02, 0x22, 0x24, 0x04, 0x0a, 0x11, 0x28, 0x00, 0x99, 0x02, 0x22, + 0x24, 0x04, 0x0a, 0x01, 0x10, 0x11, 0xa8, 0x01, 0x21, 0x24, 0x04, 0x09, 0x08, 0xa8, 0x01, 0x2b, + 0x03, 0xa9, 0x01, 0x10, 0x21, 0x23, 0x14, 0x03, 0x09, 0x03, 0xa9, 0x01, 0x04, 0x24, 0x29, 0x11, + 0xa8, 0x01, 0x04, 0x24, 0x29, 0x00, 0x99, 0x02, 0x04, 0x24, 0x2a, 0x01, 0x10, 0xa9, 0x01, 0x04, + 0x24, 0x29, 0x08, 0xa8, 0x01, 0x02, 0x13, 0x1c, 0x13, 0x22, 0x29, 0x11, 0xa8, 0x00, 0x0c, 0x01, + 0x11, 0x22, 0x13, 0x8b, 0x00, 0x0d, 0x00, 0x10, 0x21, 0x1a, 0x02, 0x22, 0x24, 0x8c, 0x02, 0x04, + 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x00, 0x99, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x11, 0xa8, + 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x01, 0x10, 0xa9, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, + 0x0a, 0x01, 0x10, 0x11, 0xa8, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x09, 0xa9, 0x02, 0x04, + 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x01, 0x10, 0x21, 0x89, 0x02, 0x1b, 0x02, 0x04, 0x2c, 0x12, 0x1c, + 0x12, 0x2a, 0x13, 0x2b, 0x22, 0xab, 0x03, 0x04, 0x2c, 0x03, 0x12, 0x2a, 0x14, 0x15, 0x8d, 0x24, + 0x04, 0x02, 0x22, 0x23, 0x1b, 0x00, 0x99, 0x24, 0x04, 0x02, 0x22, 0x23, 0x1b, 0x11, 0xa8, 0x24, + 0x04, 0x02, 0x22, 0x23, 0x1b, 0x01, 0x10, 0xa9, 0x24, 0x04, 0x02, 0x22, 0x23, 0x1b, 0x09, 0xa9, + 0x12, 0x1c, 0x00, 0x99, 0x12, 0x1c, 0x11, 0xa8, 0x12, 0x1c, 0x01, 0x10, 0xa9, 0x12, 0x1c, 0x09, + 0xa9, 0x00, 0x2a, 0x11, 0x28, 0x02, 0x22, 0x24, 0x04, 0x8a, 0x02, 0x0c, 0x03, 0x12, 0x23, 0x2c, + 0x01, 0x10, 0x11, 0xa8, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x00, 0x99, 0x02, 0x04, 0x24, 0x22, 0x0a, + 0x11, 0xa8, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x01, 0x10, 0xa9, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x01, + 0x10, 0x11, 0xa8, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x09, 0xa9, 0x19, 0x02, 0x2a, 0x9b, 0x02, 0x04, + 0x24, 0x22, 0x0a, 0x04, 0xaa, 0x02, 0x04, 0x14, 0x2b, 0x24, 0x2a, 0x00, 0x99, 0x02, 0x04, 0x14, + 0x2b, 0x24, 0x2a, 0x11, 0xa8, 0x02, 0x04, 0x14, 0x2b, 0x24, 0x2a, 0x01, 0x10, 0xa9, 0x02, 0x04, + 0x14, 0x2b, 0x24, 0x2a, 0x09, 0xa9, 0x02, 0x03, 0x1c, 0x22, 0x23, 0x0d, 0x11, 0xa8, 0x00, 0x0c, + 0x02, 0x11, 0x22, 0x13, 0x8a, 0x02, 0x03, 0x1c, 0x22, 0x23, 0x0d, 0x09, 0xa9, }; + + + +//Set this if you are only using CNFG to create an OpenGL context. +#ifndef CNFGCONTEXTONLY +uint32_t CNFGDialogColor; + +void CNFGDrawBox( short x1, short y1, short x2, short y2 ) +{ + uint32_t lc = CNFGLastColor; + CNFGColor( CNFGDialogColor ); + CNFGTackRectangle( x1, y1, x2, y2 ); + CNFGColor( lc ); + CNFGTackSegment( x1, y1, x2, y1 ); + CNFGTackSegment( x2, y1, x2, y2 ); + CNFGTackSegment( x2, y2, x1, y2 ); + CNFGTackSegment( x1, y2, x1, y1 ); +} + +void CNFGDrawText( const char * text, short scale ) +{ + const unsigned char * lmap; + float iox = (float)CNFGPenX; //x offset + float ioy = (float)CNFGPenY; //y offset + + int drawPlace = 0; + unsigned short index; + int bQuit = 0; + while( text[drawPlace] ) + { + unsigned char c = text[drawPlace]; + switch( c ) + { + case 9: // tab + iox += 12 * scale; + break; + case 10: // linefeed + iox = (float)CNFGPenX; + ioy += 6 * scale; + break; + default: + index = RawdrawFontCharMap[c]; + if( index == 65535 ) + { + iox += 3 * scale; + break; + } + + lmap = &RawdrawFontCharData[index]; + short penx = 0, peny = 0; + unsigned char start_seg = 1; + do + { + unsigned char data = (*(lmap++)); + short x1 = (short)(((data >> 4) & 0x07)*scale + iox); + short y1 = (short)((data & 0x07)*scale + ioy); + if( start_seg ) + { + penx = x1; + peny = y1; + start_seg = 0; + if( data & 0x08 ) + CNFGTackPixel( x1, y1 ); + } + else + { + CNFGTackSegment( penx, peny, x1, y1 ); + penx = x1; + peny = y1; + } + if( data & 0x08 ) start_seg = 1; + bQuit = data & 0x80; + } while( !bQuit ); + + iox += 3 * scale; + } + drawPlace++; + } +} + +#ifndef FONT_CREATION_TOOL +#ifdef _CNFG_FANCYFONT + +void CNFGDrawNiceText(const char* text, short scale) +{ + const unsigned char* lmap; + float iox = (float)CNFGPenX; //x offset + float ioy = (float)CNFGPenY; //y offset + + int place = 0; + unsigned short index; + int bQuit = 0; + int segmentEnd = 0; + while (text[place]) { + unsigned char c = text[place]; + switch (c) + { + case 9: // tab + iox += 16 * scale; + break; + case 10: // linefeed + iox = (float)CNFGPenX; + ioy += 6 * scale; + break; + default: + index = CharIndex[c]; + if (index == 0) { + iox += 4 * scale; + break; + } + + lmap = &FontData[index]; + + short charWidth = ((*lmap) & 0xE0) >> 5; //0b11100000 + short xbase = ((*lmap) & 0x18) >> 3; //0b00011000 + short ybase = (*lmap) & 0x07; //0b00000111 + lmap++; + do { + + int x1 = ((((*lmap) & 0x38) >> 3) * scale + iox + xbase * scale); //0b00111000 + int y1 = (((*lmap) & 0x07) * scale + ioy + ybase * scale); + segmentEnd = *lmap & 0x40; + int x2 = 0; + int y2 = 0; + lmap++; + if (segmentEnd) { + x2 = x1; + y2 = y1; + } + else { + + x2 = ((((*lmap) & 0x38) >> 3) * scale + iox + xbase * scale); + y2 = (((*lmap) & 0x07) * scale + ioy + ybase * scale); + + } + + + CNFGTackSegment(x1, y1, x2, y2); + bQuit = *(lmap - 1) & 0x80; + + } while (!bQuit); + iox += (charWidth + 2) * scale; + //iox += 8 * scale; + } + place++; + } +} +#endif +#endif + +void CNFGGetTextExtents( const char * text, int * w, int * h, int textsize ) +{ + int charsx = 0; + int charsy = 1; + int charsline = 0; + const char * s; + + for( s = text; *s; s++ ) + { + if( *s == '\n' ) + { + charsline = 0; + if( *(s+1) ) + charsy++; + } + else + { + charsline++; + if( charsline > charsx ) + charsx = charsline; + } + } + + *w = charsx * textsize * 3-1*textsize; + *h = charsy * textsize * 6; +} + +#if defined( CNFG_BATCH ) + +//This is the path by which we convert rawdraw functionality +//into nice batched triangle streams. + +//Just FYI we use floats for geometry instead of shorts becase it is harder +//to triangularize a diagonal line int triangles with shorts and have it look good. +void CNFGEmitBackendTriangles( const float * fv, const uint32_t * col, int nr_verts ); + +//If on WASM, sqrtf is implied. On other platforms, need sqrtf from math.h +#ifdef __wasm__ +float sqrtf( float f ); +#define cnfg_sqrtf sqrtf +#elif defined( __TINYC__ ) && defined( WIN32 ) +#define cnfg_sqrtf sqrt +#else +#define cnfg_sqrtf sqrtf +#include +#endif + +//Geometry batching system - so we can batch geometry and deliver it all at once. +float CNFGVertDataV[CNFG_BATCH*3]; +uint32_t CNFGVertDataC[CNFG_BATCH]; +int CNFGVertPlace; +static float wgl_last_width_over_2 = .5f; + +static void EmitQuad( float cx0, float cy0, float cx1, float cy1, float cx2, float cy2, float cx3, float cy3 ) +{ + //Because quads are really useful, but it's best to keep them all triangles if possible. + //This lets us draw arbitrary quads. + if( CNFGVertPlace >= CNFG_BATCH-6 ) CNFGFlushRender(); + float * fv = &CNFGVertDataV[CNFGVertPlace*3]; + fv[0] = cx0; fv[1] = cy0; + fv[3] = cx1; fv[4] = cy1; + fv[6] = cx2; fv[7] = cy2; + fv[9] = cx2; fv[10] = cy2; + fv[12] = cx1; fv[13] = cy1; + fv[15] = cx3; fv[16] = cy3; + uint32_t * col = &CNFGVertDataC[CNFGVertPlace]; + uint32_t color = CNFGLastColor; + col[0] = color; col[1] = color; col[2] = color; col[3] = color; col[4] = color; col[5] = color; + CNFGVertPlace += 6; +} + + +#if !defined( CNFGRASTERIZER ) && !defined( CNFGHTTP ) + +void CNFGTackPixel( short x1, short y1 ) +{ + x1++; y1++; + const float l2 = wgl_last_width_over_2; + const float l2u = wgl_last_width_over_2+0.5f; + EmitQuad( x1-l2u, y1-l2u, x1+l2, y1-l2u, x1-l2u, y1+l2, x1+l2, y1+l2 ); +} + + +void CNFGTackSegment( short x1, short y1, short x2, short y2 ) +{ + float ix1 = x1; + float iy1 = y1; + float ix2 = x2; + float iy2 = y2; + + float dx = ix2-ix1; + float dy = iy2-iy1; + float imag = 1.f/(float)(cnfg_sqrtf(dx*dx+dy*dy)); + dx *= imag; + dy *= imag; + float orthox = dy*wgl_last_width_over_2; + float orthoy =-dx*wgl_last_width_over_2; + + ix2 += dx/2 + 0.5f; + iy2 += dy/2 + 0.5f; + ix1 -= dx/2 - 0.5f; + iy1 -= dy/2 - 0.5f; + + //This logic is incorrect. XXX FIXME. + EmitQuad( (ix1 - orthox), (iy1 - orthoy), (ix1 + orthox), (iy1 + orthoy), (ix2 - orthox), (iy2 - orthoy), ( ix2 + orthox), ( iy2 + orthoy) ); +} + +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ) +{ + float ix1 = x1; + float iy1 = y1; + float ix2 = x2; + float iy2 = y2; + EmitQuad( ix1,iy1,ix2,iy1,ix1,iy2,ix2,iy2 ); +} + +void CNFGTackPoly( RDPoint * points, int verts ) +{ + int i; + int tris = verts-2; + if( CNFGVertPlace >= CNFG_BATCH-tris*3 ) CNFGFlushRender(); + + uint32_t color = CNFGLastColor; + short * ptrsrc = (short*)points; + + for( i = 0; i < tris; i++ ) + { + float * fv = &CNFGVertDataV[CNFGVertPlace*3]; + fv[0] = ptrsrc[0]; + fv[1] = ptrsrc[1]; + fv[3] = ptrsrc[i*2+2]; + fv[4] = ptrsrc[i*2+3]; + fv[6] = ptrsrc[i*2+4]; + fv[7] = ptrsrc[i*2+5]; + + uint32_t * col = &CNFGVertDataC[CNFGVertPlace]; + col[0] = color; + col[1] = color; + col[2] = color; + + CNFGVertPlace += 3; + } +} + +uint32_t CNFGColor( uint32_t RGB ) +{ + return CNFGLastColor = RGB; +} + +void CNFGSetLineWidth( short width ) +{ + wgl_last_width_over_2 = width/2.0f;// + 0.5; +} + +#endif + + +#if !defined( __wasm__ ) && !defined( CNFGHTTP ) +//In WASM, Javascript takes over this functionality. + +//Shader compilation errors go to stderr. +#include + +#ifndef GL_VERTEX_SHADER +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_LINK_STATUS 0x8B82 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_CLAMP_TO_EDGE 0x812F +#define LGLchar char +#else +#define LGLchar GLchar +#endif + +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) +#define CNFGOGL_NEED_EXTENSION +#include +#endif + +#ifdef CNFGOGL_NEED_EXTENSION +// If we are going to be defining our own function pointer call + #if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) + // Make sure to use __stdcall on Windows + #define CHEWTYPEDEF( ret, name, rv, paramcall, ... ) \ + typedef ret (__stdcall *CNFGTYPE##name)( __VA_ARGS__ ); \ + ret (__stdcall *CNFG##name)( __VA_ARGS__ ); + #else + #define CHEWTYPEDEF( ret, name, rv, paramcall, ... ) \ + typedef ret (*CNFGTYPE##name)( __VA_ARGS__ ); \ + ret (*CNFG##name)( __VA_ARGS__ ); + #endif +#else +//If we are going to be defining the real call +#define CHEWTYPEDEF( ret, name, rv, paramcall, ... ) \ + ret name (__VA_ARGS__); +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +int (*MyFunc)( int program, const LGLchar *name ); + +CHEWTYPEDEF( GLint, glGetUniformLocation, return, (program,name), GLuint program, const LGLchar *name ) +CHEWTYPEDEF( void, glEnableVertexAttribArray, , (index), GLuint index ) +CHEWTYPEDEF( void, glUseProgram, , (program), GLuint program ) +CHEWTYPEDEF( void, glGetProgramInfoLog, , (program,maxLength, length, infoLog), GLuint program, GLsizei maxLength, GLsizei *length, LGLchar *infoLog ) +CHEWTYPEDEF( void, glGetProgramiv, , (program,pname,params), GLuint program, GLenum pname, GLint *params ) +CHEWTYPEDEF( void, glBindAttribLocation, , (program,index,name), GLuint program, GLuint index, const LGLchar *name ) +CHEWTYPEDEF( void, glGetShaderiv, , (shader,pname,params), GLuint shader, GLenum pname, GLint *params ) +CHEWTYPEDEF( GLuint, glCreateShader, return, (e), GLenum e ) +CHEWTYPEDEF( void, glVertexAttribPointer, , (index,size,type,normalized,stride,pointer), GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer ) +CHEWTYPEDEF( void, glShaderSource, , (shader,count,string,length), GLuint shader, GLsizei count, const LGLchar *const*string, const GLint *length ) +CHEWTYPEDEF( void, glAttachShader, , (program,shader), GLuint program, GLuint shader ) +CHEWTYPEDEF( void, glCompileShader, ,(shader), GLuint shader ) +CHEWTYPEDEF( void, glGetShaderInfoLog , , (shader,maxLength, length, infoLog), GLuint shader, GLsizei maxLength, GLsizei *length, LGLchar *infoLog ) +CHEWTYPEDEF( GLuint, glCreateProgram, return, () , void ) +CHEWTYPEDEF( void, glLinkProgram, , (program), GLuint program ) +CHEWTYPEDEF( void, glDeleteShader, , (shader), GLuint shader ) +CHEWTYPEDEF( void, glUniform4f, , (location,v0,v1,v2,v3), GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3 ) +CHEWTYPEDEF( void, glUniform1i, , (location,i0), GLint location, GLint i0 ) +CHEWTYPEDEF( void, glActiveTexture, , (texture), GLenum texture ) + +#ifndef CNFGOGL_NEED_EXTENSION +#define CNFGglGetUniformLocation glGetUniformLocation +#define CNFGglEnableVertexAttribArray glEnableVertexAttribArray +#define CNFGglUseProgram glUseProgram +#define CNFGglEnableVertexAttribArray glEnableVertexAttribArray +#define CNFGglUseProgram glUseProgram +#define CNFGglGetProgramInfoLog glGetProgramInfoLog +#define CNFGglGetProgramiv glGetProgramiv +#define CNFGglShaderSource glShaderSource +#define CNFGglCreateShader glCreateShader +#define CNFGglAttachShader glAttachShader +#define CNFGglGetShaderiv glGetShaderiv +#define CNFGglCompileShader glCompileShader +#define CNFGglGetShaderInfoLog glGetShaderInfoLog +#define CNFGglCreateProgram glCreateProgram +#define CNFGglLinkProgram glLinkProgram +#define CNFGglDeleteShader glDeleteShader +#define CNFGglUniform4f glUniform4f +#define CNFGglBindAttribLocation glBindAttribLocation +#define CNFGglVertexAttribPointer glVertexAttribPointer +#define CNFGglUniform1i glUniform1i +#define CNFGglActiveTexture glActiveTexture + +#endif + +#ifdef __cplusplus +}; +#endif + +#ifdef CNFGOGL_NEED_EXTENSION +#if defined( WIN32 ) || defined( WINDOWS ) || defined( WIN64 ) + +//From https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions +void * CNFGGetProcAddress(const char *name) +{ + void *p = (void *)wglGetProcAddress(name); + if(p == 0 || + (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) || + (p == (void*)-1) ) + { + static HMODULE module; + if( !module ) module = LoadLibraryA("opengl32.dll"); + p = (void *)GetProcAddress(module, name); + } + // We were unable to load the required openGL function + if (!p) { + fprintf(stderr,"[rawdraw][warn]: Unable to load openGL extension \"%s\"\n", name); + } + return p; +} + +#else +#include + + +void * CNFGGetProcAddress(const char *name) +{ + //Tricky use RTLD_NEXT first so we don't accidentally link against ourselves. + void * v1 = dlsym( (void*)((intptr_t)-1) /*RTLD_NEXT = -1*/ /*RTLD_DEFAULT = 0*/, name ); + //printf( "%s = %p\n", name, v1 ); + if( !v1 ) v1 = dlsym( 0, name ); + return v1; +} + +#endif + +// Try and load openGL extension functions required for rawdraw +static void CNFGLoadExtensionsInternal() +{ + CNFGglGetUniformLocation = (CNFGTYPEglGetUniformLocation) CNFGGetProcAddress( "glGetUniformLocation" ); + CNFGglEnableVertexAttribArray = (CNFGTYPEglEnableVertexAttribArray)CNFGGetProcAddress( "glEnableVertexAttribArray" ); + CNFGglUseProgram = (CNFGTYPEglUseProgram)CNFGGetProcAddress( "glUseProgram" ); + CNFGglGetProgramInfoLog = (CNFGTYPEglGetProgramInfoLog)CNFGGetProcAddress( "glGetProgramInfoLog" ); + CNFGglBindAttribLocation = (CNFGTYPEglBindAttribLocation)CNFGGetProcAddress( "glBindAttribLocation" ); + CNFGglGetProgramiv = (CNFGTYPEglGetProgramiv)CNFGGetProcAddress( "glGetProgramiv" ); + CNFGglGetShaderiv = (CNFGTYPEglGetShaderiv)CNFGGetProcAddress( "glGetShaderiv" ); + CNFGglVertexAttribPointer = (CNFGTYPEglVertexAttribPointer)CNFGGetProcAddress( "glVertexAttribPointer" ); + CNFGglCreateShader = (CNFGTYPEglCreateShader)CNFGGetProcAddress( "glCreateShader" ); + CNFGglShaderSource = (CNFGTYPEglShaderSource)CNFGGetProcAddress( "glShaderSource" ); + CNFGglAttachShader = (CNFGTYPEglAttachShader)CNFGGetProcAddress( "glAttachShader" ); + CNFGglCompileShader = (CNFGTYPEglCompileShader)CNFGGetProcAddress( "glCompileShader" ); + CNFGglGetShaderInfoLog = (CNFGTYPEglGetShaderInfoLog)CNFGGetProcAddress( "glGetShaderInfoLog" ); + CNFGglDeleteShader = (CNFGTYPEglDeleteShader)CNFGGetProcAddress( "glDeleteShader" ); + CNFGglLinkProgram = (CNFGTYPEglLinkProgram)CNFGGetProcAddress( "glLinkProgram" ); + CNFGglCreateProgram = (CNFGTYPEglCreateProgram)CNFGGetProcAddress( "glCreateProgram" ); + CNFGglUniform4f = (CNFGTYPEglUniform4f)CNFGGetProcAddress( "glUniform4f" ); + CNFGglUniform1i = (CNFGTYPEglUniform1i)CNFGGetProcAddress( "glUniform1i" ); + CNFGglActiveTexture = (CNFGTYPEglActiveTexture)CNFGGetProcAddress("glActiveTexture"); + + // Check if any of these functions didn't get loaded + uint8_t not_all_functions_loaded = + !CNFGglGetUniformLocation || !CNFGglEnableVertexAttribArray || !CNFGglUseProgram || + !CNFGglGetProgramInfoLog || !CNFGglBindAttribLocation || !CNFGglGetProgramiv || + !CNFGglVertexAttribPointer || !CNFGglCreateShader || !CNFGglShaderSource || + !CNFGglAttachShader || !CNFGglCompileShader || !CNFGglGetShaderInfoLog || + !CNFGglDeleteShader || !CNFGglLinkProgram || !CNFGglCreateProgram || + !CNFGglUniform4f || !CNFGglUniform1i || !CNFGglActiveTexture; + if (not_all_functions_loaded) { + fprintf( + stderr, + "[rawdraw][err]: Unable to load all openGL extensions required for rawdraw\n" + "\tPlease update your graphics drivers or unexpected crashes may occur.\n" + ); + } + + // Give a very stern warning if unable to create or compile shaders + if (!CNFGglCreateShader || !CNFGglCompileShader) { + fprintf( + stderr, + "[rawdraw][err]: Unable to create or compile shaders, this will cause a fatal error if " + "openGL is used.\n" + "\tUpdate your video graphics drivers or switch to software graphics.\n" + ); + } +} +#else +static void CNFGLoadExtensionsInternal() { } +#endif + + + +GLuint gRDShaderProg = -1; +GLuint gRDBlitProg = -1; +GLuint gRDShaderProgUX = -1; +GLuint gRDBlitProgUX = -1; +GLuint gRDBlitProgUT = -1; +GLuint gRDBlitProgTex = -1; +GLuint gRDLastResizeW; +GLuint gRDLastResizeH; + + +GLuint CNFGGLInternalLoadShader( const char * vertex_shader, const char * fragment_shader ) +{ + GLuint fragment_shader_object = 0; + GLuint vertex_shader_object = 0; + GLuint program = 0; + int ret; + + vertex_shader_object = CNFGglCreateShader(GL_VERTEX_SHADER); + if (!vertex_shader_object) { + fprintf( stderr, "Error: glCreateShader(GL_VERTEX_SHADER) " + "failed: 0x%08X\n", glGetError()); + goto fail; + } + + CNFGglShaderSource(vertex_shader_object, 1, &vertex_shader, NULL); + CNFGglCompileShader(vertex_shader_object); + + CNFGglGetShaderiv(vertex_shader_object, GL_COMPILE_STATUS, &ret); + if (!ret) { + fprintf( stderr,"Error: vertex shader compilation failed!\n"); + CNFGglGetShaderiv(vertex_shader_object, GL_INFO_LOG_LENGTH, &ret); + + if (ret > 1) { + //TODO: Refactor to remove malloc reliance. + #ifndef __clang__ + char * log = (char*)alloca(ret); + CNFGglGetShaderInfoLog(vertex_shader_object, ret, NULL, log); + fprintf( stderr, "%s", log); + #endif + } + goto fail; + } + + fragment_shader_object = CNFGglCreateShader(GL_FRAGMENT_SHADER); + if (!fragment_shader_object) { + fprintf( stderr, "Error: glCreateShader(GL_FRAGMENT_SHADER) " + "failed: 0x%08X\n", glGetError()); + goto fail; + } + + CNFGglShaderSource(fragment_shader_object, 1, &fragment_shader, NULL); + CNFGglCompileShader(fragment_shader_object); + + CNFGglGetShaderiv(fragment_shader_object, GL_COMPILE_STATUS, &ret); + if (!ret) { + fprintf( stderr, "Error: fragment shader compilation failed!\n"); + CNFGglGetShaderiv(fragment_shader_object, GL_INFO_LOG_LENGTH, &ret); + + if (ret > 1) { + //TODO: Refactor to remove malloc reliance. + #ifndef __clang__ + char * log = (char*)malloc(ret); + CNFGglGetShaderInfoLog(fragment_shader_object, ret, NULL, log); + fprintf( stderr, "%s", log); + free( log ); + #endif + } + goto fail; + } + + program = CNFGglCreateProgram(); + if (!program) { + fprintf( stderr, "Error: failed to create program!\n"); + goto fail; + } + + CNFGglAttachShader(program, vertex_shader_object); + CNFGglAttachShader(program, fragment_shader_object); + + CNFGglBindAttribLocation(program, 0, "a0"); + CNFGglBindAttribLocation(program, 1, "a1"); + + CNFGglLinkProgram(program); + + CNFGglGetProgramiv(program, GL_LINK_STATUS, &ret); + if (!ret) { + fprintf( stderr, "Error: program linking failed!\n"); + CNFGglGetProgramiv(program, GL_INFO_LOG_LENGTH, &ret); + + if (ret > 1) { + //TODO: Refactor to remove malloc reliance. + #ifndef __clang__ + char *log = (char*)alloca(ret); + CNFGglGetProgramInfoLog(program, ret, NULL, log); + fprintf( stderr, "%s", log); + #endif + } + goto fail; + } + return program; +fail: + if( !vertex_shader_object ) CNFGglDeleteShader( vertex_shader_object ); + if( !fragment_shader_object ) CNFGglDeleteShader( fragment_shader_object ); + if( !program ) CNFGglDeleteShader( program ); + return -1; +} + +#if defined( CNFGEWGL ) && !defined( CNFG_NO_PRECISION ) +#define PRECISIONA "lowp" +#define PRECISIONB "mediump" +#else +#define PRECISIONA +#define PRECISIONB +#endif + +void CNFGSetupBatchInternal() +{ + short w, h; + + CNFGLoadExtensionsInternal(); + + CNFGGetDimensions( &w, &h ); + + gRDShaderProg = CNFGGLInternalLoadShader( + "uniform vec4 xfrm;" + "attribute vec3 a0;" + "attribute vec4 a1;" + "varying " PRECISIONA " vec4 vc;" + "void main() { gl_Position = vec4( a0.xy*xfrm.xy+xfrm.zw, a0.z, 0.5 ); vc = a1; }", + + "varying " PRECISIONA " vec4 vc;" + "void main() { gl_FragColor = vec4(vc.abgr); }" + ); + + CNFGglUseProgram( gRDShaderProg ); + gRDShaderProgUX = CNFGglGetUniformLocation ( gRDShaderProg , "xfrm" ); + + + gRDBlitProg = CNFGGLInternalLoadShader( + "uniform vec4 xfrm;" + "attribute vec3 a0;" + "attribute vec4 a1;" + "varying " PRECISIONB " vec2 tc;" + "void main() { gl_Position = vec4( a0.xy*xfrm.xy+xfrm.zw, a0.z, 0.5 ); tc = a1.xy; }", + + "varying " PRECISIONB " vec2 tc;" + "uniform sampler2D tex;" + "void main() { gl_FragColor = texture2D(tex,tc)." + +#if !defined( CNFGRASTERIZER ) +"wzyx" +#else +"wxyz" +#endif +";}" ); + + CNFGglUseProgram( gRDBlitProg ); + gRDBlitProgUX = CNFGglGetUniformLocation ( gRDBlitProg , "xfrm" ); + gRDBlitProgUT = CNFGglGetUniformLocation ( gRDBlitProg , "tex" ); + glGenTextures( 1, &gRDBlitProgTex ); + + CNFGglEnableVertexAttribArray(0); + CNFGglEnableVertexAttribArray(1); + + glDisable(GL_DEPTH_TEST); + glDepthMask( GL_FALSE ); + glEnable( GL_BLEND ); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + CNFGVertPlace = 0; +} + +#ifndef CNFGRASTERIZER +void CNFGInternalResize(short x, short y) +#else +void CNFGInternalResizeOGLBACKEND(short x, short y) +#endif +{ + glViewport( 0, 0, x, y ); + gRDLastResizeW = x; + gRDLastResizeH = y; + if (gRDShaderProg == 0xFFFFFFFF) { return; } // Prevent trying to set uniform if the shader isn't ready yet. + CNFGglUseProgram( gRDShaderProg ); + CNFGglUniform4f( gRDShaderProgUX, 1.f/x, -1.f/y, -0.5f, 0.5f); +} + +void CNFGEmitBackendTriangles( const float * vertices, const uint32_t * colors, int num_vertices ) +{ + CNFGglUseProgram( gRDShaderProg ); + CNFGglUniform4f( gRDShaderProgUX, 1.f/gRDLastResizeW, -1.f/gRDLastResizeH, -0.5f, 0.5f); + CNFGglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices); + CNFGglVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors); + glDrawArrays( GL_TRIANGLES, 0, num_vertices); +} + + +#ifdef CNFGOGL +// this is here, so people don't have to include opengl +void CNFGDeleteTex( unsigned int tex ) +{ + glDeleteTextures(1, &tex); +} + +unsigned int CNFGTexImage( uint32_t *data, int w, int h ) +{ + GLuint tex; + + glGenTextures(1, &tex); + glEnable( GL_TEXTURE_2D ); + CNFGglActiveTexture( 0 ); + glBindTexture( GL_TEXTURE_2D, tex ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, + GL_UNSIGNED_BYTE, data ); + + return (unsigned int)tex; +} + +void CNFGBlitTex( unsigned int tex, int x, int y, int w, int h ) +{ + if( w == 0 || h == 0 ) return; + + CNFGFlushRender(); + + CNFGglUseProgram( gRDBlitProg ); + CNFGglUniform4f( gRDBlitProgUX, + 1.f/gRDLastResizeW, -1.f/gRDLastResizeH, + -0.5f+x/(float)gRDLastResizeW, 0.5f-y/(float)gRDLastResizeH ); + CNFGglUniform1i( gRDBlitProgUT, 0 ); + + glBindTexture(GL_TEXTURE_2D, tex); + + const float verts[] = { + 0,0, (float)w,0, (float)w,(float)h, + 0,0, (float)w,(float)h, 0,(float)h, }; + static const uint8_t tex_verts[] = { + 0,0, 255,0, 255,255, + 0,0, 255,255, 0,255 }; + + CNFGglVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts); + CNFGglVertexAttribPointer(1, 2, GL_UNSIGNED_BYTE, GL_TRUE, 0, tex_verts); + + glDrawArrays( GL_TRIANGLES, 0, 6); +} +#endif + +#ifdef CNFGRASTERIZER +void CNFGBlitImageInternal( uint32_t * data, int x, int y, int w, int h ) +#else +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ) +#endif +{ + glEnable( GL_TEXTURE_2D ); + CNFGglActiveTexture( 0 ); + glBindTexture( GL_TEXTURE_2D, gRDBlitProgTex ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, + GL_UNSIGNED_BYTE, data ); + + CNFGBlitTex( gRDBlitProgTex, x, y, w, h ); +} + +void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h ) +{ +#ifdef CNFGRASTERIZER + CNFGBlitImageInternal( data, 0, 0, w, h ); + void CNFGSwapBuffersInternal(); + CNFGSwapBuffersInternal(); +#else + CNFGBlitImage( data, 0, 0, w, h ); +#endif +} + +#ifndef CNFGRASTERIZER + +void CNFGFlushRender() +{ + if( !CNFGVertPlace ) return; + CNFGEmitBackendTriangles( CNFGVertDataV, CNFGVertDataC, CNFGVertPlace ); + CNFGVertPlace = 0; +} + +void CNFGClearFrame() +{ + glClearColor( ((CNFGBGColor&0xff000000)>>24)/255.0f, + ((CNFGBGColor&0xff0000)>>16)/255.0f, + (CNFGBGColor&0xff00)/65280.0f, + (CNFGBGColor&0xff)/255.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); +} + +#endif + +#endif //__wasm__ + +#else + +void CNFGFlushRender() { } + +#endif + + +#endif +#endif // CNFGHTTPSERVERONLY +#endif //_CNFG_C diff --git a/app/src/nativecode/rawdraw/CNFGHTTP.c b/app/src/nativecode/rawdraw/CNFGHTTP.c new file mode 100644 index 0000000..6ff71fc --- /dev/null +++ b/app/src/nativecode/rawdraw/CNFGHTTP.c @@ -0,0 +1,2017 @@ +//Copyright 2015-2021 <>< Charles Lohr Under the MIT/x11 License, NewBSD License or +// ColorChord License. You Choose. This file mostly based on `cnhttp` from cntools. + +#ifdef CNFGHTTP + +//Pull from a buffer +#ifndef CNFGHTTP_LIVE_FS +#define USE_RAM_MFS +#endif + +//single_file_http.c base from https://github.com/cntools/httptest. +//scroll to bottom for implementation. + +#ifndef CUSTOM_HTTPHEADER_CODE +#define CUSTOM_HTTPHEADER_CODE PushString("Access-Control-Allow-Origin: *\r\n"); +#endif + +/* public api for steve reid's public domain SHA-1 implementation */ +/* this file is in the public domain */ + + +#include + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + uint8_t buffer[64]; +} RD_SHA1_CTX; + +#define RD_SHA1_DIGEST_SIZE 20 + +void static RD_SHA1_Init(RD_SHA1_CTX* context); +void static RD_SHA1_Update(RD_SHA1_CTX* context, const uint8_t* data, const unsigned long len); +void static RD_SHA1_Final(uint8_t digest[RD_SHA1_DIGEST_SIZE],RD_SHA1_CTX* context); //WARNINGThe parameters are flipped here. (CNL) + +//Not to be confused with MFS for the AVR. + +#ifndef _MFS_H +#define _MFS_H + + +#ifndef USE_RAM_MFS +#include +#endif + +#define MFS_SECTOR 256 +#define MFS_FILENAMELEN 32-8 +#define MFS_FILE_COMPRESSED_MEMORY (-2) + +//Format: +// [FILE NAME (24)] [Start (4)] [Len (4)] +// NOTE: Filename must be null-terminated within the 24. +struct MFSFileEntry +{ + char name[MFS_FILENAMELEN]; + uint32_t start; //From beginning of mfs thing. + uint32_t len; +}; + + +struct MFSFileInfo +{ + uint32_t filelen; +#ifdef USE_RAM_MFS + uint32_t offset; +#else + FILE * file; +#endif +}; + + + +//Returns 0 on succses. +//Returns size of file if non-empty +//If positive, populates mfi. +//Returns -1 if can't find file or reached end of file list. +int8_t MFSOpenFile( const char * fname, struct MFSFileInfo * mfi ); +int32_t MFSReadSector( uint8_t* data, struct MFSFileInfo * mfi ); //returns # of bytes left in file. +void MFSClose( struct MFSFileInfo * mfi ); + + + +#endif + + +#ifndef _CNHTTP_H +#define _CNHTTP_H + +#include + +extern struct HTTPConnection * curhttp; +extern uint8_t * curdata; +extern uint16_t curlen; +extern uint8_t wsmask[4]; +extern uint8_t wsmaskplace; + + + +uint8_t WSPOPMASK(); +#define HTTPPOP (*curdata++) + +//You must provide this. +void HTTPCustomStart( ); +void HTTPCustomCallback( ); //called when we can send more data +void WebSocketData( int len ); +void WebSocketTick( ); +void WebSocketNew(); +void HTTPHandleInternalCallback( ); +uint8_t hex2byte( const char * c ); +void NewWebSocket(); +void et_espconn_disconnect( int socket ); + +//Internal Functions +void HTTPTick( uint8_t timedtick ); +int URLDecode( char * decodeinto, int maxlen, const char * buf ); +void WebSocketGotData( uint8_t c ); +void WebSocketTickInternal(); +void WebSocketSend( uint8_t * data, int size ); + +//Host-level functions +void my_base64_encode(const unsigned char *data, unsigned int input_length, uint8_t * encoded_data ); +void Uint32To10Str( char * out, uint32_t dat ); +void http_recvcb(int conn, char *pusrdata, unsigned short length); +void http_disconnetcb(int conn); +int httpserver_connectcb( int socket ); // return which HTTP it is. -1 for failure +void DataStartPacket(); +extern uint8_t * databuff_ptr; +void PushString( const char * data ); +void PushByte( uint8_t c ); +void PushBlob( const uint8_t * datam, int len ); +int TCPCanSend( int socket, int size ); +int TCPDoneSend( int socket ); +int EndTCPWrite( int socket ); + +#define HTTP_CONNECTIONS 50 +#ifndef MAX_HTTP_PATHLEN +#define MAX_HTTP_PATHLEN 80 +#endif +#define HTTP_SERVER_TIMEOUT 500 + + +#define HTTP_STATE_NONE 0 +#define HTTP_STATE_WAIT_METHOD 1 +#define HTTP_STATE_WAIT_PATH 2 +#define HTTP_STATE_WAIT_PROTO 3 + +#define HTTP_STATE_WAIT_FLAG 4 +#define HTTP_STATE_WAIT_INFLAG 5 +#define HTTP_STATE_DATA_XFER 7 +#define HTTP_STATE_DATA_WEBSOCKET 8 + +#define HTTP_WAIT_CLOSE 15 + +struct HTTPConnection +{ + uint8_t state:4; + uint8_t state_deets; + + //Provides path, i.e. "/index.html" but, for websockets, the last + //32 bytes of the buffer are used for the websockets key. + uint8_t pathbuffer[MAX_HTTP_PATHLEN]; + uint8_t is_dynamic:1; + uint16_t timeout; + + union data_t + { + struct MFSFileInfo filedescriptor; + struct UserData { uint16_t a, b, c; } user; + struct UserDataPtr { void * v; } userptr; + } data; + + void * rcb; + void * rcbDat; //For websockets primarily. + void * ccb; //Close callback (used for websockets, primarily) + + uint32_t bytesleft; + uint32_t bytessofar; + + uint8_t is404:1; + uint8_t isdone:1; + uint8_t isfirst:1; + uint8_t keep_alive:1; + uint8_t need_resend:1; + uint8_t send_pending:1; //If we can send data, we should? + uint8_t is_gzip:1; + + int socket; + uint8_t corked_data[4096]; + int corked_data_place; +}; + +extern struct HTTPConnection HTTPConnections[HTTP_CONNECTIONS]; + +#endif + +#ifndef _HTTP_BSD_H +#define _HTTP_BSD_H + + +//Call this to start your webserver. +int RunHTTP( int port ); +int TickHTTP(); //returns -1 if problem. + +//For running on a BSD Sockets System +int htsend( int socket, uint8_t * data, int datact ); +void et_espconn_disconnect( int socket ); +void http_recvcb(int whichhttp, char *pusrdata, unsigned short length); +void http_disconnetcb(int whichhttp); +int httpserver_connectcb( int socket ); // return which HTTP it is. -1 for failure +void DataStartPacket(); +extern uint8_t * databuff_ptr; +void PushBlob( const uint8_t * data, int len ); +void PushByte( uint8_t c ); +void PushString( const char * data ); +int TCPCanSend( int socket, int size ); +int TCPDoneSend( int socket ); +int EndTCPWrite( int socket ); +void TermHTTPServer(); + +extern int cork_binary_rx; + +#endif + +//Copyright 2012-2016 <>< Charles Lohr Under the MIT/x11 License, NewBSD License or ColorChord License. You Choose. + +#include +#include + +struct HTTPConnection HTTPConnections[HTTP_CONNECTIONS]; + +#define HTDEBUG( x, ... ) printf( x, ##__VA_ARGS__ ) +//#define HTDEBUG( x... ) + +//#define ISKEEPALIVE "keep-alive" +#define ISKEEPALIVE "close" + +struct HTTPConnection HTTPConnections[HTTP_CONNECTIONS]; +struct HTTPConnection * curhttp; +uint8_t * curdata; +uint16_t curlen; +uint8_t wsmask[4]; +uint8_t wsmaskplace; + + +void CloseEvent(); +void InternalStartHTTP( ); +void HTTPHandleInternalCallback( ); + +void HTTPClose( ) +{ + //This is dead code, but it is a testament to Charles. + //Do not do this here. Wait for the ESP to tell us the + //socket is successfully closed. + //curhttp->state = HTTP_STATE_NONE; + curhttp->state = HTTP_WAIT_CLOSE; + et_espconn_disconnect( curhttp->socket ); + CloseEvent(); +} + + +void HTTPGotData( ) +{ + uint8_t c; + curhttp->timeout = 0; + while( curlen-- ) + { + c = HTTPPOP; + // sendhex2( h->state ); sendchr( ' ' ); + + switch( curhttp->state ) + { + case HTTP_STATE_WAIT_METHOD: + if( c == ' ' ) + { + curhttp->state = HTTP_STATE_WAIT_PATH; + curhttp->state_deets = 0; + } + break; + case HTTP_STATE_WAIT_PATH: + curhttp->pathbuffer[curhttp->state_deets++] = c; + if( curhttp->state_deets == MAX_HTTP_PATHLEN ) + { + curhttp->state_deets--; + } + + if( c == ' ' ) + { + //Tricky: If we're a websocket, we need the whole header. + curhttp->pathbuffer[curhttp->state_deets-1] = 0; + curhttp->state_deets = 0; + + if( strncmp( (const char*)curhttp->pathbuffer, "/d/ws", 5 ) == 0 ) + { + curhttp->state = HTTP_STATE_DATA_WEBSOCKET; + curhttp->state_deets = 0; + } + else + { + curhttp->state = HTTP_STATE_WAIT_PROTO; + } + } + break; + case HTTP_STATE_WAIT_PROTO: + if( c == '\n' ) + { + curhttp->state = HTTP_STATE_WAIT_FLAG; + } + break; + case HTTP_STATE_WAIT_FLAG: + if( c == '\n' ) + { + curhttp->state = HTTP_STATE_DATA_XFER; + InternalStartHTTP( ); + } + else if( c != '\r' ) + { + curhttp->state = HTTP_STATE_WAIT_INFLAG; + } + break; + case HTTP_STATE_WAIT_INFLAG: + if( c == '\n' ) + { + curhttp->state = HTTP_STATE_WAIT_FLAG; + curhttp->state_deets = 0; + } + break; + case HTTP_STATE_DATA_XFER: + //Ignore any further data? + curlen = 0; + break; + case HTTP_STATE_DATA_WEBSOCKET: + WebSocketGotData( c ); + break; + case HTTP_WAIT_CLOSE: + if( curhttp->keep_alive ) + { + curhttp->state = HTTP_STATE_WAIT_METHOD; + } + else + { + HTTPClose( ); + } + break; + default: + break; + }; + } + +} + + +static void DoHTTP( uint8_t timed ) +{ + switch( curhttp->state ) + { + case HTTP_STATE_NONE: //do nothing if no state. + curhttp->send_pending = 0; + break; + case HTTP_STATE_DATA_XFER: + curhttp->send_pending = 1; + if( TCPCanSend( curhttp->socket, 1300 ) ) //TCPDoneSend + { + if( curhttp->is_dynamic ) + { + HTTPCustomCallback( ); + } + else + { + HTTPHandleInternalCallback( ); + } + } + break; + case HTTP_WAIT_CLOSE: + curhttp->send_pending = 0; + if( TCPDoneSend( curhttp->socket ) ) + { + if( curhttp->keep_alive ) + { + curhttp->state = HTTP_STATE_WAIT_METHOD; + } + else + { + HTTPClose( ); + } + } + break; + case HTTP_STATE_DATA_WEBSOCKET: + curhttp->send_pending = 0; + if( TCPCanSend( curhttp->socket, 1300 ) ) //TCPDoneSend + { + WebSocketTickInternal(); + } + break; + default: + if( timed ) + { + if( curhttp->timeout++ > HTTP_SERVER_TIMEOUT ) + { + HTTPClose( ); + } + } + } +} + +void HTTPTick( uint8_t timed ) +{ + uint8_t i; + for( i = 0; i < HTTP_CONNECTIONS; i++ ) + { + if( curhttp ) { HTDEBUG( "HTTPRXQ\n" ); break; } + curhttp = &HTTPConnections[i]; + DoHTTP( timed ); + curhttp = 0; + } +} + +void HTTPHandleInternalCallback( ) +{ + uint16_t i, bytestoread; + + if( curhttp->isdone ) + { + HTTPClose( ); + return; + } + if( curhttp->is404 ) + { + DataStartPacket(); + PushString("HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\nFile not found."); + EndTCPWrite( curhttp->socket ); + curhttp->isdone = 1; + return; + } + if( curhttp->isfirst ) + { + char stto[10]; + uint8_t slen = strlen( curhttp->pathbuffer ); + const char * k; + + DataStartPacket();; + //TODO: Content Length? MIME-Type? + PushString("HTTP/1.1 200 Ok\r\n"); + +#ifdef CUSTOM_HTTPHEADER_CODE + CUSTOM_HTTPHEADER_CODE +#endif + + if( curhttp->bytesleft < 0xfffffffe ) + { + PushString("Connection: "ISKEEPALIVE"\r\nContent-Length: "); + Uint32To10Str( stto, curhttp->bytesleft ); + PushBlob( stto, strlen( stto ) ); + curhttp->keep_alive = 1; + } + else + { + PushString("Connection: close"); + curhttp->keep_alive = 0; + } + + PushString( "\r\nContent-Type: " ); + //Content-Type? + while( slen && ( curhttp->pathbuffer[--slen] != '.' ) ); + k = &curhttp->pathbuffer[slen+1]; + + if( strcmp( k, "mp3" ) == 0 ) PushString( "audio/mpeg3" ); + else if( strcmp( k, "jpg" ) == 0 ) PushString( "image/jpeg" ); + else if( strcmp( k, "png" ) == 0 ) PushString( "image/png" ); + else if( strcmp( k, "css" ) == 0 ) PushString( "text/css" ); + else if( strcmp( k, "js" ) == 0 ) PushString( "text/javascript" ); + else if( strcmp( k, "gz" ) == 0 ) PushString( "text/plain\r\nContent-Encoding: gzip\r\nCache-Control: public, max-age=3600" ); + else if( curhttp->bytesleft == 0xfffffffe ) PushString( "text/plain" ); + else PushString( "text/html" ); + + if( curhttp->is_gzip ) PushString( "\r\nContent-Encoding: gzip" ); + + PushString( "\r\n\r\n" ); + EndTCPWrite( curhttp->socket ); + curhttp->isfirst = 0; + + return; + } + + DataStartPacket(); + + for( i = 0; i < 2 && curhttp->bytesleft; i++ ) + { + int bpt = curhttp->bytesleft; + if( bpt > MFS_SECTOR ) bpt = MFS_SECTOR; + curhttp->bytesleft = MFSReadSector( databuff_ptr, &curhttp->data.filedescriptor ); + databuff_ptr += bpt; + } + + EndTCPWrite( curhttp->socket ); + + if( !curhttp->bytesleft ) + curhttp->isdone = 1; +} + +void InternalStartHTTP( ) +{ + int32_t clusterno; + int8_t i; + char * path = &curhttp->pathbuffer[0]; + + curhttp->is_gzip = 0; + + if( curhttp->pathbuffer[0] == '/' ) + path++; + + if( path[0] == 'd' && path[1] == '/' ) + { + curhttp->is_dynamic = 1; + curhttp->isfirst = 1; + curhttp->isdone = 0; + curhttp->is404 = 0; + HTTPCustomStart(); + return; + } + + if( !path[0] ) + { + path = "index.html"; + } + + for( i = 0; path[i]; i++ ) + if( path[i] == '?' ) path[i] = 0; + + i = MFSOpenFile( path, &curhttp->data.filedescriptor ); + + curhttp->bytessofar = 0; + curhttp->is_gzip = 0; + + if( i == MFS_FILE_COMPRESSED_MEMORY ) + { + curhttp->is_gzip = 1; + } + else if( i < 0 ) + { + HTDEBUG( "404(%s)\n", path ); + curhttp->is404 = 1; + curhttp->isfirst = 1; + curhttp->isdone = 0; + curhttp->is_dynamic = 0; + curhttp->bytesleft = 0; + return; + } + + curhttp->isfirst = 1; + curhttp->isdone = 0; + curhttp->is404 = 0; + curhttp->is_dynamic = 0; + curhttp->bytesleft = curhttp->data.filedescriptor.filelen; +} + + +void http_disconnetcb(int conn ) { + int r = conn; + if( r>=0 ) + { + if( !HTTPConnections[r].is_dynamic ) MFSClose( &HTTPConnections[r].data.filedescriptor ); + HTTPConnections[r].state = 0; + } +} + + +void http_recvcb(int conn, char *pusrdata, unsigned short length) +{ + int whichhttp = conn; + //Though it might be possible for this to interrupt the other + //tick task, I don't know if this is actually a probelem. + //I'm adding this back-up-the-register just in case. + if( curhttp ) { HTDEBUG( "Unexpected Race Condition\n" ); return; } + + curhttp = &HTTPConnections[whichhttp]; + curdata = (uint8_t*)pusrdata; + curlen = length; + HTTPGotData(); + curhttp = 0 ; + +} + +int httpserver_connectcb( int socket ) +{ + int i; + + for( i = 0; i < HTTP_CONNECTIONS; i++ ) + { + if( HTTPConnections[i].state == 0 ) + { + HTTPConnections[i].socket = socket; + HTTPConnections[i].state = HTTP_STATE_WAIT_METHOD; + break; + } + } + if( i == HTTP_CONNECTIONS ) + { + return -1; + } + + return i; +} + + +int URLDecode( char * decodeinto, int maxlen, const char * buf ) +{ + int i = 0; + + for( ; buf && *buf; buf++ ) + { + char c = *buf; + if( c == '+' ) + { + decodeinto[i++] = ' '; + } + else if( c == '?' || c == '&' ) + { + break; + } + else if( c == '%' ) + { + if( *(buf+1) && *(buf+2) ) + { + decodeinto[i++] = hex2byte( buf+1 ); + buf += 2; + } + } + else + { + decodeinto[i++] = c; + } + if( i >= maxlen -1 ) break; + + } + decodeinto[i] = 0; + return i; +} + + +#ifndef RD_SHA1_HASH_LEN +#define RD_SHA1_HASH_LEN RD_SHA1_DIGEST_SIZE +#endif + +void WebSocketGotData( uint8_t c ) +{ + switch( curhttp->state_deets ) + { + case 0: + { + int i = 0; + char inkey[120]; + unsigned char hash[RD_SHA1_HASH_LEN]; + RD_SHA1_CTX c; + int inkeylen = 0; + + curhttp->is_dynamic = 1; + while( curlen > 20 ) + { + curdata++; curlen--; + if( strncmp( curdata, "Sec-WebSocket-Key: ", 19 ) == 0 ) + { + break; + } + } + + if( curlen <= 21 ) + { + HTDEBUG( "No websocket key found.\n" ); + curhttp->state = HTTP_WAIT_CLOSE; + return; + } + + curdata+= 19; + curlen -= 19; + + +#define WS_KEY_LEN 36 +#define WS_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +#define WS_RETKEY_SIZEM1 32 + + while( curlen > 1 ) + { + uint8_t lc = *(curdata++); + inkey[i] = lc; + curlen--; + if( lc == '\r' ) + { + inkey[i] = 0; + break; + } + i++; + if( i >= sizeof( inkey ) - WS_KEY_LEN - 5 ) + { + HTDEBUG( "Websocket key too big.\n" ); + curhttp->state = HTTP_WAIT_CLOSE; + return; + } + } + if( curlen <= 1 ) + { + HTDEBUG( "Invalid websocket key found.\n" ); + curhttp->state = HTTP_WAIT_CLOSE; + return; + } + + if( i + WS_KEY_LEN + 1 >= sizeof( inkey ) ) + { + HTDEBUG( "WSKEY Too Big.\n" ); + curhttp->state = HTTP_WAIT_CLOSE; + return; + } + + memcpy( &inkey[i], WS_KEY, WS_KEY_LEN + 1 ); + i += WS_KEY_LEN; + RD_SHA1_Init( &c ); + RD_SHA1_Update( &c, inkey, i ); + RD_SHA1_Final( hash, &c ); + +#if (WS_RETKEY_SIZE > MAX_HTTP_PATHLEN - 10 ) +#error MAX_HTTP_PATHLEN too short. +#endif + + my_base64_encode( hash, RD_SHA1_HASH_LEN, curhttp->pathbuffer + (MAX_HTTP_PATHLEN-WS_RETKEY_SIZEM1) ); + + curhttp->bytessofar = 0; + curhttp->bytesleft = 0; + + NewWebSocket(); + //Respond... + curhttp->state_deets = 1; + break; + } + case 1: + if( c == '\n' ) curhttp->state_deets = 2; + break; + case 2: + if( c == '\r' ) curhttp->state_deets = 3; + else curhttp->state_deets = 1; + break; + case 3: + if( c == '\n' ) curhttp->state_deets = 4; + else curhttp->state_deets = 1; + break; + case 5: //Established connection. + { + //XXX TODO: Seems to malfunction on large-ish packets. I know it has problems with 140-byte payloads. + + do + { + if( curlen < 5 ) //Can't interpret packet. + break; + + uint8_t fin = c & 1; + uint8_t opcode = c << 4; + uint16_t payloadlen = *(curdata++); + + curlen--; + if( !(payloadlen & 0x80) ) + { + HTDEBUG( "Unmasked packet (%d)\n", payloadlen ); + curhttp->state = HTTP_WAIT_CLOSE; + break; + } + + if( opcode == 128 ) + { + //Close connection. + //HTDEBUG( "CLOSE\n" ); + //curhttp->state = HTTP_WAIT_CLOSE; + //break; + } + + payloadlen &= 0x7f; + if( payloadlen == 127 ) + { + //Very long payload. + //Not supported. + HTDEBUG( "Unsupported payload packet.\n" ); + curhttp->state = HTTP_WAIT_CLOSE; + break; + } + else if( payloadlen == 126 ) + { + payloadlen = (curdata[0] << 8) | curdata[1]; + curdata += 2; + curlen -= 2; + } + wsmask[0] = curdata[0]; + wsmask[1] = curdata[1]; + wsmask[2] = curdata[2]; + wsmask[3] = curdata[3]; + curdata += 4; + curlen -= 4; + wsmaskplace = 0; + + //XXX Warning: When packets get larger, they may split the + //websockets packets into multiple parts. We could handle this + //but at the cost of prescious RAM. I am chosing to just drop those + //packets on the floor, and restarting the connection. + if( curlen < payloadlen ) + { + extern int cork_binary_rx; + cork_binary_rx = 1; + //HTDEBUG( "Websocket Fragmented. %d %d\n", curlen, payloadlen ); + //curhttp->state = HTTP_WAIT_CLOSE; + HTDEBUG( "Websocket Fragmented. %d %d\n", curlen, payloadlen ); + curhttp->state = HTTP_WAIT_CLOSE; + return; + } + + char * newcurdata = curdata + payloadlen + 1; + WebSocketData( payloadlen ); + curlen -= payloadlen; + curdata = newcurdata; + } + while( curlen > 5 ); + break; + } + default: + break; + } +} + +void WebSocketTickInternal() +{ + switch( curhttp->state_deets ) + { + case 4: //Has key full HTTP header, etc. wants response. + DataStartPacket();; + PushString( "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " ); + PushString( curhttp->pathbuffer + (MAX_HTTP_PATHLEN-WS_RETKEY_SIZEM1) ); + PushString( "\r\n\r\n" ); + EndTCPWrite( curhttp->socket ); + curhttp->state_deets = 5; + curhttp->keep_alive = 0; + break; + case 5: + WebSocketTick(); + break; + } +} + +void WebSocketSend( uint8_t * data, int size ) +{ + DataStartPacket();; + PushByte( 0x82 ); //0x81 is text. + if( size >= 126 ) + { + PushByte( 0x00 | 126 ); + PushByte( size>>8 ); + PushByte( size&0xff ); + } + else + { + PushByte( 0x00 | size ); + } + PushBlob( data, size ); + EndTCPWrite( curhttp->socket ); + curhttp->send_pending = 1; +} + +uint8_t WSPOPMASK() +{ + uint8_t mask = wsmask[wsmaskplace]; + wsmaskplace = (wsmaskplace+1)&3; + return (*curdata++)^(mask); +} + + + + + + +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#include +#define socklen_t uint32_t +#define SHUT_RDWR SD_BOTH +#define MSG_NOSIGNAL 0 +#else +#define closesocket close +#include +#include +#include +#include +#include +uint16_t htons(uint16_t hostshort); +#endif + +#include +#include +#include +#include + +static int serverSocket; + +uint8_t * databuff_ptr; +uint8_t databuff[1536]; +int cork_binary_rx; + + +#ifndef HTTP_POLL_TIMEOUT +#define HTTP_POLL_TIMEOUT 0 //This is a little weird, it's an adaptation from cnhttp. +#endif + + +#define DESTROY_SOCKETS_LIST 200 +int destroy_sockets[DESTROY_SOCKETS_LIST]; +int destroy_socket_head = 0; +int sockets[HTTP_CONNECTIONS]; + +void et_espconn_disconnect( int socket ) +{ + shutdown( socket, SHUT_RDWR ); + int i; + //printf( "Shut: %d\n", socket ); + for( i = 0; i < HTTP_CONNECTIONS; i++ ) + { + if( sockets[i] == socket ) + { + http_disconnetcb( i ); + sockets[i] = 0; + } + } + + if( destroy_sockets[destroy_socket_head] ) closesocket( destroy_sockets[destroy_socket_head] ); + destroy_sockets[destroy_socket_head] = socket; + destroy_socket_head = (destroy_socket_head+1)%DESTROY_SOCKETS_LIST; + +} + +void DataStartPacket() +{ + databuff_ptr = databuff; +} + +void PushByte( uint8_t c ) +{ + if( databuff_ptr - databuff + 1 >= sizeof( databuff ) ) return; + *(databuff_ptr++) = c; +} + +void PushBlob( const uint8_t * data, int len ) +{ + if( databuff_ptr - databuff + len >= sizeof( databuff ) ) return; + memcpy( databuff_ptr, data, len ); + databuff_ptr += len; +} + +void PushString( const char * data ) +{ + int len = strlen( data ); + if( databuff_ptr - databuff + len >= sizeof( databuff ) ) return; + memcpy( databuff_ptr, data, len ); + databuff_ptr += len; +} + +int TCPCanSend( int socket, int size ) +{ + fd_set write_fd_set; + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO (&write_fd_set); + FD_SET (socket, &write_fd_set); + + int r = select (FD_SETSIZE, NULL, &write_fd_set, NULL, &tv); + if (r < 0) + { + perror ("select"); + return -1; + } + return r; +} + + +int TCPCanRead( int sock ) +{ + fd_set read_fd_set; + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO (&read_fd_set); + FD_SET (sock, &read_fd_set); + + int r; + r = select (FD_SETSIZE, &read_fd_set, NULL, NULL, &tv); + if (r < 0) + { + perror ("select"); + return -1; + } + return r; +} + + +int TCPException( int sock ) +{ + int error_code; + int error_code_size = sizeof(error_code); + getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&error_code, &error_code_size); + if( error_code >= 0 ) return 0; + else return 1; +} + +int TCPDoneSend( int socket ) +{ + return TCPCanSend( socket, 1 ); +} + + +int EndTCPWrite( int socket ) +{ + int r = send( socket, databuff, databuff_ptr-databuff, MSG_NOSIGNAL ); + databuff_ptr = databuff; + return r; +} + +void TermHTTPServer() +{ + shutdown( serverSocket, SHUT_RDWR ); +} + +int TickHTTP() +{ + int i; + //struct pollfd allpolls[HTTP_CONNECTIONS+1]; + short mappedhttp[HTTP_CONNECTIONS+1]; + if( serverSocket == 0 ) return -1; + + do + { + static double last; + double now; +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) + static LARGE_INTEGER lpf; + LARGE_INTEGER li; + + if( !lpf.QuadPart ) + { + QueryPerformanceFrequency( &lpf ); + } + + QueryPerformanceCounter( &li ); + now = (double)li.QuadPart / (double)lpf.QuadPart; +#else + struct timeval tv; + gettimeofday( &tv, 0 ); + now = ((double)tv.tv_usec)/1000000. + (tv.tv_sec); +#endif + double dl = now - last; + if( dl > .1 ) + { + int i; + HTTPTick( 1 ); + last = now; + } + else + { + HTTPTick( 0 ); + } + +/* int pollct = 1; + allpolls[0].fd = serverSocket; + allpolls[0].events = LISTENPOLL; + for( i = 0; i < HTTP_CONNECTIONS;i ++) + { + if( !sockets[i] || HTTPConnections[i].state == 0 ) continue; + allpolls[pollct].fd = sockets[i]; + allpolls[pollct].events = POLLIN | (HTTPConnections[i].send_pending?POLLOUT:0); + mappedhttp[pollct] = i; + pollct++; + } + + //Do something to watch all currently-waiting sockets. + poll( allpolls, pollct, HTTP_POLL_TIMEOUT ); +*/ + //If there's faults, bail. + if( TCPException( serverSocket ) ) + { + closesocket( serverSocket ); + for( i = 0; i < HTTP_CONNECTIONS;i ++) + { + if( sockets[i] ) closesocket( sockets[i] ); + } + break; + } + if( TCPCanRead( serverSocket ) ) + { + struct sockaddr_in tin; + socklen_t addrlen = sizeof(tin); + memset( &tin, 0, addrlen ); + int tsocket = accept( serverSocket, (struct sockaddr *)&tin, &addrlen ); + +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) + struct linger lx; + lx.l_onoff = 1; + lx.l_linger = 0; + + //Disable the linger here, too. + setsockopt( tsocket, SOL_SOCKET, SO_LINGER, (const char*)&lx, sizeof( lx ) ); +#else + struct linger lx; + lx.l_onoff = 1; + lx.l_linger = 0; + + //Disable the linger here, too. + setsockopt( tsocket, SOL_SOCKET, SO_LINGER, &lx, sizeof( lx ) ); +#endif + + int r = httpserver_connectcb( tsocket ); + + if( r == -1 ) + { + closesocket( tsocket ); + } + else + { + sockets[r] = tsocket; + } + } + for( i = 0; i < HTTP_CONNECTIONS; i++) + { + int wc = i; + if( !sockets[i] || HTTPConnections[i].state == 0 ) continue; + if( TCPException(sockets[i]) ) + { + http_disconnetcb( wc ); + closesocket( sockets[wc] ); + sockets[wc] = 0; + } + else if( TCPCanRead( sockets[i] ) ) + { + int dco = HTTPConnections[i].corked_data_place; + uint8_t data[8192]; + memcpy( data, HTTPConnections[i].corked_data, dco ); + int len = recv( sockets[wc], data+dco, 8192-dco, 0 ); + if( len ) + { + cork_binary_rx = 0; + http_recvcb( wc, data, len+dco ); + if( cork_binary_rx ) + { + int to_cork = len; + if( to_cork > sizeof( HTTPConnections[i].corked_data ) + HTTPConnections[i].corked_data_place ) + { + http_disconnetcb( wc ); + closesocket ( sockets[wc] ); + sockets[wc] = 0; + fprintf( stderr, "Error: too much data to buffer on websocket\n" ); + } + else + { + memcpy( HTTPConnections[i].corked_data + dco, data + dco, to_cork ); + HTTPConnections[i].corked_data_place += to_cork; + } + } + else + { + HTTPConnections[i].corked_data_place = 0; + } + } + else + { + http_disconnetcb( wc ); + closesocket( sockets[wc] ); + sockets[wc] = 0; + } + } + } + } +#if HTTP_POLL_TIMEOUT != 0 + while(1); +#else + while(0); +#endif + + return 0; +} + + +int RunHTTP( int port ) +{ +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) +{ + WORD wVersionRequested; + WSADATA wsaData; + int err; + wVersionRequested = MAKEWORD(2, 2); + + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + /* Tell the user that we could not find a usable */ + /* Winsock DLL. */ + fprintf( stderr, "WSAStartup failed with error: %d\n", err); + return 1; + } +} +#endif + + + int acceptfaults = 0; + struct sockaddr_in sin; + serverSocket = socket(AF_INET, SOCK_STREAM, 0); + + //Make sure the socket worked. + if( serverSocket == -1 ) + { + fprintf( stderr, "Error: Cannot create socket.\n" ); + return -1; + } + + //Disable SO_LINGER (Well, enable it but turn it way down) +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) + struct linger lx; + lx.l_onoff = 1; + lx.l_linger = 0; + setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, (const char *)&lx, sizeof( lx ) ); + + //Enable SO_REUSEADDR + int reusevar = 1; + setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reusevar, sizeof(reusevar)); +#else + struct linger lx; + lx.l_onoff = 1; + lx.l_linger = 0; + setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, &lx, sizeof( lx ) ); + + //Enable SO_REUSEADDR + int reusevar = 1; + setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &reusevar, sizeof(reusevar)); +#endif + //Setup socket for listening address. + memset( &sin, 0, sizeof( sin ) ); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = htons( port ); + + //Actually bind to the socket + if( bind( serverSocket, (struct sockaddr *) &sin, sizeof( sin ) ) == -1 ) + { + fprintf( stderr, "Could not bind to socket: %d\n", port ); + closesocket( serverSocket ); + serverSocket = 0; + return -1; + } + + //Finally listen. + if( listen( serverSocket, 5 ) == -1 ) + { + fprintf(stderr, "Could not lieten to socket."); + closesocket( serverSocket ); + serverSocket = 0; + return -1; + } + + return 0; +} + + + + + + +void Uint32To10Str( char * out, uint32_t dat ) +{ + int tens = 1000000000; + int val; + int place = 0; + + while( tens > 1 ) + { + if( dat/tens ) break; + tens/=10; + } + + while( tens ) + { + val = dat/tens; + dat -= val*tens; + tens /= 10; + out[place++] = val + '0'; + } + + out[place] = 0; +} + +//from http://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c +static const char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/'}; + +static const int mod_table[] = {0, 2, 1}; + +void my_base64_encode(const unsigned char *data, unsigned int input_length, uint8_t * encoded_data ) +{ + + int i, j; + int output_length = 4 * ((input_length + 2) / 3); + + if( !encoded_data ) return; + if( !data ) { encoded_data[0] = '='; encoded_data[1] = 0; return; } + + for (i = 0, j = 0; i < input_length; ) { + + uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0; + uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0; + uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0; + + uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; + + encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; + encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; + encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; + encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; + } + + for (i = 0; i < mod_table[input_length % 3]; i++) + encoded_data[output_length - 1 - i] = '='; + + encoded_data[j] = 0; +} + +uint8_t hex1byte( char c ) +{ + if( c >= '0' && c <= '9' ) return c - '0'; + if( c >= 'a' && c <= 'f' ) return c - 'a' + 10; + if( c >= 'A' && c <= 'F' ) return c - 'A' + 10; + return 0; +} + +uint8_t hex2byte( const char * c ) +{ + return (hex1byte(c[0])<<4) | (hex1byte(c[1])); +} + +#include + +#ifdef USE_RAM_MFS + +#include "tools/rawdraw_http_page.h" + +//Returns 0 on succses. +//Returns size of file if non-empty +//If positive, populates mfi. +//Returns -1 if can't find file or reached end of file list. +int8_t MFSOpenFile( const char * fname, struct MFSFileInfo * mfi ) +{ +#ifdef CNFG_DISABLE_HTTP_FILES + mfi->filelen = 0; + return -1; +#else + if( strcmp( fname, "/" ) == 0 || strcmp( fname, "index.html" ) == 0 ) + { + mfi->offset = 0; + mfi->filelen = sizeof(webpage_buffer); + return MFS_FILE_COMPRESSED_MEMORY; + } + else + return -1; +#endif +} + +int32_t MFSReadSector( uint8_t* data, struct MFSFileInfo * mfi ) +{ +#ifdef CNFG_DISABLE_HTTP_FILES + return 0; +#else + //returns # of bytes left tin file. + if( !mfi->filelen ) + { + return 0; + } + + int toread = mfi->filelen; + if( toread > MFS_SECTOR ) toread = MFS_SECTOR; + memcpy( data, &webpage_buffer[mfi->offset], toread ); + mfi->offset += toread; + mfi->filelen -= toread; + return mfi->filelen; +#endif +} + +void MFSClose( struct MFSFileInfo * mfi ) +{ +} + +#else + + +//Returns 0 on succses. +//Returns size of file if non-empty +//If positive, populates mfi. +//Returns -1 if can't find file or reached end of file list. +int8_t MFSOpenFile( const char * fname, struct MFSFileInfo * mfi ) +{ + char targfile[1024]; + + if( strlen( fname ) == 0 || fname[strlen(fname)-1] == '/' ) + { + snprintf( targfile, sizeof( targfile ) - 1, "tools/rawdraw_http_files/%s/index.html", fname ); + } + else + { + snprintf( targfile, sizeof( targfile ) - 1, "tools/rawdraw_http_files/%s", fname ); + } + + //printf( ":%s:\n", targfile ); + + FILE * f = mfi->file = fopen( targfile, "rb" ); + if( f <= 0 ) return -1; + //printf( "F: %p\n", f ); + fseek( f, 0, SEEK_END ); + mfi->filelen = ftell( f ); + fseek( f, 0, SEEK_SET ); + return 0; +} + +int32_t MFSReadSector( uint8_t* data, struct MFSFileInfo * mfi ) +{ + if( !mfi->filelen ) + { + return 0; + } + + int toread = fread( data, 1, MFS_SECTOR, mfi->file ); + mfi->filelen -= toread; + return mfi->filelen; +} + +void MFSClose( struct MFSFileInfo * mfi ) +{ + if( mfi->file ) fclose( mfi->file ); +} + + +#endif + + +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +----------------- +Modified 7/98 +By James H. Brown +Still 100% Public Domain + +Corrected a problem which generated improper hash values on 16 bit machines +Routine SHA1Update changed from + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int +len) +to + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned +long len) + +The 'len' parameter was declared an int which works fine on 32 bit machines. +However, on 16 bit machines an int is too small for the shifts being done +against +it. This caused the hash function to generate incorrect values if len was +greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + +Since the file IO in main() reads 16K at a time, any file 8K or larger would +be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million +"a"s). + +I also changed the declaration of variables i & j in SHA1Update to +unsigned long from unsigned int for the same reason. + +These changes should make no difference to any 32 bit implementations since +an +int and a long are the same size in those environments. + +-- +I also corrected a few compiler warnings generated by Borland C. +1. Added #include for exit() prototype +2. Removed unused variable 'j' in SHA1Final +3. Changed exit(0) to return(0) at end of main. + +ALL changes I made can be located by searching for comments containing 'JHB' +----------------- +Modified 8/98 +By Steve Reid +Still 100% public domain + +1- Removed #include and used return() instead of exit() +2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) +3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + +----------------- +Modified 4/01 +By Saul Kravitz +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Modified 07/2002 +By Ralph Giles +Still 100% public domain +modified for use with stdint types, autoconf +code cleanup, removed attribution comments +switched SHA1Final() argument order for consistency +use SHA1_ prefix for public api +move public api to sha1.h +*/ + +/* +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define SHA1HANDSOFF */ + +#include +#include + + +static void RD_SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); + +#define RDrol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +/* FIXME: can we do this in an endian-proof way? */ +#ifdef WORDS_BIGENDIAN +#define RDblk0(i) block->l[i] +#else +#define RDblk0(i) (block->l[i] = (RDrol(block->l[i],24)&0xFF00FF00) \ + |(RDrol(block->l[i],8)&0x00FF00FF)) +#endif +#define RDblk(i) (block->l[i&15] = RDrol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+RDblk0(i)+0x5A827999+RDrol(v,5);w=RDrol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+RDblk(i)+0x5A827999+RDrol(v,5);w=RDrol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+RDblk(i)+0x6ED9EBA1+RDrol(v,5);w=RDrol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+RDblk(i)+0x8F1BBCDC+RDrol(v,5);w=RDrol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+RDblk(i)+0xCA62C1D6+RDrol(v,5);w=RDrol(w,30); + +/* Hash a single 512-bit block. This is the core of the algorithm. */ +static void RD_SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) +{ + uint32_t a, b, c, d, e; + typedef union { + uint8_t c[64]; + uint32_t l[16]; + } CHAR64LONG16; + CHAR64LONG16* block; + +#ifdef SHA1HANDSOFF + static uint8_t workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16*)buffer; +#endif + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1Init - Initialize new context */ +static void RD_SHA1_Init(RD_SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ +static void RD_SHA1_Update(RD_SHA1_CTX* context, const uint8_t* data, const unsigned long len) +{ + size_t i, j; + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + RD_SHA1_Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + RD_SHA1_Transform(context->state, data + i); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); + +#ifdef VERBOSE + SHAPrintContext(context, "after "); +#endif +} + + +/* Add padding and return the message digest. */ +static void RD_SHA1_Final(uint8_t digest[RD_SHA1_DIGEST_SIZE],RD_SHA1_CTX* context) +{ + uint32_t i; + uint8_t finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + RD_SHA1_Update(context, (uint8_t *)"\200", 1); + while ((context->count[0] & 504) != 448) { + RD_SHA1_Update(context, (uint8_t *)"\0", 1); + } + RD_SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ + for (i = 0; i < RD_SHA1_DIGEST_SIZE; i++) { + digest[i] = (uint8_t) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + + /* Wipe variables */ + i = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(finalcount, 0, 8); /* SWR */ + +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ + SHA1_Transform(context->state, context->buffer); +#endif +} + +#ifndef CNFGHTTPSERVERONLY + +/*************************************************************/ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +char * cnfg_http_window_name; + +int rd_request_fullscreen; + +uint32_t * transitbuffer; +int transitlen; +int transitmax; + +uint32_t * backbuffer; +int backbufferlen; +int backbuffermax; + +uint32_t * datacmds; +int curdatacmds; +int maxdatacmds; + +short last_dimensions_w; +short last_dimensions_h; + +short cnfg_req_w; +short cnfg_req_h; + + +void HTTPCustomCallback( ) +{ + if( curhttp->rcb ) + ((void(*)())curhttp->rcb)(); + else + curhttp->isdone = 1; +} + + +//Close of curhttp happened. +void CloseEvent() +{ +} + +static void readrdbuffer_websocket_dat( int len ) +{ + do + { + int bufferok = TCPCanSend( curhttp->socket, 1340 ); + if( transitlen <= 0 || !bufferok || curhttp->bytesleft == -1 ) return; + + int offset = transitlen * 4 - curhttp->bytesleft; + uint8_t * offdata = ((uint8_t*)transitbuffer) + offset; + int tosend = curhttp->bytesleft; + if( tosend > 1340 ) tosend = 1340; + + WebSocketSend( offdata, tosend ); + + //Special - we send an empty frame to indicate competion. + if( tosend == 0 ) + curhttp->bytesleft = -1; + else + curhttp->bytesleft -= tosend; + } while(1); +} + + + +void ConsumeBackBufferForTransit() +{ + int len = transitlen; + int maxv = transitmax; + uint32_t * dcmd = transitbuffer; + + transitbuffer = backbuffer; + transitlen = backbufferlen; + transitmax = backbuffermax; + + backbuffer = dcmd; + backbufferlen = 0; + backbuffermax = maxv; +} + +static void readrdbuffer_websocket_cmd( int len ) +{ + uint8_t buf[1300]; + int i; + + if( len > 1300 ) len = 1300; + + for( i = 0; i < len; i++ ) + { + buf[i] = WSPOPMASK(); + } + + if( strncmp( buf, "SWAP", 4 ) == 0 ) + { + last_dimensions_w = buf[4] | ( buf[5]<<8 ); + last_dimensions_h = buf[6] | ( buf[7]<<8 ); + ConsumeBackBufferForTransit(); + curhttp->bytesleft = transitlen * 4; + } + + if( strncmp( buf, "MOTN", 4 ) == 0 ) + { + int x = buf[4] | ( buf[5]<<8 ); + int y = buf[6] | ( buf[7]<<8 ); + int but = buf[11]; + HandleMotion( x, y, but ); + } + + if( strncmp( buf, "BUTN", 4 ) == 0 ) + { + int x = buf[4] | ( buf[5]<<8 ); + int y = buf[6] | ( buf[7]<<8 ); + int down = buf[10]; + int but = buf[11]; + HandleButton( x, y, but, down ); + } + + if( strncmp( buf, "KEYB", 4 ) == 0 ) + { + int key = buf[6]; + int down = buf[7]; + HandleKey( key, down ); + } +} + + + + + +void NewWebSocket() +{ + if( strncmp( (const char*)curhttp->pathbuffer, "/d/ws/cmdbuf", 9 ) == 0 ) + { + printf( "Got connection.\n" ); + curhttp->rcb = (void*)&readrdbuffer_websocket_dat; + curhttp->rcbDat = (void*)&readrdbuffer_websocket_cmd; + } + else + { + curhttp->is404 = 1; + } +} + +void WebSocketTick() +{ + if( curhttp->rcb ) + { + ((void(*)())curhttp->rcb)(); + } +} + +void WebSocketData( int len ) +{ + if( curhttp->rcbDat ) + { + ((void(*)( int ))curhttp->rcbDat)( len ); + } +} + +void HTTPCustomStart( ) +{ + + curhttp->rcb = 0; + curhttp->bytesleft = 0; + + curhttp->isfirst = 1; + HTTPHandleInternalCallback(); +} + +void QueueCmds( uint32_t * toqueue, int numwords ) +{ + int origcmds = curdatacmds; + curdatacmds += numwords; + if( curdatacmds > maxdatacmds ) + { + datacmds = realloc( datacmds, curdatacmds*4 ); + maxdatacmds = curdatacmds; + } + memcpy( datacmds + origcmds, toqueue, numwords*4 ); +} + +int CNFGSetup( const char * WindowName, int w, int h ) +{ + RunHTTP( 8888 ); + if( cnfg_http_window_name ) free( cnfg_http_window_name ); + cnfg_http_window_name = strdup( WindowName ); + datacmds = 0; + backbuffer = 0; + backbufferlen = 0; + curdatacmds = 0; + maxdatacmds = 0; + rd_request_fullscreen = 0; + cnfg_req_w = w; + cnfg_req_h = h; + return 0; +} + +void CNFGSetupFullscreen( const char * WindowName, int screen_number ) +{ + CNFGSetup( WindowName, -1, -1 ); + rd_request_fullscreen = 1; +} + +int CNFGHandleInput() +{ + TickHTTP(); + return 1; +} + +// command structure: +// 0: Continuation +// 1: Color +// 2: Pixel +// 3: SegmentStart +// 4: RectangleStart +// 5: PolyStart +// 6: PolyContinue +// 7: Blit Pixels +// 8: Clear frame +// 9: Swap Buffers (Used as "end of frame") +// a: CNFGSetLineWidth + +uint32_t CNFGColor( uint32_t RGBA ) +{ + uint32_t cmds[2] = { 0x10000000, RGBA }; + QueueCmds( cmds, 2 ); + return RGBA; +} + +void CNFGTackPixel( short x1, short y1 ) +{ + if( x1 < 0 || x1 > 0x3fff ) return; + if( y1 < 0 || y1 > 0x3fff ) return; + uint32_t cmds[1] = { 0x20000000 | (x1 & 0x3fff ) | ( ( y1 & 0x3fff ) << 14 ) }; + QueueCmds( cmds, 1 ); +} + +void CNFGTackSegment( short x1, short y1, short x2, short y2 ) +{ + uint32_t cmds[3] = { + 0x30000000, + ( (uint16_t)x1 ) | ( (uint16_t)y1 << 16 ), + ( (uint16_t)x2 ) | ( (uint16_t)y2 << 16 ) }; + QueueCmds( cmds, 3 ); +} + +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ) +{ + uint32_t cmds[3] = { + 0x40000000, + ( (uint16_t)x1 ) | ( (uint16_t)y1 << 16 ), + ( (uint16_t)x2 ) | ( (uint16_t)y2 << 16 ) }; + QueueCmds( cmds, 3 ); +} + +void CNFGSetLineWidth( short width ) +{ + uint32_t cmds[1] = { 0xa0000000 | width }; + QueueCmds( cmds, 1 ); +} + +void CNFGTackPoly( RDPoint * points, int verts ) +{ + uint32_t * cmds = alloca( 4 * verts + 4 ); + int i; + cmds[0] = 0x50000000 | verts; + for( i = 0; i < verts; i++ ) + { + uint16_t lx = points[i].x; + uint16_t ly = points[i].y; + cmds[i+1] = lx | ( ly << 16 ); + } + QueueCmds( cmds, verts + 1 ); +} + +void CNFGClearFrame() +{ + int sl = strlen( cnfg_http_window_name ); + int blocks = ( sl + 8 + 8 ) / 4; + uint32_t * cmds = alloca( blocks * 4 ); + cmds[0] = 0x80000000 | blocks | (rd_request_fullscreen<<8) | (sl<<16); + cmds[1] = CNFGBGColor; + cmds[2] = cnfg_req_w | ( cnfg_req_h << 16); + memcpy( cmds+3, cnfg_http_window_name, sl + 1 ); + QueueCmds( cmds, blocks ); +} + +void CNFGSwapBuffers() +{ + uint32_t cmds[1] = { 0x90000000 }; + QueueCmds( cmds, 1 ); + + // And swap. + int len = curdatacmds; + int maxv = maxdatacmds; + uint32_t * dcmd = datacmds; + + datacmds = backbuffer; + curdatacmds = 0; + maxdatacmds = backbuffermax; + + backbuffer = dcmd; + backbufferlen = len; + backbuffermax = maxv; +} + +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ) +{ + if( w < 0 || h < 0 ) return; + + uint32_t * cmds = alloca( 4 * w * h + 8 ); + cmds[0] = 0x70000000 | ( w & 0x3fff ) | ( ( h & 0x3fff ) << 14 ); + cmds[1] = ( x | ( y<<16) ); + int i; + memcpy( cmds + 2, data, w * h * 4 ); + QueueCmds( cmds, w * h + 2 ); +} + +void CNFGGetDimensions( short * x, short * y ) +{ + *x = last_dimensions_w; + *y = last_dimensions_h; +} + +#endif + +#endif diff --git a/app/src/nativecode/rawdraw/CNFGNullDriver.c b/app/src/nativecode/rawdraw/CNFGNullDriver.c new file mode 100644 index 0000000..8994954 --- /dev/null +++ b/app/src/nativecode/rawdraw/CNFGNullDriver.c @@ -0,0 +1,70 @@ +//Copyright (c) 2017 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose. + +#include "DrawFunctions.h" + +static int w, h; +void CNFGGetDimensions( short * x, short * y ) +{ + *x = w; + *y = h; +} + +static void InternalLinkScreenAndGo( const char * WindowName ) +{ +} + +void CNFGSetupFullscreen( const char * WindowName, int screen_no ) +{ + CNFGSetup( WindowName, 640, 480 ); +} + + +void CNFGTearDown() +{ +} + +int CNFGSetup( const char * WindowName, int sw, int sh ) +{ + w = sw; + h = sh; + return 0; +} + +void CNFGHandleInput() +{ +} + + +void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h ) +{ +} + + +uint32_t CNFGColor( uint32_t RGB ) +{ +} + +void CNFGClearFrame() +{ +} + +void CNFGSwapBuffers() +{ +} + +void CNFGTackSegment( short x1, short y1, short x2, short y2 ) +{ +} + +void CNFGTackPixel( short x1, short y1 ) +{ +} + +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ) +{ +} + +void CNFGTackPoly( RDPoint * points, int verts ) +{ +} + diff --git a/app/src/nativecode/rawdraw/CNFGOGLEGLDriver.c b/app/src/nativecode/rawdraw/CNFGOGLEGLDriver.c new file mode 100644 index 0000000..bae2e2a --- /dev/null +++ b/app/src/nativecode/rawdraw/CNFGOGLEGLDriver.c @@ -0,0 +1,189 @@ +//Copyright (c) 2011, 2017, 2018 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose. +//portions from +//https://github.com/NVIDIA-developer-blog/code-samples/blob/master/posts/egl_OpenGl_without_Xserver/tinyegl.cc + +//NOTE: This is a truly incomplete driver - if no EGL surface is available, it does not support direct buffer rendering. +//Additionally no input is connected. + +#include "CNFG.h" + +#include +#include +#include +#include +#include + +#include +#include + +#ifdef USE_EGL_GET_DISPLAY +#include +#else +#define EGL_EGLEXT_PROTOTYPES +#include +#include +#endif + +#ifdef USE_EGL_SURFACE +#include +#else +#define GL_GLEXT_PROTOTYPES +#include +#include +#endif + + +static const EGLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_DEPTH_SIZE, 1, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_NONE +}; + +static int pbufferWidth = 0; +static int pbufferHeight = 0; +static EGLint pbufferAttribs[] = { + EGL_WIDTH, 0, + EGL_HEIGHT, 0, + EGL_NONE, +}; + +#ifdef CNFGOGL +#include +#include +GLXContext CNFGCtx; +void * CNFGGetExtension( const char * extname ) { return glXGetProcAddressARB((const GLubyte *) extname); } +#endif + +EGLDisplay eglDpy = 0; +EGLContext eglCtx = 0; +EGLSurface eglSurf = 0; + +//These aren't actually used. Just here to allow linking. +XWindowAttributes CNFGWinAtt; +XClassHint *CNFGClassHint; +Display *CNFGDisplay; +Window CNFGWindow; +int CNFGWindowInvisible; +Pixmap CNFGPixmap; +GC CNFGGC; +GC CNFGWindowGC; +Visual * CNFGVisual; + +int FullScreen = 0; + +void CNFGGetDimensions( short * x, short * y ) +{ + *x = pbufferWidth; + *y = pbufferHeight; +} + +void CNFGChangeWindowTitle( const char * WindowName ) +{ +} + +void CNFGSetupFullscreen( const char * WindowName, int screen_no ) +{ + printf( "Full screen setup stubbed.\n" ); + CNFGSetup( WindowName, 640, 480 ); +} + +void CNFGTearDown() +{ + if( eglDpy ) + { + eglTerminate( eglDpy ); + } + //Unimplemented. +} + +int CNFGSetup( const char * WindowName, int w, int h ) +{ +// glXMakeCurrent( CNFGDisplay, CNFGWindow, CNFGCtx ); + eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + atexit( CNFGTearDown ); + printf( "EGL Display: %p\n", eglDpy ); + if( eglDpy == 0 ) + { + printf( "Trying alternate method.\n" ); + // obtain display by specifying an appropriate device. This is the preferred method today. + // load the function pointers for the device,platform extensions + PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = + (PFNEGLQUERYDEVICESEXTPROC) eglGetProcAddress("eglQueryDevicesEXT"); + if(!eglQueryDevicesEXT) { + printf("ERROR: extension eglQueryDevicesEXT not available"); + return(-1); + } + + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = + (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); + + if(!eglGetPlatformDisplayEXT) { + printf("ERROR: extension eglGetPlatformDisplayEXT not available"); + return(-1); + } + + static const int MAX_DEVICES = 16; + EGLDeviceEXT devices[MAX_DEVICES]; + EGLint numDevices; + + eglQueryDevicesEXT(MAX_DEVICES, devices, &numDevices); + + eglDpy = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, devices[0], 0); + printf( "EGL Backup Display: %p\n", eglDpy ); + } + + + pbufferAttribs[1] = pbufferWidth = w; + pbufferAttribs[3] = pbufferHeight = h; + + EGLint major, minor; + eglInitialize(eglDpy, &major, &minor); + + EGLint numConfigs; + EGLConfig eglCfg; + eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs); + printf( "EGL Major Minor: %d %d\n", major, minor ); + eglBindAPI(EGL_OPENGL_API); + eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, NULL); + int err = eglGetError(); if(err != EGL_SUCCESS) { printf("1. Error %d\n", err); } + printf( "EGL Got context: %p\n", eglCtx ); + + if( w > 0 && h > 0 ) + { + eglSurf = eglCreatePbufferSurface(eglDpy, eglCfg, pbufferAttribs); + eglMakeCurrent(eglDpy, eglSurf, eglSurf, eglCtx); + printf( "EGL Current, with surface %p\n", eglSurf ); + //Actually have a surface. Need to allocate it. + } + else + { + eglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, eglCtx); + printf( "EGL Current, no surface.\n" ); + } + return 0; +} + +int CNFGHandleInput() +{ + //Stubbed (No input) + return 1; +} +void CNFGUpdateScreenWithBitmap( unsigned long * data, int w, int h ) +{ + printf( "Stubbed update screen\n" ); +} +void CNFGSetVSync( int vson ) +{ + //No-op +} + +void CNFGSwapBuffers() +{ + //No-op +} + + diff --git a/app/src/nativecode/rawdraw/CNFGRasterizer.c b/app/src/nativecode/rawdraw/CNFGRasterizer.c new file mode 100644 index 0000000..6561417 --- /dev/null +++ b/app/src/nativecode/rawdraw/CNFGRasterizer.c @@ -0,0 +1,341 @@ +//Don't call this file yourself. It is intended to be included in any drivers which want to support the rasterizer plugin. + +#ifdef CNFGRASTERIZER +#include "CNFG.h" +//#include +#include + +uint32_t * CNFGBuffer = 0; +short CNFGBufferx; +short CNFGBuffery; + +#ifdef CNFGOGL +void CNFGFlushRender() +{ +} +#endif + +void CNFGInternalResize( short x, short y ) +{ + CNFGBufferx = x; + CNFGBuffery = y; + if( CNFGBuffer ) free( CNFGBuffer ); + CNFGBuffer = malloc( CNFGBufferx * CNFGBuffery * 4 ); +#ifdef CNFGOGL + void CNFGInternalResizeOGLBACKEND( short w, short h ); + CNFGInternalResizeOGLBACKEND( x, y ); +#endif +} + +#ifdef __wasm__ +static uint32_t SWAPS( uint32_t r ) +{ + uint32_t ret = (r&0xFF)<<24; + r>>=8; + ret |= (r&0xff)<<16; + r>>=8; + ret |= (r&0xff)<<8; + r>>=8; + ret |= (r&0xff)<<0; + return ret; +} +#elif !defined(CNFGOGL) +#define SWAPS(x) (x>>8) +#else +static uint32_t SWAPS( uint32_t r ) +{ + uint32_t ret = (r&0xFF)<<16; + r>>=8; + ret |= (r&0xff)<<8; + r>>=8; + ret |= (r&0xff); + r>>=8; + ret |= (r&0xff)<<24; + return ret; +} +#endif +uint32_t CNFGColor( uint32_t RGB ) +{ + CNFGLastColor = SWAPS(RGB); + return CNFGLastColor; +} + +void CNFGTackSegment( short x1, short y1, short x2, short y2 ) +{ + short tx, ty; + //float slope, lp; + float slope; + + short dx = x2 - x1; + short dy = y2 - y1; + + if( !CNFGBuffer ) return; + + if( dx < 0 ) dx = -dx; + if( dy < 0 ) dy = -dy; + + if( dx > dy ) + { + short minx = (x1 < x2)?x1:x2; + short maxx = (x1 < x2)?x2:x1; + short miny = (x1 < x2)?y1:y2; + short maxy = (x1 < x2)?y2:y1; + float thisy = miny; + slope = (float)(maxy-miny) / (float)(maxx-minx); + + for( tx = minx; tx <= maxx; tx++ ) + { + ty = thisy; + if( tx < 0 || ty < 0 || ty >= CNFGBuffery ) continue; + if( tx >= CNFGBufferx ) break; + CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor; + thisy += slope; + } + } + else + { + short minx = (y1 < y2)?x1:x2; + short maxx = (y1 < y2)?x2:x1; + short miny = (y1 < y2)?y1:y2; + short maxy = (y1 < y2)?y2:y1; + float thisx = minx; + slope = (float)(maxx-minx) / (float)(maxy-miny); + + for( ty = miny; ty <= maxy; ty++ ) + { + tx = thisx; + if( ty < 0 || tx < 0 || tx >= CNFGBufferx ) continue; + if( ty >= CNFGBuffery ) break; + CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor; + thisx += slope; + } + } +} +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ) +{ + short minx = (x1=x2)?x1:x2; + short maxy = (y1>=y2)?y1:y2; + + short x, y; + + if( minx < 0 ) minx = 0; + if( miny < 0 ) miny = 0; + if( maxx >= CNFGBufferx ) maxx = CNFGBufferx-1; + if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1; + + for( y = miny; y <= maxy; y++ ) + { + uint32_t * CNFGBufferstart = &CNFGBuffer[y * CNFGBufferx + minx]; + for( x = minx; x <= maxx; x++ ) + { + (*CNFGBufferstart++) = CNFGLastColor; + } + } +} + +void CNFGTackPoly( RDPoint * points, int verts ) +{ + short minx = 10000, miny = 10000; + short maxx =-10000, maxy =-10000; + short i, x, y; + + //Just in case... + if( verts > 32767 ) return; + + for( i = 0; i < verts; i++ ) + { + RDPoint * p = &points[i]; + if( p->x < minx ) minx = p->x; + if( p->y < miny ) miny = p->y; + if( p->x > maxx ) maxx = p->x; + if( p->y > maxy ) maxy = p->y; + } + + if( miny < 0 ) miny = 0; + if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1; + + for( y = miny; y <= maxy; y++ ) + { + short startfillx = maxx; + short endfillx = minx; + + //Figure out what line segments intersect this line. + for( i = 0; i < verts; i++ ) + { + short pl = i + 1; + if( pl == verts ) pl = 0; + + RDPoint ptop; + RDPoint pbot; + + ptop.x = points[i].x; + ptop.y = points[i].y; + pbot.x = points[pl].x; + pbot.y = points[pl].y; +//printf( "Poly: %d %d\n", pbot.y, ptop.y ); + + if( pbot.y < ptop.y ) + { + RDPoint ptmp; + ptmp.x = pbot.x; + ptmp.y = pbot.y; + pbot.x = ptop.x; + pbot.y = ptop.y; + ptop.x = ptmp.x; + ptop.y = ptmp.y; + } + + //Make sure this line segment is within our range. +//printf( "PT: %d %d %d\n", y, ptop.y, pbot.y ); + if( ptop.y <= y && pbot.y >= y ) + { + short diffy = pbot.y - ptop.y; + uint32_t placey = (uint32_t)(y - ptop.y)<<16; //Scale by 16 so we can do integer math. + short diffx = pbot.x - ptop.x; + short isectx; + + if( diffy == 0 ) + { + if( pbot.x < ptop.x ) + { + if( startfillx > pbot.x ) startfillx = pbot.x; + if( endfillx < ptop.x ) endfillx = ptop.x; + } + else + { + if( startfillx > ptop.x ) startfillx = ptop.x; + if( endfillx < pbot.x ) endfillx = pbot.x; + } + } + else + { + //Inner part is scaled by 65536, outer part must be scaled back. + isectx = (( (placey / diffy) * diffx + 32768 )>>16) + ptop.x; + if( isectx < startfillx ) startfillx = isectx; + if( isectx > endfillx ) endfillx = isectx; + } +//printf( "R: %d %d %d\n", pbot.x, ptop.x, isectx ); + } + } + +//printf( "%d %d %d\n", y, startfillx, endfillx ); + + if( endfillx >= CNFGBufferx ) endfillx = CNFGBufferx - 1; + if( endfillx >= CNFGBufferx ) endfillx = CNFGBuffery - 1; + if( startfillx < 0 ) startfillx = 0; + if( startfillx < 0 ) startfillx = 0; + + unsigned int * bufferstart = &CNFGBuffer[y * CNFGBufferx + startfillx]; + for( x = startfillx; x <= endfillx; x++ ) + { + (*bufferstart++) = CNFGLastColor; + } + } +//exit(1); +} + + +void CNFGClearFrame() +{ + int i, m; + uint32_t col = 0; + short x, y; + CNFGGetDimensions( &x, &y ); + if( x != CNFGBufferx || y != CNFGBuffery || !CNFGBuffer ) + { + CNFGBufferx = x; + CNFGBuffery = y; + CNFGBuffer = malloc( x * y * 8 ); + } + + m = x * y; + col = CNFGColor( CNFGBGColor ); + for( i = 0; i < m; i++ ) + { + CNFGBuffer[i] = col; + } +} + +void CNFGTackPixel( short x, short y ) +{ + if( x < 0 || y < 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return; + CNFGBuffer[x+CNFGBufferx*y] = CNFGLastColor; +} + + +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ) +{ + int ox = x; + int stride = w; + if( w <= 0 || h <= 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return; + if( x < 0 ) { w += x; x = 0; } + if( y < 0 ) { h += y; y = 0; } + + //Switch w,h to x2, y2 + h += y; + w += x; + + if( w >= CNFGBufferx ) { w = CNFGBufferx; } + if( h >= CNFGBuffery ) { h = CNFGBuffery; } + + + for( ; y < h-1; y++ ) + { + x = ox; + uint32_t * indat = data; + uint32_t * outdat = CNFGBuffer + y * CNFGBufferx + x; + for( ; x < w-1; x++ ) + { + uint32_t newm = *(indat++); + uint32_t oldm = *(outdat); + if( (newm & 0xff) == 0xff ) + { + *(outdat++) = newm; + } + else + { + //Alpha blend. + int alfa = newm&0xff; + int onemalfa = 255-alfa; +#ifdef __wasm__ + uint32_t newv = 255<<0; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; +#elif defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) + uint32_t newv = 255UL<<24; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; +#elif defined( ANDROID ) || defined( __android__ ) + uint32_t newv = 255<<16; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; +#elif defined( CNFGOGL ) //OGL, on X11 + uint32_t newv = 255<<16; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; +#else //X11 + uint32_t newv = 255UL<<24; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; +#endif + *(outdat++) = newv; + } + } + data += stride; + } +} + +void CNFGSwapBuffers() +{ + CNFGUpdateScreenWithBitmap( (uint32_t*)CNFGBuffer, CNFGBufferx, CNFGBuffery ); +} + + +#endif diff --git a/app/src/nativecode/rawdraw/CNFGWASMDriver.c b/app/src/nativecode/rawdraw/CNFGWASMDriver.c new file mode 100644 index 0000000..4a2dcff --- /dev/null +++ b/app/src/nativecode/rawdraw/CNFGWASMDriver.c @@ -0,0 +1,84 @@ +//Right now, designed for use with https://github.com/cnlohr/rawdrawwasm/ +#include +#include + +extern void __attribute__((import_module("bynsyncify"))) CNFGSwapBuffersInternal(); +void CNFGBlitImageInternal( uint32_t * data, int x, int y, int w, int h ); +void print( double idebug ); +void prints( const char* sdebug ); + + +//Forward declarations that we get from either WASM or our javascript code. +void CNFGClearFrameInternal( uint32_t bgcolor ); + +//The WASM driver handles internal resizing automatically. +#ifndef CNFGRASTERIZER + +void CNFGInternalResize( short x, short y ) +{ +} + +void CNFGFlushRender() +{ + if( !CNFGVertPlace ) return; + CNFGEmitBackendTriangles( CNFGVertDataV, CNFGVertDataC, CNFGVertPlace ); + CNFGVertPlace = 0; +} +void CNFGClearFrame() +{ + CNFGFlushRender(); + CNFGClearFrameInternal( CNFGBGColor ); +} +void CNFGSwapBuffers() +{ + CNFGFlushRender(); + CNFGSwapBuffersInternal( ); +} + +int CNFGHandleInput() +{ + //Do nothing. + //Input is handled on swap frame. + return 1; +} + +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ) +{ + CNFGBlitImageInternal( data, x, y, w, h ); +} + +#else + +//Rasterizer - if you want to do this, you will need to enable blitting in the javascript. +//XXX TODO: NEED MEMORY ALLOCATOR +extern unsigned char __heap_base; +unsigned int bump_pointer = (unsigned int)&__heap_base; +void* malloc(unsigned long size) { + unsigned int ptr = bump_pointer; + bump_pointer += size; + return (void *)ptr; +} +void free(void* ptr) { } + +#include "CNFGRasterizer.c" + +extern void CNFGUpdateScreenWithBitmapInternal( uint32_t * data, int w, int h ); +void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h ) +{ + CNFGBlitImageInternal( data, 0, 0, w, h ); + CNFGSwapBuffersInternal(); +} + + +void CNFGSetLineWidth( short width ) +{ + //Rasterizer does not support line width. +} + +void CNFGHandleInput() +{ + //Do nothing. + //Input is handled on swap frame. +} + +#endif diff --git a/app/src/nativecode/rawdraw/CNFGWinDriver.c b/app/src/nativecode/rawdraw/CNFGWinDriver.c new file mode 100644 index 0000000..9a82987 --- /dev/null +++ b/app/src/nativecode/rawdraw/CNFGWinDriver.c @@ -0,0 +1,681 @@ +//Copyright (c) 2011-2019 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose. +//Portion from: http://en.wikibooks.org/wiki/Windows_Programming/Window_Creation + +#ifndef _CNFGWINDRIVER_C +#define _CNFGWINDRIVER_C + +#include "CNFG.h" +#include +#include +#include + +HBITMAP CNFGlsBitmap; +HWND CNFGlsHWND; +HDC CNFGlsWindowHDC; +HDC CNFGlsHDC; +HDC CNFGlsHDCBlit; + +int ShouldClose = 0; + +//Queue up lines and points for a faster render. +#ifndef CNFG_WINDOWS_DISABLE_BATCH +#define BATCH_ELEMENTS +#endif + +#define COLORSWAPS( RGB ) \ + ((((RGB )& 0xFF000000)>>24) | ( ((RGB )& 0xFF0000 ) >> 8 ) | ( ((RGB )& 0xFF00 )<<8 )) + + +void CNFGChangeWindowTitle( const char * windowtitle ) +{ + SetWindowTextA( CNFGlsHWND, windowtitle ); +} + +#ifdef CNFGRASTERIZER +#include "CNFGRasterizer.c" + +void InternalHandleResize() +{ + if( CNFGlsBitmap ) DeleteObject( CNFGlsBitmap ); + + CNFGInternalResize( CNFGBufferx, CNFGBuffery ); + CNFGlsBitmap = CreateBitmap( CNFGBufferx, CNFGBuffery, 1, 32, CNFGBuffer ); + SelectObject( CNFGlsHDC, CNFGlsBitmap ); + CNFGInternalResize( CNFGBufferx, CNFGBuffery); +} +#else +static short CNFGBufferx, CNFGBuffery; +static void InternalHandleResize(); +#endif + + +#ifdef CNFGOGL +#include +static HGLRC hRC=NULL; +static void InternalHandleResize() { } +void CNFGSwapBuffers() +{ +#ifdef CNFG_BATCH +#ifndef CNFGCONTEXTONLY + CNFGFlushRender(); +#endif +#endif + + SwapBuffers(CNFGlsWindowHDC); +} +#endif + +CNFGCursorShape CNFGCurShape = CNFG_CURSOR_ARROW; + +void CNFGSetMousePosition( int x, int y ) { + RECT window; + + int bordersize = GetSystemMetrics( SM_CXSIZEFRAME ); + + GetWindowRect( CNFGlsHWND, &window ); + x += window.left + bordersize; + y += window.top + GetSystemMetrics( SM_CYCAPTION ) + bordersize; + + SetCursorPos( x, y ); +} + +void CNFGConfineMouse( int confined ) { + if ( !confined ) { + ClipCursor( NULL ); + return; + } + + int bordersize = GetSystemMetrics( SM_CXSIZEFRAME ); + RECT window; + GetWindowRect( CNFGlsHWND, &window ); + window.left += bordersize; + window.top += GetSystemMetrics( SM_CYCAPTION ) + bordersize; + window.right -= bordersize; + window.bottom -= bordersize; + + ClipCursor( &window ); +} + +void CNFGSetCursor( CNFGCursorShape shape ) { + if (shape == CNFGCurShape ) return; + + // If the current shape's visibility is the opposite of the new shape, toggle the cursor visibility + // According to MSDN, ShowCursor increments and decrements an internal counter, which is used to determine visibility + // The internal counter seemingly has no limit + if ( shape == CNFG_CURSOR_HIDDEN && CNFGCurShape != CNFG_CURSOR_HIDDEN ) ShowCursor( FALSE ); + else if ( shape != CNFG_CURSOR_HIDDEN && CNFGCurShape == CNFG_CURSOR_HIDDEN ) ShowCursor( TRUE ); + + CNFGCurShape = shape; +} + +void CNFGGetDimensions( short * x, short * y ) +{ + static short lastx, lasty; + RECT window; + GetClientRect( CNFGlsHWND, &window ); + CNFGBufferx = (short)( window.right - window.left); + CNFGBuffery = (short)( window.bottom - window.top); + if( CNFGBufferx != lastx || CNFGBuffery != lasty ) + { + lastx = CNFGBufferx; + lasty = CNFGBuffery; + #ifndef CNFGCONTEXTONLY + CNFGInternalResize( lastx, lasty ); + #endif + InternalHandleResize(); + } + *x = CNFGBufferx; + *y = CNFGBuffery; +} + +#ifndef CNFGOGL +void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h ) +{ + RECT r; + + SelectObject( CNFGlsHDC, CNFGlsBitmap ); + SetBitmapBits(CNFGlsBitmap,w*h*4,data); + BitBlt(CNFGlsWindowHDC, 0, 0, w, h, CNFGlsHDC, 0, 0, SRCCOPY); + UpdateWindow( CNFGlsHWND ); + + short thisw, thish; + + //Check to see if the window is closed. + if( !IsWindow( CNFGlsHWND ) ) + { + exit( 0 ); + } + + GetClientRect( CNFGlsHWND, &r ); + thisw = (short)(r.right - r.left); + thish = (short)(r.bottom - r.top); + if( thisw != CNFGBufferx || thish != CNFGBuffery ) + { + CNFGBufferx = thisw; + CNFGBuffery = thish; + InternalHandleResize(); + } +} +#endif + +void CNFGTearDown() +{ + PostQuitMessage(0); + ShouldClose = 1; +} + +//This was from the article +LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) + { +#ifndef CNFGOGL + case WM_SYSCOMMAND: //Not sure why, if deactivated, the dc gets unassociated? + if( wParam == SC_RESTORE || wParam == SC_MAXIMIZE || wParam == SC_SCREENSAVE ) + { + SelectObject( CNFGlsHDC, CNFGlsBitmap ); + SelectObject( CNFGlsWindowHDC, CNFGlsBitmap ); + } + break; +#endif + case WM_KILLFOCUS: + case WM_SETFOCUS: + CNFGLastScancode = 0; + HandleKey( CNFG_KEY_FOCUS, msg == WM_SETFOCUS ); + return 0; + case WM_CLOSE: + if( HandleDestroy() ) + return 0; + break; + case WM_DESTROY: + CNFGTearDown(); + return 0; + } + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +int CNFGSetupWinInternal( const char * name_of_window, int width, int height, int isFullscreen ); + +void CNFGSetupFullscreen( const char * WindowName, int screen_number ) +{ + // Get primary monitor dimensions, but default to 1920x1080 + int monitorW = GetSystemMetrics(SM_CXSCREEN); + if(0 == monitorW) + { + monitorW = 1920; + } + + int monitorH = GetSystemMetrics(SM_CYSCREEN); + if(0 == monitorH) + { + monitorH = 1080; + } + + CNFGSetupWinInternal(WindowName, monitorW, monitorH, 1); +} + +int CNFGSetup( const char * name_of_window, int width, int height ) +{ + return CNFGSetupWinInternal(name_of_window, width, height, 0); +} + +//This was from the article, too... well, mostly. +int CNFGSetupWinInternal( const char * name_of_window, int width, int height, int isFullscreen ) +{ + static LPCSTR szClassName = "MyClass"; + RECT client, window; + WNDCLASSA wnd; + int w, h, wd, hd; + int show_window = 1; + HINSTANCE hInstance = GetModuleHandle(NULL); + + if( width < 0 ) + { + show_window = 0; + width = -width; + } + if( height < 0 ) + { + show_window = 0; + height = -height; + } + + CNFGBufferx = (short)width; + CNFGBuffery = (short)height; + + wnd.style = CS_HREDRAW | CS_VREDRAW; //we will explain this later + wnd.lpfnWndProc = MyWndProc; + wnd.cbClsExtra = 0; + wnd.cbWndExtra = 0; + wnd.hInstance = hInstance; + wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION); //default icon + wnd.hCursor = LoadCursor(NULL, IDC_ARROW); //default arrow mouse cursor + wnd.hbrBackground = (HBRUSH)(COLOR_BACKGROUND); + wnd.lpszMenuName = NULL; //no menu + wnd.lpszClassName = szClassName; + + if(!RegisterClassA(&wnd)) //register the WNDCLASS + { + MessageBoxA(NULL, "This Program Requires Windows NT", "Error", MB_OK); + } + +#ifdef UNICODE + // CreateWindowA **requires** unicode window name even in non-unicode mode. + int wlen = strlen( name_of_window ); + char * unicodeao = (char*)alloca( wlen * 2 + 2 ); + int i; + for( i = 0; i <= wlen; i++ ) + { + unicodeao[i * 2 + 1] = 0; + unicodeao[i * 2 + 0] = name_of_window[i]; + } + name_of_window = unicodeao; +#endif + + + CNFGlsHWND = CreateWindowA(szClassName, + name_of_window, //name_of_window, but must always be + isFullscreen ? (WS_MAXIMIZE | WS_POPUP) : (WS_OVERLAPPEDWINDOW), //basic window style + CW_USEDEFAULT, + CW_USEDEFAULT, //set starting point to default value + CNFGBufferx, + CNFGBuffery, //set all the dimensions to default value + NULL, //no parent window + NULL, //no menu + hInstance, + NULL); //no parameters to pass + + CNFGlsWindowHDC = GetDC( CNFGlsHWND ); + +#ifdef CNFGOGL + //From NeHe + static PIXELFORMATDESCRIPTOR pfd = + { + sizeof(PIXELFORMATDESCRIPTOR), + 1, + PFD_DRAW_TO_WINDOW | + PFD_SUPPORT_OPENGL | + PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, + 24, + 8, 0, 8, 8, 8, 16, + 8, + 24, + 32, + 8, 8, 8, 8, + 16, + 0, + 0, + PFD_MAIN_PLANE, + 0, + 0, 0, 0 + }; + GLuint PixelFormat = ChoosePixelFormat( CNFGlsWindowHDC, &pfd ); + if( !SetPixelFormat( CNFGlsWindowHDC, PixelFormat, &pfd ) ) + { + MessageBoxA( 0, "Could not create PFD for OpenGL Context\n", 0, 0 ); + exit( -1 ); + } + if (!(hRC=wglCreateContext(CNFGlsWindowHDC))) // Are We Able To Get A Rendering Context? + { + MessageBoxA( 0, "Could not create OpenGL Context\n", 0, 0 ); + exit( -1 ); + } + if(!wglMakeCurrent(CNFGlsWindowHDC,hRC)) // Try To Activate The Rendering Context + { + MessageBoxA( 0, "Could not current OpenGL Context\n", 0, 0 ); + exit( -1 ); + } +#endif + + CNFGlsHDC = CreateCompatibleDC( CNFGlsWindowHDC ); + CNFGlsHDCBlit = CreateCompatibleDC( CNFGlsWindowHDC ); + CNFGlsBitmap = CreateCompatibleBitmap( CNFGlsWindowHDC, CNFGBufferx, CNFGBuffery ); + SelectObject( CNFGlsHDC, CNFGlsBitmap ); + + //lsClearBrush = CreateSolidBrush( CNFGBGColor ); + //lsHBR = CreateSolidBrush( 0xFFFFFF ); + //lsHPEN = CreatePen( PS_SOLID, 0, 0xFFFFFF ); + + if( show_window ) + ShowWindow(CNFGlsHWND, isFullscreen ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); //display the window on the screen + + //Once set up... we have to change the window's borders so we get the client size right. + GetClientRect( CNFGlsHWND, &client ); + GetWindowRect( CNFGlsHWND, &window ); + w = ( window.right - window.left); + h = ( window.bottom - window.top); + wd = w - client.right; + hd = h - client.bottom; + MoveWindow( CNFGlsHWND, window.left, window.top, CNFGBufferx + wd, CNFGBuffery + hd, 1 ); + + InternalHandleResize(); + +#ifdef CNFG_BATCH +#ifndef CNFGCONTEXTONLY + CNFGSetupBatchInternal(); +#endif +#endif + + ShouldClose = 0; + + return 0; +} + + + +int CNFGHandleInput() +{ +#ifdef CNFGOGL + if (ShouldClose) + exit(0); +#endif + + MSG msg; + while( PeekMessage( &msg, NULL, 0, 0xFFFF, 1 ) ) + { + TranslateMessage(&msg); + MSG charMSG; + + switch( msg.message ) + { + case WM_MOUSEMOVE: + HandleMotion( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, ( (msg.wParam & 0x01)?1:0) | ((msg.wParam & 0x02)?2:0) | ((msg.wParam & 0x10)?4:0) ); + break; + case WM_LBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 1, 1 ); break; + case WM_RBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 2, 1 ); break; + case WM_MBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 3, 1 ); break; + case WM_LBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 1, 0 ); break; + case WM_RBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 2, 0 ); break; + case WM_MBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 3, 0 ); break; + case WM_KEYDOWN: + // Check if there is a WM_CHAR message in the queue. If there is one, put it into CNFGLastCharacter + // Otherwise, we don't want HandleKey to handle the wrong character, so set to 0 + if (PeekMessage(&charMSG, NULL, WM_CHAR, WM_CHAR, PM_REMOVE)) CNFGLastCharacter = charMSG.wParam; + else CNFGLastCharacter = 0; + + // fall through + case WM_KEYUP: + CNFGLastScancode = (msg.lParam >> 16) & 0xFF; + + if (msg.lParam & 0x01000000) HandleKey( (int) msg.wParam + 0x7C , (msg.message==WM_KEYDOWN) ); + else HandleKey( (int) msg.wParam, (msg.message==WM_KEYDOWN) ); + + // Don't confuse the program when CNFGLastCharacter is set on WM_KEYUP. + // Since we shouldn't be using CNFGLastCharacter outside HandleKey anyways, just clear it here. + CNFGLastCharacter = 0; + break; + case WM_MOUSEWHEEL: + { + POINT p = { 0 }; + p.x = LOWORD( msg.lParam ); + p.y = HIWORD( msg.lParam ); + ScreenToClient(CNFGlsHWND, &p); + HandleButton(p.x, p.y, GET_WHEEL_DELTA_WPARAM(msg.wParam) > 0 ? 0x0E : 0x0F, 1); + } break; + default: + DispatchMessage(&msg); + break; + } + } + + return !ShouldClose; +} + +#ifndef CNFGOGL + +#ifndef CNFGRASTERIZER + +static HBITMAP lsBackBitmap; +static HBRUSH lsHBR; +static HPEN lsHPEN; +static HBRUSH lsClearBrush; + +static void InternalHandleResize() +{ + DeleteObject( lsBackBitmap ); + lsBackBitmap = CreateCompatibleBitmap( CNFGlsHDC, CNFGBufferx, CNFGBuffery ); + SelectObject( CNFGlsHDC, lsBackBitmap ); +} + +#ifdef BATCH_ELEMENTS + +static int linelisthead; +static int pointlisthead; +static int polylisthead; +static int polylistindex; +static POINT linelist[4096*3]; +static DWORD twoarray[4096]; +static POINT pointlist[4096]; +static POINT polylist[8192]; +static INT polylistcutoffs[8192]; + + +static int last_linex; +static int last_liney; +static int possible_lastline; + +void FlushTacking() +{ + int i; + + if( twoarray[0] != 2 ) + for( i = 0; i < 4096; i++ ) twoarray[i] = 2; + + if( linelisthead ) + { + PolyPolyline( CNFGlsHDC, linelist, twoarray, linelisthead ); + linelisthead = 0; + } + + if( polylistindex ) + { + PolyPolygon( CNFGlsHDC, polylist, polylistcutoffs, polylistindex ); + polylistindex = 0; + polylisthead = 0; + } + + if( possible_lastline ) + CNFGTackPixel( (short)last_linex, (short)last_liney ); + possible_lastline = 0; + + //XXX TODO: Consider locking the bitmap, and manually drawing the pixels. + if( pointlisthead ) + { + for( i = 0; i < pointlisthead; i++ ) + { + SetPixel( CNFGlsHDC, pointlist[i].x, pointlist[i].y, CNFGLastColor ); + } + pointlisthead = 0; + } +} +#endif + +uint32_t CNFGColor( uint32_t RGB ) +{ + RGB = COLORSWAPS( RGB ); + if( CNFGLastColor == RGB ) return RGB; + +#ifdef BATCH_ELEMENTS + FlushTacking(); +#endif + + CNFGLastColor = RGB; + + DeleteObject( lsHBR ); + lsHBR = CreateSolidBrush( RGB ); + SelectObject( CNFGlsHDC, lsHBR ); + + DeleteObject( lsHPEN ); + lsHPEN = CreatePen( PS_SOLID, 0, RGB ); + SelectObject( CNFGlsHDC, lsHPEN ); + + return RGB; +} + +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ) +{ + static int pbw, pbh; + static HBITMAP pbb; + if( !pbb || pbw != w || pbh !=h ) + { + if( pbb ) DeleteObject( pbb ); + pbb = CreateBitmap( w, h, 1, 32, 0 ); + pbh = h; + pbw = w; + } + SetBitmapBits(pbb,w*h*4,data); + SelectObject( CNFGlsHDCBlit, pbb ); + BitBlt(CNFGlsHDC, x, y, w, h, CNFGlsHDCBlit, 0, 0, SRCCOPY); +} + +void CNFGTackSegment( short x1, short y1, short x2, short y2 ) +{ +#ifdef BATCH_ELEMENTS + + if( ( x1 != last_linex || y1 != last_liney ) && possible_lastline ) + { + CNFGTackPixel( (short)last_linex, (short)last_liney ); + } + + if( x1 == x2 && y1 == y2 ) + { + CNFGTackPixel( x1, y1 ); + possible_lastline = 0; + return; + } + + last_linex = x2; + last_liney = y2; + possible_lastline = 1; + + if( x1 != x2 || y1 != y2 ) + { + linelist[linelisthead*2+0].x = x1; + linelist[linelisthead*2+0].y = y1; + linelist[linelisthead*2+1].x = x2; + linelist[linelisthead*2+1].y = y2; + linelisthead++; + if( linelisthead >= 2048 ) FlushTacking(); + } +#else + POINT pt[2] = { {x1, y1}, {x2, y2} }; + Polyline( CNFGlsHDC, pt, 2 ); + SetPixel( CNFGlsHDC, x1, y1, CNFGLastColor ); + SetPixel( CNFGlsHDC, x2, y2, CNFGLastColor ); +#endif +} + +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ) +{ +#ifdef BATCH_ELEMENTS + FlushTacking(); +#endif + RECT r; + if( x1 < x2 ) { r.left = x1; r.right = x2; } + else { r.left = x2; r.right = x1; } + if( y1 < y2 ) { r.top = y1; r.bottom = y2; } + else { r.top = y2; r.bottom = y1; } + FillRect( CNFGlsHDC, &r, lsHBR ); +} + +void CNFGClearFrame() +{ +#ifdef BATCH_ELEMENTS + FlushTacking(); +#endif + RECT r = { 0, 0, CNFGBufferx, CNFGBuffery }; + DeleteObject( lsClearBrush ); + lsClearBrush = CreateSolidBrush( COLORSWAPS(CNFGBGColor) ); + HBRUSH prevBrush = (HBRUSH)SelectObject( CNFGlsHDC, lsClearBrush ); + FillRect( CNFGlsHDC, &r, lsClearBrush); + SelectObject( CNFGlsHDC, prevBrush ); +} + +void CNFGTackPoly( RDPoint * points, int verts ) +{ +#ifdef BATCH_ELEMENTS + if( verts > 8192 ) + { + FlushTacking(); + //Fall-through + } + else + { + if( polylistindex >= 8191 || polylisthead + verts >= 8191 ) + { + FlushTacking(); + } + int i; + for( i = 0; i < verts; i++ ) + { + polylist[polylisthead].x = points[i].x; + polylist[polylisthead].y = points[i].y; + polylisthead++; + } + polylistcutoffs[polylistindex++] = verts; + return; + } +#endif + { + int i; + POINT * t = (POINT*)alloca( sizeof( POINT ) * verts ); + for( i = 0; i < verts; i++ ) + { + t[i].x = points[i].x; + t[i].y = points[i].y; + } + Polygon( CNFGlsHDC, t, verts ); + } +} + + +void CNFGTackPixel( short x1, short y1 ) +{ +#ifdef BATCH_ELEMENTS + pointlist[pointlisthead+0].x = x1; + pointlist[pointlisthead+0].y = y1; + pointlisthead++; + + if( pointlisthead >=4096 ) FlushTacking(); +#else + SetPixel( CNFGlsHDC, x1, y1, CNFGLastColor ); +#endif + +} + +void CNFGSwapBuffers() +{ +#ifdef BATCH_ELEMENTS + FlushTacking(); +#endif + int thisw, thish; + + RECT r; + BitBlt( CNFGlsWindowHDC, 0, 0, CNFGBufferx, CNFGBuffery, CNFGlsHDC, 0, 0, SRCCOPY ); + UpdateWindow( CNFGlsHWND ); + //Check to see if the window is closed. + if( !IsWindow( CNFGlsHWND ) ) + { + exit( 0 ); + } + + GetClientRect( CNFGlsHWND, &r ); + thisw = r.right - r.left; + thish = r.bottom - r.top; + + if( thisw != CNFGBufferx || thish != CNFGBuffery ) + { + CNFGBufferx = (short)thisw; + CNFGBuffery = (short)thish; + InternalHandleResize(); + } +} + +void CNFGInternalResize( short bfx, short bfy ) { } +#endif + +#endif + +#endif // _CNFGWINDRIVER_C + diff --git a/app/src/nativecode/rawdraw/CNFGXDriver.c b/app/src/nativecode/rawdraw/CNFGXDriver.c new file mode 100644 index 0000000..1ca4b33 --- /dev/null +++ b/app/src/nativecode/rawdraw/CNFGXDriver.c @@ -0,0 +1,684 @@ +//Copyright (c) 2011, 2017, 2018 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose. +//portions from +//http://www.xmission.com/~georgeps/documentation/tutorials/Xlib_Beginner.html + +//#define HAS_XINERAMA +//#define CNFG_HAS_XSHAPE +//#define FULL_SCREEN_STEAL_FOCUS + +#ifndef _CNFGXDRIVER_C +#define _CNFGXDRIVER_C + +#include "CNFG.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef HAS_XINERAMA + #include + #include +#endif +#ifdef CNFG_HAS_XSHAPE + #include + static XGCValues xsval; + static Pixmap xspixmap; + static GC xsgc; + + static int taint_shape; + static int prepare_xshape; + static int was_transp; + +#endif + +#ifdef CNFG_BATCH +void CNFGSetupBatchInternal(); +#endif + +XWindowAttributes CNFGWinAtt; +XClassHint *CNFGClassHint; +char * wm_res_name = 0; +char * wm_res_class = 0; +Display *CNFGDisplay; +Atom CFNGWMDeleteWindow; +Window CNFGWindow; +int CNFGWindowInvisible; +Pixmap CNFGPixmap; +GC CNFGGC; +GC CNFGWindowGC; +int CNFGDepth; +int CNFGScreen; +Visual * CNFGVisual; +VisualID CNFGVisualID; + +#ifdef CNFGOGL +#include +#include + +GLXContext CNFGCtx; +void * CNFGGetExtension( const char * extname ) { return (void*)glXGetProcAddressARB((const GLubyte *) extname); } +GLXFBConfig CNFGGLXFBConfig; +GLXFBConfig* CNFGGLXFBConfigs; + + +void CNFGGLXSetup( ) +{ + int attribs[] = { + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DOUBLEBUFFER, True, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DEPTH_SIZE, 1, + None }; + int elements = 0; + GLXFBConfig * cfgs = glXChooseFBConfig( CNFGDisplay, CNFGScreen, attribs, &elements ); + if( elements == 0 ) + { + fprintf( stderr, "Error: could not get valid GLXFBConfig visual.\n" ); + exit( -1 ); + } + CNFGGLXFBConfigs = cfgs; + CNFGGLXFBConfig = cfgs[0]; + XVisualInfo * vis = glXGetVisualFromFBConfig( CNFGDisplay, CNFGGLXFBConfig ); + CNFGVisual = vis->visual; + CNFGVisualID = vis->visualid; + CNFGDepth = vis->depth; + CNFGCtx = glXCreateContext( CNFGDisplay, vis, NULL, True ); + XFree( vis ); +} + +#endif + +int CNFGX11ForceNoDecoration; +XImage *xi; + +XIM CNFGXIM = NULL; +XIC CNFGXIC = NULL; + +int g_x_global_key_state; +int g_x_global_shift_key; + +void CNFGSetWindowIconData( int w, int h, uint32_t * data ) +{ + static Atom net_wm_icon; + static Atom cardinal; + + if( !net_wm_icon ) net_wm_icon = XInternAtom( CNFGDisplay, "_NET_WM_ICON", False ); + if( !cardinal ) cardinal = XInternAtom( CNFGDisplay, "CARDINAL", False ); + + unsigned long outdata[w*h]; + int i; + for( i = 0; i < w*h; i++ ) + { + outdata[i+2] = data[i]; + } + outdata[0] = w; + outdata[1] = h; + XChangeProperty(CNFGDisplay, CNFGWindow, net_wm_icon, cardinal, + 32, PropModeReplace, (const unsigned char*)outdata, 2 + w*h); +} + + +#ifdef CNFG_HAS_XSHAPE +void CNFGPrepareForTransparency() { prepare_xshape = 1; } +void CNFGDrawToTransparencyMode( int transp ) +{ + static Pixmap BackupCNFGPixmap; + static GC BackupCNFGGC; + if( was_transp && ! transp ) + { + CNFGGC = BackupCNFGGC; + CNFGPixmap = BackupCNFGPixmap; + } + if( !was_transp && transp ) + { + BackupCNFGPixmap = CNFGPixmap; + BackupCNFGGC = CNFGGC; + taint_shape = 1; + CNFGGC = xsgc; + CNFGPixmap = xspixmap; + } + was_transp = transp; +} +void CNFGClearTransparencyLevel() +{ + taint_shape = 1; + XSetForeground(CNFGDisplay, xsgc, 0); + XFillRectangle(CNFGDisplay, xspixmap, xsgc, 0, 0, CNFGWinAtt.width, CNFGWinAtt.height); + XSetForeground(CNFGDisplay, xsgc, 1); +} +#endif + +void CNFGSetMousePosition( int x, int y ) { + XWarpPointer( CNFGDisplay, None, CNFGWindow, 0, 0, 0, 0, x, y ); +} + +void CNFGConfineMouse( int confined ) { + if (confined) { + XGrabPointer( CNFGDisplay, CNFGWindow, True, 0, GrabModeAsync, GrabModeAsync, CNFGWindow, None, CurrentTime ); + } else { + XUngrabPointer( CNFGDisplay, CurrentTime ); + } +} + +Cursor CNFGCursors[CNFG_CURSOR_LAST] = { None }; + +void CNFGSetCursor( CNFGCursorShape shape ) { + if (shape == CNFG_CURSOR_ARROW) XUndefineCursor( CNFGDisplay, CNFGWindow ); + else XDefineCursor( CNFGDisplay, CNFGWindow, CNFGCursors[shape] ); +} + +int FullScreen = 0; + +void CNFGGetDimensions( short * x, short * y ) +{ + static int lastx; + static int lasty; + + *x = CNFGWinAtt.width; + *y = CNFGWinAtt.height; + + if( lastx != *x || lasty != *y ) + { + lastx = *x; + lasty = *y; +#ifndef CNFGCONTEXTONLY + CNFGInternalResize( lastx, lasty ); +#endif + } +} + +void CNFGChangeWindowTitle( const char * WindowName ) +{ + XSetStandardProperties( CNFGDisplay, CNFGWindow, WindowName, 0, 0, 0, 0, 0 ); +} + +static void InternalLinkScreenAndGo( const char * WindowName ) +{ + XFlush(CNFGDisplay); + XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt ); + + if( !wm_res_name ) wm_res_name = strdup( "rawdraw" ); + if( !wm_res_class ) wm_res_class = strdup( "rawdraw" ); + + XGetClassHint( CNFGDisplay, CNFGWindow, CNFGClassHint ); + if (!CNFGClassHint) { + CNFGClassHint = XAllocClassHint(); + if (CNFGClassHint) { + CNFGClassHint->res_name = wm_res_name; + CNFGClassHint->res_class = wm_res_class; + XSetClassHint( CNFGDisplay, CNFGWindow, CNFGClassHint ); + } else { + fprintf( stderr, "Failed to allocate XClassHint!\n" ); + } + } else { + fprintf( stderr, "Pre-existing XClassHint\n" ); + } + + XSelectInput (CNFGDisplay, CNFGWindow, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | PointerMotionMask | FocusChangeMask ); + + + CNFGWindowGC = XCreateGC(CNFGDisplay, CNFGWindow, 0, 0); + + + if( CNFGX11ForceNoDecoration ) + { + Atom window_type = XInternAtom(CNFGDisplay, "_NET_WM_WINDOW_TYPE", False); + long value = XInternAtom(CNFGDisplay, "_NET_WM_WINDOW_TYPE_SPLASH", False); + XChangeProperty(CNFGDisplay, CNFGWindow, window_type, + XA_ATOM, 32, PropModeReplace, (unsigned char *) &value,1 ); + } + + CNFGPixmap = XCreatePixmap( CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, CNFGWinAtt.depth ); + CNFGGC = XCreateGC(CNFGDisplay, CNFGPixmap, 0, 0); + XSetLineAttributes(CNFGDisplay, CNFGGC, 1, LineSolid, CapRound, JoinRound); + CNFGChangeWindowTitle( WindowName ); + if( !CNFGWindowInvisible ) + XMapWindow(CNFGDisplay, CNFGWindow); + + CNFGXIM = XOpenIM(CNFGDisplay, NULL, wm_res_name, wm_res_class); + CNFGXIC = XCreateIC(CNFGXIM, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, NULL); + +#ifdef CNFG_HAS_XSHAPE + if( prepare_xshape ) + { + xsval.foreground = 1; + xsval.line_width = 1; + xsval.line_style = LineSolid; + xspixmap = XCreatePixmap(CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, 1); + xsgc = XCreateGC(CNFGDisplay, xspixmap, 0, &xsval); + XSetLineAttributes(CNFGDisplay, xsgc, 1, LineSolid, CapRound, JoinRound); + } +#endif +} + +void CNFGSetupFullscreen( const char * WindowName, int screen_no ) +{ +#ifdef HAS_XINERAMA + XineramaScreenInfo *screeninfo = NULL; + int screens; + int event_basep, error_basep, a, b; + CNFGDisplay = XOpenDisplay(NULL); + CNFGScreen = XDefaultScreen(CNFGDisplay); + int xpos, ypos; + + if (!XShapeQueryExtension(CNFGDisplay, &event_basep, &error_basep)) { + fprintf( stderr, "X-Server does not support shape extension\n" ); + exit( 1 ); + } + + CNFGVisual = DefaultVisual(CNFGDisplay, CNFGScreen); + CNFGVisualID = 0; + CNFGWinAtt.depth = DefaultDepth(CNFGDisplay, CNFGScreen); + +#ifdef CNFGOGL + CNFGGLXSetup(); +#endif + + if (XineramaQueryExtension(CNFGDisplay, &a, &b ) && + (screeninfo = XineramaQueryScreens(CNFGDisplay, &screens)) && + XineramaIsActive(CNFGDisplay) && screen_no >= 0 && + screen_no < screens ) { + + CNFGWinAtt.width = screeninfo[screen_no].width; + CNFGWinAtt.height = screeninfo[screen_no].height; + xpos = screeninfo[screen_no].x_org; + ypos = screeninfo[screen_no].y_org; + } else + { + CNFGWinAtt.width = XDisplayWidth(CNFGDisplay, CNFGScreen); + CNFGWinAtt.height = XDisplayHeight(CNFGDisplay, CNFGScreen); + xpos = 0; + ypos = 0; + } + if (screeninfo) + XFree(screeninfo); + + + XSetWindowAttributes setwinattr; + setwinattr.override_redirect = 1; + setwinattr.save_under = 1; +#ifdef CNFG_HAS_XSHAPE + + if (prepare_xshape && !XShapeQueryExtension(CNFGDisplay, &event_basep, &error_basep)) + { + fprintf( stderr, "X-Server does not support shape extension" ); + exit( 1 ); + } + + setwinattr.event_mask = 0; +#else + //This code is probably made irrelevant by the XSetEventMask in InternalLinkScreenAndGo, if this code is not found needed by 2019-12-31, please remove. + //setwinattr.event_mask = StructureNotifyMask | SubstructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | ButtonPressMask | PointerMotionMask | ButtonMotionMask | EnterWindowMask | LeaveWindowMask |KeyPressMask |KeyReleaseMask | SubstructureNotifyMask | FocusChangeMask; +#endif + setwinattr.border_pixel = 0; + setwinattr.colormap = XCreateColormap( CNFGDisplay, RootWindow(CNFGDisplay, 0), CNFGVisual, AllocNone); + + CNFGWindow = XCreateWindow(CNFGDisplay, XRootWindow(CNFGDisplay, CNFGScreen), + xpos, ypos, CNFGWinAtt.width, CNFGWinAtt.height, + 0, CNFGWinAtt.depth, InputOutput, CNFGVisual, + CWBorderPixel/* | CWEventMask */ | CWOverrideRedirect | CWSaveUnder | CWColormap, + &setwinattr); + + FullScreen = 1; + InternalLinkScreenAndGo( WindowName ); +#ifdef CNFGOGL + glXMakeCurrent( CNFGDisplay, CNFGWindow, CNFGCtx ); +#endif +#ifdef CNFG_BATCH +#ifndef CNFGCONTEXTONLY + CNFGSetupBatchInternal(); +#endif +#endif + +#else + CNFGSetup( WindowName, 640, 480 ); +#endif +} + + +void CNFGTearDown() +{ + HandleDestroy(); + if( xi ) free( xi ); + if ( CNFGClassHint ) XFree( CNFGClassHint ); + if ( CNFGGC ) XFreeGC( CNFGDisplay, CNFGGC ); + if ( CNFGWindowGC ) XFreeGC( CNFGDisplay, CNFGWindowGC ); + if ( CNFGDisplay ) XCloseDisplay( CNFGDisplay ); + if ( CNFGXIC ) XDestroyIC( CNFGXIC ); + if ( CNFGXIM ) XCloseIM( CNFGXIM ); + +#ifdef CNFGOGL + if ( CNFGGLXFBConfigs ) XFree( CNFGGLXFBConfigs ); + CNFGGLXFBConfigs = NULL; +#endif + CNFGDisplay = NULL; + CNFGWindowGC = CNFGGC = NULL; + CNFGClassHint = NULL; + CFNGWMDeleteWindow = None; +} + +int CNFGSetupWMClass( const char * WindowName, int w, int h , char * wm_res_name_ , char * wm_res_class_ ) +{ + wm_res_name = wm_res_name_; + wm_res_class = wm_res_class_; + return CNFGSetup( WindowName, w, h); +} + +int CNFGSetup( const char * WindowName, int w, int h ) +{ + CNFGDisplay = XOpenDisplay(NULL); + if ( !CNFGDisplay ) { + fprintf( stderr, "Could not get an X Display.\n%s", + "Are you in text mode or using SSH without X11-Forwarding?\n" ); + exit( 1 ); + } + atexit( CNFGTearDown ); + + CNFGScreen = DefaultScreen(CNFGDisplay); + CNFGDepth = DefaultDepth(CNFGDisplay, CNFGScreen); + CNFGVisual = DefaultVisual(CNFGDisplay, CNFGScreen); + CNFGVisualID = 0; + Window wnd = DefaultRootWindow( CNFGDisplay ); + +#ifdef CNFGOGL + CNFGGLXSetup( ); +#endif + + XSetWindowAttributes attr; + attr.background_pixel = 0; + attr.colormap = XCreateColormap( CNFGDisplay, wnd, CNFGVisual, AllocNone); + if( w > 0 && h > 0 ) + CNFGWindow = XCreateWindow(CNFGDisplay, wnd, 1, 1, w, h, 0, CNFGDepth, InputOutput, CNFGVisual, CWBackPixel | CWColormap, &attr ); + else + { + if( w < 0 ) w = -w; + if( h < 0 ) h = -h; + CNFGWindow = XCreateWindow(CNFGDisplay, wnd, 1, 1, w, h, 0, CNFGDepth, InputOutput, CNFGVisual, CWBackPixel | CWColormap, &attr ); + CNFGWindowInvisible = 1; + } + + InternalLinkScreenAndGo( WindowName ); + +//Not sure of the purpose of this code - if it's still commented out after 2019-12-31 and no one knows why, please delete it. + CFNGWMDeleteWindow = XInternAtom( CNFGDisplay, "WM_DELETE_WINDOW", False ); + XSetWMProtocols( CNFGDisplay, CNFGWindow, &CFNGWMDeleteWindow, 1 ); + + // X11 doesn't have a concept of a hidden cursor. Make a blank cursor as a substitute + XColor col = { 0 }; + Pixmap blank = XCreateBitmapFromData( CNFGDisplay, CNFGWindow, (char*)(&col), 1, 1 ); + CNFGCursors[CNFG_CURSOR_HIDDEN] = XCreatePixmapCursor( CNFGDisplay, blank, blank, &col, &col, 0, 0 ); + XFreePixmap( CNFGDisplay, blank ); + +#ifdef CNFGOGL + glXMakeCurrent( CNFGDisplay, CNFGWindow, CNFGCtx ); +#endif + +#ifdef CNFG_BATCH +#ifndef CNFGCONTEXTONLY + CNFGSetupBatchInternal(); +#endif +#endif + + return 0; +} + +int CNFGHandleInput() +{ + if( !CNFGWindow ) return 0; + static int ButtonsDown; + XEvent report; + + int bKeyDirection = 1; + while( XPending( CNFGDisplay ) ) + { + XNextEvent( CNFGDisplay, &report ); + + bKeyDirection = 1; + switch (report.type) + { + case NoExpose: + break; + case Expose: + XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt ); + if( CNFGPixmap ) XFreePixmap( CNFGDisplay, CNFGPixmap ); + CNFGPixmap = XCreatePixmap( CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, CNFGWinAtt.depth ); + if( CNFGGC ) XFreeGC( CNFGDisplay, CNFGGC ); + CNFGGC = XCreateGC(CNFGDisplay, CNFGPixmap, 0, 0); + + CNFGLastScancode = 0; + HandleKey( CNFG_X11_EXPOSE, 0 ); + break; + case KeyRelease: + { + bKeyDirection = 0; + //Tricky - handle key repeats cleanly. + if( XPending( CNFGDisplay ) ) + { + XEvent nev; + XPeekEvent( CNFGDisplay, &nev ); + if (nev.type == KeyPress && nev.xkey.time == report.xkey.time && nev.xkey.keycode == report.xkey.keycode ) + bKeyDirection = 2; + } + } + case KeyPress: + g_x_global_key_state = report.xkey.state; + g_x_global_shift_key = XLookupKeysym(&report.xkey, 1); + + CNFGLastScancode = report.xkey.keycode; + + // Chars should ONLY be handled on Key Press + if (report.type == KeyPress) { + char buf[8] = {0}; + if (Xutf8LookupString(CNFGXIC, &report.xkey, buf, 8, NULL, NULL)) CNFGLastCharacter = *((int*)buf); + else CNFGLastCharacter = 0; + } + + KeySym sym = XLookupKeysym(&report.xkey, 0); + HandleKey( sym, bKeyDirection ); + + // Don't confuse the program when CNFGLastCharacter is set on KeyRelease. + // Since we shouldn't be using CNFGLastCharacter outside HandleKey anyways, just clear it here. + CNFGLastCharacter = 0; + + break; + case ButtonRelease: + bKeyDirection = 0; + case ButtonPress: + HandleButton( report.xbutton.x, report.xbutton.y, report.xbutton.button, bKeyDirection ); + ButtonsDown = (ButtonsDown & (~(1<>1 ); + break; + case FocusIn: + case FocusOut: + CNFGLastScancode = 0; + HandleKey( CNFG_KEY_FOCUS, report.type == FocusIn ); + break; + case ClientMessage: + if ( report.xclient.data.l[0] == CFNGWMDeleteWindow ) + return 0; + break; + default: + break; + //printf( "Event: %d\n", report.type ); + } + } + return 1; +} + + +#ifdef CNFGOGL + +void CNFGSetVSync( int vson ) +{ + void (*glfn)( int ); + void (*glfnXE)( Display *, GLXDrawable, int ); + glfn = (void (*)( int ))CNFGGetExtension( "glXSwapIntervalMESA" ); if( glfn ) glfn( vson ); + glfn = (void (*)( int ))CNFGGetExtension( "glXSwapIntervalSGI" ); if( glfn ) glfn( vson ); + glfnXE = (void (*)( Display *, GLXDrawable, int ))CNFGGetExtension( "glXSwapIntervalEXT" ); if( glfn ) glfnXE( CNFGDisplay, CNFGWindow, vson ); +} + +#ifdef CNFGRASTERIZER +void CNFGSwapBuffersInternal() +#else +void CNFGSwapBuffers() +#endif +{ + if( CNFGWindowInvisible ) return; + +#ifndef CNFGRASTERIZER +#ifndef CNFGCONTEXTONLY + CNFGFlushRender(); +#endif +#endif + +#ifdef CNFG_HAS_XSHAPE + if( taint_shape ) + { + XShapeCombineMask(CNFGDisplay, CNFGWindow, ShapeBounding, 0, 0, xspixmap, ShapeSet); + taint_shape = 0; + } +#endif //CNFG_HAS_XSHAPE + glXSwapBuffers( CNFGDisplay, CNFGWindow ); + +#ifdef FULL_SCREEN_STEAL_FOCUS + if( FullScreen ) + XSetInputFocus( CNFGDisplay, CNFGWindow, RevertToParent, CurrentTime ); +#endif //FULL_SCREEN_STEAL_FOCUS +} + +#else //CNFGOGL + +#ifndef CNFGRASTERIZER +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ) +{ + static int lw, lh; + + if( lw != w || lh != h || !xi ) + { + if( xi ) free( xi ); + xi = XCreateImage(CNFGDisplay, CNFGVisual, CNFGDepth, ZPixmap, 0, (char*)data, w, h, 32, w*4 ); + lw = w; + lh = h; + } + xi->data = (char*)data; + //Draw image to pixmap (not a screen flip) + XPutImage(CNFGDisplay, CNFGPixmap, CNFGGC, xi, 0, 0, x, y, w, h ); +} +#endif + +void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h ) +{ + static int lw, lh; + + if( lw != w || lh != h || !xi ) + { + if( xi ) free( xi ); + xi = XCreateImage(CNFGDisplay, CNFGVisual, CNFGDepth, ZPixmap, 0, (char*)data, w, h, 32, w*4 ); + lw = w; + lh = h; + } + xi->data = (char*)data; + //Directly write image to screen (effectively a flip) + XPutImage(CNFGDisplay, CNFGWindow, CNFGWindowGC, xi, 0, 0, 0, 0, w, h ); +} + +#endif //CNFGOGL + +#if !defined( CNFGOGL) +#define AGLF(x) x +#else +#define AGLF(x) static inline BACKEND_##x +#endif + +#if defined( CNFGRASTERIZER ) +#include "CNFGRasterizer.c" +#undef AGLF +#define AGLF(x) static inline BACKEND_##x +#endif + +uint32_t AGLF(CNFGColor)( uint32_t RGB ) +{ + CNFGLastColor = RGB; + unsigned char red = ( RGB >> 24 ) & 0xFF; + unsigned char grn = ( RGB >> 16 ) & 0xFF; + unsigned char blu = ( RGB >> 8 ) & 0xFF; + unsigned long color = (red<<16)|(grn<<8)|(blu); + XSetForeground(CNFGDisplay, CNFGGC, color); + return color; +} + +void AGLF(CNFGClearFrame)() +{ + XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt ); + XSetForeground(CNFGDisplay, CNFGGC, CNFGColor(CNFGBGColor) ); + XFillRectangle(CNFGDisplay, CNFGPixmap, CNFGGC, 0, 0, CNFGWinAtt.width, CNFGWinAtt.height ); +} + +void AGLF(CNFGSwapBuffers)() +{ +#ifdef CNFG_HAS_XSHAPE + if( taint_shape ) + { + XShapeCombineMask(CNFGDisplay, CNFGWindow, ShapeBounding, 0, 0, xspixmap, ShapeSet); + taint_shape = 0; + } +#endif + XCopyArea(CNFGDisplay, CNFGPixmap, CNFGWindow, CNFGWindowGC, 0,0,CNFGWinAtt.width,CNFGWinAtt.height,0,0); + XFlush(CNFGDisplay); +#ifdef FULL_SCREEN_STEAL_FOCUS + if( FullScreen ) + XSetInputFocus( CNFGDisplay, CNFGWindow, RevertToParent, CurrentTime ); +#endif +} + +void AGLF(CNFGTackSegment)( short x1, short y1, short x2, short y2 ) +{ + if( x1 == x2 && y1 == y2 ) + { + //On some targets, zero-length lines will not show up. + //This is tricky - since this will also cause more draw calls for points on systems like GLAMOR. + XDrawPoint( CNFGDisplay, CNFGPixmap, CNFGGC, x2, y2 ); + } + else + { + //XXX HACK! See discussion here: https://github.com/cntools/cnping/issues/68 + XDrawLine( CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1, x2, y2 ); + XDrawLine( CNFGDisplay, CNFGPixmap, CNFGGC, x2, y2, x1, y1 ); + } +} + +void AGLF(CNFGTackPixel)( short x1, short y1 ) +{ + XDrawPoint( CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1 ); +} + +void AGLF(CNFGTackRectangle)( short x1, short y1, short x2, short y2 ) +{ + XFillRectangle(CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1, x2-x1, y2-y1 ); +} + +void AGLF(CNFGTackPoly)( RDPoint * points, int verts ) +{ + XFillPolygon(CNFGDisplay, CNFGPixmap, CNFGGC, (XPoint *)points, verts, Convex, CoordModeOrigin ); +} + +void AGLF(CNFGInternalResize)( short x, short y ) { } + +void AGLF(CNFGSetLineWidth)( short width ) +{ + XSetLineAttributes(CNFGDisplay, CNFGGC, width, LineSolid, CapRound, JoinRound); +} + +#endif // _CNFGXDRIVER_C + diff --git a/app/src/nativecode/rawdraw/LICENSE b/app/src/nativecode/rawdraw/LICENSE new file mode 100644 index 0000000..a428635 --- /dev/null +++ b/app/src/nativecode/rawdraw/LICENSE @@ -0,0 +1,23 @@ +The MIT License (MIT) + +Copyright (c) 2010-2020 <>< CNLohr +Copyright (c) 2004-2008 Joshua Allen (portions of CNFG3D) +Copyright (c) 2011-2013 Luc Verhaegen (Android) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/app/src/nativecode/rawdraw/Makefile b/app/src/nativecode/rawdraw/Makefile new file mode 100644 index 0000000..4b9d672 --- /dev/null +++ b/app/src/nativecode/rawdraw/Makefile @@ -0,0 +1,69 @@ +all : rawdraw simple rawdraw_sf.h + +#for X11 consider: xorg-dev +#for X11, you will need: libx-dev +#for full screen you'll need: libxinerama-dev libxext-dev +#for OGL You'll need: mesa-common-dev libglu1-mesa-dev + +#-DCNFGRASTERIZER +# and +#-CNFGOGL +# are incompatible. + + +MINGW32:=/usr/bin/i686-w64-mingw32 + +rawdraw-clang.exe: rawdraw.c + clang rawdraw.c -o rawdraw-clang.exe -g -O1 -Irawdraw -lopengl32 -lgdi32 -luser32 + +rawdrawogl-clang.exe: rawdraw.c + clang rawdraw.c -o rawdrawogl-clang.exe -g -O1 -Irawdraw -DCNFGOGL -lopengl32 -lgdi32 -luser32 + +rawdraw.exe : rawdraw.c + $(MINGW32)gcc -g -m32 -o $@ $^ -lgdi32 + +rawdrawogl.exe : rawdraw.c + $(MINGW32)gcc -g -m32 -o $@ $^ -lgdi32 -DCNFGOGL -lopengl32 + +rawdraw_egl : rawdraw.c + gcc -o $@ $^ -lMali -lpthread -lm -O3 + +simple : simple.c + gcc -o $@ $^ -lX11 -lpthread -lXinerama -lXext -lGL -g -lm -ldl + +rawdraw : rawdraw.c + gcc -o $@ $^ -lX11 -lpthread -lXinerama -lXext -lGL -g -lm -ldl + +rawdraw_ogl : rawdraw.c + gcc -o $@ $^ -lX11 -lpthread -lXinerama -lXext -lGL -g -DCNFGOGL -lm -ldl + +osdtest : osdtest.c CNFG.c + gcc -o $@ $^ -lX11 -lpthread -lXinerama -lXext -DCNFG_HAS_XINERAMA -DCNFG_HAS_XSHAPE -lm -ldl + +ogltest : ogltest.c CNFG.c + gcc -o $@ $^ -lX11 -lXinerama -lGL -DCNFGOGL -lm + +rawdraw_http : rawdraw.c tools/rawdraw_http_page.h + $(CC) -o $@ $< -DCNFGHTTP -lm -ldl -Os -s + +tools/binary_to_buffer : tools/binary_to_buffer.c + $(CC) -o $@ $^ + +tools/rawdraw_http_page.h : tools/rawdraw_http_files/index.html tools/binary_to_buffer + cat $< | gzip -9 | tools/binary_to_buffer webpage_buffer > $@ + +ogltest.exe : ogltest.c CNFG.c + $(MINGW32)gcc -o $@ $^ -lgdi32 -lkernel32 -lopengl32 -DCNFGOGL -lm + +tools/single_file_creator : tools/single_file_creator.c + gcc -o $@ $^ + +rawdraw_sf.h : tools/single_file_creator CNFG.h tools/rawdraw_http_page.h + echo "//This file was automatically generated by Makefile at https://github.com/cntools/rawdraw" > $@ + echo "//This single-file feature is still in early alpha testing." >> $@f + echo "//Generated from files git hash $(shell git rev-parse HEAD) on $(shell date) (This is not the git hash of this file)" >> $@ + ./tools/single_file_creator CNFG.h CNFG.c CNFGWinDriver.c CNFGEGLLeanAndMean.c CNFGRasterizer.c CNFGEGLDriver.c CNFGWASMDriver.c CNFGXDriver.c CNFGFunctions.c CNFG3D.c CNFGAndroid.h CNFGHTTP.c tools/rawdraw_http_page.h >> $@ + +clean : + rm -rf *.o *~ simple simple.exe rawdraw.exe rawdrawogl.exe rawdraw rawdraw_ogl rawdraw_mac rawdraw_mac_soft rawdraw_mac_cg rawdraw_mac_ogl ogltest ogltest.exe rawdraw_egl rawdraw_http rawdraw_sf.h rawdraw_sf.hf tools/single_file_creator tools/binary_to_buffer tools/rawdraw_http_page.h + diff --git a/app/src/nativecode/rawdraw/README.md b/app/src/nativecode/rawdraw/README.md new file mode 100644 index 0000000..0e357b1 --- /dev/null +++ b/app/src/nativecode/rawdraw/README.md @@ -0,0 +1,229 @@ +# CN's Fundamental Graphics Library + +An OS-Agnostic (including No OS AT ALL!) drawing system. It includes lines, +boxes, linetext, points, bitmaps. + +The drawing method can either be by the OS (i.e. SetPixel in Windows), +rasterized in software (we draw our own lines) or by OpenGL where available. +You can reconfigure it with various `#define`s. It also makes it really easy +to get an OpenGL window on just about any supported platform. + +Somewhere unusual and want to open a window and draw stuff? Use rawdraw. + +Other packages too big, bulky or include junk you don't want? Use rawdraw. + +Want to use OpenGL really quickly? Use rawdraw. + +For embedded platforms, `CNFGRASTERIZER` is usually the back end which allows +you to have high level commands on a framebuffer. It's what was used in ESP +Channel 3. You can even play with the rasterizer on every mode except the +Android port by defining it. You can even be twisted on Windows, Linux and +on WASM (Web) and define it. + +This is **not** like SDL - some drivers have different quirks, some have +more features implemented than others, and a major goal is not to abstract +very much, but almost to provide an exmaple how to use graphics on a specific +platform. + +## Platform statuses + + * Windows, 100% Support for CNFGOGL and/or CNFGRASTERIZER, Native (GDI32) does not support alpha-blitting, or variable line width. + * Linux, 100% Support for CNFGOGL and/or CNFGRASTERIZER, Native (X11) does not support alpha-blitting, or variable line thickness +. + * Android, 100% Support for CNFGOGL (It is forced on) CNFGRASTERIZER not allowed. + * WASM, 100% Support for CNFGOGL + * Other EGL (Like Raspberry Pi Raw Metal) Platforms, same as Android, but fixed window size. + * EGL, CNFGOGL represent OpenGL or OpenGL ES depending on platform support. + * Vulkan support in progress. + * CNFG3D Support has been ported with fixed-precision numericals to ESP8266. + * Mac/OSX Support currently unknown. Legacy files moved here: https://github.com/cntools/rawdrawtools/tree/main/attic + +## Usage in project + +You may want to include CNFG as a submodule or as a single-file header. You can just download rawdraw_sf.h to the path your project is in and `#include` it. + +To use it as a single file (rawdraw_sf.h), you can just `wget https://raw.githubusercontent.com/cntools/rawdraw/master/rawdraw_sf.h` + +To use it as a submodule you can: + +`git submodule add https://github.com/cntools/rawdraw` + +paste the command above on your terminal to get a clone of the repository on your system. + +note: Incase you get a error saying +``` +fatal: not a git repository (or any parent up to mount point /) +Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set). +``` +use the command `git init` first. + +To use CNFG, be sure to do this, or include "CNFG.c" in your project. +``` +#define CNFG_IMPLEMENTATION +``` + + +## Usage on Linux +### Prerequisites + +Firstly, make sure you have the necessary packages and libraries installed. + +To install on **Debian/Ubuntu**: +``` +sudo apt-get update +sudo apt-get install xorg-dev libx11-dev libxinerama-dev libxext-dev mesa-common-dev libglu1-mesa-dev +``` +To install on **Arch Linux**: +``` +sudo pacman -Sy xorg-server-devel libx11 libxinerama libxext mesa glu +``` + +### Running + +To run the sample program in rawdraw: +1. Clone the repository on your system and run `cd rawdraw/`. +2. Run the command `make` (compiles and creates an executable called "simple" from "simple.c"). +3. Run the command `./simple` to execute "simple". + + +## Example program +A more comprehensive example can be found in `rawdraw.c`, but a basic example +is as follows: + +```c + + +//Make it so we don't need to include any other C files in our build. +#define CNFG_IMPLEMENTATION + +#include "rawdraw_sf.h" + +void HandleKey( int keycode, int bDown ) { } +void HandleButton( int x, int y, int button, int bDown ) { } +void HandleMotion( int x, int y, int mask ) { } +int HandleDestroy() { return 0; } +int main() +{ + CNFGSetup( "Example App", 1024, 768 ); + while(CNFGHandleInput()) + { + CNFGBGColor = 0x000080ff; //Dark Blue Background + + short w, h; + CNFGClearFrame(); + CNFGGetDimensions( &w, &h ); + + //Change color to white. + CNFGColor( 0xffffffff ); + + CNFGPenX = 1; CNFGPenY = 1; + CNFGDrawText( "Hello, World", 2 ); + //Draw a white pixel at 30, 30 + CNFGTackPixel( 30, 30 ); + + //Draw a line from 50,50 to 100,50 + CNFGTackSegment( 50, 50, 100, 50 ); + + //Dark Red Color Select + CNFGColor( 0x800000FF ); + + //Draw 50x50 box starting at 100,50 + CNFGTackRectangle( 100, 50, 150, 100 ); + + //Bright Purple Select + CNFGColor( 0x800000FF ); + + //Draw a triangle + RDPoint points[3] = { { 30, 36}, {20, 50}, { 40, 50 } }; + CNFGTackPoly( points, 3 ); + + //Draw a bunch of random pixels as a blitted image. + { + static uint32_t data[64*64]; + int x, y; + + for( y = 0; y < 64; y++ ) for( x = 0; x < 64; x++ ) + data[x+y*64] = 0xff | (rand()<<8); + + CNFGBlitImage( data, 150, 30, 64, 64 ); + } + + //Display the image and wait for time to display next frame. + CNFGSwapBuffers(); + } +} +``` + +## Building +Windows compile: +Build with clang +``` +clang simple.c -o simple -Irawdraw -lopengl32 -lgdi32 -luser32 +``` + +Build with TCC +``` +C:\tcc\tcc simple.c -Irawdraw -lopengl32 -lgdi32 -luser32 C:\windows\system32\msvcrt.dll +``` + +Linux compile: +``` +gcc -o simple simple.c -lm -lX11 -lGL +``` + +Note, with the STB-style header, you don't need to +`#define CNFG_IMPLEMENTATION` anywhere in your code, and instead can also +compile in `cnfg.c` + +## Configurations +Rawdraw is a very disjoint set of configurations + +CNFG Configuration options include: +* `CNFG_IMPLEMENTATION` = Include code for implementation. +* `__android__` = build Android port +* `WINDOWS`, `WIN32`, `WIN64` = Windows build +* `CNFGOGL` = Make underlying functions use OpenGL if possible. +* `CNFGRASTERIZER` = Make the underlying graphics engine rasterized functions (software rendering) +* `CNFG3D` = Include CNFG3D with this rawdraw build. This provides *rasterized* graphics functions. Only use this option with the rasterizer. + +Platform-Specific +* `HAS_XSHAPE` = Include extra functions for handling on-screen display functionality in X11. +* `HAS_XINERAMA` = Use X11's Xinerama to handle full-screen more cleanly. + +Flags you will probably never want to use: +* `EGL_LEAN_AND_MEAN` = Bare bones EGL Driver + + +## Extra goodies + +### RDUI + +RDUI is mainted by Overlisted and contains a lot of extra features for rawdraw. You can check it out here: https://git.overlisted.net/me/rdui/ + +### chew.h + +You can copy-and-paste `chew.c` and `chew.h` into your program for a single-file compile-only +version of GLEW. This is WAY BETTER than having to deal with the complicated system they have +set up, but it doesn't include all the functions. You can easily add OpenGL functions to it. + +It provides a very easy-to-use interface for OpenGL Extensions. + +### os_generic.h + +os_generic is a platform independent way of creating threads, managing TLS, mutices, +semaphores as well as getting the current time. It has the following configuration options: + +`OSG_NO_IMPLEMENTATION` - Do not include implementation as static functions. +`OSG_PREFIX` - Override function prefix +`OSG_NOSTATIC` - Do not declare the functions as static. + +### TextTool/Text.c etc... + +There is a sister repository, https://github.com/cntools/rawdrawtools which houses a text tool. + +## Warnings + +* OSX Support has been moved to attic. +* No support for openGL 1.0. If your hardware doesn't support openGL 2.0 or newer, disable +openGL support. (If you are on desktop, it probably does, you just need to install updated +drivers.) diff --git a/app/src/nativecode/rawdraw/chew.c b/app/src/nativecode/rawdraw/chew.c new file mode 100644 index 0000000..cabb3b4 --- /dev/null +++ b/app/src/nativecode/rawdraw/chew.c @@ -0,0 +1,83 @@ +#if defined( WIN32 ) || defined( WINDOWS ) || defined( WIN64 ) +#include +#else +#include +#endif + +#include +#include "chew.h" + +#define TABLEONLY +#undef CHEWTYPEDEF +#undef CHEWTYPEDEF2 +#define CHEWTYPEDEF( ret, name, retcmd, parameters, ... ) name##_t name##fnptr; +#define CHEWTYPEDEF2( ret, name, usename, retcmd, parameters, ... ) usename##_t usename##fnptr; +#include "chew.h" + +#define TABLEONLY +#undef CHEWTYPEDEF +#undef CHEWTYPEDEF2 +#define CHEWTYPEDEF( ret, name, retcmd, parameters, ... ) ret name( __VA_ARGS__ ) { retcmd name##fnptr parameters; } +#define CHEWTYPEDEF2( ret, name, usename, retcmd, parameters, ... ) ret usename( __VA_ARGS__ ) { retcmd usename##fnptr parameters ; } +#include "chew.h" + +#define TABLEONLY +#undef CHEWTYPEDEF +#undef CHEWTYPEDEF2 +#define CHEWTYPEDEF( ret, name, retcmd, parameters, ... ) #name, +#define CHEWTYPEDEF2( ret, name, usename, retcmd, parameters, ... ) #name, +const char * symnames[] = { + #include "chew.h" +}; + +#define TABLEONLY +#undef CHEWTYPEDEF +#undef CHEWTYPEDEF2 +#define CHEWTYPEDEF( ret, name, retcmd, parameters, ... ) (void**)&name##fnptr, +#define CHEWTYPEDEF2( ret, name, usename, retcmd, parameters, ... ) (void**)&usename##fnptr, +void ** syms[] = { + #include "chew.h" +}; + +#if defined( WIN32 ) || defined( WINDOWS ) || defined( WIN64 ) + +//From https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions +void * chewGetProcAddress(const char *name) +{ + void *p = (void *)wglGetProcAddress(name); + if(p == 0 || + (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) || + (p == (void*)-1) ) + { + static HMODULE module; + if( !module ) module = LoadLibraryA("opengl32.dll"); + p = (void *)GetProcAddress(module, name); + } + return p; +} + +#else + +void * chewGetProcAddress(const char *name) +{ + //Tricky use RTLD_NEXT first so we don't accidentally link against ourselves. + void * v1 = dlsym( (void*)((intptr_t)-1) /*RTLD_NEXT = -1*/ /*RTLD_DEFAULT = 0*/, name ); + //printf( "%s = %p\n", name, v1 ); + if( !v1 ) v1 = dlsym( 0, name ); + return v1; +} + +#endif + + + +void chewInit() { + int i; + for( i = 0; i < sizeof(symnames) / sizeof(symnames[0]); i++ ) + { + void * v = *syms[i] = (void*)chewGetProcAddress( symnames[i] ); + if( !v ) fprintf( stderr, "Warning: Can't find \"gl%s\"\n", symnames[i] ); + //else printf( "%p = %s\n", v, symnames[i] ); + } +} + diff --git a/app/src/nativecode/rawdraw/chew.h b/app/src/nativecode/rawdraw/chew.h new file mode 100644 index 0000000..f44e07a --- /dev/null +++ b/app/src/nativecode/rawdraw/chew.h @@ -0,0 +1,184 @@ +#if !defined( _CHEW_H ) || defined( TABLEONLY ) +#define _CHEW_H + +#ifndef TABLEONLY + +#if defined( WINDOWS ) || defined( _WINDOWS ) || defined( WIN32 ) || defined( WIN64 ) +#ifdef __TINYC__ +#define GLsizeiptr intptr_t +#define GLintptr intptr_t +#endif +//#include +#include +#else +//Include OpenGL or something maybe? +#endif +#endif + +#if defined( WIN32 ) || defined( WINDOWS ) +#define STDCALL __stdcall +#else +#define STDCALL +#endif + +#ifdef __cplusplus +#ifndef TABLEONLY +extern "C" { +#endif +#endif + +#ifndef TABLEONLY + +#ifdef EGL_LEAN_AND_MEAN +#include +#include +#else +#include +#endif + +#ifdef NO_OPENGL_HEADERS +#include "chewtypes.h" +#endif + +#ifndef chew_FUN_EXPORT +#define chew_FUN_EXPORT extern +#endif + +#define CHEWTYPEDEF( ret, name, retcmd, parameters, ... ) \ +typedef ret (STDCALL *name##_t)( __VA_ARGS__ ); \ +chew_FUN_EXPORT name##_t name##fnptr; \ +chew_FUN_EXPORT ret name( __VA_ARGS__ ); + +#define CHEWTYPEDEF2( ret, name, usename, retcmd, parameters, ... ) \ +typedef ret (STDCALL *usename##_t)( __VA_ARGS__ ); \ +chew_FUN_EXPORT usename##_t usename##fnptr; \ +chew_FUN_EXPORT ret usename( __VA_ARGS__ ); + +#if defined( __TINYC__ ) && ( defined( WINDOWS ) || defined( _WINDOWS ) || defined( WIN32 ) || defined( WIN64 ) ) +#undef GLchar +#define GLchar char +#undef GLDEBUGPROC +#define GLDEBUGPROC void* +#endif + +#if defined (TCCINSTANCE) +#undef GLvoid +#define GLvoid void +#undef GLintptr +#define GLintptr int* +#undef GLchar +#define GLchar char +#undef GLsizeiptr +#define GLsizeiptr unsigned int +#endif + +void chewInit(); +void * chewGetProcAddress( const char *name ); + +#endif + +// Add the things you want here; DO NOT put ; at end of line. + +CHEWTYPEDEF( void, glGenVertexArrays, , (n,arrays), GLsizei n, GLuint *arrays ) +CHEWTYPEDEF( void, glBindVertexArray, , (array), GLuint array ) +CHEWTYPEDEF( void, glGenBuffers, , (n,buffers), GLsizei n, GLuint * buffers ) +CHEWTYPEDEF( void, glBindBuffer, , (target,buffer), GLenum target, GLuint buffer ) +CHEWTYPEDEF( void, glBufferData, , (target,size,data,usage), GLenum target, GLsizeiptr size, const GLvoid * data, GLenum usage ) +CHEWTYPEDEF( void, glNamedBufferData, , (buffer,size,data,usage) , GLuint buffer, GLsizeiptr size, const GLvoid *data, GLenum usage ) +CHEWTYPEDEF( void, glEnableVertexAttribArray, , (index), GLuint index ) +CHEWTYPEDEF( void, glDisableVertexAttribArray, , (index), GLuint index ) +CHEWTYPEDEF( void, glEnableVertexArrayAttrib, , (vaobj,index), GLuint vaobj, GLuint index ) +CHEWTYPEDEF( void, glDisableVertexArrayAttrib, , (vaobj,index), GLuint vaobj, GLuint index ) +CHEWTYPEDEF( void, glVertexAttribPointer, , (index,size,type,normalized,stride,pointer), GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer ) +CHEWTYPEDEF( void, glVertexAttribIPointer, , (index,size,type,stride,pointer), GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid * pointer ) +CHEWTYPEDEF( void, glVertexAttribLPointer, , (index,size,type,stride,pointer), GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid * pointer ) +CHEWTYPEDEF( void, glBindAttribLocation, , (program,index,name), GLuint program, GLuint index, const GLchar *name ) + +CHEWTYPEDEF( void, glDeleteVertexArrays, , (n,arrays), GLsizei n, const GLuint *arrays ) +CHEWTYPEDEF( void, glDeleteBuffers, , (n,buffers), GLsizei n, const GLuint * buffers ) +CHEWTYPEDEF( void, glBufferSubData, , (target,offset,size,data), GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data ) +CHEWTYPEDEF( void, glNamedBufferSubData, , (buffer,offset,size,data), GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data ) + +//Already covered in SDL_opengl.h +CHEWTYPEDEF2( void, glActiveTexture, glActiveTextureCHEW, , (texture) , GLenum texture ) +CHEWTYPEDEF2( void, glSampleCoverage, glSampleCoverageCHEW, , (value,invert), GLfloat value, GLboolean invert ) + +#ifndef EGL_LEAN_AND_MEAN +#ifndef TCCINSTANCE +CHEWTYPEDEF( void, glDebugMessageCallback, , (callback,userParam), GLDEBUGPROC callback, const void * userParam ) +CHEWTYPEDEF( void, glDebugMessageControl, , (source,type,severity,count,ids,enabled), GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled ) +#endif +#endif + +CHEWTYPEDEF2( void, glGenerateMipmap, glGenerateMipmapCHEW, , (index), GLuint index ) + +CHEWTYPEDEF( void, glGenFramebuffers, , (n,framebuffers), GLsizei n, GLuint * framebuffers ) +CHEWTYPEDEF( void, glGenRenderbuffers, , (n,renderbuffers), GLsizei n, GLuint * renderbuffers ) +CHEWTYPEDEF( void, glBindFramebuffer, , (target,framebuffer), GLenum target, GLuint framebuffer ) +CHEWTYPEDEF( void, glBindRenderbuffer, , (target,renderbuffer), GLenum target, GLuint renderbuffer ) +CHEWTYPEDEF( void, glRenderbufferStorage, , (target,internalformat,width,height), GLenum target, GLenum internalformat, GLsizei width, GLsizei height ) +CHEWTYPEDEF( void, glRenderbufferStorageMultisample, , (target,samples,internalformat,width,height), GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height ) +CHEWTYPEDEF( void, glNamedRenderbufferStorageMultisample, , (renderbuffer,samples,internalformat,width,height), GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height ) +CHEWTYPEDEF( void, glFramebufferRenderbuffer, ,(target,attachment,renderbuffertarget,renderbuffer), GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer ) +CHEWTYPEDEF( void, glTexImage2DMultisample, ,(target,samples,internalformat, width, height, fixedsamplelocations),GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations ) +CHEWTYPEDEF( void, glFramebufferTexture2D, ,(target,attachment, textarget, texture, level) , GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level ) +CHEWTYPEDEF( void, glBlitFramebuffer, , (srcX0, srcY0, srcX1, srcY1,dstX0, dstY0,dstX1, dstY1,mask, filter) , GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter ) +CHEWTYPEDEF( void, glBlitNamedFramebuffer, , (readFramebuffer, drawFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter), GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter ) +CHEWTYPEDEF( void, glDeleteFramebuffers, , (n,framebuffers), GLsizei n, const GLuint * framebuffers ) +CHEWTYPEDEF( void, glDeleteRenderbuffers, , (n,renderbuffers), GLsizei n, const GLuint * renderbuffers ) +CHEWTYPEDEF( GLenum, glCheckFramebufferStatus, return, (target) , GLenum target ) +CHEWTYPEDEF( GLenum, glCheckNamedFramebufferStatus, return, (framebuffer,target), GLuint framebuffer, GLenum target ) +CHEWTYPEDEF( void, glFramebufferTexture, , (target,attachment,texture,level), GLenum target, GLenum attachment, GLuint texture, GLint level ) + + + +CHEWTYPEDEF( GLuint, glCreateProgram, return, () , void ) +CHEWTYPEDEF( GLuint, glCreateShader, return, (e), GLenum e ) +#ifndef EGL_LEAN_AND_MEAN +CHEWTYPEDEF( void, glShaderSource, , (shader,count,string,length), GLuint shader, GLsizei count, const GLchar **string, const GLint *length ) +#endif +CHEWTYPEDEF( void, glCompileShader, ,(shader), GLuint shader ) +CHEWTYPEDEF( void, glGetShaderiv, , (shader,pname,params), GLuint shader, GLenum pname, GLint *params ) +CHEWTYPEDEF( void, glGetShaderInfoLog , , (shader,maxLength, length, infoLog), GLuint shader, GLsizei maxLength, GLsizei *length, GLchar *infoLog ) +CHEWTYPEDEF( void, glDeleteProgram, , (program), GLuint program ) +CHEWTYPEDEF( void, glDeleteShader, , (shader), GLuint shader ) +CHEWTYPEDEF( void, glAttachShader, , (program,shader), GLuint program, GLuint shader ) +CHEWTYPEDEF( void, glLinkProgram, , (program), GLuint program ) +CHEWTYPEDEF( void, glGetProgramiv, , (program,pname,params), GLuint program, GLenum pname, GLint *params ) +CHEWTYPEDEF( void, glUseProgram, , (program), GLuint program ) +CHEWTYPEDEF( void, glUniform1f, , (location,v0), GLint location, GLfloat v0 ) +CHEWTYPEDEF( void, glUniform2f, , (location,v0,v1), GLint location, GLfloat v0, GLfloat v1 ) +CHEWTYPEDEF( void, glUniform3f, , (location,v0,v1,v2), GLint location, GLfloat v0, GLfloat v1, GLfloat v2 ) +CHEWTYPEDEF( void, glUniform4f, , (location,v0,v1,v2,v3), GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3 ) +CHEWTYPEDEF( void, glUniform1i, , (location,v0), GLint location, GLint v0 ) +CHEWTYPEDEF( void, glUniform2i, , (location,v0,v1), GLint location, GLint v0, GLint v1 ) +CHEWTYPEDEF( void, glUniform3i, , (location,v0,v1,v2), GLint location, GLint v0, GLint v1, GLint v2 ) +CHEWTYPEDEF( void, glUniform4i, , (location,v0,v1,v2,v3), GLint location, GLint v0, GLint v1, GLint v2, GLint v3 ) +CHEWTYPEDEF( void, glUniform1fv, , (location,count,value), GLint location, GLsizei count, const GLfloat *value ) +CHEWTYPEDEF( void, glUniform2fv, , (location,count,value), GLint location, GLsizei count, const GLfloat *value ) +CHEWTYPEDEF( void, glUniform3fv, , (location,count,value), GLint location, GLsizei count, const GLfloat *value ) +CHEWTYPEDEF( void, glUniform4fv, , (location,count,value), GLint location, GLsizei count, const GLfloat *value ) +CHEWTYPEDEF2( void, glUniform4fv, glUniform4fvCHEW, , (location,count,value), GLint location, GLsizei count, const GLfloat *value ) +CHEWTYPEDEF( void, glUniform1iv, , (location,count,value), GLint location, GLsizei count, const GLint *value ) +CHEWTYPEDEF( void, glUniform2iv, , (location,count,value), GLint location, GLsizei count, const GLint *value ) +CHEWTYPEDEF( void, glUniform3iv, , (location,count,value), GLint location, GLsizei count, const GLint *value ) +CHEWTYPEDEF( void, glUniform4iv, , (location,count,value), GLint location, GLsizei count, const GLint *value ) +CHEWTYPEDEF( void, glUniformMatrix2fv, ,(location,count,transpose,value) , GLint location, GLsizei count, GLboolean transpose, const GLfloat *value ) +CHEWTYPEDEF( void, glUniformMatrix3fv, ,(location,count,transpose,value) , GLint location, GLsizei count, GLboolean transpose, const GLfloat *value ) +CHEWTYPEDEF( void, glUniformMatrix4fv, ,(location,count,transpose,value) , GLint location, GLsizei count, GLboolean transpose, const GLfloat *value ) +CHEWTYPEDEF( void, glGetProgramInfoLog, , (program,maxLength, length, infoLog), GLuint program, GLsizei maxLength, GLsizei *length, GLchar *infoLog ) +CHEWTYPEDEF( GLint, glGetUniformLocation, return, (program,name), GLuint program, const GLchar *name ) + +CHEWTYPEDEF( void *, glMapBuffer, return, (target,access), GLenum target, GLenum access ) +CHEWTYPEDEF( void *, glMapNamedBuffer, return, (buffer,access), GLuint buffer, GLenum access ) +CHEWTYPEDEF( void *, glMapBufferRange, return, (buffer,offset,length,access), GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access ) + +CHEWTYPEDEF( GLboolean, glUnmapBuffer, return, (target), GLenum target ) + +#ifdef __cplusplus +#ifndef TABLEONLY +}; +#endif +#endif + +#endif // _CHEW_H diff --git a/app/src/nativecode/rawdraw/chewtypes.h b/app/src/nativecode/rawdraw/chewtypes.h new file mode 100644 index 0000000..d7c0d1e --- /dev/null +++ b/app/src/nativecode/rawdraw/chewtypes.h @@ -0,0 +1,118 @@ +#ifndef _CHEWTYPES_H +#define _CHEWTYPES_H + +#include + +typedef int GLfixed; +typedef int GLclampx; +typedef int64_t GLint64; +typedef uint64_t GLuint64; +typedef uint32_t GLuint32; +typedef int32_t GLint32; +typedef uint32_t GLuint; +typedef char GLchar; +typedef struct __GLsync *GLsync; +typedef intptr_t GLsizeiptr; +typedef intptr_t GLintptr; + +#ifndef APIENTRY +#define APIENTRY +#endif + +#ifndef GLDEBUGPROC +typedef void (APIENTRY *GLDEBUGPROC)( + GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, + const GLchar *message, const void *userParam); +#endif + +#ifndef GL_DEBUG_OUTPUT_SYNCHRONOUS +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 + +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_STATIC_DRAW 0x88E4 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_MULTISAMPLE 0x809D +#define GL_RENDERBUFFER 0x8D41 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_TEXTURE_MAX_ANISOTROPY 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY 0x84FF +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_POINT_SPRITE_OES 0x8861 + +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_STREAM_DRAW 0x88E0 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_READ_ONLY 0x88B8 +#define GL_BGRA 0x80E1 +#define GL_STREAM_READ 0x88E1 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 + +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E + +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA + +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 + +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_DEPTH_CLAMP 0x864F + +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 +#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 +#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 +#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 + + +#define GL_POINT_SPRITE 0x8861 +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_R32F 0x822E +#define GL_RG32F 0x8230 +#define GL_RGB32F 0x8815 +#define GL_RGBA32F 0x8814 +#define GL_R8 0x8229 +#define GL_RG8 0x822B +#define GL_RG 0x8227 + +#endif + + +#endif diff --git a/app/src/nativecode/rawdraw/examples/Makefile b/app/src/nativecode/rawdraw/examples/Makefile new file mode 100644 index 0000000..b7b4080 --- /dev/null +++ b/app/src/nativecode/rawdraw/examples/Makefile @@ -0,0 +1,27 @@ +all : fontsize fontsize_ogl + +#for X11 consider: xorg-dev +#for X11, you will need: libx-dev +#for full screen you'll need: libxinerama-dev libxext-dev +#for OGL You'll need: mesa-common-dev libglu1-mesa-dev + +#-DCNFGRASTERIZER +# and +#-CNFGOGL +# are incompatible. + + +MINGW32:=/usr/bin/i686-w64-mingw32- + +fontsize : fontsize.c + gcc -o $@ $^ -lX11 -lm -lpthread -lXinerama -lXext -lGL -g -DCNFGRASTERIZER -Wall + +fontsize_ogl : fontsize.c + gcc -o $@ $^ -lX11 -lm -lpthread -lXinerama -lXext -lGL -g -lX11 -lXinerama -lGL -DCNFGOGL -Wall + +fontsize.exe : fontsize.c + $(MINGW32)gcc -m32 -o $@ $^ -lgdi32 $(CFLAGS) + +clean : + rm -rf *.o *~ rawdraw.exe rawdraw ontop rawdraw_mac rawdraw_mac_soft rawdraw_mac_cg rawdraw_mac_ogl ogltest ogltest.exe rawdraw_egl fontsize fontsize_ogl fontsize.exe + diff --git a/app/src/nativecode/rawdraw/examples/fontsize.c b/app/src/nativecode/rawdraw/examples/fontsize.c new file mode 100644 index 0000000..841f013 --- /dev/null +++ b/app/src/nativecode/rawdraw/examples/fontsize.c @@ -0,0 +1,77 @@ +#include +#include +#include + +#define CNFG_IMPLEMENTATION +#include "../CNFG.h" +#include "../os_generic.h" + +#if defined( CNFGOGL ) +#define TITLESTRING "Fontsize test with OpenGL" +#elif defined( CNFGRASTERIZER ) +#define TITLESTRING "Fontsize test with Rasterizer" +#else +#define TITLESTRING "Fontsize test with Unknown" +#endif + +void HandleKey( int keycode, int bDown ) +{ + if( keycode == CNFG_KEY_ESCAPE ) exit( 0 ); + printf( "Key: %d -> %d\n", keycode, bDown ); +} + +void HandleButton( int x, int y, int button, int bDown ) +{ + printf( "Button: %d,%d (%d) -> %d\n", x, y, button, bDown ); +} + +void HandleMotion( int x, int y, int mask ) +{ +// printf( "Motion: %d,%d (%d)\n", x, y, mask ); +} + +int HandleDestroy() +{ + printf( "Destroying\n" ); + exit(10); + return 0; +} + +int main() +{ + + int i; + + CNFGBGColor = 0x800000; + // Deprecated CNFGDialogColor = 0x444444; + CNFGSetup( TITLESTRING, 1220, 480 ); + + + + while (CNFGHandleInput()) { + CNFGClearFrame(); + CNFGColor( 0xffffff ); + for( i = 1; i < 5; i++ ) + { + int c; + char tw[2] = { 0, 0 }; + for( c = 0; c < 256; c++ ) + { + tw[0] = c; + + CNFGPenX = ( c % 16 ) * 16 + 5 + (i - 1) * 320; + CNFGPenY = ( c / 16 ) * 18 + 5; + CNFGDrawText( tw, i ); + } + } + + CNFGPenX = 20; + CNFGPenY = 300; + for( i = 1; i < 7; i++) { + CNFGPenY += i * 6; + CNFGDrawText("A quick brown fox jumps over the lazy dog.", i); + } + CNFGSwapBuffers(); + OGUSleep( (int)( 0.5 * 1000000 ) ); + } +} diff --git a/app/src/nativecode/rawdraw/ogltest.c b/app/src/nativecode/rawdraw/ogltest.c new file mode 100644 index 0000000..3ae8f5d --- /dev/null +++ b/app/src/nativecode/rawdraw/ogltest.c @@ -0,0 +1,56 @@ +#include +#include +#include + +#include "CNFG.h" + +void HandleKey( int keycode, int bDown ) +{ + printf( "Key: %d %d\n", keycode, bDown ); +} + +void HandleButton( int x, int y, int button, int bDown ) +{ +} + +void HandleMotion( int x, int y, int mask ) +{ +} + +int HandleDestroy() +{ + exit(10); + return 0; +} + +int main() +{ + short w, h; + int frameno = 0; + CNFGSetup( "Test", 100, 100 ); + + while(CNFGHandleInput()) + { + CNFGClearFrame(); + + glRotatef( frameno/100.0, 0, 0, 1); + + glColor4f( 0, 0, .5, 1 ); + + glBegin( GL_TRIANGLES ); + glVertex3f( -40, -40, 0 ); + glVertex3f( 40, 0, 0 ); + glVertex3f( 0, 40, 0 ); + glEnd(); + + char stmp[100]; + CNFGColor( 0xFFFFFF ); + sprintf( stmp, "%d\n", frameno ); + CNFGPenX = 10; CNFGPenY = 10; + CNFGDrawText( stmp, 3 ); + CNFGTackPixel( 100, 100 ); + + CNFGSwapBuffers(); + frameno++; + } +} diff --git a/app/src/nativecode/rawdraw/os_generic.h b/app/src/nativecode/rawdraw/os_generic.h new file mode 100644 index 0000000..d1d1dfc --- /dev/null +++ b/app/src/nativecode/rawdraw/os_generic.h @@ -0,0 +1,520 @@ +#ifndef _OS_GENERIC_H +#define _OS_GENERIC_H +/* + "osgeneric" Generic, platform independent tool for threads and time. + Geared around Windows and Linux. Designed for operation on MSVC, + TCC, GCC and clang. Others may work. + + It offers the following operations: + + Delay functions: + void OGSleep( int is ); + void OGUSleep( int ius ); + + Getting current time (may be time from program start, boot, or epoc) + double OGGetAbsoluteTime(); + double OGGetFileTime( const char * file ); + + Thread functions + og_thread_t OGCreateThread( void * (routine)( void * ), void * parameter ); + void * OGJoinThread( og_thread_t ot ); + void OGCancelThread( og_thread_t ot ); + + Mutex functions, used for protecting data structures. + (recursive on platforms where available.) + og_mutex_t OGCreateMutex(); + void OGLockMutex( og_mutex_t om ); + void OGUnlockMutex( og_mutex_t om ); + void OGDeleteMutex( og_mutex_t om ); + + Always a semaphore (not recursive) + og_sema_t OGCreateSema(); //Create a semaphore, comes locked initially. + NOTE: For platform compatibility, max count is 32767 + void OGLockSema( og_sema_t os ); + int OGGetSema( og_sema_t os ); //if <0 there was a failure. + void OGUnlockSema( og_sema_t os ); + void OGDeleteSema( og_sema_t os ); + + TLS (Thread-Local Storage) + og_tls_t OGCreateTLS(); + void OGDeleteTLS( og_tls_t tls ); + void OGSetTLS( og_tls_t tls, void * data ); + void * OGGetTLS( og_tls_t tls ); + + You can permute the operations of this file by the following means: + OSG_NO_IMPLEMENTATION + OSG_PREFIX + OSG_NOSTATIC + + The default behavior is to do static inline. + + Copyright (c) 2011-2012,2013,2016,2018,2019,2020 <>< Charles Lohr + + This file may be licensed under the MIT/x11 license, NewBSD or CC0 licenses + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of this file. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. + + Date Stamp: 2019-09-05 CNL: Allow for noninstantiation and added TLS. + Date Stamp: 2018-03-25 CNL: Switched to header-only format. +*/ + + +#if defined( OSG_NOSTATIC ) && OSG_NOSTATIC != 0 +#ifndef OSG_PREFIX +#define OSG_PREFIX +#endif +#ifndef OSG_NO_IMPLEMENTATION +#define OSG_NO_IMPLEMENTATION +#endif +#endif + +#ifndef OSG_PREFIX +#ifdef __wasm__ +#define OSG_PREFIX +#else +#define OSG_PREFIX static inline +#endif +#endif + +//In case you want to hook the closure of a thread, i.e. if your system has thread-local storage. +#ifndef OSG_TERM_THREAD_CODE +#define OSG_TERM_THREAD_CODE +#endif + +typedef void* og_thread_t; +typedef void* og_mutex_t; +typedef void* og_sema_t; +typedef void* og_tls_t; + +#ifdef __cplusplus +extern "C" { +#endif + +OSG_PREFIX void OGSleep( int is ); +OSG_PREFIX void OGUSleep( int ius ); +OSG_PREFIX double OGGetAbsoluteTime(); +OSG_PREFIX double OGGetFileTime( const char * file ); +OSG_PREFIX og_thread_t OGCreateThread( void * (routine)( void * ), void * parameter ); +OSG_PREFIX void * OGJoinThread( og_thread_t ot ); +OSG_PREFIX void OGCancelThread( og_thread_t ot ); +OSG_PREFIX og_mutex_t OGCreateMutex(); +OSG_PREFIX void OGLockMutex( og_mutex_t om ); +OSG_PREFIX void OGUnlockMutex( og_mutex_t om ); +OSG_PREFIX void OGDeleteMutex( og_mutex_t om ); +OSG_PREFIX og_sema_t OGCreateSema(); +OSG_PREFIX int OGGetSema( og_sema_t os ); +OSG_PREFIX void OGLockSema( og_sema_t os ); +OSG_PREFIX void OGUnlockSema( og_sema_t os ); +OSG_PREFIX void OGDeleteSema( og_sema_t os ); +OSG_PREFIX og_tls_t OGCreateTLS(); +OSG_PREFIX void OGDeleteTLS( og_tls_t key ); +OSG_PREFIX void * OGGetTLS( og_tls_t key ); +OSG_PREFIX void OGSetTLS( og_tls_t key, void * data ); + +#ifdef __cplusplus +}; +#endif + +#ifndef OSG_NO_IMPLEMENTATION + +#if defined( WIN32 ) || defined (WINDOWS) || defined( _WIN32) +#define USE_WINDOWS +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef USE_WINDOWS + +#include +#include + +OSG_PREFIX void OGSleep( int is ) +{ + Sleep( is*1000 ); +} + +OSG_PREFIX void OGUSleep( int ius ) +{ + Sleep( ius/1000 ); +} + +OSG_PREFIX double OGGetAbsoluteTime() +{ + static LARGE_INTEGER lpf; + LARGE_INTEGER li; + + if( !lpf.QuadPart ) + { + QueryPerformanceFrequency( &lpf ); + } + + QueryPerformanceCounter( &li ); + return (double)li.QuadPart / (double)lpf.QuadPart; +} + + +OSG_PREFIX double OGGetFileTime( const char * file ) +{ + FILETIME ft; + + HANDLE h = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + + if( h==INVALID_HANDLE_VALUE ) + return -1; + + GetFileTime( h, 0, 0, &ft ); + + CloseHandle( h ); + + return ft.dwHighDateTime + ft.dwLowDateTime; +} + + +OSG_PREFIX og_thread_t OGCreateThread( void * (routine)( void * ), void * parameter ) +{ + return (og_thread_t)CreateThread( 0, 0, (LPTHREAD_START_ROUTINE)routine, parameter, 0, 0 ); +} + +OSG_PREFIX void * OGJoinThread( og_thread_t ot ) +{ + WaitForSingleObject( ot, INFINITE ); + OSG_TERM_THREAD_CODE + CloseHandle( ot ); + return 0; +} + +OSG_PREFIX void OGCancelThread( og_thread_t ot ) +{ + OSG_TERM_THREAD_CODE + TerminateThread( ot, 0); + CloseHandle( ot ); +} + +OSG_PREFIX og_mutex_t OGCreateMutex() +{ + return CreateMutex( 0, 0, 0 ); +} + +OSG_PREFIX void OGLockMutex( og_mutex_t om ) +{ + WaitForSingleObject(om, INFINITE); +} + +OSG_PREFIX void OGUnlockMutex( og_mutex_t om ) +{ + ReleaseMutex(om); +} + +OSG_PREFIX void OGDeleteMutex( og_mutex_t om ) +{ + CloseHandle( om ); +} + + + +OSG_PREFIX og_sema_t OGCreateSema() +{ + HANDLE sem = CreateSemaphore( 0, 0, 32767, 0 ); + return (og_sema_t)sem; +} + +OSG_PREFIX int OGGetSema( og_sema_t os ) +{ + typedef LONG NTSTATUS; + HANDLE sem = (HANDLE)os; + typedef NTSTATUS (NTAPI *_NtQuerySemaphore)( + HANDLE SemaphoreHandle, + DWORD SemaphoreInformationClass, /* Would be SEMAPHORE_INFORMATION_CLASS */ + PVOID SemaphoreInformation, /* but this is to much to dump here */ + ULONG SemaphoreInformationLength, + PULONG ReturnLength OPTIONAL + ); + + typedef struct _SEMAPHORE_BASIC_INFORMATION { + ULONG CurrentCount; + ULONG MaximumCount; + } SEMAPHORE_BASIC_INFORMATION; + + + static _NtQuerySemaphore NtQuerySemaphore; + SEMAPHORE_BASIC_INFORMATION BasicInfo; + NTSTATUS Status; + + if( !NtQuerySemaphore ) + { + NtQuerySemaphore = (_NtQuerySemaphore)GetProcAddress (GetModuleHandle ("ntdll.dll"), "NtQuerySemaphore"); + if( !NtQuerySemaphore ) + { + return -1; + } + } + + + Status = NtQuerySemaphore (sem, 0 /*SemaphoreBasicInformation*/, + &BasicInfo, sizeof (SEMAPHORE_BASIC_INFORMATION), NULL); + + if (Status == ERROR_SUCCESS) + { + return BasicInfo.CurrentCount; + } + + return -2; +} + +OSG_PREFIX void OGLockSema( og_sema_t os ) +{ + WaitForSingleObject( (HANDLE)os, INFINITE ); +} + +OSG_PREFIX void OGUnlockSema( og_sema_t os ) +{ + ReleaseSemaphore( (HANDLE)os, 1, 0 ); +} + +OSG_PREFIX void OGDeleteSema( og_sema_t os ) +{ + CloseHandle( os ); +} + +OSG_PREFIX og_tls_t OGCreateTLS() +{ + return (og_tls_t)(intptr_t)TlsAlloc(); +} + +OSG_PREFIX void OGDeleteTLS( og_tls_t key ) +{ + TlsFree( (DWORD)(intptr_t)key ); +} + +OSG_PREFIX void * OGGetTLS( og_tls_t key ) +{ + return TlsGetValue( (DWORD)(intptr_t)key ); +} + +OSG_PREFIX void OGSetTLS( og_tls_t key, void * data ) +{ + TlsSetValue( (DWORD)(intptr_t)key, data ); +} + +#elif defined( __wasm__ ) + +//We don't actually have any function defintions here. +//The outside system will handle it. + +#else + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include + +OSG_PREFIX void OGSleep( int is ) +{ + sleep( is ); +} + +OSG_PREFIX void OGUSleep( int ius ) +{ + usleep( ius ); +} + +OSG_PREFIX double OGGetAbsoluteTime() +{ + struct timeval tv; + gettimeofday( &tv, 0 ); + return ((double)tv.tv_usec)/1000000. + (tv.tv_sec); +} + +OSG_PREFIX double OGGetFileTime( const char * file ) +{ + struct stat buff; + + int r = stat( file, &buff ); + + if( r < 0 ) + { + return -1; + } + + return buff.st_mtime; +} + + + +OSG_PREFIX og_thread_t OGCreateThread( void * (routine)( void * ), void * parameter ) +{ + pthread_t * ret = (pthread_t *)malloc( sizeof( pthread_t ) ); + if( !ret ) return 0; + int r = pthread_create( ret, 0, routine, parameter ); + if( r ) + { + free( ret ); + return 0; + } + return (og_thread_t)ret; +} + +OSG_PREFIX void * OGJoinThread( og_thread_t ot ) +{ + void * retval; + if( !ot ) + { + return 0; + } + pthread_join( *(pthread_t*)ot, &retval ); + OSG_TERM_THREAD_CODE + free( ot ); + return retval; +} + +OSG_PREFIX void OGCancelThread( og_thread_t ot ) +{ + if( !ot ) + { + return; + } +#ifdef ANDROID + pthread_kill( *(pthread_t*)ot, SIGTERM ); +#else + pthread_cancel( *(pthread_t*)ot ); +#endif + OSG_TERM_THREAD_CODE + free( ot ); +} + +OSG_PREFIX og_mutex_t OGCreateMutex() +{ + pthread_mutexattr_t mta; + og_mutex_t r = malloc( sizeof( pthread_mutex_t ) ); + if( !r ) return 0; + + pthread_mutexattr_init(&mta); + pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_RECURSIVE); + + pthread_mutex_init( (pthread_mutex_t *)r, &mta ); + + return r; +} + +OSG_PREFIX void OGLockMutex( og_mutex_t om ) +{ + if( !om ) + { + return; + } + pthread_mutex_lock( (pthread_mutex_t*)om ); +} + +OSG_PREFIX void OGUnlockMutex( og_mutex_t om ) +{ + if( !om ) + { + return; + } + pthread_mutex_unlock( (pthread_mutex_t*)om ); +} + +OSG_PREFIX void OGDeleteMutex( og_mutex_t om ) +{ + if( !om ) + { + return; + } + + pthread_mutex_destroy( (pthread_mutex_t*)om ); + free( om ); +} + + + + +OSG_PREFIX og_sema_t OGCreateSema() +{ + sem_t * sem = (sem_t *)malloc( sizeof( sem_t ) ); + if( !sem ) return 0; + sem_init( sem, 0, 0 ); + return (og_sema_t)sem; +} + +OSG_PREFIX int OGGetSema( og_sema_t os ) +{ + int valp; + sem_getvalue( (sem_t*)os, &valp ); + return valp; +} + + +OSG_PREFIX void OGLockSema( og_sema_t os ) +{ + sem_wait( (sem_t*)os ); +} + +OSG_PREFIX void OGUnlockSema( og_sema_t os ) +{ + sem_post( (sem_t*)os ); +} + +OSG_PREFIX void OGDeleteSema( og_sema_t os ) +{ + sem_destroy( (sem_t*)os ); + free(os); +} + +OSG_PREFIX og_tls_t OGCreateTLS() +{ + pthread_key_t ret = 0; + pthread_key_create(&ret, 0); + return (og_tls_t)(intptr_t)ret; +} + +OSG_PREFIX void OGDeleteTLS( og_tls_t key ) +{ + pthread_key_delete( (pthread_key_t)(intptr_t)key ); +} + +OSG_PREFIX void * OGGetTLS( og_tls_t key ) +{ + return pthread_getspecific( (pthread_key_t)(intptr_t)key ); +} + +OSG_PREFIX void OGSetTLS( og_tls_t key, void * data ) +{ + pthread_setspecific( (pthread_key_t)(intptr_t)key, data ); +} + +#endif + +#ifdef __cplusplus +}; +#endif + +#endif //OSG_NO_IMPLEMENTATION + +#endif //_OS_GENERIC_H + diff --git a/app/src/nativecode/rawdraw/osdtest.c b/app/src/nativecode/rawdraw/osdtest.c new file mode 100644 index 0000000..dda9761 --- /dev/null +++ b/app/src/nativecode/rawdraw/osdtest.c @@ -0,0 +1,153 @@ +//Copyright (c) 2011 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose. + +#include +#include +#include +#include "CNFG.h" +#include "os_generic.h" + +unsigned frames = 0; +unsigned long iframeno = 0; + +void HandleKey( int keycode, int bDown ) +{ + if( keycode == 65307 ) exit( 0 ); + printf( "Key: %d -> %d\n", keycode, bDown ); +} + +void HandleButton( int x, int y, int button, int bDown ) +{ + printf( "Button: %d,%d (%d) -> %d\n", x, y, button, bDown ); +} + +void HandleMotion( int x, int y, int mask ) +{ +// printf( "Motion: %d,%d (%d)\n", x, y, mask ); +} + + +int HandleDestroy() +{ + printf( "Destroying\n" ); + exit(10); + return 0; +} + + +short screenx, screeny; + +int main() +{ + int i, x, y; + double ThisTime; + double LastFPSTime = OGGetAbsoluteTime(); + double LastFrameTime = OGGetAbsoluteTime(); + double SecToWait; + int linesegs = 0; + + CNFGBGColor = 0x800000; + //Deprecated CNFGDialogColor = 0x444444; + CNFGPrepareForTransparency(); + CNFGSetupFullscreen( "Test Bench", 0 ); + + while(CNFGHandleInput()) + { + int i, pos; + float f; + iframeno++; + RDPoint pto[3]; + + CNFGClearFrame(); + CNFGColor( 0xFFFFFF ); + CNFGGetDimensions( &screenx, &screeny ); + +// DrawFrame(); + +/* + + pto[0].x = 100; + pto[0].y = 100; + pto[1].x = 200; + pto[1].y = 100; + pto[2].x = 100; + pto[2].y = 200; + CNFGTackPoly( &pto[0], 3 ); + + CNFGColor( 0xFF00FF ); +*/ + +/* CNFGTackSegment( pto[0].x, pto[0].y, pto[1].x, pto[1].y ); + CNFGTackSegment( pto[1].x, pto[1].y, pto[2].x, pto[2].y ); + CNFGTackSegment( pto[2].x, pto[2].y, pto[0].x, pto[0].y ); +*/ + + CNFGClearTransparencyLevel(); + CNFGDrawToTransparencyMode(1); + CNFGTackRectangle( 0, 0, 260, 260 ); + CNFGTackRectangle( 400, 400, 600, 600 ); + CNFGPenX = 300; + CNFGPenY = 200; + CNFGSetLineWidth( 7 + sin(iframeno)*5 ); + CNFGDrawText( "HELLO!", 5 ); + CNFGDrawToTransparencyMode(0); + + + CNFGSetLineWidth( 1 ); + CNFGDrawText( "HELLO!", 5 ); + + CNFGDrawBox( 0, 0, 260, 260 ); + + CNFGPenX = 10; CNFGPenY = 10; + + pos = 0; + CNFGColor( 0xffffff ); + for( i = 0; i < 1; i++ ) + { + int c; + char tw[2] = { 0, 0 }; + for( c = 0; c < 256; c++ ) + { + tw[0] = c; + + CNFGPenX = ( c % 16 ) * 16+5; + CNFGPenY = ( c / 16 ) * 16+5; + CNFGDrawText( tw, 2 ); + } + } + + CNFGPenX = 0; + CNFGPenY = 0; + + + RDPoint pp[3]; + CNFGColor( 0x00FF00 ); + pp[0].x = (short)(-200*sin((float)(iframeno)*.01) + 500); + pp[0].y = (short)(-200*cos((float)(iframeno)*.01) + 500); + pp[1].x = (short)(200*sin((float)(iframeno)*.01) + 500); + pp[1].y = (short)(00*cos((float)(iframeno)*.01) + 500); + pp[2].x = (short)(200*sin((float)(iframeno)*.01) + 500); + pp[2].y = (short)(200*cos((float)(iframeno)*.01) + 500); + CNFGTackPoly( pp, 3 ); + + + frames++; + CNFGSwapBuffers(); + + ThisTime = OGGetAbsoluteTime(); + if( ThisTime > LastFPSTime + 1 ) + { + printf( "FPS: %d\n", frames ); + frames = 0; + linesegs = 0; + LastFPSTime+=1; + } + + SecToWait = .016 - ( ThisTime - LastFrameTime ); + LastFrameTime += .016; + if( SecToWait > 0 ) + OGUSleep( (int)( SecToWait * 1000000 ) ); + } + + return(0); +} + diff --git a/app/src/nativecode/rawdraw/rawdraw.c b/app/src/nativecode/rawdraw/rawdraw.c new file mode 100644 index 0000000..c952951 --- /dev/null +++ b/app/src/nativecode/rawdraw/rawdraw.c @@ -0,0 +1,293 @@ +//Copyright (c) 2011 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose. + +#include +#include +#include +#include "os_generic.h" + +#define CNFG3D +#define CNFG_IMPLEMENTATION +//#define CNFGOGL +//#define CNFGRASTERIZER +//#define CNFG_WINDOWS_DISABLE_BATCH + +#include "CNFG.h" + +unsigned frames = 0; +unsigned long iframeno = 0; + +void HandleKey( int keycode, int bDown ) +{ + printf( "Key: %d -> %d\n", keycode, bDown ); + printf( "Scancode: %d -> %d\n", CNFGLastScancode, bDown ); + printf( "Char: %c\n", CNFGLastCharacter ); + if( keycode == 27 || keycode == CNFG_KEY_ESCAPE ) exit( 0 ); +} + +void HandleButton( int x, int y, int button, int bDown ) +{ + printf( "Button: %d,%d (%d) -> %d\n", x, y, button, bDown ); +} + +void HandleMotion( int x, int y, int mask ) +{ +// printf( "Motion: %d,%d (%d)\n", x, y, mask ); +} + +int HandleDestroy() +{ + printf( "Destroying\n" ); + exit(10); + return 0; +} + +#define HMX 40 +#define HMY 40 +short screenx, screeny; +float Heightmap[HMX*HMY]; + +void DrawHeightmap() +{ + int x, y; + float fdt = ((iframeno++)%(360*10))/10.0; + float eye[3] = { (float)sin(fdt*(3.14159/180.0))*1, (float)cos(fdt*(3.14159/180.0))*1, 1 }; + float at[3] = { 0,0, 0 }; + float up[3] = { 0,0, 1 }; + + tdSetViewport( -1, -1, 1, 1, screenx, screeny ); + + tdMode( tdPROJECTION ); + tdIdentity( gSMatrix ); + tdPerspective( 40, ((float)screenx)/((float)screeny), .1, 200., gSMatrix ); + + tdMode( tdMODELVIEW ); + tdIdentity( gSMatrix ); + tdTranslate( gSMatrix, 0, 0, -40 ); + tdLookAt( gSMatrix, eye, at, up ); + + + for( x = 0; x < HMX-1; x++ ) + for( y = 0; y < HMY-1; y++ ) + { + float tx = x-HMX/2; + float ty = y-HMY/2; + float pta[3]; + float ptb[3]; + float ptc[3]; + float ptd[3]; + + float normal[3]; + float lightdir[3] = { 1, -1, 1 }; + float tmp1[3]; + float tmp2[3]; + + RDPoint pto[6]; + + pta[0] = tx+0; pta[1] = ty+0; pta[2] = Heightmap[(x+0)+(y+0)*HMX]; + ptb[0] = tx+1; ptb[1] = ty+0; ptb[2] = Heightmap[(x+1)+(y+0)*HMX]; + ptc[0] = tx+0; ptc[1] = ty+1; ptc[2] = Heightmap[(x+0)+(y+1)*HMX]; + ptd[0] = tx+1; ptd[1] = ty+1; ptd[2] = Heightmap[(x+1)+(y+1)*HMX]; + + tdPSub( pta, ptb, tmp2 ); + tdPSub( ptc, ptb, tmp1 ); + tdCross( tmp1, tmp2, normal ); + + tdFinalPoint( pta, pta ); + tdFinalPoint( ptb, ptb ); + tdFinalPoint( ptc, ptc ); + tdFinalPoint( ptd, ptd ); + + if( pta[2] >= 1.0 ) continue; + if( ptb[2] >= 1.0 ) continue; + if( ptc[2] >= 1.0 ) continue; + if( ptd[2] >= 1.0 ) continue; + + if( pta[2] < 0 ) continue; + if( ptb[2] < 0 ) continue; + if( ptc[2] < 0 ) continue; + if( ptd[2] < 0 ) continue; + +/* if( pta[3] < -1 ) continue; + if( ptb[3] < -1 ) continue; + if( ptc[3] < -1 ) continue; + if( ptd[3] < -1 ) continue; +*/ + +// if( pta[2] < 0 ) continue; +// if( ptb[2] < 0 ) continue; +// if( ptc[2] < 0 ) continue; +// if( ptd[2] < 0 ) continue; + + pto[0].x = pta[0]; pto[0].y = pta[1]; + pto[1].x = ptb[0]; pto[1].y = ptb[1]; + pto[2].x = ptd[0]; pto[2].y = ptd[1]; + + pto[3].x = ptc[0]; pto[3].y = ptc[1]; + pto[4].x = ptd[0]; pto[4].y = ptd[1]; + pto[5].x = pta[0]; pto[5].y = pta[1]; + +// CNFGColor(((x+y)&1)?0xFFFFFFFF:0x000000FF); + + float bright = tdDot( normal, lightdir ); + if( bright < 0 ) bright = 0; + CNFGColor( 0xff | (((int)( bright * 50 ))<<24) ); + +// CNFGTackPoly( &pto[0], 3 ); CNFGTackPoly( &pto[3], 3 ); + + + CNFGTackSegment( pta[0], pta[1], ptb[0], ptb[1] ); +// CNFGTackSegment( ptb[0], ptb[1], ptc[0], ptc[1] ); +// CNFGTackSegment( ptc[0], ptc[1], ptd[0], ptd[1] ); +// CNFGTackSegment( ptd[0], ptd[1], pta[0], pta[1] ); + CNFGTackSegment( pta[0], pta[1], ptc[0], ptc[1] ); +// CNFGTackSegment( ptd[0], ptd[1], ptb[0], ptb[1] ); + + } + +/* + for( f = 0; f <= 6.28; f+=0.01 ) + { + tdPSet( pta, cos( f ), sin(f), cos( f * 10. + ThisTime) ); + tdPSet( ptb, cos( f - 0.01 ), sin(f - 0.01), cos( (f-0.01) * 10. + ThisTime) ); + // printf( "(%f, %f, %f) -> ", pta[0], pta[1], pta[2] ); + tdFinalPoint( pta, pta ); + tdFinalPoint( ptb, ptb ); + // printf( "%f, %f, %f\n", pta[0], pta[1], pta[2] ); + CNFGTackSegment( pta[0], pta[1], ptb[0], ptb[1] ); + } + +*/ + + +} + + +uint32_t randomtexturedata[65536]; + +int main() +{ + int i, x, y; + double ThisTime; + double LastFPSTime = OGGetAbsoluteTime(); + double LastFrameTime = OGGetAbsoluteTime(); + double SecToWait; + int linesegs = 0; + + CNFGBGColor = 0x000080FF; //Darkblue + CNFGSetup( "Test Bench", 640, 480 ); + + // CNFGSetupFullscreen( "Test Bench", 0 ); + + //CNFGSetLineWidth( 2 ); + + for( x = 0; x < HMX; x++ ) + for( y = 0; y < HMY; y++ ) + { + Heightmap[x+y*HMX] = tdPerlin2D( x, y )*8.; + } + + while(CNFGHandleInput()) + { + int i, pos; + float f; + iframeno++; + RDPoint pto[3]; + + CNFGClearFrame(); + CNFGColor( 0xFFFFFFFF ); + CNFGGetDimensions( &screenx, &screeny ); + + // Mesh in background + DrawHeightmap(); +/* + + pto[0].x = 100; + pto[0].y = 100; + pto[1].x = 200; + pto[1].y = 100; + pto[2].x = 100; + pto[2].y = 200; + CNFGTackPoly( &pto[0], 3 ); + + CNFGColor( 0xFF00FF ); +*/ + +/* CNFGTackSegment( pto[0].x, pto[0].y, pto[1].x, pto[1].y ); + CNFGTackSegment( pto[1].x, pto[1].y, pto[2].x, pto[2].y ); + CNFGTackSegment( pto[2].x, pto[2].y, pto[0].x, pto[0].y ); +*/ + + CNFGColor( 0xffffffff ); + CNFGDialogColor = 0x202020ff; + CNFGDrawBox( 300, 0, 320, 20 ); + + // Square behind text + CNFGColor( 0x444444FF ); + CNFGTackRectangle( 0, 0, 260, 260 ); + + CNFGPenX = 10; CNFGPenY = 10; + + // Text + pos = 0; + CNFGColor( 0xffffffff ); + for( i = 0; i < 1; i++ ) + { + int c; + char tw[2] = { 0, 0 }; + for( c = 0; c < 256; c++ ) + { + tw[0] = c; + + CNFGPenX = ( c % 16 ) * 16+5; + CNFGPenY = ( c / 16 ) * 16+5; + CNFGDrawText( tw, 2 ); + } + } + + // Green triangles + CNFGPenX = 0; + CNFGPenY = 0; + + for( i = 0; i < 400; i++ ) + { + RDPoint pp[3]; + CNFGColor( 0x00FF00FF ); //Green + pp[0].x = (short)(50*sin((float)(i+iframeno)*.01) + (i%20)*30); + pp[0].y = (short)(50*cos((float)(i+iframeno)*.01) + (i/20)*20); + pp[1].x = (short)(20*sin((float)(i+iframeno)*.01) + (i%20)*30); + pp[1].y = (short)(50*cos((float)(i+iframeno)*.01) + (i/20)*20); + pp[2].x = (short)(10*sin((float)(i+iframeno)*.01) + (i%20)*30); + pp[2].y = (short)(30*cos((float)(i+iframeno)*.01) + (i/20)*20); + CNFGTackPoly( pp, 3 ); + } + + + int x, y; + for( y = 0; y < 256; y++ ) + for( x = 0; x < 256; x++ ) + randomtexturedata[x+y*256] = x | ((x*394543L+y*355+iframeno)<<8); + CNFGBlitImage( randomtexturedata, 100, 300, 256, 256 ); + + + + frames++; + CNFGSwapBuffers(); + + ThisTime = OGGetAbsoluteTime(); + if( ThisTime > LastFPSTime + 1 ) + { + printf( "FPS: %d\n", frames ); + frames = 0; + linesegs = 0; + LastFPSTime+=1; + } + + SecToWait = .016 - ( ThisTime - LastFrameTime ); + LastFrameTime += .016; + if( SecToWait > 0 ) + OGUSleep( (int)( SecToWait * 1000000 ) ); + } + + return(0); +} + diff --git a/app/src/nativecode/rawdraw/rawdraw_sf.h b/app/src/nativecode/rawdraw/rawdraw_sf.h new file mode 100644 index 0000000..44e2737 --- /dev/null +++ b/app/src/nativecode/rawdraw/rawdraw_sf.h @@ -0,0 +1,8156 @@ +//This file was automatically generated by Makefile at https://github.com/cntools/rawdraw +//Generated from files git hash 565787ec7c65e4f8289ce4b2e7fc5ba20ab3833a on Thu Oct 3 04:35:48 PM PDT 2024 (This is not the git hash of this file) +// Copyright 2010-2021 <>< CNLohr, et. al. (Several other authors, many but not all mentioned) +// Licensed under the MIT/x11 or NewBSD License you choose. +// +// CN Foundational Graphics Main Header File. This is the main header you +// should include. See README.md for more details. + + +#ifndef _CNFG_H +#define _CNFG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Rawdraw flags: + CNFG3D -> Enable the weird 3D functionality that rawdraw has to allow you to + write apps which emit basic rawdraw primitives but look 3D! + CNFG_USE_DOUBLE_FUNCTIONS -> Use double-precision floating point for CNFG3D. + CNFGOGL -> Use an OpenGL Backend for all rawdraw functionality. + ->Caveat->If using CNFG_HAS_XSHAPE, then, we do something realy wacky. + CNFGRASTERIZER -> Software-rasterize the rawdraw calls, and, use + CNFGUpdateScreenWithBitmap to send video to webpage. + CNFGCONTEXTONLY -> Don't add any drawing functions, only opening a window to + get an OpenGL context. + + CNFG_IMPLEMENTATION -> #define this and it will make _this_ the file where the cnfg + functions actually live. + +Usually tested combinations: + * TCC On Windows and X11 (Linux) with: + - CNFGOGL on or CNFGOGL off. If CNFGOGL is off you can use + CNFG_WINDOWS_DISABLE_BATCH to disable all batching. + -or- + - CNFGRASTERIZER + + NOTE: Sometimes you can also use CNFGOGL + CNFGRASTERIZER + + * WASM driver supports both: CNFGRASTERIZER and without CNFGRASTERIZER (Recommended turn rasterizer off) + * ANDROID (But this automatically sets CNFGRASTERIZER OFF and CNFGOGL ON) + + Unusual compiler flags: + + * CNFGHTTP - Enable the HTTP server-version of rawdraw, where it renders to a website. + * CNFGHTTPSERVERONLY - if you want to use the HTTP server w/o rawdraw. You will need to implement: + - CloseEvent, HTTPCustomCallback, HTTPCustomStart, NewWebSocket, WebSocketData, WebSocketTick + * CNFG_DISABLE_HTTP_FILES - disable the HTTP file server. + +*/ + + +#include + +//Some per-platform logic. +#if defined( ANDROID ) || defined( __android__ ) + #define CNFGOGL +#endif + +#if ( defined( CNFGOGL ) || defined( __wasm__ ) ) && !defined(CNFG_HAS_XSHAPE) + + #define CNFG_BATCH 8192 //131,072 bytes. + + #if defined( ANDROID ) || defined( __android__ ) || defined( __wasm__ ) || defined( EGL_LEAN_AND_MEAN ) + #define CNFGEWGL //EGL or WebGL + #else + #define CNFGDESKTOPGL + #endif +#endif + +typedef struct { + short x, y; +} RDPoint; + +extern int CNFGPenX, CNFGPenY; +extern uint32_t CNFGBGColor; +extern uint32_t CNFGLastColor; +extern uint32_t CNFGDialogColor; //Only used for DrawBox + +//Draws text at CNFGPenX, CNFGPenY, with scale of `scale`. +void CNFGDrawText( const char * text, short scale ); + +//Determine how large a given test would be to draw. +void CNFGGetTextExtents( const char * text, int * w, int * h, int textsize ); + +//Draws a box, outline as whatever the last CNFGColor was set to but also draws +//a rectangle as a background as whatever CNFGDialogColor is set to. +void CNFGDrawBox( short x1, short y1, short x2, short y2 ); + +//To be provided by driver. Rawdraw uses colors in the format 0xRRGGBBAA +//Note that some backends do not support alpha of any kind. +//Some platforms also support alpha blending. So, be sure to set alpha to 0xFF +uint32_t CNFGColor( uint32_t RGBA ); + +//This both updates the screen, and flips, all as a single operation. +void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h ); + + +//This is only supported on a FEW architectures, but allows arbitrary +//image blitting. Note that the alpha channel behavior is different +//on different systems. +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ); + +// Only supported with CNFGOGL +#ifdef CNFGOGL +void CNFGDeleteTex( unsigned int tex ); +unsigned int CNFGTexImage( uint32_t *data, int w, int h ); +void CNFGBlitTex( unsigned int tex, int x, int y, int w, int h ); +#endif + +void CNFGTackPixel( short x1, short y1 ); +void CNFGTackSegment( short x1, short y1, short x2, short y2 ); +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ); +void CNFGTackPoly( RDPoint * points, int verts ); +void CNFGClearFrame(); +void CNFGSwapBuffers(); + +void CNFGGetDimensions( short * x, short * y ); + + +//This will setup a window. Note that w and h have special meaning. On Windows +//and X11, for instance if you set w and h to be negative, then rawdraw will not +//show the window to the user. This is useful if you just need it for some +//off-screen-rendering purpose. +// +//Return value of 0 indicates success. Nonzero indicates error. +int CNFGSetup( const char * WindowName, int w, int h ); + +void CNFGSetupFullscreen( const char * WindowName, int screen_number ); +int CNFGHandleInput(); + + +//You must provide: +void HandleKey( int keycode, int bDown ); +void HandleButton( int x, int y, int button, int bDown ); +void HandleMotion( int x, int y, int mask ); +int HandleDestroy(); // Return nonzero if you want to cancel destroy. +#ifdef ANDROID_WANT_WINDOW_TERMINATION +void HandleWindowTermination(); +#endif + +// Guaranteed to be for the current key inside HandleKey for drivers that support it +// If drivers don't support it, it is 0 +extern int CNFGLastCharacter; +extern int CNFGLastScancode; + +//Internal function for resizing rasterizer for rasterizer-mode. +void CNFGInternalResize( short x, short y ); //don't call this. + +//Not available on all systems. Use The OGL portion with care. +#ifdef CNFGOGL +void CNFGSetVSync( int vson ); +void * CNFGGetExtension( const char * extname ); +#endif + +//Also not available on all systems. Transparency. +void CNFGPrepareForTransparency(); +void CNFGDrawToTransparencyMode( int transp ); +void CNFGClearTransparencyLevel(); + +//Only available on systems that support it. +void CNFGSetLineWidth( short width ); +void CNFGChangeWindowTitle( const char * windowtitle ); +void CNFGSetWindowIconData( int w, int h, uint32_t * data ); +int CNFGSetupWMClass( const char * WindowName, int w, int h , char * wm_res_name_ , char * wm_res_class_ ); + +// Mouse related functions on systems that support it. + +// enum just in case we want more cursor shapes +typedef enum { + CNFG_CURSOR_HIDDEN, CNFG_CURSOR_ARROW, CNFG_CURSOR_LAST +} CNFGCursorShape; + +// This may put a motion event into the queue, which will call HandleMotion +void CNFGSetMousePosition( int x, int y ); + +void CNFGConfineMouse( int confined ); +void CNFGSetCursor( CNFGCursorShape shape ); + + +//If you're using a batching renderer, for instance on Android or an OpenGL +//You will need to call this function inbetewen swtiching properties of drawing. This is usually +//only needed if you calling OpenGL / OGLES functions directly and outside of CNFG. +// +//Note that these are the functions that are used on the backends which support this +//sort of thing. +#ifdef CNFG_BATCH + +//If you are not using the CNFGOGL driver, you will need to define these in your driver. +void CNFGEmitBackendTriangles( const float * vertices, const uint32_t * colors, int num_vertices ); +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ); + +//These need to be defined for the specific driver. +void CNFGClearFrame(); +void CNFGSwapBuffers(); + +void CNFGFlushRender(); //Emit any geometry (lines, squares, polys) which are slated to be rendered. +void CNFGInternalResize( short x, short y ); //Driver calls this after resize happens. +void CNFGSetupBatchInternal(); //Driver calls this after setup is complete. + +//Useful function for emitting a non-axis-aligned quad. +void CNFGEmitQuad( float cx0, float cy0, float cx1, float cy1, float cx2, float cy2, float cx3, float cy3 ); + +extern int CNFGVertPlace; +extern float CNFGVertDataV[CNFG_BATCH*3]; +extern uint32_t CNFGVertDataC[CNFG_BATCH]; +#endif + +#define CNFG_KEY_FOCUS 0xf000 + +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) + +#define CNFG_KEY_BACKSPACE 0x08 +#define CNFG_KEY_TAB 0x09 +#define CNFG_KEY_CLEAR 0x0C +#define CNFG_KEY_ENTER 0x0D +#define CNFG_KEY_SHIFT 0x10 +#define CNFG_KEY_CTRL 0x11 +#define CNFG_KEY_ALT 0x12 +#define CNFG_KEY_PAUSE 0x13 +#define CNFG_KEY_CAPS_LOCK 0x14 +#define CNFG_KEY_ESCAPE 0x1B +#define CNFG_KEY_SPACE 0x20 +#define CNFG_KEY_PAGE_UP 0x21 +#define CNFG_KEY_PAGE_DOWN 0x22 +#define CNFG_KEY_END 0x23 +#define CNFG_KEY_HOME 0x24 +#define CNFG_KEY_LEFT_ARROW 0x25 +#define CNFG_KEY_TOP_ARROW 0x26 +#define CNFG_KEY_RIGHT_ARROW 0x27 +#define CNFG_KEY_BOTTOM_ARROW 0x28 +#define CNFG_KEY_SELECT 0x29 +#define CNFG_KEY_PRINT 0x2A +#define CNFG_KEY_EXECUTE 0x2B +#define CNFG_KEY_PRINT_SCREEN 0x2C +#define CNFG_KEY_INSERT 0x2D +#define CNFG_KEY_DELETE 0x2E +#define CNFG_KEY_HELP 0x2F +#define CNFG_KEY_LEFT_SUPER 0x5B +#define CNFG_KEY_RIGHT_SUPER 0x5C +#define CNFG_KEY_NUM_0 0x60 +#define CNFG_KEY_NUM_1 0x61 +#define CNFG_KEY_NUM_2 0x62 +#define CNFG_KEY_NUM_3 0x63 +#define CNFG_KEY_NUM_4 0x64 +#define CNFG_KEY_NUM_5 0x65 +#define CNFG_KEY_NUM_6 0x66 +#define CNFG_KEY_NUM_7 0x67 +#define CNFG_KEY_NUM_8 0x68 +#define CNFG_KEY_NUM_9 0x69 +#define CNFG_KEY_NUM_MULTIPLY 0x6A +#define CNFG_KEY_NUM_ADD 0x6B +#define CNFG_KEY_NUM_SEPARATOR 0x6C +#define CNFG_KEY_NUM_SUBTRACT 0x6D +#define CNFG_KEY_NUM_DECIMAL 0x6E +#define CNFG_KEY_NUM_DIVIDE 0x6F +#define CNFG_KEY_F1 0x70 +#define CNFG_KEY_F2 0x71 +#define CNFG_KEY_F3 0x72 +#define CNFG_KEY_F4 0x73 +#define CNFG_KEY_F5 0x74 +#define CNFG_KEY_F6 0x75 +#define CNFG_KEY_F7 0x76 +#define CNFG_KEY_F8 0x77 +#define CNFG_KEY_F9 0x78 +#define CNFG_KEY_F10 0x79 +#define CNFG_KEY_F11 0x7A +#define CNFG_KEY_F12 0x7B +#define CNFG_KEY_F13 0x7C +#define CNFG_KEY_F14 0x7D +#define CNFG_KEY_F15 0x7E +#define CNFG_KEY_F16 0x7F +#define CNFG_KEY_F17 0x80 +#define CNFG_KEY_F18 0x81 +#define CNFG_KEY_F19 0x82 +#define CNFG_KEY_F20 0x83 +#define CNFG_KEY_F21 0x84 +#define CNFG_KEY_F22 0x85 +#define CNFG_KEY_F23 0x86 +#define CNFG_KEY_F24 0x87 +#define CNFG_KEY_NUM_LOCK 0x90 +#define CNFG_KEY_SCROLL_LOCK 0x91 +#define CNFG_KEY_LEFT_SHIFT 0xA0 +#define CNFG_KEY_RIGHT_SHIFT 0xA1 +#define CNFG_KEY_LEFT_CONTROL 0xA2 +#define CNFG_KEY_RIGHT_CONTROL 0xA3 +#define CNFG_KEY_LEFT_ALT 0xA4 +#define CNFG_KEY_RIGHT_ALT 0xA5 + +#elif defined( EGL_LEAN_AND_MEAN ) // doesn't have any keys +#elif defined( __android__ ) || defined( ANDROID ) // ^ +#elif defined( __wasm__ ) + +#define CNFG_KEY_BACKSPACE 8 +#define CNFG_KEY_TAB 9 +#define CNFG_KEY_CLEAR 12 +#define CNFG_KEY_ENTER 13 +#define CNFG_KEY_SHIFT 16 +#define CNFG_KEY_CTRL 17 +#define CNFG_KEY_ALT 18 +#define CNFG_KEY_PAUSE 19 +#define CNFG_KEY_CAPS_LOCK 20 +#define CNFG_KEY_ESCAPE 27 +#define CNFG_KEY_SPACE 32 +#define CNFG_KEY_PAGE_UP 33 +#define CNFG_KEY_PAGE_DOWN 34 +#define CNFG_KEY_END 35 +#define CNFG_KEY_HOME 36 +#define CNFG_KEY_LEFT_ARROW 37 +#define CNFG_KEY_TOP_ARROW 38 +#define CNFG_KEY_RIGHT_ARROW 39 +#define CNFG_KEY_BOTTOM_ARROW 40 +#define CNFG_KEY_SELECT 41 +#define CNFG_KEY_PRINT 42 +#define CNFG_KEY_EXECUTE 43 +#define CNFG_KEY_PRINT_SCREEN 44 +#define CNFG_KEY_INSERT 45 +#define CNFG_KEY_DELETE 46 +#define CNFG_KEY_HELP 47 +#define CNFG_KEY_LEFT_SUPER 91 +#define CNFG_KEY_RIGHT_SUPER 92 +#define CNFG_KEY_NUM_0 96 +#define CNFG_KEY_NUM_1 97 +#define CNFG_KEY_NUM_2 98 +#define CNFG_KEY_NUM_3 99 +#define CNFG_KEY_NUM_4 100 +#define CNFG_KEY_NUM_5 101 +#define CNFG_KEY_NUM_6 102 +#define CNFG_KEY_NUM_7 103 +#define CNFG_KEY_NUM_8 104 +#define CNFG_KEY_NUM_9 105 +#define CNFG_KEY_NUM_MULTIPLY 106 +#define CNFG_KEY_NUM_ADD 107 +#define CNFG_KEY_NUM_SEPARATOR 108 +#define CNFG_KEY_NUM_SUBTRACT 109 +#define CNFG_KEY_NUM_DECIMAL 110 +#define CNFG_KEY_NUM_DIVIDE 111 +#define CNFG_KEY_F1 112 +#define CNFG_KEY_F2 113 +#define CNFG_KEY_F3 114 +#define CNFG_KEY_F4 115 +#define CNFG_KEY_F5 116 +#define CNFG_KEY_F6 117 +#define CNFG_KEY_F7 118 +#define CNFG_KEY_F8 119 +#define CNFG_KEY_F9 120 +#define CNFG_KEY_F10 121 +#define CNFG_KEY_F11 122 +#define CNFG_KEY_F12 123 +#define CNFG_KEY_F13 124 +#define CNFG_KEY_F14 125 +#define CNFG_KEY_F15 126 +#define CNFG_KEY_F16 127 +#define CNFG_KEY_F17 128 +#define CNFG_KEY_F18 129 +#define CNFG_KEY_F19 130 +#define CNFG_KEY_F20 131 +#define CNFG_KEY_F21 132 +#define CNFG_KEY_F22 133 +#define CNFG_KEY_F23 134 +#define CNFG_KEY_F24 135 +#define CNFG_KEY_NUM_LOCK 144 +#define CNFG_KEY_SCROLL_LOCK 145 +#define CNFG_KEY_LEFT_SHIFT 160 +#define CNFG_KEY_RIGHT_SHIFT 161 +#define CNFG_KEY_LEFT_CONTROL 162 +#define CNFG_KEY_RIGHT_CONTROL 163 +#define CNFG_KEY_LEFT_ALT 164 +#define CNFG_KEY_RIGHT_ALT 165 + +#else // most likely x11 + +#define CNFG_KEY_BACKSPACE 65288 +#define CNFG_KEY_TAB 65289 +#define CNFG_KEY_CLEAR 65291 +#define CNFG_KEY_ENTER 65293 +#define CNFG_KEY_SHIFT 65505 +#define CNFG_KEY_CTRL 65507 +#define CNFG_KEY_ALT 65513 +#define CNFG_KEY_PAUSE 65299 +#define CNFG_KEY_CAPS_LOCK 65509 +#define CNFG_KEY_ESCAPE 65505 +#define CNFG_KEY_SPACE 32 +#define CNFG_KEY_PAGE_UP 65365 +#define CNFG_KEY_PAGE_DOWN 65366 +#define CNFG_KEY_END 65367 +#define CNFG_KEY_HOME 65360 +#define CNFG_KEY_LEFT_ARROW 65361 +#define CNFG_KEY_TOP_ARROW 65362 +#define CNFG_KEY_RIGHT_ARROW 65363 +#define CNFG_KEY_BOTTOM_ARROW 65364 +#define CNFG_KEY_SELECT 65376 +#define CNFG_KEY_PRINT 65377 +#define CNFG_KEY_EXECUTE 65378 +#define CNFG_KEY_PRINT_SCREEN 64797 +#define CNFG_KEY_INSERT 65379 +#define CNFG_KEY_DELETE 65535 +#define CNFG_KEY_HELP 65386 +#define CNFG_KEY_LEFT_SUPER 65515 +#define CNFG_KEY_RIGHT_SUPER 65516 +#define CNFG_KEY_NUM_0 65456 +#define CNFG_KEY_NUM_1 65457 +#define CNFG_KEY_NUM_2 65458 +#define CNFG_KEY_NUM_3 65459 +#define CNFG_KEY_NUM_4 65460 +#define CNFG_KEY_NUM_5 65461 +#define CNFG_KEY_NUM_6 65462 +#define CNFG_KEY_NUM_7 65463 +#define CNFG_KEY_NUM_8 65464 +#define CNFG_KEY_NUM_9 65465 +#define CNFG_KEY_NUM_MULTIPLY 65450 +#define CNFG_KEY_NUM_ADD 65451 +#define CNFG_KEY_NUM_SEPARATOR 65452 +#define CNFG_KEY_NUM_SUBTRACT 65453 +#define CNFG_KEY_NUM_DECIMAL 65454 +#define CNFG_KEY_NUM_DIVIDE 65455 +#define CNFG_KEY_F1 65470 +#define CNFG_KEY_F2 65471 +#define CNFG_KEY_F3 65472 +#define CNFG_KEY_F4 65473 +#define CNFG_KEY_F5 65474 +#define CNFG_KEY_F6 65475 +#define CNFG_KEY_F7 65476 +#define CNFG_KEY_F8 65477 +#define CNFG_KEY_F9 65478 +#define CNFG_KEY_F10 65479 +#define CNFG_KEY_F11 65480 +#define CNFG_KEY_F12 65481 +#define CNFG_KEY_F13 65482 +#define CNFG_KEY_F14 65483 +#define CNFG_KEY_F15 65484 +#define CNFG_KEY_F16 65485 +#define CNFG_KEY_F17 65486 +#define CNFG_KEY_F18 65487 +#define CNFG_KEY_F19 65488 +#define CNFG_KEY_F20 65489 +#define CNFG_KEY_F21 65490 +#define CNFG_KEY_F22 65491 +#define CNFG_KEY_F23 65492 +#define CNFG_KEY_F24 65493 +#define CNFG_KEY_NUM_LOCK 65407 +#define CNFG_KEY_SCROLL_LOCK 65300 +#define CNFG_KEY_LEFT_SHIFT 65505 +#define CNFG_KEY_RIGHT_SHIFT 65506 +#define CNFG_KEY_LEFT_CONTROL 65507 +#define CNFG_KEY_RIGHT_CONTROL 65508 +#define CNFG_KEY_LEFT_ALT 65513 +#define CNFG_KEY_RIGHT_ALT 65514 + +#define CNFG_X11_EXPOSE 0xff00 //65280 + +#endif + +#ifdef CNFG3D + +#ifndef __wasm__ +#include +#endif + +#ifdef CNFG_USE_DOUBLE_FUNCTIONS +#define tdCOS cos +#define tdSIN sin +#define tdTAN tan +#define tdSQRT sqrt +#else +#define tdCOS cosf +#define tdSIN sinf +#define tdTAN tanf +#define tdSQRT sqrtf +#endif + +#ifdef __wasm__ +void tdMATCOPY( float * x, const float * y ); //Copy y into x +#else +#define tdMATCOPY(x,y) memcpy( x, y, 16*sizeof(float)) +#endif + +#define tdQ_PI 3.141592653589 +#define tdDEGRAD (tdQ_PI/180.) +#define tdRADDEG (180./tdQ_PI) + + +//General Matrix Functions +void tdIdentity( float * f ); +void tdZero( float * f ); +void tdTranslate( float * f, float x, float y, float z ); //Operates ON f +void tdScale( float * f, float x, float y, float z ); //Operates ON f +void tdRotateAA( float * f, float angle, float x, float y, float z ); //Operates ON f +void tdRotateQuat( float * f, float qw, float qx, float qy, float qz ); //Operates ON f +void tdRotateEA( float * f, float x, float y, float z ); //Operates ON f +void tdMultiply( float * fin1, float * fin2, float * fout ); //Operates ON f +void tdPrint( const float * f ); +void tdTransposeSelf( float * f ); + +//Specialty Matrix Functions +void tdPerspective( float fovy, float aspect, float zNear, float zFar, float * out ); //Sets, NOT OPERATES. (FOVX=degrees) +void tdLookAt( float * m, float * eye, float * at, float * up ); //Operates ON m +//General point functions +#define tdPSet( f, x, y, z ) { f[0] = x; f[1] = y; f[2] = z; } +void tdPTransform( const float * pin, float * f, float * pout ); +void tdVTransform( const float * vin, float * f, float * vout ); +void td4Transform( float * kin, float * f, float * kout ); +void td4RTransform( float * kin, float * f, float * kout ); +void tdNormalizeSelf( float * vin ); +void tdCross( float * va, float * vb, float * vout ); +float tdDistance( float * va, float * vb ); +float tdDot( float * va, float * vb ); +#define tdPSub( x, y, z ) { (z)[0] = (x)[0] - (y)[0]; (z)[1] = (x)[1] - (y)[1]; (z)[2] = (x)[2] - (y)[2]; } +#define tdPAdd( x, y, z ) { (z)[0] = (x)[0] + (y)[0]; (z)[1] = (x)[1] + (y)[1]; (z)[2] = (x)[2] + (y)[2]; } + +//Stack Functionality +#define tdMATRIXMAXDEPTH 32 +extern float * gSMatrix; +void tdPush(); +void tdPop(); +void tdMode( int mode ); +#define tdMODELVIEW 0 +#define tdPROJECTION 1 + +//Final stage tools +void tdSetViewport( float leftx, float topy, float rightx, float bottomy, float pixx, float pixy ); +void tdFinalPoint( float * pin, float * pout ); + +float tdNoiseAt( int x, int y ); +float tdFLerp( float a, float b, float t ); +float tdPerlin2D( float x, float y ); + +#endif + +extern const unsigned char RawdrawFontCharData[1405]; +extern const unsigned short RawdrawFontCharMap[256]; + +#ifdef __cplusplus +}; +#endif + + +#if defined( ANDROID ) || defined( __android__ ) +#ifndef _CNFG_ANDROID_H +#define _CNFG_ANDROID_H + +//This file contains the additional functions that are available on the Android platform. +//In order to build rawdraw for Android, please compile CNFGEGLDriver.c with -DANDROID + +// Tricky: Android headers are confused by c++ if linking statically. +#ifdef __cplusplus +extern "C" { + int __system_property_get(const char* __name, char* __value); +}; +#endif + +extern struct android_app * gapp; +void AndroidMakeFullscreen(); +const char* AndroidGetExternalFilesDir(); +int AndroidHasPermissions(const char* perm_name); +void AndroidRequestAppPermissions(const char * perm); +void AndroidDisplayKeyboard(int pShow); +int AndroidGetUnicodeChar( int keyCode, int metaState ); +void AndroidSendToBack( int param ); + +extern int android_sdk_version; //Derived at start from property ro.build.version.sdk +extern int android_width, android_height; +extern int UpdateScreenWithBitmapOffsetX; +extern int UpdateScreenWithBitmapOffsetY; +extern void (*HandleCustomEventCallback)(); +extern void (*HandleWindowTermination)(); + + +// If you need them, these are the names of raw EGL symbols. +//extern EGLDisplay egl_display; +//extern EGLSurface egl_surface; +//extern EGLContext egl_context; +//extern EGLConfig egl_config; + + +//You must implement these. +void HandleResume(); +void HandleSuspend(); + + +//Departures: + +// HandleMotion's "mask" parameter is actually just an index, not a mask + +// CNFGSetup / CNFGSetupFullScreen only controls whether or not the navigation +// decoration is removed. Fullscreen means *full screen* To choose fullscreen +// or not fullscrene, modify, in your AndroidManifest.xml file, the application +// section to either contain or not contain: +// android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + + +// For debugging: + +#if defined( ANDROID ) && !defined( __cplusplus ) + +#include + +static inline void PrintClassOfObject( jobject bundle ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jclass myclass = env->GetObjectClass( envptr, bundle ); + jmethodID mid = env->GetMethodID( envptr, myclass, "getClass", "()Ljava/lang/Class;"); + jobject clsObj = env->CallObjectMethod( envptr, bundle, mid ); + jclass clazzz = env->GetObjectClass( envptr, clsObj ); + mid = env->GetMethodID(envptr, clazzz, "getName", "()Ljava/lang/String;"); + jstring strObj = (jstring)env->CallObjectMethod( envptr, clsObj, mid); + const char * name = env->GetStringUTFChars( envptr, strObj, 0); + printf( "Class type: %s\n", name ); + + env->DeleteLocalRef( envptr, myclass ); + env->DeleteLocalRef( envptr, clsObj ); + env->DeleteLocalRef( envptr, clazzz ); + env->ReleaseStringUTFChars(envptr, strObj, name); + env->DeleteLocalRef( envptr, strObj ); +} + +static inline void PrintObjectString( jobject bundle ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jclass myclass = env->GetObjectClass( envptr, bundle ); + jmethodID toStringMethod = env->GetMethodID( envptr, myclass, "toString", "()Ljava/lang/String;"); + jstring strObjDescr = (jstring)env->CallObjectMethod( envptr, bundle, toStringMethod); + const char *descr = env->GetStringUTFChars( envptr, strObjDescr, 0); + printf( "String: %s\n", descr ); + + env->DeleteLocalRef( envptr, myclass ); + env->ReleaseStringUTFChars( envptr, strObjDescr, descr ); + env->DeleteLocalRef( envptr, strObjDescr ); +} + + +static inline void DumpObjectClassProperties( jobject objToDump ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jclass mpclass = env->GetObjectClass( envptr, objToDump ); + + jmethodID midGetClass = env->GetMethodID( envptr, mpclass, "getClass", "()Ljava/lang/Class;"); + jclass ClassClass = env->FindClass(envptr, "java/lang/Class"); + jobject clsObj = env->CallObjectMethod( envptr, objToDump, midGetClass); + + jmethodID gnid = env->GetMethodID(envptr, ClassClass, "getName", "()Ljava/lang/String;"); + jstring nameObj = (jstring)env->CallObjectMethod( envptr, clsObj, gnid); + const char *name = env->GetStringUTFChars( envptr, nameObj, 0); + printf( "Class Name: %s\n", name ); + env->ReleaseStringUTFChars(envptr, nameObj, name); + env->DeleteLocalRef( envptr, nameObj ); + + jmethodID getMethodsMethod = env->GetMethodID( envptr, ClassClass, "getMethods","()[Ljava/lang/reflect/Method;"); + jobject jobjArray = env->CallObjectMethod( envptr, clsObj, getMethodsMethod ); + + jclass MethodType = env->FindClass(envptr, "java/lang/reflect/Method"); + jclass TypeType = env->FindClass(envptr, "java/lang/reflect/Type"); + jmethodID tnamemid = env->GetMethodID( envptr, TypeType, "getTypeName", "()Ljava/lang/String;" ); + + jmethodID getFieldsMethod = env->GetMethodID( envptr, ClassClass, "getFields","()[Ljava/lang/reflect/Field;"); + jobject jobjArrayFields = env->CallObjectMethod( envptr, clsObj, getFieldsMethod ); + + jsize len = env->GetArrayLength(envptr, jobjArray); + jsize i; + printf( "Methods:\n" ); + + for (i = 0 ; i < len ; i++) { + jobject _strMethod = env->GetObjectArrayElement( envptr, jobjArray, i ) ; + jclass _methodClazz = env->GetObjectClass(envptr, _strMethod) ; + jmethodID mid = env->GetMethodID(envptr, _methodClazz , "getName", "()Ljava/lang/String;" ); + jmethodID getGenericParameterTypes = env->GetMethodID( envptr, _methodClazz, "getGenericParameterTypes","()[Ljava/lang/reflect/Type;"); + jmethodID getReturnTypeMethod = env->GetMethodID( envptr, _methodClazz, "getGenericReturnType","()Ljava/lang/reflect/Type;"); + jstring _name = (jstring)env->CallObjectMethod( envptr, _strMethod , mid ) ; + const char *str = env->GetStringUTFChars(envptr, _name, 0); + printf(" %s( ", str); + + jobject types = env->CallObjectMethod( envptr, _strMethod, getGenericParameterTypes ); + jsize mlen = env->GetArrayLength(envptr, types); + jsize mi; + for( mi = 0; mi < mlen; mi++ ) + { + jobject typeo = env->GetObjectArrayElement( envptr, types, mi ); + jstring _tn = (jstring)env->CallObjectMethod( envptr, typeo, tnamemid ); + const char * str = env->GetStringUTFChars(envptr, _tn, 0); + printf("%s%s ", str, (mi == mlen-1)?"":"," ); + env->ReleaseStringUTFChars(envptr, _tn, str); + env->DeleteLocalRef( envptr, _tn ); + env->DeleteLocalRef( envptr, typeo ); + } + + jobject rtype = env->CallObjectMethod( envptr, _strMethod, getReturnTypeMethod ); + jstring _tn = (jstring)env->CallObjectMethod( envptr, rtype, tnamemid ); + const char * strret = env->GetStringUTFChars(envptr, _tn, 0); + printf(") -> %s\n", strret); + env->ReleaseStringUTFChars(envptr, _tn, strret); + env->DeleteLocalRef( envptr, _tn ); + env->DeleteLocalRef( envptr, rtype ); + + + env->DeleteLocalRef( envptr, types ); + env->ReleaseStringUTFChars(envptr, _name, str); + env->DeleteLocalRef( envptr, _methodClazz ); + env->DeleteLocalRef( envptr, _strMethod ); + env->DeleteLocalRef( envptr, _name ); + } + len = env->GetArrayLength(envptr, jobjArrayFields); + printf( "Fields:\n" ); + for ( i = 0; i < len; i++) { + jobject _strField = env->GetObjectArrayElement( envptr, jobjArrayFields, i) ; + jclass _methodClazz = env->GetObjectClass(envptr, _strField ); + jmethodID mid = env->GetMethodID(envptr, _methodClazz , "getName", "()Ljava/lang/String;") ; + jstring _name = (jstring)env->CallObjectMethod( envptr, _strField , mid ) ; + const char *str = env->GetStringUTFChars(envptr, _name, 0); + + jmethodID getTypeMethod = env->GetMethodID( envptr, _methodClazz, "getGenericType","()Ljava/lang/reflect/Type;"); + jobject rtype = env->CallObjectMethod( envptr, _strField, getTypeMethod ); + jstring _tn = (jstring)env->CallObjectMethod( envptr, rtype, tnamemid ); + const char * strret = env->GetStringUTFChars(envptr, _tn, 0); + + printf(" %s -> %s\n", str, strret ); + env->ReleaseStringUTFChars(envptr, _name, str); + env->ReleaseStringUTFChars(envptr, _tn, strret); + env->DeleteLocalRef( envptr, _methodClazz ); + env->DeleteLocalRef( envptr, _strField ); + env->DeleteLocalRef( envptr, _name ); + env->DeleteLocalRef( envptr, _tn ); + } + env->DeleteLocalRef( envptr, TypeType ); + env->DeleteLocalRef( envptr, MethodType ); + env->DeleteLocalRef( envptr, clsObj ); + env->DeleteLocalRef( envptr, ClassClass ); + env->DeleteLocalRef( envptr, jobjArrayFields ); + env->DeleteLocalRef( envptr, jobjArray ); + + env->DeleteLocalRef( envptr, mpclass ); +} +#endif + +#endif + + +#endif + +#ifdef CNFG_IMPLEMENTATION +//Include this file to get all of rawdraw. You usually will not +//want to include this in your build, but instead, #include "CNFG.h" +//after #define CNFG_IMPLEMENTATION in one of your C files. + +// Defined here for universal definition +int CNFGLastCharacter = 0; +int CNFGLastScancode = 0; + +#if defined( CNFGHTTP ) +//Copyright 2015-2021 <>< Charles Lohr Under the MIT/x11 License, NewBSD License or +// ColorChord License. You Choose. This file mostly based on `cnhttp` from cntools. + +#ifdef CNFGHTTP + +//Pull from a buffer +#ifndef CNFGHTTP_LIVE_FS +#define USE_RAM_MFS +#endif + +//single_file_http.c base from https://github.com/cntools/httptest. +//scroll to bottom for implementation. + +#ifndef CUSTOM_HTTPHEADER_CODE +#define CUSTOM_HTTPHEADER_CODE PushString("Access-Control-Allow-Origin: *\r\n"); +#endif + +/* public api for steve reid's public domain SHA-1 implementation */ +/* this file is in the public domain */ + + +#include + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + uint8_t buffer[64]; +} RD_SHA1_CTX; + +#define RD_SHA1_DIGEST_SIZE 20 + +void static RD_SHA1_Init(RD_SHA1_CTX* context); +void static RD_SHA1_Update(RD_SHA1_CTX* context, const uint8_t* data, const unsigned long len); +void static RD_SHA1_Final(uint8_t digest[RD_SHA1_DIGEST_SIZE],RD_SHA1_CTX* context); //WARNINGThe parameters are flipped here. (CNL) + +//Not to be confused with MFS for the AVR. + +#ifndef _MFS_H +#define _MFS_H + + +#ifndef USE_RAM_MFS +#include +#endif + +#define MFS_SECTOR 256 +#define MFS_FILENAMELEN 32-8 +#define MFS_FILE_COMPRESSED_MEMORY (-2) + +//Format: +// [FILE NAME (24)] [Start (4)] [Len (4)] +// NOTE: Filename must be null-terminated within the 24. +struct MFSFileEntry +{ + char name[MFS_FILENAMELEN]; + uint32_t start; //From beginning of mfs thing. + uint32_t len; +}; + + +struct MFSFileInfo +{ + uint32_t filelen; +#ifdef USE_RAM_MFS + uint32_t offset; +#else + FILE * file; +#endif +}; + + + +//Returns 0 on succses. +//Returns size of file if non-empty +//If positive, populates mfi. +//Returns -1 if can't find file or reached end of file list. +int8_t MFSOpenFile( const char * fname, struct MFSFileInfo * mfi ); +int32_t MFSReadSector( uint8_t* data, struct MFSFileInfo * mfi ); //returns # of bytes left in file. +void MFSClose( struct MFSFileInfo * mfi ); + + + +#endif + + +#ifndef _CNHTTP_H +#define _CNHTTP_H + +#include + +extern struct HTTPConnection * curhttp; +extern uint8_t * curdata; +extern uint16_t curlen; +extern uint8_t wsmask[4]; +extern uint8_t wsmaskplace; + + + +uint8_t WSPOPMASK(); +#define HTTPPOP (*curdata++) + +//You must provide this. +void HTTPCustomStart( ); +void HTTPCustomCallback( ); //called when we can send more data +void WebSocketData( int len ); +void WebSocketTick( ); +void WebSocketNew(); +void HTTPHandleInternalCallback( ); +uint8_t hex2byte( const char * c ); +void NewWebSocket(); +void et_espconn_disconnect( int socket ); + +//Internal Functions +void HTTPTick( uint8_t timedtick ); +int URLDecode( char * decodeinto, int maxlen, const char * buf ); +void WebSocketGotData( uint8_t c ); +void WebSocketTickInternal(); +void WebSocketSend( uint8_t * data, int size ); + +//Host-level functions +void my_base64_encode(const unsigned char *data, unsigned int input_length, uint8_t * encoded_data ); +void Uint32To10Str( char * out, uint32_t dat ); +void http_recvcb(int conn, char *pusrdata, unsigned short length); +void http_disconnetcb(int conn); +int httpserver_connectcb( int socket ); // return which HTTP it is. -1 for failure +void DataStartPacket(); +extern uint8_t * databuff_ptr; +void PushString( const char * data ); +void PushByte( uint8_t c ); +void PushBlob( const uint8_t * datam, int len ); +int TCPCanSend( int socket, int size ); +int TCPDoneSend( int socket ); +int EndTCPWrite( int socket ); + +#define HTTP_CONNECTIONS 50 +#ifndef MAX_HTTP_PATHLEN +#define MAX_HTTP_PATHLEN 80 +#endif +#define HTTP_SERVER_TIMEOUT 500 + + +#define HTTP_STATE_NONE 0 +#define HTTP_STATE_WAIT_METHOD 1 +#define HTTP_STATE_WAIT_PATH 2 +#define HTTP_STATE_WAIT_PROTO 3 + +#define HTTP_STATE_WAIT_FLAG 4 +#define HTTP_STATE_WAIT_INFLAG 5 +#define HTTP_STATE_DATA_XFER 7 +#define HTTP_STATE_DATA_WEBSOCKET 8 + +#define HTTP_WAIT_CLOSE 15 + +struct HTTPConnection +{ + uint8_t state:4; + uint8_t state_deets; + + //Provides path, i.e. "/index.html" but, for websockets, the last + //32 bytes of the buffer are used for the websockets key. + uint8_t pathbuffer[MAX_HTTP_PATHLEN]; + uint8_t is_dynamic:1; + uint16_t timeout; + + union data_t + { + struct MFSFileInfo filedescriptor; + struct UserData { uint16_t a, b, c; } user; + struct UserDataPtr { void * v; } userptr; + } data; + + void * rcb; + void * rcbDat; //For websockets primarily. + void * ccb; //Close callback (used for websockets, primarily) + + uint32_t bytesleft; + uint32_t bytessofar; + + uint8_t is404:1; + uint8_t isdone:1; + uint8_t isfirst:1; + uint8_t keep_alive:1; + uint8_t need_resend:1; + uint8_t send_pending:1; //If we can send data, we should? + uint8_t is_gzip:1; + + int socket; + uint8_t corked_data[4096]; + int corked_data_place; +}; + +extern struct HTTPConnection HTTPConnections[HTTP_CONNECTIONS]; + +#endif + +#ifndef _HTTP_BSD_H +#define _HTTP_BSD_H + + +//Call this to start your webserver. +int RunHTTP( int port ); +int TickHTTP(); //returns -1 if problem. + +//For running on a BSD Sockets System +int htsend( int socket, uint8_t * data, int datact ); +void et_espconn_disconnect( int socket ); +void http_recvcb(int whichhttp, char *pusrdata, unsigned short length); +void http_disconnetcb(int whichhttp); +int httpserver_connectcb( int socket ); // return which HTTP it is. -1 for failure +void DataStartPacket(); +extern uint8_t * databuff_ptr; +void PushBlob( const uint8_t * data, int len ); +void PushByte( uint8_t c ); +void PushString( const char * data ); +int TCPCanSend( int socket, int size ); +int TCPDoneSend( int socket ); +int EndTCPWrite( int socket ); +void TermHTTPServer(); + +extern int cork_binary_rx; + +#endif + +//Copyright 2012-2016 <>< Charles Lohr Under the MIT/x11 License, NewBSD License or ColorChord License. You Choose. + +#include +#include + +struct HTTPConnection HTTPConnections[HTTP_CONNECTIONS]; + +#define HTDEBUG( x, ... ) printf( x, ##__VA_ARGS__ ) +//#define HTDEBUG( x... ) + +//#define ISKEEPALIVE "keep-alive" +#define ISKEEPALIVE "close" + +struct HTTPConnection HTTPConnections[HTTP_CONNECTIONS]; +struct HTTPConnection * curhttp; +uint8_t * curdata; +uint16_t curlen; +uint8_t wsmask[4]; +uint8_t wsmaskplace; + + +void CloseEvent(); +void InternalStartHTTP( ); +void HTTPHandleInternalCallback( ); + +void HTTPClose( ) +{ + //This is dead code, but it is a testament to Charles. + //Do not do this here. Wait for the ESP to tell us the + //socket is successfully closed. + //curhttp->state = HTTP_STATE_NONE; + curhttp->state = HTTP_WAIT_CLOSE; + et_espconn_disconnect( curhttp->socket ); + CloseEvent(); +} + + +void HTTPGotData( ) +{ + uint8_t c; + curhttp->timeout = 0; + while( curlen-- ) + { + c = HTTPPOP; + // sendhex2( h->state ); sendchr( ' ' ); + + switch( curhttp->state ) + { + case HTTP_STATE_WAIT_METHOD: + if( c == ' ' ) + { + curhttp->state = HTTP_STATE_WAIT_PATH; + curhttp->state_deets = 0; + } + break; + case HTTP_STATE_WAIT_PATH: + curhttp->pathbuffer[curhttp->state_deets++] = c; + if( curhttp->state_deets == MAX_HTTP_PATHLEN ) + { + curhttp->state_deets--; + } + + if( c == ' ' ) + { + //Tricky: If we're a websocket, we need the whole header. + curhttp->pathbuffer[curhttp->state_deets-1] = 0; + curhttp->state_deets = 0; + + if( strncmp( (const char*)curhttp->pathbuffer, "/d/ws", 5 ) == 0 ) + { + curhttp->state = HTTP_STATE_DATA_WEBSOCKET; + curhttp->state_deets = 0; + } + else + { + curhttp->state = HTTP_STATE_WAIT_PROTO; + } + } + break; + case HTTP_STATE_WAIT_PROTO: + if( c == '\n' ) + { + curhttp->state = HTTP_STATE_WAIT_FLAG; + } + break; + case HTTP_STATE_WAIT_FLAG: + if( c == '\n' ) + { + curhttp->state = HTTP_STATE_DATA_XFER; + InternalStartHTTP( ); + } + else if( c != '\r' ) + { + curhttp->state = HTTP_STATE_WAIT_INFLAG; + } + break; + case HTTP_STATE_WAIT_INFLAG: + if( c == '\n' ) + { + curhttp->state = HTTP_STATE_WAIT_FLAG; + curhttp->state_deets = 0; + } + break; + case HTTP_STATE_DATA_XFER: + //Ignore any further data? + curlen = 0; + break; + case HTTP_STATE_DATA_WEBSOCKET: + WebSocketGotData( c ); + break; + case HTTP_WAIT_CLOSE: + if( curhttp->keep_alive ) + { + curhttp->state = HTTP_STATE_WAIT_METHOD; + } + else + { + HTTPClose( ); + } + break; + default: + break; + }; + } + +} + + +static void DoHTTP( uint8_t timed ) +{ + switch( curhttp->state ) + { + case HTTP_STATE_NONE: //do nothing if no state. + curhttp->send_pending = 0; + break; + case HTTP_STATE_DATA_XFER: + curhttp->send_pending = 1; + if( TCPCanSend( curhttp->socket, 1300 ) ) //TCPDoneSend + { + if( curhttp->is_dynamic ) + { + HTTPCustomCallback( ); + } + else + { + HTTPHandleInternalCallback( ); + } + } + break; + case HTTP_WAIT_CLOSE: + curhttp->send_pending = 0; + if( TCPDoneSend( curhttp->socket ) ) + { + if( curhttp->keep_alive ) + { + curhttp->state = HTTP_STATE_WAIT_METHOD; + } + else + { + HTTPClose( ); + } + } + break; + case HTTP_STATE_DATA_WEBSOCKET: + curhttp->send_pending = 0; + if( TCPCanSend( curhttp->socket, 1300 ) ) //TCPDoneSend + { + WebSocketTickInternal(); + } + break; + default: + if( timed ) + { + if( curhttp->timeout++ > HTTP_SERVER_TIMEOUT ) + { + HTTPClose( ); + } + } + } +} + +void HTTPTick( uint8_t timed ) +{ + uint8_t i; + for( i = 0; i < HTTP_CONNECTIONS; i++ ) + { + if( curhttp ) { HTDEBUG( "HTTPRXQ\n" ); break; } + curhttp = &HTTPConnections[i]; + DoHTTP( timed ); + curhttp = 0; + } +} + +void HTTPHandleInternalCallback( ) +{ + uint16_t i, bytestoread; + + if( curhttp->isdone ) + { + HTTPClose( ); + return; + } + if( curhttp->is404 ) + { + DataStartPacket(); + PushString("HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\nFile not found."); + EndTCPWrite( curhttp->socket ); + curhttp->isdone = 1; + return; + } + if( curhttp->isfirst ) + { + char stto[10]; + uint8_t slen = strlen( curhttp->pathbuffer ); + const char * k; + + DataStartPacket();; + //TODO: Content Length? MIME-Type? + PushString("HTTP/1.1 200 Ok\r\n"); + +#ifdef CUSTOM_HTTPHEADER_CODE + CUSTOM_HTTPHEADER_CODE +#endif + + if( curhttp->bytesleft < 0xfffffffe ) + { + PushString("Connection: "ISKEEPALIVE"\r\nContent-Length: "); + Uint32To10Str( stto, curhttp->bytesleft ); + PushBlob( stto, strlen( stto ) ); + curhttp->keep_alive = 1; + } + else + { + PushString("Connection: close"); + curhttp->keep_alive = 0; + } + + PushString( "\r\nContent-Type: " ); + //Content-Type? + while( slen && ( curhttp->pathbuffer[--slen] != '.' ) ); + k = &curhttp->pathbuffer[slen+1]; + + if( strcmp( k, "mp3" ) == 0 ) PushString( "audio/mpeg3" ); + else if( strcmp( k, "jpg" ) == 0 ) PushString( "image/jpeg" ); + else if( strcmp( k, "png" ) == 0 ) PushString( "image/png" ); + else if( strcmp( k, "css" ) == 0 ) PushString( "text/css" ); + else if( strcmp( k, "js" ) == 0 ) PushString( "text/javascript" ); + else if( strcmp( k, "gz" ) == 0 ) PushString( "text/plain\r\nContent-Encoding: gzip\r\nCache-Control: public, max-age=3600" ); + else if( curhttp->bytesleft == 0xfffffffe ) PushString( "text/plain" ); + else PushString( "text/html" ); + + if( curhttp->is_gzip ) PushString( "\r\nContent-Encoding: gzip" ); + + PushString( "\r\n\r\n" ); + EndTCPWrite( curhttp->socket ); + curhttp->isfirst = 0; + + return; + } + + DataStartPacket(); + + for( i = 0; i < 2 && curhttp->bytesleft; i++ ) + { + int bpt = curhttp->bytesleft; + if( bpt > MFS_SECTOR ) bpt = MFS_SECTOR; + curhttp->bytesleft = MFSReadSector( databuff_ptr, &curhttp->data.filedescriptor ); + databuff_ptr += bpt; + } + + EndTCPWrite( curhttp->socket ); + + if( !curhttp->bytesleft ) + curhttp->isdone = 1; +} + +void InternalStartHTTP( ) +{ + int32_t clusterno; + int8_t i; + char * path = &curhttp->pathbuffer[0]; + + curhttp->is_gzip = 0; + + if( curhttp->pathbuffer[0] == '/' ) + path++; + + if( path[0] == 'd' && path[1] == '/' ) + { + curhttp->is_dynamic = 1; + curhttp->isfirst = 1; + curhttp->isdone = 0; + curhttp->is404 = 0; + HTTPCustomStart(); + return; + } + + if( !path[0] ) + { + path = "index.html"; + } + + for( i = 0; path[i]; i++ ) + if( path[i] == '?' ) path[i] = 0; + + i = MFSOpenFile( path, &curhttp->data.filedescriptor ); + + curhttp->bytessofar = 0; + curhttp->is_gzip = 0; + + if( i == MFS_FILE_COMPRESSED_MEMORY ) + { + curhttp->is_gzip = 1; + } + else if( i < 0 ) + { + HTDEBUG( "404(%s)\n", path ); + curhttp->is404 = 1; + curhttp->isfirst = 1; + curhttp->isdone = 0; + curhttp->is_dynamic = 0; + curhttp->bytesleft = 0; + return; + } + + curhttp->isfirst = 1; + curhttp->isdone = 0; + curhttp->is404 = 0; + curhttp->is_dynamic = 0; + curhttp->bytesleft = curhttp->data.filedescriptor.filelen; +} + + +void http_disconnetcb(int conn ) { + int r = conn; + if( r>=0 ) + { + if( !HTTPConnections[r].is_dynamic ) MFSClose( &HTTPConnections[r].data.filedescriptor ); + HTTPConnections[r].state = 0; + } +} + + +void http_recvcb(int conn, char *pusrdata, unsigned short length) +{ + int whichhttp = conn; + //Though it might be possible for this to interrupt the other + //tick task, I don't know if this is actually a probelem. + //I'm adding this back-up-the-register just in case. + if( curhttp ) { HTDEBUG( "Unexpected Race Condition\n" ); return; } + + curhttp = &HTTPConnections[whichhttp]; + curdata = (uint8_t*)pusrdata; + curlen = length; + HTTPGotData(); + curhttp = 0 ; + +} + +int httpserver_connectcb( int socket ) +{ + int i; + + for( i = 0; i < HTTP_CONNECTIONS; i++ ) + { + if( HTTPConnections[i].state == 0 ) + { + HTTPConnections[i].socket = socket; + HTTPConnections[i].state = HTTP_STATE_WAIT_METHOD; + break; + } + } + if( i == HTTP_CONNECTIONS ) + { + return -1; + } + + return i; +} + + +int URLDecode( char * decodeinto, int maxlen, const char * buf ) +{ + int i = 0; + + for( ; buf && *buf; buf++ ) + { + char c = *buf; + if( c == '+' ) + { + decodeinto[i++] = ' '; + } + else if( c == '?' || c == '&' ) + { + break; + } + else if( c == '%' ) + { + if( *(buf+1) && *(buf+2) ) + { + decodeinto[i++] = hex2byte( buf+1 ); + buf += 2; + } + } + else + { + decodeinto[i++] = c; + } + if( i >= maxlen -1 ) break; + + } + decodeinto[i] = 0; + return i; +} + + +#ifndef RD_SHA1_HASH_LEN +#define RD_SHA1_HASH_LEN RD_SHA1_DIGEST_SIZE +#endif + +void WebSocketGotData( uint8_t c ) +{ + switch( curhttp->state_deets ) + { + case 0: + { + int i = 0; + char inkey[120]; + unsigned char hash[RD_SHA1_HASH_LEN]; + RD_SHA1_CTX c; + int inkeylen = 0; + + curhttp->is_dynamic = 1; + while( curlen > 20 ) + { + curdata++; curlen--; + if( strncmp( curdata, "Sec-WebSocket-Key: ", 19 ) == 0 ) + { + break; + } + } + + if( curlen <= 21 ) + { + HTDEBUG( "No websocket key found.\n" ); + curhttp->state = HTTP_WAIT_CLOSE; + return; + } + + curdata+= 19; + curlen -= 19; + + +#define WS_KEY_LEN 36 +#define WS_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +#define WS_RETKEY_SIZEM1 32 + + while( curlen > 1 ) + { + uint8_t lc = *(curdata++); + inkey[i] = lc; + curlen--; + if( lc == '\r' ) + { + inkey[i] = 0; + break; + } + i++; + if( i >= sizeof( inkey ) - WS_KEY_LEN - 5 ) + { + HTDEBUG( "Websocket key too big.\n" ); + curhttp->state = HTTP_WAIT_CLOSE; + return; + } + } + if( curlen <= 1 ) + { + HTDEBUG( "Invalid websocket key found.\n" ); + curhttp->state = HTTP_WAIT_CLOSE; + return; + } + + if( i + WS_KEY_LEN + 1 >= sizeof( inkey ) ) + { + HTDEBUG( "WSKEY Too Big.\n" ); + curhttp->state = HTTP_WAIT_CLOSE; + return; + } + + memcpy( &inkey[i], WS_KEY, WS_KEY_LEN + 1 ); + i += WS_KEY_LEN; + RD_SHA1_Init( &c ); + RD_SHA1_Update( &c, inkey, i ); + RD_SHA1_Final( hash, &c ); + +#if (WS_RETKEY_SIZE > MAX_HTTP_PATHLEN - 10 ) +#error MAX_HTTP_PATHLEN too short. +#endif + + my_base64_encode( hash, RD_SHA1_HASH_LEN, curhttp->pathbuffer + (MAX_HTTP_PATHLEN-WS_RETKEY_SIZEM1) ); + + curhttp->bytessofar = 0; + curhttp->bytesleft = 0; + + NewWebSocket(); + //Respond... + curhttp->state_deets = 1; + break; + } + case 1: + if( c == '\n' ) curhttp->state_deets = 2; + break; + case 2: + if( c == '\r' ) curhttp->state_deets = 3; + else curhttp->state_deets = 1; + break; + case 3: + if( c == '\n' ) curhttp->state_deets = 4; + else curhttp->state_deets = 1; + break; + case 5: //Established connection. + { + //XXX TODO: Seems to malfunction on large-ish packets. I know it has problems with 140-byte payloads. + + do + { + if( curlen < 5 ) //Can't interpret packet. + break; + + uint8_t fin = c & 1; + uint8_t opcode = c << 4; + uint16_t payloadlen = *(curdata++); + + curlen--; + if( !(payloadlen & 0x80) ) + { + HTDEBUG( "Unmasked packet (%d)\n", payloadlen ); + curhttp->state = HTTP_WAIT_CLOSE; + break; + } + + if( opcode == 128 ) + { + //Close connection. + //HTDEBUG( "CLOSE\n" ); + //curhttp->state = HTTP_WAIT_CLOSE; + //break; + } + + payloadlen &= 0x7f; + if( payloadlen == 127 ) + { + //Very long payload. + //Not supported. + HTDEBUG( "Unsupported payload packet.\n" ); + curhttp->state = HTTP_WAIT_CLOSE; + break; + } + else if( payloadlen == 126 ) + { + payloadlen = (curdata[0] << 8) | curdata[1]; + curdata += 2; + curlen -= 2; + } + wsmask[0] = curdata[0]; + wsmask[1] = curdata[1]; + wsmask[2] = curdata[2]; + wsmask[3] = curdata[3]; + curdata += 4; + curlen -= 4; + wsmaskplace = 0; + + //XXX Warning: When packets get larger, they may split the + //websockets packets into multiple parts. We could handle this + //but at the cost of prescious RAM. I am chosing to just drop those + //packets on the floor, and restarting the connection. + if( curlen < payloadlen ) + { + extern int cork_binary_rx; + cork_binary_rx = 1; + //HTDEBUG( "Websocket Fragmented. %d %d\n", curlen, payloadlen ); + //curhttp->state = HTTP_WAIT_CLOSE; + HTDEBUG( "Websocket Fragmented. %d %d\n", curlen, payloadlen ); + curhttp->state = HTTP_WAIT_CLOSE; + return; + } + + char * newcurdata = curdata + payloadlen + 1; + WebSocketData( payloadlen ); + curlen -= payloadlen; + curdata = newcurdata; + } + while( curlen > 5 ); + break; + } + default: + break; + } +} + +void WebSocketTickInternal() +{ + switch( curhttp->state_deets ) + { + case 4: //Has key full HTTP header, etc. wants response. + DataStartPacket();; + PushString( "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " ); + PushString( curhttp->pathbuffer + (MAX_HTTP_PATHLEN-WS_RETKEY_SIZEM1) ); + PushString( "\r\n\r\n" ); + EndTCPWrite( curhttp->socket ); + curhttp->state_deets = 5; + curhttp->keep_alive = 0; + break; + case 5: + WebSocketTick(); + break; + } +} + +void WebSocketSend( uint8_t * data, int size ) +{ + DataStartPacket();; + PushByte( 0x82 ); //0x81 is text. + if( size >= 126 ) + { + PushByte( 0x00 | 126 ); + PushByte( size>>8 ); + PushByte( size&0xff ); + } + else + { + PushByte( 0x00 | size ); + } + PushBlob( data, size ); + EndTCPWrite( curhttp->socket ); + curhttp->send_pending = 1; +} + +uint8_t WSPOPMASK() +{ + uint8_t mask = wsmask[wsmaskplace]; + wsmaskplace = (wsmaskplace+1)&3; + return (*curdata++)^(mask); +} + + + + + + +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#include +#define socklen_t uint32_t +#define SHUT_RDWR SD_BOTH +#define MSG_NOSIGNAL 0 +#else +#define closesocket close +#include +#include +#include +#include +#include +uint16_t htons(uint16_t hostshort); +#endif + +#include +#include +#include +#include + +static int serverSocket; + +uint8_t * databuff_ptr; +uint8_t databuff[1536]; +int cork_binary_rx; + + +#ifndef HTTP_POLL_TIMEOUT +#define HTTP_POLL_TIMEOUT 0 //This is a little weird, it's an adaptation from cnhttp. +#endif + + +#define DESTROY_SOCKETS_LIST 200 +int destroy_sockets[DESTROY_SOCKETS_LIST]; +int destroy_socket_head = 0; +int sockets[HTTP_CONNECTIONS]; + +void et_espconn_disconnect( int socket ) +{ + shutdown( socket, SHUT_RDWR ); + int i; + //printf( "Shut: %d\n", socket ); + for( i = 0; i < HTTP_CONNECTIONS; i++ ) + { + if( sockets[i] == socket ) + { + http_disconnetcb( i ); + sockets[i] = 0; + } + } + + if( destroy_sockets[destroy_socket_head] ) closesocket( destroy_sockets[destroy_socket_head] ); + destroy_sockets[destroy_socket_head] = socket; + destroy_socket_head = (destroy_socket_head+1)%DESTROY_SOCKETS_LIST; + +} + +void DataStartPacket() +{ + databuff_ptr = databuff; +} + +void PushByte( uint8_t c ) +{ + if( databuff_ptr - databuff + 1 >= sizeof( databuff ) ) return; + *(databuff_ptr++) = c; +} + +void PushBlob( const uint8_t * data, int len ) +{ + if( databuff_ptr - databuff + len >= sizeof( databuff ) ) return; + memcpy( databuff_ptr, data, len ); + databuff_ptr += len; +} + +void PushString( const char * data ) +{ + int len = strlen( data ); + if( databuff_ptr - databuff + len >= sizeof( databuff ) ) return; + memcpy( databuff_ptr, data, len ); + databuff_ptr += len; +} + +int TCPCanSend( int socket, int size ) +{ + fd_set write_fd_set; + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO (&write_fd_set); + FD_SET (socket, &write_fd_set); + + int r = select (FD_SETSIZE, NULL, &write_fd_set, NULL, &tv); + if (r < 0) + { + perror ("select"); + return -1; + } + return r; +} + + +int TCPCanRead( int sock ) +{ + fd_set read_fd_set; + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO (&read_fd_set); + FD_SET (sock, &read_fd_set); + + int r; + r = select (FD_SETSIZE, &read_fd_set, NULL, NULL, &tv); + if (r < 0) + { + perror ("select"); + return -1; + } + return r; +} + + +int TCPException( int sock ) +{ + int error_code; + int error_code_size = sizeof(error_code); + getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&error_code, &error_code_size); + if( error_code >= 0 ) return 0; + else return 1; +} + +int TCPDoneSend( int socket ) +{ + return TCPCanSend( socket, 1 ); +} + + +int EndTCPWrite( int socket ) +{ + int r = send( socket, databuff, databuff_ptr-databuff, MSG_NOSIGNAL ); + databuff_ptr = databuff; + return r; +} + +void TermHTTPServer() +{ + shutdown( serverSocket, SHUT_RDWR ); +} + +int TickHTTP() +{ + int i; + //struct pollfd allpolls[HTTP_CONNECTIONS+1]; + short mappedhttp[HTTP_CONNECTIONS+1]; + if( serverSocket == 0 ) return -1; + + do + { + static double last; + double now; +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) + static LARGE_INTEGER lpf; + LARGE_INTEGER li; + + if( !lpf.QuadPart ) + { + QueryPerformanceFrequency( &lpf ); + } + + QueryPerformanceCounter( &li ); + now = (double)li.QuadPart / (double)lpf.QuadPart; +#else + struct timeval tv; + gettimeofday( &tv, 0 ); + now = ((double)tv.tv_usec)/1000000. + (tv.tv_sec); +#endif + double dl = now - last; + if( dl > .1 ) + { + int i; + HTTPTick( 1 ); + last = now; + } + else + { + HTTPTick( 0 ); + } + +/* int pollct = 1; + allpolls[0].fd = serverSocket; + allpolls[0].events = LISTENPOLL; + for( i = 0; i < HTTP_CONNECTIONS;i ++) + { + if( !sockets[i] || HTTPConnections[i].state == 0 ) continue; + allpolls[pollct].fd = sockets[i]; + allpolls[pollct].events = POLLIN | (HTTPConnections[i].send_pending?POLLOUT:0); + mappedhttp[pollct] = i; + pollct++; + } + + //Do something to watch all currently-waiting sockets. + poll( allpolls, pollct, HTTP_POLL_TIMEOUT ); +*/ + //If there's faults, bail. + if( TCPException( serverSocket ) ) + { + closesocket( serverSocket ); + for( i = 0; i < HTTP_CONNECTIONS;i ++) + { + if( sockets[i] ) closesocket( sockets[i] ); + } + break; + } + if( TCPCanRead( serverSocket ) ) + { + struct sockaddr_in tin; + socklen_t addrlen = sizeof(tin); + memset( &tin, 0, addrlen ); + int tsocket = accept( serverSocket, (struct sockaddr *)&tin, &addrlen ); + +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) + struct linger lx; + lx.l_onoff = 1; + lx.l_linger = 0; + + //Disable the linger here, too. + setsockopt( tsocket, SOL_SOCKET, SO_LINGER, (const char*)&lx, sizeof( lx ) ); +#else + struct linger lx; + lx.l_onoff = 1; + lx.l_linger = 0; + + //Disable the linger here, too. + setsockopt( tsocket, SOL_SOCKET, SO_LINGER, &lx, sizeof( lx ) ); +#endif + + int r = httpserver_connectcb( tsocket ); + + if( r == -1 ) + { + closesocket( tsocket ); + } + else + { + sockets[r] = tsocket; + } + } + for( i = 0; i < HTTP_CONNECTIONS; i++) + { + int wc = i; + if( !sockets[i] || HTTPConnections[i].state == 0 ) continue; + if( TCPException(sockets[i]) ) + { + http_disconnetcb( wc ); + closesocket( sockets[wc] ); + sockets[wc] = 0; + } + else if( TCPCanRead( sockets[i] ) ) + { + int dco = HTTPConnections[i].corked_data_place; + uint8_t data[8192]; + memcpy( data, HTTPConnections[i].corked_data, dco ); + int len = recv( sockets[wc], data+dco, 8192-dco, 0 ); + if( len ) + { + cork_binary_rx = 0; + http_recvcb( wc, data, len+dco ); + if( cork_binary_rx ) + { + int to_cork = len; + if( to_cork > sizeof( HTTPConnections[i].corked_data ) + HTTPConnections[i].corked_data_place ) + { + http_disconnetcb( wc ); + closesocket ( sockets[wc] ); + sockets[wc] = 0; + fprintf( stderr, "Error: too much data to buffer on websocket\n" ); + } + else + { + memcpy( HTTPConnections[i].corked_data + dco, data + dco, to_cork ); + HTTPConnections[i].corked_data_place += to_cork; + } + } + else + { + HTTPConnections[i].corked_data_place = 0; + } + } + else + { + http_disconnetcb( wc ); + closesocket( sockets[wc] ); + sockets[wc] = 0; + } + } + } + } +#if HTTP_POLL_TIMEOUT != 0 + while(1); +#else + while(0); +#endif + + return 0; +} + + +int RunHTTP( int port ) +{ +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) +{ + WORD wVersionRequested; + WSADATA wsaData; + int err; + wVersionRequested = MAKEWORD(2, 2); + + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + /* Tell the user that we could not find a usable */ + /* Winsock DLL. */ + fprintf( stderr, "WSAStartup failed with error: %d\n", err); + return 1; + } +} +#endif + + + int acceptfaults = 0; + struct sockaddr_in sin; + serverSocket = socket(AF_INET, SOCK_STREAM, 0); + + //Make sure the socket worked. + if( serverSocket == -1 ) + { + fprintf( stderr, "Error: Cannot create socket.\n" ); + return -1; + } + + //Disable SO_LINGER (Well, enable it but turn it way down) +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) + struct linger lx; + lx.l_onoff = 1; + lx.l_linger = 0; + setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, (const char *)&lx, sizeof( lx ) ); + + //Enable SO_REUSEADDR + int reusevar = 1; + setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reusevar, sizeof(reusevar)); +#else + struct linger lx; + lx.l_onoff = 1; + lx.l_linger = 0; + setsockopt( serverSocket, SOL_SOCKET, SO_LINGER, &lx, sizeof( lx ) ); + + //Enable SO_REUSEADDR + int reusevar = 1; + setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &reusevar, sizeof(reusevar)); +#endif + //Setup socket for listening address. + memset( &sin, 0, sizeof( sin ) ); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = htons( port ); + + //Actually bind to the socket + if( bind( serverSocket, (struct sockaddr *) &sin, sizeof( sin ) ) == -1 ) + { + fprintf( stderr, "Could not bind to socket: %d\n", port ); + closesocket( serverSocket ); + serverSocket = 0; + return -1; + } + + //Finally listen. + if( listen( serverSocket, 5 ) == -1 ) + { + fprintf(stderr, "Could not lieten to socket."); + closesocket( serverSocket ); + serverSocket = 0; + return -1; + } + + return 0; +} + + + + + + +void Uint32To10Str( char * out, uint32_t dat ) +{ + int tens = 1000000000; + int val; + int place = 0; + + while( tens > 1 ) + { + if( dat/tens ) break; + tens/=10; + } + + while( tens ) + { + val = dat/tens; + dat -= val*tens; + tens /= 10; + out[place++] = val + '0'; + } + + out[place] = 0; +} + +//from http://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c +static const char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/'}; + +static const int mod_table[] = {0, 2, 1}; + +void my_base64_encode(const unsigned char *data, unsigned int input_length, uint8_t * encoded_data ) +{ + + int i, j; + int output_length = 4 * ((input_length + 2) / 3); + + if( !encoded_data ) return; + if( !data ) { encoded_data[0] = '='; encoded_data[1] = 0; return; } + + for (i = 0, j = 0; i < input_length; ) { + + uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0; + uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0; + uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0; + + uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; + + encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F]; + encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F]; + encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F]; + encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F]; + } + + for (i = 0; i < mod_table[input_length % 3]; i++) + encoded_data[output_length - 1 - i] = '='; + + encoded_data[j] = 0; +} + +uint8_t hex1byte( char c ) +{ + if( c >= '0' && c <= '9' ) return c - '0'; + if( c >= 'a' && c <= 'f' ) return c - 'a' + 10; + if( c >= 'A' && c <= 'F' ) return c - 'A' + 10; + return 0; +} + +uint8_t hex2byte( const char * c ) +{ + return (hex1byte(c[0])<<4) | (hex1byte(c[1])); +} + +#include + +#ifdef USE_RAM_MFS + +unsigned char webpage_buffer[] = { + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0xbd, 0x3b, 0x6b, 0x73, 0xda, 0xc8, 0xb2, 0x9f, 0xe1, 0x57, 0x4c, 0xb8, 0x75, 0x37, 0x60, 0x83, 0xc4, 0xc3, 0x76, 0xbc, 0xc6, 0x76, + 0x15, 0xb6, 0x71, 0xe2, 0x73, 0x6c, 0xc7, 0xd7, 0xe0, 0x4d, 0x52, 0xa9, 0x14, 0x25, 0xa4, 0x01, 0x69, 0x2d, 0x24, 0x56, 0x0f, 0x03, 0xd9, 0xf8, 0xbf, 0x9f, 0xee, 0x9e, 0xd1, 0x13, 0x61, 0x9c, + 0x3d, 0xbb, 0x97, 0xc4, 0x20, 0xcd, 0xf4, 0xf4, 0xf4, 0x6b, 0xfa, 0x31, 0x1a, 0xb1, 0xe3, 0x37, 0x17, 0x1f, 0xcf, 0x87, 0x5f, 0xee, 0xfa, 0xcc, 0x0c, 0x66, 0xf6, 0x69, 0xf9, 0x58, 0xfc, 0x94, + 0x8e, 0x4d, 0xae, 0x19, 0xf0, 0x5b, 0x3a, 0x9e, 0xf1, 0x40, 0x63, 0xba, 0xa9, 0x79, 0x3e, 0x0f, 0x4e, 0x2a, 0x0f, 0xc3, 0xcb, 0xc6, 0x61, 0x85, 0x3a, 0x02, 0x2b, 0xb0, 0xf9, 0xa9, 0xee, 0x98, + 0x41, 0x30, 0x3f, 0x56, 0xc5, 0x1d, 0xb6, 0xfb, 0xc1, 0x4a, 0x5c, 0x95, 0xc6, 0xae, 0xb1, 0x62, 0x7f, 0xb2, 0xb1, 0xa6, 0x3f, 0x4e, 0x3d, 0x37, 0x74, 0x8c, 0x86, 0xee, 0xda, 0xae, 0x77, 0xc4, + 0xfe, 0xa7, 0x09, 0x9f, 0xc3, 0x66, 0x97, 0x3d, 0x23, 0x98, 0xe1, 0xea, 0xe1, 0x8c, 0x3b, 0x01, 0x3b, 0x62, 0x7f, 0xba, 0x4f, 0xdc, 0x9b, 0xd8, 0xee, 0xe2, 0x88, 0x99, 0x96, 0x61, 0x70, 0xa7, + 0xcb, 0x16, 0x96, 0x11, 0x98, 0x27, 0xad, 0x66, 0xf3, 0x7f, 0xbb, 0xcc, 0xe4, 0xd6, 0xd4, 0x0c, 0xc4, 0x0d, 0x0e, 0x3d, 0x56, 0xa3, 0xc9, 0x8e, 0x55, 0x49, 0xf0, 0x31, 0x4d, 0x4a, 0xcd, 0x27, + 0x15, 0x01, 0x7f, 0x44, 0xf0, 0x33, 0xcd, 0x9b, 0x5a, 0xce, 0x11, 0x4c, 0x3a, 0xd7, 0x0c, 0xc3, 0x72, 0xa6, 0x47, 0x4d, 0xc1, 0x87, 0x61, 0x3d, 0xb1, 0xc1, 0xf0, 0xcb, 0x75, 0xff, 0xa4, 0x32, + 0x77, 0x7d, 0x2b, 0x70, 0x9d, 0x23, 0x6d, 0xec, 0xbb, 0x76, 0x18, 0xf0, 0xee, 0xf7, 0x86, 0xe5, 0x18, 0x7c, 0x79, 0xd4, 0x21, 0xd0, 0xd2, 0xb1, 0xae, 0x39, 0x4f, 0x9a, 0x2f, 0x69, 0xda, 0x6f, + 0x36, 0x23, 0x8a, 0xf0, 0xd2, 0x32, 0x4e, 0x44, 0xf7, 0xe9, 0xb1, 0x2a, 0x2f, 0x88, 0x44, 0xc0, 0x5f, 0x38, 0x8f, 0x95, 0x9e, 0x68, 0xec, 0x06, 0x81, 0x3b, 0x03, 0xea, 0x08, 0xf5, 0xd1, 0xc1, + 0x5e, 0x33, 0x9e, 0xbb, 0xd5, 0xec, 0x0a, 0xb1, 0x2d, 0x4c, 0x0b, 0x20, 0xb5, 0x23, 0x8f, 0x1b, 0x5d, 0xc9, 0xd9, 0x7e, 0x73, 0xbe, 0xac, 0x9c, 0x1e, 0x5b, 0xce, 0x3c, 0x0c, 0x58, 0xb0, 0x9a, + 0xf3, 0x13, 0x3f, 0x1c, 0xcf, 0xac, 0x80, 0x3d, 0x69, 0x76, 0x08, 0x02, 0x98, 0xf8, 0x15, 0xe6, 0x3a, 0xba, 0x6d, 0xe9, 0x8f, 0x27, 0x95, 0xa1, 0x3b, 0x9d, 0xda, 0xfc, 0x72, 0x50, 0xad, 0x75, + 0x61, 0x0c, 0x92, 0x03, 0x24, 0xfb, 0x81, 0x16, 0x84, 0x48, 0x32, 0x92, 0x99, 0x10, 0xeb, 0xeb, 0x9e, 0x35, 0x0f, 0x4e, 0xcb, 0x36, 0x0f, 0x98, 0xc7, 0x81, 0x0e, 0x0f, 0x24, 0xc6, 0x4e, 0xd8, + 0x44, 0xb3, 0x7d, 0xde, 0xa5, 0xe6, 0x49, 0x68, 0xdb, 0x00, 0xc6, 0xb9, 0xb3, 0xa9, 0x1d, 0x95, 0xe9, 0x59, 0x06, 0x87, 0x7e, 0x07, 0x1a, 0xbb, 0xe5, 0x49, 0xe8, 0xe8, 0xc8, 0x37, 0x4b, 0x48, + 0x01, 0xfb, 0x28, 0x1c, 0xf0, 0x66, 0xbd, 0x15, 0xcd, 0xa5, 0xac, 0xaa, 0xe7, 0xae, 0x33, 0xb1, 0xa6, 0xa1, 0xc7, 0xd9, 0x27, 0x3e, 0x7e, 0x7f, 0xcd, 0x06, 0x41, 0x38, 0x99, 0xb0, 0xaa, 0x66, + 0x83, 0xd9, 0xb0, 0xc0, 0x65, 0x63, 0x0e, 0x1a, 0xf6, 0x02, 0xe6, 0x4e, 0xd8, 0xd4, 0x76, 0xc7, 0x9a, 0xcd, 0x74, 0xd7, 0x09, 0xf8, 0x32, 0xa8, 0x11, 0x79, 0x52, 0x85, 0x27, 0x2c, 0x32, 0x3b, + 0x65, 0xca, 0x83, 0xbe, 0xcd, 0xf1, 0xf2, 0x6c, 0x75, 0x65, 0x54, 0xdf, 0x0a, 0x88, 0xb7, 0x35, 0xc1, 0xce, 0x62, 0x6a, 0x03, 0xb0, 0x68, 0x43, 0xd0, 0x73, 0x81, 0xac, 0xfa, 0x76, 0xc1, 0xc7, + 0x53, 0x1b, 0xa1, 0xac, 0x49, 0x95, 0xbd, 0x41, 0xb0, 0x5a, 0xf9, 0xcf, 0x72, 0x49, 0x55, 0xff, 0xa5, 0x39, 0x8f, 0x2b, 0xd6, 0x00, 0xc1, 0xb3, 0x4b, 0xcb, 0xe3, 0x13, 0x77, 0xc9, 0x0e, 0x3b, + 0x75, 0x30, 0x9a, 0xc0, 0x64, 0xb7, 0xbf, 0x5d, 0x5d, 0x5c, 0xf5, 0xd8, 0xfb, 0xbb, 0x87, 0x3a, 0x5b, 0xb9, 0x21, 0x73, 0x38, 0x37, 0x90, 0x68, 0xcd, 0x7f, 0x64, 0xc1, 0xc2, 0xd2, 0xb9, 0x52, + 0x2e, 0x6d, 0x9f, 0xf1, 0x39, 0xa2, 0x6c, 0x60, 0x6a, 0xa0, 0x9a, 0x48, 0xbe, 0x4c, 0x55, 0x07, 0x81, 0xe6, 0x18, 0x9a, 0x67, 0xb0, 0x89, 0xad, 0x01, 0xaf, 0x68, 0x37, 0xcc, 0x27, 0xa0, 0x68, + 0x48, 0xef, 0xec, 0xb7, 0x18, 0x1e, 0x3e, 0xaa, 0xda, 0xf3, 0x3c, 0x6d, 0xc5, 0xc6, 0x20, 0x44, 0xc0, 0x34, 0x01, 0x78, 0x10, 0x77, 0x00, 0x94, 0xf8, 0xc9, 0x88, 0xf3, 0x2d, 0x23, 0x68, 0x1e, + 0x5f, 0x89, 0x06, 0x3c, 0x7c, 0xbe, 0xbc, 0xbf, 0x49, 0x86, 0xa8, 0xea, 0x83, 0x63, 0x01, 0xd8, 0x8c, 0xd9, 0xae, 0xae, 0x91, 0xfe, 0x71, 0x50, 0xe0, 0x69, 0x8e, 0x4f, 0xcd, 0xd0, 0x00, 0xcb, + 0xc0, 0x32, 0x24, 0x1e, 0x54, 0xf2, 0x43, 0x60, 0xd9, 0x56, 0x80, 0x0b, 0x19, 0x95, 0x8b, 0xe0, 0x42, 0xd9, 0xbe, 0x66, 0x22, 0xc3, 0x60, 0x17, 0x84, 0x48, 0x49, 0x4c, 0x0a, 0xe6, 0x1d, 0xcd, + 0xb4, 0x47, 0x2e, 0x44, 0x52, 0x25, 0x2e, 0x86, 0x20, 0xb6, 0x3a, 0x9b, 0x78, 0xda, 0x14, 0xaf, 0x84, 0x7e, 0x90, 0x46, 0xec, 0x03, 0xfa, 0x60, 0x88, 0x42, 0x98, 0xa2, 0x41, 0xd8, 0xf0, 0x5b, + 0xff, 0x7e, 0xd8, 0xff, 0x3c, 0x1a, 0x7c, 0xe8, 0x5d, 0xf4, 0xef, 0x41, 0xd6, 0xa8, 0x0e, 0x45, 0x88, 0x70, 0xe0, 0x86, 0x9e, 0xce, 0xab, 0x38, 0xba, 0x1e, 0xe3, 0x67, 0x11, 0x8c, 0xee, 0xce, + 0xe6, 0x96, 0x1d, 0xa1, 0xc2, 0x6e, 0xec, 0xb1, 0xc0, 0x34, 0xd1, 0x38, 0x50, 0x91, 0xa2, 0xeb, 0x4e, 0xf3, 0x34, 0x70, 0xaa, 0x12, 0xa6, 0x4e, 0x54, 0x9c, 0x7f, 0xbc, 0xb9, 0xbb, 0xba, 0xee, + 0x8f, 0x06, 0xc3, 0xde, 0xf0, 0x61, 0x50, 0x83, 0x55, 0x81, 0xee, 0x46, 0xb3, 0x01, 0xa0, 0x9a, 0x19, 0x7c, 0xe5, 0x4c, 0xdc, 0x6b, 0x77, 0x2a, 0xd0, 0x23, 0x7e, 0x58, 0x12, 0xc4, 0x12, 0x32, + 0xb9, 0x89, 0xa5, 0xcb, 0xfb, 0xde, 0xfb, 0x9b, 0xfe, 0xed, 0xf0, 0x25, 0xa6, 0x70, 0x7c, 0x5a, 0x54, 0x85, 0x4c, 0x61, 0xf7, 0x36, 0xa6, 0x04, 0xa2, 0xbf, 0xc4, 0x14, 0xa1, 0x17, 0x4c, 0x95, + 0x84, 0xe3, 0xc9, 0x6a, 0xe9, 0xce, 0x73, 0xa7, 0x30, 0x4d, 0x35, 0x22, 0x4e, 0x0b, 0x02, 0x4d, 0x37, 0x25, 0x6d, 0x00, 0x2c, 0xe8, 0xdf, 0xdc, 0x1b, 0xe9, 0x04, 0x7b, 0x6d, 0xcb, 0x79, 0x8c, + 0xf0, 0x41, 0x67, 0xd4, 0x3c, 0x06, 0x9f, 0xdb, 0x0b, 0x02, 0xcf, 0x1a, 0x5f, 0x4b, 0x5b, 0xad, 0x32, 0x1a, 0xdb, 0xac, 0xb3, 0x8a, 0xd6, 0xac, 0xb0, 0xad, 0x80, 0x2d, 0x04, 0x6c, 0x09, 0x40, + 0x68, 0x08, 0x3d, 0x07, 0xdb, 0x71, 0xcd, 0x96, 0x85, 0x7b, 0xf8, 0xc4, 0x61, 0x1d, 0x68, 0xb0, 0xea, 0x17, 0xae, 0x5c, 0x9a, 0x7e, 0x1d, 0x96, 0x00, 0x67, 0x96, 0xcf, 0x34, 0xb1, 0x10, 0x1a, + 0xe9, 0x85, 0x5b, 0x27, 0xf3, 0x9f, 0xb9, 0x3e, 0x08, 0x44, 0x5b, 0x18, 0xf0, 0xc7, 0xdc, 0xf1, 0xef, 0x5c, 0x0f, 0x7c, 0xe1, 0x2b, 0x62, 0x1f, 0x90, 0x5f, 0x01, 0x20, 0xed, 0x4a, 0x28, 0x57, + 0xde, 0x13, 0xd7, 0xf7, 0xd8, 0x72, 0xe2, 0xcd, 0xba, 0x4c, 0x23, 0xaa, 0x21, 0xe8, 0x60, 0x63, 0x87, 0x69, 0xcd, 0x5c, 0xd3, 0x1e, 0xd3, 0x5a, 0x5d, 0x08, 0x20, 0xde, 0x0a, 0x5d, 0x3e, 0x35, + 0x3c, 0xe9, 0xd0, 0xe0, 0xc2, 0x02, 0x9d, 0x69, 0x96, 0x43, 0x3e, 0x1b, 0xa6, 0xba, 0x93, 0x41, 0x0c, 0x66, 0x46, 0xa0, 0x2a, 0x60, 0x52, 0x96, 0xab, 0x1d, 0x9c, 0x04, 0x7e, 0x77, 0xe9, 0xf7, + 0xfb, 0xa2, 0x8e, 0xcd, 0xdf, 0x41, 0x7e, 0xca, 0x3e, 0x88, 0x04, 0x30, 0x01, 0x38, 0xe2, 0x7f, 0xae, 0xd4, 0x91, 0xbe, 0xb9, 0xc7, 0x75, 0xcb, 0x47, 0x2c, 0x33, 0x6e, 0x58, 0xe1, 0x6c, 0x0e, + 0x8e, 0xcb, 0xd5, 0x82, 0xd7, 0xcc, 0x7f, 0x09, 0xba, 0x3e, 0x27, 0x39, 0x49, 0x02, 0x9e, 0x74, 0x98, 0xf7, 0xfb, 0xa2, 0x86, 0xc8, 0x51, 0xfc, 0x24, 0x9d, 0xc8, 0x17, 0x49, 0x83, 0x93, 0x9e, + 0x28, 0xd6, 0x59, 0x2c, 0x3f, 0xd0, 0x1a, 0x52, 0x2c, 0x07, 0x62, 0x94, 0x21, 0xc3, 0x67, 0x81, 0xc9, 0x23, 0x2d, 0x09, 0x71, 0x2b, 0xa1, 0x1f, 0x1b, 0x62, 0x3c, 0x5a, 0x0e, 0x7a, 0x0f, 0x06, + 0xeb, 0xbb, 0x33, 0x4e, 0x96, 0xc6, 0x97, 0xaa, 0x50, 0xa3, 0x70, 0x94, 0xa0, 0x64, 0xf0, 0xf3, 0x18, 0xa7, 0xa7, 0x1c, 0x40, 0x02, 0x6f, 0xc5, 0x2c, 0x47, 0xa0, 0x14, 0x2e, 0x39, 0xb1, 0xf3, + 0x33, 0x1a, 0x10, 0x99, 0xb9, 0x70, 0xbf, 0x45, 0xbd, 0xc2, 0x9a, 0xde, 0x42, 0x24, 0x0c, 0x7d, 0x94, 0x15, 0x5a, 0x54, 0x3c, 0x99, 0xef, 0xb2, 0xdf, 0x43, 0xb0, 0x19, 0xee, 0x68, 0x63, 0xc1, + 0xc7, 0xac, 0xce, 0x1c, 0x77, 0x21, 0xb9, 0x10, 0xcd, 0xbf, 0x11, 0x9d, 0xc2, 0x8e, 0xc9, 0xab, 0x57, 0x9b, 0x91, 0x81, 0x6f, 0x02, 0x68, 0xc9, 0x89, 0xfb, 0x02, 0xaf, 0x66, 0xcf, 0x4d, 0x8d, + 0xc1, 0x95, 0x83, 0x39, 0x55, 0x7a, 0x28, 0xad, 0xf1, 0xb3, 0xeb, 0xfe, 0xed, 0x45, 0xbc, 0x66, 0x10, 0xea, 0x12, 0x1c, 0x36, 0x75, 0x0d, 0xee, 0xcf, 0x47, 0xbd, 0xeb, 0xbb, 0x0f, 0x3d, 0xe1, + 0x2d, 0x3e, 0xde, 0xf6, 0x47, 0x37, 0x57, 0xb7, 0x0f, 0x83, 0x51, 0xdc, 0x81, 0xe3, 0x20, 0x76, 0xc3, 0x92, 0xe0, 0x8a, 0x0d, 0xfe, 0x81, 0x62, 0x31, 0x86, 0x3f, 0x55, 0xbd, 0x70, 0x19, 0x45, + 0x43, 0xb6, 0x70, 0x3d, 0x88, 0x9c, 0x26, 0x04, 0xbb, 0x19, 0x72, 0x6b, 0x6a, 0xf3, 0x39, 0x24, 0x24, 0x1c, 0x14, 0xb0, 0x42, 0x6f, 0x30, 0x83, 0x80, 0x8a, 0xf1, 0x44, 0x08, 0x0d, 0x62, 0x6d, + 0x2c, 0x7c, 0x7f, 0xe5, 0x07, 0x7c, 0xa6, 0x40, 0xdf, 0xd0, 0x84, 0x95, 0x67, 0x42, 0xdc, 0xb4, 0xb9, 0x2f, 0xe5, 0x87, 0xd2, 0x04, 0xae, 0x80, 0x54, 0x13, 0xf3, 0x08, 0x70, 0x16, 0xdc, 0x57, + 0x7d, 0x3e, 0xc5, 0x24, 0x01, 0x64, 0x0b, 0xb0, 0x91, 0xc8, 0x41, 0xac, 0x20, 0x03, 0x1b, 0x96, 0x10, 0x26, 0x5a, 0x30, 0x9b, 0xc8, 0x39, 0x9e, 0xe2, 0x9c, 0x27, 0x89, 0x50, 0xe7, 0xb7, 0x97, + 0xef, 0xfb, 0x90, 0x9c, 0x9d, 0x41, 0x3e, 0x0c, 0x82, 0x18, 0x7a, 0x96, 0xe6, 0x40, 0x16, 0xe4, 0xff, 0x6b, 0x20, 0xa2, 0x95, 0x7f, 0x59, 0x97, 0x11, 0xf0, 0x4a, 0xf8, 0x2a, 0x1d, 0x72, 0x66, + 0x19, 0xb5, 0x30, 0xc7, 0x48, 0x9a, 0x8e, 0x4f, 0x58, 0x93, 0xd5, 0x98, 0x70, 0x2f, 0x5d, 0x26, 0xa4, 0x14, 0x30, 0x6d, 0x2c, 0x0d, 0xa5, 0x77, 0x7f, 0xdf, 0xfb, 0x32, 0x3a, 0x7b, 0xb8, 0xbc, + 0xec, 0xdf, 0xa7, 0xfc, 0x95, 0xb4, 0x1c, 0x6d, 0x5c, 0x97, 0x89, 0x40, 0xac, 0x18, 0xea, 0xb8, 0xd0, 0x02, 0x8d, 0x3a, 0x23, 0x62, 0xb0, 0xeb, 0xe2, 0xcb, 0x6d, 0xef, 0xe6, 0xea, 0x7c, 0x74, + 0x71, 0xdf, 0xfb, 0x14, 0x81, 0x3f, 0xa5, 0x8c, 0xe2, 0xce, 0xb5, 0x1c, 0x74, 0xfb, 0xe0, 0x1f, 0x3b, 0x62, 0xc4, 0xe5, 0xf5, 0xc7, 0xde, 0xb0, 0x2e, 0x32, 0x42, 0xf2, 0x9b, 0xcd, 0xda, 0x0b, + 0x34, 0x9c, 0x6f, 0xa0, 0xc1, 0xe1, 0x0b, 0xf6, 0x00, 0xa8, 0x0f, 0x85, 0xd5, 0x45, 0x82, 0x91, 0x60, 0xac, 0xf6, 0x73, 0xd4, 0x81, 0x53, 0xde, 0x13, 0x23, 0x1e, 0x6e, 0x07, 0x57, 0xef, 0x6f, + 0xfb, 0x17, 0xa3, 0xb3, 0x2f, 0xc3, 0x3e, 0x2c, 0x49, 0x2f, 0xcc, 0x11, 0x89, 0x0e, 0x96, 0xe6, 0xf4, 0xc9, 0x4a, 0x87, 0xf7, 0x57, 0xbd, 0xdb, 0xf7, 0xd7, 0xfd, 0x01, 0x41, 0xa5, 0xb4, 0x02, + 0xe0, 0x89, 0xaa, 0x85, 0xc4, 0xc8, 0xc9, 0x83, 0xad, 0x41, 0xe6, 0x12, 0xa0, 0x71, 0x44, 0x9a, 0xd7, 0x30, 0x95, 0x01, 0x3b, 0x94, 0xe9, 0x11, 0xf6, 0xa7, 0x12, 0xaa, 0xd4, 0x00, 0xe1, 0x68, + 0x44, 0x76, 0x04, 0x51, 0x18, 0x72, 0xdc, 0x68, 0x0c, 0x04, 0xe5, 0x6c, 0x0e, 0x36, 0x14, 0xfd, 0xd2, 0x6e, 0x69, 0xc8, 0x58, 0xa2, 0xc9, 0xe6, 0x61, 0x99, 0xd9, 0x5e, 0x93, 0x8a, 0x11, 0x1a, + 0xc8, 0x17, 0xcb, 0x14, 0xaa, 0x34, 0x98, 0xc3, 0x71, 0x21, 0x95, 0x0c, 0x3d, 0xa8, 0x03, 0x02, 0x1b, 0x16, 0x4e, 0x38, 0x9f, 0xbb, 0x5e, 0x20, 0xad, 0x1f, 0xfc, 0xcc, 0x24, 0x58, 0x20, 0x94, + 0x28, 0x13, 0xb8, 0xa7, 0x94, 0x37, 0xc4, 0x38, 0x84, 0x76, 0xe1, 0xcb, 0x13, 0x91, 0x4e, 0x72, 0x98, 0x89, 0x72, 0x11, 0x0b, 0x00, 0x0b, 0xdf, 0xe0, 0x71, 0x13, 0x71, 0xad, 0x47, 0xb7, 0xbf, + 0x25, 0xb8, 0xb5, 0x59, 0xf0, 0xb7, 0x05, 0xb7, 0x40, 0x04, 0x37, 0x00, 0x90, 0xf1, 0xed, 0x55, 0xe1, 0x4d, 0x50, 0x10, 0xf1, 0xe2, 0x6b, 0xb3, 0x39, 0x64, 0x48, 0xed, 0x0b, 0x94, 0xcf, 0x96, + 0xa8, 0x27, 0x25, 0xd8, 0xbe, 0xa8, 0xc2, 0x55, 0x3d, 0xd0, 0x6b, 0xca, 0xe2, 0xfb, 0x6a, 0xd9, 0x95, 0xd1, 0x2f, 0x67, 0x00, 0x9b, 0x03, 0x20, 0x02, 0x64, 0xc2, 0x5f, 0xc6, 0x63, 0x61, 0xef, + 0xd5, 0x4c, 0x9b, 0xf2, 0x2b, 0x5c, 0x49, 0x60, 0xcb, 0xd5, 0x10, 0x97, 0xe5, 0x8c, 0xcf, 0x0c, 0x58, 0xab, 0x75, 0xb6, 0x84, 0x42, 0x06, 0x96, 0x56, 0x9d, 0x99, 0x89, 0xa7, 0x5a, 0x08, 0x0f, + 0xf5, 0xe3, 0x07, 0x34, 0x66, 0x7d, 0x55, 0xb9, 0x28, 0x86, 0xe2, 0x14, 0x32, 0xaa, 0xdc, 0x60, 0x9a, 0x03, 0x1e, 0x17, 0x6d, 0x25, 0xb0, 0x20, 0x92, 0x2e, 0x38, 0x14, 0x6a, 0xce, 0xdb, 0x00, + 0xfc, 0x2d, 0x8f, 0x18, 0x16, 0xd1, 0x4d, 0x34, 0x5b, 0x0e, 0xe8, 0x09, 0xa2, 0x22, 0x3a, 0x60, 0xa8, 0x65, 0xbd, 0x40, 0x91, 0x24, 0xc8, 0x35, 0x23, 0x2c, 0x9f, 0xd5, 0x4a, 0xf1, 0x22, 0x4a, + 0x22, 0xa9, 0x5c, 0x43, 0x10, 0x4a, 0x99, 0xa4, 0x4b, 0x03, 0xbe, 0x9f, 0xe2, 0x76, 0x5a, 0xff, 0xfd, 0xcf, 0xc3, 0x87, 0xfb, 0x7e, 0x33, 0x8a, 0x45, 0x50, 0x66, 0xb7, 0x0d, 0x89, 0x45, 0xf6, + 0x8d, 0xda, 0x17, 0x29, 0x07, 0x17, 0x0d, 0x06, 0xb0, 0xba, 0xa4, 0x42, 0xf2, 0x76, 0xeb, 0x06, 0x5c, 0x84, 0xaa, 0xd0, 0xb1, 0xad, 0x47, 0x91, 0x5b, 0x38, 0xa0, 0x0c, 0x2a, 0x51, 0x51, 0xa5, + 0xee, 0x9c, 0x7b, 0xa4, 0x96, 0x7a, 0xc2, 0xb7, 0xa9, 0x3d, 0x01, 0x73, 0x10, 0xd2, 0x96, 0xb0, 0x4a, 0x41, 0x34, 0x13, 0x9f, 0x07, 0x51, 0x26, 0x22, 0x74, 0xb9, 0x27, 0xd8, 0x8d, 0x95, 0x8d, + 0x89, 0x55, 0x4b, 0x51, 0xc9, 0x17, 0x5a, 0x7c, 0x81, 0x8b, 0xf5, 0x13, 0xee, 0x2a, 0xd4, 0x59, 0x23, 0xd7, 0xfc, 0x81, 0x76, 0x11, 0x10, 0xbe, 0xa1, 0xec, 0xef, 0x2e, 0x8b, 0x86, 0x28, 0xfb, + 0x8d, 0x55, 0xc1, 0x98, 0x28, 0x4d, 0x1a, 0x9a, 0xdc, 0xa7, 0x7a, 0x5b, 0x64, 0xff, 0x3e, 0x13, 0x9e, 0xe0, 0x8f, 0x10, 0x0a, 0x5f, 0x43, 0x61, 0x0c, 0x78, 0x06, 0x87, 0x01, 0x6d, 0x0b, 0x73, + 0x45, 0xfc, 0x1a, 0x7c, 0xa2, 0x85, 0x76, 0xe0, 0x4b, 0xee, 0x30, 0x7c, 0x4b, 0x6e, 0x40, 0xb9, 0x71, 0x15, 0x61, 0xc5, 0xe2, 0x8b, 0x85, 0xfc, 0xe9, 0xbe, 0x77, 0x37, 0x1a, 0xca, 0xca, 0xe2, + 0xba, 0x77, 0x03, 0x37, 0x1f, 0x47, 0xfd, 0x8b, 0xf7, 0xfd, 0xc8, 0x71, 0xbf, 0x66, 0xfc, 0xe0, 0xaf, 0x8f, 0x87, 0xdc, 0x64, 0x74, 0x79, 0x75, 0x3d, 0xec, 0xdf, 0x8b, 0xf6, 0xdb, 0x7e, 0xef, + 0xbe, 0x3f, 0x18, 0x46, 0x79, 0x26, 0x0e, 0xa7, 0x65, 0x82, 0xab, 0x11, 0xc7, 0x36, 0x05, 0xd8, 0xfd, 0xfb, 0xb3, 0x9e, 0x58, 0x1e, 0xd9, 0x26, 0x90, 0x79, 0x41, 0x38, 0x4a, 0x2f, 0x2d, 0x21, + 0xe2, 0x97, 0x92, 0x06, 0xc0, 0x81, 0x61, 0xf2, 0x12, 0x5d, 0x4a, 0xa7, 0x2d, 0x03, 0xe5, 0xd7, 0x66, 0x1d, 0xff, 0xc1, 0x94, 0xf4, 0x83, 0x9f, 0x45, 0xdd, 0x8c, 0x2e, 0x19, 0x6b, 0x46, 0xed, + 0xb9, 0x56, 0xb8, 0x61, 0xdf, 0x20, 0xae, 0x4a, 0xa4, 0xe9, 0xd8, 0x2b, 0x51, 0x02, 0x70, 0x7b, 0x7f, 0x3f, 0x7d, 0x19, 0xdd, 0xb2, 0xe6, 0x86, 0x56, 0x79, 0x29, 0xf1, 0x1e, 0xc4, 0x59, 0xf9, + 0xa6, 0x24, 0xfa, 0xb9, 0x8b, 0x61, 0x67, 0x80, 0xe1, 0xcb, 0x84, 0x34, 0xce, 0x5f, 0x68, 0xf3, 0x28, 0xa3, 0xc5, 0xa0, 0xa1, 0x43, 0xa6, 0x05, 0x39, 0xdc, 0x78, 0xc5, 0x4c, 0xf4, 0x13, 0x90, + 0xeb, 0x89, 0x58, 0x47, 0x89, 0xde, 0x48, 0x44, 0x31, 0xee, 0x8d, 0x0c, 0xac, 0xa6, 0xe2, 0x3d, 0x29, 0x40, 0x08, 0x16, 0x29, 0xdc, 0x4a, 0x26, 0x22, 0xe7, 0x1c, 0xdd, 0xb9, 0xcd, 0x35, 0xef, + 0x12, 0x31, 0xc5, 0x9e, 0x4e, 0x2e, 0x4b, 0xf2, 0x6b, 0xe2, 0xf2, 0x07, 0x78, 0xb3, 0xa8, 0x34, 0x46, 0x78, 0x72, 0xc5, 0x55, 0x56, 0xad, 0x52, 0xf7, 0xe9, 0x69, 0x7b, 0xaf, 0xf6, 0x4b, 0x73, + 0x39, 0x99, 0xd4, 0x54, 0x60, 0x5d, 0xa9, 0x27, 0x1d, 0xad, 0x83, 0x0d, 0x1d, 0x87, 0x1b, 0xda, 0x9b, 0xe9, 0x76, 0x8c, 0x2e, 0xa9, 0x59, 0xab, 0xb2, 0xbe, 0xbe, 0xfe, 0x78, 0x2f, 0xb3, 0xbc, + 0xd1, 0xd9, 0xd5, 0x90, 0xfd, 0x48, 0x35, 0x5f, 0xf4, 0xef, 0x86, 0x1f, 0xa8, 0x95, 0xf6, 0x8b, 0xb2, 0xac, 0x42, 0x9d, 0x72, 0x01, 0xce, 0xd5, 0xc1, 0xc8, 0xe4, 0x57, 0xe7, 0x60, 0x9d, 0x73, + 0x93, 0x98, 0xfc, 0xd0, 0xef, 0xdd, 0xb5, 0x0e, 0xbe, 0xce, 0x17, 0x40, 0xf0, 0xb7, 0x64, 0x13, 0x8a, 0x36, 0x25, 0xbb, 0x49, 0xb7, 0x99, 0xed, 0x16, 0x9b, 0x91, 0xd9, 0x69, 0x3e, 0xe2, 0x24, + 0x3d, 0xb9, 0xbd, 0x39, 0x84, 0xc9, 0x28, 0x80, 0xc9, 0x82, 0x18, 0x0d, 0x0c, 0x32, 0x3d, 0x68, 0xc3, 0x90, 0x24, 0x7a, 0xd5, 0x56, 0xb3, 0xd9, 0x54, 0x68, 0x8f, 0x4f, 0xe4, 0x55, 0x90, 0xdf, + 0xfb, 0x2e, 0x18, 0x7d, 0x9c, 0xbd, 0xe4, 0xdb, 0x47, 0x14, 0x20, 0x4e, 0xc0, 0xab, 0xe1, 0x76, 0xf2, 0x7a, 0x37, 0xb8, 0x6b, 0x07, 0xca, 0x64, 0x6e, 0x64, 0x6c, 0xe1, 0x0e, 0x5c, 0xbf, 0x4d, + 0xd9, 0x0f, 0xb8, 0x55, 0x66, 0x6b, 0xde, 0x14, 0x7e, 0x75, 0x3d, 0x9c, 0x85, 0xb6, 0x86, 0xb0, 0xc2, 0xdc, 0x94, 0x72, 0xaa, 0x6d, 0x24, 0x93, 0xce, 0x93, 0x38, 0x2b, 0x8d, 0x57, 0xdb, 0x5e, + 0x93, 0x3e, 0x28, 0xe4, 0xf5, 0x01, 0xa3, 0xb9, 0xad, 0xe9, 0x48, 0x21, 0x10, 0x57, 0x46, 0xa7, 0x08, 0x56, 0x38, 0x32, 0xe1, 0xbe, 0x73, 0xb8, 0xd7, 0x8d, 0x1b, 0x16, 0xd0, 0xb0, 0xdf, 0x6a, + 0x13, 0x71, 0x1f, 0x29, 0x3d, 0x92, 0xdb, 0x96, 0xb8, 0x35, 0xcb, 0xcb, 0xba, 0x33, 0x99, 0x8e, 0x74, 0x19, 0xf1, 0xd1, 0x22, 0xc4, 0x27, 0x1d, 0xa6, 0x07, 0xe0, 0x18, 0x06, 0xb0, 0x54, 0xaa, + 0x71, 0x00, 0x7e, 0xb3, 0x59, 0x16, 0xe9, 0x50, 0x4c, 0x79, 0xa2, 0x0c, 0x66, 0x19, 0xbf, 0xdf, 0x15, 0x7d, 0x66, 0xae, 0xef, 0x83, 0x54, 0xb4, 0x0c, 0xf2, 0x27, 0x10, 0x5d, 0x43, 0x48, 0xfa, + 0x26, 0x50, 0x1e, 0x01, 0xe2, 0x72, 0x09, 0x37, 0x78, 0x22, 0x7c, 0x98, 0x47, 0x43, 0x86, 0x23, 0xf2, 0xfd, 0x08, 0x69, 0xc9, 0x2c, 0xea, 0x8d, 0xd1, 0x3e, 0x97, 0x4b, 0x2f, 0x2e, 0x65, 0xf4, + 0x54, 0x39, 0xce, 0x14, 0x1f, 0x98, 0xaf, 0xae, 0x95, 0x0b, 0x5f, 0x69, 0xcb, 0xf5, 0xf0, 0x5d, 0x9d, 0x1d, 0xec, 0xc3, 0x2f, 0xf9, 0x43, 0x06, 0x56, 0x7d, 0x48, 0x8e, 0x18, 0xec, 0xf7, 0x10, + 0x9d, 0x1d, 0x2e, 0x2b, 0x55, 0xad, 0x0c, 0x3e, 0xf5, 0xee, 0x2a, 0x19, 0xeb, 0xfd, 0x40, 0x59, 0xf5, 0x8d, 0x2b, 0xf6, 0x75, 0x44, 0x8a, 0x03, 0x29, 0x64, 0x92, 0xe1, 0xbc, 0x20, 0xdf, 0x72, + 0xe9, 0x67, 0xc8, 0x7c, 0x07, 0x24, 0xbe, 0xfb, 0x15, 0x48, 0x84, 0xfa, 0xe4, 0xdd, 0x21, 0xa5, 0x53, 0x4b, 0x22, 0x13, 0x66, 0x5c, 0xd1, 0x45, 0x33, 0xfa, 0x8f, 0x04, 0x08, 0xa2, 0x21, 0xf0, + 0x56, 0x6e, 0x3e, 0x0e, 0x6f, 0x8b, 0x88, 0x3e, 0x0b, 0x83, 0x20, 0x45, 0x74, 0x00, 0xb9, 0x84, 0xe1, 0x2e, 0x9c, 0x7f, 0x80, 0xf4, 0x83, 0x03, 0x20, 0x7b, 0x7f, 0x3b, 0xe9, 0x38, 0x3d, 0x51, + 0x92, 0xa2, 0xfe, 0xec, 0xa1, 0x98, 0xfa, 0x7f, 0x73, 0x40, 0xfd, 0xc8, 0x57, 0xff, 0x18, 0xd1, 0xef, 0x80, 0xe0, 0x03, 0x94, 0x37, 0xfc, 0x21, 0x03, 0x82, 0xc4, 0x64, 0xc6, 0x84, 0xc4, 0x7f, + 0xf7, 0xbf, 0x9c, 0x11, 0x89, 0xb8, 0x30, 0xff, 0xb1, 0x0f, 0x46, 0xa7, 0x7b, 0xb9, 0xad, 0x17, 0xc9, 0xc2, 0xc7, 0xdc, 0x7d, 0x64, 0x6b, 0xe8, 0x1c, 0x70, 0xe1, 0x8c, 0xf0, 0x09, 0xc8, 0xa8, + 0x0d, 0xeb, 0x40, 0xd9, 0xef, 0x96, 0xd1, 0x81, 0x8f, 0xce, 0x7a, 0xc3, 0xf3, 0x0f, 0xd0, 0xd0, 0x3e, 0x68, 0xb7, 0xf6, 0xc0, 0x99, 0x94, 0xa9, 0x19, 0x77, 0x6b, 0xb0, 0x82, 0xfe, 0x4d, 0xba, + 0xaa, 0x4c, 0x66, 0x90, 0x8c, 0xdb, 0xe9, 0xd4, 0xba, 0x19, 0xf8, 0xf3, 0x02, 0xd7, 0x96, 0x80, 0xa7, 0x80, 0xef, 0x52, 0x0e, 0x2d, 0x13, 0x51, 0x2e, 0xed, 0xd0, 0x37, 0xef, 0xa9, 0x02, 0x14, + 0x5e, 0xe8, 0xa5, 0xbc, 0x25, 0x43, 0x6a, 0x3d, 0x73, 0x7b, 0x9e, 0xdc, 0x8a, 0xb9, 0x30, 0x3b, 0x2b, 0x98, 0x3d, 0x6d, 0x39, 0x38, 0xcd, 0xff, 0x85, 0x1a, 0xa8, 0x5b, 0x5f, 0x82, 0x2e, 0xf5, + 0x15, 0x7e, 0x2d, 0x5b, 0x78, 0x85, 0x5f, 0xcb, 0x36, 0x5e, 0xe1, 0xd7, 0xb2, 0x83, 0x57, 0x1d, 0xd0, 0xb0, 0xd8, 0x89, 0x3d, 0xe3, 0xba, 0x86, 0x65, 0xc5, 0x1f, 0x30, 0x38, 0xca, 0x5c, 0x21, + 0x36, 0xac, 0xb0, 0xd6, 0x98, 0x84, 0xb6, 0x58, 0x71, 0x56, 0xf0, 0xd6, 0x67, 0x63, 0xcc, 0x1f, 0x02, 0x17, 0xec, 0x84, 0xcf, 0x93, 0x3d, 0x9f, 0x20, 0x62, 0x8a, 0x59, 0x13, 0x36, 0x77, 0x7d, + 0xdf, 0x1a, 0xdb, 0xf8, 0x00, 0x47, 0x6e, 0x29, 0x81, 0x07, 0xf5, 0x01, 0x15, 0x23, 0xdd, 0x6a, 0xde, 0xd8, 0x02, 0xaf, 0xe6, 0xad, 0xc4, 0x6c, 0xb2, 0x4a, 0xc9, 0x72, 0x76, 0x7a, 0xc2, 0x12, + 0xa9, 0x37, 0x20, 0x5b, 0x5a, 0x17, 0x6d, 0x94, 0x12, 0xc6, 0xe2, 0xfb, 0x9a, 0x41, 0xb1, 0xd3, 0xd9, 0x6d, 0x52, 0x60, 0x5e, 0x42, 0xe1, 0xcb, 0x5e, 0x06, 0x14, 0x11, 0x7c, 0xd5, 0xec, 0x6e, + 0xc3, 0xd8, 0x11, 0x18, 0x5b, 0x5b, 0x31, 0xee, 0x09, 0x8c, 0xad, 0xad, 0x18, 0x0f, 0x04, 0xc6, 0xf6, 0x56, 0x8c, 0xef, 0x04, 0xc6, 0xf6, 0x56, 0x8c, 0xbf, 0xbe, 0x16, 0x63, 0xab, 0xf9, 0x5a, + 0x94, 0xad, 0x76, 0xcc, 0xf7, 0x16, 0xc0, 0xce, 0x6b, 0xf9, 0x6e, 0xed, 0x0b, 0x94, 0x9d, 0xad, 0x28, 0x85, 0x84, 0x56, 0x9d, 0x1c, 0xca, 0xf3, 0x2c, 0xa4, 0x54, 0x76, 0x9c, 0x21, 0x6c, 0x81, + 0x6e, 0xfd, 0x14, 0x74, 0xfb, 0xa7, 0xa0, 0x3b, 0x3f, 0x05, 0xbd, 0xf7, 0x53, 0xd0, 0xfb, 0x1b, 0xa1, 0xc5, 0xba, 0xd9, 0x3d, 0x61, 0x07, 0xeb, 0x49, 0xee, 0x10, 0x3c, 0xcf, 0x9d, 0xb5, 0xe4, + 0x90, 0xc6, 0xa3, 0x33, 0x58, 0xb5, 0x44, 0x34, 0x59, 0xb6, 0x76, 0x77, 0xbb, 0x70, 0x07, 0xdf, 0x22, 0xc9, 0xb1, 0xdb, 0x72, 0x77, 0x69, 0xcd, 0xe1, 0xc6, 0x00, 0xe1, 0x26, 0x88, 0xdd, 0x26, + 0x7a, 0xe4, 0x52, 0xe2, 0x81, 0x96, 0xad, 0x06, 0x80, 0xe3, 0x6c, 0xe2, 0x17, 0x66, 0xb3, 0xdb, 0xe9, 0xdb, 0xa8, 0x9b, 0x9a, 0xe3, 0x5e, 0xf8, 0x91, 0x79, 0xfa, 0x3a, 0x0f, 0x03, 0xb1, 0xed, + 0x1c, 0x71, 0x01, 0xa3, 0x70, 0x48, 0x9b, 0xb8, 0x51, 0xd5, 0xec, 0x06, 0xf9, 0xb2, 0xc5, 0x76, 0x59, 0x05, 0xfe, 0xed, 0x22, 0xbf, 0xd1, 0xe5, 0xb2, 0x9d, 0xb4, 0xb6, 0x45, 0x29, 0x81, 0x7c, + 0x59, 0x00, 0x7d, 0x02, 0x43, 0x24, 0x9b, 0xd6, 0x0a, 0x6f, 0x57, 0xf1, 0xed, 0x12, 0xe5, 0xb2, 0x6c, 0xc7, 0xbd, 0x78, 0x8b, 0xcb, 0x46, 0xdc, 0x1b, 0xb8, 0x95, 0x02, 0x30, 0x0d, 0x2b, 0x46, + 0x60, 0xac, 0xb0, 0x69, 0x05, 0x4d, 0x09, 0x92, 0x19, 0x3d, 0x95, 0x6c, 0x29, 0xea, 0x8d, 0x16, 0x98, 0x8a, 0xff, 0x87, 0x17, 0x54, 0x8d, 0xe5, 0x8e, 0xb1, 0xdc, 0x35, 0x56, 0x3b, 0xc6, 0x0a, + 0x9d, 0x3b, 0x20, 0xda, 0x39, 0x21, 0x40, 0xbc, 0x59, 0xa5, 0x6e, 0x10, 0x01, 0x24, 0x9d, 0xa6, 0x8b, 0x53, 0x01, 0xfc, 0x4b, 0x3a, 0x22, 0x38, 0x98, 0xbf, 0x01, 0xd8, 0x37, 0xc0, 0x81, 0xc7, + 0x45, 0x41, 0x00, 0xaa, 0xa5, 0x8a, 0x02, 0x11, 0xaa, 0x43, 0xbe, 0xb0, 0x6d, 0x95, 0x6e, 0x03, 0xc1, 0x34, 0x24, 0x5c, 0x23, 0x86, 0x13, 0x6d, 0xab, 0xa4, 0x2d, 0x71, 0xf4, 0xee, 0xd4, 0xd2, + 0xb1, 0x3e, 0xb5, 0x1c, 0xdd, 0xf5, 0x3c, 0xc8, 0x4b, 0x14, 0xf6, 0xf9, 0xf3, 0x67, 0x76, 0x79, 0xf5, 0xf9, 0xa6, 0xaf, 0xa4, 0xcd, 0xa3, 0x4a, 0xa8, 0x25, 0x57, 0x35, 0xa8, 0xf6, 0x08, 0xad, + 0xa4, 0x9e, 0xee, 0x49, 0x83, 0xd9, 0xfe, 0xdd, 0x4c, 0x7f, 0x3b, 0x37, 0xbe, 0x9d, 0x19, 0x4f, 0x6a, 0x4b, 0x23, 0x20, 0xc5, 0xc5, 0x18, 0x8a, 0x8a, 0x41, 0xb4, 0xb1, 0x7b, 0xa0, 0x99, 0x42, + 0x59, 0xa1, 0x95, 0xfd, 0x37, 0xd6, 0x92, 0x62, 0x1e, 0x30, 0xd4, 0x61, 0x58, 0x1d, 0x60, 0xe5, 0x2f, 0xde, 0xb7, 0xe5, 0x7d, 0x7b, 0x13, 0x6d, 0x77, 0xae, 0x0d, 0x99, 0xdb, 0x1c, 0x77, 0xf2, + 0x7d, 0xf9, 0x6c, 0x42, 0x50, 0x25, 0x2e, 0x65, 0x45, 0x4e, 0xb3, 0x62, 0x6e, 0xf0, 0x23, 0xba, 0x83, 0xe8, 0xec, 0x47, 0x5b, 0xf3, 0x8d, 0x76, 0x77, 0x7b, 0xc0, 0xc5, 0x01, 0x3b, 0x9d, 0x4d, + 0x51, 0x77, 0x82, 0x75, 0x3e, 0xcd, 0xd0, 0x85, 0x9f, 0x63, 0x42, 0x0f, 0x57, 0xbb, 0xbb, 0x51, 0xe1, 0xf3, 0x8a, 0xb0, 0x2c, 0x98, 0xf8, 0xda, 0xfc, 0xd6, 0xdd, 0x0a, 0xdf, 0x4a, 0xc1, 0xb7, + 0x5e, 0x01, 0xdf, 0x49, 0xc1, 0x5b, 0x3b, 0x6d, 0xf0, 0xde, 0xdb, 0xc7, 0xec, 0xe5, 0xc6, 0x74, 0x5e, 0x31, 0xe6, 0x20, 0x37, 0x66, 0xef, 0x15, 0x63, 0xde, 0xe5, 0xc6, 0xec, 0x7f, 0x43, 0x81, + 0xfe, 0x64, 0x60, 0xfb, 0xc9, 0xc8, 0xf6, 0x73, 0xa1, 0x2d, 0x05, 0x1f, 0x87, 0x94, 0x0e, 0x15, 0xa4, 0x79, 0x8b, 0x1c, 0xf0, 0xe0, 0x1a, 0xca, 0x5d, 0x2a, 0x68, 0xab, 0xe2, 0x20, 0x98, 0xb0, + 0xc6, 0x4d, 0x29, 0x3b, 0xdd, 0xaa, 0x6d, 0xa5, 0xd9, 0x85, 0x4c, 0x5f, 0x7a, 0x18, 0x2a, 0x2a, 0x52, 0xa7, 0xa1, 0x2c, 0xfd, 0xf1, 0x53, 0x54, 0xb9, 0x24, 0x45, 0x7c, 0xd1, 0x16, 0x4a, 0x83, + 0x15, 0x6e, 0x82, 0x9c, 0xb2, 0x4e, 0x64, 0x86, 0x9b, 0x8e, 0x35, 0xb1, 0x8a, 0x38, 0xea, 0x55, 0x61, 0x35, 0xc5, 0x82, 0xe2, 0xc9, 0xfb, 0x30, 0xbc, 0xb9, 0x06, 0xfa, 0x2a, 0xe7, 0xa2, 0x94, + 0x82, 0x6a, 0xbc, 0x82, 0x72, 0x03, 0x2a, 0x6f, 0xe5, 0xa1, 0x24, 0x59, 0x64, 0x81, 0x07, 0x2b, 0x6d, 0xd8, 0x7a, 0x29, 0x20, 0xb1, 0x5b, 0x00, 0x5d, 0xb0, 0x13, 0xb3, 0x06, 0x24, 0x4b, 0x10, + 0x90, 0xc3, 0x40, 0xc8, 0x81, 0xb1, 0xca, 0xc2, 0x3f, 0x82, 0x42, 0x0c, 0xa4, 0x16, 0x3d, 0xad, 0x52, 0x68, 0xdf, 0x0f, 0x82, 0x99, 0x6a, 0xa8, 0x0b, 0x5f, 0xd5, 0x67, 0xc6, 0x38, 0x9c, 0x88, + 0xb3, 0x16, 0x79, 0x7c, 0xb8, 0x19, 0x0f, 0xe9, 0xf6, 0x70, 0x35, 0x47, 0x42, 0xdf, 0x6a, 0x58, 0xd4, 0x88, 0x6d, 0x99, 0xb7, 0x45, 0xd0, 0xae, 0xe3, 0xce, 0xc5, 0x49, 0x36, 0xa9, 0x15, 0xd0, + 0x43, 0x89, 0x04, 0xfa, 0x97, 0x25, 0xca, 0x0d, 0x12, 0xe8, 0x8b, 0xd2, 0xc0, 0xe7, 0x8f, 0x04, 0x94, 0xec, 0xe1, 0xe0, 0xed, 0x73, 0x21, 0x85, 0x33, 0xee, 0xfb, 0xda, 0x94, 0xa7, 0x88, 0x64, + 0x55, 0xfe, 0x04, 0xe4, 0xc8, 0xa3, 0x35, 0xe8, 0xf5, 0xe8, 0x19, 0x30, 0x35, 0x2a, 0xb8, 0x89, 0x5c, 0x48, 0xc0, 0xcb, 0xca, 0x23, 0xdb, 0xd3, 0xc6, 0xca, 0x78, 0x15, 0xf0, 0x6b, 0xee, 0x4c, + 0xc1, 0xc0, 0x4f, 0xe8, 0x19, 0x0e, 0xf6, 0xd1, 0x3c, 0x25, 0xdc, 0x45, 0x73, 0x75, 0xa0, 0x26, 0xde, 0x2d, 0x2b, 0xc9, 0xe9, 0x13, 0x17, 0x2c, 0x1b, 0x70, 0x49, 0xd0, 0x6e, 0xb6, 0x70, 0xd3, + 0xf0, 0x59, 0x98, 0x16, 0xc6, 0x19, 0xf4, 0xa1, 0x1b, 0xb7, 0xcc, 0x68, 0x2e, 0x39, 0x19, 0xa5, 0x16, 0x02, 0xc3, 0x3a, 0xfc, 0x57, 0x70, 0xc0, 0xdf, 0x04, 0x5e, 0x24, 0xbb, 0x2a, 0x20, 0x4f, + 0x4f, 0x4f, 0x59, 0x7b, 0x0f, 0x3c, 0xf9, 0x2f, 0xb8, 0x63, 0xc6, 0x7e, 0xf9, 0x85, 0x65, 0xbb, 0x0e, 0x6b, 0xec, 0x0d, 0xd4, 0xce, 0x58, 0xea, 0xa7, 0x26, 0xca, 0xa6, 0x52, 0x95, 0x4b, 0x7c, + 0x4e, 0x81, 0x8f, 0x95, 0xd0, 0x00, 0x2d, 0xca, 0xa0, 0xee, 0xb9, 0x86, 0xa7, 0x1c, 0xa8, 0x85, 0xb0, 0x61, 0x23, 0x72, 0x48, 0x2d, 0x31, 0xab, 0xd8, 0x0a, 0xcb, 0x08, 0xdb, 0x8a, 0x28, 0x6e, + 0x80, 0xb7, 0x8a, 0xf2, 0xb1, 0xc2, 0xfe, 0xf6, 0xcb, 0xfd, 0xe0, 0x04, 0x3a, 0x5b, 0x30, 0x80, 0x67, 0x17, 0xba, 0x2c, 0x95, 0xc6, 0x50, 0xd3, 0x3e, 0x8a, 0xeb, 0x67, 0x21, 0xcf, 0x44, 0x23, + 0xb1, 0x85, 0x94, 0xfc, 0x85, 0x15, 0xe8, 0x66, 0x46, 0x46, 0x39, 0xd9, 0x68, 0x50, 0x28, 0x1f, 0x1c, 0xa5, 0x6e, 0x9a, 0x47, 0x0c, 0x9d, 0x05, 0x9e, 0x4e, 0xb4, 0x9c, 0x50, 0x3c, 0x4c, 0x6e, + 0xe0, 0x73, 0x2b, 0xdf, 0x74, 0x43, 0xdb, 0x80, 0xd5, 0xfc, 0x84, 0x7b, 0x99, 0xb8, 0x8d, 0xc8, 0x3d, 0x2e, 0x4c, 0x24, 0x43, 0x8e, 0x40, 0xd3, 0x3a, 0x62, 0xe2, 0x89, 0x36, 0xa3, 0xbd, 0x74, + 0xd1, 0x23, 0x27, 0x25, 0xdd, 0x83, 0x8b, 0xde, 0xa6, 0x7a, 0x40, 0x95, 0xde, 0x25, 0x45, 0x65, 0xe3, 0xa8, 0xac, 0x19, 0x4c, 0xe0, 0xe2, 0x07, 0xf5, 0x25, 0xbd, 0xad, 0x83, 0x74, 0xef, 0xf1, + 0x31, 0x3b, 0x2c, 0x00, 0x3a, 0xcc, 0xc1, 0xd0, 0xa0, 0x3c, 0x50, 0x33, 0x07, 0x84, 0xf3, 0x76, 0x0b, 0x78, 0x7e, 0x4e, 0xb1, 0xde, 0x96, 0xe2, 0x2c, 0xe5, 0xca, 0x15, 0x52, 0x02, 0x22, 0xeb, + 0x4c, 0x26, 0x93, 0x7a, 0xda, 0x72, 0x5b, 0x11, 0x37, 0xd8, 0x53, 0x3c, 0x01, 0x61, 0xee, 0x1c, 0xad, 0xcb, 0xd1, 0xe1, 0xcb, 0x60, 0xc2, 0x17, 0xd2, 0xcf, 0x5e, 0x39, 0x41, 0xeb, 0x40, 0xee, + 0x8a, 0xad, 0xcb, 0x56, 0x1e, 0xb3, 0x50, 0x7c, 0xdb, 0xd2, 0x71, 0xad, 0xee, 0xec, 0xd5, 0xf1, 0x6b, 0xf7, 0x50, 0xec, 0x99, 0x5a, 0xbb, 0x27, 0xed, 0x6e, 0x8e, 0xf6, 0xb8, 0x4c, 0x91, 0xf3, + 0x40, 0x96, 0x53, 0x8f, 0xaf, 0x5b, 0xa9, 0xeb, 0x76, 0xea, 0xba, 0xf3, 0x6d, 0xbb, 0x94, 0xf6, 0xfe, 0xff, 0x79, 0x49, 0xa5, 0xc3, 0x7f, 0x37, 0x37, 0xfb, 0x45, 0xdc, 0x84, 0xb3, 0x79, 0xe0, + 0xcb, 0xf5, 0x28, 0xad, 0x88, 0xb6, 0xf8, 0x13, 0x10, 0x72, 0xeb, 0x7f, 0x9d, 0x57, 0x31, 0xc3, 0xce, 0x1e, 0xb1, 0x2c, 0xd1, 0x5a, 0x98, 0xe4, 0x88, 0x8e, 0x3c, 0xff, 0x22, 0xe5, 0xa6, 0x63, + 0x38, 0x82, 0xb4, 0xad, 0x6c, 0xbd, 0xcb, 0xb1, 0xa5, 0xaa, 0x69, 0x48, 0xbc, 0xb5, 0xad, 0xe0, 0x4d, 0x6a, 0x9a, 0x5c, 0x7e, 0x9d, 0x70, 0xba, 0x5c, 0x6d, 0x5f, 0xed, 0x04, 0x87, 0xf5, 0xc6, + 0x2a, 0x96, 0x56, 0xba, 0x0b, 0x31, 0x54, 0xa1, 0x2f, 0xb7, 0xc2, 0xb3, 0x40, 0x8b, 0xb4, 0xbc, 0x3b, 0xb9, 0x4e, 0x7c, 0x4c, 0x51, 0xc5, 0x5e, 0x40, 0xd1, 0xda, 0xab, 0x15, 0xc2, 0xc4, 0x2a, + 0x49, 0x6f, 0x30, 0xbf, 0x5e, 0x25, 0x6c, 0xb1, 0x63, 0x16, 0x28, 0x04, 0x5a, 0xd3, 0xda, 0x58, 0x3f, 0x7d, 0x41, 0x6a, 0xc9, 0x1c, 0xba, 0x78, 0x59, 0x33, 0x87, 0x39, 0xcd, 0xbc, 0x28, 0x7c, + 0x50, 0xf7, 0x18, 0x72, 0xad, 0xc7, 0x9c, 0x31, 0xa6, 0x41, 0x26, 0xbe, 0xc7, 0xff, 0x20, 0x27, 0x1b, 0x7b, 0xa5, 0xc8, 0x41, 0xb6, 0xd2, 0x70, 0xf4, 0x42, 0x88, 0x4d, 0x29, 0x55, 0x35, 0xf1, + 0x5f, 0x89, 0x36, 0x32, 0x9e, 0x7b, 0x3c, 0x8d, 0x7c, 0xf7, 0x2b, 0x34, 0x0f, 0xf3, 0xfb, 0xdb, 0x21, 0xd3, 0x8f, 0xd7, 0x68, 0x44, 0xde, 0x08, 0xd2, 0x0f, 0xe4, 0xaa, 0x02, 0x64, 0x93, 0xbd, + 0x6c, 0x7c, 0x3c, 0x9c, 0xa6, 0x3d, 0x23, 0xc8, 0xdf, 0xd3, 0x79, 0x50, 0x4a, 0x20, 0xff, 0x95, 0xc9, 0xc4, 0x22, 0x2d, 0x58, 0xc5, 0x52, 0x6d, 0x0d, 0x2a, 0x5b, 0xe8, 0x13, 0xa7, 0xad, 0xe9, + 0x89, 0xf1, 0x74, 0xca, 0x05, 0xd7, 0x5d, 0xd2, 0xbc, 0x62, 0xd0, 0x55, 0x55, 0x92, 0x16, 0xe3, 0xcc, 0xbe, 0xd9, 0x81, 0xfa, 0xde, 0xb6, 0xfc, 0x7f, 0x3d, 0x8a, 0x17, 0xfa, 0x20, 0xfd, 0xcc, + 0xbe, 0x8a, 0x47, 0xc7, 0x70, 0x53, 0x9d, 0xf6, 0xd4, 0x0d, 0x91, 0x1f, 0x18, 0x3c, 0x00, 0xff, 0xca, 0xc0, 0xfc, 0xe8, 0x91, 0x3c, 0x8a, 0x54, 0xbc, 0x1c, 0xa1, 0xb1, 0x66, 0xc3, 0x16, 0x19, + 0xa7, 0x4c, 0x75, 0x95, 0xda, 0x36, 0xab, 0x5d, 0xcf, 0x27, 0x20, 0x2f, 0x51, 0x55, 0x1f, 0x53, 0x4f, 0xa8, 0xd3, 0x44, 0xf9, 0x95, 0x42, 0x92, 0xad, 0xdf, 0xd2, 0x0e, 0xb7, 0xd0, 0xc9, 0x3d, + 0xcb, 0x6f, 0xfa, 0x29, 0x7e, 0x36, 0x19, 0xa7, 0xf0, 0xa5, 0x2d, 0x4f, 0x80, 0x23, 0x74, 0x1c, 0x0a, 0xa0, 0x24, 0x97, 0x2e, 0x50, 0xbf, 0xcf, 0x83, 0xea, 0xfa, 0xa3, 0x66, 0xf0, 0x37, 0xb5, + 0xfa, 0x0b, 0x29, 0xf3, 0x16, 0x1a, 0xc0, 0x4c, 0x32, 0x49, 0xbd, 0xba, 0xd7, 0x8d, 0x28, 0x2a, 0xae, 0x36, 0x74, 0xdb, 0xf5, 0xd3, 0xb5, 0x86, 0x2c, 0x35, 0xa2, 0xaa, 0x28, 0x97, 0x2c, 0xbb, + 0x9e, 0x0e, 0xfa, 0xa5, 0x31, 0x8a, 0x2c, 0xc8, 0x5e, 0x57, 0x06, 0x3e, 0xcb, 0x2a, 0x3b, 0xf3, 0x89, 0xeb, 0x1b, 0x5a, 0x70, 0x03, 0x3c, 0xd7, 0x55, 0x95, 0x6f, 0xd7, 0x64, 0xe6, 0xdd, 0x3c, + 0x43, 0x94, 0x1e, 0x17, 0xe9, 0xac, 0xd6, 0xdd, 0xfe, 0x94, 0xbc, 0x5c, 0x2a, 0x25, 0xcf, 0xc9, 0x11, 0xb8, 0xe8, 0x2d, 0xa5, 0xf8, 0x74, 0x99, 0x28, 0xbd, 0x21, 0x87, 0xbd, 0xa0, 0x53, 0x4d, + 0x1a, 0xc8, 0x5d, 0x4f, 0xbf, 0x09, 0x45, 0x8f, 0xb6, 0x85, 0xde, 0x11, 0x30, 0xbb, 0xc0, 0xd6, 0x5f, 0x74, 0x12, 0xaf, 0x75, 0xa8, 0xea, 0xa5, 0xb5, 0x0c, 0xe7, 0xd1, 0x1b, 0x4b, 0xbe, 0xf5, + 0x9d, 0xfb, 0x79, 0x5a, 0xe2, 0xa7, 0xee, 0xf9, 0x27, 0xf8, 0xb9, 0xc3, 0x1b, 0xb4, 0x11, 0xe1, 0x18, 0xee, 0x42, 0x54, 0xaa, 0xf1, 0xf3, 0xf8, 0x82, 0x93, 0x5d, 0xb9, 0x63, 0x1d, 0xb9, 0x91, + 0xf1, 0xb3, 0xfa, 0x92, 0x84, 0xa2, 0x57, 0xed, 0xb0, 0xea, 0x5d, 0x7f, 0xa9, 0x8d, 0x05, 0xee, 0x1c, 0x5f, 0xb8, 0xb3, 0xf9, 0x24, 0x80, 0xdf, 0x4a, 0x4e, 0x0a, 0xaf, 0xa0, 0x39, 0x76, 0xe2, + 0xaf, 0xa6, 0x35, 0xf6, 0xea, 0xdd, 0x52, 0x89, 0xe6, 0x93, 0x12, 0x2b, 0x32, 0x83, 0x48, 0x6b, 0x37, 0xda, 0x23, 0x17, 0xa7, 0xd4, 0x22, 0xf4, 0x74, 0xc2, 0x5b, 0xbe, 0x55, 0x17, 0x9d, 0x60, + 0xa5, 0x03, 0x48, 0x72, 0x7b, 0x97, 0x86, 0x7d, 0xc2, 0x23, 0x7a, 0x78, 0x52, 0xd5, 0xc7, 0xe3, 0x88, 0xe0, 0xd8, 0x80, 0x16, 0x36, 0xd7, 0xa0, 0x2a, 0x86, 0xa0, 0xcd, 0x3d, 0x3a, 0xd5, 0xe7, + 0x7a, 0x50, 0x30, 0xc2, 0x62, 0xc4, 0xbd, 0xe1, 0xc0, 0x4d, 0xbd, 0x4d, 0xa0, 0xe4, 0x18, 0xaa, 0xca, 0x27, 0xd1, 0x05, 0x47, 0xf0, 0x36, 0x9d, 0xbe, 0x03, 0x12, 0x1e, 0xe6, 0x06, 0x9e, 0x63, + 0x89, 0x0f, 0xb5, 0x27, 0x07, 0x79, 0xab, 0x03, 0x5d, 0xb3, 0xb9, 0xea, 0x9b, 0xd6, 0x84, 0xd6, 0xec, 0x86, 0xf3, 0x82, 0x75, 0x3c, 0x44, 0xf6, 0xd3, 0xa7, 0x05, 0x4b, 0x8d, 0xa6, 0xb2, 0x4f, + 0xc7, 0x5e, 0x05, 0x21, 0xe9, 0xcd, 0x8b, 0xfc, 0xa6, 0x59, 0xcf, 0xb1, 0x66, 0x54, 0x1c, 0xd2, 0x52, 0x16, 0xab, 0x38, 0xb7, 0xe1, 0x05, 0xca, 0x4d, 0xaf, 0x73, 0x7a, 0x71, 0x86, 0xb4, 0x98, + 0x1b, 0x9b, 0xc3, 0x25, 0x37, 0x8c, 0x5f, 0x0d, 0x2b, 0x4d, 0x45, 0x33, 0x8c, 0x3e, 0x7a, 0xb2, 0x6b, 0xcb, 0x0f, 0x38, 0x58, 0x74, 0xf5, 0xed, 0xcc, 0x85, 0xe0, 0x34, 0x83, 0xd5, 0xf7, 0xb6, + 0xce, 0xc0, 0x94, 0x4f, 0xd9, 0x9f, 0xb9, 0x03, 0x20, 0x5c, 0x11, 0xe7, 0x2e, 0x3f, 0xd7, 0xe3, 0xcb, 0x2f, 0x78, 0x39, 0xa6, 0xb3, 0x16, 0x98, 0x1e, 0xb3, 0xe7, 0x17, 0x67, 0x08, 0xdc, 0x50, + 0x37, 0x5f, 0x9e, 0x81, 0x40, 0x38, 0x6e, 0x0e, 0x2b, 0xba, 0x6d, 0xc1, 0x68, 0x9a, 0x6c, 0xad, 0x15, 0xe6, 0x6d, 0x6d, 0x9f, 0x8f, 0x38, 0x0a, 0xe7, 0xc9, 0x6c, 0x2c, 0x77, 0x3a, 0xe4, 0x65, + 0x8e, 0xea, 0x58, 0xce, 0x76, 0xa3, 0xd3, 0x5a, 0xc2, 0x55, 0xbf, 0x66, 0x46, 0x3c, 0x41, 0x91, 0xe7, 0xf0, 0xb5, 0x33, 0xb6, 0x36, 0xcc, 0x18, 0xa7, 0x31, 0xeb, 0x73, 0x3e, 0xf2, 0x55, 0xd1, + 0x8c, 0x74, 0x86, 0x84, 0x2b, 0xd0, 0x7b, 0x0e, 0xd9, 0x4d, 0x5a, 0x5e, 0x2f, 0xe3, 0x2a, 0x92, 0x57, 0x1e, 0x57, 0x33, 0xc6, 0x85, 0xc7, 0x42, 0x76, 0x52, 0x67, 0xb0, 0xe8, 0x75, 0x12, 0x61, + 0xc1, 0x22, 0x9f, 0x4a, 0x1f, 0x8a, 0x5e, 0xcb, 0xc5, 0x02, 0xf7, 0x61, 0x78, 0x79, 0x98, 0xce, 0xbc, 0x5e, 0xe3, 0xc1, 0x73, 0x60, 0x1b, 0x9d, 0xa0, 0x89, 0x0b, 0x63, 0x47, 0x15, 0x6f, 0x48, + 0xcb, 0x37, 0x79, 0xe1, 0x12, 0x5f, 0x8d, 0x3e, 0x2d, 0x1f, 0xab, 0xe2, 0x1d, 0xef, 0xff, 0x00, 0x62, 0xbb, 0xd2, 0xb4, 0xfc, 0x3d, 0x00, 0x00, }; + + +//Returns 0 on succses. +//Returns size of file if non-empty +//If positive, populates mfi. +//Returns -1 if can't find file or reached end of file list. +int8_t MFSOpenFile( const char * fname, struct MFSFileInfo * mfi ) +{ +#ifdef CNFG_DISABLE_HTTP_FILES + mfi->filelen = 0; + return -1; +#else + if( strcmp( fname, "/" ) == 0 || strcmp( fname, "index.html" ) == 0 ) + { + mfi->offset = 0; + mfi->filelen = sizeof(webpage_buffer); + return MFS_FILE_COMPRESSED_MEMORY; + } + else + return -1; +#endif +} + +int32_t MFSReadSector( uint8_t* data, struct MFSFileInfo * mfi ) +{ +#ifdef CNFG_DISABLE_HTTP_FILES + return 0; +#else + //returns # of bytes left tin file. + if( !mfi->filelen ) + { + return 0; + } + + int toread = mfi->filelen; + if( toread > MFS_SECTOR ) toread = MFS_SECTOR; + memcpy( data, &webpage_buffer[mfi->offset], toread ); + mfi->offset += toread; + mfi->filelen -= toread; + return mfi->filelen; +#endif +} + +void MFSClose( struct MFSFileInfo * mfi ) +{ +} + +#else + + +//Returns 0 on succses. +//Returns size of file if non-empty +//If positive, populates mfi. +//Returns -1 if can't find file or reached end of file list. +int8_t MFSOpenFile( const char * fname, struct MFSFileInfo * mfi ) +{ + char targfile[1024]; + + if( strlen( fname ) == 0 || fname[strlen(fname)-1] == '/' ) + { + snprintf( targfile, sizeof( targfile ) - 1, "tools/rawdraw_http_files/%s/index.html", fname ); + } + else + { + snprintf( targfile, sizeof( targfile ) - 1, "tools/rawdraw_http_files/%s", fname ); + } + + //printf( ":%s:\n", targfile ); + + FILE * f = mfi->file = fopen( targfile, "rb" ); + if( f <= 0 ) return -1; + //printf( "F: %p\n", f ); + fseek( f, 0, SEEK_END ); + mfi->filelen = ftell( f ); + fseek( f, 0, SEEK_SET ); + return 0; +} + +int32_t MFSReadSector( uint8_t* data, struct MFSFileInfo * mfi ) +{ + if( !mfi->filelen ) + { + return 0; + } + + int toread = fread( data, 1, MFS_SECTOR, mfi->file ); + mfi->filelen -= toread; + return mfi->filelen; +} + +void MFSClose( struct MFSFileInfo * mfi ) +{ + if( mfi->file ) fclose( mfi->file ); +} + + +#endif + + +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +----------------- +Modified 7/98 +By James H. Brown +Still 100% Public Domain + +Corrected a problem which generated improper hash values on 16 bit machines +Routine SHA1Update changed from + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int +len) +to + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned +long len) + +The 'len' parameter was declared an int which works fine on 32 bit machines. +However, on 16 bit machines an int is too small for the shifts being done +against +it. This caused the hash function to generate incorrect values if len was +greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + +Since the file IO in main() reads 16K at a time, any file 8K or larger would +be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million +"a"s). + +I also changed the declaration of variables i & j in SHA1Update to +unsigned long from unsigned int for the same reason. + +These changes should make no difference to any 32 bit implementations since +an +int and a long are the same size in those environments. + +-- +I also corrected a few compiler warnings generated by Borland C. +1. Added #include for exit() prototype +2. Removed unused variable 'j' in SHA1Final +3. Changed exit(0) to return(0) at end of main. + +ALL changes I made can be located by searching for comments containing 'JHB' +----------------- +Modified 8/98 +By Steve Reid +Still 100% public domain + +1- Removed #include and used return() instead of exit() +2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) +3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + +----------------- +Modified 4/01 +By Saul Kravitz +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Modified 07/2002 +By Ralph Giles +Still 100% public domain +modified for use with stdint types, autoconf +code cleanup, removed attribution comments +switched SHA1Final() argument order for consistency +use SHA1_ prefix for public api +move public api to sha1.h +*/ + +/* +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define SHA1HANDSOFF */ + +#include +#include + + +static void RD_SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); + +#define RDrol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +/* FIXME: can we do this in an endian-proof way? */ +#ifdef WORDS_BIGENDIAN +#define RDblk0(i) block->l[i] +#else +#define RDblk0(i) (block->l[i] = (RDrol(block->l[i],24)&0xFF00FF00) \ + |(RDrol(block->l[i],8)&0x00FF00FF)) +#endif +#define RDblk(i) (block->l[i&15] = RDrol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+RDblk0(i)+0x5A827999+RDrol(v,5);w=RDrol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+RDblk(i)+0x5A827999+RDrol(v,5);w=RDrol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+RDblk(i)+0x6ED9EBA1+RDrol(v,5);w=RDrol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+RDblk(i)+0x8F1BBCDC+RDrol(v,5);w=RDrol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+RDblk(i)+0xCA62C1D6+RDrol(v,5);w=RDrol(w,30); + +/* Hash a single 512-bit block. This is the core of the algorithm. */ +static void RD_SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) +{ + uint32_t a, b, c, d, e; + typedef union { + uint8_t c[64]; + uint32_t l[16]; + } CHAR64LONG16; + CHAR64LONG16* block; + +#ifdef SHA1HANDSOFF + static uint8_t workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16*)buffer; +#endif + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1Init - Initialize new context */ +static void RD_SHA1_Init(RD_SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ +static void RD_SHA1_Update(RD_SHA1_CTX* context, const uint8_t* data, const unsigned long len) +{ + size_t i, j; + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + RD_SHA1_Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + RD_SHA1_Transform(context->state, data + i); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); + +#ifdef VERBOSE + SHAPrintContext(context, "after "); +#endif +} + + +/* Add padding and return the message digest. */ +static void RD_SHA1_Final(uint8_t digest[RD_SHA1_DIGEST_SIZE],RD_SHA1_CTX* context) +{ + uint32_t i; + uint8_t finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + RD_SHA1_Update(context, (uint8_t *)"\200", 1); + while ((context->count[0] & 504) != 448) { + RD_SHA1_Update(context, (uint8_t *)"\0", 1); + } + RD_SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ + for (i = 0; i < RD_SHA1_DIGEST_SIZE; i++) { + digest[i] = (uint8_t) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + + /* Wipe variables */ + i = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(finalcount, 0, 8); /* SWR */ + +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ + SHA1_Transform(context->state, context->buffer); +#endif +} + +#ifndef CNFGHTTPSERVERONLY + +/*************************************************************/ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +char * cnfg_http_window_name; + +int rd_request_fullscreen; + +uint32_t * transitbuffer; +int transitlen; +int transitmax; + +uint32_t * backbuffer; +int backbufferlen; +int backbuffermax; + +uint32_t * datacmds; +int curdatacmds; +int maxdatacmds; + +short last_dimensions_w; +short last_dimensions_h; + +short cnfg_req_w; +short cnfg_req_h; + + +void HTTPCustomCallback( ) +{ + if( curhttp->rcb ) + ((void(*)())curhttp->rcb)(); + else + curhttp->isdone = 1; +} + + +//Close of curhttp happened. +void CloseEvent() +{ +} + +static void readrdbuffer_websocket_dat( int len ) +{ + do + { + int bufferok = TCPCanSend( curhttp->socket, 1340 ); + if( transitlen <= 0 || !bufferok || curhttp->bytesleft == -1 ) return; + + int offset = transitlen * 4 - curhttp->bytesleft; + uint8_t * offdata = ((uint8_t*)transitbuffer) + offset; + int tosend = curhttp->bytesleft; + if( tosend > 1340 ) tosend = 1340; + + WebSocketSend( offdata, tosend ); + + //Special - we send an empty frame to indicate competion. + if( tosend == 0 ) + curhttp->bytesleft = -1; + else + curhttp->bytesleft -= tosend; + } while(1); +} + + + +void ConsumeBackBufferForTransit() +{ + int len = transitlen; + int maxv = transitmax; + uint32_t * dcmd = transitbuffer; + + transitbuffer = backbuffer; + transitlen = backbufferlen; + transitmax = backbuffermax; + + backbuffer = dcmd; + backbufferlen = 0; + backbuffermax = maxv; +} + +static void readrdbuffer_websocket_cmd( int len ) +{ + uint8_t buf[1300]; + int i; + + if( len > 1300 ) len = 1300; + + for( i = 0; i < len; i++ ) + { + buf[i] = WSPOPMASK(); + } + + if( strncmp( buf, "SWAP", 4 ) == 0 ) + { + last_dimensions_w = buf[4] | ( buf[5]<<8 ); + last_dimensions_h = buf[6] | ( buf[7]<<8 ); + ConsumeBackBufferForTransit(); + curhttp->bytesleft = transitlen * 4; + } + + if( strncmp( buf, "MOTN", 4 ) == 0 ) + { + int x = buf[4] | ( buf[5]<<8 ); + int y = buf[6] | ( buf[7]<<8 ); + int but = buf[11]; + HandleMotion( x, y, but ); + } + + if( strncmp( buf, "BUTN", 4 ) == 0 ) + { + int x = buf[4] | ( buf[5]<<8 ); + int y = buf[6] | ( buf[7]<<8 ); + int down = buf[10]; + int but = buf[11]; + HandleButton( x, y, but, down ); + } + + if( strncmp( buf, "KEYB", 4 ) == 0 ) + { + int key = buf[6]; + int down = buf[7]; + HandleKey( key, down ); + } +} + + + + + +void NewWebSocket() +{ + if( strncmp( (const char*)curhttp->pathbuffer, "/d/ws/cmdbuf", 9 ) == 0 ) + { + printf( "Got connection.\n" ); + curhttp->rcb = (void*)&readrdbuffer_websocket_dat; + curhttp->rcbDat = (void*)&readrdbuffer_websocket_cmd; + } + else + { + curhttp->is404 = 1; + } +} + +void WebSocketTick() +{ + if( curhttp->rcb ) + { + ((void(*)())curhttp->rcb)(); + } +} + +void WebSocketData( int len ) +{ + if( curhttp->rcbDat ) + { + ((void(*)( int ))curhttp->rcbDat)( len ); + } +} + +void HTTPCustomStart( ) +{ + + curhttp->rcb = 0; + curhttp->bytesleft = 0; + + curhttp->isfirst = 1; + HTTPHandleInternalCallback(); +} + +void QueueCmds( uint32_t * toqueue, int numwords ) +{ + int origcmds = curdatacmds; + curdatacmds += numwords; + if( curdatacmds > maxdatacmds ) + { + datacmds = realloc( datacmds, curdatacmds*4 ); + maxdatacmds = curdatacmds; + } + memcpy( datacmds + origcmds, toqueue, numwords*4 ); +} + +int CNFGSetup( const char * WindowName, int w, int h ) +{ + RunHTTP( 8888 ); + if( cnfg_http_window_name ) free( cnfg_http_window_name ); + cnfg_http_window_name = strdup( WindowName ); + datacmds = 0; + backbuffer = 0; + backbufferlen = 0; + curdatacmds = 0; + maxdatacmds = 0; + rd_request_fullscreen = 0; + cnfg_req_w = w; + cnfg_req_h = h; + return 0; +} + +void CNFGSetupFullscreen( const char * WindowName, int screen_number ) +{ + CNFGSetup( WindowName, -1, -1 ); + rd_request_fullscreen = 1; +} + +int CNFGHandleInput() +{ + TickHTTP(); + return 1; +} + +// command structure: +// 0: Continuation +// 1: Color +// 2: Pixel +// 3: SegmentStart +// 4: RectangleStart +// 5: PolyStart +// 6: PolyContinue +// 7: Blit Pixels +// 8: Clear frame +// 9: Swap Buffers (Used as "end of frame") +// a: CNFGSetLineWidth + +uint32_t CNFGColor( uint32_t RGBA ) +{ + uint32_t cmds[2] = { 0x10000000, RGBA }; + QueueCmds( cmds, 2 ); + return RGBA; +} + +void CNFGTackPixel( short x1, short y1 ) +{ + if( x1 < 0 || x1 > 0x3fff ) return; + if( y1 < 0 || y1 > 0x3fff ) return; + uint32_t cmds[1] = { 0x20000000 | (x1 & 0x3fff ) | ( ( y1 & 0x3fff ) << 14 ) }; + QueueCmds( cmds, 1 ); +} + +void CNFGTackSegment( short x1, short y1, short x2, short y2 ) +{ + uint32_t cmds[3] = { + 0x30000000, + ( (uint16_t)x1 ) | ( (uint16_t)y1 << 16 ), + ( (uint16_t)x2 ) | ( (uint16_t)y2 << 16 ) }; + QueueCmds( cmds, 3 ); +} + +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ) +{ + uint32_t cmds[3] = { + 0x40000000, + ( (uint16_t)x1 ) | ( (uint16_t)y1 << 16 ), + ( (uint16_t)x2 ) | ( (uint16_t)y2 << 16 ) }; + QueueCmds( cmds, 3 ); +} + +void CNFGSetLineWidth( short width ) +{ + uint32_t cmds[1] = { 0xa0000000 | width }; + QueueCmds( cmds, 1 ); +} + +void CNFGTackPoly( RDPoint * points, int verts ) +{ + uint32_t * cmds = alloca( 4 * verts + 4 ); + int i; + cmds[0] = 0x50000000 | verts; + for( i = 0; i < verts; i++ ) + { + uint16_t lx = points[i].x; + uint16_t ly = points[i].y; + cmds[i+1] = lx | ( ly << 16 ); + } + QueueCmds( cmds, verts + 1 ); +} + +void CNFGClearFrame() +{ + int sl = strlen( cnfg_http_window_name ); + int blocks = ( sl + 8 + 8 ) / 4; + uint32_t * cmds = alloca( blocks * 4 ); + cmds[0] = 0x80000000 | blocks | (rd_request_fullscreen<<8) | (sl<<16); + cmds[1] = CNFGBGColor; + cmds[2] = cnfg_req_w | ( cnfg_req_h << 16); + memcpy( cmds+3, cnfg_http_window_name, sl + 1 ); + QueueCmds( cmds, blocks ); +} + +void CNFGSwapBuffers() +{ + uint32_t cmds[1] = { 0x90000000 }; + QueueCmds( cmds, 1 ); + + // And swap. + int len = curdatacmds; + int maxv = maxdatacmds; + uint32_t * dcmd = datacmds; + + datacmds = backbuffer; + curdatacmds = 0; + maxdatacmds = backbuffermax; + + backbuffer = dcmd; + backbufferlen = len; + backbuffermax = maxv; +} + +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ) +{ + if( w < 0 || h < 0 ) return; + + uint32_t * cmds = alloca( 4 * w * h + 8 ); + cmds[0] = 0x70000000 | ( w & 0x3fff ) | ( ( h & 0x3fff ) << 14 ); + cmds[1] = ( x | ( y<<16) ); + int i; + memcpy( cmds + 2, data, w * h * 4 ); + QueueCmds( cmds, w * h + 2 ); +} + +void CNFGGetDimensions( short * x, short * y ) +{ + *x = last_dimensions_w; + *y = last_dimensions_h; +} + +#endif + +#endif + +#elif defined( __wasm__ ) +//Right now, designed for use with https://github.com/cnlohr/rawdrawwasm/ +#include +#include + +extern void __attribute__((import_module("bynsyncify"))) CNFGSwapBuffersInternal(); +void CNFGBlitImageInternal( uint32_t * data, int x, int y, int w, int h ); +void print( double idebug ); +void prints( const char* sdebug ); + + +//Forward declarations that we get from either WASM or our javascript code. +void CNFGClearFrameInternal( uint32_t bgcolor ); + +//The WASM driver handles internal resizing automatically. +#ifndef CNFGRASTERIZER + +void CNFGInternalResize( short x, short y ) +{ +} + +void CNFGFlushRender() +{ + if( !CNFGVertPlace ) return; + CNFGEmitBackendTriangles( CNFGVertDataV, CNFGVertDataC, CNFGVertPlace ); + CNFGVertPlace = 0; +} +void CNFGClearFrame() +{ + CNFGFlushRender(); + CNFGClearFrameInternal( CNFGBGColor ); +} +void CNFGSwapBuffers() +{ + CNFGFlushRender(); + CNFGSwapBuffersInternal( ); +} + +int CNFGHandleInput() +{ + //Do nothing. + //Input is handled on swap frame. + return 1; +} + +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ) +{ + CNFGBlitImageInternal( data, x, y, w, h ); +} + +#else + +//Rasterizer - if you want to do this, you will need to enable blitting in the javascript. +//XXX TODO: NEED MEMORY ALLOCATOR +extern unsigned char __heap_base; +unsigned int bump_pointer = (unsigned int)&__heap_base; +void* malloc(unsigned long size) { + unsigned int ptr = bump_pointer; + bump_pointer += size; + return (void *)ptr; +} +void free(void* ptr) { } + +//Don't call this file yourself. It is intended to be included in any drivers which want to support the rasterizer plugin. + +#ifdef CNFGRASTERIZER +//#include +#include + +uint32_t * CNFGBuffer = 0; +short CNFGBufferx; +short CNFGBuffery; + +#ifdef CNFGOGL +void CNFGFlushRender() +{ +} +#endif + +void CNFGInternalResize( short x, short y ) +{ + CNFGBufferx = x; + CNFGBuffery = y; + if( CNFGBuffer ) free( CNFGBuffer ); + CNFGBuffer = malloc( CNFGBufferx * CNFGBuffery * 4 ); +#ifdef CNFGOGL + void CNFGInternalResizeOGLBACKEND( short w, short h ); + CNFGInternalResizeOGLBACKEND( x, y ); +#endif +} + +#ifdef __wasm__ +static uint32_t SWAPS( uint32_t r ) +{ + uint32_t ret = (r&0xFF)<<24; + r>>=8; + ret |= (r&0xff)<<16; + r>>=8; + ret |= (r&0xff)<<8; + r>>=8; + ret |= (r&0xff)<<0; + return ret; +} +#elif !defined(CNFGOGL) +#define SWAPS(x) (x>>8) +#else +static uint32_t SWAPS( uint32_t r ) +{ + uint32_t ret = (r&0xFF)<<16; + r>>=8; + ret |= (r&0xff)<<8; + r>>=8; + ret |= (r&0xff); + r>>=8; + ret |= (r&0xff)<<24; + return ret; +} +#endif +uint32_t CNFGColor( uint32_t RGB ) +{ + CNFGLastColor = SWAPS(RGB); + return CNFGLastColor; +} + +void CNFGTackSegment( short x1, short y1, short x2, short y2 ) +{ + short tx, ty; + //float slope, lp; + float slope; + + short dx = x2 - x1; + short dy = y2 - y1; + + if( !CNFGBuffer ) return; + + if( dx < 0 ) dx = -dx; + if( dy < 0 ) dy = -dy; + + if( dx > dy ) + { + short minx = (x1 < x2)?x1:x2; + short maxx = (x1 < x2)?x2:x1; + short miny = (x1 < x2)?y1:y2; + short maxy = (x1 < x2)?y2:y1; + float thisy = miny; + slope = (float)(maxy-miny) / (float)(maxx-minx); + + for( tx = minx; tx <= maxx; tx++ ) + { + ty = thisy; + if( tx < 0 || ty < 0 || ty >= CNFGBuffery ) continue; + if( tx >= CNFGBufferx ) break; + CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor; + thisy += slope; + } + } + else + { + short minx = (y1 < y2)?x1:x2; + short maxx = (y1 < y2)?x2:x1; + short miny = (y1 < y2)?y1:y2; + short maxy = (y1 < y2)?y2:y1; + float thisx = minx; + slope = (float)(maxx-minx) / (float)(maxy-miny); + + for( ty = miny; ty <= maxy; ty++ ) + { + tx = thisx; + if( ty < 0 || tx < 0 || tx >= CNFGBufferx ) continue; + if( ty >= CNFGBuffery ) break; + CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor; + thisx += slope; + } + } +} +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ) +{ + short minx = (x1=x2)?x1:x2; + short maxy = (y1>=y2)?y1:y2; + + short x, y; + + if( minx < 0 ) minx = 0; + if( miny < 0 ) miny = 0; + if( maxx >= CNFGBufferx ) maxx = CNFGBufferx-1; + if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1; + + for( y = miny; y <= maxy; y++ ) + { + uint32_t * CNFGBufferstart = &CNFGBuffer[y * CNFGBufferx + minx]; + for( x = minx; x <= maxx; x++ ) + { + (*CNFGBufferstart++) = CNFGLastColor; + } + } +} + +void CNFGTackPoly( RDPoint * points, int verts ) +{ + short minx = 10000, miny = 10000; + short maxx =-10000, maxy =-10000; + short i, x, y; + + //Just in case... + if( verts > 32767 ) return; + + for( i = 0; i < verts; i++ ) + { + RDPoint * p = &points[i]; + if( p->x < minx ) minx = p->x; + if( p->y < miny ) miny = p->y; + if( p->x > maxx ) maxx = p->x; + if( p->y > maxy ) maxy = p->y; + } + + if( miny < 0 ) miny = 0; + if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1; + + for( y = miny; y <= maxy; y++ ) + { + short startfillx = maxx; + short endfillx = minx; + + //Figure out what line segments intersect this line. + for( i = 0; i < verts; i++ ) + { + short pl = i + 1; + if( pl == verts ) pl = 0; + + RDPoint ptop; + RDPoint pbot; + + ptop.x = points[i].x; + ptop.y = points[i].y; + pbot.x = points[pl].x; + pbot.y = points[pl].y; +//printf( "Poly: %d %d\n", pbot.y, ptop.y ); + + if( pbot.y < ptop.y ) + { + RDPoint ptmp; + ptmp.x = pbot.x; + ptmp.y = pbot.y; + pbot.x = ptop.x; + pbot.y = ptop.y; + ptop.x = ptmp.x; + ptop.y = ptmp.y; + } + + //Make sure this line segment is within our range. +//printf( "PT: %d %d %d\n", y, ptop.y, pbot.y ); + if( ptop.y <= y && pbot.y >= y ) + { + short diffy = pbot.y - ptop.y; + uint32_t placey = (uint32_t)(y - ptop.y)<<16; //Scale by 16 so we can do integer math. + short diffx = pbot.x - ptop.x; + short isectx; + + if( diffy == 0 ) + { + if( pbot.x < ptop.x ) + { + if( startfillx > pbot.x ) startfillx = pbot.x; + if( endfillx < ptop.x ) endfillx = ptop.x; + } + else + { + if( startfillx > ptop.x ) startfillx = ptop.x; + if( endfillx < pbot.x ) endfillx = pbot.x; + } + } + else + { + //Inner part is scaled by 65536, outer part must be scaled back. + isectx = (( (placey / diffy) * diffx + 32768 )>>16) + ptop.x; + if( isectx < startfillx ) startfillx = isectx; + if( isectx > endfillx ) endfillx = isectx; + } +//printf( "R: %d %d %d\n", pbot.x, ptop.x, isectx ); + } + } + +//printf( "%d %d %d\n", y, startfillx, endfillx ); + + if( endfillx >= CNFGBufferx ) endfillx = CNFGBufferx - 1; + if( endfillx >= CNFGBufferx ) endfillx = CNFGBuffery - 1; + if( startfillx < 0 ) startfillx = 0; + if( startfillx < 0 ) startfillx = 0; + + unsigned int * bufferstart = &CNFGBuffer[y * CNFGBufferx + startfillx]; + for( x = startfillx; x <= endfillx; x++ ) + { + (*bufferstart++) = CNFGLastColor; + } + } +//exit(1); +} + + +void CNFGClearFrame() +{ + int i, m; + uint32_t col = 0; + short x, y; + CNFGGetDimensions( &x, &y ); + if( x != CNFGBufferx || y != CNFGBuffery || !CNFGBuffer ) + { + CNFGBufferx = x; + CNFGBuffery = y; + CNFGBuffer = malloc( x * y * 8 ); + } + + m = x * y; + col = CNFGColor( CNFGBGColor ); + for( i = 0; i < m; i++ ) + { + CNFGBuffer[i] = col; + } +} + +void CNFGTackPixel( short x, short y ) +{ + if( x < 0 || y < 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return; + CNFGBuffer[x+CNFGBufferx*y] = CNFGLastColor; +} + + +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ) +{ + int ox = x; + int stride = w; + if( w <= 0 || h <= 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return; + if( x < 0 ) { w += x; x = 0; } + if( y < 0 ) { h += y; y = 0; } + + //Switch w,h to x2, y2 + h += y; + w += x; + + if( w >= CNFGBufferx ) { w = CNFGBufferx; } + if( h >= CNFGBuffery ) { h = CNFGBuffery; } + + + for( ; y < h-1; y++ ) + { + x = ox; + uint32_t * indat = data; + uint32_t * outdat = CNFGBuffer + y * CNFGBufferx + x; + for( ; x < w-1; x++ ) + { + uint32_t newm = *(indat++); + uint32_t oldm = *(outdat); + if( (newm & 0xff) == 0xff ) + { + *(outdat++) = newm; + } + else + { + //Alpha blend. + int alfa = newm&0xff; + int onemalfa = 255-alfa; +#ifdef __wasm__ + uint32_t newv = 255<<0; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; +#elif defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) + uint32_t newv = 255UL<<24; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; +#elif defined( ANDROID ) || defined( __android__ ) + uint32_t newv = 255<<16; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; +#elif defined( CNFGOGL ) //OGL, on X11 + uint32_t newv = 255<<16; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; +#else //X11 + uint32_t newv = 255UL<<24; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; +#endif + *(outdat++) = newv; + } + } + data += stride; + } +} + +void CNFGSwapBuffers() +{ + CNFGUpdateScreenWithBitmap( (uint32_t*)CNFGBuffer, CNFGBufferx, CNFGBuffery ); +} + + +#endif + + +extern void CNFGUpdateScreenWithBitmapInternal( uint32_t * data, int w, int h ); +void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h ) +{ + CNFGBlitImageInternal( data, 0, 0, w, h ); + CNFGSwapBuffersInternal(); +} + + +void CNFGSetLineWidth( short width ) +{ + //Rasterizer does not support line width. +} + +void CNFGHandleInput() +{ + //Do nothing. + //Input is handled on swap frame. +} + +#endif + +#elif defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) +//Copyright (c) 2011-2019 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose. +//Portion from: http://en.wikibooks.org/wiki/Windows_Programming/Window_Creation + +#ifndef _CNFGWINDRIVER_C +#define _CNFGWINDRIVER_C + +#include +#include +#include + +HBITMAP CNFGlsBitmap; +HWND CNFGlsHWND; +HDC CNFGlsWindowHDC; +HDC CNFGlsHDC; +HDC CNFGlsHDCBlit; + +int ShouldClose = 0; + +//Queue up lines and points for a faster render. +#ifndef CNFG_WINDOWS_DISABLE_BATCH +#define BATCH_ELEMENTS +#endif + +#define COLORSWAPS( RGB ) \ + ((((RGB )& 0xFF000000)>>24) | ( ((RGB )& 0xFF0000 ) >> 8 ) | ( ((RGB )& 0xFF00 )<<8 )) + + +void CNFGChangeWindowTitle( const char * windowtitle ) +{ + SetWindowTextA( CNFGlsHWND, windowtitle ); +} + +#ifdef CNFGRASTERIZER +//Don't call this file yourself. It is intended to be included in any drivers which want to support the rasterizer plugin. + +#ifdef CNFGRASTERIZER +//#include +#include + +uint32_t * CNFGBuffer = 0; +short CNFGBufferx; +short CNFGBuffery; + +#ifdef CNFGOGL +void CNFGFlushRender() +{ +} +#endif + +void CNFGInternalResize( short x, short y ) +{ + CNFGBufferx = x; + CNFGBuffery = y; + if( CNFGBuffer ) free( CNFGBuffer ); + CNFGBuffer = malloc( CNFGBufferx * CNFGBuffery * 4 ); +#ifdef CNFGOGL + void CNFGInternalResizeOGLBACKEND( short w, short h ); + CNFGInternalResizeOGLBACKEND( x, y ); +#endif +} + +#ifdef __wasm__ +static uint32_t SWAPS( uint32_t r ) +{ + uint32_t ret = (r&0xFF)<<24; + r>>=8; + ret |= (r&0xff)<<16; + r>>=8; + ret |= (r&0xff)<<8; + r>>=8; + ret |= (r&0xff)<<0; + return ret; +} +#elif !defined(CNFGOGL) +#define SWAPS(x) (x>>8) +#else +static uint32_t SWAPS( uint32_t r ) +{ + uint32_t ret = (r&0xFF)<<16; + r>>=8; + ret |= (r&0xff)<<8; + r>>=8; + ret |= (r&0xff); + r>>=8; + ret |= (r&0xff)<<24; + return ret; +} +#endif +uint32_t CNFGColor( uint32_t RGB ) +{ + CNFGLastColor = SWAPS(RGB); + return CNFGLastColor; +} + +void CNFGTackSegment( short x1, short y1, short x2, short y2 ) +{ + short tx, ty; + //float slope, lp; + float slope; + + short dx = x2 - x1; + short dy = y2 - y1; + + if( !CNFGBuffer ) return; + + if( dx < 0 ) dx = -dx; + if( dy < 0 ) dy = -dy; + + if( dx > dy ) + { + short minx = (x1 < x2)?x1:x2; + short maxx = (x1 < x2)?x2:x1; + short miny = (x1 < x2)?y1:y2; + short maxy = (x1 < x2)?y2:y1; + float thisy = miny; + slope = (float)(maxy-miny) / (float)(maxx-minx); + + for( tx = minx; tx <= maxx; tx++ ) + { + ty = thisy; + if( tx < 0 || ty < 0 || ty >= CNFGBuffery ) continue; + if( tx >= CNFGBufferx ) break; + CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor; + thisy += slope; + } + } + else + { + short minx = (y1 < y2)?x1:x2; + short maxx = (y1 < y2)?x2:x1; + short miny = (y1 < y2)?y1:y2; + short maxy = (y1 < y2)?y2:y1; + float thisx = minx; + slope = (float)(maxx-minx) / (float)(maxy-miny); + + for( ty = miny; ty <= maxy; ty++ ) + { + tx = thisx; + if( ty < 0 || tx < 0 || tx >= CNFGBufferx ) continue; + if( ty >= CNFGBuffery ) break; + CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor; + thisx += slope; + } + } +} +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ) +{ + short minx = (x1=x2)?x1:x2; + short maxy = (y1>=y2)?y1:y2; + + short x, y; + + if( minx < 0 ) minx = 0; + if( miny < 0 ) miny = 0; + if( maxx >= CNFGBufferx ) maxx = CNFGBufferx-1; + if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1; + + for( y = miny; y <= maxy; y++ ) + { + uint32_t * CNFGBufferstart = &CNFGBuffer[y * CNFGBufferx + minx]; + for( x = minx; x <= maxx; x++ ) + { + (*CNFGBufferstart++) = CNFGLastColor; + } + } +} + +void CNFGTackPoly( RDPoint * points, int verts ) +{ + short minx = 10000, miny = 10000; + short maxx =-10000, maxy =-10000; + short i, x, y; + + //Just in case... + if( verts > 32767 ) return; + + for( i = 0; i < verts; i++ ) + { + RDPoint * p = &points[i]; + if( p->x < minx ) minx = p->x; + if( p->y < miny ) miny = p->y; + if( p->x > maxx ) maxx = p->x; + if( p->y > maxy ) maxy = p->y; + } + + if( miny < 0 ) miny = 0; + if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1; + + for( y = miny; y <= maxy; y++ ) + { + short startfillx = maxx; + short endfillx = minx; + + //Figure out what line segments intersect this line. + for( i = 0; i < verts; i++ ) + { + short pl = i + 1; + if( pl == verts ) pl = 0; + + RDPoint ptop; + RDPoint pbot; + + ptop.x = points[i].x; + ptop.y = points[i].y; + pbot.x = points[pl].x; + pbot.y = points[pl].y; +//printf( "Poly: %d %d\n", pbot.y, ptop.y ); + + if( pbot.y < ptop.y ) + { + RDPoint ptmp; + ptmp.x = pbot.x; + ptmp.y = pbot.y; + pbot.x = ptop.x; + pbot.y = ptop.y; + ptop.x = ptmp.x; + ptop.y = ptmp.y; + } + + //Make sure this line segment is within our range. +//printf( "PT: %d %d %d\n", y, ptop.y, pbot.y ); + if( ptop.y <= y && pbot.y >= y ) + { + short diffy = pbot.y - ptop.y; + uint32_t placey = (uint32_t)(y - ptop.y)<<16; //Scale by 16 so we can do integer math. + short diffx = pbot.x - ptop.x; + short isectx; + + if( diffy == 0 ) + { + if( pbot.x < ptop.x ) + { + if( startfillx > pbot.x ) startfillx = pbot.x; + if( endfillx < ptop.x ) endfillx = ptop.x; + } + else + { + if( startfillx > ptop.x ) startfillx = ptop.x; + if( endfillx < pbot.x ) endfillx = pbot.x; + } + } + else + { + //Inner part is scaled by 65536, outer part must be scaled back. + isectx = (( (placey / diffy) * diffx + 32768 )>>16) + ptop.x; + if( isectx < startfillx ) startfillx = isectx; + if( isectx > endfillx ) endfillx = isectx; + } +//printf( "R: %d %d %d\n", pbot.x, ptop.x, isectx ); + } + } + +//printf( "%d %d %d\n", y, startfillx, endfillx ); + + if( endfillx >= CNFGBufferx ) endfillx = CNFGBufferx - 1; + if( endfillx >= CNFGBufferx ) endfillx = CNFGBuffery - 1; + if( startfillx < 0 ) startfillx = 0; + if( startfillx < 0 ) startfillx = 0; + + unsigned int * bufferstart = &CNFGBuffer[y * CNFGBufferx + startfillx]; + for( x = startfillx; x <= endfillx; x++ ) + { + (*bufferstart++) = CNFGLastColor; + } + } +//exit(1); +} + + +void CNFGClearFrame() +{ + int i, m; + uint32_t col = 0; + short x, y; + CNFGGetDimensions( &x, &y ); + if( x != CNFGBufferx || y != CNFGBuffery || !CNFGBuffer ) + { + CNFGBufferx = x; + CNFGBuffery = y; + CNFGBuffer = malloc( x * y * 8 ); + } + + m = x * y; + col = CNFGColor( CNFGBGColor ); + for( i = 0; i < m; i++ ) + { + CNFGBuffer[i] = col; + } +} + +void CNFGTackPixel( short x, short y ) +{ + if( x < 0 || y < 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return; + CNFGBuffer[x+CNFGBufferx*y] = CNFGLastColor; +} + + +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ) +{ + int ox = x; + int stride = w; + if( w <= 0 || h <= 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return; + if( x < 0 ) { w += x; x = 0; } + if( y < 0 ) { h += y; y = 0; } + + //Switch w,h to x2, y2 + h += y; + w += x; + + if( w >= CNFGBufferx ) { w = CNFGBufferx; } + if( h >= CNFGBuffery ) { h = CNFGBuffery; } + + + for( ; y < h-1; y++ ) + { + x = ox; + uint32_t * indat = data; + uint32_t * outdat = CNFGBuffer + y * CNFGBufferx + x; + for( ; x < w-1; x++ ) + { + uint32_t newm = *(indat++); + uint32_t oldm = *(outdat); + if( (newm & 0xff) == 0xff ) + { + *(outdat++) = newm; + } + else + { + //Alpha blend. + int alfa = newm&0xff; + int onemalfa = 255-alfa; +#ifdef __wasm__ + uint32_t newv = 255<<0; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; +#elif defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) + uint32_t newv = 255UL<<24; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; +#elif defined( ANDROID ) || defined( __android__ ) + uint32_t newv = 255<<16; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; +#elif defined( CNFGOGL ) //OGL, on X11 + uint32_t newv = 255<<16; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; +#else //X11 + uint32_t newv = 255UL<<24; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; +#endif + *(outdat++) = newv; + } + } + data += stride; + } +} + +void CNFGSwapBuffers() +{ + CNFGUpdateScreenWithBitmap( (uint32_t*)CNFGBuffer, CNFGBufferx, CNFGBuffery ); +} + + +#endif + + +void InternalHandleResize() +{ + if( CNFGlsBitmap ) DeleteObject( CNFGlsBitmap ); + + CNFGInternalResize( CNFGBufferx, CNFGBuffery ); + CNFGlsBitmap = CreateBitmap( CNFGBufferx, CNFGBuffery, 1, 32, CNFGBuffer ); + SelectObject( CNFGlsHDC, CNFGlsBitmap ); + CNFGInternalResize( CNFGBufferx, CNFGBuffery); +} +#else +static short CNFGBufferx, CNFGBuffery; +static void InternalHandleResize(); +#endif + + +#ifdef CNFGOGL +#include +static HGLRC hRC=NULL; +static void InternalHandleResize() { } +void CNFGSwapBuffers() +{ +#ifdef CNFG_BATCH +#ifndef CNFGCONTEXTONLY + CNFGFlushRender(); +#endif +#endif + + SwapBuffers(CNFGlsWindowHDC); +} +#endif + +CNFGCursorShape CNFGCurShape = CNFG_CURSOR_ARROW; + +void CNFGSetMousePosition( int x, int y ) { + RECT window; + + int bordersize = GetSystemMetrics( SM_CXSIZEFRAME ); + + GetWindowRect( CNFGlsHWND, &window ); + x += window.left + bordersize; + y += window.top + GetSystemMetrics( SM_CYCAPTION ) + bordersize; + + SetCursorPos( x, y ); +} + +void CNFGConfineMouse( int confined ) { + if ( !confined ) { + ClipCursor( NULL ); + return; + } + + int bordersize = GetSystemMetrics( SM_CXSIZEFRAME ); + RECT window; + GetWindowRect( CNFGlsHWND, &window ); + window.left += bordersize; + window.top += GetSystemMetrics( SM_CYCAPTION ) + bordersize; + window.right -= bordersize; + window.bottom -= bordersize; + + ClipCursor( &window ); +} + +void CNFGSetCursor( CNFGCursorShape shape ) { + if (shape == CNFGCurShape ) return; + + // If the current shape's visibility is the opposite of the new shape, toggle the cursor visibility + // According to MSDN, ShowCursor increments and decrements an internal counter, which is used to determine visibility + // The internal counter seemingly has no limit + if ( shape == CNFG_CURSOR_HIDDEN && CNFGCurShape != CNFG_CURSOR_HIDDEN ) ShowCursor( FALSE ); + else if ( shape != CNFG_CURSOR_HIDDEN && CNFGCurShape == CNFG_CURSOR_HIDDEN ) ShowCursor( TRUE ); + + CNFGCurShape = shape; +} + +void CNFGGetDimensions( short * x, short * y ) +{ + static short lastx, lasty; + RECT window; + GetClientRect( CNFGlsHWND, &window ); + CNFGBufferx = (short)( window.right - window.left); + CNFGBuffery = (short)( window.bottom - window.top); + if( CNFGBufferx != lastx || CNFGBuffery != lasty ) + { + lastx = CNFGBufferx; + lasty = CNFGBuffery; + #ifndef CNFGCONTEXTONLY + CNFGInternalResize( lastx, lasty ); + #endif + InternalHandleResize(); + } + *x = CNFGBufferx; + *y = CNFGBuffery; +} + +#ifndef CNFGOGL +void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h ) +{ + RECT r; + + SelectObject( CNFGlsHDC, CNFGlsBitmap ); + SetBitmapBits(CNFGlsBitmap,w*h*4,data); + BitBlt(CNFGlsWindowHDC, 0, 0, w, h, CNFGlsHDC, 0, 0, SRCCOPY); + UpdateWindow( CNFGlsHWND ); + + short thisw, thish; + + //Check to see if the window is closed. + if( !IsWindow( CNFGlsHWND ) ) + { + exit( 0 ); + } + + GetClientRect( CNFGlsHWND, &r ); + thisw = (short)(r.right - r.left); + thish = (short)(r.bottom - r.top); + if( thisw != CNFGBufferx || thish != CNFGBuffery ) + { + CNFGBufferx = thisw; + CNFGBuffery = thish; + InternalHandleResize(); + } +} +#endif + +void CNFGTearDown() +{ + PostQuitMessage(0); + ShouldClose = 1; +} + +//This was from the article +LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) + { +#ifndef CNFGOGL + case WM_SYSCOMMAND: //Not sure why, if deactivated, the dc gets unassociated? + if( wParam == SC_RESTORE || wParam == SC_MAXIMIZE || wParam == SC_SCREENSAVE ) + { + SelectObject( CNFGlsHDC, CNFGlsBitmap ); + SelectObject( CNFGlsWindowHDC, CNFGlsBitmap ); + } + break; +#endif + case WM_KILLFOCUS: + case WM_SETFOCUS: + CNFGLastScancode = 0; + HandleKey( CNFG_KEY_FOCUS, msg == WM_SETFOCUS ); + return 0; + case WM_CLOSE: + if( HandleDestroy() ) + return 0; + break; + case WM_DESTROY: + CNFGTearDown(); + return 0; + } + return DefWindowProc(hwnd, msg, wParam, lParam); +} + +int CNFGSetupWinInternal( const char * name_of_window, int width, int height, int isFullscreen ); + +void CNFGSetupFullscreen( const char * WindowName, int screen_number ) +{ + // Get primary monitor dimensions, but default to 1920x1080 + int monitorW = GetSystemMetrics(SM_CXSCREEN); + if(0 == monitorW) + { + monitorW = 1920; + } + + int monitorH = GetSystemMetrics(SM_CYSCREEN); + if(0 == monitorH) + { + monitorH = 1080; + } + + CNFGSetupWinInternal(WindowName, monitorW, monitorH, 1); +} + +int CNFGSetup( const char * name_of_window, int width, int height ) +{ + return CNFGSetupWinInternal(name_of_window, width, height, 0); +} + +//This was from the article, too... well, mostly. +int CNFGSetupWinInternal( const char * name_of_window, int width, int height, int isFullscreen ) +{ + static LPCSTR szClassName = "MyClass"; + RECT client, window; + WNDCLASSA wnd; + int w, h, wd, hd; + int show_window = 1; + HINSTANCE hInstance = GetModuleHandle(NULL); + + if( width < 0 ) + { + show_window = 0; + width = -width; + } + if( height < 0 ) + { + show_window = 0; + height = -height; + } + + CNFGBufferx = (short)width; + CNFGBuffery = (short)height; + + wnd.style = CS_HREDRAW | CS_VREDRAW; //we will explain this later + wnd.lpfnWndProc = MyWndProc; + wnd.cbClsExtra = 0; + wnd.cbWndExtra = 0; + wnd.hInstance = hInstance; + wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION); //default icon + wnd.hCursor = LoadCursor(NULL, IDC_ARROW); //default arrow mouse cursor + wnd.hbrBackground = (HBRUSH)(COLOR_BACKGROUND); + wnd.lpszMenuName = NULL; //no menu + wnd.lpszClassName = szClassName; + + if(!RegisterClassA(&wnd)) //register the WNDCLASS + { + MessageBoxA(NULL, "This Program Requires Windows NT", "Error", MB_OK); + } + +#ifdef UNICODE + // CreateWindowA **requires** unicode window name even in non-unicode mode. + int wlen = strlen( name_of_window ); + char * unicodeao = (char*)alloca( wlen * 2 + 2 ); + int i; + for( i = 0; i <= wlen; i++ ) + { + unicodeao[i * 2 + 1] = 0; + unicodeao[i * 2 + 0] = name_of_window[i]; + } + name_of_window = unicodeao; +#endif + + + CNFGlsHWND = CreateWindowA(szClassName, + name_of_window, //name_of_window, but must always be + isFullscreen ? (WS_MAXIMIZE | WS_POPUP) : (WS_OVERLAPPEDWINDOW), //basic window style + CW_USEDEFAULT, + CW_USEDEFAULT, //set starting point to default value + CNFGBufferx, + CNFGBuffery, //set all the dimensions to default value + NULL, //no parent window + NULL, //no menu + hInstance, + NULL); //no parameters to pass + + CNFGlsWindowHDC = GetDC( CNFGlsHWND ); + +#ifdef CNFGOGL + //From NeHe + static PIXELFORMATDESCRIPTOR pfd = + { + sizeof(PIXELFORMATDESCRIPTOR), + 1, + PFD_DRAW_TO_WINDOW | + PFD_SUPPORT_OPENGL | + PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, + 24, + 8, 0, 8, 8, 8, 16, + 8, + 24, + 32, + 8, 8, 8, 8, + 16, + 0, + 0, + PFD_MAIN_PLANE, + 0, + 0, 0, 0 + }; + GLuint PixelFormat = ChoosePixelFormat( CNFGlsWindowHDC, &pfd ); + if( !SetPixelFormat( CNFGlsWindowHDC, PixelFormat, &pfd ) ) + { + MessageBoxA( 0, "Could not create PFD for OpenGL Context\n", 0, 0 ); + exit( -1 ); + } + if (!(hRC=wglCreateContext(CNFGlsWindowHDC))) // Are We Able To Get A Rendering Context? + { + MessageBoxA( 0, "Could not create OpenGL Context\n", 0, 0 ); + exit( -1 ); + } + if(!wglMakeCurrent(CNFGlsWindowHDC,hRC)) // Try To Activate The Rendering Context + { + MessageBoxA( 0, "Could not current OpenGL Context\n", 0, 0 ); + exit( -1 ); + } +#endif + + CNFGlsHDC = CreateCompatibleDC( CNFGlsWindowHDC ); + CNFGlsHDCBlit = CreateCompatibleDC( CNFGlsWindowHDC ); + CNFGlsBitmap = CreateCompatibleBitmap( CNFGlsWindowHDC, CNFGBufferx, CNFGBuffery ); + SelectObject( CNFGlsHDC, CNFGlsBitmap ); + + //lsClearBrush = CreateSolidBrush( CNFGBGColor ); + //lsHBR = CreateSolidBrush( 0xFFFFFF ); + //lsHPEN = CreatePen( PS_SOLID, 0, 0xFFFFFF ); + + if( show_window ) + ShowWindow(CNFGlsHWND, isFullscreen ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); //display the window on the screen + + //Once set up... we have to change the window's borders so we get the client size right. + GetClientRect( CNFGlsHWND, &client ); + GetWindowRect( CNFGlsHWND, &window ); + w = ( window.right - window.left); + h = ( window.bottom - window.top); + wd = w - client.right; + hd = h - client.bottom; + MoveWindow( CNFGlsHWND, window.left, window.top, CNFGBufferx + wd, CNFGBuffery + hd, 1 ); + + InternalHandleResize(); + +#ifdef CNFG_BATCH +#ifndef CNFGCONTEXTONLY + CNFGSetupBatchInternal(); +#endif +#endif + + ShouldClose = 0; + + return 0; +} + + + +int CNFGHandleInput() +{ +#ifdef CNFGOGL + if (ShouldClose) + exit(0); +#endif + + MSG msg; + while( PeekMessage( &msg, NULL, 0, 0xFFFF, 1 ) ) + { + TranslateMessage(&msg); + MSG charMSG; + + switch( msg.message ) + { + case WM_MOUSEMOVE: + HandleMotion( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, ( (msg.wParam & 0x01)?1:0) | ((msg.wParam & 0x02)?2:0) | ((msg.wParam & 0x10)?4:0) ); + break; + case WM_LBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 1, 1 ); break; + case WM_RBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 2, 1 ); break; + case WM_MBUTTONDOWN: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 3, 1 ); break; + case WM_LBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 1, 0 ); break; + case WM_RBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 2, 0 ); break; + case WM_MBUTTONUP: HandleButton( (msg.lParam & 0xFFFF), (msg.lParam>>16) & 0xFFFF, 3, 0 ); break; + case WM_KEYDOWN: + // Check if there is a WM_CHAR message in the queue. If there is one, put it into CNFGLastCharacter + // Otherwise, we don't want HandleKey to handle the wrong character, so set to 0 + if (PeekMessage(&charMSG, NULL, WM_CHAR, WM_CHAR, PM_REMOVE)) CNFGLastCharacter = charMSG.wParam; + else CNFGLastCharacter = 0; + + // fall through + case WM_KEYUP: + CNFGLastScancode = (msg.lParam >> 16) & 0xFF; + + if (msg.lParam & 0x01000000) HandleKey( (int) msg.wParam + 0x7C , (msg.message==WM_KEYDOWN) ); + else HandleKey( (int) msg.wParam, (msg.message==WM_KEYDOWN) ); + + // Don't confuse the program when CNFGLastCharacter is set on WM_KEYUP. + // Since we shouldn't be using CNFGLastCharacter outside HandleKey anyways, just clear it here. + CNFGLastCharacter = 0; + break; + case WM_MOUSEWHEEL: + { + POINT p = { 0 }; + p.x = LOWORD( msg.lParam ); + p.y = HIWORD( msg.lParam ); + ScreenToClient(CNFGlsHWND, &p); + HandleButton(p.x, p.y, GET_WHEEL_DELTA_WPARAM(msg.wParam) > 0 ? 0x0E : 0x0F, 1); + } break; + default: + DispatchMessage(&msg); + break; + } + } + + return !ShouldClose; +} + +#ifndef CNFGOGL + +#ifndef CNFGRASTERIZER + +static HBITMAP lsBackBitmap; +static HBRUSH lsHBR; +static HPEN lsHPEN; +static HBRUSH lsClearBrush; + +static void InternalHandleResize() +{ + DeleteObject( lsBackBitmap ); + lsBackBitmap = CreateCompatibleBitmap( CNFGlsHDC, CNFGBufferx, CNFGBuffery ); + SelectObject( CNFGlsHDC, lsBackBitmap ); +} + +#ifdef BATCH_ELEMENTS + +static int linelisthead; +static int pointlisthead; +static int polylisthead; +static int polylistindex; +static POINT linelist[4096*3]; +static DWORD twoarray[4096]; +static POINT pointlist[4096]; +static POINT polylist[8192]; +static INT polylistcutoffs[8192]; + + +static int last_linex; +static int last_liney; +static int possible_lastline; + +void FlushTacking() +{ + int i; + + if( twoarray[0] != 2 ) + for( i = 0; i < 4096; i++ ) twoarray[i] = 2; + + if( linelisthead ) + { + PolyPolyline( CNFGlsHDC, linelist, twoarray, linelisthead ); + linelisthead = 0; + } + + if( polylistindex ) + { + PolyPolygon( CNFGlsHDC, polylist, polylistcutoffs, polylistindex ); + polylistindex = 0; + polylisthead = 0; + } + + if( possible_lastline ) + CNFGTackPixel( (short)last_linex, (short)last_liney ); + possible_lastline = 0; + + //XXX TODO: Consider locking the bitmap, and manually drawing the pixels. + if( pointlisthead ) + { + for( i = 0; i < pointlisthead; i++ ) + { + SetPixel( CNFGlsHDC, pointlist[i].x, pointlist[i].y, CNFGLastColor ); + } + pointlisthead = 0; + } +} +#endif + +uint32_t CNFGColor( uint32_t RGB ) +{ + RGB = COLORSWAPS( RGB ); + if( CNFGLastColor == RGB ) return RGB; + +#ifdef BATCH_ELEMENTS + FlushTacking(); +#endif + + CNFGLastColor = RGB; + + DeleteObject( lsHBR ); + lsHBR = CreateSolidBrush( RGB ); + SelectObject( CNFGlsHDC, lsHBR ); + + DeleteObject( lsHPEN ); + lsHPEN = CreatePen( PS_SOLID, 0, RGB ); + SelectObject( CNFGlsHDC, lsHPEN ); + + return RGB; +} + +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ) +{ + static int pbw, pbh; + static HBITMAP pbb; + if( !pbb || pbw != w || pbh !=h ) + { + if( pbb ) DeleteObject( pbb ); + pbb = CreateBitmap( w, h, 1, 32, 0 ); + pbh = h; + pbw = w; + } + SetBitmapBits(pbb,w*h*4,data); + SelectObject( CNFGlsHDCBlit, pbb ); + BitBlt(CNFGlsHDC, x, y, w, h, CNFGlsHDCBlit, 0, 0, SRCCOPY); +} + +void CNFGTackSegment( short x1, short y1, short x2, short y2 ) +{ +#ifdef BATCH_ELEMENTS + + if( ( x1 != last_linex || y1 != last_liney ) && possible_lastline ) + { + CNFGTackPixel( (short)last_linex, (short)last_liney ); + } + + if( x1 == x2 && y1 == y2 ) + { + CNFGTackPixel( x1, y1 ); + possible_lastline = 0; + return; + } + + last_linex = x2; + last_liney = y2; + possible_lastline = 1; + + if( x1 != x2 || y1 != y2 ) + { + linelist[linelisthead*2+0].x = x1; + linelist[linelisthead*2+0].y = y1; + linelist[linelisthead*2+1].x = x2; + linelist[linelisthead*2+1].y = y2; + linelisthead++; + if( linelisthead >= 2048 ) FlushTacking(); + } +#else + POINT pt[2] = { {x1, y1}, {x2, y2} }; + Polyline( CNFGlsHDC, pt, 2 ); + SetPixel( CNFGlsHDC, x1, y1, CNFGLastColor ); + SetPixel( CNFGlsHDC, x2, y2, CNFGLastColor ); +#endif +} + +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ) +{ +#ifdef BATCH_ELEMENTS + FlushTacking(); +#endif + RECT r; + if( x1 < x2 ) { r.left = x1; r.right = x2; } + else { r.left = x2; r.right = x1; } + if( y1 < y2 ) { r.top = y1; r.bottom = y2; } + else { r.top = y2; r.bottom = y1; } + FillRect( CNFGlsHDC, &r, lsHBR ); +} + +void CNFGClearFrame() +{ +#ifdef BATCH_ELEMENTS + FlushTacking(); +#endif + RECT r = { 0, 0, CNFGBufferx, CNFGBuffery }; + DeleteObject( lsClearBrush ); + lsClearBrush = CreateSolidBrush( COLORSWAPS(CNFGBGColor) ); + HBRUSH prevBrush = (HBRUSH)SelectObject( CNFGlsHDC, lsClearBrush ); + FillRect( CNFGlsHDC, &r, lsClearBrush); + SelectObject( CNFGlsHDC, prevBrush ); +} + +void CNFGTackPoly( RDPoint * points, int verts ) +{ +#ifdef BATCH_ELEMENTS + if( verts > 8192 ) + { + FlushTacking(); + //Fall-through + } + else + { + if( polylistindex >= 8191 || polylisthead + verts >= 8191 ) + { + FlushTacking(); + } + int i; + for( i = 0; i < verts; i++ ) + { + polylist[polylisthead].x = points[i].x; + polylist[polylisthead].y = points[i].y; + polylisthead++; + } + polylistcutoffs[polylistindex++] = verts; + return; + } +#endif + { + int i; + POINT * t = (POINT*)alloca( sizeof( POINT ) * verts ); + for( i = 0; i < verts; i++ ) + { + t[i].x = points[i].x; + t[i].y = points[i].y; + } + Polygon( CNFGlsHDC, t, verts ); + } +} + + +void CNFGTackPixel( short x1, short y1 ) +{ +#ifdef BATCH_ELEMENTS + pointlist[pointlisthead+0].x = x1; + pointlist[pointlisthead+0].y = y1; + pointlisthead++; + + if( pointlisthead >=4096 ) FlushTacking(); +#else + SetPixel( CNFGlsHDC, x1, y1, CNFGLastColor ); +#endif + +} + +void CNFGSwapBuffers() +{ +#ifdef BATCH_ELEMENTS + FlushTacking(); +#endif + int thisw, thish; + + RECT r; + BitBlt( CNFGlsWindowHDC, 0, 0, CNFGBufferx, CNFGBuffery, CNFGlsHDC, 0, 0, SRCCOPY ); + UpdateWindow( CNFGlsHWND ); + //Check to see if the window is closed. + if( !IsWindow( CNFGlsHWND ) ) + { + exit( 0 ); + } + + GetClientRect( CNFGlsHWND, &r ); + thisw = r.right - r.left; + thish = r.bottom - r.top; + + if( thisw != CNFGBufferx || thish != CNFGBuffery ) + { + CNFGBufferx = (short)thisw; + CNFGBuffery = (short)thish; + InternalHandleResize(); + } +} + +void CNFGInternalResize( short bfx, short bfy ) { } +#endif + +#endif + +#endif // _CNFGWINDRIVER_C + + +#elif defined( EGL_LEAN_AND_MEAN ) +//Copyright (c) 2011, 2017, 2018, 2020 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose. + +//This driver cannot create an OpenGL Surface, but can be used for computing for background tasks. + +//NOTE: This is a truly incomplete driver - if no EGL surface is available, it does not support direct buffer rendering. +//Additionally no input is connected. + + +#include +#include + +#include +#include +#include +#include +#include +#include + + +static const EGLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_NONE +}; + +EGLint context_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE +}; + +static int pbufferWidth = 0; +static int pbufferHeight = 0; + +static EGLint pbufferAttribs[] = { + EGL_WIDTH, 0, + EGL_HEIGHT, 0, + EGL_NONE, +}; + +EGLDisplay eglDpy = 0; +EGLContext eglCtx = 0; +EGLSurface eglSurf = 0; + +void CNFGGetDimensions( short * x, short * y ) +{ + *x = pbufferWidth; + *y = pbufferHeight; +} + +void CNFGChangeWindowTitle( const char * WindowName ) +{ +} + +void CNFGSetupFullscreen( const char * WindowName, int screen_no ) +{ + //Fullscreen is meaningless for this driver, since it doesn't really open a window. + CNFGSetup( WindowName, 1024, 1024 ); +} + +void CNFGTearDown() +{ + if( eglDpy ) + { + eglTerminate( eglDpy ); + } + //Unimplemented. +} + +int CNFGSetup( const char * WindowName, int w, int h ) +{ + eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + atexit( CNFGTearDown ); + printf( "EGL Display: %p\n", eglDpy ); + + pbufferAttribs[1] = pbufferWidth = w; + pbufferAttribs[3] = pbufferHeight = h; + + EGLint major, minor; + eglInitialize(eglDpy, &major, &minor); + + EGLint numConfigs=0; + EGLConfig eglCfg=NULL; + + eglChooseConfig(eglDpy, configAttribs, 0, 0, &numConfigs); //this gets number of configs + if (numConfigs) { + eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs); + printf( " EGL config found\n" ); + } else { + printf( " Error could not find a valid config avail.. \n" ); + } + + printf( "EGL Major Minor: %d %d\n", major, minor ); + eglBindAPI(EGL_OPENGL_API); + eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, context_attribs); + int err = eglGetError(); if(err != EGL_SUCCESS) { printf("1. Error %d\n", err); } + printf( "EGL Got context: %p\n", eglCtx ); + + if( w > 0 && h > 0 ) + { + eglSurf = eglCreatePbufferSurface(eglDpy, eglCfg, pbufferAttribs); + eglMakeCurrent(eglDpy, eglSurf, eglSurf, eglCtx); + printf( "EGL Current, with surface %p\n", eglSurf ); + //Actually have a surface. Need to allocate it. + EGLint surfwid; + EGLint surfht; + eglQuerySurface(eglDpy, eglSurf, EGL_WIDTH, &surfwid); + eglQuerySurface(eglDpy, eglSurf, EGL_HEIGHT, &surfht); + printf("Window dimensions: %d x %d\n", surfwid, surfht); + } + else + { + eglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, eglCtx); + printf( "EGL Current, no surface.\n" ); + } + return 0; +} + +int CNFGHandleInput() +{ + //Stubbed (No input) + return 1; +} + +void CNFGSetVSync( int vson ) +{ + //No-op +} + +void CNFGSwapBuffers() +{ + //No-op +} + + +#elif defined( __android__ ) || defined( ANDROID ) +/* + * Copyright (c) 2011-2013 Luc Verhaegen + * Copyright (c) 2018-2020 <>< Charles Lohr + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + + +#if defined( __android__ ) && !defined( ANDROID ) +#define ANDROID +#endif + +//Note: This interface provides the following two things privately. +//you may "extern" them in your code. + + +#ifdef ANDROID + + +#ifndef _CNFG_ANDROID_H +#define _CNFG_ANDROID_H + +//This file contains the additional functions that are available on the Android platform. +//In order to build rawdraw for Android, please compile CNFGEGLDriver.c with -DANDROID + +// Tricky: Android headers are confused by c++ if linking statically. +#ifdef __cplusplus +extern "C" { + int __system_property_get(const char* __name, char* __value); +}; +#endif + +extern struct android_app * gapp; +void AndroidMakeFullscreen(); +const char* AndroidGetExternalFilesDir(); +int AndroidHasPermissions(const char* perm_name); +void AndroidRequestAppPermissions(const char * perm); +void AndroidDisplayKeyboard(int pShow); +int AndroidGetUnicodeChar( int keyCode, int metaState ); +void AndroidSendToBack( int param ); + +extern int android_sdk_version; //Derived at start from property ro.build.version.sdk +extern int android_width, android_height; +extern int UpdateScreenWithBitmapOffsetX; +extern int UpdateScreenWithBitmapOffsetY; +extern void (*HandleCustomEventCallback)(); +extern void (*HandleWindowTermination)(); + + +// If you need them, these are the names of raw EGL symbols. +//extern EGLDisplay egl_display; +//extern EGLSurface egl_surface; +//extern EGLContext egl_context; +//extern EGLConfig egl_config; + + +//You must implement these. +void HandleResume(); +void HandleSuspend(); + + +//Departures: + +// HandleMotion's "mask" parameter is actually just an index, not a mask + +// CNFGSetup / CNFGSetupFullScreen only controls whether or not the navigation +// decoration is removed. Fullscreen means *full screen* To choose fullscreen +// or not fullscrene, modify, in your AndroidManifest.xml file, the application +// section to either contain or not contain: +// android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + + +// For debugging: + +#if defined( ANDROID ) && !defined( __cplusplus ) + +#include + +static inline void PrintClassOfObject( jobject bundle ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jclass myclass = env->GetObjectClass( envptr, bundle ); + jmethodID mid = env->GetMethodID( envptr, myclass, "getClass", "()Ljava/lang/Class;"); + jobject clsObj = env->CallObjectMethod( envptr, bundle, mid ); + jclass clazzz = env->GetObjectClass( envptr, clsObj ); + mid = env->GetMethodID(envptr, clazzz, "getName", "()Ljava/lang/String;"); + jstring strObj = (jstring)env->CallObjectMethod( envptr, clsObj, mid); + const char * name = env->GetStringUTFChars( envptr, strObj, 0); + printf( "Class type: %s\n", name ); + + env->DeleteLocalRef( envptr, myclass ); + env->DeleteLocalRef( envptr, clsObj ); + env->DeleteLocalRef( envptr, clazzz ); + env->ReleaseStringUTFChars(envptr, strObj, name); + env->DeleteLocalRef( envptr, strObj ); +} + +static inline void PrintObjectString( jobject bundle ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jclass myclass = env->GetObjectClass( envptr, bundle ); + jmethodID toStringMethod = env->GetMethodID( envptr, myclass, "toString", "()Ljava/lang/String;"); + jstring strObjDescr = (jstring)env->CallObjectMethod( envptr, bundle, toStringMethod); + const char *descr = env->GetStringUTFChars( envptr, strObjDescr, 0); + printf( "String: %s\n", descr ); + + env->DeleteLocalRef( envptr, myclass ); + env->ReleaseStringUTFChars( envptr, strObjDescr, descr ); + env->DeleteLocalRef( envptr, strObjDescr ); +} + + +static inline void DumpObjectClassProperties( jobject objToDump ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jclass mpclass = env->GetObjectClass( envptr, objToDump ); + + jmethodID midGetClass = env->GetMethodID( envptr, mpclass, "getClass", "()Ljava/lang/Class;"); + jclass ClassClass = env->FindClass(envptr, "java/lang/Class"); + jobject clsObj = env->CallObjectMethod( envptr, objToDump, midGetClass); + + jmethodID gnid = env->GetMethodID(envptr, ClassClass, "getName", "()Ljava/lang/String;"); + jstring nameObj = (jstring)env->CallObjectMethod( envptr, clsObj, gnid); + const char *name = env->GetStringUTFChars( envptr, nameObj, 0); + printf( "Class Name: %s\n", name ); + env->ReleaseStringUTFChars(envptr, nameObj, name); + env->DeleteLocalRef( envptr, nameObj ); + + jmethodID getMethodsMethod = env->GetMethodID( envptr, ClassClass, "getMethods","()[Ljava/lang/reflect/Method;"); + jobject jobjArray = env->CallObjectMethod( envptr, clsObj, getMethodsMethod ); + + jclass MethodType = env->FindClass(envptr, "java/lang/reflect/Method"); + jclass TypeType = env->FindClass(envptr, "java/lang/reflect/Type"); + jmethodID tnamemid = env->GetMethodID( envptr, TypeType, "getTypeName", "()Ljava/lang/String;" ); + + jmethodID getFieldsMethod = env->GetMethodID( envptr, ClassClass, "getFields","()[Ljava/lang/reflect/Field;"); + jobject jobjArrayFields = env->CallObjectMethod( envptr, clsObj, getFieldsMethod ); + + jsize len = env->GetArrayLength(envptr, jobjArray); + jsize i; + printf( "Methods:\n" ); + + for (i = 0 ; i < len ; i++) { + jobject _strMethod = env->GetObjectArrayElement( envptr, jobjArray, i ) ; + jclass _methodClazz = env->GetObjectClass(envptr, _strMethod) ; + jmethodID mid = env->GetMethodID(envptr, _methodClazz , "getName", "()Ljava/lang/String;" ); + jmethodID getGenericParameterTypes = env->GetMethodID( envptr, _methodClazz, "getGenericParameterTypes","()[Ljava/lang/reflect/Type;"); + jmethodID getReturnTypeMethod = env->GetMethodID( envptr, _methodClazz, "getGenericReturnType","()Ljava/lang/reflect/Type;"); + jstring _name = (jstring)env->CallObjectMethod( envptr, _strMethod , mid ) ; + const char *str = env->GetStringUTFChars(envptr, _name, 0); + printf(" %s( ", str); + + jobject types = env->CallObjectMethod( envptr, _strMethod, getGenericParameterTypes ); + jsize mlen = env->GetArrayLength(envptr, types); + jsize mi; + for( mi = 0; mi < mlen; mi++ ) + { + jobject typeo = env->GetObjectArrayElement( envptr, types, mi ); + jstring _tn = (jstring)env->CallObjectMethod( envptr, typeo, tnamemid ); + const char * str = env->GetStringUTFChars(envptr, _tn, 0); + printf("%s%s ", str, (mi == mlen-1)?"":"," ); + env->ReleaseStringUTFChars(envptr, _tn, str); + env->DeleteLocalRef( envptr, _tn ); + env->DeleteLocalRef( envptr, typeo ); + } + + jobject rtype = env->CallObjectMethod( envptr, _strMethod, getReturnTypeMethod ); + jstring _tn = (jstring)env->CallObjectMethod( envptr, rtype, tnamemid ); + const char * strret = env->GetStringUTFChars(envptr, _tn, 0); + printf(") -> %s\n", strret); + env->ReleaseStringUTFChars(envptr, _tn, strret); + env->DeleteLocalRef( envptr, _tn ); + env->DeleteLocalRef( envptr, rtype ); + + + env->DeleteLocalRef( envptr, types ); + env->ReleaseStringUTFChars(envptr, _name, str); + env->DeleteLocalRef( envptr, _methodClazz ); + env->DeleteLocalRef( envptr, _strMethod ); + env->DeleteLocalRef( envptr, _name ); + } + len = env->GetArrayLength(envptr, jobjArrayFields); + printf( "Fields:\n" ); + for ( i = 0; i < len; i++) { + jobject _strField = env->GetObjectArrayElement( envptr, jobjArrayFields, i) ; + jclass _methodClazz = env->GetObjectClass(envptr, _strField ); + jmethodID mid = env->GetMethodID(envptr, _methodClazz , "getName", "()Ljava/lang/String;") ; + jstring _name = (jstring)env->CallObjectMethod( envptr, _strField , mid ) ; + const char *str = env->GetStringUTFChars(envptr, _name, 0); + + jmethodID getTypeMethod = env->GetMethodID( envptr, _methodClazz, "getGenericType","()Ljava/lang/reflect/Type;"); + jobject rtype = env->CallObjectMethod( envptr, _strField, getTypeMethod ); + jstring _tn = (jstring)env->CallObjectMethod( envptr, rtype, tnamemid ); + const char * strret = env->GetStringUTFChars(envptr, _tn, 0); + + printf(" %s -> %s\n", str, strret ); + env->ReleaseStringUTFChars(envptr, _name, str); + env->ReleaseStringUTFChars(envptr, _tn, strret); + env->DeleteLocalRef( envptr, _methodClazz ); + env->DeleteLocalRef( envptr, _strField ); + env->DeleteLocalRef( envptr, _name ); + env->DeleteLocalRef( envptr, _tn ); + } + env->DeleteLocalRef( envptr, TypeType ); + env->DeleteLocalRef( envptr, MethodType ); + env->DeleteLocalRef( envptr, clsObj ); + env->DeleteLocalRef( envptr, ClassClass ); + env->DeleteLocalRef( envptr, jobjArrayFields ); + env->DeleteLocalRef( envptr, jobjArray ); + + env->DeleteLocalRef( envptr, mpclass ); +} +#endif + +#endif + + +extern struct android_app * gapp; +static int OGLESStarted; +void (*HandleCustomEventCallback)(); +int android_width, android_height; +int override_android_screen_dimensons = 0; +int android_sdk_version; + +#include +#include +#include +#define ERRLOG(...) printf( __VA_ARGS__ ); +#else +#define ERRLOG(...) fprintf( stderr, __VA_ARGS__ ); +#endif + + + + +#include +#include +#include +#include +#include + +#ifdef ANDROID +#include +#else +#include +#endif + +#define EGL_ZBITS 16 +#define EGL_IMMEDIATE_SIZE 2048 + +#ifdef USE_EGL_X + #error This feature has never been completed or tested. + Display *XDisplay; + Window XWindow; +#else + typedef enum + { + FBDEV_PIXMAP_DEFAULT = 0, + FBDEV_PIXMAP_SUPPORTS_UMP = (1<<0), + FBDEV_PIXMAP_ALPHA_FORMAT_PRE = (1<<1), + FBDEV_PIXMAP_COLORSPACE_sRGB = (1<<2), + FBDEV_PIXMAP_EGL_MEMORY = (1<<3) /* EGL allocates/frees this memory */ + } fbdev_pixmap_flags; + + typedef struct fbdev_window + { + unsigned short width; + unsigned short height; + } fbdev_window; + + typedef struct fbdev_pixmap + { + unsigned int height; + unsigned int width; + unsigned int bytes_per_pixel; + unsigned char buffer_size; + unsigned char red_size; + unsigned char green_size; + unsigned char blue_size; + unsigned char alpha_size; + unsigned char luminance_size; + fbdev_pixmap_flags flags; + unsigned short *data; + unsigned int format; /* extra format information in case rgbal is not enough, especially for YUV formats */ + } fbdev_pixmap; + +#if defined( ANDROID ) +EGLNativeWindowType native_window; +#else +struct fbdev_window native_window; +#endif + +#endif + + +static EGLint const config_attribute_list[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_BUFFER_SIZE, 32, + EGL_STENCIL_SIZE, 0, + EGL_DEPTH_SIZE, EGL_ZBITS, + //EGL_SAMPLES, 1, +#ifdef ANDROID +#if ANDROIDVERSION >= 28 + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, +#else + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, +#endif + +#else + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PIXMAP_BIT, +#endif + EGL_NONE +}; + + +static EGLint window_attribute_list[] = { + EGL_NONE +}; + +static const EGLint context_attribute_list[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE +}; + +EGLDisplay egl_display; +EGLSurface egl_surface; +EGLContext egl_context; +EGLConfig egl_config; + +void CNFGSetVSync( int vson ) +{ + eglSwapInterval(egl_display, vson); +} + +static short iLastInternalW, iLastInternalH; + +void CNFGSwapBuffers() +{ + if ( egl_display == EGL_NO_DISPLAY ) return; + CNFGFlushRender(); + eglSwapBuffers(egl_display, egl_surface); +#ifdef ANDROID + if( !override_android_screen_dimensons ) + { + android_width = ANativeWindow_getWidth( native_window ); + android_height = ANativeWindow_getHeight( native_window ); + } + glViewport( 0, 0, android_width, android_height ); + if( iLastInternalW != android_width || iLastInternalH != android_height ) + CNFGInternalResize( iLastInternalW=android_width, iLastInternalH=android_height ); +#endif +} + +void CNFGGetDimensions( short * x, short * y ) +{ +#ifdef ANDROID + *x = android_width; + *y = android_height; +#else + *x = native_window.width; + *y = native_window.height; +#endif + if( *x != iLastInternalW || *y != iLastInternalH ) + CNFGInternalResize( iLastInternalW=*x, iLastInternalH=*y ); +} + +int CNFGSetup( const char * WindowName, int w, int h ) +{ + EGLint egl_major, egl_minor; + EGLint num_config; + + //This MUST be called before doing any initialization. + int events; + while( !OGLESStarted ) + { + struct android_poll_source* source; + if (ALooper_pollOnce( 0, 0, &events, (void**)&source) >= 0) + { + if (source != NULL) source->process(gapp, source); + } + } + + +#ifdef USE_EGL_X + XDisplay = XOpenDisplay(NULL); + if (!XDisplay) { + ERRLOG( "Error: failed to open X display.\n"); + return -1; + } + + Window XRoot = DefaultRootWindow(XDisplay); + + XSetWindowAttributes XWinAttr; + XWinAttr.event_mask = ExposureMask | PointerMotionMask; + + XWindow = XCreateWindow(XDisplay, XRoot, 0, 0, WIDTH, HEIGHT, 0, + CopyFromParent, InputOutput, + CopyFromParent, CWEventMask, &XWinAttr); + + Atom XWMDeleteMessage = + XInternAtom(XDisplay, "WM_DELETE_WINDOW", False); + + XMapWindow(XDisplay, XWindow); + XStoreName(XDisplay, XWindow, "Mali libs test"); + XSetWMProtocols(XDisplay, XWindow, &XWMDeleteMessage, 1); + + egl_display = eglGetDisplay((EGLNativeDisplayType) XDisplay); +#else + +#ifndef ANDROID + if( w >= 1 && h >= 1 ) + { + native_window.width = w; + native_window.height =h; + } +#endif + + egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); +#endif + if (egl_display == EGL_NO_DISPLAY) { + ERRLOG( "Error: No display found!\n"); + return -1; + } + + if (!eglInitialize(egl_display, &egl_major, &egl_minor)) { + ERRLOG( "Error: eglInitialise failed!\n"); + return -1; + } + + printf("EGL Version: \"%s\"\n", + eglQueryString(egl_display, EGL_VERSION)); + printf("EGL Vendor: \"%s\"\n", + eglQueryString(egl_display, EGL_VENDOR)); + printf("EGL Extensions: \"%s\"\n", + eglQueryString(egl_display, EGL_EXTENSIONS)); + + eglChooseConfig(egl_display, config_attribute_list, &egl_config, 1, + &num_config); + printf( "Config: %d\n", num_config ); + + printf( "Creating Context\n" ); + egl_context = eglCreateContext(egl_display, egl_config, EGL_NO_CONTEXT, +// NULL ); + context_attribute_list); + if (egl_context == EGL_NO_CONTEXT) { + ERRLOG( "Error: eglCreateContext failed: 0x%08X\n", + eglGetError()); + return -1; + } + printf( "Context Created %p\n", egl_context ); + +#ifdef USE_EGL_X + egl_surface = eglCreateWindowSurface(egl_display, egl_config, XWindow, + window_attribute_list); +#else + + if( native_window && !gapp->window ) + { + printf( "WARNING: App restarted without a window. Cannot progress.\n" ); + exit( 0 ); + } + + printf( "Getting Surface %p\n", native_window = gapp->window ); + + if( !native_window ) + { + printf( "FAULT: Cannot get window\n" ); + return -5; + } + + if( w <= 0 || h <= 0 ) + { + android_width = ANativeWindow_getWidth( native_window ); + android_height = ANativeWindow_getHeight( native_window ); + } + else + { + override_android_screen_dimensons = 1; + android_width = w; + android_height = h; + } + printf( "Width/Height: %dx%d\n", android_width, android_height ); + egl_surface = eglCreateWindowSurface(egl_display, egl_config, +#ifdef ANDROID + gapp->window, +#else + (EGLNativeWindowType)&native_window, +#endif + window_attribute_list); +#endif + printf( "Got Surface: %p\n", egl_surface ); + + if (egl_surface == EGL_NO_SURFACE) { + ERRLOG( "Error: eglCreateWindowSurface failed: " + "0x%08X\n", eglGetError()); + return -1; + } + +#ifndef ANDROID + int width, height; + if (!eglQuerySurface(egl_display, egl_surface, EGL_WIDTH, &width) || + !eglQuerySurface(egl_display, egl_surface, EGL_HEIGHT, &height)) { + ERRLOG( "Error: eglQuerySurface failed: 0x%08X\n", + eglGetError()); + return -1; + } + printf("Surface size: %dx%d\n", width, height); + + native_window.width = width; + native_window.height = height; +#endif + + if (!eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)) { + ERRLOG( "Error: eglMakeCurrent() failed: 0x%08X\n", + eglGetError()); + return -1; + } + + printf("GL Vendor: \"%s\"\n", glGetString(GL_VENDOR)); + printf("GL Renderer: \"%s\"\n", glGetString(GL_RENDERER)); + printf("GL Version: \"%s\"\n", glGetString(GL_VERSION)); + printf("GL Extensions: \"%s\"\n", glGetString(GL_EXTENSIONS)); + + CNFGSetupBatchInternal(); + + { + short dummyx, dummyy; + CNFGGetDimensions( &dummyx, &dummyy ); + } + + return 0; +} + +void CNFGSetupFullscreen( const char * WindowName, int screen_number ) +{ + //Removes decoration, must be called before setup. + AndroidMakeFullscreen(); + + CNFGSetup( WindowName, -1, -1 ); +} + +int debuga, debugb, debugc; + +int32_t handle_input(struct android_app* app, AInputEvent* event) +{ +#ifdef ANDROID + //Potentially do other things here. + + if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) + { + int action = AMotionEvent_getAction( event ); + int whichsource = action >> 8; + action &= AMOTION_EVENT_ACTION_MASK; + size_t pointerCount = AMotionEvent_getPointerCount(event); + + for (size_t i = 0; i < pointerCount; ++i) + { + int x, y, index; + x = AMotionEvent_getX(event, i); + y = AMotionEvent_getY(event, i); + index = AMotionEvent_getPointerId( event, i ); + + if( action == AMOTION_EVENT_ACTION_POINTER_DOWN || action == AMOTION_EVENT_ACTION_DOWN ) + { + int id = index; + if( action == AMOTION_EVENT_ACTION_POINTER_DOWN && id != whichsource ) continue; + HandleButton( x, y, id, 1 ); + ANativeActivity_showSoftInput( gapp->activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED ); + } + else if( action == AMOTION_EVENT_ACTION_POINTER_UP || action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL ) + { + int id = index; + if( action == AMOTION_EVENT_ACTION_POINTER_UP && id != whichsource ) continue; + HandleButton( x, y, id, 0 ); + } + else if( action == AMOTION_EVENT_ACTION_MOVE ) + { + HandleMotion( x, y, index ); + } + } + return 1; + } + else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) + { + int code = AKeyEvent_getKeyCode(event); +#ifdef ANDROID_USE_SCANCODES + HandleKey( code, AKeyEvent_getAction(event) ); +#else + int unicode = AndroidGetUnicodeChar( code, AMotionEvent_getMetaState( event ) ); + if( unicode ) + HandleKey( unicode, AKeyEvent_getAction(event) ); + else + { + HandleKey( code, !AKeyEvent_getAction(event) ); + return (code == 4)?1:0; //don't override functionality. + } +#endif + + return 1; + } +#endif + return 0; +} + +int CNFGHandleInput() +{ + +#ifdef ANDROID + int events; + struct android_poll_source* source; + while( ALooper_pollOnce( 0, 0, &events, (void**)&source) >= 0 ) + { + if (source != NULL) + { + source->process(gapp, source); + } + } +#endif + +#ifdef USE_EGL_X + while (1) { + XEvent event; + + XNextEvent(XDisplay, &event); + + if ((event.type == MotionNotify) || + (event.type == Expose)) + Redraw(width, height); + else if (event.type == ClientMessage) { + if (event.xclient.data.l[0] == XWMDeleteMessage) + break; + } + } + XSetWMProtocols(XDisplay, XWindow, &XWMDeleteMessage, 0); +#endif + return 1; +} + + + +#ifdef ANDROID + +void (*HandleWindowTermination)(); + +void handle_cmd(struct android_app* app, int32_t cmd) +{ + switch (cmd) + { + case APP_CMD_DESTROY: + //This gets called initially after back. + HandleDestroy(); + ANativeActivity_finish( gapp->activity ); + break; + case APP_CMD_INIT_WINDOW: + //When returning from a back button suspension, this isn't called. + if( !OGLESStarted ) + { + OGLESStarted = 1; + printf( "Got start event\n" ); + } + else + { + CNFGSetup( "", -1, -1 ); + HandleResume(); + } + break; + case APP_CMD_TERM_WINDOW: + //This gets called initially when you click "back" + //This also gets called when you are brought into standby. + //Not sure why - callbacks here seem to break stuff. + if( egl_display != EGL_NO_DISPLAY ) { + eglMakeCurrent( egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT ); + if( egl_context != EGL_NO_CONTEXT ) { + eglDestroyContext( egl_display, egl_context ); + } + if( egl_surface != EGL_NO_SURFACE ) { + eglDestroySurface( egl_display, egl_surface ); + } + eglTerminate( egl_display ); + } + egl_context = EGL_NO_CONTEXT; + egl_surface = EGL_NO_SURFACE; + egl_display = EGL_NO_DISPLAY; +#ifdef ANDROID_WANT_WINDOW_TERMINATION + if( HandleWindowTermination ) HandleWindowTermination(); +#endif + break; + + case APP_CMD_PAUSE: + HandleSuspend(); + break; + + case APP_CMD_RESUME: + HandleResume(); + break; + case APP_CMD_CUSTOM_EVENT: + if( HandleCustomEventCallback ) HandleCustomEventCallback(); + break; + default: + printf( "event not handled: %d\n", cmd); + } +} + +int __system_property_get(const char* name, char* value); + +void android_main(struct android_app* app) +{ + int main( int argc, char ** argv ); + char mainptr[5] = { 'm', 'a', 'i', 'n', 0 }; + char * argv[] = { mainptr, 0 }; + + { + char sdk_ver_str[92]; + int len = __system_property_get("ro.build.version.sdk", sdk_ver_str); + if( len <= 0 ) + android_sdk_version = 0; + else + android_sdk_version = atoi(sdk_ver_str); + } + + gapp = app; + app->onAppCmd = handle_cmd; + app->onInputEvent = handle_input; + printf( "Starting with Android SDK Version: %d\n", android_sdk_version ); + main( 1, argv ); + printf( "Main Complete\n" ); +} + +#ifdef __cplusplus +#define SETUP_FOR_JAVA_CALL \ + JNIEnv * env = 0; \ + JNIEnv ** envptr = &env; \ + JavaVM * jniiptr = gapp->activity->vm; \ + jniiptr->AttachCurrentThread( (JNIEnv**)&env, 0 ); \ + env = (*envptr); +#define ENVCALL +#define JAVA_CALL_DETACH jniiptr->DetachCurrentThread(); +#else +#define SETUP_FOR_JAVA_CALL \ + const struct JNINativeInterface * env = (struct JNINativeInterface*)gapp->activity->env; \ + const struct JNINativeInterface ** envptr = &env; \ + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; \ + const struct JNIInvokeInterface * jnii = *jniiptr; \ + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); \ + env = (*envptr); +#define ENVCALL envptr, +#define JAVA_CALL_DETACH jnii->DetachCurrentThread( jniiptr ); +#endif + +void AndroidMakeFullscreen() +{ + //Partially based on https://stackoverflow.com/questions/47507714/how-do-i-enable-full-screen-immersive-mode-for-a-native-activity-ndk-app + SETUP_FOR_JAVA_CALL + + //Get android.app.NativeActivity, then get getWindow method handle, returns view.Window type + jclass activityClass = env->FindClass( ENVCALL "android/app/NativeActivity"); + jmethodID getWindow = env->GetMethodID( ENVCALL activityClass, "getWindow", "()Landroid/view/Window;"); + jobject window = env->CallObjectMethod( ENVCALL gapp->activity->clazz, getWindow); + jclass windowClass = env->FindClass( ENVCALL "android/view/Window"); + jmethodID getDecorView = env->GetMethodID( ENVCALL windowClass, "getDecorView", "()Landroid/view/View;"); + jobject decorView = env->CallObjectMethod( ENVCALL window, getDecorView); + + /* + jclass ClassActivity = env->FindClass( ENVCALL "android/app/Activity" ); + const int flag_WindowProp = env->GetStaticIntField( ENVCALL windowClass, env->GetStaticFieldID( ENVCALL windowClass, "FEATURE_NO_TITLE", "I") ); + jmethodID requestWindowFeature = env->GetMethodID( ENVCALL ClassActivity, "requestWindowFeature", "(I)Z" ); + jobject lNativeActivity = gapp->activity->clazz; + env->CallBooleanMethod( ENVCALL lNativeActivity, requestWindowFeature, flag_WindowProp ); + */ + + //Get the flag values associated with systemuivisibility + jclass viewClass = env->FindClass( ENVCALL "android/view/View"); + const int flagLayoutHideNavigation = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION", "I")); + const int flagLayoutFullscreen = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN", "I")); + const int flagLowProfile = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LOW_PROFILE", "I")); + const int flagHideNavigation = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_HIDE_NAVIGATION", "I")); + const int flagFullscreen = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_FULLSCREEN", "I")); + const int flagImmersiveSticky = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_IMMERSIVE_STICKY", "I")); + const int flagLayoutStable = env->GetStaticIntField( ENVCALL viewClass, env->GetStaticFieldID( ENVCALL viewClass, "SYSTEM_UI_FLAG_LAYOUT_STABLE", "I")); + jmethodID setSystemUiVisibility = env->GetMethodID( ENVCALL viewClass, "setSystemUiVisibility", "(I)V"); + //Call the decorView.setSystemUiVisibility(FLAGS) + env->CallVoidMethod( ENVCALL decorView, setSystemUiVisibility, + (flagLayoutHideNavigation | flagLayoutFullscreen | flagLowProfile | flagHideNavigation | flagFullscreen | flagImmersiveSticky | flagLayoutStable)); + + //now set some more flags associated with layoutmanager -- note the $ in the class path + //search for api-versions.xml + //https://android.googlesource.com/platform/development/+/refs/tags/android-9.0.0_r48/sdk/api-versions.xml + jclass layoutManagerClass = env->FindClass( ENVCALL "android/view/WindowManager$LayoutParams"); + const int flag_WinMan_Fullscreen = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_FULLSCREEN", "I") )); + const int flag_WinMan_KeepScreenOn = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_KEEP_SCREEN_ON", "I") )); + const int flag_WinMan_hw_acc = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_HARDWARE_ACCELERATED", "I") )); + const int flag_WinMan_NoLimits = env->GetStaticIntField( ENVCALL layoutManagerClass, (env->GetStaticFieldID( ENVCALL layoutManagerClass, "FLAG_LAYOUT_NO_LIMITS", "I") )); + // const int flag_WinMan_flag_not_fullscreen = env->GetStaticIntField(layoutManagerClass, (env->GetStaticFieldID(layoutManagerClass, "FLAG_FORCE_NOT_FULLSCREEN", "I") )); + //call window.addFlags(FLAGS) + env->CallVoidMethod( ENVCALL window, (env->GetMethodID (ENVCALL windowClass, "addFlags" , "(I)V")), (flag_WinMan_Fullscreen | flag_WinMan_KeepScreenOn | flag_WinMan_hw_acc | flag_WinMan_NoLimits)); + + + +/* + // Seems to have no impact, and doesn't work with older Android versions. + jmethodID setDecorFitsSystemWindows = env->GetMethodID( ENVCALL windowClass, "setDecorFitsSystemWindows", "(Z)V"); + env->CallVoidMethod( ENVCALL window, setDecorFitsSystemWindows, JNI_FALSE ); + + // "Immersive Mode" (Since Android 11+) + jmethodID getWindowInsetsController = env->GetMethodID( ENVCALL viewClass, "getWindowInsetsController", "()Landroid/view/WindowInsetsController;" ); + if( getWindowInsetsController ) + { + //windowInsetsController.hide(Type.systemBars()) + jobject windowInsetsController = env->CallObjectMethod( ENVCALL decorView, getWindowInsetsController ); + jclass windowInsetsControllerClass = env->FindClass( ENVCALL "android/view/WindowInsetsController" ); + + jclass windowInsetsTypeClass = env->FindClass( ENVCALL "android/view/WindowInsets/Type" ); + jmethodID typeSystemBars = env->GetStaticMethodID( ENVCALL windowInsetsTypeClass, "systemBars", "()I" ); + int systemBarsType = env->CallStaticIntMethod( ENVCALL windowInsetsTypeClass, typeSystemBars ); + + jmethodID hide = env->GetMethodID( ENVCALL windowInsetsControllerClass, "hide", "(I)V" ); + env->CallVoidMethod( ENVCALL windowInsetsController, hide, systemBarsType ); + } +*/ + + JAVA_CALL_DETACH +} + +const char* AndroidGetExternalFilesDir() +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + jclass activityClass = env->FindClass( envptr, "android/app/NativeActivity"); + jobject lNativeActivity = gapp->activity->clazz; + + jmethodID mid_getExtStorage = env->GetMethodID(envptr,activityClass,"getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;"); + jobject obj_File = env->CallObjectMethod(envptr,lNativeActivity, mid_getExtStorage, NULL); + jclass cls_File = env->FindClass(envptr,"java/io/File"); + jmethodID mid_getPath = env->GetMethodID(envptr,cls_File, "getPath", "()Ljava/lang/String;"); + jstring obj_Path = (jstring) env->CallObjectMethod(envptr,obj_File, mid_getPath); + const char* path = env->GetStringUTFChars(envptr,obj_Path, NULL); + //printf("EXTERNAL PATH = %s\n", path); + env->ReleaseStringUTFChars(envptr,obj_Path, path); + jnii->DetachCurrentThread( jniiptr ); + return path; +} + +void AndroidDisplayKeyboard(int pShow) +{ + //Based on https://stackoverflow.com/questions/5864790/how-to-show-the-soft-keyboard-on-native-activity + jint lFlags = 0; + SETUP_FOR_JAVA_CALL + + jclass activityClass = env->FindClass( ENVCALL "android/app/NativeActivity"); + + // Retrieves NativeActivity. + jobject lNativeActivity = gapp->activity->clazz; + + + // Retrieves Context.INPUT_METHOD_SERVICE. + jclass ClassContext = env->FindClass( ENVCALL "android/content/Context"); + jfieldID FieldINPUT_METHOD_SERVICE = env->GetStaticFieldID( ENVCALL ClassContext, "INPUT_METHOD_SERVICE", "Ljava/lang/String;" ); + jobject INPUT_METHOD_SERVICE = env->GetStaticObjectField( ENVCALL ClassContext, FieldINPUT_METHOD_SERVICE ); + + // Runs getSystemService(Context.INPUT_METHOD_SERVICE). + jclass ClassInputMethodManager = env->FindClass( ENVCALL "android/view/inputmethod/InputMethodManager" ); + jmethodID MethodGetSystemService = env->GetMethodID( ENVCALL activityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); + jobject lInputMethodManager = env->CallObjectMethod( ENVCALL lNativeActivity, MethodGetSystemService, INPUT_METHOD_SERVICE); + + // Runs getWindow().getDecorView(). + jmethodID MethodGetWindow = env->GetMethodID( ENVCALL activityClass, "getWindow", "()Landroid/view/Window;"); + jobject lWindow = env->CallObjectMethod( ENVCALL lNativeActivity, MethodGetWindow); + jclass ClassWindow = env->FindClass( ENVCALL "android/view/Window"); + jmethodID MethodGetDecorView = env->GetMethodID( ENVCALL ClassWindow, "getDecorView", "()Landroid/view/View;"); + jobject lDecorView = env->CallObjectMethod( ENVCALL lWindow, MethodGetDecorView); + + if (pShow) { + // Runs lInputMethodManager.showSoftInput(...). + jmethodID MethodShowSoftInput = env->GetMethodID( ENVCALL ClassInputMethodManager, "showSoftInput", "(Landroid/view/View;I)Z"); + /*jboolean lResult = */env->CallBooleanMethod( ENVCALL lInputMethodManager, MethodShowSoftInput, lDecorView, lFlags); + } else { + // Runs lWindow.getViewToken() + jclass ClassView = env->FindClass( ENVCALL "android/view/View"); + jmethodID MethodGetWindowToken = env->GetMethodID( ENVCALL ClassView, "getWindowToken", "()Landroid/os/IBinder;"); + jobject lBinder = env->CallObjectMethod( ENVCALL lDecorView, MethodGetWindowToken); + + // lInputMethodManager.hideSoftInput(...). + jmethodID MethodHideSoftInput = env->GetMethodID( ENVCALL ClassInputMethodManager, "hideSoftInputFromWindow", "(Landroid/os/IBinder;I)Z"); + /*jboolean lRes = */env->CallBooleanMethod( ENVCALL lInputMethodManager, MethodHideSoftInput, lBinder, lFlags); + } + + JAVA_CALL_DETACH +} + +int AndroidGetUnicodeChar( int keyCode, int metaState ) +{ + //https://stackoverflow.com/questions/21124051/receive-complete-android-unicode-input-in-c-c/43871301 + + int eventType = AKEY_EVENT_ACTION_DOWN; + + SETUP_FOR_JAVA_CALL + + //jclass activityClass = env->FindClass( envptr, "android/app/NativeActivity"); + // Retrieves NativeActivity. + //jobject lNativeActivity = gapp->activity->clazz; + + jclass class_key_event = env->FindClass( ENVCALL "android/view/KeyEvent"); + int unicodeKey; + + jmethodID method_get_unicode_char = env->GetMethodID( ENVCALL class_key_event, "getUnicodeChar", "(I)I"); + jmethodID eventConstructor = env->GetMethodID( ENVCALL class_key_event, "", "(II)V"); + jobject eventObj = env->NewObject( ENVCALL class_key_event, eventConstructor, eventType, keyCode); + + unicodeKey = env->CallIntMethod( ENVCALL eventObj, method_get_unicode_char, metaState ); + + // Finished with the JVM. + JAVA_CALL_DETACH + + //printf("Unicode key is: %d", unicodeKey); + return unicodeKey; +} + + +//Based on: https://stackoverflow.com/questions/41820039/jstringjni-to-stdstringc-with-utf8-characters +#ifdef __cplusplus +jstring android_permission_name(JNIEnv ** envptr, const char* perm_name) +#else +jstring android_permission_name(const struct JNINativeInterface ** envptr, const char* perm_name) +#endif +{ + // nested class permission in class android.Manifest, + // hence android 'slash' Manifest 'dollar' permission +#ifdef __cplusplus + JNIEnv * env = *envptr; +#else + const struct JNINativeInterface * env = *envptr; +#endif + jclass ClassManifestpermission = env->FindClass( ENVCALL "android/Manifest$permission"); + jfieldID lid_PERM = env->GetStaticFieldID( ENVCALL ClassManifestpermission, perm_name, "Ljava/lang/String;" ); + jstring ls_PERM = (jstring)(env->GetStaticObjectField( ENVCALL ClassManifestpermission, lid_PERM )); + return ls_PERM; +} + +/** + * \brief Tests whether a permission is granted. + * \param[in] app a pointer to the android app. + * \param[in] perm_name the name of the permission, e.g., + * "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE". + * \retval true if the permission is granted. + * \retval false otherwise. + * \note Requires Android API level 23 (Marshmallow, May 2015) + */ +int AndroidHasPermissions( const char* perm_name) +{ + struct android_app* app = gapp; + SETUP_FOR_JAVA_CALL + + if( android_sdk_version < 23 ) + { + printf( "Android SDK version %d does not support AndroidHasPermissions\n", android_sdk_version ); + return 1; + } + + int result = 0; + jstring ls_PERM = android_permission_name(envptr, perm_name); + + jint PERMISSION_GRANTED = (-1); + + { + jclass ClassPackageManager = env->FindClass( ENVCALL "android/content/pm/PackageManager" ); + jfieldID lid_PERMISSION_GRANTED = env->GetStaticFieldID( ENVCALL ClassPackageManager, "PERMISSION_GRANTED", "I" ); + PERMISSION_GRANTED = env->GetStaticIntField( ENVCALL ClassPackageManager, lid_PERMISSION_GRANTED ); + } + { + jobject activity = app->activity->clazz; + jclass ClassContext = env->FindClass( ENVCALL "android/content/Context" ); + jmethodID MethodcheckSelfPermission = env->GetMethodID( ENVCALL ClassContext, "checkSelfPermission", "(Ljava/lang/String;)I" ); + jint int_result = env->CallIntMethod( ENVCALL activity, MethodcheckSelfPermission, ls_PERM ); + result = (int_result == PERMISSION_GRANTED); + } + + JAVA_CALL_DETACH + + return result; +} + +/** + * \brief Query file permissions. + * \details This opens the system dialog that lets the user + * grant (or deny) the permission. + * \param[in] app a pointer to the android app. + * \note Requires Android API level 23 (Marshmallow, May 2015) + */ +void AndroidRequestAppPermissions(const char * perm) +{ + if( android_sdk_version < 23 ) + { + printf( "Android SDK version %d does not support AndroidRequestAppPermissions\n",android_sdk_version ); + return; + } + + struct android_app* app = gapp; + SETUP_FOR_JAVA_CALL + + jobject activity = app->activity->clazz; + + jobjectArray perm_array = env->NewObjectArray( ENVCALL 1, env->FindClass( ENVCALL "java/lang/String"), env->NewStringUTF( ENVCALL "" ) ); + env->SetObjectArrayElement( ENVCALL perm_array, 0, android_permission_name(envptr, perm ) ); + jclass ClassActivity = env->FindClass( ENVCALL "android/app/Activity" ); + + jmethodID MethodrequestPermissions = env->GetMethodID( ENVCALL ClassActivity, "requestPermissions", "([Ljava/lang/String;I)V" ); + + // Last arg (0) is just for the callback (that I do not use) + env->CallVoidMethod( ENVCALL activity, MethodrequestPermissions, perm_array, 0 ); + + JAVA_CALL_DETACH +} + +/* Example: + int hasperm = android_has_permission( "RECORD_AUDIO" ); + if( !hasperm ) + { + android_request_app_permissions( "RECORD_AUDIO" ); + } +*/ + +void AndroidSendToBack( int param ) +{ + struct android_app* app = gapp; + SETUP_FOR_JAVA_CALL + jobject activity = app->activity->clazz; + + //_glfmCallJavaMethodWithArgs(jni, gapp->activity->clazz, "moveTaskToBack", "(Z)Z", Boolean, false); + jclass ClassActivity = env->FindClass( ENVCALL "android/app/Activity" ); + jmethodID MethodmoveTaskToBack = env->GetMethodID( ENVCALL ClassActivity, "moveTaskToBack", "(Z)Z" ); + env->CallBooleanMethod( ENVCALL activity, MethodmoveTaskToBack, param ); + JAVA_CALL_DETACH +} + +#endif + + +#else +//Copyright (c) 2011, 2017, 2018 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose. +//portions from +//http://www.xmission.com/~georgeps/documentation/tutorials/Xlib_Beginner.html + +//#define HAS_XINERAMA +//#define CNFG_HAS_XSHAPE +//#define FULL_SCREEN_STEAL_FOCUS + +#ifndef _CNFGXDRIVER_C +#define _CNFGXDRIVER_C + + +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef HAS_XINERAMA + #include + #include +#endif +#ifdef CNFG_HAS_XSHAPE + #include + static XGCValues xsval; + static Pixmap xspixmap; + static GC xsgc; + + static int taint_shape; + static int prepare_xshape; + static int was_transp; + +#endif + +#ifdef CNFG_BATCH +void CNFGSetupBatchInternal(); +#endif + +XWindowAttributes CNFGWinAtt; +XClassHint *CNFGClassHint; +char * wm_res_name = 0; +char * wm_res_class = 0; +Display *CNFGDisplay; +Atom CFNGWMDeleteWindow; +Window CNFGWindow; +int CNFGWindowInvisible; +Pixmap CNFGPixmap; +GC CNFGGC; +GC CNFGWindowGC; +int CNFGDepth; +int CNFGScreen; +Visual * CNFGVisual; +VisualID CNFGVisualID; + +#ifdef CNFGOGL +#include +#include + +GLXContext CNFGCtx; +void * CNFGGetExtension( const char * extname ) { return (void*)glXGetProcAddressARB((const GLubyte *) extname); } +GLXFBConfig CNFGGLXFBConfig; +GLXFBConfig* CNFGGLXFBConfigs; + + +void CNFGGLXSetup( ) +{ + int attribs[] = { + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DOUBLEBUFFER, True, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_DEPTH_SIZE, 1, + None }; + int elements = 0; + GLXFBConfig * cfgs = glXChooseFBConfig( CNFGDisplay, CNFGScreen, attribs, &elements ); + if( elements == 0 ) + { + fprintf( stderr, "Error: could not get valid GLXFBConfig visual.\n" ); + exit( -1 ); + } + CNFGGLXFBConfigs = cfgs; + CNFGGLXFBConfig = cfgs[0]; + XVisualInfo * vis = glXGetVisualFromFBConfig( CNFGDisplay, CNFGGLXFBConfig ); + CNFGVisual = vis->visual; + CNFGVisualID = vis->visualid; + CNFGDepth = vis->depth; + CNFGCtx = glXCreateContext( CNFGDisplay, vis, NULL, True ); + XFree( vis ); +} + +#endif + +int CNFGX11ForceNoDecoration; +XImage *xi; + +XIM CNFGXIM = NULL; +XIC CNFGXIC = NULL; + +int g_x_global_key_state; +int g_x_global_shift_key; + +void CNFGSetWindowIconData( int w, int h, uint32_t * data ) +{ + static Atom net_wm_icon; + static Atom cardinal; + + if( !net_wm_icon ) net_wm_icon = XInternAtom( CNFGDisplay, "_NET_WM_ICON", False ); + if( !cardinal ) cardinal = XInternAtom( CNFGDisplay, "CARDINAL", False ); + + unsigned long outdata[w*h]; + int i; + for( i = 0; i < w*h; i++ ) + { + outdata[i+2] = data[i]; + } + outdata[0] = w; + outdata[1] = h; + XChangeProperty(CNFGDisplay, CNFGWindow, net_wm_icon, cardinal, + 32, PropModeReplace, (const unsigned char*)outdata, 2 + w*h); +} + + +#ifdef CNFG_HAS_XSHAPE +void CNFGPrepareForTransparency() { prepare_xshape = 1; } +void CNFGDrawToTransparencyMode( int transp ) +{ + static Pixmap BackupCNFGPixmap; + static GC BackupCNFGGC; + if( was_transp && ! transp ) + { + CNFGGC = BackupCNFGGC; + CNFGPixmap = BackupCNFGPixmap; + } + if( !was_transp && transp ) + { + BackupCNFGPixmap = CNFGPixmap; + BackupCNFGGC = CNFGGC; + taint_shape = 1; + CNFGGC = xsgc; + CNFGPixmap = xspixmap; + } + was_transp = transp; +} +void CNFGClearTransparencyLevel() +{ + taint_shape = 1; + XSetForeground(CNFGDisplay, xsgc, 0); + XFillRectangle(CNFGDisplay, xspixmap, xsgc, 0, 0, CNFGWinAtt.width, CNFGWinAtt.height); + XSetForeground(CNFGDisplay, xsgc, 1); +} +#endif + +void CNFGSetMousePosition( int x, int y ) { + XWarpPointer( CNFGDisplay, None, CNFGWindow, 0, 0, 0, 0, x, y ); +} + +void CNFGConfineMouse( int confined ) { + if (confined) { + XGrabPointer( CNFGDisplay, CNFGWindow, True, 0, GrabModeAsync, GrabModeAsync, CNFGWindow, None, CurrentTime ); + } else { + XUngrabPointer( CNFGDisplay, CurrentTime ); + } +} + +Cursor CNFGCursors[CNFG_CURSOR_LAST] = { None }; + +void CNFGSetCursor( CNFGCursorShape shape ) { + if (shape == CNFG_CURSOR_ARROW) XUndefineCursor( CNFGDisplay, CNFGWindow ); + else XDefineCursor( CNFGDisplay, CNFGWindow, CNFGCursors[shape] ); +} + +int FullScreen = 0; + +void CNFGGetDimensions( short * x, short * y ) +{ + static int lastx; + static int lasty; + + *x = CNFGWinAtt.width; + *y = CNFGWinAtt.height; + + if( lastx != *x || lasty != *y ) + { + lastx = *x; + lasty = *y; +#ifndef CNFGCONTEXTONLY + CNFGInternalResize( lastx, lasty ); +#endif + } +} + +void CNFGChangeWindowTitle( const char * WindowName ) +{ + XSetStandardProperties( CNFGDisplay, CNFGWindow, WindowName, 0, 0, 0, 0, 0 ); +} + +static void InternalLinkScreenAndGo( const char * WindowName ) +{ + XFlush(CNFGDisplay); + XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt ); + + if( !wm_res_name ) wm_res_name = strdup( "rawdraw" ); + if( !wm_res_class ) wm_res_class = strdup( "rawdraw" ); + + XGetClassHint( CNFGDisplay, CNFGWindow, CNFGClassHint ); + if (!CNFGClassHint) { + CNFGClassHint = XAllocClassHint(); + if (CNFGClassHint) { + CNFGClassHint->res_name = wm_res_name; + CNFGClassHint->res_class = wm_res_class; + XSetClassHint( CNFGDisplay, CNFGWindow, CNFGClassHint ); + } else { + fprintf( stderr, "Failed to allocate XClassHint!\n" ); + } + } else { + fprintf( stderr, "Pre-existing XClassHint\n" ); + } + + XSelectInput (CNFGDisplay, CNFGWindow, KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | PointerMotionMask | FocusChangeMask ); + + + CNFGWindowGC = XCreateGC(CNFGDisplay, CNFGWindow, 0, 0); + + + if( CNFGX11ForceNoDecoration ) + { + Atom window_type = XInternAtom(CNFGDisplay, "_NET_WM_WINDOW_TYPE", False); + long value = XInternAtom(CNFGDisplay, "_NET_WM_WINDOW_TYPE_SPLASH", False); + XChangeProperty(CNFGDisplay, CNFGWindow, window_type, + XA_ATOM, 32, PropModeReplace, (unsigned char *) &value,1 ); + } + + CNFGPixmap = XCreatePixmap( CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, CNFGWinAtt.depth ); + CNFGGC = XCreateGC(CNFGDisplay, CNFGPixmap, 0, 0); + XSetLineAttributes(CNFGDisplay, CNFGGC, 1, LineSolid, CapRound, JoinRound); + CNFGChangeWindowTitle( WindowName ); + if( !CNFGWindowInvisible ) + XMapWindow(CNFGDisplay, CNFGWindow); + + CNFGXIM = XOpenIM(CNFGDisplay, NULL, wm_res_name, wm_res_class); + CNFGXIC = XCreateIC(CNFGXIM, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, NULL); + +#ifdef CNFG_HAS_XSHAPE + if( prepare_xshape ) + { + xsval.foreground = 1; + xsval.line_width = 1; + xsval.line_style = LineSolid; + xspixmap = XCreatePixmap(CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, 1); + xsgc = XCreateGC(CNFGDisplay, xspixmap, 0, &xsval); + XSetLineAttributes(CNFGDisplay, xsgc, 1, LineSolid, CapRound, JoinRound); + } +#endif +} + +void CNFGSetupFullscreen( const char * WindowName, int screen_no ) +{ +#ifdef HAS_XINERAMA + XineramaScreenInfo *screeninfo = NULL; + int screens; + int event_basep, error_basep, a, b; + CNFGDisplay = XOpenDisplay(NULL); + CNFGScreen = XDefaultScreen(CNFGDisplay); + int xpos, ypos; + + if (!XShapeQueryExtension(CNFGDisplay, &event_basep, &error_basep)) { + fprintf( stderr, "X-Server does not support shape extension\n" ); + exit( 1 ); + } + + CNFGVisual = DefaultVisual(CNFGDisplay, CNFGScreen); + CNFGVisualID = 0; + CNFGWinAtt.depth = DefaultDepth(CNFGDisplay, CNFGScreen); + +#ifdef CNFGOGL + CNFGGLXSetup(); +#endif + + if (XineramaQueryExtension(CNFGDisplay, &a, &b ) && + (screeninfo = XineramaQueryScreens(CNFGDisplay, &screens)) && + XineramaIsActive(CNFGDisplay) && screen_no >= 0 && + screen_no < screens ) { + + CNFGWinAtt.width = screeninfo[screen_no].width; + CNFGWinAtt.height = screeninfo[screen_no].height; + xpos = screeninfo[screen_no].x_org; + ypos = screeninfo[screen_no].y_org; + } else + { + CNFGWinAtt.width = XDisplayWidth(CNFGDisplay, CNFGScreen); + CNFGWinAtt.height = XDisplayHeight(CNFGDisplay, CNFGScreen); + xpos = 0; + ypos = 0; + } + if (screeninfo) + XFree(screeninfo); + + + XSetWindowAttributes setwinattr; + setwinattr.override_redirect = 1; + setwinattr.save_under = 1; +#ifdef CNFG_HAS_XSHAPE + + if (prepare_xshape && !XShapeQueryExtension(CNFGDisplay, &event_basep, &error_basep)) + { + fprintf( stderr, "X-Server does not support shape extension" ); + exit( 1 ); + } + + setwinattr.event_mask = 0; +#else + //This code is probably made irrelevant by the XSetEventMask in InternalLinkScreenAndGo, if this code is not found needed by 2019-12-31, please remove. + //setwinattr.event_mask = StructureNotifyMask | SubstructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | ButtonPressMask | PointerMotionMask | ButtonMotionMask | EnterWindowMask | LeaveWindowMask |KeyPressMask |KeyReleaseMask | SubstructureNotifyMask | FocusChangeMask; +#endif + setwinattr.border_pixel = 0; + setwinattr.colormap = XCreateColormap( CNFGDisplay, RootWindow(CNFGDisplay, 0), CNFGVisual, AllocNone); + + CNFGWindow = XCreateWindow(CNFGDisplay, XRootWindow(CNFGDisplay, CNFGScreen), + xpos, ypos, CNFGWinAtt.width, CNFGWinAtt.height, + 0, CNFGWinAtt.depth, InputOutput, CNFGVisual, + CWBorderPixel/* | CWEventMask */ | CWOverrideRedirect | CWSaveUnder | CWColormap, + &setwinattr); + + FullScreen = 1; + InternalLinkScreenAndGo( WindowName ); +#ifdef CNFGOGL + glXMakeCurrent( CNFGDisplay, CNFGWindow, CNFGCtx ); +#endif +#ifdef CNFG_BATCH +#ifndef CNFGCONTEXTONLY + CNFGSetupBatchInternal(); +#endif +#endif + +#else + CNFGSetup( WindowName, 640, 480 ); +#endif +} + + +void CNFGTearDown() +{ + HandleDestroy(); + if( xi ) free( xi ); + if ( CNFGClassHint ) XFree( CNFGClassHint ); + if ( CNFGGC ) XFreeGC( CNFGDisplay, CNFGGC ); + if ( CNFGWindowGC ) XFreeGC( CNFGDisplay, CNFGWindowGC ); + if ( CNFGDisplay ) XCloseDisplay( CNFGDisplay ); + if ( CNFGXIC ) XDestroyIC( CNFGXIC ); + if ( CNFGXIM ) XCloseIM( CNFGXIM ); + +#ifdef CNFGOGL + if ( CNFGGLXFBConfigs ) XFree( CNFGGLXFBConfigs ); + CNFGGLXFBConfigs = NULL; +#endif + CNFGDisplay = NULL; + CNFGWindowGC = CNFGGC = NULL; + CNFGClassHint = NULL; + CFNGWMDeleteWindow = None; +} + +int CNFGSetupWMClass( const char * WindowName, int w, int h , char * wm_res_name_ , char * wm_res_class_ ) +{ + wm_res_name = wm_res_name_; + wm_res_class = wm_res_class_; + return CNFGSetup( WindowName, w, h); +} + +int CNFGSetup( const char * WindowName, int w, int h ) +{ + CNFGDisplay = XOpenDisplay(NULL); + if ( !CNFGDisplay ) { + fprintf( stderr, "Could not get an X Display.\n%s", + "Are you in text mode or using SSH without X11-Forwarding?\n" ); + exit( 1 ); + } + atexit( CNFGTearDown ); + + CNFGScreen = DefaultScreen(CNFGDisplay); + CNFGDepth = DefaultDepth(CNFGDisplay, CNFGScreen); + CNFGVisual = DefaultVisual(CNFGDisplay, CNFGScreen); + CNFGVisualID = 0; + Window wnd = DefaultRootWindow( CNFGDisplay ); + +#ifdef CNFGOGL + CNFGGLXSetup( ); +#endif + + XSetWindowAttributes attr; + attr.background_pixel = 0; + attr.colormap = XCreateColormap( CNFGDisplay, wnd, CNFGVisual, AllocNone); + if( w > 0 && h > 0 ) + CNFGWindow = XCreateWindow(CNFGDisplay, wnd, 1, 1, w, h, 0, CNFGDepth, InputOutput, CNFGVisual, CWBackPixel | CWColormap, &attr ); + else + { + if( w < 0 ) w = -w; + if( h < 0 ) h = -h; + CNFGWindow = XCreateWindow(CNFGDisplay, wnd, 1, 1, w, h, 0, CNFGDepth, InputOutput, CNFGVisual, CWBackPixel | CWColormap, &attr ); + CNFGWindowInvisible = 1; + } + + InternalLinkScreenAndGo( WindowName ); + +//Not sure of the purpose of this code - if it's still commented out after 2019-12-31 and no one knows why, please delete it. + CFNGWMDeleteWindow = XInternAtom( CNFGDisplay, "WM_DELETE_WINDOW", False ); + XSetWMProtocols( CNFGDisplay, CNFGWindow, &CFNGWMDeleteWindow, 1 ); + + // X11 doesn't have a concept of a hidden cursor. Make a blank cursor as a substitute + XColor col = { 0 }; + Pixmap blank = XCreateBitmapFromData( CNFGDisplay, CNFGWindow, (char*)(&col), 1, 1 ); + CNFGCursors[CNFG_CURSOR_HIDDEN] = XCreatePixmapCursor( CNFGDisplay, blank, blank, &col, &col, 0, 0 ); + XFreePixmap( CNFGDisplay, blank ); + +#ifdef CNFGOGL + glXMakeCurrent( CNFGDisplay, CNFGWindow, CNFGCtx ); +#endif + +#ifdef CNFG_BATCH +#ifndef CNFGCONTEXTONLY + CNFGSetupBatchInternal(); +#endif +#endif + + return 0; +} + +int CNFGHandleInput() +{ + if( !CNFGWindow ) return 0; + static int ButtonsDown; + XEvent report; + + int bKeyDirection = 1; + while( XPending( CNFGDisplay ) ) + { + XNextEvent( CNFGDisplay, &report ); + + bKeyDirection = 1; + switch (report.type) + { + case NoExpose: + break; + case Expose: + XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt ); + if( CNFGPixmap ) XFreePixmap( CNFGDisplay, CNFGPixmap ); + CNFGPixmap = XCreatePixmap( CNFGDisplay, CNFGWindow, CNFGWinAtt.width, CNFGWinAtt.height, CNFGWinAtt.depth ); + if( CNFGGC ) XFreeGC( CNFGDisplay, CNFGGC ); + CNFGGC = XCreateGC(CNFGDisplay, CNFGPixmap, 0, 0); + + CNFGLastScancode = 0; + HandleKey( CNFG_X11_EXPOSE, 0 ); + break; + case KeyRelease: + { + bKeyDirection = 0; + //Tricky - handle key repeats cleanly. + if( XPending( CNFGDisplay ) ) + { + XEvent nev; + XPeekEvent( CNFGDisplay, &nev ); + if (nev.type == KeyPress && nev.xkey.time == report.xkey.time && nev.xkey.keycode == report.xkey.keycode ) + bKeyDirection = 2; + } + } + case KeyPress: + g_x_global_key_state = report.xkey.state; + g_x_global_shift_key = XLookupKeysym(&report.xkey, 1); + + CNFGLastScancode = report.xkey.keycode; + + // Chars should ONLY be handled on Key Press + if (report.type == KeyPress) { + char buf[8] = {0}; + if (Xutf8LookupString(CNFGXIC, &report.xkey, buf, 8, NULL, NULL)) CNFGLastCharacter = *((int*)buf); + else CNFGLastCharacter = 0; + } + + KeySym sym = XLookupKeysym(&report.xkey, 0); + HandleKey( sym, bKeyDirection ); + + // Don't confuse the program when CNFGLastCharacter is set on KeyRelease. + // Since we shouldn't be using CNFGLastCharacter outside HandleKey anyways, just clear it here. + CNFGLastCharacter = 0; + + break; + case ButtonRelease: + bKeyDirection = 0; + case ButtonPress: + HandleButton( report.xbutton.x, report.xbutton.y, report.xbutton.button, bKeyDirection ); + ButtonsDown = (ButtonsDown & (~(1<>1 ); + break; + case FocusIn: + case FocusOut: + CNFGLastScancode = 0; + HandleKey( CNFG_KEY_FOCUS, report.type == FocusIn ); + break; + case ClientMessage: + if ( report.xclient.data.l[0] == CFNGWMDeleteWindow ) + return 0; + break; + default: + break; + //printf( "Event: %d\n", report.type ); + } + } + return 1; +} + + +#ifdef CNFGOGL + +void CNFGSetVSync( int vson ) +{ + void (*glfn)( int ); + void (*glfnXE)( Display *, GLXDrawable, int ); + glfn = (void (*)( int ))CNFGGetExtension( "glXSwapIntervalMESA" ); if( glfn ) glfn( vson ); + glfn = (void (*)( int ))CNFGGetExtension( "glXSwapIntervalSGI" ); if( glfn ) glfn( vson ); + glfnXE = (void (*)( Display *, GLXDrawable, int ))CNFGGetExtension( "glXSwapIntervalEXT" ); if( glfn ) glfnXE( CNFGDisplay, CNFGWindow, vson ); +} + +#ifdef CNFGRASTERIZER +void CNFGSwapBuffersInternal() +#else +void CNFGSwapBuffers() +#endif +{ + if( CNFGWindowInvisible ) return; + +#ifndef CNFGRASTERIZER +#ifndef CNFGCONTEXTONLY + CNFGFlushRender(); +#endif +#endif + +#ifdef CNFG_HAS_XSHAPE + if( taint_shape ) + { + XShapeCombineMask(CNFGDisplay, CNFGWindow, ShapeBounding, 0, 0, xspixmap, ShapeSet); + taint_shape = 0; + } +#endif //CNFG_HAS_XSHAPE + glXSwapBuffers( CNFGDisplay, CNFGWindow ); + +#ifdef FULL_SCREEN_STEAL_FOCUS + if( FullScreen ) + XSetInputFocus( CNFGDisplay, CNFGWindow, RevertToParent, CurrentTime ); +#endif //FULL_SCREEN_STEAL_FOCUS +} + +#else //CNFGOGL + +#ifndef CNFGRASTERIZER +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ) +{ + static int lw, lh; + + if( lw != w || lh != h || !xi ) + { + if( xi ) free( xi ); + xi = XCreateImage(CNFGDisplay, CNFGVisual, CNFGDepth, ZPixmap, 0, (char*)data, w, h, 32, w*4 ); + lw = w; + lh = h; + } + xi->data = (char*)data; + //Draw image to pixmap (not a screen flip) + XPutImage(CNFGDisplay, CNFGPixmap, CNFGGC, xi, 0, 0, x, y, w, h ); +} +#endif + +void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h ) +{ + static int lw, lh; + + if( lw != w || lh != h || !xi ) + { + if( xi ) free( xi ); + xi = XCreateImage(CNFGDisplay, CNFGVisual, CNFGDepth, ZPixmap, 0, (char*)data, w, h, 32, w*4 ); + lw = w; + lh = h; + } + xi->data = (char*)data; + //Directly write image to screen (effectively a flip) + XPutImage(CNFGDisplay, CNFGWindow, CNFGWindowGC, xi, 0, 0, 0, 0, w, h ); +} + +#endif //CNFGOGL + +#if !defined( CNFGOGL) +#define AGLF(x) x +#else +#define AGLF(x) static inline BACKEND_##x +#endif + +#if defined( CNFGRASTERIZER ) +//Don't call this file yourself. It is intended to be included in any drivers which want to support the rasterizer plugin. + +#ifdef CNFGRASTERIZER +//#include +#include + +uint32_t * CNFGBuffer = 0; +short CNFGBufferx; +short CNFGBuffery; + +#ifdef CNFGOGL +void CNFGFlushRender() +{ +} +#endif + +void CNFGInternalResize( short x, short y ) +{ + CNFGBufferx = x; + CNFGBuffery = y; + if( CNFGBuffer ) free( CNFGBuffer ); + CNFGBuffer = malloc( CNFGBufferx * CNFGBuffery * 4 ); +#ifdef CNFGOGL + void CNFGInternalResizeOGLBACKEND( short w, short h ); + CNFGInternalResizeOGLBACKEND( x, y ); +#endif +} + +#ifdef __wasm__ +static uint32_t SWAPS( uint32_t r ) +{ + uint32_t ret = (r&0xFF)<<24; + r>>=8; + ret |= (r&0xff)<<16; + r>>=8; + ret |= (r&0xff)<<8; + r>>=8; + ret |= (r&0xff)<<0; + return ret; +} +#elif !defined(CNFGOGL) +#define SWAPS(x) (x>>8) +#else +static uint32_t SWAPS( uint32_t r ) +{ + uint32_t ret = (r&0xFF)<<16; + r>>=8; + ret |= (r&0xff)<<8; + r>>=8; + ret |= (r&0xff); + r>>=8; + ret |= (r&0xff)<<24; + return ret; +} +#endif +uint32_t CNFGColor( uint32_t RGB ) +{ + CNFGLastColor = SWAPS(RGB); + return CNFGLastColor; +} + +void CNFGTackSegment( short x1, short y1, short x2, short y2 ) +{ + short tx, ty; + //float slope, lp; + float slope; + + short dx = x2 - x1; + short dy = y2 - y1; + + if( !CNFGBuffer ) return; + + if( dx < 0 ) dx = -dx; + if( dy < 0 ) dy = -dy; + + if( dx > dy ) + { + short minx = (x1 < x2)?x1:x2; + short maxx = (x1 < x2)?x2:x1; + short miny = (x1 < x2)?y1:y2; + short maxy = (x1 < x2)?y2:y1; + float thisy = miny; + slope = (float)(maxy-miny) / (float)(maxx-minx); + + for( tx = minx; tx <= maxx; tx++ ) + { + ty = thisy; + if( tx < 0 || ty < 0 || ty >= CNFGBuffery ) continue; + if( tx >= CNFGBufferx ) break; + CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor; + thisy += slope; + } + } + else + { + short minx = (y1 < y2)?x1:x2; + short maxx = (y1 < y2)?x2:x1; + short miny = (y1 < y2)?y1:y2; + short maxy = (y1 < y2)?y2:y1; + float thisx = minx; + slope = (float)(maxx-minx) / (float)(maxy-miny); + + for( ty = miny; ty <= maxy; ty++ ) + { + tx = thisx; + if( ty < 0 || tx < 0 || tx >= CNFGBufferx ) continue; + if( ty >= CNFGBuffery ) break; + CNFGBuffer[ty * CNFGBufferx + tx] = CNFGLastColor; + thisx += slope; + } + } +} +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ) +{ + short minx = (x1=x2)?x1:x2; + short maxy = (y1>=y2)?y1:y2; + + short x, y; + + if( minx < 0 ) minx = 0; + if( miny < 0 ) miny = 0; + if( maxx >= CNFGBufferx ) maxx = CNFGBufferx-1; + if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1; + + for( y = miny; y <= maxy; y++ ) + { + uint32_t * CNFGBufferstart = &CNFGBuffer[y * CNFGBufferx + minx]; + for( x = minx; x <= maxx; x++ ) + { + (*CNFGBufferstart++) = CNFGLastColor; + } + } +} + +void CNFGTackPoly( RDPoint * points, int verts ) +{ + short minx = 10000, miny = 10000; + short maxx =-10000, maxy =-10000; + short i, x, y; + + //Just in case... + if( verts > 32767 ) return; + + for( i = 0; i < verts; i++ ) + { + RDPoint * p = &points[i]; + if( p->x < minx ) minx = p->x; + if( p->y < miny ) miny = p->y; + if( p->x > maxx ) maxx = p->x; + if( p->y > maxy ) maxy = p->y; + } + + if( miny < 0 ) miny = 0; + if( maxy >= CNFGBuffery ) maxy = CNFGBuffery-1; + + for( y = miny; y <= maxy; y++ ) + { + short startfillx = maxx; + short endfillx = minx; + + //Figure out what line segments intersect this line. + for( i = 0; i < verts; i++ ) + { + short pl = i + 1; + if( pl == verts ) pl = 0; + + RDPoint ptop; + RDPoint pbot; + + ptop.x = points[i].x; + ptop.y = points[i].y; + pbot.x = points[pl].x; + pbot.y = points[pl].y; +//printf( "Poly: %d %d\n", pbot.y, ptop.y ); + + if( pbot.y < ptop.y ) + { + RDPoint ptmp; + ptmp.x = pbot.x; + ptmp.y = pbot.y; + pbot.x = ptop.x; + pbot.y = ptop.y; + ptop.x = ptmp.x; + ptop.y = ptmp.y; + } + + //Make sure this line segment is within our range. +//printf( "PT: %d %d %d\n", y, ptop.y, pbot.y ); + if( ptop.y <= y && pbot.y >= y ) + { + short diffy = pbot.y - ptop.y; + uint32_t placey = (uint32_t)(y - ptop.y)<<16; //Scale by 16 so we can do integer math. + short diffx = pbot.x - ptop.x; + short isectx; + + if( diffy == 0 ) + { + if( pbot.x < ptop.x ) + { + if( startfillx > pbot.x ) startfillx = pbot.x; + if( endfillx < ptop.x ) endfillx = ptop.x; + } + else + { + if( startfillx > ptop.x ) startfillx = ptop.x; + if( endfillx < pbot.x ) endfillx = pbot.x; + } + } + else + { + //Inner part is scaled by 65536, outer part must be scaled back. + isectx = (( (placey / diffy) * diffx + 32768 )>>16) + ptop.x; + if( isectx < startfillx ) startfillx = isectx; + if( isectx > endfillx ) endfillx = isectx; + } +//printf( "R: %d %d %d\n", pbot.x, ptop.x, isectx ); + } + } + +//printf( "%d %d %d\n", y, startfillx, endfillx ); + + if( endfillx >= CNFGBufferx ) endfillx = CNFGBufferx - 1; + if( endfillx >= CNFGBufferx ) endfillx = CNFGBuffery - 1; + if( startfillx < 0 ) startfillx = 0; + if( startfillx < 0 ) startfillx = 0; + + unsigned int * bufferstart = &CNFGBuffer[y * CNFGBufferx + startfillx]; + for( x = startfillx; x <= endfillx; x++ ) + { + (*bufferstart++) = CNFGLastColor; + } + } +//exit(1); +} + + +void CNFGClearFrame() +{ + int i, m; + uint32_t col = 0; + short x, y; + CNFGGetDimensions( &x, &y ); + if( x != CNFGBufferx || y != CNFGBuffery || !CNFGBuffer ) + { + CNFGBufferx = x; + CNFGBuffery = y; + CNFGBuffer = malloc( x * y * 8 ); + } + + m = x * y; + col = CNFGColor( CNFGBGColor ); + for( i = 0; i < m; i++ ) + { + CNFGBuffer[i] = col; + } +} + +void CNFGTackPixel( short x, short y ) +{ + if( x < 0 || y < 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return; + CNFGBuffer[x+CNFGBufferx*y] = CNFGLastColor; +} + + +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ) +{ + int ox = x; + int stride = w; + if( w <= 0 || h <= 0 || x >= CNFGBufferx || y >= CNFGBuffery ) return; + if( x < 0 ) { w += x; x = 0; } + if( y < 0 ) { h += y; y = 0; } + + //Switch w,h to x2, y2 + h += y; + w += x; + + if( w >= CNFGBufferx ) { w = CNFGBufferx; } + if( h >= CNFGBuffery ) { h = CNFGBuffery; } + + + for( ; y < h-1; y++ ) + { + x = ox; + uint32_t * indat = data; + uint32_t * outdat = CNFGBuffer + y * CNFGBufferx + x; + for( ; x < w-1; x++ ) + { + uint32_t newm = *(indat++); + uint32_t oldm = *(outdat); + if( (newm & 0xff) == 0xff ) + { + *(outdat++) = newm; + } + else + { + //Alpha blend. + int alfa = newm&0xff; + int onemalfa = 255-alfa; +#ifdef __wasm__ + uint32_t newv = 255<<0; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; +#elif defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) + uint32_t newv = 255UL<<24; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; +#elif defined( ANDROID ) || defined( __android__ ) + uint32_t newv = 255<<16; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; +#elif defined( CNFGOGL ) //OGL, on X11 + uint32_t newv = 255<<16; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>24)&0xff) * onemalfa + 128)>>8)<<24; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; +#else //X11 + uint32_t newv = 255UL<<24; //Alpha, then RGB + newv |= ((((newm>>24)&0xff) * alfa + ((oldm>>16)&0xff) * onemalfa + 128)>>8)<<16; + newv |= ((((newm>>16)&0xff) * alfa + ((oldm>>8)&0xff) * onemalfa + 128)>>8)<<8; + newv |= ((((newm>>8)&0xff) * alfa + ((oldm>>0)&0xff) * onemalfa + 128)>>8)<<0; +#endif + *(outdat++) = newv; + } + } + data += stride; + } +} + +void CNFGSwapBuffers() +{ + CNFGUpdateScreenWithBitmap( (uint32_t*)CNFGBuffer, CNFGBufferx, CNFGBuffery ); +} + + +#endif + +#undef AGLF +#define AGLF(x) static inline BACKEND_##x +#endif + +uint32_t AGLF(CNFGColor)( uint32_t RGB ) +{ + CNFGLastColor = RGB; + unsigned char red = ( RGB >> 24 ) & 0xFF; + unsigned char grn = ( RGB >> 16 ) & 0xFF; + unsigned char blu = ( RGB >> 8 ) & 0xFF; + unsigned long color = (red<<16)|(grn<<8)|(blu); + XSetForeground(CNFGDisplay, CNFGGC, color); + return color; +} + +void AGLF(CNFGClearFrame)() +{ + XGetWindowAttributes( CNFGDisplay, CNFGWindow, &CNFGWinAtt ); + XSetForeground(CNFGDisplay, CNFGGC, CNFGColor(CNFGBGColor) ); + XFillRectangle(CNFGDisplay, CNFGPixmap, CNFGGC, 0, 0, CNFGWinAtt.width, CNFGWinAtt.height ); +} + +void AGLF(CNFGSwapBuffers)() +{ +#ifdef CNFG_HAS_XSHAPE + if( taint_shape ) + { + XShapeCombineMask(CNFGDisplay, CNFGWindow, ShapeBounding, 0, 0, xspixmap, ShapeSet); + taint_shape = 0; + } +#endif + XCopyArea(CNFGDisplay, CNFGPixmap, CNFGWindow, CNFGWindowGC, 0,0,CNFGWinAtt.width,CNFGWinAtt.height,0,0); + XFlush(CNFGDisplay); +#ifdef FULL_SCREEN_STEAL_FOCUS + if( FullScreen ) + XSetInputFocus( CNFGDisplay, CNFGWindow, RevertToParent, CurrentTime ); +#endif +} + +void AGLF(CNFGTackSegment)( short x1, short y1, short x2, short y2 ) +{ + if( x1 == x2 && y1 == y2 ) + { + //On some targets, zero-length lines will not show up. + //This is tricky - since this will also cause more draw calls for points on systems like GLAMOR. + XDrawPoint( CNFGDisplay, CNFGPixmap, CNFGGC, x2, y2 ); + } + else + { + //XXX HACK! See discussion here: https://github.com/cntools/cnping/issues/68 + XDrawLine( CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1, x2, y2 ); + XDrawLine( CNFGDisplay, CNFGPixmap, CNFGGC, x2, y2, x1, y1 ); + } +} + +void AGLF(CNFGTackPixel)( short x1, short y1 ) +{ + XDrawPoint( CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1 ); +} + +void AGLF(CNFGTackRectangle)( short x1, short y1, short x2, short y2 ) +{ + XFillRectangle(CNFGDisplay, CNFGPixmap, CNFGGC, x1, y1, x2-x1, y2-y1 ); +} + +void AGLF(CNFGTackPoly)( RDPoint * points, int verts ) +{ + XFillPolygon(CNFGDisplay, CNFGPixmap, CNFGGC, (XPoint *)points, verts, Convex, CoordModeOrigin ); +} + +void AGLF(CNFGInternalResize)( short x, short y ) { } + +void AGLF(CNFGSetLineWidth)( short width ) +{ + XSetLineAttributes(CNFGDisplay, CNFGGC, width, LineSolid, CapRound, JoinRound); +} + +#endif // _CNFGXDRIVER_C + + +#endif + +/* +Copyright (c) 2010-2021 <>< Charles Lohr, and several others! + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef _CNFG_C +#define _CNFG_C + + +#ifndef CNFGHTTPSERVERONLY + +#ifdef _CNFG_FANCYFONT +#include "TextTool/FontData.h" +#endif + +int CNFGPenX, CNFGPenY; +uint32_t CNFGBGColor; +uint32_t CNFGLastColor; +//uint32_t CNFGDialogColor; //background for boxes [DEPRECATED] + +// The following two arrays are generated by Fonter/fonter.cpp +const unsigned short RawdrawFontCharMap[256] = { + 65535, 0, 8, 16, 24, 31, 41, 50, 51, 65535, 65535, 57, 66, 65535, 75, 83, + 92, 96, 100, 108, 114, 123, 132, 137, 147, 152, 158, 163, 169, 172, 178, 182, + 65535, 186, 189, 193, 201, 209, 217, 226, 228, 232, 236, 244, 248, 250, 252, 253, + 255, 261, 266, 272, 278, 283, 289, 295, 300, 309, 316, 318, 321, 324, 328, 331, + 337, 345, 352, 362, 368, 375, 382, 388, 396, 402, 408, 413, 422, 425, 430, 435, + 442, 449, 458, 466, 472, 476, 480, 485, 492, 500, 507, 512, 516, 518, 522, 525, + 527, 529, 536, 541, 546, 551, 557, 564, 572, 578, 581, 586, 593, 595, 604, 610, + 615, 621, 627, 632, 638, 642, 648, 653, 660, 664, 670, 674, 680, 684, 690, 694, + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535, + 700, 703, 711, 718, 731, 740, 744, 754, 756, 760, 766, 772, 775, 777, 785, 787, + 792, 798, 803, 811, 813, 820, 827, 828, 831, 833, 838, 844, 853, 862, 874, 880, + 889, 898, 908, 919, 928, 939, 951, 960, 969, 978, 988, 997, 1005, 1013, 1022, 1030, + 1039, 1047, 1054, 1061, 1070, 1079, 1086, 1090, 1099, 1105, 1111, 1118, 1124, 1133, 1140, 1150, + 1159, 1168, 1178, 1189, 1198, 1209, 1222, 1231, 1239, 1247, 1256, 1264, 1268, 1272, 1277, 1281, + 1290, 1300, 1307, 1314, 1322, 1331, 1338, 1342, 1349, 1357, 1365, 1374, 1382, 1390, 1397, 65535, }; + +const unsigned char RawdrawFontCharData[1405] = { + 0x00, 0x09, 0x20, 0x29, 0x03, 0x23, 0x14, 0x8b, 0x00, 0x09, 0x20, 0x29, 0x04, 0x24, 0x13, 0x8c, + 0x01, 0x21, 0x23, 0x14, 0x03, 0x09, 0x11, 0x9a, 0x11, 0x22, 0x23, 0x14, 0x03, 0x02, 0x99, 0x01, + 0x21, 0x23, 0x09, 0x03, 0x29, 0x03, 0x09, 0x12, 0x9c, 0x03, 0x2b, 0x13, 0x1c, 0x23, 0x22, 0x11, + 0x02, 0x8b, 0x9a, 0x1a, 0x01, 0x21, 0x23, 0x03, 0x89, 0x03, 0x21, 0x2a, 0x21, 0x19, 0x03, 0x14, + 0x23, 0x9a, 0x01, 0x10, 0x21, 0x12, 0x09, 0x12, 0x1c, 0x03, 0xab, 0x02, 0x03, 0x1b, 0x02, 0x1a, + 0x13, 0x10, 0xa9, 0x01, 0x2b, 0x03, 0x29, 0x02, 0x11, 0x22, 0x13, 0x8a, 0x00, 0x22, 0x04, 0x88, + 0x20, 0x02, 0x24, 0xa8, 0x01, 0x10, 0x29, 0x10, 0x14, 0x0b, 0x14, 0xab, 0x00, 0x0b, 0x0c, 0x20, + 0x2b, 0xac, 0x00, 0x28, 0x00, 0x02, 0x2a, 0x10, 0x1c, 0x20, 0xac, 0x01, 0x21, 0x23, 0x03, 0x09, + 0x20, 0x10, 0x14, 0x8c, 0x03, 0x23, 0x24, 0x04, 0x8b, 0x01, 0x10, 0x29, 0x10, 0x14, 0x0b, 0x14, + 0x2b, 0x04, 0xac, 0x01, 0x18, 0x21, 0x10, 0x9c, 0x03, 0x1c, 0x23, 0x1c, 0x10, 0x9c, 0x02, 0x22, + 0x19, 0x22, 0x9b, 0x02, 0x2a, 0x02, 0x19, 0x02, 0x9b, 0x01, 0x02, 0xaa, 0x02, 0x22, 0x11, 0x02, + 0x13, 0xaa, 0x11, 0x22, 0x02, 0x99, 0x02, 0x13, 0x22, 0x8a, 0x10, 0x1b, 0x9c, 0x10, 0x09, 0x20, + 0x99, 0x10, 0x1c, 0x20, 0x2c, 0x01, 0x29, 0x03, 0xab, 0x21, 0x10, 0x01, 0x23, 0x14, 0x0b, 0x10, + 0x9c, 0x00, 0x09, 0x23, 0x2c, 0x04, 0x03, 0x21, 0xa8, 0x21, 0x10, 0x01, 0x12, 0x03, 0x14, 0x2b, + 0x02, 0xac, 0x10, 0x99, 0x10, 0x01, 0x03, 0x9c, 0x10, 0x21, 0x23, 0x9c, 0x01, 0x2b, 0x11, 0x1b, + 0x21, 0x0b, 0x02, 0xaa, 0x02, 0x2a, 0x11, 0x9b, 0x04, 0x9b, 0x02, 0xaa, 0x9c, 0x03, 0xa9, 0x00, + 0x20, 0x24, 0x04, 0x08, 0x9a, 0x01, 0x10, 0x1c, 0x04, 0xac, 0x01, 0x10, 0x21, 0x22, 0x04, 0xac, + 0x00, 0x20, 0x24, 0x0c, 0x12, 0xaa, 0x00, 0x02, 0x2a, 0x20, 0xac, 0x20, 0x00, 0x02, 0x22, 0x24, + 0x8c, 0x20, 0x02, 0x22, 0x24, 0x04, 0x8a, 0x00, 0x20, 0x21, 0x12, 0x9c, 0x00, 0x0c, 0x00, 0x20, + 0x2c, 0x04, 0x2c, 0x02, 0xaa, 0x00, 0x02, 0x22, 0x20, 0x08, 0x22, 0x8c, 0x19, 0x9b, 0x19, 0x13, + 0x8c, 0x20, 0x02, 0xac, 0x01, 0x29, 0x03, 0xab, 0x00, 0x22, 0x8c, 0x01, 0x10, 0x21, 0x12, 0x1b, + 0x9c, 0x21, 0x01, 0x04, 0x24, 0x22, 0x12, 0x13, 0xab, 0x04, 0x01, 0x10, 0x21, 0x2c, 0x02, 0xaa, + 0x00, 0x04, 0x14, 0x23, 0x12, 0x0a, 0x12, 0x21, 0x10, 0x88, 0x23, 0x14, 0x03, 0x01, 0x10, 0xa9, + 0x00, 0x10, 0x21, 0x23, 0x14, 0x04, 0x88, 0x00, 0x04, 0x2c, 0x00, 0x28, 0x02, 0x9a, 0x00, 0x0c, + 0x00, 0x28, 0x02, 0x9a, 0x21, 0x10, 0x01, 0x03, 0x14, 0x23, 0x22, 0x9a, 0x00, 0x0c, 0x20, 0x2c, + 0x02, 0xaa, 0x00, 0x28, 0x10, 0x1c, 0x04, 0xac, 0x00, 0x20, 0x23, 0x14, 0x8b, 0x00, 0x0c, 0x02, + 0x12, 0x21, 0x28, 0x12, 0x23, 0xac, 0x00, 0x04, 0xac, 0x04, 0x00, 0x11, 0x20, 0xac, 0x04, 0x00, + 0x2a, 0x20, 0xac, 0x01, 0x10, 0x21, 0x23, 0x14, 0x03, 0x89, 0x00, 0x0c, 0x00, 0x10, 0x21, 0x12, + 0x8a, 0x01, 0x10, 0x21, 0x23, 0x14, 0x03, 0x09, 0x04, 0x9b, 0x00, 0x0c, 0x00, 0x10, 0x21, 0x12, + 0x02, 0xac, 0x21, 0x10, 0x01, 0x23, 0x14, 0x8b, 0x00, 0x28, 0x10, 0x9c, 0x00, 0x04, 0x24, 0xa8, + 0x00, 0x03, 0x14, 0x23, 0xa8, 0x00, 0x04, 0x2c, 0x14, 0x1b, 0x24, 0xa8, 0x00, 0x01, 0x23, 0x2c, + 0x04, 0x03, 0x21, 0xa8, 0x00, 0x01, 0x12, 0x1c, 0x12, 0x21, 0xa8, 0x00, 0x20, 0x02, 0x04, 0xac, + 0x10, 0x00, 0x04, 0x9c, 0x01, 0xab, 0x10, 0x20, 0x24, 0x9c, 0x01, 0x10, 0xa9, 0x04, 0xac, 0x00, + 0x99, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x8a, 0x00, 0x04, 0x24, 0x22, 0x8a, 0x24, 0x04, 0x03, + 0x12, 0xaa, 0x20, 0x24, 0x04, 0x02, 0xaa, 0x24, 0x04, 0x02, 0x22, 0x23, 0x9b, 0x04, 0x09, 0x02, + 0x1a, 0x01, 0x10, 0xa9, 0x23, 0x12, 0x03, 0x14, 0x23, 0x24, 0x15, 0x8c, 0x00, 0x0c, 0x03, 0x12, + 0x23, 0xac, 0x19, 0x12, 0x9c, 0x2a, 0x23, 0x24, 0x15, 0x8c, 0x00, 0x0c, 0x03, 0x13, 0x2a, 0x13, + 0xac, 0x10, 0x9c, 0x02, 0x0c, 0x02, 0x1b, 0x12, 0x1c, 0x12, 0x23, 0xac, 0x02, 0x0c, 0x03, 0x12, + 0x23, 0xac, 0x02, 0x22, 0x24, 0x04, 0x8a, 0x02, 0x0d, 0x04, 0x24, 0x22, 0x8a, 0x02, 0x04, 0x2c, + 0x25, 0x22, 0x8a, 0x02, 0x0c, 0x03, 0x12, 0xaa, 0x22, 0x02, 0x03, 0x23, 0x24, 0x8c, 0x11, 0x1c, + 0x02, 0xaa, 0x02, 0x04, 0x14, 0x2b, 0x24, 0xaa, 0x02, 0x03, 0x14, 0x23, 0xaa, 0x02, 0x03, 0x14, + 0x1a, 0x13, 0x24, 0xaa, 0x02, 0x2c, 0x04, 0xaa, 0x02, 0x03, 0x1c, 0x22, 0x23, 0x8d, 0x02, 0x22, + 0x04, 0xac, 0x20, 0x10, 0x14, 0x2c, 0x12, 0x8a, 0x10, 0x19, 0x13, 0x9c, 0x00, 0x10, 0x14, 0x0c, + 0x12, 0xaa, 0x01, 0x10, 0x11, 0xa8, 0x03, 0x04, 0x24, 0x23, 0x12, 0x8b, 0x18, 0x11, 0x9c, 0x21, + 0x10, 0x01, 0x02, 0x13, 0x2a, 0x10, 0x9b, 0x11, 0x00, 0x04, 0x24, 0x2b, 0x02, 0x9a, 0x01, 0x0a, + 0x11, 0x29, 0x22, 0x2b, 0x03, 0x1b, 0x02, 0x11, 0x22, 0x13, 0x8a, 0x00, 0x11, 0x28, 0x11, 0x1c, + 0x02, 0x2a, 0x03, 0xab, 0x10, 0x1a, 0x13, 0x9d, 0x20, 0x00, 0x02, 0x11, 0x2a, 0x02, 0x13, 0x22, + 0x24, 0x8c, 0x08, 0xa8, 0x20, 0x10, 0x11, 0xa9, 0x10, 0x29, 0x20, 0x21, 0x11, 0x98, 0x11, 0x02, + 0x1b, 0x21, 0x12, 0xab, 0x01, 0x21, 0xaa, 0x12, 0xaa, 0x10, 0x20, 0x21, 0x19, 0x12, 0x18, 0x11, + 0xaa, 0x00, 0xa8, 0x01, 0x10, 0x21, 0x12, 0x89, 0x02, 0x2a, 0x11, 0x1b, 0x03, 0xab, 0x01, 0x10, + 0x21, 0x03, 0xab, 0x01, 0x10, 0x21, 0x12, 0x0a, 0x12, 0x23, 0x8b, 0x11, 0xa8, 0x02, 0x0d, 0x04, + 0x14, 0x2b, 0x22, 0xac, 0x14, 0x10, 0x01, 0x1a, 0x10, 0x20, 0xac, 0x9a, 0x14, 0x15, 0x8d, 0x20, + 0xa9, 0x10, 0x20, 0x21, 0x11, 0x98, 0x01, 0x12, 0x0b, 0x11, 0x22, 0x9b, 0x00, 0x09, 0x02, 0x28, + 0x12, 0x13, 0x2b, 0x22, 0xac, 0x00, 0x09, 0x02, 0x28, 0x12, 0x22, 0x13, 0x14, 0xac, 0x00, 0x10, + 0x11, 0x09, 0x11, 0x02, 0x28, 0x12, 0x13, 0x2b, 0x22, 0xac, 0x18, 0x11, 0x12, 0x03, 0x14, 0xab, + 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x10, 0xa9, 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, + 0x01, 0x98, 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x01, 0x10, 0xa9, 0x04, 0x02, 0x11, 0x22, + 0x2c, 0x03, 0x2b, 0x01, 0x10, 0x11, 0xa8, 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x08, 0xa8, + 0x04, 0x02, 0x11, 0x22, 0x2c, 0x03, 0x2b, 0x00, 0x20, 0x11, 0x88, 0x00, 0x0c, 0x02, 0x2a, 0x00, + 0x19, 0x10, 0x1c, 0x10, 0x28, 0x14, 0xac, 0x23, 0x14, 0x03, 0x01, 0x10, 0x29, 0x14, 0x15, 0x8d, + 0x02, 0x2a, 0x02, 0x04, 0x2c, 0x03, 0x1b, 0x00, 0x99, 0x02, 0x2a, 0x02, 0x04, 0x2c, 0x03, 0x1b, + 0x11, 0xa8, 0x02, 0x2a, 0x02, 0x04, 0x2c, 0x03, 0x1b, 0x01, 0x10, 0xa9, 0x02, 0x2a, 0x02, 0x04, + 0x2c, 0x03, 0x1b, 0x08, 0xa8, 0x02, 0x2a, 0x12, 0x1c, 0x04, 0x2c, 0x00, 0x99, 0x02, 0x2a, 0x12, + 0x1c, 0x04, 0x2c, 0x11, 0xa8, 0x02, 0x2a, 0x12, 0x1c, 0x04, 0x2c, 0x01, 0x10, 0xa9, 0x02, 0x2a, + 0x12, 0x1c, 0x04, 0x2c, 0x28, 0x88, 0x00, 0x10, 0x21, 0x23, 0x14, 0x04, 0x08, 0x02, 0x9a, 0x04, + 0x02, 0x24, 0x2a, 0x01, 0x10, 0x11, 0xa8, 0x02, 0x22, 0x24, 0x04, 0x0a, 0x00, 0x99, 0x02, 0x22, + 0x24, 0x04, 0x0a, 0x11, 0xa8, 0x02, 0x22, 0x24, 0x04, 0x0a, 0x11, 0x28, 0x00, 0x99, 0x02, 0x22, + 0x24, 0x04, 0x0a, 0x01, 0x10, 0x11, 0xa8, 0x01, 0x21, 0x24, 0x04, 0x09, 0x08, 0xa8, 0x01, 0x2b, + 0x03, 0xa9, 0x01, 0x10, 0x21, 0x23, 0x14, 0x03, 0x09, 0x03, 0xa9, 0x01, 0x04, 0x24, 0x29, 0x11, + 0xa8, 0x01, 0x04, 0x24, 0x29, 0x00, 0x99, 0x02, 0x04, 0x24, 0x2a, 0x01, 0x10, 0xa9, 0x01, 0x04, + 0x24, 0x29, 0x08, 0xa8, 0x01, 0x02, 0x13, 0x1c, 0x13, 0x22, 0x29, 0x11, 0xa8, 0x00, 0x0c, 0x01, + 0x11, 0x22, 0x13, 0x8b, 0x00, 0x0d, 0x00, 0x10, 0x21, 0x1a, 0x02, 0x22, 0x24, 0x8c, 0x02, 0x04, + 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x00, 0x99, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x11, 0xa8, + 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x01, 0x10, 0xa9, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, + 0x0a, 0x01, 0x10, 0x11, 0xa8, 0x02, 0x04, 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x09, 0xa9, 0x02, 0x04, + 0x24, 0x2a, 0x23, 0x12, 0x0a, 0x01, 0x10, 0x21, 0x89, 0x02, 0x1b, 0x02, 0x04, 0x2c, 0x12, 0x1c, + 0x12, 0x2a, 0x13, 0x2b, 0x22, 0xab, 0x03, 0x04, 0x2c, 0x03, 0x12, 0x2a, 0x14, 0x15, 0x8d, 0x24, + 0x04, 0x02, 0x22, 0x23, 0x1b, 0x00, 0x99, 0x24, 0x04, 0x02, 0x22, 0x23, 0x1b, 0x11, 0xa8, 0x24, + 0x04, 0x02, 0x22, 0x23, 0x1b, 0x01, 0x10, 0xa9, 0x24, 0x04, 0x02, 0x22, 0x23, 0x1b, 0x09, 0xa9, + 0x12, 0x1c, 0x00, 0x99, 0x12, 0x1c, 0x11, 0xa8, 0x12, 0x1c, 0x01, 0x10, 0xa9, 0x12, 0x1c, 0x09, + 0xa9, 0x00, 0x2a, 0x11, 0x28, 0x02, 0x22, 0x24, 0x04, 0x8a, 0x02, 0x0c, 0x03, 0x12, 0x23, 0x2c, + 0x01, 0x10, 0x11, 0xa8, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x00, 0x99, 0x02, 0x04, 0x24, 0x22, 0x0a, + 0x11, 0xa8, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x01, 0x10, 0xa9, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x01, + 0x10, 0x11, 0xa8, 0x02, 0x04, 0x24, 0x22, 0x0a, 0x09, 0xa9, 0x19, 0x02, 0x2a, 0x9b, 0x02, 0x04, + 0x24, 0x22, 0x0a, 0x04, 0xaa, 0x02, 0x04, 0x14, 0x2b, 0x24, 0x2a, 0x00, 0x99, 0x02, 0x04, 0x14, + 0x2b, 0x24, 0x2a, 0x11, 0xa8, 0x02, 0x04, 0x14, 0x2b, 0x24, 0x2a, 0x01, 0x10, 0xa9, 0x02, 0x04, + 0x14, 0x2b, 0x24, 0x2a, 0x09, 0xa9, 0x02, 0x03, 0x1c, 0x22, 0x23, 0x0d, 0x11, 0xa8, 0x00, 0x0c, + 0x02, 0x11, 0x22, 0x13, 0x8a, 0x02, 0x03, 0x1c, 0x22, 0x23, 0x0d, 0x09, 0xa9, }; + + + +//Set this if you are only using CNFG to create an OpenGL context. +#ifndef CNFGCONTEXTONLY +uint32_t CNFGDialogColor; + +void CNFGDrawBox( short x1, short y1, short x2, short y2 ) +{ + uint32_t lc = CNFGLastColor; + CNFGColor( CNFGDialogColor ); + CNFGTackRectangle( x1, y1, x2, y2 ); + CNFGColor( lc ); + CNFGTackSegment( x1, y1, x2, y1 ); + CNFGTackSegment( x2, y1, x2, y2 ); + CNFGTackSegment( x2, y2, x1, y2 ); + CNFGTackSegment( x1, y2, x1, y1 ); +} + +void CNFGDrawText( const char * text, short scale ) +{ + const unsigned char * lmap; + float iox = (float)CNFGPenX; //x offset + float ioy = (float)CNFGPenY; //y offset + + int drawPlace = 0; + unsigned short index; + int bQuit = 0; + while( text[drawPlace] ) + { + unsigned char c = text[drawPlace]; + switch( c ) + { + case 9: // tab + iox += 12 * scale; + break; + case 10: // linefeed + iox = (float)CNFGPenX; + ioy += 6 * scale; + break; + default: + index = RawdrawFontCharMap[c]; + if( index == 65535 ) + { + iox += 3 * scale; + break; + } + + lmap = &RawdrawFontCharData[index]; + short penx = 0, peny = 0; + unsigned char start_seg = 1; + do + { + unsigned char data = (*(lmap++)); + short x1 = (short)(((data >> 4) & 0x07)*scale + iox); + short y1 = (short)((data & 0x07)*scale + ioy); + if( start_seg ) + { + penx = x1; + peny = y1; + start_seg = 0; + if( data & 0x08 ) + CNFGTackPixel( x1, y1 ); + } + else + { + CNFGTackSegment( penx, peny, x1, y1 ); + penx = x1; + peny = y1; + } + if( data & 0x08 ) start_seg = 1; + bQuit = data & 0x80; + } while( !bQuit ); + + iox += 3 * scale; + } + drawPlace++; + } +} + +#ifndef FONT_CREATION_TOOL +#ifdef _CNFG_FANCYFONT + +void CNFGDrawNiceText(const char* text, short scale) +{ + const unsigned char* lmap; + float iox = (float)CNFGPenX; //x offset + float ioy = (float)CNFGPenY; //y offset + + int place = 0; + unsigned short index; + int bQuit = 0; + int segmentEnd = 0; + while (text[place]) { + unsigned char c = text[place]; + switch (c) + { + case 9: // tab + iox += 16 * scale; + break; + case 10: // linefeed + iox = (float)CNFGPenX; + ioy += 6 * scale; + break; + default: + index = CharIndex[c]; + if (index == 0) { + iox += 4 * scale; + break; + } + + lmap = &FontData[index]; + + short charWidth = ((*lmap) & 0xE0) >> 5; //0b11100000 + short xbase = ((*lmap) & 0x18) >> 3; //0b00011000 + short ybase = (*lmap) & 0x07; //0b00000111 + lmap++; + do { + + int x1 = ((((*lmap) & 0x38) >> 3) * scale + iox + xbase * scale); //0b00111000 + int y1 = (((*lmap) & 0x07) * scale + ioy + ybase * scale); + segmentEnd = *lmap & 0x40; + int x2 = 0; + int y2 = 0; + lmap++; + if (segmentEnd) { + x2 = x1; + y2 = y1; + } + else { + + x2 = ((((*lmap) & 0x38) >> 3) * scale + iox + xbase * scale); + y2 = (((*lmap) & 0x07) * scale + ioy + ybase * scale); + + } + + + CNFGTackSegment(x1, y1, x2, y2); + bQuit = *(lmap - 1) & 0x80; + + } while (!bQuit); + iox += (charWidth + 2) * scale; + //iox += 8 * scale; + } + place++; + } +} +#endif +#endif + +void CNFGGetTextExtents( const char * text, int * w, int * h, int textsize ) +{ + int charsx = 0; + int charsy = 1; + int charsline = 0; + const char * s; + + for( s = text; *s; s++ ) + { + if( *s == '\n' ) + { + charsline = 0; + if( *(s+1) ) + charsy++; + } + else + { + charsline++; + if( charsline > charsx ) + charsx = charsline; + } + } + + *w = charsx * textsize * 3-1*textsize; + *h = charsy * textsize * 6; +} + +#if defined( CNFG_BATCH ) + +//This is the path by which we convert rawdraw functionality +//into nice batched triangle streams. + +//Just FYI we use floats for geometry instead of shorts becase it is harder +//to triangularize a diagonal line int triangles with shorts and have it look good. +void CNFGEmitBackendTriangles( const float * fv, const uint32_t * col, int nr_verts ); + +//If on WASM, sqrtf is implied. On other platforms, need sqrtf from math.h +#ifdef __wasm__ +float sqrtf( float f ); +#define cnfg_sqrtf sqrtf +#elif defined( __TINYC__ ) && defined( WIN32 ) +#define cnfg_sqrtf sqrt +#else +#define cnfg_sqrtf sqrtf +#include +#endif + +//Geometry batching system - so we can batch geometry and deliver it all at once. +float CNFGVertDataV[CNFG_BATCH*3]; +uint32_t CNFGVertDataC[CNFG_BATCH]; +int CNFGVertPlace; +static float wgl_last_width_over_2 = .5f; + +static void EmitQuad( float cx0, float cy0, float cx1, float cy1, float cx2, float cy2, float cx3, float cy3 ) +{ + //Because quads are really useful, but it's best to keep them all triangles if possible. + //This lets us draw arbitrary quads. + if( CNFGVertPlace >= CNFG_BATCH-6 ) CNFGFlushRender(); + float * fv = &CNFGVertDataV[CNFGVertPlace*3]; + fv[0] = cx0; fv[1] = cy0; + fv[3] = cx1; fv[4] = cy1; + fv[6] = cx2; fv[7] = cy2; + fv[9] = cx2; fv[10] = cy2; + fv[12] = cx1; fv[13] = cy1; + fv[15] = cx3; fv[16] = cy3; + uint32_t * col = &CNFGVertDataC[CNFGVertPlace]; + uint32_t color = CNFGLastColor; + col[0] = color; col[1] = color; col[2] = color; col[3] = color; col[4] = color; col[5] = color; + CNFGVertPlace += 6; +} + + +#if !defined( CNFGRASTERIZER ) && !defined( CNFGHTTP ) + +void CNFGTackPixel( short x1, short y1 ) +{ + x1++; y1++; + const float l2 = wgl_last_width_over_2; + const float l2u = wgl_last_width_over_2+0.5f; + EmitQuad( x1-l2u, y1-l2u, x1+l2, y1-l2u, x1-l2u, y1+l2, x1+l2, y1+l2 ); +} + + +void CNFGTackSegment( short x1, short y1, short x2, short y2 ) +{ + float ix1 = x1; + float iy1 = y1; + float ix2 = x2; + float iy2 = y2; + + float dx = ix2-ix1; + float dy = iy2-iy1; + float imag = 1.f/(float)(cnfg_sqrtf(dx*dx+dy*dy)); + dx *= imag; + dy *= imag; + float orthox = dy*wgl_last_width_over_2; + float orthoy =-dx*wgl_last_width_over_2; + + ix2 += dx/2 + 0.5f; + iy2 += dy/2 + 0.5f; + ix1 -= dx/2 - 0.5f; + iy1 -= dy/2 - 0.5f; + + //This logic is incorrect. XXX FIXME. + EmitQuad( (ix1 - orthox), (iy1 - orthoy), (ix1 + orthox), (iy1 + orthoy), (ix2 - orthox), (iy2 - orthoy), ( ix2 + orthox), ( iy2 + orthoy) ); +} + +void CNFGTackRectangle( short x1, short y1, short x2, short y2 ) +{ + float ix1 = x1; + float iy1 = y1; + float ix2 = x2; + float iy2 = y2; + EmitQuad( ix1,iy1,ix2,iy1,ix1,iy2,ix2,iy2 ); +} + +void CNFGTackPoly( RDPoint * points, int verts ) +{ + int i; + int tris = verts-2; + if( CNFGVertPlace >= CNFG_BATCH-tris*3 ) CNFGFlushRender(); + + uint32_t color = CNFGLastColor; + short * ptrsrc = (short*)points; + + for( i = 0; i < tris; i++ ) + { + float * fv = &CNFGVertDataV[CNFGVertPlace*3]; + fv[0] = ptrsrc[0]; + fv[1] = ptrsrc[1]; + fv[3] = ptrsrc[i*2+2]; + fv[4] = ptrsrc[i*2+3]; + fv[6] = ptrsrc[i*2+4]; + fv[7] = ptrsrc[i*2+5]; + + uint32_t * col = &CNFGVertDataC[CNFGVertPlace]; + col[0] = color; + col[1] = color; + col[2] = color; + + CNFGVertPlace += 3; + } +} + +uint32_t CNFGColor( uint32_t RGB ) +{ + return CNFGLastColor = RGB; +} + +void CNFGSetLineWidth( short width ) +{ + wgl_last_width_over_2 = width/2.0f;// + 0.5; +} + +#endif + + +#if !defined( __wasm__ ) && !defined( CNFGHTTP ) +//In WASM, Javascript takes over this functionality. + +//Shader compilation errors go to stderr. +#include + +#ifndef GL_VERTEX_SHADER +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_LINK_STATUS 0x8B82 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_CLAMP_TO_EDGE 0x812F +#define LGLchar char +#else +#define LGLchar GLchar +#endif + +#if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) +#define CNFGOGL_NEED_EXTENSION +#include +#endif + +#ifdef CNFGOGL_NEED_EXTENSION +// If we are going to be defining our own function pointer call + #if defined(WINDOWS) || defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) + // Make sure to use __stdcall on Windows + #define CHEWTYPEDEF( ret, name, rv, paramcall, ... ) \ + typedef ret (__stdcall *CNFGTYPE##name)( __VA_ARGS__ ); \ + ret (__stdcall *CNFG##name)( __VA_ARGS__ ); + #else + #define CHEWTYPEDEF( ret, name, rv, paramcall, ... ) \ + typedef ret (*CNFGTYPE##name)( __VA_ARGS__ ); \ + ret (*CNFG##name)( __VA_ARGS__ ); + #endif +#else +//If we are going to be defining the real call +#define CHEWTYPEDEF( ret, name, rv, paramcall, ... ) \ + ret name (__VA_ARGS__); +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + +int (*MyFunc)( int program, const LGLchar *name ); + +CHEWTYPEDEF( GLint, glGetUniformLocation, return, (program,name), GLuint program, const LGLchar *name ) +CHEWTYPEDEF( void, glEnableVertexAttribArray, , (index), GLuint index ) +CHEWTYPEDEF( void, glUseProgram, , (program), GLuint program ) +CHEWTYPEDEF( void, glGetProgramInfoLog, , (program,maxLength, length, infoLog), GLuint program, GLsizei maxLength, GLsizei *length, LGLchar *infoLog ) +CHEWTYPEDEF( void, glGetProgramiv, , (program,pname,params), GLuint program, GLenum pname, GLint *params ) +CHEWTYPEDEF( void, glBindAttribLocation, , (program,index,name), GLuint program, GLuint index, const LGLchar *name ) +CHEWTYPEDEF( void, glGetShaderiv, , (shader,pname,params), GLuint shader, GLenum pname, GLint *params ) +CHEWTYPEDEF( GLuint, glCreateShader, return, (e), GLenum e ) +CHEWTYPEDEF( void, glVertexAttribPointer, , (index,size,type,normalized,stride,pointer), GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer ) +CHEWTYPEDEF( void, glShaderSource, , (shader,count,string,length), GLuint shader, GLsizei count, const LGLchar *const*string, const GLint *length ) +CHEWTYPEDEF( void, glAttachShader, , (program,shader), GLuint program, GLuint shader ) +CHEWTYPEDEF( void, glCompileShader, ,(shader), GLuint shader ) +CHEWTYPEDEF( void, glGetShaderInfoLog , , (shader,maxLength, length, infoLog), GLuint shader, GLsizei maxLength, GLsizei *length, LGLchar *infoLog ) +CHEWTYPEDEF( GLuint, glCreateProgram, return, () , void ) +CHEWTYPEDEF( void, glLinkProgram, , (program), GLuint program ) +CHEWTYPEDEF( void, glDeleteShader, , (shader), GLuint shader ) +CHEWTYPEDEF( void, glUniform4f, , (location,v0,v1,v2,v3), GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3 ) +CHEWTYPEDEF( void, glUniform1i, , (location,i0), GLint location, GLint i0 ) +CHEWTYPEDEF( void, glActiveTexture, , (texture), GLenum texture ) + +#ifndef CNFGOGL_NEED_EXTENSION +#define CNFGglGetUniformLocation glGetUniformLocation +#define CNFGglEnableVertexAttribArray glEnableVertexAttribArray +#define CNFGglUseProgram glUseProgram +#define CNFGglEnableVertexAttribArray glEnableVertexAttribArray +#define CNFGglUseProgram glUseProgram +#define CNFGglGetProgramInfoLog glGetProgramInfoLog +#define CNFGglGetProgramiv glGetProgramiv +#define CNFGglShaderSource glShaderSource +#define CNFGglCreateShader glCreateShader +#define CNFGglAttachShader glAttachShader +#define CNFGglGetShaderiv glGetShaderiv +#define CNFGglCompileShader glCompileShader +#define CNFGglGetShaderInfoLog glGetShaderInfoLog +#define CNFGglCreateProgram glCreateProgram +#define CNFGglLinkProgram glLinkProgram +#define CNFGglDeleteShader glDeleteShader +#define CNFGglUniform4f glUniform4f +#define CNFGglBindAttribLocation glBindAttribLocation +#define CNFGglVertexAttribPointer glVertexAttribPointer +#define CNFGglUniform1i glUniform1i +#define CNFGglActiveTexture glActiveTexture + +#endif + +#ifdef __cplusplus +}; +#endif + +#ifdef CNFGOGL_NEED_EXTENSION +#if defined( WIN32 ) || defined( WINDOWS ) || defined( WIN64 ) + +//From https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions +void * CNFGGetProcAddress(const char *name) +{ + void *p = (void *)wglGetProcAddress(name); + if(p == 0 || + (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) || + (p == (void*)-1) ) + { + static HMODULE module; + if( !module ) module = LoadLibraryA("opengl32.dll"); + p = (void *)GetProcAddress(module, name); + } + // We were unable to load the required openGL function + if (!p) { + fprintf(stderr,"[rawdraw][warn]: Unable to load openGL extension \"%s\"\n", name); + } + return p; +} + +#else +#include + + +void * CNFGGetProcAddress(const char *name) +{ + //Tricky use RTLD_NEXT first so we don't accidentally link against ourselves. + void * v1 = dlsym( (void*)((intptr_t)-1) /*RTLD_NEXT = -1*/ /*RTLD_DEFAULT = 0*/, name ); + //printf( "%s = %p\n", name, v1 ); + if( !v1 ) v1 = dlsym( 0, name ); + return v1; +} + +#endif + +// Try and load openGL extension functions required for rawdraw +static void CNFGLoadExtensionsInternal() +{ + CNFGglGetUniformLocation = (CNFGTYPEglGetUniformLocation) CNFGGetProcAddress( "glGetUniformLocation" ); + CNFGglEnableVertexAttribArray = (CNFGTYPEglEnableVertexAttribArray)CNFGGetProcAddress( "glEnableVertexAttribArray" ); + CNFGglUseProgram = (CNFGTYPEglUseProgram)CNFGGetProcAddress( "glUseProgram" ); + CNFGglGetProgramInfoLog = (CNFGTYPEglGetProgramInfoLog)CNFGGetProcAddress( "glGetProgramInfoLog" ); + CNFGglBindAttribLocation = (CNFGTYPEglBindAttribLocation)CNFGGetProcAddress( "glBindAttribLocation" ); + CNFGglGetProgramiv = (CNFGTYPEglGetProgramiv)CNFGGetProcAddress( "glGetProgramiv" ); + CNFGglGetShaderiv = (CNFGTYPEglGetShaderiv)CNFGGetProcAddress( "glGetShaderiv" ); + CNFGglVertexAttribPointer = (CNFGTYPEglVertexAttribPointer)CNFGGetProcAddress( "glVertexAttribPointer" ); + CNFGglCreateShader = (CNFGTYPEglCreateShader)CNFGGetProcAddress( "glCreateShader" ); + CNFGglShaderSource = (CNFGTYPEglShaderSource)CNFGGetProcAddress( "glShaderSource" ); + CNFGglAttachShader = (CNFGTYPEglAttachShader)CNFGGetProcAddress( "glAttachShader" ); + CNFGglCompileShader = (CNFGTYPEglCompileShader)CNFGGetProcAddress( "glCompileShader" ); + CNFGglGetShaderInfoLog = (CNFGTYPEglGetShaderInfoLog)CNFGGetProcAddress( "glGetShaderInfoLog" ); + CNFGglDeleteShader = (CNFGTYPEglDeleteShader)CNFGGetProcAddress( "glDeleteShader" ); + CNFGglLinkProgram = (CNFGTYPEglLinkProgram)CNFGGetProcAddress( "glLinkProgram" ); + CNFGglCreateProgram = (CNFGTYPEglCreateProgram)CNFGGetProcAddress( "glCreateProgram" ); + CNFGglUniform4f = (CNFGTYPEglUniform4f)CNFGGetProcAddress( "glUniform4f" ); + CNFGglUniform1i = (CNFGTYPEglUniform1i)CNFGGetProcAddress( "glUniform1i" ); + CNFGglActiveTexture = (CNFGTYPEglActiveTexture)CNFGGetProcAddress("glActiveTexture"); + + // Check if any of these functions didn't get loaded + uint8_t not_all_functions_loaded = + !CNFGglGetUniformLocation || !CNFGglEnableVertexAttribArray || !CNFGglUseProgram || + !CNFGglGetProgramInfoLog || !CNFGglBindAttribLocation || !CNFGglGetProgramiv || + !CNFGglVertexAttribPointer || !CNFGglCreateShader || !CNFGglShaderSource || + !CNFGglAttachShader || !CNFGglCompileShader || !CNFGglGetShaderInfoLog || + !CNFGglDeleteShader || !CNFGglLinkProgram || !CNFGglCreateProgram || + !CNFGglUniform4f || !CNFGglUniform1i || !CNFGglActiveTexture; + if (not_all_functions_loaded) { + fprintf( + stderr, + "[rawdraw][err]: Unable to load all openGL extensions required for rawdraw\n" + "\tPlease update your graphics drivers or unexpected crashes may occur.\n" + ); + } + + // Give a very stern warning if unable to create or compile shaders + if (!CNFGglCreateShader || !CNFGglCompileShader) { + fprintf( + stderr, + "[rawdraw][err]: Unable to create or compile shaders, this will cause a fatal error if " + "openGL is used.\n" + "\tUpdate your video graphics drivers or switch to software graphics.\n" + ); + } +} +#else +static void CNFGLoadExtensionsInternal() { } +#endif + + + +GLuint gRDShaderProg = -1; +GLuint gRDBlitProg = -1; +GLuint gRDShaderProgUX = -1; +GLuint gRDBlitProgUX = -1; +GLuint gRDBlitProgUT = -1; +GLuint gRDBlitProgTex = -1; +GLuint gRDLastResizeW; +GLuint gRDLastResizeH; + + +GLuint CNFGGLInternalLoadShader( const char * vertex_shader, const char * fragment_shader ) +{ + GLuint fragment_shader_object = 0; + GLuint vertex_shader_object = 0; + GLuint program = 0; + int ret; + + vertex_shader_object = CNFGglCreateShader(GL_VERTEX_SHADER); + if (!vertex_shader_object) { + fprintf( stderr, "Error: glCreateShader(GL_VERTEX_SHADER) " + "failed: 0x%08X\n", glGetError()); + goto fail; + } + + CNFGglShaderSource(vertex_shader_object, 1, &vertex_shader, NULL); + CNFGglCompileShader(vertex_shader_object); + + CNFGglGetShaderiv(vertex_shader_object, GL_COMPILE_STATUS, &ret); + if (!ret) { + fprintf( stderr,"Error: vertex shader compilation failed!\n"); + CNFGglGetShaderiv(vertex_shader_object, GL_INFO_LOG_LENGTH, &ret); + + if (ret > 1) { + //TODO: Refactor to remove malloc reliance. + #ifndef __clang__ + char * log = (char*)alloca(ret); + CNFGglGetShaderInfoLog(vertex_shader_object, ret, NULL, log); + fprintf( stderr, "%s", log); + #endif + } + goto fail; + } + + fragment_shader_object = CNFGglCreateShader(GL_FRAGMENT_SHADER); + if (!fragment_shader_object) { + fprintf( stderr, "Error: glCreateShader(GL_FRAGMENT_SHADER) " + "failed: 0x%08X\n", glGetError()); + goto fail; + } + + CNFGglShaderSource(fragment_shader_object, 1, &fragment_shader, NULL); + CNFGglCompileShader(fragment_shader_object); + + CNFGglGetShaderiv(fragment_shader_object, GL_COMPILE_STATUS, &ret); + if (!ret) { + fprintf( stderr, "Error: fragment shader compilation failed!\n"); + CNFGglGetShaderiv(fragment_shader_object, GL_INFO_LOG_LENGTH, &ret); + + if (ret > 1) { + //TODO: Refactor to remove malloc reliance. + #ifndef __clang__ + char * log = (char*)malloc(ret); + CNFGglGetShaderInfoLog(fragment_shader_object, ret, NULL, log); + fprintf( stderr, "%s", log); + free( log ); + #endif + } + goto fail; + } + + program = CNFGglCreateProgram(); + if (!program) { + fprintf( stderr, "Error: failed to create program!\n"); + goto fail; + } + + CNFGglAttachShader(program, vertex_shader_object); + CNFGglAttachShader(program, fragment_shader_object); + + CNFGglBindAttribLocation(program, 0, "a0"); + CNFGglBindAttribLocation(program, 1, "a1"); + + CNFGglLinkProgram(program); + + CNFGglGetProgramiv(program, GL_LINK_STATUS, &ret); + if (!ret) { + fprintf( stderr, "Error: program linking failed!\n"); + CNFGglGetProgramiv(program, GL_INFO_LOG_LENGTH, &ret); + + if (ret > 1) { + //TODO: Refactor to remove malloc reliance. + #ifndef __clang__ + char *log = (char*)alloca(ret); + CNFGglGetProgramInfoLog(program, ret, NULL, log); + fprintf( stderr, "%s", log); + #endif + } + goto fail; + } + return program; +fail: + if( !vertex_shader_object ) CNFGglDeleteShader( vertex_shader_object ); + if( !fragment_shader_object ) CNFGglDeleteShader( fragment_shader_object ); + if( !program ) CNFGglDeleteShader( program ); + return -1; +} + +#if defined( CNFGEWGL ) && !defined( CNFG_NO_PRECISION ) +#define PRECISIONA "lowp" +#define PRECISIONB "mediump" +#else +#define PRECISIONA +#define PRECISIONB +#endif + +void CNFGSetupBatchInternal() +{ + short w, h; + + CNFGLoadExtensionsInternal(); + + CNFGGetDimensions( &w, &h ); + + gRDShaderProg = CNFGGLInternalLoadShader( + "uniform vec4 xfrm;" + "attribute vec3 a0;" + "attribute vec4 a1;" + "varying " PRECISIONA " vec4 vc;" + "void main() { gl_Position = vec4( a0.xy*xfrm.xy+xfrm.zw, a0.z, 0.5 ); vc = a1; }", + + "varying " PRECISIONA " vec4 vc;" + "void main() { gl_FragColor = vec4(vc.abgr); }" + ); + + CNFGglUseProgram( gRDShaderProg ); + gRDShaderProgUX = CNFGglGetUniformLocation ( gRDShaderProg , "xfrm" ); + + + gRDBlitProg = CNFGGLInternalLoadShader( + "uniform vec4 xfrm;" + "attribute vec3 a0;" + "attribute vec4 a1;" + "varying " PRECISIONB " vec2 tc;" + "void main() { gl_Position = vec4( a0.xy*xfrm.xy+xfrm.zw, a0.z, 0.5 ); tc = a1.xy; }", + + "varying " PRECISIONB " vec2 tc;" + "uniform sampler2D tex;" + "void main() { gl_FragColor = texture2D(tex,tc)." + +#if !defined( CNFGRASTERIZER ) +"wzyx" +#else +"wxyz" +#endif +";}" ); + + CNFGglUseProgram( gRDBlitProg ); + gRDBlitProgUX = CNFGglGetUniformLocation ( gRDBlitProg , "xfrm" ); + gRDBlitProgUT = CNFGglGetUniformLocation ( gRDBlitProg , "tex" ); + glGenTextures( 1, &gRDBlitProgTex ); + + CNFGglEnableVertexAttribArray(0); + CNFGglEnableVertexAttribArray(1); + + glDisable(GL_DEPTH_TEST); + glDepthMask( GL_FALSE ); + glEnable( GL_BLEND ); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + CNFGVertPlace = 0; +} + +#ifndef CNFGRASTERIZER +void CNFGInternalResize(short x, short y) +#else +void CNFGInternalResizeOGLBACKEND(short x, short y) +#endif +{ + glViewport( 0, 0, x, y ); + gRDLastResizeW = x; + gRDLastResizeH = y; + if (gRDShaderProg == 0xFFFFFFFF) { return; } // Prevent trying to set uniform if the shader isn't ready yet. + CNFGglUseProgram( gRDShaderProg ); + CNFGglUniform4f( gRDShaderProgUX, 1.f/x, -1.f/y, -0.5f, 0.5f); +} + +void CNFGEmitBackendTriangles( const float * vertices, const uint32_t * colors, int num_vertices ) +{ + CNFGglUseProgram( gRDShaderProg ); + CNFGglUniform4f( gRDShaderProgUX, 1.f/gRDLastResizeW, -1.f/gRDLastResizeH, -0.5f, 0.5f); + CNFGglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices); + CNFGglVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors); + glDrawArrays( GL_TRIANGLES, 0, num_vertices); +} + + +#ifdef CNFGOGL +// this is here, so people don't have to include opengl +void CNFGDeleteTex( unsigned int tex ) +{ + glDeleteTextures(1, &tex); +} + +unsigned int CNFGTexImage( uint32_t *data, int w, int h ) +{ + GLuint tex; + + glGenTextures(1, &tex); + glEnable( GL_TEXTURE_2D ); + CNFGglActiveTexture( 0 ); + glBindTexture( GL_TEXTURE_2D, tex ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, + GL_UNSIGNED_BYTE, data ); + + return (unsigned int)tex; +} + +void CNFGBlitTex( unsigned int tex, int x, int y, int w, int h ) +{ + if( w == 0 || h == 0 ) return; + + CNFGFlushRender(); + + CNFGglUseProgram( gRDBlitProg ); + CNFGglUniform4f( gRDBlitProgUX, + 1.f/gRDLastResizeW, -1.f/gRDLastResizeH, + -0.5f+x/(float)gRDLastResizeW, 0.5f-y/(float)gRDLastResizeH ); + CNFGglUniform1i( gRDBlitProgUT, 0 ); + + glBindTexture(GL_TEXTURE_2D, tex); + + const float verts[] = { + 0,0, (float)w,0, (float)w,(float)h, + 0,0, (float)w,(float)h, 0,(float)h, }; + static const uint8_t tex_verts[] = { + 0,0, 255,0, 255,255, + 0,0, 255,255, 0,255 }; + + CNFGglVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts); + CNFGglVertexAttribPointer(1, 2, GL_UNSIGNED_BYTE, GL_TRUE, 0, tex_verts); + + glDrawArrays( GL_TRIANGLES, 0, 6); +} +#endif + +#ifdef CNFGRASTERIZER +void CNFGBlitImageInternal( uint32_t * data, int x, int y, int w, int h ) +#else +void CNFGBlitImage( uint32_t * data, int x, int y, int w, int h ) +#endif +{ + glEnable( GL_TEXTURE_2D ); + CNFGglActiveTexture( 0 ); + glBindTexture( GL_TEXTURE_2D, gRDBlitProgTex ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, + GL_UNSIGNED_BYTE, data ); + + CNFGBlitTex( gRDBlitProgTex, x, y, w, h ); +} + +void CNFGUpdateScreenWithBitmap( uint32_t * data, int w, int h ) +{ +#ifdef CNFGRASTERIZER + CNFGBlitImageInternal( data, 0, 0, w, h ); + void CNFGSwapBuffersInternal(); + CNFGSwapBuffersInternal(); +#else + CNFGBlitImage( data, 0, 0, w, h ); +#endif +} + +#ifndef CNFGRASTERIZER + +void CNFGFlushRender() +{ + if( !CNFGVertPlace ) return; + CNFGEmitBackendTriangles( CNFGVertDataV, CNFGVertDataC, CNFGVertPlace ); + CNFGVertPlace = 0; +} + +void CNFGClearFrame() +{ + glClearColor( ((CNFGBGColor&0xff000000)>>24)/255.0f, + ((CNFGBGColor&0xff0000)>>16)/255.0f, + (CNFGBGColor&0xff00)/65280.0f, + (CNFGBGColor&0xff)/255.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); +} + +#endif + +#endif //__wasm__ + +#else + +void CNFGFlushRender() { } + +#endif + + +#endif +#endif // CNFGHTTPSERVERONLY +#endif //_CNFG_C + + +#ifdef CNFG3D +//Copyright 2012-2017 <>< Charles Lohr +//You may license this file under the MIT/x11, NewBSD, or any GPL license. +//This is a series of tools useful for software rendering. +//Use of this file with OpenGL is untested. + +#ifdef CNFG3D + + +#ifdef __wasm__ +double sin( double v ); +double cos( double v ); +double tan( double v ); +double sqrt( double v ); +float sinf( float v ); +float cosf( float v ); +float tanf( float v ); +float sqrtf( float v ); +void tdMATCOPY( float * x, const float * y ) +{ + int i; + for( i = 0; i < 16; i++ ) x[i] = y[i]; +} +#else +#include +#include +#endif + +#ifdef CNFG3D_USE_OGL_MAJOR +#define m00 0 +#define m10 1 +#define m20 2 +#define m30 3 +#define m01 4 +#define m11 5 +#define m21 6 +#define m31 7 +#define m02 8 +#define m12 9 +#define m22 10 +#define m32 11 +#define m03 12 +#define m13 13 +#define m23 14 +#define m33 15 +#else +#define m00 0 +#define m01 1 +#define m02 2 +#define m03 3 +#define m10 4 +#define m11 5 +#define m12 6 +#define m13 7 +#define m20 8 +#define m21 9 +#define m22 10 +#define m23 11 +#define m30 12 +#define m31 13 +#define m32 14 +#define m33 15 +#endif + +void tdIdentity( float * f ) +{ + f[m00] = 1; f[m01] = 0; f[m02] = 0; f[m03] = 0; + f[m10] = 0; f[m11] = 1; f[m12] = 0; f[m13] = 0; + f[m20] = 0; f[m21] = 0; f[m22] = 1; f[m23] = 0; + f[m30] = 0; f[m31] = 0; f[m32] = 0; f[m33] = 1; +} + +void tdZero( float * f ) +{ + f[m00] = 0; f[m01] = 0; f[m02] = 0; f[m03] = 0; + f[m10] = 0; f[m11] = 0; f[m12] = 0; f[m13] = 0; + f[m20] = 0; f[m21] = 0; f[m22] = 0; f[m23] = 0; + f[m30] = 0; f[m31] = 0; f[m32] = 0; f[m33] = 0; +} + +void tdTranslate( float * f, float x, float y, float z ) +{ + float ftmp[16]; + tdIdentity(ftmp); + ftmp[m03] += x; + ftmp[m13] += y; + ftmp[m23] += z; + tdMultiply( f, ftmp, f ); +} + +void tdScale( float * f, float x, float y, float z ) +{ +#if 0 + f[m00] *= x; + f[m01] *= x; + f[m02] *= x; + f[m03] *= x; + + f[m10] *= y; + f[m11] *= y; + f[m12] *= y; + f[m13] *= y; + + f[m20] *= z; + f[m21] *= z; + f[m22] *= z; + f[m23] *= z; +#endif + + float ftmp[16]; + tdIdentity(ftmp); + ftmp[m00] *= x; + ftmp[m11] *= y; + ftmp[m22] *= z; + + tdMultiply( f, ftmp, f ); + +} + +void tdRotateAA( float * f, float angle, float ix, float iy, float iz ) +{ + float ftmp[16]; + + float c = tdCOS( angle*tdDEGRAD ); + float s = tdSIN( angle*tdDEGRAD ); + float absin = tdSQRT( ix*ix + iy*iy + iz*iz ); + float x = ix/absin; + float y = iy/absin; + float z = iz/absin; + + ftmp[m00] = x*x*(1-c)+c; + ftmp[m01] = x*y*(1-c)-z*s; + ftmp[m02] = x*z*(1-c)+y*s; + ftmp[m03] = 0; + + ftmp[m10] = y*x*(1-c)+z*s; + ftmp[m11] = y*y*(1-c)+c; + ftmp[m12] = y*z*(1-c)-x*s; + ftmp[m13] = 0; + + ftmp[m20] = x*z*(1-c)-y*s; + ftmp[m21] = y*z*(1-c)+x*s; + ftmp[m22] = z*z*(1-c)+c; + ftmp[m23] = 0; + + ftmp[m30] = 0; + ftmp[m31] = 0; + ftmp[m32] = 0; + ftmp[m33] = 1; + + tdMultiply( f, ftmp, f ); +} + +void tdRotateQuat( float * f, float qw, float qx, float qy, float qz ) +{ + float ftmp[16]; + //float qw2 = qw*qw; + float qx2 = qx*qx; + float qy2 = qy*qy; + float qz2 = qz*qz; + + ftmp[m00] = 1 - 2*qy2 - 2*qz2; + ftmp[m01] = 2*qx*qy - 2*qz*qw; + ftmp[m02] = 2*qx*qz + 2*qy*qw; + ftmp[m03] = 0; + + ftmp[m10] = 2*qx*qy + 2*qz*qw; + ftmp[m11] = 1 - 2*qx2 - 2*qz2; + ftmp[m12] = 2*qy*qz - 2*qx*qw; + ftmp[m13] = 0; + + ftmp[m20] = 2*qx*qz - 2*qy*qw; + ftmp[m21] = 2*qy*qz + 2*qx*qw; + ftmp[m22] = 1 - 2*qx2 - 2*qy2; + ftmp[m23] = 0; + + ftmp[m30] = 0; + ftmp[m31] = 0; + ftmp[m32] = 0; + ftmp[m33] = 1; + + tdMultiply( f, ftmp, f ); + +} + +void tdRotateEA( float * f, float x, float y, float z ) +{ + float ftmp[16]; + + //x,y,z must be negated for some reason + float X = -x*2*tdQ_PI/360; //Reduced calulation for speed + float Y = -y*2*tdQ_PI/360; + float Z = -z*2*tdQ_PI/360; + float cx = tdCOS(X); + float sx = tdSIN(X); + float cy = tdCOS(Y); + float sy = tdSIN(Y); + float cz = tdCOS(Z); + float sz = tdSIN(Z); + + //Row major (unless CNFG3D_USE_OGL_MAJOR is selected) + //manually transposed + ftmp[m00] = cy*cz; + ftmp[m10] = (sx*sy*cz)-(cx*sz); + ftmp[m20] = (cx*sy*cz)+(sx*sz); + ftmp[m30] = 0; + + ftmp[m01] = cy*sz; + ftmp[m11] = (sx*sy*sz)+(cx*cz); + ftmp[m21] = (cx*sy*sz)-(sx*cz); + ftmp[m31] = 0; + + ftmp[m02] = -sy; + ftmp[m12] = sx*cy; + ftmp[m22] = cx*cy; + ftmp[m32] = 0; + + ftmp[m03] = 0; + ftmp[m13] = 0; + ftmp[m23] = 0; + ftmp[m33] = 1; + + tdMultiply( f, ftmp, f ); +} + +void tdMultiply( float * fin1, float * fin2, float * fout ) +{ + float fotmp[16]; + int i, k; +#ifdef CNFG3D_USE_OGL_MAJOR + fotmp[m00] = fin1[m00] * fin2[m00] + fin1[m01] * fin2[m10] + fin1[m02] * fin2[m20] + fin1[m03] * fin2[m30]; + fotmp[m01] = fin1[m00] * fin2[m01] + fin1[m01] * fin2[m11] + fin1[m02] * fin2[m21] + fin1[m03] * fin2[m31]; + fotmp[m02] = fin1[m00] * fin2[m02] + fin1[m01] * fin2[m12] + fin1[m02] * fin2[m22] + fin1[m03] * fin2[m32]; + fotmp[m03] = fin1[m00] * fin2[m03] + fin1[m01] * fin2[m13] + fin1[m02] * fin2[m23] + fin1[m03] * fin2[m33]; + + fotmp[m10] = fin1[m10] * fin2[m00] + fin1[m11] * fin2[m10] + fin1[m12] * fin2[m20] + fin1[m13] * fin2[m30]; + fotmp[m11] = fin1[m10] * fin2[m01] + fin1[m11] * fin2[m11] + fin1[m12] * fin2[m21] + fin1[m13] * fin2[m31]; + fotmp[m12] = fin1[m10] * fin2[m02] + fin1[m11] * fin2[m12] + fin1[m12] * fin2[m22] + fin1[m13] * fin2[m32]; + fotmp[m13] = fin1[m10] * fin2[m03] + fin1[m11] * fin2[m13] + fin1[m12] * fin2[m23] + fin1[m13] * fin2[m33]; + + fotmp[m20] = fin1[m20] * fin2[m00] + fin1[m21] * fin2[m10] + fin1[m22] * fin2[m20] + fin1[m23] * fin2[m30]; + fotmp[m21] = fin1[m20] * fin2[m01] + fin1[m21] * fin2[m11] + fin1[m22] * fin2[m21] + fin1[m23] * fin2[m31]; + fotmp[m22] = fin1[m20] * fin2[m02] + fin1[m21] * fin2[m12] + fin1[m22] * fin2[m22] + fin1[m23] * fin2[m32]; + fotmp[m23] = fin1[m20] * fin2[m03] + fin1[m21] * fin2[m13] + fin1[m22] * fin2[m23] + fin1[m23] * fin2[m33]; + + fotmp[m30] = fin1[m30] * fin2[m00] + fin1[m31] * fin2[m10] + fin1[m32] * fin2[m20] + fin1[m33] * fin2[m30]; + fotmp[m31] = fin1[m30] * fin2[m01] + fin1[m31] * fin2[m11] + fin1[m32] * fin2[m21] + fin1[m33] * fin2[m31]; + fotmp[m32] = fin1[m30] * fin2[m02] + fin1[m31] * fin2[m12] + fin1[m32] * fin2[m22] + fin1[m33] * fin2[m32]; + fotmp[m33] = fin1[m30] * fin2[m03] + fin1[m31] * fin2[m13] + fin1[m32] * fin2[m23] + fin1[m33] * fin2[m33]; +#else + for( i = 0; i < 16; i++ ) + { + int xp = i & 0x03; + int yp = i & 0x0c; + fotmp[i] = 0; + for( k = 0; k < 4; k++ ) + { + fotmp[i] += fin1[yp+k] * fin2[(k<<2)|xp]; + } + } +#endif + tdMATCOPY( fout, fotmp ); +} + +#ifndef __wasm__ +void tdPrint( const float * f ) +{ + int i; + printf( "{\n" ); +#ifdef CNFG3D_USE_OGL_MAJOR + for( i = 0; i < 4; i++ ) + { + printf( " %f, %f, %f, %f\n", f[0+i], f[4+i], f[8+i], f[12+i] ); + } +#else + for( i = 0; i < 16; i+=4 ) + { + printf( " %f, %f, %f, %f\n", f[0+i], f[1+i], f[2+i], f[3+i] ); + } +#endif + printf( "}\n" ); +} +#endif + +void tdTransposeSelf( float * f ) +{ + float fout[16]; + fout[m00] = f[m00]; fout[m01] = f[m10]; fout[m02] = f[m20]; fout[m03] = f[m30]; + fout[m10] = f[m01]; fout[m11] = f[m11]; fout[m12] = f[m21]; fout[m13] = f[m31]; + fout[m20] = f[m02]; fout[m21] = f[m12]; fout[m22] = f[m22]; fout[m23] = f[m32]; + fout[m30] = f[m03]; fout[m31] = f[m13]; fout[m32] = f[m23]; fout[m33] = f[m33]; + tdMATCOPY( f, fout ); +} + + +void tdPerspective( float fovy, float aspect, float zNear, float zFar, float * out ) +{ + float f = 1./tdTAN(fovy * tdQ_PI / 360.0); + out[m00] = f/aspect; out[m01] = 0; out[m02] = 0; out[m03] = 0; + out[m10] = 0; out[m11] = f; out[m12] = 0; out[m13] = 0; + out[m20] = 0; out[m21] = 0; + out[m22] = (zFar + zNear)/(zNear - zFar); + out[m23] = 2*zFar*zNear /(zNear - zFar); + out[m30] = 0; out[m31] = 0; out[m32] = -1; out[m33] = 0; +} + +void tdLookAt( float * m, float * eye, float * at, float * up ) +{ + float out[16]; + float F[3] = { at[0] - eye[0], at[1] - eye[1], at[2] - eye[2] }; + float fdiv = 1./tdSQRT( F[0]*F[0] + F[1]*F[1] + F[2]*F[2] ); + float f[3] = { F[0]*fdiv, F[1]*fdiv, F[2]*fdiv }; + float udiv = 1./tdSQRT( up[0]*up[0] + up[1]*up[1] + up[2]*up[2] ); + float UP[3] = { up[0]*udiv, up[1]*udiv, up[2]*udiv }; + float s[3]; + float u[3]; + tdCross( f, UP, s ); + tdCross( s, f, u ); + + out[m00] = s[0]; out[m01] = s[1]; out[m02] = s[2]; out[m03] = 0; + out[m10] = u[0]; out[m11] = u[1]; out[m12] = u[2]; out[m13] = 0; + out[m20] = -f[0];out[m21] =-f[1]; out[m22] =-f[2]; out[m23] = 0; + out[m30] = 0; out[m31] = 0; out[m32] = 0; out[m33] = 1; + + tdMultiply( m, out, m ); + tdTranslate( m, -eye[0], -eye[1], -eye[2] ); +} + + + + + + + + + + + + +void tdPTransform( const float * pin, float * f, float * pout ) +{ + float ptmp[2]; + ptmp[0] = pin[0] * f[m00] + pin[1] * f[m01] + pin[2] * f[m02] + f[m03]; + ptmp[1] = pin[0] * f[m10] + pin[1] * f[m11] + pin[2] * f[m12] + f[m13]; + pout[2] = pin[0] * f[m20] + pin[1] * f[m21] + pin[2] * f[m22] + f[m23]; + pout[0] = ptmp[0]; + pout[1] = ptmp[1]; +} + +void tdVTransform( const float * pin, float * f, float * pout ) +{ + float ptmp[2]; + ptmp[0] = pin[0] * f[m00] + pin[1] * f[m01] + pin[2] * f[m02]; + ptmp[1] = pin[0] * f[m10] + pin[1] * f[m11] + pin[2] * f[m12]; + pout[2] = pin[0] * f[m20] + pin[1] * f[m21] + pin[2] * f[m22]; + pout[0] = ptmp[0]; + pout[1] = ptmp[1]; +} + +void td4Transform( float * pin, float * f, float * pout ) +{ + float ptmp[3]; + ptmp[0] = pin[0] * f[m00] + pin[1] * f[m01] + pin[2] * f[m02] + pin[3] * f[m03]; + ptmp[1] = pin[0] * f[m10] + pin[1] * f[m11] + pin[2] * f[m12] + pin[3] * f[m13]; + ptmp[2] = pin[0] * f[m20] + pin[1] * f[m21] + pin[2] * f[m22] + pin[3] * f[m23]; + pout[3] = pin[0] * f[m30] + pin[1] * f[m31] + pin[2] * f[m32] + pin[3] * f[m33]; + pout[0] = ptmp[0]; + pout[1] = ptmp[1]; + pout[2] = ptmp[2]; +} + +void td4RTransform( float * pin, float * f, float * pout ) +{ + float ptmp[3]; + ptmp[0] = pin[0] * f[m00] + pin[1] * f[m10] + pin[2] * f[m20] + pin[3] * f[m30]; + ptmp[1] = pin[0] * f[m01] + pin[1] * f[m11] + pin[2] * f[m21] + pin[3] * f[m31]; + ptmp[2] = pin[0] * f[m02] + pin[1] * f[m12] + pin[2] * f[m22] + pin[3] * f[m32]; + pout[3] = pin[0] * f[m03] + pin[1] * f[m13] + pin[2] * f[m23] + pin[3] * f[m33]; + pout[0] = ptmp[0]; + pout[1] = ptmp[1]; + pout[2] = ptmp[2]; +} + +void tdNormalizeSelf( float * vin ) +{ + float vsq = 1./tdSQRT(vin[0]*vin[0] + vin[1]*vin[1] + vin[2]*vin[2]); + vin[0] *= vsq; + vin[1] *= vsq; + vin[2] *= vsq; +} + +void tdCross( float * va, float * vb, float * vout ) +{ + float vtmp[2]; + vtmp[0] = va[1] * vb[2] - va[2] * vb[1]; + vtmp[1] = va[2] * vb[0] - va[0] * vb[2]; + vout[2] = va[0] * vb[1] - va[1] * vb[0]; + vout[0] = vtmp[0]; + vout[1] = vtmp[1]; +} + +float tdDistance( float * va, float * vb ) +{ + float dx = va[0]-vb[0]; + float dy = va[1]-vb[1]; + float dz = va[2]-vb[2]; + + return tdSQRT(dx*dx + dy*dy + dz*dz); +} + +float tdDot( float * va, float * vb ) +{ + return va[0]*vb[0] + va[1]*vb[1] + va[2]*vb[2]; +} + +//Stack functionality. +static float gsMatricies[2][tdMATRIXMAXDEPTH][16]; +float * gSMatrix = gsMatricies[0][0]; +static int gsMMode; +static int gsMPlace[2]; + +void tdPush() +{ + if( gsMPlace[gsMMode] > tdMATRIXMAXDEPTH - 2 ) + return; + + tdMATCOPY( gsMatricies[gsMMode][gsMPlace[gsMMode] + 1], gsMatricies[gsMMode][gsMPlace[gsMMode]] ); + gsMPlace[gsMMode]++; + + gSMatrix = gsMatricies[gsMMode][gsMPlace[gsMMode]]; +} + +void tdPop() +{ + if( gsMPlace[gsMMode] < 1 ) + return; + + gsMPlace[gsMMode]--; + + gSMatrix = gsMatricies[gsMMode][gsMPlace[gsMMode]]; + +} + +void tdMode( int mode ) +{ + if( mode < 0 || mode > 1 ) + return; + + gsMMode = mode; + + gSMatrix = gsMatricies[gsMMode][gsMPlace[gsMMode]]; + +} + +static float translateX; +static float translateY; +static float scaleX; +static float scaleY; + +void tdSetViewport( float leftx, float topy, float rightx, float bottomy, float pixx, float pixy ) +{ + translateX = leftx; + translateY = bottomy; + scaleX = pixx/(rightx-leftx); + scaleY = pixy/(topy-bottomy); + +} + +void tdFinalPoint( float * pin, float * pout ) +{ + float tdin[4] = { pin[0], pin[1], pin[2], 1. }; + float tmp[4]; + td4Transform( tdin, gsMatricies[0][gsMPlace[0]], tmp ); +// printf( "XFORM1Out: %f %f %f %f\n", tmp[0], tmp[1], tmp[2], tmp[3] ); + td4Transform( tmp, gsMatricies[1][gsMPlace[1]], tmp ); +// printf( "XFORM2Out: %f %f %f %f\n", tmp[0], tmp[1], tmp[2], tmp[3] ); + pout[0] = (tmp[0]/tmp[3] - translateX) * scaleX; + pout[1] = (tmp[1]/tmp[3] - translateY) * scaleY; + pout[2] = tmp[2]/tmp[3]; +// printf( "XFORMFOut: %f %f %f\n", pout[0], pout[1], pout[2] ); +} + + + + + + + + + + +float tdNoiseAt( int x, int y ) +{ + return ((x*13241*y + y * 33455927)%9293) / 4646. - 1.0; +} + +static inline float tdFade( float f ) +{ + float ft3 = f*f*f; + return ft3 * 10 - ft3 * f * 15 + 6 * ft3 * f * f; +} + +float tdFLerp( float a, float b, float t ) +{ + float fr = tdFade( t ); + return a * (1.-fr) + b * fr; +} + +static inline float tdFNoiseAt( float x, float y ) +{ + int ix = x; + int iy = y; + float fx = x - ix; + float fy = y - iy; + + float a = tdNoiseAt( ix, iy ); + float b = tdNoiseAt( ix+1, iy ); + float c = tdNoiseAt( ix, iy+1 ); + float d = tdNoiseAt( ix+1, iy+1 ); + + float top = tdFLerp( a, b, fx ); + float bottom = tdFLerp( c, d, fx ); + + return tdFLerp( top, bottom, fy ); +} + +float tdPerlin2D( float x, float y ) +{ + int ndepth = 5; + + int depth; + float ret = 0; + for( depth = 0; depth < ndepth; depth++ ) + { + float nx = x / (1<<(ndepth-depth-1)); + float ny = y / (1<<(ndepth-depth-1)); + ret += tdFNoiseAt( nx, ny ) / (1<<(depth+1)); + } + return ret; +} + +#endif + +#endif + + +#endif + +#endif + + diff --git a/app/src/nativecode/rawdraw/simple.c b/app/src/nativecode/rawdraw/simple.c new file mode 100644 index 0000000..5e72d31 --- /dev/null +++ b/app/src/nativecode/rawdraw/simple.c @@ -0,0 +1,62 @@ +//Make it so we don't need to include any other C files in our build. +#define CNFG_IMPLEMENTATION + +//Optional: Use OpenGL to do rendering on appropriate systems. +#define CNFGOGL + +#include "CNFG.h" + +void HandleKey( int keycode, int bDown ) { } +void HandleButton( int x, int y, int button, int bDown ) { } +void HandleMotion( int x, int y, int mask ) { } +int HandleDestroy() { return 0; } + +int main() +{ + CNFGSetup( "Example App", 1024, 768 ); + + while( CNFGHandleInput() ) + { + CNFGBGColor = 0x000080ff; //Dark Blue Background + + short w, h; + CNFGClearFrame(); + CNFGGetDimensions( &w, &h ); + + //Change color to white. + CNFGColor( 0xffffffff ); + + CNFGPenX = 1; CNFGPenY = 1; + CNFGDrawText( "Hello, World", 2 ); + //Draw a white pixel at 3,0 30 + CNFGTackPixel( 30, 30 ); + + //Draw a line from 50,50 to 100,50 + CNFGTackSegment( 50, 50, 100, 50 ); + + //Dark Red Color Select + CNFGColor( 0x800000ff ); + + //Draw 50x50 box starting at 100,50 + CNFGTackRectangle( 100, 50, 150, 100 ); + + //Draw a triangle + RDPoint points[3] = { { 30, 36 }, { 20, 50 }, { 40, 50 } }; + CNFGTackPoly( points, 3 ); + + // Blit random pixel data + { + static uint32_t data[64*64]; + int x, y; + + for( y = 0; y < 64; y++ ) for( x = 0; x < 64; x++ ) + data[x+y*64] = 0xff | (rand()<<8); + + CNFGBlitImage( data, 120, 190, 64, 64 ); + } + + //Display the image and wait for time to display next frame. + CNFGSwapBuffers(); + } +} + diff --git a/app/src/nativecode/rawdraw/tccbuild.bat b/app/src/nativecode/rawdraw/tccbuild.bat new file mode 100644 index 0000000..9a85ec9 --- /dev/null +++ b/app/src/nativecode/rawdraw/tccbuild.bat @@ -0,0 +1,3 @@ +set TCC=C:\tcc\tcc + +%TCC% rawdraw.c -o rawdraw.exe -luser32 -lgdi32 -lopengl32 C:/windows/system32/msvcrt.dll \ No newline at end of file diff --git a/app/src/nativecode/rawdraw/tools/binary_to_buffer.c b/app/src/nativecode/rawdraw/tools/binary_to_buffer.c new file mode 100644 index 0000000..9c243a0 --- /dev/null +++ b/app/src/nativecode/rawdraw/tools/binary_to_buffer.c @@ -0,0 +1,24 @@ +#include + +int main( int argc, char ** argv ) +{ + if( argc != 2 ) + { + fprintf( stderr, "Error: need a symbol name\n" ); + return -1; + } + int c; + int count = 0; + printf( "unsigned char %s[] = {", argv[1] ); + while( ( c = getchar() ) != EOF ) + { + if( ( count & 0x1f ) == 0 ) + { + printf( "\n\t" ); + } + printf( "0x%02x, ", c ); + count++; + } + printf( "};\n" ); + +} diff --git a/app/src/nativecode/rawdraw/tools/rawdraw_http_files/index.html b/app/src/nativecode/rawdraw/tools/rawdraw_http_files/index.html new file mode 100644 index 0000000..4363dcb --- /dev/null +++ b/app/src/nativecode/rawdraw/tools/rawdraw_http_files/index.html @@ -0,0 +1,546 @@ + + + + + cnhttp + + + +
+ +
+
+ + + diff --git a/app/src/nativecode/rawdraw/tools/single_file_creator.c b/app/src/nativecode/rawdraw/tools/single_file_creator.c new file mode 100644 index 0000000..8c608bb --- /dev/null +++ b/app/src/nativecode/rawdraw/tools/single_file_creator.c @@ -0,0 +1,86 @@ +#include +#include +#include + +char * hitfiles; + +char * OSGLineFromFile( FILE * f ) +{ + int c; + int mlen = 128; + int len = 0; + + if( !f || ferror(f) || feof( f ) ) return 0; + + char * ret = malloc( mlen ); + + while( 1 ) + { + c = fgetc( f ); + if( c == EOF || c == 0 || c == '\n' ) break; + if( c == '\r' ) continue; + + ret[len++] = c; + if( len >= mlen-1 ) + { + mlen += 128; + ret = realloc( ret, mlen ); + } + } + ret[len] = 0; + return ret; +} + +void IncludeFile( char * filename, char ** includes, int nrinc ) +{ + FILE * f = fopen( filename, "rb" ); + if( !f ) + { + fprintf( stderr, "Error: Could not open %s\n", filename ); + exit( -5 ); + } + while( !feof( f ) ) + { + char * line = OSGLineFromFile( f ); + if( line ) + { + int match = 0; + if( strncmp( line, "#include \"", 10 ) == 0 ) + { + int i; + for( i = 0; i < nrinc; i++ ) + { + if( strncmp( line + 10, includes[i], strlen( includes[i] ) ) == 0 ) + { + if( !hitfiles[i] ) + { + IncludeFile( includes[i], includes, nrinc ); + hitfiles[i] = 0; + } + match = 1; + } + } + } + if( !match ) + { + puts( line ); + } + } + } +} + +int main( int argc, char ** argv ) +{ + if( argc < 3 ) + { + fprintf( stderr, "Error: Usage: single_file_creator {file to turn into a single file} {list of files to include}\n" ); + return -5; + } + + hitfiles = malloc( argc ); + memset( hitfiles, 0, sizeof(hitfiles) ); + hitfiles[0] = 1; + IncludeFile( argv[1], argv + 1, argc - 1 ); + + return 0; +} diff --git a/app/src/nativecode/rawdraw/wasm/.gitignore b/app/src/nativecode/rawdraw/wasm/.gitignore new file mode 100644 index 0000000..13c78bc --- /dev/null +++ b/app/src/nativecode/rawdraw/wasm/.gitignore @@ -0,0 +1,2 @@ +index.html +node_modules \ No newline at end of file diff --git a/app/src/nativecode/rawdraw/wasm/Makefile b/app/src/nativecode/rawdraw/wasm/Makefile new file mode 100644 index 0000000..cf6051e --- /dev/null +++ b/app/src/nativecode/rawdraw/wasm/Makefile @@ -0,0 +1,46 @@ +all : subst index.html + +#For tools (Works in Ubuntu 20.04 (Including WSL), Mint) +# sudo apt-get install clang-10 lld-10 binaryen +# sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-10 20 +# sudo update-alternatives --install /usr/bin/wasm-ld wasm-ld /usr/bin/wasm-ld-10 20 + +#node-uglify lld clang-10 lld-10 binaryen + +#Path to rawdraw +CFLAGS:=-I.. + +CLANG?=clang +WASMOPT?=wasm-opt +TERSER?=terser +TERSERFLAGS?= --ecma 2017 -d RAWDRAW_USE_LOOP_FUNCTION=false -d RAWDRAW_NEED_BLITTER=true + +CFLAGS+=-DWASM -nostdlib --target=wasm32 \ + -flto -Oz \ + -Wl,--lto-O3 \ + -Wl,--no-entry \ + -Wl,--allow-undefined \ + -Wl,--import-memory + +WOFLAGS:=--asyncify --pass-arg=asyncify-imports@bynsyncify.* --pass-arg=asyncify-ignore-indirect + +opt.js : template.js main.wasm + cat main.wasm | base64 | sed -e "$$ ! {/./s/$$/ \\\\/}" > blob_b64; + ./subst template.js -s -f BLOB blob_b64 -o mid.js + #Comment the below line out if you don't want to uglify the javascript. + $(TERSER) $(TERSERFLAGS) mid.js -o opt.js + rm mid.js blob_b64 + +index.html : template.ht opt.js + ./subst template.ht -s -f JAVASCRIPT_DATA opt.js -o $@ + +subst : subst.c + cc -o $@ $^ + +main.wasm: rawdraw.c + $(CLANG) $(CFLAGS) $^ -o $@ + $(WASMOPT) $(WOFLAGS) -Oz main.wasm -o main.wasm + #wasm-objdump -d main.wasm > main.disassembly.txt + +clean: + rm -rf main.wasm opt.js index.html blob_b64 diff --git a/app/src/nativecode/rawdraw/wasm/README.md b/app/src/nativecode/rawdraw/wasm/README.md new file mode 100644 index 0000000..d05b719 --- /dev/null +++ b/app/src/nativecode/rawdraw/wasm/README.md @@ -0,0 +1,93 @@ +# rawdrawwasm + +rawdrawwasm + +## Prerequisites + +``` +sudo apt-get install npm binaryen clang llvm lld +sudo npm install terser -g +``` + +You may need to add llvm bins to your path, i.e. + +``` +export PATH=$PATH:/usr/lib/llvm-10/bin +``` + +Note also: clang. llvm, lld-12 has also been tested and works. + +Depending on your llvm version. + + +## Old Notes + +My stab at rawdraw on wasm. This is based on: + https://github.com/zNoctum/wasm-tool +and has inspiration from: + https://github.com/cnlohr/wasm_integrated + +Lots of stuff todo, hopefully this will get rolled into main rawdraw. + +TODO notes for now: + * hook up button presses, and mouse input and destroy + * hook up sleep in a more meaningful way maybe? + * Make printf work. + * Make libc work. + * Figure out how to #include math.h + * Come up with guide on how to use this. + +Check it out, live: https://cnlohr.github.io/rawdrawwasm/ + + +I wanted to compare the difference beteen an asynchronous and synchronous style. Also, measure the overhead introudced by adding `--asyncify`. This is using the canvas 2d methods. Those are being abandoned. + + +``` +//TEST ON CHROME: +//NOT RASTERIZER +// Async drive: ~7.2ms +// Sync drive: ~6.9ms +//sync w/o asyncify: 7.0ms. +// +//RASTERIZER +//Async: 7.5ms +//Sync: 3.9ms +//Sync w/o asyncify: 3.6ms + +//TEST ON FIREFOX: +//Async: 17.3ms +//sync: 13.7ms +//Sync w/o asyncify: 13.3ms +// +//FF without including swap. +//Async: 11.5 +//Sync: 11.5ms +//Sync w/o asyncify: 11.2ms +``` + + +This was something I found convenient for a while... +``` + + function wasmloaded(wa) + { + instance = wa; + wasmExports = instance.exports; + instance.exports.main(); + } + + //If at all possible, we should attempt to load in-thread + //This will make the load not flash. + if( blob.length < 4096 ) + { + let mod = new WebAssembly.Module(array); + var wa = new WebAssembly.Instance( mod, imports ); + wasmloaded( wa ); + } + else + { + WebAssembly.instantiate(array, imports).then( function(wa) { wasmloaded(wa.instance); } ); + } +``` + diff --git a/app/src/nativecode/rawdraw/wasm/build-wasm-index.ps1 b/app/src/nativecode/rawdraw/wasm/build-wasm-index.ps1 new file mode 100644 index 0000000..8761f0d --- /dev/null +++ b/app/src/nativecode/rawdraw/wasm/build-wasm-index.ps1 @@ -0,0 +1,83 @@ +# Install LLVM to C:\Program Files\LLVM or set parameter below (Recommends LLVM-11) +# Install Binaryen to C:\binaryen\ or set parameter below (Recommends binaryen-98) +# If you want uglify-js, install node to your system paths, https://nodejs.org/en/download/ +# Execute: npm install -g terser + +# You can also install clang via msys2 with "pacman -S llvm mingw-w64-clang-x86_64-wasm-pack mingw-w64-clang-x86_64-binaryen lld" + +#$Clang = "C:\Program Files\LLVM\bin\clang" +$Clang = "D:\msys64\usr\bin\clang" + +# Assuming you have downloaded binaryen-98 and put it someheere that is in your paths. +#$WASMOpt = "wasm-opt" +$WASMOpt = "D:\msys64\clang64\bin\wasm-opt" + + +$OutFile = 'index.html' +$TemplateHT = 'template.ht' +$TemplateJS = 'template.js' +$MainWASM = 'main.wasm' +$InputC = 'rawdraw.c' +$IntermediateJS = 'main.js.tmp' + +Write-Host "Using Clang at $Clang" +Write-Host "Compiling $InputC..." +$ClangProc = Start-Process -NoNewWindow -FilePath $Clang -PassThru -ArgumentList @( + '-I..', + '-DWASM', + '-nostdlib', + '--target=wasm32', + '-flto', + '-Oz', + '-Wl,--lto-O3', + '-Wl,--no-entry', + '-Wl,--allow-undefined', + '-Wl,--import-memory', + "-o $MainWASM", + $InputC +) +$ClangProc.WaitForExit(); + +Write-Host "Using Binaryen at $WASMOpt" +Write-Host 'Asyncifying...' +$AsyncProc = Start-Process -NoNewWindow -FilePath $WASMOpt -PassThru -ArgumentList @( + '--asyncify', + '--pass-arg=asyncify-imports@bynsyncify.*', + '--pass-arg=asyncify-ignore-indirect' + '-Oz', + $MainWASM, + "-o $MainWASM.opt" +) +$AsyncProc.WaitForExit(); + +Write-Host 'Merging files...' +Write-Host ("WASM Binary size: {0:N0} B" -f (Get-Item $PSScriptRoot\$MainWASM).length) +$WASMasB64 = [Convert]::ToBase64String([IO.File]::ReadAllBytes("$PSScriptRoot\$MainWASM.opt")) +#Remove-Item $MainWASM + +$ContentHT = Get-Content $TemplateHT -Raw +$ContentJS = Get-Content $TemplateJS -Raw + +$Output = $ContentJS.Replace('${BLOB}', $WASMasB64); + +if (Get-Command "terser" -ErrorAction SilentlyContinue) +{ + Set-Content $IntermediateJS -Value $Output + Write-Host ("Before minifaction: {0:N0} B" -f ($Output).length) + $Output = terser -ecma 2017 ` + -d RAWDRAW_USE_LOOP_FUNCTION=false ` + -d RAWDRAW_NEED_BLITTER=true ` + $IntermediateJS + + Write-Host ("After minifacation: {0:N0} B" -f ($Output).length) + Remove-Item $IntermediateJS +} +else +{ + Write-Host 'terser not found. Not uglifying javascript.'; +} + +$Output = $ContentHT.Replace('${JAVASCRIPT_DATA}', $Output); + +Set-Content $OutFile -Value $Output +Write-Host ("$OutFile size: {0:N0} B" -f (Get-Item $OutFile).length) diff --git a/app/src/nativecode/rawdraw/wasm/rawdraw.c b/app/src/nativecode/rawdraw/wasm/rawdraw.c new file mode 100644 index 0000000..dcf04d2 --- /dev/null +++ b/app/src/nativecode/rawdraw/wasm/rawdraw.c @@ -0,0 +1,276 @@ +//Copyright (c) 2011 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose. + +#include "os_generic.h" + +#define CNFG3D +#define CNFG_IMPLEMENTATION +//#define CNFGRASTERIZER + +#include "CNFG.h" + +double OGGetAbsoluteTime(); +void OGUSleep( int us ); +void prints( const char * sdebug ); +void print( double idebug ); +double sin( double x ); +double cos( double x ); + +unsigned long iframeno = 0; +int lastmousex; +int lastmousey = 100; + +void __attribute__((export_name("HandleKey"))) HandleKey( int keycode, int bDown ) +{ + print( keycode ); + print( bDown ); +} + +void __attribute__((export_name("HandleButton"))) HandleButton( int x, int y, int button, int bDown ) +{ + print( x ); + print( y ); + print( button ); + print( bDown ); +} + +void __attribute__((export_name("HandleMotion"))) HandleMotion( int x, int y, int mask ) +{ + lastmousex = x; + lastmousey = y; +} + +void HandleDestroy() +{ + //printf( "Destroying\n" ); +} + +#define HMX 40 +#define HMY 40 +short screenx, screeny; +float Heightmap[HMX*HMY]; +void DrawHeightmap(); + +unsigned fpsframes = 0; +double fpstime = 0; + +int __attribute__((export_name("main"))) main() +{ + int i, x, y; + fpstime = OGGetAbsoluteTime(); + + //Setup colors. + CNFGBGColor = 0xff800000; + + //Actually sets outside window title, and + //If not in full-screen mode, will also resize. + //CNFGSetup( "Test Bench", 640, 480 ); + CNFGSetupFullscreen( "Test Bench", 0 ); + + //Configure heightmap. + for( x = 0; x < HMX; x++ ) + for( y = 0; y < HMY; y++ ) + { + Heightmap[x+y*HMX] = tdPerlin2D( x, y )*8.; + } + + prints( "Main started. This will appear in your browser console." ); + +#ifdef RAWDRAW_USE_LOOP_FUNCTION + return 0; +} +int __attribute__((export_name("loop"))) loop() +{ +#else +while(CNFGHandleInput()) +#endif + { + int i, pos; + float f; + iframeno++; + RDPoint pto[3]; + + //Normally this would invoke callbacks, but + //at least currently, we do that out-of-band. + //CNFGHandleInput(); + + + CNFGClearFrame(); + CNFGColor( 0xFFFFFFFF ); + CNFGGetDimensions( &screenx, &screeny ); + CNFGPenX = 100; + CNFGPenY = 100; + +//quick minitest +// CNFGDrawText("+hello!!",4 ); +// CNFGSwapBuffers(); +// continue; + + // Mesh in background + DrawHeightmap(); + + // Square behind text + CNFGColor( 0xff444444 ); + CNFGSetLineWidth(3); + CNFGTackSegment( 0, 50, 100, 50 ); + CNFGTackSegment( 0, 50, 0, 150 ); + CNFGTackRectangle( 2, 2+50, 345, 345+50 ); + + CNFGSetLineWidth(3); + + // Text stuff in upper left. + pos = 0; + CNFGColor( 0xffffffff ); + for( i = 0; i < 1; i++ ) + { + int c; + char tw[2] = { 0, 0 }; + for( c = 0; c < 256; c++ ) + { + tw[0] = c; + + CNFGPenX = ( c % 16 ) * 20+5; + CNFGPenY = ( c / 16 ) * 20+55; + CNFGDrawText( tw, 4 ); + } + } + +#if 1 + // Green triangles + CNFGPenX = 0; + CNFGPenY = 0; + + RDPoint pp[3]; + for( i = 0; i < 400; i++ ) + { + CNFGColor( 0xFF00FF00 ); + pp[0].x = (short)(50*sin((float)(i+iframeno)*.01) + (i%20)*30); + pp[0].y = (short)(50*cos((float)(i+iframeno)*.01) + (i/20)*20)+100; + pp[1].x = (short)(20*sin((float)(i+iframeno)*.01) + (i%20)*30); + pp[1].y = (short)(50*cos((float)(i+iframeno)*.01) + (i/20)*20)+100; + pp[2].x = (short)(10*sin((float)(i+iframeno)*.01) + (i%20)*30); + pp[2].y = (short)(30*cos((float)(i+iframeno)*.01) + (i/20)*20)+100; + CNFGTackPoly( pp, 3 ); + } +#endif + +#if 0 //Profiling + extern int Add1( int i ); + double now = OGGetAbsoluteTime(); + double k = 4; + for( i = 0; i < 1000000; i++ ) + { + //CNFGTackPoly( pp, 3 ); //3200 ns + CNFGTackPixel( 0,0 ); //340ns + //CNFGTackSegment( 0, 0, 10, 10 ); //157ns + //k = sin(k); //28ns + //k = Add1(k); //16ns + //NOTE: actual callback time is ~14ns, so loop overhead is about 10ns. + } + print( (OGGetAbsoluteTime() - now)*1000 ); + print( k ); +#endif + +#if 1 //Enable alpha objec test + static uint32_t randomtexturedata[65536]; + int x, y; + for( y = 0; y < 256; y++ ) + for( x = 0; x < 256; x++ ) + randomtexturedata[x+y*256] = x | ((x*394543L+y*355+iframeno)<<8); + CNFGBlitImage( randomtexturedata, 100, 300, 256, 256 ); +#endif + + fpsframes++; + CNFGSwapBuffers(); + + double Now = OGGetAbsoluteTime(); + if( Now - fpstime >= 1 ) + { + print( fpsframes ); + fpsframes = 0; + fpstime += 1; + } + } + + return(0); +} + + +void DrawHeightmap() +{ + int x, y; + float fdt = ((iframeno++)%(360*10))/100.0 + lastmousex; + float eye[3] = { (float)sin(fdt*(3.14159/180.0))*1, (float)cos(fdt*(3.14159/180.0))*1, lastmousey*.01 }; + float at[3] = { 0,0, 0 }; + float up[3] = { 0,0, 1 }; + + tdSetViewport( -1, -1, 1, 1, screenx, screeny ); + + tdMode( tdPROJECTION ); + tdIdentity( gSMatrix ); + tdPerspective( 40, ((float)screenx)/((float)screeny), .1, 200., gSMatrix ); + + tdMode( tdMODELVIEW ); + tdIdentity( gSMatrix ); + tdTranslate( gSMatrix, 0, 0, -40 ); + tdLookAt( gSMatrix, eye, at, up ); + + + for( x = 0; x < HMX-1; x++ ) + for( y = 0; y < HMY-1; y++ ) + { + float tx = x-HMX/2; + float ty = y-HMY/2; + float pta[3]; + float ptb[3]; + float ptc[3]; + float ptd[3]; + + float normal[3]; + float lightdir[3] = { 1, -1, 1 }; + float tmp1[3]; + float tmp2[3]; + + RDPoint pto[6]; + + pta[0] = tx+0; pta[1] = ty+0; pta[2] = Heightmap[(x+0)+(y+0)*HMX]; + ptb[0] = tx+1; ptb[1] = ty+0; ptb[2] = Heightmap[(x+1)+(y+0)*HMX]; + ptc[0] = tx+0; ptc[1] = ty+1; ptc[2] = Heightmap[(x+0)+(y+1)*HMX]; + ptd[0] = tx+1; ptd[1] = ty+1; ptd[2] = Heightmap[(x+1)+(y+1)*HMX]; + + tdPSub( pta, ptb, tmp2 ); + tdPSub( ptc, ptb, tmp1 ); + tdCross( tmp1, tmp2, normal ); + + tdFinalPoint( pta, pta ); + tdFinalPoint( ptb, ptb ); + tdFinalPoint( ptc, ptc ); + tdFinalPoint( ptd, ptd ); + + if( pta[2] >= 1.0 ) continue; + if( ptb[2] >= 1.0 ) continue; + if( ptc[2] >= 1.0 ) continue; + if( ptd[2] >= 1.0 ) continue; + + if( pta[2] < 0 ) continue; + if( ptb[2] < 0 ) continue; + if( ptc[2] < 0 ) continue; + if( ptd[2] < 0 ) continue; + + pto[0].x = pta[0]; pto[0].y = pta[1]; + pto[1].x = ptb[0]; pto[1].y = ptb[1]; + pto[2].x = ptd[0]; pto[2].y = ptd[1]; + + pto[3].x = ptc[0]; pto[3].y = ptc[1]; + pto[4].x = ptd[0]; pto[4].y = ptd[1]; + pto[5].x = pta[0]; pto[5].y = pta[1]; + + float bright = tdDot( normal, lightdir ); + if( bright < 0 ) bright = 0; + CNFGColor( 0xff000000 | (int)( bright * 50 ) ); + + CNFGTackSegment( pta[0], pta[1], ptb[0], ptb[1] ); + CNFGTackSegment( pta[0], pta[1], ptc[0], ptc[1] ); + } +} + + diff --git a/app/src/nativecode/rawdraw/wasm/subst.c b/app/src/nativecode/rawdraw/wasm/subst.c new file mode 100644 index 0000000..d385469 --- /dev/null +++ b/app/src/nativecode/rawdraw/wasm/subst.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2021 zNoctum + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +struct table_entry { + size_t keysize; + char *key; + char *value; +}; + +#define SWALLOW 1 + +char *readfile(const char *filename, int flags) +{ + size_t size; + char *buffer; + FILE *file = fopen(filename, "r"); + + if (file == NULL) { + printf("Failed to open '%s'!\n", filename); + exit(1); + } + + fseek(file, 0, SEEK_END); + size = ftell(file); + rewind(file); + + buffer = malloc(size); + + fread(buffer, size, 1, file); + + if ((flags & SWALLOW) != 0) { + if (buffer[size - 1] == '\n') + buffer[size - 1] = 0x00; + } + + fclose(file); + return buffer; +} + +void print_help() +{ + printf( + "Usage: subst [TEMPLATE-FILE] [VARIABLES]\n" + "\n" + "Options:\n" + " -s: Swallow following newline in files\n" + " -o: Define output file\n" + " -f: Define variable to replace '-f VARNAME FILEPATH'\n" + " -d: Define variable to replace '-f VARNAME VARCONTENT'\n" + " -h: Print this message\n" + ); +} + + +char *outfile_name = NULL; +char *outfile = NULL; +size_t outfile_size; +size_t outfile_current_size; +int outfile_set = 0; +void putchar_in_output(char c) +{ + if (outfile_set) { + if (outfile == NULL) { + outfile = malloc(sizeof(char) * 1024); + outfile_size = 1024; + } + if (outfile_current_size == outfile_size) { + outfile_size *= 2; + outfile = realloc(outfile, outfile_size); + } + outfile[outfile_current_size++] = c; + } else { + putchar(c); + fflush(stdout); + } +} + + +int main(int argc, char *argv[]) +{ + int flags = 0; + size_t i; + size_t table_counter = 0; + struct table_entry *table = malloc((argc - 2) / 3 * sizeof(struct table_entry)); + char *infile; + char *ident_begin; + char *table_str; + size_t ident_len; + + if (argc < 2) { + printf("No input file was specified\n"); + exit(1); + } + + if (argv[1][0] == '-' && argv[1][1] == 'h') { + print_help(); + return 0; + } + + + infile = readfile(argv[1], 0); + +read_args: + for (i = 2; i < argc; i++) { + if (argv[i][0] == '-') { + switch(argv[i][1]) { + case 's': + if ((flags & SWALLOW) == 0) { + flags |= SWALLOW; + goto read_args; + } + break; + case 'o': + outfile_name = argv[++i]; + outfile_set = 1; + break; + case 'd': + if (i++ == argc) { + printf("supplied not enough arguments\n"); + } + table[table_counter].keysize = strlen(argv[i]); + table[table_counter].key = argv[i]; + i++; + table[table_counter].value = argv[i]; + table_counter++; + break; + case 'f': + if (i++ == argc) { + printf("supplied not enough arguments\n"); + } + table[table_counter].keysize = strlen(argv[i]); + table[table_counter].key = argv[i]; + i++; + table[table_counter].value = readfile(argv[i], flags); + table_counter++; + break; + case 'h': + print_help(); + return 0; + } + } + + } + + while (*infile) { + if (!(*infile == '$' && infile[1] == '{')) { + putchar_in_output(*infile); + infile++; + continue; + } + infile += 2; + ident_begin = infile; + ident_len = 0; + while (*infile != '}') { + if (*infile == 0x00) { + printf("Unclosed variable refrence!\n"); + exit(1); + } + infile++; + ident_len++; + } + for (i = 0; i < table_counter; i++) { + if (table[i].keysize != ident_len) + continue; + if (strncmp(table[i].key, ident_begin, ident_len) != 0) + continue; + table_str = table[i].value; + while (*table_str) + putchar_in_output(*table_str++); + } + + infile++; + } + + if (outfile_set) { + FILE* file = fopen(outfile_name, "w"); + if (file == NULL) { + printf("Failed to open %s for output!\n", outfile_name); + exit(1); + } + fwrite(outfile, outfile_current_size - 3, 1, file); + fclose(file); + } + return 0; +} diff --git a/app/src/nativecode/rawdraw/wasm/template.ht b/app/src/nativecode/rawdraw/wasm/template.ht new file mode 100644 index 0000000..979cc6b --- /dev/null +++ b/app/src/nativecode/rawdraw/wasm/template.ht @@ -0,0 +1,23 @@ + + + + + Test + + + +
+ This is a demonstration of rawdrawwasm, a minimal technique to use C with rawdraw with WASM and without Emscripten. + This file is a single-file HTML file. Thanks to @zNoctum and @redline2466. Check out the info surrounding this project here. This is now part of rawdraw. +
+
+ +
+ + + diff --git a/app/src/nativecode/rawdraw/wasm/template.js b/app/src/nativecode/rawdraw/wasm/template.js new file mode 100644 index 0000000..2c4a1fb --- /dev/null +++ b/app/src/nativecode/rawdraw/wasm/template.js @@ -0,0 +1,314 @@ +//Portions of code from zNoctum and redline2466 + +//Global memory for application. +let memory = new WebAssembly.Memory({initial:200}); +let HEAP8 = new Int8Array(memory.buffer); +let HEAPU8 = new Uint8Array(memory.buffer); +let HEAP16 = new Int16Array(memory.buffer); +let HEAPU16 = new Uint16Array(memory.buffer); +let HEAP32 = new Uint32Array(memory.buffer); +let HEAPU32 = new Uint32Array(memory.buffer); +let HEAPF32 = new Float32Array(memory.buffer); +let HEAPF64 = new Float64Array(memory.buffer); + +let toUtf8Decoder = new TextDecoder( "utf-8" ); +function toUTF8(ptr) { + let len = 0|0; ptr |= 0; + for( let i = ptr; HEAPU8[i] != 0; i++) len++; + return toUtf8Decoder.decode(HEAPU8.subarray(ptr, ptr+len)); +} + +let wasmExports; +const DATA_ADDR = 16|0; // Where the unwind/rewind data structure will live. +let rendering = false; +let fullscreen = false; + +//Configure WebGL Stuff (allow to be part of global context) +let canvas = document.getElementById('canvas'); +let wgl = canvas.getContext('webgl'); +if( !wgl ) +{ + //Janky - on Firefox 83, with NVIDIA GPU, you need to ask twice. + wgl = canvas.getContext('webgl'); +} +let wglShader = null; //Standard flat color shader +let wglABV = null; //Array buffer for vertices +let wglABC = null; //Array buffer for colors. +let wglUXFRM = null; //Uniform location for transform on solid colors + + +//Utility stuff for WebGL sahder creation. +function wgl_makeShader( vertText, fragText ) +{ + let vert = wgl.createShader(wgl.VERTEX_SHADER); + wgl.shaderSource(vert, vertText ); + wgl.compileShader(vert); + if (!wgl.getShaderParameter(vert, wgl.COMPILE_STATUS)) { + alert(wgl.getShaderInfoLog(vert)); + } + + let frag = wgl.createShader(wgl.FRAGMENT_SHADER); + wgl.shaderSource(frag, fragText ); + wgl.compileShader(frag); + if (!wgl.getShaderParameter(frag, wgl.COMPILE_STATUS)) { + alert(wgl.getShaderInfoLog(frag)); + } + let ret = wgl.createProgram(); + wgl.attachShader(ret, frag); + wgl.attachShader(ret, vert); + wgl.linkProgram(ret); + wgl.bindAttribLocation( ret, 0, "a0" ); + wgl.bindAttribLocation( ret, 1, "a1" ); + return ret; +} + +{ + //We load two shaders, one is a solid-color shader, for most rawdraw objects. + wglShader = wgl_makeShader( + "uniform vec4 xfrm; attribute vec3 a0; attribute vec4 a1; varying vec4 vc; void main() { gl_Position = vec4( a0.xy*xfrm.xy+xfrm.zw, a0.z, 0.5 ); vc = a1; }", + "precision mediump float; varying vec4 vc; void main() { gl_FragColor = vec4(vc.xyzw); }" ); + + wglUXFRM = wgl.getUniformLocation(wglShader, "xfrm" ); + + //Compile the shaders. + wgl.useProgram(wglShader); + + //Get some vertex/color buffers, to put geometry in. + wglABV = wgl.createBuffer(); + wglABC = wgl.createBuffer(); + + //We're using two buffers, so just enable them, now. + wgl.enableVertexAttribArray(0); + wgl.enableVertexAttribArray(1); + + //Enable alpha blending + wgl.enable(wgl.BLEND); + wgl.blendFunc(wgl.SRC_ALPHA, wgl.ONE_MINUS_SRC_ALPHA); +} + +//Do webgl work that must happen every frame. +function FrameStart() +{ + //Fixup canvas sizes + if( fullscreen ) + { + wgl.viewportWidth = canvas.width = window.innerWidth; + wgl.viewportHeight = canvas.height = window.innerHeight; + } + + //Make sure viewport and input to shader is correct. + //We do this so we can pass literal coordinates into the shader. + wgl.viewport( 0, 0, wgl.viewportWidth, wgl.viewportHeight ); + + //Update geometry transform (Scale/shift) + wgl.uniform4f( wglUXFRM, + 1./wgl.viewportWidth, -1./wgl.viewportHeight, + -0.5, 0.5); +} + +function SystemStart( title, w, h ) +{ + document.title = toUTF8( title ); + wgl.viewportWidth = canvas.width = w; + wgl.viewportHeight = canvas.height = h; +} + +//Buffered geometry system. +//This handles buffering a bunch of lines/segments, and using them all at once. +globalv = null; + +function CNFGEmitBackendTrianglesJS( vertsF, colorsI, vertcount ) +{ + const ab = wgl.ARRAY_BUFFER; + wgl.bindBuffer(ab, wglABV); + wgl.bufferData(ab, vertsF, wgl.DYNAMIC_DRAW); + wgl.vertexAttribPointer(0, 3, wgl.FLOAT, false, 0, 0); + wgl.bindBuffer(ab, wglABC); + wgl.bufferData(ab, colorsI, wgl.DYNAMIC_DRAW); + wgl.vertexAttribPointer(1, 4, wgl.UNSIGNED_BYTE, true, 0, 0); + wgl.drawArrays(wgl.TRIANGLES, 0, vertcount ); + globalv = vertsF; +} + +//This defines the list of imports, the things that C will be importing from Javascript. +//To use functions here, just call them. Surprisingly, signatures justwork. +let imports = { + env: { + //Mapping our array buffer into the system. + memory: memory, + + //Various draw-functions. + CNFGEmitBackendTriangles : (vertsF, colorsI, vertcount )=> + { + //Take a float* and uint32_t* of vertices, and flat-render them. + CNFGEmitBackendTrianglesJS( + HEAPF32.slice(vertsF>>2,(vertsF>>2)+vertcount*3), + HEAPU8.slice(colorsI,(colorsI)+vertcount*4), + vertcount ); + }, + CNFGSetup : (title,w,h ) => { + SystemStart( title, w, h ); + fullscreen = false; + }, + CNFGSetupFullscreen : (title,w,h ) => { + SystemStart( title, w, h ); + canvas.style = "position:absolute; top:0; left:0;" + fullscreen = true; + }, + CNFGClearFrameInternal: ( color ) => { + wgl.clearColor( (color&0xff)/255., ((color>>8)&0xff)/255., + ((color>>16)&0xff)/255., ((color>>24)&0xff)/255. ); + wgl.clear( wgl.COLOR_BUFFER_BIT | wgl.COLOR_DEPTH_BIT ); + }, + CNFGGetDimensions: (pw, ph) => { + HEAP16[pw>>1] = canvas.width; + HEAP16[ph>>1] = canvas.height; + }, + OGGetAbsoluteTime : () => { return new Date().getTime()/1000.; }, + + Add1 : (i) => { return i+1; }, //Super simple function for speed testing. + + //Tricky - math functions just automatically link through. + sin : Math.sin, + cos : Math.cos, + tan : Math.tan, + sinf : Math.sin, + cosf : Math.cos, + tanf : Math.tan, + + //Quick-and-dirty debug. + print: console.log, + prints: (str) => { console.log(toUTF8(str)); }, + } +}; + +if( !RAWDRAW_USE_LOOP_FUNCTION ) +{ + imports.bynsyncify = { + //Any javascript functions which may unwind the stack should be placed here. + CNFGSwapBuffersInternal: () => { + if (!rendering) { + // We are called in order to start a sleep/unwind. + // Fill in the data structure. The first value has the stack location, + // which for simplicity we can start right after the data structure itself. + HEAPU32[DATA_ADDR >> 2] = DATA_ADDR + 8; + // The end of the stack will not be reached here anyhow. + HEAPU32[DATA_ADDR + 4 >> 2] = 1024|0; + wasmExports.asyncify_start_unwind(DATA_ADDR); + rendering = true; + // Resume after the proper delay. + requestAnimationFrame(function() { + FrameStart(); + wasmExports.asyncify_start_rewind(DATA_ADDR); + // The code is now ready to rewind; to start the process, enter the + // first function that should be on the call stack. + wasmExports.main(); + }); + } else { + // We are called as part of a resume/rewind. Stop sleeping. + wasmExports.asyncify_stop_rewind(); + rendering = false; + } + } + } +} + +if( RAWDRAW_NEED_BLITTER ) +{ + let wglBlit = null; //Blitting shader for texture + let wglTex = null; //Texture handle for blitting. + let wglUXFRMBlit = null; //Uniform location for transform on blitter + + //We are not currently supporting the software renderer. + //We load two shaders, the other is a texture shader, for blitting things. + wglBlit = wgl_makeShader( + "uniform vec4 xfrm; attribute vec3 a0; attribute vec4 a1; varying vec2 tc; void main() { gl_Position = vec4( a0.xy*xfrm.xy+xfrm.zw, a0.z, 0.5 ); tc = a1.xy; }", + "precision mediump float; varying vec2 tc; uniform sampler2D tex; void main() { gl_FragColor = texture2D(tex,tc).wzyx;}" ); + + wglUXFRMBlit = wgl.getUniformLocation(wglBlit, "xfrm" ); + + imports.env.CNFGBlitImageInternal = (memptr, x, y, w, h ) => { + if( w <= 0 || h <= 0 ) return; + + wgl.useProgram(wglBlit); + + //Most of the time we don't use textures, so don't initiate at start. + if( wglTex == null ) wglTex = wgl.createTexture(); + + wgl.activeTexture(wgl.TEXTURE0); + const t2d = wgl.TEXTURE_2D; + wgl.bindTexture(t2d, wglTex); + + //Note that unlike the normal color operation, we don't have an extra offset. + wgl.uniform4f( wglUXFRMBlit, + 1./wgl.viewportWidth, -1./wgl.viewportHeight, + -.5+x/wgl.viewportWidth, .5-y/wgl.viewportHeight ); + + //These parameters are required. Not sure why the defaults don't work. + wgl.texParameteri(t2d, wgl.TEXTURE_WRAP_T, wgl.CLAMP_TO_EDGE); + wgl.texParameteri(t2d, wgl.TEXTURE_WRAP_S, wgl.CLAMP_TO_EDGE); + wgl.texParameteri(t2d, wgl.TEXTURE_MIN_FILTER, wgl.NEAREST); + + wgl.texImage2D(t2d, 0, wgl.RGBA, w, h, 0, wgl.RGBA, + wgl.UNSIGNED_BYTE, new Uint8Array(memory.buffer,memptr,w*h*4) ); + + CNFGEmitBackendTrianglesJS( + new Float32Array( [0,0,0, w,0,0, w,h,0, 0,0,0, w,h,0, 0,h,0 ] ), + new Uint8Array( [0,0,0,0, 255,0,0,0, 255,255,0,0, 0,0,0,0, 255,255,0,0, 0,255,0,0] ), + 6 ); + + wgl.useProgram(wglShader); + }; +} + +{ + // Actually load the WASM blob. + let blob = atob('${BLOB}'); + let array = new Uint8Array(new ArrayBuffer(blob.length)); + for(let i = 0; i < blob.length; i++) array[i] = blob.charCodeAt(i); + + WebAssembly.instantiate(array, imports).then( + function(wa) { + instance = wa.instance; + wasmExports = instance.exports; + + //Attach inputs. + if( instance.exports.HandleMotion ) + { + canvas.addEventListener('mousemove', e => { instance.exports.HandleMotion( e.offsetX, e.offsetY, e.buttons ); } ); + canvas.addEventListener('touchmove', e => { instance.exports.HandleMotion( e.touches[0].clientX, e.touches[0].clientY, 1 ); } ); + } + + if( instance.exports.HandleButton ) + { + canvas.addEventListener('mouseup', e => { instance.exports.HandleButton( e.offsetX, e.offsetY, e.button, 0 ); return false; } ); + canvas.addEventListener('mousedown', e => { instance.exports.HandleButton( e.offsetX, e.offsetY, e.button, 1 ); return false; } ); + } + + if( instance.exports.HandleKey ) + { + document.addEventListener('keydown', e => { instance.exports.HandleKey( e.keyCode, 1 ); } ); + document.addEventListener('keyup', e => { instance.exports.HandleKey( e.keyCode, 0 ); } ); + } + + + //Actually invoke main(). Note that, upon "CNFGSwapBuffers" this will 'exit' + //But, will get re-entered from the swapbuffers animation callback. + instance.exports.main(); + + if( RAWDRAW_USE_LOOP_FUNCTION ) + { + function floop() { + FrameStart(); + requestAnimationFrame(floop); + // The code is now ready to rewind; to start the process, enter the + // first function that should be on the call stack. + wasmExports.loop(); + } + floop(); + } + } ); + + //Code here would continue executing, but this code is executed *before* main. +} + diff --git a/app/src/nativecode/test.c b/app/src/nativecode/test.c new file mode 100644 index 0000000..06ba999 --- /dev/null +++ b/app/src/nativecode/test.c @@ -0,0 +1,663 @@ +//Copyright (c) 2011-2020 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose. +// NO WARRANTY! NO GUARANTEE OF SUPPORT! USE AT YOUR OWN RISK + +#include +#include +#include +#include +#include "os_generic.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "CNFGAndroid.h" + +//#define CNFA_IMPLEMENTATION +#define CNFG_IMPLEMENTATION +#define CNFG3D + +//#include "cnfa/CNFA.h" +#include "CNFG.h" + +#define WEBVIEW_NATIVE_ACTIVITY_IMPLEMENTATION +#include "webview_native_activity.h" + +float mountainangle; +float mountainoffsetx; +float mountainoffsety; + +ASensorManager * sm; +const ASensor * as; +bool no_sensor_for_gyro = false; +ASensorEventQueue* aeq; +ALooper * l; + +WebViewNativeActivityObject MyWebView; + +const uint32_t SAMPLE_RATE = 44100; +const uint16_t SAMPLE_COUNT = 512; +uint32_t stream_offset = 0; +uint16_t audio_frequency; + +void SetupIMU() +{ + sm = ASensorManager_getInstanceForPackage("gyroscope"); + as = ASensorManager_getDefaultSensor( sm, ASENSOR_TYPE_GYROSCOPE ); + no_sensor_for_gyro = as == NULL; + l = ALooper_prepare( ALOOPER_PREPARE_ALLOW_NON_CALLBACKS ); + aeq = ASensorManager_createEventQueue( sm, (ALooper*)&l, 0, 0, 0 ); //XXX??!?! This looks wrong. + if(!no_sensor_for_gyro) { + ASensorEventQueue_enableSensor( aeq, as); + printf( "setEvent Rate: %d\n", ASensorEventQueue_setEventRate( aeq, as, 10000 ) ); + } + +} + +float accx, accy, accz; +int accs; + +void AccCheck() +{ + if(no_sensor_for_gyro) { + return; + } + + ASensorEvent evt; + do + { + ssize_t s = ASensorEventQueue_getEvents( aeq, &evt, 1 ); + if( s <= 0 ) break; + accx = evt.vector.v[0]; + accy = evt.vector.v[1]; + accz = evt.vector.v[2]; + mountainangle /*degrees*/ -= accz;// * 3.1415 / 360.0;// / 100.0; + mountainoffsety += accy; + mountainoffsetx += accx; + accs++; + } while( 1 ); +} + +unsigned frames = 0; +unsigned long iframeno = 0; + +void AndroidDisplayKeyboard(int pShow); + +int lastbuttonx = 0; +int lastbuttony = 0; +int lastmotionx = 0; +int lastmotiony = 0; +int lastbid = 0; +int lastmask = 0; +int lastkey, lastkeydown; + +static int keyboard_up; +uint8_t buttonstate[8]; + +void HandleKey( int keycode, int bDown ) +{ + lastkey = keycode; + lastkeydown = bDown; + if( keycode == 10 && !bDown ) { keyboard_up = 0; AndroidDisplayKeyboard( keyboard_up ); } + + if( keycode == 4 ) { AndroidSendToBack( 1 ); } //Handle Physical Back Button. +} + +void HandleButton( int x, int y, int button, int bDown ) +{ + buttonstate[button] = bDown; + lastbid = button; + lastbuttonx = x; + lastbuttony = y; + + if( bDown ) { keyboard_up = !keyboard_up; AndroidDisplayKeyboard( keyboard_up ); } +} + +void HandleMotion( int x, int y, int mask ) +{ + lastmask = mask; + lastmotionx = x; + lastmotiony = y; +} + +//writes the text to a file to path (example): /storage/emulated/0/Android/data/org.yourorg.cnfgtest/files +// You would not normally want to do this, but it's an example of how to do local storage. +void Log(const char *fmt, ...) +{ + const char* getpath = AndroidGetExternalFilesDir(); + char buffer[2048]; + snprintf(buffer, sizeof(buffer), "%s/log.txt", getpath); + FILE *f = fopen(buffer, "w"); + if (f == NULL) + { + exit(1); + } + + va_list arg; + va_start(arg, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, arg); + va_end(arg); + + fprintf(f, "%s\n", buffer); + + fclose(f); +} +#define HMX 162 +#define HMY 162 +short screenx, screeny; +float Heightmap[HMX*HMY]; + +extern struct android_app * gapp; + +void DrawHeightmap() +{ + int x, y; + //float fdt = ((iframeno++)%(360*10))/10.0; + + mountainangle += .2; + if( mountainangle < 0 ) mountainangle += 360; + if( mountainangle > 360 ) mountainangle -= 360; + + mountainoffsety = mountainoffsety - ((mountainoffsety-100) * .1); + + float eye[3] = { (float)(sin(mountainangle*(3.14159/180.0))*30*sin(mountainoffsety/100.)), (float)(cos(mountainangle*(3.14159/180.0))*30*sin(mountainoffsety/100.)), (float)(30*cos(mountainoffsety/100.)) }; + float at[3] = { 0,0, 0 }; + float up[3] = { 0,0, 1 }; + + tdSetViewport( -1, -1, 1, 1, screenx, screeny ); + + tdMode( tdPROJECTION ); + tdIdentity( gSMatrix ); + tdPerspective( 30, ((float)screenx)/((float)screeny), .1, 200., gSMatrix ); + + tdMode( tdMODELVIEW ); + tdIdentity( gSMatrix ); + tdTranslate( gSMatrix, 0, 0, -40 ); + tdLookAt( gSMatrix, eye, at, up ); + + float scale = 60./HMX; + + for( x = 0; x < HMX-1; x++ ) + for( y = 0; y < HMY-1; y++ ) + { + float tx = x-HMX/2; + float ty = y-HMY/2; + float pta[3]; + float ptb[3]; + float ptc[3]; + float ptd[3]; + + float normal[3]; + float lightdir[3] = { .6, -.6, 1 }; + float tmp1[3]; + float tmp2[3]; + + RDPoint pto[6]; + + pta[0] = (tx+0)*scale; pta[1] = (ty+0)*scale; pta[2] = Heightmap[(x+0)+(y+0)*HMX]*scale; + ptb[0] = (tx+1)*scale; ptb[1] = (ty+0)*scale; ptb[2] = Heightmap[(x+1)+(y+0)*HMX]*scale; + ptc[0] = (tx+0)*scale; ptc[1] = (ty+1)*scale; ptc[2] = Heightmap[(x+0)+(y+1)*HMX]*scale; + ptd[0] = (tx+1)*scale; ptd[1] = (ty+1)*scale; ptd[2] = Heightmap[(x+1)+(y+1)*HMX]*scale; + + tdPSub( pta, ptb, tmp2 ); + tdPSub( ptc, ptb, tmp1 ); + tdCross( tmp1, tmp2, normal ); + tdNormalizeSelf( normal ); + + tdFinalPoint( pta, pta ); + tdFinalPoint( ptb, ptb ); + tdFinalPoint( ptc, ptc ); + tdFinalPoint( ptd, ptd ); + + if( pta[2] >= 1.0 ) continue; + if( ptb[2] >= 1.0 ) continue; + if( ptc[2] >= 1.0 ) continue; + if( ptd[2] >= 1.0 ) continue; + + if( pta[2] < 0 ) continue; + if( ptb[2] < 0 ) continue; + if( ptc[2] < 0 ) continue; + if( ptd[2] < 0 ) continue; + + pto[0].x = pta[0]; pto[0].y = pta[1]; + pto[1].x = ptb[0]; pto[1].y = ptb[1]; + pto[2].x = ptd[0]; pto[2].y = ptd[1]; + + pto[3].x = ptc[0]; pto[3].y = ptc[1]; + pto[4].x = ptd[0]; pto[4].y = ptd[1]; + pto[5].x = pta[0]; pto[5].y = pta[1]; + +// CNFGColor(((x+y)&1)?0xFFFFFF:0x000000); + + float bright = tdDot( normal, lightdir ); + if( bright < 0 ) bright = 0; + CNFGColor( 0xff | ( ( (int)( bright * 90 ) ) << 24 ) ); + +// CNFGTackPoly( &pto[0], 3 ); CNFGTackPoly( &pto[3], 3 ); + CNFGTackSegment( pta[0], pta[1], ptb[0], ptb[1] ); + CNFGTackSegment( pta[0], pta[1], ptc[0], ptc[1] ); + CNFGTackSegment( ptb[0], ptb[1], ptc[0], ptc[1] ); + + } +} + + +int HandleDestroy() +{ + printf( "Destroying\n" ); + return 0; +} + +volatile int suspended; + +void HandleSuspend() +{ + suspended = 1; +} + +void HandleResume() +{ + suspended = 0; +} + +/* +void AudioCallback( struct CNFADriver * sd, short * out, short * in, int framesp, int framesr ) +{ + memset(out, 0, framesp*sizeof(uint16_t)); + if(suspended) return; + if(!buttonstate[1]) return; // play audio only if ~touching with two fingers + audio_frequency = 440; + for(uint32_t i = 0; i < framesp; i++) { + int16_t sample = INT16_MAX * sin(audio_frequency*(2*M_PI)*(stream_offset+i)/SAMPLE_RATE); + out[i] = sample; + } + stream_offset += framesp; +} +*/ + +void MakeNotification( const char * channelID, const char * channelName, const char * title, const char * message ) +{ + static int id; + id++; + + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jstring channelIDStr = env->NewStringUTF( ENVCALL channelID ); + jstring channelNameStr = env->NewStringUTF( ENVCALL channelName ); + + // Runs getSystemService(Context.NOTIFICATION_SERVICE). + jclass NotificationManagerClass = env->FindClass( ENVCALL "android/app/NotificationManager" ); + jclass activityClass = env->GetObjectClass( ENVCALL gapp->activity->clazz ); + jmethodID MethodGetSystemService = env->GetMethodID( ENVCALL activityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); + jstring notificationServiceName = env->NewStringUTF( ENVCALL "notification" ); + jobject notificationServiceObj = env->CallObjectMethod( ENVCALL gapp->activity->clazz, MethodGetSystemService, notificationServiceName); + + // create the Notification channel. + jclass notificationChannelClass = env->FindClass( ENVCALL "android/app/NotificationChannel" ); + jmethodID notificationChannelConstructorID = env->GetMethodID( ENVCALL notificationChannelClass, "", "(Ljava/lang/String;Ljava/lang/CharSequence;I)V" ); + jobject notificationChannelObj = env->NewObject( ENVCALL notificationChannelClass, notificationChannelConstructorID, channelIDStr, channelNameStr, 3 ); // IMPORTANCE_DEFAULT + jmethodID createNotificationChannelID = env->GetMethodID( ENVCALL NotificationManagerClass, "createNotificationChannel", "(Landroid/app/NotificationChannel;)V" ); + env->CallVoidMethod( ENVCALL notificationServiceObj, createNotificationChannelID, notificationChannelObj ); + + env->DeleteLocalRef( ENVCALL channelNameStr ); + env->DeleteLocalRef( ENVCALL notificationChannelObj ); + + // Create the Notification builder. + jclass classBuilder = env->FindClass( ENVCALL "android/app/Notification$Builder" ); + jstring titleStr = env->NewStringUTF( ENVCALL title ); + jstring messageStr = env->NewStringUTF( ENVCALL message ); + jmethodID eventConstructor = env->GetMethodID( ENVCALL classBuilder, "", "(Landroid/content/Context;Ljava/lang/String;)V" ); + jobject eventObj = env->NewObject( ENVCALL classBuilder, eventConstructor, gapp->activity->clazz, channelIDStr ); + jmethodID setContentTitleID = env->GetMethodID( ENVCALL classBuilder, "setContentTitle", "(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;" ); + jmethodID setContentTextID = env->GetMethodID( ENVCALL classBuilder, "setContentText", "(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;" ); + jmethodID setSmallIconID = env->GetMethodID( ENVCALL classBuilder, "setSmallIcon", "(I)Landroid/app/Notification$Builder;" ); + + // You could do things like setPriority, or setContentIntent if you want it to do something when you click it. + + env->CallObjectMethod( ENVCALL eventObj, setContentTitleID, titleStr ); + env->CallObjectMethod( ENVCALL eventObj, setContentTextID, messageStr ); + env->CallObjectMethod( ENVCALL eventObj, setSmallIconID, 17301504 ); // R.drawable.alert_dark_frame + + // eventObj.build() + jmethodID buildID = env->GetMethodID( ENVCALL classBuilder, "build", "()Landroid/app/Notification;" ); + jobject notification = env->CallObjectMethod( ENVCALL eventObj, buildID ); + + // NotificationManager.notify(...) + jmethodID notifyID = env->GetMethodID( ENVCALL NotificationManagerClass, "notify", "(ILandroid/app/Notification;)V" ); + env->CallVoidMethod( ENVCALL notificationServiceObj, notifyID, id, notification ); + + env->DeleteLocalRef( ENVCALL notification ); + env->DeleteLocalRef( ENVCALL titleStr ); + env->DeleteLocalRef( ENVCALL activityClass ); + env->DeleteLocalRef( ENVCALL messageStr ); + env->DeleteLocalRef( ENVCALL channelIDStr ); + env->DeleteLocalRef( ENVCALL NotificationManagerClass ); + env->DeleteLocalRef( ENVCALL notificationServiceObj ); + env->DeleteLocalRef( ENVCALL notificationServiceName ); + +} + +void HandleThisWindowTermination() +{ + suspended = 1; +} + + +uint32_t randomtexturedata[256*256]; +uint32_t webviewdata[500*500]; +char fromJSBuffer[128]; + +void CheckWebView( void * v ) +{ + static int runno = 0; + WebViewNativeActivityObject * wvn = (WebViewNativeActivityObject*)v; + if( WebViewGetProgress( wvn ) != 100 ) return; + + runno++; + if( runno == 1 ) + { + // The attach (initial) message payload has no meaning. + WebViewPostMessage( wvn, "", 1 ); + } + else + { + // Invoke JavaScript, which calls a function to send a webmessage + // back into C land. + WebViewExecuteJavascript( wvn, "SendMessageToC();" ); + + // Send a WebMessage into the JavaScript code. + char st[128]; + sprintf( st, "Into JavaScript %d\n", runno ); + WebViewPostMessage( wvn, st, 0 ); + } +} + +jobject g_attachLooper; + +void SetupWebView( void * v ) +{ + WebViewNativeActivityObject * wvn = (WebViewNativeActivityObject*)v; + + + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + while( g_attachLooper == 0 ) usleep(1); + WebViewCreate( wvn, "file:///android_asset/index.html", g_attachLooper, 500, 500 ); + //WebViewCreate( wvn, "about:blank", g_attachLooper, 500, 500 ); +} + + +pthread_t jsthread; + +void * JavscriptThread( void * v ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + // Create a looper on this thread... + jclass LooperClass = env->FindClass(envptr, "android/os/Looper"); + jmethodID myLooperMethod = env->GetStaticMethodID(envptr, LooperClass, "myLooper", "()Landroid/os/Looper;"); + jobject thisLooper = env->CallStaticObjectMethod( envptr, LooperClass, myLooperMethod ); + if( !thisLooper ) + { + jmethodID prepareMethod = env->GetStaticMethodID(envptr, LooperClass, "prepare", "()V"); + env->CallStaticVoidMethod( envptr, LooperClass, prepareMethod ); + thisLooper = env->CallStaticObjectMethod( envptr, LooperClass, myLooperMethod ); + g_attachLooper = env->NewGlobalRef(envptr, thisLooper); + } + + jmethodID getQueueMethod = env->GetMethodID( envptr, LooperClass, "getQueue", "()Landroid/os/MessageQueue;" ); + jobject lque = env->CallObjectMethod( envptr, g_attachLooper, getQueueMethod ); + + jclass MessageQueueClass = env->FindClass(envptr, "android/os/MessageQueue"); + jmethodID nextMethod = env->GetMethodID( envptr, MessageQueueClass, "next", "()Landroid/os/Message;" ); + + jclass MessageClass = env->FindClass(envptr, "android/os/Message"); + jfieldID objid = env->GetFieldID( envptr, MessageClass, "obj", "Ljava/lang/Object;" ); + jclass PairClass = env->FindClass(envptr, "android/util/Pair"); + jfieldID pairfirst = env->GetFieldID( envptr, PairClass, "first", "Ljava/lang/Object;" ); + + while(1) + { + // Instead of using Looper::loop(), we just call next on the looper object. + jobject msg = env->CallObjectMethod( envptr, lque, nextMethod ); + jobject innerObj = env->GetObjectField( envptr, msg, objid ); + const char * name; + jstring strObj; + jclass innerClass; + + // Check Object Type + { + innerClass = env->GetObjectClass( envptr, innerObj ); + jmethodID mid = env->GetMethodID( envptr, innerClass, "getClass", "()Ljava/lang/Class;"); + jobject clsObj = env->CallObjectMethod( envptr, innerObj, mid ); + jclass clazzz = env->GetObjectClass( envptr, clsObj ); + mid = env->GetMethodID(envptr, clazzz, "getName", "()Ljava/lang/String;"); + strObj = (jstring)env->CallObjectMethod( envptr, clsObj, mid); + name = env->GetStringUTFChars( envptr, strObj, 0); + env->DeleteLocalRef( envptr, clsObj ); + env->DeleteLocalRef( envptr, clazzz ); + } + + if( strcmp( name, "z5" ) == 0 ) + { + // Special, Some Androids (notably Meta Quest) use a different private message type. + jfieldID mstrf = env->GetFieldID( envptr, innerClass, "a", "[B" ); + jbyteArray jba = (jstring)env->GetObjectField(envptr, innerObj, mstrf ); + int len = env->GetArrayLength( envptr, jba ); + jboolean isCopy = 0; + jbyte * bufferPtr = env->GetByteArrayElements(envptr, jba, &isCopy); + + if( len >= 6 ) + { + const char *descr = (const char*)bufferPtr + 6; + char tcpy[len-5]; + memcpy( tcpy, descr, len-6 ); + tcpy[len-6] = 0; + snprintf( fromJSBuffer, sizeof( fromJSBuffer)-1, "WebMessage: %s\n", tcpy ); + + env->DeleteLocalRef( envptr, jba ); + } + } + else + { + jobject MessagePayload = env->GetObjectField( envptr, innerObj, pairfirst ); + // MessagePayload is a org.chromium.content_public.browser.MessagePayload + + jclass mpclass = env->GetObjectClass( envptr, MessagePayload ); + + // Get field "b" which is the web message payload. + // If you are using binary sockets, it will be in `c` and be a byte array. + jfieldID mstrf = env->GetFieldID( envptr, mpclass, "b", "Ljava/lang/String;" ); + jstring strObjDescr = (jstring)env->GetObjectField(envptr, MessagePayload, mstrf ); + + const char *descr = env->GetStringUTFChars( envptr, strObjDescr, 0); + snprintf( fromJSBuffer, sizeof( fromJSBuffer)-1, "WebMessage: %s\n", descr ); + + env->ReleaseStringUTFChars(envptr, strObjDescr, descr); + env->DeleteLocalRef( envptr, strObjDescr ); + env->DeleteLocalRef( envptr, MessagePayload ); + env->DeleteLocalRef( envptr, mpclass ); + } + env->ReleaseStringUTFChars(envptr, strObj, name); + env->DeleteLocalRef( envptr, strObj ); + env->DeleteLocalRef( envptr, msg ); + env->DeleteLocalRef( envptr, innerObj ); + env->DeleteLocalRef( envptr, innerClass ); + } +} + +void SetupJSThread() +{ + pthread_create( &jsthread, 0, JavscriptThread, 0 ); +} + +int main( int argc, char ** argv ) +{ + int x, y; + double ThisTime; + double LastFPSTime = OGGetAbsoluteTime(); + + Log( "Starting Up" ); + + CNFGBGColor = 0x000040ff; + CNFGSetupFullscreen( "Test Bench", 0 ); + + HandleWindowTermination = HandleThisWindowTermination; + + for( x = 0; x < HMX; x++ ) + for( y = 0; y < HMY; y++ ) + { + Heightmap[x+y*HMX] = tdPerlin2D( x, y )*8.; + } + + const char * assettext = "Not Found"; + AAsset * file = AAssetManager_open( gapp->activity->assetManager, "asset.txt", AASSET_MODE_BUFFER ); + if( file ) + { + size_t fileLength = AAsset_getLength(file); + char * temp = (char*)malloc( fileLength + 1); + memcpy( temp, AAsset_getBuffer( file ), fileLength ); + temp[fileLength] = 0; + assettext = temp; + } + + SetupIMU(); + + // Disabled, for now. + //InitCNFAAndroid( AudioCallback, "A Name", SAMPLE_RATE, 0, 1, 0, SAMPLE_COUNT, 0, 0, 0 ); + + SetupJSThread(); + + // Create webview and wait for its completion + RunCallbackOnUIThread( SetupWebView, &MyWebView ); + while( !MyWebView.WebViewObject ) usleep(1); + + Log( "Startup Complete" ); + + while(1) + { + int i; + iframeno++; + + if( iframeno == 200 ) + { + MakeNotification( "default", "rawdraw alerts", "rawdraw", "Hit frame two hundred\nNew Line" ); + } + + CNFGHandleInput(); + AccCheck(); + + if( suspended ) { usleep(50000); continue; } + + RunCallbackOnUIThread( (void(*)(void*))WebViewRequestRenderToCanvas, &MyWebView ); + RunCallbackOnUIThread( CheckWebView, &MyWebView ); + + CNFGClearFrame(); + CNFGColor( 0xFFFFFFFF ); + CNFGGetDimensions( &screenx, &screeny ); + + // Mesh in background + CNFGSetLineWidth( 9 ); + DrawHeightmap(); + CNFGPenX = 0; CNFGPenY = 400; + CNFGColor( 0xffffffff ); + CNFGDrawText( assettext, 15 ); + CNFGFlushRender(); + + CNFGPenX = 0; CNFGPenY = 480; + char st[50]; + sprintf( st, "%dx%d %d %d %d %d %d %d\n%d %d\n%5.2f %5.2f %5.2f %d", screenx, screeny, lastbuttonx, lastbuttony, lastmotionx, lastmotiony, lastkey, lastkeydown, lastbid, lastmask, accx, accy, accz, accs ); + CNFGDrawText( st, 10 ); + CNFGSetLineWidth( 2 ); + + // Square behind text + CNFGColor( 0x303030ff ); + CNFGTackRectangle( 600, 0, 950, 350); + + CNFGPenX = 10; CNFGPenY = 10; + + // Text + CNFGColor( 0xffffffff ); + for( i = 0; i < 1; i++ ) + { + int c; + char tw[2] = { 0, 0 }; + for( c = 0; c < 256; c++ ) + { + tw[0] = c; + + CNFGPenX = ( c % 16 ) * 20+606; + CNFGPenY = ( c / 16 ) * 20+5; + CNFGDrawText( tw, 4 ); + } + } + + // Green triangles + CNFGPenX = 0; + CNFGPenY = 0; + CNFGColor( 0x00FF00FF ); + + for( i = 0; i < 400; i++ ) + { + RDPoint pp[3]; + pp[0].x = (short)(50*sin((float)(i+iframeno)*.01) + (i%20)*30); + pp[0].y = (short)(50*cos((float)(i+iframeno)*.01) + (i/20)*20)+700; + pp[1].x = (short)(20*sin((float)(i+iframeno)*.01) + (i%20)*30); + pp[1].y = (short)(50*cos((float)(i+iframeno)*.01) + (i/20)*20)+700; + pp[2].x = (short)(10*sin((float)(i+iframeno)*.01) + (i%20)*30); + pp[2].y = (short)(30*cos((float)(i+iframeno)*.01) + (i/20)*20)+700; + CNFGTackPoly( pp, 3 ); + } + + // Last WebMessage + CNFGColor( 0xFFFFFFFF ); + CNFGPenX = 0; CNFGPenY = 100; + CNFGDrawText( fromJSBuffer, 6 ); + + int x, y; + for( y = 0; y < 256; y++ ) + for( x = 0; x < 256; x++ ) + randomtexturedata[x+y*256] = x | ((x*394543L+y*355+iframeno*3)<<8); + CNFGBlitImage( randomtexturedata, 100, 600, 256, 256 ); + + WebViewNativeGetPixels( &MyWebView, webviewdata, 500, 500 ); + CNFGBlitImage( webviewdata, 500, 640, 500, 500 ); + + frames++; + //On Android, CNFGSwapBuffers must be called, and CNFGUpdateScreenWithBitmap does not have an implied framebuffer swap. + CNFGSwapBuffers(); + + ThisTime = OGGetAbsoluteTime(); + if( ThisTime > LastFPSTime + 1 ) + { + printf( "FPS: %d\n", frames ); + frames = 0; + LastFPSTime+=1; + } + + } + + return(0); +} + diff --git a/app/src/nativecode/webview_native_activity.h b/app/src/nativecode/webview_native_activity.h new file mode 100644 index 0000000..b850540 --- /dev/null +++ b/app/src/nativecode/webview_native_activity.h @@ -0,0 +1,312 @@ +#ifndef _WEBVIEW_NATIVE_ACTIVITY +#define _WEBVIEW_NATIVE_ACTIVITY + +#include + +extern volatile jobject g_objRootView; + +typedef struct +{ + jobject WebViewObject; + jobjectArray MessageChannels; + jobject BackingBitmap; + jobject BackingCanvas; + int updated_canvas; + int w, h; +} WebViewNativeActivityObject; + +// Must be called from main thread + +// initial_url = "about:blank" for a java-script only page. Can also be file:///android_asset/test.html. +// Loading from "about:blank" will make the page ready almost immediately, otherwise it's about 50ms to load. +// useLooperForWebMessages is required, and must be a global jobject of your preferred looper to handle webmessages. +void WebViewCreate( WebViewNativeActivityObject * w, const char * initial_url, jobject useLooperForWebMessages, int pw, int ph ); +void WebViewExecuteJavascript( WebViewNativeActivityObject * obj, const char * js ); + +// Note: Do not initialize until page reports as 100% loaded, with WebViewGetProgress. +void WebViewPostMessage( WebViewNativeActivityObject * obj, const char * mesg, int initial ); +void WebViewRequestRenderToCanvas( WebViewNativeActivityObject * obj ); +int WebViewGetProgress( WebViewNativeActivityObject * obj ); +char * WebViewGetLastWindowTitle( WebViewNativeActivityObject * obj ); + +// Can be called from any thread. +void WebViewNativeGetPixels( WebViewNativeActivityObject * obj, uint32_t * pixel_data, int w, int h ); + +#ifdef WEBVIEW_NATIVE_ACTIVITY_IMPLEMENTATION + +volatile jobject g_objRootView; + +void WebViewCreate( WebViewNativeActivityObject * w, const char * initial_url, jobject useLooperForWebMessages, int pw, int ph ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + jobject clazz = gapp->activity->clazz; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + if( g_objRootView == 0 ) + { + jclass ViewClass = env->FindClass(envptr, "android/widget/LinearLayout"); + jmethodID ViewConstructor = env->GetMethodID(envptr, ViewClass, "", "(Landroid/content/Context;)V"); + jclass activityClass = env->FindClass(envptr, "android/app/Activity"); + jmethodID activityGetContextMethod = env->GetMethodID(envptr, activityClass, "getApplicationContext", "()Landroid/content/Context;"); + jobject contextObject = env->CallObjectMethod(envptr, clazz, activityGetContextMethod); + jobject jv = env->NewObject(envptr, ViewClass, ViewConstructor, contextObject ); + g_objRootView = env->NewGlobalRef(envptr, jv); + + jclass clszz = env->GetObjectClass(envptr,clazz); + jmethodID setContentViewMethod = env->GetMethodID(envptr, clszz, "setContentView", "(Landroid/view/View;)V"); + env->CallVoidMethod(envptr,clazz, setContentViewMethod, g_objRootView ); + } + + jclass WebViewClass = env->FindClass(envptr, "android/webkit/WebView"); + jclass activityClass = env->FindClass(envptr, "android/app/Activity"); + jmethodID activityGetContextMethod = env->GetMethodID(envptr, activityClass, "getApplicationContext", "()Landroid/content/Context;"); + jobject contextObject = env->CallObjectMethod(envptr, clazz, activityGetContextMethod); + + jmethodID WebViewConstructor = env->GetMethodID(envptr, WebViewClass, "", "(Landroid/content/Context;)V"); + jobject wvObj = env->NewObject(envptr, WebViewClass, WebViewConstructor, contextObject ); + + // Unknown reason why - if you don't first load about:blank, it sometimes doesn't render right? + // Even more annoying - you can't pre-use loadUrl if you want to use message channels. + jmethodID WebViewLoadBaseURLMethod = env->GetMethodID(envptr, WebViewClass, "loadDataWithBaseURL", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + jstring strul = env->NewStringUTF( envptr, "http://example.com" ); + jstring strdata = env->NewStringUTF( envptr, "not-yet-loaded" ); + jstring strmime = env->NewStringUTF( envptr, "text/html" ); + jstring strencoding = env->NewStringUTF( envptr, "utf8" ); + jstring strhistoryurl = env->NewStringUTF( envptr, "" ); + env->CallVoidMethod(envptr, wvObj, WebViewLoadBaseURLMethod, strul, strdata, strmime, strencoding, strhistoryurl ); + env->DeleteLocalRef( envptr, strul ); + env->DeleteLocalRef( envptr, strdata ); + env->DeleteLocalRef( envptr, strmime ); + env->DeleteLocalRef( envptr, strencoding ); + env->DeleteLocalRef( envptr, strhistoryurl ); + + // You have to switch to this to be able to run javascript code. + jmethodID LoadURLMethod = env->GetMethodID(envptr, WebViewClass, "loadUrl", "(Ljava/lang/String;)V"); + jstring strjs = env->NewStringUTF( envptr, initial_url ); + env->CallVoidMethod(envptr, wvObj, LoadURLMethod, strjs ); + env->DeleteLocalRef( envptr, strjs ); + + jmethodID WebViewGetSettingMethod = env->GetMethodID(envptr, WebViewClass, "getSettings", "()Landroid/webkit/WebSettings;"); + jobject websettings = env->CallObjectMethod(envptr, wvObj, WebViewGetSettingMethod ); + jclass WebSettingsClass = env->FindClass(envptr, "android/webkit/WebSettings"); + jmethodID setJavaScriptEnabledMethod = env->GetMethodID(envptr, WebSettingsClass, "setJavaScriptEnabled", "(Z)V"); + env->CallVoidMethod( envptr, websettings, setJavaScriptEnabledMethod, true ); + env->DeleteLocalRef( envptr, websettings ); + + jmethodID setMeasuredDimensionMethodID = env->GetMethodID(envptr, WebViewClass, "setMeasuredDimension", "(II)V"); + env->CallVoidMethod(envptr, wvObj, setMeasuredDimensionMethodID, pw, ph ); + + jclass ViewClass = env->FindClass(envptr, "android/widget/LinearLayout"); + jmethodID addViewMethod = env->GetMethodID(envptr, ViewClass, "addView", "(Landroid/view/View;)V"); + env->CallVoidMethod( envptr, g_objRootView, addViewMethod, wvObj ); + + jclass WebMessagePortClass = env->FindClass(envptr, "android/webkit/WebMessagePort" ); + jmethodID createWebMessageChannelMethod = env->GetMethodID(envptr, WebViewClass, "createWebMessageChannel", "()[Landroid/webkit/WebMessagePort;"); + jobjectArray messageChannels = env->CallObjectMethod( envptr, wvObj, createWebMessageChannelMethod ); + jobject mc0 = env->GetObjectArrayElement(envptr, messageChannels, 0); // MC1 is handed over to javascript. + + jclass HandlerClassType = env->FindClass(envptr, "android/os/Handler" ); + jmethodID HandlerObjectConstructor = env->GetMethodID(envptr, HandlerClassType, "", "(Landroid/os/Looper;)V"); + jobject handlerObject = env->NewObject( envptr, HandlerClassType, HandlerObjectConstructor, useLooperForWebMessages ); + handlerObject = env->NewGlobalRef(envptr, handlerObject); + jmethodID setWebMessageCallbackMethod = env->GetMethodID( envptr, WebMessagePortClass, "setWebMessageCallback", "(Landroid/webkit/WebMessagePort$WebMessageCallback;Landroid/os/Handler;)V" ); + + // Only can receive messages on MC0 + env->CallVoidMethod( envptr, mc0, setWebMessageCallbackMethod, 0, handlerObject ); + + // Generate backing bitmap and canvas. + jclass CanvasClass = env->FindClass(envptr, "android/graphics/Canvas"); + jclass BitmapClass = env->FindClass(envptr, "android/graphics/Bitmap"); + jmethodID createBitmap = env->GetStaticMethodID(envptr, BitmapClass, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); + jclass bmpCfgCls = env->FindClass(envptr, "android/graphics/Bitmap$Config"); + jstring bitmap_mode = env->NewStringUTF(envptr, "ARGB_8888"); + jmethodID bmpClsValueOfMid = env->GetStaticMethodID(envptr, bmpCfgCls, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;"); + jobject jBmpCfg = env->CallStaticObjectMethod(envptr, bmpCfgCls, bmpClsValueOfMid, bitmap_mode); + jobject bitmap = env->CallStaticObjectMethod( envptr, BitmapClass, createBitmap, pw, ph, jBmpCfg ); + jmethodID canvasConstructor = env->GetMethodID(envptr, CanvasClass, "", "(Landroid/graphics/Bitmap;)V"); + jobject canvas = env->NewObject(envptr, CanvasClass, canvasConstructor, bitmap ); + + env->DeleteLocalRef( envptr, CanvasClass ); + env->DeleteLocalRef( envptr, BitmapClass ); + env->DeleteLocalRef( envptr, bmpCfgCls ); + env->DeleteLocalRef( envptr, bitmap_mode ); + + w->BackingBitmap = env->NewGlobalRef(envptr, bitmap ); + w->BackingCanvas = env->NewGlobalRef(envptr, canvas ); + w->WebViewObject = env->NewGlobalRef(envptr, wvObj); + w->MessageChannels = env->NewGlobalRef(envptr, messageChannels); + w->w = pw; + w->h = ph; + + env->DeleteLocalRef( envptr, WebViewClass ); + env->DeleteLocalRef( envptr, activityClass ); + env->DeleteLocalRef( envptr, WebSettingsClass ); + env->DeleteLocalRef( envptr, ViewClass ); +} + +int WebViewGetProgress( WebViewNativeActivityObject * obj ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jclass WebViewClass = env->FindClass(envptr, "android/webkit/WebView"); + jmethodID WebViewProgress = env->GetMethodID(envptr, WebViewClass, "getProgress", "()I"); + int ret = env->CallIntMethod( envptr, obj->WebViewObject, WebViewProgress ); + env->DeleteLocalRef( envptr, WebViewClass ); + return ret; +} + +void WebViewPostMessage( WebViewNativeActivityObject * w, const char * mesg, int initial ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jclass WebMessagePortClass = env->FindClass(envptr, "android/webkit/WebMessagePort" ); + jclass WebViewClass = env->FindClass(envptr, "android/webkit/WebView"); + jclass WebMessageClass = env->FindClass(envptr, "android/webkit/WebMessage" ); + + jstring strjs = env->NewStringUTF( envptr, mesg ); + + if( initial ) + { + jobject mc1 = env->GetObjectArrayElement(envptr, w->MessageChannels, 1); + jmethodID WebMessageConstructor = env->GetMethodID(envptr, WebMessageClass, "", "(Ljava/lang/String;[Landroid/webkit/WebMessagePort;)V"); + + //https://stackoverflow.com/questions/41753104/how-do-you-use-webmessageport-as-an-alternative-to-addjavascriptinterface + // Only on initial hop do we want to post the root webmessage, which hooks up out webmessage port. + jmethodID postMessageMethod = env->GetMethodID(envptr, WebViewClass, "postWebMessage", "(Landroid/webkit/WebMessage;Landroid/net/Uri;)V"); + + // Need to generate a new message channel array. + jobjectArray jsUseWebPorts = env->NewObjectArray( envptr, 1, WebMessagePortClass, mc1); + + // Need Uri.EMPTY + jclass UriClass = env->FindClass(envptr, "android/net/Uri" ); + jfieldID EmptyField = env->GetStaticFieldID( envptr, UriClass, "EMPTY", "Landroid/net/Uri;" ); + jobject EmptyURI = env->GetStaticObjectField( envptr, UriClass, EmptyField ); + + + jobject newwm = env->NewObject(envptr, WebMessageClass, WebMessageConstructor, strjs, jsUseWebPorts ); + env->CallVoidMethod( envptr, w->WebViewObject, postMessageMethod, newwm, EmptyURI ); + + env->DeleteLocalRef( envptr, jsUseWebPorts ); + env->DeleteLocalRef( envptr, newwm ); + env->DeleteLocalRef( envptr, EmptyURI ); + env->DeleteLocalRef( envptr, UriClass ); + } + else + { + jobject mc0 = env->GetObjectArrayElement(envptr, w->MessageChannels, 0); + jmethodID postMessageMethod = env->GetMethodID(envptr, WebMessagePortClass, "postMessage", "(Landroid/webkit/WebMessage;)V"); + jmethodID WebMessageConstructor = env->GetMethodID(envptr, WebMessageClass, "", "(Ljava/lang/String;)V"); + + jobject newwm = env->NewObject(envptr, WebMessageClass, WebMessageConstructor, strjs ); + env->CallVoidMethod( envptr, mc0, postMessageMethod, newwm ); + + env->DeleteLocalRef( envptr, newwm ); + env->DeleteLocalRef( envptr, mc0 ); + } + + env->DeleteLocalRef( envptr, strjs ); + env->DeleteLocalRef( envptr, WebViewClass ); + env->DeleteLocalRef( envptr, WebMessageClass ); + env->DeleteLocalRef( envptr, WebMessagePortClass ); +} + +void WebViewRequestRenderToCanvas( WebViewNativeActivityObject * obj ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jclass WebViewClass = env->FindClass(envptr, "android/webkit/WebView"); + jmethodID drawMethod = env->GetMethodID(envptr, WebViewClass, "draw", "(Landroid/graphics/Canvas;)V"); + env->CallVoidMethod( envptr, obj->WebViewObject, drawMethod, obj->BackingCanvas ); + env->DeleteLocalRef( envptr, WebViewClass ); +} + +void WebViewNativeGetPixels( WebViewNativeActivityObject * obj, uint32_t * pixel_data, int w, int h ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jclass BitmapClass = env->FindClass(envptr, "android/graphics/Bitmap"); + jobject buffer = env->NewDirectByteBuffer(envptr, pixel_data, obj->w*obj->h*4 ); + jmethodID copyPixelsBufferID = env->GetMethodID( envptr, BitmapClass, "copyPixelsToBuffer", "(Ljava/nio/Buffer;)V" ); + env->CallVoidMethod( envptr, obj->BackingBitmap, copyPixelsBufferID, buffer ); + + int i; + int num = obj->w * obj->h; + for( i = 0; i < num; i++ ) pixel_data[i] = bswap_32( pixel_data[i] ); + + env->DeleteLocalRef( envptr, BitmapClass ); + env->DeleteLocalRef( envptr, buffer ); + + jnii->DetachCurrentThread( jniiptr ); +} + +void WebViewExecuteJavascript( WebViewNativeActivityObject * obj, const char * js ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jclass WebViewClass = env->FindClass(envptr, "android/webkit/WebView"); + jmethodID WebViewEvalJSMethod = env->GetMethodID(envptr, WebViewClass, "evaluateJavascript", "(Ljava/lang/String;Landroid/webkit/ValueCallback;)V"); + + //WebView.evaluateJavascript(String script, ValueCallback resultCallback) + jstring strjs = env->NewStringUTF( envptr, js ); + env->CallVoidMethod( envptr, obj->WebViewObject, WebViewEvalJSMethod, strjs, 0 ); // Tricky: resultCallback = 0, if you try running looper.loop() it will crash - only manually process messages. + env->DeleteLocalRef( envptr, WebViewClass ); + env->DeleteLocalRef( envptr, strjs ); +} + +char * WebViewGetLastWindowTitle( WebViewNativeActivityObject * obj ) +{ + const struct JNINativeInterface * env = 0; + const struct JNINativeInterface ** envptr = &env; + const struct JNIInvokeInterface ** jniiptr = gapp->activity->vm; + const struct JNIInvokeInterface * jnii = *jniiptr; + jnii->AttachCurrentThread( jniiptr, &envptr, NULL); + env = (*envptr); + + jclass WebViewClass = env->FindClass(envptr, "android/webkit/WebView"); + jmethodID getTitle = env->GetMethodID(envptr, WebViewClass, "getTitle", "()Ljava/lang/String;"); + jobject titleObject = env->CallObjectMethod( envptr, obj->WebViewObject, getTitle ); + char *nativeString = strdup( env->GetStringUTFChars(envptr, titleObject, 0) ); + env->DeleteLocalRef( envptr, titleObject ); + env->DeleteLocalRef( envptr, WebViewClass ); + + return nativeString; +} + + +#endif +#endif + diff --git a/build-android-app.sh b/build-android-app.sh index af8c4aa..c9f1256 100755 --- a/build-android-app.sh +++ b/build-android-app.sh @@ -25,7 +25,7 @@ done DockerfileContent(){ cat << 'DOCKERFILEEOF' FROM debian:latest -RUN apt-get update -y && apt-get install -y make openjdk-17-jdk-headless unzip zip wget curl whiptail +RUN apt-get update -y && apt-get install -y make openjdk-17-jdk-headless unzip zip wget curl whiptail build-essential ENV JAVA_HOME="/usr/lib/jvm/java-17-openjdk-amd64/" ENV ANDROID_SDK_ROOT="/app/android-sdk" ENV BUILD_TOOLS_LATEST="$ANDROID_SDK_ROOT/cmdline-tools/latest" @@ -36,7 +36,11 @@ RUN < /bin/makefile-bash-wrapper.sh << 'WRAPPER' #!/bin/bash printf $'\033[0;32m''#----------------------------------------\n'$'\033[0m' >&2 -bash "$@" +bash -e "$@" || { + EXITCODE=$? + printf $'\033[0;31m''ERROR EXITCODE='"$EXITCODE"'\n'$'\033[0m' >&2 + exit $EXITCODE +} printf '\n\n\n\n' >&2 WRAPPER chmod u+x /bin/makefile-bash-wrapper.sh @@ -44,6 +48,7 @@ EOF DOCKERFILEEOF } +printf $'\033[0;33m''$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n'$'\033[0m' diff Dockerfile <(DockerfileContent) 2>/dev/null > /dev/null || { test -f Dockerfile && { read -p 'reset/start Dockerfile[Y/n]' YES