initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
15
platform/windows/README.md
Normal file
15
platform/windows/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Windows platform port
|
||||
|
||||
This folder contains the C++ code for the Windows platform port.
|
||||
|
||||
See also [`misc/dist/windows`](/misc/dist/windows) folder for additional files
|
||||
used by this platform.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Compiling for Windows](https://docs.godotengine.org/en/latest/engine_details/development/compiling/compiling_for_windows.html)
|
||||
- Instructions on building this platform port from source.
|
||||
- [Exporting for Windows](https://docs.godotengine.org/en/latest/tutorials/export/exporting_for_windows.html)
|
||||
- Instructions on using the compiled export templates to export a project.
|
||||
- [Changing application icon for Windows](https://docs.godotengine.org/en/latest/tutorials/export/changing_application_icon_for_windows.html)
|
||||
- Instructions on using a custom icon for the exported project executable.
|
151
platform/windows/SCsub
Normal file
151
platform/windows/SCsub
Normal file
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import platform_windows_builders
|
||||
|
||||
from methods import redirect_emitter
|
||||
|
||||
sources = []
|
||||
|
||||
common_win = [
|
||||
"godot_windows.cpp",
|
||||
"os_windows.cpp",
|
||||
"display_server_windows.cpp",
|
||||
"key_mapping_windows.cpp",
|
||||
"tts_windows.cpp",
|
||||
"windows_terminal_logger.cpp",
|
||||
"windows_utils.cpp",
|
||||
"native_menu_windows.cpp",
|
||||
"gl_manager_windows_native.cpp",
|
||||
"gl_manager_windows_angle.cpp",
|
||||
"wgl_detect_version.cpp",
|
||||
"rendering_context_driver_vulkan_windows.cpp",
|
||||
"drop_target_windows.cpp",
|
||||
]
|
||||
|
||||
if env.msvc:
|
||||
common_win += ["crash_handler_windows_seh.cpp"]
|
||||
else:
|
||||
common_win += ["crash_handler_windows_signal.cpp"]
|
||||
|
||||
common_win_wrap = [
|
||||
"console_wrapper_windows.cpp",
|
||||
]
|
||||
|
||||
env_wrap = env.Clone()
|
||||
|
||||
if env["arch"] == "x86_64":
|
||||
env_cpp_check = env.Clone()
|
||||
env_cpp_check.add_source_files(sources, ["cpu_feature_validation.c"])
|
||||
if env.msvc:
|
||||
if "/d2archSSE42" in env_cpp_check["CCFLAGS"]:
|
||||
env_cpp_check["CCFLAGS"].remove("/d2archSSE42")
|
||||
env.Append(LINKFLAGS=["/ENTRY:ShimMainCRTStartup"])
|
||||
else:
|
||||
if "-msse4.2" in env_cpp_check["CCFLAGS"]:
|
||||
env_cpp_check["CCFLAGS"].remove("-msse4.2")
|
||||
env.Append(LINKFLAGS=["-Wl,--entry=ShimMainCRTStartup"])
|
||||
|
||||
|
||||
def arrange_program_clean(prog):
|
||||
"""
|
||||
Given an SCons program, arrange for output files SCons doesn't know about
|
||||
to be cleaned when SCons is called with --clean
|
||||
"""
|
||||
extensions_to_clean = [".ilk", ".exp", ".pdb", ".lib"]
|
||||
for program in prog:
|
||||
executable_stem = Path(program.name).stem
|
||||
extra_files_to_clean = [f"#bin/{executable_stem}{extension}" for extension in extensions_to_clean]
|
||||
Clean(prog, extra_files_to_clean)
|
||||
|
||||
|
||||
env["BUILDERS"]["RES"].emitter = redirect_emitter
|
||||
res_file = "godot_res.rc"
|
||||
res_target = "godot_res" + env["OBJSUFFIX"]
|
||||
res_obj = env.RES(res_target, res_file)
|
||||
env.Depends(res_obj, "#core/version_generated.gen.h")
|
||||
|
||||
env.add_source_files(sources, common_win)
|
||||
sources += res_obj
|
||||
|
||||
if env["accesskit"] and not env.msvc:
|
||||
sources += env.DEFLIB("uiautomationcore")
|
||||
|
||||
prog = env.add_program("#bin/godot", sources, PROGSUFFIX=env["PROGSUFFIX"])
|
||||
arrange_program_clean(prog)
|
||||
|
||||
if env.msvc:
|
||||
env.Depends(prog, "godot.natvis")
|
||||
|
||||
# Build console wrapper app.
|
||||
if env["windows_subsystem"] == "gui":
|
||||
res_wrap_file = "godot_res_wrap.rc"
|
||||
res_wrap_target = "godot_res_wrap" + env["OBJSUFFIX"]
|
||||
res_wrap_obj = env_wrap.RES(res_wrap_target, res_wrap_file)
|
||||
env_wrap.Depends(res_wrap_obj, "#core/version_generated.gen.h")
|
||||
|
||||
if env.msvc:
|
||||
env_wrap.Append(LINKFLAGS=["/SUBSYSTEM:CONSOLE"])
|
||||
env_wrap.Append(LINKFLAGS=["version.lib"])
|
||||
else:
|
||||
env_wrap.Append(LINKFLAGS=["-Wl,--subsystem,console"])
|
||||
env_wrap.Append(LIBS=["version"])
|
||||
|
||||
prog_wrap = env_wrap.add_program("#bin/godot", common_win_wrap + res_wrap_obj, PROGSUFFIX=env["PROGSUFFIX_WRAP"])
|
||||
arrange_program_clean(prog_wrap)
|
||||
env_wrap.Depends(prog_wrap, prog)
|
||||
sources += common_win_wrap + res_wrap_obj
|
||||
|
||||
if env["d3d12"]:
|
||||
dxc_target_aliases = {
|
||||
"x86_32": "x86",
|
||||
"x86_64": "x64",
|
||||
"arm32": "arm",
|
||||
"arm64": "arm64",
|
||||
}
|
||||
dxc_arch_subdir = dxc_target_aliases[env["arch"]]
|
||||
|
||||
agility_target_aliases = {
|
||||
"x86_32": "win32",
|
||||
"x86_64": "x64",
|
||||
"arm32": "arm",
|
||||
"arm64": "arm64",
|
||||
}
|
||||
agility_arch_subdir = agility_target_aliases[env["arch"]]
|
||||
|
||||
# Used in cases where we can have multiple archs side-by-side.
|
||||
arch_bin_dir = "#bin/" + env["arch"]
|
||||
|
||||
# Agility SDK
|
||||
if env["agility_sdk_path"] != "" and os.path.exists(env["agility_sdk_path"]):
|
||||
agility_dlls = ["D3D12Core.dll", "d3d12SDKLayers.dll"]
|
||||
# Whether these are loaded from arch-specific directory or not has to be known at build time.
|
||||
target_dir = arch_bin_dir if env["agility_sdk_multiarch"] else "#bin"
|
||||
for dll in agility_dlls:
|
||||
env.CommandNoCache(
|
||||
target_dir + "/" + dll,
|
||||
env["agility_sdk_path"] + "/build/native/bin/" + agility_arch_subdir + "/" + dll,
|
||||
Copy("$TARGET", "$SOURCE"),
|
||||
)
|
||||
|
||||
# PIX
|
||||
if env["use_pix"]:
|
||||
pix_dll = "WinPixEventRuntime.dll"
|
||||
env.CommandNoCache(
|
||||
"#bin/" + pix_dll,
|
||||
env["pix_path"] + "/bin/" + dxc_arch_subdir + "/" + pix_dll,
|
||||
Copy("$TARGET", "$SOURCE"),
|
||||
)
|
||||
|
||||
if not env.msvc:
|
||||
if env["debug_symbols"]:
|
||||
env.AddPostAction(prog, env.Run(platform_windows_builders.make_debug_mingw))
|
||||
if env["windows_subsystem"] == "gui":
|
||||
env.AddPostAction(prog_wrap, env.Run(platform_windows_builders.make_debug_mingw))
|
||||
|
||||
env.platform_sources += sources
|
187
platform/windows/console_wrapper_windows.cpp
Normal file
187
platform/windows/console_wrapper_windows.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/**************************************************************************/
|
||||
/* console_wrapper_windows.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 <windows.h>
|
||||
|
||||
#include <shlwapi.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Get executable name.
|
||||
WCHAR exe_name[32767] = {};
|
||||
if (!GetModuleFileNameW(nullptr, exe_name, 32767)) {
|
||||
wprintf(L"GetModuleFileName failed, error %d\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get product name from the resources and set console title.
|
||||
DWORD ver_info_handle = 0;
|
||||
DWORD ver_info_size = GetFileVersionInfoSizeW(exe_name, &ver_info_handle);
|
||||
if (ver_info_size > 0) {
|
||||
LPBYTE ver_info = (LPBYTE)malloc(ver_info_size);
|
||||
if (ver_info) {
|
||||
if (GetFileVersionInfoW(exe_name, ver_info_handle, ver_info_size, ver_info)) {
|
||||
LPCWSTR text_ptr = nullptr;
|
||||
UINT text_size = 0;
|
||||
if (VerQueryValueW(ver_info, L"\\StringFileInfo\\040904b0\\ProductName", (void **)&text_ptr, &text_size) && (text_size > 0)) {
|
||||
SetConsoleTitleW(text_ptr);
|
||||
}
|
||||
}
|
||||
free(ver_info);
|
||||
}
|
||||
}
|
||||
|
||||
// Enable virtual terminal sequences processing.
|
||||
HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD out_mode = 0;
|
||||
GetConsoleMode(stdout_handle, &out_mode);
|
||||
out_mode |= ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
SetConsoleMode(stdout_handle, out_mode);
|
||||
|
||||
// Find main executable name and check if it exist.
|
||||
static PCWSTR exe_renames[] = {
|
||||
L".console.exe",
|
||||
L"_console.exe",
|
||||
L" console.exe",
|
||||
L"console.exe",
|
||||
nullptr,
|
||||
};
|
||||
|
||||
bool rename_found = false;
|
||||
for (int i = 0; exe_renames[i]; i++) {
|
||||
PWSTR c = StrRStrIW(exe_name, nullptr, exe_renames[i]);
|
||||
if (c) {
|
||||
CopyMemory(c, L".exe", sizeof(WCHAR) * 5);
|
||||
rename_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!rename_found) {
|
||||
wprintf(L"Invalid wrapper executable name.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
DWORD file_attrib = GetFileAttributesW(exe_name);
|
||||
if (file_attrib == INVALID_FILE_ATTRIBUTES || (file_attrib & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
wprintf(L"Main executable %ls not found.\n", exe_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create job to monitor process tree.
|
||||
HANDLE job_handle = CreateJobObjectW(nullptr, nullptr);
|
||||
if (!job_handle) {
|
||||
wprintf(L"CreateJobObject failed, error %d\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
HANDLE io_port_handle = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1);
|
||||
if (!io_port_handle) {
|
||||
wprintf(L"CreateIoCompletionPort failed, error %d\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
JOBOBJECT_ASSOCIATE_COMPLETION_PORT compl_port;
|
||||
ZeroMemory(&compl_port, sizeof(compl_port));
|
||||
compl_port.CompletionKey = job_handle;
|
||||
compl_port.CompletionPort = io_port_handle;
|
||||
|
||||
if (!SetInformationJobObject(job_handle, JobObjectAssociateCompletionPortInformation, &compl_port, sizeof(compl_port))) {
|
||||
wprintf(L"SetInformationJobObject(AssociateCompletionPortInformation) failed, error %d\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli;
|
||||
ZeroMemory(&jeli, sizeof(jeli));
|
||||
jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
||||
|
||||
if (!SetInformationJobObject(job_handle, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli))) {
|
||||
wprintf(L"SetInformationJobObject(ExtendedLimitInformation) failed, error %d\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Start the main process.
|
||||
PROCESS_INFORMATION pi;
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
STARTUPINFOW si;
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
si.dwFlags = STARTF_USESTDHANDLES;
|
||||
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
WCHAR new_command_line[32767];
|
||||
_snwprintf_s(new_command_line, 32767, _TRUNCATE, L"%ls %ls", exe_name, PathGetArgsW(GetCommandLineW()));
|
||||
|
||||
if (!CreateProcessW(nullptr, new_command_line, nullptr, nullptr, true, CREATE_SUSPENDED, nullptr, nullptr, &si, &pi)) {
|
||||
wprintf(L"CreateProcess failed, error %d\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!AssignProcessToJobObject(job_handle, pi.hProcess)) {
|
||||
wprintf(L"AssignProcessToJobObject failed, error %d\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
ResumeThread(pi.hThread);
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
// Wait until main process and all of its children are finished.
|
||||
DWORD completion_code = 0;
|
||||
ULONG_PTR completion_key = 0;
|
||||
LPOVERLAPPED overlapped = nullptr;
|
||||
|
||||
while (GetQueuedCompletionStatus(io_port_handle, &completion_code, &completion_key, &overlapped, INFINITE)) {
|
||||
if ((HANDLE)completion_key == job_handle && completion_code == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(job_handle);
|
||||
CloseHandle(io_port_handle);
|
||||
|
||||
// Get exit code of the main process.
|
||||
DWORD exit_code = 0;
|
||||
GetExitCodeProcess(pi.hProcess, &exit_code);
|
||||
|
||||
CloseHandle(pi.hProcess);
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
|
||||
return main(0, nullptr);
|
||||
}
|
80
platform/windows/cpu_feature_validation.c
Normal file
80
platform/windows/cpu_feature_validation.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/**************************************************************************/
|
||||
/* cpu_feature_validation.c */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 <windows.h>
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h> // For builtin __cpuid.
|
||||
#else
|
||||
void __cpuid(int *r_cpuinfo, int p_info) {
|
||||
// Note: Some compilers have a buggy `__cpuid` intrinsic, using inline assembly (based on LLVM-20 implementation) instead.
|
||||
__asm__ __volatile__(
|
||||
"xchgq %%rbx, %q1;"
|
||||
"cpuid;"
|
||||
"xchgq %%rbx, %q1;"
|
||||
: "=a"(r_cpuinfo[0]), "=r"(r_cpuinfo[1]), "=c"(r_cpuinfo[2]), "=d"(r_cpuinfo[3])
|
||||
: "0"(p_info));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef PF_SSE4_2_INSTRUCTIONS_AVAILABLE
|
||||
#define PF_SSE4_2_INSTRUCTIONS_AVAILABLE 38
|
||||
#endif
|
||||
|
||||
#ifdef WINDOWS_SUBSYSTEM_CONSOLE
|
||||
extern int WINAPI mainCRTStartup();
|
||||
#else
|
||||
extern int WINAPI WinMainCRTStartup();
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
extern int WINAPI ShimMainCRTStartup() __attribute__((used));
|
||||
#endif
|
||||
|
||||
extern int WINAPI ShimMainCRTStartup() {
|
||||
BOOL win_sse42_supported = FALSE;
|
||||
BOOL cpuid_sse42_supported = FALSE;
|
||||
|
||||
int cpuinfo[4];
|
||||
__cpuid(cpuinfo, 0x01);
|
||||
|
||||
win_sse42_supported = IsProcessorFeaturePresent(PF_SSE4_2_INSTRUCTIONS_AVAILABLE);
|
||||
cpuid_sse42_supported = cpuinfo[2] & (1 << 20);
|
||||
|
||||
if (win_sse42_supported || cpuid_sse42_supported) {
|
||||
#ifdef WINDOWS_SUBSYSTEM_CONSOLE
|
||||
return mainCRTStartup();
|
||||
#else
|
||||
return WinMainCRTStartup();
|
||||
#endif
|
||||
} else {
|
||||
MessageBoxW(NULL, L"A CPU with SSE4.2 instruction set support is required.", L"Godot Engine", MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
|
||||
return -1;
|
||||
}
|
||||
}
|
57
platform/windows/crash_handler_windows.h
Normal file
57
platform/windows/crash_handler_windows.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/**************************************************************************/
|
||||
/* crash_handler_windows.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
// Crash handler exception only enabled with MSVC
|
||||
#if defined(DEBUG_ENABLED)
|
||||
#define CRASH_HANDLER_EXCEPTION 1
|
||||
|
||||
#ifdef _MSC_VER
|
||||
extern DWORD CrashHandlerException(EXCEPTION_POINTERS *ep);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
class CrashHandler {
|
||||
bool disabled;
|
||||
|
||||
public:
|
||||
void initialize();
|
||||
|
||||
void disable();
|
||||
bool is_disabled() const { return disabled; }
|
||||
|
||||
CrashHandler();
|
||||
~CrashHandler();
|
||||
};
|
262
platform/windows/crash_handler_windows_seh.cpp
Normal file
262
platform/windows/crash_handler_windows_seh.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
/**************************************************************************/
|
||||
/* crash_handler_windows_seh.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 "crash_handler_windows.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/version.h"
|
||||
#include "main/main.h"
|
||||
|
||||
#ifdef CRASH_HANDLER_EXCEPTION
|
||||
|
||||
// Backtrace code based on: https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <psapi.h>
|
||||
|
||||
// Some versions of imagehlp.dll lack the proper packing directives themselves
|
||||
// so we need to do it.
|
||||
#pragma pack(push, before_imagehlp, 8)
|
||||
#include <imagehlp.h>
|
||||
#pragma pack(pop, before_imagehlp)
|
||||
|
||||
struct module_data {
|
||||
std::string image_name;
|
||||
std::string module_name;
|
||||
void *base_address = nullptr;
|
||||
DWORD load_size;
|
||||
};
|
||||
|
||||
class symbol {
|
||||
typedef IMAGEHLP_SYMBOL64 sym_type;
|
||||
sym_type *sym;
|
||||
static const int max_name_len = 1024;
|
||||
|
||||
public:
|
||||
symbol(HANDLE process, DWORD64 address) :
|
||||
sym((sym_type *)::operator new(sizeof(*sym) + max_name_len)) {
|
||||
memset(sym, '\0', sizeof(*sym) + max_name_len);
|
||||
sym->SizeOfStruct = sizeof(*sym);
|
||||
sym->MaxNameLength = max_name_len;
|
||||
DWORD64 displacement;
|
||||
|
||||
SymGetSymFromAddr64(process, address, &displacement, sym);
|
||||
}
|
||||
|
||||
std::string name() { return std::string(sym->Name); }
|
||||
std::string undecorated_name() {
|
||||
if (*sym->Name == '\0') {
|
||||
return "<couldn't map PC to fn name>";
|
||||
}
|
||||
std::vector<char> und_name(max_name_len);
|
||||
UnDecorateSymbolName(sym->Name, &und_name[0], max_name_len, UNDNAME_COMPLETE);
|
||||
return std::string(&und_name[0], strlen(&und_name[0]));
|
||||
}
|
||||
};
|
||||
|
||||
class get_mod_info {
|
||||
HANDLE process;
|
||||
|
||||
public:
|
||||
get_mod_info(HANDLE h) :
|
||||
process(h) {}
|
||||
|
||||
module_data operator()(HMODULE module) {
|
||||
module_data ret;
|
||||
char temp[4096];
|
||||
MODULEINFO mi;
|
||||
|
||||
GetModuleInformation(process, module, &mi, sizeof(mi));
|
||||
ret.base_address = mi.lpBaseOfDll;
|
||||
ret.load_size = mi.SizeOfImage;
|
||||
|
||||
GetModuleFileNameEx(process, module, temp, sizeof(temp));
|
||||
ret.image_name = temp;
|
||||
GetModuleBaseName(process, module, temp, sizeof(temp));
|
||||
ret.module_name = temp;
|
||||
std::vector<char> img(ret.image_name.begin(), ret.image_name.end());
|
||||
std::vector<char> mod(ret.module_name.begin(), ret.module_name.end());
|
||||
SymLoadModule64(process, nullptr, &img[0], &mod[0], (DWORD64)ret.base_address, ret.load_size);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
DWORD CrashHandlerException(EXCEPTION_POINTERS *ep) {
|
||||
HANDLE process = GetCurrentProcess();
|
||||
HANDLE hThread = GetCurrentThread();
|
||||
DWORD offset_from_symbol = 0;
|
||||
IMAGEHLP_LINE64 line = {};
|
||||
std::vector<module_data> modules;
|
||||
DWORD cbNeeded;
|
||||
std::vector<HMODULE> module_handles(1);
|
||||
|
||||
if (OS::get_singleton() == nullptr || OS::get_singleton()->is_disable_crash_handler() || IsDebuggerPresent()) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
if (OS::get_singleton()->is_crash_handler_silent()) {
|
||||
std::_Exit(0);
|
||||
}
|
||||
|
||||
String msg;
|
||||
if (ProjectSettings::get_singleton()) {
|
||||
msg = GLOBAL_GET("debug/settings/crash_handler/message");
|
||||
}
|
||||
|
||||
// Tell MainLoop about the crash. This can be handled by users too in Node.
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
|
||||
}
|
||||
|
||||
print_error("\n================================================================");
|
||||
print_error(vformat("%s: Program crashed", __FUNCTION__));
|
||||
|
||||
// Print the engine version just before, so that people are reminded to include the version in backtrace reports.
|
||||
if (String(GODOT_VERSION_HASH).is_empty()) {
|
||||
print_error(vformat("Engine version: %s", GODOT_VERSION_FULL_NAME));
|
||||
} else {
|
||||
print_error(vformat("Engine version: %s (%s)", GODOT_VERSION_FULL_NAME, GODOT_VERSION_HASH));
|
||||
}
|
||||
print_error(vformat("Dumping the backtrace. %s", msg));
|
||||
|
||||
// Load the symbols:
|
||||
if (!SymInitialize(process, nullptr, false)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME | SYMOPT_EXACT_SYMBOLS);
|
||||
EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded);
|
||||
module_handles.resize(cbNeeded / sizeof(HMODULE));
|
||||
EnumProcessModules(process, &module_handles[0], module_handles.size() * sizeof(HMODULE), &cbNeeded);
|
||||
std::transform(module_handles.begin(), module_handles.end(), std::back_inserter(modules), get_mod_info(process));
|
||||
void *base = modules[0].base_address;
|
||||
|
||||
// Setup stuff:
|
||||
CONTEXT *context = ep->ContextRecord;
|
||||
STACKFRAME64 frame;
|
||||
bool skip_first = false;
|
||||
|
||||
frame.AddrPC.Mode = AddrModeFlat;
|
||||
frame.AddrStack.Mode = AddrModeFlat;
|
||||
frame.AddrFrame.Mode = AddrModeFlat;
|
||||
|
||||
#if defined(_M_X64)
|
||||
frame.AddrPC.Offset = context->Rip;
|
||||
frame.AddrStack.Offset = context->Rsp;
|
||||
frame.AddrFrame.Offset = context->Rbp;
|
||||
#elif defined(_M_ARM64) || defined(_M_ARM64EC)
|
||||
frame.AddrPC.Offset = context->Pc;
|
||||
frame.AddrStack.Offset = context->Sp;
|
||||
frame.AddrFrame.Offset = context->Fp;
|
||||
#elif defined(_M_ARM)
|
||||
frame.AddrPC.Offset = context->Pc;
|
||||
frame.AddrStack.Offset = context->Sp;
|
||||
frame.AddrFrame.Offset = context->R11;
|
||||
#else
|
||||
frame.AddrPC.Offset = context->Eip;
|
||||
frame.AddrStack.Offset = context->Esp;
|
||||
frame.AddrFrame.Offset = context->Ebp;
|
||||
|
||||
// Skip the first one to avoid a duplicate on 32-bit mode
|
||||
skip_first = true;
|
||||
#endif
|
||||
|
||||
line.SizeOfStruct = sizeof(line);
|
||||
IMAGE_NT_HEADERS *h = ImageNtHeader(base);
|
||||
DWORD image_type = h->FileHeader.Machine;
|
||||
|
||||
int n = 0;
|
||||
do {
|
||||
if (skip_first) {
|
||||
skip_first = false;
|
||||
} else {
|
||||
if (frame.AddrPC.Offset != 0) {
|
||||
std::string fnName = symbol(process, frame.AddrPC.Offset).undecorated_name();
|
||||
|
||||
if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &offset_from_symbol, &line)) {
|
||||
print_error(vformat("[%d] %s (%s:%d)", n, fnName.c_str(), (char *)line.FileName, (int)line.LineNumber));
|
||||
} else {
|
||||
print_error(vformat("[%d] %s", n, fnName.c_str()));
|
||||
}
|
||||
} else {
|
||||
print_error(vformat("[%d] ???", n));
|
||||
}
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
if (!StackWalk64(image_type, process, hThread, &frame, context, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr)) {
|
||||
break;
|
||||
}
|
||||
} while (frame.AddrReturn.Offset != 0 && n < 256);
|
||||
|
||||
print_error("-- END OF C++ BACKTRACE --");
|
||||
print_error("================================================================");
|
||||
|
||||
SymCleanup(process);
|
||||
|
||||
for (const Ref<ScriptBacktrace> &backtrace : ScriptServer::capture_script_backtraces(false)) {
|
||||
if (!backtrace->is_empty()) {
|
||||
print_error(backtrace->format());
|
||||
print_error(vformat("-- END OF %s BACKTRACE --", backtrace->get_language_name().to_upper()));
|
||||
print_error("================================================================");
|
||||
}
|
||||
}
|
||||
|
||||
// Pass the exception to the OS
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
#endif
|
||||
|
||||
CrashHandler::CrashHandler() {
|
||||
disabled = false;
|
||||
}
|
||||
|
||||
CrashHandler::~CrashHandler() {
|
||||
}
|
||||
|
||||
void CrashHandler::disable() {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
disabled = true;
|
||||
}
|
||||
|
||||
void CrashHandler::initialize() {
|
||||
}
|
224
platform/windows/crash_handler_windows_signal.cpp
Normal file
224
platform/windows/crash_handler_windows_signal.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/**************************************************************************/
|
||||
/* crash_handler_windows_signal.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 "crash_handler_windows.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/object/script_language.h"
|
||||
#include "core/os/os.h"
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/version.h"
|
||||
#include "main/main.h"
|
||||
|
||||
#ifdef CRASH_HANDLER_EXCEPTION
|
||||
|
||||
#include <cxxabi.h>
|
||||
#include <algorithm>
|
||||
#include <csignal>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <psapi.h>
|
||||
|
||||
#include "thirdparty/libbacktrace/backtrace.h"
|
||||
|
||||
struct CrashHandlerData {
|
||||
int64_t index = 0;
|
||||
backtrace_state *state = nullptr;
|
||||
int64_t offset = 0;
|
||||
};
|
||||
|
||||
int symbol_callback(void *data, uintptr_t pc, const char *filename, int lineno, const char *function) {
|
||||
CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data);
|
||||
if (!function) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char fname[1024];
|
||||
snprintf(fname, 1024, "%s", function);
|
||||
|
||||
if (function[0] == '_') {
|
||||
int status;
|
||||
char *demangled = abi::__cxa_demangle(function, nullptr, nullptr, &status);
|
||||
|
||||
if (status == 0 && demangled) {
|
||||
snprintf(fname, 1024, "%s", demangled);
|
||||
}
|
||||
|
||||
if (demangled) {
|
||||
free(demangled);
|
||||
}
|
||||
}
|
||||
|
||||
print_error(vformat("[%d] %s (%s:%d)", ch_data->index++, String::utf8(fname), String::utf8(filename), lineno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void error_callback(void *data, const char *msg, int errnum) {
|
||||
CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data);
|
||||
if (ch_data->index == 0) {
|
||||
print_error(vformat("Error(%d): %s", errnum, String::utf8(msg)));
|
||||
} else {
|
||||
print_error(vformat("[%d] error(%d): %s", ch_data->index++, errnum, String::utf8(msg)));
|
||||
}
|
||||
}
|
||||
|
||||
int trace_callback(void *data, uintptr_t pc) {
|
||||
CrashHandlerData *ch_data = reinterpret_cast<CrashHandlerData *>(data);
|
||||
backtrace_pcinfo(ch_data->state, pc - ch_data->offset, &symbol_callback, &error_callback, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t get_image_base(const String &p_path) {
|
||||
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
|
||||
if (f.is_null()) {
|
||||
return 0;
|
||||
}
|
||||
{
|
||||
f->seek(0x3c);
|
||||
uint32_t pe_pos = f->get_32();
|
||||
|
||||
f->seek(pe_pos);
|
||||
uint32_t magic = f->get_32();
|
||||
if (magic != 0x00004550) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
int64_t opt_header_pos = f->get_position() + 0x14;
|
||||
f->seek(opt_header_pos);
|
||||
|
||||
uint16_t opt_header_magic = f->get_16();
|
||||
if (opt_header_magic == 0x10B) {
|
||||
f->seek(opt_header_pos + 0x1C);
|
||||
return f->get_32();
|
||||
} else if (opt_header_magic == 0x20B) {
|
||||
f->seek(opt_header_pos + 0x18);
|
||||
return f->get_64();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
extern void CrashHandlerException(int signal) {
|
||||
CrashHandlerData data;
|
||||
|
||||
if (OS::get_singleton() == nullptr || OS::get_singleton()->is_disable_crash_handler() || IsDebuggerPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (OS::get_singleton()->is_crash_handler_silent()) {
|
||||
std::_Exit(0);
|
||||
}
|
||||
|
||||
String msg;
|
||||
if (ProjectSettings::get_singleton()) {
|
||||
msg = GLOBAL_GET("debug/settings/crash_handler/message");
|
||||
}
|
||||
|
||||
// Tell MainLoop about the crash. This can be handled by users too in Node.
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_CRASH);
|
||||
}
|
||||
|
||||
print_error("\n================================================================");
|
||||
print_error(vformat("%s: Program crashed with signal %d", __FUNCTION__, signal));
|
||||
|
||||
// Print the engine version just before, so that people are reminded to include the version in backtrace reports.
|
||||
if (String(GODOT_VERSION_HASH).is_empty()) {
|
||||
print_error(vformat("Engine version: %s", GODOT_VERSION_FULL_NAME));
|
||||
} else {
|
||||
print_error(vformat("Engine version: %s (%s)", GODOT_VERSION_FULL_NAME, GODOT_VERSION_HASH));
|
||||
}
|
||||
print_error(vformat("Dumping the backtrace. %s", msg));
|
||||
|
||||
String _execpath = OS::get_singleton()->get_executable_path();
|
||||
|
||||
// Load process and image info to determine ASLR addresses offset.
|
||||
MODULEINFO mi;
|
||||
GetModuleInformation(GetCurrentProcess(), GetModuleHandle(nullptr), &mi, sizeof(mi));
|
||||
int64_t image_mem_base = reinterpret_cast<int64_t>(mi.lpBaseOfDll);
|
||||
int64_t image_file_base = get_image_base(_execpath);
|
||||
data.offset = image_mem_base - image_file_base;
|
||||
|
||||
if (FileAccess::exists(_execpath + ".debugsymbols")) {
|
||||
_execpath = _execpath + ".debugsymbols";
|
||||
}
|
||||
_execpath = _execpath.replace_char('/', '\\');
|
||||
|
||||
CharString cs = _execpath.utf8(); // Note: should remain in scope during backtrace_simple call.
|
||||
data.state = backtrace_create_state(cs.get_data(), 0, &error_callback, reinterpret_cast<void *>(&data));
|
||||
if (data.state != nullptr) {
|
||||
data.index = 1;
|
||||
backtrace_simple(data.state, 1, &trace_callback, &error_callback, reinterpret_cast<void *>(&data));
|
||||
}
|
||||
|
||||
print_error("-- END OF C++ BACKTRACE --");
|
||||
print_error("================================================================");
|
||||
|
||||
for (const Ref<ScriptBacktrace> &backtrace : ScriptServer::capture_script_backtraces(false)) {
|
||||
if (!backtrace->is_empty()) {
|
||||
print_error(backtrace->format());
|
||||
print_error(vformat("-- END OF %s BACKTRACE --", backtrace->get_language_name().to_upper()));
|
||||
print_error("================================================================");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
CrashHandler::CrashHandler() {
|
||||
disabled = false;
|
||||
}
|
||||
|
||||
CrashHandler::~CrashHandler() {
|
||||
}
|
||||
|
||||
void CrashHandler::disable() {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(CRASH_HANDLER_EXCEPTION)
|
||||
signal(SIGSEGV, nullptr);
|
||||
signal(SIGFPE, nullptr);
|
||||
signal(SIGILL, nullptr);
|
||||
#endif
|
||||
|
||||
disabled = true;
|
||||
}
|
||||
|
||||
void CrashHandler::initialize() {
|
||||
#if defined(CRASH_HANDLER_EXCEPTION)
|
||||
signal(SIGSEGV, CrashHandlerException);
|
||||
signal(SIGFPE, CrashHandlerException);
|
||||
signal(SIGILL, CrashHandlerException);
|
||||
#endif
|
||||
}
|
936
platform/windows/detect.py
Normal file
936
platform/windows/detect.py
Normal file
@@ -0,0 +1,936 @@
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import methods
|
||||
from methods import print_error, print_warning
|
||||
from platform_methods import detect_arch, validate_arch
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from SCons.Script.SConscript import SConsEnvironment
|
||||
|
||||
# To match other platforms
|
||||
STACK_SIZE = 8388608
|
||||
STACK_SIZE_SANITIZERS = 30 * 1024 * 1024
|
||||
|
||||
|
||||
def get_name():
|
||||
return "Windows"
|
||||
|
||||
|
||||
def try_cmd(test, prefix, arch, check_clang=False):
|
||||
archs = ["x86_64", "x86_32", "arm64", "arm32"]
|
||||
if arch:
|
||||
archs = [arch]
|
||||
|
||||
for a in archs:
|
||||
try:
|
||||
out = subprocess.Popen(
|
||||
get_mingw_bin_prefix(prefix, a) + test,
|
||||
shell=True,
|
||||
stderr=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
)
|
||||
outs, errs = out.communicate()
|
||||
if out.returncode == 0:
|
||||
if check_clang and not outs.startswith(b"clang"):
|
||||
return False
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def can_build():
|
||||
if os.name == "nt":
|
||||
# Building natively on Windows
|
||||
return True
|
||||
|
||||
if os.name == "posix":
|
||||
# Cross-compiling with MinGW-w64 (old MinGW32 is not supported)
|
||||
prefix = os.getenv("MINGW_PREFIX", "")
|
||||
|
||||
if try_cmd("gcc --version", prefix, "") or try_cmd("clang --version", prefix, ""):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_mingw_bin_prefix(prefix, arch):
|
||||
bin_prefix = (os.path.normpath(os.path.join(prefix, "bin")) + os.sep) if prefix else ""
|
||||
ARCH_PREFIXES = {
|
||||
"x86_64": "x86_64-w64-mingw32-",
|
||||
"x86_32": "i686-w64-mingw32-",
|
||||
"arm32": "armv7-w64-mingw32-",
|
||||
"arm64": "aarch64-w64-mingw32-",
|
||||
}
|
||||
arch_prefix = ARCH_PREFIXES[arch] if arch else ""
|
||||
return bin_prefix + arch_prefix
|
||||
|
||||
|
||||
def get_detected(env: "SConsEnvironment", tool: str) -> str:
|
||||
checks = [
|
||||
get_mingw_bin_prefix(env["mingw_prefix"], env["arch"]) + tool,
|
||||
get_mingw_bin_prefix(env["mingw_prefix"], "") + tool,
|
||||
]
|
||||
return str(env.Detect(checks))
|
||||
|
||||
|
||||
def detect_build_env_arch():
|
||||
msvc_target_aliases = {
|
||||
"amd64": "x86_64",
|
||||
"i386": "x86_32",
|
||||
"i486": "x86_32",
|
||||
"i586": "x86_32",
|
||||
"i686": "x86_32",
|
||||
"x86": "x86_32",
|
||||
"x64": "x86_64",
|
||||
"x86_64": "x86_64",
|
||||
"arm": "arm32",
|
||||
"arm64": "arm64",
|
||||
"aarch64": "arm64",
|
||||
}
|
||||
if os.getenv("VCINSTALLDIR") or os.getenv("VCTOOLSINSTALLDIR"):
|
||||
if os.getenv("Platform"):
|
||||
msvc_arch = os.getenv("Platform").lower()
|
||||
if msvc_arch in msvc_target_aliases.keys():
|
||||
return msvc_target_aliases[msvc_arch]
|
||||
|
||||
if os.getenv("VSCMD_ARG_TGT_ARCH"):
|
||||
msvc_arch = os.getenv("VSCMD_ARG_TGT_ARCH").lower()
|
||||
if msvc_arch in msvc_target_aliases.keys():
|
||||
return msvc_target_aliases[msvc_arch]
|
||||
|
||||
# Pre VS 2017 checks.
|
||||
if os.getenv("VCINSTALLDIR"):
|
||||
PATH = os.getenv("PATH").upper()
|
||||
VCINSTALLDIR = os.getenv("VCINSTALLDIR").upper()
|
||||
path_arch = {
|
||||
"BIN\\x86_ARM;": "arm32",
|
||||
"BIN\\amd64_ARM;": "arm32",
|
||||
"BIN\\x86_ARM64;": "arm64",
|
||||
"BIN\\amd64_ARM64;": "arm64",
|
||||
"BIN\\x86_amd64;": "a86_64",
|
||||
"BIN\\amd64;": "x86_64",
|
||||
"BIN\\amd64_x86;": "x86_32",
|
||||
"BIN;": "x86_32",
|
||||
}
|
||||
for path, arch in path_arch.items():
|
||||
final_path = VCINSTALLDIR + path
|
||||
if final_path in PATH:
|
||||
return arch
|
||||
|
||||
# VS 2017 and newer.
|
||||
if os.getenv("VCTOOLSINSTALLDIR"):
|
||||
host_path_index = os.getenv("PATH").upper().find(os.getenv("VCTOOLSINSTALLDIR").upper() + "BIN\\HOST")
|
||||
if host_path_index > -1:
|
||||
first_path_arch = os.getenv("PATH")[host_path_index:].split(";")[0].rsplit("\\", 1)[-1].lower()
|
||||
if first_path_arch in msvc_target_aliases.keys():
|
||||
return msvc_target_aliases[first_path_arch]
|
||||
|
||||
msys_target_aliases = {
|
||||
"mingw32": "x86_32",
|
||||
"mingw64": "x86_64",
|
||||
"ucrt64": "x86_64",
|
||||
"clang64": "x86_64",
|
||||
"clang32": "x86_32",
|
||||
"clangarm64": "arm64",
|
||||
}
|
||||
if os.getenv("MSYSTEM"):
|
||||
msys_arch = os.getenv("MSYSTEM").lower()
|
||||
if msys_arch in msys_target_aliases.keys():
|
||||
return msys_target_aliases[msys_arch]
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
def get_tools(env: "SConsEnvironment"):
|
||||
from SCons.Tool.MSCommon import msvc_exists
|
||||
|
||||
if os.name != "nt" or env.get("use_mingw") or not msvc_exists():
|
||||
return ["mingw"]
|
||||
else:
|
||||
msvc_arch_aliases = {"x86_32": "x86", "arm32": "arm"}
|
||||
env["TARGET_ARCH"] = msvc_arch_aliases.get(env["arch"], env["arch"])
|
||||
env["MSVC_VERSION"] = env["MSVS_VERSION"] = env.get("msvc_version")
|
||||
return ["msvc", "mslink", "mslib"]
|
||||
|
||||
|
||||
def get_opts():
|
||||
from SCons.Variables import BoolVariable, EnumVariable
|
||||
|
||||
mingw = os.getenv("MINGW_PREFIX", "")
|
||||
|
||||
# Direct3D 12 SDK dependencies folder.
|
||||
d3d12_deps_folder = os.getenv("LOCALAPPDATA")
|
||||
if d3d12_deps_folder:
|
||||
d3d12_deps_folder = os.path.join(d3d12_deps_folder, "Godot", "build_deps")
|
||||
else:
|
||||
# Cross-compiling, the deps install script puts things in `bin`.
|
||||
# Getting an absolute path to it is a bit hacky in Python.
|
||||
try:
|
||||
import inspect
|
||||
|
||||
caller_frame = inspect.stack()[1]
|
||||
caller_script_dir = os.path.dirname(os.path.abspath(caller_frame[1]))
|
||||
d3d12_deps_folder = os.path.join(caller_script_dir, "bin", "build_deps")
|
||||
except Exception: # Give up.
|
||||
d3d12_deps_folder = ""
|
||||
|
||||
return [
|
||||
("mingw_prefix", "MinGW prefix", mingw),
|
||||
EnumVariable("windows_subsystem", "Windows subsystem", "gui", ["gui", "console"], ignorecase=2),
|
||||
("msvc_version", "MSVC version to use. Handled automatically by SCons if omitted.", ""),
|
||||
BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed.", False),
|
||||
BoolVariable("use_llvm", "Use the LLVM compiler", False),
|
||||
BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True),
|
||||
BoolVariable("use_asan", "Use address sanitizer (ASAN)", False),
|
||||
BoolVariable("use_ubsan", "Use LLVM compiler undefined behavior sanitizer (UBSAN)", False),
|
||||
BoolVariable("debug_crt", "Compile with MSVC's debug CRT (/MDd)", False),
|
||||
BoolVariable("incremental_link", "Use MSVC incremental linking. May increase or decrease build times.", False),
|
||||
BoolVariable("silence_msvc", "Silence MSVC's cl/link stdout bloat, redirecting any errors to stderr.", True),
|
||||
("angle_libs", "Path to the ANGLE static libraries", ""),
|
||||
# Direct3D 12 support.
|
||||
(
|
||||
"mesa_libs",
|
||||
"Path to the MESA/NIR static libraries (required for D3D12)",
|
||||
os.path.join(d3d12_deps_folder, "mesa"),
|
||||
),
|
||||
(
|
||||
"agility_sdk_path",
|
||||
"Path to the Agility SDK distribution (optional for D3D12)",
|
||||
os.path.join(d3d12_deps_folder, "agility_sdk"),
|
||||
),
|
||||
BoolVariable(
|
||||
"agility_sdk_multiarch",
|
||||
"Whether the Agility SDK DLLs will be stored in arch-specific subdirectories",
|
||||
False,
|
||||
),
|
||||
BoolVariable("use_pix", "Use PIX (Performance tuning and debugging for DirectX 12) runtime", False),
|
||||
(
|
||||
"pix_path",
|
||||
"Path to the PIX runtime distribution (optional for D3D12)",
|
||||
os.path.join(d3d12_deps_folder, "pix"),
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def get_doc_classes():
|
||||
return [
|
||||
"EditorExportPlatformWindows",
|
||||
]
|
||||
|
||||
|
||||
def get_doc_path():
|
||||
return "doc_classes"
|
||||
|
||||
|
||||
def get_flags():
|
||||
arch = detect_build_env_arch() or detect_arch()
|
||||
|
||||
return {
|
||||
"arch": arch,
|
||||
"supported": ["d3d12", "dcomp", "mono", "xaudio2"],
|
||||
}
|
||||
|
||||
|
||||
def configure_msvc(env: "SConsEnvironment"):
|
||||
"""Configure env to work with MSVC"""
|
||||
|
||||
## Build type
|
||||
|
||||
# TODO: Re-evaluate the need for this / streamline with common config.
|
||||
if env["target"] == "template_release":
|
||||
env.Append(LINKFLAGS=["/ENTRY:mainCRTStartup"])
|
||||
|
||||
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
|
||||
|
||||
if env["use_llvm"]:
|
||||
env["CC"] = "clang-cl"
|
||||
env["CXX"] = "clang-cl"
|
||||
env["LINK"] = "lld-link"
|
||||
env["AR"] = "llvm-lib"
|
||||
|
||||
env.AppendUnique(CPPDEFINES=["R128_STDC_ONLY"])
|
||||
env.extra_suffix = ".llvm" + env.extra_suffix
|
||||
|
||||
# Ensure intellisense tools like `compile_commands.json` play nice with MSVC syntax.
|
||||
env["CPPDEFPREFIX"] = "-D"
|
||||
env["INCPREFIX"] = "-I"
|
||||
env.AppendUnique(CPPDEFINES=[("alloca", "_alloca")])
|
||||
|
||||
if env["silence_msvc"] and not env.GetOption("clean"):
|
||||
from tempfile import mkstemp
|
||||
|
||||
# Ensure we have a location to write captured output to, in case of false positives.
|
||||
capture_path = methods.base_folder / "platform" / "windows" / "msvc_capture.log"
|
||||
with open(capture_path, "wt", encoding="utf-8"):
|
||||
pass
|
||||
|
||||
old_spawn = env["SPAWN"]
|
||||
re_redirect_stream = re.compile(r"^[12]?>")
|
||||
re_cl_capture = re.compile(r"^.+\.(c|cc|cpp|cxx|c[+]{2})$", re.IGNORECASE)
|
||||
re_link_capture = re.compile(r'\s{3}\S.+\s(?:"[^"]+.lib"|\S+.lib)\s.+\s(?:"[^"]+.exp"|\S+.exp)')
|
||||
|
||||
def spawn_capture(sh, escape, cmd, args, env):
|
||||
# We only care about cl/link, process everything else as normal.
|
||||
if args[0] not in ["cl", "link"]:
|
||||
return old_spawn(sh, escape, cmd, args, env)
|
||||
|
||||
# Process as normal if the user is manually rerouting output.
|
||||
for arg in args:
|
||||
if re_redirect_stream.match(arg):
|
||||
return old_spawn(sh, escape, cmd, args, env)
|
||||
|
||||
tmp_stdout, tmp_stdout_name = mkstemp()
|
||||
os.close(tmp_stdout)
|
||||
args.append(f">{tmp_stdout_name}")
|
||||
ret = old_spawn(sh, escape, cmd, args, env)
|
||||
|
||||
try:
|
||||
with open(tmp_stdout_name, "r", encoding=sys.stdout.encoding, errors="replace") as tmp_stdout:
|
||||
lines = tmp_stdout.read().splitlines()
|
||||
os.remove(tmp_stdout_name)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
# Early process no lines (OSError)
|
||||
if not lines:
|
||||
return ret
|
||||
|
||||
is_cl = args[0] == "cl"
|
||||
content = ""
|
||||
caught = False
|
||||
for line in lines:
|
||||
# These conditions are far from all-encompassing, but are specialized
|
||||
# for what can be reasonably expected to show up in the repository.
|
||||
if not caught and (is_cl and re_cl_capture.match(line)) or (not is_cl and re_link_capture.match(line)):
|
||||
caught = True
|
||||
try:
|
||||
with open(capture_path, "a", encoding=sys.stdout.encoding) as log:
|
||||
log.write(line + "\n")
|
||||
except OSError:
|
||||
print_warning(f'Failed to log captured line: "{line}".')
|
||||
continue
|
||||
content += line + "\n"
|
||||
# Content remaining assumed to be an error/warning.
|
||||
if content:
|
||||
sys.stderr.write(content)
|
||||
|
||||
return ret
|
||||
|
||||
env["SPAWN"] = spawn_capture
|
||||
|
||||
if env["debug_crt"]:
|
||||
# Always use dynamic runtime, static debug CRT breaks thread_local.
|
||||
env.AppendUnique(CCFLAGS=["/MDd"])
|
||||
else:
|
||||
if env["use_static_cpp"]:
|
||||
env.AppendUnique(CCFLAGS=["/MT"])
|
||||
else:
|
||||
env.AppendUnique(CCFLAGS=["/MD"])
|
||||
|
||||
# MSVC incremental linking is broken and may _increase_ link time (GH-77968).
|
||||
if not env["incremental_link"]:
|
||||
env.Append(LINKFLAGS=["/INCREMENTAL:NO"])
|
||||
|
||||
if env["arch"] == "x86_32":
|
||||
env["x86_libtheora_opt_vc"] = True
|
||||
|
||||
env.Append(CCFLAGS=["/fp:strict"])
|
||||
|
||||
env.AppendUnique(CCFLAGS=["/Gd", "/GR", "/nologo"])
|
||||
env.AppendUnique(CCFLAGS=["/utf-8"]) # Force to use Unicode encoding.
|
||||
# Once it was thought that only debug builds would be too large,
|
||||
# but this has recently stopped being true. See the mingw function
|
||||
# for notes on why this shouldn't be enabled for gcc
|
||||
env.AppendUnique(CCFLAGS=["/bigobj"])
|
||||
|
||||
env.AppendUnique(
|
||||
CPPDEFINES=[
|
||||
"WINDOWS_ENABLED",
|
||||
"WASAPI_ENABLED",
|
||||
"WINMIDI_ENABLED",
|
||||
"TYPED_METHOD_BIND",
|
||||
"WIN32",
|
||||
"WINVER=0x0A00",
|
||||
"_WIN32_WINNT=0x0A00",
|
||||
]
|
||||
)
|
||||
env.AppendUnique(CPPDEFINES=["NOMINMAX"]) # disable bogus min/max WinDef.h macros
|
||||
if env["arch"] == "x86_64":
|
||||
env.AppendUnique(CPPDEFINES=["_WIN64"])
|
||||
|
||||
# Sanitizers
|
||||
prebuilt_lib_extra_suffix = ""
|
||||
if env["use_asan"]:
|
||||
env.extra_suffix += ".san"
|
||||
prebuilt_lib_extra_suffix = ".san"
|
||||
env.AppendUnique(CPPDEFINES=["SANITIZERS_ENABLED"])
|
||||
env.Append(CCFLAGS=["/fsanitize=address"])
|
||||
env.Append(LINKFLAGS=["/INFERASANLIBS"])
|
||||
|
||||
## Libs
|
||||
|
||||
LIBS = [
|
||||
"winmm",
|
||||
"dsound",
|
||||
"kernel32",
|
||||
"ole32",
|
||||
"oleaut32",
|
||||
"sapi",
|
||||
"user32",
|
||||
"gdi32",
|
||||
"IPHLPAPI",
|
||||
"Shlwapi",
|
||||
"Shcore",
|
||||
"wsock32",
|
||||
"Ws2_32",
|
||||
"shell32",
|
||||
"advapi32",
|
||||
"dinput8",
|
||||
"dxguid",
|
||||
"imm32",
|
||||
"bcrypt",
|
||||
"Crypt32",
|
||||
"Avrt",
|
||||
"dwmapi",
|
||||
"dwrite",
|
||||
"wbemuuid",
|
||||
"ntdll",
|
||||
]
|
||||
|
||||
if env.debug_features:
|
||||
LIBS += ["psapi", "dbghelp"]
|
||||
|
||||
if env["accesskit"]:
|
||||
if env["accesskit_sdk_path"] != "":
|
||||
env.Prepend(CPPPATH=[env["accesskit_sdk_path"] + "/include"])
|
||||
if env["arch"] == "arm64":
|
||||
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/arm64/msvc/static"])
|
||||
elif env["arch"] == "x86_64":
|
||||
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/x86_64/msvc/static"])
|
||||
elif env["arch"] == "x86_32":
|
||||
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/x86/msvc/static"])
|
||||
LIBS += [
|
||||
"accesskit",
|
||||
"uiautomationcore",
|
||||
"runtimeobject",
|
||||
"propsys",
|
||||
"oleaut32",
|
||||
"user32",
|
||||
"userenv",
|
||||
"ntdll",
|
||||
]
|
||||
else:
|
||||
env.Append(CPPDEFINES=["ACCESSKIT_DYNAMIC"])
|
||||
env.Append(CPPDEFINES=["ACCESSKIT_ENABLED"])
|
||||
|
||||
if env["vulkan"]:
|
||||
env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
|
||||
if not env["use_volk"]:
|
||||
LIBS += ["vulkan"]
|
||||
|
||||
if env["sdl"]:
|
||||
env.Append(CPPDEFINES=["SDL_ENABLED"])
|
||||
|
||||
if env["d3d12"]:
|
||||
check_d3d12_installed(env, env["arch"] + "-msvc")
|
||||
|
||||
env.AppendUnique(CPPDEFINES=["D3D12_ENABLED", "RD_ENABLED"])
|
||||
LIBS += ["dxgi", "dxguid"]
|
||||
LIBS += ["version"] # Mesa dependency.
|
||||
|
||||
# Needed for avoiding C1128.
|
||||
if env["target"] == "release_debug":
|
||||
env.Append(CXXFLAGS=["/bigobj"])
|
||||
|
||||
# PIX
|
||||
if env["arch"] not in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]):
|
||||
env["use_pix"] = False
|
||||
|
||||
if env["use_pix"]:
|
||||
arch_subdir = "arm64" if env["arch"] == "arm64" else "x64"
|
||||
|
||||
env.Append(LIBPATH=[env["pix_path"] + "/bin/" + arch_subdir])
|
||||
LIBS += ["WinPixEventRuntime"]
|
||||
|
||||
if os.path.exists(env["mesa_libs"] + "-" + env["arch"] + "-msvc"):
|
||||
env.Append(LIBPATH=[env["mesa_libs"] + "-" + env["arch"] + "-msvc/bin"])
|
||||
else:
|
||||
env.Append(LIBPATH=[env["mesa_libs"] + "/bin"])
|
||||
LIBS += ["libNIR.windows." + env["arch"] + prebuilt_lib_extra_suffix]
|
||||
|
||||
if env["opengl3"]:
|
||||
env.AppendUnique(CPPDEFINES=["GLES3_ENABLED"])
|
||||
if env["angle_libs"] != "":
|
||||
env.AppendUnique(CPPDEFINES=["EGL_STATIC"])
|
||||
env.Append(LIBPATH=[env["angle_libs"]])
|
||||
LIBS += [
|
||||
"libANGLE.windows." + env["arch"] + prebuilt_lib_extra_suffix,
|
||||
"libEGL.windows." + env["arch"] + prebuilt_lib_extra_suffix,
|
||||
"libGLES.windows." + env["arch"] + prebuilt_lib_extra_suffix,
|
||||
]
|
||||
LIBS += ["dxgi", "d3d9", "d3d11"]
|
||||
env.Prepend(CPPEXTPATH=["#thirdparty/angle/include"])
|
||||
|
||||
if env["target"] in ["editor", "template_debug"]:
|
||||
LIBS += ["psapi", "dbghelp"]
|
||||
|
||||
if env["use_llvm"]:
|
||||
LIBS += [f"clang_rt.builtins-{env['arch']}"]
|
||||
|
||||
env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS])
|
||||
|
||||
## LTO
|
||||
|
||||
if env["lto"] == "auto": # No LTO by default for MSVC, doesn't help.
|
||||
env["lto"] = "none"
|
||||
|
||||
if env["lto"] != "none":
|
||||
if env["lto"] == "thin":
|
||||
if not env["use_llvm"]:
|
||||
print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
|
||||
sys.exit(255)
|
||||
|
||||
env.AppendUnique(CCFLAGS=["-flto=thin"])
|
||||
elif env["use_llvm"]:
|
||||
env.AppendUnique(CCFLAGS=["-flto"])
|
||||
else:
|
||||
env.AppendUnique(CCFLAGS=["/GL"])
|
||||
if env["progress"]:
|
||||
env.AppendUnique(LINKFLAGS=["/LTCG:STATUS"])
|
||||
else:
|
||||
env.AppendUnique(LINKFLAGS=["/LTCG"])
|
||||
env.AppendUnique(ARFLAGS=["/LTCG"])
|
||||
|
||||
env.Append(LINKFLAGS=["/NATVIS:platform\\windows\\godot.natvis"])
|
||||
|
||||
if env["use_asan"]:
|
||||
env.AppendUnique(LINKFLAGS=["/STACK:" + str(STACK_SIZE_SANITIZERS)])
|
||||
else:
|
||||
env.AppendUnique(LINKFLAGS=["/STACK:" + str(STACK_SIZE)])
|
||||
|
||||
|
||||
def get_ar_version(env):
|
||||
ret = {
|
||||
"major": -1,
|
||||
"minor": -1,
|
||||
"patch": -1,
|
||||
"is_llvm": False,
|
||||
}
|
||||
try:
|
||||
output = (
|
||||
subprocess.check_output([env.subst(env["AR"]), "--version"], shell=(os.name == "nt"))
|
||||
.strip()
|
||||
.decode("utf-8")
|
||||
)
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
print_warning("Couldn't check version of `ar`.")
|
||||
return ret
|
||||
|
||||
match = re.search(r"GNU ar(?: \(GNU Binutils\)| version) (\d+)\.(\d+)(?:\.(\d+))?", output)
|
||||
if match:
|
||||
ret["major"] = int(match[1])
|
||||
ret["minor"] = int(match[2])
|
||||
if match[3]:
|
||||
ret["patch"] = int(match[3])
|
||||
else:
|
||||
ret["patch"] = 0
|
||||
return ret
|
||||
|
||||
match = re.search(r"LLVM version (\d+)\.(\d+)\.(\d+)", output)
|
||||
if match:
|
||||
ret["major"] = int(match[1])
|
||||
ret["minor"] = int(match[2])
|
||||
ret["patch"] = int(match[3])
|
||||
ret["is_llvm"] = True
|
||||
return ret
|
||||
|
||||
print_warning("Couldn't parse version of `ar`.")
|
||||
return ret
|
||||
|
||||
|
||||
def get_is_ar_thin_supported(env):
|
||||
"""Check whether `ar --thin` is supported. It is only supported since Binutils 2.38 or LLVM 14."""
|
||||
ar_version = get_ar_version(env)
|
||||
if ar_version["major"] == -1:
|
||||
return False
|
||||
|
||||
if ar_version["is_llvm"]:
|
||||
return ar_version["major"] >= 14
|
||||
|
||||
if ar_version["major"] == 2:
|
||||
return ar_version["minor"] >= 38
|
||||
|
||||
print_warning("Unknown Binutils `ar` version.")
|
||||
return False
|
||||
|
||||
|
||||
WINPATHSEP_RE = re.compile(r"\\([^\"'\\]|$)")
|
||||
|
||||
|
||||
def tempfile_arg_esc_func(arg):
|
||||
from SCons.Subst import quote_spaces
|
||||
|
||||
arg = quote_spaces(arg)
|
||||
# GCC requires double Windows slashes, let's use UNIX separator
|
||||
return WINPATHSEP_RE.sub(r"/\1", arg)
|
||||
|
||||
|
||||
def configure_mingw(env: "SConsEnvironment"):
|
||||
if os.getenv("MSYSTEM") == "MSYS":
|
||||
print_error(
|
||||
"Running from base MSYS2 console/environment, use target specific environment instead (e.g., mingw32, mingw64, clang32, clang64)."
|
||||
)
|
||||
sys.exit(255)
|
||||
|
||||
if (env_arch := detect_build_env_arch()) and env["arch"] != env_arch:
|
||||
print_error(
|
||||
f"Arch argument ({env['arch']}) is not matching MSYS2 console/environment that is being used to run SCons ({env_arch}).\n"
|
||||
"Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSYS2 compiler will be executed and inform you."
|
||||
)
|
||||
sys.exit(255)
|
||||
|
||||
if not try_cmd("gcc --version", env["mingw_prefix"], env["arch"]) and not try_cmd(
|
||||
"clang --version", env["mingw_prefix"], env["arch"]
|
||||
):
|
||||
print_error("No valid compilers found, use MINGW_PREFIX environment variable to set MinGW path.")
|
||||
sys.exit(255)
|
||||
|
||||
# Workaround for MinGW. See:
|
||||
# https://www.scons.org/wiki/LongCmdLinesOnWin32
|
||||
env.use_windows_spawn_fix()
|
||||
|
||||
# HACK: For some reason, Windows-native shells have their MinGW tools
|
||||
# frequently fail as a result of parsing path separators incorrectly.
|
||||
# For some other reason, this issue is circumvented entirely if the
|
||||
# `mingw_prefix` bin is prepended to PATH.
|
||||
if os.sep == "\\":
|
||||
env.PrependENVPath("PATH", os.path.join(env["mingw_prefix"], "bin"))
|
||||
|
||||
# In case the command line to AR is too long, use a response file.
|
||||
env["ARCOM_ORIG"] = env["ARCOM"]
|
||||
env["ARCOM"] = "${TEMPFILE('$ARCOM_ORIG', '$ARCOMSTR')}"
|
||||
env["TEMPFILESUFFIX"] = ".rsp"
|
||||
if os.name == "nt":
|
||||
env["TEMPFILEARGESCFUNC"] = tempfile_arg_esc_func
|
||||
|
||||
## Build type
|
||||
|
||||
if not env["use_llvm"] and not try_cmd("gcc --version", env["mingw_prefix"], env["arch"]):
|
||||
env["use_llvm"] = True
|
||||
|
||||
if env["use_llvm"] and not try_cmd("clang --version", env["mingw_prefix"], env["arch"]):
|
||||
env["use_llvm"] = False
|
||||
|
||||
if not env["use_llvm"] and try_cmd("gcc --version", env["mingw_prefix"], env["arch"], True):
|
||||
print("Detected GCC to be a wrapper for Clang.")
|
||||
env["use_llvm"] = True
|
||||
|
||||
if env.dev_build:
|
||||
# Allow big objects. It's supposed not to have drawbacks but seems to break
|
||||
# GCC LTO, so enabling for debug builds only (which are not built with LTO
|
||||
# and are the only ones with too big objects).
|
||||
env.Append(CCFLAGS=["-Wa,-mbig-obj"])
|
||||
|
||||
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 env["arch"] == "x86_32":
|
||||
if env["use_static_cpp"]:
|
||||
env.Append(LINKFLAGS=["-static"])
|
||||
env.Append(LINKFLAGS=["-static-libgcc"])
|
||||
env.Append(LINKFLAGS=["-static-libstdc++"])
|
||||
else:
|
||||
if env["use_static_cpp"]:
|
||||
env.Append(LINKFLAGS=["-static"])
|
||||
|
||||
if env["arch"] == "x86_32":
|
||||
env["x86_libtheora_opt_gcc"] = True
|
||||
|
||||
env.Append(CCFLAGS=["-ffp-contract=off"])
|
||||
|
||||
if env["use_llvm"]:
|
||||
env["CC"] = get_detected(env, "clang")
|
||||
env["CXX"] = get_detected(env, "clang++")
|
||||
env["AR"] = get_detected(env, "ar")
|
||||
env["RANLIB"] = get_detected(env, "ranlib")
|
||||
env["AS"] = get_detected(env, "clang")
|
||||
env.Append(ASFLAGS=["-c"])
|
||||
env.extra_suffix = ".llvm" + env.extra_suffix
|
||||
else:
|
||||
env["CC"] = get_detected(env, "gcc")
|
||||
env["CXX"] = get_detected(env, "g++")
|
||||
env["AR"] = get_detected(env, "gcc-ar" if os.name != "nt" else "ar")
|
||||
env["RANLIB"] = get_detected(env, "gcc-ranlib")
|
||||
env["AS"] = get_detected(env, "gcc")
|
||||
env.Append(ASFLAGS=["-c"])
|
||||
|
||||
env["RC"] = get_detected(env, "windres")
|
||||
ARCH_TARGETS = {
|
||||
"x86_32": "pe-i386",
|
||||
"x86_64": "pe-x86-64",
|
||||
"arm32": "armv7-w64-mingw32",
|
||||
"arm64": "aarch64-w64-mingw32",
|
||||
}
|
||||
env.AppendUnique(RCFLAGS=f"--target={ARCH_TARGETS[env['arch']]}")
|
||||
|
||||
env["OBJCOPY"] = get_detected(env, "objcopy")
|
||||
env["STRIP"] = get_detected(env, "strip")
|
||||
|
||||
## LTO
|
||||
|
||||
if env["lto"] == "auto": # Enable LTO for production with MinGW.
|
||||
env["lto"] = "thin" if env["use_llvm"] else "full"
|
||||
|
||||
if env["lto"] != "none":
|
||||
if env["lto"] == "thin":
|
||||
if not env["use_llvm"]:
|
||||
print("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
|
||||
sys.exit(255)
|
||||
env.Append(CCFLAGS=["-flto=thin"])
|
||||
env.Append(LINKFLAGS=["-flto=thin"])
|
||||
elif not env["use_llvm"] and env.GetOption("num_jobs") > 1:
|
||||
env.Append(CCFLAGS=["-flto"])
|
||||
env.Append(LINKFLAGS=["-flto=" + str(env.GetOption("num_jobs"))])
|
||||
else:
|
||||
env.Append(CCFLAGS=["-flto"])
|
||||
env.Append(LINKFLAGS=["-flto"])
|
||||
if not env["use_llvm"]:
|
||||
# For mingw-gcc LTO, disable linker plugin and enable whole program to work around GH-102867.
|
||||
env.Append(CCFLAGS=["-fno-use-linker-plugin", "-fwhole-program"])
|
||||
env.Append(LINKFLAGS=["-fno-use-linker-plugin", "-fwhole-program"])
|
||||
|
||||
if env["use_asan"]:
|
||||
env.Append(LINKFLAGS=["-Wl,--stack," + str(STACK_SIZE_SANITIZERS)])
|
||||
else:
|
||||
env.Append(LINKFLAGS=["-Wl,--stack," + str(STACK_SIZE)])
|
||||
|
||||
## Compile flags
|
||||
|
||||
if not env["use_llvm"]:
|
||||
env.Append(CCFLAGS=["-mwindows"])
|
||||
|
||||
if env["use_asan"] or env["use_ubsan"]:
|
||||
if not env["use_llvm"]:
|
||||
print("GCC does not support sanitizers on Windows.")
|
||||
sys.exit(255)
|
||||
if env["arch"] not in ["x86_32", "x86_64"]:
|
||||
print("Sanitizers are only supported for x86_32 and x86_64.")
|
||||
sys.exit(255)
|
||||
|
||||
env.extra_suffix += ".san"
|
||||
env.AppendUnique(CPPDEFINES=["SANITIZERS_ENABLED"])
|
||||
san_flags = []
|
||||
if env["use_asan"]:
|
||||
san_flags.append("-fsanitize=address")
|
||||
if env["use_ubsan"]:
|
||||
san_flags.append("-fsanitize=undefined")
|
||||
# Disable the vptr check since it gets triggered on any COM interface calls.
|
||||
san_flags.append("-fno-sanitize=vptr")
|
||||
env.Append(CFLAGS=san_flags)
|
||||
env.Append(CCFLAGS=san_flags)
|
||||
env.Append(LINKFLAGS=san_flags)
|
||||
|
||||
if get_is_ar_thin_supported(env):
|
||||
env.Append(ARFLAGS=["--thin"])
|
||||
|
||||
env.Append(CPPDEFINES=["WINDOWS_ENABLED", "WASAPI_ENABLED", "WINMIDI_ENABLED"])
|
||||
env.Append(
|
||||
CPPDEFINES=[
|
||||
"WINVER=0x0A00",
|
||||
"_WIN32_WINNT=0x0A00",
|
||||
]
|
||||
)
|
||||
env.Append(
|
||||
LIBS=[
|
||||
"mingw32",
|
||||
"dsound",
|
||||
"ole32",
|
||||
"d3d9",
|
||||
"winmm",
|
||||
"gdi32",
|
||||
"iphlpapi",
|
||||
"shell32",
|
||||
"shlwapi",
|
||||
"shcore",
|
||||
"wsock32",
|
||||
"ws2_32",
|
||||
"kernel32",
|
||||
"oleaut32",
|
||||
"sapi",
|
||||
"dinput8",
|
||||
"dxguid",
|
||||
"ksuser",
|
||||
"imm32",
|
||||
"bcrypt",
|
||||
"crypt32",
|
||||
"avrt",
|
||||
"uuid",
|
||||
"dwmapi",
|
||||
"dwrite",
|
||||
"wbemuuid",
|
||||
"ntdll",
|
||||
]
|
||||
)
|
||||
|
||||
if env["accesskit"]:
|
||||
if env["accesskit_sdk_path"] != "":
|
||||
env.Prepend(CPPPATH=[env["accesskit_sdk_path"] + "/include"])
|
||||
if env["use_llvm"]:
|
||||
if env["arch"] == "arm64":
|
||||
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/arm64/mingw-llvm/static/"])
|
||||
elif env["arch"] == "x86_64":
|
||||
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/x86_64/mingw-llvm/static/"])
|
||||
elif env["arch"] == "x86_32":
|
||||
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/x86/mingw-llvm/static/"])
|
||||
else:
|
||||
if env["arch"] == "x86_64":
|
||||
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/x86_64/mingw/static/"])
|
||||
elif env["arch"] == "x86_32":
|
||||
env.Append(LIBPATH=[env["accesskit_sdk_path"] + "/lib/windows/x86/mingw/static/"])
|
||||
env.Append(LIBPATH=["#bin/obj/platform/windows"])
|
||||
env.Append(
|
||||
LIBS=[
|
||||
"accesskit",
|
||||
"uiautomationcore." + env["arch"],
|
||||
"runtimeobject",
|
||||
"propsys",
|
||||
"oleaut32",
|
||||
"user32",
|
||||
"userenv",
|
||||
"ntdll",
|
||||
]
|
||||
)
|
||||
else:
|
||||
env.Append(CPPDEFINES=["ACCESSKIT_DYNAMIC"])
|
||||
env.Append(LIBPATH=["#platform/windows"])
|
||||
env.Append(CPPDEFINES=["ACCESSKIT_ENABLED"])
|
||||
|
||||
if env.debug_features:
|
||||
env.Append(LIBS=["psapi", "dbghelp"])
|
||||
|
||||
if env["vulkan"]:
|
||||
env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
|
||||
if not env["use_volk"]:
|
||||
env.Append(LIBS=["vulkan"])
|
||||
|
||||
if env["sdl"]:
|
||||
env.Append(CPPDEFINES=["SDL_ENABLED"])
|
||||
|
||||
if env["d3d12"]:
|
||||
if env["use_llvm"]:
|
||||
check_d3d12_installed(env, env["arch"] + "-llvm")
|
||||
else:
|
||||
check_d3d12_installed(env, env["arch"] + "-gcc")
|
||||
|
||||
env.AppendUnique(CPPDEFINES=["D3D12_ENABLED", "RD_ENABLED"])
|
||||
env.Append(LIBS=["dxgi", "dxguid"])
|
||||
|
||||
# PIX
|
||||
if env["arch"] not in ["x86_64", "arm64"] or env["pix_path"] == "" or not os.path.exists(env["pix_path"]):
|
||||
env["use_pix"] = False
|
||||
|
||||
if env["use_pix"]:
|
||||
arch_subdir = "arm64" if env["arch"] == "arm64" else "x64"
|
||||
|
||||
env.Append(LIBPATH=[env["pix_path"] + "/bin/" + arch_subdir])
|
||||
env.Append(LIBS=["WinPixEventRuntime"])
|
||||
|
||||
if env["use_llvm"] and os.path.exists(env["mesa_libs"] + "-" + env["arch"] + "-llvm"):
|
||||
env.Append(LIBPATH=[env["mesa_libs"] + "-" + env["arch"] + "-llvm/bin"])
|
||||
elif not env["use_llvm"] and os.path.exists(env["mesa_libs"] + "-" + env["arch"] + "-gcc"):
|
||||
env.Append(LIBPATH=[env["mesa_libs"] + "-" + env["arch"] + "-gcc/bin"])
|
||||
else:
|
||||
env.Append(LIBPATH=[env["mesa_libs"] + "/bin"])
|
||||
env.Append(LIBS=["libNIR.windows." + env["arch"]])
|
||||
env.Append(LIBS=["version"]) # Mesa dependency.
|
||||
|
||||
if env["opengl3"]:
|
||||
env.Append(CPPDEFINES=["GLES3_ENABLED"])
|
||||
if env["angle_libs"] != "":
|
||||
env.AppendUnique(CPPDEFINES=["EGL_STATIC"])
|
||||
env.Append(LIBPATH=[env["angle_libs"]])
|
||||
env.Append(
|
||||
LIBS=[
|
||||
"EGL.windows." + env["arch"],
|
||||
"GLES.windows." + env["arch"],
|
||||
"ANGLE.windows." + env["arch"],
|
||||
]
|
||||
)
|
||||
env.Append(LIBS=["dxgi", "d3d9", "d3d11"])
|
||||
env.Prepend(CPPEXTPATH=["#thirdparty/angle/include"])
|
||||
|
||||
env.Append(CPPDEFINES=["MINGW_ENABLED", ("MINGW_HAS_SECURE_API", 1)])
|
||||
|
||||
# dlltool
|
||||
env["DEF"] = get_detected(env, "dlltool")
|
||||
env["DEFCOM"] = "$DEF $DEFFLAGS -d $SOURCE -l $TARGET"
|
||||
env["DEFCOMSTR"] = "$CXXCOMSTR"
|
||||
env["DEFPREFIX"] = "$LIBPREFIX"
|
||||
env["DEFSUFFIX"] = ".${__env__['arch']}$LIBSUFFIX"
|
||||
env["DEFSRCSUFFIX"] = ".${__env__['arch']}.def"
|
||||
DEF_ALIASES = {
|
||||
"x86_32": "i386",
|
||||
"x86_64": "i386:x86-64",
|
||||
"arm32": "arm",
|
||||
"arm64": "arm64",
|
||||
}
|
||||
env.Append(DEFFLAGS=["-m", DEF_ALIASES[env["arch"]]])
|
||||
if env["arch"] == "x86_32":
|
||||
env.Append(DEFFLAGS=["-k"])
|
||||
else:
|
||||
env.Append(DEFFLAGS=["--no-leading-underscore"])
|
||||
|
||||
env.Append(
|
||||
BUILDERS={
|
||||
"DEFLIB": env.Builder(
|
||||
action=env.Run("$DEFCOM", "$DEFCOMSTR"),
|
||||
prefix="$DEFPREFIX",
|
||||
suffix="$DEFSUFFIX",
|
||||
src_suffix="$DEFSRCSUFFIX",
|
||||
emitter=methods.redirect_emitter,
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def configure(env: "SConsEnvironment"):
|
||||
# Validate arch.
|
||||
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
|
||||
validate_arch(env["arch"], get_name(), supported_arches)
|
||||
|
||||
# At this point the env has been set up with basic tools/compilers.
|
||||
env.Prepend(CPPPATH=["#platform/windows"])
|
||||
|
||||
env.msvc = "mingw" not in env["TOOLS"]
|
||||
if env.msvc:
|
||||
configure_msvc(env)
|
||||
else:
|
||||
configure_mingw(env)
|
||||
|
||||
|
||||
def check_d3d12_installed(env, suffix):
|
||||
if not os.path.exists(env["mesa_libs"]) and not os.path.exists(env["mesa_libs"] + "-" + suffix):
|
||||
print_error(
|
||||
"The Direct3D 12 rendering driver requires dependencies to be installed.\n"
|
||||
"You can install them by running `python misc\\scripts\\install_d3d12_sdk_windows.py`.\n"
|
||||
"See the documentation for more information:\n\t"
|
||||
"https://docs.godotengine.org/en/latest/engine_details/development/compiling/compiling_for_windows.html"
|
||||
)
|
||||
sys.exit(255)
|
7471
platform/windows/display_server_windows.cpp
Normal file
7471
platform/windows/display_server_windows.cpp
Normal file
File diff suppressed because it is too large
Load Diff
723
platform/windows/display_server_windows.h
Normal file
723
platform/windows/display_server_windows.h
Normal file
@@ -0,0 +1,723 @@
|
||||
/**************************************************************************/
|
||||
/* display_server_windows.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "crash_handler_windows.h"
|
||||
#include "key_mapping_windows.h"
|
||||
#include "tts_windows.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/input/input.h"
|
||||
#include "core/io/image.h"
|
||||
#include "core/os/os.h"
|
||||
#include "drivers/wasapi/audio_driver_wasapi.h"
|
||||
#include "drivers/winmidi/midi_driver_winmidi.h"
|
||||
#include "servers/audio_server.h"
|
||||
#include "servers/display_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(RD_ENABLED)
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
#endif
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
#include "gl_manager_windows_angle.h"
|
||||
#include "gl_manager_windows_native.h"
|
||||
#endif // GLES3_ENABLED
|
||||
|
||||
#include "native_menu_windows.h"
|
||||
|
||||
#include <io.h>
|
||||
#include <cstdio>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#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_STATUS 0x0002
|
||||
#define PK_NORMAL_PRESSURE 0x0400
|
||||
#define PK_TANGENT_PRESSURE 0x0800
|
||||
#define PK_ORIENTATION 0x1000
|
||||
|
||||
#define TPS_INVERT 0x0010 /* 1.1 */
|
||||
|
||||
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 pkStatus;
|
||||
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);
|
||||
|
||||
enum PreferredAppMode {
|
||||
APPMODE_DEFAULT = 0,
|
||||
APPMODE_ALLOWDARK = 1,
|
||||
APPMODE_FORCEDARK = 2,
|
||||
APPMODE_FORCELIGHT = 3,
|
||||
APPMODE_MAX = 4
|
||||
};
|
||||
|
||||
typedef const char *(CDECL *WineGetVersionPtr)(void);
|
||||
typedef bool(WINAPI *ShouldAppsUseDarkModePtr)();
|
||||
typedef DWORD(WINAPI *GetImmersiveColorFromColorSetExPtr)(UINT dwImmersiveColorSet, UINT dwImmersiveColorType, bool bIgnoreHighContrast, UINT dwHighContrastCacheMode);
|
||||
typedef int(WINAPI *GetImmersiveColorTypeFromNamePtr)(const WCHAR *name);
|
||||
typedef int(WINAPI *GetImmersiveUserColorSetPreferencePtr)(bool bForceCheckRegistry, bool bSkipCheckOnFail);
|
||||
typedef HRESULT(WINAPI *RtlGetVersionPtr)(OSVERSIONINFOEXW *lpVersionInformation);
|
||||
typedef bool(WINAPI *AllowDarkModeForAppPtr)(bool darkMode);
|
||||
typedef PreferredAppMode(WINAPI *SetPreferredAppModePtr)(PreferredAppMode appMode);
|
||||
typedef void(WINAPI *RefreshImmersiveColorPolicyStatePtr)();
|
||||
typedef void(WINAPI *FlushMenuThemesPtr)();
|
||||
|
||||
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 DropTargetWindows;
|
||||
|
||||
#ifndef WDA_EXCLUDEFROMCAPTURE
|
||||
#define WDA_EXCLUDEFROMCAPTURE 0x00000011
|
||||
#endif
|
||||
|
||||
class JoypadSDL;
|
||||
|
||||
class DisplayServerWindows : public DisplayServer {
|
||||
GDSOFTCLASS(DisplayServerWindows, DisplayServer);
|
||||
|
||||
friend class DropTargetWindows;
|
||||
|
||||
_THREAD_SAFE_CLASS_
|
||||
|
||||
// UXTheme API
|
||||
static bool dark_title_available;
|
||||
static bool use_legacy_dark_mode_before_20H1;
|
||||
static bool ux_theme_available;
|
||||
static ShouldAppsUseDarkModePtr ShouldAppsUseDarkMode;
|
||||
static GetImmersiveColorFromColorSetExPtr GetImmersiveColorFromColorSetEx;
|
||||
static GetImmersiveColorTypeFromNamePtr GetImmersiveColorTypeFromName;
|
||||
static GetImmersiveUserColorSetPreferencePtr GetImmersiveUserColorSetPreference;
|
||||
|
||||
// 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;
|
||||
|
||||
void _update_tablet_ctx(const String &p_old_driver, const String &p_new_driver);
|
||||
String tablet_driver;
|
||||
Vector<String> tablet_drivers;
|
||||
bool winink_disabled = false;
|
||||
|
||||
enum DriverID {
|
||||
DRIVER_ID_COMPAT_OPENGL3 = 1 << 0,
|
||||
DRIVER_ID_COMPAT_ANGLE_D3D11 = 1 << 1,
|
||||
DRIVER_ID_RD_VULKAN = 1 << 2,
|
||||
DRIVER_ID_RD_D3D12 = 1 << 3,
|
||||
};
|
||||
static BitField<DriverID> tested_drivers;
|
||||
|
||||
enum TimerID {
|
||||
TIMER_ID_MOVE_REDRAW = 1,
|
||||
TIMER_ID_WINDOW_ACTIVATION = 2,
|
||||
};
|
||||
|
||||
OSVERSIONINFOEXW os_ver;
|
||||
|
||||
enum {
|
||||
KEY_EVENT_BUFFER_SIZE = 512
|
||||
};
|
||||
|
||||
struct KeyEvent {
|
||||
WindowID window_id;
|
||||
bool alt, shift, control, meta, altgr;
|
||||
UINT uMsg;
|
||||
WPARAM wParam;
|
||||
LPARAM lParam;
|
||||
};
|
||||
|
||||
WindowID window_mouseover_id = INVALID_WINDOW_ID;
|
||||
|
||||
KeyEvent key_event_buffer[KEY_EVENT_BUFFER_SIZE];
|
||||
int key_event_pos;
|
||||
|
||||
bool old_invalid;
|
||||
int old_x, old_y;
|
||||
Point2i center;
|
||||
|
||||
#if defined(GLES3_ENABLED)
|
||||
GLManagerANGLE_Windows *gl_manager_angle = nullptr;
|
||||
GLManagerNative_Windows *gl_manager_native = nullptr;
|
||||
#endif
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
RenderingContextDriver *rendering_context = nullptr;
|
||||
RenderingDevice *rendering_device = nullptr;
|
||||
#endif
|
||||
|
||||
RBMap<int, Vector2> touch_state;
|
||||
|
||||
int pressrc;
|
||||
HINSTANCE hInstance; // Holds The Instance Of The Application
|
||||
String rendering_driver;
|
||||
bool app_focused = false;
|
||||
bool keep_screen_on = false;
|
||||
bool get_object_received = false;
|
||||
HANDLE power_request;
|
||||
|
||||
TTS_Windows *tts = nullptr;
|
||||
NativeMenuWindows *native_menu = nullptr;
|
||||
|
||||
struct WindowData {
|
||||
HWND hWnd;
|
||||
WindowID id;
|
||||
|
||||
Vector<Vector2> mpath;
|
||||
|
||||
bool create_completed = false;
|
||||
bool pre_fs_valid = false;
|
||||
RECT pre_fs_rect;
|
||||
bool maximized = false;
|
||||
bool maximized_fs = false;
|
||||
bool minimized = false;
|
||||
bool fullscreen = false;
|
||||
bool multiwindow_fs = false;
|
||||
bool borderless = false;
|
||||
bool resizable = true;
|
||||
bool no_min_btn = false;
|
||||
bool no_max_btn = false;
|
||||
bool window_focused = false;
|
||||
int activate_state = 0;
|
||||
bool was_maximized_pre_fs = false;
|
||||
bool was_fullscreen_pre_min = false;
|
||||
bool first_activation_done = false;
|
||||
bool was_maximized = false;
|
||||
bool always_on_top = false;
|
||||
bool no_focus = false;
|
||||
bool exclusive = false;
|
||||
bool context_created = false;
|
||||
bool mpass = false;
|
||||
bool sharp_corners = false;
|
||||
bool hide_from_capture = false;
|
||||
|
||||
// Used to transfer data between events using timer.
|
||||
WPARAM saved_wparam;
|
||||
LPARAM saved_lparam;
|
||||
|
||||
// Timers.
|
||||
uint32_t move_timer_id = 0U;
|
||||
uint32_t activate_timer_id = 0U;
|
||||
|
||||
HANDLE wtctx;
|
||||
LOGCONTEXTW wtlc;
|
||||
int min_pressure;
|
||||
int max_pressure;
|
||||
bool tilt_supported;
|
||||
bool pen_inverted = false;
|
||||
bool block_mm = false;
|
||||
|
||||
int last_pressure_update;
|
||||
float last_pressure;
|
||||
Vector2 last_tilt;
|
||||
bool last_pen_inverted = false;
|
||||
|
||||
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 ime_active = false;
|
||||
bool ime_in_progress = false;
|
||||
bool ime_suppress_next_keyup = false;
|
||||
|
||||
bool layered_window = false;
|
||||
|
||||
Callable rect_changed_callback;
|
||||
Callable event_callback;
|
||||
Callable input_event_callback;
|
||||
Callable input_text_callback;
|
||||
Callable drop_files_callback;
|
||||
|
||||
// OLE API
|
||||
DropTargetWindows *drop_target = nullptr;
|
||||
|
||||
WindowID transient_parent = INVALID_WINDOW_ID;
|
||||
HashSet<WindowID> transient_children;
|
||||
|
||||
bool is_popup = false;
|
||||
Rect2i parent_safe_rect;
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
HWND parent_hwnd = 0;
|
||||
};
|
||||
|
||||
#ifdef SDL_ENABLED
|
||||
JoypadSDL *joypad_sdl = nullptr;
|
||||
#endif
|
||||
HHOOK mouse_monitor = nullptr;
|
||||
List<WindowID> popup_list;
|
||||
uint64_t time_since_popup = 0;
|
||||
Ref<Image> icon;
|
||||
|
||||
WindowID _create_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect, bool p_exclusive, WindowID p_transient_parent, HWND p_parent_hwnd);
|
||||
WindowID window_id_counter = MAIN_WINDOW_ID;
|
||||
RBMap<WindowID, WindowData> windows;
|
||||
|
||||
WindowID last_focused_window = INVALID_WINDOW_ID;
|
||||
WindowID last_mouse_button_down_window = INVALID_WINDOW_ID;
|
||||
HCURSOR hCursor;
|
||||
|
||||
WNDPROC user_proc = nullptr;
|
||||
|
||||
struct IndicatorData {
|
||||
RID menu_rid;
|
||||
Callable callback;
|
||||
};
|
||||
|
||||
IndicatorID indicator_id_counter = 0;
|
||||
HashMap<IndicatorID, IndicatorData> indicators;
|
||||
|
||||
struct FileDialogData {
|
||||
HWND hwnd_owner = 0;
|
||||
Rect2i wrect;
|
||||
String appid;
|
||||
String title;
|
||||
String current_directory;
|
||||
String root;
|
||||
String filename;
|
||||
bool show_hidden = false;
|
||||
DisplayServer::FileDialogMode mode = FileDialogMode::FILE_DIALOG_MODE_OPEN_ANY;
|
||||
Vector<String> filters;
|
||||
TypedArray<Dictionary> options;
|
||||
WindowID window_id = DisplayServer::INVALID_WINDOW_ID;
|
||||
Callable callback;
|
||||
bool options_in_cb = false;
|
||||
Thread listener_thread;
|
||||
SafeFlag close_requested;
|
||||
SafeFlag finished;
|
||||
};
|
||||
Mutex file_dialog_mutex;
|
||||
List<FileDialogData *> file_dialogs;
|
||||
HashMap<HWND, FileDialogData *> file_dialog_wnd;
|
||||
struct FileDialogCallback {
|
||||
Callable callback;
|
||||
Variant status;
|
||||
Variant files;
|
||||
Variant index;
|
||||
Variant options;
|
||||
bool opt_in_cb = false;
|
||||
};
|
||||
List<FileDialogCallback> pending_cbs;
|
||||
void process_file_dialog_callbacks();
|
||||
|
||||
static void _thread_fd_monitor(void *p_ud);
|
||||
|
||||
HashMap<int64_t, MouseButton> pointer_prev_button;
|
||||
HashMap<int64_t, MouseButton> pointer_button;
|
||||
HashMap<int64_t, LONG> pointer_down_time;
|
||||
HashMap<int64_t, Vector2> pointer_last_pos;
|
||||
|
||||
void _send_window_event(const WindowData &wd, WindowEvent p_event);
|
||||
void _get_window_style(bool p_main_window, bool p_initialized, bool p_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_no_min_btn, bool p_no_max_btn, bool p_minimized, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, bool p_embed_child, DWORD &r_style, DWORD &r_style_ex);
|
||||
|
||||
MouseMode mouse_mode;
|
||||
MouseMode mouse_mode_base = MOUSE_MODE_VISIBLE;
|
||||
MouseMode mouse_mode_override = MOUSE_MODE_VISIBLE;
|
||||
bool mouse_mode_override_enabled = false;
|
||||
void _mouse_update_mode();
|
||||
int restore_mouse_trails = 0;
|
||||
|
||||
bool use_raw_input = false;
|
||||
bool drop_events = false;
|
||||
bool in_dispatch_input_event = false;
|
||||
|
||||
WNDCLASSEXW wc;
|
||||
HBRUSH window_bkg_brush = nullptr;
|
||||
uint32_t window_bkg_brush_color = 0;
|
||||
|
||||
HCURSOR cursors[CURSOR_MAX] = { nullptr };
|
||||
CursorShape cursor_shape = CursorShape::CURSOR_ARROW;
|
||||
RBMap<CursorShape, Vector<Variant>> cursors_cache;
|
||||
|
||||
Callable system_theme_changed;
|
||||
|
||||
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);
|
||||
|
||||
bool _is_always_on_top_recursive(WindowID p_window) const;
|
||||
|
||||
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);
|
||||
WindowID _get_focused_window_or_popup() const;
|
||||
void _register_raw_input_devices(WindowID p_target_window);
|
||||
bool _has_moving_window() const;
|
||||
|
||||
void _process_activate_event(WindowID p_window_id);
|
||||
void _process_key_events();
|
||||
|
||||
static void _dispatch_input_events(const Ref<InputEvent> &p_event);
|
||||
void _dispatch_input_event(const Ref<InputEvent> &p_event);
|
||||
|
||||
LRESULT _handle_early_window_message(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
Point2i _get_screens_origin() const;
|
||||
|
||||
enum class WinKeyModifierMask {
|
||||
ALT_GR = (1 << 1),
|
||||
SHIFT = (1 << 2),
|
||||
ALT = (1 << 3),
|
||||
META = (1 << 4),
|
||||
CTRL = (1 << 5),
|
||||
};
|
||||
BitField<WinKeyModifierMask> _get_mods() const;
|
||||
|
||||
Error _file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, bool p_options_in_cb, WindowID p_window_id);
|
||||
|
||||
String _get_keyboard_layout_display_name(const String &p_klid) const;
|
||||
String _get_klid(HKL p_hkl) const;
|
||||
|
||||
struct EmbeddedProcessData {
|
||||
HWND window_handle = 0;
|
||||
HWND parent_window_handle = 0;
|
||||
bool is_visible = false;
|
||||
};
|
||||
HashMap<OS::ProcessID, EmbeddedProcessData *> embedded_processes;
|
||||
|
||||
HWND _find_window_from_process_id(OS::ProcessID p_pid, HWND p_current_hwnd);
|
||||
|
||||
void initialize_tts() const;
|
||||
|
||||
public:
|
||||
LRESULT WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT MouseProc(int code, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
void popup_open(WindowID p_window);
|
||||
void popup_close(WindowID p_window);
|
||||
|
||||
virtual bool has_feature(Feature p_feature) const override;
|
||||
virtual String get_name() const override;
|
||||
|
||||
virtual bool tts_is_speaking() const override;
|
||||
virtual bool tts_is_paused() const override;
|
||||
virtual TypedArray<Dictionary> tts_get_voices() const override;
|
||||
|
||||
virtual void tts_speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int p_utterance_id = 0, bool p_interrupt = false) override;
|
||||
virtual void tts_pause() override;
|
||||
virtual void tts_resume() override;
|
||||
virtual void tts_stop() override;
|
||||
|
||||
virtual bool is_dark_mode_supported() const override;
|
||||
virtual bool is_dark_mode() const override;
|
||||
virtual Color get_accent_color() const override;
|
||||
virtual Color get_base_color() const override;
|
||||
virtual void set_system_theme_change_callback(const Callable &p_callable) override;
|
||||
|
||||
virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback, WindowID p_window_id) override;
|
||||
virtual Error file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, WindowID p_window_id) override;
|
||||
|
||||
virtual void beep() const override;
|
||||
|
||||
virtual void mouse_set_mode(MouseMode p_mode) override;
|
||||
virtual MouseMode mouse_get_mode() const override;
|
||||
virtual void mouse_set_mode_override(MouseMode p_mode) override;
|
||||
virtual MouseMode mouse_get_mode_override() const override;
|
||||
virtual void mouse_set_mode_override_enabled(bool p_override_enabled) override;
|
||||
virtual bool mouse_is_mode_override_enabled() const override;
|
||||
|
||||
virtual void warp_mouse(const Point2i &p_position) override;
|
||||
virtual Point2i mouse_get_position() const override;
|
||||
virtual BitField<MouseButtonMask> mouse_get_button_state() const override;
|
||||
|
||||
virtual void clipboard_set(const String &p_text) override;
|
||||
virtual String clipboard_get() const override;
|
||||
virtual Ref<Image> clipboard_get_image() const override;
|
||||
virtual bool clipboard_has() const override;
|
||||
virtual bool clipboard_has_image() const override;
|
||||
|
||||
virtual int get_screen_count() const override;
|
||||
virtual int get_primary_screen() const override;
|
||||
virtual int get_keyboard_focus_screen() const override;
|
||||
virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual Rect2i screen_get_usable_rect(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual float screen_get_refresh_rate(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual Color screen_get_pixel(const Point2i &p_position) const override;
|
||||
virtual Ref<Image> screen_get_image(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
|
||||
virtual Ref<Image> screen_get_image_rect(const Rect2i &p_rect) const override;
|
||||
|
||||
virtual void screen_set_keep_on(bool p_enable) override; //disable screensaver
|
||||
virtual bool screen_is_kept_on() const override;
|
||||
|
||||
virtual Vector<DisplayServer::WindowID> get_window_list() const override;
|
||||
|
||||
virtual WindowID create_sub_window(WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i(), bool p_exclusive = false, WindowID p_transient_parent = INVALID_WINDOW_ID) override;
|
||||
virtual void show_window(WindowID p_window) override;
|
||||
virtual void delete_sub_window(WindowID p_window) override;
|
||||
|
||||
virtual WindowID window_get_active_popup() const override;
|
||||
virtual void window_set_popup_safe_rect(WindowID p_window, const Rect2i &p_rect) override;
|
||||
virtual Rect2i window_get_popup_safe_rect(WindowID p_window) const override;
|
||||
|
||||
virtual int64_t window_get_native_handle(HandleType p_handle_type, WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual WindowID get_window_at_screen_position(const Point2i &p_position) const override;
|
||||
|
||||
virtual void window_attach_instance_id(ObjectID p_instance, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual ObjectID window_get_attached_instance_id(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual void gl_window_make_current(DisplayServer::WindowID p_window_id) override;
|
||||
|
||||
virtual void window_set_rect_changed_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_input_text_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual void window_set_drop_files_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_title_size(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual void window_set_mouse_passthrough(const Vector<Vector2> &p_region, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual Point2i window_get_position_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual void window_set_transient(WindowID p_window, WindowID p_parent) override;
|
||||
virtual void window_set_exclusive(WindowID p_window, bool p_exclusive) override;
|
||||
|
||||
virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
virtual Size2i window_get_size_with_decorations(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual bool window_is_focused(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual WindowID get_focused_window() const override;
|
||||
|
||||
virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override;
|
||||
|
||||
virtual bool can_any_window_draw() const override;
|
||||
|
||||
virtual void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual int accessibility_should_increase_contrast() const override;
|
||||
virtual int accessibility_should_reduce_animation() const override;
|
||||
virtual int accessibility_should_reduce_transparency() const override;
|
||||
virtual int accessibility_screen_reader_active() const override;
|
||||
|
||||
virtual Point2i ime_get_selection() const override;
|
||||
virtual String ime_get_text() const override;
|
||||
|
||||
virtual void window_set_vsync_mode(DisplayServer::VSyncMode p_vsync_mode, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual DisplayServer::VSyncMode window_get_vsync_mode(WindowID p_vsync_mode) const override;
|
||||
|
||||
virtual void window_start_drag(WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
virtual void window_start_resize(WindowResizeEdge p_edge, WindowID p_window = MAIN_WINDOW_ID) override;
|
||||
|
||||
virtual void cursor_set_shape(CursorShape p_shape) override;
|
||||
virtual CursorShape cursor_get_shape() const override;
|
||||
virtual void cursor_set_custom_image(const Ref<Resource> &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) override;
|
||||
|
||||
virtual bool get_swap_cancel_ok() override;
|
||||
|
||||
virtual void enable_for_stealing_focus(OS::ProcessID pid) override;
|
||||
virtual Error embed_process(WindowID p_window, OS::ProcessID p_pid, const Rect2i &p_rect, bool p_visible, bool p_grab_focus) override;
|
||||
virtual Error request_close_embedded_process(OS::ProcessID p_pid) override;
|
||||
virtual Error remove_embedded_process(OS::ProcessID p_pid) override;
|
||||
virtual OS::ProcessID get_focused_process_id() override;
|
||||
|
||||
virtual Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) override;
|
||||
virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) override;
|
||||
|
||||
virtual int keyboard_get_layout_count() const override;
|
||||
virtual int keyboard_get_current_layout() const override;
|
||||
virtual void keyboard_set_current_layout(int p_index) override;
|
||||
virtual String keyboard_get_layout_language(int p_index) const override;
|
||||
virtual String keyboard_get_layout_name(int p_index) const override;
|
||||
virtual Key keyboard_get_keycode_from_physical(Key p_keycode) const override;
|
||||
virtual Key keyboard_get_label_from_physical(Key p_keycode) const override;
|
||||
virtual void show_emoji_and_symbol_picker() const override;
|
||||
|
||||
virtual int tablet_get_driver_count() const override;
|
||||
virtual String tablet_get_driver_name(int p_driver) const override;
|
||||
virtual String tablet_get_current_driver() const override;
|
||||
virtual void tablet_set_current_driver(const String &p_driver) override;
|
||||
|
||||
virtual void process_events() override;
|
||||
|
||||
virtual void force_process_and_drop_events() override;
|
||||
|
||||
virtual void release_rendering_thread() override;
|
||||
virtual void swap_buffers() override;
|
||||
|
||||
virtual void set_native_icon(const String &p_filename) override;
|
||||
virtual void set_icon(const Ref<Image> &p_icon) override;
|
||||
|
||||
virtual IndicatorID create_status_indicator(const Ref<Texture2D> &p_icon, const String &p_tooltip, const Callable &p_callback) override;
|
||||
virtual void status_indicator_set_icon(IndicatorID p_id, const Ref<Texture2D> &p_icon) override;
|
||||
virtual void status_indicator_set_tooltip(IndicatorID p_id, const String &p_tooltip) override;
|
||||
virtual void status_indicator_set_menu(IndicatorID p_id, const RID &p_rid) override;
|
||||
virtual void status_indicator_set_callback(IndicatorID p_id, const Callable &p_callback) override;
|
||||
virtual Rect2 status_indicator_get_rect(IndicatorID p_id) const override;
|
||||
virtual void delete_status_indicator(IndicatorID p_id) override;
|
||||
|
||||
virtual void set_context(Context p_context) override;
|
||||
|
||||
virtual bool is_window_transparency_available() const override;
|
||||
|
||||
static DisplayServer *create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
|
||||
static Vector<String> get_rendering_drivers_func();
|
||||
static void register_windows_driver();
|
||||
|
||||
DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, int64_t p_parent_window, Error &r_error);
|
||||
~DisplayServerWindows();
|
||||
};
|
142
platform/windows/doc_classes/EditorExportPlatformWindows.xml
Normal file
142
platform/windows/doc_classes/EditorExportPlatformWindows.xml
Normal file
@@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<class name="EditorExportPlatformWindows" inherits="EditorExportPlatformPC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
|
||||
<brief_description>
|
||||
Exporter for Windows.
|
||||
</brief_description>
|
||||
<description>
|
||||
The Windows exporter customizes how a Windows build is handled. In the editor's "Export" window, it is created when adding a new "Windows" preset.
|
||||
</description>
|
||||
<tutorials>
|
||||
<link title="Exporting for Windows">$DOCS_URL/tutorials/export/exporting_for_windows.html</link>
|
||||
</tutorials>
|
||||
<members>
|
||||
<member name="application/company_name" type="String" setter="" getter="">
|
||||
Company that produced the application. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
|
||||
</member>
|
||||
<member name="application/console_wrapper_icon" type="String" setter="" getter="">
|
||||
Console wrapper icon file. If left empty, it will fallback to [member application/icon], then to [member ProjectSettings.application/config/windows_native_icon], and lastly, [member ProjectSettings.application/config/icon].
|
||||
</member>
|
||||
<member name="application/copyright" type="String" setter="" getter="">
|
||||
Copyright notice for the bundle visible to the user. Optional. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
|
||||
</member>
|
||||
<member name="application/d3d12_agility_sdk_multiarch" type="bool" setter="" getter="">
|
||||
If [code]true[/code], and [member application/export_d3d12] is set, the Agility SDK DLLs will be stored in arch-specific subdirectories.
|
||||
</member>
|
||||
<member name="application/export_angle" type="int" setter="" getter="">
|
||||
If set to [code]1[/code], ANGLE libraries are exported with the exported application. If set to [code]0[/code], ANGLE libraries are exported only if [member ProjectSettings.rendering/gl_compatibility/driver] is set to [code]"opengl3_angle"[/code].
|
||||
</member>
|
||||
<member name="application/export_d3d12" type="int" setter="" getter="">
|
||||
If set to [code]1[/code], the Direct3D 12 runtime libraries (Agility SDK, PIX) are exported with the exported application. If set to [code]0[/code], Direct3D 12 libraries are exported only if [member ProjectSettings.rendering/rendering_device/driver] is set to [code]"d3d12"[/code].
|
||||
</member>
|
||||
<member name="application/file_description" type="String" setter="" getter="">
|
||||
File description to be presented to users. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
|
||||
</member>
|
||||
<member name="application/file_version" type="String" setter="" getter="">
|
||||
Version number of the file. Falls back to [member ProjectSettings.application/config/version] if left empty. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
|
||||
</member>
|
||||
<member name="application/icon" type="String" setter="" getter="">
|
||||
Application icon file. If left empty, it will fallback to [member ProjectSettings.application/config/windows_native_icon], and then to [member ProjectSettings.application/config/icon].
|
||||
</member>
|
||||
<member name="application/icon_interpolation" type="int" setter="" getter="">
|
||||
Interpolation method used to resize application icon.
|
||||
</member>
|
||||
<member name="application/modify_resources" type="bool" setter="" getter="">
|
||||
If enabled, icon and metadata of the exported executable is set according to the other [code]application/*[/code] values.
|
||||
</member>
|
||||
<member name="application/product_name" type="String" setter="" getter="">
|
||||
Name of the application. Required. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
|
||||
</member>
|
||||
<member name="application/product_version" type="String" setter="" getter="">
|
||||
Application version visible to the user. Falls back to [member ProjectSettings.application/config/version] if left empty. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
|
||||
</member>
|
||||
<member name="application/trademarks" type="String" setter="" getter="">
|
||||
Trademarks and registered trademarks that apply to the file. Optional. See [url=https://learn.microsoft.com/en-us/windows/win32/menurc/stringfileinfo-block]StringFileInfo[/url].
|
||||
</member>
|
||||
<member name="binary_format/architecture" type="String" setter="" getter="">
|
||||
Application executable architecture.
|
||||
Supported architectures: [code]x86_32[/code], [code]x86_64[/code], and [code]arm64[/code].
|
||||
</member>
|
||||
<member name="binary_format/embed_pck" type="bool" setter="" getter="">
|
||||
If [code]true[/code], project resources are embedded into the executable.
|
||||
</member>
|
||||
<member name="codesign/custom_options" type="PackedStringArray" setter="" getter="">
|
||||
Array of the additional command line arguments passed to the code signing tool. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].
|
||||
</member>
|
||||
<member name="codesign/description" type="String" setter="" getter="">
|
||||
Description of the signed content. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].
|
||||
</member>
|
||||
<member name="codesign/digest_algorithm" type="int" setter="" getter="">
|
||||
Digest algorithm to use for creating signature. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].
|
||||
</member>
|
||||
<member name="codesign/enable" type="bool" setter="" getter="">
|
||||
If [code]true[/code], executable signing is enabled.
|
||||
</member>
|
||||
<member name="codesign/identity" type="String" setter="" getter="">
|
||||
PKCS #12 certificate file used to sign executable or certificate SHA-1 hash (if [member codesign/identity_type] is set to "Use certificate store"). See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].
|
||||
Can be overridden with the environment variable [code]GODOT_WINDOWS_CODESIGN_IDENTITY[/code].
|
||||
</member>
|
||||
<member name="codesign/identity_type" type="int" setter="" getter="">
|
||||
Type of identity to use. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].
|
||||
Can be overridden with the environment variable [code]GODOT_WINDOWS_CODESIGN_IDENTITY_TYPE[/code].
|
||||
</member>
|
||||
<member name="codesign/password" type="String" setter="" getter="">
|
||||
Password for the certificate file used to sign executable. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].
|
||||
Can be overridden with the environment variable [code]GODOT_WINDOWS_CODESIGN_PASSWORD[/code].
|
||||
</member>
|
||||
<member name="codesign/timestamp" type="bool" setter="" getter="">
|
||||
If [code]true[/code], time-stamp is added to the signature. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].
|
||||
</member>
|
||||
<member name="codesign/timestamp_server_url" type="String" setter="" getter="">
|
||||
URL of the time stamp server. If left empty, the default server is used. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].
|
||||
</member>
|
||||
<member name="custom_template/debug" type="String" setter="" getter="">
|
||||
Path to the custom export template. If left empty, default template is used.
|
||||
</member>
|
||||
<member name="custom_template/release" type="String" setter="" getter="">
|
||||
Path to the custom export template. If left empty, default template is used.
|
||||
</member>
|
||||
<member name="debug/export_console_wrapper" type="int" setter="" getter="">
|
||||
If [code]true[/code], a console wrapper executable is exported alongside the main executable, which allows running the project with enabled console output.
|
||||
</member>
|
||||
<member name="shader_baker/enabled" type="bool" setter="" getter="">
|
||||
If [code]true[/code], shaders will be compiled and embedded in the application. This option is only supported when using the Forward+ and Mobile renderers.
|
||||
</member>
|
||||
<member name="ssh_remote_deploy/cleanup_script" type="String" setter="" getter="">
|
||||
Script code to execute on the remote host when app is finished.
|
||||
The following variables can be used in the script:
|
||||
- [code]{temp_dir}[/code] - Path of temporary folder on the remote, used to upload app and scripts to.
|
||||
- [code]{archive_name}[/code] - Name of the ZIP containing uploaded application.
|
||||
- [code]{exe_name}[/code] - Name of application executable.
|
||||
- [code]{cmd_args}[/code] - Array of the command line argument for the application.
|
||||
</member>
|
||||
<member name="ssh_remote_deploy/enabled" type="bool" setter="" getter="">
|
||||
Enables remote deploy using SSH/SCP.
|
||||
</member>
|
||||
<member name="ssh_remote_deploy/extra_args_scp" type="String" setter="" getter="">
|
||||
Array of the additional command line arguments passed to the SCP.
|
||||
</member>
|
||||
<member name="ssh_remote_deploy/extra_args_ssh" type="String" setter="" getter="">
|
||||
Array of the additional command line arguments passed to the SSH.
|
||||
</member>
|
||||
<member name="ssh_remote_deploy/host" type="String" setter="" getter="">
|
||||
Remote host SSH user name and address, in [code]user@address[/code] format.
|
||||
</member>
|
||||
<member name="ssh_remote_deploy/port" type="String" setter="" getter="">
|
||||
Remote host SSH port number.
|
||||
</member>
|
||||
<member name="ssh_remote_deploy/run_script" type="String" setter="" getter="">
|
||||
Script code to execute on the remote host when running the app.
|
||||
The following variables can be used in the script:
|
||||
- [code]{temp_dir}[/code] - Path of temporary folder on the remote, used to upload app and scripts to.
|
||||
- [code]{archive_name}[/code] - Name of the ZIP containing uploaded application.
|
||||
- [code]{exe_name}[/code] - Name of application executable.
|
||||
- [code]{cmd_args}[/code] - Array of the command line argument for the application.
|
||||
</member>
|
||||
<member name="texture_format/etc2_astc" type="bool" setter="" getter="">
|
||||
If [code]true[/code], project textures are exported in the ETC2/ASTC format.
|
||||
</member>
|
||||
<member name="texture_format/s3tc_bptc" type="bool" setter="" getter="">
|
||||
If [code]true[/code], project textures are exported in the S3TC/BPTC format.
|
||||
</member>
|
||||
</members>
|
||||
</class>
|
376
platform/windows/drop_target_windows.cpp
Normal file
376
platform/windows/drop_target_windows.cpp
Normal file
@@ -0,0 +1,376 @@
|
||||
/**************************************************************************/
|
||||
/* drop_target_windows.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 "drop_target_windows.h"
|
||||
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/math/random_pcg.h"
|
||||
#include "core/os/time.h"
|
||||
|
||||
#include <fileapi.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
// Helpers
|
||||
|
||||
static String create_temp_dir() {
|
||||
Char16String buf;
|
||||
int bufsize = GetTempPathW(0, nullptr) + 1;
|
||||
buf.resize_uninitialized(bufsize);
|
||||
if (GetTempPathW(bufsize, (LPWSTR)buf.ptrw()) == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String tmp_dir = String::utf16((const char16_t *)buf.ptr());
|
||||
RandomPCG gen(Time::get_singleton()->get_ticks_usec());
|
||||
|
||||
const int attempts = 4;
|
||||
|
||||
for (int i = 0; i < attempts; ++i) {
|
||||
uint32_t rnd = gen.rand();
|
||||
String dirname = "godot_tmp_" + String::num_uint64(rnd);
|
||||
String res_dir = tmp_dir.path_join(dirname);
|
||||
Char16String res_dir16 = res_dir.utf16();
|
||||
|
||||
if (CreateDirectoryW((LPCWSTR)res_dir16.ptr(), nullptr)) {
|
||||
return res_dir;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static bool remove_dir_recursive(const String &p_dir) {
|
||||
Ref<DirAccess> dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
if (dir_access->change_dir(p_dir) != OK) {
|
||||
return false;
|
||||
}
|
||||
return dir_access->erase_contents_recursive() == OK;
|
||||
}
|
||||
|
||||
static bool stream2file(IStream *p_stream, FILEDESCRIPTORW *p_desc, const String &p_path) {
|
||||
if (DirAccess::make_dir_recursive_absolute(p_path.get_base_dir()) != OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Char16String path16 = p_path.utf16();
|
||||
DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
|
||||
|
||||
if (p_desc->dwFlags & FD_ATTRIBUTES) {
|
||||
dwFlagsAndAttributes = p_desc->dwFileAttributes;
|
||||
}
|
||||
|
||||
HANDLE file = CreateFileW(
|
||||
(LPCWSTR)path16.ptr(),
|
||||
GENERIC_WRITE,
|
||||
0,
|
||||
nullptr,
|
||||
CREATE_NEW,
|
||||
dwFlagsAndAttributes,
|
||||
nullptr);
|
||||
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int bufsize = 4096;
|
||||
char buf[bufsize];
|
||||
ULONG nread = 0;
|
||||
DWORD nwritten = 0;
|
||||
HRESULT read_result = S_OK;
|
||||
bool result = true;
|
||||
|
||||
while (true) {
|
||||
read_result = p_stream->Read(buf, bufsize, &nread);
|
||||
if (read_result != S_OK && read_result != S_FALSE) {
|
||||
result = false;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!nread) {
|
||||
break;
|
||||
}
|
||||
|
||||
while (nread > 0) {
|
||||
if (!WriteFile(file, buf, nread, &nwritten, nullptr) || !nwritten) {
|
||||
result = false;
|
||||
goto cleanup;
|
||||
}
|
||||
nread -= nwritten;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
CloseHandle(file);
|
||||
return result;
|
||||
}
|
||||
|
||||
// DropTargetWindows
|
||||
|
||||
bool DropTargetWindows::is_valid_filedescriptor() {
|
||||
return cf_filedescriptor != 0 && cf_filecontents != 0;
|
||||
}
|
||||
|
||||
HRESULT DropTargetWindows::handle_hdrop_format(Vector<String> *p_files, IDataObject *pDataObj) {
|
||||
FORMATETC fmt = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||
STGMEDIUM stg;
|
||||
HRESULT res = S_OK;
|
||||
|
||||
if (pDataObj->GetData(&fmt, &stg) != S_OK) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
HDROP hDropInfo = (HDROP)GlobalLock(stg.hGlobal);
|
||||
|
||||
Char16String buf;
|
||||
|
||||
if (hDropInfo == nullptr) {
|
||||
ReleaseStgMedium(&stg);
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
int fcount = DragQueryFileW(hDropInfo, 0xFFFFFFFF, nullptr, 0);
|
||||
|
||||
for (int i = 0; i < fcount; i++) {
|
||||
int buffsize = DragQueryFileW(hDropInfo, i, nullptr, 0);
|
||||
buf.resize_uninitialized(buffsize + 1);
|
||||
if (DragQueryFileW(hDropInfo, i, (LPWSTR)buf.ptrw(), buffsize + 1) == 0) {
|
||||
res = E_UNEXPECTED;
|
||||
goto cleanup;
|
||||
}
|
||||
String file = String::utf16((const char16_t *)buf.ptr());
|
||||
p_files->push_back(file);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
GlobalUnlock(stg.hGlobal);
|
||||
ReleaseStgMedium(&stg);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT DropTargetWindows::handle_filedescriptor_format(Vector<String> *p_files, IDataObject *pDataObj) {
|
||||
FORMATETC fmt = { cf_filedescriptor, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||
STGMEDIUM stg;
|
||||
HRESULT res = S_OK;
|
||||
|
||||
if (pDataObj->GetData(&fmt, &stg) != S_OK) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
FILEGROUPDESCRIPTORW *filegroup_desc = (FILEGROUPDESCRIPTORW *)GlobalLock(stg.hGlobal);
|
||||
|
||||
if (!filegroup_desc) {
|
||||
ReleaseStgMedium(&stg);
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
tmp_path = create_temp_dir();
|
||||
Ref<DirAccess> dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
PackedStringArray copied;
|
||||
|
||||
if (dir_access->change_dir(tmp_path) != OK) {
|
||||
res = E_UNEXPECTED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)filegroup_desc->cItems; ++i) {
|
||||
res = save_as_file(tmp_path, filegroup_desc->fgd + i, pDataObj, i);
|
||||
if (res != S_OK) {
|
||||
res = E_UNEXPECTED;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
copied = dir_access->get_files();
|
||||
for (const String &file : copied) {
|
||||
p_files->push_back(tmp_path.path_join(file));
|
||||
}
|
||||
|
||||
copied = dir_access->get_directories();
|
||||
for (const String &dir : copied) {
|
||||
p_files->push_back(tmp_path.path_join(dir));
|
||||
}
|
||||
|
||||
cleanup:
|
||||
GlobalUnlock(filegroup_desc);
|
||||
ReleaseStgMedium(&stg);
|
||||
if (res != S_OK) {
|
||||
remove_dir_recursive(tmp_path);
|
||||
tmp_path.clear();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
HRESULT DropTargetWindows::save_as_file(const String &p_out_dir, FILEDESCRIPTORW *p_file_desc, IDataObject *pDataObj, int p_file_idx) {
|
||||
String relpath = String::utf16((const char16_t *)p_file_desc->cFileName);
|
||||
String fullpath = p_out_dir.path_join(relpath);
|
||||
|
||||
if (p_file_desc->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if (DirAccess::make_dir_recursive_absolute(fullpath) != OK) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
FORMATETC fmt = { cf_filecontents, nullptr, DVASPECT_CONTENT, p_file_idx, TYMED_ISTREAM };
|
||||
STGMEDIUM stg;
|
||||
HRESULT res = S_OK;
|
||||
|
||||
if (pDataObj->GetData(&fmt, &stg) != S_OK) {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
IStream *stream = stg.pstm;
|
||||
if (stream == nullptr) {
|
||||
res = E_UNEXPECTED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!stream2file(stream, p_file_desc, fullpath)) {
|
||||
res = E_UNEXPECTED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
ReleaseStgMedium(&stg);
|
||||
return res;
|
||||
}
|
||||
|
||||
DropTargetWindows::DropTargetWindows(DisplayServerWindows::WindowData *p_window_data) :
|
||||
ref_count(1), window_data(p_window_data) {
|
||||
cf_filedescriptor = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
|
||||
cf_filecontents = RegisterClipboardFormat(CFSTR_FILECONTENTS);
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DropTargetWindows::QueryInterface(REFIID riid, void **ppvObject) {
|
||||
if (riid == IID_IUnknown || riid == IID_IDropTarget) {
|
||||
*ppvObject = static_cast<IDropTarget *>(this);
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
*ppvObject = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE DropTargetWindows::AddRef() {
|
||||
return InterlockedIncrement(&ref_count);
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE DropTargetWindows::Release() {
|
||||
ULONG count = InterlockedDecrement(&ref_count);
|
||||
if (count == 0) {
|
||||
memfree(this);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DropTargetWindows::DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
|
||||
(void)grfKeyState;
|
||||
(void)pt;
|
||||
|
||||
FORMATETC hdrop_fmt = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||
FORMATETC filedesc_fmt = { cf_filedescriptor, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||
|
||||
if (!window_data->drop_files_callback.is_valid()) {
|
||||
*pdwEffect = DROPEFFECT_NONE;
|
||||
} else if (pDataObj->QueryGetData(&hdrop_fmt) == S_OK) {
|
||||
*pdwEffect = DROPEFFECT_COPY;
|
||||
} else if (is_valid_filedescriptor() && pDataObj->QueryGetData(&filedesc_fmt) == S_OK) {
|
||||
*pdwEffect = DROPEFFECT_COPY;
|
||||
} else {
|
||||
*pdwEffect = DROPEFFECT_NONE;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DropTargetWindows::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
|
||||
(void)grfKeyState;
|
||||
(void)pt;
|
||||
|
||||
*pdwEffect = DROPEFFECT_COPY;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DropTargetWindows::DragLeave() {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE DropTargetWindows::Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
|
||||
(void)grfKeyState;
|
||||
(void)pt;
|
||||
|
||||
*pdwEffect = DROPEFFECT_NONE;
|
||||
|
||||
if (!window_data->drop_files_callback.is_valid()) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
FORMATETC hdrop_fmt = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||
FORMATETC filedesc_fmt = { cf_filedescriptor, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||
Vector<String> files;
|
||||
|
||||
if (pDataObj->QueryGetData(&hdrop_fmt) == S_OK) {
|
||||
HRESULT res = handle_hdrop_format(&files, pDataObj);
|
||||
if (res != S_OK) {
|
||||
return res;
|
||||
}
|
||||
} else if (pDataObj->QueryGetData(&filedesc_fmt) == S_OK && is_valid_filedescriptor()) {
|
||||
HRESULT res = handle_filedescriptor_format(&files, pDataObj);
|
||||
if (res != S_OK) {
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (!files.size()) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
Variant v_files = files;
|
||||
const Variant *v_args[1] = { &v_files };
|
||||
Variant ret;
|
||||
Callable::CallError ce;
|
||||
window_data->drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce);
|
||||
|
||||
if (!tmp_path.is_empty()) {
|
||||
remove_dir_recursive(tmp_path);
|
||||
tmp_path.clear();
|
||||
}
|
||||
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(window_data->drop_files_callback, v_args, 1, ce)));
|
||||
return E_UNEXPECTED;
|
||||
}
|
||||
|
||||
*pdwEffect = DROPEFFECT_COPY;
|
||||
return S_OK;
|
||||
}
|
68
platform/windows/drop_target_windows.h
Normal file
68
platform/windows/drop_target_windows.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/**************************************************************************/
|
||||
/* drop_target_windows.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "display_server_windows.h"
|
||||
|
||||
#include <shlobj.h>
|
||||
|
||||
GODOT_GCC_WARNING_PUSH_AND_IGNORE("-Wnon-virtual-dtor") // Silence warning due to a COM API weirdness.
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-dodragdrop#remarks
|
||||
class DropTargetWindows : public IDropTarget {
|
||||
LONG ref_count;
|
||||
DisplayServerWindows::WindowData *window_data = nullptr;
|
||||
CLIPFORMAT cf_filedescriptor = 0;
|
||||
CLIPFORMAT cf_filecontents = 0;
|
||||
String tmp_path;
|
||||
|
||||
bool is_valid_filedescriptor();
|
||||
HRESULT handle_hdrop_format(Vector<String> *p_files, IDataObject *pDataObj);
|
||||
HRESULT handle_filedescriptor_format(Vector<String> *p_files, IDataObject *pDataObj);
|
||||
HRESULT save_as_file(const String &p_out_dir, FILEDESCRIPTORW *p_file_desc, IDataObject *pDataObj, int p_file_idx);
|
||||
|
||||
public:
|
||||
DropTargetWindows(DisplayServerWindows::WindowData *p_window_data);
|
||||
virtual ~DropTargetWindows() {}
|
||||
|
||||
// IUnknown
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
|
||||
ULONG STDMETHODCALLTYPE AddRef() override;
|
||||
ULONG STDMETHODCALLTYPE Release() override;
|
||||
|
||||
// IDropTarget
|
||||
HRESULT STDMETHODCALLTYPE DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
|
||||
HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
|
||||
HRESULT STDMETHODCALLTYPE DragLeave() override;
|
||||
HRESULT STDMETHODCALLTYPE Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
|
||||
};
|
||||
|
||||
GODOT_GCC_WARNING_POP
|
57
platform/windows/export/export.cpp
Normal file
57
platform/windows/export/export.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/**************************************************************************/
|
||||
/* export.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 "export.h"
|
||||
|
||||
#include "export_plugin.h"
|
||||
|
||||
#include "editor/export/editor_export.h"
|
||||
|
||||
void register_windows_exporter_types() {
|
||||
GDREGISTER_VIRTUAL_CLASS(EditorExportPlatformWindows);
|
||||
}
|
||||
|
||||
void register_windows_exporter() {
|
||||
// TODO: Move to editor_settings.cpp
|
||||
#ifdef WINDOWS_ENABLED
|
||||
EDITOR_DEF_BASIC("export/windows/signtool", "");
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/signtool", PROPERTY_HINT_GLOBAL_FILE, "*.exe"));
|
||||
#else
|
||||
EDITOR_DEF_BASIC("export/windows/osslsigncode", "");
|
||||
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/osslsigncode", PROPERTY_HINT_GLOBAL_FILE));
|
||||
#endif
|
||||
|
||||
Ref<EditorExportPlatformWindows> platform;
|
||||
platform.instantiate();
|
||||
platform->set_name("Windows Desktop");
|
||||
platform->set_os_name("Windows");
|
||||
|
||||
EditorExport::get_singleton()->add_export_platform(platform);
|
||||
}
|
34
platform/windows/export/export.h
Normal file
34
platform/windows/export/export.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/**************************************************************************/
|
||||
/* export.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
void register_windows_exporter_types();
|
||||
void register_windows_exporter();
|
1115
platform/windows/export/export_plugin.cpp
Normal file
1115
platform/windows/export/export_plugin.cpp
Normal file
File diff suppressed because it is too large
Load Diff
101
platform/windows/export/export_plugin.h
Normal file
101
platform/windows/export/export_plugin.h
Normal file
@@ -0,0 +1,101 @@
|
||||
/**************************************************************************/
|
||||
/* export_plugin.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/os/os.h"
|
||||
#include "editor/export/editor_export_platform_pc.h"
|
||||
#include "editor/settings/editor_settings.h"
|
||||
|
||||
// Optional environment variables for defining confidential information. If any
|
||||
// of these is set, they will override the values set in the credentials file.
|
||||
const String ENV_WIN_CODESIGN_ID_TYPE = "GODOT_WINDOWS_CODESIGN_IDENTITY_TYPE";
|
||||
const String ENV_WIN_CODESIGN_ID = "GODOT_WINDOWS_CODESIGN_IDENTITY";
|
||||
const String ENV_WIN_CODESIGN_PASS = "GODOT_WINDOWS_CODESIGN_PASSWORD";
|
||||
|
||||
class EditorExportPlatformWindows : public EditorExportPlatformPC {
|
||||
GDCLASS(EditorExportPlatformWindows, EditorExportPlatformPC);
|
||||
|
||||
struct SSHCleanupCommand {
|
||||
String host;
|
||||
String port;
|
||||
Vector<String> ssh_args;
|
||||
String cmd_args;
|
||||
bool wait = false;
|
||||
|
||||
SSHCleanupCommand() {}
|
||||
SSHCleanupCommand(const String &p_host, const String &p_port, const Vector<String> &p_ssh_arg, const String &p_cmd_args, bool p_wait = false) {
|
||||
host = p_host;
|
||||
port = p_port;
|
||||
ssh_args = p_ssh_arg;
|
||||
cmd_args = p_cmd_args;
|
||||
wait = p_wait;
|
||||
}
|
||||
};
|
||||
|
||||
Ref<ImageTexture> run_icon;
|
||||
Ref<ImageTexture> stop_icon;
|
||||
|
||||
Vector<SSHCleanupCommand> cleanup_commands;
|
||||
OS::ProcessID ssh_pid = 0;
|
||||
int menu_options = 0;
|
||||
|
||||
Error _process_icon(const Ref<EditorExportPreset> &p_preset, const String &p_src_path, const String &p_dst_path);
|
||||
Error _add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_console_icon);
|
||||
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
|
||||
|
||||
String _get_exe_arch(const String &p_path) const;
|
||||
|
||||
public:
|
||||
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags = 0) override;
|
||||
virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, BitField<EditorExportPlatform::DebugFlags> p_flags) override;
|
||||
virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path) override;
|
||||
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
|
||||
virtual void get_export_options(List<ExportOption> *r_options) const override;
|
||||
virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
|
||||
virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
|
||||
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override;
|
||||
virtual String get_export_option_warning(const EditorExportPreset *p_preset, const StringName &p_name) const override;
|
||||
|
||||
virtual String get_template_file_name(const String &p_target, const String &p_arch) const override;
|
||||
virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override;
|
||||
|
||||
virtual Ref<Texture2D> get_run_icon() const override;
|
||||
virtual bool poll_export() override;
|
||||
virtual Ref<Texture2D> get_option_icon(int p_index) const override;
|
||||
virtual int get_options_count() const override;
|
||||
virtual String get_option_label(int p_index) const override;
|
||||
virtual String get_option_tooltip(int p_index) const override;
|
||||
virtual Error run(const Ref<EditorExportPreset> &p_preset, int p_device, BitField<EditorExportPlatform::DebugFlags> p_debug_flags) override;
|
||||
virtual void cleanup() override;
|
||||
|
||||
EditorExportPlatformWindows();
|
||||
};
|
1
platform/windows/export/logo.svg
Normal file
1
platform/windows/export/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path fill="#00abed" d="m1 5.132 12.295-1.694v11.879H1zm0 21.736 12.295 1.695V16.83H1zm13.647 1.875L31 31V16.83H14.647zm0-25.486v12.06H31V1z"/></svg>
|
After Width: | Height: | Size: 213 B |
1
platform/windows/export/run_icon.svg
Normal file
1
platform/windows/export/run_icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16"><path fill="#e0e0e0" d="m1.095 2.997 5.66-.78v5.469h-5.66zm0 10.006 5.66.78v-5.4h-5.66zm6.282.863 7.528 1.04V8.381H7.377Zm0-11.732v5.552h7.528V1.095Z"/></svg>
|
After Width: | Height: | Size: 243 B |
726
platform/windows/export/template_modifier.cpp
Normal file
726
platform/windows/export/template_modifier.cpp
Normal file
@@ -0,0 +1,726 @@
|
||||
/**************************************************************************/
|
||||
/* template_modifier.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 "template_modifier.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
|
||||
void TemplateModifier::ByteStream::save(uint8_t p_value, Vector<uint8_t> &r_bytes) const {
|
||||
save(p_value, r_bytes, 1);
|
||||
}
|
||||
|
||||
void TemplateModifier::ByteStream::save(uint16_t p_value, Vector<uint8_t> &r_bytes) const {
|
||||
save(p_value, r_bytes, 2);
|
||||
}
|
||||
|
||||
void TemplateModifier::ByteStream::save(uint32_t p_value, Vector<uint8_t> &r_bytes) const {
|
||||
save(p_value, r_bytes, 4);
|
||||
}
|
||||
|
||||
void TemplateModifier::ByteStream::save(const String &p_value, Vector<uint8_t> &r_bytes) const {
|
||||
r_bytes.append_array(p_value.to_utf16_buffer());
|
||||
save((uint16_t)0, r_bytes);
|
||||
}
|
||||
|
||||
void TemplateModifier::ByteStream::save(uint32_t p_value, Vector<uint8_t> &r_bytes, uint32_t p_count) const {
|
||||
for (uint32_t i = 0; i < p_count; i++) {
|
||||
r_bytes.append((uint8_t)(p_value & 0xff));
|
||||
p_value >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::ByteStream::save() const {
|
||||
return Vector<uint8_t>();
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::Structure::save() const {
|
||||
Vector<uint8_t> bytes;
|
||||
ByteStream::save(length, bytes);
|
||||
ByteStream::save(value_length, bytes);
|
||||
ByteStream::save(type, bytes);
|
||||
ByteStream::save(key, bytes);
|
||||
while (bytes.size() % 4) {
|
||||
bytes.append(0);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
Vector<uint8_t> &TemplateModifier::Structure::add_length(Vector<uint8_t> &r_bytes) const {
|
||||
r_bytes.write[0] = r_bytes.size() & 0xff;
|
||||
r_bytes.write[1] = r_bytes.size() >> 8 & 0xff;
|
||||
return r_bytes;
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::ResourceDirectoryTable::save() const {
|
||||
Vector<uint8_t> bytes;
|
||||
bytes.resize_initialized(12);
|
||||
ByteStream::save(name_entry_count, bytes);
|
||||
ByteStream::save(id_entry_count, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::ResourceDirectoryEntry::save() const {
|
||||
Vector<uint8_t> bytes;
|
||||
ByteStream::save(id | (name ? HIGH_BIT : 0), bytes);
|
||||
ByteStream::save(data_offset | (subdirectory ? HIGH_BIT : 0), bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::FixedFileInfo::save() const {
|
||||
Vector<uint8_t> bytes;
|
||||
ByteStream::save(signature, bytes);
|
||||
ByteStream::save(struct_version, bytes);
|
||||
ByteStream::save(file_version_ms, bytes);
|
||||
ByteStream::save(file_version_ls, bytes);
|
||||
ByteStream::save(product_version_ms, bytes);
|
||||
ByteStream::save(product_version_ls, bytes);
|
||||
ByteStream::save(file_flags_mask, bytes);
|
||||
ByteStream::save(file_flags, bytes);
|
||||
ByteStream::save(file_os, bytes);
|
||||
ByteStream::save(file_type, bytes);
|
||||
ByteStream::save(file_subtype, bytes);
|
||||
ByteStream::save(file_date_ms, bytes);
|
||||
ByteStream::save(file_date_ls, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void TemplateModifier::FixedFileInfo::set_file_version(const String &p_file_version) {
|
||||
Vector<String> parts = p_file_version.split(".");
|
||||
while (parts.size() < 4) {
|
||||
parts.append("0");
|
||||
}
|
||||
file_version_ms = parts[0].to_int() << 16 | (parts[1].to_int() & 0xffff);
|
||||
file_version_ls = parts[2].to_int() << 16 | (parts[3].to_int() & 0xffff);
|
||||
}
|
||||
|
||||
void TemplateModifier::FixedFileInfo::set_product_version(const String &p_product_version) {
|
||||
Vector<String> parts = p_product_version.split(".");
|
||||
while (parts.size() < 4) {
|
||||
parts.append("0");
|
||||
}
|
||||
product_version_ms = parts[0].to_int() << 16 | (parts[1].to_int() & 0xffff);
|
||||
product_version_ls = parts[2].to_int() << 16 | (parts[3].to_int() & 0xffff);
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::StringStructure::save() const {
|
||||
Vector<uint8_t> bytes = Structure::save();
|
||||
ByteStream::save(value, bytes);
|
||||
return add_length(bytes);
|
||||
}
|
||||
|
||||
TemplateModifier::StringStructure::StringStructure() {
|
||||
type = 1;
|
||||
}
|
||||
|
||||
TemplateModifier::StringStructure::StringStructure(const String &p_key, const String &p_value) {
|
||||
type = 1;
|
||||
value_length = p_value.length() + 1;
|
||||
key = p_key;
|
||||
value = p_value;
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::StringTable::save() const {
|
||||
Vector<uint8_t> bytes = Structure::save();
|
||||
for (const StringStructure &string : strings) {
|
||||
bytes.append_array(string.save());
|
||||
while (bytes.size() % 4) {
|
||||
bytes.append(0);
|
||||
}
|
||||
}
|
||||
return add_length(bytes);
|
||||
}
|
||||
|
||||
void TemplateModifier::StringTable::put(const String &p_key, const String &p_value) {
|
||||
strings.append(StringStructure(p_key, p_value));
|
||||
}
|
||||
|
||||
TemplateModifier::StringTable::StringTable() {
|
||||
key = "040904b0";
|
||||
type = 1;
|
||||
}
|
||||
|
||||
TemplateModifier::StringFileInfo::StringFileInfo() {
|
||||
key = "StringFileInfo";
|
||||
value_length = 0;
|
||||
type = 1;
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::StringFileInfo::save() const {
|
||||
Vector<uint8_t> bytes = Structure::save();
|
||||
bytes.append_array(string_table.save());
|
||||
return add_length(bytes);
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::Var::save() const {
|
||||
Vector<uint8_t> bytes = Structure::save();
|
||||
ByteStream::save(value, bytes);
|
||||
return add_length(bytes);
|
||||
}
|
||||
|
||||
TemplateModifier::Var::Var() {
|
||||
value_length = 4;
|
||||
key = "Translation";
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::VarFileInfo::save() const {
|
||||
Vector<uint8_t> bytes = Structure::save();
|
||||
bytes.append_array(var.save());
|
||||
return add_length(bytes);
|
||||
}
|
||||
|
||||
TemplateModifier::VarFileInfo::VarFileInfo() {
|
||||
type = 1;
|
||||
key = "VarFileInfo";
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::VersionInfo::save() const {
|
||||
Vector<uint8_t> fixed_file_info = value.save();
|
||||
Vector<uint8_t> bytes = Structure::save();
|
||||
bytes.append_array(fixed_file_info);
|
||||
bytes.append_array(string_file_info.save());
|
||||
while (bytes.size() % 4) {
|
||||
bytes.append(0);
|
||||
}
|
||||
bytes.append_array(var_file_info.save());
|
||||
return add_length(bytes);
|
||||
}
|
||||
|
||||
TemplateModifier::VersionInfo::VersionInfo() {
|
||||
key = "VS_VERSION_INFO";
|
||||
value_length = 52;
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::IconEntry::save() const {
|
||||
Vector<uint8_t> bytes;
|
||||
ByteStream::save(width, bytes);
|
||||
ByteStream::save(height, bytes);
|
||||
ByteStream::save(colors, bytes);
|
||||
ByteStream::save((uint8_t)0, bytes);
|
||||
ByteStream::save(planes, bytes);
|
||||
ByteStream::save(bits_per_pixel, bytes);
|
||||
ByteStream::save(image_size, bytes);
|
||||
ByteStream::save((uint16_t)image_offset, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void TemplateModifier::IconEntry::load(Ref<FileAccess> p_file) {
|
||||
width = p_file->get_8(); // Width in pixels.
|
||||
height = p_file->get_8(); // Height in pixels.
|
||||
colors = p_file->get_8(); // Number of colors in the palette (0 - no palette).
|
||||
p_file->get_8(); // Reserved.
|
||||
planes = p_file->get_16(); // Number of color planes.
|
||||
bits_per_pixel = p_file->get_16(); // Bits per pixel.
|
||||
image_size = p_file->get_32(); // Image data size in bytes.
|
||||
image_offset = p_file->get_32(); // Image data offset.
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::GroupIcon::save() const {
|
||||
Vector<uint8_t> bytes;
|
||||
ByteStream::save(reserved, bytes);
|
||||
ByteStream::save(type, bytes);
|
||||
ByteStream::save(image_count, bytes);
|
||||
for (const IconEntry &icon_entry : icon_entries) {
|
||||
bytes.append_array(icon_entry.save());
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void TemplateModifier::GroupIcon::load(Ref<FileAccess> p_icon_file) {
|
||||
if (p_icon_file->get_32() != 0x10000) { // Wrong reserved bytes
|
||||
ERR_FAIL_MSG("Wrong icon file type.");
|
||||
}
|
||||
|
||||
image_count = p_icon_file->get_16();
|
||||
for (uint16_t i = 0; i < image_count; i++) {
|
||||
IconEntry icon_entry;
|
||||
icon_entry.load(p_icon_file);
|
||||
icon_entries.append(icon_entry);
|
||||
}
|
||||
|
||||
int id = 1;
|
||||
for (IconEntry &icon_entry : icon_entries) {
|
||||
Vector<uint8_t> image;
|
||||
image.resize(icon_entry.image_size);
|
||||
p_icon_file->seek(icon_entry.image_offset);
|
||||
p_icon_file->get_buffer(image.ptrw(), image.size());
|
||||
icon_entry.image_offset = id++;
|
||||
images.append(image);
|
||||
}
|
||||
}
|
||||
|
||||
void TemplateModifier::GroupIcon::fill_with_godot_blue() {
|
||||
uint32_t id = 1;
|
||||
for (uint8_t size : SIZES) {
|
||||
Ref<Image> image = Image::create_empty(size ? size : 256, size ? size : 256, false, Image::FORMAT_RGB8);
|
||||
image->fill(Color::hex(0x478cbfff));
|
||||
Vector<uint8_t> data = image->save_png_to_buffer();
|
||||
IconEntry icon_entry;
|
||||
icon_entry.width = size;
|
||||
icon_entry.height = size;
|
||||
icon_entry.bits_per_pixel = 24;
|
||||
icon_entry.image_size = data.size();
|
||||
icon_entry.image_offset = id++;
|
||||
icon_entries.append(icon_entry);
|
||||
images.append(data);
|
||||
}
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::SectionEntry::save() const {
|
||||
Vector<uint8_t> bytes;
|
||||
bytes.append_array(name.to_utf8_buffer());
|
||||
while (bytes.size() < 8) {
|
||||
bytes.append(0);
|
||||
}
|
||||
ByteStream::save(virtual_size, bytes);
|
||||
ByteStream::save(virtual_address, bytes);
|
||||
ByteStream::save(size_of_raw_data, bytes);
|
||||
ByteStream::save(pointer_to_raw_data, bytes);
|
||||
ByteStream::save(pointer_to_relocations, bytes);
|
||||
ByteStream::save(pointer_to_line_numbers, bytes);
|
||||
ByteStream::save(number_of_relocations, bytes);
|
||||
ByteStream::save(number_of_line_numbers, bytes);
|
||||
ByteStream::save(characteristics, bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void TemplateModifier::SectionEntry::load(Ref<FileAccess> p_file) {
|
||||
uint8_t section_name[8];
|
||||
p_file->get_buffer(section_name, 8);
|
||||
name = String::utf8((char *)section_name, 8);
|
||||
virtual_size = p_file->get_32();
|
||||
virtual_address = p_file->get_32();
|
||||
size_of_raw_data = p_file->get_32();
|
||||
pointer_to_raw_data = p_file->get_32();
|
||||
pointer_to_relocations = p_file->get_32();
|
||||
pointer_to_line_numbers = p_file->get_32();
|
||||
number_of_relocations = p_file->get_16();
|
||||
number_of_line_numbers = p_file->get_16();
|
||||
characteristics = p_file->get_32();
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::ResourceDataEntry::save() const {
|
||||
Vector<uint8_t> bytes;
|
||||
ByteStream::save(rva, bytes);
|
||||
ByteStream::save(size, bytes);
|
||||
ByteStream::save(0, bytes, 8);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
uint32_t TemplateModifier::_get_pe_header_offset(Ref<FileAccess> p_executable) const {
|
||||
p_executable->seek(POINTER_TO_PE_HEADER_OFFSET);
|
||||
uint32_t pe_header_offset = p_executable->get_32();
|
||||
|
||||
p_executable->seek(pe_header_offset);
|
||||
uint32_t magic = p_executable->get_32();
|
||||
|
||||
return magic == 0x00004550 ? pe_header_offset : 0;
|
||||
}
|
||||
|
||||
uint32_t TemplateModifier::_snap(uint32_t p_value, uint32_t p_size) const {
|
||||
return p_value + (p_value % p_size ? p_size - (p_value % p_size) : 0);
|
||||
}
|
||||
|
||||
Vector<uint8_t> TemplateModifier::_create_resources(uint32_t p_virtual_address, const GroupIcon &p_group_icon, const VersionInfo &p_version_info) const {
|
||||
// 0x04, 0x00 as string length ICON in UTF16 and padding to 32 bits
|
||||
const uint8_t ICON_DIRECTORY_STRING[] = { 0x04, 0x00, 0x49, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x4e, 0x00, 0x00, 0x00 };
|
||||
const uint16_t RT_ENTRY_COUNT = 3;
|
||||
const uint32_t icon_count = p_group_icon.images.size();
|
||||
|
||||
ResourceDirectoryTable root_directory_table;
|
||||
root_directory_table.id_entry_count = RT_ENTRY_COUNT;
|
||||
|
||||
Vector<uint8_t> resources = root_directory_table.save();
|
||||
|
||||
ResourceDirectoryEntry rt_icon_entry;
|
||||
rt_icon_entry.id = ResourceDirectoryEntry::ICON;
|
||||
rt_icon_entry.data_offset = ResourceDirectoryTable::SIZE + RT_ENTRY_COUNT * ResourceDirectoryEntry::SIZE;
|
||||
rt_icon_entry.subdirectory = true;
|
||||
resources.append_array(rt_icon_entry.save());
|
||||
|
||||
ResourceDirectoryEntry rt_group_icon_entry;
|
||||
rt_group_icon_entry.id = ResourceDirectoryEntry::GROUP_ICON;
|
||||
rt_group_icon_entry.data_offset = (2 + icon_count) * ResourceDirectoryTable::SIZE + (RT_ENTRY_COUNT + 2 * icon_count) * ResourceDirectoryEntry::SIZE;
|
||||
rt_group_icon_entry.subdirectory = true;
|
||||
resources.append_array(rt_group_icon_entry.save());
|
||||
|
||||
ResourceDirectoryEntry rt_version_entry;
|
||||
rt_version_entry.id = ResourceDirectoryEntry::VERSION;
|
||||
rt_version_entry.data_offset = (4 + icon_count) * ResourceDirectoryTable::SIZE + (RT_ENTRY_COUNT + 2 * icon_count + 2) * ResourceDirectoryEntry::SIZE;
|
||||
rt_version_entry.subdirectory = true;
|
||||
resources.append_array(rt_version_entry.save());
|
||||
|
||||
ResourceDirectoryTable icon_table;
|
||||
icon_table.id_entry_count = icon_count;
|
||||
resources.append_array(icon_table.save());
|
||||
|
||||
for (uint32_t i = 0; i < icon_count; i++) {
|
||||
ResourceDirectoryEntry icon_entry;
|
||||
icon_entry.id = i + 1;
|
||||
icon_entry.data_offset = (2 + i) * ResourceDirectoryTable::SIZE + (RT_ENTRY_COUNT + icon_count + i) * ResourceDirectoryEntry::SIZE;
|
||||
icon_entry.subdirectory = true;
|
||||
resources.append_array(icon_entry.save());
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < icon_count; i++) {
|
||||
ResourceDirectoryTable language_icon_table;
|
||||
language_icon_table.id_entry_count = 1;
|
||||
resources.append_array(language_icon_table.save());
|
||||
|
||||
ResourceDirectoryEntry language_icon_entry;
|
||||
language_icon_entry.id = ResourceDirectoryEntry::ENGLISH;
|
||||
language_icon_entry.data_offset = (6 + icon_count) * ResourceDirectoryTable::SIZE + (RT_ENTRY_COUNT + icon_count * 2 + 4) * ResourceDirectoryEntry::SIZE + sizeof(ICON_DIRECTORY_STRING) + i * ResourceDataEntry::SIZE;
|
||||
resources.append_array(language_icon_entry.save());
|
||||
}
|
||||
|
||||
ResourceDirectoryTable group_icon_name_table;
|
||||
group_icon_name_table.name_entry_count = 1;
|
||||
resources.append_array(group_icon_name_table.save());
|
||||
|
||||
ResourceDirectoryEntry group_icon_name_entry;
|
||||
group_icon_name_entry.id = (6 + icon_count) * ResourceDirectoryTable::SIZE + (RT_ENTRY_COUNT + icon_count * 2 + 4) * ResourceDirectoryEntry::SIZE;
|
||||
group_icon_name_entry.data_offset = (3 + icon_count) * ResourceDirectoryTable::SIZE + (RT_ENTRY_COUNT + 2 * icon_count + 1) * ResourceDirectoryEntry::SIZE;
|
||||
group_icon_name_entry.name = true;
|
||||
group_icon_name_entry.subdirectory = true;
|
||||
resources.append_array(group_icon_name_entry.save());
|
||||
|
||||
ResourceDirectoryTable group_icon_language_table;
|
||||
group_icon_language_table.id_entry_count = 1;
|
||||
resources.append_array(group_icon_language_table.save());
|
||||
|
||||
ResourceDirectoryEntry group_icon_language_entry;
|
||||
group_icon_language_entry.id = ResourceDirectoryEntry::ENGLISH;
|
||||
group_icon_language_entry.data_offset = (6 + icon_count) * ResourceDirectoryTable::SIZE + (RT_ENTRY_COUNT + 2 * icon_count + 4) * ResourceDirectoryEntry::SIZE + sizeof(ICON_DIRECTORY_STRING) + icon_count * ResourceDataEntry::SIZE;
|
||||
resources.append_array(group_icon_language_entry.save());
|
||||
|
||||
ResourceDirectoryTable version_table;
|
||||
version_table.id_entry_count = 1;
|
||||
resources.append_array(version_table.save());
|
||||
|
||||
ResourceDirectoryEntry version_entry;
|
||||
version_entry.id = 1;
|
||||
version_entry.data_offset = (5 + icon_count) * ResourceDirectoryTable::SIZE + (RT_ENTRY_COUNT + 2 * icon_count + 3) * ResourceDirectoryEntry::SIZE;
|
||||
version_entry.subdirectory = true;
|
||||
resources.append_array(version_entry.save());
|
||||
|
||||
ResourceDirectoryTable version_language_table;
|
||||
version_language_table.id_entry_count = 1;
|
||||
resources.append_array(version_language_table.save());
|
||||
|
||||
ResourceDirectoryEntry version_language_entry;
|
||||
version_language_entry.id = ResourceDirectoryEntry::ENGLISH;
|
||||
version_language_entry.data_offset = (6 + icon_count) * ResourceDirectoryTable::SIZE + (RT_ENTRY_COUNT + 2 * icon_count + 4) * ResourceDirectoryEntry::SIZE + sizeof(ICON_DIRECTORY_STRING) + (icon_count + 1) * ResourceDataEntry::SIZE;
|
||||
resources.append_array(version_language_entry.save());
|
||||
|
||||
Vector<uint8_t> icon_directory_string;
|
||||
icon_directory_string.resize(sizeof(ICON_DIRECTORY_STRING));
|
||||
memcpy(icon_directory_string.ptrw(), ICON_DIRECTORY_STRING, sizeof(ICON_DIRECTORY_STRING));
|
||||
resources.append_array(icon_directory_string);
|
||||
|
||||
Vector<Vector<uint8_t>> data_entries;
|
||||
for (const Vector<uint8_t> &image : p_group_icon.images) {
|
||||
data_entries.append(image);
|
||||
}
|
||||
data_entries.append(p_group_icon.save());
|
||||
data_entries.append(p_version_info.save());
|
||||
|
||||
uint32_t offset = resources.size() + data_entries.size() * ResourceDataEntry::SIZE;
|
||||
|
||||
for (const Vector<uint8_t> &data_entry : data_entries) {
|
||||
ResourceDataEntry resource_data_entry;
|
||||
resource_data_entry.rva = p_virtual_address + offset;
|
||||
resource_data_entry.size = data_entry.size();
|
||||
resources.append_array(resource_data_entry.save());
|
||||
offset += resource_data_entry.size;
|
||||
while (offset % 4) {
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (const Vector<uint8_t> &data_entry : data_entries) {
|
||||
resources.append_array(data_entry);
|
||||
while (resources.size() % 4) {
|
||||
resources.append(0);
|
||||
}
|
||||
}
|
||||
|
||||
return resources;
|
||||
}
|
||||
|
||||
TemplateModifier::VersionInfo TemplateModifier::_create_version_info(const HashMap<String, String> &p_strings) const {
|
||||
StringTable string_table;
|
||||
for (const KeyValue<String, String> &E : p_strings) {
|
||||
string_table.put(E.key, E.value);
|
||||
}
|
||||
|
||||
StringFileInfo string_file_info;
|
||||
string_file_info.string_table = string_table;
|
||||
|
||||
FixedFileInfo fixed_file_info;
|
||||
if (p_strings.has("FileVersion")) {
|
||||
fixed_file_info.set_file_version(p_strings["FileVersion"]);
|
||||
}
|
||||
if (p_strings.has("ProductVersion")) {
|
||||
fixed_file_info.set_product_version(p_strings["ProductVersion"]);
|
||||
}
|
||||
|
||||
VersionInfo version_info;
|
||||
version_info.value = fixed_file_info;
|
||||
version_info.string_file_info = string_file_info;
|
||||
|
||||
return version_info;
|
||||
}
|
||||
|
||||
TemplateModifier::GroupIcon TemplateModifier::_create_group_icon(const String &p_icon_path) const {
|
||||
GroupIcon group_icon;
|
||||
|
||||
Ref<FileAccess> icon_file = FileAccess::open(p_icon_path, FileAccess::READ);
|
||||
if (icon_file.is_null()) {
|
||||
group_icon.fill_with_godot_blue();
|
||||
return group_icon;
|
||||
}
|
||||
|
||||
group_icon.load(icon_file);
|
||||
|
||||
return group_icon;
|
||||
}
|
||||
|
||||
Error TemplateModifier::_truncate(const String &p_path, uint32_t p_size) const {
|
||||
Error error;
|
||||
|
||||
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &error);
|
||||
ERR_FAIL_COND_V(error != OK, ERR_CANT_OPEN);
|
||||
|
||||
String truncated_path = p_path + ".truncated";
|
||||
Ref<FileAccess> truncated = FileAccess::open(truncated_path, FileAccess::WRITE, &error);
|
||||
ERR_FAIL_COND_V(error != OK, ERR_CANT_CREATE);
|
||||
|
||||
truncated->store_buffer(file->get_buffer(p_size));
|
||||
|
||||
file->close();
|
||||
truncated->close();
|
||||
|
||||
DirAccess::remove_absolute(p_path);
|
||||
DirAccess::rename_absolute(truncated_path, p_path);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
HashMap<String, String> TemplateModifier::_get_strings(const Ref<EditorExportPreset> &p_preset) const {
|
||||
String file_version = p_preset->get_version("application/file_version", true);
|
||||
String product_version = p_preset->get_version("application/product_version", true);
|
||||
String company_name = p_preset->get("application/company_name");
|
||||
String product_name = p_preset->get("application/product_name");
|
||||
String file_description = p_preset->get("application/file_description");
|
||||
String copyright = p_preset->get("application/copyright");
|
||||
String trademarks = p_preset->get("application/trademarks");
|
||||
|
||||
HashMap<String, String> strings;
|
||||
if (!file_version.is_empty()) {
|
||||
strings["FileVersion"] = file_version;
|
||||
}
|
||||
if (!product_version.is_empty()) {
|
||||
strings["ProductVersion"] = product_version;
|
||||
}
|
||||
if (!company_name.is_empty()) {
|
||||
strings["CompanyName"] = company_name;
|
||||
}
|
||||
if (!product_name.is_empty()) {
|
||||
strings["ProductName"] = product_name;
|
||||
}
|
||||
if (!file_description.is_empty()) {
|
||||
strings["FileDescription"] = file_description;
|
||||
}
|
||||
if (!copyright.is_empty()) {
|
||||
strings["LegalCopyright"] = copyright;
|
||||
}
|
||||
if (!trademarks.is_empty()) {
|
||||
strings["LegalTrademarks"] = trademarks;
|
||||
}
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
Error TemplateModifier::_modify_template(const Ref<EditorExportPreset> &p_preset, const String &p_template_path, const String &p_icon_path) const {
|
||||
Error error;
|
||||
Ref<FileAccess> template_file = FileAccess::open(p_template_path, FileAccess::READ_WRITE, &error);
|
||||
ERR_FAIL_COND_V(error != OK, ERR_CANT_OPEN);
|
||||
|
||||
Vector<SectionEntry> section_entries = _get_section_entries(template_file);
|
||||
ERR_FAIL_COND_V(section_entries.size() < 2, ERR_CANT_OPEN);
|
||||
|
||||
// Find resource (".rsrc") and relocation (".reloc") sections, usually last two, but ".debug_*" sections (referenced as "/[n]"), symbol table, and string table can follow.
|
||||
int resource_index = section_entries.size() - 2;
|
||||
int relocations_index = section_entries.size() - 1;
|
||||
for (int i = 0; i < section_entries.size(); i++) {
|
||||
if (section_entries[i].name == ".rsrc") {
|
||||
resource_index = i;
|
||||
} else if (section_entries[i].name == ".reloc") {
|
||||
relocations_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V(section_entries[resource_index].name != ".rsrc", ERR_CANT_OPEN);
|
||||
ERR_FAIL_COND_V(section_entries[relocations_index].name != ".reloc", ERR_CANT_OPEN);
|
||||
|
||||
uint64_t original_template_size = template_file->get_length();
|
||||
|
||||
GroupIcon group_icon = _create_group_icon(p_icon_path);
|
||||
|
||||
VersionInfo version_info = _create_version_info(_get_strings(p_preset));
|
||||
|
||||
SectionEntry &resources_section_entry = section_entries.write[resource_index];
|
||||
uint32_t old_resources_size_of_raw_data = resources_section_entry.size_of_raw_data;
|
||||
Vector<uint8_t> resources = _create_resources(resources_section_entry.virtual_address, group_icon, version_info);
|
||||
resources_section_entry.virtual_size = resources.size();
|
||||
resources.resize_initialized(_snap(resources.size(), BLOCK_SIZE));
|
||||
resources_section_entry.size_of_raw_data = resources.size();
|
||||
|
||||
int32_t raw_size_delta = resources_section_entry.size_of_raw_data - old_resources_size_of_raw_data;
|
||||
uint32_t old_last_section_virtual_address = section_entries.get(section_entries.size() - 1).virtual_address;
|
||||
|
||||
// Some data (e.g. DWARF debug symbols) can be placed after the last section.
|
||||
uint32_t old_footer_offset = section_entries.get(section_entries.size() - 1).pointer_to_raw_data + section_entries.get(section_entries.size() - 1).size_of_raw_data;
|
||||
|
||||
// Copy and update sections after ".rsrc".
|
||||
Vector<Vector<uint8_t>> moved_section_data;
|
||||
uint32_t prev_virtual_address = resources_section_entry.virtual_address;
|
||||
uint32_t prev_virtual_size = resources_section_entry.virtual_size;
|
||||
for (int i = resource_index + 1; i < section_entries.size(); i++) {
|
||||
SectionEntry §ion_entry = section_entries.write[i];
|
||||
template_file->seek(section_entry.pointer_to_raw_data);
|
||||
Vector<uint8_t> data = template_file->get_buffer(section_entry.size_of_raw_data);
|
||||
moved_section_data.push_back(data);
|
||||
section_entry.pointer_to_raw_data += raw_size_delta;
|
||||
section_entry.virtual_address = prev_virtual_address + _snap(prev_virtual_size, PE_PAGE_SIZE);
|
||||
prev_virtual_address = section_entry.virtual_address;
|
||||
prev_virtual_size = section_entry.virtual_size;
|
||||
}
|
||||
|
||||
// Copy COFF symbol table and string table after the last section.
|
||||
uint32_t footer_size = template_file->get_length() - old_footer_offset;
|
||||
template_file->seek(old_footer_offset);
|
||||
Vector<uint8_t> footer;
|
||||
if (footer_size > 0) {
|
||||
footer = template_file->get_buffer(footer_size);
|
||||
}
|
||||
|
||||
uint32_t pe_header_offset = _get_pe_header_offset(template_file);
|
||||
|
||||
// Update symbol table pointer.
|
||||
template_file->seek(pe_header_offset + 12);
|
||||
uint32_t symbols_offset = template_file->get_32();
|
||||
if (symbols_offset > resources_section_entry.pointer_to_raw_data) {
|
||||
template_file->seek(pe_header_offset + 12);
|
||||
template_file->store_32(symbols_offset + raw_size_delta);
|
||||
}
|
||||
|
||||
template_file->seek(pe_header_offset + MAGIC_NUMBER_OFFSET);
|
||||
uint16_t magic_number = template_file->get_16();
|
||||
ERR_FAIL_COND_V_MSG(magic_number != 0x10b && magic_number != 0x20b, ERR_CANT_OPEN, vformat("Magic number has wrong value: %04x", magic_number));
|
||||
bool pe32plus = magic_number == 0x20b;
|
||||
|
||||
// Update image size.
|
||||
template_file->seek(pe_header_offset + SIZE_OF_INITIALIZED_DATA_OFFSET);
|
||||
uint32_t size_of_initialized_data = template_file->get_32();
|
||||
size_of_initialized_data += resources_section_entry.size_of_raw_data - old_resources_size_of_raw_data;
|
||||
template_file->seek(pe_header_offset + SIZE_OF_INITIALIZED_DATA_OFFSET);
|
||||
template_file->store_32(size_of_initialized_data);
|
||||
|
||||
template_file->seek(pe_header_offset + SIZE_OF_IMAGE_OFFSET);
|
||||
uint32_t size_of_image = template_file->get_32();
|
||||
size_of_image += section_entries.get(section_entries.size() - 1).virtual_address - old_last_section_virtual_address;
|
||||
template_file->seek(pe_header_offset + SIZE_OF_IMAGE_OFFSET);
|
||||
template_file->store_32(size_of_image);
|
||||
|
||||
uint32_t optional_header_offset = pe_header_offset + COFF_HEADER_SIZE;
|
||||
|
||||
// Update resource section size.
|
||||
template_file->seek(optional_header_offset + (pe32plus ? 132 : 116));
|
||||
template_file->store_32(resources_section_entry.virtual_size);
|
||||
|
||||
// Update relocation section size and pointer.
|
||||
template_file->seek(optional_header_offset + (pe32plus ? 152 : 136));
|
||||
template_file->store_32(section_entries[relocations_index].virtual_address);
|
||||
template_file->store_32(section_entries[relocations_index].virtual_size);
|
||||
|
||||
template_file->seek(optional_header_offset + (pe32plus ? 240 : 224) + SectionEntry::SIZE * resource_index);
|
||||
template_file->store_buffer(resources_section_entry.save());
|
||||
for (int i = resource_index + 1; i < section_entries.size(); i++) {
|
||||
template_file->seek(optional_header_offset + (pe32plus ? 240 : 224) + SectionEntry::SIZE * i);
|
||||
template_file->store_buffer(section_entries[i].save());
|
||||
}
|
||||
|
||||
// Write new resource section.
|
||||
template_file->seek(resources_section_entry.pointer_to_raw_data);
|
||||
template_file->store_buffer(resources);
|
||||
// Write the rest of sections.
|
||||
for (const Vector<uint8_t> &data : moved_section_data) {
|
||||
template_file->store_buffer(data);
|
||||
}
|
||||
// Write footer data.
|
||||
if (footer_size > 0) {
|
||||
template_file->store_buffer(footer);
|
||||
}
|
||||
|
||||
if (template_file->get_position() < original_template_size) {
|
||||
template_file->close();
|
||||
_truncate(p_template_path, section_entries.get(section_entries.size() - 1).pointer_to_raw_data + section_entries.get(section_entries.size() - 1).size_of_raw_data + footer_size);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Vector<TemplateModifier::SectionEntry> TemplateModifier::_get_section_entries(Ref<FileAccess> p_executable) const {
|
||||
Vector<SectionEntry> section_entries;
|
||||
|
||||
uint32_t pe_header_offset = _get_pe_header_offset(p_executable);
|
||||
if (pe_header_offset == 0) {
|
||||
return section_entries;
|
||||
}
|
||||
|
||||
p_executable->seek(pe_header_offset + 6);
|
||||
int num_sections = p_executable->get_16();
|
||||
p_executable->seek(pe_header_offset + 20);
|
||||
uint16_t size_of_optional_header = p_executable->get_16();
|
||||
p_executable->seek(pe_header_offset + COFF_HEADER_SIZE + size_of_optional_header);
|
||||
|
||||
for (int i = 0; i < num_sections; ++i) {
|
||||
SectionEntry section_entry;
|
||||
section_entry.load(p_executable);
|
||||
section_entries.append(section_entry);
|
||||
}
|
||||
|
||||
return section_entries;
|
||||
}
|
||||
|
||||
Error TemplateModifier::modify(const Ref<EditorExportPreset> &p_preset, const String &p_template_path, const String &p_icon_path) {
|
||||
TemplateModifier template_modifier;
|
||||
return template_modifier._modify_template(p_preset, p_template_path, p_icon_path);
|
||||
}
|
226
platform/windows/export/template_modifier.h
Normal file
226
platform/windows/export/template_modifier.h
Normal file
@@ -0,0 +1,226 @@
|
||||
/**************************************************************************/
|
||||
/* template_modifier.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/file_access.h"
|
||||
#include "editor/export/editor_export_platform_pc.h"
|
||||
|
||||
class TemplateModifier {
|
||||
const uint32_t PE_PAGE_SIZE = 4096;
|
||||
const uint32_t BLOCK_SIZE = 512;
|
||||
const uint32_t COFF_HEADER_SIZE = 24;
|
||||
const uint32_t POINTER_TO_PE_HEADER_OFFSET = 0x3c;
|
||||
// all offsets below are calculated from POINTER_TO_PE_HEADER_OFFSET value, at 0 is magic string PE (0x50450000)
|
||||
const uint32_t MAGIC_NUMBER_OFFSET = 24;
|
||||
const uint32_t SIZE_OF_INITIALIZED_DATA_OFFSET = 32;
|
||||
const uint32_t SIZE_OF_IMAGE_OFFSET = 80;
|
||||
|
||||
struct ByteStream {
|
||||
void save(uint8_t p_value, Vector<uint8_t> &r_bytes) const;
|
||||
void save(uint16_t p_value, Vector<uint8_t> &r_bytes) const;
|
||||
void save(uint32_t p_value, Vector<uint8_t> &r_bytes) const;
|
||||
void save(const String &p_value, Vector<uint8_t> &r_bytes) const;
|
||||
void save(uint32_t p_value, Vector<uint8_t> &r_bytes, uint32_t p_count) const;
|
||||
Vector<uint8_t> save() const;
|
||||
};
|
||||
|
||||
struct ResourceDirectoryTable : ByteStream {
|
||||
static const uint16_t SIZE = 16;
|
||||
|
||||
uint16_t name_entry_count = 0;
|
||||
uint16_t id_entry_count = 0;
|
||||
|
||||
Vector<uint8_t> save() const;
|
||||
};
|
||||
|
||||
struct ResourceDirectoryEntry : ByteStream {
|
||||
static const uint16_t SIZE = 8;
|
||||
static const uint32_t ICON = 0x03;
|
||||
static const uint32_t GROUP_ICON = 0x0e;
|
||||
static const uint32_t VERSION = 0x10;
|
||||
static const uint32_t ENGLISH = 0x0409;
|
||||
static const uint32_t HIGH_BIT = 0x80000000;
|
||||
|
||||
uint32_t id = 0;
|
||||
uint32_t data_offset = 0;
|
||||
bool name = false;
|
||||
bool subdirectory = false;
|
||||
|
||||
Vector<uint8_t> save() const;
|
||||
};
|
||||
|
||||
struct FixedFileInfo : ByteStream {
|
||||
uint32_t signature = 0xfeef04bd;
|
||||
uint32_t struct_version = 0x10000;
|
||||
uint32_t file_version_ms = 0;
|
||||
uint32_t file_version_ls = 0;
|
||||
uint32_t product_version_ms = 0;
|
||||
uint32_t product_version_ls = 0;
|
||||
uint32_t file_flags_mask = 0;
|
||||
uint32_t file_flags = 0;
|
||||
uint32_t file_os = 0x00000004;
|
||||
uint32_t file_type = 0x00000001;
|
||||
uint32_t file_subtype = 0;
|
||||
uint32_t file_date_ms = 0;
|
||||
uint32_t file_date_ls = 0;
|
||||
|
||||
Vector<uint8_t> save() const;
|
||||
void set_file_version(const String &p_file_version);
|
||||
void set_product_version(const String &p_product_version);
|
||||
};
|
||||
|
||||
struct Structure : ByteStream {
|
||||
uint16_t length = 0;
|
||||
uint16_t value_length = 0;
|
||||
uint16_t type = 0;
|
||||
String key;
|
||||
|
||||
Vector<uint8_t> save() const;
|
||||
Vector<uint8_t> &add_length(Vector<uint8_t> &r_bytes) const;
|
||||
};
|
||||
|
||||
struct StringStructure : Structure {
|
||||
String value;
|
||||
|
||||
Vector<uint8_t> save() const;
|
||||
StringStructure();
|
||||
StringStructure(const String &p_key, const String &p_value);
|
||||
};
|
||||
|
||||
struct StringTable : Structure {
|
||||
Vector<StringStructure> strings;
|
||||
|
||||
Vector<uint8_t> save() const;
|
||||
void put(const String &p_key, const String &p_value);
|
||||
StringTable();
|
||||
};
|
||||
|
||||
struct StringFileInfo : Structure {
|
||||
StringTable string_table;
|
||||
|
||||
Vector<uint8_t> save() const;
|
||||
StringFileInfo();
|
||||
};
|
||||
|
||||
struct Var : Structure {
|
||||
const uint32_t value = 0x04b00409;
|
||||
|
||||
Vector<uint8_t> save() const;
|
||||
Var();
|
||||
};
|
||||
|
||||
struct VarFileInfo : Structure {
|
||||
Var var;
|
||||
|
||||
Vector<uint8_t> save() const;
|
||||
VarFileInfo();
|
||||
};
|
||||
|
||||
struct VersionInfo : Structure {
|
||||
FixedFileInfo value;
|
||||
StringFileInfo string_file_info;
|
||||
VarFileInfo var_file_info;
|
||||
|
||||
Vector<uint8_t> save() const;
|
||||
VersionInfo();
|
||||
};
|
||||
|
||||
struct IconEntry : ByteStream {
|
||||
static const uint32_t SIZE = 16;
|
||||
|
||||
uint8_t width = 0;
|
||||
uint8_t height = 0;
|
||||
uint8_t colors = 0;
|
||||
uint8_t reserved = 0;
|
||||
uint16_t planes = 0;
|
||||
uint16_t bits_per_pixel = 32;
|
||||
uint32_t image_size = 0;
|
||||
uint32_t image_offset = 0;
|
||||
Vector<uint8_t> data;
|
||||
|
||||
Vector<uint8_t> save() const;
|
||||
void load(Ref<FileAccess> p_file);
|
||||
};
|
||||
|
||||
struct GroupIcon : ByteStream {
|
||||
static constexpr uint8_t SIZES[6] = { 16, 32, 48, 64, 128, 0 };
|
||||
|
||||
uint16_t reserved = 0;
|
||||
uint16_t type = 1;
|
||||
uint16_t image_count = 0;
|
||||
Vector<IconEntry> icon_entries;
|
||||
Vector<Vector<uint8_t>> images;
|
||||
|
||||
Vector<uint8_t> save() const;
|
||||
void load(Ref<FileAccess> p_icon_file);
|
||||
void fill_with_godot_blue();
|
||||
};
|
||||
|
||||
struct SectionEntry : ByteStream {
|
||||
static const uint32_t SIZE = 40;
|
||||
|
||||
String name;
|
||||
uint32_t virtual_size = 0;
|
||||
uint32_t virtual_address = 0;
|
||||
uint32_t size_of_raw_data = 0;
|
||||
uint32_t pointer_to_raw_data = 0;
|
||||
uint32_t pointer_to_relocations = 0;
|
||||
uint32_t pointer_to_line_numbers = 0;
|
||||
uint16_t number_of_relocations = 0;
|
||||
uint16_t number_of_line_numbers = 0;
|
||||
uint32_t characteristics = 0;
|
||||
|
||||
Vector<uint8_t> save() const;
|
||||
void load(Ref<FileAccess> p_file);
|
||||
};
|
||||
|
||||
struct ResourceDataEntry : ByteStream {
|
||||
static const uint16_t SIZE = 16;
|
||||
|
||||
uint32_t rva = 0;
|
||||
uint32_t size = 0;
|
||||
|
||||
Vector<uint8_t> save() const;
|
||||
};
|
||||
|
||||
uint32_t _snap(uint32_t p_value, uint32_t p_size) const;
|
||||
uint32_t _get_pe_header_offset(Ref<FileAccess> p_executable) const;
|
||||
Vector<SectionEntry> _get_section_entries(Ref<FileAccess> p_executable) const;
|
||||
GroupIcon _create_group_icon(const String &p_icon_path) const;
|
||||
VersionInfo _create_version_info(const HashMap<String, String> &p_strings) const;
|
||||
Vector<uint8_t> _create_resources(uint32_t p_virtual_address, const GroupIcon &p_group_icon, const VersionInfo &p_version_info) const;
|
||||
Error _truncate(const String &p_executable_path, uint32_t p_size) const;
|
||||
HashMap<String, String> _get_strings(const Ref<EditorExportPreset> &p_preset) const;
|
||||
Error _modify_template(const Ref<EditorExportPreset> &p_preset, const String &p_template_path, const String &p_icon_path) const;
|
||||
|
||||
public:
|
||||
static Error modify(const Ref<EditorExportPreset> &p_preset, const String &p_template_path, const String &p_icon_path);
|
||||
};
|
75
platform/windows/gl_manager_windows_angle.cpp
Normal file
75
platform/windows/gl_manager_windows_angle.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/**************************************************************************/
|
||||
/* gl_manager_windows_angle.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 "gl_manager_windows_angle.h"
|
||||
|
||||
#if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED)
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <EGL/eglext_angle.h>
|
||||
|
||||
const char *GLManagerANGLE_Windows::_get_platform_extension_name() const {
|
||||
return "EGL_ANGLE_platform_angle";
|
||||
}
|
||||
|
||||
EGLenum GLManagerANGLE_Windows::_get_platform_extension_enum() const {
|
||||
return EGL_PLATFORM_ANGLE_ANGLE;
|
||||
}
|
||||
|
||||
Vector<EGLAttrib> GLManagerANGLE_Windows::_get_platform_display_attributes() const {
|
||||
Vector<EGLAttrib> ret;
|
||||
ret.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
|
||||
ret.push_back(EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE);
|
||||
ret.push_back(EGL_NONE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EGLenum GLManagerANGLE_Windows::_get_platform_api_enum() const {
|
||||
return EGL_OPENGL_ES_API;
|
||||
}
|
||||
|
||||
Vector<EGLint> GLManagerANGLE_Windows::_get_platform_context_attribs() const {
|
||||
Vector<EGLint> ret;
|
||||
ret.push_back(EGL_CONTEXT_CLIENT_VERSION);
|
||||
ret.push_back(3);
|
||||
ret.push_back(EGL_NONE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GLManagerANGLE_Windows::window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {
|
||||
window_make_current(p_window_id);
|
||||
eglWaitNative(EGL_CORE_NATIVE_ENGINE);
|
||||
}
|
||||
|
||||
#endif // WINDOWS_ENABLED && GLES3_ENABLED
|
57
platform/windows/gl_manager_windows_angle.h
Normal file
57
platform/windows/gl_manager_windows_angle.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/**************************************************************************/
|
||||
/* gl_manager_windows_angle.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED)
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "drivers/egl/egl_manager.h"
|
||||
#include "servers/display_server.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
class GLManagerANGLE_Windows : public EGLManager {
|
||||
private:
|
||||
virtual const char *_get_platform_extension_name() const override;
|
||||
virtual EGLenum _get_platform_extension_enum() const override;
|
||||
virtual EGLenum _get_platform_api_enum() const override;
|
||||
virtual Vector<EGLAttrib> _get_platform_display_attributes() const override;
|
||||
virtual Vector<EGLint> _get_platform_context_attribs() const override;
|
||||
|
||||
public:
|
||||
void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height);
|
||||
|
||||
GLManagerANGLE_Windows() {}
|
||||
~GLManagerANGLE_Windows() {}
|
||||
};
|
||||
|
||||
#endif // WINDOWS_ENABLED && GLES3_ENABLED
|
552
platform/windows/gl_manager_windows_native.cpp
Normal file
552
platform/windows/gl_manager_windows_native.cpp
Normal file
@@ -0,0 +1,552 @@
|
||||
/**************************************************************************/
|
||||
/* gl_manager_windows_native.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 "gl_manager_windows_native.h"
|
||||
|
||||
#if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED)
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/version.h"
|
||||
|
||||
#include "thirdparty/misc/nvapi_minimal.h"
|
||||
|
||||
#include <dwmapi.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
|
||||
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
|
||||
#define WGL_CONTEXT_FLAGS_ARB 0x2094
|
||||
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
|
||||
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
|
||||
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
|
||||
|
||||
#define _WGL_CONTEXT_DEBUG_BIT_ARB 0x0001
|
||||
|
||||
typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXT)(HDC);
|
||||
typedef BOOL(APIENTRY *PFNWGLDELETECONTEXT)(HGLRC);
|
||||
typedef BOOL(APIENTRY *PFNWGLMAKECURRENT)(HDC, HGLRC);
|
||||
typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *);
|
||||
typedef void *(APIENTRY *PFNWGLGETPROCADDRESS)(LPCSTR);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const int OGL_THREAD_CONTROL_ID = 0x20C1221E;
|
||||
const int OGL_THREAD_CONTROL_DISABLE = 0x00000002;
|
||||
const int OGL_THREAD_CONTROL_ENABLE = 0x00000001;
|
||||
const int VRR_MODE_ID = 0x1194F158;
|
||||
const int VRR_MODE_FULLSCREEN_ONLY = 0x1;
|
||||
|
||||
typedef int(__cdecl *NvAPI_Initialize_t)();
|
||||
typedef int(__cdecl *NvAPI_Unload_t)();
|
||||
typedef int(__cdecl *NvAPI_GetErrorMessage_t)(unsigned int, NvAPI_ShortString);
|
||||
typedef int(__cdecl *NvAPI_DRS_CreateSession_t)(NvDRSSessionHandle *);
|
||||
typedef int(__cdecl *NvAPI_DRS_DestroySession_t)(NvDRSSessionHandle);
|
||||
typedef int(__cdecl *NvAPI_DRS_LoadSettings_t)(NvDRSSessionHandle);
|
||||
typedef int(__cdecl *NvAPI_DRS_CreateProfile_t)(NvDRSSessionHandle, NVDRS_PROFILE *, NvDRSProfileHandle *);
|
||||
typedef int(__cdecl *NvAPI_DRS_CreateApplication_t)(NvDRSSessionHandle, NvDRSProfileHandle, NVDRS_APPLICATION *);
|
||||
typedef int(__cdecl *NvAPI_DRS_SaveSettings_t)(NvDRSSessionHandle);
|
||||
typedef int(__cdecl *NvAPI_DRS_SetSetting_t)(NvDRSSessionHandle, NvDRSProfileHandle, NVDRS_SETTING *);
|
||||
typedef int(__cdecl *NvAPI_DRS_FindProfileByName_t)(NvDRSSessionHandle, NvAPI_UnicodeString, NvDRSProfileHandle *);
|
||||
typedef int(__cdecl *NvAPI_DRS_GetApplicationInfo_t)(NvDRSSessionHandle, NvDRSProfileHandle, NvAPI_UnicodeString, NVDRS_APPLICATION *);
|
||||
typedef int(__cdecl *NvAPI_DRS_DeleteProfile_t)(NvDRSSessionHandle, NvDRSProfileHandle);
|
||||
NvAPI_GetErrorMessage_t NvAPI_GetErrorMessage__;
|
||||
|
||||
static bool nvapi_err_check(const char *msg, int status) {
|
||||
if (status != 0) {
|
||||
if (OS::get_singleton()->is_stdout_verbose()) {
|
||||
NvAPI_ShortString err_desc = { 0 };
|
||||
NvAPI_GetErrorMessage__(status, err_desc);
|
||||
print_verbose(vformat("%s: %s(code %d)", msg, err_desc, status));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// On windows we have to customize the NVIDIA application profile:
|
||||
// * disable threaded optimization when using NVIDIA cards to avoid stuttering, see
|
||||
// https://stackoverflow.com/questions/36959508/nvidia-graphics-driver-causing-noticeable-frame-stuttering/37632948
|
||||
// https://github.com/Ryujinx/Ryujinx/blob/master/src/Ryujinx.Common/GraphicsDriver/NVThreadedOptimization.cs
|
||||
// * disable G-SYNC in windowed mode, as it results in unstable editor refresh rates
|
||||
void GLManagerNative_Windows::_nvapi_setup_profile() {
|
||||
HMODULE nvapi = nullptr;
|
||||
#ifdef _WIN64
|
||||
nvapi = LoadLibraryA("nvapi64.dll");
|
||||
#else
|
||||
nvapi = LoadLibraryA("nvapi.dll");
|
||||
#endif
|
||||
|
||||
if (nvapi == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
void *(__cdecl * NvAPI_QueryInterface)(unsigned int interface_id) = nullptr;
|
||||
|
||||
NvAPI_QueryInterface = (void *(__cdecl *)(unsigned int))(void *)GetProcAddress(nvapi, "nvapi_QueryInterface");
|
||||
|
||||
if (NvAPI_QueryInterface == nullptr) {
|
||||
print_verbose("Error getting NVAPI NvAPI_QueryInterface");
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup NVAPI function pointers
|
||||
NvAPI_Initialize_t NvAPI_Initialize = (NvAPI_Initialize_t)NvAPI_QueryInterface(0x0150E828);
|
||||
NvAPI_GetErrorMessage__ = (NvAPI_GetErrorMessage_t)NvAPI_QueryInterface(0x6C2D048C);
|
||||
NvAPI_DRS_CreateSession_t NvAPI_DRS_CreateSession = (NvAPI_DRS_CreateSession_t)NvAPI_QueryInterface(0x0694D52E);
|
||||
NvAPI_DRS_DestroySession_t NvAPI_DRS_DestroySession = (NvAPI_DRS_DestroySession_t)NvAPI_QueryInterface(0xDAD9CFF8);
|
||||
NvAPI_Unload_t NvAPI_Unload = (NvAPI_Unload_t)NvAPI_QueryInterface(0xD22BDD7E);
|
||||
NvAPI_DRS_LoadSettings_t NvAPI_DRS_LoadSettings = (NvAPI_DRS_LoadSettings_t)NvAPI_QueryInterface(0x375DBD6B);
|
||||
NvAPI_DRS_CreateProfile_t NvAPI_DRS_CreateProfile = (NvAPI_DRS_CreateProfile_t)NvAPI_QueryInterface(0xCC176068);
|
||||
NvAPI_DRS_CreateApplication_t NvAPI_DRS_CreateApplication = (NvAPI_DRS_CreateApplication_t)NvAPI_QueryInterface(0x4347A9DE);
|
||||
NvAPI_DRS_SaveSettings_t NvAPI_DRS_SaveSettings = (NvAPI_DRS_SaveSettings_t)NvAPI_QueryInterface(0xFCBC7E14);
|
||||
NvAPI_DRS_SetSetting_t NvAPI_DRS_SetSetting = (NvAPI_DRS_SetSetting_t)NvAPI_QueryInterface(0x577DD202);
|
||||
NvAPI_DRS_FindProfileByName_t NvAPI_DRS_FindProfileByName = (NvAPI_DRS_FindProfileByName_t)NvAPI_QueryInterface(0x7E4A9A0B);
|
||||
NvAPI_DRS_GetApplicationInfo_t NvAPI_DRS_GetApplicationInfo = (NvAPI_DRS_GetApplicationInfo_t)NvAPI_QueryInterface(0xED1F8C69);
|
||||
NvAPI_DRS_DeleteProfile_t NvAPI_DRS_DeleteProfile = (NvAPI_DRS_DeleteProfile_t)NvAPI_QueryInterface(0x17093206);
|
||||
|
||||
if (!nvapi_err_check("NVAPI: Init failed", NvAPI_Initialize())) {
|
||||
return;
|
||||
}
|
||||
|
||||
print_verbose("NVAPI: Init OK!");
|
||||
|
||||
NvDRSSessionHandle session_handle;
|
||||
|
||||
if (NvAPI_DRS_CreateSession == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nvapi_err_check("NVAPI: Error creating DRS session", NvAPI_DRS_CreateSession(&session_handle))) {
|
||||
NvAPI_Unload();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nvapi_err_check("NVAPI: Error loading DRS settings", NvAPI_DRS_LoadSettings(session_handle))) {
|
||||
NvAPI_DRS_DestroySession(session_handle);
|
||||
NvAPI_Unload();
|
||||
return;
|
||||
}
|
||||
|
||||
String app_executable_name = OS::get_singleton()->get_executable_path().get_file();
|
||||
String app_profile_name = GLOBAL_GET("application/config/name");
|
||||
// We need a name anyways, so let's use the engine name if an application name is not available
|
||||
// (this is used mostly by the Project Manager)
|
||||
if (app_profile_name.is_empty()) {
|
||||
app_profile_name = GODOT_VERSION_NAME;
|
||||
}
|
||||
String old_profile_name = app_profile_name + " Nvidia Profile";
|
||||
Char16String app_profile_name_u16 = app_profile_name.utf16();
|
||||
Char16String old_profile_name_u16 = old_profile_name.utf16();
|
||||
Char16String app_executable_name_u16 = app_executable_name.utf16();
|
||||
|
||||
// A previous error in app creation logic could result in invalid profiles,
|
||||
// clean these if they exist before proceeding.
|
||||
NvDRSProfileHandle old_profile_handle;
|
||||
|
||||
int old_status = NvAPI_DRS_FindProfileByName(session_handle, (NvU16 *)(old_profile_name_u16.ptrw()), &old_profile_handle);
|
||||
|
||||
if (old_status == 0) {
|
||||
print_verbose("NVAPI: Deleting old profile...");
|
||||
|
||||
if (!nvapi_err_check("NVAPI: Error deleting old profile", NvAPI_DRS_DeleteProfile(session_handle, old_profile_handle))) {
|
||||
NvAPI_DRS_DestroySession(session_handle);
|
||||
NvAPI_Unload();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nvapi_err_check("NVAPI: Error deleting old profile", NvAPI_DRS_SaveSettings(session_handle))) {
|
||||
NvAPI_DRS_DestroySession(session_handle);
|
||||
NvAPI_Unload();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NvDRSProfileHandle profile_handle = nullptr;
|
||||
|
||||
int profile_status = NvAPI_DRS_FindProfileByName(session_handle, (NvU16 *)(app_profile_name_u16.ptrw()), &profile_handle);
|
||||
|
||||
if (profile_status != 0) {
|
||||
print_verbose("NVAPI: Profile not found, creating...");
|
||||
|
||||
NVDRS_PROFILE profile_info;
|
||||
profile_info.version = NVDRS_PROFILE_VER;
|
||||
profile_info.isPredefined = 0;
|
||||
memcpy(profile_info.profileName, app_profile_name_u16.get_data(), sizeof(char16_t) * app_profile_name_u16.size());
|
||||
|
||||
if (!nvapi_err_check("NVAPI: Error creating profile", NvAPI_DRS_CreateProfile(session_handle, &profile_info, &profile_handle))) {
|
||||
NvAPI_DRS_DestroySession(session_handle);
|
||||
NvAPI_Unload();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NVDRS_APPLICATION_V4 app;
|
||||
app.version = NVDRS_APPLICATION_VER_V4;
|
||||
|
||||
int app_status = NvAPI_DRS_GetApplicationInfo(session_handle, profile_handle, (NvU16 *)(app_executable_name_u16.ptrw()), &app);
|
||||
|
||||
if (app_status != 0) {
|
||||
print_verbose("NVAPI: Application not found in profile, creating...");
|
||||
|
||||
app.isPredefined = 0;
|
||||
memcpy(app.appName, app_executable_name_u16.get_data(), sizeof(char16_t) * app_executable_name_u16.size());
|
||||
memcpy(app.launcher, L"", sizeof(wchar_t));
|
||||
memcpy(app.fileInFolder, L"", sizeof(wchar_t));
|
||||
|
||||
if (!nvapi_err_check("NVAPI: Error creating application", NvAPI_DRS_CreateApplication(session_handle, profile_handle, &app))) {
|
||||
NvAPI_DRS_DestroySession(session_handle);
|
||||
NvAPI_Unload();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NVDRS_SETTING ogl_thread_control_setting = {};
|
||||
ogl_thread_control_setting.version = NVDRS_SETTING_VER;
|
||||
ogl_thread_control_setting.settingId = OGL_THREAD_CONTROL_ID;
|
||||
ogl_thread_control_setting.settingType = NVDRS_DWORD_TYPE;
|
||||
int thread_control_val = OGL_THREAD_CONTROL_DISABLE;
|
||||
if (!GLOBAL_GET("rendering/gl_compatibility/nvidia_disable_threaded_optimization")) {
|
||||
thread_control_val = OGL_THREAD_CONTROL_ENABLE;
|
||||
}
|
||||
ogl_thread_control_setting.u32CurrentValue = thread_control_val;
|
||||
|
||||
if (!nvapi_err_check("NVAPI: Error calling NvAPI_DRS_SetSetting", NvAPI_DRS_SetSetting(session_handle, profile_handle, &ogl_thread_control_setting))) {
|
||||
NvAPI_DRS_DestroySession(session_handle);
|
||||
NvAPI_Unload();
|
||||
return;
|
||||
}
|
||||
|
||||
NVDRS_SETTING vrr_mode_setting = {};
|
||||
vrr_mode_setting.version = NVDRS_SETTING_VER;
|
||||
vrr_mode_setting.settingId = VRR_MODE_ID;
|
||||
vrr_mode_setting.settingType = NVDRS_DWORD_TYPE;
|
||||
vrr_mode_setting.u32CurrentValue = VRR_MODE_FULLSCREEN_ONLY;
|
||||
|
||||
if (!nvapi_err_check("NVAPI: Error calling NvAPI_DRS_SetSetting", NvAPI_DRS_SetSetting(session_handle, profile_handle, &vrr_mode_setting))) {
|
||||
NvAPI_DRS_DestroySession(session_handle);
|
||||
NvAPI_Unload();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nvapi_err_check("NVAPI: Error saving settings", NvAPI_DRS_SaveSettings(session_handle))) {
|
||||
NvAPI_DRS_DestroySession(session_handle);
|
||||
NvAPI_Unload();
|
||||
return;
|
||||
}
|
||||
|
||||
if (thread_control_val == OGL_THREAD_CONTROL_DISABLE) {
|
||||
print_verbose("NVAPI: Disabled OpenGL threaded optimization successfully");
|
||||
} else {
|
||||
print_verbose("NVAPI: Enabled OpenGL threaded optimization successfully");
|
||||
}
|
||||
print_verbose("NVAPI: Disabled G-SYNC for windowed mode successfully");
|
||||
|
||||
NvAPI_DRS_DestroySession(session_handle);
|
||||
}
|
||||
|
||||
int GLManagerNative_Windows::_find_or_create_display(GLWindow &win) {
|
||||
// find display NYI, only 1 supported so far
|
||||
if (_displays.size()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// create
|
||||
GLDisplay d_temp = {};
|
||||
_displays.push_back(d_temp);
|
||||
int new_display_id = _displays.size() - 1;
|
||||
|
||||
// create context
|
||||
GLDisplay &d = _displays[new_display_id];
|
||||
Error err = _create_context(win, d);
|
||||
|
||||
if (err != OK) {
|
||||
// not good
|
||||
// delete the _display?
|
||||
_displays.remove_at(new_display_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return new_display_id;
|
||||
}
|
||||
|
||||
static Error _configure_pixel_format(HDC hDC) {
|
||||
static PIXELFORMATDESCRIPTOR pfd = {
|
||||
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
|
||||
1,
|
||||
PFD_DRAW_TO_WINDOW | // Format Must Support Window
|
||||
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
|
||||
PFD_DOUBLEBUFFER,
|
||||
(BYTE)PFD_TYPE_RGBA,
|
||||
(BYTE)(OS::get_singleton()->is_layered_allowed() ? 32 : 24),
|
||||
(BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, // Color Bits Ignored
|
||||
(BYTE)(OS::get_singleton()->is_layered_allowed() ? 8 : 0), // Alpha Buffer
|
||||
(BYTE)0, // Shift Bit Ignored
|
||||
(BYTE)0, // No Accumulation Buffer
|
||||
(BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, // Accumulation Bits Ignored
|
||||
(BYTE)24, // 24Bit Z-Buffer (Depth Buffer)
|
||||
(BYTE)0, // No Stencil Buffer
|
||||
(BYTE)0, // No Auxiliary Buffer
|
||||
(BYTE)PFD_MAIN_PLANE, // Main Drawing Layer
|
||||
(BYTE)0, // Reserved
|
||||
0, 0, 0 // Layer Masks Ignored
|
||||
};
|
||||
|
||||
int pixel_format = ChoosePixelFormat(hDC, &pfd);
|
||||
if (!pixel_format) // Did Windows Find A Matching Pixel Format?
|
||||
{
|
||||
return ERR_CANT_CREATE; // Return FALSE
|
||||
}
|
||||
|
||||
BOOL ret = SetPixelFormat(hDC, pixel_format, &pfd);
|
||||
if (!ret) // Are We Able To Set The Pixel Format?
|
||||
{
|
||||
return ERR_CANT_CREATE; // Return FALSE
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
PFNWGLCREATECONTEXT gd_wglCreateContext;
|
||||
PFNWGLMAKECURRENT gd_wglMakeCurrent;
|
||||
PFNWGLDELETECONTEXT gd_wglDeleteContext;
|
||||
PFNWGLGETPROCADDRESS gd_wglGetProcAddress;
|
||||
|
||||
Error GLManagerNative_Windows::_create_context(GLWindow &win, GLDisplay &gl_display) {
|
||||
Error err = _configure_pixel_format(win.hDC);
|
||||
if (err != OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
HMODULE module = LoadLibraryW(L"opengl32.dll");
|
||||
if (!module) {
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
gd_wglCreateContext = (PFNWGLCREATECONTEXT)(void *)GetProcAddress(module, "wglCreateContext");
|
||||
gd_wglMakeCurrent = (PFNWGLMAKECURRENT)(void *)GetProcAddress(module, "wglMakeCurrent");
|
||||
gd_wglDeleteContext = (PFNWGLDELETECONTEXT)(void *)GetProcAddress(module, "wglDeleteContext");
|
||||
gd_wglGetProcAddress = (PFNWGLGETPROCADDRESS)(void *)GetProcAddress(module, "wglGetProcAddress");
|
||||
if (!gd_wglCreateContext || !gd_wglMakeCurrent || !gd_wglDeleteContext || !gd_wglGetProcAddress) {
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
|
||||
gl_display.hRC = gd_wglCreateContext(win.hDC);
|
||||
if (!gl_display.hRC) // Are We Able To Get A Rendering Context?
|
||||
{
|
||||
return ERR_CANT_CREATE; // Return FALSE
|
||||
}
|
||||
|
||||
if (!gd_wglMakeCurrent(win.hDC, gl_display.hRC)) {
|
||||
ERR_PRINT("Could not attach OpenGL context to newly created window: " + format_error_message(GetLastError()));
|
||||
}
|
||||
|
||||
int attribs[] = {
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB, 3, //we want a 3.3 context
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
|
||||
//and it shall be forward compatible so that we can only use up to date functionality
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB /*| _WGL_CONTEXT_DEBUG_BIT_ARB*/,
|
||||
0
|
||||
}; //zero indicates the end of the array
|
||||
|
||||
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = nullptr; //pointer to the method
|
||||
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)gd_wglGetProcAddress("wglCreateContextAttribsARB");
|
||||
|
||||
if (wglCreateContextAttribsARB == nullptr) //OpenGL 3.0 is not supported
|
||||
{
|
||||
gd_wglDeleteContext(gl_display.hRC);
|
||||
gl_display.hRC = nullptr;
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
|
||||
HGLRC new_hRC = wglCreateContextAttribsARB(win.hDC, nullptr, attribs);
|
||||
if (!new_hRC) {
|
||||
gd_wglDeleteContext(gl_display.hRC);
|
||||
gl_display.hRC = nullptr;
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
|
||||
if (!gd_wglMakeCurrent(win.hDC, nullptr)) {
|
||||
ERR_PRINT("Could not detach OpenGL context from newly created window: " + format_error_message(GetLastError()));
|
||||
}
|
||||
|
||||
gd_wglDeleteContext(gl_display.hRC);
|
||||
gl_display.hRC = new_hRC;
|
||||
|
||||
if (!gd_wglMakeCurrent(win.hDC, gl_display.hRC)) // Try to activate the rendering context.
|
||||
{
|
||||
ERR_PRINT("Could not attach OpenGL context to newly created window with replaced OpenGL context: " + format_error_message(GetLastError()));
|
||||
gd_wglDeleteContext(gl_display.hRC);
|
||||
gl_display.hRC = nullptr;
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
|
||||
if (!wglSwapIntervalEXT) {
|
||||
wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)gd_wglGetProcAddress("wglSwapIntervalEXT");
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
Error GLManagerNative_Windows::window_create(DisplayServer::WindowID p_window_id, HWND p_hwnd, HINSTANCE p_hinstance, int p_width, int p_height) {
|
||||
HDC hDC = GetDC(p_hwnd);
|
||||
if (!hDC) {
|
||||
return ERR_CANT_CREATE;
|
||||
}
|
||||
|
||||
// configure the HDC to use a compatible pixel format
|
||||
Error result = _configure_pixel_format(hDC);
|
||||
if (result != OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
GLWindow win;
|
||||
win.hwnd = p_hwnd;
|
||||
win.hDC = hDC;
|
||||
|
||||
win.gldisplay_id = _find_or_create_display(win);
|
||||
|
||||
if (win.gldisplay_id == -1) {
|
||||
return FAILED;
|
||||
}
|
||||
|
||||
// WARNING: `p_window_id` is an eternally growing integer since popup windows keep coming and going
|
||||
// and each of them has a higher id than the previous, so it must be used in a map not a vector.
|
||||
_windows[p_window_id] = win;
|
||||
|
||||
// make current
|
||||
window_make_current(p_window_id);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
void GLManagerNative_Windows::window_destroy(DisplayServer::WindowID p_window_id) {
|
||||
GLWindow &win = get_window(p_window_id);
|
||||
if (_current_window == &win) {
|
||||
_current_window = nullptr;
|
||||
}
|
||||
_windows.erase(p_window_id);
|
||||
}
|
||||
|
||||
void GLManagerNative_Windows::release_current() {
|
||||
if (!_current_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gd_wglMakeCurrent(_current_window->hDC, nullptr)) {
|
||||
ERR_PRINT("Could not detach OpenGL context from window marked current: " + format_error_message(GetLastError()));
|
||||
}
|
||||
|
||||
_current_window = nullptr;
|
||||
}
|
||||
|
||||
void GLManagerNative_Windows::window_make_current(DisplayServer::WindowID p_window_id) {
|
||||
if (p_window_id == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// crash if our data structures are out of sync, i.e. not found
|
||||
GLWindow &win = _windows[p_window_id];
|
||||
|
||||
// noop
|
||||
if (&win == _current_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
const GLDisplay &disp = get_display(win.gldisplay_id);
|
||||
if (!gd_wglMakeCurrent(win.hDC, disp.hRC)) {
|
||||
ERR_PRINT("Could not switch OpenGL context to other window: " + format_error_message(GetLastError()));
|
||||
}
|
||||
|
||||
_current_window = &win;
|
||||
}
|
||||
|
||||
void GLManagerNative_Windows::swap_buffers() {
|
||||
SwapBuffers(_current_window->hDC);
|
||||
}
|
||||
|
||||
Error GLManagerNative_Windows::initialize() {
|
||||
_nvapi_setup_profile();
|
||||
return OK;
|
||||
}
|
||||
|
||||
void GLManagerNative_Windows::set_use_vsync(DisplayServer::WindowID p_window_id, bool p_use) {
|
||||
GLWindow &win = get_window(p_window_id);
|
||||
|
||||
if (&win != _current_window) {
|
||||
window_make_current(p_window_id);
|
||||
}
|
||||
|
||||
if (wglSwapIntervalEXT) {
|
||||
win.use_vsync = p_use;
|
||||
|
||||
if (!wglSwapIntervalEXT(p_use ? 1 : 0)) {
|
||||
WARN_PRINT_ONCE("Could not set V-Sync mode, as changing V-Sync mode is not supported by the graphics driver.");
|
||||
}
|
||||
} else {
|
||||
WARN_PRINT_ONCE("Could not set V-Sync mode, as changing V-Sync mode is not supported by the graphics driver.");
|
||||
}
|
||||
}
|
||||
|
||||
bool GLManagerNative_Windows::is_using_vsync(DisplayServer::WindowID p_window_id) const {
|
||||
return get_window(p_window_id).use_vsync;
|
||||
}
|
||||
|
||||
HDC GLManagerNative_Windows::get_hdc(DisplayServer::WindowID p_window_id) {
|
||||
return get_window(p_window_id).hDC;
|
||||
}
|
||||
|
||||
HGLRC GLManagerNative_Windows::get_hglrc(DisplayServer::WindowID p_window_id) {
|
||||
const GLWindow &win = get_window(p_window_id);
|
||||
const GLDisplay &disp = get_display(win.gldisplay_id);
|
||||
return disp.hRC;
|
||||
}
|
||||
|
||||
GLManagerNative_Windows::GLManagerNative_Windows() {
|
||||
direct_render = false;
|
||||
glx_minor = glx_major = 0;
|
||||
_current_window = nullptr;
|
||||
}
|
||||
|
||||
GLManagerNative_Windows::~GLManagerNative_Windows() {
|
||||
release_current();
|
||||
}
|
||||
|
||||
#endif // WINDOWS_ENABLED && GLES3_ENABLED
|
105
platform/windows/gl_manager_windows_native.h
Normal file
105
platform/windows/gl_manager_windows_native.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/**************************************************************************/
|
||||
/* gl_manager_windows_native.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED)
|
||||
|
||||
#include "core/os/os.h"
|
||||
#include "core/templates/local_vector.h"
|
||||
#include "servers/display_server.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
typedef bool(APIENTRY *PFNWGLSWAPINTERVALEXTPROC)(int interval);
|
||||
typedef int(APIENTRY *PFNWGLGETSWAPINTERVALEXTPROC)(void);
|
||||
|
||||
class GLManagerNative_Windows {
|
||||
private:
|
||||
// any data specific to the window
|
||||
struct GLWindow {
|
||||
bool use_vsync = false;
|
||||
|
||||
// windows specific
|
||||
HDC hDC;
|
||||
HWND hwnd;
|
||||
|
||||
int gldisplay_id = 0;
|
||||
};
|
||||
|
||||
struct GLDisplay {
|
||||
// windows specific
|
||||
HGLRC hRC;
|
||||
};
|
||||
|
||||
RBMap<DisplayServer::WindowID, GLWindow> _windows;
|
||||
LocalVector<GLDisplay> _displays;
|
||||
|
||||
GLWindow *_current_window = nullptr;
|
||||
|
||||
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = nullptr;
|
||||
|
||||
GLWindow &get_window(unsigned int id) { return _windows[id]; }
|
||||
const GLWindow &get_window(unsigned int id) const { return _windows[id]; }
|
||||
|
||||
const GLDisplay &get_current_display() const { return _displays[_current_window->gldisplay_id]; }
|
||||
const GLDisplay &get_display(unsigned int id) { return _displays[id]; }
|
||||
|
||||
bool direct_render;
|
||||
int glx_minor, glx_major;
|
||||
|
||||
private:
|
||||
void _nvapi_setup_profile();
|
||||
int _find_or_create_display(GLWindow &win);
|
||||
Error _create_context(GLWindow &win, GLDisplay &gl_display);
|
||||
|
||||
public:
|
||||
Error window_create(DisplayServer::WindowID p_window_id, HWND p_hwnd, HINSTANCE p_hinstance, int p_width, int p_height);
|
||||
void window_destroy(DisplayServer::WindowID p_window_id);
|
||||
void window_resize(DisplayServer::WindowID p_window_id, int p_width, int p_height) {}
|
||||
|
||||
void release_current();
|
||||
void swap_buffers();
|
||||
|
||||
void window_make_current(DisplayServer::WindowID p_window_id);
|
||||
|
||||
Error initialize();
|
||||
|
||||
void set_use_vsync(DisplayServer::WindowID p_window_id, bool p_use);
|
||||
bool is_using_vsync(DisplayServer::WindowID p_window_id) const;
|
||||
|
||||
HDC get_hdc(DisplayServer::WindowID p_window_id);
|
||||
HGLRC get_hglrc(DisplayServer::WindowID p_window_id);
|
||||
|
||||
GLManagerNative_Windows();
|
||||
~GLManagerNative_Windows();
|
||||
};
|
||||
|
||||
#endif // WINDOWS_ENABLED && GLES3_ENABLED
|
BIN
platform/windows/godot.ico
Normal file
BIN
platform/windows/godot.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 139 KiB |
438
platform/windows/godot.natvis
Normal file
438
platform/windows/godot.natvis
Normal file
@@ -0,0 +1,438 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="Ref<*>">
|
||||
<SmartPointer Usage="Minimal">reference</SmartPointer>
|
||||
<DisplayString Condition="!reference">[empty]</DisplayString>
|
||||
<DisplayString Condition="!!reference">{*reference}</DisplayString>
|
||||
<Expand>
|
||||
<Item Condition="!!reference" Name="[ptr]">reference</Item>
|
||||
<Item Condition="!!reference" Name="[refcount]">reference->refcount.count.value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Vector<*>">
|
||||
<Expand>
|
||||
<Item Name="[size]">_cowdata._ptr ? (((const unsigned long long *)(_cowdata._ptr))[-1]) : 0</Item>
|
||||
<ArrayItems>
|
||||
<Size>_cowdata._ptr ? (((const unsigned long long *)(_cowdata._ptr))[-1]) : 0</Size>
|
||||
<ValuePointer>($T1 *) _cowdata._ptr</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Array">
|
||||
<Expand>
|
||||
<Item Name="[size]">_p->array._cowdata._ptr ? (((const unsigned long long *)(_p->array._cowdata._ptr))[-1]) : 0</Item>
|
||||
<ArrayItems>
|
||||
<Size>_p->array._cowdata._ptr ? (((const unsigned long long *)(_p->array._cowdata._ptr))[-1]) : 0</Size>
|
||||
<ValuePointer>(Variant *) _p->array._cowdata._ptr</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Dictionary">
|
||||
<Expand>
|
||||
<Item Name="[size]">_p && _p->variant_map.head_element ? _p->variant_map.num_elements : 0</Item>
|
||||
<LinkedListItems>
|
||||
<Size>_p && _p->variant_map.head_element ? _p->variant_map.num_elements : 0</Size>
|
||||
<HeadPointer>_p ? _p->variant_map.head_element : nullptr</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode Name="[{data.key}]">(*this),view(MapHelper)</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="LocalVector<*>">
|
||||
<Expand>
|
||||
<Item Name="[size]">count</Item>
|
||||
<ArrayItems>
|
||||
<Size>count</Size>
|
||||
<ValuePointer>data</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="List<*>">
|
||||
<Expand>
|
||||
<Item Name="[size]">_data ? (_data->size_cache) : 0</Item>
|
||||
<LinkedListItems>
|
||||
<Size>_data ? (_data->size_cache) : 0</Size>
|
||||
<HeadPointer>_data->first</HeadPointer>
|
||||
<NextPointer>next_ptr</NextPointer>
|
||||
<ValueNode>value</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="NodePath">
|
||||
<DisplayString Condition="!data">[empty]</DisplayString>
|
||||
<DisplayString Condition="!!data">{{[absolute] = {data->absolute} [path] = {data->path,view(NodePathHelper)} [subpath] = {data->subpath,view(NodePathHelper)}}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[path]">data->path,view(NodePathHelper)</Item>
|
||||
<Item Name="[subpath]">data->subpath,view(NodePathHelper)</Item>
|
||||
<Item Name="[absolute]">data->absolute</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Vector<StringName>" IncludeView="NodePathHelper">
|
||||
<Expand>
|
||||
<ArrayItems>
|
||||
<Size>_cowdata._ptr ? (((const unsigned long long *)(_cowdata._ptr))[-1]) : 0</Size>
|
||||
<ValuePointer>((StringName *)_cowdata._ptr),view(NodePathHelper)</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="StringName" IncludeView="NodePathHelper">
|
||||
<DisplayString Condition="_data && _data->cname">{_data->cname,s8b}</DisplayString>
|
||||
<DisplayString Condition="_data && !_data->cname">{_data->name,s32b}</DisplayString>
|
||||
<DisplayString Condition="!_data">[empty]</DisplayString>
|
||||
<StringView Condition="_data && _data->cname">_data->cname,s8b</StringView>
|
||||
<StringView Condition="_data && !_data->cname">_data->name,s32b</StringView>
|
||||
</Type>
|
||||
|
||||
<Type Name="HashSet<*,*,*>">
|
||||
<Expand>
|
||||
<Item Name="[size]">num_elements</Item>
|
||||
<ArrayItems>
|
||||
<Size>num_elements</Size>
|
||||
<ValuePointer>($T1 *) keys._cowdata._ptr</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="HashMapElement<*,*>">
|
||||
<DisplayString>{{Key = {($T1 *) &data.key} Value = {($T2 *) &data.value}}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[key]">($T1 *) &data.key</Item>
|
||||
<Item Name="[value]">($T2 *) &data.value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- elements displayed by index -->
|
||||
<Type Name="HashMap<*,*,*,*,*>" Priority="Medium">
|
||||
<Expand>
|
||||
<Item Name="[size]">head_element ? num_elements : 0</Item>
|
||||
<LinkedListItems>
|
||||
<Size>head_element ? num_elements : 0</Size>
|
||||
<HeadPointer>head_element</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>(*this)</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- elements by key:value -->
|
||||
<!-- show elements by index by specifying "ShowElementsByIndex"-->
|
||||
<Type Name="HashMap<*,*,*,*,*>" ExcludeView="ShowElementsByIndex" Priority="MediumHigh">
|
||||
<Expand>
|
||||
<Item Name="[size]">head_element ? num_elements : 0</Item>
|
||||
<LinkedListItems>
|
||||
<Size>head_element ? num_elements : 0</Size>
|
||||
<HeadPointer>head_element</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode Name="[{data.key}]">(*this),view(MapHelper)</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="KeyValue<*,*>" IncludeView="MapHelper">
|
||||
<DisplayString>{value}</DisplayString>
|
||||
</Type>
|
||||
|
||||
<Type Name="HashMapElement<*,*>" IncludeView="MapHelper">
|
||||
<DisplayString>{data.value}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[key]">($T1 *) &data.key</Item>
|
||||
<Item Name="[value]">($T2 *) &data.value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="VMap<*,*>">
|
||||
<Expand>
|
||||
<Item Condition="_cowdata._ptr" Name="[size]">*(reinterpret_cast<long long*>(_cowdata._ptr) - 1)</Item>
|
||||
<ArrayItems Condition="_cowdata._ptr">
|
||||
<Size>*(reinterpret_cast<long long*>(_cowdata._ptr) - 1)</Size>
|
||||
<ValuePointer>reinterpret_cast<VMap<$T1,$T2>::Pair*>(_cowdata._ptr)</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="VMap<Callable,*>::Pair">
|
||||
<DisplayString Condition="dynamic_cast<CallableCustomMethodPointerBase*>(key.custom)">{dynamic_cast<CallableCustomMethodPointerBase*>(key.custom)->text}</DisplayString>
|
||||
</Type>
|
||||
|
||||
|
||||
<Type Name="Variant">
|
||||
<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::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::TRANSFORM3D">{_data._transform3d}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PROJECTION">{_data._projection}</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::VECTOR4">{*(Vector4 *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PLANE">{*(Plane *)_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::STRING_NAME">{*(StringName *)_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::OBJECT">{*(*reinterpret_cast<ObjData*>(&_data._mem[0])).obj}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::DICTIONARY">{*(Dictionary *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::ARRAY">{*(Array *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_BYTE_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<unsigned char>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_INT32_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<int>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_INT64_ARRAY">{*reinterpret_cast<PackedInt64Array *>(&_data.packed_array[1])}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_FLOAT32_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<float>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_FLOAT64_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<double>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_STRING_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<String>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_VECTOR2_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<Vector2>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_VECTOR3_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<Vector3>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_COLOR_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<Color>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_VECTOR4_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<Vector4>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type < 0 || type >= Variant::VARIANT_MAX">[INVALID]</DisplayString>
|
||||
|
||||
<StringView Condition="type == Variant::STRING && ((String *)(_data._mem))->_cowdata._ptr">((String *)(_data._mem))->_cowdata._ptr,s32</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::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::TRANSFORM3D">_data._transform3d</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::QUATERNION">*(Quaternion *)_data._mem</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::COLOR">*(Color *)_data._mem</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::STRING_NAME">*(StringName *)_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::OBJECT">*(*reinterpret_cast<ObjData*>(&_data._mem[0])).obj</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::PACKED_BYTE_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<unsigned char>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_INT32_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<int>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_INT64_ARRAY">*reinterpret_cast<PackedInt64Array *>(&_data.packed_array[1])</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_FLOAT32_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<float>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_FLOAT64_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<double>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_STRING_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<String>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_VECTOR2_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<Vector2>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_VECTOR3_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<Vector3>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_COLOR_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<Color>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_VECTOR4_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<Vector4>*>(_data.packed_array)->array</Item>
|
||||
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="String">
|
||||
<DisplayString Condition="_cowdata._ptr == 0">[empty]</DisplayString>
|
||||
<DisplayString Condition="_cowdata._ptr != 0">{_cowdata._ptr,s32}</DisplayString>
|
||||
<StringView Condition="_cowdata._ptr != 0">_cowdata._ptr,s32</StringView>
|
||||
</Type>
|
||||
|
||||
<Type Name="godot::String">
|
||||
<DisplayString>{*reinterpret_cast<void**>(opaque),s32}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="opaque_ptr">*reinterpret_cast<void**>(opaque)</Item>
|
||||
<Item Name="string">*reinterpret_cast<void**>(opaque),s32</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="StringName">
|
||||
<DisplayString Condition="_data && _data->cname">{_data->cname,na}</DisplayString>
|
||||
<DisplayString Condition="_data && !_data->cname">{_data->name,s32}</DisplayString>
|
||||
<DisplayString Condition="!_data">[empty]</DisplayString>
|
||||
<StringView Condition="_data && _data->cname">_data->cname,na</StringView>
|
||||
<StringView Condition="_data && !_data->cname">_data->name,s32</StringView>
|
||||
</Type>
|
||||
|
||||
<!-- can't cast the opaque to ::StringName because Natvis does not support global namespace specifier? -->
|
||||
<Type Name="godot::StringName">
|
||||
<DisplayString Condition="(*reinterpret_cast<const char***>(opaque))[1]">{(*reinterpret_cast<const char***>(opaque))[1],s8}</DisplayString>
|
||||
<DisplayString Condition="!(*reinterpret_cast<const char***>(opaque))[1]">{(*reinterpret_cast<const char***>(opaque))[2],s32}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="opaque_ptr">*reinterpret_cast<void**>(opaque)</Item>
|
||||
<Item Name="&cname">(*reinterpret_cast<const char***>(opaque))+1</Item>
|
||||
<Item Name="cname">(*reinterpret_cast<const char***>(opaque))[1],s8</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Object::SignalData">
|
||||
<DisplayString Condition="user.name._cowdata._ptr">"{user.name}" {slot_map}</DisplayString>
|
||||
<DisplayString Condition="!user.name._cowdata._ptr">"{slot_map}</DisplayString>
|
||||
</Type>
|
||||
|
||||
<Type Name="Vector2">
|
||||
<DisplayString>({x,g}, {y,g})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[x]">x</Item>
|
||||
<Item Name="[y]">y</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="Vector2i">
|
||||
<DisplayString>({x}, {y})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[x]">x</Item>
|
||||
<Item Name="[y]">y</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Vector3">
|
||||
<DisplayString>({x,g}, {y,g}, {z,g})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[x]">x</Item>
|
||||
<Item Name="[y]">y</Item>
|
||||
<Item Name="[z]">z</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="Vector3i">
|
||||
<DisplayString>({x}, {y}, {z})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[x]">x</Item>
|
||||
<Item Name="[y]">y</Item>
|
||||
<Item Name="[z]">z</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Vector4">
|
||||
<DisplayString>({x,g}, {y,g}, {z,g}, {w,g})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[x]">x</Item>
|
||||
<Item Name="[y]">y</Item>
|
||||
<Item Name="[z]">z</Item>
|
||||
<Item Name="[w]">w</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="Vector4i">
|
||||
<DisplayString>({x}, {y}, {z}, {w})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[x]">x</Item>
|
||||
<Item Name="[y]">y</Item>
|
||||
<Item Name="[z]">z</Item>
|
||||
<Item Name="[w]">w</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Quaternion">
|
||||
<DisplayString>({x,g}, {y,g}, {z,g}, {w,g})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[x]">x</Item>
|
||||
<Item Name="[y]">y</Item>
|
||||
<Item Name="[z]">z</Item>
|
||||
<Item Name="[w]">w</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Color">
|
||||
<DisplayString>({r,g}, {g,g}, {b,g}, {a,g})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[red]">r</Item>
|
||||
<Item Name="[green]">g</Item>
|
||||
<Item Name="[blue]">b</Item>
|
||||
<Item Name="[alpha]">a</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Rect2">
|
||||
<DisplayString>[P: {position}, S: {size}]</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[position]">position,nr</Item>
|
||||
<Item Name="[size]">size,nr</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="Rect2i">
|
||||
<DisplayString>[P: {position}, S: {size}]</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[position]">position,nr</Item>
|
||||
<Item Name="[size]">size,nr</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="AABB">
|
||||
<DisplayString>[P: {position}, S: {size}]</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[position]">position,nr</Item>
|
||||
<Item Name="[size]">size,nr</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Plane">
|
||||
<DisplayString>[N: {normal}, D: {d,g}]</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[normal]">normal,nr</Item>
|
||||
<Item Name="[d]">d</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Basis">
|
||||
<DisplayString>[X: {rows[0]}, Y: {rows[1]}, Z: {rows[2]}]</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[x]">rows[0],nr</Item>
|
||||
<Item Name="[y]">rows[1],nr</Item>
|
||||
<Item Name="[z]">rows[2],nr</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Transform2D">
|
||||
<DisplayString>[X: {columns[0]}, Y: {columns[1]}, O: {columns[2]}]</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[x]">columns[0],nr</Item>
|
||||
<Item Name="[y]">columns[1],nr</Item>
|
||||
<Item Name="[origin]">columns[2],nr</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Transform3D">
|
||||
<!-- Can't call column functions, so just pretend we can via obscene code duplication. -->
|
||||
<DisplayString>[X: ({basis.rows[0].x,g}, {basis.rows[1].x,g}, {basis.rows[2].x,g}), Y: ({basis.rows[0].y,g}, {basis.rows[1].y,g}, {basis.rows[2].y,g}), Z: ({basis.rows[0].z,g}, {basis.rows[1].z,g}, {basis.rows[2].z,g}), O: {origin}]</DisplayString>
|
||||
<Expand>
|
||||
<Synthetic Name="[x]">
|
||||
<DisplayString>({basis.rows[0].x,g}, {basis.rows[1].x,g}, {basis.rows[2].x,g})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[x]">basis.rows[0].x</Item>
|
||||
<Item Name="[y]">basis.rows[1].x</Item>
|
||||
<Item Name="[z]">basis.rows[2].x</Item>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[y]">
|
||||
<DisplayString>({basis.rows[0].y,g}, {basis.rows[1].y,g}, {basis.rows[2].y,g})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[x]">basis.rows[0].y</Item>
|
||||
<Item Name="[y]">basis.rows[1].y</Item>
|
||||
<Item Name="[z]">basis.rows[2].y</Item>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[z]">
|
||||
<DisplayString>({basis.rows[0].z,g}, {basis.rows[1].z,g}, {basis.rows[2].z,g})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[x]">basis.rows[0].z</Item>
|
||||
<Item Name="[y]">basis.rows[1].z</Item>
|
||||
<Item Name="[z]">basis.rows[2].z</Item>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Item Name="[origin]">origin,nr</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Projection">
|
||||
<DisplayString>[X: {columns[0]}, Y: {columns[1]}, Z: {columns[2]}, W: {columns[3]}]</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[x]">columns[0],nr</Item>
|
||||
<Item Name="[y]">columns[1],nr</Item>
|
||||
<Item Name="[z]">columns[2],nr</Item>
|
||||
<Item Name="[w]">columns[3],nr</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
BIN
platform/windows/godot_console.ico
Normal file
BIN
platform/windows/godot_console.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 137 KiB |
34
platform/windows/godot_res.rc
Normal file
34
platform/windows/godot_res.rc
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "core/version.h"
|
||||
|
||||
#ifndef RT_MANIFEST
|
||||
#define RT_MANIFEST 24
|
||||
#endif
|
||||
|
||||
GODOT_ICON ICON platform/windows/godot.ico
|
||||
1 RT_MANIFEST "godot.manifest"
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION GODOT_VERSION_MAJOR,GODOT_VERSION_MINOR,GODOT_VERSION_PATCH,0
|
||||
PRODUCTVERSION GODOT_VERSION_MAJOR,GODOT_VERSION_MINOR,GODOT_VERSION_PATCH,0
|
||||
FILEOS 4
|
||||
FILETYPE 1
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Godot Engine"
|
||||
VALUE "FileDescription", GODOT_VERSION_NAME
|
||||
VALUE "FileVersion", GODOT_VERSION_NUMBER
|
||||
VALUE "ProductName", GODOT_VERSION_NAME
|
||||
VALUE "Licence", "MIT"
|
||||
VALUE "LegalCopyright", "(c) 2007-present Juan Linietsky, Ariel Manzur and Godot Engine contributors"
|
||||
VALUE "Info", "https://godotengine.org"
|
||||
VALUE "ProductVersion", GODOT_VERSION_FULL_BUILD
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
34
platform/windows/godot_res_wrap.rc
Normal file
34
platform/windows/godot_res_wrap.rc
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "core/version.h"
|
||||
|
||||
#ifndef RT_MANIFEST
|
||||
#define RT_MANIFEST 24
|
||||
#endif
|
||||
|
||||
GODOT_ICON ICON platform/windows/godot_console.ico
|
||||
1 RT_MANIFEST "godot.manifest"
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION GODOT_VERSION_MAJOR,GODOT_VERSION_MINOR,GODOT_VERSION_PATCH,0
|
||||
PRODUCTVERSION GODOT_VERSION_MAJOR,GODOT_VERSION_MINOR,GODOT_VERSION_PATCH,0
|
||||
FILEOS 4
|
||||
FILETYPE 1
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Godot Engine"
|
||||
VALUE "FileDescription", GODOT_VERSION_NAME " (Console)"
|
||||
VALUE "FileVersion", GODOT_VERSION_NUMBER
|
||||
VALUE "ProductName", GODOT_VERSION_NAME " (Console)"
|
||||
VALUE "Licence", "MIT"
|
||||
VALUE "LegalCopyright", "(c) 2007-present Juan Linietsky, Ariel Manzur and Godot Engine contributors"
|
||||
VALUE "Info", "https://godotengine.org"
|
||||
VALUE "ProductVersion", GODOT_VERSION_FULL_BUILD
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
150
platform/windows/godot_windows.cpp
Normal file
150
platform/windows/godot_windows.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
/**************************************************************************/
|
||||
/* godot_windows.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "os_windows.h"
|
||||
|
||||
#include "main/main.h"
|
||||
|
||||
#include <clocale>
|
||||
#include <cstdio>
|
||||
|
||||
// For export templates, add a section; the exporter will patch it to enclose
|
||||
// the data appended to the executable (bundled PCK).
|
||||
#ifndef TOOLS_ENABLED
|
||||
#if defined _MSC_VER
|
||||
#pragma section("pck", read)
|
||||
__declspec(allocate("pck")) static char dummy[8] = { 0 };
|
||||
|
||||
// Dummy function to prevent LTO from discarding "pck" section.
|
||||
extern "C" char *__cdecl pck_section_dummy_call() {
|
||||
return &dummy[0];
|
||||
}
|
||||
#if defined _AMD64_
|
||||
#pragma comment(linker, "/include:pck_section_dummy_call")
|
||||
#elif defined _X86_
|
||||
#pragma comment(linker, "/include:_pck_section_dummy_call")
|
||||
#endif
|
||||
|
||||
#elif defined __GNUC__
|
||||
static const char dummy[8] __attribute__((section("pck"), used)) = { 0 };
|
||||
#endif
|
||||
#endif
|
||||
|
||||
char *wc_to_utf8(const wchar_t *wc) {
|
||||
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, nullptr, nullptr);
|
||||
ubuf[ulen] = 0;
|
||||
return ubuf;
|
||||
}
|
||||
|
||||
int widechar_main(int argc, wchar_t **argv) {
|
||||
OS_Windows os(nullptr);
|
||||
|
||||
setlocale(LC_CTYPE, "");
|
||||
|
||||
char **argv_utf8 = new char *[argc];
|
||||
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
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) {
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
delete[] argv_utf8[i];
|
||||
}
|
||||
delete[] argv_utf8;
|
||||
|
||||
if (err == ERR_HELP) { // Returned by --help and --version, so success.
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (Main::start() == EXIT_SUCCESS) {
|
||||
os.run();
|
||||
} else {
|
||||
os.set_exit_code(EXIT_FAILURE);
|
||||
}
|
||||
Main::cleanup();
|
||||
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
delete[] argv_utf8[i];
|
||||
}
|
||||
delete[] argv_utf8;
|
||||
|
||||
return os.get_exit_code();
|
||||
}
|
||||
|
||||
int _main() {
|
||||
LPWSTR *wc_argv;
|
||||
int argc;
|
||||
int result;
|
||||
|
||||
wc_argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||
|
||||
if (nullptr == wc_argv) {
|
||||
wprintf(L"CommandLineToArgvW failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = widechar_main(argc, wc_argv);
|
||||
|
||||
LocalFree(wc_argv);
|
||||
return result;
|
||||
}
|
||||
|
||||
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
|
||||
#if defined(CRASH_HANDLER_EXCEPTION) && defined(_MSC_VER)
|
||||
__try {
|
||||
return _main();
|
||||
} __except (CrashHandlerException(GetExceptionInformation())) {
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
return _main();
|
||||
#endif
|
||||
}
|
||||
|
||||
HINSTANCE godot_hinstance = nullptr;
|
||||
|
||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
|
||||
godot_hinstance = hInstance;
|
||||
return main(0, nullptr);
|
||||
}
|
449
platform/windows/key_mapping_windows.cpp
Normal file
449
platform/windows/key_mapping_windows.cpp
Normal file
@@ -0,0 +1,449 @@
|
||||
/**************************************************************************/
|
||||
/* key_mapping_windows.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 "key_mapping_windows.h"
|
||||
|
||||
#include "core/templates/hash_map.h"
|
||||
|
||||
// This provides translation from Windows virtual key codes to Godot and back.
|
||||
// See WinUser.h and the below for documentation:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
|
||||
|
||||
struct HashMapHasherKeys {
|
||||
static _FORCE_INLINE_ uint32_t hash(const Key p_key) { return hash_fmix32(static_cast<uint32_t>(p_key)); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(p_uchar); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const unsigned p_key) { return hash_fmix32(p_key); }
|
||||
};
|
||||
|
||||
HashMap<unsigned int, Key, HashMapHasherKeys> vk_map;
|
||||
HashMap<unsigned int, Key, HashMapHasherKeys> scansym_map;
|
||||
HashMap<Key, unsigned int, HashMapHasherKeys> scansym_map_inv;
|
||||
HashMap<unsigned int, Key, HashMapHasherKeys> scansym_map_ext;
|
||||
HashMap<unsigned int, KeyLocation, HashMapHasherKeys> location_map;
|
||||
|
||||
void KeyMappingWindows::initialize() {
|
||||
// VK_LBUTTON (0x01)
|
||||
// VK_RBUTTON (0x02)
|
||||
// VK_CANCEL (0x03)
|
||||
// VK_MBUTTON (0x04)
|
||||
// VK_XBUTTON1 (0x05)
|
||||
// VK_XBUTTON2 (0x06), We have no mappings for the above;as we only map keyboard buttons here.
|
||||
// 0x07 is undefined.
|
||||
vk_map[VK_BACK] = Key::BACKSPACE; // (0x08)
|
||||
vk_map[VK_TAB] = Key::TAB; // (0x09)
|
||||
// 0x0A-0B are reserved.
|
||||
vk_map[VK_CLEAR] = Key::CLEAR; // (0x0C)
|
||||
vk_map[VK_RETURN] = Key::ENTER; // (0x0D)
|
||||
// 0x0E-0F are undefined.
|
||||
vk_map[VK_SHIFT] = Key::SHIFT; // (0x10)
|
||||
vk_map[VK_CONTROL] = Key::CTRL; // (0x11)
|
||||
vk_map[VK_MENU] = Key::ALT; // (0x12)
|
||||
vk_map[VK_PAUSE] = Key::PAUSE; // (0x13)
|
||||
vk_map[VK_CAPITAL] = Key::CAPSLOCK; // (0x14)
|
||||
// 0x15-1A are IME keys.
|
||||
vk_map[VK_ESCAPE] = Key::ESCAPE; // (0x1B)
|
||||
// 0x1C-1F are IME keys.
|
||||
vk_map[VK_SPACE] = Key::SPACE; // (0x20)
|
||||
vk_map[VK_PRIOR] = Key::PAGEUP; // (0x21)
|
||||
vk_map[VK_NEXT] = Key::PAGEDOWN; // (0x22)
|
||||
vk_map[VK_END] = Key::END; // (0x23)
|
||||
vk_map[VK_HOME] = Key::HOME; // (0x24)
|
||||
vk_map[VK_LEFT] = Key::LEFT; // (0x25)
|
||||
vk_map[VK_UP] = Key::UP; // (0x26)
|
||||
vk_map[VK_RIGHT] = Key::RIGHT; // (0x27)
|
||||
vk_map[VK_DOWN] = Key::DOWN; // (0x28)
|
||||
// VK_SELECT (0x29), Old select key; e.g. on Digital Equipment Corporation keyboards.
|
||||
vk_map[VK_PRINT] = Key::PRINT; // (0x2A), Old IBM key; modern keyboards use VK_SNAPSHOT.
|
||||
// VK_EXECUTE (0x2B), Old and uncommon.
|
||||
vk_map[VK_SNAPSHOT] = Key::PRINT; // (0x2C)
|
||||
vk_map[VK_INSERT] = Key::INSERT; // (0x2D)
|
||||
vk_map[VK_DELETE] = Key::KEY_DELETE; // (0x2E)
|
||||
vk_map[VK_HELP] = Key::HELP; // (0x2F)
|
||||
vk_map[0x30] = Key::KEY_0; // 0 key.
|
||||
vk_map[0x31] = Key::KEY_1; // 1 key.
|
||||
vk_map[0x32] = Key::KEY_2; // 2 key.
|
||||
vk_map[0x33] = Key::KEY_3; // 3 key.
|
||||
vk_map[0x34] = Key::KEY_4; // 4 key.
|
||||
vk_map[0x35] = Key::KEY_5; // 5 key.
|
||||
vk_map[0x36] = Key::KEY_6; // 6 key.
|
||||
vk_map[0x37] = Key::KEY_7; // 7 key.
|
||||
vk_map[0x38] = Key::KEY_8; // 8 key.
|
||||
vk_map[0x39] = Key::KEY_9; // 9 key.
|
||||
// 0x3A-40 are undefined.
|
||||
vk_map[0x41] = Key::A; // A key.
|
||||
vk_map[0x42] = Key::B; // B key.
|
||||
vk_map[0x43] = Key::C; // C key.
|
||||
vk_map[0x44] = Key::D; // D key.
|
||||
vk_map[0x45] = Key::E; // E key.
|
||||
vk_map[0x46] = Key::F; // F key.
|
||||
vk_map[0x47] = Key::G; // G key.
|
||||
vk_map[0x48] = Key::H; // H key.
|
||||
vk_map[0x49] = Key::I; // I key
|
||||
vk_map[0x4A] = Key::J; // J key.
|
||||
vk_map[0x4B] = Key::K; // K key.
|
||||
vk_map[0x4C] = Key::L; // L key.
|
||||
vk_map[0x4D] = Key::M; // M key.
|
||||
vk_map[0x4E] = Key::N; // N key.
|
||||
vk_map[0x4F] = Key::O; // O key.
|
||||
vk_map[0x50] = Key::P; // P key.
|
||||
vk_map[0x51] = Key::Q; // Q key.
|
||||
vk_map[0x52] = Key::R; // R key.
|
||||
vk_map[0x53] = Key::S; // S key.
|
||||
vk_map[0x54] = Key::T; // T key.
|
||||
vk_map[0x55] = Key::U; // U key.
|
||||
vk_map[0x56] = Key::V; // V key.
|
||||
vk_map[0x57] = Key::W; // W key.
|
||||
vk_map[0x58] = Key::X; // X key.
|
||||
vk_map[0x59] = Key::Y; // Y key.
|
||||
vk_map[0x5A] = Key::Z; // Z key.
|
||||
vk_map[VK_LWIN] = (Key)Key::META; // (0x5B)
|
||||
vk_map[VK_RWIN] = (Key)Key::META; // (0x5C)
|
||||
vk_map[VK_APPS] = Key::MENU; // (0x5D)
|
||||
// 0x5E is reserved.
|
||||
vk_map[VK_SLEEP] = Key::STANDBY; // (0x5F)
|
||||
vk_map[VK_NUMPAD0] = Key::KP_0; // (0x60)
|
||||
vk_map[VK_NUMPAD1] = Key::KP_1; // (0x61)
|
||||
vk_map[VK_NUMPAD2] = Key::KP_2; // (0x62)
|
||||
vk_map[VK_NUMPAD3] = Key::KP_3; // (0x63)
|
||||
vk_map[VK_NUMPAD4] = Key::KP_4; // (0x64)
|
||||
vk_map[VK_NUMPAD5] = Key::KP_5; // (0x65)
|
||||
vk_map[VK_NUMPAD6] = Key::KP_6; // (0x66)
|
||||
vk_map[VK_NUMPAD7] = Key::KP_7; // (0x67)
|
||||
vk_map[VK_NUMPAD8] = Key::KP_8; // (0x68)
|
||||
vk_map[VK_NUMPAD9] = Key::KP_9; // (0x69)
|
||||
vk_map[VK_MULTIPLY] = Key::KP_MULTIPLY; // (0x6A)
|
||||
vk_map[VK_ADD] = Key::KP_ADD; // (0x6B)
|
||||
vk_map[VK_SEPARATOR] = Key::KP_PERIOD; // (0x6C)
|
||||
vk_map[VK_SUBTRACT] = Key::KP_SUBTRACT; // (0x6D)
|
||||
vk_map[VK_DECIMAL] = Key::KP_PERIOD; // (0x6E)
|
||||
vk_map[VK_DIVIDE] = Key::KP_DIVIDE; // (0x6F)
|
||||
vk_map[VK_F1] = Key::F1; // (0x70)
|
||||
vk_map[VK_F2] = Key::F2; // (0x71)
|
||||
vk_map[VK_F3] = Key::F3; // (0x72)
|
||||
vk_map[VK_F4] = Key::F4; // (0x73)
|
||||
vk_map[VK_F5] = Key::F5; // (0x74)
|
||||
vk_map[VK_F6] = Key::F6; // (0x75)
|
||||
vk_map[VK_F7] = Key::F7; // (0x76)
|
||||
vk_map[VK_F8] = Key::F8; // (0x77)
|
||||
vk_map[VK_F9] = Key::F9; // (0x78)
|
||||
vk_map[VK_F10] = Key::F10; // (0x79)
|
||||
vk_map[VK_F11] = Key::F11; // (0x7A)
|
||||
vk_map[VK_F12] = Key::F12; // (0x7B)
|
||||
vk_map[VK_F13] = Key::F13; // (0x7C)
|
||||
vk_map[VK_F14] = Key::F14; // (0x7D)
|
||||
vk_map[VK_F15] = Key::F15; // (0x7E)
|
||||
vk_map[VK_F16] = Key::F16; // (0x7F)
|
||||
vk_map[VK_F17] = Key::F17; // (0x80)
|
||||
vk_map[VK_F18] = Key::F18; // (0x81)
|
||||
vk_map[VK_F19] = Key::F19; // (0x82)
|
||||
vk_map[VK_F20] = Key::F20; // (0x83)
|
||||
vk_map[VK_F21] = Key::F21; // (0x84)
|
||||
vk_map[VK_F22] = Key::F22; // (0x85)
|
||||
vk_map[VK_F23] = Key::F23; // (0x86)
|
||||
vk_map[VK_F24] = Key::F24; // (0x87)
|
||||
// 0x88-8F are reserved for UI navigation.
|
||||
vk_map[VK_NUMLOCK] = Key::NUMLOCK; // (0x90)
|
||||
vk_map[VK_SCROLL] = Key::SCROLLLOCK; // (0x91)
|
||||
vk_map[VK_OEM_NEC_EQUAL] = Key::EQUAL; // (0x92), OEM NEC PC-9800 numpad '=' key.
|
||||
// 0x93-96 are OEM specific (e.g. used by Fujitsu/OASYS);
|
||||
// 0x97-9F are unassigned.
|
||||
vk_map[VK_LSHIFT] = Key::SHIFT; // (0xA0)
|
||||
vk_map[VK_RSHIFT] = Key::SHIFT; // (0xA1)
|
||||
vk_map[VK_LCONTROL] = Key::CTRL; // (0xA2)
|
||||
vk_map[VK_RCONTROL] = Key::CTRL; // (0xA3)
|
||||
vk_map[VK_LMENU] = Key::MENU; // (0xA4)
|
||||
vk_map[VK_RMENU] = Key::MENU; // (0xA5)
|
||||
vk_map[VK_BROWSER_BACK] = Key::BACK; // (0xA6)
|
||||
vk_map[VK_BROWSER_FORWARD] = Key::FORWARD; // (0xA7)
|
||||
vk_map[VK_BROWSER_REFRESH] = Key::REFRESH; // (0xA8)
|
||||
vk_map[VK_BROWSER_STOP] = Key::STOP; // (0xA9)
|
||||
vk_map[VK_BROWSER_SEARCH] = Key::SEARCH; // (0xAA)
|
||||
vk_map[VK_BROWSER_FAVORITES] = Key::FAVORITES; // (0xAB)
|
||||
vk_map[VK_BROWSER_HOME] = Key::HOMEPAGE; // (0xAC)
|
||||
vk_map[VK_VOLUME_MUTE] = Key::VOLUMEMUTE; // (0xAD)
|
||||
vk_map[VK_VOLUME_DOWN] = Key::VOLUMEDOWN; // (0xAE)
|
||||
vk_map[VK_VOLUME_UP] = Key::VOLUMEUP; // (0xAF)
|
||||
vk_map[VK_MEDIA_NEXT_TRACK] = Key::MEDIANEXT; // (0xB0)
|
||||
vk_map[VK_MEDIA_PREV_TRACK] = Key::MEDIAPREVIOUS; // (0xB1)
|
||||
vk_map[VK_MEDIA_STOP] = Key::MEDIASTOP; // (0xB2)
|
||||
vk_map[VK_MEDIA_PLAY_PAUSE] = Key::MEDIAPLAY; // (0xB3), Media button play/pause toggle.
|
||||
vk_map[VK_LAUNCH_MAIL] = Key::LAUNCHMAIL; // (0xB4)
|
||||
vk_map[VK_LAUNCH_MEDIA_SELECT] = Key::LAUNCHMEDIA; // (0xB5)
|
||||
vk_map[VK_LAUNCH_APP1] = Key::LAUNCH0; // (0xB6)
|
||||
vk_map[VK_LAUNCH_APP2] = Key::LAUNCH1; // (0xB7)
|
||||
// 0xB8-B9 are reserved.
|
||||
vk_map[VK_OEM_1] = Key::SEMICOLON; // (0xBA), Misc. character;can vary by keyboard/region. For US standard keyboards;the ';:' key.
|
||||
vk_map[VK_OEM_PLUS] = Key::EQUAL; // (0xBB)
|
||||
vk_map[VK_OEM_COMMA] = Key::COMMA; // (0xBC)
|
||||
vk_map[VK_OEM_MINUS] = Key::MINUS; // (0xBD)
|
||||
vk_map[VK_OEM_PERIOD] = Key::PERIOD; // (0xBE)
|
||||
vk_map[VK_OEM_2] = Key::SLASH; // (0xBF), For US standard keyboards;the '/?' key.
|
||||
vk_map[VK_OEM_3] = Key::QUOTELEFT; // (0xC0), For US standard keyboards;the '`~' key.
|
||||
// 0xC1-D7 are reserved. 0xD8-DA are unassigned.
|
||||
// 0xC3-DA may be used for old gamepads? Maybe we want to support this? See WinUser.h.
|
||||
vk_map[VK_OEM_4] = Key::BRACKETLEFT; // (0xDB), For US standard keyboards;the '[{' key.
|
||||
vk_map[VK_OEM_5] = Key::BACKSLASH; // (0xDC), For US standard keyboards;the '\|' key.
|
||||
vk_map[VK_OEM_6] = Key::BRACKETRIGHT; // (0xDD), For US standard keyboards;the ']}' key.
|
||||
vk_map[VK_OEM_7] = Key::APOSTROPHE; // (0xDE), For US standard keyboards;single quote/double quote.
|
||||
// VK_OEM_8 (0xDF)
|
||||
// 0xE0 is reserved. 0xE1 is OEM specific.
|
||||
vk_map[VK_OEM_102] = Key::BAR; // (0xE2), Either angle bracket or backslash key on the RT 102-key keyboard.
|
||||
vk_map[VK_ICO_HELP] = Key::HELP; // (0xE3)
|
||||
// 0xE4 is OEM (e.g. ICO) specific.
|
||||
// VK_PROCESSKEY (0xE5), For IME.
|
||||
vk_map[VK_ICO_CLEAR] = Key::CLEAR; // (0xE6)
|
||||
// VK_PACKET (0xE7), Used to pass Unicode characters as if they were keystrokes.
|
||||
// 0xE8 is unassigned.
|
||||
// 0xE9-F5 are OEM (Nokia/Ericsson) specific.
|
||||
vk_map[VK_ATTN] = Key::ESCAPE; // (0xF6), Old IBM 'ATTN' key used on midrange computers ;e.g. AS/400.
|
||||
vk_map[VK_CRSEL] = Key::TAB; // (0xF7), Old IBM 3270 'CrSel' (cursor select) key; used to select data fields.
|
||||
// VK_EXSEL (0xF7), Old IBM 3270 extended selection key.
|
||||
// VK_EREOF (0xF8), Old IBM 3270 erase to end of field key.
|
||||
vk_map[VK_PLAY] = Key::MEDIAPLAY; // (0xFA), Old IBM 3270 'Play' key.
|
||||
// VK_ZOOM (0xFB), Old IBM 3290 'Zoom' key.
|
||||
// VK_NONAME (0xFC), Reserved.
|
||||
// VK_PA1 (0xFD), Old IBM 3270 PA1 key.
|
||||
vk_map[VK_OEM_CLEAR] = Key::CLEAR; // (0xFE), OEM specific clear key. Unclear how it differs from normal clear.
|
||||
|
||||
scansym_map[0x00] = Key::PAUSE;
|
||||
scansym_map[0x01] = Key::ESCAPE;
|
||||
scansym_map[0x02] = Key::KEY_1;
|
||||
scansym_map[0x03] = Key::KEY_2;
|
||||
scansym_map[0x04] = Key::KEY_3;
|
||||
scansym_map[0x05] = Key::KEY_4;
|
||||
scansym_map[0x06] = Key::KEY_5;
|
||||
scansym_map[0x07] = Key::KEY_6;
|
||||
scansym_map[0x08] = Key::KEY_7;
|
||||
scansym_map[0x09] = Key::KEY_8;
|
||||
scansym_map[0x0A] = Key::KEY_9;
|
||||
scansym_map[0x0B] = Key::KEY_0;
|
||||
scansym_map[0x0C] = Key::MINUS;
|
||||
scansym_map[0x0D] = Key::EQUAL;
|
||||
scansym_map[0x0E] = Key::BACKSPACE;
|
||||
scansym_map[0x0F] = Key::TAB;
|
||||
scansym_map[0x10] = Key::Q;
|
||||
scansym_map[0x11] = Key::W;
|
||||
scansym_map[0x12] = Key::E;
|
||||
scansym_map[0x13] = Key::R;
|
||||
scansym_map[0x14] = Key::T;
|
||||
scansym_map[0x15] = Key::Y;
|
||||
scansym_map[0x16] = Key::U;
|
||||
scansym_map[0x17] = Key::I;
|
||||
scansym_map[0x18] = Key::O;
|
||||
scansym_map[0x19] = Key::P;
|
||||
scansym_map[0x1A] = Key::BRACKETLEFT;
|
||||
scansym_map[0x1B] = Key::BRACKETRIGHT;
|
||||
scansym_map[0x1C] = Key::ENTER;
|
||||
scansym_map[0x1D] = Key::CTRL;
|
||||
scansym_map[0x1E] = Key::A;
|
||||
scansym_map[0x1F] = Key::S;
|
||||
scansym_map[0x20] = Key::D;
|
||||
scansym_map[0x21] = Key::F;
|
||||
scansym_map[0x22] = Key::G;
|
||||
scansym_map[0x23] = Key::H;
|
||||
scansym_map[0x24] = Key::J;
|
||||
scansym_map[0x25] = Key::K;
|
||||
scansym_map[0x26] = Key::L;
|
||||
scansym_map[0x27] = Key::SEMICOLON;
|
||||
scansym_map[0x28] = Key::APOSTROPHE;
|
||||
scansym_map[0x29] = Key::QUOTELEFT;
|
||||
scansym_map[0x2A] = Key::SHIFT;
|
||||
scansym_map[0x2B] = Key::BACKSLASH;
|
||||
scansym_map[0x2C] = Key::Z;
|
||||
scansym_map[0x2D] = Key::X;
|
||||
scansym_map[0x2E] = Key::C;
|
||||
scansym_map[0x2F] = Key::V;
|
||||
scansym_map[0x30] = Key::B;
|
||||
scansym_map[0x31] = Key::N;
|
||||
scansym_map[0x32] = Key::M;
|
||||
scansym_map[0x33] = Key::COMMA;
|
||||
scansym_map[0x34] = Key::PERIOD;
|
||||
scansym_map[0x35] = Key::SLASH;
|
||||
scansym_map[0x36] = Key::SHIFT;
|
||||
scansym_map[0x37] = Key::KP_MULTIPLY;
|
||||
scansym_map[0x38] = Key::ALT;
|
||||
scansym_map[0x39] = Key::SPACE;
|
||||
scansym_map[0x3A] = Key::CAPSLOCK;
|
||||
scansym_map[0x3B] = Key::F1;
|
||||
scansym_map[0x3C] = Key::F2;
|
||||
scansym_map[0x3D] = Key::F3;
|
||||
scansym_map[0x3E] = Key::F4;
|
||||
scansym_map[0x3F] = Key::F5;
|
||||
scansym_map[0x40] = Key::F6;
|
||||
scansym_map[0x41] = Key::F7;
|
||||
scansym_map[0x42] = Key::F8;
|
||||
scansym_map[0x43] = Key::F9;
|
||||
scansym_map[0x44] = Key::F10;
|
||||
scansym_map[0x45] = Key::NUMLOCK;
|
||||
scansym_map[0x46] = Key::SCROLLLOCK;
|
||||
scansym_map[0x47] = Key::KP_7;
|
||||
scansym_map[0x48] = Key::KP_8;
|
||||
scansym_map[0x49] = Key::KP_9;
|
||||
scansym_map[0x4A] = Key::KP_SUBTRACT;
|
||||
scansym_map[0x4B] = Key::KP_4;
|
||||
scansym_map[0x4C] = Key::KP_5;
|
||||
scansym_map[0x4D] = Key::KP_6;
|
||||
scansym_map[0x4E] = Key::KP_ADD;
|
||||
scansym_map[0x4F] = Key::KP_1;
|
||||
scansym_map[0x50] = Key::KP_2;
|
||||
scansym_map[0x51] = Key::KP_3;
|
||||
scansym_map[0x52] = Key::KP_0;
|
||||
scansym_map[0x53] = Key::KP_PERIOD;
|
||||
scansym_map[0x56] = Key::SECTION;
|
||||
scansym_map[0x57] = Key::F11;
|
||||
scansym_map[0x58] = Key::F12;
|
||||
scansym_map[0x5B] = Key::META;
|
||||
scansym_map[0x5C] = Key::META;
|
||||
scansym_map[0x5D] = Key::MENU;
|
||||
scansym_map[0x64] = Key::F13;
|
||||
scansym_map[0x65] = Key::F14;
|
||||
scansym_map[0x66] = Key::F15;
|
||||
scansym_map[0x67] = Key::F16;
|
||||
scansym_map[0x68] = Key::F17;
|
||||
scansym_map[0x69] = Key::F18;
|
||||
scansym_map[0x6A] = Key::F19;
|
||||
scansym_map[0x6B] = Key::F20;
|
||||
scansym_map[0x6C] = Key::F21;
|
||||
scansym_map[0x6D] = Key::F22;
|
||||
scansym_map[0x6E] = Key::F23;
|
||||
// scansym_map[0x71] = Key::JIS_KANA;
|
||||
// scansym_map[0x72] = Key::JIS_EISU;
|
||||
scansym_map[0x76] = Key::F24;
|
||||
|
||||
for (const KeyValue<unsigned int, Key> &E : scansym_map) {
|
||||
scansym_map_inv[E.value] = E.key;
|
||||
}
|
||||
|
||||
scansym_map_ext[0x09] = Key::MENU;
|
||||
scansym_map_ext[0x10] = Key::MEDIAPREVIOUS;
|
||||
scansym_map_ext[0x19] = Key::MEDIANEXT;
|
||||
scansym_map_ext[0x1C] = Key::KP_ENTER;
|
||||
scansym_map_ext[0x20] = Key::VOLUMEMUTE;
|
||||
scansym_map_ext[0x21] = Key::LAUNCH1;
|
||||
scansym_map_ext[0x22] = Key::MEDIAPLAY;
|
||||
scansym_map_ext[0x24] = Key::MEDIASTOP;
|
||||
scansym_map_ext[0x2E] = Key::VOLUMEDOWN;
|
||||
scansym_map_ext[0x30] = Key::VOLUMEUP;
|
||||
scansym_map_ext[0x32] = Key::HOMEPAGE;
|
||||
scansym_map_ext[0x35] = Key::KP_DIVIDE;
|
||||
scansym_map_ext[0x37] = Key::PRINT;
|
||||
scansym_map_ext[0x3A] = Key::KP_ADD;
|
||||
scansym_map_ext[0x45] = Key::NUMLOCK;
|
||||
scansym_map_ext[0x47] = Key::HOME;
|
||||
scansym_map_ext[0x48] = Key::UP;
|
||||
scansym_map_ext[0x49] = Key::PAGEUP;
|
||||
scansym_map_ext[0x4A] = Key::KP_SUBTRACT;
|
||||
scansym_map_ext[0x4B] = Key::LEFT;
|
||||
scansym_map_ext[0x4C] = Key::KP_5;
|
||||
scansym_map_ext[0x4D] = Key::RIGHT;
|
||||
scansym_map_ext[0x4E] = Key::KP_ADD;
|
||||
scansym_map_ext[0x4F] = Key::END;
|
||||
scansym_map_ext[0x50] = Key::DOWN;
|
||||
scansym_map_ext[0x51] = Key::PAGEDOWN;
|
||||
scansym_map_ext[0x52] = Key::INSERT;
|
||||
scansym_map_ext[0x53] = Key::KEY_DELETE;
|
||||
scansym_map_ext[0x5D] = Key::MENU;
|
||||
scansym_map_ext[0x5F] = Key::STANDBY;
|
||||
scansym_map_ext[0x65] = Key::SEARCH;
|
||||
scansym_map_ext[0x66] = Key::FAVORITES;
|
||||
scansym_map_ext[0x67] = Key::REFRESH;
|
||||
scansym_map_ext[0x68] = Key::STOP;
|
||||
scansym_map_ext[0x69] = Key::FORWARD;
|
||||
scansym_map_ext[0x6A] = Key::BACK;
|
||||
scansym_map_ext[0x6B] = Key::LAUNCH0;
|
||||
scansym_map_ext[0x6C] = Key::LAUNCHMAIL;
|
||||
scansym_map_ext[0x6D] = Key::LAUNCHMEDIA;
|
||||
scansym_map_ext[0x78] = Key::MEDIARECORD;
|
||||
|
||||
// Scancode to physical location map.
|
||||
// Shift.
|
||||
location_map[0x2A] = KeyLocation::LEFT;
|
||||
location_map[0x36] = KeyLocation::RIGHT;
|
||||
// Meta.
|
||||
location_map[0x5B] = KeyLocation::LEFT;
|
||||
location_map[0x5C] = KeyLocation::RIGHT;
|
||||
// Ctrl and Alt must be handled differently.
|
||||
}
|
||||
|
||||
Key KeyMappingWindows::get_keysym(unsigned int p_code) {
|
||||
const Key *key = vk_map.getptr(p_code);
|
||||
if (key) {
|
||||
return *key;
|
||||
}
|
||||
return Key::UNKNOWN;
|
||||
}
|
||||
|
||||
unsigned int KeyMappingWindows::get_scancode(Key p_keycode) {
|
||||
const unsigned int *key = scansym_map_inv.getptr(p_keycode);
|
||||
if (key) {
|
||||
return *key;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Key KeyMappingWindows::get_scansym(unsigned int p_code, bool p_extended) {
|
||||
if (p_extended) {
|
||||
const Key *key = scansym_map_ext.getptr(p_code);
|
||||
if (key) {
|
||||
return *key;
|
||||
}
|
||||
}
|
||||
const Key *key = scansym_map.getptr(p_code);
|
||||
if (key) {
|
||||
return *key;
|
||||
}
|
||||
return Key::NONE;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
KeyLocation KeyMappingWindows::get_location(unsigned int p_code, bool p_extended) {
|
||||
// Right- ctrl and alt have the same scancode as left, but are in the extended keys.
|
||||
const Key *key = scansym_map.getptr(p_code);
|
||||
if (key && (*key == Key::CTRL || *key == Key::ALT)) {
|
||||
return p_extended ? KeyLocation::RIGHT : KeyLocation::LEFT;
|
||||
}
|
||||
const KeyLocation *location = location_map.getptr(p_code);
|
||||
if (location) {
|
||||
return *location;
|
||||
}
|
||||
return KeyLocation::UNSPECIFIED;
|
||||
}
|
50
platform/windows/key_mapping_windows.h
Normal file
50
platform/windows/key_mapping_windows.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/**************************************************************************/
|
||||
/* key_mapping_windows.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/os/keyboard.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <winuser.h>
|
||||
|
||||
class KeyMappingWindows {
|
||||
KeyMappingWindows() {}
|
||||
|
||||
public:
|
||||
static void initialize();
|
||||
|
||||
static Key get_keysym(unsigned int p_code);
|
||||
static unsigned int get_scancode(Key p_keycode);
|
||||
static Key get_scansym(unsigned int p_code, bool p_extended);
|
||||
static bool is_extended_key(unsigned int p_code);
|
||||
static KeyLocation get_location(unsigned int p_code, bool p_extended);
|
||||
};
|
188
platform/windows/lang_table.h
Normal file
188
platform/windows/lang_table.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/**************************************************************************/
|
||||
/* lang_table.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct _WinLocale {
|
||||
const char *locale;
|
||||
int main_lang;
|
||||
int sublang;
|
||||
};
|
||||
|
||||
static const _WinLocale _win_locales[] = {
|
||||
{ "ar", LANG_ARABIC, SUBLANG_NEUTRAL },
|
||||
{ "ar_AE", LANG_ARABIC, SUBLANG_ARABIC_UAE },
|
||||
{ "ar_BH", LANG_ARABIC, SUBLANG_ARABIC_BAHRAIN },
|
||||
{ "ar_DZ", LANG_ARABIC, SUBLANG_ARABIC_ALGERIA },
|
||||
{ "ar_EG", LANG_ARABIC, SUBLANG_ARABIC_EGYPT },
|
||||
{ "ar_IQ", LANG_ARABIC, SUBLANG_ARABIC_IRAQ },
|
||||
{ "ar_JO", LANG_ARABIC, SUBLANG_ARABIC_JORDAN },
|
||||
{ "ar_KW", LANG_ARABIC, SUBLANG_ARABIC_KUWAIT },
|
||||
{ "ar_LB", LANG_ARABIC, SUBLANG_ARABIC_LEBANON },
|
||||
{ "ar_LY", LANG_ARABIC, SUBLANG_ARABIC_LIBYA },
|
||||
{ "ar_MA", LANG_ARABIC, SUBLANG_ARABIC_MOROCCO },
|
||||
{ "ar_OM", LANG_ARABIC, SUBLANG_ARABIC_OMAN },
|
||||
{ "ar_QA", LANG_ARABIC, SUBLANG_ARABIC_QATAR },
|
||||
{ "ar_SA", LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA },
|
||||
//no sudan
|
||||
{ "ar_SY", LANG_ARABIC, SUBLANG_ARABIC_SYRIA },
|
||||
{ "ar_TN", LANG_ARABIC, SUBLANG_ARABIC_TUNISIA },
|
||||
{ "ar_YE", LANG_ARABIC, SUBLANG_ARABIC_YEMEN },
|
||||
{ "be", LANG_BELARUSIAN, SUBLANG_NEUTRAL },
|
||||
{ "be_BY", LANG_BELARUSIAN, SUBLANG_BELARUSIAN_BELARUS },
|
||||
{ "bg", LANG_BULGARIAN, SUBLANG_NEUTRAL },
|
||||
{ "bg_BG", LANG_BULGARIAN, SUBLANG_BULGARIAN_BULGARIA },
|
||||
{ "ca", LANG_CATALAN, SUBLANG_NEUTRAL },
|
||||
{ "ca_ES", LANG_CATALAN, SUBLANG_CATALAN_CATALAN },
|
||||
{ "cs", LANG_CZECH, SUBLANG_NEUTRAL },
|
||||
{ "cs_CZ", LANG_CZECH, SUBLANG_CZECH_CZECH_REPUBLIC },
|
||||
{ "da", LANG_DANISH, SUBLANG_NEUTRAL },
|
||||
{ "da_DK", LANG_DANISH, SUBLANG_DANISH_DENMARK },
|
||||
{ "de", LANG_GERMAN, SUBLANG_NEUTRAL },
|
||||
{ "de_AT", LANG_GERMAN, SUBLANG_GERMAN_AUSTRIAN },
|
||||
{ "de_CH", LANG_GERMAN, SUBLANG_GERMAN_SWISS },
|
||||
{ "de_DE", LANG_GERMAN, SUBLANG_GERMAN },
|
||||
{ "de_LU", LANG_GERMAN, SUBLANG_GERMAN_LUXEMBOURG },
|
||||
{ "el", LANG_GREEK, SUBLANG_NEUTRAL },
|
||||
{ "el_GR", LANG_GREEK, SUBLANG_GREEK_GREECE },
|
||||
//{"en_029", LANG_ENGLISH,SUBLANG_ENGLISH_CARIBBEAN},
|
||||
{ "en", LANG_ENGLISH, SUBLANG_NEUTRAL },
|
||||
{ "en_AU", LANG_ENGLISH, SUBLANG_ENGLISH_AUS },
|
||||
{ "en_CA", LANG_ENGLISH, SUBLANG_ENGLISH_CAN },
|
||||
{ "en_GB", LANG_ENGLISH, SUBLANG_ENGLISH_UK },
|
||||
//{"en_IE", LANG_ENGLISH,SUBLANG_ENGLISH_IRELAND},
|
||||
{ "en_IN", LANG_ENGLISH, SUBLANG_ENGLISH_INDIA },
|
||||
//MT
|
||||
{ "en_NZ", LANG_ENGLISH, SUBLANG_ENGLISH_NZ },
|
||||
{ "en_PH", LANG_ENGLISH, SUBLANG_ENGLISH_PHILIPPINES },
|
||||
{ "en_SG", LANG_ENGLISH, SUBLANG_ENGLISH_SINGAPORE },
|
||||
{ "en_US", LANG_ENGLISH, SUBLANG_ENGLISH_US },
|
||||
{ "en_ZA", LANG_ENGLISH, SUBLANG_ENGLISH_SOUTH_AFRICA },
|
||||
{ "es", LANG_SPANISH, SUBLANG_NEUTRAL },
|
||||
{ "es_AR", LANG_SPANISH, SUBLANG_SPANISH_ARGENTINA },
|
||||
{ "es_BO", LANG_SPANISH, SUBLANG_SPANISH_BOLIVIA },
|
||||
{ "es_CL", LANG_SPANISH, SUBLANG_SPANISH_CHILE },
|
||||
{ "es_CO", LANG_SPANISH, SUBLANG_SPANISH_COLOMBIA },
|
||||
{ "es_CR", LANG_SPANISH, SUBLANG_SPANISH_COSTA_RICA },
|
||||
{ "es_DO", LANG_SPANISH, SUBLANG_SPANISH_DOMINICAN_REPUBLIC },
|
||||
{ "es_EC", LANG_SPANISH, SUBLANG_SPANISH_ECUADOR },
|
||||
{ "es_ES", LANG_SPANISH, SUBLANG_SPANISH },
|
||||
{ "es_GT", LANG_SPANISH, SUBLANG_SPANISH_GUATEMALA },
|
||||
{ "es_HN", LANG_SPANISH, SUBLANG_SPANISH_HONDURAS },
|
||||
{ "es_MX", LANG_SPANISH, SUBLANG_SPANISH_MEXICAN },
|
||||
{ "es_NI", LANG_SPANISH, SUBLANG_SPANISH_NICARAGUA },
|
||||
{ "es_PA", LANG_SPANISH, SUBLANG_SPANISH_PANAMA },
|
||||
{ "es_PE", LANG_SPANISH, SUBLANG_SPANISH_PERU },
|
||||
{ "es_PR", LANG_SPANISH, SUBLANG_SPANISH_PUERTO_RICO },
|
||||
{ "es_PY", LANG_SPANISH, SUBLANG_SPANISH_PARAGUAY },
|
||||
{ "es_SV", LANG_SPANISH, SUBLANG_SPANISH_EL_SALVADOR },
|
||||
{ "es_US", LANG_SPANISH, SUBLANG_SPANISH_US },
|
||||
{ "es_UY", LANG_SPANISH, SUBLANG_SPANISH_URUGUAY },
|
||||
{ "es_VE", LANG_SPANISH, SUBLANG_SPANISH_VENEZUELA },
|
||||
{ "et", LANG_ESTONIAN, SUBLANG_NEUTRAL },
|
||||
{ "et_EE", LANG_ESTONIAN, SUBLANG_ESTONIAN_ESTONIA },
|
||||
{ "fi", LANG_FINNISH, SUBLANG_NEUTRAL },
|
||||
{ "fi_FI", LANG_FINNISH, SUBLANG_FINNISH_FINLAND },
|
||||
{ "fr", LANG_FRENCH, SUBLANG_NEUTRAL },
|
||||
{ "fr_BE", LANG_FRENCH, SUBLANG_FRENCH_BELGIAN },
|
||||
{ "fr_CA", LANG_FRENCH, SUBLANG_FRENCH_CANADIAN },
|
||||
{ "fr_CH", LANG_FRENCH, SUBLANG_FRENCH_SWISS },
|
||||
{ "fr_FR", LANG_FRENCH, SUBLANG_FRENCH },
|
||||
{ "fr_LU", LANG_FRENCH, SUBLANG_FRENCH_LUXEMBOURG },
|
||||
{ "ga", LANG_IRISH, SUBLANG_NEUTRAL },
|
||||
{ "ga_IE", LANG_IRISH, SUBLANG_IRISH_IRELAND },
|
||||
{ "hi", LANG_HINDI, SUBLANG_NEUTRAL },
|
||||
{ "hi_IN", LANG_HINDI, SUBLANG_HINDI_INDIA },
|
||||
{ "hr", LANG_CROATIAN, SUBLANG_NEUTRAL },
|
||||
{ "hr_HR", LANG_CROATIAN, SUBLANG_CROATIAN_CROATIA },
|
||||
{ "hu", LANG_HUNGARIAN, SUBLANG_NEUTRAL },
|
||||
{ "hu_HU", LANG_HUNGARIAN, SUBLANG_HUNGARIAN_HUNGARY },
|
||||
{ "in", LANG_ARMENIAN, SUBLANG_NEUTRAL },
|
||||
{ "in_ID", LANG_INDONESIAN, SUBLANG_INDONESIAN_INDONESIA },
|
||||
{ "is", LANG_ICELANDIC, SUBLANG_NEUTRAL },
|
||||
{ "is_IS", LANG_ICELANDIC, SUBLANG_ICELANDIC_ICELAND },
|
||||
{ "it", LANG_ITALIAN, SUBLANG_NEUTRAL },
|
||||
{ "it_CH", LANG_ITALIAN, SUBLANG_ITALIAN_SWISS },
|
||||
{ "it_IT", LANG_ITALIAN, SUBLANG_ITALIAN },
|
||||
{ "iw", LANG_HEBREW, SUBLANG_NEUTRAL },
|
||||
{ "iw_IL", LANG_HEBREW, SUBLANG_HEBREW_ISRAEL },
|
||||
{ "ja", LANG_JAPANESE, SUBLANG_NEUTRAL },
|
||||
{ "ja_JP", LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN },
|
||||
{ "ko", LANG_KOREAN, SUBLANG_NEUTRAL },
|
||||
{ "ko_KR", LANG_KOREAN, SUBLANG_KOREAN },
|
||||
{ "lt", LANG_LITHUANIAN, SUBLANG_NEUTRAL },
|
||||
//{"lt_LT", LANG_LITHUANIAN,SUBLANG_LITHUANIAN_LITHUANIA},
|
||||
{ "lv", LANG_LATVIAN, SUBLANG_NEUTRAL },
|
||||
{ "lv_LV", LANG_LATVIAN, SUBLANG_LATVIAN_LATVIA },
|
||||
{ "mk", LANG_MACEDONIAN, SUBLANG_NEUTRAL },
|
||||
{ "mk_MK", LANG_MACEDONIAN, SUBLANG_MACEDONIAN_MACEDONIA },
|
||||
{ "ms", LANG_MALAY, SUBLANG_NEUTRAL },
|
||||
{ "ms_MY", LANG_MALAY, SUBLANG_MALAY_MALAYSIA },
|
||||
{ "mt", LANG_MALTESE, SUBLANG_NEUTRAL },
|
||||
{ "mt_MT", LANG_MALTESE, SUBLANG_MALTESE_MALTA },
|
||||
{ "nl", LANG_DUTCH, SUBLANG_NEUTRAL },
|
||||
{ "nl_BE", LANG_DUTCH, SUBLANG_DUTCH_BELGIAN },
|
||||
{ "nl_NL", LANG_DUTCH, SUBLANG_DUTCH },
|
||||
{ "no", LANG_NORWEGIAN, SUBLANG_NEUTRAL },
|
||||
{ "no_NO", LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL },
|
||||
{ "no_NO_NY", LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK },
|
||||
{ "pl", LANG_POLISH, SUBLANG_NEUTRAL },
|
||||
{ "pl_PL", LANG_POLISH, SUBLANG_POLISH_POLAND },
|
||||
{ "pt", LANG_PORTUGUESE, SUBLANG_NEUTRAL },
|
||||
{ "pt_BR", LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN },
|
||||
{ "pt_PT", LANG_PORTUGUESE, SUBLANG_PORTUGUESE },
|
||||
{ "ro", LANG_ROMANIAN, SUBLANG_NEUTRAL },
|
||||
{ "ro_RO", LANG_ROMANIAN, SUBLANG_ROMANIAN_ROMANIA },
|
||||
{ "ru", LANG_RUSSIAN, SUBLANG_NEUTRAL },
|
||||
{ "ru_RU", LANG_RUSSIAN, SUBLANG_RUSSIAN_RUSSIA },
|
||||
{ "sk", LANG_SLOVAK, SUBLANG_NEUTRAL },
|
||||
{ "sk_SK", LANG_SLOVAK, SUBLANG_SLOVAK_SLOVAKIA },
|
||||
{ "sl", LANG_SLOVENIAN, SUBLANG_NEUTRAL },
|
||||
{ "sl_SI", LANG_SLOVENIAN, SUBLANG_SLOVENIAN_SLOVENIA },
|
||||
{ "sq", LANG_ALBANIAN, SUBLANG_NEUTRAL },
|
||||
{ "sq_AL", LANG_ALBANIAN, SUBLANG_ALBANIAN_ALBANIA },
|
||||
{ "sr", LANG_SERBIAN_NEUTRAL, SUBLANG_NEUTRAL },
|
||||
{ "sv", LANG_SWEDISH, SUBLANG_NEUTRAL },
|
||||
{ "sv_SE", LANG_SWEDISH, SUBLANG_SWEDISH },
|
||||
{ "th", LANG_THAI, SUBLANG_NEUTRAL },
|
||||
{ "th_TH", LANG_THAI, SUBLANG_THAI_THAILAND },
|
||||
{ "tr", LANG_TURKISH, SUBLANG_NEUTRAL },
|
||||
{ "tr_TR", LANG_TURKISH, SUBLANG_TURKISH_TURKEY },
|
||||
{ "uk", LANG_UKRAINIAN, SUBLANG_NEUTRAL },
|
||||
{ "uk_UA", LANG_UKRAINIAN, SUBLANG_UKRAINIAN_UKRAINE },
|
||||
{ "vi", LANG_VIETNAMESE, SUBLANG_NEUTRAL },
|
||||
{ "vi_VN", LANG_VIETNAMESE, SUBLANG_VIETNAMESE_VIETNAM },
|
||||
{ "zh", LANG_CHINESE, SUBLANG_NEUTRAL },
|
||||
{ "zh_CN", LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED },
|
||||
{ "zh_HK", LANG_CHINESE, SUBLANG_CHINESE_HONGKONG },
|
||||
{ "zh_SG", LANG_CHINESE, SUBLANG_CHINESE_SINGAPORE },
|
||||
{ "zh_TW", LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL },
|
||||
{ 0, 0, 0 },
|
||||
};
|
24
platform/windows/msvs.py
Normal file
24
platform/windows/msvs.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import methods
|
||||
|
||||
|
||||
# Tuples with the name of the arch that will be used in VS, mapped to our internal arch names.
|
||||
# For Windows platforms, Win32 is what VS wants. For other platforms, it can be different.
|
||||
def get_platforms():
|
||||
return [("Win32", "x86_32"), ("x64", "x86_64")]
|
||||
|
||||
|
||||
def get_configurations():
|
||||
return ["editor", "template_debug", "template_release"]
|
||||
|
||||
|
||||
def get_build_prefix(env):
|
||||
if not env.msvc:
|
||||
return []
|
||||
batch_file = methods.find_visual_c_batch_file(env)
|
||||
return [
|
||||
"cmd /V /C",
|
||||
"set "plat=$(PlatformTarget)"",
|
||||
"^& (if "$(PlatformTarget)"=="x64" (set "plat=x86_amd64"))",
|
||||
f"^& call "{batch_file}" !plat!",
|
||||
"^&",
|
||||
]
|
1184
platform/windows/native_menu_windows.cpp
Normal file
1184
platform/windows/native_menu_windows.cpp
Normal file
File diff suppressed because it is too large
Load Diff
154
platform/windows/native_menu_windows.h
Normal file
154
platform/windows/native_menu_windows.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/**************************************************************************/
|
||||
/* native_menu_windows.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/io/image.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "servers/display/native_menu.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
class NativeMenuWindows : public NativeMenu {
|
||||
GDCLASS(NativeMenuWindows, NativeMenu)
|
||||
|
||||
enum GlobalMenuCheckType {
|
||||
CHECKABLE_TYPE_NONE,
|
||||
CHECKABLE_TYPE_CHECK_BOX,
|
||||
CHECKABLE_TYPE_RADIO_BUTTON,
|
||||
};
|
||||
|
||||
struct MenuItemData {
|
||||
Callable callback;
|
||||
Variant meta;
|
||||
GlobalMenuCheckType checkable_type;
|
||||
bool checked = false;
|
||||
int max_states = 0;
|
||||
int state = 0;
|
||||
Ref<Image> img;
|
||||
HBITMAP bmp = 0;
|
||||
};
|
||||
|
||||
struct MenuData {
|
||||
HMENU menu = 0;
|
||||
|
||||
Callable close_cb;
|
||||
bool is_rtl = false;
|
||||
};
|
||||
|
||||
mutable RID_PtrOwner<MenuData> menus;
|
||||
HashMap<HMENU, RID> menu_lookup;
|
||||
|
||||
HBITMAP _make_bitmap(const Ref<Image> &p_img) const;
|
||||
|
||||
public:
|
||||
void _menu_activate(HMENU p_menu, int p_index) const;
|
||||
|
||||
virtual bool has_feature(Feature p_feature) const override;
|
||||
|
||||
virtual bool has_system_menu(SystemMenus p_menu_id) const override;
|
||||
virtual RID get_system_menu(SystemMenus p_menu_id) const override;
|
||||
|
||||
virtual RID create_menu() override;
|
||||
virtual bool has_menu(const RID &p_rid) const override;
|
||||
virtual void free_menu(const RID &p_rid) override;
|
||||
|
||||
virtual Size2 get_size(const RID &p_rid) const override;
|
||||
virtual void popup(const RID &p_rid, const Vector2i &p_position) override;
|
||||
|
||||
virtual void set_interface_direction(const RID &p_rid, bool p_is_rtl) override;
|
||||
virtual void set_popup_open_callback(const RID &p_rid, const Callable &p_callback) override;
|
||||
virtual Callable get_popup_open_callback(const RID &p_rid) const override;
|
||||
virtual void set_popup_close_callback(const RID &p_rid, const Callable &p_callback) override;
|
||||
virtual Callable get_popup_close_callback(const RID &p_rid) const override;
|
||||
virtual void set_minimum_width(const RID &p_rid, float p_width) override;
|
||||
virtual float get_minimum_width(const RID &p_rid) const override;
|
||||
|
||||
virtual bool is_opened(const RID &p_rid) const override;
|
||||
|
||||
virtual int add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag = Variant(), int p_index = -1) override;
|
||||
virtual int add_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
|
||||
virtual int add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
|
||||
virtual int add_icon_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
|
||||
virtual int add_icon_check_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
|
||||
virtual int add_radio_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
|
||||
virtual int add_icon_radio_check_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
|
||||
virtual int add_multistate_item(const RID &p_rid, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback = Callable(), const Callable &p_key_callback = Callable(), const Variant &p_tag = Variant(), Key p_accel = Key::NONE, int p_index = -1) override;
|
||||
virtual int add_separator(const RID &p_rid, int p_index = -1) override;
|
||||
|
||||
virtual int find_item_index_with_text(const RID &p_rid, const String &p_text) const override;
|
||||
virtual int find_item_index_with_tag(const RID &p_rid, const Variant &p_tag) const override;
|
||||
|
||||
virtual bool is_item_checked(const RID &p_rid, int p_idx) const override;
|
||||
virtual bool is_item_checkable(const RID &p_rid, int p_idx) const override;
|
||||
virtual bool is_item_radio_checkable(const RID &p_rid, int p_idx) const override;
|
||||
virtual Callable get_item_callback(const RID &p_rid, int p_idx) const override;
|
||||
virtual Callable get_item_key_callback(const RID &p_rid, int p_idx) const override;
|
||||
virtual Variant get_item_tag(const RID &p_rid, int p_idx) const override;
|
||||
virtual String get_item_text(const RID &p_rid, int p_idx) const override;
|
||||
virtual RID get_item_submenu(const RID &p_rid, int p_idx) const override;
|
||||
virtual Key get_item_accelerator(const RID &p_rid, int p_idx) const override;
|
||||
virtual bool is_item_disabled(const RID &p_rid, int p_idx) const override;
|
||||
virtual bool is_item_hidden(const RID &p_rid, int p_idx) const override;
|
||||
virtual String get_item_tooltip(const RID &p_rid, int p_idx) const override;
|
||||
virtual int get_item_state(const RID &p_rid, int p_idx) const override;
|
||||
virtual int get_item_max_states(const RID &p_rid, int p_idx) const override;
|
||||
virtual Ref<Texture2D> get_item_icon(const RID &p_rid, int p_idx) const override;
|
||||
virtual int get_item_indentation_level(const RID &p_rid, int p_idx) const override;
|
||||
|
||||
virtual void set_item_checked(const RID &p_rid, int p_idx, bool p_checked) override;
|
||||
virtual void set_item_checkable(const RID &p_rid, int p_idx, bool p_checkable) override;
|
||||
virtual void set_item_radio_checkable(const RID &p_rid, int p_idx, bool p_checkable) override;
|
||||
virtual void set_item_callback(const RID &p_rid, int p_idx, const Callable &p_callback) override;
|
||||
virtual void set_item_key_callback(const RID &p_rid, int p_idx, const Callable &p_key_callback) override;
|
||||
virtual void set_item_hover_callbacks(const RID &p_rid, int p_idx, const Callable &p_callback) override;
|
||||
virtual void set_item_tag(const RID &p_rid, int p_idx, const Variant &p_tag) override;
|
||||
virtual void set_item_text(const RID &p_rid, int p_idx, const String &p_text) override;
|
||||
virtual void set_item_submenu(const RID &p_rid, int p_idx, const RID &p_submenu_rid) override;
|
||||
virtual void set_item_accelerator(const RID &p_rid, int p_idx, Key p_keycode) override;
|
||||
virtual void set_item_disabled(const RID &p_rid, int p_idx, bool p_disabled) override;
|
||||
virtual void set_item_hidden(const RID &p_rid, int p_idx, bool p_hidden) override;
|
||||
virtual void set_item_tooltip(const RID &p_rid, int p_idx, const String &p_tooltip) override;
|
||||
virtual void set_item_state(const RID &p_rid, int p_idx, int p_state) override;
|
||||
virtual void set_item_max_states(const RID &p_rid, int p_idx, int p_max_states) override;
|
||||
virtual void set_item_icon(const RID &p_rid, int p_idx, const Ref<Texture2D> &p_icon) override;
|
||||
virtual void set_item_indentation_level(const RID &p_rid, int p_idx, int p_level) override;
|
||||
|
||||
virtual int get_item_count(const RID &p_rid) const override;
|
||||
virtual bool is_system_menu(const RID &p_rid) const override;
|
||||
|
||||
virtual void remove_item(const RID &p_rid, int p_idx) override;
|
||||
virtual void clear(const RID &p_rid) override;
|
||||
|
||||
NativeMenuWindows();
|
||||
~NativeMenuWindows();
|
||||
};
|
2715
platform/windows/os_windows.cpp
Normal file
2715
platform/windows/os_windows.cpp
Normal file
File diff suppressed because it is too large
Load Diff
274
platform/windows/os_windows.h
Normal file
274
platform/windows/os_windows.h
Normal file
@@ -0,0 +1,274 @@
|
||||
/**************************************************************************/
|
||||
/* os_windows.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "crash_handler_windows.h"
|
||||
#include "key_mapping_windows.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/input/input.h"
|
||||
#include "core/os/os.h"
|
||||
#include "drivers/wasapi/audio_driver_wasapi.h"
|
||||
#include "drivers/winmidi/midi_driver_winmidi.h"
|
||||
#include "servers/audio_server.h"
|
||||
|
||||
#ifdef XAUDIO2_ENABLED
|
||||
#include "drivers/xaudio2/audio_driver_xaudio2.h"
|
||||
#endif
|
||||
|
||||
#if defined(RD_ENABLED)
|
||||
#include "servers/rendering/rendering_device.h"
|
||||
#endif
|
||||
|
||||
#include <io.h>
|
||||
#include <shellapi.h>
|
||||
#include <cstdio>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <dwrite.h>
|
||||
#include <dwrite_2.h>
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
// forward error messages to OutputDebugString
|
||||
#define WINDOWS_DEBUG_OUTPUT_ENABLED
|
||||
#endif
|
||||
|
||||
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
|
||||
#endif
|
||||
|
||||
#ifndef SAFE_RELEASE // when Windows Media Device M? is not present
|
||||
#define SAFE_RELEASE(x) \
|
||||
if (x != nullptr) { \
|
||||
x->Release(); \
|
||||
x = nullptr; \
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
class ComAutoreleaseRef {
|
||||
public:
|
||||
T *reference = nullptr;
|
||||
|
||||
_FORCE_INLINE_ T *operator->() { return reference; }
|
||||
_FORCE_INLINE_ const T *operator->() const { return reference; }
|
||||
_FORCE_INLINE_ T *operator*() { return reference; }
|
||||
_FORCE_INLINE_ const T *operator*() const { return reference; }
|
||||
_FORCE_INLINE_ bool is_valid() const { return reference != nullptr; }
|
||||
_FORCE_INLINE_ bool is_null() const { return reference == nullptr; }
|
||||
ComAutoreleaseRef() {}
|
||||
ComAutoreleaseRef(T *p_ref) {
|
||||
reference = p_ref;
|
||||
}
|
||||
~ComAutoreleaseRef() {
|
||||
if (reference != nullptr) {
|
||||
reference->Release();
|
||||
reference = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class OS_Windows : public OS {
|
||||
uint64_t target_ticks = 0;
|
||||
uint64_t ticks_start = 0;
|
||||
uint64_t ticks_per_second = 0;
|
||||
uint64_t delay_resolution = 1000;
|
||||
|
||||
HINSTANCE hInstance;
|
||||
MainLoop *main_loop = nullptr;
|
||||
|
||||
#ifdef WASAPI_ENABLED
|
||||
AudioDriverWASAPI driver_wasapi;
|
||||
#endif
|
||||
#ifdef XAUDIO2_ENABLED
|
||||
AudioDriverXAudio2 driver_xaudio2;
|
||||
#endif
|
||||
#ifdef WINMIDI_ENABLED
|
||||
MIDIDriverWinMidi driver_midi;
|
||||
#endif
|
||||
|
||||
CrashHandler crash_handler;
|
||||
|
||||
#ifdef WINDOWS_DEBUG_OUTPUT_ENABLED
|
||||
ErrorHandlerList error_handlers;
|
||||
#endif
|
||||
|
||||
HWND main_window;
|
||||
|
||||
IDWriteFactory *dwrite_factory = nullptr;
|
||||
IDWriteFactory2 *dwrite_factory2 = nullptr;
|
||||
IDWriteFontCollection *font_collection = nullptr;
|
||||
IDWriteFontFallback *system_font_fallback = nullptr;
|
||||
|
||||
bool dwrite_init = false;
|
||||
bool dwrite2_init = false;
|
||||
|
||||
HashMap<void *, String> temp_libraries;
|
||||
|
||||
void _remove_temp_library(void *p_library_handle);
|
||||
String _get_default_fontname(const String &p_font_name) const;
|
||||
DWRITE_FONT_WEIGHT _weight_to_dw(int p_weight) const;
|
||||
DWRITE_FONT_STRETCH _stretch_to_dw(int p_stretch) const;
|
||||
|
||||
bool is_using_con_wrapper() const;
|
||||
|
||||
HashMap<String, int> encodings;
|
||||
void _init_encodings();
|
||||
|
||||
// functions used by main to initialize/deinitialize the OS
|
||||
protected:
|
||||
virtual void initialize() override;
|
||||
|
||||
virtual void set_main_loop(MainLoop *p_main_loop) override;
|
||||
virtual void delete_main_loop() override;
|
||||
|
||||
virtual void finalize() override;
|
||||
virtual void finalize_core() override;
|
||||
|
||||
virtual String get_stdin_string(int64_t p_buffer_size = 1024) override;
|
||||
virtual PackedByteArray get_stdin_buffer(int64_t p_buffer_size = 1024) override;
|
||||
virtual StdHandleType get_stdin_type() const override;
|
||||
virtual StdHandleType get_stdout_type() const override;
|
||||
virtual StdHandleType get_stderr_type() const override;
|
||||
|
||||
String _quote_command_line_argument(const String &p_text) const;
|
||||
|
||||
struct ProcessInfo {
|
||||
STARTUPINFOEX si;
|
||||
PROCESS_INFORMATION pi;
|
||||
mutable bool is_running = true;
|
||||
mutable int exit_code = -1;
|
||||
};
|
||||
HashMap<ProcessID, ProcessInfo> *process_map = nullptr;
|
||||
Mutex process_map_mutex;
|
||||
|
||||
public:
|
||||
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
|
||||
|
||||
virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override;
|
||||
|
||||
virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, GDExtensionData *p_data = nullptr) 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 MainLoop *get_main_loop() const override;
|
||||
|
||||
virtual String get_name() const override;
|
||||
virtual String get_distribution_name() const override;
|
||||
virtual String get_version() const override;
|
||||
virtual String get_version_alias() const override;
|
||||
|
||||
virtual Vector<String> get_video_adapter_driver_info() const override;
|
||||
virtual bool get_user_prefers_integrated_gpu() const override;
|
||||
|
||||
virtual void initialize_joypads() override {}
|
||||
|
||||
virtual DateTime get_datetime(bool p_utc) const override;
|
||||
virtual TimeZoneInfo get_time_zone_info() const override;
|
||||
virtual double get_unix_time() const override;
|
||||
|
||||
virtual Error set_cwd(const String &p_cwd) override;
|
||||
|
||||
virtual void add_frame_delay(bool p_can_draw, bool p_wake_for_events) override;
|
||||
virtual void delay_usec(uint32_t p_usec) const override;
|
||||
virtual uint64_t get_ticks_usec() const override;
|
||||
|
||||
virtual Dictionary get_memory_info() const override;
|
||||
|
||||
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, bool p_open_console = false) override;
|
||||
virtual Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments, bool p_blocking = true) override;
|
||||
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
|
||||
virtual Error kill(const ProcessID &p_pid) override;
|
||||
virtual int get_process_id() const override;
|
||||
virtual bool is_process_running(const ProcessID &p_pid) const override;
|
||||
virtual int get_process_exit_code(const ProcessID &p_pid) const override;
|
||||
|
||||
virtual bool has_environment(const String &p_var) const override;
|
||||
virtual String get_environment(const String &p_var) const override;
|
||||
virtual void set_environment(const String &p_var, const String &p_value) const override;
|
||||
virtual void unset_environment(const String &p_var) const override;
|
||||
|
||||
virtual Vector<String> get_system_fonts() const override;
|
||||
virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override;
|
||||
virtual Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const override;
|
||||
|
||||
virtual String get_executable_path() const override;
|
||||
|
||||
virtual String get_locale() const override;
|
||||
|
||||
virtual String get_processor_name() const override;
|
||||
|
||||
virtual String get_model_name() const override;
|
||||
|
||||
virtual uint64_t get_embedded_pck_offset() const override;
|
||||
|
||||
virtual String get_config_path() const override;
|
||||
virtual String get_data_path() const override;
|
||||
virtual String get_cache_path() const override;
|
||||
virtual String get_temp_path() const override;
|
||||
virtual String get_godot_dir_name() const override;
|
||||
|
||||
virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override;
|
||||
virtual String get_user_data_dir(const String &p_user_dir) const override;
|
||||
|
||||
virtual String get_unique_id() const override;
|
||||
|
||||
virtual Error shell_open(const String &p_uri) override;
|
||||
virtual Error shell_show_in_file_manager(String p_path, bool p_open_folder) override;
|
||||
|
||||
void run();
|
||||
|
||||
virtual bool _check_internal_feature_support(const String &p_feature) override;
|
||||
|
||||
virtual String multibyte_to_string(const String &p_encoding, const PackedByteArray &p_array) const override;
|
||||
virtual PackedByteArray string_to_multibyte(const String &p_encoding, const String &p_string) const override;
|
||||
|
||||
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) override;
|
||||
|
||||
virtual String get_system_ca_certificates() override;
|
||||
|
||||
void set_main_window(HWND p_main_window) { main_window = p_main_window; }
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
virtual bool _test_create_rendering_device_and_gl(const String &p_display_driver) const override;
|
||||
virtual bool _test_create_rendering_device(const String &p_display_driver) const override;
|
||||
#endif
|
||||
|
||||
HINSTANCE get_hinstance() { return hInstance; }
|
||||
OS_Windows(HINSTANCE _hInstance);
|
||||
~OS_Windows();
|
||||
};
|
33
platform/windows/platform_config.h
Normal file
33
platform/windows/platform_config.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/**************************************************************************/
|
||||
/* platform_config.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <malloc.h>
|
50
platform/windows/platform_gl.h
Normal file
50
platform/windows/platform_gl.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/**************************************************************************/
|
||||
/* platform_gl.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef GL_API_ENABLED
|
||||
#define GL_API_ENABLED // Allow using desktop GL.
|
||||
#endif
|
||||
|
||||
#ifndef GLES_API_ENABLED
|
||||
#define GLES_API_ENABLED // Allow using GLES (ANGLE).
|
||||
#endif
|
||||
|
||||
#ifdef EGL_STATIC
|
||||
#define KHRONOS_STATIC 1
|
||||
#include "thirdparty/angle/include/EGL/egl.h"
|
||||
#include "thirdparty/angle/include/EGL/eglext.h"
|
||||
#undef KHRONOS_STATIC
|
||||
#else
|
||||
#include "thirdparty/glad/glad/egl.h"
|
||||
#endif
|
||||
|
||||
#include "thirdparty/glad/glad/gl.h"
|
12
platform/windows/platform_windows_builders.py
Normal file
12
platform/windows/platform_windows_builders.py
Normal file
@@ -0,0 +1,12 @@
|
||||
"""Functions used to generate source files during build time"""
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def make_debug_mingw(target, source, env):
|
||||
dst = str(target[0])
|
||||
# Force separate debug symbols if executable size is larger than 1.9 GB.
|
||||
if env["separate_debug_symbols"] or os.stat(dst).st_size >= 2040109465:
|
||||
os.system("{0} --only-keep-debug {1} {1}.debugsymbols".format(env["OBJCOPY"], dst))
|
||||
os.system("{0} --strip-debug --strip-unneeded {1}".format(env["STRIP"], dst))
|
||||
os.system("{0} --add-gnu-debuglink={1}.debugsymbols {1}".format(env["OBJCOPY"], dst))
|
71
platform/windows/rendering_context_driver_vulkan_windows.cpp
Normal file
71
platform/windows/rendering_context_driver_vulkan_windows.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_context_driver_vulkan_windows.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#if defined(WINDOWS_ENABLED) && defined(VULKAN_ENABLED)
|
||||
|
||||
#include "core/os/os.h"
|
||||
|
||||
#include "rendering_context_driver_vulkan_windows.h"
|
||||
|
||||
#include "drivers/vulkan/godot_vulkan.h"
|
||||
|
||||
const char *RenderingContextDriverVulkanWindows::_get_platform_surface_extension() const {
|
||||
return VK_KHR_WIN32_SURFACE_EXTENSION_NAME;
|
||||
}
|
||||
|
||||
RenderingContextDriverVulkanWindows::RenderingContextDriverVulkanWindows() {
|
||||
// Workaround for Vulkan not working on setups with AMD integrated graphics + NVIDIA dedicated GPU (GH-57708).
|
||||
// This prevents using AMD integrated graphics with Vulkan entirely, but it allows the engine to start
|
||||
// even on outdated/broken driver setups.
|
||||
OS::get_singleton()->set_environment("DISABLE_LAYER_AMD_SWITCHABLE_GRAPHICS_1", "1");
|
||||
}
|
||||
|
||||
RenderingContextDriverVulkanWindows::~RenderingContextDriverVulkanWindows() {
|
||||
// Does nothing.
|
||||
}
|
||||
|
||||
RenderingContextDriver::SurfaceID RenderingContextDriverVulkanWindows::surface_create(const void *p_platform_data) {
|
||||
const WindowPlatformData *wpd = (const WindowPlatformData *)(p_platform_data);
|
||||
|
||||
VkWin32SurfaceCreateInfoKHR create_info = {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
|
||||
create_info.hinstance = wpd->instance;
|
||||
create_info.hwnd = wpd->window;
|
||||
|
||||
VkSurfaceKHR vk_surface = VK_NULL_HANDLE;
|
||||
VkResult err = vkCreateWin32SurfaceKHR(instance_get(), &create_info, get_allocation_callbacks(VK_OBJECT_TYPE_SURFACE_KHR), &vk_surface);
|
||||
ERR_FAIL_COND_V(err != VK_SUCCESS, SurfaceID());
|
||||
|
||||
Surface *surface = memnew(Surface);
|
||||
surface->vk_surface = vk_surface;
|
||||
return SurfaceID(surface);
|
||||
}
|
||||
|
||||
#endif // WINDOWS_ENABLED && VULKAN_ENABLED
|
57
platform/windows/rendering_context_driver_vulkan_windows.h
Normal file
57
platform/windows/rendering_context_driver_vulkan_windows.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/**************************************************************************/
|
||||
/* rendering_context_driver_vulkan_windows.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef VULKAN_ENABLED
|
||||
|
||||
#include "drivers/vulkan/rendering_context_driver_vulkan.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
class RenderingContextDriverVulkanWindows : public RenderingContextDriverVulkan {
|
||||
private:
|
||||
const char *_get_platform_surface_extension() const override final;
|
||||
|
||||
protected:
|
||||
SurfaceID surface_create(const void *p_platform_data) override final;
|
||||
|
||||
public:
|
||||
struct WindowPlatformData {
|
||||
HWND window;
|
||||
HINSTANCE instance;
|
||||
};
|
||||
|
||||
RenderingContextDriverVulkanWindows();
|
||||
~RenderingContextDriverVulkanWindows() override;
|
||||
};
|
||||
|
||||
#endif // VULKAN_ENABLED
|
272
platform/windows/tts_windows.cpp
Normal file
272
platform/windows/tts_windows.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
/**************************************************************************/
|
||||
/* tts_windows.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 "tts_windows.h"
|
||||
|
||||
TTS_Windows *TTS_Windows::singleton = nullptr;
|
||||
|
||||
void __stdcall TTS_Windows::speech_event_callback(WPARAM wParam, LPARAM lParam) {
|
||||
TTS_Windows *tts = TTS_Windows::get_singleton();
|
||||
SPEVENT event;
|
||||
while (tts->synth->GetEvents(1, &event, nullptr) == S_OK) {
|
||||
uint32_t stream_num = (uint32_t)event.ulStreamNum;
|
||||
if (tts->ids.has(stream_num)) {
|
||||
if (event.eEventId == SPEI_START_INPUT_STREAM) {
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_STARTED, tts->ids[stream_num].id);
|
||||
} else if (event.eEventId == SPEI_END_INPUT_STREAM) {
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_ENDED, tts->ids[stream_num].id);
|
||||
tts->ids.erase(stream_num);
|
||||
tts->update_requested = true;
|
||||
} else if (event.eEventId == SPEI_WORD_BOUNDARY) {
|
||||
const Char16String &string = tts->ids[stream_num].string;
|
||||
int pos = 0;
|
||||
for (int i = 0; i < MIN(event.lParam, string.length()); i++) {
|
||||
char16_t c = string[i];
|
||||
if ((c & 0xfffffc00) == 0xd800) {
|
||||
i++;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_BOUNDARY, tts->ids[stream_num].id, pos - tts->ids[stream_num].offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TTS_Windows::process_events() {
|
||||
if (update_requested && !paused && queue.size() > 0 && !is_speaking()) {
|
||||
DisplayServer::TTSUtterance &message = queue.front()->get();
|
||||
|
||||
String text;
|
||||
DWORD flags = SPF_ASYNC | SPF_PURGEBEFORESPEAK | SPF_IS_XML;
|
||||
String pitch_tag = String("<pitch absmiddle=\"") + String::num_int64(message.pitch * 10 - 10, 10) + String("\">");
|
||||
text = pitch_tag + message.text + String("</pitch>");
|
||||
|
||||
IEnumSpObjectTokens *cpEnum;
|
||||
ISpObjectToken *cpVoiceToken;
|
||||
ULONG ulCount = 0;
|
||||
ULONG stream_number = 0;
|
||||
ISpObjectTokenCategory *cpCategory;
|
||||
HRESULT hr = CoCreateInstance(CLSID_SpObjectTokenCategory, nullptr, CLSCTX_INPROC_SERVER, IID_ISpObjectTokenCategory, (void **)&cpCategory);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = cpCategory->SetId(SPCAT_VOICES, false);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = cpCategory->EnumTokens(nullptr, nullptr, &cpEnum);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = cpEnum->GetCount(&ulCount);
|
||||
while (SUCCEEDED(hr) && ulCount--) {
|
||||
wchar_t *w_id = nullptr;
|
||||
hr = cpEnum->Next(1, &cpVoiceToken, nullptr);
|
||||
cpVoiceToken->GetId(&w_id);
|
||||
if (String::utf16((const char16_t *)w_id) == message.voice) {
|
||||
synth->SetVoice(cpVoiceToken);
|
||||
cpVoiceToken->Release();
|
||||
break;
|
||||
}
|
||||
cpVoiceToken->Release();
|
||||
}
|
||||
cpEnum->Release();
|
||||
}
|
||||
}
|
||||
cpCategory->Release();
|
||||
}
|
||||
|
||||
UTData ut;
|
||||
ut.string = text.utf16();
|
||||
ut.offset = pitch_tag.length(); // Subtract injected <pitch> tag offset.
|
||||
ut.id = message.id;
|
||||
|
||||
synth->SetVolume(message.volume);
|
||||
synth->SetRate(10.f * std::log10(message.rate) / std::log10(3.f));
|
||||
synth->Speak((LPCWSTR)ut.string.get_data(), flags, &stream_number);
|
||||
|
||||
ids[(uint32_t)stream_number] = ut;
|
||||
|
||||
queue.pop_front();
|
||||
|
||||
update_requested = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool TTS_Windows::is_speaking() const {
|
||||
ERR_FAIL_NULL_V(synth, false);
|
||||
|
||||
SPVOICESTATUS status;
|
||||
synth->GetStatus(&status, nullptr);
|
||||
return (status.dwRunningState == SPRS_IS_SPEAKING || status.dwRunningState == 0 /* Waiting To Speak */);
|
||||
}
|
||||
|
||||
bool TTS_Windows::is_paused() const {
|
||||
ERR_FAIL_NULL_V(synth, false);
|
||||
return paused;
|
||||
}
|
||||
|
||||
Array TTS_Windows::get_voices() const {
|
||||
Array list;
|
||||
IEnumSpObjectTokens *cpEnum;
|
||||
ISpObjectToken *cpVoiceToken;
|
||||
ISpDataKey *cpDataKeyAttribs;
|
||||
ULONG ulCount = 0;
|
||||
ISpObjectTokenCategory *cpCategory;
|
||||
HRESULT hr = CoCreateInstance(CLSID_SpObjectTokenCategory, nullptr, CLSCTX_INPROC_SERVER, IID_ISpObjectTokenCategory, (void **)&cpCategory);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = cpCategory->SetId(SPCAT_VOICES, false);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = cpCategory->EnumTokens(nullptr, nullptr, &cpEnum);
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = cpEnum->GetCount(&ulCount);
|
||||
while (SUCCEEDED(hr) && ulCount--) {
|
||||
hr = cpEnum->Next(1, &cpVoiceToken, nullptr);
|
||||
HRESULT hr_attr = cpVoiceToken->OpenKey(SPTOKENKEY_ATTRIBUTES, &cpDataKeyAttribs);
|
||||
if (SUCCEEDED(hr_attr)) {
|
||||
wchar_t *w_id = nullptr;
|
||||
wchar_t *w_lang = nullptr;
|
||||
wchar_t *w_name = nullptr;
|
||||
cpVoiceToken->GetId(&w_id);
|
||||
cpDataKeyAttribs->GetStringValue(L"Language", &w_lang);
|
||||
cpDataKeyAttribs->GetStringValue(nullptr, &w_name);
|
||||
LCID locale = wcstol(w_lang, nullptr, 16);
|
||||
|
||||
int locale_chars = GetLocaleInfoW(locale, LOCALE_SISO639LANGNAME, nullptr, 0);
|
||||
int region_chars = GetLocaleInfoW(locale, LOCALE_SISO3166CTRYNAME, nullptr, 0);
|
||||
wchar_t *w_lang_code = new wchar_t[locale_chars];
|
||||
wchar_t *w_reg_code = new wchar_t[region_chars];
|
||||
GetLocaleInfoW(locale, LOCALE_SISO639LANGNAME, w_lang_code, locale_chars);
|
||||
GetLocaleInfoW(locale, LOCALE_SISO3166CTRYNAME, w_reg_code, region_chars);
|
||||
|
||||
Dictionary voice_d;
|
||||
voice_d["id"] = String::utf16((const char16_t *)w_id);
|
||||
if (w_name) {
|
||||
voice_d["name"] = String::utf16((const char16_t *)w_name);
|
||||
} else {
|
||||
voice_d["name"] = voice_d["id"].operator String().replace("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Speech\\Voices\\Tokens\\", "");
|
||||
}
|
||||
voice_d["language"] = String::utf16((const char16_t *)w_lang_code) + "_" + String::utf16((const char16_t *)w_reg_code);
|
||||
list.push_back(voice_d);
|
||||
|
||||
delete[] w_lang_code;
|
||||
delete[] w_reg_code;
|
||||
|
||||
cpDataKeyAttribs->Release();
|
||||
}
|
||||
cpVoiceToken->Release();
|
||||
}
|
||||
cpEnum->Release();
|
||||
}
|
||||
}
|
||||
cpCategory->Release();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void TTS_Windows::speak(const String &p_text, const String &p_voice, int p_volume, float p_pitch, float p_rate, int p_utterance_id, bool p_interrupt) {
|
||||
ERR_FAIL_NULL(synth);
|
||||
if (p_interrupt) {
|
||||
stop();
|
||||
}
|
||||
|
||||
if (p_text.is_empty()) {
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, p_utterance_id);
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayServer::TTSUtterance message;
|
||||
message.text = p_text;
|
||||
message.voice = p_voice;
|
||||
message.volume = CLAMP(p_volume, 0, 100);
|
||||
message.pitch = CLAMP(p_pitch, 0.f, 2.f);
|
||||
message.rate = CLAMP(p_rate, 0.1f, 10.f);
|
||||
message.id = p_utterance_id;
|
||||
queue.push_back(message);
|
||||
|
||||
if (is_paused()) {
|
||||
resume();
|
||||
} else {
|
||||
update_requested = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TTS_Windows::pause() {
|
||||
ERR_FAIL_NULL(synth);
|
||||
if (!paused) {
|
||||
if (synth->Pause() == S_OK) {
|
||||
paused = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TTS_Windows::resume() {
|
||||
ERR_FAIL_NULL(synth);
|
||||
synth->Resume();
|
||||
paused = false;
|
||||
}
|
||||
|
||||
void TTS_Windows::stop() {
|
||||
ERR_FAIL_NULL(synth);
|
||||
|
||||
SPVOICESTATUS status;
|
||||
synth->GetStatus(&status, nullptr);
|
||||
uint32_t current_stream = (uint32_t)status.ulCurrentStream;
|
||||
if (ids.has(current_stream)) {
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, ids[current_stream].id);
|
||||
ids.erase(current_stream);
|
||||
}
|
||||
for (DisplayServer::TTSUtterance &message : queue) {
|
||||
DisplayServer::get_singleton()->tts_post_utterance_event(DisplayServer::TTS_UTTERANCE_CANCELED, message.id);
|
||||
}
|
||||
queue.clear();
|
||||
synth->Speak(nullptr, SPF_PURGEBEFORESPEAK, nullptr);
|
||||
synth->Resume();
|
||||
paused = false;
|
||||
}
|
||||
|
||||
TTS_Windows *TTS_Windows::get_singleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
TTS_Windows::TTS_Windows() {
|
||||
singleton = this;
|
||||
|
||||
if (SUCCEEDED(CoCreateInstance(CLSID_SpVoice, nullptr, CLSCTX_ALL, IID_ISpVoice, (void **)&synth))) {
|
||||
ULONGLONG event_mask = SPFEI(SPEI_END_INPUT_STREAM) | SPFEI(SPEI_START_INPUT_STREAM) | SPFEI(SPEI_WORD_BOUNDARY);
|
||||
synth->SetInterest(event_mask, event_mask);
|
||||
synth->SetNotifyCallbackFunction(&speech_event_callback, (WPARAM)(this), 0);
|
||||
print_verbose("Text-to-Speech: SAPI initialized.");
|
||||
} else {
|
||||
print_verbose("Text-to-Speech: Cannot initialize ISpVoice!");
|
||||
}
|
||||
}
|
||||
|
||||
TTS_Windows::~TTS_Windows() {
|
||||
if (synth) {
|
||||
synth->Release();
|
||||
}
|
||||
singleton = nullptr;
|
||||
}
|
79
platform/windows/tts_windows.h
Normal file
79
platform/windows/tts_windows.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/**************************************************************************/
|
||||
/* tts_windows.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/templates/list.h"
|
||||
#include "core/variant/array.h"
|
||||
#include "servers/display_server.h"
|
||||
|
||||
#include <objbase.h>
|
||||
#include <sapi.h>
|
||||
#include <winnls.h>
|
||||
#include <cwchar>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
class TTS_Windows {
|
||||
List<DisplayServer::TTSUtterance> queue;
|
||||
ISpVoice *synth = nullptr;
|
||||
bool paused = false;
|
||||
struct UTData {
|
||||
Char16String string;
|
||||
int offset;
|
||||
int id;
|
||||
};
|
||||
HashMap<uint32_t, UTData> ids;
|
||||
bool update_requested = false;
|
||||
|
||||
static void __stdcall speech_event_callback(WPARAM wParam, LPARAM lParam);
|
||||
|
||||
static TTS_Windows *singleton;
|
||||
|
||||
public:
|
||||
static TTS_Windows *get_singleton();
|
||||
|
||||
bool is_speaking() const;
|
||||
bool is_paused() const;
|
||||
Array get_voices() const;
|
||||
|
||||
void speak(const String &p_text, const String &p_voice, int p_volume = 50, float p_pitch = 1.f, float p_rate = 1.f, int p_utterance_id = 0, bool p_interrupt = false);
|
||||
void pause();
|
||||
void resume();
|
||||
void stop();
|
||||
|
||||
void process_events();
|
||||
|
||||
TTS_Windows();
|
||||
~TTS_Windows();
|
||||
};
|
115
platform/windows/uiautomationcore.arm32.def
Normal file
115
platform/windows/uiautomationcore.arm32.def
Normal file
@@ -0,0 +1,115 @@
|
||||
;
|
||||
; Definition file of UIAutomationCore.DLL
|
||||
; Automatic generated by gendef
|
||||
; written by Kai Tietz 2008
|
||||
;
|
||||
LIBRARY "UIAutomationCore.DLL"
|
||||
EXPORTS
|
||||
DllGetActivationFactory
|
||||
DllCanUnloadNow
|
||||
DllGetClassObject
|
||||
DockPattern_SetDockPosition
|
||||
ExpandCollapsePattern_Collapse
|
||||
ExpandCollapsePattern_Expand
|
||||
GridPattern_GetItem
|
||||
IgnoreLeaksInCurrentlyTrackedMemory
|
||||
InitializeChannelBasedConnectionForProviderProxy
|
||||
InvokePattern_Invoke
|
||||
IsIgnoringLeaks
|
||||
ItemContainerPattern_FindItemByProperty
|
||||
LegacyIAccessiblePattern_DoDefaultAction
|
||||
LegacyIAccessiblePattern_GetIAccessible
|
||||
LegacyIAccessiblePattern_Select
|
||||
LegacyIAccessiblePattern_SetValue
|
||||
MultipleViewPattern_GetViewName
|
||||
MultipleViewPattern_SetCurrentView
|
||||
PostTestCheckForLeaks
|
||||
RangeValuePattern_SetValue
|
||||
ScrollItemPattern_ScrollIntoView
|
||||
ScrollPattern_Scroll
|
||||
ScrollPattern_SetScrollPercent
|
||||
SelectionItemPattern_AddToSelection
|
||||
SelectionItemPattern_RemoveFromSelection
|
||||
SelectionItemPattern_Select
|
||||
StartIgnoringLeaks
|
||||
StopIgnoringLeaks
|
||||
SynchronizedInputPattern_Cancel
|
||||
SynchronizedInputPattern_StartListening
|
||||
TextPattern_GetSelection
|
||||
TextPattern_GetVisibleRanges
|
||||
TextPattern_RangeFromChild
|
||||
TextPattern_RangeFromPoint
|
||||
TextPattern_get_DocumentRange
|
||||
TextPattern_get_SupportedTextSelection
|
||||
TextRange_AddToSelection
|
||||
TextRange_Clone
|
||||
TextRange_Compare
|
||||
TextRange_CompareEndpoints
|
||||
TextRange_ExpandToEnclosingUnit
|
||||
TextRange_FindAttribute
|
||||
TextRange_FindText
|
||||
TextRange_GetAttributeValue
|
||||
TextRange_GetBoundingRectangles
|
||||
TextRange_GetChildren
|
||||
TextRange_GetEnclosingElement
|
||||
TextRange_GetText
|
||||
TextRange_Move
|
||||
TextRange_MoveEndpointByRange
|
||||
TextRange_MoveEndpointByUnit
|
||||
TextRange_RemoveFromSelection
|
||||
TextRange_ScrollIntoView
|
||||
TextRange_Select
|
||||
TogglePattern_Toggle
|
||||
TransformPattern_Move
|
||||
TransformPattern_Resize
|
||||
TransformPattern_Rotate
|
||||
UiaAddEvent
|
||||
UiaClientsAreListening
|
||||
UiaDisconnectAllProviders
|
||||
UiaDisconnectProvider
|
||||
UiaEventAddWindow
|
||||
UiaEventRemoveWindow
|
||||
UiaFind
|
||||
UiaGetErrorDescription
|
||||
UiaGetPatternProvider
|
||||
UiaGetPropertyValue
|
||||
UiaGetReservedMixedAttributeValue
|
||||
UiaGetReservedNotSupportedValue
|
||||
UiaGetRootNode
|
||||
UiaGetRuntimeId
|
||||
UiaGetUpdatedCache
|
||||
UiaHPatternObjectFromVariant
|
||||
UiaHTextRangeFromVariant
|
||||
UiaHUiaNodeFromVariant
|
||||
UiaHasServerSideProvider
|
||||
UiaHostProviderFromHwnd
|
||||
UiaIAccessibleFromProvider
|
||||
UiaLookupId
|
||||
UiaNavigate
|
||||
UiaNodeFromFocus
|
||||
UiaNodeFromHandle
|
||||
UiaNodeFromPoint
|
||||
UiaNodeFromProvider
|
||||
UiaNodeRelease
|
||||
UiaPatternRelease
|
||||
UiaProviderForNonClient
|
||||
UiaProviderFromIAccessible
|
||||
UiaRaiseActiveTextPositionChangedEvent
|
||||
UiaRaiseAsyncContentLoadedEvent
|
||||
UiaRaiseAutomationEvent
|
||||
UiaRaiseAutomationPropertyChangedEvent
|
||||
UiaRaiseChangesEvent
|
||||
UiaRaiseNotificationEvent
|
||||
UiaRaiseStructureChangedEvent
|
||||
UiaRaiseTextEditTextChangedEvent
|
||||
UiaRegisterProviderCallback
|
||||
UiaRemoveEvent
|
||||
UiaReturnRawElementProvider
|
||||
UiaSetFocus
|
||||
UiaTextRangeRelease
|
||||
UpdateErrorLoggingCallback
|
||||
ValuePattern_SetValue
|
||||
VirtualizedItemPattern_Realize
|
||||
WindowPattern_Close
|
||||
WindowPattern_SetWindowVisualState
|
||||
WindowPattern_WaitForInputIdle
|
115
platform/windows/uiautomationcore.arm64.def
Normal file
115
platform/windows/uiautomationcore.arm64.def
Normal file
@@ -0,0 +1,115 @@
|
||||
;
|
||||
; Definition file of UIAutomationCore.DLL
|
||||
; Automatic generated by gendef
|
||||
; written by Kai Tietz 2008
|
||||
;
|
||||
LIBRARY "UIAutomationCore.DLL"
|
||||
EXPORTS
|
||||
DllGetActivationFactory
|
||||
DllCanUnloadNow
|
||||
DllGetClassObject
|
||||
DockPattern_SetDockPosition
|
||||
ExpandCollapsePattern_Collapse
|
||||
ExpandCollapsePattern_Expand
|
||||
GridPattern_GetItem
|
||||
IgnoreLeaksInCurrentlyTrackedMemory
|
||||
InitializeChannelBasedConnectionForProviderProxy
|
||||
InvokePattern_Invoke
|
||||
IsIgnoringLeaks
|
||||
ItemContainerPattern_FindItemByProperty
|
||||
LegacyIAccessiblePattern_DoDefaultAction
|
||||
LegacyIAccessiblePattern_GetIAccessible
|
||||
LegacyIAccessiblePattern_Select
|
||||
LegacyIAccessiblePattern_SetValue
|
||||
MultipleViewPattern_GetViewName
|
||||
MultipleViewPattern_SetCurrentView
|
||||
PostTestCheckForLeaks
|
||||
RangeValuePattern_SetValue
|
||||
ScrollItemPattern_ScrollIntoView
|
||||
ScrollPattern_Scroll
|
||||
ScrollPattern_SetScrollPercent
|
||||
SelectionItemPattern_AddToSelection
|
||||
SelectionItemPattern_RemoveFromSelection
|
||||
SelectionItemPattern_Select
|
||||
StartIgnoringLeaks
|
||||
StopIgnoringLeaks
|
||||
SynchronizedInputPattern_Cancel
|
||||
SynchronizedInputPattern_StartListening
|
||||
TextPattern_GetSelection
|
||||
TextPattern_GetVisibleRanges
|
||||
TextPattern_RangeFromChild
|
||||
TextPattern_RangeFromPoint
|
||||
TextPattern_get_DocumentRange
|
||||
TextPattern_get_SupportedTextSelection
|
||||
TextRange_AddToSelection
|
||||
TextRange_Clone
|
||||
TextRange_Compare
|
||||
TextRange_CompareEndpoints
|
||||
TextRange_ExpandToEnclosingUnit
|
||||
TextRange_FindAttribute
|
||||
TextRange_FindText
|
||||
TextRange_GetAttributeValue
|
||||
TextRange_GetBoundingRectangles
|
||||
TextRange_GetChildren
|
||||
TextRange_GetEnclosingElement
|
||||
TextRange_GetText
|
||||
TextRange_Move
|
||||
TextRange_MoveEndpointByRange
|
||||
TextRange_MoveEndpointByUnit
|
||||
TextRange_RemoveFromSelection
|
||||
TextRange_ScrollIntoView
|
||||
TextRange_Select
|
||||
TogglePattern_Toggle
|
||||
TransformPattern_Move
|
||||
TransformPattern_Resize
|
||||
TransformPattern_Rotate
|
||||
UiaAddEvent
|
||||
UiaClientsAreListening
|
||||
UiaDisconnectAllProviders
|
||||
UiaDisconnectProvider
|
||||
UiaEventAddWindow
|
||||
UiaEventRemoveWindow
|
||||
UiaFind
|
||||
UiaGetErrorDescription
|
||||
UiaGetPatternProvider
|
||||
UiaGetPropertyValue
|
||||
UiaGetReservedMixedAttributeValue
|
||||
UiaGetReservedNotSupportedValue
|
||||
UiaGetRootNode
|
||||
UiaGetRuntimeId
|
||||
UiaGetUpdatedCache
|
||||
UiaHPatternObjectFromVariant
|
||||
UiaHTextRangeFromVariant
|
||||
UiaHUiaNodeFromVariant
|
||||
UiaHasServerSideProvider
|
||||
UiaHostProviderFromHwnd
|
||||
UiaIAccessibleFromProvider
|
||||
UiaLookupId
|
||||
UiaNavigate
|
||||
UiaNodeFromFocus
|
||||
UiaNodeFromHandle
|
||||
UiaNodeFromPoint
|
||||
UiaNodeFromProvider
|
||||
UiaNodeRelease
|
||||
UiaPatternRelease
|
||||
UiaProviderForNonClient
|
||||
UiaProviderFromIAccessible
|
||||
UiaRaiseActiveTextPositionChangedEvent
|
||||
UiaRaiseAsyncContentLoadedEvent
|
||||
UiaRaiseAutomationEvent
|
||||
UiaRaiseAutomationPropertyChangedEvent
|
||||
UiaRaiseChangesEvent
|
||||
UiaRaiseNotificationEvent
|
||||
UiaRaiseStructureChangedEvent
|
||||
UiaRaiseTextEditTextChangedEvent
|
||||
UiaRegisterProviderCallback
|
||||
UiaRemoveEvent
|
||||
UiaReturnRawElementProvider
|
||||
UiaSetFocus
|
||||
UiaTextRangeRelease
|
||||
UpdateErrorLoggingCallback
|
||||
ValuePattern_SetValue
|
||||
VirtualizedItemPattern_Realize
|
||||
WindowPattern_Close
|
||||
WindowPattern_SetWindowVisualState
|
||||
WindowPattern_WaitForInputIdle
|
115
platform/windows/uiautomationcore.x86_32.def
Normal file
115
platform/windows/uiautomationcore.x86_32.def
Normal file
@@ -0,0 +1,115 @@
|
||||
;
|
||||
; Definition file of UIAutomationCore.DLL
|
||||
; Automatic generated by gendef
|
||||
; written by Kai Tietz 2008
|
||||
;
|
||||
LIBRARY "UIAutomationCore.DLL"
|
||||
EXPORTS
|
||||
DllGetActivationFactory@8
|
||||
DllCanUnloadNow
|
||||
DllGetClassObject@12
|
||||
DockPattern_SetDockPosition@8
|
||||
ExpandCollapsePattern_Collapse@4
|
||||
ExpandCollapsePattern_Expand@4
|
||||
GridPattern_GetItem@16
|
||||
IgnoreLeaksInCurrentlyTrackedMemory
|
||||
InitializeChannelBasedConnectionForProviderProxy@12
|
||||
InvokePattern_Invoke@4
|
||||
IsIgnoringLeaks
|
||||
ItemContainerPattern_FindItemByProperty@32
|
||||
LegacyIAccessiblePattern_DoDefaultAction@4
|
||||
LegacyIAccessiblePattern_GetIAccessible@8
|
||||
LegacyIAccessiblePattern_Select@8
|
||||
LegacyIAccessiblePattern_SetValue@8
|
||||
MultipleViewPattern_GetViewName@12
|
||||
MultipleViewPattern_SetCurrentView@8
|
||||
PostTestCheckForLeaks@8
|
||||
RangeValuePattern_SetValue@12
|
||||
ScrollItemPattern_ScrollIntoView@4
|
||||
ScrollPattern_Scroll@12
|
||||
ScrollPattern_SetScrollPercent@20
|
||||
SelectionItemPattern_AddToSelection@4
|
||||
SelectionItemPattern_RemoveFromSelection@4
|
||||
SelectionItemPattern_Select@4
|
||||
StartIgnoringLeaks@4
|
||||
StopIgnoringLeaks
|
||||
SynchronizedInputPattern_Cancel@4
|
||||
SynchronizedInputPattern_StartListening@8
|
||||
TextPattern_GetSelection@8
|
||||
TextPattern_GetVisibleRanges@8
|
||||
TextPattern_RangeFromChild@12
|
||||
TextPattern_RangeFromPoint@24
|
||||
TextPattern_get_DocumentRange@8
|
||||
TextPattern_get_SupportedTextSelection@8
|
||||
TextRange_AddToSelection@4
|
||||
TextRange_Clone@8
|
||||
TextRange_Compare@12
|
||||
TextRange_CompareEndpoints@20
|
||||
TextRange_ExpandToEnclosingUnit@8
|
||||
TextRange_FindAttribute@32
|
||||
TextRange_FindText@20
|
||||
TextRange_GetAttributeValue@12
|
||||
TextRange_GetBoundingRectangles@8
|
||||
TextRange_GetChildren@8
|
||||
TextRange_GetEnclosingElement@8
|
||||
TextRange_GetText@12
|
||||
TextRange_Move@16
|
||||
TextRange_MoveEndpointByRange@16
|
||||
TextRange_MoveEndpointByUnit@20
|
||||
TextRange_RemoveFromSelection@4
|
||||
TextRange_ScrollIntoView@8
|
||||
TextRange_Select@4
|
||||
TogglePattern_Toggle@4
|
||||
TransformPattern_Move@20
|
||||
TransformPattern_Resize@20
|
||||
TransformPattern_Rotate@12
|
||||
UiaAddEvent@32
|
||||
UiaClientsAreListening
|
||||
UiaDisconnectAllProviders
|
||||
UiaDisconnectProvider@4
|
||||
UiaEventAddWindow@8
|
||||
UiaEventRemoveWindow@8
|
||||
UiaFind@24
|
||||
UiaGetErrorDescription@4
|
||||
UiaGetPatternProvider@12
|
||||
UiaGetPropertyValue@12
|
||||
UiaGetReservedMixedAttributeValue@4
|
||||
UiaGetReservedNotSupportedValue@4
|
||||
UiaGetRootNode@4
|
||||
UiaGetRuntimeId@8
|
||||
UiaGetUpdatedCache@24
|
||||
UiaHPatternObjectFromVariant@8
|
||||
UiaHTextRangeFromVariant@8
|
||||
UiaHUiaNodeFromVariant@8
|
||||
UiaHasServerSideProvider@4
|
||||
UiaHostProviderFromHwnd@8
|
||||
UiaIAccessibleFromProvider@16
|
||||
UiaLookupId@8
|
||||
UiaNavigate@24
|
||||
UiaNodeFromFocus@12
|
||||
UiaNodeFromHandle@8
|
||||
UiaNodeFromPoint@28
|
||||
UiaNodeFromProvider@8
|
||||
UiaNodeRelease@4
|
||||
UiaPatternRelease@4
|
||||
UiaProviderForNonClient@16
|
||||
UiaProviderFromIAccessible@16
|
||||
UiaRaiseActiveTextPositionChangedEvent@8
|
||||
UiaRaiseAsyncContentLoadedEvent@16
|
||||
UiaRaiseAutomationEvent@8
|
||||
UiaRaiseAutomationPropertyChangedEvent@40
|
||||
UiaRaiseChangesEvent@12
|
||||
UiaRaiseNotificationEvent@20
|
||||
UiaRaiseStructureChangedEvent@16
|
||||
UiaRaiseTextEditTextChangedEvent@12
|
||||
UiaRegisterProviderCallback@4
|
||||
UiaRemoveEvent@4
|
||||
UiaReturnRawElementProvider@16
|
||||
UiaSetFocus@4
|
||||
UiaTextRangeRelease@4
|
||||
UpdateErrorLoggingCallback@4
|
||||
ValuePattern_SetValue@8
|
||||
VirtualizedItemPattern_Realize@4
|
||||
WindowPattern_Close@4
|
||||
WindowPattern_SetWindowVisualState@8
|
||||
WindowPattern_WaitForInputIdle@12
|
115
platform/windows/uiautomationcore.x86_64.def
Normal file
115
platform/windows/uiautomationcore.x86_64.def
Normal file
@@ -0,0 +1,115 @@
|
||||
;
|
||||
; Definition file of UIAutomationCore.DLL
|
||||
; Automatic generated by gendef
|
||||
; written by Kai Tietz 2008
|
||||
;
|
||||
LIBRARY "UIAutomationCore.DLL"
|
||||
EXPORTS
|
||||
DllGetActivationFactory
|
||||
DllCanUnloadNow
|
||||
DllGetClassObject
|
||||
DockPattern_SetDockPosition
|
||||
ExpandCollapsePattern_Collapse
|
||||
ExpandCollapsePattern_Expand
|
||||
GridPattern_GetItem
|
||||
IgnoreLeaksInCurrentlyTrackedMemory
|
||||
InitializeChannelBasedConnectionForProviderProxy
|
||||
InvokePattern_Invoke
|
||||
IsIgnoringLeaks
|
||||
ItemContainerPattern_FindItemByProperty
|
||||
LegacyIAccessiblePattern_DoDefaultAction
|
||||
LegacyIAccessiblePattern_GetIAccessible
|
||||
LegacyIAccessiblePattern_Select
|
||||
LegacyIAccessiblePattern_SetValue
|
||||
MultipleViewPattern_GetViewName
|
||||
MultipleViewPattern_SetCurrentView
|
||||
PostTestCheckForLeaks
|
||||
RangeValuePattern_SetValue
|
||||
ScrollItemPattern_ScrollIntoView
|
||||
ScrollPattern_Scroll
|
||||
ScrollPattern_SetScrollPercent
|
||||
SelectionItemPattern_AddToSelection
|
||||
SelectionItemPattern_RemoveFromSelection
|
||||
SelectionItemPattern_Select
|
||||
StartIgnoringLeaks
|
||||
StopIgnoringLeaks
|
||||
SynchronizedInputPattern_Cancel
|
||||
SynchronizedInputPattern_StartListening
|
||||
TextPattern_GetSelection
|
||||
TextPattern_GetVisibleRanges
|
||||
TextPattern_RangeFromChild
|
||||
TextPattern_RangeFromPoint
|
||||
TextPattern_get_DocumentRange
|
||||
TextPattern_get_SupportedTextSelection
|
||||
TextRange_AddToSelection
|
||||
TextRange_Clone
|
||||
TextRange_Compare
|
||||
TextRange_CompareEndpoints
|
||||
TextRange_ExpandToEnclosingUnit
|
||||
TextRange_FindAttribute
|
||||
TextRange_FindText
|
||||
TextRange_GetAttributeValue
|
||||
TextRange_GetBoundingRectangles
|
||||
TextRange_GetChildren
|
||||
TextRange_GetEnclosingElement
|
||||
TextRange_GetText
|
||||
TextRange_Move
|
||||
TextRange_MoveEndpointByRange
|
||||
TextRange_MoveEndpointByUnit
|
||||
TextRange_RemoveFromSelection
|
||||
TextRange_ScrollIntoView
|
||||
TextRange_Select
|
||||
TogglePattern_Toggle
|
||||
TransformPattern_Move
|
||||
TransformPattern_Resize
|
||||
TransformPattern_Rotate
|
||||
UiaAddEvent
|
||||
UiaClientsAreListening
|
||||
UiaDisconnectAllProviders
|
||||
UiaDisconnectProvider
|
||||
UiaEventAddWindow
|
||||
UiaEventRemoveWindow
|
||||
UiaFind
|
||||
UiaGetErrorDescription
|
||||
UiaGetPatternProvider
|
||||
UiaGetPropertyValue
|
||||
UiaGetReservedMixedAttributeValue
|
||||
UiaGetReservedNotSupportedValue
|
||||
UiaGetRootNode
|
||||
UiaGetRuntimeId
|
||||
UiaGetUpdatedCache
|
||||
UiaHPatternObjectFromVariant
|
||||
UiaHTextRangeFromVariant
|
||||
UiaHUiaNodeFromVariant
|
||||
UiaHasServerSideProvider
|
||||
UiaHostProviderFromHwnd
|
||||
UiaIAccessibleFromProvider
|
||||
UiaLookupId
|
||||
UiaNavigate
|
||||
UiaNodeFromFocus
|
||||
UiaNodeFromHandle
|
||||
UiaNodeFromPoint
|
||||
UiaNodeFromProvider
|
||||
UiaNodeRelease
|
||||
UiaPatternRelease
|
||||
UiaProviderForNonClient
|
||||
UiaProviderFromIAccessible
|
||||
UiaRaiseActiveTextPositionChangedEvent
|
||||
UiaRaiseAsyncContentLoadedEvent
|
||||
UiaRaiseAutomationEvent
|
||||
UiaRaiseAutomationPropertyChangedEvent
|
||||
UiaRaiseChangesEvent
|
||||
UiaRaiseNotificationEvent
|
||||
UiaRaiseStructureChangedEvent
|
||||
UiaRaiseTextEditTextChangedEvent
|
||||
UiaRegisterProviderCallback
|
||||
UiaRemoveEvent
|
||||
UiaReturnRawElementProvider
|
||||
UiaSetFocus
|
||||
UiaTextRangeRelease
|
||||
UpdateErrorLoggingCallback
|
||||
ValuePattern_SetValue
|
||||
VirtualizedItemPattern_Realize
|
||||
WindowPattern_Close
|
||||
WindowPattern_SetWindowVisualState
|
||||
WindowPattern_WaitForInputIdle
|
192
platform/windows/wgl_detect_version.cpp
Normal file
192
platform/windows/wgl_detect_version.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
/**************************************************************************/
|
||||
/* wgl_detect_version.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED)
|
||||
|
||||
#include "wgl_detect_version.h"
|
||||
#include "os_windows.h"
|
||||
|
||||
#include "core/string/print_string.h"
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/variant/dictionary.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <dwmapi.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
|
||||
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
|
||||
#define WGL_CONTEXT_FLAGS_ARB 0x2094
|
||||
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
|
||||
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
|
||||
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
|
||||
#define WGL_VENDOR 0x1F00
|
||||
#define WGL_RENDERER 0x1F01
|
||||
#define WGL_VERSION 0x1F02
|
||||
|
||||
typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXT)(HDC);
|
||||
typedef BOOL(APIENTRY *PFNWGLDELETECONTEXT)(HGLRC);
|
||||
typedef BOOL(APIENTRY *PFNWGLMAKECURRENT)(HDC, HGLRC);
|
||||
typedef HGLRC(APIENTRY *PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC, HGLRC, const int *);
|
||||
typedef void *(APIENTRY *PFNWGLGETPROCADDRESS)(LPCSTR);
|
||||
typedef const char *(APIENTRY *PFNWGLGETSTRINGPROC)(unsigned int);
|
||||
|
||||
Dictionary detect_wgl() {
|
||||
Dictionary gl_info;
|
||||
gl_info["version"] = 0;
|
||||
gl_info["vendor"] = String();
|
||||
gl_info["name"] = String();
|
||||
|
||||
PFNWGLCREATECONTEXT gd_wglCreateContext;
|
||||
PFNWGLMAKECURRENT gd_wglMakeCurrent;
|
||||
PFNWGLDELETECONTEXT gd_wglDeleteContext;
|
||||
PFNWGLGETPROCADDRESS gd_wglGetProcAddress;
|
||||
|
||||
HMODULE module = LoadLibraryW(L"opengl32.dll");
|
||||
if (!module) {
|
||||
return gl_info;
|
||||
}
|
||||
gd_wglCreateContext = (PFNWGLCREATECONTEXT)(void *)GetProcAddress(module, "wglCreateContext");
|
||||
gd_wglMakeCurrent = (PFNWGLMAKECURRENT)(void *)GetProcAddress(module, "wglMakeCurrent");
|
||||
gd_wglDeleteContext = (PFNWGLDELETECONTEXT)(void *)GetProcAddress(module, "wglDeleteContext");
|
||||
gd_wglGetProcAddress = (PFNWGLGETPROCADDRESS)(void *)GetProcAddress(module, "wglGetProcAddress");
|
||||
if (!gd_wglCreateContext || !gd_wglMakeCurrent || !gd_wglDeleteContext || !gd_wglGetProcAddress) {
|
||||
return gl_info;
|
||||
}
|
||||
|
||||
LPCWSTR class_name = L"EngineWGLDetect";
|
||||
HINSTANCE hInstance = static_cast<OS_Windows *>(OS::get_singleton())->get_hinstance();
|
||||
WNDCLASSW wc = {};
|
||||
|
||||
wc.lpfnWndProc = DefWindowProcW;
|
||||
wc.hInstance = hInstance;
|
||||
wc.lpszClassName = class_name;
|
||||
|
||||
RegisterClassW(&wc);
|
||||
|
||||
HWND hWnd = CreateWindowExW(WS_EX_APPWINDOW, class_name, L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, hInstance, nullptr);
|
||||
if (hWnd) {
|
||||
HDC hDC = GetDC(hWnd);
|
||||
if (hDC) {
|
||||
static PIXELFORMATDESCRIPTOR pfd = {
|
||||
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
|
||||
1,
|
||||
PFD_DRAW_TO_WINDOW | // Format Must Support Window
|
||||
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
|
||||
PFD_DOUBLEBUFFER,
|
||||
(BYTE)PFD_TYPE_RGBA,
|
||||
(BYTE)(OS::get_singleton()->is_layered_allowed() ? 32 : 24),
|
||||
(BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, // Color Bits Ignored
|
||||
(BYTE)(OS::get_singleton()->is_layered_allowed() ? 8 : 0), // Alpha Buffer
|
||||
(BYTE)0, // Shift Bit Ignored
|
||||
(BYTE)0, // No Accumulation Buffer
|
||||
(BYTE)0, (BYTE)0, (BYTE)0, (BYTE)0, // Accumulation Bits Ignored
|
||||
(BYTE)24, // 24Bit Z-Buffer (Depth Buffer)
|
||||
(BYTE)0, // No Stencil Buffer
|
||||
(BYTE)0, // No Auxiliary Buffer
|
||||
(BYTE)PFD_MAIN_PLANE, // Main Drawing Layer
|
||||
(BYTE)0, // Reserved
|
||||
0, 0, 0 // Layer Masks Ignored
|
||||
};
|
||||
|
||||
int pixel_format = ChoosePixelFormat(hDC, &pfd);
|
||||
SetPixelFormat(hDC, pixel_format, &pfd);
|
||||
|
||||
HGLRC hRC = gd_wglCreateContext(hDC);
|
||||
if (hRC) {
|
||||
if (gd_wglMakeCurrent(hDC, hRC)) {
|
||||
int attribs[] = {
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
||||
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
|
||||
0
|
||||
};
|
||||
|
||||
PFNWGLCREATECONTEXTATTRIBSARBPROC gd_wglCreateContextAttribsARB = nullptr;
|
||||
gd_wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)gd_wglGetProcAddress("wglCreateContextAttribsARB");
|
||||
if (gd_wglCreateContextAttribsARB) {
|
||||
HGLRC new_hRC = gd_wglCreateContextAttribsARB(hDC, nullptr, attribs);
|
||||
if (new_hRC) {
|
||||
if (gd_wglMakeCurrent(hDC, new_hRC)) {
|
||||
PFNWGLGETSTRINGPROC gd_wglGetString = (PFNWGLGETSTRINGPROC)(void *)GetProcAddress(module, "glGetString");
|
||||
if (gd_wglGetString) {
|
||||
const char *prefixes[] = {
|
||||
"OpenGL ES-CM ",
|
||||
"OpenGL ES-CL ",
|
||||
"OpenGL ES ",
|
||||
"OpenGL SC ",
|
||||
nullptr
|
||||
};
|
||||
const char *version = (const char *)gd_wglGetString(WGL_VERSION);
|
||||
if (version) {
|
||||
const String device_vendor = String::utf8((const char *)gd_wglGetString(WGL_VENDOR)).strip_edges().trim_suffix(" Corporation");
|
||||
const String device_name = String::utf8((const char *)gd_wglGetString(WGL_RENDERER)).strip_edges().trim_suffix("/PCIe/SSE2");
|
||||
for (int i = 0; prefixes[i]; i++) {
|
||||
size_t length = strlen(prefixes[i]);
|
||||
if (strncmp(version, prefixes[i], length) == 0) {
|
||||
version += length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int major = 0;
|
||||
int minor = 0;
|
||||
#ifdef _MSC_VER
|
||||
sscanf_s(version, "%d.%d", &major, &minor);
|
||||
#else
|
||||
sscanf(version, "%d.%d", &major, &minor);
|
||||
#endif
|
||||
print_verbose(vformat("Native OpenGL API detected: %d.%d: %s - %s", major, minor, device_vendor, device_name));
|
||||
gl_info["vendor"] = device_vendor;
|
||||
gl_info["name"] = device_name;
|
||||
gl_info["version"] = major * 10000 + minor;
|
||||
}
|
||||
}
|
||||
}
|
||||
gd_wglMakeCurrent(nullptr, nullptr);
|
||||
gd_wglDeleteContext(new_hRC);
|
||||
}
|
||||
}
|
||||
}
|
||||
gd_wglMakeCurrent(nullptr, nullptr);
|
||||
gd_wglDeleteContext(hRC);
|
||||
}
|
||||
ReleaseDC(hWnd, hDC);
|
||||
}
|
||||
DestroyWindow(hWnd);
|
||||
}
|
||||
UnregisterClassW(class_name, hInstance);
|
||||
|
||||
return gl_info;
|
||||
}
|
||||
|
||||
#endif // WINDOWS_ENABLED && GLES3_ENABLED
|
39
platform/windows/wgl_detect_version.h
Normal file
39
platform/windows/wgl_detect_version.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**************************************************************************/
|
||||
/* wgl_detect_version.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED)
|
||||
|
||||
class Dictionary;
|
||||
|
||||
Dictionary detect_wgl();
|
||||
|
||||
#endif // WINDOWS_ENABLED && GLES3_ENABLED
|
140
platform/windows/windows_terminal_logger.cpp
Normal file
140
platform/windows/windows_terminal_logger.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
/**************************************************************************/
|
||||
/* windows_terminal_logger.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 "windows_terminal_logger.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_err) {
|
||||
if (!should_log(p_err)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int static_buffer_size = 1024;
|
||||
char static_buf[static_buffer_size];
|
||||
char *buf = static_buf;
|
||||
va_list list_copy;
|
||||
va_copy(list_copy, p_list);
|
||||
int len = vsnprintf(buf, static_buffer_size, p_format, p_list);
|
||||
if (len >= static_buffer_size) {
|
||||
buf = (char *)memalloc(len + 1);
|
||||
len = vsnprintf(buf, len + 1, p_format, list_copy);
|
||||
}
|
||||
va_end(list_copy);
|
||||
|
||||
String str_buf = String::utf8(buf, len).replace("\r\n", "\n").replace("\n", "\r\n");
|
||||
if (len >= static_buffer_size) {
|
||||
memfree(buf);
|
||||
}
|
||||
CharString cstr_buf = str_buf.utf8();
|
||||
if (cstr_buf.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD written = 0;
|
||||
HANDLE h = p_err ? GetStdHandle(STD_ERROR_HANDLE) : GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
WriteFile(h, cstr_buf.ptr(), cstr_buf.length(), &written, nullptr);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
FlushFileBuffers(h);
|
||||
#endif
|
||||
}
|
||||
|
||||
void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces) {
|
||||
if (!should_log(true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (OS::get_singleton()->get_stdout_type() != OS::STD_HANDLE_CONSOLE || !hCon || hCon == INVALID_HANDLE_VALUE) {
|
||||
StdLogger::log_error(p_function, p_file, p_line, p_code, p_rationale, p_editor_notify, p_type, p_script_backtraces);
|
||||
} else {
|
||||
CONSOLE_SCREEN_BUFFER_INFO sbi; //original
|
||||
GetConsoleScreenBufferInfo(hCon, &sbi);
|
||||
|
||||
WORD current_bg = sbi.wAttributes & (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY);
|
||||
|
||||
uint32_t basecol = 0;
|
||||
switch (p_type) {
|
||||
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:
|
||||
default:
|
||||
basecol = FOREGROUND_RED;
|
||||
break;
|
||||
}
|
||||
|
||||
basecol |= current_bg;
|
||||
|
||||
SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY);
|
||||
logf_error("%s:", error_type_string(p_type));
|
||||
|
||||
SetConsoleTextAttribute(hCon, basecol);
|
||||
if (p_rationale && p_rationale[0]) {
|
||||
logf_error(" %s\n", p_rationale);
|
||||
} else {
|
||||
logf_error(" %s\n", p_code);
|
||||
}
|
||||
|
||||
// `FOREGROUND_INTENSITY` alone results in gray text.
|
||||
SetConsoleTextAttribute(hCon, FOREGROUND_INTENSITY);
|
||||
if (p_rationale && p_rationale[0]) {
|
||||
logf_error("%sat: (%s:%i)\n", error_type_indent(p_type), p_file, p_line);
|
||||
} else {
|
||||
logf_error("%sat: %s (%s:%i)\n", error_type_indent(p_type), p_function, p_file, p_line);
|
||||
}
|
||||
|
||||
for (const Ref<ScriptBacktrace> &backtrace : p_script_backtraces) {
|
||||
if (!backtrace->is_empty()) {
|
||||
logf_error("%s\n", backtrace->format(strlen(error_type_indent(p_type))).utf8().get_data());
|
||||
}
|
||||
}
|
||||
|
||||
SetConsoleTextAttribute(hCon, sbi.wAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
WindowsTerminalLogger::~WindowsTerminalLogger() {}
|
||||
|
||||
#endif // WINDOWS_ENABLED
|
44
platform/windows/windows_terminal_logger.h
Normal file
44
platform/windows/windows_terminal_logger.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/**************************************************************************/
|
||||
/* windows_terminal_logger.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
|
||||
#include "core/io/logger.h"
|
||||
|
||||
class WindowsTerminalLogger : public StdLogger {
|
||||
public:
|
||||
virtual void logv(const char *p_format, va_list p_list, bool p_err) override;
|
||||
virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify = false, ErrorType p_type = ERR_ERROR, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces = {}) override;
|
||||
virtual ~WindowsTerminalLogger();
|
||||
};
|
||||
|
||||
#endif // WINDOWS_ENABLED
|
281
platform/windows/windows_utils.cpp
Normal file
281
platform/windows/windows_utils.cpp
Normal file
@@ -0,0 +1,281 @@
|
||||
/**************************************************************************/
|
||||
/* windows_utils.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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 "windows_utils.h"
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
|
||||
#include "core/error/error_macros.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#undef FAILED // Overrides Error::FAILED
|
||||
|
||||
// dbghelp is linked only in DEBUG_ENABLED builds.
|
||||
#ifdef DEBUG_ENABLED
|
||||
#include <dbghelp.h>
|
||||
#endif
|
||||
#include <winnt.h>
|
||||
|
||||
HashMap<String, Vector<String>> WindowsUtils::temp_pdbs;
|
||||
|
||||
Error WindowsUtils::copy_and_rename_pdb(const String &p_dll_path) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
// 1000 ought to be enough for anybody, in case the debugger does not unblock previous PDBs.
|
||||
// Usually no more than 2 will be used.
|
||||
const int max_pdb_names = 1000;
|
||||
|
||||
struct PDBResourceInfo {
|
||||
uint32_t address = 0;
|
||||
String path;
|
||||
} pdb_info;
|
||||
|
||||
// Open and read the PDB information if available.
|
||||
{
|
||||
ULONG dbg_info_size = 0;
|
||||
DWORD dbg_info_position = 0;
|
||||
|
||||
{
|
||||
// The custom LoadLibraryExW is used instead of open_dynamic_library
|
||||
// to avoid loading the original PDB into the debugger.
|
||||
HMODULE library_ptr = LoadLibraryExW((LPCWSTR)(p_dll_path.utf16().get_data()), nullptr, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE);
|
||||
|
||||
ERR_FAIL_NULL_V_MSG(library_ptr, ERR_FILE_CANT_OPEN, vformat("Failed to load library '%s'.", p_dll_path));
|
||||
|
||||
IMAGE_DEBUG_DIRECTORY *dbg_dir = (IMAGE_DEBUG_DIRECTORY *)ImageDirectoryEntryToDataEx(library_ptr, FALSE, IMAGE_DIRECTORY_ENTRY_DEBUG, &dbg_info_size, nullptr);
|
||||
|
||||
bool has_debug = dbg_dir && dbg_dir->Type == IMAGE_DEBUG_TYPE_CODEVIEW;
|
||||
if (has_debug) {
|
||||
dbg_info_position = dbg_dir->PointerToRawData;
|
||||
dbg_info_size = dbg_dir->SizeOfData;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_V_MSG(!FreeLibrary((HMODULE)library_ptr), FAILED, vformat("Failed to free library '%s'.", p_dll_path));
|
||||
|
||||
if (!has_debug) {
|
||||
// Skip with no debugging symbols.
|
||||
return ERR_SKIP;
|
||||
}
|
||||
}
|
||||
|
||||
struct CV_HEADER {
|
||||
DWORD Signature;
|
||||
DWORD Offset;
|
||||
};
|
||||
|
||||
const DWORD nb10_magic = 0x3031424e; // "01BN" (little-endian)
|
||||
struct CV_INFO_PDB20 {
|
||||
CV_HEADER CvHeader; // CvHeader.Signature = "NB10"
|
||||
DWORD Signature;
|
||||
DWORD Age;
|
||||
BYTE PdbFileName[1];
|
||||
};
|
||||
|
||||
const DWORD rsds_magic = 0x53445352; // "SDSR" (little-endian)
|
||||
struct CV_INFO_PDB70 {
|
||||
DWORD Signature; // "RSDS"
|
||||
BYTE Guid[16];
|
||||
DWORD Age;
|
||||
BYTE PdbFileName[1];
|
||||
};
|
||||
|
||||
Vector<uint8_t> dll_data;
|
||||
|
||||
{
|
||||
Error err = OK;
|
||||
Ref<FileAccess> file = FileAccess::open(p_dll_path, FileAccess::READ, &err);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to read library '%s'.", p_dll_path));
|
||||
|
||||
file->seek(dbg_info_position);
|
||||
dll_data = file->get_buffer(dbg_info_size);
|
||||
ERR_FAIL_COND_V_MSG(file->get_error() != OK, file->get_error(), vformat("Failed to read data from library '%s'.", p_dll_path));
|
||||
}
|
||||
|
||||
const char *raw_pdb_path = nullptr;
|
||||
int raw_pdb_offset = 0;
|
||||
DWORD *pdb_info_signature = (DWORD *)dll_data.ptr();
|
||||
|
||||
if (*pdb_info_signature == rsds_magic) {
|
||||
raw_pdb_path = (const char *)(((CV_INFO_PDB70 *)pdb_info_signature)->PdbFileName);
|
||||
raw_pdb_offset = offsetof(CV_INFO_PDB70, PdbFileName);
|
||||
} else if (*pdb_info_signature == nb10_magic) {
|
||||
// Not even sure if this format still exists anywhere...
|
||||
raw_pdb_path = (const char *)(((CV_INFO_PDB20 *)pdb_info_signature)->PdbFileName);
|
||||
raw_pdb_offset = offsetof(CV_INFO_PDB20, PdbFileName);
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(FAILED, vformat("Unknown PDB format in '%s'.", p_dll_path));
|
||||
}
|
||||
|
||||
String utf_path;
|
||||
Error err = utf_path.append_utf8(raw_pdb_path);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to read PDB path from '%s'.", p_dll_path));
|
||||
|
||||
pdb_info.path = utf_path;
|
||||
pdb_info.address = dbg_info_position + raw_pdb_offset;
|
||||
}
|
||||
|
||||
String dll_base_dir = p_dll_path.get_base_dir();
|
||||
String copy_pdb_path = pdb_info.path;
|
||||
|
||||
// Attempting to find the PDB by absolute and relative paths.
|
||||
if (copy_pdb_path.is_relative_path()) {
|
||||
copy_pdb_path = dll_base_dir.path_join(copy_pdb_path);
|
||||
if (!FileAccess::exists(copy_pdb_path)) {
|
||||
copy_pdb_path = dll_base_dir.path_join(copy_pdb_path.get_file());
|
||||
}
|
||||
} else if (!FileAccess::exists(copy_pdb_path)) {
|
||||
copy_pdb_path = dll_base_dir.path_join(copy_pdb_path.get_file());
|
||||
}
|
||||
if (!FileAccess::exists(copy_pdb_path)) {
|
||||
// The PDB file may be distributed separately on purpose, so we don't consider this an error.
|
||||
WARN_VERBOSE(vformat("PDB file '%s' for library '%s' was not found, skipping copy/rename.", copy_pdb_path, p_dll_path));
|
||||
return ERR_SKIP;
|
||||
}
|
||||
|
||||
String new_pdb_base_name = p_dll_path.get_file().get_basename() + "_";
|
||||
|
||||
// Checking the available space for the updated string
|
||||
// and trying to shorten it if there is not much space.
|
||||
{
|
||||
// e.g. 999.pdb
|
||||
const uint8_t suffix_size = String::num_characters((int64_t)max_pdb_names - 1) + 4;
|
||||
// e.g. ~lib_ + 1 for the \0
|
||||
const uint8_t min_base_size = 5 + 1;
|
||||
int original_path_size = pdb_info.path.utf8().length();
|
||||
CharString utf8_name = new_pdb_base_name.utf8();
|
||||
int new_expected_buffer_size = utf8_name.length() + suffix_size;
|
||||
|
||||
// Since we have limited space inside the DLL to patch the path to the PDB,
|
||||
// it is necessary to limit the size based on the number of bytes occupied by the string.
|
||||
if (new_expected_buffer_size > original_path_size) {
|
||||
ERR_FAIL_COND_V_MSG(original_path_size < min_base_size + suffix_size, FAILED, vformat("The original PDB path size in bytes is too small: '%s'. Expected size: %d or more bytes, but available %d.", pdb_info.path, min_base_size + suffix_size, original_path_size));
|
||||
|
||||
new_pdb_base_name.clear();
|
||||
new_pdb_base_name.append_utf8(utf8_name.get_data(), original_path_size - suffix_size);
|
||||
new_pdb_base_name[new_pdb_base_name.length() - 1] = '_'; // Restore the last '_'
|
||||
WARN_PRINT(vformat("The original path size of '%s' in bytes was too small to fit the new name, so it was shortened to '%s%d.pdb'.", pdb_info.path, new_pdb_base_name, max_pdb_names - 1));
|
||||
}
|
||||
}
|
||||
|
||||
// Delete old PDB files.
|
||||
for (const String &file : DirAccess::get_files_at(dll_base_dir)) {
|
||||
if (file.begins_with(new_pdb_base_name) && file.ends_with(".pdb")) {
|
||||
String path = dll_base_dir.path_join(file);
|
||||
|
||||
// Just try to delete without showing any errors.
|
||||
Error err = DirAccess::remove_absolute(path);
|
||||
if (err == OK && temp_pdbs[p_dll_path].has(path)) {
|
||||
temp_pdbs[p_dll_path].erase(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to copy PDB with new name and patch DLL.
|
||||
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
for (int i = 0; i < max_pdb_names; i++) {
|
||||
String new_pdb_name = vformat("%s%d.pdb", new_pdb_base_name, i);
|
||||
String new_pdb_path = dll_base_dir.path_join(new_pdb_name);
|
||||
Error err = OK;
|
||||
|
||||
Ref<FileAccess> test_pdb_is_locked = FileAccess::open(new_pdb_path, FileAccess::READ_WRITE, &err);
|
||||
if (err == ERR_FILE_CANT_OPEN) {
|
||||
// If the file is blocked, continue searching.
|
||||
continue;
|
||||
} else if (err != OK && err != ERR_FILE_NOT_FOUND) {
|
||||
ERR_FAIL_V_MSG(err, vformat("Failed to open '%s' to check if it is locked.", new_pdb_path));
|
||||
}
|
||||
|
||||
err = d->copy(copy_pdb_path, new_pdb_path);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to copy PDB from '%s' to '%s'.", copy_pdb_path, new_pdb_path));
|
||||
temp_pdbs[p_dll_path].append(new_pdb_path);
|
||||
|
||||
Ref<FileAccess> file = FileAccess::open(p_dll_path, FileAccess::READ_WRITE, &err);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to open '%s' to patch the PDB path.", p_dll_path));
|
||||
|
||||
int original_path_size = pdb_info.path.utf8().length();
|
||||
// Double-check file bounds.
|
||||
ERR_FAIL_UNSIGNED_INDEX_V_MSG(pdb_info.address + original_path_size, file->get_length(), FAILED, vformat("Failed to write a new PDB path. Probably '%s' has been changed.", p_dll_path));
|
||||
|
||||
Vector<uint8_t> u8 = new_pdb_name.to_utf8_buffer();
|
||||
file->seek(pdb_info.address);
|
||||
file->store_buffer(u8);
|
||||
|
||||
// Terminate string and fill the remaining part of the original string with the '\0'.
|
||||
Vector<uint8_t> padding_buffer;
|
||||
padding_buffer.resize_initialized((int64_t)original_path_size - u8.size());
|
||||
file->store_buffer(padding_buffer);
|
||||
ERR_FAIL_COND_V_MSG(err != OK, err, vformat("Failed to write a new PDB path to '%s'.", p_dll_path));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
ERR_FAIL_V_MSG(FAILED, vformat("Failed to find an unblocked PDB name for '%s' among %d files.", p_dll_path, max_pdb_names));
|
||||
#else
|
||||
WARN_PRINT_ONCE("Renaming PDB files is only available in debug builds. If your libraries use PDB files, then the original ones will be used.");
|
||||
return ERR_SKIP;
|
||||
#endif
|
||||
}
|
||||
|
||||
void WindowsUtils::remove_temp_pdbs(const String &p_dll_path) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (temp_pdbs.has(p_dll_path)) {
|
||||
Vector<String> removed;
|
||||
int failed = 0;
|
||||
const int failed_limit = 10;
|
||||
for (const String &pdb : temp_pdbs[p_dll_path]) {
|
||||
if (FileAccess::exists(pdb)) {
|
||||
Error err = DirAccess::remove_absolute(pdb);
|
||||
if (err == OK) {
|
||||
removed.append(pdb);
|
||||
} else {
|
||||
failed++;
|
||||
if (failed <= failed_limit) {
|
||||
print_verbose("Failed to remove temp PDB: " + pdb);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
removed.append(pdb);
|
||||
}
|
||||
}
|
||||
|
||||
if (failed > failed_limit) {
|
||||
print_verbose(vformat("And %d more PDB files could not be removed....", failed - failed_limit));
|
||||
}
|
||||
|
||||
for (const String &pdb : removed) {
|
||||
temp_pdbs[p_dll_path].erase(pdb);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // WINDOWS_ENABLED
|
46
platform/windows/windows_utils.h
Normal file
46
platform/windows/windows_utils.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/**************************************************************************/
|
||||
/* windows_utils.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef WINDOWS_ENABLED
|
||||
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
|
||||
class WindowsUtils {
|
||||
static HashMap<String, Vector<String>> temp_pdbs;
|
||||
|
||||
public:
|
||||
static Error copy_and_rename_pdb(const String &p_dll_path);
|
||||
static void remove_temp_pdbs(const String &p_dll_path);
|
||||
};
|
||||
|
||||
#endif // WINDOWS_ENABLED
|
Reference in New Issue
Block a user