Compare commits

...

18 Commits

Author SHA1 Message Date
bourgesl
e66f996829 JBR-9283 Enhance Window counters to provide statistics
Enhanced Window counters to provide statistics (using the new marlin StatDouble class), enhanced logging code to dump regularly (10s) window stats, added shutdown hook, bumpCounter() renamed to incrementCounter(), added addStat(window, name, value) used by MTLLayer to report blitTexture & nextDrawable timings (ms), use InnocuousThread for shutdown hooks, fixed D3DSurfaceData bumpCounter() usages to incrementCounter()

(cherry picked from commit af437d9d61)
2025-09-12 15:18:23 +02:00
Vitaly Provodin
8569475930 update exclude list on results of 25.107.28 test runs 2025-09-12 13:33:33 +04:00
Nikita Gubarkov
7433067506 JBR-9111 Vulkan: Lock RQ while disposing the surface 2025-09-11 15:25:26 +02:00
Nikita Gubarkov
2e517af41f JBR-9236 Vulkan: Proper builds without Vulkan 2025-09-11 11:03:12 +02:00
Nikita Tsarev
a54096f118 JBR-9336: Fix build error with old wayland protocol headers [WLToolkit] 2025-09-11 08:55:16 +02:00
Maxim Kartashev
d3f0367a19 JBR-9310 Wayland: Gtk-WARNING in swing app 2025-09-10 10:23:53 +04:00
Nikita Tsarev
14f93c154e JBR-9326 Support TransferHandler.setDragImage [WLToolkit] 2025-09-09 11:08:31 +02:00
Maxim Kartashev
07a1aee823 JBR-9302 Wayland: default window decoration to look more like KDE 2025-09-08 15:44:48 +04:00
Maxim Kartashev
3345eb8e16 JBR-9288 Wayland: use builtin window decorations in KDE 2025-09-08 15:44:41 +04:00
Dmitry Drobotov
987f5f9561 JBR-4478 Implement support for native accessible caret events on Windows
The feature adds caret tracking support for assistive tools that don't work with Java Access Bridge, specifically, for the built-in Windows Magnifier.
It works by implementing Win32 IAccessible interface for the text caret, and sending EVENT_OBJECT_LOCATIONCHANGE events whenever it changes.
It's enabled by default and can be disabled by setting `sun.awt.windows.use.native.caret.accessibility.events` property to false.

(cherry picked from commit 88f1599bad)
2025-09-08 06:54:45 +02:00
Maxim Kartashev
bfa8e73dcf JBR-9016 Make screenshot JBR API work on Windows 2025-09-05 17:33:08 +04:00
Maxim Kartashev
5493f14d30 JBR-9016 Add API for making screenshots of some regions of the application without interacting with OS 2025-09-04 13:23:01 +04:00
Maxim Kartashev
09ecf47329 JBR-9228 KDE: jb/java/awt/Toolkit/DetectingOSThemeTest.java fails 2025-09-04 12:01:59 +04:00
Maxim Kartashev
b3becb25a2 JBR-9289 Wayland: re-enable window shadow by default 2025-09-03 13:19:50 +04:00
Maxim Kartashev
8bad559f91 JBR-9289 Wayland: an option to turn window shadow off
Use -Dsun.awt.wl.Shadow=false to turn all the window shadows off
2025-09-03 12:49:12 +04:00
Nikita Gubarkov
43cbf9a7db JBR-7334 Skip custom title bar reconfiguration if nothing changed 2025-09-01 15:17:52 +02:00
Vitaly Provodin
3c70bd7f50 fixup! JBR-9238 Introduce distinct test groups for Vulkan runs 2025-08-29 15:47:14 +04:00
Vitaly Provodin
dca326f640 JBR-9274 turn off streaming output for attach API by default 2025-08-29 15:47:14 +04:00
50 changed files with 1693 additions and 366 deletions

View File

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

View File

@@ -172,8 +172,8 @@ volatile AttachListenerState AttachListener::_state = AL_NOT_INITIALIZED;
AttachAPIVersion AttachListener::_supported_version = ATTACH_API_V1;
// Default is true (if jdk.attach.vm.streaming property is not set).
bool AttachListener::_default_streaming_output = true;
// Default is false (if jdk.attach.vm.streaming property is not set).
bool AttachListener::_default_streaming_output = false;
static bool get_bool_sys_prop(const char* name, bool default_value, TRAPS) {
ResourceMark rm(THREAD);

View File

@@ -33,7 +33,6 @@ 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;
@@ -150,11 +149,25 @@ 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().bumpCounter(window, "java2d.native.frames");
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.frames");
}
}
@@ -164,7 +177,7 @@ public class MTLLayer extends CFLayer {
// when those attempts are too frequent.
Component target = peer.getTarget();
if (target instanceof Window window) {
AWTAccessor.getWindowAccessor().bumpCounter(window, "java2d.native.framesDropped");
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.framesDropped");
}
}
}

View File

@@ -71,6 +71,8 @@
- (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,29 +226,32 @@ BOOL MTLLayer_isExtraRedrawEnabled() {
}
// Acquire CAMetalDrawable without blocking:
const CFTimeInterval beforeDrawableTime = (TRACE_DISPLAY) ? CACurrentMediaTime() : 0.0;
const CFTimeInterval beforeDrawableTime = CACurrentMediaTime();
const id<CAMetalDrawable> mtlDrawable = [self nextDrawable];
if (mtlDrawable == nil) {
J2dTraceLn(J2D_TRACE_VERBOSE, "MTLLayer.blitTexture: nextDrawable is null");
return;
}
const CFTimeInterval nextDrawableTime = (TRACE_DISPLAY) ? CACurrentMediaTime() : 0.0;
const CFTimeInterval nextDrawableTime = CACurrentMediaTime();
const CFTimeInterval nextDrawableLatency = (nextDrawableTime - beforeDrawableTime);
// rolling mean weight (lerp):
static const NSTimeInterval a = 0.25;
#if TRACE_DISPLAY_ON
const CFTimeInterval nextDrawableLatency = (nextDrawableTime - beforeDrawableTime);
if (nextDrawableLatency > 0.0) {
[self addStatCallback:1 value:1000.0 * nextDrawableLatency]; // See MTLLayer.STAT_NAMES[1]
#if TRACE_DISPLAY_ON
self.avgNextDrawableTime = nextDrawableLatency * a + self.avgNextDrawableTime * (1.0 - a);
}
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"[%.6lf] MTLLayer_blitTexture: drawable(%d) presented"
" - nextDrawableLatency = %.3lf ms - average = %.3lf ms",
CACurrentMediaTime(), mtlDrawable.drawableID,
1000.0 * nextDrawableLatency, 1000.0 * self.avgNextDrawableTime
);
J2dRlsTraceLn(J2D_TRACE_VERBOSE,
"[%.6lf] MTLLayer_blitTexture: drawable(%d) presented"
" - nextDrawableLatency = %.3lf ms - average = %.3lf ms",
CACurrentMediaTime(), mtlDrawable.drawableID,
1000.0 * nextDrawableLatency, 1000.0 * self.avgNextDrawableTime
);
#endif
}
// Keep Fence from now:
releaseFence = NO;
@@ -389,8 +392,15 @@ 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);
}
@@ -516,6 +526,20 @@ 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

@@ -53,10 +53,14 @@ import java.awt.event.TextEvent;
import java.awt.im.InputContext;
import java.awt.im.InputMethodRequests;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.VolatileImage;
import java.awt.image.WritableRaster;
import java.awt.peer.ComponentPeer;
import java.awt.peer.ContainerPeer;
import java.awt.peer.LightweightPeer;
@@ -89,6 +93,7 @@ import javax.accessibility.AccessibleStateSet;
import javax.swing.JComponent;
import javax.swing.JRootPane;
import com.jetbrains.exported.JBRApi;
import sun.awt.AWTAccessor;
import sun.awt.AppContext;
import sun.awt.ComponentFactory;
@@ -99,6 +104,7 @@ import sun.awt.EmbeddedFrame;
import sun.awt.RequestFocusController;
import sun.awt.SubRegionShowable;
import sun.awt.SunToolkit;
import sun.awt.SurfacePixelGrabber;
import sun.awt.dnd.SunDropTargetEvent;
import sun.awt.im.CompositionArea;
import sun.awt.image.VSyncedBSManager;
@@ -10559,4 +10565,79 @@ public abstract class Component implements ImageObserver, MenuContainer,
}
return p.updateCustomTitleBarHitTest(allowNativeActions);
}
@JBRApi.Provides("Screenshoter#getWindowBackbufferArea")
private static BufferedImage getWindowBackbufferArea(Window window, int x, int y, int width, int height) {
Objects.requireNonNull(window);
if (x < 0 || y < 0) {
throw new IllegalArgumentException("Negative coordinates are not allowed");
}
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("The size must be positive");
}
Image fullBackbuffer = window.getBackBuffer();
if (fullBackbuffer == null) {
return null;
}
var bufferWidth = fullBackbuffer.getWidth(null);
var bufferHeight = fullBackbuffer.getHeight(null);
if (x >= width) {
throw new IllegalArgumentException(String.format("x coordinate (%d) is out of bounds (%d)", x, bufferWidth));
}
if (y >= height) {
throw new IllegalArgumentException(String.format("y coordinate (%d) is out of bounds (%d)", y, bufferHeight));
}
if ((long) x + width > bufferWidth) {
width = bufferWidth - x;
}
if ((long) y + height > bufferHeight) {
height = bufferHeight - y;
}
if (fullBackbuffer instanceof BufferedImage bufferedImage) {
return bufferedImage.getSubimage(x, y, width, height);
} else {
ColorModel colorModel = window.getGraphicsConfiguration().getColorModel();
SampleModel sampleModel = colorModel.createCompatibleSampleModel(width, height);
WritableRaster raster = Raster.createWritableRaster(sampleModel, null);
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
image.getGraphics().drawImage(fullBackbuffer,
0, 0, width, height,
x, y, x + width, y + height,
null);
return image;
}
}
@JBRApi.Provides("Screenshoter#getWindowSurfaceArea")
private static BufferedImage getWindowSurfaceArea(Window window, int x, int y, int width, int height) {
Objects.requireNonNull(window);
if (x < 0 || y < 0) {
throw new IllegalArgumentException("Negative coordinates are not allowed");
}
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("The size must be positive");
}
ComponentPeer peer = window.peer;
if (peer == null || !window.isVisible()) {
return null;
}
if (peer instanceof SurfacePixelGrabber spg) {
// TODO: translate coordinates, maybe?
return spg.getClientAreaSnapshot(x, y, width, height);
}
return null;
}
}

View File

@@ -34,7 +34,6 @@ import java.awt.event.WindowFocusListener;
import java.awt.event.WindowListener;
import java.awt.event.WindowStateListener;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.im.InputContext;
import java.awt.image.BufferStrategy;
import java.awt.peer.ComponentPeer;
@@ -44,6 +43,7 @@ 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;
@@ -53,12 +53,15 @@ 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;
@@ -69,11 +72,14 @@ 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.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.util.logging.PlatformLogger;
@@ -1153,45 +1159,51 @@ public class Window extends Container implements Accessible {
}
void doDispose() {
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);
}
final class DisposeAction implements Runnable {
public void run() {
final Window window = Window.this;
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();
// 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);
}
}
hide();
beforeFirstShow = true;
removeNotify();
synchronized (inputContextLock) {
if (inputContext != null) {
inputContext.dispose();
inputContext = 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;
}
clearCurrentFocusCycleRootOnHide();
} finally {
disposing = false;
}
}
}
boolean fireWindowClosedEvent = isDisplayable();
DisposeAction action = new DisposeAction();
if (EventQueue.isDispatchThread()) {
@@ -3950,6 +3962,7 @@ public class Window extends Container implements Accessible {
private float getHeight() { return height; }
private void setHeight(float height) {
if (height <= 0.0f) throw new IllegalArgumentException("TitleBar height must be positive");
if (this.height == height) return;
this.height = height;
notifyUpdate();
}
@@ -3958,13 +3971,17 @@ public class Window extends Container implements Accessible {
}
private void putProperties(Map<String, ?> m) {
if (properties == null) properties = new HashMap<>();
properties.putAll(m);
notifyUpdate();
boolean needsUpdate = false;
for (Map.Entry<String, ?> e : m.entrySet()) {
Object old = properties.put(e.getKey(), e.getValue());
if (!needsUpdate && !Objects.equals(old, e.getValue())) needsUpdate = true;
}
if (needsUpdate) notifyUpdate();
}
private void putProperty(String key, Object value) {
if (properties == null) properties = new HashMap<>();
properties.put(key, value);
notifyUpdate();
Object old = properties.put(key, value);
if (!Objects.equals(old, value)) notifyUpdate();
}
private float getLeftInset() { return insets[0]; }
private float getRightInset() { return insets[1]; }
@@ -4159,8 +4176,36 @@ public class Window extends Container implements Accessible {
return value;
}
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 {
String counters = System.getProperty("awt.window.counters");
SYSTEM_PROPERTY_COUNTERS = System.getProperty("awt.window.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) {
@@ -4197,100 +4242,201 @@ public class Window extends Container implements Accessible {
public boolean countersEnabled(Window w) {
// May want to selectively enable or disable counters per window
return counters != null;
return USE_COUNTERS;
}
public void bumpCounter(Window w, String counterName) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
private final static long NANO_IN_SEC = java.util.concurrent.TimeUnit.SECONDS.toNanos(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);
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, (_, v) ->
v == null
? new PerfCounter(curTimeNanos, 1L)
: new PerfCounter(curTimeNanos, v.value + 1));
}
synchronized (w.perfCountersPrev) {
w.perfCountersPrev.put(counterName, newCounter);
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();
}
}
}
}
}
}
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 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 getCounterPerSecond(Window w, String counterName) {
Objects.requireNonNull(w);
Objects.requireNonNull(counterName);
PerfCounter newCounter;
PerfCounter prevCounter;
synchronized (w.perfCounters) {
newCounter = w.perfCounters.get(counterName);
private static void doLog(final String msg, final boolean traceEnabled) {
if (traceEnabled) {
getTraceStdStream().println(msg);
}
synchronized (w.perfCountersPrev) {
prevCounter = w.perfCountersPrev.get(counterName);
if (perfLog.isLoggable(PlatformLogger.Level.FINE)) {
perfLog.fine(msg);
}
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 Map<String, PerfCounter> perfCounters = new HashMap<>(4);
private transient final Map<String, PerfCounter> perfCountersPrev = new HashMap<>(4);
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;
} // class Window

View File

@@ -27,10 +27,7 @@ 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.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.applet.*;
@@ -43,14 +40,12 @@ import sun.java2d.SunGraphicsEnvironment;
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;
import sun.swing.SwingAccessor;
import sun.swing.SwingUtilities2;
import sun.swing.SwingUtilities2.RepaintListener;
import java.util.stream.Collectors;
/**
* This class manages repaint requests, allowing the number
@@ -755,7 +750,7 @@ public class RepaintManager
.filter(Objects::nonNull)
.distinct()
.forEach(w -> AWTAccessor.getWindowAccessor()
.bumpCounter(w, "swing.RepaintManager.updateWindows"));
.incrementCounter(w, "swing.RepaintManager.updateWindows"));
if (Toolkit.getDefaultToolkit() instanceof SunToolkit sunToolkit &&
sunToolkit.needUpdateWindow()) {
@@ -774,14 +769,14 @@ public class RepaintManager
for (Window window : windows) {
AWTAccessor.getWindowAccessor().updateWindow(window);
AWTAccessor.getWindowAccessor().bumpCounter(window, "swing.RepaintManager.updateWindows");
AWTAccessor.getWindowAccessor().incrementCounter(window, "swing.RepaintManager.updateWindows");
}
} else {
dirtyComponents.keySet().stream()
.map(c -> c instanceof Window w ? w : SwingUtilities.getWindowAncestor(c))
.filter(Objects::nonNull)
.forEach(w -> AWTAccessor.getWindowAccessor()
.bumpCounter(w, "swing.RepaintManager.updateWindows"));
.incrementCounter(w, "swing.RepaintManager.updateWindows"));
}
}

View File

@@ -37,7 +37,6 @@ import java.awt.event.InputEvent;
import java.awt.event.InvocationEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.awt.image.BufferStrategy;
import java.awt.peer.ComponentPeer;
@@ -329,10 +328,15 @@ public final class AWTAccessor {
*/
Window[] getOwnedWindows(Window w);
/* JBR Window counters API */
boolean countersEnabled(Window w);
void bumpCounter(Window w, String counterName);
void incrementCounter(Window w, String counterName);
void addStat(Window w, String statName, double value);
long getCounter(Window w, String counterName);
long getCounterPerSecond(Window w, String counterName);
double getCounterPerSecond(Window w, String counterName);
void dumpStats(Window w, boolean reset, StringBuilder sb);
}
/**

View File

@@ -0,0 +1,33 @@
/*
* 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.awt;
import java.awt.image.BufferedImage;
public interface SurfacePixelGrabber {
BufferedImage getClientAreaSnapshot(int x, int y, int width, int height);
}

View File

@@ -28,6 +28,8 @@ package sun.java2d.marlin;
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;
@@ -383,16 +385,8 @@ public final class RendererStats implements MarlinConst {
= new ConcurrentLinkedQueue<>();
private RendererStatsHolder() {
final Thread hook = new Thread(
MarlinUtils.getRootThreadGroup(),
new Runnable() {
@Override
public void run() {
dump();
}
},
"MarlinStatsHook"
);
final Thread hook = InnocuousThread.newSystemThread("MarlinStatsHook", () -> dump());
hook.setDaemon(true);
hook.setContextClassLoader(null);
Runtime.getRuntime().addShutdownHook(hook);

View File

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

View File

@@ -31,7 +31,6 @@ import sun.util.logging.PlatformLogger;
import java.awt.Toolkit;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public final class VKEnv {
@@ -109,16 +108,20 @@ public final class VKEnv {
state = newState;
if (Options.verbose || log.isLoggable(PlatformLogger.Level.FINE)) {
String message;
StringBuilder msg = new StringBuilder("Vulkan rendering enabled: ");
if (isVulkanEnabled()) {
message = "Vulkan rendering enabled: YES" +
"\n presentation enabled: " + (isPresentationEnabled() ? "YES" : "NO") +
"\n accelerated surface data enabled: " + (isSurfaceDataAccelerated() ? "YES" : "NO") +
"\n devices:" + Stream.of(devices).map(d -> (d == defaultDevice ?
"\n *" : "\n ") + d.getName()).collect(Collectors.joining());
msg.append("YES")
.append("\n Presentation enabled: ").append(isPresentationEnabled() ? "YES" : "NO")
.append("\n Accelerated surface data enabled: ").append(isSurfaceDataAccelerated() ? "YES" : "NO")
.append("\n Devices:");
for (int i = 0; i < devices.length; i++) {
VKGPU d = devices[i];
msg.append(d == defaultDevice ? "\n *" : "\n ").append(i).append(": ").append(d.getName());
}
} else {
message = "Vulkan rendering enabled: NO";
msg.append("NO");
}
String message = msg.toString();
if (Options.verbose) {
System.err.println(message);
}

View File

@@ -137,7 +137,7 @@ public abstract class VKSurfaceData extends SurfaceData
}
}
protected BufferedImage getSnapshot(int x, int y, int width, int height) {
public BufferedImage getSnapshot(int x, int y, int width, int height) {
BufferedImage image = getFormat().createCompatibleImage(width, height, getTransparency());
SurfaceData sd = SurfaceData.getPrimarySurfaceData(image);
Blit blit = Blit.getFromCache(getSurfaceType(), CompositeType.SrcNoEa, sd.getSurfaceType());

View File

@@ -292,7 +292,15 @@ VkBool32 VKSD_ConfigureWindowSurface(VKWinSDOps* vkwinsdo) {
}
static void VKSD_OnDispose(JNIEnv* env, SurfaceDataOps* ops) {
// We are being called from the disposer thread, RQ might be working in parallel.
// VKRenderQueue.lock/unlock is equivalent to AWT_LOCK/AWT_UNLOCK,
// but those are only available in the toolkit-specific part of AWT, so we call RQ there.
jobject rq = JNU_CallStaticMethodByName(env, NULL,
"sun/java2d/vulkan/VKRenderQueue", "getInstance", "()Lsun/java2d/vulkan/VKRenderQueue;").l;
JNU_CallMethodByName(env, NULL, rq, "lock", "()V");
VKSD_ResetSurface((VKSDOps*) ops);
JNU_CallMethodByName(env, NULL, rq, "unlock", "()V");
(*env)->DeleteLocalRef(env, rq);
}
JNIEXPORT VKSDOps* VKSD_CreateSurface(JNIEnv* env, jobject vksd, jint type, jint format, jint backgroundRGB,

View File

@@ -40,35 +40,44 @@ import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
public class DefaultFrameDecoration extends FullFrameDecorationHelper {
private static final int HEIGHT = 30;
private static final int BORDER_SIZE = 1;
private static final int HEIGHT = 28 + BORDER_SIZE;
private static final int BUTTON_ICON_SIZE = 4;
private static final int BUTTON_CIRCLE_RADIUS = 10;
private static final int BUTTON_SIZE = 16;
private static final int BUTTONS_RIGHT_PADDING = 7;
private static final int BUTTONS_PADDING = 8;
private static final Font FONT = new Font(Font.DIALOG, Font.BOLD, 12);
private static final Color ACTIVE_BACKGROUND = new Color(0xebebeb);
private static final Color ACTIVE_BACKGROUND_DARK = new Color(0x222222);
private static final Color INACTIVE_BACKGROUND = new Color(0xfafafa);
private static final Color INACTIVE_BACKGROUND_DARK = new Color(0x2c2c2c);
private static final Color ACTIVE_BACKGROUND = new Color(0xedeeef);
private static final Color ACTIVE_BACKGROUND_DARK = new Color(0x31363b);
private static final Color INACTIVE_BACKGROUND = new Color(0xdcddde);
private static final Color INACTIVE_BACKGROUND_DARK = new Color(0x292d31);
private static final Color ICON_BACKGROUND = ACTIVE_BACKGROUND;
private static final Color ICON_BACKGROUND_DARK = ACTIVE_BACKGROUND_DARK;
private static final Color ICON_HOVERED_BACKGROUND = new Color(0xd1d1d1);
private static final Color ICON_HOVERED_BACKGROUND_DARK = new Color(0x373737);
private static final Color ICON_PRESSED_BACKGROUND = new Color(0xc0c0c0);
private static final Color ICON_PRESSED_BACKGROUND_DARK = new Color(0x565656);
private static final Color ACTIVE_FOREGROUND = Color.darkGray;
private static final Color ACTIVE_FOREGROUND_DARK = new Color(0xf7f7f7);
private static final Color INACTIVE_FOREGROUND = Color.gray;
private static final Color INACTIVE_FOREGROUND_DARK = new Color(0xb5b5b5);
private static final Color ICON_HOVERED_BACKGROUND = new Color(0x232629);
private static final Color ICON_HOVERED_BACKGROUND_DARK = new Color(0xfcfcfc);
private static final Color ICON_HOVERED_FOREGROUND = new Color(0xcacdcf);
private static final Color ICON_HOVERED_FOREGROUND_DARK = new Color(0x43484c);
private static final Color ICON_PRESSED_BACKGROUND = new Color(0xa6a8ab);
private static final Color ICON_PRESSED_BACKGROUND_DARK = new Color(0x6e7175);
private static final Color CLOSE_ICON_PRESSED_BACKGROUND = new Color(0x6d2229);
private static final Color CLOSE_ICON_PRESSED_BACKGROUND_DARK = new Color(0x6d2229);
private static final Color CLOSE_ICON_HOVERED_BACKGROUND = new Color(0xff98a2);
private static final Color CLOSE_ICON_HOVERED_INACTIVE_BACKGROUND = new Color(0xda4453);
private static final Color CLOSE_ICON_HOVERED_INACTIVE_BACKGROUND_DARK = new Color(0xda4453);
private static final Color CLOSE_ICON_HOVERED_BACKGROUND_DARK = new Color(0xff98a2);
private static final Color ACTIVE_FOREGROUND = new Color(0x2d3033);
private static final Color ACTIVE_FOREGROUND_DARK = new Color(0xf1f1f1);
private static final Color INACTIVE_FOREGROUND = ACTIVE_FOREGROUND;
private static final Color INACTIVE_FOREGROUND_DARK = ACTIVE_FOREGROUND_DARK;
private static final Color ACTIVE_BACKGROUND_TOP = new Color(0xfbfbfb);
private static final Color ACTIVE_BACKGROUND_TOP_DARK = new Color(0x313131);
private static final Color INACTIVE_BACKGROUND_TOP = new Color(0xfefefe);
private static final Color INACTIVE_BACKGROUND_TOP_DARK = new Color(0x3a3a3a);
private static final Color ACTIVE_BORDER = new Color(0x9e9e9e);
private static final Color ACTIVE_BORDER_DARK = new Color(0x080808);
private static final Color INACTIVE_BORDER = new Color(0xbcbcbc);
private static final Color INACTIVE_BORDER_DARK = new Color(0x121212);
private static final int BORDER_SIZE = 1;
private static final Color ACTIVE_BACKGROUND_TOP = new Color(0xa9abac);
private static final Color ACTIVE_BACKGROUND_TOP_DARK = new Color(0x4c565f);
private static final Color INACTIVE_BACKGROUND_TOP = new Color(0xb7b8b9);
private static final Color INACTIVE_BACKGROUND_TOP_DARK = new Color(0x424952);
private static final Color ACTIVE_BORDER = ACTIVE_BACKGROUND_TOP;
private static final Color ACTIVE_BORDER_DARK = ACTIVE_BACKGROUND_TOP_DARK;
private static final Color INACTIVE_BORDER = INACTIVE_BACKGROUND_TOP;
private static final Color INACTIVE_BORDER_DARK = INACTIVE_BACKGROUND_TOP_DARK;
private static final int SIGNIFICANT_DRAG_DISTANCE = 4;
public DefaultFrameDecoration(WLDecoratedPeer peer, boolean showMinimize, boolean showMaximize) {
@@ -96,64 +105,27 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
return new Dimension(getButtonSpaceWidth(), HEIGHT);
}
private Point getCloseButtonCenter() {
int width = peer.getWidth();
return width >= HEIGHT ? new Point(width - HEIGHT / 2, HEIGHT / 2) : null;
}
private Point getMaximizeButtonCenter() {
if (!hasMaximizeButton()) return null;
int width = peer.getWidth();
return width >= 2 * HEIGHT ? new Point(width - HEIGHT * 3 / 2, HEIGHT / 2) : null;
}
private Point getMinimizeButtonCenter() {
if (!hasMinimizeButton()) return null;
int width = peer.getWidth();
int buttonSpaceWidth = getButtonSpaceWidth();
return width >= buttonSpaceWidth ? new Point(width - buttonSpaceWidth + HEIGHT / 2, HEIGHT / 2) : null;
}
@Override
protected Rectangle getCloseButtonBounds() {
int width = peer.getWidth();
if (width >= HEIGHT) {
return new Rectangle(width - HEIGHT / 2 - BUTTON_CIRCLE_RADIUS,
HEIGHT / 2 - BUTTON_CIRCLE_RADIUS,
BUTTON_CIRCLE_RADIUS * 2,
BUTTON_CIRCLE_RADIUS * 2);
} else {
return null;
}
int x = peer.getWidth() - BUTTON_SIZE - BUTTONS_RIGHT_PADDING - BORDER_SIZE;
int y = (int) Math.floor((HEIGHT - BUTTON_SIZE + 1f) / 2);
return new Rectangle(x, y, BUTTON_SIZE, BUTTON_SIZE);
}
@Override
protected Rectangle getMaximizeButtonBounds() {
if (!hasMaximizeButton()) return null;
int width = peer.getWidth();
if (width >= 2 * HEIGHT) {
return new Rectangle(width - HEIGHT * 3 / 2 - BUTTON_CIRCLE_RADIUS,
HEIGHT / 2 - BUTTON_CIRCLE_RADIUS,
BUTTON_CIRCLE_RADIUS * 2,
BUTTON_CIRCLE_RADIUS * 2);
} else {
return null;
}
int x = peer.getWidth() - BUTTON_SIZE * 2 - BUTTONS_RIGHT_PADDING
- BUTTONS_PADDING - BORDER_SIZE;
int y = (int) Math.floor((HEIGHT - BUTTON_SIZE + 1f) / 2);
return x > 0 ? new Rectangle(x, y, BUTTON_SIZE, BUTTON_SIZE) : null;
}
@Override
protected Rectangle getMinimizeButtonBounds() {
if (!hasMinimizeButton()) return null;
int width = peer.getWidth();
int buttonSpaceWidth = getButtonSpaceWidth();
if (width >= buttonSpaceWidth) {
return new Rectangle(width - buttonSpaceWidth + HEIGHT / 2 - BUTTON_CIRCLE_RADIUS,
HEIGHT / 2 - BUTTON_CIRCLE_RADIUS,
BUTTON_CIRCLE_RADIUS * 2,
BUTTON_CIRCLE_RADIUS * 2);
} else {
return null;
}
int x = peer.getWidth() - BUTTON_SIZE * 3 - BUTTONS_RIGHT_PADDING
- BUTTONS_PADDING * 2 - BORDER_SIZE;
int y = (int) Math.floor((HEIGHT - BUTTON_SIZE + 1f) / 2);
return x > 0 ? new Rectangle(x, y, BUTTON_SIZE, BUTTON_SIZE) : null;
}
@Override
@@ -170,7 +142,7 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
final int numButtons = 1
+ (hasMaximizeButton() ? 1 : 0)
+ (hasMinimizeButton() ? 1 : 0);
return numButtons * HEIGHT;
return numButtons * BUTTON_SIZE + (numButtons - 1) * BUTTONS_PADDING + BUTTONS_RIGHT_PADDING;
}
private Color getBackgroundColor(boolean isActive) {
@@ -217,13 +189,33 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
}
}
private Color getButtonForeground(boolean isHovered) {
if (isHovered) {
return isDarkTheme() ? ICON_HOVERED_FOREGROUND_DARK : ICON_HOVERED_FOREGROUND;
} else {
return isDarkTheme() ? ACTIVE_FOREGROUND_DARK : ACTIVE_FOREGROUND;
}
}
private Color getClosePressedBackground() {
return isDarkTheme() ? CLOSE_ICON_PRESSED_BACKGROUND_DARK : CLOSE_ICON_PRESSED_BACKGROUND;
}
private Color getCloseHoveredBackground(boolean isActive) {
if (isActive) {
return isDarkTheme() ? CLOSE_ICON_HOVERED_BACKGROUND_DARK : CLOSE_ICON_HOVERED_BACKGROUND;
} else {
return isDarkTheme() ? CLOSE_ICON_HOVERED_INACTIVE_BACKGROUND_DARK : CLOSE_ICON_HOVERED_INACTIVE_BACKGROUND;
}
}
@Override
protected void paintBorder(Graphics2D g2d) {
int width = peer.getWidth();
int height = peer.getHeight();
g2d.setColor(getBorderColor(isActive()));
g2d.setStroke(new BasicStroke(BORDER_SIZE));
g2d.drawRect(0, 0, width - BORDER_SIZE, height - BORDER_SIZE);
g2d.drawRect(0, 0, width, height);
}
@Override
@@ -250,17 +242,11 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
// The title bar
g.fillRoundRect(0, 0, width, HEIGHT + radius + 1, radius, radius);
// The top bevel of the title bar
g.setColor(getBackgroundTopColor(active));
g.drawLine(radius / 2, 1, width - radius / 2, 1);
g.drawArc(1, 1, (radius - 1), (radius - 1), 90, 60);
g.drawArc(width - radius, 1, (radius - 1), (radius - 1), 45, 45);
// The border
var oldStroke = g.getStroke();
g.setColor(getBorderColor(active));
g.setStroke(new BasicStroke(BORDER_SIZE));
g.drawRoundRect(0, 0, width - BORDER_SIZE, HEIGHT + radius + 1, radius, radius);
g.drawRoundRect(0, 0, width, HEIGHT + radius + 1, radius, radius);
g.setStroke(oldStroke);
g.drawLine(0, HEIGHT - 1, width, HEIGHT - 1);
} else {
@@ -270,32 +256,35 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
// The top bevel of the title bar
g.setColor(getBackgroundTopColor(active));
g.drawLine(BORDER_SIZE, BORDER_SIZE, width - BORDER_SIZE, BORDER_SIZE);
g.drawLine(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, HEIGHT - BORDER_SIZE);
g.drawLine(BORDER_SIZE, BORDER_SIZE, width, BORDER_SIZE);
g.drawLine(BORDER_SIZE, BORDER_SIZE, BORDER_SIZE, HEIGHT);
// The border
var oldStroke = g.getStroke();
g.setColor(getBorderColor(active));
g.setStroke(new BasicStroke(BORDER_SIZE));
g.drawRect(0, 0, width - BORDER_SIZE, HEIGHT - BORDER_SIZE);
g.drawRect(0, 0, width, HEIGHT);
g.setStroke(oldStroke);
}
paintTitle(g, title, foregroundColor, width);
Point closeButtonCenter = getCloseButtonCenter();
if (closeButtonCenter != null) {
paintButtonBackground(g, closeButtonCenter, closeButton);
paintCloseButton(g, closeButtonCenter, foregroundColor);
Rectangle closeButtonBounds = getCloseButtonBounds();
if (closeButtonBounds != null) {
paintCloseButtonBackground(g, closeButtonBounds, closeButton);
Color buttonColor = getButtonForeground(closeButton.hovered);
paintCloseButton(g, closeButtonBounds, buttonColor);
}
Point maximizedButtonCenter = getMaximizeButtonCenter();
if (maximizedButtonCenter != null) {
paintButtonBackground(g, maximizedButtonCenter, maximizeButton);
paintMaximizeButton(g, maximizedButtonCenter, foregroundColor);
Rectangle maximizedButtonBounds = getMaximizeButtonBounds();
if (maximizedButtonBounds != null) {
paintButtonBackground(g, maximizedButtonBounds, maximizeButton);
Color buttonColor = getButtonForeground(maximizeButton.hovered);
paintMaximizeButton(g, maximizedButtonBounds, buttonColor);
}
Point minimizedButtonCenter = getMinimizeButtonCenter();
if (minimizedButtonCenter != null) {
paintButtonBackground(g, minimizedButtonCenter, minimizeButton);
paintMinimizeButton(g, minimizedButtonCenter, foregroundColor);
Rectangle minimizedButtonBounds = getMinimizeButtonBounds();
if (minimizedButtonBounds != null) {
paintButtonBackground(g, minimizedButtonBounds, minimizeButton);
Color buttonColor = getButtonForeground(minimizeButton.hovered);
paintMinimizeButton(g, minimizedButtonBounds, buttonColor);
}
g.setClip(null);
}
@@ -304,7 +293,7 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
g.setColor(foregroundColor);
g.setFont(FONT);
FontMetrics fm = g.getFontMetrics();
int leftMargin = HEIGHT / 2 - BUTTON_CIRCLE_RADIUS; // same as space between close button and right window edge
int leftMargin = HEIGHT / 2 - BUTTON_SIZE; // same as space between close button and right window edge
int availableWidth = width - getButtonSpaceWidth() - leftMargin;
String text = SwingUtilities2.clipStringIfNecessary(null, fm, title, availableWidth);
int textWidth = fm.stringWidth(text);
@@ -313,48 +302,60 @@ public class DefaultFrameDecoration extends FullFrameDecorationHelper {
(HEIGHT - fm.getHeight()) / 2 + fm.getAscent());
}
private void paintButtonBackground(Graphics2D g, Point center, ButtonState state) {
if (isActive()) {
private void paintCloseButtonBackground(Graphics2D g, Rectangle bounds, ButtonState state) {
if (!isActive() && !state.hovered && !state.pressed) return;
g.setColor(state.pressed ? getClosePressedBackground() :
state.hovered ? getCloseHoveredBackground(isActive()) : getIconBackground());
g.fill(new Ellipse2D.Float(bounds.x, bounds.y, bounds.width, bounds.height));
}
private void paintButtonBackground(Graphics2D g, Rectangle bounds, ButtonState state) {
if (state.hovered || state.pressed) {
g.setColor(state.pressed ? getIconPressedBackground() :
state.hovered ? getIconHoveredBackground() : getIconBackground());
g.fill(new Ellipse2D.Float(center.x - BUTTON_CIRCLE_RADIUS + .5f,
center.y - BUTTON_CIRCLE_RADIUS + .5f,
2 * BUTTON_CIRCLE_RADIUS, 2 * BUTTON_CIRCLE_RADIUS));
g.fill(new Ellipse2D.Float(bounds.x, bounds.y, bounds.width, bounds.height));
}
}
private void paintCloseButton(Graphics2D g, Point center, Color foregroundColor) {
private static Point centerOf(Rectangle rect) {
return new Point((int) Math.floor(rect.x + rect.width / 2f),
(int) Math.floor(rect.y + rect.height / 2f));
}
private void paintCloseButton(Graphics2D g, Rectangle bounds, Color foregroundColor) {
g.setColor(foregroundColor);
Point center = centerOf(bounds);
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE,
center.x + BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE);
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE,
center.x + BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE);
}
private void paintMaximizeButton(Graphics2D g, Point center, Color foregroundColor) {
private void paintMaximizeButton(Graphics2D g, Rectangle bounds, Color foregroundColor) {
g.setColor(foregroundColor);
Point center = centerOf(bounds);
int size = BUTTON_ICON_SIZE + 1;
if (peer.getState() == Frame.MAXIMIZED_BOTH) {
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y,
center.x, center.y - BUTTON_ICON_SIZE);
g.drawLine(center.x, center.y - BUTTON_ICON_SIZE,
center.x + BUTTON_ICON_SIZE, center.y);
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y,
center.x, center.y + BUTTON_ICON_SIZE);
g.drawLine(center.x, center.y + BUTTON_ICON_SIZE,
center.x + BUTTON_ICON_SIZE, center.y);
g.drawLine(center.x - size, center.y, center.x, center.y - size);
g.drawLine(center.x, center.y - size, center.x + size, center.y);
g.drawLine(center.x - size, center.y, center.x, center.y + size);
g.drawLine(center.x, center.y + size, center.x + size, center.y);
} else {
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE / 2,
center.x, center.y - BUTTON_ICON_SIZE / 2);
g.drawLine(center.x, center.y - BUTTON_ICON_SIZE / 2,
center.x + BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE / 2);
g.drawLine(center.x - size, (int) (center.y + size / 2f),
center.x, (int) (center.y - size / 2f));
g.drawLine(center.x, (int) (center.y - size / 2f),
center.x + size, (int) (center.y + size / 2f));
}
}
private void paintMinimizeButton(Graphics2D g, Point center, Color foregroundColor) {
private void paintMinimizeButton(Graphics2D g, Rectangle bounds, Color foregroundColor) {
g.setColor(foregroundColor);
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE / 2,
center.x, center.y + BUTTON_ICON_SIZE / 2);
g.drawLine(center.x, center.y + BUTTON_ICON_SIZE / 2,
center.x + BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE / 2);
Point center = centerOf(bounds);
int size = BUTTON_ICON_SIZE + 1;
g.drawLine(center.x - size, (int) (center.y - size / 2f),
center.x, (int) (center.y + size / 2f));
g.drawLine(center.x, (int) (center.y + size / 2f),
center.x + size, (int) (center.y - size / 2f));
}
}

View File

@@ -76,8 +76,10 @@ public class GtkFrameDecoration extends FullFrameDecorationHelper {
@Override
public void paint(Graphics g) {
// Determine buttons' bounds, etc.
nativePrePaint(nativePtr, peer.getWidth());
super.paint(g);
nativePrePaint(nativePtr, peer.getWidth(), peer.getHeight());
if (peer.getWidth() >= titleBarMinWidth && peer.getHeight() >= titleBarHeight) {
super.paint(g);
}
}
@Override
@@ -85,6 +87,9 @@ public class GtkFrameDecoration extends FullFrameDecorationHelper {
int width = peer.getWidth();
int height = titleBarHeight;
assert width >= titleBarMinWidth;
assert peer.getHeight() >= titleBarHeight;
double scale = ((WLGraphicsConfig) peer.getGraphicsConfiguration()).getEffectiveScale();
g2d.setBackground(new Color(0, true));
g2d.clearRect(0, 0, width, height);
@@ -217,5 +222,5 @@ public class GtkFrameDecoration extends FullFrameDecorationHelper {
String title, int buttonsState);
private native int nativeGetIntProperty(long nativePtr, String name);
private native void nativeNotifyConfigured(long nativePtr, boolean active, boolean maximized, boolean fullscreen);
private native void nativePrePaint(long nativePtr, int width);
private native void nativePrePaint(long nativePtr, int width, int height);
}

View File

@@ -88,8 +88,8 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.awt.wl.focus.WLComponentPeer");
private static final PlatformLogger popupLog = PlatformLogger.getLogger("sun.awt.wl.popup.WLComponentPeer");
private static final int MINIMUM_WIDTH = 1;
private static final int MINIMUM_HEIGHT = 1;
protected static final int MINIMUM_WIDTH = 1;
protected static final int MINIMUM_HEIGHT = 1;
private final Object stateLock = new Object();
@@ -135,7 +135,12 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
log.fine("WLComponentPeer: target=" + target + " with size=" + wlSize);
}
shadow = new Shadow(targetIsWlPopup() ? ShadowImage.POPUP_SHADOW_SIZE : ShadowImage.WINDOW_SHADOW_SIZE);
boolean shadowEnabled = Boolean.parseBoolean(System.getProperty("sun.awt.wl.Shadow", "true"));
if (shadowEnabled) {
shadow = new ShadowImpl(targetIsWlPopup() ? ShadowImage.POPUP_SHADOW_SIZE : ShadowImage.WINDOW_SHADOW_SIZE);
} else {
shadow = new NilShadow();
}
// TODO
// setup parent window for target
}
@@ -852,8 +857,10 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
}
public Dimension getMinimumSize() {
int shadowSize = (int) Math.ceil(shadow.getSize() * 4);
return new Dimension(shadowSize, shadowSize);
int shadowSize = shadow != null ? (int) Math.ceil(shadow.getSize() * 4) : 0;
return shadowSize == 0
? new Dimension(MINIMUM_WIDTH, MINIMUM_HEIGHT)
: new Dimension(shadowSize, shadowSize);
}
void showWindowMenu(long serial, int x, int y) {
@@ -1611,6 +1618,14 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
return new Dimension(javaUnitsToSurfaceSize(d.width), javaUnitsToSurfaceSize(d.height));
}
int javaUnitsToBufferUnits(int value) {
return (int) Math.floor(value * effectiveScale);
}
int javaSizeToBufferSize(int value) {
return (int) Math.ceil(value * effectiveScale);
}
/**
* Converts a point in the device (screen) space into coordinates on this surface
*/
@@ -1741,9 +1756,12 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
private Dimension constrainSize(int width, int height) {
Dimension maxBounds = getMaxBufferBounds();
Dimension minSize = getMinimumSize();
minSize.width = Math.max(MINIMUM_WIDTH, minSize.width);
minSize.height = Math.max(MINIMUM_HEIGHT, minSize.height);
return new Dimension(
Math.max(Math.min(width, maxBounds.width), MINIMUM_WIDTH),
Math.max(Math.min(height, maxBounds.height), MINIMUM_HEIGHT));
Math.max(Math.min(width, maxBounds.width), minSize.width),
Math.max(Math.min(height, maxBounds.height), minSize.height));
}
private Dimension constrainSize(Dimension bounds) {
@@ -1812,6 +1830,20 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
return result;
}
private interface Shadow {
int getSize();
void updateSurfaceSize();
void resizeToParentWindow();
void createSurface();
void commitSurface();
void dispose();
void hide();
void updateSurfaceData();
void paint();
void commitSurfaceData();
void notifyConfigured(boolean active, boolean maximized, boolean fullscreen);
}
private static class ShadowImage {
private static final Color activeColor = new Color(0, 0, 0, 0xA0);
private static final Color inactiveColor = new Color(0, 0, 0, 0x40);
@@ -1916,7 +1948,7 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
}
}
private class Shadow implements WLSurfaceSizeListener {
private class ShadowImpl implements WLSurfaceSizeListener, Shadow {
private WLSubSurface shadowSurface; // protected by AWT lock
private SurfaceData shadowSurfaceData; // protected by AWT lock
private boolean needsRepaint = true; // protected by AWT lock
@@ -1924,7 +1956,7 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
private final WLSize shadowWlSize = new WLSize(); // protected by stateLock
private boolean isActive; // protected by AWT lock
public Shadow(int shadowSize) {
public ShadowImpl(int shadowSize) {
this.shadowSize = shadowSize;
shadowWlSize.deriveFromJavaSize(wlSize.getJavaWidth() + shadowSize * 2, wlSize.getJavaHeight() + shadowSize * 2);
shadowSurfaceData = ((WLGraphicsConfig) getGraphicsConfiguration()).createSurfaceData(this, shadowWlSize.getPixelWidth(), shadowWlSize.getPixelHeight());
@@ -2039,6 +2071,20 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
}
}
private static class NilShadow implements Shadow {
@Override public int getSize() { return 0; }
@Override public void updateSurfaceSize() { }
@Override public void resizeToParentWindow() { }
@Override public void createSurface() { }
@Override public void commitSurface() { }
@Override public void dispose() { }
@Override public void hide() { }
@Override public void updateSurfaceData() { }
@Override public void paint() { }
@Override public void commitSurfaceData() { }
@Override public void notifyConfigured(boolean active, boolean maximized, boolean fullscreen) { }
}
private class WLSize {
/**
* Represents the full size of the component in "client" units as returned by Component.getSize().

View File

@@ -88,7 +88,7 @@ public class WLDataDevice {
private static native void dispatchDataSourceQueueImpl(long nativePtr);
private static native void setSelectionImpl(int protocol, long nativePtr, long dataOfferNativePtr, long serial);
private static native void startDragImpl(long nativePtr, long dataOfferNativePtr,
long originSurfaceNativePtr, long iconNativePtr, long serial);
long originSurfaceNativePtr, long serial);
public boolean isProtocolSupported(int protocol) {
return isProtocolSupportedImpl(nativePtr, protocol);
@@ -98,8 +98,8 @@ public class WLDataDevice {
setSelectionImpl(protocol, nativePtr, (source == null) ? 0 : source.getNativePtr(), serial);
}
public void startDrag(WLDataSource source, long originSurfaceNativePtr, long iconNativePtr, long serial) {
startDragImpl(nativePtr, source.getNativePtr(), originSurfaceNativePtr, iconNativePtr, serial);
public void startDrag(WLDataSource source, long originSurfaceNativePtr, long serial) {
startDragImpl(nativePtr, source.getNativePtr(), originSurfaceNativePtr, serial);
}
public WLClipboard getSystemClipboard() {

View File

@@ -25,7 +25,10 @@
package sun.awt.wl;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.datatransfer.Transferable;
import java.awt.image.BufferedImage;
import java.util.HashSet;
public class WLDataSource {
@@ -44,6 +47,8 @@ public class WLDataSource {
private static native void setDnDActionsImpl(long nativePtr, int actions);
private static native void setDnDIconImpl(long nativePtr, int width, int height, int offsetX, int offsetY, int[] pixels);
WLDataSource(WLDataDevice dataDevice, int protocol, Transferable data) {
var wlDataTransferer = (WLDataTransferer) WLDataTransferer.getInstance();
@@ -91,6 +96,31 @@ public class WLDataSource {
setDnDActionsImpl(nativePtr, actions);
}
public void setDnDIcon(Image image, int offsetX, int offsetY) {
if (nativePtr == 0) {
throw new IllegalStateException("Native pointer is null");
}
int width = image.getWidth(null);
int height = image.getHeight(null);
int[] pixels = new int[width * height];
if (image instanceof BufferedImage) {
// NOTE: no need to ensure that the BufferedImage is TYPE_INT_ARGB,
// getRGB() does pixel format conversion automatically
((BufferedImage) image).getRGB(0, 0, width, height, pixels, 0, width);
} else {
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bufferedImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
bufferedImage.getRGB(0, 0, width, height, pixels, 0, width);
}
setDnDIconImpl(nativePtr, width, height, offsetX, offsetY, pixels);
}
public synchronized void destroy() {
if (nativePtr != 0) {
destroyImpl(nativePtr);

View File

@@ -63,7 +63,7 @@ public abstract class WLDecoratedPeer extends WLWindowPeer {
d = new DefaultFrameDecoration(this, showMinimize, showMaximize);
}
} else {
if (isGTKAvailable()) {
if (!WLToolkit.isKDE() && isGTKAvailable()) {
d = new GtkFrameDecoration(this, showMinimize, showMaximize);
} else {
d = new DefaultFrameDecoration(this, showMinimize, showMaximize);
@@ -134,10 +134,11 @@ public abstract class WLDecoratedPeer extends WLWindowPeer {
@Override
public Dimension getMinimumSize() {
final Dimension parentMinimumSize = super.getMinimumSize();
final Dimension decorMinimumSize = getDecoration().getMinimumSize();
var d = getDecoration();
final Dimension decorMinimumSize = d != null ? d.getMinimumSize() : new Dimension(0, 0);
final Dimension frameMinimumSize
= (decorMinimumSize.getWidth() == 0 && decorMinimumSize.getHeight() == 0)
? new Dimension(1, 1)
? new Dimension(MINIMUM_WIDTH, MINIMUM_HEIGHT)
: decorMinimumSize;
return new Rectangle(parentMinimumSize)
.union(new Rectangle(frameMinimumSize))

View File

@@ -115,10 +115,16 @@ public class WLDragSourceContextPeer extends SunDragSourceContextPeer {
source.setDnDActions(waylandActions);
var dragImage = getDragImage();
if (dragImage != null) {
var dragImageOffset = getDragImageOffset();
source.setDnDIcon(dragImage, dragImageOffset.x, dragImageOffset.y);
}
long eventSerial = WLToolkit.getInputState().pointerButtonSerial();
var wlSurface = getComponentWlSurfacePtr();
dataDevice.startDrag(source, wlSurface, 0, eventSerial);
dataDevice.startDrag(source, wlSurface, eventSerial);
}
@Override

View File

@@ -52,7 +52,8 @@ public class WLRoundedCornersManager implements RoundedCornersManager {
public static int roundCornerRadiusFor(RoundedCornerKind kind) {
return switch (kind) {
case DEFAULT, FULL -> 24;
case DEFAULT -> 12;
case FULL -> 24;
case NONE -> 0;
case SMALL -> 8;
};

View File

@@ -138,6 +138,8 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
private static Boolean sunAwtDisableGtkFileDialogs = null;
private static final boolean isKDE;
private static native void initIDs(long displayPtr);
static {
@@ -147,6 +149,8 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
VKEnv.init(display);
initIDs(display);
}
String desktop = System.getenv("XDG_CURRENT_DESKTOP");
isKDE = desktop != null && desktop.toLowerCase().contains("kde");
initialized = true;
}
@@ -1076,4 +1080,7 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
return WLCursorManager.getInstance();
}
public static boolean isKDE() {
return isKDE;
}
}

View File

@@ -25,7 +25,10 @@
package sun.awt.wl;
import sun.awt.AWTAccessor;
import sun.awt.SurfacePixelGrabber;
import sun.java2d.SunGraphics2D;
import sun.java2d.vulkan.VKSurfaceData;
import sun.java2d.wl.WLSMSurfaceData;
import javax.swing.JRootPane;
import javax.swing.RootPaneContainer;
@@ -36,16 +39,18 @@ import java.awt.Dialog;
import java.awt.Font;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.SystemColor;
import java.awt.Window;
import java.awt.event.WindowEvent;
import java.awt.geom.Path2D;
import java.awt.image.BufferedImage;
import java.awt.peer.ComponentPeer;
import java.awt.peer.WindowPeer;
import java.lang.ref.WeakReference;
public class WLWindowPeer extends WLComponentPeer implements WindowPeer {
public class WLWindowPeer extends WLComponentPeer implements WindowPeer, SurfacePixelGrabber {
private static Font defaultFont;
private Dialog blocker;
@@ -236,6 +241,64 @@ public class WLWindowPeer extends WLComponentPeer implements WindowPeer {
synthFocusOwner = new WeakReference<>(c);
}
@Override
public BufferedImage getClientAreaSnapshot(int x, int y, int width, int height) {
// Move the coordinate system to the client area
Insets insets = getInsets();
x += insets.left;
y += insets.top;
if (width <= 0 || height <= 0) {
return null;
}
if (x < 0 || y < 0) {
// Shouldn't happen, but better avoid accessing surface data outside the range
throw new IllegalArgumentException("Negative coordinates are not allowed");
}
if (x >= getWidth()) {
throw new IllegalArgumentException(String.format("x coordinate (%d) is out of bounds (%d)", x, getWidth()));
}
if (y >= getHeight()) {
throw new IllegalArgumentException(String.format("y coordinate (%d) is out of bounds (%d)", y, getHeight()));
}
if ((long) x + width > getWidth()) {
width = getWidth() - x;
}
if ((long) y + height > getHeight()) {
height = getHeight() - y;
}
// At this point the coordinates and size are in Java units;
// need to convert them into pixels.
Rectangle bounds = new Rectangle(
javaUnitsToBufferUnits(x),
javaUnitsToBufferUnits(y),
javaSizeToBufferSize(width),
javaSizeToBufferSize(height)
);
Rectangle bufferBounds = getBufferBounds();
if (bounds.x >= bufferBounds.width) {
bounds.x = bufferBounds.width - 1;
}
if (bounds.y >= bufferBounds.height) {
bounds.y = bufferBounds.height - 1;
}
if (bounds.x + bounds.width > bufferBounds.width) {
bounds.width = bufferBounds.width - bounds.x;
}
if (bounds.y + bounds.height > bufferBounds.height) {
bounds.height = bufferBounds.height - bounds.y;
}
if (surfaceData instanceof VKSurfaceData vksd) {
return vksd.getSnapshot(bounds.x, bounds.y, bounds.width, bounds.height);
} else if (surfaceData instanceof WLSMSurfaceData smsd) {
return smsd.getSnapshot(bounds.x, bounds.y, bounds.width, bounds.height);
}
return null;
}
private boolean canPaintRoundedCorners() {
int roundedCornerSize = WLRoundedCornersManager.roundCornerRadiusFor(roundedCornerKind);
// Note: You would normally get a transparency-capable color model when using

View File

@@ -26,19 +26,25 @@
package sun.java2d.wl;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Window;
import java.awt.GraphicsConfiguration;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.util.Objects;
import sun.awt.AWTAccessor;
import sun.awt.wl.WLComponentPeer;
import sun.awt.wl.WLGraphicsConfig;
import sun.awt.wl.WLSMGraphicsConfig;
import sun.java2d.SurfaceData;
import sun.java2d.loops.Blit;
import sun.java2d.loops.CompositeType;
import sun.java2d.loops.SurfaceType;
import sun.util.logging.PlatformLogger;
@@ -124,16 +130,20 @@ public class WLSMSurfaceData extends SurfaceData implements WLSurfaceDataExt, WL
return gc;
}
public BufferedImage getSnapshot(int x, int y, int width, int height) {
ColorModel colorModel = getColorModel();
SampleModel sampleModel = colorModel.createCompatibleSampleModel(width, height);
WritableRaster raster = Raster.createWritableRaster(sampleModel, null);
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
SurfaceData sd = SurfaceData.getPrimarySurfaceData(image);
Blit blit = Blit.getFromCache(getSurfaceType(), CompositeType.SrcNoEa, sd.getSurfaceType());
blit.Blit(this, sd, AlphaComposite.Src, null, x, y, 0, 0, width, height);
return image;
}
@Override
public Raster getRaster(int x, int y, int w, int h) {
// Can do something like the following:
// Raster r = getColorModel().createCompatibleWritableRaster(w, h);
// copy surface data to this raster
// save a reference to this raster
// return r;
// then in flush() check if raster was modified and take pixels from there
// This is obviously suboptimal and shouldn't be used in performance-critical situations.
throw new UnsupportedOperationException("Not implemented yet");
return getSnapshot(x, y, w, h).getRaster().createTranslatedChild(x, y);
}
@Override
@@ -189,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().bumpCounter(window, "java2d.native.frames");
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.frames");
}
}
@@ -198,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().bumpCounter(window, "java2d.native.framesDropped");
AWTAccessor.getWindowAccessor().incrementCounter(window, "java2d.native.framesDropped");
}
}

View File

@@ -684,7 +684,7 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_GtkFrameDecoration_nativePaintTitleBar
}
JNIEXPORT void JNICALL Java_sun_awt_wl_GtkFrameDecoration_nativePrePaint(JNIEnv *env, jobject obj,
jlong ptr, jint width) {
jlong ptr, jint width, jint height) {
assert (ptr != 0);
GtkFrameDecorationDescr* decor = jlong_to_ptr(ptr);
@@ -714,6 +714,12 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_GtkFrameDecoration_nativePrePaint(JNIEnv
(*env)->SetIntField(env, obj, TitleBarHeightFID, pref_height);
(*env)->SetIntField(env, obj, TitleBarMinWidthFID, min_width);
if (width < min_width || height < pref_height) {
// Avoid gtk warnings in case of insufficient space
p_gdk_threads_leave();
return;
}
GtkAllocation ha = {0, 0, width, pref_height};
p_gtk_widget_size_allocate(decor->titlebar, &ha);

View File

@@ -72,6 +72,9 @@ struct DataSource
struct wl_data_source *wlDataSource;
struct zwp_primary_selection_source_v1 *zwpPrimarySelectionSource;
};
struct wl_surface* dragIcon;
struct wl_buffer* dragIconBuffer;
};
// native part of WLDataOffer, remains alive until WLDataOffer.destroy() is called
@@ -937,7 +940,7 @@ Java_sun_awt_wl_WLDataDevice_setSelectionImpl(JNIEnv *env,
JNIEXPORT void JNICALL
Java_sun_awt_wl_WLDataDevice_startDragImpl(JNIEnv *env, jclass clazz, jlong dataDeviceNativePtr,
jlong dataSourceNativePtr, jlong wlSurfacePtr,
jlong iconPtr, jlong serial)
jlong serial)
{
struct DataDevice *dataDevice = jlong_to_ptr(dataDeviceNativePtr);
assert(dataDevice != NULL);
@@ -946,7 +949,10 @@ Java_sun_awt_wl_WLDataDevice_startDragImpl(JNIEnv *env, jclass clazz, jlong data
assert(source != NULL);
wl_data_device_start_drag(dataDevice->wlDataDevice, source->wlDataSource, jlong_to_ptr(wlSurfacePtr),
jlong_to_ptr(iconPtr), serial);
source->dragIcon, serial);
if (source->dragIcon != NULL) {
wl_surface_commit(source->dragIcon);
}
}
JNIEXPORT jlong JNICALL
@@ -1045,6 +1051,14 @@ Java_sun_awt_wl_WLDataSource_destroyImpl(JNIEnv *env, jclass clazz, jlong native
zwp_primary_selection_source_v1_destroy(source->zwpPrimarySelectionSource);
}
if (source->dragIconBuffer) {
wl_buffer_destroy(source->dragIconBuffer);
}
if (source->dragIcon) {
wl_surface_destroy(source->dragIcon);
}
free(source);
}
@@ -1058,6 +1072,65 @@ Java_sun_awt_wl_WLDataSource_setDnDActionsImpl(JNIEnv *env,
DataSource_setDnDActions(source, actions);
}
JNIEXPORT void JNICALL Java_sun_awt_wl_WLDataSource_setDnDIconImpl
(JNIEnv * env, jclass clazz, jlong nativePtr, jint width, jint height, jint offsetX, jint offsetY, jintArray pixels)
{
struct DataSource *source = jlong_to_ptr(nativePtr);
size_t pixelCount = (size_t)((*env)->GetArrayLength(env, pixels));
size_t byteSize = pixelCount * 4U;
if (byteSize >= INT32_MAX) {
return;
}
jint *shmPixels = NULL;
struct wl_shm_pool *pool = CreateShmPool(byteSize, "WLDataSource_DragIcon", (void**)&shmPixels, NULL);
if (!pool) {
return;
}
(*env)->GetIntArrayRegion(env, pixels, 0, pixelCount, shmPixels);
#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
// Wayland requires little-endian data
for (size_t i = 0; i < pixelCount; i++) {
uint32_t value = (uint32_t)shmPixels[i];
shmPixels[i] = (jint)((value & 0xFFU) << 24 |
(value & 0xFF00U) << 8 |
(value & 0xFF0000U) >> 8 |
(value & 0xFF000000U) >> 24 & 0xFFU);
}
#endif
source->dragIconBuffer = wl_shm_pool_create_buffer(pool, 0, width, height, width * 4, WL_SHM_FORMAT_ARGB8888);
wl_shm_pool_destroy(pool);
if (!source->dragIconBuffer) {
return;
}
source->dragIcon = wl_compositor_create_surface(wl_compositor);
if (!source->dragIcon) {
wl_buffer_destroy(source->dragIconBuffer);
source->dragIconBuffer = NULL;
return;
}
#if WL_SURFACE_OFFSET_SINCE_VERSION >= 5
int wl_compositor_version = wl_compositor_get_version(wl_compositor);
if (wl_compositor_version >= 5) {
wl_surface_attach(source->dragIcon, source->dragIconBuffer, 0, 0);
wl_surface_offset(source->dragIcon, offsetX, offsetY);
} else {
wl_surface_attach(source->dragIcon, source->dragIconBuffer, offsetX, offsetY);
}
#else
wl_surface_attach(source->dragIcon, source->dragIconBuffer, offsetX, offsetY);
#endif
wl_surface_damage_buffer(source->dragIcon, 0, 0, width, height);
// NOTE: we still need to commit the surface, this is done immediately after start_drag
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_WLDataOffer_destroyImpl(JNIEnv *env, jclass clazz, jlong nativePtr)
{

View File

@@ -536,7 +536,12 @@ registry_global(void *data, struct wl_registry *wl_registry,
if (strcmp(interface, wl_shm_interface.name) == 0) {
wl_shm = wl_registry_bind( wl_registry, name, &wl_shm_interface, 1);
} else if (strcmp(interface, wl_compositor_interface.name) == 0) {
wl_compositor = wl_registry_bind(wl_registry, name, &wl_compositor_interface, 4);
#if WL_SURFACE_OFFSET_SINCE_VERSION >= 5
uint32_t chosen_version = (version >= 5) ? 5 : 4;
#else
uint32_t chosen_version = 4;
#endif
wl_compositor = wl_registry_bind(wl_registry, name, &wl_compositor_interface, chosen_version);
} else if (strcmp(interface, wl_subcompositor_interface.name) == 0) {
wl_subcompositor = wl_registry_bind(wl_registry, name, &wl_subcompositor_interface, 1);
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {

View File

@@ -0,0 +1,173 @@
/*
* 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

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

View File

@@ -0,0 +1,272 @@
/*
* 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

@@ -0,0 +1,83 @@
/*
* 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

@@ -50,6 +50,7 @@
#include "awt_Win32GraphicsDevice.h"
#include "Hashtable.h"
#include "ComCtl32Util.h"
#include "AccessibleCaret.h"
#include <Region.h>
@@ -2037,6 +2038,32 @@ 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;
}
}
@@ -2450,7 +2477,7 @@ void AwtComponent::WmTouchHandler(const TOUCHINPUT& touchInput)
const jint scrollModifiers = modifiers & ~java_awt_event_InputEvent_SHIFT_DOWN_MASK;
SendMouseWheelEventFromTouch(p, scrollModifiers, sun_awt_event_TouchEvent_TOUCH_UPDATE, deltaY);
}
const jint deltaX = ScaleDownX(static_cast<int>(m_lastTouchPoint.x - p.x));
if (deltaX != 0) {
const jint scrollModifiers = modifiers | java_awt_event_InputEvent_SHIFT_DOWN_MASK;

View File

@@ -63,8 +63,10 @@ public abstract class HotSpotVirtualMachine extends VirtualMachine {
static {
String s = VM.getSavedProperty("jdk.attach.allowAttachSelf");
ALLOW_ATTACH_SELF = "".equals(s) || Boolean.parseBoolean(s);
// For now the default is false because it makes jstack hang with
// buffer overflow on lengthy outputs, which occur often in automatic tests.
String s2 = VM.getSavedProperty("jdk.attach.allowStreamingOutput");
ALLOW_STREAMING_OUTPUT = !("false".equals(s2));
ALLOW_STREAMING_OUTPUT = "".equals(s2) || Boolean.parseBoolean(s2);
}
private final boolean selfAttach;

View File

@@ -89,9 +89,12 @@ vmTestbase/jit/misctests/JitBug1/JitBug1.java JBR-8801 linux-aarch64,windows-all
vmTestbase/jit/misctests/Pi/Pi.java JBR-8854 linux-aarch64
vmTestbase/jit/misctests/t5/t5.java JBR-8854 linux-aarch64
vmTestbase/jit/misctests/putfield00802/putfield00802.java JBR-8801 linux-aarch64,windows-all
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi002/TestDescription.java JBR-8743 windows-all
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi003/TestDescription.java JBR-8743 windows-all
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi004/TestDescription.java JBR-8743,JBR-8744 windows-all
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi001/Multi001.java JBR-8545 windows-aarch64,windows-x64
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi002/TestDescription.java JBR-8927,JBR-8545 windows-aarch64,windows-x64
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi003/TestDescription.java JBR-8927,JBR-8545 windows-aarch64,windows-x64
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi004/TestDescription.java JBR-8927,JBR-8545 windows-aarch64,windows-x64
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi005/TestDescription.java JBR-8927,JBR-8545 windows-aarch64,windows-x64
#############################################################################

View File

@@ -345,11 +345,10 @@ vmTestbase/vm/mlvm/meth/stress/compiler/deoptimize/Test.java#id1 8324756 generic
vmTestbase/nsk/jdwp/ThreadReference/ForceEarlyReturn/forceEarlyReturn001/forceEarlyReturn001.java 7199837 generic-all
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi001/Multi001.java JBR-8545, windows-all
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi002/TestDescription.java JBR-8743 windows-all
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi003/TestDescription.java JBR-8743 windows-all
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi004/TestDescription.java JBR-8743 windows-all
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi005/TestDescription.java 8076494,JBR-8927 windows-x64,windows-aarch64
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi001/Multi001.java JBR-8545 windows-aarch64
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi002/TestDescription.java JBR-8927,JBR-8545 windows-aarch64
vmTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi003/TestDescription.java JBR-8927,JBR-8545 windows-aarch64
mTestbase/nsk/monitoring/ThreadMXBean/ThreadInfo/Multi/Multi005/TestDescription.java JBR-8927,JBR-8545 windows-aarch64
vmTestbase/nsk/monitoring/ThreadMXBean/findMonitorDeadlockedThreads/find006/TestDescription.java 8310144 macosx-aarch64

View File

@@ -447,14 +447,8 @@ jdk_editpad = \
jbr_all = \
jb \
-jb/java/awt/wayland/VulkanBlitTest.java \
-jb/java/awt/wayland/RobotGetPixelsTest.java \
-jb/java/awt/wayland/ImageTransformTest.java \
-jb/java/awt/wayland/VulkanGCCompatibilityTest.java \
-jb/java/awt/wayland/RobotGetPixelTest.java \
-jb/java/awt/wayland/RobotGetOOBPixelsTest.java \
-jb/java/awt/wayland/VulkanCompositeTest.java \
-jb/java/awt/wayland/VulkanMaskFillTest.java
-jb/java/awt/vulkan \
-jb/java/awt/wayland/vulkan \
jdk_desktop = \
:jdk_desktop_part1 \
@@ -823,6 +817,7 @@ jdk_since_checks = \
jdk_awt_wayland = \
:jdk_awt \
jb/java/awt/wayland \
-jb/java/awt/wayland/vulkan \
jb/java/awt/Focus/ActivateAfterHide.java \
-com/apple/eawt \
-com/apple/laf \

View File

@@ -35,6 +35,7 @@ public class DetectingOSThemeTest {
private static final String LIGHT_THEME_NAME = "Light";
private static final String DARK_THEME_NAME = "Dark";
private static final String UNDEFINED_THEME_NAME = "Undefined";
private static boolean isKDE = false;
private static String currentTheme() {
Boolean val = (Boolean) Toolkit.getDefaultToolkit().getDesktopProperty("awt.os.theme.isDark");
@@ -44,8 +45,14 @@ public class DetectingOSThemeTest {
return (val) ? DARK_THEME_NAME : LIGHT_THEME_NAME;
}
private static void setOsDarkTheme(String val) {
try {
private static void setOsDarkTheme(String val) throws Exception {
if (isKDE) {
if (val.equals(DARK_THEME_NAME)) {
Runtime.getRuntime().exec("plasma-apply-colorscheme BreezeDark");
} else {
Runtime.getRuntime().exec("plasma-apply-colorscheme BreezeLight");
}
} else {
if (val.equals(DARK_THEME_NAME)) {
Runtime.getRuntime().exec("gsettings set org.gnome.desktop.interface gtk-theme 'Adwaita-dark'");
Runtime.getRuntime().exec("gsettings set org.gnome.desktop.interface color-scheme 'prefer-dark'");
@@ -53,14 +60,13 @@ public class DetectingOSThemeTest {
Runtime.getRuntime().exec("gsettings set org.gnome.desktop.interface gtk-theme 'Adwaita'");
Runtime.getRuntime().exec("gsettings set org.gnome.desktop.interface color-scheme 'default'");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static String currentTheme = null;
public static void main(String[] args) throws Exception {
isKDE = "KDE".equals(System.getenv("XDG_CURRENT_DESKTOP"));
currentTheme = currentTheme();
if (currentTheme.equals(UNDEFINED_THEME_NAME)) {
throw new RuntimeException("Test Failed! Cannot detect current OS theme");

View File

@@ -0,0 +1,65 @@
/*
* 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.
*
* 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.
*/
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import javax.swing.JFrame;
import javax.swing.JLabel;
/**
* @test
* @summary Verifies that a small window does not generate GTK warnings in Wayland
* @requires os.family == "linux"
* @key headful
* @library /test/lib
* @run main WLFrameMinSize
*/
public class WLFrameMinSize {
private static JFrame frame;
public static void main(String[] args) throws Exception {
if (args.length > 0 && args[0].equals("--test")) {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
frame = new JFrame("WLFrameMinSize");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new JLabel("a"));
frame.pack();
frame.setVisible(true);
}
});
Thread.sleep(2000);
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
frame.dispose();
}
});
} else {
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
WLFrameMinSize.class.getName(),
"--test");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldNotContain("Gtk-WARNING");
}
}
}

View File

@@ -36,6 +36,7 @@ java/awt/Window/GrabSequence/GrabSequence.java 6848409,JBR-8584 macosx-all,linux
javax/accessibility/JFileChooserAccessibleDescriptionTest.java JBR-5397,JBR-7379 macosx-all,windows-x64
javax/accessibility/SlowPanelIteration/SlowPanelIteration.java JBR-870,JBR-5397 windows-x64,linux-x64,macosx-all
javax/swing/event/FocusEventCauseTest.java JBR-7381 windows-x64
javax/swing/JButton/4796987/bug4796987.java JBR-9345 windows-x64
javax/swing/JButton/8151303/PressedIconTest.java JBR-5210,JBR-5510,JBR-5397 windows-all,linux-all,macosx-all
javax/swing/JComboBox/4743225/bug4743225.java JBR-5210,JBR-5397 windows-all,macosx-all
javax/swing/JComboBox/6559152/bug6559152.java JBR-5397,JBR-7382 macosx-all,windows-x64
@@ -57,9 +58,10 @@ javax/swing/text/StyledEditorKit/4506788/bug4506788.java JBR-7386 windows-x64
jb/java/awt/CustomTitleBar/DialogNativeControlsTest.java JBR-9083 windows-x64
jb/java/awt/Desktop/AboutHandlerTest.java nobug macosx-all,linux-all,windows-all
jb/java/awt/Focus/FrameAfterPopup.java JBR-9161 windows-x64
jb/java/awt/Focus/NewFrameAfterDialogTest.java JBR-7387 windows-x64
jb/java/awt/Focus/ChainOfPopupsFocusTest.java JBR-8407 windows-x64
jb/java/awt/Focus/FrameAfterPopup.java JBR-9161 windows-x64
jb/java/awt/Focus/MaximizedCustomDecorationsTest.java JBR-9346 windows-x64
jb/java/awt/Focus/NewFrameAfterDialogTest.java JBR-7387 windows-x64
# VoiceOver on MacOS
java/awt/event/KeyEvent/ExtendedModifiersTest/ExtendedModifiersTest.java JBR-5397 macosx-all

View File

@@ -115,12 +115,13 @@ java/awt/dnd/RemoveDropTargetCrashTest/RemoveDropTargetCrashTest.java JBR-8960 w
java/awt/Focus/ModalDialogInFocusEventTest.java JBR-7818 linux-6.14.0-1010-aws
java/awt/Frame/GetGraphicsStressTest/GetGraphicsStressTest.java JBR-8542 windows-all timeout
java/awt/Graphics2D/TextPerf.java JBR-8541 linux-all,windows-all
java/awt/GridBagLayout/ComponentShortage.java JBR-9347 macosx-x64
java/awt/KeyboardFocusmanager/TypeAhead/EnqueueWithDialogTest/EnqueueWithDialogTest.java JBR-7077 linux-all,windows-all
java/awt/MenuItem/EnableTest.java JBR-8543 windows-all timeout
java/awt/Robot/NonEmptyErrorStream.java JBR-6275 macosx-all
java/awt/Robot/NonEmptyErrorStream.java 8340330,JBR-5510,JBR-6275,JBR-8299,JBR-9259 macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-15.6,macosx-all,linux-5.18.2-arch1-1,linux-aarch64,linux-6.15.8-100.fc41.x86_64
javax/swing/JWindow/ShapedAndTranslucentWindows/PerPixelTranslucentCanvas.java JBR-7404 macosx-all
javax/swing/plaf/synth/7158712/bug7158712.java 8322653,JBR-9061 macosx-all,linux-6.8.0-1033-aws
javax/swing/plaf/synth/7158712/bug7158712.java 8322653,JBR-9061 macosx-all,linux-6.8.0-1033-aws,linux-6.8.0-1036-aws
javax/swing/UI/UnninstallUIMemoryLeaks/UnninstallUIMemoryLeaks.java JBR-5952,8340330,JBR-6274 windows-all,macosx-15.3,macosx-15.3.1,macosx-15.3.2,macosx-15.4,macosx-15.4.1,macosx-15.5,macosx-all
jb/build/ResolveSymbolsTest/ResolveSymbolsRealEnv.java JBR-8544 linux-all

View File

@@ -24,6 +24,7 @@ java/awt/font/TextLayout/VisibleAdvance.java JBR-8766 linux-all
java/awt/FontMetrics/ExtremeFontSizeTest.java JBR-7823 linux-all
java/awt/Graphics2D/DrawString/IgnoredWhitespaceTest.java JBR-9086 linux-all
java/awt/Graphics2D/DrawString/RotTransText.java JBR-8767 linux-all
java/awt/Graphics/NativeWin32Clear.java JBR-8689 linux-x64
java/awt/Graphics/XORPaint.java#id2 JBR-7373 linux-x64
java/awt/image/DrawImage/SimpleManagedImage.java JBR-8769 linux-all
java/awt/image/DrawImage/SimpleUnmanagedImage.java JBR-8769 linux-all

View File

@@ -134,6 +134,7 @@ java/awt/Focus/FocusTraversalPolicy/ButtonGroupLayoutTraversal/ButtonGroupLayout
java/awt/Focus/FrameMinimizeTest/FrameMinimizeTest.java 8016266 linux-all
java/awt/Focus/IconifiedFrameFocusChangeTest/IconifiedFrameFocusChangeTest.java 6849364 generic-all
java/awt/Focus/InactiveFocusRace.java 8023263,JBR-8586 linux-all,windows-x64
java/awt/Focus/InputVerifierTest3/InputVerifierTest3.java JBR-7311 linux-6.8.0-1036-aws
java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusToFrontTest.java 6848406 generic-all
java/awt/Focus/AutoRequestFocusTest/AutoRequestFocusSetVisibleTest.java 6848407 generic-all
java/awt/Focus/LabelScrollBarFocus.java JBR-8027 linux-all
@@ -353,14 +354,9 @@ java/awt/Window/Grab/GrabTest.java 8253184 windows-all
java/awt/Window/GrabSequence/GrabSequence.java 6848409 macosx-all,linux-all
java/awt/Window/LocationAtScreenCorner/LocationAtScreenCorner.java 8203371 linux-all
java/awt/FontClass/FontAccess.java JBR-5225 windows-all
java/awt/font/ComplexEmoji.java JBR-5009 linux-aarch64
java/awt/font/Emoji/ComplexEmoji.java JBR-8282 generic-all
java/awt/font/Emoji/EmojiVariation.java JBR-8716 linux-5.18.2-arch1-1
java/awt/font/EmojiVariation.java JBR-5009,JBR-5510 linux-aarch64,linux-5.18.2-arch1-1
java/awt/font/FontScaling/StretchedFontTest.java JBR-6225 macosx-all
java/awt/font/GlyphVector/LayoutCompatTest.java JBR-8437 linux-aarch64
java/awt/font/JNICheck/JNICheck.sh JBR-5011 linux-all
java/awt/font/OutlineTextRendererEmoji.java JBR-5009 linux-aarch64
java/awt/font/TextLayout/ArabicDiacriticTest.java JBR-6760 linux-i386
java/awt/font/TextLayout/DecorationBoundsTest.java JBR-6760 linux-i386
java/awt/font/TextLayout/FormatCharAdvanceTest.java JBR-8289 macosx-all
@@ -397,7 +393,7 @@ sun/java2d/loops/XORClearRect.java JBR-8718 linux-5.18.2-arch1-1
java/awt/Graphics/GDIResourceExhaustionTest.java JBR-8719 linux-5.18.2-arch1-1
java/awt/Graphics/NativeWin32Clear.java JBR-8689 linux-aarch64,linux-5.18.2-arch1-1
java/awt/Graphics/XORPaint.java#id1 JBR-8642 macosx-aarch64
java/awt/Graphics/XORPaint.java#id2 JBR-5510 linux-5.18.2-arch1-1
java/awt/Graphics/XORPaint.java#id2 JBR-9348 linux-6.15.8-100.fc41.x86_64
java/awt/Graphics2D/CopyAreaOOB.java JBR-5354,JBR-9206 macosx-all,windows-all,linux-5.18.2-arch1-1,linux-6.15.8-100.fc41.x86_64
java/awt/Graphics2D/DrawString/DisposerTest.java JBR-5010,JBR-5510 linux-aarch64,linux-5.18.2-arch1-1
java/awt/Graphics2D/DrawString/DrawRotatedStringUsingRotatedFont.java 8266283 generic-all
@@ -903,6 +899,7 @@ java/nio/channels/DatagramChannel/MulticastSendReceiveTests.java 8144003,JBR-921
java/nio/channels/DatagramChannel/Promiscuous.java 8144003,JBR-9218 macosx-all,linux-6.15.8-100.fc41.x86_64
java/nio/channels/DatagramChannel/PromiscuousIPv6.java JBR-8828,JBR-9218 macosx-x64,linux-6.15.8-100.fc41.x86_64
java/nio/channels/DatagramChannel/Unref.java 8233437 generic-all
java/nio/channels/FileChannel/LargeGatheringWrite.java JBR-9316 linux-6.15.8-100.fc41.x86_64
java/nio/channels/Selector/LotsOfInterrupts.java#virtual JBR-8940 windows-aarch64
java/nio/channels/Selector/Wakeup.java 6963118 windows-all

View File

@@ -8,6 +8,7 @@ java/awt/dnd/DnDAWTLockTest.java JBR-8745 linux-all
java/awt/dnd/DragOverDropTargetPerformanceTest.java JBR-5799 windows-all
java/awt/dnd/DragSourceGCrashTest.java JBR-8745 linux-all
java/awt/dnd/InterJVMGetDropSuccessTest/InterJVMGetDropSuccessTest.java JBR-8745 linux-all
java/awt/dnd/InterJVMLinkTest.java JBR-9255 linux-6.15.8-100.fc41.x86_64
java/awt/Dialog/ChoiceModalDialogTest.java JBR-8724 windows-all
java/awt/dnd/MozillaDnDTest.java JBR-6442 linux-all
java/awt/event/ComponentEvent/TextComponentTextEventTest.java JBR-6287 windows-all
@@ -67,7 +68,7 @@ javax/swing/JButton/bug4490179.java JBR-8925 windows-all
javax/swing/JFileChooser/JFileChooserSetLocationTest.java JBR-8098 linux-all,windows-all
javax/swing/JInternalFrame/4202966/IntFrameCoord.java JBR-9006 window-all
javax/swing/JMenu/bug4342646.java JBR-8727 linux-all,windows-all
javax/swing/JPopupMenu/6580930/bug6580930.java JBR-5071 linux-6.8.0-1033-aws
javax/swing/JPopupMenu/6580930/bug6580930.java JBR-5071 linux-6.8.0-1033-aws,linux-6.8.0-1036-aws,linux-6.15.8-100.fc41.x86_64
javax/swing/JTable/JTableRightOrientationTest.java JBR-8102 linux-all,windows-all
javax/swing/text/ParagraphView/6364882/bug6364882.java JBR-8747 linux-all
javax/swing/plaf/basic/BasicGraphicsUtils/8132119/bug8132119.java JBR-8357 linux-all
@@ -86,6 +87,7 @@ jdk/editpad/EditPadTest.java
sanity/client/SwingSet/src/ColorChooserDemoTest.java JBR-8354,JBR-8933 linux-all,windows-aarch64,windows-10.0
sanity/client/SwingSet/src/EditorPaneDemoTest.java JBR-5510,JBR-6285 linux-5.18.2-arch1-1,linux-all
sanity/client/SwingSet/src/GridBagLayoutDemoTest.java JBR-6285 linux-all
sanity/client/SwingSet/src/InternalFrameDemoTest.java JBR-6685 linux-all
sanity/client/SwingSet/src/ToolTipDemoTest.java 8293001,JBR-6293 linux-all,windows-all
sun/java2d/GdiRendering/ClipShapeRendering.java JBR-5204 linux-all,macosx-all,windows-all
@@ -110,7 +112,7 @@ java/awt/Frame/ShapeNotSetSometimes/ShapeNotSetSometimes.java nobug generic-all
java/awt/Graphics/CopyScaledArea/CopyScaledAreaTest.java nobug generic-all
java/awt/Graphics/DrawOvalTest.java nobug generic-all
java/awt/Graphics/XORPaint.java#id1 JBR-8642,nobug macosx-aarch64,windows-all
java/awt/Graphics/XORPaint.java#id2 JBR-5510,nobug linux-5.18.2-arch1-1,windows-all
java/awt/Graphics/XORPaint.java#id2 nobug generic-all
java/awt/Graphics/XORPaint.java#id3 nobug windows-all
java/awt/Graphics/XORPaint.java#id4 nobug windows-all
java/awt/Graphics/XORPaint.java#id5 nobug windows-all
@@ -177,6 +179,7 @@ javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java#id0 JBR-7965,JBR-8345 w
javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java#id2 JBR-8345 linux-all
javax/swing/JButton/TestGlyphBreak.java nobug generic-all
javax/swing/JComboBox/6559152/bug6559152.java JBR-8964 windows-all
javax/swing/JComboBox/8033069/bug8033069NoScrollBar.java JBR-9342 windows-x64
javax/swing/JComboBox/DisabledComboBoxFontTestAuto.java nobug generic-all
javax/swing/JFrame/8175301/ScaledFrameBackgroundTest.java nobug generic-all
javax/swing/JInternalFrame/8069348/bug8069348.java nobug generic-all

View File

@@ -22,9 +22,13 @@ java/awt/Toolkit/ToolkitPropertyTest/ToolkitPropertyTest_Disable.java JBR-9100 l
javax/swing/AbstractButton/bug4147740.java JBR-9100 linux-all
javax/swing/AbstractButton/bug4246045.java JBR-9100 linux-all
javax/swing/border/Test6981576.java JBR-9100 linux-all
java/awt/image/DrawImage/BlitRotateClippedArea.java JBR-9026 linux-all
java/awt/image/DrawImage/EABlitTest.java JBR-9027 linux-all
javax/swing/GraphicsConfigNotifier/StalePreferredSize.java JBR-9031 linux-all
javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java JBR-8266 linux-x64
javax/swing/GroupLayout/8079640/bug8079640.java JBR-9100 linux-all
javax/swing/JButton/JButtonPaintNPE/JButtonPaintNPE.java JBR-9100 linux-all
javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java#id0 JBR-7928,JBR-9341 linux-6.8.0-1036-aws,linux-6.14.9-arch1-1
javax/swing/JColorChooser/Test4177735.java JBR-9100 linux-all
javax/swing/JComboBox/8019180/Test8019180.java JBR-9100 linux-all
javax/swing/JComboBox/bug4276920.java JBR-9100 linux-all
@@ -39,6 +43,7 @@ javax/swing/JFileChooser/6489130/bug6489130.java JBR-9100 linux-all
javax/swing/JFileChooser/DeserializedJFileChooser/DeserializedJFileChooserTest.java JBR-9100 linux-all
javax/swing/JFileChooser/FileSizeCheck.java JBR-9100 linux-all
javax/swing/JFileChooser/FileViewNPETest.java JBR-9100 linux-all
javax/swing/JFormattedTextField/bug4741926.java JBR-9321 linux-6.14.9-arch1-1
javax/swing/JFormattedTextField/TestSelectedTextBackgroundColor.java JBR-9100 linux-all
javax/swing/JFrame/AlwaysOnTop/AlwaysOnTopImeTest.java JBR-9100 linux-all
javax/swing/JFrame/HangNonVolatileBuffer/HangNonVolatileBuffer.java JBR-9100 linux-all
@@ -48,7 +53,7 @@ javax/swing/JLayer/6824395/bug6824395.java JBR-9100 linux-all
javax/swing/JLayer/8041982/bug8041982.java JBR-9100 linux-all
javax/swing/JMenu/8178430/LabelDotTest.java JBR-9100 linux-all
javax/swing/JPanel/bug4907772.java JBR-9100 linux-all
javax/swing/JPasswordField/TestSelectedTextBackgroundColor.java JBR-9100 linux-all
javax/swing/JPasswordField/TestSelectedTextBackgroundColor.java JBR-9277 linux-6.14.9-arch1-1
javax/swing/JProgressBar/8161664/ProgressBarMemoryLeakTest.java JBR-9100 linux-all
javax/swing/JProgressBar/TestJProgressBarHighlightColor.java JBR-9100 linux-all
javax/swing/JScrollPane/bug8044371.java JBR-9100 linux-all
@@ -57,7 +62,7 @@ javax/swing/JSlider/TestJSliderRendering.java JBR-9100 linux-all
javax/swing/JSpinner/8008657/bug8008657.java JBR-9100 linux-all
javax/swing/JSpinner/bug4656590.java JBR-9100 linux-all
javax/swing/JSpinner/bug4680204.java JBR-9100 linux-all
javax/swing/JSpinner/TestSelectedTextBackgroundColor.java JBR-9100 linux-all
javax/swing/JSpinner/TestSelectedTextBackgroundColor.java JBR-9277 linux-6.14.9-arch1-1
javax/swing/JSplitPane/4816114/bug4816114.java JBR-9100 linux-all
javax/swing/JSplitPane/JSplitPaneTestNegDivSize.java JBR-9100 linux-all
javax/swing/JTabbedPane/7170310/bug7170310.java JBR-9100 linux-all
@@ -78,6 +83,7 @@ javax/swing/JTextPane/JTextPaneDocumentWrapping.java JBR-9100 linux-all
javax/swing/JTextPane/TestJTextPaneBackgroundColor.java JBR-9100 linux-all
javax/swing/JTree/8041705/DefaultTreeCellRendererBorderTest.java JBR-9100 linux-all
javax/swing/JViewport/7107099/bug7107099.java JBR-9100 linux-all
javax/swing/InputVerifier/VerifyTarget/VerifyTargetTest.java JBR-9320 linux-6.14.9-arch1-1
javax/swing/LookAndFeel/8145547/DemandGTK.java JBR-9100 linux-all
javax/swing/LookAndFeel/8145547/DemandGTK3.sh JBR-9100 linux-all
javax/swing/plaf/basic/6866751/bug6866751.java JBR-9100 linux-all

View File

@@ -1,2 +1,9 @@
javax/swing/JFileChooser/6520101/bug6520101.java JBR-8434 linux-all
javax/swing/JMenu/bug4342646.java JBR-8727 linux-all
javax/swing/JTable/JTableRightOrientationTest.java JBR-8102 linux-all
javax/swing/plaf/basic/BasicGraphicsUtils/8132119/bug8132119.java JBR-8357 linux-all
javax/swing/plaf/basic/BasicGraphicsUtils/8132119/bug8132119.java JBR-8357 linux-all
jb/java/awt/wayland/vulkan/ImageTransformTest.java JBR-8434 linux-all
jb/java/awt/wayland/vulkan/RobotGetPixelsTest.java JBR-8434 linux-all
jb/java/awt/wayland/vulkan/RobotGetPixelTest.java JBR-8434 linux-all
jb/java/awt/wayland/vulkan/VulkanMaskFillTest.java JBR-8434 linux-all

View File

@@ -7,18 +7,18 @@ javax/swing/AbstractDocument/bug4549069.java JBR-8267 linux-6.14.0-1010-aws
javax/swing/JComponent/6989617/bug6989617.java JBR-8796 linux-6.14.0-1010-aws
javax/swing/JDesktopPane/TestDesktopManagerNPE.java JBR-8449 linux-x64
javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java JBR-8266 linux-x64
javax/swing/InputVerifier/VerifyTarget/VerifyTargetTest.java JBR-7520 linux-all
javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java#id0 JBR-7928,JBR-8265 linux-all
javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java#id2 JBR-8265 linux-all
javax/swing/InputVerifier/VerifyTarget/VerifyTargetTest.java JBR-7520,JBR-9320 linux-6.8.0-1036-aws,linux-6.14.9-arch1-1
javax/swing/JButton/SwingButtonResizeTestWithOpenGL.java#id0 JBR-7928,JBR-9341 linux-6.8.0-1036-aws,linux-6.14.9-arch1-1
javax/swing/JEditorPane/bug4325606.java JBR-8267 linux-aarch64
javax/swing/JEditorPane/JEditorPaneFontFallback.java JBR-8305 linux-all
javax/swing/JFormattedTextField/bug4741926.java JBR-7530 linux-all
javax/swing/JFormattedTextField/bug4741926.java JBR-7530,JBR-9321 linux-all,linux-6.14.9-arch1-1
javax/swing/JFormattedTextField/TestSelectedTextBackgroundColor.java JBR-8790 linux-all
javax/swing/JInternalFrame/bug5009724.java JBR-7087 linux-all
javax/swing/JMenu/JMenuSelectedColorTest.java JBR-8551 linux-all
javax/swing/JPasswordField/TestSelectedTextBackgroundColor.java JBR-9277 linux-6.14.9-arch1-1
javax/swing/JPopupMenu/7156657/bug7156657.java JBR-8210 linux-all
javax/swing/JRadioButton/bug4823809.java JBR-7774 linux-all
javax/swing/JSlider/TestJSliderRendering.java JBR-8551 linux-all
javax/swing/JSpinner/TestSelectedTextBackgroundColor.java JBR-9277 linux-6.14.9-arch1-1
javax/swing/JTabbedPane/6495408/bug6495408.java JBR-8267 linux-aarch64
javax/swing/JToolTip/TestTooltipBackgroundColor.java JBR-8551 linux-all
javax/swing/LookAndFeel/8145547/DemandGTK3.sh JBR-8274 linux-all
@@ -27,4 +27,4 @@ javax/swing/text/ParagraphView/6364882/bug6364882.java JBR-8324 linux-all
javax/swing/UI/UnninstallUIMemoryLeaks/UnninstallUIMemoryLeaks.java JBR-8304 linux-all
jb/java/awt/wayland/VulkanMaskFillTest.java JBR-8446 linux-6.8.0-1017-raspi
jb/javax/swing/Popup/WLPopupMoves.java JBR-8296 linux-all
jb/javax/swing/wayland/WLPopupMoves.java JBR-8296 linux-all

View File

@@ -1,2 +1,4 @@
javax/swing/JFileChooser/6520101/bug6520101.java JBR-7140 linux-all
javax/swing/JMenu/bug4342646.java JBR-8727 linux-all
javax/swing/JTable/JTableRightOrientationTest.java JBR-8102 linux-all
javax/swing/plaf/basic/BasicGraphicsUtils/8132119/bug8132119.java JBR-8357 linux-all