From 8fc51bd1e4d58dc7f84086b761ff5e9934b18ab0 Mon Sep 17 00:00:00 2001 From: Nishi Date: Sun, 13 Sep 2026 12:21:05 +0900 Subject: [PATCH] unicode fix --- examples/basic/filechooser.c | 4 +- examples/vkdemos/vulkan.c | 4 +- include/Mw/Abstract/CharSet.h | 4 +- include/Mw/Unicode.h | 40 +++++++++++- pl/rules.pl | 3 +- src/abstract/charset.c | 42 ++---------- src/abstract/directory.c | 6 +- src/backend/cairo.c | 12 ++-- src/dialog/filechooser.c | 4 +- src/text/gdi.c | 46 +++++++++---- src/unicode.c | 118 ++++++++++++++++++++++++++++++++++ 11 files changed, 218 insertions(+), 65 deletions(-) diff --git a/examples/basic/filechooser.c b/examples/basic/filechooser.c index 6ad5636..a9a5ec0 100644 --- a/examples/basic/filechooser.c +++ b/examples/basic/filechooser.c @@ -16,10 +16,12 @@ void MWAPI ok(MwWidget handle, void* user, void* call) { #ifdef BUG msgbox_wait = MwFALSE; #endif + + MwDestroyWidget(mb); } void MWAPI file_callback(MwWidget handle, void* user_data, void* call_data) { - mb = MwMessageBox(handle, call_data, "File chosen", MwMB_ICONERROR | MwMB_BUTTONOK); + mb = MwMessageBox(window, call_data, "File chosen", MwMB_ICONERROR | MwMB_BUTTONOK); (void)user_data; diff --git a/examples/vkdemos/vulkan.c b/examples/vkdemos/vulkan.c index 6015415..9060a97 100644 --- a/examples/vkdemos/vulkan.c +++ b/examples/vkdemos/vulkan.c @@ -257,8 +257,8 @@ void vulkan_setup(MwWidget handle) { swapchainCreateInfo.imageExtent = (VkExtent2D){.width = ow, .height = oh}, swapchainCreateInfo.imageArrayLayers = 1, swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_TRANSFER_DST_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT; + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT; // th is how we specify no transformation. swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; diff --git a/include/Mw/Abstract/CharSet.h b/include/Mw/Abstract/CharSet.h index f0a42f7..0d92ec4 100644 --- a/include/Mw/Abstract/CharSet.h +++ b/include/Mw/Abstract/CharSet.h @@ -11,9 +11,9 @@ extern "C" { #endif -MWDECL char* MWAPI MwACPToUTF8(const char* input); +MWDECL char* MWAPI MwACPTextToUTF8Text(const char* input); -MWDECL char* MWAPI MwUTF8ToACP(const char* input); +MWDECL char* MWAPI MwUTF8TextToACPText(const char* input); #ifdef __cplusplus } diff --git a/include/Mw/Unicode.h b/include/Mw/Unicode.h index 32edeaa..781fad2 100644 --- a/include/Mw/Unicode.h +++ b/include/Mw/Unicode.h @@ -20,6 +20,22 @@ extern "C" { */ MWDECL int MWAPI MwUTF8ToUTF32(const char* input, int* output); +/*! + * @brief Converts UTF-8 to UTF-16 + * @brief input Input + * @brief output Output + * @return Bytes this multibyte takes + * @note Output gets NUL terminated + */ +MWDECL int MWAPI MwUTF8ToUTF16(const char* input, wchar_t* output); + +/*! + * @brief Converts UTF-8 text to UTF-16 text + * @brief input Input + * @return Output + */ +MWDECL wchar_t* MWAPI MwUTF8TextToUTF16Text(const char* input); + /*! * @brief Calculates UTF-8 string length * @brief input Input @@ -38,11 +54,33 @@ MWDECL int MWAPI MwUTF8Length(const char* input); */ MWDECL int MWAPI MwUTF8Copy(const char* src, int srcskip, char* dst, int dstskip, int len); +/*! + * @brief Returns the count current word takes + * @brief input Input + * @return Count + */ +MWDECL int MWAPI MwUTF16Count(const wchar_t* input); + +/*! + * @brief Converts UTF-16 to UTF-8 + * @brief input Input + * @brief output Output + * @return Bytes this multibyte takes + */ +MWDECL int MWAPI MwUTF16ToUTF8(const wchar_t* input, char* output); + +/*! + * @brief Converts UTF-16 text to UTF-8 text + * @brief input Input + * @return Output + */ +MWDECL char* MWAPI MwUTF16TextToUTF8Text(const wchar_t* input); + /*! * @brief Converts UTF-32 to UTF-8 * @brief input Input * @brief output Output - * @return Bytes this wide byte takes + * @return Bytes this multibyte takes */ MWDECL int MWAPI MwUTF32ToUTF8(int input, char* output); diff --git a/pl/rules.pl b/pl/rules.pl index 6c4df63..0fe264c 100644 --- a/pl/rules.pl +++ b/pl/rules.pl @@ -67,7 +67,8 @@ if (grep(/^wayland$/, @backends)) { scan_wayland_protocol("unstable", "pointer-constraints", "-unstable-v1"); scan_wayland_protocol("unstable", "relative-pointer", "-unstable-v1"); scan_wayland_protocol("staging", "fifo", "-v1"); - scan_wayland_protocol_from_file("wlr-layer-shell", "wlr-layer-shell-unstable-v1.xml"); + scan_wayland_protocol_from_file("wlr-layer-shell", + "wlr-layer-shell-unstable-v1.xml"); $gl_libs = "-lGL -lGLU"; } diff --git a/src/abstract/charset.c b/src/abstract/charset.c index 3bd7625..2c339d5 100644 --- a/src/abstract/charset.c +++ b/src/abstract/charset.c @@ -1,21 +1,16 @@ #include -#ifndef CP_UTF8 -#define CP_UTF8 65001 -#endif - -char* MwACPToUTF8(const char* input) { +char* MwACPTextToUTF8Text(const char* input) { #if defined(_WIN32) && defined(MW_HAS_WCHAR) int mbbytes = (strlen(input) + 1) * 4; int wbytes = 0; int len; wchar_t* wout; - char* mbout = malloc(mbbytes); + char* mbout; wbytes = MultiByteToWideChar(CP_ACP, 0, input, strlen(input), NULL, 0) * sizeof(wchar_t); if(wbytes == 0) { - free(mbout); return MwStringDuplicate(input); } @@ -23,23 +18,15 @@ char* MwACPToUTF8(const char* input) { len = wbytes / sizeof(wchar_t); memset(wout, 0, wbytes); - memset(mbout, 0, mbbytes); len = MultiByteToWideChar(CP_ACP, 0, input, strlen(input), wout, len); if(len == 0) { - free(mbout); free(wout); return MwStringDuplicate(input); } wout[len] = 0; - len = WideCharToMultiByte(CP_UTF8, 0, wout, len, mbout, mbbytes, 0, 0); - if(len == 0) { - free(mbout); - free(wout); - return MwStringDuplicate(input); - } - mbout[len] = 0; + mbout = MwUTF16TextToUTF8Text(wout); free(wout); @@ -49,7 +36,7 @@ char* MwACPToUTF8(const char* input) { #endif } -char* MwUTF8ToACP(const char* input) { +char* MwUTF8TextToACPText(const char* input) { #if defined(_WIN32) && defined(MW_HAS_WCHAR) int mbbytes = (strlen(input) + 1) * 4; int wbytes = 0; @@ -58,25 +45,8 @@ char* MwUTF8ToACP(const char* input) { wchar_t* wout; char* mbout = malloc(mbbytes); - wbytes = MultiByteToWideChar(CP_UTF8, 0, input, strlen(input), NULL, 0) * sizeof(wchar_t); - if(wbytes == 0) { - free(mbout); - return MwStringDuplicate(input); - } - - wout = malloc(wbytes + sizeof(wchar_t)); - len = wbytes / sizeof(wchar_t); - - memset(wout, 0, wbytes); - memset(mbout, 0, mbbytes); - - len = MultiByteToWideChar(CP_UTF8, 0, input, strlen(input), wout, len); - if(len == 0) { - free(mbout); - free(wout); - return MwStringDuplicate(input); - } - wout[len] = 0; + wout = MwUTF8TextToUTF16Text(input); + len = wcslen(wout); len = WideCharToMultiByte(CP_ACP, 0, wout, len, mbout, mbbytes, 0, 0); if(len == 0) { diff --git a/src/abstract/directory.c b/src/abstract/directory.c index 977f2f5..d7b364f 100644 --- a/src/abstract/directory.c +++ b/src/abstract/directory.c @@ -184,8 +184,8 @@ static void MwDirectoryJoinSingle(char* target, char* p) { b[0] = DIRSEP; b[1] = 0; - MwStringConcat(target, b); - MwStringConcat(target, p); + strcat(target, b); + strcat(target, p); } for(i = strlen(target) - 1; i >= 0; i--) { @@ -201,7 +201,7 @@ static void MwDirectoryJoinSingle(char* target, char* p) { b[0] = DIRSEP; b[1] = 0; - MwStringConcat(target, b); + strcat(target, b); } } diff --git a/src/backend/cairo.c b/src/backend/cairo.c index d70de1b..65380fb 100644 --- a/src/backend/cairo.c +++ b/src/backend/cairo.c @@ -212,8 +212,8 @@ static void MwLLFreeColorImpl(MwLLColor color) {} static MwBool lmao = MwFALSE; static int MwLLPendingImpl(MwLL handle) { - lmao = !lmao; - return lmao; + lmao = !lmao; + return lmao; } static void MwLLNextEventImpl(MwLL handle) { MwLLDispatch(handle, draw, NULL); @@ -257,10 +257,10 @@ static void MwLLRaiseImpl(MwLL handle) {} static void MwLLClipImpl(MwLL handle, MwRect* rect) {} static void MwLLSetupDragAndDropImpl(MwLL handle) {} static int MwLLCairoCallInitImpl(void) { - if(cairo_load_funcs() != 0) { - return 1; - } - return 0; + if(cairo_load_funcs() != 0) { + return 1; + } + return 0; } #include "call.c" CALL(Cairo); diff --git a/src/dialog/filechooser.c b/src/dialog/filechooser.c index 138fc6c..b1fcc9b 100644 --- a/src/dialog/filechooser.c +++ b/src/dialog/filechooser.c @@ -469,7 +469,7 @@ static void scan(MwWidget handle, const char* path, int record) { if(strcmp(fc->entries[i]->name, ".") == 0 || strcmp(fc->entries[i]->name, "..") == 0) continue; if(fc->entries[i]->type == MwDIRECTORY_DIRECTORY) { char date[128]; - char* n = MwACPToUTF8(fc->entries[i]->name); + char* n = MwACPTextToUTF8Text(fc->entries[i]->name); MwStringTime(date, fc->entries[i]->mtime); @@ -487,7 +487,7 @@ static void scan(MwWidget handle, const char* path, int record) { if(fc->entries[i]->type == MwDIRECTORY_FILE && !fc->dir_only) { char date[128]; char size[128]; - char* n = MwACPToUTF8(fc->entries[i]->name); + char* n = MwACPTextToUTF8Text(fc->entries[i]->name); MwStringTime(date, fc->entries[i]->mtime); MwStringSize(size, fc->entries[i]->size); diff --git a/src/text/gdi.c b/src/text/gdi.c index 97e6870..91ccac9 100644 --- a/src/text/gdi.c +++ b/src/text/gdi.c @@ -5,6 +5,8 @@ static HANDLE(WINAPI* _AddFontMemResourceEx)(PVOID pFileView, DWORD cjSize, PVOI static BOOL(WINAPI* _RemoveFontMemResourceEx)(HANDLE h) = NULL; static HANDLE WINAPI _AddFontMemResourceExPolyFill(PVOID pFileView, DWORD cjSize, PVOID pvResrved, DWORD* pNumFonts); static BOOL WINAPI _RemoveFontMemResourceExPolyFill(HANDLE h); +static BOOL(WINAPI* _TextOutW)(HDC hdc, int x, int y, LPCWSTR lpString, int c); +static BOOL(WINAPI* _GetTextExtentPoint32W)(HDC hdc, LPCWSTR lpString, int c, LPSIZE psizl); struct _MwFLFont { HANDLE handle; @@ -19,8 +21,9 @@ static int GDI_MwDrawText(MwWidget handle, MwFLFont ttf, MwPoint* point, const c HBITMAP hbm; RGBQUAD* bits = NULL; HDC hmdc; - char* t = MwUTF8ToACP(text); unsigned char* px; + char* t; + wchar_t* t16; int y, x; MwRect r; MwLLPixmap p; @@ -33,14 +36,20 @@ static int GDI_MwDrawText(MwWidget handle, MwFLFont ttf, MwPoint* point, const c int old_bkmode = SetBkMode(handle->lowlevel->gdi.hDC, TRANSPARENT); COLORREF old_color = SetTextColor(handle->lowlevel->gdi.hDC, RGB(color->common.red, color->common.green, color->common.blue)); - TextOut(handle->lowlevel->gdi.hDC, point->x, point->y - th / 2, t, strlen(t)); + if(_TextOutW == NULL) { + t = MwUTF8TextToACPText(text); + TextOutA(handle->lowlevel->gdi.hDC, point->x, point->y - th / 2, t, strlen(t)); + free(t); + } else { + t16 = MwUTF8TextToUTF16Text(text); + TextOutW(handle->lowlevel->gdi.hDC, point->x, point->y - th / 2, t16, wcslen(t16)); + free(t16); + } SetTextColor(handle->lowlevel->gdi.hDC, old_color); SetBkMode(handle->lowlevel->gdi.hDC, old_bkmode); SelectObject(handle->lowlevel->gdi.hDC, old_font); - free(t); - return 0; } @@ -64,7 +73,15 @@ static int GDI_MwDrawText(MwWidget handle, MwFLFont ttf, MwPoint* point, const c SelectObject(hmdc, hbm); SelectObject(hmdc, ttf->font); - TextOut(hmdc, 0, 0, t, strlen(t)); + if(_TextOutW == NULL) { + t = MwUTF8TextToACPText(text); + TextOutA(handle->lowlevel->gdi.hDC, point->x, point->y - th / 2, t, strlen(t)); + free(t); + } else { + t16 = MwUTF8TextToUTF16Text(text); + TextOutW(handle->lowlevel->gdi.hDC, point->x, point->y - th / 2, t16, wcslen(t16)); + free(t16); + } DeleteDC(hmdc); @@ -90,18 +107,22 @@ static int GDI_MwDrawText(MwWidget handle, MwFLFont ttf, MwPoint* point, const c free(px); DeleteObject(hbm); - free(t); return 0; } static int GDI_MwTextWidth(MwFLFont ttf, const char* text) { - char* t = MwUTF8ToACP(text); - SIZE sz; + SIZE sz; - GetTextExtentPoint32(ttf->dc, t, strlen(t), &sz); - - free(t); + if(_GetTextExtentPoint32W == NULL) { + char* t = MwUTF8TextToACPText(text); + GetTextExtentPoint32A(ttf->dc, t, strlen(t), &sz); + free(t); + } else { + wchar_t* t16 = MwUTF8TextToUTF16Text(text); + _GetTextExtentPoint32W(ttf->dc, t16, wcslen(t16), &sz); + free(t16); + } return sz.cx; } @@ -255,6 +276,9 @@ int MwFL_GDISetup(void) { if(!_RemoveFontMemResourceEx) { _RemoveFontMemResourceEx = _RemoveFontMemResourceExPolyFill; } + + _TextOutW = (void*)GetProcAddress(gdilib, "TextOutW"); + _GetTextExtentPoint32W = (void*)GetProcAddress(gdilib, "GetTextExtentPoint32W"); } MwFLDrawText = GDI_MwDrawText; diff --git a/src/unicode.c b/src/unicode.c index f6c7685..979fd31 100644 --- a/src/unicode.c +++ b/src/unicode.c @@ -58,6 +58,50 @@ int MwUTF8ToUTF32(const char* input, int* output) { return b; } +int MwUTF8ToUTF16(const char* input, wchar_t* output) { + int o; + int n = MwUTF8ToUTF32(input, &o); + + if(n == 0 || n >= 0x10ffff) return 0; + + if(o < 0x10000) { + output[0] = o; + output[1] = 0; + } else { + output[0] = (o - 0x10000) / 0x400 + 0xd800; + output[1] = (o - 0x10000) % 0x400 + 0xdc00; + output[2] = 0; + } + + return n; +} + +wchar_t* MwUTF8TextToUTF16Text(const char* input) { + wchar_t* o = malloc((MwUTF8Length(input) * 2 + 1) * sizeof(*o)); + int n = 0; + wchar_t buffer[3]; + + o[0] = 0; + + while(input[0] != 0) { + int new = MwUTF8ToUTF16(input, buffer); + + if(new == 0) { + free(o); + return NULL; + } + + memcpy(o + n, buffer, wcslen(buffer) * sizeof(*o)); + + input += new; + n += wcslen(buffer); + } + + o[n] = 0; + + return o; +} + int MwUTF8Length(const char* input) { int out; int len = 0; @@ -97,6 +141,80 @@ int MwUTF8Copy(const char* src, int srcskip, char* dst, int dstskip, int len) { return total; } +static int is_u16_high(wchar_t ch) { + return 0xd800 <= ch && ch < 0xdc00; +} + +static int is_u16_low(wchar_t ch) { + return 0xdc00 <= ch && ch < 0xe000; +} + +int MwUTF16Count(const wchar_t* input) { + if(is_u16_high(input[0])) { + if(is_u16_low(input[1])) { + return 2; + } else if(input[1] == 0) { + return 2; + } else { + return 0; + } + } else if(is_u16_low(input[0])) { + if(input[1] == 0) { + return 2; + } else { + return 0; + } + } else { + return 1; + } +} + +int MwUTF16ToUTF8(const wchar_t* input, char* output) { + if(is_u16_high(input[0])) { + if(is_u16_low(input[1])) { + return MwUTF32ToUTF8(0x10000 + (CAST_I32(input[0]) - 0xd800) * 0x400 + (CAST_I32(input[1]) - 0xdc00), output); + } else if(input[1] == 0) { + return MwUTF32ToUTF8(input[0], output); + } else { + return 0; + } + } else if(is_u16_low(input[0])) { + if(input[1] == 0) { + return MwUTF32ToUTF8(input[0], output); + } else { + return 0; + } + } else { + return MwUTF32ToUTF8(input[0], output); + } +} + +char* MwUTF16TextToUTF8Text(const wchar_t* input) { + char* o = malloc(4 * wcslen(input) + 1); + int n = 0; + char buffer[4]; + + o[0] = 0; + + while(input[0]) { + int new = MwUTF16ToUTF8(input, buffer); + + if(new == 0) { + free(o); + return NULL; + } + + memcpy(o + n, buffer, new); + + input += MwUTF16Count(input); + n += new; + } + + o[n] = 0; + + return o; +} + int MwUTF32ToUTF8(int input, char* output) { if(input < 128) { output[0] = input;