mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-15 16:09:44 +00:00
354 lines
16 KiB
Java
354 lines
16 KiB
Java
/*
|
|
* Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
package apple.laf;
|
|
|
|
import java.nio.*;
|
|
import java.util.*;
|
|
|
|
import apple.laf.JRSUIConstants.*;
|
|
|
|
public final class JRSUIControl {
|
|
private static native int initNativeJRSUI();
|
|
|
|
private static native long getPtrOfBuffer(ByteBuffer byteBuffer);
|
|
private static native long getCFDictionary(boolean flipped);
|
|
private static native void disposeCFDictionary(long cfDictionaryPtr);
|
|
|
|
private static native int syncChanges(long cfDictionaryPtr, long byteBufferPtr);
|
|
|
|
// private static native int paint(long cfDictionaryPtr, long oldProperties, long newProperties, OSXSurfaceData osxsd, double x, double y, double w, double h);
|
|
// private static native int paintChanges(long cfDictionaryPtr, long byteBufferPtr, long oldProperties, long newProperties, OSXSurfaceData osxsd, double x, double y, double w, double h);
|
|
|
|
private static native int paintToCGContext (long cgContext, long cfDictionaryPtr, long oldProperties, long newProperties, double x, double y, double w, double h);
|
|
private static native int paintChangesToCGContext (long cgContext, long cfDictionaryPtr, long oldProperties, long newProperties, double x, double y, double w, double h, long byteBufferPtr);
|
|
|
|
private static native int paintImage (int[] data, int imgW, int imgH, long cfDictionaryPtr, long oldProperties, long newProperties, double x, double y, double w, double h);
|
|
private static native int paintChangesImage (int[] data, int imgW, int imgH, long cfDictionaryPtr, long oldProperties, long newProperties, double x, double y, double w, double h, long byteBufferPtr);
|
|
|
|
private static native int getNativeHitPart( long cfDictionaryPtr, long oldProperties, long newProperties, double x, double y, double w, double h, double hitX, double hitY);
|
|
private static native void getNativePartBounds(final double[] rect, long cfDictionaryPtr, long oldProperties, long newProperties, double x, double y, double w, double h, int part);
|
|
private static native double getNativeScrollBarOffsetChange( long cfDictionaryPtr, long oldProperties, long newProperties, double x, double y, double w, double h, int offset, int visibleAmount, int extent);
|
|
|
|
private static final int INCOHERENT = 2;
|
|
private static final int NOT_INIT = 1;
|
|
private static final int SUCCESS = 0;
|
|
private static final int NULL_PTR = -1;
|
|
private static final int NULL_CG_REF = -2;
|
|
|
|
private static int nativeJRSInitialized = NOT_INIT;
|
|
|
|
|
|
public static void initJRSUI() {
|
|
if (nativeJRSInitialized == SUCCESS) return;
|
|
nativeJRSInitialized = initNativeJRSUI();
|
|
if (nativeJRSInitialized != SUCCESS) throw new RuntimeException("JRSUI could not be initialized (" + nativeJRSInitialized + ").");
|
|
}
|
|
|
|
private static final int NIO_BUFFER_SIZE = 128;
|
|
private static final class ThreadLocalByteBuffer {
|
|
final ByteBuffer buffer;
|
|
final long ptr;
|
|
|
|
public ThreadLocalByteBuffer() {
|
|
buffer = ByteBuffer.allocateDirect(NIO_BUFFER_SIZE);
|
|
buffer.order(ByteOrder.nativeOrder());
|
|
ptr = getPtrOfBuffer(buffer);
|
|
}
|
|
}
|
|
|
|
private static final ThreadLocal<ThreadLocalByteBuffer> threadLocal = new ThreadLocal<ThreadLocalByteBuffer>();
|
|
private static ThreadLocalByteBuffer getThreadLocalBuffer() {
|
|
ThreadLocalByteBuffer byteBuffer = threadLocal.get();
|
|
if (byteBuffer != null) return byteBuffer;
|
|
|
|
byteBuffer = new ThreadLocalByteBuffer();
|
|
threadLocal.set(byteBuffer);
|
|
return byteBuffer;
|
|
}
|
|
|
|
private final HashMap<Key, DoubleValue> nativeMap;
|
|
private final HashMap<Key, DoubleValue> changes;
|
|
private long cfDictionaryPtr;
|
|
|
|
private long priorEncodedProperties;
|
|
private long currentEncodedProperties;
|
|
private final boolean flipped;
|
|
|
|
public JRSUIControl(final boolean flipped){
|
|
this.flipped = flipped;
|
|
cfDictionaryPtr = getCFDictionary(flipped);
|
|
if (cfDictionaryPtr == 0) throw new RuntimeException("Unable to create native representation");
|
|
nativeMap = new HashMap<Key, DoubleValue>();
|
|
changes = new HashMap<Key, DoubleValue>();
|
|
}
|
|
|
|
JRSUIControl(final JRSUIControl other) {
|
|
flipped = other.flipped;
|
|
cfDictionaryPtr = getCFDictionary(flipped);
|
|
if (cfDictionaryPtr == 0) throw new RuntimeException("Unable to create native representation");
|
|
nativeMap = new HashMap<Key, DoubleValue>();
|
|
changes = new HashMap<Key, DoubleValue>(other.nativeMap);
|
|
changes.putAll(other.changes);
|
|
}
|
|
|
|
@Override
|
|
@SuppressWarnings("removal")
|
|
protected synchronized void finalize() throws Throwable {
|
|
if (cfDictionaryPtr == 0) return;
|
|
disposeCFDictionary(cfDictionaryPtr);
|
|
cfDictionaryPtr = 0;
|
|
}
|
|
|
|
|
|
enum BufferState {
|
|
NO_CHANGE,
|
|
ALL_CHANGES_IN_BUFFER,
|
|
SOME_CHANGES_IN_BUFFER,
|
|
CHANGE_WONT_FIT_IN_BUFFER
|
|
}
|
|
|
|
private BufferState loadBufferWithChanges(final ThreadLocalByteBuffer localByteBuffer) {
|
|
final ByteBuffer buffer = localByteBuffer.buffer;
|
|
buffer.rewind();
|
|
|
|
for (final JRSUIConstants.Key key : new HashSet<JRSUIConstants.Key>(changes.keySet())) {
|
|
final int changeIndex = buffer.position();
|
|
final JRSUIConstants.DoubleValue value = changes.get(key);
|
|
|
|
try {
|
|
buffer.putLong(key.getConstantPtr());
|
|
buffer.put(value.getTypeCode());
|
|
value.putValueInBuffer(buffer);
|
|
} catch (final BufferOverflowException e) {
|
|
return handleBufferOverflow(buffer, changeIndex);
|
|
} catch (final RuntimeException e) {
|
|
System.err.println(this);
|
|
throw e;
|
|
}
|
|
|
|
if (buffer.position() >= NIO_BUFFER_SIZE - 8) {
|
|
return handleBufferOverflow(buffer, changeIndex);
|
|
}
|
|
|
|
changes.remove(key);
|
|
nativeMap.put(key, value);
|
|
}
|
|
|
|
buffer.putLong(0);
|
|
return BufferState.ALL_CHANGES_IN_BUFFER;
|
|
}
|
|
|
|
private BufferState handleBufferOverflow(final ByteBuffer buffer, final int changeIndex) {
|
|
if (changeIndex == 0) {
|
|
buffer.putLong(0, 0);
|
|
return BufferState.CHANGE_WONT_FIT_IN_BUFFER;
|
|
}
|
|
|
|
buffer.putLong(changeIndex, 0);
|
|
return BufferState.SOME_CHANGES_IN_BUFFER;
|
|
}
|
|
|
|
private synchronized void set(final JRSUIConstants.Key key, final JRSUIConstants.DoubleValue value) {
|
|
final JRSUIConstants.DoubleValue existingValue = nativeMap.get(key);
|
|
|
|
if (existingValue != null && existingValue.equals(value)) {
|
|
changes.remove(key);
|
|
return;
|
|
}
|
|
|
|
changes.put(key, value);
|
|
}
|
|
|
|
public void set(final JRSUIState state) {
|
|
state.apply(this);
|
|
}
|
|
|
|
void setEncodedState(final long state) {
|
|
currentEncodedProperties = state;
|
|
}
|
|
|
|
void set(final JRSUIConstants.Key key, final double value) {
|
|
set(key, new JRSUIConstants.DoubleValue(value));
|
|
}
|
|
|
|
// private static final Color blue = new Color(0x00, 0x00, 0xFF, 0x40);
|
|
// private static void paintDebug(Graphics2D g, double x, double y, double w, double h) {
|
|
// final Color prev = g.getColor();
|
|
// g.setColor(blue);
|
|
// g.drawRect((int)x, (int)y, (int)w, (int)h);
|
|
// g.setColor(prev);
|
|
// }
|
|
|
|
// private static int paintsWithNoChange = 0;
|
|
// private static int paintsWithChangesThatFit = 0;
|
|
// private static int paintsWithChangesThatOverflowed = 0;
|
|
|
|
public void paint(final int[] data, final int imgW, final int imgH, final double x, final double y, final double w, final double h) {
|
|
paintImage(data, imgW, imgH, x, y, w, h);
|
|
priorEncodedProperties = currentEncodedProperties;
|
|
}
|
|
|
|
private synchronized int paintImage(final int[] data, final int imgW, final int imgH, final double x, final double y, final double w, final double h) {
|
|
if (changes.isEmpty()) {
|
|
// paintsWithNoChange++;
|
|
return paintImage(data, imgW, imgH, cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h);
|
|
}
|
|
|
|
final ThreadLocalByteBuffer localByteBuffer = getThreadLocalBuffer();
|
|
BufferState bufferState = loadBufferWithChanges(localByteBuffer);
|
|
|
|
// fast tracking this, since it's the likely scenario
|
|
if (bufferState == BufferState.ALL_CHANGES_IN_BUFFER) {
|
|
// paintsWithChangesThatFit++;
|
|
return paintChangesImage(data, imgW, imgH, cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h, localByteBuffer.ptr);
|
|
}
|
|
|
|
while (bufferState == BufferState.SOME_CHANGES_IN_BUFFER) {
|
|
final int status = syncChanges(cfDictionaryPtr, localByteBuffer.ptr);
|
|
if (status != SUCCESS) throw new RuntimeException("JRSUI failed to sync changes into the native buffer: " + this);
|
|
bufferState = loadBufferWithChanges(localByteBuffer);
|
|
}
|
|
|
|
if (bufferState == BufferState.CHANGE_WONT_FIT_IN_BUFFER) {
|
|
throw new RuntimeException("JRSUI failed to sync changes to the native buffer, because some change was too big: " + this);
|
|
}
|
|
|
|
// implicitly ALL_CHANGES_IN_BUFFER, now that we sync'd the buffer down to native a few times
|
|
// paintsWithChangesThatOverflowed++;
|
|
return paintChangesImage(data, imgW, imgH, cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h, localByteBuffer.ptr);
|
|
}
|
|
|
|
public void paint(final long cgContext, final double x, final double y, final double w, final double h) {
|
|
paintToCGContext(cgContext, x, y, w, h);
|
|
priorEncodedProperties = currentEncodedProperties;
|
|
}
|
|
|
|
private synchronized int paintToCGContext(final long cgContext, final double x, final double y, final double w, final double h) {
|
|
if (changes.isEmpty()) {
|
|
// paintsWithNoChange++;
|
|
return paintToCGContext(cgContext, cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h);
|
|
}
|
|
|
|
final ThreadLocalByteBuffer localByteBuffer = getThreadLocalBuffer();
|
|
BufferState bufferState = loadBufferWithChanges(localByteBuffer);
|
|
|
|
// fast tracking this, since it's the likely scenario
|
|
if (bufferState == BufferState.ALL_CHANGES_IN_BUFFER) {
|
|
// paintsWithChangesThatFit++;
|
|
return paintChangesToCGContext(cgContext, cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h, localByteBuffer.ptr);
|
|
}
|
|
|
|
while (bufferState == BufferState.SOME_CHANGES_IN_BUFFER) {
|
|
final int status = syncChanges(cfDictionaryPtr, localByteBuffer.ptr);
|
|
if (status != SUCCESS) throw new RuntimeException("JRSUI failed to sync changes into the native buffer: " + this);
|
|
bufferState = loadBufferWithChanges(localByteBuffer);
|
|
}
|
|
|
|
if (bufferState == BufferState.CHANGE_WONT_FIT_IN_BUFFER) {
|
|
throw new RuntimeException("JRSUI failed to sync changes to the native buffer, because some change was too big: " + this);
|
|
}
|
|
|
|
// implicitly ALL_CHANGES_IN_BUFFER, now that we sync'd the buffer down to native a few times
|
|
// paintsWithChangesThatOverflowed++;
|
|
return paintChangesToCGContext(cgContext, cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h, localByteBuffer.ptr);
|
|
}
|
|
|
|
|
|
Hit getHitForPoint(final int x, final int y, final int w, final int h, final int hitX, final int hitY) {
|
|
sync();
|
|
// reflect hitY about the midline of the control before sending to native
|
|
final Hit hit = JRSUIConstants.getHit(getNativeHitPart(cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h, hitX, 2 * y + h - hitY));
|
|
priorEncodedProperties = currentEncodedProperties;
|
|
return hit;
|
|
}
|
|
|
|
void getPartBounds(final double[] rect, final int x, final int y, final int w, final int h, final int part) {
|
|
if (rect == null) throw new NullPointerException("Cannot load null rect");
|
|
if (rect.length != 4) throw new IllegalArgumentException("Rect must have four elements");
|
|
|
|
sync();
|
|
getNativePartBounds(rect, cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h, part);
|
|
priorEncodedProperties = currentEncodedProperties;
|
|
}
|
|
|
|
double getScrollBarOffsetChange(final int x, final int y, final int w, final int h, final int offset, final int visibleAmount, final int extent) {
|
|
sync();
|
|
final double offsetChange = getNativeScrollBarOffsetChange(cfDictionaryPtr, priorEncodedProperties, currentEncodedProperties, x, y, w, h, offset, visibleAmount, extent);
|
|
priorEncodedProperties = currentEncodedProperties;
|
|
return offsetChange;
|
|
}
|
|
|
|
private void sync() {
|
|
if (changes.isEmpty()) return;
|
|
|
|
final ThreadLocalByteBuffer localByteBuffer = getThreadLocalBuffer();
|
|
BufferState bufferState = loadBufferWithChanges(localByteBuffer);
|
|
if (bufferState == BufferState.ALL_CHANGES_IN_BUFFER) {
|
|
final int status = syncChanges(cfDictionaryPtr, localByteBuffer.ptr);
|
|
if (status != SUCCESS) throw new RuntimeException("JRSUI failed to sync changes into the native buffer: " + this);
|
|
return;
|
|
}
|
|
|
|
while (bufferState == BufferState.SOME_CHANGES_IN_BUFFER) {
|
|
final int status = syncChanges(cfDictionaryPtr, localByteBuffer.ptr);
|
|
if (status != SUCCESS) throw new RuntimeException("JRSUI failed to sync changes into the native buffer: " + this);
|
|
bufferState = loadBufferWithChanges(localByteBuffer);
|
|
}
|
|
|
|
if (bufferState == BufferState.CHANGE_WONT_FIT_IN_BUFFER) {
|
|
throw new RuntimeException("JRSUI failed to sync changes to the native buffer, because some change was too big: " + this);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
int bits = Long.hashCode(currentEncodedProperties);
|
|
bits ^= nativeMap.hashCode();
|
|
bits ^= changes.hashCode();
|
|
return bits;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(final Object obj) {
|
|
if (!(obj instanceof JRSUIControl)) return false;
|
|
final JRSUIControl other = (JRSUIControl)obj;
|
|
if (currentEncodedProperties != other.currentEncodedProperties) return false;
|
|
if (!nativeMap.equals(other.nativeMap)) return false;
|
|
if (!changes.equals(other.changes)) return false;
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
final StringBuilder builder = new StringBuilder("JRSUIControl[inNative:");
|
|
builder.append(Arrays.toString(nativeMap.entrySet().toArray()));
|
|
builder.append(", changes:");
|
|
builder.append(Arrays.toString(changes.entrySet().toArray()));
|
|
builder.append("]");
|
|
return builder.toString();
|
|
}
|
|
}
|