Make sure only *W functions from Win32 API are called.

After this commit, SolveSpace always uses UTF-8 strings internally
(represented as char* and std::string) on every OS, for everything:
UI labels, paths and user input. OS X always uses UTF-8; on Windows,
strings are converted at boundary; and on Linux/BSD/etc SolveSpace
refuses to start unless the current locale is UTF-8. This will
negatively affect literally no one.

This commit does not bring forth any user-visible improvement yet;
in order to correctly handle Unicode in filenames, it is still
necessary to change the fopen calls to _wfopen on Windows.
pull/4/head
whitequark 2015-12-27 15:51:28 +08:00
parent 5c9c32cfc7
commit 97a9b4743e
5 changed files with 209 additions and 131 deletions

View File

@ -35,7 +35,9 @@ if(WIN32)
-D_WIN32_IE=_WIN32_WINNT
-DISOLATION_AWARE_ENABLED=1
-DWIN32=1
-DWIN32_LEAN_AND_MEAN=1)
-DWIN32_LEAN_AND_MEAN=1
-DUNICODE
-D_UNICODE)
endif()
if(MINGW)

View File

@ -1470,7 +1470,21 @@ void ExitNow(void) {
};
int main(int argc, char** argv) {
/* If we don't call this, gtk_init will set the C standard library
/* It would in principle be possible to judiciously use
Glib::filename_{from,to}_utf8, but it's not really worth
the effort.
The setlocale() call is necessary for Glib::get_charset()
to detect the system character set; otherwise it thinks
it is always ANSI_X3.4-1968.
We set it back to C after all. */
setlocale(LC_ALL, "");
if(!Glib::get_charset()) {
std::cerr << "Sorry, only UTF-8 locales are supported." << std::endl;
return 1;
}
setlocale(LC_ALL, "C");
/* If we don't do this, gtk_init will set the C standard library
locale, and printf will format floats using ",". We will then
fail to parse these. Also, many text window lines will become
ambiguous. */
@ -1500,7 +1514,8 @@ int main(int argc, char** argv) {
<< std::endl;
}
SS.OpenFile(argv[1]);
/* Make sure the argument is valid UTF-8. */
SS.OpenFile(Glib::ustring(argv[1]));
}
main.run(*GW);

View File

@ -107,6 +107,13 @@ typedef UCHAR uint8_t;
typedef CHAR int8_t;
#endif
#if defined(WIN32)
std::string Narrow(const wchar_t *s);
std::wstring Widen(const char *s);
std::string Narrow(const std::wstring &s);
std::wstring Widen(const std::string &s);
#endif
inline double Random(double vmax) {
return (vmax*rand()) / RAND_MAX;
}

View File

@ -90,9 +90,9 @@ static LRESULT CALLBACK MessageProc(HWND hwnd, UINT msg, WPARAM wParam,
col = 0;
row++;
} else {
TextOut(hdc, col*SS.TW.CHAR_WIDTH + 10,
row*SS.TW.LINE_HEIGHT + 10,
&(MessageString[i]), 1);
TextOutW(hdc, col*SS.TW.CHAR_WIDTH + 10,
row*SS.TW.LINE_HEIGHT + 10,
Widen(&(MessageString[i])).c_str(), 1);
col++;
}
}
@ -107,11 +107,11 @@ static LRESULT CALLBACK MessageProc(HWND hwnd, UINT msg, WPARAM wParam,
return 1;
}
HWND CreateWindowClient(DWORD exStyle, const char *className, const char *windowName,
HWND CreateWindowClient(DWORD exStyle, const wchar_t *className, const wchar_t *windowName,
DWORD style, int x, int y, int width, int height, HWND parent,
HMENU menu, HINSTANCE instance, void *param)
{
HWND h = CreateWindowEx(exStyle, className, windowName, style, x, y,
HWND h = CreateWindowExW(exStyle, className, windowName, style, x, y,
width, height, parent, menu, instance, param);
RECT r;
@ -128,16 +128,15 @@ void SolveSpace::DoMessageBox(const char *str, int rows, int cols, bool error)
{
EnableWindow(GraphicsWnd, false);
EnableWindow(TextWnd, false);
//HWND h = GetForegroundWindow();
// Register the window class for our dialog.
WNDCLASSEX wc = {};
wc.cbSize = sizeof(wc);
wc.cbSize = sizeof(wc);
wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)MessageProc;
wc.hInstance = Instance;
wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW;
wc.lpszClassName = "MessageWnd";
wc.lpszClassName = L"MessageWnd";
wc.lpszMenuName = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
@ -153,11 +152,11 @@ void SolveSpace::DoMessageBox(const char *str, int rows, int cols, bool error)
const char *title = error ? "SolveSpace - Error" : "SolveSpace - Message";
int width = cols*SS.TW.CHAR_WIDTH + 20,
height = rows*SS.TW.LINE_HEIGHT + 60;
MessageWnd = CreateWindowClient(0, "MessageWnd", title,
MessageWnd = CreateWindowClient(0, L"MessageWnd", Widen(title).c_str(),
WS_OVERLAPPED | WS_SYSMENU,
r.left + 100, r.top + 100, width, height, NULL, NULL, Instance, NULL);
OkButton = CreateWindowEx(0, WC_BUTTON, "OK",
OkButton = CreateWindowExW(0, WC_BUTTON, L"OK",
WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | BS_DEFPUSHBUTTON,
(width - 70)/2, rows*SS.TW.LINE_HEIGHT + 20,
70, 25, MessageWnd, NULL, Instance, NULL);
@ -196,15 +195,15 @@ void SolveSpace::AddContextMenuItem(const char *label, int id)
if(!ContextMenu) ContextMenu = CreatePopupMenu();
if(id == CONTEXT_SUBMENU) {
AppendMenu(ContextMenu, MF_STRING | MF_POPUP,
(UINT_PTR)ContextSubmenu, label);
AppendMenuW(ContextMenu, MF_STRING | MF_POPUP,
(UINT_PTR)ContextSubmenu, Widen(label).c_str());
ContextSubmenu = NULL;
} else {
HMENU m = ContextSubmenu ? ContextSubmenu : ContextMenu;
if(id == CONTEXT_SEPARATOR) {
AppendMenu(m, MF_SEPARATOR, 0, "");
AppendMenuW(m, MF_SEPARATOR, 0, L"");
} else {
AppendMenu(m, MF_STRING, id, label);
AppendMenuW(m, MF_STRING, id, Widen(label).c_str());
}
}
}
@ -271,7 +270,7 @@ void SolveSpace::GetTextWindowSize(int *w, int *h)
}
void SolveSpace::OpenWebsite(const char *url) {
ShellExecute(GraphicsWnd, "open", url, NULL, NULL, SW_SHOWNORMAL);
ShellExecuteW(GraphicsWnd, L"open", Widen(url).c_str(), NULL, NULL, SW_SHOWNORMAL);
}
void SolveSpace::ExitNow(void) {
@ -292,12 +291,13 @@ inline int CLAMP(int v, int a, int b) {
static HKEY GetRegistryKey()
{
HKEY Software;
if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software", 0, KEY_ALL_ACCESS, &Software) != ERROR_SUCCESS)
if(RegOpenKeyExW(HKEY_CURRENT_USER, L"Software", 0,
KEY_ALL_ACCESS, &Software) != ERROR_SUCCESS)
return NULL;
HKEY SolveSpace;
if(RegCreateKeyEx(Software, "SolveSpace", 0, NULL, 0,
KEY_ALL_ACCESS, NULL, &SolveSpace, NULL) != ERROR_SUCCESS)
if(RegCreateKeyExW(Software, L"SolveSpace", 0, NULL, 0,
KEY_ALL_ACCESS, NULL, &SolveSpace, NULL) != ERROR_SUCCESS)
return NULL;
RegCloseKey(Software);
@ -308,8 +308,8 @@ static HKEY GetRegistryKey()
void SolveSpace::CnfFreezeInt(uint32_t val, const std::string &name)
{
HKEY SolveSpace = GetRegistryKey();
RegSetValueEx(SolveSpace, &name[0], 0,
REG_DWORD, (const BYTE*) &val, sizeof(DWORD));
RegSetValueExW(SolveSpace, &Widen(name)[0], 0,
REG_DWORD, (const BYTE*) &val, sizeof(DWORD));
RegCloseKey(SolveSpace);
}
void SolveSpace::CnfFreezeFloat(float val, const std::string &name)
@ -317,15 +317,16 @@ void SolveSpace::CnfFreezeFloat(float val, const std::string &name)
static_assert(sizeof(float) == sizeof(DWORD),
"sizes of float and DWORD must match");
HKEY SolveSpace = GetRegistryKey();
RegSetValueEx(SolveSpace, &name[0], 0,
REG_DWORD, (const BYTE*) &val, sizeof(DWORD));
RegSetValueExW(SolveSpace, &Widen(name)[0], 0,
REG_DWORD, (const BYTE*) &val, sizeof(DWORD));
RegCloseKey(SolveSpace);
}
void SolveSpace::CnfFreezeString(const std::string &str, const std::string &name)
{
HKEY SolveSpace = GetRegistryKey();
RegSetValueEx(SolveSpace, &name[0], 0,
REG_SZ, (const BYTE*) &str[0], str.length() + 1);
std::wstring strW = Widen(str);
RegSetValueExW(SolveSpace, &Widen(name)[0], 0,
REG_SZ, (const BYTE*) &strW[0], (strW.length() + 1) * 2);
RegCloseKey(SolveSpace);
}
static void FreezeWindowPos(HWND hwnd, const std::string &name)
@ -344,7 +345,8 @@ uint32_t SolveSpace::CnfThawInt(uint32_t val, const std::string &name)
{
HKEY SolveSpace = GetRegistryKey();
DWORD type, newval, len = sizeof(DWORD);
LONG result = RegQueryValueEx(SolveSpace, &name[0], NULL, &type, (BYTE*) &newval, &len);
LONG result = RegQueryValueEx(SolveSpace, &Widen(name)[0], NULL,
&type, (BYTE*) &newval, &len);
RegCloseKey(SolveSpace);
if(result == ERROR_SUCCESS && type == REG_DWORD)
@ -357,7 +359,8 @@ float SolveSpace::CnfThawFloat(float val, const std::string &name)
HKEY SolveSpace = GetRegistryKey();
DWORD type, len = sizeof(DWORD);
float newval;
LONG result = RegQueryValueEx(SolveSpace, &name[0], NULL, &type, (BYTE*) &newval, &len);
LONG result = RegQueryValueExW(SolveSpace, &Widen(name)[0], NULL,
&type, (BYTE*) &newval, &len);
RegCloseKey(SolveSpace);
if(result == ERROR_SUCCESS && type == REG_DWORD)
@ -369,22 +372,22 @@ std::string SolveSpace::CnfThawString(const std::string &val, const std::string
{
HKEY SolveSpace = GetRegistryKey();
DWORD type, len;
if(RegQueryValueEx(SolveSpace, &name[0], NULL,
&type, NULL, &len) != ERROR_SUCCESS || type != REG_SZ) {
if(RegQueryValueExW(SolveSpace, &Widen(name)[0], NULL,
&type, NULL, &len) != ERROR_SUCCESS || type != REG_SZ) {
RegCloseKey(SolveSpace);
return val;
}
std::string newval;
newval.resize(len - 1);
if(RegQueryValueEx(SolveSpace, &name[0], NULL,
NULL, (BYTE*) &newval[0], &len) != ERROR_SUCCESS) {
std::wstring newval;
newval.resize(len / 2 - 1);
if(RegQueryValueExW(SolveSpace, &Widen(name)[0], NULL,
NULL, (BYTE*) &newval[0], &len) != ERROR_SUCCESS) {
RegCloseKey(SolveSpace);
return val;
}
RegCloseKey(SolveSpace);
return newval;
return Narrow(newval);
}
static void ThawWindowPos(HWND hwnd, const std::string &name)
{
@ -414,9 +417,9 @@ static void ThawWindowPos(HWND hwnd, const std::string &name)
void SolveSpace::SetCurrentFilename(const std::string &filename) {
if(!filename.empty()) {
SetWindowText(GraphicsWnd, ("SolveSpace - " + filename).c_str());
SetWindowTextW(GraphicsWnd, Widen("SolveSpace - " + filename).c_str());
} else {
SetWindowText(GraphicsWnd, "SolveSpace - (not yet saved)");
SetWindowTextW(GraphicsWnd, L"SolveSpace - (not yet saved)");
}
}
@ -622,13 +625,19 @@ LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
return 1;
}
static std::string EditControlText(HWND hwnd)
{
std::wstring result;
result.resize(GetWindowTextLength(hwnd));
GetWindowTextW(hwnd, &result[0], result.length() + 1);
return Narrow(result);
}
static bool ProcessKeyDown(WPARAM wParam)
{
if(GraphicsEditControlIsVisible() && wParam != VK_ESCAPE) {
if(wParam == VK_RETURN) {
char s[1024] = {};
SendMessage(GraphicsEditControl, WM_GETTEXT, 900, (LPARAM)s);
SS.GW.EditControlDone(s);
SS.GW.EditControlDone(EditControlText(GraphicsEditControl).c_str());
return true;
} else {
return false;
@ -636,9 +645,7 @@ static bool ProcessKeyDown(WPARAM wParam)
}
if(TextEditControlIsVisible() && wParam != VK_ESCAPE) {
if(wParam == VK_RETURN) {
char s[1024] = {};
SendMessage(TextEditControl, WM_GETTEXT, 900, (LPARAM)s);
SS.TW.EditControlDone(s);
SS.TW.EditControlDone(EditControlText(TextEditControl).c_str());
} else {
return false;
}
@ -796,12 +803,12 @@ void SolveSpace::InvalidateText(void)
InvalidateRect(TextWnd, NULL, false);
}
static void ShowEditControl(HWND h, int x, int y, char *s) {
static void ShowEditControl(HWND h, int x, int y, const std::wstring &s) {
MoveWindow(h, x, y, EDIT_WIDTH, EDIT_HEIGHT, true);
ShowWindow(h, SW_SHOW);
if(s) {
SendMessage(h, WM_SETTEXT, 0, (LPARAM)s);
SendMessage(h, EM_SETSEL, 0, strlen(s));
if(!s.empty()) {
SendMessage(h, WM_SETTEXT, 0, (LPARAM)s.c_str());
SendMessage(h, EM_SETSEL, 0, s.length());
SetFocus(h);
}
}
@ -809,7 +816,7 @@ void SolveSpace::ShowTextEditControl(int x, int y, char *s)
{
if(GraphicsEditControlIsVisible()) return;
ShowEditControl(TextEditControl, x, y, s);
ShowEditControl(TextEditControl, x, y, Widen(s));
}
void SolveSpace::HideTextEditControl(void)
{
@ -832,7 +839,7 @@ void SolveSpace::ShowGraphicsEditControl(int x, int y, char *s)
// top left corner
y -= 20;
ShowEditControl(GraphicsEditControl, x, y, s);
ShowEditControl(GraphicsEditControl, x, y, Widen(s));
}
void SolveSpace::HideGraphicsEditControl(void)
{
@ -958,67 +965,63 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam,
//-----------------------------------------------------------------------------
// Common dialog routines, to open or save a file.
//-----------------------------------------------------------------------------
bool SolveSpace::GetOpenFile(std::string &filename,
const char *defExtension, const char *selPattern)
{
// UNC paths may be arbitrarily long.
static size_t strlen2(const char *p) {
const char *s = p;
while(*p || (!*p && *(p+1))) p++;
return p - s + 1;
}
static bool OpenSaveFile(bool isOpen, std::string &filename,
const char *defExtension, const char *selPattern) {
// UNC paths may be as long as 32767 characters.
// Unfortunately, the Get*FileName API does not provide any way to use it
// except with a preallocated buffer of fixed size, so we use something
// reasonably large.
char filenameC[0xFFFF] = {};
strncpy(filenameC, filename.c_str(), sizeof(filenameC) - 1);
const int len = 32768;
wchar_t filenameC[len] = {};
wcsncpy(filenameC, Widen(filename).c_str(), len - 1);
std::wstring selPatternW = Widen(std::string(selPattern, strlen2(selPattern)));
std::wstring defExtensionW = Widen(defExtension);
OPENFILENAME ofn = {};
ofn.lStructSize = sizeof(ofn);
ofn.hInstance = Instance;
ofn.hwndOwner = GraphicsWnd;
ofn.lpstrFilter = selPattern;
ofn.lpstrDefExt = defExtension;
ofn.lpstrFilter = selPatternW.c_str();
ofn.lpstrDefExt = defExtensionW.c_str();
ofn.lpstrFile = filenameC;
ofn.nMaxFile = sizeof(filenameC);
ofn.nMaxFile = len;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
EnableWindow(GraphicsWnd, false);
EnableWindow(TextWnd, false);
BOOL r = GetOpenFileName(&ofn);
BOOL r;
if(isOpen) {
r = GetOpenFileNameW(&ofn);
} else {
r = GetSaveFileNameW(&ofn);
}
EnableWindow(TextWnd, true);
EnableWindow(GraphicsWnd, true);
SetForegroundWindow(GraphicsWnd);
if(r) filename = filenameC;
if(r) filename = Narrow(filenameC);
return r ? true : false;
}
bool SolveSpace::GetOpenFile(std::string &filename,
const char *defExtension, const char *selPattern)
{
return OpenSaveFile(true, filename, defExtension, selPattern);
}
bool SolveSpace::GetSaveFile(std::string &filename,
const char *defExtension, const char *selPattern)
{
// See GetOpenFile
char filenameC[0xFFFF] = {};
strncpy(filenameC, filename.c_str(), sizeof(filenameC) - 1);
OPENFILENAME ofn = {};
ofn.lStructSize = sizeof(ofn);
ofn.hInstance = Instance;
ofn.hwndOwner = GraphicsWnd;
ofn.lpstrFilter = selPattern;
ofn.lpstrDefExt = defExtension;
ofn.lpstrFile = filenameC;
ofn.nMaxFile = sizeof(filenameC);
ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
EnableWindow(GraphicsWnd, false);
EnableWindow(TextWnd, false);
BOOL r = GetSaveFileName(&ofn);
EnableWindow(TextWnd, true);
EnableWindow(GraphicsWnd, true);
SetForegroundWindow(GraphicsWnd);
if(r) filename = filenameC;
return r ? true : false;
return OpenSaveFile(false, filename, defExtension, selPattern);
}
int SolveSpace::SaveFileYesNoCancel(void)
@ -1026,9 +1029,9 @@ int SolveSpace::SaveFileYesNoCancel(void)
EnableWindow(GraphicsWnd, false);
EnableWindow(TextWnd, false);
int r = MessageBox(GraphicsWnd,
"The program has changed since it was last saved.\r\n\r\n"
"Do you want to save the changes?", "SolveSpace",
int r = MessageBoxW(GraphicsWnd,
L"The program has changed since it was last saved.\r\n\r\n"
L"Do you want to save the changes?", L"SolveSpace",
MB_YESNOCANCEL | MB_ICONWARNING);
EnableWindow(TextWnd, true);
@ -1050,9 +1053,9 @@ int SolveSpace::LoadAutosaveYesNo(void)
EnableWindow(GraphicsWnd, false);
EnableWindow(TextWnd, false);
int r = MessageBox(GraphicsWnd,
"An autosave file is availible for this project.\r\n\r\n"
"Do you want to load the autosave file instead?", "SolveSpace",
int r = MessageBoxW(GraphicsWnd,
L"An autosave file is availible for this project.\r\n\r\n"
L"Do you want to load the autosave file instead?", L"SolveSpace",
MB_YESNO | MB_ICONWARNING);
EnableWindow(TextWnd, true);
@ -1069,18 +1072,18 @@ int SolveSpace::LoadAutosaveYesNo(void)
void SolveSpace::LoadAllFontFiles(void)
{
std::string fontsDir(MAX_PATH, '\0');
fontsDir.resize(GetWindowsDirectory(&fontsDir[0], fontsDir.length()));
fontsDir += "\\fonts\\";
std::wstring fontsDir(MAX_PATH, '\0');
fontsDir.resize(GetWindowsDirectoryW(&fontsDir[0], fontsDir.length()));
fontsDir += L"\\fonts\\";
WIN32_FIND_DATA wfd;
HANDLE h = FindFirstFile((fontsDir + "*.ttf").c_str(), &wfd);
HANDLE h = FindFirstFileW((fontsDir + L"*.ttf").c_str(), &wfd);
while(h != INVALID_HANDLE_VALUE) {
TtfFont tf = {};
tf.fontFile = fontsDir + wfd.cFileName;
tf.fontFile = Narrow(fontsDir) + Narrow(wfd.cFileName);
SS.fonts.l.Add(&tf);
if(!FindNextFile(h, &wfd)) break;
if(!FindNextFileW(h, &wfd)) break;
}
}
@ -1128,11 +1131,11 @@ static void DoRecent(HMENU m, int base)
int i, c = 0;
for(i = 0; i < MAX_RECENT; i++) {
if(!RecentFile[i].empty()) {
AppendMenu(m, MF_STRING, base + i, RecentFile[i].c_str());
AppendMenuW(m, MF_STRING, base + i, Widen(RecentFile[i]).c_str());
c++;
}
}
if(c == 0) AppendMenu(m, MF_STRING | MF_GRAYED, 0, "(no recent files)");
if(c == 0) AppendMenuW(m, MF_STRING | MF_GRAYED, 0, L"(no recent files)");
}
void SolveSpace::RefreshRecentMenus(void)
{
@ -1160,23 +1163,23 @@ HMENU CreateGraphicsWindowMenus(void)
if(SS.GW.menu[i].level == 0) {
m = CreateMenu();
AppendMenu(top, MF_STRING | MF_POPUP, (UINT_PTR)m, label);
AppendMenuW(top, MF_STRING | MF_POPUP, (UINT_PTR)m, Widen(label).c_str());
if(subMenu >= (int)arraylen(SubMenus)) oops();
SubMenus[subMenu] = m;
subMenu++;
} else if(SS.GW.menu[i].level == 1) {
if(SS.GW.menu[i].id == GraphicsWindow::MNU_OPEN_RECENT) {
RecentOpenMenu = CreateMenu();
AppendMenu(m, MF_STRING | MF_POPUP,
(UINT_PTR)RecentOpenMenu, label);
AppendMenuW(m, MF_STRING | MF_POPUP,
(UINT_PTR)RecentOpenMenu, Widen(label).c_str());
} else if(SS.GW.menu[i].id == GraphicsWindow::MNU_GROUP_RECENT) {
RecentImportMenu = CreateMenu();
AppendMenu(m, MF_STRING | MF_POPUP,
(UINT_PTR)RecentImportMenu, label);
AppendMenuW(m, MF_STRING | MF_POPUP,
(UINT_PTR)RecentImportMenu, Widen(label).c_str());
} else if(SS.GW.menu[i].label) {
AppendMenu(m, MF_STRING, SS.GW.menu[i].id, label);
AppendMenuW(m, MF_STRING, SS.GW.menu[i].id, Widen(label).c_str());
} else {
AppendMenu(m, MF_SEPARATOR, SS.GW.menu[i].id, "");
AppendMenuW(m, MF_SEPARATOR, SS.GW.menu[i].id, L"");
}
} else oops();
}
@ -1196,7 +1199,7 @@ static void CreateMainWindows(void)
CS_DBLCLKS;
wc.lpfnWndProc = (WNDPROC)GraphicsWndProc;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszClassName = "GraphicsWnd";
wc.lpszClassName = L"GraphicsWnd";
wc.lpszMenuName = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000),
@ -1206,14 +1209,14 @@ static void CreateMainWindows(void)
if(!RegisterClassEx(&wc)) oops();
HMENU top = CreateGraphicsWindowMenus();
GraphicsWnd = CreateWindowEx(0, "GraphicsWnd",
"SolveSpace (not yet saved)",
GraphicsWnd = CreateWindowExW(0, L"GraphicsWnd",
L"SolveSpace (not yet saved)",
WS_OVERLAPPED | WS_THICKFRAME | WS_CLIPCHILDREN | WS_MAXIMIZEBOX |
WS_MINIMIZEBOX | WS_SYSMENU | WS_SIZEBOX | WS_CLIPSIBLINGS,
50, 50, 900, 600, NULL, top, Instance, NULL);
if(!GraphicsWnd) oops();
GraphicsEditControl = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "",
GraphicsEditControl = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDIT, L"",
WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS,
50, 50, 100, 21, GraphicsWnd, NULL, Instance, NULL);
SendMessage(GraphicsEditControl, WM_SETFONT, (WPARAM)FixedFont, true);
@ -1223,24 +1226,24 @@ static void CreateMainWindows(void)
wc.style &= ~CS_DBLCLKS;
wc.lpfnWndProc = (WNDPROC)TextWndProc;
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszClassName = "TextWnd";
wc.lpszClassName = L"TextWnd";
wc.hCursor = NULL;
if(!RegisterClassEx(&wc)) oops();
// We get the desired Alt+Tab behaviour by specifying that the text
// window is a child of the graphics window.
TextWnd = CreateWindowEx(0,
"TextWnd", "SolveSpace - Browser", WS_THICKFRAME | WS_CLIPCHILDREN,
TextWnd = CreateWindowExW(0,
L"TextWnd", L"SolveSpace - Browser", WS_THICKFRAME | WS_CLIPCHILDREN,
650, 500, 420, 300, GraphicsWnd, (HMENU)NULL, Instance, NULL);
if(!TextWnd) oops();
TextWndScrollBar = CreateWindowEx(0, WC_SCROLLBAR, "", WS_CHILD |
TextWndScrollBar = CreateWindowExW(0, WC_SCROLLBAR, L"", WS_CHILD |
SBS_VERT | SBS_LEFTALIGN | WS_VISIBLE | WS_CLIPSIBLINGS,
200, 100, 100, 100, TextWnd, NULL, Instance, NULL);
// Force the scrollbar to get resized to the window,
TextWndProc(TextWnd, WM_SIZE, 0, 0);
TextEditControl = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, "",
TextEditControl = CreateWindowExW(WS_EX_CLIENTEDGE, WC_EDIT, L"",
WS_CHILD | ES_AUTOHSCROLL | WS_TABSTOP | WS_CLIPSIBLINGS,
50, 50, 100, 21, TextWnd, NULL, Instance, NULL);
SendMessage(TextEditControl, WM_SETFONT, (WPARAM)FixedFont, true);
@ -1304,10 +1307,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
InitCommonControls();
// A monospaced font
FixedFont = CreateFont(SS.TW.CHAR_HEIGHT, SS.TW.CHAR_WIDTH, 0, 0,
FixedFont = CreateFontW(SS.TW.CHAR_HEIGHT, SS.TW.CHAR_WIDTH, 0, 0,
FW_REGULAR, false,
false, false, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, FF_DONTCARE, "Lucida Console");
DEFAULT_QUALITY, FF_DONTCARE, L"Lucida Console");
if(!FixedFont)
FixedFont = (HFONT)GetStockObject(SYSTEM_FONT);
@ -1331,23 +1334,29 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
// Create the heaps for all dynamic memory (AllocTemporary, MemAlloc)
InitHeaps();
// Pull out the Unicode command line.
int argc;
LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc);
// A filename may have been specified on the command line; if so, then
// strip any quotation marks, and make it absolute.
std::string filename = lpCmdLine;
size_t pos;
pos = filename.find_last_not_of("\"");
if(pos != std::string::npos)
filename.erase(pos + 1);
pos = filename.find_first_not_of(" \"");
if(pos != std::string::npos)
filename.erase(0, pos);
if(!filename.empty())
filename = GetAbsoluteFilename(filename);
std::wstring filenameRel, filename;
if(argc >= 2) {
filenameRel = argv[1];
if(filenameRel[0] == L'\"' && filenameRel[filenameRel.length() - 1] == L'\"') {
filenameRel.erase(0, 1);
filenameRel.erase(filenameRel.length() - 1, 1);
}
DWORD len = GetFullPathNameW(&filenameRel[0], 0, NULL, NULL);
filename.resize(len - 1);
GetFullPathNameW(&filenameRel[0], len, &filename[0], NULL);
}
#ifdef HAVE_SPACEWARE
// Initialize the SpaceBall, if present. Test if the driver is running
// first, to avoid a long timeout if it's not.
HWND swdc = FindWindow("SpaceWare Driver Class", NULL);
HWND swdc = FindWindowW(L"SpaceWare Driver Class", NULL);
if(swdc != NULL) {
SiOpenData sod;
SiInitialize();
@ -1361,7 +1370,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
// Call in to the platform-independent code, and let them do their init
SS.Init();
if(!filename.empty())
SS.OpenFile(filename);
SS.OpenFile(Narrow(filename));
// And now it's the message loop. All calls in to the rest of the code
// will be from the wndprocs.

View File

@ -19,7 +19,52 @@ void dbp(const char *str, ...)
_vsnprintf(buf, sizeof(buf), str, f);
va_end(f);
OutputDebugString(buf);
// The native version of OutputDebugString, unlike most others,
// is OutputDebugStringA.
OutputDebugStringA(buf);
}
std::string Narrow(const wchar_t *in)
{
std::string out;
DWORD len = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL);
out.resize(len - 1);
if(!WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], len, NULL, NULL))
oops();
return out;
}
std::string Narrow(const std::wstring &in)
{
if(in == L"") return "";
std::string out;
out.resize(WideCharToMultiByte(CP_UTF8, 0, &in[0], in.length(), NULL, 0, NULL, NULL));
if(!WideCharToMultiByte(CP_UTF8, 0, &in[0], in.length(),
&out[0], out.length(), NULL, NULL))
oops();
return out;
}
std::wstring Widen(const char *in)
{
std::wstring out;
DWORD len = MultiByteToWideChar(CP_UTF8, 0, in, -1, NULL, 0);
out.resize(len - 1);
if(!MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], len))
oops();
return out;
}
std::wstring Widen(const std::string &in)
{
if(in == "") return L"";
std::wstring out;
out.resize(MultiByteToWideChar(CP_UTF8, 0, &in[0], in.length(), NULL, 0));
if(!MultiByteToWideChar(CP_UTF8, 0, &in[0], in.length(), &out[0], out.length()))
oops();
return out;
}
//-----------------------------------------------------------------------------