From 68423b35cf5bb85de446841d33e1ecaa97f1c1ab Mon Sep 17 00:00:00 2001 From: rubidium Date: Wed, 13 Nov 2013 21:35:44 +0000 Subject: [PATCH] (svn r25982) [1.3] -Backport from trunk: - Fix: Textbuf caret rendering for complex scripts (e.g. Tamil) (r25696, r25694, r25652, r25651, r25092, r25091) - Fix: Vehicle::MarkDirty must be called for the front engine [FS#5700] (r25695) - Fix: [Win32] Several issues regarding conversion of characters (r25677, r25676, r25675, r25674, r25673) - Fix: [Win32] Handle Unicode characters from outside the BMP correctly (r25672, r25670, r25669, r25668) --- src/bridge_gui.cpp | 2 +- src/console_gui.cpp | 2 +- src/debug.cpp | 11 ++- src/error_gui.cpp | 2 +- src/fios_gui.cpp | 2 +- src/fontdetection.cpp | 65 ++++--------- src/gfx_layout.cpp | 14 ++- src/gfx_layout.h | 2 + src/highscore_gui.cpp | 2 +- src/hotkeys.cpp | 2 +- src/hotkeys.h | 3 +- src/misc_gui.cpp | 2 +- src/network/network_chat_gui.cpp | 2 +- src/network/network_content_gui.cpp | 2 +- src/network/network_gui.cpp | 2 +- src/newgrf_gui.cpp | 2 +- src/news_gui.cpp | 2 +- src/os/windows/crashlog_win.cpp | 14 +-- src/os/windows/win32.cpp | 145 +++++++++++----------------- src/os/windows/win32.h | 15 +-- src/stdafx.h | 27 ++++-- src/textbuf.cpp | 2 +- src/textbuf_type.h | 2 +- src/vehicle.cpp | 4 + src/video/win32_v.cpp | 79 ++++++++++++--- src/window.cpp | 2 +- src/window_gui.h | 5 +- 27 files changed, 208 insertions(+), 206 deletions(-) diff --git a/src/bridge_gui.cpp b/src/bridge_gui.cpp index 4a349b6050..47105712c7 100644 --- a/src/bridge_gui.cpp +++ b/src/bridge_gui.cpp @@ -249,7 +249,7 @@ public: } } - virtual EventState OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(WChar key, uint16 keycode) { const uint8 i = keycode - '1'; if (i < 9 && i < this->bridges->Length()) { diff --git a/src/console_gui.cpp b/src/console_gui.cpp index ca0a01853d..ffadbb4072 100644 --- a/src/console_gui.cpp +++ b/src/console_gui.cpp @@ -236,7 +236,7 @@ struct IConsoleWindow : Window if (_iconsole_cmdline.HandleCaret()) this->SetDirty(); } - virtual EventState OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(WChar key, uint16 keycode) { if (_focused_window != this) return ES_NOT_HANDLED; diff --git a/src/debug.cpp b/src/debug.cpp index e6fb61292e..d173ff14c4 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -131,13 +131,14 @@ static void debug_print(const char *dbg, const char *buf) fflush(f); #endif } else { + char buffer[512]; + seprintf(buffer, lastof(buffer), "%sdbg: [%s] %s\n", GetLogPrefix(), dbg, buf); #if defined(WINCE) - /* We need to do OTTD2FS twice, but as it uses a static buffer, we need to store one temporary */ - TCHAR tbuf[512]; - _sntprintf(tbuf, sizeof(tbuf), _T("%s"), OTTD2FS(dbg)); - NKDbgPrintfW(_T("dbg: [%s] %s\n"), tbuf, OTTD2FS(buf)); + NKDbgPrintfW(OTTD2FS(buffer)); +#elif defined(WIN32) || defined(WIN64) + _fputts(OTTD2FS(buffer, true), stderr); #else - fprintf(stderr, "%sdbg: [%s] %s\n", GetLogPrefix(), dbg, buf); + fputs(buffer, stderr); #endif #ifdef ENABLE_NETWORK NetworkAdminConsole(dbg, buf); diff --git a/src/error_gui.cpp b/src/error_gui.cpp index db73d31b2f..8862178083 100644 --- a/src/error_gui.cpp +++ b/src/error_gui.cpp @@ -300,7 +300,7 @@ public: if (_window_system_initialized) ShowFirstError(); } - virtual EventState OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(WChar key, uint16 keycode) { if (keycode != WKC_SPACE) return ES_NOT_HANDLED; delete this; diff --git a/src/fios_gui.cpp b/src/fios_gui.cpp index 498798b13e..a4619b0d45 100644 --- a/src/fios_gui.cpp +++ b/src/fios_gui.cpp @@ -615,7 +615,7 @@ public: } } - virtual EventState OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(WChar key, uint16 keycode) { if (keycode == WKC_ESC) { delete this; diff --git a/src/fontdetection.cpp b/src/fontdetection.cpp index 2a847933f0..82c4f354f6 100644 --- a/src/fontdetection.cpp +++ b/src/fontdetection.cpp @@ -42,26 +42,19 @@ extern FT_Library _library; * filename into something that isn't UTF-8 but represents the Unicode file * name. This is the short DOS 8.3 format. This does not contain any * characters that fopen doesn't support. - * @param long_path the path in UTF-8. + * @param long_path the path in system encoding. * @return the short path in ANSI (ASCII). */ -char *GetShortPath(const char *long_path) +const char *GetShortPath(const TCHAR *long_path) { static char short_path[MAX_PATH]; #ifdef UNICODE - /* The non-unicode GetShortPath doesn't support UTF-8..., - * so convert the path to wide chars, then get the short - * path and convert it back again. */ - wchar_t long_path_w[MAX_PATH]; - MultiByteToWideChar(CP_UTF8, 0, long_path, -1, long_path_w, MAX_PATH); - - wchar_t short_path_w[MAX_PATH]; - GetShortPathNameW(long_path_w, short_path_w, MAX_PATH); - - WideCharToMultiByte(CP_ACP, 0, short_path_w, -1, short_path, MAX_PATH, NULL, NULL); + WCHAR short_path_w[MAX_PATH]; + GetShortPathName(long_path, short_path_w, lengthof(short_path_w)); + WideCharToMultiByte(CP_ACP, 0, short_path_w, -1, short_path, lengthof(short_path), NULL, NULL); #else /* Technically not needed, but do it for consistency. */ - GetShortPathNameA(long_path, short_path, MAX_PATH); + GetShortPathName(long_path, short_path, lengthof(short_path)); #endif return short_path; } @@ -82,9 +75,10 @@ FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) HKEY hKey; LONG ret; TCHAR vbuffer[MAX_PATH], dbuffer[256]; - TCHAR *font_namep; - char *font_path; + TCHAR *pathbuf; + const char *font_path; uint index; + size_t path_len; /* On windows NT (2000, NT3.5, XP, etc.) the fonts are stored in the * "Windows NT" key, on Windows 9x in the Windows key. To save us having @@ -97,15 +91,8 @@ FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) return err; } - /* For Unicode we need some conversion between widechar and - * normal char to match the data returned by RegEnumValue, - * otherwise just use parameter */ -#if defined(UNICODE) - font_namep = MallocT(MAX_PATH); - MB_TO_WIDE_BUFFER(font_name, font_namep, MAX_PATH * sizeof(TCHAR)); -#else - font_namep = const_cast(font_name); // only cast because in unicode pointer is not const -#endif + /* Convert font name to file system encoding. */ + TCHAR *font_namep = _tcsdup(OTTD2FS(font_name)); for (index = 0;; index++) { TCHAR *s; @@ -142,23 +129,13 @@ FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) /* Some fonts are contained in .ttc files, TrueType Collection fonts. These * contain multiple fonts inside this single file. GetFontData however * returns the whole file, so we need to check each font inside to get the - * proper font. - * Also note that FreeType does not support UNICODE filenames! */ -#if defined(UNICODE) - /* We need a cast here back from wide because FreeType doesn't support - * widechar filenames. Just use the buffer we allocated before for the - * font_name search */ - font_path = (char*)font_namep; - WIDE_TO_MB_BUFFER(vbuffer, font_path, MAX_PATH * sizeof(TCHAR)); -#else - font_path = vbuffer; -#endif + * proper font. */ + path_len = _tcslen(vbuffer) + _tcslen(dbuffer) + 2; // '\' and terminating nul. + pathbuf = AllocaM(TCHAR, path_len); + _sntprintf(pathbuf, path_len, _T("%s\\%s"), vbuffer, dbuffer); - ttd_strlcat(font_path, "\\", MAX_PATH * sizeof(TCHAR)); - ttd_strlcat(font_path, WIDE_TO_MB(dbuffer), MAX_PATH * sizeof(TCHAR)); - - /* Convert the path into something that FreeType understands */ - font_path = GetShortPath(font_path); + /* Convert the path into something that FreeType understands. */ + font_path = GetShortPath(pathbuf); index = 0; do { @@ -175,9 +152,7 @@ FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) folder_error: registry_no_font_found: -#if defined(UNICODE) free(font_namep); -#endif RegCloseKey(hKey); return err; } @@ -338,11 +313,7 @@ static int CALLBACK EnumFontCallback(const ENUMLOGFONTEX *logfont, const NEWTEXT } char font_name[MAX_PATH]; -#if defined(UNICODE) - WIDE_TO_MB_BUFFER((const TCHAR*)logfont->elfFullName, font_name, lengthof(font_name)); -#else - strecpy(font_name, (const TCHAR*)logfont->elfFullName, lastof(font_name)); -#endif + convert_from_fs((const TCHAR *)logfont->elfFullName, font_name, lengthof(font_name)); /* Add english name after font name */ const char *english_name = GetEnglishFontName(logfont); diff --git a/src/gfx_layout.cpp b/src/gfx_layout.cpp index 165edee3b6..f3a7335b96 100644 --- a/src/gfx_layout.cpp +++ b/src/gfx_layout.cpp @@ -157,6 +157,7 @@ ParagraphLayout::VisualRun::VisualRun(Font *font, const WChar *chars, int char_c font(font), glyph_count(char_count) { this->glyphs = MallocT(this->glyph_count); + this->glyph_to_char = MallocT(this->glyph_count); /* Positions contains the location of the begin of each of the glyphs, and the end of the last one. */ this->positions = MallocT(this->glyph_count * 2 + 2); @@ -167,6 +168,7 @@ ParagraphLayout::VisualRun::VisualRun(Font *font, const WChar *chars, int char_c this->glyphs[i] = font->fc->MapCharToGlyph(chars[i]); this->positions[2 * i + 2] = this->positions[2 * i] + font->fc->GetGlyphWidth(this->glyphs[i]); this->positions[2 * i + 3] = 0; + this->glyph_to_char[i] = i; } } @@ -174,6 +176,7 @@ ParagraphLayout::VisualRun::VisualRun(Font *font, const WChar *chars, int char_c ParagraphLayout::VisualRun::~VisualRun() { free(this->positions); + free(this->glyph_to_char); free(this->glyphs); } @@ -213,6 +216,15 @@ float *ParagraphLayout::VisualRun::getPositions() const return this->positions; } +/** + * Get the glyph-to-character map for this visual run. + * @return The glyph-to-character map. + */ +const int *ParagraphLayout::VisualRun::getGlyphToCharMap() const +{ + return this->glyph_to_char; +} + /** * Get the height of this font. * @return The height of the font. @@ -554,7 +566,7 @@ Point Layouter::GetCharPosition(const char *ch) const for (int i = 0; i < run->getGlyphCount(); i++) { /* Matching glyph? Return position. */ if ((size_t)run->getGlyphToCharMap()[i] == index) { - Point p = { run->getPositions()[i * 2], run->getPositions()[i * 2 + 1] }; + Point p = { (int)run->getPositions()[i * 2], (int)run->getPositions()[i * 2 + 1] }; return p; } } diff --git a/src/gfx_layout.h b/src/gfx_layout.h index 27c1e9c082..8b5d0e8faf 100644 --- a/src/gfx_layout.h +++ b/src/gfx_layout.h @@ -125,6 +125,7 @@ public: Font *font; ///< The font used to layout these. GlyphID *glyphs; ///< The glyphs we're drawing. float *positions; ///< The positions of the glyphs. + int *glyph_to_char; ///< The char index of the glyphs. int glyph_count; ///< The number of glyphs. public: @@ -135,6 +136,7 @@ public: const GlyphID *getGlyphs() const; float *getPositions() const; int getLeading() const; + const int *getGlyphToCharMap() const; }; /** A single line worth of VisualRuns. */ diff --git a/src/highscore_gui.cpp b/src/highscore_gui.cpp index 667ef85da1..5be479f452 100644 --- a/src/highscore_gui.cpp +++ b/src/highscore_gui.cpp @@ -63,7 +63,7 @@ struct EndGameHighScoreBaseWindow : Window { delete this; } - virtual EventState OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(WChar key, uint16 keycode) { /* All keys are 'handled' by this window but we want to make * sure that 'quit' still works correctly. Not handling the diff --git a/src/hotkeys.cpp b/src/hotkeys.cpp index a41ede7e4b..373cc1c58f 100644 --- a/src/hotkeys.cpp +++ b/src/hotkeys.cpp @@ -324,7 +324,7 @@ GlobalHotkeyHandler *_global_hotkey_handlers_editor[] = { }; -void HandleGlobalHotkeys(uint16 key, uint16 keycode) +void HandleGlobalHotkeys(WChar key, uint16 keycode) { if (_game_mode == GM_NORMAL) { for (uint i = 0; i < lengthof(_global_hotkey_handlers); i++) { diff --git a/src/hotkeys.h b/src/hotkeys.h index b87787e1bb..c8f967fd31 100644 --- a/src/hotkeys.h +++ b/src/hotkeys.h @@ -14,6 +14,7 @@ #include "core/smallvec_type.hpp" #include "gfx_type.h" +#include "string_type.h" /** * All data for a single hotkey. The name (for saving/loading a configfile), @@ -130,6 +131,6 @@ void LoadHotkeysFromConfig(); void SaveHotkeysToConfig(); -void HandleGlobalHotkeys(uint16 key, uint16 keycode); +void HandleGlobalHotkeys(WChar key, uint16 keycode); #endif /* HOTKEYS_H */ diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp index 9cea9f8762..5c7454a741 100644 --- a/src/misc_gui.cpp +++ b/src/misc_gui.cpp @@ -1016,7 +1016,7 @@ struct QueryWindow : public Window { } } - virtual EventState OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(WChar key, uint16 keycode) { /* ESC closes the window, Enter confirms the action */ switch (keycode) { diff --git a/src/network/network_chat_gui.cpp b/src/network/network_chat_gui.cpp index ae59995c7c..cf3ae6cbe3 100644 --- a/src/network/network_chat_gui.cpp +++ b/src/network/network_chat_gui.cpp @@ -491,7 +491,7 @@ struct NetworkChatWindow : public Window { } } - virtual EventState OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(WChar key, uint16 keycode) { EventState state = ES_NOT_HANDLED; if (keycode == WKC_TAB) { diff --git a/src/network/network_content_gui.cpp b/src/network/network_content_gui.cpp index a7c503a608..97046b1f58 100644 --- a/src/network/network_content_gui.cpp +++ b/src/network/network_content_gui.cpp @@ -799,7 +799,7 @@ public: } } - virtual EventState OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(WChar key, uint16 keycode) { switch (keycode) { case WKC_UP: diff --git a/src/network/network_gui.cpp b/src/network/network_gui.cpp index f93b31c773..3cb0d818b9 100644 --- a/src/network/network_gui.cpp +++ b/src/network/network_gui.cpp @@ -793,7 +793,7 @@ public: this->SetDirty(); } - virtual EventState OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(WChar key, uint16 keycode) { EventState state = ES_NOT_HANDLED; diff --git a/src/newgrf_gui.cpp b/src/newgrf_gui.cpp index 1a78cd53e2..cf79f407f0 100644 --- a/src/newgrf_gui.cpp +++ b/src/newgrf_gui.cpp @@ -1234,7 +1234,7 @@ struct NewGRFWindow : public Window, NewGRFScanCallback { this->SetWidgetDisabledState(WID_NS_PRESET_SAVE, has_missing); } - virtual EventState OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(WChar key, uint16 keycode) { if (!this->editable) return ES_NOT_HANDLED; diff --git a/src/news_gui.cpp b/src/news_gui.cpp index a0c9bdbd2c..9920ec6b58 100644 --- a/src/news_gui.cpp +++ b/src/news_gui.cpp @@ -449,7 +449,7 @@ struct NewsWindow : Window { } } - virtual EventState OnKeyPress(uint16 key, uint16 keycode) + virtual EventState OnKeyPress(WChar key, uint16 keycode) { if (keycode == WKC_SPACE) { /* Don't continue. */ diff --git a/src/os/windows/crashlog_win.cpp b/src/os/windows/crashlog_win.cpp index 646b06f2b3..41f83b60ba 100644 --- a/src/os/windows/crashlog_win.cpp +++ b/src/os/windows/crashlog_win.cpp @@ -10,12 +10,6 @@ /** @file crashlog_win.cpp Implementation of a crashlogger for Windows */ #include "../../stdafx.h" -#if defined(_MSC_VER) && defined(_M_AMD64) -/* Redefine WinNT version to get RtlCaptureContext prototype. */ -#undef _WIN32_WINNT -#undef NTDDI_VERSION -#define _WIN32_WINNT _WIN32_WINNT_WINXP -#endif /* defined(_MSC_VER) && defined(_M_AMD64) */ #include "../../crashlog.h" #include "win32.h" #include "../../core/alloc_func.hpp" @@ -197,7 +191,7 @@ static char *PrintModuleInfo(char *output, const char *last, HMODULE mod) GetModuleFileName(mod, buffer, MAX_PATH); GetFileInfo(&dfi, buffer); output += seprintf(output, last, " %-20s handle: %p size: %d crc: %.8X date: %d-%.2d-%.2d %.2d:%.2d:%.2d\n", - WIDE_TO_MB(buffer), + FS2OTTD(buffer), mod, dfi.size, dfi.crc32, @@ -621,11 +615,9 @@ static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARA { switch (msg) { case WM_INITDIALOG: { -#if defined(UNICODE) /* We need to put the crash-log in a separate buffer because the default * buffer in MB_TO_WIDE is not large enough (512 chars) */ - wchar_t crash_msgW[lengthof(CrashLogWindows::current->crashlog)]; -#endif + TCHAR crash_msgW[lengthof(CrashLogWindows::current->crashlog)]; /* Convert unix -> dos newlines because the edit box only supports that properly :( */ const char *unix_nl = CrashLogWindows::current->crashlog; char dos_nl[lengthof(CrashLogWindows::current->crashlog)]; @@ -655,7 +647,7 @@ static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARA } SetDlgItemText(wnd, 10, text); - SetDlgItemText(wnd, 11, MB_TO_WIDE_BUFFER(dos_nl, crash_msgW, lengthof(crash_msgW))); + SetDlgItemText(wnd, 11, convert_to_fs(dos_nl, crash_msgW, lengthof(crash_msgW))); SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE); SetWndSize(wnd, -1); } return TRUE; diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp index 3bb22288f5..93f4e6ce37 100644 --- a/src/os/windows/win32.cpp +++ b/src/os/windows/win32.cpp @@ -78,12 +78,12 @@ bool LoadLibraryList(Function proc[], const char *dll) void ShowOSErrorBox(const char *buf, bool system) { MyShowCursor(true); - MessageBox(GetActiveWindow(), MB_TO_WIDE(buf), _T("Error!"), MB_ICONSTOP); + MessageBox(GetActiveWindow(), OTTD2FS(buf), _T("Error!"), MB_ICONSTOP); } void OSOpenBrowser(const char *url) { - ShellExecute(GetActiveWindow(), _T("open"), MB_TO_WIDE(url), NULL, NULL, SW_SHOWNORMAL); + ShellExecute(GetActiveWindow(), _T("open"), OTTD2FS(url), NULL, NULL, SW_SHOWNORMAL); } /* Code below for windows version of opendir/readdir/closedir copied and @@ -371,12 +371,10 @@ static INT_PTR CALLBACK HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM *q++ = *p++; } *q = '\0'; -#if defined(UNICODE) /* We need to put the text in a separate buffer because the default - * buffer in MB_TO_WIDE might not be large enough (512 chars) */ - wchar_t help_msgW[8192]; -#endif - SetDlgItemText(wnd, 11, MB_TO_WIDE_BUFFER(help_msg, help_msgW, lengthof(help_msgW))); + * buffer in OTTD2FS might not be large enough (512 chars). */ + TCHAR help_msg_buf[8192]; + SetDlgItemText(wnd, 11, convert_to_fs(help_msg, help_msg_buf, lengthof(help_msg_buf))); SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE); } return TRUE; @@ -407,12 +405,10 @@ void ShowInfo(const char *str) _help_msg = str; DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(101), NULL, HelpDialogFunc); } else { -#if defined(UNICODE) /* We need to put the text in a separate buffer because the default - * buffer in MB_TO_WIDE might not be large enough (512 chars) */ - wchar_t help_msgW[8192]; -#endif - MessageBox(GetActiveWindow(), MB_TO_WIDE_BUFFER(str, help_msgW, lengthof(help_msgW)), _T("OpenTTD"), MB_ICONINFORMATION | MB_OK); + * buffer in OTTD2FS might not be large enough (512 chars). */ + TCHAR help_msg_buf[8192]; + MessageBox(GetActiveWindow(), convert_to_fs(str, help_msg_buf, lengthof(help_msg_buf)), _T("OpenTTD"), MB_ICONINFORMATION | MB_OK); } MyShowCursor(old); } @@ -426,28 +422,18 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi { int argc; char *argv[64]; // max 64 command line arguments - char *cmdline; - -#if !defined(UNICODE) - _codepage = GetACP(); // get system codepage as some kind of a default -#endif /* UNICODE */ CrashLog::InitialiseCrashLog(); -#if defined(UNICODE) - -#if !defined(WINCE) +#if defined(UNICODE) && !defined(WINCE) /* Check if a win9x user started the win32 version */ if (HasBit(GetVersion(), 31)) usererror("This version of OpenTTD doesn't run on windows 95/98/ME.\nPlease download the win9x binary and try again."); #endif - /* For UNICODE we need to convert the commandline to char* _AND_ - * save it because argv[] points into this buffer and thus needs to - * be available between subsequent calls to FS2OTTD() */ - char cmdlinebuf[MAX_PATH]; -#endif /* UNICODE */ - - cmdline = WIDE_TO_MB_BUFFER(GetCommandLine(), cmdlinebuf, lengthof(cmdlinebuf)); + /* Convert the command line to UTF-8. We need a dedicated buffer + * for this because argv[] points into this buffer and this needs to + * be available between subsequent calls to FS2OTTD(). */ + char *cmdline = strdup(FS2OTTD(GetCommandLine())); #if defined(_DEBUG) CreateConsole(); @@ -463,6 +449,7 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi argc = ParseCommandLine(cmdline, argv, lengthof(argv)); ttd_main(argc, argv); + free(cmdline); return 0; } @@ -490,12 +477,10 @@ char *getcwd(char *buf, size_t size) /* GetModuleFileName returns dir with file, so remove everything behind latest '\\' */ char *p = strrchr(buf, '\\'); if (p != NULL) *p = '\0'; -#elif defined(UNICODE) +#else TCHAR path[MAX_PATH]; GetCurrentDirectory(MAX_PATH - 1, path); convert_from_fs(path, buf, size); -#else - GetCurrentDirectory(size, buf); #endif return buf; } @@ -507,7 +492,7 @@ void DetermineBasePaths(const char *exe) TCHAR path[MAX_PATH]; #ifdef WITH_PERSONAL_DIR if (SUCCEEDED(OTTDSHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path))) { - strecpy(tmp, WIDE_TO_MB_BUFFER(path, tmp, lengthof(tmp)), lastof(tmp)); + strecpy(tmp, FS2OTTD(path), lastof(tmp)); AppendPathSeparator(tmp, MAX_PATH); ttd_strlcat(tmp, PERSONAL_DIR, MAX_PATH); AppendPathSeparator(tmp, MAX_PATH); @@ -517,7 +502,7 @@ void DetermineBasePaths(const char *exe) } if (SUCCEEDED(OTTDSHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path))) { - strecpy(tmp, WIDE_TO_MB_BUFFER(path, tmp, lengthof(tmp)), lastof(tmp)); + strecpy(tmp, FS2OTTD(path), lastof(tmp)); AppendPathSeparator(tmp, MAX_PATH); ttd_strlcat(tmp, PERSONAL_DIR, MAX_PATH); AppendPathSeparator(tmp, MAX_PATH); @@ -540,12 +525,12 @@ void DetermineBasePaths(const char *exe) _searchpaths[SP_BINARY_DIR] = NULL; } else { TCHAR exec_dir[MAX_PATH]; - _tcsncpy(path, MB_TO_WIDE_BUFFER(exe, path, lengthof(path)), lengthof(path)); + _tcsncpy(path, convert_to_fs(exe, path, lengthof(path)), lengthof(path)); if (!GetFullPathName(path, lengthof(exec_dir), exec_dir, NULL)) { DEBUG(misc, 0, "GetFullPathName failed (%lu)\n", GetLastError()); _searchpaths[SP_BINARY_DIR] = NULL; } else { - strecpy(tmp, WIDE_TO_MB_BUFFER(exec_dir, tmp, lengthof(tmp)), lastof(tmp)); + strecpy(tmp, convert_from_fs(exec_dir, tmp, lengthof(tmp)), lastof(tmp)); char *s = strrchr(tmp, PATHSEPCHAR); *(s + 1) = '\0'; _searchpaths[SP_BINARY_DIR] = strdup(tmp); @@ -567,11 +552,11 @@ bool GetClipboardContents(char *buffer, size_t buff_len) cbuf = GetClipboardData(CF_UNICODETEXT); ptr = (const char*)GlobalLock(cbuf); - const char *ret = convert_from_fs((const wchar_t*)ptr, buffer, buff_len); + int out_len = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)ptr, -1, buffer, (int)buff_len, NULL, NULL); GlobalUnlock(cbuf); CloseClipboard(); - if (*ret == '\0') return false; + if (out_len == 0) return false; #if !defined(UNICODE) } else if (IsClipboardFormatAvailable(CF_TEXT)) { OpenClipboard(NULL); @@ -613,26 +598,7 @@ void CSleep(int milliseconds) const char *FS2OTTD(const TCHAR *name) { static char utf8_buf[512]; -#if defined(UNICODE) return convert_from_fs(name, utf8_buf, lengthof(utf8_buf)); -#else - char *s = utf8_buf; - - for (; *name != '\0'; name++) { - wchar_t w; - int len = MultiByteToWideChar(_codepage, 0, name, 1, &w, 1); - if (len != 1) { - DEBUG(misc, 0, "[utf8] M2W error converting '%c'. Errno %lu", *name, GetLastError()); - continue; - } - - if (s + Utf8CharLen(w) >= lastof(utf8_buf)) break; - s += Utf8Encode(s, w); - } - - *s = '\0'; - return utf8_buf; -#endif /* UNICODE */ } /** @@ -644,34 +610,13 @@ const char *FS2OTTD(const TCHAR *name) * The returned value's contents can only be guaranteed until the next call to * this function. So if the value is needed for anything else, use convert_from_fs * @param name pointer to a valid string that will be converted (UTF8) + * @param console_cp convert to the console encoding instead of the normal system encoding. * @return pointer to the converted string; if failed string is of zero-length - * @see the current code-page comes from video\win32_v.cpp, event-notification - * WM_INPUTLANGCHANGE */ -const TCHAR *OTTD2FS(const char *name) +const TCHAR *OTTD2FS(const char *name, bool console_cp) { static TCHAR system_buf[512]; -#if defined(UNICODE) - return convert_to_fs(name, system_buf, lengthof(system_buf)); -#else - char *s = system_buf; - - for (WChar c; (c = Utf8Consume(&name)) != '\0';) { - if (s >= lastof(system_buf)) break; - - char mb; - int len = WideCharToMultiByte(_codepage, 0, (wchar_t*)&c, 1, &mb, 1, NULL, NULL); - if (len != 1) { - DEBUG(misc, 0, "[utf8] W2M error converting '0x%X'. Errno %lu", c, GetLastError()); - continue; - } - - *s++ = mb; - } - - *s = '\0'; - return system_buf; -#endif /* UNICODE */ + return convert_to_fs(name, system_buf, lengthof(system_buf), console_cp); } @@ -683,14 +628,26 @@ const TCHAR *OTTD2FS(const char *name) * @param buflen length in characters of the receiving buffer * @return pointer to utf8_buf. If conversion fails the string is of zero-length */ -char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen) +char *convert_from_fs(const TCHAR *name, char *utf8_buf, size_t buflen) { - int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, utf8_buf, (int)buflen, NULL, NULL); - if (len == 0) { - DEBUG(misc, 0, "[utf8] W2M error converting wide-string. Errno %lu", GetLastError()); +#if defined(UNICODE) + const WCHAR *wide_buf = name; +#else + /* Convert string from the local codepage to UTF-16. */ + int wide_len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0); + if (wide_len == 0) { utf8_buf[0] = '\0'; + return utf8_buf; } + WCHAR *wide_buf = AllocaM(WCHAR, wide_len); + MultiByteToWideChar(CP_ACP, 0, name, -1, wide_buf, wide_len); +#endif + + /* Convert UTF-16 string to UTF-8. */ + int len = WideCharToMultiByte(CP_UTF8, 0, wide_buf, -1, utf8_buf, (int)buflen, NULL, NULL); + if (len == 0) utf8_buf[0] = '\0'; + return utf8_buf; } @@ -702,17 +659,29 @@ char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen) * @param utf16_buf pointer to a valid wide-char buffer that will receive the * converted string * @param buflen length in wide characters of the receiving buffer + * @param console_cp convert to the console encoding instead of the normal system encoding. * @return pointer to utf16_buf. If conversion fails the string is of zero-length */ -wchar_t *convert_to_fs(const char *name, wchar_t *utf16_buf, size_t buflen) +TCHAR *convert_to_fs(const char *name, TCHAR *system_buf, size_t buflen, bool console_cp) { - int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, utf16_buf, (int)buflen); +#if defined(UNICODE) + int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, system_buf, (int)buflen); + if (len == 0) system_buf[0] = '\0'; +#else + int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0); if (len == 0) { - DEBUG(misc, 0, "[utf8] M2W error converting '%s'. Errno %lu", name, GetLastError()); - utf16_buf[0] = '\0'; + system_buf[0] = '\0'; + return system_buf; } - return utf16_buf; + WCHAR *wide_buf = AllocaM(WCHAR, len); + MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_buf, len); + + len = WideCharToMultiByte(console_cp ? CP_OEMCP : CP_ACP, 0, wide_buf, len, system_buf, (int)buflen, NULL, NULL); + if (len == 0) system_buf[0] = '\0'; +#endif + + return system_buf; } /** diff --git a/src/os/windows/win32.h b/src/os/windows/win32.h index 941d3ea840..45bcff10ab 100644 --- a/src/os/windows/win32.h +++ b/src/os/windows/win32.h @@ -18,26 +18,19 @@ bool MyShowCursor(bool show, bool toggle = false); typedef void (*Function)(int); bool LoadLibraryList(Function proc[], const char *dll); -char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen); -wchar_t *convert_to_fs(const char *name, wchar_t *utf16_buf, size_t buflen); +char *convert_from_fs(const TCHAR *name, char *utf8_buf, size_t buflen); +TCHAR *convert_to_fs(const char *name, TCHAR *utf16_buf, size_t buflen, bool console_cp = false); /* Function shortcuts for UTF-8 <> UNICODE conversion. When unicode is not * defined these macros return the string passed to them, with UNICODE - * they return a pointer to the converted string. The only difference between - * XX_TO_YY and XX_TO_YY_BUFFER is that with the buffer variant you can - * specify where to put the converted string (and how long it can be). Without - * the buffer and internal buffer is used, of max 512 characters */ + * they return a pointer to the converted string. These functions use an + * internal buffer of max 512 characters. */ #if defined(UNICODE) # define MB_TO_WIDE(str) OTTD2FS(str) -# define MB_TO_WIDE_BUFFER(str, buffer, buflen) convert_to_fs(str, buffer, buflen) # define WIDE_TO_MB(str) FS2OTTD(str) -# define WIDE_TO_MB_BUFFER(str, buffer, buflen) convert_from_fs(str, buffer, buflen) #else -extern uint _codepage; // local code-page in the system @see win32_v.cpp:WM_INPUTLANGCHANGE # define MB_TO_WIDE(str) (str) -# define MB_TO_WIDE_BUFFER(str, buffer, buflen) (str) # define WIDE_TO_MB(str) (str) -# define WIDE_TO_MB_BUFFER(str, buffer, buflen) (str) #endif HRESULT OTTDSHGetFolderPath(HWND, int, HANDLE, DWORD, LPTSTR); diff --git a/src/stdafx.h b/src/stdafx.h index b91466403d..5464c59fa0 100644 --- a/src/stdafx.h +++ b/src/stdafx.h @@ -177,15 +177,24 @@ /* Stuff for MSVC */ #if defined(_MSC_VER) #pragma once - /* Define a win32 target platform, to override defaults of the SDK - * We need to define NTDDI version for Vista SDK, but win2k is minimum */ - #define NTDDI_VERSION NTDDI_WIN2K // Windows 2000 - #define _WIN32_WINNT 0x0500 // Windows 2000 - #define _WIN32_WINDOWS 0x400 // Windows 95 - #if !defined(WINCE) - #define WINVER 0x0400 // Windows NT 4.0 / Windows 95 + #ifdef _WIN64 + /* No 64-bit Windows below XP, so we can safely assume it as the target platform. */ + #define NTDDI_VERSION NTDDI_WINXP // Windows XP + #define _WIN32_WINNT 0x501 // Windows XP + #define _WIN32_WINDOWS 0x501 // Windows XP + #define WINVER 0x0501 // Windows XP + #define _WIN32_IE_ 0x0600 // 6.0 (XP+) + #else + /* Define a win32 target platform, to override defaults of the SDK + * We need to define NTDDI version for Vista SDK, but win2k is minimum */ + #define NTDDI_VERSION NTDDI_WIN2K // Windows 2000 + #define _WIN32_WINNT 0x0500 // Windows 2000 + #define _WIN32_WINDOWS 0x400 // Windows 95 + #if !defined(WINCE) + #define WINVER 0x0400 // Windows NT 4.0 / Windows 95 + #endif + #define _WIN32_IE_ 0x0401 // 4.01 (win98 and NT4SP5+) #endif - #define _WIN32_IE_ 0x0401 // 4.01 (win98 and NT4SP5+) #define NOMINMAX // Disable min/max macros in windows.h. #pragma warning(disable: 4244) // 'conversion' conversion from 'type1' to 'type2', possible loss of data @@ -287,7 +296,7 @@ #endif /* WINCE */ const char *FS2OTTD(const TCHAR *name); - const TCHAR *OTTD2FS(const char *name); + const TCHAR *OTTD2FS(const char *name, bool console_cp = false); #define SQ2OTTD(name) FS2OTTD(name) #define OTTD2SQ(name) OTTD2FS(name) #else diff --git a/src/textbuf.cpp b/src/textbuf.cpp index c7071565ab..58f931f8a5 100644 --- a/src/textbuf.cpp +++ b/src/textbuf.cpp @@ -362,7 +362,7 @@ bool Textbuf::HandleCaret() return false; } -HandleKeyPressResult Textbuf::HandleKeyPress(uint16 key, uint16 keycode) +HandleKeyPressResult Textbuf::HandleKeyPress(WChar key, uint16 keycode) { bool edited = false; diff --git a/src/textbuf_type.h b/src/textbuf_type.h index 4d1a926fbe..5976159909 100644 --- a/src/textbuf_type.h +++ b/src/textbuf_type.h @@ -56,7 +56,7 @@ struct Textbuf { bool DeleteChar(uint16 keycode); bool MovePos(uint16 keycode); - HandleKeyPressResult HandleKeyPress(uint16 key, uint16 keycode); + HandleKeyPressResult HandleKeyPress(WChar key, uint16 keycode); bool HandleCaret(); void UpdateSize(); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index f4376ce7b5..047a73d45a 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -853,6 +853,10 @@ static void RunVehicleDayProc() } if (HasBit(callback, 1)) v->colourmap = PAL_NONE; + /* After a vehicle trigger, the graphics and properties of the vehicle could change. + * Note: MarkDirty also invalidates the palette, which is the meaning of bit 1. So, nothing special there. */ + if (callback != 0) v->First()->MarkDirty(); + if (callback & ~3) ErrorUnknownCallbackResult(v->GetGRFID(), CBID_VEHICLE_32DAY_CALLBACK, callback); } } diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 814fdc4b94..9834c9586d 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -50,9 +50,6 @@ bool _window_maximize; uint _display_hz; uint _fullscreen_bpp; static Dimension _bck_resolution; -#if !defined(UNICODE) -uint _codepage; -#endif /** Whether the drawing is/may be done in a separate thread. */ static bool _draw_threaded; @@ -439,12 +436,61 @@ static void PaintWindowThread(void *) } /** Forward key presses to the window system. */ -static LRESULT HandleCharMsg(uint keycode, uint charcode) +static LRESULT HandleCharMsg(uint keycode, WChar charcode) { #if !defined(UNICODE) - wchar_t w; - int len = MultiByteToWideChar(_codepage, 0, (char*)&charcode, 1, &w, 1); - charcode = len == 1 ? w : 0; + static char prev_char = 0; + + char input[2] = {(char)charcode, 0}; + int input_len = 1; + + if (prev_char != 0) { + /* We stored a lead byte previously, combine it with this byte. */ + input[0] = prev_char; + input[1] = (char)charcode; + input_len = 2; + } else if (IsDBCSLeadByte(charcode)) { + /* We got a lead byte, store and exit. */ + prev_char = charcode; + return 0; + } + prev_char = 0; + + wchar_t w[2]; // Can get up to two code points as a result. + int len = MultiByteToWideChar(CP_ACP, 0, input, input_len, w, 2); + switch (len) { + case 1: // Normal unicode character. + charcode = w[0]; + break; + + case 2: // Got an UTF-16 surrogate pair back. + charcode = Utf16DecodeSurrogate(w[0], w[1]); + break; + + default: // Some kind of error. + DEBUG(driver, 1, "Invalid DBCS character sequence encountered, dropping input"); + charcode = 0; + break; + } +#else + static WChar prev_char = 0; + + /* Did we get a lead surrogate? If yes, store and exit. */ + if (Utf16IsLeadSurrogate(charcode)) { + if (prev_char != 0) DEBUG(driver, 1, "Got two UTF-16 lead surrogates, dropping the first one"); + prev_char = charcode; + return 0; + } + + /* Stored lead surrogate and incoming trail surrogate? Combine and forward to input handling. */ + if (prev_char != 0) { + if (Utf16IsTrailSurrogate(charcode)) { + charcode = Utf16DecodeSurrogate(prev_char, charcode); + } else { + DEBUG(driver, 1, "Got an UTF-16 lead surrogate without a trail surrogate, dropping the lead surrogate"); + } + } + prev_char = 0; #endif /* UNICODE */ HandleKeypress(GB(charcode, 0, 16) | (keycode << 16)); @@ -586,16 +632,17 @@ static LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lP return 0; } +#if !defined(WINCE) || _WIN32_WCE >= 0x400 #if !defined(UNICODE) - case WM_INPUTLANGCHANGE: { - TCHAR locale[6]; - LCID lcid = GB(lParam, 0, 16); - - int len = GetLocaleInfo(lcid, LOCALE_IDEFAULTANSICODEPAGE, locale, lengthof(locale)); - if (len != 0) _codepage = _ttoi(locale); - return 1; - } -#endif /* UNICODE */ + case WM_IME_CHAR: + if (GB(wParam, 8, 8) != 0) { + /* DBCS character, send lead byte first. */ + HandleCharMsg(0, GB(wParam, 8, 8)); + } + HandleCharMsg(0, GB(wParam, 0, 8)); + return 0; +#endif +#endif case WM_DEADCHAR: console = GB(lParam, 16, 8) == 41; diff --git a/src/window.cpp b/src/window.cpp index 77d4859900..4f5a2d9012 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -2248,7 +2248,7 @@ static bool MaybeBringWindowToFront(Window *w) * @return #ES_HANDLED if the key press has been handled and no other * window should receive the event. */ -EventState Window::HandleEditBoxKey(int wid, uint16 key, uint16 keycode) +EventState Window::HandleEditBoxKey(int wid, WChar key, uint16 keycode) { QueryString *query = this->GetQueryString(wid); if (query == NULL) return ES_NOT_HANDLED; diff --git a/src/window_gui.h b/src/window_gui.h index c0beaf3236..bcb06d6036 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -19,6 +19,7 @@ #include "widget_type.h" #include "core/smallvec_type.hpp" #include "core/smallmap_type.hpp" +#include "string_type.h" /** State of handling an event. */ enum EventState { @@ -465,7 +466,7 @@ public: void UnfocusFocusedWidget(); bool SetFocusedWidget(int widget_index); - EventState HandleEditBoxKey(int wid, uint16 key, uint16 keycode); + EventState HandleEditBoxKey(int wid, WChar key, uint16 keycode); void HandleButtonClick(byte widget); int GetRowFromWidget(int clickpos, int widget, int padding, int line_height = -1) const; @@ -570,7 +571,7 @@ public: * @return #ES_HANDLED if the key press has been handled and no other * window should receive the event. */ - virtual EventState OnKeyPress(uint16 key, uint16 keycode) { return ES_NOT_HANDLED; } + virtual EventState OnKeyPress(WChar key, uint16 keycode) { return ES_NOT_HANDLED; } /** * The state of the control key has changed