feat: godot-engine-source-4.3-stable
This commit is contained in:
parent
c59a7dcade
commit
7125d019b5
11149 changed files with 5070401 additions and 0 deletions
15
engine/platform/windows/README.md
Normal file
15
engine/platform/windows/README.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Windows platform port
|
||||
|
||||
This folder contains the C++ code for the Windows platform port.
|
||||
|
||||
See also [`misc/dist/windows`](/misc/dist/windows) folder for additional files
|
||||
used by this platform.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Compiling for Windows](https://docs.godotengine.org/en/latest/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.
|
||||
138
engine/platform/windows/SCsub
Normal file
138
engine/platform/windows/SCsub
Normal 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
|
||||
185
engine/platform/windows/console_wrapper_windows.cpp
Normal file
185
engine/platform/windows/console_wrapper_windows.cpp
Normal 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);
|
||||
}
|
||||
60
engine/platform/windows/crash_handler_windows.h
Normal file
60
engine/platform/windows/crash_handler_windows.h
Normal 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
|
||||
249
engine/platform/windows/crash_handler_windows_seh.cpp
Normal file
249
engine/platform/windows/crash_handler_windows_seh.cpp
Normal 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() {
|
||||
}
|
||||
205
engine/platform/windows/crash_handler_windows_signal.cpp
Normal file
205
engine/platform/windows/crash_handler_windows_signal.cpp
Normal 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
|
||||
}
|
||||
864
engine/platform/windows/detect.py
Normal file
864
engine/platform/windows/detect.py
Normal 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)
|
||||
6327
engine/platform/windows/display_server_windows.cpp
Normal file
6327
engine/platform/windows/display_server_windows.cpp
Normal file
File diff suppressed because it is too large
Load diff
785
engine/platform/windows/display_server_windows.h
Normal file
785
engine/platform/windows/display_server_windows.h
Normal 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
|
||||
|
|
@ -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>
|
||||
63
engine/platform/windows/export/export.cpp
Normal file
63
engine/platform/windows/export/export.cpp
Normal 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);
|
||||
}
|
||||
37
engine/platform/windows/export/export.h
Normal file
37
engine/platform/windows/export/export.h
Normal 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
|
||||
1106
engine/platform/windows/export/export_plugin.cpp
Normal file
1106
engine/platform/windows/export/export_plugin.cpp
Normal file
File diff suppressed because it is too large
Load diff
102
engine/platform/windows/export/export_plugin.h
Normal file
102
engine/platform/windows/export/export_plugin.h
Normal 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
|
||||
1
engine/platform/windows/export/logo.svg
Normal file
1
engine/platform/windows/export/logo.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path fill="#00abed" d="m1 5.132 12.295-1.694v11.879H1zm0 21.736 12.295 1.695V16.83H1zm13.647 1.875L31 31V16.83H14.647zm0-25.486v12.06H31V1z"/></svg>
|
||||
|
After Width: | Height: | Size: 212 B |
1
engine/platform/windows/export/run_icon.svg
Normal file
1
engine/platform/windows/export/run_icon.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16"><path fill="#e0e0e0" d="m1.095 2.997 5.66-.78v5.469h-5.66zm0 10.006 5.66.78v-5.4h-5.66zm6.282.863 7.528 1.04V8.381H7.377Zm0-11.732v5.552h7.528V1.095Z"/></svg>
|
||||
|
After Width: | Height: | Size: 242 B |
75
engine/platform/windows/gl_manager_windows_angle.cpp
Normal file
75
engine/platform/windows/gl_manager_windows_angle.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/**************************************************************************/
|
||||
/* gl_manager_windows_angle.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "gl_manager_windows_angle.h"
|
||||
|
||||
#if defined(WINDOWS_ENABLED) && defined(GLES3_ENABLED)
|
||||
|
||||
#include <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
|
||||
61
engine/platform/windows/gl_manager_windows_angle.h
Normal file
61
engine/platform/windows/gl_manager_windows_angle.h
Normal 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
|
||||
557
engine/platform/windows/gl_manager_windows_native.cpp
Normal file
557
engine/platform/windows/gl_manager_windows_native.cpp
Normal 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
|
||||
109
engine/platform/windows/gl_manager_windows_native.h
Normal file
109
engine/platform/windows/gl_manager_windows_native.h
Normal 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
|
||||
BIN
engine/platform/windows/godot.ico
Normal file
BIN
engine/platform/windows/godot.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 139 KiB |
303
engine/platform/windows/godot.natvis
Normal file
303
engine/platform/windows/godot.natvis
Normal 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<*>">
|
||||
<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<*>">
|
||||
<Expand>
|
||||
<Item Name="[size]"> _p->array._cowdata._ptr ? (((const unsigned long long *)(_p->array._cowdata._ptr))[-1]) : 0</Item>
|
||||
<ArrayItems>
|
||||
<Size>_p->array._cowdata._ptr ? (((const unsigned long long *)(_p->array._cowdata._ptr))[-1]) : 0</Size>
|
||||
<ValuePointer >(Variant *) _p->array._cowdata._ptr</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Dictionary">
|
||||
<Expand>
|
||||
<Item Name="[size]">_p && _p->variant_map.head_element ? _p->variant_map.num_elements : 0</Item>
|
||||
<LinkedListItems>
|
||||
<Size>_p && _p->variant_map.head_element ? _p->variant_map.num_elements : 0</Size>
|
||||
<HeadPointer>_p ? _p->variant_map.head_element : nullptr</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode Name="[{data.key}]">(*this),view(MapHelper)</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="LocalVector<*>">
|
||||
<Expand>
|
||||
<Item Name="[size]">count</Item>
|
||||
<ArrayItems>
|
||||
<Size>count</Size>
|
||||
<ValuePointer>data</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="List<*>">
|
||||
<Expand>
|
||||
<Item Name="[size]">_data ? (_data->size_cache) : 0</Item>
|
||||
<LinkedListItems>
|
||||
<Size>_data ? (_data->size_cache) : 0</Size>
|
||||
<HeadPointer>_data->first</HeadPointer>
|
||||
<NextPointer>next_ptr</NextPointer>
|
||||
<ValueNode>value</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="NodePath">
|
||||
<DisplayString Condition="!data">[empty]</DisplayString>
|
||||
<DisplayString Condition="!!data">{{[absolute] = {data->absolute} [path] = {data->path,view(NodePathHelper)} [subpath] = {data->subpath,view(NodePathHelper)}}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[path]">data->path,view(NodePathHelper)</Item>
|
||||
<Item Name="[subpath]">data->subpath,view(NodePathHelper)</Item>
|
||||
<Item Name="[absolute]">data->absolute</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Vector<StringName>" IncludeView="NodePathHelper">
|
||||
<Expand>
|
||||
<ArrayItems>
|
||||
<Size>_cowdata._ptr ? (((const unsigned long long *)(_cowdata._ptr))[-1]) : 0</Size>
|
||||
<ValuePointer>((StringName *)_cowdata._ptr),view(NodePathHelper)</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="StringName" IncludeView="NodePathHelper">
|
||||
<DisplayString Condition="_data && _data->cname">{_data->cname,s8b}</DisplayString>
|
||||
<DisplayString Condition="_data && !_data->cname">{_data->name,s32b}</DisplayString>
|
||||
<DisplayString Condition="!_data">[empty]</DisplayString>
|
||||
<StringView Condition="_data && _data->cname">_data->cname,s8b</StringView>
|
||||
<StringView Condition="_data && !_data->cname">_data->name,s32b</StringView>
|
||||
</Type>
|
||||
|
||||
<Type Name="HashMapElement<*,*>">
|
||||
<DisplayString>{{Key = {($T1 *) &data.key} Value = {($T2 *) &data.value}}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[key]">($T1 *) &data.key</Item>
|
||||
<Item Name="[value]">($T2 *) &data.value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- elements displayed by index -->
|
||||
<Type Name="HashMap<*,*,*,*,*>" Priority="Medium">
|
||||
<Expand>
|
||||
<Item Name="[size]">head_element ? num_elements : 0</Item>
|
||||
<LinkedListItems>
|
||||
<Size>head_element ? num_elements : 0</Size>
|
||||
<HeadPointer>head_element</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>(*this)</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- elements by key:value -->
|
||||
<!-- show elements by index by specifying "ShowElementsByIndex"-->
|
||||
<Type Name="HashMap<*,*,*,*,*>" ExcludeView="ShowElementsByIndex" Priority="MediumHigh">
|
||||
<Expand>
|
||||
<Item Name="[size]">head_element ? num_elements : 0</Item>
|
||||
<LinkedListItems>
|
||||
<Size>head_element ? num_elements : 0</Size>
|
||||
<HeadPointer>head_element</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode Name="[{data.key}]">(*this),view(MapHelper)</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="KeyValue<*,*>" IncludeView="MapHelper">
|
||||
<DisplayString>{value}</DisplayString>
|
||||
</Type>
|
||||
|
||||
<Type Name="HashMapElement<*,*>" IncludeView="MapHelper">
|
||||
<DisplayString>{data.value}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[key]" >($T1 *) &data.key</Item>
|
||||
<Item Name="[value]">($T2 *) &data.value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="VMap<*,*>">
|
||||
<Expand>
|
||||
<Item Condition="_cowdata._ptr" Name="[size]">*(reinterpret_cast<long long*>(_cowdata._ptr) - 1)</Item>
|
||||
<ArrayItems Condition="_cowdata._ptr">
|
||||
<Size>*(reinterpret_cast<long long*>(_cowdata._ptr) - 1)</Size>
|
||||
<ValuePointer>reinterpret_cast<VMap<$T1,$T2>::Pair*>(_cowdata._ptr)</ValuePointer>
|
||||
</ArrayItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="VMap<Callable,*>::Pair">
|
||||
<DisplayString Condition="dynamic_cast<CallableCustomMethodPointerBase*>(key.custom)">{dynamic_cast<CallableCustomMethodPointerBase*>(key.custom)->text}</DisplayString>
|
||||
</Type>
|
||||
|
||||
|
||||
<Type Name="Variant">
|
||||
<DisplayString Condition="type == Variant::NIL">nil</DisplayString>
|
||||
<DisplayString Condition="type == Variant::BOOL">{_data._bool}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::INT">{_data._int}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::FLOAT">{_data._float}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::TRANSFORM2D">{_data._transform2d}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::AABB">{_data._aabb}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::BASIS">{_data._basis}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::TRANSFORM3D">{_data._transform3d}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PROJECTION">{_data._projection}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::STRING">{*(String *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::VECTOR2">{*(Vector2 *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::RECT2">{*(Rect2 *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::VECTOR3">{*(Vector3 *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::VECTOR4">{*(Vector4 *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PLANE">{*(Plane *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::QUATERNION">{*(Quaternion *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::COLOR">{*(Color *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::STRING_NAME">{*(StringName *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::NODE_PATH">{*(NodePath *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::RID">{*(::RID *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::OBJECT">{*(*reinterpret_cast<ObjData*>(&_data._mem[0])).obj}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::DICTIONARY">{*(Dictionary *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::ARRAY">{*(Array *)_data._mem}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_BYTE_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<unsigned char>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_INT32_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<int>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_INT64_ARRAY">{*reinterpret_cast<PackedInt64Array *>(&_data.packed_array[1])}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_FLOAT32_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<float>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_FLOAT64_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<double>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_STRING_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<String>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_VECTOR2_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<Vector2>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_VECTOR3_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<Vector3>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_COLOR_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<Color>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type == Variant::PACKED_VECTOR4_ARRAY">{reinterpret_cast<const Variant::PackedArrayRef<Vector4>*>(_data.packed_array)->array}</DisplayString>
|
||||
<DisplayString Condition="type < 0 || type >= Variant::VARIANT_MAX">[INVALID]</DisplayString>
|
||||
|
||||
<StringView Condition="type == Variant::STRING && ((String *)(_data._mem))->_cowdata._ptr">((String *)(_data._mem))->_cowdata._ptr,s32</StringView>
|
||||
|
||||
<Expand>
|
||||
<Item Name="[value]" Condition="type == Variant::BOOL">_data._bool</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::INT">_data._int</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::FLOAT">_data._float</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::TRANSFORM2D">_data._transform2d</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::AABB">_data._aabb</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::BASIS">_data._basis</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::TRANSFORM3D">_data._transform3d</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::STRING">*(String *)_data._mem</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::VECTOR2">*(Vector2 *)_data._mem</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::RECT2">*(Rect2 *)_data._mem</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::VECTOR3">*(Vector3 *)_data._mem</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PLANE">*(Plane *)_data._mem</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::QUATERNION">*(Quaternion *)_data._mem</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::COLOR">*(Color *)_data._mem</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::STRING_NAME">*(StringName *)_data._mem</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::NODE_PATH">*(NodePath *)_data._mem</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::RID">*(::RID *)_data._mem</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::OBJECT">*(*reinterpret_cast<ObjData*>(&_data._mem[0])).obj</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::DICTIONARY">*(Dictionary *)_data._mem</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::ARRAY">*(Array *)_data._mem</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_BYTE_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<unsigned char>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_INT32_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<int>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_INT64_ARRAY">*reinterpret_cast<PackedInt64Array *>(&_data.packed_array[1])</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_FLOAT32_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<float>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_FLOAT64_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<double>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_STRING_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<String>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_VECTOR2_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<Vector2>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_VECTOR3_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<Vector3>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_COLOR_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<Color>*>(_data.packed_array)->array</Item>
|
||||
<Item Name="[value]" Condition="type == Variant::PACKED_VECTOR4_ARRAY">reinterpret_cast<const Variant::PackedArrayRef<Vector4>*>(_data.packed_array)->array</Item>
|
||||
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="String">
|
||||
<DisplayString Condition="_cowdata._ptr == 0">[empty]</DisplayString>
|
||||
<DisplayString Condition="_cowdata._ptr != 0">{_cowdata._ptr,s32}</DisplayString>
|
||||
<StringView Condition="_cowdata._ptr != 0">_cowdata._ptr,s32</StringView>
|
||||
</Type>
|
||||
|
||||
<Type Name="godot::String">
|
||||
<DisplayString>{*reinterpret_cast<void**>(opaque),s32}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="opaque_ptr">*reinterpret_cast<void**>(opaque)</Item>
|
||||
<Item Name="string">*reinterpret_cast<void**>(opaque),s32</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="StringName">
|
||||
<DisplayString Condition="_data && _data->cname">{_data->cname,na}</DisplayString>
|
||||
<DisplayString Condition="_data && !_data->cname">{_data->name,s32}</DisplayString>
|
||||
<DisplayString Condition="!_data">[empty]</DisplayString>
|
||||
<StringView Condition="_data && _data->cname">_data->cname,na</StringView>
|
||||
<StringView Condition="_data && !_data->cname">_data->name,s32</StringView>
|
||||
</Type>
|
||||
|
||||
<!-- can't cast the opaque to ::StringName because Natvis does not support global namespace specifier? -->
|
||||
<Type Name="godot::StringName">
|
||||
<DisplayString Condition="(*reinterpret_cast<const char***>(opaque))[1]">{(*reinterpret_cast<const char***>(opaque))[1],s8}</DisplayString>
|
||||
<DisplayString Condition="!(*reinterpret_cast<const char***>(opaque))[1]">{(*reinterpret_cast<const char***>(opaque))[2],s32}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="opaque_ptr">*reinterpret_cast<void**>(opaque)</Item>
|
||||
<Item Name="&cname">(*reinterpret_cast<const char***>(opaque))+1</Item>
|
||||
<Item Name="cname">(*reinterpret_cast<const char***>(opaque))[1],s8</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="Object::SignalData">
|
||||
<DisplayString Condition="user.name._cowdata._ptr">"{user.name}" {slot_map}</DisplayString>
|
||||
<DisplayString Condition="!user.name._cowdata._ptr">"{slot_map}</DisplayString>
|
||||
</Type>
|
||||
|
||||
<Type Name="Vector2">
|
||||
<DisplayString>{{{x},{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>
|
||||
BIN
engine/platform/windows/godot_console.ico
Normal file
BIN
engine/platform/windows/godot_console.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 137 KiB |
34
engine/platform/windows/godot_res.rc
Normal file
34
engine/platform/windows/godot_res.rc
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#include "core/version.h"
|
||||
|
||||
#ifndef RT_MANIFEST
|
||||
#define RT_MANIFEST 24
|
||||
#endif
|
||||
|
||||
GODOT_ICON ICON platform/windows/godot.ico
|
||||
1 RT_MANIFEST "godot.manifest"
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION 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
|
||||
29
engine/platform/windows/godot_res_wrap.rc
Normal file
29
engine/platform/windows/godot_res_wrap.rc
Normal 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
|
||||
234
engine/platform/windows/godot_windows.cpp
Normal file
234
engine/platform/windows/godot_windows.cpp
Normal 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);
|
||||
}
|
||||
568
engine/platform/windows/joypad_windows.cpp
Normal file
568
engine/platform/windows/joypad_windows.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
143
engine/platform/windows/joypad_windows.h
Normal file
143
engine/platform/windows/joypad_windows.h
Normal 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
|
||||
449
engine/platform/windows/key_mapping_windows.cpp
Normal file
449
engine/platform/windows/key_mapping_windows.cpp
Normal file
|
|
@ -0,0 +1,449 @@
|
|||
/**************************************************************************/
|
||||
/* key_mapping_windows.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "key_mapping_windows.h"
|
||||
|
||||
#include "core/templates/hash_map.h"
|
||||
|
||||
// This provides translation from Windows virtual key codes to Godot and back.
|
||||
// See WinUser.h and the below for documentation:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
|
||||
|
||||
struct HashMapHasherKeys {
|
||||
static _FORCE_INLINE_ uint32_t hash(const Key p_key) { return hash_fmix32(static_cast<uint32_t>(p_key)); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(p_uchar); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const unsigned p_key) { return hash_fmix32(p_key); }
|
||||
};
|
||||
|
||||
HashMap<unsigned int, Key, HashMapHasherKeys> vk_map;
|
||||
HashMap<unsigned int, Key, HashMapHasherKeys> scansym_map;
|
||||
HashMap<Key, unsigned int, HashMapHasherKeys> scansym_map_inv;
|
||||
HashMap<unsigned int, Key, HashMapHasherKeys> scansym_map_ext;
|
||||
HashMap<unsigned int, KeyLocation, HashMapHasherKeys> location_map;
|
||||
|
||||
void KeyMappingWindows::initialize() {
|
||||
// VK_LBUTTON (0x01)
|
||||
// VK_RBUTTON (0x02)
|
||||
// VK_CANCEL (0x03)
|
||||
// VK_MBUTTON (0x04)
|
||||
// VK_XBUTTON1 (0x05)
|
||||
// VK_XBUTTON2 (0x06), We have no mappings for the above;as we only map keyboard buttons here.
|
||||
// 0x07 is undefined.
|
||||
vk_map[VK_BACK] = Key::BACKSPACE; // (0x08)
|
||||
vk_map[VK_TAB] = Key::TAB; // (0x09)
|
||||
// 0x0A-0B are reserved.
|
||||
vk_map[VK_CLEAR] = Key::CLEAR; // (0x0C)
|
||||
vk_map[VK_RETURN] = Key::ENTER; // (0x0D)
|
||||
// 0x0E-0F are undefined.
|
||||
vk_map[VK_SHIFT] = Key::SHIFT; // (0x10)
|
||||
vk_map[VK_CONTROL] = Key::CTRL; // (0x11)
|
||||
vk_map[VK_MENU] = Key::ALT; // (0x12)
|
||||
vk_map[VK_PAUSE] = Key::PAUSE; // (0x13)
|
||||
vk_map[VK_CAPITAL] = Key::CAPSLOCK; // (0x14)
|
||||
// 0x15-1A are IME keys.
|
||||
vk_map[VK_ESCAPE] = Key::ESCAPE; // (0x1B)
|
||||
// 0x1C-1F are IME keys.
|
||||
vk_map[VK_SPACE] = Key::SPACE; // (0x20)
|
||||
vk_map[VK_PRIOR] = Key::PAGEUP; // (0x21)
|
||||
vk_map[VK_NEXT] = Key::PAGEDOWN; // (0x22)
|
||||
vk_map[VK_END] = Key::END; // (0x23)
|
||||
vk_map[VK_HOME] = Key::HOME; // (0x24)
|
||||
vk_map[VK_LEFT] = Key::LEFT; // (0x25)
|
||||
vk_map[VK_UP] = Key::UP; // (0x26)
|
||||
vk_map[VK_RIGHT] = Key::RIGHT; // (0x27)
|
||||
vk_map[VK_DOWN] = Key::DOWN; // (0x28)
|
||||
// VK_SELECT (0x29), Old select key; e.g. on Digital Equipment Corporation keyboards.
|
||||
vk_map[VK_PRINT] = Key::PRINT; // (0x2A), Old IBM key; modern keyboards use VK_SNAPSHOT.
|
||||
// VK_EXECUTE (0x2B), Old and uncommon.
|
||||
vk_map[VK_SNAPSHOT] = Key::PRINT; // (0x2C)
|
||||
vk_map[VK_INSERT] = Key::INSERT; // (0x2D)
|
||||
vk_map[VK_DELETE] = Key::KEY_DELETE; // (0x2E)
|
||||
vk_map[VK_HELP] = Key::HELP; // (0x2F)
|
||||
vk_map[0x30] = Key::KEY_0; // 0 key.
|
||||
vk_map[0x31] = Key::KEY_1; // 1 key.
|
||||
vk_map[0x32] = Key::KEY_2; // 2 key.
|
||||
vk_map[0x33] = Key::KEY_3; // 3 key.
|
||||
vk_map[0x34] = Key::KEY_4; // 4 key.
|
||||
vk_map[0x35] = Key::KEY_5; // 5 key.
|
||||
vk_map[0x36] = Key::KEY_6; // 6 key.
|
||||
vk_map[0x37] = Key::KEY_7; // 7 key.
|
||||
vk_map[0x38] = Key::KEY_8; // 8 key.
|
||||
vk_map[0x39] = Key::KEY_9; // 9 key.
|
||||
// 0x3A-40 are undefined.
|
||||
vk_map[0x41] = Key::A; // A key.
|
||||
vk_map[0x42] = Key::B; // B key.
|
||||
vk_map[0x43] = Key::C; // C key.
|
||||
vk_map[0x44] = Key::D; // D key.
|
||||
vk_map[0x45] = Key::E; // E key.
|
||||
vk_map[0x46] = Key::F; // F key.
|
||||
vk_map[0x47] = Key::G; // G key.
|
||||
vk_map[0x48] = Key::H; // H key.
|
||||
vk_map[0x49] = Key::I; // I key
|
||||
vk_map[0x4A] = Key::J; // J key.
|
||||
vk_map[0x4B] = Key::K; // K key.
|
||||
vk_map[0x4C] = Key::L; // L key.
|
||||
vk_map[0x4D] = Key::M; // M key.
|
||||
vk_map[0x4E] = Key::N; // N key.
|
||||
vk_map[0x4F] = Key::O; // O key.
|
||||
vk_map[0x50] = Key::P; // P key.
|
||||
vk_map[0x51] = Key::Q; // Q key.
|
||||
vk_map[0x52] = Key::R; // R key.
|
||||
vk_map[0x53] = Key::S; // S key.
|
||||
vk_map[0x54] = Key::T; // T key.
|
||||
vk_map[0x55] = Key::U; // U key.
|
||||
vk_map[0x56] = Key::V; // V key.
|
||||
vk_map[0x57] = Key::W; // W key.
|
||||
vk_map[0x58] = Key::X; // X key.
|
||||
vk_map[0x59] = Key::Y; // Y key.
|
||||
vk_map[0x5A] = Key::Z; // Z key.
|
||||
vk_map[VK_LWIN] = (Key)Key::META; // (0x5B)
|
||||
vk_map[VK_RWIN] = (Key)Key::META; // (0x5C)
|
||||
vk_map[VK_APPS] = Key::MENU; // (0x5D)
|
||||
// 0x5E is reserved.
|
||||
vk_map[VK_SLEEP] = Key::STANDBY; // (0x5F)
|
||||
vk_map[VK_NUMPAD0] = Key::KP_0; // (0x60)
|
||||
vk_map[VK_NUMPAD1] = Key::KP_1; // (0x61)
|
||||
vk_map[VK_NUMPAD2] = Key::KP_2; // (0x62)
|
||||
vk_map[VK_NUMPAD3] = Key::KP_3; // (0x63)
|
||||
vk_map[VK_NUMPAD4] = Key::KP_4; // (0x64)
|
||||
vk_map[VK_NUMPAD5] = Key::KP_5; // (0x65)
|
||||
vk_map[VK_NUMPAD6] = Key::KP_6; // (0x66)
|
||||
vk_map[VK_NUMPAD7] = Key::KP_7; // (0x67)
|
||||
vk_map[VK_NUMPAD8] = Key::KP_8; // (0x68)
|
||||
vk_map[VK_NUMPAD9] = Key::KP_9; // (0x69)
|
||||
vk_map[VK_MULTIPLY] = Key::KP_MULTIPLY; // (0x6A)
|
||||
vk_map[VK_ADD] = Key::KP_ADD; // (0x6B)
|
||||
vk_map[VK_SEPARATOR] = Key::KP_PERIOD; // (0x6C)
|
||||
vk_map[VK_SUBTRACT] = Key::KP_SUBTRACT; // (0x6D)
|
||||
vk_map[VK_DECIMAL] = Key::KP_PERIOD; // (0x6E)
|
||||
vk_map[VK_DIVIDE] = Key::KP_DIVIDE; // (0x6F)
|
||||
vk_map[VK_F1] = Key::F1; // (0x70)
|
||||
vk_map[VK_F2] = Key::F2; // (0x71)
|
||||
vk_map[VK_F3] = Key::F3; // (0x72)
|
||||
vk_map[VK_F4] = Key::F4; // (0x73)
|
||||
vk_map[VK_F5] = Key::F5; // (0x74)
|
||||
vk_map[VK_F6] = Key::F6; // (0x75)
|
||||
vk_map[VK_F7] = Key::F7; // (0x76)
|
||||
vk_map[VK_F8] = Key::F8; // (0x77)
|
||||
vk_map[VK_F9] = Key::F9; // (0x78)
|
||||
vk_map[VK_F10] = Key::F10; // (0x79)
|
||||
vk_map[VK_F11] = Key::F11; // (0x7A)
|
||||
vk_map[VK_F12] = Key::F12; // (0x7B)
|
||||
vk_map[VK_F13] = Key::F13; // (0x7C)
|
||||
vk_map[VK_F14] = Key::F14; // (0x7D)
|
||||
vk_map[VK_F15] = Key::F15; // (0x7E)
|
||||
vk_map[VK_F16] = Key::F16; // (0x7F)
|
||||
vk_map[VK_F17] = Key::F17; // (0x80)
|
||||
vk_map[VK_F18] = Key::F18; // (0x81)
|
||||
vk_map[VK_F19] = Key::F19; // (0x82)
|
||||
vk_map[VK_F20] = Key::F20; // (0x83)
|
||||
vk_map[VK_F21] = Key::F21; // (0x84)
|
||||
vk_map[VK_F22] = Key::F22; // (0x85)
|
||||
vk_map[VK_F23] = Key::F23; // (0x86)
|
||||
vk_map[VK_F24] = Key::F24; // (0x87)
|
||||
// 0x88-8F are reserved for UI navigation.
|
||||
vk_map[VK_NUMLOCK] = Key::NUMLOCK; // (0x90)
|
||||
vk_map[VK_SCROLL] = Key::SCROLLLOCK; // (0x91)
|
||||
vk_map[VK_OEM_NEC_EQUAL] = Key::EQUAL; // (0x92), OEM NEC PC-9800 numpad '=' key.
|
||||
// 0x93-96 are OEM specific (e.g. used by Fujitsu/OASYS);
|
||||
// 0x97-9F are unassigned.
|
||||
vk_map[VK_LSHIFT] = Key::SHIFT; // (0xA0)
|
||||
vk_map[VK_RSHIFT] = Key::SHIFT; // (0xA1)
|
||||
vk_map[VK_LCONTROL] = Key::CTRL; // (0xA2)
|
||||
vk_map[VK_RCONTROL] = Key::CTRL; // (0xA3)
|
||||
vk_map[VK_LMENU] = Key::MENU; // (0xA4)
|
||||
vk_map[VK_RMENU] = Key::MENU; // (0xA5)
|
||||
vk_map[VK_BROWSER_BACK] = Key::BACK; // (0xA6)
|
||||
vk_map[VK_BROWSER_FORWARD] = Key::FORWARD; // (0xA7)
|
||||
vk_map[VK_BROWSER_REFRESH] = Key::REFRESH; // (0xA8)
|
||||
vk_map[VK_BROWSER_STOP] = Key::STOP; // (0xA9)
|
||||
vk_map[VK_BROWSER_SEARCH] = Key::SEARCH; // (0xAA)
|
||||
vk_map[VK_BROWSER_FAVORITES] = Key::FAVORITES; // (0xAB)
|
||||
vk_map[VK_BROWSER_HOME] = Key::HOMEPAGE; // (0xAC)
|
||||
vk_map[VK_VOLUME_MUTE] = Key::VOLUMEMUTE; // (0xAD)
|
||||
vk_map[VK_VOLUME_DOWN] = Key::VOLUMEDOWN; // (0xAE)
|
||||
vk_map[VK_VOLUME_UP] = Key::VOLUMEUP; // (0xAF)
|
||||
vk_map[VK_MEDIA_NEXT_TRACK] = Key::MEDIANEXT; // (0xB0)
|
||||
vk_map[VK_MEDIA_PREV_TRACK] = Key::MEDIAPREVIOUS; // (0xB1)
|
||||
vk_map[VK_MEDIA_STOP] = Key::MEDIASTOP; // (0xB2)
|
||||
vk_map[VK_MEDIA_PLAY_PAUSE] = Key::MEDIAPLAY; // (0xB3), Media button play/pause toggle.
|
||||
vk_map[VK_LAUNCH_MAIL] = Key::LAUNCHMAIL; // (0xB4)
|
||||
vk_map[VK_LAUNCH_MEDIA_SELECT] = Key::LAUNCHMEDIA; // (0xB5)
|
||||
vk_map[VK_LAUNCH_APP1] = Key::LAUNCH0; // (0xB6)
|
||||
vk_map[VK_LAUNCH_APP2] = Key::LAUNCH1; // (0xB7)
|
||||
// 0xB8-B9 are reserved.
|
||||
vk_map[VK_OEM_1] = Key::SEMICOLON; // (0xBA), Misc. character;can vary by keyboard/region. For US standard keyboards;the ';:' key.
|
||||
vk_map[VK_OEM_PLUS] = Key::EQUAL; // (0xBB)
|
||||
vk_map[VK_OEM_COMMA] = Key::COMMA; // (0xBC)
|
||||
vk_map[VK_OEM_MINUS] = Key::MINUS; // (0xBD)
|
||||
vk_map[VK_OEM_PERIOD] = Key::PERIOD; // (0xBE)
|
||||
vk_map[VK_OEM_2] = Key::SLASH; // (0xBF), For US standard keyboards;the '/?' key.
|
||||
vk_map[VK_OEM_3] = Key::QUOTELEFT; // (0xC0), For US standard keyboards;the '`~' key.
|
||||
// 0xC1-D7 are reserved. 0xD8-DA are unassigned.
|
||||
// 0xC3-DA may be used for old gamepads? Maybe we want to support this? See WinUser.h.
|
||||
vk_map[VK_OEM_4] = Key::BRACKETLEFT; // (0xDB), For US standard keyboards;the '[{' key.
|
||||
vk_map[VK_OEM_5] = Key::BACKSLASH; // (0xDC), For US standard keyboards;the '\|' key.
|
||||
vk_map[VK_OEM_6] = Key::BRACKETRIGHT; // (0xDD), For US standard keyboards;the ']}' key.
|
||||
vk_map[VK_OEM_7] = Key::APOSTROPHE; // (0xDE), For US standard keyboards;single quote/double quote.
|
||||
// VK_OEM_8 (0xDF)
|
||||
// 0xE0 is reserved. 0xE1 is OEM specific.
|
||||
vk_map[VK_OEM_102] = Key::BAR; // (0xE2), Either angle bracket or backslash key on the RT 102-key keyboard.
|
||||
vk_map[VK_ICO_HELP] = Key::HELP; // (0xE3)
|
||||
// 0xE4 is OEM (e.g. ICO) specific.
|
||||
// VK_PROCESSKEY (0xE5), For IME.
|
||||
vk_map[VK_ICO_CLEAR] = Key::CLEAR; // (0xE6)
|
||||
// VK_PACKET (0xE7), Used to pass Unicode characters as if they were keystrokes.
|
||||
// 0xE8 is unassigned.
|
||||
// 0xE9-F5 are OEM (Nokia/Ericsson) specific.
|
||||
vk_map[VK_ATTN] = Key::ESCAPE; // (0xF6), Old IBM 'ATTN' key used on midrange computers ;e.g. AS/400.
|
||||
vk_map[VK_CRSEL] = Key::TAB; // (0xF7), Old IBM 3270 'CrSel' (cursor select) key; used to select data fields.
|
||||
// VK_EXSEL (0xF7), Old IBM 3270 extended selection key.
|
||||
// VK_EREOF (0xF8), Old IBM 3270 erase to end of field key.
|
||||
vk_map[VK_PLAY] = Key::MEDIAPLAY; // (0xFA), Old IBM 3270 'Play' key.
|
||||
// VK_ZOOM (0xFB), Old IBM 3290 'Zoom' key.
|
||||
// VK_NONAME (0xFC), Reserved.
|
||||
// VK_PA1 (0xFD), Old IBM 3270 PA1 key.
|
||||
vk_map[VK_OEM_CLEAR] = Key::CLEAR; // (0xFE), OEM specific clear key. Unclear how it differs from normal clear.
|
||||
|
||||
scansym_map[0x00] = Key::PAUSE;
|
||||
scansym_map[0x01] = Key::ESCAPE;
|
||||
scansym_map[0x02] = Key::KEY_1;
|
||||
scansym_map[0x03] = Key::KEY_2;
|
||||
scansym_map[0x04] = Key::KEY_3;
|
||||
scansym_map[0x05] = Key::KEY_4;
|
||||
scansym_map[0x06] = Key::KEY_5;
|
||||
scansym_map[0x07] = Key::KEY_6;
|
||||
scansym_map[0x08] = Key::KEY_7;
|
||||
scansym_map[0x09] = Key::KEY_8;
|
||||
scansym_map[0x0A] = Key::KEY_9;
|
||||
scansym_map[0x0B] = Key::KEY_0;
|
||||
scansym_map[0x0C] = Key::MINUS;
|
||||
scansym_map[0x0D] = Key::EQUAL;
|
||||
scansym_map[0x0E] = Key::BACKSPACE;
|
||||
scansym_map[0x0F] = Key::TAB;
|
||||
scansym_map[0x10] = Key::Q;
|
||||
scansym_map[0x11] = Key::W;
|
||||
scansym_map[0x12] = Key::E;
|
||||
scansym_map[0x13] = Key::R;
|
||||
scansym_map[0x14] = Key::T;
|
||||
scansym_map[0x15] = Key::Y;
|
||||
scansym_map[0x16] = Key::U;
|
||||
scansym_map[0x17] = Key::I;
|
||||
scansym_map[0x18] = Key::O;
|
||||
scansym_map[0x19] = Key::P;
|
||||
scansym_map[0x1A] = Key::BRACKETLEFT;
|
||||
scansym_map[0x1B] = Key::BRACKETRIGHT;
|
||||
scansym_map[0x1C] = Key::ENTER;
|
||||
scansym_map[0x1D] = Key::CTRL;
|
||||
scansym_map[0x1E] = Key::A;
|
||||
scansym_map[0x1F] = Key::S;
|
||||
scansym_map[0x20] = Key::D;
|
||||
scansym_map[0x21] = Key::F;
|
||||
scansym_map[0x22] = Key::G;
|
||||
scansym_map[0x23] = Key::H;
|
||||
scansym_map[0x24] = Key::J;
|
||||
scansym_map[0x25] = Key::K;
|
||||
scansym_map[0x26] = Key::L;
|
||||
scansym_map[0x27] = Key::SEMICOLON;
|
||||
scansym_map[0x28] = Key::APOSTROPHE;
|
||||
scansym_map[0x29] = Key::QUOTELEFT;
|
||||
scansym_map[0x2A] = Key::SHIFT;
|
||||
scansym_map[0x2B] = Key::BACKSLASH;
|
||||
scansym_map[0x2C] = Key::Z;
|
||||
scansym_map[0x2D] = Key::X;
|
||||
scansym_map[0x2E] = Key::C;
|
||||
scansym_map[0x2F] = Key::V;
|
||||
scansym_map[0x30] = Key::B;
|
||||
scansym_map[0x31] = Key::N;
|
||||
scansym_map[0x32] = Key::M;
|
||||
scansym_map[0x33] = Key::COMMA;
|
||||
scansym_map[0x34] = Key::PERIOD;
|
||||
scansym_map[0x35] = Key::SLASH;
|
||||
scansym_map[0x36] = Key::SHIFT;
|
||||
scansym_map[0x37] = Key::KP_MULTIPLY;
|
||||
scansym_map[0x38] = Key::ALT;
|
||||
scansym_map[0x39] = Key::SPACE;
|
||||
scansym_map[0x3A] = Key::CAPSLOCK;
|
||||
scansym_map[0x3B] = Key::F1;
|
||||
scansym_map[0x3C] = Key::F2;
|
||||
scansym_map[0x3D] = Key::F3;
|
||||
scansym_map[0x3E] = Key::F4;
|
||||
scansym_map[0x3F] = Key::F5;
|
||||
scansym_map[0x40] = Key::F6;
|
||||
scansym_map[0x41] = Key::F7;
|
||||
scansym_map[0x42] = Key::F8;
|
||||
scansym_map[0x43] = Key::F9;
|
||||
scansym_map[0x44] = Key::F10;
|
||||
scansym_map[0x45] = Key::NUMLOCK;
|
||||
scansym_map[0x46] = Key::SCROLLLOCK;
|
||||
scansym_map[0x47] = Key::KP_7;
|
||||
scansym_map[0x48] = Key::KP_8;
|
||||
scansym_map[0x49] = Key::KP_9;
|
||||
scansym_map[0x4A] = Key::KP_SUBTRACT;
|
||||
scansym_map[0x4B] = Key::KP_4;
|
||||
scansym_map[0x4C] = Key::KP_5;
|
||||
scansym_map[0x4D] = Key::KP_6;
|
||||
scansym_map[0x4E] = Key::KP_ADD;
|
||||
scansym_map[0x4F] = Key::KP_1;
|
||||
scansym_map[0x50] = Key::KP_2;
|
||||
scansym_map[0x51] = Key::KP_3;
|
||||
scansym_map[0x52] = Key::KP_0;
|
||||
scansym_map[0x53] = Key::KP_PERIOD;
|
||||
scansym_map[0x56] = Key::SECTION;
|
||||
scansym_map[0x57] = Key::F11;
|
||||
scansym_map[0x58] = Key::F12;
|
||||
scansym_map[0x5B] = Key::META;
|
||||
scansym_map[0x5C] = Key::META;
|
||||
scansym_map[0x5D] = Key::MENU;
|
||||
scansym_map[0x64] = Key::F13;
|
||||
scansym_map[0x65] = Key::F14;
|
||||
scansym_map[0x66] = Key::F15;
|
||||
scansym_map[0x67] = Key::F16;
|
||||
scansym_map[0x68] = Key::F17;
|
||||
scansym_map[0x69] = Key::F18;
|
||||
scansym_map[0x6A] = Key::F19;
|
||||
scansym_map[0x6B] = Key::F20;
|
||||
scansym_map[0x6C] = Key::F21;
|
||||
scansym_map[0x6D] = Key::F22;
|
||||
scansym_map[0x6E] = Key::F23;
|
||||
// scansym_map[0x71] = Key::JIS_KANA;
|
||||
// scansym_map[0x72] = Key::JIS_EISU;
|
||||
scansym_map[0x76] = Key::F24;
|
||||
|
||||
for (const KeyValue<unsigned int, Key> &E : scansym_map) {
|
||||
scansym_map_inv[E.value] = E.key;
|
||||
}
|
||||
|
||||
scansym_map_ext[0x09] = Key::MENU;
|
||||
scansym_map_ext[0x10] = Key::MEDIAPREVIOUS;
|
||||
scansym_map_ext[0x19] = Key::MEDIANEXT;
|
||||
scansym_map_ext[0x1C] = Key::KP_ENTER;
|
||||
scansym_map_ext[0x20] = Key::VOLUMEMUTE;
|
||||
scansym_map_ext[0x21] = Key::LAUNCH1;
|
||||
scansym_map_ext[0x22] = Key::MEDIAPLAY;
|
||||
scansym_map_ext[0x24] = Key::MEDIASTOP;
|
||||
scansym_map_ext[0x2E] = Key::VOLUMEDOWN;
|
||||
scansym_map_ext[0x30] = Key::VOLUMEUP;
|
||||
scansym_map_ext[0x32] = Key::HOMEPAGE;
|
||||
scansym_map_ext[0x35] = Key::KP_DIVIDE;
|
||||
scansym_map_ext[0x37] = Key::PRINT;
|
||||
scansym_map_ext[0x3A] = Key::KP_ADD;
|
||||
scansym_map_ext[0x45] = Key::NUMLOCK;
|
||||
scansym_map_ext[0x47] = Key::HOME;
|
||||
scansym_map_ext[0x48] = Key::UP;
|
||||
scansym_map_ext[0x49] = Key::PAGEUP;
|
||||
scansym_map_ext[0x4A] = Key::KP_SUBTRACT;
|
||||
scansym_map_ext[0x4B] = Key::LEFT;
|
||||
scansym_map_ext[0x4C] = Key::KP_5;
|
||||
scansym_map_ext[0x4D] = Key::RIGHT;
|
||||
scansym_map_ext[0x4E] = Key::KP_ADD;
|
||||
scansym_map_ext[0x4F] = Key::END;
|
||||
scansym_map_ext[0x50] = Key::DOWN;
|
||||
scansym_map_ext[0x51] = Key::PAGEDOWN;
|
||||
scansym_map_ext[0x52] = Key::INSERT;
|
||||
scansym_map_ext[0x53] = Key::KEY_DELETE;
|
||||
scansym_map_ext[0x5D] = Key::MENU;
|
||||
scansym_map_ext[0x5F] = Key::STANDBY;
|
||||
scansym_map_ext[0x65] = Key::SEARCH;
|
||||
scansym_map_ext[0x66] = Key::FAVORITES;
|
||||
scansym_map_ext[0x67] = Key::REFRESH;
|
||||
scansym_map_ext[0x68] = Key::STOP;
|
||||
scansym_map_ext[0x69] = Key::FORWARD;
|
||||
scansym_map_ext[0x6A] = Key::BACK;
|
||||
scansym_map_ext[0x6B] = Key::LAUNCH0;
|
||||
scansym_map_ext[0x6C] = Key::LAUNCHMAIL;
|
||||
scansym_map_ext[0x6D] = Key::LAUNCHMEDIA;
|
||||
scansym_map_ext[0x78] = Key::MEDIARECORD;
|
||||
|
||||
// Scancode to physical location map.
|
||||
// Shift.
|
||||
location_map[0x2A] = KeyLocation::LEFT;
|
||||
location_map[0x36] = KeyLocation::RIGHT;
|
||||
// Meta.
|
||||
location_map[0x5B] = KeyLocation::LEFT;
|
||||
location_map[0x5C] = KeyLocation::RIGHT;
|
||||
// Ctrl and Alt must be handled differently.
|
||||
}
|
||||
|
||||
Key KeyMappingWindows::get_keysym(unsigned int p_code) {
|
||||
const Key *key = vk_map.getptr(p_code);
|
||||
if (key) {
|
||||
return *key;
|
||||
}
|
||||
return Key::UNKNOWN;
|
||||
}
|
||||
|
||||
unsigned int KeyMappingWindows::get_scancode(Key p_keycode) {
|
||||
const unsigned int *key = scansym_map_inv.getptr(p_keycode);
|
||||
if (key) {
|
||||
return *key;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Key KeyMappingWindows::get_scansym(unsigned int p_code, bool p_extended) {
|
||||
if (p_extended) {
|
||||
const Key *key = scansym_map_ext.getptr(p_code);
|
||||
if (key) {
|
||||
return *key;
|
||||
}
|
||||
}
|
||||
const Key *key = scansym_map.getptr(p_code);
|
||||
if (key) {
|
||||
return *key;
|
||||
}
|
||||
return Key::NONE;
|
||||
}
|
||||
|
||||
bool KeyMappingWindows::is_extended_key(unsigned int p_code) {
|
||||
return p_code == VK_INSERT ||
|
||||
p_code == VK_DELETE ||
|
||||
p_code == VK_HOME ||
|
||||
p_code == VK_END ||
|
||||
p_code == VK_PRIOR ||
|
||||
p_code == VK_NEXT ||
|
||||
p_code == VK_LEFT ||
|
||||
p_code == VK_UP ||
|
||||
p_code == VK_RIGHT ||
|
||||
p_code == VK_DOWN;
|
||||
}
|
||||
|
||||
KeyLocation KeyMappingWindows::get_location(unsigned int p_code, bool p_extended) {
|
||||
// Right- ctrl and alt have the same scancode as left, but are in the extended keys.
|
||||
const Key *key = scansym_map.getptr(p_code);
|
||||
if (key && (*key == Key::CTRL || *key == Key::ALT)) {
|
||||
return p_extended ? KeyLocation::RIGHT : KeyLocation::LEFT;
|
||||
}
|
||||
const KeyLocation *location = location_map.getptr(p_code);
|
||||
if (location) {
|
||||
return *location;
|
||||
}
|
||||
return KeyLocation::UNSPECIFIED;
|
||||
}
|
||||
53
engine/platform/windows/key_mapping_windows.h
Normal file
53
engine/platform/windows/key_mapping_windows.h
Normal 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
|
||||
191
engine/platform/windows/lang_table.h
Normal file
191
engine/platform/windows/lang_table.h
Normal 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
|
||||
20
engine/platform/windows/msvs.py
Normal file
20
engine/platform/windows/msvs.py
Normal 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 "plat=$(PlatformTarget)"",
|
||||
"(if "$(PlatformTarget)"=="x64" (set "plat=x86_amd64"))",
|
||||
f"call "{batch_file}" !plat!",
|
||||
]
|
||||
1169
engine/platform/windows/native_menu_windows.cpp
Normal file
1169
engine/platform/windows/native_menu_windows.cpp
Normal file
File diff suppressed because it is too large
Load diff
154
engine/platform/windows/native_menu_windows.h
Normal file
154
engine/platform/windows/native_menu_windows.h
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/**************************************************************************/
|
||||
/* native_menu_windows.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#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
|
||||
1941
engine/platform/windows/os_windows.cpp
Normal file
1941
engine/platform/windows/os_windows.cpp
Normal file
File diff suppressed because it is too large
Load diff
246
engine/platform/windows/os_windows.h
Normal file
246
engine/platform/windows/os_windows.h
Normal 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
|
||||
31
engine/platform/windows/platform_config.h
Normal file
31
engine/platform/windows/platform_config.h
Normal 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>
|
||||
53
engine/platform/windows/platform_gl.h
Normal file
53
engine/platform/windows/platform_gl.h
Normal 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
|
||||
24
engine/platform/windows/platform_windows_builders.py
Normal file
24
engine/platform/windows/platform_windows_builders.py
Normal 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))
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
270
engine/platform/windows/tts_windows.cpp
Normal file
270
engine/platform/windows/tts_windows.cpp
Normal 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;
|
||||
}
|
||||
80
engine/platform/windows/tts_windows.h
Normal file
80
engine/platform/windows/tts_windows.h
Normal 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
|
||||
197
engine/platform/windows/wgl_detect_version.cpp
Normal file
197
engine/platform/windows/wgl_detect_version.cpp
Normal 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
|
||||
42
engine/platform/windows/wgl_detect_version.h
Normal file
42
engine/platform/windows/wgl_detect_version.h
Normal 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
|
||||
159
engine/platform/windows/windows_terminal_logger.cpp
Normal file
159
engine/platform/windows/windows_terminal_logger.cpp
Normal 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
|
||||
47
engine/platform/windows/windows_terminal_logger.h
Normal file
47
engine/platform/windows/windows_terminal_logger.h
Normal 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
|
||||
280
engine/platform/windows/windows_utils.cpp
Normal file
280
engine/platform/windows/windows_utils.cpp
Normal 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
|
||||
49
engine/platform/windows/windows_utils.h
Normal file
49
engine/platform/windows/windows_utils.h
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue