diff options
Diffstat (limited to 'platform/windows')
26 files changed, 5055 insertions, 3801 deletions
diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 892d734734..47d8e14680 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -1,6 +1,6 @@ #!/usr/bin/env python -Import('env') +Import("env") import os from platform_methods import run_in_subprocess @@ -8,28 +8,29 @@ import platform_windows_builders common_win = [ "godot_windows.cpp", - "context_gl_windows.cpp", "crash_handler_windows.cpp", "os_windows.cpp", + "display_server_windows.cpp", "key_mapping_windows.cpp", "joypad_windows.cpp", - "power_windows.cpp", - "windows_terminal_logger.cpp" + "windows_terminal_logger.cpp", + "vulkan_context_win.cpp", + "context_gl_windows.cpp", ] -res_file = 'godot_res.rc' +res_file = "godot_res.rc" res_target = "godot_res" + env["OBJSUFFIX"] res_obj = env.RES(res_target, res_file) -prog = env.add_program('#bin/godot', common_win + res_obj, PROGSUFFIX=env["PROGSUFFIX"]) +prog = env.add_program("#bin/godot", common_win + res_obj, PROGSUFFIX=env["PROGSUFFIX"]) # Microsoft Visual Studio Project Generation -if env['vsproj']: - env.vs_srcs = env.vs_srcs + ["platform/windows/" + res_file] - env.vs_srcs = env.vs_srcs + ["platform/windows/godot.natvis"] +if env["vsproj"]: + env.vs_srcs += ["platform/windows/" + res_file] + env.vs_srcs += ["platform/windows/godot.natvis"] for x in common_win: - env.vs_srcs = env.vs_srcs + ["platform/windows/" + str(x)] + env.vs_srcs += ["platform/windows/" + str(x)] if not os.getenv("VCINSTALLDIR"): - if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes") and env["separate_debug_symbols"]: + if env["debug_symbols"] and env["separate_debug_symbols"]: env.AddPostAction(prog, run_in_subprocess(platform_windows_builders.make_debug_mingw)) diff --git a/platform/windows/context_gl_windows.cpp b/platform/windows/context_gl_windows.cpp index ad62e3a306..7cf9738f13 100644 --- a/platform/windows/context_gl_windows.cpp +++ b/platform/windows/context_gl_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -51,27 +51,22 @@ typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *); void ContextGL_Windows::release_current() { - - wglMakeCurrent(hDC, NULL); + wglMakeCurrent(hDC, nullptr); } void ContextGL_Windows::make_current() { - wglMakeCurrent(hDC, hRC); } int ContextGL_Windows::get_window_width() { - return OS::get_singleton()->get_video_mode().width; } int ContextGL_Windows::get_window_height() { - return OS::get_singleton()->get_video_mode().height; } bool ContextGL_Windows::should_vsync_via_compositor() { - if (OS::get_singleton()->is_window_fullscreen() || !OS::get_singleton()->is_vsync_via_compositor_enabled()) { return false; } @@ -88,7 +83,6 @@ bool ContextGL_Windows::should_vsync_via_compositor() { } void ContextGL_Windows::swap_buffers() { - SwapBuffers(hDC); if (use_vsync) { @@ -100,7 +94,7 @@ void ContextGL_Windows::swap_buffers() { if (vsync_via_compositor_now != vsync_via_compositor) { // The previous frame had a different operating mode than this - // frame. Set the 'vsync_via_compositor' member variable and the + // frame. Set the 'vsync_via_compositor' member variable and the // OpenGL swap interval to their proper values. set_use_vsync(true); } @@ -108,7 +102,6 @@ void ContextGL_Windows::swap_buffers() { } void ContextGL_Windows::set_use_vsync(bool p_use) { - vsync_via_compositor = p_use && should_vsync_via_compositor(); if (wglSwapIntervalEXT) { @@ -120,14 +113,12 @@ void ContextGL_Windows::set_use_vsync(bool p_use) { } bool ContextGL_Windows::is_using_vsync() const { - return use_vsync; } #define _WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 Error ContextGL_Windows::initialize() { - static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor 1, @@ -175,7 +166,6 @@ Error ContextGL_Windows::initialize() { wglMakeCurrent(hDC, hRC); if (opengl_3_context) { - int attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, //we want a 3.3 context WGL_CONTEXT_MINOR_VERSION_ARB, 3, @@ -185,10 +175,10 @@ Error ContextGL_Windows::initialize() { 0 }; //zero indicates the end of the array - PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL; //pointer to the method + PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = nullptr; //pointer to the method wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB"); - if (wglCreateContextAttribsARB == NULL) //OpenGL 3.0 is not supported + if (wglCreateContextAttribsARB == nullptr) //OpenGL 3.0 is not supported { wglDeleteContext(hRC); return ERR_CANT_CREATE; @@ -199,7 +189,7 @@ Error ContextGL_Windows::initialize() { wglDeleteContext(hRC); return ERR_CANT_CREATE; // Return false } - wglMakeCurrent(hDC, NULL); + wglMakeCurrent(hDC, nullptr); wglDeleteContext(hRC); hRC = new_hRC; @@ -217,11 +207,11 @@ Error ContextGL_Windows::initialize() { } ContextGL_Windows::ContextGL_Windows(HWND hwnd, bool p_opengl_3_context) { - opengl_3_context = p_opengl_3_context; hWnd = hwnd; use_vsync = false; vsync_via_compositor = false; + pixel_format = 0; } ContextGL_Windows::~ContextGL_Windows() { diff --git a/platform/windows/context_gl_windows.h b/platform/windows/context_gl_windows.h index 280c5a1e3c..e44e2945ca 100644 --- a/platform/windows/context_gl_windows.h +++ b/platform/windows/context_gl_windows.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -35,7 +35,7 @@ #ifndef CONTEXT_GL_WIN_H #define CONTEXT_GL_WIN_H -#include "core/error_list.h" +#include "core/error/error_list.h" #include "core/os/os.h" #include <windows.h> @@ -44,7 +44,6 @@ typedef bool(APIENTRY *PFNWGLSWAPINTERVALEXTPROC)(int interval); typedef int(APIENTRY *PFNWGLGETSWAPINTERVALEXTPROC)(void); class ContextGL_Windows { - HDC hDC; HGLRC hRC; unsigned int pixel_format; diff --git a/platform/windows/crash_handler_windows.cpp b/platform/windows/crash_handler_windows.cpp index 6145751e00..e2d507eddd 100644 --- a/platform/windows/crash_handler_windows.cpp +++ b/platform/windows/crash_handler_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -30,19 +30,21 @@ #include "crash_handler_windows.h" +#include "core/config/project_settings.h" #include "core/os/os.h" -#include "core/project_settings.h" #include "main/main.h" #ifdef CRASH_HANDLER_EXCEPTION -// Backtrace code code based on: https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app +// Backtrace code based on: https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app -#include <psapi.h> #include <algorithm> #include <iterator> +#include <string> #include <vector> +#include <psapi.h> + #pragma comment(lib, "psapi.lib") #pragma comment(lib, "dbghelp.lib") @@ -55,7 +57,7 @@ struct module_data { std::string image_name; std::string module_name; - void *base_address; + void *base_address = nullptr; DWORD load_size; }; @@ -121,7 +123,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { DWORD cbNeeded; std::vector<HMODULE> module_handles(1); - if (OS::get_singleton() == NULL || OS::get_singleton()->is_disable_crash_handler() || IsDebuggerPresent()) { + if (OS::get_singleton() == nullptr || OS::get_singleton()->is_disable_crash_handler() || IsDebuggerPresent()) { return EXCEPTION_CONTINUE_SEARCH; } @@ -131,7 +133,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH); // Load the symbols: - if (!SymInitialize(process, NULL, false)) + if (!SymInitialize(process, nullptr, false)) return EXCEPTION_CONTINUE_SEARCH; SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME); @@ -173,7 +175,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { msg = proj_settings->get("debug/settings/crash_handler/message"); } - fprintf(stderr, "Dumping the backtrace. %ls\n", msg.c_str()); + fprintf(stderr, "Dumping the backtrace. %s\n", msg.utf8().get_data()); int n = 0; do { @@ -193,7 +195,7 @@ DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) { n++; } - if (!StackWalk64(image_type, process, hThread, &frame, context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) + if (!StackWalk64(image_type, process, hThread, &frame, context, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr)) break; } while (frame.AddrReturn.Offset != 0 && n < 256); diff --git a/platform/windows/crash_handler_windows.h b/platform/windows/crash_handler_windows.h index adc548073c..e1ec8e6787 100644 --- a/platform/windows/crash_handler_windows.h +++ b/platform/windows/crash_handler_windows.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -41,7 +41,6 @@ extern DWORD CrashHandlerException(EXCEPTION_POINTERS *ep); #endif class CrashHandler { - bool disabled; public: diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 500736bd3f..7772ba2dbe 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -1,6 +1,9 @@ import methods import os +# To match other platforms +STACK_SIZE = 8388608 + def is_active(): return True @@ -11,10 +14,10 @@ def get_name(): def can_build(): - if (os.name == "nt"): + if os.name == "nt": # Building natively on Windows # If VCINSTALLDIR is set in the OS environ, use traditional Godot logic to set up MSVC - if (os.getenv("VCINSTALLDIR")): # MSVC, manual setup + if os.getenv("VCINSTALLDIR"): # MSVC, manual setup return True # Otherwise, let SCons find MSVC if installed, or else Mingw. @@ -23,18 +26,18 @@ def can_build(): # null compiler. return True - if (os.name == "posix"): + if os.name == "posix": # Cross-compiling with MinGW-w64 (old MinGW32 is not supported) mingw32 = "i686-w64-mingw32-" mingw64 = "x86_64-w64-mingw32-" - if (os.getenv("MINGW32_PREFIX")): + if os.getenv("MINGW32_PREFIX"): mingw32 = os.getenv("MINGW32_PREFIX") - if (os.getenv("MINGW64_PREFIX")): + if os.getenv("MINGW64_PREFIX"): mingw64 = os.getenv("MINGW64_PREFIX") test = "gcc --version > /dev/null 2>&1" - if (os.system(mingw64 + test) == 0 or os.system(mingw32 + test) == 0): + if os.system(mingw64 + test) == 0 or os.system(mingw32 + test) == 0: return True return False @@ -45,64 +48,67 @@ def get_opts(): mingw32 = "" mingw64 = "" - if (os.name == "posix"): + if os.name == "posix": mingw32 = "i686-w64-mingw32-" mingw64 = "x86_64-w64-mingw32-" - if (os.getenv("MINGW32_PREFIX")): + if os.getenv("MINGW32_PREFIX"): mingw32 = os.getenv("MINGW32_PREFIX") - if (os.getenv("MINGW64_PREFIX")): + if os.getenv("MINGW64_PREFIX"): mingw64 = os.getenv("MINGW64_PREFIX") return [ - ('mingw_prefix_32', 'MinGW prefix (Win32)', mingw32), - ('mingw_prefix_64', 'MinGW prefix (Win64)', mingw64), + ("mingw_prefix_32", "MinGW prefix (Win32)", mingw32), + ("mingw_prefix_64", "MinGW prefix (Win64)", mingw64), # Targeted Windows version: 7 (and later), minimum supported version # XP support dropped after EOL due to missing API for IPv6 and other issues # Vista support dropped after EOL due to GH-10243 - ('target_win_version', 'Targeted Windows version, >= 0x0601 (Windows 7)', '0x0601'), - EnumVariable('debug_symbols', 'Add debugging symbols to release builds', 'yes', ('yes', 'no', 'full')), - BoolVariable('separate_debug_symbols', 'Create a separate file containing debugging symbols', False), - ('msvc_version', 'MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.', None), - BoolVariable('use_mingw', 'Use the Mingw compiler, even if MSVC is installed. Only used on Windows.', False), - BoolVariable('use_llvm', 'Use the LLVM compiler', False), - BoolVariable('use_thinlto', 'Use ThinLTO', False), + ("target_win_version", "Targeted Windows version, >= 0x0601 (Windows 7)", "0x0601"), + BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True), + EnumVariable("windows_subsystem", "Windows subsystem", "default", ("default", "console", "gui")), + BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False), + ("msvc_version", "MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.", None), + BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed.", False), + BoolVariable("use_llvm", "Use the LLVM compiler", False), + BoolVariable("use_thinlto", "Use ThinLTO", False), + BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True), + BoolVariable("use_asan", "Use address sanitizer (ASAN)", False), ] def get_flags(): - - return [ - ] + return [] def build_res_file(target, source, env): - - if (env["bits"] == "32"): - cmdbase = env['mingw_prefix_32'] + if env["bits"] == "32": + cmdbase = env["mingw_prefix_32"] else: - cmdbase = env['mingw_prefix_64'] - cmdbase = cmdbase + 'windres --include-dir . ' + cmdbase = env["mingw_prefix_64"] + cmdbase = cmdbase + "windres --include-dir . " import subprocess + for x in range(len(source)): - cmd = cmdbase + '-i ' + str(source[x]) + ' -o ' + str(target[x]) + cmd = cmdbase + "-i " + str(source[x]) + " -o " + str(target[x]) try: out = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE).communicate() if len(out[1]): return 1 - except: + except Exception: return 1 return 0 def setup_msvc_manual(env): """Set up env to use MSVC manually, using VCINSTALLDIR""" - if (env["bits"] != "default"): - print(""" + if env["bits"] != "default": + print( + """ Bits argument is not supported for MSVC compilation. Architecture depends on the Native/Cross Compile Tools Prompt/Developer Console (or Visual Studio settings) that is being used to run SCons. As a consequence, bits argument is disabled. Run scons again without bits argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler will be executed and inform you. - """) + """ + ) raise SCons.Errors.UserError("Bits argument should not be used when using VCINSTALLDIR") # Force bits arg @@ -111,18 +117,23 @@ def setup_msvc_manual(env): env["x86_libtheora_opt_vc"] = True # find compiler manually - compiler_version_str = methods.detect_visual_c_compiler_version(env['ENV']) + compiler_version_str = methods.detect_visual_c_compiler_version(env["ENV"]) print("Found MSVC compiler: " + compiler_version_str) # If building for 64bit architecture, disable assembly optimisations for 32 bit builds (theora as of writing)... vc compiler for 64bit can not compile _asm - if(compiler_version_str == "amd64" or compiler_version_str == "x86_amd64"): + if compiler_version_str == "amd64" or compiler_version_str == "x86_amd64": env["bits"] = "64" env["x86_libtheora_opt_vc"] = False print("Compiled program architecture will be a 64 bit executable (forcing bits=64).") - elif (compiler_version_str == "x86" or compiler_version_str == "amd64_x86"): + elif compiler_version_str == "x86" or compiler_version_str == "amd64_x86": print("Compiled program architecture will be a 32 bit executable. (forcing bits=32).") else: - print("Failed to manually detect MSVC compiler architecture version... Defaulting to 32bit executable settings (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture this build is compiled for. You should check your settings/compilation setup, or avoid setting VCINSTALLDIR.") + print( + "Failed to manually detect MSVC compiler architecture version... Defaulting to 32bit executable settings" + " (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture this" + " build is compiled for. You should check your settings/compilation setup, or avoid setting VCINSTALLDIR." + ) + def setup_msvc_auto(env): """Set up MSVC using SCons's auto-detection logic""" @@ -135,94 +146,147 @@ def setup_msvc_auto(env): # (Ideally we'd decide on the tool config before configuring any # environment, and just set the env up once, but this function runs # on an existing env so this is the simplest way.) - env['MSVC_SETUP_RUN'] = False # Need to set this to re-run the tool - env['MSVS_VERSION'] = None - env['MSVC_VERSION'] = None - env['TARGET_ARCH'] = None - if env['bits'] != 'default': - env['TARGET_ARCH'] = {'32': 'x86', '64': 'x86_64'}[env['bits']] - if env.has_key('msvc_version'): - env['MSVC_VERSION'] = env['msvc_version'] - env.Tool('msvc') - env.Tool('mssdk') # we want the MS SDK + env["MSVC_SETUP_RUN"] = False # Need to set this to re-run the tool + env["MSVS_VERSION"] = None + env["MSVC_VERSION"] = None + env["TARGET_ARCH"] = None + if env["bits"] != "default": + env["TARGET_ARCH"] = {"32": "x86", "64": "x86_64"}[env["bits"]] + if env.has_key("msvc_version"): + env["MSVC_VERSION"] = env["msvc_version"] + env.Tool("msvc") + env.Tool("mssdk") # we want the MS SDK # Note: actual compiler version can be found in env['MSVC_VERSION'], e.g. "14.1" for VS2015 # Get actual target arch into bits (it may be "default" at this point): - if env['TARGET_ARCH'] in ('amd64', 'x86_64'): - env['bits'] = '64' + if env["TARGET_ARCH"] in ("amd64", "x86_64"): + env["bits"] = "64" else: - env['bits'] = '32' - print("Found MSVC version %s, arch %s, bits=%s" % (env['MSVC_VERSION'], env['TARGET_ARCH'], env['bits'])) - if env['TARGET_ARCH'] in ('amd64', 'x86_64'): + env["bits"] = "32" + print("Found MSVC version %s, arch %s, bits=%s" % (env["MSVC_VERSION"], env["TARGET_ARCH"], env["bits"])) + if env["TARGET_ARCH"] in ("amd64", "x86_64"): env["x86_libtheora_opt_vc"] = False + def setup_mingw(env): """Set up env for use with mingw""" # Nothing to do here print("Using MinGW") pass + def configure_msvc(env, manual_msvc_config): """Configure env to work with MSVC""" # Build type - if (env["target"] == "release"): - if (env["optimize"] == "speed"): #optimize for speed (default) - env.Append(CCFLAGS=['/O2']) - else: # optimize for size - env.Append(CCFLAGS=['/O1']) - env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS']) - env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup']) - env.Append(LINKFLAGS=['/OPT:REF']) - - elif (env["target"] == "release_debug"): - if (env["optimize"] == "speed"): #optimize for speed (default) - env.Append(CCFLAGS=['/O2']) - else: # optimize for size - env.Append(CCFLAGS=['/O1']) - env.AppendUnique(CPPDEFINES = ['DEBUG_ENABLED']) - env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) - env.Append(LINKFLAGS=['/OPT:REF']) - - elif (env["target"] == "debug"): - env.AppendUnique(CCFLAGS=['/Z7', '/Od', '/EHsc']) - env.AppendUnique(CPPDEFINES = ['DEBUG_ENABLED', 'DEBUG_MEMORY_ENABLED', - 'D3D_DEBUG_INFO']) - env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) - env.Append(LINKFLAGS=['/DEBUG']) - - if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes"): - env.AppendUnique(CCFLAGS=['/Z7']) - env.AppendUnique(LINKFLAGS=['/DEBUG']) + if env["tests"]: + env["windows_subsystem"] = "console" + elif env["windows_subsystem"] == "default": + # Default means we use console for debug, gui for release. + if "debug" in env["target"]: + env["windows_subsystem"] = "console" + else: + env["windows_subsystem"] = "gui" + + if env["target"] == "release": + if env["optimize"] == "speed": # optimize for speed (default) + env.Append(CCFLAGS=["/O2"]) + env.Append(LINKFLAGS=["/OPT:REF"]) + elif env["optimize"] == "size": # optimize for size + env.Append(CCFLAGS=["/O1"]) + env.Append(LINKFLAGS=["/OPT:REF"]) + env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"]) + + elif env["target"] == "release_debug": + if env["optimize"] == "speed": # optimize for speed (default) + env.Append(CCFLAGS=["/O2"]) + env.Append(LINKFLAGS=["/OPT:REF"]) + elif env["optimize"] == "size": # optimize for size + env.Append(CCFLAGS=["/O1"]) + env.Append(LINKFLAGS=["/OPT:REF"]) + env.AppendUnique(CPPDEFINES=["DEBUG_ENABLED"]) + + elif env["target"] == "debug": + env.AppendUnique(CCFLAGS=["/Zi", "/FS", "/Od", "/EHsc"]) + env.AppendUnique(CPPDEFINES=["DEBUG_ENABLED"]) + env.Append(LINKFLAGS=["/DEBUG"]) + + if env["debug_symbols"]: + env.AppendUnique(CCFLAGS=["/Zi", "/FS"]) + env.AppendUnique(LINKFLAGS=["/DEBUG"]) + + if env["windows_subsystem"] == "gui": + env.Append(LINKFLAGS=["/SUBSYSTEM:WINDOWS"]) + else: + env.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"]) + env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"]) ## Compile/link flags - env.AppendUnique(CCFLAGS=['/MT', '/Gd', '/GR', '/nologo']) - if int(env['MSVC_VERSION'].split('.')[0]) >= 14: #vs2015 and later - env.AppendUnique(CCFLAGS=['/utf-8']) - env.AppendUnique(CXXFLAGS=['/TP']) # assume all sources are C++ - if manual_msvc_config: # should be automatic if SCons found it + if env["use_static_cpp"]: + env.AppendUnique(CCFLAGS=["/MT"]) + else: + env.AppendUnique(CCFLAGS=["/MD"]) + + env.AppendUnique(CCFLAGS=["/Gd", "/GR", "/nologo"]) + # Force to use Unicode encoding + env.AppendUnique(CCFLAGS=["/utf-8"]) + env.AppendUnique(CXXFLAGS=["/TP"]) # assume all sources are C++ + if manual_msvc_config: # should be automatic if SCons found it if os.getenv("WindowsSdkDir") is not None: env.Prepend(CPPPATH=[os.getenv("WindowsSdkDir") + "/Include"]) else: print("Missing environment variable: WindowsSdkDir") - env.AppendUnique(CPPDEFINES = ['WINDOWS_ENABLED', 'OPENGL_ENABLED', - 'WASAPI_ENABLED', 'WINMIDI_ENABLED', - 'TYPED_METHOD_BIND', - 'WIN32', 'MSVC', - 'WINVER=%s' % env["target_win_version"], - '_WIN32_WINNT=%s' % env["target_win_version"]]) - env.AppendUnique(CPPDEFINES=['NOMINMAX']) # disable bogus min/max WinDef.h macros + env.AppendUnique( + CPPDEFINES=[ + "WINDOWS_ENABLED", + "WASAPI_ENABLED", + "WINMIDI_ENABLED", + "TYPED_METHOD_BIND", + "WIN32", + "MSVC", + "WINVER=%s" % env["target_win_version"], + "_WIN32_WINNT=%s" % env["target_win_version"], + ] + ) + env.AppendUnique(CPPDEFINES=["NOMINMAX"]) # disable bogus min/max WinDef.h macros if env["bits"] == "64": - env.AppendUnique(CPPDEFINES=['_WIN64']) + env.AppendUnique(CPPDEFINES=["_WIN64"]) ## Libs - LIBS = ['winmm', 'opengl32', 'dsound', 'kernel32', 'ole32', 'oleaut32', - 'user32', 'gdi32', 'IPHLPAPI', 'Shlwapi', 'wsock32', 'Ws2_32', - 'shell32', 'advapi32', 'dinput8', 'dxguid', 'imm32', 'bcrypt','Avrt', - 'dwmapi'] + LIBS = [ + "winmm", + "dsound", + "kernel32", + "ole32", + "oleaut32", + "user32", + "gdi32", + "IPHLPAPI", + "Shlwapi", + "wsock32", + "Ws2_32", + "shell32", + "advapi32", + "dinput8", + "dxguid", + "imm32", + "bcrypt", + "Avrt", + "dwmapi", + ] + + env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED"]) + if not env["builtin_vulkan"]: + LIBS += ["vulkan"] + else: + LIBS += ["cfgmgr32"] + + # env.AppendUnique(CPPDEFINES = ['OPENGL_ENABLED']) + LIBS += ["opengl32"] + env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS]) if manual_msvc_config: @@ -233,21 +297,30 @@ def configure_msvc(env, manual_msvc_config): ## LTO - if (env["use_lto"]): - env.AppendUnique(CCFLAGS=['/GL']) - env.AppendUnique(ARFLAGS=['/LTCG']) + if env["use_lto"]: + env.AppendUnique(CCFLAGS=["/GL"]) + env.AppendUnique(ARFLAGS=["/LTCG"]) if env["progress"]: - env.AppendUnique(LINKFLAGS=['/LTCG:STATUS']) + env.AppendUnique(LINKFLAGS=["/LTCG:STATUS"]) else: - env.AppendUnique(LINKFLAGS=['/LTCG']) + env.AppendUnique(LINKFLAGS=["/LTCG"]) if manual_msvc_config: env.Prepend(CPPPATH=[p for p in os.getenv("INCLUDE").split(";")]) env.Append(LIBPATH=[p for p in os.getenv("LIB").split(";")]) + # Sanitizers + if env["use_asan"]: + env.extra_suffix += ".s" + env.Append(LINKFLAGS=["/INFERASANLIBS"]) + env.Append(CCFLAGS=["/fsanitize=address"]) + # Incremental linking fix - env['BUILDERS']['ProgramOriginal'] = env['BUILDERS']['Program'] - env['BUILDERS']['Program'] = methods.precious_program + env["BUILDERS"]["ProgramOriginal"] = env["BUILDERS"]["Program"] + env["BUILDERS"]["Program"] = methods.precious_program + + env.AppendUnique(LINKFLAGS=["/STACK:" + str(STACK_SIZE)]) + def configure_mingw(env): # Workaround for MinGW. See: @@ -256,118 +329,158 @@ def configure_mingw(env): ## Build type - if (env["target"] == "release"): - env.Append(CCFLAGS=['-msse2']) + if env["tests"]: + env["windows_subsystem"] = "console" + elif env["windows_subsystem"] == "default": + # Default means we use console for debug, gui for release. + if "debug" in env["target"]: + env["windows_subsystem"] = "console" + else: + env["windows_subsystem"] = "gui" - if (env["optimize"] == "speed"): #optimize for speed (default) - if (env["bits"] == "64"): - env.Append(CCFLAGS=['-O3']) + if env["target"] == "release": + env.Append(CCFLAGS=["-msse2"]) + + if env["optimize"] == "speed": # optimize for speed (default) + if env["bits"] == "64": + env.Append(CCFLAGS=["-O3"]) else: - env.Append(CCFLAGS=['-O2']) - else: #optimize for size - env.Prepend(CCFLAGS=['-Os']) - - - env.Append(LINKFLAGS=['-Wl,--subsystem,windows']) - - if (env["debug_symbols"] == "yes"): - env.Prepend(CCFLAGS=['-g1']) - if (env["debug_symbols"] == "full"): - env.Prepend(CCFLAGS=['-g2']) - - elif (env["target"] == "release_debug"): - env.Append(CCFLAGS=['-O2']) - env.Append(CPPDEFINES=['DEBUG_ENABLED']) - if (env["debug_symbols"] == "yes"): - env.Prepend(CCFLAGS=['-g1']) - if (env["debug_symbols"] == "full"): - env.Prepend(CCFLAGS=['-g2']) - if (env["optimize"] == "speed"): #optimize for speed (default) - env.Append(CCFLAGS=['-O2']) - else: #optimize for size - env.Prepend(CCFLAGS=['-Os']) - - elif (env["target"] == "debug"): - env.Append(CCFLAGS=['-g3']) - env.Append(CPPDEFINES=['DEBUG_ENABLED', 'DEBUG_MEMORY_ENABLED']) + env.Append(CCFLAGS=["-O2"]) + else: # optimize for size + env.Prepend(CCFLAGS=["-Os"]) + + if env["debug_symbols"]: + env.Prepend(CCFLAGS=["-g2"]) + + elif env["target"] == "release_debug": + env.Append(CCFLAGS=["-O2"]) + env.Append(CPPDEFINES=["DEBUG_ENABLED"]) + if env["debug_symbols"]: + env.Prepend(CCFLAGS=["-g2"]) + if env["optimize"] == "speed": # optimize for speed (default) + env.Append(CCFLAGS=["-O2"]) + else: # optimize for size + env.Prepend(CCFLAGS=["-Os"]) + + elif env["target"] == "debug": + env.Append(CCFLAGS=["-g3"]) + env.Append(CPPDEFINES=["DEBUG_ENABLED"]) + + if env["windows_subsystem"] == "gui": + env.Append(LINKFLAGS=["-Wl,--subsystem,windows"]) + else: + env.Append(LINKFLAGS=["-Wl,--subsystem,console"]) + env.AppendUnique(CPPDEFINES=["WINDOWS_SUBSYSTEM_CONSOLE"]) ## Compiler configuration - if (os.name == "nt"): - # Force splitting libmodules.a in multiple chunks to work around - # issues reaching the linker command line size limit, which also - # seem to induce huge slowdown for 'ar' (GH-30892). - env['split_libmodules'] = True - else: + if os.name != "nt": env["PROGSUFFIX"] = env["PROGSUFFIX"] + ".exe" # for linux cross-compilation - if (env["bits"] == "default"): - if (os.name == "nt"): + if env["bits"] == "default": + if os.name == "nt": env["bits"] = "64" if "PROGRAMFILES(X86)" in os.environ else "32" - else: # default to 64-bit on Linux + else: # default to 64-bit on Linux env["bits"] = "64" mingw_prefix = "" - if (env["bits"] == "32"): - env.Append(LINKFLAGS=['-static']) - env.Append(LINKFLAGS=['-static-libgcc']) - env.Append(LINKFLAGS=['-static-libstdc++']) + if env["bits"] == "32": + if env["use_static_cpp"]: + env.Append(LINKFLAGS=["-static"]) + env.Append(LINKFLAGS=["-static-libgcc"]) + env.Append(LINKFLAGS=["-static-libstdc++"]) mingw_prefix = env["mingw_prefix_32"] else: - env.Append(LINKFLAGS=['-static']) + if env["use_static_cpp"]: + env.Append(LINKFLAGS=["-static"]) mingw_prefix = env["mingw_prefix_64"] - if env['use_llvm']: + if env["use_llvm"]: env["CC"] = mingw_prefix + "clang" - env['AS'] = mingw_prefix + "as" env["CXX"] = mingw_prefix + "clang++" - env['AR'] = mingw_prefix + "ar" - env['RANLIB'] = mingw_prefix + "ranlib" - env["LINK"] = mingw_prefix + "clang++" + env["AS"] = mingw_prefix + "as" + env["AR"] = mingw_prefix + "ar" + env["RANLIB"] = mingw_prefix + "ranlib" else: env["CC"] = mingw_prefix + "gcc" - env['AS'] = mingw_prefix + "as" - env['CXX'] = mingw_prefix + "g++" - env['AR'] = mingw_prefix + "gcc-ar" - env['RANLIB'] = mingw_prefix + "gcc-ranlib" - env['LINK'] = mingw_prefix + "g++" + env["CXX"] = mingw_prefix + "g++" + env["AS"] = mingw_prefix + "as" + env["AR"] = mingw_prefix + "gcc-ar" + env["RANLIB"] = mingw_prefix + "gcc-ranlib" + env["x86_libtheora_opt_gcc"] = True - if env['use_lto']: - if not env['use_llvm'] and env.GetOption("num_jobs") > 1: - env.Append(CCFLAGS=['-flto']) - env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))]) + if env["use_lto"]: + if not env["use_llvm"] and env.GetOption("num_jobs") > 1: + env.Append(CCFLAGS=["-flto"]) + env.Append(LINKFLAGS=["-flto=" + str(env.GetOption("num_jobs"))]) else: - if env['use_thinlto']: - env.Append(CCFLAGS=['-flto=thin']) - env.Append(LINKFLAGS=['-flto=thin']) + if env["use_thinlto"]: + env.Append(CCFLAGS=["-flto=thin"]) + env.Append(LINKFLAGS=["-flto=thin"]) else: - env.Append(CCFLAGS=['-flto']) - env.Append(LINKFLAGS=['-flto']) + env.Append(CCFLAGS=["-flto"]) + env.Append(LINKFLAGS=["-flto"]) + env.Append(LINKFLAGS=["-Wl,--stack," + str(STACK_SIZE)]) ## Compile flags - env.Append(CCFLAGS=['-mwindows']) - env.Append(CPPDEFINES=['WINDOWS_ENABLED', 'OPENGL_ENABLED', 'WASAPI_ENABLED', 'WINMIDI_ENABLED']) - env.Append(CPPDEFINES=[('WINVER', env['target_win_version']), ('_WIN32_WINNT', env['target_win_version'])]) - env.Append(LIBS=['mingw32', 'opengl32', 'dsound', 'ole32', 'd3d9', 'winmm', 'gdi32', 'iphlpapi', 'shlwapi', 'wsock32', 'ws2_32', 'kernel32', 'oleaut32', 'dinput8', 'dxguid', 'ksuser', 'imm32', 'bcrypt', 'avrt', 'uuid', 'dwmapi']) + env.Append(CCFLAGS=["-mwindows"]) + + env.Append(CPPDEFINES=["WINDOWS_ENABLED", "WASAPI_ENABLED", "WINMIDI_ENABLED"]) + env.Append(CPPDEFINES=[("WINVER", env["target_win_version"]), ("_WIN32_WINNT", env["target_win_version"])]) + env.Append( + LIBS=[ + "mingw32", + "dsound", + "ole32", + "d3d9", + "winmm", + "gdi32", + "iphlpapi", + "shlwapi", + "wsock32", + "ws2_32", + "kernel32", + "oleaut32", + "dinput8", + "dxguid", + "ksuser", + "imm32", + "bcrypt", + "avrt", + "uuid", + "dwmapi", + ] + ) + + env.Append(CPPDEFINES=["VULKAN_ENABLED"]) + if not env["builtin_vulkan"]: + env.Append(LIBS=["vulkan"]) + else: + env.Append(LIBS=["cfgmgr32"]) + + ## TODO !!! Re-enable when OpenGLES Rendering Device is implemented !!! + # env.Append(CPPDEFINES=['OPENGL_ENABLED']) + env.Append(LIBS=["opengl32"]) - env.Append(CPPDEFINES=['MINGW_ENABLED', ('MINGW_HAS_SECURE_API', 1)]) + env.Append(CPPDEFINES=["MINGW_ENABLED", ("MINGW_HAS_SECURE_API", 1)]) # resrc - env.Append(BUILDERS={'RES': env.Builder(action=build_res_file, suffix='.o', src_suffix='.rc')}) + env.Append(BUILDERS={"RES": env.Builder(action=build_res_file, suffix=".o", src_suffix=".rc")}) + def configure(env): # At this point the env has been set up with basic tools/compilers. - env.Prepend(CPPPATH=['#platform/windows']) + env.Prepend(CPPPATH=["#platform/windows"]) - print("Configuring for Windows: target=%s, bits=%s" % (env['target'], env['bits'])) + print("Configuring for Windows: target=%s, bits=%s" % (env["target"], env["bits"])) - if (os.name == "nt"): - env['ENV'] = os.environ # this makes build less repeatable, but simplifies some things - env['ENV']['TMP'] = os.environ['TMP'] + if os.name == "nt": + env["ENV"] = os.environ # this makes build less repeatable, but simplifies some things + env["ENV"]["TMP"] = os.environ["TMP"] # First figure out which compiler, version, and target arch we're using if os.getenv("VCINSTALLDIR") and not env["use_mingw"]: @@ -375,7 +488,7 @@ def configure(env): setup_msvc_manual(env) env.msvc = True manual_msvc_config = True - elif env.get('MSVC_VERSION', '') and not env["use_mingw"]: + elif env.get("MSVC_VERSION", "") and not env["use_mingw"]: setup_msvc_auto(env) env.msvc = True manual_msvc_config = False @@ -387,5 +500,5 @@ def configure(env): if env.msvc: configure_msvc(env, manual_msvc_config) - else: # MinGW + else: # MinGW configure_mingw(env) diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp new file mode 100644 index 0000000000..f7172598ec --- /dev/null +++ b/platform/windows/display_server_windows.cpp @@ -0,0 +1,3398 @@ +/*************************************************************************/ +/* display_server_windows.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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_windows.h" + +#include "core/io/marshalls.h" +#include "core/math/geometry_2d.h" +#include "main/main.h" +#include "os_windows.h" +#include "scene/resources/texture.h" + +#include <avrt.h> + +#ifdef DEBUG_ENABLED +static String format_error_message(DWORD id) { + LPWSTR messageBuffer = nullptr; + size_t size = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr); + + String msg = "Error " + itos(id) + ": " + String::utf16((const char16_t *)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(nullptr, (LPCWSTR)(p_alert.utf16().get_data()), (LPCWSTR)(p_title.utf16().get_data()), 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 || p_mode == MOUSE_MODE_CONFINED_HIDDEN) { + // Mouse is grabbed (captured or 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 { + // Mouse is free to move around (not captured or confined). + ReleaseCapture(); + ClipCursor(nullptr); + } + + if (p_mode == MOUSE_MODE_HIDDEN || p_mode == MOUSE_MODE_CAPTURED || p_mode == MOUSE_MODE_CONFINED_HIDDEN) { + if (hCursor == nullptr) { + hCursor = SetCursor(nullptr); + } else { + SetCursor(nullptr); + } + } 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; + + mouse_mode = p_mode; + + _set_mouse_mode_impl(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); +} + +MouseButton 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("\r\n", "\n").replace("\n", "\r\n"); // avoid \r\r\n + + if (!OpenClipboard(windows[last_focused_window].hWnd)) { + ERR_FAIL_MSG("Unable to open clipboard."); + } + EmptyClipboard(); + + Char16String utf16 = text.utf16(); + HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (utf16.length() + 1) * sizeof(WCHAR)); + ERR_FAIL_COND_MSG(mem == nullptr, "Unable to allocate memory for clipboard contents."); + + LPWSTR lptstrCopy = (LPWSTR)GlobalLock(mem); + memcpy(lptstrCopy, utf16.get_data(), (utf16.length() + 1) * sizeof(WCHAR)); + 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 == nullptr, "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 != nullptr) { + LPWSTR ptr = (LPWSTR)GlobalLock(mem); + if (ptr != nullptr) { + ret = String::utf16((const char16_t *)ptr); + GlobalUnlock(mem); + }; + }; + + } else if (IsClipboardFormatAvailable(CF_TEXT)) { + HGLOBAL mem = GetClipboardData(CF_UNICODETEXT); + if (mem != nullptr) { + LPTSTR ptr = (LPTSTR)GlobalLock(mem); + if (ptr != nullptr) { + 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(nullptr, nullptr, _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(nullptr, nullptr, _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(nullptr, nullptr, _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; + memset(&minfo, 0, 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(nullptr, nullptr, _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 = nullptr; + typedef HRESULT(WINAPI * GetDPIForMonitor_t)(HMONITOR hmonitor, _MonitorDpiType dpiType, UINT * dpiX, UINT * dpiY); + static GetDPIForMonitor_t getDPIForMonitor = nullptr; + + if (Shcore == nullptr) { + Shcore = LoadLibraryW(L"Shcore.dll"); + getDPIForMonitor = Shcore ? (GetDPIForMonitor_t)GetProcAddress(Shcore, "GetDpiForMonitor") : nullptr; + + if ((Shcore == nullptr) || (getDPIForMonitor == nullptr)) { + 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(nullptr); + if (hdc) { + overallX = GetDeviceCaps(hdc, LOGPIXELSX); + overallY = GetDeviceCaps(hdc, LOGPIXELSY); + ReleaseDC(nullptr, 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(nullptr, nullptr, _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); + ERR_FAIL_COND_V_MSG(window_id == INVALID_WINDOW_ID, INVALID_WINDOW_ID, "Failed to create sub window."); + + 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; + } + + return window_id; +} + +void DisplayServerWindows::show_window(WindowID p_id) { + WindowData &wd = windows[p_id]; + + if (p_id != MAIN_WINDOW_ID) { + _update_window_style(p_id); + } + + ShowWindow(wd.hWnd, wd.no_focus ? SW_SHOWNOACTIVATE : SW_SHOW); // Show The Window + if (!wd.no_focus) { + SetForegroundWindow(wd.hWnd); // Slightly Higher Priority + SetFocus(wd.hWnd); // Sets Keyboard Focus To + } +} + +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 + + if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[p_window].wtctx) { + wintab_WTClose(windows[p_window].wtctx); + windows[p_window].wtctx = 0; + } + 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, (LPCWSTR)(p_title.utf16().get_data())); +} + +void DisplayServerWindows::window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window) { + _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + windows[p_window].mpath = p_region; + _update_window_mouse_passthrough(p_window); +} + +void DisplayServerWindows::_update_window_mouse_passthrough(WindowID p_window) { + if (windows[p_window].mpath.size() == 0) { + SetWindowRgn(windows[p_window].hWnd, nullptr, TRUE); + } else { + POINT *points = (POINT *)memalloc(sizeof(POINT) * windows[p_window].mpath.size()); + for (int i = 0; i < windows[p_window].mpath.size(); i++) { + if (windows[p_window].borderless) { + points[i].x = windows[p_window].mpath[i].x; + points[i].y = windows[p_window].mpath[i].y; + } else { + points[i].x = windows[p_window].mpath[i].x + GetSystemMetrics(SM_CXSIZEFRAME); + points[i].y = windows[p_window].mpath[i].y + GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYCAPTION); + } + } + + HRGN region = CreatePolygonRgn(points, windows[p_window].mpath.size(), ALTERNATE); + SetWindowRgn(windows[p_window].hWnd, region, TRUE); + DeleteObject(region); + memfree(points); + } +} + +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(nullptr, nullptr, _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; + Input::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 || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { + 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, (LONG_PTR) nullptr); + } 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 || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN) { + 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_borderless) { + r_style |= WS_VISIBLE; + } + + 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) { + _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; + wd.maximized = wd.was_maximized; + + 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; + wd.pre_fs_valid = true; + } + + _update_window_style(p_window, false); + + MoveWindow(wd.hWnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, TRUE); + } else if (p_mode == WINDOW_MODE_WINDOWED) { + ShowWindow(wd.hWnd, SW_RESTORE); + wd.maximized = false; + wd.minimized = false; + } + + if (p_mode == WINDOW_MODE_MAXIMIZED) { + ShowWindow(wd.hWnd, SW_MAXIMIZE); + wd.maximized = true; + 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); + + // FIXME: Implement this, or confirm that it should always be true. + + 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); + _update_window_mouse_passthrough(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: { + // FIXME: Implement. + } break; + case WINDOW_FLAG_NO_FOCUS: { + wd.no_focus = p_enabled; + _update_window_style(p_window); + } break; + case WINDOW_FLAG_MAX: + 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: { + // FIXME: Implement. + } break; + case WINDOW_FLAG_NO_FOCUS: { + return wd.no_focus; + } break; + case WINDOW_FLAG_MAX: + 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] != nullptr) { + 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(nullptr); + + // 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(nullptr, 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 the 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 the 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_image(); + } + + 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_image(); + + 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 = nullptr; + HBITMAP hXorMask = nullptr; + + GetMaskBitmaps(bitmap, clrTransparent, hAndMask, hXorMask); + + if (nullptr == hAndMask || nullptr == 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 != nullptr) { + DeleteObject(hAndMask); + } + + if (hXorMask != nullptr) { + DeleteObject(hXorMask); + } + + memfree(buffer); + DeleteObject(bitmap); + } else { + // Reset to default system cursor + if (cursors[p_shape]) { + DestroyIcon(cursors[p_shape]); + cursors[p_shape] = nullptr; + } + + CursorShape c = cursor_shape; + cursor_shape = CURSOR_MAX; + cursor_set_shape(c); + + cursors_cache.erase(p_shape); + } +} + +bool DisplayServerWindows::get_swap_cancel_ok() { + return true; +} + +void DisplayServerWindows::enable_for_stealing_focus(OS::ProcessID pid) { + _THREAD_SAFE_METHOD_ + + AllowSetForegroundWindow(pid); +} + +int DisplayServerWindows::keyboard_get_layout_count() const { + return GetKeyboardLayoutList(0, nullptr); +} + +int DisplayServerWindows::keyboard_get_current_layout() const { + HKL cur_layout = GetKeyboardLayout(0); + + int layout_count = GetKeyboardLayoutList(0, nullptr); + HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL)); + GetKeyboardLayoutList(layout_count, layouts); + + for (int i = 0; i < layout_count; i++) { + if (cur_layout == layouts[i]) { + memfree(layouts); + return i; + } + } + memfree(layouts); + return -1; +} + +void DisplayServerWindows::keyboard_set_current_layout(int p_index) { + int layout_count = GetKeyboardLayoutList(0, nullptr); + + ERR_FAIL_INDEX(p_index, layout_count); + + HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL)); + GetKeyboardLayoutList(layout_count, layouts); + ActivateKeyboardLayout(layouts[p_index], KLF_SETFORPROCESS); + memfree(layouts); +} + +String DisplayServerWindows::keyboard_get_layout_language(int p_index) const { + int layout_count = GetKeyboardLayoutList(0, nullptr); + + ERR_FAIL_INDEX_V(p_index, layout_count, ""); + + HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL)); + GetKeyboardLayoutList(layout_count, layouts); + + WCHAR buf[LOCALE_NAME_MAX_LENGTH]; + memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR)); + LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0); + + memfree(layouts); + + return String::utf16((const char16_t *)buf).substr(0, 2); +} + +String _get_full_layout_name_from_registry(HKL p_layout) { + String id = "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\" + String::num_int64((int64_t)p_layout, 16, false).lpad(8, "0"); + String ret; + + HKEY hkey; + WCHAR layout_text[1024]; + memset(layout_text, 0, 1024 * sizeof(WCHAR)); + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, (LPCWSTR)(id.utf16().get_data()), 0, KEY_QUERY_VALUE, &hkey) != ERROR_SUCCESS) { + return ret; + } + + DWORD buffer = 1024; + DWORD vtype = REG_SZ; + if (RegQueryValueExW(hkey, L"Layout Text", nullptr, &vtype, (LPBYTE)layout_text, &buffer) == ERROR_SUCCESS) { + ret = String::utf16((const char16_t *)layout_text); + } + RegCloseKey(hkey); + return ret; +} + +String DisplayServerWindows::keyboard_get_layout_name(int p_index) const { + int layout_count = GetKeyboardLayoutList(0, nullptr); + + ERR_FAIL_INDEX_V(p_index, layout_count, ""); + + HKL *layouts = (HKL *)memalloc(layout_count * sizeof(HKL)); + GetKeyboardLayoutList(layout_count, layouts); + + String ret = _get_full_layout_name_from_registry(layouts[p_index]); // Try reading full name from Windows registry, fallback to locale name if failed (e.g. on Wine). + if (ret == String()) { + WCHAR buf[LOCALE_NAME_MAX_LENGTH]; + memset(buf, 0, LOCALE_NAME_MAX_LENGTH * sizeof(WCHAR)); + LCIDToLocaleName(MAKELCID(LOWORD(layouts[p_index]), SORT_DEFAULT), buf, LOCALE_NAME_MAX_LENGTH, 0); + + WCHAR name[1024]; + memset(name, 0, 1024 * sizeof(WCHAR)); + GetLocaleInfoEx(buf, LOCALE_SLOCALIZEDDISPLAYNAME, (LPWSTR)&name, 1024); + + ret = String::utf16((const char16_t *)name); + } + memfree(layouts); + + return ret; +} + +void DisplayServerWindows::process_events() { + _THREAD_SAFE_METHOD_ + + MSG msg; + + if (!drop_events) { + joypad->process_joypads(); + } + + while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + if (!drop_events) { + _process_key_events(); + Input::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.instantiate(); + event->set_index(idx); + event->set_window_id(p_window); + event->set_pressed(p_pressed); + event->set_position(Vector2(p_x, p_y)); + + Input::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.instantiate(); + 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()); + + Input::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) { + _THREAD_SAFE_METHOD_ + if (in_dispatch_input_event) { + return; + } + + in_dispatch_input_event = true; + 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())) { + in_dispatch_input_event = false; + ERR_FAIL_MSG("DisplayServerWindows: Invalid window id in input event."); + } + Callable callable = windows[event_from_window->get_window_id()].input_event_callback; + if (callable.is_null()) { + in_dispatch_input_event = false; + 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); + } + } + + in_dispatch_input_event = false; +} + +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; + bool window_created = false; + + for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { + if (E->get().hWnd == hWnd) { + window_id = E->key(); + window_created = true; + break; + } + } + + if (!window_created) { + // Window creation in progress. + window_id = window_id_counter; + ERR_FAIL_COND_V(!windows.has(window_id), 0); + } + + 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); + + if (!app_focused) { + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN); + } + app_focused = true; + } + break; + } + case 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(); + + bool self_steal = false; + HWND new_hwnd = (HWND)wParam; + if (IsWindow(new_hwnd)) { + self_steal = true; + } + + if (!self_steal) { + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT); + } + app_focused = false; + } + + break; + } + case WM_ACTIVATE: { // Watch For Window Activate Message + if (!windows[window_id].window_focused) { + _process_activate_event(window_id, wParam, lParam); + } else { + windows[window_id].saved_wparam = wParam; + windows[window_id].saved_lparam = lParam; + + // Run a timer to prevent event catching warning if the focused window is closing. + windows[window_id].focus_timer_id = SetTimer(windows[window_id].hWnd, 2, USER_TIMER_MINIMUM, (TIMERPROC) nullptr); + } + 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? + { + if (windows[window_id].focus_timer_id != 0U) { + KillTimer(windows[window_id].hWnd, windows[window_id].focus_timer_id); + } + _send_window_event(windows[window_id], WINDOW_EVENT_CLOSE_REQUEST); + + 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, nullptr, &dwSize, sizeof(RAWINPUTHEADER)); + LPBYTE lpb = new BYTE[dwSize]; + if (lpb == nullptr) { + 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.instantiate(); + + mm->set_window_id(window_id); + mm->set_ctrl_pressed(control_mem); + mm->set_shift_pressed(shift_mem); + mm->set_alt_pressed(alt_mem); + + mm->set_pressure((raw->data.mouse.ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN) ? 1.0f : 0.0f); + + 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); + Input::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()) + Input::get_singleton()->accumulate_input_event(mm); + } + delete[] lpb; + } break; + case WT_CSRCHANGE: + case WT_PROXIMITY: { + if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) { + AXIS pressure; + if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_NPRESSURE, &pressure)) { + windows[window_id].min_pressure = int(pressure.axMin); + windows[window_id].max_pressure = int(pressure.axMax); + } + AXIS orientation[3]; + if (wintab_WTInfo(WTI_DEVICES + windows[window_id].wtlc.lcDevice, DVC_ORIENTATION, &orientation)) { + windows[window_id].tilt_supported = orientation[0].axResolution && orientation[1].axResolution; + } + return 0; + } + } break; + case WT_PACKET: { + if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) { + PACKET packet; + if (wintab_WTPacket(windows[window_id].wtctx, wParam, &packet)) { + float pressure = float(packet.pkNormalPressure - windows[window_id].min_pressure) / float(windows[window_id].max_pressure - windows[window_id].min_pressure); + windows[window_id].last_pressure = pressure; + windows[window_id].last_pressure_update = 0; + + double azim = (packet.pkOrientation.orAzimuth / 10.0f) * (Math_PI / 180); + double alt = Math::tan((Math::abs(packet.pkOrientation.orAltitude / 10.0f)) * (Math_PI / 180)); + + if (windows[window_id].tilt_supported) { + windows[window_id].last_tilt = Vector2(Math::atan(Math::sin(azim) / alt), Math::atan(Math::cos(azim) / alt)); + } else { + windows[window_id].last_tilt = Vector2(); + } + + POINT coords; + GetCursorPos(&coords); + ScreenToClient(windows[window_id].hWnd, &coords); + + // 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.instantiate(); + mm->set_window_id(window_id); + mm->set_ctrl_pressed(GetKeyState(VK_CONTROL) < 0); + mm->set_shift_pressed(GetKeyState(VK_SHIFT) < 0); + mm->set_alt_pressed(alt_mem); + + mm->set_pressure(windows[window_id].last_pressure); + mm->set_tilt(windows[window_id].last_tilt); + + mm->set_button_mask(last_button_state); + + 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(windows[window_id].hWnd, &pos); + SetCursorPos(pos.x, pos.y); + } + + Input::get_singleton()->set_mouse_position(mm->get_position()); + mm->set_speed(Input::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) + Input::get_singleton()->accumulate_input_event(mm); + } + return 0; + } + } break; + case WM_POINTERENTER: { + if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) { + break; + } + + if ((tablet_get_current_driver() != "winink") || !winink_available) { + 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; + } + + windows[window_id].block_mm = true; + return 0; + } break; + case WM_POINTERLEAVE: { + windows[window_id].block_mm = false; + return 0; + } break; + case WM_POINTERUPDATE: { + if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) { + break; + } + + if ((tablet_get_current_driver() != "winink") || !winink_available) { + 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::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.instantiate(); + + mm->set_window_id(window_id); + if (pen_info.penMask & PEN_MASK_PRESSURE) { + mm->set_pressure((float)pen_info.pressure / 1024); + } else { + mm->set_pressure((HIWORD(wParam) & POINTER_MESSAGE_FLAG_FIRSTBUTTON) ? 1.0f : 0.0f); + } + if ((pen_info.penMask & PEN_MASK_TILT_X) && (pen_info.penMask & PEN_MASK_TILT_Y)) { + mm->set_tilt(Vector2((float)pen_info.tiltX / 90, (float)pen_info.tiltY / 90)); + } + + mm->set_ctrl_pressed(GetKeyState(VK_CONTROL) < 0); + mm->set_shift_pressed(GetKeyState(VK_SHIFT) < 0); + mm->set_alt_pressed(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); + } + + Input::get_singleton()->set_mouse_position(mm->get_position()); + mm->set_speed(Input::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) { + Input::get_singleton()->accumulate_input_event(mm); + } + + return 0; //Pointer event handled return 0 to avoid duplicate WM_MOUSEMOVE event + } break; + case WM_MOUSEMOVE: { + if (windows[window_id].block_mm) { + break; + } + + if (mouse_mode == MOUSE_MODE_CAPTURED && use_raw_input) { + break; + } + + if (Input::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.instantiate(); + mm->set_window_id(window_id); + mm->set_ctrl_pressed((wParam & MK_CONTROL) != 0); + mm->set_shift_pressed((wParam & MK_SHIFT) != 0); + mm->set_alt_pressed(alt_mem); + + if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[window_id].wtctx) { + // Note: WinTab sends both WT_PACKET and WM_xBUTTONDOWN/UP/MOUSEMOVE events, use mouse 1/0 pressure only when last_pressure was not updated recently. + if (windows[window_id].last_pressure_update < 10) { + windows[window_id].last_pressure_update++; + } else { + windows[window_id].last_tilt = Vector2(); + windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f; + } + } else { + windows[window_id].last_tilt = Vector2(); + windows[window_id].last_pressure = (wParam & MK_LBUTTON) ? 1.0f : 0.0f; + } + + mm->set_pressure(windows[window_id].last_pressure); + mm->set_tilt(windows[window_id].last_tilt); + + 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); + } + + Input::get_singleton()->set_mouse_position(mm->get_position()); + mm->set_speed(Input::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) + Input::get_singleton()->accumulate_input_event(mm); + + } break; + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + if (Input::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.instantiate(); + mb->set_window_id(window_id); + + switch (uMsg) { + case WM_LBUTTONDOWN: { + mb->set_pressed(true); + mb->set_button_index(MOUSE_BUTTON_LEFT); + } break; + case WM_LBUTTONUP: { + mb->set_pressed(false); + mb->set_button_index(MOUSE_BUTTON_LEFT); + } break; + case WM_MBUTTONDOWN: { + mb->set_pressed(true); + mb->set_button_index(MOUSE_BUTTON_MIDDLE); + } break; + case WM_MBUTTONUP: { + mb->set_pressed(false); + mb->set_button_index(MOUSE_BUTTON_MIDDLE); + } break; + case WM_RBUTTONDOWN: { + mb->set_pressed(true); + mb->set_button_index(MOUSE_BUTTON_RIGHT); + } break; + case WM_RBUTTONUP: { + mb->set_pressed(false); + mb->set_button_index(MOUSE_BUTTON_RIGHT); + } break; + case WM_LBUTTONDBLCLK: { + mb->set_pressed(true); + mb->set_button_index(MOUSE_BUTTON_LEFT); + mb->set_double_click(true); + } break; + case WM_RBUTTONDBLCLK: { + mb->set_pressed(true); + mb->set_button_index(MOUSE_BUTTON_RIGHT); + mb->set_double_click(true); + } break; + case WM_MBUTTONDBLCLK: { + mb->set_pressed(true); + mb->set_button_index(MOUSE_BUTTON_MIDDLE); + mb->set_double_click(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(MOUSE_BUTTON_WHEEL_UP); + } else { + mb->set_button_index(MOUSE_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(MOUSE_BUTTON_WHEEL_LEFT); + mb->set_factor(fabs((double)motion / (double)WHEEL_DELTA)); + } else { + mb->set_button_index(MOUSE_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(MOUSE_BUTTON_XBUTTON1); + } else { + mb->set_button_index(MOUSE_BUTTON_XBUTTON2); + } + } break; + case WM_XBUTTONUP: { + mb->set_pressed(false); + if (HIWORD(wParam) == XBUTTON1) { + mb->set_button_index(MOUSE_BUTTON_XBUTTON1); + } else { + mb->set_button_index(MOUSE_BUTTON_XBUTTON2); + } + } break; + case WM_XBUTTONDBLCLK: { + mb->set_pressed(true); + if (HIWORD(wParam) == XBUTTON1) { + mb->set_button_index(MOUSE_BUTTON_XBUTTON1); + } else { + mb->set_button_index(MOUSE_BUTTON_XBUTTON2); + } + mb->set_double_click(true); + } break; + default: { + return 0; + } + } + + mb->set_ctrl_pressed((wParam & MK_CONTROL) != 0); + mb->set_shift_pressed((wParam & MK_SHIFT) != 0); + mb->set_alt_pressed(alt_mem); + //mb->is_alt_pressed()=(wParam&MK_MENU)!=0; + if (mb->is_pressed()) { + last_button_state |= MouseButton(1 << (mb->get_button_index() - 1)); + } else { + last_button_state &= (MouseButton) ~(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()); + + Input::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 &= (MouseButton) ~(1 << (mbd->get_button_index() - 1)); + mbd->set_button_mask(last_button_state); + mbd->set_pressed(false); + Input::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") && window_created) { + 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); + } + } else { + windows[window_id].preserve_window_size = true; + } + + 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, nullptr, 0x0); + SelectObject(hDC_dib, hBitmap); + + ZeroMemory(dib_data, dib_size.x * dib_size.y * 4); + } +#endif + //return 0; // Jump Back + } break; + + case WM_ENTERSIZEMOVE: { + Input::get_singleton()->release_pressed_events(); + windows[window_id].move_timer_id = SetTimer(windows[window_id].hWnd, 1, USER_TIMER_MINIMUM, (TIMERPROC) nullptr); + } break; + case WM_EXITSIZEMOVE: { + KillTimer(windows[window_id].hWnd, windows[window_id].move_timer_id); + } break; + case WM_TIMER: { + if (wParam == windows[window_id].move_timer_id) { + _process_key_events(); + if (!Main::is_iterating()) { + Main::iteration(); + } + } else if (wParam == windows[window_id].focus_timer_id) { + _process_activate_event(window_id, windows[window_id].saved_wparam, windows[window_id].saved_lparam); + KillTimer(windows[window_id].hWnd, wParam); + windows[window_id].focus_timer_id = 0U; + } + } break; + + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYUP: + case WM_KEYDOWN: { + if (wParam == VK_SHIFT) + shift_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN); + if (wParam == VK_CONTROL) + control_mem = (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN); + 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 || mouse_mode == MOUSE_MODE_CONFINED_HIDDEN)) { + //Hide the cursor + if (hCursor == nullptr) { + hCursor = SetCursor(nullptr); + } else { + SetCursor(nullptr); + } + } else { + if (hCursor != nullptr) { + CursorShape c = cursor_shape; + cursor_shape = CURSOR_MAX; + cursor_set_shape(c); + hCursor = nullptr; + } + } + } + + } break; + case WM_DROPFILES: { + HDROP hDropInfo = (HDROP)wParam; + const int buffsize = 4096; + WCHAR buf[buffsize]; + + int fcount = DragQueryFileW(hDropInfo, 0xFFFFFFFF, nullptr, 0); + + Vector<String> files; + + for (int i = 0; i < fcount; i++) { + DragQueryFileW(hDropInfo, i, buf, buffsize); + String file = String::utf16((const char16_t *)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_activate_event(WindowID p_window_id, WPARAM wParam, LPARAM lParam) { + if (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) { + _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_IN); + windows[p_window_id].window_focused = true; + alt_mem = false; + control_mem = false; + shift_mem = false; + } else { // WM_INACTIVE + Input::get_singleton()->release_pressed_events(); + _send_window_event(windows[p_window_id], WINDOW_EVENT_FOCUS_OUT); + windows[p_window_id].window_focused = false; + alt_mem = false; + } + + if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[p_window_id].wtctx) { + wintab_WTEnable(windows[p_window_id].wtctx, GET_WM_ACTIVATE_STATE(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: { + // extended keys should only be processed as WM_KEYDOWN message. + if (!KeyMappingWindows::is_extended_key(ke.wParam) && ((i == 0 && ke.uMsg == WM_CHAR) || (i > 0 && key_event_buffer[i - 1].uMsg == WM_CHAR))) { + static char32_t prev_wc = 0; + char32_t unicode = ke.wParam; + if ((unicode & 0xfffffc00) == 0xd800) { + if (prev_wc != 0) { + ERR_PRINT("invalid utf16 surrogate input"); + } + prev_wc = unicode; + break; // Skip surrogate. + } else if ((unicode & 0xfffffc00) == 0xdc00) { + if (prev_wc == 0) { + ERR_PRINT("invalid utf16 surrogate input"); + break; // Skip invalid surrogate. + } + unicode = (prev_wc << 10UL) + unicode - ((0xd800 << 10UL) + 0xdc00 - 0x10000); + prev_wc = 0; + } else { + prev_wc = 0; + } + Ref<InputEventKey> k; + k.instantiate(); + + k->set_window_id(ke.window_id); + k->set_shift_pressed(ke.shift); + k->set_alt_pressed(ke.alt); + k->set_ctrl_pressed(ke.control); + k->set_meta_pressed(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(unicode); + if (k->get_unicode() && gr_mem) { + k->set_alt_pressed(false); + k->set_ctrl_pressed(false); + } + + if (k->get_unicode() < 32) + k->set_unicode(0); + + Input::get_singleton()->accumulate_input_event(k); + } + + //do nothing + } break; + case WM_KEYUP: + case WM_KEYDOWN: { + Ref<InputEventKey> k; + k.instantiate(); + + k->set_window_id(ke.window_id); + k->set_shift_pressed(ke.shift); + k->set_alt_pressed(ke.alt); + k->set_ctrl_pressed(ke.control); + k->set_meta_pressed(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) { + char32_t unicode = key_event_buffer[i + 1].wParam; + static char32_t prev_wck = 0; + if ((unicode & 0xfffffc00) == 0xd800) { + if (prev_wck != 0) { + ERR_PRINT("invalid utf16 surrogate input"); + } + prev_wck = unicode; + break; // Skip surrogate. + } else if ((unicode & 0xfffffc00) == 0xdc00) { + if (prev_wck == 0) { + ERR_PRINT("invalid utf16 surrogate input"); + break; // Skip invalid surrogate. + } + unicode = (prev_wck << 10UL) + unicode - ((0xd800 << 10UL) + 0xdc00 - 0x10000); + prev_wck = 0; + } else { + prev_wck = 0; + } + k->set_unicode(unicode); + } + if (k->get_unicode() && gr_mem) { + k->set_alt_pressed(false); + k->set_ctrl_pressed(false); + } + + if (k->get_unicode() < 32) + k->set_unicode(0); + + k->set_echo((ke.uMsg == WM_KEYDOWN && (ke.lParam & (1 << 30)))); + + Input::get_singleton()->accumulate_input_event(k); + + } break; + } + } + + key_event_pos = 0; +} + +void DisplayServerWindows::_update_tablet_ctx(const String &p_old_driver, const String &p_new_driver) { + for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { + WindowData &wd = E->get(); + wd.block_mm = false; + if ((p_old_driver == "wintab") && wintab_available && wd.wtctx) { + wintab_WTEnable(wd.wtctx, false); + wintab_WTClose(wd.wtctx); + wd.wtctx = 0; + } + if ((p_new_driver == "wintab") && wintab_available) { + wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc); + wd.wtlc.lcOptions |= CXO_MESSAGES; + wd.wtlc.lcPktData = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION; + wd.wtlc.lcMoveMask = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE; + wd.wtlc.lcPktMode = 0; + wd.wtlc.lcOutOrgX = 0; + wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX; + wd.wtlc.lcOutOrgY = 0; + wd.wtlc.lcOutExtY = -wd.wtlc.lcInExtY; + wd.wtctx = wintab_WTOpen(wd.hWnd, &wd.wtlc, false); + if (wd.wtctx) { + wintab_WTEnable(wd.wtctx, true); + AXIS pressure; + if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_NPRESSURE, &pressure)) { + wd.min_pressure = int(pressure.axMin); + wd.max_pressure = int(pressure.axMax); + } + AXIS orientation[3]; + if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_ORIENTATION, &orientation)) { + wd.tilt_supported = orientation[0].axResolution && orientation[1].axResolution; + } + wintab_WTEnable(wd.wtctx, true); + } else { + print_verbose("WinTab context creation failed."); + } + } + } +} + +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; + + if (p_mode == WINDOW_MODE_FULLSCREEN) { + int nearest_area = 0; + Rect2i screen_rect; + for (int i = 0; i < get_screen_count(); i++) { + Rect2i r; + r.position = screen_get_position(i); + r.size = screen_get_size(i); + Rect2 inters = r.intersection(p_rect); + int area = inters.size.width * inters.size.height; + if (area >= nearest_area) { + screen_rect = r; + nearest_area = area; + } + } + + WindowRect.left = screen_rect.position.x; + WindowRect.right = screen_rect.position.x + screen_rect.size.x; + WindowRect.top = screen_rect.position.y; + WindowRect.bottom = screen_rect.position.y + screen_rect.size.y; + } + + AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); + + WindowID id = window_id_counter; + { + WindowData &wd = windows[id]; + + 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, + nullptr, nullptr, hInstance, nullptr); + if (!wd.hWnd) { + MessageBoxW(nullptr, L"Window Creation Error.", L"ERROR", MB_OK | MB_ICONEXCLAMATION); + windows.erase(id); + return INVALID_WINDOW_ID; + } + if (p_mode != WINDOW_MODE_FULLSCREEN) { + wd.pre_fs_valid = true; + } +#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 = nullptr; + windows.erase(id); + ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Failed to create Vulkan Window."); + } + } +#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); + + if ((tablet_get_current_driver() == "wintab") && wintab_available) { + wintab_WTInfo(WTI_DEFSYSCTX, 0, &wd.wtlc); + wd.wtlc.lcOptions |= CXO_MESSAGES; + wd.wtlc.lcPktData = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE | PK_ORIENTATION; + wd.wtlc.lcMoveMask = PK_NORMAL_PRESSURE | PK_TANGENT_PRESSURE; + wd.wtlc.lcPktMode = 0; + wd.wtlc.lcOutOrgX = 0; + wd.wtlc.lcOutExtX = wd.wtlc.lcInExtX; + wd.wtlc.lcOutOrgY = 0; + wd.wtlc.lcOutExtY = -wd.wtlc.lcInExtY; + wd.wtctx = wintab_WTOpen(wd.hWnd, &wd.wtlc, false); + if (wd.wtctx) { + wintab_WTEnable(wd.wtctx, true); + AXIS pressure; + if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_NPRESSURE, &pressure)) { + wd.min_pressure = int(pressure.axMin); + wd.max_pressure = int(pressure.axMax); + } + AXIS orientation[3]; + if (wintab_WTInfo(WTI_DEVICES + wd.wtlc.lcDevice, DVC_ORIENTATION, &orientation)) { + wd.tilt_supported = orientation[0].axResolution && orientation[1].axResolution; + } + } else { + print_verbose("WinTab context creation failed."); + } + } else { + wd.wtctx = 0; + } + + wd.last_pressure = 0; + wd.last_pressure_update = 0; + wd.last_tilt = Vector2(); + + // 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; + + window_id_counter++; + } + + return id; +} + +// WinTab API +bool DisplayServerWindows::wintab_available = false; +WTOpenPtr DisplayServerWindows::wintab_WTOpen = nullptr; +WTClosePtr DisplayServerWindows::wintab_WTClose = nullptr; +WTInfoPtr DisplayServerWindows::wintab_WTInfo = nullptr; +WTPacketPtr DisplayServerWindows::wintab_WTPacket = nullptr; +WTEnablePtr DisplayServerWindows::wintab_WTEnable = nullptr; + +// Windows Ink API +bool DisplayServerWindows::winink_available = false; +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; + +int DisplayServerWindows::tablet_get_driver_count() const { + return tablet_drivers.size(); +} + +String DisplayServerWindows::tablet_get_driver_name(int p_driver) const { + if (p_driver < 0 || p_driver >= tablet_drivers.size()) { + return ""; + } else { + return tablet_drivers[p_driver]; + } +} + +String DisplayServerWindows::tablet_get_current_driver() const { + return tablet_driver; +} + +void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) { + if (tablet_get_driver_count() == 0) { + return; + } + bool found = false; + for (int i = 0; i < tablet_get_driver_count(); i++) { + if (p_driver == tablet_get_driver_name(i)) { + found = true; + } + } + if (found) { + _update_tablet_ctx(tablet_driver, p_driver); + tablet_driver = p_driver; + } else { + ERR_PRINT("Unknown tablet driver " + p_driver + "."); + } +} + +DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { + 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; + + //Note: Wacom WinTab driver API for pen input, for devices incompatible with Windows Ink. + HMODULE wintab_lib = LoadLibraryW(L"wintab32.dll"); + if (wintab_lib) { + wintab_WTOpen = (WTOpenPtr)GetProcAddress(wintab_lib, "WTOpenW"); + wintab_WTClose = (WTClosePtr)GetProcAddress(wintab_lib, "WTClose"); + wintab_WTInfo = (WTInfoPtr)GetProcAddress(wintab_lib, "WTInfoW"); + wintab_WTPacket = (WTPacketPtr)GetProcAddress(wintab_lib, "WTPacket"); + wintab_WTEnable = (WTEnablePtr)GetProcAddress(wintab_lib, "WTEnable"); + + wintab_available = wintab_WTOpen && wintab_WTClose && wintab_WTInfo && wintab_WTPacket && wintab_WTEnable; + } + + if (wintab_available) { + tablet_drivers.push_back("wintab"); + } + + //Note: Windows Ink API for pen input, available on Windows 8+ only. + HMODULE user32_lib = LoadLibraryW(L"user32.dll"); + if (user32_lib) { + win8p_GetPointerType = (GetPointerTypePtr)GetProcAddress(user32_lib, "GetPointerType"); + win8p_GetPointerPenInfo = (GetPointerPenInfoPtr)GetProcAddress(user32_lib, "GetPointerPenInfo"); + + winink_available = win8p_GetPointerType && win8p_GetPointerPenInfo; + } + + if (winink_available) { + tablet_drivers.push_back("winink"); + } + + if (OS::get_singleton()->is_hidpi_allowed()) { + HMODULE Shcore = LoadLibraryW(L"Shcore.dll"); + + if (Shcore != nullptr) { + 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(nullptr); + wc.hIcon = LoadIcon(nullptr, IDI_WINLOGO); + wc.hCursor = nullptr; //LoadCursor(nullptr, IDC_ARROW); + wc.hbrBackground = nullptr; + wc.lpszMenuName = nullptr; + wc.lpszClassName = L"Engine"; + + if (!RegisterClassExW(&wc)) { + MessageBox(nullptr, "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 = nullptr; + 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 = nullptr; + 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 = nullptr; + ERR_FAIL_V(ERR_UNAVAILABLE); + } + } +#endif + Point2i window_position( + (screen_get_size(0).width - p_resolution.width) / 2, + (screen_get_size(0).height - p_resolution.height) / 2); + + WindowID main_window = _create_window(p_mode, 0, Rect2i(window_position, p_resolution)); + ERR_FAIL_COND_MSG(main_window == INVALID_WINDOW_ID, "Failed to create main window."); + + for (int i = 0; i < WINDOW_FLAG_MAX; i++) { + if (p_flags & (1 << i)) { + window_set_flag(WindowFlags(i), true, main_window); + } + } + + show_window(MAIN_WINDOW_ID); + +#if defined(VULKAN_ENABLED) + + if (rendering_driver == "vulkan") { + rendering_device_vulkan = memnew(RenderingDeviceVulkan); + rendering_device_vulkan->initialize(context_vulkan); + + RendererCompositorRD::make_current(); + } +#endif + + //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); + Input::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) { + DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_flags, p_resolution, r_error)); + if (r_error != OK) { + ds->alert("Your video card driver does not support any of the supported Vulkan versions.\n" + "Please update your drivers or if you have a very old or integrated GPU upgrade it.", + "Unable to initialize Video driver"); + } + return ds; +} + +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 (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 + if (wintab_available && windows[MAIN_WINDOW_ID].wtctx) { + wintab_WTClose(windows[MAIN_WINDOW_ID].wtctx); + windows[MAIN_WINDOW_ID].wtctx = 0; + } + DestroyWindow(windows[MAIN_WINDOW_ID].hWnd); + } + +#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 +} diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h new file mode 100644 index 0000000000..d71e579c67 --- /dev/null +++ b/platform/windows/display_server_windows.h @@ -0,0 +1,574 @@ +/*************************************************************************/ +/* display_server_windows.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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_WINDOWS_H +#define DISPLAY_SERVER_WINDOWS_H + +#include "servers/display_server.h" + +#include "core/config/project_settings.h" +#include "core/input/input.h" +#include "core/os/os.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/rendering/renderer_compositor.h" +#include "servers/rendering/renderer_rd/renderer_compositor_rd.h" +#include "servers/rendering_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> + +// WinTab API +#define WT_PACKET 0x7FF0 +#define WT_PROXIMITY 0x7FF5 +#define WT_INFOCHANGE 0x7FF6 +#define WT_CSRCHANGE 0x7FF7 + +#define WTI_DEFSYSCTX 4 +#define WTI_DEVICES 100 +#define DVC_NPRESSURE 15 +#define DVC_TPRESSURE 16 +#define DVC_ORIENTATION 17 +#define DVC_ROTATION 18 + +#define CXO_MESSAGES 0x0004 +#define PK_NORMAL_PRESSURE 0x0400 +#define PK_TANGENT_PRESSURE 0x0800 +#define PK_ORIENTATION 0x1000 + +typedef struct tagLOGCONTEXTW { + WCHAR lcName[40]; + UINT lcOptions; + UINT lcStatus; + UINT lcLocks; + UINT lcMsgBase; + UINT lcDevice; + UINT lcPktRate; + DWORD lcPktData; + DWORD lcPktMode; + DWORD lcMoveMask; + DWORD lcBtnDnMask; + DWORD lcBtnUpMask; + LONG lcInOrgX; + LONG lcInOrgY; + LONG lcInOrgZ; + LONG lcInExtX; + LONG lcInExtY; + LONG lcInExtZ; + LONG lcOutOrgX; + LONG lcOutOrgY; + LONG lcOutOrgZ; + LONG lcOutExtX; + LONG lcOutExtY; + LONG lcOutExtZ; + DWORD lcSensX; + DWORD lcSensY; + DWORD lcSensZ; + BOOL lcSysMode; + int lcSysOrgX; + int lcSysOrgY; + int lcSysExtX; + int lcSysExtY; + DWORD lcSysSensX; + DWORD lcSysSensY; +} LOGCONTEXTW; + +typedef struct tagAXIS { + LONG axMin; + LONG axMax; + UINT axUnits; + DWORD axResolution; +} AXIS; + +typedef struct tagORIENTATION { + int orAzimuth; + int orAltitude; + int orTwist; +} ORIENTATION; + +typedef struct tagPACKET { + int pkNormalPressure; + int pkTangentPressure; + ORIENTATION pkOrientation; +} PACKET; + +typedef HANDLE(WINAPI *WTOpenPtr)(HWND p_window, LOGCONTEXTW *p_ctx, BOOL p_enable); +typedef BOOL(WINAPI *WTClosePtr)(HANDLE p_ctx); +typedef UINT(WINAPI *WTInfoPtr)(UINT p_category, UINT p_index, LPVOID p_output); +typedef BOOL(WINAPI *WTPacketPtr)(HANDLE p_ctx, UINT p_param, LPVOID p_packets); +typedef BOOL(WINAPI *WTEnablePtr)(HANDLE p_ctx, BOOL p_enable); + +// Windows Ink API +#ifndef POINTER_STRUCTURES + +#define POINTER_STRUCTURES + +typedef DWORD POINTER_INPUT_TYPE; +typedef UINT32 POINTER_FLAGS; +typedef UINT32 PEN_FLAGS; +typedef UINT32 PEN_MASK; + +#ifndef PEN_MASK_PRESSURE +#define PEN_MASK_PRESSURE 0x00000001 +#endif + +#ifndef PEN_MASK_TILT_X +#define PEN_MASK_TILT_X 0x00000004 +#endif + +#ifndef PEN_MASK_TILT_Y +#define PEN_MASK_TILT_Y 0x00000008 +#endif + +#ifndef POINTER_MESSAGE_FLAG_FIRSTBUTTON +#define POINTER_MESSAGE_FLAG_FIRSTBUTTON 0x00000010 +#endif + +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 + +#ifndef WM_POINTERUPDATE +#define WM_POINTERUPDATE 0x0245 +#endif + +#ifndef WM_POINTERENTER +#define WM_POINTERENTER 0x0249 +#endif + +#ifndef WM_POINTERLEAVE +#define WM_POINTERLEAVE 0x024A +#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 DisplayServerWindows : public DisplayServer { + //No need to register, it's platform-specific and nothing is added + //GDCLASS(DisplayServerWindows, DisplayServer) + + _THREAD_SAFE_CLASS_ + + // WinTab API + static bool wintab_available; + static WTOpenPtr wintab_WTOpen; + static WTClosePtr wintab_WTClose; + static WTInfoPtr wintab_WTInfo; + static WTPacketPtr wintab_WTPacket; + static WTEnablePtr wintab_WTEnable; + + // Windows Ink API + static bool winink_available; + static GetPointerTypePtr win8p_GetPointerType; + static GetPointerPenInfoPtr win8p_GetPointerPenInfo; + + void _update_tablet_ctx(const String &p_old_driver, const String &p_new_driver); + String tablet_driver; + Vector<String> tablet_drivers; + + 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; + bool app_focused = false; + + struct WindowData { + HWND hWnd; + //layered window + + Vector<Vector2> mpath; + + 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; + + // Used to transfer data between events using timer. + WPARAM saved_wparam; + LPARAM saved_lparam; + + // Timers. + uint32_t move_timer_id = 0U; + uint32_t focus_timer_id = 0U; + + HANDLE wtctx; + LOGCONTEXTW wtlc; + int min_pressure; + int max_pressure; + bool tilt_supported; + bool block_mm = false; + + int last_pressure_update; + float last_pressure; + Vector2 last_tilt; + + 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; + + 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; + MouseButton last_button_state = MOUSE_BUTTON_NONE; + bool use_raw_input = false; + bool drop_events = false; + bool in_dispatch_input_event = false; + bool console_visible = false; + + WNDCLASSEXW wc; + + HCURSOR cursors[CURSOR_MAX] = { nullptr }; + CursorShape cursor_shape = CursorShape::CURSOR_ARROW; + 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); + void _update_window_mouse_passthrough(WindowID p_window); + + void _update_real_mouse_position(WindowID p_window); + + void _set_mouse_mode_impl(MouseMode p_mode); + + void _process_activate_event(WindowID p_window_id, WPARAM wParam, LPARAM lParam); + 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 MouseButton 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 show_window(WindowID p_window); + 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 void window_set_mouse_passthrough(const Vector<Vector2> &p_region, 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_cancel_ok(); + + virtual void enable_for_stealing_focus(OS::ProcessID pid); + + virtual int keyboard_get_layout_count() const; + virtual int keyboard_get_current_layout() const; + virtual void keyboard_set_current_layout(int p_index); + virtual String keyboard_get_layout_language(int p_index) const; + virtual String keyboard_get_layout_name(int p_index) const; + + virtual int tablet_get_driver_count() const; + virtual String tablet_get_driver_name(int p_driver) const; + virtual String tablet_get_current_driver() const; + virtual void tablet_set_current_driver(const String &p_driver); + + 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/export/export.cpp b/platform/windows/export/export.cpp index 34d66ecd79..10f953f2ec 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/os/file_access.h" +#include "core/io/file_access.h" #include "core/os/os.h" #include "editor/editor_export.h" #include "editor/editor_node.h" @@ -38,7 +38,6 @@ static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size); class EditorExportPlatformWindows : public EditorExportPlatformPC { - void _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path); Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path); @@ -85,7 +84,7 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/timestamp_server_url"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/digest_algorithm", PROPERTY_HINT_ENUM, "SHA1,SHA256"), 1)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/description"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::POOL_STRING_ARRAY, "codesign/custom_options"), PoolStringArray())); + r_options->push_back(ExportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "codesign/custom_options"), PackedStringArray())); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); @@ -105,7 +104,7 @@ void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> } if (!FileAccess::exists(rcedit_path)) { - ERR_PRINTS("Could not find rcedit executable at " + rcedit_path + ", no icon or app information data will be included."); + ERR_PRINT("Could not find rcedit executable at " + rcedit_path + ", no icon or app information data will be included."); return; } @@ -114,7 +113,7 @@ void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> String wine_path = EditorSettings::get_singleton()->get("export/windows/wine"); if (wine_path != String() && !FileAccess::exists(wine_path)) { - ERR_PRINTS("Could not find wine executable at " + wine_path + ", no icon or app information data will be included."); + ERR_PRINT("Could not find wine executable at " + wine_path + ", no icon or app information data will be included."); return; } @@ -174,11 +173,11 @@ void EditorExportPlatformWindows::_rcedit_add_data(const Ref<EditorExportPreset> } #ifdef WINDOWS_ENABLED - OS::get_singleton()->execute(rcedit_path, args, true); + OS::get_singleton()->execute(rcedit_path, args); #else // On non-Windows we need WINE to run rcedit args.push_front(rcedit_path); - OS::get_singleton()->execute(wine_path, args, true); + OS::get_singleton()->execute(wine_path, args); #endif } @@ -188,7 +187,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p #ifdef WINDOWS_ENABLED String signtool_path = EditorSettings::get_singleton()->get("export/windows/signtool"); if (signtool_path != String() && !FileAccess::exists(signtool_path)) { - ERR_PRINTS("Could not find signtool executable at " + signtool_path + ", aborting."); + ERR_PRINT("Could not find signtool executable at " + signtool_path + ", aborting."); return ERR_FILE_NOT_FOUND; } if (signtool_path == String()) { @@ -197,7 +196,7 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p #else String signtool_path = EditorSettings::get_singleton()->get("export/windows/osslsigncode"); if (signtool_path != String() && !FileAccess::exists(signtool_path)) { - ERR_PRINTS("Could not find osslsigncode executable at " + signtool_path + ", aborting."); + ERR_PRINT("Could not find osslsigncode executable at " + signtool_path + ", aborting."); return ERR_FILE_NOT_FOUND; } if (signtool_path == String()) { @@ -297,10 +296,10 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p } //user options - PoolStringArray user_args = p_preset->get("codesign/custom_options"); + PackedStringArray user_args = p_preset->get("codesign/custom_options"); for (int i = 0; i < user_args.size(); i++) { String user_arg = user_args[i].strip_edges(); - if (!user_arg.empty()) { + if (!user_arg.is_empty()) { args.push_back(user_arg); } } @@ -311,11 +310,11 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p args.push_back(p_path); #ifndef WINDOWS_ENABLED args.push_back("-out"); - args.push_back(p_path); + args.push_back(p_path + "_signed"); #endif String str; - Error err = OS::get_singleton()->execute(signtool_path, args, true, NULL, &str, NULL, true); + Error err = OS::get_singleton()->execute(signtool_path, args, &str, nullptr, true); ERR_FAIL_COND_V(err != OK, err); print_line("codesign (" + p_path + "): " + str); @@ -327,11 +326,20 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p return FAILED; } +#ifndef WINDOWS_ENABLED + DirAccessRef tmp_dir = DirAccess::create_for_path(p_path.get_base_dir()); + + err = tmp_dir->remove(p_path); + ERR_FAIL_COND_V(err != OK, err); + + err = tmp_dir->rename(p_path + "_signed", p_path); + ERR_FAIL_COND_V(err != OK, err); +#endif + return OK; } void register_windows_exporter() { - EDITOR_DEF("export/windows/rcedit", ""); EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/rcedit", PROPERTY_HINT_GLOBAL_FILE, "*.exe")); #ifdef WINDOWS_ENABLED @@ -346,11 +354,11 @@ void register_windows_exporter() { #endif Ref<EditorExportPlatformWindows> platform; - platform.instance(); + platform.instantiate(); Ref<Image> img = memnew(Image(_windows_logo)); Ref<ImageTexture> logo; - logo.instance(); + logo.instantiate(); logo->create_from_image(img); platform->set_logo(logo); platform->set_name("Windows Desktop"); @@ -366,7 +374,6 @@ void register_windows_exporter() { } static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) { - // Patch the header of the "pck" section in the PE file so that it corresponds to the embedded data FileAccess *f = FileAccess::open(p_path, FileAccess::READ_WRITE); @@ -408,7 +415,6 @@ static Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, bool found = false; for (int i = 0; i < num_sections; ++i) { - int64_t section_header_pos = section_table_pos + i * 40; f->seek(section_header_pos); diff --git a/platform/windows/export/export.h b/platform/windows/export/export.h index d669192831..6a7131c73f 100644 --- a/platform/windows/export/export.h +++ b/platform/windows/export/export.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ diff --git a/platform/windows/godot.natvis b/platform/windows/godot.natvis index 593557cc69..bb855e4ac8 100644 --- a/platform/windows/godot.natvis +++ b/platform/windows/godot.natvis @@ -10,16 +10,16 @@ </Expand> </Type> - <Type Name="PoolVector<*>"> + <Type Name="LocalVector<*>"> <Expand> - <Item Name="[size]">alloc ? (alloc->size / sizeof($T1)) : 0</Item> + <Item Name="[size]">count</Item> <ArrayItems> - <Size>alloc ? (alloc->size / sizeof($T1)) : 0</Size> - <ValuePointer>alloc ? (($T1 *)alloc->mem) : 0</ValuePointer> + <Size>count</Size> + <ValuePointer>data</ValuePointer> </ArrayItems> </Expand> </Type> - + <Type Name="List<*>"> <Expand> <Item Name="[size]">_data ? (_data->size_cache) : 0</Item> @@ -36,75 +36,79 @@ <DisplayString Condition="type == Variant::NIL">nil</DisplayString> <DisplayString Condition="type == Variant::BOOL">{_data._bool}</DisplayString> <DisplayString Condition="type == Variant::INT">{_data._int}</DisplayString> - <DisplayString Condition="type == Variant::REAL">{_data._real}</DisplayString> + <DisplayString Condition="type == Variant::FLOAT">{_data._float}</DisplayString> <DisplayString Condition="type == Variant::TRANSFORM2D">{_data._transform2d}</DisplayString> <DisplayString Condition="type == Variant::AABB">{_data._aabb}</DisplayString> <DisplayString Condition="type == Variant::BASIS">{_data._basis}</DisplayString> - <DisplayString Condition="type == Variant::TRANSFORM">{_data._transform}</DisplayString> + <DisplayString Condition="type == Variant::TRANSFORM3D">{_data._transform}</DisplayString> <DisplayString Condition="type == Variant::STRING">{*(String *)_data._mem}</DisplayString> <DisplayString Condition="type == Variant::VECTOR2">{*(Vector2 *)_data._mem}</DisplayString> <DisplayString Condition="type == Variant::RECT2">{*(Rect2 *)_data._mem}</DisplayString> <DisplayString Condition="type == Variant::VECTOR3">{*(Vector3 *)_data._mem}</DisplayString> <DisplayString Condition="type == Variant::PLANE">{*(Plane *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::QUAT">{*(Quat *)_data._mem}</DisplayString> + <DisplayString Condition="type == Variant::QUATERNION">{*(Quaternion *)_data._mem}</DisplayString> <DisplayString Condition="type == Variant::COLOR">{*(Color *)_data._mem}</DisplayString> <DisplayString Condition="type == Variant::NODE_PATH">{*(NodePath *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::_RID">{*(RID *)_data._mem}</DisplayString> + <DisplayString Condition="type == Variant::RID">{*(::RID *)_data._mem}</DisplayString> <DisplayString Condition="type == Variant::OBJECT">{*(Object *)_data._mem}</DisplayString> <DisplayString Condition="type == Variant::DICTIONARY">{*(Dictionary *)_data._mem}</DisplayString> <DisplayString Condition="type == Variant::ARRAY">{*(Array *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::POOL_BYTE_ARRAY">{*(PoolByteArray *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::POOL_INT_ARRAY">{*(PoolIntArray *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::POOL_REAL_ARRAY">{*(PoolRealArray *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::POOL_STRING_ARRAY">{*(PoolStringArray *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::POOL_VECTOR2_ARRAY">{*(PoolVector2Array *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::POOL_VECTOR3_ARRAY">{*(PoolVector3Array *)_data._mem}</DisplayString> - <DisplayString Condition="type == Variant::POOL_COLOR_ARRAY">{*(PoolColorArray *)_data._mem}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_BYTE_ARRAY">{*(PackedByteArray *)_data._mem}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_INT32_ARRAY">{*(PackedInt32Array *)_data._mem}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_INT64_ARRAY">{*(PackedInt64Array *)_data._mem}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_FLOAT32_ARRAY">{*(PackedFloat32Array *)_data._mem}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_FLOAT64_ARRAY">{*(PackedFloat64Array *)_data._mem}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_STRING_ARRAY">{*(PackedStringArray *)_data._mem}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_VECTOR2_ARRAY">{*(PackedVector2Array *)_data._mem}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_VECTOR3_ARRAY">{*(PackedVector3Array *)_data._mem}</DisplayString> + <DisplayString Condition="type == Variant::PACKED_COLOR_ARRAY">{*(PackedColorArray *)_data._mem}</DisplayString> + + <StringView Condition="type == Variant::STRING && ((String *)(_data._mem))->_cowdata._ptr">((String *)(_data._mem))->_cowdata._ptr,s32</StringView> - <StringView Condition="type == Variant::STRING && ((String *)(_data._mem))->_cowdata._ptr">((String *)(_data._mem))->_cowdata._ptr,su</StringView> - <Expand> <Item Name="[value]" Condition="type == Variant::BOOL">_data._bool</Item> <Item Name="[value]" Condition="type == Variant::INT">_data._int</Item> - <Item Name="[value]" Condition="type == Variant::REAL">_data._real</Item> + <Item Name="[value]" Condition="type == Variant::FLOAT">_data._float</Item> <Item Name="[value]" Condition="type == Variant::TRANSFORM2D">_data._transform2d</Item> <Item Name="[value]" Condition="type == Variant::AABB">_data._aabb</Item> <Item Name="[value]" Condition="type == Variant::BASIS">_data._basis</Item> - <Item Name="[value]" Condition="type == Variant::TRANSFORM">_data._transform</Item> + <Item Name="[value]" Condition="type == Variant::TRANSFORM3D">_data._transform</Item> <Item Name="[value]" Condition="type == Variant::STRING">*(String *)_data._mem</Item> <Item Name="[value]" Condition="type == Variant::VECTOR2">*(Vector2 *)_data._mem</Item> <Item Name="[value]" Condition="type == Variant::RECT2">*(Rect2 *)_data._mem</Item> <Item Name="[value]" Condition="type == Variant::VECTOR3">*(Vector3 *)_data._mem</Item> <Item Name="[value]" Condition="type == Variant::PLANE">*(Plane *)_data._mem</Item> - <Item Name="[value]" Condition="type == Variant::QUAT">*(Quat *)_data._mem</Item> + <Item Name="[value]" Condition="type == Variant::QUATERNION">*(Quaternion *)_data._mem</Item> <Item Name="[value]" Condition="type == Variant::COLOR">*(Color *)_data._mem</Item> <Item Name="[value]" Condition="type == Variant::NODE_PATH">*(NodePath *)_data._mem</Item> - <Item Name="[value]" Condition="type == Variant::_RID">*(RID *)_data._mem</Item> + <Item Name="[value]" Condition="type == Variant::RID">*(::RID *)_data._mem</Item> <Item Name="[value]" Condition="type == Variant::OBJECT">*(Object *)_data._mem</Item> <Item Name="[value]" Condition="type == Variant::DICTIONARY">*(Dictionary *)_data._mem</Item> <Item Name="[value]" Condition="type == Variant::ARRAY">*(Array *)_data._mem</Item> - <Item Name="[value]" Condition="type == Variant::POOL_BYTE_ARRAY">*(PoolByteArray *)_data._mem</Item> - <Item Name="[value]" Condition="type == Variant::POOL_INT_ARRAY">*(PoolIntArray *)_data._mem</Item> - <Item Name="[value]" Condition="type == Variant::POOL_REAL_ARRAY">*(PoolRealArray *)_data._mem</Item> - <Item Name="[value]" Condition="type == Variant::POOL_STRING_ARRAY">*(PoolStringArray *)_data._mem</Item> - <Item Name="[value]" Condition="type == Variant::POOL_VECTOR2_ARRAY">*(PoolVector2Array *)_data._mem</Item> - <Item Name="[value]" Condition="type == Variant::POOL_VECTOR3_ARRAY">*(PoolVector3Array *)_data._mem</Item> - <Item Name="[value]" Condition="type == Variant::POOL_COLOR_ARRAY">*(PoolColorArray *)_data._mem</Item> + <Item Name="[value]" Condition="type == Variant::PACKED_BYTE_ARRAY">*(PackedByteArray *)_data._mem</Item> + <Item Name="[value]" Condition="type == Variant::PACKED_INT32_ARRAY">*(PackedInt32Array *)_data._mem</Item> + <Item Name="[value]" Condition="type == Variant::PACKED_INT64_ARRAY">*(PackedInt64Array *)_data._mem</Item> + <Item Name="[value]" Condition="type == Variant::PACKED_FLOAT32_ARRAY">*(PackedFloat32Array *)_data._mem</Item> + <Item Name="[value]" Condition="type == Variant::PACKED_FLOAT64_ARRAY">*(PackedFloat64Array *)_data._mem</Item> + <Item Name="[value]" Condition="type == Variant::PACKED_STRING_ARRAY">*(PackedStringArray *)_data._mem</Item> + <Item Name="[value]" Condition="type == Variant::PACKED_VECTOR2_ARRAY">*(PackedVector2Array *)_data._mem</Item> + <Item Name="[value]" Condition="type == Variant::PACKED_VECTOR3_ARRAY">*(PackedVector3Array *)_data._mem</Item> + <Item Name="[value]" Condition="type == Variant::PACKED_COLOR_ARRAY">*(PackedColorArray *)_data._mem</Item> </Expand> </Type> <Type Name="String"> <DisplayString Condition="_cowdata._ptr == 0">[empty]</DisplayString> - <DisplayString Condition="_cowdata._ptr != 0">{_cowdata._ptr,su}</DisplayString> - <StringView Condition="_cowdata._ptr != 0">_cowdata._ptr,su</StringView> + <DisplayString Condition="_cowdata._ptr != 0">{_cowdata._ptr,s32}</DisplayString> + <StringView Condition="_cowdata._ptr != 0">_cowdata._ptr,s32</StringView> </Type> <Type Name="StringName"> <DisplayString Condition="_data && _data->cname">{_data->cname}</DisplayString> - <DisplayString Condition="_data && !_data->cname">{_data->name,su}</DisplayString> + <DisplayString Condition="_data && !_data->cname">{_data->name,s32}</DisplayString> <DisplayString Condition="!_data">[empty]</DisplayString> <StringView Condition="_data && _data->cname">_data->cname</StringView> - <StringView Condition="_data && !_data->cname">_data->name,su</StringView> + <StringView Condition="_data && !_data->cname">_data->name,s32</StringView> </Type> <Type Name="Vector2"> @@ -124,8 +128,8 @@ </Expand> </Type> - <Type Name="Quat"> - <DisplayString>Quat {{{x},{y},{z},{w}}}</DisplayString> + <Type Name="Quaternion"> + <DisplayString>Quaternion {{{x},{y},{z},{w}}}</DisplayString> <Expand> <Item Name="x">x</Item> <Item Name="y">y</Item> @@ -143,7 +147,7 @@ <Item Name="alpha">a</Item> </Expand> </Type> - + <Type Name="Node" Inheritable="false"> <Expand> <Item Name="Object">(Object*)this</Item> diff --git a/platform/windows/godot_windows.cpp b/platform/windows/godot_windows.cpp index dcc12b7649..22e2e5f7e5 100644 --- a/platform/windows/godot_windows.cpp +++ b/platform/windows/godot_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -121,23 +121,22 @@ CommandLineToArgvA( i++; } _argv[j] = '\0'; - argv[argc] = NULL; + argv[argc] = nullptr; (*_argc) = argc; return argv; } char *wc_to_utf8(const wchar_t *wc) { - int ulen = WideCharToMultiByte(CP_UTF8, 0, wc, -1, NULL, 0, NULL, NULL); + int ulen = WideCharToMultiByte(CP_UTF8, 0, wc, -1, nullptr, 0, nullptr, nullptr); char *ubuf = new char[ulen + 1]; - WideCharToMultiByte(CP_UTF8, 0, wc, -1, ubuf, ulen, NULL, NULL); + WideCharToMultiByte(CP_UTF8, 0, wc, -1, ubuf, ulen, nullptr, nullptr); ubuf[ulen] = 0; return ubuf; } int widechar_main(int argc, wchar_t **argv) { - - OS_Windows os(NULL); + OS_Windows os(nullptr); setlocale(LC_CTYPE, ""); @@ -147,6 +146,8 @@ int widechar_main(int argc, wchar_t **argv) { argv_utf8[i] = wc_to_utf8(argv[i]); } + TEST_MAIN_PARAM_OVERRIDE(argc, argv_utf8) + Error err = Main::setup(argv_utf8[0], argc - 1, &argv_utf8[1]); if (err != OK) { @@ -176,7 +177,7 @@ int _main() { wc_argv = CommandLineToArgvW(GetCommandLineW(), &argc); - if (NULL == wc_argv) { + if (nullptr == wc_argv) { wprintf(L"CommandLineToArgvW failed\n"); return 0; } @@ -187,10 +188,12 @@ int _main() { return result; } -int main(int _argc, char **_argv) { +int main(int argc, char **argv) { + // override the arguments for the test handler / if symbol is provided + // TEST_MAIN_OVERRIDE + // _argc and _argv are ignored // we are going to use the WideChar version of them instead - #ifdef CRASH_HANDLER_EXCEPTION __try { return _main(); @@ -202,9 +205,9 @@ int main(int _argc, char **_argv) { #endif } -HINSTANCE godot_hinstance = NULL; +HINSTANCE godot_hinstance = nullptr; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { godot_hinstance = hInstance; - return main(0, NULL); + return main(0, nullptr); } diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp index 49432435b9..94da63e49d 100644 --- a/platform/windows/joypad_windows.cpp +++ b/platform/windows/joypad_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,10 +33,6 @@ #include <oleauto.h> #include <wbemidl.h> -#ifndef __GNUC__ -#define __builtin_bswap32 _byteswap_ulong -#endif - #if defined(__GNUC__) // Workaround GCC warning from -Wcast-function-type. #define GetProcAddress (void *)GetProcAddress @@ -45,6 +41,7 @@ DWORD WINAPI _xinput_get_state(DWORD dwUserIndex, XINPUT_STATE *pState) { return ERROR_DEVICE_NOT_CONNECTED; } + DWORD WINAPI _xinput_set_state(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration) { return ERROR_DEVICE_NOT_CONNECTED; } @@ -52,42 +49,45 @@ DWORD WINAPI _xinput_set_state(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration) JoypadWindows::JoypadWindows() { } -JoypadWindows::JoypadWindows(InputDefault *_input, HWND *hwnd) { - - input = _input; +JoypadWindows::JoypadWindows(HWND *hwnd) { + input = Input::get_singleton(); hWnd = hwnd; joypad_count = 0; - dinput = NULL; - xinput_dll = NULL; - xinput_get_state = NULL; - xinput_set_state = NULL; + dinput = nullptr; + xinput_dll = nullptr; + xinput_get_state = nullptr; + xinput_set_state = nullptr; load_xinput(); for (int i = 0; i < JOYPADS_MAX; i++) attached_joypads[i] = false; - HRESULT result; - result = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, NULL); - if (FAILED(result)) { - printf("failed init DINPUT: %ld\n", result); + HRESULT result = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr); + if (result == DI_OK) { + probe_joypads(); + } else { + ERR_PRINT("Couldn't initialize DirectInput. Error: " + itos(result)); + if (result == DIERR_OUTOFMEMORY) { + ERR_PRINT("The Windows DirectInput subsystem could not allocate sufficient memory."); + ERR_PRINT("Rebooting your PC may solve this issue."); + } + // Ensure dinput is still a nullptr. + dinput = nullptr; } - probe_joypads(); } JoypadWindows::~JoypadWindows() { - close_joypad(); - dinput->Release(); + if (dinput) { + dinput->Release(); + } unload_xinput(); } bool JoypadWindows::have_device(const GUID &p_guid) { - for (int i = 0; i < JOYPADS_MAX; i++) { - if (d_joypads[i].guid == p_guid) { - d_joypads[i].confirmed = true; return true; } @@ -97,7 +97,6 @@ bool JoypadWindows::have_device(const GUID &p_guid) { // adapted from SDL2, works a lot better than the MSDN version bool JoypadWindows::is_xinput_device(const GUID *p_guid) { - static GUID IID_ValveStreamingGamepad = { MAKELONG(0x28DE, 0x11FF), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }; static GUID IID_X360WiredGamepad = { MAKELONG(0x045E, 0x02A1), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }; static GUID IID_X360WirelessGamepad = { MAKELONG(0x045E, 0x028E), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } }; @@ -105,21 +104,20 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) { if (p_guid == &IID_ValveStreamingGamepad || p_guid == &IID_X360WiredGamepad || p_guid == &IID_X360WirelessGamepad) return true; - PRAWINPUTDEVICELIST dev_list = NULL; + PRAWINPUTDEVICELIST dev_list = nullptr; unsigned int dev_list_count = 0; - if (GetRawInputDeviceList(NULL, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) { + if (GetRawInputDeviceList(nullptr, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) { return false; } - dev_list = (PRAWINPUTDEVICELIST)malloc(sizeof(RAWINPUTDEVICELIST) * dev_list_count); - if (!dev_list) return false; + dev_list = (PRAWINPUTDEVICELIST)memalloc(sizeof(RAWINPUTDEVICELIST) * dev_list_count); + ERR_FAIL_NULL_V_MSG(dev_list, false, "Out of memory."); if (GetRawInputDeviceList(dev_list, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) { - free(dev_list); + memfree(dev_list); return false; } for (unsigned int i = 0; i < dev_list_count; i++) { - RID_DEVICE_INFO rdi; char dev_name[128]; UINT rdiSize = sizeof(rdi); @@ -130,34 +128,33 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) { (GetRawInputDeviceInfoA(dev_list[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != (UINT)-1) && (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == (LONG)p_guid->Data1) && (GetRawInputDeviceInfoA(dev_list[i].hDevice, RIDI_DEVICENAME, &dev_name, &nameSize) != (UINT)-1) && - (strstr(dev_name, "IG_") != NULL)) { - - free(dev_list); + (strstr(dev_name, "IG_") != nullptr)) { + memfree(dev_list); return true; } } - free(dev_list); + memfree(dev_list); return false; } bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) { - + ERR_FAIL_NULL_V_MSG(dinput, false, "DirectInput not initialized. Rebooting your PC may solve this issue."); HRESULT hr; int num = input->get_unused_joy_id(); if (have_device(instance->guidInstance) || num == -1) return false; - d_joypads[joypad_count] = dinput_gamepad(); - dinput_gamepad *joy = &d_joypads[joypad_count]; + d_joypads[num] = dinput_gamepad(); + dinput_gamepad *joy = &d_joypads[num]; const DWORD devtype = (instance->dwDevType & 0xFF); - if ((devtype != DI8DEVTYPE_JOYSTICK) && (devtype != DI8DEVTYPE_GAMEPAD) && (devtype != DI8DEVTYPE_1STPERSON)) { + if ((devtype != DI8DEVTYPE_JOYSTICK) && (devtype != DI8DEVTYPE_GAMEPAD) && (devtype != DI8DEVTYPE_1STPERSON) && (devtype != DI8DEVTYPE_DRIVING)) { return false; } - hr = dinput->CreateDevice(instance->guidInstance, &joy->di_joy, NULL); + hr = dinput->CreateDevice(instance->guidInstance, &joy->di_joy, nullptr); if (FAILED(hr)) { return false; @@ -165,12 +162,16 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) { const GUID &guid = instance->guidProduct; char uid[128]; - sprintf_s(uid, "%08lx%04hx%04hx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", - __builtin_bswap32(guid.Data1), guid.Data2, guid.Data3, - guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], - guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); - id_to_change = joypad_count; + ERR_FAIL_COND_V_MSG(memcmp(&guid.Data4[2], "PIDVID", 6), false, "DirectInput device not recognised."); + WORD type = BSWAP16(0x03); + WORD vendor = BSWAP16(LOWORD(guid.Data1)); + WORD product = BSWAP16(HIWORD(guid.Data1)); + WORD version = 0; + sprintf_s(uid, "%04x%04x%04x%04x%04x%04x%04x%04x", type, 0, vendor, 0, product, 0, version, 0); + + id_to_change = num; + slider_count = 0; joy->di_joy->SetDataFormat(&c_dfDIJoystick2); joy->di_joy->SetCooperativeLevel(*hWnd, DISCL_FOREGROUND); @@ -188,13 +189,11 @@ bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) { } void JoypadWindows::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_joy_id) { - if (ob->dwType & DIDFT_AXIS) { - HRESULT res; DIPROPRANGE prop_range; DIPROPDWORD dilong; - DWORD ofs; + LONG ofs; if (ob->guidType == GUID_XAxis) ofs = DIJOFS_X; else if (ob->guidType == GUID_YAxis) @@ -207,9 +206,14 @@ void JoypadWindows::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_ ofs = DIJOFS_RY; else if (ob->guidType == GUID_RzAxis) ofs = DIJOFS_RZ; - else if (ob->guidType == GUID_Slider) - ofs = DIJOFS_SLIDER(0); - else + else if (ob->guidType == GUID_Slider) { + if (slider_count < 2) { + ofs = DIJOFS_SLIDER(slider_count); + slider_count++; + } else { + return; + } + } else return; prop_range.diph.dwSize = sizeof(DIPROPRANGE); prop_range.diph.dwHeaderSize = sizeof(DIPROPHEADER); @@ -239,7 +243,6 @@ void JoypadWindows::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_ } BOOL CALLBACK JoypadWindows::enumCallback(const DIDEVICEINSTANCE *p_instance, void *p_context) { - JoypadWindows *self = (JoypadWindows *)p_context; if (self->is_xinput_device(&p_instance->guidProduct)) { return DIENUM_CONTINUE; @@ -249,7 +252,6 @@ BOOL CALLBACK JoypadWindows::enumCallback(const DIDEVICEINSTANCE *p_instance, vo } BOOL CALLBACK JoypadWindows::objectsCallback(const DIDEVICEOBJECTINSTANCE *instance, void *context) { - JoypadWindows *self = (JoypadWindows *)context; self->setup_joypad_object(instance, self->id_to_change); @@ -257,17 +259,15 @@ BOOL CALLBACK JoypadWindows::objectsCallback(const DIDEVICEOBJECTINSTANCE *insta } void JoypadWindows::close_joypad(int id) { - if (id == -1) { - for (int i = 0; i < JOYPADS_MAX; i++) { - close_joypad(i); } return; } - if (!d_joypads[id].attached) return; + if (!d_joypads[id].attached) + return; d_joypads[id].di_joy->Unacquire(); d_joypads[id].di_joy->Release(); @@ -279,18 +279,15 @@ void JoypadWindows::close_joypad(int id) { } void JoypadWindows::probe_joypads() { - + ERR_FAIL_NULL_MSG(dinput, "DirectInput not initialized. Rebooting your PC may solve this issue."); DWORD dwResult; for (DWORD i = 0; i < XUSER_MAX_COUNT; i++) { - ZeroMemory(&x_joypads[i].state, sizeof(XINPUT_STATE)); dwResult = xinput_get_state(i, &x_joypads[i].state); if (dwResult == ERROR_SUCCESS) { - int id = input->get_unused_joy_id(); if (id != -1 && !x_joypads[i].attached) { - x_joypads[i].attached = true; x_joypads[i].id = id; x_joypads[i].ff_timestamp = 0; @@ -300,7 +297,6 @@ void JoypadWindows::probe_joypads() { input->joy_connection_changed(id, true, "XInput Gamepad", "__XINPUT_DEVICE__"); } } else if (x_joypads[i].attached) { - x_joypads[i].attached = false; attached_joypads[x_joypads[i].id] = false; input->joy_connection_changed(x_joypads[i].id, false, ""); @@ -308,27 +304,22 @@ void JoypadWindows::probe_joypads() { } for (int i = 0; i < joypad_count; i++) { - d_joypads[i].confirmed = false; } dinput->EnumDevices(DI8DEVCLASS_GAMECTRL, enumCallback, this, DIEDFL_ATTACHEDONLY); for (int i = 0; i < joypad_count; i++) { - if (!d_joypads[i].confirmed) { - close_joypad(i); } } } void JoypadWindows::process_joypads() { - HRESULT hr; for (int i = 0; i < XUSER_MAX_COUNT; i++) { - xinput_gamepad &joy = x_joypads[i]; if (!joy.attached) { continue; @@ -337,20 +328,18 @@ void JoypadWindows::process_joypads() { xinput_get_state(i, &joy.state); if (joy.state.dwPacketNumber != joy.last_packet) { - int button_mask = XINPUT_GAMEPAD_DPAD_UP; for (int j = 0; j <= 16; j++) { - - input->joy_button(joy.id, j, joy.state.Gamepad.wButtons & button_mask); + input->joy_button(joy.id, (JoyButton)j, joy.state.Gamepad.wButtons & button_mask); button_mask = button_mask * 2; } - input->joy_axis(joy.id, JOY_AXIS_0, axis_correct(joy.state.Gamepad.sThumbLX, true)); - input->joy_axis(joy.id, JOY_AXIS_1, axis_correct(joy.state.Gamepad.sThumbLY, true, false, true)); - input->joy_axis(joy.id, JOY_AXIS_2, axis_correct(joy.state.Gamepad.sThumbRX, true)); - input->joy_axis(joy.id, JOY_AXIS_3, axis_correct(joy.state.Gamepad.sThumbRY, true, false, true)); - input->joy_axis(joy.id, JOY_AXIS_4, axis_correct(joy.state.Gamepad.bLeftTrigger, true, true)); - input->joy_axis(joy.id, JOY_AXIS_5, axis_correct(joy.state.Gamepad.bRightTrigger, true, true)); + input->joy_axis(joy.id, JOY_AXIS_LEFT_X, axis_correct(joy.state.Gamepad.sThumbLX, true)); + input->joy_axis(joy.id, JOY_AXIS_LEFT_Y, axis_correct(joy.state.Gamepad.sThumbLY, true, false, true)); + input->joy_axis(joy.id, JOY_AXIS_RIGHT_X, axis_correct(joy.state.Gamepad.sThumbRX, true)); + input->joy_axis(joy.id, JOY_AXIS_RIGHT_Y, axis_correct(joy.state.Gamepad.sThumbRY, true, false, true)); + input->joy_axis(joy.id, JOY_AXIS_TRIGGER_LEFT, axis_correct(joy.state.Gamepad.bLeftTrigger, true, true)); + input->joy_axis(joy.id, JOY_AXIS_TRIGGER_RIGHT, axis_correct(joy.state.Gamepad.bRightTrigger, true, true)); joy.last_packet = joy.state.dwPacketNumber; } uint64_t timestamp = input->get_joy_vibration_timestamp(joy.id); @@ -370,7 +359,6 @@ void JoypadWindows::process_joypads() { } for (int i = 0; i < JOYPADS_MAX; i++) { - dinput_gamepad *joy = &d_joypads[i]; if (!joy->attached) @@ -391,34 +379,28 @@ void JoypadWindows::process_joypads() { post_hat(joy->id, js.rgdwPOV[0]); for (int j = 0; j < 128; j++) { - if (js.rgbButtons[j] & 0x80) { - if (!joy->last_buttons[j]) { - - input->joy_button(joy->id, j, true); + input->joy_button(joy->id, (JoyButton)j, true); joy->last_buttons[j] = true; } } else { - if (joy->last_buttons[j]) { - - input->joy_button(joy->id, j, false); + input->joy_button(joy->id, (JoyButton)j, false); joy->last_buttons[j] = false; } } } // on mingw, these constants are not constants - int count = 6; - unsigned int axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ }; - int values[] = { js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz }; + int count = 8; + LONG axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ, (LONG)DIJOFS_SLIDER(0), (LONG)DIJOFS_SLIDER(1) }; + int values[] = { js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz, js.rglSlider[0], js.rglSlider[1] }; for (int j = 0; j < joy->joy_axis.size(); j++) { - for (int k = 0; k < count; k++) { if (joy->joy_axis[j] == axes[k]) { - input->joy_axis(joy->id, j, axis_correct(values[k])); + input->joy_axis(joy->id, (JoyAxis)j, axis_correct(values[k])); break; }; }; @@ -428,61 +410,50 @@ void JoypadWindows::process_joypads() { } void JoypadWindows::post_hat(int p_device, DWORD p_dpad) { - - int dpad_val = 0; + HatMask dpad_val = (HatMask)0; // Should be -1 when centered, but according to docs: // "Some drivers report the centered position of the POV indicator as 65,535. Determine whether the indicator is centered as follows: // 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 = (HatMask)HatMask::HAT_MASK_CENTER; } if (p_dpad == 0) { - - dpad_val = InputDefault::HAT_MASK_UP; + dpad_val = (HatMask)HatMask::HAT_MASK_UP; } else if (p_dpad == 4500) { - - dpad_val = (InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_RIGHT); + dpad_val = (HatMask)(HatMask::HAT_MASK_UP | HatMask::HAT_MASK_RIGHT); } else if (p_dpad == 9000) { - - dpad_val = InputDefault::HAT_MASK_RIGHT; + dpad_val = (HatMask)HatMask::HAT_MASK_RIGHT; } else if (p_dpad == 13500) { - - dpad_val = (InputDefault::HAT_MASK_RIGHT | InputDefault::HAT_MASK_DOWN); + dpad_val = (HatMask)(HatMask::HAT_MASK_RIGHT | HatMask::HAT_MASK_DOWN); } else if (p_dpad == 18000) { - - dpad_val = InputDefault::HAT_MASK_DOWN; + dpad_val = (HatMask)HatMask::HAT_MASK_DOWN; } else if (p_dpad == 22500) { - - dpad_val = (InputDefault::HAT_MASK_DOWN | InputDefault::HAT_MASK_LEFT); + dpad_val = (HatMask)(HatMask::HAT_MASK_DOWN | HatMask::HAT_MASK_LEFT); } else if (p_dpad == 27000) { - - dpad_val = InputDefault::HAT_MASK_LEFT; + dpad_val = (HatMask)HatMask::HAT_MASK_LEFT; } else if (p_dpad == 31500) { - - dpad_val = (InputDefault::HAT_MASK_LEFT | InputDefault::HAT_MASK_UP); + dpad_val = (HatMask)(HatMask::HAT_MASK_LEFT | HatMask::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 { - - InputDefault::JoyAxis jx; +Input::JoyAxisValue JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const { + Input::JoyAxisValue jx; if (Math::abs(p_val) < MIN_JOY_AXIS) { jx.min = p_trigger ? 0 : -1; jx.value = 0.0f; return jx; } if (p_xinput) { - if (p_trigger) { jx.min = 0; jx.value = (float)p_val / MAX_TRIGGER; @@ -532,7 +503,6 @@ void JoypadWindows::joypad_vibration_stop_xinput(int p_device, uint64_t p_timest } void JoypadWindows::load_xinput() { - xinput_get_state = &_xinput_get_state; xinput_set_state = &_xinput_set_state; xinput_dll = LoadLibrary("XInput1_4.dll"); @@ -559,9 +529,7 @@ void JoypadWindows::load_xinput() { } void JoypadWindows::unload_xinput() { - if (xinput_dll) { - FreeLibrary((HMODULE)xinput_dll); } } diff --git a/platform/windows/joypad_windows.h b/platform/windows/joypad_windows.h index ab85bc60ac..757fb54fb3 100644 --- a/platform/windows/joypad_windows.h +++ b/platform/windows/joypad_windows.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -39,9 +39,9 @@ #ifndef SAFE_RELEASE // when Windows Media Device M? is not present #define SAFE_RELEASE(x) \ - if (x != NULL) { \ + if (x != nullptr) { \ x->Release(); \ - x = NULL; \ + x = nullptr; \ } #endif @@ -52,7 +52,7 @@ class JoypadWindows { public: JoypadWindows(); - JoypadWindows(InputDefault *_input, HWND *hwnd); + JoypadWindows(HWND *hwnd); ~JoypadWindows(); void probe_joypads(); @@ -70,7 +70,6 @@ private: }; struct dinput_gamepad { - int id; bool attached; bool confirmed; @@ -78,7 +77,7 @@ private: DWORD last_pad; LPDIRECTINPUTDEVICE8 di_joy; - List<DWORD> joy_axis; + List<LONG> joy_axis; GUID guid; dinput_gamepad() { @@ -93,22 +92,13 @@ private: }; struct xinput_gamepad { - - int id; - bool attached; - bool vibrating; - DWORD last_packet; + int id = 0; + bool attached = false; + bool vibrating = false; + DWORD last_packet = 0; XINPUT_STATE state; - uint64_t ff_timestamp; - uint64_t ff_end_timestamp; - - xinput_gamepad() { - attached = false; - vibrating = false; - ff_timestamp = 0; - ff_end_timestamp = 0; - last_packet = 0; - } + uint64_t ff_timestamp = 0; + uint64_t ff_end_timestamp = 0; }; typedef DWORD(WINAPI *XInputGetState_t)(DWORD dwUserIndex, XINPUT_STATE *pState); @@ -117,9 +107,10 @@ private: HWND *hWnd; HANDLE xinput_dll; LPDIRECTINPUT8 dinput; - InputDefault *input; + Input *input; int id_to_change; + int slider_count; int joypad_count; bool attached_joypads[JOYPADS_MAX]; dinput_gamepad d_joypads[JOYPADS_MAX]; @@ -141,7 +132,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; + Input::JoyAxisValue 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/key_mapping_windows.cpp b/platform/windows/key_mapping_windows.cpp index c76b31ca9c..c367c69826 100644 --- a/platform/windows/key_mapping_windows.cpp +++ b/platform/windows/key_mapping_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,13 +33,11 @@ #include <stdio.h> struct _WinTranslatePair { - unsigned int keysym; unsigned int keycode; }; static _WinTranslatePair _vk_to_keycode[] = { - { KEY_BACKSPACE, VK_BACK }, // (0x08) // backspace { KEY_TAB, VK_TAB }, //(0x09) @@ -49,7 +47,7 @@ static _WinTranslatePair _vk_to_keycode[] = { { KEY_SHIFT, VK_SHIFT }, //(0x10) - { KEY_CONTROL, VK_CONTROL }, //(0x11) + { KEY_CTRL, VK_CONTROL }, //(0x11) { KEY_ALT, VK_MENU }, //(0x12) @@ -130,7 +128,7 @@ static _WinTranslatePair _vk_to_keycode[] = { { KEY_MASK_META, VK_LWIN }, //(0x5B) { KEY_MASK_META, VK_RWIN }, //(0x5C) - //VK_APPS (0x5D) + { KEY_MENU, VK_APPS }, //(0x5D) { KEY_STANDBY, VK_SLEEP }, //(0x5F) { KEY_KP_0, VK_NUMPAD0 }, //(0x60) { KEY_KP_1, VK_NUMPAD1 }, //(0x61) @@ -168,8 +166,8 @@ static _WinTranslatePair _vk_to_keycode[] = { { KEY_SCROLLLOCK, VK_SCROLL }, // (0x91) { KEY_SHIFT, VK_LSHIFT }, // (0xA0) { KEY_SHIFT, VK_RSHIFT }, // (0xA1) - { KEY_CONTROL, VK_LCONTROL }, // (0xA2) - { KEY_CONTROL, VK_RCONTROL }, // (0xA3) + { KEY_CTRL, VK_LCONTROL }, // (0xA2) + { KEY_CTRL, VK_RCONTROL }, // (0xA3) { KEY_MENU, VK_LMENU }, // (0xA4) { KEY_MENU, VK_RMENU }, // (0xA5) @@ -238,10 +236,105 @@ VK_PA1 (0xFD) VK_OEM_CLEAR (0xFE) */ -unsigned int KeyMappingWindows::get_keysym(unsigned int p_code) { +static _WinTranslatePair _scancode_to_keycode[] = { + { KEY_ESCAPE, 0x01 }, + { KEY_1, 0x02 }, + { KEY_2, 0x03 }, + { KEY_3, 0x04 }, + { KEY_4, 0x05 }, + { KEY_5, 0x06 }, + { KEY_6, 0x07 }, + { KEY_7, 0x08 }, + { KEY_8, 0x09 }, + { KEY_9, 0x0A }, + { KEY_0, 0x0B }, + { KEY_MINUS, 0x0C }, + { KEY_EQUAL, 0x0D }, + { KEY_BACKSPACE, 0x0E }, + { KEY_TAB, 0x0F }, + { KEY_Q, 0x10 }, + { KEY_W, 0x11 }, + { KEY_E, 0x12 }, + { KEY_R, 0x13 }, + { KEY_T, 0x14 }, + { KEY_Y, 0x15 }, + { KEY_U, 0x16 }, + { KEY_I, 0x17 }, + { KEY_O, 0x18 }, + { KEY_P, 0x19 }, + { KEY_BRACELEFT, 0x1A }, + { KEY_BRACERIGHT, 0x1B }, + { KEY_ENTER, 0x1C }, + { KEY_CTRL, 0x1D }, + { KEY_A, 0x1E }, + { KEY_S, 0x1F }, + { KEY_D, 0x20 }, + { KEY_F, 0x21 }, + { KEY_G, 0x22 }, + { KEY_H, 0x23 }, + { KEY_J, 0x24 }, + { KEY_K, 0x25 }, + { KEY_L, 0x26 }, + { KEY_SEMICOLON, 0x27 }, + { KEY_APOSTROPHE, 0x28 }, + { KEY_QUOTELEFT, 0x29 }, + { KEY_SHIFT, 0x2A }, + { KEY_BACKSLASH, 0x2B }, + { KEY_Z, 0x2C }, + { KEY_X, 0x2D }, + { KEY_C, 0x2E }, + { KEY_V, 0x2F }, + { KEY_B, 0x30 }, + { KEY_N, 0x31 }, + { KEY_M, 0x32 }, + { KEY_COMMA, 0x33 }, + { KEY_PERIOD, 0x34 }, + { KEY_SLASH, 0x35 }, + { KEY_SHIFT, 0x36 }, + { KEY_PRINT, 0x37 }, + { KEY_ALT, 0x38 }, + { KEY_SPACE, 0x39 }, + { KEY_CAPSLOCK, 0x3A }, + { KEY_F1, 0x3B }, + { KEY_F2, 0x3C }, + { KEY_F3, 0x3D }, + { KEY_F4, 0x3E }, + { KEY_F5, 0x3F }, + { KEY_F6, 0x40 }, + { KEY_F7, 0x41 }, + { KEY_F8, 0x42 }, + { KEY_F9, 0x43 }, + { KEY_F10, 0x44 }, + { KEY_NUMLOCK, 0x45 }, + { KEY_SCROLLLOCK, 0x46 }, + { KEY_HOME, 0x47 }, + { KEY_UP, 0x48 }, + { KEY_PAGEUP, 0x49 }, + { KEY_KP_SUBTRACT, 0x4A }, + { KEY_LEFT, 0x4B }, + { KEY_KP_5, 0x4C }, + { KEY_RIGHT, 0x4D }, + { KEY_KP_ADD, 0x4E }, + { KEY_END, 0x4F }, + { KEY_DOWN, 0x50 }, + { KEY_PAGEDOWN, 0x51 }, + { KEY_INSERT, 0x52 }, + { KEY_DELETE, 0x53 }, + //{ KEY_???, 0x56 }, //NON US BACKSLASH + { KEY_F11, 0x57 }, + { KEY_F12, 0x58 }, + { KEY_META, 0x5B }, + { KEY_META, 0x5C }, + { KEY_MENU, 0x5D }, + { KEY_F13, 0x64 }, + { KEY_F14, 0x65 }, + { KEY_F15, 0x66 }, + { KEY_F16, 0x67 }, + { KEY_UNKNOWN, 0 } +}; +unsigned int KeyMappingWindows::get_keysym(unsigned int p_code) { for (int i = 0; _vk_to_keycode[i].keysym != KEY_UNKNOWN; i++) { - if (_vk_to_keycode[i].keycode == p_code) { //printf("outcode: %x\n",_vk_to_keycode[i].keysym); @@ -251,3 +344,81 @@ unsigned int KeyMappingWindows::get_keysym(unsigned int p_code) { return KEY_UNKNOWN; } + +unsigned int KeyMappingWindows::get_scansym(unsigned int p_code, bool p_extended) { + unsigned int keycode = KEY_UNKNOWN; + for (int i = 0; _scancode_to_keycode[i].keysym != KEY_UNKNOWN; i++) { + if (_scancode_to_keycode[i].keycode == p_code) { + keycode = _scancode_to_keycode[i].keysym; + break; + } + } + + if (p_extended) { + switch (keycode) { + case KEY_ENTER: { + keycode = KEY_KP_ENTER; + } break; + case KEY_SLASH: { + keycode = KEY_KP_DIVIDE; + } break; + case KEY_CAPSLOCK: { + keycode = KEY_KP_ADD; + } break; + } + } else { + switch (keycode) { + case KEY_NUMLOCK: { + keycode = KEY_PAUSE; + } break; + case KEY_HOME: { + keycode = KEY_KP_7; + } break; + case KEY_UP: { + keycode = KEY_KP_8; + } break; + case KEY_PAGEUP: { + keycode = KEY_KP_9; + } break; + case KEY_LEFT: { + keycode = KEY_KP_4; + } break; + case KEY_RIGHT: { + keycode = KEY_KP_6; + } break; + case KEY_END: { + keycode = KEY_KP_1; + } break; + case KEY_DOWN: { + keycode = KEY_KP_2; + } break; + case KEY_PAGEDOWN: { + keycode = KEY_KP_3; + } break; + case KEY_INSERT: { + keycode = KEY_KP_0; + } break; + case KEY_DELETE: { + keycode = KEY_KP_PERIOD; + } break; + case KEY_PRINT: { + keycode = KEY_KP_MULTIPLY; + } break; + } + } + + return keycode; +} + +bool KeyMappingWindows::is_extended_key(unsigned int p_code) { + return p_code == VK_INSERT || + p_code == VK_DELETE || + p_code == VK_HOME || + p_code == VK_END || + p_code == VK_PRIOR || + p_code == VK_NEXT || + p_code == VK_LEFT || + p_code == VK_UP || + p_code == VK_RIGHT || + p_code == VK_DOWN; +} diff --git a/platform/windows/key_mapping_windows.h b/platform/windows/key_mapping_windows.h index 0f9bdecde1..fb07227014 100644 --- a/platform/windows/key_mapping_windows.h +++ b/platform/windows/key_mapping_windows.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -38,11 +38,12 @@ #include <winuser.h> class KeyMappingWindows { - - KeyMappingWindows(){}; + KeyMappingWindows() {} public: static unsigned int get_keysym(unsigned int p_code); + static unsigned int get_scansym(unsigned int p_code, bool p_extended); + static bool is_extended_key(unsigned int p_code); }; #endif // KEY_MAPPING_WINDOWS_H diff --git a/platform/windows/lang_table.h b/platform/windows/lang_table.h index f81bab13a4..51583cc11e 100644 --- a/platform/windows/lang_table.h +++ b/platform/windows/lang_table.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index a6977a7a86..56d673afc3 100755..100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -33,22 +33,18 @@ #include "os_windows.h" +#include "core/debugger/engine_debugger.h" +#include "core/debugger/script_debugger.h" #include "core/io/marshalls.h" #include "core/version_generated.gen.h" -#include "drivers/gles2/rasterizer_gles2.h" -#include "drivers/gles3/rasterizer_gles3.h" #include "drivers/windows/dir_access_windows.h" #include "drivers/windows/file_access_windows.h" -#include "drivers/windows/mutex_windows.h" -#include "drivers/windows/rw_lock_windows.h" -#include "drivers/windows/semaphore_windows.h" -#include "drivers/windows/thread_windows.h" #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" +#include "servers/rendering/rendering_server_default.h" #include "windows_terminal_logger.h" #include <avrt.h> @@ -79,38 +75,13 @@ __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) { - - LPWSTR messageBuffer = NULL; + LPWSTR messageBuffer = nullptr; 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); + nullptr, id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&messageBuffer, 0, nullptr); - String msg = "Error " + itos(id) + ": " + String(messageBuffer, size); + String msg = "Error " + itos(id) + ": " + String::utf16((const char16_t *)messageBuffer, size); LocalFree(messageBuffer); @@ -118,10 +89,7 @@ static String format_error_message(DWORD id) { } #endif // DEBUG_ENABLED -extern HINSTANCE godot_hinstance; - void RedirectIOToConsole() { - int hConHandle; intptr_t lStdHandle; @@ -136,15 +104,11 @@ void RedirectIOToConsole() { // set the screen buffer to be big enough to let us scroll text - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), - - &coninfo); + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); coninfo.dwSize.Y = MAX_CONSOLE_LINES; - SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), - - coninfo.dwSize); + SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); // redirect unbuffered STDOUT to the console @@ -156,7 +120,7 @@ void RedirectIOToConsole() { *stdout = *fp; - setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stdout, nullptr, _IONBF, 0); // redirect unbuffered STDIN to the console @@ -168,7 +132,7 @@ void RedirectIOToConsole() { *stdin = *fp; - setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdin, nullptr, _IONBF, 0); // redirect unbuffered STDERR to the console @@ -180,7 +144,7 @@ void RedirectIOToConsole() { *stderr = *fp; - setvbuf(stderr, NULL, _IONBF, 0); + setvbuf(stderr, nullptr, _IONBF, 0); // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog @@ -188,47 +152,31 @@ void RedirectIOToConsole() { } BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) { - if (ScriptDebugger::get_singleton() == NULL) + if (!EngineDebugger::is_active()) return FALSE; switch (dwCtrlType) { case CTRL_C_EVENT: - ScriptDebugger::get_singleton()->set_depth(-1); - ScriptDebugger::get_singleton()->set_lines_left(1); + EngineDebugger::get_script_debugger()->set_depth(-1); + EngineDebugger::get_script_debugger()->set_lines_left(1); return TRUE; default: return FALSE; } } -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(); - SemaphoreWindows::make_default(); - MutexWindows::make_default(); - RWLockWindows::make_default(); FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES); FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA); FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM); - //FileAccessBufferedFA<FileAccessWindows>::make_default(); DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_RESOURCES); DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_USERDATA); DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_FILESYSTEM); @@ -249,1401 +197,28 @@ 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 (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_scancode(KeyMappingWindows::get_keysym(ke.wParam)); - 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_scancode(KEY_KP_ENTER); - } else { - k->set_scancode(KeyMappingWindows::get_keysym(ke.wParam)); - } - - 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); - } + // Add current Godot PID to the list of known PIDs + ProcessInfo current_pi = {}; + PROCESS_INFORMATION current_pi_pi = {}; + current_pi.pi = current_pi_pi; + current_pi.pi.hProcess = GetCurrentProcess(); + process_map->insert(GetCurrentProcessId(), current_pi); - 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; + IPUnix::make_default(); + main_loop = nullptr; } -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(¤t, 0, sizeof(current)); - EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, ¤t); - - 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); - } - -#if defined(OPENGL_ENABLED) - - bool gles3_context = true; - if (p_video_driver == VIDEO_DRIVER_GLES2) { - gles3_context = false; - } - - bool editor = Engine::get_singleton()->is_editor_hint(); - bool gl_initialization_error = false; - - gl_context = NULL; - while (!gl_context) { - gl_context = memnew(ContextGL_Windows(hWnd, gles3_context)); - - if (gl_context->initialize() != OK) { - memdelete(gl_context); - gl_context = NULL; - - if (GLOBAL_GET("rendering/quality/driver/fallback_to_gles2") || editor) { - if (p_video_driver == VIDEO_DRIVER_GLES2) { - gl_initialization_error = true; - break; - } - - p_video_driver = VIDEO_DRIVER_GLES2; - gles3_context = false; - } else { - gl_initialization_error = true; - break; - } - } - } - - while (true) { - if (gles3_context) { - if (RasterizerGLES3::is_viable() == OK) { - RasterizerGLES3::register_config(); - RasterizerGLES3::make_current(); - break; - } else { - if (GLOBAL_GET("rendering/quality/driver/fallback_to_gles2") || editor) { - p_video_driver = VIDEO_DRIVER_GLES2; - gles3_context = false; - continue; - } else { - gl_initialization_error = true; - break; - } - } - } else { - if (RasterizerGLES2::is_viable() == OK) { - RasterizerGLES2::register_config(); - RasterizerGLES2::make_current(); - break; - } else { - gl_initialization_error = true; - break; - } - } - } - - if (gl_initialization_error) { - OS::get_singleton()->alert("Your video card driver does not support any of the supported OpenGL versions.\n" - "Please update your drivers or if you have a very old or integrated GPU upgrade it.", - "Unable to initialize Video driver"); - return ERR_UNAVAILABLE; - } - - video_driver_index = p_video_driver; - - gl_context->set_use_vsync(video_mode.use_vsync); - set_vsync_via_compositor(video_mode.vsync_via_compositor); -#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)); - - power_manager = memnew(PowerWindows); - - 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) memdelete(main_loop); - main_loop = NULL; + main_loop = nullptr; } void OS_Windows::set_main_loop(MainLoop *p_main_loop) { - - input->set_main_loop(p_main_loop); main_loop = p_main_loop; } void OS_Windows::finalize() { - #ifdef WINMIDI_ENABLED driver_midi.close(); #endif @@ -1651,601 +226,18 @@ void OS_Windows::finalize() { if (main_loop) memdelete(main_loop); - main_loop = NULL; - - memdelete(joypad); - memdelete(input); - touch_state.clear(); - - cursors_cache.clear(); - visual_server->finish(); - memdelete(visual_server); -#ifdef OPENGL_ENABLED - if (gl_context) - memdelete(gl_context); -#endif - - if (user_proc) { - SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)user_proc); - }; + main_loop = nullptr; } void OS_Windows::finalize_core() { - timeEndPeriod(1); memdelete(process_map); 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 (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; + String path = p_path.replace("/", "\\"); if (!FileAccess::exists(path)) { //this code exists so gdnative can load .dll files from within the executable path @@ -2258,14 +250,14 @@ Error OS_Windows::open_dynamic_library(const String p_path, void *&p_library_han PAddDllDirectory add_dll_directory = (PAddDllDirectory)GetProcAddress(GetModuleHandle("kernel32.dll"), "AddDllDirectory"); PRemoveDllDirectory remove_dll_directory = (PRemoveDllDirectory)GetProcAddress(GetModuleHandle("kernel32.dll"), "RemoveDllDirectory"); - bool has_dll_directory_api = ((add_dll_directory != NULL) && (remove_dll_directory != NULL)); - DLL_DIRECTORY_COOKIE cookie = NULL; + bool has_dll_directory_api = ((add_dll_directory != nullptr) && (remove_dll_directory != nullptr)); + DLL_DIRECTORY_COOKIE cookie = nullptr; if (p_also_set_library_path && has_dll_directory_api) { - cookie = add_dll_directory(path.get_base_dir().c_str()); + cookie = add_dll_directory((LPCWSTR)(path.get_base_dir().utf16().get_data())); } - p_library_handle = (void *)LoadLibraryExW(path.c_str(), NULL, (p_also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0); + p_library_handle = (void *)LoadLibraryExW((LPCWSTR)(path.utf16().get_data()), nullptr, (p_also_set_library_path && has_dll_directory_api) ? LOAD_LIBRARY_SEARCH_DEFAULT_DIRS : 0); ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + format_error_message(GetLastError()) + "."); if (cookie) { @@ -2294,24 +286,11 @@ 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"; } OS::Date OS_Windows::get_date(bool utc) const { - SYSTEMTIME systemtime; if (utc) GetSystemTime(&systemtime); @@ -2326,8 +305,8 @@ OS::Date OS_Windows::get_date(bool utc) const { date.dst = false; return date; } -OS::Time OS_Windows::get_time(bool utc) const { +OS::Time OS_Windows::get_time(bool utc) const { SYSTEMTIME systemtime; if (utc) GetSystemTime(&systemtime); @@ -2336,8 +315,8 @@ OS::Time OS_Windows::get_time(bool utc) const { Time time; time.hour = systemtime.wHour; - time.min = systemtime.wMinute; - time.sec = systemtime.wSecond; + time.minute = systemtime.wMinute; + time.second = systemtime.wSecond; return time; } @@ -2355,385 +334,138 @@ OS::TimeZoneInfo OS_Windows::get_time_zone_info() const { } // Bias value returned by GetTimeZoneInformation is inverted of what we expect - // For example on GMT-3 GetTimeZoneInformation return a Bias of 180, so invert the value to get -180 + // For example, on GMT-3 GetTimeZoneInformation return a Bias of 180, so invert the value to get -180 ret.bias = -info.Bias; return ret; } -uint64_t OS_Windows::get_unix_time() const { - - FILETIME ft; - SYSTEMTIME st; - GetSystemTime(&st); - SystemTimeToFileTime(&st, &ft); - - SYSTEMTIME ep; - ep.wYear = 1970; - ep.wMonth = 1; - ep.wDayOfWeek = 4; - ep.wDay = 1; - ep.wHour = 0; - ep.wMinute = 0; - ep.wSecond = 0; - ep.wMilliseconds = 0; - FILETIME fep; - SystemTimeToFileTime(&ep, &fep); - - // Type punning through unions (rather than pointer cast) as per: - // https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime#remarks - ULARGE_INTEGER ft_punning; - ft_punning.LowPart = ft.dwLowDateTime; - ft_punning.HighPart = ft.dwHighDateTime; - - ULARGE_INTEGER fep_punning; - fep_punning.LowPart = fep.dwLowDateTime; - fep_punning.HighPart = fep.dwHighDateTime; - - return (ft_punning.QuadPart - fep_punning.QuadPart) / 10000000; -}; - -uint64_t OS_Windows::get_system_time_secs() const { - - return get_system_time_msecs() / 1000; -} - -uint64_t OS_Windows::get_system_time_msecs() const { - - const uint64_t WINDOWS_TICK = 10000; - const uint64_t MSEC_TO_UNIX_EPOCH = 11644473600000LL; +double OS_Windows::get_unix_time() const { + // 1 Windows tick is 100ns + const uint64_t WINDOWS_TICKS_PER_SECOND = 10000000; + const uint64_t TICKS_TO_UNIX_EPOCH = 116444736000000000LL; SYSTEMTIME st; GetSystemTime(&st); FILETIME ft; SystemTimeToFileTime(&st, &ft); - uint64_t ret; - ret = ft.dwHighDateTime; - ret <<= 32; - ret |= ft.dwLowDateTime; + uint64_t ticks_time; + ticks_time = ft.dwHighDateTime; + ticks_time <<= 32; + ticks_time |= ft.dwLowDateTime; - return (uint64_t)(ret / WINDOWS_TICK - MSEC_TO_UNIX_EPOCH); + return (double)(ticks_time - TICKS_TO_UNIX_EPOCH) / WINDOWS_TICKS_PER_SECOND; } void OS_Windows::delay_usec(uint32_t p_usec) const { - if (p_usec < 1000) Sleep(1); else Sleep(p_usec / 1000); } -uint64_t OS_Windows::get_ticks_usec() const { +uint64_t OS_Windows::get_ticks_usec() const { uint64_t ticks; - uint64_t time; + // This is the number of clock ticks since start if (!QueryPerformanceCounter((LARGE_INTEGER *)&ticks)) ticks = (UINT64)timeGetTime(); - // Divide by frequency to get the time in seconds - time = ticks * 1000000L / ticks_per_second; - // Subtract the time at game start to get - // the time since the game started - time -= ticks_start; - 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) { + // Divide by frequency to get the time in seconds + // original calculation shown below is subject to overflow + // with high ticks_per_second and a number of days since the last reboot. + // time = ticks * 1000000L / ticks_per_second; - ERR_FAIL_INDEX(p_shape, CURSOR_MAX); + // we can prevent this by either using 128 bit math + // or separating into a calculation for seconds, and the fraction + uint64_t seconds = ticks / ticks_per_second; - if (cursor_shape == p_shape) - return; + // compiler will optimize these two into one divide + uint64_t leftover = ticks % ticks_per_second; - if (mouse_mode != MOUSE_MODE_VISIBLE && mouse_mode != MOUSE_MODE_CONFINED) { - cursor_shape = p_shape; - return; - } + // remainder + uint64_t time = (leftover * 1000000L) / ticks_per_second; - 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 - }; + // seconds + time += seconds * 1000000L; - 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; + // Subtract the time at game start to get + // the time since the game started + time -= ticks_start; + return time; } -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<Texture> 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); - - image->lock(); - 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(); - } - image->unlock(); - - // 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]); - } +String OS_Windows::_quote_command_line_argument(const String &p_text) const { + for (int i = 0; i < p_text.size(); i++) { + char32_t c = p_text[i]; + if (c == ' ' || c == '&' || c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}' || c == '^' || c == '=' || c == ';' || c == '!' || c == '\'' || c == '+' || c == ',' || c == '`' || c == '~') { + return "\"" + p_text + "\""; } - - 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); } + return p_text; } -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) { - - String argss; - argss = "\"\"" + p_path + "\""; - - for (const List<String>::Element *E = p_arguments.front(); E; E = E->next()) { - - argss += " \"" + E->get() + "\""; - } - - argss += "\""; +Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex) { + String path = p_path.replace("/", "\\"); + String command = _quote_command_line_argument(path); + for (const List<String>::Element *E = p_arguments.front(); E; E = E->next()) { + command += " " + _quote_command_line_argument(E->get()); + } + if (r_pipe) { if (read_stderr) { - argss += " 2>&1"; // Read stderr too + command += " 2>&1"; // Include stderr } + // Add extra quotes around the full command, to prevent it from stripping quotes in the command, + // because _wpopen calls command as "cmd.exe /c command", instead of executing it directly + command = _quote_command_line_argument(command); - FILE *f = _wpopen(argss.c_str(), L"r"); - - ERR_FAIL_COND_V(!f, ERR_CANT_OPEN); - + FILE *f = _wpopen((LPCWSTR)(command.utf16().get_data()), L"r"); + ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot create pipe from command: " + command); char buf[65535]; while (fgets(buf, 65535, f)) { - if (p_pipe_mutex) { p_pipe_mutex->lock(); } - (*r_pipe) += buf; + (*r_pipe) += String::utf8(buf); if (p_pipe_mutex) { p_pipe_mutex->unlock(); } } - int rv = _pclose(f); - if (r_exitcode) - *r_exitcode = rv; + if (r_exitcode) { + *r_exitcode = rv; + } return OK; } - String cmdline = "\"" + p_path + "\""; - const List<String>::Element *I = p_arguments.front(); - while (I) { + ProcessInfo pi; + ZeroMemory(&pi.si, sizeof(pi.si)); + pi.si.cb = sizeof(pi.si); + ZeroMemory(&pi.pi, sizeof(pi.pi)); + LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si; + + int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, NORMAL_PRIORITY_CLASS & CREATE_NO_WINDOW, nullptr, nullptr, si_w, &pi.pi); + ERR_FAIL_COND_V_MSG(ret == 0, ERR_CANT_FORK, "Could not create child process: " + command); - cmdline += " \"" + I->get() + "\""; + WaitForSingleObject(pi.pi.hProcess, INFINITE); + if (r_exitcode) { + DWORD ret2; + GetExitCodeProcess(pi.pi.hProcess, &ret2); + *r_exitcode = ret2; + } + CloseHandle(pi.pi.hProcess); + CloseHandle(pi.pi.hThread); - I = I->next(); - }; + return OK; +}; + +Error OS_Windows::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id) { + String path = p_path.replace("/", "\\"); + String command = _quote_command_line_argument(path); + for (const List<String>::Element *E = p_arguments.front(); E; E = E->next()) { + command += " " + _quote_command_line_argument(E->get()); + } ProcessInfo pi; ZeroMemory(&pi.si, sizeof(pi.si)); @@ -2741,34 +473,19 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, ZeroMemory(&pi.pi, sizeof(pi.pi)); LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si; - Vector<CharType> modstr; //windows wants to change this no idea why - modstr.resize(cmdline.size()); - for (int i = 0; i < cmdline.size(); i++) - modstr.write[i] = cmdline[i]; - int ret = CreateProcessW(NULL, modstr.ptrw(), NULL, NULL, 0, NORMAL_PRIORITY_CLASS & CREATE_NO_WINDOW, NULL, NULL, si_w, &pi.pi); - ERR_FAIL_COND_V(ret == 0, ERR_CANT_FORK); - - if (p_blocking) { - - DWORD ret2 = WaitForSingleObject(pi.pi.hProcess, INFINITE); - if (r_exitcode) - *r_exitcode = ret2; + int ret = CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, false, NORMAL_PRIORITY_CLASS & CREATE_NO_WINDOW, nullptr, nullptr, si_w, &pi.pi); + ERR_FAIL_COND_V_MSG(ret == 0, ERR_CANT_FORK, "Could not create child process: " + command); - CloseHandle(pi.pi.hProcess); - CloseHandle(pi.pi.hThread); - } else { + ProcessID pid = pi.pi.dwProcessId; + if (r_child_id) { + *r_child_id = pid; + } + process_map->insert(pid, pi); - ProcessID pid = pi.pi.dwProcessId; - if (r_child_id) { - *r_child_id = pid; - }; - process_map->insert(pid, pi); - }; return OK; }; Error OS_Windows::kill(const ProcessID &p_pid) { - ERR_FAIL_COND_V(!process_map->has(p_pid), FAILED); const PROCESS_INFORMATION pi = (*process_map)[p_pid].pi; @@ -2787,197 +504,46 @@ int OS_Windows::get_process_id() const { } Error OS_Windows::set_cwd(const String &p_cwd) { - - if (_wchdir(p_cwd.c_str()) != 0) + if (_wchdir((LPCWSTR)(p_cwd.utf16().get_data())) != 0) return ERR_CANT_OPEN; return OK; } String OS_Windows::get_executable_path() const { - - wchar_t bufname[4096]; - GetModuleFileNameW(NULL, bufname, 4096); - String s = bufname; + WCHAR bufname[4096]; + GetModuleFileNameW(nullptr, bufname, 4096); + String s = String::utf16((const char16_t *)bufname).replace("\\", "/"); 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_PRINTS("No small icon found, reusing " + itos(big_icon_width) + "x" + itos(big_icon_width) + " @" + itos(big_icon_cc) + " icon!"); - small_icon_index = big_icon_index; - small_icon_cc = big_icon_cc; - } - - // Read the big icon - DWORD bytecount_big = icon_dir->idEntries[big_icon_index].dwBytesInRes; - Vector<uint8_t> data_big; - data_big.resize(bytecount_big); - pos = icon_dir->idEntries[big_icon_index].dwImageOffset; - f->seek(pos); - f->get_buffer((uint8_t *)&data_big.write[0], bytecount_big); - HICON icon_big = CreateIconFromResource((PBYTE)&data_big.write[0], bytecount_big, TRUE, 0x00030000); - 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]; - PoolVector<uint8_t>::Read r = icon->get_data().read(); - - 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 - return _wgetenv(p_var.c_str()) != NULL; + return _wgetenv((LPCWSTR)(p_var.utf16().get_data())) != nullptr; #else - wchar_t *env; + WCHAR *env; size_t len; - _wdupenv_s(&env, &len, p_var.c_str()); - const bool has_env = env != NULL; + _wdupenv_s(&env, &len, (LPCWSTR)(p_var.utf16().get_data())); + const bool has_env = env != nullptr; free(env); return has_env; #endif }; String OS_Windows::get_environment(const String &p_var) const { - - wchar_t wval[0x7Fff]; // MSDN says 32767 char is the maximum - int wlen = GetEnvironmentVariableW(p_var.c_str(), wval, 0x7Fff); + WCHAR wval[0x7fff]; // MSDN says 32767 char is the maximum + int wlen = GetEnvironmentVariableW((LPCWSTR)(p_var.utf16().get_data()), wval, 0x7fff); if (wlen > 0) { - return wval; + return String::utf16((const char16_t *)wval); } return ""; } bool OS_Windows::set_environment(const String &p_var, const String &p_value) const { - - return (bool)SetEnvironmentVariableW(p_var.c_str(), p_value.c_str()); + return (bool)SetEnvironmentVariableW((LPCWSTR)(p_var.utf16().get_data()), (LPCWSTR)(p_value.utf16().get_data())); } String OS_Windows::get_stdin_string(bool p_block) { - if (p_block) { char buff[1024]; return fgets(buff, 1024, stdin); @@ -2986,44 +552,31 @@ 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); + ShellExecuteW(nullptr, nullptr, (LPCWSTR)(p_uri.utf16().get_data()), nullptr, nullptr, SW_SHOWNORMAL); return OK; } String OS_Windows::get_locale() const { - const _WinLocale *wl = &_win_locales[0]; LANGID langid = GetUserDefaultUILanguage(); String neutral; - int lang = langid & ((1 << 9) - 1); - int sublang = langid & ~((1 << 9) - 1); + int lang = PRIMARYLANGID(langid); + int sublang = SUBLANGID(langid); while (wl->locale) { - if (wl->main_lang == lang && wl->sublang == SUBLANG_NEUTRAL) neutral = wl->locale; if (lang == wl->main_lang && sublang == wl->sublang) - return wl->locale; + return String(wl->locale).replace("-", "_"); wl++; } if (neutral != "") - return neutral; + return String(neutral).replace("-", "_"); return "en"; } @@ -3058,148 +611,73 @@ 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() { - - gl_context->release_current(); -} - -void OS_Windows::make_rendering_thread() { - - gl_context->make_current(); -} - -void OS_Windows::swap_buffers() { - - gl_context->swap_buffers(); -} - -void OS_Windows::force_process_input() { - process_events(); // get rid of pending events -} - void OS_Windows::run() { - if (!main_loop) return; - main_loop->init(); + main_loop->initialize(); while (!force_quit) { - - process_events(); // get rid of pending events + DisplayServer::get_singleton()->process_events(); // get rid of pending events if (Main::iteration()) break; }; - main_loop->finish(); + main_loop->finalize(); } MainLoop *OS_Windows::get_main_loop() const { - return main_loop; } String OS_Windows::get_config_path() const { - - if (has_environment("XDG_CONFIG_HOME")) { // unlikely, but after all why not? - return get_environment("XDG_CONFIG_HOME"); - } else if (has_environment("APPDATA")) { - return get_environment("APPDATA"); - } else { - return "."; + // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well. + if (has_environment("XDG_CONFIG_HOME")) { + if (get_environment("XDG_CONFIG_HOME").is_absolute_path()) { + return get_environment("XDG_CONFIG_HOME").replace("\\", "/"); + } else { + WARN_PRINT_ONCE("`XDG_CONFIG_HOME` is a relative path. Ignoring its value and falling back to `%APPDATA%` or `.` per the XDG Base Directory specification."); + } + } + if (has_environment("APPDATA")) { + return get_environment("APPDATA").replace("\\", "/"); } + return "."; } String OS_Windows::get_data_path() const { - + // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well. if (has_environment("XDG_DATA_HOME")) { - return get_environment("XDG_DATA_HOME"); - } else { - return get_config_path(); + if (get_environment("XDG_DATA_HOME").is_absolute_path()) { + return get_environment("XDG_DATA_HOME").replace("\\", "/"); + } else { + WARN_PRINT_ONCE("`XDG_DATA_HOME` is a relative path. Ignoring its value and falling back to `get_config_path()` per the XDG Base Directory specification."); + } } + return get_config_path(); } String OS_Windows::get_cache_path() const { - + // The XDG Base Directory specification technically only applies on Linux/*BSD, but it doesn't hurt to support it on Windows as well. if (has_environment("XDG_CACHE_HOME")) { - return get_environment("XDG_CACHE_HOME"); - } else if (has_environment("TEMP")) { - return get_environment("TEMP"); - } else { - return get_config_path(); + if (get_environment("XDG_CACHE_HOME").is_absolute_path()) { + return get_environment("XDG_CACHE_HOME").replace("\\", "/"); + } else { + WARN_PRINT_ONCE("`XDG_CACHE_HOME` is a relative path. Ignoring its value and falling back to `%TEMP%` or `get_config_path()` per the XDG Base Directory specification."); + } + } + if (has_environment("TEMP")) { + return get_environment("TEMP").replace("\\", "/"); } + return get_config_path(); } // Get properly capitalized engine name for system paths String OS_Windows::get_godot_dir_name() const { - return String(VERSION_SHORT_NAME).capitalize(); } String OS_Windows::get_system_dir(SystemDir p_dir) const { - KNOWNFOLDERID id; switch (p_dir) { @@ -3230,15 +708,14 @@ String OS_Windows::get_system_dir(SystemDir p_dir) const { } PWSTR szPath; - HRESULT res = SHGetKnownFolderPath(id, 0, NULL, &szPath); + HRESULT res = SHGetKnownFolderPath(id, 0, nullptr, &szPath); ERR_FAIL_COND_V(res != S_OK, String()); - String path = String(szPath); + String path = String::utf16((const char16_t *)szPath).replace("\\", "/"); CoTaskMemFree(szPath); return path; } String OS_Windows::get_user_data_dir() const { - String appname = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name")); if (appname != "") { bool use_custom_dir = ProjectSettings::get_singleton()->get("application/config/use_custom_user_dir"); @@ -3257,75 +734,12 @@ String OS_Windows::get_user_data_dir() const { } String OS_Windows::get_unique_id() const { - HW_PROFILE_INFO HwProfInfo; ERR_FAIL_COND_V(!GetCurrentHwProfile(&HwProfInfo), ""); - 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 (gl_context) - gl_context->set_use_vsync(p_enable); -} -/* -bool OS_Windows::is_vsync_enabled() const { - - if (gl_context) - return gl_context->is_using_vsync(); - - return true; -}*/ - -OS::PowerState OS_Windows::get_power_state() { - return power_manager->get_power_state(); -} - -int OS_Windows::get_power_seconds_left() { - return power_manager->get_power_seconds_left(); -} - -int OS_Windows::get_power_percent_left() { - return power_manager->get_power_percent_left(); + return String::utf16((const char16_t *)(HwProfInfo.szHwProfileGuid), HW_PROFILE_GUIDLEN); } bool OS_Windows::_check_internal_feature_support(const String &p_feature) { - return p_feature == "pc"; } @@ -3337,33 +751,28 @@ 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; + Char16String utf16 = p_path.utf16(); + WCHAR *from = new WCHAR[utf16.length() + 2]; + wcscpy_s(from, utf16.length() + 1, (LPCWSTR)(utf16.get_data())); + from[utf16.length() + 1] = 0; + + sf.hwnd = main_window; sf.wFunc = FO_DELETE; sf.pFrom = from; - sf.pTo = NULL; + sf.pTo = nullptr; sf.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION; sf.fAnyOperationsAborted = FALSE; - sf.hNameMappings = NULL; - sf.lpszProgressTitle = NULL; + sf.hNameMappings = nullptr; + sf.lpszProgressTitle = nullptr; int ret = SHFileOperationW(&sf); delete[] from; if (ret) { - ERR_PRINTS("SHFileOperation error: " + itos(ret)); + ERR_PRINT("SHFileOperation error: " + itos(ret)); return FAILED; } @@ -3371,37 +780,17 @@ Error OS_Windows::move_to_trash(const String &p_path) { } OS_Windows::OS_Windows(HINSTANCE _hInstance) { + ticks_per_second = 0; + ticks_start = 0; + main_loop = nullptr; + process_map = nullptr; - 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); @@ -3410,16 +799,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 cf16295a70..8f9ef254f1 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -31,202 +31,48 @@ #ifndef OS_WINDOWS_H #define OS_WINDOWS_H -#include "context_gl_windows.h" -#include "core/os/input.h" +#include "core/config/project_settings.h" +#include "core/input/input.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 "key_mapping_windows.h" -#include "main/input_default.h" -#include "power_windows.h" #include "servers/audio_server.h" -#include "servers/visual/rasterizer.h" -#include "servers/visual_server.h" +#include "servers/rendering/renderer_compositor.h" +#include "servers/rendering_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 - -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 *gl_context; -#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; - - PowerWindows *power_manager; - - int video_driver_index; #ifdef WASAPI_ENABLED AudioDriverWASAPI driver_wasapi; #endif @@ -239,201 +85,89 @@ 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() override; - virtual void initialize_core(); - virtual Error initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver); + virtual void set_main_loop(MainLoop *p_main_loop) override; + virtual void delete_main_loop() override; - virtual void set_main_loop(MainLoop *p_main_loop); - virtual void delete_main_loop(); + virtual void finalize() override; + virtual void finalize_core() override; + virtual String get_stdin_string(bool p_block) override; - virtual void finalize(); - virtual void finalize_core(); - - void process_events(); - void process_key_events(); + String _quote_command_line_argument(const String &p_text) const; struct ProcessInfo { - STARTUPINFO si; PROCESS_INFORMATION pi; }; 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 Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false) override; + virtual Error close_dynamic_library(void *p_library_handle) override; + virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false) override; - virtual void set_borderless_window(bool p_borderless); - virtual bool get_borderless_window(); + virtual MainLoop *get_main_loop() const override; - virtual bool get_window_per_pixel_transparency_enabled() const; - virtual void set_window_per_pixel_transparency_enabled(bool p_enabled); + virtual String get_name() const override; - virtual uint8_t *get_layered_buffer_data(); - virtual Size2 get_layered_buffer_size(); - virtual void swap_layered_buffer(); + virtual void initialize_joypads() override {} - 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); + virtual Date get_date(bool utc) const override; + virtual Time get_time(bool utc) const override; + virtual TimeZoneInfo get_time_zone_info() const override; + virtual double get_unix_time() const override; - virtual MainLoop *get_main_loop() const; + virtual Error set_cwd(const String &p_cwd) override; - virtual String get_name() const; + virtual void delay_usec(uint32_t p_usec) const override; + virtual uint64_t get_ticks_usec() const override; - virtual Date get_date(bool utc) const; - virtual Time get_time(bool utc) const; - virtual TimeZoneInfo get_time_zone_info() const; - virtual uint64_t get_unix_time() const; - virtual uint64_t get_system_time_secs() const; - virtual uint64_t get_system_time_msecs() const; + virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) override; + virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr) override; + virtual Error kill(const ProcessID &p_pid) override; + virtual int get_process_id() const override; - virtual bool can_draw() const; - virtual Error set_cwd(const String &p_cwd); + virtual bool has_environment(const String &p_var) const override; + virtual String get_environment(const String &p_var) const override; + virtual bool set_environment(const String &p_var, const String &p_value) const override; - virtual void delay_usec(uint32_t p_usec) const; - virtual uint64_t get_ticks_usec() const; + virtual String get_executable_path() const override; - virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking = true, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL); - virtual Error kill(const ProcessID &p_pid); - virtual int get_process_id() const; + virtual String get_locale() const override; - virtual bool has_environment(const String &p_var) const; - virtual String get_environment(const String &p_var) const; - virtual bool set_environment(const String &p_var, const String &p_value) const; + virtual int get_processor_count() const override; - virtual void set_clipboard(const String &p_text); - virtual String get_clipboard() const; + virtual String get_config_path() const override; + virtual String get_data_path() const override; + virtual String get_cache_path() const override; + virtual String get_godot_dir_name() const override; - 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); + virtual String get_system_dir(SystemDir p_dir) const override; + virtual String get_user_data_dir() const override; - void set_native_icon(const String &p_filename); - void set_icon(const Ref<Image> &p_icon); + virtual String get_unique_id() const override; - 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; - virtual String get_godot_dir_name() const; - - virtual String get_system_dir(SystemDir p_dir) const; - virtual String get_user_data_dir() const; - - 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); + virtual Error shell_open(String p_uri) override; 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 OS::PowerState get_power_state(); - virtual int get_power_seconds_left(); - virtual int get_power_percent_left(); - - virtual bool _check_internal_feature_support(const String &p_feature); - - void disable_crash_handler(); - bool is_disable_crash_handler() const; - virtual void initialize_debugging(); + virtual bool _check_internal_feature_support(const String &p_feature) override; - void force_process_input(); + virtual void disable_crash_handler() override; + virtual bool is_disable_crash_handler() const override; + virtual void initialize_debugging() override; - virtual Error move_to_trash(const String &p_path); + virtual Error move_to_trash(const String &p_path) override; - 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/platform_config.h b/platform/windows/platform_config.h index 04653ee56c..481f583f6f 100644 --- a/platform/windows/platform_config.h +++ b/platform/windows/platform_config.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -29,8 +29,3 @@ /*************************************************************************/ #include <malloc.h> -//#else -//#include <alloca.h> -//#endif -#define GLES3_INCLUDE_H "thirdparty/glad/glad/glad.h" -#define GLES2_INCLUDE_H "thirdparty/glad/glad/glad.h" diff --git a/platform/windows/platform_windows_builders.py b/platform/windows/platform_windows_builders.py index a1ad3b8b50..22e33b51b4 100644 --- a/platform/windows/platform_windows_builders.py +++ b/platform/windows/platform_windows_builders.py @@ -9,14 +9,14 @@ from platform_methods import subprocess_main def make_debug_mingw(target, source, env): mingw_prefix = "" - if (env["bits"] == "32"): + if env["bits"] == "32": mingw_prefix = env["mingw_prefix_32"] else: mingw_prefix = env["mingw_prefix_64"] - os.system(mingw_prefix + 'objcopy --only-keep-debug {0} {0}.debugsymbols'.format(target[0])) - os.system(mingw_prefix + 'strip --strip-debug --strip-unneeded {0}'.format(target[0])) - os.system(mingw_prefix + 'objcopy --add-gnu-debuglink={0}.debugsymbols {0}'.format(target[0])) + os.system(mingw_prefix + "objcopy --only-keep-debug {0} {0}.debugsymbols".format(target[0])) + os.system(mingw_prefix + "strip --strip-debug --strip-unneeded {0}".format(target[0])) + os.system(mingw_prefix + "objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(target[0])) -if __name__ == '__main__': +if __name__ == "__main__": subprocess_main(globals()) diff --git a/platform/windows/power_windows.cpp b/platform/windows/power_windows.cpp deleted file mode 100644 index aea06da413..0000000000 --- a/platform/windows/power_windows.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/*************************************************************************/ -/* power_windows.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. */ -/*************************************************************************/ - -/* -Adapted from corresponding SDL 2.0 code. -*/ - -/* - Simple DirectMedia Layer - Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org> - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ - -#include "power_windows.h" - -// CODE CHUNK IMPORTED FROM SDL 2.0 - -bool PowerWindows::GetPowerInfo_Windows() { - SYSTEM_POWER_STATUS status; - bool need_details = FALSE; - - /* This API should exist back to Win95. */ - if (!GetSystemPowerStatus(&status)) { - /* !!! FIXME: push GetLastError() into GetError() */ - power_state = OS::POWERSTATE_UNKNOWN; - } else if (status.BatteryFlag == 0xFF) { /* unknown state */ - power_state = OS::POWERSTATE_UNKNOWN; - } else if (status.BatteryFlag & (1 << 7)) { /* no battery */ - power_state = OS::POWERSTATE_NO_BATTERY; - } else if (status.BatteryFlag & (1 << 3)) { /* charging */ - power_state = OS::POWERSTATE_CHARGING; - need_details = TRUE; - } else if (status.ACLineStatus == 1) { - power_state = OS::POWERSTATE_CHARGED; /* on AC, not charging. */ - need_details = TRUE; - } else { - power_state = OS::POWERSTATE_ON_BATTERY; /* not on AC. */ - need_details = TRUE; - } - - percent_left = -1; - nsecs_left = -1; - if (need_details) { - const int pct = (int)status.BatteryLifePercent; - const int secs = (int)status.BatteryLifeTime; - - if (pct != 255) { /* 255 == unknown */ - percent_left = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */ - } - if (secs != (int)0xFFFFFFFF) { /* ((DWORD)-1) == unknown */ - nsecs_left = secs; - } - } - - return TRUE; /* always the definitive answer on Windows. */ -} - -OS::PowerState PowerWindows::get_power_state() { - if (GetPowerInfo_Windows()) { - return power_state; - } else { - return OS::POWERSTATE_UNKNOWN; - } -} - -int PowerWindows::get_power_seconds_left() { - if (GetPowerInfo_Windows()) { - return nsecs_left; - } else { - return -1; - } -} - -int PowerWindows::get_power_percent_left() { - if (GetPowerInfo_Windows()) { - return percent_left; - } else { - return -1; - } -} - -PowerWindows::PowerWindows() : - nsecs_left(-1), - percent_left(-1), - power_state(OS::POWERSTATE_UNKNOWN) { -} - -PowerWindows::~PowerWindows() { -} diff --git a/platform/windows/vulkan_context_win.cpp b/platform/windows/vulkan_context_win.cpp new file mode 100644 index 0000000000..e5e176ab93 --- /dev/null +++ b/platform/windows/vulkan_context_win.cpp @@ -0,0 +1,56 @@ +/*************************************************************************/ +/* vulkan_context_win.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "vulkan_context_win.h" +#include <vulkan/vulkan_win32.h> + +const char *VulkanContextWindows::_get_platform_surface_extension() const { + return VK_KHR_WIN32_SURFACE_EXTENSION_NAME; +} + +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; + createInfo.pNext = nullptr; + createInfo.flags = 0; + createInfo.hinstance = p_instance; + createInfo.hwnd = p_window; + + VkSurfaceKHR surface; + VkResult err = vkCreateWin32SurfaceKHR(_get_instance(), &createInfo, nullptr, &surface); + ERR_FAIL_COND_V(err, -1); + return _window_create(p_window_id, surface, p_width, p_height); +} + +VulkanContextWindows::VulkanContextWindows() { +} + +VulkanContextWindows::~VulkanContextWindows() { +} diff --git a/platform/windows/power_windows.h b/platform/windows/vulkan_context_win.h index 80d86a12c5..4fe987218d 100644 --- a/platform/windows/power_windows.h +++ b/platform/windows/vulkan_context_win.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* power_windows.h */ +/* vulkan_context_win.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). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -28,31 +28,20 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef POWER_WINDOWS_H -#define POWER_WINDOWS_H - -#include "core/os/dir_access.h" -#include "core/os/file_access.h" -#include "core/os/os.h" +#ifndef VULKAN_DEVICE_WIN_H +#define VULKAN_DEVICE_WIN_H +#include "drivers/vulkan/vulkan_context.h" #include <windows.h> -class PowerWindows { - -private: - int nsecs_left; - int percent_left; - OS::PowerState power_state; - - bool GetPowerInfo_Windows(); +class VulkanContextWindows : public VulkanContext { + virtual const char *_get_platform_surface_extension() const; public: - PowerWindows(); - virtual ~PowerWindows(); + int window_create(DisplayServer::WindowID p_window_id, HWND p_window, HINSTANCE p_instance, int p_width, int p_height); - OS::PowerState get_power_state(); - int get_power_seconds_left(); - int get_power_percent_left(); + VulkanContextWindows(); + ~VulkanContextWindows(); }; -#endif // POWER_WINDOWS_H +#endif // VULKAN_DEVICE_WIN_H diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp index 8eb6adc27b..8cab7ca521 100644 --- a/platform/windows/windows_terminal_logger.cpp +++ b/platform/windows/windows_terminal_logger.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ @@ -49,11 +49,12 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er len = BUFFER_SIZE; // Output is too big, will be truncated buf[len] = 0; - int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, len, NULL, 0); + int wlen = MultiByteToWideChar(CP_UTF8, 0, buf, len, nullptr, 0); if (wlen < 0) return; - wchar_t *wbuf = (wchar_t *)malloc((len + 1) * sizeof(wchar_t)); + wchar_t *wbuf = (wchar_t *)memalloc((len + 1) * sizeof(wchar_t)); + ERR_FAIL_NULL_MSG(wbuf, "Out of memory."); MultiByteToWideChar(CP_UTF8, 0, buf, len, wbuf, wlen); wbuf[wlen] = 0; @@ -62,7 +63,7 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er else wprintf(L"%ls", wbuf); - free(wbuf); + memfree(wbuf); #ifdef DEBUG_ENABLED fflush(stdout); @@ -81,70 +82,73 @@ void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file StdLogger::log_error(p_function, p_file, p_line, p_code, p_rationale, p_type); #ifndef UWP_ENABLED } else { - CONSOLE_SCREEN_BUFFER_INFO sbi; //original GetConsoleScreenBufferInfo(hCon, &sbi); - WORD current_fg = sbi.wAttributes & (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); WORD current_bg = sbi.wAttributes & (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY); uint32_t basecol = 0; switch (p_type) { - case ERR_ERROR: basecol = FOREGROUND_RED; break; - case ERR_WARNING: basecol = FOREGROUND_RED | FOREGROUND_GREEN; break; - case ERR_SCRIPT: basecol = FOREGROUND_RED | FOREGROUND_BLUE; break; - case ERR_SHADER: basecol = FOREGROUND_GREEN | FOREGROUND_BLUE; break; + case ERR_ERROR: + basecol = FOREGROUND_RED; + break; + case ERR_WARNING: + basecol = FOREGROUND_RED | FOREGROUND_GREEN; + break; + case ERR_SCRIPT: + basecol = FOREGROUND_RED | FOREGROUND_BLUE; + break; + case ERR_SHADER: + basecol = FOREGROUND_GREEN | FOREGROUND_BLUE; + break; } basecol |= current_bg; - if (p_rationale && p_rationale[0]) { - - SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY); - switch (p_type) { - case ERR_ERROR: logf("ERROR: "); break; - case ERR_WARNING: logf("WARNING: "); break; - case ERR_SCRIPT: logf("SCRIPT ERROR: "); break; - case ERR_SHADER: logf("SHADER ERROR: "); break; - } - - SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY); - logf("%s\n", p_rationale); + SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY); + switch (p_type) { + case ERR_ERROR: + logf_error("ERROR:"); + break; + case ERR_WARNING: + logf_error("WARNING:"); + break; + case ERR_SCRIPT: + logf_error("SCRIPT ERROR:"); + break; + case ERR_SHADER: + logf_error("SHADER ERROR:"); + break; + } - SetConsoleTextAttribute(hCon, basecol); - switch (p_type) { - case ERR_ERROR: logf(" At: "); break; - case ERR_WARNING: logf(" At: "); break; - case ERR_SCRIPT: logf(" At: "); break; - case ERR_SHADER: logf(" At: "); break; - } + SetConsoleTextAttribute(hCon, basecol); + if (p_rationale && p_rationale[0]) { + logf_error(" %s\n", p_rationale); + } else { + logf_error(" %s\n", p_code); + } - SetConsoleTextAttribute(hCon, current_fg | current_bg); - logf("%s:%i\n", p_file, p_line); + // `FOREGROUND_INTENSITY` alone results in gray text. + SetConsoleTextAttribute(hCon, FOREGROUND_INTENSITY); + switch (p_type) { + case ERR_ERROR: + logf_error(" at: "); + break; + case ERR_WARNING: + logf_error(" at: "); + break; + case ERR_SCRIPT: + logf_error(" at: "); + break; + case ERR_SHADER: + logf_error(" at: "); + break; + } + if (p_rationale && p_rationale[0]) { + logf_error("(%s:%i)\n", p_file, p_line); } else { - - SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY); - switch (p_type) { - case ERR_ERROR: logf("ERROR: %s: ", p_function); break; - case ERR_WARNING: logf("WARNING: %s: ", p_function); break; - case ERR_SCRIPT: logf("SCRIPT ERROR: %s: ", p_function); break; - case ERR_SHADER: logf("SCRIPT ERROR: %s: ", p_function); break; - } - - SetConsoleTextAttribute(hCon, current_fg | current_bg | FOREGROUND_INTENSITY); - logf("%s\n", p_code); - - SetConsoleTextAttribute(hCon, basecol); - switch (p_type) { - case ERR_ERROR: logf(" At: "); break; - case ERR_WARNING: logf(" At: "); break; - case ERR_SCRIPT: logf(" At: "); break; - case ERR_SHADER: logf(" At: "); break; - } - - SetConsoleTextAttribute(hCon, current_fg | current_bg); - logf("%s:%i\n", p_file, p_line); + logf_error("%s (%s:%i)\n", p_function, p_file, p_line); } SetConsoleTextAttribute(hCon, sbi.wAttributes); diff --git a/platform/windows/windows_terminal_logger.h b/platform/windows/windows_terminal_logger.h index d4443a707d..aacfe5869e 100644 --- a/platform/windows/windows_terminal_logger.h +++ b/platform/windows/windows_terminal_logger.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 */ |