diff options
Diffstat (limited to 'platform')
26 files changed, 536 insertions, 194 deletions
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index b987b3aebd..30267aa968 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -790,7 +790,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { if (tname == "manifest" && attrname == "versionName") { if (attr_value == 0xFFFFFFFF) { - WARN_PRINT("Version name in a resource, should be plaintext") + WARN_PRINT("Version name in a resource, should be plain text"); } else string_table.write[attr_value] = version_name; } diff --git a/platform/android/java/gradlew.bat b/platform/android/java/gradlew.bat index e95643d6a2..f9553162f1 100644 --- a/platform/android/java/gradlew.bat +++ b/platform/android/java/gradlew.bat @@ -1,84 +1,84 @@ -@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/platform/android/java/src/org/godotengine/godot/Godot.java b/platform/android/java/src/org/godotengine/godot/Godot.java index 374d40463a..0eeaf0701c 100644 --- a/platform/android/java/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/src/org/godotengine/godot/Godot.java @@ -1059,4 +1059,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC mProgressFraction.setText(Helpers.getDownloadProgressString(progress.mOverallProgress, progress.mOverallTotal)); } + public void initInputDevices() { + mView.initInputDevices(); + } } diff --git a/platform/android/java/src/org/godotengine/godot/GodotView.java b/platform/android/java/src/org/godotengine/godot/GodotView.java index d7cd5b4360..ab28d9ec33 100644 --- a/platform/android/java/src/org/godotengine/godot/GodotView.java +++ b/platform/android/java/src/org/godotengine/godot/GodotView.java @@ -111,6 +111,18 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { init(translucent, depth, stencil); } + public void initInputDevices() { + /* initially add input devices*/ + int[] deviceIds = mInputManager.getInputDeviceIds(); + for (int deviceId : deviceIds) { + InputDevice device = mInputManager.getInputDevice(deviceId); + if (DEBUG) { + Log.v("GodotView", String.format("init() deviceId:%d, Name:%s\n", deviceId, device.getName())); + } + onInputDeviceAdded(deviceId); + } + } + @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouchEvent(MotionEvent event) { @@ -217,36 +229,42 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { // Check if the device has not been already added if (id < 0) { InputDevice device = mInputManager.getInputDevice(deviceId); + //device can be null if deviceId is not found + if (device != null) { + int sources = device.getSources(); + if (((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) || + ((sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK)) { + id = joy_devices.size(); + + joystick joy = new joystick(); + joy.device_id = deviceId; + joy.name = device.getName(); + joy.axes = new ArrayList<InputDevice.MotionRange>(); + joy.hats = new ArrayList<InputDevice.MotionRange>(); + + List<InputDevice.MotionRange> ranges = device.getMotionRanges(); + Collections.sort(ranges, new RangeComparator()); + + for (InputDevice.MotionRange range : ranges) { + if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { + joy.hats.add(range); + } else { + joy.axes.add(range); + } + } - id = joy_devices.size(); - - joystick joy = new joystick(); - joy.device_id = deviceId; - joy.name = device.getName(); - joy.axes = new ArrayList<InputDevice.MotionRange>(); - joy.hats = new ArrayList<InputDevice.MotionRange>(); - - List<InputDevice.MotionRange> ranges = device.getMotionRanges(); - Collections.sort(ranges, new RangeComparator()); + joy_devices.add(joy); - for (InputDevice.MotionRange range : ranges) { - if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) { - joy.hats.add(range); - } else { - joy.axes.add(range); + final int device_id = id; + final String name = joy.name; + queueEvent(new Runnable() { + @Override + public void run() { + GodotLib.joyconnectionchanged(device_id, true, name); + } + }); } } - - joy_devices.add(joy); - - final int device_id = id; - final String name = joy.name; - queueEvent(new Runnable() { - @Override - public void run() { - GodotLib.joyconnectionchanged(device_id, true, name); - } - }); } } @@ -269,6 +287,8 @@ public class GodotView extends GLSurfaceView implements InputDeviceListener { @Override public void onInputDeviceChanged(int deviceId) { + onInputDeviceRemoved(deviceId); + onInputDeviceAdded(deviceId); } @Override public boolean onKeyUp(final int keyCode, KeyEvent event) { diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 101a1d76c6..e92d4437b1 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -59,6 +59,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) { _get_clipboard = p_env->GetMethodID(cls, "getClipboard", "()Ljava/lang/String;"); _set_clipboard = p_env->GetMethodID(cls, "setClipboard", "(Ljava/lang/String;)V"); _request_permission = p_env->GetMethodID(cls, "requestPermission", "(Ljava/lang/String;)Z"); + _init_input_devices = p_env->GetMethodID(cls, "initInputDevices", "()V"); } GodotJavaWrapper::~GodotJavaWrapper() { @@ -183,3 +184,10 @@ bool GodotJavaWrapper::request_permission(const String &p_name) { return false; } } + +void GodotJavaWrapper::init_input_devices() { + if (_init_input_devices) { + JNIEnv *env = ThreadAndroid::get_env(); + env->CallVoidMethod(godot_instance, _init_input_devices); + } +} diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index 438aee019b..be4f109d8c 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -54,6 +54,7 @@ private: jmethodID _get_clipboard = 0; jmethodID _set_clipboard = 0; jmethodID _request_permission = 0; + jmethodID _init_input_devices = 0; public: GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance); @@ -76,6 +77,7 @@ public: bool has_set_clipboard(); void set_clipboard(const String &p_text); bool request_permission(const String &p_name); + void init_input_devices(); }; #endif /* !JAVA_GODOT_WRAPPER_H */ diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index ff1632cba8..f8076dfd2d 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -251,6 +251,10 @@ int OS_Android::get_mouse_button_state() const { } void OS_Android::set_window_title(const String &p_title) { + //This queries/updates the currently connected devices/joypads + //Set_window_title is called when initializing the main loop (main.cpp) + //therefore this place is found to be suitable (I found no better). + godot_java->init_input_devices(); } void OS_Android::set_video_mode(const VideoMode &p_video_mode, int p_screen) { diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index ba405ab7ae..7ca83320d0 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -914,7 +914,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p }; DirAccess *tmp_app_path = DirAccess::create_for_path(dest_dir); - ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE) + ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE); print_line("Unzipping..."); FileAccess *src_f = NULL; diff --git a/platform/iphone/icloud.mm b/platform/iphone/icloud.mm index e32618e8f6..c60db3d661 100644 --- a/platform/iphone/icloud.mm +++ b/platform/iphone/icloud.mm @@ -138,7 +138,7 @@ Variant nsobject_to_variant(NSObject *object) { //this is a type that icloud supports...but how did you submit it in the first place? //I guess this is a type that *might* show up, if you were, say, trying to make your game //compatible with existing cloud data written by another engine's version of your game - WARN_PRINT("NSDate unsupported, returning null Variant") + WARN_PRINT("NSDate unsupported, returning null Variant"); return Variant(); } else if ([object isKindOfClass:[NSNull class]] or object == nil) { return Variant(); diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index c69e6f0cb8..e7981bf576 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -795,6 +795,47 @@ const char *OS_JavaScript::get_audio_driver_name(int p_driver) const { return "JavaScript"; } +// Clipboard +extern "C" EMSCRIPTEN_KEEPALIVE void update_clipboard(const char *p_text) { + // Only call set_clipboard from OS (sets local clipboard) + OS::get_singleton()->OS::set_clipboard(p_text); +} + +void OS_JavaScript::set_clipboard(const String &p_text) { + OS::set_clipboard(p_text); + /* clang-format off */ + int err = EM_ASM_INT({ + var text = UTF8ToString($0); + if (!navigator.clipboard || !navigator.clipboard.writeText) + return 1; + navigator.clipboard.writeText(text).catch(e => { + // Setting OS clipboard is only possible from an input callback. + console.error("Setting OS clipboard is only possible from an input callback for the HTML5 plafrom. Exception:", e); + }); + return 0; + }, p_text.utf8().get_data()); + /* clang-format on */ + ERR_EXPLAIN("Clipboard API is not supported."); + ERR_FAIL_COND(err); +} + +String OS_JavaScript::get_clipboard() const { + /* clang-format off */ + EM_ASM({ + try { + navigator.clipboard.readText().then(function (result) { + ccall('update_clipboard', 'void', ['string'], [result]); + }).catch(function (e) { + // Fail graciously. + }); + } catch (e) { + // Fail graciously. + } + }); + /* clang-format on */ + return this->OS::get_clipboard(); +} + // Lifecycle int OS_JavaScript::get_current_video_driver() const { return video_driver_index; @@ -939,6 +980,11 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, index) { Module.canvas.addEventListener(event, send_notification.bind(null, notifications[index])); }); + // Clipboard + const update_clipboard = cwrap('update_clipboard', null, ['string']); + window.addEventListener('paste', function(evt) { + update_clipboard(evt.clipboardData.getData('text')); + }, true); }, MainLoop::NOTIFICATION_WM_MOUSE_ENTER, MainLoop::NOTIFICATION_WM_MOUSE_EXIT, @@ -1098,7 +1144,7 @@ void OS_JavaScript::set_icon(const Ref<Image> &p_icon) { Ref<Image> icon = p_icon; if (icon->is_compressed()) { icon = icon->duplicate(); - ERR_FAIL_COND(icon->decompress() != OK) + ERR_FAIL_COND(icon->decompress() != OK); } if (icon->get_format() != Image::FORMAT_RGBA8) { if (icon == p_icon) diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index a0c7c31f2d..27b23a4673 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -133,6 +133,9 @@ public: virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; + virtual void set_clipboard(const String &p_text); + virtual String get_clipboard() const; + virtual MainLoop *get_main_loop() const; void run_async(); bool main_loop_iterate(); diff --git a/platform/osx/crash_handler_osx.mm b/platform/osx/crash_handler_osx.mm index ed8a955ae5..e19fdf1b9f 100644 --- a/platform/osx/crash_handler_osx.mm +++ b/platform/osx/crash_handler_osx.mm @@ -77,7 +77,12 @@ static void handle_crash(int sig) { void *bt_buffer[256]; size_t size = backtrace(bt_buffer, 256); String _execpath = OS::get_singleton()->get_executable_path(); - String msg = GLOBAL_GET("debug/settings/crash_handler/message"); + + String msg; + const ProjectSettings *proj_settings = ProjectSettings::get_singleton(); + if (proj_settings) { + msg = proj_settings->get("debug/settings/crash_handler/message"); + } // Dump the backtrace to stderr with a message to the user fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig); diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index d2a6f38b01..eed230ba89 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -60,6 +60,7 @@ public: unsigned int osx_state; bool pressed; bool echo; + bool raw; uint32_t scancode; uint32_t unicode; }; @@ -186,6 +187,7 @@ public: virtual Size2 get_window_size() const; virtual Size2 get_real_window_size() const; + virtual void set_native_icon(const String &p_filename); virtual void set_icon(const Ref<Image> &p_icon); virtual MainLoop *get_main_loop() const; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index fc97bd4a20..bdc17c7124 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -392,7 +392,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt @interface GodotContentView : NSView <NSTextInputClient> { NSTrackingArea *trackingArea; NSMutableAttributedString *markedText; - bool imeMode; + bool imeInputEventInProgress; } - (void)cancelComposition; - (BOOL)wantsUpdateLayer; @@ -418,7 +418,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt - (id)init { self = [super init]; trackingArea = nil; - imeMode = false; + imeInputEventInProgress = false; [self updateTrackingAreas]; [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]]; markedText = [[NSMutableAttributedString alloc] init]; @@ -452,7 +452,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; [markedText initWithString:aString]; } if (OS_OSX::singleton->im_active) { - imeMode = true; + imeInputEventInProgress = true; OS_OSX::singleton->im_text.parse_utf8([[markedText mutableString] UTF8String]); OS_OSX::singleton->im_selection = Point2(selectedRange.location, selectedRange.length); @@ -467,7 +467,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; } - (void)unmarkText { - imeMode = false; + imeInputEventInProgress = false; [[markedText mutableString] setString:@""]; if (OS_OSX::singleton->im_active) { OS_OSX::singleton->im_text = String(); @@ -540,6 +540,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; ke.osx_state = [event modifierFlags]; ke.pressed = true; ke.echo = false; + ke.raw = false; // IME input event ke.scancode = 0; ke.unicode = codepoint; @@ -1001,7 +1002,7 @@ static const _KeyCodeMap _keycodes[55] = { { '/', KEY_SLASH } }; -static int remapKey(unsigned int key) { +static int remapKey(unsigned int key, unsigned int state) { if (isNumpadKey(key)) return translateKey(key); @@ -1023,7 +1024,7 @@ static int remapKey(unsigned int key) { OSStatus err = UCKeyTranslate(keyboardLayout, key, kUCKeyActionDisplay, - 0, + (state >> 8) & 0xFF, LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &keysDown, @@ -1045,29 +1046,52 @@ static int remapKey(unsigned int key) { - (void)keyDown:(NSEvent *)event { - //disable raw input in IME mode - if (!imeMode) { - OS_OSX::KeyEvent ke; + // Ignore all input if IME input is in progress + if (!imeInputEventInProgress) { + NSString *characters = [event characters]; + NSUInteger length = [characters length]; - ke.osx_state = [event modifierFlags]; - ke.pressed = true; - ke.echo = [event isARepeat]; - ke.scancode = remapKey([event keyCode]); - ke.unicode = 0; + if (!OS_OSX::singleton->im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode], [event modifierFlags]))) { + // Fallback unicode character handler used if IME is not active + for (NSUInteger i = 0; i < length; i++) { + OS_OSX::KeyEvent ke; - push_to_key_event_buffer(ke); + ke.osx_state = [event modifierFlags]; + ke.pressed = true; + ke.echo = [event isARepeat]; + ke.scancode = remapKey([event keyCode], [event modifierFlags]); + ke.raw = true; + ke.unicode = [characters characterAtIndex:i]; + + push_to_key_event_buffer(ke); + } + } else { + OS_OSX::KeyEvent ke; + + ke.osx_state = [event modifierFlags]; + ke.pressed = true; + ke.echo = [event isARepeat]; + ke.scancode = remapKey([event keyCode], [event modifierFlags]); + ke.raw = false; + ke.unicode = 0; + + push_to_key_event_buffer(ke); + } } - if (OS_OSX::singleton->im_active == true) + // Pass events to IME handler + if (OS_OSX::singleton->im_active) [self interpretKeyEvents:[NSArray arrayWithObject:event]]; } - (void)flagsChanged:(NSEvent *)event { - if (!imeMode) { + // Ignore all input if IME input is in progress + if (!imeInputEventInProgress) { OS_OSX::KeyEvent ke; ke.echo = false; + ke.raw = true; int key = [event keyCode]; int mod = [event modifierFlags]; @@ -1105,7 +1129,7 @@ static int remapKey(unsigned int key) { } ke.osx_state = mod; - ke.scancode = remapKey(key); + ke.scancode = remapKey(key, mod); ke.unicode = 0; push_to_key_event_buffer(ke); @@ -1114,17 +1138,37 @@ static int remapKey(unsigned int key) { - (void)keyUp:(NSEvent *)event { - if (!imeMode) { + // Ignore all input if IME input is in progress + if (!imeInputEventInProgress) { + NSString *characters = [event characters]; + NSUInteger length = [characters length]; - OS_OSX::KeyEvent ke; + // Fallback unicode character handler used if IME is not active + if (!OS_OSX::singleton->im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode], [event modifierFlags]))) { + for (NSUInteger i = 0; i < length; i++) { + OS_OSX::KeyEvent ke; - ke.osx_state = [event modifierFlags]; - ke.pressed = false; - ke.echo = false; - ke.scancode = remapKey([event keyCode]); - ke.unicode = 0; + ke.osx_state = [event modifierFlags]; + ke.pressed = false; + ke.echo = [event isARepeat]; + ke.scancode = remapKey([event keyCode], [event modifierFlags]); + ke.raw = true; + ke.unicode = [characters characterAtIndex:i]; - push_to_key_event_buffer(ke); + push_to_key_event_buffer(ke); + } + } else { + OS_OSX::KeyEvent ke; + + ke.osx_state = [event modifierFlags]; + ke.pressed = false; + ke.echo = [event isARepeat]; + ke.scancode = remapKey([event keyCode], [event modifierFlags]); + ke.raw = true; + ke.unicode = 0; + + push_to_key_event_buffer(ke); + } } } @@ -1507,7 +1551,7 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a restore_rect = Rect2(get_window_position(), get_window_size()); - if (p_desired.layered_splash) { + if (p_desired.layered) { set_window_per_pixel_transparency_enabled(true); } return OK; @@ -1858,6 +1902,31 @@ void OS_OSX::set_window_title(const String &p_title) { [window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]]; } +void OS_OSX::set_native_icon(const String &p_filename) { + + FileAccess *f = FileAccess::open(p_filename, FileAccess::READ); + ERR_FAIL_COND(!f); + + Vector<uint8_t> data; + uint32_t len = f->get_len(); + data.resize(len); + f->get_buffer((uint8_t *)&data.write[0], len); + memdelete(f); + + NSData *icon_data = [[[NSData alloc] initWithBytes:&data.write[0] length:len] autorelease]; + if (!icon_data) { + ERR_EXPLAIN("Error reading icon data"); + ERR_FAIL(); + } + NSImage *icon = [[[NSImage alloc] initWithData:icon_data] autorelease]; + if (!icon) { + ERR_EXPLAIN("Error loading icon"); + ERR_FAIL(); + } + + [NSApp setApplicationIconImage:icon]; +} + void OS_OSX::set_icon(const Ref<Image> &p_icon) { Ref<Image> img = p_icon; @@ -2587,30 +2656,44 @@ void OS_OSX::process_key_events() { const KeyEvent &ke = key_event_buffer[i]; - if ((i == 0 && ke.scancode == 0) || (i > 0 && key_event_buffer[i - 1].scancode == 0)) { + if (ke.raw) { + // Non IME input - no composite characters, pass events as is k.instance(); get_key_modifier_state(ke.osx_state, k); k->set_pressed(ke.pressed); k->set_echo(ke.echo); - k->set_scancode(0); + k->set_scancode(ke.scancode); k->set_unicode(ke.unicode); push_input(k); - } - if (ke.scancode != 0) { - k.instance(); + } else { + // IME input + if ((i == 0 && ke.scancode == 0) || (i > 0 && key_event_buffer[i - 1].scancode == 0)) { + k.instance(); - get_key_modifier_state(ke.osx_state, k); - k->set_pressed(ke.pressed); - k->set_echo(ke.echo); - k->set_scancode(ke.scancode); + get_key_modifier_state(ke.osx_state, k); + k->set_pressed(ke.pressed); + k->set_echo(ke.echo); + k->set_scancode(0); + k->set_unicode(ke.unicode); - if (i + 1 < key_event_pos && key_event_buffer[i + 1].scancode == 0) { - k->set_unicode(key_event_buffer[i + 1].unicode); + push_input(k); } + if (ke.scancode != 0) { + k.instance(); - push_input(k); + get_key_modifier_state(ke.osx_state, k); + k->set_pressed(ke.pressed); + k->set_echo(ke.echo); + k->set_scancode(ke.scancode); + + if (i + 1 < key_event_pos && key_event_buffer[i + 1].scancode == 0) { + k->set_unicode(key_event_buffer[i + 1].unicode); + } + + push_input(k); + } } } diff --git a/platform/server/detect.py b/platform/server/detect.py index 08c2eb6aaf..a5648d8d9d 100644 --- a/platform/server/detect.py +++ b/platform/server/detect.py @@ -145,12 +145,12 @@ def configure(env): env.ParseConfig('pkg-config libpng --cflags --libs') if not env['builtin_bullet']: - # We need at least version 2.88 + # We need at least version 2.89 import subprocess bullet_version = subprocess.check_output(['pkg-config', 'bullet', '--modversion']).strip() - if str(bullet_version) < "2.88": + if str(bullet_version) < "2.89": # Abort as system bullet was requested but too old - print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.88")) + print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.89")) sys.exit(255) env.ParseConfig('pkg-config bullet --cflags --libs') diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index cdcad33f6d..ec43a4c26f 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -241,7 +241,6 @@ void AppxPackager::make_block_map() { tmp_file->close(); memdelete(tmp_file); - tmp_file = NULL; } String AppxPackager::content_type(String p_extension) { @@ -291,7 +290,6 @@ void AppxPackager::make_content_types() { tmp_file->close(); memdelete(tmp_file); - tmp_file = NULL; } Vector<uint8_t> AppxPackager::make_file_header(FileMeta p_file_meta) { @@ -606,7 +604,6 @@ void AppxPackager::finish() { blockmap_file->close(); memdelete(blockmap_file); - blockmap_file = NULL; // Add content types EditorNode::progress_task_step("export", "Setting content types...", 5); @@ -622,7 +619,6 @@ void AppxPackager::finish() { types_file->close(); memdelete(types_file); - types_file = NULL; // Pre-process central directory before signing for (int i = 0; i < file_metadata.size(); i++) { diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp index 4006c4c60e..0716ee67f4 100644 --- a/platform/windows/crash_handler_windows.cpp +++ b/platform/windows/crash_handler_windows.cpp @@ -166,11 +166,16 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { line.SizeOfStruct = sizeof(line); IMAGE_NT_HEADERS *h = ImageNtHeader(base); DWORD image_type = h->FileHeader.Machine; - int n = 0; - String msg = GLOBAL_GET("debug/settings/crash_handler/message"); + + String msg; + const ProjectSettings *proj_settings = ProjectSettings::get_singleton(); + if (proj_settings) { + msg = proj_settings->get("debug/settings/crash_handler/message"); + } fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str()); + int n = 0; do { if (skip_first) { skip_first = false; diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 141ab96370..4a72d07adc 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -138,8 +138,8 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio EditorExportPlatformPC::get_export_options(r_options); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), "")); diff --git a/platform/windows/godot_res.rc b/platform/windows/godot_res.rc index f2dca10d55..1fa8957f15 100644 --- a/platform/windows/godot_res.rc +++ b/platform/windows/godot_res.rc @@ -21,7 +21,7 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Godot Engine" - VALUE "FileDescription", VERSION_NAME " Editor" + VALUE "FileDescription", VERSION_NAME VALUE "FileVersion", VERSION_NUMBER VALUE "ProductName", VERSION_NAME VALUE "Licence", "MIT" diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 4c6e4e96b5..4cd637a4b2 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1381,7 +1381,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int SetFocus(hWnd); // Sets Keyboard Focus To } - if (p_desired.layered_splash) { + if (p_desired.layered) { set_window_per_pixel_transparency_enabled(true); } @@ -2475,7 +2475,13 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, for (const List<String>::Element *E = p_arguments.front(); E; E = E->next()) { - argss += String(" \"") + E->get() + "\""; + argss += " \"" + E->get() + "\""; + } + + argss += "\""; + + if (read_stderr) { + argss += " 2>&1"; // Read stderr too } FILE *f = _wpopen(argss.c_str(), L"r"); @@ -2577,6 +2583,117 @@ String OS_Windows::get_executable_path() const { return s; } +void OS_Windows::set_native_icon(const String &p_filename) { + + FileAccess *f = FileAccess::open(p_filename, FileAccess::READ); + ERR_FAIL_COND(!f); + + ICONDIR *icon_dir = (ICONDIR *)memalloc(sizeof(ICONDIR)); + int pos = 0; + + icon_dir->idReserved = f->get_32(); + pos += sizeof(WORD); + f->seek(pos); + + icon_dir->idType = f->get_32(); + pos += sizeof(WORD); + f->seek(pos); + + if (icon_dir->idType != 1) { + ERR_EXPLAIN("Invalid icon file format!"); + ERR_FAIL(); + } + + icon_dir->idCount = f->get_32(); + pos += sizeof(WORD); + f->seek(pos); + + icon_dir = (ICONDIR *)memrealloc(icon_dir, 3 * sizeof(WORD) + icon_dir->idCount * sizeof(ICONDIRENTRY)); + f->get_buffer((uint8_t *)&icon_dir->idEntries[0], icon_dir->idCount * sizeof(ICONDIRENTRY)); + + int small_icon_index = -1; // Select 16x16 with largest color count + int small_icon_cc = 0; + int big_icon_index = -1; // Select largest + int big_icon_width = 16; + int big_icon_cc = 0; + + for (int i = 0; i < icon_dir->idCount; i++) { + int colors = (icon_dir->idEntries[i].bColorCount == 0) ? 32768 : icon_dir->idEntries[i].bColorCount; + int width = (icon_dir->idEntries[i].bWidth == 0) ? 256 : icon_dir->idEntries[i].bWidth; + if (width == 16) { + if (colors >= small_icon_cc) { + small_icon_index = i; + small_icon_cc = colors; + } + } + if (width >= big_icon_width) { + if (colors >= big_icon_cc) { + big_icon_index = i; + big_icon_width = width; + big_icon_cc = colors; + } + } + } + + if (big_icon_index == -1) { + ERR_EXPLAIN("No valid icons found!"); + ERR_FAIL(); + } + + if (small_icon_index == -1) { + WARN_PRINTS("No small icon found, reusing " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon!"); + small_icon_index = big_icon_index; + small_icon_cc = big_icon_cc; + } + + // Read the big icon + DWORD bytecount_big = icon_dir->idEntries[big_icon_index].dwBytesInRes; + Vector<uint8_t> data_big; + data_big.resize(bytecount_big); + pos = icon_dir->idEntries[big_icon_index].dwImageOffset; + f->seek(pos); + f->get_buffer((uint8_t *)&data_big.write[0], bytecount_big); + HICON icon_big = CreateIconFromResource((PBYTE)&data_big.write[0], bytecount_big, TRUE, 0x00030000); + if (!icon_big) { + ERR_EXPLAIN("Could not create " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon, error: " + format_error_message(GetLastError())); + ERR_FAIL(); + } + + // Read the small icon + DWORD bytecount_small = icon_dir->idEntries[small_icon_index].dwBytesInRes; + Vector<uint8_t> data_small; + data_small.resize(bytecount_small); + pos = icon_dir->idEntries[small_icon_index].dwImageOffset; + f->seek(pos); + f->get_buffer((uint8_t *)&data_small.write[0], bytecount_small); + HICON icon_small = CreateIconFromResource((PBYTE)&data_small.write[0], bytecount_small, TRUE, 0x00030000); + if (!icon_small) { + ERR_EXPLAIN("Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError())); + ERR_FAIL(); + } + + // Online tradition says to be sure last error is cleared and set the small icon first + int err = 0; + SetLastError(err); + + SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_small); + err = GetLastError(); + if (err) { + ERR_EXPLAIN("Error setting ICON_SMALL: " + format_error_message(err)); + ERR_FAIL(); + } + + SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big); + err = GetLastError(); + if (err) { + ERR_EXPLAIN("Error setting ICON_BIG: " + format_error_message(err)); + ERR_FAIL(); + } + + memdelete(f); + memdelete(icon_dir); +} + void OS_Windows::set_icon(const Ref<Image> &p_icon) { ERR_FAIL_COND(!p_icon.is_valid()); diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 59aeb01b51..0aacbcb9ff 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -58,6 +58,25 @@ /** @author Juan Linietsky <reduzio@gmail.com> */ + +typedef struct { + BYTE bWidth; // Width, in pixels, of the image + BYTE bHeight; // Height, in pixels, of the image + BYTE bColorCount; // Number of colors in image (0 if >=8bpp) + BYTE bReserved; // Reserved ( must be 0) + WORD wPlanes; // Color Planes + WORD wBitCount; // Bits per pixel + DWORD dwBytesInRes; // How many bytes in this resource? + DWORD dwImageOffset; // Where in the file is this image? +} ICONDIRENTRY, *LPICONDIRENTRY; + +typedef struct { + WORD idReserved; // Reserved (must be 0) + WORD idType; // Resource Type (1 for icons) + WORD idCount; // How many images? + ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em) +} ICONDIR, *LPICONDIR; + class JoypadWindows; class OS_Windows : public OS { @@ -276,6 +295,8 @@ public: CursorShape get_cursor_shape() const; virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap); + + void set_native_icon(const String &p_filename); void set_icon(const Ref<Image> &p_icon); virtual String get_executable_path() const; diff --git a/platform/x11/context_gl_x11.cpp b/platform/x11/context_gl_x11.cpp index aadf7ee36d..9718b03164 100644 --- a/platform/x11/context_gl_x11.cpp +++ b/platform/x11/context_gl_x11.cpp @@ -191,6 +191,7 @@ Error ContextGL_X11::initialize() { swa.colormap = XCreateColormap(x11_display, RootWindow(x11_display, vi->screen), vi->visual, AllocNone); x11_window = XCreateWindow(x11_display, RootWindow(x11_display, vi->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, vi->depth, InputOutput, vi->visual, valuemask, &swa); + XStoreName(x11_display, x11_window, "Godot Engine"); ERR_FAIL_COND_V(!x11_window, ERR_UNCONFIGURED); set_class_hint(x11_display, x11_window); diff --git a/platform/x11/crash_handler_x11.cpp b/platform/x11/crash_handler_x11.cpp index 44d3cf1910..ca7251078f 100644 --- a/platform/x11/crash_handler_x11.cpp +++ b/platform/x11/crash_handler_x11.cpp @@ -53,7 +53,12 @@ static void handle_crash(int sig) { void *bt_buffer[256]; size_t size = backtrace(bt_buffer, 256); String _execpath = OS::get_singleton()->get_executable_path(); - String msg = GLOBAL_GET("debug/settings/crash_handler/message"); + + String msg; + const ProjectSettings *proj_settings = ProjectSettings::get_singleton(); + if (proj_settings) { + msg = proj_settings->get("debug/settings/crash_handler/message"); + } // Dump the backtrace to stderr with a message to the user fprintf(stderr, "%s: Program crashed with signal %d\n", __FUNCTION__, sig); diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 933ee6b72e..a502308eee 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -219,12 +219,12 @@ def configure(env): env.ParseConfig('pkg-config libpng --cflags --libs') if not env['builtin_bullet']: - # We need at least version 2.88 + # We need at least version 2.89 import subprocess bullet_version = subprocess.check_output(['pkg-config', 'bullet', '--modversion']).strip() - if str(bullet_version) < "2.88": + if str(bullet_version) < "2.89": # Abort as system bullet was requested but too old - print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.88")) + print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.89")) sys.exit(255) env.ParseConfig('pkg-config bullet --cflags --libs') diff --git a/platform/x11/joypad_linux.cpp b/platform/x11/joypad_linux.cpp index c4dd8fe0e0..3e9e8033e8 100644 --- a/platform/x11/joypad_linux.cpp +++ b/platform/x11/joypad_linux.cpp @@ -444,10 +444,10 @@ InputDefault::JoyAxis JoypadLinux::axis_correct(const input_absinfo *p_abs, int jx.min = -1; if (p_value < 0) { jx.value = (float)-p_value / min; + } else { + jx.value = (float)p_value / max; } - jx.value = (float)p_value / max; - } - if (min == 0) { + } else if (min == 0) { jx.min = 0; jx.value = 0.0f + (float)p_value / max; } diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 56389578f7..510f3d6114 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -534,22 +534,26 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a } { - Pixmap cursormask; - XGCValues xgc; - GC gc; - XColor col; - Cursor cursor; + // Creating an empty/transparent cursor + + // Create 1x1 bitmap + Pixmap cursormask = XCreatePixmap(x11_display, + RootWindow(x11_display, DefaultScreen(x11_display)), 1, 1, 1); - cursormask = XCreatePixmap(x11_display, RootWindow(x11_display, DefaultScreen(x11_display)), 1, 1, 1); + // Fill with zero + XGCValues xgc; xgc.function = GXclear; - gc = XCreateGC(x11_display, cursormask, GCFunction, &xgc); + GC gc = XCreateGC(x11_display, cursormask, GCFunction, &xgc); XFillRectangle(x11_display, cursormask, gc, 0, 0, 1, 1); - col.pixel = 0; - col.red = 0; - col.flags = 4; - cursor = XCreatePixmapCursor(x11_display, - cursormask, cursormask, + + // Color value doesn't matter. Mask zero means no foreground or background will be drawn + XColor col = {}; + + Cursor cursor = XCreatePixmapCursor(x11_display, + cursormask, // source (using cursor mask as placeholder, since it'll all be ignored) + cursormask, // mask &col, &col, 0, 0); + XFreePixmap(x11_display, cursormask); XFreeGC(x11_display, gc); @@ -589,7 +593,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a power_manager = memnew(PowerX11); - if (p_desired.layered_splash) { + if (p_desired.layered) { set_window_per_pixel_transparency_enabled(true); } @@ -1013,12 +1017,12 @@ void OS_X11::set_wm_fullscreen(bool p_enabled) { XFree(xsh); } - if (!p_enabled && !get_borderless_window()) { - // put decorations back if the window wasn't suppoesed to be borderless + if (!p_enabled) { + // put back or remove decorations according to the last set borderless state Hints hints; Atom property; hints.flags = 2; - hints.decorations = 1; + hints.decorations = current_videomode.borderless_window ? 0 : 1; property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); } @@ -1527,7 +1531,7 @@ bool OS_X11::is_window_always_on_top() const { void OS_X11::set_borderless_window(bool p_borderless) { - if (current_videomode.borderless_window == p_borderless) + if (get_borderless_window() == p_borderless) return; if (!p_borderless && layered_window) @@ -1547,7 +1551,24 @@ void OS_X11::set_borderless_window(bool p_borderless) { } bool OS_X11::get_borderless_window() { - return current_videomode.borderless_window; + + bool borderless = current_videomode.borderless_window; + Atom prop = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); + if (prop != None) { + + Atom type; + int format; + unsigned long len; + unsigned long remaining; + unsigned char *data = NULL; + if (XGetWindowProperty(x11_display, x11_window, prop, 0, sizeof(Hints), False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) { + if (data && (format == 32) && (len >= 5)) { + borderless = !((Hints *)data)->decorations; + } + XFree(data); + } + } + return borderless; } void OS_X11::request_attention() { @@ -2374,7 +2395,7 @@ void OS_X11::process_xevents() { Vector<String> files = String((char *)p.data).split("\n", false); for (int i = 0; i < files.size(); i++) { - files.write[i] = files[i].replace("file://", "").http_unescape().strip_escapes(); + files.write[i] = files[i].replace("file://", "").http_unescape().strip_edges(); } main_loop->drop_files(files); |