diff options
Diffstat (limited to 'platform')
34 files changed, 1113 insertions, 1104 deletions
diff --git a/platform/android/AndroidManifest.xml.template b/platform/android/AndroidManifest.xml.template index 13d10b5026..81f4c15849 100644 --- a/platform/android/AndroidManifest.xml.template +++ b/platform/android/AndroidManifest.xml.template @@ -32,174 +32,9 @@ $$ADD_APPLICATION_CHUNKS$$ </application> - <uses-feature android:glEsVersion="0x00030000" android:required="true" /> + <uses-feature android:glEsVersion="0x00020000" android:required="true" /> $$ADD_PERMISSION_CHUNKS$$ -<uses-permission android:name="godot.ACCESS_CHECKIN_PROPERTIES"/> -<uses-permission android:name="godot.ACCESS_COARSE_LOCATION"/> -<uses-permission android:name="godot.ACCESS_FINE_LOCATION"/> -<uses-permission android:name="godot.ACCESS_LOCATION_EXTRA_COMMANDS"/> -<uses-permission android:name="godot.ACCESS_MOCK_LOCATION"/> -<uses-permission android:name="godot.ACCESS_NETWORK_STATE"/> -<uses-permission android:name="godot.ACCESS_SURFACE_FLINGER"/> -<uses-permission android:name="godot.ACCESS_WIFI_STATE"/> -<uses-permission android:name="godot.ACCOUNT_MANAGER"/> -<uses-permission android:name="godot.ADD_VOICEMAIL"/> -<uses-permission android:name="godot.AUTHENTICATE_ACCOUNTS"/> -<uses-permission android:name="godot.BATTERY_STATS"/> -<uses-permission android:name="godot.BIND_ACCESSIBILITY_SERVICE"/> -<uses-permission android:name="godot.BIND_APPWIDGET"/> -<uses-permission android:name="godot.BIND_DEVICE_ADMIN"/> -<uses-permission android:name="godot.BIND_INPUT_METHOD"/> -<uses-permission android:name="godot.BIND_NFC_SERVICE"/> -<uses-permission android:name="godot.BIND_NOTIFICATION_LISTENER_SERVICE"/> -<uses-permission android:name="godot.BIND_PRINT_SERVICE"/> -<uses-permission android:name="godot.BIND_REMOTEVIEWS"/> -<uses-permission android:name="godot.BIND_TEXT_SERVICE"/> -<uses-permission android:name="godot.BIND_VPN_SERVICE"/> -<uses-permission android:name="godot.BIND_WALLPAPER"/> -<uses-permission android:name="godot.BLUETOOTH"/> -<uses-permission android:name="godot.BLUETOOTH_ADMIN"/> -<uses-permission android:name="godot.BLUETOOTH_PRIVILEGED"/> -<uses-permission android:name="godot.BRICK"/> -<uses-permission android:name="godot.BROADCAST_PACKAGE_REMOVED"/> -<uses-permission android:name="godot.BROADCAST_SMS"/> -<uses-permission android:name="godot.BROADCAST_STICKY"/> -<uses-permission android:name="godot.BROADCAST_WAP_PUSH"/> -<uses-permission android:name="godot.CALL_PHONE"/> -<uses-permission android:name="godot.CALL_PRIVILEGED"/> -<uses-permission android:name="godot.CAMERA"/> -<uses-permission android:name="godot.CAPTURE_AUDIO_OUTPUT"/> -<uses-permission android:name="godot.CAPTURE_SECURE_VIDEO_OUTPUT"/> -<uses-permission android:name="godot.CAPTURE_VIDEO_OUTPUT"/> -<uses-permission android:name="godot.CHANGE_COMPONENT_ENABLED_STATE"/> -<uses-permission android:name="godot.CHANGE_CONFIGURATION"/> -<uses-permission android:name="godot.CHANGE_NETWORK_STATE"/> -<uses-permission android:name="godot.CHANGE_WIFI_MULTICAST_STATE"/> -<uses-permission android:name="godot.CHANGE_WIFI_STATE"/> -<uses-permission android:name="godot.CLEAR_APP_CACHE"/> -<uses-permission android:name="godot.CLEAR_APP_USER_DATA"/> -<uses-permission android:name="godot.CONTROL_LOCATION_UPDATES"/> -<uses-permission android:name="godot.DELETE_CACHE_FILES"/> -<uses-permission android:name="godot.DELETE_PACKAGES"/> -<uses-permission android:name="godot.DEVICE_POWER"/> -<uses-permission android:name="godot.DIAGNOSTIC"/> -<uses-permission android:name="godot.DISABLE_KEYGUARD"/> -<uses-permission android:name="godot.DUMP"/> -<uses-permission android:name="godot.EXPAND_STATUS_BAR"/> -<uses-permission android:name="godot.FACTORY_TEST"/> -<uses-permission android:name="godot.FLASHLIGHT"/> -<uses-permission android:name="godot.FORCE_BACK"/> -<uses-permission android:name="godot.GET_ACCOUNTS"/> -<uses-permission android:name="godot.GET_PACKAGE_SIZE"/> -<uses-permission android:name="godot.GET_TASKS"/> -<uses-permission android:name="godot.GET_TOP_ACTIVITY_INFO"/> -<uses-permission android:name="godot.GLOBAL_SEARCH"/> -<uses-permission android:name="godot.HARDWARE_TEST"/> -<uses-permission android:name="godot.INJECT_EVENTS"/> -<uses-permission android:name="godot.INSTALL_LOCATION_PROVIDER"/> -<uses-permission android:name="godot.INSTALL_PACKAGES"/> -<uses-permission android:name="godot.INSTALL_SHORTCUT"/> -<uses-permission android:name="godot.INTERNAL_SYSTEM_WINDOW"/> -<uses-permission android:name="godot.INTERNET"/> -<uses-permission android:name="godot.KILL_BACKGROUND_PROCESSES"/> -<uses-permission android:name="godot.LOCATION_HARDWARE"/> -<uses-permission android:name="godot.MANAGE_ACCOUNTS"/> -<uses-permission android:name="godot.MANAGE_APP_TOKENS"/> -<uses-permission android:name="godot.MANAGE_DOCUMENTS"/> -<uses-permission android:name="godot.MASTER_CLEAR"/> -<uses-permission android:name="godot.MEDIA_CONTENT_CONTROL"/> -<uses-permission android:name="godot.MODIFY_AUDIO_SETTINGS"/> -<uses-permission android:name="godot.MODIFY_PHONE_STATE"/> -<uses-permission android:name="godot.MOUNT_FORMAT_FILESYSTEMS"/> -<uses-permission android:name="godot.MOUNT_UNMOUNT_FILESYSTEMS"/> -<uses-permission android:name="godot.NFC"/> -<uses-permission android:name="godot.PERSISTENT_ACTIVITY"/> -<uses-permission android:name="godot.PROCESS_OUTGOING_CALLS"/> -<uses-permission android:name="godot.READ_CALENDAR"/> -<uses-permission android:name="godot.READ_CALL_LOG"/> -<uses-permission android:name="godot.READ_CONTACTS"/> -<uses-permission android:name="godot.READ_EXTERNAL_STORAGE"/> -<uses-permission android:name="godot.READ_FRAME_BUFFER"/> -<uses-permission android:name="godot.READ_HISTORY_BOOKMARKS"/> -<uses-permission android:name="godot.READ_INPUT_STATE"/> -<uses-permission android:name="godot.READ_LOGS"/> -<uses-permission android:name="godot.READ_PHONE_STATE"/> -<uses-permission android:name="godot.READ_PROFILE"/> -<uses-permission android:name="godot.READ_SMS"/> -<uses-permission android:name="godot.READ_SOCIAL_STREAM"/> -<uses-permission android:name="godot.READ_SYNC_SETTINGS"/> -<uses-permission android:name="godot.READ_SYNC_STATS"/> -<uses-permission android:name="godot.READ_USER_DICTIONARY"/> -<uses-permission android:name="godot.REBOOT"/> -<uses-permission android:name="godot.RECEIVE_BOOT_COMPLETED"/> -<uses-permission android:name="godot.RECEIVE_MMS"/> -<uses-permission android:name="godot.RECEIVE_SMS"/> -<uses-permission android:name="godot.RECEIVE_WAP_PUSH"/> -<uses-permission android:name="godot.RECORD_AUDIO"/> -<uses-permission android:name="godot.REORDER_TASKS"/> -<uses-permission android:name="godot.RESTART_PACKAGES"/> -<uses-permission android:name="godot.SEND_RESPOND_VIA_MESSAGE"/> -<uses-permission android:name="godot.SEND_SMS"/> -<uses-permission android:name="godot.SET_ACTIVITY_WATCHER"/> -<uses-permission android:name="godot.SET_ALARM"/> -<uses-permission android:name="godot.SET_ALWAYS_FINISH"/> -<uses-permission android:name="godot.SET_ANIMATION_SCALE"/> -<uses-permission android:name="godot.SET_DEBUG_APP"/> -<uses-permission android:name="godot.SET_ORIENTATION"/> -<uses-permission android:name="godot.SET_POINTER_SPEED"/> -<uses-permission android:name="godot.SET_PREFERRED_APPLICATIONS"/> -<uses-permission android:name="godot.SET_PROCESS_LIMIT"/> -<uses-permission android:name="godot.SET_TIME"/> -<uses-permission android:name="godot.SET_TIME_ZONE"/> -<uses-permission android:name="godot.SET_WALLPAPER"/> -<uses-permission android:name="godot.SET_WALLPAPER_HINTS"/> -<uses-permission android:name="godot.SIGNAL_PERSISTENT_PROCESSES"/> -<uses-permission android:name="godot.STATUS_BAR"/> -<uses-permission android:name="godot.SUBSCRIBED_FEEDS_READ"/> -<uses-permission android:name="godot.SUBSCRIBED_FEEDS_WRITE"/> -<uses-permission android:name="godot.SYSTEM_ALERT_WINDOW"/> -<uses-permission android:name="godot.TRANSMIT_IR"/> -<uses-permission android:name="godot.UNINSTALL_SHORTCUT"/> -<uses-permission android:name="godot.UPDATE_DEVICE_STATS"/> -<uses-permission android:name="godot.USE_CREDENTIALS"/> -<uses-permission android:name="godot.USE_SIP"/> -<uses-permission android:name="godot.VIBRATE"/> -<uses-permission android:name="godot.WAKE_LOCK"/> -<uses-permission android:name="godot.WRITE_APN_SETTINGS"/> -<uses-permission android:name="godot.WRITE_CALENDAR"/> -<uses-permission android:name="godot.WRITE_CALL_LOG"/> -<uses-permission android:name="godot.WRITE_CONTACTS"/> -<uses-permission android:name="godot.WRITE_EXTERNAL_STORAGE"/> -<uses-permission android:name="godot.WRITE_GSERVICES"/> -<uses-permission android:name="godot.WRITE_HISTORY_BOOKMARKS"/> -<uses-permission android:name="godot.WRITE_PROFILE"/> -<uses-permission android:name="godot.WRITE_SECURE_SETTINGS"/> -<uses-permission android:name="godot.WRITE_SETTINGS"/> -<uses-permission android:name="godot.WRITE_SMS"/> -<uses-permission android:name="godot.WRITE_SOCIAL_STREAM"/> -<uses-permission android:name="godot.WRITE_SYNC_SETTINGS"/> -<uses-permission android:name="godot.WRITE_USER_DICTIONARY"/> -<uses-permission android:name="godot.custom.0"/> -<uses-permission android:name="godot.custom.1"/> -<uses-permission android:name="godot.custom.2"/> -<uses-permission android:name="godot.custom.3"/> -<uses-permission android:name="godot.custom.4"/> -<uses-permission android:name="godot.custom.5"/> -<uses-permission android:name="godot.custom.6"/> -<uses-permission android:name="godot.custom.7"/> -<uses-permission android:name="godot.custom.8"/> -<uses-permission android:name="godot.custom.9"/> -<uses-permission android:name="godot.custom.10"/> -<uses-permission android:name="godot.custom.11"/> -<uses-permission android:name="godot.custom.12"/> -<uses-permission android:name="godot.custom.13"/> -<uses-permission android:name="godot.custom.14"/> -<uses-permission android:name="godot.custom.15"/> -<uses-permission android:name="godot.custom.16"/> -<uses-permission android:name="godot.custom.17"/> -<uses-permission android:name="godot.custom.18"/> -<uses-permission android:name="godot.custom.19"/> <uses-sdk android:minSdkVersion="18" android:targetSdkVersion="27"/> diff --git a/platform/android/SCsub b/platform/android/SCsub index 8c08289932..a65dab9668 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -53,7 +53,7 @@ if len(env.android_flat_dirs) > 0: gradle_maven_flat_text = gradle_maven_flat_text[:-1] gradle_maven_flat_text += "\n\t}\n" - + gradle_maven_repos_text = "" gradle_maven_repos_text += gradle_maven_flat_text @@ -99,6 +99,9 @@ for x in env.android_jni_dirs: gradle_asset_dirs_text = "" +for x in env.android_asset_dirs: + gradle_asset_dirs_text += ",'" + x.replace("\\", "/") + "'" + gradle_default_config_text = "" minSdk = 18 diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 9e6377f4fe..c3ff157f99 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -570,7 +570,7 @@ class EditorExportAndroid : public EditorExportPlatform { // const int CHUNK_RESOURCEIDS = 0x00080180; const int CHUNK_STRINGS = 0x001C0001; // const int CHUNK_XML_END_NAMESPACE = 0x00100101; - // const int CHUNK_XML_END_TAG = 0x00100103; + const int CHUNK_XML_END_TAG = 0x00100103; // const int CHUNK_XML_START_NAMESPACE = 0x00100100; const int CHUNK_XML_START_TAG = 0x00100102; // const int CHUNK_XML_TEXT = 0x00100104; @@ -601,24 +601,28 @@ class EditorExportAndroid : public EditorExportPlatform { bool screen_support_large = p_preset->get("screen/support_large"); bool screen_support_xlarge = p_preset->get("screen/support_xlarge"); - String user_perms[MAX_USER_PERMISSIONS]; - - for (int i = 0; i < MAX_USER_PERMISSIONS; i++) { - - user_perms[i] = p_preset->get("user_permissions/" + itos(i)); - } - - Set<String> perms; + Vector<String> perms; const char **aperms = android_perms; while (*aperms) { bool enabled = p_preset->get("permissions/" + String(*aperms).to_lower()); if (enabled) - perms.insert(String(*aperms)); + perms.push_back("android.permission." + String(*aperms)); aperms++; } + for (int i = 0; i < MAX_USER_PERMISSIONS; i++) { + String user_perm = p_preset->get("user_permissions/" + itos(i)); + if (user_perm.strip_edges() != "" && user_perm.strip_edges() != "False") + perms.push_back(user_perm.strip_edges()); + } + + if (p_give_internet) { + if (perms.find("android.permission.INTERNET") == -1) + perms.push_back("android.permission.INTERNET"); + } + while (ofs < (uint32_t)p_manifest.size()) { uint32_t chunk = decode_uint32(&p_manifest[ofs]); @@ -741,27 +745,6 @@ class EditorExportAndroid : public EditorExportPlatform { print_line("version number: " + itos(decode_uint32(&p_manifest[iofs + 16]))); } - if (tname == "uses-permission" && /*nspace=="android" &&*/ attrname == "name") { - - if (value.begins_with("godot.custom")) { - - int which = value.get_slice(".", 2).to_int(); - if (which >= 0 && which < MAX_USER_PERMISSIONS && user_perms[which].strip_edges() != "") { - - string_table[attr_value] = user_perms[which].strip_edges(); - } - - } else if (value.begins_with("godot.")) { - String perm = value.get_slice(".", 1); - - if (perms.has(perm) || (p_give_internet && perm == "INTERNET")) { - - print_line("PERM: " + perm); - string_table[attr_value] = "android.permission." + perm; - } - } - } - if (tname == "supports-screens") { if (attrname == "smallScreens") { @@ -786,6 +769,91 @@ class EditorExportAndroid : public EditorExportPlatform { } } break; + case CHUNK_XML_END_TAG: { + int iofs = ofs + 8; + uint32_t name = decode_uint32(&p_manifest[iofs + 12]); + String tname = string_table[name]; + + if (tname == "manifest") { + print_line("Found manifest end"); + + // save manifest ending so we can restore it + Vector<uint8_t> manifest_end; + uint32_t manifest_cur_size = p_manifest.size(); + uint32_t node_size = size; + + manifest_end.resize(p_manifest.size() - ofs); + memcpy(manifest_end.ptrw(), &p_manifest[ofs], manifest_end.size()); + + int32_t attr_name_string = string_table.find("name"); + ERR_EXPLAIN("Template does not have 'name' attribute"); + ERR_FAIL_COND(attr_name_string == -1); + + int32_t ns_android_string = string_table.find("android"); + ERR_EXPLAIN("Template does not have 'android' namespace"); + ERR_FAIL_COND(ns_android_string == -1); + + int32_t attr_uses_permission_string = string_table.find("uses-permission"); + if (attr_uses_permission_string == -1) { + string_table.push_back("uses-permission"); + attr_uses_permission_string = string_table.size() - 1; + } + + for (int i = 0; i < perms.size(); ++i) { + print_line("Adding permission " + perms[i]); + + manifest_cur_size += 56 + 24; // node + end node + p_manifest.resize(manifest_cur_size); + + // Add permission to the string pool + int32_t perm_string = string_table.find(perms[i]); + if (perm_string == -1) { + string_table.push_back(perms[i]); + perm_string = string_table.size() - 1; + } + + // start tag + encode_uint16(0x102, &p_manifest[ofs]); // type + encode_uint16(16, &p_manifest[ofs + 2]); // headersize + encode_uint32(56, &p_manifest[ofs + 4]); // size + encode_uint32(0, &p_manifest[ofs + 8]); // lineno + encode_uint32(-1, &p_manifest[ofs + 12]); // comment + encode_uint32(-1, &p_manifest[ofs + 16]); // ns + encode_uint32(attr_uses_permission_string, &p_manifest[ofs + 20]); // name + encode_uint16(20, &p_manifest[ofs + 24]); // attr_start + encode_uint16(20, &p_manifest[ofs + 26]); // attr_size + encode_uint16(1, &p_manifest[ofs + 28]); // num_attrs + encode_uint16(0, &p_manifest[ofs + 30]); // id_index + encode_uint16(0, &p_manifest[ofs + 32]); // class_index + encode_uint16(0, &p_manifest[ofs + 34]); // style_index + + // attribute + encode_uint32(ns_android_string, &p_manifest[ofs + 36]); // ns + encode_uint32(attr_name_string, &p_manifest[ofs + 40]); // 'name' + encode_uint32(perm_string, &p_manifest[ofs + 44]); // raw_value + encode_uint16(8, &p_manifest[ofs + 48]); // typedvalue_size + p_manifest[ofs + 50] = 0; // typedvalue_always0 + p_manifest[ofs + 51] = 0x03; // typedvalue_type (string) + encode_uint32(perm_string, &p_manifest[ofs + 52]); // typedvalue reference + + ofs += 56; + + // end tag + encode_uint16(0x103, &p_manifest[ofs]); // type + encode_uint16(16, &p_manifest[ofs + 2]); // headersize + encode_uint32(24, &p_manifest[ofs + 4]); // size + encode_uint32(0, &p_manifest[ofs + 8]); // lineno + encode_uint32(-1, &p_manifest[ofs + 12]); // comment + encode_uint32(-1, &p_manifest[ofs + 16]); // ns + encode_uint32(attr_uses_permission_string, &p_manifest[ofs + 20]); // name + + ofs += 24; + } + + // copy footer back in + memcpy(&p_manifest[ofs], manifest_end.ptr(), manifest_end.size()); + } + } break; } ofs += size; @@ -806,17 +874,17 @@ class EditorExportAndroid : public EditorExportPlatform { encode_uint32(ofs, &ret[string_table_begins + i * 4]); ofs += string_table[i].length() * 2 + 2 + 2; - //print_line("ofs: "+itos(i)+": "+itos(ofs)); } + ret.resize(ret.size() + ofs); - uint8_t *chars = &ret[ret.size() - ofs]; + string_data_offset = ret.size() - ofs; + uint8_t *chars = &ret[string_data_offset]; for (int i = 0; i < string_table.size(); i++) { String s = string_table[i]; - //print_line("savint string :"+s); encode_uint16(s.length(), chars); chars += 2; - for (int j = 0; j < s.length(); j++) { //include zero? + for (int j = 0; j < s.length(); j++) { encode_uint16(s[j], chars); chars += 2; } @@ -828,6 +896,7 @@ class EditorExportAndroid : public EditorExportPlatform { ret.push_back(stable_extra[i]); } + //pad while (ret.size() % 4) ret.push_back(0); @@ -843,6 +912,8 @@ class EditorExportAndroid : public EditorExportPlatform { encode_uint32(ret.size(), &ret[4]); //update new file size encode_uint32(new_stable_end - 8, &ret[12]); //update new string table size + encode_uint32(string_table.size(), &ret[16]); //update new number of strings + encode_uint32(string_data_offset - 8, &ret[28]); //update new string data offset //print_line("file size: "+itos(ret.size())); diff --git a/platform/android/java/res/drawable-hdpi/notify_panel_notification_icon_bg.png b/platform/android/java/res/drawable-hdpi/notify_panel_notification_icon_bg.png Binary files differindex 94bc406416..372b763ec5 100644 --- a/platform/android/java/res/drawable-hdpi/notify_panel_notification_icon_bg.png +++ b/platform/android/java/res/drawable-hdpi/notify_panel_notification_icon_bg.png diff --git a/platform/android/java/res/drawable-mdpi/notify_panel_notification_icon_bg.png b/platform/android/java/res/drawable-mdpi/notify_panel_notification_icon_bg.png Binary files differindex ef6fe4e836..c61c440636 100644 --- a/platform/android/java/res/drawable-mdpi/notify_panel_notification_icon_bg.png +++ b/platform/android/java/res/drawable-mdpi/notify_panel_notification_icon_bg.png diff --git a/platform/android/java/res/drawable/icon.png b/platform/android/java/res/drawable/icon.png Binary files differindex 29c4a7b8fc..6ad9b43117 100644 --- a/platform/android/java/res/drawable/icon.png +++ b/platform/android/java/res/drawable/icon.png diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java index 90848e6a90..8a2d789dc5 100644 --- a/platform/android/java/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/src/org/godotengine/godot/Godot.java @@ -32,6 +32,7 @@ package org.godotengine.godot; import android.R; import android.app.Activity; +import android.content.pm.ConfigurationInfo; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; @@ -246,9 +247,11 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC } }; - public void onVideoInit(boolean use_gl2) { + public void onVideoInit() { - //mView = new GodotView(getApplication(),io,use_gl2); + boolean use_gl3 = getGLESVersionCode() >= 0x00030000; + + //mView = new GodotView(getApplication(),io,use_gl3); //setContentView(mView); layout = new FrameLayout(this); @@ -261,7 +264,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC // ...add to FrameLayout layout.addView(edittext); - mView = new GodotView(getApplication(), io, use_gl2, use_32_bits, this); + mView = new GodotView(getApplication(), io, use_gl3, use_32_bits, this); layout.addView(mView, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); edittext.setView(mView); io.setEdit(edittext); @@ -338,6 +341,12 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC return Godot._self; } + public int getGLESVersionCode() { + ActivityManager am = (ActivityManager)Godot.getInstance().getSystemService(Context.ACTIVITY_SERVICE); + ConfigurationInfo deviceInfo = am.getDeviceConfigurationInfo(); + return deviceInfo.reqGlEsVersion; + } + private String[] getCommandLine() { InputStream is; try { diff --git a/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java b/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java index 766989f953..aaf18c74bf 100644 --- a/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java +++ b/platform/android/java/src/org/godotengine/godot/payments/HandlePurchaseTask.java @@ -58,17 +58,15 @@ abstract public class HandlePurchaseTask { public void handlePurchaseRequest(int resultCode, Intent data) { //Log.d("XXX", "Handling purchase response"); - //int responseCode = data.getIntExtra("RESPONSE_CODE", 0); - PaymentsCache pc = new PaymentsCache(context); - - String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA"); - //Log.d("XXX", "Purchase data:" + purchaseData); - String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE"); - //Log.d("XXX", "Purchase signature:" + dataSignature); - if (resultCode == Activity.RESULT_OK) { - try { + //int responseCode = data.getIntExtra("RESPONSE_CODE", 0); + PaymentsCache pc = new PaymentsCache(context); + + String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA"); + //Log.d("XXX", "Purchase data:" + purchaseData); + String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE"); + //Log.d("XXX", "Purchase signature:" + dataSignature); //Log.d("SARLANGA", purchaseData); JSONObject jo = new JSONObject(purchaseData); diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp index 579c06f76b..e6240ad9e9 100644 --- a/platform/android/java_glue.cpp +++ b/platform/android/java_glue.cpp @@ -614,6 +614,7 @@ static jmethodID _hideKeyboard = 0; static jmethodID _setScreenOrientation = 0; static jmethodID _getUniqueID = 0; static jmethodID _getSystemDir = 0; +static jmethodID _getGLESVersionCode = 0; static jmethodID _playVideo = 0; static jmethodID _isVideoPlaying = 0; static jmethodID _pauseVideo = 0; @@ -685,6 +686,11 @@ static String _get_system_dir(int p_dir) { return String(env->GetStringUTFChars(s, NULL)); } +static int _get_gles_version_code() { + JNIEnv *env = ThreadAndroid::get_env(); + return env->CallIntMethod(_godot_instance, _getGLESVersionCode); +} + static void _hide_vk() { JNIEnv *env = ThreadAndroid::get_env(); @@ -764,9 +770,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en godot_io = gob; - _on_video_init = env->GetMethodID(cls, "onVideoInit", "(Z)V"); + _on_video_init = env->GetMethodID(cls, "onVideoInit", "()V"); _setKeepScreenOn = env->GetMethodID(cls, "setKeepScreenOn", "(Z)V"); _alertDialog = env->GetMethodID(cls, "alert", "(Ljava/lang/String;Ljava/lang/String;)V"); + _getGLESVersionCode = env->GetMethodID(cls, "getGLESVersionCode", "()I"); jclass clsio = env->FindClass("org/godotengine/godot/Godot"); if (cls) { @@ -800,16 +807,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en AudioDriverAndroid::setup(gob); } - os_android = new OS_Android(_gfx_init_func, env, _open_uri, _get_user_data_dir, _get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk, _get_vk_height, _set_screen_orient, _get_unique_id, _get_system_dir, _play_video, _is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, _alert, p_use_apk_expansion); + os_android = new OS_Android(_gfx_init_func, env, _open_uri, _get_user_data_dir, _get_locale, _get_model, _get_screen_dpi, _show_vk, _hide_vk, _get_vk_height, _set_screen_orient, _get_unique_id, _get_system_dir, _get_gles_version_code, _play_video, _is_video_playing, _pause_video, _stop_video, _set_keep_screen_on, _alert, p_use_apk_expansion); os_android->set_need_reload_hooks(p_need_reload_hook); char wd[500]; getcwd(wd, 500); - //video driver is determined here, because once initialized, it can't be changed - // String vd = ProjectSettings::get_singleton()->get("display/driver"); - - env->CallVoidMethod(_godot_instance, _on_video_init, (jboolean) true); + env->CallVoidMethod(_godot_instance, _on_video_init); } static void _initialize_java_modules() { diff --git a/platform/android/logo.png b/platform/android/logo.png Binary files differindex fcf684c026..ba2a0e366a 100644 --- a/platform/android/logo.png +++ b/platform/android/logo.png diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index fc41adeb76..9188f09f21 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -32,6 +32,7 @@ #include "core/io/file_access_buffered_fa.h" #include "core/project_settings.h" +#include "drivers/gles2/rasterizer_gles2.h" #include "drivers/gles3/rasterizer_gles3.h" #include "drivers/unix/dir_access_unix.h" #include "drivers/unix/file_access_unix.h" @@ -125,13 +126,20 @@ void OS_Android::set_opengl_extensions(const char *p_gl_extensions) { Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { - use_gl2 = p_video_driver != 1; + bool use_gl3 = get_gl_version_code_func() >= 0x00030000; + use_gl3 = use_gl3 && (GLOBAL_GET("rendering/quality/driver/driver_name") == "GLES3"); + use_gl2 = !use_gl3; if (gfx_init_func) gfx_init_func(gfx_init_ud, use_gl2); - RasterizerGLES3::register_config(); - RasterizerGLES3::make_current(); + if (use_gl2) { + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + } else { + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + } visual_server = memnew(VisualServerRaster); /* if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { @@ -684,7 +692,7 @@ bool OS_Android::_check_internal_feature_support(const String &p_feature) { return false; } -OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion) { +OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, GetGLVersionCodeFunc p_get_gl_version_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion) { use_apk_expansion = p_use_apk_expansion; default_videomode.width = 800; @@ -706,6 +714,7 @@ OS_Android::OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURI get_screen_dpi_func = p_get_screen_dpi_func; get_unique_id_func = p_get_unique_id; get_system_dir_func = p_get_sdir_func; + get_gl_version_code_func = p_get_gl_version_func; video_play_func = p_video_play_func; video_is_playing_func = p_video_is_playing_func; diff --git a/platform/android/os_android.h b/platform/android/os_android.h index d2457e538d..ac901d4832 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -58,6 +58,7 @@ typedef void (*ShowVirtualKeyboardFunc)(const String &); typedef void (*HideVirtualKeyboardFunc)(); typedef void (*SetScreenOrientationFunc)(int); typedef String (*GetSystemDirFunc)(int); +typedef int (*GetGLVersionCodeFunc)(); typedef void (*VideoPlayFunc)(const String &); typedef bool (*VideoIsPlayingFunc)(); @@ -126,6 +127,7 @@ private: SetScreenOrientationFunc set_screen_orientation_func; GetUniqueIDFunc get_unique_id_func; GetSystemDirFunc get_system_dir_func; + GetGLVersionCodeFunc get_gl_version_code_func; VideoPlayFunc video_play_func; VideoIsPlayingFunc video_is_playing_func; @@ -239,7 +241,7 @@ public: void joy_connection_changed(int p_device, bool p_connected, String p_name); virtual bool _check_internal_feature_support(const String &p_feature); - OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion); + OS_Android(GFXInitFunc p_gfx_init_func, void *p_gfx_init_ud, OpenURIFunc p_open_uri_func, GetUserDataDirFunc p_get_user_data_dir_func, GetLocaleFunc p_get_locale_func, GetModelFunc p_get_model_func, GetScreenDPIFunc p_get_screen_dpi_func, ShowVirtualKeyboardFunc p_show_vk, HideVirtualKeyboardFunc p_hide_vk, VirtualKeyboardHeightFunc p_vk_height_func, SetScreenOrientationFunc p_screen_orient, GetUniqueIDFunc p_get_unique_id, GetSystemDirFunc p_get_sdir_func, GetGLVersionCodeFunc p_get_gl_version_func, VideoPlayFunc p_video_play_func, VideoIsPlayingFunc p_video_is_playing_func, VideoPauseFunc p_video_pause_func, VideoStopFunc p_video_stop_func, SetKeepScreenOnFunc p_set_keep_screen_on_func, AlertFunc p_alert_func, bool p_use_apk_expansion); ~OS_Android(); }; diff --git a/platform/android/run_icon.png b/platform/android/run_icon.png Binary files differindex e53f8e9da5..b687c9ac31 100644 --- a/platform/android/run_icon.png +++ b/platform/android/run_icon.png diff --git a/platform/haiku/logo.png b/platform/haiku/logo.png Binary files differindex d5d98e4cc6..a2d8e242a6 100644 --- a/platform/haiku/logo.png +++ b/platform/haiku/logo.png diff --git a/platform/iphone/logo.png b/platform/iphone/logo.png Binary files differindex 8dd718524c..405b6f93ca 100644 --- a/platform/iphone/logo.png +++ b/platform/iphone/logo.png diff --git a/platform/javascript/dom_keys.h b/platform/javascript/dom_keys.inc index 4edca63c6d..dc8d67d52b 100644 --- a/platform/javascript/dom_keys.h +++ b/platform/javascript/dom_keys.inc @@ -1,5 +1,5 @@ /*************************************************************************/ -/* dom_keys.h */ +/* dom_keys.inc */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,9 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef DOM_KEYS_H -#define DOM_KEYS_H - #include "os/keyboard.h" // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Constants_for_keyCode_value @@ -295,8 +292,8 @@ int dom2godot_scancode(int dom_keycode) { //case DOM_VK_SELECT: return KEY_UNKNOWN; - case DOM_VK_PRINTSCREEN: // this is the usual printScreen key - case DOM_VK_PRINT: // maybe for alt+printScreen or physical printers? + case DOM_VK_PRINTSCREEN: + case DOM_VK_PRINT: return KEY_PRINT; //case DOM_VK_EXECUTE: return KEY_UNKNOWN; @@ -311,11 +308,11 @@ int dom2godot_scancode(int dom_keycode) { case DOM_VK_SLEEP: return KEY_STANDBY; - // these are numpad keys according to MDN + // Numpad keys case DOM_VK_MULTIPLY: return KEY_KP_MULTIPLY; case DOM_VK_ADD: return KEY_KP_ADD; case DOM_VK_SEPARATOR: - return KEY_KP_PERIOD; // good enough? + return KEY_KP_PERIOD; // Good enough? case DOM_VK_SUBTRACT: return KEY_KP_SUBTRACT; case DOM_VK_DECIMAL: return KEY_KP_PERIOD; case DOM_VK_DIVIDE: @@ -376,10 +373,8 @@ int dom2godot_scancode(int dom_keycode) { case DOM_VK_QUOTE: return KEY_APOSTROPHE; - // rest is OEM/unusual + // The rest is OEM/unusual. default: return KEY_UNKNOWN; }; } - -#endif diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp index 68a2d72464..3829e8d406 100644 --- a/platform/javascript/javascript_main.cpp +++ b/platform/javascript/javascript_main.cpp @@ -28,17 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "emscripten.h" #include "io/resource_loader.h" #include "main/main.h" #include "os_javascript.h" -OS_JavaScript *os = NULL; - -static void main_loop() { - - os->main_loop_iterate(); -} +#include <emscripten/emscripten.h> extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) { @@ -46,18 +40,18 @@ extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) { if (!idbfs_err.empty()) { print_line("IndexedDB not available: " + idbfs_err); } - os->set_idbfs_available(idbfs_err.empty()); - // Ease up compatibility + OS_JavaScript *os = OS_JavaScript::get_singleton(); + os->set_idb_available(idbfs_err.empty()); + // Ease up compatibility. ResourceLoader::set_abort_on_missing_resources(false); Main::start(); - os->main_loop_begin(); - emscripten_set_main_loop(main_loop, 0, false); + os->run_async(); } int main(int argc, char *argv[]) { - // sync from persistent state into memory and then - // run the 'main_after_fs_sync' function + // Sync from persistent state into memory and then + // run the 'main_after_fs_sync' function. /* clang-format off */ EM_ASM( FS.mkdir('/userfs'); @@ -68,9 +62,10 @@ int main(int argc, char *argv[]) { ); /* clang-format on */ - os = new OS_JavaScript(argv[0], NULL); - Error err = Main::setup(argv[0], argc - 1, &argv[1]); + new OS_JavaScript(argc, argv); + // TODO: Check error return value. + Main::setup(argv[0], argc - 1, &argv[1]); return 0; - // continued async in main_after_fs_sync() from syncfs() callback + // Continued async in main_after_fs_sync() from the syncfs() callback. } diff --git a/platform/javascript/logo.png b/platform/javascript/logo.png Binary files differindex ce911180ac..36832d93ba 100644 --- a/platform/javascript/logo.png +++ b/platform/javascript/logo.png diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index 6c6e4d2d1c..c05ae03ec6 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -30,290 +30,196 @@ #include "os_javascript.h" -#include "core/engine.h" -#include "core/io/file_access_buffered_fa.h" -#include "dom_keys.h" -#include "drivers/gles2/rasterizer_gles2.h" -#include "drivers/gles3/rasterizer_gles3.h" -#include "drivers/unix/dir_access_unix.h" -#include "drivers/unix/file_access_unix.h" +#include "gles2/rasterizer_gles2.h" +#include "gles3/rasterizer_gles3.h" +#include "io/file_access_buffered_fa.h" #include "main/main.h" #include "servers/visual/visual_server_raster.h" +#include "unix/dir_access_unix.h" +#include "unix/file_access_unix.h" #include <emscripten.h> #include <stdlib.h> +#include "dom_keys.inc" + #define DOM_BUTTON_LEFT 0 #define DOM_BUTTON_MIDDLE 1 #define DOM_BUTTON_RIGHT 2 +#define DOM_BUTTON_XBUTTON1 3 +#define DOM_BUTTON_XBUTTON2 4 -template <typename T> -static void dom2godot_mod(T emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event) { - - godot_event->set_shift(emscripten_event_ptr->shiftKey); - godot_event->set_alt(emscripten_event_ptr->altKey); - godot_event->set_control(emscripten_event_ptr->ctrlKey); - godot_event->set_metakey(emscripten_event_ptr->metaKey); -} - -int OS_JavaScript::get_video_driver_count() const { +// Window (canvas) - return VIDEO_DRIVER_MAX; -} - -const char *OS_JavaScript::get_video_driver_name(int p_driver) const { - - switch (p_driver) { - case VIDEO_DRIVER_GLES3: - return "GLES3"; - case VIDEO_DRIVER_GLES2: - return "GLES2"; - } - ERR_EXPLAIN("Invalid video driver index " + itos(p_driver)); - ERR_FAIL_V(NULL); -} - -int OS_JavaScript::get_audio_driver_count() const { - - return 1; -} - -const char *OS_JavaScript::get_audio_driver_name(int p_driver) const { +static void focus_canvas() { - return "JavaScript"; + /* clang-format off */ + EM_ASM( + Module.canvas.focus(); + ); + /* clang-format on */ } -void OS_JavaScript::initialize_core() { +static bool is_canvas_focused() { - OS_Unix::initialize_core(); - FileAccess::make_default<FileAccessBufferedFA<FileAccessUnix> >(FileAccess::ACCESS_RESOURCES); + /* clang-format off */ + return EM_ASM_INT_V( + return document.activeElement == Module.canvas; + ); + /* clang-format on */ } -static EM_BOOL _browser_resize_callback(int event_type, const EmscriptenUiEvent *ui_event, void *user_data) { +static bool cursor_inside_canvas = true; - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_RESIZE, false); +EM_BOOL OS_JavaScript::browser_resize_callback(int p_event_type, const EmscriptenUiEvent *p_event, void *p_user_data) { - OS_JavaScript *os = static_cast<OS_JavaScript *>(user_data); // The order of the fullscreen change event and the window size change - // event varies, even within just one browser, so defer handling - os->request_canvas_size_adjustment(); + // event varies, even within just one browser, so defer handling. + get_singleton()->canvas_size_adjustment_requested = true; return false; } -static EM_BOOL _fullscreen_change_callback(int event_type, const EmscriptenFullscreenChangeEvent *event, void *user_data) { - - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_FULLSCREENCHANGE, false); +EM_BOOL OS_JavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) { - OS_JavaScript *os = static_cast<OS_JavaScript *>(user_data); - String id = String::utf8(event->id); - // empty id is canvas - if (id.empty() || id == "canvas") { - - OS::VideoMode vm = os->get_video_mode(); - // this event property is the only reliable information on - // browser fullscreen state - vm.fullscreen = event->isFullscreen; - os->set_video_mode(vm); - os->request_canvas_size_adjustment(); + OS_JavaScript *os = get_singleton(); + // Empty ID is canvas. + String target_id = String::utf8(p_event->id); + if (target_id.empty() || target_id == "canvas") { + // This event property is the only reliable data on + // browser fullscreen state. + os->video_mode.fullscreen = p_event->isFullscreen; + os->canvas_size_adjustment_requested = true; } return false; } -static InputDefault *_input; - -static bool is_canvas_focused() { +void OS_JavaScript::set_video_mode(const VideoMode &p_video_mode, int p_screen) { - /* clang-format off */ - return EM_ASM_INT_V( - return document.activeElement == Module.canvas; - ); - /* clang-format on */ + video_mode = p_video_mode; } -static void focus_canvas() { +OS::VideoMode OS_JavaScript::get_video_mode(int p_screen) const { - /* clang-format off */ - EM_ASM( - Module.canvas.focus(); - ); - /* clang-format on */ + return video_mode; } -static bool _cursor_inside_canvas = true; - -static bool is_cursor_inside_canvas() { +Size2 OS_JavaScript::get_screen_size(int p_screen) const { - return _cursor_inside_canvas; + EmscriptenFullscreenChangeEvent ev; + EMSCRIPTEN_RESULT result = emscripten_get_fullscreen_status(&ev); + ERR_FAIL_COND_V(result != EMSCRIPTEN_RESULT_SUCCESS, Size2()); + return Size2(ev.screenWidth, ev.screenHeight); } -static EM_BOOL _mousebutton_callback(int event_type, const EmscriptenMouseEvent *mouse_event, void *user_data) { - - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_MOUSEDOWN && event_type != EMSCRIPTEN_EVENT_MOUSEUP, false); - - Ref<InputEventMouseButton> ev; - ev.instance(); - ev->set_pressed(event_type == EMSCRIPTEN_EVENT_MOUSEDOWN); - ev->set_position(Point2(mouse_event->canvasX, mouse_event->canvasY)); - ev->set_global_position(ev->get_position()); - dom2godot_mod(mouse_event, ev); - - switch (mouse_event->button) { - case DOM_BUTTON_LEFT: ev->set_button_index(BUTTON_LEFT); break; - case DOM_BUTTON_MIDDLE: ev->set_button_index(BUTTON_MIDDLE); break; - case DOM_BUTTON_RIGHT: ev->set_button_index(BUTTON_RIGHT); break; - default: return false; - } +void OS_JavaScript::set_window_size(const Size2 p_size) { - int mask = _input->get_mouse_button_mask(); - int button_flag = 1 << (ev->get_button_index() - 1); - if (ev->is_pressed()) { - // Since the event is consumed, focus manually. The containing iframe, - // if used, may not have focus yet, so focus even if already focused. - focus_canvas(); - mask |= button_flag; - } else if (mask & button_flag) { - mask &= ~button_flag; + windowed_size = p_size; + if (is_window_fullscreen()) { + window_maximized = false; + set_window_fullscreen(false); + } else if (is_window_maximized()) { + set_window_maximized(false); } else { - // release event, but press was outside the canvas, so ignore - return false; + video_mode.width = p_size.x; + video_mode.height = p_size.y; + emscripten_set_canvas_size(p_size.x, p_size.y); } - ev->set_button_mask(mask); - - _input->parse_input_event(ev); - // Prevent multi-click text selection and wheel-click scrolling anchor. - // Context menu is prevented through contextmenu event. - return true; } -static EM_BOOL _mousemove_callback(int event_type, const EmscriptenMouseEvent *mouse_event, void *user_data) { - - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_MOUSEMOVE, false); - OS_JavaScript *os = static_cast<OS_JavaScript *>(user_data); - int input_mask = _input->get_mouse_button_mask(); - Point2 pos = Point2(mouse_event->canvasX, mouse_event->canvasY); - // outside the canvas, only read mouse movement if dragging started inside - // the canvas; imitating desktop app behaviour - if (!is_cursor_inside_canvas() && !input_mask) - return false; - - Ref<InputEventMouseMotion> ev; - ev.instance(); - dom2godot_mod(mouse_event, ev); - ev->set_button_mask(input_mask); - - ev->set_position(pos); - ev->set_global_position(ev->get_position()); - - ev->set_relative(Vector2(mouse_event->movementX, mouse_event->movementY)); - _input->set_mouse_position(ev->get_position()); - ev->set_speed(_input->get_last_mouse_speed()); +Size2 OS_JavaScript::get_window_size() const { - _input->parse_input_event(ev); - // don't suppress mouseover/leave events - return false; + int canvas[3]; + emscripten_get_canvas_size(canvas, canvas + 1, canvas + 2); + return Size2(canvas[0], canvas[1]); } -static EM_BOOL _wheel_callback(int event_type, const EmscriptenWheelEvent *wheel_event, void *user_data) { +void OS_JavaScript::set_window_maximized(bool p_enabled) { - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_WHEEL, false); - if (!is_canvas_focused()) { - if (is_cursor_inside_canvas()) { - focus_canvas(); - } else { - return false; - } + window_maximized = p_enabled; + if (is_window_fullscreen()) { + set_window_fullscreen(false); + return; } + // Calling emscripten_enter_soft_fullscreen mutltiple times hides all + // page elements except the canvas permanently, so track state. + if (p_enabled && !soft_fullscreen_enabled) { - Ref<InputEventMouseButton> ev; - ev.instance(); - ev->set_button_mask(_input->get_mouse_button_mask()); - ev->set_position(_input->get_mouse_position()); - ev->set_global_position(ev->get_position()); - - ev->set_shift(_input->is_key_pressed(KEY_SHIFT)); - ev->set_alt(_input->is_key_pressed(KEY_ALT)); - ev->set_control(_input->is_key_pressed(KEY_CONTROL)); - ev->set_metakey(_input->is_key_pressed(KEY_META)); - - if (wheel_event->deltaY < 0) - ev->set_button_index(BUTTON_WHEEL_UP); - else if (wheel_event->deltaY > 0) - ev->set_button_index(BUTTON_WHEEL_DOWN); - else if (wheel_event->deltaX > 0) - ev->set_button_index(BUTTON_WHEEL_LEFT); - else if (wheel_event->deltaX < 0) - ev->set_button_index(BUTTON_WHEEL_RIGHT); - else - return false; - - // Different browsers give wildly different delta values, and we can't - // interpret deltaMode, so use default value for wheel events' factor + EmscriptenFullscreenStrategy strategy; + strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; + strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF; + strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT; + strategy.canvasResizedCallback = NULL; + emscripten_enter_soft_fullscreen(NULL, &strategy); + soft_fullscreen_enabled = true; + video_mode.width = get_window_size().width; + video_mode.height = get_window_size().height; + } else if (!p_enabled) { - ev->set_pressed(true); - _input->parse_input_event(ev); + emscripten_exit_soft_fullscreen(); + soft_fullscreen_enabled = false; + video_mode.width = windowed_size.width; + video_mode.height = windowed_size.height; + emscripten_set_canvas_size(video_mode.width, video_mode.height); + } +} - ev->set_pressed(false); - _input->parse_input_event(ev); +bool OS_JavaScript::is_window_maximized() const { - return true; + return window_maximized; } -static Point2 _prev_touches[32]; - -static EM_BOOL _touchpress_callback(int event_type, const EmscriptenTouchEvent *touch_event, void *user_data) { +void OS_JavaScript::set_window_fullscreen(bool p_enabled) { - ERR_FAIL_COND_V( - event_type != EMSCRIPTEN_EVENT_TOUCHSTART && - event_type != EMSCRIPTEN_EVENT_TOUCHEND && - event_type != EMSCRIPTEN_EVENT_TOUCHCANCEL, - false); + if (p_enabled == is_window_fullscreen()) { + return; + } - Ref<InputEventScreenTouch> ev; - ev.instance(); - int lowest_id_index = -1; - for (int i = 0; i < touch_event->numTouches; ++i) { + // Just request changes here, if successful, canvas is resized in + // _browser_resize_callback or _fullscreen_change_callback. + EMSCRIPTEN_RESULT result; + if (p_enabled) { + if (window_maximized) { + // Soft fullsreen during real fulllscreen can cause issues. + set_window_maximized(false); + window_maximized = true; + } + EmscriptenFullscreenStrategy strategy; + strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; + strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF; + strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT; + strategy.canvasResizedCallback = NULL; + emscripten_request_fullscreen_strategy(NULL, false, &strategy); + } else { + result = emscripten_exit_fullscreen(); + if (result != EMSCRIPTEN_RESULT_SUCCESS) { + ERR_PRINTS("Failed to exit fullscreen: Code " + itos(result)); + } + } +} - const EmscriptenTouchPoint &touch = touch_event->touches[i]; - if (lowest_id_index == -1 || touch.identifier < touch_event->touches[lowest_id_index].identifier) - lowest_id_index = i; - if (!touch.isChanged) - continue; - ev->set_index(touch.identifier); - ev->set_position(Point2(touch.canvasX, touch.canvasY)); - _prev_touches[i] = ev->get_position(); - ev->set_pressed(event_type == EMSCRIPTEN_EVENT_TOUCHSTART); +bool OS_JavaScript::is_window_fullscreen() const { - _input->parse_input_event(ev); - } - return true; + return video_mode.fullscreen; } -static EM_BOOL _touchmove_callback(int event_type, const EmscriptenTouchEvent *touch_event, void *user_data) { +void OS_JavaScript::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const { - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_TOUCHMOVE, false); + Size2 screen = get_screen_size(); + p_list->push_back(OS::VideoMode(screen.width, screen.height, true)); +} - Ref<InputEventScreenDrag> ev; - ev.instance(); - int lowest_id_index = -1; - for (int i = 0; i < touch_event->numTouches; ++i) { +// Keys - const EmscriptenTouchPoint &touch = touch_event->touches[i]; - if (lowest_id_index == -1 || touch.identifier < touch_event->touches[lowest_id_index].identifier) - lowest_id_index = i; - if (!touch.isChanged) - continue; - ev->set_index(touch.identifier); - ev->set_position(Point2(touch.canvasX, touch.canvasY)); - Point2 &prev = _prev_touches[i]; - ev->set_relative(ev->get_position() - prev); - prev = ev->get_position(); +template <typename T> +static void dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event) { - _input->parse_input_event(ev); - } - return true; + godot_event->set_shift(emscripten_event_ptr->shiftKey); + godot_event->set_alt(emscripten_event_ptr->altKey); + godot_event->set_control(emscripten_event_ptr->ctrlKey); + godot_event->set_metakey(emscripten_event_ptr->metaKey); } -static Ref<InputEventKey> _setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) { +static Ref<InputEventKey> setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) { Ref<InputEventKey> ev; ev.instance(); @@ -322,9 +228,9 @@ static Ref<InputEventKey> _setup_key_event(const EmscriptenKeyboardEvent *emscri ev->set_scancode(dom2godot_scancode(emscripten_event->keyCode)); String unicode = String::utf8(emscripten_event->key); - // check if empty or multi-character (e.g. `CapsLock`) + // Check if empty or multi-character (e.g. `CapsLock`). if (unicode.length() != 1) { - // might be empty as well, but better than nonsense + // Might be empty as well, but better than nonsense. unicode = String::utf8(emscripten_event->charValue); } if (unicode.length() == 1) { @@ -334,175 +240,115 @@ static Ref<InputEventKey> _setup_key_event(const EmscriptenKeyboardEvent *emscri return ev; } -static Ref<InputEventKey> deferred_key_event; - -static EM_BOOL _keydown_callback(int event_type, const EmscriptenKeyboardEvent *key_event, void *user_data) { +EM_BOOL OS_JavaScript::keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) { - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_KEYDOWN, false); - - Ref<InputEventKey> ev = _setup_key_event(key_event); + OS_JavaScript *os = get_singleton(); + Ref<InputEventKey> ev = setup_key_event(p_event); ev->set_pressed(true); if (ev->get_unicode() == 0 && keycode_has_unicode(ev->get_scancode())) { - // defer to keypress event for legacy unicode retrieval - deferred_key_event = ev; - return false; // do not suppress keypress event + // Defer to keypress event for legacy unicode retrieval. + os->deferred_key_event = ev; + // Do not suppress keypress event. + return false; } - _input->parse_input_event(ev); + os->input->parse_input_event(ev); return true; } -static EM_BOOL _keypress_callback(int event_type, const EmscriptenKeyboardEvent *key_event, void *user_data) { - - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_KEYPRESS, false); +EM_BOOL OS_JavaScript::keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) { - deferred_key_event->set_unicode(key_event->charCode); - _input->parse_input_event(deferred_key_event); + OS_JavaScript *os = get_singleton(); + os->deferred_key_event->set_unicode(p_event->charCode); + os->input->parse_input_event(os->deferred_key_event); return true; } -static EM_BOOL _keyup_callback(int event_type, const EmscriptenKeyboardEvent *key_event, void *user_data) { +EM_BOOL OS_JavaScript::keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data) { - ERR_FAIL_COND_V(event_type != EMSCRIPTEN_EVENT_KEYUP, false); - - Ref<InputEventKey> ev = _setup_key_event(key_event); + Ref<InputEventKey> ev = setup_key_event(p_event); ev->set_pressed(false); - _input->parse_input_event(ev); + get_singleton()->input->parse_input_event(ev); return ev->get_scancode() != KEY_UNKNOWN && ev->get_scancode() != 0; } -static EM_BOOL joy_callback_func(int p_type, const EmscriptenGamepadEvent *p_event, void *p_user) { - OS_JavaScript *os = (OS_JavaScript *)OS::get_singleton(); - if (os) { - return os->joy_connection_changed(p_type, p_event); - } - return false; +// Mouse + +Point2 OS_JavaScript::get_mouse_position() const { + + return input->get_mouse_position(); } -extern "C" EMSCRIPTEN_KEEPALIVE void send_notification(int notif) { +int OS_JavaScript::get_mouse_button_state() const { - if (notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || notif == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) { - _cursor_inside_canvas = notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER; - } - OS_JavaScript::get_singleton()->get_main_loop()->notification(notif); + return input->get_mouse_button_mask(); } -Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { +EM_BOOL OS_JavaScript::mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) { - print_line("Init OS"); + OS_JavaScript *os = get_singleton(); - EmscriptenWebGLContextAttributes attributes; - emscripten_webgl_init_context_attributes(&attributes); - attributes.alpha = false; - attributes.antialias = false; - ERR_FAIL_INDEX_V(p_video_driver, VIDEO_DRIVER_MAX, ERR_INVALID_PARAMETER); - switch (p_video_driver) { - case VIDEO_DRIVER_GLES3: - attributes.majorVersion = 2; - RasterizerGLES3::register_config(); - RasterizerGLES3::make_current(); - break; - case VIDEO_DRIVER_GLES2: - attributes.majorVersion = 1; - RasterizerGLES2::register_config(); - RasterizerGLES2::make_current(); - break; + Ref<InputEventMouseButton> ev; + ev.instance(); + ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_MOUSEDOWN); + ev->set_position(Point2(p_event->canvasX, p_event->canvasY)); + ev->set_global_position(ev->get_position()); + dom2godot_mod(p_event, ev); + switch (p_event->button) { + case DOM_BUTTON_LEFT: ev->set_button_index(BUTTON_LEFT); break; + case DOM_BUTTON_MIDDLE: ev->set_button_index(BUTTON_MIDDLE); break; + case DOM_BUTTON_RIGHT: ev->set_button_index(BUTTON_RIGHT); break; + case DOM_BUTTON_XBUTTON1: ev->set_button_index(BUTTON_XBUTTON1); break; + case DOM_BUTTON_XBUTTON2: ev->set_button_index(BUTTON_XBUTTON2); break; + default: return false; } - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(NULL, &attributes); - ERR_EXPLAIN("WebGL " + itos(attributes.majorVersion) + ".0 not available"); - ERR_FAIL_COND_V(emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS, ERR_UNAVAILABLE); - video_mode = p_desired; - // can't fulfil fullscreen request due to browser security - video_mode.fullscreen = false; - /* clang-format off */ - if (EM_ASM_INT_V({ return Module.resizeCanvasOnStart })) { - /* clang-format on */ - set_window_size(Size2(video_mode.width, video_mode.height)); + int mask = os->input->get_mouse_button_mask(); + int button_flag = 1 << (ev->get_button_index() - 1); + if (ev->is_pressed()) { + // Since the event is consumed, focus manually. The containing iframe, + // if exists, may not have focus yet, so focus even if already focused. + focus_canvas(); + mask |= button_flag; + } else if (mask & button_flag) { + mask &= ~button_flag; } else { - set_window_size(get_window_size()); + // Received release event, but press was outside the canvas, so ignore. + return false; } + ev->set_button_mask(mask); - char locale_ptr[16]; - /* clang-format off */ - EM_ASM_ARGS({ - stringToUTF8(Module.locale, $0, 16); - }, locale_ptr); - /* clang-format on */ - setenv("LANG", locale_ptr, true); - - print_line("Init Audio"); - - AudioDriverManager::initialize(p_audio_driver); - - print_line("Init VS"); - - visual_server = memnew(VisualServerRaster()); - // visual_server->cursor_set_visible(false, 0); - - print_line("Init Physicsserver"); - - input = memnew(InputDefault); - _input = input; - -#define EM_CHECK(ev) \ - if (result != EMSCRIPTEN_RESULT_SUCCESS) \ - ERR_PRINTS("Error while setting " #ev " callback: Code " + itos(result)) -#define SET_EM_CALLBACK(target, ev, cb) \ - result = emscripten_set_##ev##_callback(target, this, true, &cb); \ - EM_CHECK(ev) -#define SET_EM_CALLBACK_NODATA(ev, cb) \ - result = emscripten_set_##ev##_callback(NULL, true, &cb); \ - EM_CHECK(ev) - - EMSCRIPTEN_RESULT result; - SET_EM_CALLBACK("#window", mousemove, _mousemove_callback) - SET_EM_CALLBACK("#canvas", mousedown, _mousebutton_callback) - SET_EM_CALLBACK("#window", mouseup, _mousebutton_callback) - SET_EM_CALLBACK("#window", wheel, _wheel_callback) - SET_EM_CALLBACK("#window", touchstart, _touchpress_callback) - SET_EM_CALLBACK("#window", touchmove, _touchmove_callback) - SET_EM_CALLBACK("#window", touchend, _touchpress_callback) - SET_EM_CALLBACK("#window", touchcancel, _touchpress_callback) - SET_EM_CALLBACK("#canvas", keydown, _keydown_callback) - SET_EM_CALLBACK("#canvas", keypress, _keypress_callback) - SET_EM_CALLBACK("#canvas", keyup, _keyup_callback) - SET_EM_CALLBACK(NULL, resize, _browser_resize_callback) - SET_EM_CALLBACK(NULL, fullscreenchange, _fullscreen_change_callback) - SET_EM_CALLBACK_NODATA(gamepadconnected, joy_callback_func) - SET_EM_CALLBACK_NODATA(gamepaddisconnected, joy_callback_func) - -#undef SET_EM_CALLBACK_NODATA -#undef SET_EM_CALLBACK -#undef EM_CHECK - - visual_server->init(); - - return OK; + os->input->parse_input_event(ev); + // Prevent multi-click text selection and wheel-click scrolling anchor. + // Context menu is prevented through contextmenu event. + return true; } -void OS_JavaScript::set_main_loop(MainLoop *p_main_loop) { +EM_BOOL OS_JavaScript::mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data) { - main_loop = p_main_loop; - input->set_main_loop(p_main_loop); -} - -void OS_JavaScript::delete_main_loop() { + OS_JavaScript *os = get_singleton(); - memdelete(main_loop); -} + int input_mask = os->input->get_mouse_button_mask(); + Point2 pos = Point2(p_event->canvasX, p_event->canvasY); + // For motion outside the canvas, only read mouse movement if dragging + // started inside the canvas; imitating desktop app behaviour. + if (!cursor_inside_canvas && !input_mask) + return false; -void OS_JavaScript::finalize() { + Ref<InputEventMouseMotion> ev; + ev.instance(); + dom2godot_mod(p_event, ev); + ev->set_button_mask(input_mask); - memdelete(input); -} + ev->set_position(pos); + ev->set_global_position(ev->get_position()); -void OS_JavaScript::alert(const String &p_alert, const String &p_title) { + ev->set_relative(Vector2(p_event->movementX, p_event->movementY)); + os->input->set_mouse_position(ev->get_position()); + ev->set_speed(os->input->get_last_mouse_speed()); - /* clang-format off */ - EM_ASM_({ - window.alert(UTF8ToString($0)); - }, p_alert.utf8().get_data()); - /* clang-format on */ + os->input->parse_input_event(ev); + // Don't suppress mouseover/-leave events. + return false; } static const char *godot2dom_cursor(OS::CursorShape p_shape) { @@ -530,7 +376,7 @@ static const char *godot2dom_cursor(OS::CursorShape p_shape) { } } -void OS_JavaScript::set_css_cursor(const char *p_cursor) { +static void set_css_cursor(const char *p_cursor) { /* clang-format off */ EM_ASM_({ @@ -539,7 +385,7 @@ void OS_JavaScript::set_css_cursor(const char *p_cursor) { /* clang-format on */ } -const char *OS_JavaScript::get_css_cursor() const { +static const char *get_css_cursor() { char cursor[16]; /* clang-format off */ @@ -550,9 +396,20 @@ const char *OS_JavaScript::get_css_cursor() const { return cursor; } +void OS_JavaScript::set_cursor_shape(CursorShape p_shape) { + + ERR_FAIL_INDEX(p_shape, CURSOR_MAX); + + cursor_shape = p_shape; + if (get_mouse_mode() != MOUSE_MODE_HIDDEN) + set_css_cursor(godot2dom_cursor(cursor_shape)); +} + +void OS_JavaScript::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { +} + void OS_JavaScript::set_mouse_mode(OS::MouseMode p_mode) { - ERR_FAIL_INDEX(p_mode, MOUSE_MODE_CONFINED + 1); ERR_EXPLAIN("MOUSE_MODE_CONFINED is not supported for the HTML5 platform"); ERR_FAIL_COND(p_mode == MOUSE_MODE_CONFINED); if (p_mode == get_mouse_mode()) @@ -580,190 +437,303 @@ void OS_JavaScript::set_mouse_mode(OS::MouseMode p_mode) { OS::MouseMode OS_JavaScript::get_mouse_mode() const { - if (!strcmp(get_css_cursor(), "none")) + if (String::utf8(get_css_cursor()) == "none") return MOUSE_MODE_HIDDEN; EmscriptenPointerlockChangeEvent ev; emscripten_get_pointerlock_status(&ev); - return ev.isActive && (strcmp(ev.id, "canvas") == 0) ? MOUSE_MODE_CAPTURED : MOUSE_MODE_VISIBLE; + return (ev.isActive && String::utf8(ev.id) == "canvas") ? MOUSE_MODE_CAPTURED : MOUSE_MODE_VISIBLE; } -Point2 OS_JavaScript::get_mouse_position() const { +// Wheel - return input->get_mouse_position(); -} +EM_BOOL OS_JavaScript::wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data) { -int OS_JavaScript::get_mouse_button_state() const { + ERR_FAIL_COND_V(p_event_type != EMSCRIPTEN_EVENT_WHEEL, false); + if (!is_canvas_focused()) { + if (cursor_inside_canvas) { + focus_canvas(); + } else { + return false; + } + } - return input->get_mouse_button_mask(); -} + InputDefault *input = get_singleton()->input; + Ref<InputEventMouseButton> ev; + ev.instance(); + ev->set_button_mask(input->get_mouse_button_mask()); + ev->set_position(input->get_mouse_position()); + ev->set_global_position(ev->get_position()); -void OS_JavaScript::set_window_title(const String &p_title) { + ev->set_shift(input->is_key_pressed(KEY_SHIFT)); + ev->set_alt(input->is_key_pressed(KEY_ALT)); + ev->set_control(input->is_key_pressed(KEY_CONTROL)); + ev->set_metakey(input->is_key_pressed(KEY_META)); - /* clang-format off */ - EM_ASM_({ - document.title = UTF8ToString($0); - }, p_title.utf8().get_data()); - /* clang-format on */ -} + if (p_event->deltaY < 0) + ev->set_button_index(BUTTON_WHEEL_UP); + else if (p_event->deltaY > 0) + ev->set_button_index(BUTTON_WHEEL_DOWN); + else if (p_event->deltaX > 0) + ev->set_button_index(BUTTON_WHEEL_LEFT); + else if (p_event->deltaX < 0) + ev->set_button_index(BUTTON_WHEEL_RIGHT); + else + return false; -//interesting byt not yet -//void set_clipboard(const String& p_text); -//String get_clipboard() const; + // Different browsers give wildly different delta values, and we can't + // interpret deltaMode, so use default value for wheel events' factor. -void OS_JavaScript::set_video_mode(const VideoMode &p_video_mode, int p_screen) { + ev->set_pressed(true); + input->parse_input_event(ev); - video_mode = p_video_mode; + ev->set_pressed(false); + input->parse_input_event(ev); + + return true; } -OS::VideoMode OS_JavaScript::get_video_mode(int p_screen) const { +// Touch - return video_mode; +bool OS_JavaScript::has_touchscreen_ui_hint() const { + + /* clang-format off */ + return EM_ASM_INT_V( + return 'ontouchstart' in window; + ); + /* clang-format on */ } -Size2 OS_JavaScript::get_screen_size(int p_screen) const { +EM_BOOL OS_JavaScript::touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) { - EmscriptenFullscreenChangeEvent ev; - EMSCRIPTEN_RESULT result = emscripten_get_fullscreen_status(&ev); - ERR_FAIL_COND_V(result != EMSCRIPTEN_RESULT_SUCCESS, Size2()); - return Size2(ev.screenWidth, ev.screenHeight); -} + OS_JavaScript *os = get_singleton(); + Ref<InputEventScreenTouch> ev; + ev.instance(); + int lowest_id_index = -1; + for (int i = 0; i < p_event->numTouches; ++i) { -void OS_JavaScript::set_window_size(const Size2 p_size) { + const EmscriptenTouchPoint &touch = p_event->touches[i]; + if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier) + lowest_id_index = i; + if (!touch.isChanged) + continue; + ev->set_index(touch.identifier); + ev->set_position(Point2(touch.canvasX, touch.canvasY)); + os->touches[i] = ev->get_position(); + ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_TOUCHSTART); - windowed_size = p_size; - if (is_window_fullscreen()) { - window_maximized = false; - set_window_fullscreen(false); - } else if (is_window_maximized()) { - set_window_maximized(false); - } else { - video_mode.width = p_size.x; - video_mode.height = p_size.y; - emscripten_set_canvas_size(p_size.x, p_size.y); + os->input->parse_input_event(ev); } + return true; } -Size2 OS_JavaScript::get_window_size() const { +EM_BOOL OS_JavaScript::touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data) { - int canvas[3]; - emscripten_get_canvas_size(canvas, canvas + 1, canvas + 2); - return Size2(canvas[0], canvas[1]); -} + OS_JavaScript *os = get_singleton(); + Ref<InputEventScreenDrag> ev; + ev.instance(); + int lowest_id_index = -1; + for (int i = 0; i < p_event->numTouches; ++i) { -void OS_JavaScript::set_window_maximized(bool p_enabled) { + const EmscriptenTouchPoint &touch = p_event->touches[i]; + if (lowest_id_index == -1 || touch.identifier < p_event->touches[lowest_id_index].identifier) + lowest_id_index = i; + if (!touch.isChanged) + continue; + ev->set_index(touch.identifier); + ev->set_position(Point2(touch.canvasX, touch.canvasY)); + Point2 &prev = os->touches[i]; + ev->set_relative(ev->get_position() - prev); + prev = ev->get_position(); - window_maximized = p_enabled; - if (is_window_fullscreen()) { - set_window_fullscreen(false); - return; + os->input->parse_input_event(ev); } - // Calling emscripten_enter_soft_fullscreen mutltiple times hides all - // page elements except the canvas permanently, so track state - if (p_enabled && !soft_fs_enabled) { + return true; +} - EmscriptenFullscreenStrategy strategy; - strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; - strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF; - strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT; - strategy.canvasResizedCallback = NULL; - emscripten_enter_soft_fullscreen(NULL, &strategy); - soft_fs_enabled = true; - video_mode.width = get_window_size().width; - video_mode.height = get_window_size().height; - } else if (!p_enabled) { +// Gamepad - emscripten_exit_soft_fullscreen(); - soft_fs_enabled = false; - video_mode.width = windowed_size.width; - video_mode.height = windowed_size.height; - emscripten_set_canvas_size(video_mode.width, video_mode.height); +EM_BOOL OS_JavaScript::gamepad_change_callback(int p_event_type, const EmscriptenGamepadEvent *p_event, void *p_user_data) { + + InputDefault *input = get_singleton()->input; + if (p_event_type == EMSCRIPTEN_EVENT_GAMEPADCONNECTED) { + + String guid = ""; + if (String::utf8(p_event->mapping) == "standard") + guid = "Default HTML5 Gamepad"; + input->joy_connection_changed(p_event->index, true, String::utf8(p_event->id), guid); + } else { + input->joy_connection_changed(p_event->index, false, ""); } + return true; } -void OS_JavaScript::set_window_fullscreen(bool p_enable) { +void OS_JavaScript::process_joypads() { - if (p_enable == is_window_fullscreen()) { - return; - } + int joypad_count = emscripten_get_num_gamepads(); + for (int joypad = 0; joypad < joypad_count; joypad++) { + EmscriptenGamepadEvent state; + emscripten_get_gamepad_status(joypad, &state); + if (state.connected) { - // only requesting changes here, if successful, canvas is resized in - // _browser_resize_callback or _fullscreen_change_callback - EMSCRIPTEN_RESULT result; - if (p_enable) { - if (window_maximized) { - // soft fs during real fs can cause issues - set_window_maximized(false); - window_maximized = true; - } - EmscriptenFullscreenStrategy strategy; - strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; - strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF; - strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT; - strategy.canvasResizedCallback = NULL; - emscripten_request_fullscreen_strategy(NULL, false, &strategy); - } else { - result = emscripten_exit_fullscreen(); - if (result != EMSCRIPTEN_RESULT_SUCCESS) { - ERR_PRINTS("Failed to exit fullscreen: Code " + itos(result)); + int button_count = MIN(state.numButtons, 18); + int axis_count = MIN(state.numAxes, 8); + for (int button = 0; button < button_count; button++) { + + float value = state.analogButton[button]; + if (String::utf8(state.mapping) == "standard" && (button == JOY_ANALOG_L2 || button == JOY_ANALOG_R2)) { + InputDefault::JoyAxis joy_axis; + joy_axis.min = 0; + joy_axis.value = value; + input->joy_axis(joypad, button, joy_axis); + } else { + input->joy_button(joypad, button, value); + } + } + for (int axis = 0; axis < axis_count; axis++) { + + InputDefault::JoyAxis joy_axis; + joy_axis.min = -1; + joy_axis.value = state.axis[axis]; + input->joy_axis(joypad, axis, joy_axis); + } } } } -bool OS_JavaScript::is_window_fullscreen() const { +bool OS_JavaScript::is_joy_known(int p_device) { - return video_mode.fullscreen; + return input->is_joy_mapped(p_device); } -void OS_JavaScript::request_canvas_size_adjustment() { +String OS_JavaScript::get_joy_guid(int p_device) const { - canvas_size_adjustment_requested = true; + return input->get_joy_guid_remapped(p_device); } -void OS_JavaScript::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const { +// Video - Size2 screen = get_screen_size(); - p_list->push_back(OS::VideoMode(screen.width, screen.height, true)); +int OS_JavaScript::get_video_driver_count() const { + + return VIDEO_DRIVER_MAX; } -String OS_JavaScript::get_name() { +const char *OS_JavaScript::get_video_driver_name(int p_driver) const { - return "HTML5"; + switch (p_driver) { + case VIDEO_DRIVER_GLES3: + return "GLES3"; + case VIDEO_DRIVER_GLES2: + return "GLES2"; + } + ERR_EXPLAIN("Invalid video driver index " + itos(p_driver)); + ERR_FAIL_V(NULL); } -MainLoop *OS_JavaScript::get_main_loop() const { +// Audio - return main_loop; +int OS_JavaScript::get_audio_driver_count() const { + + return 1; } -bool OS_JavaScript::can_draw() const { +const char *OS_JavaScript::get_audio_driver_name(int p_driver) const { - return true; //always? + return "JavaScript"; } -void OS_JavaScript::set_cursor_shape(CursorShape p_shape) { +// Lifecycle - ERR_FAIL_INDEX(p_shape, CURSOR_MAX); +void OS_JavaScript::initialize_core() { - cursor_shape = p_shape; - if (get_mouse_mode() != MOUSE_MODE_HIDDEN) - set_css_cursor(godot2dom_cursor(cursor_shape)); + OS_Unix::initialize_core(); + FileAccess::make_default<FileAccessBufferedFA<FileAccessUnix> >(FileAccess::ACCESS_RESOURCES); } -void OS_JavaScript::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { -} +Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { + + EmscriptenWebGLContextAttributes attributes; + emscripten_webgl_init_context_attributes(&attributes); + attributes.alpha = false; + attributes.antialias = false; + ERR_FAIL_INDEX_V(p_video_driver, VIDEO_DRIVER_MAX, ERR_INVALID_PARAMETER); + switch (p_video_driver) { + case VIDEO_DRIVER_GLES3: + attributes.majorVersion = 2; + RasterizerGLES3::register_config(); + RasterizerGLES3::make_current(); + break; + case VIDEO_DRIVER_GLES2: + attributes.majorVersion = 1; + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + break; + } + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(NULL, &attributes); + ERR_EXPLAIN("WebGL " + itos(attributes.majorVersion) + ".0 not available"); + ERR_FAIL_COND_V(emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS, ERR_UNAVAILABLE); -void OS_JavaScript::main_loop_begin() { + video_mode = p_desired; + // Can't fulfil fullscreen request during start-up due to browser security. + video_mode.fullscreen = false; + /* clang-format off */ + if (EM_ASM_INT_V({ return Module.resizeCanvasOnStart })) { + /* clang-format on */ + set_window_size(Size2(video_mode.width, video_mode.height)); + } else { + set_window_size(get_window_size()); + } - if (main_loop) - main_loop->init(); + char locale_ptr[16]; + /* clang-format off */ + EM_ASM_ARGS({ + stringToUTF8(Module.locale, $0, 16); + }, locale_ptr); + /* clang-format on */ + setenv("LANG", locale_ptr, true); + + AudioDriverManager::initialize(p_audio_driver); + VisualServer *visual_server = memnew(VisualServerRaster()); + input = memnew(InputDefault); + + EMSCRIPTEN_RESULT result; +#define EM_CHECK(ev) \ + if (result != EMSCRIPTEN_RESULT_SUCCESS) \ + ERR_PRINTS("Error while setting " #ev " callback: Code " + itos(result)) +#define SET_EM_CALLBACK(target, ev, cb) \ + result = emscripten_set_##ev##_callback(target, NULL, true, &cb); \ + EM_CHECK(ev) +#define SET_EM_CALLBACK_NOTARGET(ev, cb) \ + result = emscripten_set_##ev##_callback(NULL, true, &cb); \ + EM_CHECK(ev) + // These callbacks from Emscripten's html5.h suffice to access most + // JavaScript APIs. For APIs that are not (sufficiently) exposed, EM_ASM + // is used below. + SET_EM_CALLBACK("#window", mousemove, mousemove_callback) + SET_EM_CALLBACK("#canvas", mousedown, mouse_button_callback) + SET_EM_CALLBACK("#window", mouseup, mouse_button_callback) + SET_EM_CALLBACK("#window", wheel, wheel_callback) + SET_EM_CALLBACK("#window", touchstart, touch_press_callback) + SET_EM_CALLBACK("#window", touchmove, touchmove_callback) + SET_EM_CALLBACK("#window", touchend, touch_press_callback) + SET_EM_CALLBACK("#window", touchcancel, touch_press_callback) + SET_EM_CALLBACK("#canvas", keydown, keydown_callback) + SET_EM_CALLBACK("#canvas", keypress, keypress_callback) + SET_EM_CALLBACK("#canvas", keyup, keyup_callback) + SET_EM_CALLBACK(NULL, resize, browser_resize_callback) + SET_EM_CALLBACK(NULL, fullscreenchange, fullscreen_change_callback) + SET_EM_CALLBACK_NOTARGET(gamepadconnected, gamepad_change_callback) + SET_EM_CALLBACK_NOTARGET(gamepaddisconnected, gamepad_change_callback) +#undef SET_EM_CALLBACK_NODATA +#undef SET_EM_CALLBACK +#undef EM_CHECK /* clang-format off */ EM_ASM_ARGS({ const send_notification = cwrap('send_notification', null, ['number']); - const notifs = arguments; - (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, i) { - Module.canvas.addEventListener(event, send_notification.bind(null, notifs[i])); + const notifications = arguments; + (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, index) { + Module.canvas.addEventListener(event, send_notification.bind(null, notifications[index])); }); }, MainLoop::NOTIFICATION_WM_MOUSE_ENTER, @@ -772,22 +742,44 @@ void OS_JavaScript::main_loop_begin() { MainLoop::NOTIFICATION_WM_FOCUS_OUT ); /* clang-format on */ + + visual_server->init(); + + return OK; } -bool OS_JavaScript::main_loop_iterate() { +void OS_JavaScript::set_main_loop(MainLoop *p_main_loop) { - if (!main_loop) - return false; + main_loop = p_main_loop; + input->set_main_loop(p_main_loop); +} + +MainLoop *OS_JavaScript::get_main_loop() const { + + return main_loop; +} + +void OS_JavaScript::run_async() { + + main_loop->init(); + emscripten_set_main_loop(main_loop_callback, -1, false); +} + +void OS_JavaScript::main_loop_callback() { + + get_singleton()->main_loop_iterate(); +} + +bool OS_JavaScript::main_loop_iterate() { - if (idbfs_available && time_to_save_sync >= 0) { - int64_t newtime = get_ticks_msec(); - int64_t elapsed = newtime - last_sync_time; - last_sync_time = newtime; + if (is_userfs_persistent() && sync_wait_time >= 0) { + int64_t current_time = get_ticks_msec(); + int64_t elapsed_time = current_time - last_sync_check_time; + last_sync_check_time = current_time; - time_to_save_sync -= elapsed; + sync_wait_time -= elapsed_time; - if (time_to_save_sync < 0) { - //time to sync, for real + if (sync_wait_time < 0) { /* clang-format off */ EM_ASM( FS.syncfs(function(err) { @@ -812,121 +804,101 @@ bool OS_JavaScript::main_loop_iterate() { return Main::iteration(); } -void OS_JavaScript::main_loop_end() { +void OS_JavaScript::delete_main_loop() { - if (main_loop) - main_loop->finish(); + memdelete(main_loop); } -void OS_JavaScript::process_accelerometer(const Vector3 &p_accelerometer) { +void OS_JavaScript::finalize() { - input->set_accelerometer(p_accelerometer); + memdelete(input); } -bool OS_JavaScript::has_touchscreen_ui_hint() const { +// Miscellaneous - /* clang-format off */ - return EM_ASM_INT_V( - return 'ontouchstart' in window; - ); - /* clang-format on */ +extern "C" EMSCRIPTEN_KEEPALIVE void send_notification(int p_notification) { + + if (p_notification == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || p_notification == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) { + cursor_inside_canvas = p_notification == MainLoop::NOTIFICATION_WM_MOUSE_ENTER; + } + OS_JavaScript::get_singleton()->get_main_loop()->notification(p_notification); } -void OS_JavaScript::main_loop_request_quit() { +bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) { + + if (p_feature == "HTML5" || p_feature == "web") + return true; + +#ifdef JAVASCRIPT_EVAL_ENABLED + if (p_feature == "JavaScript") + return true; +#endif + + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_get_current_context(); + // All extensions are already automatically enabled, this function allows + // checking WebGL extension support without inline JavaScript + if (p_feature == "s3tc") + return emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_s3tc_srgb"); + if (p_feature == "etc") + return emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc1"); + if (p_feature == "etc2") + return emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc"); - if (main_loop) - main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); + return false; } -Error OS_JavaScript::shell_open(String p_uri) { +void OS_JavaScript::alert(const String &p_alert, const String &p_title) { + /* clang-format off */ EM_ASM_({ - window.open(UTF8ToString($0), '_blank'); - }, p_uri.utf8().get_data()); + window.alert(UTF8ToString($0)); + }, p_alert.utf8().get_data()); /* clang-format on */ - return OK; } -String OS_JavaScript::get_resource_dir() const { +void OS_JavaScript::set_window_title(const String &p_title) { - return "/"; //javascript has it's own filesystem for resources inside the APK + /* clang-format off */ + EM_ASM_({ + document.title = UTF8ToString($0); + }, p_title.utf8().get_data()); + /* clang-format on */ } -String OS_JavaScript::get_user_data_dir() const { - - /* - if (get_user_data_dir_func) - return get_user_data_dir_func(); - */ - return "/userfs"; -}; - String OS_JavaScript::get_executable_path() const { return OS::get_executable_path(); } -void OS_JavaScript::_close_notification_funcs(const String &p_file, int p_flags) { +Error OS_JavaScript::shell_open(String p_uri) { - OS_JavaScript *os = static_cast<OS_JavaScript *>(get_singleton()); - if (os->idbfs_available && p_file.begins_with("/userfs") && p_flags & FileAccess::WRITE) { - os->last_sync_time = OS::get_singleton()->get_ticks_msec(); - os->time_to_save_sync = 5000; //five seconds since last save - } + // Open URI in a new tab, browser will deal with it by protocol. + /* clang-format off */ + EM_ASM_({ + window.open(UTF8ToString($0), '_blank'); + }, p_uri.utf8().get_data()); + /* clang-format on */ + return OK; } -void OS_JavaScript::process_joypads() { - - int joy_count = emscripten_get_num_gamepads(); - for (int i = 0; i < joy_count; i++) { - EmscriptenGamepadEvent state; - emscripten_get_gamepad_status(i, &state); - if (state.connected) { +String OS_JavaScript::get_name() { - int num_buttons = MIN(state.numButtons, 18); - int num_axes = MIN(state.numAxes, 8); - for (int j = 0; j < num_buttons; j++) { + return "HTML5"; +} - float value = state.analogButton[j]; - if (String(state.mapping) == "standard" && (j == 6 || j == 7)) { - InputDefault::JoyAxis jx; - jx.min = 0; - jx.value = value; - input->joy_axis(i, j, jx); - } else { - input->joy_button(i, j, value); - } - } - for (int j = 0; j < num_axes; j++) { +bool OS_JavaScript::can_draw() const { - InputDefault::JoyAxis jx; - jx.min = -1; - jx.value = state.axis[j]; - input->joy_axis(i, j, jx); - } - } - } + return true; // Always? } -bool OS_JavaScript::joy_connection_changed(int p_type, const EmscriptenGamepadEvent *p_event) { - if (p_type == EMSCRIPTEN_EVENT_GAMEPADCONNECTED) { +String OS_JavaScript::get_user_data_dir() const { - String guid = ""; - if (String(p_event->mapping) == "standard") - guid = "Default HTML5 Gamepad"; - input->joy_connection_changed(p_event->index, true, String(p_event->id), guid); - } else { - input->joy_connection_changed(p_event->index, false, ""); - } - return true; -} + return "/userfs"; +}; -bool OS_JavaScript::is_joy_known(int p_device) { - return input->is_joy_mapped(p_device); -} +String OS_JavaScript::get_resource_dir() const { -String OS_JavaScript::get_joy_guid(int p_device) const { - return input->get_joy_guid_remapped(p_device); + return "/"; } OS::PowerState OS_JavaScript::get_power_state() { @@ -947,59 +919,53 @@ int OS_JavaScript::get_power_percent_left() { return -1; } -bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) { +void OS_JavaScript::file_access_close_callback(const String &p_file, int p_flags) { - if (p_feature == "HTML5" || p_feature == "web") - return true; - -#ifdef JAVASCRIPT_EVAL_ENABLED - if (p_feature == "JavaScript") - return true; -#endif + OS_JavaScript *os = get_singleton(); + if (os->is_userfs_persistent() && p_file.begins_with("/userfs") && p_flags & FileAccess::WRITE) { + os->last_sync_check_time = OS::get_singleton()->get_ticks_msec(); + // Wait five seconds in case more files are about to be closed. + os->sync_wait_time = 5000; + } +} - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_get_current_context(); - // all extensions are already automatically enabled, this function allows - // checking WebGL extension support without inline JavaScript - if (p_feature == "s3tc" && emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_s3tc_srgb")) - return true; - if (p_feature == "etc" && emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc1")) - return true; - if (p_feature == "etc2" && emscripten_webgl_enable_extension(ctx, "WEBGL_compressed_texture_etc")) - return true; +void OS_JavaScript::set_idb_available(bool p_idb_available) { - return false; + idb_available = p_idb_available; } -void OS_JavaScript::set_idbfs_available(bool p_idbfs_available) { +bool OS_JavaScript::is_userfs_persistent() const { - idbfs_available = p_idbfs_available; + return idb_available; } -bool OS_JavaScript::is_userfs_persistent() const { +OS_JavaScript *OS_JavaScript::get_singleton() { - return idbfs_available; + return static_cast<OS_JavaScript *>(OS::get_singleton()); } -OS_JavaScript::OS_JavaScript(const char *p_execpath, GetUserDataDirFunc p_get_user_data_dir_func) { +OS_JavaScript::OS_JavaScript(int p_argc, char *p_argv[]) { + + List<String> arguments; + for (int i = 1; i < p_argc; i++) { + arguments.push_back(String::utf8(p_argv[i])); + } + set_cmdline(p_argv[0], arguments); - set_cmdline(p_execpath, get_cmdline_args()); - main_loop = NULL; window_maximized = false; - soft_fs_enabled = false; + soft_fullscreen_enabled = false; canvas_size_adjustment_requested = false; - get_user_data_dir_func = p_get_user_data_dir_func; - FileAccessUnix::close_notification_func = _close_notification_funcs; + main_loop = NULL; - idbfs_available = false; - time_to_save_sync = -1; + idb_available = false; + sync_wait_time = -1; + + AudioDriverManager::add_driver(&audio_driver_javascript); Vector<Logger *> loggers; loggers.push_back(memnew(StdLogger)); _set_logger(memnew(CompositeLogger(loggers))); - AudioDriverManager::add_driver(&audio_driver_javascript); -} - -OS_JavaScript::~OS_JavaScript() { + FileAccessUnix::close_notification_func = file_access_close_callback; } diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index 46eb1b3f13..503c92585b 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -32,52 +32,56 @@ #define OS_JAVASCRIPT_H #include "audio_driver_javascript.h" -#include "drivers/unix/os_unix.h" #include "main/input_default.h" -#include "os/input.h" -#include "os/main_loop.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" +#include "unix/os_unix.h" #include <emscripten/html5.h> -typedef String (*GetUserDataDirFunc)(); - class OS_JavaScript : public OS_Unix { - bool idbfs_available; - int64_t time_to_save_sync; - int64_t last_sync_time; - - VisualServer *visual_server; - AudioDriverJavaScript audio_driver_javascript; - - InputDefault *input; + VideoMode video_mode; Vector2 windowed_size; bool window_maximized; - bool soft_fs_enabled; + bool soft_fullscreen_enabled; bool canvas_size_adjustment_requested; - VideoMode video_mode; + + InputDefault *input; + Ref<InputEventKey> deferred_key_event; CursorShape cursor_shape; + Point2 touches[32]; + MainLoop *main_loop; + AudioDriverJavaScript audio_driver_javascript; - GetUserDataDirFunc get_user_data_dir_func; + bool idb_available; + int64_t sync_wait_time; + int64_t last_sync_check_time; - static void _close_notification_funcs(const String &p_file, int p_flags); + static EM_BOOL browser_resize_callback(int p_event_type, const EmscriptenUiEvent *p_event, void *p_user_data); + static EM_BOOL fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data); - void process_joypads(); + static EM_BOOL keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data); + static EM_BOOL keypress_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data); + static EM_BOOL keyup_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data); - void set_css_cursor(const char *); - const char *get_css_cursor() const; + static EM_BOOL mousemove_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data); + static EM_BOOL mouse_button_callback(int p_event_type, const EmscriptenMouseEvent *p_event, void *p_user_data); -public: - // functions used by main to initialize/deintialize the OS - virtual int get_video_driver_count() const; - virtual const char *get_video_driver_name(int p_driver) const; + static EM_BOOL wheel_callback(int p_event_type, const EmscriptenWheelEvent *p_event, void *p_user_data); - virtual int get_audio_driver_count() const; - virtual const char *get_audio_driver_name(int p_driver) const; + static EM_BOOL touch_press_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data); + static EM_BOOL touchmove_callback(int p_event_type, const EmscriptenTouchEvent *p_event, void *p_user_data); + + static EM_BOOL gamepad_change_callback(int p_event_type, const EmscriptenGamepadEvent *p_event, void *p_user_data); + void process_joypads(); + static void main_loop_callback(); + + static void file_access_close_callback(const String &p_file, int p_flags); + +protected: virtual void initialize_core(); virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); @@ -86,77 +90,64 @@ public: virtual void finalize(); - typedef int64_t ProcessID; - - //static OS* get_singleton(); - - virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); - - virtual void set_mouse_mode(MouseMode p_mode); - virtual MouseMode get_mouse_mode() const; - virtual Point2 get_mouse_position() const; - virtual int get_mouse_button_state() const; - virtual void set_window_title(const String &p_title); + virtual bool _check_internal_feature_support(const String &p_feature); - //virtual void set_clipboard(const String& p_text); - //virtual String get_clipboard() const; +public: + // Override return type to make writing static callbacks less tedious. + static OS_JavaScript *get_singleton(); virtual void set_video_mode(const VideoMode &p_video_mode, int p_screen = 0); virtual VideoMode get_video_mode(int p_screen = 0) const; virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const; - virtual Size2 get_screen_size(int p_screen = -1) const; - virtual void set_window_size(const Size2); virtual Size2 get_window_size() const; virtual void set_window_maximized(bool p_enabled); - virtual bool is_window_maximized() const { return window_maximized; } - virtual void set_window_fullscreen(bool p_enable); + virtual bool is_window_maximized() const; + virtual void set_window_fullscreen(bool p_enabled); virtual bool is_window_fullscreen() const; + virtual Size2 get_screen_size(int p_screen = -1) const; - void request_canvas_size_adjustment(); + virtual Point2 get_mouse_position() const; + virtual int get_mouse_button_state() const; + virtual void set_cursor_shape(CursorShape p_shape); + virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); + virtual void set_mouse_mode(MouseMode p_mode); + virtual MouseMode get_mouse_mode() const; - virtual String get_name(); - virtual MainLoop *get_main_loop() const; + virtual bool has_touchscreen_ui_hint() const; - virtual bool can_draw() const; + virtual bool is_joy_known(int p_device); + virtual String get_joy_guid(int p_device) const; - virtual bool is_userfs_persistent() const; + virtual int get_video_driver_count() const; + virtual const char *get_video_driver_name(int p_driver) const; - virtual void set_cursor_shape(CursorShape p_shape); - virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); + virtual int get_audio_driver_count() const; + virtual const char *get_audio_driver_name(int p_driver) const; - void main_loop_begin(); + virtual MainLoop *get_main_loop() const; + void run_async(); bool main_loop_iterate(); - void main_loop_request_quit(); - void main_loop_end(); - void main_loop_focusout(); - void main_loop_focusin(); - virtual bool has_touchscreen_ui_hint() const; - - virtual Error shell_open(String p_uri); - virtual String get_user_data_dir() const; + virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); + virtual void set_window_title(const String &p_title); String get_executable_path() const; - virtual String get_resource_dir() const; - - void process_accelerometer(const Vector3 &p_accelerometer); - void push_input(const Ref<InputEvent> &p_ev); + virtual Error shell_open(String p_uri); + virtual String get_name(); + virtual bool can_draw() const; - virtual bool is_joy_known(int p_device); - virtual String get_joy_guid(int p_device) const; - bool joy_connection_changed(int p_type, const EmscriptenGamepadEvent *p_event); + virtual String get_resource_dir() const; + virtual String get_user_data_dir() const; virtual OS::PowerState get_power_state(); virtual int get_power_seconds_left(); virtual int get_power_percent_left(); - virtual bool _check_internal_feature_support(const String &p_feature); - - void set_idbfs_available(bool p_idbfs_available); + void set_idb_available(bool p_idb_available); + virtual bool is_userfs_persistent() const; - OS_JavaScript(const char *p_execpath, GetUserDataDirFunc p_get_user_data_dir_func); - ~OS_JavaScript(); + OS_JavaScript(int p_argc, char *p_argv[]); }; #endif diff --git a/platform/javascript/run_icon.png b/platform/javascript/run_icon.png Binary files differindex dedee6f479..574abb0150 100644 --- a/platform/javascript/run_icon.png +++ b/platform/javascript/run_icon.png diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm index 99ce25adfb..1664c5ce8e 100644 --- a/platform/osx/crash_handler_osx.mm +++ b/platform/osx/crash_handler_osx.mm @@ -78,6 +78,10 @@ static void handle_crash(int sig) { // Dump the backtrace to stderr with a message to the user fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig); + + if (OS::get_singleton()->get_main_loop()) + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH); + fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str()); char **strings = backtrace_symbols(bt_buffer, size); if (strings) { diff --git a/platform/osx/logo.png b/platform/osx/logo.png Binary files differindex 93c6890e85..62086fc415 100644 --- a/platform/osx/logo.png +++ b/platform/osx/logo.png diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 2b2d21553b..7bd5b16f36 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -117,6 +117,7 @@ public: String open_with_filename; Point2 im_position; + bool im_active; ImeCallback im_callback; void *im_target; @@ -233,6 +234,7 @@ public: virtual bool get_window_per_pixel_transparency_enabled() const; virtual void set_window_per_pixel_transparency_enabled(bool p_enabled); + virtual void set_ime_active(const bool p_active); virtual void set_ime_position(const Point2 &p_pos); virtual void set_ime_intermediate_text_callback(ImeCallback p_callback, void *p_inp); diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index a49ae1a2f3..41a19ac992 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -625,10 +625,18 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { - (void)otherMouseDown:(NSEvent *)event { - if ((int)[event buttonNumber] != 2) - return; + if ((int)[event buttonNumber] == 2) { + _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, true); + + } else if ((int)[event buttonNumber] == 3) { + _mouseDownEvent(event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, true); - _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, true); + } else if ((int)[event buttonNumber] == 4) { + _mouseDownEvent(event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, true); + + } else { + return; + } } - (void)otherMouseDragged:(NSEvent *)event { @@ -637,10 +645,18 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { - (void)otherMouseUp:(NSEvent *)event { - if ((int)[event buttonNumber] != 2) - return; + if ((int)[event buttonNumber] == 2) { + _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, false); - _mouseDownEvent(event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, false); + } else if ((int)[event buttonNumber] == 3) { + _mouseDownEvent(event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, false); + + } else if ((int)[event buttonNumber] == 4) { + _mouseDownEvent(event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, false); + + } else { + return; + } } - (void)mouseExited:(NSEvent *)event { @@ -959,7 +975,7 @@ static int remapKey(unsigned int key) { push_to_key_event_buffer(ke); } - if ((OS_OSX::singleton->im_position.x != 0) && (OS_OSX::singleton->im_position.y != 0)) + if (OS_OSX::singleton->im_active == true) [self interpretKeyEvents:[NSArray arrayWithObject:event]]; } @@ -1129,6 +1145,10 @@ String OS_OSX::get_unique_id() const { return serial_number; } +void OS_OSX::set_ime_active(const bool p_active) { + im_active = p_active; +} + void OS_OSX::set_ime_position(const Point2 &p_pos) { im_position = p_pos; } @@ -2542,6 +2562,7 @@ OS_OSX::OS_OSX() { mouse_mode = OS::MOUSE_MODE_VISIBLE; main_loop = NULL; singleton = this; + im_active = false; im_position = Point2(); im_callback = NULL; im_target = NULL; diff --git a/platform/server/logo.png b/platform/server/logo.png Binary files differindex 5e98ac26ec..8666ada9ca 100644 --- a/platform/server/logo.png +++ b/platform/server/logo.png diff --git a/platform/windows/crash_handler_win.cpp b/platform/windows/crash_handler_win.cpp index 804c2d44eb..76a227c608 100644 --- a/platform/windows/crash_handler_win.cpp +++ b/platform/windows/crash_handler_win.cpp @@ -124,6 +124,9 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { fprintf(stderr, "%s: Program crashed\n", __FUNCTION__); + if (OS::get_singleton()->get_main_loop()) + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH); + // Load the symbols: if (!SymInitialize(process, NULL, false)) return EXCEPTION_CONTINUE_SEARCH; diff --git a/platform/windows/logo.png b/platform/windows/logo.png Binary files differindex 4376abd563..f06b463850 100644 --- a/platform/windows/logo.png +++ b/platform/windows/logo.png diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 3e0c4a7c0c..05d16a5964 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -70,6 +70,30 @@ __attribute__((visibility("default"))) DWORD NvOptimusEnablement = 0x00000001; #define WM_TOUCH 576 #endif +typedef struct { + int count; + int screen; + Size2 size; +} EnumSizeData; + +typedef struct { + int count; + int screen; + Point2 pos; +} EnumPosData; + +static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { + + EnumSizeData *data = (EnumSizeData *)dwData; + if (data->count == data->screen) { + data->size.x = lprcMonitor->right - lprcMonitor->left; + data->size.y = lprcMonitor->bottom - lprcMonitor->top; + } + + data->count++; + return TRUE; +} + static String format_error_message(DWORD id) { LPWSTR messageBuffer = NULL; @@ -410,11 +434,12 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) bmask |= (wParam & MK_LBUTTON) ? (1 << 0) : 0; bmask |= (wParam & MK_RBUTTON) ? (1 << 1) : 0; bmask |= (wParam & MK_MBUTTON) ? (1 << 2) : 0; + bmask |= (wParam & MK_XBUTTON1) ? (1 << 7) : 0; + bmask |= (wParam & MK_XBUTTON2) ? (1 << 8) : 0; mm->set_button_mask(bmask); last_button_state = mm->get_button_mask(); - /*mm->get_button_mask()|=(wParam&MK_XBUTTON1)?(1<<5):0; - mm->get_button_mask()|=(wParam&MK_XBUTTON2)?(1<<6):0;*/ + mm->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); mm->set_global_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); @@ -455,6 +480,13 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } break; case WM_LBUTTONDOWN: case WM_LBUTTONUP: + if (input->is_emulating_mouse_from_touch()) { + // Universal translation enabled; ignore OS translations for left button + LPARAM extra = GetMessageExtraInfo(); + if (IsPenEvent(extra)) { + break; + } + } case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_RBUTTONDOWN: @@ -464,161 +496,168 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_LBUTTONDBLCLK: case WM_MBUTTONDBLCLK: case WM_RBUTTONDBLCLK: - /*case WM_XBUTTONDOWN: - case WM_XBUTTONUP: */ { - - if (input->is_emulating_mouse_from_touch()) { - // Universal translation enabled; ignore OS translation - LPARAM extra = GetMessageExtraInfo(); - if (IsPenEvent(extra)) { - break; - } - } + case WM_XBUTTONDBLCLK: + case WM_XBUTTONDOWN: + case WM_XBUTTONUP: { - Ref<InputEventMouseButton> mb; - mb.instance(); - - switch (uMsg) { - case WM_LBUTTONDOWN: { - mb->set_pressed(true); - mb->set_button_index(1); - } break; - case WM_LBUTTONUP: { - mb->set_pressed(false); - mb->set_button_index(1); - } break; - case WM_MBUTTONDOWN: { - mb->set_pressed(true); - mb->set_button_index(3); - - } break; - case WM_MBUTTONUP: { - mb->set_pressed(false); - mb->set_button_index(3); - } break; - case WM_RBUTTONDOWN: { - mb->set_pressed(true); - mb->set_button_index(2); - } break; - case WM_RBUTTONUP: { - mb->set_pressed(false); - mb->set_button_index(2); - } break; - case WM_LBUTTONDBLCLK: { - - mb->set_pressed(true); - mb->set_button_index(1); - mb->set_doubleclick(true); - } break; - case WM_RBUTTONDBLCLK: { - - mb->set_pressed(true); - mb->set_button_index(2); - mb->set_doubleclick(true); - } break; - case WM_MBUTTONDBLCLK: { - - mb->set_pressed(true); - mb->set_button_index(3); - mb->set_doubleclick(true); - } break; - case WM_MOUSEWHEEL: { - - mb->set_pressed(true); - int motion = (short)HIWORD(wParam); - if (!motion) - return 0; - - if (motion > 0) - mb->set_button_index(BUTTON_WHEEL_UP); - else - mb->set_button_index(BUTTON_WHEEL_DOWN); - - } break; - case WM_MOUSEHWHEEL: { - - mb->set_pressed(true); - int motion = (short)HIWORD(wParam); - if (!motion) - return 0; - - if (motion < 0) { - mb->set_button_index(BUTTON_WHEEL_LEFT); - mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); - } else { - mb->set_button_index(BUTTON_WHEEL_RIGHT); - mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); - } - } break; - /* - case WM_XBUTTONDOWN: { - mb->is_pressed()=true; - mb->get_button_index()=(HIWORD(wParam)==XBUTTON1)?6:7; + Ref<InputEventMouseButton> mb; + mb.instance(); + + switch (uMsg) { + case WM_LBUTTONDOWN: { + mb->set_pressed(true); + mb->set_button_index(1); } break; - case WM_XBUTTONUP: - mb->is_pressed()=true; - mb->get_button_index()=(HIWORD(wParam)==XBUTTON1)?6:7; - } break;*/ - default: { return 0; } - } + case WM_LBUTTONUP: { + mb->set_pressed(false); + mb->set_button_index(1); + } break; + case WM_MBUTTONDOWN: { + mb->set_pressed(true); + mb->set_button_index(3); - mb->set_control((wParam & MK_CONTROL) != 0); - mb->set_shift((wParam & MK_SHIFT) != 0); - mb->set_alt(alt_mem); - //mb->get_alt()=(wParam&MK_MENU)!=0; - int bmask = 0; - bmask |= (wParam & MK_LBUTTON) ? (1 << 0) : 0; - bmask |= (wParam & MK_RBUTTON) ? (1 << 1) : 0; - bmask |= (wParam & MK_MBUTTON) ? (1 << 2) : 0; - mb->set_button_mask(bmask); - - last_button_state = mb->get_button_mask(); - /* - mb->get_button_mask()|=(wParam&MK_XBUTTON1)?(1<<5):0; - mb->get_button_mask()|=(wParam&MK_XBUTTON2)?(1<<6):0;*/ - mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); - - if (mouse_mode == MOUSE_MODE_CAPTURED) { - - mb->set_position(Vector2(old_x, old_y)); - } + } break; + case WM_MBUTTONUP: { + mb->set_pressed(false); + mb->set_button_index(3); + } break; + case WM_RBUTTONDOWN: { + mb->set_pressed(true); + mb->set_button_index(2); + } break; + case WM_RBUTTONUP: { + mb->set_pressed(false); + mb->set_button_index(2); + } break; + case WM_LBUTTONDBLCLK: { - if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) { - if (mb->is_pressed()) { + mb->set_pressed(true); + mb->set_button_index(1); + mb->set_doubleclick(true); + } break; + case WM_RBUTTONDBLCLK: { - if (++pressrc > 0) - SetCapture(hWnd); - } else { + mb->set_pressed(true); + mb->set_button_index(2); + mb->set_doubleclick(true); + } break; + case WM_MBUTTONDBLCLK: { + + mb->set_pressed(true); + mb->set_button_index(3); + mb->set_doubleclick(true); + } break; + case WM_MOUSEWHEEL: { + + mb->set_pressed(true); + int motion = (short)HIWORD(wParam); + if (!motion) + return 0; + + if (motion > 0) + mb->set_button_index(BUTTON_WHEEL_UP); + else + mb->set_button_index(BUTTON_WHEEL_DOWN); - if (--pressrc <= 0) { - ReleaseCapture(); - pressrc = 0; - } + } break; + case WM_MOUSEHWHEEL: { + + mb->set_pressed(true); + int motion = (short)HIWORD(wParam); + if (!motion) + return 0; + + if (motion < 0) { + mb->set_button_index(BUTTON_WHEEL_LEFT); + mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); + } else { + mb->set_button_index(BUTTON_WHEEL_RIGHT); + mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); } - } else if (mouse_mode != MOUSE_MODE_CAPTURED) { - // for reasons unknown to mankind, wheel comes in screen cordinates - POINT coords; - coords.x = mb->get_position().x; - coords.y = mb->get_position().y; + } break; + case WM_XBUTTONDOWN: { - ScreenToClient(hWnd, &coords); + mb->set_pressed(true); + if (HIWORD(wParam) == XBUTTON1) + mb->set_button_index(BUTTON_XBUTTON1); + else + mb->set_button_index(BUTTON_XBUTTON2); + } break; + case WM_XBUTTONUP: { - mb->set_position(Vector2(coords.x, coords.y)); - } + mb->set_pressed(false); + if (HIWORD(wParam) == XBUTTON1) + mb->set_button_index(BUTTON_XBUTTON1); + else + mb->set_button_index(BUTTON_XBUTTON2); + } break; + case WM_XBUTTONDBLCLK: { - mb->set_global_position(mb->get_position()); + mb->set_pressed(true); + if (HIWORD(wParam) == XBUTTON1) + mb->set_button_index(BUTTON_XBUTTON1); + else + mb->set_button_index(BUTTON_XBUTTON2); + mb->set_doubleclick(true); + } break; + default: { return 0; } + } - if (main_loop) { - input->parse_input_event(mb); - if (mb->is_pressed() && mb->get_button_index() > 3) { - //send release for mouse wheel - Ref<InputEventMouseButton> mbd = mb->duplicate(); - mbd->set_pressed(false); - input->parse_input_event(mbd); + mb->set_control((wParam & MK_CONTROL) != 0); + mb->set_shift((wParam & MK_SHIFT) != 0); + mb->set_alt(alt_mem); + //mb->get_alt()=(wParam&MK_MENU)!=0; + int bmask = 0; + bmask |= (wParam & MK_LBUTTON) ? (1 << 0) : 0; + bmask |= (wParam & MK_RBUTTON) ? (1 << 1) : 0; + bmask |= (wParam & MK_MBUTTON) ? (1 << 2) : 0; + bmask |= (wParam & MK_XBUTTON1) ? (1 << 7) : 0; + bmask |= (wParam & MK_XBUTTON2) ? (1 << 8) : 0; + mb->set_button_mask(bmask); + + last_button_state = mb->get_button_mask(); + mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))); + + if (mouse_mode == MOUSE_MODE_CAPTURED) { + + mb->set_position(Vector2(old_x, old_y)); + } + + if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) { + if (mb->is_pressed()) { + + if (++pressrc > 0) + SetCapture(hWnd); + } else { + + if (--pressrc <= 0) { + ReleaseCapture(); + pressrc = 0; } } + } else if (mouse_mode != MOUSE_MODE_CAPTURED) { + // for reasons unknown to mankind, wheel comes in screen cordinates + POINT coords; + coords.x = mb->get_position().x; + coords.y = mb->get_position().y; + + ScreenToClient(hWnd, &coords); + + mb->set_position(Vector2(coords.x, coords.y)); } - break; + + mb->set_global_position(mb->get_position()); + + if (main_loop) { + input->parse_input_event(mb); + if (mb->is_pressed() && mb->get_button_index() > 3 && mb->get_button_index() < 8) { + //send release for mouse wheel + Ref<InputEventMouseButton> mbd = mb->duplicate(); + mbd->set_pressed(false); + input->parse_input_event(mbd); + } + } + } break; case WM_SIZE: { int window_w = LOWORD(lParam); @@ -742,13 +781,18 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))) { for (UINT i = 0; i < cInputs; i++) { TOUCHINPUT ti = pInputs[i]; + POINT touch_pos = { + TOUCH_COORD_TO_PIXEL(ti.x), + TOUCH_COORD_TO_PIXEL(ti.y), + }; + ScreenToClient(hWnd, &touch_pos); //do something with each touch input entry if (ti.dwFlags & TOUCHEVENTF_MOVE) { - _drag_event(ti.x / 100.0f, ti.y / 100.0f, ti.dwID); + _drag_event(touch_pos.x, touch_pos.y, ti.dwID); } else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) { - _touch_event(ti.dwFlags & TOUCHEVENTF_DOWN, ti.x / 100.0f, ti.y / 100.0f, ti.dwID); + _touch_event(ti.dwFlags & TOUCHEVENTF_DOWN, touch_pos.x, touch_pos.y, ti.dwID); }; } bHandled = TRUE; @@ -976,6 +1020,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int WNDCLASSEXW wc; if (is_hidpi_allowed()) { + print_line("hidpi aware?"); HMODULE Shcore = LoadLibraryW(L"Shcore.dll"); if (Shcore != NULL) { @@ -1020,6 +1065,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int pre_fs_valid = true; if (video_mode.fullscreen) { + /* this returns DPI unaware size, commenting DEVMODE current; memset(¤t, 0, sizeof(current)); EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, ¤t); @@ -1027,6 +1073,16 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int WindowRect.right = current.dmPelsWidth; WindowRect.bottom = current.dmPelsHeight; + */ + + EnumSizeData data = { 0, 0, Size2() }; + EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcSize, (LPARAM)&data); + + WindowRect.right = data.size.width; + WindowRect.bottom = data.size.height; + + print_line("wr right " + itos(WindowRect.right) + ", " + itos(WindowRect.bottom)); + /* DEVMODE dmScreenSettings; memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); dmScreenSettings.dmSize=sizeof(dmScreenSettings); @@ -1181,6 +1237,15 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int if (p_desired.layered_splash) { set_window_per_pixel_transparency_enabled(true); } + + // IME + im_himc = ImmGetContext(hWnd); + ImmReleaseContext(hWnd, im_himc); + + im_position = Vector2(); + + set_ime_active(false); + return OK; } @@ -1442,12 +1507,6 @@ void OS_Windows::set_current_screen(int p_screen) { set_window_position(ofs + get_screen_position(p_screen)); } -typedef struct { - int count; - int screen; - Point2 pos; -} EnumPosData; - static BOOL CALLBACK _MonitorEnumProcPos(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { EnumPosData *data = (EnumPosData *)dwData; @@ -1467,24 +1526,6 @@ Point2 OS_Windows::get_screen_position(int p_screen) const { return data.pos; } -typedef struct { - int count; - int screen; - Size2 size; -} EnumSizeData; - -static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) { - - EnumSizeData *data = (EnumSizeData *)dwData; - if (data->count == data->screen) { - data->size.x = lprcMonitor->right - lprcMonitor->left; - data->size.y = lprcMonitor->bottom - lprcMonitor->top; - } - - data->count++; - return TRUE; -} - Size2 OS_Windows::get_screen_size(int p_screen) const { EnumSizeData data = { 0, p_screen == -1 ? get_current_screen() : p_screen, Size2() }; @@ -1544,16 +1585,16 @@ Size2 OS_Windows::get_real_window_size() const { } void OS_Windows::set_window_size(const Size2 p_size) { - video_mode.width = p_size.width; - video_mode.height = p_size.height; + int w = p_size.width; + int h = p_size.height; + + video_mode.width = w; + video_mode.height = h; if (video_mode.fullscreen) { return; } - int w = p_size.width; - int h = p_size.height; - RECT rect; GetWindowRect(hWnd, &rect); @@ -2659,13 +2700,29 @@ String OS_Windows::get_unique_id() const { return String(HwProfInfo.szHwProfileGuid); } +void OS_Windows::set_ime_active(const bool p_active) { + + if (p_active) { + ImmAssociateContext(hWnd, im_himc); + + set_ime_position(im_position); + } else { + ImmAssociateContext(hWnd, (HIMC)0); + } +} + void OS_Windows::set_ime_position(const Point2 &p_pos) { + im_position = p_pos; + HIMC himc = ImmGetContext(hWnd); + if (himc == (HIMC)0) + return; + COMPOSITIONFORM cps; cps.dwStyle = CFS_FORCE_POSITION; - cps.ptCurrentPos.x = p_pos.x; - cps.ptCurrentPos.y = p_pos.y; + cps.ptCurrentPos.x = im_position.x; + cps.ptCurrentPos.y = im_position.y; ImmSetCompositionWindow(himc, &cps); ImmReleaseContext(hWnd, himc); } diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 81849497ee..19af63bae0 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -111,6 +111,10 @@ class OS_Windows : public OS { WNDPROC user_proc; + // IME + HIMC im_himc; + Vector2 im_position; + MouseMode mouse_mode; bool alt_mem; bool gr_mem; @@ -282,6 +286,7 @@ public: virtual String get_unique_id() const; + virtual void set_ime_active(const bool p_active); virtual void set_ime_position(const Point2 &p_pos); virtual void release_rendering_thread(); diff --git a/platform/x11/crash_handler_x11.cpp b/platform/x11/crash_handler_x11.cpp index d39fc33f81..960105271b 100644 --- a/platform/x11/crash_handler_x11.cpp +++ b/platform/x11/crash_handler_x11.cpp @@ -55,6 +55,10 @@ static void handle_crash(int sig) { // Dump the backtrace to stderr with a message to the user fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig); + + if (OS::get_singleton()->get_main_loop()) + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH); + fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str()); char **strings = backtrace_symbols(bt_buffer, size); if (strings) { diff --git a/platform/x11/logo.png b/platform/x11/logo.png Binary files differindex 1cc93b46ac..078654b757 100644 --- a/platform/x11/logo.png +++ b/platform/x11/logo.png diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 1fa6993306..2bc85f76c9 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -391,6 +391,9 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a wm_delete = XInternAtom(x11_display, "WM_DELETE_WINDOW", true); XSetWMProtocols(x11_display, x11_window, &wm_delete, 1); + im_active = false; + im_position = Vector2(); + if (xim && xim_style) { xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, x11_window, XNFocusWindow, x11_window, (char *)NULL); @@ -400,7 +403,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a xic = NULL; } if (xic) { - XSetICFocus(xic); + XUnsetICFocus(xic); } else { WARN_PRINT("XCreateIC couldn't create xic"); } @@ -541,8 +544,25 @@ void OS_X11::xim_destroy_callback(::XIM im, ::XPointer client_data, os->xic = NULL; } +void OS_X11::set_ime_active(const bool p_active) { + + im_active = p_active; + + if (!xic) + return; + + if (p_active) { + XSetICFocus(xic); + set_ime_position(im_position); + } else { + XUnsetICFocus(xic); + } +} + void OS_X11::set_ime_position(const Point2 &p_pos) { + im_position = p_pos; + if (!xic) return; @@ -1934,6 +1954,7 @@ void OS_X11::process_xevents() { // to be able to send relative motion events. Point2i pos(event.xmotion.x, event.xmotion.y); +#ifdef TOUCH_ENABLED // Avoidance of spurious mouse motion (see handling of touch) bool filter = false; // Adding some tolerance to match better Point2i to Vector2 @@ -1945,6 +1966,7 @@ void OS_X11::process_xevents() { if (filter) { break; } +#endif if (mouse_mode == MOUSE_MODE_CAPTURED) { @@ -2507,17 +2529,23 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c void OS_X11::release_rendering_thread() { +#if defined(OPENGL_ENABLED) context_gl->release_current(); +#endif } void OS_X11::make_rendering_thread() { +#if defined(OPENGL_ENABLED) context_gl->make_current(); +#endif } void OS_X11::swap_buffers() { +#if defined(OPENGL_ENABLED) context_gl->swap_buffers(); +#endif } void OS_X11::alert(const String &p_alert, const String &p_title) { @@ -2611,8 +2639,10 @@ String OS_X11::get_joy_guid(int p_device) const { } void OS_X11::_set_use_vsync(bool p_enable) { +#if defined(OPENGL_ENABLED) if (context_gl) - return context_gl->set_use_vsync(p_enable); + context_gl->set_use_vsync(p_enable); +#endif } /* bool OS_X11::is_vsync_enabled() const { diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index 09ed9588c4..8cab23fe63 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -116,6 +116,10 @@ class OS_X11 : public OS_Unix { static void xim_destroy_callback(::XIM im, ::XPointer client_data, ::XPointer call_data); + // IME + bool im_active; + Vector2 im_position; + Point2i last_mouse_pos; bool last_mouse_pos_valid; Point2i last_click_pos; @@ -269,6 +273,7 @@ public: virtual bool get_window_per_pixel_transparency_enabled() const; virtual void set_window_per_pixel_transparency_enabled(bool p_enabled); + virtual void set_ime_active(const bool p_active); virtual void set_ime_position(const Point2 &p_pos); virtual String get_unique_id() const; |