From: <sp...@us...> - 2011-08-20 16:38:52
|
Revision: 3620 http://java-game-lib.svn.sourceforge.net/java-game-lib/?rev=3620&view=rev Author: spasi Date: 2011-08-20 16:38:45 +0000 (Sat, 20 Aug 2011) Log Message: ----------- Text encoding improvements. Modified Paths: -------------- trunk/LWJGL/src/java/org/lwjgl/MemoryUtil.java trunk/LWJGL/src/java/org/lwjgl/WindowsSysImplementation.java trunk/LWJGL/src/java/org/lwjgl/opengl/LinuxDisplay.java trunk/LWJGL/src/java/org/lwjgl/opengl/WindowsDisplay.java trunk/LWJGL/src/native/linux/opengl/org_lwjgl_opengl_Display.c trunk/LWJGL/src/native/linux/opengles/org_lwjgl_opengl_Display.c Modified: trunk/LWJGL/src/java/org/lwjgl/MemoryUtil.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/MemoryUtil.java 2011-08-20 11:56:46 UTC (rev 3619) +++ trunk/LWJGL/src/java/org/lwjgl/MemoryUtil.java 2011-08-20 16:38:45 UTC (rev 3620) @@ -33,26 +33,28 @@ import java.lang.reflect.Field; import java.nio.*; +import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; /** * [INTERNAL USE ONLY] * <p/> - * This class provides utility methods for passing buffer addresses to JNI API calls. + * This class provides utility methods for passing buffers to JNI API calls. * * @author Spasi */ public final class MemoryUtil { - private static final CharsetEncoder textEncoder; + private static final Charset ascii; + private static final Charset utf8; + private static final Charset utf16; static { - CharsetEncoder encoder = Charset.defaultCharset().newEncoder(); - if ( 1.0f < encoder.maxBytesPerChar() ) - encoder = Charset.forName("ISO-8859-1").newEncoder(); - - textEncoder = encoder; + ascii = Charset.forName("ISO-8859-1"); + utf8 = Charset.forName("UTF-8"); + utf16 = Charset.forName("UTF-16LE"); } private static final Accessor memUtil; @@ -190,63 +192,128 @@ // --- [ String utilities ] --- /** - * Returns the specified text as a null-terminated CharBuffer. + * Returns a ByteBuffer containing the specified text ASCII encoded and null-terminated. + * If text is null, null is returned. * * @param text the text to encode * - * @return the encoded text + * @return the encoded text or null + * + * @see String#getBytes() */ - public static CharBuffer encodeUTF16(final CharSequence text) { - CharBuffer buffer = BufferUtils.createCharBuffer(text.length() + 1); - buffer.append(text).append('\0'); - buffer.flip(); - return buffer; + public static ByteBuffer encodeASCII(final CharSequence text) { + return encode(text, ascii); } /** - * Returns the specified text array as a CharBuffer. The CharBuffer is packed - * and each text is null-terminated. + * Returns a ByteBuffer containing the specified text UTF-8 encoded and null-terminated. + * If text is null, null is returned. * - * @param text the text array to encode + * @param text the text to encode * - * @return the encoded text + * @return the encoded text or null + * + * @see String#getBytes() */ - public static CharBuffer encodeUTF16(final CharSequence... text) { - int len = 0; - for ( CharSequence cs : text ) - len += cs.length(); - - final CharBuffer buffer = BufferUtils.createCharBuffer(len + text.length); - for ( CharSequence cs : text ) - buffer.append(cs).append('\0'); - - buffer.flip(); - return buffer; + public static ByteBuffer encodeUTF8(final CharSequence text) { + return encode(text, utf8); } /** - * Encodes and null-terminated the specified text and returns a ByteBuffer. + * Returns a ByteBuffer containing the specified text UTF-16LE encoded and null-terminated. * If text is null, null is returned. * * @param text the text to encode * - * @return the encoded text or null + * @return the encoded text + */ + public static ByteBuffer encodeUTF16(final CharSequence text) { + return encode(text, utf16); + } + + /** + * Wraps the specified text in a null-terminated CharBuffer and encodes it using the specified Charset. * - * @see String#getBytes() + * @param text the text to encode + * @param charset the charset to use for encoding + * + * @return the encoded text */ - public static ByteBuffer encodeASCII(final CharSequence text) { + private static ByteBuffer encode(final CharSequence text, final Charset charset) { if ( text == null ) return null; - final ByteBuffer buffer = BufferUtils.createByteBuffer(text.length() + 1); + return encode(CharBuffer.wrap(new CharSequenceNT(text)), charset); + } - textEncoder.encode(CharBuffer.wrap(text), buffer, true); - buffer.put((byte)0); - buffer.flip(); + /** + * A {@link CharsetEncoder#encode(java.nio.CharBuffer)} implementation that uses {@link BufferUtils#createByteBuffer(int)} + * instead of {@link ByteBuffer#allocate(int)}. + * + * @see CharsetEncoder#encode(java.nio.CharBuffer) + */ + private static ByteBuffer encode(final CharBuffer in, final Charset charset) { + final CharsetEncoder encoder = charset.newEncoder(); // encoders are not thread-safe, create a new one on every call - return buffer; + int n = (int)(in.remaining() * encoder.averageBytesPerChar()); + ByteBuffer out = BufferUtils.createByteBuffer(n); + + if ( n == 0 && in.remaining() == 0 ) + return out; + + encoder.reset(); + while ( true ) { + CoderResult cr = in.hasRemaining() ? encoder.encode(in, out, true) : CoderResult.UNDERFLOW; + if ( cr.isUnderflow() ) + cr = encoder.flush(out); + + if ( cr.isUnderflow() ) + break; + + if ( cr.isOverflow() ) { + n = 2 * n + 1; // Ensure progress; n might be 0! + ByteBuffer o = BufferUtils.createByteBuffer(n); + out.flip(); + o.put(out); + out = o; + continue; + } + + try { + cr.throwException(); + } catch (CharacterCodingException e) { + throw new RuntimeException(e); + } + } + out.flip(); + return out; } + /** A null-terminated CharSequence. */ + private static class CharSequenceNT implements CharSequence { + + final CharSequence source; + + CharSequenceNT(CharSequence source) { + this.source = source; + } + + public int length() { + return source.length() + 1; + + } + + public char charAt(final int index) { + return index == source.length() ? '\0' : source.charAt(index); + + } + + public CharSequence subSequence(final int start, final int end) { + return new CharSequenceNT(source.subSequence(start, Math.min(end, source.length()))); + } + + } + interface Accessor { long getAddress(Buffer buffer); @@ -307,4 +374,4 @@ throw new NoSuchFieldException(fieldName + " does not exist in " + type.getSimpleName() + " or any of its superclasses."); } -} \ No newline at end of file +} Modified: trunk/LWJGL/src/java/org/lwjgl/WindowsSysImplementation.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/WindowsSysImplementation.java 2011-08-20 11:56:46 UTC (rev 3619) +++ trunk/LWJGL/src/java/org/lwjgl/WindowsSysImplementation.java 2011-08-20 16:38:45 UTC (rev 3620) @@ -31,7 +31,7 @@ */ package org.lwjgl; -import java.nio.CharBuffer; +import java.nio.ByteBuffer; import java.security.PrivilegedExceptionAction; import java.security.PrivilegedActionException; import java.security.AccessController; @@ -99,10 +99,9 @@ LWJGLUtil.log(String.format("*** Alert *** %s\n%s\n", title, message)); - // Pack both strings in the same buffer - final CharBuffer buffer = MemoryUtil.encodeUTF16(title, message); - final long address = MemoryUtil.getAddress0(buffer); - nAlert(getHwnd(), address, address + (title.length() + 1) * 2); + final ByteBuffer titleText = MemoryUtil.encodeUTF16(title); + final ByteBuffer messageText = MemoryUtil.encodeUTF16(message); + nAlert(getHwnd(), MemoryUtil.getAddress(titleText), MemoryUtil.getAddress(messageText)); } private static native void nAlert(long parent_hwnd, long title, long message); private static native void initCommonControls(); Modified: trunk/LWJGL/src/java/org/lwjgl/opengl/LinuxDisplay.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/opengl/LinuxDisplay.java 2011-08-20 11:56:46 UTC (rev 3619) +++ trunk/LWJGL/src/java/org/lwjgl/opengl/LinuxDisplay.java 2011-08-20 16:38:45 UTC (rev 3620) @@ -53,6 +53,7 @@ import org.lwjgl.BufferUtils; import org.lwjgl.LWJGLException; import org.lwjgl.LWJGLUtil; +import org.lwjgl.MemoryUtil; import org.lwjgl.opengl.XRandR.Screen; import org.lwjgl.opengles.EGL; @@ -138,7 +139,7 @@ private long current_cursor; private long blank_cursor; private boolean mouseInside = true; - + private Canvas parent; private long parent_window; private boolean xembedded; @@ -148,7 +149,7 @@ private LinuxKeyboard keyboard; private LinuxMouse mouse; - + private final FocusListener focus_listener = new FocusListener() { public void focusGained(FocusEvent e) { synchronized (GlobalLock.lock) { @@ -499,7 +500,7 @@ private static native void reparentWindow(long display, long window, long parent, int x, int y); private static native long nGetInputFocus(long display) throws LWJGLException; private static native void nSetInputFocus(long display, long window, long time); - + private static boolean isAncestorXEmbedded(long window) throws LWJGLException { long xembed_atom = internAtom("_XEMBED_INFO", true); if (xembed_atom != None) { @@ -728,12 +729,13 @@ public void setTitle(String title) { lockAWT(); try { - nSetTitle(getDisplay(), getWindow(), title); + final ByteBuffer titleText = MemoryUtil.encodeUTF8(title); + nSetTitle(getDisplay(), getWindow(), MemoryUtil.getAddress(titleText), titleText.remaining() - 1); } finally { unlockAWT(); } } - private static native void nSetTitle(long display, long window, String title); + private static native void nSetTitle(long display, long window, long title, int len); public boolean isCloseRequested() { boolean result = close_requested; @@ -915,19 +917,19 @@ unlockAWT(); } } - + private void checkInput() { if (parent == null) return; - + if (xembedded) { long current_focus_window = 0; - + try { current_focus_window = nGetInputFocus(getDisplay()); } catch (LWJGLException e) { return; // fail silently as it can fail whilst splitting browser tabs } - + if (last_window_focus != current_focus_window || parent_focused != focused) { if (isParentWindowActive(current_focus_window)) { if (parent_focused) { @@ -963,49 +965,49 @@ } } } - + /** * This method will check if the parent window is active when running - * in xembed mode. Every xembed embedder window has a focus proxy - * window that recieves all the input. This method will test whether - * the provided window handle is the focus proxy, if so it will get its + * in xembed mode. Every xembed embedder window has a focus proxy + * window that recieves all the input. This method will test whether + * the provided window handle is the focus proxy, if so it will get its * parent window and then test whether this is an ancestor to our * current_window. If so then parent window is active. - * + * * @param window - the window handle to test */ private boolean isParentWindowActive(long window) { try { // parent window already active as window is current_window if (window == current_window) return true; - + // xembed focus proxy will have no children if (getChildCount(getDisplay(), window) != 0) return false; - + // get parent, will be xembed embedder window and ancestor of current_window long parent_window = getParentWindow(getDisplay(), window); - + // parent must not be None if (parent_window == None) return false; - + // scroll current_window's ancestors to find parent_window long w = current_window; - + while (w != None) { w = getParentWindow(getDisplay(), w); if (w == parent_window) { parent_proxy_focus_window = window; // save focus proxy window return true; } - } + } } catch (LWJGLException e) { LWJGLUtil.log("Failed to detect if parent window is active: " + e.getMessage()); return true; // on failure assume still active } - + return false; // failed to find an active parent window } - + private void setFocused(boolean got_focus, int focus_detail) { if (focused == got_focus || focus_detail == NotifyDetailNone || focus_detail == NotifyPointer || focus_detail == NotifyPointerRoot || parent != null) return; @@ -1356,11 +1358,11 @@ public boolean isInsideWindow() { return mouseInside; } - + public void setResizable(boolean resizable) { - + } - + public boolean wasResized() { return false; } @@ -1552,4 +1554,4 @@ } } -} \ No newline at end of file +} Modified: trunk/LWJGL/src/java/org/lwjgl/opengl/WindowsDisplay.java =================================================================== --- trunk/LWJGL/src/java/org/lwjgl/opengl/WindowsDisplay.java 2011-08-20 11:56:46 UTC (rev 3619) +++ trunk/LWJGL/src/java/org/lwjgl/opengl/WindowsDisplay.java 2011-08-20 16:38:45 UTC (rev 3620) @@ -438,7 +438,7 @@ private static native DisplayMode getCurrentDisplayMode() throws LWJGLException; public void setTitle(String title) { - CharBuffer buffer = MemoryUtil.encodeUTF16(title); + ByteBuffer buffer = MemoryUtil.encodeUTF16(title); nSetTitle(hwnd, MemoryUtil.getAddress0(buffer)); } private static native void nSetTitle(long hwnd, long title); Modified: trunk/LWJGL/src/native/linux/opengl/org_lwjgl_opengl_Display.c =================================================================== --- trunk/LWJGL/src/native/linux/opengl/org_lwjgl_opengl_Display.c 2011-08-20 11:56:46 UTC (rev 3619) +++ trunk/LWJGL/src/native/linux/opengl/org_lwjgl_opengl_Display.c 2011-08-20 16:38:45 UTC (rev 3620) @@ -1,31 +1,31 @@ -/* +/* * Copyright (c) 2002-2008 LWJGL Project * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are + * modification, are permitted provided that the following conditions are * met: - * - * * Redistributions of source code must retain the above copyright + * + * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * * Neither the name of 'LWJGL' nor the names of - * its contributors may be used to endorse or promote products derived + * * Neither the name of 'LWJGL' nor the names of + * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ @@ -72,8 +72,8 @@ static Colormap cmap; static int current_depth; -static Pixmap current_icon_pixmap; -static Pixmap current_icon_mask_pixmap; +static Pixmap current_icon_pixmap; +static Pixmap current_icon_mask_pixmap; static Visual *current_visual; @@ -94,7 +94,7 @@ jmethodID handler_method = (*env)->GetStaticMethodID(env, org_lwjgl_LinuxDisplay_class, "globalErrorHandler", "(JJJJJJJ)I"); if (handler_method == NULL) return 0; - return (*env)->CallStaticIntMethod(env, org_lwjgl_LinuxDisplay_class, handler_method, (jlong)(intptr_t)disp, (jlong)(intptr_t)error, + return (*env)->CallStaticIntMethod(env, org_lwjgl_LinuxDisplay_class, handler_method, (jlong)(intptr_t)disp, (jlong)(intptr_t)error, (jlong)(intptr_t)error->display, (jlong)error->serial, (jlong)error->error_code, (jlong)error->request_code, (jlong)error->minor_code); } else return 0; @@ -170,15 +170,16 @@ return window_mode == org_lwjgl_opengl_LinuxDisplay_FULLSCREEN_LEGACY; } -static void setWindowTitle(Display *disp, Window window, const char *title) { - XStoreName(disp, window, title); +static void setWindowTitle(Display *disp, Window window, jlong title, jint len) { + // ASCII fallback if XChangeProperty fails. + XStoreName(disp, window, (const char *)(intptr_t)title); - // tell WM to use Unicode + // Set the UTF-8 encoded title XChangeProperty(disp, window, XInternAtom(disp, "_NET_WM_NAME", False), XInternAtom(disp, "UTF8_STRING", False), - 8, PropModeReplace, (unsigned char *) title, - strlen(title)); + 8, PropModeReplace, (const char *)(intptr_t)title, + len); } JNIEXPORT jlong JNICALL Java_org_lwjgl_opengl_LinuxDisplay_openDisplay(JNIEnv *env, jclass clazz) { @@ -202,13 +203,11 @@ Display *disp = (Display *)(intptr_t)display; initPeerInfo(env, peer_info_handle, disp, screen, pixel_format, true, GLX_WINDOW_BIT, true, false); } - -JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_nSetTitle(JNIEnv * env, jclass clazz, jlong display, jlong window_ptr, jstring title_obj) { + +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_nSetTitle(JNIEnv * env, jclass clazz, jlong display, jlong window_ptr, jlong title, jint len) { Display *disp = (Display *)(intptr_t)display; Window window = (Window)window_ptr; - char * title = GetStringNativeChars(env, title_obj); - setWindowTitle(disp, window, title); - free(title); + setWindowTitle(disp, window, title, len); } static void freeIconPixmap(Display *disp) { @@ -285,7 +284,7 @@ throwException(env, "XAllocWMHints failed"); return; } - + win_hints->flags = InputHint; win_hints->input = True; if (current_icon_pixmap != 0) { @@ -321,10 +320,10 @@ attribs.override_redirect = True; } win = XCreateWindow(disp, parent, x, y, width, height, 0, vis_info->depth, InputOutput, vis_info->visual, attribmask, &attribs); - + current_depth = vis_info->depth; current_visual = vis_info->visual; - + XFree(vis_info); if (!checkXError(env, disp)) { XFreeColormap(disp, cmap); @@ -511,7 +510,7 @@ throwException(env, "XCreateImage failed"); return None; } - + GC gc = XCreateGC(disp, pixmap, 0, NULL); XPutImage(disp, pixmap, gc, image, 0, 0, 0, 0, width, height); XFreeGC(disp, gc); @@ -530,7 +529,7 @@ freeIconPixmap(disp); return; } - + updateWindowHints(env, disp, window); } Modified: trunk/LWJGL/src/native/linux/opengles/org_lwjgl_opengl_Display.c =================================================================== --- trunk/LWJGL/src/native/linux/opengles/org_lwjgl_opengl_Display.c 2011-08-20 11:56:46 UTC (rev 3619) +++ trunk/LWJGL/src/native/linux/opengles/org_lwjgl_opengl_Display.c 2011-08-20 16:38:45 UTC (rev 3620) @@ -167,15 +167,16 @@ return window_mode == org_lwjgl_opengl_LinuxDisplay_FULLSCREEN_LEGACY; } -static void setWindowTitle(Display *disp, Window window, const char *title) { - XStoreName(disp, window, title); +static void setWindowTitle(Display *disp, Window window, jlong title, jint len) { + // ASCII fallback if XChangeProperty fails. + XStoreName(disp, window, (const char *)(intptr_t)title); - // tell WM to use Unicode + // Set the UTF-8 encoded title XChangeProperty(disp, window, XInternAtom(disp, "_NET_WM_NAME", False), XInternAtom(disp, "UTF8_STRING", False), - 8, PropModeReplace, (unsigned char *) title, - strlen(title)); + 8, PropModeReplace, (const char *)(intptr_t)title, + len); } JNIEXPORT jlong JNICALL Java_org_lwjgl_opengl_LinuxDisplay_openDisplay(JNIEnv *env, jclass clazz) { @@ -197,12 +198,10 @@ //initPeerInfo(env, peer_info_handle, disp, screen); } -JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_nSetTitle(JNIEnv * env, jclass clazz, jlong display, jlong window_ptr, jstring title_obj) { +JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplay_nSetTitle(JNIEnv * env, jclass clazz, jlong display, jlong window_ptr, jlong title, jint len) { Display *disp = (Display *)(intptr_t)display; Window window = (Window)window_ptr; - char * title = GetStringNativeChars(env, title_obj); - setWindowTitle(disp, window, title); - free(title); + setWindowTitle(disp, window, title, len); } static void freeIconPixmap(Display *disp) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |