├── .gitignore
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── boat
├── src
│ └── main
│ │ ├── res
│ │ └── drawable
│ │ │ └── ic_boat.png
│ │ ├── jni
│ │ ├── CMakeLists.txt
│ │ ├── loadme
│ │ │ ├── CMakeLists.txt
│ │ │ └── loadme.c
│ │ └── boat
│ │ │ ├── CMakeLists.txt
│ │ │ ├── include
│ │ │ ├── boat.h
│ │ │ ├── boat_event.h
│ │ │ └── boat_keycodes.h
│ │ │ ├── boat_clipboard.c
│ │ │ ├── boat.c
│ │ │ ├── boat_internal.h
│ │ │ └── boat_event.c
│ │ ├── java
│ │ ├── cosine
│ │ │ └── boat
│ │ │ │ ├── LoadMe.java
│ │ │ │ ├── BoatTask.java
│ │ │ │ ├── BoatService.java
│ │ │ │ ├── BoatLib.java
│ │ │ │ ├── BoatScript.java
│ │ │ │ ├── BoatKeycodes.java
│ │ │ │ └── BoatActivity.java
│ │ └── com
│ │ │ └── mojang
│ │ │ └── minecraftpe
│ │ │ └── TextInputProxyEditTextbox.java
│ │ └── AndroidManifest.xml
└── build.gradle
├── sample
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable
│ │ │ ├── cursor.png
│ │ │ ├── control_button.xml
│ │ │ ├── control_button_normal.xml
│ │ │ └── control_button_pressed.xml
│ │ ├── values
│ │ │ ├── colors.xml
│ │ │ ├── styles.xml
│ │ │ └── strings.xml
│ │ └── layout
│ │ │ ├── launcher_layout.xml
│ │ │ └── minecraft_layout.xml
│ │ ├── java
│ │ └── cosine
│ │ │ └── boat
│ │ │ ├── VirglService.java
│ │ │ ├── LauncherActivity.java
│ │ │ ├── logcat
│ │ │ ├── LogcatUtils.java
│ │ │ ├── Logcat.java
│ │ │ └── LogcatService.java
│ │ │ └── MinecraftActivity.java
│ │ └── AndroidManifest.xml
└── build.gradle
├── .classpath
├── project.properties
├── gradle.properties
├── README-zh_CN.md
├── README.md
├── gradlew.bat
├── gradlew
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | build
3 | .cxx
4 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':boat', ':sample'
2 |
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AOF-Dev/Boat/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/boat/src/main/res/drawable/ic_boat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AOF-Dev/Boat/HEAD/boat/src/main/res/drawable/ic_boat.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/cursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AOF-Dev/Boat/HEAD/sample/src/main/res/drawable/cursor.png
--------------------------------------------------------------------------------
/boat/src/main/jni/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.4.1)
2 |
3 | # include libs
4 | ADD_SUBDIRECTORY(boat/)
5 | ADD_SUBDIRECTORY(loadme/)
6 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ff468ef5
4 |
5 |
--------------------------------------------------------------------------------
/boat/src/main/jni/loadme/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.4.1)
2 |
3 | # build loadme
4 |
5 | add_library(loadme SHARED loadme.c)
6 |
7 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -Wall -Werror")
8 | target_link_libraries(loadme dl log)
9 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 14 10:14:02 PST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/control_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/boat/src/main/jni/boat/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.4.1)
2 |
3 | # build libboat
4 | set(INCLUDE_DIR include/)
5 |
6 | add_library(boat SHARED boat.c boat_clipboard.c boat_event.c)
7 | include_directories(${INCLUDE_DIR})
8 |
9 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -Wall -Werror -DBUILD_BOAT")
10 | target_link_libraries(boat dl android)
11 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/control_button_normal.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable/control_button_pressed.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system use,
7 | # "build.properties", and override values to adapt the script to your
8 | # project structure.
9 |
10 | # Project target.
11 | target=android-19
12 |
--------------------------------------------------------------------------------
/boat/src/main/jni/boat/include/boat.h:
--------------------------------------------------------------------------------
1 | #ifndef BOAT_H
2 | #define BOAT_H
3 |
4 | #include
5 | #include
6 |
7 | ANativeWindow* boatGetNativeWindow(void);
8 | int boatWaitForEvent(int timeout);
9 | int boatPollEvent(BoatEvent* event);
10 | int boatGetEventFd(void);
11 | void boatSetCursorMode(int mode);
12 | void boatSetPrimaryClipString(const char* string);
13 | const char* boatGetPrimaryClipString(void);
14 |
15 | #endif
16 |
17 |
--------------------------------------------------------------------------------
/sample/src/main/java/cosine/boat/VirglService.java:
--------------------------------------------------------------------------------
1 | package cosine.boat;
2 |
3 | import android.app.Service;
4 | import android.content.Intent;
5 | import android.app.Notification;
6 | import android.content.Context;
7 | import android.os.IBinder;
8 | import android.app.PendingIntent;
9 | import java.util.Map;
10 | import java.util.HashMap;
11 |
12 | import cosine.boat.BoatService;
13 | import cosine.boat.BoatTask;
14 |
15 | public class VirglService extends BoatService
16 | {
17 |
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/boat/src/main/java/cosine/boat/LoadMe.java:
--------------------------------------------------------------------------------
1 | package cosine.boat;
2 |
3 | public class LoadMe {
4 |
5 | public static native int chdir(String path);
6 | public static native void redirectStdio(String file);
7 | public static native void setenv(String name, String value);
8 | public static native int dlopen(String name);
9 | public static native void patchLinker();
10 | public static native int dlexec(String[] args);
11 |
12 | static {
13 | System.loadLibrary("loadme");
14 | }
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/boat/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
10 |
12 |
14 |
16 |
17 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/launcher_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion '29.0.2'
6 | ndkVersion '21.1.6352462'
7 |
8 | defaultConfig {
9 | applicationId "cosine.boat"
10 | minSdkVersion 21
11 | targetSdkVersion 21
12 | versionCode 28
13 | versionName "0.5.4"
14 | }
15 |
16 | buildTypes {
17 | release {
18 | minifyEnabled = false
19 | }
20 | }
21 |
22 | compileOptions {
23 | sourceCompatibility JavaVersion.VERSION_1_8
24 | targetCompatibility JavaVersion.VERSION_1_8
25 | }
26 | }
27 |
28 | dependencies {
29 | implementation fileTree(dir: 'libs', include: ['*.jar'])
30 | implementation project(path: ':boat')
31 | }
32 |
--------------------------------------------------------------------------------
/boat/src/main/jni/boat/boat_clipboard.c:
--------------------------------------------------------------------------------
1 | #include "boat_internal.h"
2 |
3 | void boatSetPrimaryClipString(const char* string) {
4 | PrepareBoatLibJNI();
5 | CallBoatLibJNIFunc( , Void, setPrimaryClipString, "(Ljava/lang/String;)V", (*env)->NewStringUTF(env, string));
6 | }
7 |
8 | const char* boatGetPrimaryClipString() {
9 | PrepareBoatLibJNI();
10 | if (mBoat.clipboard_string != NULL) {
11 | free(mBoat.clipboard_string);
12 | mBoat.clipboard_string = NULL;
13 | }
14 | CallBoatLibJNIFunc(jstring clipstr = , Object, getPrimaryClipString, "()Ljava/lang/String;");
15 | const char* string = NULL;
16 | if (clipstr != NULL) {
17 | string = (*env)->GetStringUTFChars(env, clipstr, NULL);
18 | if (string != NULL) {
19 | mBoat.clipboard_string = strdup(string);
20 | }
21 | }
22 | return mBoat.clipboard_string;
23 | }
24 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
10 |
11 |
18 |
19 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Boat
4 | Play Minecraft
5 | Excute
6 | Install
7 |
8 | 3rd
9 | Cmd
10 | F3
11 | IME
12 |
13 | Pri
14 | Sec
15 | Sft
16 | Inv
17 | ■
18 | ▲
19 | ◀
20 | ▶
21 | ▼
22 | Esc
23 | Stop
24 | VirGL
25 |
26 |
27 |
--------------------------------------------------------------------------------
/README-zh_CN.md:
--------------------------------------------------------------------------------
1 | # Boat - 用于移植程序到 Android
2 | [English](./README.md)
3 | ## 目录
4 | - [简介](#简介)
5 | - [构建](#构建)
6 | - [相关项目](#相关项目)
7 | - [许可](#许可)
8 |
9 | ## 简介
10 | Boat 提供一个在 Android 上运行一类图形程序的环境,不是模拟器。它最早衍生自 zhuowei 的 [Boarwalk](https://github.com/zhuowei/Boardwalk)。
11 | 最初完全是为了运行 Minecraft Java 版,目前也主要针对 Minecraft Java 版做有关测试。但是,*Boat 不是 Minecraft 启动器*,目前我们还有
12 | Xboat X server 等项目。请参看 [相关项目](#相关项目)。
13 |
14 | ## 构建
15 | 通过 gradle 可以轻松构建 Android library 的 aar 包,不过也可以直接使用源码。请注意,仅支持 aarch64。原来对 32位 arm 的支持已经放弃,理论上
16 | 还可以使用不过我们不再测试。
17 |
18 | `export ANDROID_SDK_ROOT=你的 Android SDK 路径`
19 |
20 | `./gradlew assembleDebug`
21 |
22 | ## 相关项目
23 | - [MCinaBox](https://github.com/AOF-Dev/MCinaBox)。 一个用于 Android 的 Minecraft Java 版启动器,基于 Boat 提供 Minecraft 运行环境。
24 | - [xserver-xboat](https://github.com/AOF-Dev/xserver-xboat)。 一个用于 Android 的 X 显示服务器,从 X.org 的 Xephyr 移植而来。
25 | - [GLFW](https://github.com/AOF-Dev/MCinaBox/glfw-boat)。 在 Android 上使用 GLFW 创建和管理 OpenGL 上下文!大部分 API 可以使用!
26 |
27 | ## 许可
28 | 本项目以 [GPL v2.0](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html) 授权。
29 |
--------------------------------------------------------------------------------
/boat/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 29
5 | buildToolsVersion '29.0.2'
6 | ndkVersion '21.1.6352462'
7 |
8 | defaultConfig {
9 | minSdkVersion 21
10 | targetSdkVersion 21
11 | versionCode 28
12 | versionName "0.5.4"
13 |
14 | externalNativeBuild {
15 | cmake {
16 | abiFilters 'arm64-v8a'
17 | arguments '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_static'
18 | }
19 | }
20 | }
21 | externalNativeBuild {
22 | cmake {
23 | path 'src/main/jni/CMakeLists.txt'
24 | }
25 | }
26 |
27 | buildTypes {
28 | release {
29 | minifyEnabled = false
30 | }
31 | }
32 |
33 | compileOptions {
34 | sourceCompatibility JavaVersion.VERSION_1_8
35 | targetCompatibility JavaVersion.VERSION_1_8
36 | }
37 | }
38 |
39 | dependencies {
40 | implementation fileTree(dir: 'libs', include: ['*.jar'])
41 | implementation 'com.google.code.gson:gson:2.8.2'
42 | }
43 |
--------------------------------------------------------------------------------
/boat/src/main/java/cosine/boat/BoatTask.java:
--------------------------------------------------------------------------------
1 | package cosine.boat;
2 |
3 | import java.util.Map;
4 | import java.util.List;
5 | import java.io.IOException;
6 |
7 | public class BoatTask implements Runnable {
8 |
9 | public static final String BOAT_TASK_SCRIPT_PATH = "boat_script_path";
10 |
11 | private BoatScript script;
12 | private Thread thread;
13 |
14 | public void beforeExecute() {
15 |
16 | }
17 |
18 | public void afterExecute() {
19 |
20 | }
21 |
22 | public void startTask() {
23 | this.thread.start();
24 | }
25 |
26 | @Override
27 | public void run() {
28 | beforeExecute();
29 | this.script.execute();
30 | afterExecute();
31 | }
32 |
33 | public BoatTask(Map envvars, String scriptPath) {
34 | try {
35 | List cmds = BoatScript.parseJson(scriptPath);
36 | this.script = new BoatScript(envvars, false, cmds, scriptPath);
37 | this.thread = new Thread(this);
38 | }
39 | catch (IOException e) {
40 | e.printStackTrace();
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/boat/src/main/jni/boat/boat.c:
--------------------------------------------------------------------------------
1 | #include "boat_internal.h"
2 |
3 | #include
4 | #include
5 |
6 | BoatInternal mBoat;
7 |
8 | ANativeWindow* boatGetNativeWindow() {
9 | return mBoat.window;
10 | }
11 |
12 | JNIEXPORT void JNICALL Java_cosine_boat_BoatLib_setBoatNativeWindow(JNIEnv* env, jclass clazz, jobject surface) {
13 | mBoat.window = ANativeWindow_fromSurface(env, surface);
14 | BOAT_INTERNAL_LOG("setBoatNativeWindow : %p", mBoat.window);
15 | }
16 |
17 | JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
18 | memset(&mBoat, 0, sizeof(mBoat));
19 | mBoat.android_jvm = vm;
20 | JNIEnv* env = 0;
21 | jint result = (*mBoat.android_jvm)->AttachCurrentThread(mBoat.android_jvm, &env, 0);
22 | if (result != JNI_OK || env == 0) {
23 | BOAT_INTERNAL_LOG("Failed to attach thread to JavaVM.");
24 | abort();
25 | }
26 | jclass class_BoatLib = (*env)->FindClass(env, "cosine/boat/BoatLib");
27 | if (class_BoatLib == 0) {
28 | BOAT_INTERNAL_LOG("Failed to find class: cosine/boat/BoatLib.");
29 | abort();
30 | }
31 | mBoat.class_BoatLib = (jclass)(*env)->NewGlobalRef(env, class_BoatLib);
32 | return JNI_VERSION_1_2;
33 | }
34 |
--------------------------------------------------------------------------------
/boat/src/main/jni/boat/include/boat_event.h:
--------------------------------------------------------------------------------
1 | #ifndef BOAT_EVENT_H
2 | #define BOAT_EVENT_H
3 |
4 | #define KeyPress 2
5 | #define KeyRelease 3
6 | #define ButtonPress 4
7 | #define ButtonRelease 5
8 | #define MotionNotify 6
9 | #define ConfigureNotify 22
10 | #define BoatMessage 37
11 |
12 | #define Button1 1
13 | #define Button2 2
14 | #define Button3 3
15 | #define Button4 4
16 | #define Button5 5
17 | #define Button6 6
18 | #define Button7 7
19 |
20 | #define CursorEnabled 1
21 | #define CursorDisabled 0
22 |
23 | #define ShiftMask (1<<0)
24 | #define LockMask (1<<1)
25 | #define ControlMask (1<<2)
26 | #define Mod1Mask (1<<3)
27 | #define Mod2Mask (1<<4)
28 | #define Mod3Mask (1<<5)
29 | #define Mod4Mask (1<<6)
30 | #define Mod5Mask (1<<7)
31 |
32 | #define CloseRequest 0
33 |
34 | typedef struct {
35 | long long time;
36 | int type;
37 | int state;
38 | int button;
39 | int message;
40 | int x;
41 | int y;
42 | int keycode;
43 | int keychar;
44 | int width;
45 | int height;
46 | } BoatEvent;
47 |
48 | #endif // BOAT_EVENT_H
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Boat - Helper library for Android
2 | [中文](./README-zh_CN.md)
3 | ## Contents
4 | - [Introduction](#Introduction)
5 | - [Building](#Building)
6 | - [Related Projects](#Related-Projects)
7 | - [License](#License)
8 |
9 | ## Introducion
10 | Boat provides an environment to run some interesting programs on Android. It evolves from zhuowei's [Boarwalk](https://github.com/zhuowei/Boardwalk).
11 | Initilaly, Boat is just for playing Minecraft Java Edition on Android. Currently, we still test Boat with Minecraft JE. However,
12 | *Boat is NOT a Minecraft launcher*. Please have a look on [Related Projects](#Related-Projects).
13 |
14 | ## Building
15 | It is very simple to build the aar package. *NOTE*,only support aarch64。32bit arm has been abandoned and is never tested.
16 |
17 | `export ANDROID_SDK_ROOT=Your Android SDK path`
18 |
19 | `./gradlew assembleDebug`
20 |
21 | ## Related-Projects
22 | - [MCinaBox](https://github.com/AOF-Dev/MCinaBox). A Minecraft Java Edition Launcher on Android. Using Boat to provide runtime environment.
23 | - [xserver-xboat](https://github.com/AOF-Dev/xserver-xboat)。 X server on Android, ported from Xephyr.
24 | - [GLFW](https://github.com/AOF-Dev/MCinaBox/glfw-boat)。 Create and manage OpenGL context with GLFW! Most of APIs work!
25 |
26 | ## License
27 | This app project is distributed under [GPL v2.0](http://www.gnu.org/licenses/old-licenses/gpl-2.0.html).
28 |
--------------------------------------------------------------------------------
/sample/src/main/java/cosine/boat/LauncherActivity.java:
--------------------------------------------------------------------------------
1 | package cosine.boat;
2 |
3 | import android.app.Activity;
4 | import android.view.View;
5 | import android.widget.Button;
6 | import android.os.Bundle;
7 | import cosine.boat.logcat.Logcat;
8 | import cosine.boat.logcat.LogcatService;
9 | import android.content.Intent;
10 | import android.widget.TextView;
11 |
12 | public class LauncherActivity extends Activity implements View.OnClickListener {
13 | public Button minecraftButton;
14 | public Button virglButton;
15 |
16 | public void onCreate(Bundle savedInstance) {
17 | super.onCreate(savedInstance);
18 |
19 | final String logPath = "/mnt/sdcard/boat/log.txt";
20 | Logcat.initializeOutOfProcess(this, logPath, LogcatService.class);
21 |
22 | setContentView(R.layout.launcher_layout);
23 | this.minecraftButton = (Button) findViewById(R.id.launcher_minecraft_button);
24 | this.minecraftButton.setOnClickListener(this);
25 | this.virglButton = (Button) findViewById(R.id.launcher_virgl_button);
26 | this.virglButton.setOnClickListener(this);
27 | }
28 |
29 | // OnClickListener
30 | public void onClick(View v) {
31 | if (v == this.minecraftButton) {
32 | Intent i = new Intent(this, MinecraftActivity.class);
33 | i.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
34 | i.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
35 | i.putExtra(BoatTask.BOAT_TASK_SCRIPT_PATH, "/sdcard/boat/minecraft.json");
36 | this.startActivity(i);
37 | }
38 | else if (v == this.virglButton) {
39 | Intent i = new Intent(this, VirglService.class);
40 | i.putExtra(BoatTask.BOAT_TASK_SCRIPT_PATH, "/sdcard/boat/virgl_test_server.json");
41 | this.startService(i);
42 | }
43 | }
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
16 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
40 |
41 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/boat/src/main/jni/boat/boat_internal.h:
--------------------------------------------------------------------------------
1 | #ifndef BOAT_INTERNAL_H
2 | #define BOAT_INTERNAL_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include
13 | #include
14 |
15 | typedef struct _QueueElement {
16 | struct _QueueElement* next;
17 | BoatEvent event;
18 | } QueueElement;
19 |
20 | typedef struct {
21 | int count;
22 | int capacity;
23 | QueueElement* head;
24 | QueueElement* tail;
25 | } EventQueue;
26 |
27 | typedef struct {
28 | JavaVM* android_jvm;
29 | jclass class_BoatLib;
30 | ANativeWindow* window;
31 | char* clipboard_string;
32 | EventQueue event_queue;
33 | pthread_mutex_t event_queue_mutex;
34 | int has_event_pipe;
35 | int event_pipe_fd[2];
36 | int epoll_fd;
37 | } BoatInternal;
38 |
39 | extern BoatInternal mBoat;
40 |
41 | #define BOAT_INTERNAL_LOG(x...) do { \
42 | fprintf(stderr, "[Boat Internal] %s:%d\n", __FILE__, __LINE__); \
43 | fprintf(stderr, x); \
44 | fprintf(stderr, "\n"); \
45 | fflush(stderr); \
46 | } while (0)
47 |
48 | #define PrepareBoatLibJNI() \
49 | JavaVM* vm = mBoat.android_jvm; \
50 | JNIEnv* env = NULL; \
51 | jint attached = (*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_2); \
52 | if (attached == JNI_EDETACHED) { \
53 | attached = (*vm)->AttachCurrentThread(vm, &env, NULL); \
54 | if (attached != JNI_OK || env == NULL) { \
55 | BOAT_INTERNAL_LOG("Failed to attach thread to Android JavaVM."); \
56 | } \
57 | } \
58 | do {} while(0)
59 |
60 | #define CallBoatLibJNIFunc(return_exp, func_type, func_name, signature, args...) \
61 | jmethodID BoatLib_##func_name = (*env)->GetStaticMethodID(env, mBoat.class_BoatLib, #func_name, signature); \
62 | if (BoatLib_##func_name == NULL) { \
63 | BOAT_INTERNAL_LOG("Failed to find static method BoatLib_"#func_name ); \
64 | } \
65 | return_exp (*env)->CallStatic##func_type##Method(env, mBoat.class_BoatLib, BoatLib_##func_name, ##args); \
66 | do {} while(0)
67 |
68 | #endif // BOAT_INTERNAL_H
69 |
--------------------------------------------------------------------------------
/sample/src/main/java/cosine/boat/logcat/LogcatUtils.java:
--------------------------------------------------------------------------------
1 | package cosine.boat.logcat;
2 |
3 | import android.app.ActivityManager;
4 | import android.content.ComponentName;
5 | import android.content.Context;
6 | import android.content.pm.PackageManager;
7 | import android.content.pm.ServiceInfo;
8 | import android.os.Process;
9 | //import android.support.annotation.NonNull;
10 |
11 | /**
12 | * Contains some utility code.
13 | */
14 | public class LogcatUtils {
15 |
16 | /**
17 | * Checks if a current process is a main process of application.
18 | *
19 | * @param context Current context.
20 | * @return Flag whether it's a main process.
21 | */
22 | public static boolean isMainProcess(/*@NonNull*/ Context context) {
23 | final int pid = Process.myPid();
24 | final String packageName = context.getPackageName();
25 | final ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
26 | if (manager != null) {
27 | for (final ActivityManager.RunningAppProcessInfo info : manager.getRunningAppProcesses()) {
28 | if (info.pid == pid) {
29 | return packageName.equals(info.processName);
30 | }
31 | }
32 | }
33 | return true;
34 | }
35 |
36 | /**
37 | * Checks if a current process is a background crash service process.
38 | *
39 | * @param context Current context.
40 | * @param serviceClass Class of background crash reporting service.
41 | * @return Flag whether a current process is a background crash service process.
42 | */
43 | public static boolean isLogcatServiceProcess(/*@NonNull */Context context,/* @NonNull */Class extends LogcatService> serviceClass) {
44 | final ServiceInfo serviceInfo;
45 | try {
46 | serviceInfo = context.getPackageManager().getServiceInfo(new ComponentName(context, serviceClass), 0);
47 | } catch (PackageManager.NameNotFoundException ignored) {
48 | return false;
49 | }
50 | final int pid = Process.myPid();
51 | final ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
52 | if (manager != null) {
53 | for (final ActivityManager.RunningAppProcessInfo info : manager.getRunningAppProcesses()) {
54 | if (info.pid == pid) {
55 | return serviceInfo.processName.equals(info.processName);
56 | }
57 | }
58 | }
59 | return false;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/boat/src/main/java/cosine/boat/BoatService.java:
--------------------------------------------------------------------------------
1 | package cosine.boat;
2 |
3 | import android.app.Service;
4 | import android.content.Intent;
5 | import android.app.Notification;
6 | import android.os.IBinder;
7 | import android.app.PendingIntent;
8 | import java.util.Map;
9 | import java.util.HashMap;
10 |
11 | public class BoatService extends Service
12 | {
13 | public static final String STOP_BOAT_SERVICE = "stop_boat_service";
14 |
15 | private BoatTask task = null;
16 |
17 | public void showForegroundNotification() {
18 | Notification.Builder builder = new Notification.Builder(this);
19 | builder.setSmallIcon(R.drawable.ic_boat);
20 | builder.setContentTitle("BoatService");
21 | builder.setContentText("Running");
22 | builder.setOngoing(true);
23 | Intent exitIntent = new Intent(this, this.getClass()).setAction(STOP_BOAT_SERVICE);
24 | builder.setContentIntent(PendingIntent.getService(this, 0, exitIntent, 0));
25 | Notification notification = builder.build();
26 | startForeground(8888, notification);
27 | }
28 |
29 | public void dismissForegroundNotification() {
30 | stopForeground(true);
31 | }
32 |
33 | @Override
34 | public void onDestroy() {
35 | super.onDestroy();
36 | dismissForegroundNotification();
37 | System.exit(0);
38 | }
39 |
40 | @Override
41 | public int onStartCommand(Intent intent, int flags, int startId) {
42 | String action = intent.getAction();
43 | if (action != null) {
44 | if (action.equals(STOP_BOAT_SERVICE)) {
45 | this.stopSelf();
46 | return Service.START_NOT_STICKY;
47 | }
48 | }
49 | if (this.task == null) {
50 | Map envvars = new HashMap<>();
51 | envvars.put(BoatScript.BOAT_ENV_WINDOW_WIDTH, "0");
52 | envvars.put(BoatScript.BOAT_ENV_WINDOW_HEIGHT, "0");
53 | envvars.put(BoatScript.BOAT_ENV_TMPDIR, this.getCacheDir().getAbsolutePath());
54 | this.task = new BoatTask(envvars, intent.getStringExtra(BoatTask.BOAT_TASK_SCRIPT_PATH)) {
55 | @Override
56 | public void afterExecute() {
57 | BoatService.this.stopSelf();
58 | }
59 | };
60 | this.task.startTask();
61 | showForegroundNotification();
62 | }
63 | return Service.START_NOT_STICKY;
64 | }
65 |
66 | @Override
67 | public IBinder onBind(Intent intent) {
68 | // Service doesn't support to be bound.
69 | return null;
70 | }
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/sample/src/main/java/cosine/boat/logcat/Logcat.java:
--------------------------------------------------------------------------------
1 | package cosine.boat.logcat;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | /*
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 | */
9 |
10 | /**
11 | * Main binding class for NDCrash functionality.
12 | */
13 | public class Logcat {
14 |
15 | /**
16 | * Initializes NDCrash library signal handler using out-of-process mode. Should be called from
17 | * onCreate() method of your subclass of Application.
18 | *
19 | * @param context Context instance. Used to determine a socket name and start a service.
20 | * @param crashReportPath Path where a crash report is saved.
21 | * @param unwinder Used unwinder. See ndcrash_unwinder type in ndcrash.h.
22 | * @param serviceClass Class of background service. Used when we need to use a custom subclass
23 | * of NDCrashUnwinder to use as a background service. If you didn't subclass
24 | * NDCrashUnwinder, please pass NDCrashUnwinder.class.
25 | * @return Error status.
26 | */
27 | public static int initializeOutOfProcess(
28 | /*@NonNull*/ Context context,
29 | /*@Nullable*/ String crashReportPath,
30 | /*@NonNull*/ Class extends LogcatService> serviceClass) {
31 | if (LogcatUtils.isLogcatServiceProcess(context, serviceClass)) {
32 | // If it's a background crash service process we don't need to initialize anything,
33 | // we treat this situation as no error because this method is designed to call from
34 | // Application.onCreate().
35 | return 1;
36 | }
37 | // Saving service class, we should be able to stop it on de-initialization.
38 | mServiceClass = serviceClass;
39 | // Starting crash reporting service. Only from main process.
40 | if (LogcatUtils.isMainProcess(context)) {
41 | final Intent serviceIntent = new Intent(context, serviceClass);
42 | serviceIntent.putExtra(LogcatService.EXTRA_REPORT_FILE, crashReportPath);
43 | try {
44 | context.startService(serviceIntent);
45 | } catch (RuntimeException e) {
46 | return 2;
47 | }
48 | }
49 | // Initializing signal handler.
50 | return 0;
51 | }
52 |
53 | /**
54 | * De-initializes NDCrash library signal handler using out-of-process mode.
55 | *
56 | * @param context Context instance. Used to stop a service.
57 | * @return Flag whether de-initialization was successful.
58 | */
59 | public static boolean deInitializeOutOfProcess(/*@NonNull*/ Context context) {
60 | if (mServiceClass != null) {
61 | context.stopService(new Intent(context, mServiceClass));
62 | mServiceClass = null;
63 | }
64 | return true;
65 | }
66 |
67 | /**
68 | * Background service class for out-of-process mode.
69 | */
70 | //@Nullable
71 | private static Class extends LogcatService> mServiceClass = null;
72 | }
73 |
--------------------------------------------------------------------------------
/boat/src/main/java/com/mojang/minecraftpe/TextInputProxyEditTextbox.java:
--------------------------------------------------------------------------------
1 | package com.mojang.minecraftpe;
2 |
3 | import android.content.Context;
4 | import android.text.InputFilter;
5 | import android.text.Spanned;
6 | import android.util.AttributeSet;
7 | import android.view.KeyEvent;
8 | import android.view.inputmethod.EditorInfo;
9 | import android.view.inputmethod.InputConnection;
10 | import android.view.inputmethod.InputConnectionWrapper;
11 | import android.widget.EditText;
12 |
13 | public class TextInputProxyEditTextbox extends EditText {
14 | /* access modifiers changed from: private */
15 | public MCPEKeyWatcher _mcpeKeyWatcher;
16 | public final int allowedLength;
17 | public final boolean limitInput;
18 |
19 | public interface MCPEKeyWatcher {
20 | void onBackKeyPressed();
21 |
22 | void onDeleteKeyPressed();
23 | }
24 |
25 | public TextInputProxyEditTextbox(Context context, AttributeSet attrs, int defStyle) {
26 | super(context, attrs, defStyle);
27 | this._mcpeKeyWatcher = null;
28 | this.allowedLength = 160;
29 | this.limitInput = false;
30 | }
31 |
32 | public TextInputProxyEditTextbox(Context context, AttributeSet attrs) {
33 | super(context, attrs);
34 | this._mcpeKeyWatcher = null;
35 | this.allowedLength = 160;
36 | this.limitInput = false;
37 | }
38 |
39 | public TextInputProxyEditTextbox(Context context, int allowedLength2, boolean limitInput2) {
40 | super(context);
41 | this._mcpeKeyWatcher = null;
42 | this.allowedLength = allowedLength2;
43 | this.limitInput = limitInput2;
44 | setFilters(limitInput2 ? new InputFilter[]{new InputFilter.LengthFilter(this.allowedLength), new InputFilter() {
45 | public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
46 | if (source.equals("")) {
47 | }
48 | return source;
49 | }
50 | }} : new InputFilter[]{new InputFilter.LengthFilter(this.allowedLength)});
51 | }
52 |
53 | public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
54 | return new MCPEInputConnection(super.onCreateInputConnection(outAttrs), true, this);
55 | }
56 |
57 | public boolean onKeyPreIme(int keyCode, KeyEvent event) {
58 | if (keyCode != 4 || event.getAction() != 1) {
59 | return super.onKeyPreIme(keyCode, event);
60 | }
61 | if (this._mcpeKeyWatcher != null) {
62 | this._mcpeKeyWatcher.onBackKeyPressed();
63 | }
64 | return false;
65 | }
66 |
67 | public void setOnMCPEKeyWatcher(MCPEKeyWatcher mcpeKeyWatcher) {
68 | this._mcpeKeyWatcher = mcpeKeyWatcher;
69 | }
70 |
71 | private class MCPEInputConnection extends InputConnectionWrapper {
72 | TextInputProxyEditTextbox textbox;
73 |
74 | public MCPEInputConnection(InputConnection target, boolean mutable, TextInputProxyEditTextbox textbox2) {
75 | super(target, mutable);
76 | this.textbox = textbox2;
77 | }
78 |
79 | public boolean sendKeyEvent(KeyEvent event) {
80 | if (this.textbox.getText().length() != 0 || event.getAction() != 0 || event.getKeyCode() != 67) {
81 | return super.sendKeyEvent(event);
82 | }
83 | if (TextInputProxyEditTextbox.this._mcpeKeyWatcher != null) {
84 | TextInputProxyEditTextbox.this._mcpeKeyWatcher.onDeleteKeyPressed();
85 | }
86 | return false;
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/boat/src/main/java/cosine/boat/BoatLib.java:
--------------------------------------------------------------------------------
1 | package cosine.boat;
2 |
3 | import android.view.Surface;
4 | import android.content.ClipboardManager;
5 | import android.content.ClipData;
6 | import android.content.Context;
7 |
8 | public class BoatLib {
9 |
10 | public static final int KeyPress = 2;
11 | public static final int KeyRelease = 3;
12 | public static final int ButtonPress = 4;
13 | public static final int ButtonRelease = 5;
14 | public static final int MotionNotify = 6;
15 | public static final int ConfigureNotify = 22;
16 | public static final int BoatMessage = 37;
17 |
18 | public static final int Button1 = 1;
19 | public static final int Button2 = 2;
20 | public static final int Button3 = 3;
21 | public static final int Button4 = 4;
22 | public static final int Button5 = 5;
23 | public static final int Button6 = 6;
24 | public static final int Button7 = 7;
25 |
26 | public static final int CursorEnabled = 1;
27 | public static final int CursorDisabled = 0;
28 |
29 | public static final int ShiftMask = 1 << 0;
30 | public static final int LockMask = 1 << 1;
31 | public static final int ControlMask = 1 << 2;
32 | public static final int Mod1Mask = 1 << 3;
33 | public static final int Mod2Mask = 1 << 4;
34 | public static final int Mod3Mask = 1 << 5;
35 | public static final int Mod4Mask = 1 << 6;
36 | public static final int Mod5Mask = 1 << 7;
37 |
38 | public static final int CloseRequest = 0;
39 |
40 | static {
41 | System.loadLibrary("boat");
42 | }
43 |
44 | public static native void setBoatNativeWindow(Surface surface);
45 | public static native void setEventPipe();
46 | public static native void pushEvent(long time, int type, int p1, int p2);
47 |
48 | public static void pushEventMouseButton(int button, boolean press) {
49 | BoatLib.pushEvent(System.nanoTime(), press ? ButtonPress : ButtonRelease, button, 0);
50 | }
51 | public static void pushEventPointer(int x, int y) {
52 | BoatLib.pushEvent(System.nanoTime(), MotionNotify, x, y);
53 | }
54 | public static void pushEventKey(int keyCode, int keyChar, boolean press) {
55 | BoatLib.pushEvent(System.nanoTime(), press ? KeyPress : KeyRelease, keyCode, keyChar);
56 | }
57 | public static void pushEventWindow(int width, int height) {
58 | BoatLib.pushEvent(System.nanoTime(), ConfigureNotify, width, height);
59 | }
60 | public static void pushEventMessage(int msg) {
61 | BoatLib.pushEvent(System.nanoTime(), BoatMessage, msg, 0);
62 | }
63 |
64 | // BoatLib callbacks
65 | public static void setCursorMode(int mode) {
66 | BoatActivity boat_activity = BoatActivity.getCurrentInstance();
67 | boat_activity.setCursorViewMode(mode);
68 | }
69 |
70 | public static void setPrimaryClipString(String string) {
71 | BoatActivity boat_activity = BoatActivity.getCurrentInstance();
72 | ClipboardManager clipboard = (ClipboardManager)boat_activity.getSystemService(Context.CLIPBOARD_SERVICE);
73 | ClipData clip = ClipData.newPlainText("Boat Clipboard", string);
74 | clipboard.setPrimaryClip(clip);
75 | }
76 |
77 | public static String getPrimaryClipString() {
78 | BoatActivity boat_activity = BoatActivity.getCurrentInstance();
79 | ClipboardManager clipboard = (ClipboardManager)boat_activity.getSystemService(Context.CLIPBOARD_SERVICE);
80 | if (!clipboard.hasPrimaryClip()) {
81 | return null;
82 | }
83 | ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);
84 | return item.getText().toString();
85 | }
86 | }
87 |
88 |
--------------------------------------------------------------------------------
/boat/src/main/jni/boat/include/boat_keycodes.h:
--------------------------------------------------------------------------------
1 | #ifndef BOAT_KEYCODES_H
2 | #define BOAT_KEYCODES_H
3 |
4 | /*
5 | * from linux/input-event-codes.h
6 | **/
7 |
8 | #define BOAT_MAX_SCANCODE 240
9 | #define BOAT_MIN_SCANCODE 0
10 |
11 | #define KEY_RESERVED 0
12 |
13 | #define KEY_ESC 1
14 | #define KEY_1 2
15 | #define KEY_2 3
16 | #define KEY_3 4
17 | #define KEY_4 5
18 | #define KEY_5 6
19 | #define KEY_6 7
20 | #define KEY_7 8
21 | #define KEY_8 9
22 | #define KEY_9 10
23 | #define KEY_0 11
24 | #define KEY_MINUS 12
25 | #define KEY_EQUAL 13
26 | #define KEY_BACKSPACE 14
27 | #define KEY_TAB 15
28 | #define KEY_Q 16
29 | #define KEY_W 17
30 | #define KEY_E 18
31 | #define KEY_R 19
32 | #define KEY_T 20
33 | #define KEY_Y 21
34 | #define KEY_U 22
35 | #define KEY_I 23
36 | #define KEY_O 24
37 | #define KEY_P 25
38 | #define KEY_LEFTBRACE 26
39 | #define KEY_RIGHTBRACE 27
40 | #define KEY_ENTER 28
41 | #define KEY_LEFTCTRL 29
42 | #define KEY_A 30
43 | #define KEY_S 31
44 | #define KEY_D 32
45 | #define KEY_F 33
46 | #define KEY_G 34
47 | #define KEY_H 35
48 | #define KEY_J 36
49 | #define KEY_K 37
50 | #define KEY_L 38
51 | #define KEY_SEMICOLON 39
52 | #define KEY_APOSTROPHE 40
53 | #define KEY_GRAVE 41
54 | #define KEY_LEFTSHIFT 42
55 | #define KEY_BACKSLASH 43
56 | #define KEY_Z 44
57 | #define KEY_X 45
58 | #define KEY_C 46
59 | #define KEY_V 47
60 | #define KEY_B 48
61 | #define KEY_N 49
62 | #define KEY_M 50
63 | #define KEY_COMMA 51
64 | #define KEY_DOT 52
65 | #define KEY_SLASH 53
66 | #define KEY_RIGHTSHIFT 54
67 | #define KEY_KPASTERISK 55
68 | #define KEY_LEFTALT 56
69 | #define KEY_SPACE 57
70 | #define KEY_CAPSLOCK 58
71 | #define KEY_F1 59
72 | #define KEY_F2 60
73 | #define KEY_F3 61
74 | #define KEY_F4 62
75 | #define KEY_F5 63
76 | #define KEY_F6 64
77 | #define KEY_F7 65
78 | #define KEY_F8 66
79 | #define KEY_F9 67
80 | #define KEY_F10 68
81 | #define KEY_NUMLOCK 69
82 | #define KEY_SCROLLLOCK 70
83 | #define KEY_KP7 71
84 | #define KEY_KP8 72
85 | #define KEY_KP9 73
86 | #define KEY_KPMINUS 74
87 | #define KEY_KP4 75
88 | #define KEY_KP5 76
89 | #define KEY_KP6 77
90 | #define KEY_KPPLUS 78
91 | #define KEY_KP1 79
92 | #define KEY_KP2 80
93 | #define KEY_KP3 81
94 | #define KEY_KP0 82
95 | #define KEY_KPDOT 83
96 |
97 | #define KEY_F11 87
98 | #define KEY_F12 88
99 |
100 | #define KEY_KPENTER 96
101 | #define KEY_RIGHTCTRL 97
102 | #define KEY_KPSLASH 98
103 | #define KEY_SYSRQ 99
104 | #define KEY_RIGHTALT 100
105 |
106 | #define KEY_HOME 102
107 | #define KEY_UP 103
108 | #define KEY_PAGEUP 104
109 | #define KEY_LEFT 105
110 | #define KEY_RIGHT 106
111 | #define KEY_END 107
112 | #define KEY_DOWN 108
113 | #define KEY_PAGEDOWN 109
114 | #define KEY_INSERT 110
115 | #define KEY_DELETE 111
116 |
117 | #define KEY_KPEQUAL 117
118 |
119 | #define KEY_PAUSE 119
120 |
121 | #define KEY_KPCOMMA 121
122 |
123 | #define KEY_LEFTMETA 125
124 | #define KEY_RIGHTMETA 126
125 |
126 | #define KEY_MENU 139
127 |
128 | #define KEY_F13 183
129 | #define KEY_F14 184
130 | #define KEY_F15 185
131 | #define KEY_F16 186
132 | #define KEY_F17 187
133 | #define KEY_F18 188
134 | #define KEY_F19 189
135 | #define KEY_F20 190
136 | #define KEY_F21 191
137 | #define KEY_F22 192
138 | #define KEY_F23 193
139 | #define KEY_F24 194
140 |
141 | #define KEY_PRINT 210
142 |
143 | #define KEY_UNKNOWN 240
144 |
145 | #endif // BOAT_KEYCODES_H
146 |
--------------------------------------------------------------------------------
/sample/src/main/java/cosine/boat/logcat/LogcatService.java:
--------------------------------------------------------------------------------
1 | package cosine.boat.logcat;
2 |
3 | import android.app.Service;
4 | import android.content.Intent;
5 | import android.content.SharedPreferences;
6 | import android.os.IBinder;
7 | import android.content.Context;
8 | import java.io.IOException;
9 |
10 | /**
11 | * Service for out-of-process crash handling daemon. Should be run from a separate process.
12 | */
13 | public class LogcatService extends Service
14 | {
15 |
16 | /**
17 | * Indicates if a daemon was started.
18 | */
19 | private static boolean mDaemonStarted = false;
20 |
21 | /**
22 | * A name for shared preferences.
23 | */
24 | private static final String PREFS_NAME = "LogcatService";
25 |
26 | /**
27 | * Key for report file in arguments.
28 | */
29 | public static final String EXTRA_REPORT_FILE = "report_file";
30 |
31 | private static Process mLogcatProcess;
32 | @Override
33 | public int onStartCommand(Intent intent, int flags, int startId) {
34 | final SharedPreferences preferences = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
35 | String reportPath = null;
36 | if (intent != null) {
37 | reportPath = intent.getStringExtra(EXTRA_REPORT_FILE);
38 | // Using the same keys as extras.
39 | final SharedPreferences.Editor editor = preferences.edit();
40 | if (reportPath != null) {
41 | editor.putString(EXTRA_REPORT_FILE, reportPath);
42 | } else {
43 | editor.remove(EXTRA_REPORT_FILE);
44 | }
45 | editor.apply();
46 | } else {
47 | reportPath = preferences.getString(EXTRA_REPORT_FILE, null);
48 | }
49 | if (!mDaemonStarted) {
50 | mDaemonStarted = true;
51 | final int initResult = startOutOfProcessDaemon(this, reportPath);
52 | if (initResult != 0) {
53 | //Log.e(TAG, "Couldn't start NDCrash out-of-process daemon with unwinder: " + unwinder + ", error: " + initResult);
54 | } else {
55 | //Log.i(TAG, "Out-of-process unwinding daemon is started with unwinder: " + unwinder + " report path: " +
56 | // (reportPath != null ? reportPath : "null"));
57 |
58 | }
59 | } else {
60 | //Log.i(TAG, "NDCrash out-of-process daemon is already started.");
61 | }
62 | // START_REDELIVER_INTENT may seem better but found by experimental way that when we return
63 | // this value a service is restarted significantly slower (with a longer delay) after its
64 | // process is killed. So a workaround is used: Saving initialization parameters to shared
65 | // preferences and reading them when intent is null.
66 | return Service.START_STICKY;
67 | }
68 |
69 | @Override //@CallSuper
70 | public void onDestroy() {
71 |
72 | if (mDaemonStarted) {
73 | mDaemonStarted = false;
74 | final boolean stoppedSuccessfully = stopOutOfProcessDaemon();
75 | //Log.i(TAG, "Out-of-process daemon " + (stoppedSuccessfully ? "is successfully stopped." : "failed to stop."));
76 | }
77 | super.onDestroy();
78 | }
79 |
80 | @Override
81 | public IBinder onBind(Intent intent) {
82 | // Service doesn't support to be bound.
83 | return null;
84 | }
85 |
86 | /**
87 | * Starts NDCrash out-of-process unwinding daemon. This is necessary for out of process crash
88 | * handling. This method is run from a service that works in separate process.
89 | *
90 | * @param context Context instance. Used to determine a socket name.
91 | * @param crashReportPath Path where to save a crash report.
92 | * @param unwinder Unwinder to use.
93 | * @param callback Callback to execute when a crash has occurred.
94 | * @return Error status.
95 | */
96 | static int startOutOfProcessDaemon(
97 | /*@NonNull */Context context,
98 | /*@Nullable */String crashReportPath) {
99 | if (LogcatUtils.isMainProcess(context)) {
100 | return 1;
101 | }
102 | try
103 | {
104 | mLogcatProcess = new ProcessBuilder("logcat", "-v", "long", "-f", crashReportPath).start();
105 | }
106 | catch (IOException e)
107 | {}
108 | return 0;
109 | }
110 |
111 | /**
112 | * Stops NDCrash out-of-process unwinding daemon.
113 | *
114 | * @return Flag whether daemon stopping was successful.
115 | */
116 | static boolean stopOutOfProcessDaemon() {
117 | mLogcatProcess.destroy();
118 | return true;
119 | }
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/boat/src/main/jni/boat/boat_event.c:
--------------------------------------------------------------------------------
1 | #include "boat_internal.h"
2 |
3 | void EventQueue_init(EventQueue* queue) {
4 | queue->count = 0;
5 | queue->head = NULL;
6 | queue->tail = NULL;
7 | }
8 |
9 | BoatEvent* EventQueue_add(EventQueue* queue) {
10 | BoatEvent* ret = NULL;
11 | QueueElement* e = malloc(sizeof(QueueElement));
12 | if (e != NULL) {
13 | e->next = NULL;
14 | if (queue->count > 0) {
15 | queue->tail->next = e;
16 | queue->tail = e;
17 | }
18 | else { // count == 0
19 | queue->head = e;
20 | queue->tail = e;
21 | }
22 | queue->count++;
23 | ret = &queue->tail->event;
24 | }
25 | return ret;
26 | }
27 |
28 | int EventQueue_take(EventQueue* queue, BoatEvent* event) {
29 | int ret = 0;
30 | if (queue->count > 0) {
31 | QueueElement* e = queue->head;
32 | if (queue->count == 1) {
33 | queue->head = NULL;
34 | queue->tail = NULL;
35 | }
36 | else {
37 | queue->head = e->next;
38 | }
39 | queue->count--;
40 | ret = 1;
41 | if (event != NULL) {
42 | memcpy(event, &e->event, sizeof(BoatEvent));
43 | }
44 | free(e);
45 | }
46 | return ret;
47 | }
48 |
49 | void EventQueue_clear(EventQueue* queue) {
50 | while (queue->count > 0) {
51 | EventQueue_take(queue, NULL);
52 | }
53 | }
54 |
55 | void boatSetCursorMode(int mode) {
56 | if (!mBoat.has_event_pipe) {
57 | return;
58 | }
59 | PrepareBoatLibJNI();
60 | CallBoatLibJNIFunc( , Void, setCursorMode, "(I)V", mode);
61 | }
62 |
63 | int boatGetEventFd() {
64 | if (!mBoat.has_event_pipe) {
65 | return -1;
66 | }
67 | return mBoat.event_pipe_fd[0];
68 | }
69 |
70 | int boatWaitForEvent(int timeout) {
71 | if (!mBoat.has_event_pipe) {
72 | return 0;
73 | }
74 | struct epoll_event ev;
75 | int ret = epoll_wait(mBoat.epoll_fd, &ev, 1, timeout);
76 | if (ret > 0 && (ev.events & EPOLLIN)) {
77 | return 1;
78 | }
79 | return 0;
80 | }
81 |
82 | int boatPollEvent(BoatEvent* event) {
83 | if (!mBoat.has_event_pipe) {
84 | return 0;
85 | }
86 | if (pthread_mutex_lock(&mBoat.event_queue_mutex)) {
87 | BOAT_INTERNAL_LOG("Failed to acquire mutex");
88 | return 0;
89 | }
90 | char c;
91 | int ret = 0;
92 | if (read(mBoat.event_pipe_fd[0], &c, 1) > 0) {
93 | ret = EventQueue_take(&mBoat.event_queue, event);
94 | }
95 | if (pthread_mutex_unlock(&mBoat.event_queue_mutex)) {
96 | BOAT_INTERNAL_LOG("Failed to release mutex");
97 | return 0;
98 | }
99 | return ret;
100 | }
101 |
102 | JNIEXPORT void JNICALL Java_cosine_boat_BoatLib_pushEvent(JNIEnv* env, jclass clazz, jlong time, jint type, jint p1, jint p2) {
103 | if (!mBoat.has_event_pipe) {
104 | return;
105 | }
106 | if (pthread_mutex_lock(&mBoat.event_queue_mutex)) {
107 | BOAT_INTERNAL_LOG("Failed to acquire mutex");
108 | return;
109 | }
110 | BoatEvent* event = EventQueue_add(&mBoat.event_queue);
111 | if (event == NULL) {
112 | BOAT_INTERNAL_LOG("Failed to add event to event queue");
113 | return;
114 | }
115 | event->time = time;
116 | event->type = type;
117 | event->state = 0;
118 | switch (type) {
119 | case MotionNotify:
120 | event->x = p1;
121 | event->y = p2;
122 | break;
123 | case ButtonPress:
124 | case ButtonRelease:
125 | event->button = p1;
126 | break;
127 | case KeyPress:
128 | case KeyRelease:
129 | event->keycode = p1;
130 | event->keychar = p2;
131 | break;
132 | case ConfigureNotify:
133 | event->width = p1;
134 | event->height = p2;
135 | break;
136 | case BoatMessage:
137 | event->message = p1;
138 | break;
139 | }
140 | write(mBoat.event_pipe_fd[1], "E", 1);
141 | if (pthread_mutex_unlock(&mBoat.event_queue_mutex)) {
142 | BOAT_INTERNAL_LOG("Failed to release mutex");
143 | }
144 | }
145 |
146 | JNIEXPORT void JNICALL Java_cosine_boat_BoatLib_setEventPipe(JNIEnv* env, jclass clazz) {
147 | if (pipe(mBoat.event_pipe_fd) == -1) {
148 | BOAT_INTERNAL_LOG("Failed to create event pipe : %s", strerror(errno));
149 | return;
150 | }
151 | mBoat.epoll_fd = epoll_create(3);
152 | if (mBoat.epoll_fd == -1) {
153 | BOAT_INTERNAL_LOG("Failed to get epoll fd : %s", strerror(errno));
154 | return;
155 | }
156 | struct epoll_event ev;
157 | ev.events = EPOLLIN;
158 | ev.data.fd = mBoat.event_pipe_fd[0];
159 | if (epoll_ctl(mBoat.epoll_fd, EPOLL_CTL_ADD, mBoat.event_pipe_fd[0], &ev) == -1) {
160 | BOAT_INTERNAL_LOG("Failed to add epoll event : %s", strerror(errno));
161 | return;
162 | }
163 | EventQueue_init(&mBoat.event_queue);
164 | pthread_mutex_init(&mBoat.event_queue_mutex, NULL);
165 | mBoat.has_event_pipe = 1;
166 | BOAT_INTERNAL_LOG("Succeeded to set event pipe");
167 | }
168 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/boat/src/main/java/cosine/boat/BoatScript.java:
--------------------------------------------------------------------------------
1 | package cosine.boat;
2 |
3 | import java.lang.reflect.Type;
4 | import java.text.ParseException;
5 | import java.util.HashMap;
6 | import java.util.LinkedList;
7 | import java.util.List;
8 | import java.util.Map;
9 | import java.util.regex.Matcher;
10 | import java.util.regex.Pattern;
11 |
12 | import com.google.gson.Gson;
13 | import com.google.gson.reflect.TypeToken;
14 | import java.io.IOException;
15 | import java.io.File;
16 | import java.io.FileInputStream;
17 |
18 | import static cosine.boat.LoadMe.*;
19 |
20 | public class BoatScript {
21 |
22 | public static final String BOAT_ENV_WINDOW_WIDTH = "window_width";
23 | public static final String BOAT_ENV_WINDOW_HEIGHT = "window_height";
24 | public static final String BOAT_ENV_TMPDIR = "tmpdir";
25 |
26 | private static Pattern variablePattern;
27 | static {
28 | variablePattern = Pattern.compile("\\$\\{[a-zA-Z_]+\\}");
29 | }
30 |
31 | public static LinkedList parseJson(String filePath) throws IOException {
32 | File file = new File(filePath);
33 | FileInputStream fis = new FileInputStream(file);
34 | byte[] buffer = new byte[(int)fis.available()];
35 | fis.read(buffer);
36 | fis.close();
37 | String json = new String(buffer, "UTF-8");
38 |
39 | Type type = new TypeToken>() {}.getType();
40 | return new Gson().fromJson(json, type);
41 | }
42 |
43 | private Map variables;
44 | private List commands;
45 |
46 | private String scriptFile;
47 |
48 | public BoatScript(Map envvars, boolean priv, List cmds, String file) {
49 | if (priv) {
50 | this.variables = new HashMap<>();
51 | this.variables.putAll(envvars);
52 | }
53 | else {
54 | this.variables = envvars;
55 | }
56 | this.commands = new LinkedList<>();
57 | this.commands.addAll(cmds);
58 | this.scriptFile = file;
59 | if (this.scriptFile == null) {
60 | this.scriptFile = "";
61 | }
62 | }
63 |
64 | private String replaceVariables(String str) throws ParseException {
65 | if (str == null) {
66 | str = "";
67 | }
68 | Matcher m = variablePattern.matcher(str);
69 | while (m.find()) {
70 | String varRef = m.group();
71 | String varName = varRef.substring(2, varRef.length() - 1);
72 | String varValue = this.variables.get(varName);
73 | if (varValue == null) {
74 | varValue = "";
75 | }
76 | str = str.replace(varRef, varValue);
77 | }
78 | return str;
79 | }
80 |
81 | public void execute() {
82 | int line = 0;
83 | try {
84 | for (; line < this.commands.size(); line++) {
85 | String[] args = this.commands.get(line);
86 | if (args == null) {
87 | continue;
88 | }
89 | for (int i = 1; i < args.length; i++) {
90 | args[i] = replaceVariables(args[i]);
91 | }
92 | switch (args[0]) {
93 | case "patchLinker": {
94 | patchLinker();
95 | break;
96 | }
97 | case "setenv": {
98 | setenv(args[1], args[2]);
99 | break;
100 | }
101 | case "chdir": {
102 | chdir(args[1]);
103 | break;
104 | }
105 | case "redirectStdio": {
106 | redirectStdio(args[1]);
107 | break;
108 | }
109 | case "dlopen": {
110 | dlopen(args[1]);
111 | break;
112 | }
113 | case "dlexec": {
114 | String[] finalArgs = new String[args.length - 1];
115 | System.arraycopy(args, 1, finalArgs, 0, args.length - 1);
116 | dlexec(finalArgs);
117 | break;
118 | }
119 | case "strdef": {
120 | String value = args[2];
121 | if (value == null) {
122 | value = "";
123 | }
124 | this.variables.put(args[1], value);
125 | break;
126 | }
127 | case "strcat": {
128 | String value = this.variables.get(args[1]);
129 | if (value == null) {
130 | value = "";
131 | }
132 | StringBuilder builder = new StringBuilder(value);
133 | for (int i = 2; i < args.length; i++) {
134 | if (args[i] != null) {
135 | builder.append(args[i]);
136 | }
137 | }
138 | this.variables.put(args[1], builder.toString());
139 | break;
140 | }
141 | case "json": {
142 | if (this.scriptFile.equals(args[1])) {
143 | throw new Exception("Recursive script!");
144 | }
145 | LinkedList includes = parseJson(args[1]);
146 | new BoatScript(this.variables, false, includes, args[1]).execute();
147 | break;
148 | }
149 | default : {
150 | break;
151 | }
152 | }
153 | }
154 | }
155 | catch (Exception e) {
156 | System.out.println("Exception occurred, " + this.scriptFile + ":" + line);
157 | System.out.println(this.commands.get(line));
158 | e.printStackTrace();
159 | }
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/boat/src/main/java/cosine/boat/BoatKeycodes.java:
--------------------------------------------------------------------------------
1 | package cosine.boat;
2 |
3 | /**
4 | * Boat key codes, from linux/input-event-codes.h
5 | */
6 | public final class BoatKeycodes {
7 | public static final int KEY_RESERVED = 0;
8 |
9 | public static final int KEY_ESC = 1;
10 | public static final int KEY_1 = 2;
11 | public static final int KEY_2 = 3;
12 | public static final int KEY_3 = 4;
13 | public static final int KEY_4 = 5;
14 | public static final int KEY_5 = 6;
15 | public static final int KEY_6 = 7;
16 | public static final int KEY_7 = 8;
17 | public static final int KEY_8 = 9;
18 | public static final int KEY_9 = 10;
19 | public static final int KEY_0 = 11;
20 | public static final int KEY_MINUS = 12;
21 | public static final int KEY_EQUAL = 13;
22 | public static final int KEY_BACKSPACE = 14;
23 | public static final int KEY_TAB = 15;
24 | public static final int KEY_Q = 16;
25 | public static final int KEY_W = 17;
26 | public static final int KEY_E = 18;
27 | public static final int KEY_R = 19;
28 | public static final int KEY_T = 20;
29 | public static final int KEY_Y = 21;
30 | public static final int KEY_U = 22;
31 | public static final int KEY_I = 23;
32 | public static final int KEY_O = 24;
33 | public static final int KEY_P = 25;
34 | public static final int KEY_LEFTBRACE = 26;
35 | public static final int KEY_RIGHTBRACE = 27;
36 | public static final int KEY_ENTER = 28;
37 | public static final int KEY_LEFTCTRL = 29;
38 | public static final int KEY_A = 30;
39 | public static final int KEY_S = 31;
40 | public static final int KEY_D = 32;
41 | public static final int KEY_F = 33;
42 | public static final int KEY_G = 34;
43 | public static final int KEY_H = 35;
44 | public static final int KEY_J = 36;
45 | public static final int KEY_K = 37;
46 | public static final int KEY_L = 38;
47 | public static final int KEY_SEMICOLON = 39;
48 | public static final int KEY_APOSTROPHE = 40;
49 | public static final int KEY_GRAVE = 41;
50 | public static final int KEY_LEFTSHIFT = 42;
51 | public static final int KEY_BACKSLASH = 43;
52 | public static final int KEY_Z = 44;
53 | public static final int KEY_X = 45;
54 | public static final int KEY_C = 46;
55 | public static final int KEY_V = 47;
56 | public static final int KEY_B = 48;
57 | public static final int KEY_N = 49;
58 | public static final int KEY_M = 50;
59 | public static final int KEY_COMMA = 51;
60 | public static final int KEY_DOT = 52;
61 | public static final int KEY_SLASH = 53;
62 | public static final int KEY_RIGHTSHIFT = 54;
63 | public static final int KEY_KPASTERISK = 55;
64 | public static final int KEY_LEFTALT = 56;
65 | public static final int KEY_SPACE = 57;
66 | public static final int KEY_CAPSLOCK = 58;
67 | public static final int KEY_F1 = 59;
68 | public static final int KEY_F2 = 60;
69 | public static final int KEY_F3 = 61;
70 | public static final int KEY_F4 = 62;
71 | public static final int KEY_F5 = 63;
72 | public static final int KEY_F6 = 64;
73 | public static final int KEY_F7 = 65;
74 | public static final int KEY_F8 = 66;
75 | public static final int KEY_F9 = 67;
76 | public static final int KEY_F10 = 68;
77 | public static final int KEY_NUMLOCK = 69;
78 | public static final int KEY_SCROLLLOCK = 70;
79 | public static final int KEY_KP7 = 71;
80 | public static final int KEY_KP8 = 72;
81 | public static final int KEY_KP9 = 73;
82 | public static final int KEY_KPMINUS = 74;
83 | public static final int KEY_KP4 = 75;
84 | public static final int KEY_KP5 = 76;
85 | public static final int KEY_KP6 = 77;
86 | public static final int KEY_KPPLUS = 78;
87 | public static final int KEY_KP1 = 79;
88 | public static final int KEY_KP2 = 80;
89 | public static final int KEY_KP3 = 81;
90 | public static final int KEY_KP0 = 82;
91 | public static final int KEY_KPDOT = 83;
92 |
93 | public static final int KEY_F11 = 87;
94 | public static final int KEY_F12 = 88;
95 |
96 | public static final int KEY_KPENTER = 96;
97 | public static final int KEY_RIGHTCTRL = 97;
98 | public static final int KEY_KPSLASH = 98;
99 | public static final int KEY_SYSRQ = 99;
100 | public static final int KEY_RIGHTALT = 100;
101 |
102 | public static final int KEY_HOME = 102;
103 | public static final int KEY_UP = 103;
104 | public static final int KEY_PAGEUP = 104;
105 | public static final int KEY_LEFT = 105;
106 | public static final int KEY_RIGHT = 106;
107 | public static final int KEY_END = 107;
108 | public static final int KEY_DOWN = 108;
109 | public static final int KEY_PAGEDOWN = 109;
110 | public static final int KEY_INSERT = 110;
111 | public static final int KEY_DELETE = 111;
112 |
113 | public static final int KEY_KPEQUAL = 117;
114 |
115 | public static final int KEY_PAUSE = 119;
116 |
117 | public static final int KEY_KPCOMMA = 121;
118 |
119 | public static final int KEY_LEFTMATA = 125;
120 | public static final int KEY_RIGHTMETA = 126;
121 |
122 | public static final int KEY_F13 = 183;
123 | public static final int KEY_F14 = 184;
124 | public static final int KEY_F15 = 185;
125 | public static final int KEY_F16 = 186;
126 | public static final int KEY_F17 = 187;
127 | public static final int KEY_F18 = 188;
128 | public static final int KEY_F19 = 189;
129 | public static final int KEY_F20 = 190;
130 | public static final int KEY_F21 = 191;
131 | public static final int KEY_F22 = 192;
132 | public static final int KEY_F23 = 193;
133 | public static final int KEY_F24 = 194;
134 |
135 | public static final int KEY_UNKNOWN = 240;
136 | }
137 |
--------------------------------------------------------------------------------
/boat/src/main/jni/loadme/loadme.c:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | JNIEXPORT void JNICALL Java_cosine_boat_LoadMe_redirectStdio(JNIEnv* env, jclass clazz, jstring path) {
11 | char const* file = (*env)->GetStringUTFChars(env, path, 0);
12 |
13 | int fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
14 | dup2(fd, 1);
15 | dup2(fd, 2);
16 |
17 | (*env)->ReleaseStringUTFChars(env, path, file);
18 | }
19 |
20 | JNIEXPORT jint JNICALL Java_cosine_boat_LoadMe_chdir(JNIEnv* env, jclass clazz, jstring path) {
21 | char const* dir = (*env)->GetStringUTFChars(env, path, 0);
22 |
23 | int b = chdir(dir);
24 |
25 | (*env)->ReleaseStringUTFChars(env, path, dir);
26 | return b;
27 | }
28 | JNIEXPORT void JNICALL Java_cosine_boat_LoadMe_setenv(JNIEnv* env, jclass clazz, jstring str1, jstring str2) {
29 | char const* name = (*env)->GetStringUTFChars(env, str1, 0);
30 | char const* value = (*env)->GetStringUTFChars(env, str2, 0);
31 |
32 | setenv(name, value, 1);
33 |
34 | (*env)->ReleaseStringUTFChars(env, str1, name);
35 | (*env)->ReleaseStringUTFChars(env, str2, value);
36 | }
37 |
38 | JNIEXPORT jint JNICALL Java_cosine_boat_LoadMe_dlopen(JNIEnv* env, jclass clazz, jstring str1) {
39 | dlerror();
40 |
41 | int ret = 0;
42 | char const* lib_name = (*env)->GetStringUTFChars(env, str1, 0);
43 |
44 | void* handle;
45 | dlerror();
46 | handle = dlopen(lib_name, RTLD_GLOBAL);
47 | __android_log_print(ANDROID_LOG_ERROR, "Boat", "loading %s (error = %s)", lib_name, dlerror());
48 |
49 | if (handle == NULL) {
50 | ret = -1;
51 | }
52 |
53 | (*env)->ReleaseStringUTFChars(env, str1, lib_name);
54 | return ret;
55 | }
56 |
57 | extern char** environ;
58 | JNIEXPORT int JNICALL Java_cosine_boat_LoadMe_dlexec(JNIEnv* env, jclass clazz, jobjectArray argsArray){
59 | dlerror();
60 |
61 | int argc = (*env)->GetArrayLength(env, argsArray);
62 | char* argv[argc];
63 | for (int i = 0; i < argc; i++) {
64 | jstring str = (*env)->GetObjectArrayElement(env, argsArray, i);
65 | int len = (*env)->GetStringUTFLength(env, str);
66 | char* buf = malloc(len + 1);
67 | int characterLen = (*env)->GetStringLength(env, str);
68 | (*env)->GetStringUTFRegion(env, str, 0, characterLen, buf);
69 | buf[len] = 0;
70 | argv[i] = buf;
71 | }
72 | char** envp = environ;
73 |
74 | jstring str0 = (*env)->GetObjectArrayElement(env, argsArray, 0);
75 | char const* lib_name = (*env)->GetStringUTFChars(env, str0, 0);
76 |
77 | void* handle;
78 | handle = dlopen(lib_name, RTLD_GLOBAL);
79 | __android_log_print(ANDROID_LOG_ERROR, "Boat", "loading %s (error = %s)", lib_name, dlerror());
80 | if (handle == NULL) {
81 | return -1;
82 | }
83 |
84 | int (*main_func)(int, char**, char**) = (int (*)())dlsym(handle, "main");
85 | __android_log_print(ANDROID_LOG_ERROR, "Boat", "getting main() in %s (error = %s)", lib_name, dlerror());
86 | if (main_func == NULL) {
87 | return -2;
88 | }
89 | int ret = main_func(argc, argv, envp);
90 | (*env)->ReleaseStringUTFChars(env, str0, lib_name);
91 | return ret;
92 | }
93 |
94 | #ifdef __aarch64__
95 | unsigned gen_ldr_pc(unsigned rt, signed long off) {
96 | // 33 222 2 22 2222111111111100000 00000
97 | // 10 987 6 54 3210987654321098765 43210
98 | // 01 011 0 00 1111111111111111011 00010
99 | // imm rt
100 | unsigned result = 0x0;
101 |
102 | result |= 0x58; // 01 011 0 00;
103 | result <<= 19;
104 |
105 | signed long imm = off / 4;
106 | imm &= 0x7ffff;
107 | result |= imm;
108 | result <<= 5;
109 |
110 | rt &= 0x1f;
111 | result |= rt;
112 |
113 | return result;
114 | }
115 | unsigned gen_ret(unsigned lr) {
116 | // 3322222 2 2 22 21111 1111 1 1 00000 00000
117 | // 1098765 4 3 21 09876 5432 1 0 98765 43210
118 | // 1101011 0 0 10 11111 0000 0 0 11110 00000
119 | // lr
120 | unsigned result = 0x0;
121 |
122 | result |= 0x3597c0; // 1101011 0 0 10 11111 0000 0 0
123 | result <<= 5;
124 | lr &= 0x1f;
125 | result |= lr;
126 | result <<= 5;
127 | result |= 0x0;
128 | return result;
129 | }
130 | unsigned gen_mov_reg(unsigned tr, unsigned sr) {
131 | // 3 32 22222 22 2 21111 111111 00000 00000
132 | // 1 09 87654 32 1 09876 543210 98765 43210
133 | // 1 01 01010 00 0 11110 000000 11111 00010
134 | // sr tr
135 | unsigned result = 0x0;
136 |
137 | result |= 0x550; // 1 01 01010 00 0
138 | result <<= 5;
139 |
140 | sr &= 0x1f;
141 | result |= sr;
142 | result <<= 11;
143 |
144 | result |= 0x1f; // 000000 11111
145 | result <<= 5;
146 |
147 | tr &= 0x1f;
148 | result |= tr;
149 |
150 | return result;
151 | }
152 |
153 | unsigned gen_mov_imm(unsigned tr, signed short imm) {
154 | // 3 32 222222 22 2111111111100000 00000
155 | // 1 09 876543 21 0987654321098765 43210
156 | // 1 10 100101 00 0000000001111111 00000
157 | // imm tr
158 | unsigned result = 0x0;
159 |
160 | result |= 0x694; // 1 10 100101 00
161 | result <<= 16;
162 |
163 | result |= imm;
164 | result <<= 5;
165 |
166 | tr &= 0x1f;
167 | result |= tr;
168 |
169 | return result;
170 | }
171 |
172 | void stub() {
173 |
174 | }
175 | #endif
176 | JNIEXPORT void JNICALL Java_cosine_boat_LoadMe_patchLinker(JNIEnv *env, jclass clazz) {
177 |
178 | #ifdef __aarch64__
179 | #define PAGE_START(x) ((void*)((unsigned long)(x) & ~((unsigned long)getpagesize() - 1)))
180 |
181 | void* libdl_handle = dlopen("libdl.so", RTLD_LAZY);
182 |
183 | unsigned* dlopen_addr = (unsigned*)dlsym(libdl_handle, "dlopen");
184 | unsigned* dlsym_addr = (unsigned*)dlsym(libdl_handle, "dlsym");
185 | unsigned* dlvsym_addr = (unsigned*)dlsym(libdl_handle, "dlvsym");
186 | unsigned* buffer = (unsigned*)dlsym(libdl_handle, "android_get_LD_LIBRARY_PATH");
187 | mprotect(PAGE_START(buffer), getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC );
188 | mprotect(PAGE_START(dlopen_addr), getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC );
189 | mprotect(PAGE_START(dlsym_addr), getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC );
190 | mprotect(PAGE_START(dlvsym_addr), getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC );
191 |
192 | unsigned ins_ret = gen_ret(30);
193 | unsigned ins_mov_x0_0 = gen_mov_imm(0, 0);
194 |
195 | buffer[0] = ins_mov_x0_0;
196 | buffer[1] = ins_ret;
197 |
198 | void** tmp_addr = (void**)(buffer + 2);
199 | *tmp_addr = stub;
200 |
201 | unsigned ins_mov_x2_x30 = gen_mov_reg(2, 30);
202 | unsigned ins_mov_x3_x30 = gen_mov_reg(3, 30);
203 |
204 | int dlopen_hooked = 0;
205 | int dlsym_hooked = 0;
206 | int dlvsym_hooked = 0;
207 | for (int i = 0; dlopen_addr[i] != ins_ret; i++){
208 | if (dlopen_addr[i] == ins_mov_x2_x30) {
209 | dlopen_addr[i] = gen_ldr_pc(2, (unsigned long)tmp_addr - (unsigned long)&dlopen_addr[i]);
210 | dlopen_hooked = 1;
211 | break;
212 | }
213 | }
214 | for (int i = 0; dlsym_addr[i] != ins_ret; i++){
215 | if (dlsym_addr[i] == ins_mov_x2_x30) {
216 | dlsym_addr[i] = gen_ldr_pc(2, (unsigned long)tmp_addr - (unsigned long)&dlsym_addr[i]);
217 | dlsym_hooked = 1;
218 | break;
219 | }
220 | }
221 | for (int i = 0; dlvsym_addr[i] != ins_ret; i++){
222 | if (dlvsym_addr[i] == ins_mov_x3_x30) {
223 | dlvsym_addr[i] = gen_ldr_pc(3, (unsigned long)tmp_addr - (unsigned long)&dlvsym_addr[i]);
224 | dlvsym_hooked = 1;
225 | break;
226 | }
227 | }
228 |
229 | if (dlopen_hooked == 0) {
230 | __android_log_print(ANDROID_LOG_ERROR, "Boat", "dlopen() not patched");
231 | }
232 | if (dlsym_hooked == 0) {
233 | __android_log_print(ANDROID_LOG_ERROR, "Boat", "dlsym() not patched");
234 | }
235 | if (dlvsym_hooked == 0) {
236 | __android_log_print(ANDROID_LOG_ERROR, "Boat", "dlvsym() not patched");
237 | }
238 | #undef PAGE_START
239 | #else // !__aarch64__
240 | __android_log_print(ANDROID_LOG_ERROR, "Boat", "Nothing to patch.");
241 | #endif // __aarch64__
242 | }
243 |
244 |
--------------------------------------------------------------------------------
/boat/src/main/java/cosine/boat/BoatActivity.java:
--------------------------------------------------------------------------------
1 | package cosine.boat;
2 |
3 | import android.view.MotionEvent;
4 | import android.os.Bundle;
5 | import android.app.Activity;
6 | import android.os.SystemClock;
7 | import android.widget.RelativeLayout;
8 | import android.view.View;
9 | import android.widget.EditText;
10 | import android.widget.TextView;
11 | import android.text.TextWatcher;
12 | import android.text.Editable;
13 | import android.view.inputmethod.EditorInfo;
14 | import android.view.KeyEvent;
15 | import android.view.TextureView;
16 | import android.graphics.SurfaceTexture;
17 | import android.view.Surface;
18 | import android.text.InputType;
19 | import java.util.Map;
20 | import java.util.HashMap;
21 |
22 | import com.mojang.minecraftpe.TextInputProxyEditTextbox;
23 |
24 | public abstract class BoatActivity extends Activity implements View.OnTouchListener, TextureView.SurfaceTextureListener
25 | {
26 | private static BoatActivity currentActivity = null;
27 |
28 | public static BoatActivity getCurrentInstance() {
29 | return currentActivity;
30 | }
31 |
32 | @Override
33 | protected void onStart() {
34 | super.onStart();
35 | currentActivity = this;
36 | }
37 |
38 | public TextInputProxyEditTextbox textInputWidget;
39 | public boolean textBoxShowing = false;
40 |
41 | public void showKeyboard() {
42 | BoatActivity.this.textBoxShowing = true;
43 | BoatActivity.this.textInputWidget.setVisibility(View.VISIBLE);
44 | RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) new RelativeLayout.LayoutParams(BoatActivity.this.textInputWidget.getLayoutParams());
45 | lp.leftMargin = baseX;
46 | lp.topMargin = baseY;
47 | BoatActivity.this.textInputWidget.setLayoutParams(lp);
48 |
49 | BoatActivity.this.textInputWidget.postDelayed(new Runnable() {
50 | public void run() {
51 | MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), 0, 0.0f, 0.0f, 0);
52 | BoatActivity.this.textInputWidget.dispatchTouchEvent(event);
53 | event.recycle();
54 | MotionEvent event2 = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), 1, 0.0f, 0.0f, 0);
55 | BoatActivity.this.textInputWidget.dispatchTouchEvent(event2);
56 | event2.recycle();
57 | BoatActivity.this.textInputWidget.setSelection(BoatActivity.this.textInputWidget.length());
58 | }
59 | }, 200);
60 | }
61 |
62 | public void hideKeyboard() {
63 | BoatActivity.this.textBoxShowing = false;
64 | BoatActivity.this.textInputWidget.setVisibility(View.GONE);
65 | }
66 |
67 | public abstract void findViews();
68 |
69 | public TextureView mainTextureView;
70 |
71 | @Override
72 | protected void onCreate(Bundle savedInstanceState) {
73 | super.onCreate(savedInstanceState);
74 |
75 | findViews();
76 |
77 | this.mainTextureView.setSurfaceTextureListener(this);
78 | //this.mainTextureView.setOpaque(false);
79 | this.mainTextureView.setOnTouchListener(this);
80 |
81 | this.textInputWidget.setFocusable(true);
82 | this.textInputWidget.setFocusableInTouchMode(true);
83 | this.textInputWidget.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL);
84 | this.textInputWidget.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_ACTION_DONE);
85 |
86 | this.textInputWidget.setOnEditorActionListener(new TextView.OnEditorActionListener() {
87 | public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
88 | BoatLib.pushEventKey(BoatKeycodes.KEY_ENTER, '\n', true);
89 | BoatLib.pushEventKey(BoatKeycodes.KEY_ENTER, '\n', false);
90 | return false;
91 |
92 | }
93 | });
94 | this.textInputWidget.addTextChangedListener(new TextWatcher() {
95 | public void onTextChanged(CharSequence s, int start, int before, int count) {
96 | }
97 |
98 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
99 | }
100 |
101 | public void afterTextChanged(Editable s) {
102 | String newText = s.toString();
103 | if (newText.length() > 0){
104 | for(int i = 0; i < newText.length(); i++){
105 | BoatLib.pushEventKey(0, newText.charAt(i), true);
106 | BoatLib.pushEventKey(0, newText.charAt(i), false);
107 | }
108 |
109 | BoatActivity.this.textInputWidget.setText("");
110 | }
111 | }
112 | });
113 | this.textInputWidget.setOnMCPEKeyWatcher(new TextInputProxyEditTextbox.MCPEKeyWatcher() {
114 | public void onDeleteKeyPressed() {
115 | BoatActivity.this.runOnUiThread(new Runnable() {
116 | public void run() {
117 | BoatLib.pushEventKey(BoatKeycodes.KEY_BACKSPACE, 0, true);
118 | BoatLib.pushEventKey(BoatKeycodes.KEY_BACKSPACE, 0, false);
119 | }
120 | });
121 | }
122 |
123 | public void onBackKeyPressed() {
124 | BoatActivity.this.runOnUiThread(new Runnable() {
125 | public void run() {
126 | hideKeyboard();
127 | }
128 | });
129 | }
130 | });
131 | }
132 |
133 | public int cursorMode = BoatLib.CursorEnabled;
134 |
135 | @Override
136 | public void onBackPressed() {
137 | BoatLib.pushEventMessage(BoatLib.CloseRequest);
138 | }
139 |
140 | private BoatTask task;
141 |
142 | @Override
143 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
144 | BoatLib.setBoatNativeWindow(new Surface(surface));
145 | BoatLib.setEventPipe();
146 |
147 | Map envvars = new HashMap<>();
148 | envvars.put(BoatScript.BOAT_ENV_WINDOW_WIDTH, Integer.toString(width));
149 | envvars.put(BoatScript.BOAT_ENV_WINDOW_HEIGHT, Integer.toString(height));
150 | envvars.put(BoatScript.BOAT_ENV_TMPDIR, this.getCacheDir().getAbsolutePath());
151 | this.task = new BoatTask(envvars, getIntent().getStringExtra(BoatTask.BOAT_TASK_SCRIPT_PATH)) {
152 | @Override
153 | public void afterExecute() {
154 | BoatActivity.this.finish();
155 | }
156 | };
157 | this.task.startTask();
158 | }
159 |
160 | @Override
161 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
162 | BoatLib.pushEventWindow(width, height);
163 | }
164 |
165 | @Override
166 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
167 | return false;
168 | }
169 |
170 | @Override
171 | public void onSurfaceTextureUpdated(SurfaceTexture surface) {
172 | // TODO: Implement this method
173 | }
174 |
175 | public abstract void setCursorViewPos(float x, float y);
176 | public abstract void setCursorViewMode(int mode);
177 |
178 | private int initialX;
179 | private int initialY;
180 | private int baseX;
181 | private int baseY;
182 |
183 | @Override
184 | public boolean onTouch(View view, MotionEvent event) {
185 | if (view == mainTextureView) {
186 | if (textBoxShowing != false) {
187 | hideKeyboard();
188 | }
189 | int currentX = (int)event.getX();
190 | int currentY = (int)event.getY();
191 | if (cursorMode == BoatLib.CursorDisabled) {
192 | switch(event.getActionMasked()) {
193 | case MotionEvent.ACTION_DOWN:
194 | initialX = currentX;
195 | initialY = currentY;
196 | case MotionEvent.ACTION_MOVE:
197 | BoatLib.pushEventPointer(baseX + currentX -initialX, baseY + currentY - initialY);
198 | break;
199 | case MotionEvent.ACTION_UP:
200 | baseX += (currentX - initialX);
201 | baseY += (currentY - initialY);
202 | BoatLib.pushEventPointer(baseX, baseY);
203 | break;
204 | default:
205 | break;
206 | }
207 | }
208 | else if (cursorMode == BoatLib.CursorEnabled) {
209 | baseX = currentX;
210 | baseY = currentY;
211 | BoatLib.pushEventPointer(baseX, baseY);
212 | }
213 | setCursorViewPos(event.getX(), event.getY());
214 | return true;
215 | }
216 | return false;
217 | }
218 | }
219 |
220 |
--------------------------------------------------------------------------------
/sample/src/main/java/cosine/boat/MinecraftActivity.java:
--------------------------------------------------------------------------------
1 | package cosine.boat;
2 |
3 | import android.view.MotionEvent;
4 | import android.os.Bundle;
5 | import android.app.Activity;
6 | import android.widget.PopupWindow;
7 | import android.widget.RelativeLayout;
8 | import android.view.LayoutInflater;
9 | import android.view.Gravity;
10 | import android.view.WindowManager.LayoutParams;
11 | import android.view.View;
12 | import android.widget.Button;
13 | import android.widget.ImageView;
14 | import java.nio.ByteBuffer;
15 | import android.widget.LinearLayout;
16 | import android.os.Handler;
17 | import android.os.Message;
18 | import android.widget.EditText;
19 | import android.widget.TextView;
20 | import android.text.TextWatcher;
21 | import android.text.Editable;
22 | import android.view.inputmethod.EditorInfo;
23 | import android.view.KeyEvent;
24 | import android.view.ViewGroup;
25 | import android.view.TextureView;
26 | import android.graphics.SurfaceTexture;
27 | import android.view.Surface;
28 | import android.os.SystemClock;
29 | import android.text.InputType;
30 | import android.util.Log;
31 |
32 | import cosine.boat.BoatActivity;
33 | import cosine.boat.BoatTask;
34 | import cosine.boat.BoatLib;
35 | import cosine.boat.BoatKeycodes;
36 |
37 | import com.mojang.minecraftpe.TextInputProxyEditTextbox;
38 |
39 | public class MinecraftActivity extends BoatActivity
40 | {
41 | @Override
42 | public void findViews() {
43 | setContentView(R.layout.minecraft_layout);
44 |
45 | base = (RelativeLayout)this.findViewById(R.id.main_base);
46 | mainTextureView = this.findViewById(R.id.main_surface);
47 | textInputWidget = this.findViewById(R.id.text_box);
48 | mouseCursor = (ImageView)findViewById(R.id.mouse_cursor);
49 | controlUp = this.findButton(R.id.control_up);
50 | controlDown = this.findButton(R.id.control_down);
51 | controlLeft = this.findButton(R.id.control_left);
52 | controlRight = this.findButton(R.id.control_right);
53 | controlJump = this.findButton(R.id.control_jump);
54 | controlInv = this.findButton(R.id.control_inventory);
55 | controlLshift = this.findButton(R.id.control_lshift);
56 | control1 = this.findButton(R.id.control_1);
57 | control2 = this.findButton(R.id.control_2);
58 | control3 = this.findButton(R.id.control_3);
59 | control4 = this.findButton(R.id.control_4);
60 | control5 = this.findButton(R.id.control_5);
61 | control6 = this.findButton(R.id.control_6);
62 | control7 = this.findButton(R.id.control_7);
63 | control8 = this.findButton(R.id.control_8);
64 | control9 = this.findButton(R.id.control_9);
65 |
66 | itemBar = (LinearLayout)findViewById(R.id.item_bar);
67 | mousePrimary = this.findButton(R.id.mouse_primary);
68 | mouseSecondary = this.findButton(R.id.mouse_secondary);
69 | esc = this.findButton(R.id.esc);
70 | showIME = this.findButton(R.id.show_ime);
71 | controlDebug = this.findButton(R.id.control_debug);
72 | controlCommand = this.findButton(R.id.control_command);
73 | control3rd = this.findButton(R.id.control_3rd);
74 | }
75 |
76 | @Override
77 | protected void onCreate(Bundle savedInstanceState) {
78 | super.onCreate(savedInstanceState);
79 | mHandler = new MyHandler();
80 | base.setSystemUiVisibility(BOAT_SYSTEM_UI_FLAGS);
81 | }
82 |
83 | public static final int BOAT_SYSTEM_UI_FLAGS =
84 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
85 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
86 | View.SYSTEM_UI_FLAG_FULLSCREEN |
87 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
88 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
89 | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
90 |
91 | @Override
92 | protected void onResume() {
93 | super.onResume();
94 | base.setSystemUiVisibility(BOAT_SYSTEM_UI_FLAGS);
95 | }
96 |
97 | @Override
98 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
99 | runOnUiThread(new Runnable() {
100 | @Override
101 | public void run() {
102 | int scale = 1;
103 | while(width / (scale + 1) >= 320 && height / (scale + 1) >= 240) {
104 | scale++;
105 | }
106 | RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)itemBar.getLayoutParams();
107 | lp.height = 20 * scale;
108 | lp.width = 20 * scale * 9;
109 | itemBar.setLayoutParams(lp);
110 | }
111 | });
112 | super.onSurfaceTextureAvailable(surface, width, height);
113 | }
114 |
115 | @Override
116 | public void setCursorViewMode(int mode) {
117 | Message msg = new Message();
118 | msg.what = mode;
119 | mHandler.sendMessage(msg);
120 | }
121 |
122 | @Override
123 | public void setCursorViewPos(float x, float y) {
124 | mouseCursor.setX(x);
125 | mouseCursor.setY(y);
126 | }
127 |
128 | private RelativeLayout base;
129 | private Button controlUp;
130 | private Button controlDown;
131 | private Button controlLeft;
132 | private Button controlRight;
133 | private Button controlJump;
134 | private Button controlInv;
135 | private Button controlLshift;
136 | private Button control1;
137 | private Button control2;
138 | private Button control3;
139 | private Button control4;
140 | private Button control5;
141 | private Button control6;
142 | private Button control7;
143 | private Button control8;
144 | private Button control9;
145 | private LinearLayout itemBar;
146 | private Button mousePrimary;
147 | private Button mouseSecondary;
148 | private Button controlDebug;
149 | private Button controlCommand;
150 | private Button control3rd;
151 | private ImageView mouseCursor;
152 | private Button esc;
153 | private Button showIME;
154 |
155 | private Button findButton(int id) {
156 | Button b = (Button)findViewById(id);
157 | b.setOnTouchListener(this);
158 | return b;
159 | }
160 |
161 | private class MyHandler extends Handler {
162 | @Override
163 | public void handleMessage(Message msg) {
164 | switch (msg.what) {
165 | case BoatLib.CursorDisabled:
166 | hideKeyboard();
167 | MinecraftActivity.this.mouseCursor.setVisibility(View.INVISIBLE);
168 | MinecraftActivity.this.itemBar.setVisibility(View.VISIBLE);
169 | MinecraftActivity.this.cursorMode = BoatLib.CursorDisabled;
170 | break;
171 |
172 | case BoatLib.CursorEnabled:
173 | MinecraftActivity.this.mouseCursor.setVisibility(View.VISIBLE);
174 | MinecraftActivity.this.itemBar.setVisibility(View.INVISIBLE);
175 | MinecraftActivity.this.cursorMode = BoatLib.CursorEnabled;
176 | break;
177 | default:
178 | MinecraftActivity.this.finish();
179 | break;
180 | }
181 | }
182 | }
183 |
184 | private MyHandler mHandler;
185 |
186 | @Override
187 | public boolean onTouch(View p1, MotionEvent p2) {
188 | if (p1 == showIME) {
189 | if (p2.getActionMasked() == MotionEvent.ACTION_UP && this.cursorMode == BoatLib.CursorEnabled) {
190 | this.showKeyboard();
191 | }
192 | return false;
193 | }
194 |
195 | if (super.onTouch(p1, p2)) {
196 | return true;
197 | }
198 |
199 | if (p2.getActionMasked() != MotionEvent.ACTION_DOWN && p2.getActionMasked() != MotionEvent.ACTION_UP) {
200 | return false;
201 | }
202 |
203 | boolean down = p2.getActionMasked() == MotionEvent.ACTION_DOWN;
204 | if (p1 == mousePrimary) {
205 | BoatLib.pushEventMouseButton(BoatLib.Button1, down);
206 | return false;
207 | }
208 | if (p1 == mouseSecondary) {
209 | BoatLib.pushEventMouseButton(BoatLib.Button3, down);
210 | return false;
211 | }
212 | if (p1 == controlUp) {
213 | BoatLib.pushEventKey(BoatKeycodes.KEY_W, 0, down);
214 | return false;
215 | }
216 | if (p1 == controlDown) {
217 | BoatLib.pushEventKey(BoatKeycodes.KEY_S, 0, down);
218 | return false;
219 | }
220 | if (p1 == controlLeft) {
221 | BoatLib.pushEventKey(BoatKeycodes.KEY_A, 0, down);
222 | return false;
223 | }
224 | if (p1 == controlRight) {
225 | BoatLib.pushEventKey(BoatKeycodes.KEY_D, 0, down);
226 | return false;
227 | }
228 | if (p1 == controlJump) {
229 | BoatLib.pushEventKey(BoatKeycodes.KEY_SPACE, 0, down);
230 | return false;
231 | }
232 | if (p1 == esc) {
233 | BoatLib.pushEventKey(BoatKeycodes.KEY_ESC, 0, down);
234 | return false;
235 | }
236 | if (p1 == controlInv) {
237 | BoatLib.pushEventKey(BoatKeycodes.KEY_E, 0, down);
238 | return false;
239 | }
240 | if (p1 == controlLshift) {
241 | BoatLib.pushEventKey(BoatKeycodes.KEY_LEFTSHIFT, 0, down);
242 | return false;
243 | }
244 | if (p1 == control1) {
245 | BoatLib.pushEventKey(BoatKeycodes.KEY_1, 0, down);
246 | return false;
247 | }
248 | if (p1 == control2) {
249 | BoatLib.pushEventKey(BoatKeycodes.KEY_2, 0, down);
250 | return false;
251 | }
252 | if (p1 == control3) {
253 | BoatLib.pushEventKey(BoatKeycodes.KEY_3, 0, down);
254 | return false;
255 | }
256 | if (p1 == control4) {
257 | BoatLib.pushEventKey(BoatKeycodes.KEY_4, 0, down);
258 | return false;
259 | }
260 | if (p1 == control5) {
261 | BoatLib.pushEventKey(BoatKeycodes.KEY_5, 0, down);
262 | return false;
263 | }
264 | if (p1 == control6) {
265 | BoatLib.pushEventKey(BoatKeycodes.KEY_6, 0, down);
266 | return false;
267 | }
268 | if (p1 == control7) {
269 | BoatLib.pushEventKey(BoatKeycodes.KEY_7, 0, down);
270 | return false;
271 | }
272 | if (p1 == control8) {
273 | BoatLib.pushEventKey(BoatKeycodes.KEY_8, 0, down);
274 | return false;
275 | }
276 | if (p1 == control9) {
277 | BoatLib.pushEventKey(BoatKeycodes.KEY_9, 0, down);
278 | return false;
279 | }
280 | if (p1 == controlDebug) {
281 | BoatLib.pushEventKey(BoatKeycodes.KEY_F3, 0, down);
282 | return false;
283 | }
284 | if (p1 == controlCommand) {
285 | BoatLib.pushEventKey(BoatKeycodes.KEY_SLASH, 0, down);
286 | return false;
287 | }
288 | if (p1 == control3rd) {
289 | BoatLib.pushEventKey(BoatKeycodes.KEY_F5, 0, down);
290 | return false;
291 | }
292 | return false;
293 | }
294 | }
295 |
296 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/minecraft_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
17 |
18 |
23 |
24 |
38 |
39 |
50 |
51 |
62 |
63 |
74 |
75 |
86 |
87 |
98 |
99 |
110 |
111 |
122 |
123 |
134 |
135 |
146 |
147 |
159 |
160 |
167 |
168 |
175 |
176 |
183 |
184 |
191 |
192 |
199 |
200 |
207 |
208 |
215 |
216 |
223 |
224 |
231 |
232 |
233 |
234 |
245 |
246 |
257 |
258 |
269 |
270 |
281 |
282 |
293 |
294 |
295 |
296 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C)
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | , 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------