Merge remote repo native into ext/native/.

This commit is contained in:
Unknown W. Brackets
2015-09-06 12:24:17 -07:00
378 changed files with 190761 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
Release
Debug
*.vcxproj.user
android/.settings
local.properties
.DS_Store
+133
View File
@@ -0,0 +1,133 @@
# Basic Android.mk for libnative
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libnative
LOCAL_ARM_MODE := arm
LOCAL_SRC_FILES :=\
android/native_audio.cpp \
android/native-audio-so.cpp \
audio/wav_read.cpp \
audio/mixer.cpp.arm \
base/backtrace.cpp \
base/buffer.cpp \
base/compat.cpp \
base/display.cpp \
base/timeutil.cpp \
base/colorutil.cpp \
base/error_context.cpp \
base/stringutil.cpp \
data/compression.cpp \
ext/rg_etc1/rg_etc1.cpp \
ext/cityhash/city.cpp \
ext/libpng17/png.c \
ext/libpng17/pngerror.c \
ext/libpng17/pngget.c \
ext/libpng17/pngmem.c \
ext/libpng17/pngpread.c \
ext/libpng17/pngread.c \
ext/libpng17/pngrio.c \
ext/libpng17/pngrtran.c \
ext/libpng17/pngrutil.c \
ext/libpng17/pngset.c \
ext/libpng17/pngtest.c \
ext/libpng17/pngtrans.c \
ext/libpng17/pngwio.c \
ext/libpng17/pngwrite.c \
ext/libpng17/pngwtran.c \
ext/libpng17/pngwutil.c \
ext/jpge/jpgd.cpp \
ext/jpge/jpge.cpp \
ext/sha1/sha1.cpp \
ext/stb_vorbis/stb_vorbis.c.arm \
ext/vjson/json.cpp \
ext/vjson/block_allocator.cpp \
file/dialog.cpp \
file/fd_util.cpp \
file/easy_file.cpp \
file/chunk_file.cpp \
file/file_util.cpp \
file/path.cpp \
file/ini_file.cpp \
file/zip_read.cpp \
json/json_writer.cpp \
i18n/i18n.cpp \
input/gesture_detector.cpp \
input/input_state.cpp \
math/fast/fast_math.c \
math/fast/fast_matrix.c \
math/math_util.cpp \
math/curves.cpp \
math/expression_parser.cpp \
math/lin/aabb.cpp.arm \
math/lin/plane.cpp.arm \
math/lin/quat.cpp.arm \
math/lin/vec3.cpp.arm \
math/lin/matrix4x4.cpp.arm \
midi/midi_input.cpp \
net/http_client.cpp \
net/http_server.cpp \
net/http_headers.cpp \
net/resolve.cpp \
net/url.cpp \
profiler/profiler.cpp \
thread/executor.cpp \
thread/threadutil.cpp \
thread/prioritizedworkqueue.cpp \
thread/threadpool.cpp \
gfx_es2/glsl_program.cpp \
gfx_es2/gpu_features.cpp \
gfx_es2/gl3stub.c \
gfx_es2/draw_buffer.cpp.arm \
gfx_es2/draw_text.cpp.arm \
gfx_es2/vertex_format.cpp \
gfx/gl_debug_log.cpp \
gfx/gl_lost_manager.cpp \
gfx/texture.cpp \
gfx/texture_atlas.cpp \
gfx/texture_gen.cpp \
image/zim_load.cpp \
image/zim_save.cpp \
image/png_load.cpp \
thin3d/thin3d.cpp \
thin3d/thin3d_gl.cpp \
ui/view.cpp \
ui/viewgroup.cpp \
ui/ui.cpp \
ui/ui_screen.cpp \
ui/ui_context.cpp \
ui/screen.cpp \
ui/virtual_input.cpp \
util/random/perlin.cpp \
util/text/utf8.cpp \
util/text/parsers.cpp \
util/hash/hash.cpp
LOCAL_CFLAGS := -O3 -DUSING_GLES2 -fsigned-char -fno-strict-aliasing -Wall -Wno-multichar -D__STDC_CONSTANT_MACROS
LOCAL_CPPFLAGS := -fno-exceptions -std=gnu++11 -fno-rtti -Wno-reorder
LOCAL_C_INCLUDES := $(LOCAL_PATH)/ext $(LOCAL_PATH)/ext/libzip
#Portable native and separate code on android in future is easy you needs add files
#by ($(target_arch_ABI),arquitecture (armeabi-v7a , armeabi , x86 , MIPS)
# ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
ifeq ($(findstring armeabi-v7a,$(TARGET_ARCH_ABI)),armeabi-v7a)
LOCAL_CFLAGS := $(LOCAL_CFLAGS) -DARM -DARMEABI_V7A
LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \
math/fast/fast_matrix_neon.S.neon \
ext/libpng17/arm/arm_init.c \
ext/libpng17/arm/filter_neon_intrinsics.c \
ext/libpng17/arm/filter_neon.S.neon
else ifeq ($(TARGET_ARCH_ABI),armeabi)
LOCAL_CFLAGS := $(LOCAL_CFLAGS) -DARM -DARMEABI -march=armv6
else ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
LOCAL_CFLAGS := $(LOCAL_CFLAGS) -D_ARCH_64 -DARM64
else ifeq ($(TARGET_ARCH_ABI),x86)
LOCAL_CFLAGS := $(LOCAL_CFLAGS) -D_M_IX86
LOCAL_SRC_FILES := $(LOCAL_SRC_FILES) \
math/fast/fast_matrix_sse.c
endif
include $(BUILD_STATIC_LIBRARY)
+9
View File
@@ -0,0 +1,9 @@
Copyright (C) 2012 Henrik Rydgard
This applies to all the code here not covered under other licenses, see README.md.
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.
+50
View File
@@ -0,0 +1,50 @@
native
======
This is my library of stuff that I use when writing C++ programs, mostly for Android but it's all written to enable easy portability between Android, Linux, Windows and MacOSX. The code is part ugly, part inconsistent but quite useful.
Features
--------
* JSON read/write (two libraries that should be made more similar)
* basic OpenGL utility code, like compressed texture loading
* 2D texture atlases and drawing code
* ETC1 texture save/load support
* basic logging
* Really simple audio mixer with OGG sample support
* RIFF file read/write
* MIDI Input (only on Windows)
Notes
-----
* The associated tools to create ZIM texture files and atlases do not yet live here but I might move them here eventually.
* This library is not really meant to be a public library but I see no reason not to set it free.
* Note that the included VS project is probably not very useful for you and you're likely better off making your own.
* Don't complain about inconsistent naming etc - this consists of code that has been cobbled together from a variety of my projects through the years. Fashions come and go.
Licenses
--------
This library, for my convenience, incorporates code from a variety of public domain or similarly-licensed code. This is the list:
* glew (GL extension wrangler), MIT license. TODO: should just use a submodule.
* rg_etc1. ZLIB license.
* sha1, public domain implementation by Dominik Reichl
* vjson in a heavily modified form, originally by Ivan Vashchaev (TODO: break out into its own repo?)
* libzip with attribution "Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner"
* stb_vorbis, public domain by Sean Barrett of RAD Tools
If you're not okay with the licenses above, don't use this code.
I hereby release all code here not under the licenses above under the MIT license.
Contact
-------
If you find this useful for your own projects, drop me a line at hrydgard@gmail.com .
Henrik Rydgård
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>
+2
View File
@@ -0,0 +1,2 @@
gen
bin
+33
View File
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>libnative</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.turboviking.libnative"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19" />
<application
android:icon="@drawable/ic_launcher" >
</application>
</manifest>
+8
View File
@@ -0,0 +1,8 @@
Here's an Android library project. Add this to your eclipse workspace
and add a dependency to it, then just inherit from NativeActivity,
write your C++ code, and Bob's your uncle.
Contact: hrydgard@gmail.com
+536
View File
@@ -0,0 +1,536 @@
// This is generic code that is included in all Android apps that use the
// Native framework by Henrik Rydgård (https://github.com/hrydgard/native).
// It calls a set of methods defined in NativeApp.h. These should be implemented
// by your game or app.
#include <jni.h>
#include <android/log.h>
#include <stdlib.h>
#include <stdint.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <queue>
#include "base/basictypes.h"
#include "base/display.h"
#include "base/mutex.h"
#include "base/NativeApp.h"
#include "base/logging.h"
#include "base/timeutil.h"
#include "thread/threadutil.h"
#include "file/zip_read.h"
#include "input/input_state.h"
#include "profiler/profiler.h"
#include "audio/mixer.h"
#include "math/math_util.h"
#include "net/resolve.h"
#include "android/native_audio.h"
#include "gfx/gl_common.h"
#include "app-android.h"
static JNIEnv *jniEnvUI;
struct FrameCommand {
FrameCommand() {}
FrameCommand(std::string cmd, std::string prm) : command(cmd), params(prm) {}
std::string command;
std::string params;
};
static recursive_mutex frameCommandLock;
static std::queue<FrameCommand> frameCommands;
std::string systemName;
std::string langRegion;
std::string mogaVersion;
static float left_joystick_x_async;
static float left_joystick_y_async;
static float right_joystick_x_async;
static float right_joystick_y_async;
static float hat_joystick_x_async;
static float hat_joystick_y_async;
static int optimalFramesPerBuffer = 0;
static int optimalSampleRate = 0;
static int sampleRate = 0;
static int framesPerBuffer = 0;
static int androidVersion;
static int deviceType;
// Should only be used for display detection during startup (for config defaults etc)
static int display_xres;
static int display_yres;
// Android implementation of callbacks to the Java part of the app
void SystemToast(const char *text) {
lock_guard guard(frameCommandLock);
frameCommands.push(FrameCommand("toast", text));
}
void ShowKeyboard() {
lock_guard guard(frameCommandLock);
frameCommands.push(FrameCommand("showKeyboard", ""));
}
void Vibrate(int length_ms) {
lock_guard guard(frameCommandLock);
char temp[32];
sprintf(temp, "%i", length_ms);
frameCommands.push(FrameCommand("vibrate", temp));
}
void LaunchBrowser(const char *url) {
lock_guard guard(frameCommandLock);
frameCommands.push(FrameCommand("launchBrowser", url));
}
void LaunchMarket(const char *url) {
lock_guard guard(frameCommandLock);
frameCommands.push(FrameCommand("launchMarket", url));
}
void LaunchEmail(const char *email_address) {
lock_guard guard(frameCommandLock);
frameCommands.push(FrameCommand("launchEmail", email_address));
}
void System_SendMessage(const char *command, const char *parameter) {
lock_guard guard(frameCommandLock);
frameCommands.push(FrameCommand(command, parameter));
}
std::string System_GetProperty(SystemProperty prop) {
switch (prop) {
case SYSPROP_NAME:
return systemName;
case SYSPROP_LANGREGION: // "en_US"
return langRegion;
case SYSPROP_MOGA_VERSION:
return mogaVersion;
default:
return "";
}
}
int System_GetPropertyInt(SystemProperty prop) {
switch (prop) {
case SYSPROP_SYSTEMVERSION:
return androidVersion;
case SYSPROP_DEVICE_TYPE:
return deviceType;
case SYSPROP_DISPLAY_XRES:
return display_xres;
case SYSPROP_DISPLAY_YRES:
return display_yres;
case SYSPROP_AUDIO_SAMPLE_RATE:
return sampleRate;
case SYSPROP_AUDIO_FRAMES_PER_BUFFER:
return framesPerBuffer;
case SYSPROP_AUDIO_OPTIMAL_SAMPLE_RATE:
return optimalSampleRate;
case SYSPROP_AUDIO_OPTIMAL_FRAMES_PER_BUFFER:
return optimalFramesPerBuffer;
case SYSPROP_DISPLAY_REFRESH_RATE:
return (int)(display_hz * 1000.0);
default:
return -1;
}
}
// Remember that all of these need initialization on init! The process
// may be reused when restarting the game. Globals are DANGEROUS.
float dp_xscale = 1.0f;
float dp_yscale = 1.0f;
InputState input_state;
static bool renderer_inited = false;
static bool first_lost = true;
static std::string library_path;
std::string GetJavaString(JNIEnv *env, jstring jstr) {
const char *str = env->GetStringUTFChars(jstr, 0);
std::string cpp_string = std::string(str);
env->ReleaseStringUTFChars(jstr, str);
return cpp_string;
}
// This is now only used as a trigger for GetAppInfo as a function to all before Init.
// On Android we don't use any of the values it returns.
extern "C" jboolean Java_com_henrikrydgard_libnative_NativeApp_isLandscape(JNIEnv *env, jclass) {
std::string app_name, app_nice_name, version;
bool landscape;
NativeGetAppInfo(&app_name, &app_nice_name, &landscape, &version);
return landscape;
}
// Allow the app to intercept the back button.
extern "C" jboolean Java_com_henrikrydgard_libnative_NativeApp_isAtTopLevel(JNIEnv *env, jclass) {
return NativeIsAtTopLevel();
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_audioConfig
(JNIEnv *env, jclass, jint optimalFPB, jint optimalSR) {
optimalFramesPerBuffer = optimalFPB;
optimalSampleRate = optimalSR;
}
extern "C" jstring Java_com_henrikrydgard_libnative_NativeApp_queryConfig
(JNIEnv *env, jclass, jstring jquery) {
std::string query = GetJavaString(env, jquery);
std::string result = NativeQueryConfig(query);
jstring jresult = env->NewStringUTF(result.c_str());
return jresult;
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_init
(JNIEnv *env, jclass, jstring jmodel, jint jdeviceType, jint jxres, jint jyres, jstring jlangRegion, jstring japkpath,
jstring jdataDir, jstring jexternalDir, jstring jlibraryDir, jstring jshortcutParam,
jstring jinstallID, jint jAndroidVersion) {
jniEnvUI = env;
setCurrentThreadName("androidInit");
ILOG("NativeApp.init() -- begin");
PROFILE_INIT();
memset(&input_state, 0, sizeof(input_state));
renderer_inited = false;
first_lost = true;
androidVersion = jAndroidVersion;
deviceType = jdeviceType;
g_buttonTracker.Reset();
left_joystick_x_async = 0;
left_joystick_y_async = 0;
right_joystick_x_async = 0;
right_joystick_y_async = 0;
hat_joystick_x_async = 0;
hat_joystick_y_async = 0;
display_xres = jxres;
display_yres = jyres;
std::string apkPath = GetJavaString(env, japkpath);
VFSRegister("", new ZipAssetReader(apkPath.c_str(), "assets/"));
systemName = GetJavaString(env, jmodel);
langRegion = GetJavaString(env, jlangRegion);
std::string externalDir = GetJavaString(env, jexternalDir);
std::string user_data_path = GetJavaString(env, jdataDir) + "/";
library_path = GetJavaString(env, jlibraryDir) + "/";
std::string shortcut_param = GetJavaString(env, jshortcutParam);
std::string installID = GetJavaString(env, jinstallID);
ILOG("NativeApp.init(): External storage path: %s", externalDir.c_str());
ILOG("NativeApp.init(): Launch shortcut parameter: %s", shortcut_param.c_str());
std::string app_name;
std::string app_nice_name;
std::string version;
bool landscape;
net::Init();
NativeGetAppInfo(&app_name, &app_nice_name, &landscape, &version);
// If shortcut_param is not empty, pass it as additional varargs argument to NativeInit() method.
// NativeInit() is expected to treat extra argument as boot_filename, which in turn will start game immediately.
// NOTE: Will only work if ppsspp started from Activity.onCreate(). Won't work if ppsspp app start from onResume().
if (shortcut_param.empty()) {
const char *argv[2] = {app_name.c_str(), 0};
NativeInit(1, argv, user_data_path.c_str(), externalDir.c_str(), installID.c_str());
}
else {
const char *argv[3] = {app_name.c_str(), shortcut_param.c_str(), 0};
NativeInit(2, argv, user_data_path.c_str(), externalDir.c_str(), installID.c_str());
}
ILOG("NativeApp.init() -- end");
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_audioInit(JNIEnv *, jclass) {
sampleRate = optimalSampleRate;
if (NativeQueryConfig("force44khz") != "0" || optimalSampleRate == 0) {
sampleRate = 44100;
}
if (optimalFramesPerBuffer > 0) {
framesPerBuffer = optimalFramesPerBuffer;
} else {
framesPerBuffer = 512;
}
// Some devices have totally bonkers buffer sizes like 8192. They will have terrible latency anyway, so to avoid having to
// create extra smart buffering code, we'll just let their regular mixer deal with it, missing the fast path (as if they had one...)
if (framesPerBuffer > 512) {
framesPerBuffer = 512;
sampleRate = 44100;
}
ILOG("NativeApp.audioInit() -- Using OpenSL audio! frames/buffer: %i optimal sr: %i actual sr: %i", optimalFramesPerBuffer, optimalSampleRate, sampleRate);
AndroidAudio_Init(&NativeMix, library_path, framesPerBuffer, sampleRate);
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_audioShutdown(JNIEnv *, jclass) {
AndroidAudio_Shutdown();
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_resume(JNIEnv *, jclass) {
ILOG("NativeApp.resume() - resuming audio");
AndroidAudio_Resume();
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_pause(JNIEnv *, jclass) {
ILOG("NativeApp.pause() - pausing audio");
AndroidAudio_Pause();
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_shutdown(JNIEnv *, jclass) {
ILOG("NativeApp.shutdown() -- begin");
NativeShutdown();
VFSShutdown();
net::Shutdown();
ILOG("NativeApp.shutdown() -- end");
}
static jmethodID postCommand;
extern "C" void Java_com_henrikrydgard_libnative_NativeRenderer_displayInit(JNIEnv * env, jobject obj) {
ILOG("NativeApp.displayInit()");
if (!renderer_inited) {
NativeInitGraphics();
renderer_inited = true;
} else {
NativeDeviceLost(); // ???
ILOG("displayInit: NativeDeviceLost completed.");
}
DLOG("(Re)-fetching method ID to postCommand...");
postCommand = env->GetMethodID(env->GetObjectClass(obj), "postCommand", "(Ljava/lang/String;Ljava/lang/String;)V");
}
extern "C" void Java_com_henrikrydgard_libnative_NativeRenderer_displayResize(JNIEnv *, jobject clazz, jint w, jint h, jint dpi, jfloat refreshRate) {
ILOG("NativeApp.displayResize(%i x %i, dpi=%i, refresh=%0.2f)", w, h, dpi, refreshRate);
g_dpi = dpi;
g_dpi_scale = 240.0f / (float)g_dpi;
pixel_xres = w;
pixel_yres = h;
dp_xres = pixel_xres * g_dpi_scale;
dp_yres = pixel_yres * g_dpi_scale;
dp_xscale = (float)dp_xres / pixel_xres;
dp_yscale = (float)dp_yres / pixel_yres;
display_hz = refreshRate;
NativeResized();
}
extern "C" void Java_com_henrikrydgard_libnative_NativeRenderer_displayRender(JNIEnv *env, jobject obj) {
static bool hasSetThreadName = false;
if (!hasSetThreadName) {
hasSetThreadName = true;
setCurrentThreadName("AndroidRender");
}
if (renderer_inited) {
// TODO: Look into if these locks are a perf loss
{
lock_guard guard(input_state.lock);
input_state.pad_lstick_x = left_joystick_x_async;
input_state.pad_lstick_y = left_joystick_y_async;
input_state.pad_rstick_x = right_joystick_x_async;
input_state.pad_rstick_y = right_joystick_y_async;
UpdateInputState(&input_state);
}
NativeUpdate(input_state);
{
lock_guard guard(input_state.lock);
EndInputState(&input_state);
}
NativeRender();
time_update();
} else {
ELOG("BAD: Ended up in nativeRender even though app has quit.%s", "");
// Shouldn't really get here. Let's draw magenta.
glDepthMask(GL_TRUE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glClearColor(1.0, 0.0, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
lock_guard guard(frameCommandLock);
while (!frameCommands.empty()) {
FrameCommand frameCmd;
frameCmd = frameCommands.front();
frameCommands.pop();
DLOG("frameCommand %s %s", frameCmd.command.c_str(), frameCmd.params.c_str());
jstring cmd = env->NewStringUTF(frameCmd.command.c_str());
jstring param = env->NewStringUTF(frameCmd.params.c_str());
env->CallVoidMethod(obj, postCommand, cmd, param);
env->DeleteLocalRef(cmd);
env->DeleteLocalRef(param);
}
}
extern "C" void Java_com_henrikrydgard_libnative_NativeRenderer_displayShutdown(JNIEnv *env, jobject obj) {
if (renderer_inited) {
NativeDeviceLost();
ILOG("NativeDeviceLost completed.");
NativeShutdownGraphics();
renderer_inited = false;
NativeMessageReceived("recreateviews", "");
}
}
extern "C" jboolean JNICALL Java_com_henrikrydgard_libnative_NativeApp_touch
(JNIEnv *, jclass, float x, float y, int code, int pointerId) {
float scaledX = x * dp_xscale;
float scaledY = y * dp_yscale;
TouchInput touch;
touch.id = pointerId;
touch.x = scaledX;
touch.y = scaledY;
touch.flags = code;
if (code & 2) {
input_state.pointer_down[pointerId] = true;
} else if (code & 4) {
input_state.pointer_down[pointerId] = false;
}
bool retval = NativeTouch(touch);
{
lock_guard guard(input_state.lock);
if (pointerId >= MAX_POINTERS) {
ELOG("Too many pointers: %i", pointerId);
return false; // We ignore 8+ pointers entirely.
}
input_state.pointer_x[pointerId] = scaledX;
input_state.pointer_y[pointerId] = scaledY;
input_state.mouse_valid = true;
}
return retval;
}
extern "C" jboolean Java_com_henrikrydgard_libnative_NativeApp_keyDown(JNIEnv *, jclass, jint deviceId, jint key, jboolean isRepeat) {
KeyInput keyInput;
keyInput.deviceId = deviceId;
keyInput.keyCode = key;
keyInput.flags = KEY_DOWN;
if (isRepeat) {
keyInput.flags |= KEY_IS_REPEAT;
}
return NativeKey(keyInput);
}
extern "C" jboolean Java_com_henrikrydgard_libnative_NativeApp_keyUp(JNIEnv *, jclass, jint deviceId, jint key) {
KeyInput keyInput;
keyInput.deviceId = deviceId;
keyInput.keyCode = key;
keyInput.flags = KEY_UP;
return NativeKey(keyInput);
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_beginJoystickEvent(
JNIEnv *env, jclass) {
// mutex lock?
}
extern "C" jboolean Java_com_henrikrydgard_libnative_NativeApp_joystickAxis(
JNIEnv *env, jclass, jint deviceId, jint axisId, jfloat value) {
if (!renderer_inited)
return false;
switch (axisId) {
case JOYSTICK_AXIS_X:
left_joystick_x_async = value;
break;
case JOYSTICK_AXIS_Y:
left_joystick_y_async = -value;
break;
case JOYSTICK_AXIS_Z:
right_joystick_x_async = value;
break;
case JOYSTICK_AXIS_RZ:
right_joystick_y_async = -value;
break;
case JOYSTICK_AXIS_HAT_X:
hat_joystick_x_async = value;
break;
case JOYSTICK_AXIS_HAT_Y:
hat_joystick_y_async = -value;
break;
}
AxisInput axis;
axis.axisId = axisId;
axis.deviceId = deviceId;
axis.value = value;
return NativeAxis(axis);
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_endJoystickEvent(
JNIEnv *env, jclass) {
// mutex unlock?
}
extern "C" jboolean Java_com_henrikrydgard_libnative_NativeApp_mouseWheelEvent(
JNIEnv *env, jclass, jint stick, jfloat x, jfloat y) {
// TODO: Support mousewheel for android
return true;
}
extern "C" jboolean JNICALL Java_com_henrikrydgard_libnative_NativeApp_accelerometer(JNIEnv *, jclass, float x, float y, float z) {
if (!renderer_inited)
return false;
// Theoretically this needs locking but I doubt it matters. Worst case, the X
// from one "sensor frame" will be used together with Y from the next.
// Should look into quantization though, for compressed movement storage.
input_state.accelerometer_valid = true;
input_state.acc.x = x;
input_state.acc.y = y;
input_state.acc.z = z;
AxisInput axis;
axis.deviceId = DEVICE_ID_ACCELEROMETER;
axis.flags = 0;
axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_X;
axis.value = x;
bool retvalX = NativeAxis(axis);
axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_Y;
axis.value = y;
bool retvalY = NativeAxis(axis);
axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_Z;
axis.value = z;
bool retvalZ = NativeAxis(axis);
return retvalX || retvalY || retvalZ;
}
extern "C" void Java_com_henrikrydgard_libnative_NativeApp_sendMessage(JNIEnv *env, jclass, jstring message, jstring param) {
std::string msg = GetJavaString(env, message);
std::string prm = GetJavaString(env, param);
if (msg == "moga") {
mogaVersion = prm;
}
NativeMessageReceived(msg.c_str(), prm.c_str());
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include "input/keycodes.h"
// Compatability we alias the keycodes
// since native's keycodes are based on
// android keycodes.
typedef enum _keycode_t AndroidKeyCodes;
+92
View File
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="android" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked into
Version Control Systems. -->
<property file="local.properties" />
<!-- The ant.properties file can be created by you. It is only edited by the
'android' tool to add properties to it.
This is the place to change some Ant specific build properties.
Here are some properties you may want to change/update:
source.dir
The name of the source directory. Default is 'src'.
out.dir
The name of the output directory. Default is 'bin'.
For other overridable properties, look at the beginning of the rules
files in the SDK, at tools/ant/build.xml
Properties related to the SDK location or the project target should
be updated using the 'android' tool with the 'update' action.
This file is an integral part of the build system for your
application and should be checked into Version Control Systems.
-->
<property file="ant.properties" />
<!-- if sdk.dir was not set from one of the property file, then
get it from the ANDROID_HOME env var.
This must be done before we load project.properties since
the proguard config can use sdk.dir -->
<property environment="env" />
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
<isset property="env.ANDROID_HOME" />
</condition>
<!-- The project.properties file is created and updated by the 'android'
tool, as well as ADT.
This contains project specific properties such as project target, and library
dependencies. Lower level build properties are stored in ant.properties
(or in .classpath for Eclipse projects).
This file is an integral part of the build system for your
application and should be checked into Version Control Systems. -->
<loadproperties srcFile="project.properties" />
<!-- quick check on sdk.dir -->
<fail
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
unless="sdk.dir"
/>
<!--
Import per project custom build rules if present at the root of the project.
This is the place to put custom intermediary targets such as:
-pre-build
-pre-compile
-post-compile (This is typically used for code obfuscation.
Compiled code location: ${out.classes.absolute.dir}
If this is not done in place, override ${out.dex.input.absolute.dir})
-post-package
-post-build
-pre-clean
-->
<import file="custom_rules.xml" optional="true" />
<!-- Import the actual build file.
To customize existing targets, there are two options:
- Customize only one target:
- copy/paste the target into this file, *before* the
<import> task.
- customize it to your needs.
- Customize the whole content of build.xml
- copy/paste the content of the rules files (minus the top node)
into this file, replacing the <import> task.
- customize to your needs.
***********************
****** IMPORTANT ******
***********************
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
in order to avoid having your file be overridden by tools such as "android update project"
-->
<!-- version-tag: 1 -->
<import file="${sdk.dir}/tools/ant/build.xml" />
</project>
Binary file not shown.
+190
View File
@@ -0,0 +1,190 @@
// Minimal audio streaming using OpenSL.
//
// Loosely based on the Android NDK sample code.
#include <assert.h>
#include <string.h>
#include <unistd.h>
// for native audio
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include "../base/logging.h"
#include "native-audio-so.h"
// This is kinda ugly, but for simplicity I've left these as globals just like in the sample,
// as there's not really any use case for this where we have multiple audio devices yet.
// engine interfaces
static SLObjectItf engineObject;
static SLEngineItf engineEngine;
static SLObjectItf outputMixObject;
// buffer queue player interfaces
static SLObjectItf bqPlayerObject = NULL;
static SLPlayItf bqPlayerPlay;
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
static SLMuteSoloItf bqPlayerMuteSolo;
static SLVolumeItf bqPlayerVolume;
// Double buffering.
static short *buffer[2];
static int curBuffer = 0;
static int framesPerBuffer;
int sampleRate;
static AndroidAudioCallback audioCallback;
// This callback handler is called every time a buffer finishes playing.
// The documentation available is very unclear about how to best manage buffers.
// I've chosen to this approach: Instantly enqueue a buffer that was rendered to the last time,
// and then render the next. Hopefully it's okay to spend time in this callback after having enqueued.
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
if (bq != bqPlayerBufferQueue) {
ELOG("Wrong bq!");
return;
}
int renderedFrames = audioCallback(buffer[curBuffer], framesPerBuffer);
int sizeInBytes = framesPerBuffer * 2 * sizeof(short);
int byteCount = (framesPerBuffer - renderedFrames) * 4;
if (byteCount > 0) {
memset(buffer[curBuffer] + renderedFrames * 2, 0, byteCount);
}
SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeInBytes);
// Comment from sample code:
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
// which for this code example would indicate a programming error
if (result != SL_RESULT_SUCCESS) {
ELOG("OpenSL ES: Failed to enqueue! %i %i", renderedFrames, sizeInBytes);
}
curBuffer ^= 1; // Switch buffer
}
// create the engine and output mix objects
bool OpenSLWrap_Init(AndroidAudioCallback cb, int _FramesPerBuffer, int _SampleRate) {
audioCallback = cb;
framesPerBuffer = _FramesPerBuffer;
if (framesPerBuffer == 0)
framesPerBuffer = 256;
if (framesPerBuffer < 32)
framesPerBuffer = 32;
sampleRate = _SampleRate;
if (sampleRate != 44100 && sampleRate != 48000) {
ELOG("Invalid sample rate %i - choosing 44100", sampleRate);
sampleRate = 44100;
}
buffer[0] = new short[framesPerBuffer * 2];
buffer[1] = new short[framesPerBuffer * 2];
SLresult result;
// create engine
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
assert(SL_RESULT_SUCCESS == result);
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result);
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
assert(SL_RESULT_SUCCESS == result);
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
assert(SL_RESULT_SUCCESS == result);
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result);
SLuint32 sr = SL_SAMPLINGRATE_44_1;
if (sampleRate == 48000) {
sr = SL_SAMPLINGRATE_48;
}
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataFormat_PCM format_pcm = {
SL_DATAFORMAT_PCM,
2,
sr,
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
SL_BYTEORDER_LITTLEENDIAN
};
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
// configure audio sink
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
SLDataSink audioSnk = {&loc_outmix, NULL};
// create audio player
const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
&bqPlayerBufferQueue);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
assert(SL_RESULT_SUCCESS == result);
// Render and enqueue a first buffer. (or should we just play the buffer empty?)
curBuffer = 0;
audioCallback(buffer[curBuffer], framesPerBuffer);
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeof(buffer[curBuffer]));
if (SL_RESULT_SUCCESS != result) {
return false;
}
curBuffer ^= 1;
return true;
}
// shut down the native audio system
void OpenSLWrap_Shutdown() {
SLresult result;
ILOG("OpenSLWrap_Shutdown - stopping playback");
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
if (SL_RESULT_SUCCESS != result) {
ELOG("SetPlayState failed");
}
ILOG("OpenSLWrap_Shutdown - deleting player object");
if (bqPlayerObject != NULL) {
(*bqPlayerObject)->Destroy(bqPlayerObject);
bqPlayerObject = NULL;
bqPlayerPlay = NULL;
bqPlayerBufferQueue = NULL;
bqPlayerMuteSolo = NULL;
bqPlayerVolume = NULL;
}
ILOG("OpenSLWrap_Shutdown - deleting mix object");
if (outputMixObject != NULL) {
(*outputMixObject)->Destroy(outputMixObject);
outputMixObject = NULL;
}
ILOG("OpenSLWrap_Shutdown - deleting engine object");
if (engineObject != NULL) {
(*engineObject)->Destroy(engineObject);
engineObject = NULL;
engineEngine = NULL;
}
delete [] buffer[0];
delete [] buffer[1];
ILOG("OpenSLWrap_Shutdown - finished");
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
typedef int (*AndroidAudioCallback)(short *buffer, int num_samples);
bool OpenSLWrap_Init(AndroidAudioCallback cb, int _FramesPerBuffer, int _SampleRate);
void OpenSLWrap_Shutdown();
+71
View File
@@ -0,0 +1,71 @@
#include "base/logging.h"
#include "android/native_audio.h"
#include "android/native-audio-so.h"
struct AudioState {
void *so;
AndroidAudioCallback callback;
bool playing;
int frames_per_buffer;
int sample_rate;
};
static AudioState *state = 0;
bool AndroidAudio_Init(AndroidAudioCallback callback, std::string libraryDir, int optimalFramesPerBuffer, int optimalSampleRate) {
if (state != 0) {
ELOG("Audio state already exists");
return false;
}
state = new AudioState();
state->callback = callback;
state->playing = false;
state->frames_per_buffer = optimalFramesPerBuffer ? optimalFramesPerBuffer : 256;
state->sample_rate = optimalSampleRate ? optimalSampleRate : 44100;
return true;
}
bool AndroidAudio_Resume() {
if (!state) {
ELOG("Audio was shutdown, cannot resume!");
return false;
}
if (!state->playing) {
ILOG("Calling OpenSLWrap_Init_T...");
bool init_retval = OpenSLWrap_Init(state->callback, state->frames_per_buffer, state->sample_rate);
ILOG("Returned from OpenSLWrap_Init_T");
state->playing = true;
return init_retval;
}
return false;
}
bool AndroidAudio_Pause() {
if (!state) {
ELOG("Audio was shutdown, cannot pause!");
return false;
}
if (state->playing) {
ILOG("Calling OpenSLWrap_Shutdown_T...");
OpenSLWrap_Shutdown();
ILOG("Returned from OpenSLWrap_Shutdown_T ...");
state->playing = false;
return true;
}
return false;
}
void AndroidAudio_Shutdown() {
if (!state) {
ELOG("Audio already shutdown!");
return;
}
if (state->playing) {
ELOG("Should not shut down when playing! Something is wrong!");
}
delete state;
state = 0;
ILOG("OpenSLWrap completely unloaded.");
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include "native-audio-so.h"
#include <string>
// This is the file you should include from your program. It dynamically loads
// the native_audio.so shared object and sets up the function pointers.
// Do not call this if you have detected that the android version is below
// 2.2, as it will fail miserably.
// It's okay for optimalFramesPerBuffer and optimalSampleRate to be 0. Defaults will be used.
bool AndroidAudio_Init(AndroidAudioCallback cb, std::string libraryDir, int optimalFramesPerBuffer, int optimalSampleRate);
bool AndroidAudio_Pause();
bool AndroidAudio_Resume();
void AndroidAudio_Shutdown();
+20
View File
@@ -0,0 +1,20 @@
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
+15
View File
@@ -0,0 +1,15 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}\tools\proguard\proguard-android.txt:proguard-project.txt
# Project target.
target=android-22
android.library=true
Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

@@ -0,0 +1,26 @@
package com.henrikrydgard.libnative;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
public class AudioFocusChangeListener implements OnAudioFocusChangeListener{
// not used right now, but we may need to use it sometime. So just store it
// for now.
private boolean hasAudioFocus = false;
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange){
case AudioManager.AUDIOFOCUS_GAIN:
hasAudioFocus = true;
break;
case AudioManager.AUDIOFOCUS_LOSS:
hasAudioFocus = false;
break;
}
}
public boolean hasAudioFocus() {
return hasAudioFocus;
}
}
@@ -0,0 +1,93 @@
package com.henrikrydgard.libnative;
import android.annotation.TargetApi;
import android.os.Build;
import android.util.Log;
import android.view.InputDevice;
import android.view.InputDevice.MotionRange;
import android.view.KeyEvent;
import android.view.MotionEvent;
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
public class InputDeviceState {
private static final String TAG = "InputDeviceState";
private static final int deviceId = NativeApp.DEVICE_ID_PAD_0;
private InputDevice mDevice;
private int[] mAxes;
InputDevice getDevice() { return mDevice; }
@TargetApi(19)
void logAdvanced(InputDevice device) {
Log.i(TAG, "Vendor ID:" + device.getVendorId() + " productId: " + device.getProductId());
}
public InputDeviceState(InputDevice device) {
mDevice = device;
int numAxes = 0;
for (MotionRange range : device.getMotionRanges()) {
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
numAxes += 1;
}
}
mAxes = new int[numAxes];
int i = 0;
for (MotionRange range : device.getMotionRanges()) {
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
mAxes[i++] = range.getAxis();
}
}
Log.i(TAG, "Registering input device with " + numAxes + " axes: " + device.getName());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
logAdvanced(device);
}
NativeApp.sendMessage("inputDeviceConnected", device.getName());
}
public static float ProcessAxis(InputDevice.MotionRange range, float axisvalue) {
float absaxisvalue = Math.abs(axisvalue);
float deadzone = range.getFlat();
if (absaxisvalue <= deadzone) {
return 0.0f;
}
float normalizedvalue;
if (axisvalue < 0.0f) {
normalizedvalue = absaxisvalue / range.getMin();
} else {
normalizedvalue = absaxisvalue / range.getMax();
}
return normalizedvalue;
}
public boolean onKeyDown(KeyEvent event) {
int keyCode = event.getKeyCode();
boolean repeat = event.getRepeatCount() > 0;
return NativeApp.keyDown(deviceId, keyCode, repeat);
}
public boolean onKeyUp(KeyEvent event) {
int keyCode = event.getKeyCode();
return NativeApp.keyUp(deviceId, keyCode);
}
public boolean onJoystickMotion(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) {
return false;
}
NativeApp.beginJoystickEvent();
for (int i = 0; i < mAxes.length; i++) {
int axisId = mAxes[i];
float value = event.getAxisValue(axisId);
// TODO: Use processAxis or move that to the C++ code
NativeApp.joystickAxis(deviceId, axisId, value);
}
NativeApp.endJoystickEvent();
return true;
}
}
@@ -0,0 +1,46 @@
package com.henrikrydgard.libnative;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.UUID;
import android.content.Context;
public class Installation {
private static String sID = null;
private static final String INSTALLATION = "INSTALLATION";
public synchronized static String id(Context context) {
if (sID == null) {
File installation = new File(context.getFilesDir(), INSTALLATION);
try {
if (!installation.exists())
writeInstallationFile(installation);
sID = readInstallationFile(installation);
} catch (Exception e) {
// We can't even open a file for writing? Then we can't get a unique-ish installation id.
return "BROKENAPPUSERFILESYSTEM";
}
}
return sID;
}
private static String readInstallationFile(File installation) throws IOException {
RandomAccessFile f = new RandomAccessFile(installation, "r");
byte[] bytes = new byte[(int) f.length()];
f.readFully(bytes);
f.close();
return new String(bytes);
}
private static void writeInstallationFile(File installation) throws IOException {
FileOutputStream out = new FileOutputStream(installation);
String id = UUID.randomUUID().toString();
out.write(id.getBytes());
out.close();
}
}
@@ -0,0 +1,118 @@
/**
* Mupen64PlusAE, an N64 emulator for the Android platform
*
* Copyright (C) 2013 Paul Lamb
*
* This file is part of Mupen64PlusAE.
*
* Mupen64PlusAE is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Mupen64PlusAE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Mupen64PlusAE. If
* not, see <http://www.gnu.org/licenses/>.
*
* Authors: Paul Lamb
*/
package com.henrikrydgard.libnative;
import java.util.List;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Build;
import android.util.Log;
import com.bda.controller.Controller;
import com.bda.controller.IControllerService;
/**
* Temporary hack for crash in MOGA library on Lollipop. This hack can be removed once MOGA fixes
* their library. The actual issue is caused by the use of implicit service intents, which are
* illegal in Lollipop, as seen in the logcat message below.
*
* <pre>
* {@code Service Intent must be explicit: Intent { act=com.bda.controller.IControllerService } }
* </pre>
*
* @see <a href="http://www.mogaanywhere.com/developers/">MOGA developer site</a>
* @see <a href="http://commonsware.com/blog/2014/06/29/dealing-deprecations-bindservice.html">
* Discussion on explicit intents</a>
*/
public class MogaHack
{
public static void init(Controller controller, Context context )
{
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT)
{
boolean mIsBound = false;
java.lang.reflect.Field fIsBound = null;
android.content.ServiceConnection mServiceConnection = null;
java.lang.reflect.Field fServiceConnection = null;
try
{
Class<?> cMogaController = controller.getClass();
fIsBound = cMogaController.getDeclaredField( "mIsBound" );
fIsBound.setAccessible( true );
mIsBound = fIsBound.getBoolean( controller );
fServiceConnection = cMogaController.getDeclaredField( "mServiceConnection" );
fServiceConnection.setAccessible( true );
mServiceConnection = ( android.content.ServiceConnection ) fServiceConnection.get( controller );
}
catch( NoSuchFieldException e )
{
Log.e( "MogaHack", "MOGA Lollipop Hack NoSuchFieldException (get)", e );
}
catch( IllegalAccessException e )
{
Log.e( "MogaHack", "MOGA Lollipop Hack IllegalAccessException (get)", e );
}
catch( IllegalArgumentException e )
{
Log.e( "MogaHack", "MOGA Lollipop Hack IllegalArgumentException (get)", e );
}
if( ( !mIsBound ) && ( mServiceConnection != null ) )
{
// Convert implicit intent to explicit intent, see http://stackoverflow.com/a/26318757
Intent intent = new Intent( IControllerService.class.getName() );
List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentServices( intent, 0 );
if( resolveInfos == null || resolveInfos.size() != 1 )
{
// What? this doesn't do anything.
// Log.e( "MogaHack", "Somebody is trying to intercept our intent. Disabling MOGA controller for security." );
}
ServiceInfo serviceInfo = resolveInfos.get( 0 ).serviceInfo;
String packageName = serviceInfo.packageName;
String className = serviceInfo.name;
intent.setComponent( new ComponentName( packageName, className ) );
// Start the service explicitly
context.startService( intent );
context.bindService( intent, mServiceConnection, 1 );
try
{
fIsBound.setBoolean( controller, true );
}
catch( IllegalAccessException e )
{
Log.e( "MogaHack", "MOGA Lollipop Hack IllegalAccessException (set)", e );
}
catch( IllegalArgumentException e )
{
Log.e( "MogaHack", "MOGA Lollipop Hack IllegalArgumentException (set)", e );
}
}
}
else
{
controller.init();
}
}
}
@@ -0,0 +1,875 @@
package com.henrikrydgard.libnative;
import java.io.File;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Locale;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.UiModeManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Vibrator;
import android.text.InputType;
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnSystemUiVisibilityChangeListener;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.Toast;
public class NativeActivity extends Activity {
// Remember to loadLibrary your JNI .so in a static {} block
// Adjust these as necessary
private static String TAG = "NativeActivity";
// Allows us to skip a lot of initialization on secondary calls to onCreate.
private static boolean initialized = false;
// Graphics and audio interfaces
private NativeGLView mGLSurfaceView;
protected NativeRenderer nativeRenderer;
private String shortcutParam = "";
public static String runCommand;
public static String commandParameter;
public static String installID;
// Remember settings for best audio latency
private int optimalFramesPerBuffer;
private int optimalSampleRate;
// audioFocusChangeListener to listen to changes in audio state
private AudioFocusChangeListener audioFocusChangeListener;
private AudioManager audioManager;
private Vibrator vibrator;
private boolean isXperiaPlay;
// Allow for multiple connected gamepads but just consider them the same for now.
// Actually this is not entirely true, see the code.
InputDeviceState inputPlayerA;
InputDeviceState inputPlayerB;
InputDeviceState inputPlayerC;
String inputPlayerADesc;
// Functions for the app activity to override to change behaviour.
public boolean useLowProfileButtons() {
return true;
}
NativeRenderer getRenderer() {
return nativeRenderer;
}
@TargetApi(17)
private void detectOptimalAudioSettings() {
try {
optimalFramesPerBuffer = Integer.parseInt(this.audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER));
} catch (NumberFormatException e) {
// Ignore, if we can't parse it it's bogus and zero is a fine value (means we couldn't detect it).
}
try {
optimalSampleRate = Integer.parseInt(this.audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE));
} catch (NumberFormatException e) {
// Ignore, if we can't parse it it's bogus and zero is a fine value (means we couldn't detect it).
}
}
String getApplicationLibraryDir(ApplicationInfo application) {
String libdir = null;
try {
// Starting from Android 2.3, nativeLibraryDir is available:
Field field = ApplicationInfo.class.getField("nativeLibraryDir");
libdir = (String) field.get(application);
} catch (SecurityException e1) {
} catch (NoSuchFieldException e1) {
} catch (IllegalArgumentException e) {
} catch (IllegalAccessException e) {
}
if (libdir == null) {
// Fallback for Android < 2.3:
libdir = application.dataDir + "/lib";
}
return libdir;
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
void GetScreenSizeJB(Point size, boolean real) {
WindowManager w = getWindowManager();
if (real) {
w.getDefaultDisplay().getRealSize(size);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
void GetScreenSizeHC(Point size, boolean real) {
WindowManager w = getWindowManager();
if (real && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
GetScreenSizeJB(size, real);
} else {
w.getDefaultDisplay().getSize(size);
}
}
@SuppressWarnings("deprecation")
public void GetScreenSize(Point size) {
boolean real = useImmersive();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
GetScreenSizeHC(size, real);
} else {
WindowManager w = getWindowManager();
Display d = w.getDefaultDisplay();
size.x = d.getWidth();
size.y = d.getHeight();
}
}
public void setShortcutParam(String shortcutParam) {
this.shortcutParam = ((shortcutParam == null) ? "" : shortcutParam);
}
public void Initialize() {
// Initialize audio classes. Do this here since detectOptimalAudioSettings()
// needs audioManager
this.audioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
this.audioFocusChangeListener = new AudioFocusChangeListener();
if (Build.VERSION.SDK_INT >= 17) {
// Get the optimal buffer sz
detectOptimalAudioSettings();
}
// isLandscape is used to trigger GetAppInfo currently, we
boolean landscape = NativeApp.isLandscape();
Log.d(TAG, "Landscape: " + landscape);
// Get system information
ApplicationInfo appInfo = null;
PackageManager packMgmr = getPackageManager();
String packageName = getPackageName();
try {
appInfo = packMgmr.getApplicationInfo(packageName, 0);
} catch (NameNotFoundException e) {
e.printStackTrace();
throw new RuntimeException("Unable to locate assets, aborting...");
}
int deviceType = NativeApp.DEVICE_TYPE_MOBILE;
UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE);
switch (uiModeManager.getCurrentModeType()) {
case Configuration.UI_MODE_TYPE_TELEVISION:
deviceType = NativeApp.DEVICE_TYPE_TV;
Log.i(TAG, "Running on an Android TV Device");
break;
case Configuration.UI_MODE_TYPE_DESK:
deviceType = NativeApp.DEVICE_TYPE_DESKTOP;
Log.i(TAG, "Running on an Android desktop computer (!)");
break;
// All other device types are treated the same.
}
isXperiaPlay = IsXperiaPlay();
String libraryDir = getApplicationLibraryDir(appInfo);
File sdcard = Environment.getExternalStorageDirectory();
String externalStorageDir = sdcard.getAbsolutePath();
String dataDir = this.getFilesDir().getAbsolutePath();
String apkFilePath = appInfo.sourceDir;
String model = Build.MANUFACTURER + ":" + Build.MODEL;
String languageRegion = Locale.getDefault().getLanguage() + "_" + Locale.getDefault().getCountry();
Point displaySize = new Point();
GetScreenSize(displaySize);
NativeApp.audioConfig(optimalFramesPerBuffer, optimalSampleRate);
NativeApp.init(model, deviceType, displaySize.x, displaySize.y, languageRegion, apkFilePath, dataDir, externalStorageDir, libraryDir, shortcutParam, installID, Build.VERSION.SDK_INT);
NativeApp.sendMessage("cacheDir", getCacheDir().getAbsolutePath());
// OK, config should be initialized, we can query for screen rotation.
if (Build.VERSION.SDK_INT >= 9) {
updateScreenRotation();
}
// Detect OpenGL support.
// We don't currently use this detection for anything but good to have in the log.
if (!detectOpenGLES20()) {
Log.i(TAG, "OpenGL ES 2.0 NOT detected. Things will likely go badly.");
} else {
if (detectOpenGLES30()) {
Log.i(TAG, "OpenGL ES 3.0 detected.");
}
else {
Log.i(TAG, "OpenGL ES 2.0 detected.");
}
}
vibrator = (Vibrator)getSystemService(VIBRATOR_SERVICE);
if (Build.VERSION.SDK_INT >= 11) {
checkForVibrator();
}
}
@TargetApi(9)
private void updateScreenRotation() {
// Query the native application on the desired rotation.
int rot = 0;
String rotString = NativeApp.queryConfig("screenRotation");
try {
rot = Integer.parseInt(rotString);
} catch (NumberFormatException e) {
Log.e(TAG, "Invalid rotation: " + rotString);
return;
}
switch (rot) {
case 0:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
break;
case 1:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
break;
case 2:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
break;
case 3:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
break;
case 4:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
break;
}
}
private boolean useImmersive() {
String immersive = NativeApp.queryConfig("immersiveMode");
return immersive.equals("1") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
}
@SuppressLint("InlinedApi")
@TargetApi(14)
private void updateSystemUiVisibility() {
int flags = 0;
if (useLowProfileButtons()) {
flags |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
}
if (useImmersive()) {
flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
}
if (getWindow().getDecorView() != null) {
getWindow().getDecorView().setSystemUiVisibility(flags);
} else {
Log.e(TAG, "updateSystemUiVisibility: decor view not yet created, ignoring");
}
}
// Need API 11 to check for existence of a vibrator? Zany.
@TargetApi(11)
public void checkForVibrator() {
if (Build.VERSION.SDK_INT >= 11) {
if (!vibrator.hasVibrator()) {
vibrator = null;
}
}
}
// Override this to scale the backbuffer (use the Android hardware scaler)
public void getDesiredBackbufferSize(Point sz) {
sz.x = 0;
sz.y = 0;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
installID = Installation.id(this);
if (!initialized) {
Initialize();
initialized = true;
}
// Keep the screen bright - very annoying if it goes dark when tilting away
Window window = this.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
gainAudioFocus(this.audioManager, this.audioFocusChangeListener);
NativeApp.audioInit();
mGLSurfaceView = new NativeGLView(this);
nativeRenderer = new NativeRenderer(this);
Point sz = new Point();
getDesiredBackbufferSize(sz);
if (sz.x > 0) {
Log.i(TAG, "Requesting fixed size buffer: " + sz.x + "x" + sz.y);
// Auto-calculates new DPI and forwards to the correct call on mGLSurfaceView.getHolder()
nativeRenderer.setFixedSize(sz.x, sz.y, mGLSurfaceView);
}
mGLSurfaceView.setEGLContextClientVersion(2);
// Setup the GLSurface and ask android for the correct
// Number of bits for r, g, b, a, depth and stencil components
// The PSP only has 16-bit Z so that should be enough.
// Might want to change this for other apps (24-bit might be useful).
// Actually, we might be able to do without both stencil and depth in
// the back buffer, but that would kill non-buffered rendering.
// It appears some gingerbread devices blow up if you use a config chooser at all ???? (Xperia Play)
//if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
// On some (especially older devices), things blow up later (EGL_BAD_MATCH) if we don't set the format here,
// if we specify that we want destination alpha in the config chooser, which we do.
// http://grokbase.com/t/gg/android-developers/11bj40jm4w/fall-back
// Needed to avoid banding on Ouya?
if (Build.MANUFACTURER == "OUYA") {
mGLSurfaceView.getHolder().setFormat(PixelFormat.RGBX_8888);
mGLSurfaceView.setEGLConfigChooser(new NativeEGLConfigChooser());
}
mGLSurfaceView.setRenderer(nativeRenderer);
setContentView(mGLSurfaceView);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
updateSystemUiVisibility();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
setupSystemUiCallback();
}
}
}
@TargetApi(19)
void setupSystemUiCallback() {
getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() {
@Override
public void onSystemUiVisibilityChange(int visibility) {
if (visibility == 0) {
updateSystemUiVisibility();
}
}
});
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop - do nothing special");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy");
mGLSurfaceView.onDestroy();
nativeRenderer.onDestroyed();
NativeApp.audioShutdown();
// Probably vain attempt to help the garbage collector...
mGLSurfaceView = null;
audioFocusChangeListener = null;
audioManager = null;
}
private boolean detectOpenGLES20() {
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo info = am.getDeviceConfigurationInfo();
return info.reqGlEsVersion >= 0x20000;
}
private boolean detectOpenGLES30() {
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ConfigurationInfo info = am.getDeviceConfigurationInfo();
return info.reqGlEsVersion >= 0x30000;
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "onPause");
loseAudioFocus(this.audioManager, this.audioFocusChangeListener);
NativeApp.pause();
mGLSurfaceView.onPause();
}
@Override
protected void onResume() {
super.onResume();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
updateSystemUiVisibility();
}
// OK, config should be initialized, we can query for screen rotation.
if (Build.VERSION.SDK_INT >= 9) {
updateScreenRotation();
}
Log.i(TAG, "onResume");
if (mGLSurfaceView != null) {
mGLSurfaceView.onResume();
} else {
Log.e(TAG, "mGLSurfaceView really shouldn't be null in onResume");
}
gainAudioFocus(this.audioManager, this.audioFocusChangeListener);
NativeApp.resume();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
updateSystemUiVisibility();
}
Point sz = new Point();
getDesiredBackbufferSize(sz);
if (sz.x > 0) {
mGLSurfaceView.getHolder().setFixedSize(sz.x/2, sz.y/2);
}
}
//keep this static so we can call this even if we don't
//instantiate NativeAudioPlayer
public static void gainAudioFocus(AudioManager audioManager, AudioFocusChangeListener focusChangeListener) {
if (audioManager != null) {
audioManager.requestAudioFocus(focusChangeListener,
AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
}
}
//keep this static so we can call this even if we don't
//instantiate NativeAudioPlayer
public static void loseAudioFocus(AudioManager audioManager,AudioFocusChangeListener focusChangeListener){
if (audioManager != null) {
audioManager.abandonAudioFocus(focusChangeListener);
}
}
// We simply grab the first input device to produce an event and ignore all others that are connected.
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
private InputDeviceState getInputDeviceState(InputEvent event) {
InputDevice device = event.getDevice();
if (device == null) {
return null;
}
if (inputPlayerA == null) {
inputPlayerADesc = getInputDesc(device);
Log.i(TAG, "Input player A registered: desc = " + inputPlayerADesc);
inputPlayerA = new InputDeviceState(device);
}
if (inputPlayerA.getDevice() == device) {
return inputPlayerA;
}
if (inputPlayerB == null) {
Log.i(TAG, "Input player B registered: desc = " + getInputDesc(device));
inputPlayerB = new InputDeviceState(device);
}
if (inputPlayerB.getDevice() == device) {
return inputPlayerB;
}
if (inputPlayerC == null) {
Log.i(TAG, "Input player C registered");
inputPlayerC = new InputDeviceState(device);
}
if (inputPlayerC.getDevice() == device) {
return inputPlayerC;
}
return inputPlayerA;
}
public boolean IsXperiaPlay() {
return android.os.Build.MODEL.equals("R800a") || android.os.Build.MODEL.equals("R800i") || android.os.Build.MODEL.equals("R800x") || android.os.Build.MODEL.equals("R800at") || android.os.Build.MODEL.equals("SO-01D") || android.os.Build.MODEL.equals("zeus");
}
// We grab the keys before onKeyDown/... even see them. This is also better because it lets us
// distinguish devices.
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && !isXperiaPlay) {
InputDeviceState state = getInputDeviceState(event);
if (state == null) {
return super.dispatchKeyEvent(event);
}
// Let's let back and menu through to dispatchKeyEvent.
boolean passThrough = false;
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_BACK:
case KeyEvent.KEYCODE_MENU:
passThrough = true;
break;
default:
break;
}
// Don't passthrough back button if gamepad.
int sources = event.getSource();
switch (sources) {
case InputDevice.SOURCE_GAMEPAD:
case InputDevice.SOURCE_JOYSTICK:
case InputDevice.SOURCE_DPAD:
passThrough = false;
break;
}
if (!passThrough) {
switch (event.getAction()) {
case KeyEvent.ACTION_DOWN:
if (state.onKeyDown(event)) {
return true;
}
break;
case KeyEvent.ACTION_UP:
if (state.onKeyUp(event)) {
return true;
}
break;
}
}
}
// Let's go through the old path (onKeyUp, onKeyDown).
return super.dispatchKeyEvent(event);
}
@TargetApi(16)
static public String getInputDesc(InputDevice input) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
return input.getDescriptor();
} else {
List<InputDevice.MotionRange> motions = input.getMotionRanges();
String fakeid = "";
for (InputDevice.MotionRange range : motions)
fakeid += range.getAxis();
return fakeid;
}
}
@Override
@TargetApi(12)
public boolean onGenericMotionEvent(MotionEvent event) {
// Log.d(TAG, "onGenericMotionEvent: " + event);
if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
if (Build.VERSION.SDK_INT >= 12) {
InputDeviceState state = getInputDeviceState(event);
if (state == null) {
Log.w(TAG, "Joystick event but failed to get input device state.");
return super.onGenericMotionEvent(event);
}
state.onJoystickMotion(event);
return true;
}
}
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
switch (event.getAction()) {
case MotionEvent.ACTION_HOVER_MOVE:
// process the mouse hover movement...
return true;
case MotionEvent.ACTION_SCROLL:
NativeApp.mouseWheelEvent(event.getX(), event.getY());
return true;
}
}
return super.onGenericMotionEvent(event);
}
@SuppressLint("NewApi")
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// Eat these keys, to avoid accidental exits / other screwups.
// Maybe there's even more we need to eat on tablets?
boolean repeat = event.getRepeatCount() > 0;
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (event.isAltPressed()) {
NativeApp.keyDown(0, 1004, repeat); // special custom keycode for the O button on Xperia Play
} else if (NativeApp.isAtTopLevel()) {
Log.i(TAG, "IsAtTopLevel returned true.");
// Pass through the back event.
return super.onKeyDown(keyCode, event);
} else {
NativeApp.keyDown(0, keyCode, repeat);
}
return true;
case KeyEvent.KEYCODE_MENU:
case KeyEvent.KEYCODE_SEARCH:
NativeApp.keyDown(0, keyCode, repeat);
return true;
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
// Joysticks are supported in Honeycomb MR1 and later via the onGenericMotionEvent method.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && event.getSource() == InputDevice.SOURCE_JOYSTICK) {
return super.onKeyDown(keyCode, event);
}
// Fall through
default:
// send the rest of the keys through.
// TODO: get rid of the three special cases above by adjusting the native side of the code.
// Log.d(TAG, "Key down: " + keyCode + ", KeyEvent: " + event);
return NativeApp.keyDown(0, keyCode, repeat);
}
}
@SuppressLint("NewApi")
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (event.isAltPressed()) {
NativeApp.keyUp(0, 1004); // special custom keycode
} else if (NativeApp.isAtTopLevel()) {
Log.i(TAG, "IsAtTopLevel returned true.");
return super.onKeyUp(keyCode, event);
} else {
NativeApp.keyUp(0, keyCode);
}
return true;
case KeyEvent.KEYCODE_MENU:
case KeyEvent.KEYCODE_SEARCH:
// Search probably should also be ignored. We send it to the app.
NativeApp.keyUp(0, keyCode);
return true;
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
// Joysticks are supported in Honeycomb MR1 and later via the onGenericMotionEvent method.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1 && event.getSource() == InputDevice.SOURCE_JOYSTICK) {
return super.onKeyUp(keyCode, event);
}
// Fall through
default:
// send the rest of the keys through.
// TODO: get rid of the three special cases above by adjusting the native side of the code.
// Log.d(TAG, "Key down: " + keyCode + ", KeyEvent: " + event);
return NativeApp.keyUp(0, keyCode);
}
}
@TargetApi(11)
private AlertDialog.Builder createDialogBuilderWithTheme() {
return new AlertDialog.Builder(this, AlertDialog.THEME_HOLO_DARK);
}
// The return value is sent elsewhere. TODO in java, in SendMessage in C++.
public void inputBox(String title, String defaultText, String defaultAction) {
final FrameLayout fl = new FrameLayout(this);
final EditText input = new EditText(this);
input.setGravity(Gravity.CENTER);
FrameLayout.LayoutParams editBoxLayout = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
editBoxLayout.setMargins(2, 20, 2, 20);
fl.addView(input, editBoxLayout);
input.setInputType(InputType.TYPE_CLASS_TEXT);
input.setText(defaultText);
input.selectAll();
AlertDialog.Builder bld = null;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
bld = new AlertDialog.Builder(this);
else
bld = createDialogBuilderWithTheme();
AlertDialog dlg = bld
.setView(fl)
.setTitle(title)
.setPositiveButton(defaultAction, new DialogInterface.OnClickListener(){
public void onClick(DialogInterface d, int which) {
NativeApp.sendMessage("inputbox_completed", input.getText().toString());
d.dismiss();
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface d, int which) {
NativeApp.sendMessage("inputbox_failed", "");
d.cancel();
}
}).create();
dlg.setCancelable(true);
dlg.show();
}
public boolean processCommand(String command, String params) {
if (command.equals("launchBrowser")) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(params));
startActivity(i);
return true;
} else if (command.equals("launchEmail")) {
Intent send = new Intent(Intent.ACTION_SENDTO);
String uriText;
uriText = "mailto:email@gmail.com" + "?subject=Your app is..."
+ "&body=great! Or?";
uriText = uriText.replace(" ", "%20");
Uri uri = Uri.parse(uriText);
send.setData(uri);
startActivity(Intent.createChooser(send, "E-mail the app author!"));
return true;
} else if (command.equals("sharejpeg")) {
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/jpeg");
share.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + params));
startActivity(Intent.createChooser(share, "Share Picture"));
} else if (command.equals("sharetext")) {
Intent sendIntent = new Intent();
sendIntent.setType("text/plain");
sendIntent.putExtra(Intent.EXTRA_TEXT, params);
sendIntent.setAction(Intent.ACTION_SEND);
startActivity(sendIntent);
} else if (command.equals("showTwitter")) {
String twitter_user_name = params;
try {
startActivity(new Intent(Intent.ACTION_VIEW,
Uri.parse("twitter://user?screen_name="
+ twitter_user_name)));
} catch (Exception e) {
startActivity(new Intent(
Intent.ACTION_VIEW,
Uri.parse("https://twitter.com/#!/" + twitter_user_name)));
}
} else if (command.equals("launchMarket")) {
// Don't need this, can just use launchBrowser with a market:
// http://stackoverflow.com/questions/3442366/android-link-to-market-from-inside-another-app
// http://developer.android.com/guide/publishing/publishing.html#marketintent
return false;
} else if (command.equals("toast")) {
Toast toast = Toast.makeText(this, params, Toast.LENGTH_SHORT);
toast.show();
Log.i(TAG, params);
return true;
} else if (command.equals("showKeyboard")) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
// No idea what the point of the ApplicationWindowToken is or if it
// matters where we get it from...
inputMethodManager.toggleSoftInputFromWindow(
mGLSurfaceView.getApplicationWindowToken(),
InputMethodManager.SHOW_FORCED, 0);
return true;
} else if (command.equals("hideKeyboard")) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.toggleSoftInputFromWindow(
mGLSurfaceView.getApplicationWindowToken(),
InputMethodManager.SHOW_FORCED, 0);
return true;
} else if (command.equals("inputbox")) {
String title = "Input";
String defString = "";
String[] param = params.split(":");
if (param[0].length() > 0)
title = param[0];
if (param.length > 1)
defString = param[1];
Log.i(TAG, "Launching inputbox: " + title + " " + defString);
inputBox(title, defString, "OK");
return true;
} else if (command.equals("vibrate")) {
int milliseconds = -1;
if (params != "") {
try {
milliseconds = Integer.parseInt(params);
} catch (NumberFormatException e) {
}
}
// Special parameters to perform standard haptic feedback
// operations
// -1 = Standard keyboard press feedback
// -2 = Virtual key press
// -3 = Long press feedback
// Note that these three do not require the VIBRATE Android
// permission.
switch (milliseconds) {
case -1:
mGLSurfaceView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
break;
case -2:
mGLSurfaceView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
break;
case -3:
mGLSurfaceView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
break;
default:
if (vibrator != null) {
vibrator.vibrate(milliseconds);
}
break;
}
return true;
} else if (command.equals("finish")) {
finish();
} else if (command.equals("rotate")) {
if (Build.VERSION.SDK_INT >= 9) {
updateScreenRotation();
}
} else if (command.equals("immersive")) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
updateSystemUiVisibility();
}
} else if (command.equals("recreate")) {
recreate();
}
return false;
}
@SuppressLint("NewApi")
@Override
public void recreate()
{
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
{
super.recreate();
}
else
{
startActivity(getIntent());
finish();
}
}
}
@@ -0,0 +1,53 @@
package com.henrikrydgard.libnative;
// Note that the display* methods are in NativeRenderer.java
public class NativeApp {
public final static int DEVICE_ID_DEFAULT = 0;
public final static int DEVICE_ID_KEYBOARD = 1;
public final static int DEVICE_ID_MOUSE = 2;
public final static int DEVICE_ID_PAD_0 = 10;
public final static int DEVICE_TYPE_MOBILE = 0;
public final static int DEVICE_TYPE_TV = 1;
public final static int DEVICE_TYPE_DESKTOP = 2;
public static native void init(String model, int deviceType, int xres, int yres, String languageRegion, String apkPath, String dataDir, String externalDir, String libraryDir, String shortcutParam, String installID, int androidVersion);
public static native void audioInit();
public static native void audioShutdown();
public static native void audioConfig(int optimalFramesPerBuffer, int optimalSampleRate);
public static native boolean isLandscape();
public static native boolean isAtTopLevel();
// These have Android semantics: Resume is always called on bootup, after init
public static native void pause();
public static native void resume();
// There's not really any reason to ever call shutdown as we can recover from a killed activity.
public static native void shutdown();
public static native boolean keyDown(int deviceId, int key, boolean isRepeat);
public static native boolean keyUp(int deviceId, int key);
public static native void beginJoystickEvent();
public static native void joystickAxis(int deviceId, int axis, float value);
public static native void endJoystickEvent();
public static native boolean mouseWheelEvent(float x, float y);
// will only be called between init() and shutdown()
public static native int audioRender(short[] buffer);
// Sensor/input data. These are asynchronous, beware!
public static native boolean touch(float x, float y, int data, int pointerId);
public static native boolean accelerometer(float x, float y, float z);
public static native void sendMessage(String msg, String arg);
public static native String queryConfig(String queryName);
}
@@ -0,0 +1,206 @@
package com.henrikrydgard.libnative;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;
import android.opengl.GLSurfaceView.EGLConfigChooser;
import android.util.Log;
public class NativeEGLConfigChooser implements EGLConfigChooser {
private static final String TAG = "NativeEGLConfigChooser";
private static final int EGL_OPENGL_ES2_BIT = 4;
private class ConfigAttribs {
EGLConfig config;
public int red;
public int green;
public int blue;
public int alpha;
public int stencil;
public int depth;
public int samples;
public void Log() {
Log.i(TAG, "EGLConfig: red=" + red + " green=" + green + " blue=" + blue + " alpha=" + alpha + " depth=" + depth + " stencil=" + stencil + " samples=" + samples);
}
}
int getEglConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config, int attr) {
int[] value = new int[1];
try {
if (egl.eglGetConfigAttrib(display, config, attr, value))
return value[0];
else
return -1;
} catch (IllegalArgumentException e) {
if (config == null) {
Log.e(TAG, "Called getEglConfigAttrib with null config. Bad developer.");
} else {
Log.e(TAG, "Illegal argument to getEglConfigAttrib: attr=" + attr);
}
return -1;
}
}
ConfigAttribs[] getConfigAttribs(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
ConfigAttribs[] attr = new ConfigAttribs[configs.length];
for (int i = 0; i < configs.length; i++) {
ConfigAttribs cfg = new ConfigAttribs();
cfg.config = configs[i];
cfg.red = getEglConfigAttrib(egl, display, configs[i], EGL10.EGL_RED_SIZE);
cfg.green = getEglConfigAttrib(egl, display, configs[i], EGL10.EGL_GREEN_SIZE);
cfg.blue = getEglConfigAttrib(egl, display, configs[i], EGL10.EGL_BLUE_SIZE);
cfg.alpha = getEglConfigAttrib(egl, display, configs[i], EGL10.EGL_ALPHA_SIZE);
cfg.depth = getEglConfigAttrib(egl, display, configs[i], EGL10.EGL_DEPTH_SIZE);
cfg.stencil = getEglConfigAttrib(egl, display, configs[i], EGL10.EGL_STENCIL_SIZE);
cfg.samples = getEglConfigAttrib(egl, display, configs[i], EGL10.EGL_SAMPLES);
attr[i] = cfg;
}
return attr;
}
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
// The absolute minimum. We will do our best to choose a better config though.
int[] configSpec = {
EGL10.EGL_RED_SIZE, 5,
EGL10.EGL_GREEN_SIZE, 6,
EGL10.EGL_BLUE_SIZE, 5,
EGL10.EGL_DEPTH_SIZE, 16,
EGL10.EGL_STENCIL_SIZE, 0,
EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL10.EGL_NONE
};
int[] num_config = new int[1];
if (!egl.eglChooseConfig(display, configSpec, null, 0, num_config)) {
throw new IllegalArgumentException("eglChooseConfig failed when counting");
}
int numConfigs = num_config[0];
Log.i(TAG, "There are " + numConfigs + " egl configs");
if (numConfigs <= 0) {
throw new IllegalArgumentException("No configs match configSpec");
}
EGLConfig[] eglConfigs = new EGLConfig[numConfigs];
if (!egl.eglChooseConfig(display, configSpec, eglConfigs, numConfigs, num_config)) {
throw new IllegalArgumentException("eglChooseConfig failed when retrieving");
}
ConfigAttribs [] configs = getConfigAttribs(egl, display, eglConfigs);
ConfigAttribs chosen = null;
// Log them all.
for (int i = 0; i < configs.length; i++) {
configs[i].Log();
}
// We now ignore destination alpha as a workaround for the Mali issue
// where we get badly composited if we use it.
// First, find our ideal configuration. Prefer depth.
for (int i = 0; i < configs.length; i++) {
ConfigAttribs c = configs[i];
if (c.red == 8 && c.green == 8 && c.blue == 8 && c.alpha == 0 && c.stencil >= 8 && c.depth >= 24) {
chosen = c;
break;
}
}
if (chosen == null) {
// Then, prefer one with 20-bit depth (Tegra 3)
for (int i = 0; i < configs.length; i++) {
ConfigAttribs c = configs[i];
if (c.red == 8 && c.green == 8 && c.blue == 8 && c.alpha == 0 && c.stencil >= 8 && c.depth >= 20) {
chosen = c;
break;
}
}
}
if (chosen == null) {
// Second, accept one with 16-bit depth.
for (int i = 0; i < configs.length; i++) {
ConfigAttribs c = configs[i];
if (c.red == 8 && c.green == 8 && c.blue == 8 && c.alpha == 0 && c.stencil >= 8 && c.depth >= 16) {
chosen = c;
break;
}
}
}
if (chosen == null) {
// Third, accept one with no stencil.
for (int i = 0; i < configs.length; i++) {
ConfigAttribs c = configs[i];
if (c.red == 8 && c.green == 8 && c.blue == 8 && c.alpha == 0 && c.depth >= 16) {
chosen = c;
break;
}
}
}
if (chosen == null) {
// Third, accept one with alpha but with stencil.
for (int i = 0; i < configs.length; i++) {
ConfigAttribs c = configs[i];
if (c.red == 8 && c.green == 8 && c.blue == 8 && c.alpha == 8 && c.stencil >= 8 && c.depth >= 24) {
chosen = c;
break;
}
}
}
if (chosen == null) {
// Third, accept one with alpha but with stencil.
for (int i = 0; i < configs.length; i++) {
ConfigAttribs c = configs[i];
if (c.red == 8 && c.green == 8 && c.blue == 8 && c.alpha == 8 && c.stencil >= 8 && c.depth >= 16) {
chosen = c;
break;
}
}
}
if (chosen == null) {
// Fourth, accept one with 16-bit color but depth and stencil required.
for (int i = 0; i < configs.length; i++) {
ConfigAttribs c = configs[i];
if (c.red >= 5 && c.green >= 6 && c.blue >= 5 && c.depth >= 16 && c.stencil >= 8) {
chosen = c;
break;
}
}
}
if (chosen == null) {
// Fifth, accept one with 16-bit color but depth required.
for (int i = 0; i < configs.length; i++) {
ConfigAttribs c = configs[i];
if (c.red >= 5 && c.green >= 6 && c.blue >= 5 && c.depth >= 16) {
chosen = c;
break;
}
}
}
if (chosen == null) {
// Final, accept the first one in the list.
if (configs.length > 0)
chosen = configs[0];
}
if (chosen == null) {
throw new IllegalArgumentException("Failed to find a valid EGL config");
}
Log.i(TAG, "Final chosen config: ");
chosen.Log();
return chosen.config;
}
}
@@ -0,0 +1,235 @@
package com.henrikrydgard.libnative;
// Touch- and sensor-enabled GLSurfaceView.
// Supports simple multitouch and pressure.
import android.annotation.TargetApi;
import android.app.Activity;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.os.Handler;
// import android.os.Build;
// import android.util.Log;
import android.util.Log;
import android.view.MotionEvent;
import com.bda.controller.*;
public class NativeGLView extends GLSurfaceView implements SensorEventListener, ControllerListener {
private static String TAG = "NativeGLView";
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private NativeActivity mActivity;
// Moga controller
private Controller mController = null;
private boolean isMogaPro = false;
public NativeGLView(NativeActivity activity) {
super(activity);
mActivity = activity;
/*// TODO: This would be nice.
if (Build.VERSION.SDK_INT >= 11) {
try {
Method method_setPreserveEGLContextOnPause = GLSurfaceView.class.getMethod(
"setPreserveEGLContextOnPause", new Class[] { Boolean.class });
Log.i(TAG, "Invoking setPreserveEGLContextOnPause");
method_setPreserveEGLContextOnPause.invoke(this, true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}*/
mSensorManager = (SensorManager)activity.getSystemService(Activity.SENSOR_SERVICE);
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mController = Controller.getInstance(activity);
try {
MogaHack.init(mController, activity);
Log.i(TAG, "MOGA initialized");
mController.setListener(this, new Handler());
} catch (Exception e) {
Log.i(TAG, "Moga failed to initialize");
}
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private int getToolType(final MotionEvent ev, int pointer) {
return ev.getToolType(pointer);
}
public boolean onTouchEvent(final MotionEvent ev) {
boolean canReadToolType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
int numTouchesHandled = 0;
float scaleX = (float)mActivity.getRenderer().getDpiScaleX();
float scaleY = (float)mActivity.getRenderer().getDpiScaleY();
for (int i = 0; i < ev.getPointerCount(); i++) {
int pid = ev.getPointerId(i);
int code = 0;
final int action = ev.getActionMasked();
// These code bits are now the same as the constants in input_state.h.
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
if (ev.getActionIndex() == i)
code = 2;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
if (ev.getActionIndex() == i)
code = 4;
break;
case MotionEvent.ACTION_MOVE:
code = 1;
break;
default:
break;
}
if (code != 0) {
if (canReadToolType) {
int tool = getToolType(ev, i);
code |= tool << 10; // We use the Android tool type codes
}
// Can't use || due to short circuit evaluation
numTouchesHandled += NativeApp.touch(scaleX * ev.getX(i), scaleY * ev.getY(i), code, pid) ? 1 : 0;
}
}
return numTouchesHandled > 0;
}
// Sensor management
public void onAccuracyChanged(Sensor sensor, int arg1) {
}
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) {
return;
}
// Can also look at event.timestamp for accuracy magic
NativeApp.accelerometer(event.values[0], event.values[1], event.values[2]);
}
@Override
public void onPause() {
super.onPause();
mSensorManager.unregisterListener(this);
if (mController != null) {
mController.onPause();
}
}
@Override
public void onResume() {
super.onResume();
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME);
if (mController != null) {
mController.onResume();
// According to the docs, the Moga's state can be inconsistent here.
// We should do a one time poll. TODO
}
}
public void onDestroy() {
if (mController != null) {
mController.exit();
}
}
// MOGA Controller - from ControllerListener
@Override
public void onKeyEvent(KeyEvent event) {
// The Moga left stick doubles as a D-pad. This creates mapping conflicts so let's turn it off.
// Unfortunately this breaks menu navigation in PPSSPP currently but meh.
// This is different on Moga Pro though.
if (!isMogaPro) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
return;
default:
break;
}
}
boolean repeat = false; // Moga has no repeats?
switch (event.getAction()) {
case KeyEvent.ACTION_DOWN:
NativeApp.keyDown(NativeApp.DEVICE_ID_PAD_0, event.getKeyCode(), repeat);
break;
case KeyEvent.ACTION_UP:
NativeApp.keyUp(NativeApp.DEVICE_ID_PAD_0, event.getKeyCode());
break;
}
}
// MOGA Controller - from ControllerListener
@Override
public void onMotionEvent(com.bda.controller.MotionEvent event) {
NativeApp.joystickAxis(NativeApp.DEVICE_ID_PAD_0, com.bda.controller.MotionEvent.AXIS_X, event.getAxisValue(com.bda.controller.MotionEvent.AXIS_X));
NativeApp.joystickAxis(NativeApp.DEVICE_ID_PAD_0, com.bda.controller.MotionEvent.AXIS_Y, event.getAxisValue(com.bda.controller.MotionEvent.AXIS_Y));
NativeApp.joystickAxis(NativeApp.DEVICE_ID_PAD_0, com.bda.controller.MotionEvent.AXIS_Z, event.getAxisValue(com.bda.controller.MotionEvent.AXIS_Z));
NativeApp.joystickAxis(NativeApp.DEVICE_ID_PAD_0, com.bda.controller.MotionEvent.AXIS_RZ, event.getAxisValue(com.bda.controller.MotionEvent.AXIS_RZ));
NativeApp.joystickAxis(NativeApp.DEVICE_ID_PAD_0, com.bda.controller.MotionEvent.AXIS_LTRIGGER, event.getAxisValue(com.bda.controller.MotionEvent.AXIS_LTRIGGER));
NativeApp.joystickAxis(NativeApp.DEVICE_ID_PAD_0, com.bda.controller.MotionEvent.AXIS_RTRIGGER, event.getAxisValue(com.bda.controller.MotionEvent.AXIS_RTRIGGER));
}
// MOGA Controller - from ControllerListener
@Override
public void onStateEvent(StateEvent state) {
switch (state.getState()) {
case StateEvent.STATE_CONNECTION:
switch (state.getAction()) {
case StateEvent.ACTION_CONNECTED:
Log.i(TAG, "Moga Connected");
if (mController.getState(Controller.STATE_CURRENT_PRODUCT_VERSION) == Controller.ACTION_VERSION_MOGA) {
NativeApp.sendMessage("moga", "Moga");
} else {
Log.i(TAG, "MOGA Pro detected");
isMogaPro = true;
NativeApp.sendMessage("moga", "MogaPro");
}
break;
case StateEvent.ACTION_CONNECTING:
Log.i(TAG, "Moga Connecting...");
break;
case StateEvent.ACTION_DISCONNECTED:
Log.i(TAG, "Moga Disconnected (or simply Not connected)");
NativeApp.sendMessage("moga", "");
break;
}
break;
case StateEvent.STATE_POWER_LOW:
switch (state.getAction()) {
case StateEvent.ACTION_TRUE:
Log.i(TAG, "Moga Power Low");
break;
case StateEvent.ACTION_FALSE:
Log.i(TAG, "Moga Power OK");
break;
}
break;
default:
break;
}
}
}
@@ -0,0 +1,109 @@
package com.henrikrydgard.libnative;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.graphics.Point;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
public class NativeRenderer implements GLSurfaceView.Renderer {
private static String TAG = "NativeRenderer";
private NativeActivity mActivity;
private boolean isDark = false;
private int dpi;
private float refreshRate;
private double dpi_scale_x;
private double dpi_scale_y;
int last_width, last_height;
NativeRenderer(NativeActivity act) {
mActivity = act;
DisplayMetrics metrics = new DisplayMetrics();
Display display = act.getWindowManager().getDefaultDisplay();
display.getMetrics(metrics);
dpi = metrics.densityDpi;
refreshRate = display.getRefreshRate();
}
double getDpiScaleX() {
return dpi_scale_x;
}
double getDpiScaleY() {
return dpi_scale_y;
}
public void setDark(boolean d) {
isDark = d;
}
public void setFixedSize(int xres, int yres, GLSurfaceView surfaceView) {
Log.i(TAG, "Setting surface to fixed size " + xres + "x" + yres);
surfaceView.getHolder().setFixedSize(xres, yres);
}
@Override
public void onDrawFrame(GL10 unused /*use GLES20*/) {
if (isDark) {
GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_STENCIL_BUFFER_BIT);
} else {
displayRender();
}
}
@Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
// Log.i(TAG, "onSurfaceCreated - EGL context is new or was lost");
// Actually, it seems that it is here we should recreate lost GL objects.
displayInit();
}
@Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
Point sz = new Point();
mActivity.GetScreenSize(sz);
double actualW = sz.x;
double actualH = sz.y;
dpi_scale_x = ((double)width / (double)actualW);
dpi_scale_y = ((double)height / (double)actualH);
Log.i(TAG, "onSurfaceChanged: " + dpi_scale_x + "x" + dpi_scale_y + " (width=" + width + ", actualW=" + actualW);
int scaled_dpi = (int)((double)dpi * dpi_scale_x);
displayResize(width, height, scaled_dpi, refreshRate);
last_width = width;
last_height = height;
}
// Not override, it's custom.
public void onDestroyed() {
displayShutdown();
}
// NATIVE METHODS
// Note: This also means "device lost" and you should reload
// all buffered objects.
public native void displayInit();
public native void displayResize(int w, int h, int dpi, float refreshRate);
public native void displayRender();
public native void displayShutdown();
// called by the C++ code through JNI. Dispatch anything we can't directly handle
// on the gfx thread to the UI thread.
public void postCommand(String command, String parameter) {
final String cmd = command;
final String param = parameter;
mActivity.runOnUiThread(new Runnable() {
public void run() {
NativeRenderer.this.mActivity.processCommand(cmd, param);
}
});
}
}
+11
View File
@@ -0,0 +1,11 @@
set(SRCS
mixer.cpp
wav_read.cpp)
set(SRCS ${SRCS})
add_library(mixer STATIC ${SRCS})
if(UNIX)
add_definitions(-fPIC)
endif(UNIX)
+201
View File
@@ -0,0 +1,201 @@
#include <string.h>
#include "audio/mixer.h"
#include "audio/wav_read.h"
#include "base/logging.h"
#include "ext/stb_vorbis/stb_vorbis.h"
#include "file/vfs.h"
// TODO:
// * Vorbis streaming playback
struct ChannelEffectState {
// Filter state
};
enum CLIP_TYPE {
CT_PCM16,
CT_SYNTHFX,
CT_VORBIS,
// CT_PHOENIX?
};
struct Clip {
int type;
short *data;
int length;
int num_channels; // this is NOT stereo vs mono
int sample_rate;
int loop_start;
int loop_end;
};
// If current_clip == 0, the channel is free.
enum ClipPlaybackState {
PB_STOPPED = 0,
PB_PLAYING = 1,
};
struct Channel {
const Clip *current_clip;
// Playback state
ClipPlaybackState state;
int pos;
PlayParams params;
// Effect state
ChannelEffectState effect_state;
};
struct Mixer {
Channel *channels;
int sample_rate;
int num_channels;
int num_fixed_channels;
};
Mixer *mixer_create(int sample_rate, int channels, int fixed_channels) {
Mixer *mixer = new Mixer();
memset(mixer, 0, sizeof(Mixer));
mixer->channels = new Channel[channels];
memset(mixer->channels, 0, sizeof(Channel) * channels);
mixer->sample_rate = sample_rate;
mixer->num_channels = channels;
mixer->num_fixed_channels = fixed_channels;
return mixer;
}
void mixer_destroy(Mixer *mixer) {
delete [] mixer->channels;
delete mixer;
}
static int get_free_channel(Mixer *mixer) {
int chan_with_biggest_pos = -1;
int biggest_pos = -1;
for (int i = mixer->num_fixed_channels; i < mixer->num_channels; i++) {
Channel *chan = &mixer->channels[i];
if (!chan->current_clip) {
return i;
}
if (chan->pos > biggest_pos) {
biggest_pos = chan->pos;
chan_with_biggest_pos = i;
}
}
return chan_with_biggest_pos;
}
Clip *clip_load(const char *filename) {
short *data;
int num_samples;
int sample_rate, num_channels;
if (!strcmp(filename + strlen(filename) - 4, ".ogg")) {
// Ogg file. For now, directly decompress, no streaming support.
uint8_t *filedata;
size_t size;
filedata = VFSReadFile(filename, &size);
num_samples = (int)stb_vorbis_decode_memory(filedata, (int)size, &num_channels, &data);
if (num_samples <= 0)
return NULL;
sample_rate = 44100;
ILOG("read ogg %s, length %i, rate %i", filename, num_samples, sample_rate);
} else {
// Wav file. Easy peasy.
data = wav_read(filename, &num_samples, &sample_rate, &num_channels);
if (!data) {
return NULL;
}
}
Clip *clip = new Clip();
clip->type = CT_PCM16;
clip->data = data;
clip->length = num_samples;
clip->num_channels = num_channels;
clip->sample_rate = sample_rate;
clip->loop_start = 0;
clip->loop_end = 0;
return clip;
}
void clip_destroy(Clip *clip) {
if (clip) {
free(clip->data);
delete clip;
} else {
ELOG("Can't destroy zero clip");
}
}
const short *clip_data(const Clip *clip)
{
return clip->data;
}
size_t clip_length(const Clip *clip) {
return clip->length;
}
void clip_set_loop(Clip *clip, int start, int end) {
clip->loop_start = start;
clip->loop_end = end;
}
PlayParams *mixer_play_clip(Mixer *mixer, const Clip *clip, int channel) {
if (channel == -1) {
channel = get_free_channel(mixer);
}
Channel *chan = &mixer->channels[channel];
// Think about this order and make sure it's thread"safe" (not perfect but should not cause crashes).
chan->pos = 0;
chan->current_clip = clip;
chan->state = PB_PLAYING;
PlayParams *params = &chan->params;
params->volume = 128;
params->pan = 128;
return params;
}
void mixer_mix(Mixer *mixer, short *buffer, int num_samples) {
// Clear the buffer.
memset(buffer, 0, num_samples * sizeof(short) * 2);
for (int i = 0; i < mixer->num_channels; i++) {
Channel *chan = &mixer->channels[i];
if (chan->state == PB_PLAYING) {
const Clip *clip = chan->current_clip;
if (clip->type == CT_PCM16) {
// For now, only allow mono PCM
CHECK(clip->num_channels == 1);
if (true || chan->params.delta == 0) {
// Fast playback of non pitched clips
int cnt = num_samples;
if (clip->length - chan->pos < cnt) {
cnt = clip->length - chan->pos;
}
// TODO: Take pan into account.
int left_volume = chan->params.volume;
int right_volume = chan->params.volume;
// TODO: NEONize. Can also make special loops for left_volume == right_volume etc.
for (int s = 0; s < cnt; s++) {
int cdata = clip->data[chan->pos];
buffer[s * 2 + 0] += cdata * left_volume >> 8;
buffer[s * 2 + 1] += cdata * right_volume >> 8;
chan->pos++;
}
if (chan->pos >= clip->length) {
chan->state = PB_STOPPED;
chan->current_clip = 0;
break;
}
}
} else if (clip->type == CT_VORBIS) {
// For music
}
}
}
}
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include "base/basictypes.h"
// Simple mixer intended for sound effects for games.
// The clip loading code supports ogg SFX.
struct Mixer;
struct Clip;
struct Channel;
// This struct is public for easy manipulation of running channels.
struct PlayParams {
uint8_t volume; // 0-255
uint8_t pan; // 0-255, 127 is dead center.
int32_t delta;
};
// Mixer
// ==========================
// For now, channels is required to be 2 (it specifies L/R, not mixing channels)
Mixer *mixer_create(int sample_rate, int channels, int fixed_channels);
void mixer_destroy(Mixer *mixer);
// Buffer must be r/w.
void mixer_mix(Mixer *mixer, short *buffer, int num_samples);
// Clip
// ==========================
Clip *clip_load(const char *filename);
void clip_destroy(Clip *clip);
const short *clip_data(const Clip *clip);
size_t clip_length(const Clip *clip);
void clip_set_loop(int start, int end);
// The returned PlayState pointer can be used to set the playback parameters,
// but must not be kept around unless you are using a fixed channel.
// Channel must be either -1 for auto assignment to a free channel, or less
// than the number of fixed channels that the mixer was created with.
PlayParams *mixer_play_clip(Mixer *mixer, const Clip *clip, int channel);
+70
View File
@@ -0,0 +1,70 @@
#include "base/basictypes.h"
#include "base/logging.h"
#include "audio/wav_read.h"
#include "file/chunk_file.h"
short *wav_read(const char *filename,
int *num_samples, int *sample_rate,
int *num_channels)
{
ChunkFile cf(filename, true);
if (cf.failed()) {
WLOG("ERROR: Wave file %s could not be opened", filename);
return 0;
}
short *data = 0;
int samplesPerSec, avgBytesPerSec,wBlockAlign,wBytesPerSample;
if (cf.descend('RIFF')) {
cf.readInt(); //get past 'WAVE'
if (cf.descend('fmt ')) { //enter the format chunk
int temp = cf.readInt();
int format = temp & 0xFFFF;
if (format != 1) {
cf.ascend();
cf.ascend();
ELOG("Error - bad format");
return NULL;
}
*num_channels = temp >> 16;
samplesPerSec = cf.readInt();
avgBytesPerSec = cf.readInt();
temp = cf.readInt();
wBlockAlign = temp & 0xFFFF;
wBytesPerSample = temp >> 16;
cf.ascend();
// ILOG("got fmt data: %i", samplesPerSec);
} else {
ELOG("Error - no format chunk in wav");
cf.ascend();
return NULL;
}
if (cf.descend('data')) { //enter the data chunk
int numBytes = cf.getCurrentChunkSize();
int numSamples = numBytes / wBlockAlign;
data = (short *)malloc(sizeof(short) * numSamples * *num_channels);
*num_samples = numSamples;
if (wBlockAlign == 2 && *num_channels == 1) {
cf.readData((uint8_t *)data,numBytes);
} else {
ELOG("Error - bad blockalign or channels");
free(data);
return NULL;
}
cf.ascend();
} else {
ELOG("Error - no data chunk in wav");
cf.ascend();
return NULL;
}
cf.ascend();
} else {
ELOG("Could not descend into RIFF file");
return NULL;
}
*sample_rate = samplesPerSec;
ILOG("read wav %s, length %i, rate %i", filename, *num_samples, *sample_rate);
return data;
}
+7
View File
@@ -0,0 +1,7 @@
#include "base/basictypes.h"
// Allocates a buffer that should be freed using free().
short *wav_read(const char *filename,
int *num_samples, int *sample_rate,
int *num_channels);
// TODO: Non-allocating version.
+86
View File
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2013 Sacha Refshauge
*
*/
// Blackberry implementation of the framework.
#ifndef BLACKBERRYAUDIO_H
#define BLACKBERRYAUDIO_H
#include <AL/al.h>
#include <AL/alc.h>
#include <unistd.h>
#include "base/NativeApp.h"
#define AUDIO_FREQ 44100
#define SAMPLE_SIZE 2048
class BlackberryAudio
{
public:
BlackberryAudio()
{
alcDevice = alcOpenDevice(NULL);
if (alContext = alcCreateContext(alcDevice, NULL))
alcMakeContextCurrent(alContext);
alGenSources(1, &source);
alGenBuffers(1, &buffer);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&thread_handle, &attr, &BlackberryAudio::staticThreadProc, this);
}
~BlackberryAudio()
{
pthread_cancel(thread_handle);
alcMakeContextCurrent(NULL);
if (alContext)
{
alcDestroyContext(alContext);
alContext = NULL;
}
if (alcDevice)
{
alcCloseDevice(alcDevice);
alcDevice = NULL;
}
}
static void* staticThreadProc(void* arg)
{
return reinterpret_cast<BlackberryAudio*>(arg)->RunAudio();
}
private:
void* RunAudio()
{
while(true)
{
size_t frames_ready;
alGetSourcei(source, AL_SOURCE_STATE, &state);
if (state != AL_PLAYING) {
frames_ready = NativeMix((short*)stream, 5*SAMPLE_SIZE);
}
else
frames_ready = 0;
if (frames_ready > 0)
{
const size_t bytes_ready = frames_ready * sizeof(short) * 2;
alSourcei(source, AL_BUFFER, 0);
alBufferData(buffer, AL_FORMAT_STEREO16, stream, bytes_ready, AUDIO_FREQ);
alSourcei(source, AL_BUFFER, buffer);
alSourcePlay(source);
// TODO: Maybe this could get behind?
usleep((1000000 * SAMPLE_SIZE) / AUDIO_FREQ);
}
else
usleep(10000);
}
}
ALCdevice *alcDevice;
ALCcontext *alContext;
ALenum state;
ALuint buffer;
ALuint source;
char stream[20*SAMPLE_SIZE];
pthread_t thread_handle;
};
#endif
+175
View File
@@ -0,0 +1,175 @@
/*
* Copyright (c) 2013 Sacha Refshauge
*
*/
// Blackberry implementation of the framework.
#include "BlackberryMain.h"
const char* BlackberryMain::displayTypeString(int type)
{
switch (type)
{
case SCREEN_DISPLAY_TYPE_INTERNAL:
return "Internal"; break;
case SCREEN_DISPLAY_TYPE_COMPOSITE:
return "Composite"; break;
case SCREEN_DISPLAY_TYPE_DVI:
return "DVI"; break;
case SCREEN_DISPLAY_TYPE_HDMI:
return "HDMI"; break;
case SCREEN_DISPLAY_TYPE_DISPLAYPORT:
return "DisplayPort"; break;
case SCREEN_DISPLAY_TYPE_OTHER:
default:
break;
}
return "Unknown Port";
}
void BlackberryMain::startDisplays() {
int num_configs;
EGLint attrib_list[]= {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_DEPTH_SIZE, 24,
EGL_STENCIL_SIZE, 8,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE};
const EGLint attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
screen_get_context_property_iv(screen_cxt, SCREEN_PROPERTY_DISPLAY_COUNT, &ndisplays);
egl_disp = (EGLDisplay*)calloc(ndisplays, sizeof(EGLDisplay));
egl_surf = (EGLSurface*)calloc(ndisplays, sizeof(EGLSurface));
displays = (dispdata_t*)calloc(ndisplays, sizeof(dispdata_t));
screen_win = (screen_window_t *)calloc(ndisplays, sizeof(screen_window_t ));
screen_dpy = (screen_display_t*)calloc(ndisplays, sizeof(screen_display_t));
screen_get_context_property_pv(screen_cxt, SCREEN_PROPERTY_DISPLAYS, (void **)screen_dpy);
// Common data
int usage = SCREEN_USAGE_ROTATION | SCREEN_USAGE_OPENGL_ES2;
int format = SCREEN_FORMAT_RGBX8888;
int sensitivity = SCREEN_SENSITIVITY_ALWAYS;
// Initialise every display
for (int i = 0; i < ndisplays; i++) {
screen_get_display_property_iv(screen_dpy[i], SCREEN_PROPERTY_TYPE, &(displays[i].type));
screen_get_display_property_iv(screen_dpy[i], SCREEN_PROPERTY_ATTACHED, &(displays[i].attached));
screen_create_window(&screen_win[i], screen_cxt);
screen_set_window_property_iv(screen_win[i], SCREEN_PROPERTY_FORMAT, &format);
screen_set_window_property_iv(screen_win[i], SCREEN_PROPERTY_USAGE, &usage);
screen_set_window_property_iv(screen_win[i], SCREEN_PROPERTY_SENSITIVITY, &sensitivity);
screen_set_window_property_pv(screen_win[i], SCREEN_PROPERTY_DISPLAY, (void **)&screen_dpy[i]);
egl_disp[i] = eglGetDisplay((EGLNativeDisplayType)i);
eglInitialize(egl_disp[i], NULL, NULL);
if (egl_cont == EGL_NO_CONTEXT) {
eglChooseConfig(egl_disp[0], attrib_list, &egl_conf, 1, &num_configs);
egl_cont = eglCreateContext(egl_disp[0], egl_conf, EGL_NO_CONTEXT, attributes);
}
fprintf(stderr, "Display %i: %s, %s\n", i, displayTypeString(displays[i].type), displays[i].attached ? "Attached" : "Detached");
if (displays[i].attached)
realiseDisplay(i);
}
#ifdef ARM
screen_get_display_property_iv(screen_dpy[0], SCREEN_PROPERTY_DPI, &dpi); // Only internal display has DPI
// We only use dpi to calculate the width. Smaller aspect ratios have giant text despite high DPI.
dpi = dpi * (((float)displays[0].width/(float)displays[0].height) / (16.0/9.0)); // Adjust to 16:9
#else
dpi = 340.0f;
#endif
g_dpi_scale = 210.0f / dpi;
switchDisplay(screen_ui);
}
void BlackberryMain::realiseDisplay(int idx) {
const EGLint egl_surfaceAttr[] = { EGL_RENDER_BUFFER, EGL_BACK_BUFFER, EGL_NONE };
int size[2] = { atoi(getenv("WIDTH")), atoi(getenv("HEIGHT")) };
if (idx != 0)
screen_get_display_property_iv(screen_dpy[idx], SCREEN_PROPERTY_SIZE, size);
displays[idx].width = size[0];
displays[idx].height = size[1];
screen_set_window_property_iv(screen_win[idx], SCREEN_PROPERTY_BUFFER_SIZE, size);
screen_create_window_buffers(screen_win[idx], 2); // Double buffered
fprintf(stderr, "Display %i realised with %ix%i\n", idx, size[0], size[1]);
egl_surf[idx] = eglCreateWindowSurface(egl_disp[idx], egl_conf, screen_win[idx], egl_surfaceAttr);
// Only enable for devices with hardware QWERTY, 1:1 aspect ratio
if ((pixel_xres == pixel_yres) && displays[idx].type != SCREEN_DISPLAY_TYPE_INTERNAL)
{
screen_emu = idx;
if (emulating)
switchDisplay(idx);
}
displays[idx].realised = true;
}
void BlackberryMain::unrealiseDisplay(int idx) {
if (displays[idx].type != SCREEN_DISPLAY_TYPE_INTERNAL) // Always true, only external can unrealise
{
screen_emu = screen_ui;
if (emulating)
switchDisplay(screen_ui);
}
killDisplay(idx, false);
displays[idx].realised = false;
}
void BlackberryMain::switchDisplay(int idx) {
static int screen_curr = -1;
if (idx != screen_curr) {
pixel_xres = displays[idx].width;
pixel_yres = displays[idx].height;
dp_xres = (int)(pixel_xres * g_dpi_scale);
dp_yres = (int)(pixel_yres * g_dpi_scale);
screen_curr = idx;
eglMakeCurrent(egl_disp[idx], egl_surf[idx], egl_surf[idx], egl_cont);
}
if (emulating) {
PSP_CoreParameter().pixelWidth = pixel_xres;
PSP_CoreParameter().pixelHeight = pixel_yres;
}
}
void BlackberryMain::killDisplays() {
for (int i = 0; i < ndisplays; i++) {
killDisplay(i, true);
}
if (egl_cont != EGL_NO_CONTEXT) {
eglDestroyContext(egl_disp[0], egl_cont);
egl_cont = EGL_NO_CONTEXT;
}
free(egl_disp);
free(egl_surf);
eglReleaseThread();
free(screen_dpy);
}
void BlackberryMain::killDisplay(int idx, bool killContext) {
if (egl_disp[idx] != EGL_NO_DISPLAY) {
if (killContext)
eglMakeCurrent(egl_disp[idx], EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (egl_surf[idx] != EGL_NO_SURFACE) {
eglDestroySurface(egl_disp[idx], egl_surf[idx]);
egl_surf[idx] = EGL_NO_SURFACE;
}
if (killContext)
{
eglTerminate(egl_disp[idx]);
egl_disp[idx] = EGL_NO_DISPLAY;
}
}
if (killContext && screen_win[idx] != NULL) {
screen_destroy_window(screen_win[idx]);
screen_win[idx] = NULL;
}
screen_destroy_window_buffers(screen_win[idx]);
}
+359
View File
@@ -0,0 +1,359 @@
/*
* Copyright (c) 2013 Sacha Refshauge
*
*/
// Blackberry implementation of the framework.
#include <pwd.h>
#include <unistd.h>
#include <string>
#include <string.h>
#include <bps/locale.h> // Get locale
#include <bps/navigator_invoke.h> // Receive invocation messages
#include "BlackberryMain.h"
#include "base/NKCodeFromBlackberry.h"
// Bad: PPSSPP includes from native
#include "Core/System.h"
#include "Core/Config.h"
#include "Core/Core.h"
#include "UI/MiscScreens.h"
static bool g_quitRequested = false;
// Simple implementations of System functions
std::string System_GetProperty(SystemProperty prop) {
switch (prop) {
case SYSPROP_NAME: {
std::string name = "Blackberry:";
#ifdef ARM
return name + ((pixel_xres != pixel_yres) ? "Touch" : "QWERTY");
#else
return name + "Simulator";
#endif
}
case SYSPROP_LANGREGION: {
char *locale = 0;
locale_get_locale(&locale);
return std::string(locale);
}
default:
return "";
}
}
int System_GetPropertyInt(SystemProperty prop) {
switch (prop) {
case SYSPROP_AUDIO_SAMPLE_RATE:
return 44100;
case SYSPROP_DISPLAY_REFRESH_RATE:
return 60000;
case SYSPROP_DEVICE_TYPE:
return DEVICE_TYPE_MOBILE;
default:
return -1;
}
}
void System_SendMessage(const char *command, const char *parameter) {
if (!strcmp(command, "finish")) {
g_quitRequested = true;
}
}
void SystemToast(const char *text) {
dialog_instance_t dialog = 0;
dialog_create_toast(&dialog);
dialog_set_toast_message_text(dialog, text);
dialog_set_toast_position(dialog, DIALOG_POSITION_TOP_CENTER);
dialog_show(dialog);
}
void ShowAd(int x, int y, bool center_x) {
}
void ShowKeyboard() {
virtualkeyboard_show();
}
void Vibrate(int length_ms) {
// Vibration: intensity strength(1-100), duration ms(0-5000)
// Intensity: LOW = 1, MEDIUM = 10, HIGH = 100
switch (length_ms) {
case -1: // Keyboard Tap
vibration_request(VIBRATION_INTENSITY_LOW, 50);
break;
case -2: // Virtual Key
vibration_request(VIBRATION_INTENSITY_LOW, 25);
break;
case -3: // Long Press
vibration_request(VIBRATION_INTENSITY_LOW, 50);
break;
default:
vibration_request(VIBRATION_INTENSITY_LOW, length_ms);
break;
}
}
void LaunchBrowser(const char *url)
{
char* error;
navigator_invoke(url, &error);
}
void LaunchMarket(const char *url)
{
char* error;
navigator_invoke(url, &error);
}
void LaunchEmail(const char *email_address)
{
char* error;
navigator_invoke((std::string("mailto:") + email_address).c_str(), &error);
}
InputState input_state;
void BlackberryMain::handleInput(screen_event_t screen_event)
{
TouchInput input;
KeyInput key;
int val, buttons, pointerId;
int pair[2];
screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TYPE, &val);
screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_SOURCE_POSITION, pair);
input_state.mouse_valid = true;
switch(val)
{
// Touchscreen
case SCREEN_EVENT_MTOUCH_TOUCH:
case SCREEN_EVENT_MTOUCH_RELEASE: // Up, down
screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TOUCH_ID, &pointerId);
input_state.pointer_down[pointerId] = (val == SCREEN_EVENT_MTOUCH_TOUCH);
input_state.pointer_x[pointerId] = pair[0] * g_dpi_scale;
input_state.pointer_y[pointerId] = pair[1] * g_dpi_scale;
input.x = pair[0] * g_dpi_scale;
input.y = pair[1] * g_dpi_scale;
input.flags = (val == SCREEN_EVENT_MTOUCH_TOUCH) ? TOUCH_DOWN : TOUCH_UP;
input.id = pointerId;
NativeTouch(input);
break;
case SCREEN_EVENT_MTOUCH_MOVE:
screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_TOUCH_ID, &pointerId);
input_state.pointer_x[pointerId] = pair[0] * g_dpi_scale;
input_state.pointer_y[pointerId] = pair[1] * g_dpi_scale;
input.x = pair[0] * g_dpi_scale;
input.y = pair[1] * g_dpi_scale;
input.flags = TOUCH_MOVE;
input.id = pointerId;
NativeTouch(input);
break;
// Mouse, Simulator
case SCREEN_EVENT_POINTER:
screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_BUTTONS, &buttons);
if (buttons == SCREEN_LEFT_MOUSE_BUTTON) { // Down
input_state.pointer_x[0] = pair[0] * g_dpi_scale;
input_state.pointer_y[0] = pair[1] * g_dpi_scale;
input_state.pointer_down[0] = true;
input.x = pair[0] * g_dpi_scale;
input.y = pair[1] * g_dpi_scale;
input.flags = TOUCH_DOWN;
input.id = 0;
NativeTouch(input);
} else if (input_state.pointer_down[0]) { // Up
input_state.pointer_x[0] = pair[0] * g_dpi_scale;
input_state.pointer_y[0] = pair[1] * g_dpi_scale;
input_state.pointer_down[0] = false;
input.x = pair[0] * g_dpi_scale;
input.y = pair[1] * g_dpi_scale;
input.flags = TOUCH_UP;
input.id = 0;
NativeTouch(input);
}
break;
// Keyboard
case SCREEN_EVENT_KEYBOARD:
int flags, value;
screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_KEY_FLAGS, &flags);
screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_KEY_SYM, &value);
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, KeyMapRawBlackberrytoNative.find(value)->second, (flags & KEY_DOWN) ? KEY_DOWN : KEY_UP));
break;
// Gamepad
case SCREEN_EVENT_GAMEPAD:
case SCREEN_EVENT_JOYSTICK:
int analog0[3];
screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_BUTTONS, &buttons);
for (int i = 0; i < 32; i++) {
int mask = 1 << i;
if ((old_buttons & mask) != (buttons & mask))
NativeKey(KeyInput(DEVICE_ID_PAD_0, KeyMapPadBlackberrytoNative.find(mask)->second, (buttons & mask) ? KEY_DOWN : KEY_UP));
}
if (!screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_ANALOG0, analog0)) {
for (int i = 0; i < 2; i++) {
AxisInput axis;
axis.axisId = JOYSTICK_AXIS_X + i;
// 1.2 to try to approximate the PSP's clamped rectangular range.
axis.value = 1.2 * analog0[i] / 128.0f;
if (axis.value > 1.0f) axis.value = 1.0f;
if (axis.value < -1.0f) axis.value = -1.0f;
axis.deviceId = DEVICE_ID_PAD_0;
axis.flags = 0;
NativeAxis(axis);
}
}
old_buttons = buttons;
break;
case SCREEN_EVENT_DISPLAY:
screen_display_t new_dpy = NULL;
screen_get_event_property_pv(screen_event, SCREEN_PROPERTY_DISPLAY, (void **)&new_dpy);
for (int i = 0; i < ndisplays; i++) {
if (new_dpy != screen_dpy[i])
continue;
int active = 0;
screen_get_event_property_iv(screen_event, SCREEN_PROPERTY_ATTACHED, &active);
if (active) {
int size[2];
screen_get_display_property_iv(screen_dpy[i], SCREEN_PROPERTY_SIZE, size);
if (size[0] == 0 || size[1] == 0)
active = 0;
}
if (active && !displays[i].attached)
realiseDisplay(i);
else if (!active && displays[i].attached && displays[i].realised)
unrealiseDisplay(i);
displays[i].attached = active;
}
break;
}
}
void BlackberryMain::startMain(int argc, char *argv[]) {
g_quitRequested = false;
// Receive events from window manager
screen_create_context(&screen_cxt, 0);
// Initialise Blackberry Platform Services
bps_initialize();
// TODO: Enable/disable based on setting
sensor_set_rate(SENSOR_TYPE_ACCELEROMETER, 25000);
sensor_request_events(SENSOR_TYPE_ACCELEROMETER);
net::Init();
startDisplays();
screen_request_events(screen_cxt);
navigator_request_events(0);
dialog_request_events(0);
vibration_request_events(0);
NativeInit(argc, (const char **)argv, "/accounts/1000/shared/misc/", "app/native/assets/", "BADCOFFEE");
NativeInitGraphics();
audio = new BlackberryAudio();
runMain();
}
void BlackberryMain::runMain() {
bool running = true;
while (running && !g_quitRequested) {
input_state.mouse_valid = false;
input_state.accelerometer_valid = false;
while (true) {
// Handle Blackberry events
bps_event_t *event = NULL;
bps_get_event(&event, 0);
if (event == NULL)
break; // Ran out of events
int domain = bps_event_get_domain(event);
if (domain == screen_get_domain()) {
handleInput(screen_event_get_event(event));
} else if (domain == navigator_get_domain()) {
switch(bps_event_get_code(event))
{
case NAVIGATOR_INVOKE_TARGET:
{
const navigator_invoke_invocation_t *invoke = navigator_invoke_event_get_invocation(event);
if(invoke) {
boot_filename = navigator_invoke_invocation_get_uri(invoke)+7; // Remove file://
}
}
break;
case NAVIGATOR_ORIENTATION:
sensor_remap_coordinates(navigator_event_get_orientation_angle(event));
break;
case NAVIGATOR_WINDOW_STATE:
Core_NotifyWindowHidden(navigator_event_get_window_state(event) != NAVIGATOR_WINDOW_FULLSCREEN);
break;
case NAVIGATOR_BACK:
case NAVIGATOR_SWIPE_DOWN:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_BACK, KEY_DOWN));
break;
case NAVIGATOR_EXIT:
return;
}
} else if (domain == sensor_get_domain()) {
if (SENSOR_ACCELEROMETER_READING == bps_event_get_code(event)) {
sensor_event_get_xyz(event, &(input_state.acc.y), &(input_state.acc.x), &(input_state.acc.z));
AxisInput axis;
axis.deviceId = DEVICE_ID_ACCELEROMETER;
axis.flags = 0;
axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_X;
axis.value = input_state.acc.x;
NativeAxis(axis);
axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_Y;
axis.value = input_state.acc.y;
NativeAxis(axis);
axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_Z;
axis.value = input_state.acc.z;
NativeAxis(axis);
}
}
}
UpdateInputState(&input_state);
// Work in Progress
// Currently: Render to HDMI port (eg. 1080p) when in game. Render to device when in menu.
// Idea: Render to all displays. Controls go to internal, game goes to external(s).
if (GetUIState() == UISTATE_INGAME && !emulating) {
emulating = true;
switchDisplay(screen_emu);
if (g_Config.iShowFPSCounter == 4) {
int options = SCREEN_DEBUG_STATISTICS;
screen_set_window_property_iv(screen_win[0], SCREEN_PROPERTY_DEBUG, &options);
}
} else if (GetUIState() != UISTATE_INGAME && emulating) {
emulating = false;
switchDisplay(screen_ui);
}
time_update();
UpdateRunLoop();
// This handles VSync
if (emulating)
eglSwapBuffers(egl_disp[screen_emu], egl_surf[screen_emu]);
else
eglSwapBuffers(egl_disp[screen_ui], egl_surf[screen_ui]);
}
}
void BlackberryMain::endMain() {
screen_stop_events(screen_cxt);
bps_shutdown();
NativeShutdownGraphics();
delete audio;
NativeShutdown();
killDisplays();
net::Shutdown();
screen_destroy_context(screen_cxt);
}
// Entry Point
int main(int argc, char *argv[]) {
delete new BlackberryMain(argc, argv);
return 0;
}
+93
View File
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2013 Sacha Refshauge
*
*/
// Blackberry implementation of the framework.
#ifndef BLACKBERRYMAIN_H
#define BLACKBERRYMAIN_H
// Blackberry specific
#include <bps/bps.h> // Blackberry Platform Services
#include <bps/screen.h> // Blackberry Window Manager
#include <bps/navigator.h> // Invoke Service
#include <bps/virtualkeyboard.h>// Keyboard Service
#include <bps/sensor.h> // Accelerometer
#include <bps/dialog.h> // Dialog Service (Toast=BB10)
#include <bps/vibration.h> // Vibrate Service (BB10)
#include "sys/keycodes.h"
#include "input/keycodes.h"
// Display
#include <EGL/egl.h>
#include <screen/screen.h>
#include <sys/platform.h>
#include <GLES2/gl2.h>
#include "Core/System.h"
// Native
#include "base/timeutil.h"
#include "gfx_es2/glsl_program.h"
#include "file/zip_read.h"
#include "base/NativeApp.h"
#include "input/input_state.h"
#include "net/resolve.h"
#include "base/display.h"
#include "BlackberryAudio.h"
struct dispdata_t {
int attached;
int type;
bool realised;
int width, height;
};
class BlackberryMain
{
public:
BlackberryMain(int argc, char *argv[]) :
emulating(false),
screen_ui(0), screen_emu(0),
old_buttons(0),
egl_cont(EGL_NO_CONTEXT)
{
startMain(argc, argv);
}
~BlackberryMain() {
endMain();
}
void startMain(int argc, char *argv[]);
private:
void runMain();
void endMain();
void handleInput(screen_event_t screen_event);
const char* displayTypeString(int type);
void startDisplays();
void* startDisplay(int idx);
void realiseDisplay(int idx);
void unrealiseDisplay(int idx);
void switchDisplay(int idx);
void killDisplays();
void killDisplay(int idx, bool killContext);
BlackberryAudio* audio;
dispdata_t *displays;
int dpi;
int ndisplays;
int screen_ui, screen_emu;
bool emulating;
int old_buttons;
EGLDisplay* egl_disp;
EGLSurface* egl_surf;
EGLContext egl_cont;
EGLConfig egl_conf;
screen_context_t screen_cxt;
screen_display_t *screen_dpy;
screen_window_t *screen_win;
};
#endif
+20
View File
@@ -0,0 +1,20 @@
set(SRCS
colorutil.cpp
timeutil.cpp
../thread/threadutil.cpp
error_context.cpp
display.cpp
buffer.cpp
backtrace.cpp)
add_library(base STATIC ${SRCS})
if(UNIX)
add_definitions(-fPIC)
endif(UNIX)
add_library(timeutil STATIC timeutil.cpp)
if(UNIX)
add_definitions(-fPIC)
endif(UNIX)
+100
View File
@@ -0,0 +1,100 @@
#include <map>
#include "util/const_map.h"
static const std::map<int, int> KeyMapPadBlackberrytoNative = InitConstMap<int, int>
(SCREEN_A_GAME_BUTTON, NKCODE_BUTTON_A)
(SCREEN_B_GAME_BUTTON, NKCODE_BUTTON_B)
(SCREEN_C_GAME_BUTTON, NKCODE_BUTTON_C)
(SCREEN_X_GAME_BUTTON, NKCODE_BUTTON_X)
(SCREEN_Y_GAME_BUTTON, NKCODE_BUTTON_Y)
(SCREEN_Z_GAME_BUTTON, NKCODE_BUTTON_Z)
(SCREEN_MENU1_GAME_BUTTON, NKCODE_BUTTON_START)
(SCREEN_MENU2_GAME_BUTTON, NKCODE_BUTTON_SELECT)
(SCREEN_MENU3_GAME_BUTTON, NKCODE_MENU)
(SCREEN_MENU4_GAME_BUTTON, NKCODE_BACK)
(SCREEN_L1_GAME_BUTTON, NKCODE_BUTTON_L1)
(SCREEN_L2_GAME_BUTTON, NKCODE_BUTTON_L2)
(SCREEN_L3_GAME_BUTTON, NKCODE_OUYA_BUTTON_L3)
(SCREEN_R1_GAME_BUTTON, NKCODE_BUTTON_R1)
(SCREEN_R2_GAME_BUTTON, NKCODE_BUTTON_R2)
(SCREEN_R3_GAME_BUTTON, NKCODE_OUYA_BUTTON_R3)
(SCREEN_DPAD_UP_GAME_BUTTON, NKCODE_DPAD_UP)
(SCREEN_DPAD_DOWN_GAME_BUTTON, NKCODE_DPAD_DOWN)
(SCREEN_DPAD_LEFT_GAME_BUTTON, NKCODE_DPAD_LEFT)
(SCREEN_DPAD_RIGHT_GAME_BUTTON, NKCODE_DPAD_RIGHT);
// TODO: Add any missing keys
static const std::map<int, int> KeyMapRawBlackberrytoNative = InitConstMap<int, int>
(KEYCODE_P, NKCODE_P)
(KEYCODE_O, NKCODE_O)
(KEYCODE_I, NKCODE_I)
(KEYCODE_U, NKCODE_U)
(KEYCODE_Y, NKCODE_Y)
(KEYCODE_T, NKCODE_T)
(KEYCODE_R, NKCODE_R)
(KEYCODE_E, NKCODE_E)
(KEYCODE_W, NKCODE_W)
(KEYCODE_Q, NKCODE_Q)
(KEYCODE_L, NKCODE_L)
(KEYCODE_K, NKCODE_K)
(KEYCODE_J, NKCODE_J)
(KEYCODE_H, NKCODE_H)
(KEYCODE_G, NKCODE_G)
(KEYCODE_F, NKCODE_F)
(KEYCODE_D, NKCODE_D)
(KEYCODE_S, NKCODE_S)
(KEYCODE_A, NKCODE_A)
(KEYCODE_M, NKCODE_M)
(KEYCODE_N, NKCODE_N)
(KEYCODE_B, NKCODE_B)
(KEYCODE_V, NKCODE_V)
(KEYCODE_C, NKCODE_C)
(KEYCODE_X, NKCODE_X)
(KEYCODE_Z, NKCODE_Z)
(KEYCODE_COMMA, NKCODE_COMMA)
(KEYCODE_PERIOD, NKCODE_PERIOD)
(KEYCODE_LEFT_ALT, NKCODE_ALT_LEFT)
(KEYCODE_RIGHT_ALT, NKCODE_ALT_RIGHT)
(KEYCODE_LEFT_SHIFT, NKCODE_SHIFT_LEFT)
(KEYCODE_RIGHT_SHIFT, NKCODE_SHIFT_RIGHT)
(KEYCODE_TAB, NKCODE_TAB)
(KEYCODE_SPACE, NKCODE_SPACE)
(KEYCODE_RETURN, NKCODE_ENTER)
(KEYCODE_MINUS, NKCODE_MINUS)
(KEYCODE_EQUAL, NKCODE_EQUALS)
(KEYCODE_LEFT_BRACKET, NKCODE_LEFT_BRACKET)
(KEYCODE_RIGHT_BRACKET, NKCODE_RIGHT_BRACKET)
(KEYCODE_BACK_SLASH, NKCODE_BACKSLASH)
(KEYCODE_SEMICOLON, NKCODE_SEMICOLON)
(KEYCODE_APOSTROPHE, NKCODE_APOSTROPHE)
(KEYCODE_SLASH, NKCODE_SLASH)
(KEYCODE_AT, NKCODE_AT)
(KEYCODE_PLUS, NKCODE_PLUS)
(KEYCODE_ESCAPE, NKCODE_ESCAPE)
(KEYCODE_DELETE, NKCODE_FORWARD_DEL)
(KEYCODE_LEFT_CTRL, NKCODE_CTRL_LEFT)
(KEYCODE_RIGHT_CTRL, NKCODE_CTRL_RIGHT)
(KEYCODE_CAPS_LOCK, NKCODE_CAPS_LOCK)
(KEYCODE_HOME, NKCODE_MOVE_HOME)
(KEYCODE_END, NKCODE_MOVE_END)
(KEYCODE_INSERT, NKCODE_INSERT)
(KEYCODE_ZERO, NKCODE_0)
(KEYCODE_ONE, NKCODE_1)
(KEYCODE_TWO, NKCODE_2)
(KEYCODE_THREE, NKCODE_3)
(KEYCODE_FOUR, NKCODE_4)
(KEYCODE_FIVE, NKCODE_5)
(KEYCODE_SIX, NKCODE_6)
(KEYCODE_SEVEN, NKCODE_7)
(KEYCODE_EIGHT, NKCODE_8)
(KEYCODE_NINE, NKCODE_9)
(KEYCODE_KP_DIVIDE, NKCODE_NUMPAD_DIVIDE)
(KEYCODE_KP_MULTIPLY, NKCODE_NUMPAD_MULTIPLY)
(KEYCODE_KP_MINUS, NKCODE_NUMPAD_SUBTRACT)
(KEYCODE_KP_PLUS, NKCODE_NUMPAD_ADD)
(KEYCODE_KP_ENTER, NKCODE_NUMPAD_ENTER)
(KEYCODE_LEFT, NKCODE_DPAD_LEFT)
(KEYCODE_UP, NKCODE_DPAD_UP)
(KEYCODE_RIGHT, NKCODE_DPAD_RIGHT)
(KEYCODE_DOWN, NKCODE_DPAD_DOWN);
+101
View File
@@ -0,0 +1,101 @@
#include "util/const_map.h"
#include <map>
// TODO: Add any missing keys
static const std::map<int, int> KeyMapRawQttoNative = InitConstMap<int, int>
(Qt::Key_P, NKCODE_P)
(Qt::Key_O, NKCODE_O)
(Qt::Key_I, NKCODE_I)
(Qt::Key_U, NKCODE_U)
(Qt::Key_Y, NKCODE_Y)
(Qt::Key_T, NKCODE_T)
(Qt::Key_R, NKCODE_R)
(Qt::Key_E, NKCODE_E)
(Qt::Key_W, NKCODE_W)
(Qt::Key_Q, NKCODE_Q)
(Qt::Key_L, NKCODE_L)
(Qt::Key_K, NKCODE_K)
(Qt::Key_J, NKCODE_J)
(Qt::Key_H, NKCODE_H)
(Qt::Key_G, NKCODE_G)
(Qt::Key_F, NKCODE_F)
(Qt::Key_D, NKCODE_D)
(Qt::Key_S, NKCODE_S)
(Qt::Key_A, NKCODE_A)
(Qt::Key_M, NKCODE_M)
(Qt::Key_N, NKCODE_N)
(Qt::Key_B, NKCODE_B)
(Qt::Key_V, NKCODE_V)
(Qt::Key_C, NKCODE_C)
(Qt::Key_X, NKCODE_X)
(Qt::Key_Z, NKCODE_Z)
(Qt::Key_P + 0x20, NKCODE_P)
(Qt::Key_O + 0x20, NKCODE_O)
(Qt::Key_I + 0x20, NKCODE_I)
(Qt::Key_U + 0x20, NKCODE_U)
(Qt::Key_Y + 0x20, NKCODE_Y)
(Qt::Key_T + 0x20, NKCODE_T)
(Qt::Key_R + 0x20, NKCODE_R)
(Qt::Key_E + 0x20, NKCODE_E)
(Qt::Key_W + 0x20, NKCODE_W)
(Qt::Key_Q + 0x20, NKCODE_Q)
(Qt::Key_L + 0x20, NKCODE_L)
(Qt::Key_K + 0x20, NKCODE_K)
(Qt::Key_J + 0x20, NKCODE_J)
(Qt::Key_H + 0x20, NKCODE_H)
(Qt::Key_G + 0x20, NKCODE_G)
(Qt::Key_F + 0x20, NKCODE_F)
(Qt::Key_D + 0x20, NKCODE_D)
(Qt::Key_S + 0x20, NKCODE_S)
(Qt::Key_A + 0x20, NKCODE_A)
(Qt::Key_M + 0x20, NKCODE_M)
(Qt::Key_N + 0x20, NKCODE_N)
(Qt::Key_B + 0x20, NKCODE_B)
(Qt::Key_V + 0x20, NKCODE_V)
(Qt::Key_C + 0x20, NKCODE_C)
(Qt::Key_X + 0x20, NKCODE_X)
(Qt::Key_Z + 0x20, NKCODE_Z)
(Qt::Key_Comma, NKCODE_COMMA)
(Qt::Key_Period, NKCODE_PERIOD)
(Qt::Key_Alt, NKCODE_ALT_LEFT)
(Qt::Key_Shift, NKCODE_SHIFT_LEFT)
(Qt::Key_Tab, NKCODE_TAB)
(Qt::Key_Space, NKCODE_SPACE)
(Qt::Key_Return, NKCODE_ENTER)
(Qt::Key_Minus, NKCODE_MINUS)
(Qt::Key_Minus, NKCODE_PLUS)
(Qt::Key_Equal, NKCODE_EQUALS)
(Qt::Key_BracketLeft, NKCODE_LEFT_BRACKET)
(Qt::Key_BracketRight, NKCODE_RIGHT_BRACKET)
(Qt::Key_Backslash, NKCODE_BACKSLASH)
(Qt::Key_Semicolon, NKCODE_SEMICOLON)
(Qt::Key_Apostrophe, NKCODE_APOSTROPHE)
(Qt::Key_Slash, NKCODE_SLASH)
(Qt::Key_At, NKCODE_AT)
(Qt::Key_PageUp, NKCODE_PAGE_UP)
(Qt::Key_PageDown, NKCODE_PAGE_DOWN)
(Qt::Key_Escape, NKCODE_ESCAPE)
(Qt::Key_Delete, NKCODE_FORWARD_DEL)
(Qt::Key_Control, NKCODE_CTRL_LEFT)
(Qt::Key_CapsLock, NKCODE_CAPS_LOCK)
(Qt::Key_Home, NKCODE_MOVE_HOME)
(Qt::Key_End, NKCODE_MOVE_END)
(Qt::Key_Insert, NKCODE_INSERT)
(Qt::Key_Period, NKCODE_NUMPAD_DOT)
(Qt::Key_Enter, NKCODE_NUMPAD_ENTER)
(Qt::Key_1, NKCODE_1)
(Qt::Key_2, NKCODE_2)
(Qt::Key_3, NKCODE_3)
(Qt::Key_4, NKCODE_4)
(Qt::Key_5, NKCODE_5)
(Qt::Key_6, NKCODE_6)
(Qt::Key_7, NKCODE_7)
(Qt::Key_8, NKCODE_8)
(Qt::Key_9, NKCODE_9)
(Qt::Key_0, NKCODE_0)
(Qt::Key_Left, NKCODE_DPAD_LEFT)
(Qt::Key_Up, NKCODE_DPAD_UP)
(Qt::Key_Right, NKCODE_DPAD_RIGHT)
(Qt::Key_Down, NKCODE_DPAD_DOWN)
(Qt::Key_Back, NKCODE_BACK);
+111
View File
@@ -0,0 +1,111 @@
#pragma once
#include "util/const_map.h"
#include <map>
// TODO: Add any missing keys
static const std::map<int, int> KeyMapRawSDLtoNative = InitConstMap<int, int>
(SDLK_p, NKCODE_P)
(SDLK_o, NKCODE_O)
(SDLK_i, NKCODE_I)
(SDLK_u, NKCODE_U)
(SDLK_y, NKCODE_Y)
(SDLK_t, NKCODE_T)
(SDLK_r, NKCODE_R)
(SDLK_e, NKCODE_E)
(SDLK_w, NKCODE_W)
(SDLK_q, NKCODE_Q)
(SDLK_l, NKCODE_L)
(SDLK_k, NKCODE_K)
(SDLK_j, NKCODE_J)
(SDLK_h, NKCODE_H)
(SDLK_g, NKCODE_G)
(SDLK_f, NKCODE_F)
(SDLK_d, NKCODE_D)
(SDLK_s, NKCODE_S)
(SDLK_a, NKCODE_A)
(SDLK_m, NKCODE_M)
(SDLK_n, NKCODE_N)
(SDLK_b, NKCODE_B)
(SDLK_v, NKCODE_V)
(SDLK_c, NKCODE_C)
(SDLK_x, NKCODE_X)
(SDLK_z, NKCODE_Z)
(SDLK_COMMA, NKCODE_COMMA)
(SDLK_PERIOD, NKCODE_PERIOD)
(SDLK_LALT, NKCODE_ALT_LEFT)
(SDLK_RALT, NKCODE_ALT_RIGHT)
(SDLK_LSHIFT, NKCODE_SHIFT_LEFT)
(SDLK_RSHIFT, NKCODE_SHIFT_RIGHT)
(SDLK_TAB, NKCODE_TAB)
(SDLK_SPACE, NKCODE_SPACE)
(SDLK_RETURN, NKCODE_ENTER)
(SDLK_MINUS, NKCODE_MINUS)
(SDLK_EQUALS, NKCODE_EQUALS)
(SDLK_LEFTBRACKET, NKCODE_LEFT_BRACKET)
(SDLK_RIGHTBRACKET, NKCODE_RIGHT_BRACKET)
(SDLK_BACKSLASH, NKCODE_BACKSLASH)
(SDLK_SEMICOLON, NKCODE_SEMICOLON)
// (SDLK_, NKCODE_APOSTROPHE) // I cannot find the SDL equiv
(SDLK_SLASH, NKCODE_SLASH)
(SDLK_AT, NKCODE_AT)
(SDLK_PLUS, NKCODE_PLUS)
(SDLK_PAGEUP, NKCODE_PAGE_UP)
(SDLK_PAGEDOWN, NKCODE_PAGE_DOWN)
#ifdef MAEMO
(SDLK_BACKSPACE, NKCODE_ESCAPE)
#else
(SDLK_ESCAPE, NKCODE_ESCAPE)
(SDLK_BACKSPACE, NKCODE_DEL)
#endif
(SDLK_DELETE, NKCODE_FORWARD_DEL)
(SDLK_LCTRL, NKCODE_CTRL_LEFT)
(SDLK_RCTRL, NKCODE_CTRL_RIGHT)
(SDLK_CAPSLOCK, NKCODE_CAPS_LOCK)
(SDLK_HOME, NKCODE_MOVE_HOME)
(SDLK_END, NKCODE_MOVE_END)
(SDLK_INSERT, NKCODE_INSERT)
(SDLK_KP_0, NKCODE_NUMPAD_0)
(SDLK_KP_1, NKCODE_NUMPAD_1)
(SDLK_KP_2, NKCODE_NUMPAD_2)
(SDLK_KP_3, NKCODE_NUMPAD_3)
(SDLK_KP_4, NKCODE_NUMPAD_4)
(SDLK_KP_5, NKCODE_NUMPAD_5)
(SDLK_KP_6, NKCODE_NUMPAD_6)
(SDLK_KP_7, NKCODE_NUMPAD_7)
(SDLK_KP_8, NKCODE_NUMPAD_8)
(SDLK_KP_9, NKCODE_NUMPAD_9)
(SDLK_KP_DIVIDE, NKCODE_NUMPAD_DIVIDE)
(SDLK_KP_MULTIPLY, NKCODE_NUMPAD_MULTIPLY)
(SDLK_KP_MINUS, NKCODE_NUMPAD_SUBTRACT)
(SDLK_KP_PLUS, NKCODE_NUMPAD_ADD)
(SDLK_KP_PERIOD, NKCODE_NUMPAD_DOT)
(SDLK_KP_ENTER, NKCODE_NUMPAD_ENTER)
(SDLK_KP_EQUALS, NKCODE_NUMPAD_EQUALS)
(SDLK_1, NKCODE_1)
(SDLK_2, NKCODE_2)
(SDLK_3, NKCODE_3)
(SDLK_4, NKCODE_4)
(SDLK_5, NKCODE_5)
(SDLK_6, NKCODE_6)
(SDLK_7, NKCODE_7)
(SDLK_8, NKCODE_8)
(SDLK_9, NKCODE_9)
(SDLK_0, NKCODE_0)
(SDLK_F1, NKCODE_F1)
(SDLK_F2, NKCODE_F2)
(SDLK_F3, NKCODE_F3)
(SDLK_F4, NKCODE_F4)
(SDLK_F5, NKCODE_F5)
(SDLK_F6, NKCODE_F6)
(SDLK_F7, NKCODE_F7)
(SDLK_F8, NKCODE_F8)
(SDLK_F9, NKCODE_F9)
(SDLK_F10, NKCODE_F10)
(SDLK_F11, NKCODE_F11)
(SDLK_F12, NKCODE_F12)
(SDLK_LEFT, NKCODE_DPAD_LEFT)
(SDLK_UP, NKCODE_DPAD_UP)
(SDLK_RIGHT, NKCODE_DPAD_RIGHT)
(SDLK_DOWN, NKCODE_DPAD_DOWN);
+155
View File
@@ -0,0 +1,155 @@
#pragma once
#include <string>
// The Native App API.
//
// Implement these functions and you've got a native app. These are called
// from the framework, which exposes the native JNI api which is a bit
// more complicated.
// This is defined in input/input_state.h.
struct InputState;
struct TouchInput;
struct KeyInput;
struct AxisInput;
// The first function to get called, just write strings to the two pointers.
// This might get called multiple times in some implementations, you must be able to handle that.
void NativeGetAppInfo(std::string *app_dir_name, std::string *app_nice_name, bool *landscape, std::string *version);
// Generic host->C++ messaging, used for functionality like system-native popup input boxes.
void NativeMessageReceived(const char *message, const char *value);
// Easy way for the Java side to ask the C++ side for configuration options, such as
// the rotation lock which must be controlled from Java on Android.
// It is currently not called on non-Android platforms.
std::string NativeQueryConfig(std::string query);
// For the back button to work right, this should return true on your main or title screen.
// Otherwise, just return false.
bool NativeIsAtTopLevel();
// The very first function to be called after NativeGetAppInfo. Even NativeMix is not called
// before this, although it may be called at any point in time afterwards (on any thread!)
// This functions must NOT call OpenGL. Main thread.
void NativeInit(int argc, const char *argv[], const char *savegame_directory, const char *external_directory, const char *installID, bool fs=false);
// Runs after NativeInit() at some point. May (and probably should) call OpenGL.
// Should not initialize anything screen-size-dependent - do that in NativeResized.
void NativeInitGraphics();
// Signals that you need to destroy and recreate all buffered OpenGL resources,
// like textures, vbo etc.
void NativeDeviceLost();
// If you want to change DPI stuff (such as modifying dp_xres and dp_yres), this is the
// place to do it. You should only read g_dpi_scale and pixel_xres and pixel_yres in this,
// and only write dp_xres and dp_yres.
void NativeResized();
// Called ~sixty times a second, delivers the current input state.
// Main thread.
void NativeUpdate(InputState &input);
// Delivers touch events "instantly", without waiting for the next frame so that NativeUpdate can deliver.
// Useful for triggering audio events, saving a few ms.
// If you don't care about touch latency, just do a no-op implementation of this.
// time is not yet implemented. finger can be from 0 to 7, inclusive.
bool NativeTouch(const TouchInput &touch);
bool NativeKey(const KeyInput &key);
bool NativeAxis(const AxisInput &axis);
// Called when it's time to render. If the device can keep up, this
// will also be called sixty times per second. Main thread.
void NativeRender();
// This should render num_samples 44khz stereo samples.
// Try not to make too many assumptions on the granularity
// of num_samples.
// This function may be called from a totally separate thread from
// the rest of the game, so be careful with synchronization.
// Returns the number of samples actually output. The app should do everything it can
// to fill the buffer completely.
int NativeMix(short *audio, int num_samples);
void NativeSetMixer(void* mixer);
// Called when it's time to shutdown. After this has been called,
// no more calls to any other function will be made from the framework
// before process exit.
// The graphics context should still be active when calling this, as freeing
// of graphics resources happens here.
// Main thread.
void NativeShutdownGraphics();
void NativeShutdown();
// Called on app.onCreate and app.onDestroy (?). Tells the app to save/restore
// light state. If app was fully rebooted between these calls, it's okay if some minor
// state is lost (position in level) but the level currently playihg, or the song
// currently being edited, or whatever, should be restored properly. In this case,
// firstTime will be set so that appropriate action can be taken (or not taken when
// it's not set).
//
// Note that NativeRestore is always called on bootup.
void NativeRestoreState(bool firstTime); // onCreate
void NativeSaveState(); // onDestroy
// Calls back into Java / SDL
// These APIs must be implemented by every port (for example app-android.cpp, PCMain.cpp).
// You are free to call these.
void SystemToast(const char *text);
void ShowKeyboard();
void ShowAd(int x, int y, bool center_x);
// Vibrate either takes a number of milliseconds to vibrate unconditionally,
// or you can specify these constants for "standard" feedback. On Android,
// these will only be performed if haptic feedback is enabled globally.
// Also, on Android, these will work even if you don't have the VIBRATE permission,
// while generic vibration will not if you don't have it.
enum {
HAPTIC_SOFT_KEYBOARD = -1,
HAPTIC_VIRTUAL_KEY = -2,
HAPTIC_LONG_PRESS_ACTIVATED = -3,
};
void Vibrate(int length_ms);
void LaunchBrowser(const char *url);
void LaunchMarket(const char *url);
void LaunchEmail(const char *email_address);
bool System_InputBoxGetString(const char *title, const char *defaultValue, char *outValue, size_t outlength);
bool System_InputBoxGetWString(const wchar_t *title, const std::wstring &defaultValue, std::wstring &outValue);
void System_SendMessage(const char *command, const char *parameter);
// This will get muddy with multi-screen support :/ But this will always be the type of the main device.
enum SystemDeviceType {
DEVICE_TYPE_MOBILE = 0, // phones and pads
DEVICE_TYPE_TV = 1, // Android TV and similar
DEVICE_TYPE_DESKTOP = 2, // Desktop computer
};
enum SystemProperty {
SYSPROP_NAME,
SYSPROP_LANGREGION,
SYSPROP_CPUINFO,
SYSPROP_CLIPBOARD_TEXT,
SYSPROP_GPUDRIVER_VERSION,
// Available as Int:
SYSPROP_SYSTEMVERSION,
SYSPROP_DISPLAY_XRES,
SYSPROP_DISPLAY_YRES,
SYSPROP_DISPLAY_REFRESH_RATE, // returns 1000*the refresh rate in Hz as it can be non-integer
SYSPROP_MOGA_VERSION,
SYSPROP_DEVICE_TYPE,
// Exposed on Android. Choosing the optimal sample rate for audio
// will result in lower latencies. Buffer size is automatically matched
// by the OpenSL audio backend, only exposed here for debugging/info.
SYSPROP_AUDIO_SAMPLE_RATE,
SYSPROP_AUDIO_FRAMES_PER_BUFFER,
SYSPROP_AUDIO_OPTIMAL_SAMPLE_RATE,
SYSPROP_AUDIO_OPTIMAL_FRAMES_PER_BUFFER,
};
std::string System_GetProperty(SystemProperty prop);
int System_GetPropertyInt(SystemProperty prop);
+898
View File
@@ -0,0 +1,898 @@
// SDL/EGL implementation of the framework.
// This is quite messy due to platform-specific implementations and #ifdef's.
// Note: SDL1.2 implementation is deprecated and will soon be replaced by SDL2.0.
// If your platform is not supported, it is suggested to use Qt instead.
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <ShellAPI.h>
#else
#include <unistd.h>
#include <pwd.h>
#endif
#include "SDL.h"
#ifndef _WIN32
#include "SDL/SDLJoystick.h"
SDLJoystick *joystick = NULL;
#endif
#ifdef RPI
#include <bcm_host.h>
#endif
#include <algorithm>
#include "base/display.h"
#include "base/logging.h"
#include "base/timeutil.h"
#include "gfx/gl_common.h"
#include "gfx_es2/gpu_features.h"
#include "input/input_state.h"
#include "input/keycodes.h"
#include "net/resolve.h"
#include "base/NKCodeFromSDL.h"
#include "util/const_map.h"
#include "util/text/utf8.h"
#include "math/math_util.h"
#ifdef PPSSPP
// Bad: PPSSPP includes from native
#include "Core/System.h"
#include "Core/Core.h"
#include "Core/Config.h"
GlobalUIState lastUIState = UISTATE_MENU;
GlobalUIState GetUIState();
#endif
static SDL_Window* g_Screen = NULL;
static bool g_ToggleFullScreenNextFrame = false;
static int g_QuitRequested = 0;
static int g_DesktopWidth = 0;
static int g_DesktopHeight = 0;
#if defined(USING_EGL)
#include "EGL/egl.h"
#if !defined(USING_FBDEV)
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#endif
#include "SDL_syswm.h"
#include "math.h"
static EGLDisplay g_eglDisplay = NULL;
static EGLContext g_eglContext = NULL;
static EGLSurface g_eglSurface = NULL;
#ifdef USING_FBDEV
static EGLNativeDisplayType g_Display = NULL;
#else
static Display* g_Display = NULL;
#endif
static NativeWindowType g_Window = (NativeWindowType)NULL;
int8_t CheckEGLErrors(const std::string& file, uint16_t line) {
EGLenum error;
std::string errortext;
error = eglGetError();
switch (error)
{
case EGL_SUCCESS: case 0: return 0;
case EGL_NOT_INITIALIZED: errortext = "EGL_NOT_INITIALIZED"; break;
case EGL_BAD_ACCESS: errortext = "EGL_BAD_ACCESS"; break;
case EGL_BAD_ALLOC: errortext = "EGL_BAD_ALLOC"; break;
case EGL_BAD_ATTRIBUTE: errortext = "EGL_BAD_ATTRIBUTE"; break;
case EGL_BAD_CONTEXT: errortext = "EGL_BAD_CONTEXT"; break;
case EGL_BAD_CONFIG: errortext = "EGL_BAD_CONFIG"; break;
case EGL_BAD_CURRENT_SURFACE: errortext = "EGL_BAD_CURRENT_SURFACE"; break;
case EGL_BAD_DISPLAY: errortext = "EGL_BAD_DISPLAY"; break;
case EGL_BAD_SURFACE: errortext = "EGL_BAD_SURFACE"; break;
case EGL_BAD_MATCH: errortext = "EGL_BAD_MATCH"; break;
case EGL_BAD_PARAMETER: errortext = "EGL_BAD_PARAMETER"; break;
case EGL_BAD_NATIVE_PIXMAP: errortext = "EGL_BAD_NATIVE_PIXMAP"; break;
case EGL_BAD_NATIVE_WINDOW: errortext = "EGL_BAD_NATIVE_WINDOW"; break;
default: errortext = "unknown"; break;
}
printf( "ERROR: EGL Error detected in file %s at line %d: %s (0x%X)\n", file.c_str(), line, errortext.c_str(), error );
return 1;
}
#define EGL_ERROR(str, check) { \
if (check) CheckEGLErrors( __FILE__, __LINE__ ); \
printf("EGL ERROR: " str "\n"); \
return 1; \
}
int8_t EGL_Open() {
#ifdef USING_FBDEV
g_Display = ((EGLNativeDisplayType)0);
#else
if ((g_Display = XOpenDisplay(NULL)) == NULL)
EGL_ERROR("Unable to get display!", false);
#endif
if ((g_eglDisplay = eglGetDisplay((NativeDisplayType)g_Display)) == EGL_NO_DISPLAY)
EGL_ERROR("Unable to create EGL display.", true);
if (eglInitialize(g_eglDisplay, NULL, NULL) != EGL_TRUE)
EGL_ERROR("Unable to initialize EGL display.", true);
return 0;
}
int8_t EGL_Init() {
EGLConfig g_eglConfig;
EGLint g_numConfigs = 0;
EGLint attrib_list[]= {
// TODO: Should cycle through fallbacks, like on Android
#ifdef USING_FBDEV
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
#endif
EGL_DEPTH_SIZE, 16,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
#ifdef USING_GLES2
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
#else
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
#endif
EGL_SAMPLE_BUFFERS, 0,
EGL_SAMPLES, 0,
#ifdef MAEMO
EGL_BUFFER_SIZE, 16,
#endif
EGL_NONE};
const EGLint attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
EGLBoolean result = eglChooseConfig(g_eglDisplay, attrib_list, &g_eglConfig, 1, &g_numConfigs);
if (result != EGL_TRUE || g_numConfigs == 0) EGL_ERROR("Unable to query for available configs.", true);
g_eglContext = eglCreateContext(g_eglDisplay, g_eglConfig, NULL, attributes );
if (g_eglContext == EGL_NO_CONTEXT) EGL_ERROR("Unable to create GLES context!", true);
#if !defined(USING_FBDEV)
//Get the SDL window handle
SDL_SysWMinfo sysInfo; //Will hold our Window information
SDL_VERSION(&sysInfo.version); //Set SDL version
#endif
#ifdef USING_FBDEV
g_Window = (NativeWindowType)NULL;
#else
g_Window = (NativeWindowType)sysInfo.info.x11.window;
#endif
g_eglSurface = eglCreateWindowSurface(g_eglDisplay, g_eglConfig, g_Window, 0);
if (g_eglSurface == EGL_NO_SURFACE)
EGL_ERROR("Unable to create EGL surface!", true);
if (eglMakeCurrent(g_eglDisplay, g_eglSurface, g_eglSurface, g_eglContext) != EGL_TRUE)
EGL_ERROR("Unable to make GLES context current.", true);
return 0;
}
void EGL_Close() {
if (g_eglDisplay != NULL) {
eglMakeCurrent(g_eglDisplay, NULL, NULL, EGL_NO_CONTEXT);
if (g_eglContext != NULL) {
eglDestroyContext(g_eglDisplay, g_eglContext);
}
if (g_eglSurface != NULL) {
eglDestroySurface(g_eglDisplay, g_eglSurface);
}
eglTerminate(g_eglDisplay);
g_eglDisplay = NULL;
}
if (g_Display != NULL) {
#if !defined(USING_FBDEV)
XCloseDisplay(g_Display);
#endif
g_Display = NULL;
}
g_eglSurface = NULL;
g_eglContext = NULL;
}
#endif
int getDisplayNumber(void)
{
int displayNumber = 0;
char * displayNumberStr;
//get environment
displayNumberStr=getenv("SDL_VIDEO_FULLSCREEN_HEAD");
if (displayNumberStr)
{
displayNumber = atoi(displayNumberStr);
}
return displayNumber;
}
// Simple implementations of System functions
void SystemToast(const char *text) {
#ifdef _WIN32
MessageBox(0, text, "Toast!", MB_ICONINFORMATION);
#else
puts(text);
#endif
}
void ShowKeyboard() {
// Irrelevant on PC
}
void Vibrate(int length_ms) {
// Ignore on PC
}
void System_SendMessage(const char *command, const char *parameter) {
if (!strcmp(command, "toggle_fullscreen")) {
g_ToggleFullScreenNextFrame = true;
} else if (!strcmp(command, "finish")) {
// Do a clean exit
g_QuitRequested = true;
}
}
void LaunchBrowser(const char *url) {
#if defined(MOBILE_DEVICE)
ILOG("Would have gone to %s but LaunchBrowser is not implemented on this platform", url);
#elif defined(_WIN32)
ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL);
#elif defined(__APPLE__)
std::string command = std::string("open ") + url;
system(command.c_str());
#else
std::string command = std::string("xdg-open ") + url;
int err = system(command.c_str());
if (err) {
ILOG("Would have gone to %s but xdg-utils seems not to be installed", url)
}
#endif
}
void LaunchMarket(const char *url) {
#if defined(MOBILE_DEVICE)
ILOG("Would have gone to %s but LaunchMarket is not implemented on this platform", url);
#elif defined(_WIN32)
ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL);
#elif defined(__APPLE__)
std::string command = std::string("open ") + url;
system(command.c_str());
#else
std::string command = std::string("xdg-open ") + url;
int err = system(command.c_str());
if (err) {
ILOG("Would have gone to %s but xdg-utils seems not to be installed", url)
}
#endif
}
void LaunchEmail(const char *email_address) {
#if defined(MOBILE_DEVICE)
ILOG("Would have opened your email client for %s but LaunchEmail is not implemented on this platform", email_address);
#elif defined(_WIN32)
ShellExecute(NULL, "open", (std::string("mailto:") + email_address).c_str(), NULL, NULL, SW_SHOWNORMAL);
#elif defined(__APPLE__)
std::string command = std::string("open mailto:") + email_address;
system(command.c_str());
#else
std::string command = std::string("xdg-email ") + email_address;
int err = system(command.c_str());
if (err) {
ILOG("Would have gone to %s but xdg-utils seems not to be installed", email_address)
}
#endif
}
std::string System_GetProperty(SystemProperty prop) {
switch (prop) {
case SYSPROP_NAME:
#ifdef _WIN32
return "SDL:Windows";
#elif __linux__
return "SDL:Linux";
#elif __APPLE__
return "SDL:OSX";
#else
return "SDL:";
#endif
case SYSPROP_LANGREGION:
return "en_US";
default:
return "";
}
}
int System_GetPropertyInt(SystemProperty prop) {
switch (prop) {
case SYSPROP_AUDIO_SAMPLE_RATE:
return 44100;
case SYSPROP_DISPLAY_REFRESH_RATE:
return 60000;
case SYSPROP_DEVICE_TYPE:
#if defined(MOBILE_DEVICE)
return DEVICE_TYPE_MOBILE;
#else
return DEVICE_TYPE_DESKTOP;
#endif
default:
return -1;
}
}
InputState input_state;
static const int legacyKeyMap[] {
NKCODE_X, //A
NKCODE_S, //B
NKCODE_Z, //X
NKCODE_A, //Y
NKCODE_W, //LBUMPER
NKCODE_Q, //RBUMPER
NKCODE_1, //START
NKCODE_2, //SELECT
NKCODE_DPAD_UP, //UP
NKCODE_DPAD_DOWN, //DOWN
NKCODE_DPAD_LEFT, //LEFT
NKCODE_DPAD_RIGHT, //RIGHT
0, //MENU (SwipeDown)
NKCODE_ESCAPE, //BACK
NKCODE_I, //JOY UP
NKCODE_K, //JOY DOWN
NKCODE_J, //JOY LEFT
NKCODE_L, //JOY RIGHT
};
void SimulateGamepad(const uint8_t *keys, InputState *input) {
// Legacy key mapping.
input->pad_buttons = 0;
input->pad_lstick_x = 0;
input->pad_lstick_y = 0;
input->pad_rstick_x = 0;
input->pad_rstick_y = 0;
}
extern void mixaudio(void *userdata, Uint8 *stream, int len) {
NativeMix((short *)stream, len / 4);
}
// returns -1 on failure
static int parseInt(const char *str) {
int val;
int retval = sscanf(str, "%d", &val);
printf("%i = scanf %s\n", retval, str);
if (retval != 1) {
return -1;
} else {
return val;
}
}
static float parseFloat(const char *str) {
float val;
int retval = sscanf(str, "%f", &val);
printf("%i = sscanf %s\n", retval, str);
if (retval != 1) {
return -1.0f;
} else {
return val;
}
}
void ToggleFullScreenIfFlagSet() {
if (g_ToggleFullScreenNextFrame) {
g_ToggleFullScreenNextFrame = false;
Uint32 window_flags = SDL_GetWindowFlags(g_Screen);
SDL_SetWindowFullscreen(g_Screen, window_flags ^ SDL_WINDOW_FULLSCREEN_DESKTOP);
}
}
#ifdef _WIN32
#undef main
#endif
int main(int argc, char *argv[]) {
#ifdef RPI
bcm_host_init();
#endif
putenv((char*)"SDL_VIDEO_CENTERED=1");
std::string app_name;
std::string app_name_nice;
std::string version;
bool landscape;
NativeGetAppInfo(&app_name, &app_name_nice, &landscape, &version);
net::Init();
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO) < 0) {
fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
return 1;
}
#ifdef __APPLE__
// Make sure to request a somewhat modern GL context at least - the
// latest supported by MacOSX (really, really sad...)
// Requires SDL 2.0
// We really should upgrade to SDL 2.0 soon.
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
//SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
#endif
#ifdef USING_EGL
if (EGL_Open())
return 1;
#endif
// Get the video info before doing anything else, so we don't get skewed resolution results.
// TODO: support multiple displays correctly
SDL_DisplayMode displayMode;
int should_be_zero = SDL_GetCurrentDisplayMode(0, &displayMode);
if (should_be_zero != 0) {
fprintf(stderr, "Could not get display mode: %s\n", SDL_GetError());
return 1;
}
g_DesktopWidth = displayMode.w;
g_DesktopHeight = displayMode.h;
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetSwapInterval(1);
Uint32 mode;
#ifdef USING_GLES2
mode = SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN;
#else
mode = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
#endif
int set_xres = -1;
int set_yres = -1;
bool portrait = false;
bool set_ipad = false;
float set_dpi = 1.0f;
float set_scale = 1.0f;
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i],"--fullscreen"))
mode |= SDL_WINDOW_FULLSCREEN_DESKTOP;
if (set_xres == -2) {
set_xres = parseInt(argv[i]);
} else if (set_yres == -2) {
set_yres = parseInt(argv[i]);
}
if (set_dpi == -2)
set_dpi = parseFloat(argv[i]);
if (set_scale == -2)
set_scale = parseFloat(argv[i]);
if (!strcmp(argv[i],"--xres"))
set_xres = -2;
if (!strcmp(argv[i],"--yres"))
set_yres = -2;
if (!strcmp(argv[i],"--dpi"))
set_dpi = -2;
if (!strcmp(argv[i],"--scale"))
set_scale = -2;
if (!strcmp(argv[i],"--ipad"))
set_ipad = true;
if (!strcmp(argv[i],"--portrait"))
portrait = true;
}
// Is resolution is too low to run windowed
if (g_DesktopWidth < 480 * 2 && g_DesktopHeight < 272 * 2) {
mode |= SDL_WINDOW_FULLSCREEN_DESKTOP;
}
if (mode & SDL_WINDOW_FULLSCREEN_DESKTOP) {
pixel_xres = g_DesktopWidth;
pixel_yres = g_DesktopHeight;
#ifdef PPSSPP
g_Config.bFullScreen = true;
#endif
} else {
// set a sensible default resolution (2x)
pixel_xres = 480 * 2 * set_scale;
pixel_yres = 272 * 2 * set_scale;
if (portrait) {
std::swap(pixel_xres, pixel_yres);
}
#ifdef PPSSPP
g_Config.bFullScreen = false;
#endif
}
set_dpi = 1.0f / set_dpi;
if (set_ipad) {
pixel_xres = 1024;
pixel_yres = 768;
}
if (!landscape) {
std::swap(pixel_xres, pixel_yres);
}
if (set_xres > 0) {
pixel_xres = set_xres;
}
if (set_yres > 0) {
pixel_yres = set_yres;
}
float dpi_scale = 1.0f;
if (set_dpi > 0) {
dpi_scale = set_dpi;
}
dp_xres = (float)pixel_xres * dpi_scale;
dp_yres = (float)pixel_yres * dpi_scale;
g_Screen = SDL_CreateWindow(app_name_nice.c_str(), SDL_WINDOWPOS_UNDEFINED_DISPLAY(getDisplayNumber()),\
SDL_WINDOWPOS_UNDEFINED, pixel_xres, pixel_yres, mode);
if (g_Screen == NULL) {
fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError());
SDL_Quit();
return 2;
}
SDL_GLContext glContext = SDL_GL_CreateContext(g_Screen);
if (glContext == NULL) {
fprintf(stderr, "SDL_GL_CreateContext failed: %s\n", SDL_GetError());
SDL_Quit();
return 2;
}
#ifdef USING_EGL
EGL_Init();
#endif
#ifdef PPSSPP
SDL_SetWindowTitle(g_Screen, (app_name_nice + " " + PPSSPP_GIT_VERSION).c_str());
#endif
#ifdef MOBILE_DEVICE
SDL_ShowCursor(SDL_DISABLE);
#endif
#ifndef USING_GLES2
if (GLEW_OK != glewInit()) {
printf("Failed to initialize glew!\n");
return 1;
}
if (GLEW_VERSION_2_0) {
printf("OpenGL 2.0 or higher.\n");
} else {
printf("Sorry, this program requires OpenGL 2.0.\n");
return 1;
}
#endif
#ifdef _MSC_VER
// VFSRegister("temp/", new DirectoryAssetReader("E:\\Temp\\"));
TCHAR path[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, path);
PathAppend(path, (app_name + "\\").c_str());
#else
// Mac / Linux
char path[2048];
const char *the_path = getenv("HOME");
if (!the_path) {
struct passwd* pwd = getpwuid(getuid());
if (pwd)
the_path = pwd->pw_dir;
}
strcpy(path, the_path);
if (path[strlen(path)-1] != '/')
strcat(path, "/");
#endif
#ifdef _WIN32
NativeInit(argc, (const char **)argv, path, "D:\\", "BADCOFFEE");
#else
NativeInit(argc, (const char **)argv, path, "/tmp", "BADCOFFEE");
#endif
pixel_in_dps = (float)pixel_xres / dp_xres;
g_dpi_scale = dp_xres / (float)pixel_xres;
printf("Pixels: %i x %i\n", pixel_xres, pixel_yres);
printf("Virtual pixels: %i x %i\n", dp_xres, dp_yres);
NativeInitGraphics();
NativeResized();
SDL_AudioSpec fmt, ret_fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.freq = 44100;
fmt.format = AUDIO_S16;
fmt.channels = 2;
fmt.samples = 2048;
fmt.callback = &mixaudio;
fmt.userdata = (void *)0;
if (SDL_OpenAudio(&fmt, &ret_fmt) < 0) {
ELOG("Failed to open audio: %s", SDL_GetError());
} else {
if (ret_fmt.samples != fmt.samples) // Notify, but still use it
ELOG("Output audio samples: %d (requested: %d)", ret_fmt.samples, fmt.samples);
if (ret_fmt.freq != fmt.freq || ret_fmt.format != fmt.format || ret_fmt.channels != fmt.channels) {
ELOG("Sound buffer format does not match requested format.");
ELOG("Output audio freq: %d (requested: %d)", ret_fmt.freq, fmt.freq);
ELOG("Output audio format: %d (requested: %d)", ret_fmt.format, fmt.format);
ELOG("Output audio channels: %d (requested: %d)", ret_fmt.channels, fmt.channels);
ELOG("Provided output format does not match requirement, turning audio off");
SDL_CloseAudio();
}
}
// Audio must be unpaused _after_ NativeInit()
SDL_PauseAudio(0);
#ifndef _WIN32
joystick = new SDLJoystick();
#endif
EnableFZ();
int framecount = 0;
float t = 0;
float lastT = 0;
uint32_t pad_buttons = 0; // legacy pad buttons
while (true) {
input_state.accelerometer_valid = false;
input_state.mouse_valid = true;
SDL_Event event;
while (SDL_PollEvent(&event)) {
float mx = event.motion.x * g_dpi_scale;
float my = event.motion.y * g_dpi_scale;
switch (event.type) {
case SDL_QUIT:
g_QuitRequested = 1;
break;
#if !defined(MOBILE_DEVICE)
case SDL_WINDOWEVENT:
switch (event.window.event) {
case SDL_WINDOWEVENT_RESIZED:
{
Uint32 window_flags = SDL_GetWindowFlags(g_Screen);
bool fullscreen = (window_flags & SDL_WINDOW_FULLSCREEN);
pixel_xres = event.window.data1;
pixel_yres = event.window.data2;
dp_xres = (float)pixel_xres * dpi_scale;
dp_yres = (float)pixel_yres * dpi_scale;
NativeResized();
#if defined(PPSSPP)
// Set variable here in case fullscreen was toggled by hotkey
g_Config.bFullScreen = fullscreen;
// Hide/Show cursor correctly toggling fullscreen
if (lastUIState == UISTATE_INGAME && fullscreen && !g_Config.bShowTouchControls) {
SDL_ShowCursor(SDL_DISABLE);
} else if (lastUIState != UISTATE_INGAME || !fullscreen) {
SDL_ShowCursor(SDL_ENABLE);
}
#endif
break;
}
break;
}
#endif
case SDL_KEYDOWN:
{
int k = event.key.keysym.sym;
KeyInput key;
key.flags = KEY_DOWN;
key.keyCode = KeyMapRawSDLtoNative.find(k)->second;
key.deviceId = DEVICE_ID_KEYBOARD;
NativeKey(key);
for (int i = 0; i < ARRAY_SIZE(legacyKeyMap); i++) {
if (legacyKeyMap[i] == key.keyCode)
pad_buttons |= 1 << i;
}
break;
}
case SDL_KEYUP:
{
int k = event.key.keysym.sym;
KeyInput key;
key.flags = KEY_UP;
key.keyCode = KeyMapRawSDLtoNative.find(k)->second;
key.deviceId = DEVICE_ID_KEYBOARD;
NativeKey(key);
for (int i = 0; i < ARRAY_SIZE(legacyKeyMap); i++) {
if (legacyKeyMap[i] == key.keyCode)
pad_buttons &= ~(1 << i);
}
break;
}
case SDL_TEXTINPUT:
{
int pos = 0;
int c = u8_nextchar(event.text.text, &pos);
KeyInput key;
key.flags = KEY_CHAR;
key.keyCode = c;
key.deviceId = DEVICE_ID_KEYBOARD;
NativeKey(key);
break;
}
case SDL_MOUSEBUTTONDOWN:
switch (event.button.button) {
case SDL_BUTTON_LEFT:
{
input_state.pointer_x[0] = mx;
input_state.pointer_y[0] = my;
input_state.pointer_down[0] = true;
input_state.mouse_valid = true;
TouchInput input;
input.x = mx;
input.y = my;
input.flags = TOUCH_DOWN | TOUCH_MOUSE;
input.id = 0;
NativeTouch(input);
KeyInput key(DEVICE_ID_MOUSE, NKCODE_EXT_MOUSEBUTTON_1, KEY_DOWN);
NativeKey(key);
}
break;
case SDL_BUTTON_RIGHT:
{
KeyInput key(DEVICE_ID_MOUSE, NKCODE_EXT_MOUSEBUTTON_2, KEY_DOWN);
NativeKey(key);
}
break;
}
break;
case SDL_MOUSEWHEEL:
{
KeyInput key;
key.deviceId = DEVICE_ID_MOUSE;
if (event.wheel.y > 0) {
key.keyCode = NKCODE_EXT_MOUSEWHEEL_UP;
} else {
key.keyCode = NKCODE_EXT_MOUSEWHEEL_DOWN;
}
key.flags = KEY_DOWN;
NativeKey(key);
// SDL2 doesn't consider the mousewheel a button anymore
// so let's send the KEY_UP right away.
// Maybe KEY_UP alone will suffice?
key.flags = KEY_UP;
NativeKey(key);
}
case SDL_MOUSEMOTION:
if (input_state.pointer_down[0]) {
input_state.pointer_x[0] = mx;
input_state.pointer_y[0] = my;
input_state.mouse_valid = true;
TouchInput input;
input.x = mx;
input.y = my;
input.flags = TOUCH_MOVE | TOUCH_MOUSE;
input.id = 0;
NativeTouch(input);
}
break;
case SDL_MOUSEBUTTONUP:
switch (event.button.button) {
case SDL_BUTTON_LEFT:
{
input_state.pointer_x[0] = mx;
input_state.pointer_y[0] = my;
input_state.pointer_down[0] = false;
input_state.mouse_valid = true;
//input_state.mouse_buttons_up = 1;
TouchInput input;
input.x = mx;
input.y = my;
input.flags = TOUCH_UP | TOUCH_MOUSE;
input.id = 0;
NativeTouch(input);
KeyInput key(DEVICE_ID_MOUSE, NKCODE_EXT_MOUSEBUTTON_1, KEY_UP);
NativeKey(key);
}
break;
case SDL_BUTTON_RIGHT:
{
KeyInput key(DEVICE_ID_MOUSE, NKCODE_EXT_MOUSEBUTTON_2, KEY_UP);
NativeKey(key);
}
break;
}
break;
default:
#ifndef _WIN32
joystick->ProcessInput(event);
#endif
break;
}
}
if (g_QuitRequested)
break;
const uint8_t *keys = SDL_GetKeyboardState(NULL);
SimulateGamepad(keys, &input_state);
input_state.pad_buttons = pad_buttons;
UpdateInputState(&input_state, true);
#ifdef PPSSPP
UpdateRunLoop();
#else
NativeUpdate(input_state);
NativeRender();
#endif
if (g_QuitRequested)
break;
#if defined(PPSSPP) && !defined(MOBILE_DEVICE)
if (lastUIState != GetUIState()) {
lastUIState = GetUIState();
if (lastUIState == UISTATE_INGAME && g_Config.bFullScreen && !g_Config.bShowTouchControls)
SDL_ShowCursor(SDL_DISABLE);
if (lastUIState != UISTATE_INGAME && g_Config.bFullScreen)
SDL_ShowCursor(SDL_ENABLE);
}
#endif
if (framecount % 60 == 0) {
// glsl_refresh(); // auto-reloads modified GLSL shaders once per second.
}
#ifdef USING_EGL
eglSwapBuffers(g_eglDisplay, g_eglSurface);
#else
if (!keys[SDLK_TAB] || t - lastT >= 1.0/60.0)
{
SDL_GL_SwapWindow(g_Screen);
lastT = t;
}
#endif
ToggleFullScreenIfFlagSet();
time_update();
t = time_now();
framecount++;
}
#ifndef _WIN32
delete joystick;
#endif
// Faster exit, thanks to the OS. Remove this if you want to debug shutdown
// The speed difference is only really noticable on Linux. On Windows you do notice it though
#ifndef MOBILE_DEVICE
exit(0);
#endif
NativeShutdownGraphics();
SDL_PauseAudio(1);
SDL_CloseAudio();
NativeShutdown();
#ifdef USING_EGL
EGL_Close();
#endif
SDL_GL_DeleteContext(glContext);
SDL_Quit();
net::Shutdown();
#ifdef RPI
bcm_host_deinit();
#endif
exit(0);
return 0;
}
+257
View File
@@ -0,0 +1,257 @@
/*
* Copyright (c) 2012 Sacha Refshauge
*
*/
// Qt 4.7+ / 5.0+ implementation of the framework.
// Currently supports: Android, Symbian, Blackberry, Maemo/Meego, Linux, Windows, Mac OSX
#include <QApplication>
#include <QUrl>
#include <QDir>
#include <QDesktopWidget>
#include <QDesktopServices>
#include <QLocale>
#include <QThread>
#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
#include <QStandardPaths>
#ifdef QT_HAS_SYSTEMINFO
#include <QScreenSaver>
#endif
#endif
#ifdef __SYMBIAN32__
#include <QSystemScreenSaver>
#include <QFeedbackHapticsEffect>
#include "SymbianMediaKeys.h"
#endif
#ifdef SDL
#include "SDL/SDLJoystick.h"
#include "SDL_audio.h"
#endif
#include "QtMain.h"
#include "math/math_util.h"
#include <string.h>
InputState* input_state;
#ifdef SDL
extern void mixaudio(void *userdata, Uint8 *stream, int len) {
NativeMix((short *)stream, len / 4);
}
#endif
std::string System_GetProperty(SystemProperty prop) {
switch (prop) {
case SYSPROP_NAME:
#ifdef __SYMBIAN32__
return "Qt:Symbian";
#elif defined(BLACKBERRY)
return "Qt:Blackberry";
#elif defined(MAEMO)
return "Qt:Maemo";
#elif defined(ANDROID)
return "Qt:Android";
#elif defined(Q_OS_LINUX)
return "Qt:Linux";
#elif defined(_WIN32)
return "Qt:Windows";
#elif defined(Q_OS_MAC)
return "Qt:Mac";
#else
return "Qt";
#endif
case SYSPROP_LANGREGION:
return QLocale::system().name().toStdString();
default:
return "";
}
}
int System_GetPropertyInt(SystemProperty prop) {
switch (prop) {
case SYSPROP_AUDIO_SAMPLE_RATE:
return 44100;
case SYSPROP_DISPLAY_REFRESH_RATE:
return 60000;
case SYSPROP_DEVICE_TYPE:
#ifdef __SYMBIAN32__
return DEVICE_TYPE_MOBILE;
#elif defined(BLACKBERRY)
return DEVICE_TYPE_MOBILE;
#elif defined(MAEMO)
return DEVICE_TYPE_MOBILE;
#elif defined(ANDROID)
return DEVICE_TYPE_MOBILE;
#elif defined(Q_OS_LINUX)
return DEVICE_TYPE_DESKTOP;
#elif defined(_WIN32)
return DEVICE_TYPE_DESKTOP;
#elif defined(Q_OS_MAC)
return DEVICE_TYPE_DESKTOP;
#else
return DEVICE_TYPE_DESKTOP;
#endif
default:
return -1;
}
}
void System_SendMessage(const char *command, const char *parameter) {
if (!strcmp(command, "finish")) {
qApp->exit(0);
}
}
bool System_InputBoxGetString(const char *title, const char *defaultValue, char *outValue, size_t outLength)
{
QString text = emugl->InputBoxGetQString(QString(title), QString(defaultValue));
if (text.isEmpty())
return false;
strcpy(outValue, text.toStdString().c_str());
return true;
}
void Vibrate(int length_ms) {
if (length_ms == -1 || length_ms == -3)
length_ms = 50;
else if (length_ms == -2)
length_ms = 25;
// Symbian only for now
#if defined(__SYMBIAN32__)
QFeedbackHapticsEffect effect;
effect.setIntensity(0.8);
effect.setDuration(length_ms);
effect.start();
#endif
}
void LaunchBrowser(const char *url)
{
QDesktopServices::openUrl(QUrl(url));
}
float CalculateDPIScale()
{
// Sane default rather than check DPI
#ifdef __SYMBIAN32__
return 1.4f;
#elif defined(USING_GLES2)
return 1.2f;
#else
return 1.0f;
#endif
}
static int mainInternal(QApplication &a)
{
#ifdef MOBILE_DEVICE
emugl = new MainUI();
emugl->resize(pixel_xres, pixel_yres);
emugl->showFullScreen();
#endif
EnableFZ();
// Disable screensaver
#ifdef __SYMBIAN32__
QSystemScreenSaver ssObject(emugl);
ssObject.setScreenSaverInhibit();
QScopedPointer<SymbianMediaKeys> mediakeys(new SymbianMediaKeys());
#elif defined(QT_HAS_SYSTEMINFO)
QScreenSaver ssObject(emugl);
ssObject.setScreenSaverEnabled(false);
#endif
#ifdef SDL
SDLJoystick joy(true);
joy.startEventLoop();
SDL_Init(SDL_INIT_AUDIO);
SDL_AudioSpec fmt, ret_fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.freq = 44100;
fmt.format = AUDIO_S16;
fmt.channels = 2;
fmt.samples = 2048;
fmt.callback = &mixaudio;
fmt.userdata = (void *)0;
if (SDL_OpenAudio(&fmt, &ret_fmt) < 0) {
ELOG("Failed to open audio: %s", SDL_GetError());
} else {
if (ret_fmt.samples != fmt.samples) // Notify, but still use it
ELOG("Output audio samples: %d (requested: %d)", ret_fmt.samples, fmt.samples);
if (ret_fmt.freq != fmt.freq || ret_fmt.format != fmt.format || ret_fmt.channels != fmt.channels) {
ELOG("Sound buffer format does not match requested format.");
ELOG("Output audio freq: %d (requested: %d)", ret_fmt.freq, fmt.freq);
ELOG("Output audio format: %d (requested: %d)", ret_fmt.format, fmt.format);
ELOG("Output audio channels: %d (requested: %d)", ret_fmt.channels, fmt.channels);
ELOG("Provided output format does not match requirement, turning audio off");
SDL_CloseAudio();
}
}
SDL_PauseAudio(0);
#else
QScopedPointer<MainAudio> audio(new MainAudio());
audio->run();
#endif
return a.exec();
}
#ifndef SDL
Q_DECL_EXPORT
#endif
int main(int argc, char *argv[])
{
#if defined(Q_OS_LINUX) && !defined(MAEMO)
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
#endif
QApplication a(argc, argv);
QSize res = QApplication::desktop()->screenGeometry().size();
if (res.width() < res.height())
res.transpose();
pixel_xres = res.width();
pixel_yres = res.height();
g_dpi_scale = CalculateDPIScale();
dp_xres = (int)(pixel_xres * g_dpi_scale); dp_yres = (int)(pixel_yres * g_dpi_scale);
net::Init();
std::string savegame_dir = ".";
std::string assets_dir = ".";
#if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)
savegame_dir = QStandardPaths::writableLocation(QStandardPaths::HomeLocation).toStdString();
assets_dir = QStandardPaths::writableLocation(QStandardPaths::DataLocation).toStdString();
#elif defined(__SYMBIAN32__)
savegame_dir = "E:/PPSSPP";
assets_dir = "E:/PPSSPP";
#elif defined(BLACKBERRY)
savegame_dir = "/accounts/1000/shared/misc";
assets_dir = "app/native/assets";
#elif defined(MAEMO)
savegame_dir = "/home/user/MyDocs/PPSSPP";
assets_dir = "/opt/PPSSPP";
#endif
savegame_dir += "/";
assets_dir += "/";
bool fullscreenCLI=false;
for (int i = 1; i < argc; i++)
{
if (!strcmp(argv[i],"--fullscreen"))
fullscreenCLI=true;
}
NativeInit(argc, (const char **)argv, savegame_dir.c_str(), assets_dir.c_str(), "BADCOFFEE",fullscreenCLI);
int ret = mainInternal(a);
#ifndef MOBILE_DEVICE
exit(0);
#endif
NativeShutdownGraphics();
#ifdef SDL
SDL_PauseAudio(1);
SDL_CloseAudio();
#endif
NativeShutdown();
net::Shutdown();
return ret;
}
+296
View File
@@ -0,0 +1,296 @@
#ifndef QTMAIN_H
#define QTMAIN_H
#include <QTouchEvent>
#include <QMouseEvent>
#include <QInputDialog>
#include "gfx_es2/glsl_program.h"
#include <QGLWidget>
#ifndef SDL
#include <QAudioOutput>
#include <QAudioFormat>
#endif
#if defined(MOBILE_DEVICE) && !defined(MAEMO)
#include <QAccelerometer>
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
QTM_USE_NAMESPACE
#endif
#endif
#include "base/display.h"
#include "base/logging.h"
#include "base/timeutil.h"
#include "file/zip_read.h"
#include "gfx/gl_common.h"
#include "input/input_state.h"
#include "input/keycodes.h"
#include "base/NativeApp.h"
#include "net/resolve.h"
#include "base/NKCodeFromQt.h"
// Bad: PPSSPP includes from native
#include "Core/System.h"
#include "Core/Core.h"
#include "Core/Config.h"
// Input
void SimulateGamepad(InputState *input);
//GUI
class MainUI : public QGLWidget
{
Q_OBJECT
public:
explicit MainUI(QWidget *parent = 0):
QGLWidget(parent)
{
setAttribute(Qt::WA_AcceptTouchEvents);
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
setAttribute(Qt::WA_LockLandscapeOrientation);
#endif
#if defined(MOBILE_DEVICE) && !defined(MAEMO)
acc = new QAccelerometer(this);
acc->start();
#endif
setFocus();
setFocusPolicy(Qt::StrongFocus);
startTimer(16);
}
~MainUI() {
#if defined(MOBILE_DEVICE) && !defined(MAEMO)
delete acc;
#endif
NativeShutdownGraphics();
}
public slots:
QString InputBoxGetQString(QString title, QString defaultValue) {
bool ok;
QString text = QInputDialog::getText(this, title, title, QLineEdit::Normal, defaultValue, &ok);
if (!ok)
text = QString();
return text;
}
signals:
void doubleClick();
void newFrame();
protected:
void resizeEvent(QResizeEvent * e)
{
UpdateScreenScale(e->size().width(), e->size().height(), false);
PSP_CoreParameter().pixelWidth = pixel_xres;
PSP_CoreParameter().pixelHeight = pixel_yres;
}
void timerEvent(QTimerEvent *) {
updateGL();
emit newFrame();
}
void changeEvent(QEvent *e)
{
QGLWidget::changeEvent(e);
if(e->type() == QEvent::WindowStateChange)
Core_NotifyWindowHidden(isMinimized());
}
bool event(QEvent *e)
{
TouchInput input;
QList<QTouchEvent::TouchPoint> touchPoints;
switch(e->type())
{
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
touchPoints = static_cast<QTouchEvent *>(e)->touchPoints();
foreach (const QTouchEvent::TouchPoint &touchPoint, touchPoints) {
switch (touchPoint.state()) {
case Qt::TouchPointStationary:
break;
case Qt::TouchPointPressed:
case Qt::TouchPointReleased:
input_state.pointer_down[touchPoint.id()] = (touchPoint.state() == Qt::TouchPointPressed);
input_state.pointer_x[touchPoint.id()] = touchPoint.pos().x() * g_dpi_scale;
input_state.pointer_y[touchPoint.id()] = touchPoint.pos().y() * g_dpi_scale;
input.x = touchPoint.pos().x() * g_dpi_scale;
input.y = touchPoint.pos().y() * g_dpi_scale;
input.flags = (touchPoint.state() == Qt::TouchPointPressed) ? TOUCH_DOWN : TOUCH_UP;
input.id = touchPoint.id();
NativeTouch(input);
break;
case Qt::TouchPointMoved:
input_state.pointer_x[touchPoint.id()] = touchPoint.pos().x() * g_dpi_scale;
input_state.pointer_y[touchPoint.id()] = touchPoint.pos().y() * g_dpi_scale;
input.x = touchPoint.pos().x() * g_dpi_scale;
input.y = touchPoint.pos().y() * g_dpi_scale;
input.flags = TOUCH_MOVE;
input.id = touchPoint.id();
NativeTouch(input);
break;
default:
break;
}
}
break;
case QEvent::MouseButtonDblClick:
if (!g_Config.bShowTouchControls || GetUIState() != UISTATE_INGAME)
emit doubleClick();
break;
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
input_state.pointer_down[0] = (e->type() == QEvent::MouseButtonPress);
input_state.pointer_x[0] = ((QMouseEvent*)e)->pos().x() * g_dpi_scale;
input_state.pointer_y[0] = ((QMouseEvent*)e)->pos().y() * g_dpi_scale;
input.x = ((QMouseEvent*)e)->pos().x() * g_dpi_scale;
input.y = ((QMouseEvent*)e)->pos().y() * g_dpi_scale;
input.flags = (e->type() == QEvent::MouseButtonPress) ? TOUCH_DOWN : TOUCH_UP;
input.id = 0;
NativeTouch(input);
break;
case QEvent::MouseMove:
input_state.pointer_x[0] = ((QMouseEvent*)e)->pos().x() * g_dpi_scale;
input_state.pointer_y[0] = ((QMouseEvent*)e)->pos().y() * g_dpi_scale;
input.x = ((QMouseEvent*)e)->pos().x() * g_dpi_scale;
input.y = ((QMouseEvent*)e)->pos().y() * g_dpi_scale;
input.flags = TOUCH_MOVE;
input.id = 0;
NativeTouch(input);
break;
case QEvent::Wheel:
NativeKey(KeyInput(DEVICE_ID_MOUSE, ((QWheelEvent*)e)->delta()<0 ? NKCODE_EXT_MOUSEWHEEL_DOWN : NKCODE_EXT_MOUSEWHEEL_UP, KEY_DOWN));
break;
case QEvent::KeyPress:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, KeyMapRawQttoNative.find(((QKeyEvent*)e)->key())->second, KEY_DOWN));
break;
case QEvent::KeyRelease:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, KeyMapRawQttoNative.find(((QKeyEvent*)e)->key())->second, KEY_UP));
break;
default:
return QWidget::event(e);
}
e->accept();
return true;
}
void initializeGL()
{
#ifndef USING_GLES2
glewInit();
#endif
NativeInitGraphics();
}
void paintGL()
{
updateAccelerometer();
UpdateInputState(&input_state);
time_update();
UpdateRunLoop();
}
void updateAccelerometer()
{
#if defined(MOBILE_DEVICE) && !defined(MAEMO)
// TODO: Toggle it depending on whether it is enabled
QAccelerometerReading *reading = acc->reading();
if (reading) {
input_state.acc.x = reading->x();
input_state.acc.y = reading->y();
input_state.acc.z = reading->z();
AxisInput axis;
axis.deviceId = DEVICE_ID_ACCELEROMETER;
axis.flags = 0;
axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_X;
axis.value = input_state.acc.x;
NativeAxis(axis);
axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_Y;
axis.value = input_state.acc.y;
NativeAxis(axis);
axis.axisId = JOYSTICK_AXIS_ACCELEROMETER_Z;
axis.value = input_state.acc.z;
NativeAxis(axis);
}
#endif
}
private:
InputState input_state;
#if defined(MOBILE_DEVICE) && !defined(MAEMO)
QAccelerometer* acc;
#endif
};
static MainUI* emugl = NULL;
#ifndef SDL
// Audio
#define AUDIO_FREQ 44100
#define AUDIO_CHANNELS 2
#define AUDIO_SAMPLES 2048
#define AUDIO_SAMPLESIZE 16
#define AUDIO_BUFFERS 5
class MainAudio: public QObject
{
Q_OBJECT
public:
MainAudio() {
}
~MainAudio() {
if (feed != NULL) {
killTimer(timer);
feed->close();
}
if (output) {
output->stop();
delete output;
}
if (mixbuf)
free(mixbuf);
}
public slots:
void run() {
QAudioFormat fmt;
fmt.setSampleRate(AUDIO_FREQ);
fmt.setCodec("audio/pcm");
fmt.setChannelCount(AUDIO_CHANNELS);
fmt.setSampleSize(AUDIO_SAMPLESIZE);
fmt.setByteOrder(QAudioFormat::LittleEndian);
fmt.setSampleType(QAudioFormat::SignedInt);
mixlen = sizeof(short)*AUDIO_BUFFERS*AUDIO_CHANNELS*AUDIO_SAMPLES;
mixbuf = (char*)malloc(mixlen);
output = new QAudioOutput(fmt);
output->setBufferSize(mixlen);
feed = output->start();
if (feed != NULL)
timer = startTimer((1000*AUDIO_SAMPLES) / AUDIO_FREQ);
}
protected:
void timerEvent(QTimerEvent *) {
memset(mixbuf, 0, mixlen);
size_t frames = NativeMix((short *)mixbuf, AUDIO_BUFFERS*AUDIO_SAMPLES);
if (frames > 0)
feed->write(mixbuf, sizeof(short) * AUDIO_CHANNELS * frames);
}
private:
QIODevice* feed;
QAudioOutput* output;
int mixlen;
char* mixbuf;
int timer;
};
#endif //SDL
#endif
+333
View File
@@ -0,0 +1,333 @@
/*
* Copyright (c) 2013 Antti Pohjola
*
*/
//Adds mediakey support for Symbian (volume up/down)
#include <QApplication>
#include "SymbianMediaKeys.h"
#include "input/keycodes.h"
#include "input/input_state.h"
#include "base/NativeApp.h"
#define KTimeOut 80
SymbianMediaKeys::SymbianMediaKeys()
: CActive ( EPriorityNormal ){
CActiveScheduler::Add( this );
iInterfaceSelector = CRemConInterfaceSelector::NewL();
iRemConCore = CRemConCoreApiTarget::NewL(*iInterfaceSelector, *this);
iInterfaceSelector->OpenTargetL();
playtimer = new QTimer(this);
connect(playtimer, SIGNAL(timeout()), this, SLOT(playtimerexpired()));
stoptimer = new QTimer(this);
connect(stoptimer, SIGNAL(timeout()), this, SLOT(stoptimerexpired()));
forwardtimer = new QTimer(this);
connect(forwardtimer, SIGNAL(timeout()), this, SLOT(forwardtimerexpired()));
backwardtimer = new QTimer(this);
connect(backwardtimer, SIGNAL(timeout()), this, SLOT(backwardtimerexpired()));
voluptimer = new QTimer(this);
connect(voluptimer, SIGNAL(timeout()), this, SLOT(voluptimerexpired()));
voldowntimer = new QTimer(this);
connect(voldowntimer, SIGNAL(timeout()), this, SLOT(voldowntimerexpired()));
}
SymbianMediaKeys::~SymbianMediaKeys(){
delete iInterfaceSelector;
iRemConCore = NULL; //owned by interfaceselector
Cancel();
iResponseQ.Reset();
iResponseQ.Close();
}
void SymbianMediaKeys::subscribeKeyEvent(QObject* aObject ){
receiver = aObject;
}
/*
* it seems that it takes about 600ms to get an update after buttonpress
* */
void SymbianMediaKeys::MrccatoCommand(TRemConCoreApiOperationId aOperationId,TRemConCoreApiButtonAction aButtonAct){
TRequestStatus status;
switch( aOperationId ){
case ERemConCoreApiPausePlayFunction:
{
switch (aButtonAct){
case ERemConCoreApiButtonPress:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_PLAY_PAUSE, KEY_DOWN));
break;
case ERemConCoreApiButtonRelease:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_PLAY_PAUSE, KEY_UP));
break;
case ERemConCoreApiButtonClick:
playtimer->start(KTimeOut);
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_PLAY_PAUSE, KEY_DOWN));
break;
default:
// Play/Pause unknown action
break;
}
break;
}
case ERemConCoreApiStop:
{
switch (aButtonAct){
case ERemConCoreApiButtonPress:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_STOP, KEY_DOWN));
break;
case ERemConCoreApiButtonRelease:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_STOP, KEY_UP));
break;
case ERemConCoreApiButtonClick:
stoptimer->start(KTimeOut);
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_STOP, KEY_DOWN));
break;
default:
break;
}
break;
}
case ERemConCoreApiRewind:
{
switch (aButtonAct){
case ERemConCoreApiButtonPress:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_PREVIOUS, KEY_DOWN));
break;
case ERemConCoreApiButtonRelease:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_PREVIOUS, KEY_UP));
break;
case ERemConCoreApiButtonClick:
backwardtimer->start(KTimeOut);
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_PREVIOUS, KEY_DOWN));
default:
break;
}
break;
}
case ERemConCoreApiFastForward:
{
switch (aButtonAct){
case ERemConCoreApiButtonPress:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_NEXT, KEY_DOWN));
break;
case ERemConCoreApiButtonRelease:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_NEXT, KEY_UP));
break;
case ERemConCoreApiButtonClick:
forwardtimer->start(KTimeOut);
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_NEXT, KEY_DOWN));
default:
break;
}
break;
}
case ERemConCoreApiVolumeUp:
{
switch (aButtonAct){
case ERemConCoreApiButtonPress:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_VOLUME_UP, KEY_DOWN));
break;
case ERemConCoreApiButtonRelease:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_VOLUME_UP, KEY_UP));
break;
case ERemConCoreApiButtonClick:
voluptimer->start(KTimeOut);
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_VOLUME_UP, KEY_DOWN));
default:
break;
}
break;
}
case ERemConCoreApiVolumeDown:
{
switch (aButtonAct){
case ERemConCoreApiButtonPress:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_VOLUME_DOWN, KEY_DOWN));
break;
case ERemConCoreApiButtonRelease:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_VOLUME_DOWN, KEY_UP));
break;
case ERemConCoreApiButtonClick:
voldowntimer->start(KTimeOut);
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_VOLUME_DOWN, KEY_DOWN));
default:
break;
}
break;
}
case ERemConCoreApiBackward:
{
switch (aButtonAct)
{
case ERemConCoreApiButtonPress:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_PREVIOUS, KEY_DOWN));
break;
case ERemConCoreApiButtonRelease:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_PREVIOUS, KEY_UP));
break;
case ERemConCoreApiButtonClick:
backwardtimer->start(KTimeOut);
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_PREVIOUS, KEY_DOWN));
default:
break;
}
break;
}
case ERemConCoreApiForward:
{
switch (aButtonAct)
{
case ERemConCoreApiButtonPress:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_NEXT, KEY_DOWN));
break;
case ERemConCoreApiButtonRelease:
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_NEXT, KEY_UP));
break;
case ERemConCoreApiButtonClick:
forwardtimer->start(KTimeOut);
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_NEXT, KEY_DOWN));
default:
break;
}
break;
}
default:
break;
}
//complete key event
CompleteMediaKeyEvent( aOperationId );
}
void SymbianMediaKeys::MrccatoPlay(TRemConCoreApiPlaybackSpeed aSpeed,TRemConCoreApiButtonAction aButtonAct){
}
void SymbianMediaKeys::MrccatoTuneFunction(TBool aTwoPart, TUint aMajorChannel,TUint aMinorChannel,TRemConCoreApiButtonAction aButtonAct){
}
void SymbianMediaKeys::MrccatoSelectDiskFunction(TUint aDisk, TRemConCoreApiButtonAction aButtonAct){
}
void SymbianMediaKeys::MrccatoSelectAvInputFunction(TUint8 aAvInputSignalNumber,TRemConCoreApiButtonAction aButtonAct){
}
void SymbianMediaKeys::MrccatoSelectAudioInputFunction(TUint8 aAudioInputSignalNumber,TRemConCoreApiButtonAction aButtonAct){
}
void SymbianMediaKeys::CompleteMediaKeyEvent( TRemConCoreApiOperationId aOperationId ){
if( !IsActive() ){
switch ( aOperationId )
{
case ERemConCoreApiVolumeUp:
{
iRemConCore->VolumeUpResponse( iStatus, KErrNone );
SetActive();
break;
}
case ERemConCoreApiVolumeDown:
{
iRemConCore->VolumeDownResponse( iStatus, KErrNone );
SetActive();
break;
}
case ERemConCoreApiPlay:
{
iRemConCore-> PlayResponse(iStatus, KErrNone);
SetActive();
break;
}
case ERemConCoreApiStop:
{
iRemConCore->StopResponse(iStatus, KErrNone);
SetActive();
break;
}
case ERemConCoreApiPause:
{
iRemConCore->PauseResponse(iStatus, KErrNone);
SetActive();
break;
}
case ERemConCoreApiRewind:
{
iRemConCore->RewindResponse(iStatus, KErrNone);
SetActive();
break;
}
case ERemConCoreApiFastForward:
{
iRemConCore->FastForwardResponse(iStatus, KErrNone);
SetActive();
break;
}
case ERemConCoreApiForward:
{
iRemConCore->ForwardResponse( iStatus, KErrNone );
SetActive();
break;
}
case ERemConCoreApiBackward:
{
iRemConCore->BackwardResponse(iStatus, KErrNone );
SetActive();
break;
}
default:
break;
}
}
else{
//active, append to queue
iResponseQ.Append( aOperationId );
}
}
void SymbianMediaKeys::RunL(){
if ( iResponseQ.Count() ){
CompleteMediaKeyEvent( iResponseQ[0] );
//remove old response from que
iResponseQ.Remove(0);
iResponseQ.Compress();
}
}
void SymbianMediaKeys::DoCancel(){
}
void SymbianMediaKeys::playtimerexpired(){
playtimer->stop();
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_PLAY_PAUSE, KEY_UP));
}
void SymbianMediaKeys::stoptimerexpired(){
stoptimer->stop();
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_STOP, KEY_UP));
}
void SymbianMediaKeys::forwardtimerexpired(){
forwardtimer->stop();
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_NEXT, KEY_UP));
}
void SymbianMediaKeys::backwardtimerexpired(){
backwardtimer->stop();
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_MEDIA_PREVIOUS, KEY_UP));
}
void SymbianMediaKeys::voluptimerexpired(){
voluptimer->stop();
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_VOLUME_UP, KEY_UP));
}
void SymbianMediaKeys::voldowntimerexpired(){
voldowntimer->stop();
NativeKey(KeyInput(DEVICE_ID_KEYBOARD, NKCODE_VOLUME_DOWN, KEY_UP));
}
+70
View File
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2013 Antti Pohjola
*
*/
//Adds mediakey support for Symbian (volume up/down)
#ifndef SYMBIANMEDIAKEYS_H_
#define SYMBIANMEDIAKEYS_H_
#include <QObject>
#include <QTimer>
#include <e32base.h>
#include <remconcoreapitargetobserver.h> // link against RemConCoreApi.lib
#include <remconcoreapitarget.h> // and
#include <remconinterfaceselector.h> // RemConInterfaceBase.lib
class SymbianMediaKeys: public QObject, public CActive, public MRemConCoreApiTargetObserver
{
Q_OBJECT
public:
SymbianMediaKeys();
virtual ~SymbianMediaKeys();
public:
void subscribeKeyEvent(QObject* aObject );
public: //From MRemConCoreApiTargetObserver
void MrccatoCommand(TRemConCoreApiOperationId aOperationId,TRemConCoreApiButtonAction aButtonAct);
void MrccatoPlay(TRemConCoreApiPlaybackSpeed aSpeed,TRemConCoreApiButtonAction aButtonAct);
void MrccatoTuneFunction(TBool aTwoPart,TUint aMajorChannel,TUint aMinorChannel,TRemConCoreApiButtonAction aButtonAct);
void MrccatoSelectDiskFunction(TUint aDisk,TRemConCoreApiButtonAction aButtonAct);
void MrccatoSelectAvInputFunction(TUint8 aAvInputSignalNumber,TRemConCoreApiButtonAction aButtonAct);
void MrccatoSelectAudioInputFunction(TUint8 aAudioInputSignalNumber,TRemConCoreApiButtonAction aButtonAct);
private:
void CompleteMediaKeyEvent( TRemConCoreApiOperationId aOperationId );
void RunL();
void DoCancel();
public slots:
void playtimerexpired();
void stoptimerexpired();
void forwardtimerexpired();
void backwardtimerexpired();
void voluptimerexpired();
void voldowntimerexpired();
private:
RArray<TRemConCoreApiOperationId> iResponseQ; //response queue
CRemConCoreApiTarget* iRemConCore; //the controller
CRemConInterfaceSelector* iInterfaceSelector;
QObject* receiver;
QTimer* playtimer;
QTimer* stoptimer;
QTimer* forwardtimer;
QTimer* backwardtimer;
QTimer* voluptimer;
QTimer* voldowntimer;
};
#endif /* SYMBIANMEDIAKEYS_H_ */
+17
View File
@@ -0,0 +1,17 @@
#pragma once
// This provides MSVC's arch-detection defines on other platforms as well.
#ifndef _WIN32
#if defined(__x86_64__) && !defined(_M_X64)
#define _M_X64 1
#endif
#if defined(__x86__) && !defined(_M_IX86)
#define _M_IX86 1
#endif
// TODO: ARM, ARM64
#endif
+22
View File
@@ -0,0 +1,22 @@
#include "base/backtrace.h"
#if defined(__GLIBC__) && !defined(__UCLIBC__)
#include <execinfo.h>
#include <unistd.h>
static void *backtrace_buffer[128];
void PrintBacktraceToStderr() {
int num_addrs = backtrace(backtrace_buffer, 128);
backtrace_symbols_fd(backtrace_buffer, num_addrs, STDERR_FILENO);
}
#else
#include <stdio.h>
void PrintBacktraceToStderr() {
fprintf(stderr, "No backtrace available to print on this platform\n");
}
#endif
+3
View File
@@ -0,0 +1,3 @@
#pragma once
void PrintBacktraceToStderr();
+95
View File
@@ -0,0 +1,95 @@
#pragma once
#include <stdint.h>
#include <stdlib.h> // for byte swapping
#ifdef _WIN32
#pragma warning(disable:4244)
#pragma warning(disable:4996)
#pragma warning(disable:4305) // truncation from double to float
#endif
#define DISALLOW_COPY_AND_ASSIGN(t) \
private: \
t(const t &other); \
void operator =(const t &other);
#ifdef _WIN32
typedef intptr_t ssize_t;
#include <tchar.h>
#define ALIGNED16(x) __declspec(align(16)) x
#define ALIGNED32(x) __declspec(align(32)) x
#define ALIGNED64(x) __declspec(align(64)) x
#define ALIGNED16_DECL(x) __declspec(align(16)) x
#define ALIGNED64_DECL(x) __declspec(align(64)) x
#else
#define ALIGNED16(x) __attribute__((aligned(16))) x
#define ALIGNED32(x) __attribute__((aligned(32))) x
#define ALIGNED64(x) __attribute__((aligned(64))) x
#define ALIGNED16_DECL(x) __attribute__((aligned(16))) x
#define ALIGNED64_DECL(x) __attribute__((aligned(64))) x
#endif // _WIN32
// Byteswapping
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#endif
inline uint8_t swap8(uint8_t _data) {return _data;}
// Just in case this has been defined by platform
#undef swap16
#undef swap32
#undef swap64
#ifdef _WIN32
inline uint16_t swap16(uint16_t _data) {return _byteswap_ushort(_data);}
inline uint32_t swap32(uint32_t _data) {return _byteswap_ulong (_data);}
inline uint64_t swap64(uint64_t _data) {return _byteswap_uint64(_data);}
#elif defined(ARM)
inline uint16_t swap16 (uint16_t _data) { uint32_t data = _data; __asm__ ("rev16 %0, %1\n" : "=l" (data) : "l" (data)); return (uint16_t)data;}
inline uint32_t swap32 (uint32_t _data) {__asm__ ("rev %0, %1\n" : "=l" (_data) : "l" (_data)); return _data;}
inline uint64_t swap64(uint64_t _data) {return ((uint64_t)swap32(_data) << 32) | swap32(_data >> 32);}
#elif __linux__ && !defined(ANDROID)
#include <byteswap.h>
inline uint16_t swap16(uint16_t _data) {return bswap_16(_data);}
inline uint32_t swap32(uint32_t _data) {return bswap_32(_data);}
inline uint64_t swap64(uint64_t _data) {return bswap_64(_data);}
#elif defined(__DragonFly__) || defined(__FreeBSD__)
#include <sys/endian.h>
inline uint16_t swap16(uint16_t _data) {return bswap16(_data);}
inline uint32_t swap32(uint32_t _data) {return bswap32(_data);}
inline uint64_t swap64(uint64_t _data) {return bswap64(_data);}
#elif defined(__GNUC__)
inline uint16_t swap16(uint16_t _data) {return (_data >> 8) | (_data << 8);}
inline uint32_t swap32(uint32_t _data) {return __builtin_bswap32(_data);}
inline uint64_t swap64(uint64_t _data) {return __builtin_bswap64(_data);}
#else
// Slow generic implementation. Hopefully this never hits
inline uint16_t swap16(uint16_t data) {return (data >> 8) | (data << 8);}
inline uint32_t swap32(uint32_t data) {return (swap16(data) << 16) | swap16(data >> 16);}
inline uint64_t swap64(uint64_t data) {return ((uint64_t)swap32(data) << 32) | swap32(data >> 32);}
#endif
inline uint16_t swap16(const uint8_t* _pData) {return swap16(*(const uint16_t*)_pData);}
inline uint32_t swap32(const uint8_t* _pData) {return swap32(*(const uint32_t*)_pData);}
inline uint64_t swap64(const uint8_t* _pData) {return swap64(*(const uint64_t*)_pData);}
// Thread local storage
#ifdef _WIN32
#define __THREAD __declspec( thread )
#else
#define __THREAD __thread
#endif
// For really basic windows code compat
#ifndef _TCHAR_DEFINED
typedef char TCHAR;
#endif
+234
View File
@@ -0,0 +1,234 @@
#include "base/buffer.h"
#include <stdarg.h>
#include <stdlib.h>
#include <algorithm>
#ifdef _WIN32
#include <winsock2.h>
#undef min
#undef max
#else
#include <sys/socket.h>
#include <unistd.h>
#endif
#include "base/logging.h"
#include "base/timeutil.h"
#include "file/fd_util.h"
Buffer::Buffer() { }
Buffer::~Buffer() { }
char *Buffer::Append(ssize_t length) {
size_t old_size = data_.size();
data_.resize(old_size + length);
return &data_[0] + old_size;
}
void Buffer::Append(const std::string &str) {
char *ptr = Append(str.size());
memcpy(ptr, str.data(), str.size());
}
void Buffer::Append(const char *str) {
size_t len = strlen(str);
char *dest = Append(len);
memcpy(dest, str, len);
}
void Buffer::Append(const Buffer &other) {
size_t len = other.size();
char *dest = Append(len);
memcpy(dest, &other.data_[0], len);
}
void Buffer::AppendValue(int value) {
char buf[16];
// This is slow.
sprintf(buf, "%i", value);
Append(buf);
}
void Buffer::Take(size_t length, std::string *dest) {
if (length > data_.size()) {
ELOG("Truncating length in Buffer::Take()");
length = data_.size();
}
dest->resize(length);
if (length > 0) {
Take(length, &(*dest)[0]);
}
}
void Buffer::Take(size_t length, char *dest) {
memcpy(dest, &data_[0], length);
data_.erase(data_.begin(), data_.begin() + length);
}
int Buffer::TakeLineCRLF(std::string *dest) {
int after_next_line = OffsetToAfterNextCRLF();
if (after_next_line < 0)
return after_next_line;
else {
Take(after_next_line - 2, dest);
Skip(2); // Skip the CRLF
return after_next_line - 2;
}
}
void Buffer::Skip(size_t length) {
if (length > data_.size()) {
ELOG("Truncating length in Buffer::Skip()");
length = data_.size();
}
data_.erase(data_.begin(), data_.begin() + length);
}
int Buffer::SkipLineCRLF() {
int after_next_line = OffsetToAfterNextCRLF();
if (after_next_line < 0)
return after_next_line;
else {
Skip(after_next_line);
return after_next_line - 2;
}
}
int Buffer::OffsetToAfterNextCRLF() {
for (int i = 0; i < (int)data_.size() - 1; i++) {
if (data_[i] == '\r' && data_[i + 1] == '\n') {
return i + 2;
}
}
return -1;
}
void Buffer::Printf(const char *fmt, ...) {
char buffer[2048];
va_list vl;
va_start(vl, fmt);
ssize_t retval = vsnprintf(buffer, sizeof(buffer), fmt, vl);
if (retval >= (ssize_t)sizeof(buffer)) {
// Output was truncated. TODO: Do something.
ELOG("Buffer::Printf truncated output");
}
if (retval < 0) {
ELOG("Buffer::Printf failed");
}
va_end(vl);
char *ptr = Append(retval);
memcpy(ptr, buffer, retval);
}
bool Buffer::Flush(int fd) {
// Look into using send() directly.
bool success = (ssize_t)data_.size() == fd_util::WriteLine(fd, &data_[0], data_.size());
if (success) {
data_.resize(0);
}
return success;
}
bool Buffer::FlushToFile(const char *filename) {
FILE *f = fopen(filename, "wb");
if (!f)
return false;
if (data_.size()) {
fwrite(&data_[0], 1, data_.size(), f);
}
fclose(f);
return true;
}
bool Buffer::FlushSocket(uintptr_t sock) {
for (size_t pos = 0, end = data_.size(); pos < end; ) {
int sent = send(sock, &data_[pos], (int)(end - pos), 0);
if (sent < 0) {
ELOG("FlushSocket failed");
return false;
}
pos += sent;
// Buffer full, don't spin.
if (sent == 0) {
sleep_ms(1);
}
}
data_.resize(0);
return true;
}
bool Buffer::ReadAll(int fd, int hintSize) {
std::vector<char> buf;
if (hintSize >= 65536 * 16) {
buf.resize(65536);
} else if (hintSize >= 1024 * 16) {
buf.resize(hintSize / 16);
} else {
buf.resize(1024);
}
while (true) {
int retval = recv(fd, &buf[0], (int)buf.size(), 0);
if (retval == 0) {
break;
} else if (retval < 0) {
ELOG("Error reading from buffer: %i", retval);
return false;
}
char *p = Append((size_t)retval);
memcpy(p, &buf[0], retval);
}
return true;
}
bool Buffer::ReadAllWithProgress(int fd, int knownSize, float *progress) {
std::vector<char> buf;
if (knownSize >= 65536 * 16) {
buf.resize(65536);
} else if (knownSize >= 1024 * 16) {
buf.resize(knownSize / 16);
} else {
buf.resize(1024);
}
int total = 0;
while (true) {
int retval = recv(fd, &buf[0], (int)buf.size(), 0);
if (retval == 0) {
return true;
} else if (retval < 0) {
ELOG("Error reading from buffer: %i", retval);
return false;
}
char *p = Append((size_t)retval);
memcpy(p, &buf[0], retval);
total += retval;
*progress = (float)total / (float)knownSize;
}
return true;
}
int Buffer::Read(int fd, size_t sz) {
char buf[1024];
int retval;
size_t received = 0;
while ((retval = recv(fd, buf, (int)std::min(sz, sizeof(buf)), 0)) > 0) {
if (retval < 0) {
return retval;
}
char *p = Append((size_t)retval);
memcpy(p, buf, retval);
sz -= retval;
received += retval;
if (sz == 0)
return 0;
}
return (int)received;
}
void Buffer::PeekAll(std::string *dest) {
dest->resize(data_.size());
memcpy(&(*dest)[0], &data_[0], data_.size());
}
+88
View File
@@ -0,0 +1,88 @@
#ifndef _IO_BUFFER_H
#define _IO_BUFFER_H
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/logging.h"
// Acts as a queue. Intended to be as fast as possible for most uses.
// Does not do synchronization, must use external mutexes.
class Buffer {
public:
Buffer();
~Buffer();
// Write max [length] bytes to the returned pointer.
// Any other operation on this Buffer invalidates the pointer.
char *Append(ssize_t length);
char *Append(size_t length) { return Append((ssize_t)length); }
// These work pretty much like you'd expect.
void Append(const char *str); // str null-terminated. The null is not copied.
void Append(const std::string &str);
void Append(const Buffer &other);
// Various types. Useful for varz etc. Appends a string representation of the
// value, rather than a binary representation.
void AppendValue(int value);
// Parsing Helpers
// Use for easy line skipping. If no CRLF within the buffer, returns -1.
// If parsing HTML headers, this indicates that you should probably buffer up
// more data.
int OffsetToAfterNextCRLF();
// Takers
void Take(size_t length, std::string *dest);
void Take(size_t length, char *dest);
void TakeAll(std::string *dest) { Take(size(), dest); }
// On failure, return value < 0 and *dest is unchanged.
// Strips off the actual CRLF from the result.
int TakeLineCRLF(std::string *dest);
// Skippers
void Skip(size_t length);
// Returns -1 on failure (no CRLF within sight).
// Otherwise returns the length of the line skipped, not including CRLF. Can be 0.
int SkipLineCRLF();
// Utility functions.
void Printf(const char *fmt, ...);
// Dumps the entire buffer to the string, but keeps it around.
// Only to be used for debugging, since it might not be fast at all.
void PeekAll(std::string *dest);
// Simple I/O.
// Writes the entire buffer to the file descriptor. Also resets the
// size to zero. On failure, data remains in buffer and nothing is
// written.
bool Flush(int fd);
bool FlushToFile(const char *filename);
bool FlushSocket(uintptr_t sock); // Windows portability
bool ReadAll(int fd, int hintSize = 0);
bool ReadAllWithProgress(int fd, int knownSize, float *progress);
// < 0: error
// >= 0: number of bytes read
int Read(int fd, size_t sz);
// Utilities. Try to avoid checking for size.
size_t size() const { return data_.size(); }
bool empty() const { return size() == 0; }
void clear() { data_.resize(0); }
private:
// TODO: Find a better internal representation, like a cord.
std::vector<char> data_;
DISALLOW_COPY_AND_ASSIGN(Buffer);
};
#endif // _IO_BUFFER_H
+16
View File
@@ -0,0 +1,16 @@
#pragma once
typedef unsigned int Color;
//have to use a define to ensure constant folding.. with an inline I don't get that, sucks
#define COLOR(i) (((i&0xFF) << 16) | (i & 0xFF00) | ((i & 0xFF0000) >> 16) | 0xFF000000)
inline Color darkenColor(Color color) {
return (color & 0xFF000000) | ((color >> 1)&0x7F7F7F);
}
inline Color whitenColor(Color color) {
return ((color & 0xFF000000) | ((color >> 1)&0x7F7F7F)) + 0x7F7F7F;
}
inline Color colorInterpol(Color x, Color y, int n) {
// TODO
return x;
}
+101
View File
@@ -0,0 +1,101 @@
#include "base/colorutil.h"
uint32_t whiteAlpha(float alpha) {
if (alpha < 0.0f) alpha = 0.0f;
if (alpha > 1.0f) alpha = 1.0f;
uint32_t color = (int)(alpha*255) << 24;
color |= 0xFFFFFF;
return color;
}
uint32_t blackAlpha(float alpha) {
if (alpha < 0.0f) alpha = 0.0f;
if (alpha > 1.0f) alpha = 1.0f;
return (int)(alpha*255)<<24;
}
uint32_t colorAlpha(uint32_t rgb, float alpha) {
if (alpha < 0.0f) alpha = 0.0f;
if (alpha > 1.0f) alpha = 1.0f;
return ((int)(alpha*255)<<24) | (rgb & 0xFFFFFF);
}
uint32_t alphaMul(uint32_t color, float alphaMul) {
uint32_t rgb = color & 0xFFFFFF;
uint32_t alpha = color >> 24;
alpha *= alphaMul;
if (alpha < 0.0f) alpha = 0.0f;
if (alpha > 255.0f) alpha = 255.0f;
return ((int)(alpha)<<24) | (rgb & 0xFFFFFF);
}
uint32_t rgba(float r, float g, float b, float alpha) {
uint32_t color = (int)(alpha*255)<<24;
color |= (int)(b*255)<<16;
color |= (int)(g*255)<<8;
color |= (int)(r*255);
return color;
}
uint32_t rgba_clamp(float r, float g, float b, float a) {
if (r > 1.0f) r = 1.0f;
if (g > 1.0f) g = 1.0f;
if (b > 1.0f) b = 1.0f;
if (a > 1.0f) a = 1.0f;
if (r < 0.0f) r = 0.0f;
if (g < 0.0f) g = 0.0f;
if (b < 0.0f) b = 0.0f;
if (a < 0.0f) a = 0.0f;
return rgba(r,g,b,a);
}
/* hsv2rgb.c
* Convert Hue Saturation Value to Red Green Blue
*
* P.J. 08-Aug-98
*
* Reference:
* D. F. Rogers
* Procedural Elements for Computer Graphics
* McGraw Hill 1985
*/
uint32_t hsva(float H, float S, float V, float alpha) {
/*
* Purpose:
* Convert HSV values to RGB values
* All values are in the range [0.0 .. 1.0]
*/
float F, M, N, K;
int I;
if ( S == 0.0 ) {
// Achromatic case, set level of grey
return rgba(V, V, V, alpha);
} else {
/*
* Determine levels of primary colours.
*/
if (H >= 1.0) {
H = 0.0;
} else {
H = H * 6;
}
I = (int) H; /* should be in the range 0..5 */
F = H - I; /* fractional part */
M = V * (1 - S);
N = V * (1 - S * F);
K = V * (1 - S * (1 - F));
float r, g, b;
if (I == 0) { r = V; g = K; b = M; }
else if (I == 1) { r = N; g = V; b = M; }
else if (I == 2) { r = M; g = V; b = K; }
else if (I == 3) { r = M; g = N; b = V; }
else if (I == 4) { r = K; g = M; b = V; }
else if (I == 5) { r = V; g = M; b = N; }
else return 0;
return rgba(r, g, b, alpha);
}
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include "base/basictypes.h"
uint32_t whiteAlpha(float alpha);
uint32_t blackAlpha(float alpha);
uint32_t colorAlpha(uint32_t color, float alpha);
uint32_t colorBlend(uint32_t color, uint32_t color2, float alpha);
uint32_t alphaMul(uint32_t color, float alphaMul);
uint32_t rgba(float r, float g, float b, float alpha);
uint32_t rgba_clamp(float r, float g, float b, float alpha);
uint32_t hsva(float h, float s, float v, float alpha);
+28
View File
@@ -0,0 +1,28 @@
#include <stdio.h>
#include <stdarg.h>
#include "base/compat.h"
#if defined(_MSC_VER) && _MSC_VER < 1900
static int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) {
int count = -1;
if (size != 0)
count = _vsnprintf_s(str, size, _TRUNCATE, format, ap);
if (count == -1)
count = _vscprintf(format, ap);
return count;
}
int c99_snprintf(char* str, size_t size, const char* format, ...) {
int count;
va_list ap;
va_start(ap, format);
count = c99_vsnprintf(str, size, format, ap);
va_end(ap);
return count;
}
#endif
+11
View File
@@ -0,0 +1,11 @@
#pragma once
// Implement C99 functions and similar that are missing in MSVC.
#if defined(_MSC_VER) && _MSC_VER < 1900
int c99_snprintf(char* str, size_t size, const char* format, ...);
#define snprintf c99_snprintf
#define vscprintf _vscprintf
#endif
+12
View File
@@ -0,0 +1,12 @@
#include "base/display.h"
int dp_xres;
int dp_yres;
int pixel_xres;
int pixel_yres;
int g_dpi = 1; // will be overwritten
float g_dpi_scale = 1.0f;
float pixel_in_dps = 1.0f;
float display_hz = 60.0f;
+14
View File
@@ -0,0 +1,14 @@
#pragma once
// This is meant to be a framework for handling DPI scaling etc.
// For now, it just consists of these ugly globals.
extern int dp_xres;
extern int dp_yres;
extern int pixel_xres;
extern int pixel_yres;
extern int g_dpi;
extern float g_dpi_scale;
extern float pixel_in_dps;
extern float display_hz;
+40
View File
@@ -0,0 +1,40 @@
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/error_context.h"
#include <vector>
// TODO: Fix this threadery
#ifndef _WIN32
#undef __THREAD
#define __THREAD
#endif
__THREAD std::vector<const char *> *_error_context_name;
__THREAD std::vector<const char *> *_error_context_data;
_ErrorContext::_ErrorContext(const char *name, const char *data) {
if (!_error_context_name) {
_error_context_name = new std::vector<const char *>();
_error_context_data = new std::vector<const char *>();
_error_context_name->reserve(16);
_error_context_data->reserve(16);
}
_error_context_name->push_back(name);
_error_context_data->push_back(data);
}
_ErrorContext::~_ErrorContext() {
_error_context_name->pop_back();
_error_context_data->pop_back();
}
void _ErrorContext::Log(const char *message) {
ILOG("EC: %s", message);
for (size_t i = 0; i < _error_context_name->size(); i++) {
if ((*_error_context_data)[i] != 0) {
ILOG("EC: %s: %s", (*_error_context_name)[i], (*_error_context_data)[i]);
} else {
ILOG("EC: %s: %s", (*_error_context_name)[i], (*_error_context_data)[i]);
}
}
}
+18
View File
@@ -0,0 +1,18 @@
#pragma once
// do not inherit
// TODO: Have the crash handler investigate the error context.
// Must only be constructed on the stack - DO NOT put these on the heap.
class _ErrorContext
{
public:
_ErrorContext(const char *name, const char *data = 0);
~_ErrorContext();
// Logs the current context stack.
static void Log(const char *message);
};
#define ErrorContext(...) _ErrorContext __ec(__VA_ARGS__)
#define LogErrorContext(msg) _ErrorContext::Log(msg)
+39
View File
@@ -0,0 +1,39 @@
#pragma once
// An inline fastlist is a fixed size array with a position counter.
// Objects stored in a fastlist must be copyable.
// [] returns the objects in consecutive order, up to and not including
// the value of .size().
// Order is not preserved when removing objects.
template<class T, int max_size>
class InlineFastList {
public:
InlineFastList() : count_(0) {}
~InlineFastList() {}
T& operator [](int index) { return data_[index]; }
const T& operator [](int index) const { return data_[index]; }
int size() const { return count_; }
void Add(T t) {
data_[count_++] = t;
}
void RemoveAt(int index) {
data_[index] = data_[count_ - 1];
count_--;
}
void Remove(T t) { // Requires operator==
for (int i = 0; i < count_; i++) {
if (data_[i] == t) {
RemoveAt(i);
return;
}
}
}
private:
T data_[max_size];
int count_;
};
+19
View File
@@ -0,0 +1,19 @@
#include "base/fastlist.h"
#include "base/logging.h"
/*
#include <gtest/gtest.h>
TEST(fastlist, AddRemove) {
InlineFastList<int, 8> list;
list.Add(8);
list.Remove(7);
EXPECT_EQ(1, list.size());
list.Remove(8);
EXPECT_EQ(0, list.size());
list.Add(1);
list.Add(2);
list.Add(3);
EXPECT_EQ(3, list.size());
}
*/
+53
View File
@@ -0,0 +1,53 @@
// This file simply includes <functional> and applies any necessary compatibility fixes for
// strange platforms like iOS.
// Use placeholder as the namespace for placeholders.
#pragma once
#include <functional>
#ifdef __SYMBIAN32__
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/weak_ptr.hpp>
#else
#include <memory>
#endif
#include <vector>
#if defined(__SYMBIAN32__) || defined(MACGNUSTD)
#ifndef __SYMBIAN32__
#include <tr1/functional>
#include <tr1/memory>
#endif
namespace std {
#ifdef __SYMBIAN32__
using boost::bind;
using boost::function;
using boost::shared_ptr;
#else
using tr1::bind;
using tr1::function;
using tr1::shared_ptr;
#endif
template <typename T>
inline shared_ptr<T> make_shared()
{
return shared_ptr<T>(new T());
}
template <typename T, typename Arg1>
inline shared_ptr<T> make_shared(Arg1& arg1)
{
return shared_ptr<T>(new T(arg1));
}
}
#endif
#ifdef __SYMBIAN32__
#define placeholder
#else
namespace placeholder = std::placeholders;
#endif
+60
View File
@@ -0,0 +1,60 @@
// From http://ootips.org/yonat/4dev/linked_ptr.h
/*
* linked_ptr - simple reference linked pointer
* (like reference counting, just using a linked list of the references
* instead of their count.)
*
* The implementation stores three pointers for every linked_ptr, but
* does not allocate anything on the free store.
*/
#pragma once
template <class X> class linked_ptr
{
public:
explicit linked_ptr(X* p = 0) throw() : itsPtr(p) {itsPrev = itsNext = this;}
~linked_ptr() {release();}
linked_ptr(const linked_ptr& r) throw() {acquire(r);}
linked_ptr& operator=(const linked_ptr& r)
{
if (this != &r) {
release();
acquire(r);
}
return *this;
}
X& operator*() const throw() {return *itsPtr;}
X* operator->() const throw() {return itsPtr;}
X* get() const throw() {return itsPtr;}
bool unique() const throw() {return itsPrev ? itsPrev==this : true;}
private:
X* itsPtr;
mutable const linked_ptr* itsPrev;
mutable const linked_ptr* itsNext;
void acquire(const linked_ptr& r) throw()
{ // insert this to the list
itsPtr = r.itsPtr;
itsNext = r.itsNext;
itsNext->itsPrev = this;
itsPrev = &r;
r.itsNext = this;
}
void release()
{ // erase this from the list, delete if unique
if (unique()) delete itsPtr;
else {
itsPrev->itsNext = itsNext;
itsNext->itsPrev = itsPrev;
itsPrev = itsNext = 0;
}
itsPtr = 0;
}
};
+144
View File
@@ -0,0 +1,144 @@
#pragma once
#include <string.h>
#include "base/arch.h"
#include "base/backtrace.h"
#include "base/compat.h"
// Simple wrapper around Android's logging interface that also allows other
// implementations, and also some misc utilities.
#ifdef _MSC_VER
// Disable annoying warnings in VS
#pragma warning (disable:4996) //strcpy may be dangerous, etc.
#endif
#undef Crash
#include <stdio.h>
// Logging
#ifdef _WIN32
#ifdef _M_X64
inline void Crash() { int *x = (int *)1337; *x = 1; }
#else
inline void Crash() { __asm { int 3 }; }
#endif
#else
#if defined(_M_IX86) || defined(_M_X64)
inline void Crash() {
PrintBacktraceToStderr();
asm("int $0x3");
}
#else
inline void Crash() {
PrintBacktraceToStderr();
char *p = (char *)1337;
*p = 1;
}
#endif
#endif
// Just ILOGs on nonWindows. On Windows it outputs to the VS output console.
void OutputDebugStringUTF8(const char *p);
#if defined(ANDROID)
#include <android/log.h>
// Must only be used for logging
#ifndef APP_NAME
#define APP_NAME "NativeApp"
#endif
#ifdef _DEBUG
#define DLOG(...) __android_log_print(ANDROID_LOG_INFO, APP_NAME, __VA_ARGS__);
#else
#define DLOG(...)
#endif
#define ILOG(...) __android_log_print(ANDROID_LOG_INFO, APP_NAME, __VA_ARGS__);
#define WLOG(...) __android_log_print(ANDROID_LOG_WARN, APP_NAME, __VA_ARGS__);
#define ELOG(...) __android_log_print(ANDROID_LOG_ERROR, APP_NAME, __VA_ARGS__);
#define FLOG(...) { __android_log_print(ANDROID_LOG_ERROR, APP_NAME, __VA_ARGS__); Crash(); }
#define MessageBox(a, b, c, d) __android_log_print(ANDROID_LOG_INFO, APP_NAME, "%s %s", (b), (c));
#elif defined(__SYMBIAN32__)
#include <QDebug>
#ifdef _DEBUG
#define DLOG(...) { qDebug(__VA_ARGS__);}
#else
#define DLOG(...)
#endif
#define ILOG(...) { qDebug(__VA_ARGS__);}
#define WLOG(...) { qDebug(__VA_ARGS__);}
#define ELOG(...) { qDebug(__VA_ARGS__);}
#define FLOG(...) { qDebug(__VA_ARGS__); Crash();}
#else
#ifdef _WIN32
#define XLOG_IMPL(type, ...) do {\
char temp[512]; \
char *p = temp; \
int len = snprintf(p, sizeof(temp), type ": %s:%i: ", __FILE__, __LINE__); \
if (len < sizeof(temp)) { \
p += len; \
p += snprintf(p, sizeof(temp) - len - 3, type ": " __VA_ARGS__); \
if (p > temp + sizeof(temp) - 3) \
p = temp + sizeof(temp) - 3; \
p += sprintf(p, "\n"); \
OutputDebugStringUTF8(temp); \
} \
} while (false)
#ifdef _DEBUG
#define DLOG(...) XLOG_IMPL("D", __VA_ARGS__)
#else
#define DLOG(...)
#endif
#define ILOG(...) XLOG_IMPL("I", __VA_ARGS__)
#define WLOG(...) XLOG_IMPL("W", __VA_ARGS__)
#define ELOG(...) XLOG_IMPL("E", __VA_ARGS__)
#define FLOG(...) do {XLOG_IMPL("F", __VA_ARGS__); Crash();} while (false)
// TODO: Win32 version using OutputDebugString
#else
#include <stdio.h>
inline const char *removePath(const char *str) {
const char *slash = strrchr(str, '/');
return slash ? (slash + 1) : str;
}
#ifdef _DEBUG
#define DLOG(...) {printf("D: %s:%i: ", removePath(__FILE__), __LINE__); printf("D: " __VA_ARGS__); printf("\n");}
#else
#define DLOG(...)
#endif
#define ILOG(...) {printf("I: %s:%i: ", removePath(__FILE__), __LINE__); printf(__VA_ARGS__); printf("\n");}
#define WLOG(...) {printf("W: %s:%i: ", removePath(__FILE__), __LINE__); printf(__VA_ARGS__); printf("\n");}
#define ELOG(...) {printf("E: %s:%i: ", removePath(__FILE__), __LINE__); printf(__VA_ARGS__); printf("\n");}
#define FLOG(...) {printf("F: %s:%i: ", removePath(__FILE__), __LINE__); printf(__VA_ARGS__); printf("\n"); Crash();}
#endif
#endif
#undef CHECK
#define CHECK(a) {if (!(a)) {FLOG("%i: CHECK failed on this line", __LINE__);}}
#define CHECK_P(a, ...) {if (!(a)) {FLOG("CHECK failed: " __VA_ARGS__);}}
#define CHECK_EQ(a, b) CHECK((a) == (b));
#define CHECK_NE(a, b) CHECK((a) != (b));
#define CHECK_GT(a, b) CHECK((a) > (b));
#define CHECK_GE(a, b) CHECK((a) >= (b));
#define CHECK_LT(a, b) CHECK((a) < (b));
#define CHECK_LE(a, b) CHECK((a) <= (b));
+334
View File
@@ -0,0 +1,334 @@
#pragma once
// Simple cross platform mutex implementation.
// Similar to the new C++11 api.
// Windows and pthreads implementations in one.
// TODO: Need to clean up these primitives and put them in a reasonable namespace.
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include <limits.h>
#include <intrin.h>
// Zap stupid windows defines
// Should move these somewhere clever.
#undef p
#undef DrawText
#undef itoa
#else
#include <pthread.h>
#include <errno.h>
#include <sys/time.h>
#endif
#include "base/basictypes.h"
struct atomic_flag_init {
atomic_flag_init() : v(false) {
}
bool v;
};
static const atomic_flag_init NATIVE_ATOMIC_FLAG_INIT;
class atomic_flag {
public:
atomic_flag() {
}
atomic_flag(const atomic_flag_init &v) : value(0) {
}
void clear() {
#if defined(_WIN32)
_WriteBarrier();
value = 0;
#else
__sync_lock_release(&value);
#endif
}
// Returns the previous value.
bool test_and_set() {
#if defined(_WIN32)
return InterlockedExchange(&value, 1) != 0;
#else
return __sync_lock_test_and_set(&value, 1) != 0;
#endif
}
private:
volatile unsigned int value;
DISALLOW_COPY_AND_ASSIGN(atomic_flag);
};
class recursive_mutex {
#ifdef _WIN32
typedef CRITICAL_SECTION mutexType;
#else
typedef pthread_mutex_t mutexType;
#endif
public:
recursive_mutex() {
#ifdef _WIN32
InitializeCriticalSection(&mut_);
#else
// Critical sections are recursive so let's make these recursive too.
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mut_, &attr);
#endif
}
~recursive_mutex() {
#ifdef _WIN32
DeleteCriticalSection(&mut_);
#else
pthread_mutex_destroy(&mut_);
#endif
}
bool trylock() {
#ifdef _WIN32
return TryEnterCriticalSection(&mut_) != FALSE;
#else
return pthread_mutex_trylock(&mut_) != EBUSY;
#endif
}
void lock() {
#ifdef _WIN32
EnterCriticalSection(&mut_);
#else
pthread_mutex_lock(&mut_);
#endif
}
void unlock() {
#ifdef _WIN32
LeaveCriticalSection(&mut_);
#else
pthread_mutex_unlock(&mut_);
#endif
}
mutexType &native_handle() {
return mut_;
}
private:
mutexType mut_;
recursive_mutex(const recursive_mutex &other);
};
class lock_guard {
public:
lock_guard(recursive_mutex &mtx) : mtx_(mtx) {mtx_.lock();}
~lock_guard() {mtx_.unlock();}
private:
recursive_mutex &mtx_;
};
// Like a Windows event, or a modern condition variable.
class event {
public:
#ifdef _WIN32
#else
#endif
event() {
#ifdef _WIN32
event_ = CreateEvent(0, FALSE, FALSE, 0);
#else
pthread_cond_init(&event_, NULL);
#endif
}
~event() {
#ifdef _WIN32
CloseHandle(event_);
#else
pthread_cond_destroy(&event_);
#endif
}
void notify_one() {
#ifdef _WIN32
SetEvent(event_);
#else
pthread_cond_signal(&event_);
#endif
}
// notify_all is not really possible to implement with win32 events?
void wait(recursive_mutex &mtx) {
// broken
#ifdef _WIN32
// This has to be horribly racy.
mtx.lock();
WaitForSingleObject(event_, INFINITE);
ResetEvent(event_); // necessary?
mtx.unlock();
#else
pthread_mutex_lock(&mtx.native_handle());
pthread_cond_wait(&event_, &mtx.native_handle());
pthread_mutex_unlock(&mtx.native_handle());
#endif
}
void wait_for(recursive_mutex &mtx, int milliseconds) {
#ifdef _WIN32
//mtx.unlock();
WaitForSingleObject(event_, milliseconds);
ResetEvent(event_); // necessary?
// mtx.lock();
#else
timespec timeout;
#ifdef __APPLE__
timeval tv;
gettimeofday(&tv, NULL);
timeout.tv_sec = tv.tv_sec;
timeout.tv_nsec = tv.tv_usec * 1000;
#else
clock_gettime(CLOCK_REALTIME, &timeout);
#endif
timeout.tv_sec += milliseconds / 1000;
timeout.tv_nsec += (milliseconds % 1000) * 1000000;
pthread_mutex_lock(&mtx.native_handle());
pthread_cond_timedwait(&event_, &mtx.native_handle(), &timeout);
pthread_mutex_unlock(&mtx.native_handle());
#endif
}
void reset() {
#ifdef _WIN32
ResetEvent(event_);
#endif
}
private:
#ifdef _WIN32
HANDLE event_;
#else
pthread_cond_t event_;
#endif
};
// Win32 CONDITION_VARIABLE is Vista+ only. We thus limit it to our 64-bit builds where
// we can be almost certain that the user isn't running XP.
class condition_variable {
public:
#ifdef _WIN32
#else
#endif
condition_variable() {
#ifdef _WIN32
#ifdef _WIN64
InitializeConditionVariable(&cond_);
#else
sema_ = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
waiting_ = 0;
#endif
#else
pthread_cond_init(&event_, NULL);
#endif
}
~condition_variable() {
#ifdef _WIN32
#ifdef _WIN64
#else
CloseHandle(sema_);
#endif
#else
pthread_cond_destroy(&event_);
#endif
}
void notify_one() {
#ifdef _WIN32
#ifdef _M_X64
WakeConditionVariable(&cond_);
#else
// Should be locked at this time.
if (waiting_ != 0) {
ReleaseSemaphore(sema_, 1, NULL);
}
#endif
#else
pthread_cond_signal(&event_);
#endif
}
// notify_all is not really possible to implement with win32 events?
// Can be done just fine using WakeAllConditionVariable though.
void wait(recursive_mutex &mtx) {
// broken http://msdn.microsoft.com/en-us/library/windows/desktop/ms686301(v=vs.85).aspx
#ifdef _WIN32
#ifdef _M_X64
SleepConditionVariableCS(&cond_, &mtx.native_handle(), INFINITE);
#else
// Since a semaphore keeps a count, the window between unlock and lock won't cause us to deadlock.
// However, we could wake early incorrectly (if the semaphore is signalled already.)
// That's better than deadlocking or forgetting to wake.
waiting_++;
mtx.unlock();
WaitForSingleObject(sema_, INFINITE);
mtx.lock();
waiting_--;
#endif
#else
pthread_cond_wait(&event_, &mtx.native_handle());
#endif
}
void wait_for(recursive_mutex &mtx, int milliseconds) {
#ifdef _WIN32
#ifdef _M_X64
SleepConditionVariableCS(&cond_, &mtx.native_handle(), milliseconds);
#else
// Since a semaphore keeps a count, the window between unlock and lock won't cause us to deadlock.
// However, we could wake early incorrectly (if the semaphore is signalled already.)
// That's better than deadlocking or forgetting to wake.
waiting_++;
mtx.unlock();
WaitForSingleObject(sema_, milliseconds);
mtx.lock();
waiting_--;
#endif
#else
timespec timeout;
#ifdef __APPLE__
timeval tv;
gettimeofday(&tv, NULL);
timeout.tv_sec = tv.tv_sec;
timeout.tv_nsec = tv.tv_usec * 1000;
#else
clock_gettime(CLOCK_REALTIME, &timeout);
#endif
timeout.tv_sec += milliseconds / 1000;
timeout.tv_nsec += (milliseconds % 1000) * 1000000;
pthread_cond_timedwait(&event_, &mtx.native_handle(), &timeout);
#endif
}
private:
#ifdef _WIN32
#ifdef _M_X64
CONDITION_VARIABLE cond_;
#else
HANDLE sema_;
volatile int waiting_;
#endif
#else
pthread_cond_t event_;
#endif
};
+30
View File
@@ -0,0 +1,30 @@
#pragma once
// Trivial implementation of boost::scoped_ptr, in a way that I prefer.
template<class T>
class scoped_ptr {
public:
scoped_ptr() : ptr_(0) {}
scoped_ptr(T *p) : ptr_(p) {}
~scoped_ptr() {
delete ptr_;
}
void reset(T *p) {
delete ptr_;
ptr_ = p;
}
T *release() {
T *p = ptr_;
ptr_ = 0;
return p;
}
T *operator->() { return ptr_; }
const T *operator->() const { return ptr_; }
const T *get() const { return ptr_; }
T *get() { return ptr_; }
private:
scoped_ptr(const scoped_ptr<T> &other);
void operator=(const scoped_ptr<T> &other);
T *ptr_;
};
+18
View File
@@ -0,0 +1,18 @@
#pragma once
// Statistics collection. Not very developed.
#define STATS_ENABLE
#ifdef STATS_ENABLE
void IncrementStat(const char *name);
#define INCSTAT(name) IncrementStat(name);
#else
#define INCSTAT(name)
#endif
+280
View File
@@ -0,0 +1,280 @@
#ifdef _WIN32
#include <windows.h>
#undef min
#undef max
#endif
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <string>
#include <sstream>
#include <limits.h>
#include <algorithm>
#include <iomanip>
#include "base/buffer.h"
#include "base/stringutil.h"
#ifdef _WIN32
// Function Cross-Compatibility
#define strcasecmp _stricmp
void OutputDebugStringUTF8(const char *p) {
wchar_t temp[2048];
int len = std::min(2047, (int)strlen(p));
int size = (int)MultiByteToWideChar(CP_UTF8, 0, p, len, NULL, 0);
MultiByteToWideChar(CP_UTF8, 0, p, len, temp, size);
temp[size] = 0;
OutputDebugString(temp);
}
#else
void OutputDebugStringUTF8(const char *p) {
ILOG("%s", p);
}
#endif
void StringTrimEndNonAlphaNum(char *str) {
ssize_t n = strlen(str);
while (!isalnum(str[n]) && n >= 0) {
str[n--] = '\0';
}
}
void SkipSpace(const char **ptr) {
while (**ptr && isspace(**ptr)) {
(*ptr)++;
}
}
void StringUpper(char *str) {
while (*str) {
*str = toupper(*str);
str++;
}
}
void StringUpper(char *str, int len) {
while (len--) {
*str = toupper(*str);
str++;
}
}
unsigned int parseHex(const char *_szValue)
{
int Value = 0;
size_t Finish = strlen(_szValue);
if (Finish > 8 ) { Finish = 8; }
for (size_t Count = 0; Count < Finish; Count++) {
Value = (Value << 4);
switch( _szValue[Count] ) {
case '0': break;
case '1': Value += 1; break;
case '2': Value += 2; break;
case '3': Value += 3; break;
case '4': Value += 4; break;
case '5': Value += 5; break;
case '6': Value += 6; break;
case '7': Value += 7; break;
case '8': Value += 8; break;
case '9': Value += 9; break;
case 'A': Value += 10; break;
case 'a': Value += 10; break;
case 'B': Value += 11; break;
case 'b': Value += 11; break;
case 'C': Value += 12; break;
case 'c': Value += 12; break;
case 'D': Value += 13; break;
case 'd': Value += 13; break;
case 'E': Value += 14; break;
case 'e': Value += 14; break;
case 'F': Value += 15; break;
case 'f': Value += 15; break;
default:
Value = (Value >> 4);
Count = Finish;
}
}
return Value;
}
void DataToHexString(const uint8_t *data, size_t size, std::string *output) {
Buffer buffer;
for (size_t i = 0; i < size; i++) {
buffer.Printf("%02x ", data[i]);
if (i && !(i & 15))
buffer.Printf("\n");
}
buffer.TakeAll(output);
}
std::string StringFromFormat(const char* format, ...)
{
va_list args;
char *buf = NULL;
std::string temp = "";
#ifdef _WIN32
int required = 0;
va_start(args, format);
required = _vscprintf(format, args);
buf = new char[required + 1];
if(vsnprintf(buf, required, format, args) < 0)
buf[0] = '\0';
va_end(args);
buf[required] = '\0';
temp = buf;
delete[] buf;
#else
va_start(args, format);
if(vasprintf(&buf, format, args) < 0)
buf = NULL;
va_end(args);
if(buf != NULL) {
temp = buf;
free(buf);
}
#endif
return temp;
}
std::string StringFromInt(int value)
{
char temp[16];
sprintf(temp, "%i", value);
return temp;
}
std::string StringFromBool(bool value)
{
return value ? "True" : "False";
}
// Turns " hej " into "hej". Also handles tabs.
std::string StripSpaces(const std::string &str)
{
const size_t s = str.find_first_not_of(" \t\r\n");
if (str.npos != s)
return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1);
else
return "";
}
// "\"hello\"" is turned to "hello"
// This one assumes that the string has already been space stripped in both
// ends, as done by StripSpaces above, for example.
std::string StripQuotes(const std::string& s)
{
if (s.size() && '\"' == s[0] && '\"' == *s.rbegin())
return s.substr(1, s.size() - 2);
else
return s;
}
// For Debugging. Read out an u8 array.
std::string ArrayToString(const uint8_t *data, uint32_t size, int line_len, bool spaces)
{
std::ostringstream oss;
oss << std::setfill('0') << std::hex;
for (int line = 0; size; ++data, --size)
{
oss << std::setw(2) << (int)*data;
if (line_len == ++line)
{
oss << '\n';
line = 0;
}
else if (spaces)
oss << ' ';
}
return oss.str();
}
bool TryParse(const std::string &str, uint32_t *const output)
{
char *endptr = NULL;
// Holy crap this is ugly.
// Reset errno to a value other than ERANGE
errno = 0;
unsigned long value = strtoul(str.c_str(), &endptr, 0);
if (!endptr || *endptr)
return false;
if (errno == ERANGE)
return false;
if (ULONG_MAX > UINT_MAX) {
#ifdef _MSC_VER
#pragma warning (disable:4309)
#endif
// Note: The typecasts avoid GCC warnings when long is 32 bits wide.
if (value >= static_cast<unsigned long>(0x100000000ull)
&& value <= static_cast<unsigned long>(0xFFFFFFFF00000000ull))
return false;
}
*output = static_cast<uint32_t>(value);
return true;
}
bool TryParse(const std::string &str, bool *const output)
{
if ("1" == str || !strcasecmp("true", str.c_str()))
*output = true;
else if ("0" == str || !strcasecmp("false", str.c_str()))
*output = false;
else
return false;
return true;
}
void SplitString(const std::string& str, const char delim, std::vector<std::string>& output)
{
std::istringstream iss(str);
output.resize(1);
while (std::getline(iss, *output.rbegin(), delim))
output.push_back("");
output.pop_back();
}
std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest)
{
size_t pos = 0;
if (src == dest)
return result;
while(1)
{
pos = result.find(src, pos);
if (pos == result.npos)
break;
result.replace(pos, src.size(), dest);
pos += dest.size();
}
return result;
}
int strcmpIgnore(std::string str1, std::string str2, std::string ignorestr1, std::string ignorestr2) {
str1 = ReplaceAll(str1, ignorestr1, ignorestr2);
str2 = ReplaceAll(str2, ignorestr1, ignorestr2);
return strcmp(str1.c_str(),str2.c_str());
}
+165
View File
@@ -0,0 +1,165 @@
#pragma once
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
#include <sstream>
#include <stdarg.h>
#include <vector>
#include "base/basictypes.h"
#ifdef _MSC_VER
#pragma warning (disable:4996)
#define strncasecmp _strnicmp
#endif
#ifdef BLACKBERRY
// QNX Does not have an implementation of vasprintf
static inline int vasprintf(char **rResult, const char *aFormat, va_list aAp)
{
int rVal;
char *result;
va_list ap;
result = (char *) malloc(16);
if (result == NULL) return -1;
va_copy(ap, aAp);
rVal = vsnprintf(result, 16, aFormat, ap);
va_end(ap);
if (rVal == -1)
{
free(result);
return rVal;
}
else if (rVal >= 16)
{
free(result);
result = (char *) malloc(rVal + 1);
if (result == NULL) return -1;
va_copy(ap, aAp);
rVal = vsnprintf(result, rVal + 1, aFormat, aAp);
va_end(ap);
}
*rResult = result;
return rVal;
}
#endif
// Dumb wrapper around itoa, providing a buffer. Declare this on the stack.
class ITOA {
public:
char buffer[16];
const char *p(int i) {
sprintf(buffer, "%i", i);
return &buffer[0];
}
};
// Other simple string utilities.
inline bool startsWith(const std::string &str, const std::string &what) {
if (str.size() < what.size())
return false;
return str.substr(0, what.size()) == what;
}
inline bool endsWith(const std::string &str, const std::string &what) {
if (str.size() < what.size())
return false;
return str.substr(str.size() - what.size()) == what;
}
// Only use on strings where you're only concerned about ASCII.
inline bool startsWithNoCase(const std::string &str, const std::string &what) {
if (str.size() < what.size())
return false;
return strncasecmp(str.c_str(), what.c_str(), what.size()) == 0;
}
inline bool endsWithNoCase(const std::string &str, const std::string &what) {
if (str.size() < what.size())
return false;
const size_t offset = str.size() - what.size();
return strncasecmp(str.c_str() + offset, what.c_str(), what.size()) == 0;
}
void DataToHexString(const uint8_t *data, size_t size, std::string *output);
inline void StringToHexString(const std::string &data, std::string *output) {
DataToHexString((uint8_t *)(&data[0]), data.size(), output);
}
// highly unsafe and not recommended.
unsigned int parseHex(const char* _szValue);
// Suitable for inserting into maps, unlike char*, and cheaper than std::string.
// Strings must be constant and preferably be stored in the read-only part
// of the binary.
class ConstString {
public:
ConstString(const char *ptr) {
ptr_ = ptr;
}
bool operator <(const ConstString &other) const {
return strcmp(ptr_, other.ptr_) < 0;
}
bool operator ==(const ConstString &other) const {
return ptr_ == other.ptr_ || !strcmp(ptr_, other.ptr_);
}
const char *get() const { return ptr_; }
private:
const char *ptr_;
};
std::string StringFromFormat(const char* format, ...);
std::string StringFromInt(int value);
std::string StringFromBool(bool value);
std::string ArrayToString(const uint8_t *data, uint32_t size, int line_len = 20, bool spaces = true);
std::string StripSpaces(const std::string &s);
std::string StripQuotes(const std::string &s);
bool TryParse(const std::string &str, bool *const output);
bool TryParse(const std::string &str, uint32_t *const output);
template <typename N>
static bool TryParse(const std::string &str, N *const output)
{
std::istringstream iss(str);
N tmp = 0;
if (iss >> tmp)
{
*output = tmp;
return true;
}
else
return false;
}
void SplitString(const std::string& str, const char delim, std::vector<std::string>& output);
std::string ReplaceAll(std::string input, const std::string& src, const std::string& dest);
// Compare two strings, ignore the difference between the ignorestr1 and the ignorestr2 in str1 and str2.
int strcmpIgnore(std::string str1, std::string str2, std::string ignorestr1, std::string ignorestr2);
template <typename N>
static std::string ValueToString(const N value)
{
std::stringstream string;
string << value;
return string.str();
}
void StringTrimEndNonAlphaNum(char *str);
void SkipSpace(const char **ptr);
void StringUpper(char *str);
void StringUpper(char *str, int len);
+151
View File
@@ -0,0 +1,151 @@
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/timeutil.h"
// For NV time functions. Ugly!
#include "gfx/gl_common.h"
#include "gfx_es2/gpu_features.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/time.h>
#include <unistd.h>
#endif
#include <stdio.h>
static double curtime = 0;
static float curtime_f = 0;
#ifdef _WIN32
LARGE_INTEGER frequency;
double frequencyMult;
LARGE_INTEGER startTime;
double real_time_now() {
if (frequency.QuadPart == 0) {
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&startTime);
curtime = 0.0;
frequencyMult = 1.0 / static_cast<double>(frequency.QuadPart);
}
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
double elapsed = static_cast<double>(time.QuadPart - startTime.QuadPart);
return elapsed * frequencyMult;
}
#elif defined(BLACKBERRY)
double real_time_now() {
struct timespec time;
clock_gettime(CLOCK_MONOTONIC, &time); // Linux must use CLOCK_MONOTONIC_RAW due to time warps
return time.tv_sec + time.tv_nsec / 1.0e9;
}
#else
uint64_t _frequency = 0;
uint64_t _starttime = 0;
double real_time_now() {
#ifdef ANDROID
if (false && gl_extensions.EGL_NV_system_time) {
// This is needed to profile using PerfHUD on Tegra
if (_frequency == 0) {
_frequency = eglGetSystemTimeFrequencyNV();
_starttime = eglGetSystemTimeNV();
}
uint64_t cur = eglGetSystemTimeNV();
int64_t diff = cur - _starttime;
return (double)diff / (double)_frequency;
}
#if 0
// This clock is really "choppy" on Nexus 9!
struct timespec time;
clock_gettime(CLOCK_MONOTONIC_RAW, &time);
return time.tv_sec + time.tv_nsec / 1.0e9;
#else
static time_t start;
struct timeval tv;
gettimeofday(&tv, NULL);
if (start == 0) {
start = tv.tv_sec;
}
tv.tv_sec -= start;
return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
#endif
#else
static time_t start;
struct timeval tv;
gettimeofday(&tv, NULL);
if (start == 0) {
start = tv.tv_sec;
}
tv.tv_sec -= start;
return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
#endif
}
#endif
void time_update() {
curtime = real_time_now();
curtime_f = (float)curtime;
//printf("curtime: %f %f\n", curtime, curtime_f);
// also smooth time.
//curtime+=float((double) (time-_starttime) / (double) _frequency);
//curtime*=0.5f;
//curtime+=1.0f/60.0f;
//lastTime=curtime;
//curtime_f = (float)curtime;
}
float time_now() {
return curtime_f;
}
double time_now_d() {
return curtime;
}
int time_now_ms() {
return int(curtime*1000.0);
}
void sleep_ms(int ms) {
#ifdef _WIN32
#ifndef METRO
Sleep(ms);
#endif
#else
usleep(ms * 1000);
#endif
}
LoggingDeadline::LoggingDeadline(const char *name, int ms) : name_(name), endCalled_(false) {
totalTime_ = (double)ms * 0.001;
time_update();
endTime_ = time_now_d() + totalTime_;
}
LoggingDeadline::~LoggingDeadline() {
if (!endCalled_)
End();
}
bool LoggingDeadline::End() {
endCalled_ = true;
time_update();
if (time_now_d() > endTime_) {
double late = (time_now_d() - endTime_);
double totalTime = late + totalTime_;
ELOG("===== %0.2fms DEADLINE PASSED FOR %s at %0.2fms - %0.2fms late =====", totalTime_ * 1000.0, name_, 1000.0 * totalTime, 1000.0 * late);
return false;
}
return true;
}
+37
View File
@@ -0,0 +1,37 @@
#pragma once
// http://linux.die.net/man/3/clock_gettime
// This time implementation caches the time for max performance (call time_now() as much as you like).
// You need to call time_update() once per frame (or whenever you need the correct time right now).
void time_update();
// Seconds.
float time_now();
double time_now_d();
// Uncached time. Slower than the above cached time functions. Does not update cached time, call time_update for that.
double real_time_now();
int time_now_ms();
// Sleep. Does not necessarily have millisecond granularity, especially on Windows.
void sleep_ms(int ms);
// Can be sprinkled around the code to make sure that thing don't take
// unexpectedly long. What that means is up to you.
class LoggingDeadline {
public:
LoggingDeadline(const char *name, int deadline_in_ms);
~LoggingDeadline();
bool End();
private:
const char *name_;
bool endCalled_;
double totalTime_;
double endTime_;
};
+105
View File
@@ -0,0 +1,105 @@
// Little utility functions for data compression.
// Taken from http://panthema.net/2007/0328-ZLibString.html
#include <string>
#include <stdexcept>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <cstring>
#include <zlib.h>
#include "base/logging.h"
/** Compress a STL string using zlib with given compression level and return
* the binary data. */
bool compress_string(const std::string& str, std::string *dest, int compressionlevel) {
z_stream zs; // z_stream is zlib's control structure
memset(&zs, 0, sizeof(zs));
if (deflateInit(&zs, compressionlevel) != Z_OK) {
ELOG("deflateInit failed while compressing.");
return false;
}
zs.next_in = (Bytef*)str.data();
zs.avail_in = (uInt)str.size(); // set the z_stream's input
int ret;
char outbuffer[32768];
std::string outstring;
// retrieve the compressed bytes blockwise
do {
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
zs.avail_out = sizeof(outbuffer);
ret = deflate(&zs, Z_FINISH);
if (outstring.size() < zs.total_out) {
// append the block to the output string
outstring.append(outbuffer,
zs.total_out - outstring.size());
}
} while (ret == Z_OK);
deflateEnd(&zs);
if (ret != Z_STREAM_END) { // an error occurred that was not EOF
std::ostringstream oss;
oss << "Exception during zlib compression: (" << ret << ") " << zs.msg;
return false;
}
*dest = outstring;
return true;
}
/** Decompress an STL string using zlib and return the original data. */
bool decompress_string(const std::string& str, std::string *dest) {
if (!str.size())
return false;
z_stream zs; // z_stream is zlib's control structure
memset(&zs, 0, sizeof(zs));
// modification by hrydgard: inflateInit2, 16+MAXWBITS makes it read gzip data too
if (inflateInit2(&zs, 32+MAX_WBITS) != Z_OK) {
ELOG("inflateInit failed while decompressing.");
return false;
}
zs.next_in = (Bytef*)str.data();
zs.avail_in = (uInt)str.size();
int ret;
char outbuffer[32768];
std::string outstring;
// get the decompressed bytes blockwise using repeated calls to inflate
do {
zs.next_out = reinterpret_cast<Bytef*>(outbuffer);
zs.avail_out = sizeof(outbuffer);
ret = inflate(&zs, 0);
if (outstring.size() < zs.total_out) {
outstring.append(outbuffer,
zs.total_out - outstring.size());
}
} while (ret == Z_OK);
inflateEnd(&zs);
if (ret != Z_STREAM_END) { // an error occurred that was not EOF
std::ostringstream oss;
ELOG("Exception during zlib decompression: (%i) %s", ret, zs.msg);
return false;
}
*dest = outstring;
return true;
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <string>
bool compress_string(const std::string& str, std::string *dest, int compressionlevel = 9);
bool decompress_string(const std::string& str, std::string *dest);
// Delta encoding/decoding - many formats benefit from a pass of this before zlibbing.
// WARNING : Do not use these with floating point data, especially not float16...
template <class T>
inline void delta(T *data, int length) {
T prev = data[0];
for (int i = 1; i < length; i++) {
T temp = data[i] - prev;
prev = data[i];
data[i] = temp;
}
}
template <class T>
inline void dedelta(T *data, int length) {
for (int i = 1; i < length; i++) {
data[i] += data[i - 1];
}
}
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#include <vector>
#include <string>
class Listable
{
public:
virtual ~Listable() {}
virtual const char *getItem(size_t i) const = 0;
virtual size_t numItems() const = 0;
// Returns -1 for not found.
// Child classes are meant to specialize this if they have a faster way
// than brute force search.
virtual int getIndex(const char *text) {
for (size_t i = 0; i < numItems(); i++) {
if (!strcmp(getItem(i), text))
return i;
}
return -1;
}
};
class ArrayListable : public Listable
{
public:
ArrayListable(const char * const*arr, size_t count) : arr_(arr), count_(count) {}
virtual ~ArrayListable() {}
virtual const char *getItem(size_t i) const { return arr_[i]; }
virtual size_t numItems() const { return count_; }
private:
const char *const*arr_;
size_t count_;
};
class VectorListable : public Listable
{
VectorListable(const std::vector<std::string> &vec) : vec_(vec) {}
virtual ~VectorListable() {}
virtual const char *getItem(size_t i) const { return vec_[i].c_str(); }
virtual size_t numItems() const { return vec_.size(); }
private:
const std::vector<std::string> &vec_;
};
+19
View File
@@ -0,0 +1,19 @@
// Copyright (c) 2011 Google, Inc.
//
// 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.
+200
View File
@@ -0,0 +1,200 @@
NOTE: This is a cut down version of Google CityHash for easy building.
CityHash, a family of hash functions for strings.
Introduction
============
CityHash provides hash functions for strings. The functions mix the
input bits thoroughly but are not suitable for cryptography. See
"Hash Quality," below, for details on how CityHash was tested and so on.
We provide reference implementations in C++, with a friendly MIT license.
CityHash32() returns a 32-bit hash.
CityHash64() and similar return a 64-bit hash.
CityHash128() and similar return a 128-bit hash and are tuned for
strings of at least a few hundred bytes. Depending on your compiler
and hardware, it's likely faster than CityHash64() on sufficiently long
strings. It's slower than necessary on shorter strings, but we expect
that case to be relatively unimportant.
CityHashCrc128() and similar are variants of CityHash128() that depend
on _mm_crc32_u64(), an intrinsic that compiles to a CRC32 instruction
on some CPUs. However, none of the functions we provide are CRCs.
CityHashCrc256() is a variant of CityHashCrc128() that also depends
on _mm_crc32_u64(). It returns a 256-bit hash.
All members of the CityHash family were designed with heavy reliance
on previous work by Austin Appleby, Bob Jenkins, and others.
For example, CityHash32 has many similarities with Murmur3a.
Performance on long strings: 64-bit CPUs
========================================
We are most excited by the performance of CityHash64() and its variants on
short strings, but long strings are interesting as well.
CityHash is intended to be fast, under the constraint that it hash very
well. For CPUs with the CRC32 instruction, CRC is speedy, but CRC wasn't
designed as a hash function and shouldn't be used as one. CityHashCrc128()
is not a CRC, but it uses the CRC32 machinery.
On a single core of a 2.67GHz Intel Xeon X5550, CityHashCrc256 peaks at about
5 to 5.5 bytes/cycle. The other CityHashCrc functions are wrappers around
CityHashCrc256 and should have similar performance on long strings.
(CityHashCrc256 in v1.0.3 was even faster, but we decided it wasn't as thorough
as it should be.) CityHash128 peaks at about 4.3 bytes/cycle. The fastest
Murmur variant on that hardware, Murmur3F, peaks at about 2.4 bytes/cycle.
We expect the peak speed of CityHash128 to dominate CityHash64, which is
aimed more toward short strings or use in hash tables.
For long strings, a new function by Bob Jenkins, SpookyHash, is just
slightly slower than CityHash128 on Intel x86-64 CPUs, but noticeably
faster on AMD x86-64 CPUs. For hashing long strings on AMD CPUs
and/or CPUs without the CRC instruction, SpookyHash may be just as
good or better than any of the CityHash variants.
Performance on short strings: 64-bit CPUs
=========================================
For short strings, e.g., most hash table keys, CityHash64 is faster than
CityHash128, and probably faster than all the aforementioned functions,
depending on the mix of string lengths. Here are a few results from that
same hardware, where we (unrealistically) tested a single string length over
and over again:
Hash Results
------------------------------------------------------------------------------
CityHash64 v1.0.3 7ns for 1 byte, or 6ns for 8 bytes, or 9ns for 64 bytes
Murmur2 (64-bit) 6ns for 1 byte, or 6ns for 8 bytes, or 15ns for 64 bytes
Murmur3F 14ns for 1 byte, or 15ns for 8 bytes, or 23ns for 64 bytes
We don't have CityHash64 benchmarks results for v1.1, but we expect the
numbers to be similar.
Performance: 32-bit CPUs
========================
CityHash32 is the newest variant of CityHash. It is intended for
32-bit hardware in general but has been mostly tested on x86. Our benchmarks
suggest that Murmur3 is the nearest competitor to CityHash32 on x86.
We don't know of anything faster that has comparable quality. The speed rankings
in our testing: CityHash32 > Murmur3f > Murmur3a (for long strings), and
CityHash32 > Murmur3a > Murmur3f (for short strings).
Installation
============
We provide reference implementations of several CityHash functions, written
in C++. The build system is based on autoconf. It defaults the C++
compiler flags to "-g -O2", which is probably slower than -O3 if you are
using gcc. YMMV.
On systems with gcc, we generally recommend:
./configure
make all check CXXFLAGS="-g -O3"
sudo make install
Or, if your system has the CRC32 instruction, and you want to build everything:
./configure --enable-sse4.2
make all check CXXFLAGS="-g -O3 -msse4.2"
sudo make install
Note that our build system doesn't try to determine the appropriate compiler
flag for enabling SSE4.2. For gcc it is "-msse4.2". The --enable-sse4.2
flag to the configure script controls whether citycrc.h is installed when
you "make install." In general, picking the right compiler flags can be
tricky, and may depend on your compiler, your hardware, and even how you
plan to use the library.
For generic information about how to configure this software, please try:
./configure --help
Failing that, please work from city.cc and city*.h, as they contain all the
necessary code.
Usage
=====
The above installation instructions will produce a single library. It will
contain CityHash32(), CityHash64(), and CityHash128(), and their variants,
and possibly CityHashCrc128(), CityHashCrc128WithSeed(), and
CityHashCrc256(). The functions with Crc in the name are declared in
citycrc.h; the rest are declared in city.h.
Limitations
===========
1) CityHash32 is intended for little-endian 32-bit code, and everything else in
the current version of CityHash is intended for little-endian 64-bit CPUs.
All functions that don't use the CRC32 instruction should work in
little-endian 32-bit or 64-bit code. CityHash should work on big-endian CPUs
as well, but we haven't tested that very thoroughly yet.
2) CityHash is fairly complex. As a result of its complexity, it may not
perform as expected on some compilers. For example, preliminary reports
suggest that some Microsoft compilers compile CityHash to assembly that's
10-20% slower than it could be.
Hash Quality
============
We like to test hash functions with SMHasher, among other things.
SMHasher isn't perfect, but it seems to find almost any significant flaw.
SMHasher is available at http://code.google.com/p/smhasher/
SMHasher is designed to pass a 32-bit seed to the hash functions it tests.
No CityHash function is designed to work that way, so we adapt as follows:
For our functions that accept a seed, we use the given seed directly (padded
with zeroes); for our functions that don't accept a seed, we hash the
concatenation of the given seed and the input string.
The CityHash functions have the following flaws according to SMHasher:
(1) CityHash64: none
(2) CityHash64WithSeed: none
(3) CityHash64WithSeeds: did not test
(4) CityHash128: none
(5) CityHash128WithSeed: none
(6) CityHashCrc128: none
(7) CityHashCrc128WithSeed: none
(8) CityHashCrc256: none
(9) CityHash32: none
Some minor flaws in 32-bit and 64-bit functions are harmless, as we
expect the primary use of these functions will be in hash tables. We
may have gone slightly overboard in trying to please SMHasher and other
similar tests, but we don't want anyone to choose a different hash function
because of some minor issue reported by a quality test.
For more information
====================
http://code.google.com/p/cityhash/
cityhash-discuss@googlegroups.com
Please feel free to send us comments, questions, bug reports, or patches.
+654
View File
@@ -0,0 +1,654 @@
// Copyright (c) 2011 Google, Inc.
//
// 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.
//
// CityHash, by Geoff Pike and Jyrki Alakuijala
//
// This file provides CityHash64() and related functions.
//
// It's probably possible to create even faster hash functions by
// writing a program that systematically explores some of the space of
// possible hash functions, by using SIMD instructions, or by
// compromising on hash quality.
#ifdef ANDROID
#undef __SSE4_2__
#endif
#include "city.h"
#include <algorithm>
#include <stdlib.h> // To check for glibc
#include <string.h> // for memcpy and memset
using namespace std;
static uint64 UNALIGNED_LOAD64(const char *p) {
uint64 result;
memcpy(&result, p, sizeof(result));
return result;
}
static uint32 UNALIGNED_LOAD32(const char *p) {
uint32 result;
memcpy(&result, p, sizeof(result));
return result;
}
#ifdef _MSC_VER
#define bswap_32(x) _byteswap_ulong(x)
#define bswap_64(x) _byteswap_uint64(x)
#elif defined(__GLIBC__)
#include <byteswap.h>
#elif defined(__APPLE__)
// Mac OS X / Darwin features
#include <libkern/OSByteOrder.h>
#define bswap_32(x) OSSwapInt32(x)
#define bswap_64(x) OSSwapInt64(x)
#elif defined(__sun) || defined(sun)
#include <sys/byteorder.h>
#define bswap_32(x) BSWAP_32(x)
#define bswap_64(x) BSWAP_64(x)
#elif defined(__DragonFly__) || defined(__FreeBSD__)
#include <sys/endian.h>
#define bswap_32(x) bswap32(x)
#define bswap_64(x) bswap64(x)
#elif defined(__Bitrig__) || defined(__OpenBSD__)
#include <sys/types.h>
#define bswap_32(x) swap32(x)
#define bswap_64(x) swap64(x)
#elif defined(__NetBSD__)
#include <sys/types.h>
#include <machine/bswap.h>
#if defined(__BSWAP_RENAME) && !defined(__bswap_32)
#define bswap_32(x) bswap32(x)
#define bswap_64(x) bswap64(x)
#endif
#else
#define bswap_32(x) (0 | (((x) & 0x000000ff) << 24) \
| (((x) & 0x0000ff00) << 8) \
| (((x) & 0x00ff0000) >> 8) \
| (((x) & 0xff000000) >> 24))
#define bswap_64(x) (0 | (((x) & UINT64_C(0x00000000000000ff)) << 56) \
| (((x) & UINT64_C(0x000000000000ff00)) << 40) \
| (((x) & UINT64_C(0x0000000000ff0000)) << 24) \
| (((x) & UINT64_C(0x00000000ff000000)) << 8) \
| (((x) & UINT64_C(0x000000ff00000000)) >> 8) \
| (((x) & UINT64_C(0x0000ff0000000000)) >> 24) \
| (((x) & UINT64_C(0x00ff000000000000)) >> 40) \
| (((x) & UINT64_C(0xff00000000000000)) >> 56))
#endif
#ifdef WORDS_BIGENDIAN
#define uint32_in_expected_order(x) (bswap_32(x))
#define uint64_in_expected_order(x) (bswap_64(x))
#else
#define uint32_in_expected_order(x) (x)
#define uint64_in_expected_order(x) (x)
#endif
#if !defined(LIKELY)
#if HAVE_BUILTIN_EXPECT
#define LIKELY(x) (__builtin_expect(!!(x), 1))
#else
#define LIKELY(x) (x)
#endif
#endif
static uint64 Fetch64(const char *p) {
return uint64_in_expected_order(UNALIGNED_LOAD64(p));
}
static uint32 Fetch32(const char *p) {
return uint32_in_expected_order(UNALIGNED_LOAD32(p));
}
// Some primes between 2^63 and 2^64 for various uses.
static const uint64 k0 = 0xc3a5c85c97cb3127ULL;
static const uint64 k1 = 0xb492b66fbe98f273ULL;
static const uint64 k2 = 0x9ae16a3b2f90404fULL;
// Magic numbers for 32-bit hashing. Copied from Murmur3.
static const uint32 c1 = 0xcc9e2d51;
static const uint32 c2 = 0x1b873593;
// A 32-bit to 32-bit integer hash copied from Murmur3.
static uint32 fmix(uint32 h)
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
static uint32 Rotate32(uint32 val, int shift) {
// Avoid shifting by 32: doing so yields an undefined result.
return shift == 0 ? val : ((val >> shift) | (val << (32 - shift)));
}
#undef PERMUTE3
#define PERMUTE3(a, b, c) do { std::swap(a, b); std::swap(a, c); } while (0)
static uint32 Mur(uint32 a, uint32 h) {
// Helper from Murmur3 for combining two 32-bit values.
a *= c1;
a = Rotate32(a, 17);
a *= c2;
h ^= a;
h = Rotate32(h, 19);
return h * 5 + 0xe6546b64;
}
static uint32 Hash32Len13to24(const char *s, size_t len) {
uint32 a = Fetch32(s - 4 + (len >> 1));
uint32 b = Fetch32(s + 4);
uint32 c = Fetch32(s + len - 8);
uint32 d = Fetch32(s + (len >> 1));
uint32 e = Fetch32(s);
uint32 f = Fetch32(s + len - 4);
uint32 h = (uint32)len;
return fmix(Mur(f, Mur(e, Mur(d, Mur(c, Mur(b, Mur(a, h)))))));
}
static uint32 Hash32Len0to4(const char *s, size_t len) {
uint32 b = 0;
uint32 c = 9;
for (size_t i = 0; i < len; i++) {
signed char v = s[i];
b = b * c1 + v;
c ^= b;
}
return fmix(Mur(b, Mur((uint32)len, c)));
}
static uint32 Hash32Len5to12(const char *s, size_t len) {
uint32 a = (uint32)len, b = (uint32)len * 5, c = 9, d = b;
a += Fetch32(s);
b += Fetch32(s + len - 4);
c += Fetch32(s + ((len >> 1) & 4));
return fmix(Mur(c, Mur(b, Mur(a, d))));
}
uint32 CityHash32(const char *s, size_t len) {
if (len <= 24) {
return len <= 12 ?
(len <= 4 ? Hash32Len0to4(s, len) : Hash32Len5to12(s, len)) :
Hash32Len13to24(s, len);
}
// len > 24
uint32 h = (uint32)len, g = c1 * (uint32)len, f = g;
uint32 a0 = Rotate32(Fetch32(s + len - 4) * c1, 17) * c2;
uint32 a1 = Rotate32(Fetch32(s + len - 8) * c1, 17) * c2;
uint32 a2 = Rotate32(Fetch32(s + len - 16) * c1, 17) * c2;
uint32 a3 = Rotate32(Fetch32(s + len - 12) * c1, 17) * c2;
uint32 a4 = Rotate32(Fetch32(s + len - 20) * c1, 17) * c2;
h ^= a0;
h = Rotate32(h, 19);
h = h * 5 + 0xe6546b64;
h ^= a2;
h = Rotate32(h, 19);
h = h * 5 + 0xe6546b64;
g ^= a1;
g = Rotate32(g, 19);
g = g * 5 + 0xe6546b64;
g ^= a3;
g = Rotate32(g, 19);
g = g * 5 + 0xe6546b64;
f += a4;
f = Rotate32(f, 19);
f = f * 5 + 0xe6546b64;
size_t iters = (len - 1) / 20;
do {
uint32 a0 = Rotate32(Fetch32(s) * c1, 17) * c2;
uint32 a1 = Fetch32(s + 4);
uint32 a2 = Rotate32(Fetch32(s + 8) * c1, 17) * c2;
uint32 a3 = Rotate32(Fetch32(s + 12) * c1, 17) * c2;
uint32 a4 = Fetch32(s + 16);
h ^= a0;
h = Rotate32(h, 18);
h = h * 5 + 0xe6546b64;
f += a1;
f = Rotate32(f, 19);
f = f * c1;
g += a2;
g = Rotate32(g, 18);
g = g * 5 + 0xe6546b64;
h ^= a3 + a1;
h = Rotate32(h, 19);
h = h * 5 + 0xe6546b64;
g ^= a4;
g = bswap_32(g) * 5;
h += a4 * 5;
h = bswap_32(h);
f += a0;
PERMUTE3(f, h, g);
s += 20;
} while (--iters != 0);
g = Rotate32(g, 11) * c1;
g = Rotate32(g, 17) * c1;
f = Rotate32(f, 11) * c1;
f = Rotate32(f, 17) * c1;
h = Rotate32(h + g, 19);
h = h * 5 + 0xe6546b64;
h = Rotate32(h, 17) * c1;
h = Rotate32(h + f, 19);
h = h * 5 + 0xe6546b64;
h = Rotate32(h, 17) * c1;
return h;
}
// Bitwise right rotate. Normally this will compile to a single
// instruction, especially if the shift is a manifest constant.
static uint64 Rotate(uint64 val, int shift) {
// Avoid shifting by 64: doing so yields an undefined result.
return shift == 0 ? val : ((val >> shift) | (val << (64 - shift)));
}
static uint64 ShiftMix(uint64 val) {
return val ^ (val >> 47);
}
static uint64 HashLen16(uint64 u, uint64 v) {
return Hash128to64(uint128(u, v));
}
static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) {
// Murmur-inspired hashing.
uint64 a = (u ^ v) * mul;
a ^= (a >> 47);
uint64 b = (v ^ a) * mul;
b ^= (b >> 47);
b *= mul;
return b;
}
static uint64 HashLen0to16(const char *s, size_t len) {
if (len >= 8) {
uint64 mul = k2 + len * 2;
uint64 a = Fetch64(s) + k2;
uint64 b = Fetch64(s + len - 8);
uint64 c = Rotate(b, 37) * mul + a;
uint64 d = (Rotate(a, 25) + b) * mul;
return HashLen16(c, d, mul);
}
if (len >= 4) {
uint64 mul = k2 + len * 2;
uint64 a = Fetch32(s);
return HashLen16(len + (a << 3), Fetch32(s + len - 4), mul);
}
if (len > 0) {
uint8 a = s[0];
uint8 b = s[len >> 1];
uint8 c = s[len - 1];
uint32 y = static_cast<uint32>(a) + (static_cast<uint32>(b) << 8);
uint32 z = (uint32)len + (static_cast<uint32>(c) << 2);
return ShiftMix(y * k2 ^ z * k0) * k2;
}
return k2;
}
// This probably works well for 16-byte strings as well, but it may be overkill
// in that case.
static uint64 HashLen17to32(const char *s, size_t len) {
uint64 mul = k2 + len * 2;
uint64 a = Fetch64(s) * k1;
uint64 b = Fetch64(s + 8);
uint64 c = Fetch64(s + len - 8) * mul;
uint64 d = Fetch64(s + len - 16) * k2;
return HashLen16(Rotate(a + b, 43) + Rotate(c, 30) + d,
a + Rotate(b + k2, 18) + c, mul);
}
// Return a 16-byte hash for 48 bytes. Quick and dirty.
// Callers do best to use "random-looking" values for a and b.
static pair<uint64, uint64> WeakHashLen32WithSeeds(
uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, uint64 b) {
a += w;
b = Rotate(b + a + z, 21);
uint64 c = a;
a += x;
a += y;
b += Rotate(a, 44);
return make_pair(a + z, b + c);
}
// Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty.
static pair<uint64, uint64> WeakHashLen32WithSeeds(
const char* s, uint64 a, uint64 b) {
return WeakHashLen32WithSeeds(Fetch64(s),
Fetch64(s + 8),
Fetch64(s + 16),
Fetch64(s + 24),
a,
b);
}
// Return an 8-byte hash for 33 to 64 bytes.
static uint64 HashLen33to64(const char *s, size_t len) {
uint64 mul = k2 + len * 2;
uint64 a = Fetch64(s) * k2;
uint64 b = Fetch64(s + 8);
uint64 c = Fetch64(s + len - 24);
uint64 d = Fetch64(s + len - 32);
uint64 e = Fetch64(s + 16) * k2;
uint64 f = Fetch64(s + 24) * 9;
uint64 g = Fetch64(s + len - 8);
uint64 h = Fetch64(s + len - 16) * mul;
uint64 u = Rotate(a + g, 43) + (Rotate(b, 30) + c) * 9;
uint64 v = ((a + g) ^ d) + f + 1;
uint64 w = bswap_64((u + v) * mul) + h;
uint64 x = Rotate(e + f, 42) + c;
uint64 y = (bswap_64((v + w) * mul) + g) * mul;
uint64 z = e + f + c;
a = bswap_64((x + z) * mul + y) + b;
b = ShiftMix((z + a) * mul + d + h) * mul;
return b + x;
}
uint64 CityHash64(const char *s, size_t len) {
if (len <= 32) {
if (len <= 16) {
return HashLen0to16(s, len);
} else {
return HashLen17to32(s, len);
}
} else if (len <= 64) {
return HashLen33to64(s, len);
}
// For strings over 64 bytes we hash the end first, and then as we
// loop we keep 56 bytes of state: v, w, x, y, and z.
uint64 x = Fetch64(s + len - 40);
uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56);
uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24));
pair<uint64, uint64> v = WeakHashLen32WithSeeds(s + len - 64, len, z);
pair<uint64, uint64> w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x);
x = x * k1 + Fetch64(s);
// Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
len = (len - 1) & ~static_cast<size_t>(63);
do {
x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
x ^= w.second;
y += v.first + Fetch64(s + 40);
z = Rotate(z + w.first, 33) * k1;
v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
std::swap(z, x);
s += 64;
len -= 64;
} while (len != 0);
return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z,
HashLen16(v.second, w.second) + x);
}
uint64 CityHash64WithSeed(const char *s, size_t len, uint64 seed) {
return CityHash64WithSeeds(s, len, k2, seed);
}
uint64 CityHash64WithSeeds(const char *s, size_t len,
uint64 seed0, uint64 seed1) {
return HashLen16(CityHash64(s, len) - seed0, seed1);
}
// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
// of any length representable in signed long. Based on City and Murmur.
static uint128 CityMurmur(const char *s, size_t len, uint128 seed) {
uint64 a = Uint128Low64(seed);
uint64 b = Uint128High64(seed);
uint64 c = 0;
uint64 d = 0;
signed long l = (signed long)len - 16;
if (l <= 0) { // len <= 16
a = ShiftMix(a * k1) * k1;
c = b * k1 + HashLen0to16(s, len);
d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c));
} else { // len > 16
c = HashLen16(Fetch64(s + len - 8) + k1, a);
d = HashLen16(b + len, c + Fetch64(s + len - 16));
a += d;
do {
a ^= ShiftMix(Fetch64(s) * k1) * k1;
a *= k1;
b ^= a;
c ^= ShiftMix(Fetch64(s + 8) * k1) * k1;
c *= k1;
d ^= c;
s += 16;
l -= 16;
} while (l > 0);
}
a = HashLen16(a, c);
b = HashLen16(d, b);
return uint128(a ^ b, HashLen16(b, a));
}
uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed) {
if (len < 128) {
return CityMurmur(s, len, seed);
}
// We expect len >= 128 to be the common case. Keep 56 bytes of state:
// v, w, x, y, and z.
pair<uint64, uint64> v, w;
uint64 x = Uint128Low64(seed);
uint64 y = Uint128High64(seed);
uint64 z = len * k1;
v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s);
v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8);
w.first = Rotate(y + z, 35) * k1 + x;
w.second = Rotate(x + Fetch64(s + 88), 53) * k1;
// This is the same inner loop as CityHash64(), manually unrolled.
do {
x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
x ^= w.second;
y += v.first + Fetch64(s + 40);
z = Rotate(z + w.first, 33) * k1;
v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
std::swap(z, x);
s += 64;
x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
x ^= w.second;
y += v.first + Fetch64(s + 40);
z = Rotate(z + w.first, 33) * k1;
v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first);
w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16));
std::swap(z, x);
s += 64;
len -= 128;
} while (LIKELY(len >= 128));
x += Rotate(v.first + z, 49) * k0;
y = y * k0 + Rotate(w.second, 37);
z = z * k0 + Rotate(w.first, 27);
w.first *= 9;
v.first *= k0;
// If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
for (size_t tail_done = 0; tail_done < len; ) {
tail_done += 32;
y = Rotate(x + y, 42) * k0 + v.second;
w.first += Fetch64(s + len - tail_done + 16);
x = x * k0 + w.first;
z += w.second + Fetch64(s + len - tail_done);
w.second += v.first;
v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second);
v.first *= k0;
}
// At this point our 56 bytes of state should contain more than
// enough information for a strong 128-bit hash. We use two
// different 56-byte-to-8-byte hashes to get a 16-byte final result.
x = HashLen16(x, v.first);
y = HashLen16(y + z, w.first);
return uint128(HashLen16(x + v.second, w.second) + y,
HashLen16(x + w.second, y + v.second));
}
uint128 CityHash128(const char *s, size_t len) {
return len >= 16 ?
CityHash128WithSeed(s + 16, len - 16,
uint128(Fetch64(s), Fetch64(s + 8) + k0)) :
CityHash128WithSeed(s, len, uint128(k0, k1));
}
#ifdef __SSE4_2__
#include "citycrc.h"
#include <nmmintrin.h>
// Requires len >= 240.
static void CityHashCrc256Long(const char *s, size_t len,
uint32 seed, uint64 *result) {
uint64 a = Fetch64(s + 56) + k0;
uint64 b = Fetch64(s + 96) + k0;
uint64 c = result[0] = HashLen16(b, len);
uint64 d = result[1] = Fetch64(s + 120) * k0 + len;
uint64 e = Fetch64(s + 184) + seed;
uint64 f = 0;
uint64 g = 0;
uint64 h = c + d;
uint64 x = seed;
uint64 y = 0;
uint64 z = 0;
// 240 bytes of input per iter.
size_t iters = len / 240;
len -= iters * 240;
do {
#undef CHUNK
#define CHUNK(r) \
PERMUTE3(x, z, y); \
b += Fetch64(s); \
c += Fetch64(s + 8); \
d += Fetch64(s + 16); \
e += Fetch64(s + 24); \
f += Fetch64(s + 32); \
a += b; \
h += f; \
b += c; \
f += d; \
g += e; \
e += z; \
g += x; \
z = _mm_crc32_u64(z, b + g); \
y = _mm_crc32_u64(y, e + h); \
x = _mm_crc32_u64(x, f + a); \
e = Rotate(e, r); \
c += e; \
s += 40
CHUNK(0); PERMUTE3(a, h, c);
CHUNK(33); PERMUTE3(a, h, f);
CHUNK(0); PERMUTE3(b, h, f);
CHUNK(42); PERMUTE3(b, h, d);
CHUNK(0); PERMUTE3(b, h, e);
CHUNK(33); PERMUTE3(a, h, e);
} while (--iters > 0);
while (len >= 40) {
CHUNK(29);
e ^= Rotate(a, 20);
h += Rotate(b, 30);
g ^= Rotate(c, 40);
f += Rotate(d, 34);
PERMUTE3(c, h, g);
len -= 40;
}
if (len > 0) {
s = s + len - 40;
CHUNK(33);
e ^= Rotate(a, 43);
h += Rotate(b, 42);
g ^= Rotate(c, 41);
f += Rotate(d, 40);
}
result[0] ^= h;
result[1] ^= g;
g += h;
a = HashLen16(a, g + z);
x += y << 32;
b += x;
c = HashLen16(c, z) + h;
d = HashLen16(d, e + result[0]);
g += e;
h += HashLen16(x, f);
e = HashLen16(a, d) + g;
z = HashLen16(b, c) + a;
y = HashLen16(g, h) + c;
result[0] = e + z + y + x;
a = ShiftMix((a + y) * k0) * k0 + b;
result[1] += a + result[0];
a = ShiftMix(a * k0) * k0 + c;
result[2] = a + result[1];
a = ShiftMix((a + e) * k0) * k0;
result[3] = a + result[2];
}
// Requires len < 240.
static void CityHashCrc256Short(const char *s, size_t len, uint64 *result) {
char buf[240];
memcpy(buf, s, len);
memset(buf + len, 0, 240 - len);
CityHashCrc256Long(buf, 240, ~static_cast<uint32>(len), result);
}
void CityHashCrc256(const char *s, size_t len, uint64 *result) {
if (LIKELY(len >= 240)) {
CityHashCrc256Long(s, len, 0, result);
} else {
CityHashCrc256Short(s, len, result);
}
}
uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed) {
if (len <= 900) {
return CityHash128WithSeed(s, len, seed);
} else {
uint64 result[4];
CityHashCrc256(s, len, result);
uint64 u = Uint128High64(seed) + result[0];
uint64 v = Uint128Low64(seed) + result[1];
return uint128(HashLen16(u, v + result[2]),
HashLen16(Rotate(v, 32), u * k0 + result[3]));
}
}
uint128 CityHashCrc128(const char *s, size_t len) {
if (len <= 900) {
return CityHash128(s, len);
} else {
uint64 result[4];
CityHashCrc256(s, len, result);
return uint128(result[2], result[3]);
}
}
#endif
+112
View File
@@ -0,0 +1,112 @@
// Copyright (c) 2011 Google, Inc.
//
// 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.
//
// CityHash, by Geoff Pike and Jyrki Alakuijala
//
// http://code.google.com/p/cityhash/
//
// This file provides a few functions for hashing strings. All of them are
// high-quality functions in the sense that they pass standard tests such
// as Austin Appleby's SMHasher. They are also fast.
//
// For 64-bit x86 code, on short strings, we don't know of anything faster than
// CityHash64 that is of comparable quality. We believe our nearest competitor
// is Murmur3. For 64-bit x86 code, CityHash64 is an excellent choice for hash
// tables and most other hashing (excluding cryptography).
//
// For 64-bit x86 code, on long strings, the picture is more complicated.
// On many recent Intel CPUs, such as Nehalem, Westmere, Sandy Bridge, etc.,
// CityHashCrc128 appears to be faster than all competitors of comparable
// quality. CityHash128 is also good but not quite as fast. We believe our
// nearest competitor is Bob Jenkins' Spooky. We don't have great data for
// other 64-bit CPUs, but for long strings we know that Spooky is slightly
// faster than CityHash on some relatively recent AMD x86-64 CPUs, for example.
// Note that CityHashCrc128 is declared in citycrc.h.
//
// For 32-bit x86 code, we don't know of anything faster than CityHash32 that
// is of comparable quality. We believe our nearest competitor is Murmur3A.
// (On 64-bit CPUs, it is typically faster to use the other CityHash variants.)
//
// Functions in the CityHash family are not suitable for cryptography.
//
// Please see CityHash's README file for more details on our performance
// measurements and so on.
//
// WARNING: This code has been only lightly tested on big-endian platforms!
// It is known to work well on little-endian platforms that have a small penalty
// for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs.
// It should work on all 32-bit and 64-bit platforms that allow unaligned reads;
// bug reports are welcome.
//
// By the way, for some hash functions, given strings a and b, the hash
// of a+b is easily derived from the hashes of a and b. This property
// doesn't hold for any hash functions in this file.
#ifndef CITY_HASH_H_
#define CITY_HASH_H_
#include <stdlib.h> // for size_t.
#include <stdint.h>
#include <utility>
typedef uint8_t uint8;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef std::pair<uint64, uint64> uint128;
inline uint64 Uint128Low64(const uint128& x) { return x.first; }
inline uint64 Uint128High64(const uint128& x) { return x.second; }
// Hash function for a byte array.
uint64 CityHash64(const char *buf, size_t len);
// Hash function for a byte array. For convenience, a 64-bit seed is also
// hashed into the result.
uint64 CityHash64WithSeed(const char *buf, size_t len, uint64 seed);
// Hash function for a byte array. For convenience, two seeds are also
// hashed into the result.
uint64 CityHash64WithSeeds(const char *buf, size_t len,
uint64 seed0, uint64 seed1);
// Hash function for a byte array.
uint128 CityHash128(const char *s, size_t len);
// Hash function for a byte array. For convenience, a 128-bit seed is also
// hashed into the result.
uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed);
// Hash function for a byte array. Most useful in 32-bit binaries.
uint32 CityHash32(const char *buf, size_t len);
// Hash 128 input bits down to 64 bits of output.
// This is intended to be a reasonably good hash function.
inline uint64 Hash128to64(const uint128& x) {
// Murmur-inspired hashing.
const uint64 kMul = 0x9ddfea08eb382d69ULL;
uint64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul;
a ^= (a >> 47);
uint64 b = (Uint128High64(x) ^ a) * kMul;
b ^= (b >> 47);
b *= kMul;
return b;
}
#endif // CITY_HASH_H_
+43
View File
@@ -0,0 +1,43 @@
// Copyright (c) 2011 Google, Inc.
//
// 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.
//
// CityHash, by Geoff Pike and Jyrki Alakuijala
//
// This file declares the subset of the CityHash functions that require
// _mm_crc32_u64(). See the CityHash README for details.
//
// Functions in the CityHash family are not suitable for cryptography.
#ifndef CITY_HASH_CRC_H_
#define CITY_HASH_CRC_H_
#include "city.h"
// Hash function for a byte array.
uint128 CityHashCrc128(const char *s, size_t len);
// Hash function for a byte array. For convenience, a 128-bit seed is also
// hashed into the result.
uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed);
// Hash function for a byte array. Sets result[0] ... result[3].
void CityHashCrc256(const char *s, size_t len, uint64 *result);
#endif // CITY_HASH_CRC_H_
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+319
View File
@@ -0,0 +1,319 @@
// jpgd.h - C++ class for JPEG decompression.
// Public domain, Rich Geldreich <richgel99@gmail.com>
#ifndef JPEG_DECODER_H
#define JPEG_DECODER_H
#include <stdlib.h>
#include <stdio.h>
#include <setjmp.h>
#ifdef _MSC_VER
#define JPGD_NORETURN __declspec(noreturn)
#elif defined(__GNUC__)
#define JPGD_NORETURN __attribute__ ((noreturn))
#else
#define JPGD_NORETURN
#endif
namespace jpgd
{
typedef unsigned char uint8;
typedef signed short int16;
typedef unsigned short uint16;
typedef unsigned int uint;
typedef signed int int32;
// Loads a JPEG image from a memory buffer or a file.
// req_comps can be 1 (grayscale), 3 (RGB), or 4 (RGBA).
// On return, width/height will be set to the image's dimensions, and actual_comps will be set to the either 1 (grayscale) or 3 (RGB).
// Notes: For more control over where and how the source data is read, see the decompress_jpeg_image_from_stream() function below, or call the jpeg_decoder class directly.
// Requesting a 8 or 32bpp image is currently a little faster than 24bpp because the jpeg_decoder class itself currently always unpacks to either 8 or 32bpp.
unsigned char *decompress_jpeg_image_from_memory(const unsigned char *pSrc_data, int src_data_size, int *width, int *height, int *actual_comps, int req_comps);
unsigned char *decompress_jpeg_image_from_file(const char *pSrc_filename, int *width, int *height, int *actual_comps, int req_comps);
// Success/failure error codes.
enum jpgd_status
{
JPGD_SUCCESS = 0, JPGD_FAILED = -1, JPGD_DONE = 1,
JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE,
JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS,
JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH,
JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMITIC_SUPPORT, JPGD_UNEXPECTED_MARKER,
JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS,
JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE,
JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER, JPGD_ASSERTION_ERROR,
JPGD_BAD_SOS_SPECTRAL, JPGD_BAD_SOS_SUCCESSIVE, JPGD_STREAM_READ, JPGD_NOTENOUGHMEM
};
// Input stream interface.
// Derive from this class to read input data from sources other than files or memory. Set m_eof_flag to true when no more data is available.
// The decoder is rather greedy: it will keep on calling this method until its internal input buffer is full, or until the EOF flag is set.
// It the input stream contains data after the JPEG stream's EOI (end of image) marker it will probably be pulled into the internal buffer.
// Call the get_total_bytes_read() method to determine the actual size of the JPEG stream after successful decoding.
class jpeg_decoder_stream
{
public:
jpeg_decoder_stream() { }
virtual ~jpeg_decoder_stream() { }
// The read() method is called when the internal input buffer is empty.
// Parameters:
// pBuf - input buffer
// max_bytes_to_read - maximum bytes that can be written to pBuf
// pEOF_flag - set this to true if at end of stream (no more bytes remaining)
// Returns -1 on error, otherwise return the number of bytes actually written to the buffer (which may be 0).
// Notes: This method will be called in a loop until you set *pEOF_flag to true or the internal buffer is full.
virtual int read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag) = 0;
};
// stdio FILE stream class.
class jpeg_decoder_file_stream : public jpeg_decoder_stream
{
jpeg_decoder_file_stream(const jpeg_decoder_file_stream &);
jpeg_decoder_file_stream &operator =(const jpeg_decoder_file_stream &);
FILE *m_pFile;
bool m_eof_flag, m_error_flag;
public:
jpeg_decoder_file_stream();
virtual ~jpeg_decoder_file_stream();
bool open(const char *Pfilename);
void close();
virtual int read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag);
};
// Memory stream class.
class jpeg_decoder_mem_stream : public jpeg_decoder_stream
{
const uint8 *m_pSrc_data;
uint m_ofs, m_size;
public:
jpeg_decoder_mem_stream() : m_pSrc_data(NULL), m_ofs(0), m_size(0) { }
jpeg_decoder_mem_stream(const uint8 *pSrc_data, uint size) : m_pSrc_data(pSrc_data), m_ofs(0), m_size(size) { }
virtual ~jpeg_decoder_mem_stream() { }
bool open(const uint8 *pSrc_data, uint size);
void close() { m_pSrc_data = NULL; m_ofs = 0; m_size = 0; }
virtual int read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag);
};
// Loads JPEG file from a jpeg_decoder_stream.
unsigned char *decompress_jpeg_image_from_stream(jpeg_decoder_stream *pStream, int *width, int *height, int *actual_comps, int req_comps);
enum
{
JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4,
JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 8192, JPGD_MAX_HEIGHT = 16384, JPGD_MAX_WIDTH = 16384
};
typedef int16 jpgd_quant_t;
typedef int16 jpgd_block_t;
class jpeg_decoder
{
public:
// Call get_error_code() after constructing to determine if the stream is valid or not. You may call the get_width(), get_height(), etc.
// methods after the constructor is called. You may then either destruct the object, or begin decoding the image by calling begin_decoding(), then decode() on each scanline.
jpeg_decoder(jpeg_decoder_stream *pStream);
~jpeg_decoder();
// Call this method after constructing the object to begin decompression.
// If JPGD_SUCCESS is returned you may then call decode() on each scanline.
int begin_decoding();
// Returns the next scan line.
// For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1).
// Otherwise, it will always point to a buffer containing 32-bit RGBA pixels (A will always be 255, and get_bytes_per_pixel() will return 4).
// Returns JPGD_SUCCESS if a scan line has been returned.
// Returns JPGD_DONE if all scan lines have been returned.
// Returns JPGD_FAILED if an error occurred. Call get_error_code() for a more info.
int decode(const void** pScan_line, uint* pScan_line_len);
inline jpgd_status get_error_code() const { return m_error_code; }
inline int get_width() const { return m_image_x_size; }
inline int get_height() const { return m_image_y_size; }
inline int get_num_components() const { return m_comps_in_frame; }
inline int get_bytes_per_pixel() const { return m_dest_bytes_per_pixel; }
inline int get_bytes_per_scan_line() const { return m_image_x_size * get_bytes_per_pixel(); }
// Returns the total number of bytes actually consumed by the decoder (which should equal the actual size of the JPEG file).
inline int get_total_bytes_read() const { return m_total_bytes_read; }
private:
jpeg_decoder(const jpeg_decoder &);
jpeg_decoder &operator =(const jpeg_decoder &);
typedef void (*pDecode_block_func)(jpeg_decoder *, int, int, int);
struct huff_tables
{
bool ac_table;
uint look_up[256];
uint look_up2[256];
uint8 code_size[256];
uint tree[512];
};
struct coeff_buf
{
uint8 *pData;
int block_num_x, block_num_y;
int block_len_x, block_len_y;
int block_size;
};
struct mem_block
{
mem_block *m_pNext;
size_t m_used_count;
size_t m_size;
char m_data[1];
};
jmp_buf m_jmp_state;
mem_block *m_pMem_blocks;
int m_image_x_size;
int m_image_y_size;
jpeg_decoder_stream *m_pStream;
int m_progressive_flag;
uint8 m_huff_ac[JPGD_MAX_HUFF_TABLES];
uint8* m_huff_num[JPGD_MAX_HUFF_TABLES]; // pointer to number of Huffman codes per bit size
uint8* m_huff_val[JPGD_MAX_HUFF_TABLES]; // pointer to Huffman codes per bit size
jpgd_quant_t* m_quant[JPGD_MAX_QUANT_TABLES]; // pointer to quantization tables
int m_scan_type; // Gray, Yh1v1, Yh1v2, Yh2v1, Yh2v2 (CMYK111, CMYK4114 no longer supported)
int m_comps_in_frame; // # of components in frame
int m_comp_h_samp[JPGD_MAX_COMPONENTS]; // component's horizontal sampling factor
int m_comp_v_samp[JPGD_MAX_COMPONENTS]; // component's vertical sampling factor
int m_comp_quant[JPGD_MAX_COMPONENTS]; // component's quantization table selector
int m_comp_ident[JPGD_MAX_COMPONENTS]; // component's ID
int m_comp_h_blocks[JPGD_MAX_COMPONENTS];
int m_comp_v_blocks[JPGD_MAX_COMPONENTS];
int m_comps_in_scan; // # of components in scan
int m_comp_list[JPGD_MAX_COMPS_IN_SCAN]; // components in this scan
int m_comp_dc_tab[JPGD_MAX_COMPONENTS]; // component's DC Huffman coding table selector
int m_comp_ac_tab[JPGD_MAX_COMPONENTS]; // component's AC Huffman coding table selector
int m_spectral_start; // spectral selection start
int m_spectral_end; // spectral selection end
int m_successive_low; // successive approximation low
int m_successive_high; // successive approximation high
int m_max_mcu_x_size; // MCU's max. X size in pixels
int m_max_mcu_y_size; // MCU's max. Y size in pixels
int m_blocks_per_mcu;
int m_max_blocks_per_row;
int m_mcus_per_row, m_mcus_per_col;
int m_mcu_org[JPGD_MAX_BLOCKS_PER_MCU];
int m_total_lines_left; // total # lines left in image
int m_mcu_lines_left; // total # lines left in this MCU
int m_real_dest_bytes_per_scan_line;
int m_dest_bytes_per_scan_line; // rounded up
int m_dest_bytes_per_pixel; // 4 (RGB) or 1 (Y)
huff_tables* m_pHuff_tabs[JPGD_MAX_HUFF_TABLES];
coeff_buf* m_dc_coeffs[JPGD_MAX_COMPONENTS];
coeff_buf* m_ac_coeffs[JPGD_MAX_COMPONENTS];
int m_eob_run;
int m_block_y_mcu[JPGD_MAX_COMPONENTS];
uint8* m_pIn_buf_ofs;
int m_in_buf_left;
int m_tem_flag;
bool m_eof_flag;
uint8 m_in_buf_pad_start[128];
uint8 m_in_buf[JPGD_IN_BUF_SIZE + 128];
uint8 m_in_buf_pad_end[128];
int m_bits_left;
uint m_bit_buf;
int m_restart_interval;
int m_restarts_left;
int m_next_restart_num;
int m_max_mcus_per_row;
int m_max_blocks_per_mcu;
int m_expanded_blocks_per_mcu;
int m_expanded_blocks_per_row;
int m_expanded_blocks_per_component;
bool m_freq_domain_chroma_upsample;
int m_max_mcus_per_col;
uint m_last_dc_val[JPGD_MAX_COMPONENTS];
jpgd_block_t* m_pMCU_coefficients;
int m_mcu_block_max_zag[JPGD_MAX_BLOCKS_PER_MCU];
uint8* m_pSample_buf;
int m_crr[256];
int m_cbb[256];
int m_crg[256];
int m_cbg[256];
uint8* m_pScan_line_0;
uint8* m_pScan_line_1;
jpgd_status m_error_code;
bool m_ready_flag;
int m_total_bytes_read;
void free_all_blocks();
JPGD_NORETURN void stop_decoding(jpgd_status status);
void *alloc(size_t n, bool zero = false);
void word_clear(void *p, uint16 c, uint n);
void prep_in_buffer();
void read_dht_marker();
void read_dqt_marker();
void read_sof_marker();
void skip_variable_marker();
void read_dri_marker();
void read_sos_marker();
int next_marker();
int process_markers();
void locate_soi_marker();
void locate_sof_marker();
int locate_sos_marker();
void init(jpeg_decoder_stream * pStream);
void create_look_ups();
void fix_in_buffer();
void transform_mcu(int mcu_row);
void transform_mcu_expand(int mcu_row);
coeff_buf* coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y);
inline jpgd_block_t *coeff_buf_getp(coeff_buf *cb, int block_x, int block_y);
void load_next_row();
void decode_next_row();
void make_huff_table(int index, huff_tables *pH);
void check_quant_tables();
void check_huff_tables();
void calc_mcu_block_order();
int init_scan();
void init_frame();
void process_restart();
void decode_scan(pDecode_block_func decode_block_func);
void init_progressive();
void init_sequential();
void decode_start();
void decode_init(jpeg_decoder_stream * pStream);
void H2V2Convert();
void H2V1Convert();
void H1V2Convert();
void H1V1Convert();
void gray_convert();
void expanded_convert();
void find_eoi();
inline uint get_char();
inline uint get_char(bool *pPadding_flag);
inline void stuff_char(uint8 q);
inline uint8 get_octet();
inline uint get_bits(int num_bits);
inline uint get_bits_no_markers(int numbits);
inline int huff_decode(huff_tables *pH);
inline int huff_decode(huff_tables *pH, int& extrabits);
static inline uint8 clamp(int i);
static void decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
static void decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
static void decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
static void decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
};
} // namespace jpgd
#endif // JPEG_DECODER_H
File diff suppressed because it is too large Load Diff
+169
View File
@@ -0,0 +1,169 @@
// jpge.h - C++ class for JPEG compression.
// Public domain, Rich Geldreich <richgel99@gmail.com>
// Alex Evans: Added RGBA support, linear memory allocator.
#ifndef JPEG_ENCODER_H
#define JPEG_ENCODER_H
namespace jpge
{
typedef unsigned char uint8;
typedef signed short int16;
typedef signed int int32;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef unsigned int uint;
// JPEG chroma subsampling factors. Y_ONLY (grayscale images) and H2V2 (color images) are the most common.
enum subsampling_t { Y_ONLY = 0, H1V1 = 1, H2V1 = 2, H2V2 = 3 };
// JPEG compression parameters structure.
struct params
{
inline params() : m_quality(85), m_subsampling(H2V2), m_no_chroma_discrim_flag(false), m_two_pass_flag(false) { }
inline bool check() const
{
if ((m_quality < 1) || (m_quality > 100)) return false;
if ((uint)m_subsampling > (uint)H2V2) return false;
return true;
}
// Quality: 1-100, higher is better. Typical values are around 50-95.
int m_quality;
// m_subsampling:
// 0 = Y (grayscale) only
// 1 = YCbCr, no subsampling (H1V1, YCbCr 1x1x1, 3 blocks per MCU)
// 2 = YCbCr, H2V1 subsampling (YCbCr 2x1x1, 4 blocks per MCU)
// 3 = YCbCr, H2V2 subsampling (YCbCr 4x1x1, 6 blocks per MCU-- very common)
subsampling_t m_subsampling;
// Disables CbCr discrimination - only intended for testing.
// If true, the Y quantization table is also used for the CbCr channels.
bool m_no_chroma_discrim_flag;
bool m_two_pass_flag;
};
// Writes JPEG image to a file.
// num_channels must be 1 (Y) or 3 (RGB), image pitch must be width*num_channels.
bool compress_image_to_jpeg_file(const char *pFilename, int width, int height, int num_channels, const uint8 *pImage_data, const params &comp_params = params());
// Writes JPEG image to memory buffer.
// On entry, buf_size is the size of the output buffer pointed at by pBuf, which should be at least ~1024 bytes.
// If return value is true, buf_size will be set to the size of the compressed data.
bool compress_image_to_jpeg_file_in_memory(void *pBuf, int &buf_size, int width, int height, int num_channels, const uint8 *pImage_data, const params &comp_params = params());
// Output stream abstract class - used by the jpeg_encoder class to write to the output stream.
// put_buf() is generally called with len==JPGE_OUT_BUF_SIZE bytes, but for headers it'll be called with smaller amounts.
class output_stream
{
public:
virtual ~output_stream() { };
virtual bool put_buf(const void* Pbuf, int len) = 0;
template<class T> inline bool put_obj(const T& obj) { return put_buf(&obj, sizeof(T)); }
};
// Lower level jpeg_encoder class - useful if more control is needed than the above helper functions.
class jpeg_encoder
{
public:
jpeg_encoder();
~jpeg_encoder();
// Initializes the compressor.
// pStream: The stream object to use for writing compressed data.
// params - Compression parameters structure, defined above.
// width, height - Image dimensions.
// channels - May be 1, or 3. 1 indicates grayscale, 3 indicates RGB source data.
// Returns false on out of memory or if a stream write fails.
bool init(output_stream *pStream, int width, int height, int src_channels, const params &comp_params = params());
const params &get_params() const { return m_params; }
// Deinitializes the compressor, freeing any allocated memory. May be called at any time.
void deinit();
uint get_total_passes() const { return m_params.m_two_pass_flag ? 2 : 1; }
inline uint get_cur_pass() { return m_pass_num; }
// Call this method with each source scanline.
// width * src_channels bytes per scanline is expected (RGB or Y format).
// You must call with NULL after all scanlines are processed to finish compression.
// Returns false on out of memory or if a stream write fails.
bool process_scanline(const void* pScanline);
private:
jpeg_encoder(const jpeg_encoder &);
jpeg_encoder &operator =(const jpeg_encoder &);
typedef int32 sample_array_t;
output_stream *m_pStream;
params m_params;
uint8 m_num_components;
uint8 m_comp_h_samp[3], m_comp_v_samp[3];
int m_image_x, m_image_y, m_image_bpp, m_image_bpl;
int m_image_x_mcu, m_image_y_mcu;
int m_image_bpl_xlt, m_image_bpl_mcu;
int m_mcus_per_row;
int m_mcu_x, m_mcu_y;
uint8 *m_mcu_lines[16];
uint8 m_mcu_y_ofs;
sample_array_t m_sample_array[64];
int16 m_coefficient_array[64];
int32 m_quantization_tables[2][64];
uint m_huff_codes[4][256];
uint8 m_huff_code_sizes[4][256];
uint8 m_huff_bits[4][17];
uint8 m_huff_val[4][256];
uint32 m_huff_count[4][256];
int m_last_dc_val[3];
enum { JPGE_OUT_BUF_SIZE = 2048 };
uint8 m_out_buf[JPGE_OUT_BUF_SIZE];
uint8 *m_pOut_buf;
uint m_out_buf_left;
uint32 m_bit_buffer;
uint m_bits_in;
uint8 m_pass_num;
bool m_all_stream_writes_succeeded;
void optimize_huffman_table(int table_num, int table_len);
void emit_byte(uint8 i);
void emit_word(uint i);
void emit_marker(int marker);
void emit_jfif_app0();
void emit_dqt();
void emit_sof();
void emit_dht(uint8 *bits, uint8 *val, int index, bool ac_flag);
void emit_dhts();
void emit_sos();
void emit_markers();
void compute_huffman_table(uint *codes, uint8 *code_sizes, uint8 *bits, uint8 *val);
void compute_quant_table(int32 *dst, int16 *src);
void adjust_quant_table(int32 *dst, int32 *src);
void first_pass_init();
bool second_pass_init();
bool jpg_open(int p_x_res, int p_y_res, int src_channels);
void load_block_8_8_grey(int x);
void load_block_8_8(int x, int y, int c);
void load_block_16_8(int x, int c);
void load_block_16_8_8(int x, int c);
void load_quantized_coefficients(int component_num);
void flush_output_buffer();
void put_bits(uint bits, uint len);
void code_coefficients_pass_one(int component_num);
void code_coefficients_pass_two(int component_num);
void code_block(int component_num);
void process_mcu_row();
bool terminate_pass_one();
bool terminate_pass_two();
bool process_end_of_image();
void load_mcu(const void* src);
void clear();
void init();
};
} // namespace jpge
#endif // JPEG_ENCODER
+1
View File
@@ -0,0 +1 @@
This is https://code.google.com/p/jpeg-compressor/ , public domain code.
+26
View File
@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 2.6)
add_library(png17
pngconf.h
pngdebug.h
png.c
png.h
pngerror.c
pngget.c
pnginfo.h
pnglibconf.h
pngmem.c
pngpread.c
pngpriv.h
pngread.c
pngrio.c
pngrtran.c
pngrutil.c
pngset.c
pngstruct.h
pngtest.c
pngtrans.c
pngwio.c
pngwrite.c
pngwtran.c
pngwutil.c)
+133
View File
@@ -0,0 +1,133 @@
/* arm_init.c - NEON optimised filter functions
*
* Copyright (c) 2014 Glenn Randers-Pehrson
* Written by Mans Rullgard, 2011.
* Last changed in libpng 1.6.10 [(PENDING RELEASE)]
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
/* Below, after checking __linux__, various non-C90 POSIX 1003.1 functions are
* called.
*/
#define _POSIX_SOURCE 1
#include "../pngpriv.h"
#ifdef PNG_READ_SUPPORTED
#if PNG_ARM_NEON_OPT > 0
#ifdef PNG_ARM_NEON_CHECK_SUPPORTED /* Do run-time checks */
/* WARNING: it is strongly recommended that you do not build libpng with
* run-time checks for CPU features if at all possible. In the case of the ARM
* NEON instructions there is no processor-specific way of detecting the
* presence of the required support, therefore run-time detection is extremely
* OS specific.
*
* You may set the macro PNG_ARM_NEON_FILE to the file name of file containing
* a fragment of C source code which defines the png_have_neon function. There
* are a number of implementations in contrib/arm-neon, but the only one that
* has partial support is contrib/arm-neon/linux.c - a generic Linux
* implementation which reads /proc/cpufino.
*/
#ifndef PNG_ARM_NEON_FILE
# ifdef __linux__
# define PNG_ARM_NEON_FILE "contrib/arm-neon/linux.c"
# endif
#endif
#ifdef PNG_ARM_NEON_FILE
#include <signal.h> /* for sig_atomic_t */
static int png_have_neon(png_structp png_ptr);
#include PNG_ARM_NEON_FILE
#else /* PNG_ARM_NEON_FILE */
# error "PNG_ARM_NEON_FILE undefined: no support for run-time ARM NEON checks"
#endif /* PNG_ARM_NEON_FILE */
#endif /* PNG_ARM_NEON_CHECK_SUPPORTED */
#ifndef PNG_ALIGNED_MEMORY_SUPPORTED
# error "ALIGNED_MEMORY is required; set: -DPNG_ALIGNED_MEMORY_SUPPORTED"
#endif
void
png_init_filter_functions_neon(png_structp pp, unsigned int bpp)
{
/* The switch statement is compiled in for ARM_NEON_API, the call to
* png_have_neon is compiled in for ARM_NEON_CHECK. If both are defined
* the check is only performed if the API has not set the NEON option on
* or off explicitly. In this case the check controls what happens.
*
* If the CHECK is not compiled in and the option is UNSET the behavior prior
* to 1.6.7 was to use the NEON code - this was a bug caused by having the
* wrong order of the 'ON' and 'default' cases. UNSET now defaults to OFF,
* as documented in png.h
*/
#ifdef PNG_ARM_NEON_API_SUPPORTED
switch ((pp->options >> PNG_ARM_NEON) & 3)
{
case PNG_OPTION_UNSET:
/* Allow the run-time check to execute if it has been enabled -
* thus both API and CHECK can be turned on. If it isn't supported
* this case will fall through to the 'default' below, which just
* returns.
*/
#endif /* PNG_ARM_NEON_API_SUPPORTED */
#ifdef PNG_ARM_NEON_CHECK_SUPPORTED
{
static volatile sig_atomic_t no_neon = -1; /* not checked */
if (no_neon < 0)
no_neon = !png_have_neon(pp);
if (no_neon)
return;
}
#ifdef PNG_ARM_NEON_API_SUPPORTED
break;
#endif
#endif /* PNG_ARM_NEON_CHECK_SUPPORTED */
#ifdef PNG_ARM_NEON_API_SUPPORTED
default: /* OFF or INVALID */
return;
case PNG_OPTION_ON:
/* Option turned on */
break;
}
#endif
/* IMPORTANT: any new external functions used here must be declared using
* PNG_INTERNAL_FUNCTION in ../pngpriv.h. This is required so that the
* 'prefix' option to configure works:
*
* ./configure --with-libpng-prefix=foobar_
*
* Verify you have got this right by running the above command, doing a build
* and examining pngprefix.h; it must contain a #define for every external
* function you add. (Notice that this happens automatically for the
* initialization function.)
*/
pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up_neon;
if (bpp == 3)
{
pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_neon;
pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_neon;
pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
png_read_filter_row_paeth3_neon;
}
else if (bpp == 4)
{
pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_neon;
pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_neon;
pp->read_filter[PNG_FILTER_VALUE_PAETH-1] =
png_read_filter_row_paeth4_neon;
}
}
#endif /* PNG_ARM_NEON_OPT > 0 */
#endif /* PNG_READ_SUPPORTED */
+246
View File
@@ -0,0 +1,246 @@
/* filter_neon.S - NEON optimised filter functions
*
* Copyright (c) 2013 Glenn Randers-Pehrson
* Written by Mans Rullgard, 2011.
* Last changed in libpng 1.6.8 [December 19, 2013]
*
* This code is released under the libpng license.
* For conditions of distribution and use, see the disclaimer
* and license in png.h
*/
/* This is required to get the symbol renames, which are #defines, and also
* includes the definition (or not) of PNG_ARM_NEON_OPT and
* PNG_ARM_NEON_IMPLEMENTATION.
*/
#define PNG_VERSION_INFO_ONLY
#include "../pngpriv.h"
#if defined(__linux__) && defined(__ELF__)
.section .note.GNU-stack,"",%progbits /* mark stack as non-executable */
#endif
/* Assembler NEON support - only works for 32-bit ARM (i.e. it does not work for
* ARM64). The code in arm/filter_neon_intrinsics.c supports ARM64, however it
* only works if -mfpu=neon is specified on the GCC command line. See pngpriv.h
* for the logic which sets PNG_USE_ARM_NEON_ASM:
*/
#if PNG_ARM_NEON_IMPLEMENTATION == 2 /* hand-coded assembler */
#ifdef PNG_READ_SUPPORTED
#if PNG_ARM_NEON_OPT > 0
#ifdef __ELF__
# define ELF
#else
# define ELF @
#endif
.arch armv7-a
.fpu neon
.macro func name, export=0
.macro endfunc
ELF .size \name, . - \name
.endfunc
.purgem endfunc
.endm
.text
.if \export
.global \name
.endif
ELF .type \name, STT_FUNC
.func \name
\name:
.endm
func png_read_filter_row_sub4_neon, export=1
ldr r3, [r0, #4] @ rowbytes
vmov.i8 d3, #0
1:
vld4.32 {d4[],d5[],d6[],d7[]}, [r1,:128]
vadd.u8 d0, d3, d4
vadd.u8 d1, d0, d5
vadd.u8 d2, d1, d6
vadd.u8 d3, d2, d7
vst4.32 {d0[0],d1[0],d2[0],d3[0]},[r1,:128]!
subs r3, r3, #16
bgt 1b
bx lr
endfunc
func png_read_filter_row_sub3_neon, export=1
ldr r3, [r0, #4] @ rowbytes
vmov.i8 d3, #0
mov r0, r1
mov r2, #3
mov r12, #12
vld1.8 {q11}, [r0], r12
1:
vext.8 d5, d22, d23, #3
vadd.u8 d0, d3, d22
vext.8 d6, d22, d23, #6
vadd.u8 d1, d0, d5
vext.8 d7, d23, d23, #1
vld1.8 {q11}, [r0], r12
vst1.32 {d0[0]}, [r1,:32], r2
vadd.u8 d2, d1, d6
vst1.32 {d1[0]}, [r1], r2
vadd.u8 d3, d2, d7
vst1.32 {d2[0]}, [r1], r2
vst1.32 {d3[0]}, [r1], r2
subs r3, r3, #12
bgt 1b
bx lr
endfunc
func png_read_filter_row_up_neon, export=1
ldr r3, [r0, #4] @ rowbytes
1:
vld1.8 {q0}, [r1,:128]
vld1.8 {q1}, [r2,:128]!
vadd.u8 q0, q0, q1
vst1.8 {q0}, [r1,:128]!
subs r3, r3, #16
bgt 1b
bx lr
endfunc
func png_read_filter_row_avg4_neon, export=1
ldr r12, [r0, #4] @ rowbytes
vmov.i8 d3, #0
1:
vld4.32 {d4[],d5[],d6[],d7[]}, [r1,:128]
vld4.32 {d16[],d17[],d18[],d19[]},[r2,:128]!
vhadd.u8 d0, d3, d16
vadd.u8 d0, d0, d4
vhadd.u8 d1, d0, d17
vadd.u8 d1, d1, d5
vhadd.u8 d2, d1, d18
vadd.u8 d2, d2, d6
vhadd.u8 d3, d2, d19
vadd.u8 d3, d3, d7
vst4.32 {d0[0],d1[0],d2[0],d3[0]},[r1,:128]!
subs r12, r12, #16
bgt 1b
bx lr
endfunc
func png_read_filter_row_avg3_neon, export=1
push {r4,lr}
ldr r12, [r0, #4] @ rowbytes
vmov.i8 d3, #0
mov r0, r1
mov r4, #3
mov lr, #12
vld1.8 {q11}, [r0], lr
1:
vld1.8 {q10}, [r2], lr
vext.8 d5, d22, d23, #3
vhadd.u8 d0, d3, d20
vext.8 d17, d20, d21, #3
vadd.u8 d0, d0, d22
vext.8 d6, d22, d23, #6
vhadd.u8 d1, d0, d17
vext.8 d18, d20, d21, #6
vadd.u8 d1, d1, d5
vext.8 d7, d23, d23, #1
vld1.8 {q11}, [r0], lr
vst1.32 {d0[0]}, [r1,:32], r4
vhadd.u8 d2, d1, d18
vst1.32 {d1[0]}, [r1], r4
vext.8 d19, d21, d21, #1
vadd.u8 d2, d2, d6
vhadd.u8 d3, d2, d19
vst1.32 {d2[0]}, [r1], r4
vadd.u8 d3, d3, d7
vst1.32 {d3[0]}, [r1], r4
subs r12, r12, #12
bgt 1b
pop {r4,pc}
endfunc
.macro paeth rx, ra, rb, rc
vaddl.u8 q12, \ra, \rb @ a + b
vaddl.u8 q15, \rc, \rc @ 2*c
vabdl.u8 q13, \rb, \rc @ pa
vabdl.u8 q14, \ra, \rc @ pb
vabd.u16 q15, q12, q15 @ pc
vcle.u16 q12, q13, q14 @ pa <= pb
vcle.u16 q13, q13, q15 @ pa <= pc
vcle.u16 q14, q14, q15 @ pb <= pc
vand q12, q12, q13 @ pa <= pb && pa <= pc
vmovn.u16 d28, q14
vmovn.u16 \rx, q12
vbsl d28, \rb, \rc
vbsl \rx, \ra, d28
.endm
func png_read_filter_row_paeth4_neon, export=1
ldr r12, [r0, #4] @ rowbytes
vmov.i8 d3, #0
vmov.i8 d20, #0
1:
vld4.32 {d4[],d5[],d6[],d7[]}, [r1,:128]
vld4.32 {d16[],d17[],d18[],d19[]},[r2,:128]!
paeth d0, d3, d16, d20
vadd.u8 d0, d0, d4
paeth d1, d0, d17, d16
vadd.u8 d1, d1, d5
paeth d2, d1, d18, d17
vadd.u8 d2, d2, d6
paeth d3, d2, d19, d18
vmov d20, d19
vadd.u8 d3, d3, d7
vst4.32 {d0[0],d1[0],d2[0],d3[0]},[r1,:128]!
subs r12, r12, #16
bgt 1b
bx lr
endfunc
func png_read_filter_row_paeth3_neon, export=1
push {r4,lr}
ldr r12, [r0, #4] @ rowbytes
vmov.i8 d3, #0
vmov.i8 d4, #0
mov r0, r1
mov r4, #3
mov lr, #12
vld1.8 {q11}, [r0], lr
1:
vld1.8 {q10}, [r2], lr
paeth d0, d3, d20, d4
vext.8 d5, d22, d23, #3
vadd.u8 d0, d0, d22
vext.8 d17, d20, d21, #3
paeth d1, d0, d17, d20
vst1.32 {d0[0]}, [r1,:32], r4
vext.8 d6, d22, d23, #6
vadd.u8 d1, d1, d5
vext.8 d18, d20, d21, #6
paeth d2, d1, d18, d17
vext.8 d7, d23, d23, #1
vld1.8 {q11}, [r0], lr
vst1.32 {d1[0]}, [r1], r4
vadd.u8 d2, d2, d6
vext.8 d19, d21, d21, #1
paeth d3, d2, d19, d18
vst1.32 {d2[0]}, [r1], r4
vmov d4, d19
vadd.u8 d3, d3, d7
vst1.32 {d3[0]}, [r1], r4
subs r12, r12, #12
bgt 1b
pop {r4,pc}
endfunc
#endif /* PNG_ARM_NEON_OPT > 0 */
#endif /* PNG_READ_SUPPORTED */
#endif /* PNG_ARM_NEON_IMPLEMENTATION == 2 (assembler) */

Some files were not shown because too many files have changed in this diff Show More