summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbruvzg <7645683+bruvzg@users.noreply.github.com>2023-04-12 11:39:05 +0300
committerYuri Sizov <yuris@humnom.net>2023-04-24 17:03:55 +0200
commit9c1ea280927a1376cd31e1b0dfe7ca5abd12e693 (patch)
tree7d2a57f114ab3c176f3feb30464995aadd7445c7
parent3a1af9393f0accfed8d05a257e2ff8af6b2e7050 (diff)
Improve line BiDi handling, prevent crash on recursive log updates.
(cherry picked from commit 282e4231c26c172b186a5bf22a8ba7f0337ba3d6)
-rw-r--r--editor/editor_log.cpp5
-rw-r--r--modules/text_server_adv/text_server_adv.cpp45
-rw-r--r--modules/text_server_adv/text_server_adv.h1
-rw-r--r--scene/gui/rich_text_label.cpp11
-rw-r--r--scene/gui/rich_text_label.h2
5 files changed, 54 insertions, 10 deletions
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index 4ee28c5c31..cc88424c55 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -278,6 +278,11 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
return;
}
+ if (unlikely(log->is_updating())) {
+ // The new message arrived during log RTL text processing/redraw (invalid BiDi control characters / font error), ignore it to avoid RTL data corruption.
+ return;
+ }
+
// Only add the message to the log if it passes the filters.
bool filter_active = type_filter_map[p_message.type]->is_active();
String search_text = search_box->get_text();
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index ae32a08c58..e1aa467fdc 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -4105,6 +4105,7 @@ RID TextServerAdvanced::_shaped_text_substr(const RID &p_shaped, int64_t p_start
new_sd->direction = sd->direction;
new_sd->custom_punct = sd->custom_punct;
new_sd->para_direction = sd->para_direction;
+ new_sd->base_para_direction = sd->base_para_direction;
for (int i = 0; i < TextServer::SPACING_MAX; i++) {
new_sd->extra_spacing[i] = sd->extra_spacing[i];
}
@@ -4155,9 +4156,33 @@ bool TextServerAdvanced::_shape_substr(ShapedTextDataAdvanced *p_new_sd, const S
if (U_SUCCESS(err)) {
ubidi_setLine(p_sd->bidi_iter[ov], start, end, bidi_iter, &err);
if (U_FAILURE(err)) {
- ubidi_close(bidi_iter);
- bidi_iter = nullptr;
- ERR_PRINT(vformat("BiDi reordering for the line failed: %s", u_errorName(err)));
+ // Line BiDi failed (string contains incompatible control characters), try full paragraph BiDi instead.
+ err = U_ZERO_ERROR;
+ const UChar *data = p_sd->utf16.get_data();
+ switch (static_cast<TextServer::Direction>(p_sd->bidi_override[ov].z)) {
+ case DIRECTION_LTR: {
+ ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_LTR, nullptr, &err);
+ } break;
+ case DIRECTION_RTL: {
+ ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_RTL, nullptr, &err);
+ } break;
+ case DIRECTION_INHERITED: {
+ ubidi_setPara(bidi_iter, data + start, end - start, p_sd->base_para_direction, nullptr, &err);
+ } break;
+ case DIRECTION_AUTO: {
+ UBiDiDirection direction = ubidi_getBaseDirection(data + start, end - start);
+ if (direction != UBIDI_NEUTRAL) {
+ ubidi_setPara(bidi_iter, data + start, end - start, direction, nullptr, &err);
+ } else {
+ ubidi_setPara(bidi_iter, data + start, end - start, p_sd->base_para_direction, nullptr, &err);
+ }
+ } break;
+ }
+ if (U_FAILURE(err)) {
+ ubidi_close(bidi_iter);
+ bidi_iter = nullptr;
+ ERR_PRINT(vformat("BiDi reordering for the line failed: %s", u_errorName(err)));
+ }
}
} else {
bidi_iter = nullptr;
@@ -5586,25 +5611,25 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
sd->script_iter = memnew(ScriptIterator(sd->text, 0, sd->text.length()));
}
- int base_para_direction = UBIDI_DEFAULT_LTR;
+ sd->base_para_direction = UBIDI_DEFAULT_LTR;
switch (sd->direction) {
case DIRECTION_LTR: {
sd->para_direction = DIRECTION_LTR;
- base_para_direction = UBIDI_LTR;
+ sd->base_para_direction = UBIDI_LTR;
} break;
case DIRECTION_RTL: {
sd->para_direction = DIRECTION_RTL;
- base_para_direction = UBIDI_RTL;
+ sd->base_para_direction = UBIDI_RTL;
} break;
case DIRECTION_INHERITED:
case DIRECTION_AUTO: {
UBiDiDirection direction = ubidi_getBaseDirection(data, sd->utf16.length());
if (direction != UBIDI_NEUTRAL) {
sd->para_direction = (direction == UBIDI_RTL) ? DIRECTION_RTL : DIRECTION_LTR;
- base_para_direction = direction;
+ sd->base_para_direction = direction;
} else {
sd->para_direction = DIRECTION_LTR;
- base_para_direction = UBIDI_DEFAULT_LTR;
+ sd->base_para_direction = UBIDI_DEFAULT_LTR;
}
} break;
}
@@ -5633,14 +5658,14 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) {
ubidi_setPara(bidi_iter, data + start, end - start, UBIDI_RTL, nullptr, &err);
} break;
case DIRECTION_INHERITED: {
- ubidi_setPara(bidi_iter, data + start, end - start, base_para_direction, nullptr, &err);
+ ubidi_setPara(bidi_iter, data + start, end - start, sd->base_para_direction, nullptr, &err);
} break;
case DIRECTION_AUTO: {
UBiDiDirection direction = ubidi_getBaseDirection(data + start, end - start);
if (direction != UBIDI_NEUTRAL) {
ubidi_setPara(bidi_iter, data + start, end - start, direction, nullptr, &err);
} else {
- ubidi_setPara(bidi_iter, data + start, end - start, base_para_direction, nullptr, &err);
+ ubidi_setPara(bidi_iter, data + start, end - start, sd->base_para_direction, nullptr, &err);
}
} break;
}
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index 046a85d2f7..dff87f3d74 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -475,6 +475,7 @@ class TextServerAdvanced : public TextServerExtension {
/* Shaped data */
TextServer::Direction para_direction = DIRECTION_LTR; // Detected text direction.
+ int base_para_direction = UBIDI_DEFAULT_LTR;
bool valid = false; // String is shaped.
bool line_breaks_valid = false; // Line and word break flags are populated (and virtual zero width spaces inserted).
bool justification_ops_valid = false; // Virtual elongation glyphs are added to the string.
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 81ef8dee36..cf7b6cf608 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -2697,6 +2697,10 @@ bool RichTextLabel::is_ready() const {
return (main->first_invalid_line.load() == (int)main->lines.size() && main->first_resized_line.load() == (int)main->lines.size() && main->first_invalid_font_line.load() == (int)main->lines.size());
}
+bool RichTextLabel::is_updating() const {
+ return updating.load() || validating.load();
+}
+
void RichTextLabel::set_threaded(bool p_threaded) {
if (threaded != p_threaded) {
_stop_thread();
@@ -2721,6 +2725,7 @@ bool RichTextLabel::_validate_line_caches() {
if (updating.load()) {
return false;
}
+ validating.store(true);
if (main->first_invalid_line.load() == (int)main->lines.size()) {
MutexLock data_lock(data_mutex);
Rect2 text_rect = _get_text_rect();
@@ -2739,6 +2744,7 @@ bool RichTextLabel::_validate_line_caches() {
if (main->first_resized_line.load() == (int)main->lines.size()) {
vscroll->set_value(old_scroll);
+ validating.store(false);
return true;
}
@@ -2790,8 +2796,10 @@ bool RichTextLabel::_validate_line_caches() {
if (fit_content) {
update_minimum_size();
}
+ validating.store(false);
return true;
}
+ validating.store(false);
stop_thread.store(false);
if (threaded) {
updating.store(true);
@@ -2801,7 +2809,9 @@ bool RichTextLabel::_validate_line_caches() {
loading_started = OS::get_singleton()->get_ticks_msec();
return false;
} else {
+ updating.store(true);
_process_line_caches();
+ updating.store(false);
queue_redraw();
return true;
}
@@ -5882,6 +5892,7 @@ RichTextLabel::RichTextLabel(const String &p_text) {
set_text(p_text);
updating.store(false);
+ validating.store(false);
stop_thread.store(false);
set_clip_contents(true);
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 1dae8b75ca..5a9a8478b7 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -376,6 +376,7 @@ private:
bool threaded = false;
std::atomic<bool> stop_thread;
std::atomic<bool> updating;
+ std::atomic<bool> validating;
std::atomic<double> loaded;
uint64_t loading_started = 0;
@@ -679,6 +680,7 @@ public:
void deselect();
bool is_ready() const;
+ bool is_updating() const;
void set_threaded(bool p_threaded);
bool is_threaded() const;