summaryrefslogtreecommitdiff
path: root/core/os
diff options
context:
space:
mode:
Diffstat (limited to 'core/os')
-rw-r--r--core/os/copymem.h50
-rw-r--r--core/os/dir_access.cpp416
-rw-r--r--core/os/dir_access.h142
-rw-r--r--core/os/file_access.cpp669
-rw-r--r--core/os/file_access.h199
-rw-r--r--core/os/keyboard.cpp714
-rw-r--r--core/os/keyboard.h591
-rw-r--r--core/os/main_loop.cpp57
-rw-r--r--core/os/main_loop.h27
-rw-r--r--core/os/memory.cpp35
-rw-r--r--core/os/memory.h31
-rw-r--r--core/os/midi_driver.cpp32
-rw-r--r--core/os/midi_driver.h6
-rw-r--r--core/os/mutex.cpp4
-rw-r--r--core/os/mutex.h6
-rw-r--r--core/os/os.cpp139
-rw-r--r--core/os/os.h109
-rw-r--r--core/os/pool_allocator.cpp595
-rw-r--r--core/os/pool_allocator.h149
-rw-r--r--core/os/rw_lock.h90
-rw-r--r--core/os/semaphore.h6
-rw-r--r--core/os/spin_lock.h (renamed from core/os/rw_lock.cpp)32
-rw-r--r--core/os/thread.cpp99
-rw-r--r--core/os/thread.h86
-rw-r--r--core/os/thread_dummy.cpp49
-rw-r--r--core/os/thread_dummy.h62
-rw-r--r--core/os/thread_safe.h4
-rw-r--r--core/os/threaded_array_processor.h31
-rw-r--r--core/os/time.cpp433
-rw-r--r--core/os/time.h109
30 files changed, 2461 insertions, 2511 deletions
diff --git a/core/os/copymem.h b/core/os/copymem.h
deleted file mode 100644
index 04ea3caeff..0000000000
--- a/core/os/copymem.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*************************************************************************/
-/* copymem.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 COPYMEM_H
-#define COPYMEM_H
-
-#include "core/typedefs.h"
-
-#ifdef PLATFORM_COPYMEM
-
-#include "platform_copymem.h" // included from platform/<current_platform>/platform_copymem.h"
-
-#else
-
-#include <string.h>
-
-#define copymem(to, from, count) memcpy(to, from, count)
-#define zeromem(to, count) memset(to, 0, count)
-#define movemem(to, from, count) memmove(to, from, count)
-
-#endif
-
-#endif // COPYMEM_H
diff --git a/core/os/dir_access.cpp b/core/os/dir_access.cpp
deleted file mode 100644
index 5e1cb8ea29..0000000000
--- a/core/os/dir_access.cpp
+++ /dev/null
@@ -1,416 +0,0 @@
-/*************************************************************************/
-/* dir_access.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 "dir_access.h"
-
-#include "core/os/file_access.h"
-#include "core/os/memory.h"
-#include "core/os/os.h"
-#include "core/project_settings.h"
-
-String DirAccess::_get_root_path() const {
- switch (_access_type) {
- case ACCESS_RESOURCES:
- return ProjectSettings::get_singleton()->get_resource_path();
- case ACCESS_USERDATA:
- return OS::get_singleton()->get_user_data_dir();
- default:
- return "";
- }
-}
-
-String DirAccess::_get_root_string() const {
- switch (_access_type) {
- case ACCESS_RESOURCES:
- return "res://";
- case ACCESS_USERDATA:
- return "user://";
- default:
- return "";
- }
-}
-
-int DirAccess::get_current_drive() {
- String path = get_current_dir().to_lower();
- for (int i = 0; i < get_drive_count(); i++) {
- String d = get_drive(i).to_lower();
- if (path.begins_with(d)) {
- return i;
- }
- }
-
- return 0;
-}
-
-bool DirAccess::drives_are_shortcuts() {
- return false;
-}
-
-static Error _erase_recursive(DirAccess *da) {
- List<String> dirs;
- List<String> files;
-
- da->list_dir_begin();
- String n = da->get_next();
- while (n != String()) {
- if (n != "." && n != "..") {
- if (da->current_is_dir()) {
- dirs.push_back(n);
- } else {
- files.push_back(n);
- }
- }
-
- n = da->get_next();
- }
-
- da->list_dir_end();
-
- for (List<String>::Element *E = dirs.front(); E; E = E->next()) {
- Error err = da->change_dir(E->get());
- if (err == OK) {
- err = _erase_recursive(da);
- if (err) {
- da->change_dir("..");
- return err;
- }
- err = da->change_dir("..");
- if (err) {
- return err;
- }
- err = da->remove(da->get_current_dir().plus_file(E->get()));
- if (err) {
- return err;
- }
- } else {
- return err;
- }
- }
-
- for (List<String>::Element *E = files.front(); E; E = E->next()) {
- Error err = da->remove(da->get_current_dir().plus_file(E->get()));
- if (err) {
- return err;
- }
- }
-
- return OK;
-}
-
-Error DirAccess::erase_contents_recursive() {
- return _erase_recursive(this);
-}
-
-Error DirAccess::make_dir_recursive(String p_dir) {
- if (p_dir.length() < 1) {
- return OK;
- }
-
- String full_dir;
-
- if (p_dir.is_rel_path()) {
- //append current
- full_dir = get_current_dir().plus_file(p_dir);
-
- } else {
- full_dir = p_dir;
- }
-
- full_dir = full_dir.replace("\\", "/");
-
- //int slices = full_dir.get_slice_count("/");
-
- String base;
-
- if (full_dir.begins_with("res://")) {
- base = "res://";
- } else if (full_dir.begins_with("user://")) {
- base = "user://";
- } else if (full_dir.begins_with("/")) {
- base = "/";
- } else if (full_dir.find(":/") != -1) {
- base = full_dir.substr(0, full_dir.find(":/") + 2);
- } else {
- ERR_FAIL_V(ERR_INVALID_PARAMETER);
- }
-
- full_dir = full_dir.replace_first(base, "").simplify_path();
-
- Vector<String> subdirs = full_dir.split("/");
-
- String curpath = base;
- for (int i = 0; i < subdirs.size(); i++) {
- curpath = curpath.plus_file(subdirs[i]);
- Error err = make_dir(curpath);
- if (err != OK && err != ERR_ALREADY_EXISTS) {
- ERR_FAIL_V(err);
- }
- }
-
- return OK;
-}
-
-String DirAccess::fix_path(String p_path) const {
- switch (_access_type) {
- case ACCESS_RESOURCES: {
- if (ProjectSettings::get_singleton()) {
- if (p_path.begins_with("res://")) {
- String resource_path = ProjectSettings::get_singleton()->get_resource_path();
- if (resource_path != "") {
- return p_path.replace_first("res:/", resource_path);
- }
- return p_path.replace_first("res://", "");
- }
- }
-
- } break;
- case ACCESS_USERDATA: {
- if (p_path.begins_with("user://")) {
- String data_dir = OS::get_singleton()->get_user_data_dir();
- if (data_dir != "") {
- return p_path.replace_first("user:/", data_dir);
- }
- return p_path.replace_first("user://", "");
- }
-
- } break;
- case ACCESS_FILESYSTEM: {
- return p_path;
- } break;
- case ACCESS_MAX:
- break; // Can't happen, but silences warning
- }
-
- return p_path;
-}
-
-DirAccess::CreateFunc DirAccess::create_func[ACCESS_MAX] = { nullptr, nullptr, nullptr };
-
-DirAccess *DirAccess::create_for_path(const String &p_path) {
- DirAccess *da = nullptr;
- if (p_path.begins_with("res://")) {
- da = create(ACCESS_RESOURCES);
- } else if (p_path.begins_with("user://")) {
- da = create(ACCESS_USERDATA);
- } else {
- da = create(ACCESS_FILESYSTEM);
- }
-
- return da;
-}
-
-DirAccess *DirAccess::open(const String &p_path, Error *r_error) {
- DirAccess *da = create_for_path(p_path);
-
- ERR_FAIL_COND_V_MSG(!da, nullptr, "Cannot create DirAccess for path '" + p_path + "'.");
- Error err = da->change_dir(p_path);
- if (r_error) {
- *r_error = err;
- }
- if (err != OK) {
- memdelete(da);
- return nullptr;
- }
-
- return da;
-}
-
-DirAccess *DirAccess::create(AccessType p_access) {
- DirAccess *da = create_func[p_access] ? create_func[p_access]() : nullptr;
- if (da) {
- da->_access_type = p_access;
- }
-
- return da;
-}
-
-String DirAccess::get_full_path(const String &p_path, AccessType p_access) {
- DirAccess *d = DirAccess::create(p_access);
- if (!d) {
- return p_path;
- }
-
- d->change_dir(p_path);
- String full = d->get_current_dir();
- memdelete(d);
- return full;
-}
-
-Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) {
- //printf("copy %s -> %s\n",p_from.ascii().get_data(),p_to.ascii().get_data());
- Error err;
- FileAccess *fsrc = FileAccess::open(p_from, FileAccess::READ, &err);
-
- if (err) {
- ERR_PRINT("Failed to open " + p_from);
- return err;
- }
-
- FileAccess *fdst = FileAccess::open(p_to, FileAccess::WRITE, &err);
- if (err) {
- fsrc->close();
- memdelete(fsrc);
- ERR_PRINT("Failed to open " + p_to);
- return err;
- }
-
- fsrc->seek_end(0);
- int size = fsrc->get_position();
- fsrc->seek(0);
- err = OK;
- while (size--) {
- if (fsrc->get_error() != OK) {
- err = fsrc->get_error();
- break;
- }
- if (fdst->get_error() != OK) {
- err = fdst->get_error();
- break;
- }
-
- fdst->store_8(fsrc->get_8());
- }
-
- if (err == OK && p_chmod_flags != -1) {
- fdst->close();
- err = FileAccess::set_unix_permissions(p_to, p_chmod_flags);
- // If running on a platform with no chmod support (i.e., Windows), don't fail
- if (err == ERR_UNAVAILABLE) {
- err = OK;
- }
- }
-
- memdelete(fsrc);
- memdelete(fdst);
-
- return err;
-}
-
-// Changes dir for the current scope, returning back to the original dir
-// when scope exits
-class DirChanger {
- DirAccess *da;
- String original_dir;
-
-public:
- DirChanger(DirAccess *p_da, String p_dir) :
- da(p_da),
- original_dir(p_da->get_current_dir()) {
- p_da->change_dir(p_dir);
- }
-
- ~DirChanger() {
- da->change_dir(original_dir);
- }
-};
-
-Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags) {
- List<String> dirs;
-
- String curdir = get_current_dir();
- list_dir_begin();
- String n = get_next();
- while (n != String()) {
- if (n != "." && n != "..") {
- if (current_is_dir()) {
- dirs.push_back(n);
- } else {
- const String &rel_path = n;
- if (!n.is_rel_path()) {
- list_dir_end();
- return ERR_BUG;
- }
- Error err = copy(get_current_dir().plus_file(n), p_to + rel_path, p_chmod_flags);
- if (err) {
- list_dir_end();
- return err;
- }
- }
- }
-
- n = get_next();
- }
-
- list_dir_end();
-
- for (List<String>::Element *E = dirs.front(); E; E = E->next()) {
- String rel_path = E->get();
- String target_dir = p_to + rel_path;
- if (!p_target_da->dir_exists(target_dir)) {
- Error err = p_target_da->make_dir(target_dir);
- ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + target_dir + "'.");
- }
-
- Error err = change_dir(E->get());
- ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + E->get() + "'.");
-
- err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags);
- if (err) {
- change_dir("..");
- ERR_FAIL_V_MSG(err, "Failed to copy recursively.");
- }
- err = change_dir("..");
- ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to go back.");
- }
-
- return OK;
-}
-
-Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags) {
- ERR_FAIL_COND_V_MSG(!dir_exists(p_from), ERR_FILE_NOT_FOUND, "Source directory doesn't exist.");
-
- DirAccess *target_da = DirAccess::create_for_path(p_to);
- ERR_FAIL_COND_V_MSG(!target_da, ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_to + "'.");
-
- if (!target_da->dir_exists(p_to)) {
- Error err = target_da->make_dir_recursive(p_to);
- if (err) {
- memdelete(target_da);
- }
- ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + p_to + "'.");
- }
-
- if (!p_to.ends_with("/")) {
- p_to = p_to + "/";
- }
-
- DirChanger dir_changer(this, p_from);
- Error err = _copy_dir(target_da, p_to, p_chmod_flags);
- memdelete(target_da);
-
- return err;
-}
-
-bool DirAccess::exists(String p_dir) {
- DirAccess *da = DirAccess::create_for_path(p_dir);
- bool valid = da->change_dir(p_dir) == OK;
- memdelete(da);
- return valid;
-}
diff --git a/core/os/dir_access.h b/core/os/dir_access.h
deleted file mode 100644
index 6bce9a4c12..0000000000
--- a/core/os/dir_access.h
+++ /dev/null
@@ -1,142 +0,0 @@
-/*************************************************************************/
-/* dir_access.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 DIR_ACCESS_H
-#define DIR_ACCESS_H
-
-#include "core/typedefs.h"
-#include "core/ustring.h"
-
-//@ TODO, excellent candidate for THREAD_SAFE MACRO, should go through all these and add THREAD_SAFE where it applies
-class DirAccess {
-public:
- enum AccessType {
- ACCESS_RESOURCES,
- ACCESS_USERDATA,
- ACCESS_FILESYSTEM,
- ACCESS_MAX
- };
-
- typedef DirAccess *(*CreateFunc)();
-
-private:
- AccessType _access_type = ACCESS_FILESYSTEM;
- static CreateFunc create_func[ACCESS_MAX]; ///< set this to instance a filesystem object
-
- Error _copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flags);
-
-protected:
- String _get_root_path() const;
- String _get_root_string() const;
-
- String fix_path(String p_path) const;
- bool next_is_dir;
-
- template <class T>
- static DirAccess *_create_builtin() {
- return memnew(T);
- }
-
-public:
- virtual Error list_dir_begin() = 0; ///< This starts dir listing
- virtual String get_next() = 0;
- virtual bool current_is_dir() const = 0;
- virtual bool current_is_hidden() const = 0;
-
- virtual void list_dir_end() = 0; ///<
-
- virtual int get_drive_count() = 0;
- virtual String get_drive(int p_drive) = 0;
- virtual int get_current_drive();
- virtual bool drives_are_shortcuts();
-
- virtual Error change_dir(String p_dir) = 0; ///< can be relative or absolute, return false on success
- virtual String get_current_dir(bool p_include_drive = true) = 0; ///< return current dir location
- virtual Error make_dir(String p_dir) = 0;
- virtual Error make_dir_recursive(String p_dir);
- virtual Error erase_contents_recursive(); //super dangerous, use with care!
-
- virtual bool file_exists(String p_file) = 0;
- virtual bool dir_exists(String p_dir) = 0;
- static bool exists(String p_dir);
- virtual size_t get_space_left() = 0;
-
- Error copy_dir(String p_from, String p_to, int p_chmod_flags = -1);
- virtual Error copy(String p_from, String p_to, int p_chmod_flags = -1);
- virtual Error rename(String p_from, String p_to) = 0;
- virtual Error remove(String p_name) = 0;
-
- // Meant for editor code when we want to quickly remove a file without custom
- // handling (e.g. removing a cache file).
- static void remove_file_or_error(String p_path) {
- DirAccess *da = create(ACCESS_FILESYSTEM);
- if (da->file_exists(p_path)) {
- if (da->remove(p_path) != OK) {
- ERR_FAIL_MSG("Cannot remove file or directory: " + p_path);
- }
- }
- memdelete(da);
- }
-
- virtual String get_filesystem_type() const = 0;
- static String get_full_path(const String &p_path, AccessType p_access);
- static DirAccess *create_for_path(const String &p_path);
-
- static DirAccess *create(AccessType p_access);
-
- template <class T>
- static void make_default(AccessType p_access) {
- create_func[p_access] = _create_builtin<T>;
- }
-
- static DirAccess *open(const String &p_path, Error *r_error = nullptr);
-
- DirAccess() {}
- virtual ~DirAccess() {}
-};
-
-struct DirAccessRef {
- _FORCE_INLINE_ DirAccess *operator->() {
- return f;
- }
-
- operator bool() const { return f != nullptr; }
-
- DirAccess *f;
-
- DirAccessRef(DirAccess *fa) { f = fa; }
- ~DirAccessRef() {
- if (f) {
- memdelete(f);
- }
- }
-};
-
-#endif // DIR_ACCESS_H
diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp
deleted file mode 100644
index 9dbb2952f7..0000000000
--- a/core/os/file_access.cpp
+++ /dev/null
@@ -1,669 +0,0 @@
-/*************************************************************************/
-/* file_access.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 "file_access.h"
-
-#include "core/crypto/crypto_core.h"
-#include "core/io/file_access_pack.h"
-#include "core/io/marshalls.h"
-#include "core/os/os.h"
-#include "core/project_settings.h"
-
-FileAccess::CreateFunc FileAccess::create_func[ACCESS_MAX] = { nullptr, nullptr };
-
-FileAccess::FileCloseFailNotify FileAccess::close_fail_notify = nullptr;
-
-bool FileAccess::backup_save = false;
-
-FileAccess *FileAccess::create(AccessType p_access) {
- ERR_FAIL_INDEX_V(p_access, ACCESS_MAX, nullptr);
-
- FileAccess *ret = create_func[p_access]();
- ret->_set_access_type(p_access);
- return ret;
-}
-
-bool FileAccess::exists(const String &p_name) {
- if (PackedData::get_singleton() && PackedData::get_singleton()->has_path(p_name)) {
- return true;
- }
-
- FileAccess *f = open(p_name, READ);
- if (!f) {
- return false;
- }
- memdelete(f);
- return true;
-}
-
-void FileAccess::_set_access_type(AccessType p_access) {
- _access_type = p_access;
-}
-
-FileAccess *FileAccess::create_for_path(const String &p_path) {
- FileAccess *ret = nullptr;
- if (p_path.begins_with("res://")) {
- ret = create(ACCESS_RESOURCES);
- } else if (p_path.begins_with("user://")) {
- ret = create(ACCESS_USERDATA);
-
- } else {
- ret = create(ACCESS_FILESYSTEM);
- }
-
- return ret;
-}
-
-Error FileAccess::reopen(const String &p_path, int p_mode_flags) {
- return _open(p_path, p_mode_flags);
-}
-
-FileAccess *FileAccess::open(const String &p_path, int p_mode_flags, Error *r_error) {
- //try packed data first
-
- FileAccess *ret = nullptr;
- if (!(p_mode_flags & WRITE) && PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled()) {
- ret = PackedData::get_singleton()->try_open_path(p_path);
- if (ret) {
- if (r_error) {
- *r_error = OK;
- }
- return ret;
- }
- }
-
- ret = create_for_path(p_path);
- Error err = ret->_open(p_path, p_mode_flags);
-
- if (r_error) {
- *r_error = err;
- }
- if (err != OK) {
- memdelete(ret);
- ret = nullptr;
- }
-
- return ret;
-}
-
-FileAccess::CreateFunc FileAccess::get_create_func(AccessType p_access) {
- return create_func[p_access];
-}
-
-String FileAccess::fix_path(const String &p_path) const {
- //helper used by file accesses that use a single filesystem
-
- String r_path = p_path.replace("\\", "/");
-
- switch (_access_type) {
- case ACCESS_RESOURCES: {
- if (ProjectSettings::get_singleton()) {
- if (r_path.begins_with("res://")) {
- String resource_path = ProjectSettings::get_singleton()->get_resource_path();
- if (resource_path != "") {
- return r_path.replace("res:/", resource_path);
- }
- return r_path.replace("res://", "");
- }
- }
-
- } break;
- case ACCESS_USERDATA: {
- if (r_path.begins_with("user://")) {
- String data_dir = OS::get_singleton()->get_user_data_dir();
- if (data_dir != "") {
- return r_path.replace("user:/", data_dir);
- }
- return r_path.replace("user://", "");
- }
-
- } break;
- case ACCESS_FILESYSTEM: {
- return r_path;
- } break;
- case ACCESS_MAX:
- break; // Can't happen, but silences warning
- }
-
- return r_path;
-}
-
-/* these are all implemented for ease of porting, then can later be optimized */
-
-uint16_t FileAccess::get_16() const {
- uint16_t res;
- uint8_t a, b;
-
- a = get_8();
- b = get_8();
-
- if (endian_swap) {
- SWAP(a, b);
- }
-
- res = b;
- res <<= 8;
- res |= a;
-
- return res;
-}
-
-uint32_t FileAccess::get_32() const {
- uint32_t res;
- uint16_t a, b;
-
- a = get_16();
- b = get_16();
-
- if (endian_swap) {
- SWAP(a, b);
- }
-
- res = b;
- res <<= 16;
- res |= a;
-
- return res;
-}
-
-uint64_t FileAccess::get_64() const {
- uint64_t res;
- uint32_t a, b;
-
- a = get_32();
- b = get_32();
-
- if (endian_swap) {
- SWAP(a, b);
- }
-
- res = b;
- res <<= 32;
- res |= a;
-
- return res;
-}
-
-float FileAccess::get_float() const {
- MarshallFloat m;
- m.i = get_32();
- return m.f;
-}
-
-real_t FileAccess::get_real() const {
- if (real_is_double) {
- return get_double();
- } else {
- return get_float();
- }
-}
-
-double FileAccess::get_double() const {
- MarshallDouble m;
- m.l = get_64();
- return m.d;
-}
-
-String FileAccess::get_token() const {
- CharString token;
-
- char32_t c = get_8();
-
- while (!eof_reached()) {
- if (c <= ' ') {
- if (token.length()) {
- break;
- }
- } else {
- token += c;
- }
- c = get_8();
- }
-
- return String::utf8(token.get_data());
-}
-
-class CharBuffer {
- Vector<char> vector;
- char stack_buffer[256];
-
- char *buffer;
- int capacity;
- int written = 0;
-
- bool grow() {
- if (vector.resize(next_power_of_2(1 + written)) != OK) {
- return false;
- }
-
- if (buffer == stack_buffer) { // first chunk?
-
- for (int i = 0; i < written; i++) {
- vector.write[i] = stack_buffer[i];
- }
- }
-
- buffer = vector.ptrw();
- capacity = vector.size();
- ERR_FAIL_COND_V(written >= capacity, false);
-
- return true;
- }
-
-public:
- _FORCE_INLINE_ CharBuffer() :
- buffer(stack_buffer),
- capacity(sizeof(stack_buffer) / sizeof(char)) {
- }
-
- _FORCE_INLINE_ void push_back(char c) {
- if (written >= capacity) {
- ERR_FAIL_COND(!grow());
- }
-
- buffer[written++] = c;
- }
-
- _FORCE_INLINE_ const char *get_data() const {
- return buffer;
- }
-};
-
-String FileAccess::get_line() const {
- CharBuffer line;
-
- char32_t c = get_8();
-
- while (!eof_reached()) {
- if (c == '\n' || c == '\0') {
- line.push_back(0);
- return String::utf8(line.get_data());
- } else if (c != '\r') {
- line.push_back(c);
- }
-
- c = get_8();
- }
- line.push_back(0);
- return String::utf8(line.get_data());
-}
-
-Vector<String> FileAccess::get_csv_line(const String &p_delim) const {
- ERR_FAIL_COND_V(p_delim.length() != 1, Vector<String>());
-
- String l;
- int qc = 0;
- do {
- if (eof_reached()) {
- break;
- }
-
- l += get_line() + "\n";
- qc = 0;
- for (int i = 0; i < l.length(); i++) {
- if (l[i] == '"') {
- qc++;
- }
- }
-
- } while (qc % 2);
-
- l = l.substr(0, l.length() - 1);
-
- Vector<String> strings;
-
- bool in_quote = false;
- String current;
- for (int i = 0; i < l.length(); i++) {
- char32_t c = l[i];
- char32_t s[2] = { 0, 0 };
-
- if (!in_quote && c == p_delim[0]) {
- strings.push_back(current);
- current = String();
- } else if (c == '"') {
- if (l[i + 1] == '"') {
- s[0] = '"';
- current += s;
- i++;
- } else {
- in_quote = !in_quote;
- }
- } else {
- s[0] = c;
- current += s;
- }
- }
-
- strings.push_back(current);
-
- return strings;
-}
-
-int FileAccess::get_buffer(uint8_t *p_dst, int p_length) const {
- int i = 0;
- for (i = 0; i < p_length && !eof_reached(); i++) {
- p_dst[i] = get_8();
- }
-
- return i;
-}
-
-String FileAccess::get_as_utf8_string() const {
- Vector<uint8_t> sourcef;
- int len = get_len();
- sourcef.resize(len + 1);
-
- uint8_t *w = sourcef.ptrw();
- int r = get_buffer(w, len);
- ERR_FAIL_COND_V(r != len, String());
- w[len] = 0;
-
- String s;
- if (s.parse_utf8((const char *)w)) {
- return String();
- }
- return s;
-}
-
-void FileAccess::store_16(uint16_t p_dest) {
- uint8_t a, b;
-
- a = p_dest & 0xFF;
- b = p_dest >> 8;
-
- if (endian_swap) {
- SWAP(a, b);
- }
-
- store_8(a);
- store_8(b);
-}
-
-void FileAccess::store_32(uint32_t p_dest) {
- uint16_t a, b;
-
- a = p_dest & 0xFFFF;
- b = p_dest >> 16;
-
- if (endian_swap) {
- SWAP(a, b);
- }
-
- store_16(a);
- store_16(b);
-}
-
-void FileAccess::store_64(uint64_t p_dest) {
- uint32_t a, b;
-
- a = p_dest & 0xFFFFFFFF;
- b = p_dest >> 32;
-
- if (endian_swap) {
- SWAP(a, b);
- }
-
- store_32(a);
- store_32(b);
-}
-
-void FileAccess::store_real(real_t p_real) {
- if (sizeof(real_t) == 4) {
- store_float(p_real);
- } else {
- store_double(p_real);
- }
-}
-
-void FileAccess::store_float(float p_dest) {
- MarshallFloat m;
- m.f = p_dest;
- store_32(m.i);
-}
-
-void FileAccess::store_double(double p_dest) {
- MarshallDouble m;
- m.d = p_dest;
- store_64(m.l);
-}
-
-uint64_t FileAccess::get_modified_time(const String &p_file) {
- if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && PackedData::get_singleton()->has_path(p_file)) {
- return 0;
- }
-
- FileAccess *fa = create_for_path(p_file);
- ERR_FAIL_COND_V_MSG(!fa, 0, "Cannot create FileAccess for path '" + p_file + "'.");
-
- uint64_t mt = fa->_get_modified_time(p_file);
- memdelete(fa);
- return mt;
-}
-
-uint32_t FileAccess::get_unix_permissions(const String &p_file) {
- if (PackedData::get_singleton() && !PackedData::get_singleton()->is_disabled() && PackedData::get_singleton()->has_path(p_file)) {
- return 0;
- }
-
- FileAccess *fa = create_for_path(p_file);
- ERR_FAIL_COND_V_MSG(!fa, 0, "Cannot create FileAccess for path '" + p_file + "'.");
-
- uint32_t mt = fa->_get_unix_permissions(p_file);
- memdelete(fa);
- return mt;
-}
-
-Error FileAccess::set_unix_permissions(const String &p_file, uint32_t p_permissions) {
- FileAccess *fa = create_for_path(p_file);
- ERR_FAIL_COND_V_MSG(!fa, ERR_CANT_CREATE, "Cannot create FileAccess for path '" + p_file + "'.");
-
- Error err = fa->_set_unix_permissions(p_file, p_permissions);
- memdelete(fa);
- return err;
-}
-
-void FileAccess::store_string(const String &p_string) {
- if (p_string.length() == 0) {
- return;
- }
-
- CharString cs = p_string.utf8();
- store_buffer((uint8_t *)&cs[0], cs.length());
-}
-
-void FileAccess::store_pascal_string(const String &p_string) {
- CharString cs = p_string.utf8();
- store_32(cs.length());
- store_buffer((uint8_t *)&cs[0], cs.length());
-}
-
-String FileAccess::get_pascal_string() {
- uint32_t sl = get_32();
- CharString cs;
- cs.resize(sl + 1);
- get_buffer((uint8_t *)cs.ptr(), sl);
- cs[sl] = 0;
-
- String ret;
- ret.parse_utf8(cs.ptr());
-
- return ret;
-}
-
-void FileAccess::store_line(const String &p_line) {
- store_string(p_line);
- store_8('\n');
-}
-
-void FileAccess::store_csv_line(const Vector<String> &p_values, const String &p_delim) {
- ERR_FAIL_COND(p_delim.length() != 1);
-
- String line = "";
- int size = p_values.size();
- for (int i = 0; i < size; ++i) {
- String value = p_values[i];
-
- if (value.find("\"") != -1 || value.find(p_delim) != -1 || value.find("\n") != -1) {
- value = "\"" + value.replace("\"", "\"\"") + "\"";
- }
- if (i < size - 1) {
- value += p_delim;
- }
-
- line += value;
- }
-
- store_line(line);
-}
-
-void FileAccess::store_buffer(const uint8_t *p_src, int p_length) {
- for (int i = 0; i < p_length; i++) {
- store_8(p_src[i]);
- }
-}
-
-Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path, Error *r_error) {
- FileAccess *f = FileAccess::open(p_path, READ, r_error);
- if (!f) {
- if (r_error) { // if error requested, do not throw error
- return Vector<uint8_t>();
- }
- ERR_FAIL_V_MSG(Vector<uint8_t>(), "Can't open file from path '" + String(p_path) + "'.");
- }
- Vector<uint8_t> data;
- data.resize(f->get_len());
- f->get_buffer(data.ptrw(), data.size());
- memdelete(f);
- return data;
-}
-
-String FileAccess::get_file_as_string(const String &p_path, Error *r_error) {
- Error err;
- Vector<uint8_t> array = get_file_as_array(p_path, &err);
- if (r_error) {
- *r_error = err;
- }
- if (err != OK) {
- if (r_error) {
- return String();
- }
- ERR_FAIL_V_MSG(String(), "Can't get file as string from path '" + String(p_path) + "'.");
- }
-
- String ret;
- ret.parse_utf8((const char *)array.ptr(), array.size());
- return ret;
-}
-
-String FileAccess::get_md5(const String &p_file) {
- FileAccess *f = FileAccess::open(p_file, READ);
- if (!f) {
- return String();
- }
-
- CryptoCore::MD5Context ctx;
- ctx.start();
-
- unsigned char step[32768];
-
- while (true) {
- int br = f->get_buffer(step, 32768);
- if (br > 0) {
- ctx.update(step, br);
- }
- if (br < 4096) {
- break;
- }
- }
-
- unsigned char hash[16];
- ctx.finish(hash);
-
- memdelete(f);
-
- return String::md5(hash);
-}
-
-String FileAccess::get_multiple_md5(const Vector<String> &p_file) {
- CryptoCore::MD5Context ctx;
- ctx.start();
-
- for (int i = 0; i < p_file.size(); i++) {
- FileAccess *f = FileAccess::open(p_file[i], READ);
- ERR_CONTINUE(!f);
-
- unsigned char step[32768];
-
- while (true) {
- int br = f->get_buffer(step, 32768);
- if (br > 0) {
- ctx.update(step, br);
- }
- if (br < 4096) {
- break;
- }
- }
- memdelete(f);
- }
-
- unsigned char hash[16];
- ctx.finish(hash);
-
- return String::md5(hash);
-}
-
-String FileAccess::get_sha256(const String &p_file) {
- FileAccess *f = FileAccess::open(p_file, READ);
- if (!f) {
- return String();
- }
-
- CryptoCore::SHA256Context ctx;
- ctx.start();
-
- unsigned char step[32768];
-
- while (true) {
- int br = f->get_buffer(step, 32768);
- if (br > 0) {
- ctx.update(step, br);
- }
- if (br < 4096) {
- break;
- }
- }
-
- unsigned char hash[32];
- ctx.finish(hash);
-
- memdelete(f);
- return String::hex_encode_buffer(hash, 32);
-}
diff --git a/core/os/file_access.h b/core/os/file_access.h
deleted file mode 100644
index 48b9ee4269..0000000000
--- a/core/os/file_access.h
+++ /dev/null
@@ -1,199 +0,0 @@
-/*************************************************************************/
-/* file_access.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 FILE_ACCESS_H
-#define FILE_ACCESS_H
-
-#include "core/math/math_defs.h"
-#include "core/os/memory.h"
-#include "core/typedefs.h"
-#include "core/ustring.h"
-
-/**
- * Multi-Platform abstraction for accessing to files.
- */
-
-class FileAccess {
-public:
- enum AccessType {
- ACCESS_RESOURCES,
- ACCESS_USERDATA,
- ACCESS_FILESYSTEM,
- ACCESS_MAX
- };
-
- typedef void (*FileCloseFailNotify)(const String &);
-
- typedef FileAccess *(*CreateFunc)();
- bool endian_swap = false;
- bool real_is_double = false;
-
- virtual uint32_t _get_unix_permissions(const String &p_file) = 0;
- virtual Error _set_unix_permissions(const String &p_file, uint32_t p_permissions) = 0;
-
-protected:
- String fix_path(const String &p_path) const;
- virtual Error _open(const String &p_path, int p_mode_flags) = 0; ///< open a file
- virtual uint64_t _get_modified_time(const String &p_file) = 0;
-
- static FileCloseFailNotify close_fail_notify;
-
-private:
- static bool backup_save;
-
- AccessType _access_type = ACCESS_FILESYSTEM;
- static CreateFunc create_func[ACCESS_MAX]; /** default file access creation function for a platform */
- template <class T>
- static FileAccess *_create_builtin() {
- return memnew(T);
- }
-
-public:
- static void set_file_close_fail_notify_callback(FileCloseFailNotify p_cbk) { close_fail_notify = p_cbk; }
-
- virtual void _set_access_type(AccessType p_access);
-
- enum ModeFlags {
-
- READ = 1,
- WRITE = 2,
- READ_WRITE = 3,
- WRITE_READ = 7,
- };
-
- virtual void close() = 0; ///< close a file
- virtual bool is_open() const = 0; ///< true when file is open
-
- virtual String get_path() const { return ""; } /// returns the path for the current open file
- virtual String get_path_absolute() const { return ""; } /// returns the absolute path for the current open file
-
- virtual void seek(size_t p_position) = 0; ///< seek to a given position
- virtual void seek_end(int64_t p_position = 0) = 0; ///< seek from the end of file
- virtual size_t get_position() const = 0; ///< get position in the file
- virtual size_t get_len() const = 0; ///< get size of the file
-
- virtual bool eof_reached() const = 0; ///< reading passed EOF
-
- virtual uint8_t get_8() const = 0; ///< get a byte
- virtual uint16_t get_16() const; ///< get 16 bits uint
- virtual uint32_t get_32() const; ///< get 32 bits uint
- virtual uint64_t get_64() const; ///< get 64 bits uint
-
- virtual float get_float() const;
- virtual double get_double() const;
- virtual real_t get_real() const;
-
- virtual int get_buffer(uint8_t *p_dst, int p_length) const; ///< get an array of bytes
- virtual String get_line() const;
- virtual String get_token() const;
- virtual Vector<String> get_csv_line(const String &p_delim = ",") const;
- virtual String get_as_utf8_string() const;
-
- /**< use this for files WRITTEN in _big_ endian machines (ie, amiga/mac)
- * It's not about the current CPU type but file formats.
- * this flags get reset to false (little endian) on each open
- */
-
- virtual void set_endian_swap(bool p_swap) { endian_swap = p_swap; }
- inline bool get_endian_swap() const { return endian_swap; }
-
- virtual Error get_error() const = 0; ///< get last error
-
- virtual void flush() = 0;
- virtual void store_8(uint8_t p_dest) = 0; ///< store a byte
- virtual void store_16(uint16_t p_dest); ///< store 16 bits uint
- virtual void store_32(uint32_t p_dest); ///< store 32 bits uint
- virtual void store_64(uint64_t p_dest); ///< store 64 bits uint
-
- virtual void store_float(float p_dest);
- virtual void store_double(double p_dest);
- virtual void store_real(real_t p_real);
-
- virtual void store_string(const String &p_string);
- virtual void store_line(const String &p_line);
- virtual void store_csv_line(const Vector<String> &p_values, const String &p_delim = ",");
-
- virtual void store_pascal_string(const String &p_string);
- virtual String get_pascal_string();
-
- virtual void store_buffer(const uint8_t *p_src, int p_length); ///< store an array of bytes
-
- virtual bool file_exists(const String &p_name) = 0; ///< return true if a file exists
-
- virtual Error reopen(const String &p_path, int p_mode_flags); ///< does not change the AccessType
-
- static FileAccess *create(AccessType p_access); /// Create a file access (for the current platform) this is the only portable way of accessing files.
- static FileAccess *create_for_path(const String &p_path);
- static FileAccess *open(const String &p_path, int p_mode_flags, Error *r_error = nullptr); /// Create a file access (for the current platform) this is the only portable way of accessing files.
- static CreateFunc get_create_func(AccessType p_access);
- static bool exists(const String &p_name); ///< return true if a file exists
- static uint64_t get_modified_time(const String &p_file);
- static uint32_t get_unix_permissions(const String &p_file);
- static Error set_unix_permissions(const String &p_file, uint32_t p_permissions);
-
- static void set_backup_save(bool p_enable) { backup_save = p_enable; };
- static bool is_backup_save_enabled() { return backup_save; };
-
- static String get_md5(const String &p_file);
- static String get_sha256(const String &p_file);
- static String get_multiple_md5(const Vector<String> &p_file);
-
- static Vector<uint8_t> get_file_as_array(const String &p_path, Error *r_error = nullptr);
- static String get_file_as_string(const String &p_path, Error *r_error = nullptr);
-
- template <class T>
- static void make_default(AccessType p_access) {
- create_func[p_access] = _create_builtin<T>;
- }
-
- FileAccess() {}
- virtual ~FileAccess() {}
-};
-
-struct FileAccessRef {
- _FORCE_INLINE_ FileAccess *operator->() {
- return f;
- }
-
- operator bool() const { return f != nullptr; }
-
- FileAccess *f;
-
- operator FileAccess *() { return f; }
-
- FileAccessRef(FileAccess *fa) { f = fa; }
- ~FileAccessRef() {
- if (f) {
- memdelete(f);
- }
- }
-};
-
-#endif // FILE_ACCESS_H
diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp
index d088151a6d..fdc43da7a5 100644
--- a/core/os/keyboard.cpp
+++ b/core/os/keyboard.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,401 +33,399 @@
#include "core/os/os.h"
struct _KeyCodeText {
- int code;
+ 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_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_CONTROL ,"Control"},
+ {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"},
#ifdef OSX_ENABLED
- {KEY_META ,"Command"},
+ {Key::META ,"Command"},
#else
- {KEY_META ,"Meta"},
+ {Key::META ,"Meta"},
#endif
- {KEY_ALT ,"Alt"},
- {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_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_SUPER_L ,"Super L"},
- {KEY_SUPER_R ,"Super R"},
- {KEY_MENU ,"Menu"},
- {KEY_HYPER_L ,"Hyper L"},
- {KEY_HYPER_R ,"Hyper R"},
- {KEY_HELP ,"Help"},
- {KEY_DIRECTION_L ,"Direction L"},
- {KEY_DIRECTION_R ,"Direction R"},
- {KEY_BACK ,"Back"},
- {KEY_FORWARD ,"Forward"},
- {KEY_STOP ,"Stop"},
- {KEY_REFRESH ,"Refresh"},
- {KEY_VOLUMEDOWN ,"VolumeDown"},
- {KEY_VOLUMEMUTE ,"VolumeMute"},
- {KEY_VOLUMEUP ,"VolumeUp"},
- {KEY_BASSBOOST ,"BassBoost"},
- {KEY_BASSUP ,"BassUp"},
- {KEY_BASSDOWN ,"BassDown"},
- {KEY_TREBLEUP ,"TrebleUp"},
- {KEY_TREBLEDOWN ,"TrebleDown"},
- {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_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_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_0 ,"0"},
- {KEY_1 ,"1"},
- {KEY_2 ,"2"},
- {KEY_3 ,"3"},
- {KEY_4 ,"4"},
- {KEY_5 ,"5"},
- {KEY_6 ,"6"},
- {KEY_7 ,"7"},
- {KEY_8 ,"8"},
- {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_NOBREAKSPACE ,"NoBreakSpace"},
- {KEY_EXCLAMDOWN ,"ExclamDown"},
- {KEY_CENT ,"Cent"},
- {KEY_STERLING ,"Sterling"},
- {KEY_CURRENCY ,"Currency"},
- {KEY_YEN ,"Yen"},
- {KEY_BROKENBAR ,"BrokenBar"},
- {KEY_SECTION ,"Section"},
- {KEY_DIAERESIS ,"Diaeresis"},
- {KEY_COPYRIGHT ,"Copyright"},
- {KEY_ORDFEMININE ,"Ordfeminine"},
- {KEY_GUILLEMOTLEFT ,"GuillemotLeft"},
- {KEY_NOTSIGN ,"NotSign"},
- {KEY_HYPHEN ,"Hyphen"},
- {KEY_REGISTERED ,"Registered"},
- {KEY_MACRON ,"Macron"},
- {KEY_DEGREE ,"Degree"},
- {KEY_PLUSMINUS ,"PlusMinus"},
- {KEY_TWOSUPERIOR ,"TwoSuperior"},
- {KEY_THREESUPERIOR ,"ThreeSuperior"},
- {KEY_ACUTE ,"Acute"},
- {KEY_MU ,"Mu"},
- {KEY_PARAGRAPH ,"Paragraph"},
- {KEY_PERIODCENTERED ,"PeriodCentered"},
- {KEY_CEDILLA ,"Cedilla"},
- {KEY_ONESUPERIOR ,"OneSuperior"},
- {KEY_MASCULINE ,"Masculine"},
- {KEY_GUILLEMOTRIGHT ,"GuillemotRight"},
- {KEY_ONEQUARTER ,"OneQuarter"},
- {KEY_ONEHALF ,"OneHalf"},
- {KEY_THREEQUARTERS ,"ThreeQuarters"},
- {KEY_QUESTIONDOWN ,"QuestionDown"},
- {KEY_AGRAVE ,"Agrave"},
- {KEY_AACUTE ,"Aacute"},
- {KEY_ACIRCUMFLEX ,"AcircumFlex"},
- {KEY_ATILDE ,"Atilde"},
- {KEY_ADIAERESIS ,"Adiaeresis"},
- {KEY_ARING ,"Aring"},
- {KEY_AE ,"Ae"},
- {KEY_CCEDILLA ,"Ccedilla"},
- {KEY_EGRAVE ,"Egrave"},
- {KEY_EACUTE ,"Eacute"},
- {KEY_ECIRCUMFLEX ,"Ecircumflex"},
- {KEY_EDIAERESIS ,"Ediaeresis"},
- {KEY_IGRAVE ,"Igrave"},
- {KEY_IACUTE ,"Iacute"},
- {KEY_ICIRCUMFLEX ,"Icircumflex"},
- {KEY_IDIAERESIS ,"Idiaeresis"},
- {KEY_ETH ,"Eth"},
- {KEY_NTILDE ,"Ntilde"},
- {KEY_OGRAVE ,"Ograve"},
- {KEY_OACUTE ,"Oacute"},
- {KEY_OCIRCUMFLEX ,"Ocircumflex"},
- {KEY_OTILDE ,"Otilde"},
- {KEY_ODIAERESIS ,"Odiaeresis"},
- {KEY_MULTIPLY ,"Multiply"},
- {KEY_OOBLIQUE ,"Ooblique"},
- {KEY_UGRAVE ,"Ugrave"},
- {KEY_UACUTE ,"Uacute"},
- {KEY_UCIRCUMFLEX ,"Ucircumflex"},
- {KEY_UDIAERESIS ,"Udiaeresis"},
- {KEY_YACUTE ,"Yacute"},
- {KEY_THORN ,"Thorn"},
- {KEY_SSHARP ,"Ssharp"},
-
- {KEY_DIVISION ,"Division"},
- {KEY_YDIAERESIS ,"Ydiaeresis"},
- {0 ,nullptr}
+ {Key::ALT ,"Alt"},
+ {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::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::SUPER_L ,"Super L"},
+ {Key::SUPER_R ,"Super R"},
+ {Key::MENU ,"Menu"},
+ {Key::HYPER_L ,"Hyper L"},
+ {Key::HYPER_R ,"Hyper R"},
+ {Key::HELP ,"Help"},
+ {Key::DIRECTION_L ,"Direction L"},
+ {Key::DIRECTION_R ,"Direction R"},
+ {Key::BACK ,"Back"},
+ {Key::FORWARD ,"Forward"},
+ {Key::STOP ,"Stop"},
+ {Key::REFRESH ,"Refresh"},
+ {Key::VOLUMEDOWN ,"VolumeDown"},
+ {Key::VOLUMEMUTE ,"VolumeMute"},
+ {Key::VOLUMEUP ,"VolumeUp"},
+ {Key::BASSBOOST ,"BassBoost"},
+ {Key::BASSUP ,"BassUp"},
+ {Key::BASSDOWN ,"BassDown"},
+ {Key::TREBLEUP ,"TrebleUp"},
+ {Key::TREBLEDOWN ,"TrebleDown"},
+ {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::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::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::NOBREAKSPACE ,"NoBreakSpace"},
+ {Key::EXCLAMDOWN ,"ExclamDown"},
+ {Key::CENT ,"Cent"},
+ {Key::STERLING ,"Sterling"},
+ {Key::CURRENCY ,"Currency"},
+ {Key::YEN ,"Yen"},
+ {Key::BROKENBAR ,"BrokenBar"},
+ {Key::SECTION ,"Section"},
+ {Key::DIAERESIS ,"Diaeresis"},
+ {Key::COPYRIGHT ,"Copyright"},
+ {Key::ORDFEMININE ,"Ordfeminine"},
+ {Key::GUILLEMOTLEFT ,"GuillemotLeft"},
+ {Key::NOTSIGN ,"NotSign"},
+ {Key::HYPHEN ,"Hyphen"},
+ {Key::KEY_REGISTERED ,"Registered"},
+ {Key::MACRON ,"Macron"},
+ {Key::DEGREE ,"Degree"},
+ {Key::PLUSMINUS ,"PlusMinus"},
+ {Key::TWOSUPERIOR ,"TwoSuperior"},
+ {Key::THREESUPERIOR ,"ThreeSuperior"},
+ {Key::ACUTE ,"Acute"},
+ {Key::MU ,"Mu"},
+ {Key::PARAGRAPH ,"Paragraph"},
+ {Key::PERIODCENTERED ,"PeriodCentered"},
+ {Key::CEDILLA ,"Cedilla"},
+ {Key::ONESUPERIOR ,"OneSuperior"},
+ {Key::MASCULINE ,"Masculine"},
+ {Key::GUILLEMOTRIGHT ,"GuillemotRight"},
+ {Key::ONEQUARTER ,"OneQuarter"},
+ {Key::ONEHALF ,"OneHalf"},
+ {Key::THREEQUARTERS ,"ThreeQuarters"},
+ {Key::QUESTIONDOWN ,"QuestionDown"},
+ {Key::AGRAVE ,"Agrave"},
+ {Key::AACUTE ,"Aacute"},
+ {Key::ACIRCUMFLEX ,"AcircumFlex"},
+ {Key::ATILDE ,"Atilde"},
+ {Key::ADIAERESIS ,"Adiaeresis"},
+ {Key::ARING ,"Aring"},
+ {Key::AE ,"Ae"},
+ {Key::CCEDILLA ,"Ccedilla"},
+ {Key::EGRAVE ,"Egrave"},
+ {Key::EACUTE ,"Eacute"},
+ {Key::ECIRCUMFLEX ,"Ecircumflex"},
+ {Key::EDIAERESIS ,"Ediaeresis"},
+ {Key::IGRAVE ,"Igrave"},
+ {Key::IACUTE ,"Iacute"},
+ {Key::ICIRCUMFLEX ,"Icircumflex"},
+ {Key::IDIAERESIS ,"Idiaeresis"},
+ {Key::ETH ,"Eth"},
+ {Key::NTILDE ,"Ntilde"},
+ {Key::OGRAVE ,"Ograve"},
+ {Key::OACUTE ,"Oacute"},
+ {Key::OCIRCUMFLEX ,"Ocircumflex"},
+ {Key::OTILDE ,"Otilde"},
+ {Key::ODIAERESIS ,"Odiaeresis"},
+ {Key::MULTIPLY ,"Multiply"},
+ {Key::OOBLIQUE ,"Ooblique"},
+ {Key::UGRAVE ,"Ugrave"},
+ {Key::UACUTE ,"Uacute"},
+ {Key::UCIRCUMFLEX ,"Ucircumflex"},
+ {Key::UDIAERESIS ,"Udiaeresis"},
+ {Key::YACUTE ,"Yacute"},
+ {Key::THORN ,"Thorn"},
+ {Key::SSHARP ,"Ssharp"},
+ {Key::DIVISION ,"Division"},
+ {Key::YDIAERESIS ,"Ydiaeresis"},
+ {Key::NONE ,nullptr}
/* clang-format on */
};
-bool keycode_has_unicode(uint32_t p_keycode) {
+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_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_CONTROL:
- 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_SUPER_L:
- case KEY_SUPER_R:
- case KEY_MENU:
- case KEY_HYPER_L:
- case KEY_HYPER_R:
- case KEY_HELP:
- case KEY_DIRECTION_L:
- case KEY_DIRECTION_R:
- case KEY_BACK:
- case KEY_FORWARD:
- case KEY_STOP:
- case KEY_REFRESH:
- case KEY_VOLUMEDOWN:
- case KEY_VOLUMEMUTE:
- case KEY_VOLUMEUP:
- case KEY_BASSBOOST:
- case KEY_BASSUP:
- case KEY_BASSDOWN:
- case KEY_TREBLEUP:
- case KEY_TREBLEDOWN:
- 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::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::SUPER_L:
+ case Key::SUPER_R:
+ case Key::MENU:
+ case Key::HYPER_L:
+ case Key::HYPER_R:
+ case Key::HELP:
+ case Key::DIRECTION_L:
+ case Key::DIRECTION_R:
+ case Key::BACK:
+ case Key::FORWARD:
+ case Key::STOP:
+ case Key::REFRESH:
+ case Key::VOLUMEDOWN:
+ case Key::VOLUMEMUTE:
+ case Key::VOLUMEUP:
+ case Key::BASSBOOST:
+ case Key::BASSUP:
+ case Key::BASSDOWN:
+ case Key::TREBLEUP:
+ case Key::TREBLEDOWN:
+ 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:
return false;
+ default: {
+ }
}
return true;
}
-String keycode_get_string(uint32_t p_code) {
+String keycode_get_string(Key p_code) {
String codestr;
- if (p_code & KEY_MASK_SHIFT) {
- codestr += find_keycode_name(KEY_SHIFT);
+ if ((p_code & KeyModifierMask::SHIFT) != Key::NONE) {
+ codestr += find_keycode_name(Key::SHIFT);
codestr += "+";
}
- if (p_code & KEY_MASK_ALT) {
- codestr += find_keycode_name(KEY_ALT);
+ if ((p_code & KeyModifierMask::ALT) != Key::NONE) {
+ codestr += find_keycode_name(Key::ALT);
codestr += "+";
}
- if (p_code & KEY_MASK_CTRL) {
- codestr += find_keycode_name(KEY_CONTROL);
+ if ((p_code & KeyModifierMask::CTRL) != Key::NONE) {
+ codestr += find_keycode_name(Key::CTRL);
codestr += "+";
}
- if (p_code & KEY_MASK_META) {
- codestr += find_keycode_name(KEY_META);
+ if ((p_code & KeyModifierMask::META) != Key::NONE) {
+ codestr += find_keycode_name(Key::META);
codestr += "+";
}
- p_code &= KEY_CODE_MASK;
+ p_code &= KeyModifierMask::CODE_MASK;
const _KeyCodeText *kct = &_keycodes[0];
while (kct->text) {
- if (kct->code == (int)p_code) {
+ if (kct->code == p_code) {
codestr += kct->text;
return codestr;
}
kct++;
}
- codestr += String::chr(p_code);
+ codestr += String::chr((char32_t)p_code);
return codestr;
}
-int find_keycode(const String &p_code) {
+Key find_keycode(const String &p_code) {
const _KeyCodeText *kct = &_keycodes[0];
while (kct->text) {
@@ -437,10 +435,10 @@ int find_keycode(const String &p_code) {
kct++;
}
- return 0;
+ return Key::NONE;
}
-const char *find_keycode_name(int p_keycode) {
+const char *find_keycode_name(Key p_keycode) {
const _KeyCodeText *kct = &_keycodes[0];
while (kct->text) {
@@ -465,7 +463,7 @@ int keycode_get_count() {
}
int keycode_get_value_by_index(int p_index) {
- return _keycodes[p_index].code;
+ return (int)_keycodes[p_index].code;
}
const char *keycode_get_name_by_index(int p_index) {
diff --git a/core/os/keyboard.h b/core/os/keyboard.h
index 5d11e6a378..c780099553 100644
--- a/core/os/keyboard.h
+++ b/core/os/keyboard.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,149 +31,144 @@
#ifndef KEYBOARD_H
#define KEYBOARD_H
-#include "core/ustring.h"
+#include "core/string/ustring.h"
-/*
- Special Key:
-
- The strategy here is similar to the one used by toolkits,
- which consists in leaving the 24 bits unicode range for printable
- characters, and use the upper 8 bits for special keys and
- modifiers. This way everything (char/keycode) can fit nicely in one 32 bits unsigned integer.
-*/
-enum {
- SPKEY = (1 << 24)
-};
-
-enum KeyList {
+enum class Key {
+ NONE = 0,
+ // Special key: The strategy here is similar to the one used by toolkits,
+ // which consists in leaving the 24 bits unicode range for printable
+ // characters, and use the upper 8 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 << 24),
/* CURSOR/FUNCTION/BROWSER/MULTIMEDIA/MISC KEYS */
- KEY_ESCAPE = SPKEY | 0x01,
- KEY_TAB = SPKEY | 0x02,
- KEY_BACKTAB = SPKEY | 0x03,
- KEY_BACKSPACE = SPKEY | 0x04,
- KEY_ENTER = SPKEY | 0x05,
- KEY_KP_ENTER = SPKEY | 0x06,
- KEY_INSERT = SPKEY | 0x07,
- KEY_DELETE = SPKEY | 0x08,
- KEY_PAUSE = SPKEY | 0x09,
- KEY_PRINT = SPKEY | 0x0A,
- KEY_SYSREQ = SPKEY | 0x0B,
- KEY_CLEAR = SPKEY | 0x0C,
- KEY_HOME = SPKEY | 0x0D,
- KEY_END = SPKEY | 0x0E,
- KEY_LEFT = SPKEY | 0x0F,
- KEY_UP = SPKEY | 0x10,
- KEY_RIGHT = SPKEY | 0x11,
- KEY_DOWN = SPKEY | 0x12,
- KEY_PAGEUP = SPKEY | 0x13,
- KEY_PAGEDOWN = SPKEY | 0x14,
- KEY_SHIFT = SPKEY | 0x15,
- KEY_CONTROL = SPKEY | 0x16,
- KEY_META = SPKEY | 0x17,
- KEY_ALT = SPKEY | 0x18,
- KEY_CAPSLOCK = SPKEY | 0x19,
- KEY_NUMLOCK = SPKEY | 0x1A,
- KEY_SCROLLLOCK = SPKEY | 0x1B,
- KEY_F1 = SPKEY | 0x1C,
- KEY_F2 = SPKEY | 0x1D,
- KEY_F3 = SPKEY | 0x1E,
- KEY_F4 = SPKEY | 0x1F,
- KEY_F5 = SPKEY | 0x20,
- KEY_F6 = SPKEY | 0x21,
- KEY_F7 = SPKEY | 0x22,
- KEY_F8 = SPKEY | 0x23,
- KEY_F9 = SPKEY | 0x24,
- KEY_F10 = SPKEY | 0x25,
- KEY_F11 = SPKEY | 0x26,
- KEY_F12 = SPKEY | 0x27,
- KEY_F13 = SPKEY | 0x28,
- KEY_F14 = SPKEY | 0x29,
- KEY_F15 = SPKEY | 0x2A,
- KEY_F16 = SPKEY | 0x2B,
- KEY_KP_MULTIPLY = SPKEY | 0x81,
- KEY_KP_DIVIDE = SPKEY | 0x82,
- KEY_KP_SUBTRACT = SPKEY | 0x83,
- KEY_KP_PERIOD = SPKEY | 0x84,
- KEY_KP_ADD = SPKEY | 0x85,
- KEY_KP_0 = SPKEY | 0x86,
- KEY_KP_1 = SPKEY | 0x87,
- KEY_KP_2 = SPKEY | 0x88,
- KEY_KP_3 = SPKEY | 0x89,
- KEY_KP_4 = SPKEY | 0x8A,
- KEY_KP_5 = SPKEY | 0x8B,
- KEY_KP_6 = SPKEY | 0x8C,
- KEY_KP_7 = SPKEY | 0x8D,
- KEY_KP_8 = SPKEY | 0x8E,
- KEY_KP_9 = SPKEY | 0x8F,
- KEY_SUPER_L = SPKEY | 0x2C,
- KEY_SUPER_R = SPKEY | 0x2D,
- KEY_MENU = SPKEY | 0x2E,
- KEY_HYPER_L = SPKEY | 0x2F,
- KEY_HYPER_R = SPKEY | 0x30,
- KEY_HELP = SPKEY | 0x31,
- KEY_DIRECTION_L = SPKEY | 0x32,
- KEY_DIRECTION_R = SPKEY | 0x33,
- KEY_BACK = SPKEY | 0x40,
- KEY_FORWARD = SPKEY | 0x41,
- KEY_STOP = SPKEY | 0x42,
- KEY_REFRESH = SPKEY | 0x43,
- KEY_VOLUMEDOWN = SPKEY | 0x44,
- KEY_VOLUMEMUTE = SPKEY | 0x45,
- KEY_VOLUMEUP = SPKEY | 0x46,
- KEY_BASSBOOST = SPKEY | 0x47,
- KEY_BASSUP = SPKEY | 0x48,
- KEY_BASSDOWN = SPKEY | 0x49,
- KEY_TREBLEUP = SPKEY | 0x4A,
- KEY_TREBLEDOWN = SPKEY | 0x4B,
- KEY_MEDIAPLAY = SPKEY | 0x4C,
- KEY_MEDIASTOP = SPKEY | 0x4D,
- KEY_MEDIAPREVIOUS = SPKEY | 0x4E,
- KEY_MEDIANEXT = SPKEY | 0x4F,
- KEY_MEDIARECORD = SPKEY | 0x50,
- KEY_HOMEPAGE = SPKEY | 0x51,
- KEY_FAVORITES = SPKEY | 0x52,
- KEY_SEARCH = SPKEY | 0x53,
- KEY_STANDBY = SPKEY | 0x54,
- KEY_OPENURL = SPKEY | 0x55,
- KEY_LAUNCHMAIL = SPKEY | 0x56,
- KEY_LAUNCHMEDIA = SPKEY | 0x57,
- KEY_LAUNCH0 = SPKEY | 0x58,
- KEY_LAUNCH1 = SPKEY | 0x59,
- KEY_LAUNCH2 = SPKEY | 0x5A,
- KEY_LAUNCH3 = SPKEY | 0x5B,
- KEY_LAUNCH4 = SPKEY | 0x5C,
- KEY_LAUNCH5 = SPKEY | 0x5D,
- KEY_LAUNCH6 = SPKEY | 0x5E,
- KEY_LAUNCH7 = SPKEY | 0x5F,
- KEY_LAUNCH8 = SPKEY | 0x60,
- KEY_LAUNCH9 = SPKEY | 0x61,
- KEY_LAUNCHA = SPKEY | 0x62,
- KEY_LAUNCHB = SPKEY | 0x63,
- KEY_LAUNCHC = SPKEY | 0x64,
- KEY_LAUNCHD = SPKEY | 0x65,
- KEY_LAUNCHE = SPKEY | 0x66,
- KEY_LAUNCHF = SPKEY | 0x67,
+ 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,
+ 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,
+ 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,
+ SUPER_L = SPECIAL | 0x2C,
+ SUPER_R = SPECIAL | 0x2D,
+ MENU = SPECIAL | 0x2E,
+ HYPER_L = SPECIAL | 0x2F,
+ HYPER_R = SPECIAL | 0x30,
+ HELP = SPECIAL | 0x31,
+ DIRECTION_L = SPECIAL | 0x32,
+ DIRECTION_R = SPECIAL | 0x33,
+ BACK = SPECIAL | 0x40,
+ FORWARD = SPECIAL | 0x41,
+ STOP = SPECIAL | 0x42,
+ REFRESH = SPECIAL | 0x43,
+ VOLUMEDOWN = SPECIAL | 0x44,
+ VOLUMEMUTE = SPECIAL | 0x45,
+ VOLUMEUP = SPECIAL | 0x46,
+ BASSBOOST = SPECIAL | 0x47,
+ BASSUP = SPECIAL | 0x48,
+ BASSDOWN = SPECIAL | 0x49,
+ TREBLEUP = SPECIAL | 0x4A,
+ TREBLEDOWN = SPECIAL | 0x4B,
+ MEDIAPLAY = SPECIAL | 0x4C,
+ MEDIASTOP = SPECIAL | 0x4D,
+ MEDIAPREVIOUS = SPECIAL | 0x4E,
+ MEDIANEXT = SPECIAL | 0x4F,
+ MEDIARECORD = SPECIAL | 0x50,
+ HOMEPAGE = SPECIAL | 0x51,
+ FAVORITES = SPECIAL | 0x52,
+ SEARCH = SPECIAL | 0x53,
+ STANDBY = SPECIAL | 0x54,
+ OPENURL = SPECIAL | 0x55,
+ LAUNCHMAIL = SPECIAL | 0x56,
+ LAUNCHMEDIA = SPECIAL | 0x57,
+ LAUNCH0 = SPECIAL | 0x58,
+ LAUNCH1 = SPECIAL | 0x59,
+ LAUNCH2 = SPECIAL | 0x5A,
+ LAUNCH3 = SPECIAL | 0x5B,
+ LAUNCH4 = SPECIAL | 0x5C,
+ LAUNCH5 = SPECIAL | 0x5D,
+ LAUNCH6 = SPECIAL | 0x5E,
+ LAUNCH7 = SPECIAL | 0x5F,
+ LAUNCH8 = SPECIAL | 0x60,
+ LAUNCH9 = SPECIAL | 0x61,
+ LAUNCHA = SPECIAL | 0x62,
+ LAUNCHB = SPECIAL | 0x63,
+ LAUNCHC = SPECIAL | 0x64,
+ LAUNCHD = SPECIAL | 0x65,
+ LAUNCHE = SPECIAL | 0x66,
+ LAUNCHF = SPECIAL | 0x67,
- KEY_UNKNOWN = SPKEY | 0xFFFFFF,
+ UNKNOWN = SPECIAL | 0xFFFFFF,
/* PRINTABLE LATIN 1 CODES */
- KEY_SPACE = 0x0020,
- KEY_EXCLAM = 0x0021,
- KEY_QUOTEDBL = 0x0022,
- KEY_NUMBERSIGN = 0x0023,
- KEY_DOLLAR = 0x0024,
- KEY_PERCENT = 0x0025,
- KEY_AMPERSAND = 0x0026,
- KEY_APOSTROPHE = 0x0027,
- KEY_PARENLEFT = 0x0028,
- KEY_PARENRIGHT = 0x0029,
- KEY_ASTERISK = 0x002A,
- KEY_PLUS = 0x002B,
- KEY_COMMA = 0x002C,
- KEY_MINUS = 0x002D,
- KEY_PERIOD = 0x002E,
- KEY_SLASH = 0x002F,
+ 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,
@@ -184,143 +179,205 @@ enum KeyList {
KEY_7 = 0x0037,
KEY_8 = 0x0038,
KEY_9 = 0x0039,
- KEY_COLON = 0x003A,
- KEY_SEMICOLON = 0x003B,
- KEY_LESS = 0x003C,
- KEY_EQUAL = 0x003D,
- KEY_GREATER = 0x003E,
- KEY_QUESTION = 0x003F,
- KEY_AT = 0x0040,
- KEY_A = 0x0041,
- KEY_B = 0x0042,
- KEY_C = 0x0043,
- KEY_D = 0x0044,
- KEY_E = 0x0045,
- KEY_F = 0x0046,
- KEY_G = 0x0047,
- KEY_H = 0x0048,
- KEY_I = 0x0049,
- KEY_J = 0x004A,
- KEY_K = 0x004B,
- KEY_L = 0x004C,
- KEY_M = 0x004D,
- KEY_N = 0x004E,
- KEY_O = 0x004F,
- KEY_P = 0x0050,
- KEY_Q = 0x0051,
- KEY_R = 0x0052,
- KEY_S = 0x0053,
- KEY_T = 0x0054,
- KEY_U = 0x0055,
- KEY_V = 0x0056,
- KEY_W = 0x0057,
- KEY_X = 0x0058,
- KEY_Y = 0x0059,
- KEY_Z = 0x005A,
- KEY_BRACKETLEFT = 0x005B,
- KEY_BACKSLASH = 0x005C,
- KEY_BRACKETRIGHT = 0x005D,
- KEY_ASCIICIRCUM = 0x005E,
- KEY_UNDERSCORE = 0x005F,
- KEY_QUOTELEFT = 0x0060,
- KEY_BRACELEFT = 0x007B,
- KEY_BAR = 0x007C,
- KEY_BRACERIGHT = 0x007D,
- KEY_ASCIITILDE = 0x007E,
- KEY_NOBREAKSPACE = 0x00A0,
- KEY_EXCLAMDOWN = 0x00A1,
- KEY_CENT = 0x00A2,
- KEY_STERLING = 0x00A3,
- KEY_CURRENCY = 0x00A4,
- KEY_YEN = 0x00A5,
- KEY_BROKENBAR = 0x00A6,
- KEY_SECTION = 0x00A7,
- KEY_DIAERESIS = 0x00A8,
- KEY_COPYRIGHT = 0x00A9,
- KEY_ORDFEMININE = 0x00AA,
- KEY_GUILLEMOTLEFT = 0x00AB,
- KEY_NOTSIGN = 0x00AC,
- KEY_HYPHEN = 0x00AD,
- KEY_REGISTERED = 0x00AE,
- KEY_MACRON = 0x00AF,
- KEY_DEGREE = 0x00B0,
- KEY_PLUSMINUS = 0x00B1,
- KEY_TWOSUPERIOR = 0x00B2,
- KEY_THREESUPERIOR = 0x00B3,
- KEY_ACUTE = 0x00B4,
- KEY_MU = 0x00B5,
- KEY_PARAGRAPH = 0x00B6,
- KEY_PERIODCENTERED = 0x00B7,
- KEY_CEDILLA = 0x00B8,
- KEY_ONESUPERIOR = 0x00B9,
- KEY_MASCULINE = 0x00BA,
- KEY_GUILLEMOTRIGHT = 0x00BB,
- KEY_ONEQUARTER = 0x00BC,
- KEY_ONEHALF = 0x00BD,
- KEY_THREEQUARTERS = 0x00BE,
- KEY_QUESTIONDOWN = 0x00BF,
- KEY_AGRAVE = 0x00C0,
- KEY_AACUTE = 0x00C1,
- KEY_ACIRCUMFLEX = 0x00C2,
- KEY_ATILDE = 0x00C3,
- KEY_ADIAERESIS = 0x00C4,
- KEY_ARING = 0x00C5,
- KEY_AE = 0x00C6,
- KEY_CCEDILLA = 0x00C7,
- KEY_EGRAVE = 0x00C8,
- KEY_EACUTE = 0x00C9,
- KEY_ECIRCUMFLEX = 0x00CA,
- KEY_EDIAERESIS = 0x00CB,
- KEY_IGRAVE = 0x00CC,
- KEY_IACUTE = 0x00CD,
- KEY_ICIRCUMFLEX = 0x00CE,
- KEY_IDIAERESIS = 0x00CF,
- KEY_ETH = 0x00D0,
- KEY_NTILDE = 0x00D1,
- KEY_OGRAVE = 0x00D2,
- KEY_OACUTE = 0x00D3,
- KEY_OCIRCUMFLEX = 0x00D4,
- KEY_OTILDE = 0x00D5,
- KEY_ODIAERESIS = 0x00D6,
- KEY_MULTIPLY = 0x00D7,
- KEY_OOBLIQUE = 0x00D8,
- KEY_UGRAVE = 0x00D9,
- KEY_UACUTE = 0x00DA,
- KEY_UCIRCUMFLEX = 0x00DB,
- KEY_UDIAERESIS = 0x00DC,
- KEY_YACUTE = 0x00DD,
- KEY_THORN = 0x00DE,
- KEY_SSHARP = 0x00DF,
-
- KEY_DIVISION = 0x00F7,
- KEY_YDIAERESIS = 0x00FF,
+ 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,
+ NOBREAKSPACE = 0x00A0,
+ EXCLAMDOWN = 0x00A1,
+ CENT = 0x00A2,
+ STERLING = 0x00A3,
+ CURRENCY = 0x00A4,
+ YEN = 0x00A5,
+ BROKENBAR = 0x00A6,
+ SECTION = 0x00A7,
+ DIAERESIS = 0x00A8,
+ COPYRIGHT = 0x00A9,
+ ORDFEMININE = 0x00AA,
+ GUILLEMOTLEFT = 0x00AB,
+ NOTSIGN = 0x00AC,
+ HYPHEN = 0x00AD,
+ KEY_REGISTERED = 0x00AE, // "REGISTERED" is a reserved word on Windows.
+ MACRON = 0x00AF,
+ DEGREE = 0x00B0,
+ PLUSMINUS = 0x00B1,
+ TWOSUPERIOR = 0x00B2,
+ THREESUPERIOR = 0x00B3,
+ ACUTE = 0x00B4,
+ MU = 0x00B5,
+ PARAGRAPH = 0x00B6,
+ PERIODCENTERED = 0x00B7,
+ CEDILLA = 0x00B8,
+ ONESUPERIOR = 0x00B9,
+ MASCULINE = 0x00BA,
+ GUILLEMOTRIGHT = 0x00BB,
+ ONEQUARTER = 0x00BC,
+ ONEHALF = 0x00BD,
+ THREEQUARTERS = 0x00BE,
+ QUESTIONDOWN = 0x00BF,
+ AGRAVE = 0x00C0,
+ AACUTE = 0x00C1,
+ ACIRCUMFLEX = 0x00C2,
+ ATILDE = 0x00C3,
+ ADIAERESIS = 0x00C4,
+ ARING = 0x00C5,
+ AE = 0x00C6,
+ CCEDILLA = 0x00C7,
+ EGRAVE = 0x00C8,
+ EACUTE = 0x00C9,
+ ECIRCUMFLEX = 0x00CA,
+ EDIAERESIS = 0x00CB,
+ IGRAVE = 0x00CC,
+ IACUTE = 0x00CD,
+ ICIRCUMFLEX = 0x00CE,
+ IDIAERESIS = 0x00CF,
+ ETH = 0x00D0,
+ NTILDE = 0x00D1,
+ OGRAVE = 0x00D2,
+ OACUTE = 0x00D3,
+ OCIRCUMFLEX = 0x00D4,
+ OTILDE = 0x00D5,
+ ODIAERESIS = 0x00D6,
+ MULTIPLY = 0x00D7,
+ OOBLIQUE = 0x00D8,
+ UGRAVE = 0x00D9,
+ UACUTE = 0x00DA,
+ UCIRCUMFLEX = 0x00DB,
+ UDIAERESIS = 0x00DC,
+ YACUTE = 0x00DD,
+ THORN = 0x00DE,
+ SSHARP = 0x00DF,
+ DIVISION = 0x00F7,
+ YDIAERESIS = 0x00FF,
+ END_LATIN1 = 0x0100,
};
-enum KeyModifierMask {
-
- KEY_CODE_MASK = ((1 << 25) - 1), ///< Apply this mask to any keycode to remove modifiers.
- KEY_MODIFIER_MASK = (0xFF << 24), ///< Apply this mask to isolate modifiers.
- KEY_MASK_SHIFT = (1 << 25),
- KEY_MASK_ALT = (1 << 26),
- KEY_MASK_META = (1 << 27),
- KEY_MASK_CTRL = (1 << 28),
+enum class KeyModifierMask {
+ CODE_MASK = ((1 << 25) - 1), ///< Apply this mask to any keycode to remove modifiers.
+ MODIFIER_MASK = (0xFF << 24), ///< Apply this mask to isolate modifiers.
+ SHIFT = (1 << 25),
+ ALT = (1 << 26),
+ META = (1 << 27),
+ CTRL = (1 << 28),
#ifdef APPLE_STYLE_KEYS
- KEY_MASK_CMD = KEY_MASK_META,
+ CMD = META,
#else
- KEY_MASK_CMD = KEY_MASK_CTRL,
+ CMD = CTRL,
#endif
+ KPAD = (1 << 29),
+ GROUP_SWITCH = (1 << 30)
+};
- KEY_MASK_KPAD = (1 << 29),
- KEY_MASK_GROUP_SWITCH = (1 << 30)
- // bit 31 can't be used because variant uses regular 32 bits int as datatype
+// To avoid having unnecessary operators, only define the ones that are needed.
-};
+inline Key operator-(uint32_t a, Key b) {
+ return (Key)(a - (uint32_t)b);
+}
+
+inline Key &operator-=(Key &a, int b) {
+ return (Key &)((int &)a -= b);
+}
+
+inline Key operator+(Key a, int b) {
+ return (Key)((int)a + (int)b);
+}
+
+inline Key operator+(Key a, Key b) {
+ return (Key)((int)a + (int)b);
+}
+
+inline Key operator-(Key a, Key b) {
+ return (Key)((int)a - (int)b);
+}
+
+inline Key operator&(Key a, Key b) {
+ return (Key)((int)a & (int)b);
+}
+
+inline Key operator|(Key a, Key b) {
+ return (Key)((int)a | (int)b);
+}
+
+inline Key &operator|=(Key &a, Key b) {
+ return (Key &)((int &)a |= (int)b);
+}
+
+inline Key &operator|=(Key &a, KeyModifierMask b) {
+ return (Key &)((int &)a |= (int)b);
+}
+
+inline Key &operator&=(Key &a, KeyModifierMask b) {
+ return (Key &)((int &)a &= (int)b);
+}
+
+inline Key operator|(Key a, KeyModifierMask b) {
+ return (Key)((int)a | (int)b);
+}
+
+inline Key operator&(Key a, KeyModifierMask b) {
+ return (Key)((int)a & (int)b);
+}
+
+inline Key operator+(KeyModifierMask a, Key b) {
+ return (Key)((int)a + (int)b);
+}
+
+inline Key operator|(KeyModifierMask a, Key b) {
+ return (Key)((int)a | (int)b);
+}
+
+inline KeyModifierMask operator+(KeyModifierMask a, KeyModifierMask b) {
+ return (KeyModifierMask)((int)a + (int)b);
+}
+
+inline KeyModifierMask operator|(KeyModifierMask a, KeyModifierMask b) {
+ return (KeyModifierMask)((int)a | (int)b);
+}
-String keycode_get_string(uint32_t p_code);
-bool keycode_has_unicode(uint32_t p_keycode);
-int find_keycode(const String &p_code);
-const char *find_keycode_name(int p_keycode);
+String keycode_get_string(Key p_code);
+bool keycode_has_unicode(Key p_keycode);
+Key find_keycode(const String &p_code);
+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);
diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp
index 434f6fa300..0ba69a8d47 100644
--- a/core/os/main_loop.cpp
+++ b/core/os/main_loop.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,19 +30,9 @@
#include "main_loop.h"
-#include "core/script_language.h"
+#include "core/object/script_language.h"
void MainLoop::_bind_methods() {
- ClassDB::bind_method(D_METHOD("init"), &MainLoop::init);
- ClassDB::bind_method(D_METHOD("iteration", "delta"), &MainLoop::iteration);
- ClassDB::bind_method(D_METHOD("idle", "delta"), &MainLoop::idle);
- ClassDB::bind_method(D_METHOD("finish"), &MainLoop::finish);
-
- BIND_VMETHOD(MethodInfo("_initialize"));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_iteration", PropertyInfo(Variant::FLOAT, "delta")));
- BIND_VMETHOD(MethodInfo(Variant::BOOL, "_idle", PropertyInfo(Variant::FLOAT, "delta")));
- BIND_VMETHOD(MethodInfo("_finalize"));
-
BIND_CONSTANT(NOTIFICATION_OS_MEMORY_WARNING);
BIND_CONSTANT(NOTIFICATION_TRANSLATION_CHANGED);
BIND_CONSTANT(NOTIFICATION_WM_ABOUT);
@@ -52,43 +42,50 @@ void MainLoop::_bind_methods() {
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")));
-};
-void MainLoop::set_init_script(const Ref<Script> &p_init_script) {
- init_script = p_init_script;
+ GDVIRTUAL_BIND(_initialize);
+ GDVIRTUAL_BIND(_physics_process, "delta");
+ GDVIRTUAL_BIND(_process, "delta");
+ GDVIRTUAL_BIND(_finalize);
}
-void MainLoop::init() {
- if (init_script.is_valid()) {
- set_script(init_script);
- }
+void MainLoop::set_initialize_script(const Ref<Script> &p_initialize_script) {
+ initialize_script = p_initialize_script;
+}
- if (get_script_instance()) {
- get_script_instance()->call("_initialize");
+void MainLoop::initialize() {
+ if (initialize_script.is_valid()) {
+ set_script(initialize_script);
}
+
+ GDVIRTUAL_CALL(_initialize);
}
-bool MainLoop::iteration(float p_time) {
- if (get_script_instance()) {
- return get_script_instance()->call("_iteration", p_time);
+bool MainLoop::physics_process(double p_time) {
+ bool quit;
+ if (GDVIRTUAL_CALL(_physics_process, p_time, quit)) {
+ return quit;
}
return false;
}
-bool MainLoop::idle(float p_time) {
- if (get_script_instance()) {
- return get_script_instance()->call("_idle", p_time);
+bool MainLoop::process(double p_time) {
+ bool quit;
+ if (GDVIRTUAL_CALL(_process, p_time, quit)) {
+ return quit;
}
return false;
}
-void MainLoop::finish() {
+void MainLoop::finalize() {
+ GDVIRTUAL_CALL(_finalize);
+
if (get_script_instance()) {
- get_script_instance()->call("_finalize");
set_script(Variant()); //clear script
}
}
diff --git a/core/os/main_loop.h b/core/os/main_loop.h
index 2c34cf193c..4da01d767e 100644
--- a/core/os/main_loop.h
+++ b/core/os/main_loop.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,18 +32,24 @@
#define MAIN_LOOP_H
#include "core/input/input_event.h"
-#include "core/reference.h"
-#include "core/script_language.h"
+#include "core/object/gdvirtual.gen.inc"
+#include "core/object/ref_counted.h"
+#include "core/object/script_language.h"
class MainLoop : public Object {
GDCLASS(MainLoop, Object);
OBJ_CATEGORY("Main Loop");
- Ref<Script> init_script;
+ Ref<Script> initialize_script;
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
@@ -56,14 +62,15 @@ public:
NOTIFICATION_APPLICATION_PAUSED = 2015,
NOTIFICATION_APPLICATION_FOCUS_IN = 2016,
NOTIFICATION_APPLICATION_FOCUS_OUT = 2017,
+ NOTIFICATION_TEXT_SERVER_CHANGED = 2018,
};
- virtual void init();
- virtual bool iteration(float p_time);
- virtual bool idle(float p_time);
- virtual void finish();
+ virtual void initialize();
+ virtual bool physics_process(double p_time);
+ virtual bool process(double p_time);
+ virtual void finalize();
- void set_init_script(const Ref<Script> &p_init_script);
+ void set_initialize_script(const Ref<Script> &p_initialize_script);
MainLoop() {}
virtual ~MainLoop() {}
diff --git a/core/os/memory.cpp b/core/os/memory.cpp
index 8457c52092..a756c1d5dd 100644
--- a/core/os/memory.cpp
+++ b/core/os/memory.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,9 +30,8 @@
#include "memory.h"
-#include "core/error_macros.h"
-#include "core/os/copymem.h"
-#include "core/safe_refcount.h"
+#include "core/error/error_macros.h"
+#include "core/templates/safe_refcount.h"
#include <stdio.h>
#include <stdlib.h>
@@ -60,11 +59,11 @@ void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_d
#endif
#ifdef DEBUG_ENABLED
-uint64_t Memory::mem_usage = 0;
-uint64_t Memory::max_usage = 0;
+SafeNumeric<uint64_t> Memory::mem_usage;
+SafeNumeric<uint64_t> Memory::max_usage;
#endif
-uint64_t Memory::alloc_count = 0;
+SafeNumeric<uint64_t> Memory::alloc_count;
void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) {
#ifdef DEBUG_ENABLED
@@ -77,7 +76,7 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) {
ERR_FAIL_COND_V(!mem, nullptr);
- atomic_increment(&alloc_count);
+ alloc_count.increment();
if (prepad) {
uint64_t *s = (uint64_t *)mem;
@@ -86,8 +85,8 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) {
uint8_t *s8 = (uint8_t *)mem;
#ifdef DEBUG_ENABLED
- atomic_add(&mem_usage, p_bytes);
- atomic_exchange_if_greater(&max_usage, mem_usage);
+ uint64_t new_mem_usage = mem_usage.add(p_bytes);
+ max_usage.exchange_if_greater(new_mem_usage);
#endif
return s8 + PAD_ALIGN;
} else {
@@ -114,10 +113,10 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) {
#ifdef DEBUG_ENABLED
if (p_bytes > *s) {
- atomic_add(&mem_usage, p_bytes - *s);
- atomic_exchange_if_greater(&max_usage, mem_usage);
+ uint64_t new_mem_usage = mem_usage.add(p_bytes - *s);
+ max_usage.exchange_if_greater(new_mem_usage);
} else {
- atomic_sub(&mem_usage, *s - p_bytes);
+ mem_usage.sub(*s - p_bytes);
}
#endif
@@ -156,14 +155,14 @@ void Memory::free_static(void *p_ptr, bool p_pad_align) {
bool prepad = p_pad_align;
#endif
- atomic_decrement(&alloc_count);
+ alloc_count.decrement();
if (prepad) {
mem -= PAD_ALIGN;
#ifdef DEBUG_ENABLED
uint64_t *s = (uint64_t *)mem;
- atomic_sub(&mem_usage, *s);
+ mem_usage.sub(*s);
#endif
free(mem);
@@ -178,7 +177,7 @@ uint64_t Memory::get_mem_available() {
uint64_t Memory::get_mem_usage() {
#ifdef DEBUG_ENABLED
- return mem_usage;
+ return mem_usage.get();
#else
return 0;
#endif
@@ -186,7 +185,7 @@ uint64_t Memory::get_mem_usage() {
uint64_t Memory::get_mem_max_usage() {
#ifdef DEBUG_ENABLED
- return max_usage;
+ return max_usage.get();
#else
return 0;
#endif
diff --git a/core/os/memory.h b/core/os/memory.h
index 46ffb4124b..ac56a12330 100644
--- a/core/os/memory.h
+++ b/core/os/memory.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,23 +31,23 @@
#ifndef MEMORY_H
#define MEMORY_H
-#include "core/error_macros.h"
-#include "core/safe_refcount.h"
+#include "core/error/error_macros.h"
+#include "core/templates/safe_refcount.h"
#include <stddef.h>
+#include <new>
#ifndef PAD_ALIGN
#define PAD_ALIGN 16 //must always be greater than this at much
#endif
class Memory {
- Memory();
#ifdef DEBUG_ENABLED
- static uint64_t mem_usage;
- static uint64_t max_usage;
+ static SafeNumeric<uint64_t> mem_usage;
+ static SafeNumeric<uint64_t> max_usage;
#endif
- static uint64_t alloc_count;
+ static SafeNumeric<uint64_t> alloc_count;
public:
static void *alloc_static(size_t p_bytes, bool p_pad_align = false);
@@ -80,7 +80,7 @@ void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_d
#define memalloc(m_size) Memory::alloc_static(m_size)
#define memrealloc(m_mem, m_size) Memory::realloc_static(m_mem, m_size)
-#define memfree(m_size) Memory::free_static(m_size)
+#define memfree(m_mem) Memory::free_static(m_mem)
_ALWAYS_INLINE_ void postinitialize_handler(void *) {}
@@ -92,15 +92,8 @@ _ALWAYS_INLINE_ T *_post_initialize(T *p_obj) {
#define memnew(m_class) _post_initialize(new ("") m_class)
-_ALWAYS_INLINE_ void *operator new(size_t p_size, void *p_pointer, size_t check, const char *p_description) {
- //void *failptr=0;
- //ERR_FAIL_COND_V( check < p_size , failptr); /** bug, or strange compiler, most likely */
-
- return p_pointer;
-}
-
#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, sizeof(m_class), "") m_class)
+#define memnew_placement(m_placement, m_class) _post_initialize(new (m_placement) m_class)
_ALWAYS_INLINE_ bool predelete_handler(void *) {
return true;
@@ -140,7 +133,7 @@ void memdelete_allocator(T *p_class) {
#define memnew_arr(m_class, m_count) memnew_arr_template<m_class>(m_count)
template <typename T>
-T *memnew_arr_template(size_t p_elements, const char *p_descr = "") {
+T *memnew_arr_template(size_t p_elements) {
if (p_elements == 0) {
return nullptr;
}
@@ -158,7 +151,7 @@ T *memnew_arr_template(size_t p_elements, const char *p_descr = "") {
/* call operator new */
for (size_t i = 0; i < p_elements; i++) {
- new (&elems[i], sizeof(T), p_descr) T;
+ new (&elems[i]) T;
}
}
diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp
index e9919aeb86..ee33eef83f 100644
--- a/core/os/midi_driver.cpp
+++ b/core/os/midi_driver.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -45,73 +45,75 @@ void MIDIDriver::set_singleton() {
void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length) {
Ref<InputEventMIDI> event;
- event.instance();
+ event.instantiate();
uint32_t param_position = 1;
if (length >= 1) {
if (data[0] >= 0xF0) {
// channel does not apply to system common messages
event->set_channel(0);
- event->set_message(data[0]);
+ event->set_message(MIDIMessage(data[0]));
last_received_message = data[0];
} else if ((data[0] & 0x80) == 0x00) {
// running status
event->set_channel(last_received_message & 0xF);
- event->set_message(last_received_message >> 4);
+ event->set_message(MIDIMessage(last_received_message >> 4));
param_position = 0;
} else {
event->set_channel(data[0] & 0xF);
- event->set_message(data[0] >> 4);
+ event->set_message(MIDIMessage(data[0] >> 4));
param_position = 1;
last_received_message = data[0];
}
}
switch (event->get_message()) {
- case MIDI_MESSAGE_AFTERTOUCH:
+ case MIDIMessage::AFTERTOUCH:
if (length >= 2 + param_position) {
event->set_pitch(data[param_position]);
event->set_pressure(data[param_position + 1]);
}
break;
- case MIDI_MESSAGE_CONTROL_CHANGE:
+ case MIDIMessage::CONTROL_CHANGE:
if (length >= 2 + param_position) {
event->set_controller_number(data[param_position]);
event->set_controller_value(data[param_position + 1]);
}
break;
- case MIDI_MESSAGE_NOTE_ON:
- case MIDI_MESSAGE_NOTE_OFF:
+ case MIDIMessage::NOTE_ON:
+ case MIDIMessage::NOTE_OFF:
if (length >= 2 + param_position) {
event->set_pitch(data[param_position]);
event->set_velocity(data[param_position + 1]);
- if (event->get_message() == MIDI_MESSAGE_NOTE_ON && event->get_velocity() == 0) {
+ if (event->get_message() == MIDIMessage::NOTE_ON && event->get_velocity() == 0) {
// https://www.midi.org/forum/228-writing-midi-software-send-note-off,-or-zero-velocity-note-on
- event->set_message(MIDI_MESSAGE_NOTE_OFF);
+ event->set_message(MIDIMessage::NOTE_OFF);
}
}
break;
- case MIDI_MESSAGE_PITCH_BEND:
+ case MIDIMessage::PITCH_BEND:
if (length >= 2 + param_position) {
event->set_pitch((data[param_position + 1] << 7) | data[param_position]);
}
break;
- case MIDI_MESSAGE_PROGRAM_CHANGE:
+ case MIDIMessage::PROGRAM_CHANGE:
if (length >= 1 + param_position) {
event->set_instrument(data[param_position]);
}
break;
- case MIDI_MESSAGE_CHANNEL_PRESSURE:
+ case MIDIMessage::CHANNEL_PRESSURE:
if (length >= 1 + param_position) {
event->set_pressure(data[param_position]);
}
break;
+ default:
+ break;
}
Input *id = Input::get_singleton();
diff --git a/core/os/midi_driver.h b/core/os/midi_driver.h
index bc922e1fcf..ccf624e07e 100644
--- a/core/os/midi_driver.h
+++ b/core/os/midi_driver.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,7 +32,7 @@
#define MIDI_DRIVER_H
#include "core/typedefs.h"
-#include "core/variant.h"
+#include "core/variant/variant.h"
/**
* Multi-Platform abstraction for accessing to MIDI.
diff --git a/core/os/mutex.cpp b/core/os/mutex.cpp
index 31a0dc2bfa..b7d7752d35 100644
--- a/core/os/mutex.cpp
+++ b/core/os/mutex.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/core/os/mutex.h b/core/os/mutex.h
index d42cbed821..d77ec362a1 100644
--- a/core/os/mutex.h
+++ b/core/os/mutex.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,7 +31,7 @@
#ifndef MUTEX_H
#define MUTEX_H
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "core/typedefs.h"
#if !defined(NO_THREADS)
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 3a398316bd..03e251880f 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -30,13 +30,12 @@
#include "os.h"
+#include "core/config/project_settings.h"
#include "core/input/input.h"
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
+#include "core/io/dir_access.h"
+#include "core/io/file_access.h"
#include "core/os/midi_driver.h"
-#include "core/project_settings.h"
#include "core/version_generated.gen.h"
-#include "servers/audio_server.h"
#include <stdarg.h>
@@ -47,37 +46,8 @@ OS *OS::get_singleton() {
return singleton;
}
-uint32_t OS::get_ticks_msec() const {
- return get_ticks_usec() / 1000;
-}
-
-String OS::get_iso_date_time(bool local) const {
- OS::Date date = get_date(local);
- OS::Time time = get_time(local);
-
- String timezone;
- if (!local) {
- TimeZoneInfo zone = get_time_zone_info();
- if (zone.bias >= 0) {
- timezone = "+";
- }
- timezone = timezone + itos(zone.bias / 60).pad_zeros(2) + itos(zone.bias % 60).pad_zeros(2);
- } else {
- timezone = "Z";
- }
-
- return itos(date.year).pad_zeros(2) +
- "-" +
- itos(date.month).pad_zeros(2) +
- "-" +
- itos(date.day).pad_zeros(2) +
- "T" +
- itos(time.hour).pad_zeros(2) +
- ":" +
- itos(time.min).pad_zeros(2) +
- ":" +
- itos(time.sec).pad_zeros(2) +
- timezone;
+uint64_t OS::get_ticks_msec() const {
+ return get_ticks_usec() / 1000ULL;
}
double OS::get_unix_time() const {
@@ -105,11 +75,19 @@ void OS::add_logger(Logger *p_logger) {
}
}
-void OS::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type) {
- _logger->log_error(p_function, p_file, p_line, p_code, p_rationale, p_type);
+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) {
+ if (!_stderr_enabled) {
+ return;
+ }
+
+ _logger->log_error(p_function, p_file, p_line, p_code, p_rationale, p_editor_notify, p_type);
}
void OS::print(const char *p_format, ...) {
+ if (!_stdout_enabled) {
+ return;
+ }
+
va_list argp;
va_start(argp, p_format);
@@ -119,6 +97,10 @@ void OS::print(const char *p_format, ...) {
}
void OS::printerr(const char *p_format, ...) {
+ if (!_stderr_enabled) {
+ return;
+ }
+
va_list argp;
va_start(argp, p_format);
@@ -127,6 +109,10 @@ void OS::printerr(const char *p_format, ...) {
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;
}
@@ -159,10 +145,30 @@ bool OS::is_stdout_verbose() const {
return _verbose_stdout;
}
+bool OS::is_single_window() const {
+ return _single_window;
+}
+
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;
+}
+
void OS::dump_memory_to_file(const char *p_file) {
//Memory::dump_static_mem_to_file(p_file);
}
@@ -175,7 +181,7 @@ static void _OS_printres(Object *p_obj) {
return;
}
- String str = itos(res->get_instance_id()) + String(res->get_class()) + ":" + String(res->get_name()) + " - " + res->get_path();
+ String str = vformat("%s - %s - %s", res->to_string(), res->get_name(), res->get_path());
if (_OSPRF) {
_OSPRF->store_line(str);
} else {
@@ -212,14 +218,6 @@ void OS::dump_resources_to_file(const char *p_file) {
ResourceCache::dump(p_file);
}
-void OS::set_no_window_mode(bool p_enable) {
- _no_window = p_enable;
-}
-
-bool OS::is_no_window_mode_enabled() const {
- return _no_window;
-}
-
int OS::get_exit_code() const {
return _exit_code;
}
@@ -232,6 +230,12 @@ 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).replace("_", "");
+}
+
// 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_dir_separator) const {
Vector<String> invalid_chars = String(": * ? \" < > |").split(" ");
@@ -277,6 +281,11 @@ 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 {
return ".";
@@ -288,7 +297,7 @@ String OS::get_resource_dir() const {
}
// Access system-specific dirs like Documents, Downloads, etc.
-String OS::get_system_dir(SystemDir p_dir) const {
+String OS::get_system_dir(SystemDir p_dir, bool p_shared_storage) const {
return ".";
}
@@ -362,9 +371,17 @@ void OS::set_has_server_feature_callback(HasServerFeatureCallback p_callback) {
}
bool OS::has_feature(const String &p_feature) {
- if (p_feature == get_name()) {
+ // Feature tags are always lowercase for consistency.
+ if (p_feature == get_name().to_lower()) {
+ return true;
+ }
+
+ // Catch-all `linuxbsd` feature tag that matches on both Linux and BSD.
+ // This is the one exposed in the project settings dialog.
+ if (p_feature == "linuxbsd" && (get_name() == "Linux" || get_name() == "FreeBSD" || get_name() == "NetBSD" || get_name() == "OpenBSD" || get_name() == "BSD")) {
return true;
}
+
#ifdef DEBUG_ENABLED
if (p_feature == "debug") {
return true;
@@ -414,6 +431,24 @@ bool OS::has_feature(const String &p_feature) {
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;
+ }
#endif
if (_check_internal_feature_support(p_feature)) {
@@ -505,12 +540,8 @@ void OS::add_frame_delay(bool p_can_draw) {
}
OS::OS() {
- void *volatile stack_bottom;
-
singleton = this;
- _stack_bottom = (void *)(&stack_bottom);
-
Vector<Logger *> loggers;
loggers.push_back(memnew(StdLogger));
_set_logger(memnew(CompositeLogger(loggers)));
diff --git a/core/os/os.h b/core/os/os.h
index 4c1d930107..52bf731501 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,15 +31,16 @@
#ifndef OS_H
#define OS_H
-#include "core/engine.h"
-#include "core/image.h"
+#include "core/config/engine.h"
+#include "core/io/image.h"
#include "core/io/logger.h"
-#include "core/list.h"
#include "core/os/main_loop.h"
-#include "core/ustring.h"
-#include "core/vector.h"
+#include "core/string/ustring.h"
+#include "core/templates/list.h"
+#include "core/templates/vector.h"
#include <stdarg.h>
+#include <stdlib.h>
class OS {
static OS *singleton;
@@ -51,24 +52,27 @@ class OS {
int low_processor_usage_mode_sleep_usec = 10000;
bool _verbose_stdout = false;
bool _debug_stdout = false;
+ bool _single_window = false;
String _local_clipboard;
- bool _no_window = false;
- int _exit_code = 0;
+ int _exit_code = EXIT_FAILURE; // unexpected exit is marked as failure
int _orientation;
bool _allow_hidpi = false;
bool _allow_layered = false;
- bool _use_vsync;
- bool _vsync_via_compositor;
+ bool _stdout_enabled = true;
+ bool _stderr_enabled = true;
char *last_error;
- void *_stack_bottom;
-
CompositeLogger *_logger = nullptr;
bool restart_on_exit = false;
List<String> restart_commandline;
+ // for the user interface we keep a record of the current display driver
+ // so we can retrieve the rendering drivers available
+ int _display_driver_id = -1;
+ String _current_rendering_driver_name = "";
+
protected:
void _set_logger(CompositeLogger *p_logger);
@@ -77,12 +81,16 @@ public:
typedef bool (*HasServerFeatureCallback)(const String &p_feature);
enum RenderThreadMode {
-
RENDER_THREAD_UNSAFE,
RENDER_THREAD_SAFE,
RENDER_SEPARATE_THREAD
};
+ enum RenderMainThreadMode {
+ RENDER_MAIN_THREAD_ONLY,
+ RENDER_ANY_THREAD,
+ };
+
protected:
friend class Main;
// Needed by tests to setup command-line args.
@@ -90,6 +98,7 @@ protected:
HasServerFeatureCallback has_server_feature_callback = nullptr;
RenderThreadMode _render_thread_mode = RENDER_THREAD_SAFE;
+ RenderMainThreadMode _render_main_thread_mode = RENDER_ANY_THREAD;
// Functions used by Main to initialize/deinitialize the OS.
void add_logger(Logger *p_logger);
@@ -97,6 +106,9 @@ protected:
virtual void initialize() = 0;
virtual void initialize_joypads() = 0;
+ void set_current_rendering_driver_name(String p_driver_name) { _current_rendering_driver_name = p_driver_name; }
+ void set_display_driver_id(int p_display_driver_id) { _display_driver_id = p_display_driver_id; }
+
virtual void set_main_loop(MainLoop *p_main_loop) = 0;
virtual void delete_main_loop() = 0;
@@ -112,7 +124,10 @@ public:
static OS *get_singleton();
- void print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, Logger::ErrorType p_type = Logger::ERR_ERROR);
+ String get_current_rendering_driver_name() const { return _current_rendering_driver_name; }
+ int get_display_driver_id() const { return _display_driver_id; }
+
+ 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);
void print(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
void printerr(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
@@ -122,6 +137,8 @@ public:
virtual void open_midi_inputs();
virtual void close_midi_inputs();
+ virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
+
virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false) { 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; }
@@ -132,7 +149,9 @@ public:
virtual int get_low_processor_usage_mode_sleep_usec() const;
virtual String get_executable_path() const;
- virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking = true, ProcessID *r_child_id = nullptr, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr) = 0;
+ 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) = 0;
+ virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr) = 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 kill(const ProcessID &p_pid) = 0;
virtual int get_process_id() const;
virtual void vibrate_handheld(int p_duration_ms = 500);
@@ -151,28 +170,23 @@ public:
bool is_layered_allowed() const { return _allow_layered; }
bool is_hidpi_allowed() const { return _allow_hidpi; }
- virtual int get_tablet_driver_count() const { return 0; };
- virtual String get_tablet_driver_name(int p_driver) const { return ""; };
- virtual String get_current_tablet_driver() const { return ""; };
- virtual void set_current_tablet_driver(const String &p_driver){};
-
void ensure_user_data_dir();
virtual MainLoop *get_main_loop() const = 0;
virtual void yield();
- enum Weekday {
- DAY_SUNDAY,
- DAY_MONDAY,
- DAY_TUESDAY,
- DAY_WEDNESDAY,
- DAY_THURSDAY,
- DAY_FRIDAY,
- DAY_SATURDAY
+ enum Weekday : uint8_t {
+ WEEKDAY_SUNDAY,
+ WEEKDAY_MONDAY,
+ WEEKDAY_TUESDAY,
+ WEEKDAY_WEDNESDAY,
+ WEEKDAY_THURSDAY,
+ WEEKDAY_FRIDAY,
+ WEEKDAY_SATURDAY,
};
- enum Month {
+ enum Month : uint8_t {
/// 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,
@@ -186,21 +200,21 @@ public:
MONTH_SEPTEMBER,
MONTH_OCTOBER,
MONTH_NOVEMBER,
- MONTH_DECEMBER
+ MONTH_DECEMBER,
};
struct Date {
- int year;
+ int64_t year;
Month month;
- int day;
+ uint8_t day;
Weekday weekday;
bool dst;
};
struct Time {
- int hour;
- int min;
- int sec;
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
};
struct TimeZoneInfo {
@@ -208,23 +222,29 @@ public:
String name;
};
- virtual Date get_date(bool local = false) const = 0;
- virtual Time get_time(bool local = false) const = 0;
+ virtual Date get_date(bool p_utc = false) const = 0;
+ virtual Time get_time(bool p_utc = false) const = 0;
virtual TimeZoneInfo get_time_zone_info() const = 0;
- virtual String get_iso_date_time(bool local = false) const;
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);
virtual uint64_t get_ticks_usec() const = 0;
- uint32_t get_ticks_msec() const;
+ 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);
+
+ bool is_single_window() const;
+
virtual void disable_crash_handler() {}
virtual bool is_disable_crash_handler() const { return false; }
virtual void initialize_debugging() {}
@@ -239,8 +259,11 @@ public:
virtual uint64_t get_free_static_memory() const;
RenderThreadMode get_render_thread_mode() const { return _render_thread_mode; }
+ RenderMainThreadMode get_render_main_thread_mode() const { return _render_main_thread_mode; }
+ void set_render_main_thread_mode(RenderMainThreadMode p_thread_mode) { _render_main_thread_mode = p_thread_mode; }
virtual String get_locale() const;
+ String get_locale_language() const;
String get_safe_dir_name(const String &p_dir_name, bool p_allow_dir_separator = false) const;
virtual String get_godot_dir_name() const;
@@ -249,6 +272,7 @@ public:
virtual String get_config_path() const;
virtual String get_cache_path() const;
virtual String get_bundle_resource_dir() const;
+ virtual String get_bundle_icon_path() const;
virtual String get_user_data_dir() const;
virtual String get_resource_dir() const;
@@ -264,13 +288,10 @@ public:
SYSTEM_DIR_RINGTONES,
};
- virtual String get_system_dir(SystemDir p_dir) const;
+ 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; }
- virtual void set_no_window_mode(bool p_enable);
- virtual bool is_no_window_mode_enabled() const;
-
virtual void debug_break();
virtual int get_exit_code() const;
diff --git a/core/os/pool_allocator.cpp b/core/os/pool_allocator.cpp
new file mode 100644
index 0000000000..74e9c24e04
--- /dev/null
+++ b/core/os/pool_allocator.cpp
@@ -0,0 +1,595 @@
+/*************************************************************************/
+/* pool_allocator.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 "pool_allocator.h"
+
+#include "core/error/error_macros.h"
+#include "core/os/memory.h"
+#include "core/os/os.h"
+#include "core/string/print_string.h"
+
+#include <assert.h>
+
+#define COMPACT_CHUNK(m_entry, m_to_pos) \
+ do { \
+ void *_dst = &((unsigned char *)pool)[m_to_pos]; \
+ void *_src = &((unsigned char *)pool)[(m_entry).pos]; \
+ memmove(_dst, _src, aligned((m_entry).len)); \
+ (m_entry).pos = m_to_pos; \
+ } while (0);
+
+void PoolAllocator::mt_lock() const {
+}
+
+void PoolAllocator::mt_unlock() const {
+}
+
+bool PoolAllocator::get_free_entry(EntryArrayPos *p_pos) {
+ if (entry_count == entry_max) {
+ return false;
+ }
+
+ for (int i = 0; i < entry_max; i++) {
+ if (entry_array[i].len == 0) {
+ *p_pos = i;
+ return true;
+ }
+ }
+
+ ERR_PRINT("Out of memory Chunks!");
+
+ return false; //
+}
+
+/**
+ * Find a hole
+ * @param p_pos The hole is behind the block pointed by this variable upon return. if pos==entry_count, then allocate at end
+ * @param p_for_size hole size
+ * @return false if hole found, true if no hole found
+ */
+bool PoolAllocator::find_hole(EntryArrayPos *p_pos, int p_for_size) {
+ /* position where previous entry ends. Defaults to zero (begin of pool) */
+
+ int prev_entry_end_pos = 0;
+
+ for (int i = 0; i < entry_count; i++) {
+ Entry &entry = entry_array[entry_indices[i]];
+
+ /* determine hole size to previous entry */
+
+ int hole_size = entry.pos - prev_entry_end_pos;
+
+ /* determine if what we want fits in that hole */
+ if (hole_size >= p_for_size) {
+ *p_pos = i;
+ return true;
+ }
+
+ /* prepare for next one */
+ prev_entry_end_pos = entry_end(entry);
+ }
+
+ /* No holes between entries, check at the end..*/
+
+ if ((pool_size - prev_entry_end_pos) >= p_for_size) {
+ *p_pos = entry_count;
+ return true;
+ }
+
+ return false;
+}
+
+void PoolAllocator::compact(int p_up_to) {
+ uint32_t prev_entry_end_pos = 0;
+
+ if (p_up_to < 0) {
+ p_up_to = entry_count;
+ }
+ for (int i = 0; i < p_up_to; i++) {
+ Entry &entry = entry_array[entry_indices[i]];
+
+ /* determine hole size to previous entry */
+
+ int hole_size = entry.pos - prev_entry_end_pos;
+
+ /* if we can compact, do it */
+ if (hole_size > 0 && !entry.lock) {
+ COMPACT_CHUNK(entry, prev_entry_end_pos);
+ }
+
+ /* prepare for next one */
+ prev_entry_end_pos = entry_end(entry);
+ }
+}
+
+void PoolAllocator::compact_up(int p_from) {
+ uint32_t next_entry_end_pos = pool_size; // - static_area_size;
+
+ for (int i = entry_count - 1; i >= p_from; i--) {
+ Entry &entry = entry_array[entry_indices[i]];
+
+ /* determine hole size for next entry */
+
+ int hole_size = next_entry_end_pos - (entry.pos + aligned(entry.len));
+
+ /* if we can compact, do it */
+ if (hole_size > 0 && !entry.lock) {
+ COMPACT_CHUNK(entry, (next_entry_end_pos - aligned(entry.len)));
+ }
+
+ /* prepare for next one */
+ next_entry_end_pos = entry.pos;
+ }
+}
+
+bool PoolAllocator::find_entry_index(EntryIndicesPos *p_map_pos, Entry *p_entry) {
+ EntryArrayPos entry_pos = entry_max;
+
+ for (int i = 0; i < entry_count; i++) {
+ if (&entry_array[entry_indices[i]] == p_entry) {
+ entry_pos = i;
+ break;
+ }
+ }
+
+ if (entry_pos == entry_max) {
+ return false;
+ }
+
+ *p_map_pos = entry_pos;
+ return true;
+}
+
+PoolAllocator::ID PoolAllocator::alloc(int p_size) {
+ ERR_FAIL_COND_V(p_size < 1, POOL_ALLOCATOR_INVALID_ID);
+#ifdef DEBUG_ENABLED
+ if (p_size > free_mem) {
+ OS::get_singleton()->debug_break();
+ }
+#endif
+ ERR_FAIL_COND_V(p_size > free_mem, POOL_ALLOCATOR_INVALID_ID);
+
+ mt_lock();
+
+ if (entry_count == entry_max) {
+ mt_unlock();
+ ERR_PRINT("entry_count==entry_max");
+ return POOL_ALLOCATOR_INVALID_ID;
+ }
+
+ int size_to_alloc = aligned(p_size);
+
+ EntryIndicesPos new_entry_indices_pos;
+
+ if (!find_hole(&new_entry_indices_pos, size_to_alloc)) {
+ /* No hole could be found, try compacting mem */
+ compact();
+ /* Then search again */
+
+ if (!find_hole(&new_entry_indices_pos, size_to_alloc)) {
+ mt_unlock();
+ ERR_FAIL_V_MSG(POOL_ALLOCATOR_INVALID_ID, "Memory can't be compacted further.");
+ }
+ }
+
+ EntryArrayPos new_entry_array_pos;
+
+ bool found_free_entry = get_free_entry(&new_entry_array_pos);
+
+ if (!found_free_entry) {
+ mt_unlock();
+ ERR_FAIL_V_MSG(POOL_ALLOCATOR_INVALID_ID, "No free entry found in PoolAllocator.");
+ }
+
+ /* move all entry indices up, make room for this one */
+ for (int i = entry_count; i > new_entry_indices_pos; i--) {
+ entry_indices[i] = entry_indices[i - 1];
+ }
+
+ entry_indices[new_entry_indices_pos] = new_entry_array_pos;
+
+ entry_count++;
+
+ Entry &entry = entry_array[entry_indices[new_entry_indices_pos]];
+
+ entry.len = p_size;
+ entry.pos = (new_entry_indices_pos == 0) ? 0 : entry_end(entry_array[entry_indices[new_entry_indices_pos - 1]]); //alloc either at beginning or end of previous
+ entry.lock = 0;
+ entry.check = (check_count++) & CHECK_MASK;
+ free_mem -= size_to_alloc;
+ if (free_mem < free_mem_peak) {
+ free_mem_peak = free_mem;
+ }
+
+ ID retval = (entry_indices[new_entry_indices_pos] << CHECK_BITS) | entry.check;
+ mt_unlock();
+
+ //ERR_FAIL_COND_V( (uintptr_t)get(retval)%align != 0, retval );
+
+ return retval;
+}
+
+PoolAllocator::Entry *PoolAllocator::get_entry(ID p_mem) {
+ unsigned int check = p_mem & CHECK_MASK;
+ int entry = p_mem >> CHECK_BITS;
+ ERR_FAIL_INDEX_V(entry, entry_max, nullptr);
+ ERR_FAIL_COND_V(entry_array[entry].check != check, nullptr);
+ ERR_FAIL_COND_V(entry_array[entry].len == 0, nullptr);
+
+ return &entry_array[entry];
+}
+
+const PoolAllocator::Entry *PoolAllocator::get_entry(ID p_mem) const {
+ unsigned int check = p_mem & CHECK_MASK;
+ int entry = p_mem >> CHECK_BITS;
+ ERR_FAIL_INDEX_V(entry, entry_max, nullptr);
+ ERR_FAIL_COND_V(entry_array[entry].check != check, nullptr);
+ ERR_FAIL_COND_V(entry_array[entry].len == 0, nullptr);
+
+ return &entry_array[entry];
+}
+
+void PoolAllocator::free(ID p_mem) {
+ mt_lock();
+ Entry *e = get_entry(p_mem);
+ if (!e) {
+ mt_unlock();
+ ERR_PRINT("!e");
+ return;
+ }
+ if (e->lock) {
+ mt_unlock();
+ ERR_PRINT("e->lock");
+ return;
+ }
+
+ EntryIndicesPos entry_indices_pos;
+
+ bool index_found = find_entry_index(&entry_indices_pos, e);
+ if (!index_found) {
+ mt_unlock();
+ ERR_FAIL_COND(!index_found);
+ }
+
+ for (int i = entry_indices_pos; i < (entry_count - 1); i++) {
+ entry_indices[i] = entry_indices[i + 1];
+ }
+
+ entry_count--;
+ free_mem += aligned(e->len);
+ e->clear();
+ mt_unlock();
+}
+
+int PoolAllocator::get_size(ID p_mem) const {
+ int size;
+ mt_lock();
+
+ const Entry *e = get_entry(p_mem);
+ if (!e) {
+ mt_unlock();
+ ERR_PRINT("!e");
+ return 0;
+ }
+
+ size = e->len;
+
+ mt_unlock();
+
+ return size;
+}
+
+Error PoolAllocator::resize(ID p_mem, int p_new_size) {
+ mt_lock();
+ Entry *e = get_entry(p_mem);
+
+ if (!e) {
+ mt_unlock();
+ ERR_FAIL_COND_V(!e, ERR_INVALID_PARAMETER);
+ }
+
+ if (needs_locking && e->lock) {
+ mt_unlock();
+ ERR_FAIL_COND_V(e->lock, ERR_ALREADY_IN_USE);
+ }
+
+ uint32_t alloc_size = aligned(p_new_size);
+
+ if ((uint32_t)aligned(e->len) == alloc_size) {
+ e->len = p_new_size;
+ mt_unlock();
+ return OK;
+ } else if (e->len > (uint32_t)p_new_size) {
+ free_mem += aligned(e->len);
+ free_mem -= alloc_size;
+ e->len = p_new_size;
+ mt_unlock();
+ return OK;
+ }
+
+ //p_new_size = align(p_new_size)
+ int _free = free_mem; // - static_area_size;
+
+ if (uint32_t(_free + aligned(e->len)) < alloc_size) {
+ mt_unlock();
+ ERR_FAIL_V(ERR_OUT_OF_MEMORY);
+ }
+
+ EntryIndicesPos entry_indices_pos;
+
+ bool index_found = find_entry_index(&entry_indices_pos, e);
+
+ if (!index_found) {
+ mt_unlock();
+ ERR_FAIL_COND_V(!index_found, ERR_BUG);
+ }
+
+ //no need to move stuff around, it fits before the next block
+ uint32_t next_pos;
+ if (entry_indices_pos + 1 == entry_count) {
+ next_pos = pool_size; // - static_area_size;
+ } else {
+ next_pos = entry_array[entry_indices[entry_indices_pos + 1]].pos;
+ }
+
+ if ((next_pos - e->pos) > alloc_size) {
+ free_mem += aligned(e->len);
+ e->len = p_new_size;
+ free_mem -= alloc_size;
+ mt_unlock();
+ return OK;
+ }
+ //it doesn't fit, compact around BEFORE current index (make room behind)
+
+ compact(entry_indices_pos + 1);
+
+ if ((next_pos - e->pos) > alloc_size) {
+ //now fits! hooray!
+ free_mem += aligned(e->len);
+ e->len = p_new_size;
+ free_mem -= alloc_size;
+ mt_unlock();
+ if (free_mem < free_mem_peak) {
+ free_mem_peak = free_mem;
+ }
+ return OK;
+ }
+
+ //STILL doesn't fit, compact around AFTER current index (make room after)
+
+ compact_up(entry_indices_pos + 1);
+
+ if ((entry_array[entry_indices[entry_indices_pos + 1]].pos - e->pos) > alloc_size) {
+ //now fits! hooray!
+ free_mem += aligned(e->len);
+ e->len = p_new_size;
+ free_mem -= alloc_size;
+ mt_unlock();
+ if (free_mem < free_mem_peak) {
+ free_mem_peak = free_mem;
+ }
+ return OK;
+ }
+
+ mt_unlock();
+ ERR_FAIL_V(ERR_OUT_OF_MEMORY);
+}
+
+Error PoolAllocator::lock(ID p_mem) {
+ if (!needs_locking) {
+ return OK;
+ }
+ mt_lock();
+ Entry *e = get_entry(p_mem);
+ if (!e) {
+ mt_unlock();
+ ERR_PRINT("!e");
+ return ERR_INVALID_PARAMETER;
+ }
+ e->lock++;
+ mt_unlock();
+ return OK;
+}
+
+bool PoolAllocator::is_locked(ID p_mem) const {
+ if (!needs_locking) {
+ return false;
+ }
+
+ mt_lock();
+ const Entry *e = ((PoolAllocator *)(this))->get_entry(p_mem);
+ if (!e) {
+ mt_unlock();
+ ERR_PRINT("!e");
+ return false;
+ }
+ bool locked = e->lock;
+ mt_unlock();
+ return locked;
+}
+
+const void *PoolAllocator::get(ID p_mem) const {
+ if (!needs_locking) {
+ const Entry *e = get_entry(p_mem);
+ ERR_FAIL_COND_V(!e, nullptr);
+ return &pool[e->pos];
+ }
+
+ mt_lock();
+ const Entry *e = get_entry(p_mem);
+
+ if (!e) {
+ mt_unlock();
+ ERR_FAIL_COND_V(!e, nullptr);
+ }
+ if (e->lock == 0) {
+ mt_unlock();
+ ERR_PRINT("e->lock == 0");
+ return nullptr;
+ }
+
+ if ((int)e->pos >= pool_size) {
+ mt_unlock();
+ ERR_PRINT("e->pos<0 || e->pos>=pool_size");
+ return nullptr;
+ }
+ const void *ptr = &pool[e->pos];
+
+ mt_unlock();
+
+ return ptr;
+}
+
+void *PoolAllocator::get(ID p_mem) {
+ if (!needs_locking) {
+ Entry *e = get_entry(p_mem);
+ ERR_FAIL_COND_V(!e, nullptr);
+ return &pool[e->pos];
+ }
+
+ mt_lock();
+ Entry *e = get_entry(p_mem);
+
+ if (!e) {
+ mt_unlock();
+ ERR_FAIL_COND_V(!e, nullptr);
+ }
+ if (e->lock == 0) {
+ //assert(0);
+ mt_unlock();
+ ERR_PRINT("e->lock == 0");
+ return nullptr;
+ }
+
+ if ((int)e->pos >= pool_size) {
+ mt_unlock();
+ ERR_PRINT("e->pos<0 || e->pos>=pool_size");
+ return nullptr;
+ }
+ void *ptr = &pool[e->pos];
+
+ mt_unlock();
+
+ return ptr;
+}
+
+void PoolAllocator::unlock(ID p_mem) {
+ if (!needs_locking) {
+ return;
+ }
+ mt_lock();
+ Entry *e = get_entry(p_mem);
+ if (!e) {
+ mt_unlock();
+ ERR_FAIL_COND(!e);
+ }
+ if (e->lock == 0) {
+ mt_unlock();
+ ERR_PRINT("e->lock == 0");
+ return;
+ }
+ e->lock--;
+ mt_unlock();
+}
+
+int PoolAllocator::get_used_mem() const {
+ return pool_size - free_mem;
+}
+
+int PoolAllocator::get_free_peak() {
+ return free_mem_peak;
+}
+
+int PoolAllocator::get_free_mem() {
+ return free_mem;
+}
+
+void PoolAllocator::create_pool(void *p_mem, int p_size, int p_max_entries) {
+ pool = (uint8_t *)p_mem;
+ pool_size = p_size;
+
+ entry_array = memnew_arr(Entry, p_max_entries);
+ entry_indices = memnew_arr(int, p_max_entries);
+ entry_max = p_max_entries;
+ entry_count = 0;
+
+ free_mem = p_size;
+ free_mem_peak = p_size;
+
+ check_count = 0;
+}
+
+PoolAllocator::PoolAllocator(int p_size, bool p_needs_locking, int p_max_entries) {
+ mem_ptr = memalloc(p_size);
+ ERR_FAIL_COND(!mem_ptr);
+ align = 1;
+ create_pool(mem_ptr, p_size, p_max_entries);
+ needs_locking = p_needs_locking;
+}
+
+PoolAllocator::PoolAllocator(void *p_mem, int p_size, int p_align, bool p_needs_locking, int p_max_entries) {
+ if (p_align > 1) {
+ uint8_t *mem8 = (uint8_t *)p_mem;
+ uint64_t ofs = (uint64_t)mem8;
+ if (ofs % p_align) {
+ int dif = p_align - (ofs % p_align);
+ mem8 += p_align - (ofs % p_align);
+ p_size -= dif;
+ p_mem = (void *)mem8;
+ }
+ }
+
+ create_pool(p_mem, p_size, p_max_entries);
+ needs_locking = p_needs_locking;
+ align = p_align;
+ mem_ptr = nullptr;
+}
+
+PoolAllocator::PoolAllocator(int p_align, int p_size, bool p_needs_locking, int p_max_entries) {
+ ERR_FAIL_COND(p_align < 1);
+ mem_ptr = Memory::alloc_static(p_size + p_align, true);
+ uint8_t *mem8 = (uint8_t *)mem_ptr;
+ uint64_t ofs = (uint64_t)mem8;
+ if (ofs % p_align) {
+ mem8 += p_align - (ofs % p_align);
+ }
+ create_pool(mem8, p_size, p_max_entries);
+ needs_locking = p_needs_locking;
+ align = p_align;
+}
+
+PoolAllocator::~PoolAllocator() {
+ if (mem_ptr) {
+ memfree(mem_ptr);
+ }
+
+ memdelete_arr(entry_array);
+ memdelete_arr(entry_indices);
+}
diff --git a/core/os/pool_allocator.h b/core/os/pool_allocator.h
new file mode 100644
index 0000000000..49f433ba97
--- /dev/null
+++ b/core/os/pool_allocator.h
@@ -0,0 +1,149 @@
+/*************************************************************************/
+/* pool_allocator.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 POOL_ALLOCATOR_H
+#define POOL_ALLOCATOR_H
+
+#include "core/typedefs.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+ * Generic Pool Allocator.
+ * This is a generic memory pool allocator, with locking, compacting and alignment. (@TODO alignment)
+ * It used as a standard way to manage allocation in a specific region of memory, such as texture memory,
+ * audio sample memory, or just any kind of memory overall.
+ * (@TODO) abstraction should be greater, because in many platforms, you need to manage a nonreachable memory.
+*/
+
+enum {
+ POOL_ALLOCATOR_INVALID_ID = -1 ///< default invalid value. use INVALID_ID( id ) to test
+};
+
+class PoolAllocator {
+public:
+ typedef int ID;
+
+private:
+ enum {
+ CHECK_BITS = 8,
+ CHECK_LEN = (1 << CHECK_BITS),
+ CHECK_MASK = CHECK_LEN - 1
+
+ };
+
+ struct Entry {
+ unsigned int pos = 0;
+ unsigned int len = 0;
+ unsigned int lock = 0;
+ unsigned int check = 0;
+
+ inline void clear() {
+ pos = 0;
+ len = 0;
+ lock = 0;
+ check = 0;
+ }
+ Entry() {}
+ };
+
+ typedef int EntryArrayPos;
+ typedef int EntryIndicesPos;
+
+ Entry *entry_array;
+ int *entry_indices;
+ int entry_max;
+ int entry_count;
+
+ uint8_t *pool;
+ void *mem_ptr;
+ int pool_size;
+
+ int free_mem;
+ int free_mem_peak;
+
+ unsigned int check_count;
+ int align;
+
+ bool needs_locking;
+
+ inline int entry_end(const Entry &p_entry) const {
+ return p_entry.pos + aligned(p_entry.len);
+ }
+ inline int aligned(int p_size) const {
+ int rem = p_size % align;
+ if (rem) {
+ p_size += align - rem;
+ }
+
+ return p_size;
+ }
+
+ void compact(int p_up_to = -1);
+ void compact_up(int p_from = 0);
+ bool get_free_entry(EntryArrayPos *p_pos);
+ bool find_hole(EntryArrayPos *p_pos, int p_for_size);
+ bool find_entry_index(EntryIndicesPos *p_map_pos, Entry *p_entry);
+ Entry *get_entry(ID p_mem);
+ const Entry *get_entry(ID p_mem) const;
+
+ void create_pool(void *p_mem, int p_size, int p_max_entries);
+
+protected:
+ virtual void mt_lock() const; ///< Reimplement for custom mt locking
+ virtual void mt_unlock() const; ///< Reimplement for custom mt locking
+
+public:
+ enum {
+ DEFAULT_MAX_ALLOCS = 4096,
+ };
+
+ ID alloc(int p_size); ///< Alloc memory, get an ID on success, POOL_ALOCATOR_INVALID_ID on failure
+ void free(ID p_mem); ///< Free allocated memory
+ Error resize(ID p_mem, int p_new_size); ///< resize a memory chunk
+ int get_size(ID p_mem) const;
+
+ int get_free_mem(); ///< get free memory
+ int get_used_mem() const;
+ int get_free_peak(); ///< get free memory
+
+ Error lock(ID p_mem); //@todo move this out
+ void *get(ID p_mem);
+ const void *get(ID p_mem) const;
+ void unlock(ID p_mem);
+ bool is_locked(ID p_mem) const;
+
+ PoolAllocator(int p_size, bool p_needs_locking = false, int p_max_entries = DEFAULT_MAX_ALLOCS);
+ PoolAllocator(void *p_mem, int p_size, int p_align = 1, bool p_needs_locking = false, int p_max_entries = DEFAULT_MAX_ALLOCS);
+ PoolAllocator(int p_align, int p_size, bool p_needs_locking = false, int p_max_entries = DEFAULT_MAX_ALLOCS);
+
+ virtual ~PoolAllocator();
+};
+
+#endif // POOL_ALLOCATOR_H
diff --git a/core/os/rw_lock.h b/core/os/rw_lock.h
index 1035072cce..560ec57a5f 100644
--- a/core/os/rw_lock.h
+++ b/core/os/rw_lock.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,57 +31,85 @@
#ifndef RW_LOCK_H
#define RW_LOCK_H
-#include "core/error_list.h"
+#include "core/error/error_list.h"
+
+#if !defined(NO_THREADS)
+
+#include <shared_mutex>
class RWLock {
-protected:
- static RWLock *(*create_func)();
+ mutable std::shared_timed_mutex mutex;
public:
- virtual void read_lock() = 0; ///< Lock the rwlock, block if locked by someone else
- virtual void read_unlock() = 0; ///< Unlock the rwlock, let other threads continue
- virtual Error read_try_lock() = 0; ///< Attempt to lock the rwlock, OK on success, ERROR means it can't lock.
+ // Lock the rwlock, block if locked by someone else
+ void read_lock() const {
+ mutex.lock_shared();
+ }
- virtual void write_lock() = 0; ///< Lock the rwlock, block if locked by someone else
- virtual void write_unlock() = 0; ///< Unlock the rwlock, let other thwrites continue
- virtual Error write_try_lock() = 0; ///< Attempt to lock the rwlock, OK on success, ERROR means it can't lock.
+ // Unlock the rwlock, let other threads continue
+ void read_unlock() const {
+ mutex.unlock_shared();
+ }
- static RWLock *create(); ///< Create a rwlock
+ // Attempt to lock the rwlock, OK on success, ERR_BUSY means it can't lock.
+ Error read_try_lock() const {
+ return mutex.try_lock_shared() ? OK : ERR_BUSY;
+ }
+
+ // Lock the rwlock, block if locked by someone else
+ void write_lock() {
+ mutex.lock();
+ }
+
+ // Unlock the rwlock, let other thwrites continue
+ void write_unlock() {
+ mutex.unlock();
+ }
- virtual ~RWLock() {}
+ // Attempt to lock the rwlock, OK on success, ERR_BUSY means it can't lock.
+ Error write_try_lock() {
+ return mutex.try_lock() ? OK : ERR_BUSY;
+ }
+};
+
+#else
+
+class RWLock {
+public:
+ void read_lock() const {}
+ void read_unlock() const {}
+ Error read_try_lock() const { return OK; }
+
+ void write_lock() {}
+ void write_unlock() {}
+ Error write_try_lock() { return OK; }
};
+#endif
+
class RWLockRead {
- RWLock *lock;
+ const RWLock &lock;
public:
- RWLockRead(const RWLock *p_lock) {
- lock = const_cast<RWLock *>(p_lock);
- if (lock) {
- lock->read_lock();
- }
+ RWLockRead(const RWLock &p_lock) :
+ lock(p_lock) {
+ lock.read_lock();
}
~RWLockRead() {
- if (lock) {
- lock->read_unlock();
- }
+ lock.read_unlock();
}
};
class RWLockWrite {
- RWLock *lock;
+ RWLock &lock;
public:
- RWLockWrite(RWLock *p_lock) {
- lock = p_lock;
- if (lock) {
- lock->write_lock();
- }
+ RWLockWrite(RWLock &p_lock) :
+ lock(p_lock) {
+ lock.write_lock();
}
~RWLockWrite() {
- if (lock) {
- lock->write_unlock();
- }
+ lock.write_unlock();
}
};
diff --git a/core/os/semaphore.h b/core/os/semaphore.h
index 077e04704b..01ae7a3c65 100644
--- a/core/os/semaphore.h
+++ b/core/os/semaphore.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -31,7 +31,7 @@
#ifndef SEMAPHORE_H
#define SEMAPHORE_H
-#include "core/error_list.h"
+#include "core/error/error_list.h"
#include "core/typedefs.h"
#if !defined(NO_THREADS)
diff --git a/core/os/rw_lock.cpp b/core/os/spin_lock.h
index a668fe2b4c..929e8b9a58 100644
--- a/core/os/rw_lock.cpp
+++ b/core/os/spin_lock.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* rw_lock.cpp */
+/* spin_lock.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,16 +28,24 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "rw_lock.h"
+#ifndef SPIN_LOCK_H
+#define SPIN_LOCK_H
-#include "core/error_macros.h"
+#include "core/typedefs.h"
-#include <stddef.h>
+#include <atomic>
-RWLock *(*RWLock::create_func)() = nullptr;
+class SpinLock {
+ std::atomic_flag locked = ATOMIC_FLAG_INIT;
-RWLock *RWLock::create() {
- ERR_FAIL_COND_V(!create_func, nullptr);
-
- return create_func();
-}
+public:
+ _ALWAYS_INLINE_ void lock() {
+ while (locked.test_and_set(std::memory_order_acquire)) {
+ ;
+ }
+ }
+ _ALWAYS_INLINE_ void unlock() {
+ locked.clear(std::memory_order_release);
+ }
+};
+#endif // SPIN_LOCK_H
diff --git a/core/os/thread.cpp b/core/os/thread.cpp
index fc0ce3c9b4..27aefc98de 100644
--- a/core/os/thread.cpp
+++ b/core/os/thread.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,32 +28,81 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+#ifndef PLATFORM_THREAD_OVERRIDE // See details in thread.h
+
#include "thread.h"
-Thread *(*Thread::create_func)(ThreadCreateCallback, void *, const Settings &) = nullptr;
-Thread::ID (*Thread::get_thread_id_func)() = nullptr;
-void (*Thread::wait_to_finish_func)(Thread *) = nullptr;
+#include "core/object/script_language.h"
+
+#if !defined(NO_THREADS)
+
+#include "core/templates/safe_refcount.h"
+
Error (*Thread::set_name_func)(const String &) = nullptr;
+void (*Thread::set_priority_func)(Thread::Priority) = nullptr;
+void (*Thread::init_func)() = nullptr;
+void (*Thread::term_func)() = nullptr;
-Thread::ID Thread::_main_thread_id = 0;
+uint64_t Thread::_thread_id_hash(const std::thread::id &p_t) {
+ static std::hash<std::thread::id> hasher;
+ return hasher(p_t);
+}
-Thread::ID Thread::get_caller_id() {
- if (get_thread_id_func) {
- return get_thread_id_func();
+Thread::ID Thread::main_thread_id = _thread_id_hash(std::this_thread::get_id());
+thread_local Thread::ID Thread::caller_id = 0;
+
+void Thread::_set_platform_funcs(
+ Error (*p_set_name_func)(const String &),
+ void (*p_set_priority_func)(Thread::Priority),
+ void (*p_init_func)(),
+ void (*p_term_func)()) {
+ Thread::set_name_func = p_set_name_func;
+ Thread::set_priority_func = p_set_priority_func;
+ Thread::init_func = p_init_func;
+ Thread::term_func = p_term_func;
+}
+
+void Thread::callback(Thread *p_self, const Settings &p_settings, Callback p_callback, void *p_userdata) {
+ Thread::caller_id = _thread_id_hash(p_self->thread.get_id());
+ if (set_priority_func) {
+ set_priority_func(p_settings.priority);
+ }
+ if (init_func) {
+ init_func();
+ }
+ ScriptServer::thread_enter(); //scripts may need to attach a stack
+ p_callback(p_userdata);
+ ScriptServer::thread_exit();
+ if (term_func) {
+ term_func();
}
- return 0;
}
-Thread *Thread::create(ThreadCreateCallback p_callback, void *p_user, const Settings &p_settings) {
- if (create_func) {
- return create_func(p_callback, p_user, p_settings);
+void Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_settings) {
+ if (id != _thread_id_hash(std::thread::id())) {
+#ifdef DEBUG_ENABLED
+ WARN_PRINT("A Thread object has been re-started without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread.");
+#endif
+ thread.detach();
+ std::thread empty_thread;
+ thread.swap(empty_thread);
}
- return nullptr;
+ std::thread new_thread(&Thread::callback, this, p_settings, p_callback, p_user);
+ thread.swap(new_thread);
+ id = _thread_id_hash(thread.get_id());
+}
+
+bool Thread::is_started() const {
+ return id != _thread_id_hash(std::thread::id());
}
-void Thread::wait_to_finish(Thread *p_thread) {
- if (wait_to_finish_func) {
- wait_to_finish_func(p_thread);
+void Thread::wait_to_finish() {
+ if (id != _thread_id_hash(std::thread::id())) {
+ ERR_FAIL_COND_MSG(id == get_caller_id(), "A Thread can't wait for itself to finish.");
+ thread.join();
+ std::thread empty_thread;
+ thread.swap(empty_thread);
+ id = _thread_id_hash(std::thread::id());
}
}
@@ -64,3 +113,19 @@ Error Thread::set_name(const String &p_name) {
return ERR_UNAVAILABLE;
}
+
+Thread::Thread() {
+ caller_id = _thread_id_hash(std::this_thread::get_id());
+}
+
+Thread::~Thread() {
+ if (id != _thread_id_hash(std::thread::id())) {
+#ifdef DEBUG_ENABLED
+ WARN_PRINT("A Thread object has been destroyed without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread.");
+#endif
+ thread.detach();
+ }
+}
+
+#endif
+#endif // PLATFORM_THREAD_OVERRIDE
diff --git a/core/os/thread.h b/core/os/thread.h
index f761d4ca43..59cb58ac57 100644
--- a/core/os/thread.h
+++ b/core/os/thread.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,18 +28,31 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
+// 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 platform implementation is required in some proprietary platforms
+#ifdef PLATFORM_THREAD_OVERRIDE
+#include "platform_thread.h"
+#else
#ifndef THREAD_H
#define THREAD_H
#include "core/typedefs.h"
-#include "core/ustring.h"
-typedef void (*ThreadCreateCallback)(void *p_userdata);
+#if !defined(NO_THREADS)
+#include "core/templates/safe_refcount.h"
+#include <thread>
+#endif
+
+class String;
class Thread {
public:
- enum Priority {
+ typedef void (*Callback)(void *p_userdata);
+
+ typedef uint64_t ID;
+ enum Priority {
PRIORITY_LOW,
PRIORITY_NORMAL,
PRIORITY_HIGH
@@ -50,30 +63,63 @@ public:
Settings() { priority = PRIORITY_NORMAL; }
};
- typedef uint64_t ID;
+private:
+#if !defined(NO_THREADS)
+ friend class Main;
-protected:
- static Thread *(*create_func)(ThreadCreateCallback p_callback, void *, const Settings &);
- static ID (*get_thread_id_func)();
- static void (*wait_to_finish_func)(Thread *);
- static Error (*set_name_func)(const String &);
+ static ID main_thread_id;
- friend class Main;
+ static uint64_t _thread_id_hash(const std::thread::id &p_t);
+
+ ID id = _thread_id_hash(std::thread::id());
+ static thread_local ID caller_id;
+ std::thread thread;
- static ID _main_thread_id;
+ static void callback(Thread *p_self, const Settings &p_settings, Thread::Callback p_callback, void *p_userdata);
- Thread() {}
+ static Error (*set_name_func)(const String &);
+ static void (*set_priority_func)(Thread::Priority);
+ static void (*init_func)();
+ static void (*term_func)();
+#endif
public:
- virtual ID get_id() const = 0;
+ static void _set_platform_funcs(
+ Error (*p_set_name_func)(const String &),
+ void (*p_set_priority_func)(Thread::Priority),
+ void (*p_init_func)() = nullptr,
+ void (*p_term_func)() = nullptr);
+
+#if !defined(NO_THREADS)
+ _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_thread_id; }
static Error set_name(const String &p_name);
- _FORCE_INLINE_ static ID get_main_id() { return _main_thread_id; } ///< get the ID of the main thread
- static ID get_caller_id(); ///< get the ID of the caller function ID
- static void wait_to_finish(Thread *p_thread); ///< waits until thread is finished, and deallocates it.
- static Thread *create(ThreadCreateCallback p_callback, void *p_user, const Settings &p_settings = Settings()); ///< Static function to create a thread, will call p_callback
- virtual ~Thread() {}
+ void 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
+ _FORCE_INLINE_ ID get_id() const { return 0; }
+ // get the ID of the caller thread
+ _FORCE_INLINE_ static ID get_caller_id() { return 0; }
+ // get the ID of the main thread
+ _FORCE_INLINE_ static ID get_main_id() { return 0; }
+
+ 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
};
#endif // THREAD_H
+#endif // PLATFORM_THREAD_OVERRIDE
diff --git a/core/os/thread_dummy.cpp b/core/os/thread_dummy.cpp
deleted file mode 100644
index 2672cd7ad9..0000000000
--- a/core/os/thread_dummy.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*************************************************************************/
-/* thread_dummy.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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 "thread_dummy.h"
-
-#include "core/os/memory.h"
-
-Thread *ThreadDummy::create(ThreadCreateCallback p_callback, void *p_user, const Thread::Settings &p_settings) {
- return memnew(ThreadDummy);
-}
-
-void ThreadDummy::make_default() {
- Thread::create_func = &ThreadDummy::create;
-}
-
-RWLock *RWLockDummy::create() {
- return memnew(RWLockDummy);
-}
-
-void RWLockDummy::make_default() {
- RWLock::create_func = &RWLockDummy::create;
-}
diff --git a/core/os/thread_dummy.h b/core/os/thread_dummy.h
deleted file mode 100644
index 37d9ee0846..0000000000
--- a/core/os/thread_dummy.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*************************************************************************/
-/* thread_dummy.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* 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_DUMMY_H
-#define THREAD_DUMMY_H
-
-#include "core/os/rw_lock.h"
-#include "core/os/semaphore.h"
-#include "core/os/thread.h"
-
-class ThreadDummy : public Thread {
- static Thread *create(ThreadCreateCallback p_callback, void *p_user, const Settings &p_settings = Settings());
-
-public:
- virtual ID get_id() const { return 0; };
-
- static void make_default();
-};
-
-class RWLockDummy : public RWLock {
- static RWLock *create();
-
-public:
- virtual void read_lock() {}
- virtual void read_unlock() {}
- virtual Error read_try_lock() { return OK; }
-
- virtual void write_lock() {}
- virtual void write_unlock() {}
- virtual Error write_try_lock() { return OK; }
-
- static void make_default();
-};
-
-#endif // THREAD_DUMMY_H
diff --git a/core/os/thread_safe.h b/core/os/thread_safe.h
index 670ee8b125..81de079bf3 100644
--- a/core/os/thread_safe.h
+++ b/core/os/thread_safe.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/core/os/threaded_array_processor.h b/core/os/threaded_array_processor.h
index d27399e4cc..fec6473589 100644
--- a/core/os/threaded_array_processor.h
+++ b/core/os/threaded_array_processor.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -35,12 +35,12 @@
#include "core/os/os.h"
#include "core/os/thread.h"
#include "core/os/thread_safe.h"
-#include "core/safe_refcount.h"
+#include "core/templates/safe_refcount.h"
template <class C, class U>
struct ThreadArrayProcessData {
uint32_t elements;
- uint32_t index;
+ SafeNumeric<uint32_t> index;
C *instance;
U userdata;
void (C::*method)(uint32_t, U);
@@ -56,7 +56,7 @@ template <class T>
void process_array_thread(void *ud) {
T &data = *(T *)ud;
while (true) {
- uint32_t index = atomic_increment(&data.index);
+ uint32_t index = data.index.increment();
if (index >= data.elements) {
break;
}
@@ -70,22 +70,21 @@ void thread_process_array(uint32_t p_elements, C *p_instance, M p_method, U p_us
data.method = p_method;
data.instance = p_instance;
data.userdata = p_userdata;
- data.index = 0;
+ data.index.set(0);
data.elements = p_elements;
- data.process(data.index); //process first, let threads increment for next
+ data.process(0); //process first, let threads increment for next
- Vector<Thread *> threads;
+ int thread_count = OS::get_singleton()->get_processor_count();
+ Thread *threads = memnew_arr(Thread, thread_count);
- threads.resize(OS::get_singleton()->get_processor_count());
-
- for (int i = 0; i < threads.size(); i++) {
- threads.write[i] = Thread::create(process_array_thread<ThreadArrayProcessData<C, U>>, &data);
+ for (int i = 0; i < thread_count; i++) {
+ threads[i].start(process_array_thread<ThreadArrayProcessData<C, U>>, &data);
}
- for (int i = 0; i < threads.size(); i++) {
- Thread::wait_to_finish(threads[i]);
- memdelete(threads[i]);
+ for (int i = 0; i < thread_count; i++) {
+ threads[i].wait_to_finish();
}
+ memdelete_arr(threads);
}
#else
@@ -96,7 +95,7 @@ void thread_process_array(uint32_t p_elements, C *p_instance, M p_method, U p_us
data.method = p_method;
data.instance = p_instance;
data.userdata = p_userdata;
- data.index = 0;
+ data.index.set(0);
data.elements = p_elements;
for (uint32_t i = 0; i < p_elements; i++) {
data.process(i);
diff --git a/core/os/time.cpp b/core/os/time.cpp
new file mode 100644
index 0000000000..a185029969
--- /dev/null
+++ b/core/os/time.cpp
@@ -0,0 +1,433 @@
+/*************************************************************************/
+/* time.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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"
+
+#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)
+
+#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 }
+};
+
+VARIANT_ENUM_CAST(Time::Month);
+VARIANT_ENUM_CAST(Time::Weekday);
+
+#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; \
+ year = UNIX_EPOCH_YEAR_AD; \
+ uint8_t month_zero_index = 0; \
+ while (day_number_copy >= YEAR_SIZE(year)) { \
+ day_number_copy -= YEAR_SIZE(year); \
+ year++; \
+ } \
+ while (day_number_copy < 0) { \
+ year--; \
+ day_number_copy += YEAR_SIZE(year); \
+ } \
+ /* 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 \
+ ERR_FAIL_COND_V_MSG(month == 0, 0, "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 > 12, 0, "Invalid month value of: " + itos(month) + ". See the Time.Month enum for valid values."); \
+ ERR_FAIL_COND_V_MSG(hour > 23, 0, "Invalid hour value of: " + itos(hour) + "."); \
+ ERR_FAIL_COND_V_MSG(minute > 59, 0, "Invalid minute value of: " + itos(minute) + "."); \
+ ERR_FAIL_COND_V_MSG(second > 59, 0, "Invalid second value of: " + itos(second) + " (leap seconds are not supported)."); \
+ /* Do this check after month is tested as valid. */ \
+ ERR_FAIL_COND_V_MSG(day == 0, 0, "Invalid day value of: " + itos(month) + ", days are 1-indexed and cannot be 0."); \
+ 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, 0, "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. */ \
+ if (year >= UNIX_EPOCH_YEAR_AD) { \
+ for (int64_t iyear = UNIX_EPOCH_YEAR_AD; iyear < year; iyear++) { \
+ day_number += YEAR_SIZE(iyear); \
+ } \
+ } else { \
+ for (int64_t iyear = UNIX_EPOCH_YEAR_AD - 1; iyear >= year; iyear--) { \
+ day_number -= YEAR_SIZE(iyear); \
+ } \
+ }
+
+#define PARSE_ISO8601_STRING \
+ int64_t year = UNIX_EPOCH_YEAR_AD; \
+ Month month = MONTH_JANUARY; \
+ uint8_t day = 1; \
+ uint8_t hour = 0; \
+ uint8_t minute = 0; \
+ uint8_t 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"); \
+ date = array[0]; \
+ time = array[1]; \
+ } else if (p_datetime.find_char(' ') > 0) { \
+ has_date = has_time = true; \
+ PackedStringArray array = p_datetime.split(" "); \
+ 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); \
+ 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); \
+ 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)) ? uint8_t(p_datetime[MONTH_KEY]) : 1); \
+ uint8_t day = p_datetime.has(DAY_KEY) ? uint8_t(p_datetime[DAY_KEY]) : 1; \
+ uint8_t hour = p_datetime.has(HOUR_KEY) ? uint8_t(p_datetime[HOUR_KEY]) : 0; \
+ uint8_t minute = p_datetime.has(MINUTE_KEY) ? uint8_t(p_datetime[MINUTE_KEY]) : 0; \
+ uint8_t second = p_datetime.has(SECOND_KEY) ? uint8_t(p_datetime[SECOND_KEY]) : 0;
+
+Time *Time::singleton = nullptr;
+
+Time *Time::get_singleton() {
+ if (!singleton) {
+ memnew(Time);
+ }
+ 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
+ // vformat only supports up to 6 arguments, so we need to split this up into 2 parts.
+ String timestamp = vformat("%04d-%02d-%02d", year, (uint8_t)month, day);
+ if (p_use_space) {
+ timestamp = vformat("%s %02d:%02d:%02d", timestamp, hour, minute, second);
+ } else {
+ timestamp = vformat("%sT%02d:%02d:%02d", timestamp, hour, minute, second);
+ }
+
+ return timestamp;
+}
+
+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_string(String p_datetime, bool p_weekday) const {
+ PARSE_ISO8601_STRING
+ 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_dict(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
+ // vformat only supports up to 6 arguments, so we need to split this up into 2 parts.
+ String timestamp = vformat("%04d-%02d-%02d", year, (uint8_t)month, day);
+ if (p_use_space) {
+ timestamp = vformat("%s %02d:%02d:%02d", timestamp, hour, minute, second);
+ } else {
+ timestamp = vformat("%sT%02d:%02d:%02d", timestamp, hour, minute, second);
+ }
+ return timestamp;
+}
+
+int64_t Time::get_unix_time_from_datetime_dict(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
+ YMD_TO_DAY_NUMBER
+ return day_number * SECONDS_PER_DAY + hour * 3600 + minute * 60 + second;
+}
+
+int64_t Time::get_unix_time_from_datetime_string(String p_datetime) const {
+ PARSE_ISO8601_STRING
+ VALIDATE_YMDHMS
+ YMD_TO_DAY_NUMBER
+ return day_number * SECONDS_PER_DAY + hour * 3600 + minute * 60 + second;
+}
+
+Dictionary Time::get_datetime_dict_from_system(bool p_utc) const {
+ OS::Date date = OS::get_singleton()->get_date(p_utc);
+ OS::Time time = OS::get_singleton()->get_time(p_utc);
+ Dictionary datetime;
+ datetime[YEAR_KEY] = date.year;
+ datetime[MONTH_KEY] = (uint8_t)date.month;
+ datetime[DAY_KEY] = date.day;
+ datetime[WEEKDAY_KEY] = (uint8_t)date.weekday;
+ datetime[DST_KEY] = date.dst;
+ datetime[HOUR_KEY] = time.hour;
+ datetime[MINUTE_KEY] = time.minute;
+ datetime[SECOND_KEY] = time.second;
+ return datetime;
+}
+
+Dictionary Time::get_date_dict_from_system(bool p_utc) const {
+ OS::Date date = OS::get_singleton()->get_date(p_utc);
+ Dictionary date_dictionary;
+ date_dictionary[YEAR_KEY] = date.year;
+ date_dictionary[MONTH_KEY] = (uint8_t)date.month;
+ date_dictionary[DAY_KEY] = date.day;
+ date_dictionary[WEEKDAY_KEY] = (uint8_t)date.weekday;
+ date_dictionary[DST_KEY] = date.dst;
+ return date_dictionary;
+}
+
+Dictionary Time::get_time_dict_from_system(bool p_utc) const {
+ OS::Time time = OS::get_singleton()->get_time(p_utc);
+ Dictionary time_dictionary;
+ time_dictionary[HOUR_KEY] = time.hour;
+ time_dictionary[MINUTE_KEY] = time.minute;
+ time_dictionary[SECOND_KEY] = time.second;
+ return time_dictionary;
+}
+
+String Time::get_datetime_string_from_system(bool p_utc, bool p_use_space) const {
+ OS::Date date = OS::get_singleton()->get_date(p_utc);
+ OS::Time time = OS::get_singleton()->get_time(p_utc);
+ // vformat only supports up to 6 arguments, so we need to split this up into 2 parts.
+ String timestamp = vformat("%04d-%02d-%02d", date.year, (uint8_t)date.month, date.day);
+ if (p_use_space) {
+ timestamp = vformat("%s %02d:%02d:%02d", timestamp, time.hour, time.minute, time.second);
+ } else {
+ timestamp = vformat("%sT%02d:%02d:%02d", timestamp, time.hour, time.minute, time.second);
+ }
+
+ return timestamp;
+}
+
+String Time::get_date_string_from_system(bool p_utc) const {
+ OS::Date date = OS::get_singleton()->get_date(p_utc);
+ // Android is picky about the types passed to make Variant, so we need a cast.
+ return vformat("%04d-%02d-%02d", date.year, (uint8_t)date.month, date.day);
+}
+
+String Time::get_time_string_from_system(bool p_utc) const {
+ OS::Time time = OS::get_singleton()->get_time(p_utc);
+ return vformat("%02d:%02d:%02d", time.hour, time.minute, time.second);
+}
+
+Dictionary Time::get_time_zone_from_system() const {
+ OS::TimeZoneInfo info = OS::get_singleton()->get_time_zone_info();
+ Dictionary timezone;
+ timezone["bias"] = info.bias;
+ timezone["name"] = info.name;
+ return 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_string", "datetime", "weekday"), &Time::get_datetime_dict_from_string);
+ ClassDB::bind_method(D_METHOD("get_datetime_string_from_dict", "datetime", "use_space"), &Time::get_datetime_string_from_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_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;
+}
diff --git a/core/os/time.h b/core/os/time.h
new file mode 100644
index 0000000000..4325f93d56
--- /dev/null
+++ b/core/os/time.h
@@ -0,0 +1,109 @@
+/*************************************************************************/
+/* time.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* 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 TIME_H
+#define TIME_H
+
+#include "core/object/class_db.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();
+
+ enum Month : uint8_t {
+ /// 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,
+ };
+
+ // 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_string(String p_datetime, bool p_weekday = true) const;
+ String get_datetime_string_from_dict(Dictionary p_datetime, bool p_use_space = false) const;
+ int64_t get_unix_time_from_datetime_dict(Dictionary p_datetime) const;
+ int64_t get_unix_time_from_datetime_string(String p_datetime) 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();
+};
+
+#endif // TIME_H