Text encoding improvements.

This commit is contained in:
Ioannis Tsakpinis 2011-08-20 16:38:45 +00:00
parent 6502050630
commit 87c04cc995
6 changed files with 187 additions and 121 deletions

View File

@ -33,26 +33,28 @@ package org.lwjgl;
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,42 +192,7 @@ public final class MemoryUtil {
// --- [ String utilities ] ---
/**
* Returns the specified text as a null-terminated CharBuffer.
*
* @param text the text to encode
*
* @return the encoded text
*/
public static CharBuffer encodeUTF16(final CharSequence text) {
CharBuffer buffer = BufferUtils.createCharBuffer(text.length() + 1);
buffer.append(text).append('\0');
buffer.flip();
return buffer;
}
/**
* Returns the specified text array as a CharBuffer. The CharBuffer is packed
* and each text is null-terminated.
*
* @param text the text array to encode
*
* @return the encoded text
*/
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;
}
/**
* Encodes and null-terminated the specified text and returns a ByteBuffer.
* 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
@ -235,16 +202,116 @@ public final class MemoryUtil {
* @see String#getBytes()
*/
public static ByteBuffer encodeASCII(final CharSequence text) {
return encode(text, ascii);
}
/**
* Returns a ByteBuffer containing the specified text UTF-8 encoded and null-terminated.
* If text is null, null is returned.
*
* @param text the text to encode
*
* @return the encoded text or null
*
* @see String#getBytes()
*/
public static ByteBuffer encodeUTF8(final CharSequence text) {
return encode(text, utf8);
}
/**
* 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
*/
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.
*
* @param text the text to encode
* @param charset the charset to use for encoding
*
* @return the encoded 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
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())));
}
return buffer;
}
interface Accessor {
@ -307,4 +374,4 @@ public final class MemoryUtil {
throw new NoSuchFieldException(fieldName + " does not exist in " + type.getSimpleName() + " or any of its superclasses.");
}
}
}

View File

@ -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 @@ final class WindowsSysImplementation extends DefaultSysImplementation {
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();

View File

@ -53,6 +53,7 @@ import java.lang.reflect.InvocationTargetException;
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 @@ final class LinuxDisplay implements DisplayImplementation {
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 @@ final class LinuxDisplay implements DisplayImplementation {
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 @@ final class LinuxDisplay implements DisplayImplementation {
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 @@ final class LinuxDisplay implements DisplayImplementation {
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 @@ final class LinuxDisplay implements DisplayImplementation {
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 @@ final class LinuxDisplay implements DisplayImplementation {
}
}
}
/**
* 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 @@ final class LinuxDisplay implements DisplayImplementation {
public boolean isInsideWindow() {
return mouseInside;
}
public void setResizable(boolean resizable) {
}
public boolean wasResized() {
return false;
}
@ -1552,4 +1554,4 @@ final class LinuxDisplay implements DisplayImplementation {
}
}
}
}

View File

@ -438,7 +438,7 @@ final class WindowsDisplay implements DisplayImplementation {
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);

View File

@ -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 GLXWindow glx_window = None;
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 @@ static int global_error_handler(Display *disp, XErrorEvent *error) {
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 @@ static bool isLegacyFullscreen(jint window_mode) {
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 @@ JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplayPeerInfo_initDefaultPee
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 @@ static void updateWindowHints(JNIEnv *env, Display *disp, Window window) {
throwException(env, "XAllocWMHints failed");
return;
}
win_hints->flags = InputHint;
win_hints->input = True;
if (current_icon_pixmap != 0) {
@ -321,10 +320,10 @@ static Window createWindow(JNIEnv* env, Display *disp, int screen, jint window_m
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 @@ static Pixmap createPixmapFromBuffer(JNIEnv *env, Display *disp, Window window,
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 @@ static void setIcon(JNIEnv *env, Display *disp, Window window, char *rgb_data, i
freeIconPixmap(disp);
return;
}
updateWindowHints(env, disp, window);
}

View File

@ -167,15 +167,16 @@ static bool isLegacyFullscreen(jint window_mode) {
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 @@ JNIEXPORT void JNICALL Java_org_lwjgl_opengl_LinuxDisplayPeerInfo_initDefaultPee
//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) {