diff --git a/readme.html b/readme.html
index ee03881d..e342a91d 100644
--- a/readme.html
+++ b/readme.html
@@ -285,6 +285,7 @@ version of GLFW.
Added initial window title parameter to glfwOpenWindow
Added glfwSetGamma
, glfwSetGammaRamp
and glfwGetGammaRamp
functions and GLFWgammaramp
type for monitor gamma ramp control
Changed buffer bit depth parameters of glfwOpenWindow
to window hints
+ Changed glfwOpenWindow
and glfwSetWindowTitle
to use UTF-8 encoded strings
Renamed glfw.h
to glfw3.h
to avoid conflicts with 2.x series
Renamed GLFW_WINDOW
token to GLFW_WINDOWED
Renamed GLFW_WINDOW_NO_RESIZE
to GLFW_WINDOW_RESIZABLE
@@ -315,8 +316,11 @@ version of GLFW.
[X11] Added support for the GLX_EXT_swap_control
extension as an alternative to GLX_SGI_swap_control
[X11] Added the POSIX CLOCK_MONOTONIC
time source as the preferred method
[X11] Added dependency on libm, where present
+ [X11] Added support for the _NET_WM_NAME
and _NET_WM_ICON_NAME
EWMH window properties
+ [X11] Bugfix: Some window properties required by the ICCCM were not set
[X11] Bugfix: Calling glXCreateContextAttribsARB
with an unavailable OpenGL version caused the application to terminate with a BadMatch
Xlib error
[X11] Bugfix: A synchronization point necessary for jitter-free locked cursor mode was incorrectly removed
+ [Win32] Changed port to use Unicode mode only
[Win32] Removed explicit support for versions of Windows older than Windows XP
[Win32] Bugfix: Window activation and iconification did not work as expected
[Win32] Bugfix: Software rasterizer pixel formats were not discarded by the WGL_ARB_pixel_format code path
@@ -862,6 +866,8 @@ their skills. Special thanks go out to:
Tristam MacDonald, for his bug reports and feedback on the Cocoa port
+ Hans 'Hanmac' Mackowiak, for adding UTF-8 window title support on X11
+
David Medlock, for doing the initial Lua port
Kenneth Miller, for his many and detailed bug reports on Win32
diff --git a/src/cocoa_window.m b/src/cocoa_window.m
index 55de95e7..b5dfb436 100644
--- a/src/cocoa_window.m
+++ b/src/cocoa_window.m
@@ -476,9 +476,7 @@ static GLboolean createWindow(_GLFWwindow* window,
return GL_FALSE;
}
- [window->NS.window setTitle:[NSString stringWithCString:wndconfig->title
- encoding:NSISOLatin1StringEncoding]];
-
+ [window->NS.window setTitle:[NSString stringWithUTF8String:wndconfig->title]];
[window->NS.window setContentView:[[GLFWContentView alloc]
initWithGlfwWindow:window]];
[window->NS.window setDelegate:window->NS.delegate];
@@ -769,8 +767,7 @@ void _glfwPlatformCloseWindow(_GLFWwindow* window)
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char *title)
{
- [window->NS.window setTitle:[NSString stringWithCString:title
- encoding:NSISOLatin1StringEncoding]];
+ [window->NS.window setTitle:[NSString stringWithUTF8String:title]];
}
//========================================================================
diff --git a/src/win32_init.c b/src/win32_init.c
index 0756b0f2..b23e7d57 100644
--- a/src/win32_init.c
+++ b/src/win32_init.c
@@ -47,7 +47,7 @@ static GLboolean initLibraries(void)
#ifndef _GLFW_NO_DLOAD_GDI32
// gdi32.dll (OpenGL pixel format functions & SwapBuffers)
- _glfwLibrary.Win32.gdi.instance = LoadLibrary("gdi32.dll");
+ _glfwLibrary.Win32.gdi.instance = LoadLibrary(L"gdi32.dll");
if (!_glfwLibrary.Win32.gdi.instance)
return GL_FALSE;
@@ -81,12 +81,12 @@ static GLboolean initLibraries(void)
#ifndef _GLFW_NO_DLOAD_WINMM
// winmm.dll (for joystick and timer support)
- _glfwLibrary.Win32.winmm.instance = LoadLibrary("winmm.dll");
+ _glfwLibrary.Win32.winmm.instance = LoadLibrary(L"winmm.dll");
if (!_glfwLibrary.Win32.winmm.instance)
return GL_FALSE;
- _glfwLibrary.Win32.winmm.joyGetDevCapsA = (JOYGETDEVCAPSA_T)
- GetProcAddress(_glfwLibrary.Win32.winmm.instance, "joyGetDevCapsA");
+ _glfwLibrary.Win32.winmm.joyGetDevCaps = (JOYGETDEVCAPS_T)
+ GetProcAddress(_glfwLibrary.Win32.winmm.instance, "joyGetDevCapsW");
_glfwLibrary.Win32.winmm.joyGetPos = (JOYGETPOS_T)
GetProcAddress(_glfwLibrary.Win32.winmm.instance, "joyGetPos");
_glfwLibrary.Win32.winmm.joyGetPosEx = (JOYGETPOSEX_T)
@@ -94,7 +94,7 @@ static GLboolean initLibraries(void)
_glfwLibrary.Win32.winmm.timeGetTime = (TIMEGETTIME_T)
GetProcAddress(_glfwLibrary.Win32.winmm.instance, "timeGetTime");
- if (!_glfwLibrary.Win32.winmm.joyGetDevCapsA ||
+ if (!_glfwLibrary.Win32.winmm.joyGetDevCaps ||
!_glfwLibrary.Win32.winmm.joyGetPos ||
!_glfwLibrary.Win32.winmm.joyGetPosEx ||
!_glfwLibrary.Win32.winmm.timeGetTime)
diff --git a/src/win32_platform.h b/src/win32_platform.h
index 074e28ba..0d63e09c 100644
--- a/src/win32_platform.h
+++ b/src/win32_platform.h
@@ -45,6 +45,8 @@
// thinks it is the only one that gets to do so
#undef APIENTRY
+#define UNICODE
+
#include
#include
@@ -162,7 +164,7 @@ typedef BOOL (WINAPI * SETDEVICEGAMMARAMP_T) (HDC,PVOID);
// winmm.dll function pointer typedefs
#ifndef _GLFW_NO_DLOAD_WINMM
-typedef MMRESULT (WINAPI * JOYGETDEVCAPSA_T) (UINT,LPJOYCAPSA,UINT);
+typedef MMRESULT (WINAPI * JOYGETDEVCAPS_T) (UINT,LPJOYCAPS,UINT);
typedef MMRESULT (WINAPI * JOYGETPOS_T) (UINT,LPJOYINFO);
typedef MMRESULT (WINAPI * JOYGETPOSEX_T) (UINT,LPJOYINFOEX);
typedef DWORD (WINAPI * TIMEGETTIME_T) (void);
@@ -190,12 +192,12 @@ typedef DWORD (WINAPI * TIMEGETTIME_T) (void);
// winmm.dll shortcuts
#ifndef _GLFW_NO_DLOAD_WINMM
-#define _glfw_joyGetDevCaps _glfwLibrary.Win32.winmm.joyGetDevCapsA
+#define _glfw_joyGetDevCaps _glfwLibrary.Win32.winmm.joyGetDevCaps
#define _glfw_joyGetPos _glfwLibrary.Win32.winmm.joyGetPos
#define _glfw_joyGetPosEx _glfwLibrary.Win32.winmm.joyGetPosEx
#define _glfw_timeGetTime _glfwLibrary.Win32.winmm.timeGetTime
#else
-#define _glfw_joyGetDevCaps joyGetDevCapsA
+#define _glfw_joyGetDevCaps joyGetDevCaps
#define _glfw_joyGetPos joyGetPos
#define _glfw_joyGetPosEx joyGetPosEx
#define _glfw_timeGetTime timeGetTime
@@ -204,7 +206,7 @@ typedef DWORD (WINAPI * TIMEGETTIME_T) (void);
// We use versioned window class names in order not to cause conflicts
// between applications using different versions of GLFW
-#define _GLFW_WNDCLASSNAME "GLFW30"
+#define _GLFW_WNDCLASSNAME L"GLFW30"
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWin32 Win32
@@ -309,7 +311,7 @@ typedef struct _GLFWlibraryWin32
// winmm.dll
struct {
HINSTANCE instance;
- JOYGETDEVCAPSA_T joyGetDevCapsA;
+ JOYGETDEVCAPS_T joyGetDevCaps;
JOYGETPOS_T joyGetPos;
JOYGETPOSEX_T joyGetPosEx;
TIMEGETTIME_T timeGetTime;
diff --git a/src/win32_window.c b/src/win32_window.c
index e2619acf..6ebe07b4 100644
--- a/src/win32_window.c
+++ b/src/win32_window.c
@@ -34,6 +34,31 @@
#include
+//========================================================================
+// Convert the specified UTF-8 string to a wide string
+//========================================================================
+
+static WCHAR* createWideStringFromUTF8(const char* source)
+{
+ WCHAR* target;
+ int length;
+
+ length = MultiByteToWideChar(CP_UTF8, 0, source, -1, NULL, 0);
+ if (!length)
+ return NULL;
+
+ target = (WCHAR*) _glfwMalloc(sizeof(WCHAR) * (length + 1));
+
+ if (!MultiByteToWideChar(CP_UTF8, 0, source, -1, target, length + 1))
+ {
+ _glfwFree(target);
+ return NULL;
+ }
+
+ return target;
+}
+
+
//========================================================================
// Convert BPP to RGB bits based on "best guess"
//========================================================================
@@ -1220,7 +1245,7 @@ static ATOM registerWindowClass(void)
wc.lpszClassName = _GLFW_WNDCLASSNAME; // Set class name
// Load user-provided icon if available
- wc.hIcon = LoadIcon(_glfwLibrary.Win32.instance, "GLFW_ICON");
+ wc.hIcon = LoadIcon(_glfwLibrary.Win32.instance, L"GLFW_ICON");
if (!wc.hIcon)
{
// Load default icon
@@ -1280,9 +1305,10 @@ static int createWindow(_GLFWwindow* window,
const _GLFWfbconfig* fbconfig)
{
DWORD dwStyle, dwExStyle;
- int pixelFormat, fullWidth, fullHeight;
+ int length, pixelFormat, fullWidth, fullHeight;
RECT wa;
POINT pos;
+ WCHAR* wideTitle;
// Set common window styles
dwStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE;
@@ -1331,9 +1357,17 @@ static int createWindow(_GLFWwindow* window,
else
SystemParametersInfo(SPI_GETWORKAREA, 0, &wa, 0);
+ wideTitle = createWideStringFromUTF8(wndconfig->title);
+ if (!wideTitle)
+ {
+ _glfwSetError(GLFW_PLATFORM_ERROR,
+ "glfwOpenWindow: Failed to convert title to wide string");
+ return;
+ }
+
window->Win32.handle = CreateWindowEx(window->Win32.dwExStyle,
_GLFW_WNDCLASSNAME,
- wndconfig->title,
+ wideTitle,
window->Win32.dwStyle,
wa.left, wa.top, // Window position
fullWidth, // Decorated window width
@@ -1349,6 +1383,8 @@ static int createWindow(_GLFWwindow* window,
return GL_FALSE;
}
+ _glfwFree(wideTitle);
+
window->WGL.DC = GetDC(window->Win32.handle);
if (!window->WGL.DC)
{
@@ -1568,7 +1604,17 @@ void _glfwPlatformCloseWindow(_GLFWwindow* window)
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
{
- SetWindowText(window->Win32.handle, title);
+ WCHAR* wideTitle = createWideStringFromUTF8(title);
+ if (!wideTitle)
+ {
+ _glfwSetError(GLFW_PLATFORM_ERROR,
+ "glfwSetWindowTitle: Failed to convert title to wide string");
+ return;
+ }
+
+ SetWindowText(window->Win32.handle, wideTitle);
+
+ _glfwFree(wideTitle);
}
diff --git a/src/x11_platform.h b/src/x11_platform.h
index 8292d635..3b824d64 100644
--- a/src/x11_platform.h
+++ b/src/x11_platform.h
@@ -134,6 +134,8 @@ typedef struct _GLFWwindowX11
Colormap colormap; // Window colormap
Window handle; // Window handle
Atom wmDeleteWindow; // WM_DELETE_WINDOW atom
+ Atom wmName; // _NET_WM_NAME atom
+ Atom wmIconName; // _NET_WM_ICON_NAME atom
Atom wmPing; // _NET_WM_PING atom
Atom wmState; // _NET_WM_STATE atom
Atom wmStateFullscreen; // _NET_WM_STATE_FULLSCREEN atom
diff --git a/src/x11_window.c b/src/x11_window.c
index 641b3c84..7ad34d05 100644
--- a/src/x11_window.c
+++ b/src/x11_window.c
@@ -195,6 +195,12 @@ static GLboolean hasEWMH(_GLFWwindow* window)
window->X11.wmStateFullscreen =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN");
+ window->X11.wmName =
+ getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_NAME");
+
+ window->X11.wmIconName =
+ getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_ICON_NAME");
+
window->X11.wmPing =
getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_PING");
@@ -1509,9 +1515,39 @@ void _glfwPlatformCloseWindow(_GLFWwindow* window)
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
{
- // Set window & icon title
- XStoreName(_glfwLibrary.X11.display, window->X11.handle, title);
- XSetIconName(_glfwLibrary.X11.display, window->X11.handle, title);
+ Atom type = XInternAtom(_glfwLibrary.X11.display, "UTF8_STRING", False);
+
+#if defined(X_HAVE_UTF8_STRING)
+ Xutf8SetWMProperties(_glfwLibrary.X11.display,
+ window->X11.handle,
+ title, title,
+ NULL, 0,
+ NULL, NULL, NULL);
+#else
+ // This may be a slightly better fallback than using XStoreName and
+ // XSetIconName, which always store their arguments using STRING
+ XmbSetWMProperties(_glfwLibrary.X11.display,
+ window->X11.handle,
+ title, title,
+ NULL, 0,
+ NULL, NULL, NULL);
+#endif
+
+ if (window->X11.wmName != None)
+ {
+ XChangeProperty(_glfwLibrary.X11.display, window->X11.handle,
+ window->X11.wmName, type, 8,
+ PropModeReplace,
+ (unsigned char*) title, strlen(title));
+ }
+
+ if (window->X11.wmIconName != None)
+ {
+ XChangeProperty(_glfwLibrary.X11.display, window->X11.handle,
+ window->X11.wmIconName, type, 8,
+ PropModeReplace,
+ (unsigned char*) title, strlen(title));
+ }
}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 5adbf351..d7ac0954 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -51,10 +51,13 @@ target_link_libraries(sharing ${STATIC_DEPS})
add_executable(tearing WIN32 MACOSX_BUNDLE tearing.c)
target_link_libraries(tearing ${STATIC_DEPS})
+add_executable(title WIN32 MACOSX_BUNDLE title.c)
+target_link_libraries(title ${STATIC_DEPS})
+
add_executable(windows WIN32 MACOSX_BUNDLE windows.c)
target_link_libraries(windows ${STATIC_DEPS})
-set(WINDOWS_BINARIES accuracy sharing tearing windows)
+set(WINDOWS_BINARIES accuracy sharing tearing title windows)
set(CONSOLE_BINARIES defaults events fsaa fsfocus gamma glfwinfo iconify
joysticks listmodes peter reopen)
diff --git a/tests/title.c b/tests/title.c
new file mode 100644
index 00000000..35344a04
--- /dev/null
+++ b/tests/title.c
@@ -0,0 +1,70 @@
+//========================================================================
+// UTF-8 window title test
+// Copyright (c) Camilla Berglund
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would
+// be appreciated but is not required.
+//
+// 2. Altered source versions must be plainly marked as such, and must not
+// be misrepresented as being the original software.
+//
+// 3. This notice may not be removed or altered from any source
+// distribution.
+//
+//========================================================================
+//
+// This test sets a UTF-8 window title
+//
+//========================================================================
+
+#include
+
+#include
+#include
+
+static void window_size_callback(GLFWwindow window, int width, int height)
+{
+ glViewport(0, 0, width, height);
+}
+
+int main(void)
+{
+ GLFWwindow window;
+
+ if (!glfwInit(NULL))
+ {
+ fprintf(stderr, "Failed to initialize GLFW: %s\n", glfwErrorString(glfwGetError()));
+ exit(EXIT_FAILURE);
+ }
+
+ window = glfwOpenWindow(0, 0, GLFW_WINDOWED, "English 日本語 русский язык 官話", NULL);
+ if (!window)
+ {
+ fprintf(stderr, "Failed to open GLFW window: %s\n", glfwErrorString(glfwGetError()));
+ exit(EXIT_FAILURE);
+ }
+
+ glfwSwapInterval(1);
+
+ glfwSetWindowSizeCallback(window_size_callback);
+
+ while (glfwIsWindow(window) == GL_TRUE)
+ {
+ glClear(GL_COLOR_BUFFER_BIT);
+ glfwSwapBuffers();
+ glfwWaitEvents();
+ }
+
+ exit(EXIT_SUCCESS);
+}
+