summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
authorRĂ©mi Verschelde <rverschelde@gmail.com>2020-03-26 17:23:11 +0100
committerGitHub <noreply@github.com>2020-03-26 17:23:11 +0100
commit5f1107aa30295e686be6f41cb6d17fc2cff1e036 (patch)
tree7bce4c680e6686c9d29be8b479be5b39205ce7a3 /platform
parenta2da99f40cf2123c0906c734a2eb01e9b65a45a2 (diff)
parentbe07f86f85ab70a48b310b42faa64e72a74ca694 (diff)
Merge pull request #37317 from akien-mga/display-server-rebased
Separate DisplayServer from OS and add multiple windows support
Diffstat (limited to 'platform')
-rw-r--r--platform/android/java_godot_lib_jni.cpp4
-rw-r--r--platform/android/os_android.cpp6
-rw-r--r--platform/android/os_android.h3
-rw-r--r--platform/haiku/haiku_direct_window.cpp2
-rw-r--r--platform/haiku/haiku_direct_window.h2
-rw-r--r--platform/haiku/os_haiku.h2
-rw-r--r--platform/iphone/os_iphone.h4
-rw-r--r--platform/javascript/os_javascript.cpp12
-rw-r--r--platform/javascript/os_javascript.h2
-rw-r--r--platform/linuxbsd/SCsub23
-rw-r--r--platform/linuxbsd/context_gl_x11.cpp (renamed from platform/x11/context_gl_x11.cpp)0
-rw-r--r--platform/linuxbsd/context_gl_x11.h (renamed from platform/x11/context_gl_x11.h)0
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.cpp (renamed from platform/x11/crash_handler_x11.cpp)4
-rw-r--r--platform/linuxbsd/crash_handler_linuxbsd.h (renamed from platform/x11/crash_handler_x11.h)2
-rw-r--r--platform/linuxbsd/detect.py (renamed from platform/x11/detect.py)8
-rw-r--r--platform/linuxbsd/detect_prime_x11.cpp (renamed from platform/x11/detect_prime.cpp)2
-rw-r--r--platform/linuxbsd/detect_prime_x11.h (renamed from platform/x11/detect_prime.h)2
-rw-r--r--platform/linuxbsd/display_server_x11.cpp (renamed from platform/x11/os_x11.cpp)4136
-rw-r--r--platform/linuxbsd/display_server_x11.h352
-rw-r--r--platform/linuxbsd/export/export.cpp (renamed from platform/x11/export/export.cpp)6
-rw-r--r--platform/linuxbsd/export/export.h (renamed from platform/x11/export/export.h)8
-rw-r--r--platform/linuxbsd/godot_linuxbsd.cpp (renamed from platform/x11/godot_x11.cpp)6
-rw-r--r--platform/linuxbsd/joypad_linux.cpp (renamed from platform/x11/joypad_linux.cpp)22
-rw-r--r--platform/linuxbsd/joypad_linux.h (renamed from platform/x11/joypad_linux.h)10
-rw-r--r--platform/linuxbsd/key_mapping_x11.cpp (renamed from platform/x11/key_mapping_x11.cpp)0
-rw-r--r--platform/linuxbsd/key_mapping_x11.h (renamed from platform/x11/key_mapping_x11.h)0
-rw-r--r--platform/linuxbsd/logo.png (renamed from platform/x11/logo.png)bin1679 -> 1679 bytes
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp381
-rw-r--r--platform/linuxbsd/os_linuxbsd.h106
-rw-r--r--platform/linuxbsd/pck_embed.ld (renamed from platform/x11/pck_embed.ld)0
-rw-r--r--platform/linuxbsd/pck_embed.legacy.ld (renamed from platform/x11/pck_embed.legacy.ld)0
-rw-r--r--platform/linuxbsd/platform_config.h (renamed from platform/x11/platform_config.h)0
-rw-r--r--platform/linuxbsd/platform_linuxbsd_builders.py (renamed from platform/x11/platform_x11_builders.py)2
-rw-r--r--platform/linuxbsd/vulkan_context_x11.cpp (renamed from platform/x11/vulkan_context_x11.cpp)6
-rw-r--r--platform/linuxbsd/vulkan_context_x11.h (renamed from platform/x11/vulkan_context_x11.h)2
-rw-r--r--platform/osx/SCsub1
-rw-r--r--platform/osx/display_server_osx.h306
-rw-r--r--platform/osx/display_server_osx.mm3587
-rw-r--r--platform/osx/joypad_osx.cpp28
-rw-r--r--platform/osx/joypad_osx.h6
-rw-r--r--platform/osx/os_osx.h244
-rw-r--r--platform/osx/os_osx.mm2880
-rw-r--r--platform/osx/vulkan_context_osx.h2
-rw-r--r--platform/osx/vulkan_context_osx.mm6
-rw-r--r--platform/server/detect.py2
-rw-r--r--platform/server/os_server.h4
-rw-r--r--platform/uwp/joypad_uwp.h2
-rw-r--r--platform/uwp/os_uwp.h3
-rw-r--r--platform/windows/SCsub1
-rw-r--r--platform/windows/display_server_windows.cpp2968
-rw-r--r--platform/windows/display_server_windows.h389
-rw-r--r--platform/windows/joypad_windows.cpp26
-rw-r--r--platform/windows/joypad_windows.h6
-rw-r--r--platform/windows/os_windows.cpp2604
-rw-r--r--platform/windows/os_windows.h293
-rw-r--r--platform/windows/vulkan_context_win.cpp4
-rw-r--r--platform/windows/vulkan_context_win.h2
-rw-r--r--platform/x11/SCsub21
-rw-r--r--platform/x11/os_x11.h339
59 files changed, 10523 insertions, 8316 deletions
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 8bbf41d82d..e018ee69f0 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "java_godot_lib_jni.h"
+
#include "java_godot_io_wrapper.h"
#include "java_godot_wrapper.h"
@@ -37,17 +38,18 @@
#include "api/java_class_wrapper.h"
#include "audio_driver_jandroid.h"
#include "core/engine.h"
+#include "core/input/input_filter.h"
#include "core/project_settings.h"
#include "dir_access_jandroid.h"
#include "file_access_android.h"
#include "file_access_jandroid.h"
#include "jni_utils.h"
-#include "main/input_default.h"
#include "main/main.h"
#include "net_socket_android.h"
#include "os_android.h"
#include "string_android.h"
#include "thread_jandroid.h"
+
#include <unistd.h>
static JavaClassWrapper *java_class_wrapper = NULL;
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 8021d6dd07..e3792b1f31 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -306,14 +306,14 @@ void OS_Android::main_loop_end() {
void OS_Android::main_loop_focusout() {
if (main_loop)
- main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
+ main_loop->notification(NOTIFICATION_WM_FOCUS_OUT);
audio_driver_android.set_pause(true);
}
void OS_Android::main_loop_focusin() {
if (main_loop)
- main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
+ main_loop->notification(NOTIFICATION_WM_FOCUS_IN);
audio_driver_android.set_pause(false);
}
@@ -568,7 +568,7 @@ void OS_Android::init_video_mode(int p_video_width, int p_video_height) {
void OS_Android::main_loop_request_go_back() {
if (main_loop)
- main_loop->notification(MainLoop::NOTIFICATION_WM_GO_BACK_REQUEST);
+ main_loop->notification(NOTIFICATION_WM_GO_BACK_REQUEST);
}
void OS_Android::set_display_size(Size2 p_size) {
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index ec6ffe5438..d09f4e10d6 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -33,10 +33,9 @@
#include "audio_driver_jandroid.h"
#include "audio_driver_opensl.h"
-#include "core/os/input.h"
+#include "core/input/input_filter.h"
#include "core/os/main_loop.h"
#include "drivers/unix/os_unix.h"
-#include "main/input_default.h"
#include "servers/audio_server.h"
#include "servers/visual/rasterizer.h"
diff --git a/platform/haiku/haiku_direct_window.cpp b/platform/haiku/haiku_direct_window.cpp
index 2d7efe6b61..18e1862d33 100644
--- a/platform/haiku/haiku_direct_window.cpp
+++ b/platform/haiku/haiku_direct_window.cpp
@@ -74,7 +74,7 @@ void HaikuDirectWindow::SetMainLoop(MainLoop *p_main_loop) {
bool HaikuDirectWindow::QuitRequested() {
StopMessageRunner();
- main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
+ main_loop->notification(NOTIFICATION_WM_CLOSE_REQUEST);
return false;
}
diff --git a/platform/haiku/haiku_direct_window.h b/platform/haiku/haiku_direct_window.h
index ccc9542f0e..925bb9ac5d 100644
--- a/platform/haiku/haiku_direct_window.h
+++ b/platform/haiku/haiku_direct_window.h
@@ -35,8 +35,8 @@
#include <DirectWindow.h>
+#include "core/input/input_filter.h"
#include "core/os/os.h"
-#include "main/input_default.h"
#include "haiku_gl_view.h"
diff --git a/platform/haiku/os_haiku.h b/platform/haiku/os_haiku.h
index fc8cb77a91..90c0abc3ef 100644
--- a/platform/haiku/os_haiku.h
+++ b/platform/haiku/os_haiku.h
@@ -33,10 +33,10 @@
#include "audio_driver_media_kit.h"
#include "context_gl_haiku.h"
+#include "core/input/input_filter.h"
#include "drivers/unix/os_unix.h"
#include "haiku_application.h"
#include "haiku_direct_window.h"
-#include "main/input_default.h"
#include "servers/audio_server.h"
#include "servers/visual_server.h"
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index f42679e754..2efb7af7c2 100644
--- a/platform/iphone/os_iphone.h
+++ b/platform/iphone/os_iphone.h
@@ -33,15 +33,13 @@
#ifndef OS_IPHONE_H
#define OS_IPHONE_H
-#include "core/os/input.h"
+#include "core/input/input_filter.h"
#include "drivers/coreaudio/audio_driver_coreaudio.h"
#include "drivers/unix/os_unix.h"
-
#include "game_center.h"
#include "icloud.h"
#include "in_app_store.h"
#include "ios.h"
-#include "main/input_default.h"
#include "servers/audio_server.h"
#include "servers/visual/rasterizer.h"
#include "servers/visual_server.h"
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index db90b01f8f..b7feb5bfbd 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -1009,10 +1009,10 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver,
update_clipboard(evt.clipboardData.getData('text'));
}, true);
},
- MainLoop::NOTIFICATION_WM_MOUSE_ENTER,
- MainLoop::NOTIFICATION_WM_MOUSE_EXIT,
- MainLoop::NOTIFICATION_WM_FOCUS_IN,
- MainLoop::NOTIFICATION_WM_FOCUS_OUT
+ NOTIFICATION_WM_MOUSE_ENTER,
+ NOTIFICATION_WM_MOUSE_EXIT,
+ NOTIFICATION_WM_FOCUS_IN,
+ NOTIFICATION_WM_FOCUS_OUT
);
/* clang-format on */
@@ -1121,8 +1121,8 @@ int OS_JavaScript::get_process_id() const {
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;
+ if (p_notification == NOTIFICATION_WM_MOUSE_ENTER || p_notification == NOTIFICATION_WM_MOUSE_EXIT) {
+ cursor_inside_canvas = p_notification == NOTIFICATION_WM_MOUSE_ENTER;
}
OS_JavaScript::get_singleton()->get_main_loop()->notification(p_notification);
}
diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h
index ed59477914..1a3a6616b0 100644
--- a/platform/javascript/os_javascript.h
+++ b/platform/javascript/os_javascript.h
@@ -32,8 +32,8 @@
#define OS_JAVASCRIPT_H
#include "audio_driver_javascript.h"
+#include "core/input/input_filter.h"
#include "drivers/unix/os_unix.h"
-#include "main/input_default.h"
#include "servers/audio_server.h"
#include "servers/visual/rasterizer.h"
diff --git a/platform/linuxbsd/SCsub b/platform/linuxbsd/SCsub
new file mode 100644
index 0000000000..f3f65e216e
--- /dev/null
+++ b/platform/linuxbsd/SCsub
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+Import('env')
+
+from platform_methods import run_in_subprocess
+import platform_linuxbsd_builders
+
+common_x11 = [
+ "crash_handler_linuxbsd.cpp",
+ "os_linuxbsd.cpp",
+ "joypad_linux.cpp",
+ "context_gl_x11.cpp",
+ "detect_prime_x11.cpp",
+ "display_server_x11.cpp",
+ "vulkan_context_x11.cpp",
+ "key_mapping_x11.cpp",
+
+]
+
+prog = env.add_program('#bin/godot', ['godot_linuxbsd.cpp'] + common_x11)
+
+if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]:
+ env.AddPostAction(prog, run_in_subprocess(platform_linuxbsd_builders.make_debug_linuxbsd))
diff --git a/platform/x11/context_gl_x11.cpp b/platform/linuxbsd/context_gl_x11.cpp
index 5442af3bef..5442af3bef 100644
--- a/platform/x11/context_gl_x11.cpp
+++ b/platform/linuxbsd/context_gl_x11.cpp
diff --git a/platform/x11/context_gl_x11.h b/platform/linuxbsd/context_gl_x11.h
index 2c0643c95a..2c0643c95a 100644
--- a/platform/x11/context_gl_x11.h
+++ b/platform/linuxbsd/context_gl_x11.h
diff --git a/platform/x11/crash_handler_x11.cpp b/platform/linuxbsd/crash_handler_linuxbsd.cpp
index 19c8f71d0e..1b3804e3ed 100644
--- a/platform/x11/crash_handler_x11.cpp
+++ b/platform/linuxbsd/crash_handler_linuxbsd.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* crash_handler_x11.cpp */
+/* crash_handler_linuxbsd.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,7 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "crash_handler_x11.h"
+#include "crash_handler_linuxbsd.h"
#include "core/os/os.h"
#include "core/project_settings.h"
diff --git a/platform/x11/crash_handler_x11.h b/platform/linuxbsd/crash_handler_linuxbsd.h
index 98620cc789..94b4649690 100644
--- a/platform/x11/crash_handler_x11.h
+++ b/platform/linuxbsd/crash_handler_linuxbsd.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* crash_handler_x11.h */
+/* crash_handler_linuxbsd.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
diff --git a/platform/x11/detect.py b/platform/linuxbsd/detect.py
index 9a9ab86068..1a395efffe 100644
--- a/platform/x11/detect.py
+++ b/platform/linuxbsd/detect.py
@@ -8,7 +8,7 @@ def is_active():
def get_name():
- return "X11"
+ return "LinuxBSD"
def can_build():
@@ -317,7 +317,7 @@ def configure(env):
if not env['builtin_zlib']:
env.ParseConfig('pkg-config zlib --cflags --libs')
- env.Prepend(CPPPATH=['#platform/x11'])
+ env.Prepend(CPPPATH=['#platform/linuxbsd'])
env.Append(CPPDEFINES=['X11_ENABLED', 'UNIX_ENABLED'])
env.Append(CPPDEFINES=['VULKAN_ENABLED'])
@@ -350,9 +350,9 @@ def configure(env):
print("Warning: Creating template binaries enabled for PCK embedding is currently only supported with GNU ld")
else:
if float(gnu_ld_version.group(1)) >= 2.30:
- env.Append(LINKFLAGS=['-T', 'platform/x11/pck_embed.ld'])
+ env.Append(LINKFLAGS=['-T', 'platform/linuxbsd/pck_embed.ld'])
else:
- env.Append(LINKFLAGS=['-T', 'platform/x11/pck_embed.legacy.ld'])
+ env.Append(LINKFLAGS=['-T', 'platform/linuxbsd/pck_embed.legacy.ld'])
## Cross-compilation
diff --git a/platform/x11/detect_prime.cpp b/platform/linuxbsd/detect_prime_x11.cpp
index d79fd00118..69b0837e6c 100644
--- a/platform/x11/detect_prime.cpp
+++ b/platform/linuxbsd/detect_prime_x11.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* detect_prime.cpp */
+/* detect_prime_x11.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
diff --git a/platform/x11/detect_prime.h b/platform/linuxbsd/detect_prime_x11.h
index df636449f4..039bdee76b 100644
--- a/platform/x11/detect_prime.h
+++ b/platform/linuxbsd/detect_prime_x11.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* detect_prime.h */
+/* detect_prime_x11.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
diff --git a/platform/x11/os_x11.cpp b/platform/linuxbsd/display_server_x11.cpp
index 772913980b..4bea458187 100644
--- a/platform/x11/os_x11.cpp
+++ b/platform/linuxbsd/display_server_x11.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* os_x11.cpp */
+/* display_server_x11.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,8 +28,11 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "os_x11.h"
-#include "detect_prime.h"
+#include "display_server_x11.h"
+
+#ifdef X11_ENABLED
+
+#include "detect_prime_x11.h"
#include "core/os/dir_access.h"
#include "core/print_string.h"
@@ -44,8 +47,7 @@
#include "servers/visual/rasterizer_rd/rasterizer_rd.h"
#endif
-#include "servers/visual/visual_server_raster.h"
-#include "servers/visual/visual_server_wrap_mt.h"
+#include "scene/resources/texture.h"
#ifdef HAVE_MNTENT
#include <mntent.h>
@@ -86,6 +88,8 @@
#include <X11/XKBlib.h>
+#include "core/project_settings.h"
+
// 2.2 is the first release with multitouch
#define XINPUT_CLIENT_VERSION_MAJOR 2
#define XINPUT_CLIENT_VERSION_MINOR 2
@@ -99,584 +103,117 @@
static const double abs_resolution_mult = 10000.0;
static const double abs_resolution_range_mult = 10.0;
-void OS_X11::initialize_core() {
-
- crash_handler.initialize();
-
- OS_Unix::initialize_core();
-}
-
-int OS_X11::get_current_video_driver() const {
- return video_driver_index;
-}
-
-Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
-
- long im_event_mask = 0;
- last_button_state = 0;
-
- xmbstring = NULL;
- x11_window = 0;
- last_click_ms = 0;
- last_click_button_index = -1;
- last_click_pos = Point2(-100, -100);
- args = OS::get_singleton()->get_cmdline_args();
- current_videomode = p_desired;
- main_loop = NULL;
- last_timestamp = 0;
- last_mouse_pos_valid = false;
- last_keyrelease_time = 0;
- xdnd_version = 0;
-
- if (get_render_thread_mode() == RENDER_SEPARATE_THREAD) {
- XInitThreads();
- }
-
- /** XLIB INITIALIZATION **/
- x11_display = XOpenDisplay(NULL);
-
- if (!x11_display) {
- ERR_PRINT("X11 Display is not available");
- return ERR_UNAVAILABLE;
- }
-
- char *modifiers = NULL;
- Bool xkb_dar = False;
- XAutoRepeatOn(x11_display);
- xkb_dar = XkbSetDetectableAutoRepeat(x11_display, True, NULL);
-
- // Try to support IME if detectable auto-repeat is supported
- if (xkb_dar == True) {
-
-#ifdef X_HAVE_UTF8_STRING
- // Xutf8LookupString will be used later instead of XmbLookupString before
- // the multibyte sequences can be converted to unicode string.
- modifiers = XSetLocaleModifiers("");
+bool DisplayServerX11::has_feature(Feature p_feature) const {
+ switch (p_feature) {
+ case FEATURE_SUBWINDOWS:
+#ifdef TOUCH_ENABLED
+ case FEATURE_TOUCHSCREEN:
#endif
- }
-
- if (modifiers == NULL) {
- if (is_stdout_verbose()) {
- WARN_PRINT("IME is disabled");
- }
- XSetLocaleModifiers("@im=none");
- WARN_PRINT("Error setting locale modifiers");
- }
-
- const char *err;
- xrr_get_monitors = NULL;
- xrr_free_monitors = NULL;
- int xrandr_major = 0;
- int xrandr_minor = 0;
- int event_base, error_base;
- xrandr_ext_ok = XRRQueryExtension(x11_display, &event_base, &error_base);
- xrandr_handle = dlopen("libXrandr.so.2", RTLD_LAZY);
- if (!xrandr_handle) {
- err = dlerror();
- fprintf(stderr, "could not load libXrandr.so.2, Error: %s\n", err);
- } else {
- XRRQueryVersion(x11_display, &xrandr_major, &xrandr_minor);
- if (((xrandr_major << 8) | xrandr_minor) >= 0x0105) {
- xrr_get_monitors = (xrr_get_monitors_t)dlsym(xrandr_handle, "XRRGetMonitors");
- if (!xrr_get_monitors) {
- err = dlerror();
- fprintf(stderr, "could not find symbol XRRGetMonitors\nError: %s\n", err);
- } else {
- xrr_free_monitors = (xrr_free_monitors_t)dlsym(xrandr_handle, "XRRFreeMonitors");
- if (!xrr_free_monitors) {
- err = dlerror();
- fprintf(stderr, "could not find XRRFreeMonitors\nError: %s\n", err);
- xrr_get_monitors = NULL;
- }
- }
- }
- }
-
- if (!refresh_device_info()) {
- OS::get_singleton()->alert("Your system does not support XInput 2.\n"
- "Please upgrade your distribution.",
- "Unable to initialize XInput");
- return ERR_UNAVAILABLE;
- }
-
- xim = XOpenIM(x11_display, NULL, NULL, NULL);
-
- if (xim == NULL) {
- WARN_PRINT("XOpenIM failed");
- xim_style = 0L;
- } else {
- ::XIMCallback im_destroy_callback;
- im_destroy_callback.client_data = (::XPointer)(this);
- im_destroy_callback.callback = (::XIMProc)(xim_destroy_callback);
- if (XSetIMValues(xim, XNDestroyCallback, &im_destroy_callback,
- NULL) != NULL) {
- WARN_PRINT("Error setting XIM destroy callback");
- }
-
- ::XIMStyles *xim_styles = NULL;
- xim_style = 0L;
- char *imvalret = XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL);
- if (imvalret != NULL || xim_styles == NULL) {
- fprintf(stderr, "Input method doesn't support any styles\n");
- }
-
- if (xim_styles) {
- xim_style = 0L;
- for (int i = 0; i < xim_styles->count_styles; i++) {
-
- if (xim_styles->supported_styles[i] ==
- (XIMPreeditNothing | XIMStatusNothing)) {
-
- xim_style = xim_styles->supported_styles[i];
- break;
- }
- }
-
- XFree(xim_styles);
+ case FEATURE_MOUSE:
+ case FEATURE_MOUSE_WARP:
+ case FEATURE_CLIPBOARD:
+ case FEATURE_CURSOR_SHAPE:
+ case FEATURE_CUSTOM_CURSOR_SHAPE:
+ case FEATURE_IME:
+ case FEATURE_WINDOW_TRANSPARENCY:
+ //case FEATURE_HIDPI:
+ case FEATURE_ICON:
+ case FEATURE_NATIVE_ICON:
+ case FEATURE_SWAP_BUFFERS:
+ return true;
+ default: {
}
- XFree(imvalret);
}
- //!!!!!!!!!!!!!!!!!!!!!!!!!!
- //TODO - do Vulkan and GLES2 support checks, driver selection and fallback
- video_driver_index = p_video_driver;
-#ifndef _MSC_VER
-#warning Forcing vulkan video driver because OpenGL not implemented yet
-#endif
- video_driver_index = VIDEO_DRIVER_VULKAN;
-
- print_verbose("Driver: " + String(get_video_driver_name(video_driver_index)) + " [" + itos(video_driver_index) + "]");
- //!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- //Create window
-
- long visualMask = VisualScreenMask;
- int numberOfVisuals;
- XVisualInfo vInfoTemplate = {};
- vInfoTemplate.screen = DefaultScreen(x11_display);
- XVisualInfo *visualInfo = XGetVisualInfo(x11_display, visualMask, &vInfoTemplate, &numberOfVisuals);
-
- Colormap colormap = XCreateColormap(x11_display, RootWindow(x11_display, vInfoTemplate.screen), visualInfo->visual, AllocNone);
-
- XSetWindowAttributes windowAttributes = {};
- windowAttributes.colormap = colormap;
- windowAttributes.background_pixel = 0xFFFFFFFF;
- windowAttributes.border_pixel = 0;
- windowAttributes.event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask;
-
- unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask;
- x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, visualInfo->depth, InputOutput, visualInfo->visual, valuemask, &windowAttributes);
-
- wm_delete = XInternAtom(x11_display, "WM_DELETE_WINDOW", true);
- XSetWMProtocols(x11_display, x11_window, &wm_delete, 1);
-
- //set_class_hint(x11_display, x11_window);
- XMapWindow(x11_display, x11_window);
- XFlush(x11_display);
-
- XSync(x11_display, False);
- //XSetErrorHandler(oldHandler);
-
- XFree(visualInfo);
-
- // Init context and rendering device
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- if (getenv("DRI_PRIME") == NULL) {
- int use_prime = -1;
-
- if (getenv("PRIMUS_DISPLAY") ||
- getenv("PRIMUS_libGLd") ||
- getenv("PRIMUS_libGLa") ||
- getenv("PRIMUS_libGL") ||
- getenv("PRIMUS_LOAD_GLOBAL") ||
- getenv("BUMBLEBEE_SOCKET")) {
-
- print_verbose("Optirun/primusrun detected. Skipping GPU detection");
- use_prime = 0;
- }
-
- if (getenv("LD_LIBRARY_PATH")) {
- String ld_library_path(getenv("LD_LIBRARY_PATH"));
- Vector<String> libraries = ld_library_path.split(":");
+ return false;
+}
+String DisplayServerX11::get_name() const {
+ return "X11";
+}
- for (int i = 0; i < libraries.size(); ++i) {
- if (FileAccess::exists(libraries[i] + "/libGL.so.1") ||
- FileAccess::exists(libraries[i] + "/libGL.so")) {
+void DisplayServerX11::alert(const String &p_alert, const String &p_title) {
+ const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" };
- print_verbose("Custom libGL override detected. Skipping GPU detection");
- use_prime = 0;
- }
- }
- }
+ String path = OS::get_singleton()->get_environment("PATH");
+ Vector<String> path_elems = path.split(":", false);
+ String program;
- if (use_prime == -1) {
- print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic.");
- use_prime = detect_prime();
- }
+ for (int i = 0; i < path_elems.size(); i++) {
+ for (uint64_t k = 0; k < sizeof(message_programs) / sizeof(char *); k++) {
+ String tested_path = path_elems[i].plus_file(message_programs[k]);
- if (use_prime) {
- print_line("Found discrete GPU, setting DRI_PRIME=1 to use it.");
- print_line("Note: Set DRI_PRIME=0 in the environment to disable Godot from using the discrete GPU.");
- setenv("DRI_PRIME", "1", 1);
+ if (FileAccess::exists(tested_path)) {
+ program = tested_path;
+ break;
}
}
- ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE;
-
- context_gles2 = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, opengl_api_type));
-
- if (context_gles2->initialize() != OK) {
- memdelete(context_gles2);
- context_gles2 = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
-
- context_gles2->set_use_vsync(current_videomode.use_vsync);
-
- if (RasterizerGLES2::is_viable() == OK) {
- RasterizerGLES2::register_config();
- RasterizerGLES2::make_current();
- } else {
- memdelete(context_gles2);
- context_gles2 = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
-
- context_vulkan = memnew(VulkanContextX11);
- if (context_vulkan->initialize() != OK) {
- memdelete(context_vulkan);
- context_vulkan = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
- if (context_vulkan->window_create(x11_window, x11_display, get_video_mode().width, get_video_mode().height) == -1) {
- memdelete(context_vulkan);
- context_vulkan = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
-
- //temporary
- rendering_device_vulkan = memnew(RenderingDeviceVulkan);
- rendering_device_vulkan->initialize(context_vulkan);
-
- RasterizerRD::make_current();
- }
-#endif
-
- visual_server = memnew(VisualServerRaster);
- if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
- visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
+ if (program.length())
+ break;
}
- if (current_videomode.maximized) {
- current_videomode.maximized = false;
- set_window_maximized(true);
- // borderless fullscreen window mode
- } else if (current_videomode.fullscreen) {
- current_videomode.fullscreen = false;
- set_window_fullscreen(true);
- } else if (current_videomode.borderless_window) {
- Hints hints;
- Atom property;
- hints.flags = 2;
- hints.decorations = 0;
- property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
- }
+ List<String> args;
- // make PID known to X11
- {
- const long pid = this->get_process_id();
- Atom net_wm_pid = XInternAtom(x11_display, "_NET_WM_PID", False);
- XChangeProperty(x11_display, x11_window, net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
+ if (program.ends_with("zenity")) {
+ args.push_back("--error");
+ args.push_back("--width");
+ args.push_back("500");
+ args.push_back("--title");
+ args.push_back(p_title);
+ args.push_back("--text");
+ args.push_back(p_alert);
}
- // disable resizable window
- if (!current_videomode.resizable && !current_videomode.fullscreen) {
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- xsh->flags = PMinSize | PMaxSize;
- XWindowAttributes xwa;
- if (current_videomode.fullscreen) {
- XGetWindowAttributes(x11_display, DefaultRootWindow(x11_display), &xwa);
- } else {
- XGetWindowAttributes(x11_display, x11_window, &xwa);
- }
- xsh->min_width = xwa.width;
- xsh->max_width = xwa.width;
- xsh->min_height = xwa.height;
- xsh->max_height = xwa.height;
- XSetWMNormalHints(x11_display, x11_window, xsh);
- XFree(xsh);
+ if (program.ends_with("kdialog")) {
+ args.push_back("--error");
+ args.push_back(p_alert);
+ args.push_back("--title");
+ args.push_back(p_title);
}
- if (current_videomode.always_on_top) {
- current_videomode.always_on_top = false;
- set_window_always_on_top(true);
+ if (program.ends_with("Xdialog")) {
+ args.push_back("--title");
+ args.push_back(p_title);
+ args.push_back("--msgbox");
+ args.push_back(p_alert);
+ args.push_back("0");
+ args.push_back("0");
}
- ERR_FAIL_COND_V(!visual_server, ERR_UNAVAILABLE);
- ERR_FAIL_COND_V(x11_window == 0, ERR_UNAVAILABLE);
-
- XSetWindowAttributes new_attr;
-
- new_attr.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask |
- ButtonReleaseMask | EnterWindowMask |
- LeaveWindowMask | PointerMotionMask |
- Button1MotionMask |
- Button2MotionMask | Button3MotionMask |
- Button4MotionMask | Button5MotionMask |
- ButtonMotionMask | KeymapStateMask |
- ExposureMask | VisibilityChangeMask |
- StructureNotifyMask |
- SubstructureNotifyMask | SubstructureRedirectMask |
- FocusChangeMask | PropertyChangeMask |
- ColormapChangeMask | OwnerGrabButtonMask |
- im_event_mask;
-
- XChangeWindowAttributes(x11_display, x11_window, CWEventMask, &new_attr);
-
- static unsigned char all_mask_data[XIMaskLen(XI_LASTEVENT)] = {};
- static unsigned char all_master_mask_data[XIMaskLen(XI_LASTEVENT)] = {};
-
- xi.all_event_mask.deviceid = XIAllDevices;
- xi.all_event_mask.mask_len = sizeof(all_mask_data);
- xi.all_event_mask.mask = all_mask_data;
-
- xi.all_master_event_mask.deviceid = XIAllMasterDevices;
- xi.all_master_event_mask.mask_len = sizeof(all_master_mask_data);
- xi.all_master_event_mask.mask = all_master_mask_data;
-
- XISetMask(xi.all_event_mask.mask, XI_HierarchyChanged);
- XISetMask(xi.all_master_event_mask.mask, XI_DeviceChanged);
- XISetMask(xi.all_master_event_mask.mask, XI_RawMotion);
-
-#ifdef TOUCH_ENABLED
- if (xi.touch_devices.size()) {
- XISetMask(xi.all_event_mask.mask, XI_TouchBegin);
- XISetMask(xi.all_event_mask.mask, XI_TouchUpdate);
- XISetMask(xi.all_event_mask.mask, XI_TouchEnd);
- XISetMask(xi.all_event_mask.mask, XI_TouchOwnership);
+ if (program.ends_with("xmessage")) {
+ args.push_back("-center");
+ args.push_back("-title");
+ args.push_back(p_title);
+ args.push_back(p_alert);
}
-#endif
-
- XISelectEvents(x11_display, x11_window, &xi.all_event_mask, 1);
- XISelectEvents(x11_display, DefaultRootWindow(x11_display), &xi.all_master_event_mask, 1);
-
- // Disabled by now since grabbing also blocks mouse events
- // (they are received as extended events instead of standard events)
- /*XIClearMask(xi.touch_event_mask.mask, XI_TouchOwnership);
-
- // Grab touch devices to avoid OS gesture interference
- for (int i = 0; i < xi.touch_devices.size(); ++i) {
- XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask);
- }*/
-
- /* set the titlebar name */
- XStoreName(x11_display, x11_window, "Godot");
-
- im_active = false;
- im_position = Vector2();
- if (xim && xim_style) {
-
- xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, x11_window, XNFocusWindow, x11_window, (char *)NULL);
- if (XGetICValues(xic, XNFilterEvents, &im_event_mask, NULL) != NULL) {
- WARN_PRINT("XGetICValues couldn't obtain XNFilterEvents value");
- XDestroyIC(xic);
- xic = NULL;
- }
- if (xic) {
- XUnsetICFocus(xic);
- } else {
- WARN_PRINT("XCreateIC couldn't create xic");
- }
+ if (program.length()) {
+ OS::get_singleton()->execute(program, args, true);
} else {
-
- xic = NULL;
- WARN_PRINT("XCreateIC couldn't create xic");
- }
-
- cursor_size = XcursorGetDefaultSize(x11_display);
- cursor_theme = XcursorGetTheme(x11_display);
-
- if (!cursor_theme) {
- print_verbose("XcursorGetTheme could not get cursor theme");
- cursor_theme = "default";
- }
-
- for (int i = 0; i < CURSOR_MAX; i++) {
-
- cursors[i] = None;
- img[i] = NULL;
- }
-
- current_cursor = CURSOR_ARROW;
-
- for (int i = 0; i < CURSOR_MAX; i++) {
-
- static const char *cursor_file[] = {
- "left_ptr",
- "xterm",
- "hand2",
- "cross",
- "watch",
- "left_ptr_watch",
- "fleur",
- "dnd-move",
- "crossed_circle",
- "v_double_arrow",
- "h_double_arrow",
- "size_bdiag",
- "size_fdiag",
- "move",
- "row_resize",
- "col_resize",
- "question_arrow"
- };
-
- img[i] = XcursorLibraryLoadImage(cursor_file[i], cursor_theme, cursor_size);
- if (!img[i]) {
- const char *fallback = NULL;
-
- switch (i) {
- case CURSOR_POINTING_HAND:
- fallback = "pointer";
- break;
- case CURSOR_CROSS:
- fallback = "crosshair";
- break;
- case CURSOR_WAIT:
- fallback = "wait";
- break;
- case CURSOR_BUSY:
- fallback = "progress";
- break;
- case CURSOR_DRAG:
- fallback = "grabbing";
- break;
- case CURSOR_CAN_DROP:
- fallback = "hand1";
- break;
- case CURSOR_FORBIDDEN:
- fallback = "forbidden";
- break;
- case CURSOR_VSIZE:
- fallback = "ns-resize";
- break;
- case CURSOR_HSIZE:
- fallback = "ew-resize";
- break;
- case CURSOR_BDIAGSIZE:
- fallback = "fd_double_arrow";
- break;
- case CURSOR_FDIAGSIZE:
- fallback = "bd_double_arrow";
- break;
- case CURSOR_MOVE:
- img[i] = img[CURSOR_DRAG];
- break;
- case CURSOR_VSPLIT:
- fallback = "sb_v_double_arrow";
- break;
- case CURSOR_HSPLIT:
- fallback = "sb_h_double_arrow";
- break;
- case CURSOR_HELP:
- fallback = "help";
- break;
- }
- if (fallback != NULL) {
- img[i] = XcursorLibraryLoadImage(fallback, cursor_theme, cursor_size);
- }
- }
- if (img[i]) {
- cursors[i] = XcursorImageLoadCursor(x11_display, img[i]);
- } else {
- print_verbose("Failed loading custom cursor: " + String(cursor_file[i]));
- }
- }
-
- {
- // Creating an empty/transparent cursor
-
- // Create 1x1 bitmap
- Pixmap cursormask = XCreatePixmap(x11_display,
- RootWindow(x11_display, DefaultScreen(x11_display)), 1, 1, 1);
-
- // Fill with zero
- XGCValues xgc;
- xgc.function = GXclear;
- GC gc = XCreateGC(x11_display, cursormask, GCFunction, &xgc);
- XFillRectangle(x11_display, cursormask, gc, 0, 0, 1, 1);
-
- // 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);
-
- if (cursor == None) {
- ERR_PRINT("FAILED CREATING CURSOR");
- }
-
- null_cursor = cursor;
+ print_line(p_alert);
}
- set_cursor_shape(CURSOR_BUSY);
-
- //Set Xdnd (drag & drop) support
- Atom XdndAware = XInternAtom(x11_display, "XdndAware", False);
- Atom version = 5;
- XChangeProperty(x11_display, x11_window, XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&version, 1);
-
- xdnd_enter = XInternAtom(x11_display, "XdndEnter", False);
- xdnd_position = XInternAtom(x11_display, "XdndPosition", False);
- xdnd_status = XInternAtom(x11_display, "XdndStatus", False);
- xdnd_action_copy = XInternAtom(x11_display, "XdndActionCopy", False);
- xdnd_drop = XInternAtom(x11_display, "XdndDrop", False);
- xdnd_finished = XInternAtom(x11_display, "XdndFinished", False);
- xdnd_selection = XInternAtom(x11_display, "XdndSelection", False);
- requested = None;
-
- visual_server->init();
-
- AudioDriverManager::initialize(p_audio_driver);
+}
- input = memnew(InputDefault);
+void DisplayServerX11::_update_real_mouse_position(const WindowData &wd) {
+ Window root_return, child_return;
+ int root_x, root_y, win_x, win_y;
+ unsigned int mask_return;
- window_has_focus = true; // Set focus to true at init
-#ifdef JOYDEV_ENABLED
- joypad = memnew(JoypadLinux(input));
-#endif
- _ensure_user_data_dir();
+ Bool xquerypointer_result = XQueryPointer(x11_display, wd.x11_window, &root_return, &child_return, &root_x, &root_y,
+ &win_x, &win_y, &mask_return);
- if (p_desired.layered) {
- set_window_per_pixel_transparency_enabled(true);
- }
+ if (xquerypointer_result) {
+ if (win_x > 0 && win_y > 0 && win_x <= wd.size.width && win_y <= wd.size.height) {
- XEvent xevent;
- while (XPending(x11_display) > 0) {
- XNextEvent(x11_display, &xevent);
- if (xevent.type == ConfigureNotify) {
- _window_changed(&xevent);
+ last_mouse_pos.x = win_x;
+ last_mouse_pos.y = win_y;
+ last_mouse_pos_valid = true;
+ InputFilter::get_singleton()->set_mouse_position(last_mouse_pos);
}
}
-
- update_real_mouse_position();
-
- return OK;
}
-bool OS_X11::refresh_device_info() {
+bool DisplayServerX11::_refresh_device_info() {
int event_base, error_base;
print_verbose("XInput: Refreshing devices.");
@@ -786,136 +323,35 @@ bool OS_X11::refresh_device_info() {
return true;
}
-void OS_X11::xim_destroy_callback(::XIM im, ::XPointer client_data,
- ::XPointer call_data) {
-
- WARN_PRINT("Input method stopped");
- OS_X11 *os = reinterpret_cast<OS_X11 *>(client_data);
- os->xim = NULL;
- 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;
-
- ::XPoint spot;
- spot.x = short(p_pos.x);
- spot.y = short(p_pos.y);
- XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
- XSetICValues(xic, XNPreeditAttributes, preedit_attr, NULL);
- XFree(preedit_attr);
-}
+void DisplayServerX11::_flush_mouse_motion() {
+ while (true) {
+ if (XPending(x11_display) > 0) {
+ XEvent event;
+ XPeekEvent(x11_display, &event);
-String OS_X11::get_unique_id() const {
+ if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
+ XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
- static String machine_id;
- if (machine_id.empty()) {
- if (FileAccess *f = FileAccess::open("/etc/machine-id", FileAccess::READ)) {
- while (machine_id.empty() && !f->eof_reached()) {
- machine_id = f->get_line().strip_edges();
+ if (event_data->evtype == XI_RawMotion) {
+ XNextEvent(x11_display, &event);
+ } else {
+ break;
+ }
+ } else {
+ break;
}
- f->close();
- memdelete(f);
- }
- }
- return machine_id;
-}
-
-void OS_X11::finalize() {
-
- if (main_loop)
- memdelete(main_loop);
- main_loop = NULL;
-
- /*
- if (debugger_connection_console) {
- memdelete(debugger_connection_console);
- }
- */
-#ifdef ALSAMIDI_ENABLED
- driver_alsamidi.close();
-#endif
-
-#ifdef JOYDEV_ENABLED
- memdelete(joypad);
-#endif
-
- xi.touch_devices.clear();
- xi.state.clear();
-
- memdelete(input);
-
- cursors_cache.clear();
- visual_server->finish();
- memdelete(visual_server);
-
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
-
- if (context_gles2)
- memdelete(context_gles2);
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
-
- if (rendering_device_vulkan) {
- rendering_device_vulkan->finalize();
- memdelete(rendering_device_vulkan);
+ } else {
+ break;
}
-
- if (context_vulkan)
- memdelete(context_vulkan);
- }
-#endif
-
- if (xrandr_handle)
- dlclose(xrandr_handle);
-
- XUnmapWindow(x11_display, x11_window);
- XDestroyWindow(x11_display, x11_window);
-
- for (int i = 0; i < CURSOR_MAX; i++) {
- if (cursors[i] != None)
- XFreeCursor(x11_display, cursors[i]);
- if (img[i] != NULL)
- XcursorImageDestroy(img[i]);
- };
-
- if (xic) {
- XDestroyIC(xic);
}
- if (xim) {
- XCloseIM(xim);
- }
-
- XCloseDisplay(x11_display);
- if (xmbstring)
- memfree(xmbstring);
- args.clear();
+ xi.relative_motion.x = 0;
+ xi.relative_motion.y = 0;
}
-void OS_X11::set_mouse_mode(MouseMode p_mode) {
+void DisplayServerX11::mouse_set_mode(MouseMode p_mode) {
+
+ _THREAD_SAFE_METHOD_
if (p_mode == mouse_mode)
return;
@@ -926,34 +362,37 @@ void OS_X11::set_mouse_mode(MouseMode p_mode) {
// The only modes that show a cursor are VISIBLE and CONFINED
bool showCursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED);
- if (showCursor) {
- XDefineCursor(x11_display, x11_window, cursors[current_cursor]); // show cursor
- } else {
- XDefineCursor(x11_display, x11_window, null_cursor); // hide cursor
- }
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (showCursor) {
+ XDefineCursor(x11_display, E->get().x11_window, cursors[current_cursor]); // show cursor
+ } else {
+ XDefineCursor(x11_display, E->get().x11_window, null_cursor); // hide cursor
+ }
+ }
mouse_mode = p_mode;
if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) {
//flush pending motion events
- flush_mouse_motion();
+ _flush_mouse_motion();
+ WindowData &main_window = windows[MAIN_WINDOW_ID];
if (XGrabPointer(
- x11_display, x11_window, True,
+ x11_display, main_window.x11_window, True,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
- GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime) != GrabSuccess) {
+ GrabModeAsync, GrabModeAsync, windows[MAIN_WINDOW_ID].x11_window, None, CurrentTime) != GrabSuccess) {
ERR_PRINT("NO GRAB");
}
if (mouse_mode == MOUSE_MODE_CAPTURED) {
- center.x = current_videomode.width / 2;
- center.y = current_videomode.height / 2;
+ center.x = main_window.size.width / 2;
+ center.y = main_window.size.height / 2;
- XWarpPointer(x11_display, None, x11_window,
+ XWarpPointer(x11_display, None, main_window.x11_window,
0, 0, 0, 0, (int)center.x, (int)center.y);
- input->set_mouse_position(center);
+ InputFilter::get_singleton()->set_mouse_position(center);
}
} else {
do_mouse_warp = false;
@@ -961,8 +400,13 @@ void OS_X11::set_mouse_mode(MouseMode p_mode) {
XFlush(x11_display);
}
+DisplayServerX11::MouseMode DisplayServerX11::mouse_get_mode() const {
+ return mouse_mode;
+}
-void OS_X11::warp_mouse_position(const Point2 &p_to) {
+void DisplayServerX11::mouse_warp_to_position(const Point2i &p_to) {
+
+ _THREAD_SAFE_METHOD_
if (mouse_mode == MOUSE_MODE_CAPTURED) {
@@ -973,184 +417,133 @@ void OS_X11::warp_mouse_position(const Point2 &p_to) {
XGetWindowAttributes(x11_display, x11_window, &xwa);
printf("%d %d\n", xwa.x, xwa.y); needed? */
- XWarpPointer(x11_display, None, x11_window,
+ XWarpPointer(x11_display, None, windows[MAIN_WINDOW_ID].x11_window,
0, 0, 0, 0, (int)p_to.x, (int)p_to.y);
}
}
-void OS_X11::flush_mouse_motion() {
- while (true) {
- if (XPending(x11_display) > 0) {
- XEvent event;
- XPeekEvent(x11_display, &event);
+Point2i DisplayServerX11::mouse_get_position() const {
+ return last_mouse_pos;
+}
- if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) {
- XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data;
+Point2i DisplayServerX11::mouse_get_absolute_position() const {
+ int number_of_screens = XScreenCount(x11_display);
+ for (int i = 0; i < number_of_screens; i++) {
+ Window root, child;
+ int root_x, root_y, win_x, win_y;
+ unsigned int mask;
+ if (XQueryPointer(x11_display, XRootWindow(x11_display, i), &root, &child, &root_x, &root_y, &win_x, &win_y, &mask)) {
+ XWindowAttributes root_attrs;
+ XGetWindowAttributes(x11_display, root, &root_attrs);
- if (event_data->evtype == XI_RawMotion) {
- XNextEvent(x11_display, &event);
- } else {
- break;
- }
- } else {
- break;
- }
- } else {
- break;
+ return Vector2i(root_attrs.x + root_x, root_attrs.y + root_y);
}
}
-
- xi.relative_motion.x = 0;
- xi.relative_motion.y = 0;
-}
-
-OS::MouseMode OS_X11::get_mouse_mode() const {
- return mouse_mode;
+ return Vector2i();
}
-int OS_X11::get_mouse_button_state() const {
+int DisplayServerX11::mouse_get_button_state() const {
return last_button_state;
}
-Point2 OS_X11::get_mouse_position() const {
- return last_mouse_pos;
-}
+void DisplayServerX11::clipboard_set(const String &p_text) {
-bool OS_X11::get_window_per_pixel_transparency_enabled() const {
+ _THREAD_SAFE_METHOD_
- if (!is_layered_allowed()) return false;
- return layered_window;
+ internal_clipboard = p_text;
+ XSetSelectionOwner(x11_display, XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
+ XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window, CurrentTime);
}
-void OS_X11::set_window_per_pixel_transparency_enabled(bool p_enabled) {
+static String _clipboard_get_impl(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard, Atom target) {
- if (!is_layered_allowed()) return;
- if (layered_window != p_enabled) {
- if (p_enabled) {
- set_borderless_window(true);
- layered_window = true;
- } else {
- layered_window = false;
- }
- }
-}
-
-void OS_X11::set_window_title(const String &p_title) {
- XStoreName(x11_display, x11_window, p_title.utf8().get_data());
+ String ret;
- Atom _net_wm_name = XInternAtom(x11_display, "_NET_WM_NAME", false);
- Atom utf8_string = XInternAtom(x11_display, "UTF8_STRING", false);
- XChangeProperty(x11_display, x11_window, _net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)p_title.utf8().get_data(), p_title.utf8().length());
-}
+ Atom type;
+ Atom selection = XA_PRIMARY;
+ int format, result;
+ unsigned long len, bytes_left, dummy;
+ unsigned char *data;
+ Window Sown = XGetSelectionOwner(x11_display, p_source);
-void OS_X11::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
-}
+ if (Sown == x11_window) {
-OS::VideoMode OS_X11::get_video_mode(int p_screen) const {
- return current_videomode;
-}
+ return p_internal_clipboard;
+ };
-void OS_X11::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
-}
+ if (Sown != None) {
+ XConvertSelection(x11_display, p_source, target, selection,
+ x11_window, CurrentTime);
+ XFlush(x11_display);
+ while (true) {
+ XEvent event;
+ XNextEvent(x11_display, &event);
+ if (event.type == SelectionNotify && event.xselection.requestor == x11_window) {
+ break;
+ };
+ };
-void OS_X11::set_wm_fullscreen(bool p_enabled) {
- if (p_enabled && !get_borderless_window()) {
- // remove decorations if the window is not already borderless
- Hints hints;
- Atom property;
- hints.flags = 2;
- hints.decorations = 0;
- property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ //
+ // Do not get any data, see how much data is there
+ //
+ XGetWindowProperty(x11_display, x11_window,
+ selection, // Tricky..
+ 0, 0, // offset - len
+ 0, // Delete 0==FALSE
+ AnyPropertyType, //flag
+ &type, // return type
+ &format, // return format
+ &len, &bytes_left, //that
+ &data);
+ // DATA is There
+ if (bytes_left > 0) {
+ result = XGetWindowProperty(x11_display, x11_window,
+ selection, 0, bytes_left, 0,
+ AnyPropertyType, &type, &format,
+ &len, &dummy, &data);
+ if (result == Success) {
+ ret.parse_utf8((const char *)data);
+ } else
+ printf("FAIL\n");
+ if (data) {
+ XFree(data);
+ }
+ }
}
- if (p_enabled && !is_window_resizable()) {
- // Set the window as resizable to prevent window managers to ignore the fullscreen state flag.
- XSizeHints *xsh;
+ return ret;
+}
- xsh = XAllocSizeHints();
- xsh->flags = 0L;
- XSetWMNormalHints(x11_display, x11_window, xsh);
- XFree(xsh);
+static String _clipboard_get(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard) {
+ String ret;
+ Atom utf8_atom = XInternAtom(x11_display, "UTF8_STRING", True);
+ if (utf8_atom != None) {
+ ret = _clipboard_get_impl(p_source, x11_window, x11_display, p_internal_clipboard, utf8_atom);
}
+ if (ret == "") {
+ ret = _clipboard_get_impl(p_source, x11_window, x11_display, p_internal_clipboard, XA_STRING);
+ }
+ return ret;
+}
- // Using EWMH -- Extended Window Manager Hints
- XEvent xev;
- Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
- Atom wm_fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False);
-
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = x11_window;
- xev.xclient.message_type = wm_state;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
- xev.xclient.data.l[1] = wm_fullscreen;
- xev.xclient.data.l[2] = 0;
-
- XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+String DisplayServerX11::clipboard_get() const {
- // set bypass compositor hint
- Atom bypass_compositor = XInternAtom(x11_display, "_NET_WM_BYPASS_COMPOSITOR", False);
- unsigned long compositing_disable_on = p_enabled ? 1 : 0;
- XChangeProperty(x11_display, x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1);
+ _THREAD_SAFE_METHOD_
- XFlush(x11_display);
+ String ret;
+ ret = _clipboard_get(XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window, x11_display, internal_clipboard);
- if (!p_enabled) {
- // Reset the non-resizable flags if we un-set these before.
- Size2 size = get_window_size();
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- if (!is_window_resizable()) {
- xsh->flags = PMinSize | PMaxSize;
- xsh->min_width = size.x;
- xsh->max_width = size.x;
- xsh->min_height = size.y;
- xsh->max_height = size.y;
- } else {
- xsh->flags = 0L;
- if (min_size != Size2()) {
- xsh->flags |= PMinSize;
- xsh->min_width = min_size.x;
- xsh->min_height = min_size.y;
- }
- if (max_size != Size2()) {
- xsh->flags |= PMaxSize;
- xsh->max_width = max_size.x;
- xsh->max_height = max_size.y;
- }
- }
- XSetWMNormalHints(x11_display, x11_window, xsh);
- XFree(xsh);
+ if (ret == "") {
+ ret = _clipboard_get(XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window, x11_display, internal_clipboard);
+ };
- // put back or remove decorations according to the last set borderless state
- Hints hints;
- Atom property;
- hints.flags = 2;
- 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);
- }
+ return ret;
}
-void OS_X11::set_wm_above(bool p_enabled) {
- Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
- Atom wm_above = XInternAtom(x11_display, "_NET_WM_STATE_ABOVE", False);
+int DisplayServerX11::get_screen_count() const {
- XClientMessageEvent xev;
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.window = x11_window;
- xev.message_type = wm_state;
- xev.format = 32;
- xev.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
- xev.data.l[1] = wm_above;
- xev.data.l[3] = 1;
- XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xev);
-}
+ _THREAD_SAFE_METHOD_
-int OS_X11::get_screen_count() const {
// Using Xinerama Extension
int event_base, error_base;
const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base);
@@ -1161,42 +554,12 @@ int OS_X11::get_screen_count() const {
XFree(xsi);
return count;
}
+Point2i DisplayServerX11::screen_get_position(int p_screen) const {
-int OS_X11::get_current_screen() const {
- int x, y;
- Window child;
- XTranslateCoordinates(x11_display, x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child);
+ _THREAD_SAFE_METHOD_
- int count = get_screen_count();
- for (int i = 0; i < count; i++) {
- Point2i pos = get_screen_position(i);
- Size2i size = get_screen_size(i);
- if ((x >= pos.x && x < pos.x + size.width) && (y >= pos.y && y < pos.y + size.height))
- return i;
- }
- return 0;
-}
-
-void OS_X11::set_current_screen(int p_screen) {
- int count = get_screen_count();
- if (p_screen >= count) return;
-
- if (current_videomode.fullscreen) {
- Point2i position = get_screen_position(p_screen);
- Size2i size = get_screen_size(p_screen);
-
- XMoveResizeWindow(x11_display, x11_window, position.x, position.y, size.x, size.y);
- } else {
- if (p_screen != get_current_screen()) {
- Point2i position = get_screen_position(p_screen);
- XMoveWindow(x11_display, x11_window, position.x, position.y);
- }
- }
-}
-
-Point2 OS_X11::get_screen_position(int p_screen) const {
- if (p_screen == -1) {
- p_screen = get_current_screen();
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
}
// Using Xinerama Extension
@@ -1219,39 +582,48 @@ Point2 OS_X11::get_screen_position(int p_screen) const {
return position;
}
-Size2 OS_X11::get_screen_size(int p_screen) const {
- if (p_screen == -1) {
- p_screen = get_current_screen();
+Size2i DisplayServerX11::screen_get_size(int p_screen) const {
+ return screen_get_usable_rect(p_screen).size;
+}
+
+Rect2i DisplayServerX11::screen_get_usable_rect(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
}
// Using Xinerama Extension
int event_base, error_base;
const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base);
- if (!ext_okay) return Size2i(0, 0);
+ if (!ext_okay) return Rect2i(0, 0, 0, 0);
int count;
XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count);
- if (p_screen >= count) return Size2i(0, 0);
+ if (p_screen >= count) return Rect2i(0, 0, 0, 0);
- Size2i size = Point2i(xsi[p_screen].width, xsi[p_screen].height);
+ Rect2i rect = Rect2i(xsi[p_screen].x_org, xsi[p_screen].y_org, xsi[p_screen].width, xsi[p_screen].height);
XFree(xsi);
- return size;
+ return rect;
}
-int OS_X11::get_screen_dpi(int p_screen) const {
- if (p_screen == -1) {
- p_screen = get_current_screen();
+int DisplayServerX11::screen_get_dpi(int p_screen) const {
+
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
}
//invalid screen?
ERR_FAIL_INDEX_V(p_screen, get_screen_count(), 0);
//Get physical monitor Dimensions through XRandR and calculate dpi
- Size2 sc = get_screen_size(p_screen);
+ Size2i sc = screen_get_size(p_screen);
if (xrandr_ext_ok) {
int count = 0;
if (xrr_get_monitors) {
- xrr_monitor_info *monitors = xrr_get_monitors(x11_display, x11_window, true, &count);
+ xrr_monitor_info *monitors = xrr_get_monitors(x11_display, windows[MAIN_WINDOW_ID].x11_window, true, &count);
if (p_screen < count) {
double xdpi = sc.width / (double)monitors[p_screen].mwidth * 25.4;
double ydpi = sc.height / (double)monitors[p_screen].mheight * 25.4;
@@ -1279,18 +651,249 @@ int OS_X11::get_screen_dpi(int p_screen) const {
//could not get dpi
return 96;
}
+bool DisplayServerX11::screen_is_touchscreen(int p_screen) const {
+
+ _THREAD_SAFE_METHOD_
+
+#ifndef _MSC_VER
+#warning Need to get from proper window
+#endif
+
+ return DisplayServer::screen_is_touchscreen(p_screen);
+}
+
+Vector<DisplayServer::WindowID> DisplayServerX11::get_window_list() const {
+ _THREAD_SAFE_METHOD_
+
+ Vector<int> ret;
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+
+DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+
+ _THREAD_SAFE_METHOD_
+
+ WindowID id = _create_window(p_mode, p_flags, p_rect);
+ for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
+ if (p_flags & (1 << i)) {
+ window_set_flag(WindowFlags(i), true, id);
+ }
+ }
+
+ return id;
+}
+
+void DisplayServerX11::delete_sub_window(WindowID p_id) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_id));
+ ERR_FAIL_COND_MSG(p_id == MAIN_WINDOW_ID, "Main window can't be deleted"); //ma
+
+ WindowData &wd = windows[p_id];
+
+ while (wd.transient_children.size()) {
+ window_set_transient(wd.transient_children.front()->get(), INVALID_WINDOW_ID);
+ }
+
+ if (wd.transient_parent != INVALID_WINDOW_ID) {
+ window_set_transient(p_id, INVALID_WINDOW_ID);
+ }
+
+#ifdef VULKAN_ENABLED
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_destroy(p_id);
+ }
+#endif
+ XUnmapWindow(x11_display, wd.x11_window);
+ XDestroyWindow(x11_display, wd.x11_window);
+ if (wd.xic) {
+ XDestroyIC(wd.xic);
+ }
+
+ windows.erase(p_id);
+}
+
+void DisplayServerX11::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ wd.instance_id = p_instance;
+}
+
+ObjectID DisplayServerX11::window_get_attached_instance_id(WindowID p_window) const {
+
+ ERR_FAIL_COND_V(!windows.has(p_window), ObjectID());
+ const WindowData &wd = windows[p_window];
+ return wd.instance_id;
+}
+
+DisplayServerX11::WindowID DisplayServerX11::get_window_at_screen_position(const Point2i &p_position) const {
+
+ return INVALID_WINDOW_ID;
+}
+
+void DisplayServerX11::window_set_title(const String &p_title, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ XStoreName(x11_display, wd.x11_window, p_title.utf8().get_data());
+
+ Atom _net_wm_name = XInternAtom(x11_display, "_NET_WM_NAME", false);
+ Atom utf8_string = XInternAtom(x11_display, "UTF8_STRING", false);
+ XChangeProperty(x11_display, wd.x11_window, _net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)p_title.utf8().get_data(), p_title.utf8().length());
+}
+
+void DisplayServerX11::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.rect_changed_callback = p_callable;
+}
+
+void DisplayServerX11::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.event_callback = p_callable;
+}
+
+void DisplayServerX11::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.input_event_callback = p_callable;
+}
+void DisplayServerX11::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.input_text_callback = p_callable;
+}
+
+void DisplayServerX11::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.drop_files_callback = p_callable;
+}
+
+int DisplayServerX11::window_get_current_screen(WindowID p_window) const {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), -1);
+ const WindowData &wd = windows[p_window];
-Point2 OS_X11::get_window_position() const {
int x, y;
Window child;
- XTranslateCoordinates(x11_display, x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child);
- return Point2i(x, y);
+ XTranslateCoordinates(x11_display, wd.x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child);
+
+ int count = get_screen_count();
+ for (int i = 0; i < count; i++) {
+ Point2i pos = screen_get_position(i);
+ Size2i size = screen_get_size(i);
+ if ((x >= pos.x && x < pos.x + size.width) && (y >= pos.y && y < pos.y + size.height))
+ return i;
+ }
+ return 0;
+}
+void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ int count = get_screen_count();
+ if (p_screen >= count) return;
+
+ if (window_get_mode(p_window) == WINDOW_MODE_FULLSCREEN) {
+ Point2i position = screen_get_position(p_screen);
+ Size2i size = screen_get_size(p_screen);
+
+ XMoveResizeWindow(x11_display, wd.x11_window, position.x, position.y, size.x, size.y);
+ } else {
+ if (p_screen != window_get_current_screen(p_window)) {
+ Point2i position = screen_get_position(p_screen);
+ XMoveWindow(x11_display, wd.x11_window, position.x, position.y);
+ }
+ }
+}
+
+void DisplayServerX11::window_set_transient(WindowID p_window, WindowID p_parent) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(p_window == p_parent);
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd_window = windows[p_window];
+
+ ERR_FAIL_COND(wd_window.transient_parent == p_parent);
+
+ ERR_FAIL_COND_MSG(wd_window.on_top, "Windows with the 'on top' can't become transient.");
+ if (p_parent == INVALID_WINDOW_ID) {
+ //remove transient
+
+ ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID);
+ ERR_FAIL_COND(!windows.has(wd_window.transient_parent));
+
+ WindowData &wd_parent = windows[wd_window.transient_parent];
+
+ wd_window.transient_parent = INVALID_WINDOW_ID;
+ wd_parent.transient_children.erase(p_window);
+
+ XSetTransientForHint(x11_display, wd_window.x11_window, None);
+ } else {
+ ERR_FAIL_COND(!windows.has(p_parent));
+ ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
+ WindowData &wd_parent = windows[p_parent];
+
+ wd_window.transient_parent = p_parent;
+ wd_parent.transient_children.insert(p_window);
+
+ XSetTransientForHint(x11_display, wd_window.x11_window, wd_parent.x11_window);
+ }
+}
+
+Point2i DisplayServerX11::window_get_position(WindowID p_window) const {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Point2i());
+ const WindowData &wd = windows[p_window];
+
+ return wd.position;
}
-void OS_X11::set_window_position(const Point2 &p_position) {
+void DisplayServerX11::window_set_position(const Point2i &p_position, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
int x = 0;
int y = 0;
- if (!get_borderless_window()) {
+ if (!window_get_flag(WINDOW_FLAG_BORDERLESS, p_window)) {
//exclude window decorations
XSync(x11_display, False);
Atom prop = XInternAtom(x11_display, "_NET_FRAME_EXTENTS", True);
@@ -1300,8 +903,8 @@ void OS_X11::set_window_position(const Point2 &p_position) {
unsigned long len;
unsigned long remaining;
unsigned char *data = NULL;
- if (XGetWindowProperty(x11_display, x11_window, prop, 0, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
- if (format == 32 && len == 4) {
+ if (XGetWindowProperty(x11_display, wd.x11_window, prop, 0, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
+ if (format == 32 && len == 4 && data) {
long *extents = (long *)data;
x = extents[0];
y = extents[2];
@@ -1310,153 +913,151 @@ void OS_X11::set_window_position(const Point2 &p_position) {
}
}
}
- XMoveWindow(x11_display, x11_window, p_position.x - x, p_position.y - y);
- update_real_mouse_position();
+ XMoveWindow(x11_display, wd.x11_window, p_position.x - x, p_position.y - y);
+ _update_real_mouse_position(wd);
}
-Size2 OS_X11::get_window_size() const {
- // Use current_videomode width and height instead of XGetWindowAttributes
- // since right after a XResizeWindow the attributes may not be updated yet
- return Size2i(current_videomode.width, current_videomode.height);
-}
+void DisplayServerX11::window_set_max_size(const Size2i p_size, WindowID p_window) {
-Size2 OS_X11::get_real_window_size() const {
- XWindowAttributes xwa;
- XSync(x11_display, False);
- XGetWindowAttributes(x11_display, x11_window, &xwa);
- int w = xwa.width;
- int h = xwa.height;
- Atom prop = XInternAtom(x11_display, "_NET_FRAME_EXTENTS", 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, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
- if (format == 32 && len == 4) {
- long *extents = (long *)data;
- w += extents[0] + extents[1]; // left, right
- h += extents[2] + extents[3]; // top, bottom
- }
- XFree(data);
- }
- }
- return Size2(w, h);
-}
+ _THREAD_SAFE_METHOD_
-Size2 OS_X11::get_max_window_size() const {
- return max_size;
-}
-
-Size2 OS_X11::get_min_window_size() const {
- return min_size;
-}
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
-void OS_X11::set_min_window_size(const Size2 p_size) {
-
- if ((p_size != Size2()) && (max_size != Size2()) && ((p_size.x > max_size.x) || (p_size.y > max_size.y))) {
- ERR_PRINT("Minimum window size can't be larger than maximum window size!");
+ if ((p_size != Size2i()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {
+ ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
return;
}
- min_size = p_size;
+ wd.max_size = p_size;
- if (is_window_resizable()) {
+ if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
XSizeHints *xsh;
xsh = XAllocSizeHints();
xsh->flags = 0L;
- if (min_size != Size2()) {
+ if (wd.min_size != Size2i()) {
xsh->flags |= PMinSize;
- xsh->min_width = min_size.x;
- xsh->min_height = min_size.y;
+ xsh->min_width = wd.min_size.x;
+ xsh->min_height = wd.min_size.y;
}
- if (max_size != Size2()) {
+ if (wd.max_size != Size2i()) {
xsh->flags |= PMaxSize;
- xsh->max_width = max_size.x;
- xsh->max_height = max_size.y;
+ xsh->max_width = wd.max_size.x;
+ xsh->max_height = wd.max_size.y;
}
- XSetWMNormalHints(x11_display, x11_window, xsh);
+ XSetWMNormalHints(x11_display, wd.x11_window, xsh);
XFree(xsh);
XFlush(x11_display);
}
}
+Size2i DisplayServerX11::window_get_max_size(WindowID p_window) const {
-void OS_X11::set_max_window_size(const Size2 p_size) {
+ _THREAD_SAFE_METHOD_
- if ((p_size != Size2()) && ((p_size.x < min_size.x) || (p_size.y < min_size.y))) {
- ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+
+ return wd.max_size;
+}
+
+void DisplayServerX11::window_set_min_size(const Size2i p_size, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if ((p_size != Size2i()) && (wd.max_size != Size2i()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {
+ ERR_PRINT("Minimum window size can't be larger than maximum window size!");
return;
}
- max_size = p_size;
+ wd.min_size = p_size;
- if (is_window_resizable()) {
+ if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
XSizeHints *xsh;
xsh = XAllocSizeHints();
xsh->flags = 0L;
- if (min_size != Size2()) {
+ if (wd.min_size != Size2i()) {
xsh->flags |= PMinSize;
- xsh->min_width = min_size.x;
- xsh->min_height = min_size.y;
+ xsh->min_width = wd.min_size.x;
+ xsh->min_height = wd.min_size.y;
}
- if (max_size != Size2()) {
+ if (wd.max_size != Size2i()) {
xsh->flags |= PMaxSize;
- xsh->max_width = max_size.x;
- xsh->max_height = max_size.y;
+ xsh->max_width = wd.max_size.x;
+ xsh->max_height = wd.max_size.y;
}
- XSetWMNormalHints(x11_display, x11_window, xsh);
+ XSetWMNormalHints(x11_display, wd.x11_window, xsh);
XFree(xsh);
XFlush(x11_display);
}
}
+Size2i DisplayServerX11::window_get_min_size(WindowID p_window) const {
+
+ _THREAD_SAFE_METHOD_
-void OS_X11::set_window_size(const Size2 p_size) {
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+
+ return wd.min_size;
+}
- if (current_videomode.width == p_size.width && current_videomode.height == p_size.height)
+void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+
+ Size2i size = p_size;
+ size.x = MAX(1, size.x);
+ size.y = MAX(1, size.y);
+
+ WindowData &wd = windows[p_window];
+
+ if (wd.size.width == size.width && wd.size.height == size.height)
return;
XWindowAttributes xwa;
XSync(x11_display, False);
- XGetWindowAttributes(x11_display, x11_window, &xwa);
+ XGetWindowAttributes(x11_display, wd.x11_window, &xwa);
int old_w = xwa.width;
int old_h = xwa.height;
// If window resizable is disabled we need to update the attributes first
XSizeHints *xsh;
xsh = XAllocSizeHints();
- if (!is_window_resizable()) {
+ if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
xsh->flags = PMinSize | PMaxSize;
- xsh->min_width = p_size.x;
- xsh->max_width = p_size.x;
- xsh->min_height = p_size.y;
- xsh->max_height = p_size.y;
+ xsh->min_width = size.x;
+ xsh->max_width = size.x;
+ xsh->min_height = size.y;
+ xsh->max_height = size.y;
} else {
xsh->flags = 0L;
- if (min_size != Size2()) {
+ if (wd.min_size != Size2i()) {
xsh->flags |= PMinSize;
- xsh->min_width = min_size.x;
- xsh->min_height = min_size.y;
+ xsh->min_width = wd.min_size.x;
+ xsh->min_height = wd.min_size.y;
}
- if (max_size != Size2()) {
+ if (wd.max_size != Size2i()) {
xsh->flags |= PMaxSize;
- xsh->max_width = max_size.x;
- xsh->max_height = max_size.y;
+ xsh->max_width = wd.max_size.x;
+ xsh->max_height = wd.max_size.y;
}
}
- XSetWMNormalHints(x11_display, x11_window, xsh);
+ XSetWMNormalHints(x11_display, wd.x11_window, xsh);
XFree(xsh);
// Resize the window
- XResizeWindow(x11_display, x11_window, p_size.x, p_size.y);
+ XResizeWindow(x11_display, wd.x11_window, size.x, size.y);
// Update our videomode width and height
- current_videomode.width = p_size.x;
- current_videomode.height = p_size.y;
+ wd.size = size;
for (int timeout = 0; timeout < 50; ++timeout) {
XSync(x11_display, False);
- XGetWindowAttributes(x11_display, x11_window, &xwa);
+ XGetWindowAttributes(x11_display, wd.x11_window, &xwa);
if (old_w != xwa.width || old_h != xwa.height)
break;
@@ -1464,105 +1065,53 @@ void OS_X11::set_window_size(const Size2 p_size) {
usleep(10000);
}
}
+Size2i DisplayServerX11::window_get_size(WindowID p_window) const {
-void OS_X11::set_window_fullscreen(bool p_enabled) {
-
- if (current_videomode.fullscreen == p_enabled)
- return;
+ _THREAD_SAFE_METHOD_
- if (layered_window)
- set_window_per_pixel_transparency_enabled(false);
-
- if (p_enabled && current_videomode.always_on_top) {
- // Fullscreen + Always-on-top requires a maximized window on some window managers (Metacity)
- set_window_maximized(true);
- }
- set_wm_fullscreen(p_enabled);
- if (!p_enabled && current_videomode.always_on_top) {
- // Restore
- set_window_maximized(false);
- }
- if (!p_enabled) {
- set_window_position(last_position_before_fs);
- } else {
- last_position_before_fs = get_window_position();
- }
- current_videomode.fullscreen = p_enabled;
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+ return wd.size;
}
+Size2i DisplayServerX11::window_get_real_size(WindowID p_window) const {
-bool OS_X11::is_window_fullscreen() const {
- return current_videomode.fullscreen;
-}
+ _THREAD_SAFE_METHOD_
-void OS_X11::set_window_resizable(bool p_enabled) {
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
- XSizeHints *xsh;
- xsh = XAllocSizeHints();
- if (!p_enabled) {
- Size2 size = get_window_size();
-
- xsh->flags = PMinSize | PMaxSize;
- xsh->min_width = size.x;
- xsh->max_width = size.x;
- xsh->min_height = size.y;
- xsh->max_height = size.y;
- } else {
- xsh->flags = 0L;
- if (min_size != Size2()) {
- xsh->flags |= PMinSize;
- xsh->min_width = min_size.x;
- xsh->min_height = min_size.y;
- }
- if (max_size != Size2()) {
- xsh->flags |= PMaxSize;
- xsh->max_width = max_size.x;
- xsh->max_height = max_size.y;
+ XWindowAttributes xwa;
+ XSync(x11_display, False);
+ XGetWindowAttributes(x11_display, wd.x11_window, &xwa);
+ int w = xwa.width;
+ int h = xwa.height;
+ Atom prop = XInternAtom(x11_display, "_NET_FRAME_EXTENTS", True);
+ if (prop != None) {
+ Atom type;
+ int format;
+ unsigned long len;
+ unsigned long remaining;
+ unsigned char *data = NULL;
+ if (XGetWindowProperty(x11_display, wd.x11_window, prop, 0, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
+ if (format == 32 && len == 4 && data) {
+ long *extents = (long *)data;
+ w += extents[0] + extents[1]; // left, right
+ h += extents[2] + extents[3]; // top, bottom
+ }
+ XFree(data);
}
}
-
- XSetWMNormalHints(x11_display, x11_window, xsh);
- XFree(xsh);
-
- current_videomode.resizable = p_enabled;
-
- XFlush(x11_display);
-}
-
-bool OS_X11::is_window_resizable() const {
- return current_videomode.resizable;
+ return Size2i(w, h);
}
-void OS_X11::set_window_minimized(bool p_enabled) {
- // Using ICCCM -- Inter-Client Communication Conventions Manual
- XEvent xev;
- Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False);
-
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = x11_window;
- xev.xclient.message_type = wm_change;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = p_enabled ? WM_IconicState : WM_NormalState;
-
- XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
-
- Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
- Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False);
+bool DisplayServerX11::window_is_maximize_allowed(WindowID p_window) const {
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = x11_window;
- xev.xclient.message_type = wm_state;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
- xev.xclient.data.l[1] = wm_hidden;
+ _THREAD_SAFE_METHOD_
- XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
-}
+ ERR_FAIL_COND_V(!windows.has(p_window), false);
+ const WindowData &wd = windows[p_window];
-bool OS_X11::is_window_minimized() const {
- // Using ICCCM -- Inter-Client Communication Conventions Manual
- Atom property = XInternAtom(x11_display, "WM_STATE", True);
+ Atom property = XInternAtom(x11_display, "_NET_WM_ALLOWED_ACTIONS", False);
Atom type;
int format;
unsigned long len;
@@ -1571,29 +1120,44 @@ bool OS_X11::is_window_minimized() const {
int result = XGetWindowProperty(
x11_display,
- x11_window,
+ wd.x11_window,
property,
0,
- 32,
+ 1024,
False,
- AnyPropertyType,
+ XA_ATOM,
&type,
&format,
&len,
&remaining,
&data);
- if (result == Success) {
- long *state = (long *)data;
- if (state[0] == WM_IconicState)
- return true;
+ if (result == Success && data) {
+ Atom *atoms = (Atom *)data;
+ Atom wm_act_max_horz = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_HORZ", False);
+ Atom wm_act_max_vert = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_VERT", False);
+ bool found_wm_act_max_horz = false;
+ bool found_wm_act_max_vert = false;
+
+ for (uint64_t i = 0; i < len; i++) {
+ if (atoms[i] == wm_act_max_horz)
+ found_wm_act_max_horz = true;
+ if (atoms[i] == wm_act_max_vert)
+ found_wm_act_max_vert = true;
+
+ if (found_wm_act_max_horz || found_wm_act_max_vert)
+ return true;
+ }
+ XFree(atoms);
}
+
return false;
}
-void OS_X11::set_window_maximized(bool p_enabled) {
- if (is_window_maximized() == p_enabled)
- return;
+void DisplayServerX11::_set_wm_maximized(WindowID p_window, bool p_enabled) {
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
// Using EWMH -- Extended Window Manager Hints
XEvent xev;
@@ -1603,7 +1167,7 @@ void OS_X11::set_window_maximized(bool p_enabled) {
memset(&xev, 0, sizeof(xev));
xev.type = ClientMessage;
- xev.xclient.window = x11_window;
+ xev.xclient.window = wd.x11_window;
xev.xclient.message_type = wm_state;
xev.xclient.format = 32;
xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
@@ -1612,178 +1176,454 @@ void OS_X11::set_window_maximized(bool p_enabled) {
XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
- if (p_enabled && is_window_maximize_allowed()) {
+ if (p_enabled && window_is_maximize_allowed(p_window)) {
// Wait for effective resizing (so the GLX context is too).
// Give up after 0.5s, it's not going to happen on this WM.
// https://github.com/godotengine/godot/issues/19978
- for (int attempt = 0; !is_window_maximized() && attempt < 50; attempt++) {
+ for (int attempt = 0; window_get_mode(p_window) != WINDOW_MODE_MAXIMIZED && attempt < 50; attempt++) {
usleep(10000);
}
}
-
- maximized = p_enabled;
}
-bool OS_X11::is_window_maximize_allowed() {
- Atom property = XInternAtom(x11_display, "_NET_WM_ALLOWED_ACTIONS", False);
- Atom type;
- int format;
- unsigned long len;
- unsigned long remaining;
- unsigned char *data = NULL;
+void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) {
- int result = XGetWindowProperty(
- x11_display,
- x11_window,
- property,
- 0,
- 1024,
- False,
- XA_ATOM,
- &type,
- &format,
- &len,
- &remaining,
- &data);
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
- if (result == Success) {
- Atom *atoms = (Atom *)data;
- Atom wm_act_max_horz = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_HORZ", False);
- Atom wm_act_max_vert = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_VERT", False);
- bool found_wm_act_max_horz = false;
- bool found_wm_act_max_vert = false;
+ if (p_enabled && !window_get_flag(WINDOW_FLAG_BORDERLESS, p_window)) {
+ // remove decorations if the window is not already borderless
+ Hints hints;
+ Atom property;
+ hints.flags = 2;
+ hints.decorations = 0;
+ property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ }
- for (uint64_t i = 0; i < len; i++) {
- if (atoms[i] == wm_act_max_horz)
- found_wm_act_max_horz = true;
- if (atoms[i] == wm_act_max_vert)
- found_wm_act_max_vert = true;
+ if (p_enabled && window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
+ // Set the window as resizable to prevent window managers to ignore the fullscreen state flag.
+ XSizeHints *xsh;
- if (found_wm_act_max_horz || found_wm_act_max_vert)
- return true;
- }
- XFree(atoms);
+ xsh = XAllocSizeHints();
+ xsh->flags = 0L;
+ XSetWMNormalHints(x11_display, wd.x11_window, xsh);
+ XFree(xsh);
}
- return false;
-}
-
-bool OS_X11::is_window_maximized() const {
// Using EWMH -- Extended Window Manager Hints
- Atom property = XInternAtom(x11_display, "_NET_WM_STATE", False);
- Atom type;
- int format;
- unsigned long len;
- unsigned long remaining;
- unsigned char *data = NULL;
- bool retval = false;
+ XEvent xev;
+ Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
+ Atom wm_fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False);
- int result = XGetWindowProperty(
- x11_display,
- x11_window,
- property,
- 0,
- 1024,
- False,
- XA_ATOM,
- &type,
- &format,
- &len,
- &remaining,
- &data);
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.xclient.window = wd.x11_window;
+ xev.xclient.message_type = wm_state;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
+ xev.xclient.data.l[1] = wm_fullscreen;
+ xev.xclient.data.l[2] = 0;
- if (result == Success) {
- Atom *atoms = (Atom *)data;
- Atom wm_max_horz = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
- Atom wm_max_vert = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
- bool found_wm_max_horz = false;
- bool found_wm_max_vert = false;
+ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
- for (uint64_t i = 0; i < len; i++) {
- if (atoms[i] == wm_max_horz)
- found_wm_max_horz = true;
- if (atoms[i] == wm_max_vert)
- found_wm_max_vert = true;
+ // set bypass compositor hint
+ Atom bypass_compositor = XInternAtom(x11_display, "_NET_WM_BYPASS_COMPOSITOR", False);
+ unsigned long compositing_disable_on = p_enabled ? 1 : 0;
+ XChangeProperty(x11_display, wd.x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1);
- if (found_wm_max_horz && found_wm_max_vert) {
- retval = true;
- break;
+ XFlush(x11_display);
+
+ if (!p_enabled) {
+ // Reset the non-resizable flags if we un-set these before.
+ Size2i size = window_get_size(p_window);
+ XSizeHints *xsh;
+ xsh = XAllocSizeHints();
+ if (window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) {
+ xsh->flags = PMinSize | PMaxSize;
+ xsh->min_width = size.x;
+ xsh->max_width = size.x;
+ xsh->min_height = size.y;
+ xsh->max_height = size.y;
+ } else {
+ xsh->flags = 0L;
+ if (wd.min_size != Size2i()) {
+ xsh->flags |= PMinSize;
+ xsh->min_width = wd.min_size.x;
+ xsh->min_height = wd.min_size.y;
+ }
+ if (wd.max_size != Size2i()) {
+ xsh->flags |= PMaxSize;
+ xsh->max_width = wd.max_size.x;
+ xsh->max_height = wd.max_size.y;
}
}
- }
+ XSetWMNormalHints(x11_display, wd.x11_window, xsh);
+ XFree(xsh);
- XFree(data);
- return retval;
+ // put back or remove decorations according to the last set borderless state
+ Hints hints;
+ Atom property;
+ hints.flags = 2;
+ hints.decorations = window_get_flag(WINDOW_FLAG_BORDERLESS, p_window) ? 0 : 1;
+ property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+ }
}
-void OS_X11::set_window_always_on_top(bool p_enabled) {
- if (is_window_always_on_top() == p_enabled)
- return;
+void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) {
- if (p_enabled && current_videomode.fullscreen) {
- // Fullscreen + Always-on-top requires a maximized window on some window managers (Metacity)
- set_window_maximized(true);
- }
- set_wm_above(p_enabled);
- if (!p_enabled && !current_videomode.fullscreen) {
- // Restore
- set_window_maximized(false);
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ WindowMode old_mode = window_get_mode(p_window);
+ if (old_mode == p_mode) {
+ return; // do nothing
}
+ //remove all "extra" modes
- current_videomode.always_on_top = p_enabled;
-}
+ switch (old_mode) {
+ case WINDOW_MODE_WINDOWED: {
+ //do nothing
+ } break;
+ case WINDOW_MODE_MINIMIZED: {
+ //Un-Minimize
+ // Using ICCCM -- Inter-Client Communication Conventions Manual
+ XEvent xev;
+ Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False);
+
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.xclient.window = wd.x11_window;
+ xev.xclient.message_type = wm_change;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = WM_NormalState;
+
+ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+
+ Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
+ Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False);
+
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.xclient.window = wd.x11_window;
+ xev.xclient.message_type = wm_state;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
+ xev.xclient.data.l[1] = wm_hidden;
+
+ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+ } break;
+ case WINDOW_MODE_FULLSCREEN: {
+ //Remove full-screen
+ _set_wm_fullscreen(p_window, false);
-bool OS_X11::is_window_always_on_top() const {
- return current_videomode.always_on_top;
-}
+ //un-maximize required for always on top
+ bool on_top = window_get_flag(WINDOW_FLAG_ALWAYS_ON_TOP, p_window);
-bool OS_X11::is_window_focused() const {
- return window_focused;
-}
+ wd.fullscreen = false;
-void OS_X11::set_borderless_window(bool p_borderless) {
+ window_set_position(wd.last_position_before_fs, p_window);
- if (get_borderless_window() == p_borderless)
- return;
+ if (on_top) {
+ _set_wm_maximized(p_window, false);
+ }
- if (!p_borderless && layered_window)
- set_window_per_pixel_transparency_enabled(false);
+ } break;
+ case WINDOW_MODE_MAXIMIZED: {
- current_videomode.borderless_window = p_borderless;
+ _set_wm_maximized(p_window, false);
+ } break;
+ }
- Hints hints;
- Atom property;
- hints.flags = 2;
- 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);
+ switch (p_mode) {
+ case WINDOW_MODE_WINDOWED: {
+ //do nothing
+ } break;
+ case WINDOW_MODE_MINIMIZED: {
+ // Using ICCCM -- Inter-Client Communication Conventions Manual
+ XEvent xev;
+ Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False);
+
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.xclient.window = wd.x11_window;
+ xev.xclient.message_type = wm_change;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = WM_IconicState;
+
+ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+
+ Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
+ Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False);
+
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.xclient.window = wd.x11_window;
+ xev.xclient.message_type = wm_state;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
+ xev.xclient.data.l[1] = wm_hidden;
+
+ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+ } break;
+ case WINDOW_MODE_FULLSCREEN: {
+ wd.last_position_before_fs = wd.position;
+ if (window_get_flag(WINDOW_FLAG_ALWAYS_ON_TOP, p_window)) {
+ _set_wm_maximized(p_window, true);
+ }
+ _set_wm_fullscreen(p_window, true);
+ wd.fullscreen = true;
+ } break;
+ case WINDOW_MODE_MAXIMIZED: {
+
+ _set_wm_maximized(p_window, true);
- // Preserve window size
- set_window_size(Size2(current_videomode.width, current_videomode.height));
+ } break;
+ }
}
-bool OS_X11::get_borderless_window() {
+DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) const {
- bool borderless = current_videomode.borderless_window;
- Atom prop = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
- if (prop != None) {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED);
+ const WindowData &wd = windows[p_window];
+
+ if (wd.fullscreen) { //if fullscreen, it's not in another mode
+ return WINDOW_MODE_FULLSCREEN;
+ }
+ { //test maximized
+ // Using EWMH -- Extended Window Manager Hints
+ Atom property = XInternAtom(x11_display, "_NET_WM_STATE", False);
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;
+ bool retval = false;
+
+ int result = XGetWindowProperty(
+ x11_display,
+ wd.x11_window,
+ property,
+ 0,
+ 1024,
+ False,
+ XA_ATOM,
+ &type,
+ &format,
+ &len,
+ &remaining,
+ &data);
+
+ if (result == Success && data) {
+ Atom *atoms = (Atom *)data;
+ Atom wm_max_horz = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
+ Atom wm_max_vert = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
+ bool found_wm_max_horz = false;
+ bool found_wm_max_vert = false;
+
+ for (uint64_t i = 0; i < len; i++) {
+ if (atoms[i] == wm_max_horz)
+ found_wm_max_horz = true;
+ if (atoms[i] == wm_max_vert)
+ found_wm_max_vert = true;
+
+ if (found_wm_max_horz && found_wm_max_vert) {
+ retval = true;
+ break;
+ }
}
+
XFree(data);
}
+
+ if (retval) {
+ return WINDOW_MODE_MAXIMIZED;
+ }
+ }
+
+ { // test minimzed
+ // Using ICCCM -- Inter-Client Communication Conventions Manual
+ Atom property = XInternAtom(x11_display, "WM_STATE", True);
+ Atom type;
+ int format;
+ unsigned long len;
+ unsigned long remaining;
+ unsigned char *data = NULL;
+
+ int result = XGetWindowProperty(
+ x11_display,
+ wd.x11_window,
+ property,
+ 0,
+ 32,
+ False,
+ AnyPropertyType,
+ &type,
+ &format,
+ &len,
+ &remaining,
+ &data);
+
+ if (result == Success && data) {
+ long *state = (long *)data;
+ if (state[0] == WM_IconicState)
+ return WINDOW_MODE_MINIMIZED;
+ }
+ }
+
+ // all other discarded, return windowed.
+
+ return WINDOW_MODE_WINDOWED;
+}
+
+void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ switch (p_flag) {
+ case WINDOW_FLAG_RESIZE_DISABLED: {
+ XSizeHints *xsh;
+ xsh = XAllocSizeHints();
+ if (p_enabled) {
+ Size2i size = window_get_size(p_window);
+
+ xsh->flags = PMinSize | PMaxSize;
+ xsh->min_width = size.x;
+ xsh->max_width = size.x;
+ xsh->min_height = size.y;
+ xsh->max_height = size.y;
+ } else {
+ xsh->flags = 0L;
+ if (wd.min_size != Size2i()) {
+ xsh->flags |= PMinSize;
+ xsh->min_width = wd.min_size.x;
+ xsh->min_height = wd.min_size.y;
+ }
+ if (wd.max_size != Size2i()) {
+ xsh->flags |= PMaxSize;
+ xsh->max_width = wd.max_size.x;
+ xsh->max_height = wd.max_size.y;
+ }
+ }
+
+ XSetWMNormalHints(x11_display, wd.x11_window, xsh);
+ XFree(xsh);
+
+ wd.resize_disabled = p_enabled;
+
+ XFlush(x11_display);
+
+ } break;
+ case WINDOW_FLAG_BORDERLESS: {
+
+ Hints hints;
+ Atom property;
+ hints.flags = 2;
+ hints.decorations = p_enabled ? 0 : 1;
+ property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
+
+ // Preserve window size
+ window_set_size(window_get_size(p_window), p_window);
+
+ wd.borderless = p_enabled;
+ } break;
+ case WINDOW_FLAG_ALWAYS_ON_TOP: {
+
+ ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID, "Can't make a window transient if the 'on top' flag is active.");
+ if (p_enabled && wd.fullscreen) {
+ _set_wm_maximized(p_window, true);
+ }
+
+ Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False);
+ Atom wm_above = XInternAtom(x11_display, "_NET_WM_STATE_ABOVE", False);
+
+ XClientMessageEvent xev;
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.window = wd.x11_window;
+ xev.message_type = wm_state;
+ xev.format = 32;
+ xev.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
+ xev.data.l[1] = wm_above;
+ xev.data.l[3] = 1;
+ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xev);
+
+ if (!p_enabled && !wd.fullscreen) {
+ _set_wm_maximized(p_window, false);
+ }
+ wd.on_top = p_enabled;
+
+ } break;
+ case WINDOW_FLAG_TRANSPARENT: {
+ //todo reimplement
+ } break;
+ default: {
+ }
}
- return borderless;
}
+bool DisplayServerX11::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), false);
+ const WindowData &wd = windows[p_window];
+
+ switch (p_flag) {
+ case WINDOW_FLAG_RESIZE_DISABLED: {
+
+ return wd.resize_disabled;
+ } break;
+ case WINDOW_FLAG_BORDERLESS: {
+
+ bool borderless = wd.borderless;
+ 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, wd.x11_window, prop, 0, sizeof(Hints), False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) {
+ if (data && (format == 32) && (len >= 5)) {
+ borderless = !((Hints *)data)->decorations;
+ }
+ if (data) {
+ XFree(data);
+ }
+ }
+ }
+ return borderless;
+ } break;
+ case WINDOW_FLAG_ALWAYS_ON_TOP: {
-void OS_X11::request_attention() {
+ return wd.on_top;
+ } break;
+ case WINDOW_FLAG_TRANSPARENT: {
+ //todo reimplement
+ } break;
+ default: {
+ }
+ }
+
+ return false;
+}
+
+void DisplayServerX11::window_request_attention(WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
// Using EWMH -- Extended Window Manager Hints
//
// Sets the _NET_WM_STATE_DEMANDS_ATTENTION atom for WM_STATE
@@ -1795,7 +1635,7 @@ void OS_X11::request_attention() {
memset(&xev, 0, sizeof(xev));
xev.type = ClientMessage;
- xev.xclient.window = x11_window;
+ xev.xclient.window = wd.x11_window;
xev.xclient.message_type = wm_state;
xev.xclient.format = 32;
xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
@@ -1805,7 +1645,314 @@ void OS_X11::request_attention() {
XFlush(x11_display);
}
-void OS_X11::get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state) {
+void DisplayServerX11::window_move_to_foreground(WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ XEvent xev;
+ Atom net_active_window = XInternAtom(x11_display, "_NET_ACTIVE_WINDOW", False);
+
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.xclient.window = wd.x11_window;
+ xev.xclient.message_type = net_active_window;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = 1;
+ xev.xclient.data.l[1] = CurrentTime;
+
+ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+ XFlush(x11_display);
+}
+
+bool DisplayServerX11::window_can_draw(WindowID p_window) const {
+
+ //this seems to be all that is provided by X11
+ return window_get_mode(p_window) != WINDOW_MODE_MINIMIZED;
+}
+bool DisplayServerX11::can_any_window_draw() const {
+
+ _THREAD_SAFE_METHOD_
+
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (window_get_mode(E->key()) != WINDOW_MODE_MINIMIZED) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void DisplayServerX11::window_set_ime_active(const bool p_active, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ wd.im_active = p_active;
+
+ if (!wd.xic)
+ return;
+
+ if (p_active) {
+ XSetICFocus(wd.xic);
+ window_set_ime_position(wd.im_position, p_window);
+ } else {
+ XUnsetICFocus(wd.xic);
+ }
+}
+void DisplayServerX11::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ wd.im_position = p_pos;
+
+ if (!wd.xic)
+ return;
+
+ ::XPoint spot;
+ spot.x = short(p_pos.x);
+ spot.y = short(p_pos.y);
+ XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL);
+ XSetICValues(wd.xic, XNPreeditAttributes, preedit_attr, NULL);
+ XFree(preedit_attr);
+}
+
+void DisplayServerX11::cursor_set_shape(CursorShape p_shape) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
+
+ if (p_shape == current_cursor) {
+ return;
+ }
+
+ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
+ if (cursors[p_shape] != None) {
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ XDefineCursor(x11_display, E->get().x11_window, cursors[p_shape]);
+ }
+ } else if (cursors[CURSOR_ARROW] != None) {
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ XDefineCursor(x11_display, E->get().x11_window, cursors[CURSOR_ARROW]);
+ }
+ }
+ }
+
+ current_cursor = p_shape;
+}
+DisplayServerX11::CursorShape DisplayServerX11::cursor_get_shape() const {
+ return current_cursor;
+}
+void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
+
+ _THREAD_SAFE_METHOD_
+
+ if (p_cursor.is_valid()) {
+
+ Map<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);
+
+ if (cursor_c) {
+ if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {
+ cursor_set_shape(p_shape);
+ return;
+ }
+
+ cursors_cache.erase(p_shape);
+ }
+
+ Ref<Texture2D> texture = p_cursor;
+ Ref<AtlasTexture> atlas_texture = p_cursor;
+ Ref<Image> image;
+ Size2i texture_size;
+ Rect2i atlas_rect;
+
+ if (texture.is_valid()) {
+ image = texture->get_data();
+ }
+
+ if (!image.is_valid() && atlas_texture.is_valid()) {
+ texture = atlas_texture->get_atlas();
+
+ atlas_rect.size.width = texture->get_width();
+ atlas_rect.size.height = texture->get_height();
+ atlas_rect.position.x = atlas_texture->get_region().position.x;
+ atlas_rect.position.y = atlas_texture->get_region().position.y;
+
+ texture_size.width = atlas_texture->get_region().size.x;
+ texture_size.height = atlas_texture->get_region().size.y;
+ } else if (image.is_valid()) {
+ texture_size.width = texture->get_width();
+ texture_size.height = texture->get_height();
+ }
+
+ ERR_FAIL_COND(!texture.is_valid());
+ ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
+ ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
+ ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
+
+ image = texture->get_data();
+
+ ERR_FAIL_COND(!image.is_valid());
+
+ // Create the cursor structure
+ XcursorImage *cursor_image = XcursorImageCreate(texture_size.width, texture_size.height);
+ XcursorUInt image_size = texture_size.width * texture_size.height;
+ XcursorDim size = sizeof(XcursorPixel) * image_size;
+
+ cursor_image->version = 1;
+ cursor_image->size = size;
+ cursor_image->xhot = p_hotspot.x;
+ cursor_image->yhot = p_hotspot.y;
+
+ // allocate memory to contain the whole file
+ cursor_image->pixels = (XcursorPixel *)memalloc(size);
+
+ for (XcursorPixel index = 0; index < image_size; index++) {
+ int row_index = floor(index / texture_size.width) + atlas_rect.position.y;
+ int column_index = (index % int(texture_size.width)) + atlas_rect.position.x;
+
+ if (atlas_texture.is_valid()) {
+ column_index = MIN(column_index, atlas_rect.size.width - 1);
+ row_index = MIN(row_index, atlas_rect.size.height - 1);
+ }
+
+ *(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32();
+ }
+
+ ERR_FAIL_COND(cursor_image->pixels == NULL);
+
+ // Save it for a further usage
+ cursors[p_shape] = XcursorImageLoadCursor(x11_display, cursor_image);
+
+ Vector<Variant> params;
+ params.push_back(p_cursor);
+ params.push_back(p_hotspot);
+ cursors_cache.insert(p_shape, params);
+
+ if (p_shape == current_cursor) {
+ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ XDefineCursor(x11_display, E->get().x11_window, cursors[p_shape]);
+ }
+ }
+ }
+
+ memfree(cursor_image->pixels);
+ XcursorImageDestroy(cursor_image);
+ } else {
+ // Reset to default system cursor
+ if (img[p_shape]) {
+ cursors[p_shape] = XcursorImageLoadCursor(x11_display, img[p_shape]);
+ }
+
+ CursorShape c = current_cursor;
+ current_cursor = CURSOR_MAX;
+ cursor_set_shape(c);
+
+ cursors_cache.erase(p_shape);
+ }
+}
+
+DisplayServerX11::LatinKeyboardVariant DisplayServerX11::get_latin_keyboard_variant() const {
+ _THREAD_SAFE_METHOD_
+
+ XkbDescRec *xkbdesc = XkbAllocKeyboard();
+ ERR_FAIL_COND_V(!xkbdesc, LATIN_KEYBOARD_QWERTY);
+
+ XkbGetNames(x11_display, XkbSymbolsNameMask, xkbdesc);
+ ERR_FAIL_COND_V(!xkbdesc->names, LATIN_KEYBOARD_QWERTY);
+ ERR_FAIL_COND_V(!xkbdesc->names->symbols, LATIN_KEYBOARD_QWERTY);
+
+ char *layout = XGetAtomName(x11_display, xkbdesc->names->symbols);
+ ERR_FAIL_COND_V(!layout, LATIN_KEYBOARD_QWERTY);
+
+ Vector<String> info = String(layout).split("+");
+ ERR_FAIL_INDEX_V(1, info.size(), LATIN_KEYBOARD_QWERTY);
+
+ if (info[1].find("colemak") != -1) {
+ return LATIN_KEYBOARD_COLEMAK;
+ } else if (info[1].find("qwertz") != -1) {
+ return LATIN_KEYBOARD_QWERTZ;
+ } else if (info[1].find("azerty") != -1) {
+ return LATIN_KEYBOARD_AZERTY;
+ } else if (info[1].find("qzerty") != -1) {
+ return LATIN_KEYBOARD_QZERTY;
+ } else if (info[1].find("dvorak") != -1) {
+ return LATIN_KEYBOARD_DVORAK;
+ } else if (info[1].find("neo") != -1) {
+ return LATIN_KEYBOARD_NEO;
+ }
+
+ return LATIN_KEYBOARD_QWERTY;
+}
+
+DisplayServerX11::Property DisplayServerX11::_read_property(Display *p_display, Window p_window, Atom p_property) {
+
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems;
+ unsigned long bytes_after;
+ unsigned char *ret = 0;
+
+ int read_bytes = 1024;
+
+ //Keep trying to read the property until there are no
+ //bytes unread.
+ do {
+ if (ret != 0)
+ XFree(ret);
+
+ XGetWindowProperty(p_display, p_window, p_property, 0, read_bytes, False, AnyPropertyType,
+ &actual_type, &actual_format, &nitems, &bytes_after,
+ &ret);
+
+ read_bytes *= 2;
+
+ } while (bytes_after != 0);
+
+ Property p = { ret, actual_format, (int)nitems, actual_type };
+
+ return p;
+}
+
+static Atom pick_target_from_list(Display *p_display, Atom *p_list, int p_count) {
+
+ static const char *target_type = "text/uri-list";
+
+ for (int i = 0; i < p_count; i++) {
+
+ Atom atom = p_list[i];
+
+ if (atom != None && String(XGetAtomName(p_display, atom)) == target_type)
+ return atom;
+ }
+ return None;
+}
+
+static Atom pick_target_from_atoms(Display *p_disp, Atom p_t1, Atom p_t2, Atom p_t3) {
+
+ static const char *target_type = "text/uri-list";
+ if (p_t1 != None && String(XGetAtomName(p_disp, p_t1)) == target_type)
+ return p_t1;
+
+ if (p_t2 != None && String(XGetAtomName(p_disp, p_t2)) == target_type)
+ return p_t2;
+
+ if (p_t3 != None && String(XGetAtomName(p_disp, p_t3)) == target_type)
+ return p_t3;
+
+ return None;
+}
+
+void DisplayServerX11::_get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state) {
state->set_shift((p_x11_state & ShiftMask));
state->set_control((p_x11_state & ControlMask));
@@ -1813,7 +1960,7 @@ void OS_X11::get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWith
state->set_metakey((p_x11_state & Mod4Mask));
}
-unsigned int OS_X11::get_mouse_button_state(unsigned int p_x11_button, int p_x11_type) {
+unsigned int DisplayServerX11::_get_mouse_button_state(unsigned int p_x11_button, int p_x11_type) {
unsigned int mask = 1 << (p_x11_button - 1);
@@ -1826,8 +1973,9 @@ unsigned int OS_X11::get_mouse_button_state(unsigned int p_x11_button, int p_x11
return last_button_state;
}
-void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
+void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, bool p_echo) {
+ WindowData wd = windows[p_window];
// X11 functions don't know what const is
XKeyEvent *xkeyevent = p_event;
@@ -1853,7 +2001,7 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
KeySym keysym_keycode = 0; // keysym used to find a keycode
KeySym keysym_unicode = 0; // keysym used to find unicode
- // XLookupString returns keysyms usable as nice scancodes/
+ // XLookupString returns keysyms usable as nice keycodes.
char str[256 + 1];
XKeyEvent xkeyevent_no_mod = *xkeyevent;
xkeyevent_no_mod.state &= ~ShiftMask;
@@ -1869,18 +2017,18 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
xmblen = 8;
}
- if (xkeyevent->type == KeyPress && xic) {
+ if (xkeyevent->type == KeyPress && wd.xic) {
Status status;
#ifdef X_HAVE_UTF8_STRING
int utf8len = 8;
char *utf8string = (char *)memalloc(sizeof(char) * utf8len);
- int utf8bytes = Xutf8LookupString(xic, xkeyevent, utf8string,
+ int utf8bytes = Xutf8LookupString(wd.xic, xkeyevent, utf8string,
utf8len - 1, &keysym_unicode, &status);
if (status == XBufferOverflow) {
utf8len = utf8bytes + 1;
utf8string = (char *)memrealloc(utf8string, utf8len);
- utf8bytes = Xutf8LookupString(xic, xkeyevent, utf8string,
+ utf8bytes = Xutf8LookupString(wd.xic, xkeyevent, utf8string,
utf8len - 1, &keysym_unicode, &status);
}
utf8string[utf8bytes] = '\0';
@@ -1902,11 +2050,13 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
continue;
}
- if (keycode == 0)
+ if (keycode == 0) {
keycode = physical_keycode;
+ }
- get_key_modifier_state(xkeyevent->state, k);
+ _get_key_modifier_state(xkeyevent->state, k);
+ k->set_window_id(p_window);
k->set_unicode(tmp[i]);
k->set_pressed(keypress);
@@ -1924,7 +2074,7 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
k->set_shift(true);
}
- input->accumulate_input_event(k);
+ InputFilter::get_singleton()->accumulate_input_event(k);
}
memfree(utf8string);
return;
@@ -1944,7 +2094,7 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
#endif
}
- /* Phase 2, obtain a pigui keycode from the keysym */
+ /* Phase 2, obtain a Godot keycode from the keysym */
// KeyMappingX11 just translated the X11 keysym to a PIGUI
// keysym, so it works in all platforms the same.
@@ -1970,11 +2120,13 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
bool keypress = xkeyevent->type == KeyPress;
- if (physical_keycode == 0 && keycode == 0 && unicode == 0)
+ if (physical_keycode == 0 && keycode == 0 && unicode == 0) {
return;
+ }
- if (keycode == 0)
+ if (keycode == 0) {
keycode = physical_keycode;
+ }
/* Phase 5, determine modifier mask */
@@ -1986,8 +2138,9 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
Ref<InputEventKey> k;
k.instance();
+ k->set_window_id(p_window);
- get_key_modifier_state(xkeyevent->state, k);
+ _get_key_modifier_state(xkeyevent->state, k);
/* Phase 6, determine echo character */
@@ -2021,7 +2174,7 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
if (rk == keysym_keycode) {
XEvent event;
XNextEvent(x11_display, &event); //erase next event
- handle_key_event((XKeyEvent *)&event, true);
+ _handle_key_event(p_window, (XKeyEvent *)&event, true);
return; //ignore current, echo next
}
}
@@ -2064,104 +2217,133 @@ void OS_X11::handle_key_event(XKeyEvent *p_event, bool p_echo) {
k->set_metakey(false);
}
- bool last_is_pressed = Input::get_singleton()->is_key_pressed(k->get_keycode());
+ bool last_is_pressed = InputFilter::get_singleton()->is_key_pressed(k->get_keycode());
if (k->is_pressed()) {
if (last_is_pressed) {
k->set_echo(true);
}
}
- //printf("key: %x\n",k->get_keycode());
- input->accumulate_input_event(k);
+ InputFilter::get_singleton()->accumulate_input_event(k);
}
-struct Property {
- unsigned char *data;
- int format, nitems;
- Atom type;
-};
-
-static Property read_property(Display *p_display, Window p_window, Atom p_property) {
-
- Atom actual_type;
- int actual_format;
- unsigned long nitems;
- unsigned long bytes_after;
- unsigned char *ret = 0;
-
- int read_bytes = 1024;
-
- //Keep trying to read the property until there are no
- //bytes unread.
- do {
- if (ret != 0)
- XFree(ret);
+void DisplayServerX11::_xim_destroy_callback(::XIM im, ::XPointer client_data,
+ ::XPointer call_data) {
- XGetWindowProperty(p_display, p_window, p_property, 0, read_bytes, False, AnyPropertyType,
- &actual_type, &actual_format, &nitems, &bytes_after,
- &ret);
+ WARN_PRINT("Input method stopped");
+ DisplayServerX11 *ds = reinterpret_cast<DisplayServerX11 *>(client_data);
+ ds->xim = NULL;
- read_bytes *= 2;
+ for (Map<WindowID, WindowData>::Element *E = ds->windows.front(); E; E = E->next()) {
+ E->get().xic = NULL;
+ }
+}
- } while (bytes_after != 0);
+void DisplayServerX11::_window_changed(XEvent *event) {
- Property p = { ret, actual_format, (int)nitems, actual_type };
+ WindowID window_id = MAIN_WINDOW_ID;
- return p;
-}
+ // Assign the event to the relevant window
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (event->xany.window == E->get().x11_window) {
+ window_id = E->key();
+ break;
+ }
+ }
-static Atom pick_target_from_list(Display *p_display, Atom *p_list, int p_count) {
+ Rect2i new_rect;
- static const char *target_type = "text/uri-list";
+ WindowData &wd = windows[window_id];
+ if (wd.x11_window != event->xany.window) { // Check if the correct window, in case it was not main window or anything else
+ return;
+ }
- for (int i = 0; i < p_count; i++) {
+ {
+ //the position in xconfigure is not useful here, obtain it manually
+ int x, y;
+ Window child;
+ XTranslateCoordinates(x11_display, wd.x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child);
+ new_rect.position.x = x;
+ new_rect.position.y = y;
- Atom atom = p_list[i];
+ new_rect.size.width = event->xconfigure.width;
+ new_rect.size.height = event->xconfigure.height;
+ }
- if (atom != None && String(XGetAtomName(p_display, atom)) == target_type)
- return atom;
+ if (new_rect == Rect2i(wd.position, wd.size)) {
+ return;
+ }
+ if (wd.xic) {
+ // Not portable.
+ window_set_ime_position(Point2(0, 1));
}
- return None;
-}
-static Atom pick_target_from_atoms(Display *p_disp, Atom p_t1, Atom p_t2, Atom p_t3) {
+ wd.position = new_rect.position;
+ wd.size = new_rect.size;
- static const char *target_type = "text/uri-list";
- if (p_t1 != None && String(XGetAtomName(p_disp, p_t1)) == target_type)
- return p_t1;
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_resize(window_id, wd.size.width, wd.size.height);
+ }
+#endif
- if (p_t2 != None && String(XGetAtomName(p_disp, p_t2)) == target_type)
- return p_t2;
+ print_line("DisplayServer::_window_changed: " + itos(window_id) + " rect: " + new_rect);
+ if (!wd.rect_changed_callback.is_null()) {
+ Rect2i r = new_rect;
- if (p_t3 != None && String(XGetAtomName(p_disp, p_t3)) == target_type)
- return p_t3;
+ Variant rect = r;
- return None;
+ Variant *rectp = &rect;
+ Variant ret;
+ Callable::CallError ce;
+ wd.rect_changed_callback.call((const Variant **)&rectp, 1, ret, ce);
+ }
}
-void OS_X11::_window_changed(XEvent *event) {
+void DisplayServerX11::_dispatch_input_events(const Ref<InputEvent> &p_event) {
+ ((DisplayServerX11 *)(get_singleton()))->_dispatch_input_event(p_event);
+}
- if (xic) {
- // Not portable.
- set_ime_position(Point2(0, 1));
- }
- if ((event->xconfigure.width == current_videomode.width) &&
- (event->xconfigure.height == current_videomode.height))
- return;
+void DisplayServerX11::_dispatch_input_event(const Ref<InputEvent> &p_event) {
- current_videomode.width = event->xconfigure.width;
- current_videomode.height = event->xconfigure.height;
+ Variant ev = p_event;
+ Variant *evp = &ev;
+ Variant ret;
+ Callable::CallError ce;
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
- context_vulkan->window_resize(0, current_videomode.width, current_videomode.height);
+ Ref<InputEventFromWindow> event_from_window = p_event;
+ if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) {
+ //send to a window
+ ERR_FAIL_COND(!windows.has(event_from_window->get_window_id()));
+ Callable callable = windows[event_from_window->get_window_id()].input_event_callback;
+ if (callable.is_null()) {
+ return;
+ }
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ } else {
+ //send to all windows
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ Callable callable = E->get().input_event_callback;
+ if (callable.is_null()) {
+ continue;
+ }
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ }
}
-#endif
}
-void OS_X11::process_xevents() {
+void DisplayServerX11::_send_window_event(const WindowData &wd, WindowEvent p_event) {
+ if (!wd.event_callback.is_null()) {
+ Variant event = int(p_event);
+ Variant *eventp = &event;
+ Variant ret;
+ Callable::CallError ce;
+ wd.event_callback.call((const Variant **)&eventp, 1, ret, ce);
+ }
+}
+void DisplayServerX11::process_events() {
- //printf("checking events %i\n", XPending(x11_display));
+ _THREAD_SAFE_METHOD_
do_mouse_warp = false;
@@ -2172,6 +2354,16 @@ void OS_X11::process_xevents() {
XEvent event;
XNextEvent(x11_display, &event);
+ WindowID window_id = MAIN_WINDOW_ID;
+
+ // Assign the event to the relevant window
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (event.xany.window == E->get().x11_window) {
+ window_id = E->key();
+ break;
+ }
+ }
+
if (XFilterEvent(&event, None)) {
continue;
}
@@ -2187,7 +2379,7 @@ void OS_X11::process_xevents() {
switch (event_data->evtype) {
case XI_HierarchyChanged:
case XI_DeviceChanged: {
- refresh_device_info();
+ _refresh_device_info();
} break;
case XI_RawMotion: {
XIRawEvent *raw_event = (XIRawEvent *)event_data;
@@ -2277,6 +2469,7 @@ void OS_X11::process_xevents() {
Ref<InputEventScreenTouch> st;
st.instance();
+ st->set_window_id(window_id);
st->set_index(index);
st->set_position(pos);
st->set_pressed(is_begin);
@@ -2290,12 +2483,12 @@ void OS_X11::process_xevents() {
// in a spurious mouse motion event being sent to Godot; remember it to be able to filter it out
xi.mouse_pos_to_filter = pos;
}
- input->accumulate_input_event(st);
+ InputFilter::get_singleton()->accumulate_input_event(st);
} else {
if (!xi.state.has(index)) // Defensive
break;
xi.state.erase(index);
- input->accumulate_input_event(st);
+ InputFilter::get_singleton()->accumulate_input_event(st);
}
} break;
@@ -2310,10 +2503,11 @@ void OS_X11::process_xevents() {
Ref<InputEventScreenDrag> sd;
sd.instance();
+ sd->set_window_id(window_id);
sd->set_index(index);
sd->set_position(pos);
sd->set_relative(pos - curr_pos_elem->value());
- input->accumulate_input_event(sd);
+ InputFilter::get_singleton()->accumulate_input_event(sd);
curr_pos_elem->value() = pos;
}
@@ -2338,31 +2532,37 @@ void OS_X11::process_xevents() {
minimized = (visibility->state == VisibilityFullyObscured);
} break;
case LeaveNotify: {
- if (main_loop && !mouse_mode_grab)
- main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
+ if (!mouse_mode_grab) {
+ _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
+ }
} break;
case EnterNotify: {
- if (main_loop && !mouse_mode_grab)
- main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
+ if (!mouse_mode_grab) {
+ _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
+ }
} break;
case FocusIn:
minimized = false;
window_has_focus = true;
- main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
+ _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.
- if (mouse_mode == MOUSE_MODE_CONFINED)
- XUndefineCursor(x11_display, x11_window);
- else if (mouse_mode == MOUSE_MODE_CAPTURED) // or re-hide it in captured mode
- XDefineCursor(x11_display, x11_window, null_cursor);
-
- XGrabPointer(
- x11_display, x11_window, True,
- ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
- GrabModeAsync, GrabModeAsync, x11_window, None, CurrentTime);
+
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+
+ if (mouse_mode == MOUSE_MODE_CONFINED)
+ XUndefineCursor(x11_display, E->get().x11_window);
+ else if (mouse_mode == MOUSE_MODE_CAPTURED) // or re-hide it in captured mode
+ XDefineCursor(x11_display, E->get().x11_window, null_cursor);
+
+ XGrabPointer(
+ x11_display, E->get().x11_window, True,
+ ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
+ GrabModeAsync, GrabModeAsync, E->get().x11_window, None, CurrentTime);
+ }
}
#ifdef TOUCH_ENABLED
// Grab touch devices to avoid OS gesture interference
@@ -2370,22 +2570,25 @@ void OS_X11::process_xevents() {
XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask);
}*/
#endif
- if (xic) {
- XSetICFocus(xic);
+ if (windows[window_id].xic) {
+ XSetICFocus(windows[window_id].xic);
}
break;
case FocusOut:
window_has_focus = false;
- input->release_pressed_events();
- main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
+ InputFilter::get_singleton()->release_pressed_events();
+ _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_OUT);
window_focused = false;
if (mouse_mode_grab) {
- //dear X11, I try, I really try, but you never work, you do whathever you want.
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
- // Show the cursor if we're in captured mode so it doesn't look weird.
- XUndefineCursor(x11_display, x11_window);
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+
+ //dear X11, I try, I really try, but you never work, you do whathever you want.
+ if (mouse_mode == MOUSE_MODE_CAPTURED) {
+ // Show the cursor if we're in captured mode so it doesn't look weird.
+ XUndefineCursor(x11_display, E->get().x11_window);
+ }
}
XUngrabPointer(x11_display, CurrentTime);
}
@@ -2401,13 +2604,14 @@ void OS_X11::process_xevents() {
Ref<InputEventScreenTouch> st;
st.instance();
st->set_index(E->key());
+ st->set_window_id(window_id);
st->set_position(E->get());
- input->accumulate_input_event(st);
+ InputFilter::get_singleton()->accumulate_input_event(st);
}
xi.state.clear();
#endif
- if (xic) {
- XUnsetICFocus(xic);
+ if (windows[window_id].xic) {
+ XSetICFocus(windows[window_id].xic);
}
break;
@@ -2427,13 +2631,14 @@ void OS_X11::process_xevents() {
Ref<InputEventMouseButton> mb;
mb.instance();
- get_key_modifier_state(event.xbutton.state, mb);
+ mb->set_window_id(window_id);
+ _get_key_modifier_state(event.xbutton.state, mb);
mb->set_button_index(event.xbutton.button);
if (mb->get_button_index() == 2)
mb->set_button_index(3);
else if (mb->get_button_index() == 3)
mb->set_button_index(2);
- mb->set_button_mask(get_mouse_button_state(mb->get_button_index(), event.xbutton.type));
+ mb->set_button_mask(_get_mouse_button_state(mb->get_button_index(), event.xbutton.type));
mb->set_position(Vector2(event.xbutton.x, event.xbutton.y));
mb->set_global_position(mb->get_position());
@@ -2441,14 +2646,14 @@ void OS_X11::process_xevents() {
if (event.type == ButtonPress) {
- uint64_t diff = get_ticks_usec() / 1000 - last_click_ms;
+ uint64_t diff = OS::get_singleton()->get_ticks_usec() / 1000 - last_click_ms;
if (mb->get_button_index() == last_click_button_index) {
- if (diff < 400 && Point2(last_click_pos).distance_to(Point2(event.xbutton.x, event.xbutton.y)) < 5) {
+ if (diff < 400 && Vector2(last_click_pos).distance_to(Vector2(event.xbutton.x, event.xbutton.y)) < 5) {
last_click_ms = 0;
- last_click_pos = Point2(-100, -100);
+ last_click_pos = Point2i(-100, -100);
last_click_button_index = -1;
mb->set_doubleclick(true);
}
@@ -2459,11 +2664,11 @@ void OS_X11::process_xevents() {
if (!mb->is_doubleclick()) {
last_click_ms += diff;
- last_click_pos = Point2(event.xbutton.x, event.xbutton.y);
+ last_click_pos = Point2i(event.xbutton.x, event.xbutton.y);
}
}
- input->accumulate_input_event(mb);
+ InputFilter::get_singleton()->accumulate_input_event(mb);
} break;
case MotionNotify: {
@@ -2473,7 +2678,7 @@ void OS_X11::process_xevents() {
// generated by warping the mouse pointer.
while (true) {
- if (mouse_mode == MOUSE_MODE_CAPTURED && event.xmotion.x == current_videomode.width / 2 && event.xmotion.y == current_videomode.height / 2) {
+ if (mouse_mode == MOUSE_MODE_CAPTURED && event.xmotion.x == windows[MAIN_WINDOW_ID].size.width / 2 && event.xmotion.y == windows[MAIN_WINDOW_ID].size.height / 2) {
//this is likely the warp event since it was warped here
center = Vector2(event.xmotion.x, event.xmotion.y);
break;
@@ -2497,7 +2702,7 @@ void OS_X11::process_xevents() {
// Motion is also simple.
// A little hack is in order
// to be able to send relative motion events.
- Point2 pos(event.xmotion.x, event.xmotion.y);
+ Point2i pos(event.xmotion.x, event.xmotion.y);
// Avoidance of spurious mouse motion (see handling of touch)
bool filter = false;
@@ -2534,7 +2739,7 @@ void OS_X11::process_xevents() {
// RawMotion provides more precision than MotionNotify, which doesn't sense subpixel motion.
// Therefore, MotionNotify cannot be the authority on relative mouse motion.
// This means we need to take a combined approach...
- Point2 rel;
+ Point2i rel;
// Only use raw input if in capture mode. Otherwise use the classic behavior.
if (mouse_mode == MOUSE_MODE_CAPTURED) {
@@ -2548,24 +2753,25 @@ void OS_X11::process_xevents() {
xi.relative_motion.y = 0;
if (mouse_mode == MOUSE_MODE_CAPTURED) {
- pos = Point2i(current_videomode.width / 2, current_videomode.height / 2);
+ pos = Point2i(windows[MAIN_WINDOW_ID].size.width / 2, windows[MAIN_WINDOW_ID].size.height / 2);
}
Ref<InputEventMouseMotion> mm;
mm.instance();
+ mm->set_window_id(window_id);
mm->set_pressure(xi.pressure);
mm->set_tilt(xi.tilt);
// Make the absolute position integral so it doesn't look _too_ weird :)
Point2i posi(pos);
- get_key_modifier_state(event.xmotion.state, mm);
- mm->set_button_mask(get_mouse_button_state());
+ _get_key_modifier_state(event.xmotion.state, mm);
+ mm->set_button_mask(mouse_get_button_state());
mm->set_position(posi);
mm->set_global_position(posi);
- input->set_mouse_position(posi);
- mm->set_speed(input->get_last_mouse_speed());
+ InputFilter::get_singleton()->set_mouse_position(posi);
+ mm->set_speed(InputFilter::get_singleton()->get_last_mouse_speed());
mm->set_relative(rel);
@@ -2576,7 +2782,7 @@ void OS_X11::process_xevents() {
// this is so that the relative motion doesn't get messed up
// after we regain focus.
if (window_has_focus || !mouse_mode_grab)
- input->accumulate_input_event(mm);
+ InputFilter::get_singleton()->accumulate_input_event(mm);
} break;
case KeyPress:
@@ -2586,7 +2792,7 @@ void OS_X11::process_xevents() {
// key event is a little complex, so
// it will be handled in its own function.
- handle_key_event((XKeyEvent *)&event);
+ _handle_key_event(window_id, (XKeyEvent *)&event);
} break;
case SelectionRequest: {
@@ -2601,7 +2807,7 @@ void OS_X11::process_xevents() {
req->target == XA_STRING ||
req->target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) ||
req->target == XInternAtom(x11_display, "text/plain", 0)) {
- CharString clip = OS::get_clipboard().utf8();
+ CharString clip = clipboard_get().utf8();
XChangeProperty(x11_display,
req->requestor,
req->property,
@@ -2654,13 +2860,20 @@ void OS_X11::process_xevents() {
if (event.xselection.target == requested) {
- Property p = read_property(x11_display, x11_window, XInternAtom(x11_display, "PRIMARY", 0));
+ Property p = _read_property(x11_display, windows[window_id].x11_window, XInternAtom(x11_display, "PRIMARY", 0));
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_edges();
}
- main_loop->drop_files(files);
+
+ if (!windows[window_id].drop_files_callback.is_null()) {
+ Variant v = files;
+ Variant *vp = &v;
+ Variant ret;
+ Callable::CallError ce;
+ windows[window_id].drop_files_callback.call((const Variant **)&vp, 1, ret, ce);
+ }
//Reply that all is well.
XClientMessageEvent m;
@@ -2670,7 +2883,7 @@ void OS_X11::process_xevents() {
m.window = xdnd_source_window;
m.message_type = xdnd_finished;
m.format = 32;
- m.data.l[0] = x11_window;
+ m.data.l[0] = windows[window_id].x11_window;
m.data.l[1] = 1;
m.data.l[2] = xdnd_action_copy; //We only ever copy.
@@ -2680,8 +2893,9 @@ void OS_X11::process_xevents() {
case ClientMessage:
- if ((unsigned int)event.xclient.data.l[0] == (unsigned int)wm_delete)
- main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
+ if ((unsigned int)event.xclient.data.l[0] == (unsigned int)wm_delete) {
+ _send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);
+ }
else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_enter) {
@@ -2690,7 +2904,7 @@ void OS_X11::process_xevents() {
Window source = event.xclient.data.l[0];
bool more_than_3 = event.xclient.data.l[1] & 1;
if (more_than_3) {
- Property p = read_property(x11_display, source, XInternAtom(x11_display, "XdndTypeList", False));
+ Property p = _read_property(x11_display, source, XInternAtom(x11_display, "XdndTypeList", False));
requested = pick_target_from_list(x11_display, (Atom *)p.data, p.nitems);
} else
requested = pick_target_from_atoms(x11_display, event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]);
@@ -2705,7 +2919,7 @@ void OS_X11::process_xevents() {
m.window = event.xclient.data.l[0];
m.message_type = xdnd_status;
m.format = 32;
- m.data.l[0] = x11_window;
+ m.data.l[0] = windows[window_id].x11_window;
m.data.l[1] = (requested != None);
m.data.l[2] = 0; //empty rectangle
m.data.l[3] = 0;
@@ -2718,9 +2932,9 @@ void OS_X11::process_xevents() {
if (requested != None) {
xdnd_source_window = event.xclient.data.l[0];
if (xdnd_version >= 1)
- XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), x11_window, event.xclient.data.l[2]);
+ XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), windows[window_id].x11_window, event.xclient.data.l[2]);
else
- XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), x11_window, CurrentTime);
+ XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), windows[window_id].x11_window, CurrentTime);
} else {
//Reply that we're not interested.
XClientMessageEvent m;
@@ -2730,7 +2944,7 @@ void OS_X11::process_xevents() {
m.window = event.xclient.data.l[0];
m.message_type = xdnd_finished;
m.format = 32;
- m.data.l[0] = x11_window;
+ m.data.l[0] = windows[window_id].x11_window;
m.data.l[1] = 0;
m.data.l[2] = None; //Failed.
XSendEvent(x11_display, event.xclient.data.l[0], False, NoEventMask, (XEvent *)&m);
@@ -2746,8 +2960,8 @@ void OS_X11::process_xevents() {
if (do_mouse_warp) {
- XWarpPointer(x11_display, None, x11_window,
- 0, 0, 0, 0, (int)current_videomode.width / 2, (int)current_videomode.height / 2);
+ XWarpPointer(x11_display, None, windows[MAIN_WINDOW_ID].x11_window,
+ 0, 0, 0, 0, (int)windows[MAIN_WINDOW_ID].size.width / 2, (int)windows[MAIN_WINDOW_ID].size.height / 2);
/*
Window root, child;
@@ -2761,808 +2975,878 @@ void OS_X11::process_xevents() {
*/
}
- input->flush_accumulated_events();
+ InputFilter::get_singleton()->flush_accumulated_events();
}
-MainLoop *OS_X11::get_main_loop() const {
-
- return main_loop;
+void DisplayServerX11::release_rendering_thread() {
}
-void OS_X11::delete_main_loop() {
-
- if (main_loop)
- memdelete(main_loop);
- main_loop = NULL;
+void DisplayServerX11::make_rendering_thread() {
}
-void OS_X11::set_main_loop(MainLoop *p_main_loop) {
-
- main_loop = p_main_loop;
- input->set_main_loop(p_main_loop);
+void DisplayServerX11::swap_buffers() {
}
-bool OS_X11::can_draw() const {
+void DisplayServerX11::_update_context(WindowData &wd) {
+ XClassHint *classHint = XAllocClassHint();
- return !minimized;
-};
+ if (classHint) {
-void OS_X11::set_clipboard(const String &p_text) {
+ CharString name_str;
+ switch (context) {
+ case CONTEXT_EDITOR:
+ name_str = "Godot_Editor";
+ break;
+ case CONTEXT_PROJECTMAN:
+ name_str = "Godot_ProjectList";
+ break;
+ case CONTEXT_ENGINE:
+ name_str = "Godot_Engine";
+ break;
+ }
- OS::set_clipboard(p_text);
+ CharString class_str;
+ if (context == CONTEXT_ENGINE) {
+ String config_name = GLOBAL_GET("application/config/name");
+ if (config_name.length() == 0) {
+ class_str = "Godot_Engine";
+ } else {
+ class_str = config_name.utf8();
+ }
+ } else {
+ class_str = "Godot";
+ }
- XSetSelectionOwner(x11_display, XA_PRIMARY, x11_window, CurrentTime);
- XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, CurrentTime);
-};
+ classHint->res_class = class_str.ptrw();
+ classHint->res_name = name_str.ptrw();
-static String _get_clipboard_impl(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard, Atom target) {
+ XSetClassHint(x11_display, wd.x11_window, classHint);
+ XFree(classHint);
+ }
+}
+void DisplayServerX11::set_context(Context p_context) {
- String ret;
+ _THREAD_SAFE_METHOD_
- Atom type;
- Atom selection = XA_PRIMARY;
- int format, result;
- unsigned long len, bytes_left, dummy;
- unsigned char *data;
- Window Sown = XGetSelectionOwner(x11_display, p_source);
+ context = p_context;
- if (Sown == x11_window) {
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ _update_context(E->get());
+ }
+}
+void DisplayServerX11::set_native_icon(const String &p_filename) {
+ WARN_PRINT("Native icon not supported by this display server.");
+}
- return p_internal_clipboard;
- };
+bool g_set_icon_error = false;
+int set_icon_errorhandler(Display *dpy, XErrorEvent *ev) {
+ g_set_icon_error = true;
+ return 0;
+}
- if (Sown != None) {
- XConvertSelection(x11_display, p_source, target, selection,
- x11_window, CurrentTime);
- XFlush(x11_display);
- while (true) {
- XEvent event;
- XNextEvent(x11_display, &event);
- if (event.type == SelectionNotify && event.xselection.requestor == x11_window) {
- break;
- };
- };
+void DisplayServerX11::set_icon(const Ref<Image> &p_icon) {
+ _THREAD_SAFE_METHOD_
- //
- // Do not get any data, see how much data is there
- //
- XGetWindowProperty(x11_display, x11_window,
- selection, // Tricky..
- 0, 0, // offset - len
- 0, // Delete 0==FALSE
- AnyPropertyType, //flag
- &type, // return type
- &format, // return format
- &len, &bytes_left, //that
- &data);
- // DATA is There
- if (bytes_left > 0) {
- result = XGetWindowProperty(x11_display, x11_window,
- selection, 0, bytes_left, 0,
- AnyPropertyType, &type, &format,
- &len, &dummy, &data);
- if (result == Success) {
- ret.parse_utf8((const char *)data);
- } else
- printf("FAIL\n");
- XFree(data);
- }
- }
+ WindowData &wd = windows[MAIN_WINDOW_ID];
- return ret;
-}
+ int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&set_icon_errorhandler);
-static String _get_clipboard(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard) {
- String ret;
- Atom utf8_atom = XInternAtom(x11_display, "UTF8_STRING", True);
- if (utf8_atom != None) {
- ret = _get_clipboard_impl(p_source, x11_window, x11_display, p_internal_clipboard, utf8_atom);
- }
- if (ret == "") {
- ret = _get_clipboard_impl(p_source, x11_window, x11_display, p_internal_clipboard, XA_STRING);
- }
- return ret;
-}
+ Atom net_wm_icon = XInternAtom(x11_display, "_NET_WM_ICON", False);
-String OS_X11::get_clipboard() const {
+ if (p_icon.is_valid()) {
+ Ref<Image> img = p_icon->duplicate();
+ img->convert(Image::FORMAT_RGBA8);
- String ret;
- ret = _get_clipboard(XInternAtom(x11_display, "CLIPBOARD", 0), x11_window, x11_display, OS::get_clipboard());
+ while (true) {
+ int w = img->get_width();
+ int h = img->get_height();
- if (ret == "") {
- ret = _get_clipboard(XA_PRIMARY, x11_window, x11_display, OS::get_clipboard());
- };
+ if (g_set_icon_error) {
+ g_set_icon_error = false;
- return ret;
-}
+ WARN_PRINT("Icon too large, attempting to resize icon.");
-String OS_X11::get_name() const {
+ int new_width, new_height;
+ if (w > h) {
+ new_width = w / 2;
+ new_height = h * new_width / w;
+ } else {
+ new_height = h / 2;
+ new_width = w * new_height / h;
+ }
- return "X11";
-}
+ w = new_width;
+ h = new_height;
-Error OS_X11::shell_open(String p_uri) {
+ if (!w || !h) {
+ WARN_PRINT("Unable to set icon.");
+ break;
+ }
- Error ok;
- List<String> args;
- args.push_back(p_uri);
- ok = execute("xdg-open", args, false);
- if (ok == OK)
- return OK;
- ok = execute("gnome-open", args, false);
- if (ok == OK)
- return OK;
- ok = execute("kde-open", args, false);
- return ok;
-}
+ img->resize(w, h, Image::INTERPOLATE_CUBIC);
+ }
-bool OS_X11::_check_internal_feature_support(const String &p_feature) {
+ // We're using long to have wordsize (32Bit build -> 32 Bits, 64 Bit build -> 64 Bits
+ Vector<long> pd;
- return p_feature == "pc";
-}
+ pd.resize(2 + w * h);
-String OS_X11::get_config_path() const {
+ pd.write[0] = w;
+ pd.write[1] = h;
- if (has_environment("XDG_CONFIG_HOME")) {
- return get_environment("XDG_CONFIG_HOME");
- } else if (has_environment("HOME")) {
- return get_environment("HOME").plus_file(".config");
- } else {
- return ".";
- }
-}
+ const uint8_t *r = img->get_data().ptr();
-String OS_X11::get_data_path() const {
+ long *wr = &pd.write[2];
+ uint8_t const *pr = r;
- if (has_environment("XDG_DATA_HOME")) {
- return get_environment("XDG_DATA_HOME");
- } else if (has_environment("HOME")) {
- return get_environment("HOME").plus_file(".local/share");
- } else {
- return get_config_path();
- }
-}
+ for (int i = 0; i < w * h; i++) {
+ long v = 0;
+ // A R G B
+ v |= pr[3] << 24 | pr[0] << 16 | pr[1] << 8 | pr[2];
+ *wr++ = v;
+ pr += 4;
+ }
-String OS_X11::get_cache_path() const {
+ XChangeProperty(x11_display, wd.x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size());
- if (has_environment("XDG_CACHE_HOME")) {
- return get_environment("XDG_CACHE_HOME");
- } else if (has_environment("HOME")) {
- return get_environment("HOME").plus_file(".cache");
+ if (!g_set_icon_error)
+ break;
+ }
} else {
- return get_config_path();
+ XDeleteProperty(x11_display, wd.x11_window, net_wm_icon);
}
+
+ XFlush(x11_display);
+ XSetErrorHandler(oldHandler);
}
-String OS_X11::get_system_dir(SystemDir p_dir) const {
+Vector<String> DisplayServerX11::get_rendering_drivers_func() {
+ Vector<String> drivers;
- String xdgparam;
+#ifdef VULKAN_ENABLED
+ drivers.push_back("vulkan");
+#endif
+#ifdef OPENGL_ENABLED
+ drivers.push_back("opengl");
+#endif
- switch (p_dir) {
- case SYSTEM_DIR_DESKTOP: {
+ return drivers;
+}
- xdgparam = "DESKTOP";
- } break;
- case SYSTEM_DIR_DCIM: {
+DisplayServer *DisplayServerX11::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- xdgparam = "PICTURES";
+ return memnew(DisplayServerX11(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+}
- } break;
- case SYSTEM_DIR_DOCUMENTS: {
+DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
- xdgparam = "DOCUMENTS";
+ //Create window
- } break;
- case SYSTEM_DIR_DOWNLOADS: {
+ long visualMask = VisualScreenMask;
+ int numberOfVisuals;
+ XVisualInfo vInfoTemplate = {};
+ vInfoTemplate.screen = DefaultScreen(x11_display);
+ XVisualInfo *visualInfo = XGetVisualInfo(x11_display, visualMask, &vInfoTemplate, &numberOfVisuals);
- xdgparam = "DOWNLOAD";
+ Colormap colormap = XCreateColormap(x11_display, RootWindow(x11_display, vInfoTemplate.screen), visualInfo->visual, AllocNone);
- } break;
- case SYSTEM_DIR_MOVIES: {
+ XSetWindowAttributes windowAttributes = {};
+ windowAttributes.colormap = colormap;
+ windowAttributes.background_pixel = 0xFFFFFFFF;
+ windowAttributes.border_pixel = 0;
+ windowAttributes.event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask;
- xdgparam = "VIDEOS";
+ unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask;
- } break;
- case SYSTEM_DIR_MUSIC: {
+ WindowID id;
+ {
+ WindowData wd;
+ wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo->screen), p_rect.position.x, p_rect.position.y, p_rect.size.width > 0 ? p_rect.size.width : 1, p_rect.size.height > 0 ? p_rect.size.height : 1, 0, visualInfo->depth, InputOutput, visualInfo->visual, valuemask, &windowAttributes);
- xdgparam = "MUSIC";
+ XMapWindow(x11_display, wd.x11_window);
- } break;
- case SYSTEM_DIR_PICTURES: {
+ //associate PID
+ // make PID known to X11
+ {
+ const long pid = OS::get_singleton()->get_process_id();
+ Atom net_wm_pid = XInternAtom(x11_display, "_NET_WM_PID", False);
+ XChangeProperty(x11_display, wd.x11_window, net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
+ }
- xdgparam = "PICTURES";
+ long im_event_mask = 0;
- } break;
- case SYSTEM_DIR_RINGTONES: {
+ {
+ XIEventMask all_event_mask;
+ XSetWindowAttributes new_attr;
- xdgparam = "MUSIC";
+ new_attr.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask |
+ LeaveWindowMask | PointerMotionMask |
+ Button1MotionMask |
+ Button2MotionMask | Button3MotionMask |
+ Button4MotionMask | Button5MotionMask |
+ ButtonMotionMask | KeymapStateMask |
+ ExposureMask | VisibilityChangeMask |
+ StructureNotifyMask |
+ SubstructureNotifyMask | SubstructureRedirectMask |
+ FocusChangeMask | PropertyChangeMask |
+ ColormapChangeMask | OwnerGrabButtonMask |
+ im_event_mask;
- } break;
- }
+ XChangeWindowAttributes(x11_display, wd.x11_window, CWEventMask, &new_attr);
- String pipe;
- List<String> arg;
- arg.push_back(xdgparam);
- Error err = const_cast<OS_X11 *>(this)->execute("xdg-user-dir", arg, true, NULL, &pipe);
- if (err != OK)
- return ".";
- return pipe.strip_edges();
-}
+ static unsigned char all_mask_data[XIMaskLen(XI_LASTEVENT)] = {};
-void OS_X11::move_window_to_foreground() {
+ all_event_mask.deviceid = XIAllDevices;
+ all_event_mask.mask_len = sizeof(all_mask_data);
+ all_event_mask.mask = all_mask_data;
- XEvent xev;
- Atom net_active_window = XInternAtom(x11_display, "_NET_ACTIVE_WINDOW", False);
+ XISetMask(all_event_mask.mask, XI_HierarchyChanged);
- memset(&xev, 0, sizeof(xev));
- xev.type = ClientMessage;
- xev.xclient.window = x11_window;
- xev.xclient.message_type = net_active_window;
- xev.xclient.format = 32;
- xev.xclient.data.l[0] = 1;
- xev.xclient.data.l[1] = CurrentTime;
+#ifdef TOUCH_ENABLED
+ if (xi.touch_devices.size()) {
+ XISetMask(all_event_mask.mask, XI_TouchBegin);
+ XISetMask(all_event_mask.mask, XI_TouchUpdate);
+ XISetMask(all_event_mask.mask, XI_TouchEnd);
+ XISetMask(all_event_mask.mask, XI_TouchOwnership);
+ }
+#endif
- XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
- XFlush(x11_display);
-}
+ XISelectEvents(x11_display, wd.x11_window, &all_event_mask, 1);
+ }
-void OS_X11::set_cursor_shape(CursorShape p_shape) {
+ /* set the titlebar name */
+ XStoreName(x11_display, wd.x11_window, "Godot");
+ XSetWMProtocols(x11_display, wd.x11_window, &wm_delete, 1);
+ XChangeProperty(x11_display, wd.x11_window, xdnd_aware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&xdnd_version, 1);
- ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
+ if (xim && xim_style) {
- if (p_shape == current_cursor) {
- return;
- }
+ wd.xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, wd.x11_window, XNFocusWindow, wd.x11_window, (char *)NULL);
+ if (XGetICValues(wd.xic, XNFilterEvents, &im_event_mask, NULL) != NULL) {
+ WARN_PRINT("XGetICValues couldn't obtain XNFilterEvents value");
+ XDestroyIC(wd.xic);
+ wd.xic = NULL;
+ }
+ if (wd.xic) {
+ XUnsetICFocus(wd.xic);
+ } else {
+ WARN_PRINT("XCreateIC couldn't create wd.xic");
+ }
+ } else {
- if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
- if (cursors[p_shape] != None) {
- XDefineCursor(x11_display, x11_window, cursors[p_shape]);
- } else if (cursors[CURSOR_ARROW] != None) {
- XDefineCursor(x11_display, x11_window, cursors[CURSOR_ARROW]);
+ wd.xic = NULL;
+ WARN_PRINT("XCreateIC couldn't create wd.xic");
}
- }
- current_cursor = p_shape;
-}
+ _update_context(wd);
-OS::CursorShape OS_X11::get_cursor_shape() const {
+ id = window_id_counter++;
- return current_cursor;
-}
+ windows[id] = wd;
-void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
+ {
- if (p_cursor.is_valid()) {
+ if (p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT) {
- Map<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);
+ XSizeHints *xsh;
+ xsh = XAllocSizeHints();
- if (cursor_c) {
- if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {
- set_cursor_shape(p_shape);
- return;
+ xsh->flags = PMinSize | PMaxSize;
+ xsh->min_width = p_rect.size.width;
+ xsh->max_width = p_rect.size.width;
+ xsh->min_height = p_rect.size.height;
+ xsh->max_height = p_rect.size.height;
+
+ XSetWMNormalHints(x11_display, wd.x11_window, xsh);
+ XFree(xsh);
}
- cursors_cache.erase(p_shape);
- }
+ bool make_utility = false;
- Ref<Texture2D> texture = p_cursor;
- Ref<AtlasTexture> atlas_texture = p_cursor;
- Ref<Image> image;
- Size2 texture_size;
- Rect2 atlas_rect;
+ if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) {
+ Hints hints;
+ Atom property;
+ hints.flags = 2;
+ hints.decorations = 0;
+ property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True);
+ XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
- if (texture.is_valid()) {
- image = texture->get_data();
- }
+ make_utility = true;
+ }
+ if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
+ make_utility = true;
+ }
- if (!image.is_valid() && atlas_texture.is_valid()) {
- texture = atlas_texture->get_atlas();
+ if (make_utility) {
+ //this one seems to disable the fade animations for regular windows
+ //but has the drawback that will not get focus by default, so
+ //we need fo force it, unless no focus requested
- atlas_rect.size.width = texture->get_width();
- atlas_rect.size.height = texture->get_height();
- atlas_rect.position.x = atlas_texture->get_region().position.x;
- atlas_rect.position.y = atlas_texture->get_region().position.y;
+ Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
+ Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
- texture_size.width = atlas_texture->get_region().size.x;
- texture_size.height = atlas_texture->get_region().size.y;
- } else if (image.is_valid()) {
- texture_size.width = texture->get_width();
- texture_size.height = texture->get_height();
- }
+ XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
- ERR_FAIL_COND(!texture.is_valid());
- ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
- ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
- ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
+ if (!(p_flags & WINDOW_FLAG_NO_FOCUS_BIT)) {
+ //but as utility appears unfocused, it needs to be forcefuly focused, unless no focus requested
+ XEvent xev;
+ Atom net_active_window = XInternAtom(x11_display, "_NET_ACTIVE_WINDOW", False);
- image = texture->get_data();
+ memset(&xev, 0, sizeof(xev));
+ xev.type = ClientMessage;
+ xev.xclient.window = wd.x11_window;
+ xev.xclient.message_type = net_active_window;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = 1;
+ xev.xclient.data.l[1] = CurrentTime;
- ERR_FAIL_COND(!image.is_valid());
+ XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+ }
+ } else {
+ Atom type_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE_NORMAL", False);
+ Atom wt_atom = XInternAtom(x11_display, "_NET_WM_WINDOW_TYPE", False);
- // Create the cursor structure
- XcursorImage *cursor_image = XcursorImageCreate(texture_size.width, texture_size.height);
- XcursorUInt image_size = texture_size.width * texture_size.height;
- XcursorDim size = sizeof(XcursorPixel) * image_size;
+ XChangeProperty(x11_display, wd.x11_window, wt_atom, XA_ATOM, 32, PropModeReplace, (unsigned char *)&type_atom, 1);
+ }
+ }
- cursor_image->version = 1;
- cursor_image->size = size;
- cursor_image->xhot = p_hotspot.x;
- cursor_image->yhot = p_hotspot.y;
+ if (id != MAIN_WINDOW_ID) {
- // allocate memory to contain the whole file
- cursor_image->pixels = (XcursorPixel *)memalloc(size);
+ XSizeHints my_hints = XSizeHints();
- for (XcursorPixel index = 0; index < image_size; index++) {
- int row_index = floor(index / texture_size.width) + atlas_rect.position.y;
- int column_index = (index % int(texture_size.width)) + atlas_rect.position.x;
+ my_hints.flags = PPosition | PSize; /* I want to specify position and size */
+ my_hints.x = p_rect.position.x; /* The origin and size coords I want */
+ my_hints.y = p_rect.position.y;
+ my_hints.width = p_rect.size.width;
+ my_hints.height = p_rect.size.height;
- if (atlas_texture.is_valid()) {
- column_index = MIN(column_index, atlas_rect.size.width - 1);
- row_index = MIN(row_index, atlas_rect.size.height - 1);
- }
+ XSetNormalHints(x11_display, wd.x11_window, &my_hints);
+ XMoveWindow(x11_display, wd.x11_window, p_rect.position.x, p_rect.position.y);
+ }
- *(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32();
+#if defined(VULKAN_ENABLED)
+ if (context_vulkan) {
+ Error err = context_vulkan->window_create(id, wd.x11_window, x11_display, p_rect.size.width, p_rect.size.height);
+ ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a Vulkan window");
}
+#endif
- ERR_FAIL_COND(cursor_image->pixels == NULL);
+ //set_class_hint(x11_display, wd.x11_window);
+ XFlush(x11_display);
- // Save it for a further usage
- cursors[p_shape] = XcursorImageLoadCursor(x11_display, cursor_image);
+ XSync(x11_display, False);
+ //XSetErrorHandler(oldHandler);
- Vector<Variant> params;
- params.push_back(p_cursor);
- params.push_back(p_hotspot);
- cursors_cache.insert(p_shape, params);
+ XFree(visualInfo);
+ }
- if (p_shape == current_cursor) {
- if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
- XDefineCursor(x11_display, x11_window, cursors[p_shape]);
- }
- }
+ WindowData &wd = windows[id];
- memfree(cursor_image->pixels);
- XcursorImageDestroy(cursor_image);
- } else {
- // Reset to default system cursor
- if (img[p_shape]) {
- cursors[p_shape] = XcursorImageLoadCursor(x11_display, img[p_shape]);
- }
+ window_set_mode(p_mode, id);
- CursorShape c = current_cursor;
- current_cursor = CURSOR_MAX;
- set_cursor_shape(c);
+ //sync size
+ {
+ XWindowAttributes xwa;
- cursors_cache.erase(p_shape);
- }
-}
+ XSync(x11_display, False);
+ XGetWindowAttributes(x11_display, wd.x11_window, &xwa);
-void OS_X11::release_rendering_thread() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->release_current();
- }
-#endif
-}
+ wd.position.x = xwa.x;
+ wd.position.y = xwa.y;
+ wd.size.width = xwa.width;
+ wd.size.height = xwa.height;
-void OS_X11::make_rendering_thread() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->make_current();
+ print_line("DisplayServer::_create_window " + itos(id) + " want rect: " + p_rect + " got rect " + Rect2i(xwa.x, xwa.y, xwa.width, xwa.height));
}
-#endif
-}
-void OS_X11::swap_buffers() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->swap_buffers();
- }
-#endif
- /* not needed for now
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
- context_vulkan->swap_buffers();
+ //set cursor
+ if (cursors[current_cursor] != None) {
+
+ XDefineCursor(x11_display, wd.x11_window, cursors[current_cursor]);
}
-#endif*/
+ return id;
}
-void OS_X11::alert(const String &p_alert, const String &p_title) {
- const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" };
+DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
- String path = get_environment("PATH");
- Vector<String> path_elems = path.split(":", false);
- String program;
+ InputFilter::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
- for (int i = 0; i < path_elems.size(); i++) {
- for (uint64_t k = 0; k < sizeof(message_programs) / sizeof(char *); k++) {
- String tested_path = path_elems[i].plus_file(message_programs[k]);
+ r_error = OK;
- if (FileAccess::exists(tested_path)) {
- program = tested_path;
- break;
- }
- }
+ last_button_state = 0;
- if (program.length())
- break;
- }
+ xmbstring = NULL;
- List<String> args;
+ last_click_ms = 0;
+ last_click_button_index = -1;
+ last_click_pos = Point2i(-100, -100);
- if (program.ends_with("zenity")) {
- args.push_back("--error");
- args.push_back("--width");
- args.push_back("500");
- args.push_back("--title");
- args.push_back(p_title);
- args.push_back("--text");
- args.push_back(p_alert);
- }
+ last_timestamp = 0;
+ last_mouse_pos_valid = false;
+ last_keyrelease_time = 0;
- if (program.ends_with("kdialog")) {
- args.push_back("--error");
- args.push_back(p_alert);
- args.push_back("--title");
- args.push_back(p_title);
+ XInitThreads(); //always use threads
+
+ /** XLIB INITIALIZATION **/
+ x11_display = XOpenDisplay(NULL);
+
+ if (!x11_display) {
+ ERR_PRINT("X11 Display is not available");
+ r_error = ERR_UNAVAILABLE;
+ return;
}
- if (program.ends_with("Xdialog")) {
- args.push_back("--title");
- args.push_back(p_title);
- args.push_back("--msgbox");
- args.push_back(p_alert);
- args.push_back("0");
- args.push_back("0");
+ char *modifiers = NULL;
+ Bool xkb_dar = False;
+ XAutoRepeatOn(x11_display);
+ xkb_dar = XkbSetDetectableAutoRepeat(x11_display, True, NULL);
+
+ // Try to support IME if detectable auto-repeat is supported
+ if (xkb_dar == True) {
+
+#ifdef X_HAVE_UTF8_STRING
+ // Xutf8LookupString will be used later instead of XmbLookupString before
+ // the multibyte sequences can be converted to unicode string.
+ modifiers = XSetLocaleModifiers("");
+#endif
}
- if (program.ends_with("xmessage")) {
- args.push_back("-center");
- args.push_back("-title");
- args.push_back(p_title);
- args.push_back(p_alert);
+ if (modifiers == NULL) {
+ if (OS::get_singleton()->is_stdout_verbose()) {
+ WARN_PRINT("IME is disabled");
+ }
+ XSetLocaleModifiers("@im=none");
+ WARN_PRINT("Error setting locale modifiers");
}
- if (program.length()) {
- execute(program, args, true);
+ const char *err;
+ xrr_get_monitors = NULL;
+ xrr_free_monitors = NULL;
+ int xrandr_major = 0;
+ int xrandr_minor = 0;
+ int event_base, error_base;
+ xrandr_ext_ok = XRRQueryExtension(x11_display, &event_base, &error_base);
+ xrandr_handle = dlopen("libXrandr.so.2", RTLD_LAZY);
+ if (!xrandr_handle) {
+ err = dlerror();
+ fprintf(stderr, "could not load libXrandr.so.2, Error: %s\n", err);
} else {
- print_line(p_alert);
+ XRRQueryVersion(x11_display, &xrandr_major, &xrandr_minor);
+ if (((xrandr_major << 8) | xrandr_minor) >= 0x0105) {
+ xrr_get_monitors = (xrr_get_monitors_t)dlsym(xrandr_handle, "XRRGetMonitors");
+ if (!xrr_get_monitors) {
+ err = dlerror();
+ fprintf(stderr, "could not find symbol XRRGetMonitors\nError: %s\n", err);
+ } else {
+ xrr_free_monitors = (xrr_free_monitors_t)dlsym(xrandr_handle, "XRRFreeMonitors");
+ if (!xrr_free_monitors) {
+ err = dlerror();
+ fprintf(stderr, "could not find XRRFreeMonitors\nError: %s\n", err);
+ xrr_get_monitors = NULL;
+ }
+ }
+ }
}
-}
-
-bool g_set_icon_error = false;
-int set_icon_errorhandler(Display *dpy, XErrorEvent *ev) {
- g_set_icon_error = true;
- return 0;
-}
-
-void OS_X11::set_icon(const Ref<Image> &p_icon) {
- int (*oldHandler)(Display *, XErrorEvent *) = XSetErrorHandler(&set_icon_errorhandler);
- Atom net_wm_icon = XInternAtom(x11_display, "_NET_WM_ICON", False);
-
- if (p_icon.is_valid()) {
- Ref<Image> img = p_icon->duplicate();
- img->convert(Image::FORMAT_RGBA8);
+ if (!_refresh_device_info()) {
+ alert("Your system does not support XInput 2.\n"
+ "Please upgrade your distribution.",
+ "Unable to initialize XInput");
+ r_error = ERR_UNAVAILABLE;
+ return;
+ }
- while (true) {
- int w = img->get_width();
- int h = img->get_height();
+ xim = XOpenIM(x11_display, NULL, NULL, NULL);
- if (g_set_icon_error) {
- g_set_icon_error = false;
+ if (xim == NULL) {
+ WARN_PRINT("XOpenIM failed");
+ xim_style = 0L;
+ } else {
+ ::XIMCallback im_destroy_callback;
+ im_destroy_callback.client_data = (::XPointer)(this);
+ im_destroy_callback.callback = (::XIMProc)(_xim_destroy_callback);
+ if (XSetIMValues(xim, XNDestroyCallback, &im_destroy_callback,
+ NULL) != NULL) {
+ WARN_PRINT("Error setting XIM destroy callback");
+ }
- WARN_PRINT("Icon too large, attempting to resize icon.");
+ ::XIMStyles *xim_styles = NULL;
+ xim_style = 0L;
+ char *imvalret = XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL);
+ if (imvalret != NULL || xim_styles == NULL) {
+ fprintf(stderr, "Input method doesn't support any styles\n");
+ }
- int new_width, new_height;
- if (w > h) {
- new_width = w / 2;
- new_height = h * new_width / w;
- } else {
- new_height = h / 2;
- new_width = w * new_height / h;
- }
+ if (xim_styles) {
+ xim_style = 0L;
+ for (int i = 0; i < xim_styles->count_styles; i++) {
- w = new_width;
- h = new_height;
+ if (xim_styles->supported_styles[i] ==
+ (XIMPreeditNothing | XIMStatusNothing)) {
- if (!w || !h) {
- WARN_PRINT("Unable to set icon.");
+ xim_style = xim_styles->supported_styles[i];
break;
}
-
- img->resize(w, h, Image::INTERPOLATE_CUBIC);
}
- // We're using long to have wordsize (32Bit build -> 32 Bits, 64 Bit build -> 64 Bits
- Vector<long> pd;
-
- pd.resize(2 + w * h);
-
- pd.write[0] = w;
- pd.write[1] = h;
+ XFree(xim_styles);
+ }
+ XFree(imvalret);
+ }
- const uint8_t *r = img->get_data().ptr();
+ /* Atorm internment */
+ wm_delete = XInternAtom(x11_display, "WM_DELETE_WINDOW", true);
+ //Set Xdnd (drag & drop) support
+ xdnd_aware = XInternAtom(x11_display, "XdndAware", False);
+ xdnd_version = 5;
+ xdnd_enter = XInternAtom(x11_display, "XdndEnter", False);
+ xdnd_position = XInternAtom(x11_display, "XdndPosition", False);
+ xdnd_status = XInternAtom(x11_display, "XdndStatus", False);
+ xdnd_action_copy = XInternAtom(x11_display, "XdndActionCopy", False);
+ xdnd_drop = XInternAtom(x11_display, "XdndDrop", False);
+ xdnd_finished = XInternAtom(x11_display, "XdndFinished", False);
+ xdnd_selection = XInternAtom(x11_display, "XdndSelection", False);
- long *wr = &pd.write[2];
- uint8_t const *pr = r;
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!
+ //TODO - do Vulkan and GLES2 support checks, driver selection and fallback
+ rendering_driver = p_rendering_driver;
- for (int i = 0; i < w * h; i++) {
- long v = 0;
- // A R G B
- v |= pr[3] << 24 | pr[0] << 16 | pr[1] << 8 | pr[2];
- *wr++ = v;
- pr += 4;
- }
+#ifndef _MSC_VER
+#warning Forcing vulkan rendering driver because OpenGL not implemented yet
+#endif
+ rendering_driver = "vulkan";
- XChangeProperty(x11_display, x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pd.ptr(), pd.size());
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
- if (!g_set_icon_error)
- break;
+ context_vulkan = memnew(VulkanContextX11);
+ if (context_vulkan->initialize() != OK) {
+ memdelete(context_vulkan);
+ context_vulkan = NULL;
+ r_error = ERR_CANT_CREATE;
+ ERR_FAIL_MSG("Could not initialize Vulkan");
}
- } else {
- XDeleteProperty(x11_display, x11_window, net_wm_icon);
}
-
- XFlush(x11_display);
- XSetErrorHandler(oldHandler);
-}
-
-void OS_X11::force_process_input() {
- process_xevents(); // get rid of pending events
-#ifdef JOYDEV_ENABLED
- joypad->process_joypads();
#endif
-}
+ // Init context and rendering device
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver == "opengl_es") {
+ if (getenv("DRI_PRIME") == NULL) {
+ int use_prime = -1;
-void OS_X11::run() {
+ if (getenv("PRIMUS_DISPLAY") ||
+ getenv("PRIMUS_libGLd") ||
+ getenv("PRIMUS_libGLa") ||
+ getenv("PRIMUS_libGL") ||
+ getenv("PRIMUS_LOAD_GLOBAL") ||
+ getenv("BUMBLEBEE_SOCKET")) {
- force_quit = false;
+ print_verbose("Optirun/primusrun detected. Skipping GPU detection");
+ use_prime = 0;
+ }
- if (!main_loop)
- return;
+ if (getenv("LD_LIBRARY_PATH")) {
+ String ld_library_path(getenv("LD_LIBRARY_PATH"));
+ Vector<String> libraries = ld_library_path.split(":");
- main_loop->init();
+ for (int i = 0; i < libraries.size(); ++i) {
+ if (FileAccess::exists(libraries[i] + "/libGL.so.1") ||
+ FileAccess::exists(libraries[i] + "/libGL.so")) {
- //uint64_t last_ticks=get_ticks_usec();
+ print_verbose("Custom libGL override detected. Skipping GPU detection");
+ use_prime = 0;
+ }
+ }
+ }
- //int frames=0;
- //uint64_t frame=0;
+ if (use_prime == -1) {
+ print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic.");
+ use_prime = detect_prime();
+ }
- while (!force_quit) {
+ if (use_prime) {
+ print_line("Found discrete GPU, setting DRI_PRIME=1 to use it.");
+ print_line("Note: Set DRI_PRIME=0 in the environment to disable Godot from using the discrete GPU.");
+ setenv("DRI_PRIME", "1", 1);
+ }
+ }
- process_xevents(); // get rid of pending events
-#ifdef JOYDEV_ENABLED
- joypad->process_joypads();
-#endif
- if (Main::iteration())
- break;
- };
+ ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE;
- main_loop->finish();
-}
+ context_gles2 = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, opengl_api_type));
-bool OS_X11::is_joy_known(int p_device) {
- return input->is_joy_mapped(p_device);
-}
+ if (context_gles2->initialize() != OK) {
+ memdelete(context_gles2);
+ context_gles2 = NULL;
+ ERR_FAIL_V(ERR_UNAVAILABLE);
+ }
-String OS_X11::get_joy_guid(int p_device) const {
- return input->get_joy_guid_remapped(p_device);
-}
+ context_gles2->set_use_vsync(current_videomode.use_vsync);
-void OS_X11::_set_use_vsync(bool p_enable) {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- if (context_gles2)
- context_gles2->set_use_vsync(p_enable);
+ if (RasterizerGLES2::is_viable() == OK) {
+ RasterizerGLES2::register_config();
+ RasterizerGLES2::make_current();
+ } else {
+ memdelete(context_gles2);
+ context_gles2 = NULL;
+ ERR_FAIL_V(ERR_UNAVAILABLE);
+ }
}
#endif
-}
-
-void OS_X11::set_context(int p_context) {
- XClassHint *classHint = XAllocClassHint();
+ WindowID main_window = _create_window(p_mode, p_flags, Rect2i(Point2(), p_resolution));
+ for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
+ if (p_flags & (1 << i)) {
+ window_set_flag(WindowFlags(i), true, main_window);
+ }
+ }
- if (classHint) {
+//create RenderingDevice if used
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
- CharString name_str;
- switch (p_context) {
- case CONTEXT_EDITOR:
- name_str = "Godot_Editor";
- break;
- case CONTEXT_PROJECTMAN:
- name_str = "Godot_ProjectList";
- break;
- case CONTEXT_ENGINE:
- name_str = "Godot_Engine";
- break;
- }
+ //temporary
+ rendering_device_vulkan = memnew(RenderingDeviceVulkan);
+ rendering_device_vulkan->initialize(context_vulkan);
- CharString class_str;
- if (p_context == CONTEXT_ENGINE) {
- String config_name = GLOBAL_GET("application/config/name");
- if (config_name.length() == 0) {
- class_str = "Godot_Engine";
- } else {
- class_str = config_name.utf8();
- }
- } else {
- class_str = "Godot";
- }
+ RasterizerRD::make_current();
+ }
+#endif
- classHint->res_class = class_str.ptrw();
- classHint->res_name = name_str.ptrw();
+ /*
+ visual_server = memnew(VisualServerRaster);
+ if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
+ visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
+ }
+ */
- XSetClassHint(x11_display, x11_window, classHint);
- XFree(classHint);
+ {
+ //set all event master mask
+ XIEventMask all_master_event_mask;
+ static unsigned char all_master_mask_data[XIMaskLen(XI_LASTEVENT)] = {};
+ all_master_event_mask.deviceid = XIAllMasterDevices;
+ all_master_event_mask.mask_len = sizeof(all_master_mask_data);
+ all_master_event_mask.mask = all_master_mask_data;
+ XISetMask(all_master_event_mask.mask, XI_DeviceChanged);
+ XISetMask(all_master_event_mask.mask, XI_RawMotion);
+ XISelectEvents(x11_display, DefaultRootWindow(x11_display), &all_master_event_mask, 1);
}
-}
-void OS_X11::disable_crash_handler() {
- crash_handler.disable();
-}
+ // Disabled by now since grabbing also blocks mouse events
+ // (they are received as extended events instead of standard events)
+ /*XIClearMask(xi.touch_event_mask.mask, XI_TouchOwnership);
-bool OS_X11::is_disable_crash_handler() const {
- return crash_handler.is_disabled();
-}
+ // Grab touch devices to avoid OS gesture interference
+ for (int i = 0; i < xi.touch_devices.size(); ++i) {
+ XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask);
+ }*/
-static String get_mountpoint(const String &p_path) {
- struct stat s;
- if (stat(p_path.utf8().get_data(), &s)) {
- return "";
- }
+ cursor_size = XcursorGetDefaultSize(x11_display);
+ cursor_theme = XcursorGetTheme(x11_display);
-#ifdef HAVE_MNTENT
- dev_t dev = s.st_dev;
- FILE *fd = setmntent("/proc/mounts", "r");
- if (!fd) {
- return "";
+ if (!cursor_theme) {
+ print_verbose("XcursorGetTheme could not get cursor theme");
+ cursor_theme = "default";
}
- struct mntent mnt;
- char buf[1024];
- size_t buflen = 1024;
- while (getmntent_r(fd, &mnt, buf, buflen)) {
- if (!stat(mnt.mnt_dir, &s) && s.st_dev == dev) {
- endmntent(fd);
- return String(mnt.mnt_dir);
- }
+ for (int i = 0; i < CURSOR_MAX; i++) {
+
+ cursors[i] = None;
+ img[i] = NULL;
}
- endmntent(fd);
-#endif
- return "";
-}
+ current_cursor = CURSOR_ARROW;
-Error OS_X11::move_to_trash(const String &p_path) {
- String trash_can = "";
- String mnt = get_mountpoint(p_path);
+ for (int i = 0; i < CURSOR_MAX; i++) {
- // If there is a directory "[Mountpoint]/.Trash-[UID]/files", use it as the trash can.
- if (mnt != "") {
- String path(mnt + "/.Trash-" + itos(getuid()) + "/files");
- struct stat s;
- if (!stat(path.utf8().get_data(), &s)) {
- trash_can = path;
- }
- }
+ static const char *cursor_file[] = {
+ "left_ptr",
+ "xterm",
+ "hand2",
+ "cross",
+ "watch",
+ "left_ptr_watch",
+ "fleur",
+ "dnd-move",
+ "crossed_circle",
+ "v_double_arrow",
+ "h_double_arrow",
+ "size_bdiag",
+ "size_fdiag",
+ "move",
+ "row_resize",
+ "col_resize",
+ "question_arrow"
+ };
- // Otherwise, if ${XDG_DATA_HOME} is defined, use "${XDG_DATA_HOME}/Trash/files" as the trash can.
- if (trash_can == "") {
- char *dhome = getenv("XDG_DATA_HOME");
- if (dhome) {
- trash_can = String(dhome) + "/Trash/files";
- }
- }
+ img[i] = XcursorLibraryLoadImage(cursor_file[i], cursor_theme, cursor_size);
+ if (!img[i]) {
+ const char *fallback = NULL;
- // Otherwise, if ${HOME} is defined, use "${HOME}/.local/share/Trash/files" as the trash can.
- if (trash_can == "") {
- char *home = getenv("HOME");
- if (home) {
- trash_can = String(home) + "/.local/share/Trash/files";
+ switch (i) {
+ case CURSOR_POINTING_HAND:
+ fallback = "pointer";
+ break;
+ case CURSOR_CROSS:
+ fallback = "crosshair";
+ break;
+ case CURSOR_WAIT:
+ fallback = "wait";
+ break;
+ case CURSOR_BUSY:
+ fallback = "progress";
+ break;
+ case CURSOR_DRAG:
+ fallback = "grabbing";
+ break;
+ case CURSOR_CAN_DROP:
+ fallback = "hand1";
+ break;
+ case CURSOR_FORBIDDEN:
+ fallback = "forbidden";
+ break;
+ case CURSOR_VSIZE:
+ fallback = "ns-resize";
+ break;
+ case CURSOR_HSIZE:
+ fallback = "ew-resize";
+ break;
+ case CURSOR_BDIAGSIZE:
+ fallback = "fd_double_arrow";
+ break;
+ case CURSOR_FDIAGSIZE:
+ fallback = "bd_double_arrow";
+ break;
+ case CURSOR_MOVE:
+ img[i] = img[CURSOR_DRAG];
+ break;
+ case CURSOR_VSPLIT:
+ fallback = "sb_v_double_arrow";
+ break;
+ case CURSOR_HSPLIT:
+ fallback = "sb_h_double_arrow";
+ break;
+ case CURSOR_HELP:
+ fallback = "help";
+ break;
+ }
+ if (fallback != NULL) {
+ img[i] = XcursorLibraryLoadImage(fallback, cursor_theme, cursor_size);
+ }
+ }
+ if (img[i]) {
+ cursors[i] = XcursorImageLoadCursor(x11_display, img[i]);
+ } else {
+ print_verbose("Failed loading custom cursor: " + String(cursor_file[i]));
}
}
- // Issue an error if none of the previous locations is appropriate for the trash can.
- if (trash_can == "") {
- ERR_PRINT("move_to_trash: Could not determine the trash can location");
- return FAILED;
- }
+ {
+ // Creating an empty/transparent cursor
- // Create needed directories for decided trash can location.
- DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- Error err = dir_access->make_dir_recursive(trash_can);
- memdelete(dir_access);
+ // Create 1x1 bitmap
+ Pixmap cursormask = XCreatePixmap(x11_display,
+ RootWindow(x11_display, DefaultScreen(x11_display)), 1, 1, 1);
- // Issue an error if trash can is not created proprely.
- if (err != OK) {
- ERR_PRINT("move_to_trash: Could not create the trash can \"" + trash_can + "\"");
- return err;
- }
+ // Fill with zero
+ XGCValues xgc;
+ xgc.function = GXclear;
+ GC gc = XCreateGC(x11_display, cursormask, GCFunction, &xgc);
+ XFillRectangle(x11_display, cursormask, gc, 0, 0, 1, 1);
- // The trash can is successfully created, now move the given resource to it.
- // Do not use DirAccess:rename() because it can't move files across multiple mountpoints.
- List<String> mv_args;
- mv_args.push_back(p_path);
- mv_args.push_back(trash_can);
- int retval;
- err = execute("mv", mv_args, true, NULL, NULL, &retval);
+ // Color value doesn't matter. Mask zero means no foreground or background will be drawn
+ XColor col = {};
- // Issue an error if "mv" failed to move the given resource to the trash can.
- if (err != OK || retval != 0) {
- ERR_PRINT("move_to_trash: Could not move the resource \"" + p_path + "\" to the trash can \"" + trash_can + "\"");
- return FAILED;
- }
+ Cursor cursor = XCreatePixmapCursor(x11_display,
+ cursormask, // source (using cursor mask as placeholder, since it'll all be ignored)
+ cursormask, // mask
+ &col, &col, 0, 0);
- return OK;
-}
+ XFreePixmap(x11_display, cursormask);
+ XFreeGC(x11_display, gc);
-OS::LatinKeyboardVariant OS_X11::get_latin_keyboard_variant() const {
+ if (cursor == None) {
+ ERR_PRINT("FAILED CREATING CURSOR");
+ }
- XkbDescRec *xkbdesc = XkbAllocKeyboard();
- ERR_FAIL_COND_V(!xkbdesc, LATIN_KEYBOARD_QWERTY);
+ null_cursor = cursor;
+ }
+ cursor_set_shape(CURSOR_BUSY);
- XkbGetNames(x11_display, XkbSymbolsNameMask, xkbdesc);
- ERR_FAIL_COND_V(!xkbdesc->names, LATIN_KEYBOARD_QWERTY);
- ERR_FAIL_COND_V(!xkbdesc->names->symbols, LATIN_KEYBOARD_QWERTY);
+ requested = None;
- char *layout = XGetAtomName(x11_display, xkbdesc->names->symbols);
- ERR_FAIL_COND_V(!layout, LATIN_KEYBOARD_QWERTY);
+ window_has_focus = true; // Set focus to true at init
- Vector<String> info = String(layout).split("+");
- ERR_FAIL_INDEX_V(1, info.size(), LATIN_KEYBOARD_QWERTY);
+ /*if (p_desired.layered) {
+ set_window_per_pixel_transparency_enabled(true);
+ }*/
- if (info[1].find("colemak") != -1) {
- return LATIN_KEYBOARD_COLEMAK;
- } else if (info[1].find("qwertz") != -1) {
- return LATIN_KEYBOARD_QWERTZ;
- } else if (info[1].find("azerty") != -1) {
- return LATIN_KEYBOARD_AZERTY;
- } else if (info[1].find("qzerty") != -1) {
- return LATIN_KEYBOARD_QZERTY;
- } else if (info[1].find("dvorak") != -1) {
- return LATIN_KEYBOARD_DVORAK;
- } else if (info[1].find("neo") != -1) {
- return LATIN_KEYBOARD_NEO;
+ XEvent xevent;
+ while (XPending(x11_display) > 0) {
+ XNextEvent(x11_display, &xevent);
+ if (xevent.type == ConfigureNotify) {
+ _window_changed(&xevent);
+ }
}
- return LATIN_KEYBOARD_QWERTY;
+ _update_real_mouse_position(windows[MAIN_WINDOW_ID]);
+
+ r_error = OK;
}
+DisplayServerX11::~DisplayServerX11() {
-void OS_X11::update_real_mouse_position() {
- Window root_return, child_return;
- int root_x, root_y, win_x, win_y;
- unsigned int mask_return;
+ //destroy all windows
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+#ifdef VULKAN_ENABLED
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_destroy(E->key());
+ }
+#endif
- Bool xquerypointer_result = XQueryPointer(x11_display, x11_window, &root_return, &child_return, &root_x, &root_y,
- &win_x, &win_y, &mask_return);
+ if (E->get().xic) {
+ XDestroyIC(E->get().xic);
+ }
+ XUnmapWindow(x11_display, E->get().x11_window);
+ XDestroyWindow(x11_display, E->get().x11_window);
+ }
- if (xquerypointer_result) {
- if (win_x > 0 && win_y > 0 && win_x <= current_videomode.width && win_y <= current_videomode.height) {
+ //destroy drivers
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
- last_mouse_pos.x = win_x;
- last_mouse_pos.y = win_y;
- last_mouse_pos_valid = true;
- input->set_mouse_position(last_mouse_pos);
+ if (rendering_device_vulkan) {
+ rendering_device_vulkan->finalize();
+ memdelete(rendering_device_vulkan);
}
+
+ if (context_vulkan)
+ memdelete(context_vulkan);
}
-}
+#endif
-OS_X11::OS_X11() {
+ if (xrandr_handle)
+ dlclose(xrandr_handle);
-#ifdef PULSEAUDIO_ENABLED
- AudioDriverManager::add_driver(&driver_pulseaudio);
-#endif
+ for (int i = 0; i < CURSOR_MAX; i++) {
+ if (cursors[i] != None)
+ XFreeCursor(x11_display, cursors[i]);
+ if (img[i] != NULL)
+ XcursorImageDestroy(img[i]);
+ };
-#ifdef ALSA_ENABLED
- AudioDriverManager::add_driver(&driver_alsa);
-#endif
+ if (xim) {
+ XCloseIM(xim);
+ }
- xi.opcode = 0;
- xi.last_relative_time = 0;
- layered_window = false;
- minimized = false;
- window_focused = true;
- xim_style = 0L;
- mouse_mode = MOUSE_MODE_VISIBLE;
- last_position_before_fs = Vector2();
+ XCloseDisplay(x11_display);
+ if (xmbstring)
+ memfree(xmbstring);
}
+
+void DisplayServerX11::register_x11_driver() {
+
+ register_create_function("x11", create_func, get_rendering_drivers_func);
+}
+
+#endif // X11 enabled
diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h
new file mode 100644
index 0000000000..d1680b0508
--- /dev/null
+++ b/platform/linuxbsd/display_server_x11.h
@@ -0,0 +1,352 @@
+/*************************************************************************/
+/* display_server_x11.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef DISPLAY_SERVER_X11_H
+#define DISPLAY_SERVER_X11_H
+
+#ifdef X11_ENABLED
+
+#include "servers/display_server.h"
+
+#include "core/input/input_filter.h"
+
+#include "drivers/alsa/audio_driver_alsa.h"
+#include "drivers/alsamidi/midi_driver_alsamidi.h"
+#include "drivers/pulseaudio/audio_driver_pulseaudio.h"
+#include "drivers/unix/os_unix.h"
+#include "joypad_linux.h"
+#include "servers/audio_server.h"
+#include "servers/visual/rasterizer.h"
+#include "servers/visual_server.h"
+
+#if defined(OPENGL_ENABLED)
+#include "context_gl_x11.h"
+#endif
+
+#if defined(VULKAN_ENABLED)
+#include "drivers/vulkan/rendering_device_vulkan.h"
+#include "platform/linuxbsd/vulkan_context_x11.h"
+#endif
+
+#include <X11/Xcursor/Xcursor.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XInput2.h>
+#include <X11/extensions/Xrandr.h>
+#include <X11/keysym.h>
+
+// Hints for X11 fullscreen
+typedef struct {
+ unsigned long flags;
+ unsigned long functions;
+ unsigned long decorations;
+ long inputMode;
+ unsigned long status;
+} Hints;
+
+typedef struct _xrr_monitor_info {
+ Atom name;
+ Bool primary;
+ Bool automatic;
+ int noutput;
+ int x;
+ int y;
+ int width;
+ int height;
+ int mwidth;
+ int mheight;
+ RROutput *outputs;
+} xrr_monitor_info;
+
+#undef CursorShape
+
+class DisplayServerX11 : public DisplayServer {
+ //No need to register, it's platform-specific and nothing is added
+ //GDCLASS(DisplayServerX11, DisplayServer)
+
+ _THREAD_SAFE_CLASS_
+
+ Atom wm_delete;
+ Atom xdnd_enter;
+ Atom xdnd_position;
+ Atom xdnd_status;
+ Atom xdnd_action_copy;
+ Atom xdnd_drop;
+ Atom xdnd_finished;
+ Atom xdnd_selection;
+ Atom xdnd_aware;
+ Atom requested;
+ int xdnd_version;
+
+#if defined(OPENGL_ENABLED)
+ ContextGL_X11 *context_gles2;
+#endif
+#if defined(VULKAN_ENABLED)
+ VulkanContextX11 *context_vulkan;
+ RenderingDeviceVulkan *rendering_device_vulkan;
+#endif
+
+ struct WindowData {
+ Window x11_window;
+ ::XIC xic;
+
+ Size2i min_size;
+ Size2i max_size;
+ Point2i position;
+ Size2i size;
+ Point2i im_position;
+ bool im_active = false;
+ Callable rect_changed_callback;
+ Callable event_callback;
+ Callable input_event_callback;
+ Callable input_text_callback;
+ Callable drop_files_callback;
+
+ WindowID transient_parent = INVALID_WINDOW_ID;
+ Set<WindowID> transient_children;
+
+ ObjectID instance_id;
+
+ //better to guess on the fly, given WM can change it
+ //WindowMode mode;
+ bool fullscreen = false; //OS can't exit from this mode
+ bool on_top = false;
+ bool borderless = false;
+ bool resize_disabled = false;
+ Vector2i last_position_before_fs;
+ };
+
+ Map<WindowID, WindowData> windows;
+
+ WindowID window_id_counter = MAIN_WINDOW_ID;
+ WindowID _create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect);
+
+ String internal_clipboard;
+ Window xdnd_source_window;
+ ::Display *x11_display;
+ char *xmbstring;
+ int xmblen;
+ unsigned long last_timestamp;
+ ::Time last_keyrelease_time;
+ ::XIM xim;
+ ::XIMStyle xim_style;
+ static void _xim_destroy_callback(::XIM im, ::XPointer client_data,
+ ::XPointer call_data);
+
+ Point2i last_mouse_pos;
+ bool last_mouse_pos_valid;
+ Point2i last_click_pos;
+ uint64_t last_click_ms;
+ int last_click_button_index;
+ uint32_t last_button_state;
+
+ struct {
+ int opcode;
+ Vector<int> touch_devices;
+ Map<int, Vector2> absolute_devices;
+ Map<int, Vector3> pen_devices;
+ XIEventMask all_event_mask;
+ Map<int, Vector2> state;
+ double pressure;
+ Vector2 tilt;
+ Vector2 mouse_pos_to_filter;
+ Vector2 relative_motion;
+ Vector2 raw_pos;
+ Vector2 old_raw_pos;
+ ::Time last_relative_time;
+ } xi;
+
+ bool _refresh_device_info();
+
+ unsigned int _get_mouse_button_state(unsigned int p_x11_button, int p_x11_type);
+ void _get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state);
+ void _flush_mouse_motion();
+
+ MouseMode mouse_mode;
+ Point2i center;
+
+ void _handle_key_event(WindowID p_window, XKeyEvent *p_event, bool p_echo = false);
+
+ bool force_quit;
+ bool minimized;
+ bool window_has_focus;
+ bool do_mouse_warp;
+
+ const char *cursor_theme;
+ int cursor_size;
+ XcursorImage *img[CURSOR_MAX];
+ Cursor cursors[CURSOR_MAX];
+ Cursor null_cursor;
+ CursorShape current_cursor;
+ Map<CursorShape, Vector<Variant>> cursors_cache;
+
+ bool layered_window;
+
+ String rendering_driver;
+ bool window_focused;
+ //void set_wm_border(bool p_enabled);
+ void set_wm_fullscreen(bool p_enabled);
+ void set_wm_above(bool p_enabled);
+
+ typedef xrr_monitor_info *(*xrr_get_monitors_t)(Display *dpy, Window window, Bool get_active, int *nmonitors);
+ typedef void (*xrr_free_monitors_t)(xrr_monitor_info *monitors);
+ xrr_get_monitors_t xrr_get_monitors;
+ xrr_free_monitors_t xrr_free_monitors;
+ void *xrandr_handle;
+ Bool xrandr_ext_ok;
+
+ struct Property {
+ unsigned char *data;
+ int format, nitems;
+ Atom type;
+ };
+ static Property _read_property(Display *p_display, Window p_window, Atom p_property);
+
+ void _update_real_mouse_position(const WindowData &wd);
+ void _set_wm_fullscreen(WindowID p_window, bool p_enabled);
+ void _set_wm_maximized(WindowID p_window, bool p_enabled);
+
+ void _update_context(WindowData &wd);
+
+ Context context = CONTEXT_ENGINE;
+
+ void _send_window_event(const WindowData &wd, WindowEvent p_event);
+ static void _dispatch_input_events(const Ref<InputEvent> &p_event);
+ void _dispatch_input_event(const Ref<InputEvent> &p_event);
+
+protected:
+ void _window_changed(XEvent *event);
+
+public:
+ virtual bool has_feature(Feature p_feature) const;
+ virtual String get_name() const;
+
+ virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
+
+ virtual void mouse_set_mode(MouseMode p_mode);
+ virtual MouseMode mouse_get_mode() const;
+
+ virtual void mouse_warp_to_position(const Point2i &p_to);
+ virtual Point2i mouse_get_position() const;
+ virtual Point2i mouse_get_absolute_position() const;
+ virtual int mouse_get_button_state() const;
+
+ virtual void clipboard_set(const String &p_text);
+ virtual String clipboard_get() const;
+
+ virtual int get_screen_count() const;
+ virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+ virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+ virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+ virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+ virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+
+ virtual Vector<DisplayServer::WindowID> get_window_list() const;
+
+ 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 WindowID get_window_at_screen_position(const Point2i &p_position) const;
+
+ virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID);
+ virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
+ virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_set_transient(WindowID p_window, WindowID p_parent);
+
+ virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
+ virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
+ virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual Size2i window_get_real_size(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID);
+ virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID);
+ virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual bool can_any_window_draw() const;
+
+ virtual void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual void cursor_set_shape(CursorShape p_shape);
+ virtual CursorShape cursor_get_shape() const;
+ virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
+
+ virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
+
+ virtual void process_events();
+
+ virtual void release_rendering_thread();
+ virtual void make_rendering_thread();
+ virtual void swap_buffers();
+
+ virtual void set_context(Context p_context);
+
+ virtual void set_native_icon(const String &p_filename);
+ virtual void set_icon(const Ref<Image> &p_icon);
+
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static Vector<String> get_rendering_drivers_func();
+
+ static void register_x11_driver();
+
+ DisplayServerX11(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ ~DisplayServerX11();
+};
+
+#endif // X11 enabled
+
+#endif // DISPLAY_SERVER_X11_H
diff --git a/platform/x11/export/export.cpp b/platform/linuxbsd/export/export.cpp
index 1c0c6ec096..53e3ce8f85 100644
--- a/platform/x11/export/export.cpp
+++ b/platform/linuxbsd/export/export.cpp
@@ -32,17 +32,17 @@
#include "core/os/file_access.h"
#include "editor/editor_export.h"
-#include "platform/x11/logo.gen.h"
+#include "platform/linuxbsd/logo.gen.h"
#include "scene/resources/texture.h"
static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size);
-void register_x11_exporter() {
+void register_linuxbsd_exporter() {
Ref<EditorExportPlatformPC> platform;
platform.instance();
- Ref<Image> img = memnew(Image(_x11_logo));
+ Ref<Image> img = memnew(Image(_linuxbsd_logo));
Ref<ImageTexture> logo;
logo.instance();
logo->create_from_image(img);
diff --git a/platform/x11/export/export.h b/platform/linuxbsd/export/export.h
index 4049e6a8bf..5ee81f485e 100644
--- a/platform/x11/export/export.h
+++ b/platform/linuxbsd/export/export.h
@@ -28,9 +28,9 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef X11_EXPORT_H
-#define X11_EXPORT_H
+#ifndef LINUXBSD_EXPORT_H
+#define LINUXBSD_EXPORT_H
-void register_x11_exporter();
+void register_linuxbsd_exporter();
-#endif // X11_EXPORT_H
+#endif // LINUXBSD_EXPORT_H
diff --git a/platform/x11/godot_x11.cpp b/platform/linuxbsd/godot_linuxbsd.cpp
index 77b74184ad..710ba3ca40 100644
--- a/platform/x11/godot_x11.cpp
+++ b/platform/linuxbsd/godot_linuxbsd.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* godot_x11.cpp */
+/* godot_linuxbsd.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -34,11 +34,11 @@
#include <unistd.h>
#include "main/main.h"
-#include "os_x11.h"
+#include "os_linuxbsd.h"
int main(int argc, char *argv[]) {
- OS_X11 os;
+ OS_LinuxBSD os;
setlocale(LC_CTYPE, "");
diff --git a/platform/x11/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 1aacc6a250..c4c793093d 100644
--- a/platform/x11/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -71,7 +71,7 @@ void JoypadLinux::Joypad::reset() {
dpad = 0;
fd = -1;
- InputDefault::JoyAxis jx;
+ InputFilter::JoyAxis jx;
jx.min = -1;
jx.value = 0.0f;
for (int i = 0; i < MAX_ABS; i++) {
@@ -80,7 +80,7 @@ void JoypadLinux::Joypad::reset() {
}
}
-JoypadLinux::JoypadLinux(InputDefault *in) {
+JoypadLinux::JoypadLinux(InputFilter *in) {
exit_udev = false;
input = in;
joy_thread = Thread::create(joy_thread_func, this);
@@ -436,11 +436,11 @@ void JoypadLinux::joypad_vibration_stop(int p_id, uint64_t p_timestamp) {
joy.ff_effect_timestamp = p_timestamp;
}
-InputDefault::JoyAxis JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const {
+InputFilter::JoyAxis JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const {
int min = p_abs->minimum;
int max = p_abs->maximum;
- InputDefault::JoyAxis jx;
+ InputFilter::JoyAxis jx;
if (min < 0) {
jx.min = -1;
@@ -492,11 +492,11 @@ void JoypadLinux::process_joypads() {
case ABS_HAT0X:
if (ev.value != 0) {
if (ev.value < 0)
- joy->dpad |= InputDefault::HAT_MASK_LEFT;
+ joy->dpad |= InputFilter::HAT_MASK_LEFT;
else
- joy->dpad |= InputDefault::HAT_MASK_RIGHT;
+ joy->dpad |= InputFilter::HAT_MASK_RIGHT;
} else
- joy->dpad &= ~(InputDefault::HAT_MASK_LEFT | InputDefault::HAT_MASK_RIGHT);
+ joy->dpad &= ~(InputFilter::HAT_MASK_LEFT | InputFilter::HAT_MASK_RIGHT);
input->joy_hat(i, joy->dpad);
break;
@@ -504,11 +504,11 @@ void JoypadLinux::process_joypads() {
case ABS_HAT0Y:
if (ev.value != 0) {
if (ev.value < 0)
- joy->dpad |= InputDefault::HAT_MASK_UP;
+ joy->dpad |= InputFilter::HAT_MASK_UP;
else
- joy->dpad |= InputDefault::HAT_MASK_DOWN;
+ joy->dpad |= InputFilter::HAT_MASK_DOWN;
} else
- joy->dpad &= ~(InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_DOWN);
+ joy->dpad &= ~(InputFilter::HAT_MASK_UP | InputFilter::HAT_MASK_DOWN);
input->joy_hat(i, joy->dpad);
break;
@@ -517,7 +517,7 @@ void JoypadLinux::process_joypads() {
if (ev.code >= MAX_ABS)
return;
if (joy->abs_map[ev.code] != -1 && joy->abs_info[ev.code]) {
- InputDefault::JoyAxis value = axis_correct(joy->abs_info[ev.code], ev.value);
+ InputFilter::JoyAxis value = axis_correct(joy->abs_info[ev.code], ev.value);
joy->curr_axis[joy->abs_map[ev.code]] = value;
}
break;
diff --git a/platform/x11/joypad_linux.h b/platform/linuxbsd/joypad_linux.h
index d5719b6dbe..1d2ed5bbc1 100644
--- a/platform/x11/joypad_linux.h
+++ b/platform/linuxbsd/joypad_linux.h
@@ -33,15 +33,15 @@
#define JOYPAD_LINUX_H
#ifdef JOYDEV_ENABLED
+#include "core/input/input_filter.h"
#include "core/os/mutex.h"
#include "core/os/thread.h"
-#include "main/input_default.h"
struct input_absinfo;
class JoypadLinux {
public:
- JoypadLinux(InputDefault *in);
+ JoypadLinux(InputFilter *in);
~JoypadLinux();
void process_joypads();
@@ -53,7 +53,7 @@ private:
};
struct Joypad {
- InputDefault::JoyAxis curr_axis[MAX_ABS];
+ InputFilter::JoyAxis curr_axis[MAX_ABS];
int key_map[MAX_KEY];
int abs_map[MAX_ABS];
int dpad;
@@ -74,7 +74,7 @@ private:
bool exit_udev;
Mutex joy_mutex;
Thread *joy_thread;
- InputDefault *input;
+ InputFilter *input;
Joypad joypads[JOYPADS_MAX];
Vector<String> attached_devices;
@@ -95,7 +95,7 @@ private:
void joypad_vibration_start(int p_id, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
void joypad_vibration_stop(int p_id, uint64_t p_timestamp);
- InputDefault::JoyAxis axis_correct(const input_absinfo *p_abs, int p_value) const;
+ InputFilter::JoyAxis axis_correct(const input_absinfo *p_abs, int p_value) const;
};
#endif
diff --git a/platform/x11/key_mapping_x11.cpp b/platform/linuxbsd/key_mapping_x11.cpp
index 78bd2b71a0..78bd2b71a0 100644
--- a/platform/x11/key_mapping_x11.cpp
+++ b/platform/linuxbsd/key_mapping_x11.cpp
diff --git a/platform/x11/key_mapping_x11.h b/platform/linuxbsd/key_mapping_x11.h
index 10db43bcc4..10db43bcc4 100644
--- a/platform/x11/key_mapping_x11.h
+++ b/platform/linuxbsd/key_mapping_x11.h
diff --git a/platform/x11/logo.png b/platform/linuxbsd/logo.png
index 078654b757..078654b757 100644
--- a/platform/x11/logo.png
+++ b/platform/linuxbsd/logo.png
Binary files differ
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
new file mode 100644
index 0000000000..084453bdc6
--- /dev/null
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -0,0 +1,381 @@
+/*************************************************************************/
+/* os_linuxbsd.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "os_linuxbsd.h"
+
+#include "core/os/dir_access.h"
+#include "core/print_string.h"
+#include "errno.h"
+
+#ifdef HAVE_MNTENT
+#include <mntent.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "main/main.h"
+
+#ifdef X11_ENABLED
+#include "display_server_x11.h"
+#endif
+
+void OS_LinuxBSD::initialize() {
+
+ crash_handler.initialize();
+
+ OS_Unix::initialize_core();
+}
+
+void OS_LinuxBSD::initialize_joypads() {
+
+#ifdef JOYDEV_ENABLED
+ joypad = memnew(JoypadLinux(InputFilter::get_singleton()));
+#endif
+}
+
+String OS_LinuxBSD::get_unique_id() const {
+
+ static String machine_id;
+ if (machine_id.empty()) {
+ if (FileAccess *f = FileAccess::open("/etc/machine-id", FileAccess::READ)) {
+ while (machine_id.empty() && !f->eof_reached()) {
+ machine_id = f->get_line().strip_edges();
+ }
+ f->close();
+ memdelete(f);
+ }
+ }
+ return machine_id;
+}
+
+void OS_LinuxBSD::finalize() {
+
+ if (main_loop)
+ memdelete(main_loop);
+ main_loop = NULL;
+
+#ifdef ALSAMIDI_ENABLED
+ driver_alsamidi.close();
+#endif
+
+#ifdef JOYDEV_ENABLED
+ memdelete(joypad);
+#endif
+}
+
+MainLoop *OS_LinuxBSD::get_main_loop() const {
+
+ return main_loop;
+}
+
+void OS_LinuxBSD::delete_main_loop() {
+
+ if (main_loop)
+ memdelete(main_loop);
+ main_loop = NULL;
+}
+
+void OS_LinuxBSD::set_main_loop(MainLoop *p_main_loop) {
+
+ main_loop = p_main_loop;
+}
+
+String OS_LinuxBSD::get_name() const {
+
+#ifdef __linux__
+ return "Linux";
+#elif defined(__FreeBSD__)
+ return "FreeBSD";
+#elif defined(__NetBSD__)
+ return "NetBSD";
+#else
+ return "BSD";
+#endif
+}
+
+Error OS_LinuxBSD::shell_open(String p_uri) {
+
+ Error ok;
+ List<String> args;
+ args.push_back(p_uri);
+ ok = execute("xdg-open", args, false);
+ if (ok == OK)
+ return OK;
+ ok = execute("gnome-open", args, false);
+ if (ok == OK)
+ return OK;
+ ok = execute("kde-open", args, false);
+ return ok;
+}
+
+bool OS_LinuxBSD::_check_internal_feature_support(const String &p_feature) {
+
+ return p_feature == "pc";
+}
+
+String OS_LinuxBSD::get_config_path() const {
+
+ if (has_environment("XDG_CONFIG_HOME")) {
+ return get_environment("XDG_CONFIG_HOME");
+ } else if (has_environment("HOME")) {
+ return get_environment("HOME").plus_file(".config");
+ } else {
+ return ".";
+ }
+}
+
+String OS_LinuxBSD::get_data_path() const {
+
+ if (has_environment("XDG_DATA_HOME")) {
+ return get_environment("XDG_DATA_HOME");
+ } else if (has_environment("HOME")) {
+ return get_environment("HOME").plus_file(".local/share");
+ } else {
+ return get_config_path();
+ }
+}
+
+String OS_LinuxBSD::get_cache_path() const {
+
+ if (has_environment("XDG_CACHE_HOME")) {
+ return get_environment("XDG_CACHE_HOME");
+ } else if (has_environment("HOME")) {
+ return get_environment("HOME").plus_file(".cache");
+ } else {
+ return get_config_path();
+ }
+}
+
+String OS_LinuxBSD::get_system_dir(SystemDir p_dir) const {
+
+ String xdgparam;
+
+ switch (p_dir) {
+ case SYSTEM_DIR_DESKTOP: {
+
+ xdgparam = "DESKTOP";
+ } break;
+ case SYSTEM_DIR_DCIM: {
+
+ xdgparam = "PICTURES";
+
+ } break;
+ case SYSTEM_DIR_DOCUMENTS: {
+
+ xdgparam = "DOCUMENTS";
+
+ } break;
+ case SYSTEM_DIR_DOWNLOADS: {
+
+ xdgparam = "DOWNLOAD";
+
+ } break;
+ case SYSTEM_DIR_MOVIES: {
+
+ xdgparam = "VIDEOS";
+
+ } break;
+ case SYSTEM_DIR_MUSIC: {
+
+ xdgparam = "MUSIC";
+
+ } break;
+ case SYSTEM_DIR_PICTURES: {
+
+ xdgparam = "PICTURES";
+
+ } break;
+ case SYSTEM_DIR_RINGTONES: {
+
+ xdgparam = "MUSIC";
+
+ } break;
+ }
+
+ String pipe;
+ List<String> arg;
+ arg.push_back(xdgparam);
+ Error err = const_cast<OS_LinuxBSD *>(this)->execute("xdg-user-dir", arg, true, NULL, &pipe);
+ if (err != OK)
+ return ".";
+ return pipe.strip_edges();
+}
+
+void OS_LinuxBSD::run() {
+
+ force_quit = false;
+
+ if (!main_loop)
+ return;
+
+ main_loop->init();
+
+ //uint64_t last_ticks=get_ticks_usec();
+
+ //int frames=0;
+ //uint64_t frame=0;
+
+ while (!force_quit) {
+
+ DisplayServer::get_singleton()->process_events(); // get rid of pending events
+#ifdef JOYDEV_ENABLED
+ joypad->process_joypads();
+#endif
+ if (Main::iteration())
+ break;
+ };
+
+ main_loop->finish();
+}
+
+void OS_LinuxBSD::disable_crash_handler() {
+ crash_handler.disable();
+}
+
+bool OS_LinuxBSD::is_disable_crash_handler() const {
+ return crash_handler.is_disabled();
+}
+
+static String get_mountpoint(const String &p_path) {
+ struct stat s;
+ if (stat(p_path.utf8().get_data(), &s)) {
+ return "";
+ }
+
+#ifdef HAVE_MNTENT
+ dev_t dev = s.st_dev;
+ FILE *fd = setmntent("/proc/mounts", "r");
+ if (!fd) {
+ return "";
+ }
+
+ struct mntent mnt;
+ char buf[1024];
+ size_t buflen = 1024;
+ while (getmntent_r(fd, &mnt, buf, buflen)) {
+ if (!stat(mnt.mnt_dir, &s) && s.st_dev == dev) {
+ endmntent(fd);
+ return String(mnt.mnt_dir);
+ }
+ }
+
+ endmntent(fd);
+#endif
+ return "";
+}
+
+Error OS_LinuxBSD::move_to_trash(const String &p_path) {
+ String trash_can = "";
+ String mnt = get_mountpoint(p_path);
+
+ // If there is a directory "[Mountpoint]/.Trash-[UID]/files", use it as the trash can.
+ if (mnt != "") {
+ String path(mnt + "/.Trash-" + itos(getuid()) + "/files");
+ struct stat s;
+ if (!stat(path.utf8().get_data(), &s)) {
+ trash_can = path;
+ }
+ }
+
+ // Otherwise, if ${XDG_DATA_HOME} is defined, use "${XDG_DATA_HOME}/Trash/files" as the trash can.
+ if (trash_can == "") {
+ char *dhome = getenv("XDG_DATA_HOME");
+ if (dhome) {
+ trash_can = String(dhome) + "/Trash/files";
+ }
+ }
+
+ // Otherwise, if ${HOME} is defined, use "${HOME}/.local/share/Trash/files" as the trash can.
+ if (trash_can == "") {
+ char *home = getenv("HOME");
+ if (home) {
+ trash_can = String(home) + "/.local/share/Trash/files";
+ }
+ }
+
+ // Issue an error if none of the previous locations is appropriate for the trash can.
+ if (trash_can == "") {
+ ERR_PRINT("move_to_trash: Could not determine the trash can location");
+ return FAILED;
+ }
+
+ // Create needed directories for decided trash can location.
+ DirAccess *dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ Error err = dir_access->make_dir_recursive(trash_can);
+ memdelete(dir_access);
+
+ // Issue an error if trash can is not created proprely.
+ if (err != OK) {
+ ERR_PRINT("move_to_trash: Could not create the trash can \"" + trash_can + "\"");
+ return err;
+ }
+
+ // The trash can is successfully created, now move the given resource to it.
+ // Do not use DirAccess:rename() because it can't move files across multiple mountpoints.
+ List<String> mv_args;
+ mv_args.push_back(p_path);
+ mv_args.push_back(trash_can);
+ int retval;
+ err = execute("mv", mv_args, true, NULL, NULL, &retval);
+
+ // Issue an error if "mv" failed to move the given resource to the trash can.
+ if (err != OK || retval != 0) {
+ ERR_PRINT("move_to_trash: Could not move the resource \"" + p_path + "\" to the trash can \"" + trash_can + "\"");
+ return FAILED;
+ }
+
+ return OK;
+}
+
+OS_LinuxBSD::OS_LinuxBSD() {
+
+ main_loop = NULL;
+ force_quit = false;
+
+#ifdef PULSEAUDIO_ENABLED
+ AudioDriverManager::add_driver(&driver_pulseaudio);
+#endif
+
+#ifdef ALSA_ENABLED
+ AudioDriverManager::add_driver(&driver_alsa);
+#endif
+
+#ifdef X11_ENABLED
+ DisplayServerX11::register_x11_driver();
+#endif
+}
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
new file mode 100644
index 0000000000..6c656c1945
--- /dev/null
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -0,0 +1,106 @@
+/*************************************************************************/
+/* os_linuxbsd.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef OS_LINUXBSD_H
+#define OS_LINUXBSD_H
+
+#include "core/input/input_filter.h"
+#include "crash_handler_linuxbsd.h"
+#include "drivers/alsa/audio_driver_alsa.h"
+#include "drivers/alsamidi/midi_driver_alsamidi.h"
+#include "drivers/pulseaudio/audio_driver_pulseaudio.h"
+#include "drivers/unix/os_unix.h"
+#include "joypad_linux.h"
+#include "servers/audio_server.h"
+#include "servers/visual/rasterizer.h"
+#include "servers/visual_server.h"
+
+class OS_LinuxBSD : public OS_Unix {
+
+ virtual void delete_main_loop();
+
+ bool force_quit;
+
+#ifdef JOYDEV_ENABLED
+ JoypadLinux *joypad;
+#endif
+
+#ifdef ALSA_ENABLED
+ AudioDriverALSA driver_alsa;
+#endif
+
+#ifdef ALSAMIDI_ENABLED
+ MIDIDriverALSAMidi driver_alsamidi;
+#endif
+
+#ifdef PULSEAUDIO_ENABLED
+ AudioDriverPulseAudio driver_pulseaudio;
+#endif
+
+ CrashHandler crash_handler;
+
+ MainLoop *main_loop;
+
+protected:
+ virtual void initialize();
+ virtual void finalize();
+
+ virtual void initialize_joypads();
+
+ virtual void set_main_loop(MainLoop *p_main_loop);
+
+public:
+ virtual String get_name() const;
+
+ virtual MainLoop *get_main_loop() const;
+
+ virtual String get_config_path() const;
+ virtual String get_data_path() const;
+ virtual String get_cache_path() const;
+
+ virtual String get_system_dir(SystemDir p_dir) const;
+
+ virtual Error shell_open(String p_uri);
+
+ virtual String get_unique_id() const;
+
+ virtual bool _check_internal_feature_support(const String &p_feature);
+
+ void run();
+
+ void disable_crash_handler();
+ bool is_disable_crash_handler() const;
+
+ virtual Error move_to_trash(const String &p_path);
+
+ OS_LinuxBSD();
+};
+
+#endif
diff --git a/platform/x11/pck_embed.ld b/platform/linuxbsd/pck_embed.ld
index 57a1994043..57a1994043 100644
--- a/platform/x11/pck_embed.ld
+++ b/platform/linuxbsd/pck_embed.ld
diff --git a/platform/x11/pck_embed.legacy.ld b/platform/linuxbsd/pck_embed.legacy.ld
index a23013ba7a..a23013ba7a 100644
--- a/platform/x11/pck_embed.legacy.ld
+++ b/platform/linuxbsd/pck_embed.legacy.ld
diff --git a/platform/x11/platform_config.h b/platform/linuxbsd/platform_config.h
index ac30519132..ac30519132 100644
--- a/platform/x11/platform_config.h
+++ b/platform/linuxbsd/platform_config.h
diff --git a/platform/x11/platform_x11_builders.py b/platform/linuxbsd/platform_linuxbsd_builders.py
index 5ff0c6fb14..a72306a9c0 100644
--- a/platform/x11/platform_x11_builders.py
+++ b/platform/linuxbsd/platform_linuxbsd_builders.py
@@ -7,7 +7,7 @@ import os
from platform_methods import subprocess_main
-def make_debug_x11(target, source, env):
+def make_debug_linuxbsd(target, source, env):
os.system('objcopy --only-keep-debug {0} {0}.debugsymbols'.format(target[0]))
os.system('strip --strip-debug --strip-unneeded {0}'.format(target[0]))
os.system('objcopy --add-gnu-debuglink={0}.debugsymbols {0}'.format(target[0]))
diff --git a/platform/x11/vulkan_context_x11.cpp b/platform/linuxbsd/vulkan_context_x11.cpp
index 602dbc77a2..d0e1b1678c 100644
--- a/platform/x11/vulkan_context_x11.cpp
+++ b/platform/linuxbsd/vulkan_context_x11.cpp
@@ -35,7 +35,7 @@ const char *VulkanContextX11::_get_platform_surface_extension() const {
return VK_KHR_XLIB_SURFACE_EXTENSION_NAME;
}
-int VulkanContextX11::window_create(::Window p_window, Display *p_display, int p_width, int p_height) {
+Error VulkanContextX11::window_create(DisplayServer::WindowID p_window_id, ::Window p_window, Display *p_display, int p_width, int p_height) {
VkXlibSurfaceCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
@@ -46,8 +46,8 @@ int VulkanContextX11::window_create(::Window p_window, Display *p_display, int p
VkSurfaceKHR surface;
VkResult err = vkCreateXlibSurfaceKHR(_get_instance(), &createInfo, NULL, &surface);
- ERR_FAIL_COND_V(err, -1);
- return _window_create(surface, p_width, p_height);
+ ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
+ return _window_create(p_window_id, surface, p_width, p_height);
}
VulkanContextX11::VulkanContextX11() {
diff --git a/platform/x11/vulkan_context_x11.h b/platform/linuxbsd/vulkan_context_x11.h
index 573f994ea6..6e144ab2d9 100644
--- a/platform/x11/vulkan_context_x11.h
+++ b/platform/linuxbsd/vulkan_context_x11.h
@@ -39,7 +39,7 @@ class VulkanContextX11 : public VulkanContext {
virtual const char *_get_platform_surface_extension() const;
public:
- int window_create(::Window p_window, Display *p_display, int p_width, int p_height);
+ Error window_create(DisplayServer::WindowID p_window_id, ::Window p_window, Display *p_display, int p_width, int p_height);
VulkanContextX11();
~VulkanContextX11();
diff --git a/platform/osx/SCsub b/platform/osx/SCsub
index 0a4e0a45e1..4ec8aeab6d 100644
--- a/platform/osx/SCsub
+++ b/platform/osx/SCsub
@@ -8,6 +8,7 @@ import platform_osx_builders
files = [
'crash_handler_osx.mm',
'os_osx.mm',
+ 'display_server_osx.mm',
'godot_main_osx.mm',
'dir_access_osx.mm',
'joypad_osx.cpp',
diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h
new file mode 100644
index 0000000000..d0e2babd06
--- /dev/null
+++ b/platform/osx/display_server_osx.h
@@ -0,0 +1,306 @@
+/*************************************************************************/
+/* display_server_osx.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef DISPLAY_SERVER_OSX_H
+#define DISPLAY_SERVER_OSX_H
+
+#define BitMap _QDBitMap // Suppress deprecated QuickDraw definition.
+
+#include "core/input/input_filter.h"
+#include "servers/display_server.h"
+
+#if defined(OPENGL_ENABLED)
+#include "context_gl_osx.h"
+//TODO - reimplement OpenGLES
+#endif
+
+#if defined(VULKAN_ENABLED)
+#include "drivers/vulkan/rendering_device_vulkan.h"
+#include "platform/osx/vulkan_context_osx.h"
+#endif
+
+#include <AppKit/AppKit.h>
+#include <AppKit/NSCursor.h>
+#include <ApplicationServices/ApplicationServices.h>
+#include <CoreVideo/CoreVideo.h>
+
+#undef BitMap
+#undef CursorShape
+
+class DisplayServerOSX : public DisplayServer {
+ GDCLASS(DisplayServerOSX, DisplayServer)
+
+ _THREAD_SAFE_CLASS_
+
+public:
+#if defined(OPENGL_ENABLED)
+ ContextGL_OSX *context_gles2;
+#endif
+#if defined(VULKAN_ENABLED)
+ VulkanContextOSX *context_vulkan;
+ RenderingDeviceVulkan *rendering_device_vulkan;
+#endif
+
+ const NSMenu *_get_menu_root(const String &p_menu_root) const;
+ NSMenu *_get_menu_root(const String &p_menu_root);
+
+ NSMenu *apple_menu = NULL;
+ NSMenu *dock_menu = NULL;
+ Map<String, NSMenu *> submenu;
+
+ struct KeyEvent {
+ WindowID window_id;
+ unsigned int osx_state;
+ bool pressed;
+ bool echo;
+ bool raw;
+ uint32_t keycode;
+ uint32_t physical_keycode;
+ uint32_t unicode;
+ };
+
+ Vector<KeyEvent> key_event_buffer;
+ int key_event_pos;
+
+ struct WindowData {
+ id window_delegate;
+ id window_object;
+ id window_view;
+
+#if defined(OPENGL_ENABLED)
+ ContextGL_OSX *context_gles2 = NULL;
+#endif
+ Point2i mouse_pos;
+
+ Size2i min_size;
+ Size2i max_size;
+ Size2i size;
+
+ bool mouse_down_control = false;
+
+ bool im_active = false;
+ Size2i im_position;
+
+ Callable rect_changed_callback;
+ Callable event_callback;
+ Callable input_event_callback;
+ Callable input_text_callback;
+ Callable drop_files_callback;
+
+ ObjectID instance_id;
+
+ WindowID transient_parent = INVALID_WINDOW_ID;
+ Set<WindowID> transient_children;
+
+ bool layered_window = false;
+ bool fullscreen = false;
+ bool on_top = false;
+ bool borderless = false;
+ bool resize_disabled = false;
+ };
+
+ Point2i im_selection;
+ String im_text;
+
+ Map<WindowID, WindowData> windows;
+
+ WindowID window_id_counter = MAIN_WINDOW_ID;
+
+ WindowID _create_window(WindowMode p_mode, const Rect2i &p_rect);
+ void _update_window(WindowData p_wd);
+ void _send_window_event(const WindowData &wd, WindowEvent p_event);
+ static void _dispatch_input_events(const Ref<InputEvent> &p_event);
+ void _dispatch_input_event(const Ref<InputEvent> &p_event);
+ WindowID _find_window_id(id p_window);
+
+ void _set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window);
+
+ float _display_scale(id screen) const;
+ Point2i _get_screens_origin() const;
+ Point2i _get_native_screen_position(int p_screen) const;
+
+ void _push_input(const Ref<InputEvent> &p_event);
+ void _process_key_events();
+
+ String rendering_driver;
+
+ id delegate;
+ id autoreleasePool;
+ CGEventSourceRef eventSource;
+
+ CursorShape cursor_shape;
+ NSCursor *cursors[CURSOR_MAX];
+ Map<CursorShape, Vector<Variant>> cursors_cache;
+
+ MouseMode mouse_mode;
+ Point2i last_mouse_pos;
+ uint32_t last_button_state;
+
+ bool window_focused;
+ bool drop_events;
+
+public:
+ virtual bool has_feature(Feature p_feature) const;
+ virtual String get_name() const;
+
+ virtual void global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag = Variant());
+ virtual void global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag = Variant());
+ virtual void global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu);
+ virtual void global_menu_add_separator(const String &p_menu_root);
+
+ virtual bool global_menu_is_item_checked(const String &p_menu_root, int p_idx) const;
+ virtual bool global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const;
+ virtual Callable global_menu_get_item_callback(const String &p_menu_root, int p_idx);
+ virtual Variant global_menu_get_item_tag(const String &p_menu_root, int p_idx);
+ virtual String global_menu_get_item_text(const String &p_menu_root, int p_idx);
+ virtual String global_menu_get_item_submenu(const String &p_menu_root, int p_idx);
+
+ virtual void global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked);
+ virtual void global_menu_set_item_checkable(const String &p_menu_root, int p_idx, bool p_checkable);
+ virtual void global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback);
+ virtual void global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag);
+ virtual void global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text);
+ virtual void global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu);
+
+ virtual int global_menu_get_item_count(const String &p_menu_root) const;
+
+ virtual void global_menu_remove_item(const String &p_menu_root, int p_idx);
+ virtual void global_menu_clear(const String &p_menu_root);
+
+ virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
+ virtual Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback);
+ virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback);
+
+ virtual void mouse_set_mode(MouseMode p_mode);
+ virtual MouseMode mouse_get_mode() const;
+
+ virtual void mouse_warp_to_position(const Point2i &p_to);
+ virtual Point2i mouse_get_position() const;
+ virtual Point2i mouse_get_absolute_position() const;
+ virtual int mouse_get_button_state() const;
+
+ virtual void clipboard_set(const String &p_text);
+ virtual String clipboard_get() const;
+
+ virtual int get_screen_count() const;
+ virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+ virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+ virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+ virtual float screen_get_scale(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+ virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+
+ virtual Vector<int> get_window_list() const;
+
+ virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i & = 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);
+ virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual void window_set_transient(WindowID p_window, WindowID p_parent);
+
+ virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
+ virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
+ virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
+ virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual Size2i window_get_real_size(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID);
+ virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID);
+ virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual bool can_any_window_draw() const;
+
+ virtual void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual WindowID get_window_at_screen_position(const Point2i &p_position) const;
+
+ virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID);
+ virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual Point2i ime_get_selection() const;
+ virtual String ime_get_text() const;
+
+ virtual void cursor_set_shape(CursorShape p_shape);
+ virtual CursorShape cursor_get_shape() const;
+ virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2());
+
+ virtual bool get_swap_ok_cancel();
+
+ virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
+
+ virtual void process_events();
+ virtual void force_process_and_drop_events();
+
+ virtual void release_rendering_thread();
+ virtual void make_rendering_thread();
+ virtual void swap_buffers();
+
+ virtual void set_native_icon(const String &p_filename);
+ virtual void set_icon(const Ref<Image> &p_icon);
+
+ virtual void console_set_visible(bool p_enabled);
+ virtual bool is_console_visible() const;
+
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static Vector<String> get_rendering_drivers_func();
+
+ static void register_osx_driver();
+
+ DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ ~DisplayServerOSX();
+};
+
+#endif // DISPLAY_SERVER_OSX_H
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
new file mode 100644
index 0000000000..a7099c1207
--- /dev/null
+++ b/platform/osx/display_server_osx.mm
@@ -0,0 +1,3587 @@
+/*************************************************************************/
+/* display_server_osx.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "display_server_osx.h"
+
+#include "os_osx.h"
+
+#include "core/io/marshalls.h"
+#include "core/os/keyboard.h"
+#include "main/main.h"
+#include "scene/resources/texture.h"
+
+#include <Carbon/Carbon.h>
+#include <Cocoa/Cocoa.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/hid/IOHIDKeys.h>
+#include <IOKit/hid/IOHIDLib.h>
+
+#if defined(OPENGL_ENABLED)
+#include "drivers/gles2/rasterizer_gles2.h"
+//TODO - reimplement OpenGLES
+#endif
+
+#if defined(VULKAN_ENABLED)
+#include "servers/visual/rasterizer_rd/rasterizer_rd.h"
+
+#include <QuartzCore/CAMetalLayer.h>
+#endif
+
+#define DS_OSX ((DisplayServerOSX *)(DisplayServerOSX::get_singleton()))
+
+static void _get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> r_state) {
+ r_state->set_shift((p_osx_state & NSEventModifierFlagShift));
+ r_state->set_control((p_osx_state & NSEventModifierFlagControl));
+ r_state->set_alt((p_osx_state & NSEventModifierFlagOption));
+ r_state->set_metakey((p_osx_state & NSEventModifierFlagCommand));
+}
+
+static Vector2i _get_mouse_pos(DisplayServerOSX::WindowData &p_wd, NSPoint p_locationInWindow, CGFloat p_backingScaleFactor) {
+ const NSRect contentRect = [p_wd.window_view frame];
+ const NSPoint p = p_locationInWindow;
+ p_wd.mouse_pos.x = p.x * p_backingScaleFactor;
+ p_wd.mouse_pos.y = (contentRect.size.height - p.y) * p_backingScaleFactor;
+ DS_OSX->last_mouse_pos = p_wd.mouse_pos;
+ InputFilter::get_singleton()->set_mouse_position(p_wd.mouse_pos);
+ return p_wd.mouse_pos;
+}
+
+static void _push_to_key_event_buffer(const DisplayServerOSX::KeyEvent &p_event) {
+ Vector<DisplayServerOSX::KeyEvent> &buffer = DS_OSX->key_event_buffer;
+ if (DS_OSX->key_event_pos >= buffer.size()) {
+ buffer.resize(1 + DS_OSX->key_event_pos);
+ }
+ buffer.write[DS_OSX->key_event_pos++] = p_event;
+}
+
+static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) {
+ if ([NSCursor respondsToSelector:selector]) {
+ id object = [NSCursor performSelector:selector];
+ if ([object isKindOfClass:[NSCursor class]]) {
+ return object;
+ }
+ }
+ if (fallback) {
+ // Fallback should be a reasonable default, no need to check.
+ return [NSCursor performSelector:fallback];
+ }
+ return [NSCursor arrowCursor];
+}
+
+/*************************************************************************/
+/* GodotApplication */
+/*************************************************************************/
+
+@interface GodotApplication : NSApplication
+@end
+
+@implementation GodotApplication
+
+- (void)sendEvent:(NSEvent *)event {
+ // special case handling of command-period, which is traditionally a special
+ // shortcut in macOS and doesn't arrive at our regular keyDown handler.
+ if ([event type] == NSEventTypeKeyDown) {
+ if (([event modifierFlags] & NSEventModifierFlagCommand) && [event keyCode] == 0x2f) {
+ Ref<InputEventKey> k;
+ k.instance();
+
+ _get_key_modifier_state([event modifierFlags], k);
+ k->set_window_id(DisplayServerOSX::INVALID_WINDOW_ID);
+ k->set_pressed(true);
+ k->set_keycode(KEY_PERIOD);
+ k->set_physical_keycode(KEY_PERIOD);
+ k->set_echo([event isARepeat]);
+
+ InputFilter::get_singleton()->accumulate_input_event(k);
+ }
+ }
+
+ // From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost
+ // This works around an AppKit bug, where key up events while holding
+ // down the command key don't get sent to the key window.
+ if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand))
+ [[self keyWindow] sendEvent:event];
+ else
+ [super sendEvent:event];
+}
+
+@end
+
+/*************************************************************************/
+/* GlobalMenuItem */
+/*************************************************************************/
+
+@interface GlobalMenuItem : NSObject {
+@public
+ Callable callback;
+ Variant meta;
+ bool checkable;
+}
+@end
+
+@implementation GlobalMenuItem
+@end
+
+/*************************************************************************/
+/* GodotApplicationDelegate */
+/*************************************************************************/
+
+@interface GodotApplicationDelegate : NSObject
+- (void)forceUnbundledWindowActivationHackStep1;
+- (void)forceUnbundledWindowActivationHackStep2;
+- (void)forceUnbundledWindowActivationHackStep3;
+@end
+
+@implementation GodotApplicationDelegate
+
+- (void)forceUnbundledWindowActivationHackStep1 {
+ // Step1: Switch focus to macOS Dock.
+ // Required to perform step 2, TransformProcessType will fail if app is already the in focus.
+ for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) {
+ [app activateWithOptions:NSApplicationActivateIgnoringOtherApps];
+ break;
+ }
+ [self performSelector:@selector(forceUnbundledWindowActivationHackStep2) withObject:nil afterDelay:0.02];
+}
+
+- (void)forceUnbundledWindowActivationHackStep2 {
+ // Step 2: Register app as foreground process.
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+ (void)TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ [self performSelector:@selector(forceUnbundledWindowActivationHackStep3) withObject:nil afterDelay:0.02];
+}
+
+- (void)forceUnbundledWindowActivationHackStep3 {
+ // Step 3: Switch focus back to app window.
+ [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification *)notice {
+ NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
+ if (nsappname == nil) {
+ // If executable is not a bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored).
+ [self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02];
+ }
+}
+
+- (void)globalMenuCallback:(id)sender {
+ if (![sender representedObject])
+ return;
+
+ GlobalMenuItem *value = [sender representedObject];
+
+ if (value) {
+ if (value->checkable) {
+ if ([sender state] == NSControlStateValueOff) {
+ [sender setState:NSControlStateValueOn];
+ } else {
+ [sender setState:NSControlStateValueOff];
+ }
+ }
+
+ if (value->callback != Callable()) {
+ Variant tag = value->meta;
+ Variant *tagp = &tag;
+ Variant ret;
+ Callable::CallError ce;
+ value->callback.call((const Variant **)&tagp, 1, ret, ce);
+ }
+ }
+}
+
+- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
+ return DS_OSX->dock_menu;
+}
+
+- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
+ // Note: may be called called before main loop init!
+ char *utfs = strdup([filename UTF8String]);
+ ((OS_OSX *)(OS_OSX::get_singleton()))->open_with_filename.parse_utf8(utfs);
+ free(utfs);
+
+#ifdef TOOLS_ENABLED
+ // Open new instance
+ if (OS_OSX::get_singleton()->get_main_loop()) {
+ List<String> args;
+ args.push_back(((OS_OSX *)(OS_OSX::get_singleton()))->open_with_filename);
+ String exec = OS::get_singleton()->get_executable_path();
+
+ OS::ProcessID pid = 0;
+ OS::get_singleton()->execute(exec, args, false, &pid);
+ }
+#endif
+ return YES;
+}
+
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
+ DS_OSX->_send_window_event(DS_OSX->windows[DisplayServerOSX::MAIN_WINDOW_ID], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
+ return NSTerminateCancel;
+}
+
+- (void)showAbout:(id)sender {
+ if (OS_OSX::get_singleton()->get_main_loop())
+ OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT);
+}
+
+@end
+
+/*************************************************************************/
+/* GodotWindowDelegate */
+/*************************************************************************/
+
+@interface GodotWindowDelegate : NSObject {
+ DisplayServerOSX::WindowID window_id;
+}
+
+- (void)windowWillClose:(NSNotification *)notification;
+- (void)setWindowID:(DisplayServerOSX::WindowID)wid;
+
+@end
+
+@implementation GodotWindowDelegate
+
+- (void)setWindowID:(DisplayServerOSX::WindowID)wid {
+ window_id = wid;
+}
+
+- (BOOL)windowShouldClose:(id)sender {
+ ERR_FAIL_COND_V(!DS_OSX->windows.has(window_id), YES);
+ DS_OSX->_send_window_event(DS_OSX->windows[window_id], DisplayServerOSX::WINDOW_EVENT_CLOSE_REQUEST);
+ return NO;
+}
+
+- (void)windowWillClose:(NSNotification *)notification {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ while (wd.transient_children.size()) {
+ DS_OSX->window_set_transient(wd.transient_children.front()->get(), DisplayServerOSX::INVALID_WINDOW_ID);
+ }
+
+ if (wd.transient_parent != DisplayServerOSX::INVALID_WINDOW_ID) {
+ DS_OSX->window_set_transient(window_id, DisplayServerOSX::INVALID_WINDOW_ID);
+ }
+
+#ifdef VULKAN_ENABLED
+ if (DS_OSX->rendering_driver == "vulkan") {
+ DS_OSX->context_vulkan->window_destroy(window_id);
+ }
+#endif
+ DS_OSX->windows.erase(window_id);
+}
+
+- (void)windowDidEnterFullScreen:(NSNotification *)notification {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ wd.fullscreen = true;
+
+ [wd.window_object setContentMinSize:NSMakeSize(0, 0)];
+ [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
+}
+
+- (void)windowDidExitFullScreen:(NSNotification *)notification {
+ if (!DS_OSX || !DS_OSX->windows.has(window_id))
+ return;
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ wd.fullscreen = false;
+
+ if (wd.min_size != Size2i()) {
+ Size2i size = wd.min_size / DS_OSX->_display_scale([wd.window_object screen]);
+ [wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)];
+ }
+ if (wd.max_size != Size2i()) {
+ Size2i size = wd.max_size / DS_OSX->_display_scale([wd.window_object screen]);
+ [wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
+ }
+
+ if (wd.resize_disabled)
+ [wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
+}
+
+- (void)windowDidChangeBackingProperties:(NSNotification *)notification {
+ if (!DisplayServerOSX::get_singleton())
+ return;
+
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ CGFloat newBackingScaleFactor = [wd.window_object backingScaleFactor];
+ CGFloat oldBackingScaleFactor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue];
+
+#if defined(OPENGL_ENABLED)
+ if (DS_OSX->rendering_driver == "opengl_es") {
+ //TODO - reimplement OpenGLES
+ if (OS_OSX::get_singleton()->is_hidpi_allowed()) {
+ [wd.window_view setWantsBestResolutionOpenGLSurface:YES];
+ } else {
+ [wd.window_view setWantsBestResolutionOpenGLSurface:NO];
+ }
+ }
+#endif
+
+ if (newBackingScaleFactor != oldBackingScaleFactor) {
+ //Set new display scale and window size
+ float newDisplayScale = OS_OSX::get_singleton()->is_hidpi_allowed() ? newBackingScaleFactor : 1.0;
+
+ const NSRect contentRect = [wd.window_view frame];
+
+ wd.size.width = contentRect.size.width * newDisplayScale;
+ wd.size.height = contentRect.size.height * newDisplayScale;
+
+ DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_DPI_CHANGE);
+
+#if defined(VULKAN_ENABLED)
+ if (DS_OSX->rendering_driver == "vulkan") {
+ CALayer *layer = [wd.window_view layer];
+ layer.contentsScale = DS_OSX->_display_scale([wd.window_object screen]);
+ }
+#endif
+ //Force window resize event
+ [self windowDidResize:notification];
+ }
+}
+
+- (void)windowDidResize:(NSNotification *)notification {
+ if (!DS_OSX || !DS_OSX->windows.has(window_id))
+ return;
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+#if defined(OPENGL_ENABLED)
+ if (DS_OSX->rendering_driver == "opengl_es") {
+ //TODO - reimplement OpenGLES
+ wd.context_gles2->update();
+ }
+#endif
+ const NSRect contentRect = [wd.window_view frame];
+
+ float displayScale = DS_OSX->_display_scale([wd.window_object screen]);
+ wd.size.width = contentRect.size.width * displayScale;
+ wd.size.height = contentRect.size.height * displayScale;
+
+#if defined(VULKAN_ENABLED)
+ if (DS_OSX->rendering_driver == "vulkan") {
+ CALayer *layer = [wd.window_view layer];
+ layer.contentsScale = displayScale;
+ DS_OSX->context_vulkan->window_resize(window_id, wd.size.width, wd.size.height);
+ }
+#endif
+
+ if (!wd.rect_changed_callback.is_null()) {
+ Variant size = Rect2i(DS_OSX->window_get_position(window_id), DS_OSX->window_get_size(window_id));
+ Variant *sizep = &size;
+ Variant ret;
+ Callable::CallError ce;
+ wd.rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce);
+ }
+
+ if (OS_OSX::get_singleton()->get_main_loop()) {
+ Main::force_redraw();
+ //Event retrieval blocks until resize is over. Call Main::iteration() directly.
+ if (!Main::is_iterating()) { //avoid cyclic loop
+ Main::iteration();
+ }
+ }
+}
+
+- (void)windowDidMove:(NSNotification *)notification {
+ if (InputFilter::get_singleton()) {
+ InputFilter::get_singleton()->release_pressed_events();
+ }
+}
+
+- (void)windowDidBecomeKey:(NSNotification *)notification {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [wd.window_view backingScaleFactor] : 1.0;
+ _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream], backingScaleFactor);
+ InputFilter::get_singleton()->set_mouse_position(wd.mouse_pos);
+
+ DS_OSX->window_focused = true;
+ DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN);
+}
+
+- (void)windowDidResignKey:(NSNotification *)notification {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ DS_OSX->window_focused = false;
+
+ InputFilter::get_singleton()->release_pressed_events();
+ DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_OUT);
+}
+
+- (void)windowDidMiniaturize:(NSNotification *)notification {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ DS_OSX->window_focused = false;
+
+ InputFilter::get_singleton()->release_pressed_events();
+ DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_OUT);
+}
+
+- (void)windowDidDeminiaturize:(NSNotification *)notification {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ DS_OSX->window_focused = true;
+ DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_FOCUS_IN);
+}
+
+@end
+
+/*************************************************************************/
+/* GodotContentView */
+/*************************************************************************/
+
+@interface GodotContentView : NSView <NSTextInputClient> {
+ DisplayServerOSX::WindowID window_id;
+ NSTrackingArea *trackingArea;
+ NSMutableAttributedString *markedText;
+ bool imeInputEventInProgress;
+}
+
+- (void)cancelComposition;
+- (CALayer *)makeBackingLayer;
+- (BOOL)wantsUpdateLayer;
+- (void)updateLayer;
+- (void)setWindowID:(DisplayServerOSX::WindowID)wid;
+
+@end
+
+@implementation GodotContentView
+
+- (void)setWindowID:(DisplayServerOSX::WindowID)wid {
+ window_id = wid;
+}
+
++ (void)initialize {
+ if (self == [GodotContentView class]) {
+ // nothing left to do here at the moment..
+ }
+}
+
+- (CALayer *)makeBackingLayer {
+#if defined(VULKAN_ENABLED)
+ if (DS_OSX->rendering_driver == "vulkan") {
+ CALayer *layer = [[CAMetalLayer class] layer];
+ return layer;
+ }
+#endif
+ return [super makeBackingLayer];
+}
+
+- (void)updateLayer {
+#if defined(VULKAN_ENABLED)
+ if (DS_OSX->rendering_driver == "vulkan") {
+ [super updateLayer];
+ }
+#endif
+#if defined(OPENGL_ENABLED)
+ if (DS_OSX->rendering_driver == "opengl_es") {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ wd.context_gles2->update();
+ //TODO - reimplement OpenGLES
+ }
+#endif
+}
+
+- (BOOL)wantsUpdateLayer {
+ return YES;
+}
+
+- (id)init {
+ self = [super init];
+ trackingArea = nil;
+ imeInputEventInProgress = false;
+ [self updateTrackingAreas];
+ [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
+ markedText = [[NSMutableAttributedString alloc] init];
+ return self;
+}
+
+- (void)dealloc {
+ [trackingArea release];
+ [markedText release];
+ [super dealloc];
+}
+
+static const NSRange kEmptyRange = { NSNotFound, 0 };
+
+- (BOOL)hasMarkedText {
+ return (markedText.length > 0);
+}
+
+- (NSRange)markedRange {
+ return NSMakeRange(0, markedText.length);
+}
+
+- (NSRange)selectedRange {
+ return kEmptyRange;
+}
+
+- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange {
+ if ([aString isKindOfClass:[NSAttributedString class]]) {
+ [markedText initWithAttributedString:aString];
+ } else {
+ [markedText initWithString:aString];
+ }
+ if (markedText.length == 0) {
+ [self unmarkText];
+ return;
+ }
+
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ if (wd.im_active) {
+ imeInputEventInProgress = true;
+ DS_OSX->im_text.parse_utf8([[markedText mutableString] UTF8String]);
+ DS_OSX->im_selection = Point2i(selectedRange.location, selectedRange.length);
+
+ OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
+ }
+}
+
+- (void)doCommandBySelector:(SEL)aSelector {
+ if ([self respondsToSelector:aSelector])
+ [self performSelector:aSelector];
+}
+
+- (void)unmarkText {
+ imeInputEventInProgress = false;
+ [[markedText mutableString] setString:@""];
+
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ if (wd.im_active) {
+ DS_OSX->im_text = String();
+ DS_OSX->im_selection = Point2i();
+
+ OS_OSX::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
+ }
+}
+
+- (NSArray *)validAttributesForMarkedText {
+ return [NSArray array];
+}
+
+- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
+ return nil;
+}
+
+- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
+ return 0;
+}
+
+- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
+ ERR_FAIL_COND_V(!DS_OSX->windows.has(window_id), NSMakeRect(0, 0, 0, 0));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ const NSRect contentRect = [wd.window_view frame];
+ float displayScale = DS_OSX->_display_scale([wd.window_object screen]);
+ NSRect pointInWindowRect = NSMakeRect(wd.im_position.x / displayScale, contentRect.size.height - (wd.im_position.y / displayScale) - 1, 0, 0);
+ NSPoint pointOnScreen = [wd.window_object convertRectToScreen:pointInWindowRect].origin;
+
+ return NSMakeRect(pointOnScreen.x, pointOnScreen.y, 0, 0);
+}
+
+- (void)cancelComposition {
+ [self unmarkText];
+ NSTextInputContext *currentInputContext = [NSTextInputContext currentInputContext];
+ [currentInputContext discardMarkedText];
+}
+
+- (void)insertText:(id)aString {
+ [self insertText:aString replacementRange:NSMakeRange(0, 0)];
+}
+
+- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
+ NSEvent *event = [NSApp currentEvent];
+
+ NSString *characters;
+ if ([aString isKindOfClass:[NSAttributedString class]]) {
+ characters = [aString string];
+ } else {
+ characters = (NSString *)aString;
+ }
+
+ NSUInteger i, length = [characters length];
+
+ NSCharacterSet *ctrlChars = [NSCharacterSet controlCharacterSet];
+ NSCharacterSet *wsnlChars = [NSCharacterSet whitespaceAndNewlineCharacterSet];
+ if ([characters rangeOfCharacterFromSet:ctrlChars].length && [characters rangeOfCharacterFromSet:wsnlChars].length == 0) {
+ NSTextInputContext *currentInputContext = [NSTextInputContext currentInputContext];
+ [currentInputContext discardMarkedText];
+ [self cancelComposition];
+ return;
+ }
+
+ for (i = 0; i < length; i++) {
+ const unichar codepoint = [characters characterAtIndex:i];
+ if ((codepoint & 0xFF00) == 0xF700)
+ continue;
+
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = true;
+ ke.echo = false;
+ ke.raw = false; // IME input event
+ ke.keycode = 0;
+ ke.physical_keycode = 0;
+ ke.unicode = codepoint;
+
+ _push_to_key_event_buffer(ke);
+ }
+ [self cancelComposition];
+}
+
+- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
+ return NSDragOperationCopy;
+}
+
+- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
+ return NSDragOperationCopy;
+}
+
+- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
+ ERR_FAIL_COND_V(!DS_OSX->windows.has(window_id), NO);
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ NSPasteboard *pboard = [sender draggingPasteboard];
+ NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
+
+ Vector<String> files;
+ for (NSUInteger i = 0; i < filenames.count; i++) {
+ NSString *ns = [filenames objectAtIndex:i];
+ char *utfs = strdup([ns UTF8String]);
+ String ret;
+ ret.parse_utf8(utfs);
+ free(utfs);
+ files.push_back(ret);
+ }
+
+ if (!wd.drop_files_callback.is_null()) {
+ Variant v = files;
+ Variant *vp = &v;
+ Variant ret;
+ Callable::CallError ce;
+ wd.drop_files_callback.call((const Variant **)&vp, 1, ret, ce);
+ }
+
+ return NO;
+}
+
+- (BOOL)isOpaque {
+ return YES;
+}
+
+- (BOOL)canBecomeKeyView {
+ return YES;
+}
+
+- (BOOL)acceptsFirstResponder {
+ return YES;
+}
+
+- (void)cursorUpdate:(NSEvent *)event {
+ DisplayServer::CursorShape p_shape = DS_OSX->cursor_shape;
+ DS_OSX->cursor_shape = DisplayServer::CURSOR_MAX;
+ DS_OSX->cursor_set_shape(p_shape);
+}
+
+static void _mouseDownEvent(DisplayServer::WindowID window_id, NSEvent *event, int index, int mask, bool pressed) {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ if (pressed) {
+ DS_OSX->last_button_state |= mask;
+ } else {
+ DS_OSX->last_button_state &= ~mask;
+ }
+
+ Ref<InputEventMouseButton> mb;
+ mb.instance();
+ mb->set_window_id(window_id);
+ const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [[event window] backingScaleFactor] : 1.0;
+ const Vector2 pos = _get_mouse_pos(wd, [event locationInWindow], backingScaleFactor);
+ _get_key_modifier_state([event modifierFlags], mb);
+ mb->set_button_index(index);
+ mb->set_pressed(pressed);
+ mb->set_position(pos);
+ mb->set_global_position(pos);
+ mb->set_button_mask(DS_OSX->last_button_state);
+ if (index == BUTTON_LEFT && pressed) {
+ mb->set_doubleclick([event clickCount] == 2);
+ }
+
+ InputFilter::get_singleton()->accumulate_input_event(mb);
+}
+
+- (void)mouseDown:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ if (([event modifierFlags] & NSEventModifierFlagControl)) {
+ wd.mouse_down_control = true;
+ _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true);
+ } else {
+ wd.mouse_down_control = false;
+ _mouseDownEvent(window_id, event, BUTTON_LEFT, BUTTON_MASK_LEFT, true);
+ }
+}
+
+- (void)mouseDragged:(NSEvent *)event {
+ [self mouseMoved:event];
+}
+
+- (void)mouseUp:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ if (wd.mouse_down_control) {
+ _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false);
+ } else {
+ _mouseDownEvent(window_id, event, BUTTON_LEFT, BUTTON_MASK_LEFT, false);
+ }
+}
+
+- (void)mouseMoved:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ 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);
+ mm->set_position(pos);
+ mm->set_pressure([event pressure]);
+ if ([event subtype] == NSEventSubtypeTabletPoint) {
+ const NSPoint p = [event tilt];
+ mm->set_tilt(Vector2(p.x, p.y));
+ }
+ mm->set_global_position(pos);
+ mm->set_speed(InputFilter::get_singleton()->get_last_mouse_speed());
+ Vector2i relativeMotion = Vector2i();
+ relativeMotion.x = [event deltaX] * backingScaleFactor;
+ relativeMotion.y = [event deltaY] * backingScaleFactor;
+ mm->set_relative(relativeMotion);
+ _get_key_modifier_state([event modifierFlags], mm);
+
+ InputFilter::get_singleton()->set_mouse_position(wd.mouse_pos);
+ InputFilter::get_singleton()->accumulate_input_event(mm);
+}
+
+- (void)rightMouseDown:(NSEvent *)event {
+ _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true);
+}
+
+- (void)rightMouseDragged:(NSEvent *)event {
+ [self mouseMoved:event];
+}
+
+- (void)rightMouseUp:(NSEvent *)event {
+ _mouseDownEvent(window_id, event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false);
+}
+
+- (void)otherMouseDown:(NSEvent *)event {
+ if ((int)[event buttonNumber] == 2) {
+ _mouseDownEvent(window_id, event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, true);
+ } else if ((int)[event buttonNumber] == 3) {
+ _mouseDownEvent(window_id, event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, true);
+ } else if ((int)[event buttonNumber] == 4) {
+ _mouseDownEvent(window_id, event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, true);
+ } else {
+ return;
+ }
+}
+
+- (void)otherMouseDragged:(NSEvent *)event {
+ [self mouseMoved:event];
+}
+
+- (void)otherMouseUp:(NSEvent *)event {
+ if ((int)[event buttonNumber] == 2) {
+ _mouseDownEvent(window_id, event, BUTTON_MIDDLE, BUTTON_MASK_MIDDLE, false);
+ } else if ((int)[event buttonNumber] == 3) {
+ _mouseDownEvent(window_id, event, BUTTON_XBUTTON1, BUTTON_MASK_XBUTTON1, false);
+ } else if ((int)[event buttonNumber] == 4) {
+ _mouseDownEvent(window_id, event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, false);
+ } else {
+ return;
+ }
+}
+
+- (void)mouseExited:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ if (DS_OSX->mouse_mode != DisplayServer::MOUSE_MODE_CAPTURED)
+ DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_MOUSE_EXIT);
+}
+
+- (void)mouseEntered:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ if (DS_OSX->mouse_mode != DisplayServer::MOUSE_MODE_CAPTURED)
+ DS_OSX->_send_window_event(wd, DisplayServerOSX::WINDOW_EVENT_MOUSE_ENTER);
+
+ DisplayServer::CursorShape p_shape = DS_OSX->cursor_shape;
+ DS_OSX->cursor_shape = DisplayServer::CURSOR_MAX;
+ DS_OSX->cursor_set_shape(p_shape);
+}
+
+- (void)magnifyWithEvent:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ Ref<InputEventMagnifyGesture> ev;
+ ev.instance();
+ ev->set_window_id(window_id);
+ _get_key_modifier_state([event modifierFlags], ev);
+ const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [[event window] backingScaleFactor] : 1.0;
+ ev->set_position(_get_mouse_pos(wd, [event locationInWindow], backingScaleFactor));
+ ev->set_factor([event magnification] + 1.0);
+
+ InputFilter::get_singleton()->accumulate_input_event(ev);
+}
+
+- (void)viewDidChangeBackingProperties {
+ // nothing left to do here
+}
+
+- (void)updateTrackingAreas {
+ if (trackingArea != nil) {
+ [self removeTrackingArea:trackingArea];
+ [trackingArea release];
+ }
+
+ NSTrackingAreaOptions options =
+ NSTrackingMouseEnteredAndExited |
+ NSTrackingActiveInKeyWindow |
+ NSTrackingCursorUpdate |
+ NSTrackingInVisibleRect;
+
+ trackingArea = [[NSTrackingArea alloc]
+ initWithRect:[self bounds]
+ options:options
+ owner:self
+ userInfo:nil];
+
+ [self addTrackingArea:trackingArea];
+ [super updateTrackingAreas];
+}
+
+static bool isNumpadKey(unsigned int key) {
+
+ static const unsigned int table[] = {
+ 0x41, /* kVK_ANSI_KeypadDecimal */
+ 0x43, /* kVK_ANSI_KeypadMultiply */
+ 0x45, /* kVK_ANSI_KeypadPlus */
+ 0x47, /* kVK_ANSI_KeypadClear */
+ 0x4b, /* kVK_ANSI_KeypadDivide */
+ 0x4c, /* kVK_ANSI_KeypadEnter */
+ 0x4e, /* kVK_ANSI_KeypadMinus */
+ 0x51, /* kVK_ANSI_KeypadEquals */
+ 0x52, /* kVK_ANSI_Keypad0 */
+ 0x53, /* kVK_ANSI_Keypad1 */
+ 0x54, /* kVK_ANSI_Keypad2 */
+ 0x55, /* kVK_ANSI_Keypad3 */
+ 0x56, /* kVK_ANSI_Keypad4 */
+ 0x57, /* kVK_ANSI_Keypad5 */
+ 0x58, /* kVK_ANSI_Keypad6 */
+ 0x59, /* kVK_ANSI_Keypad7 */
+ 0x5b, /* kVK_ANSI_Keypad8 */
+ 0x5c, /* kVK_ANSI_Keypad9 */
+ 0x5f, /* kVK_JIS_KeypadComma */
+ 0x00
+ };
+ for (int i = 0; table[i] != 0; i++) {
+ if (key == table[i])
+ return true;
+ }
+ return false;
+}
+
+// Translates a OS X keycode to a Godot keycode
+//
+static int translateKey(unsigned int key) {
+
+ // Keyboard symbol translation table
+ static const unsigned int table[128] = {
+ /* 00 */ KEY_A,
+ /* 01 */ KEY_S,
+ /* 02 */ KEY_D,
+ /* 03 */ KEY_F,
+ /* 04 */ KEY_H,
+ /* 05 */ KEY_G,
+ /* 06 */ KEY_Z,
+ /* 07 */ KEY_X,
+ /* 08 */ KEY_C,
+ /* 09 */ KEY_V,
+ /* 0a */ KEY_SECTION, /* ISO Section */
+ /* 0b */ KEY_B,
+ /* 0c */ KEY_Q,
+ /* 0d */ KEY_W,
+ /* 0e */ KEY_E,
+ /* 0f */ KEY_R,
+ /* 10 */ KEY_Y,
+ /* 11 */ KEY_T,
+ /* 12 */ KEY_1,
+ /* 13 */ KEY_2,
+ /* 14 */ KEY_3,
+ /* 15 */ KEY_4,
+ /* 16 */ KEY_6,
+ /* 17 */ KEY_5,
+ /* 18 */ KEY_EQUAL,
+ /* 19 */ KEY_9,
+ /* 1a */ KEY_7,
+ /* 1b */ KEY_MINUS,
+ /* 1c */ KEY_8,
+ /* 1d */ KEY_0,
+ /* 1e */ KEY_BRACERIGHT,
+ /* 1f */ KEY_O,
+ /* 20 */ KEY_U,
+ /* 21 */ KEY_BRACELEFT,
+ /* 22 */ KEY_I,
+ /* 23 */ KEY_P,
+ /* 24 */ KEY_ENTER,
+ /* 25 */ KEY_L,
+ /* 26 */ KEY_J,
+ /* 27 */ KEY_APOSTROPHE,
+ /* 28 */ KEY_K,
+ /* 29 */ KEY_SEMICOLON,
+ /* 2a */ KEY_BACKSLASH,
+ /* 2b */ KEY_COMMA,
+ /* 2c */ KEY_SLASH,
+ /* 2d */ KEY_N,
+ /* 2e */ KEY_M,
+ /* 2f */ KEY_PERIOD,
+ /* 30 */ KEY_TAB,
+ /* 31 */ KEY_SPACE,
+ /* 32 */ KEY_QUOTELEFT,
+ /* 33 */ KEY_BACKSPACE,
+ /* 34 */ KEY_UNKNOWN,
+ /* 35 */ KEY_ESCAPE,
+ /* 36 */ KEY_META,
+ /* 37 */ KEY_META,
+ /* 38 */ KEY_SHIFT,
+ /* 39 */ KEY_CAPSLOCK,
+ /* 3a */ KEY_ALT,
+ /* 3b */ KEY_CONTROL,
+ /* 3c */ KEY_SHIFT,
+ /* 3d */ KEY_ALT,
+ /* 3e */ KEY_CONTROL,
+ /* 3f */ KEY_UNKNOWN, /* Function */
+ /* 40 */ KEY_UNKNOWN, /* F17 */
+ /* 41 */ KEY_KP_PERIOD,
+ /* 42 */ KEY_UNKNOWN,
+ /* 43 */ KEY_KP_MULTIPLY,
+ /* 44 */ KEY_UNKNOWN,
+ /* 45 */ KEY_KP_ADD,
+ /* 46 */ KEY_UNKNOWN,
+ /* 47 */ KEY_NUMLOCK, /* Really KeypadClear... */
+ /* 48 */ KEY_VOLUMEUP, /* VolumeUp */
+ /* 49 */ KEY_VOLUMEDOWN, /* VolumeDown */
+ /* 4a */ KEY_VOLUMEMUTE, /* Mute */
+ /* 4b */ KEY_KP_DIVIDE,
+ /* 4c */ KEY_KP_ENTER,
+ /* 4d */ KEY_UNKNOWN,
+ /* 4e */ KEY_KP_SUBTRACT,
+ /* 4f */ KEY_UNKNOWN, /* F18 */
+ /* 50 */ KEY_UNKNOWN, /* F19 */
+ /* 51 */ KEY_EQUAL, /* KeypadEqual */
+ /* 52 */ KEY_KP_0,
+ /* 53 */ KEY_KP_1,
+ /* 54 */ KEY_KP_2,
+ /* 55 */ KEY_KP_3,
+ /* 56 */ KEY_KP_4,
+ /* 57 */ KEY_KP_5,
+ /* 58 */ KEY_KP_6,
+ /* 59 */ KEY_KP_7,
+ /* 5a */ KEY_UNKNOWN, /* F20 */
+ /* 5b */ KEY_KP_8,
+ /* 5c */ KEY_KP_9,
+ /* 5d */ KEY_YEN, /* JIS Yen */
+ /* 5e */ KEY_UNDERSCORE, /* JIS Underscore */
+ /* 5f */ KEY_COMMA, /* JIS KeypadComma */
+ /* 60 */ KEY_F5,
+ /* 61 */ KEY_F6,
+ /* 62 */ KEY_F7,
+ /* 63 */ KEY_F3,
+ /* 64 */ KEY_F8,
+ /* 65 */ KEY_F9,
+ /* 66 */ KEY_UNKNOWN, /* JIS Eisu */
+ /* 67 */ KEY_F11,
+ /* 68 */ KEY_UNKNOWN, /* JIS Kana */
+ /* 69 */ KEY_F13,
+ /* 6a */ KEY_F16,
+ /* 6b */ KEY_F14,
+ /* 6c */ KEY_UNKNOWN,
+ /* 6d */ KEY_F10,
+ /* 6e */ KEY_MENU,
+ /* 6f */ KEY_F12,
+ /* 70 */ KEY_UNKNOWN,
+ /* 71 */ KEY_F15,
+ /* 72 */ KEY_INSERT, /* Really Help... */
+ /* 73 */ KEY_HOME,
+ /* 74 */ KEY_PAGEUP,
+ /* 75 */ KEY_DELETE,
+ /* 76 */ KEY_F4,
+ /* 77 */ KEY_END,
+ /* 78 */ KEY_F2,
+ /* 79 */ KEY_PAGEDOWN,
+ /* 7a */ KEY_F1,
+ /* 7b */ KEY_LEFT,
+ /* 7c */ KEY_RIGHT,
+ /* 7d */ KEY_DOWN,
+ /* 7e */ KEY_UP,
+ /* 7f */ KEY_UNKNOWN,
+ };
+
+ if (key >= 128)
+ return KEY_UNKNOWN;
+
+ return table[key];
+}
+
+struct _KeyCodeMap {
+ UniChar kchar;
+ int kcode;
+};
+
+static const _KeyCodeMap _keycodes[55] = {
+ { '`', KEY_QUOTELEFT },
+ { '~', KEY_ASCIITILDE },
+ { '0', KEY_0 },
+ { '1', KEY_1 },
+ { '2', KEY_2 },
+ { '3', KEY_3 },
+ { '4', KEY_4 },
+ { '5', KEY_5 },
+ { '6', KEY_6 },
+ { '7', KEY_7 },
+ { '8', KEY_8 },
+ { '9', KEY_9 },
+ { '-', KEY_MINUS },
+ { '_', KEY_UNDERSCORE },
+ { '=', KEY_EQUAL },
+ { '+', KEY_PLUS },
+ { 'q', KEY_Q },
+ { 'w', KEY_W },
+ { 'e', KEY_E },
+ { 'r', KEY_R },
+ { 't', KEY_T },
+ { 'y', KEY_Y },
+ { 'u', KEY_U },
+ { 'i', KEY_I },
+ { 'o', KEY_O },
+ { 'p', KEY_P },
+ { '[', KEY_BRACELEFT },
+ { ']', KEY_BRACERIGHT },
+ { '{', KEY_BRACELEFT },
+ { '}', KEY_BRACERIGHT },
+ { 'a', KEY_A },
+ { 's', KEY_S },
+ { 'd', KEY_D },
+ { 'f', KEY_F },
+ { 'g', KEY_G },
+ { 'h', KEY_H },
+ { 'j', KEY_J },
+ { 'k', KEY_K },
+ { 'l', KEY_L },
+ { ';', KEY_SEMICOLON },
+ { ':', KEY_COLON },
+ { '\'', KEY_APOSTROPHE },
+ { '\"', KEY_QUOTEDBL },
+ { '\\', KEY_BACKSLASH },
+ { '#', KEY_NUMBERSIGN },
+ { 'z', KEY_Z },
+ { 'x', KEY_X },
+ { 'c', KEY_C },
+ { 'v', KEY_V },
+ { 'b', KEY_B },
+ { 'n', KEY_N },
+ { 'm', KEY_M },
+ { ',', KEY_COMMA },
+ { '.', KEY_PERIOD },
+ { '/', KEY_SLASH }
+};
+
+static int remapKey(unsigned int key, unsigned int state) {
+ if (isNumpadKey(key))
+ return translateKey(key);
+
+ TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
+ if (!currentKeyboard)
+ return translateKey(key);
+
+ CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
+ if (!layoutData)
+ return translateKey(key);
+
+ const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
+
+ UInt32 keysDown = 0;
+ UniChar chars[4];
+ UniCharCount realLength;
+
+ OSStatus err = UCKeyTranslate(keyboardLayout,
+ key,
+ kUCKeyActionDisplay,
+ (state >> 8) & 0xFF,
+ LMGetKbdType(),
+ kUCKeyTranslateNoDeadKeysBit,
+ &keysDown,
+ sizeof(chars) / sizeof(chars[0]),
+ &realLength,
+ chars);
+
+ if (err != noErr) {
+ return translateKey(key);
+ }
+
+ for (unsigned int i = 0; i < 55; i++) {
+ if (_keycodes[i].kchar == chars[0]) {
+ return _keycodes[i].kcode;
+ }
+ }
+ return translateKey(key);
+}
+
+- (void)keyDown:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ // Ignore all input if IME input is in progress
+ if (!imeInputEventInProgress) {
+ NSString *characters = [event characters];
+ NSUInteger length = [characters length];
+
+ if (!wd.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++) {
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = true;
+ ke.echo = [event isARepeat];
+ ke.keycode = remapKey([event keyCode], [event modifierFlags]);
+ ke.physical_keycode = translateKey([event keyCode]);
+ ke.raw = true;
+ ke.unicode = [characters characterAtIndex:i];
+
+ _push_to_key_event_buffer(ke);
+ }
+ } else {
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = true;
+ ke.echo = [event isARepeat];
+ ke.keycode = remapKey([event keyCode], [event modifierFlags]);
+ ke.physical_keycode = translateKey([event keyCode]);
+ ke.raw = false;
+ ke.unicode = 0;
+
+ _push_to_key_event_buffer(ke);
+ }
+ }
+
+ // Pass events to IME handler
+ if (wd.im_active)
+ [self interpretKeyEvents:[NSArray arrayWithObject:event]];
+}
+
+- (void)flagsChanged:(NSEvent *)event {
+ // Ignore all input if IME input is in progress
+ if (!imeInputEventInProgress) {
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.echo = false;
+ ke.raw = true;
+
+ int key = [event keyCode];
+ int mod = [event modifierFlags];
+
+ if (key == 0x36 || key == 0x37) {
+ if (mod & NSEventModifierFlagCommand) {
+ mod &= ~NSEventModifierFlagCommand;
+ ke.pressed = true;
+ } else {
+ ke.pressed = false;
+ }
+ } else if (key == 0x38 || key == 0x3c) {
+ if (mod & NSEventModifierFlagShift) {
+ mod &= ~NSEventModifierFlagShift;
+ ke.pressed = true;
+ } else {
+ ke.pressed = false;
+ }
+ } else if (key == 0x3a || key == 0x3d) {
+ if (mod & NSEventModifierFlagOption) {
+ mod &= ~NSEventModifierFlagOption;
+ ke.pressed = true;
+ } else {
+ ke.pressed = false;
+ }
+ } else if (key == 0x3b || key == 0x3e) {
+ if (mod & NSEventModifierFlagControl) {
+ mod &= ~NSEventModifierFlagControl;
+ ke.pressed = true;
+ } else {
+ ke.pressed = false;
+ }
+ } else {
+ return;
+ }
+
+ ke.osx_state = mod;
+ ke.keycode = remapKey(key, mod);
+ ke.physical_keycode = translateKey(key);
+ ke.unicode = 0;
+
+ _push_to_key_event_buffer(ke);
+ }
+}
+
+- (void)keyUp:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ // Ignore all input if IME input is in progress
+ if (!imeInputEventInProgress) {
+ NSString *characters = [event characters];
+ NSUInteger length = [characters length];
+
+ // Fallback unicode character handler used if IME is not active
+ if (!wd.im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode], [event modifierFlags]))) {
+ for (NSUInteger i = 0; i < length; i++) {
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = false;
+ ke.echo = [event isARepeat];
+ ke.keycode = remapKey([event keyCode], [event modifierFlags]);
+ ke.physical_keycode = translateKey([event keyCode]);
+ ke.raw = true;
+ ke.unicode = [characters characterAtIndex:i];
+
+ _push_to_key_event_buffer(ke);
+ }
+ } else {
+ DisplayServerOSX::KeyEvent ke;
+
+ ke.window_id = window_id;
+ ke.osx_state = [event modifierFlags];
+ ke.pressed = false;
+ ke.echo = [event isARepeat];
+ ke.keycode = remapKey([event keyCode], [event modifierFlags]);
+ ke.physical_keycode = translateKey([event keyCode]);
+ ke.raw = true;
+ ke.unicode = 0;
+
+ _push_to_key_event_buffer(ke);
+ }
+ }
+}
+
+inline void sendScrollEvent(DisplayServer::WindowID window_id, int button, double factor, int modifierFlags) {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ unsigned int mask = 1 << (button - 1);
+
+ Ref<InputEventMouseButton> sc;
+ sc.instance();
+
+ sc->set_window_id(window_id);
+ _get_key_modifier_state(modifierFlags, sc);
+ sc->set_button_index(button);
+ sc->set_factor(factor);
+ sc->set_pressed(true);
+ sc->set_position(wd.mouse_pos);
+ sc->set_global_position(wd.mouse_pos);
+ DS_OSX->last_button_state |= mask;
+ sc->set_button_mask(DS_OSX->last_button_state);
+
+ InputFilter::get_singleton()->accumulate_input_event(sc);
+
+ sc.instance();
+ sc->set_window_id(window_id);
+ sc->set_button_index(button);
+ sc->set_factor(factor);
+ sc->set_pressed(false);
+ sc->set_position(wd.mouse_pos);
+ sc->set_global_position(wd.mouse_pos);
+ DS_OSX->last_button_state &= ~mask;
+ sc->set_button_mask(DS_OSX->last_button_state);
+
+ InputFilter::get_singleton()->accumulate_input_event(sc);
+}
+
+inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy, int modifierFlags) {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ Ref<InputEventPanGesture> pg;
+ pg.instance();
+
+ pg->set_window_id(window_id);
+ _get_key_modifier_state(modifierFlags, pg);
+ pg->set_position(wd.mouse_pos);
+ pg->set_delta(Vector2(-dx, -dy));
+
+ InputFilter::get_singleton()->accumulate_input_event(pg);
+}
+
+- (void)scrollWheel:(NSEvent *)event {
+ ERR_FAIL_COND(!DS_OSX->windows.has(window_id));
+ DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id];
+
+ double deltaX, deltaY;
+
+ const CGFloat backingScaleFactor = (OS::get_singleton()->is_hidpi_allowed()) ? [[event window] backingScaleFactor] : 1.0;
+ _get_mouse_pos(wd, [event locationInWindow], backingScaleFactor);
+
+ deltaX = [event scrollingDeltaX];
+ deltaY = [event scrollingDeltaY];
+
+ if ([event hasPreciseScrollingDeltas]) {
+ deltaX *= 0.03;
+ deltaY *= 0.03;
+ }
+
+ if ([event phase] != NSEventPhaseNone || [event momentumPhase] != NSEventPhaseNone) {
+ sendPanEvent(window_id, deltaX, deltaY, [event modifierFlags]);
+ } else {
+ if (fabs(deltaX)) {
+ sendScrollEvent(window_id, 0 > deltaX ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_LEFT, fabs(deltaX * 0.3), [event modifierFlags]);
+ }
+ if (fabs(deltaY)) {
+ sendScrollEvent(window_id, 0 < deltaY ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN, fabs(deltaY * 0.3), [event modifierFlags]);
+ }
+ }
+}
+
+@end
+
+/*************************************************************************/
+/* GodotWindow */
+/*************************************************************************/
+
+@interface GodotWindow : NSWindow {
+}
+@end
+
+@implementation GodotWindow
+
+- (BOOL)canBecomeKeyWindow {
+ // Required for NSBorderlessWindowMask windows
+ return YES;
+}
+
+@end
+
+/*************************************************************************/
+/* DisplayServerOSX */
+/*************************************************************************/
+
+bool DisplayServerOSX::has_feature(Feature p_feature) const {
+ switch (p_feature) {
+ case FEATURE_GLOBAL_MENU:
+ case FEATURE_SUBWINDOWS:
+ //case FEATURE_TOUCHSCREEN:
+ case FEATURE_MOUSE:
+ case FEATURE_MOUSE_WARP:
+ case FEATURE_CLIPBOARD:
+ case FEATURE_CURSOR_SHAPE:
+ case FEATURE_CUSTOM_CURSOR_SHAPE:
+ case FEATURE_NATIVE_DIALOG:
+ //case FEATURE_CONSOLE_WINDOW:
+ case FEATURE_IME:
+ case FEATURE_WINDOW_TRANSPARENCY:
+ case FEATURE_HIDPI:
+ case FEATURE_ICON:
+ case FEATURE_NATIVE_ICON:
+ case FEATURE_SWAP_BUFFERS:
+ return true;
+ default: {
+ }
+ }
+ return false;
+}
+
+String DisplayServerOSX::get_name() const {
+ return "OSX";
+}
+
+const NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) const {
+ const NSMenu *menu = NULL;
+ if (p_menu_root == "") {
+ // Main menu.x
+ menu = [NSApp mainMenu];
+ } else if (p_menu_root.to_lower() == "_dock") {
+ // macOS dock menu.
+ menu = dock_menu;
+ } else {
+ // Submenu.
+ if (submenu.has(p_menu_root)) {
+ menu = submenu[p_menu_root];
+ }
+ }
+ if (menu == apple_menu) {
+ // Do not allow to change Apple menu.
+ return NULL;
+ }
+ return menu;
+}
+
+NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) {
+ NSMenu *menu = NULL;
+ if (p_menu_root == "") {
+ // Main menu.
+ menu = [NSApp mainMenu];
+ } else if (p_menu_root.to_lower() == "_dock") {
+ // macOS dock menu.
+ menu = dock_menu;
+ } else {
+ // Submenu.
+ if (!submenu.has(p_menu_root)) {
+ NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]];
+ submenu[p_menu_root] = n_menu;
+ }
+ menu = submenu[p_menu_root];
+ }
+ if (menu == apple_menu) {
+ // Do not allow to change Apple menu.
+ return NULL;
+ }
+ return menu;
+}
+
+void DisplayServerOSX::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
+ GlobalMenuItem *obj = [[[GlobalMenuItem alloc] init] autorelease];
+ obj->callback = p_callback;
+ obj->meta = p_tag;
+ obj->checkable = false;
+ [menu_item setRepresentedObject:obj];
+ }
+}
+
+void DisplayServerOSX::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
+ GlobalMenuItem *obj = [[[GlobalMenuItem alloc] init] autorelease];
+ obj->callback = p_callback;
+ obj->meta = p_tag;
+ obj->checkable = true;
+ [menu_item setRepresentedObject:obj];
+ }
+}
+
+void DisplayServerOSX::global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ NSMenu *sub_menu = _get_menu_root(p_submenu);
+ if (menu && sub_menu) {
+ if (sub_menu == menu) {
+ ERR_PRINT("Can't set submenu to self!");
+ return;
+ }
+ if ([sub_menu supermenu]) {
+ ERR_PRINT("Can't set submenu to menu that is already a submenu of some other menu!");
+ return;
+ }
+ NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@""];
+ [menu setSubmenu:sub_menu forItem:menu_item];
+ }
+}
+
+void DisplayServerOSX::global_menu_add_separator(const String &p_menu_root) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ [menu addItem:[NSMenuItem separatorItem]];
+ }
+}
+
+bool DisplayServerOSX::global_menu_is_item_checked(const String &p_menu_root, int p_idx) const {
+ _THREAD_SAFE_METHOD_
+
+ const NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ return ([menu_item state] == NSControlStateValueOn);
+ }
+ }
+ return false;
+}
+
+bool DisplayServerOSX::global_menu_is_item_checkable(const String &p_menu_root, int p_idx) const {
+ _THREAD_SAFE_METHOD_
+
+ const NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ GlobalMenuItem *obj = [menu_item representedObject];
+ if (obj) {
+ return obj->checkable;
+ }
+ }
+ }
+ return false;
+}
+
+Callable DisplayServerOSX::global_menu_get_item_callback(const String &p_menu_root, int p_idx) {
+ _THREAD_SAFE_METHOD_
+
+ const NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ GlobalMenuItem *obj = [menu_item representedObject];
+ if (obj) {
+ return obj->callback;
+ }
+ }
+ }
+ return Callable();
+}
+
+Variant DisplayServerOSX::global_menu_get_item_tag(const String &p_menu_root, int p_idx) {
+ _THREAD_SAFE_METHOD_
+
+ const NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ GlobalMenuItem *obj = [menu_item representedObject];
+ if (obj) {
+ return obj->meta;
+ }
+ }
+ }
+ return Variant();
+}
+
+String DisplayServerOSX::global_menu_get_item_text(const String &p_menu_root, int p_idx) {
+ _THREAD_SAFE_METHOD_
+
+ const NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ char *utfs = strdup([[menu_item title] UTF8String]);
+ String ret;
+ ret.parse_utf8(utfs);
+ free(utfs);
+ return ret;
+ }
+ }
+ return String();
+}
+
+String DisplayServerOSX::global_menu_get_item_submenu(const String &p_menu_root, int p_idx) {
+ _THREAD_SAFE_METHOD_
+
+ const NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ const NSMenu *sub_menu = [menu_item submenu];
+ if (sub_menu) {
+ for (Map<String, NSMenu *>::Element *E = submenu.front(); E; E = E->next()) {
+ if (E->get() == sub_menu) return E->key();
+ }
+ }
+ }
+ }
+ return String();
+}
+
+void DisplayServerOSX::global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+ return;
+ }
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ if (p_checked) {
+ [menu_item setState:NSControlStateValueOn];
+ } else {
+ [menu_item setState:NSControlStateValueOff];
+ }
+ }
+ }
+}
+
+void DisplayServerOSX::global_menu_set_item_checkable(const String &p_menu_root, int p_idx, bool p_checkable) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+ return;
+ }
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ GlobalMenuItem *obj = [menu_item representedObject];
+ obj->checkable = p_checkable;
+ }
+ }
+}
+
+void DisplayServerOSX::global_menu_set_item_callback(const String &p_menu_root, int p_idx, const Callable &p_callback) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+ return;
+ }
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ GlobalMenuItem *obj = [menu_item representedObject];
+ obj->callback = p_callback;
+ }
+ }
+}
+
+void DisplayServerOSX::global_menu_set_item_tag(const String &p_menu_root, int p_idx, const Variant &p_tag) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+ return;
+ }
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ GlobalMenuItem *obj = [menu_item representedObject];
+ obj->meta = p_tag;
+ }
+ }
+}
+
+void DisplayServerOSX::global_menu_set_item_text(const String &p_menu_root, int p_idx, const String &p_text) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+ return;
+ }
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ [menu_item setTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]];
+ }
+ }
+}
+
+void DisplayServerOSX::global_menu_set_item_submenu(const String &p_menu_root, int p_idx, const String &p_submenu) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ NSMenu *sub_menu = _get_menu_root(p_submenu);
+ if (menu && sub_menu) {
+ if (sub_menu == menu) {
+ ERR_PRINT("Can't set submenu to self!");
+ return;
+ }
+ if ([sub_menu supermenu]) {
+ ERR_PRINT("Can't set submenu to menu that is already a submenu of some other menu!");
+ return;
+ }
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
+ return;
+ }
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
+ if (menu_item) {
+ [menu setSubmenu:sub_menu forItem:menu_item];
+ }
+ }
+}
+
+int DisplayServerOSX::global_menu_get_item_count(const String &p_menu_root) const {
+ _THREAD_SAFE_METHOD_
+
+ const NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ return [menu numberOfItems];
+ } else {
+ return 0;
+ }
+}
+
+void DisplayServerOSX::global_menu_remove_item(const String &p_menu_root, int p_idx) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not delete Apple menu.
+ return;
+ }
+ [menu removeItemAtIndex:p_idx];
+ }
+}
+
+void DisplayServerOSX::global_menu_clear(const String &p_menu_root) {
+ _THREAD_SAFE_METHOD_
+
+ NSMenu *menu = _get_menu_root(p_menu_root);
+ if (menu) {
+ [menu removeAllItems];
+ // Restore Apple menu.
+ if (menu == [NSApp mainMenu]) {
+ NSMenuItem *menu_item = [menu addItemWithTitle:@"" action:nil keyEquivalent:@""];
+ [menu setSubmenu:apple_menu forItem:menu_item];
+ }
+ }
+}
+
+void DisplayServerOSX::alert(const String &p_alert, const String &p_title) {
+ _THREAD_SAFE_METHOD_
+
+ NSAlert *window = [[NSAlert alloc] init];
+ NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
+ NSString *ns_alert = [NSString stringWithUTF8String:p_alert.utf8().get_data()];
+
+ [window addButtonWithTitle:@"OK"];
+ [window setMessageText:ns_title];
+ [window setInformativeText:ns_alert];
+ [window setAlertStyle:NSAlertStyleWarning];
+
+ [window runModal];
+ [window release];
+}
+
+Error DisplayServerOSX::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) {
+ _THREAD_SAFE_METHOD_
+
+ NSAlert *window = [[NSAlert alloc] init];
+ NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
+ NSString *ns_description = [NSString stringWithUTF8String:p_description.utf8().get_data()];
+
+ for (int i = 0; i < p_buttons.size(); i++) {
+ NSString *ns_button = [NSString stringWithUTF8String:p_buttons[i].utf8().get_data()];
+ [window addButtonWithTitle:ns_button];
+ }
+ [window setMessageText:ns_title];
+ [window setInformativeText:ns_description];
+ [window setAlertStyle:NSAlertStyleInformational];
+
+ int button_pressed;
+ NSInteger ret = [window runModal];
+ if (ret == NSAlertFirstButtonReturn) {
+ button_pressed = 0;
+ } else if (ret == NSAlertSecondButtonReturn) {
+ button_pressed = 1;
+ } else if (ret == NSAlertThirdButtonReturn) {
+ button_pressed = 2;
+ } else {
+ button_pressed = 2 + (ret - NSAlertThirdButtonReturn);
+ }
+
+ if (!p_callback.is_null()) {
+ Variant button = button_pressed;
+ Variant *buttonp = &button;
+ Variant ret;
+ Callable::CallError ce;
+ p_callback.call((const Variant **)&buttonp, 1, ret, ce);
+ }
+
+ [window release];
+ return OK;
+}
+
+Error DisplayServerOSX::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) {
+ _THREAD_SAFE_METHOD_
+
+ NSAlert *window = [[NSAlert alloc] init];
+ NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
+ NSString *ns_description = [NSString stringWithUTF8String:p_description.utf8().get_data()];
+ NSTextField *input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 250, 30)];
+
+ [window addButtonWithTitle:@"OK"];
+ [window setMessageText:ns_title];
+ [window setInformativeText:ns_description];
+ [window setAlertStyle:NSAlertStyleInformational];
+
+ [input setStringValue:[NSString stringWithUTF8String:p_partial.utf8().get_data()]];
+ [window setAccessoryView:input];
+
+ [window runModal];
+
+ char *utfs = strdup([[input stringValue] UTF8String]);
+ String ret;
+ ret.parse_utf8(utfs);
+ free(utfs);
+
+ if (!p_callback.is_null()) {
+ Variant text = ret;
+ Variant *textp = &text;
+ Variant ret;
+ Callable::CallError ce;
+ p_callback.call((const Variant **)&textp, 1, ret, ce);
+ }
+
+ [window release];
+ return OK;
+}
+
+void DisplayServerOSX::mouse_set_mode(MouseMode p_mode) {
+ _THREAD_SAFE_METHOD_
+
+ if (p_mode == mouse_mode)
+ return;
+
+ if (p_mode == MOUSE_MODE_CAPTURED) {
+ // Apple Docs state that the display parameter is not used.
+ // "This parameter is not used. By default, you may pass kCGDirectMainDisplay."
+ // https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/Quartz_Services_Ref/Reference/reference.html
+ CGDisplayHideCursor(kCGDirectMainDisplay);
+ CGAssociateMouseAndMouseCursorPosition(false);
+ } else if (p_mode == MOUSE_MODE_HIDDEN) {
+ CGDisplayHideCursor(kCGDirectMainDisplay);
+ CGAssociateMouseAndMouseCursorPosition(true);
+ } else {
+ CGDisplayShowCursor(kCGDirectMainDisplay);
+ CGAssociateMouseAndMouseCursorPosition(true);
+ }
+
+ mouse_mode = p_mode;
+}
+
+DisplayServer::MouseMode DisplayServerOSX::mouse_get_mode() const {
+ return mouse_mode;
+}
+
+void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) {
+ _THREAD_SAFE_METHOD_
+
+ if (mouse_mode == MOUSE_MODE_CAPTURED) {
+ last_mouse_pos = p_to;
+ } else {
+ WindowData &wd = windows[MAIN_WINDOW_ID];
+
+ //local point in window coords
+ const NSRect contentRect = [wd.window_view frame];
+ float displayScale = _display_scale([wd.window_object screen]);
+ NSRect pointInWindowRect = NSMakeRect(p_to.x / displayScale, contentRect.size.height - (p_to.y / displayScale) - 1, 0, 0);
+ NSPoint pointOnScreen = [[wd.window_view window] convertRectToScreen:pointInWindowRect].origin;
+
+ //point in scren coords
+ CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y };
+
+ //do the warping
+ CGEventSourceRef lEventRef = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
+ CGEventSourceSetLocalEventsSuppressionInterval(lEventRef, 0.0);
+ CGAssociateMouseAndMouseCursorPosition(false);
+ CGWarpMouseCursorPosition(lMouseWarpPos);
+ CGAssociateMouseAndMouseCursorPosition(true);
+ }
+}
+
+Point2i DisplayServerOSX::mouse_get_position() const {
+ return last_mouse_pos;
+}
+
+Point2i DisplayServerOSX::mouse_get_absolute_position() const {
+ _THREAD_SAFE_METHOD_
+
+ const NSPoint mouse_pos = [NSEvent mouseLocation];
+
+ for (NSScreen *screen in [NSScreen screens]) {
+ NSRect frame = [screen frame];
+ if (NSMouseInRect(mouse_pos, frame, NO)) {
+ return Vector2i((int)mouse_pos.x, (int)-mouse_pos.y) + _get_screens_origin();
+ }
+ }
+ return Vector2i();
+}
+
+int DisplayServerOSX::mouse_get_button_state() const {
+ return last_button_state;
+}
+
+void DisplayServerOSX::clipboard_set(const String &p_text) {
+ _THREAD_SAFE_METHOD_
+
+ NSString *copiedString = [NSString stringWithUTF8String:p_text.utf8().get_data()];
+ NSArray *copiedStringArray = [NSArray arrayWithObject:copiedString];
+
+ NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+ [pasteboard clearContents];
+ [pasteboard writeObjects:copiedStringArray];
+}
+
+String DisplayServerOSX::clipboard_get() const {
+ _THREAD_SAFE_METHOD_
+
+ NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
+ NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
+ NSDictionary *options = [NSDictionary dictionary];
+
+ BOOL ok = [pasteboard canReadObjectForClasses:classArray options:options];
+
+ if (!ok) {
+ return "";
+ }
+
+ NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options];
+ NSString *string = [objectsToPaste objectAtIndex:0];
+
+ char *utfs = strdup([string UTF8String]);
+ String ret;
+ ret.parse_utf8(utfs);
+ free(utfs);
+
+ return ret;
+}
+
+int DisplayServerOSX::get_screen_count() const {
+ _THREAD_SAFE_METHOD_
+
+ NSArray *screenArray = [NSScreen screens];
+ return [screenArray count];
+}
+
+// Returns the native top-left screen coordinate of the smallest rectangle
+// that encompasses all screens. Needed in get_screen_position(),
+// window_get_position, and window_set_position()
+// to convert between OS X native screen coordinates and the ones expected by Godot
+
+static bool displays_arrangement_dirty = true;
+static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) {
+ displays_arrangement_dirty = true;
+}
+
+float DisplayServerOSX::_display_scale(id screen) const {
+ if (OS_OSX::get_singleton()->is_hidpi_allowed()) {
+ if ([screen respondsToSelector:@selector(backingScaleFactor)]) {
+ return fmax(1.0, [screen backingScaleFactor]);
+ }
+ }
+ return 1.0;
+}
+
+Point2i DisplayServerOSX::_get_screens_origin() const {
+ static Point2i origin;
+
+ if (displays_arrangement_dirty) {
+ origin = Point2i();
+
+ for (int i = 0; i < get_screen_count(); i++) {
+ Point2i position = _get_native_screen_position(i);
+ if (position.x < origin.x) {
+ origin.x = position.x;
+ }
+ if (position.y > origin.y) {
+ origin.y = position.y;
+ }
+ }
+ displays_arrangement_dirty = false;
+ }
+
+ return origin;
+}
+
+Point2i DisplayServerOSX::_get_native_screen_position(int p_screen) const {
+ NSArray *screenArray = [NSScreen screens];
+ if ((NSUInteger)p_screen < [screenArray count]) {
+ float display_scale = _display_scale([screenArray objectAtIndex:p_screen]);
+ NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
+ // Return the top-left corner of the screen, for OS X the y starts at the bottom
+ return Point2i(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * display_scale;
+ }
+
+ return Point2i();
+}
+
+Point2i DisplayServerOSX::screen_get_position(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
+ }
+
+ Point2i position = _get_native_screen_position(p_screen) - _get_screens_origin();
+ // OS X native y-coordinate relative to _get_screens_origin() is negative,
+ // Godot expects a positive value
+ position.y *= -1;
+ return position;
+}
+
+Size2i DisplayServerOSX::screen_get_size(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
+ }
+
+ NSArray *screenArray = [NSScreen screens];
+ if ((NSUInteger)p_screen < [screenArray count]) {
+ float displayScale = _display_scale([screenArray objectAtIndex:p_screen]);
+ // Note: Use frame to get the whole screen size
+ NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
+ return Size2i(nsrect.size.width, nsrect.size.height) * displayScale;
+ }
+
+ return Size2i();
+}
+
+int DisplayServerOSX::screen_get_dpi(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
+ }
+
+ NSArray *screenArray = [NSScreen screens];
+ if ((NSUInteger)p_screen < [screenArray count]) {
+ float displayScale = _display_scale([screenArray objectAtIndex:p_screen]);
+ NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription];
+ NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
+ CGSize displayPhysicalSize = CGDisplayScreenSize(
+ [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]);
+
+ return (displayPixelSize.width * 25.4f / displayPhysicalSize.width) * displayScale;
+ }
+
+ return 96;
+}
+
+float DisplayServerOSX::screen_get_scale(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
+ }
+ NSArray *screenArray = [NSScreen screens];
+ if ((NSUInteger)p_screen < [screenArray count]) {
+ return _display_scale([screenArray objectAtIndex:p_screen]);
+ }
+
+ return 1.f;
+}
+
+Rect2i DisplayServerOSX::screen_get_usable_rect(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ if (p_screen == SCREEN_OF_MAIN_WINDOW) {
+ p_screen = window_get_current_screen();
+ }
+
+ NSArray *screenArray = [NSScreen screens];
+ if ((NSUInteger)p_screen < [screenArray count]) {
+ float displayScale = _display_scale([screenArray objectAtIndex:p_screen]);
+ NSRect nsrect = [[screenArray objectAtIndex:p_screen] visibleFrame];
+
+ Point2i position = Point2i(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * displayScale - _get_screens_origin();
+ position.y *= -1;
+ Size2i size = Size2i(nsrect.size.width, nsrect.size.height) * displayScale;
+
+ return Rect2i(position, size);
+ }
+
+ return Rect2i();
+}
+
+Vector<DisplayServer::WindowID> DisplayServerOSX::get_window_list() const {
+ _THREAD_SAFE_METHOD_
+
+ Vector<int> ret;
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+
+DisplayServer::WindowID DisplayServerOSX::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+ _THREAD_SAFE_METHOD_
+
+ WindowID id = _create_window(p_mode, p_rect);
+ WindowData &wd = windows[id];
+ for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
+ if (p_flags & (1 << i)) {
+ window_set_flag(WindowFlags(i), true, id);
+ }
+ }
+ [wd.window_object makeKeyAndOrderFront:nil];
+ return id;
+}
+
+void DisplayServerOSX::_send_window_event(const WindowData &wd, WindowEvent p_event) {
+ if (!wd.event_callback.is_null()) {
+ Variant event = int(p_event);
+ Variant *eventp = &event;
+ Variant ret;
+ Callable::CallError ce;
+ wd.event_callback.call((const Variant **)&eventp, 1, ret, ce);
+ }
+}
+
+DisplayServerOSX::WindowID DisplayServerOSX::_find_window_id(id p_window) {
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (E->get().window_object == p_window)
+ return E->key();
+ }
+ return INVALID_WINDOW_ID;
+}
+
+void DisplayServerOSX::_update_window(WindowData p_wd) {
+ bool borderless_full = false;
+
+ if (p_wd.borderless) {
+ NSRect frameRect = [p_wd.window_object frame];
+ NSRect screenRect = [[p_wd.window_object screen] frame];
+
+ // Check if our window covers up the screen
+ if (frameRect.origin.x <= screenRect.origin.x && frameRect.origin.y <= frameRect.origin.y &&
+ frameRect.size.width >= screenRect.size.width && frameRect.size.height >= screenRect.size.height) {
+ borderless_full = true;
+ }
+ }
+
+ if (borderless_full) {
+ // If the window covers up the screen set the level to above the main menu and hide on deactivate
+ [p_wd.window_object setLevel:NSMainMenuWindowLevel + 1];
+ [p_wd.window_object setHidesOnDeactivate:YES];
+ } else {
+ // Reset these when our window is not a borderless window that covers up the screen
+ [p_wd.window_object setLevel:NSNormalWindowLevel];
+ [p_wd.window_object setHidesOnDeactivate:NO];
+ }
+}
+
+void DisplayServerOSX::delete_sub_window(WindowID p_id) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_id));
+ ERR_FAIL_COND_MSG(p_id == MAIN_WINDOW_ID, "Main window can't be deleted");
+
+ WindowData &wd = windows[p_id];
+
+ while (wd.transient_children.size()) {
+ window_set_transient(wd.transient_children.front()->get(), INVALID_WINDOW_ID);
+ }
+
+ if (wd.transient_parent != INVALID_WINDOW_ID) {
+ WindowData &pwd = windows[wd.transient_parent];
+ [pwd.window_object makeKeyAndOrderFront:nil]; // Move focus back to parent.
+ window_set_transient(p_id, INVALID_WINDOW_ID);
+ }
+
+ [wd.window_object setContentView:nil];
+ [wd.window_object close];
+
+ windows.erase(p_id);
+}
+
+void DisplayServerOSX::window_set_title(const String &p_title, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ [wd.window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]];
+}
+
+void DisplayServerOSX::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.rect_changed_callback = p_callable;
+}
+
+void DisplayServerOSX::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.event_callback = p_callable;
+}
+
+void DisplayServerOSX::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.input_event_callback = p_callable;
+}
+
+void DisplayServerOSX::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.input_text_callback = p_callable;
+}
+
+void DisplayServerOSX::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ wd.drop_files_callback = p_callable;
+}
+
+int DisplayServerOSX::window_get_current_screen(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND_V(!windows.has(p_window), -1);
+ const WindowData &wd = windows[p_window];
+
+ const NSUInteger index = [[NSScreen screens] indexOfObject:[wd.window_object screen]];
+ return (index == NSNotFound) ? 0 : index;
+}
+
+void DisplayServerOSX::window_set_current_screen(int p_screen, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+ Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));
+ window_set_position(wpos + screen_get_position(p_screen), p_window);
+}
+
+void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent) {
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND(p_window == p_parent);
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd_window = windows[p_window];
+
+ ERR_FAIL_COND(wd_window.transient_parent == p_parent);
+
+ ERR_FAIL_COND_MSG(wd_window.on_top, "Windows with the 'on top' can't become transient.");
+ if (p_parent == INVALID_WINDOW_ID) {
+ //remove transient
+ ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID);
+ ERR_FAIL_COND(!windows.has(wd_window.transient_parent));
+
+ WindowData &wd_parent = windows[wd_window.transient_parent];
+
+ wd_window.transient_parent = INVALID_WINDOW_ID;
+ wd_parent.transient_children.erase(p_window);
+
+ [wd_window.window_object setParentWindow:nil];
+ } else {
+ ERR_FAIL_COND(!windows.has(p_parent));
+ ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
+ WindowData &wd_parent = windows[p_parent];
+
+ wd_window.transient_parent = p_parent;
+ wd_parent.transient_children.insert(p_window);
+
+ [wd_window.window_object setParentWindow:wd_parent.window_object];
+ }
+}
+
+Point2i DisplayServerOSX::window_get_position(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Point2i());
+ const WindowData &wd = windows[p_window];
+
+ NSRect nsrect = [wd.window_object frame];
+ Point2i pos;
+ float display_scale = _display_scale([wd.window_object screen]);
+
+ // Return the position of the top-left corner, for OS X the y starts at the bottom
+ pos.x = nsrect.origin.x * display_scale;
+ pos.y = (nsrect.origin.y + nsrect.size.height) * display_scale;
+ pos -= _get_screens_origin();
+ // OS X native y-coordinate relative to _get_screens_origin() is negative,
+ // Godot expects a positive value
+ pos.y *= -1;
+ return pos;
+}
+
+void DisplayServerOSX::window_set_position(const Point2i &p_position, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ Point2i position = p_position;
+ // OS X native y-coordinate relative to _get_screens_origin() is negative,
+ // Godot passes a positive value
+ position.y *= -1;
+ position += _get_screens_origin();
+
+ NSPoint pos;
+ float displayScale = _display_scale([wd.window_object screen]);
+
+ pos.x = position.x / displayScale;
+ pos.y = position.y / displayScale;
+
+ [wd.window_object setFrameTopLeftPoint:pos];
+
+ _update_window(wd);
+ _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream], displayScale);
+}
+
+void DisplayServerOSX::window_set_max_size(const Size2i p_size, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if ((p_size != Size2i()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {
+ ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
+ return;
+ }
+ wd.max_size = p_size;
+
+ if ((wd.max_size != Size2i()) && !wd.fullscreen) {
+ Size2i size = wd.max_size / _display_scale([wd.window_object screen]);
+ [wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
+ } else {
+ [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
+ }
+}
+
+Size2i DisplayServerOSX::window_get_max_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+ return wd.max_size;
+}
+
+void DisplayServerOSX::window_set_min_size(const Size2i p_size, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if ((p_size != Size2i()) && (wd.max_size != Size2i()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {
+ ERR_PRINT("Minimum window size can't be larger than maximum window size!");
+ return;
+ }
+ wd.min_size = p_size;
+
+ if ((wd.min_size != Size2i()) && !wd.fullscreen) {
+ Size2i size = wd.min_size / _display_scale([wd.window_object screen]);
+ [wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)];
+ } else {
+ [wd.window_object setContentMinSize:NSMakeSize(0, 0)];
+ }
+}
+
+Size2i DisplayServerOSX::window_get_min_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+
+ return wd.min_size;
+}
+
+void DisplayServerOSX::window_set_size(const Size2i p_size, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ Size2i size = p_size / _display_scale([wd.window_object screen]);
+
+ if (!wd.borderless) {
+ // NSRect used by setFrame includes the title bar, so add it to our size.y
+ CGFloat menuBarHeight = [[[NSApplication sharedApplication] mainMenu] menuBarHeight];
+ if (menuBarHeight != 0.f) {
+ size.y += menuBarHeight;
+ }
+ }
+
+ NSRect frame = [wd.window_object frame];
+ [wd.window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, size.x, size.y) display:YES];
+
+ _update_window(wd);
+}
+
+Size2i DisplayServerOSX::window_get_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+ return wd.size;
+}
+
+Size2i DisplayServerOSX::window_get_real_size(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+ NSRect frame = [wd.window_object frame];
+ return Size2i(frame.size.width, frame.size.height) * _display_scale([wd.window_object screen]);
+}
+
+bool DisplayServerOSX::window_is_maximize_allowed(WindowID p_window) const {
+ return true;
+}
+
+void DisplayServerOSX::_set_window_per_pixel_transparency_enabled(bool p_enabled, WindowID p_window) {
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if (!OS_OSX::get_singleton()->is_layered_allowed()) return;
+ if (wd.layered_window != p_enabled) {
+ if (p_enabled) {
+ [wd.window_object setBackgroundColor:[NSColor clearColor]];
+ [wd.window_object setOpaque:NO];
+ [wd.window_object setHasShadow:NO];
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ CALayer *layer = [wd.window_view layer];
+ [layer setOpaque:NO];
+ //TODO - implement transparency for Vulkan
+ }
+#endif
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver == "opengl_es") {
+ //TODO - reimplement OpenGLES
+ wd.context_gles2->set_opacity(0);
+ }
+#endif
+ wd.layered_window = true;
+ } else {
+ [wd.window_object setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1]];
+ [wd.window_object setOpaque:YES];
+ [wd.window_object setHasShadow:YES];
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ CALayer *layer = [wd.window_view layer];
+ [layer setOpaque:YES];
+ //TODO - implement transparency for Vulkan
+ }
+#endif
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver == "opengl_es") {
+ //TODO - reimplement OpenGLES
+ wd.context_gles2->set_opacity(1);
+ }
+#endif
+ wd.layered_window = false;
+ }
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver == "opengl_es") {
+ //TODO - reimplement OpenGLES
+ wd.context_gles2->update();
+ }
+#endif
+ NSRect frameRect = [wd.window_object frame];
+ [wd.window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:YES];
+ [wd.window_object setFrame:frameRect display:YES];
+ }
+}
+
+void DisplayServerOSX::window_set_mode(WindowMode p_mode, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ WindowMode old_mode = window_get_mode(p_window);
+ if (old_mode == p_mode) {
+ return; // do nothing
+ }
+
+ switch (old_mode) {
+ case WINDOW_MODE_WINDOWED: {
+ //do nothing
+ } break;
+ case WINDOW_MODE_MINIMIZED: {
+ [wd.window_object deminiaturize:nil];
+ } break;
+ case WINDOW_MODE_FULLSCREEN: {
+ if (wd.layered_window)
+ _set_window_per_pixel_transparency_enabled(true, p_window);
+ if (wd.resize_disabled) //restore resize disabled
+ [wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
+ if (wd.min_size != Size2i()) {
+ Size2i size = wd.min_size / _display_scale([wd.window_object screen]);
+ [wd.window_object setContentMinSize:NSMakeSize(size.x, size.y)];
+ }
+ if (wd.max_size != Size2i()) {
+ Size2i size = wd.max_size / _display_scale([wd.window_object screen]);
+ [wd.window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
+ }
+ [wd.window_object toggleFullScreen:nil];
+ wd.fullscreen = false;
+ } break;
+ case WINDOW_MODE_MAXIMIZED: {
+ if ([wd.window_object isZoomed]) {
+ [wd.window_object zoom:nil];
+ }
+ } break;
+ }
+
+ switch (p_mode) {
+ case WINDOW_MODE_WINDOWED: {
+ //do nothing
+ } break;
+ case WINDOW_MODE_MINIMIZED: {
+ [wd.window_object performMiniaturize:nil];
+ } break;
+ case WINDOW_MODE_FULLSCREEN: {
+ if (wd.layered_window)
+ _set_window_per_pixel_transparency_enabled(false, p_window);
+ if (wd.resize_disabled) //fullscreen window should be resizable to work
+ [wd.window_object setStyleMask:[wd.window_object styleMask] | NSWindowStyleMaskResizable];
+ [wd.window_object setContentMinSize:NSMakeSize(0, 0)];
+ [wd.window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
+ [wd.window_object toggleFullScreen:nil];
+ wd.fullscreen = true;
+ } break;
+ case WINDOW_MODE_MAXIMIZED: {
+ if (![wd.window_object isZoomed]) {
+ [wd.window_object zoom:nil];
+ }
+ } break;
+ }
+}
+
+DisplayServer::WindowMode DisplayServerOSX::window_get_mode(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED);
+ const WindowData &wd = windows[p_window];
+
+ if (wd.fullscreen) { //if fullscreen, it's not in another mode
+ return WINDOW_MODE_FULLSCREEN;
+ }
+ if ([wd.window_object isZoomed] && !wd.resize_disabled) {
+ return WINDOW_MODE_MAXIMIZED;
+ }
+ if ([wd.window_object respondsToSelector:@selector(isMiniaturized)]) {
+ if ([wd.window_object isMiniaturized]) {
+ return WINDOW_MODE_MINIMIZED;
+ }
+ }
+
+ // all other discarded, return windowed.
+ return WINDOW_MODE_WINDOWED;
+}
+
+void DisplayServerOSX::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ switch (p_flag) {
+ case WINDOW_FLAG_RESIZE_DISABLED: {
+ wd.resize_disabled = p_enabled;
+ if (wd.fullscreen) //fullscreen window should be resizable, style will be applyed on exiting fs
+ return;
+ if (p_enabled) {
+ [wd.window_object setStyleMask:[wd.window_object styleMask] & ~NSWindowStyleMaskResizable];
+ } else {
+ [wd.window_object setStyleMask:[wd.window_object styleMask] | NSWindowStyleMaskResizable];
+ }
+ } break;
+ case WINDOW_FLAG_BORDERLESS: {
+ // OrderOut prevents a lose focus bug with the window
+ [wd.window_object orderOut:nil];
+ wd.borderless = p_enabled;
+ if (p_enabled) {
+ [wd.window_object setStyleMask:NSWindowStyleMaskBorderless];
+ } else {
+ if (wd.layered_window)
+ _set_window_per_pixel_transparency_enabled(false, p_window);
+ [wd.window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (wd.resize_disabled ? 0 : NSWindowStyleMaskResizable)];
+ // Force update of the window styles
+ NSRect frameRect = [wd.window_object frame];
+ [wd.window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO];
+ [wd.window_object setFrame:frameRect display:NO];
+ }
+ _update_window(wd);
+ [wd.window_object makeKeyAndOrderFront:nil];
+ } break;
+ case WINDOW_FLAG_ALWAYS_ON_TOP: {
+ wd.on_top = p_enabled;
+ if (p_enabled) {
+ [wd.window_object setLevel:NSFloatingWindowLevel];
+ } else {
+ [wd.window_object setLevel:NSNormalWindowLevel];
+ }
+ } break;
+ case WINDOW_FLAG_TRANSPARENT: {
+ wd.layered_window = p_enabled;
+ if (p_enabled) {
+ [wd.window_object setStyleMask:NSWindowStyleMaskBorderless]; // force borderless
+ } else if (!wd.borderless) {
+ [wd.window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (wd.resize_disabled ? 0 : NSWindowStyleMaskResizable)];
+ }
+ _set_window_per_pixel_transparency_enabled(p_enabled, p_window);
+
+ } break;
+ default: {
+ }
+ }
+}
+
+bool DisplayServerOSX::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), false);
+ const WindowData &wd = windows[p_window];
+
+ switch (p_flag) {
+ case WINDOW_FLAG_RESIZE_DISABLED: {
+ return wd.resize_disabled;
+ } break;
+ case WINDOW_FLAG_BORDERLESS: {
+ return [wd.window_object styleMask] == NSWindowStyleMaskBorderless;
+ } break;
+ case WINDOW_FLAG_ALWAYS_ON_TOP: {
+ return [wd.window_object level] == NSFloatingWindowLevel;
+ } break;
+ case WINDOW_FLAG_TRANSPARENT: {
+ return wd.layered_window;
+ } break;
+ default: {
+ }
+ }
+
+ return false;
+}
+
+void DisplayServerOSX::window_request_attention(WindowID p_window) {
+ // It's app global, ignore window id.
+ [NSApp requestUserAttention:NSCriticalRequest];
+}
+
+void DisplayServerOSX::window_move_to_foreground(WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ const WindowData &wd = windows[p_window];
+
+ [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
+ [wd.window_object makeKeyAndOrderFront:nil];
+}
+
+bool DisplayServerOSX::window_can_draw(WindowID p_window) const {
+ return window_get_mode(p_window) != WINDOW_MODE_MINIMIZED;
+}
+
+bool DisplayServerOSX::can_any_window_draw() const {
+ _THREAD_SAFE_METHOD_
+
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (window_get_mode(E->key()) != WINDOW_MODE_MINIMIZED) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void DisplayServerOSX::window_set_ime_active(const bool p_active, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ wd.im_active = p_active;
+
+ if (!p_active)
+ [wd.window_view cancelComposition];
+}
+
+void DisplayServerOSX::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ wd.im_position = p_pos;
+}
+
+bool DisplayServerOSX::get_swap_ok_cancel() {
+ return true;
+}
+
+void DisplayServerOSX::cursor_set_shape(CursorShape p_shape) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
+
+ if (cursor_shape == p_shape)
+ return;
+
+ if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
+ cursor_shape = p_shape;
+ return;
+ }
+
+ if (cursors[p_shape] != NULL) {
+ [cursors[p_shape] set];
+ } else {
+ switch (p_shape) {
+ case CURSOR_ARROW: [[NSCursor arrowCursor] set]; break;
+ case CURSOR_IBEAM: [[NSCursor IBeamCursor] set]; break;
+ case CURSOR_POINTING_HAND: [[NSCursor pointingHandCursor] set]; break;
+ case CURSOR_CROSS: [[NSCursor crosshairCursor] set]; break;
+ case CURSOR_WAIT: [[NSCursor arrowCursor] set]; break;
+ case CURSOR_BUSY: [[NSCursor arrowCursor] set]; break;
+ case CURSOR_DRAG: [[NSCursor closedHandCursor] set]; break;
+ case CURSOR_CAN_DROP: [[NSCursor openHandCursor] set]; break;
+ case CURSOR_FORBIDDEN: [[NSCursor operationNotAllowedCursor] set]; break;
+ case CURSOR_VSIZE: [_cursorFromSelector(@selector(_windowResizeNorthSouthCursor), @selector(resizeUpDownCursor)) set]; break;
+ case CURSOR_HSIZE: [_cursorFromSelector(@selector(_windowResizeEastWestCursor), @selector(resizeLeftRightCursor)) set]; break;
+ case CURSOR_BDIAGSIZE: [_cursorFromSelector(@selector(_windowResizeNorthEastSouthWestCursor)) set]; break;
+ case CURSOR_FDIAGSIZE: [_cursorFromSelector(@selector(_windowResizeNorthWestSouthEastCursor)) set]; break;
+ case CURSOR_MOVE: [[NSCursor arrowCursor] set]; break;
+ case CURSOR_VSPLIT: [[NSCursor resizeUpDownCursor] set]; break;
+ case CURSOR_HSPLIT: [[NSCursor resizeLeftRightCursor] set]; break;
+ case CURSOR_HELP: [_cursorFromSelector(@selector(_helpCursor)) set]; break;
+ default: {
+ }
+ }
+ }
+
+ cursor_shape = p_shape;
+}
+
+DisplayServerOSX::CursorShape DisplayServerOSX::cursor_get_shape() const {
+ return cursor_shape;
+}
+
+void DisplayServerOSX::cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
+ _THREAD_SAFE_METHOD_
+
+ if (p_cursor.is_valid()) {
+ Map<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);
+
+ if (cursor_c) {
+ if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {
+ cursor_set_shape(p_shape);
+ return;
+ }
+ cursors_cache.erase(p_shape);
+ }
+
+ Ref<Texture2D> texture = p_cursor;
+ Ref<AtlasTexture> atlas_texture = p_cursor;
+ Ref<Image> image;
+ Size2 texture_size;
+ Rect2 atlas_rect;
+
+ if (texture.is_valid()) {
+ image = texture->get_data();
+ }
+
+ if (!image.is_valid() && atlas_texture.is_valid()) {
+ texture = atlas_texture->get_atlas();
+
+ atlas_rect.size.width = texture->get_width();
+ atlas_rect.size.height = texture->get_height();
+ atlas_rect.position.x = atlas_texture->get_region().position.x;
+ atlas_rect.position.y = atlas_texture->get_region().position.y;
+
+ texture_size.width = atlas_texture->get_region().size.x;
+ texture_size.height = atlas_texture->get_region().size.y;
+ } else if (image.is_valid()) {
+ texture_size.width = texture->get_width();
+ texture_size.height = texture->get_height();
+ }
+
+ ERR_FAIL_COND(!texture.is_valid());
+ ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
+ ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
+ ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
+
+ image = texture->get_data();
+
+ ERR_FAIL_COND(!image.is_valid());
+
+ NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
+ initWithBitmapDataPlanes:NULL
+ pixelsWide:int(texture_size.width)
+ pixelsHigh:int(texture_size.height)
+ bitsPerSample:8
+ samplesPerPixel:4
+ hasAlpha:YES
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bytesPerRow:int(texture_size.width) * 4
+ bitsPerPixel:32];
+
+ ERR_FAIL_COND(imgrep == nil);
+ uint8_t *pixels = [imgrep bitmapData];
+
+ int len = int(texture_size.width * texture_size.height);
+
+ for (int i = 0; i < len; i++) {
+ int row_index = floor(i / texture_size.width) + atlas_rect.position.y;
+ int column_index = (i % int(texture_size.width)) + atlas_rect.position.x;
+
+ if (atlas_texture.is_valid()) {
+ column_index = MIN(column_index, atlas_rect.size.width - 1);
+ row_index = MIN(row_index, atlas_rect.size.height - 1);
+ }
+
+ uint32_t color = image->get_pixel(column_index, row_index).to_argb32();
+
+ uint8_t alpha = (color >> 24) & 0xFF;
+ pixels[i * 4 + 0] = ((color >> 16) & 0xFF) * alpha / 255;
+ pixels[i * 4 + 1] = ((color >> 8) & 0xFF) * alpha / 255;
+ pixels[i * 4 + 2] = ((color)&0xFF) * alpha / 255;
+ pixels[i * 4 + 3] = alpha;
+ }
+
+ NSImage *nsimage = [[NSImage alloc] initWithSize:NSMakeSize(texture_size.width, texture_size.height)];
+ [nsimage addRepresentation:imgrep];
+
+ NSCursor *cursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSMakePoint(p_hotspot.x, p_hotspot.y)];
+
+ [cursors[p_shape] release];
+ cursors[p_shape] = cursor;
+
+ Vector<Variant> params;
+ params.push_back(p_cursor);
+ params.push_back(p_hotspot);
+ cursors_cache.insert(p_shape, params);
+
+ if (p_shape == cursor_shape) {
+ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
+ [cursor set];
+ }
+ }
+
+ [imgrep release];
+ [nsimage release];
+ } else {
+ // Reset to default system cursor
+ if (cursors[p_shape] != NULL) {
+ [cursors[p_shape] release];
+ cursors[p_shape] = NULL;
+ }
+
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ cursor_set_shape(c);
+
+ cursors_cache.erase(p_shape);
+ }
+}
+
+static bool keyboard_layout_dirty = true;
+static void keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) {
+ keyboard_layout_dirty = true;
+}
+
+// Returns string representation of keys, if they are printable.
+static NSString *createStringForKeys(const CGKeyCode *keyCode, int length) {
+ TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
+ if (!currentKeyboard)
+ return nil;
+
+ CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
+ if (!layoutData)
+ return nil;
+
+ const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
+
+ OSStatus err;
+ CFMutableStringRef output = CFStringCreateMutable(NULL, 0);
+
+ for (int i = 0; i < length; ++i) {
+ UInt32 keysDown = 0;
+ UniChar chars[4];
+ UniCharCount realLength;
+
+ err = UCKeyTranslate(keyboardLayout,
+ keyCode[i],
+ kUCKeyActionDisplay,
+ 0,
+ LMGetKbdType(),
+ kUCKeyTranslateNoDeadKeysBit,
+ &keysDown,
+ sizeof(chars) / sizeof(chars[0]),
+ &realLength,
+ chars);
+
+ if (err != noErr) {
+ CFRelease(output);
+ return nil;
+ }
+
+ CFStringAppendCharacters(output, chars, 1);
+ }
+
+ return (NSString *)output;
+}
+
+DisplayServerOSX::LatinKeyboardVariant DisplayServerOSX::get_latin_keyboard_variant() const {
+ _THREAD_SAFE_METHOD_
+
+ static LatinKeyboardVariant layout = LATIN_KEYBOARD_QWERTY;
+
+ if (keyboard_layout_dirty) {
+
+ layout = LATIN_KEYBOARD_QWERTY;
+
+ CGKeyCode keys[] = { kVK_ANSI_Q, kVK_ANSI_W, kVK_ANSI_E, kVK_ANSI_R, kVK_ANSI_T, kVK_ANSI_Y };
+ NSString *test = createStringForKeys(keys, 6);
+
+ if ([test isEqualToString:@"qwertz"]) {
+ layout = LATIN_KEYBOARD_QWERTZ;
+ } else if ([test isEqualToString:@"azerty"]) {
+ layout = LATIN_KEYBOARD_AZERTY;
+ } else if ([test isEqualToString:@"qzerty"]) {
+ layout = LATIN_KEYBOARD_QZERTY;
+ } else if ([test isEqualToString:@"',.pyf"]) {
+ layout = LATIN_KEYBOARD_DVORAK;
+ } else if ([test isEqualToString:@"xvlcwk"]) {
+ layout = LATIN_KEYBOARD_NEO;
+ } else if ([test isEqualToString:@"qwfpgj"]) {
+ layout = LATIN_KEYBOARD_COLEMAK;
+ }
+
+ [test release];
+
+ keyboard_layout_dirty = false;
+ return layout;
+ }
+
+ return layout;
+}
+
+void DisplayServerOSX::_push_input(const Ref<InputEvent> &p_event) {
+ Ref<InputEvent> ev = p_event;
+ InputFilter::get_singleton()->accumulate_input_event(ev);
+}
+
+void DisplayServerOSX::_process_key_events() {
+ Ref<InputEventKey> k;
+ for (int i = 0; i < key_event_pos; i++) {
+ const KeyEvent &ke = key_event_buffer[i];
+ if (ke.raw) {
+ // Non IME input - no composite characters, pass events as is
+ k.instance();
+
+ k->set_window_id(ke.window_id);
+ _get_key_modifier_state(ke.osx_state, k);
+ k->set_pressed(ke.pressed);
+ k->set_echo(ke.echo);
+ k->set_keycode(ke.keycode);
+ k->set_physical_keycode(ke.physical_keycode);
+ k->set_unicode(ke.unicode);
+
+ _push_input(k);
+ } else {
+ // IME input
+ if ((i == 0 && ke.keycode == 0) || (i > 0 && key_event_buffer[i - 1].keycode == 0)) {
+ k.instance();
+
+ k->set_window_id(ke.window_id);
+ _get_key_modifier_state(ke.osx_state, k);
+ k->set_pressed(ke.pressed);
+ k->set_echo(ke.echo);
+ k->set_keycode(0);
+ k->set_physical_keycode(0);
+ k->set_unicode(ke.unicode);
+
+ _push_input(k);
+ }
+ if (ke.keycode != 0) {
+ k.instance();
+
+ k->set_window_id(ke.window_id);
+ _get_key_modifier_state(ke.osx_state, k);
+ k->set_pressed(ke.pressed);
+ k->set_echo(ke.echo);
+ k->set_keycode(ke.keycode);
+ k->set_physical_keycode(ke.physical_keycode);
+
+ if (i + 1 < key_event_pos && key_event_buffer[i + 1].keycode == 0) {
+ k->set_unicode(key_event_buffer[i + 1].unicode);
+ }
+
+ _push_input(k);
+ }
+ }
+ }
+
+ key_event_pos = 0;
+}
+
+void DisplayServerOSX::process_events() {
+ _THREAD_SAFE_METHOD_
+
+ while (true) {
+ NSEvent *event = [NSApp
+ nextEventMatchingMask:NSEventMaskAny
+ untilDate:[NSDate distantPast]
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+
+ if (event == nil)
+ break;
+
+ [NSApp sendEvent:event];
+ }
+
+ if (!drop_events) {
+ _process_key_events();
+ InputFilter::get_singleton()->flush_accumulated_events();
+ }
+
+ [autoreleasePool drain];
+ autoreleasePool = [[NSAutoreleasePool alloc] init];
+}
+
+void DisplayServerOSX::force_process_and_drop_events() {
+ _THREAD_SAFE_METHOD_
+
+ drop_events = true;
+ process_events();
+ drop_events = false;
+}
+
+void DisplayServerOSX::set_native_icon(const String &p_filename) {
+ _THREAD_SAFE_METHOD_
+
+ 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];
+ ERR_FAIL_COND_MSG(!icon_data, "Error reading icon data.");
+
+ NSImage *icon = [[[NSImage alloc] initWithData:icon_data] autorelease];
+ ERR_FAIL_COND_MSG(!icon, "Error loading icon.");
+
+ [NSApp setApplicationIconImage:icon];
+}
+
+void DisplayServerOSX::set_icon(const Ref<Image> &p_icon) {
+ _THREAD_SAFE_METHOD_
+
+ Ref<Image> img = p_icon;
+ img = img->duplicate();
+ img->convert(Image::FORMAT_RGBA8);
+ NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
+ initWithBitmapDataPlanes:NULL
+ pixelsWide:img->get_width()
+ pixelsHigh:img->get_height()
+ bitsPerSample:8
+ samplesPerPixel:4
+ hasAlpha:YES
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bytesPerRow:img->get_width() * 4
+ bitsPerPixel:32];
+ ERR_FAIL_COND(imgrep == nil);
+ uint8_t *pixels = [imgrep bitmapData];
+
+ int len = img->get_width() * img->get_height();
+ const uint8_t *r = img->get_data().ptr();
+
+ /* Premultiply the alpha channel */
+ for (int i = 0; i < len; i++) {
+ uint8_t alpha = r[i * 4 + 3];
+ pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255);
+ pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255);
+ pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255);
+ pixels[i * 4 + 3] = alpha;
+ }
+
+ NSImage *nsimg = [[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())];
+ ERR_FAIL_COND(nsimg == nil);
+
+ [nsimg addRepresentation:imgrep];
+ [NSApp setApplicationIconImage:nsimg];
+
+ [imgrep release];
+ [nsimg release];
+}
+
+Vector<String> DisplayServerOSX::get_rendering_drivers_func() {
+ Vector<String> drivers;
+
+#ifdef VULKAN_ENABLED
+ drivers.push_back("vulkan");
+#endif
+#ifdef OPENGL_ENABLED
+ drivers.push_back("opengl");
+#endif
+
+ return drivers;
+}
+
+Point2i DisplayServerOSX::ime_get_selection() const {
+ return im_selection;
+}
+
+String DisplayServerOSX::ime_get_text() const {
+ return im_text;
+}
+
+DisplayServer::WindowID DisplayServerOSX::get_window_at_screen_position(const Point2i &p_position) const {
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ Rect2i win_rect = Rect2i(window_get_position(E->key()), window_get_size(E->key()));
+ if (win_rect.has_point(p_position)) {
+ return E->key();
+ }
+ }
+ return INVALID_WINDOW_ID;
+}
+
+void DisplayServerOSX::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].instance_id = p_instance;
+}
+
+ObjectID DisplayServerOSX::window_get_attached_instance_id(WindowID p_window) const {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), ObjectID());
+ return windows[p_window].instance_id;
+}
+
+DisplayServer *DisplayServerOSX::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ return memnew(DisplayServerOSX(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+}
+
+DisplayServerOSX::WindowID DisplayServerOSX::_create_window(WindowMode p_mode, const Rect2i &p_rect) {
+ WindowID id;
+ {
+ WindowData wd;
+
+ float displayScale = 1.0;
+ if (OS_OSX::get_singleton()->is_hidpi_allowed()) {
+ // note that mainScreen is not screen #0 but the one with the keyboard focus.
+ NSScreen *screen = [NSScreen mainScreen];
+ if ([screen respondsToSelector:@selector(backingScaleFactor)]) {
+ displayScale = fmax(displayScale, [screen backingScaleFactor]);
+ }
+ }
+
+ wd.window_delegate = [[GodotWindowDelegate alloc] init];
+ ERR_FAIL_COND_V_MSG(wd.window_delegate == nil, INVALID_WINDOW_ID, "Can't create a window delegate");
+ [wd.window_delegate setWindowID:window_id_counter];
+
+ Point2i position = p_rect.position;
+ // OS X native y-coordinate relative to _get_screens_origin() is negative,
+ // Godot passes a positive value
+ position.y *= -1;
+ position += _get_screens_origin();
+
+ // initWithContentRect uses bottom-left corner of the window’s frame as origin.
+ wd.window_object = [[GodotWindow alloc]
+ initWithContentRect:NSMakeRect(position.x / displayScale, (position.y - p_rect.size.height) / displayScale, p_rect.size.width / displayScale, p_rect.size.height / displayScale)
+ styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable
+ backing:NSBackingStoreBuffered
+ defer:NO];
+ ERR_FAIL_COND_V_MSG(wd.window_object == nil, INVALID_WINDOW_ID, "Can't create a window");
+
+ wd.window_view = [[GodotContentView alloc] init];
+ ERR_FAIL_COND_V_MSG(wd.window_view == nil, INVALID_WINDOW_ID, "Can't create a window view");
+ [wd.window_view setWindowID:window_id_counter];
+ if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_14) {
+ [wd.window_view setWantsLayer:TRUE];
+ }
+
+ if (displayScale > 1.0) {
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver == "opengl_es") {
+ [wd.window_view setWantsBestResolutionOpenGLSurface:YES];
+ }
+#endif
+ [wd.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
+ } else {
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver == "opengl_es") {
+ [wd.window_view setWantsBestResolutionOpenGLSurface:NO];
+ }
+#endif
+ }
+
+ [wd.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
+ [wd.window_object setContentView:wd.window_view];
+ [wd.window_object setDelegate:wd.window_delegate];
+ [wd.window_object setAcceptsMouseMovedEvents:YES];
+ [wd.window_object setRestorable:NO];
+
+ if ([wd.window_object respondsToSelector:@selector(setTabbingMode:)])
+ [wd.window_object setTabbingMode:NSWindowTabbingModeDisallowed];
+
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ if (context_vulkan) {
+ CALayer *layer = [wd.window_view layer];
+ layer.contentsScale = displayScale;
+ Error err = context_vulkan->window_create(window_id_counter, wd.window_view, p_rect.size.width, p_rect.size.height);
+ ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a Vulkan context");
+ }
+ }
+#endif
+#ifdef OPENGL_ENABLED
+ if (rendering_driver == "opengl_es") {
+ //TODO - reimplement OpenGLES
+ wd.context_gles2 = memnew(ContextGL_OSX(wd.window_view, false));
+
+ if (wd.context_gles2->initialize() != OK) {
+ memdelete(wd.context_gles2);
+ ERR_FAIL_COND_V_MSG(err != OK, INVALID_WINDOW_ID, "Can't create a OpenGL context");
+ }
+
+ //if (RasterizerGLES2::is_viable() == OK) {
+ // RasterizerGLES2::register_config();
+ // RasterizerGLES2::make_current();
+ //}
+ }
+#endif
+ id = window_id_counter++;
+ windows[id] = wd;
+ }
+
+ WindowData &wd = windows[id];
+ window_set_mode(p_mode, id);
+
+ float displayScale = _display_scale([wd.window_object screen]);
+ const NSRect contentRect = [wd.window_view frame];
+ wd.size.width = contentRect.size.width * displayScale;
+ wd.size.height = contentRect.size.height * displayScale;
+
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver == "opengl_es") {
+ if (OS_OSX::singleton->is_hidpi_allowed()) {
+ [wd.window_view setWantsBestResolutionOpenGLSurface:YES];
+ } else {
+ [wd.window_view setWantsBestResolutionOpenGLSurface:NO];
+ }
+ wd.context_gles2->update();
+ }
+#endif
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ CALayer *layer = [wd.window_view layer];
+ layer.contentsScale = displayScale;
+ context_vulkan->window_resize(id, wd.size.width, wd.size.height);
+ }
+#endif
+
+ return id;
+}
+
+void DisplayServerOSX::_dispatch_input_events(const Ref<InputEvent> &p_event) {
+ ((DisplayServerOSX *)(get_singleton()))->_dispatch_input_event(p_event);
+}
+
+void DisplayServerOSX::_dispatch_input_event(const Ref<InputEvent> &p_event) {
+ Variant ev = p_event;
+ Variant *evp = &ev;
+ Variant ret;
+ Callable::CallError ce;
+
+ Ref<InputEventFromWindow> event_from_window = p_event;
+ if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) {
+ //send to a window
+ if (windows.has(event_from_window->get_window_id())) {
+ Callable callable = windows[event_from_window->get_window_id()].input_event_callback;
+ if (callable.is_null()) {
+ return;
+ }
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ }
+ } else {
+ //send to all windows
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ Callable callable = E->get().input_event_callback;
+ if (callable.is_null()) {
+ continue;
+ }
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ }
+ }
+}
+
+void DisplayServerOSX::release_rendering_thread() {
+ //TODO - reimplement OpenGLES
+}
+
+void DisplayServerOSX::make_rendering_thread() {
+ //TODO - reimplement OpenGLES
+}
+
+void DisplayServerOSX::swap_buffers() {
+ //TODO - reimplement OpenGLES
+}
+
+void DisplayServerOSX::console_set_visible(bool p_enabled) {
+ //TODO - open terminal and redirect
+}
+
+bool DisplayServerOSX::is_console_visible() const {
+ return isatty(STDIN_FILENO);
+}
+
+DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+ InputFilter::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
+
+ r_error = OK;
+ drop_events = false;
+
+ memset(cursors, 0, sizeof(cursors));
+ cursor_shape = CURSOR_ARROW;
+
+ key_event_pos = 0;
+ mouse_mode = MOUSE_MODE_VISIBLE;
+ last_button_state = 0;
+
+ autoreleasePool = [[NSAutoreleasePool alloc] init];
+
+ eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
+ ERR_FAIL_COND(!eventSource);
+
+ CGEventSourceSetLocalEventsSuppressionInterval(eventSource, 0.0);
+
+ // Implicitly create shared NSApplication instance
+ [GodotApplication sharedApplication];
+
+ // In case we are unbundled, make us a proper UI application
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
+
+ keyboard_layout_dirty = true;
+ displays_arrangement_dirty = true;
+
+ // Register to be notified on keyboard layout changes
+ CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
+ NULL, keyboard_layout_changed,
+ kTISNotifySelectedKeyboardInputSourceChanged, NULL,
+ CFNotificationSuspensionBehaviorDeliverImmediately);
+
+ // Register to be notified on displays arrangement changes
+ CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, NULL);
+
+ // Menu bar setup must go between sharedApplication above and
+ // finishLaunching below, in order to properly emulate the behavior
+ // of NSApplicationMain
+ NSMenuItem *menu_item;
+ NSString *title;
+
+ NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
+ if (nsappname == nil)
+ nsappname = [[NSProcessInfo processInfo] processName];
+
+ // Setup Dock menu
+ dock_menu = [[NSMenu alloc] initWithTitle:@"_dock"];
+
+ // Setup Apple menu
+ apple_menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
+ title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname];
+ [apple_menu addItemWithTitle:title action:@selector(showAbout:) keyEquivalent:@""];
+
+ [apple_menu addItem:[NSMenuItem separatorItem]];
+
+ NSMenu *services = [[NSMenu alloc] initWithTitle:@""];
+ menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Services", nil) action:nil keyEquivalent:@""];
+ [apple_menu setSubmenu:services forItem:menu_item];
+ [NSApp setServicesMenu:services];
+ [services release];
+
+ [apple_menu addItem:[NSMenuItem separatorItem]];
+
+ title = [NSString stringWithFormat:NSLocalizedString(@"Hide %@", nil), nsappname];
+ [apple_menu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
+
+ menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Hide Others", nil) action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
+ [menu_item setKeyEquivalentModifierMask:(NSEventModifierFlagOption | NSEventModifierFlagCommand)];
+
+ [apple_menu addItemWithTitle:NSLocalizedString(@"Show all", nil) action:@selector(unhideAllApplications:) keyEquivalent:@""];
+
+ [apple_menu addItem:[NSMenuItem separatorItem]];
+
+ title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname];
+ [apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
+
+ // Setup menu bar
+ NSMenu *main_menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
+ menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""];
+ [main_menu setSubmenu:apple_menu forItem:menu_item];
+ [NSApp setMainMenu:main_menu];
+
+ [NSApp finishLaunching];
+
+ delegate = [[GodotApplicationDelegate alloc] init];
+ ERR_FAIL_COND(!delegate);
+ [NSApp setDelegate:delegate];
+
+ //process application:openFile: event
+ while (true) {
+ NSEvent *event = [NSApp
+ nextEventMatchingMask:NSEventMaskAny
+ untilDate:[NSDate distantPast]
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+
+ if (event == nil)
+ break;
+
+ [NSApp sendEvent:event];
+ }
+
+ //!!!!!!!!!!!!!!!!!!!!!!!!!!
+ //TODO - do Vulkan and GLES2 support checks, driver selection and fallback
+ rendering_driver = p_rendering_driver;
+
+#ifndef _MSC_VER
+#warning Forcing vulkan rendering driver because OpenGL not implemented yet
+#endif
+ rendering_driver = "vulkan";
+
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver == "opengl_es") {
+ //TODO - reimplement OpenGLES
+ }
+#endif
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+
+ context_vulkan = memnew(VulkanContextOSX);
+ if (context_vulkan->initialize() != OK) {
+ memdelete(context_vulkan);
+ context_vulkan = NULL;
+ r_error = ERR_CANT_CREATE;
+ ERR_FAIL_MSG("Could not initialize Vulkan");
+ }
+ }
+#endif
+
+ WindowID main_window = _create_window(p_mode, Rect2i(Point2i(), p_resolution));
+ for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
+ if (p_flags & (1 << i)) {
+ window_set_flag(WindowFlags(i), true, main_window);
+ }
+ }
+ [windows[main_window].window_object makeKeyAndOrderFront:nil];
+
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ rendering_device_vulkan = memnew(RenderingDeviceVulkan);
+ rendering_device_vulkan->initialize(context_vulkan);
+
+ RasterizerRD::make_current();
+ }
+#endif
+
+ [NSApp activateIgnoringOtherApps:YES];
+
+ /*
+ visual_server = memnew(VisualServerRaster);
+ if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
+ visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
+ }
+ visual_server->init();
+ */
+}
+
+DisplayServerOSX::~DisplayServerOSX() {
+ if (dock_menu) {
+ [dock_menu release];
+ }
+
+ for (Map<String, NSMenu *>::Element *E = submenu.front(); E; E = E->next()) {
+ [E->get() release];
+ }
+
+ //destroy all windows
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ [E->get().window_object setContentView:nil];
+ [E->get().window_object close];
+ }
+
+ //destroy drivers
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+
+ if (rendering_device_vulkan) {
+ rendering_device_vulkan->finalize();
+ memdelete(rendering_device_vulkan);
+ }
+
+ if (context_vulkan)
+ memdelete(context_vulkan);
+ }
+#endif
+
+ CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, kTISNotifySelectedKeyboardInputSourceChanged, NULL);
+ CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL);
+
+ cursors_cache.clear();
+
+ //visual_server->finish();
+ //memdelete(visual_server);
+}
+
+void DisplayServerOSX::register_osx_driver() {
+ register_create_function("osx", create_func, get_rendering_drivers_func);
+}
diff --git a/platform/osx/joypad_osx.cpp b/platform/osx/joypad_osx.cpp
index e9f46fb5a4..f2d9de6fbd 100644
--- a/platform/osx/joypad_osx.cpp
+++ b/platform/osx/joypad_osx.cpp
@@ -395,38 +395,38 @@ bool joypad::check_ff_features() {
static int process_hat_value(int p_min, int p_max, int p_value) {
int range = (p_max - p_min + 1);
int value = p_value - p_min;
- int hat_value = InputDefault::HAT_MASK_CENTER;
+ int hat_value = InputFilter::HAT_MASK_CENTER;
if (range == 4) {
value *= 2;
}
switch (value) {
case 0:
- hat_value = InputDefault::HAT_MASK_UP;
+ hat_value = InputFilter::HAT_MASK_UP;
break;
case 1:
- hat_value = InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_RIGHT;
+ hat_value = InputFilter::HAT_MASK_UP | InputFilter::HAT_MASK_RIGHT;
break;
case 2:
- hat_value = InputDefault::HAT_MASK_RIGHT;
+ hat_value = InputFilter::HAT_MASK_RIGHT;
break;
case 3:
- hat_value = InputDefault::HAT_MASK_DOWN | InputDefault::HAT_MASK_RIGHT;
+ hat_value = InputFilter::HAT_MASK_DOWN | InputFilter::HAT_MASK_RIGHT;
break;
case 4:
- hat_value = InputDefault::HAT_MASK_DOWN;
+ hat_value = InputFilter::HAT_MASK_DOWN;
break;
case 5:
- hat_value = InputDefault::HAT_MASK_DOWN | InputDefault::HAT_MASK_LEFT;
+ hat_value = InputFilter::HAT_MASK_DOWN | InputFilter::HAT_MASK_LEFT;
break;
case 6:
- hat_value = InputDefault::HAT_MASK_LEFT;
+ hat_value = InputFilter::HAT_MASK_LEFT;
break;
case 7:
- hat_value = InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_LEFT;
+ hat_value = InputFilter::HAT_MASK_UP | InputFilter::HAT_MASK_LEFT;
break;
default:
- hat_value = InputDefault::HAT_MASK_CENTER;
+ hat_value = InputFilter::HAT_MASK_CENTER;
break;
}
return hat_value;
@@ -438,8 +438,8 @@ void JoypadOSX::poll_joypads() const {
}
}
-static const InputDefault::JoyAxis axis_correct(int p_value, int p_min, int p_max) {
- InputDefault::JoyAxis jx;
+static const InputFilter::JoyAxis axis_correct(int p_value, int p_min, int p_max) {
+ InputFilter::JoyAxis jx;
if (p_min < 0) {
jx.min = -1;
if (p_value < 0) {
@@ -571,9 +571,9 @@ void JoypadOSX::config_hid_manager(CFArrayRef p_matching_array) const {
}
}
-JoypadOSX::JoypadOSX() {
+JoypadOSX::JoypadOSX(InputFilter *in) {
self = this;
- input = (InputDefault *)Input::get_singleton();
+ input = in;
int okay = 1;
const void *vals[] = {
diff --git a/platform/osx/joypad_osx.h b/platform/osx/joypad_osx.h
index 2c076b3680..62027c6a30 100644
--- a/platform/osx/joypad_osx.h
+++ b/platform/osx/joypad_osx.h
@@ -40,7 +40,7 @@
#include <ForceFeedback/ForceFeedbackConstants.h>
#include <IOKit/hid/IOHIDLib.h>
-#include "main/input_default.h"
+#include "core/input/input_filter.h"
struct rec_element {
IOHIDElementRef ref;
@@ -94,7 +94,7 @@ class JoypadOSX {
};
private:
- InputDefault *input;
+ InputFilter *input;
IOHIDManagerRef hid_manager;
Vector<joypad> device_list;
@@ -118,7 +118,7 @@ public:
void _device_added(IOReturn p_res, IOHIDDeviceRef p_device);
void _device_removed(IOReturn p_res, IOHIDDeviceRef p_device);
- JoypadOSX();
+ JoypadOSX(InputFilter *in);
~JoypadOSX();
};
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index e865c3078f..d2c67cff9f 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -31,57 +31,20 @@
#ifndef OS_OSX_H
#define OS_OSX_H
-#define BitMap _QDBitMap // Suppress deprecated QuickDraw definition.
-
-#include "core/os/input.h"
+#include "core/input/input_filter.h"
#include "crash_handler_osx.h"
#include "drivers/coreaudio/audio_driver_coreaudio.h"
#include "drivers/coremidi/midi_driver_coremidi.h"
#include "drivers/unix/os_unix.h"
#include "joypad_osx.h"
-#include "main/input_default.h"
#include "servers/audio_server.h"
-#include "servers/visual/rasterizer.h"
-#include "servers/visual/visual_server_wrap_mt.h"
-#include "servers/visual_server.h"
-
-#if defined(OPENGL_ENABLED)
-#include "context_gl_osx.h"
-#endif
-
-#if defined(VULKAN_ENABLED)
-#include "drivers/vulkan/rendering_device_vulkan.h"
-#include "platform/osx/vulkan_context_osx.h"
-#endif
-
-#include <AppKit/AppKit.h>
-#include <AppKit/NSCursor.h>
-#include <ApplicationServices/ApplicationServices.h>
-#include <CoreVideo/CoreVideo.h>
-
-#undef BitMap
-#undef CursorShape
class OS_OSX : public OS_Unix {
-public:
- struct KeyEvent {
- unsigned int osx_state;
- bool pressed;
- bool echo;
- bool raw;
- uint32_t keycode;
- uint32_t physical_keycode;
- uint32_t unicode;
- };
-
- Vector<KeyEvent> key_event_buffer;
- int key_event_pos;
+ virtual void delete_main_loop();
bool force_quit;
- VisualServer *visual_server;
- List<String> args;
- MainLoop *main_loop;
+ JoypadOSX *joypad_osx;
#ifdef COREAUDIO_ENABLED
AudioDriverCoreAudio audio_driver;
@@ -90,143 +53,27 @@ public:
MIDIDriverCoreMidi midi_driver;
#endif
- InputDefault *input;
- JoypadOSX *joypad_osx;
-
- /* objc */
-
- CGEventSourceRef eventSource;
-
- void process_events();
- void process_key_events();
-
- // pthread_key_t current;
- bool mouse_grab;
- Point2 mouse_pos;
-
- id delegate;
- id window_delegate;
- id window_object;
- id window_view;
- id autoreleasePool;
- id cursor;
-
-#if defined(OPENGL_ENABLED)
- ContextGL_OSX *context_gles2;
-#endif
-
-#if defined(VULKAN_ENABLED)
- VulkanContextOSX *context_vulkan;
- RenderingDeviceVulkan *rendering_device_vulkan;
-#endif
-
- bool layered_window;
-
- CursorShape cursor_shape;
- NSCursor *cursors[CURSOR_MAX];
- Map<CursorShape, Vector<Variant>> cursors_cache;
- MouseMode mouse_mode;
-
- String title;
- bool minimized;
- bool maximized;
- bool zoomed;
- bool resizable;
- bool window_focused;
-
- Size2 window_size;
- Rect2 restore_rect;
-
- String open_with_filename;
-
- Point2 im_position;
- bool im_active;
- String im_text;
- Point2 im_selection;
-
- Size2 min_size;
- Size2 max_size;
-
CrashHandler crash_handler;
- float _mouse_scale(float p_scale) {
- if (_display_scale() > 1.0)
- return p_scale;
- else
- return 1.0;
- }
-
- float _display_scale() const;
- float _display_scale(id screen) const;
-
- void _update_window();
-
- int video_driver_index;
- virtual int get_current_video_driver() const;
-
- struct GlobalMenuItem {
- String label;
- Variant signal;
- Variant meta;
-
- GlobalMenuItem() {
- //NOP
- }
-
- GlobalMenuItem(const String &p_label, const Variant &p_signal, const Variant &p_meta) {
- label = p_label;
- signal = p_signal;
- meta = p_meta;
- }
- };
-
- Map<String, Vector<GlobalMenuItem>> global_menus;
+ MainLoop *main_loop;
- void _update_global_menu();
+public:
+ String open_with_filename;
protected:
virtual void initialize_core();
- virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
+ virtual void initialize();
virtual void finalize();
+ virtual void initialize_joypads();
+
virtual void set_main_loop(MainLoop *p_main_loop);
- virtual void delete_main_loop();
public:
- static OS_OSX *singleton;
-
- void global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta);
- void global_menu_add_separator(const String &p_menu);
- void global_menu_remove_item(const String &p_menu, int p_idx);
- void global_menu_clear(const String &p_menu);
-
- void wm_minimized(bool p_minimized);
-
virtual String get_name() const;
- virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
-
virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);
- virtual void set_cursor_shape(CursorShape p_shape);
- virtual CursorShape get_cursor_shape() const;
- virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
-
- virtual void set_mouse_show(bool p_show);
- virtual void set_mouse_grab(bool p_grab);
- virtual bool is_mouse_grab_enabled() const;
- virtual void warp_mouse_position(const Point2 &p_to);
- virtual Point2 get_mouse_position() const;
- virtual int get_mouse_button_state() const;
- void update_real_mouse_position();
- virtual void set_window_title(const String &p_title);
-
- 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;
virtual String get_config_path() const;
@@ -237,95 +84,24 @@ public:
virtual String get_system_dir(SystemDir p_dir) const;
- virtual bool can_draw() const;
-
- virtual void set_clipboard(const String &p_text);
- virtual String get_clipboard() const;
-
- virtual void release_rendering_thread();
- virtual void make_rendering_thread();
- virtual void swap_buffers();
-
Error shell_open(String p_uri);
- void push_input(const Ref<InputEvent> &p_event);
String get_locale() const;
- 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 String get_executable_path() const;
- virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
-
- virtual void move_window_to_foreground();
-
- virtual int get_screen_count() const;
- virtual int get_current_screen() const;
- virtual void set_current_screen(int p_screen);
- virtual Point2 get_screen_position(int p_screen = -1) const;
- virtual Size2 get_screen_size(int p_screen = -1) const;
- virtual int get_screen_dpi(int p_screen = -1) const;
-
- virtual Point2 get_window_position() const;
- virtual void set_window_position(const Point2 &p_position);
- virtual Size2 get_max_window_size() const;
- virtual Size2 get_min_window_size() const;
- virtual void set_min_window_size(const Size2 p_size);
- virtual void set_max_window_size(const Size2 p_size);
- virtual void set_window_size(const Size2 p_size);
- virtual void set_window_fullscreen(bool p_enabled);
- virtual bool is_window_fullscreen() const;
- virtual void set_window_resizable(bool p_enabled);
- virtual bool is_window_resizable() const;
- virtual void set_window_minimized(bool p_enabled);
- virtual bool is_window_minimized() const;
- virtual void set_window_maximized(bool p_enabled);
- virtual bool is_window_maximized() const;
- virtual void set_window_always_on_top(bool p_enabled);
- virtual bool is_window_always_on_top() const;
- virtual bool is_window_focused() const;
- virtual void request_attention();
- virtual String get_joy_guid(int p_device) const;
-
- virtual void set_borderless_window(bool p_borderless);
- virtual bool get_borderless_window();
-
- 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 Point2 get_ime_selection() const;
- virtual String get_ime_text() const;
-
- virtual String get_unique_id() const;
+ virtual String get_unique_id() const; //++
virtual bool _check_internal_feature_support(const String &p_feature);
- virtual void _set_use_vsync(bool p_enable);
- //virtual bool is_vsync_enabled() const;
-
void run();
- void set_mouse_mode(MouseMode p_mode);
- MouseMode get_mouse_mode() const;
-
void disable_crash_handler();
bool is_disable_crash_handler() const;
virtual Error move_to_trash(const String &p_path);
- void force_process_input();
-
OS_OSX();
-
-private:
- Point2 get_native_screen_position(int p_screen) const;
- Point2 get_native_window_position() const;
- void set_native_window_position(const Point2 &p_position);
- Point2 get_screens_origin() const;
};
#endif
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 8ba8ca8a33..49cb056c9f 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -30,1668 +30,21 @@
#include "os_osx.h"
-#include "core/os/keyboard.h"
-#include "core/print_string.h"
#include "core/version_generated.gen.h"
-#include "dir_access_osx.h"
-
-#if defined(OPENGL_ENABLED)
-#include "drivers/gles2/rasterizer_gles2.h"
-#endif
-
-#if defined(VULKAN_ENABLED)
-#include "servers/visual/rasterizer_rd/rasterizer_rd.h"
-#endif
+#include "dir_access_osx.h"
+#include "display_server_osx.h"
#include "main/main.h"
-#include "servers/visual/visual_server_raster.h"
-#include "servers/visual/visual_server_wrap_mt.h"
-
-#include <mach-o/dyld.h>
-
-#include <Carbon/Carbon.h>
-#import <Cocoa/Cocoa.h>
-#include <IOKit/IOCFPlugIn.h>
-#include <IOKit/IOKitLib.h>
-#include <IOKit/hid/IOHIDKeys.h>
-#include <IOKit/hid/IOHIDLib.h>
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
-#include <os/log.h>
-#endif
-
-#import <QuartzCore/CAMetalLayer.h>
-#include <vulkan/vulkan_metal.h>
#include <dlfcn.h>
-#include <fcntl.h>
#include <libproc.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200
-#define NSEventMaskAny NSAnyEventMask
-#define NSEventTypeKeyDown NSKeyDown
-#define NSEventTypeKeyUp NSKeyUp
-#define NSEventModifierFlagShift NSShiftKeyMask
-#define NSEventModifierFlagCommand NSCommandKeyMask
-#define NSEventModifierFlagControl NSControlKeyMask
-#define NSEventModifierFlagOption NSAlternateKeyMask
-#define NSWindowStyleMaskTitled NSTitledWindowMask
-#define NSWindowStyleMaskResizable NSResizableWindowMask
-#define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask
-#define NSWindowStyleMaskClosable NSClosableWindowMask
-#define NSWindowStyleMaskBorderless NSBorderlessWindowMask
-#endif
-
-#ifndef NSAppKitVersionNumber10_12
-#define NSAppKitVersionNumber10_12 1504
-#endif
-#ifndef NSAppKitVersionNumber10_14
-#define NSAppKitVersionNumber10_14 1671
-#endif
-
-static void get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> state) {
-
- state->set_shift((p_osx_state & NSEventModifierFlagShift));
- state->set_control((p_osx_state & NSEventModifierFlagControl));
- state->set_alt((p_osx_state & NSEventModifierFlagOption));
- state->set_metakey((p_osx_state & NSEventModifierFlagCommand));
-}
-
-static void push_to_key_event_buffer(const OS_OSX::KeyEvent &p_event) {
-
- Vector<OS_OSX::KeyEvent> &buffer = OS_OSX::singleton->key_event_buffer;
- if (OS_OSX::singleton->key_event_pos >= buffer.size()) {
- buffer.resize(1 + OS_OSX::singleton->key_event_pos);
- }
- buffer.write[OS_OSX::singleton->key_event_pos++] = p_event;
-}
-
-static int mouse_x = 0;
-static int mouse_y = 0;
-static int button_mask = 0;
-static bool mouse_down_control = false;
-
-static Vector2 get_mouse_pos(NSPoint locationInWindow, CGFloat backingScaleFactor) {
-
- const NSRect contentRect = [OS_OSX::singleton->window_view frame];
- const NSPoint p = locationInWindow;
- const float s = OS_OSX::singleton->_mouse_scale(backingScaleFactor);
- mouse_x = p.x * s;
- mouse_y = (contentRect.size.height - p.y) * s;
- return Vector2(mouse_x, mouse_y);
-}
-
-static NSCursor *cursorFromSelector(SEL selector, SEL fallback = nil) {
- if ([NSCursor respondsToSelector:selector]) {
- id object = [NSCursor performSelector:selector];
- if ([object isKindOfClass:[NSCursor class]]) {
- return object;
- }
- }
- if (fallback) {
- // Fallback should be a reasonable default, no need to check.
- return [NSCursor performSelector:fallback];
- }
- return [NSCursor arrowCursor];
-}
-
-@interface GodotApplication : NSApplication
-@end
-
-@implementation GodotApplication
-
-- (void)sendEvent:(NSEvent *)event {
-
- // special case handling of command-period, which is traditionally a special
- // shortcut in macOS and doesn't arrive at our regular keyDown handler.
- if ([event type] == NSEventTypeKeyDown) {
- if (([event modifierFlags] & NSEventModifierFlagCommand) && [event keyCode] == 0x2f) {
-
- Ref<InputEventKey> k;
- k.instance();
-
- get_key_modifier_state([event modifierFlags], k);
- k->set_pressed(true);
- k->set_keycode(KEY_PERIOD);
- k->set_physical_keycode(KEY_PERIOD);
- k->set_echo([event isARepeat]);
-
- OS_OSX::singleton->push_input(k);
- }
- }
-
- // From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost
- // This works around an AppKit bug, where key up events while holding
- // down the command key don't get sent to the key window.
- if ([event type] == NSEventTypeKeyUp && ([event modifierFlags] & NSEventModifierFlagCommand))
- [[self keyWindow] sendEvent:event];
- else
- [super sendEvent:event];
-}
-
-@end
-
-@interface GodotApplicationDelegate : NSObject
-- (void)forceUnbundledWindowActivationHackStep1;
-- (void)forceUnbundledWindowActivationHackStep2;
-- (void)forceUnbundledWindowActivationHackStep3;
-@end
-
-@implementation GodotApplicationDelegate
-
-- (void)forceUnbundledWindowActivationHackStep1 {
- // Step1: Switch focus to macOS Dock.
- // Required to perform step 2, TransformProcessType will fail if app is already the in focus.
- for (NSRunningApplication *app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) {
- [app activateWithOptions:NSApplicationActivateIgnoringOtherApps];
- break;
- }
- [self performSelector:@selector(forceUnbundledWindowActivationHackStep2) withObject:nil afterDelay:0.02];
-}
-
-- (void)forceUnbundledWindowActivationHackStep2 {
- // Step 2: Register app as foreground process.
- ProcessSerialNumber psn = { 0, kCurrentProcess };
- (void)TransformProcessType(&psn, kProcessTransformToForegroundApplication);
-
- [self performSelector:@selector(forceUnbundledWindowActivationHackStep3) withObject:nil afterDelay:0.02];
-}
-
-- (void)forceUnbundledWindowActivationHackStep3 {
- // Step 3: Switch focus back to app window.
- [[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
-}
-
-- (void)applicationDidFinishLaunching:(NSNotification *)notice {
- NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
- if (nsappname == nil) {
- // If executable is not a bundled, macOS WindowServer won't register and activate app window correctly (menu and title bar are grayed out and input ignored).
- [self performSelector:@selector(forceUnbundledWindowActivationHackStep1) withObject:nil afterDelay:0.02];
- }
-}
-
-- (void)globalMenuCallback:(id)sender {
-
- if (![sender representedObject])
- return;
-
- OS_OSX::GlobalMenuItem *item = (OS_OSX::GlobalMenuItem *)[[sender representedObject] pointerValue];
-
- if (!item)
- return;
-
- OS_OSX::singleton->main_loop->global_menu_action(item->signal, item->meta);
-}
-
-- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
-
- NSMenu *menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
-
- Vector<OS_OSX::GlobalMenuItem> &E = OS_OSX::singleton->global_menus["_dock"];
- for (int i = 0; i < E.size(); i++) {
- if (E[i].label == String()) {
- [menu addItem:[NSMenuItem separatorItem]];
- } else {
- NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:E[i].label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
- [menu_item setRepresentedObject:[NSValue valueWithPointer:&(E[i])]];
- }
- }
-
- return menu;
-}
-
-- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename {
- // Note: may be called called before main loop init!
- char *utfs = strdup([filename UTF8String]);
- OS_OSX::singleton->open_with_filename.parse_utf8(utfs);
- free(utfs);
-
-#ifdef TOOLS_ENABLED
- // Open new instance
- if (OS_OSX::singleton->get_main_loop()) {
- List<String> args;
- args.push_back(OS_OSX::singleton->open_with_filename);
- String exec = OS::get_singleton()->get_executable_path();
-
- OS::ProcessID pid = 0;
- OS::get_singleton()->execute(exec, args, false, &pid);
- }
-#endif
- return YES;
-}
-
-- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
-
- return NSTerminateCancel;
-}
-
-- (void)showAbout:(id)sender {
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_ABOUT);
-}
-
-@end
-
-@interface GodotWindowDelegate : NSObject {
- //_Godotwindow* window;
-}
-
-- (void)windowWillClose:(NSNotification *)notification;
-
-@end
-
-@implementation GodotWindowDelegate
-
-- (BOOL)windowShouldClose:(id)sender {
- //_GodotInputWindowCloseRequest(window);
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
-
- return NO;
-}
-
-- (void)windowWillClose:(NSNotification *)notification {
-#if defined(VULKAN_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_VULKAN) {
-
- if (OS_OSX::singleton->rendering_device_vulkan) {
- OS_OSX::singleton->rendering_device_vulkan->finalize();
- memdelete(OS_OSX::singleton->rendering_device_vulkan);
- OS_OSX::singleton->rendering_device_vulkan = NULL;
- }
-
- if (OS_OSX::singleton->context_vulkan) {
- memdelete(OS_OSX::singleton->context_vulkan);
- OS_OSX::singleton->context_vulkan = NULL;
- }
- }
-#endif
-}
-
-- (void)windowDidEnterFullScreen:(NSNotification *)notification {
- OS_OSX::singleton->zoomed = true;
-
- [OS_OSX::singleton->window_object setContentMinSize:NSMakeSize(0, 0)];
- [OS_OSX::singleton->window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
-}
-
-- (void)windowDidExitFullScreen:(NSNotification *)notification {
- OS_OSX::singleton->zoomed = false;
-
- if (OS_OSX::singleton->min_size != Size2()) {
- Size2 size = OS_OSX::singleton->min_size / OS_OSX::singleton->_display_scale();
- [OS_OSX::singleton->window_object setContentMinSize:NSMakeSize(size.x, size.y)];
- }
- if (OS_OSX::singleton->max_size != Size2()) {
- Size2 size = OS_OSX::singleton->max_size / OS_OSX::singleton->_display_scale();
- [OS_OSX::singleton->window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
- }
-
- if (!OS_OSX::singleton->resizable)
- [OS_OSX::singleton->window_object setStyleMask:[OS_OSX::singleton->window_object styleMask] & ~NSWindowStyleMaskResizable];
-}
-
-- (void)windowDidChangeBackingProperties:(NSNotification *)notification {
- if (!OS_OSX::singleton)
- return;
-
- NSWindow *window = (NSWindow *)[notification object];
- CGFloat newBackingScaleFactor = [window backingScaleFactor];
- CGFloat oldBackingScaleFactor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue];
-
-#if defined(OPENGL_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_GLES2) {
- if (OS_OSX::singleton->is_hidpi_allowed()) {
- [OS_OSX::singleton->window_view setWantsBestResolutionOpenGLSurface:YES];
- } else {
- [OS_OSX::singleton->window_view setWantsBestResolutionOpenGLSurface:NO];
- }
- }
-#endif
-
- if (newBackingScaleFactor != oldBackingScaleFactor) {
- //Set new display scale and window size
- float newDisplayScale = OS_OSX::singleton->is_hidpi_allowed() ? newBackingScaleFactor : 1.0;
-
- const NSRect contentRect = [OS_OSX::singleton->window_view frame];
- const NSRect fbRect = contentRect;
-
- OS_OSX::singleton->window_size.width = fbRect.size.width * newDisplayScale;
- OS_OSX::singleton->window_size.height = fbRect.size.height * newDisplayScale;
-
-#if defined(VULKAN_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_VULKAN) {
- CALayer *layer = [OS_OSX::singleton->window_view layer];
- layer.contentsScale = OS_OSX::singleton->_display_scale();
- }
-#endif
- //Update context
- if (OS_OSX::singleton->main_loop) {
- //Force window resize event
- [self windowDidResize:notification];
- }
- }
-}
-
-- (void)windowDidResize:(NSNotification *)notification {
-
-#if defined(OPENGL_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_GLES2) {
- OS_OSX::singleton->context_gles2->update();
- }
-#endif
- const NSRect contentRect = [OS_OSX::singleton->window_view frame];
- const NSRect fbRect = contentRect;
-
- float displayScale = OS_OSX::singleton->_display_scale();
- OS_OSX::singleton->window_size.width = fbRect.size.width * displayScale;
- OS_OSX::singleton->window_size.height = fbRect.size.height * displayScale;
-
-#if defined(VULKAN_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_VULKAN) {
- CALayer *layer = [OS_OSX::singleton->window_view layer];
- layer.contentsScale = OS_OSX::singleton->_display_scale();
- OS_OSX::singleton->context_vulkan->window_resize(0, OS_OSX::singleton->window_size.width, OS_OSX::singleton->window_size.height);
- }
-#endif
-
- if (OS_OSX::singleton->main_loop) {
- Main::force_redraw();
- //Event retrieval blocks until resize is over. Call Main::iteration() directly.
- if (!Main::is_iterating()) { //avoid cyclic loop
- Main::iteration();
- }
- }
-}
-
-- (void)windowDidMove:(NSNotification *)notification {
-
- if (OS_OSX::singleton->get_main_loop()) {
- OS_OSX::singleton->input->release_pressed_events();
- }
-}
-
-- (void)windowDidBecomeKey:(NSNotification *)notification {
- if (OS_OSX::singleton->get_main_loop()) {
- get_mouse_pos(
- [OS_OSX::singleton->window_object mouseLocationOutsideOfEventStream],
- [OS_OSX::singleton->window_view backingScaleFactor]);
- OS_OSX::singleton->input->set_mouse_position(Point2(mouse_x, mouse_y));
-
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
- }
-
- OS_OSX::singleton->window_focused = true;
-}
-
-- (void)windowDidResignKey:(NSNotification *)notification {
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
-
- OS_OSX::singleton->window_focused = false;
-}
-
-- (void)windowDidMiniaturize:(NSNotification *)notification {
- OS_OSX::singleton->wm_minimized(true);
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
-
- OS_OSX::singleton->window_focused = false;
-};
-
-- (void)windowDidDeminiaturize:(NSNotification *)notification {
- OS_OSX::singleton->wm_minimized(false);
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
-
- OS_OSX::singleton->window_focused = true;
-};
-
-@end
-
-@interface GodotContentView : NSView <NSTextInputClient> {
- NSTrackingArea *trackingArea;
- NSMutableAttributedString *markedText;
- bool imeInputEventInProgress;
-}
-- (void)cancelComposition;
-
-- (CALayer *)makeBackingLayer;
-
-- (BOOL)wantsUpdateLayer;
-- (void)updateLayer;
-
-@end
-
-@implementation GodotContentView
-
-+ (void)initialize {
- if (self == [GodotContentView class]) {
- // nothing left to do here at the moment..
- }
-}
-
-- (CALayer *)makeBackingLayer {
-#if defined(VULKAN_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_VULKAN) {
- CALayer *layer = [[CAMetalLayer class] layer];
- layer.contentsScale = OS_OSX::singleton->_display_scale();
- return layer;
- }
-#endif
- return [super makeBackingLayer];
-}
-
-- (void)updateLayer {
-#if defined(VULKAN_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_VULKAN) {
- [super updateLayer];
- }
-#endif
-#if defined(OPENGL_ENABLED)
- if (OS_OSX::singleton->video_driver_index == OS::VIDEO_DRIVER_GLES2) {
- OS_OSX::singleton->context_gles2->update();
- }
-#endif
-}
-
-- (BOOL)wantsUpdateLayer {
- return YES;
-}
-
-- (id)init {
- self = [super init];
- trackingArea = nil;
- imeInputEventInProgress = false;
- [self updateTrackingAreas];
- [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
- markedText = [[NSMutableAttributedString alloc] init];
- return self;
-}
-
-- (void)dealloc {
- [trackingArea release];
- [markedText release];
- [super dealloc];
-}
-
-static const NSRange kEmptyRange = { NSNotFound, 0 };
-
-- (BOOL)hasMarkedText {
- return (markedText.length > 0);
-}
-
-- (NSRange)markedRange {
- return NSMakeRange(0, markedText.length);
-}
-
-- (NSRange)selectedRange {
- return kEmptyRange;
-}
-
-- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange {
- if ([aString isKindOfClass:[NSAttributedString class]]) {
- [markedText initWithAttributedString:aString];
- } else {
- [markedText initWithString:aString];
- }
- if (markedText.length == 0) {
- [self unmarkText];
- return;
- }
- if (OS_OSX::singleton->im_active) {
- imeInputEventInProgress = true;
- OS_OSX::singleton->im_text.parse_utf8([[markedText mutableString] UTF8String]);
- OS_OSX::singleton->im_selection = Point2(selectedRange.location, selectedRange.length);
-
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
- }
-}
-
-- (void)doCommandBySelector:(SEL)aSelector {
- if ([self respondsToSelector:aSelector])
- [self performSelector:aSelector];
-}
-
-- (void)unmarkText {
- imeInputEventInProgress = false;
- [[markedText mutableString] setString:@""];
- if (OS_OSX::singleton->im_active) {
- OS_OSX::singleton->im_text = String();
- OS_OSX::singleton->im_selection = Point2();
-
- if (OS_OSX::singleton->get_main_loop())
- OS_OSX::singleton->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
- }
-}
-
-- (NSArray *)validAttributesForMarkedText {
- return [NSArray array];
-}
-
-- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
- return nil;
-}
-
-- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
- return 0;
-}
-
-- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange {
- const NSRect contentRect = [OS_OSX::singleton->window_view frame];
- float displayScale = OS_OSX::singleton->_display_scale();
- NSRect pointInWindowRect = NSMakeRect(OS_OSX::singleton->im_position.x / displayScale, contentRect.size.height - (OS_OSX::singleton->im_position.y / displayScale) - 1, 0, 0);
- NSPoint pointOnScreen = [[OS_OSX::singleton->window_view window] convertRectToScreen:pointInWindowRect].origin;
-
- return NSMakeRect(pointOnScreen.x, pointOnScreen.y, 0, 0);
-}
-
-- (void)cancelComposition {
- [self unmarkText];
- NSTextInputContext *currentInputContext = [NSTextInputContext currentInputContext];
- [currentInputContext discardMarkedText];
-}
-
-- (void)insertText:(id)aString {
- [self insertText:aString replacementRange:NSMakeRange(0, 0)];
-}
-
-- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange {
- NSEvent *event = [NSApp currentEvent];
-
- NSString *characters;
- if ([aString isKindOfClass:[NSAttributedString class]]) {
- characters = [aString string];
- } else {
- characters = (NSString *)aString;
- }
-
- NSUInteger i, length = [characters length];
-
- NSCharacterSet *ctrlChars = [NSCharacterSet controlCharacterSet];
- NSCharacterSet *wsnlChars = [NSCharacterSet whitespaceAndNewlineCharacterSet];
- if ([characters rangeOfCharacterFromSet:ctrlChars].length && [characters rangeOfCharacterFromSet:wsnlChars].length == 0) {
- NSTextInputContext *currentInputContext = [NSTextInputContext currentInputContext];
- [currentInputContext discardMarkedText];
- [self cancelComposition];
- return;
- }
-
- for (i = 0; i < length; i++) {
- const unichar codepoint = [characters characterAtIndex:i];
- if ((codepoint & 0xFF00) == 0xF700)
- continue;
-
- OS_OSX::KeyEvent ke;
-
- ke.osx_state = [event modifierFlags];
- ke.pressed = true;
- ke.echo = false;
- ke.raw = false; // IME input event
- ke.keycode = 0;
- ke.physical_keycode = 0;
- ke.unicode = codepoint;
-
- push_to_key_event_buffer(ke);
- }
- [self cancelComposition];
-}
-
-- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
- return NSDragOperationCopy;
-}
-
-- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
- return NSDragOperationCopy;
-}
-
-- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
-
- NSPasteboard *pboard = [sender draggingPasteboard];
- NSArray *filenames = [pboard propertyListForType:NSFilenamesPboardType];
-
- Vector<String> files;
- for (NSUInteger i = 0; i < filenames.count; i++) {
- NSString *ns = [filenames objectAtIndex:i];
- char *utfs = strdup([ns UTF8String]);
- String ret;
- ret.parse_utf8(utfs);
- free(utfs);
- files.push_back(ret);
- }
-
- if (files.size()) {
- OS_OSX::singleton->main_loop->drop_files(files, 0);
- OS_OSX::singleton->move_window_to_foreground();
- }
-
- return NO;
-}
-
-- (BOOL)isOpaque {
- return YES;
-}
-
-- (BOOL)canBecomeKeyView {
- return YES;
-}
-
-- (BOOL)acceptsFirstResponder {
- return YES;
-}
-
-- (void)cursorUpdate:(NSEvent *)event {
- OS::CursorShape p_shape = OS_OSX::singleton->cursor_shape;
- OS_OSX::singleton->cursor_shape = OS::CURSOR_MAX;
- OS_OSX::singleton->set_cursor_shape(p_shape);
-}
-
-static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) {
- if (pressed) {
- button_mask |= mask;
- } else {
- button_mask &= ~mask;
- }
-
- Ref<InputEventMouseButton> mb;
- mb.instance();
- const CGFloat backingScaleFactor = [[event window] backingScaleFactor];
- const Vector2 pos = get_mouse_pos([event locationInWindow], backingScaleFactor);
- get_key_modifier_state([event modifierFlags], mb);
- mb->set_button_index(index);
- mb->set_pressed(pressed);
- mb->set_position(pos);
- mb->set_global_position(pos);
- mb->set_button_mask(button_mask);
- if (index == BUTTON_LEFT && pressed) {
- mb->set_doubleclick([event clickCount] == 2);
- }
- OS_OSX::singleton->push_input(mb);
-}
-
-- (void)mouseDown:(NSEvent *)event {
- if (([event modifierFlags] & NSEventModifierFlagControl)) {
- mouse_down_control = true;
- _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true);
- } else {
- mouse_down_control = false;
- _mouseDownEvent(event, BUTTON_LEFT, BUTTON_MASK_LEFT, true);
- }
-}
-
-- (void)mouseDragged:(NSEvent *)event {
- [self mouseMoved:event];
-}
-
-- (void)mouseUp:(NSEvent *)event {
- if (mouse_down_control) {
- _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false);
- } else {
- _mouseDownEvent(event, BUTTON_LEFT, BUTTON_MASK_LEFT, false);
- }
-}
-
-- (void)mouseMoved:(NSEvent *)event {
-
- Ref<InputEventMouseMotion> mm;
- mm.instance();
-
- mm->set_button_mask(button_mask);
- const CGFloat backingScaleFactor = [[event window] backingScaleFactor];
- const Vector2 pos = get_mouse_pos([event locationInWindow], backingScaleFactor);
- mm->set_position(pos);
- mm->set_pressure([event pressure]);
- if ([event subtype] == NSEventSubtypeTabletPoint) {
- const NSPoint p = [event tilt];
- mm->set_tilt(Vector2(p.x, p.y));
- }
- mm->set_global_position(pos);
- mm->set_speed(OS_OSX::singleton->input->get_last_mouse_speed());
- Vector2 relativeMotion = Vector2();
- relativeMotion.x = [event deltaX] * OS_OSX::singleton -> _mouse_scale(backingScaleFactor);
- relativeMotion.y = [event deltaY] * OS_OSX::singleton -> _mouse_scale(backingScaleFactor);
- mm->set_relative(relativeMotion);
- get_key_modifier_state([event modifierFlags], mm);
-
- OS_OSX::singleton->input->set_mouse_position(Point2(mouse_x, mouse_y));
- OS_OSX::singleton->push_input(mm);
-}
-
-- (void)rightMouseDown:(NSEvent *)event {
- _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, true);
-}
-
-- (void)rightMouseDragged:(NSEvent *)event {
- [self mouseMoved:event];
-}
-
-- (void)rightMouseUp:(NSEvent *)event {
- _mouseDownEvent(event, BUTTON_RIGHT, BUTTON_MASK_RIGHT, false);
-}
-
-- (void)otherMouseDown:(NSEvent *)event {
-
- 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);
-
- } else if ((int)[event buttonNumber] == 4) {
- _mouseDownEvent(event, BUTTON_XBUTTON2, BUTTON_MASK_XBUTTON2, true);
-
- } else {
- return;
- }
-}
-
-- (void)otherMouseDragged:(NSEvent *)event {
- [self mouseMoved:event];
-}
-
-- (void)otherMouseUp:(NSEvent *)event {
-
- if ((int)[event buttonNumber] == 2) {
- _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 {
- if (!OS_OSX::singleton)
- return;
-
- if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED)
- OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
-}
-
-- (void)mouseEntered:(NSEvent *)event {
- if (!OS_OSX::singleton)
- return;
- if (OS_OSX::singleton->main_loop && OS_OSX::singleton->mouse_mode != OS::MOUSE_MODE_CAPTURED)
- OS_OSX::singleton->main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
-
- OS::CursorShape p_shape = OS_OSX::singleton->cursor_shape;
- OS_OSX::singleton->cursor_shape = OS::CURSOR_MAX;
- OS_OSX::singleton->set_cursor_shape(p_shape);
-}
-
-- (void)magnifyWithEvent:(NSEvent *)event {
- Ref<InputEventMagnifyGesture> ev;
- ev.instance();
- get_key_modifier_state([event modifierFlags], ev);
- ev->set_position(get_mouse_pos([event locationInWindow], [[event window] backingScaleFactor]));
- ev->set_factor([event magnification] + 1.0);
- OS_OSX::singleton->push_input(ev);
-}
-
-- (void)viewDidChangeBackingProperties {
- // nothing left to do here
-}
-
-- (void)updateTrackingAreas {
- if (trackingArea != nil) {
- [self removeTrackingArea:trackingArea];
- [trackingArea release];
- }
-
- NSTrackingAreaOptions options =
- NSTrackingMouseEnteredAndExited |
- NSTrackingActiveInKeyWindow |
- NSTrackingCursorUpdate |
- NSTrackingInVisibleRect;
-
- trackingArea = [[NSTrackingArea alloc]
- initWithRect:[self bounds]
- options:options
- owner:self
- userInfo:nil];
-
- [self addTrackingArea:trackingArea];
- [super updateTrackingAreas];
-}
-
-static bool isNumpadKey(unsigned int key) {
-
- static const unsigned int table[] = {
- 0x41, /* kVK_ANSI_KeypadDecimal */
- 0x43, /* kVK_ANSI_KeypadMultiply */
- 0x45, /* kVK_ANSI_KeypadPlus */
- 0x47, /* kVK_ANSI_KeypadClear */
- 0x4b, /* kVK_ANSI_KeypadDivide */
- 0x4c, /* kVK_ANSI_KeypadEnter */
- 0x4e, /* kVK_ANSI_KeypadMinus */
- 0x51, /* kVK_ANSI_KeypadEquals */
- 0x52, /* kVK_ANSI_Keypad0 */
- 0x53, /* kVK_ANSI_Keypad1 */
- 0x54, /* kVK_ANSI_Keypad2 */
- 0x55, /* kVK_ANSI_Keypad3 */
- 0x56, /* kVK_ANSI_Keypad4 */
- 0x57, /* kVK_ANSI_Keypad5 */
- 0x58, /* kVK_ANSI_Keypad6 */
- 0x59, /* kVK_ANSI_Keypad7 */
- 0x5b, /* kVK_ANSI_Keypad8 */
- 0x5c, /* kVK_ANSI_Keypad9 */
- 0x5f, /* kVK_JIS_KeypadComma */
- 0x00
- };
- for (int i = 0; table[i] != 0; i++) {
- if (key == table[i])
- return true;
- }
- return false;
-}
-
-// Translates a OS X keycode to a Godot keycode
-//
-static int translateKey(unsigned int key) {
-
- // Keyboard symbol translation table
- static const unsigned int table[128] = {
- /* 00 */ KEY_A,
- /* 01 */ KEY_S,
- /* 02 */ KEY_D,
- /* 03 */ KEY_F,
- /* 04 */ KEY_H,
- /* 05 */ KEY_G,
- /* 06 */ KEY_Z,
- /* 07 */ KEY_X,
- /* 08 */ KEY_C,
- /* 09 */ KEY_V,
- /* 0a */ KEY_SECTION, /* ISO Section */
- /* 0b */ KEY_B,
- /* 0c */ KEY_Q,
- /* 0d */ KEY_W,
- /* 0e */ KEY_E,
- /* 0f */ KEY_R,
- /* 10 */ KEY_Y,
- /* 11 */ KEY_T,
- /* 12 */ KEY_1,
- /* 13 */ KEY_2,
- /* 14 */ KEY_3,
- /* 15 */ KEY_4,
- /* 16 */ KEY_6,
- /* 17 */ KEY_5,
- /* 18 */ KEY_EQUAL,
- /* 19 */ KEY_9,
- /* 1a */ KEY_7,
- /* 1b */ KEY_MINUS,
- /* 1c */ KEY_8,
- /* 1d */ KEY_0,
- /* 1e */ KEY_BRACERIGHT,
- /* 1f */ KEY_O,
- /* 20 */ KEY_U,
- /* 21 */ KEY_BRACELEFT,
- /* 22 */ KEY_I,
- /* 23 */ KEY_P,
- /* 24 */ KEY_ENTER,
- /* 25 */ KEY_L,
- /* 26 */ KEY_J,
- /* 27 */ KEY_APOSTROPHE,
- /* 28 */ KEY_K,
- /* 29 */ KEY_SEMICOLON,
- /* 2a */ KEY_BACKSLASH,
- /* 2b */ KEY_COMMA,
- /* 2c */ KEY_SLASH,
- /* 2d */ KEY_N,
- /* 2e */ KEY_M,
- /* 2f */ KEY_PERIOD,
- /* 30 */ KEY_TAB,
- /* 31 */ KEY_SPACE,
- /* 32 */ KEY_QUOTELEFT,
- /* 33 */ KEY_BACKSPACE,
- /* 34 */ KEY_UNKNOWN,
- /* 35 */ KEY_ESCAPE,
- /* 36 */ KEY_META,
- /* 37 */ KEY_META,
- /* 38 */ KEY_SHIFT,
- /* 39 */ KEY_CAPSLOCK,
- /* 3a */ KEY_ALT,
- /* 3b */ KEY_CONTROL,
- /* 3c */ KEY_SHIFT,
- /* 3d */ KEY_ALT,
- /* 3e */ KEY_CONTROL,
- /* 3f */ KEY_UNKNOWN, /* Function */
- /* 40 */ KEY_UNKNOWN, /* F17 */
- /* 41 */ KEY_KP_PERIOD,
- /* 42 */ KEY_UNKNOWN,
- /* 43 */ KEY_KP_MULTIPLY,
- /* 44 */ KEY_UNKNOWN,
- /* 45 */ KEY_KP_ADD,
- /* 46 */ KEY_UNKNOWN,
- /* 47 */ KEY_NUMLOCK, /* Really KeypadClear... */
- /* 48 */ KEY_VOLUMEUP, /* VolumeUp */
- /* 49 */ KEY_VOLUMEDOWN, /* VolumeDown */
- /* 4a */ KEY_VOLUMEMUTE, /* Mute */
- /* 4b */ KEY_KP_DIVIDE,
- /* 4c */ KEY_KP_ENTER,
- /* 4d */ KEY_UNKNOWN,
- /* 4e */ KEY_KP_SUBTRACT,
- /* 4f */ KEY_UNKNOWN, /* F18 */
- /* 50 */ KEY_UNKNOWN, /* F19 */
- /* 51 */ KEY_EQUAL, /* KeypadEqual */
- /* 52 */ KEY_KP_0,
- /* 53 */ KEY_KP_1,
- /* 54 */ KEY_KP_2,
- /* 55 */ KEY_KP_3,
- /* 56 */ KEY_KP_4,
- /* 57 */ KEY_KP_5,
- /* 58 */ KEY_KP_6,
- /* 59 */ KEY_KP_7,
- /* 5a */ KEY_UNKNOWN, /* F20 */
- /* 5b */ KEY_KP_8,
- /* 5c */ KEY_KP_9,
- /* 5d */ KEY_YEN, /* JIS Yen */
- /* 5e */ KEY_UNDERSCORE, /* JIS Underscore */
- /* 5f */ KEY_COMMA, /* JIS KeypadComma */
- /* 60 */ KEY_F5,
- /* 61 */ KEY_F6,
- /* 62 */ KEY_F7,
- /* 63 */ KEY_F3,
- /* 64 */ KEY_F8,
- /* 65 */ KEY_F9,
- /* 66 */ KEY_UNKNOWN, /* JIS Eisu */
- /* 67 */ KEY_F11,
- /* 68 */ KEY_UNKNOWN, /* JIS Kana */
- /* 69 */ KEY_F13,
- /* 6a */ KEY_F16,
- /* 6b */ KEY_F14,
- /* 6c */ KEY_UNKNOWN,
- /* 6d */ KEY_F10,
- /* 6e */ KEY_MENU,
- /* 6f */ KEY_F12,
- /* 70 */ KEY_UNKNOWN,
- /* 71 */ KEY_F15,
- /* 72 */ KEY_INSERT, /* Really Help... */
- /* 73 */ KEY_HOME,
- /* 74 */ KEY_PAGEUP,
- /* 75 */ KEY_DELETE,
- /* 76 */ KEY_F4,
- /* 77 */ KEY_END,
- /* 78 */ KEY_F2,
- /* 79 */ KEY_PAGEDOWN,
- /* 7a */ KEY_F1,
- /* 7b */ KEY_LEFT,
- /* 7c */ KEY_RIGHT,
- /* 7d */ KEY_DOWN,
- /* 7e */ KEY_UP,
- /* 7f */ KEY_UNKNOWN,
- };
-
- if (key >= 128)
- return KEY_UNKNOWN;
-
- return table[key];
-}
-
-struct _KeyCodeMap {
- UniChar kchar;
- int kcode;
-};
-
-static const _KeyCodeMap _keycodes[55] = {
- { '`', KEY_QUOTELEFT },
- { '~', KEY_ASCIITILDE },
- { '0', KEY_0 },
- { '1', KEY_1 },
- { '2', KEY_2 },
- { '3', KEY_3 },
- { '4', KEY_4 },
- { '5', KEY_5 },
- { '6', KEY_6 },
- { '7', KEY_7 },
- { '8', KEY_8 },
- { '9', KEY_9 },
- { '-', KEY_MINUS },
- { '_', KEY_UNDERSCORE },
- { '=', KEY_EQUAL },
- { '+', KEY_PLUS },
- { 'q', KEY_Q },
- { 'w', KEY_W },
- { 'e', KEY_E },
- { 'r', KEY_R },
- { 't', KEY_T },
- { 'y', KEY_Y },
- { 'u', KEY_U },
- { 'i', KEY_I },
- { 'o', KEY_O },
- { 'p', KEY_P },
- { '[', KEY_BRACELEFT },
- { ']', KEY_BRACERIGHT },
- { '{', KEY_BRACELEFT },
- { '}', KEY_BRACERIGHT },
- { 'a', KEY_A },
- { 's', KEY_S },
- { 'd', KEY_D },
- { 'f', KEY_F },
- { 'g', KEY_G },
- { 'h', KEY_H },
- { 'j', KEY_J },
- { 'k', KEY_K },
- { 'l', KEY_L },
- { ';', KEY_SEMICOLON },
- { ':', KEY_COLON },
- { '\'', KEY_APOSTROPHE },
- { '\"', KEY_QUOTEDBL },
- { '\\', KEY_BACKSLASH },
- { '#', KEY_NUMBERSIGN },
- { 'z', KEY_Z },
- { 'x', KEY_X },
- { 'c', KEY_C },
- { 'v', KEY_V },
- { 'b', KEY_B },
- { 'n', KEY_N },
- { 'm', KEY_M },
- { ',', KEY_COMMA },
- { '.', KEY_PERIOD },
- { '/', KEY_SLASH }
-};
-
-static int remapKey(unsigned int key, unsigned int state) {
-
- if (isNumpadKey(key))
- return translateKey(key);
-
- TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
- if (!currentKeyboard)
- return translateKey(key);
-
- CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
- if (!layoutData)
- return translateKey(key);
-
- const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
-
- UInt32 keysDown = 0;
- UniChar chars[4];
- UniCharCount realLength;
-
- OSStatus err = UCKeyTranslate(keyboardLayout,
- key,
- kUCKeyActionDisplay,
- (state >> 8) & 0xFF,
- LMGetKbdType(),
- kUCKeyTranslateNoDeadKeysBit,
- &keysDown,
- sizeof(chars) / sizeof(chars[0]),
- &realLength,
- chars);
-
- if (err != noErr) {
- return translateKey(key);
- }
-
- for (unsigned int i = 0; i < 55; i++) {
- if (_keycodes[i].kchar == chars[0]) {
- return _keycodes[i].kcode;
- }
- }
- return translateKey(key);
-}
-
-- (void)keyDown:(NSEvent *)event {
-
- // Ignore all input if IME input is in progress
- if (!imeInputEventInProgress) {
- NSString *characters = [event characters];
- NSUInteger length = [characters length];
-
- 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;
-
- ke.osx_state = [event modifierFlags];
- ke.pressed = true;
- ke.echo = [event isARepeat];
- ke.keycode = remapKey([event keyCode], [event modifierFlags]);
- ke.physical_keycode = translateKey([event keyCode]);
- 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.keycode = remapKey([event keyCode], [event modifierFlags]);
- ke.physical_keycode = translateKey([event keyCode]);
- ke.raw = false;
- ke.unicode = 0;
-
- push_to_key_event_buffer(ke);
- }
- }
-
- // Pass events to IME handler
- if (OS_OSX::singleton->im_active)
- [self interpretKeyEvents:[NSArray arrayWithObject:event]];
-}
-
-- (void)flagsChanged:(NSEvent *)event {
-
- // 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];
-
- if (key == 0x36 || key == 0x37) {
- if (mod & NSEventModifierFlagCommand) {
- mod &= ~NSEventModifierFlagCommand;
- ke.pressed = true;
- } else {
- ke.pressed = false;
- }
- } else if (key == 0x38 || key == 0x3c) {
- if (mod & NSEventModifierFlagShift) {
- mod &= ~NSEventModifierFlagShift;
- ke.pressed = true;
- } else {
- ke.pressed = false;
- }
- } else if (key == 0x3a || key == 0x3d) {
- if (mod & NSEventModifierFlagOption) {
- mod &= ~NSEventModifierFlagOption;
- ke.pressed = true;
- } else {
- ke.pressed = false;
- }
- } else if (key == 0x3b || key == 0x3e) {
- if (mod & NSEventModifierFlagControl) {
- mod &= ~NSEventModifierFlagControl;
- ke.pressed = true;
- } else {
- ke.pressed = false;
- }
- } else {
- return;
- }
-
- ke.osx_state = mod;
- ke.keycode = remapKey(key, mod);
- ke.physical_keycode = translateKey(key);
- ke.unicode = 0;
-
- push_to_key_event_buffer(ke);
- }
-}
-
-- (void)keyUp:(NSEvent *)event {
-
- // Ignore all input if IME input is in progress
- if (!imeInputEventInProgress) {
- NSString *characters = [event characters];
- NSUInteger length = [characters length];
-
- // 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 = [event isARepeat];
- ke.keycode = remapKey([event keyCode], [event modifierFlags]);
- ke.physical_keycode = translateKey([event keyCode]);
- 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 = false;
- ke.echo = [event isARepeat];
- ke.keycode = remapKey([event keyCode], [event modifierFlags]);
- ke.physical_keycode = translateKey([event keyCode]);
- ke.raw = true;
- ke.unicode = 0;
-
- push_to_key_event_buffer(ke);
- }
- }
-}
-
-inline void sendScrollEvent(int button, double factor, int modifierFlags) {
-
- unsigned int mask = 1 << (button - 1);
- Vector2 mouse_pos = Vector2(mouse_x, mouse_y);
-
- Ref<InputEventMouseButton> sc;
- sc.instance();
-
- get_key_modifier_state(modifierFlags, sc);
- sc->set_button_index(button);
- sc->set_factor(factor);
- sc->set_pressed(true);
- sc->set_position(mouse_pos);
- sc->set_global_position(mouse_pos);
- button_mask |= mask;
- sc->set_button_mask(button_mask);
- OS_OSX::singleton->push_input(sc);
-
- sc.instance();
- sc->set_button_index(button);
- sc->set_factor(factor);
- sc->set_pressed(false);
- sc->set_position(mouse_pos);
- sc->set_global_position(mouse_pos);
- button_mask &= ~mask;
- sc->set_button_mask(button_mask);
- OS_OSX::singleton->push_input(sc);
-}
-
-inline void sendPanEvent(double dx, double dy, int modifierFlags) {
-
- Ref<InputEventPanGesture> pg;
- pg.instance();
-
- get_key_modifier_state(modifierFlags, pg);
- Vector2 mouse_pos = Vector2(mouse_x, mouse_y);
- pg->set_position(mouse_pos);
- pg->set_delta(Vector2(-dx, -dy));
- OS_OSX::singleton->push_input(pg);
-}
-
-- (void)scrollWheel:(NSEvent *)event {
- double deltaX, deltaY;
-
- get_mouse_pos([event locationInWindow], [[event window] backingScaleFactor]);
-
- deltaX = [event scrollingDeltaX];
- deltaY = [event scrollingDeltaY];
-
- if ([event hasPreciseScrollingDeltas]) {
- deltaX *= 0.03;
- deltaY *= 0.03;
- }
-
- if ([event phase] != NSEventPhaseNone || [event momentumPhase] != NSEventPhaseNone) {
- sendPanEvent(deltaX, deltaY, [event modifierFlags]);
- } else {
- if (fabs(deltaX)) {
- sendScrollEvent(0 > deltaX ? BUTTON_WHEEL_RIGHT : BUTTON_WHEEL_LEFT, fabs(deltaX * 0.3), [event modifierFlags]);
- }
- if (fabs(deltaY)) {
- sendScrollEvent(0 < deltaY ? BUTTON_WHEEL_UP : BUTTON_WHEEL_DOWN, fabs(deltaY * 0.3), [event modifierFlags]);
- }
- }
-}
-
-@end
-
-@interface GodotWindow : NSWindow {
-}
-@end
-
-@implementation GodotWindow
-
-- (BOOL)canBecomeKeyWindow {
- // Required for NSBorderlessWindowMask windows
- return YES;
-}
-
-@end
-
-void OS_OSX::_update_global_menu() {
-
- NSMenu *main_menu = [NSApp mainMenu];
-
- for (int i = 1; i < [main_menu numberOfItems]; i++) {
- [main_menu removeItemAtIndex:i];
- }
- for (Map<String, Vector<GlobalMenuItem>>::Element *E = global_menus.front(); E; E = E->next()) {
- if (E->key() != "_dock") {
- NSMenu *menu = [[[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:E->key().utf8().get_data()]] autorelease];
- for (int i = 0; i < E->get().size(); i++) {
- if (E->get()[i].label == String()) {
- [menu addItem:[NSMenuItem separatorItem]];
- } else {
- NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:E->get()[i].label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
- [menu_item setRepresentedObject:[NSValue valueWithPointer:&(E->get()[i])]];
- }
- }
- NSMenuItem *menu_item = [main_menu addItemWithTitle:[NSString stringWithUTF8String:E->key().utf8().get_data()] action:nil keyEquivalent:@""];
- [main_menu setSubmenu:menu forItem:menu_item];
- }
- }
-}
-
-void OS_OSX::global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta) {
-
- global_menus[p_menu].push_back(GlobalMenuItem(p_label, p_signal, p_meta));
- _update_global_menu();
-}
-
-void OS_OSX::global_menu_add_separator(const String &p_menu) {
-
- global_menus[p_menu].push_back(GlobalMenuItem());
- _update_global_menu();
-}
-
-void OS_OSX::global_menu_remove_item(const String &p_menu, int p_idx) {
-
- ERR_FAIL_INDEX(p_idx, global_menus[p_menu].size());
-
- global_menus[p_menu].remove(p_idx);
- _update_global_menu();
-}
-
-void OS_OSX::global_menu_clear(const String &p_menu) {
-
- global_menus[p_menu].clear();
- _update_global_menu();
-}
-
-Point2 OS_OSX::get_ime_selection() const {
-
- return im_selection;
-}
-
-String OS_OSX::get_ime_text() const {
-
- return im_text;
-}
-
-String OS_OSX::get_unique_id() const {
-
- static String serial_number;
-
- if (serial_number.empty()) {
- io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
- CFStringRef serialNumberAsCFString = NULL;
- if (platformExpert) {
- serialNumberAsCFString = (CFStringRef)IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0);
- IOObjectRelease(platformExpert);
- }
-
- NSString *serialNumberAsNSString = nil;
- if (serialNumberAsCFString) {
- serialNumberAsNSString = [NSString stringWithString:(NSString *)serialNumberAsCFString];
- CFRelease(serialNumberAsCFString);
- }
-
- serial_number = [serialNumberAsNSString UTF8String];
- }
-
- return serial_number;
-}
-
-void OS_OSX::set_ime_active(const bool p_active) {
-
- im_active = p_active;
- if (!im_active)
- [window_view cancelComposition];
-}
-
-void OS_OSX::set_ime_position(const Point2 &p_pos) {
-
- im_position = p_pos;
-}
-
-void OS_OSX::initialize_core() {
-
- crash_handler.initialize();
-
- OS_Unix::initialize_core();
-
- DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_RESOURCES);
- DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_USERDATA);
- DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_FILESYSTEM);
-}
-
-static bool keyboard_layout_dirty = true;
-static void keyboard_layout_changed(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) {
- keyboard_layout_dirty = true;
-}
-
-static bool displays_arrangement_dirty = true;
-static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) {
- displays_arrangement_dirty = true;
-}
-
-int OS_OSX::get_current_video_driver() const {
- return video_driver_index;
-}
-
-Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
-
- /*** OSX INITIALIZATION ***/
- /*** OSX INITIALIZATION ***/
- /*** OSX INITIALIZATION ***/
-
- keyboard_layout_dirty = true;
- displays_arrangement_dirty = true;
-
- // Register to be notified on keyboard layout changes
- CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(),
- NULL, keyboard_layout_changed,
- kTISNotifySelectedKeyboardInputSourceChanged, NULL,
- CFNotificationSuspensionBehaviorDeliverImmediately);
-
- // Register to be notified on displays arrangement changes
- CGDisplayRegisterReconfigurationCallback(displays_arrangement_changed, NULL);
-
- //!!!!!!!!!!!!!!!!!!!!!!!!!!
- //TODO - do Vulkan and GLES2 support checks, driver selection and fallback
- video_driver_index = p_video_driver;
- print_verbose("Driver: " + String(get_video_driver_name(video_driver_index)) + " [" + itos(video_driver_index) + "]");
- //!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- //Create window
-
- window_delegate = [[GodotWindowDelegate alloc] init];
-
- // Don't use accumulation buffer support; it's not accelerated
- // Aux buffers probably aren't accelerated either
-
- unsigned int styleMask;
-
- if (p_desired.borderless_window) {
- styleMask = NSWindowStyleMaskBorderless;
- } else {
- resizable = p_desired.resizable;
- styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (p_desired.resizable ? NSWindowStyleMaskResizable : 0);
- }
-
- window_object = [[GodotWindow alloc]
- initWithContentRect:NSMakeRect(0, 0, p_desired.width, p_desired.height)
- styleMask:styleMask
- backing:NSBackingStoreBuffered
- defer:NO];
-
- ERR_FAIL_COND_V(window_object == nil, ERR_UNAVAILABLE);
-
- window_view = [[GodotContentView alloc] init];
- if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_14) {
- [window_view setWantsLayer:TRUE];
- }
-
- float displayScale = 1.0;
- if (is_hidpi_allowed()) {
- // note that mainScreen is not screen #0 but the one with the keyboard focus.
- NSScreen *screen = [NSScreen mainScreen];
- if ([screen respondsToSelector:@selector(backingScaleFactor)]) {
- displayScale = fmax(displayScale, [screen backingScaleFactor]);
- }
- }
-
- window_size.width = p_desired.width * displayScale;
- window_size.height = p_desired.height * displayScale;
-
- if (displayScale > 1.0) {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- [window_view setWantsBestResolutionOpenGLSurface:YES];
- }
-#endif
- [window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
- } else {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- [window_view setWantsBestResolutionOpenGLSurface:NO];
- }
-#endif
- }
-
- [window_object setContentView:window_view];
- [window_object setDelegate:window_delegate];
- [window_object setAcceptsMouseMovedEvents:YES];
- [(NSWindow *)window_object center];
-
- [window_object setRestorable:NO];
-
- // Init context and rendering device
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
-
- context_gles2 = memnew(ContextGL_OSX(window_view, false));
-
- if (context_gles2->initialize() != OK) {
- memdelete(context_gles2);
- context_gles2 = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
-
- context_gles2->set_use_vsync(p_desired.use_vsync);
-
- if (RasterizerGLES2::is_viable() == OK) {
- RasterizerGLES2::register_config();
- RasterizerGLES2::make_current();
- } else {
- memdelete(context_gles2);
- context_gles2 = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
-
- context_vulkan = memnew(VulkanContextOSX);
- if (context_vulkan->initialize() != OK) {
- memdelete(context_vulkan);
- context_vulkan = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
- if (context_vulkan->window_create(window_view, get_video_mode().width, get_video_mode().height) == -1) {
- memdelete(context_vulkan);
- context_vulkan = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
-
- rendering_device_vulkan = memnew(RenderingDeviceVulkan);
- rendering_device_vulkan->initialize(context_vulkan);
-
- RasterizerRD::make_current();
- }
-#endif
-
- [NSApp activateIgnoringOtherApps:YES];
-
- _update_window();
-
- [window_object makeKeyAndOrderFront:nil];
-
- if (p_desired.fullscreen)
- zoomed = true;
-
- visual_server = memnew(VisualServerRaster);
- if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
- visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
- }
-
- visual_server->init();
- AudioDriverManager::initialize(p_audio_driver);
-
- input = memnew(InputDefault);
- joypad_osx = memnew(JoypadOSX);
-
- _ensure_user_data_dir();
-
- restore_rect = Rect2(get_window_position(), get_window_size());
-
- if (p_desired.layered) {
- set_window_per_pixel_transparency_enabled(true);
- }
-
- update_real_mouse_position();
-
- return OK;
-}
-
-void OS_OSX::finalize() {
-
-#ifdef COREMIDI_ENABLED
- midi_driver.close();
-#endif
-
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
-
- if (context_gles2)
- memdelete(context_gles2);
- }
-#endif
-
- CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, kTISNotifySelectedKeyboardInputSourceChanged, NULL);
- CGDisplayRemoveReconfigurationCallback(displays_arrangement_changed, NULL);
-
- delete_main_loop();
-
- memdelete(joypad_osx);
- memdelete(input);
-
- cursors_cache.clear();
- visual_server->finish();
- memdelete(visual_server);
-}
-
-void OS_OSX::set_main_loop(MainLoop *p_main_loop) {
-
- main_loop = p_main_loop;
- input->set_main_loop(p_main_loop);
-}
-
-void OS_OSX::delete_main_loop() {
-
- if (!main_loop)
- return;
- memdelete(main_loop);
- main_loop = NULL;
-}
-
-String OS_OSX::get_name() const {
+#include <mach-o/dyld.h>
+#include <os/log.h>
- return "OSX";
-}
+/*************************************************************************/
+/* OSXTerminalLogger */
+/*************************************************************************/
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
class OSXTerminalLogger : public StdLogger {
public:
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type = ERR_ERROR) {
@@ -1747,333 +100,101 @@ public:
}
};
-#else
-
-typedef UnixTerminalLogger OSXTerminalLogger;
-#endif
-
-void OS_OSX::alert(const String &p_alert, const String &p_title) {
- // Set OS X-compliant variables
- NSAlert *window = [[NSAlert alloc] init];
- NSString *ns_title = [NSString stringWithUTF8String:p_title.utf8().get_data()];
- NSString *ns_alert = [NSString stringWithUTF8String:p_alert.utf8().get_data()];
-
- [window addButtonWithTitle:@"OK"];
- [window setMessageText:ns_title];
- [window setInformativeText:ns_alert];
- [window setAlertStyle:NSAlertStyleWarning];
-
- // Display it, then release
- [window runModal];
- [window release];
-}
+/*************************************************************************/
+/* OS_OSX */
+/*************************************************************************/
-Error OS_OSX::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
+String OS_OSX::get_unique_id() const {
+ static String serial_number;
- String path = p_path;
+ if (serial_number.empty()) {
+ io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
+ CFStringRef serialNumberAsCFString = NULL;
+ if (platformExpert) {
+ serialNumberAsCFString = (CFStringRef)IORegistryEntryCreateCFProperty(platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, 0);
+ IOObjectRelease(platformExpert);
+ }
- if (!FileAccess::exists(path)) {
- //this code exists so gdnative can load .dylib files from within the executable path
- path = get_executable_path().get_base_dir().plus_file(p_path.get_file());
- }
+ NSString *serialNumberAsNSString = nil;
+ if (serialNumberAsCFString) {
+ serialNumberAsNSString = [NSString stringWithString:(NSString *)serialNumberAsCFString];
+ CFRelease(serialNumberAsCFString);
+ }
- if (!FileAccess::exists(path)) {
- //this code exists so gdnative can load .dylib files from a standard macOS location
- path = get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(p_path.get_file());
+ serial_number = [serialNumberAsNSString UTF8String];
}
- p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
- ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + ".");
- return OK;
+ return serial_number;
}
-void OS_OSX::set_cursor_shape(CursorShape p_shape) {
-
- if (cursor_shape == p_shape)
- return;
-
- if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
- cursor_shape = p_shape;
- return;
- }
-
- if (cursors[p_shape] != NULL) {
- [cursors[p_shape] set];
- } else {
- switch (p_shape) {
- case CURSOR_ARROW: [[NSCursor arrowCursor] set]; break;
- case CURSOR_IBEAM: [[NSCursor IBeamCursor] set]; break;
- case CURSOR_POINTING_HAND: [[NSCursor pointingHandCursor] set]; break;
- case CURSOR_CROSS: [[NSCursor crosshairCursor] set]; break;
- case CURSOR_WAIT: [[NSCursor arrowCursor] set]; break;
- case CURSOR_BUSY: [[NSCursor arrowCursor] set]; break;
- case CURSOR_DRAG: [[NSCursor closedHandCursor] set]; break;
- case CURSOR_CAN_DROP: [[NSCursor openHandCursor] set]; break;
- case CURSOR_FORBIDDEN: [[NSCursor operationNotAllowedCursor] set]; break;
- case CURSOR_VSIZE: [cursorFromSelector(@selector(_windowResizeNorthSouthCursor), @selector(resizeUpDownCursor)) set]; break;
- case CURSOR_HSIZE: [cursorFromSelector(@selector(_windowResizeEastWestCursor), @selector(resizeLeftRightCursor)) set]; break;
- case CURSOR_BDIAGSIZE: [cursorFromSelector(@selector(_windowResizeNorthEastSouthWestCursor)) set]; break;
- case CURSOR_FDIAGSIZE: [cursorFromSelector(@selector(_windowResizeNorthWestSouthEastCursor)) set]; break;
- case CURSOR_MOVE: [[NSCursor arrowCursor] set]; break;
- case CURSOR_VSPLIT: [[NSCursor resizeUpDownCursor] set]; break;
- case CURSOR_HSPLIT: [[NSCursor resizeLeftRightCursor] set]; break;
- case CURSOR_HELP: [cursorFromSelector(@selector(_helpCursor)) set]; break;
- default: {
- };
- }
- }
+void OS_OSX::initialize_core() {
+ OS_Unix::initialize_core();
- cursor_shape = p_shape;
+ DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_RESOURCES);
+ DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_USERDATA);
+ DirAccess::make_default<DirAccessOSX>(DirAccess::ACCESS_FILESYSTEM);
}
-OS::CursorShape OS_OSX::get_cursor_shape() const {
-
- return cursor_shape;
+void OS_OSX::initialize_joypads() {
+ joypad_osx = memnew(JoypadOSX(InputFilter::get_singleton()));
}
-void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
-
- if (p_cursor.is_valid()) {
-
- Map<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);
-
- if (cursor_c) {
- if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {
- set_cursor_shape(p_shape);
- return;
- }
-
- cursors_cache.erase(p_shape);
- }
-
- Ref<Texture2D> texture = p_cursor;
- Ref<AtlasTexture> atlas_texture = p_cursor;
- Ref<Image> image;
- Size2 texture_size;
- Rect2 atlas_rect;
-
- if (texture.is_valid()) {
- image = texture->get_data();
- }
-
- if (!image.is_valid() && atlas_texture.is_valid()) {
- texture = atlas_texture->get_atlas();
-
- atlas_rect.size.width = texture->get_width();
- atlas_rect.size.height = texture->get_height();
- atlas_rect.position.x = atlas_texture->get_region().position.x;
- atlas_rect.position.y = atlas_texture->get_region().position.y;
-
- texture_size.width = atlas_texture->get_region().size.x;
- texture_size.height = atlas_texture->get_region().size.y;
- } else if (image.is_valid()) {
- texture_size.width = texture->get_width();
- texture_size.height = texture->get_height();
- }
-
- ERR_FAIL_COND(!texture.is_valid());
- ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
- ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
- ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
-
- image = texture->get_data();
-
- ERR_FAIL_COND(!image.is_valid());
-
- NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
- initWithBitmapDataPlanes:NULL
- pixelsWide:int(texture_size.width)
- pixelsHigh:int(texture_size.height)
- bitsPerSample:8
- samplesPerPixel:4
- hasAlpha:YES
- isPlanar:NO
- colorSpaceName:NSDeviceRGBColorSpace
- bytesPerRow:int(texture_size.width) * 4
- bitsPerPixel:32];
-
- ERR_FAIL_COND(imgrep == nil);
- uint8_t *pixels = [imgrep bitmapData];
-
- int len = int(texture_size.width * texture_size.height);
-
- for (int i = 0; i < len; i++) {
- int row_index = floor(i / texture_size.width) + atlas_rect.position.y;
- int column_index = (i % int(texture_size.width)) + atlas_rect.position.x;
-
- if (atlas_texture.is_valid()) {
- column_index = MIN(column_index, atlas_rect.size.width - 1);
- row_index = MIN(row_index, atlas_rect.size.height - 1);
- }
-
- uint32_t color = image->get_pixel(column_index, row_index).to_argb32();
-
- uint8_t alpha = (color >> 24) & 0xFF;
- pixels[i * 4 + 0] = ((color >> 16) & 0xFF) * alpha / 255;
- pixels[i * 4 + 1] = ((color >> 8) & 0xFF) * alpha / 255;
- pixels[i * 4 + 2] = ((color)&0xFF) * alpha / 255;
- pixels[i * 4 + 3] = alpha;
- }
-
- NSImage *nsimage = [[NSImage alloc] initWithSize:NSMakeSize(texture_size.width, texture_size.height)];
- [nsimage addRepresentation:imgrep];
-
- NSCursor *cursor = [[NSCursor alloc] initWithImage:nsimage hotSpot:NSMakePoint(p_hotspot.x, p_hotspot.y)];
-
- [cursors[p_shape] release];
- cursors[p_shape] = cursor;
-
- Vector<Variant> params;
- params.push_back(p_cursor);
- params.push_back(p_hotspot);
- cursors_cache.insert(p_shape, params);
-
- if (p_shape == cursor_shape) {
- if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
- [cursor set];
- }
- }
-
- [imgrep release];
- [nsimage release];
- } else {
- // Reset to default system cursor
- if (cursors[p_shape] != NULL) {
- [cursors[p_shape] release];
- cursors[p_shape] = NULL;
- }
-
- CursorShape c = cursor_shape;
- cursor_shape = CURSOR_MAX;
- set_cursor_shape(c);
-
- cursors_cache.erase(p_shape);
- }
-}
-
-void OS_OSX::set_mouse_show(bool p_show) {
-}
+void OS_OSX::initialize() {
+ crash_handler.initialize();
-void OS_OSX::set_mouse_grab(bool p_grab) {
+ initialize_core();
+ //ensure_user_data_dir();
}
-bool OS_OSX::is_mouse_grab_enabled() const {
-
- return mouse_grab;
-}
+void OS_OSX::finalize() {
-void OS_OSX::warp_mouse_position(const Point2 &p_to) {
-
- //copied from windows impl with osx native calls
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
- mouse_x = p_to.x;
- mouse_y = p_to.y;
- } else { //set OS position
-
- //local point in window coords
- const NSRect contentRect = [window_view frame];
- float displayScale = _display_scale();
- NSRect pointInWindowRect = NSMakeRect(p_to.x / displayScale, contentRect.size.height - (p_to.y / displayScale) - 1, 0, 0);
- NSPoint pointOnScreen = [[window_view window] convertRectToScreen:pointInWindowRect].origin;
-
- //point in scren coords
- CGPoint lMouseWarpPos = { pointOnScreen.x, CGDisplayBounds(CGMainDisplayID()).size.height - pointOnScreen.y };
-
- //do the warping
- CGEventSourceRef lEventRef = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
- CGEventSourceSetLocalEventsSuppressionInterval(lEventRef, 0.0);
- CGAssociateMouseAndMouseCursorPosition(false);
- CGWarpMouseCursorPosition(lMouseWarpPos);
- CGAssociateMouseAndMouseCursorPosition(true);
- }
-}
+#ifdef COREMIDI_ENABLED
+ midi_driver.close();
+#endif
-void OS_OSX::update_real_mouse_position() {
+ delete_main_loop();
- get_mouse_pos([window_object mouseLocationOutsideOfEventStream], [window_view backingScaleFactor]);
- input->set_mouse_position(Point2(mouse_x, mouse_y));
+ memdelete(joypad_osx);
}
-Point2 OS_OSX::get_mouse_position() const {
-
- return Vector2(mouse_x, mouse_y);
+void OS_OSX::set_main_loop(MainLoop *p_main_loop) {
+ main_loop = p_main_loop;
}
-int OS_OSX::get_mouse_button_state() const {
- return button_mask;
+void OS_OSX::delete_main_loop() {
+ if (!main_loop)
+ return;
+ memdelete(main_loop);
+ main_loop = NULL;
}
-void OS_OSX::set_window_title(const String &p_title) {
- title = p_title;
-
- [window_object setTitle:[NSString stringWithUTF8String:p_title.utf8().get_data()]];
+String OS_OSX::get_name() const {
+ return "macOS";
}
-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];
- ERR_FAIL_COND_MSG(!icon_data, "Error reading icon data.");
-
- NSImage *icon = [[[NSImage alloc] initWithData:icon_data] autorelease];
- ERR_FAIL_COND_MSG(!icon, "Error loading icon.");
-
- [NSApp setApplicationIconImage:icon];
-}
+Error OS_OSX::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
+ String path = p_path;
-void OS_OSX::set_icon(const Ref<Image> &p_icon) {
-
- Ref<Image> img = p_icon;
- img = img->duplicate();
- img->convert(Image::FORMAT_RGBA8);
- NSBitmapImageRep *imgrep = [[[NSBitmapImageRep alloc]
- initWithBitmapDataPlanes:NULL
- pixelsWide:img->get_width()
- pixelsHigh:img->get_height()
- bitsPerSample:8
- samplesPerPixel:4
- hasAlpha:YES
- isPlanar:NO
- colorSpaceName:NSDeviceRGBColorSpace
- bytesPerRow:img->get_width() * 4
- bitsPerPixel:32] autorelease];
- ERR_FAIL_COND(imgrep == nil);
- uint8_t *pixels = [imgrep bitmapData];
-
- int len = img->get_width() * img->get_height();
- const uint8_t *r = img->get_data().ptr();
-
- /* Premultiply the alpha channel */
- for (int i = 0; i < len; i++) {
- uint8_t alpha = r[i * 4 + 3];
- pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255);
- pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255);
- pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255);
- pixels[i * 4 + 3] = alpha;
+ if (!FileAccess::exists(path)) {
+ //this code exists so gdnative can load .dylib files from within the executable path
+ path = get_executable_path().get_base_dir().plus_file(p_path.get_file());
}
- NSImage *nsimg = [[[NSImage alloc] initWithSize:NSMakeSize(img->get_width(), img->get_height())] autorelease];
- ERR_FAIL_COND(nsimg == nil);
- [nsimg addRepresentation:imgrep];
+ if (!FileAccess::exists(path)) {
+ //this code exists so gdnative can load .dylib files from a standard macOS location
+ path = get_executable_path().get_base_dir().plus_file("../Frameworks").plus_file(p_path.get_file());
+ }
- [NSApp setApplicationIconImage:nsimg];
+ p_library_handle = dlopen(path.utf8().get_data(), RTLD_NOW);
+ ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + ".");
+ return OK;
}
MainLoop *OS_OSX::get_main_loop() const {
-
return main_loop;
}
String OS_OSX::get_config_path() const {
-
if (has_environment("XDG_CONFIG_HOME")) {
return get_environment("XDG_CONFIG_HOME");
} else if (has_environment("HOME")) {
@@ -2084,7 +205,6 @@ String OS_OSX::get_config_path() const {
}
String OS_OSX::get_data_path() const {
-
if (has_environment("XDG_DATA_HOME")) {
return get_environment("XDG_DATA_HOME");
} else {
@@ -2093,7 +213,6 @@ String OS_OSX::get_data_path() const {
}
String OS_OSX::get_cache_path() const {
-
if (has_environment("XDG_CACHE_HOME")) {
return get_environment("XDG_CACHE_HOME");
} else if (has_environment("HOME")) {
@@ -2104,7 +223,6 @@ String OS_OSX::get_cache_path() const {
}
String OS_OSX::get_bundle_resource_dir() const {
-
NSBundle *main = [NSBundle mainBundle];
NSString *resourcePath = [main resourcePath];
@@ -2118,12 +236,10 @@ String OS_OSX::get_bundle_resource_dir() const {
// Get properly capitalized engine name for system paths
String OS_OSX::get_godot_dir_name() const {
-
return String(VERSION_SHORT_NAME).capitalize();
}
String OS_OSX::get_system_dir(SystemDir p_dir) const {
-
NSSearchPathDirectory id;
bool found = true;
@@ -2166,62 +282,7 @@ String OS_OSX::get_system_dir(SystemDir p_dir) const {
return ret;
}
-bool OS_OSX::can_draw() const {
-
- return true;
-}
-
-void OS_OSX::set_clipboard(const String &p_text) {
-
- NSString *copiedString = [NSString stringWithUTF8String:p_text.utf8().get_data()];
- NSArray *copiedStringArray = [NSArray arrayWithObject:copiedString];
-
- NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
- [pasteboard clearContents];
- [pasteboard writeObjects:copiedStringArray];
-}
-
-String OS_OSX::get_clipboard() const {
-
- NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
- NSArray *classArray = [NSArray arrayWithObject:[NSString class]];
- NSDictionary *options = [NSDictionary dictionary];
-
- BOOL ok = [pasteboard canReadObjectForClasses:classArray options:options];
-
- if (!ok) {
- return "";
- }
-
- NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options];
- NSString *string = [objectsToPaste objectAtIndex:0];
-
- char *utfs = strdup([string UTF8String]);
- String ret;
- ret.parse_utf8(utfs);
- free(utfs);
-
- return ret;
-}
-
-void OS_OSX::release_rendering_thread() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->release_current();
- }
-#endif
-}
-
-void OS_OSX::make_rendering_thread() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->make_current();
- }
-#endif
-}
-
Error OS_OSX::shell_open(String p_uri) {
-
[[NSWorkspace sharedWorkspace] openURL:[[NSURL alloc] initWithString:[[NSString stringWithUTF8String:p_uri.utf8().get_data()] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]]]];
return OK;
}
@@ -2231,491 +292,7 @@ String OS_OSX::get_locale() const {
return [locale_code UTF8String];
}
-void OS_OSX::swap_buffers() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->swap_buffers();
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
- context_vulkan->swap_buffers();
- }
-#endif
-}
-
-void OS_OSX::wm_minimized(bool p_minimized) {
-
- minimized = p_minimized;
-};
-
-void OS_OSX::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
-}
-
-OS::VideoMode OS_OSX::get_video_mode(int p_screen) const {
-
- VideoMode vm;
- vm.width = window_size.width;
- vm.height = window_size.height;
-
- return vm;
-}
-
-void OS_OSX::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
-}
-
-int OS_OSX::get_screen_count() const {
- NSArray *screenArray = [NSScreen screens];
- return [screenArray count];
-};
-
-// Returns the native top-left screen coordinate of the smallest rectangle
-// that encompasses all screens. Needed in get_screen_position(),
-// get_window_position, and set_window_position()
-// to convert between OS X native screen coordinates and the ones expected by Godot
-Point2 OS_OSX::get_screens_origin() const {
- static Point2 origin;
-
- if (displays_arrangement_dirty) {
- origin = Point2();
-
- for (int i = 0; i < get_screen_count(); i++) {
- Point2 position = get_native_screen_position(i);
- if (position.x < origin.x) {
- origin.x = position.x;
- }
- if (position.y > origin.y) {
- origin.y = position.y;
- }
- }
-
- displays_arrangement_dirty = false;
- }
-
- return origin;
-}
-
-static int get_screen_index(NSScreen *screen) {
- const NSUInteger index = [[NSScreen screens] indexOfObject:screen];
- return index == NSNotFound ? 0 : index;
-}
-
-int OS_OSX::get_current_screen() const {
- if (window_object) {
- return get_screen_index([window_object screen]);
- } else {
- return get_screen_index([NSScreen mainScreen]);
- }
-};
-
-void OS_OSX::set_current_screen(int p_screen) {
- Vector2 wpos = get_window_position() - get_screen_position(get_current_screen());
- set_window_position(wpos + get_screen_position(p_screen));
-};
-
-Point2 OS_OSX::get_native_screen_position(int p_screen) const {
- if (p_screen < 0) {
- p_screen = get_current_screen();
- }
-
- NSArray *screenArray = [NSScreen screens];
- if ((NSUInteger)p_screen < [screenArray count]) {
- float display_scale = _display_scale([screenArray objectAtIndex:p_screen]);
- NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
- // Return the top-left corner of the screen, for OS X the y starts at the bottom
- return Point2(nsrect.origin.x, nsrect.origin.y + nsrect.size.height) * display_scale;
- }
-
- return Point2();
-}
-
-Point2 OS_OSX::get_screen_position(int p_screen) const {
- Point2 position = get_native_screen_position(p_screen) - get_screens_origin();
- // OS X native y-coordinate relative to get_screens_origin() is negative,
- // Godot expects a positive value
- position.y *= -1;
- return position;
-}
-
-int OS_OSX::get_screen_dpi(int p_screen) const {
- if (p_screen < 0) {
- p_screen = get_current_screen();
- }
-
- NSArray *screenArray = [NSScreen screens];
- if ((NSUInteger)p_screen < [screenArray count]) {
- float displayScale = _display_scale([screenArray objectAtIndex:p_screen]);
- NSDictionary *description = [[screenArray objectAtIndex:p_screen] deviceDescription];
- NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue];
- CGSize displayPhysicalSize = CGDisplayScreenSize(
- [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]);
-
- return (displayPixelSize.width * 25.4f / displayPhysicalSize.width) * displayScale;
- }
-
- return 72;
-}
-
-Size2 OS_OSX::get_screen_size(int p_screen) const {
- if (p_screen < 0) {
- p_screen = get_current_screen();
- }
-
- NSArray *screenArray = [NSScreen screens];
- if ((NSUInteger)p_screen < [screenArray count]) {
- float displayScale = _display_scale([screenArray objectAtIndex:p_screen]);
- // Note: Use frame to get the whole screen size
- NSRect nsrect = [[screenArray objectAtIndex:p_screen] frame];
- return Size2(nsrect.size.width, nsrect.size.height) * displayScale;
- }
-
- return Size2();
-}
-
-void OS_OSX::_update_window() {
- bool borderless_full = false;
-
- if (get_borderless_window()) {
- NSRect frameRect = [window_object frame];
- NSRect screenRect = [[window_object screen] frame];
-
- // Check if our window covers up the screen
- if (frameRect.origin.x <= screenRect.origin.x && frameRect.origin.y <= frameRect.origin.y &&
- frameRect.size.width >= screenRect.size.width && frameRect.size.height >= screenRect.size.height) {
- borderless_full = true;
- }
- }
-
- if (borderless_full) {
- // If the window covers up the screen set the level to above the main menu and hide on deactivate
- [window_object setLevel:NSMainMenuWindowLevel + 1];
- [window_object setHidesOnDeactivate:YES];
- } else {
- // Reset these when our window is not a borderless window that covers up the screen
- [window_object setLevel:NSNormalWindowLevel];
- [window_object setHidesOnDeactivate:NO];
- }
-}
-
-float OS_OSX::_display_scale() const {
- if (window_object) {
- return _display_scale([window_object screen]);
- } else {
- return _display_scale([NSScreen mainScreen]);
- }
-}
-
-float OS_OSX::_display_scale(id screen) const {
- if (is_hidpi_allowed()) {
- if ([screen respondsToSelector:@selector(backingScaleFactor)]) {
- return fmax(1.0, [screen backingScaleFactor]);
- }
- }
- return 1.0;
-}
-
-Point2 OS_OSX::get_native_window_position() const {
-
- NSRect nsrect = [window_object frame];
- Point2 pos;
- float display_scale = _display_scale();
-
- // Return the position of the top-left corner, for OS X the y starts at the bottom
- pos.x = nsrect.origin.x * display_scale;
- pos.y = (nsrect.origin.y + nsrect.size.height) * display_scale;
-
- return pos;
-};
-
-Point2 OS_OSX::get_window_position() const {
- Point2 position = get_native_window_position() - get_screens_origin();
- // OS X native y-coordinate relative to get_screens_origin() is negative,
- // Godot expects a positive value
- position.y *= -1;
- return position;
-}
-
-void OS_OSX::set_native_window_position(const Point2 &p_position) {
-
- NSPoint pos;
- float displayScale = _display_scale();
-
- pos.x = p_position.x / displayScale;
- pos.y = p_position.y / displayScale;
-
- [window_object setFrameTopLeftPoint:pos];
-
- _update_window();
-};
-
-void OS_OSX::set_window_position(const Point2 &p_position) {
- Point2 position = p_position;
- // OS X native y-coordinate relative to get_screens_origin() is negative,
- // Godot passes a positive value
- position.y *= -1;
- set_native_window_position(get_screens_origin() + position);
-
- update_real_mouse_position();
-};
-
-Size2 OS_OSX::get_window_size() const {
-
- return window_size;
-};
-
-Size2 OS_OSX::get_real_window_size() const {
-
- NSRect frame = [window_object frame];
- return Size2(frame.size.width, frame.size.height) * _display_scale();
-}
-
-Size2 OS_OSX::get_max_window_size() const {
- return max_size;
-}
-
-Size2 OS_OSX::get_min_window_size() const {
- return min_size;
-}
-
-void OS_OSX::set_min_window_size(const Size2 p_size) {
-
- if ((p_size != Size2()) && (max_size != Size2()) && ((p_size.x > max_size.x) || (p_size.y > max_size.y))) {
- ERR_PRINT("Minimum window size can't be larger than maximum window size!");
- return;
- }
- min_size = p_size;
-
- if ((min_size != Size2()) && !zoomed) {
- Size2 size = min_size / _display_scale();
- [window_object setContentMinSize:NSMakeSize(size.x, size.y)];
- } else {
- [window_object setContentMinSize:NSMakeSize(0, 0)];
- }
-}
-
-void OS_OSX::set_max_window_size(const Size2 p_size) {
-
- if ((p_size != Size2()) && ((p_size.x < min_size.x) || (p_size.y < min_size.y))) {
- ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
- return;
- }
- max_size = p_size;
-
- if ((max_size != Size2()) && !zoomed) {
- Size2 size = max_size / _display_scale();
- [window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
- } else {
- [window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
- }
-}
-
-void OS_OSX::set_window_size(const Size2 p_size) {
-
- Size2 size = p_size / _display_scale();
-
- if (get_borderless_window() == false) {
- // NSRect used by setFrame includes the title bar, so add it to our size.y
- CGFloat menuBarHeight = [[[NSApplication sharedApplication] mainMenu] menuBarHeight];
- if (menuBarHeight != 0.f) {
- size.y += menuBarHeight;
- } else {
- if (floor(NSAppKitVersionNumber) < NSAppKitVersionNumber10_12) {
- size.y += [[NSStatusBar systemStatusBar] thickness];
- }
- }
- }
-
- NSRect frame = [window_object frame];
- [window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, size.x, size.y) display:YES];
-
- _update_window();
-};
-
-void OS_OSX::set_window_fullscreen(bool p_enabled) {
-
- if (zoomed != p_enabled) {
- if (layered_window)
- set_window_per_pixel_transparency_enabled(false);
- if (!resizable)
- [window_object setStyleMask:[window_object styleMask] | NSWindowStyleMaskResizable];
- if (p_enabled) {
- [window_object setContentMinSize:NSMakeSize(0, 0)];
- [window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
- } else {
- if (min_size != Size2()) {
- Size2 size = min_size / _display_scale();
- [window_object setContentMinSize:NSMakeSize(size.x, size.y)];
- }
- if (max_size != Size2()) {
- Size2 size = max_size / _display_scale();
- [window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
- }
- }
- [window_object toggleFullScreen:nil];
- }
- zoomed = p_enabled;
-};
-
-bool OS_OSX::is_window_fullscreen() const {
-
- return zoomed;
-};
-
-void OS_OSX::set_window_resizable(bool p_enabled) {
-
- if (p_enabled)
- [window_object setStyleMask:[window_object styleMask] | NSWindowStyleMaskResizable];
- else if (!zoomed)
- [window_object setStyleMask:[window_object styleMask] & ~NSWindowStyleMaskResizable];
-
- resizable = p_enabled;
-};
-
-bool OS_OSX::is_window_resizable() const {
-
- return [window_object styleMask] & NSWindowStyleMaskResizable;
-};
-
-void OS_OSX::set_window_minimized(bool p_enabled) {
-
- if (p_enabled)
- [window_object performMiniaturize:nil];
- else
- [window_object deminiaturize:nil];
-};
-
-bool OS_OSX::is_window_minimized() const {
-
- if ([window_object respondsToSelector:@selector(isMiniaturized)])
- return [window_object isMiniaturized];
-
- return minimized;
-};
-
-void OS_OSX::set_window_maximized(bool p_enabled) {
-
- if (p_enabled) {
- restore_rect = Rect2(get_window_position(), get_window_size());
- [window_object setFrame:[[[NSScreen screens] objectAtIndex:get_current_screen()] visibleFrame] display:YES];
- } else {
- set_window_size(restore_rect.size);
- set_window_position(restore_rect.position);
- };
- maximized = p_enabled;
-};
-
-bool OS_OSX::is_window_maximized() const {
-
- // don't know
- return maximized;
-};
-
-void OS_OSX::move_window_to_foreground() {
-
- [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
- [window_object makeKeyAndOrderFront:nil];
-}
-
-void OS_OSX::set_window_always_on_top(bool p_enabled) {
- if (is_window_always_on_top() == p_enabled)
- return;
-
- if (p_enabled)
- [window_object setLevel:NSFloatingWindowLevel];
- else
- [window_object setLevel:NSNormalWindowLevel];
-}
-
-bool OS_OSX::is_window_always_on_top() const {
- return [window_object level] == NSFloatingWindowLevel;
-}
-
-bool OS_OSX::is_window_focused() const {
- return window_focused;
-}
-
-void OS_OSX::request_attention() {
-
- [NSApp requestUserAttention:NSCriticalRequest];
-}
-
-bool OS_OSX::get_window_per_pixel_transparency_enabled() const {
-
- if (!is_layered_allowed()) return false;
- return layered_window;
-}
-
-void OS_OSX::set_window_per_pixel_transparency_enabled(bool p_enabled) {
-
- if (!is_layered_allowed()) return;
- if (layered_window != p_enabled) {
- if (p_enabled) {
- set_borderless_window(true);
- [window_object setBackgroundColor:[NSColor clearColor]];
- [window_object setOpaque:NO];
- [window_object setHasShadow:NO];
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->set_opacity(0);
- }
-#endif
- layered_window = true;
- } else {
- [window_object setBackgroundColor:[NSColor colorWithCalibratedWhite:1 alpha:1]];
- [window_object setOpaque:YES];
- [window_object setHasShadow:YES];
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->set_opacity(1);
- }
-#endif
- layered_window = false;
- }
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->update();
- }
-#endif
- NSRect frame = [window_object frame];
- [window_object setFrame:NSMakeRect(frame.origin.x, frame.origin.y, 1, 1) display:YES];
- [window_object setFrame:frame display:YES];
- }
-}
-
-void OS_OSX::set_borderless_window(bool p_borderless) {
-
- // OrderOut prevents a lose focus bug with the window
- [window_object orderOut:nil];
-
- if (p_borderless) {
- [window_object setStyleMask:NSWindowStyleMaskBorderless];
- } else {
- if (layered_window)
- set_window_per_pixel_transparency_enabled(false);
-
- [window_object setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | (resizable ? NSWindowStyleMaskResizable : 0)];
-
- // Force update of the window styles
- NSRect frameRect = [window_object frame];
- [window_object setFrame:NSMakeRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width + 1, frameRect.size.height) display:NO];
- [window_object setFrame:frameRect display:NO];
-
- // Restore the window title
- [window_object setTitle:[NSString stringWithUTF8String:title.utf8().get_data()]];
- }
-
- _update_window();
-
- [window_object makeKeyAndOrderFront:nil];
-}
-
-bool OS_OSX::get_borderless_window() {
-
- return [window_object styleMask] == NSWindowStyleMaskBorderless;
-}
-
String OS_OSX::get_executable_path() const {
-
int ret;
pid_t pid;
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
@@ -2732,177 +309,7 @@ String OS_OSX::get_executable_path() const {
}
}
-// Returns string representation of keys, if they are printable.
-//
-static NSString *createStringForKeys(const CGKeyCode *keyCode, int length) {
-
- TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
- if (!currentKeyboard)
- return nil;
-
- CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
- if (!layoutData)
- return nil;
-
- const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData);
-
- OSStatus err;
- CFMutableStringRef output = CFStringCreateMutable(NULL, 0);
-
- for (int i = 0; i < length; ++i) {
-
- UInt32 keysDown = 0;
- UniChar chars[4];
- UniCharCount realLength;
-
- err = UCKeyTranslate(keyboardLayout,
- keyCode[i],
- kUCKeyActionDisplay,
- 0,
- LMGetKbdType(),
- kUCKeyTranslateNoDeadKeysBit,
- &keysDown,
- sizeof(chars) / sizeof(chars[0]),
- &realLength,
- chars);
-
- if (err != noErr) {
- CFRelease(output);
- return nil;
- }
-
- CFStringAppendCharacters(output, chars, 1);
- }
-
- //CFStringUppercase(output, NULL);
-
- return (NSString *)output;
-}
-
-OS::LatinKeyboardVariant OS_OSX::get_latin_keyboard_variant() const {
-
- static LatinKeyboardVariant layout = LATIN_KEYBOARD_QWERTY;
-
- if (keyboard_layout_dirty) {
-
- layout = LATIN_KEYBOARD_QWERTY;
-
- CGKeyCode keys[] = { kVK_ANSI_Q, kVK_ANSI_W, kVK_ANSI_E, kVK_ANSI_R, kVK_ANSI_T, kVK_ANSI_Y };
- NSString *test = createStringForKeys(keys, 6);
-
- if ([test isEqualToString:@"qwertz"]) {
- layout = LATIN_KEYBOARD_QWERTZ;
- } else if ([test isEqualToString:@"azerty"]) {
- layout = LATIN_KEYBOARD_AZERTY;
- } else if ([test isEqualToString:@"qzerty"]) {
- layout = LATIN_KEYBOARD_QZERTY;
- } else if ([test isEqualToString:@"',.pyf"]) {
- layout = LATIN_KEYBOARD_DVORAK;
- } else if ([test isEqualToString:@"xvlcwk"]) {
- layout = LATIN_KEYBOARD_NEO;
- } else if ([test isEqualToString:@"qwfpgj"]) {
- layout = LATIN_KEYBOARD_COLEMAK;
- }
-
- [test release];
-
- keyboard_layout_dirty = false;
- return layout;
- }
-
- return layout;
-}
-
-void OS_OSX::process_events() {
-
- while (true) {
- NSEvent *event = [NSApp
- nextEventMatchingMask:NSEventMaskAny
- untilDate:[NSDate distantPast]
- inMode:NSDefaultRunLoopMode
- dequeue:YES];
-
- if (event == nil)
- break;
-
- [NSApp sendEvent:event];
- }
- process_key_events();
-
- [autoreleasePool drain];
- autoreleasePool = [[NSAutoreleasePool alloc] init];
-
- input->flush_accumulated_events();
-}
-
-void OS_OSX::process_key_events() {
-
- Ref<InputEventKey> k;
- for (int i = 0; i < key_event_pos; i++) {
-
- const KeyEvent &ke = key_event_buffer[i];
-
- 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_keycode(ke.keycode);
- k->set_physical_keycode(ke.physical_keycode);
- k->set_unicode(ke.unicode);
-
- push_input(k);
- } else {
- // IME input
- if ((i == 0 && ke.keycode == 0) || (i > 0 && key_event_buffer[i - 1].keycode == 0)) {
- k.instance();
-
- get_key_modifier_state(ke.osx_state, k);
- k->set_pressed(ke.pressed);
- k->set_echo(ke.echo);
- k->set_keycode(0);
- k->set_physical_keycode(0);
- k->set_unicode(ke.unicode);
-
- push_input(k);
- }
- if (ke.keycode != 0) {
- k.instance();
-
- get_key_modifier_state(ke.osx_state, k);
- k->set_pressed(ke.pressed);
- k->set_echo(ke.echo);
- k->set_keycode(ke.keycode);
- k->set_physical_keycode(ke.physical_keycode);
-
- if (i + 1 < key_event_pos && key_event_buffer[i + 1].keycode == 0) {
- k->set_unicode(key_event_buffer[i + 1].unicode);
- }
-
- push_input(k);
- }
- }
- }
-
- key_event_pos = 0;
-}
-
-void OS_OSX::push_input(const Ref<InputEvent> &p_event) {
-
- Ref<InputEvent> ev = p_event;
- input->accumulate_input_event(ev);
-}
-
-void OS_OSX::force_process_input() {
-
- process_events(); // get rid of pending events
- joypad_osx->process_joypads();
-}
-
void OS_OSX::run() {
-
force_quit = false;
if (!main_loop)
@@ -2910,23 +317,12 @@ void OS_OSX::run() {
main_loop->init();
- if (zoomed) {
- zoomed = false;
- set_window_fullscreen(true);
- }
-
- //uint64_t last_ticks=get_ticks_usec();
-
- //int frames=0;
- //uint64_t frame=0;
-
bool quit = false;
-
while (!force_quit && !quit) {
-
@try {
-
- process_events(); // get rid of pending events
+ if (DisplayServer::get_singleton()) {
+ DisplayServer::get_singleton()->process_events(); // get rid of pending events
+ }
joypad_osx->process_joypads();
if (Main::iteration() == true) {
@@ -2936,41 +332,9 @@ void OS_OSX::run() {
ERR_PRINT("NSException: " + String([exception reason].UTF8String));
}
};
-
main_loop->finish();
}
-void OS_OSX::set_mouse_mode(MouseMode p_mode) {
-
- if (p_mode == mouse_mode)
- return;
-
- if (p_mode == MOUSE_MODE_CAPTURED) {
- // Apple Docs state that the display parameter is not used.
- // "This parameter is not used. By default, you may pass kCGDirectMainDisplay."
- // https://developer.apple.com/library/mac/documentation/graphicsimaging/reference/Quartz_Services_Ref/Reference/reference.html
- CGDisplayHideCursor(kCGDirectMainDisplay);
- CGAssociateMouseAndMouseCursorPosition(false);
- } else if (p_mode == MOUSE_MODE_HIDDEN) {
- CGDisplayHideCursor(kCGDirectMainDisplay);
- CGAssociateMouseAndMouseCursorPosition(true);
- } else {
- CGDisplayShowCursor(kCGDirectMainDisplay);
- CGAssociateMouseAndMouseCursorPosition(true);
- }
-
- mouse_mode = p_mode;
-}
-
-OS::MouseMode OS_OSX::get_mouse_mode() const {
-
- return mouse_mode;
-}
-
-String OS_OSX::get_joy_guid(int p_device) const {
- return input->get_joy_guid_remapped(p_device);
-}
-
Error OS_OSX::move_to_trash(const String &p_path) {
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *url = [NSURL fileURLWithPath:@(p_path.utf8().get_data())];
@@ -2984,123 +348,19 @@ Error OS_OSX::move_to_trash(const String &p_path) {
return OK;
}
-void OS_OSX::_set_use_vsync(bool p_enable) {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- if (context_gles2)
- context_gles2->set_use_vsync(p_enable);
- }
-#endif
-}
-
-OS_OSX *OS_OSX::singleton = NULL;
-
OS_OSX::OS_OSX() {
-
- memset(cursors, 0, sizeof(cursors));
- key_event_pos = 0;
- mouse_mode = OS::MOUSE_MODE_VISIBLE;
main_loop = NULL;
- singleton = this;
- im_active = false;
- im_position = Point2();
- layered_window = false;
- autoreleasePool = [[NSAutoreleasePool alloc] init];
-
- eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
- ERR_FAIL_COND(!eventSource);
-
- CGEventSourceSetLocalEventsSuppressionInterval(eventSource, 0.0);
-
- // Implicitly create shared NSApplication instance
- [GodotApplication sharedApplication];
-
- // In case we are unbundled, make us a proper UI application
- [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
-
- // Menu bar setup must go between sharedApplication above and
- // finishLaunching below, in order to properly emulate the behavior
- // of NSApplicationMain
- NSMenuItem *menu_item;
- NSString *title;
-
- NSString *nsappname = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"];
- if (nsappname == nil)
- nsappname = [[NSProcessInfo processInfo] processName];
-
- // Setup Apple menu
- NSMenu *apple_menu = [[NSMenu alloc] initWithTitle:@""];
- title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname];
- [apple_menu addItemWithTitle:title action:@selector(showAbout:) keyEquivalent:@""];
-
- [apple_menu addItem:[NSMenuItem separatorItem]];
-
- NSMenu *services = [[NSMenu alloc] initWithTitle:@""];
- menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Services", nil) action:nil keyEquivalent:@""];
- [apple_menu setSubmenu:services forItem:menu_item];
- [NSApp setServicesMenu:services];
- [services release];
-
- [apple_menu addItem:[NSMenuItem separatorItem]];
-
- title = [NSString stringWithFormat:NSLocalizedString(@"Hide %@", nil), nsappname];
- [apple_menu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
-
- menu_item = [apple_menu addItemWithTitle:NSLocalizedString(@"Hide Others", nil) action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
- [menu_item setKeyEquivalentModifierMask:(NSEventModifierFlagOption | NSEventModifierFlagCommand)];
-
- [apple_menu addItemWithTitle:NSLocalizedString(@"Show all", nil) action:@selector(unhideAllApplications:) keyEquivalent:@""];
-
- [apple_menu addItem:[NSMenuItem separatorItem]];
-
- title = [NSString stringWithFormat:NSLocalizedString(@"Quit %@", nil), nsappname];
- [apple_menu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
-
- // Setup menu bar
- NSMenu *main_menu = [[NSMenu alloc] initWithTitle:@""];
- menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""];
- [main_menu setSubmenu:apple_menu forItem:menu_item];
- [NSApp setMainMenu:main_menu];
-
- [main_menu release];
- [apple_menu release];
-
- [NSApp finishLaunching];
-
- delegate = [[GodotApplicationDelegate alloc] init];
- ERR_FAIL_COND(!delegate);
- [NSApp setDelegate:delegate];
-
- cursor_shape = CURSOR_ARROW;
-
- maximized = false;
- minimized = false;
- window_size = Vector2(1024, 600);
- zoomed = false;
- resizable = false;
- window_focused = true;
+ force_quit = false;
Vector<Logger *> loggers;
loggers.push_back(memnew(OSXTerminalLogger));
_set_logger(memnew(CompositeLogger(loggers)));
- //process application:openFile: event
- while (true) {
- NSEvent *event = [NSApp
- nextEventMatchingMask:NSEventMaskAny
- untilDate:[NSDate distantPast]
- inMode:NSDefaultRunLoopMode
- dequeue:YES];
-
- if (event == nil)
- break;
-
- [NSApp sendEvent:event];
- }
-
#ifdef COREAUDIO_ENABLED
AudioDriverManager::add_driver(&audio_driver);
#endif
+
+ DisplayServerOSX::register_osx_driver();
}
bool OS_OSX::_check_internal_feature_support(const String &p_feature) {
diff --git a/platform/osx/vulkan_context_osx.h b/platform/osx/vulkan_context_osx.h
index 619e91d1f6..09a5494ae8 100644
--- a/platform/osx/vulkan_context_osx.h
+++ b/platform/osx/vulkan_context_osx.h
@@ -39,7 +39,7 @@ class VulkanContextOSX : public VulkanContext {
virtual const char *_get_platform_surface_extension() const;
public:
- int window_create(id p_window, int p_width, int p_height);
+ Error window_create(DisplayServer::WindowID p_window_id, id p_window, int p_width, int p_height);
VulkanContextOSX();
~VulkanContextOSX();
diff --git a/platform/osx/vulkan_context_osx.mm b/platform/osx/vulkan_context_osx.mm
index c132bd334a..320401cdcb 100644
--- a/platform/osx/vulkan_context_osx.mm
+++ b/platform/osx/vulkan_context_osx.mm
@@ -35,7 +35,7 @@ const char *VulkanContextOSX::_get_platform_surface_extension() const {
return VK_MVK_MACOS_SURFACE_EXTENSION_NAME;
}
-int VulkanContextOSX::window_create(id p_window, int p_width, int p_height) {
+Error VulkanContextOSX::window_create(DisplayServer::WindowID p_window_id, id p_window, int p_width, int p_height) {
VkMacOSSurfaceCreateInfoMVK createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
@@ -45,8 +45,8 @@ int VulkanContextOSX::window_create(id p_window, int p_width, int p_height) {
VkSurfaceKHR surface;
VkResult err = vkCreateMacOSSurfaceMVK(_get_instance(), &createInfo, NULL, &surface);
- ERR_FAIL_COND_V(err, -1);
- return _window_create(surface, p_width, p_height);
+ ERR_FAIL_COND_V(err, ERR_CANT_CREATE);
+ return _window_create(p_window_id, surface, p_width, p_height);
}
VulkanContextOSX::VulkanContextOSX() {
diff --git a/platform/server/detect.py b/platform/server/detect.py
index db9ba8d036..4ea4bddddd 100644
--- a/platform/server/detect.py
+++ b/platform/server/detect.py
@@ -16,7 +16,7 @@ def get_name():
def get_program_suffix():
if (sys.platform == "darwin"):
return "osx"
- return "x11"
+ return "linuxbsd"
def can_build():
diff --git a/platform/server/os_server.h b/platform/server/os_server.h
index 7584293722..d40905718e 100644
--- a/platform/server/os_server.h
+++ b/platform/server/os_server.h
@@ -31,14 +31,14 @@
#ifndef OS_SERVER_H
#define OS_SERVER_H
+#include "core/input/input_filter.h"
#include "drivers/dummy/texture_loader_dummy.h"
#include "drivers/unix/os_unix.h"
-#include "main/input_default.h"
#ifdef __APPLE__
#include "platform/osx/crash_handler_osx.h"
#include "platform/osx/semaphore_osx.h"
#else
-#include "platform/x11/crash_handler_x11.h"
+#include "platform/x11/crash_handler_linuxbsd.h"
#endif
#include "servers/audio_server.h"
#include "servers/visual/rasterizer.h"
diff --git a/platform/uwp/joypad_uwp.h b/platform/uwp/joypad_uwp.h
index f2a721f3dd..054b67ddc8 100644
--- a/platform/uwp/joypad_uwp.h
+++ b/platform/uwp/joypad_uwp.h
@@ -31,7 +31,7 @@
#ifndef JOYPAD_UWP_H
#define JOYPAD_UWP_H
-#include "main/input_default.h"
+#include "core/input/input_filter.h"
ref class JoypadUWP sealed {
diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h
index ac6e0f3dd5..cc4dfcc79f 100644
--- a/platform/uwp/os_uwp.h
+++ b/platform/uwp/os_uwp.h
@@ -32,13 +32,12 @@
#define OS_UWP_H
#include "context_egl_uwp.h"
+#include "core/input/input_filter.h"
#include "core/math/transform_2d.h"
-#include "core/os/input.h"
#include "core/os/os.h"
#include "core/ustring.h"
#include "drivers/xaudio2/audio_driver_xaudio2.h"
#include "joypad_uwp.h"
-#include "main/input_default.h"
#include "servers/audio_server.h"
#include "servers/visual/rasterizer.h"
#include "servers/visual_server.h"
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index 8e94c7b35d..89ee4dfa7a 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -10,6 +10,7 @@ common_win = [
"godot_windows.cpp",
"crash_handler_windows.cpp",
"os_windows.cpp",
+ "display_server_windows.cpp",
"key_mapping_windows.cpp",
"joypad_windows.cpp",
"windows_terminal_logger.cpp",
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
new file mode 100644
index 0000000000..707337bc8d
--- /dev/null
+++ b/platform/windows/display_server_windows.cpp
@@ -0,0 +1,2968 @@
+#include "display_server_windows.h"
+#include "core/io/marshalls.h"
+#include "main/main.h"
+#include "os_windows.h"
+#include "scene/resources/texture.h"
+
+#include <avrt.h>
+
+#ifndef WM_POINTERUPDATE
+#define WM_POINTERUPDATE 0x0245
+#endif
+
+#ifdef DEBUG_ENABLED
+static String format_error_message(DWORD id) {
+
+ LPWSTR messageBuffer = NULL;
+ size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, NULL);
+
+ String msg = "Error " + itos(id) + ": " + String(messageBuffer, size);
+
+ LocalFree(messageBuffer);
+
+ return msg;
+}
+#endif // DEBUG_ENABLED
+
+bool DisplayServerWindows::has_feature(Feature p_feature) const {
+ switch (p_feature) {
+ case FEATURE_SUBWINDOWS:
+ case FEATURE_TOUCHSCREEN:
+ case FEATURE_MOUSE:
+ case FEATURE_MOUSE_WARP:
+ case FEATURE_CLIPBOARD:
+ case FEATURE_CURSOR_SHAPE:
+ case FEATURE_CUSTOM_CURSOR_SHAPE:
+ case FEATURE_CONSOLE_WINDOW:
+ case FEATURE_IME:
+ case FEATURE_WINDOW_TRANSPARENCY:
+ case FEATURE_HIDPI:
+ case FEATURE_ICON:
+ case FEATURE_NATIVE_ICON:
+ case FEATURE_SWAP_BUFFERS:
+ case FEATURE_KEEP_SCREEN_ON:
+ return true;
+ default:
+ return false;
+ }
+}
+
+String DisplayServerWindows::get_name() const {
+ return "Windows";
+}
+
+void DisplayServerWindows::alert(const String &p_alert, const String &p_title) {
+ MessageBoxW(NULL, p_alert.c_str(), p_title.c_str(), MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
+}
+
+void DisplayServerWindows::_set_mouse_mode_impl(MouseMode p_mode) {
+
+ if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED) {
+ WindowData &wd = windows[MAIN_WINDOW_ID];
+
+ RECT clipRect;
+ GetClientRect(wd.hWnd, &clipRect);
+ ClientToScreen(wd.hWnd, (POINT *)&clipRect.left);
+ ClientToScreen(wd.hWnd, (POINT *)&clipRect.right);
+ ClipCursor(&clipRect);
+ if (p_mode == MOUSE_MODE_CAPTURED) {
+
+ center = window_get_size() / 2;
+ POINT pos = { (int)center.x, (int)center.y };
+ ClientToScreen(wd.hWnd, &pos);
+ SetCursorPos(pos.x, pos.y);
+ SetCapture(wd.hWnd);
+ }
+ } else {
+ ReleaseCapture();
+ ClipCursor(NULL);
+ }
+
+ if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_HIDDEN) {
+ hCursor = SetCursor(NULL);
+ } else {
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ cursor_set_shape(c);
+ }
+}
+void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) {
+
+ _THREAD_SAFE_METHOD_
+
+ if (mouse_mode == p_mode)
+ return;
+
+ _set_mouse_mode_impl(p_mode);
+
+ mouse_mode = p_mode;
+}
+DisplayServer::MouseMode DisplayServerWindows::mouse_get_mode() const {
+ return mouse_mode;
+}
+
+void DisplayServerWindows::mouse_warp_to_position(const Point2i &p_to) {
+
+ _THREAD_SAFE_METHOD_
+
+ if (!windows.has(last_focused_window)) {
+ return; //no window focused?
+ }
+
+ if (mouse_mode == MOUSE_MODE_CAPTURED) {
+
+ old_x = p_to.x;
+ old_y = p_to.y;
+ } else {
+
+ POINT p;
+ p.x = p_to.x;
+ p.y = p_to.y;
+ ClientToScreen(windows[last_focused_window].hWnd, &p);
+
+ SetCursorPos(p.x, p.y);
+ }
+}
+Point2i DisplayServerWindows::mouse_get_position() const {
+ POINT p;
+ GetCursorPos(&p);
+ return Point2i(p.x, p.y);
+ //return Point2(old_x, old_y);
+}
+int DisplayServerWindows::mouse_get_button_state() const {
+ return last_button_state;
+}
+
+void DisplayServerWindows::clipboard_set(const String &p_text) {
+
+ _THREAD_SAFE_METHOD_
+
+ if (!windows.has(last_focused_window)) {
+ return; //no window focused?
+ }
+
+ // Convert LF line endings to CRLF in clipboard content
+ // Otherwise, line endings won't be visible when pasted in other software
+ String text = p_text.replace("\n", "\r\n");
+
+ if (!OpenClipboard(windows[last_focused_window].hWnd)) {
+ ERR_FAIL_MSG("Unable to open clipboard.");
+ }
+ EmptyClipboard();
+
+ HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (text.length() + 1) * sizeof(CharType));
+ ERR_FAIL_COND_MSG(mem == NULL, "Unable to allocate memory for clipboard contents.");
+
+ LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem);
+ memcpy(lptstrCopy, text.c_str(), (text.length() + 1) * sizeof(CharType));
+ GlobalUnlock(mem);
+
+ SetClipboardData(CF_UNICODETEXT, mem);
+
+ // set the CF_TEXT version (not needed?)
+ CharString utf8 = text.utf8();
+ mem = GlobalAlloc(GMEM_MOVEABLE, utf8.length() + 1);
+ ERR_FAIL_COND_MSG(mem == NULL, "Unable to allocate memory for clipboard contents.");
+
+ LPTSTR ptr = (LPTSTR)GlobalLock(mem);
+ memcpy(ptr, utf8.get_data(), utf8.length());
+ ptr[utf8.length()] = 0;
+ GlobalUnlock(mem);
+
+ SetClipboardData(CF_TEXT, mem);
+
+ CloseClipboard();
+}
+String DisplayServerWindows::clipboard_get() const {
+
+ _THREAD_SAFE_METHOD_
+
+ if (!windows.has(last_focused_window)) {
+ return String(); //no window focused?
+ }
+
+ String ret;
+ if (!OpenClipboard(windows[last_focused_window].hWnd)) {
+ ERR_FAIL_V_MSG("", "Unable to open clipboard.");
+ };
+
+ if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
+
+ HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
+ if (mem != NULL) {
+
+ LPWSTR ptr = (LPWSTR)GlobalLock(mem);
+ if (ptr != NULL) {
+
+ ret = String((CharType *)ptr);
+ GlobalUnlock(mem);
+ };
+ };
+
+ } else if (IsClipboardFormatAvailable(CF_TEXT)) {
+
+ HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
+ if (mem != NULL) {
+
+ LPTSTR ptr = (LPTSTR)GlobalLock(mem);
+ if (ptr != NULL) {
+
+ ret.parse_utf8((const char *)ptr);
+ GlobalUnlock(mem);
+ };
+ };
+ };
+
+ CloseClipboard();
+
+ return ret;
+}
+
+typedef struct {
+ int count;
+ int screen;
+ HMONITOR monitor;
+} EnumScreenData;
+
+static BOOL CALLBACK _MonitorEnumProcScreen(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+
+ EnumScreenData *data = (EnumScreenData *)dwData;
+ if (data->monitor == hMonitor) {
+ data->screen = data->count;
+ }
+
+ data->count++;
+ return TRUE;
+}
+
+static BOOL CALLBACK _MonitorEnumProcCount(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+
+ int *data = (int *)dwData;
+ (*data)++;
+ return TRUE;
+}
+
+int DisplayServerWindows::get_screen_count() const {
+ _THREAD_SAFE_METHOD_
+
+ int data = 0;
+ EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcCount, (LPARAM)&data);
+ return data;
+}
+
+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;
+ if (data->count == data->screen) {
+ data->pos.x = lprcMonitor->left;
+ data->pos.y = lprcMonitor->top;
+ }
+
+ data->count++;
+ return TRUE;
+}
+Point2i DisplayServerWindows::screen_get_position(int p_screen) const {
+
+ _THREAD_SAFE_METHOD_
+
+ EnumPosData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, Point2() };
+ EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcPos, (LPARAM)&data);
+ return data.pos;
+}
+
+typedef struct {
+ int count;
+ int screen;
+ Size2 size;
+} EnumSizeData;
+
+typedef struct {
+ int count;
+ int screen;
+ Rect2i rect;
+} EnumRectData;
+
+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;
+}
+
+Size2i DisplayServerWindows::screen_get_size(int p_screen) const {
+
+ _THREAD_SAFE_METHOD_
+
+ EnumSizeData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, Size2() };
+ EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcSize, (LPARAM)&data);
+ return data.size;
+}
+
+static BOOL CALLBACK _MonitorEnumProcUsableSize(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+
+ EnumRectData *data = (EnumRectData *)dwData;
+ if (data->count == data->screen) {
+ MONITORINFO minfo;
+ zeromem(&minfo, sizeof(MONITORINFO));
+ minfo.cbSize = sizeof(MONITORINFO);
+ GetMonitorInfoA(hMonitor, &minfo);
+
+ data->rect.position.x = minfo.rcWork.left;
+ data->rect.position.y = minfo.rcWork.top;
+ data->rect.size.x = minfo.rcWork.right - minfo.rcWork.left;
+ data->rect.size.y = minfo.rcWork.bottom - minfo.rcWork.top;
+ }
+
+ data->count++;
+ return TRUE;
+}
+
+Rect2i DisplayServerWindows::screen_get_usable_rect(int p_screen) const {
+
+ _THREAD_SAFE_METHOD_
+
+ EnumRectData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, Rect2i() };
+ EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcUsableSize, (LPARAM)&data);
+ return data.rect;
+}
+
+typedef struct {
+ int count;
+ int screen;
+ int dpi;
+} EnumDpiData;
+
+enum _MonitorDpiType {
+ MDT_Effective_DPI = 0,
+ MDT_Angular_DPI = 1,
+ MDT_Raw_DPI = 2,
+ MDT_Default = MDT_Effective_DPI
+};
+
+static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Default) {
+
+ int dpiX = 96, dpiY = 96;
+
+ static HMODULE Shcore = NULL;
+ typedef HRESULT(WINAPI * GetDPIForMonitor_t)(HMONITOR hmonitor, _MonitorDpiType dpiType, UINT * dpiX, UINT * dpiY);
+ static GetDPIForMonitor_t getDPIForMonitor = NULL;
+
+ if (Shcore == NULL) {
+ Shcore = LoadLibraryW(L"Shcore.dll");
+ getDPIForMonitor = Shcore ? (GetDPIForMonitor_t)GetProcAddress(Shcore, "GetDpiForMonitor") : NULL;
+
+ if ((Shcore == NULL) || (getDPIForMonitor == NULL)) {
+ if (Shcore)
+ FreeLibrary(Shcore);
+ Shcore = (HMODULE)INVALID_HANDLE_VALUE;
+ }
+ }
+
+ UINT x = 0, y = 0;
+ HRESULT hr = E_FAIL;
+ if (hmon && (Shcore != (HMODULE)INVALID_HANDLE_VALUE)) {
+ hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y);
+ if (SUCCEEDED(hr) && (x > 0) && (y > 0)) {
+
+ dpiX = (int)x;
+ dpiY = (int)y;
+ }
+ } else {
+ static int overallX = 0, overallY = 0;
+ if (overallX <= 0 || overallY <= 0) {
+ HDC hdc = GetDC(NULL);
+ if (hdc) {
+ overallX = GetDeviceCaps(hdc, LOGPIXELSX);
+ overallY = GetDeviceCaps(hdc, LOGPIXELSY);
+ ReleaseDC(NULL, hdc);
+ }
+ }
+ if (overallX > 0 && overallY > 0) {
+ dpiX = overallX;
+ dpiY = overallY;
+ }
+ }
+
+ return (dpiX + dpiY) / 2;
+}
+
+static BOOL CALLBACK _MonitorEnumProcDpi(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
+
+ EnumDpiData *data = (EnumDpiData *)dwData;
+ if (data->count == data->screen) {
+ data->dpi = QueryDpiForMonitor(hMonitor);
+ }
+
+ data->count++;
+ return TRUE;
+}
+
+int DisplayServerWindows::screen_get_dpi(int p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ EnumDpiData data = { 0, p_screen == SCREEN_OF_MAIN_WINDOW ? window_get_current_screen() : p_screen, 72 };
+ EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcDpi, (LPARAM)&data);
+ return data.dpi;
+}
+bool DisplayServerWindows::screen_is_touchscreen(int p_screen) const {
+#ifndef _MSC_VER
+#warning touchscreen not working
+#endif
+ return false;
+}
+
+void DisplayServerWindows::screen_set_orientation(ScreenOrientation p_orientation, int p_screen) {
+}
+DisplayServer::ScreenOrientation DisplayServerWindows::screen_get_orientation(int p_screen) const {
+ return SCREEN_LANDSCAPE;
+}
+
+void DisplayServerWindows::screen_set_keep_on(bool p_enable) {
+}
+bool DisplayServerWindows::screen_is_kept_on() const {
+ return false;
+}
+
+Vector<DisplayServer::WindowID> DisplayServerWindows::get_window_list() const {
+
+ _THREAD_SAFE_METHOD_
+
+ Vector<DisplayServer::WindowID> ret;
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ ret.push_back(E->key());
+ }
+ return ret;
+}
+
+DisplayServer::WindowID DisplayServerWindows::get_window_at_screen_position(const Point2i &p_position) const {
+
+ POINT p;
+ p.x = p_position.x;
+ p.y = p_position.y;
+ HWND hwnd = WindowFromPoint(p);
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (E->get().hWnd == hwnd) {
+ return E->key();
+ }
+ }
+
+ return INVALID_WINDOW_ID;
+}
+
+DisplayServer::WindowID DisplayServerWindows::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+
+ _THREAD_SAFE_METHOD_
+
+ WindowID window_id = _create_window(p_mode, p_flags, p_rect);
+
+ WindowData &wd = windows[window_id];
+
+ if (p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT) {
+ wd.resizable = false;
+ }
+ if (p_flags & WINDOW_FLAG_BORDERLESS_BIT) {
+ wd.borderless = true;
+ }
+ if (p_flags & WINDOW_FLAG_ALWAYS_ON_TOP_BIT && p_mode != WINDOW_MODE_FULLSCREEN) {
+ wd.always_on_top = true;
+ }
+ if (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) {
+ wd.no_focus = true;
+ }
+
+ _update_window_style(window_id);
+
+ ShowWindow(wd.hWnd, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT) ? SW_SHOWNOACTIVATE : SW_SHOW); // Show The Window
+ if (!(p_flags & WINDOW_FLAG_NO_FOCUS_BIT)) {
+ SetForegroundWindow(wd.hWnd); // Slightly Higher Priority
+ SetFocus(wd.hWnd); // Sets Keyboard Focus To
+ }
+
+ return window_id;
+}
+void DisplayServerWindows::delete_sub_window(WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ ERR_FAIL_COND_MSG(p_window == MAIN_WINDOW_ID, "Main window cannot be deleted.");
+
+ WindowData &wd = windows[p_window];
+
+ while (wd.transient_children.size()) {
+ window_set_transient(wd.transient_children.front()->get(), INVALID_WINDOW_ID);
+ }
+
+ if (wd.transient_parent != INVALID_WINDOW_ID) {
+ window_set_transient(p_window, INVALID_WINDOW_ID);
+ }
+
+#ifdef VULKAN_ENABLED
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_destroy(p_window);
+ }
+#endif
+
+ DestroyWindow(windows[p_window].hWnd);
+ windows.erase(p_window);
+}
+
+void DisplayServerWindows::window_attach_instance_id(ObjectID p_instance, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].instance_id = p_instance;
+}
+
+ObjectID DisplayServerWindows::window_get_attached_instance_id(WindowID p_window) const {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), ObjectID());
+ return windows[p_window].instance_id;
+}
+
+void DisplayServerWindows::window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].rect_changed_callback = p_callable;
+}
+
+void DisplayServerWindows::window_set_window_event_callback(const Callable &p_callable, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].event_callback = p_callable;
+}
+void DisplayServerWindows::window_set_input_event_callback(const Callable &p_callable, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].input_event_callback = p_callable;
+}
+void DisplayServerWindows::window_set_input_text_callback(const Callable &p_callable, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].input_text_callback = p_callable;
+}
+
+void DisplayServerWindows::window_set_drop_files_callback(const Callable &p_callable, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ windows[p_window].drop_files_callback = p_callable;
+}
+
+void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ SetWindowTextW(windows[p_window].hWnd, p_title.c_str());
+}
+
+int DisplayServerWindows::window_get_current_screen(WindowID p_window) const {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), -1);
+
+ EnumScreenData data = { 0, 0, MonitorFromWindow(windows[p_window].hWnd, MONITOR_DEFAULTTONEAREST) };
+ EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcScreen, (LPARAM)&data);
+ return data.screen;
+}
+void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ ERR_FAIL_INDEX(p_screen, get_screen_count());
+
+ Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window));
+ window_set_position(ofs + screen_get_position(p_screen), p_window);
+}
+
+Point2i DisplayServerWindows::window_get_position(WindowID p_window) const {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Point2i());
+ const WindowData &wd = windows[p_window];
+
+ if (wd.minimized) {
+ return wd.last_pos;
+ }
+
+ POINT point;
+ point.x = 0;
+ point.y = 0;
+
+ ClientToScreen(wd.hWnd, &point);
+
+ return Point2i(point.x, point.y);
+
+#if 0
+ //do not use this method, as it includes windows decorations
+ RECT r;
+ GetWindowRect(wd.hWnd, &r);
+ return Point2(r.left, r.top);
+#endif
+}
+void DisplayServerWindows::_update_real_mouse_position(WindowID p_window) {
+
+ POINT mouse_pos;
+ if (GetCursorPos(&mouse_pos) && ScreenToClient(windows[p_window].hWnd, &mouse_pos)) {
+ if (mouse_pos.x > 0 && mouse_pos.y > 0 && mouse_pos.x <= windows[p_window].width && mouse_pos.y <= windows[p_window].height) {
+ old_x = mouse_pos.x;
+ old_y = mouse_pos.y;
+ old_invalid = false;
+ InputFilter::get_singleton()->set_mouse_position(Point2i(mouse_pos.x, mouse_pos.y));
+ }
+ }
+}
+void DisplayServerWindows::window_set_position(const Point2i &p_position, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if (wd.fullscreen) return;
+#if 0
+ //wrong needs to account properly for decorations
+ RECT r;
+ GetWindowRect(wd.hWnd, &r);
+ MoveWindow(wd.hWnd, p_position.x, p_position.y, r.right - r.left, r.bottom - r.top, TRUE);
+#else
+
+ RECT rc;
+ rc.left = p_position.x;
+ rc.right = p_position.x + wd.width;
+ rc.bottom = p_position.y + wd.height;
+ rc.top = p_position.y;
+
+ const DWORD style = GetWindowLongPtr(wd.hWnd, GWL_STYLE);
+ const DWORD exStyle = GetWindowLongPtr(wd.hWnd, GWL_EXSTYLE);
+
+ AdjustWindowRectEx(&rc, style, false, exStyle);
+ MoveWindow(wd.hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, TRUE);
+#endif
+ // Don't let the mouse leave the window when moved
+ if (mouse_mode == MOUSE_MODE_CONFINED) {
+ RECT rect;
+ GetClientRect(wd.hWnd, &rect);
+ ClientToScreen(wd.hWnd, (POINT *)&rect.left);
+ ClientToScreen(wd.hWnd, (POINT *)&rect.right);
+ ClipCursor(&rect);
+ }
+
+ wd.last_pos = p_position;
+ _update_real_mouse_position(p_window);
+}
+
+void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_parent) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(p_window == p_parent);
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd_window = windows[p_window];
+
+ ERR_FAIL_COND(wd_window.transient_parent == p_parent);
+
+ ERR_FAIL_COND_MSG(wd_window.always_on_top, "Windows with the 'on top' can't become transient.");
+
+ if (p_parent == INVALID_WINDOW_ID) {
+ //remove transient
+
+ ERR_FAIL_COND(wd_window.transient_parent == INVALID_WINDOW_ID);
+ ERR_FAIL_COND(!windows.has(wd_window.transient_parent));
+
+ WindowData &wd_parent = windows[wd_window.transient_parent];
+
+ wd_window.transient_parent = INVALID_WINDOW_ID;
+ wd_parent.transient_children.erase(p_window);
+
+ SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, NULL);
+ } else {
+ ERR_FAIL_COND(!windows.has(p_parent));
+ ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent");
+ WindowData &wd_parent = windows[p_parent];
+
+ wd_window.transient_parent = p_parent;
+ wd_parent.transient_children.insert(p_window);
+
+ SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd);
+ }
+}
+
+void DisplayServerWindows::window_set_max_size(const Size2i p_size, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if ((p_size != Size2()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) {
+ ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
+ return;
+ }
+ wd.max_size = p_size;
+}
+Size2i DisplayServerWindows::window_get_max_size(WindowID p_window) const {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+ return wd.max_size;
+}
+
+void DisplayServerWindows::window_set_min_size(const Size2i p_size, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if ((p_size != Size2()) && (wd.max_size != Size2()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) {
+ ERR_PRINT("Minimum window size can't be larger than maximum window size!");
+ return;
+ }
+ wd.min_size = p_size;
+}
+Size2i DisplayServerWindows::window_get_min_size(WindowID p_window) const {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+ return wd.min_size;
+}
+
+void DisplayServerWindows::window_set_size(const Size2i p_size, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ int w = p_size.width;
+ int h = p_size.height;
+
+ wd.width = w;
+ wd.height = h;
+
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_resize(p_window, w, h);
+ }
+#endif
+
+ if (wd.fullscreen) {
+ return;
+ }
+
+ RECT rect;
+ GetWindowRect(wd.hWnd, &rect);
+
+ if (!wd.borderless) {
+ RECT crect;
+ GetClientRect(wd.hWnd, &crect);
+
+ w += (rect.right - rect.left) - (crect.right - crect.left);
+ h += (rect.bottom - rect.top) - (crect.bottom - crect.top);
+ }
+
+ MoveWindow(wd.hWnd, rect.left, rect.top, w, h, TRUE);
+
+ // Don't let the mouse leave the window when resizing to a smaller resolution
+ if (mouse_mode == MOUSE_MODE_CONFINED) {
+ RECT crect;
+ GetClientRect(wd.hWnd, &crect);
+ ClientToScreen(wd.hWnd, (POINT *)&crect.left);
+ ClientToScreen(wd.hWnd, (POINT *)&crect.right);
+ ClipCursor(&crect);
+ }
+}
+Size2i DisplayServerWindows::window_get_size(WindowID p_window) const {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+
+ if (wd.minimized) {
+ return Size2(wd.width, wd.height);
+ }
+
+ RECT r;
+ if (GetClientRect(wd.hWnd, &r)) { // Only area inside of window border
+ return Size2(r.right - r.left, r.bottom - r.top);
+ }
+ return Size2();
+}
+Size2i DisplayServerWindows::window_get_real_size(WindowID p_window) const {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), Size2i());
+ const WindowData &wd = windows[p_window];
+
+ RECT r;
+ if (GetWindowRect(wd.hWnd, &r)) { // Includes area of the window border
+ return Size2(r.right - r.left, r.bottom - r.top);
+ }
+ return Size2();
+}
+
+void DisplayServerWindows::_get_window_style(bool p_main_window, bool p_fullscreen, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex) {
+
+ r_style = 0;
+ r_style_ex = WS_EX_WINDOWEDGE;
+ if (p_main_window) {
+ r_style_ex |= WS_EX_APPWINDOW;
+ }
+
+ if (p_fullscreen || p_borderless) {
+ r_style |= WS_POPUP;
+ //if (p_borderless) {
+ // r_style_ex |= WS_EX_TOOLWINDOW;
+ //}
+ } else {
+
+ if (p_resizable) {
+ if (p_maximized) {
+ r_style = WS_OVERLAPPEDWINDOW | WS_MAXIMIZE;
+ } else {
+ r_style = WS_OVERLAPPEDWINDOW;
+ }
+ } else {
+ r_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;
+ }
+ }
+
+ if (p_no_activate_focus) {
+ r_style_ex |= WS_EX_TOPMOST | WS_EX_NOACTIVATE;
+ }
+ r_style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
+}
+
+void DisplayServerWindows::_update_window_style(WindowID p_window, bool p_repaint, bool p_maximized) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ DWORD style = 0;
+ DWORD style_ex = 0;
+
+ _get_window_style(p_window == MAIN_WINDOW_ID, wd.fullscreen, wd.borderless, wd.resizable, wd.maximized, wd.no_focus, style, style_ex);
+
+ SetWindowLongPtr(wd.hWnd, GWL_STYLE, style);
+ SetWindowLongPtr(wd.hWnd, GWL_EXSTYLE, style_ex);
+
+ SetWindowPos(wd.hWnd, wd.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | (wd.no_focus ? SWP_NOACTIVATE : 0));
+
+ if (p_repaint) {
+ RECT rect;
+ GetWindowRect(wd.hWnd, &rect);
+ MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
+ }
+}
+
+void DisplayServerWindows::window_set_mode(WindowMode p_mode, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if (wd.fullscreen && p_mode != WINDOW_MODE_FULLSCREEN) {
+
+ RECT rect;
+
+ wd.fullscreen = false;
+
+ if (wd.pre_fs_valid) {
+ rect = wd.pre_fs_rect;
+ } else {
+ rect.left = 0;
+ rect.right = wd.width;
+ rect.top = 0;
+ rect.bottom = wd.height;
+ }
+
+ _update_window_style(p_window, false, wd.was_maximized);
+
+ MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
+
+ wd.pre_fs_valid = true;
+ }
+
+ if (p_mode == WINDOW_MODE_MAXIMIZED) {
+
+ ShowWindow(wd.hWnd, SW_MAXIMIZE);
+ wd.maximized = true;
+ wd.minimized = false;
+ }
+
+ if (p_mode == WINDOW_MODE_WINDOWED) {
+
+ ShowWindow(wd.hWnd, SW_RESTORE);
+ wd.maximized = false;
+ wd.minimized = false;
+ }
+
+ if (p_mode == WINDOW_MODE_MINIMIZED) {
+
+ ShowWindow(wd.hWnd, SW_MINIMIZE);
+ wd.maximized = false;
+ wd.minimized = true;
+ }
+
+ if (p_mode == WINDOW_MODE_FULLSCREEN && !wd.fullscreen) {
+ if (wd.minimized) {
+ ShowWindow(wd.hWnd, SW_RESTORE);
+ }
+ wd.was_maximized = wd.maximized;
+
+ if (wd.pre_fs_valid) {
+ GetWindowRect(wd.hWnd, &wd.pre_fs_rect);
+ }
+
+ int cs = window_get_current_screen(p_window);
+ Point2 pos = screen_get_position(cs);
+ Size2 size = screen_get_size(cs);
+
+ wd.fullscreen = true;
+ wd.maximized = false;
+ wd.minimized = false;
+
+ _update_window_style(false);
+
+ MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE);
+ }
+}
+DisplayServer::WindowMode DisplayServerWindows::window_get_mode(WindowID p_window) const {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), WINDOW_MODE_WINDOWED);
+ const WindowData &wd = windows[p_window];
+
+ if (wd.fullscreen) {
+ return WINDOW_MODE_FULLSCREEN;
+ } else if (wd.minimized) {
+ return WINDOW_MODE_MINIMIZED;
+ } else if (wd.maximized) {
+ return WINDOW_MODE_MAXIMIZED;
+ } else {
+ return WINDOW_MODE_WINDOWED;
+ }
+}
+
+bool DisplayServerWindows::window_is_maximize_allowed(WindowID p_window) const {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), false);
+ const WindowData &wd = windows[p_window];
+
+ return true; //no idea
+}
+
+void DisplayServerWindows::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+ switch (p_flag) {
+ case WINDOW_FLAG_RESIZE_DISABLED: {
+
+ wd.resizable = !p_enabled;
+ _update_window_style(p_window);
+ } break;
+ case WINDOW_FLAG_BORDERLESS: {
+
+ wd.borderless = p_enabled;
+ _update_window_style(p_window);
+ } break;
+ case WINDOW_FLAG_ALWAYS_ON_TOP: {
+
+ ERR_FAIL_COND_MSG(wd.transient_parent != INVALID_WINDOW_ID && p_enabled, "Transient windows can't become on top");
+ wd.always_on_top = p_enabled;
+ _update_window_style(p_window);
+ } break;
+ case WINDOW_FLAG_TRANSPARENT: {
+
+ } break;
+ case WINDOW_FLAG_NO_FOCUS: {
+
+ wd.no_focus = p_enabled;
+ _update_window_style(p_window);
+ } break;
+ }
+}
+bool DisplayServerWindows::window_get_flag(WindowFlags p_flag, WindowID p_window) const {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), false);
+ const WindowData &wd = windows[p_window];
+ switch (p_flag) {
+ case WINDOW_FLAG_RESIZE_DISABLED: {
+
+ return !wd.resizable;
+ } break;
+ case WINDOW_FLAG_BORDERLESS: {
+
+ return wd.borderless;
+ } break;
+ case WINDOW_FLAG_ALWAYS_ON_TOP: {
+
+ return wd.always_on_top;
+ } break;
+ case WINDOW_FLAG_TRANSPARENT: {
+
+ } break;
+ }
+
+ return false;
+}
+
+void DisplayServerWindows::window_request_attention(WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ FLASHWINFO info;
+ info.cbSize = sizeof(FLASHWINFO);
+ info.hwnd = wd.hWnd;
+ info.dwFlags = FLASHW_TRAY;
+ info.dwTimeout = 0;
+ info.uCount = 2;
+ FlashWindowEx(&info);
+}
+void DisplayServerWindows::window_move_to_foreground(WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ SetForegroundWindow(wd.hWnd);
+}
+
+bool DisplayServerWindows::window_can_draw(WindowID p_window) const {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND_V(!windows.has(p_window), false);
+ const WindowData &wd = windows[p_window];
+ return wd.minimized;
+}
+
+bool DisplayServerWindows::can_any_window_draw() const {
+
+ _THREAD_SAFE_METHOD_
+
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (!E->get().minimized) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void DisplayServerWindows::window_set_ime_active(const bool p_active, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ if (p_active) {
+ ImmAssociateContext(wd.hWnd, wd.im_himc);
+
+ window_set_ime_position(wd.im_position, p_window);
+ } else {
+ ImmAssociateContext(wd.hWnd, (HIMC)0);
+ }
+}
+void DisplayServerWindows::window_set_ime_position(const Point2i &p_pos, WindowID p_window) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!windows.has(p_window));
+ WindowData &wd = windows[p_window];
+
+ wd.im_position = p_pos;
+
+ HIMC himc = ImmGetContext(wd.hWnd);
+ if (himc == (HIMC)0)
+ return;
+
+ COMPOSITIONFORM cps;
+ cps.dwStyle = CFS_FORCE_POSITION;
+ cps.ptCurrentPos.x = wd.im_position.x;
+ cps.ptCurrentPos.y = wd.im_position.y;
+ ImmSetCompositionWindow(himc, &cps);
+ ImmReleaseContext(wd.hWnd, himc);
+}
+
+void DisplayServerWindows::console_set_visible(bool p_enabled) {
+
+ _THREAD_SAFE_METHOD_
+
+ if (console_visible == p_enabled)
+ return;
+ ShowWindow(GetConsoleWindow(), p_enabled ? SW_SHOW : SW_HIDE);
+ console_visible = p_enabled;
+}
+bool DisplayServerWindows::is_console_visible() const {
+ return console_visible;
+}
+
+void DisplayServerWindows::cursor_set_shape(CursorShape p_shape) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
+
+ if (cursor_shape == p_shape)
+ return;
+
+ if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
+ cursor_shape = p_shape;
+ return;
+ }
+
+ static const LPCTSTR win_cursors[CURSOR_MAX] = {
+ IDC_ARROW,
+ IDC_IBEAM,
+ IDC_HAND, //finger
+ IDC_CROSS,
+ IDC_WAIT,
+ IDC_APPSTARTING,
+ IDC_ARROW,
+ IDC_ARROW,
+ IDC_NO,
+ IDC_SIZENS,
+ IDC_SIZEWE,
+ IDC_SIZENESW,
+ IDC_SIZENWSE,
+ IDC_SIZEALL,
+ IDC_SIZENS,
+ IDC_SIZEWE,
+ IDC_HELP
+ };
+
+ if (cursors[p_shape] != NULL) {
+ SetCursor(cursors[p_shape]);
+ } else {
+ SetCursor(LoadCursor(hInstance, win_cursors[p_shape]));
+ }
+
+ cursor_shape = p_shape;
+}
+DisplayServer::CursorShape DisplayServerWindows::cursor_get_shape() const {
+ return cursor_shape;
+}
+
+void DisplayServerWindows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap) {
+
+ // Get the system display DC
+ HDC hDC = GetDC(NULL);
+
+ // Create helper DC
+ HDC hMainDC = CreateCompatibleDC(hDC);
+ HDC hAndMaskDC = CreateCompatibleDC(hDC);
+ HDC hXorMaskDC = CreateCompatibleDC(hDC);
+
+ // Get the dimensions of the source bitmap
+ BITMAP bm;
+ GetObject(hSourceBitmap, sizeof(BITMAP), &bm);
+
+ // Create the mask bitmaps
+ hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color
+ hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color
+
+ // Release the system display DC
+ ReleaseDC(NULL, hDC);
+
+ // Select the bitmaps to helper DC
+ HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap);
+ HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap);
+ HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap);
+
+ // Assign the monochrome AND mask bitmap pixels so that a pixels of the source bitmap
+ // with 'clrTransparent' will be white pixels of the monochrome bitmap
+ SetBkColor(hMainDC, clrTransparent);
+ BitBlt(hAndMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCCOPY);
+
+ // Assign the color XOR mask bitmap pixels so that a pixels of the source bitmap
+ // with 'clrTransparent' will be black and rest the pixels same as corresponding
+ // pixels of the source bitmap
+ SetBkColor(hXorMaskDC, RGB(0, 0, 0));
+ SetTextColor(hXorMaskDC, RGB(255, 255, 255));
+ BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hAndMaskDC, 0, 0, SRCCOPY);
+ BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCAND);
+
+ // Deselect bitmaps from the helper DC
+ SelectObject(hMainDC, hOldMainBitmap);
+ SelectObject(hAndMaskDC, hOldAndMaskBitmap);
+ SelectObject(hXorMaskDC, hOldXorMaskBitmap);
+
+ // Delete the helper DC
+ DeleteDC(hXorMaskDC);
+ DeleteDC(hAndMaskDC);
+ DeleteDC(hMainDC);
+}
+
+void DisplayServerWindows::cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
+
+ _THREAD_SAFE_METHOD_
+
+ if (p_cursor.is_valid()) {
+
+ Map<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);
+
+ if (cursor_c) {
+ if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {
+ cursor_set_shape(p_shape);
+ return;
+ }
+
+ cursors_cache.erase(p_shape);
+ }
+
+ Ref<Texture2D> texture = p_cursor;
+ Ref<AtlasTexture> atlas_texture = p_cursor;
+ Ref<Image> image;
+ Size2 texture_size;
+ Rect2 atlas_rect;
+
+ if (texture.is_valid()) {
+ image = texture->get_data();
+ }
+
+ if (!image.is_valid() && atlas_texture.is_valid()) {
+ texture = atlas_texture->get_atlas();
+
+ atlas_rect.size.width = texture->get_width();
+ atlas_rect.size.height = texture->get_height();
+ atlas_rect.position.x = atlas_texture->get_region().position.x;
+ atlas_rect.position.y = atlas_texture->get_region().position.y;
+
+ texture_size.width = atlas_texture->get_region().size.x;
+ texture_size.height = atlas_texture->get_region().size.y;
+ } else if (image.is_valid()) {
+ texture_size.width = texture->get_width();
+ texture_size.height = texture->get_height();
+ }
+
+ ERR_FAIL_COND(!texture.is_valid());
+ ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
+ ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
+ ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
+
+ image = texture->get_data();
+
+ ERR_FAIL_COND(!image.is_valid());
+
+ UINT image_size = texture_size.width * texture_size.height;
+
+ // Create the BITMAP with alpha channel
+ COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size);
+
+ for (UINT index = 0; index < image_size; index++) {
+ int row_index = floor(index / texture_size.width) + atlas_rect.position.y;
+ int column_index = (index % int(texture_size.width)) + atlas_rect.position.x;
+
+ if (atlas_texture.is_valid()) {
+ column_index = MIN(column_index, atlas_rect.size.width - 1);
+ row_index = MIN(row_index, atlas_rect.size.height - 1);
+ }
+
+ *(buffer + index) = image->get_pixel(column_index, row_index).to_argb32();
+ }
+
+ // Using 4 channels, so 4 * 8 bits
+ HBITMAP bitmap = CreateBitmap(texture_size.width, texture_size.height, 1, 4 * 8, buffer);
+ COLORREF clrTransparent = -1;
+
+ // Create the AND and XOR masks for the bitmap
+ HBITMAP hAndMask = NULL;
+ HBITMAP hXorMask = NULL;
+
+ GetMaskBitmaps(bitmap, clrTransparent, hAndMask, hXorMask);
+
+ if (NULL == hAndMask || NULL == hXorMask) {
+ memfree(buffer);
+ DeleteObject(bitmap);
+ return;
+ }
+
+ // Finally, create the icon
+ ICONINFO iconinfo;
+ iconinfo.fIcon = FALSE;
+ iconinfo.xHotspot = p_hotspot.x;
+ iconinfo.yHotspot = p_hotspot.y;
+ iconinfo.hbmMask = hAndMask;
+ iconinfo.hbmColor = hXorMask;
+
+ if (cursors[p_shape])
+ DestroyIcon(cursors[p_shape]);
+
+ cursors[p_shape] = CreateIconIndirect(&iconinfo);
+
+ Vector<Variant> params;
+ params.push_back(p_cursor);
+ params.push_back(p_hotspot);
+ cursors_cache.insert(p_shape, params);
+
+ if (p_shape == cursor_shape) {
+ if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
+ SetCursor(cursors[p_shape]);
+ }
+ }
+
+ if (hAndMask != NULL) {
+ DeleteObject(hAndMask);
+ }
+
+ if (hXorMask != NULL) {
+ DeleteObject(hXorMask);
+ }
+
+ memfree(buffer);
+ DeleteObject(bitmap);
+ } else {
+ // Reset to default system cursor
+ if (cursors[p_shape]) {
+ DestroyIcon(cursors[p_shape]);
+ cursors[p_shape] = NULL;
+ }
+
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ cursor_set_shape(c);
+
+ cursors_cache.erase(p_shape);
+ }
+}
+
+bool DisplayServerWindows::get_swap_ok_cancel() {
+ return true;
+}
+
+void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) {
+ _THREAD_SAFE_METHOD_
+
+ AllowSetForegroundWindow(pid);
+}
+
+DisplayServer::LatinKeyboardVariant DisplayServerWindows::get_latin_keyboard_variant() const {
+
+ _THREAD_SAFE_METHOD_
+
+ unsigned long azerty[] = {
+ 0x00020401, // Arabic (102) AZERTY
+ 0x0001080c, // Belgian (Comma)
+ 0x0000080c, // Belgian French
+ 0x0000040c, // French
+ 0 // <--- STOP MARK
+ };
+ unsigned long qwertz[] = {
+ 0x0000041a, // Croation
+ 0x00000405, // Czech
+ 0x00000407, // German
+ 0x00010407, // German (IBM)
+ 0x0000040e, // Hungarian
+ 0x0000046e, // Luxembourgish
+ 0x00010415, // Polish (214)
+ 0x00000418, // Romanian (Legacy)
+ 0x0000081a, // Serbian (Latin)
+ 0x0000041b, // Slovak
+ 0x00000424, // Slovenian
+ 0x0001042e, // Sorbian Extended
+ 0x0002042e, // Sorbian Standard
+ 0x0000042e, // Sorbian Standard (Legacy)
+ 0x0000100c, // Swiss French
+ 0x00000807, // Swiss German
+ 0 // <--- STOP MARK
+ };
+ unsigned long dvorak[] = {
+ 0x00010409, // US-Dvorak
+ 0x00030409, // US-Dvorak for left hand
+ 0x00040409, // US-Dvorak for right hand
+ 0 // <--- STOP MARK
+ };
+
+ char name[KL_NAMELENGTH + 1];
+ name[0] = 0;
+ GetKeyboardLayoutNameA(name);
+
+ unsigned long hex = strtoul(name, NULL, 16);
+
+ int i = 0;
+ while (azerty[i] != 0) {
+ if (azerty[i] == hex) return LATIN_KEYBOARD_AZERTY;
+ i++;
+ }
+
+ i = 0;
+ while (qwertz[i] != 0) {
+ if (qwertz[i] == hex) return LATIN_KEYBOARD_QWERTZ;
+ i++;
+ }
+
+ i = 0;
+ while (dvorak[i] != 0) {
+ if (dvorak[i] == hex) return LATIN_KEYBOARD_DVORAK;
+ i++;
+ }
+
+ return LATIN_KEYBOARD_QWERTY;
+}
+
+void DisplayServerWindows::process_events() {
+
+ _THREAD_SAFE_METHOD_
+
+ MSG msg;
+
+ if (!drop_events) {
+ joypad->process_joypads();
+ }
+
+ while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
+
+ TranslateMessage(&msg);
+ DispatchMessageW(&msg);
+ }
+
+ if (!drop_events) {
+ _process_key_events();
+ InputFilter::get_singleton()->flush_accumulated_events();
+ }
+}
+
+void DisplayServerWindows::force_process_and_drop_events() {
+
+ _THREAD_SAFE_METHOD_
+
+ drop_events = true;
+ process_events();
+ drop_events = false;
+}
+
+void DisplayServerWindows::release_rendering_thread() {
+}
+void DisplayServerWindows::make_rendering_thread() {
+}
+void DisplayServerWindows::swap_buffers() {
+}
+
+void DisplayServerWindows::set_native_icon(const String &p_filename) {
+
+ _THREAD_SAFE_METHOD_
+
+ FileAccess *f = FileAccess::open(p_filename, FileAccess::READ);
+ ERR_FAIL_COND_MSG(!f, "Cannot open file with icon '" + p_filename + "'.");
+
+ 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);
+
+ ERR_FAIL_COND_MSG(icon_dir->idType != 1, "Invalid icon file format!");
+
+ 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;
+ }
+ }
+ }
+
+ ERR_FAIL_COND_MSG(big_icon_index == -1, "No valid icons found!");
+
+ if (small_icon_index == -1) {
+ WARN_PRINT("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);
+ ERR_FAIL_COND_MSG(!icon_big, "Could not create " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
+
+ // 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);
+ ERR_FAIL_COND_MSG(!icon_small, "Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
+
+ // Online tradition says to be sure last error is cleared and set the small icon first
+ int err = 0;
+ SetLastError(err);
+
+ SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)icon_small);
+ err = GetLastError();
+ ERR_FAIL_COND_MSG(err, "Error setting ICON_SMALL: " + format_error_message(err) + ".");
+
+ SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);
+ err = GetLastError();
+ ERR_FAIL_COND_MSG(err, "Error setting ICON_BIG: " + format_error_message(err) + ".");
+
+ memdelete(f);
+ memdelete(icon_dir);
+}
+void DisplayServerWindows::set_icon(const Ref<Image> &p_icon) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_COND(!p_icon.is_valid());
+ Ref<Image> icon = p_icon->duplicate();
+ if (icon->get_format() != Image::FORMAT_RGBA8)
+ icon->convert(Image::FORMAT_RGBA8);
+ int w = icon->get_width();
+ int h = icon->get_height();
+
+ /* Create temporary bitmap buffer */
+ int icon_len = 40 + h * w * 4;
+ Vector<BYTE> v;
+ v.resize(icon_len);
+ BYTE *icon_bmp = v.ptrw();
+
+ encode_uint32(40, &icon_bmp[0]);
+ encode_uint32(w, &icon_bmp[4]);
+ encode_uint32(h * 2, &icon_bmp[8]);
+ encode_uint16(1, &icon_bmp[12]);
+ encode_uint16(32, &icon_bmp[14]);
+ encode_uint32(BI_RGB, &icon_bmp[16]);
+ encode_uint32(w * h * 4, &icon_bmp[20]);
+ encode_uint32(0, &icon_bmp[24]);
+ encode_uint32(0, &icon_bmp[28]);
+ encode_uint32(0, &icon_bmp[32]);
+ encode_uint32(0, &icon_bmp[36]);
+
+ uint8_t *wr = &icon_bmp[40];
+ const uint8_t *r = icon->get_data().ptr();
+
+ for (int i = 0; i < h; i++) {
+
+ for (int j = 0; j < w; j++) {
+
+ const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];
+ uint8_t *wpx = &wr[(i * w + j) * 4];
+ wpx[0] = rpx[2];
+ wpx[1] = rpx[1];
+ wpx[2] = rpx[0];
+ wpx[3] = rpx[3];
+ }
+ }
+
+ HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
+
+ /* Set the icon for the window */
+ SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon);
+
+ /* Set the icon in the task manager (should we do this?) */
+ SendMessage(windows[MAIN_WINDOW_ID].hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon);
+}
+
+void DisplayServerWindows::vsync_set_use_via_compositor(bool p_enable) {
+}
+bool DisplayServerWindows::vsync_is_using_via_compositor() const {
+ return false;
+}
+
+void DisplayServerWindows::set_context(Context p_context) {
+}
+
+#define MI_WP_SIGNATURE 0xFF515700
+#define SIGNATURE_MASK 0xFFFFFF00
+// Keeping the name suggested by Microsoft, but this macro really answers:
+// Is this mouse event emulated from touch or pen input?
+#define IsPenEvent(dw) (((dw)&SIGNATURE_MASK) == MI_WP_SIGNATURE)
+// This one tells whether the event comes from touchscreen (and not from pen)
+#define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw)&0x80))
+
+void DisplayServerWindows::_touch_event(WindowID p_window, bool p_pressed, float p_x, float p_y, int idx) {
+
+ // Defensive
+ if (touch_state.has(idx) == p_pressed)
+ return;
+
+ if (p_pressed) {
+ touch_state.insert(idx, Vector2(p_x, p_y));
+ } else {
+ touch_state.erase(idx);
+ }
+
+ Ref<InputEventScreenTouch> event;
+ event.instance();
+ event->set_index(idx);
+ event->set_window_id(p_window);
+ event->set_pressed(p_pressed);
+ event->set_position(Vector2(p_x, p_y));
+
+ InputFilter::get_singleton()->accumulate_input_event(event);
+}
+
+void DisplayServerWindows::_drag_event(WindowID p_window, float p_x, float p_y, int idx) {
+
+ Map<int, Vector2>::Element *curr = touch_state.find(idx);
+ // Defensive
+ if (!curr)
+ return;
+
+ if (curr->get() == Vector2(p_x, p_y))
+ return;
+
+ Ref<InputEventScreenDrag> event;
+ event.instance();
+ event->set_window_id(p_window);
+ event->set_index(idx);
+ event->set_position(Vector2(p_x, p_y));
+ event->set_relative(Vector2(p_x, p_y) - curr->get());
+
+ InputFilter::get_singleton()->accumulate_input_event(event);
+
+ curr->get() = Vector2(p_x, p_y);
+}
+
+void DisplayServerWindows::_send_window_event(const WindowData &wd, WindowEvent p_event) {
+
+ if (!wd.event_callback.is_null()) {
+ Variant event = int(p_event);
+ Variant *eventp = &event;
+ Variant ret;
+ Callable::CallError ce;
+ wd.event_callback.call((const Variant **)&eventp, 1, ret, ce);
+ }
+}
+
+void DisplayServerWindows::_dispatch_input_events(const Ref<InputEvent> &p_event) {
+ ((DisplayServerWindows *)(get_singleton()))->_dispatch_input_event(p_event);
+}
+
+void DisplayServerWindows::_dispatch_input_event(const Ref<InputEvent> &p_event) {
+
+ Variant ev = p_event;
+ Variant *evp = &ev;
+ Variant ret;
+ Callable::CallError ce;
+
+ Ref<InputEventFromWindow> event_from_window = p_event;
+ if (event_from_window.is_valid() && event_from_window->get_window_id() != INVALID_WINDOW_ID) {
+ //send to a window
+ ERR_FAIL_COND(!windows.has(event_from_window->get_window_id()));
+ Callable callable = windows[event_from_window->get_window_id()].input_event_callback;
+ if (callable.is_null()) {
+ return;
+ }
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ } else {
+ //send to all windows
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ Callable callable = E->get().input_event_callback;
+ if (callable.is_null()) {
+ continue;
+ }
+ callable.call((const Variant **)&evp, 1, ret, ce);
+ }
+ }
+}
+
+LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+
+ if (drop_events) {
+
+ if (user_proc) {
+
+ return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
+ } else {
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+ }
+ };
+
+ WindowID window_id = INVALID_WINDOW_ID;
+
+ for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
+ if (E->get().hWnd == hWnd) {
+ window_id = E->key();
+ break;
+ }
+ }
+
+ switch (uMsg) // Check For Windows Messages
+ {
+ case WM_SETFOCUS: {
+
+ windows[window_id].window_has_focus = true;
+ last_focused_window = window_id;
+
+ // Restore mouse mode
+ _set_mouse_mode_impl(mouse_mode);
+
+ break;
+ }
+ case WM_KILLFOCUS: {
+ windows[window_id].window_has_focus = false;
+ last_focused_window = window_id;
+
+ // Release capture unconditionally because it can be set due to dragging, in addition to captured mode
+ ReleaseCapture();
+
+ // Release every touch to avoid sticky points
+ for (Map<int, Vector2>::Element *E = touch_state.front(); E; E = E->next()) {
+ _touch_event(window_id, false, E->get().x, E->get().y, E->key());
+ }
+ touch_state.clear();
+
+ break;
+ }
+ case WM_ACTIVATE: // Watch For Window Activate Message
+ {
+ windows[window_id].minimized = HIWORD(wParam) != 0;
+
+ if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) {
+
+ _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_IN);
+ windows[window_id].window_focused = true;
+ alt_mem = false;
+ control_mem = false;
+ shift_mem = false;
+ } else { // WM_INACTIVE
+ InputFilter::get_singleton()->release_pressed_events();
+ _send_window_event(windows[window_id], WINDOW_EVENT_FOCUS_OUT);
+ windows[window_id].window_focused = false;
+ alt_mem = false;
+ };
+
+ return 0; // Return To The Message Loop
+ }
+ case WM_GETMINMAXINFO: {
+ if (windows[window_id].resizable && !windows[window_id].fullscreen) {
+ Size2 decor = window_get_size(window_id) - window_get_real_size(window_id); // Size of window decorations
+ MINMAXINFO *min_max_info = (MINMAXINFO *)lParam;
+ if (windows[window_id].min_size != Size2()) {
+ min_max_info->ptMinTrackSize.x = windows[window_id].min_size.x + decor.x;
+ min_max_info->ptMinTrackSize.y = windows[window_id].min_size.y + decor.y;
+ }
+ if (windows[window_id].max_size != Size2()) {
+ min_max_info->ptMaxTrackSize.x = windows[window_id].max_size.x + decor.x;
+ min_max_info->ptMaxTrackSize.y = windows[window_id].max_size.y + decor.y;
+ }
+ return 0;
+ } else {
+ break;
+ }
+ }
+ case WM_PAINT:
+
+ Main::force_redraw();
+ break;
+
+ case WM_SYSCOMMAND: // Intercept System Commands
+ {
+ switch (wParam) // Check System Calls
+ {
+ case SC_SCREENSAVE: // Screensaver Trying To Start?
+ case SC_MONITORPOWER: // Monitor Trying To Enter Powersave?
+ return 0; // Prevent From Happening
+ case SC_KEYMENU:
+ if ((lParam >> 16) <= 0)
+ return 0;
+ }
+ break; // Exit
+ }
+
+ case WM_CLOSE: // Did We Receive A Close Message?
+ {
+
+ _send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);
+
+ //force_quit=true;
+ return 0; // Jump Back
+ }
+ case WM_MOUSELEAVE: {
+
+ old_invalid = true;
+ outside = true;
+
+ _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_EXIT);
+
+ } break;
+ case WM_INPUT: {
+ if (mouse_mode != MOUSE_MODE_CAPTURED || !use_raw_input) {
+ break;
+ }
+
+ UINT dwSize;
+
+ GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
+ LPBYTE lpb = new BYTE[dwSize];
+ if (lpb == NULL) {
+ return 0;
+ }
+
+ if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize)
+ OutputDebugString(TEXT("GetRawInputData does not return correct size !\n"));
+
+ RAWINPUT *raw = (RAWINPUT *)lpb;
+
+ if (raw->header.dwType == RIM_TYPEMOUSE) {
+ Ref<InputEventMouseMotion> mm;
+ mm.instance();
+
+ mm->set_window_id(window_id);
+ mm->set_control(control_mem);
+ mm->set_shift(shift_mem);
+ mm->set_alt(alt_mem);
+
+ mm->set_button_mask(last_button_state);
+
+ Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);
+
+ // centering just so it works as before
+ POINT pos = { (int)c.x, (int)c.y };
+ ClientToScreen(windows[window_id].hWnd, &pos);
+ SetCursorPos(pos.x, pos.y);
+
+ mm->set_position(c);
+ mm->set_global_position(c);
+ InputFilter::get_singleton()->set_mouse_position(c);
+ mm->set_speed(Vector2(0, 0));
+
+ if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) {
+ mm->set_relative(Vector2(raw->data.mouse.lLastX, raw->data.mouse.lLastY));
+
+ } else if (raw->data.mouse.usFlags == MOUSE_MOVE_ABSOLUTE) {
+
+ int nScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ int nScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
+ int nScreenLeft = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ int nScreenTop = GetSystemMetrics(SM_YVIRTUALSCREEN);
+
+ Vector2 abs_pos(
+ (double(raw->data.mouse.lLastX) - 65536.0 / (nScreenWidth)) * nScreenWidth / 65536.0 + nScreenLeft,
+ (double(raw->data.mouse.lLastY) - 65536.0 / (nScreenHeight)) * nScreenHeight / 65536.0 + nScreenTop);
+
+ POINT coords; //client coords
+ coords.x = abs_pos.x;
+ coords.y = abs_pos.y;
+
+ ScreenToClient(hWnd, &coords);
+
+ mm->set_relative(Vector2(coords.x - old_x, coords.y - old_y));
+ old_x = coords.x;
+ old_y = coords.y;
+
+ /*Input.mi.dx = (int)((((double)(pos.x)-nScreenLeft) * 65536) / nScreenWidth + 65536 / (nScreenWidth));
+ Input.mi.dy = (int)((((double)(pos.y)-nScreenTop) * 65536) / nScreenHeight + 65536 / (nScreenHeight));
+ */
+ }
+
+ if (windows[window_id].window_has_focus && mm->get_relative() != Vector2())
+ InputFilter::get_singleton()->accumulate_input_event(mm);
+ }
+ delete[] lpb;
+ } break;
+ case WM_POINTERUPDATE: {
+ if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
+ break;
+ }
+
+ if (!win8p_GetPointerType || !win8p_GetPointerPenInfo) {
+ break;
+ }
+
+ uint32_t pointer_id = LOWORD(wParam);
+ POINTER_INPUT_TYPE pointer_type = PT_POINTER;
+ if (!win8p_GetPointerType(pointer_id, &pointer_type)) {
+ break;
+ }
+
+ if (pointer_type != PT_PEN) {
+ break;
+ }
+
+ POINTER_PEN_INFO pen_info;
+ if (!win8p_GetPointerPenInfo(pointer_id, &pen_info)) {
+ break;
+ }
+
+ if (InputFilter::get_singleton()->is_emulating_mouse_from_touch()) {
+ // Universal translation enabled; ignore OS translation
+ LPARAM extra = GetMessageExtraInfo();
+ if (IsTouchEvent(extra)) {
+ break;
+ }
+ }
+
+ if (outside) {
+ //mouse enter
+
+ if (mouse_mode != MOUSE_MODE_CAPTURED) {
+ _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
+ }
+
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ cursor_set_shape(c);
+ outside = false;
+
+ //Once-Off notification, must call again....
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = hWnd;
+ tme.dwHoverTime = HOVER_DEFAULT;
+ TrackMouseEvent(&tme);
+ }
+
+ // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
+ if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED)
+ break;
+
+ Ref<InputEventMouseMotion> mm;
+ mm.instance();
+
+ mm->set_window_id(window_id);
+ mm->set_pressure(pen_info.pressure ? (float)pen_info.pressure / 1024 : 0);
+ mm->set_tilt(Vector2(pen_info.tiltX ? (float)pen_info.tiltX / 90 : 0, pen_info.tiltY ? (float)pen_info.tiltY / 90 : 0));
+
+ mm->set_control((wParam & MK_CONTROL) != 0);
+ mm->set_shift((wParam & MK_SHIFT) != 0);
+ mm->set_alt(alt_mem);
+
+ mm->set_button_mask(last_button_state);
+
+ POINT coords; //client coords
+ coords.x = GET_X_LPARAM(lParam);
+ coords.y = GET_Y_LPARAM(lParam);
+
+ ScreenToClient(windows[window_id].hWnd, &coords);
+
+ mm->set_position(Vector2(coords.x, coords.y));
+ mm->set_global_position(Vector2(coords.x, coords.y));
+
+ if (mouse_mode == MOUSE_MODE_CAPTURED) {
+
+ Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);
+ old_x = c.x;
+ old_y = c.y;
+
+ if (mm->get_position() == c) {
+ center = c;
+ return 0;
+ }
+
+ Point2i ncenter = mm->get_position();
+ center = ncenter;
+ POINT pos = { (int)c.x, (int)c.y };
+ ClientToScreen(hWnd, &pos);
+ SetCursorPos(pos.x, pos.y);
+ }
+
+ InputFilter::get_singleton()->set_mouse_position(mm->get_position());
+ mm->set_speed(InputFilter::get_singleton()->get_last_mouse_speed());
+
+ if (old_invalid) {
+
+ old_x = mm->get_position().x;
+ old_y = mm->get_position().y;
+ old_invalid = false;
+ }
+
+ mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
+ old_x = mm->get_position().x;
+ old_y = mm->get_position().y;
+ if (windows[window_id].window_has_focus) {
+ InputFilter::get_singleton()->parse_input_event(mm);
+ }
+
+ return 0; //Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event
+ } break;
+ case WM_MOUSEMOVE: {
+ if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
+ break;
+ }
+
+ if (InputFilter::get_singleton()->is_emulating_mouse_from_touch()) {
+ // Universal translation enabled; ignore OS translation
+ LPARAM extra = GetMessageExtraInfo();
+ if (IsTouchEvent(extra)) {
+ break;
+ }
+ }
+
+ if (outside) {
+ //mouse enter
+
+ if (mouse_mode != MOUSE_MODE_CAPTURED) {
+ _send_window_event(windows[window_id], WINDOW_EVENT_MOUSE_ENTER);
+ }
+
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ cursor_set_shape(c);
+ outside = false;
+
+ //Once-Off notification, must call again....
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = hWnd;
+ tme.dwHoverTime = HOVER_DEFAULT;
+ TrackMouseEvent(&tme);
+ }
+
+ // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
+ if (!windows[window_id].window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED)
+ break;
+
+ Ref<InputEventMouseMotion> mm;
+ mm.instance();
+ mm->set_window_id(window_id);
+ mm->set_control((wParam & MK_CONTROL) != 0);
+ mm->set_shift((wParam & MK_SHIFT) != 0);
+ mm->set_alt(alt_mem);
+
+ mm->set_button_mask(last_button_state);
+
+ 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)));
+
+ if (mouse_mode == MOUSE_MODE_CAPTURED) {
+
+ Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);
+ old_x = c.x;
+ old_y = c.y;
+
+ if (mm->get_position() == c) {
+ center = c;
+ return 0;
+ }
+
+ Point2i ncenter = mm->get_position();
+ center = ncenter;
+ POINT pos = { (int)c.x, (int)c.y };
+ ClientToScreen(windows[window_id].hWnd, &pos);
+ SetCursorPos(pos.x, pos.y);
+ }
+
+ InputFilter::get_singleton()->set_mouse_position(mm->get_position());
+ mm->set_speed(InputFilter::get_singleton()->get_last_mouse_speed());
+
+ if (old_invalid) {
+
+ old_x = mm->get_position().x;
+ old_y = mm->get_position().y;
+ old_invalid = false;
+ }
+
+ mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
+ old_x = mm->get_position().x;
+ old_y = mm->get_position().y;
+ if (windows[window_id].window_has_focus)
+ InputFilter::get_singleton()->accumulate_input_event(mm);
+
+ } break;
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONUP:
+ if (InputFilter::get_singleton()->is_emulating_mouse_from_touch()) {
+ // Universal translation enabled; ignore OS translations for left button
+ LPARAM extra = GetMessageExtraInfo();
+ if (IsTouchEvent(extra)) {
+ break;
+ }
+ }
+ [[fallthrough]];
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONUP:
+ case WM_MOUSEWHEEL:
+ case WM_MOUSEHWHEEL:
+ case WM_LBUTTONDBLCLK:
+ case WM_MBUTTONDBLCLK:
+ case WM_RBUTTONDBLCLK:
+ case WM_XBUTTONDBLCLK:
+ case WM_XBUTTONDOWN:
+ case WM_XBUTTONUP: {
+
+ Ref<InputEventMouseButton> mb;
+ mb.instance();
+ mb->set_window_id(window_id);
+
+ 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->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_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_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;
+ }
+ }
+
+ 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;
+ if (mb->is_pressed())
+ last_button_state |= (1 << (mb->get_button_index() - 1));
+ else
+ last_button_state &= ~(1 << (mb->get_button_index() - 1));
+ mb->set_button_mask(last_button_state);
+
+ mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
+
+ if (mouse_mode == MOUSE_MODE_CAPTURED && !use_raw_input) {
+
+ mb->set_position(Vector2(old_x, old_y));
+ }
+
+ if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) {
+ if (mb->is_pressed()) {
+
+ if (++pressrc > 0 && mouse_mode != MOUSE_MODE_CAPTURED)
+ SetCapture(hWnd);
+ } else {
+
+ if (--pressrc <= 0) {
+ if (mouse_mode != MOUSE_MODE_CAPTURED) {
+ ReleaseCapture();
+ }
+ pressrc = 0;
+ }
+ }
+ } else {
+ // for reasons unknown to mankind, wheel comes in screen coordinates
+ 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));
+ }
+
+ mb->set_global_position(mb->get_position());
+
+ InputFilter::get_singleton()->accumulate_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_window_id(window_id);
+ last_button_state &= ~(1 << (mbd->get_button_index() - 1));
+ mbd->set_button_mask(last_button_state);
+ mbd->set_pressed(false);
+ InputFilter::get_singleton()->accumulate_input_event(mbd);
+ }
+
+ } break;
+
+ case WM_MOVE: {
+ if (!IsIconic(windows[window_id].hWnd)) {
+ int x = int16_t(LOWORD(lParam));
+ int y = int16_t(HIWORD(lParam));
+ windows[window_id].last_pos = Point2(x, y);
+
+ if (!windows[window_id].rect_changed_callback.is_null()) {
+
+ Variant size = Rect2i(windows[window_id].last_pos.x, windows[window_id].last_pos.y, windows[window_id].width, windows[window_id].height);
+ Variant *sizep = &size;
+ Variant ret;
+ Callable::CallError ce;
+ windows[window_id].rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce);
+ }
+ }
+ } break;
+
+ case WM_SIZE: {
+ // Ignore size when a SIZE_MINIMIZED event is triggered
+ if (wParam != SIZE_MINIMIZED) {
+ int window_w = LOWORD(lParam);
+ int window_h = HIWORD(lParam);
+ if (window_w > 0 && window_h > 0 && !windows[window_id].preserve_window_size) {
+ windows[window_id].width = window_w;
+ windows[window_id].height = window_h;
+
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_resize(window_id, windows[window_id].width, windows[window_id].height);
+ }
+#endif
+
+ } else {
+ windows[window_id].preserve_window_size = false;
+ window_set_size(Size2(windows[window_id].width, windows[window_id].height), window_id);
+ }
+ }
+
+ if (!windows[window_id].rect_changed_callback.is_null()) {
+
+ Variant size = Rect2i(windows[window_id].last_pos.x, windows[window_id].last_pos.y, windows[window_id].width, windows[window_id].height);
+ Variant *sizep = &size;
+ Variant ret;
+ Callable::CallError ce;
+ windows[window_id].rect_changed_callback.call((const Variant **)&sizep, 1, ret, ce);
+ }
+
+ if (wParam == SIZE_MAXIMIZED) {
+ windows[window_id].maximized = true;
+ windows[window_id].minimized = false;
+ } else if (wParam == SIZE_MINIMIZED) {
+ windows[window_id].maximized = false;
+ windows[window_id].minimized = true;
+ } else if (wParam == SIZE_RESTORED) {
+ windows[window_id].maximized = false;
+ windows[window_id].minimized = false;
+ }
+#if 0
+ if (is_layered_allowed() && layered_window) {
+ DeleteObject(hBitmap);
+
+ RECT r;
+ GetWindowRect(hWnd, &r);
+ dib_size = Size2i(r.right - r.left, r.bottom - r.top);
+
+ BITMAPINFO bmi;
+ ZeroMemory(&bmi, sizeof(BITMAPINFO));
+ bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bmi.bmiHeader.biWidth = dib_size.x;
+ bmi.bmiHeader.biHeight = dib_size.y;
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = 32;
+ bmi.bmiHeader.biCompression = BI_RGB;
+ bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4;
+ hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, NULL, 0x0);
+ SelectObject(hDC_dib, hBitmap);
+
+ ZeroMemory(dib_data, dib_size.x * dib_size.y * 4);
+ }
+#endif
+ //return 0; // Jump Back
+ } break;
+
+ case WM_ENTERSIZEMOVE: {
+ InputFilter::get_singleton()->release_pressed_events();
+ move_timer_id = SetTimer(windows[window_id].hWnd, 1, USER_TIMER_MINIMUM, (TIMERPROC)NULL);
+ } break;
+ case WM_EXITSIZEMOVE: {
+ KillTimer(windows[window_id].hWnd, move_timer_id);
+ } break;
+ case WM_TIMER: {
+ if (wParam == move_timer_id) {
+ _process_key_events();
+ if (!Main::is_iterating()) {
+ Main::iteration();
+ }
+ }
+ } break;
+
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ case WM_KEYUP:
+ case WM_KEYDOWN: {
+
+ if (wParam == VK_SHIFT)
+ shift_mem = uMsg == WM_KEYDOWN;
+ if (wParam == VK_CONTROL)
+ control_mem = uMsg == WM_KEYDOWN;
+ if (wParam == VK_MENU) {
+ alt_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN);
+ if (lParam & (1 << 24))
+ gr_mem = alt_mem;
+ }
+
+ if (mouse_mode == MOUSE_MODE_CAPTURED) {
+ // When SetCapture is used, ALT+F4 hotkey is ignored by Windows, so handle it ourselves
+ if (wParam == VK_F4 && alt_mem && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)) {
+ _send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST);
+ }
+ }
+ /*
+ if (wParam==VK_WIN) TODO wtf is this?
+ meta_mem=uMsg==WM_KEYDOWN;
+ */
+ [[fallthrough]];
+ }
+ case WM_CHAR: {
+
+ ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);
+
+ // Make sure we don't include modifiers for the modifier key itself.
+ KeyEvent ke;
+ ke.shift = (wParam != VK_SHIFT) ? shift_mem : false;
+ ke.alt = (!(wParam == VK_MENU && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN))) ? alt_mem : false;
+ ke.control = (wParam != VK_CONTROL) ? control_mem : false;
+ ke.meta = meta_mem;
+ ke.uMsg = uMsg;
+ ke.window_id = window_id;
+
+ if (ke.uMsg == WM_SYSKEYDOWN)
+ ke.uMsg = WM_KEYDOWN;
+ if (ke.uMsg == WM_SYSKEYUP)
+ ke.uMsg = WM_KEYUP;
+
+ ke.wParam = wParam;
+ ke.lParam = lParam;
+ key_event_buffer[key_event_pos++] = ke;
+
+ } break;
+ case WM_INPUTLANGCHANGEREQUEST: {
+
+ // FIXME: Do something?
+ } break;
+
+ case WM_TOUCH: {
+
+ BOOL bHandled = FALSE;
+ UINT cInputs = LOWORD(wParam);
+ PTOUCHINPUT pInputs = memnew_arr(TOUCHINPUT, cInputs);
+ if (pInputs) {
+ 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(window_id, touch_pos.x, touch_pos.y, ti.dwID);
+ } else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) {
+
+ _touch_event(window_id, ti.dwFlags & TOUCHEVENTF_DOWN, touch_pos.x, touch_pos.y, ti.dwID);
+ };
+ }
+ bHandled = TRUE;
+ } else {
+ /* handle the error here */
+ }
+ memdelete_arr(pInputs);
+ } else {
+ /* handle the error here, probably out of memory */
+ }
+ if (bHandled) {
+ CloseTouchInputHandle((HTOUCHINPUT)lParam);
+ return 0;
+ };
+
+ } break;
+
+ case WM_DEVICECHANGE: {
+
+ joypad->probe_joypads();
+ } break;
+ case WM_SETCURSOR: {
+ if (LOWORD(lParam) == HTCLIENT) {
+ if (windows[window_id].window_has_focus && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED)) {
+ //Hide the cursor
+ if (hCursor == NULL)
+ hCursor = SetCursor(NULL);
+ else
+ SetCursor(NULL);
+ } else {
+ if (hCursor != NULL) {
+ CursorShape c = cursor_shape;
+ cursor_shape = CURSOR_MAX;
+ cursor_set_shape(c);
+ hCursor = NULL;
+ }
+ }
+ }
+
+ } break;
+ case WM_DROPFILES: {
+
+ HDROP hDropInfo = (HDROP)wParam;
+ const int buffsize = 4096;
+ wchar_t buf[buffsize];
+
+ int fcount = DragQueryFileW(hDropInfo, 0xFFFFFFFF, NULL, 0);
+
+ Vector<String> files;
+
+ for (int i = 0; i < fcount; i++) {
+
+ DragQueryFileW(hDropInfo, i, buf, buffsize);
+ String file = buf;
+ files.push_back(file);
+ }
+
+ if (files.size() && !windows[window_id].drop_files_callback.is_null()) {
+ Variant v = files;
+ Variant *vp = &v;
+ Variant ret;
+ Callable::CallError ce;
+ windows[window_id].drop_files_callback.call((const Variant **)&vp, 1, ret, ce);
+ }
+
+ } break;
+
+ default: {
+
+ if (user_proc) {
+
+ return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
+ };
+ };
+ }
+
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+}
+
+LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+
+ DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());
+ if (ds_win)
+ return ds_win->WndProc(hWnd, uMsg, wParam, lParam);
+ else
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+}
+
+void DisplayServerWindows::_process_key_events() {
+
+ for (int i = 0; i < key_event_pos; i++) {
+
+ KeyEvent &ke = key_event_buffer[i];
+ switch (ke.uMsg) {
+
+ case WM_CHAR: {
+ if ((i == 0 && ke.uMsg == WM_CHAR) || (i > 0 && key_event_buffer[i - 1].uMsg == WM_CHAR)) {
+ Ref<InputEventKey> k;
+ k.instance();
+
+ k->set_window_id(ke.window_id);
+ k->set_shift(ke.shift);
+ k->set_alt(ke.alt);
+ k->set_control(ke.control);
+ k->set_metakey(ke.meta);
+ k->set_pressed(true);
+ k->set_keycode(KeyMappingWindows::get_keysym(ke.wParam));
+ k->set_physical_keycode(KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24)));
+ k->set_unicode(ke.wParam);
+ if (k->get_unicode() && gr_mem) {
+ k->set_alt(false);
+ k->set_control(false);
+ }
+
+ if (k->get_unicode() < 32)
+ k->set_unicode(0);
+
+ InputFilter::get_singleton()->accumulate_input_event(k);
+ }
+
+ //do nothing
+ } break;
+ case WM_KEYUP:
+ case WM_KEYDOWN: {
+
+ Ref<InputEventKey> k;
+ k.instance();
+
+ k->set_window_id(ke.window_id);
+ k->set_shift(ke.shift);
+ k->set_alt(ke.alt);
+ k->set_control(ke.control);
+ k->set_metakey(ke.meta);
+
+ k->set_pressed(ke.uMsg == WM_KEYDOWN);
+
+ if ((ke.lParam & (1 << 24)) && (ke.wParam == VK_RETURN)) {
+ // Special case for Numpad Enter key
+ k->set_keycode(KEY_KP_ENTER);
+ } else {
+ k->set_keycode(KeyMappingWindows::get_keysym(ke.wParam));
+ }
+
+ k->set_physical_keycode(KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24)));
+
+ if (i + 1 < key_event_pos && key_event_buffer[i + 1].uMsg == WM_CHAR) {
+ k->set_unicode(key_event_buffer[i + 1].wParam);
+ }
+ if (k->get_unicode() && gr_mem) {
+ k->set_alt(false);
+ k->set_control(false);
+ }
+
+ if (k->get_unicode() < 32)
+ k->set_unicode(0);
+
+ k->set_echo((ke.uMsg == WM_KEYDOWN && (ke.lParam & (1 << 30))));
+
+ InputFilter::get_singleton()->accumulate_input_event(k);
+
+ } break;
+ }
+ }
+
+ key_event_pos = 0;
+}
+
+DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) {
+
+ DWORD dwExStyle;
+ DWORD dwStyle;
+
+ _get_window_style(window_id_counter == MAIN_WINDOW_ID, p_mode == WINDOW_MODE_FULLSCREEN, p_flags & WINDOW_FLAG_BORDERLESS_BIT, !(p_flags & WINDOW_FLAG_RESIZE_DISABLED_BIT), p_mode == WINDOW_MODE_MAXIMIZED, (p_flags & WINDOW_FLAG_NO_FOCUS_BIT), dwStyle, dwExStyle);
+
+ RECT WindowRect;
+
+ WindowRect.left = p_rect.position.x;
+ WindowRect.right = p_rect.position.x + p_rect.size.x;
+ WindowRect.top = p_rect.position.y;
+ WindowRect.bottom = p_rect.position.y + p_rect.size.y;
+
+ AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
+
+ WindowID id = window_id_counter;
+ {
+ WindowData wd;
+
+ wd.hWnd = CreateWindowExW(
+ dwExStyle,
+ L"Engine", L"",
+ dwStyle,
+ // (GetSystemMetrics(SM_CXSCREEN) - WindowRect.right) / 2,
+ // (GetSystemMetrics(SM_CYSCREEN) - WindowRect.bottom) / 2,
+ WindowRect.left,
+ WindowRect.top,
+ WindowRect.right - WindowRect.left,
+ WindowRect.bottom - WindowRect.top,
+ NULL, NULL, hInstance, NULL);
+ if (!wd.hWnd) {
+ MessageBoxW(NULL, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);
+ return INVALID_WINDOW_ID;
+ }
+#ifdef VULKAN_ENABLED
+
+ if (rendering_driver == "vulkan") {
+ if (context_vulkan->window_create(id, wd.hWnd, hInstance, WindowRect.right - WindowRect.left, WindowRect.bottom - WindowRect.top) == -1) {
+ memdelete(context_vulkan);
+ context_vulkan = NULL;
+ ERR_FAIL_V(INVALID_WINDOW_ID);
+ }
+ }
+#endif
+
+ RegisterTouchWindow(wd.hWnd, 0);
+
+ TRACKMOUSEEVENT tme;
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.dwFlags = TME_LEAVE;
+ tme.hwndTrack = wd.hWnd;
+ tme.dwHoverTime = HOVER_DEFAULT;
+ TrackMouseEvent(&tme);
+
+ DragAcceptFiles(wd.hWnd, true);
+
+ // IME
+ wd.im_himc = ImmGetContext(wd.hWnd);
+ ImmReleaseContext(wd.hWnd, wd.im_himc);
+
+ wd.im_position = Vector2();
+ wd.last_pos = p_rect.position;
+ wd.width = p_rect.size.width;
+ wd.height = p_rect.size.height;
+
+ windows[id] = wd;
+
+ window_id_counter++;
+ }
+
+ return id;
+}
+
+GetPointerTypePtr DisplayServerWindows::win8p_GetPointerType = nullptr;
+GetPointerPenInfoPtr DisplayServerWindows::win8p_GetPointerPenInfo = nullptr;
+
+typedef enum _SHC_PROCESS_DPI_AWARENESS {
+ SHC_PROCESS_DPI_UNAWARE = 0,
+ SHC_PROCESS_SYSTEM_DPI_AWARE = 1,
+ SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2
+} SHC_PROCESS_DPI_AWARENESS;
+
+DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+
+ //Note: Functions for pen input, available on Windows 8+
+ HMODULE user32_lib = LoadLibraryW(L"user32.dll");
+ if (user32_lib) {
+ win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType");
+ win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo");
+ }
+
+ drop_events = false;
+ key_event_pos = 0;
+
+ alt_mem = false;
+ gr_mem = false;
+ shift_mem = false;
+ control_mem = false;
+ meta_mem = false;
+ console_visible = IsWindowVisible(GetConsoleWindow());
+ hInstance = ((OS_Windows *)OS::get_singleton())->get_hinstance();
+
+ pressrc = 0;
+ old_invalid = true;
+ mouse_mode = MOUSE_MODE_VISIBLE;
+
+ outside = true;
+
+ if (OS::get_singleton()->is_hidpi_allowed()) {
+ HMODULE Shcore = LoadLibraryW(L"Shcore.dll");
+
+ if (Shcore != NULL) {
+ typedef HRESULT(WINAPI * SetProcessDpiAwareness_t)(SHC_PROCESS_DPI_AWARENESS);
+
+ SetProcessDpiAwareness_t SetProcessDpiAwareness = (SetProcessDpiAwareness_t)GetProcAddress(Shcore, "SetProcessDpiAwareness");
+
+ if (SetProcessDpiAwareness) {
+ SetProcessDpiAwareness(SHC_PROCESS_SYSTEM_DPI_AWARE);
+ }
+ }
+ }
+
+ memset(&wc, 0, sizeof(WNDCLASSEXW));
+ wc.cbSize = sizeof(WNDCLASSEXW);
+ wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
+ wc.lpfnWndProc = (WNDPROC)::WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ //wc.hInstance = hInstance;
+ wc.hInstance = hInstance ? hInstance : GetModuleHandle(NULL);
+ wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
+ wc.hCursor = NULL; //LoadCursor(NULL, IDC_ARROW);
+ wc.hbrBackground = NULL;
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = L"Engine";
+
+ if (!RegisterClassExW(&wc)) {
+ MessageBox(NULL, "Failed To Register The Window Class.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
+ r_error = ERR_UNAVAILABLE;
+ return;
+ }
+
+ use_raw_input = true;
+
+ RAWINPUTDEVICE Rid[1];
+
+ Rid[0].usUsagePage = 0x01;
+ Rid[0].usUsage = 0x02;
+ Rid[0].dwFlags = 0;
+ Rid[0].hwndTarget = 0;
+
+ if (RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) {
+ //registration failed.
+ use_raw_input = false;
+ }
+
+ rendering_driver = "vulkan";
+
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+
+ context_vulkan = memnew(VulkanContextWindows);
+ if (context_vulkan->initialize() != OK) {
+ memdelete(context_vulkan);
+ context_vulkan = NULL;
+ r_error = ERR_UNAVAILABLE;
+ return;
+ }
+ }
+#endif
+#if defined(OPENGL_ENABLED)
+ if (rendering_driver_index == VIDEO_DRIVER_GLES2) {
+
+ context_gles2 = memnew(ContextGL_Windows(hWnd, false));
+
+ if (context_gles2->initialize() != OK) {
+ memdelete(context_gles2);
+ context_gles2 = NULL;
+ ERR_FAIL_V(ERR_UNAVAILABLE);
+ }
+
+ context_gles2->set_use_vsync(video_mode.use_vsync);
+ set_vsync_via_compositor(video_mode.vsync_via_compositor);
+
+ if (RasterizerGLES2::is_viable() == OK) {
+ RasterizerGLES2::register_config();
+ RasterizerGLES2::make_current();
+ } else {
+ memdelete(context_gles2);
+ context_gles2 = NULL;
+ ERR_FAIL_V(ERR_UNAVAILABLE);
+ }
+ }
+#endif
+ WindowID main_window = _create_window(p_mode, 0, Rect2i(Point2i(), p_resolution));
+ for (int i = 0; i < WINDOW_FLAG_MAX; i++) {
+ if (p_flags & (1 << i)) {
+ window_set_flag(WindowFlags(i), true, main_window);
+ }
+ }
+
+ ShowWindow(windows[MAIN_WINDOW_ID].hWnd, SW_SHOW); // Show The Window
+ SetForegroundWindow(windows[MAIN_WINDOW_ID].hWnd); // Slightly Higher Priority
+ SetFocus(windows[MAIN_WINDOW_ID].hWnd); // Sets Keyboard Focus To
+
+#if defined(VULKAN_ENABLED)
+
+ if (rendering_driver == "vulkan") {
+
+ rendering_device_vulkan = memnew(RenderingDeviceVulkan);
+ rendering_device_vulkan->initialize(context_vulkan);
+
+ RasterizerRD::make_current();
+ }
+#endif
+
+ move_timer_id = 1;
+
+ //set_ime_active(false);
+
+ if (!OS::get_singleton()->is_in_low_processor_usage_mode()) {
+ //SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
+ SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
+ DWORD index = 0;
+ HANDLE handle = AvSetMmThreadCharacteristics("Games", &index);
+ if (handle)
+ AvSetMmThreadPriority(handle, AVRT_PRIORITY_CRITICAL);
+
+ // This is needed to make sure that background work does not starve the main thread.
+ // This is only setting priority of this thread, not the whole process.
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+ }
+
+ cursor_shape = CURSOR_ARROW;
+
+ _update_real_mouse_position(MAIN_WINDOW_ID);
+
+ joypad = new JoypadWindows(&windows[MAIN_WINDOW_ID].hWnd);
+
+ r_error = OK;
+
+ ((OS_Windows *)OS::get_singleton())->set_main_window(windows[MAIN_WINDOW_ID].hWnd);
+ InputFilter::get_singleton()->set_event_dispatch_function(_dispatch_input_events);
+}
+
+Vector<String> DisplayServerWindows::get_rendering_drivers_func() {
+ Vector<String> drivers;
+
+#ifdef VULKAN_ENABLED
+ drivers.push_back("vulkan");
+#endif
+#ifdef OPENGL_ENABLED
+ drivers.push_back("opengl");
+#endif
+
+ return drivers;
+}
+
+DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
+
+ return memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_flags, p_resolution, r_error));
+}
+
+void DisplayServerWindows::register_windows_driver() {
+
+ register_create_function("windows", create_func, get_rendering_drivers_func);
+}
+
+DisplayServerWindows::~DisplayServerWindows() {
+
+ delete joypad;
+ touch_state.clear();
+
+ cursors_cache.clear();
+
+#if defined(VULKAN_ENABLED)
+ if (rendering_driver == "vulkan") {
+
+ if (rendering_device_vulkan) {
+ rendering_device_vulkan->finalize();
+ memdelete(rendering_device_vulkan);
+ }
+
+ if (context_vulkan)
+ memdelete(context_vulkan);
+ }
+#endif
+
+ if (user_proc) {
+ SetWindowLongPtr(windows[MAIN_WINDOW_ID].hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);
+ };
+
+ if (windows.has(MAIN_WINDOW_ID)) {
+#ifdef VULKAN_ENABLED
+ if (rendering_driver == "vulkan") {
+ context_vulkan->window_destroy(MAIN_WINDOW_ID);
+ }
+#endif
+
+ DestroyWindow(windows[MAIN_WINDOW_ID].hWnd);
+ }
+}
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
new file mode 100644
index 0000000000..47bb3f59a6
--- /dev/null
+++ b/platform/windows/display_server_windows.h
@@ -0,0 +1,389 @@
+#ifndef DISPLAY_SERVER_WINDOWS_H
+#define DISPLAY_SERVER_WINDOWS_H
+
+#include "servers/display_server.h"
+
+#include "core/input/input_filter.h"
+#include "core/os/os.h"
+#include "core/project_settings.h"
+#include "crash_handler_windows.h"
+#include "drivers/unix/ip_unix.h"
+#include "drivers/wasapi/audio_driver_wasapi.h"
+#include "drivers/winmidi/midi_driver_winmidi.h"
+#include "joypad_windows.h"
+#include "key_mapping_windows.h"
+#include "servers/audio_server.h"
+#include "servers/visual/rasterizer.h"
+#include "servers/visual/rasterizer_rd/rasterizer_rd.h"
+#include "servers/visual_server.h"
+
+#ifdef XAUDIO2_ENABLED
+#include "drivers/xaudio2/audio_driver_xaudio2.h"
+#endif
+
+#if defined(OPENGL_ENABLED)
+#include "context_gl_windows.h"
+#endif
+
+#if defined(VULKAN_ENABLED)
+#include "drivers/vulkan/rendering_device_vulkan.h"
+#include "platform/windows/vulkan_context_win.h"
+#endif
+
+#include <fcntl.h>
+#include <io.h>
+#include <stdio.h>
+#include <windows.h>
+#include <windowsx.h>
+
+#ifndef POINTER_STRUCTURES
+
+#define POINTER_STRUCTURES
+
+typedef DWORD POINTER_INPUT_TYPE;
+typedef UINT32 POINTER_FLAGS;
+typedef UINT32 PEN_FLAGS;
+typedef UINT32 PEN_MASK;
+
+enum tagPOINTER_INPUT_TYPE {
+ PT_POINTER = 0x00000001,
+ PT_TOUCH = 0x00000002,
+ PT_PEN = 0x00000003,
+ PT_MOUSE = 0x00000004,
+ PT_TOUCHPAD = 0x00000005
+};
+
+typedef enum tagPOINTER_BUTTON_CHANGE_TYPE {
+ POINTER_CHANGE_NONE,
+ POINTER_CHANGE_FIRSTBUTTON_DOWN,
+ POINTER_CHANGE_FIRSTBUTTON_UP,
+ POINTER_CHANGE_SECONDBUTTON_DOWN,
+ POINTER_CHANGE_SECONDBUTTON_UP,
+ POINTER_CHANGE_THIRDBUTTON_DOWN,
+ POINTER_CHANGE_THIRDBUTTON_UP,
+ POINTER_CHANGE_FOURTHBUTTON_DOWN,
+ POINTER_CHANGE_FOURTHBUTTON_UP,
+ POINTER_CHANGE_FIFTHBUTTON_DOWN,
+ POINTER_CHANGE_FIFTHBUTTON_UP,
+} POINTER_BUTTON_CHANGE_TYPE;
+
+typedef struct tagPOINTER_INFO {
+ POINTER_INPUT_TYPE pointerType;
+ UINT32 pointerId;
+ UINT32 frameId;
+ POINTER_FLAGS pointerFlags;
+ HANDLE sourceDevice;
+ HWND hwndTarget;
+ POINT ptPixelLocation;
+ POINT ptHimetricLocation;
+ POINT ptPixelLocationRaw;
+ POINT ptHimetricLocationRaw;
+ DWORD dwTime;
+ UINT32 historyCount;
+ INT32 InputData;
+ DWORD dwKeyStates;
+ UINT64 PerformanceCount;
+ POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
+} POINTER_INFO;
+
+typedef struct tagPOINTER_PEN_INFO {
+ POINTER_INFO pointerInfo;
+ PEN_FLAGS penFlags;
+ PEN_MASK penMask;
+ UINT32 pressure;
+ UINT32 rotation;
+ INT32 tiltX;
+ INT32 tiltY;
+} POINTER_PEN_INFO;
+
+#endif //POINTER_STRUCTURES
+
+typedef BOOL(WINAPI *GetPointerTypePtr)(uint32_t p_id, POINTER_INPUT_TYPE *p_type);
+typedef BOOL(WINAPI *GetPointerPenInfoPtr)(uint32_t p_id, POINTER_PEN_INFO *p_pen_info);
+
+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 DisplayServerWindows : public DisplayServer {
+ //No need to register, it's platform-specific and nothing is added
+ //GDCLASS(DisplayServerWindows, DisplayServer)
+
+ _THREAD_SAFE_CLASS_
+
+ static GetPointerTypePtr win8p_GetPointerType;
+ static GetPointerPenInfoPtr win8p_GetPointerPenInfo;
+
+ void GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap);
+
+ enum {
+ KEY_EVENT_BUFFER_SIZE = 512
+ };
+
+ struct KeyEvent {
+
+ WindowID window_id;
+ bool alt, shift, control, meta;
+ UINT uMsg;
+ WPARAM wParam;
+ LPARAM lParam;
+ };
+
+ KeyEvent key_event_buffer[KEY_EVENT_BUFFER_SIZE];
+ int key_event_pos;
+
+ bool old_invalid;
+ bool outside;
+ int old_x, old_y;
+ Point2i center;
+
+#if defined(OPENGL_ENABLED)
+ ContextGL_Windows *context_gles2;
+#endif
+
+#if defined(VULKAN_ENABLED)
+ VulkanContextWindows *context_vulkan;
+ RenderingDeviceVulkan *rendering_device_vulkan;
+#endif
+
+ Map<int, Vector2> touch_state;
+
+ int pressrc;
+ HINSTANCE hInstance; // Holds The Instance Of The Application
+ String rendering_driver;
+
+ struct WindowData {
+ HWND hWnd;
+ //layered window
+
+ bool preserve_window_size = false;
+ bool pre_fs_valid = false;
+ RECT pre_fs_rect;
+ bool maximized = false;
+ bool minimized = false;
+ bool fullscreen = false;
+ bool borderless = false;
+ bool resizable = true;
+ bool window_focused = false;
+ bool was_maximized = false;
+ bool always_on_top = false;
+ bool no_focus = false;
+ bool window_has_focus = false;
+
+ HBITMAP hBitmap; //DIB section for layered window
+ uint8_t *dib_data = nullptr;
+ Size2 dib_size;
+ HDC hDC_dib;
+ Size2 min_size;
+ Size2 max_size;
+ int width = 0, height = 0;
+
+ Size2 window_rect;
+ Point2 last_pos;
+
+ ObjectID instance_id;
+
+ // IME
+ HIMC im_himc;
+ Vector2 im_position;
+
+ bool layered_window = false;
+
+ Callable rect_changed_callback;
+ Callable event_callback;
+ Callable input_event_callback;
+ Callable input_text_callback;
+ Callable drop_files_callback;
+
+ WindowID transient_parent = INVALID_WINDOW_ID;
+ Set<WindowID> transient_children;
+ };
+
+ JoypadWindows *joypad;
+
+ WindowID _create_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect);
+ WindowID window_id_counter = MAIN_WINDOW_ID;
+ Map<WindowID, WindowData> windows;
+
+ WindowID last_focused_window = INVALID_WINDOW_ID;
+
+ uint32_t move_timer_id;
+
+ HCURSOR hCursor;
+
+ WNDPROC user_proc = nullptr;
+
+ void _send_window_event(const WindowData &wd, WindowEvent p_event);
+ void _get_window_style(bool p_main_window, bool p_fullscreen, bool p_borderless, bool p_resizable, bool p_maximized, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex);
+
+ MouseMode mouse_mode;
+ bool alt_mem = false;
+ bool gr_mem = false;
+ bool shift_mem = false;
+ bool control_mem = false;
+ bool meta_mem = false;
+ bool force_quit = false;
+ uint32_t last_button_state = 0;
+ bool use_raw_input = false;
+ bool drop_events = false;
+ bool console_visible = false;
+
+ WNDCLASSEXW wc;
+
+ HCURSOR cursors[CURSOR_MAX] = { NULL };
+ CursorShape cursor_shape;
+ Map<CursorShape, Vector<Variant>> cursors_cache;
+
+ void _drag_event(WindowID p_window, float p_x, float p_y, int idx);
+ void _touch_event(WindowID p_window, bool p_pressed, float p_x, float p_y, int idx);
+
+ void _update_window_style(WindowID p_window, bool p_repaint = true, bool p_maximized = false);
+
+ void _update_real_mouse_position(WindowID p_window);
+
+ void _set_mouse_mode_impl(MouseMode p_mode);
+
+ void _process_key_events();
+
+ static void _dispatch_input_events(const Ref<InputEvent> &p_event);
+ void _dispatch_input_event(const Ref<InputEvent> &p_event);
+
+public:
+ LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+ virtual bool has_feature(Feature p_feature) const;
+ virtual String get_name() const;
+
+ virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
+
+ virtual void mouse_set_mode(MouseMode p_mode);
+ virtual MouseMode mouse_get_mode() const;
+
+ virtual void mouse_warp_to_position(const Point2i &p_to);
+ virtual Point2i mouse_get_position() const;
+ virtual int mouse_get_button_state() const;
+
+ virtual void clipboard_set(const String &p_text);
+ virtual String clipboard_get() const;
+
+ virtual int get_screen_count() const;
+ virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+ virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+ virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+ virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+ virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+
+ virtual void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW);
+ ScreenOrientation screen_get_orientation(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
+
+ virtual void screen_set_keep_on(bool p_enable); //disable screensaver
+ virtual bool screen_is_kept_on() const;
+
+ virtual Vector<DisplayServer::WindowID> get_window_list() const;
+
+ virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i());
+ virtual void delete_sub_window(WindowID p_window);
+
+ virtual WindowID get_window_at_screen_position(const Point2i &p_position) const;
+
+ virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID);
+ virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual void window_set_transient(WindowID p_window, WindowID p_parent);
+
+ virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
+ virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
+ virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID);
+ virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const;
+ virtual Size2i window_get_real_size(WindowID p_window = MAIN_WINDOW_ID) const; //wtf is this? should probable use proper name
+
+ virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID);
+ virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID);
+ virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const;
+
+ virtual bool can_any_window_draw() const;
+
+ virtual void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID);
+ virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID);
+
+ virtual void console_set_visible(bool p_enabled);
+ virtual bool is_console_visible() const;
+
+ virtual void cursor_set_shape(CursorShape p_shape);
+ virtual CursorShape cursor_get_shape() const;
+ virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2());
+
+ virtual bool get_swap_ok_cancel();
+
+ virtual void enable_for_stealing_focus(OS::ProcessID pid);
+
+ virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
+
+ virtual void process_events();
+
+ virtual void force_process_and_drop_events();
+
+ virtual void release_rendering_thread();
+ virtual void make_rendering_thread();
+ virtual void swap_buffers();
+
+ virtual void set_native_icon(const String &p_filename);
+ virtual void set_icon(const Ref<Image> &p_icon);
+
+ virtual void vsync_set_use_via_compositor(bool p_enable);
+ virtual bool vsync_is_using_via_compositor() const;
+
+ virtual void set_context(Context p_context);
+
+ static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ static Vector<String> get_rendering_drivers_func();
+ static void register_windows_driver();
+
+ DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error);
+ ~DisplayServerWindows();
+};
+
+#endif // DISPLAY_SERVER_WINDOWS_H
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index 49432435b9..9de1b7b194 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -52,9 +52,9 @@ DWORD WINAPI _xinput_set_state(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration)
JoypadWindows::JoypadWindows() {
}
-JoypadWindows::JoypadWindows(InputDefault *_input, HWND *hwnd) {
+JoypadWindows::JoypadWindows(HWND *hwnd) {
- input = _input;
+ input = InputFilter::get_singleton();
hWnd = hwnd;
joypad_count = 0;
dinput = NULL;
@@ -436,46 +436,46 @@ void JoypadWindows::post_hat(int p_device, DWORD p_dpad) {
// BOOL POVCentered = (LOWORD(dwPOV) == 0xFFFF);"
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416628(v%3Dvs.85)#remarks
if (LOWORD(p_dpad) == 0xFFFF) {
- dpad_val = InputDefault::HAT_MASK_CENTER;
+ dpad_val = InputFilter::HAT_MASK_CENTER;
}
if (p_dpad == 0) {
- dpad_val = InputDefault::HAT_MASK_UP;
+ dpad_val = InputFilter::HAT_MASK_UP;
} else if (p_dpad == 4500) {
- dpad_val = (InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_RIGHT);
+ dpad_val = (InputFilter::HAT_MASK_UP | InputFilter::HAT_MASK_RIGHT);
} else if (p_dpad == 9000) {
- dpad_val = InputDefault::HAT_MASK_RIGHT;
+ dpad_val = InputFilter::HAT_MASK_RIGHT;
} else if (p_dpad == 13500) {
- dpad_val = (InputDefault::HAT_MASK_RIGHT | InputDefault::HAT_MASK_DOWN);
+ dpad_val = (InputFilter::HAT_MASK_RIGHT | InputFilter::HAT_MASK_DOWN);
} else if (p_dpad == 18000) {
- dpad_val = InputDefault::HAT_MASK_DOWN;
+ dpad_val = InputFilter::HAT_MASK_DOWN;
} else if (p_dpad == 22500) {
- dpad_val = (InputDefault::HAT_MASK_DOWN | InputDefault::HAT_MASK_LEFT);
+ dpad_val = (InputFilter::HAT_MASK_DOWN | InputFilter::HAT_MASK_LEFT);
} else if (p_dpad == 27000) {
- dpad_val = InputDefault::HAT_MASK_LEFT;
+ dpad_val = InputFilter::HAT_MASK_LEFT;
} else if (p_dpad == 31500) {
- dpad_val = (InputDefault::HAT_MASK_LEFT | InputDefault::HAT_MASK_UP);
+ dpad_val = (InputFilter::HAT_MASK_LEFT | InputFilter::HAT_MASK_UP);
}
input->joy_hat(p_device, dpad_val);
};
-InputDefault::JoyAxis JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const {
+InputFilter::JoyAxis JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const {
- InputDefault::JoyAxis jx;
+ InputFilter::JoyAxis jx;
if (Math::abs(p_val) < MIN_JOY_AXIS) {
jx.min = p_trigger ? 0 : -1;
jx.value = 0.0f;
diff --git a/platform/windows/joypad_windows.h b/platform/windows/joypad_windows.h
index ab85bc60ac..f010fd08ff 100644
--- a/platform/windows/joypad_windows.h
+++ b/platform/windows/joypad_windows.h
@@ -52,7 +52,7 @@
class JoypadWindows {
public:
JoypadWindows();
- JoypadWindows(InputDefault *_input, HWND *hwnd);
+ JoypadWindows(HWND *hwnd);
~JoypadWindows();
void probe_joypads();
@@ -117,7 +117,7 @@ private:
HWND *hWnd;
HANDLE xinput_dll;
LPDIRECTINPUT8 dinput;
- InputDefault *input;
+ InputFilter *input;
int id_to_change;
int joypad_count;
@@ -141,7 +141,7 @@ private:
void joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
void joypad_vibration_stop_xinput(int p_device, uint64_t p_timestamp);
- InputDefault::JoyAxis axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const;
+ InputFilter::JoyAxis axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const;
XInputGetState_t xinput_get_state;
XInputSetState_t xinput_set_state;
};
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 041a5bffa6..f78c87be93 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -37,15 +37,6 @@
#include "core/debugger/script_debugger.h"
#include "core/io/marshalls.h"
#include "core/version_generated.gen.h"
-
-#if defined(OPENGL_ENABLED)
-#include "drivers/gles2/rasterizer_gles2.h"
-#endif
-
-#if defined(VULKAN_ENABLED)
-#include "servers/visual/rasterizer_rd/rasterizer_rd.h"
-#endif
-
#include "drivers/windows/dir_access_windows.h"
#include "drivers/windows/file_access_windows.h"
#include "drivers/windows/rw_lock_windows.h"
@@ -53,6 +44,7 @@
#include "joypad_windows.h"
#include "lang_table.h"
#include "main/main.h"
+#include "platform/windows/display_server_windows.h"
#include "servers/audio_server.h"
#include "servers/visual/visual_server_raster.h"
#include "servers/visual/visual_server_wrap_mt.h"
@@ -86,30 +78,6 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
#define GetProcAddress (void *)GetProcAddress
#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;
-}
-
#ifdef DEBUG_ENABLED
static String format_error_message(DWORD id) {
@@ -125,8 +93,6 @@ static String format_error_message(DWORD id) {
}
#endif // DEBUG_ENABLED
-extern HINSTANCE godot_hinstance;
-
void RedirectIOToConsole() {
int hConHandle;
@@ -208,24 +174,16 @@ BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) {
}
}
-GetPointerTypePtr OS_Windows::win8p_GetPointerType = NULL;
-GetPointerPenInfoPtr OS_Windows::win8p_GetPointerPenInfo = NULL;
-
void OS_Windows::initialize_debugging() {
SetConsoleCtrlHandler(HandlerRoutine, TRUE);
}
-void OS_Windows::initialize_core() {
+void OS_Windows::initialize() {
crash_handler.initialize();
- last_button_state = 0;
-
//RedirectIOToConsole();
- maximized = false;
- minimized = false;
- borderless = false;
ThreadWindows::make_default();
RWLockWindows::make_default();
@@ -255,1371 +213,9 @@ void OS_Windows::initialize_core() {
process_map = memnew((Map<ProcessID, ProcessInfo>));
IP_Unix::make_default();
-
- cursor_shape = CURSOR_ARROW;
-}
-
-bool OS_Windows::can_draw() const {
-
- return !minimized;
-};
-
-#define MI_WP_SIGNATURE 0xFF515700
-#define SIGNATURE_MASK 0xFFFFFF00
-// Keeping the name suggested by Microsoft, but this macro really answers:
-// Is this mouse event emulated from touch or pen input?
-#define IsPenEvent(dw) (((dw)&SIGNATURE_MASK) == MI_WP_SIGNATURE)
-// This one tells whether the event comes from touchscreen (and not from pen)
-#define IsTouchEvent(dw) (IsPenEvent(dw) && ((dw)&0x80))
-
-void OS_Windows::_touch_event(bool p_pressed, float p_x, float p_y, int idx) {
-
- // Defensive
- if (touch_state.has(idx) == p_pressed)
- return;
-
- if (p_pressed) {
- touch_state.insert(idx, Vector2(p_x, p_y));
- } else {
- touch_state.erase(idx);
- }
-
- Ref<InputEventScreenTouch> event;
- event.instance();
- event->set_index(idx);
- event->set_pressed(p_pressed);
- event->set_position(Vector2(p_x, p_y));
-
- if (main_loop) {
- input->accumulate_input_event(event);
- }
-};
-
-void OS_Windows::_drag_event(float p_x, float p_y, int idx) {
-
- Map<int, Vector2>::Element *curr = touch_state.find(idx);
- // Defensive
- if (!curr)
- return;
-
- if (curr->get() == Vector2(p_x, p_y))
- return;
-
- Ref<InputEventScreenDrag> event;
- event.instance();
- event->set_index(idx);
- event->set_position(Vector2(p_x, p_y));
- event->set_relative(Vector2(p_x, p_y) - curr->get());
-
- if (main_loop)
- input->accumulate_input_event(event);
-
- curr->get() = Vector2(p_x, p_y);
-};
-
-LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
-
- if (drop_events) {
-
- if (user_proc) {
-
- return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
- } else {
- return DefWindowProcW(hWnd, uMsg, wParam, lParam);
- }
- };
-
- switch (uMsg) // Check For Windows Messages
- {
- case WM_SETFOCUS: {
- window_has_focus = true;
-
- // Restore mouse mode
- _set_mouse_mode_impl(mouse_mode);
-
- break;
- }
- case WM_KILLFOCUS: {
- window_has_focus = false;
-
- // Release capture unconditionally because it can be set due to dragging, in addition to captured mode
- ReleaseCapture();
-
- // Release every touch to avoid sticky points
- for (Map<int, Vector2>::Element *E = touch_state.front(); E; E = E->next()) {
- _touch_event(false, E->get().x, E->get().y, E->key());
- }
- touch_state.clear();
-
- break;
- }
- case WM_ACTIVATE: // Watch For Window Activate Message
- {
- minimized = HIWORD(wParam) != 0;
- if (!main_loop) {
- return 0;
- };
- if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) {
-
- main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_IN);
- window_focused = true;
- alt_mem = false;
- control_mem = false;
- shift_mem = false;
- } else { // WM_INACTIVE
- input->release_pressed_events();
- main_loop->notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT);
- window_focused = false;
- alt_mem = false;
- };
-
- return 0; // Return To The Message Loop
- }
- case WM_GETMINMAXINFO: {
- if (video_mode.resizable && !video_mode.fullscreen) {
- Size2 decor = get_real_window_size() - get_window_size(); // Size of window decorations
- MINMAXINFO *min_max_info = (MINMAXINFO *)lParam;
- if (min_size != Size2()) {
- min_max_info->ptMinTrackSize.x = min_size.x + decor.x;
- min_max_info->ptMinTrackSize.y = min_size.y + decor.y;
- }
- if (max_size != Size2()) {
- min_max_info->ptMaxTrackSize.x = max_size.x + decor.x;
- min_max_info->ptMaxTrackSize.y = max_size.y + decor.y;
- }
- return 0;
- } else {
- break;
- }
- }
- case WM_PAINT:
-
- Main::force_redraw();
- break;
-
- case WM_SYSCOMMAND: // Intercept System Commands
- {
- switch (wParam) // Check System Calls
- {
- case SC_SCREENSAVE: // Screensaver Trying To Start?
- case SC_MONITORPOWER: // Monitor Trying To Enter Powersave?
- return 0; // Prevent From Happening
- case SC_KEYMENU:
- if ((lParam >> 16) <= 0)
- return 0;
- }
- break; // Exit
- }
-
- case WM_CLOSE: // Did We Receive A Close Message?
- {
- if (main_loop)
- main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
- //force_quit=true;
- return 0; // Jump Back
- }
- case WM_MOUSELEAVE: {
-
- old_invalid = true;
- outside = true;
- if (main_loop && mouse_mode != MOUSE_MODE_CAPTURED)
- main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT);
-
- } break;
- case WM_INPUT: {
- if (mouse_mode != MOUSE_MODE_CAPTURED || !use_raw_input) {
- break;
- }
-
- UINT dwSize;
-
- GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
- LPBYTE lpb = new BYTE[dwSize];
- if (lpb == NULL) {
- return 0;
- }
-
- if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize)
- OutputDebugString(TEXT("GetRawInputData does not return correct size !\n"));
-
- RAWINPUT *raw = (RAWINPUT *)lpb;
-
- if (raw->header.dwType == RIM_TYPEMOUSE) {
- Ref<InputEventMouseMotion> mm;
- mm.instance();
-
- mm->set_control(control_mem);
- mm->set_shift(shift_mem);
- mm->set_alt(alt_mem);
-
- mm->set_button_mask(last_button_state);
-
- Point2i c(video_mode.width / 2, video_mode.height / 2);
-
- // centering just so it works as before
- POINT pos = { (int)c.x, (int)c.y };
- ClientToScreen(hWnd, &pos);
- SetCursorPos(pos.x, pos.y);
-
- mm->set_position(c);
- mm->set_global_position(c);
- input->set_mouse_position(c);
- mm->set_speed(Vector2(0, 0));
-
- if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) {
- mm->set_relative(Vector2(raw->data.mouse.lLastX, raw->data.mouse.lLastY));
-
- } else if (raw->data.mouse.usFlags == MOUSE_MOVE_ABSOLUTE) {
-
- int nScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
- int nScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
- int nScreenLeft = GetSystemMetrics(SM_XVIRTUALSCREEN);
- int nScreenTop = GetSystemMetrics(SM_YVIRTUALSCREEN);
-
- Vector2 abs_pos(
- (double(raw->data.mouse.lLastX) - 65536.0 / (nScreenWidth)) * nScreenWidth / 65536.0 + nScreenLeft,
- (double(raw->data.mouse.lLastY) - 65536.0 / (nScreenHeight)) * nScreenHeight / 65536.0 + nScreenTop);
-
- POINT coords; //client coords
- coords.x = abs_pos.x;
- coords.y = abs_pos.y;
-
- ScreenToClient(hWnd, &coords);
-
- mm->set_relative(Vector2(coords.x - old_x, coords.y - old_y));
- old_x = coords.x;
- old_y = coords.y;
-
- /*Input.mi.dx = (int)((((double)(pos.x)-nScreenLeft) * 65536) / nScreenWidth + 65536 / (nScreenWidth));
- Input.mi.dy = (int)((((double)(pos.y)-nScreenTop) * 65536) / nScreenHeight + 65536 / (nScreenHeight));
- */
- }
-
- if (window_has_focus && main_loop && mm->get_relative() != Vector2())
- input->accumulate_input_event(mm);
- }
- delete[] lpb;
- } break;
- case WM_POINTERUPDATE: {
- if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
- break;
- }
-
- if (!win8p_GetPointerType || !win8p_GetPointerPenInfo) {
- break;
- }
-
- uint32_t pointer_id = LOWORD(wParam);
- POINTER_INPUT_TYPE pointer_type = PT_POINTER;
- if (!win8p_GetPointerType(pointer_id, &pointer_type)) {
- break;
- }
-
- if (pointer_type != PT_PEN) {
- break;
- }
-
- POINTER_PEN_INFO pen_info;
- if (!win8p_GetPointerPenInfo(pointer_id, &pen_info)) {
- break;
- }
-
- if (input->is_emulating_mouse_from_touch()) {
- // Universal translation enabled; ignore OS translation
- LPARAM extra = GetMessageExtraInfo();
- if (IsTouchEvent(extra)) {
- break;
- }
- }
-
- if (outside) {
- //mouse enter
-
- if (main_loop && mouse_mode != MOUSE_MODE_CAPTURED)
- main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
-
- CursorShape c = cursor_shape;
- cursor_shape = CURSOR_MAX;
- set_cursor_shape(c);
- outside = false;
-
- //Once-Off notification, must call again....
- TRACKMOUSEEVENT tme;
- tme.cbSize = sizeof(TRACKMOUSEEVENT);
- tme.dwFlags = TME_LEAVE;
- tme.hwndTrack = hWnd;
- tme.dwHoverTime = HOVER_DEFAULT;
- TrackMouseEvent(&tme);
- }
-
- // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
- if (!window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED)
- break;
-
- Ref<InputEventMouseMotion> mm;
- mm.instance();
-
- mm->set_pressure(pen_info.pressure ? (float)pen_info.pressure / 1024 : 0);
- mm->set_tilt(Vector2(pen_info.tiltX ? (float)pen_info.tiltX / 90 : 0, pen_info.tiltY ? (float)pen_info.tiltY / 90 : 0));
-
- mm->set_control((wParam & MK_CONTROL) != 0);
- mm->set_shift((wParam & MK_SHIFT) != 0);
- mm->set_alt(alt_mem);
-
- mm->set_button_mask(last_button_state);
-
- POINT coords; //client coords
- coords.x = GET_X_LPARAM(lParam);
- coords.y = GET_Y_LPARAM(lParam);
-
- ScreenToClient(hWnd, &coords);
-
- mm->set_position(Vector2(coords.x, coords.y));
- mm->set_global_position(Vector2(coords.x, coords.y));
-
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
-
- Point2i c(video_mode.width / 2, video_mode.height / 2);
- old_x = c.x;
- old_y = c.y;
-
- if (mm->get_position() == c) {
- center = c;
- return 0;
- }
-
- Point2i ncenter = mm->get_position();
- center = ncenter;
- POINT pos = { (int)c.x, (int)c.y };
- ClientToScreen(hWnd, &pos);
- SetCursorPos(pos.x, pos.y);
- }
-
- input->set_mouse_position(mm->get_position());
- mm->set_speed(input->get_last_mouse_speed());
-
- if (old_invalid) {
-
- old_x = mm->get_position().x;
- old_y = mm->get_position().y;
- old_invalid = false;
- }
-
- mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
- old_x = mm->get_position().x;
- old_y = mm->get_position().y;
- if (window_has_focus && main_loop)
- input->parse_input_event(mm);
-
- return 0; //Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event
- } break;
- case WM_MOUSEMOVE: {
- if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) {
- break;
- }
-
- if (input->is_emulating_mouse_from_touch()) {
- // Universal translation enabled; ignore OS translation
- LPARAM extra = GetMessageExtraInfo();
- if (IsTouchEvent(extra)) {
- break;
- }
- }
-
- if (outside) {
- //mouse enter
-
- if (main_loop && mouse_mode != MOUSE_MODE_CAPTURED)
- main_loop->notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER);
-
- CursorShape c = cursor_shape;
- cursor_shape = CURSOR_MAX;
- set_cursor_shape(c);
- outside = false;
-
- //Once-Off notification, must call again....
- TRACKMOUSEEVENT tme;
- tme.cbSize = sizeof(TRACKMOUSEEVENT);
- tme.dwFlags = TME_LEAVE;
- tme.hwndTrack = hWnd;
- tme.dwHoverTime = HOVER_DEFAULT;
- TrackMouseEvent(&tme);
- }
-
- // Don't calculate relative mouse movement if we don't have focus in CAPTURED mode.
- if (!window_has_focus && mouse_mode == MOUSE_MODE_CAPTURED)
- break;
-
- Ref<InputEventMouseMotion> mm;
- mm.instance();
-
- mm->set_control((wParam & MK_CONTROL) != 0);
- mm->set_shift((wParam & MK_SHIFT) != 0);
- mm->set_alt(alt_mem);
-
- mm->set_button_mask(last_button_state);
-
- 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)));
-
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
-
- Point2i c(video_mode.width / 2, video_mode.height / 2);
- old_x = c.x;
- old_y = c.y;
-
- if (mm->get_position() == c) {
- center = c;
- return 0;
- }
-
- Point2i ncenter = mm->get_position();
- center = ncenter;
- POINT pos = { (int)c.x, (int)c.y };
- ClientToScreen(hWnd, &pos);
- SetCursorPos(pos.x, pos.y);
- }
-
- input->set_mouse_position(mm->get_position());
- mm->set_speed(input->get_last_mouse_speed());
-
- if (old_invalid) {
-
- old_x = mm->get_position().x;
- old_y = mm->get_position().y;
- old_invalid = false;
- }
-
- mm->set_relative(Vector2(mm->get_position() - Vector2(old_x, old_y)));
- old_x = mm->get_position().x;
- old_y = mm->get_position().y;
- if (window_has_focus && main_loop)
- input->accumulate_input_event(mm);
-
- } 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 (IsTouchEvent(extra)) {
- break;
- }
- }
- [[fallthrough]];
- case WM_MBUTTONDOWN:
- case WM_MBUTTONUP:
- case WM_RBUTTONDOWN:
- case WM_RBUTTONUP:
- case WM_MOUSEWHEEL:
- case WM_MOUSEHWHEEL:
- case WM_LBUTTONDBLCLK:
- case WM_MBUTTONDBLCLK:
- case WM_RBUTTONDBLCLK:
- 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->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_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_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;
- }
- }
-
- 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;
- if (mb->is_pressed())
- last_button_state |= (1 << (mb->get_button_index() - 1));
- else
- last_button_state &= ~(1 << (mb->get_button_index() - 1));
- mb->set_button_mask(last_button_state);
-
- mb->set_position(Vector2(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)));
-
- if (mouse_mode == MOUSE_MODE_CAPTURED && !use_raw_input) {
-
- mb->set_position(Vector2(old_x, old_y));
- }
-
- if (uMsg != WM_MOUSEWHEEL && uMsg != WM_MOUSEHWHEEL) {
- if (mb->is_pressed()) {
-
- if (++pressrc > 0 && mouse_mode != MOUSE_MODE_CAPTURED)
- SetCapture(hWnd);
- } else {
-
- if (--pressrc <= 0) {
- if (mouse_mode != MOUSE_MODE_CAPTURED) {
- ReleaseCapture();
- }
- pressrc = 0;
- }
- }
- } else {
- // for reasons unknown to mankind, wheel comes in screen coordinates
- 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));
- }
-
- mb->set_global_position(mb->get_position());
-
- if (main_loop) {
- input->accumulate_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();
- last_button_state &= ~(1 << (mbd->get_button_index() - 1));
- mbd->set_button_mask(last_button_state);
- mbd->set_pressed(false);
- input->accumulate_input_event(mbd);
- }
- }
- } break;
-
- case WM_MOVE: {
- if (!IsIconic(hWnd)) {
- int x = LOWORD(lParam);
- int y = HIWORD(lParam);
- last_pos = Point2(x, y);
- }
- } break;
-
- case WM_SIZE: {
- // Ignore size when a SIZE_MINIMIZED event is triggered
- if (wParam != SIZE_MINIMIZED) {
- int window_w = LOWORD(lParam);
- int window_h = HIWORD(lParam);
- if (window_w > 0 && window_h > 0 && !preserve_window_size) {
- video_mode.width = window_w;
- video_mode.height = window_h;
- } else {
- preserve_window_size = false;
- set_window_size(Size2(video_mode.width, video_mode.height));
- }
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
- context_vulkan->window_resize(0, video_mode.width, video_mode.height);
- }
-#endif
- }
-
- if (wParam == SIZE_MAXIMIZED) {
- maximized = true;
- minimized = false;
- } else if (wParam == SIZE_MINIMIZED) {
- maximized = false;
- minimized = true;
- } else if (wParam == SIZE_RESTORED) {
- maximized = false;
- minimized = false;
- }
- if (is_layered_allowed() && layered_window) {
- DeleteObject(hBitmap);
-
- RECT r;
- GetWindowRect(hWnd, &r);
- dib_size = Size2i(r.right - r.left, r.bottom - r.top);
-
- BITMAPINFO bmi;
- ZeroMemory(&bmi, sizeof(BITMAPINFO));
- bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- bmi.bmiHeader.biWidth = dib_size.x;
- bmi.bmiHeader.biHeight = dib_size.y;
- bmi.bmiHeader.biPlanes = 1;
- bmi.bmiHeader.biBitCount = 32;
- bmi.bmiHeader.biCompression = BI_RGB;
- bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4;
- hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, NULL, 0x0);
- SelectObject(hDC_dib, hBitmap);
-
- ZeroMemory(dib_data, dib_size.x * dib_size.y * 4);
- }
- //return 0; // Jump Back
- } break;
-
- case WM_ENTERSIZEMOVE: {
- input->release_pressed_events();
- move_timer_id = SetTimer(hWnd, 1, USER_TIMER_MINIMUM, (TIMERPROC)NULL);
- } break;
- case WM_EXITSIZEMOVE: {
- KillTimer(hWnd, move_timer_id);
- } break;
- case WM_TIMER: {
- if (wParam == move_timer_id) {
- process_key_events();
- if (!Main::is_iterating()) {
- Main::iteration();
- }
- }
- } break;
-
- case WM_SYSKEYDOWN:
- case WM_SYSKEYUP:
- case WM_KEYUP:
- case WM_KEYDOWN: {
-
- if (wParam == VK_SHIFT)
- shift_mem = uMsg == WM_KEYDOWN;
- if (wParam == VK_CONTROL)
- control_mem = uMsg == WM_KEYDOWN;
- if (wParam == VK_MENU) {
- alt_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN);
- if (lParam & (1 << 24))
- gr_mem = alt_mem;
- }
-
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
- // When SetCapture is used, ALT+F4 hotkey is ignored by Windows, so handle it ourselves
- if (wParam == VK_F4 && alt_mem && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN)) {
- if (main_loop)
- main_loop->notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST);
- }
- }
- /*
- if (wParam==VK_WIN) TODO wtf is this?
- meta_mem=uMsg==WM_KEYDOWN;
- */
- [[fallthrough]];
- }
- case WM_CHAR: {
-
- ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);
-
- // Make sure we don't include modifiers for the modifier key itself.
- KeyEvent ke;
- ke.shift = (wParam != VK_SHIFT) ? shift_mem : false;
- ke.alt = (!(wParam == VK_MENU && (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN))) ? alt_mem : false;
- ke.control = (wParam != VK_CONTROL) ? control_mem : false;
- ke.meta = meta_mem;
- ke.uMsg = uMsg;
-
- if (ke.uMsg == WM_SYSKEYDOWN)
- ke.uMsg = WM_KEYDOWN;
- if (ke.uMsg == WM_SYSKEYUP)
- ke.uMsg = WM_KEYUP;
-
- ke.wParam = wParam;
- ke.lParam = lParam;
- key_event_buffer[key_event_pos++] = ke;
-
- } break;
- case WM_INPUTLANGCHANGEREQUEST: {
-
- // FIXME: Do something?
- } break;
-
- case WM_TOUCH: {
-
- BOOL bHandled = FALSE;
- UINT cInputs = LOWORD(wParam);
- PTOUCHINPUT pInputs = memnew_arr(TOUCHINPUT, cInputs);
- if (pInputs) {
- 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(touch_pos.x, touch_pos.y, ti.dwID);
- } else if (ti.dwFlags & (TOUCHEVENTF_UP | TOUCHEVENTF_DOWN)) {
-
- _touch_event(ti.dwFlags & TOUCHEVENTF_DOWN, touch_pos.x, touch_pos.y, ti.dwID);
- };
- }
- bHandled = TRUE;
- } else {
- /* handle the error here */
- }
- memdelete_arr(pInputs);
- } else {
- /* handle the error here, probably out of memory */
- }
- if (bHandled) {
- CloseTouchInputHandle((HTOUCHINPUT)lParam);
- return 0;
- };
-
- } break;
-
- case WM_DEVICECHANGE: {
-
- joypad->probe_joypads();
- } break;
- case WM_SETCURSOR: {
- if (LOWORD(lParam) == HTCLIENT) {
- if (window_has_focus && (mouse_mode == MOUSE_MODE_HIDDEN || mouse_mode == MOUSE_MODE_CAPTURED)) {
- //Hide the cursor
- if (hCursor == NULL)
- hCursor = SetCursor(NULL);
- else
- SetCursor(NULL);
- } else {
- if (hCursor != NULL) {
- CursorShape c = cursor_shape;
- cursor_shape = CURSOR_MAX;
- set_cursor_shape(c);
- hCursor = NULL;
- }
- }
- }
-
- } break;
- case WM_DROPFILES: {
-
- HDROP hDropInfo = (HDROP)wParam;
- const int buffsize = 4096;
- wchar_t buf[buffsize];
-
- int fcount = DragQueryFileW(hDropInfo, 0xFFFFFFFF, NULL, 0);
-
- Vector<String> files;
-
- for (int i = 0; i < fcount; i++) {
-
- DragQueryFileW(hDropInfo, i, buf, buffsize);
- String file = buf;
- files.push_back(file);
- }
-
- if (files.size() && main_loop) {
- main_loop->drop_files(files, 0);
- }
-
- } break;
-
- default: {
-
- if (user_proc) {
-
- return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
- };
- };
- }
-
- return DefWindowProcW(hWnd, uMsg, wParam, lParam);
-}
-
-LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
-
- OS_Windows *os_win = static_cast<OS_Windows *>(OS::get_singleton());
- if (os_win)
- return os_win->WndProc(hWnd, uMsg, wParam, lParam);
- else
- return DefWindowProcW(hWnd, uMsg, wParam, lParam);
-}
-
-void OS_Windows::process_key_events() {
-
- for (int i = 0; i < key_event_pos; i++) {
-
- KeyEvent &ke = key_event_buffer[i];
- switch (ke.uMsg) {
-
- case WM_CHAR: {
- if ((i == 0 && ke.uMsg == WM_CHAR) || (i > 0 && key_event_buffer[i - 1].uMsg == WM_CHAR)) {
- Ref<InputEventKey> k;
- k.instance();
-
- k->set_shift(ke.shift);
- k->set_alt(ke.alt);
- k->set_control(ke.control);
- k->set_metakey(ke.meta);
- k->set_pressed(true);
- k->set_keycode(KeyMappingWindows::get_keysym(ke.wParam));
- k->set_physical_keycode(KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24)));
- k->set_unicode(ke.wParam);
- if (k->get_unicode() && gr_mem) {
- k->set_alt(false);
- k->set_control(false);
- }
-
- if (k->get_unicode() < 32)
- k->set_unicode(0);
-
- input->accumulate_input_event(k);
- }
-
- //do nothing
- } break;
- case WM_KEYUP:
- case WM_KEYDOWN: {
-
- Ref<InputEventKey> k;
- k.instance();
-
- k->set_shift(ke.shift);
- k->set_alt(ke.alt);
- k->set_control(ke.control);
- k->set_metakey(ke.meta);
-
- k->set_pressed(ke.uMsg == WM_KEYDOWN);
-
- if ((ke.lParam & (1 << 24)) && (ke.wParam == VK_RETURN)) {
- // Special case for Numpad Enter key
- k->set_keycode(KEY_KP_ENTER);
- } else {
- k->set_keycode(KeyMappingWindows::get_keysym(ke.wParam));
- }
-
- k->set_physical_keycode(KeyMappingWindows::get_scansym((ke.lParam >> 16) & 0xFF, ke.lParam & (1 << 24)));
-
- if (i + 1 < key_event_pos && key_event_buffer[i + 1].uMsg == WM_CHAR) {
- k->set_unicode(key_event_buffer[i + 1].wParam);
- }
- if (k->get_unicode() && gr_mem) {
- k->set_alt(false);
- k->set_control(false);
- }
-
- if (k->get_unicode() < 32)
- k->set_unicode(0);
-
- k->set_echo((ke.uMsg == WM_KEYDOWN && (ke.lParam & (1 << 30))));
-
- input->accumulate_input_event(k);
-
- } break;
- }
- }
-
- key_event_pos = 0;
-}
-
-enum _MonitorDpiType {
- MDT_Effective_DPI = 0,
- MDT_Angular_DPI = 1,
- MDT_Raw_DPI = 2,
- MDT_Default = MDT_Effective_DPI
-};
-
-static int QueryDpiForMonitor(HMONITOR hmon, _MonitorDpiType dpiType = MDT_Default) {
-
- int dpiX = 96, dpiY = 96;
-
- static HMODULE Shcore = NULL;
- typedef HRESULT(WINAPI * GetDPIForMonitor_t)(HMONITOR hmonitor, _MonitorDpiType dpiType, UINT * dpiX, UINT * dpiY);
- static GetDPIForMonitor_t getDPIForMonitor = NULL;
-
- if (Shcore == NULL) {
- Shcore = LoadLibraryW(L"Shcore.dll");
- getDPIForMonitor = Shcore ? (GetDPIForMonitor_t)GetProcAddress(Shcore, "GetDpiForMonitor") : NULL;
-
- if ((Shcore == NULL) || (getDPIForMonitor == NULL)) {
- if (Shcore)
- FreeLibrary(Shcore);
- Shcore = (HMODULE)INVALID_HANDLE_VALUE;
- }
- }
-
- UINT x = 0, y = 0;
- HRESULT hr = E_FAIL;
- if (hmon && (Shcore != (HMODULE)INVALID_HANDLE_VALUE)) {
- hr = getDPIForMonitor(hmon, dpiType /*MDT_Effective_DPI*/, &x, &y);
- if (SUCCEEDED(hr) && (x > 0) && (y > 0)) {
-
- dpiX = (int)x;
- dpiY = (int)y;
- }
- } else {
- static int overallX = 0, overallY = 0;
- if (overallX <= 0 || overallY <= 0) {
- HDC hdc = GetDC(NULL);
- if (hdc) {
- overallX = GetDeviceCaps(hdc, LOGPIXELSX);
- overallY = GetDeviceCaps(hdc, LOGPIXELSY);
- ReleaseDC(NULL, hdc);
- }
- }
- if (overallX > 0 && overallY > 0) {
- dpiX = overallX;
- dpiY = overallY;
- }
- }
-
- return (dpiX + dpiY) / 2;
-}
-
-typedef enum _SHC_PROCESS_DPI_AWARENESS {
- SHC_PROCESS_DPI_UNAWARE = 0,
- SHC_PROCESS_SYSTEM_DPI_AWARE = 1,
- SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2
-} SHC_PROCESS_DPI_AWARENESS;
-
-int OS_Windows::get_current_video_driver() const {
- return video_driver_index;
-}
-
-Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) {
-
main_loop = NULL;
- outside = true;
- window_has_focus = true;
- WNDCLASSEXW wc;
-
- if (is_hidpi_allowed()) {
- HMODULE Shcore = LoadLibraryW(L"Shcore.dll");
-
- if (Shcore != NULL) {
- typedef HRESULT(WINAPI * SetProcessDpiAwareness_t)(SHC_PROCESS_DPI_AWARENESS);
-
- SetProcessDpiAwareness_t SetProcessDpiAwareness = (SetProcessDpiAwareness_t)GetProcAddress(Shcore, "SetProcessDpiAwareness");
-
- if (SetProcessDpiAwareness) {
- SetProcessDpiAwareness(SHC_PROCESS_SYSTEM_DPI_AWARE);
- }
- }
- }
-
- video_mode = p_desired;
- //printf("**************** desired %s, mode %s\n", p_desired.fullscreen?"true":"false", video_mode.fullscreen?"true":"false");
- RECT WindowRect;
-
- WindowRect.left = 0;
- WindowRect.right = video_mode.width;
- WindowRect.top = 0;
- WindowRect.bottom = video_mode.height;
-
- memset(&wc, 0, sizeof(WNDCLASSEXW));
- wc.cbSize = sizeof(WNDCLASSEXW);
- wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
- wc.lpfnWndProc = (WNDPROC)::WndProc;
- wc.cbClsExtra = 0;
- wc.cbWndExtra = 0;
- //wc.hInstance = hInstance;
- wc.hInstance = godot_hinstance ? godot_hinstance : GetModuleHandle(NULL);
- wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
- wc.hCursor = NULL; //LoadCursor(NULL, IDC_ARROW);
- wc.hbrBackground = NULL;
- wc.lpszMenuName = NULL;
- wc.lpszClassName = L"Engine";
-
- if (!RegisterClassExW(&wc)) {
- MessageBox(NULL, "Failed To Register The Window Class.", "ERROR", MB_OK | MB_ICONEXCLAMATION);
- return ERR_UNAVAILABLE;
- }
-
- use_raw_input = true;
-
- RAWINPUTDEVICE Rid[1];
-
- Rid[0].usUsagePage = 0x01;
- Rid[0].usUsage = 0x02;
- Rid[0].dwFlags = 0;
- Rid[0].hwndTarget = 0;
-
- if (RegisterRawInputDevices(Rid, 1, sizeof(Rid[0])) == FALSE) {
- //registration failed.
- use_raw_input = false;
- }
-
- pre_fs_valid = true;
- if (video_mode.fullscreen) {
-
- /* this returns DPI unaware size, commenting
- DEVMODE current;
- memset(&current, 0, sizeof(current));
- EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &current);
-
- 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;
-
- /* DEVMODE dmScreenSettings;
- memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
- dmScreenSettings.dmSize=sizeof(dmScreenSettings);
- dmScreenSettings.dmPelsWidth = video_mode.width;
- dmScreenSettings.dmPelsHeight = video_mode.height;
- dmScreenSettings.dmBitsPerPel = current.dmBitsPerPel;
- dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
-
- LONG err = ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN);
- if (err!=DISP_CHANGE_SUCCESSFUL) {
-
- video_mode.fullscreen=false;
- }*/
- pre_fs_valid = false;
- }
-
- DWORD dwExStyle;
- DWORD dwStyle;
-
- if (video_mode.fullscreen || video_mode.borderless_window) {
-
- dwExStyle = WS_EX_APPWINDOW;
- dwStyle = WS_POPUP;
-
- } else {
- dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
- dwStyle = WS_OVERLAPPEDWINDOW;
- if (!video_mode.resizable) {
- dwStyle &= ~WS_THICKFRAME;
- dwStyle &= ~WS_MAXIMIZEBOX;
- }
- }
-
- AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle);
-
- char *windowid;
-#ifdef MINGW_ENABLED
- windowid = getenv("GODOT_WINDOWID");
-#else
- size_t len;
- _dupenv_s(&windowid, &len, "GODOT_WINDOWID");
-#endif
-
- if (windowid) {
-
-// strtoull on mingw
-#ifdef MINGW_ENABLED
- hWnd = (HWND)strtoull(windowid, NULL, 0);
-#else
- hWnd = (HWND)_strtoui64(windowid, NULL, 0);
-#endif
- free(windowid);
- SetLastError(0);
- user_proc = (WNDPROC)GetWindowLongPtr(hWnd, GWLP_WNDPROC);
- SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)(WNDPROC)::WndProc);
- DWORD le = GetLastError();
- if (user_proc == 0 && le != 0) {
-
- printf("Error setting WNDPROC: %li\n", le);
- };
- GetWindowLongPtr(hWnd, GWLP_WNDPROC);
-
- RECT rect;
- if (!GetClientRect(hWnd, &rect)) {
- MessageBoxW(NULL, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);
- return ERR_UNAVAILABLE;
- };
- video_mode.width = rect.right;
- video_mode.height = rect.bottom;
- video_mode.fullscreen = false;
- } else {
-
- hWnd = CreateWindowExW(
- dwExStyle,
- L"Engine", L"",
- dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
- (GetSystemMetrics(SM_CXSCREEN) - WindowRect.right) / 2,
- (GetSystemMetrics(SM_CYSCREEN) - WindowRect.bottom) / 2,
- WindowRect.right - WindowRect.left,
- WindowRect.bottom - WindowRect.top,
- NULL, NULL, hInstance, NULL);
- if (!hWnd) {
- MessageBoxW(NULL, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);
- return ERR_UNAVAILABLE;
- }
- };
-
- if (video_mode.always_on_top) {
- SetWindowPos(hWnd, video_mode.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
- }
-
- //!!!!!!!!!!!!!!!!!!!!!!!!!!
- //TODO - do Vulkan and GLES2 support checks, driver selection and fallback
- video_driver_index = p_video_driver;
- print_verbose("Driver: " + String(get_video_driver_name(video_driver_index)) + " [" + itos(video_driver_index) + "]");
- //!!!!!!!!!!!!!!!!!!!!!!!!!!
-
- // Init context and rendering device
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
-
- context_gles2 = memnew(ContextGL_Windows(hWnd, false));
-
- if (context_gles2->initialize() != OK) {
- memdelete(context_gles2);
- context_gles2 = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
-
- context_gles2->set_use_vsync(video_mode.use_vsync);
- set_vsync_via_compositor(video_mode.vsync_via_compositor);
-
- if (RasterizerGLES2::is_viable() == OK) {
- RasterizerGLES2::register_config();
- RasterizerGLES2::make_current();
- } else {
- memdelete(context_gles2);
- context_gles2 = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
-
- context_vulkan = memnew(VulkanContextWindows);
- if (context_vulkan->initialize() != OK) {
- memdelete(context_vulkan);
- context_vulkan = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
- if (context_vulkan->window_create(hWnd, hInstance, get_video_mode().width, get_video_mode().height) == -1) {
- memdelete(context_vulkan);
- context_vulkan = NULL;
- ERR_FAIL_V(ERR_UNAVAILABLE);
- }
-
- //temporary
- rendering_device_vulkan = memnew(RenderingDeviceVulkan);
- rendering_device_vulkan->initialize(context_vulkan);
-
- RasterizerRD::make_current();
- }
-#endif
-
- visual_server = memnew(VisualServerRaster);
- if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) {
- visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD));
- }
-
- visual_server->init();
-
- input = memnew(InputDefault);
- joypad = memnew(JoypadWindows(input, &hWnd));
-
- AudioDriverManager::initialize(p_audio_driver);
-
- TRACKMOUSEEVENT tme;
- tme.cbSize = sizeof(TRACKMOUSEEVENT);
- tme.dwFlags = TME_LEAVE;
- tme.hwndTrack = hWnd;
- tme.dwHoverTime = HOVER_DEFAULT;
- TrackMouseEvent(&tme);
-
- RegisterTouchWindow(hWnd, 0);
-
- _ensure_user_data_dir();
-
- DragAcceptFiles(hWnd, true);
-
- move_timer_id = 1;
-
- if (!is_no_window_mode_enabled()) {
- ShowWindow(hWnd, SW_SHOW); // Show The Window
- SetForegroundWindow(hWnd); // Slightly Higher Priority
- SetFocus(hWnd); // Sets Keyboard Focus To
- }
-
- if (p_desired.layered) {
- set_window_per_pixel_transparency_enabled(true);
- }
-
- // IME
- im_himc = ImmGetContext(hWnd);
- ImmReleaseContext(hWnd, im_himc);
-
- im_position = Vector2();
-
- set_ime_active(false);
-
- if (!OS::get_singleton()->is_in_low_processor_usage_mode()) {
- //SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
- SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
- DWORD index = 0;
- HANDLE handle = AvSetMmThreadCharacteristics("Games", &index);
- if (handle)
- AvSetMmThreadPriority(handle, AVRT_PRIORITY_CRITICAL);
-
- // This is needed to make sure that background work does not starve the main thread.
- // This is only setting priority of this thread, not the whole process.
- SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
- }
-
- update_real_mouse_position();
-
- return OK;
}
-void OS_Windows::set_clipboard(const String &p_text) {
-
- // Convert LF line endings to CRLF in clipboard content
- // Otherwise, line endings won't be visible when pasted in other software
- String text = p_text.replace("\n", "\r\n");
-
- if (!OpenClipboard(hWnd)) {
- ERR_FAIL_MSG("Unable to open clipboard.");
- }
- EmptyClipboard();
-
- HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (text.length() + 1) * sizeof(CharType));
- ERR_FAIL_COND_MSG(mem == NULL, "Unable to allocate memory for clipboard contents.");
-
- LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem);
- memcpy(lptstrCopy, text.c_str(), (text.length() + 1) * sizeof(CharType));
- GlobalUnlock(mem);
-
- SetClipboardData(CF_UNICODETEXT, mem);
-
- // set the CF_TEXT version (not needed?)
- CharString utf8 = text.utf8();
- mem = GlobalAlloc(GMEM_MOVEABLE, utf8.length() + 1);
- ERR_FAIL_COND_MSG(mem == NULL, "Unable to allocate memory for clipboard contents.");
-
- LPTSTR ptr = (LPTSTR)GlobalLock(mem);
- memcpy(ptr, utf8.get_data(), utf8.length());
- ptr[utf8.length()] = 0;
- GlobalUnlock(mem);
-
- SetClipboardData(CF_TEXT, mem);
-
- CloseClipboard();
-};
-
-String OS_Windows::get_clipboard() const {
-
- String ret;
- if (!OpenClipboard(hWnd)) {
- ERR_FAIL_V_MSG("", "Unable to open clipboard.");
- };
-
- if (IsClipboardFormatAvailable(CF_UNICODETEXT)) {
-
- HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
- if (mem != NULL) {
-
- LPWSTR ptr = (LPWSTR)GlobalLock(mem);
- if (ptr != NULL) {
-
- ret = String((CharType *)ptr);
- GlobalUnlock(mem);
- };
- };
-
- } else if (IsClipboardFormatAvailable(CF_TEXT)) {
-
- HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
- if (mem != NULL) {
-
- LPTSTR ptr = (LPTSTR)GlobalLock(mem);
- if (ptr != NULL) {
-
- ret.parse_utf8((const char *)ptr);
- GlobalUnlock(mem);
- };
- };
- };
-
- CloseClipboard();
-
- return ret;
-};
-
void OS_Windows::delete_main_loop() {
if (main_loop)
@@ -1629,7 +225,6 @@ void OS_Windows::delete_main_loop() {
void OS_Windows::set_main_loop(MainLoop *p_main_loop) {
- input->set_main_loop(p_main_loop);
main_loop = p_main_loop;
}
@@ -1643,38 +238,6 @@ void OS_Windows::finalize() {
memdelete(main_loop);
main_loop = NULL;
-
- memdelete(joypad);
- memdelete(input);
- touch_state.clear();
-
- cursors_cache.clear();
- visual_server->finish();
- memdelete(visual_server);
-
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
-
- if (context_gles2)
- memdelete(context_gles2);
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
-
- if (rendering_device_vulkan) {
- rendering_device_vulkan->finalize();
- memdelete(rendering_device_vulkan);
- }
-
- if (context_vulkan)
- memdelete(context_vulkan);
- }
-#endif
-
- if (user_proc) {
- SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc);
- };
}
void OS_Windows::finalize_core() {
@@ -1685,576 +248,6 @@ void OS_Windows::finalize_core() {
NetSocketPosix::cleanup();
}
-void OS_Windows::alert(const String &p_alert, const String &p_title) {
-
- if (!is_no_window_mode_enabled())
- MessageBoxW(NULL, p_alert.c_str(), p_title.c_str(), MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
- else
- print_line("ALERT: " + p_alert);
-}
-
-void OS_Windows::set_mouse_mode(MouseMode p_mode) {
-
- if (mouse_mode == p_mode)
- return;
-
- _set_mouse_mode_impl(p_mode);
-
- mouse_mode = p_mode;
-}
-
-void OS_Windows::_set_mouse_mode_impl(MouseMode p_mode) {
-
- if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED) {
- RECT clipRect;
- GetClientRect(hWnd, &clipRect);
- ClientToScreen(hWnd, (POINT *)&clipRect.left);
- ClientToScreen(hWnd, (POINT *)&clipRect.right);
- ClipCursor(&clipRect);
- if (p_mode == MOUSE_MODE_CAPTURED) {
- center = Point2i(video_mode.width / 2, video_mode.height / 2);
- POINT pos = { (int)center.x, (int)center.y };
- ClientToScreen(hWnd, &pos);
- SetCursorPos(pos.x, pos.y);
- SetCapture(hWnd);
- }
- } else {
- ReleaseCapture();
- ClipCursor(NULL);
- }
-
- if (p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_HIDDEN) {
- hCursor = SetCursor(NULL);
- } else {
- CursorShape c = cursor_shape;
- cursor_shape = CURSOR_MAX;
- set_cursor_shape(c);
- }
-}
-OS_Windows::MouseMode OS_Windows::get_mouse_mode() const {
-
- return mouse_mode;
-}
-
-void OS_Windows::warp_mouse_position(const Point2 &p_to) {
-
- if (mouse_mode == MOUSE_MODE_CAPTURED) {
-
- old_x = p_to.x;
- old_y = p_to.y;
- } else {
-
- POINT p;
- p.x = p_to.x;
- p.y = p_to.y;
- ClientToScreen(hWnd, &p);
-
- SetCursorPos(p.x, p.y);
- }
-}
-
-Point2 OS_Windows::get_mouse_position() const {
-
- return Point2(old_x, old_y);
-}
-
-void OS_Windows::update_real_mouse_position() {
-
- POINT mouse_pos;
- if (GetCursorPos(&mouse_pos) && ScreenToClient(hWnd, &mouse_pos)) {
- if (mouse_pos.x > 0 && mouse_pos.y > 0 && mouse_pos.x <= video_mode.width && mouse_pos.y <= video_mode.height) {
- old_x = mouse_pos.x;
- old_y = mouse_pos.y;
- old_invalid = false;
- input->set_mouse_position(Point2i(mouse_pos.x, mouse_pos.y));
- }
- }
-}
-
-int OS_Windows::get_mouse_button_state() const {
-
- return last_button_state;
-}
-
-void OS_Windows::set_window_title(const String &p_title) {
-
- SetWindowTextW(hWnd, p_title.c_str());
-}
-
-void OS_Windows::set_video_mode(const VideoMode &p_video_mode, int p_screen) {
-}
-
-OS::VideoMode OS_Windows::get_video_mode(int p_screen) const {
-
- return video_mode;
-}
-void OS_Windows::get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen) const {
-}
-
-static BOOL CALLBACK _MonitorEnumProcCount(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
-
- int *data = (int *)dwData;
- (*data)++;
- return TRUE;
-}
-
-int OS_Windows::get_screen_count() const {
-
- int data = 0;
- EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcCount, (LPARAM)&data);
- return data;
-}
-
-typedef struct {
- int count;
- int screen;
- HMONITOR monitor;
-} EnumScreenData;
-
-static BOOL CALLBACK _MonitorEnumProcScreen(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
-
- EnumScreenData *data = (EnumScreenData *)dwData;
- if (data->monitor == hMonitor) {
- data->screen = data->count;
- }
-
- data->count++;
- return TRUE;
-}
-
-int OS_Windows::get_current_screen() const {
-
- EnumScreenData data = { 0, 0, MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST) };
- EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcScreen, (LPARAM)&data);
- return data.screen;
-}
-
-void OS_Windows::set_current_screen(int p_screen) {
-
- Vector2 ofs = get_window_position() - get_screen_position(get_current_screen());
- set_window_position(ofs + get_screen_position(p_screen));
-}
-
-static BOOL CALLBACK _MonitorEnumProcPos(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
-
- EnumPosData *data = (EnumPosData *)dwData;
- if (data->count == data->screen) {
- data->pos.x = lprcMonitor->left;
- data->pos.y = lprcMonitor->top;
- }
-
- data->count++;
- return TRUE;
-}
-
-Point2 OS_Windows::get_screen_position(int p_screen) const {
-
- EnumPosData data = { 0, p_screen == -1 ? get_current_screen() : p_screen, Point2() };
- EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcPos, (LPARAM)&data);
- return data.pos;
-}
-
-Size2 OS_Windows::get_screen_size(int p_screen) const {
-
- EnumSizeData data = { 0, p_screen == -1 ? get_current_screen() : p_screen, Size2() };
- EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcSize, (LPARAM)&data);
- return data.size;
-}
-
-typedef struct {
- int count;
- int screen;
- int dpi;
-} EnumDpiData;
-
-static BOOL CALLBACK _MonitorEnumProcDpi(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
-
- EnumDpiData *data = (EnumDpiData *)dwData;
- if (data->count == data->screen) {
- data->dpi = QueryDpiForMonitor(hMonitor);
- }
-
- data->count++;
- return TRUE;
-}
-
-int OS_Windows::get_screen_dpi(int p_screen) const {
-
- EnumDpiData data = { 0, p_screen == -1 ? get_current_screen() : p_screen, 72 };
- EnumDisplayMonitors(NULL, NULL, _MonitorEnumProcDpi, (LPARAM)&data);
- return data.dpi;
-}
-
-Point2 OS_Windows::get_window_position() const {
-
- if (minimized) {
- return last_pos;
- }
-
- RECT r;
- GetWindowRect(hWnd, &r);
- return Point2(r.left, r.top);
-}
-
-void OS_Windows::set_window_position(const Point2 &p_position) {
-
- if (video_mode.fullscreen) return;
- RECT r;
- GetWindowRect(hWnd, &r);
- MoveWindow(hWnd, p_position.x, p_position.y, r.right - r.left, r.bottom - r.top, TRUE);
-
- // Don't let the mouse leave the window when moved
- if (mouse_mode == MOUSE_MODE_CONFINED) {
- RECT rect;
- GetClientRect(hWnd, &rect);
- ClientToScreen(hWnd, (POINT *)&rect.left);
- ClientToScreen(hWnd, (POINT *)&rect.right);
- ClipCursor(&rect);
- }
-
- last_pos = p_position;
- update_real_mouse_position();
-}
-
-Size2 OS_Windows::get_window_size() const {
-
- if (minimized) {
- return Size2(video_mode.width, video_mode.height);
- }
-
- RECT r;
- if (GetClientRect(hWnd, &r)) { // Only area inside of window border
- return Size2(r.right - r.left, r.bottom - r.top);
- }
- return Size2();
-}
-
-Size2 OS_Windows::get_max_window_size() const {
- return max_size;
-}
-
-Size2 OS_Windows::get_min_window_size() const {
- return min_size;
-}
-
-void OS_Windows::set_min_window_size(const Size2 p_size) {
-
- if ((p_size != Size2()) && (max_size != Size2()) && ((p_size.x > max_size.x) || (p_size.y > max_size.y))) {
- ERR_PRINT("Minimum window size can't be larger than maximum window size!");
- return;
- }
- min_size = p_size;
-}
-
-void OS_Windows::set_max_window_size(const Size2 p_size) {
-
- if ((p_size != Size2()) && ((p_size.x < min_size.x) || (p_size.y < min_size.y))) {
- ERR_PRINT("Maximum window size can't be smaller than minimum window size!");
- return;
- }
- max_size = p_size;
-}
-
-Size2 OS_Windows::get_real_window_size() const {
-
- RECT r;
- if (GetWindowRect(hWnd, &r)) { // Includes area of the window border
- return Size2(r.right - r.left, r.bottom - r.top);
- }
- return Size2();
-}
-
-void OS_Windows::set_window_size(const Size2 p_size) {
-
- int w = p_size.width;
- int h = p_size.height;
-
- video_mode.width = w;
- video_mode.height = h;
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
- context_vulkan->window_resize(0, video_mode.width, video_mode.height);
- }
-#endif
-
- if (video_mode.fullscreen) {
- return;
- }
-
- RECT rect;
- GetWindowRect(hWnd, &rect);
-
- if (!video_mode.borderless_window) {
- RECT crect;
- GetClientRect(hWnd, &crect);
-
- w += (rect.right - rect.left) - (crect.right - crect.left);
- h += (rect.bottom - rect.top) - (crect.bottom - crect.top);
- }
-
- MoveWindow(hWnd, rect.left, rect.top, w, h, TRUE);
-
- // Don't let the mouse leave the window when resizing to a smaller resolution
- if (mouse_mode == MOUSE_MODE_CONFINED) {
- RECT crect;
- GetClientRect(hWnd, &crect);
- ClientToScreen(hWnd, (POINT *)&crect.left);
- ClientToScreen(hWnd, (POINT *)&crect.right);
- ClipCursor(&crect);
- }
-}
-void OS_Windows::set_window_fullscreen(bool p_enabled) {
-
- if (video_mode.fullscreen == p_enabled)
- return;
-
- if (layered_window)
- set_window_per_pixel_transparency_enabled(false);
-
- if (p_enabled) {
-
- was_maximized = maximized;
-
- if (pre_fs_valid) {
- GetWindowRect(hWnd, &pre_fs_rect);
- }
-
- int cs = get_current_screen();
- Point2 pos = get_screen_position(cs);
- Size2 size = get_screen_size(cs);
-
- video_mode.fullscreen = true;
-
- _update_window_style(false);
-
- MoveWindow(hWnd, pos.x, pos.y, size.width, size.height, TRUE);
-
- } else {
-
- RECT rect;
-
- video_mode.fullscreen = false;
-
- if (pre_fs_valid) {
- rect = pre_fs_rect;
- } else {
- rect.left = 0;
- rect.right = video_mode.width;
- rect.top = 0;
- rect.bottom = video_mode.height;
- }
-
- _update_window_style(false, was_maximized);
-
- MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
-
- pre_fs_valid = true;
- }
-}
-bool OS_Windows::is_window_fullscreen() const {
-
- return video_mode.fullscreen;
-}
-void OS_Windows::set_window_resizable(bool p_enabled) {
-
- if (video_mode.resizable == p_enabled)
- return;
-
- video_mode.resizable = p_enabled;
-
- _update_window_style();
-}
-bool OS_Windows::is_window_resizable() const {
-
- return video_mode.resizable;
-}
-void OS_Windows::set_window_minimized(bool p_enabled) {
-
- if (p_enabled) {
- maximized = false;
- minimized = true;
- ShowWindow(hWnd, SW_MINIMIZE);
- } else {
- ShowWindow(hWnd, SW_RESTORE);
- maximized = false;
- minimized = false;
- }
-}
-bool OS_Windows::is_window_minimized() const {
-
- return minimized;
-}
-void OS_Windows::set_window_maximized(bool p_enabled) {
-
- if (p_enabled) {
- maximized = true;
- minimized = false;
- ShowWindow(hWnd, SW_MAXIMIZE);
- } else {
- ShowWindow(hWnd, SW_RESTORE);
- maximized = false;
- minimized = false;
- }
-}
-bool OS_Windows::is_window_maximized() const {
-
- return maximized;
-}
-
-void OS_Windows::set_window_always_on_top(bool p_enabled) {
- if (video_mode.always_on_top == p_enabled)
- return;
-
- video_mode.always_on_top = p_enabled;
-
- _update_window_style();
-}
-
-bool OS_Windows::is_window_always_on_top() const {
- return video_mode.always_on_top;
-}
-
-bool OS_Windows::is_window_focused() const {
-
- return window_focused;
-}
-
-void OS_Windows::set_console_visible(bool p_enabled) {
- if (console_visible == p_enabled)
- return;
- ShowWindow(GetConsoleWindow(), p_enabled ? SW_SHOW : SW_HIDE);
- console_visible = p_enabled;
-}
-
-bool OS_Windows::is_console_visible() const {
- return console_visible;
-}
-
-bool OS_Windows::get_window_per_pixel_transparency_enabled() const {
-
- if (!is_layered_allowed()) return false;
- return layered_window;
-}
-
-void OS_Windows::set_window_per_pixel_transparency_enabled(bool p_enabled) {
-
- if (!is_layered_allowed()) return;
- if (layered_window != p_enabled) {
- if (p_enabled) {
- set_borderless_window(true);
- //enable per-pixel alpha
- hDC_dib = CreateCompatibleDC(GetDC(hWnd));
-
- SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
-
- RECT r;
- GetWindowRect(hWnd, &r);
- dib_size = Size2(r.right - r.left, r.bottom - r.top);
-
- BITMAPINFO bmi;
- ZeroMemory(&bmi, sizeof(BITMAPINFO));
- bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- bmi.bmiHeader.biWidth = dib_size.x;
- bmi.bmiHeader.biHeight = dib_size.y;
- bmi.bmiHeader.biPlanes = 1;
- bmi.bmiHeader.biBitCount = 32;
- bmi.bmiHeader.biCompression = BI_RGB;
- bmi.bmiHeader.biSizeImage = dib_size.x * dib_size.y * 4;
- hBitmap = CreateDIBSection(hDC_dib, &bmi, DIB_RGB_COLORS, (void **)&dib_data, NULL, 0x0);
- SelectObject(hDC_dib, hBitmap);
-
- ZeroMemory(dib_data, dib_size.x * dib_size.y * 4);
-
- layered_window = true;
- } else {
- //disable per-pixel alpha
- layered_window = false;
-
- SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
-
- //cleanup
- DeleteObject(hBitmap);
- DeleteDC(hDC_dib);
- }
- }
-}
-
-uint8_t *OS_Windows::get_layered_buffer_data() {
-
- return (is_layered_allowed() && layered_window) ? dib_data : NULL;
-}
-
-Size2 OS_Windows::get_layered_buffer_size() {
-
- return (is_layered_allowed() && layered_window) ? dib_size : Size2();
-}
-
-void OS_Windows::swap_layered_buffer() {
-
- if (is_layered_allowed() && layered_window) {
-
- //premultiply alpha
- for (int y = 0; y < dib_size.y; y++) {
- for (int x = 0; x < dib_size.x; x++) {
- float alpha = (float)dib_data[y * (int)dib_size.x * 4 + x * 4 + 3] / (float)0xFF;
- dib_data[y * (int)dib_size.x * 4 + x * 4 + 0] *= alpha;
- dib_data[y * (int)dib_size.x * 4 + x * 4 + 1] *= alpha;
- dib_data[y * (int)dib_size.x * 4 + x * 4 + 2] *= alpha;
- }
- }
- //swap layered window buffer
- POINT ptSrc = { 0, 0 };
- SIZE sizeWnd = { (long)dib_size.x, (long)dib_size.y };
- BLENDFUNCTION bf;
- bf.BlendOp = AC_SRC_OVER;
- bf.BlendFlags = 0;
- bf.AlphaFormat = AC_SRC_ALPHA;
- bf.SourceConstantAlpha = 0xFF;
- UpdateLayeredWindow(hWnd, NULL, NULL, &sizeWnd, hDC_dib, &ptSrc, 0, &bf, ULW_ALPHA);
- }
-}
-
-void OS_Windows::set_borderless_window(bool p_borderless) {
- if (video_mode.borderless_window == p_borderless)
- return;
-
- if (!p_borderless && layered_window)
- set_window_per_pixel_transparency_enabled(false);
-
- video_mode.borderless_window = p_borderless;
-
- preserve_window_size = true;
- _update_window_style();
-}
-
-bool OS_Windows::get_borderless_window() {
- return video_mode.borderless_window;
-}
-
-void OS_Windows::_update_window_style(bool p_repaint, bool p_maximized) {
- if (video_mode.fullscreen || video_mode.borderless_window) {
- SetWindowLongPtr(hWnd, GWL_STYLE, WS_SYSMENU | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE);
- } else {
- if (video_mode.resizable) {
- if (p_maximized) {
- SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_MAXIMIZE);
- } else {
- SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
- }
- } else {
- SetWindowLongPtr(hWnd, GWL_STYLE, WS_CAPTION | WS_MINIMIZEBOX | WS_POPUPWINDOW | WS_VISIBLE);
- }
- }
-
- SetWindowPos(hWnd, video_mode.always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
-
- if (p_repaint) {
- RECT rect;
- GetWindowRect(hWnd, &rect);
- MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE);
- }
-}
-
Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
String path = p_path;
@@ -2306,17 +299,6 @@ Error OS_Windows::get_dynamic_library_symbol_handle(void *p_library_handle, cons
return OK;
}
-void OS_Windows::request_attention() {
-
- FLASHWINFO info;
- info.cbSize = sizeof(FLASHWINFO);
- info.hwnd = hWnd;
- info.dwFlags = FLASHW_TRAY;
- info.dwTimeout = 0;
- info.uCount = 2;
- FlashWindowEx(&info);
-}
-
String OS_Windows::get_name() const {
return "Windows";
@@ -2448,253 +430,6 @@ uint64_t OS_Windows::get_ticks_usec() const {
return time;
}
-void OS_Windows::process_events() {
-
- MSG msg;
-
- if (!drop_events) {
- joypad->process_joypads();
- }
-
- while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
-
- TranslateMessage(&msg);
- DispatchMessageW(&msg);
- }
-
- if (!drop_events) {
- process_key_events();
- input->flush_accumulated_events();
- }
-}
-
-void OS_Windows::set_cursor_shape(CursorShape p_shape) {
-
- ERR_FAIL_INDEX(p_shape, CURSOR_MAX);
-
- if (cursor_shape == p_shape)
- return;
-
- if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) {
- cursor_shape = p_shape;
- return;
- }
-
- static const LPCTSTR win_cursors[CURSOR_MAX] = {
- IDC_ARROW,
- IDC_IBEAM,
- IDC_HAND, //finger
- IDC_CROSS,
- IDC_WAIT,
- IDC_APPSTARTING,
- IDC_ARROW,
- IDC_ARROW,
- IDC_NO,
- IDC_SIZENS,
- IDC_SIZEWE,
- IDC_SIZENESW,
- IDC_SIZENWSE,
- IDC_SIZEALL,
- IDC_SIZENS,
- IDC_SIZEWE,
- IDC_HELP
- };
-
- if (cursors[p_shape] != NULL) {
- SetCursor(cursors[p_shape]);
- } else {
- SetCursor(LoadCursor(hInstance, win_cursors[p_shape]));
- }
-
- cursor_shape = p_shape;
-}
-
-OS::CursorShape OS_Windows::get_cursor_shape() const {
-
- return cursor_shape;
-}
-
-void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
-
- if (p_cursor.is_valid()) {
-
- Map<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape);
-
- if (cursor_c) {
- if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {
- set_cursor_shape(p_shape);
- return;
- }
-
- cursors_cache.erase(p_shape);
- }
-
- Ref<Texture2D> texture = p_cursor;
- Ref<AtlasTexture> atlas_texture = p_cursor;
- Ref<Image> image;
- Size2 texture_size;
- Rect2 atlas_rect;
-
- if (texture.is_valid()) {
- image = texture->get_data();
- }
-
- if (!image.is_valid() && atlas_texture.is_valid()) {
- texture = atlas_texture->get_atlas();
-
- atlas_rect.size.width = texture->get_width();
- atlas_rect.size.height = texture->get_height();
- atlas_rect.position.x = atlas_texture->get_region().position.x;
- atlas_rect.position.y = atlas_texture->get_region().position.y;
-
- texture_size.width = atlas_texture->get_region().size.x;
- texture_size.height = atlas_texture->get_region().size.y;
- } else if (image.is_valid()) {
- texture_size.width = texture->get_width();
- texture_size.height = texture->get_height();
- }
-
- ERR_FAIL_COND(!texture.is_valid());
- ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0);
- ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256);
- ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height);
-
- image = texture->get_data();
-
- ERR_FAIL_COND(!image.is_valid());
-
- UINT image_size = texture_size.width * texture_size.height;
-
- // Create the BITMAP with alpha channel
- COLORREF *buffer = (COLORREF *)memalloc(sizeof(COLORREF) * image_size);
-
- for (UINT index = 0; index < image_size; index++) {
- int row_index = floor(index / texture_size.width) + atlas_rect.position.y;
- int column_index = (index % int(texture_size.width)) + atlas_rect.position.x;
-
- if (atlas_texture.is_valid()) {
- column_index = MIN(column_index, atlas_rect.size.width - 1);
- row_index = MIN(row_index, atlas_rect.size.height - 1);
- }
-
- *(buffer + index) = image->get_pixel(column_index, row_index).to_argb32();
- }
-
- // Using 4 channels, so 4 * 8 bits
- HBITMAP bitmap = CreateBitmap(texture_size.width, texture_size.height, 1, 4 * 8, buffer);
- COLORREF clrTransparent = -1;
-
- // Create the AND and XOR masks for the bitmap
- HBITMAP hAndMask = NULL;
- HBITMAP hXorMask = NULL;
-
- GetMaskBitmaps(bitmap, clrTransparent, hAndMask, hXorMask);
-
- if (NULL == hAndMask || NULL == hXorMask) {
- memfree(buffer);
- DeleteObject(bitmap);
- return;
- }
-
- // Finally, create the icon
- ICONINFO iconinfo;
- iconinfo.fIcon = FALSE;
- iconinfo.xHotspot = p_hotspot.x;
- iconinfo.yHotspot = p_hotspot.y;
- iconinfo.hbmMask = hAndMask;
- iconinfo.hbmColor = hXorMask;
-
- if (cursors[p_shape])
- DestroyIcon(cursors[p_shape]);
-
- cursors[p_shape] = CreateIconIndirect(&iconinfo);
-
- Vector<Variant> params;
- params.push_back(p_cursor);
- params.push_back(p_hotspot);
- cursors_cache.insert(p_shape, params);
-
- if (p_shape == cursor_shape) {
- if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
- SetCursor(cursors[p_shape]);
- }
- }
-
- if (hAndMask != NULL) {
- DeleteObject(hAndMask);
- }
-
- if (hXorMask != NULL) {
- DeleteObject(hXorMask);
- }
-
- memfree(buffer);
- DeleteObject(bitmap);
- } else {
- // Reset to default system cursor
- if (cursors[p_shape]) {
- DestroyIcon(cursors[p_shape]);
- cursors[p_shape] = NULL;
- }
-
- CursorShape c = cursor_shape;
- cursor_shape = CURSOR_MAX;
- set_cursor_shape(c);
-
- cursors_cache.erase(p_shape);
- }
-}
-
-void OS_Windows::GetMaskBitmaps(HBITMAP hSourceBitmap, COLORREF clrTransparent, OUT HBITMAP &hAndMaskBitmap, OUT HBITMAP &hXorMaskBitmap) {
-
- // Get the system display DC
- HDC hDC = GetDC(NULL);
-
- // Create helper DC
- HDC hMainDC = CreateCompatibleDC(hDC);
- HDC hAndMaskDC = CreateCompatibleDC(hDC);
- HDC hXorMaskDC = CreateCompatibleDC(hDC);
-
- // Get the dimensions of the source bitmap
- BITMAP bm;
- GetObject(hSourceBitmap, sizeof(BITMAP), &bm);
-
- // Create the mask bitmaps
- hAndMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color
- hXorMaskBitmap = CreateCompatibleBitmap(hDC, bm.bmWidth, bm.bmHeight); // color
-
- // Release the system display DC
- ReleaseDC(NULL, hDC);
-
- // Select the bitmaps to helper DC
- HBITMAP hOldMainBitmap = (HBITMAP)SelectObject(hMainDC, hSourceBitmap);
- HBITMAP hOldAndMaskBitmap = (HBITMAP)SelectObject(hAndMaskDC, hAndMaskBitmap);
- HBITMAP hOldXorMaskBitmap = (HBITMAP)SelectObject(hXorMaskDC, hXorMaskBitmap);
-
- // Assign the monochrome AND mask bitmap pixels so that a pixels of the source bitmap
- // with 'clrTransparent' will be white pixels of the monochrome bitmap
- SetBkColor(hMainDC, clrTransparent);
- BitBlt(hAndMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCCOPY);
-
- // Assign the color XOR mask bitmap pixels so that a pixels of the source bitmap
- // with 'clrTransparent' will be black and rest the pixels same as corresponding
- // pixels of the source bitmap
- SetBkColor(hXorMaskDC, RGB(0, 0, 0));
- SetTextColor(hXorMaskDC, RGB(255, 255, 255));
- BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hAndMaskDC, 0, 0, SRCCOPY);
- BitBlt(hXorMaskDC, 0, 0, bm.bmWidth, bm.bmHeight, hMainDC, 0, 0, SRCAND);
-
- // Deselect bitmaps from the helper DC
- SelectObject(hMainDC, hOldMainBitmap);
- SelectObject(hAndMaskDC, hOldAndMaskBitmap);
- SelectObject(hXorMaskDC, hOldXorMaskBitmap);
-
- // Delete the helper DC
- DeleteDC(hXorMaskDC);
- DeleteDC(hAndMaskDC);
- DeleteDC(hMainDC);
-}
-
Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) {
if (p_blocking && r_pipe) {
@@ -2812,151 +547,6 @@ 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_MSG(!f, "Cannot open file with icon '" + p_filename + "'.");
-
- 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);
-
- ERR_FAIL_COND_MSG(icon_dir->idType != 1, "Invalid icon file format!");
-
- 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;
- }
- }
- }
-
- ERR_FAIL_COND_MSG(big_icon_index == -1, "No valid icons found!");
-
- if (small_icon_index == -1) {
- WARN_PRINT("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);
- ERR_FAIL_COND_MSG(!icon_big, "Could not create " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
-
- // 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);
- ERR_FAIL_COND_MSG(!icon_small, "Could not create 16x16 @" + itos(small_icon_cc) + " icon, error: " + format_error_message(GetLastError()) + ".");
-
- // 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();
- ERR_FAIL_COND_MSG(err, "Error setting ICON_SMALL: " + format_error_message(err) + ".");
-
- SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)icon_big);
- err = GetLastError();
- ERR_FAIL_COND_MSG(err, "Error setting ICON_BIG: " + format_error_message(err) + ".");
-
- memdelete(f);
- memdelete(icon_dir);
-}
-
-void OS_Windows::set_icon(const Ref<Image> &p_icon) {
-
- ERR_FAIL_COND(!p_icon.is_valid());
- Ref<Image> icon = p_icon->duplicate();
- if (icon->get_format() != Image::FORMAT_RGBA8)
- icon->convert(Image::FORMAT_RGBA8);
- int w = icon->get_width();
- int h = icon->get_height();
-
- /* Create temporary bitmap buffer */
- int icon_len = 40 + h * w * 4;
- Vector<BYTE> v;
- v.resize(icon_len);
- BYTE *icon_bmp = v.ptrw();
-
- encode_uint32(40, &icon_bmp[0]);
- encode_uint32(w, &icon_bmp[4]);
- encode_uint32(h * 2, &icon_bmp[8]);
- encode_uint16(1, &icon_bmp[12]);
- encode_uint16(32, &icon_bmp[14]);
- encode_uint32(BI_RGB, &icon_bmp[16]);
- encode_uint32(w * h * 4, &icon_bmp[20]);
- encode_uint32(0, &icon_bmp[24]);
- encode_uint32(0, &icon_bmp[28]);
- encode_uint32(0, &icon_bmp[32]);
- encode_uint32(0, &icon_bmp[36]);
-
- uint8_t *wr = &icon_bmp[40];
- const uint8_t *r = icon->get_data().ptr();
-
- for (int i = 0; i < h; i++) {
-
- for (int j = 0; j < w; j++) {
-
- const uint8_t *rpx = &r[((h - i - 1) * w + j) * 4];
- uint8_t *wpx = &wr[(i * w + j) * 4];
- wpx[0] = rpx[2];
- wpx[1] = rpx[1];
- wpx[2] = rpx[0];
- wpx[3] = rpx[3];
- }
- }
-
- HICON hicon = CreateIconFromResource(icon_bmp, icon_len, TRUE, 0x00030000);
-
- /* Set the icon for the window */
- SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hicon);
-
- /* Set the icon in the task manager (should we do this?) */
- SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)hicon);
-}
-
bool OS_Windows::has_environment(const String &p_var) const {
#ifdef MINGW_ENABLED
@@ -2996,16 +586,6 @@ String OS_Windows::get_stdin_string(bool p_block) {
return String();
}
-void OS_Windows::enable_for_stealing_focus(ProcessID pid) {
-
- AllowSetForegroundWindow(pid);
-}
-
-void OS_Windows::move_window_to_foreground() {
-
- SetForegroundWindow(hWnd);
-}
-
Error OS_Windows::shell_open(String p_uri) {
ShellExecuteW(NULL, NULL, p_uri.c_str(), NULL, NULL, SW_SHOWNORMAL);
@@ -3068,101 +648,6 @@ int OS_Windows::get_processor_count() const {
return sysinfo.dwNumberOfProcessors;
}
-OS::LatinKeyboardVariant OS_Windows::get_latin_keyboard_variant() const {
-
- unsigned long azerty[] = {
- 0x00020401, // Arabic (102) AZERTY
- 0x0001080c, // Belgian (Comma)
- 0x0000080c, // Belgian French
- 0x0000040c, // French
- 0 // <--- STOP MARK
- };
- unsigned long qwertz[] = {
- 0x0000041a, // Croation
- 0x00000405, // Czech
- 0x00000407, // German
- 0x00010407, // German (IBM)
- 0x0000040e, // Hungarian
- 0x0000046e, // Luxembourgish
- 0x00010415, // Polish (214)
- 0x00000418, // Romanian (Legacy)
- 0x0000081a, // Serbian (Latin)
- 0x0000041b, // Slovak
- 0x00000424, // Slovenian
- 0x0001042e, // Sorbian Extended
- 0x0002042e, // Sorbian Standard
- 0x0000042e, // Sorbian Standard (Legacy)
- 0x0000100c, // Swiss French
- 0x00000807, // Swiss German
- 0 // <--- STOP MARK
- };
- unsigned long dvorak[] = {
- 0x00010409, // US-Dvorak
- 0x00030409, // US-Dvorak for left hand
- 0x00040409, // US-Dvorak for right hand
- 0 // <--- STOP MARK
- };
-
- char name[KL_NAMELENGTH + 1];
- name[0] = 0;
- GetKeyboardLayoutNameA(name);
-
- unsigned long hex = strtoul(name, NULL, 16);
-
- int i = 0;
- while (azerty[i] != 0) {
- if (azerty[i] == hex) return LATIN_KEYBOARD_AZERTY;
- i++;
- }
-
- i = 0;
- while (qwertz[i] != 0) {
- if (qwertz[i] == hex) return LATIN_KEYBOARD_QWERTZ;
- i++;
- }
-
- i = 0;
- while (dvorak[i] != 0) {
- if (dvorak[i] == hex) return LATIN_KEYBOARD_DVORAK;
- i++;
- }
-
- return LATIN_KEYBOARD_QWERTY;
-}
-
-void OS_Windows::release_rendering_thread() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->release_current();
- }
-#endif
-}
-
-void OS_Windows::make_rendering_thread() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->make_current();
- }
-#endif
-}
-
-void OS_Windows::swap_buffers() {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- context_gles2->swap_buffers();
- }
-#endif
-#if defined(VULKAN_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_VULKAN) {
- context_vulkan->swap_buffers();
- }
-#endif
-}
-
-void OS_Windows::force_process_input() {
- process_events(); // get rid of pending events
-}
-
void OS_Windows::run() {
if (!main_loop)
@@ -3172,7 +657,7 @@ void OS_Windows::run() {
while (!force_quit) {
- process_events(); // get rid of pending events
+ DisplayServer::get_singleton()->process_events(); // get rid of pending events
if (Main::iteration())
break;
};
@@ -3287,50 +772,6 @@ 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 = im_position.x;
- cps.ptCurrentPos.y = im_position.y;
- ImmSetCompositionWindow(himc, &cps);
- ImmReleaseContext(hWnd, himc);
-}
-
-bool OS_Windows::is_joy_known(int p_device) {
- return input->is_joy_mapped(p_device);
-}
-
-String OS_Windows::get_joy_guid(int p_device) const {
- return input->get_joy_guid_remapped(p_device);
-}
-
-void OS_Windows::_set_use_vsync(bool p_enable) {
-#if defined(OPENGL_ENABLED)
- if (video_driver_index == VIDEO_DRIVER_GLES2) {
- if (context_gles2)
- context_gles2->set_use_vsync(p_enable);
- }
-#endif
-}
-
bool OS_Windows::_check_internal_feature_support(const String &p_feature) {
return p_feature == "pc";
@@ -3344,20 +785,13 @@ bool OS_Windows::is_disable_crash_handler() const {
return crash_handler.is_disabled();
}
-void OS_Windows::process_and_drop_events() {
-
- drop_events = true;
- process_events();
- drop_events = false;
-}
-
Error OS_Windows::move_to_trash(const String &p_path) {
SHFILEOPSTRUCTW sf;
WCHAR *from = new WCHAR[p_path.length() + 2];
wcscpy_s(from, p_path.length() + 1, p_path.c_str());
from[p_path.length() + 1] = 0;
- sf.hwnd = hWnd;
+ sf.hwnd = main_window;
sf.wFunc = FO_DELETE;
sf.pFrom = from;
sf.pTo = NULL;
@@ -3379,36 +813,12 @@ Error OS_Windows::move_to_trash(const String &p_path) {
OS_Windows::OS_Windows(HINSTANCE _hInstance) {
- drop_events = false;
- key_event_pos = 0;
- layered_window = false;
- hBitmap = NULL;
force_quit = false;
- alt_mem = false;
- gr_mem = false;
- shift_mem = false;
- control_mem = false;
- meta_mem = false;
- minimized = false;
- was_maximized = false;
- window_focused = true;
- console_visible = IsWindowVisible(GetConsoleWindow());
-
- //Note: Functions for pen input, available on Windows 8+
- HMODULE user32_lib = LoadLibraryW(L"user32.dll");
- if (user32_lib) {
- win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType");
- win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo");
- }
hInstance = _hInstance;
- pressrc = 0;
- old_invalid = true;
- mouse_mode = MOUSE_MODE_VISIBLE;
#ifdef STDOUT_FILE
stdo = fopen("stdout.txt", "wb");
#endif
- user_proc = NULL;
#ifdef WASAPI_ENABLED
AudioDriverManager::add_driver(&driver_wasapi);
@@ -3417,16 +827,14 @@ OS_Windows::OS_Windows(HINSTANCE _hInstance) {
AudioDriverManager::add_driver(&driver_xaudio2);
#endif
+ DisplayServerWindows::register_windows_driver();
+
Vector<Logger *> loggers;
loggers.push_back(memnew(WindowsTerminalLogger));
_set_logger(memnew(CompositeLogger(loggers)));
}
OS_Windows::~OS_Windows() {
- if (is_layered_allowed() && layered_window) {
- DeleteObject(hBitmap);
- DeleteDC(hDC_dib);
- }
#ifdef STDOUT_FILE
fclose(stdo);
#endif
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 8506aa7b20..040951668d 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -31,7 +31,7 @@
#ifndef OS_WINDOWS_H
#define OS_WINDOWS_H
-#include "core/os/input.h"
+#include "core/input/input_filter.h"
#include "core/os/os.h"
#include "core/project_settings.h"
#include "crash_handler_windows.h"
@@ -39,7 +39,6 @@
#include "drivers/wasapi/audio_driver_wasapi.h"
#include "drivers/winmidi/midi_driver_winmidi.h"
#include "key_mapping_windows.h"
-#include "main/input_default.h"
#include "servers/audio_server.h"
#include "servers/visual/rasterizer.h"
#include "servers/visual_server.h"
@@ -62,183 +61,19 @@
#include <windows.h>
#include <windowsx.h>
-#ifndef POINTER_STRUCTURES
-
-#define POINTER_STRUCTURES
-
-typedef DWORD POINTER_INPUT_TYPE;
-typedef UINT32 POINTER_FLAGS;
-typedef UINT32 PEN_FLAGS;
-typedef UINT32 PEN_MASK;
-
-enum tagPOINTER_INPUT_TYPE {
- PT_POINTER = 0x00000001,
- PT_TOUCH = 0x00000002,
- PT_PEN = 0x00000003,
- PT_MOUSE = 0x00000004,
- PT_TOUCHPAD = 0x00000005
-};
-
-typedef enum tagPOINTER_BUTTON_CHANGE_TYPE {
- POINTER_CHANGE_NONE,
- POINTER_CHANGE_FIRSTBUTTON_DOWN,
- POINTER_CHANGE_FIRSTBUTTON_UP,
- POINTER_CHANGE_SECONDBUTTON_DOWN,
- POINTER_CHANGE_SECONDBUTTON_UP,
- POINTER_CHANGE_THIRDBUTTON_DOWN,
- POINTER_CHANGE_THIRDBUTTON_UP,
- POINTER_CHANGE_FOURTHBUTTON_DOWN,
- POINTER_CHANGE_FOURTHBUTTON_UP,
- POINTER_CHANGE_FIFTHBUTTON_DOWN,
- POINTER_CHANGE_FIFTHBUTTON_UP,
-} POINTER_BUTTON_CHANGE_TYPE;
-
-typedef struct tagPOINTER_INFO {
- POINTER_INPUT_TYPE pointerType;
- UINT32 pointerId;
- UINT32 frameId;
- POINTER_FLAGS pointerFlags;
- HANDLE sourceDevice;
- HWND hwndTarget;
- POINT ptPixelLocation;
- POINT ptHimetricLocation;
- POINT ptPixelLocationRaw;
- POINT ptHimetricLocationRaw;
- DWORD dwTime;
- UINT32 historyCount;
- INT32 InputData;
- DWORD dwKeyStates;
- UINT64 PerformanceCount;
- POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
-} POINTER_INFO;
-
-typedef struct tagPOINTER_PEN_INFO {
- POINTER_INFO pointerInfo;
- PEN_FLAGS penFlags;
- PEN_MASK penMask;
- UINT32 pressure;
- UINT32 rotation;
- INT32 tiltX;
- INT32 tiltY;
-} POINTER_PEN_INFO;
-
-#endif
-
-typedef BOOL(WINAPI *GetPointerTypePtr)(uint32_t p_id, POINTER_INPUT_TYPE *p_type);
-typedef BOOL(WINAPI *GetPointerPenInfoPtr)(uint32_t p_id, POINTER_PEN_INFO *p_pen_info);
-
-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 {
- static GetPointerTypePtr win8p_GetPointerType;
- static GetPointerPenInfoPtr win8p_GetPointerPenInfo;
-
- enum {
- KEY_EVENT_BUFFER_SIZE = 512
- };
-
#ifdef STDOUT_FILE
FILE *stdo;
#endif
- struct KeyEvent {
-
- bool alt, shift, control, meta;
- UINT uMsg;
- WPARAM wParam;
- LPARAM lParam;
- };
-
- KeyEvent key_event_buffer[KEY_EVENT_BUFFER_SIZE];
- int key_event_pos;
-
uint64_t ticks_start;
uint64_t ticks_per_second;
- bool old_invalid;
- bool outside;
- int old_x, old_y;
- Point2i center;
-
-#if defined(OPENGL_ENABLED)
- ContextGL_Windows *context_gles2;
-#endif
-
-#if defined(VULKAN_ENABLED)
- VulkanContextWindows *context_vulkan;
- RenderingDeviceVulkan *rendering_device_vulkan;
-#endif
-
- VisualServer *visual_server;
- int pressrc;
- HINSTANCE hInstance; // Holds The Instance Of The Application
- HWND hWnd;
- Point2 last_pos;
-
- HBITMAP hBitmap; //DIB section for layered window
- uint8_t *dib_data;
- Size2 dib_size;
- HDC hDC_dib;
- bool layered_window;
-
- uint32_t move_timer_id;
-
- HCURSOR hCursor;
-
- Size2 min_size;
- Size2 max_size;
-
- Size2 window_rect;
- VideoMode video_mode;
- bool preserve_window_size = false;
-
+ HINSTANCE hInstance;
MainLoop *main_loop;
- WNDPROC user_proc;
-
- // IME
- HIMC im_himc;
- Vector2 im_position;
-
- MouseMode mouse_mode;
- bool alt_mem;
- bool gr_mem;
- bool shift_mem;
- bool control_mem;
- bool meta_mem;
- bool force_quit;
- bool window_has_focus;
- uint32_t last_button_state;
- bool use_raw_input;
- bool drop_events;
-
- HCURSOR cursors[CURSOR_MAX] = { NULL };
- CursorShape cursor_shape;
- Map<CursorShape, Vector<Variant>> cursors_cache;
-
- InputDefault *input;
- JoypadWindows *joypad;
- Map<int, Vector2> touch_state;
-
- int video_driver_index;
#ifdef WASAPI_ENABLED
AudioDriverWASAPI driver_wasapi;
#endif
@@ -251,28 +86,19 @@ class OS_Windows : public OS {
CrashHandler crash_handler;
- void _drag_event(float p_x, float p_y, int idx);
- void _touch_event(bool p_pressed, float p_x, float p_y, int idx);
-
- void _update_window_style(bool p_repaint = true, bool p_maximized = false);
-
- void _set_mouse_mode_impl(MouseMode p_mode);
+ bool force_quit;
+ HWND main_window;
// functions used by main to initialize/deinitialize the OS
protected:
- virtual int get_current_video_driver() const;
-
- virtual void initialize_core();
- virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
+ virtual void initialize();
virtual void set_main_loop(MainLoop *p_main_loop);
virtual void delete_main_loop();
virtual void finalize();
virtual void finalize_core();
-
- void process_events();
- void process_key_events();
+ virtual String get_stdin_string(bool p_block);
struct ProcessInfo {
@@ -281,75 +107,7 @@ protected:
};
Map<ProcessID, ProcessInfo> *process_map;
- bool pre_fs_valid;
- RECT pre_fs_rect;
- bool maximized;
- bool minimized;
- bool borderless;
- bool window_focused;
- bool console_visible;
- bool was_maximized;
-
public:
- LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
-
- virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
- String get_stdin_string(bool p_block);
-
- void set_mouse_mode(MouseMode p_mode);
- MouseMode get_mouse_mode() const;
-
- virtual void warp_mouse_position(const Point2 &p_to);
- virtual Point2 get_mouse_position() const;
- void update_real_mouse_position();
- virtual int get_mouse_button_state() const;
- virtual void set_window_title(const String &p_title);
-
- 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 int get_screen_count() const;
- virtual int get_current_screen() const;
- virtual void set_current_screen(int p_screen);
- virtual Point2 get_screen_position(int p_screen = -1) const;
- virtual Size2 get_screen_size(int p_screen = -1) const;
- virtual int get_screen_dpi(int p_screen = -1) const;
-
- virtual Point2 get_window_position() const;
- virtual void set_window_position(const Point2 &p_position);
- virtual Size2 get_window_size() const;
- virtual Size2 get_real_window_size() const;
- virtual Size2 get_max_window_size() const;
- virtual Size2 get_min_window_size() const;
- virtual void set_min_window_size(const Size2 p_size);
- virtual void set_max_window_size(const Size2 p_size);
- virtual void set_window_size(const Size2 p_size);
- virtual void set_window_fullscreen(bool p_enabled);
- virtual bool is_window_fullscreen() const;
- virtual void set_window_resizable(bool p_enabled);
- virtual bool is_window_resizable() const;
- virtual void set_window_minimized(bool p_enabled);
- virtual bool is_window_minimized() const;
- virtual void set_window_maximized(bool p_enabled);
- virtual bool is_window_maximized() const;
- virtual void set_window_always_on_top(bool p_enabled);
- virtual bool is_window_always_on_top() const;
- virtual bool is_window_focused() const;
- virtual void set_console_visible(bool p_enabled);
- virtual bool is_console_visible() const;
- virtual void request_attention();
-
- virtual void set_borderless_window(bool p_borderless);
- virtual bool get_borderless_window();
-
- virtual bool get_window_per_pixel_transparency_enabled() const;
- virtual void set_window_per_pixel_transparency_enabled(bool p_enabled);
-
- virtual uint8_t *get_layered_buffer_data();
- virtual Size2 get_layered_buffer_size();
- virtual void swap_layered_buffer();
-
virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);
virtual Error close_dynamic_library(void *p_library_handle);
virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false);
@@ -358,6 +116,8 @@ public:
virtual String get_name() const;
+ virtual void initialize_joypads() {}
+
virtual Date get_date(bool utc) const;
virtual Time get_time(bool utc) const;
virtual TimeZoneInfo get_time_zone_info() const;
@@ -365,7 +125,6 @@ public:
virtual uint64_t get_system_time_secs() const;
virtual uint64_t get_system_time_msecs() const;
- virtual bool can_draw() const;
virtual Error set_cwd(const String &p_cwd);
virtual void delay_usec(uint32_t p_usec) const;
@@ -379,28 +138,12 @@ public:
virtual String get_environment(const String &p_var) const;
virtual bool set_environment(const String &p_var, const String &p_value) const;
- virtual void set_clipboard(const String &p_text);
- virtual String get_clipboard() const;
-
- void set_cursor_shape(CursorShape p_shape);
- 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;
virtual String get_locale() const;
virtual int get_processor_count() const;
- virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
-
- virtual void enable_for_stealing_focus(ProcessID pid);
- virtual void move_window_to_foreground();
-
virtual String get_config_path() const;
virtual String get_data_path() const;
virtual String get_cache_path() const;
@@ -411,37 +154,21 @@ 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();
- virtual void make_rendering_thread();
- virtual void swap_buffers();
-
virtual Error shell_open(String p_uri);
void run();
- virtual bool get_swap_ok_cancel() { return true; }
-
- virtual bool is_joy_known(int p_device);
- virtual String get_joy_guid(int p_device) const;
-
- virtual void _set_use_vsync(bool p_enable);
- //virtual bool is_vsync_enabled() const;
-
virtual bool _check_internal_feature_support(const String &p_feature);
void disable_crash_handler();
bool is_disable_crash_handler() const;
virtual void initialize_debugging();
- void force_process_input();
-
virtual Error move_to_trash(const String &p_path);
- virtual void process_and_drop_events();
+ void set_main_window(HWND p_main_window) { main_window = p_main_window; }
+ HINSTANCE get_hinstance() { return hInstance; }
OS_Windows(HINSTANCE _hInstance);
~OS_Windows();
};
diff --git a/platform/windows/vulkan_context_win.cpp b/platform/windows/vulkan_context_win.cpp
index 20e1b46682..66b5cf8113 100644
--- a/platform/windows/vulkan_context_win.cpp
+++ b/platform/windows/vulkan_context_win.cpp
@@ -35,7 +35,7 @@ const char *VulkanContextWindows::_get_platform_surface_extension() const {
return VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
}
-int VulkanContextWindows::window_create(HWND p_window, HINSTANCE p_instance, int p_width, int p_height) {
+int VulkanContextWindows::window_create(DisplayServer::WindowID p_window_id, HWND p_window, HINSTANCE p_instance, int p_width, int p_height) {
VkWin32SurfaceCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
@@ -47,7 +47,7 @@ int VulkanContextWindows::window_create(HWND p_window, HINSTANCE p_instance, int
VkSurfaceKHR surface;
VkResult err = vkCreateWin32SurfaceKHR(_get_instance(), &createInfo, NULL, &surface);
ERR_FAIL_COND_V(err, -1);
- return _window_create(surface, p_width, p_height);
+ return _window_create(p_window_id, surface, p_width, p_height);
}
VulkanContextWindows::VulkanContextWindows() {
diff --git a/platform/windows/vulkan_context_win.h b/platform/windows/vulkan_context_win.h
index 1289f2a299..c9fea9369b 100644
--- a/platform/windows/vulkan_context_win.h
+++ b/platform/windows/vulkan_context_win.h
@@ -39,7 +39,7 @@ class VulkanContextWindows : public VulkanContext {
virtual const char *_get_platform_surface_extension() const;
public:
- int window_create(HWND p_window, HINSTANCE p_instance, int p_width, int p_height);
+ int window_create(DisplayServer::WindowID p_window_id, HWND p_window, HINSTANCE p_instance, int p_width, int p_height);
VulkanContextWindows();
~VulkanContextWindows();
diff --git a/platform/x11/SCsub b/platform/x11/SCsub
deleted file mode 100644
index 2268e4cc3d..0000000000
--- a/platform/x11/SCsub
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/usr/bin/env python
-
-Import('env')
-
-from platform_methods import run_in_subprocess
-import platform_x11_builders
-
-common_x11 = [
- "context_gl_x11.cpp",
- "vulkan_context_x11.cpp",
- "crash_handler_x11.cpp",
- "os_x11.cpp",
- "key_mapping_x11.cpp",
- "joypad_linux.cpp",
- "detect_prime.cpp"
-]
-
-prog = env.add_program('#bin/godot', ['godot_x11.cpp'] + common_x11)
-
-if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]:
- env.AddPostAction(prog, run_in_subprocess(platform_x11_builders.make_debug_x11))
diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h
deleted file mode 100644
index 997a6cc053..0000000000
--- a/platform/x11/os_x11.h
+++ /dev/null
@@ -1,339 +0,0 @@
-/*************************************************************************/
-/* os_x11.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef OS_X11_H
-#define OS_X11_H
-
-#include "core/os/input.h"
-#include "crash_handler_x11.h"
-#include "drivers/alsa/audio_driver_alsa.h"
-#include "drivers/alsamidi/midi_driver_alsamidi.h"
-#include "drivers/pulseaudio/audio_driver_pulseaudio.h"
-#include "drivers/unix/os_unix.h"
-#include "joypad_linux.h"
-#include "main/input_default.h"
-#include "servers/audio_server.h"
-#include "servers/visual/rasterizer.h"
-#include "servers/visual_server.h"
-
-#if defined(OPENGL_ENABLED)
-#include "context_gl_x11.h"
-#endif
-
-#if defined(VULKAN_ENABLED)
-#include "drivers/vulkan/rendering_device_vulkan.h"
-#include "platform/x11/vulkan_context_x11.h"
-#endif
-
-#include <X11/Xcursor/Xcursor.h>
-#include <X11/Xlib.h>
-#include <X11/extensions/XInput2.h>
-#include <X11/extensions/Xrandr.h>
-#include <X11/keysym.h>
-
-// Hints for X11 fullscreen
-typedef struct {
- unsigned long flags;
- unsigned long functions;
- unsigned long decorations;
- long inputMode;
- unsigned long status;
-} Hints;
-
-typedef struct _xrr_monitor_info {
- Atom name;
- Bool primary;
- Bool automatic;
- int noutput;
- int x;
- int y;
- int width;
- int height;
- int mwidth;
- int mheight;
- RROutput *outputs;
-} xrr_monitor_info;
-
-#undef CursorShape
-
-class OS_X11 : public OS_Unix {
-
- Atom wm_delete;
- Atom xdnd_enter;
- Atom xdnd_position;
- Atom xdnd_status;
- Atom xdnd_action_copy;
- Atom xdnd_drop;
- Atom xdnd_finished;
- Atom xdnd_selection;
- Atom requested;
-
- int xdnd_version;
-
-#if defined(OPENGL_ENABLED)
- ContextGL_X11 *context_gles2;
-#endif
-#if defined(VULKAN_ENABLED)
- VulkanContextX11 *context_vulkan;
- RenderingDeviceVulkan *rendering_device_vulkan;
-#endif
-
- //Rasterizer *rasterizer;
- VisualServer *visual_server;
- VideoMode current_videomode;
- List<String> args;
- Window x11_window;
- Window xdnd_source_window;
- MainLoop *main_loop;
- ::Display *x11_display;
- char *xmbstring;
- int xmblen;
- unsigned long last_timestamp;
- ::Time last_keyrelease_time;
- ::XIC xic;
- ::XIM xim;
- ::XIMStyle xim_style;
- static void xim_destroy_callback(::XIM im, ::XPointer client_data,
- ::XPointer call_data);
-
- // IME
- bool im_active;
- Vector2 im_position;
- Vector2 last_position_before_fs;
-
- Size2 min_size;
- Size2 max_size;
-
- Point2 last_mouse_pos;
- bool last_mouse_pos_valid;
- Point2i last_click_pos;
- uint64_t last_click_ms;
- int last_click_button_index;
- uint32_t last_button_state;
-
- struct {
- int opcode;
- Vector<int> touch_devices;
- Map<int, Vector2> absolute_devices;
- Map<int, Vector3> pen_devices;
- XIEventMask all_event_mask;
- XIEventMask all_master_event_mask;
- Map<int, Vector2> state;
- double pressure;
- Vector2 tilt;
- Vector2 mouse_pos_to_filter;
- Vector2 relative_motion;
- Vector2 raw_pos;
- Vector2 old_raw_pos;
- ::Time last_relative_time;
- } xi;
-
- bool refresh_device_info();
-
- unsigned int get_mouse_button_state(unsigned int p_x11_button, int p_x11_type);
- void get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state);
- void flush_mouse_motion();
-
- MouseMode mouse_mode;
- Point2i center;
-
- void handle_key_event(XKeyEvent *p_event, bool p_echo = false);
- void process_xevents();
- virtual void delete_main_loop();
-
- bool force_quit;
- bool minimized;
- bool window_has_focus;
- bool do_mouse_warp;
-
- const char *cursor_theme;
- int cursor_size;
- XcursorImage *img[CURSOR_MAX];
- Cursor cursors[CURSOR_MAX];
- Cursor null_cursor;
- CursorShape current_cursor;
- Map<CursorShape, Vector<Variant>> cursors_cache;
-
- InputDefault *input;
-
-#ifdef JOYDEV_ENABLED
- JoypadLinux *joypad;
-#endif
-
-#ifdef ALSA_ENABLED
- AudioDriverALSA driver_alsa;
-#endif
-
-#ifdef ALSAMIDI_ENABLED
- MIDIDriverALSAMidi driver_alsamidi;
-#endif
-
-#ifdef PULSEAUDIO_ENABLED
- AudioDriverPulseAudio driver_pulseaudio;
-#endif
-
- bool layered_window;
-
- CrashHandler crash_handler;
-
- int video_driver_index;
- bool maximized;
- bool window_focused;
- //void set_wm_border(bool p_enabled);
- void set_wm_fullscreen(bool p_enabled);
- void set_wm_above(bool p_enabled);
-
- typedef xrr_monitor_info *(*xrr_get_monitors_t)(Display *dpy, Window window, Bool get_active, int *nmonitors);
- typedef void (*xrr_free_monitors_t)(xrr_monitor_info *monitors);
- xrr_get_monitors_t xrr_get_monitors;
- xrr_free_monitors_t xrr_free_monitors;
- void *xrandr_handle;
- Bool xrandr_ext_ok;
-
-protected:
- virtual int get_current_video_driver() const;
-
- virtual void initialize_core();
- virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver);
- virtual void finalize();
-
- virtual void set_main_loop(MainLoop *p_main_loop);
-
- void _window_changed(XEvent *event);
-
- bool is_window_maximize_allowed();
-
-public:
- virtual String get_name() const;
-
- virtual void set_cursor_shape(CursorShape p_shape);
- virtual CursorShape get_cursor_shape() const;
- virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
-
- void set_mouse_mode(MouseMode p_mode);
- MouseMode get_mouse_mode() const;
-
- virtual void warp_mouse_position(const Point2 &p_to);
- virtual Point2 get_mouse_position() const;
- virtual int get_mouse_button_state() const;
- virtual void set_window_title(const String &p_title);
-
- virtual void set_icon(const Ref<Image> &p_icon);
-
- virtual MainLoop *get_main_loop() const;
-
- virtual bool can_draw() const;
-
- virtual void set_clipboard(const String &p_text);
- virtual String get_clipboard() const;
-
- virtual void release_rendering_thread();
- virtual void make_rendering_thread();
- virtual void swap_buffers();
-
- virtual String get_config_path() const;
- virtual String get_data_path() const;
- virtual String get_cache_path() const;
-
- virtual String get_system_dir(SystemDir p_dir) const;
-
- virtual Error shell_open(String p_uri);
-
- 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 int get_screen_count() const;
- virtual int get_current_screen() const;
- virtual void set_current_screen(int p_screen);
- virtual Point2 get_screen_position(int p_screen = -1) const;
- virtual Size2 get_screen_size(int p_screen = -1) const;
- virtual int get_screen_dpi(int p_screen = -1) const;
- virtual Point2 get_window_position() const;
- virtual void set_window_position(const Point2 &p_position);
- virtual Size2 get_window_size() const;
- virtual Size2 get_real_window_size() const;
- virtual Size2 get_max_window_size() const;
- virtual Size2 get_min_window_size() const;
- virtual void set_min_window_size(const Size2 p_size);
- virtual void set_max_window_size(const Size2 p_size);
- virtual void set_window_size(const Size2 p_size);
- virtual void set_window_fullscreen(bool p_enabled);
- virtual bool is_window_fullscreen() const;
- virtual void set_window_resizable(bool p_enabled);
- virtual bool is_window_resizable() const;
- virtual void set_window_minimized(bool p_enabled);
- virtual bool is_window_minimized() const;
- virtual void set_window_maximized(bool p_enabled);
- virtual bool is_window_maximized() const;
- virtual void set_window_always_on_top(bool p_enabled);
- virtual bool is_window_always_on_top() const;
- virtual bool is_window_focused() const;
- virtual void request_attention();
-
- virtual void set_borderless_window(bool p_borderless);
- virtual bool get_borderless_window();
-
- 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;
-
- virtual void move_window_to_foreground();
- virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
-
- virtual bool is_joy_known(int p_device);
- virtual String get_joy_guid(int p_device) const;
-
- virtual void set_context(int p_context);
-
- virtual void _set_use_vsync(bool p_enable);
- //virtual bool is_vsync_enabled() const;
-
- virtual bool _check_internal_feature_support(const String &p_feature);
-
- virtual void force_process_input();
- void run();
-
- void disable_crash_handler();
- bool is_disable_crash_handler() const;
-
- virtual Error move_to_trash(const String &p_path);
-
- virtual LatinKeyboardVariant get_latin_keyboard_variant() const;
-
- void update_real_mouse_position();
- OS_X11();
-};
-
-#endif