adopt rawdrawandroidRepo to make native app
This commit is contained in:
parent
f83bf386cd
commit
fb057ac80d
76 changed files with 25162 additions and 146 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -4,3 +4,4 @@
|
|||
!/app/obj/.gitkeep
|
||||
/app/app.apk
|
||||
/app/Makefile.app-config
|
||||
/app/output.map
|
||||
|
|
|
|||
73
README.md
73
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
|
||||
|
|
|
|||
|
|
@ -13,11 +13,12 @@ cat > "AndroidManifest.xml" << ANDROIDMANIFEST
|
|||
<uses-sdk android:minSdkVersion="$APP_VERSION_SDK_MIN"
|
||||
android:targetSdkVersion="$APP_VERSION_SDK_TARGET"/>
|
||||
$(sed 's/^/ /' <<< "$APP_PERMISSIONS")
|
||||
<application android:label="$APP_NAME" android:icon="@drawable/appicon">
|
||||
<activity android:name="$APP_PACKAGE.AppActivity"
|
||||
<application android:debuggable="true" android:hasCode="false" android:label="$APP_NAME" android:icon="@drawable/appicon">
|
||||
<activity android:name="android.app.NativeActivity"
|
||||
android:exported="true"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
android:label="$APP_NAME">
|
||||
<meta-data android:name="android.app.lib_name" android:value="nativecode"/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
|
@ -26,4 +27,3 @@ $(sed 's/^/ /' <<< "$APP_PERMISSIONS")
|
|||
</application>
|
||||
</manifest>
|
||||
ANDROIDMANIFEST
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
90
app/Makefile
90
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...)
|
||||
|
|
|
|||
|
|
@ -18,23 +18,72 @@
|
|||
</style>
|
||||
<body>
|
||||
<h1>Webview</h1>
|
||||
Test page
|
||||
<br>
|
||||
<div ID=counter1></div>
|
||||
<br>
|
||||
<div ID=counter2></div>
|
||||
<noscript>
|
||||
<h2> no javascript </h2>
|
||||
</noscript>
|
||||
<script>
|
||||
|
||||
let i = 0;
|
||||
|
||||
var webMessagePort;
|
||||
|
||||
function SendMessageToC() {
|
||||
if( webMessagePort )
|
||||
{
|
||||
webMessagePort.postMessage( "js->c: " + i );
|
||||
document.getElementById( "counter1" ).innerHTML = i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
document.getElementById( "counter1" ).innerHTML = "no port connected";
|
||||
}
|
||||
}
|
||||
|
||||
onmessage = function (e) {
|
||||
webMessagePort = e.ports[0];
|
||||
|
||||
webMessagePort.onmessage = function (f)
|
||||
{
|
||||
document.getElementById( "counter2" ).innerHTML = f.data;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
<script>
|
||||
window.addEventListener("load",()=>{
|
||||
document.body.style.backgroundColor="#ffa";
|
||||
},false);
|
||||
|
||||
setTimeout(()=>{
|
||||
window.addEventListener("error",(error)=>{
|
||||
document.body.innerHTML = "<h1>error</h1><pre>" +
|
||||
document.body.innerHTML = "<h1>aerror</h1><pre>" +
|
||||
error.filename +
|
||||
"\nline:" + error.lineno +
|
||||
"\n"+error.message +"</pre>";
|
||||
},false);
|
||||
},10000);
|
||||
|
||||
window.addEventListener("load",()=>{
|
||||
var count = localStorage.getItem("app-opened-count")|| 0;
|
||||
count++;
|
||||
localStorage.setItem("app-opened-count",count);
|
||||
var h2 = document.createElement("h2");
|
||||
h2.textContent = "Javascript works! (app was opened " + count + " times)";
|
||||
h2.style.animation="wobble 1s ease-in-out 0s 1 forwards normal running"
|
||||
document.body.appendChild(h2);
|
||||
},false);
|
||||
|
||||
|
||||
// window.addEventListener("load",()=>{
|
||||
// var count;
|
||||
// if( localStorage){
|
||||
// count = localStorage.getItem("app-opened-count")|| 0;
|
||||
// count++;
|
||||
// localStorage.setItem("app-opened-count",count);
|
||||
// } else {
|
||||
// count = "NO LOCALSTORAGE SETUP"
|
||||
// }
|
||||
// var h2 = document.createElement("h2");
|
||||
// h2.textContent = "Javascript works! (app was opened " + count + " times)";
|
||||
// h2.style.animation="wobble 1s ease-in-out 0s 1 forwards normal running"
|
||||
// document.body.appendChild(h2);
|
||||
// },false);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -6,4 +6,10 @@
|
|||
<path
|
||||
android:pathData="M95 50A45 45 0 0 1 5 50A45 45 0 0 1 95 50Z"
|
||||
android:fillColor="#FF0000" />
|
||||
<path
|
||||
android:pathData="M75 50A25 25 0 0 1 5 50A25 25 0 0 1 75 50Z"
|
||||
android:fillColor="#FFFF00" />
|
||||
<path
|
||||
android:pathData="M60 50A10 10 0 0 1 5 50A10 10 0 0 1 60 50Z"
|
||||
android:fillColor="#0000EE" />
|
||||
</vector>
|
||||
|
|
|
|||
21
app/src/nativecode/LICENSE
Normal file
21
app/src/nativecode/LICENSE
Normal file
|
|
@ -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.
|
||||
526
app/src/nativecode/android_native_app_glue.c
Normal file
526
app/src/nativecode/android_native_app_glue.c
Normal file
|
|
@ -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 <jni.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include "android_native_app_glue.h"
|
||||
#include <android/log.h>
|
||||
#include <android/native_activity.h>
|
||||
|
||||
#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) );
|
||||
}
|
||||
|
||||
376
app/src/nativecode/android_native_app_glue.h
Normal file
376
app/src/nativecode/android_native_app_glue.h
Normal file
|
|
@ -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 <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <android/configuration.h>
|
||||
#include <android/looper.h>
|
||||
#include <android/native_activity.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The native activity interface provided by <android/native_activity.h>
|
||||
* 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 */
|
||||
210
app/src/nativecode/android_usb_devices.c
Normal file
210
app/src/nativecode/android_usb_devices.c
Normal file
|
|
@ -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<String, UsbDevice> deviceList = mManager.getDeviceList();
|
||||
jmethodID MethodgetDeviceList = env->GetMethodID( envptr, ClassUsbManager, "getDeviceList", "()Ljava/util/HashMap;" );
|
||||
jobject deviceList = env->CallObjectMethod( envptr, manager, MethodgetDeviceList );
|
||||
|
||||
//Iterator<UsbDevice> 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, "<init>", "(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;
|
||||
}
|
||||
|
||||
17
app/src/nativecode/android_usb_devices.h
Normal file
17
app/src/nativecode/android_usb_devices.h
Normal file
|
|
@ -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 <asset_manager.h>
|
||||
#include <asset_manager_jni.h>
|
||||
#include <android_native_app_glue.h>
|
||||
|
||||
int RequestPermissionOrGetConnectionFD( char * debug_status, uint16_t vid, uint16_t pid );
|
||||
void DisconnectUSB(); //Disconnect from USB
|
||||
|
||||
extern jobject deviceConnection;
|
||||
extern int deviceConnectionFD;
|
||||
|
||||
#endif
|
||||
|
||||
3
app/src/nativecode/cnfa/.gitignore
vendored
Normal file
3
app/src/nativecode/cnfa/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
os_generic.h
|
||||
example
|
||||
|
||||
128
app/src/nativecode/cnfa/CNFA.c
Normal file
128
app/src/nativecode/cnfa/CNFA.c
Normal file
|
|
@ -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 <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
||||
|
||||
|
||||
122
app/src/nativecode/cnfa/CNFA.h
Normal file
122
app/src/nativecode/cnfa/CNFA.h
Normal file
|
|
@ -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
|
||||
|
||||
374
app/src/nativecode/cnfa/CNFA_alsa.c
Normal file
374
app/src/nativecode/cnfa/CNFA_alsa.c
Normal file
|
|
@ -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 <alsa/asoundlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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 );
|
||||
|
||||
319
app/src/nativecode/cnfa/CNFA_android.c
Normal file
319
app/src/nativecode/cnfa/CNFA_android.c
Normal file
|
|
@ -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 <pthread.h> //Using android threads not os_generic threads.
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//based on https://github.com/android/ndk-samples/blob/master/native-audio/app/src/main/cpp/native-audio-jni.c
|
||||
|
||||
// for native audio
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
|
||||
#include <android_native_app_glue.h>
|
||||
#include <jni.h>
|
||||
#include <native_activity.h>
|
||||
|
||||
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 );
|
||||
|
||||
46
app/src/nativecode/cnfa/CNFA_null.c
Normal file
46
app/src/nativecode/cnfa/CNFA_null.c
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
//Copyright 2015-2020 <>< Charles Lohr under the ColorChord License.
|
||||
|
||||
#include "CNFA.h"
|
||||
#include "os_generic.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
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 );
|
||||
|
||||
308
app/src/nativecode/cnfa/CNFA_pulse.c
Normal file
308
app/src/nativecode/cnfa/CNFA_pulse.c
Normal file
|
|
@ -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 <stdlib.h>
|
||||
|
||||
#include <pulse/simple.h>
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <pulse/error.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 );
|
||||
|
||||
|
||||
275
app/src/nativecode/cnfa/CNFA_sun.c
Normal file
275
app/src/nativecode/cnfa/CNFA_sun.c
Normal file
|
|
@ -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 <sys/audioio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
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 );
|
||||
|
||||
514
app/src/nativecode/cnfa/CNFA_wasapi.c
Normal file
514
app/src/nativecode/cnfa/CNFA_wasapi.c
Normal file
|
|
@ -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 <InitGuid.h>
|
||||
#include <audioclient.h> // Render and capturing audio
|
||||
#include <mmdeviceapi.h> // Audio device handling
|
||||
#include <Functiondiscoverykeys_devpkey.h> // Property keys for audio devices
|
||||
#include <avrt.h> // 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);
|
||||
636
app/src/nativecode/cnfa/CNFA_wasapi_utils.h
Normal file
636
app/src/nativecode/cnfa/CNFA_wasapi_utils.h
Normal file
|
|
@ -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
|
||||
258
app/src/nativecode/cnfa/CNFA_winmm.c
Normal file
258
app/src/nativecode/cnfa/CNFA_winmm.c
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
//Copyright 2015-2020 <>< Charles Lohr under the ColorChord License, MIT/x11 license or NewBSD Licenses.
|
||||
|
||||
#include <windows.h>
|
||||
#include "CNFA.h"
|
||||
#include "os_generic.h"
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <mmsystem.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
//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;i<BUFFS;i++)
|
||||
{
|
||||
waveInUnprepareHeader(r->hMyWaveIn,&(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;i<BUFFS;i++)
|
||||
{
|
||||
waveInUnprepareHeader(r->hMyWaveIn,&(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;i<BUFFS;i++)
|
||||
{
|
||||
memset( &(r->WavBuffIn[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;i<BUFFS;i++)
|
||||
{
|
||||
memset( &(r->WavBuffOut[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 );
|
||||
|
||||
21
app/src/nativecode/cnfa/LICENSE
Normal file
21
app/src/nativecode/cnfa/LICENSE
Normal file
|
|
@ -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.
|
||||
34
app/src/nativecode/cnfa/Makefile
Normal file
34
app/src/nativecode/cnfa/Makefile
Normal file
|
|
@ -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
|
||||
|
||||
75
app/src/nativecode/cnfa/README.md
Normal file
75
app/src/nativecode/cnfa/README.md
Normal file
|
|
@ -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
|
||||
```
|
||||
65
app/src/nativecode/cnfa/example.c
Normal file
65
app/src/nativecode/cnfa/example.c
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
// 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 );
|
||||
}
|
||||
|
||||
27
app/src/nativecode/cnfa/wave_player/Makefile
Normal file
27
app/src/nativecode/cnfa/wave_player/Makefile
Normal file
|
|
@ -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 $@
|
||||
117
app/src/nativecode/cnfa/wave_player/wavDefs.h
Normal file
117
app/src/nativecode/cnfa/wave_player/wavDefs.h
Normal file
|
|
@ -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 <stdint.h>
|
||||
|
||||
//#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_
|
||||
389
app/src/nativecode/cnfa/wave_player/wav_player.c
Normal file
389
app/src/nativecode/cnfa/wave_player/wav_player.c
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
/*
|
||||
* Sam Ellicott - 09-06-22
|
||||
* CNFA demo wave file player
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#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 <wave file>\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;
|
||||
}
|
||||
}
|
||||
41
app/src/nativecode/rawdraw/.github/workflows/github-actions.yml
vendored
Normal file
41
app/src/nativecode/rawdraw/.github/workflows/github-actions.yml
vendored
Normal file
|
|
@ -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
|
||||
|
||||
17
app/src/nativecode/rawdraw/.gitignore
vendored
Normal file
17
app/src/nativecode/rawdraw/.gitignore
vendored
Normal file
|
|
@ -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
|
||||
28
app/src/nativecode/rawdraw/CNFG.c
Normal file
28
app/src/nativecode/rawdraw/CNFG.c
Normal file
|
|
@ -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
|
||||
|
||||
545
app/src/nativecode/rawdraw/CNFG.h
Normal file
545
app/src/nativecode/rawdraw/CNFG.h
Normal file
|
|
@ -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 <stdint.h>
|
||||
|
||||
//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 <math.h>
|
||||
#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
|
||||
|
||||
542
app/src/nativecode/rawdraw/CNFG3D.c
Normal file
542
app/src/nativecode/rawdraw/CNFG3D.c
Normal file
|
|
@ -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 <string.h>
|
||||
#include <stdio.h>
|
||||
#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
|
||||
217
app/src/nativecode/rawdraw/CNFGAndroid.h
Normal file
217
app/src/nativecode/rawdraw/CNFGAndroid.h
Normal file
|
|
@ -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 <jni.h>
|
||||
|
||||
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
|
||||
|
||||
866
app/src/nativecode/rawdraw/CNFGEGLDriver.c
Normal file
866
app/src/nativecode/rawdraw/CNFGEGLDriver.c
Normal file
|
|
@ -0,0 +1,866 @@
|
|||
/*
|
||||
* Copyright (c) 2011-2013 Luc Verhaegen <libv@skynet.be>
|
||||
* 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 <android_native_app_glue.h>
|
||||
#include <jni.h>
|
||||
#include <android/native_activity.h>
|
||||
#define ERRLOG(...) printf( __VA_ARGS__ );
|
||||
#else
|
||||
#define ERRLOG(...) fprintf( stderr, __VA_ARGS__ );
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#include "CNFG.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <EGL/egl.h>
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <GLES3/gl3.h>
|
||||
#else
|
||||
#include <GLES2/gl2.h>
|
||||
#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, "<init>", "(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
|
||||
|
||||
137
app/src/nativecode/rawdraw/CNFGEGLLeanAndMean.c
Normal file
137
app/src/nativecode/rawdraw/CNFGEGLLeanAndMean.c
Normal file
|
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <GLES3/gl3.h>
|
||||
#include <GLES3/gl32.h>
|
||||
#include <EGL/egl.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
959
app/src/nativecode/rawdraw/CNFGFunctions.c
Normal file
959
app/src/nativecode/rawdraw/CNFGFunctions.c
Normal file
|
|
@ -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 <math.h>
|
||||
#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 <stdio.h>
|
||||
|
||||
#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 <GL/gl.h>
|
||||
#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 <dlfcn.h>
|
||||
|
||||
|
||||
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
|
||||
2017
app/src/nativecode/rawdraw/CNFGHTTP.c
Normal file
2017
app/src/nativecode/rawdraw/CNFGHTTP.c
Normal file
File diff suppressed because it is too large
Load diff
70
app/src/nativecode/rawdraw/CNFGNullDriver.c
Normal file
70
app/src/nativecode/rawdraw/CNFGNullDriver.c
Normal file
|
|
@ -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 )
|
||||
{
|
||||
}
|
||||
|
||||
189
app/src/nativecode/rawdraw/CNFGOGLEGLDriver.c
Normal file
189
app/src/nativecode/rawdraw/CNFGOGLEGLDriver.c
Normal file
|
|
@ -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 <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xos.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/keysym.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef USE_EGL_GET_DISPLAY
|
||||
#include <EGL/egl.h>
|
||||
#else
|
||||
#define EGL_EGLEXT_PROTOTYPES
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_EGL_SURFACE
|
||||
#include <GL/gl.h>
|
||||
#else
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#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 <GL/glx.h>
|
||||
#include <GL/glxext.h>
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
341
app/src/nativecode/rawdraw/CNFGRasterizer.c
Normal file
341
app/src/nativecode/rawdraw/CNFGRasterizer.c
Normal file
|
|
@ -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 <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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 miny = (y1<y2)?y1:y2;
|
||||
short maxx = (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
|
||||
84
app/src/nativecode/rawdraw/CNFGWASMDriver.c
Normal file
84
app/src/nativecode/rawdraw/CNFGWASMDriver.c
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
//Right now, designed for use with https://github.com/cnlohr/rawdrawwasm/
|
||||
#include <CNFG.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
681
app/src/nativecode/rawdraw/CNFGWinDriver.c
Normal file
681
app/src/nativecode/rawdraw/CNFGWinDriver.c
Normal file
|
|
@ -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 <windows.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
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 <GL/gl.h>
|
||||
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
|
||||
|
||||
684
app/src/nativecode/rawdraw/CNFGXDriver.c
Normal file
684
app/src/nativecode/rawdraw/CNFGXDriver.c
Normal file
|
|
@ -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 <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xos.h>
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include <X11/keysym.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef HAS_XINERAMA
|
||||
#include <X11/extensions/shape.h>
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
#endif
|
||||
#ifdef CNFG_HAS_XSHAPE
|
||||
#include <X11/extensions/shape.h>
|
||||
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 <GL/glx.h>
|
||||
#include <GL/glxext.h>
|
||||
|
||||
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<<report.xbutton.button))) | ( bKeyDirection << report.xbutton.button );
|
||||
|
||||
//Intentionall fall through -- we want to send a motion in event of a button as well.
|
||||
case MotionNotify:
|
||||
HandleMotion( report.xmotion.x, report.xmotion.y, ButtonsDown>>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
|
||||
|
||||
23
app/src/nativecode/rawdraw/LICENSE
Normal file
23
app/src/nativecode/rawdraw/LICENSE
Normal file
|
|
@ -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 <libv@skynet.be> (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.
|
||||
69
app/src/nativecode/rawdraw/Makefile
Normal file
69
app/src/nativecode/rawdraw/Makefile
Normal file
|
|
@ -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
|
||||
|
||||
229
app/src/nativecode/rawdraw/README.md
Normal file
229
app/src/nativecode/rawdraw/README.md
Normal file
|
|
@ -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.)
|
||||
83
app/src/nativecode/rawdraw/chew.c
Normal file
83
app/src/nativecode/rawdraw/chew.c
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#if defined( WIN32 ) || defined( WINDOWS ) || defined( WIN64 )
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#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] );
|
||||
}
|
||||
}
|
||||
|
||||
184
app/src/nativecode/rawdraw/chew.h
Normal file
184
app/src/nativecode/rawdraw/chew.h
Normal file
|
|
@ -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 <windows.h>
|
||||
#include <stdint.h>
|
||||
#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 <GLES/gl.h>
|
||||
#include <GLES3/gl3.h>
|
||||
#else
|
||||
#include <GL/gl.h>
|
||||
#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
|
||||
118
app/src/nativecode/rawdraw/chewtypes.h
Normal file
118
app/src/nativecode/rawdraw/chewtypes.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#ifndef _CHEWTYPES_H
|
||||
#define _CHEWTYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
27
app/src/nativecode/rawdraw/examples/Makefile
Normal file
27
app/src/nativecode/rawdraw/examples/Makefile
Normal file
|
|
@ -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
|
||||
|
||||
77
app/src/nativecode/rawdraw/examples/fontsize.c
Normal file
77
app/src/nativecode/rawdraw/examples/fontsize.c
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#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 ) );
|
||||
}
|
||||
}
|
||||
56
app/src/nativecode/rawdraw/ogltest.c
Normal file
56
app/src/nativecode/rawdraw/ogltest.c
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#include <GL/gl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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++;
|
||||
}
|
||||
}
|
||||
520
app/src/nativecode/rawdraw/os_generic.h
Normal file
520
app/src/nativecode/rawdraw/os_generic.h
Normal file
|
|
@ -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 <windows.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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 <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <semaphore.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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
|
||||
|
||||
153
app/src/nativecode/rawdraw/osdtest.c
Normal file
153
app/src/nativecode/rawdraw/osdtest.c
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
//Copyright (c) 2011 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
293
app/src/nativecode/rawdraw/rawdraw.c
Normal file
293
app/src/nativecode/rawdraw/rawdraw.c
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
//Copyright (c) 2011 <>< Charles Lohr - Under the MIT/x11 or NewBSD License you choose.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
8156
app/src/nativecode/rawdraw/rawdraw_sf.h
Normal file
8156
app/src/nativecode/rawdraw/rawdraw_sf.h
Normal file
File diff suppressed because it is too large
Load diff
62
app/src/nativecode/rawdraw/simple.c
Normal file
62
app/src/nativecode/rawdraw/simple.c
Normal file
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
3
app/src/nativecode/rawdraw/tccbuild.bat
Normal file
3
app/src/nativecode/rawdraw/tccbuild.bat
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
set TCC=C:\tcc\tcc
|
||||
|
||||
%TCC% rawdraw.c -o rawdraw.exe -luser32 -lgdi32 -lopengl32 C:/windows/system32/msvcrt.dll
|
||||
24
app/src/nativecode/rawdraw/tools/binary_to_buffer.c
Normal file
24
app/src/nativecode/rawdraw/tools/binary_to_buffer.c
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#include <stdio.h>
|
||||
|
||||
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" );
|
||||
|
||||
}
|
||||
546
app/src/nativecode/rawdraw/tools/rawdraw_http_files/index.html
Normal file
546
app/src/nativecode/rawdraw/tools/rawdraw_http_files/index.html
Normal file
|
|
@ -0,0 +1,546 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>cnhttp</title>
|
||||
<style>
|
||||
body { background-color: #000080; }
|
||||
document : {overflow: hidden; width=100%; height=100%;}
|
||||
</style>
|
||||
</head>
|
||||
<body style="height:100%;margin:0; padding:0">
|
||||
<div STYLE="positon:absolute;z-index:3">
|
||||
<canvas width=500 height=500 id=canvas></canvas>
|
||||
</div>
|
||||
<div STYLE="position:absolute;bottom:0;width:640;z-index:10;color:white;a:red;height:50px"><input type=submit value="fs" onclick="ToggleFS();"><div id=status></div></div>
|
||||
<script>
|
||||
let rendering = false;
|
||||
let fullscreen = false;
|
||||
let fullscreenoverride = null;
|
||||
function ToggleFS() { fullscreenoverride = !fullscreenoverride; }
|
||||
|
||||
//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);
|
||||
console.log( wgl );
|
||||
}//Do webgl work that must happen every frame.
|
||||
|
||||
|
||||
//Buffered geometry system.
|
||||
//This handles buffering a bunch of lines/segments, and using them all at once.
|
||||
globalv = null;
|
||||
|
||||
function CNFGEmitBackendTrianglesJS( vertsF, colorsI, vertcount )
|
||||
{
|
||||
if( vertcount <= 0 ) return;
|
||||
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, new Uint8Array( colorsI.buffer ), wgl.DYNAMIC_DRAW);
|
||||
wgl.vertexAttribPointer(1, 4, wgl.UNSIGNED_BYTE, true, 0, 0);
|
||||
wgl.drawArrays(wgl.TRIANGLES, 0, vertcount );
|
||||
globalv = vertsF;
|
||||
}
|
||||
|
||||
//Blitting functionality.
|
||||
|
||||
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" );
|
||||
|
||||
function CNFGBlitImageInternal(uint8memdata, 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, uint8memdata );
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
//Set when swap buffers is called by host app.
|
||||
let frame_transfer_done = false;
|
||||
|
||||
// rest of functionality
|
||||
|
||||
function CNFGClearFrameInternal( color )
|
||||
{
|
||||
color |= 0;
|
||||
wgl.clearColor( ((color>>24)&0xff)/255., ((color>>16)&0xff)/255., ((color>>8)&0xff)/255., ((color>>0)&0xff)/255. );
|
||||
wgl.clear( wgl.COLOR_BUFFER_BIT | wgl.COLOR_DEPTH_BIT );
|
||||
}
|
||||
|
||||
function CNFGGetDimensions(pw, ph)
|
||||
{
|
||||
HEAP16[pw>>1] = canvas.width;
|
||||
HEAP16[ph>>1] = canvas.height;
|
||||
}
|
||||
|
||||
function OGGetAbsoluteTime() { return new Date().getTime()/1000.; }
|
||||
|
||||
globalwebsocket = null;
|
||||
globalwebsocket_time = -10000;
|
||||
globalwebsocket_connected = false;
|
||||
|
||||
//Preallocate a large accumulated buffer.
|
||||
accumulated_buffer = new Uint32Array( 4000000 );
|
||||
accumulated_buffer_place = 0;
|
||||
|
||||
request_h = 384;
|
||||
request_w = 512;
|
||||
|
||||
//Other global state
|
||||
cnfg_color = 0xffffffff;
|
||||
|
||||
function SendSwap()
|
||||
{
|
||||
if( !globalwebsocket_connected ) return;
|
||||
|
||||
let w = wgl.viewportWidth;
|
||||
let h = wgl.viewportHeight;
|
||||
if( w === undefined )
|
||||
{
|
||||
w = wgl.drawingBufferWidth;
|
||||
h = wgl.drawingBufferHeight;
|
||||
}
|
||||
frame_transfer_done = false;
|
||||
globalwebsocket.send( new Uint8Array( [ 83, 87, 65, 80, w, w>>8, h, h>>8 ] ) ); //"SWAP"
|
||||
}
|
||||
|
||||
function HandleMotion( x, y, but )
|
||||
{
|
||||
if( globalwebsocket_connected )
|
||||
globalwebsocket.send( new Uint8Array( [ 77, 79, 84, 78, x, x>>8, y, y>>8, 0, 0, 0, but ] ) ); //"MOTN"
|
||||
}
|
||||
|
||||
function HandleButton( x, y, btn, down )
|
||||
{
|
||||
if( globalwebsocket_connected )
|
||||
globalwebsocket.send( new Uint8Array( [ 66, 85, 84, 78, x, x>>8, y, y>>8, 0, 0, down, btn ] ) ); //"BUTN"
|
||||
}
|
||||
|
||||
function HandleKey( key, down )
|
||||
{
|
||||
if( globalwebsocket_connected )
|
||||
globalwebsocket.send( new Uint8Array( [ 75, 69, 89, 66, 0, 0, key, down ] ) ); //"KEYB"
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Rawdraw functions
|
||||
wgl_last_width_over_2 = .5;
|
||||
CNFG_BATCH = 262144;
|
||||
|
||||
|
||||
CNFGVertDataV = new Float32Array(CNFG_BATCH*3);
|
||||
CNFGVertDataC = new Uint32Array(CNFG_BATCH);
|
||||
CNFGVertPlace = 0;
|
||||
|
||||
function CNFGFlushRender()
|
||||
{
|
||||
CNFGEmitBackendTrianglesJS( CNFGVertDataV, CNFGVertDataC, CNFGVertPlace );
|
||||
CNFGVertPlace = 0;
|
||||
}
|
||||
|
||||
function EmitQuad( cx0, cy0, cx1, cy1, cx2, cy2, cx3, 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();
|
||||
|
||||
CNFGVertDataV[CNFGVertPlace*3+0] = cx0; CNFGVertDataV[CNFGVertPlace*3+1] = cy0;
|
||||
CNFGVertDataV[CNFGVertPlace*3+3] = cx1; CNFGVertDataV[CNFGVertPlace*3+4] = cy1;
|
||||
CNFGVertDataV[CNFGVertPlace*3+6] = cx2; CNFGVertDataV[CNFGVertPlace*3+7] = cy2;
|
||||
CNFGVertDataV[CNFGVertPlace*3+9] = cx2; CNFGVertDataV[CNFGVertPlace*3+10] = cy2;
|
||||
CNFGVertDataV[CNFGVertPlace*3+12] = cx1; CNFGVertDataV[CNFGVertPlace*3+13] = cy1;
|
||||
CNFGVertDataV[CNFGVertPlace*3+15] = cx3; CNFGVertDataV[CNFGVertPlace*3+16] = cy3;
|
||||
CNFGVertDataC[CNFGVertPlace+0] = cnfg_color;
|
||||
CNFGVertDataC[CNFGVertPlace+1] = cnfg_color;
|
||||
CNFGVertDataC[CNFGVertPlace+2] = cnfg_color;
|
||||
CNFGVertDataC[CNFGVertPlace+3] = cnfg_color;
|
||||
CNFGVertDataC[CNFGVertPlace+4] = cnfg_color;
|
||||
CNFGVertDataC[CNFGVertPlace+5] = cnfg_color;
|
||||
CNFGVertPlace += 6;
|
||||
}
|
||||
|
||||
function CNFGTackPixel( x1, y1 )
|
||||
{
|
||||
x1++; y1++;
|
||||
let l2 = wgl_last_width_over_2;
|
||||
let l2u = wgl_last_width_over_2+0.5;
|
||||
EmitQuad( x1-l2u, y1-l2u, x1+l2, y1-l2u, x1-l2u, y1+l2, x1+l2, y1+l2 );
|
||||
}
|
||||
|
||||
|
||||
function CNFGTackSegment( x1, y1, x2, y2 )
|
||||
{
|
||||
// console.log( x1 + " " + y1 + " " + x2 + " " + y2 );
|
||||
let ix1 = x1;
|
||||
let iy1 = y1;
|
||||
let ix2 = x2;
|
||||
let iy2 = y2;
|
||||
|
||||
let dx = ix2-ix1;
|
||||
let dy = iy2-iy1;
|
||||
let imag = 1./Math.sqrt(dx*dx+dy*dy);
|
||||
dx *= imag;
|
||||
dy *= imag;
|
||||
let orthox = dy*wgl_last_width_over_2;
|
||||
let orthoy =-dx*wgl_last_width_over_2;
|
||||
|
||||
ix2 += dx/2 + 0.5;
|
||||
iy2 += dy/2 + 0.5;
|
||||
ix1 -= dx/2 - 0.5;
|
||||
iy1 -= dy/2 - 0.5;
|
||||
|
||||
//This logic is incorrect. XXX FIXME.
|
||||
EmitQuad( (ix1 - orthox), (iy1 - orthoy), (ix1 + orthox), (iy1 + orthoy), (ix2 - orthox), (iy2 - orthoy), ( ix2 + orthox), ( iy2 + orthoy) );
|
||||
}
|
||||
|
||||
function CNFGTackRectangle( x1, y1, x2, y2 )
|
||||
{
|
||||
let ix1 = x1;
|
||||
let iy1 = y1;
|
||||
let ix2 = x2;
|
||||
let iy2 = y2;
|
||||
EmitQuad( ix1,iy1,ix2,iy1,ix1,iy2,ix2,iy2 );
|
||||
}
|
||||
|
||||
function CNFGTackPoly( points, verts )
|
||||
{
|
||||
verts |= 0;
|
||||
let i = 0|0;
|
||||
let tris = verts-2;
|
||||
if( CNFGVertPlace >= CNFG_BATCH-tris*3 ) CNFGFlushRender();
|
||||
|
||||
for( i = 0; i < tris; i++ )
|
||||
{
|
||||
CNFGVertDataV[CNFGVertPlace*3+0] = points[0];
|
||||
CNFGVertDataV[CNFGVertPlace*3+1] = points[1];
|
||||
CNFGVertDataV[CNFGVertPlace*3+3] = points[i*2+2];
|
||||
CNFGVertDataV[CNFGVertPlace*3+4] = points[i*2+3];
|
||||
CNFGVertDataV[CNFGVertPlace*3+6] = points[i*2+4];
|
||||
CNFGVertDataV[CNFGVertPlace*3+7] = points[i*2+5];
|
||||
|
||||
CNFGVertDataC[CNFGVertPlace+0] = cnfg_color;
|
||||
CNFGVertDataC[CNFGVertPlace+1] = cnfg_color;
|
||||
CNFGVertDataC[CNFGVertPlace+2] = cnfg_color;
|
||||
|
||||
CNFGVertPlace += 3;
|
||||
}
|
||||
}
|
||||
|
||||
function CNFGSetLineWidth( width )
|
||||
{
|
||||
wgl_last_width_over_2 = width/2.0;// + 0.5;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function TickWebsocket()
|
||||
{
|
||||
if( OGGetAbsoluteTime() - globalwebsocket_time > 3 )
|
||||
{
|
||||
document.getElementById( "status" ).innerHTML = "Connecting";
|
||||
// Need to connect.
|
||||
globalwebsocket_time = OGGetAbsoluteTime();
|
||||
globalwebsocket_connected = false;
|
||||
globalwebsocket = new WebSocket( "ws://" + location.host + "/d/ws/cmdbuf" );
|
||||
globalwebsocket.binaryType = 'arraybuffer';
|
||||
globalwebsocket.onopen = function()
|
||||
{
|
||||
document.getElementById( "status" ).innerHTML = "Connected";
|
||||
globalwebsocket_connected = true;
|
||||
SendSwap();
|
||||
}
|
||||
globalwebsocket.onmessage = function (event) {
|
||||
let ab = event.data;
|
||||
globalwebsocket_time = OGGetAbsoluteTime();
|
||||
if( ab.byteLength == 0 )
|
||||
{
|
||||
//Process buffer.
|
||||
let i = 0|0;
|
||||
let lastdata = 0;
|
||||
while( i < accumulated_buffer_place )
|
||||
{
|
||||
let data = accumulated_buffer[i++];
|
||||
if( ( data >>> 24 ) & 0xf && ( ( data >>> 28) != 2 ) )
|
||||
{
|
||||
console.log( "Fault at " + i + " Reading " + data + " last " + lastdata + " // " + accumulated_buffer[i-1] + " " + accumulated_buffer[i-2] + " " + accumulated_buffer[i - 3] + " " + accumulated_buffer[i-4] );
|
||||
break;
|
||||
}
|
||||
lastdata = data;
|
||||
switch( data >>> 28 )
|
||||
{
|
||||
case 6:
|
||||
case 0: // Continuation - we should never get here.
|
||||
break;
|
||||
case 1: // Color
|
||||
{
|
||||
let col = accumulated_buffer[i++];
|
||||
cnfg_color = ( ( col >>> 24 ) & 0xff ) | ( ( ( col >>> 16 ) & 0xff ) << 8 ) | ( ( ( col >>> 8 ) & 0xff ) << 16 ) | ( ( ( col >>> 0 ) & 0xff ) << 24 );
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
CNFGTackPixel( data & 0x3fff, ( data >>> 14 ) & 0x3fff );
|
||||
break;
|
||||
case 3:
|
||||
{
|
||||
let nextfew = new Int16Array( accumulated_buffer.buffer.slice( i*4, i*4+8 ) ); i+=2;
|
||||
CNFGTackSegment( nextfew[0], nextfew[1], nextfew[2], nextfew[3] );
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
let nextfew = new Int16Array( accumulated_buffer.buffer.slice( i*4, i*4+8 ) ); i+=2;
|
||||
CNFGTackRectangle( nextfew[0], nextfew[1], nextfew[2], nextfew[3] );
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
let numpts = data & 0xfffff;
|
||||
let ab = new Int16Array( accumulated_buffer.buffer.slice( i*4, i*4+numpts*4 ) );
|
||||
i += numpts;
|
||||
CNFGTackPoly( ab, numpts );
|
||||
break;
|
||||
}
|
||||
case 7:
|
||||
{
|
||||
//break;
|
||||
//blit!
|
||||
CNFGFlushRender();
|
||||
let xy = accumulated_buffer[i++];
|
||||
let x = xy & 0xffff;
|
||||
let y = (xy >>> 16 ) & 0xffff;
|
||||
let w = data & 0x3fff;
|
||||
let h = (data>>>14) & 0x3fff;
|
||||
let ab = new Uint8Array( accumulated_buffer.buffer.slice( i*4, i*4+ w*h*4 ) );
|
||||
i += w*h;
|
||||
CNFGBlitImageInternal( ab, x, y, w, h )
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
CNFGFlushRender();
|
||||
let numblocks = data & 0xff;
|
||||
let fsreq = ( data >>> 8 ) & 0x1;
|
||||
let titlelen = (data >>> 16 ) & 0xff;
|
||||
cnfg_bgcolor = accumulated_buffer[i++];
|
||||
let reqs = accumulated_buffer[i++];
|
||||
request_w = reqs & 0xffff;
|
||||
request_h = ( reqs >> 16 ) & 0xffff;
|
||||
CNFGClearFrameInternal( cnfg_bgcolor );
|
||||
let j = 0|0;
|
||||
let title = new Uint8Array( accumulated_buffer.buffer.slice( i*4, i*4+titlelen ) );
|
||||
i += numblocks - 3;
|
||||
document.title = new TextDecoder().decode( title );
|
||||
fullscreen = fsreq;
|
||||
break;
|
||||
}
|
||||
case 9:
|
||||
//Swap buffers (not realy used - we detect end of frame with a 0-length message.)
|
||||
CNFGFlushRender();
|
||||
break;
|
||||
case 10: //set line width
|
||||
CNFGSetLineWidth( data & 0xfff );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
frame_transfer_done = true;
|
||||
accumulated_buffer_place = 0;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
accumulated_buffer.set( new Uint32Array( ab ), accumulated_buffer_place );
|
||||
accumulated_buffer_place += ab.byteLength/4;
|
||||
}
|
||||
}
|
||||
globalwebsocket.onclose = function(event)
|
||||
{
|
||||
console.log( "Forced close." );
|
||||
globalwebsocket_connected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function FrameStart()
|
||||
{
|
||||
//console.log( globalwebsocket_connected + " " + frame_transfer_done );
|
||||
if( !globalwebsocket_connected )
|
||||
return;
|
||||
|
||||
if( fullscreenoverride === null )
|
||||
{
|
||||
// Don't affect fullscreen
|
||||
}
|
||||
else
|
||||
{
|
||||
fullscreen = fullscreenoverride;
|
||||
}
|
||||
|
||||
//Fixup canvas sizes
|
||||
if( fullscreen )
|
||||
{
|
||||
wgl.viewportWidth = canvas.width = window.innerWidth;
|
||||
wgl.viewportHeight = canvas.height = window.innerHeight;
|
||||
canvas.style = "position:absolute; top:0; left:0;"
|
||||
}
|
||||
else
|
||||
{
|
||||
wgl.viewportWidth = canvas.width = request_w;
|
||||
wgl.viewportHeight = canvas.height = request_h;
|
||||
}
|
||||
|
||||
if( frame_transfer_done )
|
||||
{
|
||||
//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);
|
||||
|
||||
SendSwap();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function AnimationFrame()
|
||||
{
|
||||
TickWebsocket();
|
||||
FrameStart();
|
||||
requestAnimationFrame( AnimationFrame );
|
||||
}
|
||||
|
||||
requestAnimationFrame( AnimationFrame );
|
||||
canvas.addEventListener('mousemove', e => { HandleMotion( e.offsetX, e.offsetY, e.buttons ); } );
|
||||
canvas.addEventListener('touchmove', e => { HandleMotion( e.touches[0].clientX, e.touches[0].clientY, 1 ); } );
|
||||
canvas.addEventListener('mouseup', e => { HandleButton( e.offsetX, e.offsetY, e.button, 0 ); return false; } );
|
||||
canvas.addEventListener('mousedown', e => { HandleButton( e.offsetX, e.offsetY, e.button, 1 ); return false; } );
|
||||
document.addEventListener('keydown', e => { HandleKey( e.keyCode, 1 ); } );
|
||||
document.addEventListener('keyup', e => { HandleKey( e.keyCode, 0 ); } );
|
||||
|
||||
|
||||
|
||||
/*
|
||||
function SystemStart( title, w, h )
|
||||
{
|
||||
document.title = toUTF8( title );
|
||||
wgl.viewportWidth = canvas.width = w;
|
||||
wgl.viewportHeight = canvas.height = h;
|
||||
}
|
||||
*/
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
86
app/src/nativecode/rawdraw/tools/single_file_creator.c
Normal file
86
app/src/nativecode/rawdraw/tools/single_file_creator.c
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
2
app/src/nativecode/rawdraw/wasm/.gitignore
vendored
Normal file
2
app/src/nativecode/rawdraw/wasm/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
index.html
|
||||
node_modules
|
||||
46
app/src/nativecode/rawdraw/wasm/Makefile
Normal file
46
app/src/nativecode/rawdraw/wasm/Makefile
Normal file
|
|
@ -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
|
||||
93
app/src/nativecode/rawdraw/wasm/README.md
Normal file
93
app/src/nativecode/rawdraw/wasm/README.md
Normal file
|
|
@ -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); } );
|
||||
}
|
||||
```
|
||||
|
||||
83
app/src/nativecode/rawdraw/wasm/build-wasm-index.ps1
Normal file
83
app/src/nativecode/rawdraw/wasm/build-wasm-index.ps1
Normal file
|
|
@ -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)
|
||||
276
app/src/nativecode/rawdraw/wasm/rawdraw.c
Normal file
276
app/src/nativecode/rawdraw/wasm/rawdraw.c
Normal file
|
|
@ -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] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
206
app/src/nativecode/rawdraw/wasm/subst.c
Normal file
206
app/src/nativecode/rawdraw/wasm/subst.c
Normal file
|
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
23
app/src/nativecode/rawdraw/wasm/template.ht
Normal file
23
app/src/nativecode/rawdraw/wasm/template.ht
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Test</title>
|
||||
<style>
|
||||
body { background-color: #000080; }
|
||||
document : {overflow: hidden; width=100%; height=100%;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div STYLE="position:absolute;width:640;z-index:10;color:white;a:red">
|
||||
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 <A HREF=https://github.com/cnlohr/rawdrawwasm/ style="color:red">here</a>. This is now part of <a href=https://github.com/cntools/rawdraw style="color:red">rawdraw</a>.
|
||||
</div>
|
||||
<div STYLE="positon:absolute;z-index:3">
|
||||
<canvas width=500 height=500 id=canvas></canvas>
|
||||
</div>
|
||||
<script>
|
||||
${JAVASCRIPT_DATA}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
314
app/src/nativecode/rawdraw/wasm/template.js
Normal file
314
app/src/nativecode/rawdraw/wasm/template.js
Normal file
|
|
@ -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.
|
||||
}
|
||||
|
||||
663
app/src/nativecode/test.c
Normal file
663
app/src/nativecode/test.c
Normal file
|
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include "os_generic.h"
|
||||
#include <GLES3/gl3.h>
|
||||
#include <android/asset_manager.h>
|
||||
#include <android/asset_manager_jni.h>
|
||||
#include <android_native_app_glue.h>
|
||||
#include <android/sensor.h>
|
||||
#include <byteswap.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#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, "<init>", "(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, "<init>", "(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);
|
||||
}
|
||||
|
||||
312
app/src/nativecode/webview_native_activity.h
Normal file
312
app/src/nativecode/webview_native_activity.h
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
#ifndef _WEBVIEW_NATIVE_ACTIVITY
|
||||
#define _WEBVIEW_NATIVE_ACTIVITY
|
||||
|
||||
#include <android/native_activity.h>
|
||||
|
||||
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, "<init>", "(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, "<init>", "(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, "<init>", "(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, "<init>", "(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, "<init>", "(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, "<init>", "(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<String> 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
|
||||
|
||||
|
|
@ -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 <<EOF
|
|||
cat > /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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue