diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 3d5220f60..7939562ee 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -8,16 +8,12 @@
+
+
+
-
-
-
-
-
diff --git a/android/app/src/main/java/com/classicube/MainActivity.java b/android/app/src/main/java/com/classicube/MainActivity.java
index 6726485ff..8972dc577 100644
--- a/android/app/src/main/java/com/classicube/MainActivity.java
+++ b/android/app/src/main/java/com/classicube/MainActivity.java
@@ -130,7 +130,15 @@ public class MainActivity extends Activity implements SurfaceHolder.Callback2 {
void startGameAsync() {
Log.i("CC_WIN", "handing off to native..");
- System.loadLibrary("classicube");
+ try {
+ System.loadLibrary("classicube");
+ } catch (UnsatisfiedLinkError ex) {
+ ex.printStackTrace();
+ showAlert("Failed to start", ex.getMessage());
+ return;
+ }
+
+ gameRunning = true;
runGameAsync();
}
@@ -157,7 +165,6 @@ public class MainActivity extends Activity implements SurfaceHolder.Callback2 {
}
if (!gameRunning) startGameAsync();
- gameRunning = true;
super.onCreate(savedInstanceState);
}
@@ -392,7 +399,7 @@ public class MainActivity extends Activity implements SurfaceHolder.Callback2 {
setContentView(curView);
curView.requestFocus();
- if (fullscreen) goFullscreen();
+ if (fullscreen) setUIVisibility(FULLSCREEN_FLAGS);
}
class LauncherView extends SurfaceView {
@@ -516,7 +523,10 @@ public class MainActivity extends Activity implements SurfaceHolder.Callback2 {
//ApplicationInfo info = getPackageManager().getApplicationInfo(name, 0);
ApplicationInfo info = getApplicationInfo();
File apkFile = new File(info.sourceDir);
- return apkFile.lastModified();
+
+ // https://developer.android.com/reference/java/io/File#lastModified()
+ // lastModified is returned in milliseconds
+ return apkFile.lastModified() / 1000;
} catch (Exception ex) {
return 0;
}
@@ -606,7 +616,7 @@ public class MainActivity extends Activity implements SurfaceHolder.Callback2 {
//final Activity activity = this;
runOnUiThread(new Runnable() {
public void run() {
- AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.this, AlertDialog.THEME_HOLO_DARK);
+ AlertDialog.Builder dlg = new AlertDialog.Builder(MainActivity.this);
dlg.setTitle(title);
dlg.setMessage(message);
dlg.setPositiveButton("Close", new DialogInterface.OnClickListener() {
@@ -623,29 +633,30 @@ public class MainActivity extends Activity implements SurfaceHolder.Callback2 {
// TODO: this fails because multiple dialog boxes show
}
- public int getWindowState() {
- return fullscreen ? 1 : 0;
- }
-
- void goFullscreen() {
- curView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
+ public int getWindowState() { return fullscreen ? 1 : 0; }
+ final static int FULLSCREEN_FLAGS = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+
+ void setUIVisibility(int flags) {
+ if (curView == null) return;
+ try {
+ curView.setSystemUiVisibility(flags);
+ } catch (NoSuchMethodError ex) {
+ // Not available on API < 11 (Android 3.0)
+ ex.printStackTrace();
+ }
}
public void enterFullscreen() {
fullscreen = true;
runOnUiThread(new Runnable() {
- public void run() {
- if (curView != null) goFullscreen();
- }
+ public void run() { setUIVisibility(FULLSCREEN_FLAGS); }
});
}
public void exitFullscreen() {
fullscreen = false;
runOnUiThread(new Runnable() {
- public void run() {
- if (curView != null) curView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
- }
+ public void run() { setUIVisibility(View.SYSTEM_UI_FLAG_VISIBLE); }
});
}
@@ -703,16 +714,19 @@ public class MainActivity extends Activity implements SurfaceHolder.Callback2 {
int len;
try {
conn.connect();
- Map> all = conn.getHeaderFields();
-
- for (Map.Entry> h : all.entrySet()) {
- String key = h.getKey();
- for (String value : h.getValue()) {
- if (key == null) {
- httpParseHeader(value);
- } else {
- httpParseHeader(key + ":" + value);
- }
+ // Some implementations also provide this as getHeaderField(0), but some don't
+ httpParseHeader("HTTP/1.1 " + conn.getResponseCode() + " MSG");
+
+ // Legitimate webservers aren't going to reply with over 200 headers
+ for (int i = 0; i < 200; i++) {
+ String key = conn.getHeaderFieldKey(i);
+ String val = conn.getHeaderField(i);
+ if (key == null && val == null) break;
+
+ if (key == null) {
+ httpParseHeader(val);
+ } else {
+ httpParseHeader(key + ":" + val);
}
}
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ccicon.png
similarity index 100%
rename from android/app/src/main/res/mipmap-hdpi/ic_launcher.png
rename to android/app/src/main/res/mipmap-hdpi/ccicon.png
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ccicon.png
similarity index 100%
rename from android/app/src/main/res/mipmap-mdpi/ic_launcher.png
rename to android/app/src/main/res/mipmap-mdpi/ccicon.png
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ccicon.png
similarity index 100%
rename from android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
rename to android/app/src/main/res/mipmap-xhdpi/ccicon.png
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ccicon.png
similarity index 100%
rename from android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
rename to android/app/src/main/res/mipmap-xxhdpi/ccicon.png
diff --git a/misc/build_server.py b/misc/build_server.py
new file mode 100644
index 000000000..469d2c101
--- /dev/null
+++ b/misc/build_server.py
@@ -0,0 +1,64 @@
+from http.server import HTTPServer, SimpleHTTPRequestHandler
+import os
+import subprocess
+
+build_files = {
+ '/ClassiCube.exe' : 'cc-w32-d3d.exe', '/ClassiCube.opengl.exe' : 'cc-w32-ogl.exe',
+ '/ClassiCube.64.exe' : 'cc-w64-d3d.exe', '/ClassiCube.64-opengl.exe' : 'cc-w64-ogl.exe',
+ '/ClassiCube.32' : 'cc-nix32', '/ClassiCube' : 'cc-nix64',
+ '/ClassiCube.osx' : 'cc-osx32', '/ClassiCube.64.osx' : 'cc-osx64',
+ '/ClassiCube.js' : 'cc.js', '/ClassiCube.apk' : 'cc.apk',
+ '/ClassiCube.rpi' : 'cc-rpi',
+}
+
+release_files = {
+ '/win32' : 'win32/ClassiCube.exe', '/win64' : 'win64/ClassiCube.exe',
+ '/osx32' : 'osx32/ClassiCube.tar.gz', '/osx64' : 'osx64/ClassiCube.tar.gz',
+ '/mac32' : 'mac32/ClassiCube.tar.gz', '/mac64' : 'mac64/ClassiCube.tar.gz',
+ '/nix32' : 'nix32/ClassiCube.tar.gz', '/nix64' : 'nix64/ClassiCube.tar.gz',
+ '/rpi32' : 'rpi32/ClassiCube.tar.gz',
+}
+
+def run_script(file):
+ args = ["sh", file]
+ su = subprocess.Popen(args)
+ return su.wait()
+
+class Handler(SimpleHTTPRequestHandler):
+
+ def serve_script(self, file, msg):
+ ret = run_script(file)
+ self.send_response(200)
+ self.end_headers()
+ self.wfile.write(msg % ret)
+
+ def do_GET(self):
+ if self.path in build_files:
+ self.serve_exe('client/src/' + build_files[self.path])
+ elif self.path in release_files:
+ self.serve_exe('client/release/' + release_files[self.path])
+ elif self.path == '/rebuild':
+ self.serve_script('build.sh', 'Rebuild client (%s)')
+ elif self.path == '/rebuild_android':
+ self.serve_script('build_android.sh', 'Rebuild android (%s)')
+ elif self.path == '/release':
+ self.serve_script('build_release.sh', 'Package release (%s)')
+ else:
+ self.send_error(404, "Unknown action")
+ return
+
+ def serve_exe(self, path):
+ try:
+ f = open(path, 'rb')
+ fs = os.fstat(f.fileno())
+ self.send_response(200)
+ self.send_header("Content-type", "application/octet-stream")
+ self.send_header("Content-Length", str(fs[6]))
+ self.end_headers()
+ self.copyfile(f, self.wfile)
+ f.close()
+ except IOError:
+ self.send_error(404, "File not found")
+
+httpd = HTTPServer(('', 80), Handler)
+httpd.serve_forever()
diff --git a/misc/buildbot_android.sh b/misc/buildbot_android.sh
new file mode 100644
index 000000000..498b7cc0a
--- /dev/null
+++ b/misc/buildbot_android.sh
@@ -0,0 +1,54 @@
+FLAGS="-fPIC -shared -s -O1 -fvisibility=hidden -rdynamic"
+LIBS="-lGLESv2 -lEGL -lm -landroid -llog"
+NDK_ROOT="/home/buildbot/android/android-ndk-r22/toolchains/llvm/prebuilt/linux-x86_64/bin"
+TOOLS_ROOT="/home/buildbot/android/sdk/build-tools/26.0.0"
+SDK_ROOT="/home/buildbot/android/sdk/platforms/android-26"
+
+cd /home/buildbot/client/src
+$NDK_ROOT/armv7a-linux-androideabi16-clang *.c $FLAGS -march=armv5 $LIBS -o cc-droid-arm_16
+$NDK_ROOT/armv7a-linux-androideabi16-clang *.c $FLAGS $LIBS -o cc-droid-arm_32
+$NDK_ROOT/aarch64-linux-android21-clang *.c $FLAGS $LIBS -o cc-droid-arm_64
+$NDK_ROOT/i686-linux-android16-clang *.c $FLAGS $LIBS -o cc-droid-x86_32
+$NDK_ROOT/x86_64-linux-android21-clang *.c $FLAGS $LIBS -o cc-droid-x86_64
+
+cd ../android/app/src/main
+# remove old java temp files
+rm -rf obj
+mkdir obj
+rm classes.dex
+
+# copy required native libraries
+rm -rf lib
+mkdir lib
+mkdir lib/armeabi
+mkdir lib/armeabi-v7a
+mkdir lib/arm64-v8a
+mkdir lib/x86
+mkdir lib/x86_64
+cp ~/client/src/cc-droid-arm_16 lib/armeabi/libclassicube.so
+cp ~/client/src/cc-droid-arm_32 lib/armeabi-v7a/libclassicube.so
+cp ~/client/src/cc-droid-arm_64 lib/arm64-v8a/libclassicube.so
+cp ~/client/src/cc-droid-x86_32 lib/x86/libclassicube.so
+cp ~/client/src/cc-droid-x86_64 lib/x86_64/libclassicube.so
+
+# The following commands are for manually building an .apk, see
+# https://spin.atomicobject.com/2011/08/22/building-android-application-bundles-apks-by-hand/
+# https://github.com/cnlohr/rawdrawandroid/blob/master/Makefile
+# https://stackoverflow.com/questions/41132753/how-can-i-build-an-android-apk-without-gradle-on-the-command-line
+# https://github.com/skanti/Android-Manual-Build-Command-Line/blob/master/hello-jni/Makefile
+# https://github.com/skanti/Android-Manual-Build-Command-Line/blob/master/hello-jni/CMakeLists.txt
+
+# compile interop java file into its multiple .class files
+javac java/com/classicube/MainActivity.java -d ./obj -classpath $SDK_ROOT/android.jar
+# compile the multiple .class files into one .dex file
+$TOOLS_ROOT/dx --dex --output=obj/classes.dex ./obj
+# create initial .apk with packaged version of resources
+$TOOLS_ROOT/aapt package -f -M AndroidManifest.xml -S res -F obj/cc-unsigned.apk -I $SDK_ROOT/android.jar
+# and add all the required files
+cp obj/classes.dex classes.dex
+$TOOLS_ROOT/aapt add -f obj/cc-unsigned.apk classes.dex lib/armeabi/libclassicube.so lib/armeabi-v7a/libclassicube.so lib/arm64-v8a/libclassicube.so lib/x86/libclassicube.so lib/x86_64/libclassicube.so
+# sign the apk with debug key (https://stackoverflow.com/questions/16711233/)
+cp obj/cc-unsigned.apk obj/cc-signed.apk
+jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore debug.keystore -storepass android -keypass android obj/cc-signed.apk androiddebugkey
+# create aligned .apk file
+$TOOLS_ROOT/zipalign -f -v 4 obj/cc-signed.apk ~/client/src/cc.apk
\ No newline at end of file
diff --git a/misc/notify.py b/misc/notify.py
index a4866e9b2..b2c02a9ca 100644
--- a/misc/notify.py
+++ b/misc/notify.py
@@ -17,22 +17,23 @@ def notify_webhook(body):
"content" : body
}
r = requests.post(WEBHOOK_URL, json=webhook_data)
- print("BuildNotify response: " + r.text)
+ print("Webhook response: " + r.text)
except Exception as e:
- print("BuildNotify failed: %s" % (e))
+ print("Webhook failed: %s" % (e))
def build_exists(file):
return os.path.exists('client/src/' + file)
builds = {
- 'Win32' : build_exists('cc-w32-d3d.exe'),
- 'Win64' : build_exists('cc-w64-d3d.exe'),
- 'Mac32' : build_exists('cc-osx32'),
- 'Mac64' : build_exists('cc-osx64'),
- 'Nix32' : build_exists('cc-nix32'),
- 'Nix64' : build_exists('cc-nix64'),
- 'Rpi' : build_exists('cc-rpi'),
- 'Web' : build_exists('cc.js'),
+ 'Windows32' : build_exists('cc-w32-d3d.exe'),
+ 'Windows64' : build_exists('cc-w64-d3d.exe'),
+ 'macOS32' : build_exists('cc-osx32'),
+ 'macOS64' : build_exists('cc-osx64'),
+ 'Linux32' : build_exists('cc-nix32'),
+ 'Linux64' : build_exists('cc-nix64'),
+ 'RPi' : build_exists('cc-rpi'),
+ 'Web' : build_exists('cc.js'),
+ 'Android' : build_exists('cc.apk'),
'Win-ogl32' : build_exists('cc-w32-ogl.exe'),
'Win-ogl64' : build_exists('cc-w64-ogl.exe'),
}
@@ -48,17 +49,19 @@ def check_build(key):
failed.append(key)
else:
if not builds[key_32] and not builds[key_64]:
- failed.append(key + '32/64')
+ failed.append(key + ' 32/64')
elif not builds[key_32]:
- failed.append(key_32)
+ failed.append(key + ' 32')
elif not builds[key_64]:
- failed.append(key_64)
+ failed.append(key + ' 64')
-check_build('Win')
-check_build('Mac')
-check_build('Nix')
-check_build('Rpi')
+check_build('Windows')
+check_build('macOS')
+check_build('Linux')
+check_build('RPi')
check_build('Web')
+check_build('Android')
+check_build('Win-ogl')
if len(failed):
- notify_webhook('<@%s>, failed to compile for: %s' % (TARGET_USER, ', '.join(failed)))
\ No newline at end of file
+ notify_webhook('<@%s>, failed to compile for: %s' % (TARGET_USER, ', '.join(failed)))
diff --git a/readme.md b/readme.md
index 49d6d079e..f3855e248 100644
--- a/readme.md
+++ b/readme.md
@@ -1,4 +1,4 @@
-ClassiCube is a custom Minecraft Classic and ClassiCube client written in C that works on Windows, OSX, Linux, BSD, Solaris, Haiku, and in a browser.
+ClassiCube is a custom Minecraft Classic and ClassiCube client written in C that works on Windows, macOS, Linux, Android, BSD, Solaris, Haiku, and in a browser.
**It is not affiliated with (or supported by) Mojang AB, Minecraft, or Microsoft in any way.**

@@ -14,9 +14,10 @@ You can grab the latest stable binaries [from here](https://www.classicube.net/d
It **does not** work with 'modern/premium' Minecraft servers.
#### Requirements
-* Windows: 2000 or later. (Windows 98 with KernelEx also *technically* works)
-* macOS: macOS 10.5 or later. (Can be compiled to work with 10.3/10.4 though)
+* Windows: 98 or later.
+* macOS: 10.5 or later. (Can be compiled to work with 10.3/10.4 though)
* Linux: libcurl and libopenal.
+* Android: 2.3 or later.
**Note:** When running from within VirtualBox, disable Mouse Integration, otherwise the camera will not work properly.
diff --git a/src/Audio.c b/src/Audio.c
index c8132b391..81b2d2ca0 100644
--- a/src/Audio.c
+++ b/src/Audio.c
@@ -17,6 +17,8 @@
#include "Window.h"
#endif
+#define QUOTE(x) #x
+#define DefineDynFunc(sym) { QUOTE(sym), (void**)&_ ## sym }
int Audio_SoundsVolume, Audio_MusicVolume;
#if defined CC_BUILD_NOAUDIO
@@ -129,20 +131,18 @@ static const cc_string alLib = String_FromConst("libopenal.so");
static const cc_string alLib = String_FromConst("libopenal.so.1");
#endif
-#define QUOTE(x) #x
-#define DefineALFunc(sym) { QUOTE(sym), (void**)&_ ## sym }
static cc_bool LoadALFuncs(void) {
static const struct DynamicLibSym funcs[18] = {
- DefineALFunc(alcCreateContext), DefineALFunc(alcMakeContextCurrent),
- DefineALFunc(alcDestroyContext), DefineALFunc(alcOpenDevice),
- DefineALFunc(alcCloseDevice), DefineALFunc(alcGetError),
+ DefineDynFunc(alcCreateContext), DefineDynFunc(alcMakeContextCurrent),
+ DefineDynFunc(alcDestroyContext), DefineDynFunc(alcOpenDevice),
+ DefineDynFunc(alcCloseDevice), DefineDynFunc(alcGetError),
- DefineALFunc(alGetError), DefineALFunc(alGenSources),
- DefineALFunc(alDeleteSources), DefineALFunc(alGetSourcei),
- DefineALFunc(alSourcePlay), DefineALFunc(alSourceStop),
- DefineALFunc(alSourceQueueBuffers), DefineALFunc(alSourceUnqueueBuffers),
- DefineALFunc(alGenBuffers), DefineALFunc(alDeleteBuffers),
- DefineALFunc(alBufferData), DefineALFunc(alDistanceModel)
+ DefineDynFunc(alGetError), DefineDynFunc(alGenSources),
+ DefineDynFunc(alDeleteSources), DefineDynFunc(alGetSourcei),
+ DefineDynFunc(alSourcePlay), DefineDynFunc(alSourceStop),
+ DefineDynFunc(alSourceQueueBuffers), DefineDynFunc(alSourceUnqueueBuffers),
+ DefineDynFunc(alGenBuffers), DefineDynFunc(alDeleteBuffers),
+ DefineDynFunc(alBufferData), DefineDynFunc(alDistanceModel)
};
void* lib = DynamicLib_Load2(&alLib);
@@ -390,22 +390,51 @@ struct AudioContext {
SLBufferQueueItf bqPlayerQueue;
};
+static SLresult (SLAPIENTRY *_slCreateEngine)(
+ SLObjectItf *pEngine,
+ SLuint32 numOptions,
+ const SLEngineOption *pEngineOptions,
+ SLuint32 numInterfaces,
+ const SLInterfaceID *pInterfaceIds,
+ const SLboolean *pInterfaceRequired
+);
+static SLInterfaceID* _SL_IID_NULL;
+static SLInterfaceID* _SL_IID_PLAY;
+static SLInterfaceID* _SL_IID_ENGINE;
+static SLInterfaceID* _SL_IID_BUFFERQUEUE;
+static const cc_string slLib = String_FromConst("libOpenSLES.so");
+
+static cc_bool LoadSLFuncs(void) {
+ static const struct DynamicLibSym funcs[5] = {
+ DefineDynFunc(slCreateEngine), DefineDynFunc(SL_IID_NULL),
+ DefineDynFunc(SL_IID_PLAY), DefineDynFunc(SL_IID_ENGINE),
+ DefineDynFunc(SL_IID_BUFFERQUEUE)
+ };
+
+ void* lib = DynamicLib_Load2(&slLib);
+ if (!lib) { Logger_DynamicLibWarn("loading", &slLib); return false; }
+ return DynamicLib_GetAll(lib, funcs, Array_Elems(funcs));
+}
+
static cc_bool Backend_Init(void) {
+ static const cc_string msg = String_FromConst("Failed to init OpenSLES. No audio will play.");
SLInterfaceID ids[1];
SLboolean req[1];
SLresult res;
if (slEngineObject) return true;
- /* mixer doesn't use any effects */
- ids[0] = SL_IID_NULL; req[0] = SL_BOOLEAN_FALSE;
+ if (!LoadSLFuncs()) { Logger_WarnFunc(&msg); return false; }
- res = slCreateEngine(&slEngineObject, 0, NULL, 0, NULL, NULL);
+ /* mixer doesn't use any effects */
+ ids[0] = *_SL_IID_NULL; req[0] = SL_BOOLEAN_FALSE;
+
+ res = _slCreateEngine(&slEngineObject, 0, NULL, 0, NULL, NULL);
if (res) { Logger_SimpleWarn(res, "creating OpenSL ES engine"); return false; }
res = (*slEngineObject)->Realize(slEngineObject, SL_BOOLEAN_FALSE);
if (res) { Logger_SimpleWarn(res, "realising OpenSL ES engine"); return false; }
- res = (*slEngineObject)->GetInterface(slEngineObject, SL_IID_ENGINE, &slEngineEngine);
+ res = (*slEngineObject)->GetInterface(slEngineObject, *_SL_IID_ENGINE, &slEngineEngine);
if (res) { Logger_SimpleWarn(res, "initing OpenSL ES engine"); return false; }
res = (*slEngineEngine)->CreateOutputMix(slEngineEngine, &slOutputObject, 1, ids, req);
@@ -489,16 +518,16 @@ static cc_result Backend_SetFormat(struct AudioContext* ctx, struct AudioFormat*
dst.pLocator = &output;
dst.pFormat = NULL;
- ids[0] = SL_IID_BUFFERQUEUE; req[0] = SL_BOOLEAN_TRUE;
- ids[1] = SL_IID_PLAY; req[1] = SL_BOOLEAN_TRUE;
+ ids[0] = *_SL_IID_BUFFERQUEUE; req[0] = SL_BOOLEAN_TRUE;
+ ids[1] = *_SL_IID_PLAY; req[1] = SL_BOOLEAN_TRUE;
res = (*slEngineEngine)->CreateAudioPlayer(slEngineEngine, &bqPlayerObject, &src, &dst, 2, ids, req);
ctx->bqPlayerObject = bqPlayerObject;
if (res) return res;
- if ((res = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE))) return res;
- if ((res = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &ctx->bqPlayerPlayer))) return res;
- if ((res = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, &ctx->bqPlayerQueue))) return res;
+ if ((res = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE))) return res;
+ if ((res = (*bqPlayerObject)->GetInterface(bqPlayerObject, *_SL_IID_PLAY, &ctx->bqPlayerPlayer))) return res;
+ if ((res = (*bqPlayerObject)->GetInterface(bqPlayerObject, *_SL_IID_BUFFERQUEUE, &ctx->bqPlayerQueue))) return res;
return (*ctx->bqPlayerQueue)->RegisterCallback(ctx->bqPlayerQueue, OnBufferFinished, ctx);
}
diff --git a/src/Constants.h b/src/Constants.h
index cf3bd3df6..db44f942b 100644
--- a/src/Constants.h
+++ b/src/Constants.h
@@ -5,7 +5,7 @@
*/
#define GAME_MAX_CMDARGS 5
-#define GAME_APP_VER "1.2.3"
+#define GAME_APP_VER "1.2.4"
#define GAME_API_VER 1
#if defined CC_BUILD_WEB
@@ -13,14 +13,14 @@
#define GAME_APP_NAME "ClassiCube web"
#define GAME_APP_TITLE "ClassiCube"
#elif defined CC_BUILD_COCOA
-#define GAME_APP_NAME "ClassiCube 1.2.3 alpha"
-#define GAME_APP_TITLE "ClassiCube 1.2.3 alpha"
+#define GAME_APP_NAME "ClassiCube 1.2.4 alpha"
+#define GAME_APP_TITLE "ClassiCube 1.2.4 alpha"
#elif defined CC_BUILD_ANDROID
-#define GAME_APP_NAME "ClassiCube 1.2.3 android alpha"
-#define GAME_APP_TITLE "ClassiCube 1.2.3 android alpha"
+#define GAME_APP_NAME "ClassiCube 1.2.4 android alpha"
+#define GAME_APP_TITLE "ClassiCube 1.2.4 android alpha"
#else
-#define GAME_APP_NAME "ClassiCube 1.2.3"
-#define GAME_APP_TITLE "ClassiCube 1.2.3"
+#define GAME_APP_NAME "ClassiCube 1.2.4"
+#define GAME_APP_TITLE "ClassiCube 1.2.4"
#endif
/* Max number of characters strings can have. */
diff --git a/src/Drawer2D.c b/src/Drawer2D.c
index c0bdda8fb..8487e1ee4 100644
--- a/src/Drawer2D.c
+++ b/src/Drawer2D.c
@@ -36,7 +36,7 @@ void DrawTextArgs_MakeEmpty(struct DrawTextArgs* args, struct FontDesc* font, cc
*-----------------------------------------------------Font functions------------------------------------------------------*
*#########################################################################################################################*/
static char defaultBuffer[STRING_SIZE];
-static cc_string font_candidates[11] = {
+static cc_string font_candidates[12] = {
String_FromArray(defaultBuffer), /* Filled in with user's default font */
String_FromConst("Arial"), /* preferred font on all platforms */
String_FromConst("Liberation Sans"), /* nice looking fallbacks for linux */
@@ -45,9 +45,10 @@ static cc_string font_candidates[11] = {
String_FromConst("Cantarell"),
String_FromConst("DejaVu Sans Book"),
String_FromConst("Century Schoolbook L Roman"), /* commonly available on linux */
- String_FromConst("Slate For OnePlus"), /* android 10, some devices */
- String_FromConst("Roboto"), /* android (broken on some android 10 devices) */
- String_FromConst("Geneva") /* for ancient macOS versions */
+ String_FromConst("Slate For OnePlus"), /* Android 10, some devices */
+ String_FromConst("Roboto"), /* Android (broken on some Android 10 devices) */
+ String_FromConst("Geneva"), /* for ancient macOS versions */
+ String_FromConst("Droid Sans") /* for old Android versions */
};
void Drawer2D_SetDefaultFont(const cc_string* fontName) {
diff --git a/src/Graphics.c b/src/Graphics.c
index a611ec840..0203059b7 100644
--- a/src/Graphics.c
+++ b/src/Graphics.c
@@ -973,13 +973,18 @@ void Gfx_CalcOrthoMatrix(float width, float height, struct Matrix* matrix) {
matrix->row4.Z = ORTHO_NEAR / (ORTHO_NEAR - ORTHO_FAR);
}
-void Gfx_CalcPerspectiveMatrix(float fov, float aspect, float zFar, struct Matrix* matrix) {
+static float CalcZNear(float fov) {
/* With reversed z depth, near Z plane can be much closer (with sufficient depth buffer precision) */
/* This reduces clipping with high FOV without sacrificing depth precision for faraway objects */
/* However for low FOV, don't reduce near Z in order to gain a bit more depth precision */
- float zNear = (depthBits < 24 || fov <= 70 * MATH_DEG2RAD) ? 0.05f : 0.001953125f;
- Matrix_PerspectiveFieldOfView(matrix, fov, aspect, zNear, zFar);
+ if (depthBits < 24 || fov <= 70 * MATH_DEG2RAD) return 0.05f;
+ if (fov <= 100 * MATH_DEG2RAD) return 0.025f;
+ if (fov <= 150 * MATH_DEG2RAD) return 0.0125f;
+ return 0.00390625f;
+}
+void Gfx_CalcPerspectiveMatrix(float fov, float aspect, float zFar, struct Matrix* matrix) {
+ Matrix_PerspectiveFieldOfView(matrix, fov, aspect, CalcZNear(fov), zFar);
/* Adjust the projection matrix to produce reversed Z values */
matrix->row3.Z = -matrix->row3.Z - 1.0f;
matrix->row4.Z = -matrix->row4.Z;
diff --git a/src/Input.c b/src/Input.c
index 068519c0d..1ce1485b8 100644
--- a/src/Input.c
+++ b/src/Input.c
@@ -68,6 +68,18 @@ static cc_bool AnyBlockTouches(void) {
return false;
}
+static void ClearTouches(void) {
+ int i;
+ for (i = 0; i < INPUT_MAX_POINTERS; i++) touches[i].type = 0;
+ Pointers_Count = 0;
+}
+
+void Input_SetTouchMode(cc_bool enabled) {
+ ClearTouches();
+ Input_TouchMode = enabled;
+ Pointers_Count = enabled ? 0 : 1;
+}
+
void Input_AddTouch(long id, int x, int y) {
int i;
for (i = 0; i < INPUT_MAX_POINTERS; i++) {
@@ -215,6 +227,9 @@ void Input_SetPressed(int key) {
Input_Pressed[key] = true;
Event_RaiseInput(&InputEvents.Down, key, wasPressed);
+ if (key == 'C' && Key_IsActionPressed()) Event_RaiseInput(&InputEvents.Down, INPUT_CLIPBOARD_COPY, 0);
+ if (key == 'V' && Key_IsActionPressed()) Event_RaiseInput(&InputEvents.Down, INPUT_CLIPBOARD_PASTE, 0);
+
/* don't allow multiple left mouse down events */
if (key != KEY_LMOUSE || wasPressed) return;
Pointer_SetPressed(0, true);
@@ -456,9 +471,9 @@ int Hotkeys_FindPartial(int key) {
struct HotkeyData hk;
int i, modifiers = 0;
- if (Key_IsControlPressed()) modifiers |= HOTKEY_MOD_CTRL;
- if (Key_IsShiftPressed()) modifiers |= HOTKEY_MOD_SHIFT;
- if (Key_IsAltPressed()) modifiers |= HOTKEY_MOD_ALT;
+ if (Key_IsCtrlPressed()) modifiers |= HOTKEY_MOD_CTRL;
+ if (Key_IsShiftPressed()) modifiers |= HOTKEY_MOD_SHIFT;
+ if (Key_IsAltPressed()) modifiers |= HOTKEY_MOD_ALT;
for (i = 0; i < HotkeysText.count; i++) {
hk = HotkeysList[i];
@@ -806,7 +821,7 @@ cc_bool Input_HandleMouseWheel(float delta) {
struct HacksComp* h;
cc_bool hotbar;
- hotbar = Key_IsAltPressed() || Key_IsControlPressed() || Key_IsShiftPressed();
+ hotbar = Key_IsAltPressed() || Key_IsCtrlPressed() || Key_IsShiftPressed();
if (!hotbar && Camera.Active->Zoom(delta)) return true;
if (!KeyBind_IsPressed(KEYBIND_ZOOM_SCROLL)) return false;
@@ -1089,9 +1104,7 @@ static void OnInit(void) {
static void OnFree(void) {
#ifdef CC_BUILD_TOUCH
- int i;
- for (i = 0; i < INPUT_MAX_POINTERS; i++) touches[i].type = 0;
- Pointers_Count = 0;
+ ClearTouches();
#endif
}
diff --git a/src/Input.h b/src/Input.h
index 754066a8e..bc3faba82 100644
--- a/src/Input.h
+++ b/src/Input.h
@@ -44,22 +44,25 @@ enum InputButtons {
/* NOTE: RMOUSE must be before MMOUSE for PlayerClick compatibility */
KEY_XBUTTON1, KEY_XBUTTON2, KEY_LMOUSE, KEY_RMOUSE, KEY_MMOUSE,
- INPUT_COUNT
+ INPUT_COUNT,
+
+ INPUT_CLIPBOARD_COPY = 1001,
+ INPUT_CLIPBOARD_PASTE = 1002
};
/* Simple names for each input button. */
extern const char* const Input_Names[INPUT_COUNT];
-#define Key_IsWinPressed() (Input_Pressed[KEY_LWIN] || Input_Pressed[KEY_RWIN])
-#define Key_IsAltPressed() (Input_Pressed[KEY_LALT] || Input_Pressed[KEY_RALT])
-#define Key_IsControlPressed() (Input_Pressed[KEY_LCTRL] || Input_Pressed[KEY_RCTRL])
-#define Key_IsShiftPressed() (Input_Pressed[KEY_LSHIFT] || Input_Pressed[KEY_RSHIFT])
+#define Key_IsWinPressed() (Input_Pressed[KEY_LWIN] || Input_Pressed[KEY_RWIN])
+#define Key_IsAltPressed() (Input_Pressed[KEY_LALT] || Input_Pressed[KEY_RALT])
+#define Key_IsCtrlPressed() (Input_Pressed[KEY_LCTRL] || Input_Pressed[KEY_RCTRL])
+#define Key_IsShiftPressed() (Input_Pressed[KEY_LSHIFT] || Input_Pressed[KEY_RSHIFT])
#ifdef CC_BUILD_DARWIN
/* macOS uses CMD instead of CTRL for clipboard and stuff */
#define Key_IsActionPressed() Key_IsWinPressed()
#else
-#define Key_IsActionPressed() Key_IsControlPressed()
+#define Key_IsActionPressed() Key_IsCtrlPressed()
#endif
/* Pressed state of each input button. Use Input_Set to change. */
@@ -83,6 +86,7 @@ extern int Pointers_Count;
extern cc_bool Input_TapPlace, Input_HoldPlace;
/* Whether touch input is being used. */
extern cc_bool Input_TouchMode;
+void Input_SetTouchMode(cc_bool enabled);
void Input_AddTouch(long id, int x, int y);
void Input_UpdateTouch(long id, int x, int y);
diff --git a/src/LScreens.c b/src/LScreens.c
index 4da3bc693..2c41cf0f7 100644
--- a/src/LScreens.c
+++ b/src/LScreens.c
@@ -1005,10 +1005,8 @@ static struct CheckResourcesScreen {
static void CheckResourcesScreen_Yes(void* w, int idx) { FetchResourcesScreen_SetActive(); }
static void CheckResourcesScreen_Next(void* w, int idx) {
- static const cc_string optionsTxt = String_FromConst("options.txt");
Http_ClearPending();
-
- if (File_Exists(&optionsTxt)) {
+ if (Options_LoadResult != ReturnCode_FileNotFound) {
MainScreen_SetActive();
} else {
ChooseModeScreen_SetActive(true);
diff --git a/src/LWeb.c b/src/LWeb.c
index 5aa92ce6b..de3b1782c 100644
--- a/src/LWeb.c
+++ b/src/LWeb.c
@@ -109,43 +109,47 @@ static void Json_ConsumeObject(struct JsonContext* ctx) {
char keyBuffer[STRING_SIZE];
cc_string value, oldKey = ctx->curKey;
int token;
+ ctx->depth++;
ctx->OnNewObject(ctx);
while (true) {
token = Json_ConsumeToken(ctx);
if (token == ',') continue;
- if (token == '}') return;
+ if (token == '}') break;
- if (token != '"') { ctx->failed = true; return; }
+ if (token != '"') { ctx->failed = true; break; }
String_InitArray(ctx->curKey, keyBuffer);
Json_ConsumeString(ctx, &ctx->curKey);
token = Json_ConsumeToken(ctx);
- if (token != ':') { ctx->failed = true; return; }
+ if (token != ':') { ctx->failed = true; break; }
token = Json_ConsumeToken(ctx);
- if (token == TOKEN_NONE) { ctx->failed = true; return; }
+ if (token == TOKEN_NONE) { ctx->failed = true; break; }
value = Json_ConsumeValue(token, ctx);
ctx->OnValue(ctx, &value);
ctx->curKey = oldKey;
}
+ ctx->depth--;
}
static void Json_ConsumeArray(struct JsonContext* ctx) {
cc_string value;
int token;
+ ctx->depth++;
ctx->OnNewArray(ctx);
while (true) {
token = Json_ConsumeToken(ctx);
if (token == ',') continue;
- if (token == ']') return;
+ if (token == ']') break;
- if (token == TOKEN_NONE) { ctx->failed = true; return; }
+ if (token == TOKEN_NONE) { ctx->failed = true; break; }
value = Json_ConsumeValue(token, ctx);
ctx->OnValue(ctx, &value);
}
+ ctx->depth--;
}
static cc_string Json_ConsumeValue(int token, struct JsonContext* ctx) {
@@ -169,6 +173,7 @@ void Json_Init(struct JsonContext* ctx, STRING_REF char* str, int len) {
ctx->left = len;
ctx->failed = false;
ctx->curKey = String_Empty;
+ ctx->depth = 0;
ctx->OnNewArray = Json_NullOnNew;
ctx->OnNewObject = Json_NullOnNew;
@@ -421,12 +426,19 @@ void FetchServerTask_Run(const cc_string* hash) {
*#########################################################################################################################*/
struct FetchServersData FetchServersTask;
static void FetchServersTask_Count(struct JsonContext* ctx) {
+ /* JSON is expected in this format: */
+ /* { "servers" : (depth = 1) */
+ /* [ (depth = 2) */
+ /* { server1 }, (depth = 3) */
+ /* { server2 }, (depth = 3) */
+ /* ... */
+ if (ctx->depth != 3) return;
FetchServersTask.numServers++;
}
static void FetchServersTask_Next(struct JsonContext* ctx) {
+ if (ctx->depth != 3) return;
curServer++;
- if (curServer < FetchServersTask.servers) return;
ServerInfo_Init(curServer);
}
@@ -440,8 +452,7 @@ static void FetchServersTask_Handle(cc_uint8* data, cc_uint32 len) {
FetchServersTask.servers = NULL;
FetchServersTask.orders = NULL;
- /* -1 because servers is surrounded by a { */
- FetchServersTask.numServers = -1;
+ FetchServersTask.numServers = 0;
Json_Handle(data, len, NULL, NULL, FetchServersTask_Count);
count = FetchServersTask.numServers;
@@ -449,8 +460,7 @@ static void FetchServersTask_Handle(cc_uint8* data, cc_uint32 len) {
FetchServersTask.servers = (struct ServerInfo*)Mem_Alloc(count, sizeof(struct ServerInfo), "servers list");
FetchServersTask.orders = (cc_uint16*)Mem_Alloc(count, 2, "servers order");
- /* -2 because servers is surrounded by a { */
- curServer = FetchServersTask.servers - 2;
+ curServer = FetchServersTask.servers - 1;
Json_Handle(data, len, ServerInfo_Parse, NULL, FetchServersTask_Next);
}
diff --git a/src/LWeb.h b/src/LWeb.h
index 57c271d8b..a70c0a08f 100644
--- a/src/LWeb.h
+++ b/src/LWeb.h
@@ -15,6 +15,7 @@ struct JsonContext {
int left; /* Number of characters left to be inspected. */
cc_bool failed; /* Whether there was an error parsing the JSON. */
cc_string curKey; /* Key/Name of current member */
+ int depth; /* Object/Array depth (e.g. { { { is depth 3 */
JsonOnNew OnNewArray; /* Invoked when start of an array is read. */
JsonOnNew OnNewObject; /* Invoked when start of an object is read. */
diff --git a/src/LWidgets.c b/src/LWidgets.c
index 974db27f6..c916fb621 100644
--- a/src/LWidgets.c
+++ b/src/LWidgets.c
@@ -438,13 +438,16 @@ static void LInput_Unselect(void* widget, int idx) {
Window_CloseKeyboard();
}
-static void LInput_CopyFromClipboard(cc_string* text, void* widget) {
- struct LInput* w = (struct LInput*)widget;
- String_UNSAFE_TrimStart(text);
- String_UNSAFE_TrimEnd(text);
+static void LInput_CopyFromClipboard(struct LInput* w) {
+ cc_string text; char textBuffer[2048];
+ String_InitArray(text, textBuffer);
- if (w->ClipboardFilter) w->ClipboardFilter(text);
- LInput_AppendString(w, text);
+ Clipboard_GetText(&text);
+ String_UNSAFE_TrimStart(&text);
+ String_UNSAFE_TrimEnd(&text);
+
+ if (w->ClipboardFilter) w->ClipboardFilter(&text);
+ LInput_AppendString(w, &text);
}
static void LInput_KeyDown(void* widget, int key, cc_bool was) {
@@ -453,10 +456,10 @@ static void LInput_KeyDown(void* widget, int key, cc_bool was) {
LInput_Backspace(w);
} else if (key == KEY_DELETE) {
LInput_Delete(w);
- } else if (key == 'C' && Key_IsActionPressed()) {
+ } else if (key == INPUT_CLIPBOARD_COPY) {
if (w->text.length) Clipboard_SetText(&w->text);
- } else if (key == 'V' && Key_IsActionPressed()) {
- Clipboard_RequestText(LInput_CopyFromClipboard, w);
+ } else if (key == INPUT_CLIPBOARD_PASTE) {
+ LInput_CopyFromClipboard(w);
} else if (key == KEY_ESCAPE) {
LInput_Clear(w);
} else if (key == KEY_LEFT) {
diff --git a/src/Options.c b/src/Options.c
index a7cf3ac26..20b011e43 100644
--- a/src/Options.c
+++ b/src/Options.c
@@ -9,6 +9,7 @@
struct StringsBuffer Options;
static struct StringsBuffer changedOpts;
+cc_result Options_LoadResult;
void Options_Free(void) {
StringsBuffer_Clear(&Options);
@@ -35,8 +36,8 @@ static cc_bool Options_LoadFilter(const cc_string* entry) {
void Options_Load(void) {
/* Increase from max 512 to 2048 per entry */
StringsBuffer_SetLengthBits(&Options, 11);
- EntryList_Load(&Options, "options-default.txt", '=', NULL);
- EntryList_Load(&Options, "options.txt", '=', NULL);
+ Options_LoadResult = EntryList_Load(&Options, "options-default.txt", '=', NULL);
+ Options_LoadResult = EntryList_Load(&Options, "options.txt", '=', NULL);
}
void Options_Reload(void) {
@@ -52,7 +53,7 @@ void Options_Reload(void) {
StringsBuffer_Remove(&Options, i);
}
/* Load only options which have not changed */
- EntryList_Load(&Options, "options.txt", '=', Options_LoadFilter);
+ Options_LoadResult = EntryList_Load(&Options, "options.txt", '=', Options_LoadFilter);
}
static void SaveOptions(void) {
diff --git a/src/Options.h b/src/Options.h
index b99750c28..0b6f19a66 100644
--- a/src/Options.h
+++ b/src/Options.h
@@ -72,6 +72,7 @@
#define OPT_TOUCH_BUTTONS "gui-touchbuttons"
#define OPT_TOUCH_SCALE "gui-touchscale"
#define OPT_HTTP_ONLY "http-no-https"
+#define OPT_RAW_INPUT "win-raw-input"
#define LOPT_SESSION "launcher-session"
#define LOPT_USERNAME "launcher-cc-username"
@@ -87,6 +88,7 @@
struct StringsBuffer;
extern struct StringsBuffer Options;
+extern cc_result Options_LoadResult;
/* Frees any memory allocated in storing options. */
void Options_Free(void);
diff --git a/src/Platform.c b/src/Platform.c
index d423c6c88..00232cc85 100644
--- a/src/Platform.c
+++ b/src/Platform.c
@@ -357,48 +357,68 @@ int File_Exists(const cc_string* path) {
return attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY);
}
+static cc_result Directory_EnumCore(const cc_string* dirPath, const cc_string* file, DWORD attribs,
+ void* obj, Directory_EnumCallback callback) {
+ cc_string path; char pathBuffer[MAX_PATH + 10];
+ /* ignore . and .. entry */
+ if (file->length == 1 && file->buffer[0] == '.') return 0;
+ if (file->length == 2 && file->buffer[0] == '.' && file->buffer[1] == '.') return 0;
+
+ String_InitArray(path, pathBuffer);
+ String_Format2(&path, "%s/%s", dirPath, file);
+
+ if (attribs & FILE_ATTRIBUTE_DIRECTORY) return Directory_Enum(&path, obj, callback);
+ callback(&path, obj);
+ return 0;
+}
+
cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCallback callback) {
cc_string path; char pathBuffer[MAX_PATH + 10];
WCHAR str[NATIVE_STR_LEN];
- WCHAR* src;
- WIN32_FIND_DATAW entry;
+ WIN32_FIND_DATAW eW;
+ WIN32_FIND_DATAA eA;
+ int i, ansi = false;
HANDLE find;
cc_result res;
- int i;
/* Need to append \* to search for files in directory */
String_InitArray(path, pathBuffer);
String_Format1(&path, "%s\\*", dirPath);
Platform_EncodeUtf16(str, &path);
- find = FindFirstFileW(str, &entry);
- if (find == INVALID_HANDLE_VALUE) return GetLastError();
+ find = FindFirstFileW(str, &eW);
+ if (!find || find == INVALID_HANDLE_VALUE) {
+ if ((res = GetLastError()) != ERROR_CALL_NOT_IMPLEMENTED) return res;
+ ansi = true;
- do {
- path.length = 0;
- String_Format1(&path, "%s/", dirPath);
+ /* Windows 9x does not support W API functions */
+ Platform_Utf16ToAnsi(str);
+ find = FindFirstFileA((LPCSTR)str, &eA);
+ if (find == INVALID_HANDLE_VALUE) return GetLastError();
+ }
- /* ignore . and .. entry */
- src = entry.cFileName;
- if (src[0] == '.' && src[1] == '\0') continue;
- if (src[0] == '.' && src[1] == '.' && src[2] == '\0') continue;
-
- for (i = 0; i < MAX_PATH && src[i]; i++) {
- /* TODO: UTF16 to codepoint conversion */
- String_Append(&path, Convert_CodepointToCP437(src[i]));
- }
-
- if (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
- res = Directory_Enum(&path, obj, callback);
- if (res) { FindClose(find); return res; }
- } else {
- callback(&path, obj);
- }
- } while (FindNextFileW(find, &entry));
+ if (ansi) {
+ do {
+ path.length = 0;
+ for (i = 0; i < MAX_PATH && eA.cFileName[i]; i++) {
+ String_Append(&path, Convert_CodepointToCP437(eA.cFileName[i]));
+ }
+ if ((res = Directory_EnumCore(dirPath, &path, eA.dwFileAttributes, obj, callback))) return res;
+ } while (FindNextFileA(find, &eA));
+ } else {
+ do {
+ path.length = 0;
+ for (i = 0; i < MAX_PATH && eW.cFileName[i]; i++) {
+ /* TODO: UTF16 to codepoint conversion */
+ String_Append(&path, Convert_CodepointToCP437(eW.cFileName[i]));
+ }
+ if ((res = Directory_EnumCore(dirPath, &path, eW.dwFileAttributes, obj, callback))) return res;
+ } while (FindNextFileW(find, &eW));
+ }
res = GetLastError(); /* return code from FindNextFile */
FindClose(find);
- return res == ERROR_NO_MORE_FILES ? 0 : GetLastError();
+ return res == ERROR_NO_MORE_FILES ? 0 : res;
}
static cc_result DoFile(cc_file* file, const cc_string* path, DWORD access, DWORD createMode) {
@@ -1021,19 +1041,40 @@ cc_result Socket_Poll(cc_socket s, int mode, cc_bool* success) {
*-----------------------------------------------------Process/Module------------------------------------------------------*
*#########################################################################################################################*/
#if defined CC_BUILD_WIN
-static cc_result Process_RawStart(WCHAR* path, WCHAR* args) {
- STARTUPINFOW si = { 0 };
+static cc_result Process_RawGetExePath(WCHAR* path, int* len) {
+ *len = GetModuleFileNameW(NULL, path, NATIVE_STR_LEN);
+ return *len ? 0 : GetLastError();
+}
+
+cc_result Process_StartGame(const cc_string* args) {
+ WCHAR path[NATIVE_STR_LEN + 1], raw[NATIVE_STR_LEN];
+ cc_string argv; char argvBuffer[NATIVE_STR_LEN];
+ STARTUPINFOW si = { 0 };
PROCESS_INFORMATION pi = { 0 };
cc_result res;
+ int len;
+
+ Process_RawGetExePath(path, &len);
+ path[len] = '\0';
si.cb = sizeof(STARTUPINFOW);
+
+ String_InitArray(argv, argvBuffer);
+ /* Game doesn't actually care about argv[0] */
+ String_Format1(&argv, "cc %s", args);
+ String_UNSAFE_TrimEnd(&argv);
+ Platform_EncodeUtf16(raw, &argv);
- if (CreateProcessW(path, args, NULL, NULL,
+ if (CreateProcessW(path, raw, NULL, NULL,
false, 0, NULL, NULL, &si, &pi)) goto success;
- //if ((res = GetLastError()) != ERROR_CALL_NOT_IMPLEMENTED) return res;
+ if ((res = GetLastError()) != ERROR_CALL_NOT_IMPLEMENTED) return res;
- //Platform_Utf16ToAnsi(path);
- //if (CreateProcessA((LPCSTR)path, args, NULL, NULL,
- // false, 0, NULL, NULL, &si, &pi)) goto success;
+ /* Windows 9x does not support W API functions */
+ len = GetModuleFileNameA(NULL, (LPSTR)path, NATIVE_STR_LEN);
+ ((char*)path)[len] = '\0';
+ Platform_Utf16ToAnsi(raw);
+
+ if (CreateProcessA((LPCSTR)path, (LPSTR)raw, NULL, NULL,
+ false, 0, NULL, NULL, &si, &pi)) goto success;
return GetLastError();
success:
@@ -1043,27 +1084,7 @@ success:
return 0;
}
-static cc_result Process_RawGetExePath(WCHAR* path, int* len) {
- *len = GetModuleFileNameW(NULL, path, NATIVE_STR_LEN);
- return *len ? 0 : GetLastError();
-}
-
-cc_result Process_StartGame(const cc_string* args) {
- cc_string argv; char argvBuffer[NATIVE_STR_LEN];
- WCHAR raw[NATIVE_STR_LEN], path[NATIVE_STR_LEN + 1];
- int len;
-
- cc_result res = Process_RawGetExePath(path, &len);
- if (res) return res;
- path[len] = '\0';
-
- String_InitArray(argv, argvBuffer);
- String_Format1(&argv, "ClassiCube.exe %s", args);
- Platform_EncodeUtf16(raw, &argv);
- return Process_RawStart(path, raw);
-}
void Process_Exit(cc_result code) { ExitProcess(code); }
-
void Process_StartOpen(const cc_string* args) {
WCHAR str[NATIVE_STR_LEN];
Platform_EncodeUtf16(str, args);
@@ -1272,7 +1293,6 @@ cc_bool Updater_Clean(void) {
cc_result Updater_Start(const char** action) {
WCHAR path[NATIVE_STR_LEN + 1];
- WCHAR args[2] = { 'a', '\0' }; /* don't actually care about arguments */
cc_result res;
int len = 0;
@@ -1286,7 +1306,7 @@ cc_result Updater_Start(const char** action) {
if (!MoveFileExW(UPDATE_SRC, path, MOVEFILE_REPLACE_EXISTING)) return GetLastError();
*action = "Restarting game";
- return Process_RawStart(path, args);
+ return Process_StartGame(&String_Empty);
}
cc_result Updater_GetBuildTime(cc_uint64* timestamp) {
@@ -1343,10 +1363,7 @@ cc_result Updater_GetBuildTime(cc_uint64* t) { return ERR_NOT_SUPPORTED; }
cc_result Updater_GetBuildTime(cc_uint64* t) {
JNIEnv* env;
JavaGetCurrentEnv(env);
-
- /* https://developer.android.com/reference/java/io/File#lastModified() */
- /* lastModified is returned in milliseconds */
- *t = JavaCallLong(env, "getApkUpdateTime", "()J", NULL) / 1000;
+ *t = JavaCallLong(env, "getApkUpdateTime", "()J", NULL);
return 0;
}
#endif
@@ -1565,7 +1582,7 @@ void Platform_Utf16ToAnsi(void* data) {
WCHAR* src = (WCHAR*)data;
char* dst = (char*)data;
- while (*src) { *dst++ = *src++; }
+ while (*src) { *dst++ = (char)(*src++); }
*dst = '\0';
}
diff --git a/src/Protocol.c b/src/Protocol.c
index 8d2646097..09c27a37e 100644
--- a/src/Protocol.c
+++ b/src/Protocol.c
@@ -737,9 +737,12 @@ static void Classic_Reset(void) {
}
static void Classic_Tick(void) {
- struct Entity* p = &LocalPlayer_Instance.Base;
+ struct LocalPlayer* p = &LocalPlayer_Instance;
+ struct Entity* e = &LocalPlayer_Instance.Base;
if (!classic_receivedFirstPos) return;
- Classic_WritePosition(p->Position, p->Yaw, p->Pitch);
+ /* Report end position of each physics tick, rather than current position */
+ /* (otherwise can miss landing on a block then jumping off of it again) */
+ Classic_WritePosition(p->Interp.Next.Pos, e->Yaw, e->Pitch);
}
diff --git a/src/Screens.c b/src/Screens.c
index c07654eb6..5bc1479e7 100644
--- a/src/Screens.c
+++ b/src/Screens.c
@@ -1448,7 +1448,7 @@ static int InventoryScreen_PointerDown(void* screen, int id, int x, int y) {
handled = Elem_HandlesPointerDown(table, id, x, y);
if (!handled || table->pendingClose) {
- hotbar = Key_IsControlPressed() || Key_IsShiftPressed();
+ hotbar = Key_IsCtrlPressed() || Key_IsShiftPressed();
if (!hotbar) Gui_Remove((struct Screen*)s);
}
return TOUCH_TYPE_GUI;
@@ -1467,7 +1467,7 @@ static int InventoryScreen_PointerMove(void* screen, int id, int x, int y) {
static int InventoryScreen_MouseScroll(void* screen, float delta) {
struct InventoryScreen* s = (struct InventoryScreen*)screen;
- cc_bool hotbar = Key_IsAltPressed() || Key_IsControlPressed() || Key_IsShiftPressed();
+ cc_bool hotbar = Key_IsAltPressed() || Key_IsCtrlPressed() || Key_IsShiftPressed();
if (hotbar) return false;
return Elem_HandlesMouseScroll(&s->table, delta);
}
diff --git a/src/Utils.c b/src/Utils.c
index 2cd71f674..aa70e2980 100644
--- a/src/Utils.c
+++ b/src/Utils.c
@@ -222,7 +222,7 @@ int Convert_FromBase64(const char* src, int len, cc_uint8* dst) {
/*########################################################################################################################*
*--------------------------------------------------------EntryList--------------------------------------------------------*
*#########################################################################################################################*/
-void EntryList_Load(struct StringsBuffer* list, const char* file, char separator, EntryList_Filter filter) {
+cc_result EntryList_Load(struct StringsBuffer* list, const char* file, char separator, EntryList_Filter filter) {
cc_string entry; char entryBuffer[1024];
cc_string path;
cc_string key, value;
@@ -236,8 +236,8 @@ void EntryList_Load(struct StringsBuffer* list, const char* file, char separator
maxLen = list->_lenMask ? list->_lenMask : STRINGSBUFFER_DEF_LEN_MASK;
res = Stream_OpenFile(&stream, &path);
- if (res == ReturnCode_FileNotFound) return;
- if (res) { Logger_SysWarn2(res, "opening", &path); return; }
+ if (res == ReturnCode_FileNotFound) return res;
+ if (res) { Logger_SysWarn2(res, "opening", &path); return res; }
/* ReadLine reads single byte at a time */
Stream_ReadonlyBuffered(&buffered, &stream, buffer, sizeof(buffer));
@@ -274,10 +274,11 @@ void EntryList_Load(struct StringsBuffer* list, const char* file, char separator
res = stream.Close(&stream);
if (res) { Logger_SysWarn2(res, "closing", &path); }
+ return res;
}
-void EntryList_UNSAFE_Load(struct StringsBuffer* list, const char* file) {
- EntryList_Load(list, file, '\0', NULL);
+cc_result EntryList_UNSAFE_Load(struct StringsBuffer* list, const char* file) {
+ return EntryList_Load(list, file, '\0', NULL);
}
void EntryList_Save(struct StringsBuffer* list, const char* file) {
diff --git a/src/Utils.h b/src/Utils.h
index b76f96df6..5b6aef562 100644
--- a/src/Utils.h
+++ b/src/Utils.h
@@ -59,10 +59,10 @@ typedef cc_bool (*EntryList_Filter)(const cc_string* entry);
/* Loads the entries from disc. */
/* NOTE: If separator is \0, does NOT check for duplicate keys when loading. */
/* filter can be used to optionally skip loading some entries from the file. */
-CC_NOINLINE void EntryList_Load(struct StringsBuffer* list, const char* file, char separator, EntryList_Filter filter);
+CC_NOINLINE cc_result EntryList_Load(struct StringsBuffer* list, const char* file, char separator, EntryList_Filter filter);
/* Shortcut for EntryList_Load with separator of \0 and filter of NULL */
/* NOTE: Does NOT check for duplicate keys */
-CC_NOINLINE void EntryList_UNSAFE_Load(struct StringsBuffer* list, const char* file);
+CC_NOINLINE cc_result EntryList_UNSAFE_Load(struct StringsBuffer* list, const char* file);
/* Saves the entries in the given list to disc. */
CC_NOINLINE void EntryList_Save(struct StringsBuffer* list, const char* file);
/* Removes the entry whose key caselessly equals the given key. */
diff --git a/src/Widgets.c b/src/Widgets.c
index b34e41349..910830f65 100644
--- a/src/Widgets.c
+++ b/src/Widgets.c
@@ -1216,18 +1216,20 @@ static void InputWidget_EndKey(struct InputWidget* w) {
InputWidget_UpdateCaret(w);
}
-static void InputWidget_CopyFromClipboard(cc_string* text, void* w) {
- InputWidget_AppendText((struct InputWidget*)w, text);
+static void InputWidget_CopyFromClipboard(struct InputWidget* w) {
+ cc_string text; char textBuffer[2048];
+ String_InitArray(text, textBuffer);
+
+ Clipboard_GetText(&text);
+ InputWidget_AppendText(w, &text);
}
static cc_bool InputWidget_OtherKey(struct InputWidget* w, int key) {
int maxChars = w->GetMaxLines() * INPUTWIDGET_LEN;
- if (!Key_IsActionPressed()) return false;
-
- if (key == 'V' && w->text.length < maxChars) {
- Clipboard_RequestText(InputWidget_CopyFromClipboard, w);
+ if (key == INPUT_CLIPBOARD_PASTE && w->text.length < maxChars) {
+ InputWidget_CopyFromClipboard(w);
return true;
- } else if (key == 'C') {
+ } else if (key == INPUT_CLIPBOARD_COPY) {
if (!w->text.length) return true;
Clipboard_SetText(&w->text);
return true;
diff --git a/src/Window.c b/src/Window.c
index bc354e2c7..3c75d984d 100644
--- a/src/Window.c
+++ b/src/Window.c
@@ -14,17 +14,9 @@ struct _DisplayData DisplayInfo;
struct _WinData WindowInfo;
int Display_ScaleX(int x) { return (int)(x * DisplayInfo.ScaleX); }
-int Display_ScaleY(int y) { return (int)(y * DisplayInfo.ScaleY); }
-
-#ifndef CC_BUILD_WEB
-void Clipboard_RequestText(RequestClipboardCallback callback, void* obj) {
- cc_string text; char textBuffer[2048];
- String_InitArray(text, textBuffer);
-
- Clipboard_GetText(&text);
- callback(&text, obj);
-}
-#endif
+int Display_ScaleY(int y) { return (int)(y * DisplayInfo.ScaleY); }
+#define Display_CentreX(width) (DisplayInfo.X + (DisplayInfo.Width - width) / 2)
+#define Display_CentreY(height) (DisplayInfo.Y + (DisplayInfo.Height - height) / 2)
#if defined CC_BUILD_IOS
/* iOS implements these functions in external interop_ios.m file */
@@ -982,7 +974,9 @@ static void InitRawMouse(void) {
_getRawInputData = (FUNC_GetRawInputData) DynamicLib_Get2(lib, "GetRawInputData");
rawMouseSupported = _registerRawInput && _getRawInputData;
}
- if (!rawMouseSupported) { Platform_LogConst("Raw input unsupported!"); return; }
+
+ rawMouseSupported &= Options_GetBool(OPT_RAW_INPUT, true);
+ if (!rawMouseSupported) { Platform_LogConst("## Raw input unsupported!"); return; }
rid.usUsagePage = 1; /* HID_USAGE_PAGE_GENERIC; */
rid.usUsage = 2; /* HID_USAGE_GENERIC_MOUSE; */
@@ -2741,8 +2735,8 @@ void Window_FreeFramebuffer(struct Bitmap* bmp) {
/*########################################################################################################################*
*-------------------------------------------------------Cocoa window------------------------------------------------------*
*#########################################################################################################################*/
-#elif defined CC_BUILD_COCOA
-/* NOTE: Mostly implemented in interop_cocoa.m */
+#elif defined CC_BUILD_COCOA
+/* NOTE: Mostly implemented in interop_cocoa.m */
#endif
@@ -2920,7 +2914,7 @@ static const char* OnBeforeUnload(int type, const void* ev, void *data) {
return NULL;
}
-static int MapNativeKey(int k) {
+static int MapNativeKey(int k, int l) {
if (k >= '0' && k <= '9') return k;
if (k >= 'A' && k <= 'Z') return k;
if (k >= DOM_VK_F1 && k <= DOM_VK_F24) { return KEY_F1 + (k - DOM_VK_F1); }
@@ -2929,10 +2923,10 @@ static int MapNativeKey(int k) {
switch (k) {
case DOM_VK_BACK_SPACE: return KEY_BACKSPACE;
case DOM_VK_TAB: return KEY_TAB;
- case DOM_VK_RETURN: return KEY_ENTER;
- case DOM_VK_SHIFT: return KEY_LSHIFT;
- case DOM_VK_CONTROL: return KEY_LCTRL;
- case DOM_VK_ALT: return KEY_LALT;
+ case DOM_VK_RETURN: return l == DOM_KEY_LOCATION_NUMPAD ? KEY_KP_ENTER : KEY_ENTER;
+ case DOM_VK_SHIFT: return l == DOM_KEY_LOCATION_RIGHT ? KEY_RSHIFT : KEY_LSHIFT;
+ case DOM_VK_CONTROL: return l == DOM_KEY_LOCATION_RIGHT ? KEY_RCTRL : KEY_LCTRL;
+ case DOM_VK_ALT: return l == DOM_KEY_LOCATION_RIGHT ? KEY_RALT : KEY_LALT;
case DOM_VK_PAUSE: return KEY_PAUSE;
case DOM_VK_CAPS_LOCK: return KEY_CAPSLOCK;
case DOM_VK_ESCAPE: return KEY_ESCAPE;
@@ -2952,7 +2946,7 @@ static int MapNativeKey(int k) {
case DOM_VK_SEMICOLON: return KEY_SEMICOLON;
case DOM_VK_EQUALS: return KEY_EQUALS;
- case DOM_VK_WIN: return KEY_LWIN;
+ case DOM_VK_WIN: return l == DOM_KEY_LOCATION_RIGHT ? KEY_RWIN : KEY_LWIN;
case DOM_VK_MULTIPLY: return KEY_KP_MULTIPLY;
case DOM_VK_ADD: return KEY_KP_PLUS;
case DOM_VK_SUBTRACT: return KEY_KP_MINUS;
@@ -2979,34 +2973,21 @@ static int MapNativeKey(int k) {
return KEY_NONE;
}
-static EM_BOOL OnKey(int type, const EmscriptenKeyboardEvent* ev, void* data) {
- int key = MapNativeKey(ev->keyCode);
-
- if (ev->location == DOM_KEY_LOCATION_RIGHT) {
- switch (key) {
- case KEY_LALT: key = KEY_RALT; break;
- case KEY_LCTRL: key = KEY_RCTRL; break;
- case KEY_LSHIFT: key = KEY_RSHIFT; break;
- case KEY_LWIN: key = KEY_RWIN; break;
- }
- }
- else if (ev->location == DOM_KEY_LOCATION_NUMPAD) {
- switch (key) {
- case KEY_ENTER: key = KEY_KP_ENTER; break;
- }
- }
-
- if (key) Input_Set(key, type == EMSCRIPTEN_EVENT_KEYDOWN);
+static EM_BOOL OnKeyDown(int type, const EmscriptenKeyboardEvent* ev, void* data) {
+ int key = MapNativeKey(ev->keyCode, ev->location);
+ /* iOS safari still sends backspace key events, don't intercept those */
+ if (key == KEY_BACKSPACE && Input_TouchMode && keyboardOpen) return false;
+
+ if (key) Input_SetPressed(key);
DeferredEnableRawMouse();
-
if (!key) return false;
- /* KeyUp always intercepted */
- if (type != EMSCRIPTEN_EVENT_KEYDOWN) return true;
/* If holding down Ctrl or Alt, keys aren't going to generate a KeyPress event anyways. */
/* This intercepts Ctrl+S etc. Ctrl+C and Ctrl+V are not intercepted for clipboard. */
- if (Key_IsAltPressed() || Key_IsWinPressed()) return true;
- if (Key_IsControlPressed() && key != 'C' && key != 'V') return true;
+ /* NOTE: macOS uses Win (Command) key instead of Ctrl, have to account for that too */
+ if (Key_IsAltPressed()) return true;
+ if (Key_IsWinPressed()) return key != 'C' && key != 'V';
+ if (Key_IsCtrlPressed()) return key != 'C' && key != 'V';
/* Space needs special handling, as intercepting this prevents the ' ' key press event */
/* But on Safari, space scrolls the page - so need to intercept when keyboard is NOT open */
@@ -3019,6 +3000,13 @@ static EM_BOOL OnKey(int type, const EmscriptenKeyboardEvent* ev, void* data) {
(key >= KEY_INSERT && key <= KEY_MENU) || (key >= KEY_ENTER && key <= KEY_NUMLOCK);
}
+static EM_BOOL OnKeyUp(int type, const EmscriptenKeyboardEvent* ev, void* data) {
+ int key = MapNativeKey(ev->keyCode, ev->location);
+ if (key) Input_SetReleased(key);
+ DeferredEnableRawMouse();
+ return key != KEY_NONE;
+}
+
static EM_BOOL OnKeyPress(int type, const EmscriptenKeyboardEvent* ev, void* data) {
char keyChar;
DeferredEnableRawMouse();
@@ -3032,6 +3020,10 @@ static EM_BOOL OnKeyPress(int type, const EmscriptenKeyboardEvent* ev, void* dat
/* have these intercepted key presses in its text buffer) */
if (Input_TouchMode && keyboardOpen) return false;
+ /* Safari on macOS still sends a keypress event, which must not be cancelled */
+ /* (otherwise copy/paste doesn't work, as it uses Win+C / Win+V) */
+ if (ev->metaKey) return false;
+
if (Convert_TryCodepointToCP437(ev->charCode, &keyChar)) {
Event_RaiseInt(&InputEvents.Press, keyChar);
}
@@ -3056,8 +3048,8 @@ static void HookEvents(void) {
emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, OnResize);
emscripten_set_beforeunload_callback( NULL, OnBeforeUnload);
- emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, OnKey);
- emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, OnKey);
+ emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, OnKeyDown);
+ emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, OnKeyUp);
emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, OnKeyPress);
emscripten_set_touchstart_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 0, OnTouchStart);
@@ -3101,19 +3093,24 @@ void Window_Init(void) {
EM_ASM(window.addEventListener('copy',
function(e) {
if (window.getSelection && window.getSelection().toString()) return;
- if (window.cc_copyText) {
- if (e.clipboardData) { e.clipboardData.setData('text/plain', window.cc_copyText); }
+ ccall('Window_RequestClipboardText', 'void');
+ if (!window.cc_copyText) return;
+
+ if (e.clipboardData) {
+ e.clipboardData.setData('text/plain', window.cc_copyText);
e.preventDefault();
- window.cc_copyText = null;
- }
+ }
+ window.cc_copyText = null;
});
);
- /* Paste text (window.clipboardData is handled in Clipboard_RequestText instead) */
+ /* Paste text (window.clipboardData is handled in Clipboard_GetText instead) */
EM_ASM(window.addEventListener('paste',
function(e) {
- var contents = e.clipboardData ? e.clipboardData.getData('text/plain') : "";
- ccall('Window_GotClipboardText', 'void', ['string'], [contents]);
+ if (e.clipboardData) {
+ var contents = e.clipboardData.getData('text/plain');
+ ccall('Window_GotClipboardText', 'void', ['string'], [contents]);
+ }
});
);
@@ -3124,8 +3121,7 @@ void Window_Init(void) {
return /iPhone|iPad|iPod/i.test(navigator.userAgent) ||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints && navigator.maxTouchPoints > 2);
});
- Input_TouchMode = is_ios || droid;
- Pointers_Count = Input_TouchMode ? 0 : 1;
+ Input_SetTouchMode(is_ios || droid);
/* iOS shifts the whole webpage up when opening chat, which causes problems */
/* as the chat/send butons are positioned at the top of the canvas - they */
@@ -3169,20 +3165,33 @@ void Window_SetTitle(const cc_string* title) {
EM_ASM_({ document.title = UTF8ToString($0); }, str);
}
-static RequestClipboardCallback clipboard_func;
-static void* clipboard_obj;
-
-EMSCRIPTEN_KEEPALIVE void Window_GotClipboardText(char* src) {
- cc_string str; char strBuffer[512];
- if (!clipboard_func) return;
-
- String_InitArray(str, strBuffer);
- String_AppendUtf8(&str, src, String_CalcLen(src, 2048));
- clipboard_func(&str, clipboard_obj);
- clipboard_func = NULL;
+static char pasteBuffer[512];
+static cc_string pasteStr;
+EMSCRIPTEN_KEEPALIVE void Window_RequestClipboardText(void) {
+ Event_RaiseInput(&InputEvents.Down, INPUT_CLIPBOARD_COPY, 0);
}
-void Clipboard_GetText(cc_string* value) { }
+EMSCRIPTEN_KEEPALIVE void Window_StoreClipboardText(char* src) {
+ String_InitArray(pasteStr, pasteBuffer);
+ String_AppendUtf8(&pasteStr, src, String_CalcLen(src, 2048));
+}
+
+EMSCRIPTEN_KEEPALIVE void Window_GotClipboardText(char* src) {
+ Window_StoreClipboardText(src);
+ Event_RaiseInput(&InputEvents.Down, INPUT_CLIPBOARD_PASTE, 0);
+}
+
+void Clipboard_GetText(cc_string* value) {
+ /* For IE11, use window.clipboardData to get the clipboard */
+ EM_ASM_({
+ if (window.clipboardData) {
+ var contents = window.clipboardData.getData('Text');
+ ccall('Window_StoreClipboardText', 'void', ['string'], [contents]);
+ }
+ });
+ String_Copy(value, &pasteStr);
+ pasteStr.length = 0;
+}
void Clipboard_SetText(const cc_string* value) {
char str[NATIVE_STR_LEN];
Platform_EncodeUtf8(str, value);
@@ -3199,18 +3208,6 @@ void Clipboard_SetText(const cc_string* value) {
}, str);
}
-void Clipboard_RequestText(RequestClipboardCallback callback, void* obj) {
- clipboard_func = callback;
- clipboard_obj = obj;
-
- /* For IE11, use window.clipboardData to get the clipboard */
- EM_ASM_({
- if (!window.clipboardData) return;
- var contents = window.clipboardData.getData('Text');
- ccall('Window_GotClipboardText', 'void', ['string'], [contents]);
- });
-}
-
void Window_Show(void) { }
int Window_GetWindowState(void) {
@@ -3672,7 +3669,8 @@ void Window_Init(void) {
JavaRegisterNatives(env, methods);
WindowInfo.SoftKeyboard = SOFT_KEYBOARD_RESIZE;
- Input_TouchMode = true;
+ Input_SetTouchMode(true);
+
DisplayInfo.Depth = 32;
DisplayInfo.ScaleX = JavaCallFloat(env, "getDpiX", "()F", NULL);
DisplayInfo.ScaleY = JavaCallFloat(env, "getDpiY", "()F", NULL);
@@ -3820,13 +3818,13 @@ void Window_FreeFramebuffer(struct Bitmap* bmp) {
Mem_Free(bmp->scan0);
}
-void Window_OpenKeyboard(const struct OpenKeyboardArgs* args) {
+void Window_OpenKeyboard(const struct OpenKeyboardArgs* kArgs) {
JNIEnv* env;
jvalue args[2];
JavaGetCurrentEnv(env);
- args[0].l = JavaMakeString(env, args->text);
- args[1].i = args->type;
+ args[0].l = JavaMakeString(env, kArgs->text);
+ args[1].i = kArgs->type;
JavaCallVoid(env, "openKeyboard", "(Ljava/lang/String;I)V", args);
(*env)->DeleteLocalRef(env, args[0].l);
}
@@ -4214,12 +4212,11 @@ static XVisualInfo GLContext_SelectVisual(void) {
InitGraphicsMode(&mode);
GetAttribs(&mode, attribs, GLCONTEXT_DEFAULT_DEPTH);
- if (!glXQueryVersion(win_display, &major, &minor)) {
- Logger_Abort("glXQueryVersion failed");
- }
screen = DefaultScreen(win_display);
- if (major >= 1 && minor >= 3) {
+ if (!glXQueryVersion(win_display, &major, &minor)) {
+ Platform_LogConst("glXQueryVersion failed");
+ } else if (major >= 1 && minor >= 3) {
/* ChooseFBConfig returns an array of GLXFBConfig opaque structures */
fbconfigs = glXChooseFBConfig(win_display, screen, attribs, &fbcount);
if (fbconfigs && fbcount) {
diff --git a/src/Window.h b/src/Window.h
index 971a9e7a9..1d6da05c8 100644
--- a/src/Window.h
+++ b/src/Window.h
@@ -84,15 +84,14 @@ void Window_Create(int width, int height);
/* Sets the text of the titlebar above the window. */
CC_API void Window_SetTitle(const cc_string* title);
-typedef void (*RequestClipboardCallback)(cc_string* value, void* obj);
/* Gets the text currently on the clipboard. */
+/* NOTE: On most platforms this function can be called at any time. */
+/* In web backend, can only be called during INPUT_CLIPBOARD_PASTE event. */
CC_API void Clipboard_GetText(cc_string* value);
/* Sets the text currently on the clipboard. */
+/* NOTE: On most platforms this function can be called at any time. */
+/* In web backend, can only be called during INPUT_CLIPBOARD_COPY event. */
CC_API void Clipboard_SetText(const cc_string* value);
-/* Calls a callback function when text is retrieved from the clipboard. */
-/* NOTE: On most platforms this just calls Clipboard_GetText. */
-/* With emscripten however, the callback is instead called when a 'paste' event arrives. */
-void Clipboard_RequestText(RequestClipboardCallback callback, void* obj);
/* Makes the window visible and focussed on screen. */
void Window_Show(void);