// Copyright (C) 2022 TiltedPhoques SRL. // For licensing information see LICENSE at the root of this distribution. // Taken from // https://github.com/xposure/ImGuiSharp/blob/58860d38a9ec066f492c983c9c63393d6e00a442/ImGuiSharp.Shared/ImGui.h/Helpers.cpp #include "imgui.h" #include #include namespace ImGuiImpl { namespace { int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end) { unsigned int c = (unsigned int)-1; const unsigned char* str = (const unsigned char*)in_text; if (!(*str & 0x80)) { c = (unsigned int)(*str++); *out_char = c; return 1; } if ((*str & 0xe0) == 0xc0) { *out_char = 0xFFFD; // will be invalid but not end of string if (in_text_end && in_text_end - (const char*)str < 2) return 1; if (*str < 0xc2) return 2; c = (unsigned int)((*str++ & 0x1f) << 6); if ((*str & 0xc0) != 0x80) return 2; c += (*str++ & 0x3f); *out_char = c; return 2; } if ((*str & 0xf0) == 0xe0) { *out_char = 0xFFFD; // will be invalid but not end of string if (in_text_end && in_text_end - (const char*)str < 3) return 1; if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3; if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below c = (unsigned int)((*str++ & 0x0f) << 12); if ((*str & 0xc0) != 0x80) return 3; c += (unsigned int)((*str++ & 0x3f) << 6); if ((*str & 0xc0) != 0x80) return 3; c += (*str++ & 0x3f); *out_char = c; return 3; } if ((*str & 0xf8) == 0xf0) { *out_char = 0xFFFD; // will be invalid but not end of string if (in_text_end && in_text_end - (const char*)str < 4) return 1; if (*str > 0xf4) return 4; if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4; if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below c = (unsigned int)((*str++ & 0x07) << 18); if ((*str & 0xc0) != 0x80) return 4; c += (unsigned int)((*str++ & 0x3f) << 12); if ((*str & 0xc0) != 0x80) return 4; c += (unsigned int)((*str++ & 0x3f) << 6); if ((*str & 0xc0) != 0x80) return 4; c += (*str++ & 0x3f); // utf-8 encodings of values used in surrogate pairs are invalid if ((c & 0xFFFFF800) == 0xD800) return 4; *out_char = c; return 4; } *out_char = 0; return 0; } int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining) { ImWchar* buf_out = buf; ImWchar* buf_end = buf + buf_size; while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) { unsigned int c; in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); if (c == 0) break; if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes *buf_out++ = (ImWchar)c; } *buf_out = 0; if (in_text_remaining) *in_text_remaining = in_text; return (int)(buf_out - buf); } int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end) { int char_count = 0; while ((!in_text_end || in_text < in_text_end) && *in_text) { unsigned int c; in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); if (c == 0) break; if (c < 0x10000) char_count++; } return char_count; } // Based on stb_to_utf8() from github.com/nothings/stb/ static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) { if (c < 0x80) { buf[0] = (char)c; return 1; } if (c < 0x800) { if (buf_size < 2) return 0; buf[0] = (char)(0xc0 + (c >> 6)); buf[1] = (char)(0x80 + (c & 0x3f)); return 2; } if (c >= 0xdc00 && c < 0xe000) { return 0; } if (c >= 0xd800 && c < 0xdc00) { if (buf_size < 4) return 0; buf[0] = (char)(0xf0 + (c >> 18)); buf[1] = (char)(0x80 + ((c >> 12) & 0x3f)); buf[2] = (char)(0x80 + ((c >> 6) & 0x3f)); buf[3] = (char)(0x80 + ((c)&0x3f)); return 4; } // else if (c < 0x10000) { if (buf_size < 3) return 0; buf[0] = (char)(0xe0 + (c >> 12)); buf[1] = (char)(0x80 + ((c >> 6) & 0x3f)); buf[2] = (char)(0x80 + ((c)&0x3f)); return 3; } } static inline int ImTextCountUtf8BytesFromChar(unsigned int c) { if (c < 0x80) return 1; if (c < 0x800) return 2; if (c >= 0xdc00 && c < 0xe000) return 0; if (c >= 0xd800 && c < 0xdc00) return 4; return 3; } int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end) { char* buf_out = buf; const char* buf_end = buf + buf_size; while (buf_out < buf_end - 1 && (!in_text_end || in_text < in_text_end) && *in_text) { unsigned int c = (unsigned int)(*in_text++); if (c < 0x80) *buf_out++ = (char)c; else buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end - buf_out - 1), c); } *buf_out = 0; return (int)(buf_out - buf); } int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end) { int bytes_count = 0; while ((!in_text_end || in_text < in_text_end) && *in_text) { unsigned int c = (unsigned int)(*in_text++); if (c < 0x80) bytes_count++; else bytes_count += ImTextCountUtf8BytesFromChar(c); } return bytes_count; } static const char* GetClipboardTextFn_DefaultImpl(void*) { static char* buf_local = NULL; if (buf_local) { ImGui::MemFree(buf_local); buf_local = NULL; } if (!OpenClipboard(NULL)) return NULL; HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT); if (wbuf_handle == NULL) return NULL; if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle)) { int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1; buf_local = (char*)ImGui::MemAlloc(buf_len * sizeof(char)); ImTextStrToUtf8(buf_local, buf_len, wbuf_global, NULL); } GlobalUnlock(wbuf_handle); CloseClipboard(); return buf_local; } static void SetClipboardTextFn_DefaultImpl(void*, const char* text) { if (!OpenClipboard(NULL)) return; const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1; HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar)); if (wbuf_handle == NULL) return; ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle); ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL, NULL); GlobalUnlock(wbuf_handle); EmptyClipboard(); SetClipboardData(CF_UNICODETEXT, wbuf_handle); CloseClipboard(); } } // namespace void InstallClipboardHandlers(ImGuiIO& aIO) { aIO.SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; aIO.GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; } } // namespace ImGuiImpl