diff options
-rw-r--r-- | doc/translations/extract.py | 239 | ||||
-rw-r--r-- | platform/android/java/lib/src/org/godotengine/godot/Godot.java | 17 | ||||
-rw-r--r-- | platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java | 33 | ||||
-rw-r--r-- | platform/android/java_godot_lib_jni.cpp | 2 | ||||
-rw-r--r-- | platform/android/java_godot_wrapper.cpp | 8 | ||||
-rw-r--r-- | platform/android/java_godot_wrapper.h | 4 | ||||
-rw-r--r-- | scene/2d/navigation_agent_2d.cpp | 1 | ||||
-rw-r--r-- | scene/3d/navigation_agent.cpp | 1 |
8 files changed, 281 insertions, 24 deletions
diff --git a/doc/translations/extract.py b/doc/translations/extract.py new file mode 100644 index 0000000000..9d7c073b67 --- /dev/null +++ b/doc/translations/extract.py @@ -0,0 +1,239 @@ +#!/usr/bin/env python3 + +import argparse +import os +import re +import shutil +from collections import OrderedDict + +EXTRACT_TAGS = ["description", "brief_description", "member", "constant", "theme_item", "link"] +HEADER = '''\ +# LANGUAGE translation of the Godot Engine class reference +# Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. +# Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). +# This file is distributed under the same license as the Godot source code. +# +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Godot Engine class reference\\n" +"Content-Type: text/plain; charset=UTF-8\\n" +"Content-Transfer-Encoding: 8-bit\\n" + +''' + +## <xml-line-number-hack from="https://stackoverflow.com/a/36430270/10846399"> +import sys +sys.modules['_elementtree'] = None +import xml.etree.ElementTree as ET + +## override the parser to get the line number +class LineNumberingParser(ET.XMLParser): + def _start(self, *args, **kwargs): + ## Here we assume the default XML parser which is expat + ## and copy its element position attributes into output Elements + element = super(self.__class__, self)._start(*args, **kwargs) + element._start_line_number = self.parser.CurrentLineNumber + element._start_column_number = self.parser.CurrentColumnNumber + element._start_byte_index = self.parser.CurrentByteIndex + return element + + def _end(self, *args, **kwargs): + element = super(self.__class__, self)._end(*args, **kwargs) + element._end_line_number = self.parser.CurrentLineNumber + element._end_column_number = self.parser.CurrentColumnNumber + element._end_byte_index = self.parser.CurrentByteIndex + return element +## </xml-line-number-hack> + +class Desc: + def __init__(self, line_no, msg, desc_list=None): + ## line_no : the line number where the desc is + ## msg : the description string + ## desc_list : the DescList it belongs to + self.line_no = line_no + self.msg = msg + self.desc_list = desc_list + +class DescList: + def __init__(self, doc, path): + ## doc : root xml element of the document + ## path : file path of the xml document + ## list : list of Desc objects for this document + self.doc = doc + self.path = path + self.list = [] + +def print_error(error): + print("ERROR: {}".format(error)) + +## build classes with xml elements recursively +def _collect_classes_dir(path, classes): + if not os.path.isdir(path): + print_error("Invalid directory path: {}".format(path)) + exit(1) + for _dir in map(lambda dir : os.path.join(path, dir), os.listdir(path)): + if os.path.isdir(_dir): + _collect_classes_dir(_dir, classes) + elif os.path.isfile(_dir): + if not _dir.endswith(".xml"): + #print("Got non-.xml file '{}', skipping.".format(path)) + continue + _collect_classes_file(_dir, classes) + +## opens a file and parse xml add to classes +def _collect_classes_file(path, classes): + if not os.path.isfile(path) or not path.endswith(".xml"): + print_error("Invalid xml file path: {}".format(path)) + exit(1) + print('Collecting file: {}'.format(os.path.basename(path))) + + try: + tree = ET.parse(path, parser=LineNumberingParser()) + except ET.ParseError as e: + print_error("Parse error reading file '{}': {}".format(path, e)) + exit(1) + + doc = tree.getroot() + + if 'name' in doc.attrib: + if 'version' not in doc.attrib: + print_error("Version missing from 'doc', file: {}".format(path)) + + name = doc.attrib["name"] + if name in classes: + print_error("Duplicate class {} at path {}".format(name, path)) + exit(1) + classes[name] = DescList(doc, path) + else: + print_error('Unknown XML file {}, skipping'.format(path)) + + +## regions are list of tuples with size 3 (start_index, end_index, indent) +## indication in string where the codeblock starts, ends, and it's indent +## if i inside the region returns the indent, else returns -1 +def _get_xml_indent(i, regions): + for region in regions: + if region[0] < i < region[1] : + return region[2] + return -1 + +## find and build all regions of codeblock which we need later +def _make_codeblock_regions(desc, path=''): + code_block_end = False + code_block_index = 0 + code_block_regions = [] + while not code_block_end: + code_block_index = desc.find("[codeblock]", code_block_index) + if code_block_index < 0: break + xml_indent=0 + while True : + ## [codeblock] always have a trailing new line and some tabs + ## those tabs are belongs to xml indentations not code indent + if desc[code_block_index+len("[codeblock]\n")+xml_indent] == '\t': + xml_indent+=1 + else: break + end_index = desc.find("[/codeblock]", code_block_index) + if end_index < 0 : + print_error('Non terminating codeblock: {}'.format(path)) + exit(1) + code_block_regions.append( (code_block_index, end_index, xml_indent) ) + code_block_index += 1 + return code_block_regions + +def _strip_and_split_desc(desc, code_block_regions): + desc_strip = '' ## a stripped desc msg + total_indent = 0 ## code indent = total indent - xml indent + for i in range(len(desc)): + c = desc[i] + if c == '\n' : c = '\\n' + if c == '"': c = '\\"' + if c == '\\': c = '\\\\' ## <element \> is invalid for msgmerge + if c == '\t': + xml_indent = _get_xml_indent(i, code_block_regions) + if xml_indent >= 0: + total_indent += 1 + if xml_indent < total_indent: + c = '\\t' + else: + continue + else: + continue + desc_strip += c + if c == '\\n': + total_indent = 0 + return desc_strip + +## make catlog strings from xml elements +def _make_translation_catalog(classes): + unique_msgs = OrderedDict() + for class_name in classes: + desc_list = classes[class_name] + for elem in desc_list.doc.iter(): + if elem.tag in EXTRACT_TAGS: + if not elem.text or len(elem.text) == 0 : continue + line_no = elem._start_line_number if elem.text[0]!='\n' else elem._start_line_number+1 + desc_str = elem.text.strip() + code_block_regions = _make_codeblock_regions(desc_str, desc_list.path) + desc_msg = _strip_and_split_desc(desc_str, code_block_regions) + desc_obj = Desc(line_no, desc_msg, desc_list) + desc_list.list.append(desc_obj) + + if desc_msg not in unique_msgs: + unique_msgs[desc_msg] = [desc_obj] + else: + unique_msgs[desc_msg].append(desc_obj) + return unique_msgs + +## generate the catlog file +def _generate_translation_catalog_file(unique_msgs, output): + with open(output, 'w', encoding='utf8') as f: + f.write(HEADER) + for msg in unique_msgs: + if len(msg) == 0: continue ## ignore + + f.write('#:') + desc_list = unique_msgs[msg] + for desc in desc_list: + path = desc.desc_list.path.replace('\\', '/') + if path.startswith('./'): + path = path[2:] + f.write(' {}:{}'.format(path, desc.line_no)) + f.write('\n') + + f.write('msgid "{}"\n'.format(msg)) + f.write('msgstr ""\n\n') + + ## TODO: what if 'nt'? + if (os.name == "posix"): + print("Wrapping template at 79 characters for compatibility with Weblate.") + os.system("msgmerge -w79 {0} {0} > {0}.wrap".format(output)) + shutil.move("{}.wrap".format(output), output) + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--path", "-p", default=".", help="The directory containing XML files to collect.") + parser.add_argument("--output", "-o", default="translation_catlog.pot", help="The path to the output file.") + args = parser.parse_args() + + output = os.path.abspath(args.output) + if not os.path.isdir(os.path.dirname(output)) or not output.endswith('.pot'): + print_error("Invalid output path: {}".format(output)) + exit(1) + if not os.path.isdir(args.path): + print_error("Invalid working directory path: {}".format(args.path)) + exit(1) + + os.chdir(args.path) + print("Current working dir: {}\n".format(os.getcwd())) + + classes = OrderedDict() ## dictionary of key=class_name, value=DescList objects + _collect_classes_dir('.', classes) + classes = OrderedDict(sorted(classes.items(), key = lambda kv: kv[0].lower() )) + unique_msgs = _make_translation_catalog(classes) + _generate_translation_catalog_file(unique_msgs, output) + +if __name__ == '__main__': + main() diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 4e605f9950..1798a1df3a 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -55,8 +55,6 @@ import android.hardware.SensorManager; import android.os.Build; import android.os.Bundle; import android.os.Environment; -import android.os.Handler; -import android.os.Looper; import android.os.Messenger; import android.os.VibrationEffect; import android.os.Vibrator; @@ -64,7 +62,6 @@ import android.provider.Settings.Secure; import android.support.annotation.CallSuper; import android.support.annotation.Keep; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.support.v4.app.FragmentActivity; import android.view.Display; import android.view.KeyEvent; @@ -197,12 +194,12 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe }; /** - * Invoked on the GL thread when the Godot main loop has started. + * Invoked on the render thread when the Godot main loop has started. */ @CallSuper - protected void onGLGodotMainLoopStarted() { + protected void onGodotMainLoopStarted() { for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { - plugin.onGLGodotMainLoopStarted(); + plugin.onGodotMainLoopStarted(); } } @@ -247,7 +244,7 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe // Must occur after GodotLib.setup has completed. for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { - plugin.onGLRegisterPluginWithGodotNative(); + plugin.onRegisterPluginWithGodotNative(); } setKeepScreenOn("True".equals(GodotLib.getGlobal("display/window/energy_saving/keep_screen_on"))); @@ -787,11 +784,11 @@ public abstract class Godot extends FragmentActivity implements SensorEventListe } /** - * Queue a runnable to be run on the GL thread. + * Queue a runnable to be run on the render thread. * <p> - * This must be called after the GL thread has started. + * This must be called after the render thread has started. */ - public final void runOnGLThread(@NonNull Runnable action) { + public final void runOnRenderThread(@NonNull Runnable action) { if (mView != null) { mView.queueEvent(action); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java index d5bf4fc70e..e745cdd0a5 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java @@ -34,6 +34,7 @@ import android.app.Activity; import android.content.Intent; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.view.Surface; import android.view.View; import java.lang.reflect.Method; import java.util.ArrayList; @@ -84,8 +85,10 @@ public abstract class GodotPlugin { /** * Register the plugin with Godot native code. + * + * This method is invoked on the render thread. */ - public final void onGLRegisterPluginWithGodotNative() { + public final void onRegisterPluginWithGodotNative() { nativeRegisterSingleton(getPluginName()); Class clazz = getClass(); @@ -169,9 +172,9 @@ public abstract class GodotPlugin { public boolean onMainBackPressed() { return false; } /** - * Invoked on the GL thread when the Godot main loop has started. + * Invoked on the render thread when the Godot main loop has started. */ - public void onGLGodotMainLoopStarted() {} + public void onGodotMainLoopStarted() {} /** * Invoked once per frame on the GL thread after the frame is drawn. @@ -190,6 +193,22 @@ public abstract class GodotPlugin { public void onGLSurfaceCreated(GL10 gl, EGLConfig config) {} /** + * Invoked once per frame on the Vulkan thread after the frame is drawn. + */ + public void onVkDrawFrame() {} + + /** + * Called on the Vulkan thread after the surface is created and whenever the surface size + * changes. + */ + public void onVkSurfaceChanged(Surface surface, int width, int height) {} + + /** + * Called on the Vulkan thread when the surface is created or recreated. + */ + public void onVkSurfaceCreated(Surface surface) {} + + /** * Returns the name of the plugin. * <p> * This value must match the one listed in the plugin '<meta-data>' manifest entry. @@ -225,12 +244,12 @@ public abstract class GodotPlugin { } /** - * Queue the specified action to be run on the GL thread. + * Queue the specified action to be run on the render thread. * - * @param action the action to run on the GL thread + * @param action the action to run on the render thread */ - protected void runOnGLThread(Runnable action) { - godot.runOnGLThread(action); + protected void runOnRenderThread(Runnable action) { + godot.runOnRenderThread(action); } /** diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 0b1d070441..8bbf41d82d 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -207,7 +207,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl } os_android->main_loop_begin(); - godot_java->on_gl_godot_main_loop_started(env); + godot_java->on_godot_main_loop_started(env); ++step; } diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 7b677c186e..2a540bb4a9 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -66,7 +66,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) { _is_activity_resumed = p_env->GetMethodID(cls, "isActivityResumed", "()Z"); _vibrate = p_env->GetMethodID(cls, "vibrate", "(I)V"); _get_input_fallback_mapping = p_env->GetMethodID(cls, "getInputFallbackMapping", "()Ljava/lang/String;"); - _on_gl_godot_main_loop_started = p_env->GetMethodID(cls, "onGLGodotMainLoopStarted", "()V"); + _on_godot_main_loop_started = p_env->GetMethodID(cls, "onGodotMainLoopStarted", "()V"); } GodotJavaWrapper::~GodotJavaWrapper() { @@ -108,13 +108,13 @@ void GodotJavaWrapper::on_video_init(JNIEnv *p_env) { p_env->CallVoidMethod(godot_instance, _on_video_init); } -void GodotJavaWrapper::on_gl_godot_main_loop_started(JNIEnv *p_env) { - if (_on_gl_godot_main_loop_started) { +void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) { + if (_on_godot_main_loop_started) { if (p_env == NULL) { p_env = ThreadAndroid::get_env(); } } - p_env->CallVoidMethod(godot_instance, _on_gl_godot_main_loop_started); + p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started); } void GodotJavaWrapper::restart(JNIEnv *p_env) { diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index cdab2ecc9c..fb77c8ba6a 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -61,7 +61,7 @@ private: jmethodID _is_activity_resumed = 0; jmethodID _vibrate = 0; jmethodID _get_input_fallback_mapping = 0; - jmethodID _on_gl_godot_main_loop_started = 0; + jmethodID _on_godot_main_loop_started = 0; public: GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance); @@ -73,7 +73,7 @@ public: jobject get_class_loader(); void on_video_init(JNIEnv *p_env = NULL); - void on_gl_godot_main_loop_started(JNIEnv *p_env = NULL); + void on_godot_main_loop_started(JNIEnv *p_env = NULL); void restart(JNIEnv *p_env = NULL); void force_quit(JNIEnv *p_env = NULL); void set_keep_screen_on(bool p_enabled); diff --git a/scene/2d/navigation_agent_2d.cpp b/scene/2d/navigation_agent_2d.cpp index f5fe113f29..e345a076ae 100644 --- a/scene/2d/navigation_agent_2d.cpp +++ b/scene/2d/navigation_agent_2d.cpp @@ -215,6 +215,7 @@ void NavigationAgent2D::set_target_location(Vector2 p_location) { navigation_path.clear(); target_reached = false; navigation_finished = false; + update_frame_id = 0; } Vector2 NavigationAgent2D::get_target_location() const { diff --git a/scene/3d/navigation_agent.cpp b/scene/3d/navigation_agent.cpp index 728fc947e9..3da1ea66d6 100644 --- a/scene/3d/navigation_agent.cpp +++ b/scene/3d/navigation_agent.cpp @@ -234,6 +234,7 @@ void NavigationAgent::set_target_location(Vector3 p_location) { navigation_path.clear(); target_reached = false; navigation_finished = false; + update_frame_id = 0; } Vector3 NavigationAgent::get_target_location() const { |