initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
6
core/os/SCsub
Normal file
6
core/os/SCsub
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.core_sources, "*.cpp")
|
86
core/os/condition_variable.h
Normal file
86
core/os/condition_variable.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/**************************************************************************/
|
||||
/* condition_variable.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/os/safe_binary_mutex.h"
|
||||
|
||||
#ifdef THREADS_ENABLED
|
||||
|
||||
#ifdef MINGW_ENABLED
|
||||
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
|
||||
#include "thirdparty/mingw-std-threads/mingw.condition_variable.h"
|
||||
#define THREADING_NAMESPACE mingw_stdthread
|
||||
#else
|
||||
#include <condition_variable>
|
||||
#define THREADING_NAMESPACE std
|
||||
#endif
|
||||
|
||||
// An object one or multiple threads can wait on a be notified by some other.
|
||||
// Normally, you want to use a semaphore for such scenarios, but when the
|
||||
// condition is something different than a count being greater than zero
|
||||
// (which is the built-in logic in a semaphore) or you want to provide your
|
||||
// own mutex to tie the wait-notify to some other behavior, you need to use this.
|
||||
|
||||
class ConditionVariable {
|
||||
mutable THREADING_NAMESPACE::condition_variable condition;
|
||||
|
||||
public:
|
||||
template <typename BinaryMutexT>
|
||||
_ALWAYS_INLINE_ void wait(const MutexLock<BinaryMutexT> &p_lock) const {
|
||||
condition.wait(p_lock._get_lock());
|
||||
}
|
||||
|
||||
template <int Tag>
|
||||
_ALWAYS_INLINE_ void wait(const MutexLock<SafeBinaryMutex<Tag>> &p_lock) const {
|
||||
condition.wait(p_lock.mutex._get_lock());
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ void notify_one() const {
|
||||
condition.notify_one();
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ void notify_all() const {
|
||||
condition.notify_all();
|
||||
}
|
||||
};
|
||||
|
||||
#else // No threads.
|
||||
|
||||
class ConditionVariable {
|
||||
public:
|
||||
template <typename BinaryMutexT>
|
||||
void wait(const MutexLock<BinaryMutexT> &p_lock) const {}
|
||||
void notify_one() const {}
|
||||
void notify_all() const {}
|
||||
};
|
||||
|
||||
#endif // THREADS_ENABLED
|
495
core/os/keyboard.cpp
Normal file
495
core/os/keyboard.cpp
Normal file
@@ -0,0 +1,495 @@
|
||||
/**************************************************************************/
|
||||
/* keyboard.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 "keyboard.h"
|
||||
|
||||
#include "core/os/os.h"
|
||||
|
||||
struct _KeyCodeText {
|
||||
Key code;
|
||||
const char *text;
|
||||
};
|
||||
|
||||
static const _KeyCodeText _keycodes[] = {
|
||||
/* clang-format off */
|
||||
{Key::ESCAPE ,"Escape"},
|
||||
{Key::TAB ,"Tab"},
|
||||
{Key::BACKTAB ,"Backtab"},
|
||||
{Key::BACKSPACE ,"Backspace"},
|
||||
{Key::ENTER ,"Enter"},
|
||||
{Key::KP_ENTER ,"Kp Enter"},
|
||||
{Key::INSERT ,"Insert"},
|
||||
{Key::KEY_DELETE ,"Delete"},
|
||||
{Key::PAUSE ,"Pause"},
|
||||
{Key::PRINT ,"Print"},
|
||||
{Key::SYSREQ ,"SysReq"},
|
||||
{Key::CLEAR ,"Clear"},
|
||||
{Key::HOME ,"Home"},
|
||||
{Key::END ,"End"},
|
||||
{Key::LEFT ,"Left"},
|
||||
{Key::UP ,"Up"},
|
||||
{Key::RIGHT ,"Right"},
|
||||
{Key::DOWN ,"Down"},
|
||||
{Key::PAGEUP ,"PageUp"},
|
||||
{Key::PAGEDOWN ,"PageDown"},
|
||||
{Key::SHIFT ,"Shift"},
|
||||
{Key::CTRL ,"Ctrl"},
|
||||
#if defined(MACOS_ENABLED)
|
||||
{Key::META ,"Command"},
|
||||
{Key::CMD_OR_CTRL ,"Command"},
|
||||
{Key::ALT ,"Option"},
|
||||
#elif defined(WINDOWS_ENABLED)
|
||||
{Key::META ,"Windows"},
|
||||
{Key::CMD_OR_CTRL ,"Ctrl"},
|
||||
{Key::ALT ,"Alt"},
|
||||
#else
|
||||
{Key::META ,"Meta"},
|
||||
{Key::CMD_OR_CTRL ,"Ctrl"},
|
||||
{Key::ALT ,"Alt"},
|
||||
#endif
|
||||
{Key::CAPSLOCK ,"CapsLock"},
|
||||
{Key::NUMLOCK ,"NumLock"},
|
||||
{Key::SCROLLLOCK ,"ScrollLock"},
|
||||
{Key::F1 ,"F1"},
|
||||
{Key::F2 ,"F2"},
|
||||
{Key::F3 ,"F3"},
|
||||
{Key::F4 ,"F4"},
|
||||
{Key::F5 ,"F5"},
|
||||
{Key::F6 ,"F6"},
|
||||
{Key::F7 ,"F7"},
|
||||
{Key::F8 ,"F8"},
|
||||
{Key::F9 ,"F9"},
|
||||
{Key::F10 ,"F10"},
|
||||
{Key::F11 ,"F11"},
|
||||
{Key::F12 ,"F12"},
|
||||
{Key::F13 ,"F13"},
|
||||
{Key::F14 ,"F14"},
|
||||
{Key::F15 ,"F15"},
|
||||
{Key::F16 ,"F16"},
|
||||
{Key::F17 ,"F17"},
|
||||
{Key::F18 ,"F18"},
|
||||
{Key::F19 ,"F19"},
|
||||
{Key::F20 ,"F20"},
|
||||
{Key::F21 ,"F21"},
|
||||
{Key::F22 ,"F22"},
|
||||
{Key::F23 ,"F23"},
|
||||
{Key::F24 ,"F24"},
|
||||
{Key::F25 ,"F25"},
|
||||
{Key::F26 ,"F26"},
|
||||
{Key::F27 ,"F27"},
|
||||
{Key::F28 ,"F28"},
|
||||
{Key::F29 ,"F29"},
|
||||
{Key::F30 ,"F30"},
|
||||
{Key::F31 ,"F31"},
|
||||
{Key::F32 ,"F32"},
|
||||
{Key::F33 ,"F33"},
|
||||
{Key::F34 ,"F34"},
|
||||
{Key::F35 ,"F35"},
|
||||
{Key::KP_MULTIPLY ,"Kp Multiply"},
|
||||
{Key::KP_DIVIDE ,"Kp Divide"},
|
||||
{Key::KP_SUBTRACT ,"Kp Subtract"},
|
||||
{Key::KP_PERIOD ,"Kp Period"},
|
||||
{Key::KP_ADD ,"Kp Add"},
|
||||
{Key::KP_0 ,"Kp 0"},
|
||||
{Key::KP_1 ,"Kp 1"},
|
||||
{Key::KP_2 ,"Kp 2"},
|
||||
{Key::KP_3 ,"Kp 3"},
|
||||
{Key::KP_4 ,"Kp 4"},
|
||||
{Key::KP_5 ,"Kp 5"},
|
||||
{Key::KP_6 ,"Kp 6"},
|
||||
{Key::KP_7 ,"Kp 7"},
|
||||
{Key::KP_8 ,"Kp 8"},
|
||||
{Key::KP_9 ,"Kp 9"},
|
||||
{Key::MENU ,"Menu"},
|
||||
{Key::HYPER ,"Hyper"},
|
||||
{Key::HELP ,"Help"},
|
||||
{Key::BACK ,"Back"},
|
||||
{Key::FORWARD ,"Forward"},
|
||||
{Key::STOP ,"Stop"},
|
||||
{Key::REFRESH ,"Refresh"},
|
||||
{Key::VOLUMEDOWN ,"VolumeDown"},
|
||||
{Key::VOLUMEMUTE ,"VolumeMute"},
|
||||
{Key::VOLUMEUP ,"VolumeUp"},
|
||||
{Key::MEDIAPLAY ,"MediaPlay"},
|
||||
{Key::MEDIASTOP ,"MediaStop"},
|
||||
{Key::MEDIAPREVIOUS ,"MediaPrevious"},
|
||||
{Key::MEDIANEXT ,"MediaNext"},
|
||||
{Key::MEDIARECORD ,"MediaRecord"},
|
||||
{Key::HOMEPAGE ,"HomePage"},
|
||||
{Key::FAVORITES ,"Favorites"},
|
||||
{Key::SEARCH ,"Search"},
|
||||
{Key::STANDBY ,"StandBy"},
|
||||
{Key::OPENURL ,"OpenURL"},
|
||||
{Key::LAUNCHMAIL ,"LaunchMail"},
|
||||
{Key::LAUNCHMEDIA ,"LaunchMedia"},
|
||||
{Key::LAUNCH0 ,"Launch0"},
|
||||
{Key::LAUNCH1 ,"Launch1"},
|
||||
{Key::LAUNCH2 ,"Launch2"},
|
||||
{Key::LAUNCH3 ,"Launch3"},
|
||||
{Key::LAUNCH4 ,"Launch4"},
|
||||
{Key::LAUNCH5 ,"Launch5"},
|
||||
{Key::LAUNCH6 ,"Launch6"},
|
||||
{Key::LAUNCH7 ,"Launch7"},
|
||||
{Key::LAUNCH8 ,"Launch8"},
|
||||
{Key::LAUNCH9 ,"Launch9"},
|
||||
{Key::LAUNCHA ,"LaunchA"},
|
||||
{Key::LAUNCHB ,"LaunchB"},
|
||||
{Key::LAUNCHC ,"LaunchC"},
|
||||
{Key::LAUNCHD ,"LaunchD"},
|
||||
{Key::LAUNCHE ,"LaunchE"},
|
||||
{Key::LAUNCHF ,"LaunchF"},
|
||||
{Key::GLOBE ,"Globe"},
|
||||
{Key::KEYBOARD ,"On-screen keyboard"},
|
||||
{Key::JIS_EISU ,"JIS Eisu"},
|
||||
{Key::JIS_KANA ,"JIS Kana"},
|
||||
{Key::UNKNOWN ,"Unknown"},
|
||||
{Key::SPACE ,"Space"},
|
||||
{Key::EXCLAM ,"Exclam"},
|
||||
{Key::QUOTEDBL ,"QuoteDbl"},
|
||||
{Key::NUMBERSIGN ,"NumberSign"},
|
||||
{Key::DOLLAR ,"Dollar"},
|
||||
{Key::PERCENT ,"Percent"},
|
||||
{Key::AMPERSAND ,"Ampersand"},
|
||||
{Key::APOSTROPHE ,"Apostrophe"},
|
||||
{Key::PARENLEFT ,"ParenLeft"},
|
||||
{Key::PARENRIGHT ,"ParenRight"},
|
||||
{Key::ASTERISK ,"Asterisk"},
|
||||
{Key::PLUS ,"Plus"},
|
||||
{Key::COMMA ,"Comma"},
|
||||
{Key::MINUS ,"Minus"},
|
||||
{Key::PERIOD ,"Period"},
|
||||
{Key::SLASH ,"Slash"},
|
||||
{Key::KEY_0 ,"0"},
|
||||
{Key::KEY_1 ,"1"},
|
||||
{Key::KEY_2 ,"2"},
|
||||
{Key::KEY_3 ,"3"},
|
||||
{Key::KEY_4 ,"4"},
|
||||
{Key::KEY_5 ,"5"},
|
||||
{Key::KEY_6 ,"6"},
|
||||
{Key::KEY_7 ,"7"},
|
||||
{Key::KEY_8 ,"8"},
|
||||
{Key::KEY_9 ,"9"},
|
||||
{Key::COLON ,"Colon"},
|
||||
{Key::SEMICOLON ,"Semicolon"},
|
||||
{Key::LESS ,"Less"},
|
||||
{Key::EQUAL ,"Equal"},
|
||||
{Key::GREATER ,"Greater"},
|
||||
{Key::QUESTION ,"Question"},
|
||||
{Key::AT ,"At"},
|
||||
{Key::A ,"A"},
|
||||
{Key::B ,"B"},
|
||||
{Key::C ,"C"},
|
||||
{Key::D ,"D"},
|
||||
{Key::E ,"E"},
|
||||
{Key::F ,"F"},
|
||||
{Key::G ,"G"},
|
||||
{Key::H ,"H"},
|
||||
{Key::I ,"I"},
|
||||
{Key::J ,"J"},
|
||||
{Key::K ,"K"},
|
||||
{Key::L ,"L"},
|
||||
{Key::M ,"M"},
|
||||
{Key::N ,"N"},
|
||||
{Key::O ,"O"},
|
||||
{Key::P ,"P"},
|
||||
{Key::Q ,"Q"},
|
||||
{Key::R ,"R"},
|
||||
{Key::S ,"S"},
|
||||
{Key::T ,"T"},
|
||||
{Key::U ,"U"},
|
||||
{Key::V ,"V"},
|
||||
{Key::W ,"W"},
|
||||
{Key::X ,"X"},
|
||||
{Key::Y ,"Y"},
|
||||
{Key::Z ,"Z"},
|
||||
{Key::BRACKETLEFT ,"BracketLeft"},
|
||||
{Key::BACKSLASH ,"BackSlash"},
|
||||
{Key::BRACKETRIGHT ,"BracketRight"},
|
||||
{Key::ASCIICIRCUM ,"AsciiCircum"},
|
||||
{Key::UNDERSCORE ,"UnderScore"},
|
||||
{Key::QUOTELEFT ,"QuoteLeft"},
|
||||
{Key::BRACELEFT ,"BraceLeft"},
|
||||
{Key::BAR ,"Bar"},
|
||||
{Key::BRACERIGHT ,"BraceRight"},
|
||||
{Key::ASCIITILDE ,"AsciiTilde"},
|
||||
{Key::YEN ,"Yen"},
|
||||
{Key::SECTION ,"Section"},
|
||||
{Key::NONE ,nullptr}
|
||||
/* clang-format on */
|
||||
};
|
||||
|
||||
bool keycode_has_unicode(Key p_keycode) {
|
||||
switch (p_keycode) {
|
||||
case Key::ESCAPE:
|
||||
case Key::TAB:
|
||||
case Key::BACKTAB:
|
||||
case Key::BACKSPACE:
|
||||
case Key::ENTER:
|
||||
case Key::KP_ENTER:
|
||||
case Key::INSERT:
|
||||
case Key::KEY_DELETE:
|
||||
case Key::PAUSE:
|
||||
case Key::PRINT:
|
||||
case Key::SYSREQ:
|
||||
case Key::CLEAR:
|
||||
case Key::HOME:
|
||||
case Key::END:
|
||||
case Key::LEFT:
|
||||
case Key::UP:
|
||||
case Key::RIGHT:
|
||||
case Key::DOWN:
|
||||
case Key::PAGEUP:
|
||||
case Key::PAGEDOWN:
|
||||
case Key::SHIFT:
|
||||
case Key::CTRL:
|
||||
case Key::META:
|
||||
case Key::ALT:
|
||||
case Key::CAPSLOCK:
|
||||
case Key::NUMLOCK:
|
||||
case Key::SCROLLLOCK:
|
||||
case Key::F1:
|
||||
case Key::F2:
|
||||
case Key::F3:
|
||||
case Key::F4:
|
||||
case Key::F5:
|
||||
case Key::F6:
|
||||
case Key::F7:
|
||||
case Key::F8:
|
||||
case Key::F9:
|
||||
case Key::F10:
|
||||
case Key::F11:
|
||||
case Key::F12:
|
||||
case Key::F13:
|
||||
case Key::F14:
|
||||
case Key::F15:
|
||||
case Key::F16:
|
||||
case Key::F17:
|
||||
case Key::F18:
|
||||
case Key::F19:
|
||||
case Key::F20:
|
||||
case Key::F21:
|
||||
case Key::F22:
|
||||
case Key::F23:
|
||||
case Key::F24:
|
||||
case Key::F25:
|
||||
case Key::F26:
|
||||
case Key::F27:
|
||||
case Key::F28:
|
||||
case Key::F29:
|
||||
case Key::F30:
|
||||
case Key::F31:
|
||||
case Key::F32:
|
||||
case Key::F33:
|
||||
case Key::F34:
|
||||
case Key::F35:
|
||||
case Key::MENU:
|
||||
case Key::HYPER:
|
||||
case Key::HELP:
|
||||
case Key::BACK:
|
||||
case Key::FORWARD:
|
||||
case Key::STOP:
|
||||
case Key::REFRESH:
|
||||
case Key::VOLUMEDOWN:
|
||||
case Key::VOLUMEMUTE:
|
||||
case Key::VOLUMEUP:
|
||||
case Key::MEDIAPLAY:
|
||||
case Key::MEDIASTOP:
|
||||
case Key::MEDIAPREVIOUS:
|
||||
case Key::MEDIANEXT:
|
||||
case Key::MEDIARECORD:
|
||||
case Key::HOMEPAGE:
|
||||
case Key::FAVORITES:
|
||||
case Key::SEARCH:
|
||||
case Key::STANDBY:
|
||||
case Key::OPENURL:
|
||||
case Key::LAUNCHMAIL:
|
||||
case Key::LAUNCHMEDIA:
|
||||
case Key::LAUNCH0:
|
||||
case Key::LAUNCH1:
|
||||
case Key::LAUNCH2:
|
||||
case Key::LAUNCH3:
|
||||
case Key::LAUNCH4:
|
||||
case Key::LAUNCH5:
|
||||
case Key::LAUNCH6:
|
||||
case Key::LAUNCH7:
|
||||
case Key::LAUNCH8:
|
||||
case Key::LAUNCH9:
|
||||
case Key::LAUNCHA:
|
||||
case Key::LAUNCHB:
|
||||
case Key::LAUNCHC:
|
||||
case Key::LAUNCHD:
|
||||
case Key::LAUNCHE:
|
||||
case Key::LAUNCHF:
|
||||
case Key::GLOBE:
|
||||
case Key::KEYBOARD:
|
||||
case Key::JIS_EISU:
|
||||
case Key::JIS_KANA:
|
||||
return false;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
String keycode_get_string(Key p_code) {
|
||||
String codestr;
|
||||
if ((p_code & KeyModifierMask::SHIFT) != Key::NONE) {
|
||||
codestr += find_keycode_name(Key::SHIFT);
|
||||
codestr += "+";
|
||||
}
|
||||
if ((p_code & KeyModifierMask::ALT) != Key::NONE) {
|
||||
codestr += find_keycode_name(Key::ALT);
|
||||
codestr += "+";
|
||||
}
|
||||
if ((p_code & KeyModifierMask::CMD_OR_CTRL) != Key::NONE) {
|
||||
if (OS::get_singleton()->has_feature("macos") || OS::get_singleton()->has_feature("web_macos") || OS::get_singleton()->has_feature("web_ios")) {
|
||||
codestr += find_keycode_name(Key::META);
|
||||
} else {
|
||||
codestr += find_keycode_name(Key::CTRL);
|
||||
}
|
||||
codestr += "+";
|
||||
}
|
||||
if ((p_code & KeyModifierMask::CTRL) != Key::NONE) {
|
||||
codestr += find_keycode_name(Key::CTRL);
|
||||
codestr += "+";
|
||||
}
|
||||
if ((p_code & KeyModifierMask::META) != Key::NONE) {
|
||||
codestr += find_keycode_name(Key::META);
|
||||
codestr += "+";
|
||||
}
|
||||
|
||||
p_code &= KeyModifierMask::CODE_MASK;
|
||||
if ((char32_t)p_code == 0) {
|
||||
// The key was just a modifier without any code.
|
||||
return codestr;
|
||||
}
|
||||
|
||||
const _KeyCodeText *kct = &_keycodes[0];
|
||||
|
||||
while (kct->text) {
|
||||
if (kct->code == p_code) {
|
||||
codestr += kct->text;
|
||||
return codestr;
|
||||
}
|
||||
kct++;
|
||||
}
|
||||
|
||||
codestr += String::chr((char32_t)p_code);
|
||||
|
||||
return codestr;
|
||||
}
|
||||
|
||||
Key find_keycode(const String &p_codestr) {
|
||||
Key keycode = Key::NONE;
|
||||
Vector<String> code_parts = p_codestr.split("+");
|
||||
if (code_parts.is_empty()) {
|
||||
return keycode;
|
||||
}
|
||||
|
||||
const String &last_part = code_parts[code_parts.size() - 1];
|
||||
const _KeyCodeText *kct = &_keycodes[0];
|
||||
|
||||
while (kct->text) {
|
||||
if (last_part.nocasecmp_to(kct->text) == 0) {
|
||||
keycode = kct->code;
|
||||
break;
|
||||
}
|
||||
kct++;
|
||||
}
|
||||
|
||||
for (int part = 0; part < code_parts.size() - 1; part++) {
|
||||
const String &code_part = code_parts[part];
|
||||
if (code_part.nocasecmp_to(find_keycode_name(Key::SHIFT)) == 0) {
|
||||
keycode |= KeyModifierMask::SHIFT;
|
||||
} else if (code_part.nocasecmp_to(find_keycode_name(Key::CTRL)) == 0) {
|
||||
keycode |= KeyModifierMask::CTRL;
|
||||
} else if (code_part.nocasecmp_to(find_keycode_name(Key::META)) == 0) {
|
||||
keycode |= KeyModifierMask::META;
|
||||
} else if (code_part.nocasecmp_to(find_keycode_name(Key::ALT)) == 0) {
|
||||
keycode |= KeyModifierMask::ALT;
|
||||
}
|
||||
}
|
||||
|
||||
return keycode;
|
||||
}
|
||||
|
||||
const char *find_keycode_name(Key p_keycode) {
|
||||
const _KeyCodeText *kct = &_keycodes[0];
|
||||
|
||||
while (kct->text) {
|
||||
if (kct->code == p_keycode) {
|
||||
return kct->text;
|
||||
}
|
||||
kct++;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
int keycode_get_count() {
|
||||
const _KeyCodeText *kct = &_keycodes[0];
|
||||
|
||||
int count = 0;
|
||||
while (kct->text) {
|
||||
count++;
|
||||
kct++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int keycode_get_value_by_index(int p_index) {
|
||||
return (int)_keycodes[p_index].code;
|
||||
}
|
||||
|
||||
const char *keycode_get_name_by_index(int p_index) {
|
||||
return _keycodes[p_index].text;
|
||||
}
|
||||
|
||||
char32_t fix_unicode(char32_t p_char) {
|
||||
if (p_char >= 0x20 && p_char != 0x7F) {
|
||||
return p_char;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Key fix_keycode(char32_t p_char, Key p_key) {
|
||||
if (p_char >= 0x20 && p_char <= 0x7E) {
|
||||
return (Key)String::char_uppercase(p_char);
|
||||
}
|
||||
return p_key;
|
||||
}
|
||||
|
||||
Key fix_key_label(char32_t p_char, Key p_key) {
|
||||
if (p_char >= 0x20 && p_char != 0x7F) {
|
||||
return (Key)String::char_uppercase(p_char);
|
||||
}
|
||||
return p_key;
|
||||
}
|
348
core/os/keyboard.h
Normal file
348
core/os/keyboard.h
Normal file
@@ -0,0 +1,348 @@
|
||||
/**************************************************************************/
|
||||
/* keyboard.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/string/ustring.h"
|
||||
|
||||
// Keep the values in this enum in sync with `_keycodes` in `keyboard.cpp`,
|
||||
// and the bindings in `core_constants.cpp`.
|
||||
enum class Key {
|
||||
NONE = 0,
|
||||
// Special key: The strategy here is similar to the one used by toolkits,
|
||||
// which consists in leaving the 21 bits unicode range for printable
|
||||
// characters, and use the upper 11 bits for special keys and modifiers.
|
||||
// This way everything (char/keycode) can fit nicely in one 32-bit
|
||||
// integer (the enum's underlying type is `int` by default).
|
||||
SPECIAL = (1 << 22),
|
||||
/* CURSOR/FUNCTION/BROWSER/MULTIMEDIA/MISC KEYS */
|
||||
ESCAPE = SPECIAL | 0x01,
|
||||
TAB = SPECIAL | 0x02,
|
||||
BACKTAB = SPECIAL | 0x03,
|
||||
BACKSPACE = SPECIAL | 0x04,
|
||||
ENTER = SPECIAL | 0x05,
|
||||
KP_ENTER = SPECIAL | 0x06,
|
||||
INSERT = SPECIAL | 0x07,
|
||||
KEY_DELETE = SPECIAL | 0x08, // "DELETE" is a reserved word on Windows.
|
||||
PAUSE = SPECIAL | 0x09,
|
||||
PRINT = SPECIAL | 0x0A,
|
||||
SYSREQ = SPECIAL | 0x0B,
|
||||
CLEAR = SPECIAL | 0x0C,
|
||||
HOME = SPECIAL | 0x0D,
|
||||
END = SPECIAL | 0x0E,
|
||||
LEFT = SPECIAL | 0x0F,
|
||||
UP = SPECIAL | 0x10,
|
||||
RIGHT = SPECIAL | 0x11,
|
||||
DOWN = SPECIAL | 0x12,
|
||||
PAGEUP = SPECIAL | 0x13,
|
||||
PAGEDOWN = SPECIAL | 0x14,
|
||||
SHIFT = SPECIAL | 0x15,
|
||||
CTRL = SPECIAL | 0x16,
|
||||
META = SPECIAL | 0x17,
|
||||
#if defined(MACOS_ENABLED)
|
||||
CMD_OR_CTRL = META,
|
||||
#else
|
||||
CMD_OR_CTRL = CTRL,
|
||||
#endif
|
||||
ALT = SPECIAL | 0x18,
|
||||
CAPSLOCK = SPECIAL | 0x19,
|
||||
NUMLOCK = SPECIAL | 0x1A,
|
||||
SCROLLLOCK = SPECIAL | 0x1B,
|
||||
F1 = SPECIAL | 0x1C,
|
||||
F2 = SPECIAL | 0x1D,
|
||||
F3 = SPECIAL | 0x1E,
|
||||
F4 = SPECIAL | 0x1F,
|
||||
F5 = SPECIAL | 0x20,
|
||||
F6 = SPECIAL | 0x21,
|
||||
F7 = SPECIAL | 0x22,
|
||||
F8 = SPECIAL | 0x23,
|
||||
F9 = SPECIAL | 0x24,
|
||||
F10 = SPECIAL | 0x25,
|
||||
F11 = SPECIAL | 0x26,
|
||||
F12 = SPECIAL | 0x27,
|
||||
F13 = SPECIAL | 0x28,
|
||||
F14 = SPECIAL | 0x29,
|
||||
F15 = SPECIAL | 0x2A,
|
||||
F16 = SPECIAL | 0x2B,
|
||||
F17 = SPECIAL | 0x2C,
|
||||
F18 = SPECIAL | 0x2D,
|
||||
F19 = SPECIAL | 0x2E,
|
||||
F20 = SPECIAL | 0x2F,
|
||||
F21 = SPECIAL | 0x30,
|
||||
F22 = SPECIAL | 0x31,
|
||||
F23 = SPECIAL | 0x32,
|
||||
F24 = SPECIAL | 0x33,
|
||||
F25 = SPECIAL | 0x34,
|
||||
F26 = SPECIAL | 0x35,
|
||||
F27 = SPECIAL | 0x36,
|
||||
F28 = SPECIAL | 0x37,
|
||||
F29 = SPECIAL | 0x38,
|
||||
F30 = SPECIAL | 0x39,
|
||||
F31 = SPECIAL | 0x3A,
|
||||
F32 = SPECIAL | 0x3B,
|
||||
F33 = SPECIAL | 0x3C,
|
||||
F34 = SPECIAL | 0x3D,
|
||||
F35 = SPECIAL | 0x3E,
|
||||
KP_MULTIPLY = SPECIAL | 0x81,
|
||||
KP_DIVIDE = SPECIAL | 0x82,
|
||||
KP_SUBTRACT = SPECIAL | 0x83,
|
||||
KP_PERIOD = SPECIAL | 0x84,
|
||||
KP_ADD = SPECIAL | 0x85,
|
||||
KP_0 = SPECIAL | 0x86,
|
||||
KP_1 = SPECIAL | 0x87,
|
||||
KP_2 = SPECIAL | 0x88,
|
||||
KP_3 = SPECIAL | 0x89,
|
||||
KP_4 = SPECIAL | 0x8A,
|
||||
KP_5 = SPECIAL | 0x8B,
|
||||
KP_6 = SPECIAL | 0x8C,
|
||||
KP_7 = SPECIAL | 0x8D,
|
||||
KP_8 = SPECIAL | 0x8E,
|
||||
KP_9 = SPECIAL | 0x8F,
|
||||
MENU = SPECIAL | 0x42,
|
||||
HYPER = SPECIAL | 0x43,
|
||||
HELP = SPECIAL | 0x45,
|
||||
BACK = SPECIAL | 0x48,
|
||||
FORWARD = SPECIAL | 0x49,
|
||||
STOP = SPECIAL | 0x4A,
|
||||
REFRESH = SPECIAL | 0x4B,
|
||||
VOLUMEDOWN = SPECIAL | 0x4C,
|
||||
VOLUMEMUTE = SPECIAL | 0x4D,
|
||||
VOLUMEUP = SPECIAL | 0x4E,
|
||||
MEDIAPLAY = SPECIAL | 0x54,
|
||||
MEDIASTOP = SPECIAL | 0x55,
|
||||
MEDIAPREVIOUS = SPECIAL | 0x56,
|
||||
MEDIANEXT = SPECIAL | 0x57,
|
||||
MEDIARECORD = SPECIAL | 0x58,
|
||||
HOMEPAGE = SPECIAL | 0x59,
|
||||
FAVORITES = SPECIAL | 0x5A,
|
||||
SEARCH = SPECIAL | 0x5B,
|
||||
STANDBY = SPECIAL | 0x5C,
|
||||
OPENURL = SPECIAL | 0x5D,
|
||||
LAUNCHMAIL = SPECIAL | 0x5E,
|
||||
LAUNCHMEDIA = SPECIAL | 0x5F,
|
||||
LAUNCH0 = SPECIAL | 0x60,
|
||||
LAUNCH1 = SPECIAL | 0x61,
|
||||
LAUNCH2 = SPECIAL | 0x62,
|
||||
LAUNCH3 = SPECIAL | 0x63,
|
||||
LAUNCH4 = SPECIAL | 0x64,
|
||||
LAUNCH5 = SPECIAL | 0x65,
|
||||
LAUNCH6 = SPECIAL | 0x66,
|
||||
LAUNCH7 = SPECIAL | 0x67,
|
||||
LAUNCH8 = SPECIAL | 0x68,
|
||||
LAUNCH9 = SPECIAL | 0x69,
|
||||
LAUNCHA = SPECIAL | 0x6A,
|
||||
LAUNCHB = SPECIAL | 0x6B,
|
||||
LAUNCHC = SPECIAL | 0x6C,
|
||||
LAUNCHD = SPECIAL | 0x6D,
|
||||
LAUNCHE = SPECIAL | 0x6E,
|
||||
LAUNCHF = SPECIAL | 0x6F,
|
||||
|
||||
GLOBE = SPECIAL | 0x70,
|
||||
KEYBOARD = SPECIAL | 0x71,
|
||||
JIS_EISU = SPECIAL | 0x72,
|
||||
JIS_KANA = SPECIAL | 0x73,
|
||||
|
||||
UNKNOWN = SPECIAL | 0x7FFFFF,
|
||||
|
||||
/* PRINTABLE LATIN 1 CODES */
|
||||
|
||||
SPACE = 0x0020,
|
||||
EXCLAM = 0x0021,
|
||||
QUOTEDBL = 0x0022,
|
||||
NUMBERSIGN = 0x0023,
|
||||
DOLLAR = 0x0024,
|
||||
PERCENT = 0x0025,
|
||||
AMPERSAND = 0x0026,
|
||||
APOSTROPHE = 0x0027,
|
||||
PARENLEFT = 0x0028,
|
||||
PARENRIGHT = 0x0029,
|
||||
ASTERISK = 0x002A,
|
||||
PLUS = 0x002B,
|
||||
COMMA = 0x002C,
|
||||
MINUS = 0x002D,
|
||||
PERIOD = 0x002E,
|
||||
SLASH = 0x002F,
|
||||
KEY_0 = 0x0030,
|
||||
KEY_1 = 0x0031,
|
||||
KEY_2 = 0x0032,
|
||||
KEY_3 = 0x0033,
|
||||
KEY_4 = 0x0034,
|
||||
KEY_5 = 0x0035,
|
||||
KEY_6 = 0x0036,
|
||||
KEY_7 = 0x0037,
|
||||
KEY_8 = 0x0038,
|
||||
KEY_9 = 0x0039,
|
||||
COLON = 0x003A,
|
||||
SEMICOLON = 0x003B,
|
||||
LESS = 0x003C,
|
||||
EQUAL = 0x003D,
|
||||
GREATER = 0x003E,
|
||||
QUESTION = 0x003F,
|
||||
AT = 0x0040,
|
||||
A = 0x0041,
|
||||
B = 0x0042,
|
||||
C = 0x0043,
|
||||
D = 0x0044,
|
||||
E = 0x0045,
|
||||
F = 0x0046,
|
||||
G = 0x0047,
|
||||
H = 0x0048,
|
||||
I = 0x0049,
|
||||
J = 0x004A,
|
||||
K = 0x004B,
|
||||
L = 0x004C,
|
||||
M = 0x004D,
|
||||
N = 0x004E,
|
||||
O = 0x004F,
|
||||
P = 0x0050,
|
||||
Q = 0x0051,
|
||||
R = 0x0052,
|
||||
S = 0x0053,
|
||||
T = 0x0054,
|
||||
U = 0x0055,
|
||||
V = 0x0056,
|
||||
W = 0x0057,
|
||||
X = 0x0058,
|
||||
Y = 0x0059,
|
||||
Z = 0x005A,
|
||||
BRACKETLEFT = 0x005B,
|
||||
BACKSLASH = 0x005C,
|
||||
BRACKETRIGHT = 0x005D,
|
||||
ASCIICIRCUM = 0x005E,
|
||||
UNDERSCORE = 0x005F,
|
||||
QUOTELEFT = 0x0060,
|
||||
BRACELEFT = 0x007B,
|
||||
BAR = 0x007C,
|
||||
BRACERIGHT = 0x007D,
|
||||
ASCIITILDE = 0x007E,
|
||||
YEN = 0x00A5,
|
||||
SECTION = 0x00A7,
|
||||
};
|
||||
|
||||
enum class KeyModifierMask {
|
||||
CODE_MASK = ((1 << 23) - 1), ///< Apply this mask to any keycode to remove modifiers.
|
||||
MODIFIER_MASK = (0x7F << 24), ///< Apply this mask to isolate modifiers.
|
||||
//RESERVED = (1 << 23),
|
||||
CMD_OR_CTRL = (1 << 24),
|
||||
SHIFT = (1 << 25),
|
||||
ALT = (1 << 26),
|
||||
META = (1 << 27),
|
||||
CTRL = (1 << 28),
|
||||
KPAD = (1 << 29),
|
||||
GROUP_SWITCH = (1 << 30)
|
||||
};
|
||||
|
||||
enum class KeyLocation {
|
||||
UNSPECIFIED,
|
||||
LEFT,
|
||||
RIGHT
|
||||
};
|
||||
|
||||
// To avoid having unnecessary operators, only define the ones that are needed.
|
||||
|
||||
constexpr Key operator-(uint32_t a, Key b) {
|
||||
return (Key)(a - (uint32_t)b);
|
||||
}
|
||||
|
||||
constexpr Key &operator-=(Key &a, int b) {
|
||||
a = static_cast<Key>(static_cast<int>(a) - static_cast<int>(b));
|
||||
return a;
|
||||
}
|
||||
|
||||
constexpr Key operator+(Key a, int b) {
|
||||
return (Key)((int)a + (int)b);
|
||||
}
|
||||
|
||||
constexpr Key operator+(Key a, Key b) {
|
||||
return (Key)((int)a + (int)b);
|
||||
}
|
||||
|
||||
constexpr Key operator-(Key a, Key b) {
|
||||
return (Key)((int)a - (int)b);
|
||||
}
|
||||
|
||||
constexpr Key operator&(Key a, Key b) {
|
||||
return (Key)((int)a & (int)b);
|
||||
}
|
||||
|
||||
constexpr Key operator|(Key a, Key b) {
|
||||
return (Key)((int)a | (int)b);
|
||||
}
|
||||
|
||||
constexpr Key &operator|=(Key &a, Key b) {
|
||||
a = static_cast<Key>(static_cast<int>(a) | static_cast<int>(b));
|
||||
return a;
|
||||
}
|
||||
|
||||
constexpr Key &operator|=(Key &a, KeyModifierMask b) {
|
||||
a = static_cast<Key>(static_cast<int>(a) | static_cast<int>(b));
|
||||
return a;
|
||||
}
|
||||
|
||||
constexpr Key &operator&=(Key &a, KeyModifierMask b) {
|
||||
a = static_cast<Key>(static_cast<int>(a) & static_cast<int>(b));
|
||||
return a;
|
||||
}
|
||||
|
||||
constexpr Key operator|(Key a, KeyModifierMask b) {
|
||||
return (Key)((int)a | (int)b);
|
||||
}
|
||||
|
||||
constexpr Key operator&(Key a, KeyModifierMask b) {
|
||||
return (Key)((int)a & (int)b);
|
||||
}
|
||||
|
||||
constexpr Key operator+(KeyModifierMask a, Key b) {
|
||||
return (Key)((int)a + (int)b);
|
||||
}
|
||||
|
||||
constexpr Key operator|(KeyModifierMask a, Key b) {
|
||||
return (Key)((int)a | (int)b);
|
||||
}
|
||||
|
||||
constexpr KeyModifierMask operator+(KeyModifierMask a, KeyModifierMask b) {
|
||||
return (KeyModifierMask)((int)a + (int)b);
|
||||
}
|
||||
|
||||
constexpr KeyModifierMask operator|(KeyModifierMask a, KeyModifierMask b) {
|
||||
return (KeyModifierMask)((int)a | (int)b);
|
||||
}
|
||||
|
||||
String keycode_get_string(Key p_code);
|
||||
bool keycode_has_unicode(Key p_keycode);
|
||||
Key find_keycode(const String &p_codestr);
|
||||
const char *find_keycode_name(Key p_keycode);
|
||||
int keycode_get_count();
|
||||
int keycode_get_value_by_index(int p_index);
|
||||
const char *keycode_get_name_by_index(int p_index);
|
||||
|
||||
char32_t fix_unicode(char32_t p_char);
|
||||
Key fix_keycode(char32_t p_char, Key p_key);
|
||||
Key fix_key_label(char32_t p_char, Key p_key);
|
75
core/os/main_loop.cpp
Normal file
75
core/os/main_loop.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/**************************************************************************/
|
||||
/* main_loop.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 "main_loop.h"
|
||||
|
||||
void MainLoop::_bind_methods() {
|
||||
BIND_CONSTANT(NOTIFICATION_OS_MEMORY_WARNING);
|
||||
BIND_CONSTANT(NOTIFICATION_TRANSLATION_CHANGED);
|
||||
BIND_CONSTANT(NOTIFICATION_WM_ABOUT);
|
||||
BIND_CONSTANT(NOTIFICATION_CRASH);
|
||||
BIND_CONSTANT(NOTIFICATION_OS_IME_UPDATE);
|
||||
BIND_CONSTANT(NOTIFICATION_APPLICATION_RESUMED);
|
||||
BIND_CONSTANT(NOTIFICATION_APPLICATION_PAUSED);
|
||||
BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_IN);
|
||||
BIND_CONSTANT(NOTIFICATION_APPLICATION_FOCUS_OUT);
|
||||
BIND_CONSTANT(NOTIFICATION_TEXT_SERVER_CHANGED);
|
||||
|
||||
ADD_SIGNAL(MethodInfo("on_request_permissions_result", PropertyInfo(Variant::STRING, "permission"), PropertyInfo(Variant::BOOL, "granted")));
|
||||
|
||||
GDVIRTUAL_BIND(_initialize);
|
||||
GDVIRTUAL_BIND(_physics_process, "delta");
|
||||
GDVIRTUAL_BIND(_process, "delta");
|
||||
GDVIRTUAL_BIND(_finalize);
|
||||
}
|
||||
|
||||
void MainLoop::initialize() {
|
||||
GDVIRTUAL_CALL(_initialize);
|
||||
}
|
||||
|
||||
bool MainLoop::physics_process(double p_time) {
|
||||
bool quit = false;
|
||||
GDVIRTUAL_CALL(_physics_process, p_time, quit);
|
||||
return quit;
|
||||
}
|
||||
|
||||
bool MainLoop::process(double p_time) {
|
||||
bool quit = false;
|
||||
GDVIRTUAL_CALL(_process, p_time, quit);
|
||||
return quit;
|
||||
}
|
||||
|
||||
void MainLoop::finalize() {
|
||||
GDVIRTUAL_CALL(_finalize);
|
||||
|
||||
if (get_script_instance()) {
|
||||
set_script(Variant()); //clear script
|
||||
}
|
||||
}
|
72
core/os/main_loop.h
Normal file
72
core/os/main_loop.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/**************************************************************************/
|
||||
/* main_loop.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/input/input_event.h"
|
||||
#include "core/object/gdvirtual.gen.inc"
|
||||
#include "core/object/ref_counted.h"
|
||||
|
||||
class MainLoop : public Object {
|
||||
GDCLASS(MainLoop, Object);
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
GDVIRTUAL0(_initialize)
|
||||
GDVIRTUAL1R(bool, _physics_process, double)
|
||||
GDVIRTUAL1R(bool, _process, double)
|
||||
GDVIRTUAL0(_finalize)
|
||||
|
||||
public:
|
||||
enum {
|
||||
//make sure these are replicated in Node
|
||||
NOTIFICATION_OS_MEMORY_WARNING = 2009,
|
||||
NOTIFICATION_TRANSLATION_CHANGED = 2010,
|
||||
NOTIFICATION_WM_ABOUT = 2011,
|
||||
NOTIFICATION_CRASH = 2012,
|
||||
NOTIFICATION_OS_IME_UPDATE = 2013,
|
||||
NOTIFICATION_APPLICATION_RESUMED = 2014,
|
||||
NOTIFICATION_APPLICATION_PAUSED = 2015,
|
||||
NOTIFICATION_APPLICATION_FOCUS_IN = 2016,
|
||||
NOTIFICATION_APPLICATION_FOCUS_OUT = 2017,
|
||||
NOTIFICATION_TEXT_SERVER_CHANGED = 2018,
|
||||
};
|
||||
|
||||
virtual void initialize();
|
||||
virtual void iteration_prepare() {}
|
||||
virtual bool physics_process(double p_time);
|
||||
virtual void iteration_end() {}
|
||||
virtual bool process(double p_time);
|
||||
virtual void finalize();
|
||||
|
||||
MainLoop() {}
|
||||
virtual ~MainLoop() {}
|
||||
};
|
233
core/os/memory.cpp
Normal file
233
core/os/memory.cpp
Normal file
@@ -0,0 +1,233 @@
|
||||
/**************************************************************************/
|
||||
/* memory.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 "memory.h"
|
||||
|
||||
#include "core/templates/safe_refcount.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
void *operator new(size_t p_size, const char *p_description) {
|
||||
return Memory::alloc_static(p_size, false);
|
||||
}
|
||||
|
||||
void *operator new(size_t p_size, void *(*p_allocfunc)(size_t p_size)) {
|
||||
return p_allocfunc(p_size);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
void operator delete(void *p_mem, const char *p_description) {
|
||||
CRASH_NOW_MSG("Call to placement delete should not happen.");
|
||||
}
|
||||
|
||||
void operator delete(void *p_mem, void *(*p_allocfunc)(size_t p_size)) {
|
||||
CRASH_NOW_MSG("Call to placement delete should not happen.");
|
||||
}
|
||||
|
||||
void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_description) {
|
||||
CRASH_NOW_MSG("Call to placement delete should not happen.");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
SafeNumeric<uint64_t> Memory::mem_usage;
|
||||
SafeNumeric<uint64_t> Memory::max_usage;
|
||||
#endif
|
||||
|
||||
void *Memory::alloc_aligned_static(size_t p_bytes, size_t p_alignment) {
|
||||
DEV_ASSERT(is_power_of_2(p_alignment));
|
||||
|
||||
void *p1, *p2;
|
||||
if ((p1 = (void *)malloc(p_bytes + p_alignment - 1 + sizeof(uint32_t))) == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
p2 = (void *)(((uintptr_t)p1 + sizeof(uint32_t) + p_alignment - 1) & ~((p_alignment)-1));
|
||||
*((uint32_t *)p2 - 1) = (uint32_t)((uintptr_t)p2 - (uintptr_t)p1);
|
||||
return p2;
|
||||
}
|
||||
|
||||
void *Memory::realloc_aligned_static(void *p_memory, size_t p_bytes, size_t p_prev_bytes, size_t p_alignment) {
|
||||
if (p_memory == nullptr) {
|
||||
return alloc_aligned_static(p_bytes, p_alignment);
|
||||
}
|
||||
|
||||
void *ret = alloc_aligned_static(p_bytes, p_alignment);
|
||||
if (ret) {
|
||||
memcpy(ret, p_memory, p_prev_bytes);
|
||||
}
|
||||
free_aligned_static(p_memory);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Memory::free_aligned_static(void *p_memory) {
|
||||
uint32_t offset = *((uint32_t *)p_memory - 1);
|
||||
void *p = (void *)((uint8_t *)p_memory - offset);
|
||||
free(p);
|
||||
}
|
||||
|
||||
template <bool p_ensure_zero>
|
||||
void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool prepad = true;
|
||||
#else
|
||||
bool prepad = p_pad_align;
|
||||
#endif
|
||||
|
||||
void *mem;
|
||||
if constexpr (p_ensure_zero) {
|
||||
mem = calloc(1, p_bytes + (prepad ? DATA_OFFSET : 0));
|
||||
} else {
|
||||
mem = malloc(p_bytes + (prepad ? DATA_OFFSET : 0));
|
||||
}
|
||||
|
||||
ERR_FAIL_NULL_V(mem, nullptr);
|
||||
|
||||
if (prepad) {
|
||||
uint8_t *s8 = (uint8_t *)mem;
|
||||
|
||||
uint64_t *s = (uint64_t *)(s8 + SIZE_OFFSET);
|
||||
*s = p_bytes;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
uint64_t new_mem_usage = mem_usage.add(p_bytes);
|
||||
max_usage.exchange_if_greater(new_mem_usage);
|
||||
#endif
|
||||
return s8 + DATA_OFFSET;
|
||||
} else {
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
|
||||
template void *Memory::alloc_static<true>(size_t p_bytes, bool p_pad_align);
|
||||
template void *Memory::alloc_static<false>(size_t p_bytes, bool p_pad_align);
|
||||
|
||||
void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) {
|
||||
if (p_memory == nullptr) {
|
||||
return alloc_static(p_bytes, p_pad_align);
|
||||
}
|
||||
|
||||
uint8_t *mem = (uint8_t *)p_memory;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool prepad = true;
|
||||
#else
|
||||
bool prepad = p_pad_align;
|
||||
#endif
|
||||
|
||||
if (prepad) {
|
||||
mem -= DATA_OFFSET;
|
||||
uint64_t *s = (uint64_t *)(mem + SIZE_OFFSET);
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (p_bytes > *s) {
|
||||
uint64_t new_mem_usage = mem_usage.add(p_bytes - *s);
|
||||
max_usage.exchange_if_greater(new_mem_usage);
|
||||
} else {
|
||||
mem_usage.sub(*s - p_bytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (p_bytes == 0) {
|
||||
free(mem);
|
||||
return nullptr;
|
||||
} else {
|
||||
*s = p_bytes;
|
||||
|
||||
mem = (uint8_t *)realloc(mem, p_bytes + DATA_OFFSET);
|
||||
ERR_FAIL_NULL_V(mem, nullptr);
|
||||
|
||||
s = (uint64_t *)(mem + SIZE_OFFSET);
|
||||
|
||||
*s = p_bytes;
|
||||
|
||||
return mem + DATA_OFFSET;
|
||||
}
|
||||
} else {
|
||||
mem = (uint8_t *)realloc(mem, p_bytes);
|
||||
|
||||
ERR_FAIL_COND_V(mem == nullptr && p_bytes > 0, nullptr);
|
||||
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
|
||||
void Memory::free_static(void *p_ptr, bool p_pad_align) {
|
||||
ERR_FAIL_NULL(p_ptr);
|
||||
|
||||
uint8_t *mem = (uint8_t *)p_ptr;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool prepad = true;
|
||||
#else
|
||||
bool prepad = p_pad_align;
|
||||
#endif
|
||||
|
||||
if (prepad) {
|
||||
mem -= DATA_OFFSET;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
uint64_t *s = (uint64_t *)(mem + SIZE_OFFSET);
|
||||
mem_usage.sub(*s);
|
||||
#endif
|
||||
|
||||
free(mem);
|
||||
} else {
|
||||
free(mem);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Memory::get_mem_available() {
|
||||
return -1; // 0xFFFF...
|
||||
}
|
||||
|
||||
uint64_t Memory::get_mem_usage() {
|
||||
#ifdef DEBUG_ENABLED
|
||||
return mem_usage.get();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t Memory::get_mem_max_usage() {
|
||||
#ifdef DEBUG_ENABLED
|
||||
return max_usage.get();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
_GlobalNil::_GlobalNil() {
|
||||
left = this;
|
||||
right = this;
|
||||
parent = this;
|
||||
}
|
||||
|
||||
_GlobalNil _GlobalNilClass::_nil;
|
259
core/os/memory.h
Normal file
259
core/os/memory.h
Normal file
@@ -0,0 +1,259 @@
|
||||
/**************************************************************************/
|
||||
/* memory.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/error/error_macros.h"
|
||||
#include "core/templates/safe_refcount.h"
|
||||
|
||||
#include <new> // IWYU pragma: keep // `new` operators.
|
||||
#include <type_traits>
|
||||
|
||||
class Memory {
|
||||
#ifdef DEBUG_ENABLED
|
||||
static SafeNumeric<uint64_t> mem_usage;
|
||||
static SafeNumeric<uint64_t> max_usage;
|
||||
#endif
|
||||
|
||||
public:
|
||||
// Alignment: ↓ max_align_t ↓ uint64_t ↓ max_align_t
|
||||
// ┌─────────────────┬──┬────────────────┬──┬───────────...
|
||||
// │ uint64_t │░░│ uint64_t │░░│ T[]
|
||||
// │ alloc size │░░│ element count │░░│ data
|
||||
// └─────────────────┴──┴────────────────┴──┴───────────...
|
||||
// Offset: ↑ SIZE_OFFSET ↑ ELEMENT_OFFSET ↑ DATA_OFFSET
|
||||
|
||||
static constexpr size_t SIZE_OFFSET = 0;
|
||||
static constexpr size_t ELEMENT_OFFSET = ((SIZE_OFFSET + sizeof(uint64_t)) % alignof(uint64_t) == 0) ? (SIZE_OFFSET + sizeof(uint64_t)) : ((SIZE_OFFSET + sizeof(uint64_t)) + alignof(uint64_t) - ((SIZE_OFFSET + sizeof(uint64_t)) % alignof(uint64_t)));
|
||||
static constexpr size_t DATA_OFFSET = ((ELEMENT_OFFSET + sizeof(uint64_t)) % alignof(max_align_t) == 0) ? (ELEMENT_OFFSET + sizeof(uint64_t)) : ((ELEMENT_OFFSET + sizeof(uint64_t)) + alignof(max_align_t) - ((ELEMENT_OFFSET + sizeof(uint64_t)) % alignof(max_align_t)));
|
||||
|
||||
template <bool p_ensure_zero = false>
|
||||
static void *alloc_static(size_t p_bytes, bool p_pad_align = false);
|
||||
_FORCE_INLINE_ static void *alloc_static_zeroed(size_t p_bytes, bool p_pad_align = false) { return alloc_static<true>(p_bytes, p_pad_align); }
|
||||
static void *realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align = false);
|
||||
static void free_static(void *p_ptr, bool p_pad_align = false);
|
||||
|
||||
// ↓ return value of alloc_aligned_static
|
||||
// ┌─────────────────┬─────────┬─────────┬──────────────────┐
|
||||
// │ padding (up to │ uint32_t│ void* │ padding (up to │
|
||||
// │ p_alignment - 1)│ offset │ p_bytes │ p_alignment - 1) │
|
||||
// └─────────────────┴─────────┴─────────┴──────────────────┘
|
||||
//
|
||||
// alloc_aligned_static will allocate p_bytes + p_alignment - 1 + sizeof(uint32_t) and
|
||||
// then offset the pointer until alignment is satisfied.
|
||||
//
|
||||
// This offset is stored before the start of the returned ptr so we can retrieve the original/real
|
||||
// start of the ptr in order to free it.
|
||||
//
|
||||
// The rest is wasted as padding in the beginning and end of the ptr. The sum of padding at
|
||||
// both start and end of the block must add exactly to p_alignment - 1.
|
||||
//
|
||||
// p_alignment MUST be a power of 2.
|
||||
static void *alloc_aligned_static(size_t p_bytes, size_t p_alignment);
|
||||
static void *realloc_aligned_static(void *p_memory, size_t p_bytes, size_t p_prev_bytes, size_t p_alignment);
|
||||
// Pass the ptr returned by alloc_aligned_static to free it.
|
||||
// e.g.
|
||||
// void *data = realloc_aligned_static( bytes, 16 );
|
||||
// free_aligned_static( data );
|
||||
static void free_aligned_static(void *p_memory);
|
||||
|
||||
static uint64_t get_mem_available();
|
||||
static uint64_t get_mem_usage();
|
||||
static uint64_t get_mem_max_usage();
|
||||
};
|
||||
|
||||
class DefaultAllocator {
|
||||
public:
|
||||
_FORCE_INLINE_ static void *alloc(size_t p_memory) { return Memory::alloc_static(p_memory, false); }
|
||||
_FORCE_INLINE_ static void free(void *p_ptr) { Memory::free_static(p_ptr, false); }
|
||||
};
|
||||
|
||||
void *operator new(size_t p_size, const char *p_description); ///< operator new that takes a description and uses MemoryStaticPool
|
||||
void *operator new(size_t p_size, void *(*p_allocfunc)(size_t p_size)); ///< operator new that takes a description and uses MemoryStaticPool
|
||||
|
||||
void *operator new(size_t p_size, void *p_pointer, size_t check, const char *p_description); ///< operator new that takes a description and uses a pointer to the preallocated memory
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// When compiling with VC++ 2017, the above declarations of placement new generate many irrelevant warnings (C4291).
|
||||
// The purpose of the following definitions is to muffle these warnings, not to provide a usable implementation of placement delete.
|
||||
void operator delete(void *p_mem, const char *p_description);
|
||||
void operator delete(void *p_mem, void *(*p_allocfunc)(size_t p_size));
|
||||
void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_description);
|
||||
#endif
|
||||
|
||||
#define memalloc(m_size) Memory::alloc_static(m_size)
|
||||
#define memalloc_zeroed(m_size) Memory::alloc_static_zeroed(m_size)
|
||||
#define memrealloc(m_mem, m_size) Memory::realloc_static(m_mem, m_size)
|
||||
#define memfree(m_mem) Memory::free_static(m_mem)
|
||||
|
||||
_ALWAYS_INLINE_ void postinitialize_handler(void *) {}
|
||||
|
||||
template <typename T>
|
||||
_ALWAYS_INLINE_ T *_post_initialize(T *p_obj) {
|
||||
postinitialize_handler(p_obj);
|
||||
return p_obj;
|
||||
}
|
||||
|
||||
#define memnew(m_class) _post_initialize(::new ("") m_class)
|
||||
|
||||
#define memnew_allocator(m_class, m_allocator) _post_initialize(::new (m_allocator::alloc) m_class)
|
||||
#define memnew_placement(m_placement, m_class) _post_initialize(::new (m_placement) m_class)
|
||||
|
||||
_ALWAYS_INLINE_ bool predelete_handler(void *) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void memdelete(T *p_class) {
|
||||
if (!predelete_handler(p_class)) {
|
||||
return; // doesn't want to be deleted
|
||||
}
|
||||
if constexpr (!std::is_trivially_destructible_v<T>) {
|
||||
p_class->~T();
|
||||
}
|
||||
|
||||
Memory::free_static(p_class, false);
|
||||
}
|
||||
|
||||
template <typename T, typename A>
|
||||
void memdelete_allocator(T *p_class) {
|
||||
if (!predelete_handler(p_class)) {
|
||||
return; // doesn't want to be deleted
|
||||
}
|
||||
if constexpr (!std::is_trivially_destructible_v<T>) {
|
||||
p_class->~T();
|
||||
}
|
||||
|
||||
A::free(p_class);
|
||||
}
|
||||
|
||||
#define memdelete_notnull(m_v) \
|
||||
{ \
|
||||
if (m_v) { \
|
||||
memdelete(m_v); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define memnew_arr(m_class, m_count) memnew_arr_template<m_class>(m_count)
|
||||
|
||||
_FORCE_INLINE_ uint64_t *_get_element_count_ptr(uint8_t *p_ptr) {
|
||||
return (uint64_t *)(p_ptr - Memory::DATA_OFFSET + Memory::ELEMENT_OFFSET);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *memnew_arr_template(size_t p_elements) {
|
||||
if (p_elements == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
/** overloading operator new[] cannot be done , because it may not return the real allocated address (it may pad the 'element count' before the actual array). Because of that, it must be done by hand. This is the
|
||||
same strategy used by std::vector, and the Vector class, so it should be safe.*/
|
||||
|
||||
size_t len = sizeof(T) * p_elements;
|
||||
uint8_t *mem = (uint8_t *)Memory::alloc_static(len, true);
|
||||
T *failptr = nullptr; //get rid of a warning
|
||||
ERR_FAIL_NULL_V(mem, failptr);
|
||||
|
||||
uint64_t *_elem_count_ptr = _get_element_count_ptr(mem);
|
||||
*(_elem_count_ptr) = p_elements;
|
||||
|
||||
if constexpr (!std::is_trivially_constructible_v<T>) {
|
||||
T *elems = (T *)mem;
|
||||
|
||||
/* call operator new */
|
||||
for (size_t i = 0; i < p_elements; i++) {
|
||||
::new (&elems[i]) T;
|
||||
}
|
||||
}
|
||||
|
||||
return (T *)mem;
|
||||
}
|
||||
|
||||
// Fast alternative to a loop constructor pattern.
|
||||
template <typename T>
|
||||
_FORCE_INLINE_ void memnew_arr_placement(T *p_start, size_t p_num) {
|
||||
if constexpr (is_zero_constructible_v<T>) {
|
||||
// Can optimize with memset.
|
||||
memset(static_cast<void *>(p_start), 0, p_num * sizeof(T));
|
||||
} else {
|
||||
// Need to use a for loop.
|
||||
for (size_t i = 0; i < p_num; i++) {
|
||||
memnew_placement(p_start + i, T());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wonders of having own array functions, you can actually check the length of
|
||||
* an allocated-with memnew_arr() array
|
||||
*/
|
||||
|
||||
template <typename T>
|
||||
size_t memarr_len(const T *p_class) {
|
||||
uint8_t *ptr = (uint8_t *)p_class;
|
||||
uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr);
|
||||
return *(_elem_count_ptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void memdelete_arr(T *p_class) {
|
||||
uint8_t *ptr = (uint8_t *)p_class;
|
||||
|
||||
if constexpr (!std::is_trivially_destructible_v<T>) {
|
||||
uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr);
|
||||
uint64_t elem_count = *(_elem_count_ptr);
|
||||
|
||||
for (uint64_t i = 0; i < elem_count; i++) {
|
||||
p_class[i].~T();
|
||||
}
|
||||
}
|
||||
|
||||
Memory::free_static(ptr, true);
|
||||
}
|
||||
|
||||
struct _GlobalNil {
|
||||
int color = 1;
|
||||
_GlobalNil *right = nullptr;
|
||||
_GlobalNil *left = nullptr;
|
||||
_GlobalNil *parent = nullptr;
|
||||
|
||||
_GlobalNil();
|
||||
};
|
||||
|
||||
struct _GlobalNilClass {
|
||||
static _GlobalNil _nil;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class DefaultTypedAllocator {
|
||||
public:
|
||||
template <typename... Args>
|
||||
_FORCE_INLINE_ T *new_allocation(const Args &&...p_args) { return memnew(T(p_args...)); }
|
||||
_FORCE_INLINE_ void delete_allocation(T *p_allocation) { memdelete(p_allocation); }
|
||||
};
|
204
core/os/midi_driver.cpp
Normal file
204
core/os/midi_driver.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
/**************************************************************************/
|
||||
/* midi_driver.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 "midi_driver.h"
|
||||
|
||||
#include "core/input/input.h"
|
||||
|
||||
uint8_t MIDIDriver::last_received_message = 0x00;
|
||||
MIDIDriver *MIDIDriver::singleton = nullptr;
|
||||
MIDIDriver *MIDIDriver::get_singleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
MIDIDriver::MIDIDriver() {
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
MIDIDriver::MessageCategory MIDIDriver::Parser::category(uint8_t p_midi_fragment) {
|
||||
if (p_midi_fragment >= 0xf8) {
|
||||
return MessageCategory::RealTime;
|
||||
} else if (p_midi_fragment >= 0xf0) {
|
||||
// System Exclusive begin/end are specified as System Common Category
|
||||
// messages, but we separate them here and give them their own categories
|
||||
// as their behavior is significantly different.
|
||||
if (p_midi_fragment == 0xf0) {
|
||||
return MessageCategory::SysExBegin;
|
||||
} else if (p_midi_fragment == 0xf7) {
|
||||
return MessageCategory::SysExEnd;
|
||||
}
|
||||
return MessageCategory::SystemCommon;
|
||||
} else if (p_midi_fragment >= 0x80) {
|
||||
return MessageCategory::Voice;
|
||||
}
|
||||
return MessageCategory::Data;
|
||||
}
|
||||
|
||||
MIDIMessage MIDIDriver::Parser::status_to_msg_enum(uint8_t p_status_byte) {
|
||||
if (p_status_byte & 0x80) {
|
||||
if (p_status_byte < 0xf0) {
|
||||
return MIDIMessage(p_status_byte >> 4);
|
||||
} else {
|
||||
return MIDIMessage(p_status_byte);
|
||||
}
|
||||
}
|
||||
return MIDIMessage::NONE;
|
||||
}
|
||||
|
||||
size_t MIDIDriver::Parser::expected_data(uint8_t p_status_byte) {
|
||||
return expected_data(status_to_msg_enum(p_status_byte));
|
||||
}
|
||||
|
||||
size_t MIDIDriver::Parser::expected_data(MIDIMessage p_msg_type) {
|
||||
switch (p_msg_type) {
|
||||
case MIDIMessage::NOTE_OFF:
|
||||
case MIDIMessage::NOTE_ON:
|
||||
case MIDIMessage::AFTERTOUCH:
|
||||
case MIDIMessage::CONTROL_CHANGE:
|
||||
case MIDIMessage::PITCH_BEND:
|
||||
case MIDIMessage::SONG_POSITION_POINTER:
|
||||
return 2;
|
||||
case MIDIMessage::PROGRAM_CHANGE:
|
||||
case MIDIMessage::CHANNEL_PRESSURE:
|
||||
case MIDIMessage::QUARTER_FRAME:
|
||||
case MIDIMessage::SONG_SELECT:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t MIDIDriver::Parser::channel(uint8_t p_status_byte) {
|
||||
if (category(p_status_byte) == MessageCategory::Voice) {
|
||||
return p_status_byte & 0x0f;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MIDIDriver::send_event(int p_device_index, uint8_t p_status,
|
||||
const uint8_t *p_data, size_t p_data_len) {
|
||||
const MIDIMessage msg = Parser::status_to_msg_enum(p_status);
|
||||
ERR_FAIL_COND(p_data_len < Parser::expected_data(msg));
|
||||
|
||||
Ref<InputEventMIDI> event;
|
||||
event.instantiate();
|
||||
event->set_device(p_device_index);
|
||||
event->set_channel(Parser::channel(p_status));
|
||||
event->set_message(msg);
|
||||
switch (msg) {
|
||||
case MIDIMessage::NOTE_OFF:
|
||||
case MIDIMessage::NOTE_ON:
|
||||
event->set_pitch(p_data[0]);
|
||||
event->set_velocity(p_data[1]);
|
||||
break;
|
||||
case MIDIMessage::AFTERTOUCH:
|
||||
event->set_pitch(p_data[0]);
|
||||
event->set_pressure(p_data[1]);
|
||||
break;
|
||||
case MIDIMessage::CONTROL_CHANGE:
|
||||
event->set_controller_number(p_data[0]);
|
||||
event->set_controller_value(p_data[1]);
|
||||
break;
|
||||
case MIDIMessage::PROGRAM_CHANGE:
|
||||
event->set_instrument(p_data[0]);
|
||||
break;
|
||||
case MIDIMessage::CHANNEL_PRESSURE:
|
||||
event->set_pressure(p_data[0]);
|
||||
break;
|
||||
case MIDIMessage::PITCH_BEND:
|
||||
event->set_pitch((p_data[1] << 7) | p_data[0]);
|
||||
break;
|
||||
// QUARTER_FRAME, SONG_POSITION_POINTER, and SONG_SELECT not yet implemented.
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Input::get_singleton()->parse_input_event(event);
|
||||
}
|
||||
|
||||
void MIDIDriver::Parser::parse_fragment(uint8_t p_fragment) {
|
||||
switch (category(p_fragment)) {
|
||||
case MessageCategory::RealTime:
|
||||
// Real-Time messages are single byte messages that can
|
||||
// occur at any point and do not interrupt other messages.
|
||||
// We pass them straight through.
|
||||
MIDIDriver::send_event(device_index, p_fragment);
|
||||
break;
|
||||
|
||||
case MessageCategory::SysExBegin:
|
||||
status_byte = p_fragment;
|
||||
skipping_sys_ex = true;
|
||||
break;
|
||||
|
||||
case MessageCategory::SysExEnd:
|
||||
status_byte = 0;
|
||||
skipping_sys_ex = false;
|
||||
break;
|
||||
|
||||
case MessageCategory::Voice:
|
||||
case MessageCategory::SystemCommon:
|
||||
skipping_sys_ex = false; // If we were in SysEx, assume it was aborted.
|
||||
received_data_len = 0;
|
||||
status_byte = 0;
|
||||
ERR_FAIL_COND(expected_data(p_fragment) > DATA_BUFFER_SIZE);
|
||||
if (expected_data(p_fragment) == 0) {
|
||||
// No data bytes needed, post it now.
|
||||
MIDIDriver::send_event(device_index, p_fragment);
|
||||
} else {
|
||||
status_byte = p_fragment;
|
||||
}
|
||||
break;
|
||||
|
||||
case MessageCategory::Data:
|
||||
// We don't currently process SysEx messages, so ignore their data.
|
||||
if (!skipping_sys_ex) {
|
||||
const size_t expected = expected_data(status_byte);
|
||||
if (received_data_len < expected) {
|
||||
data_buffer[received_data_len] = p_fragment;
|
||||
received_data_len++;
|
||||
if (received_data_len == expected) {
|
||||
MIDIDriver::send_event(device_index, status_byte,
|
||||
data_buffer, expected);
|
||||
received_data_len = 0;
|
||||
// Voice messages can use 'running status', sending further
|
||||
// messages without resending their status byte.
|
||||
// For other messages types we clear the cached status byte.
|
||||
if (category(status_byte) != MessageCategory::Voice) {
|
||||
status_byte = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PackedStringArray MIDIDriver::get_connected_inputs() const {
|
||||
return connected_input_names;
|
||||
}
|
111
core/os/midi_driver.h
Normal file
111
core/os/midi_driver.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/**************************************************************************/
|
||||
/* midi_driver.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/typedefs.h"
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
/**
|
||||
* Multi-Platform abstraction for accessing to MIDI.
|
||||
*/
|
||||
|
||||
class MIDIDriver {
|
||||
static MIDIDriver *singleton;
|
||||
static uint8_t last_received_message;
|
||||
|
||||
protected:
|
||||
// Categories of message for parser logic.
|
||||
enum class MessageCategory {
|
||||
Data,
|
||||
Voice,
|
||||
SysExBegin,
|
||||
SystemCommon, // excluding System Exclusive Begin/End
|
||||
SysExEnd,
|
||||
RealTime,
|
||||
};
|
||||
|
||||
// Convert midi data to InputEventMIDI and send it to Input.
|
||||
// p_data_len is the length of the buffer passed at p_data, this must be
|
||||
// at least equal to the data required by the passed message type, but
|
||||
// may be larger. Only the required data will be read.
|
||||
static void send_event(int p_device_index, uint8_t p_status,
|
||||
const uint8_t *p_data = nullptr, size_t p_data_len = 0);
|
||||
|
||||
class Parser {
|
||||
public:
|
||||
Parser() = default;
|
||||
Parser(int p_device_index) :
|
||||
device_index{ p_device_index } {}
|
||||
virtual ~Parser() = default;
|
||||
|
||||
// Push a byte of MIDI stream. Any completed messages will be
|
||||
// forwarded to MIDIDriver::send_event.
|
||||
void parse_fragment(uint8_t p_fragment);
|
||||
|
||||
static MessageCategory category(uint8_t p_midi_fragment);
|
||||
|
||||
// If the byte is a Voice Message status byte return the contained
|
||||
// channel number, otherwise zero.
|
||||
static uint8_t channel(uint8_t p_status_byte);
|
||||
|
||||
// If the byte is a status byte for a message with a fixed number of
|
||||
// additional data bytes, return the number expected, otherwise zero.
|
||||
static size_t expected_data(uint8_t p_status_byte);
|
||||
static size_t expected_data(MIDIMessage p_msg_type);
|
||||
|
||||
// If the fragment is a status byte return the message type
|
||||
// represented, otherwise MIDIMessage::NONE.
|
||||
static MIDIMessage status_to_msg_enum(uint8_t p_status_byte);
|
||||
|
||||
private:
|
||||
int device_index = 0;
|
||||
|
||||
static constexpr size_t DATA_BUFFER_SIZE = 2;
|
||||
|
||||
uint8_t status_byte = 0;
|
||||
uint8_t data_buffer[DATA_BUFFER_SIZE] = { 0 };
|
||||
size_t received_data_len = 0;
|
||||
bool skipping_sys_ex = false;
|
||||
};
|
||||
|
||||
PackedStringArray connected_input_names;
|
||||
|
||||
public:
|
||||
static MIDIDriver *get_singleton();
|
||||
|
||||
MIDIDriver();
|
||||
virtual ~MIDIDriver() = default;
|
||||
|
||||
virtual Error open() = 0;
|
||||
virtual void close() = 0;
|
||||
|
||||
PackedStringArray get_connected_inputs() const;
|
||||
};
|
50
core/os/mutex.cpp
Normal file
50
core/os/mutex.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/**************************************************************************/
|
||||
/* mutex.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 "mutex.h"
|
||||
|
||||
static Mutex _global_mutex;
|
||||
|
||||
void _global_lock() {
|
||||
_global_mutex.lock();
|
||||
}
|
||||
|
||||
void _global_unlock() {
|
||||
_global_mutex.unlock();
|
||||
}
|
||||
|
||||
#ifdef THREADS_ENABLED
|
||||
|
||||
template class MutexImpl<THREADING_NAMESPACE::recursive_mutex>;
|
||||
template class MutexImpl<THREADING_NAMESPACE::mutex>;
|
||||
template class MutexLock<MutexImpl<THREADING_NAMESPACE::recursive_mutex>>;
|
||||
template class MutexLock<MutexImpl<THREADING_NAMESPACE::mutex>>;
|
||||
|
||||
#endif
|
128
core/os/mutex.h
Normal file
128
core/os/mutex.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/**************************************************************************/
|
||||
/* mutex.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#ifdef MINGW_ENABLED
|
||||
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
|
||||
#include "thirdparty/mingw-std-threads/mingw.mutex.h"
|
||||
#define THREADING_NAMESPACE mingw_stdthread
|
||||
#else
|
||||
#include <mutex>
|
||||
#define THREADING_NAMESPACE std
|
||||
#endif
|
||||
|
||||
#ifdef THREADS_ENABLED
|
||||
|
||||
template <typename MutexT>
|
||||
class MutexLock;
|
||||
|
||||
template <typename StdMutexT>
|
||||
class MutexImpl {
|
||||
friend class MutexLock<MutexImpl<StdMutexT>>;
|
||||
|
||||
using StdMutexType = StdMutexT;
|
||||
|
||||
mutable StdMutexT mutex;
|
||||
|
||||
public:
|
||||
_ALWAYS_INLINE_ void lock() const {
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ void unlock() const {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ bool try_lock() const {
|
||||
return mutex.try_lock();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename MutexT>
|
||||
class MutexLock {
|
||||
mutable THREADING_NAMESPACE::unique_lock<typename MutexT::StdMutexType> lock;
|
||||
|
||||
public:
|
||||
explicit MutexLock(const MutexT &p_mutex) :
|
||||
lock(p_mutex.mutex) {}
|
||||
|
||||
// Clarification: all the funny syntax is needed so this function exists only for binary mutexes.
|
||||
template <typename T = MutexT>
|
||||
_ALWAYS_INLINE_ THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &_get_lock(
|
||||
typename std::enable_if<std::is_same<T, THREADING_NAMESPACE::mutex>::value> * = nullptr) const {
|
||||
return lock;
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ void temp_relock() const {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ void temp_unlock() const {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
// TODO: Implement a `try_temp_relock` if needed (will also need a dummy method below).
|
||||
};
|
||||
|
||||
using Mutex = MutexImpl<THREADING_NAMESPACE::recursive_mutex>; // Recursive, for general use
|
||||
using BinaryMutex = MutexImpl<THREADING_NAMESPACE::mutex>; // Non-recursive, handle with care
|
||||
|
||||
extern template class MutexImpl<THREADING_NAMESPACE::recursive_mutex>;
|
||||
extern template class MutexImpl<THREADING_NAMESPACE::mutex>;
|
||||
extern template class MutexLock<MutexImpl<THREADING_NAMESPACE::recursive_mutex>>;
|
||||
extern template class MutexLock<MutexImpl<THREADING_NAMESPACE::mutex>>;
|
||||
|
||||
#else // No threads.
|
||||
|
||||
class MutexImpl {
|
||||
mutable THREADING_NAMESPACE::mutex mutex;
|
||||
|
||||
public:
|
||||
void lock() const {}
|
||||
void unlock() const {}
|
||||
bool try_lock() const { return true; }
|
||||
};
|
||||
|
||||
template <typename MutexT>
|
||||
class MutexLock {
|
||||
public:
|
||||
MutexLock(const MutexT &p_mutex) {}
|
||||
|
||||
void temp_relock() const {}
|
||||
void temp_unlock() const {}
|
||||
};
|
||||
|
||||
using Mutex = MutexImpl;
|
||||
using BinaryMutex = MutexImpl;
|
||||
|
||||
#endif // THREADS_ENABLED
|
818
core/os/os.cpp
Normal file
818
core/os/os.cpp
Normal file
@@ -0,0 +1,818 @@
|
||||
/**************************************************************************/
|
||||
/* os.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.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "core/io/dir_access.h"
|
||||
#include "core/io/file_access.h"
|
||||
#include "core/io/json.h"
|
||||
#include "core/os/midi_driver.h"
|
||||
#include "core/version_generated.gen.h"
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
#ifdef MINGW_ENABLED
|
||||
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
|
||||
#include "thirdparty/mingw-std-threads/mingw.thread.h"
|
||||
#define THREADING_NAMESPACE mingw_stdthread
|
||||
#else
|
||||
#include <thread>
|
||||
#define THREADING_NAMESPACE std
|
||||
#endif
|
||||
|
||||
OS *OS::singleton = nullptr;
|
||||
uint64_t OS::target_ticks = 0;
|
||||
|
||||
OS *OS::get_singleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
uint64_t OS::get_ticks_msec() const {
|
||||
return get_ticks_usec() / 1000ULL;
|
||||
}
|
||||
|
||||
double OS::get_unix_time() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OS::_set_logger(CompositeLogger *p_logger) {
|
||||
if (_logger) {
|
||||
memdelete(_logger);
|
||||
}
|
||||
_logger = p_logger;
|
||||
}
|
||||
|
||||
void OS::add_logger(Logger *p_logger) {
|
||||
if (!_logger) {
|
||||
Vector<Logger *> loggers;
|
||||
loggers.push_back(p_logger);
|
||||
_logger = memnew(CompositeLogger(loggers));
|
||||
} else {
|
||||
_logger->add_logger(p_logger);
|
||||
}
|
||||
}
|
||||
|
||||
String OS::get_identifier() const {
|
||||
return get_name().to_lower();
|
||||
}
|
||||
|
||||
void OS::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, Logger::ErrorType p_type, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces) {
|
||||
if (!_stderr_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_logger) {
|
||||
_logger->log_error(p_function, p_file, p_line, p_code, p_rationale, p_editor_notify, p_type, p_script_backtraces);
|
||||
}
|
||||
}
|
||||
|
||||
void OS::print(const char *p_format, ...) {
|
||||
if (!_stdout_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
va_list argp;
|
||||
va_start(argp, p_format);
|
||||
|
||||
if (_logger) {
|
||||
_logger->logv(p_format, argp, false);
|
||||
}
|
||||
|
||||
va_end(argp);
|
||||
}
|
||||
|
||||
void OS::print_rich(const char *p_format, ...) {
|
||||
if (!_stdout_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
va_list argp;
|
||||
va_start(argp, p_format);
|
||||
|
||||
if (_logger) {
|
||||
_logger->logv(p_format, argp, false);
|
||||
}
|
||||
|
||||
va_end(argp);
|
||||
}
|
||||
|
||||
void OS::printerr(const char *p_format, ...) {
|
||||
if (!_stderr_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
va_list argp;
|
||||
va_start(argp, p_format);
|
||||
|
||||
if (_logger) {
|
||||
_logger->logv(p_format, argp, true);
|
||||
}
|
||||
|
||||
va_end(argp);
|
||||
}
|
||||
|
||||
void OS::alert(const String &p_alert, const String &p_title) {
|
||||
fprintf(stderr, "%s: %s\n", p_title.utf8().get_data(), p_alert.utf8().get_data());
|
||||
}
|
||||
|
||||
void OS::set_low_processor_usage_mode(bool p_enabled) {
|
||||
low_processor_usage_mode = p_enabled;
|
||||
}
|
||||
|
||||
bool OS::is_in_low_processor_usage_mode() const {
|
||||
return low_processor_usage_mode;
|
||||
}
|
||||
|
||||
void OS::set_low_processor_usage_mode_sleep_usec(int p_usec) {
|
||||
low_processor_usage_mode_sleep_usec = p_usec;
|
||||
}
|
||||
|
||||
int OS::get_low_processor_usage_mode_sleep_usec() const {
|
||||
return low_processor_usage_mode_sleep_usec;
|
||||
}
|
||||
|
||||
void OS::set_delta_smoothing(bool p_enabled) {
|
||||
_delta_smoothing_enabled = p_enabled;
|
||||
}
|
||||
|
||||
bool OS::is_delta_smoothing_enabled() const {
|
||||
return _delta_smoothing_enabled;
|
||||
}
|
||||
|
||||
String OS::get_executable_path() const {
|
||||
return _execpath;
|
||||
}
|
||||
|
||||
int OS::get_process_id() const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool OS::is_stdout_verbose() const {
|
||||
return _verbose_stdout;
|
||||
}
|
||||
|
||||
bool OS::is_stdout_debug_enabled() const {
|
||||
return _debug_stdout;
|
||||
}
|
||||
|
||||
bool OS::is_stdout_enabled() const {
|
||||
return _stdout_enabled;
|
||||
}
|
||||
|
||||
bool OS::is_stderr_enabled() const {
|
||||
return _stderr_enabled;
|
||||
}
|
||||
|
||||
void OS::set_stdout_enabled(bool p_enabled) {
|
||||
_stdout_enabled = p_enabled;
|
||||
}
|
||||
|
||||
void OS::set_stderr_enabled(bool p_enabled) {
|
||||
_stderr_enabled = p_enabled;
|
||||
}
|
||||
|
||||
String OS::multibyte_to_string(const String &p_encoding, const PackedByteArray &p_array) const {
|
||||
return String();
|
||||
}
|
||||
|
||||
PackedByteArray OS::string_to_multibyte(const String &p_encoding, const String &p_string) const {
|
||||
return PackedByteArray();
|
||||
}
|
||||
|
||||
int OS::get_exit_code() const {
|
||||
return _exit_code;
|
||||
}
|
||||
|
||||
void OS::set_exit_code(int p_code) {
|
||||
_exit_code = p_code;
|
||||
}
|
||||
|
||||
String OS::get_locale() const {
|
||||
return "en";
|
||||
}
|
||||
|
||||
// Non-virtual helper to extract the 2 or 3-letter language code from
|
||||
// `get_locale()` in a way that's consistent for all platforms.
|
||||
String OS::get_locale_language() const {
|
||||
return get_locale().left(3).remove_char('_');
|
||||
}
|
||||
|
||||
// Embedded PCK offset.
|
||||
uint64_t OS::get_embedded_pck_offset() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Default boot screen rect scale mode is "Keep Aspect Centered"
|
||||
Rect2 OS::calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const {
|
||||
Rect2 screenrect;
|
||||
if (p_window_size.width > p_window_size.height) {
|
||||
// Scale horizontally.
|
||||
screenrect.size.y = p_window_size.height;
|
||||
screenrect.size.x = p_imgrect_size.x * p_window_size.height / p_imgrect_size.y;
|
||||
screenrect.position.x = (p_window_size.width - screenrect.size.x) / 2;
|
||||
} else {
|
||||
// Scale vertically.
|
||||
screenrect.size.x = p_window_size.width;
|
||||
screenrect.size.y = p_imgrect_size.y * p_window_size.width / p_imgrect_size.x;
|
||||
screenrect.position.y = (p_window_size.height - screenrect.size.y) / 2;
|
||||
}
|
||||
return screenrect;
|
||||
}
|
||||
|
||||
// Helper function to ensure that a dir name/path will be valid on the OS
|
||||
String OS::get_safe_dir_name(const String &p_dir_name, bool p_allow_paths) const {
|
||||
String safe_dir_name = p_dir_name;
|
||||
Vector<String> invalid_chars = String(": * ? \" < > |").split(" ");
|
||||
if (p_allow_paths) {
|
||||
// Dir separators are allowed, but disallow ".." to avoid going up the filesystem
|
||||
invalid_chars.push_back("..");
|
||||
safe_dir_name = safe_dir_name.replace_char('\\', '/').replace("//", "/").strip_edges();
|
||||
} else {
|
||||
invalid_chars.push_back("/");
|
||||
invalid_chars.push_back("\\");
|
||||
safe_dir_name = safe_dir_name.strip_edges();
|
||||
|
||||
// These directory names are invalid.
|
||||
if (safe_dir_name == ".") {
|
||||
safe_dir_name = "dot";
|
||||
} else if (safe_dir_name == "..") {
|
||||
safe_dir_name = "twodots";
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < invalid_chars.size(); i++) {
|
||||
safe_dir_name = safe_dir_name.replace(invalid_chars[i], "-");
|
||||
}
|
||||
|
||||
// Trim trailing periods from the returned value as it's not valid for folder names on Windows.
|
||||
// This check is still applied on non-Windows platforms so the returned value is consistent across platforms.
|
||||
return safe_dir_name.rstrip(".");
|
||||
}
|
||||
|
||||
// Path to data, config, cache, etc. OS-specific folders
|
||||
|
||||
// Get properly capitalized engine name for system paths
|
||||
String OS::get_godot_dir_name() const {
|
||||
// Default to lowercase, so only override when different case is needed
|
||||
return String(GODOT_VERSION_SHORT_NAME).to_lower();
|
||||
}
|
||||
|
||||
// OS equivalent of XDG_DATA_HOME
|
||||
String OS::get_data_path() const {
|
||||
return ".";
|
||||
}
|
||||
|
||||
// OS equivalent of XDG_CONFIG_HOME
|
||||
String OS::get_config_path() const {
|
||||
return ".";
|
||||
}
|
||||
|
||||
// OS equivalent of XDG_CACHE_HOME
|
||||
String OS::get_cache_path() const {
|
||||
return ".";
|
||||
}
|
||||
|
||||
String OS::get_temp_path() const {
|
||||
return ".";
|
||||
}
|
||||
|
||||
// Path to macOS .app bundle resources
|
||||
String OS::get_bundle_resource_dir() const {
|
||||
return ".";
|
||||
}
|
||||
|
||||
// Path to macOS .app bundle embedded icon
|
||||
String OS::get_bundle_icon_path() const {
|
||||
return String();
|
||||
}
|
||||
|
||||
// OS specific path for user://
|
||||
String OS::get_user_data_dir(const String &p_user_dir) const {
|
||||
return ".";
|
||||
}
|
||||
|
||||
String OS::get_user_data_dir() const {
|
||||
String appname = get_safe_dir_name(GLOBAL_GET("application/config/name"));
|
||||
if (!appname.is_empty()) {
|
||||
bool use_custom_dir = GLOBAL_GET("application/config/use_custom_user_dir");
|
||||
if (use_custom_dir) {
|
||||
String custom_dir = get_safe_dir_name(GLOBAL_GET("application/config/custom_user_dir_name"), true);
|
||||
if (custom_dir.is_empty()) {
|
||||
custom_dir = appname;
|
||||
}
|
||||
return get_user_data_dir(custom_dir);
|
||||
} else {
|
||||
return get_user_data_dir(get_godot_dir_name().path_join("app_userdata").path_join(appname));
|
||||
}
|
||||
} else {
|
||||
return get_user_data_dir(get_godot_dir_name().path_join("app_userdata").path_join("[unnamed project]"));
|
||||
}
|
||||
}
|
||||
|
||||
// Absolute path to res://
|
||||
String OS::get_resource_dir() const {
|
||||
return ProjectSettings::get_singleton()->get_resource_path();
|
||||
}
|
||||
|
||||
// Access system-specific dirs like Documents, Downloads, etc.
|
||||
String OS::get_system_dir(SystemDir p_dir, bool p_shared_storage) const {
|
||||
return ".";
|
||||
}
|
||||
|
||||
void OS::create_lock_file() {
|
||||
if (Engine::get_singleton()->is_recovery_mode_hint()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String lock_file_path = get_user_data_dir().path_join(".recovery_mode_lock");
|
||||
Ref<FileAccess> lock_file = FileAccess::open(lock_file_path, FileAccess::WRITE);
|
||||
if (lock_file.is_valid()) {
|
||||
lock_file->close();
|
||||
}
|
||||
}
|
||||
|
||||
void OS::remove_lock_file() {
|
||||
String lock_file_path = get_user_data_dir().path_join(".recovery_mode_lock");
|
||||
DirAccess::remove_absolute(lock_file_path);
|
||||
}
|
||||
|
||||
Error OS::shell_open(const String &p_uri) {
|
||||
return ERR_UNAVAILABLE;
|
||||
}
|
||||
|
||||
Error OS::shell_show_in_file_manager(String p_path, bool p_open_folder) {
|
||||
p_path = p_path.trim_prefix("file://");
|
||||
|
||||
if (!DirAccess::dir_exists_absolute(p_path)) {
|
||||
p_path = p_path.get_base_dir();
|
||||
}
|
||||
|
||||
p_path = String("file://") + p_path;
|
||||
|
||||
return shell_open(p_path);
|
||||
}
|
||||
// implement these with the canvas?
|
||||
|
||||
uint64_t OS::get_static_memory_usage() const {
|
||||
return Memory::get_mem_usage();
|
||||
}
|
||||
|
||||
uint64_t OS::get_static_memory_peak_usage() const {
|
||||
return Memory::get_mem_max_usage();
|
||||
}
|
||||
|
||||
Error OS::set_cwd(const String &p_cwd) {
|
||||
return ERR_CANT_OPEN;
|
||||
}
|
||||
|
||||
Dictionary OS::get_memory_info() const {
|
||||
Dictionary meminfo;
|
||||
|
||||
meminfo["physical"] = -1;
|
||||
meminfo["free"] = -1;
|
||||
meminfo["available"] = -1;
|
||||
meminfo["stack"] = -1;
|
||||
|
||||
return meminfo;
|
||||
}
|
||||
|
||||
void OS::yield() {
|
||||
}
|
||||
|
||||
void OS::ensure_user_data_dir() {
|
||||
String dd = get_user_data_dir();
|
||||
if (DirAccess::exists(dd)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
|
||||
Error err = da->make_dir_recursive(dd);
|
||||
ERR_FAIL_COND_MSG(err != OK, vformat("Error attempting to create data dir: %s.", dd));
|
||||
}
|
||||
|
||||
String OS::get_model_name() const {
|
||||
return "GenericDevice";
|
||||
}
|
||||
|
||||
void OS::set_cmdline(const char *p_execpath, const List<String> &p_args, const List<String> &p_user_args) {
|
||||
_execpath = String::utf8(p_execpath);
|
||||
_cmdline = p_args;
|
||||
_user_args = p_user_args;
|
||||
}
|
||||
|
||||
String OS::get_unique_id() const {
|
||||
return "";
|
||||
}
|
||||
|
||||
int OS::get_processor_count() const {
|
||||
return THREADING_NAMESPACE::thread::hardware_concurrency();
|
||||
}
|
||||
|
||||
String OS::get_processor_name() const {
|
||||
return "";
|
||||
}
|
||||
|
||||
void OS::set_has_server_feature_callback(HasServerFeatureCallback p_callback) {
|
||||
has_server_feature_callback = p_callback;
|
||||
}
|
||||
|
||||
bool OS::has_feature(const String &p_feature) {
|
||||
// Feature tags are always lowercase for consistency.
|
||||
if (p_feature == get_identifier()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (p_feature == "movie") {
|
||||
return _writing_movie;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (p_feature == "debug") {
|
||||
return true;
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (p_feature == "editor") {
|
||||
return true;
|
||||
}
|
||||
if (p_feature == "editor_hint") {
|
||||
return _in_editor;
|
||||
} else if (p_feature == "editor_runtime") {
|
||||
return !_in_editor;
|
||||
} else if (p_feature == "embedded_in_editor") {
|
||||
return _embedded_in_editor;
|
||||
}
|
||||
#else
|
||||
if (p_feature == "template") {
|
||||
return true;
|
||||
}
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (p_feature == "template_debug") {
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
if (p_feature == "template_release" || p_feature == "release") {
|
||||
return true;
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
#ifdef REAL_T_IS_DOUBLE
|
||||
if (p_feature == "double") {
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
if (p_feature == "single") {
|
||||
return true;
|
||||
}
|
||||
#endif // REAL_T_IS_DOUBLE
|
||||
|
||||
if (sizeof(void *) == 8 && p_feature == "64") {
|
||||
return true;
|
||||
}
|
||||
if (sizeof(void *) == 4 && p_feature == "32") {
|
||||
return true;
|
||||
}
|
||||
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(_M_X64)
|
||||
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(_M_X64)
|
||||
#if defined(MACOS_ENABLED)
|
||||
if (p_feature == "universal") {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (p_feature == "x86_64") {
|
||||
return true;
|
||||
}
|
||||
#elif defined(__i386) || defined(__i386__) || defined(_M_IX86)
|
||||
if (p_feature == "x86_32") {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (p_feature == "x86") {
|
||||
return true;
|
||||
}
|
||||
#elif defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
|
||||
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||
#if defined(MACOS_ENABLED)
|
||||
if (p_feature == "universal") {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (p_feature == "arm64") {
|
||||
return true;
|
||||
}
|
||||
#elif defined(__arm__) || defined(_M_ARM)
|
||||
if (p_feature == "arm32") {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#if defined(__ARM_ARCH_7A__)
|
||||
if (p_feature == "armv7a" || p_feature == "armv7") {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#if defined(__ARM_ARCH_7S__)
|
||||
if (p_feature == "armv7s" || p_feature == "armv7") {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (p_feature == "arm") {
|
||||
return true;
|
||||
}
|
||||
#elif defined(__riscv)
|
||||
#if __riscv_xlen == 8
|
||||
if (p_feature == "rv64") {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (p_feature == "riscv") {
|
||||
return true;
|
||||
}
|
||||
#elif defined(__powerpc__)
|
||||
#if defined(__powerpc64__)
|
||||
if (p_feature == "ppc64") {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (p_feature == "ppc") {
|
||||
return true;
|
||||
}
|
||||
#elif defined(__wasm__)
|
||||
#if defined(__wasm64__)
|
||||
if (p_feature == "wasm64") {
|
||||
return true;
|
||||
}
|
||||
#elif defined(__wasm32__)
|
||||
if (p_feature == "wasm32") {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
if (p_feature == "wasm") {
|
||||
return true;
|
||||
}
|
||||
#elif defined(__loongarch64)
|
||||
if (p_feature == "loongarch64") {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(IOS_SIMULATOR) || defined(VISIONOS_SIMULATOR)
|
||||
if (p_feature == "simulator") {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (p_feature == "threads") {
|
||||
#ifdef THREADS_ENABLED
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
if (p_feature == "nothreads") {
|
||||
#ifdef THREADS_ENABLED
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (_check_internal_feature_support(p_feature)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (has_server_feature_callback && has_server_feature_callback(p_feature)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ProjectSettings::get_singleton()->has_custom_feature(p_feature)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OS::is_sandboxed() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void OS::set_restart_on_exit(bool p_restart, const List<String> &p_restart_arguments) {
|
||||
restart_on_exit = p_restart;
|
||||
restart_commandline = p_restart_arguments;
|
||||
}
|
||||
|
||||
bool OS::is_restart_on_exit_set() const {
|
||||
return restart_on_exit;
|
||||
}
|
||||
|
||||
List<String> OS::get_restart_on_exit_arguments() const {
|
||||
return restart_commandline;
|
||||
}
|
||||
|
||||
PackedStringArray OS::get_connected_midi_inputs() {
|
||||
if (MIDIDriver::get_singleton()) {
|
||||
return MIDIDriver::get_singleton()->get_connected_inputs();
|
||||
}
|
||||
|
||||
PackedStringArray list;
|
||||
ERR_FAIL_V_MSG(list, vformat("MIDI input isn't supported on %s.", OS::get_singleton()->get_name()));
|
||||
}
|
||||
|
||||
void OS::open_midi_inputs() {
|
||||
if (MIDIDriver::get_singleton()) {
|
||||
MIDIDriver::get_singleton()->open();
|
||||
} else {
|
||||
ERR_PRINT(vformat("MIDI input isn't supported on %s.", OS::get_singleton()->get_name()));
|
||||
}
|
||||
}
|
||||
|
||||
void OS::close_midi_inputs() {
|
||||
if (MIDIDriver::get_singleton()) {
|
||||
MIDIDriver::get_singleton()->close();
|
||||
} else {
|
||||
ERR_PRINT(vformat("MIDI input isn't supported on %s.", OS::get_singleton()->get_name()));
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t OS::get_frame_delay(bool p_can_draw) const {
|
||||
const uint32_t frame_delay = Engine::get_singleton()->get_frame_delay();
|
||||
|
||||
// Add a dynamic frame delay to decrease CPU/GPU usage. This takes the
|
||||
// previous frame time into account for a smoother result.
|
||||
uint64_t dynamic_delay = 0;
|
||||
if (is_in_low_processor_usage_mode() || !p_can_draw) {
|
||||
dynamic_delay = get_low_processor_usage_mode_sleep_usec();
|
||||
}
|
||||
const int max_fps = Engine::get_singleton()->get_max_fps();
|
||||
if (max_fps > 0 && !Engine::get_singleton()->is_editor_hint()) {
|
||||
// Override the low processor usage mode sleep delay if the target FPS is lower.
|
||||
dynamic_delay = MAX(dynamic_delay, (uint64_t)(1000000 / max_fps));
|
||||
}
|
||||
|
||||
return frame_delay + dynamic_delay;
|
||||
}
|
||||
|
||||
void OS::add_frame_delay(bool p_can_draw, bool p_wake_for_events) {
|
||||
const uint32_t frame_delay = Engine::get_singleton()->get_frame_delay();
|
||||
if (frame_delay) {
|
||||
// Add fixed frame delay to decrease CPU/GPU usage. This doesn't take
|
||||
// the actual frame time into account.
|
||||
// Due to the high fluctuation of the actual sleep duration, it's not recommended
|
||||
// to use this as a FPS limiter.
|
||||
delay_usec(frame_delay * 1000);
|
||||
}
|
||||
|
||||
// Add a dynamic frame delay to decrease CPU/GPU usage. This takes the
|
||||
// previous frame time into account for a smoother result.
|
||||
uint64_t dynamic_delay = 0;
|
||||
if (is_in_low_processor_usage_mode() || !p_can_draw) {
|
||||
dynamic_delay = get_low_processor_usage_mode_sleep_usec();
|
||||
}
|
||||
const int max_fps = Engine::get_singleton()->get_max_fps();
|
||||
if (max_fps > 0 && !Engine::get_singleton()->is_editor_hint()) {
|
||||
// Override the low processor usage mode sleep delay if the target FPS is lower.
|
||||
dynamic_delay = MAX(dynamic_delay, (uint64_t)(1000000 / max_fps));
|
||||
}
|
||||
|
||||
if (dynamic_delay > 0) {
|
||||
target_ticks += dynamic_delay;
|
||||
uint64_t current_ticks = get_ticks_usec();
|
||||
|
||||
if (current_ticks < target_ticks) {
|
||||
delay_usec(target_ticks - current_ticks);
|
||||
}
|
||||
|
||||
current_ticks = get_ticks_usec();
|
||||
target_ticks = MIN(MAX(target_ticks, current_ticks - dynamic_delay), current_ticks + dynamic_delay);
|
||||
}
|
||||
}
|
||||
|
||||
Error OS::setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path) {
|
||||
return default_rfs.synchronize_with_server(p_server_host, p_port, p_password, r_project_path);
|
||||
}
|
||||
|
||||
OS::PreferredTextureFormat OS::get_preferred_texture_format() const {
|
||||
#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
|
||||
return PREFERRED_TEXTURE_FORMAT_ETC2_ASTC; // By rule, ARM hardware uses ETC texture compression.
|
||||
#elif defined(__x86_64__) || defined(_M_X64) || defined(i386) || defined(__i386__) || defined(__i386) || defined(_M_IX86)
|
||||
return PREFERRED_TEXTURE_FORMAT_S3TC_BPTC; // By rule, X86 hardware prefers S3TC and derivatives.
|
||||
#else
|
||||
return PREFERRED_TEXTURE_FORMAT_S3TC_BPTC; // Override in platform if needed.
|
||||
#endif
|
||||
}
|
||||
|
||||
void OS::set_use_benchmark(bool p_use_benchmark) {
|
||||
use_benchmark = p_use_benchmark;
|
||||
}
|
||||
|
||||
bool OS::is_use_benchmark_set() {
|
||||
return use_benchmark;
|
||||
}
|
||||
|
||||
void OS::set_benchmark_file(const String &p_benchmark_file) {
|
||||
benchmark_file = p_benchmark_file;
|
||||
}
|
||||
|
||||
String OS::get_benchmark_file() {
|
||||
return benchmark_file;
|
||||
}
|
||||
|
||||
void OS::benchmark_begin_measure(const String &p_context, const String &p_what) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
Pair<String, String> mark_key(p_context, p_what);
|
||||
ERR_FAIL_COND_MSG(benchmark_marks_from.has(mark_key), vformat("Benchmark key '%s:%s' already exists.", p_context, p_what));
|
||||
|
||||
benchmark_marks_from[mark_key] = OS::get_singleton()->get_ticks_usec();
|
||||
#endif
|
||||
}
|
||||
void OS::benchmark_end_measure(const String &p_context, const String &p_what) {
|
||||
#ifdef TOOLS_ENABLED
|
||||
Pair<String, String> mark_key(p_context, p_what);
|
||||
ERR_FAIL_COND_MSG(!benchmark_marks_from.has(mark_key), vformat("Benchmark key '%s:%s' doesn't exist.", p_context, p_what));
|
||||
|
||||
uint64_t total = OS::get_singleton()->get_ticks_usec() - benchmark_marks_from[mark_key];
|
||||
double total_f = double(total) / double(1000000);
|
||||
benchmark_marks_final[mark_key] = total_f;
|
||||
#endif
|
||||
}
|
||||
|
||||
void OS::benchmark_dump() {
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (!use_benchmark) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!benchmark_file.is_empty()) {
|
||||
Ref<FileAccess> f = FileAccess::open(benchmark_file, FileAccess::WRITE);
|
||||
if (f.is_valid()) {
|
||||
Dictionary benchmark_marks;
|
||||
for (const KeyValue<Pair<String, String>, double> &E : benchmark_marks_final) {
|
||||
const String mark_key = vformat("[%s] %s", E.key.first, E.key.second);
|
||||
benchmark_marks[mark_key] = E.value;
|
||||
}
|
||||
|
||||
Ref<JSON> json;
|
||||
json.instantiate();
|
||||
f->store_string(json->stringify(benchmark_marks, "\t", false, true));
|
||||
}
|
||||
} else {
|
||||
HashMap<String, String> results;
|
||||
for (const KeyValue<Pair<String, String>, double> &E : benchmark_marks_final) {
|
||||
if (E.key.first == "Startup" && !results.has(E.key.first)) {
|
||||
results.insert(E.key.first, "", true); // Hack to make sure "Startup" always comes first.
|
||||
}
|
||||
|
||||
results[E.key.first] += vformat("\t\t- %s: %.3f msec.\n", E.key.second, (E.value * 1000));
|
||||
}
|
||||
|
||||
print_line("BENCHMARK:");
|
||||
for (const KeyValue<String, String> &E : results) {
|
||||
print_line(vformat("\t[%s]\n%s", E.key, E.value));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
OS::OS() {
|
||||
singleton = this;
|
||||
|
||||
Vector<Logger *> loggers;
|
||||
loggers.push_back(memnew(StdLogger));
|
||||
_set_logger(memnew(CompositeLogger(loggers)));
|
||||
}
|
||||
|
||||
OS::~OS() {
|
||||
if (_logger) {
|
||||
memdelete(_logger);
|
||||
}
|
||||
singleton = nullptr;
|
||||
}
|
379
core/os/os.h
Normal file
379
core/os/os.h
Normal file
@@ -0,0 +1,379 @@
|
||||
/**************************************************************************/
|
||||
/* os.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/config/engine.h"
|
||||
#include "core/io/logger.h"
|
||||
#include "core/io/remote_filesystem_client.h"
|
||||
#include "core/os/time_enums.h"
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/templates/list.h"
|
||||
#include "core/templates/vector.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
class OS {
|
||||
static OS *singleton;
|
||||
static uint64_t target_ticks;
|
||||
String _execpath;
|
||||
List<String> _cmdline;
|
||||
List<String> _user_args;
|
||||
bool _keep_screen_on = true; // set default value to true, because this had been true before godot 2.0.
|
||||
bool low_processor_usage_mode = false;
|
||||
int low_processor_usage_mode_sleep_usec = 10000;
|
||||
bool _delta_smoothing_enabled = false;
|
||||
bool _verbose_stdout = false;
|
||||
bool _debug_stdout = false;
|
||||
String _local_clipboard;
|
||||
// Assume success by default, all failure cases need to set EXIT_FAILURE explicitly.
|
||||
int _exit_code = EXIT_SUCCESS;
|
||||
bool _allow_hidpi = false;
|
||||
bool _allow_layered = false;
|
||||
bool _stdout_enabled = true;
|
||||
bool _stderr_enabled = true;
|
||||
bool _writing_movie = false;
|
||||
bool _in_editor = false;
|
||||
bool _embedded_in_editor = false;
|
||||
|
||||
CompositeLogger *_logger = nullptr;
|
||||
|
||||
bool restart_on_exit = false;
|
||||
List<String> restart_commandline;
|
||||
|
||||
String _current_rendering_driver_name;
|
||||
String _current_rendering_method;
|
||||
bool _is_gles_over_gl = false;
|
||||
|
||||
RemoteFilesystemClient default_rfs;
|
||||
|
||||
// For tracking benchmark data
|
||||
bool use_benchmark = false;
|
||||
String benchmark_file;
|
||||
HashMap<Pair<String, String>, uint64_t> benchmark_marks_from;
|
||||
HashMap<Pair<String, String>, double> benchmark_marks_final;
|
||||
|
||||
protected:
|
||||
void _set_logger(CompositeLogger *p_logger);
|
||||
|
||||
public:
|
||||
typedef void (*ImeCallback)(void *p_inp, const String &p_text, Point2 p_selection);
|
||||
typedef bool (*HasServerFeatureCallback)(const String &p_feature);
|
||||
|
||||
enum RenderThreadMode {
|
||||
RENDER_THREAD_UNSAFE,
|
||||
RENDER_THREAD_SAFE,
|
||||
RENDER_SEPARATE_THREAD,
|
||||
};
|
||||
|
||||
enum StdHandleType {
|
||||
STD_HANDLE_INVALID,
|
||||
STD_HANDLE_CONSOLE,
|
||||
STD_HANDLE_FILE,
|
||||
STD_HANDLE_PIPE,
|
||||
STD_HANDLE_UNKNOWN,
|
||||
};
|
||||
|
||||
protected:
|
||||
friend class Main;
|
||||
// Needed by tests to setup command-line args.
|
||||
friend int test_main(int argc, char *argv[]);
|
||||
|
||||
HasServerFeatureCallback has_server_feature_callback = nullptr;
|
||||
bool _separate_thread_render = false;
|
||||
bool _silent_crash_handler = false;
|
||||
|
||||
// Functions used by Main to initialize/deinitialize the OS.
|
||||
|
||||
virtual void initialize() = 0;
|
||||
virtual void initialize_joypads() = 0;
|
||||
|
||||
virtual void set_main_loop(MainLoop *p_main_loop) = 0;
|
||||
virtual void delete_main_loop() = 0;
|
||||
|
||||
virtual void finalize() = 0;
|
||||
virtual void finalize_core() = 0;
|
||||
|
||||
virtual void set_cmdline(const char *p_execpath, const List<String> &p_args, const List<String> &p_user_args);
|
||||
|
||||
virtual bool _check_internal_feature_support(const String &p_feature) = 0;
|
||||
|
||||
public:
|
||||
typedef int64_t ProcessID;
|
||||
|
||||
static OS *get_singleton();
|
||||
|
||||
void set_current_rendering_driver_name(const String &p_driver_name) { _current_rendering_driver_name = p_driver_name; }
|
||||
void set_current_rendering_method(const String &p_name) { _current_rendering_method = p_name; }
|
||||
void set_gles_over_gl(bool p_enabled) { _is_gles_over_gl = p_enabled; }
|
||||
|
||||
String get_current_rendering_driver_name() const { return _current_rendering_driver_name; }
|
||||
String get_current_rendering_method() const { return _current_rendering_method; }
|
||||
bool get_gles_over_gl() const { return _is_gles_over_gl; }
|
||||
|
||||
virtual Vector<String> get_video_adapter_driver_info() const = 0;
|
||||
virtual bool get_user_prefers_integrated_gpu() const { return false; }
|
||||
|
||||
void print_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, Logger::ErrorType p_type = Logger::ERR_ERROR, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces = {});
|
||||
void print(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
|
||||
void print_rich(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
|
||||
void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
|
||||
|
||||
virtual String get_stdin_string(int64_t p_buffer_size = 1024) = 0;
|
||||
virtual PackedByteArray get_stdin_buffer(int64_t p_buffer_size = 1024) = 0;
|
||||
|
||||
virtual StdHandleType get_stdin_type() const { return STD_HANDLE_UNKNOWN; }
|
||||
virtual StdHandleType get_stdout_type() const { return STD_HANDLE_UNKNOWN; }
|
||||
virtual StdHandleType get_stderr_type() const { return STD_HANDLE_UNKNOWN; }
|
||||
|
||||
virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) = 0; // Should return cryptographically-safe random bytes.
|
||||
virtual String get_system_ca_certificates() { return ""; } // Concatenated certificates in PEM format.
|
||||
|
||||
virtual PackedStringArray get_connected_midi_inputs();
|
||||
virtual void open_midi_inputs();
|
||||
virtual void close_midi_inputs();
|
||||
|
||||
virtual Rect2 calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const;
|
||||
|
||||
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
|
||||
|
||||
struct GDExtensionData {
|
||||
bool also_set_library_path = false;
|
||||
String *r_resolved_path = nullptr;
|
||||
bool generate_temp_files = false;
|
||||
PackedStringArray *library_dependencies = nullptr;
|
||||
};
|
||||
|
||||
virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, GDExtensionData *p_data = nullptr) { return ERR_UNAVAILABLE; }
|
||||
virtual Error close_dynamic_library(void *p_library_handle) { return ERR_UNAVAILABLE; }
|
||||
virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional = false) { return ERR_UNAVAILABLE; }
|
||||
|
||||
virtual void set_low_processor_usage_mode(bool p_enabled);
|
||||
virtual bool is_in_low_processor_usage_mode() const;
|
||||
virtual void set_low_processor_usage_mode_sleep_usec(int p_usec);
|
||||
virtual int get_low_processor_usage_mode_sleep_usec() const;
|
||||
|
||||
void set_delta_smoothing(bool p_enabled);
|
||||
bool is_delta_smoothing_enabled() const;
|
||||
|
||||
virtual Vector<String> get_system_fonts() const { return Vector<String>(); }
|
||||
virtual String get_system_font_path(const String &p_font_name, int p_weight = 400, int p_stretch = 100, bool p_italic = false) const { return String(); }
|
||||
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 { return Vector<String>(); }
|
||||
virtual String get_executable_path() const;
|
||||
virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) = 0;
|
||||
virtual Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments, bool p_blocking = true) { return Dictionary(); }
|
||||
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) = 0;
|
||||
virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) { return create_process(get_executable_path(), p_arguments, r_child_id); }
|
||||
virtual Error open_with_program(const String &p_program_path, const List<String> &p_paths) { return create_process(p_program_path, p_paths); }
|
||||
virtual Error kill(const ProcessID &p_pid) = 0;
|
||||
virtual int get_process_id() const;
|
||||
virtual bool is_process_running(const ProcessID &p_pid) const = 0;
|
||||
virtual int get_process_exit_code(const ProcessID &p_pid) const = 0;
|
||||
virtual void vibrate_handheld(int p_duration_ms = 500, float p_amplitude = -1.0) {}
|
||||
|
||||
virtual Error shell_open(const String &p_uri);
|
||||
virtual Error shell_show_in_file_manager(String p_path, bool p_open_folder = true);
|
||||
virtual Error set_cwd(const String &p_cwd);
|
||||
|
||||
virtual bool has_environment(const String &p_var) const = 0;
|
||||
virtual String get_environment(const String &p_var) const = 0;
|
||||
virtual void set_environment(const String &p_var, const String &p_value) const = 0;
|
||||
virtual void unset_environment(const String &p_var) const = 0;
|
||||
virtual void load_shell_environment() const {}
|
||||
|
||||
virtual String get_name() const = 0;
|
||||
virtual String get_identifier() const;
|
||||
virtual String get_distribution_name() const = 0;
|
||||
virtual String get_version() const = 0;
|
||||
virtual String get_version_alias() const { return get_version(); }
|
||||
virtual List<String> get_cmdline_args() const { return _cmdline; }
|
||||
virtual List<String> get_cmdline_user_args() const { return _user_args; }
|
||||
virtual List<String> get_cmdline_platform_args() const { return List<String>(); }
|
||||
virtual String get_model_name() const;
|
||||
|
||||
bool is_layered_allowed() const { return _allow_layered; }
|
||||
bool is_hidpi_allowed() const { return _allow_hidpi; }
|
||||
|
||||
void ensure_user_data_dir();
|
||||
|
||||
virtual MainLoop *get_main_loop() const = 0;
|
||||
|
||||
virtual void yield();
|
||||
|
||||
struct DateTime {
|
||||
int64_t year;
|
||||
Month month;
|
||||
uint8_t day;
|
||||
Weekday weekday;
|
||||
uint8_t hour;
|
||||
uint8_t minute;
|
||||
uint8_t second;
|
||||
bool dst;
|
||||
};
|
||||
|
||||
struct TimeZoneInfo {
|
||||
int bias;
|
||||
String name;
|
||||
};
|
||||
|
||||
virtual DateTime get_datetime(bool utc = false) const = 0;
|
||||
virtual TimeZoneInfo get_time_zone_info() const = 0;
|
||||
virtual double get_unix_time() const;
|
||||
|
||||
virtual void delay_usec(uint32_t p_usec) const = 0;
|
||||
virtual void add_frame_delay(bool p_can_draw, bool p_wake_for_events);
|
||||
virtual uint64_t get_frame_delay(bool p_can_draw) const;
|
||||
|
||||
virtual uint64_t get_ticks_usec() const = 0;
|
||||
uint64_t get_ticks_msec() const;
|
||||
|
||||
virtual bool is_userfs_persistent() const { return true; }
|
||||
|
||||
bool is_stdout_verbose() const;
|
||||
bool is_stdout_debug_enabled() const;
|
||||
|
||||
bool is_stdout_enabled() const;
|
||||
bool is_stderr_enabled() const;
|
||||
void set_stdout_enabled(bool p_enabled);
|
||||
void set_stderr_enabled(bool p_enabled);
|
||||
|
||||
virtual void set_crash_handler_silent() { _silent_crash_handler = true; }
|
||||
virtual bool is_crash_handler_silent() { return _silent_crash_handler; }
|
||||
|
||||
virtual String multibyte_to_string(const String &p_encoding, const PackedByteArray &p_array) const;
|
||||
virtual PackedByteArray string_to_multibyte(const String &p_encoding, const String &p_string) const;
|
||||
|
||||
virtual void disable_crash_handler() {}
|
||||
virtual bool is_disable_crash_handler() const { return false; }
|
||||
virtual void initialize_debugging() {}
|
||||
|
||||
virtual uint64_t get_static_memory_usage() const;
|
||||
virtual uint64_t get_static_memory_peak_usage() const;
|
||||
virtual Dictionary get_memory_info() const;
|
||||
|
||||
bool is_separate_thread_rendering_enabled() const { return _separate_thread_render; }
|
||||
|
||||
virtual String get_locale() const;
|
||||
String get_locale_language() const;
|
||||
|
||||
virtual uint64_t get_embedded_pck_offset() const;
|
||||
|
||||
String get_safe_dir_name(const String &p_dir_name, bool p_allow_paths = false) const;
|
||||
virtual String get_godot_dir_name() const;
|
||||
|
||||
virtual String get_data_path() const;
|
||||
virtual String get_config_path() const;
|
||||
virtual String get_cache_path() const;
|
||||
virtual String get_temp_path() const;
|
||||
virtual String get_bundle_resource_dir() const;
|
||||
virtual String get_bundle_icon_path() const;
|
||||
|
||||
virtual String get_user_data_dir(const String &p_user_dir) const;
|
||||
virtual String get_user_data_dir() const;
|
||||
virtual String get_resource_dir() const;
|
||||
|
||||
enum SystemDir {
|
||||
SYSTEM_DIR_DESKTOP,
|
||||
SYSTEM_DIR_DCIM,
|
||||
SYSTEM_DIR_DOCUMENTS,
|
||||
SYSTEM_DIR_DOWNLOADS,
|
||||
SYSTEM_DIR_MOVIES,
|
||||
SYSTEM_DIR_MUSIC,
|
||||
SYSTEM_DIR_PICTURES,
|
||||
SYSTEM_DIR_RINGTONES,
|
||||
};
|
||||
|
||||
virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const;
|
||||
|
||||
virtual Error move_to_trash(const String &p_path) { return FAILED; }
|
||||
|
||||
void create_lock_file();
|
||||
void remove_lock_file();
|
||||
|
||||
virtual int get_exit_code() const;
|
||||
// `set_exit_code` should only be used from `SceneTree` (or from a similar
|
||||
// level, e.g. from the `Main::start` if leaving without creating a `SceneTree`).
|
||||
// For other components, `SceneTree.quit()` should be used instead.
|
||||
virtual void set_exit_code(int p_code);
|
||||
|
||||
virtual int get_processor_count() const;
|
||||
virtual String get_processor_name() const;
|
||||
virtual int get_default_thread_pool_size() const { return get_processor_count(); }
|
||||
|
||||
virtual String get_unique_id() const;
|
||||
|
||||
bool has_feature(const String &p_feature);
|
||||
|
||||
virtual bool is_sandboxed() const;
|
||||
|
||||
void set_has_server_feature_callback(HasServerFeatureCallback p_callback);
|
||||
|
||||
void set_restart_on_exit(bool p_restart, const List<String> &p_restart_arguments);
|
||||
bool is_restart_on_exit_set() const;
|
||||
List<String> get_restart_on_exit_arguments() const;
|
||||
|
||||
virtual bool request_permission(const String &p_name) { return true; }
|
||||
virtual bool request_permissions() { return true; }
|
||||
virtual Vector<String> get_granted_permissions() const { return Vector<String>(); }
|
||||
virtual void revoke_granted_permissions() {}
|
||||
|
||||
// For recording / measuring benchmark data. Only enabled with tools
|
||||
void set_use_benchmark(bool p_use_benchmark);
|
||||
bool is_use_benchmark_set();
|
||||
void set_benchmark_file(const String &p_benchmark_file);
|
||||
String get_benchmark_file();
|
||||
virtual void benchmark_begin_measure(const String &p_context, const String &p_what);
|
||||
virtual void benchmark_end_measure(const String &p_context, const String &p_what);
|
||||
virtual void benchmark_dump();
|
||||
|
||||
virtual Error setup_remote_filesystem(const String &p_server_host, int p_port, const String &p_password, String &r_project_path);
|
||||
|
||||
void add_logger(Logger *p_logger);
|
||||
|
||||
enum PreferredTextureFormat {
|
||||
PREFERRED_TEXTURE_FORMAT_S3TC_BPTC,
|
||||
PREFERRED_TEXTURE_FORMAT_ETC2_ASTC
|
||||
};
|
||||
|
||||
virtual PreferredTextureFormat get_preferred_texture_format() const;
|
||||
|
||||
// Load GDExtensions specific to this platform.
|
||||
// This is invoked by the GDExtensionManager after loading GDExtensions specified by the project.
|
||||
virtual void load_platform_gdextensions() const {}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// Tests OpenGL context and Rendering Device simultaneous creation. This function is expected to crash on some NVIDIA drivers.
|
||||
virtual bool _test_create_rendering_device_and_gl(const String &p_display_driver) const { return true; }
|
||||
virtual bool _test_create_rendering_device(const String &p_display_driver) const { return true; }
|
||||
#endif
|
||||
|
||||
OS();
|
||||
virtual ~OS();
|
||||
};
|
103
core/os/rw_lock.h
Normal file
103
core/os/rw_lock.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/**************************************************************************/
|
||||
/* rw_lock.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#ifdef MINGW_ENABLED
|
||||
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
|
||||
#include "thirdparty/mingw-std-threads/mingw.shared_mutex.h"
|
||||
#define THREADING_NAMESPACE mingw_stdthread
|
||||
#else
|
||||
#include <shared_mutex>
|
||||
#define THREADING_NAMESPACE std
|
||||
#endif
|
||||
|
||||
class RWLock {
|
||||
mutable THREADING_NAMESPACE::shared_timed_mutex mutex;
|
||||
|
||||
public:
|
||||
// Lock the RWLock, block if locked by someone else.
|
||||
_ALWAYS_INLINE_ void read_lock() const {
|
||||
mutex.lock_shared();
|
||||
}
|
||||
|
||||
// Unlock the RWLock, let other threads continue.
|
||||
_ALWAYS_INLINE_ void read_unlock() const {
|
||||
mutex.unlock_shared();
|
||||
}
|
||||
|
||||
// Attempt to lock the RWLock for reading. True on success, false means it can't lock.
|
||||
_ALWAYS_INLINE_ bool read_try_lock() const {
|
||||
return mutex.try_lock_shared();
|
||||
}
|
||||
|
||||
// Lock the RWLock, block if locked by someone else.
|
||||
_ALWAYS_INLINE_ void write_lock() {
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
// Unlock the RWLock, let other threads continue.
|
||||
_ALWAYS_INLINE_ void write_unlock() {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
// Attempt to lock the RWLock for writing. True on success, false means it can't lock.
|
||||
_ALWAYS_INLINE_ bool write_try_lock() {
|
||||
return mutex.try_lock();
|
||||
}
|
||||
};
|
||||
|
||||
class RWLockRead {
|
||||
const RWLock &lock;
|
||||
|
||||
public:
|
||||
_ALWAYS_INLINE_ RWLockRead(const RWLock &p_lock) :
|
||||
lock(p_lock) {
|
||||
lock.read_lock();
|
||||
}
|
||||
_ALWAYS_INLINE_ ~RWLockRead() {
|
||||
lock.read_unlock();
|
||||
}
|
||||
};
|
||||
|
||||
class RWLockWrite {
|
||||
RWLock &lock;
|
||||
|
||||
public:
|
||||
_ALWAYS_INLINE_ RWLockWrite(RWLock &p_lock) :
|
||||
lock(p_lock) {
|
||||
lock.write_lock();
|
||||
}
|
||||
_ALWAYS_INLINE_ ~RWLockWrite() {
|
||||
lock.write_unlock();
|
||||
}
|
||||
};
|
145
core/os/safe_binary_mutex.h
Normal file
145
core/os/safe_binary_mutex.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/**************************************************************************/
|
||||
/* safe_binary_mutex.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/error/error_macros.h"
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#ifdef THREADS_ENABLED
|
||||
|
||||
GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wundefined-var-template")
|
||||
|
||||
// A very special kind of mutex, used in scenarios where these
|
||||
// requirements hold at the same time:
|
||||
// - Must be used with a condition variable (only binary mutexes are suitable).
|
||||
// - Must have recursive semnantics (or simulate, as this one does).
|
||||
// The implementation keeps the lock count in TS. Therefore, only
|
||||
// one object of each version of the template can exists; hence the Tag argument.
|
||||
// Tags must be unique across the Godot codebase.
|
||||
// Also, don't forget to declare the thread_local variable on each use.
|
||||
template <int Tag>
|
||||
class SafeBinaryMutex {
|
||||
friend class MutexLock<SafeBinaryMutex<Tag>>;
|
||||
|
||||
using StdMutexType = THREADING_NAMESPACE::mutex;
|
||||
|
||||
mutable THREADING_NAMESPACE::mutex mutex;
|
||||
|
||||
struct TLSData {
|
||||
mutable THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> lock;
|
||||
uint32_t count = 0;
|
||||
|
||||
TLSData(SafeBinaryMutex<Tag> &p_mutex) :
|
||||
lock(p_mutex.mutex, THREADING_NAMESPACE::defer_lock) {}
|
||||
};
|
||||
static thread_local TLSData tls_data;
|
||||
|
||||
public:
|
||||
_ALWAYS_INLINE_ void lock() const {
|
||||
if (++tls_data.count == 1) {
|
||||
tls_data.lock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ void unlock() const {
|
||||
DEV_ASSERT(tls_data.count);
|
||||
if (--tls_data.count == 0) {
|
||||
tls_data.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &_get_lock() const {
|
||||
return const_cast<THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &>(tls_data.lock);
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ SafeBinaryMutex() {
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ ~SafeBinaryMutex() {
|
||||
DEV_ASSERT(!tls_data.count);
|
||||
}
|
||||
};
|
||||
|
||||
template <int Tag>
|
||||
class MutexLock<SafeBinaryMutex<Tag>> {
|
||||
friend class ConditionVariable;
|
||||
|
||||
const SafeBinaryMutex<Tag> &mutex;
|
||||
|
||||
public:
|
||||
explicit MutexLock(const SafeBinaryMutex<Tag> &p_mutex) :
|
||||
mutex(p_mutex) {
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
~MutexLock() {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ void temp_relock() const {
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ void temp_unlock() const {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
// TODO: Implement a `try_temp_relock` if needed (will also need a dummy method below).
|
||||
};
|
||||
|
||||
GODOT_CLANG_WARNING_POP
|
||||
|
||||
#else // No threads.
|
||||
|
||||
template <int Tag>
|
||||
class SafeBinaryMutex {
|
||||
struct TLSData {
|
||||
TLSData(SafeBinaryMutex<Tag> &p_mutex) {}
|
||||
};
|
||||
static thread_local TLSData tls_data;
|
||||
|
||||
public:
|
||||
void lock() const {}
|
||||
void unlock() const {}
|
||||
};
|
||||
|
||||
template <int Tag>
|
||||
class MutexLock<SafeBinaryMutex<Tag>> {
|
||||
public:
|
||||
MutexLock(const SafeBinaryMutex<Tag> &p_mutex) {}
|
||||
~MutexLock() {}
|
||||
|
||||
void temp_relock() const {}
|
||||
void temp_unlock() const {}
|
||||
};
|
||||
|
||||
#endif // THREADS_ENABLED
|
147
core/os/semaphore.h
Normal file
147
core/os/semaphore.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/**************************************************************************/
|
||||
/* semaphore.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#ifdef THREADS_ENABLED
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
#include "core/error/error_macros.h"
|
||||
#endif
|
||||
|
||||
#ifdef MINGW_ENABLED
|
||||
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
|
||||
#include "thirdparty/mingw-std-threads/mingw.condition_variable.h"
|
||||
#include "thirdparty/mingw-std-threads/mingw.mutex.h"
|
||||
#define THREADING_NAMESPACE mingw_stdthread
|
||||
#else
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#define THREADING_NAMESPACE std
|
||||
#endif
|
||||
|
||||
class Semaphore {
|
||||
private:
|
||||
mutable THREADING_NAMESPACE::mutex mutex;
|
||||
mutable THREADING_NAMESPACE::condition_variable condition;
|
||||
mutable uint32_t count = 0; // Initialized as locked.
|
||||
#ifdef DEBUG_ENABLED
|
||||
mutable uint32_t awaiters = 0;
|
||||
#endif
|
||||
|
||||
public:
|
||||
_ALWAYS_INLINE_ void post(uint32_t p_count = 1) const {
|
||||
std::lock_guard lock(mutex);
|
||||
count += p_count;
|
||||
for (uint32_t i = 0; i < p_count; ++i) {
|
||||
condition.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ void wait() const {
|
||||
THREADING_NAMESPACE::unique_lock lock(mutex);
|
||||
#ifdef DEBUG_ENABLED
|
||||
++awaiters;
|
||||
#endif
|
||||
while (!count) { // Handle spurious wake-ups.
|
||||
condition.wait(lock);
|
||||
}
|
||||
--count;
|
||||
#ifdef DEBUG_ENABLED
|
||||
--awaiters;
|
||||
#endif
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ bool try_wait() const {
|
||||
std::lock_guard lock(mutex);
|
||||
if (count) {
|
||||
count--;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
~Semaphore() {
|
||||
// Destroying an std::condition_variable when not all threads waiting on it have been notified
|
||||
// invokes undefined behavior (e.g., it may be nicely destroyed or it may be awaited forever.)
|
||||
// That means other threads could still be running the body of std::condition_variable::wait()
|
||||
// but already past the safety checkpoint. That's the case for instance if that function is already
|
||||
// waiting to lock again.
|
||||
//
|
||||
// We will make the rule a bit more restrictive and simpler to understand at the same time: there
|
||||
// should not be any threads at any stage of the waiting by the time the semaphore is destroyed.
|
||||
//
|
||||
// We do so because of the following reasons:
|
||||
// - We have the guideline that threads must be awaited (i.e., completed), so the waiting thread
|
||||
// must be completely done by the time the thread controlling it finally destroys the semaphore.
|
||||
// Therefore, only a coding mistake could make the program run into such a attempt at premature
|
||||
// destruction of the semaphore.
|
||||
// - In scripting, given that Semaphores are wrapped by RefCounted classes, in general it can't
|
||||
// happen that a thread is trying to destroy a Semaphore while another is still doing whatever with
|
||||
// it, so the simplification is mostly transparent to script writers.
|
||||
// - The redefined rule can be checked for failure to meet it, which is what this implementation does.
|
||||
// This is useful to detect a few cases of potential misuse; namely:
|
||||
// a) In scripting:
|
||||
// * The coder is naughtily dealing with the reference count causing a semaphore to die prematurely.
|
||||
// * The coder is letting the project reach its termination without having cleanly finished threads
|
||||
// that await on semaphores (or at least, let the usual semaphore-controlled loop exit).
|
||||
// b) In the native side, where Semaphore is not a ref-counted beast and certain coding mistakes can
|
||||
// lead to its premature destruction as well.
|
||||
//
|
||||
// Let's let users know they are doing it wrong, but apply a, somewhat hacky, countermeasure against UB
|
||||
// in debug builds.
|
||||
std::lock_guard lock(mutex);
|
||||
if (awaiters) {
|
||||
WARN_PRINT(
|
||||
"A Semaphore object is being destroyed while one or more threads are still waiting on it.\n"
|
||||
"Please call post() on it as necessary to prevent such a situation and so ensure correct cleanup.");
|
||||
// And now, the hacky countermeasure (i.e., leak the condition variable).
|
||||
new (&condition) THREADING_NAMESPACE::condition_variable();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#else // No threads.
|
||||
|
||||
class Semaphore {
|
||||
public:
|
||||
void post(uint32_t p_count = 1) const {}
|
||||
void wait() const {}
|
||||
bool try_wait() const {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // THREADS_ENABLED
|
48
core/os/shared_object.h
Normal file
48
core/os/shared_object.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/**************************************************************************/
|
||||
/* shared_object.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/string/ustring.h"
|
||||
#include "core/templates/vector.h"
|
||||
|
||||
struct SharedObject {
|
||||
String path;
|
||||
Vector<String> tags;
|
||||
String target;
|
||||
|
||||
SharedObject(const String &p_path, const Vector<String> &p_tags, const String &p_target) :
|
||||
path(p_path),
|
||||
tags(p_tags),
|
||||
target(p_target) {
|
||||
}
|
||||
|
||||
SharedObject() {}
|
||||
};
|
128
core/os/spin_lock.h
Normal file
128
core/os/spin_lock.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/**************************************************************************/
|
||||
/* spin_lock.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/os/thread.h"
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#ifdef THREADS_ENABLED
|
||||
|
||||
// Note the implementations below avoid false sharing by ensuring their
|
||||
// sizes match the assumed cache line. We can't use align attributes
|
||||
// because these objects may end up unaligned in semi-tightly packed arrays.
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
#include <os/lock.h>
|
||||
|
||||
class SpinLock {
|
||||
union {
|
||||
mutable os_unfair_lock _lock = OS_UNFAIR_LOCK_INIT;
|
||||
char aligner[Thread::CACHE_LINE_BYTES];
|
||||
};
|
||||
|
||||
public:
|
||||
_ALWAYS_INLINE_ void lock() const {
|
||||
os_unfair_lock_lock(&_lock);
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ void unlock() const {
|
||||
os_unfair_lock_unlock(&_lock);
|
||||
}
|
||||
};
|
||||
|
||||
#else // __APPLE__
|
||||
|
||||
#include <atomic>
|
||||
|
||||
_ALWAYS_INLINE_ static void _cpu_pause() {
|
||||
#if defined(_MSC_VER)
|
||||
// ----- MSVC.
|
||||
#if defined(_M_ARM) || defined(_M_ARM64) // ARM.
|
||||
__yield();
|
||||
#elif defined(_M_IX86) || defined(_M_X64) // x86.
|
||||
_mm_pause();
|
||||
#endif
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
// ----- GCC/Clang.
|
||||
#if defined(__i386__) || defined(__x86_64__) // x86.
|
||||
__builtin_ia32_pause();
|
||||
#elif defined(__arm__) || defined(__aarch64__) // ARM.
|
||||
asm volatile("yield");
|
||||
#elif defined(__powerpc__) // PowerPC.
|
||||
asm volatile("or 27,27,27");
|
||||
#elif defined(__riscv) // RISC-V.
|
||||
asm volatile(".insn i 0x0F, 0, x0, x0, 0x010");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static_assert(std::atomic_bool::is_always_lock_free);
|
||||
|
||||
class SpinLock {
|
||||
union {
|
||||
mutable std::atomic<bool> locked = ATOMIC_VAR_INIT(false);
|
||||
char aligner[Thread::CACHE_LINE_BYTES];
|
||||
};
|
||||
|
||||
public:
|
||||
_ALWAYS_INLINE_ void lock() const {
|
||||
while (true) {
|
||||
bool expected = false;
|
||||
if (locked.compare_exchange_weak(expected, true, std::memory_order_acquire, std::memory_order_relaxed)) {
|
||||
break;
|
||||
}
|
||||
do {
|
||||
_cpu_pause();
|
||||
} while (locked.load(std::memory_order_relaxed));
|
||||
}
|
||||
}
|
||||
|
||||
_ALWAYS_INLINE_ void unlock() const {
|
||||
locked.store(false, std::memory_order_release);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __APPLE__
|
||||
|
||||
#else // THREADS_ENABLED
|
||||
|
||||
class SpinLock {
|
||||
public:
|
||||
void lock() const {}
|
||||
void unlock() const {}
|
||||
};
|
||||
|
||||
#endif // THREADS_ENABLED
|
115
core/os/thread.cpp
Normal file
115
core/os/thread.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
/**************************************************************************/
|
||||
/* thread.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 "platform_config.h"
|
||||
|
||||
#ifndef PLATFORM_THREAD_OVERRIDE // See details in thread.h.
|
||||
|
||||
#include "thread.h"
|
||||
|
||||
#ifdef THREADS_ENABLED
|
||||
#include "core/object/script_language.h"
|
||||
|
||||
SafeNumeric<uint64_t> Thread::id_counter(1); // The first value after .increment() is 2, hence by default the main thread ID should be 1.
|
||||
thread_local Thread::ID Thread::caller_id = Thread::id_counter.increment();
|
||||
|
||||
#endif
|
||||
|
||||
Thread::PlatformFunctions Thread::platform_functions;
|
||||
|
||||
void Thread::_set_platform_functions(const PlatformFunctions &p_functions) {
|
||||
platform_functions = p_functions;
|
||||
}
|
||||
|
||||
#ifdef THREADS_ENABLED
|
||||
void Thread::callback(ID p_caller_id, const Settings &p_settings, Callback p_callback, void *p_userdata) {
|
||||
Thread::caller_id = p_caller_id;
|
||||
if (platform_functions.set_priority) {
|
||||
platform_functions.set_priority(p_settings.priority);
|
||||
}
|
||||
if (platform_functions.init) {
|
||||
platform_functions.init();
|
||||
}
|
||||
ScriptServer::thread_enter(); // Scripts may need to attach a stack.
|
||||
if (platform_functions.wrapper) {
|
||||
platform_functions.wrapper(p_callback, p_userdata);
|
||||
} else {
|
||||
p_callback(p_userdata);
|
||||
}
|
||||
ScriptServer::thread_exit();
|
||||
if (platform_functions.term) {
|
||||
platform_functions.term();
|
||||
}
|
||||
}
|
||||
|
||||
Thread::ID Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_settings) {
|
||||
ERR_FAIL_COND_V_MSG(id != UNASSIGNED_ID, UNASSIGNED_ID, "A Thread object has been re-started without wait_to_finish() having been called on it.");
|
||||
id = id_counter.increment();
|
||||
thread = THREADING_NAMESPACE::thread(&Thread::callback, id, p_settings, p_callback, p_user);
|
||||
return id;
|
||||
}
|
||||
|
||||
bool Thread::is_started() const {
|
||||
return id != UNASSIGNED_ID;
|
||||
}
|
||||
|
||||
void Thread::wait_to_finish() {
|
||||
ERR_FAIL_COND_MSG(id == UNASSIGNED_ID, "Attempt of waiting to finish on a thread that was never started.");
|
||||
ERR_FAIL_COND_MSG(id == get_caller_id(), "Threads can't wait to finish on themselves, another thread must wait.");
|
||||
thread.join();
|
||||
thread = THREADING_NAMESPACE::thread();
|
||||
id = UNASSIGNED_ID;
|
||||
}
|
||||
|
||||
Error Thread::set_name(const String &p_name) {
|
||||
if (platform_functions.set_name) {
|
||||
return platform_functions.set_name(p_name);
|
||||
}
|
||||
|
||||
return ERR_UNAVAILABLE;
|
||||
}
|
||||
|
||||
Thread::Thread() {
|
||||
}
|
||||
|
||||
Thread::~Thread() {
|
||||
if (id != UNASSIGNED_ID) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
WARN_PRINT(
|
||||
"A Thread object is being destroyed without its completion having been realized.\n"
|
||||
"Please call wait_to_finish() on it to ensure correct cleanup.");
|
||||
#endif
|
||||
thread.detach();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // THREADS_ENABLED
|
||||
|
||||
#endif // PLATFORM_THREAD_OVERRIDE
|
207
core/os/thread.h
Normal file
207
core/os/thread.h
Normal file
@@ -0,0 +1,207 @@
|
||||
/**************************************************************************/
|
||||
/* thread.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "platform_config.h"
|
||||
|
||||
// Define PLATFORM_THREAD_OVERRIDE in your platform's `platform_config.h`
|
||||
// to use a custom Thread implementation defined in `platform/[your_platform]/platform_thread.h`.
|
||||
// Overriding the Thread implementation is required in some proprietary platforms.
|
||||
|
||||
#ifdef PLATFORM_THREAD_OVERRIDE
|
||||
|
||||
#include "platform_thread.h"
|
||||
|
||||
#else
|
||||
|
||||
#include "core/typedefs.h"
|
||||
|
||||
#ifdef THREADS_ENABLED
|
||||
|
||||
#include "core/templates/safe_refcount.h"
|
||||
|
||||
#include <new> // IWYU pragma: keep // For hardware interference size.
|
||||
|
||||
#ifdef MINGW_ENABLED
|
||||
#define MINGW_STDTHREAD_REDUNDANCY_WARNING
|
||||
#include "thirdparty/mingw-std-threads/mingw.thread.h"
|
||||
#define THREADING_NAMESPACE mingw_stdthread
|
||||
#else
|
||||
#include <thread>
|
||||
#define THREADING_NAMESPACE std
|
||||
#endif
|
||||
|
||||
class String;
|
||||
|
||||
class Thread {
|
||||
public:
|
||||
typedef void (*Callback)(void *p_userdata);
|
||||
|
||||
typedef uint64_t ID;
|
||||
|
||||
enum : ID {
|
||||
UNASSIGNED_ID = 0,
|
||||
MAIN_ID = 1
|
||||
};
|
||||
|
||||
enum Priority {
|
||||
PRIORITY_LOW,
|
||||
PRIORITY_NORMAL,
|
||||
PRIORITY_HIGH
|
||||
};
|
||||
|
||||
struct Settings {
|
||||
Priority priority;
|
||||
Settings() { priority = PRIORITY_NORMAL; }
|
||||
};
|
||||
|
||||
struct PlatformFunctions {
|
||||
Error (*set_name)(const String &) = nullptr;
|
||||
void (*set_priority)(Thread::Priority) = nullptr;
|
||||
void (*init)() = nullptr;
|
||||
void (*wrapper)(Thread::Callback, void *) = nullptr;
|
||||
void (*term)() = nullptr;
|
||||
};
|
||||
|
||||
#if defined(__cpp_lib_hardware_interference_size) && !defined(ANDROID_ENABLED) // This would be OK with NDK >= 26.
|
||||
GODOT_GCC_WARNING_PUSH_AND_IGNORE("-Winterference-size")
|
||||
static constexpr size_t CACHE_LINE_BYTES = std::hardware_destructive_interference_size;
|
||||
GODOT_GCC_WARNING_POP
|
||||
#else
|
||||
// At a negligible memory cost, we use a conservatively high value.
|
||||
static constexpr size_t CACHE_LINE_BYTES = 128;
|
||||
#endif
|
||||
|
||||
private:
|
||||
friend class Main;
|
||||
|
||||
static PlatformFunctions platform_functions;
|
||||
|
||||
ID id = UNASSIGNED_ID;
|
||||
|
||||
static SafeNumeric<uint64_t> id_counter;
|
||||
static thread_local ID caller_id;
|
||||
THREADING_NAMESPACE::thread thread;
|
||||
|
||||
static void callback(ID p_caller_id, const Settings &p_settings, Thread::Callback p_callback, void *p_userdata);
|
||||
|
||||
static void make_main_thread() { caller_id = MAIN_ID; }
|
||||
static void release_main_thread() { caller_id = id_counter.increment(); }
|
||||
|
||||
public:
|
||||
static void _set_platform_functions(const PlatformFunctions &p_functions);
|
||||
|
||||
_FORCE_INLINE_ static void yield() { std::this_thread::yield(); }
|
||||
|
||||
_FORCE_INLINE_ ID get_id() const { return id; }
|
||||
// get the ID of the caller thread
|
||||
_FORCE_INLINE_ static ID get_caller_id() {
|
||||
return caller_id;
|
||||
}
|
||||
// get the ID of the main thread
|
||||
_FORCE_INLINE_ static ID get_main_id() { return MAIN_ID; }
|
||||
|
||||
_FORCE_INLINE_ static bool is_main_thread() { return caller_id == MAIN_ID; } // Gain a tiny bit of perf here because there is no need to validate caller_id here, because only main thread will be set as 1.
|
||||
|
||||
static Error set_name(const String &p_name);
|
||||
|
||||
ID start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings());
|
||||
bool is_started() const;
|
||||
///< waits until thread is finished, and deallocates it.
|
||||
void wait_to_finish();
|
||||
|
||||
Thread();
|
||||
~Thread();
|
||||
};
|
||||
|
||||
#else // No threads.
|
||||
|
||||
class String;
|
||||
|
||||
class Thread {
|
||||
public:
|
||||
typedef void (*Callback)(void *p_userdata);
|
||||
|
||||
typedef uint64_t ID;
|
||||
|
||||
static constexpr size_t CACHE_LINE_BYTES = sizeof(void *);
|
||||
|
||||
enum : ID {
|
||||
UNASSIGNED_ID = 0,
|
||||
MAIN_ID = 1
|
||||
};
|
||||
|
||||
enum Priority {
|
||||
PRIORITY_LOW,
|
||||
PRIORITY_NORMAL,
|
||||
PRIORITY_HIGH
|
||||
};
|
||||
|
||||
struct Settings {
|
||||
Priority priority;
|
||||
Settings() { priority = PRIORITY_NORMAL; }
|
||||
};
|
||||
|
||||
struct PlatformFunctions {
|
||||
Error (*set_name)(const String &) = nullptr;
|
||||
void (*set_priority)(Thread::Priority) = nullptr;
|
||||
void (*init)() = nullptr;
|
||||
void (*wrapper)(Thread::Callback, void *) = nullptr;
|
||||
void (*term)() = nullptr;
|
||||
};
|
||||
|
||||
private:
|
||||
friend class Main;
|
||||
|
||||
static PlatformFunctions platform_functions;
|
||||
|
||||
static void make_main_thread() {}
|
||||
static void release_main_thread() {}
|
||||
|
||||
public:
|
||||
static void _set_platform_functions(const PlatformFunctions &p_functions);
|
||||
|
||||
_FORCE_INLINE_ ID get_id() const { return 0; }
|
||||
_FORCE_INLINE_ static ID get_caller_id() { return MAIN_ID; }
|
||||
_FORCE_INLINE_ static ID get_main_id() { return MAIN_ID; }
|
||||
|
||||
_FORCE_INLINE_ static bool is_main_thread() { return true; }
|
||||
|
||||
static Error set_name(const String &p_name) { return ERR_UNAVAILABLE; }
|
||||
|
||||
void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings()) {}
|
||||
bool is_started() const { return false; }
|
||||
void wait_to_finish() {}
|
||||
};
|
||||
|
||||
#endif // THREADS_ENABLED
|
||||
|
||||
#endif // PLATFORM_THREAD_OVERRIDE
|
46
core/os/thread_safe.cpp
Normal file
46
core/os/thread_safe.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/**************************************************************************/
|
||||
/* thread_safe.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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef THREAD_SAFE_CPP
|
||||
#define THREAD_SAFE_CPP
|
||||
|
||||
#include "thread_safe.h"
|
||||
|
||||
static thread_local bool current_thread_safe_for_nodes = false;
|
||||
|
||||
bool is_current_thread_safe_for_nodes() {
|
||||
return current_thread_safe_for_nodes;
|
||||
}
|
||||
|
||||
void set_current_thread_safe_for_nodes(bool p_safe) {
|
||||
current_thread_safe_for_nodes = p_safe;
|
||||
}
|
||||
|
||||
#endif // THREAD_SAFE_CPP
|
41
core/os/thread_safe.h
Normal file
41
core/os/thread_safe.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/**************************************************************************/
|
||||
/* thread_safe.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/os/mutex.h" // IWYU pragma: keep // Used in macro.
|
||||
|
||||
#define _THREAD_SAFE_CLASS_ mutable Mutex _thread_safe_;
|
||||
#define _THREAD_SAFE_METHOD_ MutexLock _thread_safe_method_(_thread_safe_);
|
||||
#define _THREAD_SAFE_LOCK_ _thread_safe_.lock();
|
||||
#define _THREAD_SAFE_UNLOCK_ _thread_safe_.unlock();
|
||||
|
||||
bool is_current_thread_safe_for_nodes();
|
||||
void set_current_thread_safe_for_nodes(bool p_safe);
|
444
core/os/time.cpp
Normal file
444
core/os/time.cpp
Normal file
@@ -0,0 +1,444 @@
|
||||
/**************************************************************************/
|
||||
/* time.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 "time.h" // NOLINT(modernize-deprecated-headers) False positive with C-Header of the same name.
|
||||
|
||||
#include "core/os/os.h"
|
||||
|
||||
#define UNIX_EPOCH_YEAR_AD 1970 // 1970
|
||||
#define SECONDS_PER_DAY (24 * 60 * 60) // 86400
|
||||
#define IS_LEAP_YEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400)))
|
||||
#define YEAR_SIZE(year) (IS_LEAP_YEAR(year) ? 366 : 365)
|
||||
|
||||
static constexpr int64_t total_leap_days(int64_t p_year) {
|
||||
if (p_year > 0) {
|
||||
--p_year;
|
||||
return 1 + (p_year / 4 - p_year / 100 + p_year / 400);
|
||||
}
|
||||
|
||||
return p_year / 4 - p_year / 100 + p_year / 400;
|
||||
}
|
||||
|
||||
static constexpr int64_t year_to_days(int64_t p_year) {
|
||||
return p_year * 365 + total_leap_days(p_year);
|
||||
}
|
||||
|
||||
static constexpr int64_t days_to_year(int64_t p_days) {
|
||||
int64_t year = 400 * p_days / year_to_days(400);
|
||||
if (year < 0) {
|
||||
--year;
|
||||
}
|
||||
if (year_to_days(year) > p_days) {
|
||||
--year;
|
||||
}
|
||||
if (year_to_days(year + 1) <= p_days) {
|
||||
++year;
|
||||
}
|
||||
return year;
|
||||
}
|
||||
|
||||
#define YEAR_KEY "year"
|
||||
#define MONTH_KEY "month"
|
||||
#define DAY_KEY "day"
|
||||
#define WEEKDAY_KEY "weekday"
|
||||
#define HOUR_KEY "hour"
|
||||
#define MINUTE_KEY "minute"
|
||||
#define SECOND_KEY "second"
|
||||
#define DST_KEY "dst"
|
||||
|
||||
// Table of number of days in each month (for regular year and leap year).
|
||||
static const uint8_t MONTH_DAYS_TABLE[2][12] = {
|
||||
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
|
||||
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
|
||||
};
|
||||
|
||||
#define UNIX_TIME_TO_HMS \
|
||||
uint8_t hour, minute, second; \
|
||||
{ \
|
||||
/* The time of the day (in seconds since start of day). */ \
|
||||
uint32_t day_clock = Math::posmod(p_unix_time_val, SECONDS_PER_DAY); \
|
||||
/* On x86 these 4 lines can be optimized to only 2 divisions. */ \
|
||||
second = day_clock % 60; \
|
||||
day_clock /= 60; \
|
||||
minute = day_clock % 60; \
|
||||
hour = day_clock / 60; \
|
||||
}
|
||||
|
||||
#define UNIX_TIME_TO_YMD \
|
||||
int64_t year; \
|
||||
Month month; \
|
||||
uint8_t day; \
|
||||
/* The day number since Unix epoch (0-index). Days before 1970 are negative. */ \
|
||||
int64_t day_number = Math::floor(p_unix_time_val / (double)SECONDS_PER_DAY); \
|
||||
{ \
|
||||
int64_t day_number_copy = day_number; \
|
||||
day_number_copy += year_to_days(UNIX_EPOCH_YEAR_AD); \
|
||||
year = days_to_year(day_number_copy); \
|
||||
day_number_copy -= year_to_days(year); \
|
||||
uint8_t month_zero_index = 0; \
|
||||
/* After the above, day_number now represents the day of the year (0-index). */ \
|
||||
while (day_number_copy >= MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][month_zero_index]) { \
|
||||
day_number_copy -= MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][month_zero_index]; \
|
||||
month_zero_index++; \
|
||||
} \
|
||||
/* After the above, day_number now represents the day of the month (0-index). */ \
|
||||
month = (Month)(month_zero_index + 1); \
|
||||
day = day_number_copy + 1; \
|
||||
}
|
||||
|
||||
#define VALIDATE_YMDHMS(ret) \
|
||||
ERR_FAIL_COND_V_MSG(month == 0, ret, "Invalid month value of: " + itos(month) + ", months are 1-indexed and cannot be 0. See the Time.Month enum for valid values."); \
|
||||
ERR_FAIL_COND_V_MSG(month < 0, ret, "Invalid month value of: " + itos(month) + "."); \
|
||||
ERR_FAIL_COND_V_MSG(month > 12, ret, "Invalid month value of: " + itos(month) + ". See the Time.Month enum for valid values."); \
|
||||
ERR_FAIL_COND_V_MSG(hour > 23, ret, "Invalid hour value of: " + itos(hour) + "."); \
|
||||
ERR_FAIL_COND_V_MSG(hour < 0, ret, "Invalid hour value of: " + itos(hour) + "."); \
|
||||
ERR_FAIL_COND_V_MSG(minute > 59, ret, "Invalid minute value of: " + itos(minute) + "."); \
|
||||
ERR_FAIL_COND_V_MSG(minute < 0, ret, "Invalid minute value of: " + itos(minute) + "."); \
|
||||
ERR_FAIL_COND_V_MSG(second > 59, ret, "Invalid second value of: " + itos(second) + " (leap seconds are not supported)."); \
|
||||
ERR_FAIL_COND_V_MSG(second < 0, ret, "Invalid second value of: " + itos(second) + "."); \
|
||||
ERR_FAIL_COND_V_MSG(day == 0, ret, "Invalid day value of: " + itos(day) + ", days are 1-indexed and cannot be 0."); \
|
||||
ERR_FAIL_COND_V_MSG(day < 0, ret, "Invalid day value of: " + itos(day) + "."); \
|
||||
/* Do this check after month is tested as valid. */ \
|
||||
uint8_t days_in_this_month = MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][month - 1]; \
|
||||
ERR_FAIL_COND_V_MSG(day > days_in_this_month, ret, "Invalid day value of: " + itos(day) + " which is larger than the maximum for this month, " + itos(days_in_this_month) + ".");
|
||||
|
||||
#define YMD_TO_DAY_NUMBER \
|
||||
/* The day number since Unix epoch (0-index). Days before 1970 are negative. */ \
|
||||
int64_t day_number = day - 1; \
|
||||
/* Add the days in the months to day_number. */ \
|
||||
for (int i = 0; i < month - 1; i++) { \
|
||||
day_number += MONTH_DAYS_TABLE[IS_LEAP_YEAR(year)][i]; \
|
||||
} \
|
||||
/* Add the days in the years to day_number. */ \
|
||||
day_number += year_to_days(year); \
|
||||
day_number -= year_to_days(UNIX_EPOCH_YEAR_AD);
|
||||
|
||||
#define PARSE_ISO8601_STRING(ret) \
|
||||
int64_t year = UNIX_EPOCH_YEAR_AD; \
|
||||
Month month = MONTH_JANUARY; \
|
||||
int day = 1; \
|
||||
int hour = 0; \
|
||||
int minute = 0; \
|
||||
int second = 0; \
|
||||
{ \
|
||||
bool has_date = false, has_time = false; \
|
||||
String date, time; \
|
||||
if (p_datetime.find_char('T') > 0) { \
|
||||
has_date = has_time = true; \
|
||||
PackedStringArray array = p_datetime.split("T"); \
|
||||
ERR_FAIL_COND_V_MSG(array.size() < 2, ret, "Invalid ISO 8601 date/time string."); \
|
||||
date = array[0]; \
|
||||
time = array[1]; \
|
||||
} else if (p_datetime.find_char(' ') > 0) { \
|
||||
has_date = has_time = true; \
|
||||
PackedStringArray array = p_datetime.split(" "); \
|
||||
ERR_FAIL_COND_V_MSG(array.size() < 2, ret, "Invalid ISO 8601 date/time string."); \
|
||||
date = array[0]; \
|
||||
time = array[1]; \
|
||||
} else if (p_datetime.find_char('-', 1) > 0) { \
|
||||
has_date = true; \
|
||||
date = p_datetime; \
|
||||
} else if (p_datetime.find_char(':') > 0) { \
|
||||
has_time = true; \
|
||||
time = p_datetime; \
|
||||
} \
|
||||
/* Set the variables from the contents of the string. */ \
|
||||
if (has_date) { \
|
||||
PackedInt32Array array = date.split_ints("-", false); \
|
||||
ERR_FAIL_COND_V_MSG(array.size() < 3, ret, "Invalid ISO 8601 date string."); \
|
||||
year = array[0]; \
|
||||
month = (Month)array[1]; \
|
||||
day = array[2]; \
|
||||
/* Handle negative years. */ \
|
||||
if (p_datetime.find_char('-') == 0) { \
|
||||
year *= -1; \
|
||||
} \
|
||||
} \
|
||||
if (has_time) { \
|
||||
PackedInt32Array array = time.split_ints(":", false); \
|
||||
ERR_FAIL_COND_V_MSG(array.size() < 3, ret, "Invalid ISO 8601 time string."); \
|
||||
hour = array[0]; \
|
||||
minute = array[1]; \
|
||||
second = array[2]; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define EXTRACT_FROM_DICTIONARY \
|
||||
/* Get all time values from the dictionary. If it doesn't exist, set the */ \
|
||||
/* values to the default values for Unix epoch (1970-01-01 00:00:00). */ \
|
||||
int64_t year = p_datetime.has(YEAR_KEY) ? int64_t(p_datetime[YEAR_KEY]) : UNIX_EPOCH_YEAR_AD; \
|
||||
Month month = Month((p_datetime.has(MONTH_KEY)) ? int(p_datetime[MONTH_KEY]) : 1); \
|
||||
int day = p_datetime.has(DAY_KEY) ? int(p_datetime[DAY_KEY]) : 1; \
|
||||
int hour = p_datetime.has(HOUR_KEY) ? int(p_datetime[HOUR_KEY]) : 0; \
|
||||
int minute = p_datetime.has(MINUTE_KEY) ? int(p_datetime[MINUTE_KEY]) : 0; \
|
||||
int second = p_datetime.has(SECOND_KEY) ? int(p_datetime[SECOND_KEY]) : 0;
|
||||
|
||||
Time *Time::singleton = nullptr;
|
||||
|
||||
Time *Time::get_singleton() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
Dictionary Time::get_datetime_dict_from_unix_time(int64_t p_unix_time_val) const {
|
||||
UNIX_TIME_TO_HMS
|
||||
UNIX_TIME_TO_YMD
|
||||
Dictionary datetime;
|
||||
datetime[YEAR_KEY] = year;
|
||||
datetime[MONTH_KEY] = (uint8_t)month;
|
||||
datetime[DAY_KEY] = day;
|
||||
// Unix epoch was a Thursday (day 0 aka 1970-01-01).
|
||||
datetime[WEEKDAY_KEY] = Math::posmod(day_number + WEEKDAY_THURSDAY, 7);
|
||||
datetime[HOUR_KEY] = hour;
|
||||
datetime[MINUTE_KEY] = minute;
|
||||
datetime[SECOND_KEY] = second;
|
||||
|
||||
return datetime;
|
||||
}
|
||||
|
||||
Dictionary Time::get_date_dict_from_unix_time(int64_t p_unix_time_val) const {
|
||||
UNIX_TIME_TO_YMD
|
||||
Dictionary datetime;
|
||||
datetime[YEAR_KEY] = year;
|
||||
datetime[MONTH_KEY] = (uint8_t)month;
|
||||
datetime[DAY_KEY] = day;
|
||||
// Unix epoch was a Thursday (day 0 aka 1970-01-01).
|
||||
datetime[WEEKDAY_KEY] = Math::posmod(day_number + WEEKDAY_THURSDAY, 7);
|
||||
|
||||
return datetime;
|
||||
}
|
||||
|
||||
Dictionary Time::get_time_dict_from_unix_time(int64_t p_unix_time_val) const {
|
||||
UNIX_TIME_TO_HMS
|
||||
Dictionary datetime;
|
||||
datetime[HOUR_KEY] = hour;
|
||||
datetime[MINUTE_KEY] = minute;
|
||||
datetime[SECOND_KEY] = second;
|
||||
|
||||
return datetime;
|
||||
}
|
||||
|
||||
String Time::get_datetime_string_from_unix_time(int64_t p_unix_time_val, bool p_use_space) const {
|
||||
UNIX_TIME_TO_HMS
|
||||
UNIX_TIME_TO_YMD
|
||||
const String format_string = p_use_space ? "%04d-%02d-%02d %02d:%02d:%02d" : "%04d-%02d-%02dT%02d:%02d:%02d";
|
||||
return vformat(format_string, year, (uint8_t)month, day, hour, minute, second);
|
||||
}
|
||||
|
||||
String Time::get_date_string_from_unix_time(int64_t p_unix_time_val) const {
|
||||
UNIX_TIME_TO_YMD
|
||||
// Android is picky about the types passed to make Variant, so we need a cast.
|
||||
return vformat("%04d-%02d-%02d", year, (uint8_t)month, day);
|
||||
}
|
||||
|
||||
String Time::get_time_string_from_unix_time(int64_t p_unix_time_val) const {
|
||||
UNIX_TIME_TO_HMS
|
||||
return vformat("%02d:%02d:%02d", hour, minute, second);
|
||||
}
|
||||
|
||||
Dictionary Time::get_datetime_dict_from_datetime_string(const String &p_datetime, bool p_weekday) const {
|
||||
PARSE_ISO8601_STRING(Dictionary())
|
||||
Dictionary dict;
|
||||
dict[YEAR_KEY] = year;
|
||||
dict[MONTH_KEY] = (uint8_t)month;
|
||||
dict[DAY_KEY] = day;
|
||||
if (p_weekday) {
|
||||
YMD_TO_DAY_NUMBER
|
||||
// Unix epoch was a Thursday (day 0 aka 1970-01-01).
|
||||
dict[WEEKDAY_KEY] = Math::posmod(day_number + WEEKDAY_THURSDAY, 7);
|
||||
}
|
||||
dict[HOUR_KEY] = hour;
|
||||
dict[MINUTE_KEY] = minute;
|
||||
dict[SECOND_KEY] = second;
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
String Time::get_datetime_string_from_datetime_dict(const Dictionary &p_datetime, bool p_use_space) const {
|
||||
ERR_FAIL_COND_V_MSG(p_datetime.is_empty(), "", "Invalid datetime Dictionary: Dictionary is empty.");
|
||||
EXTRACT_FROM_DICTIONARY
|
||||
VALIDATE_YMDHMS("")
|
||||
const String format_string = p_use_space ? "%04d-%02d-%02d %02d:%02d:%02d" : "%04d-%02d-%02dT%02d:%02d:%02d";
|
||||
return vformat(format_string, year, (uint8_t)month, day, hour, minute, second);
|
||||
}
|
||||
|
||||
int64_t Time::get_unix_time_from_datetime_dict(const Dictionary &p_datetime) const {
|
||||
ERR_FAIL_COND_V_MSG(p_datetime.is_empty(), 0, "Invalid datetime Dictionary: Dictionary is empty");
|
||||
EXTRACT_FROM_DICTIONARY
|
||||
VALIDATE_YMDHMS(0)
|
||||
YMD_TO_DAY_NUMBER
|
||||
return day_number * SECONDS_PER_DAY + hour * 3600 + minute * 60 + second;
|
||||
}
|
||||
|
||||
int64_t Time::get_unix_time_from_datetime_string(const String &p_datetime) const {
|
||||
PARSE_ISO8601_STRING(-1)
|
||||
VALIDATE_YMDHMS(0)
|
||||
YMD_TO_DAY_NUMBER
|
||||
return day_number * SECONDS_PER_DAY + hour * 3600 + minute * 60 + second;
|
||||
}
|
||||
|
||||
String Time::get_offset_string_from_offset_minutes(int64_t p_offset_minutes) const {
|
||||
String sign;
|
||||
if (p_offset_minutes < 0) {
|
||||
sign = "-";
|
||||
p_offset_minutes = -p_offset_minutes;
|
||||
} else {
|
||||
sign = "+";
|
||||
}
|
||||
// These two lines can be optimized to one instruction on x86 and others.
|
||||
// Note that % is acceptable here only because we ensure it's positive.
|
||||
int64_t offset_hours = p_offset_minutes / 60;
|
||||
int64_t offset_minutes = p_offset_minutes % 60;
|
||||
return vformat("%s%02d:%02d", sign, offset_hours, offset_minutes);
|
||||
}
|
||||
|
||||
Dictionary Time::get_datetime_dict_from_system(bool p_utc) const {
|
||||
OS::DateTime dt = OS::get_singleton()->get_datetime(p_utc);
|
||||
Dictionary datetime;
|
||||
datetime[YEAR_KEY] = dt.year;
|
||||
datetime[MONTH_KEY] = (uint8_t)dt.month;
|
||||
datetime[DAY_KEY] = dt.day;
|
||||
datetime[WEEKDAY_KEY] = (uint8_t)dt.weekday;
|
||||
datetime[HOUR_KEY] = dt.hour;
|
||||
datetime[MINUTE_KEY] = dt.minute;
|
||||
datetime[SECOND_KEY] = dt.second;
|
||||
datetime[DST_KEY] = dt.dst;
|
||||
return datetime;
|
||||
}
|
||||
|
||||
Dictionary Time::get_date_dict_from_system(bool p_utc) const {
|
||||
OS::DateTime dt = OS::get_singleton()->get_datetime(p_utc);
|
||||
Dictionary date_dictionary;
|
||||
date_dictionary[YEAR_KEY] = dt.year;
|
||||
date_dictionary[MONTH_KEY] = (uint8_t)dt.month;
|
||||
date_dictionary[DAY_KEY] = dt.day;
|
||||
date_dictionary[WEEKDAY_KEY] = (uint8_t)dt.weekday;
|
||||
return date_dictionary;
|
||||
}
|
||||
|
||||
Dictionary Time::get_time_dict_from_system(bool p_utc) const {
|
||||
OS::DateTime dt = OS::get_singleton()->get_datetime(p_utc);
|
||||
Dictionary time_dictionary;
|
||||
time_dictionary[HOUR_KEY] = dt.hour;
|
||||
time_dictionary[MINUTE_KEY] = dt.minute;
|
||||
time_dictionary[SECOND_KEY] = dt.second;
|
||||
return time_dictionary;
|
||||
}
|
||||
|
||||
String Time::get_datetime_string_from_system(bool p_utc, bool p_use_space) const {
|
||||
OS::DateTime dt = OS::get_singleton()->get_datetime(p_utc);
|
||||
const String format_string = p_use_space ? "%04d-%02d-%02d %02d:%02d:%02d" : "%04d-%02d-%02dT%02d:%02d:%02d";
|
||||
return vformat(format_string, dt.year, (uint8_t)dt.month, dt.day, dt.hour, dt.minute, dt.second);
|
||||
}
|
||||
|
||||
String Time::get_date_string_from_system(bool p_utc) const {
|
||||
OS::DateTime dt = OS::get_singleton()->get_datetime(p_utc);
|
||||
// Android is picky about the types passed to make Variant, so we need a cast.
|
||||
return vformat("%04d-%02d-%02d", dt.year, (uint8_t)dt.month, dt.day);
|
||||
}
|
||||
|
||||
String Time::get_time_string_from_system(bool p_utc) const {
|
||||
OS::DateTime dt = OS::get_singleton()->get_datetime(p_utc);
|
||||
return vformat("%02d:%02d:%02d", dt.hour, dt.minute, dt.second);
|
||||
}
|
||||
|
||||
Dictionary Time::get_time_zone_from_system() const {
|
||||
OS::TimeZoneInfo info = OS::get_singleton()->get_time_zone_info();
|
||||
Dictionary ret_timezone;
|
||||
ret_timezone["bias"] = info.bias;
|
||||
ret_timezone["name"] = info.name;
|
||||
return ret_timezone;
|
||||
}
|
||||
|
||||
double Time::get_unix_time_from_system() const {
|
||||
return OS::get_singleton()->get_unix_time();
|
||||
}
|
||||
|
||||
uint64_t Time::get_ticks_msec() const {
|
||||
return OS::get_singleton()->get_ticks_msec();
|
||||
}
|
||||
|
||||
uint64_t Time::get_ticks_usec() const {
|
||||
return OS::get_singleton()->get_ticks_usec();
|
||||
}
|
||||
|
||||
void Time::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("get_datetime_dict_from_unix_time", "unix_time_val"), &Time::get_datetime_dict_from_unix_time);
|
||||
ClassDB::bind_method(D_METHOD("get_date_dict_from_unix_time", "unix_time_val"), &Time::get_date_dict_from_unix_time);
|
||||
ClassDB::bind_method(D_METHOD("get_time_dict_from_unix_time", "unix_time_val"), &Time::get_time_dict_from_unix_time);
|
||||
ClassDB::bind_method(D_METHOD("get_datetime_string_from_unix_time", "unix_time_val", "use_space"), &Time::get_datetime_string_from_unix_time, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("get_date_string_from_unix_time", "unix_time_val"), &Time::get_date_string_from_unix_time);
|
||||
ClassDB::bind_method(D_METHOD("get_time_string_from_unix_time", "unix_time_val"), &Time::get_time_string_from_unix_time);
|
||||
ClassDB::bind_method(D_METHOD("get_datetime_dict_from_datetime_string", "datetime", "weekday"), &Time::get_datetime_dict_from_datetime_string);
|
||||
ClassDB::bind_method(D_METHOD("get_datetime_string_from_datetime_dict", "datetime", "use_space"), &Time::get_datetime_string_from_datetime_dict);
|
||||
ClassDB::bind_method(D_METHOD("get_unix_time_from_datetime_dict", "datetime"), &Time::get_unix_time_from_datetime_dict);
|
||||
ClassDB::bind_method(D_METHOD("get_unix_time_from_datetime_string", "datetime"), &Time::get_unix_time_from_datetime_string);
|
||||
ClassDB::bind_method(D_METHOD("get_offset_string_from_offset_minutes", "offset_minutes"), &Time::get_offset_string_from_offset_minutes);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_datetime_dict_from_system", "utc"), &Time::get_datetime_dict_from_system, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("get_date_dict_from_system", "utc"), &Time::get_date_dict_from_system, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("get_time_dict_from_system", "utc"), &Time::get_time_dict_from_system, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("get_datetime_string_from_system", "utc", "use_space"), &Time::get_datetime_string_from_system, DEFVAL(false), DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("get_date_string_from_system", "utc"), &Time::get_date_string_from_system, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("get_time_string_from_system", "utc"), &Time::get_time_string_from_system, DEFVAL(false));
|
||||
ClassDB::bind_method(D_METHOD("get_time_zone_from_system"), &Time::get_time_zone_from_system);
|
||||
ClassDB::bind_method(D_METHOD("get_unix_time_from_system"), &Time::get_unix_time_from_system);
|
||||
ClassDB::bind_method(D_METHOD("get_ticks_msec"), &Time::get_ticks_msec);
|
||||
ClassDB::bind_method(D_METHOD("get_ticks_usec"), &Time::get_ticks_usec);
|
||||
|
||||
BIND_ENUM_CONSTANT(MONTH_JANUARY);
|
||||
BIND_ENUM_CONSTANT(MONTH_FEBRUARY);
|
||||
BIND_ENUM_CONSTANT(MONTH_MARCH);
|
||||
BIND_ENUM_CONSTANT(MONTH_APRIL);
|
||||
BIND_ENUM_CONSTANT(MONTH_MAY);
|
||||
BIND_ENUM_CONSTANT(MONTH_JUNE);
|
||||
BIND_ENUM_CONSTANT(MONTH_JULY);
|
||||
BIND_ENUM_CONSTANT(MONTH_AUGUST);
|
||||
BIND_ENUM_CONSTANT(MONTH_SEPTEMBER);
|
||||
BIND_ENUM_CONSTANT(MONTH_OCTOBER);
|
||||
BIND_ENUM_CONSTANT(MONTH_NOVEMBER);
|
||||
BIND_ENUM_CONSTANT(MONTH_DECEMBER);
|
||||
|
||||
BIND_ENUM_CONSTANT(WEEKDAY_SUNDAY);
|
||||
BIND_ENUM_CONSTANT(WEEKDAY_MONDAY);
|
||||
BIND_ENUM_CONSTANT(WEEKDAY_TUESDAY);
|
||||
BIND_ENUM_CONSTANT(WEEKDAY_WEDNESDAY);
|
||||
BIND_ENUM_CONSTANT(WEEKDAY_THURSDAY);
|
||||
BIND_ENUM_CONSTANT(WEEKDAY_FRIDAY);
|
||||
BIND_ENUM_CONSTANT(WEEKDAY_SATURDAY);
|
||||
}
|
||||
|
||||
Time::Time() {
|
||||
ERR_FAIL_COND_MSG(singleton, "Singleton for Time already exists.");
|
||||
singleton = this;
|
||||
}
|
||||
|
||||
Time::~Time() {
|
||||
singleton = nullptr;
|
||||
}
|
84
core/os/time.h
Normal file
84
core/os/time.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/**************************************************************************/
|
||||
/* time.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/object/class_db.h"
|
||||
#include "time_enums.h"
|
||||
|
||||
// This Time class conforms with as many of the ISO 8601 standards as possible.
|
||||
// * As per ISO 8601:2004 4.3.2.1, all dates follow the Proleptic Gregorian
|
||||
// calendar. As such, the day before 1582-10-15 is 1582-10-14, not 1582-10-04.
|
||||
// See: https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar
|
||||
// * As per ISO 8601:2004 3.4.2 and 4.1.2.4, the year before 1 AD (aka 1 BC)
|
||||
// is number "0", with the year before that (2 BC) being "-1", etc.
|
||||
// Conversion methods assume "the same timezone", and do not handle DST.
|
||||
// Leap seconds are not handled, they must be done manually if desired.
|
||||
// Suffixes such as "Z" are not handled, you need to strip them away manually.
|
||||
|
||||
class Time : public Object {
|
||||
GDCLASS(Time, Object);
|
||||
static void _bind_methods();
|
||||
static Time *singleton;
|
||||
|
||||
public:
|
||||
static Time *get_singleton();
|
||||
|
||||
// Methods that convert times.
|
||||
Dictionary get_datetime_dict_from_unix_time(int64_t p_unix_time_val) const;
|
||||
Dictionary get_date_dict_from_unix_time(int64_t p_unix_time_val) const;
|
||||
Dictionary get_time_dict_from_unix_time(int64_t p_unix_time_val) const;
|
||||
String get_datetime_string_from_unix_time(int64_t p_unix_time_val, bool p_use_space = false) const;
|
||||
String get_date_string_from_unix_time(int64_t p_unix_time_val) const;
|
||||
String get_time_string_from_unix_time(int64_t p_unix_time_val) const;
|
||||
Dictionary get_datetime_dict_from_datetime_string(const String &p_datetime, bool p_weekday = true) const;
|
||||
String get_datetime_string_from_datetime_dict(const Dictionary &p_datetime, bool p_use_space = false) const;
|
||||
int64_t get_unix_time_from_datetime_dict(const Dictionary &p_datetime) const;
|
||||
int64_t get_unix_time_from_datetime_string(const String &p_datetime) const;
|
||||
String get_offset_string_from_offset_minutes(int64_t p_offset_minutes) const;
|
||||
|
||||
// Methods that get information from OS.
|
||||
Dictionary get_datetime_dict_from_system(bool p_utc = false) const;
|
||||
Dictionary get_date_dict_from_system(bool p_utc = false) const;
|
||||
Dictionary get_time_dict_from_system(bool p_utc = false) const;
|
||||
String get_datetime_string_from_system(bool p_utc = false, bool p_use_space = false) const;
|
||||
String get_date_string_from_system(bool p_utc = false) const;
|
||||
String get_time_string_from_system(bool p_utc = false) const;
|
||||
Dictionary get_time_zone_from_system() const;
|
||||
double get_unix_time_from_system() const;
|
||||
uint64_t get_ticks_msec() const;
|
||||
uint64_t get_ticks_usec() const;
|
||||
|
||||
Time();
|
||||
virtual ~Time();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(Month);
|
||||
VARIANT_ENUM_CAST(Weekday);
|
60
core/os/time_enums.h
Normal file
60
core/os/time_enums.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/**************************************************************************/
|
||||
/* time_enums.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum Month {
|
||||
/// Start at 1 to follow Windows SYSTEMTIME structure
|
||||
/// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724950(v=vs.85).aspx
|
||||
MONTH_JANUARY = 1,
|
||||
MONTH_FEBRUARY,
|
||||
MONTH_MARCH,
|
||||
MONTH_APRIL,
|
||||
MONTH_MAY,
|
||||
MONTH_JUNE,
|
||||
MONTH_JULY,
|
||||
MONTH_AUGUST,
|
||||
MONTH_SEPTEMBER,
|
||||
MONTH_OCTOBER,
|
||||
MONTH_NOVEMBER,
|
||||
MONTH_DECEMBER,
|
||||
};
|
||||
|
||||
enum Weekday : uint8_t {
|
||||
WEEKDAY_SUNDAY,
|
||||
WEEKDAY_MONDAY,
|
||||
WEEKDAY_TUESDAY,
|
||||
WEEKDAY_WEDNESDAY,
|
||||
WEEKDAY_THURSDAY,
|
||||
WEEKDAY_FRIDAY,
|
||||
WEEKDAY_SATURDAY,
|
||||
};
|
Reference in New Issue
Block a user