feat: godot-engine-source-4.3-stable

This commit is contained in:
Jan van der Weide 2025-01-17 16:36:38 +01:00
parent c59a7dcade
commit 7125d019b5
11149 changed files with 5070401 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/contributing/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.

View file

@ -0,0 +1,138 @@
#!/usr/bin/env python
Import("env")
import os
from pathlib import Path
import platform_windows_builders
sources = []
common_win = [
"godot_windows.cpp",
"os_windows.cpp",
"display_server_windows.cpp",
"key_mapping_windows.cpp",
"joypad_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",
]
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",
]
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)
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
prog = env.add_program("#bin/godot", sources, PROGSUFFIX=env["PROGSUFFIX"])
arrange_program_clean(prog)
# Build console wrapper app.
if env["windows_subsystem"] == "gui":
env_wrap = env.Clone()
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
# Microsoft Visual Studio Project Generation
if env["vsproj"]:
env.vs_srcs += ["platform/windows/" + res_file]
env.vs_srcs += ["platform/windows/godot.natvis"]
for x in common_win:
env.vs_srcs += ["platform/windows/" + str(x)]
if env["windows_subsystem"] == "gui":
for x in common_win_wrap:
env.vs_srcs += ["platform/windows/" + str(x)]
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.Command(
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.Command(
"#bin/" + pix_dll,
env["pix_path"] + "/bin/" + dxc_arch_subdir + "/" + pix_dll,
Copy("$TARGET", "$SOURCE"),
)
if not os.getenv("VCINSTALLDIR"):
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,185 @@
/**************************************************************************/
/* 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 <stdio.h>
#include <stdlib.h>
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4
#endif
int main(int argc, char *argv[]) {
// Get executable name.
WCHAR exe_name[MAX_PATH] = {};
if (!GetModuleFileNameW(nullptr, exe_name, MAX_PATH)) {
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 = 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,60 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef CRASH_HANDLER_WINDOWS_H
#define CRASH_HANDLER_WINDOWS_H
#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();
};
#endif // CRASH_HANDLER_WINDOWS_H

View file

@ -0,0 +1,249 @@
/**************************************************************************/
/* 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/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 <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 = { 0 };
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;
}
String msg;
const ProjectSettings *proj_settings = ProjectSettings::get_singleton();
if (proj_settings) {
msg = proj_settings->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(VERSION_HASH).is_empty()) {
print_error(vformat("Engine version: %s", VERSION_FULL_NAME));
} else {
print_error(vformat("Engine version: %s (%s)", VERSION_FULL_NAME, 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 BACKTRACE --");
print_error("================================================================");
SymCleanup(process);
// 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,205 @@
/**************************************************************************/
/* 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/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 <signal.h>
#include <algorithm>
#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;
}
String msg;
const ProjectSettings *proj_settings = ProjectSettings::get_singleton();
if (proj_settings) {
msg = proj_settings->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(VERSION_HASH).is_empty()) {
print_error(vformat("Engine version: %s", VERSION_FULL_NAME));
} else {
print_error(vformat("Engine version: %s (%s)", VERSION_FULL_NAME, 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(NULL), &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;
data.state = backtrace_create_state(_execpath.utf8().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 BACKTRACE --");
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
}

View file

@ -0,0 +1,864 @@
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
if TYPE_CHECKING:
from SCons.Script.SConscript import SConsEnvironment
# To match other platforms
STACK_SIZE = 8388608
def get_name():
return "Windows"
def try_cmd(test, prefix, arch):
if arch:
try:
out = subprocess.Popen(
get_mingw_bin_prefix(prefix, arch) + test,
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
out.communicate()
if out.returncode == 0:
return True
except Exception:
pass
else:
for a in ["x86_64", "x86_32", "arm64", "arm32"]:
try:
out = subprocess.Popen(
get_mingw_bin_prefix(prefix, a) + test,
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
out.communicate()
if out.returncode == 0:
return True
except Exception:
pass
return False
def can_build():
if os.name == "nt":
# Building natively on Windows
# If VCINSTALLDIR is set in the OS environ, use traditional Godot logic to set up MSVC
if os.getenv("VCINSTALLDIR"): # MSVC, manual setup
return True
# Otherwise, let SCons find MSVC if installed, or else MinGW.
# Since we're just returning True here, if there's no compiler
# installed, we'll get errors when it tries to build with the
# null compiler.
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):
if not prefix:
mingw_bin_prefix = ""
elif prefix[-1] != "/":
mingw_bin_prefix = prefix + "/bin/"
else:
mingw_bin_prefix = prefix + "bin/"
if arch == "x86_64":
mingw_bin_prefix += "x86_64-w64-mingw32-"
elif arch == "x86_32":
mingw_bin_prefix += "i686-w64-mingw32-"
elif arch == "arm32":
mingw_bin_prefix += "armv7-w64-mingw32-"
elif arch == "arm64":
mingw_bin_prefix += "aarch64-w64-mingw32-"
return mingw_bin_prefix
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_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),
# Targeted Windows version: 7 (and later), minimum supported version
# XP support dropped after EOL due to missing API for IPv6 and other issues
# Vista support dropped after EOL due to GH-10243
(
"target_win_version",
"Targeted Windows version, >= 0x0601 (Windows 7)",
"0x0601",
),
EnumVariable("windows_subsystem", "Windows subsystem", "gui", ("gui", "console")),
(
"msvc_version",
"MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.",
None,
),
BoolVariable("use_mingw", "Use the Mingw compiler, even if MSVC is installed.", False),
BoolVariable("use_llvm", "Use the LLVM compiler", False),
BoolVariable("use_static_cpp", "Link MinGW/MSVC C++ runtime libraries statically", True),
BoolVariable("use_asan", "Use address sanitizer (ASAN)", 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": ["mono"],
}
def build_res_file(target, source, env: "SConsEnvironment"):
arch_aliases = {
"x86_32": "pe-i386",
"x86_64": "pe-x86-64",
"arm32": "armv7-w64-mingw32",
"arm64": "aarch64-w64-mingw32",
}
cmdbase = "windres --include-dir . --target=" + arch_aliases[env["arch"]]
mingw_bin_prefix = get_mingw_bin_prefix(env["mingw_prefix"], env["arch"])
for x in range(len(source)):
ok = True
# Try prefixed executable (MinGW on Linux).
cmd = mingw_bin_prefix + cmdbase + " -i " + str(source[x]) + " -o " + str(target[x])
try:
out = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE).communicate()
if len(out[1]):
ok = False
except Exception:
ok = False
# Try generic executable (MSYS2).
if not ok:
cmd = cmdbase + " -i " + str(source[x]) + " -o " + str(target[x])
try:
out = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE).communicate()
if len(out[1]):
return -1
except Exception:
return -1
return 0
def setup_msvc_manual(env: "SConsEnvironment"):
"""Running from VCVARS environment"""
env_arch = detect_build_env_arch()
if env["arch"] != env_arch:
print_error(
"Arch argument (%s) is not matching Native/Cross Compile Tools Prompt/Developer Console (or Visual Studio settings) that is being used to run SCons (%s).\n"
"Run SCons again without arch argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler will be executed and inform you."
% (env["arch"], env_arch)
)
sys.exit(255)
print("Using VCVARS-determined MSVC, arch %s" % (env_arch))
def setup_msvc_auto(env: "SConsEnvironment"):
"""Set up MSVC using SCons's auto-detection logic"""
# If MSVC_VERSION is set by SCons, we know MSVC is installed.
# But we may want a different version or target arch.
# Valid architectures for MSVC's TARGET_ARCH:
# ['amd64', 'emt64', 'i386', 'i486', 'i586', 'i686', 'ia64', 'itanium', 'x86', 'x86_64', 'arm', 'arm64', 'aarch64']
# Our x86_64 and arm64 are the same, and we need to map the 32-bit
# architectures to other names since MSVC isn't as explicit.
# The rest we don't need to worry about because they are
# aliases or aren't supported by Godot (itanium & ia64).
msvc_arch_aliases = {"x86_32": "x86", "arm32": "arm"}
if env["arch"] in msvc_arch_aliases.keys():
env["TARGET_ARCH"] = msvc_arch_aliases[env["arch"]]
else:
env["TARGET_ARCH"] = env["arch"]
# The env may have already been set up with default MSVC tools, so
# reset a few things so we can set it up with the tools we want.
# (Ideally we'd decide on the tool config before configuring any
# environment, and just set the env up once, but this function runs
# on an existing env so this is the simplest way.)
env["MSVC_SETUP_RUN"] = False # Need to set this to re-run the tool
env["MSVS_VERSION"] = None
env["MSVC_VERSION"] = None
if "msvc_version" in env:
env["MSVC_VERSION"] = env["msvc_version"]
env.Tool("msvc")
env.Tool("mssdk") # we want the MS SDK
# Re-add potentially overwritten flags.
env.AppendUnique(CCFLAGS=env.get("ccflags", "").split())
env.AppendUnique(CXXFLAGS=env.get("cxxflags", "").split())
env.AppendUnique(CFLAGS=env.get("cflags", "").split())
env.AppendUnique(RCFLAGS=env.get("rcflags", "").split())
# Note: actual compiler version can be found in env['MSVC_VERSION'], e.g. "14.1" for VS2015
print("Using SCons-detected MSVC version %s, arch %s" % (env["MSVC_VERSION"], env["arch"]))
def setup_mingw(env: "SConsEnvironment"):
"""Set up env for use with mingw"""
env_arch = detect_build_env_arch()
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 != "" and env["arch"] != env_arch:
print_error(
"Arch argument (%s) is not matching MSYS2 console/environment that is being used to run SCons (%s).\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."
% (env["arch"], env_arch)
)
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)
print("Using MinGW, arch %s" % (env["arch"]))
def configure_msvc(env: "SConsEnvironment", vcvars_msvc_config):
"""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
env["MAXLINELENGTH"] = 8192 # Windows Vista and beyond, so always applicable.
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_path + "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.
env.AppendUnique(CXXFLAGS=["/TP"]) # assume all sources are C++
# 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"])
if vcvars_msvc_config: # should be automatic if SCons found it
if os.getenv("WindowsSdkDir") is not None:
env.Prepend(CPPPATH=[str(os.getenv("WindowsSdkDir")) + "/Include"])
else:
print_warning("Missing environment variable: WindowsSdkDir")
if int(env["target_win_version"], 16) < 0x0601:
print_error("`target_win_version` should be 0x0601 or higher (Windows 7).")
sys.exit(255)
env.AppendUnique(
CPPDEFINES=[
"WINDOWS_ENABLED",
"WASAPI_ENABLED",
"WINMIDI_ENABLED",
"TYPED_METHOD_BIND",
"WIN32",
"WINVER=%s" % env["target_win_version"],
"_WIN32_WINNT=%s" % env["target_win_version"],
]
)
env.AppendUnique(CPPDEFINES=["NOMINMAX"]) # disable bogus min/max WinDef.h macros
if env["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.Append(CCFLAGS=["/fsanitize=address"])
env.Append(LINKFLAGS=["/INFERASANLIBS"])
## Libs
LIBS = [
"winmm",
"dsound",
"kernel32",
"ole32",
"oleaut32",
"sapi",
"user32",
"gdi32",
"IPHLPAPI",
"Shlwapi",
"wsock32",
"Ws2_32",
"shell32",
"advapi32",
"dinput8",
"dxguid",
"imm32",
"bcrypt",
"Crypt32",
"Avrt",
"dwmapi",
"dwrite",
"wbemuuid",
"ntdll",
]
if env.debug_features:
LIBS += ["psapi", "dbghelp"]
if env["vulkan"]:
env.AppendUnique(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
if not env["use_volk"]:
LIBS += ["vulkan"]
if env["d3d12"]:
# Check whether we have d3d12 dependencies installed.
if not os.path.exists(env["mesa_libs"]):
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/contributing/development/compiling/compiling_for_windows.html"
)
sys.exit(255)
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"]
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(CPPPATH=["#thirdparty/angle/include"])
if env["target"] in ["editor", "template_debug"]:
LIBS += ["psapi", "dbghelp"]
env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS])
if vcvars_msvc_config:
if os.getenv("WindowsSdkDir") is not None:
env.Append(LIBPATH=[str(os.getenv("WindowsSdkDir")) + "/Lib"])
else:
print_warning("Missing environment variable: WindowsSdkDir")
## 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":
print_error("ThinLTO is only compatible with LLVM, use `use_llvm=yes` or `lto=full`.")
sys.exit(255)
env.AppendUnique(CCFLAGS=["/GL"])
env.AppendUnique(ARFLAGS=["/LTCG"])
if env["progress"]:
env.AppendUnique(LINKFLAGS=["/LTCG:STATUS"])
else:
env.AppendUnique(LINKFLAGS=["/LTCG"])
if vcvars_msvc_config:
env.Prepend(CPPPATH=[p for p in str(os.getenv("INCLUDE")).split(";")])
env.Append(LIBPATH=[p for p in str(os.getenv("LIB")).split(";")])
# Incremental linking fix
env["BUILDERS"]["ProgramOriginal"] = env["BUILDERS"]["Program"]
env["BUILDERS"]["Program"] = methods.precious_program
env.Append(LINKFLAGS=["/NATVIS:platform\\windows\\godot.natvis"])
env.AppendUnique(LINKFLAGS=["/STACK:" + str(STACK_SIZE)])
def configure_mingw(env: "SConsEnvironment"):
# Workaround for MinGW. See:
# https://www.scons.org/wiki/LongCmdLinesOnWin32
env.use_windows_spawn_fix()
## 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
# TODO: Re-evaluate the need for this / streamline with common config.
if env["target"] == "template_release":
if env["arch"] != "arm64":
env.Append(CCFLAGS=["-msse2"])
elif 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 os.name != "nt":
env["PROGSUFFIX"] = env["PROGSUFFIX"] + ".exe" # for linux cross-compilation
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"] in ["x86_32", "x86_64"]:
env["x86_libtheora_opt_gcc"] = True
env.Append(CCFLAGS=["-ffp-contract=off"])
mingw_bin_prefix = get_mingw_bin_prefix(env["mingw_prefix"], env["arch"])
if env["use_llvm"]:
env["CC"] = mingw_bin_prefix + "clang"
env["CXX"] = mingw_bin_prefix + "clang++"
if try_cmd("as --version", env["mingw_prefix"], env["arch"]):
env["AS"] = mingw_bin_prefix + "as"
env.Append(ASFLAGS=["-c"])
if try_cmd("ar --version", env["mingw_prefix"], env["arch"]):
env["AR"] = mingw_bin_prefix + "ar"
if try_cmd("ranlib --version", env["mingw_prefix"], env["arch"]):
env["RANLIB"] = mingw_bin_prefix + "ranlib"
env.extra_suffix = ".llvm" + env.extra_suffix
else:
env["CC"] = mingw_bin_prefix + "gcc"
env["CXX"] = mingw_bin_prefix + "g++"
if try_cmd("as --version", env["mingw_prefix"], env["arch"]):
env["AS"] = mingw_bin_prefix + "as"
if try_cmd("gcc-ar --version", env["mingw_prefix"], env["arch"]):
env["AR"] = mingw_bin_prefix + "gcc-ar"
if try_cmd("gcc-ranlib --version", env["mingw_prefix"], env["arch"]):
env["RANLIB"] = mingw_bin_prefix + "gcc-ranlib"
## LTO
if env["lto"] == "auto": # Full LTO for production with MinGW.
env["lto"] = "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"])
env.Append(LINKFLAGS=["-Wl,--stack," + str(STACK_SIZE)])
## Compile flags
if int(env["target_win_version"], 16) < 0x0601:
print_error("`target_win_version` should be 0x0601 or higher (Windows 7).")
sys.exit(255)
if not env["use_llvm"]:
env.Append(CCFLAGS=["-mwindows"])
env.Append(CPPDEFINES=["WINDOWS_ENABLED", "WASAPI_ENABLED", "WINMIDI_ENABLED"])
env.Append(
CPPDEFINES=[
("WINVER", env["target_win_version"]),
("_WIN32_WINNT", env["target_win_version"]),
]
)
env.Append(
LIBS=[
"mingw32",
"dsound",
"ole32",
"d3d9",
"winmm",
"gdi32",
"iphlpapi",
"shlwapi",
"wsock32",
"ws2_32",
"kernel32",
"oleaut32",
"sapi",
"dinput8",
"dxguid",
"ksuser",
"imm32",
"bcrypt",
"crypt32",
"avrt",
"uuid",
"dwmapi",
"dwrite",
"wbemuuid",
"ntdll",
]
)
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["d3d12"]:
# Check whether we have d3d12 dependencies installed.
if not os.path.exists(env["mesa_libs"]):
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/contributing/development/compiling/compiling_for_windows.html"
)
sys.exit(255)
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"])
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(CPPPATH=["#thirdparty/angle/include"])
env.Append(CPPDEFINES=["MINGW_ENABLED", ("MINGW_HAS_SECURE_API", 1)])
# resrc
env.Append(BUILDERS={"RES": env.Builder(action=build_res_file, suffix=".o", src_suffix=".rc")})
def configure(env: "SConsEnvironment"):
# Validate arch.
supported_arches = ["x86_32", "x86_64", "arm32", "arm64"]
if env["arch"] not in supported_arches:
print_error(
'Unsupported CPU architecture "%s" for Windows. Supported architectures are: %s.'
% (env["arch"], ", ".join(supported_arches))
)
sys.exit(255)
# At this point the env has been set up with basic tools/compilers.
env.Prepend(CPPPATH=["#platform/windows"])
if os.name == "nt":
env["ENV"] = os.environ # this makes build less repeatable, but simplifies some things
env["ENV"]["TMP"] = os.environ["TMP"]
# First figure out which compiler, version, and target arch we're using
if os.getenv("VCINSTALLDIR") and detect_build_env_arch() and not env["use_mingw"]:
setup_msvc_manual(env)
env.msvc = True
vcvars_msvc_config = True
elif env.get("MSVC_VERSION", "") and not env["use_mingw"]:
setup_msvc_auto(env)
env.msvc = True
vcvars_msvc_config = False
else:
setup_mingw(env)
env.msvc = False
# Now set compiler/linker flags
if env.msvc:
configure_msvc(env, vcvars_msvc_config)
else: # MinGW
configure_mingw(env)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,785 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef DISPLAY_SERVER_WINDOWS_H
#define DISPLAY_SERVER_WINDOWS_H
#include "crash_handler_windows.h"
#include "joypad_windows.h"
#include "key_mapping_windows.h"
#include "tts_windows.h"
#include "core/config/project_settings.h"
#include "core/input/input.h"
#include "core/os/os.h"
#include "drivers/unix/ip_unix.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 <stdio.h>
#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)(OSVERSIONINFOW *lpVersionInformation);
typedef bool(WINAPI *AllowDarkModeForAppPtr)(bool darkMode);
typedef PreferredAppMode(WINAPI *SetPreferredAppModePtr)(PreferredAppMode appMode);
typedef void(WINAPI *RefreshImmersiveColorPolicyStatePtr)();
typedef void(WINAPI *FlushMenuThemesPtr)();
// Windows Ink API
#ifndef POINTER_STRUCTURES
#define POINTER_STRUCTURES
typedef DWORD POINTER_INPUT_TYPE;
typedef UINT32 POINTER_FLAGS;
typedef UINT32 PEN_FLAGS;
typedef UINT32 PEN_MASK;
#ifndef PEN_FLAG_INVERTED
#define PEN_FLAG_INVERTED 0x00000002
#endif
#ifndef PEN_FLAG_ERASER
#define PEN_FLAG_ERASER 0x00000004
#endif
#ifndef PEN_MASK_PRESSURE
#define PEN_MASK_PRESSURE 0x00000001
#endif
#ifndef PEN_MASK_TILT_X
#define PEN_MASK_TILT_X 0x00000004
#endif
#ifndef PEN_MASK_TILT_Y
#define PEN_MASK_TILT_Y 0x00000008
#endif
#ifndef POINTER_MESSAGE_FLAG_FIRSTBUTTON
#define POINTER_MESSAGE_FLAG_FIRSTBUTTON 0x00000010
#endif
#ifndef POINTER_MESSAGE_FLAG_SECONDBUTTON
#define POINTER_MESSAGE_FLAG_SECONDBUTTON 0x00000020
#endif
#ifndef POINTER_MESSAGE_FLAG_THIRDBUTTON
#define POINTER_MESSAGE_FLAG_THIRDBUTTON 0x00000040
#endif
#ifndef POINTER_MESSAGE_FLAG_FOURTHBUTTON
#define POINTER_MESSAGE_FLAG_FOURTHBUTTON 0x00000080
#endif
#ifndef POINTER_MESSAGE_FLAG_FIFTHBUTTON
#define POINTER_MESSAGE_FLAG_FIFTHBUTTON 0x00000100
#endif
#ifndef IS_POINTER_FLAG_SET_WPARAM
#define IS_POINTER_FLAG_SET_WPARAM(wParam, flag) (((DWORD)HIWORD(wParam) & (flag)) == (flag))
#endif
#ifndef IS_POINTER_FIRSTBUTTON_WPARAM
#define IS_POINTER_FIRSTBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIRSTBUTTON)
#endif
#ifndef IS_POINTER_SECONDBUTTON_WPARAM
#define IS_POINTER_SECONDBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_SECONDBUTTON)
#endif
#ifndef IS_POINTER_THIRDBUTTON_WPARAM
#define IS_POINTER_THIRDBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_THIRDBUTTON)
#endif
#ifndef IS_POINTER_FOURTHBUTTON_WPARAM
#define IS_POINTER_FOURTHBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FOURTHBUTTON)
#endif
#ifndef IS_POINTER_FIFTHBUTTON_WPARAM
#define IS_POINTER_FIFTHBUTTON_WPARAM(wParam) IS_POINTER_FLAG_SET_WPARAM(wParam, POINTER_MESSAGE_FLAG_FIFTHBUTTON)
#endif
#ifndef GET_POINTERID_WPARAM
#define GET_POINTERID_WPARAM(wParam) (LOWORD(wParam))
#endif
#if WINVER < 0x0602
enum tagPOINTER_INPUT_TYPE {
PT_POINTER = 0x00000001,
PT_TOUCH = 0x00000002,
PT_PEN = 0x00000003,
PT_MOUSE = 0x00000004,
PT_TOUCHPAD = 0x00000005
};
typedef enum tagPOINTER_BUTTON_CHANGE_TYPE {
POINTER_CHANGE_NONE,
POINTER_CHANGE_FIRSTBUTTON_DOWN,
POINTER_CHANGE_FIRSTBUTTON_UP,
POINTER_CHANGE_SECONDBUTTON_DOWN,
POINTER_CHANGE_SECONDBUTTON_UP,
POINTER_CHANGE_THIRDBUTTON_DOWN,
POINTER_CHANGE_THIRDBUTTON_UP,
POINTER_CHANGE_FOURTHBUTTON_DOWN,
POINTER_CHANGE_FOURTHBUTTON_UP,
POINTER_CHANGE_FIFTHBUTTON_DOWN,
POINTER_CHANGE_FIFTHBUTTON_UP,
} POINTER_BUTTON_CHANGE_TYPE;
typedef struct tagPOINTER_INFO {
POINTER_INPUT_TYPE pointerType;
UINT32 pointerId;
UINT32 frameId;
POINTER_FLAGS pointerFlags;
HANDLE sourceDevice;
HWND hwndTarget;
POINT ptPixelLocation;
POINT ptHimetricLocation;
POINT ptPixelLocationRaw;
POINT ptHimetricLocationRaw;
DWORD dwTime;
UINT32 historyCount;
INT32 InputData;
DWORD dwKeyStates;
UINT64 PerformanceCount;
POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
} POINTER_INFO;
typedef struct tagPOINTER_PEN_INFO {
POINTER_INFO pointerInfo;
PEN_FLAGS penFlags;
PEN_MASK penMask;
UINT32 pressure;
UINT32 rotation;
INT32 tiltX;
INT32 tiltY;
} POINTER_PEN_INFO;
#endif
#endif //POINTER_STRUCTURES
#ifndef WM_POINTERUPDATE
#define WM_POINTERUPDATE 0x0245
#endif
#ifndef WM_POINTERENTER
#define WM_POINTERENTER 0x0249
#endif
#ifndef WM_POINTERLEAVE
#define WM_POINTERLEAVE 0x024A
#endif
#ifndef WM_POINTERDOWN
#define WM_POINTERDOWN 0x0246
#endif
#ifndef WM_POINTERUP
#define WM_POINTERUP 0x0247
#endif
typedef BOOL(WINAPI *GetPointerTypePtr)(uint32_t p_id, POINTER_INPUT_TYPE *p_type);
typedef BOOL(WINAPI *GetPointerPenInfoPtr)(uint32_t p_id, POINTER_PEN_INFO *p_pen_info);
typedef BOOL(WINAPI *LogicalToPhysicalPointForPerMonitorDPIPtr)(HWND hwnd, LPPOINT lpPoint);
typedef BOOL(WINAPI *PhysicalToLogicalPointForPerMonitorDPIPtr)(HWND hwnd, LPPOINT lpPoint);
typedef HRESULT(WINAPI *SHLoadIndirectStringPtr)(PCWSTR pszSource, PWSTR pszOutBuf, UINT cchOutBuf, void **ppvReserved);
typedef struct {
BYTE bWidth; // Width, in pixels, of the image
BYTE bHeight; // Height, in pixels, of the image
BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
BYTE bReserved; // Reserved ( must be 0)
WORD wPlanes; // Color Planes
WORD wBitCount; // Bits per pixel
DWORD dwBytesInRes; // How many bytes in this resource?
DWORD dwImageOffset; // Where in the file is this image?
} ICONDIRENTRY, *LPICONDIRENTRY;
typedef struct {
WORD idReserved; // Reserved (must be 0)
WORD idType; // Resource Type (1 for icons)
WORD idCount; // How many images?
ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em)
} ICONDIR, *LPICONDIR;
class DisplayServerWindows : public DisplayServer {
// No need to register with GDCLASS, it's platform-specific and nothing is added.
_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;
// Windows Ink API
static bool winink_available;
static GetPointerTypePtr win8p_GetPointerType;
static GetPointerPenInfoPtr win8p_GetPointerPenInfo;
// DPI conversion API
static LogicalToPhysicalPointForPerMonitorDPIPtr win81p_LogicalToPhysicalPointForPerMonitorDPI;
static PhysicalToLogicalPointForPerMonitorDPIPtr win81p_PhysicalToLogicalPointForPerMonitorDPI;
// Shell API
static SHLoadIndirectStringPtr load_indirect_string;
void _update_tablet_ctx(const String &p_old_driver, const String &p_new_driver);
String tablet_driver;
Vector<String> tablet_drivers;
enum TimerID {
TIMER_ID_MOVE_REDRAW = 1,
TIMER_ID_WINDOW_ACTIVATION = 2,
};
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;
HANDLE power_request;
TTS_Windows *tts = nullptr;
NativeMenuWindows *native_menu = nullptr;
struct WindowData {
HWND hWnd;
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 window_focused = false;
int activate_state = 0;
bool was_maximized = false;
bool always_on_top = false;
bool no_focus = false;
bool exclusive = false;
bool context_created = false;
bool mpass = 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;
WindowID transient_parent = INVALID_WINDOW_ID;
HashSet<WindowID> transient_children;
bool is_popup = false;
Rect2i parent_safe_rect;
};
JoypadWindows *joypad = nullptr;
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);
WindowID window_id_counter = MAIN_WINDOW_ID;
RBMap<WindowID, WindowData> windows;
WindowID last_focused_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;
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_fullscreen, bool p_multiwindow_fs, bool p_borderless, bool p_resizable, bool p_maximized, bool p_maximized_fs, bool p_no_activate_focus, DWORD &r_style, DWORD &r_style_ex);
MouseMode mouse_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);
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);
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);
String _get_keyboard_layout_display_name(const String &p_klid) const;
String _get_klid(HKL p_hkl) const;
public:
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) 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) override;
virtual void mouse_set_mode(MouseMode p_mode) override;
virtual MouseMode mouse_get_mode() 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 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 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 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 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 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, 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, Error &r_error);
~DisplayServerWindows();
};
#endif // DISPLAY_SERVER_WINDOWS_H

View file

@ -0,0 +1,140 @@
<?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].
Official export templates include [code]x86_32[/code] and [code]x86_64[/code] binaries only.
</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="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,63 @@
/**************************************************************************/
/* 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() {
#ifndef ANDROID_ENABLED
EDITOR_DEF("export/windows/rcedit", "");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/rcedit", PROPERTY_HINT_GLOBAL_FILE, "*.exe"));
#ifdef WINDOWS_ENABLED
EDITOR_DEF("export/windows/signtool", "");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/signtool", PROPERTY_HINT_GLOBAL_FILE, "*.exe"));
#else
EDITOR_DEF("export/windows/osslsigncode", "");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/osslsigncode", PROPERTY_HINT_GLOBAL_FILE));
// On non-Windows we need WINE to run rcedit
EDITOR_DEF("export/windows/wine", "");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/windows/wine", PROPERTY_HINT_GLOBAL_FILE));
#endif
#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,37 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef WINDOWS_EXPORT_H
#define WINDOWS_EXPORT_H
void register_windows_exporter_types();
void register_windows_exporter();
#endif // WINDOWS_EXPORT_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,102 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef WINDOWS_EXPORT_PLUGIN_H
#define WINDOWS_EXPORT_PLUGIN_H
#include "core/io/file_access.h"
#include "core/os/os.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform_pc.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 _rcedit_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);
public:
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int 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<ImageTexture> 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, int p_debug_flags) override;
virtual void cleanup() override;
EditorExportPlatformWindows();
};
#endif // WINDOWS_EXPORT_PLUGIN_H

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: 212 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: 242 B

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 <stdio.h>
#include <stdlib.h>
#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,61 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef GL_MANAGER_WINDOWS_ANGLE_H
#define GL_MANAGER_WINDOWS_ANGLE_H
#if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED)
#include "core/error/error_list.h"
#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
#endif // GL_MANAGER_WINDOWS_ANGLE_H

View file

@ -0,0 +1,557 @@
/**************************************************************************/
/* 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/nvapi/nvapi_minimal.h"
#include <dwmapi.h>
#include <stdio.h>
#include <stdlib.h>
#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
#if defined(__GNUC__)
// Workaround GCC warning from -Wcast-function-type.
#define GetProcAddress (void *)GetProcAddress
#endif
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 = 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)GetProcAddress(module, "wglCreateContext");
gd_wglMakeCurrent = (PFNWGLMAKECURRENT)GetProcAddress(module, "wglMakeCurrent");
gd_wglDeleteContext = (PFNWGLDELETECONTEXT)GetProcAddress(module, "wglDeleteContext");
gd_wglGetProcAddress = (PFNWGLGETPROCADDRESS)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,109 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef GL_MANAGER_WINDOWS_NATIVE_H
#define GL_MANAGER_WINDOWS_NATIVE_H
#if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED)
#include "core/error/error_list.h"
#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
#endif // GL_MANAGER_WINDOWS_NATIVE_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View file

@ -0,0 +1,303 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<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="TypedArray&lt;*&gt;">
<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="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},{y}}}</DisplayString>
<Expand>
<Item Name="x">x</Item>
<Item Name="y">y</Item>
</Expand>
</Type>
<Type Name="Vector3">
<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="Quaternion">
<DisplayString>Quaternion {{{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="Color">
<DisplayString>Color {{{r},{g},{b},{a}}}</DisplayString>
<Expand>
<Item Name="red">r</Item>
<Item Name="green">g</Item>
<Item Name="blue">b</Item>
<Item Name="alpha">a</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 VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,0
PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,0
FILEOS 4
FILETYPE 1
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Godot Engine"
VALUE "FileDescription", VERSION_NAME
VALUE "FileVersion", VERSION_NUMBER
VALUE "ProductName", 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", VERSION_FULL_BUILD
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View file

@ -0,0 +1,29 @@
#include "core/version.h"
GODOT_ICON ICON platform/windows/godot_console.ico
1 VERSIONINFO
FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,0
PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,0
FILEOS 4
FILETYPE 1
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Godot Engine"
VALUE "FileDescription", VERSION_NAME " (Console)"
VALUE "FileVersion", VERSION_NUMBER
VALUE "ProductName", 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", VERSION_FULL_BUILD
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View file

@ -0,0 +1,234 @@
/**************************************************************************/
/* 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 <locale.h>
#include <stdio.h>
// 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
PCHAR *
CommandLineToArgvA(
PCHAR CmdLine,
int *_argc) {
PCHAR *argv;
PCHAR _argv;
ULONG len;
ULONG argc;
CHAR a;
ULONG i, j;
BOOLEAN in_QM;
BOOLEAN in_TEXT;
BOOLEAN in_SPACE;
len = strlen(CmdLine);
i = ((len + 2) / 2) * sizeof(PVOID) + sizeof(PVOID);
argv = (PCHAR *)GlobalAlloc(GMEM_FIXED,
i + (len + 2) * sizeof(CHAR));
_argv = (PCHAR)(((PUCHAR)argv) + i);
argc = 0;
argv[argc] = _argv;
in_QM = FALSE;
in_TEXT = FALSE;
in_SPACE = TRUE;
i = 0;
j = 0;
a = CmdLine[i];
while (a) {
if (in_QM) {
if (a == '\"') {
in_QM = FALSE;
} else {
_argv[j] = a;
j++;
}
} else {
switch (a) {
case '\"':
in_QM = TRUE;
in_TEXT = TRUE;
if (in_SPACE) {
argv[argc] = _argv + j;
argc++;
}
in_SPACE = FALSE;
break;
case ' ':
case '\t':
case '\n':
case '\r':
if (in_TEXT) {
_argv[j] = '\0';
j++;
}
in_TEXT = FALSE;
in_SPACE = TRUE;
break;
default:
in_TEXT = TRUE;
if (in_SPACE) {
argv[argc] = _argv + j;
argc++;
}
_argv[j] = a;
j++;
in_SPACE = FALSE;
break;
}
}
i++;
a = CmdLine[i];
}
_argv[j] = '\0';
argv[argc] = nullptr;
(*_argc) = argc;
return argv;
}
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,568 @@
/**************************************************************************/
/* joypad_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 "joypad_windows.h"
#include <oleauto.h>
#include <wbemidl.h>
#if defined(__GNUC__)
// Workaround GCC warning from -Wcast-function-type.
#define GetProcAddress (void *)GetProcAddress
#endif
DWORD WINAPI _xinput_get_state(DWORD dwUserIndex, XINPUT_STATE *pState) {
return ERROR_DEVICE_NOT_CONNECTED;
}
DWORD WINAPI _xinput_set_state(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration) {
return ERROR_DEVICE_NOT_CONNECTED;
}
JoypadWindows::JoypadWindows() {
}
JoypadWindows::JoypadWindows(HWND *hwnd) {
input = Input::get_singleton();
hWnd = hwnd;
joypad_count = 0;
dinput = nullptr;
xinput_dll = nullptr;
xinput_get_state = nullptr;
xinput_set_state = nullptr;
load_xinput();
for (int i = 0; i < JOYPADS_MAX; i++) {
attached_joypads[i] = false;
}
HRESULT result = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (void **)&dinput, nullptr);
if (result == DI_OK) {
probe_joypads();
} else {
ERR_PRINT("Couldn't initialize DirectInput. Error: " + itos(result));
if (result == DIERR_OUTOFMEMORY) {
ERR_PRINT("The Windows DirectInput subsystem could not allocate sufficient memory.");
ERR_PRINT("Rebooting your PC may solve this issue.");
}
// Ensure dinput is still a nullptr.
dinput = nullptr;
}
}
JoypadWindows::~JoypadWindows() {
close_joypad();
if (dinput) {
dinput->Release();
}
unload_xinput();
}
bool JoypadWindows::have_device(const GUID &p_guid) {
for (int i = 0; i < JOYPADS_MAX; i++) {
if (d_joypads[i].guid == p_guid) {
d_joypads[i].confirmed = true;
return true;
}
}
return false;
}
// adapted from SDL2, works a lot better than the MSDN version
bool JoypadWindows::is_xinput_device(const GUID *p_guid) {
static GUID IID_ValveStreamingGamepad = { MAKELONG(0x28DE, 0x11FF), 0x28DE, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
static GUID IID_X360WiredGamepad = { MAKELONG(0x045E, 0x02A1), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
static GUID IID_X360WirelessGamepad = { MAKELONG(0x045E, 0x028E), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
static GUID IID_XSWirelessGamepad = { MAKELONG(0x045E, 0x0B13), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
static GUID IID_XEliteWirelessGamepad = { MAKELONG(0x045E, 0x0B05), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
static GUID IID_XOneWiredGamepad = { MAKELONG(0x045E, 0x02FF), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
static GUID IID_XOneWirelessGamepad = { MAKELONG(0x045E, 0x02DD), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
static GUID IID_XOneNewWirelessGamepad = { MAKELONG(0x045E, 0x02D1), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
static GUID IID_XOneSWirelessGamepad = { MAKELONG(0x045E, 0x02EA), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
static GUID IID_XOneSBluetoothGamepad = { MAKELONG(0x045E, 0x02E0), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
static GUID IID_XOneEliteWirelessGamepad = { MAKELONG(0x045E, 0x02E3), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
static GUID IID_XOneElite2WirelessGamepad = { MAKELONG(0x045E, 0x0B22), 0x0000, 0x0000, { 0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44 } };
if (memcmp(p_guid, &IID_ValveStreamingGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_X360WiredGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_X360WirelessGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_XSWirelessGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_XEliteWirelessGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_XOneWiredGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_XOneWirelessGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_XOneNewWirelessGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_XOneSWirelessGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_XOneSBluetoothGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_XOneEliteWirelessGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_XOneElite2WirelessGamepad, sizeof(*p_guid)) == 0)
return true;
PRAWINPUTDEVICELIST dev_list = nullptr;
unsigned int dev_list_count = 0;
if (GetRawInputDeviceList(nullptr, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) {
return false;
}
dev_list = (PRAWINPUTDEVICELIST)memalloc(sizeof(RAWINPUTDEVICELIST) * dev_list_count);
ERR_FAIL_NULL_V_MSG(dev_list, false, "Out of memory.");
if (GetRawInputDeviceList(dev_list, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) {
memfree(dev_list);
return false;
}
for (unsigned int i = 0; i < dev_list_count; i++) {
RID_DEVICE_INFO rdi;
char dev_name[128];
UINT rdiSize = sizeof(rdi);
UINT nameSize = sizeof(dev_name);
rdi.cbSize = rdiSize;
if ((dev_list[i].dwType == RIM_TYPEHID) &&
(GetRawInputDeviceInfoA(dev_list[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) != (UINT)-1) &&
(MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == (LONG)p_guid->Data1) &&
(GetRawInputDeviceInfoA(dev_list[i].hDevice, RIDI_DEVICENAME, &dev_name, &nameSize) != (UINT)-1) &&
(strstr(dev_name, "IG_") != nullptr)) {
memfree(dev_list);
return true;
}
}
memfree(dev_list);
return false;
}
bool JoypadWindows::setup_dinput_joypad(const DIDEVICEINSTANCE *instance) {
ERR_FAIL_NULL_V_MSG(dinput, false, "DirectInput not initialized. Rebooting your PC may solve this issue.");
HRESULT hr;
int num = input->get_unused_joy_id();
if (have_device(instance->guidInstance) || num == -1) {
return false;
}
d_joypads[num] = dinput_gamepad();
dinput_gamepad *joy = &d_joypads[num];
const DWORD devtype = (instance->dwDevType & 0xFF);
if ((devtype != DI8DEVTYPE_JOYSTICK) && (devtype != DI8DEVTYPE_GAMEPAD) && (devtype != DI8DEVTYPE_1STPERSON) && (devtype != DI8DEVTYPE_DRIVING)) {
return false;
}
hr = dinput->CreateDevice(instance->guidInstance, &joy->di_joy, nullptr);
if (FAILED(hr)) {
return false;
}
const GUID &guid = instance->guidProduct;
char uid[128];
ERR_FAIL_COND_V_MSG(memcmp(&guid.Data4[2], "PIDVID", 6), false, "DirectInput device not recognized.");
WORD type = BSWAP16(0x03);
WORD vendor = BSWAP16(LOWORD(guid.Data1));
WORD product = BSWAP16(HIWORD(guid.Data1));
WORD version = 0;
sprintf_s(uid, "%04x%04x%04x%04x%04x%04x%04x%04x", type, 0, vendor, 0, product, 0, version, 0);
id_to_change = num;
slider_count = 0;
joy->di_joy->SetDataFormat(&c_dfDIJoystick2);
joy->di_joy->SetCooperativeLevel(*hWnd, DISCL_FOREGROUND);
joy->di_joy->EnumObjects(objectsCallback, this, 0);
joy->joy_axis.sort();
joy->guid = instance->guidInstance;
input->joy_connection_changed(num, true, instance->tszProductName, uid);
joy->attached = true;
joy->id = num;
attached_joypads[num] = true;
joy->confirmed = true;
joypad_count++;
return true;
}
void JoypadWindows::setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_joy_id) {
if (ob->dwType & DIDFT_AXIS) {
HRESULT res;
DIPROPRANGE prop_range;
DIPROPDWORD dilong;
LONG ofs;
if (ob->guidType == GUID_XAxis) {
ofs = DIJOFS_X;
} else if (ob->guidType == GUID_YAxis) {
ofs = DIJOFS_Y;
} else if (ob->guidType == GUID_ZAxis) {
ofs = DIJOFS_Z;
} else if (ob->guidType == GUID_RxAxis) {
ofs = DIJOFS_RX;
} else if (ob->guidType == GUID_RyAxis) {
ofs = DIJOFS_RY;
} else if (ob->guidType == GUID_RzAxis) {
ofs = DIJOFS_RZ;
} else if (ob->guidType == GUID_Slider) {
if (slider_count < 2) {
ofs = DIJOFS_SLIDER(slider_count);
slider_count++;
} else {
return;
}
} else {
return;
}
prop_range.diph.dwSize = sizeof(DIPROPRANGE);
prop_range.diph.dwHeaderSize = sizeof(DIPROPHEADER);
prop_range.diph.dwObj = ob->dwType;
prop_range.diph.dwHow = DIPH_BYID;
prop_range.lMin = -MAX_JOY_AXIS;
prop_range.lMax = +MAX_JOY_AXIS;
dinput_gamepad &joy = d_joypads[p_joy_id];
res = IDirectInputDevice8_SetProperty(joy.di_joy, DIPROP_RANGE, &prop_range.diph);
if (FAILED(res)) {
return;
}
dilong.diph.dwSize = sizeof(dilong);
dilong.diph.dwHeaderSize = sizeof(dilong.diph);
dilong.diph.dwObj = ob->dwType;
dilong.diph.dwHow = DIPH_BYID;
dilong.dwData = 0;
res = IDirectInputDevice8_SetProperty(joy.di_joy, DIPROP_DEADZONE, &dilong.diph);
if (FAILED(res)) {
return;
}
joy.joy_axis.push_back(ofs);
}
}
BOOL CALLBACK JoypadWindows::enumCallback(const DIDEVICEINSTANCE *p_instance, void *p_context) {
JoypadWindows *self = static_cast<JoypadWindows *>(p_context);
if (self->is_xinput_device(&p_instance->guidProduct)) {
return DIENUM_CONTINUE;
}
self->setup_dinput_joypad(p_instance);
return DIENUM_CONTINUE;
}
BOOL CALLBACK JoypadWindows::objectsCallback(const DIDEVICEOBJECTINSTANCE *p_instance, void *p_context) {
JoypadWindows *self = static_cast<JoypadWindows *>(p_context);
self->setup_joypad_object(p_instance, self->id_to_change);
return DIENUM_CONTINUE;
}
void JoypadWindows::close_joypad(int id) {
if (id == -1) {
for (int i = 0; i < JOYPADS_MAX; i++) {
close_joypad(i);
}
return;
}
if (!d_joypads[id].attached) {
return;
}
d_joypads[id].di_joy->Unacquire();
d_joypads[id].di_joy->Release();
d_joypads[id].attached = false;
attached_joypads[d_joypads[id].id] = false;
d_joypads[id].guid.Data1 = d_joypads[id].guid.Data2 = d_joypads[id].guid.Data3 = 0;
input->joy_connection_changed(d_joypads[id].id, false, "");
joypad_count--;
}
void JoypadWindows::probe_joypads() {
ERR_FAIL_NULL_MSG(dinput, "DirectInput not initialized. Rebooting your PC may solve this issue.");
DWORD dwResult;
for (DWORD i = 0; i < XUSER_MAX_COUNT; i++) {
ZeroMemory(&x_joypads[i].state, sizeof(XINPUT_STATE));
dwResult = xinput_get_state(i, &x_joypads[i].state);
if (dwResult == ERROR_SUCCESS) {
int id = input->get_unused_joy_id();
if (id != -1 && !x_joypads[i].attached) {
x_joypads[i].attached = true;
x_joypads[i].id = id;
x_joypads[i].ff_timestamp = 0;
x_joypads[i].ff_end_timestamp = 0;
x_joypads[i].vibrating = false;
attached_joypads[id] = true;
Dictionary joypad_info;
joypad_info["xinput_index"] = (int)i;
input->joy_connection_changed(id, true, "XInput Gamepad", "__XINPUT_DEVICE__", joypad_info);
}
} else if (x_joypads[i].attached) {
x_joypads[i].attached = false;
attached_joypads[x_joypads[i].id] = false;
input->joy_connection_changed(x_joypads[i].id, false, "");
}
}
for (int i = 0; i < joypad_count; i++) {
d_joypads[i].confirmed = false;
}
dinput->EnumDevices(DI8DEVCLASS_GAMECTRL, enumCallback, this, DIEDFL_ATTACHEDONLY);
for (int i = 0; i < joypad_count; i++) {
if (!d_joypads[i].confirmed) {
close_joypad(i);
}
}
}
void JoypadWindows::process_joypads() {
HRESULT hr;
for (int i = 0; i < XUSER_MAX_COUNT; i++) {
xinput_gamepad &joy = x_joypads[i];
if (!joy.attached) {
continue;
}
ZeroMemory(&joy.state, sizeof(XINPUT_STATE));
xinput_get_state(i, &joy.state);
if (joy.state.dwPacketNumber != joy.last_packet) {
int button_mask = XINPUT_GAMEPAD_DPAD_UP;
for (int j = 0; j <= 16; j++) {
input->joy_button(joy.id, (JoyButton)j, joy.state.Gamepad.wButtons & button_mask);
button_mask = button_mask * 2;
}
input->joy_axis(joy.id, JoyAxis::LEFT_X, axis_correct(joy.state.Gamepad.sThumbLX, true));
input->joy_axis(joy.id, JoyAxis::LEFT_Y, axis_correct(joy.state.Gamepad.sThumbLY, true, false, true));
input->joy_axis(joy.id, JoyAxis::RIGHT_X, axis_correct(joy.state.Gamepad.sThumbRX, true));
input->joy_axis(joy.id, JoyAxis::RIGHT_Y, axis_correct(joy.state.Gamepad.sThumbRY, true, false, true));
input->joy_axis(joy.id, JoyAxis::TRIGGER_LEFT, axis_correct(joy.state.Gamepad.bLeftTrigger, true, true));
input->joy_axis(joy.id, JoyAxis::TRIGGER_RIGHT, axis_correct(joy.state.Gamepad.bRightTrigger, true, true));
joy.last_packet = joy.state.dwPacketNumber;
}
uint64_t timestamp = input->get_joy_vibration_timestamp(joy.id);
if (timestamp > joy.ff_timestamp) {
Vector2 strength = input->get_joy_vibration_strength(joy.id);
float duration = input->get_joy_vibration_duration(joy.id);
if (strength.x == 0 && strength.y == 0) {
joypad_vibration_stop_xinput(i, timestamp);
} else {
joypad_vibration_start_xinput(i, strength.x, strength.y, duration, timestamp);
}
} else if (joy.vibrating && joy.ff_end_timestamp != 0) {
uint64_t current_time = OS::get_singleton()->get_ticks_usec();
if (current_time >= joy.ff_end_timestamp) {
joypad_vibration_stop_xinput(i, current_time);
}
}
}
for (int i = 0; i < JOYPADS_MAX; i++) {
dinput_gamepad *joy = &d_joypads[i];
if (!joy->attached) {
continue;
}
DIJOYSTATE2 js;
hr = joy->di_joy->Poll();
if (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED) {
IDirectInputDevice8_Acquire(joy->di_joy);
joy->di_joy->Poll();
}
hr = joy->di_joy->GetDeviceState(sizeof(DIJOYSTATE2), &js);
if (FAILED(hr)) {
continue;
}
post_hat(joy->id, js.rgdwPOV[0]);
for (int j = 0; j < 128; j++) {
if (js.rgbButtons[j] & 0x80) {
if (!joy->last_buttons[j]) {
input->joy_button(joy->id, (JoyButton)j, true);
joy->last_buttons[j] = true;
}
} else {
if (joy->last_buttons[j]) {
input->joy_button(joy->id, (JoyButton)j, false);
joy->last_buttons[j] = false;
}
}
}
// on mingw, these constants are not constants
int count = 8;
const LONG axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ, (LONG)DIJOFS_SLIDER(0), (LONG)DIJOFS_SLIDER(1) };
int values[] = { js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz, js.rglSlider[0], js.rglSlider[1] };
for (uint32_t j = 0; j < joy->joy_axis.size(); j++) {
for (int k = 0; k < count; k++) {
if (joy->joy_axis[j] == axes[k]) {
input->joy_axis(joy->id, (JoyAxis)j, axis_correct(values[k]));
break;
}
}
}
}
return;
}
void JoypadWindows::post_hat(int p_device, DWORD p_dpad) {
BitField<HatMask> dpad_val;
// Should be -1 when centered, but according to docs:
// "Some drivers report the centered position of the POV indicator as 65,535. Determine whether the indicator is centered as follows:
// BOOL POVCentered = (LOWORD(dwPOV) == 0xFFFF);"
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416628(v%3Dvs.85)#remarks
if (LOWORD(p_dpad) == 0xFFFF) {
// Do nothing.
// dpad_val.set_flag(HatMask::CENTER);
}
if (p_dpad == 0) {
dpad_val.set_flag(HatMask::UP);
} else if (p_dpad == 4500) {
dpad_val.set_flag(HatMask::UP);
dpad_val.set_flag(HatMask::RIGHT);
} else if (p_dpad == 9000) {
dpad_val.set_flag(HatMask::RIGHT);
} else if (p_dpad == 13500) {
dpad_val.set_flag(HatMask::RIGHT);
dpad_val.set_flag(HatMask::DOWN);
} else if (p_dpad == 18000) {
dpad_val.set_flag(HatMask::DOWN);
} else if (p_dpad == 22500) {
dpad_val.set_flag(HatMask::DOWN);
dpad_val.set_flag(HatMask::LEFT);
} else if (p_dpad == 27000) {
dpad_val.set_flag(HatMask::LEFT);
} else if (p_dpad == 31500) {
dpad_val.set_flag(HatMask::LEFT);
dpad_val.set_flag(HatMask::UP);
}
input->joy_hat(p_device, dpad_val);
}
float JoypadWindows::axis_correct(int p_val, bool p_xinput, bool p_trigger, bool p_negate) const {
if (Math::abs(p_val) < MIN_JOY_AXIS) {
return p_trigger ? -1.0f : 0.0f;
}
if (!p_xinput) {
return (float)p_val / MAX_JOY_AXIS;
}
if (p_trigger) {
// Convert to a value between -1.0f and 1.0f.
return 2.0f * p_val / MAX_TRIGGER - 1.0f;
}
float value;
if (p_val < 0) {
value = (float)p_val / MAX_JOY_AXIS;
} else {
value = (float)p_val / (MAX_JOY_AXIS - 1);
}
if (p_negate) {
value = -value;
}
return value;
}
void JoypadWindows::joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
xinput_gamepad &joy = x_joypads[p_device];
if (joy.attached) {
XINPUT_VIBRATION effect;
effect.wLeftMotorSpeed = (65535 * p_strong_magnitude);
effect.wRightMotorSpeed = (65535 * p_weak_magnitude);
if (xinput_set_state(p_device, &effect) == ERROR_SUCCESS) {
joy.ff_timestamp = p_timestamp;
joy.ff_end_timestamp = p_duration == 0 ? 0 : p_timestamp + (uint64_t)(p_duration * 1000000.0);
joy.vibrating = true;
}
}
}
void JoypadWindows::joypad_vibration_stop_xinput(int p_device, uint64_t p_timestamp) {
xinput_gamepad &joy = x_joypads[p_device];
if (joy.attached) {
XINPUT_VIBRATION effect;
effect.wLeftMotorSpeed = 0;
effect.wRightMotorSpeed = 0;
if (xinput_set_state(p_device, &effect) == ERROR_SUCCESS) {
joy.ff_timestamp = p_timestamp;
joy.vibrating = false;
}
}
}
void JoypadWindows::load_xinput() {
xinput_get_state = &_xinput_get_state;
xinput_set_state = &_xinput_set_state;
bool legacy_xinput = false;
xinput_dll = LoadLibrary("XInput1_4.dll");
if (!xinput_dll) {
xinput_dll = LoadLibrary("XInput1_3.dll");
if (!xinput_dll) {
xinput_dll = LoadLibrary("XInput9_1_0.dll");
legacy_xinput = true;
}
}
if (!xinput_dll) {
print_verbose("Could not find XInput, using DirectInput only");
return;
}
// (LPCSTR)100 is the magic number to get XInputGetStateEx, which also provides the state for the guide button
LPCSTR get_state_func_name = legacy_xinput ? "XInputGetState" : (LPCSTR)100;
XInputGetState_t func = (XInputGetState_t)GetProcAddress((HMODULE)xinput_dll, get_state_func_name);
XInputSetState_t set_func = (XInputSetState_t)GetProcAddress((HMODULE)xinput_dll, "XInputSetState");
if (!func || !set_func) {
unload_xinput();
return;
}
xinput_get_state = func;
xinput_set_state = set_func;
}
void JoypadWindows::unload_xinput() {
if (xinput_dll) {
FreeLibrary((HMODULE)xinput_dll);
}
}

View file

@ -0,0 +1,143 @@
/**************************************************************************/
/* joypad_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. */
/**************************************************************************/
#ifndef JOYPAD_WINDOWS_H
#define JOYPAD_WINDOWS_H
#include "os_windows.h"
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#include <xinput.h>
#ifndef SAFE_RELEASE // when Windows Media Device M? is not present
#define SAFE_RELEASE(x) \
if (x != nullptr) { \
x->Release(); \
x = nullptr; \
}
#endif
#ifndef XUSER_MAX_COUNT
#define XUSER_MAX_COUNT 4
#endif
class JoypadWindows {
public:
JoypadWindows();
JoypadWindows(HWND *hwnd);
~JoypadWindows();
void probe_joypads();
void process_joypads();
private:
enum {
JOYPADS_MAX = 16,
JOY_AXIS_COUNT = 6,
MIN_JOY_AXIS = 10,
MAX_JOY_AXIS = 32768,
MAX_JOY_BUTTONS = 128,
KEY_EVENT_BUFFER_SIZE = 512,
MAX_TRIGGER = 255
};
struct dinput_gamepad {
int id;
bool attached;
bool confirmed;
bool last_buttons[MAX_JOY_BUTTONS];
DWORD last_pad;
LPDIRECTINPUTDEVICE8 di_joy;
LocalVector<LONG> joy_axis;
GUID guid;
dinput_gamepad() {
id = -1;
last_pad = -1;
attached = false;
confirmed = false;
di_joy = nullptr;
guid = {};
for (int i = 0; i < MAX_JOY_BUTTONS; i++) {
last_buttons[i] = false;
}
}
};
struct xinput_gamepad {
int id = 0;
bool attached = false;
bool vibrating = false;
DWORD last_packet = 0;
XINPUT_STATE state;
uint64_t ff_timestamp = 0;
uint64_t ff_end_timestamp = 0;
};
typedef DWORD(WINAPI *XInputGetState_t)(DWORD dwUserIndex, XINPUT_STATE *pState);
typedef DWORD(WINAPI *XInputSetState_t)(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration);
HWND *hWnd = nullptr;
HANDLE xinput_dll;
LPDIRECTINPUT8 dinput;
Input *input = nullptr;
int id_to_change;
int slider_count;
int joypad_count;
bool attached_joypads[JOYPADS_MAX];
dinput_gamepad d_joypads[JOYPADS_MAX];
xinput_gamepad x_joypads[XUSER_MAX_COUNT];
static BOOL CALLBACK enumCallback(const DIDEVICEINSTANCE *p_instance, void *p_context);
static BOOL CALLBACK objectsCallback(const DIDEVICEOBJECTINSTANCE *instance, void *context);
void setup_joypad_object(const DIDEVICEOBJECTINSTANCE *ob, int p_joy_id);
void close_joypad(int id = -1);
void load_xinput();
void unload_xinput();
void post_hat(int p_device, DWORD p_dpad);
bool have_device(const GUID &p_guid);
bool is_xinput_device(const GUID *p_guid);
bool setup_dinput_joypad(const DIDEVICEINSTANCE *instance);
void joypad_vibration_start_xinput(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
void joypad_vibration_stop_xinput(int p_device, uint64_t p_timestamp);
float axis_correct(int p_val, bool p_xinput = false, bool p_trigger = false, bool p_negate = false) const;
XInputGetState_t xinput_get_state;
XInputSetState_t xinput_set_state;
};
#endif // JOYPAD_WINDOWS_H

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,53 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef KEY_MAPPING_WINDOWS_H
#define KEY_MAPPING_WINDOWS_H
#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);
};
#endif // KEY_MAPPING_WINDOWS_H

View file

@ -0,0 +1,191 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef LANG_TABLE_H
#define LANG_TABLE_H
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 },
};
#endif // LANG_TABLE_H

View file

@ -0,0 +1,20 @@
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):
batch_file = methods.find_visual_c_batch_file(env)
return [
"set &quot;plat=$(PlatformTarget)&quot;",
"(if &quot;$(PlatformTarget)&quot;==&quot;x64&quot; (set &quot;plat=x86_amd64&quot;))",
f"call &quot;{batch_file}&quot; !plat!",
]

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. */
/**************************************************************************/
#ifndef NATIVE_MENU_WINDOWS_H
#define NATIVE_MENU_WINDOWS_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;
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();
};
#endif // NATIVE_MENU_WINDOWS_H

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,246 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef OS_WINDOWS_H
#define OS_WINDOWS_H
#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/unix/ip_unix.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 <stdio.h>
#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
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 JoypadWindows;
class OS_Windows : public OS {
uint64_t ticks_start = 0;
uint64_t ticks_per_second = 0;
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;
// 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() override;
String _quote_command_line_argument(const String &p_text) const;
struct ProcessInfo {
STARTUPINFO 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 Vector<String> get_video_adapter_driver_info() 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 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) 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 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_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 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 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; }
HINSTANCE get_hinstance() { return hInstance; }
OS_Windows(HINSTANCE _hInstance);
~OS_Windows();
};
#endif // OS_WINDOWS_H

View file

@ -0,0 +1,31 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#include <malloc.h>

View file

@ -0,0 +1,53 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef PLATFORM_GL_H
#define PLATFORM_GL_H
#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"
#endif // PLATFORM_GL_H

View file

@ -0,0 +1,24 @@
"""Functions used to generate source files during build time"""
import os
from detect import get_mingw_bin_prefix, try_cmd
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:
mingw_bin_prefix = get_mingw_bin_prefix(env["mingw_prefix"], env["arch"])
if try_cmd("objcopy --version", env["mingw_prefix"], env["arch"]):
os.system(mingw_bin_prefix + "objcopy --only-keep-debug {0} {0}.debugsymbols".format(dst))
else:
os.system("objcopy --only-keep-debug {0} {0}.debugsymbols".format(dst))
if try_cmd("strip --version", env["mingw_prefix"], env["arch"]):
os.system(mingw_bin_prefix + "strip --strip-debug --strip-unneeded {0}".format(dst))
else:
os.system("strip --strip-debug --strip-unneeded {0}".format(dst))
if try_cmd("objcopy --version", env["mingw_prefix"], env["arch"]):
os.system(mingw_bin_prefix + "objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(dst))
else:
os.system("objcopy --add-gnu-debuglink={0}.debugsymbols {0}".format(dst))

View file

@ -0,0 +1,75 @@
/**************************************************************************/
/* 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"
#ifdef USE_VOLK
#include <volk.h>
#else
#include <vulkan/vulkan.h>
#endif
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, nullptr, &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,60 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef RENDERING_CONTEXT_DRIVER_VULKAN_WINDOWS_H
#define RENDERING_CONTEXT_DRIVER_VULKAN_WINDOWS_H
#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
#endif // RENDERING_CONTEXT_DRIVER_VULKAN_WINDOWS_H

View file

@ -0,0 +1,270 @@
/**************************************************************************/
/* 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_tts();
} 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::_update_tts() {
if (!is_speaking() && !paused && queue.size() > 0) {
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 * log10(message.rate) / log10(3.f));
synth->Speak((LPCWSTR)ut.string.get_data(), flags, &stream_number);
ids[(uint32_t)stream_number] = ut;
queue.pop_front();
}
}
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_tts();
}
}
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,80 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef TTS_WINDOWS_H
#define TTS_WINDOWS_H
#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 <wchar.h>
#include <winnls.h>
#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;
static void __stdcall speech_event_callback(WPARAM wParam, LPARAM lParam);
void _update_tts();
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();
TTS_Windows();
~TTS_Windows();
};
#endif // TTS_WINDOWS_H

View file

@ -0,0 +1,197 @@
/**************************************************************************/
/* 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 <stdio.h>
#include <stdlib.h>
#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
#if defined(__GNUC__)
// Workaround GCC warning from -Wcast-function-type.
#define GetProcAddress (void *)GetProcAddress
#endif
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)GetProcAddress(module, "wglCreateContext");
gd_wglMakeCurrent = (PFNWGLMAKECURRENT)GetProcAddress(module, "wglMakeCurrent");
gd_wglDeleteContext = (PFNWGLDELETECONTEXT)GetProcAddress(module, "wglDeleteContext");
gd_wglGetProcAddress = (PFNWGLGETPROCADDRESS)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)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,42 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef WGL_DETECT_VERSION_H
#define WGL_DETECT_VERSION_H
#if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED)
class Dictionary;
Dictionary detect_wgl();
#endif // WINDOWS_ENABLED && GLES3_ENABLED
#endif // WGL_DETECT_VERSION_H

View file

@ -0,0 +1,159 @@
/**************************************************************************/
/* 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"
#ifdef WINDOWS_ENABLED
#include <stdio.h>
#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) {
if (!should_log(true)) {
return;
}
HANDLE hCon = GetStdHandle(STD_OUTPUT_HANDLE);
if (!hCon || hCon == INVALID_HANDLE_VALUE) {
StdLogger::log_error(p_function, p_file, p_line, p_code, p_rationale, p_type);
} 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_ERROR:
basecol = FOREGROUND_RED;
break;
case ERR_WARNING:
basecol = FOREGROUND_RED | FOREGROUND_GREEN;
break;
case ERR_SCRIPT:
basecol = FOREGROUND_RED | FOREGROUND_BLUE;
break;
case ERR_SHADER:
basecol = FOREGROUND_GREEN | FOREGROUND_BLUE;
break;
}
basecol |= current_bg;
SetConsoleTextAttribute(hCon, basecol | FOREGROUND_INTENSITY);
switch (p_type) {
case ERR_ERROR:
logf_error("ERROR:");
break;
case ERR_WARNING:
logf_error("WARNING:");
break;
case ERR_SCRIPT:
logf_error("SCRIPT ERROR:");
break;
case ERR_SHADER:
logf_error("SHADER ERROR:");
break;
}
SetConsoleTextAttribute(hCon, basecol);
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);
switch (p_type) {
case ERR_ERROR:
logf_error(" at: ");
break;
case ERR_WARNING:
logf_error(" at: ");
break;
case ERR_SCRIPT:
logf_error(" at: ");
break;
case ERR_SHADER:
logf_error(" at: ");
break;
}
if (p_rationale && p_rationale[0]) {
logf_error("(%s:%i)\n", p_file, p_line);
} else {
logf_error("%s (%s:%i)\n", p_function, p_file, p_line);
}
SetConsoleTextAttribute(hCon, sbi.wAttributes);
}
}
WindowsTerminalLogger::~WindowsTerminalLogger() {}
#endif // WINDOWS_ENABLED

View file

@ -0,0 +1,47 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef WINDOWS_TERMINAL_LOGGER_H
#define WINDOWS_TERMINAL_LOGGER_H
#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) override;
virtual ~WindowsTerminalLogger();
};
#endif // WINDOWS_ENABLED
#endif // WINDOWS_TERMINAL_LOGGER_H

View file

@ -0,0 +1,280 @@
/**************************************************************************/
/* 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()), NULL, 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, NULL);
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.parse_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());
}
ERR_FAIL_COND_V_MSG(!FileAccess::exists(copy_pdb_path), FAILED, vformat("File '%s' does not exist.", copy_pdb_path));
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));
utf8_name.resize(original_path_size - suffix_size + 1); // +1 for the \0
utf8_name[utf8_name.size() - 1] = '\0';
new_pdb_base_name.parse_utf8(utf8_name);
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'.
// Can be replaced by file->store_8('\0');
Vector<uint8_t> padding_buffer;
padding_buffer.resize((int64_t)original_path_size - u8.size());
padding_buffer.fill('\0');
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,49 @@
/**************************************************************************/
/* 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. */
/**************************************************************************/
#ifndef WINDOWS_UTILS_H
#define WINDOWS_UTILS_H
#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
#endif // WINDOWS_UTILS_H