Compare commits

...

5 Commits

Author SHA1 Message Date
Vitaly Provodin
e5dfcc14f9 fixup! update exclude list on results of 21.0.8_b1115 test 2025-09-14 02:11:18 +04:00
Vitaly Provodin
7658fcd86f Revert "JBR-9283 Enhance Window counters to provide statistics"
This reverts commit e4fbfeb597.
2025-09-13 18:36:28 +04:00
Vitaly Provodin
e868f7855c fixup! update exclude list on results of 21.0.8_b1115 test 2025-09-13 12:47:02 +04:00
Vitaly Provodin
bdc288d5c7 JBR-9125 move building JBR on AL2 instead of OL7 2025-09-13 07:12:32 +04:00
Vitaly Provodin
cce49caa2d Revert "JBR-4478 Implement support for native accessible caret events on Windows"
This reverts commit 88f1599bad.
2025-09-13 07:12:22 +04:00
22 changed files with 185 additions and 1027 deletions

View File

@@ -109,6 +109,12 @@ else
WITH_BUNDLED_FREETYPE=""
fi
if [ "$bundle_type" == "lb" ]; then
WITH_VULKAN=""
else
WITH_VULKAN="--with-vulkan"
fi
REPRODUCIBLE_BUILD_OPTS="--with-source-date=$SOURCE_DATE_EPOCH
--with-hotspot-build-time=$BUILD_TIME
--with-copyright-year=$COPYRIGHT_YEAR

View File

@@ -35,7 +35,7 @@ function do_configure {
--with-version-opt=b"$build_number" \
--with-boot-jdk="$BOOT_JDK" \
--enable-cds=yes \
--with-vulkan \
$WITH_VULKAN \
$DISABLE_WARNINGS_AS_ERRORS \
$STATIC_CONF_ARGS \
$REPRODUCIBLE_BUILD_OPTS \
@@ -113,6 +113,11 @@ case "$bundle_type" in
jbr_name_postfix="_${bundle_type}"
do_maketest=1
;;
"lb")
do_reset_changes=1
jbr_name_postfix="_${bundle_type}"
do_maketest=1
;;
"nomod" | "")
bundle_type=""
;;

View File

@@ -32,13 +32,6 @@ function do_configure {
--build=x86_64-unknown-linux-gnu \
--openjdk-target=x86_64-unknown-linux-gnu"
fi
if [ -n "${JCEF_BUILD_LEGACY:-}" ]; then
WITH_VULKAN=""
else
WITH_VULKAN="--with-vulkan"
fi
sh configure \
$WITH_DEBUG_LEVEL \
--with-vendor-name="$VENDOR_NAME" \
@@ -102,9 +95,7 @@ function create_image_bundle {
# jmod does not preserve file permissions (JDK-8173610)
[ -f "$IMAGES_DIR"/"$__root_dir"/lib/jcef_helper ] && chmod a+x "$IMAGES_DIR"/"$__root_dir"/lib/jcef_helper
if [ ! -n "${JCEF_BUILD_LEGACY:-}" ]; then
[ -f "$IMAGES_DIR"/"$__root_dir"/lib/cef_server ] && chmod a+x "$IMAGES_DIR"/"$__root_dir"/lib/cef_server
fi
[ -f "$IMAGES_DIR"/"$__root_dir"/lib/cef_server ] && chmod a+x "$IMAGES_DIR"/"$__root_dir"/lib/cef_server
echo Creating "$JBR".tar.gz ...
@@ -130,6 +121,11 @@ case "$bundle_type" in
jbr_name_postfix="_${bundle_type}"
do_maketest=1
;;
"lb")
do_reset_changes=1
jbr_name_postfix="_${bundle_type}"
do_maketest=1
;;
"nomod" | "")
bundle_type=""
;;
@@ -158,7 +154,7 @@ JBRSDK_BUNDLE=jbrsdk
echo Fixing permissions
chmod -R a+r $JSDK
if [ "$bundle_type" == "jcef" ]; then
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "lb" ]; then
git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
update_jsdk_mods $JSDK $JCEF_PATH/jmods $JSDK/jmods $JSDK_MODS_DIR || do_exit $?
cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not changed
@@ -171,7 +167,7 @@ create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" ||
# create sdk image bundle
modules=$(cat $JSDK/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\n//g) || do_exit $?
if [ "$bundle_type" == "jcef" ]|| [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "lb" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
fi
create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" $JBRSDK_BUNDLE $JSDK_MODS_DIR "$modules" || do_exit $?

View File

@@ -93,7 +93,7 @@ WITH_DEBUG_LEVEL="--with-debug-level=release"
RELEASE_NAME=linux-x86-server-release
case "$bundle_type" in
"jcef")
"jcef" | "lb")
echo "not implemented" && do_exit 1
;;
"nomod" | "")
@@ -120,7 +120,7 @@ JBRSDK_BUNDLE=jbrsdk
echo Fixing permissions
chmod -R a+r $JSDK
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "lb" ]; then
jbr_name_postfix="_${bundle_type}"
else
jbr_name_postfix=""

View File

@@ -215,7 +215,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \
LIBS_windows := kernel32.lib user32.lib gdi32.lib gdiplus.lib winspool.lib \
imm32.lib ole32.lib uuid.lib shell32.lib \
comdlg32.lib winmm.lib comctl32.lib shlwapi.lib \
delayimp.lib jvm.lib $(WIN_JAVA_LIB) advapi32.lib dwmapi.lib $(A11Y_NVDA_ANNOUNCING_LIBS) oleacc.lib, \
delayimp.lib jvm.lib $(WIN_JAVA_LIB) advapi32.lib dwmapi.lib $(A11Y_NVDA_ANNOUNCING_LIBS), \
VERSIONINFO_RESOURCE := $(LIBAWT_VERSIONINFO_RESOURCE), \
EXTRA_RCFLAGS := $(LIBAWT_RCFLAGS), \
))

View File

@@ -33,6 +33,7 @@ import sun.lwawt.macosx.CFLayer;
import sun.util.logging.PlatformLogger;
import java.awt.Component;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
import java.awt.Window;
@@ -149,25 +150,11 @@ public class MTLLayer extends CFLayer {
}
}
private final static String[] STAT_NAMES = new String[]{
"java2d.native.mtlLayer.drawInMTLContext", // type = 0
"java2d.native.mtlLayer.nextDrawable" // type = 1
};
private void addStat(int type, double value) {
// Called from the native code when this layer has been presented on screen
Component target = peer.getTarget();
if (target instanceof Window window) {
AWTAccessor.getWindowAccessor().addStat(window,
((type >= 0) && (type < STAT_NAMES.length)) ? STAT_NAMES[type] : "undefined", value);
}
}
private void countNewFrame() {
// Called from the native code when this layer has been presented on screen
Component target = peer.getTarget();
if (target instanceof Window window) {
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.frames");
AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.frames");
}
}
@@ -177,7 +164,7 @@ public class MTLLayer extends CFLayer {
// when those attempts are too frequent.
Component target = peer.getTarget();
if (target instanceof Window window) {
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.framesDropped");
AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.framesDropped");
}
}
}

View File

@@ -71,8 +71,6 @@
- (void) stopRedraw:(MTLContext*)mtlc displayID:(jint)displayID force:(BOOL)force;
- (void) flushBuffer;
- (void) commitCommandBuffer:(MTLContext*)mtlc wait:(BOOL)waitUntilCompleted display:(BOOL)updateDisplay;
- (void) addStatCallback:(int)type value:(double)value;
- (void) countFramePresentedCallback;
- (void) countFrameDroppedCallback;
@end

View File

@@ -226,22 +226,20 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
}
// Acquire CAMetalDrawable without blocking:
const CFTimeInterval beforeDrawableTime = CACurrentMediaTime();
const CFTimeInterval beforeDrawableTime = (TRACE_DISPLAY) ? CACurrentMediaTime() : 0.0;
const id<CAMetalDrawable> mtlDrawable = [self nextDrawable];
if (mtlDrawable == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: nextDrawable is null");
return;
}
const CFTimeInterval nextDrawableTime = CACurrentMediaTime();
const CFTimeInterval nextDrawableLatency = (nextDrawableTime - beforeDrawableTime);
const CFTimeInterval nextDrawableTime = (TRACE_DISPLAY) ? CACurrentMediaTime() : 0.0;
// rolling mean weight (lerp):
static const NSTimeInterval a = 0.25;
if (nextDrawableLatency > 0.0) {
[self addStatCallback:1 value:1000.0 * nextDrawableLatency]; // See MTLLayer.STAT_NAMES[1]
#if TRACE_DISPLAY_ON
const CFTimeInterval nextDrawableLatency = (nextDrawableTime - beforeDrawableTime);
if (nextDrawableLatency > 0.0) {
self.avgNextDrawableTime = nextDrawableLatency * a + self.avgNextDrawableTime * (1.0 - a);
}
J2dRlsTraceLn4(J2D_TRACE_VERBOSE,
@@ -251,7 +249,6 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
1000.0 * nextDrawableLatency, 1000.0 * self.avgNextDrawableTime
);
#endif
}
// Keep Fence from now:
releaseFence = NO;
@@ -392,15 +389,8 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
return;
}
const CFTimeInterval beforeMethod = CACurrentMediaTime();
(*env)->CallVoidMethod(env, javaLayerLocalRef, jm_drawInMTLContext);
CHECK_EXCEPTION();
const CFTimeInterval drawInMTLContextLatency = (CACurrentMediaTime() - beforeMethod);
if (drawInMTLContextLatency > 0.0) {
[self addStatCallback:0 value:1000.0 * drawInMTLContextLatency]; // See MTLLayer.STAT_NAMES[0]
}
(*env)->DeleteLocalRef(env, javaLayerLocalRef);
}
@@ -526,20 +516,6 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
}
}
- (void) addStatCallback:(int)type value:(double)value {
// attach the current thread to the JVM if necessary, and get an env
JNIEnv* env = [ThreadUtilities getJNIEnvUncached];
GET_MTL_LAYER_CLASS();
DECLARE_METHOD(jm_addStatFrame, jc_JavaLayer, "addStat", "(ID)V");
jobject javaLayerLocalRef = (*env)->NewLocalRef(env, self.javaLayer);
if (javaLayerLocalRef != NULL) {
(*env)->CallVoidMethod(env, javaLayerLocalRef, jm_addStatFrame, (jint)type, (jdouble)value);
CHECK_EXCEPTION();
(*env)->DeleteLocalRef(env, javaLayerLocalRef);
}
}
- (void) countFrameDroppedCallback {
// attach the current thread to the JVM if necessary, and get an env
JNIEnv* env = [ThreadUtilities getJNIEnvUncached];

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1995, 2021, 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
@@ -44,7 +44,6 @@ import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.io.PrintStream;
import java.io.Serial;
import java.io.Serializable;
import java.lang.annotation.Native;
@@ -55,15 +54,12 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EventListener;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.HashMap;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -74,15 +70,12 @@ import javax.accessibility.AccessibleState;
import javax.accessibility.AccessibleStateSet;
import com.jetbrains.exported.JBRApi;
import jdk.internal.misc.InnocuousThread;
import sun.awt.AWTAccessor;
import sun.awt.AWTPermissions;
import sun.awt.AppContext;
import sun.awt.DebugSettings;
import sun.awt.SunToolkit;
import sun.awt.util.IdentityArrayList;
import sun.awt.util.ThreadGroupUtils;
import sun.java2d.marlin.stats.StatDouble;
import sun.java2d.pipe.Region;
import sun.security.action.GetPropertyAction;
import sun.util.logging.PlatformLogger;
@@ -1214,51 +1207,45 @@ public class Window extends Container implements Accessible {
}
void doDispose() {
final class DisposeAction implements Runnable {
public void run() {
final Window window = Window.this;
// dump stats if needed:
AWTAccessor.getWindowAccessor().dumpStats(window, true, null);
disposing = true;
try {
// Check if this window is the fullscreen window for the
// device. Exit the fullscreen mode prior to disposing
// of the window if that's the case.
GraphicsDevice gd = getGraphicsConfiguration().getDevice();
if (gd.getFullScreenWindow() == window) {
gd.setFullScreenWindow(null);
}
Object[] ownedWindowArray;
synchronized(ownedWindowList) {
ownedWindowArray = new Object[ownedWindowList.size()];
ownedWindowList.copyInto(ownedWindowArray);
}
for (int i = 0; i < ownedWindowArray.length; i++) {
Window child = (Window) (((WeakReference)
(ownedWindowArray[i])).get());
if (child != null) {
child.disposeImpl();
}
}
hide();
beforeFirstShow = true;
removeNotify();
synchronized (inputContextLock) {
if (inputContext != null) {
inputContext.dispose();
inputContext = null;
}
}
clearCurrentFocusCycleRootOnHide();
} finally {
disposing = false;
class DisposeAction implements Runnable {
public void run() {
disposing = true;
try {
// Check if this window is the fullscreen window for the
// device. Exit the fullscreen mode prior to disposing
// of the window if that's the case.
GraphicsDevice gd = getGraphicsConfiguration().getDevice();
if (gd.getFullScreenWindow() == Window.this) {
gd.setFullScreenWindow(null);
}
Object[] ownedWindowArray;
synchronized(ownedWindowList) {
ownedWindowArray = new Object[ownedWindowList.size()];
ownedWindowList.copyInto(ownedWindowArray);
}
for (int i = 0; i < ownedWindowArray.length; i++) {
Window child = (Window) (((WeakReference)
(ownedWindowArray[i])).get());
if (child != null) {
child.disposeImpl();
}
}
hide();
beforeFirstShow = true;
removeNotify();
synchronized (inputContextLock) {
if (inputContext != null) {
inputContext.dispose();
inputContext = null;
}
}
clearCurrentFocusCycleRootOnHide();
} finally {
disposing = false;
}
}
}
boolean fireWindowClosedEvent = isDisplayable();
DisposeAction action = new DisposeAction();
if (EventQueue.isDispatchThread()) {
@@ -4331,39 +4318,10 @@ public class Window extends Container implements Accessible {
return new Point2D.Double(wx, wy);
}
private final static String STATS_ALL_SUFFIX = ".all";
private final static String SYSTEM_PROPERTY_COUNTERS;
private final static boolean USE_COUNTERS;
private final static boolean TRACE_ALL_COUNTERS;
private final static boolean TRACE_STD_ERR;
private final static int TRACE_CAPACITY;
private final static boolean TRACE_COUNTERS = true;
private final static boolean DUMP_STATS = true;
// thread dump interval (ms)
static final long DUMP_INTERVAL = 10 * 1000L;
private static PrintStream getTraceStdStream() {
// get live std stream:
return TRACE_STD_ERR ? System.err : System.out;
}
static {
@SuppressWarnings("removal")
String counters = AccessController.doPrivileged(
new GetPropertyAction("awt.window.counters"));
SYSTEM_PROPERTY_COUNTERS = counters;
USE_COUNTERS = (SYSTEM_PROPERTY_COUNTERS != null);
TRACE_ALL_COUNTERS = USE_COUNTERS && (Objects.equals(SYSTEM_PROPERTY_COUNTERS, "")
|| Objects.equals(SYSTEM_PROPERTY_COUNTERS, "stderr")
|| Objects.equals(SYSTEM_PROPERTY_COUNTERS, "stdout"));
TRACE_STD_ERR = USE_COUNTERS && SYSTEM_PROPERTY_COUNTERS.contains("stderr");
TRACE_CAPACITY = USE_COUNTERS ? 8 : 0;
AWTAccessor.setWindowAccessor(new AWTAccessor.WindowAccessor() {
public void updateWindow(Window window) {
@@ -4412,201 +4370,100 @@ public class Window extends Container implements Accessible {
public boolean countersEnabled(Window w) {
// May want to selectively enable or disable counters per window
return USE_COUNTERS;
return counters != null;
}
private final static long NANO_IN_SEC = java.util.concurrent.TimeUnit.SECONDS.toNanos(1);
public void bumpCounter(Window w, String counterName) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
public void incrementCounter(final Window w, final String counterName) {
if (USE_COUNTERS) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
final long curTimeNanos = System.nanoTime();
// use try-catch to avoid throwing runtime exception to native JNI callers!
try {
PerfCounter newCounter, prevCounter;
synchronized (w.perfCounters) {
newCounter = w.perfCounters.compute(counterName, (k, v) ->
v == null
? new PerfCounter(curTimeNanos, 1L)
: new PerfCounter(curTimeNanos, v.value + 1));
PerfCounter newCounter;
long curTimeNanos = System.nanoTime();
synchronized (w.perfCounters) {
newCounter = w.perfCounters.compute(counterName, (k, v) ->
v == null
? new PerfCounter(curTimeNanos, 1L)
: new PerfCounter(curTimeNanos, v.value + 1));
}
PerfCounter prevCounter;
synchronized (w.perfCountersPrev) {
prevCounter = w.perfCountersPrev.putIfAbsent(counterName, newCounter);
}
if (prevCounter != null) {
long nanosInSecond = java.util.concurrent.TimeUnit.SECONDS.toNanos(1);
long timeDeltaNanos = curTimeNanos - prevCounter.updateTimeNanos;
if (timeDeltaNanos > nanosInSecond) {
long valPerSecond = (long) ((double) (newCounter.value - prevCounter.value)
* nanosInSecond / timeDeltaNanos);
boolean traceAllCounters = Objects.equals(counters, "")
|| Objects.equals(counters, "stdout")
|| Objects.equals(counters, "stderr");
boolean traceEnabled = traceAllCounters || (counters != null && counters.contains(counterName));
if (traceEnabled) {
if (counters.contains("stderr")) {
System.err.println(counterName + " per second: " + valPerSecond);
} else {
System.out.println(counterName + " per second: " + valPerSecond);
}
}
if (perfLog.isLoggable(PlatformLogger.Level.FINE)) {
perfLog.fine(counterName + " per second: " + valPerSecond);
}
synchronized (w.perfCountersPrev) {
prevCounter = w.perfCountersPrev.putIfAbsent(counterName, newCounter);
}
if (prevCounter != null) {
final long timeDeltaNanos = curTimeNanos - prevCounter.updateTimeNanos;
if (timeDeltaNanos > NANO_IN_SEC) {
final double valPerSecond = (double) (newCounter.value - prevCounter.value)
* NANO_IN_SEC / timeDeltaNanos;
synchronized (w.perfCountersPrev) {
w.perfCountersPrev.put(counterName, newCounter);
}
addStat(w, counterName, valPerSecond);
if (TRACE_COUNTERS) {
dumpCounter(counterName, valPerSecond);
}
}
}
} catch (RuntimeException re) {
perfLog.severe("incrementCounter: failed", re);
}
}
}
public void addStat(final Window w, final String statName, final double value) {
if (USE_COUNTERS && Double.isFinite(value)) {
Objects.requireNonNull(w);
Objects.requireNonNull(statName);
// use try-catch to avoid throwing runtime exception to native JNI callers!
try {
synchronized (w.perfStats) {
StatDouble stat = w.perfStats.computeIfAbsent(statName, StatDouble::new);
stat.add(value);
stat = w.perfStats.computeIfAbsent(statName + STATS_ALL_SUFFIX, StatDouble::new);
stat.add(value);
}
} catch (RuntimeException re) {
perfLog.severe("addStat: failed", re);
}
}
}
public long getCounter(final Window w, final String counterName) {
if (USE_COUNTERS) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
synchronized (w.perfCounters) {
PerfCounter counter = w.perfCounters.get(counterName);
return counter != null ? counter.value : -1L;
}
}
return -1L;
}
public double getCounterPerSecond(final Window w, final String counterName) {
if (USE_COUNTERS) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
PerfCounter newCounter, prevCounter;
synchronized (w.perfCounters) {
newCounter = w.perfCounters.get(counterName);
}
synchronized (w.perfCountersPrev) {
prevCounter = w.perfCountersPrev.get(counterName);
}
if (newCounter != null && prevCounter != null) {
final long timeDeltaNanos = newCounter.updateTimeNanos - prevCounter.updateTimeNanos;
// Note that this time delta will usually be above one second.
if (timeDeltaNanos > 0L) {
return (double) (newCounter.value - prevCounter.value) * NANO_IN_SEC / timeDeltaNanos;
}
}
}
return Double.NaN;
}
public void dumpStats(final Window w, final boolean reset, StringBuilder sb) {
if (USE_COUNTERS) {
synchronized (w.perfStats) {
boolean header = false;
for (final StatDouble stat : w.perfStats.values()) {
if (stat.shouldLog()) {
final boolean traceEnabled = TRACE_ALL_COUNTERS || SYSTEM_PROPERTY_COUNTERS.contains(stat.name);
if (!header) {
header = true;
doLog(String.format("* Window['%s'@%s]:",
(w instanceof Frame ? ((Frame) w).getTitle() : ""),
Integer.toHexString(System.identityHashCode(w))),
traceEnabled);
}
// format:
if (sb == null) {
sb = new StringBuilder(128);
}
sb.setLength(0);
sb.append(" - ");
stat.toString(sb);
doLog(sb.toString(), traceEnabled);
if (reset && !stat.name.endsWith(STATS_ALL_SUFFIX)) {
stat.reset();
} else {
stat.updateLastLogCount();
}
}
w.perfCountersPrev.put(counterName, newCounter);
}
}
}
}
private static void dumpCounter(final String counterName, final double valPerSecond) {
if (USE_COUNTERS) {
doLog(String.format("%s per second: %.2f", counterName, valPerSecond),
TRACE_ALL_COUNTERS || SYSTEM_PROPERTY_COUNTERS.contains(counterName));
public long getCounter(Window w, String counterName) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
synchronized (w.perfCounters) {
PerfCounter counter = w.perfCounters.get(counterName);
return counter != null ? counter.value : -1L;
}
}
private static void doLog(final String msg, final boolean traceEnabled) {
if (traceEnabled) {
getTraceStdStream().println(msg);
public long getCounterPerSecond(Window w, String counterName) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
PerfCounter newCounter;
PerfCounter prevCounter;
synchronized (w.perfCounters) {
newCounter = w.perfCounters.get(counterName);
}
if (perfLog.isLoggable(PlatformLogger.Level.FINE)) {
perfLog.fine(msg);
synchronized (w.perfCountersPrev) {
prevCounter = w.perfCountersPrev.get(counterName);
}
if (newCounter != null && prevCounter != null) {
long timeDeltaNanos = newCounter.updateTimeNanos - prevCounter.updateTimeNanos;
// Note that this time delta will usually be above one second.
if (timeDeltaNanos > 0) {
long nanosInSecond = java.util.concurrent.TimeUnit.SECONDS.toNanos(1);
long valPerSecond = (long) ((double) (newCounter.value - prevCounter.value)
* nanosInSecond / timeDeltaNanos);
return valPerSecond;
}
}
return -1;
}
}); // WindowAccessor
if (USE_COUNTERS) {
final Runnable dumper = new Runnable() {
private final static StringBuilder sb = new StringBuilder(128);
@Override
public void run() {
getTraceStdStream().printf("--- WindowStats dump at: %s ---\n", new java.util.Date());
final AWTAccessor.WindowAccessor windowAccessor = AWTAccessor.getWindowAccessor();
for (Window window : Window.getWindows()) {
// dump stats if needed:
windowAccessor.dumpStats(window, true, sb);
}
getTraceStdStream().println("-----");
}
};
final Thread hook = InnocuousThread.newSystemThread("WindowStatsHook", dumper);
hook.setDaemon(true);
hook.setContextClassLoader(null);
Runtime.getRuntime().addShutdownHook(hook);
if (DUMP_STATS) {
final Timer statTimer = new Timer("WindowStats");
statTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
dumper.run();
}
}, DUMP_INTERVAL, DUMP_INTERVAL);
}
}
} // static
// a window doesn't need to be updated in the Z-order.
@Override
void updateZOrder() {}
private record PerfCounter(long updateTimeNanos, long value) {}
private record PerfCounter(Long updateTimeNanos, Long value) {}
private transient final HashMap<String, PerfCounter> perfCounters = (USE_COUNTERS) ? new HashMap<>(TRACE_CAPACITY) : null;
private transient final HashMap<String, PerfCounter> perfCountersPrev = (USE_COUNTERS) ? new HashMap<>(TRACE_CAPACITY) : null;
private transient final LinkedHashMap<String, StatDouble> perfStats = (USE_COUNTERS) ? new LinkedHashMap<>(TRACE_CAPACITY) : null;
private transient final Map<String, PerfCounter> perfCounters = new HashMap<>(4);
private transient final Map<String, PerfCounter> perfCountersPrev = new HashMap<>(4);
} // class Window

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2021, 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
@@ -27,6 +27,8 @@ package javax.swing;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.VolatileImage;
import java.awt.peer.WindowPeer;
import java.security.AccessControlContext;
@@ -47,6 +49,7 @@ import sun.security.action.GetPropertyAction;
import com.sun.java.swing.SwingUtilities3;
import java.awt.geom.AffineTransform;
import java.util.stream.Collectors;
import sun.java2d.SunGraphics2D;
import sun.java2d.pipe.Region;
@@ -797,7 +800,7 @@ public class RepaintManager
.filter(Objects::nonNull)
.distinct()
.forEach(w -> AWTAccessor.getWindowAccessor()
.incrementCounter(w, "swing.RepaintManager.updateWindows"));
.bumpCounter(w, "swing.RepaintManager.updateWindows"));
if (Toolkit.getDefaultToolkit() instanceof SunToolkit sunToolkit &&
sunToolkit.needUpdateWindow())

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 2022, 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
@@ -348,15 +348,10 @@ public final class AWTAccessor {
*/
Window[] getOwnedWindows(Window w);
/* JBR Window counters API */
boolean countersEnabled(Window w);
void incrementCounter(Window w, String counterName);
void addStat(Window w, String statName, double value);
void bumpCounter(Window w, String counterName);
long getCounter(Window w, String counterName);
double getCounterPerSecond(Window w, String counterName);
void dumpStats(Window w, boolean reset, StringBuilder sb);
long getCounterPerSecond(Window w, String counterName);
}
/**

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2023, 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
@@ -30,8 +30,6 @@ import java.security.PrivilegedAction;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentLinkedQueue;
import jdk.internal.misc.InnocuousThread;
import jdk.internal.ref.CleanerFactory;
import sun.java2d.marlin.ArrayCacheConst.CacheStats;
import static sun.java2d.marlin.MarlinUtils.logInfo;
@@ -390,8 +388,16 @@ public final class RendererStats implements MarlinConst {
private RendererStatsHolder() {
AccessController.doPrivileged(
(PrivilegedAction<Void>) () -> {
final Thread hook = InnocuousThread.newSystemThread("MarlinStatsHook", () -> dump());
hook.setDaemon(true);
final Thread hook = new Thread(
MarlinUtils.getRootThreadGroup(),
new Runnable() {
@Override
public void run() {
dump();
}
},
"MarlinStatsHook"
);
hook.setContextClassLoader(null);
Runtime.getRuntime().addShutdownHook(hook);

View File

@@ -1,128 +0,0 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright 2025 JetBrains s.r.o.
* 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 sun.java2d.marlin.stats;
import static sun.java2d.marlin.stats.StatLong.trimTo3Digits;
/**
* Statistics on double values
*/
public final class StatDouble {
// rolling mean weight (lerp):
private final static double EMA_ALPHA = 0.25;
private final static double EMA_ONE_MINUS_ALPHA = 1.0 - EMA_ALPHA;
public final String name;
private long count, lastLogCount;
private double min, max, mean, ema_mean = 0.0, squaredError;
public StatDouble(final String name) {
this.name = name;
reset();
}
public void reset() {
count = 0L;
lastLogCount = 0L;
min = Double.POSITIVE_INFINITY;
max = Double.NEGATIVE_INFINITY;
mean = 0.0;
// skip ema_mean = 0.0;
squaredError = 0.0;
}
public void add(final double val) {
count++;
if (val < min) {
min = val;
}
if (val > max) {
max = val;
}
// Exponential smoothing (EMA):
ema_mean = EMA_ALPHA * val + EMA_ONE_MINUS_ALPHA * ema_mean;
// Welford's algorithm:
final double oldMean = mean;
mean += (val - mean) / count;
squaredError += (val - mean) * (val - oldMean);
}
public boolean shouldLog() {
return (count > lastLogCount);
}
public void updateLastLogCount() {
this.lastLogCount = this.count;
}
public long count() {
return count;
}
public double min() {
return (count != 0L) ? min : Double.NaN;
}
public double max() {
return (count != 0L) ? max : Double.NaN;
}
public double mean() {
return (count != 0L) ? mean : Double.NaN;
}
public double ema() {
return (count != 0L) ? ema_mean : Double.NaN;
}
public double variance() {
return (count != 0L) ? (squaredError / (count - 1L)) : Double.NaN;
}
public double stddev() {
return (count != 0L) ? Math.sqrt(variance()) : Double.NaN;
}
public double total() {
return (count != 0L) ? (mean() * count) : Double.NaN;
}
@Override
public String toString() {
return toString(new StringBuilder(128)).toString();
}
public StringBuilder toString(final StringBuilder sb) {
sb.append(name).append('[').append(count);
sb.append("] sum: ").append(trimTo3Digits(total()));
sb.append(" avg: ").append(trimTo3Digits(mean()));
sb.append(" stddev: ").append(trimTo3Digits(stddev()));
sb.append(" ema: ").append(trimTo3Digits(ema()));
sb.append(" [").append(trimTo3Digits(min())).append(" - ").append(trimTo3Digits(max())).append("]");
return sb;
}
}

View File

@@ -31,18 +31,20 @@ package sun.java2d.marlin.stats;
public class StatLong {
public final String name;
public long count, sum, min, max;
public long count = 0L;
public long sum = 0L;
public long min = Integer.MAX_VALUE;
public long max = Integer.MIN_VALUE;
public StatLong(final String name) {
this.name = name;
reset();
}
public void reset() {
count = 0L;
sum = 0L;
min = Long.MAX_VALUE;
max = Long.MIN_VALUE;
min = Integer.MAX_VALUE;
max = Integer.MIN_VALUE;
}
public void add(final int val) {
@@ -76,7 +78,7 @@ public class StatLong {
sb.append(name).append('[').append(count);
sb.append("] sum: ").append(sum).append(" avg: ");
sb.append(trimTo3Digits(((double) sum) / count));
sb.append(" [").append(min).append(" - ").append(max).append("]");
sb.append(" [").append(min).append(" | ").append(max).append("]");
return sb;
}
@@ -87,7 +89,7 @@ public class StatLong {
* @return double value with only 3 decimal digits
*/
public static double trimTo3Digits(final double value) {
return Double.isFinite(value) ? ((long) (1e3d * value)) / 1e3d : Double.NaN;
return ((long) (1e3d * value)) / 1e3d;
}
}

View File

@@ -199,7 +199,7 @@ public class WLSMSurfaceData extends SurfaceData implements WLSurfaceDataExt, WL
private void countNewFrame() {
// Called from the native code when this surface data has been sent to the Wayland server
if (target instanceof Window window) {
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.frames");
AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.frames");
}
}
@@ -208,7 +208,7 @@ public class WLSMSurfaceData extends SurfaceData implements WLSurfaceDataExt, WL
// the Wayland server, but that attempt was not successful. This can happen, for example,
// when those attempts are too frequent.
if (target instanceof Window window) {
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.framesDropped");
AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.framesDropped");
}
}

View File

@@ -1,173 +0,0 @@
/*
* Copyright 2024 JetBrains s.r.o.
* 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 sun.awt.windows;
import sun.awt.AWTAccessor;
import sun.java2d.SunGraphicsEnvironment;
import javax.swing.*;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.im.InputMethodRequests;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
/**
* Provides caret tracking support for assistive tools that don't work with Java Access Bridge.
* Specifically, it's targeted for the built-in Windows Magnifier.
* This class listens to caret change events of the currently focused JTextComponent
* and forwards them to the native code, which then sends them as Win32 IAccessible events.
* <p>
* A typical high-level scenario of the interaction with the magnifier:
* <ol>
* <li>Magnifier sends a WM_GETOBJECT window message to get accessible content of the window.</li>
* <li>The message is handled in AwtComponent native class (awt_Component.cpp),
* which calls {@link #startCaretNotifier}.</li>
* <li>We start listening for keyboard focus change events.</li>
* <li>If at some point focus gets to a {@link JTextComponent}, we subscribe to its caret events.</li>
* <li>When the caret changes, we need to move the magnifier viewport to the new caret location.
* To achieve this, we create a Win32 IAccessible object for the caret (see AccessibleCaret.cpp)
* and send an event that its location was changed (EVENT_OBJECT_LOCATIONCHANGE).</li>
* <li>Magnifier receives this event and sends the WM_GETOBJECT message with the OBJID_CARET argument
* to get the caret object and its location property. After that, it moves the viewport to the returned location.
* </li>
* <li>When the {@link JTextComponent} loses focus, we stop listening to caret events
* and release the IAccessible caret object.</li>
* </ol>
* </p>
* <p>
* The feature is enabled by default
* and can be toggled by setting the sun.awt.windows.use.native.caret.accessibility.events property.
* </p>
*/
@SuppressWarnings("unused") // Used from the native side through JNI.
class AccessibleCaretLocationNotifier implements PropertyChangeListener, CaretListener {
private volatile static AccessibleCaretLocationNotifier caretNotifier;
private static final boolean nativeCaretEventsEnabled =
Boolean.parseBoolean(System.getProperty("sun.awt.windows.use.native.caret.accessibility.events", "true"));
private WeakReference<JTextComponent> currentFocusedComponent;
private long currentHwnd;
@SuppressWarnings("unused") // Called from the native through JNI.
public static void startCaretNotifier(long hwnd) {
if (nativeCaretEventsEnabled && caretNotifier == null) {
SwingUtilities.invokeLater(() -> {
if (caretNotifier == null) {
caretNotifier = new AccessibleCaretLocationNotifier(hwnd);
KeyboardFocusManager cfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
cfm.addPropertyChangeListener("focusOwner", caretNotifier);
if (cfm.getFocusOwner() instanceof JTextComponent textComponent) {
caretNotifier.propertyChange(new PropertyChangeEvent(caretNotifier, "focusOwner", null, textComponent));
}
}
});
}
}
public AccessibleCaretLocationNotifier(long hwnd) {
currentHwnd = hwnd;
}
private static native void updateNativeCaretLocation(long hwnd, int x, int y, int width, int height);
private static native void releaseNativeCaret(long hwnd);
@Override
public void propertyChange(PropertyChangeEvent e) {
Window w = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
if (w != null) {
WWindowPeer wp = AWTAccessor.getComponentAccessor().getPeer(w);
if (wp != null) {
long hwnd = wp.getHWnd();
if (currentHwnd != hwnd) {
currentHwnd = hwnd;
}
}
}
Object newFocusedComponent = e.getNewValue();
if (currentFocusedComponent != null) {
JTextComponent currentComponentStrong = currentFocusedComponent.get();
if (currentComponentStrong != null && newFocusedComponent != currentComponentStrong) {
currentComponentStrong.removeCaretListener(this);
currentFocusedComponent.clear();
currentFocusedComponent = null;
releaseNativeCaret(currentHwnd);
}
}
if (newFocusedComponent instanceof JTextComponent textComponent) {
currentFocusedComponent = new WeakReference<>(textComponent);
textComponent.addCaretListener(this);
// Trigger the caret event when the text component receives focus to notify about the initial caret location
caretUpdate(new CaretEvent(textComponent) {
// Dot and mark won't be used, so we can set any values.
@Override
public int getDot() { return 0; }
@Override
public int getMark() { return 0; }
});
}
}
@Override
public void caretUpdate(CaretEvent e) {
if (!(e.getSource() instanceof JTextComponent textComponent)) {
return;
}
SwingUtilities.invokeLater(() -> {
if (!textComponent.isShowing()) return;
InputMethodRequests imr = textComponent.getInputMethodRequests();
if (imr == null) return;
Rectangle caretRectangle = imr.getTextLocation(null);
if (caretRectangle == null) return;
caretRectangle.width = 1;
Container parent = textComponent.getParent();
if (parent != null && parent.isShowing()) {
// Make sure we don't go outside of parent bounds, which can happen in the case of scrollable components.
Rectangle parentBounds = parent.getBounds();
parentBounds.setLocation(parent.getLocationOnScreen());
if (!parentBounds.contains(caretRectangle)) {
caretRectangle = parentBounds.intersection(caretRectangle);
if (caretRectangle.isEmpty()) return;
}
}
caretRectangle = SunGraphicsEnvironment.toDeviceSpaceAbs(caretRectangle);
updateNativeCaretLocation(AccessibleCaretLocationNotifier.this.currentHwnd,
(int) caretRectangle.getX(), (int) caretRectangle.getY(),
(int) caretRectangle.getWidth(), (int) caretRectangle.getHeight());
});
}
}

View File

@@ -824,11 +824,11 @@ public class D3DSurfaceData extends SurfaceData implements AccelSurface {
if (sd.getPeer().getTarget() instanceof Window window) {
switch (D3DRenderQueue.getFramePresentedStatus()) {
case 1:
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.framesPresentRequested");
AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.framesPresentRequested");
break;
case 0:
default:
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.framesPresentFailed");
AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.framesPresentFailed");
}
}
}

View File

@@ -1,272 +0,0 @@
/*
* Copyright (c) 2024, JetBrains s.r.o.. 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.
*
* 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.
*/
#include "AccessibleCaret.h"
/**
* This class implements Win32 IAccessible interface in a similar way to the system text caret.
*/
AccessibleCaret::AccessibleCaret()
: m_refCount(1), m_x(0), m_y(0), m_width(0), m_height(0) {
InitializeCriticalSection(&m_caretLocationLock);
}
AccessibleCaret* AccessibleCaret::createInstance() {
return new AccessibleCaret();
}
AccessibleCaret::~AccessibleCaret() {
DeleteCriticalSection(&m_caretLocationLock);
}
std::atomic<AccessibleCaret *> AccessibleCaret::instance{nullptr};
// IUnknown methods
IFACEMETHODIMP_(ULONG) AccessibleCaret::AddRef() {
return InterlockedIncrement(&m_refCount);
}
IFACEMETHODIMP_(ULONG) AccessibleCaret::Release() {
ULONG count = InterlockedDecrement(&m_refCount);
if (count == 0) {
delete this;
}
return count;
}
IFACEMETHODIMP AccessibleCaret::QueryInterface(REFIID riid, void **ppInterface) {
if (ppInterface == nullptr) {
return E_POINTER;
}
if (riid == IID_IUnknown || riid == IID_IDispatch || riid == IID_IAccessible) {
*ppInterface = static_cast<IAccessible *>(this);
AddRef();
return S_OK;
}
*ppInterface = nullptr;
return E_NOINTERFACE;
}
// IDispatch methods
IFACEMETHODIMP AccessibleCaret::GetTypeInfoCount(UINT *pctinfo) {
return E_NOTIMPL;
}
IFACEMETHODIMP AccessibleCaret::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo) {
return E_NOTIMPL;
}
IFACEMETHODIMP AccessibleCaret::GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames, LCID lcid,
DISPID *rgdispid) {
return E_NOTIMPL;
}
IFACEMETHODIMP AccessibleCaret::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
DISPPARAMS *pdispparams, VARIANT *pvarResult, EXCEPINFO *pexcepinfo,
UINT *puArgErr) {
return E_NOTIMPL;
}
// IAccessible methods
IFACEMETHODIMP AccessibleCaret::get_accParent(IDispatch **ppdispParent) {
if (ppdispParent == nullptr) {
return E_POINTER;
}
*ppdispParent = nullptr;
return S_FALSE;
}
IFACEMETHODIMP AccessibleCaret::get_accChildCount(long *pcountChildren) {
if (pcountChildren == nullptr) {
return E_POINTER;
}
*pcountChildren = 0;
return S_OK;
}
IFACEMETHODIMP AccessibleCaret::get_accChild(VARIANT varChild, IDispatch **ppdispChild) {
if (ppdispChild == nullptr) {
return E_POINTER;
}
*ppdispChild = nullptr;
return S_FALSE;
}
IFACEMETHODIMP AccessibleCaret::get_accName(VARIANT varChild, BSTR *pszName) {
if (pszName == nullptr) {
return E_POINTER;
}
*pszName = SysAllocString(L"Edit"); // Same name as the system caret.
return S_OK;
}
IFACEMETHODIMP AccessibleCaret::get_accValue(VARIANT varChild, BSTR *pszValue) {
return DISP_E_MEMBERNOTFOUND;
}
IFACEMETHODIMP AccessibleCaret::get_accDescription(VARIANT varChild, BSTR *pszDescription) {
return S_FALSE;
}
IFACEMETHODIMP AccessibleCaret::get_accRole(VARIANT varChild, VARIANT *pvarRole) {
if (pvarRole == nullptr) {
return E_POINTER;
}
pvarRole->vt = VT_I4;
pvarRole->lVal = ROLE_SYSTEM_CARET;
return S_OK;
}
IFACEMETHODIMP AccessibleCaret::get_accState(VARIANT varChild, VARIANT *pvarState) {
if (pvarState == nullptr) {
return E_POINTER;
}
pvarState->vt = VT_I4;
pvarState->lVal = 0; // The state without any flags, corresponds to "normal".
return S_OK;
}
IFACEMETHODIMP AccessibleCaret::get_accHelp(VARIANT varChild, BSTR *pszHelp) {
return S_FALSE;
}
IFACEMETHODIMP AccessibleCaret::get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild, long *pidTopic) {
return S_FALSE;
}
IFACEMETHODIMP AccessibleCaret::get_accKeyboardShortcut(VARIANT varChild, BSTR *pszKeyboardShortcut) {
return S_FALSE;
}
IFACEMETHODIMP AccessibleCaret::get_accFocus(VARIANT *pvarChild) {
if (pvarChild == nullptr) {
return E_POINTER;
}
pvarChild->vt = VT_EMPTY;
return S_OK;
}
IFACEMETHODIMP AccessibleCaret::get_accSelection(VARIANT *pvarChildren) {
return DISP_E_MEMBERNOTFOUND;
}
IFACEMETHODIMP AccessibleCaret::get_accDefaultAction(VARIANT varChild, BSTR *pszDefaultAction) {
return S_FALSE;
}
IFACEMETHODIMP AccessibleCaret::accSelect(long flagsSelect, VARIANT varChild) {
return DISP_E_MEMBERNOTFOUND;
}
IFACEMETHODIMP AccessibleCaret::accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight,
VARIANT varChild) {
if (pxLeft == nullptr || pyTop == nullptr || pcxWidth == nullptr || pcyHeight == nullptr) {
return E_POINTER;
}
EnterCriticalSection(&m_caretLocationLock);
*pxLeft = m_x;
*pyTop = m_y;
*pcxWidth = m_width;
*pcyHeight = m_height;
LeaveCriticalSection(&m_caretLocationLock);
return S_OK;
}
IFACEMETHODIMP AccessibleCaret::accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEndUpAt) {
return DISP_E_MEMBERNOTFOUND;
}
IFACEMETHODIMP AccessibleCaret::accHitTest(long xLeft, long yTop, VARIANT *pvarChild) {
return DISP_E_MEMBERNOTFOUND;
}
IFACEMETHODIMP AccessibleCaret::accDoDefaultAction(VARIANT varChild) {
return DISP_E_MEMBERNOTFOUND;
}
IFACEMETHODIMP AccessibleCaret::put_accName(VARIANT varChild, BSTR szName) {
return E_NOTIMPL;
}
IFACEMETHODIMP AccessibleCaret::put_accValue(VARIANT varChild, BSTR szValue) {
return DISP_E_MEMBERNOTFOUND;
}
void AccessibleCaret::setLocation(long x, long y, long width, long height) {
EnterCriticalSection(&m_caretLocationLock);
m_x = x;
m_y = y;
m_width = width;
m_height = height;
LeaveCriticalSection(&m_caretLocationLock);
}
extern "C" {
/*
* Class: sun_awt_windows_AccessibleCaretLocationNotifier
* Method: updateNativeCaretLocation
* Signature: (JIIII)V
*/
JNIEXPORT void JNICALL Java_sun_awt_windows_AccessibleCaretLocationNotifier_updateNativeCaretLocation(
JNIEnv *env, jclass jClass,
jlong jHwnd, jint x, jint y, jint width, jint height) {
HWND hwnd = reinterpret_cast<HWND>(jHwnd);
AccessibleCaret *caret = AccessibleCaret::instance.load(std::memory_order_acquire);
if (caret == nullptr) {
caret = AccessibleCaret::createInstance();
AccessibleCaret::instance.store(caret, std::memory_order_release);
// Notify with Object ID "OBJID_CARET".
// After that, an assistive tool will send a WM_GETOBJECT message with this ID,
// and we can return the caret instance.
NotifyWinEvent(EVENT_OBJECT_CREATE, hwnd, OBJID_CARET, CHILDID_SELF);
NotifyWinEvent(EVENT_OBJECT_SHOW, hwnd, OBJID_CARET, CHILDID_SELF);
}
caret->setLocation(x, y, width, height);
NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, hwnd, OBJID_CARET, CHILDID_SELF);
}
/*
* Class: sun_awt_windows_AccessibleCaretLocationNotifier
* Method: releaseNativeCaret
* Signature: (J)V
*/
JNIEXPORT void JNICALL Java_sun_awt_windows_AccessibleCaretLocationNotifier_releaseNativeCaret(
JNIEnv *env, jclass jClass, jlong jHwnd) {
AccessibleCaret *caret = AccessibleCaret::instance.exchange(nullptr, std::memory_order_acq_rel);
if (caret != nullptr) {
caret->Release();
HWND hwnd = reinterpret_cast<HWND>(jHwnd);
NotifyWinEvent(EVENT_OBJECT_HIDE, hwnd, OBJID_CARET, CHILDID_SELF);
NotifyWinEvent(EVENT_OBJECT_DESTROY, hwnd, OBJID_CARET, CHILDID_SELF);
}
}
} /* extern "C" */

View File

@@ -1,83 +0,0 @@
/*
* Copyright (c) 2024, JetBrains s.r.o.. 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.
*
* 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.
*/
#ifndef ACCESSIBLECARET_H
#define ACCESSIBLECARET_H
#include "jni.h"
#include <oleacc.h>
#include <atomic>
class AccessibleCaret : public IAccessible {
public:
static AccessibleCaret *createInstance();
static std::atomic<AccessibleCaret *> instance;
// IUnknown methods.
IFACEMETHODIMP_(ULONG) AddRef();
IFACEMETHODIMP_(ULONG) Release();
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppInterface);
// IDispatch methods.
IFACEMETHODIMP GetTypeInfoCount(UINT *pctinfo);
IFACEMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo);
IFACEMETHODIMP GetIDsOfNames(REFIID riid, __in_ecount(cNames)
OLECHAR **rgszNames, UINT cNames, LCID lcid, DISPID *rgdispid);
IFACEMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid,
WORD wFlags, DISPPARAMS *pdispparams, VARIANT *pvarResult,
EXCEPINFO *pexcepinfo, UINT *puArgErr);
// IAccessible methods
IFACEMETHODIMP get_accParent(IDispatch **ppdispParent);
IFACEMETHODIMP get_accChildCount(long *pcountChildren);
IFACEMETHODIMP get_accChild(VARIANT varChild, IDispatch **ppdispChild);
IFACEMETHODIMP get_accName(VARIANT varChild, BSTR *pszName);
IFACEMETHODIMP get_accValue(VARIANT varChild, BSTR *pszValue);
IFACEMETHODIMP get_accDescription(VARIANT varChild, BSTR *pszDescription);
IFACEMETHODIMP get_accRole(VARIANT varChild, VARIANT *pvarRole);
IFACEMETHODIMP get_accState(VARIANT varChild, VARIANT *pvarState);
IFACEMETHODIMP get_accHelp(VARIANT varChild, BSTR *pszHelp);
IFACEMETHODIMP get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild, long *pidTopic);
IFACEMETHODIMP get_accKeyboardShortcut(VARIANT varChild, BSTR *pszKeyboardShortcut);
IFACEMETHODIMP get_accFocus(VARIANT *pvarChild);
IFACEMETHODIMP get_accSelection(VARIANT *pvarChildren);
IFACEMETHODIMP get_accDefaultAction(VARIANT varChild, BSTR *pszDefaultAction);
IFACEMETHODIMP accSelect(long flagsSelect, VARIANT varChild);
IFACEMETHODIMP accLocation(long *pxLeft, long *pyTop, long *pcxWidth, long *pcyHeight, VARIANT varChild);
IFACEMETHODIMP accNavigate(long navDir, VARIANT varStart, VARIANT *pvarEndUpAt);
IFACEMETHODIMP accHitTest(long xLeft, long yTop, VARIANT *pvarChild);
IFACEMETHODIMP accDoDefaultAction(VARIANT varChild);
IFACEMETHODIMP put_accName(VARIANT varChild, BSTR szName);
IFACEMETHODIMP put_accValue(VARIANT varChild, BSTR szValue);
void setLocation(long x, long y, long width, long height);
private:
AccessibleCaret();
~AccessibleCaret();
ULONG m_refCount;
int m_x, m_y, m_width, m_height;
CRITICAL_SECTION m_caretLocationLock;
};
#endif //ACCESSIBLECARET_H

View File

@@ -49,7 +49,6 @@
#include "Hashtable.h"
#include "ComCtl32Util.h"
#include "math.h"
#include "AccessibleCaret.h"
#include <Region.h>
@@ -2072,32 +2071,6 @@ LRESULT AwtComponent::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
if (::IsWindow(AwtWindow::GetModalBlocker(GetHWnd()))) {
mr = mrConsume;
}
break;
}
case WM_GETOBJECT:
{
// We've got a WM_GETOBJECT message which was likely sent by an assistive tool.
// Therefore, we can start generating native caret accessibility events.
DWORD objId = static_cast<DWORD>(static_cast<DWORD_PTR>(lParam));
if (objId == OBJID_CLIENT) {
JNIEnv *env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);
if (env != nullptr) {
jclass cls = env->FindClass("sun/awt/windows/AccessibleCaretLocationNotifier");
if (cls != nullptr) {
jmethodID mid = env->GetStaticMethodID(cls, "startCaretNotifier", "(J)V");
if (mid != nullptr) {
env->CallStaticVoidMethod(cls, mid, reinterpret_cast<jlong>(GetHWnd()));
}
}
}
} else if (objId == OBJID_CARET) {
AccessibleCaret *caret = AccessibleCaret::instance.load(std::memory_order_acquire);
if (caret != nullptr) {
retValue = LresultFromObject(IID_IAccessible, wParam, caret);
mr = mrConsume;
}
}
break;
}
}

View File

@@ -30,14 +30,18 @@ import java.io.InputStreamReader;
/**
* @test
* @summary VerifyDependencies checks readability verifies that a Linux shared
* library has no dependency on symbols from glibc version higher than 2.28
* library has no dependency on symbols from glibc version higher than <code>expectedVersion</code>
* @run main VerifyDependencies
* @requires (os.family == "linux")
*/
public class VerifyDependencies {
static final public String EXPECTED_VERSION = "2.28";
static final public String EXPECTED_VERSION_LEGACY = "2.26";
static final public String EXPECTED_VERSION_VULKAN = "2.28";
static String expectedVersion;
public static void verifyLibrary(String libraryPath) throws IOException {
Process process;
BufferedReader reader;
@@ -52,12 +56,12 @@ public class VerifyDependencies {
System.out.println(line);
if (line.contains("GLIBC_")) {
String version = extractVersion(line);
if (compareVersions(version, EXPECTED_VERSION) > 0) {
if (compareVersions(version, expectedVersion) > 0) {
throw new RuntimeException(libraryPath + " has a dependency on glibc version " + version);
}
}
}
System.out.println(libraryPath + " has no dependency on glibc version higher than " + EXPECTED_VERSION);
System.out.println(libraryPath + " has no dependency on glibc version higher than " + expectedVersion);
}
private static String extractVersion(String line) {
@@ -125,6 +129,12 @@ public class VerifyDependencies {
public static void main(String[] args) throws IOException {
String javaHome = System.getProperty("java.home");
String vendorVersion = System.getProperty("java.vendor.version");
expectedVersion = vendorVersion.substring(Math.max(vendorVersion.length() - 3, 0)).compareTo("-lb") == 0
? EXPECTED_VERSION_LEGACY : EXPECTED_VERSION_VULKAN;
System.out.println("supporting glibc version is not less than " + expectedVersion);
findInDirectory(javaHome + "/bin", false);
findInDirectory(javaHome + "/lib", true);
}

View File

@@ -116,7 +116,7 @@
java/awt/AlphaComposite/WindowAlphaCompositeTest.java JBR-6553 macosx-all
java/awt/Button/DisabledButtonPress.java JBR-5799 windows-aarch64
java/awt/Desktop/8064934/bug8064934.java JBR-5764,JBR-5799 windows-all
java/awt/Debug/DumpOnKey/DumpOnKey.java JBR-5225 windows-all
java/awt/Debug/DumpOnKey/DumpOnKey.java JBR-5225,JBR-9350 windows-all,macosx-all
java/awt/event/HierarchyEvent/SpecTest.java JBR-7589 windows-all
java/awt/event/KeyEvent/CorrectTime/CorrectTime.java JBR-6665 linux-all,windows-all
java/awt/event/KeyEvent/SwallowKeyEvents/SwallowKeyEvents.java 8224055,JBR-5906 macosx-all,linux-all
@@ -764,7 +764,7 @@ java/awt/MenuBar/TestNoScreenMenuBar.java 8265987 macosx-all
java/awt/Dialog/DialogAboveFrame/DialogAboveFrameTest.java JBR-5210 windows-all
java/awt/Dialog/MakeWindowAlwaysOnTop/MakeWindowAlwaysOnTop.java 8266243,JBR-6632 macosx-all,linux-all
java/awt/Dialog/ModalDialogPermission/ModalDialogPermission.java JBR-5225 windows-all
java/awt/Dialog/ModalDialogPermission/ModalDialogPermission.java JBR-5225,JBR-9350 windows-all,macosx-all
java/awt/Dialog/SiblingChildOrder/SiblingChildOrderTest.java JBR-5082 linux-all
java/awt/Window/GetScreenLocation/GetScreenLocationTest.java 8225787,8253184 linux-all,windows-all
java/awt/dnd/DragSourceMotionListenerTest.java 8225131 windows-all
@@ -1078,7 +1078,7 @@ javax/swing/JPopupMenu/6495920/bug6495920.java JBR-6928 linux-all
javax/swing/JPopupMenu/6515446/bug6515446.java 8197552,JBR-6531 windows-all,linux-all
javax/swing/JPopupMenu/6544309/bug6544309.java JBR-6532 windows-all,linux-all
javax/swing/JPopupMenu/6675802/bug6675802.java JBR-5767 windows-all
javax/swing/JPopupMenu/6691503/bug6691503.java JBR-8028 linux-all
javax/swing/JPopupMenu/6691503/bug6691503.java JBR-8028,JBR-9350 linux-all,macosx-all
javax/swing/JPopupMenu/6800513/bug6800513.java 7184956,JBR-6533 macosx-all,linux-all,windows-all
javax/swing/JPopupMenu/6827786/bug6827786.java JBR-6657 linux-all,windows-x64
javax/swing/JPopupMenu/6987844/bug6987844.java JBR-6718 linux-all,windows-all