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

This commit is contained in:
2025-09-16 20:46:46 -04:00
commit 9d30169a8d
13378 changed files with 7050105 additions and 0 deletions

View 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
View 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

View 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);
}

View 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;
}
}

View 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();
};

View 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() {
}

View 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
View 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)

File diff suppressed because it is too large Load Diff

View 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();
};

View 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>

View 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;
}

View 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

View 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);
}

View 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();

File diff suppressed because it is too large Load Diff

View 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();
};

View 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

View 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

View 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 &section_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);
}

View 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);
};

View 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

View 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

View 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

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View 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&lt;*&gt;">
<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&lt;*&gt;">
<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 &amp;&amp; _p->variant_map.head_element ? _p->variant_map.num_elements : 0</Item>
<LinkedListItems>
<Size>_p &amp;&amp; _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&lt;*&gt;">
<Expand>
<Item Name="[size]">count</Item>
<ArrayItems>
<Size>count</Size>
<ValuePointer>data</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="List&lt;*&gt;">
<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&lt;StringName&gt;" 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 &amp;&amp; _data->cname">{_data->cname,s8b}</DisplayString>
<DisplayString Condition="_data &amp;&amp; !_data->cname">{_data->name,s32b}</DisplayString>
<DisplayString Condition="!_data">[empty]</DisplayString>
<StringView Condition="_data &amp;&amp; _data->cname">_data->cname,s8b</StringView>
<StringView Condition="_data &amp;&amp; !_data->cname">_data->name,s32b</StringView>
</Type>
<Type Name="HashSet&lt;*,*,*&gt;">
<Expand>
<Item Name="[size]">num_elements</Item>
<ArrayItems>
<Size>num_elements</Size>
<ValuePointer>($T1 *) keys._cowdata._ptr</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="HashMapElement&lt;*,*&gt;">
<DisplayString>{{Key = {($T1 *) &amp;data.key} Value = {($T2 *) &amp;data.value}}}</DisplayString>
<Expand>
<Item Name="[key]">($T1 *) &amp;data.key</Item>
<Item Name="[value]">($T2 *) &amp;data.value</Item>
</Expand>
</Type>
<!-- elements displayed by index -->
<Type Name="HashMap&lt;*,*,*,*,*&gt;" 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&lt;*,*,*,*,*&gt;" 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&lt;*,*&gt;" IncludeView="MapHelper">
<DisplayString>{value}</DisplayString>
</Type>
<Type Name="HashMapElement&lt;*,*&gt;" IncludeView="MapHelper">
<DisplayString>{data.value}</DisplayString>
<Expand>
<Item Name="[key]">($T1 *) &amp;data.key</Item>
<Item Name="[value]">($T2 *) &amp;data.value</Item>
</Expand>
</Type>
<Type Name="VMap&lt;*,*&gt;">
<Expand>
<Item Condition="_cowdata._ptr" Name="[size]">*(reinterpret_cast&lt;long long*&gt;(_cowdata._ptr) - 1)</Item>
<ArrayItems Condition="_cowdata._ptr">
<Size>*(reinterpret_cast&lt;long long*&gt;(_cowdata._ptr) - 1)</Size>
<ValuePointer>reinterpret_cast&lt;VMap&lt;$T1,$T2&gt;::Pair*&gt;(_cowdata._ptr)</ValuePointer>
</ArrayItems>
</Expand>
</Type>
<Type Name="VMap&lt;Callable,*&gt;::Pair">
<DisplayString Condition="dynamic_cast&lt;CallableCustomMethodPointerBase*&gt;(key.custom)">{dynamic_cast&lt;CallableCustomMethodPointerBase*&gt;(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&lt;ObjData*&gt;(&amp;_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&lt;const Variant::PackedArrayRef&lt;unsigned char&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_INT32_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;int&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_INT64_ARRAY">{*reinterpret_cast&lt;PackedInt64Array *&gt;(&amp;_data.packed_array[1])}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_FLOAT32_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;float&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_FLOAT64_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;double&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_STRING_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;String&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_VECTOR2_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector2&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_VECTOR3_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector3&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_COLOR_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Color&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type == Variant::PACKED_VECTOR4_ARRAY">{reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector4&gt;*&gt;(_data.packed_array)->array}</DisplayString>
<DisplayString Condition="type &lt; 0 || type >= Variant::VARIANT_MAX">[INVALID]</DisplayString>
<StringView Condition="type == Variant::STRING &amp;&amp; ((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&lt;ObjData*&gt;(&amp;_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&lt;const Variant::PackedArrayRef&lt;unsigned char&gt;*&gt;(_data.packed_array)->array</Item>
<Item Name="[value]" Condition="type == Variant::PACKED_INT32_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;int&gt;*&gt;(_data.packed_array)->array</Item>
<Item Name="[value]" Condition="type == Variant::PACKED_INT64_ARRAY">*reinterpret_cast&lt;PackedInt64Array *&gt;(&amp;_data.packed_array[1])</Item>
<Item Name="[value]" Condition="type == Variant::PACKED_FLOAT32_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;float&gt;*&gt;(_data.packed_array)->array</Item>
<Item Name="[value]" Condition="type == Variant::PACKED_FLOAT64_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;double&gt;*&gt;(_data.packed_array)->array</Item>
<Item Name="[value]" Condition="type == Variant::PACKED_STRING_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;String&gt;*&gt;(_data.packed_array)->array</Item>
<Item Name="[value]" Condition="type == Variant::PACKED_VECTOR2_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector2&gt;*&gt;(_data.packed_array)->array</Item>
<Item Name="[value]" Condition="type == Variant::PACKED_VECTOR3_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector3&gt;*&gt;(_data.packed_array)->array</Item>
<Item Name="[value]" Condition="type == Variant::PACKED_COLOR_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Color&gt;*&gt;(_data.packed_array)->array</Item>
<Item Name="[value]" Condition="type == Variant::PACKED_VECTOR4_ARRAY">reinterpret_cast&lt;const Variant::PackedArrayRef&lt;Vector4&gt;*&gt;(_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&lt;void**&gt;(opaque),s32}</DisplayString>
<Expand>
<Item Name="opaque_ptr">*reinterpret_cast&lt;void**&gt;(opaque)</Item>
<Item Name="string">*reinterpret_cast&lt;void**&gt;(opaque),s32</Item>
</Expand>
</Type>
<Type Name="StringName">
<DisplayString Condition="_data &amp;&amp; _data->cname">{_data->cname,na}</DisplayString>
<DisplayString Condition="_data &amp;&amp; !_data->cname">{_data->name,s32}</DisplayString>
<DisplayString Condition="!_data">[empty]</DisplayString>
<StringView Condition="_data &amp;&amp; _data->cname">_data->cname,na</StringView>
<StringView Condition="_data &amp;&amp; !_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&lt;const char***&gt;(opaque))[1]">{(*reinterpret_cast&lt;const char***&gt;(opaque))[1],s8}</DisplayString>
<DisplayString Condition="!(*reinterpret_cast&lt;const char***&gt;(opaque))[1]">{(*reinterpret_cast&lt;const char***&gt;(opaque))[2],s32}</DisplayString>
<Expand>
<Item Name="opaque_ptr">*reinterpret_cast&lt;void**&gt;(opaque)</Item>
<Item Name="&amp;cname">(*reinterpret_cast&lt;const char***&gt;(opaque))+1</Item>
<Item Name="cname">(*reinterpret_cast&lt;const char***&gt;(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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

View 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

View 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

View 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);
}

View 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;
}

View 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);
};

View 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
View 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 &quot;plat=$(PlatformTarget)&quot;",
"^&amp; (if &quot;$(PlatformTarget)&quot;==&quot;x64&quot; (set &quot;plat=x86_amd64&quot;))",
f"^&amp; call &quot;{batch_file}&quot; !plat!",
"^&amp;",
]

File diff suppressed because it is too large Load Diff

View 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();
};

File diff suppressed because it is too large Load Diff

View 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();
};

View 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>

View 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"

View 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))

View 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

View 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

View 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;
}

View 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();
};

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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