summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
Diffstat (limited to 'platform')
-rw-r--r--platform/javascript/audio_driver_javascript.cpp20
-rw-r--r--platform/javascript/audio_driver_javascript.h7
-rw-r--r--platform/javascript/display_server_javascript.cpp101
-rw-r--r--platform/javascript/display_server_javascript.h20
-rw-r--r--platform/javascript/javascript_main.cpp52
-rw-r--r--platform/javascript/os_javascript.cpp25
-rw-r--r--platform/javascript/os_javascript.h5
-rw-r--r--platform/linuxbsd/display_server_x11.cpp48
-rw-r--r--platform/linuxbsd/display_server_x11.h10
-rw-r--r--platform/osx/detect.py17
-rw-r--r--platform/osx/display_server_osx.h10
-rw-r--r--platform/osx/display_server_osx.mm72
12 files changed, 279 insertions, 108 deletions
diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp
index b8914414e6..9604914b2c 100644
--- a/platform/javascript/audio_driver_javascript.cpp
+++ b/platform/javascript/audio_driver_javascript.cpp
@@ -36,6 +36,15 @@
AudioDriverJavaScript *AudioDriverJavaScript::singleton = nullptr;
+bool AudioDriverJavaScript::is_available() {
+ return EM_ASM_INT({
+ if (!(window.AudioContext || window.webkitAudioContext)) {
+ return 0;
+ }
+ return 1;
+ }) != 0;
+}
+
const char *AudioDriverJavaScript::get_name() const {
return "JavaScript";
}
@@ -207,12 +216,14 @@ void AudioDriverJavaScript::finish_async() {
/* clang-format off */
EM_ASM({
- var ref = Module.IDHandler.get($0);
+ const id = $0;
+ var ref = Module.IDHandler.get(id);
Module.async_finish.push(new Promise(function(accept, reject) {
if (!ref) {
- console.log("Ref not found!", $0, Module.IDHandler);
+ console.log("Ref not found!", id, Module.IDHandler);
setTimeout(accept, 0);
} else {
+ Module.IDHandler.remove(id);
const context = ref['context'];
// Disconnect script and input.
ref['script'].disconnect();
@@ -226,7 +237,6 @@ void AudioDriverJavaScript::finish_async() {
});
}
}));
- Module.IDHandler.remove($0);
}, id);
/* clang-format on */
}
@@ -293,9 +303,5 @@ Error AudioDriverJavaScript::capture_stop() {
}
AudioDriverJavaScript::AudioDriverJavaScript() {
- _driver_id = 0;
- internal_buffer = nullptr;
- buffer_length = 0;
-
singleton = this;
}
diff --git a/platform/javascript/audio_driver_javascript.h b/platform/javascript/audio_driver_javascript.h
index 9b26be001e..f029a91db0 100644
--- a/platform/javascript/audio_driver_javascript.h
+++ b/platform/javascript/audio_driver_javascript.h
@@ -34,12 +34,13 @@
#include "servers/audio_server.h"
class AudioDriverJavaScript : public AudioDriver {
- float *internal_buffer;
+ float *internal_buffer = nullptr;
- int _driver_id;
- int buffer_length;
+ int _driver_id = 0;
+ int buffer_length = 0;
public:
+ static bool is_available();
void mix_to_js();
void process_capture(float sample);
diff --git a/platform/javascript/display_server_javascript.cpp b/platform/javascript/display_server_javascript.cpp
index 0312efb377..2f0a2faa83 100644
--- a/platform/javascript/display_server_javascript.cpp
+++ b/platform/javascript/display_server_javascript.cpp
@@ -44,18 +44,15 @@
#define DOM_BUTTON_XBUTTON1 3
#define DOM_BUTTON_XBUTTON2 4
+char DisplayServerJavaScript::canvas_id[256] = { 0 };
+static bool cursor_inside_canvas = true;
+
DisplayServerJavaScript *DisplayServerJavaScript::get_singleton() {
return static_cast<DisplayServerJavaScript *>(DisplayServer::get_singleton());
}
// Window (canvas)
-extern "C" EMSCRIPTEN_KEEPALIVE void _set_canvas_id(uint8_t *p_data, int p_data_size) {
- DisplayServerJavaScript *display = DisplayServerJavaScript::get_singleton();
- display->canvas_id.parse_utf8((const char *)p_data, p_data_size);
- display->canvas_id = "#" + display->canvas_id;
-}
-
-static void focus_canvas() {
+void DisplayServerJavaScript::focus_canvas() {
/* clang-format off */
EM_ASM(
Module['canvas'].focus();
@@ -63,7 +60,7 @@ static void focus_canvas() {
/* clang-format on */
}
-static bool is_canvas_focused() {
+bool DisplayServerJavaScript::is_canvas_focused() {
/* clang-format off */
return EM_ASM_INT_V(
return document.activeElement == Module['canvas'];
@@ -71,8 +68,21 @@ static bool is_canvas_focused() {
/* clang-format on */
}
-static Point2 compute_position_in_canvas(int x, int y) {
- DisplayServerJavaScript *display = DisplayServerJavaScript::get_singleton();
+bool DisplayServerJavaScript::check_size_force_redraw() {
+ int canvas_width;
+ int canvas_height;
+ emscripten_get_canvas_element_size(DisplayServerJavaScript::canvas_id, &canvas_width, &canvas_height);
+ if (last_width != canvas_width || last_height != canvas_height) {
+ last_width = canvas_width;
+ last_height = canvas_height;
+ // Update the framebuffer size and for redraw.
+ emscripten_set_canvas_element_size(DisplayServerJavaScript::canvas_id, canvas_width, canvas_height);
+ return true;
+ }
+ return false;
+}
+
+Point2 DisplayServerJavaScript::compute_position_in_canvas(int p_x, int p_y) {
int canvas_x = EM_ASM_INT({
return Module['canvas'].getBoundingClientRect().x;
});
@@ -81,23 +91,22 @@ static Point2 compute_position_in_canvas(int x, int y) {
});
int canvas_width;
int canvas_height;
- emscripten_get_canvas_element_size(display->canvas_id.utf8().get_data(), &canvas_width, &canvas_height);
+ emscripten_get_canvas_element_size(canvas_id, &canvas_width, &canvas_height);
double element_width;
double element_height;
- emscripten_get_element_css_size(display->canvas_id.utf8().get_data(), &element_width, &element_height);
+ emscripten_get_element_css_size(canvas_id, &element_width, &element_height);
- return Point2((int)(canvas_width / element_width * (x - canvas_x)),
- (int)(canvas_height / element_height * (y - canvas_y)));
+ return Point2((int)(canvas_width / element_width * (p_x - canvas_x)),
+ (int)(canvas_height / element_height * (p_y - canvas_y)));
}
-static bool cursor_inside_canvas = true;
-
EM_BOOL DisplayServerJavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) {
DisplayServerJavaScript *display = get_singleton();
// Empty ID is canvas.
String target_id = String::utf8(p_event->id);
- if (target_id.empty() || "#" + target_id == display->canvas_id) {
+ String canvas_str_id = String::utf8(canvas_id);
+ if (target_id.empty() || target_id == canvas_str_id) {
// This event property is the only reliable data on
// browser fullscreen state.
if (p_event->isFullscreen) {
@@ -131,14 +140,14 @@ extern "C" EMSCRIPTEN_KEEPALIVE void _drop_files_callback(char *p_filev[], int p
// Keys
template <typename T>
-static void dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event) {
+void DisplayServerJavaScript::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);
}
-static Ref<InputEventKey> setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) {
+Ref<InputEventKey> DisplayServerJavaScript::setup_key_event(const EmscriptenKeyboardEvent *emscripten_event) {
Ref<InputEventKey> ev;
ev.instance();
ev->set_echo(emscripten_event->repeat);
@@ -289,7 +298,7 @@ EM_BOOL DisplayServerJavaScript::mousemove_callback(int p_event_type, const Emsc
}
// Cursor
-static const char *godot2dom_cursor(DisplayServer::CursorShape p_shape) {
+const char *DisplayServerJavaScript::godot2dom_cursor(DisplayServer::CursorShape p_shape) {
switch (p_shape) {
case DisplayServer::CURSOR_ARROW:
return "auto";
@@ -330,7 +339,7 @@ static const char *godot2dom_cursor(DisplayServer::CursorShape p_shape) {
}
}
-static void set_css_cursor(const char *p_cursor) {
+void DisplayServerJavaScript::set_css_cursor(const char *p_cursor) {
/* clang-format off */
EM_ASM_({
Module['canvas'].style.cursor = UTF8ToString($0);
@@ -338,7 +347,7 @@ static void set_css_cursor(const char *p_cursor) {
/* clang-format on */
}
-static bool is_css_cursor_hidden() {
+bool DisplayServerJavaScript::is_css_cursor_hidden() const {
/* clang-format off */
return EM_ASM_INT({
return Module['canvas'].style.cursor === 'none';
@@ -820,23 +829,6 @@ DisplayServer *DisplayServerJavaScript::create_func(const String &p_rendering_dr
}
DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- /* clang-format off */
- EM_ASM({
- const canvas = Module['canvas'];
- var enc = new TextEncoder("utf-8");
- var buffer = new Uint8Array(enc.encode(canvas.id));
- var len = buffer.byteLength;
- var out = _malloc(len);
- HEAPU8.set(buffer, out);
- ccall("_set_canvas_id",
- "void",
- ["number", "number"],
- [out, len]
- );
- _free(out);
- });
- /* clang-format on */
-
RasterizerDummy::make_current(); // TODO GLES2 in Godot 4.0... or webgpu?
#if 0
EmscriptenWebGLContextAttributes attributes;
@@ -859,7 +851,7 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive
gl_initialization_error = true;
}
- EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(canvas_id.utf8().get_data(), &attributes);
+ EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(canvas_id, &attributes);
if (emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS) {
gl_initialization_error = true;
}
@@ -881,7 +873,6 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive
}
EMSCRIPTEN_RESULT result;
- CharString id = canvas_id.utf8();
#define EM_CHECK(ev) \
if (result != EMSCRIPTEN_RESULT_SUCCESS) \
ERR_PRINT("Error while setting " #ev " callback: Code " + itos(result));
@@ -895,16 +886,16 @@ DisplayServerJavaScript::DisplayServerJavaScript(const String &p_rendering_drive
// JavaScript APIs. For APIs that are not (sufficiently) exposed, EM_ASM
// is used below.
SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, mousemove, mousemove_callback)
- SET_EM_CALLBACK(id.get_data(), mousedown, mouse_button_callback)
+ SET_EM_CALLBACK(canvas_id, mousedown, mouse_button_callback)
SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_WINDOW, mouseup, mouse_button_callback)
- SET_EM_CALLBACK(id.get_data(), wheel, wheel_callback)
- SET_EM_CALLBACK(id.get_data(), touchstart, touch_press_callback)
- SET_EM_CALLBACK(id.get_data(), touchmove, touchmove_callback)
- SET_EM_CALLBACK(id.get_data(), touchend, touch_press_callback)
- SET_EM_CALLBACK(id.get_data(), touchcancel, touch_press_callback)
- SET_EM_CALLBACK(id.get_data(), keydown, keydown_callback)
- SET_EM_CALLBACK(id.get_data(), keypress, keypress_callback)
- SET_EM_CALLBACK(id.get_data(), keyup, keyup_callback)
+ SET_EM_CALLBACK(canvas_id, wheel, wheel_callback)
+ SET_EM_CALLBACK(canvas_id, touchstart, touch_press_callback)
+ SET_EM_CALLBACK(canvas_id, touchmove, touchmove_callback)
+ SET_EM_CALLBACK(canvas_id, touchend, touch_press_callback)
+ SET_EM_CALLBACK(canvas_id, touchcancel, touch_press_callback)
+ SET_EM_CALLBACK(canvas_id, keydown, keydown_callback)
+ SET_EM_CALLBACK(canvas_id, keypress, keypress_callback)
+ SET_EM_CALLBACK(canvas_id, keyup, keyup_callback)
SET_EM_CALLBACK(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, fullscreenchange, fullscreen_change_callback)
SET_EM_CALLBACK_NOTARGET(gamepadconnected, gamepad_change_callback)
SET_EM_CALLBACK_NOTARGET(gamepaddisconnected, gamepad_change_callback)
@@ -1012,7 +1003,7 @@ Size2i DisplayServerJavaScript::screen_get_size(int p_screen) const {
Rect2i DisplayServerJavaScript::screen_get_usable_rect(int p_screen) const {
int canvas[2];
- emscripten_get_canvas_element_size(canvas_id.utf8().get_data(), canvas, canvas + 1);
+ emscripten_get_canvas_element_size(canvas_id, canvas, canvas + 1);
return Rect2i(0, 0, canvas[0], canvas[1]);
}
@@ -1103,12 +1094,14 @@ Size2i DisplayServerJavaScript::window_get_min_size(WindowID p_window) const {
}
void DisplayServerJavaScript::window_set_size(const Size2i p_size, WindowID p_window) {
- emscripten_set_canvas_element_size(canvas_id.utf8().get_data(), p_size.x, p_size.y);
+ last_width = p_size.x;
+ last_height = p_size.y;
+ emscripten_set_canvas_element_size(canvas_id, p_size.x, p_size.y);
}
Size2i DisplayServerJavaScript::window_get_size(WindowID p_window) const {
int canvas[2];
- emscripten_get_canvas_element_size(canvas_id.utf8().get_data(), canvas, canvas + 1);
+ emscripten_get_canvas_element_size(canvas_id, canvas, canvas + 1);
return Size2(canvas[0], canvas[1]);
}
@@ -1134,7 +1127,7 @@ void DisplayServerJavaScript::window_set_mode(WindowMode p_mode, WindowID p_wind
strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF;
strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT;
strategy.canvasResizedCallback = nullptr;
- EMSCRIPTEN_RESULT result = emscripten_request_fullscreen_strategy(canvas_id.utf8().get_data(), false, &strategy);
+ EMSCRIPTEN_RESULT result = emscripten_request_fullscreen_strategy(canvas_id, false, &strategy);
ERR_FAIL_COND_MSG(result == EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED, "Enabling fullscreen is only possible from an input callback for the HTML5 platform.");
ERR_FAIL_COND_MSG(result != EMSCRIPTEN_RESULT_SUCCESS, "Enabling fullscreen is only possible from an input callback for the HTML5 platform.");
} break;
diff --git a/platform/javascript/display_server_javascript.h b/platform/javascript/display_server_javascript.h
index 9860ecdf98..b149665d67 100644
--- a/platform/javascript/display_server_javascript.h
+++ b/platform/javascript/display_server_javascript.h
@@ -53,6 +53,21 @@ class DisplayServerJavaScript : public DisplayServer {
double last_click_ms = 0;
int last_click_button_index = -1;
+ int last_width = 0;
+ int last_height = 0;
+
+ // utilities
+ static Point2 compute_position_in_canvas(int p_x, int p_y);
+ static void focus_canvas();
+ static bool is_canvas_focused();
+ template <typename T>
+ static void dom2godot_mod(T *emscripten_event_ptr, Ref<InputEventWithModifiers> godot_event);
+ static Ref<InputEventKey> setup_key_event(const EmscriptenKeyboardEvent *emscripten_event);
+ static const char *godot2dom_cursor(DisplayServer::CursorShape p_shape);
+ static void set_css_cursor(const char *p_cursor);
+ bool is_css_cursor_hidden() const;
+
+ // events
static EM_BOOL fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data);
static EM_BOOL keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data);
@@ -81,17 +96,20 @@ protected:
public:
// Override return type to make writing static callbacks less tedious.
static DisplayServerJavaScript *get_singleton();
+ static char canvas_id[256];
WindowMode window_mode = WINDOW_MODE_WINDOWED;
String clipboard;
- String canvas_id;
Callable window_event_callback;
Callable input_event_callback;
Callable input_text_callback;
Callable drop_files_callback;
+ // utilities
+ bool check_size_force_redraw();
+
// from DisplayServer
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
virtual bool has_feature(Feature p_feature) const;
diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp
index fd61c46e63..99672745e7 100644
--- a/platform/javascript/javascript_main.cpp
+++ b/platform/javascript/javascript_main.cpp
@@ -30,11 +30,13 @@
#include "core/io/resource_loader.h"
#include "main/main.h"
-#include "os_javascript.h"
+#include "platform/javascript/display_server_javascript.h"
+#include "platform/javascript/os_javascript.h"
#include <emscripten/emscripten.h>
static OS_JavaScript *os = nullptr;
+static uint64_t target_ticks = 0;
void exit_callback() {
emscripten_cancel_main_loop(); // After this, we can exit!
@@ -46,12 +48,32 @@ void exit_callback() {
}
void main_loop_callback() {
+ uint64_t current_ticks = os->get_ticks_usec();
+
+ bool force_draw = DisplayServerJavaScript::get_singleton()->check_size_force_redraw();
+ if (force_draw) {
+ Main::force_redraw();
+ } else if (current_ticks < target_ticks) {
+ return; // Skip frame.
+ }
+
+ int target_fps = Engine::get_singleton()->get_target_fps();
+ if (target_fps > 0) {
+ target_ticks += (uint64_t)(1000000 / target_fps);
+ }
if (os->main_loop_iterate()) {
emscripten_cancel_main_loop(); // Cancel current loop and wait for finalize_async.
+ /* clang-format off */
EM_ASM({
// This will contain the list of operations that need to complete before cleanup.
- Module.async_finish = [];
+ Module.async_finish = [
+ // Always contains at least one async promise, to avoid firing immediately if nothing is added.
+ new Promise(function(accept, reject) {
+ setTimeout(accept, 0);
+ })
+ ];
});
+ /* clang-format on */
os->get_main_loop()->finish();
os->finalize_async(); // Will add all the async finish functions.
EM_ASM({
@@ -79,13 +101,35 @@ extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) {
ResourceLoader::set_abort_on_missing_resources(false);
Main::start();
os->get_main_loop()->init();
- emscripten_resume_main_loop();
// Immediately run the first iteration.
// We are inside an animation frame, we want to immediately draw on the newly setup canvas.
main_loop_callback();
+ emscripten_resume_main_loop();
}
int main(int argc, char *argv[]) {
+ // Create and mount userfs immediately.
+ EM_ASM({
+ FS.mkdir('/userfs');
+ FS.mount(IDBFS, {}, '/userfs');
+ });
+
+ // Configure locale.
+ char locale_ptr[16];
+ /* clang-format off */
+ EM_ASM({
+ stringToUTF8(Module['locale'], $0, 16);
+ }, locale_ptr);
+ /* clang-format on */
+ setenv("LANG", locale_ptr, true);
+
+ // Ensure the canvas ID.
+ /* clang-format off */
+ EM_ASM({
+ stringToUTF8("#" + Module['canvas'].id, $0, 255);
+ }, DisplayServerJavaScript::canvas_id);
+ /* clang-format on */
+
os = new OS_JavaScript();
Main::setup(argv[0], argc - 1, &argv[1], false);
emscripten_set_main_loop(main_loop_callback, -1, false);
@@ -95,8 +139,6 @@ int main(int argc, char *argv[]) {
// run the 'main_after_fs_sync' function.
/* clang-format off */
EM_ASM({
- FS.mkdir('/userfs');
- FS.mount(IDBFS, {}, '/userfs');
FS.syncfs(true, function(err) {
requestAnimationFrame(function() {
ccall('main_after_fs_sync', null, ['string'], [err ? err.message : ""]);
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index ad4b5a5afa..1ff4304bcf 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -74,18 +74,12 @@ void OS_JavaScript::initialize() {
EngineDebugger::register_uri_handler("ws://", RemoteDebuggerPeerWebSocket::create);
EngineDebugger::register_uri_handler("wss://", RemoteDebuggerPeerWebSocket::create);
#endif
-
- char locale_ptr[16];
- /* clang-format off */
- EM_ASM({
- stringToUTF8(Module['locale'], $0, 16);
- }, locale_ptr);
- /* clang-format on */
- setenv("LANG", locale_ptr, true);
}
void OS_JavaScript::resume_audio() {
- audio_driver_javascript.resume();
+ if (audio_driver_javascript) {
+ audio_driver_javascript->resume();
+ }
}
void OS_JavaScript::set_main_loop(MainLoop *p_main_loop) {
@@ -133,11 +127,17 @@ void OS_JavaScript::delete_main_loop() {
void OS_JavaScript::finalize_async() {
finalizing = true;
- audio_driver_javascript.finish_async();
+ if (audio_driver_javascript) {
+ audio_driver_javascript->finish_async();
+ }
}
void OS_JavaScript::finalize() {
delete_main_loop();
+ if (audio_driver_javascript) {
+ memdelete(audio_driver_javascript);
+ audio_driver_javascript = nullptr;
+ }
}
// Miscellaneous
@@ -246,7 +246,10 @@ void OS_JavaScript::initialize_joypads() {
}
OS_JavaScript::OS_JavaScript() {
- AudioDriverManager::add_driver(&audio_driver_javascript);
+ if (AudioDriverJavaScript::is_available()) {
+ audio_driver_javascript = memnew(AudioDriverJavaScript);
+ AudioDriverManager::add_driver(audio_driver_javascript);
+ }
Vector<Logger *> loggers;
loggers.push_back(memnew(StdLogger));
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index f0f18b44f8..22234f9355 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -40,7 +40,7 @@
class OS_JavaScript : public OS_Unix {
MainLoop *main_loop = nullptr;
- AudioDriverJavaScript audio_driver_javascript;
+ AudioDriverJavaScript *audio_driver_javascript = nullptr;
bool finalizing = false;
bool idb_available = false;
@@ -83,6 +83,9 @@ public:
String get_executable_path() const;
virtual Error shell_open(String p_uri);
virtual String get_name() const;
+ // Override default OS implementation which would block the main thread with delay_usec.
+ // Implemented in javascript_main.cpp loop callback instead.
+ virtual void add_frame_delay(bool p_can_draw) {}
virtual bool can_draw() const;
virtual String get_cache_path() const;
diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index 94c2e989f1..827d0361b9 100644
--- a/platform/linuxbsd/display_server_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -2339,6 +2339,30 @@ void DisplayServerX11::_send_window_event(const WindowData &wd, WindowEvent p_ev
void DisplayServerX11::process_events() {
_THREAD_SAFE_METHOD_
+ if (app_focused) {
+ //verify that one of the windows has focus, else send focus out notification
+ bool focus_found = false;
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (E->get().focused) {
+ focus_found = true;
+ }
+ }
+
+ if (!focus_found) {
+ uint64_t delta = OS::get_singleton()->get_ticks_msec() - time_since_no_focus;
+
+ if (delta > 250) {
+ //X11 can go between windows and have no focus for a while, when creating them or something else. Use this as safety to avoid unnecesary focus in/outs.
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
+ }
+ app_focused = false;
+ }
+ } else {
+ time_since_no_focus = OS::get_singleton()->get_ticks_msec();
+ }
+ }
+
do_mouse_warp = false;
// Is the current mouse mode one where it needs to be grabbed.
@@ -2533,12 +2557,12 @@ void DisplayServerX11::process_events() {
break;
case NoExpose:
- minimized = true;
+ windows[window_id].minimized = true;
break;
case VisibilityNotify: {
XVisibilityEvent *visibility = (XVisibilityEvent *)&event;
- minimized = (visibility->state == VisibilityFullyObscured);
+ windows[window_id].minimized = (visibility->state == VisibilityFullyObscured);
} break;
case LeaveNotify: {
if (!mouse_mode_grab) {
@@ -2552,10 +2576,8 @@ void DisplayServerX11::process_events() {
}
} break;
case FocusIn:
- minimized = false;
- window_has_focus = true;
+ windows[window_id].focused = true;
_send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_IN);
- window_focused = true;
if (mouse_mode_grab) {
// Show and update the cursor if confined and the window regained focus.
@@ -2582,13 +2604,19 @@ void DisplayServerX11::process_events() {
if (windows[window_id].xic) {
XSetICFocus(windows[window_id].xic);
}
+
+ if (!app_focused) {
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
+ }
+ app_focused = true;
+ }
break;
case FocusOut:
- window_has_focus = false;
+ windows[window_id].focused = false;
Input::get_singleton()->release_pressed_events();
_send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_OUT);
- window_focused = false;
if (mouse_mode_grab) {
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
@@ -2727,7 +2755,7 @@ void DisplayServerX11::process_events() {
Point2i new_center = pos;
pos = last_mouse_pos + xi.relative_motion;
center = new_center;
- do_mouse_warp = window_has_focus; // warp the cursor if we're focused in
+ do_mouse_warp = windows[window_id].focused; // warp the cursor if we're focused in
}
if (!last_mouse_pos_valid) {
@@ -2787,7 +2815,7 @@ void DisplayServerX11::process_events() {
// Don't propagate the motion event unless we have focus
// this is so that the relative motion doesn't get messed up
// after we regain focus.
- if (window_has_focus || !mouse_mode_grab) {
+ if (windows[window_id].focused || !mouse_mode_grab) {
Input::get_singleton()->accumulate_input_event(mm);
}
@@ -3764,8 +3792,6 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
requested = None;
- window_has_focus = true; // Set focus to true at init
-
/*if (p_desired.layered) {
set_window_per_pixel_transparency_enabled(true);
}*/
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
index 3b2ff0e08d..b5d2ea1c63 100644
--- a/platform/linuxbsd/display_server_x11.h
+++ b/platform/linuxbsd/display_server_x11.h
@@ -139,6 +139,8 @@ class DisplayServerX11 : public DisplayServer {
bool borderless = false;
bool resize_disabled = false;
Vector2i last_position_before_fs;
+ bool focused = false;
+ bool minimized = false;
};
Map<WindowID, WindowData> windows;
@@ -164,6 +166,8 @@ class DisplayServerX11 : public DisplayServer {
uint64_t last_click_ms;
int last_click_button_index;
uint32_t last_button_state;
+ bool app_focused = false;
+ uint64_t time_since_no_focus = 0;
struct {
int opcode;
@@ -195,8 +199,8 @@ class DisplayServerX11 : public DisplayServer {
void _handle_key_event(WindowID p_window, XKeyEvent *p_event, bool p_echo = false);
- bool minimized;
- bool window_has_focus;
+ //bool minimized;
+ //bool window_has_focus;
bool do_mouse_warp;
const char *cursor_theme;
@@ -210,7 +214,7 @@ class DisplayServerX11 : public DisplayServer {
bool layered_window;
String rendering_driver;
- bool window_focused;
+ //bool window_focused;
//void set_wm_border(bool p_enabled);
void set_wm_fullscreen(bool p_enabled);
void set_wm_above(bool p_enabled);
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index 29aa8ece19..ff4c024551 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -87,8 +87,15 @@ def configure(env):
env["osxcross"] = True
if not "osxcross" in env: # regular native build
- env.Append(CCFLAGS=["-arch", "x86_64"])
- env.Append(LINKFLAGS=["-arch", "x86_64"])
+ if env["arch"] == "arm64":
+ print("Building for macOS 10.15+, platform arm64.")
+ env.Append(CCFLAGS=["-arch", "arm64", "-mmacosx-version-min=10.15", "-target", "arm64-apple-macos10.15"])
+ env.Append(LINKFLAGS=["-arch", "arm64", "-mmacosx-version-min=10.15", "-target", "arm64-apple-macos10.15"])
+ else:
+ print("Building for macOS 10.12+, platform x86-64.")
+ env.Append(CCFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.12"])
+ env.Append(LINKFLAGS=["-arch", "x86_64", "-mmacosx-version-min=10.12"])
+
if env["macports_clang"] != "no":
mpprefix = os.environ.get("MACPORTS_PREFIX", "/opt/local")
mpclangver = env["macports_clang"]
@@ -148,7 +155,8 @@ def configure(env):
## Dependencies
if env["builtin_libtheora"]:
- env["x86_libtheora_opt_gcc"] = True
+ if env["arch"] != "arm64":
+ env["x86_libtheora_opt_gcc"] = True
## Flags
@@ -189,6 +197,3 @@ def configure(env):
env.Append(LIBS=["vulkan"])
# env.Append(CPPDEFINES=['GLES_ENABLED', 'OPENGL_ENABLED'])
-
- env.Append(CCFLAGS=["-mmacosx-version-min=10.12"])
- env.Append(LINKFLAGS=["-mmacosx-version-min=10.12"])
diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h
index fddb1d0ca6..8e27f10dc2 100644
--- a/platform/osx/display_server_osx.h
+++ b/platform/osx/display_server_osx.h
@@ -86,6 +86,14 @@ public:
uint32_t unicode;
};
+ struct WarpEvent {
+ NSTimeInterval timestamp;
+ NSPoint delta;
+ };
+
+ List<WarpEvent> warp_events;
+ NSTimeInterval last_warp = 0;
+
Vector<KeyEvent> key_event_buffer;
int key_event_pos;
@@ -220,7 +228,7 @@ public:
virtual Vector<int> get_window_list() const;
- virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i & = Rect2i());
+ virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i());
virtual void delete_sub_window(WindowID p_id);
virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index 93f6e3540a..ee67f46a4c 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -578,7 +578,11 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
trackingArea = nil;
imeInputEventInProgress = false;
[self updateTrackingAreas];
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
+ [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]];
+#else
[self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
+#endif
markedText = [[NSMutableAttributedString alloc] init];
return self;
}
@@ -735,11 +739,19 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
NSPasteboard *pboard = [sender draggingPasteboard];
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
+ NSArray<NSURL *> *filenames = [pboard propertyListForType:NSPasteboardTypeFileURL];
+#else
NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
+#endif
Vector<String> files;
for (NSUInteger i = 0; i < filenames.count; i++) {
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101400
+ NSString *ns = [[filenames objectAtIndex:i] path];
+#else
NSString *ns = [filenames objectAtIndex:i];
+#endif
char *utfs = strdup([ns UTF8String]);
String ret;
ret.parse_utf8(utfs);
@@ -836,13 +848,59 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+ NSPoint delta = NSMakePoint([event deltaX], [event deltaY]);
+ NSPoint mpos = [event locationInWindow];
+
+ if (DS_OSX->mouse_mode == DisplayServer::MOUSE_MODE_CONFINED) {
+ // Discard late events
+ if (([event timestamp]) < DS_OSX->last_warp) {
+ return;
+ }
+
+ // Warp affects next event delta, subtract previous warp deltas
+ List<DisplayServerOSX::WarpEvent>::Element *F = DS_OSX->warp_events.front();
+ while (F) {
+ if (F->get().timestamp < [event timestamp]) {
+ List<DisplayServerOSX::WarpEvent>::Element *E = F;
+ delta.x -= E->get().delta.x;
+ delta.y -= E->get().delta.y;
+ F = F->next();
+ DS_OSX->warp_events.erase(E);
+ } else {
+ F = F->next();
+ }
+ }
+
+ // Confine mouse position to the window, and update delta
+ NSRect frame = [wd.window_object frame];
+ NSPoint conf_pos = mpos;
+ conf_pos.x = CLAMP(conf_pos.x + delta.x, 0.f, frame.size.width);
+ conf_pos.y = CLAMP(conf_pos.y - delta.y, 0.f, frame.size.height);
+ delta.x = conf_pos.x - mpos.x;
+ delta.y = mpos.y - conf_pos.y;
+ mpos = conf_pos;
+
+ // Move mouse cursor
+ NSRect pointInWindowRect = NSMakeRect(conf_pos.x, conf_pos.y, 0, 0);
+ conf_pos = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin;
+ conf_pos.y = CGDisplayBounds(CGMainDisplayID()).size.height - conf_pos.y;
+ CGWarpMouseCursorPosition(conf_pos);
+
+ // Save warp data
+ DS_OSX->last_warp = [[NSProcessInfo processInfo] systemUptime];
+ DisplayServerOSX::WarpEvent ev;
+ ev.timestamp = DS_OSX->last_warp;
+ ev.delta = delta;
+ DS_OSX->warp_events.push_back(ev);
+ }
+
Ref<InputEventMouseMotion> mm;
mm.instance();
mm->set_window_id(window_id);
mm->set_button_mask(DS_OSX->last_button_state);
const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [[event window] backingScaleFactor] : 1.0;
- const Vector2i pos = _get_mouse_pos(wd, [event locationInWindow], backingScaleFactor);
+ const Vector2i pos = _get_mouse_pos(wd, mpos, backingScaleFactor);
mm->set_position(pos);
mm->set_pressure([event pressure]);
if ([event subtype] == NSEventSubtypeTabletPoint) {
@@ -851,9 +909,7 @@ static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, i
}
mm->set_global_position(pos);
mm->set_speed(Input::get_singleton()->get_last_mouse_speed());
- Vector2i relativeMotion = Vector2i();
- relativeMotion.x = [event deltaX] * backingScaleFactor;
- relativeMotion.y = [event deltaY] * backingScaleFactor;
+ const Vector2i relativeMotion = Vector2i(delta.x, delta.y) * backingScaleFactor;
mm->set_relative(relativeMotion);
_get_key_modifier_state([event modifierFlags], mm);
@@ -1958,11 +2014,15 @@ void DisplayServerOSX::mouse_set_mode(MouseMode p_mode) {
CGDisplayHideCursor(kCGDirectMainDisplay);
}
CGAssociateMouseAndMouseCursorPosition(true);
+ } else if (p_mode == MOUSE_MODE_CONFINED) {
+ CGDisplayShowCursor(kCGDirectMainDisplay);
+ CGAssociateMouseAndMouseCursorPosition(false);
} else {
CGDisplayShowCursor(kCGDirectMainDisplay);
CGAssociateMouseAndMouseCursorPosition(true);
}
+ warp_events.clear();
mouse_mode = p_mode;
}
@@ -1992,7 +2052,9 @@ void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) {
CGEventSourceSetLocalEventsSuppressionInterval(lEventRef, 0.0);
CGAssociateMouseAndMouseCursorPosition(false);
CGWarpMouseCursorPosition(lMouseWarpPos);
- CGAssociateMouseAndMouseCursorPosition(true);
+ if (mouse_mode != MOUSE_MODE_CONFINED) {
+ CGAssociateMouseAndMouseCursorPosition(true);
+ }
}
}