diff --git a/source/blender/blenkernel/BKE_text.h b/source/blender/blenkernel/BKE_text.h index a69fee19380..12f8795ae2a 100644 --- a/source/blender/blenkernel/BKE_text.h +++ b/source/blender/blenkernel/BKE_text.h @@ -94,6 +94,9 @@ void txt_sel_clear(struct Text *text); void txt_sel_line(struct Text *text); void txt_sel_set(struct Text *text, int startl, int startc, int endl, int endc); char *txt_sel_to_buf(const struct Text *text, size_t *r_buf_strlen); +/** + * \param in_buffer: UTF8 encoded text, invalid UTF8 byte-sequences are handled gracefully. + */ void txt_insert_buf(struct Text *text, const char *in_buffer, int in_buffer_len) ATTR_NONNULL(1, 2); void txt_split_curline(struct Text *text); diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index 5d2ee150ea0..460ef112521 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -1051,7 +1051,8 @@ static int paste_text_exec(bContext *C, wmOperator *op) int len; } clipboard_system = {NULL}, clipboard_vfont = {NULL}; - clipboard_system.buf = WM_clipboard_text_get(selection, &clipboard_system.len); + /* No need for UTF8 validation as the conversion handles invalid sequences gracefully. */ + clipboard_system.buf = WM_clipboard_text_get(selection, false, &clipboard_system.len); if (clipboard_system.buf == NULL) { return OPERATOR_CANCELLED; diff --git a/source/blender/editors/interface/interface_handlers.cc b/source/blender/editors/interface/interface_handlers.cc index 683509d2d6b..749f74d253c 100644 --- a/source/blender/editors/interface/interface_handlers.cc +++ b/source/blender/editors/interface/interface_handlers.cc @@ -2435,19 +2435,21 @@ static void ui_apply_but( /** \name Button Copy & Paste * \{ */ -static void ui_but_get_pasted_text_from_clipboard(char **buf_paste, int *buf_len) +static void ui_but_get_pasted_text_from_clipboard(const bool ensure_utf8, + char **r_buf_paste, + int *r_buf_len) { /* get only first line even if the clipboard contains multiple lines */ int length; - char *text = WM_clipboard_text_get_firstline(false, &length); + char *text = WM_clipboard_text_get_firstline(false, ensure_utf8, &length); if (text) { - *buf_paste = text; - *buf_len = length; + *r_buf_paste = text; + *r_buf_len = length; } else { - *buf_paste = static_cast(MEM_callocN(sizeof(char), __func__)); - *buf_len = 0; + *r_buf_paste = static_cast(MEM_callocN(sizeof(char), __func__)); + *r_buf_len = 0; } } @@ -2831,7 +2833,7 @@ static void ui_but_paste(bContext *C, uiBut *but, uiHandleButtonData *data, cons int buf_paste_len = 0; char *buf_paste; - ui_but_get_pasted_text_from_clipboard(&buf_paste, &buf_paste_len); + ui_but_get_pasted_text_from_clipboard(UI_but_is_utf8(but), &buf_paste, &buf_paste_len); const bool has_required_data = !(but->poin == nullptr && but->rnapoin.data == nullptr); @@ -3310,13 +3312,9 @@ static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const in if (mode == UI_TEXTEDIT_PASTE) { /* extract the first line from the clipboard */ int buf_len; - char *pbuf = WM_clipboard_text_get_firstline(false, &buf_len); + char *pbuf = WM_clipboard_text_get_firstline(false, UI_but_is_utf8(but), &buf_len); if (pbuf) { - if (UI_but_is_utf8(but)) { - buf_len -= BLI_str_utf8_invalid_strip(pbuf, size_t(buf_len)); - } - ui_textedit_insert_buf(but, data, pbuf, buf_len); changed = true; @@ -3508,9 +3506,12 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) if (but) { if (UI_but_is_utf8(but)) { const int strip = BLI_str_utf8_invalid_strip(but->editstr, strlen(but->editstr)); - /* not a file?, strip non utf-8 chars */ + /* Strip non-UTF8 characters unless buttons support this. + * This should never happen as all text input should be valid UTF8, + * there is a small chance existing data contains invalid sequences. + * This could check could be made into an assertion if `but->editstr` + * is valid UTF8 when #ui_textedit_begin assigns the string. */ if (strip) { - /* won't happen often so isn't that annoying to keep it here for a while */ printf("%s: invalid utf8 - stripped chars %d\n", __func__, strip); } } diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c index 507156d8341..0152054d4c7 100644 --- a/source/blender/editors/space_console/console_ops.c +++ b/source/blender/editors/space_console/console_ops.c @@ -1064,7 +1064,7 @@ static int console_paste_exec(bContext *C, wmOperator *op) ConsoleLine *ci = console_history_verify(C); int buf_len; - char *buf_str = WM_clipboard_text_get(selection, &buf_len); + char *buf_str = WM_clipboard_text_get(selection, true, &buf_len); char *buf_step, *buf_next; if (buf_str == NULL) { diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index 58dffa36b2d..ecd9607396c 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -923,7 +923,8 @@ static int text_paste_exec(bContext *C, wmOperator *op) char *buf; int buf_len; - buf = WM_clipboard_text_get(selection, &buf_len); + /* No need for UTF8 validation as the conversion handles invalid sequences gracefully. */ + buf = WM_clipboard_text_get(selection, false, &buf_len); if (!buf) { return OPERATOR_CANCELLED; diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index bdd3149884c..16e2caffb58 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -506,7 +506,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) if (event->modifier & KM_CTRL) { /* extract the first line from the clipboard */ int pbuf_len; - char *pbuf = WM_clipboard_text_get_firstline(false, &pbuf_len); + char *pbuf = WM_clipboard_text_get_firstline(false, true, &pbuf_len); if (pbuf) { const bool success = editstr_insert_at_cursor(n, pbuf, pbuf_len); diff --git a/source/blender/python/intern/bpy_rna_types_capi.c b/source/blender/python/intern/bpy_rna_types_capi.c index 105a1008a89..ae37be2f761 100644 --- a/source/blender/python/intern/bpy_rna_types_capi.c +++ b/source/blender/python/intern/bpy_rna_types_capi.c @@ -111,7 +111,8 @@ PyDoc_STRVAR(pyrna_WindowManager_clipboard_doc, "Clipboard text storage.\n\n:typ static PyObject *pyrna_WindowManager_clipboard_get(PyObject *UNUSED(self), void *UNUSED(flag)) { int text_len = 0; - char *text = WM_clipboard_text_get(false, &text_len); + /* No need for UTF8 validation as #PyC_UnicodeFromBytesAndSize handles invalid byte sequences. */ + char *text = WM_clipboard_text_get(false, false, &text_len); PyObject *result = PyC_UnicodeFromBytesAndSize(text ? text : "", text_len); if (text != NULL) { MEM_freeN(text); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 5117c87db7f..6f6c5513514 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -1624,14 +1624,15 @@ void WM_job_main_thread_lock_release(struct wmJob *job); /** * Return text from the clipboard. - * - * \note Caller needs to check for valid utf8 if this is a requirement. + * \param selection: Use the "primary" clipboard, see: #WM_CAPABILITY_PRIMARY_CLIPBOARD. + * \param ensure_utf8: Ensure the resulting string does not contain invalid UTF8 encoding. */ -char *WM_clipboard_text_get(bool selection, int *r_len); +char *WM_clipboard_text_get(bool selection, bool ensure_utf8, int *r_len); /** * Convenience function for pasting to areas of Blender which don't support newlines. */ -char *WM_clipboard_text_get_firstline(bool selection, int *r_len); +char *WM_clipboard_text_get_firstline(bool selection, bool ensure_utf8, int *r_len); + void WM_clipboard_text_set(const char *buf, bool selection); /** diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index d7f0f3944d6..6eb362a97e2 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1983,7 +1983,10 @@ void WM_event_remove_timer_notifier(wmWindowManager *wm, wmWindow *win, wmTimer /** \name Clipboard * \{ */ -static char *wm_clipboard_text_get_ex(bool selection, int *r_len, bool firstline) +static char *wm_clipboard_text_get_ex(bool selection, + int *r_len, + const bool ensure_utf8, + const bool firstline) { if (G.background) { *r_len = 0; @@ -1996,8 +1999,18 @@ static char *wm_clipboard_text_get_ex(bool selection, int *r_len, bool firstline return NULL; } + int buf_len = strlen(buf); + + if (ensure_utf8) { + /* TODO(@ideasman42): It would be good if unexpected byte sequences could be interpreted + * instead of stripped - so mixed in characters (typically Latin1) aren't ignored. + * Check on how Python bytes this, see: #PyC_UnicodeFromBytesAndSize, + * there are clever ways to handle this although they increase the size of the buffer. */ + buf_len -= BLI_str_utf8_invalid_strip(buf, buf_len); + } + /* always convert from \r\n to \n */ - char *newbuf = MEM_mallocN(strlen(buf) + 1, __func__); + char *newbuf = MEM_mallocN(buf_len + 1, __func__); char *p2 = newbuf; if (firstline) { @@ -2028,14 +2041,14 @@ static char *wm_clipboard_text_get_ex(bool selection, int *r_len, bool firstline return newbuf; } -char *WM_clipboard_text_get(bool selection, int *r_len) +char *WM_clipboard_text_get(bool selection, bool ensure_utf8, int *r_len) { - return wm_clipboard_text_get_ex(selection, r_len, false); + return wm_clipboard_text_get_ex(selection, r_len, ensure_utf8, false); } -char *WM_clipboard_text_get_firstline(bool selection, int *r_len) +char *WM_clipboard_text_get_firstline(bool selection, bool ensure_utf8, int *r_len) { - return wm_clipboard_text_get_ex(selection, r_len, true); + return wm_clipboard_text_get_ex(selection, r_len, ensure_utf8, true); } void WM_clipboard_text_set(const char *buf, bool selection)