Compare commits

...

24 Commits
1993 ... 2074

Author SHA1 Message Date
Dmitry Batrak
363650bbf4 JBR-4281 Window losing focus isn't detected in some cases on macOS 2022-02-24 11:49:29 +03:00
Mikhail Grishchenko
420865b278 JBR-3975 actualize jbrsdk filename pattern to JBR 11 2022-02-22 20:13:43 +07:00
Mikhail Grishchenko
f60792e700 update jtreg exclude list
Exclude from regular runs:
java/awt/Choice/PopdownGeneratesMouseEvents/PopdownGeneratesMouseEvents.java
java/awt/MenuBar/8007006/bug8007006.java
2022-02-22 20:04:53 +07:00
Nikita Gubarkov
46829b3102 JBR API v1.1.2 - Ignore unsupported window peers for CustomWindowDecoration client-side fallback 2022-02-22 10:59:18 +03:00
Nikita Provotorov
dda07e42db JBR-2226: Typing non-accent character after a backtick (dead key) results in two backticks typed on macOS.
Fixing only a couple of keyboard layouts.
2022-02-22 09:16:16 +07:00
Anton Tarasov
fc1fc0fd8e JBR-4119 [update_1] UI freezes at sun.lwawt.macosx.CAccessibility.getChildrenAndRoles 2022-02-21 19:22:22 +03:00
Anton Tarasov
814f267c04 JBR-4106 [update_1] PyCharm hangs with 100% CPU usage on one core 2022-02-21 16:34:24 +03:00
Anton Tarasov
6ba79774d8 JBR-4119 UI freezes at sun.lwawt.macosx.CAccessibility.getChildrenAndRoles 2022-02-21 16:34:24 +03:00
Nikita Gubarkov
c63802fe59 Added internal services to JBR API 2022-02-21 15:15:15 +03:00
Maxim Kartashev
37975e6b67 fixup! JBR-4259 jb/build/CheckJBRModules.java fails on MacOS M1 2022-02-21 11:12:16 +03:00
Maxim Kartashev
24ccad3a09 JBR-4259 jb/build/CheckJBRModules.java fails on MacOS M1 2022-02-21 10:28:15 +03:00
Mikhail Grishchenko
b146b6d64e JBR-3975 added script comparing jbr size 2022-02-21 05:15:43 +07:00
Ivan Lopatin
42c3f9235d Backporting JDK-8211999: Window positioning bugs due to overlapping GraphicsDevice bounds (Windows/HiDPI)
Reviewed-by: kizune, aivanov

Reverted following changes:
JRE-119 [Mind device scale when changing window native bounds]
JRE-119 [Handle WM_DPICHANGED with appropriate re-size]
JRE-153 [hidpi] monitors bounds can overlap in multi-dpi env
JRE-660 [windows] display DPI change damage UI
JRE-742 [windows] IME candidate window position is wrong on HiDPI displa
JRE-765 [win] window dragged to another display is not resized
JRE-961 Windows size and position are not preserved on jdk 9
JRE-1172 [cleanup]
JRE-1172 forwardport: JRE-1163[IDEA-187439] Modal dialogs (e.g commit dialog or diff from it) are sometimes invisible
JBR-2687: Fixed problems with maximizing to a new screen
JBR-4095: Fixed monitors bounds

Fixes JRE-119, JRE-153, JRE-660, JRE-742, JRE-765, JRE-961, JRE-1172, JBR-2687, JBR-4095
2022-02-19 01:43:07 +07:00
Maxim Kartashev
f9483505d9 fixup! fixup! JBR-3917 Problem using windows certificate store (trustStoreType=Windows-ROOT not recognized) 2022-02-18 16:24:32 +03:00
Goetz Lindenmaier
37d2b153a6 8218546: Unable to connect to https://google.com using java.net.HttpClient
Reviewed-by: clanger
Backport-of: b240c60427
2022-02-18 00:23:58 +03:00
Maxim Kartashev
21a4de02a2 Drop build/ from .gitignore in favor of /build/
'/build/' filters out build artifacts, while 'build/' is interpreted as
a path component at any level and interferes with, for instance,
tests under 'test/jdk/jb/build/'.
2022-02-17 17:10:41 +03:00
Anton Tarasov
a0da2bd267 JBR-4106 PyCharm hangs with 100% CPU usage on one core 2022-02-17 16:46:48 +03:00
Maxim Kartashev
4fd22dd235 fixup! fixup! JBR-3917 Problem using windows certificate store (trustStoreType=Windows-ROOT not recognized) 2022-02-17 15:17:01 +03:00
Maxim Kartashev
ce379a03cd fixup! JBR-3917 Problem using windows certificate store (trustStoreType=Windows-ROOT not recognized) 2022-02-17 13:17:32 +03:00
Maxim Kartashev
da41adf366 JBR-3917 Problem using windows certificate store (trustStoreType=Windows-ROOT not recognized) 2022-02-17 12:31:04 +03:00
Nikita Gubarkov
c7d8fa3914 JBR API v1.1.1 - Implemented client-side fallbacks for JBR API 2022-02-16 16:58:39 +03:00
Anton Tarasov
18e0b4fa5c JBR-4208 [followup] LWCToolkit.invokeAndWait should not stuck on invocation loss
Changed the custom log handler in the test to:
 a) merge streams
 b) simplify logging settings

Fixed warnings.
2022-02-16 12:11:55 +03:00
Anton Tarasov
025e3f0f76 JBR-4208 LWCToolkit.invokeAndWait should not stuck on invocation loss 2022-02-15 18:57:47 +03:00
Nikita Gubarkov
e068de8629 JBR API v1.1.0
JBR-4228 report HTMINBUTTON, HTMAXBUTTON, HTCLOSE, HTMENU, HTCAPTION targets via JBR API & handle corresponding non-client mouse events for windows with custom decorations
2022-02-14 21:56:22 +03:00
82 changed files with 3001 additions and 1201 deletions

1
.gitignore vendored
View File

@@ -6,7 +6,6 @@ JTreport
.idea/modules.xml
.idea/shelf/
JetBrainsRuntime.iml
build/
# Project exclude paths
/jb/project/java-gradle/.gradle/
/build/

View File

@@ -8,7 +8,7 @@ HEAD_REVISION=0
function do_exit() {
exit_code=$1
[ $do_reset_changes -eq 1 ] && git checkout HEAD modules.list src/java.desktop/share/classes/module-info.java
[ $do_reset_changes -eq 1 ] && git checkout HEAD jb/project/tools/common/modules.list src/java.desktop/share/classes/module-info.java
if [ $do_reset_dcevm -eq 1 ]; then
[ ! -z $HEAD_REVISION ] && git reset --hard $HEAD_REVISION
fi

View File

@@ -82,7 +82,7 @@ JBR_BASE_NAME=jbr-$JBSDK_VERSION
rm -rf $BASE_DIR/$JBR_BUNDLE
JBR=$JBR_BASE_NAME-linux-aarch64-b$build_number
grep -v javafx modules.list | grep -v "jdk.internal.vm\|jdk.aot\|jcef" > modules.list.aarch64
grep -v javafx jb/project/tools/common/modules.list | grep -v "jdk.internal.vm\|jdk.aot\|jcef" > modules.list.aarch64
echo Running jlink....
${JSDK}/bin/jlink \
--module-path ${JSDK}/jmods --no-man-pages --compress=2 \

View File

@@ -70,7 +70,7 @@ function create_jbr {
echo "***ERR*** bundle was not specified" && do_exit 1
;;
esac
cat modules.list > modules_tmp.list
cat jb/project/tools/common/modules.list > modules_tmp.list
rm -rf ${BASE_DIR}/${JBR_BUNDLE}
JBR=${JBR_BASE_NAME}-linux-x64-b${build_number}
@@ -167,4 +167,4 @@ if [ "$bundle_type" == "jcef" ]; then
gzip -f ${JBRSDK_TEST}.tar || do_exit $?
fi
do_exit 0
do_exit 0

View File

@@ -63,7 +63,7 @@ JBR_BASE_NAME=jbr-$JBSDK_VERSION
rm -rf $BASE_DIR/$JBR_BUNDLE
JBR=$JBR_BASE_NAME-linux-x86-b$build_number
grep -v javafx modules.list | grep -v "jdk.internal.vm\|jdk.aot\|jcef" > modules.list.x86
grep -v javafx jb/project/tools/common/modules.list | grep -v "jdk.internal.vm\|jdk.aot\|jcef" > modules.list.x86
echo Running jlink....
${JSDK}/bin/jlink \
--module-path ${JSDK}/jmods --no-man-pages --compress=2 \
@@ -79,4 +79,4 @@ gzip $JBR.tar || exit $?
JBRSDK_TEST=$JBRSDK_BASE_NAME-linux-test-x86-b$build_number
echo Creating $JBRSDK_TEST.tar.gz ...
tar -pcf $JBRSDK_TEST.tar -C $BASE_DIR --exclude='test/jdk/demos' --exclude='test/hotspot/gtest' test || exit $?
gzip $JBRSDK_TEST.tar || exit $?
gzip $JBRSDK_TEST.tar || exit $?

View File

@@ -103,11 +103,11 @@ function create_jbr {
if [[ "${architecture}" == *aarch64* ]] && [[ "${enable_aot}" != *enable_aot* ]]; then
# aot isn't supported yet, so remove dependent modules
echo "Exclude jdk.internal.vm.compiler and jdk.aot (because aot not supported yet)"
cat modules.list | \
cat jb/project/tools/common/modules.list | \
grep -v "jdk.internal.vm.compiler\|jdk.aot" \
> modules_tmp.list
else
cat modules.list > modules_tmp.list
cat jb/project/tools/common/modules.list > modules_tmp.list
fi
rm -rf ${BASE_DIR}/${JBR_BUNDLE}
@@ -218,4 +218,4 @@ if [ "$bundle_type" == "jcef" ]; then
--exclude='test/jdk/demos' test || do_exit $?
fi
do_exit 0
do_exit 0

View File

@@ -1,7 +1,7 @@
diff --git modules.list modules.list
diff --git jb/project/tools/common/modules.list jb/project/tools/common/modules.list
index 33375b527c4..76539cbc0e0 100644
--- modules.list
+++ modules.list
--- jb/project/tools/common/modules.list
+++ jb/project/tools/common/modules.list
@@ -55,4 +55,7 @@ jdk.unsupported,
jdk.xml.dom,
jdk.zipfs,

View File

@@ -1,7 +1,7 @@
diff --git modules.list modules.list
diff --git jb/project/tools/common/modules.list jb/project/tools/common/modules.list
index 33375b527c4..76539cbc0e0 100644
--- modules.list
+++ modules.list
--- jb/project/tools/common/modules.list
+++ jb/project/tools/common/modules.list
@@ -55,4 +55,5 @@ jdk.unsupported,
jdk.xml.dom,
jdk.zipfs,

View File

@@ -0,0 +1,72 @@
#!/bin/bash
NEWFILEPATH=$1
CONFIGID=$2
BUILDID=$3
TOKEN=$4
#
# Get the size of new artifact
#
unameOut="$(uname -s)"
case "${unameOut}" in
Linux*)
NEWFILESIZE=$(stat -c%s "$NEWFILEPATH")
;;
Darwin*)
NEWFILESIZE=$(stat -f%z "$NEWFILEPATH")
;;
CYGWIN*)
NEWFILESIZE=$(stat -c%s "$NEWFILEPATH")
;;
MINGW*)
NEWFILESIZE=$(stat -c%s "$NEWFILEPATH")
;;
*)
echo "Unknown machine: ${unameOut}"
exit 1
esac
echo "New size of $NEWFILEPATH = $NEWFILESIZE bytes."
# example: IntellijCustomJdk_Jdk17_Master_LinuxX64jcef
CURL_RESPONSE=$(curl --header "Authorization: Bearer $TOKEN" "https://buildserver.labs.intellij.net/app/rest/builds/?locator=buildType:(id:$CONFIGID),status:success,count:1,finishDate:(build:$BUILDID,condition:before)")
re='id=\"([0-9]+)\".+number=\"([0-9]+)\"'
ID=0
if [[ $CURL_RESPONSE =~ $re ]]; then
ID=${BASH_REMATCH[1]}
echo "BUILD Number: ${BASH_REMATCH[2]}"
else
echo "ERROR: can't find previous build"
echo $CURL_RESPONSE
exit 1
fi
CURL_RESPONSE=$(curl --header "Authorization: Bearer $TOKEN" "https://buildserver.labs.intellij.net/app/rest/builds/$ID?fields=id,number,artifacts(file(name,size))")
echo "Atrifacts of last pinned build of $CONFIGID :\n"
echo $CURL_RESPONSE
# Find size (in response) with reg exp
re='name=\"(jbrsdk[^\"]+\.tar\.gz)\" size=\"([0-9]+)\"'
if [[ $CURL_RESPONSE =~ $re ]]; then
OLDFILENAME=${BASH_REMATCH[1]}
echo "Prev artifact name: $OLDFILENAME"
OLDFILESIZE=${BASH_REMATCH[2]}
echo "Prev artifact size = $OLDFILESIZE"
let allowedSize=OLDFILESIZE+OLDFILESIZE/20 # use 5% threshold
echo "Allowed size = $allowedSize"
if [[ "$NEWFILESIZE" -gt "$allowedSize" ]]; then
echo "ERROR: new size is significally greater than prev size (need to investigate)"
exit 1
fi
else
echo "ERROR: can't find string with size in xml response:"
echo $CURL_RESPONSE
exit 1
fi

View File

@@ -81,9 +81,10 @@ function create_jbr {
esac
echo "Exclude jdk.internal.vm.compiler and jdk.aot (because aot is not supported yet)"
cat modules.list | \
cat jb/project/tools/common/modules.list | \
grep -v "jdk.internal.vm.compiler\|jdk.aot" \
> modules_tmp.list
echo ",jdk.crypto.mscapi" >> modules_tmp.list
rm -rf ${JBR_BUNDLE}
@@ -160,4 +161,4 @@ fi
create_jbr || do_exit $?
do_exit 0
do_exit 0

View File

@@ -75,7 +75,8 @@ function create_jbr {
echo "***ERR*** bundle was not specified" && do_exit 1
;;
esac
cat modules.list > modules_tmp.list
cat jb/project/tools/common/modules.list > modules_tmp.list
echo ",jdk.crypto.mscapi" >> modules_tmp.list
rm -rf ${JBR_BUNDLE}
echo Running jlink....
@@ -151,4 +152,4 @@ fi
create_jbr || do_exit $?
do_exit 0
do_exit 0

View File

@@ -57,7 +57,8 @@ mv release ${JBRSDK_BUNDLE}/release
JBR_BUNDLE=jbr
rm -rf ${JBR_BUNDLE}
grep -v javafx modules.list | grep -v "jdk.internal.vm\|jdk.aot\|jcef" > modules.list.x86
grep -v javafx jb/project/tools/common/modules.list | grep -v "jdk.internal.vm\|jdk.aot\|jcef" > modules.list.x86
echo ",jdk.crypto.mscapi" >> modules.list.x86
${JSDK}/bin/jlink \
--module-path ${JSDK}/jmods --no-man-pages --compress=2 \
--add-modules $(xargs < modules.list.x86 | sed s/" "//g) --output ${JBR_BUNDLE} || exit $?

View File

@@ -16,6 +16,7 @@
package com.jetbrains.internal;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@@ -158,13 +159,52 @@ public class JBRApi {
RegisteredProxyInfo info = registeredProxyInfoByTargetName.get(targetName);
if (info == null) return null;
try {
return (info.type == ProxyInfo.Type.CLIENT_PROXY ? info.apiModule : outerLookup)
return (info.type.isPublicApi() ? outerLookup : info.apiModule)
.findClass(info.interfaceName);
} catch (ClassNotFoundException | IllegalAccessException e) {
return null;
}
}
public static InternalServiceBuilder internalServiceBuilder(Lookup interFace, String target) {
return new InternalServiceBuilder(new RegisteredProxyInfo(
interFace, interFace.lookupClass().getName(), target, ProxyInfo.Type.INTERNAL_SERVICE, new ArrayList<>()));
}
public static class InternalServiceBuilder {
private final RegisteredProxyInfo info;
private InternalServiceBuilder(RegisteredProxyInfo info) {
this.info = info;
}
public InternalServiceBuilder withStatic(String methodName, String clazz) {
return withStatic(methodName, clazz, methodName);
}
public InternalServiceBuilder withStatic(String interfaceMethodName, String clazz, String methodName) {
info.staticMethods.add(
new RegisteredProxyInfo.StaticMethodMapping(interfaceMethodName, clazz, methodName));
return this;
}
public Object build() {
ProxyInfo info = ProxyInfo.resolve(this.info);
if (info == null) return null;
ProxyGenerator generator = new ProxyGenerator(info);
if (!generator.areAllMethodsImplemented()) return null;
generator.defineClasses();
MethodHandle constructor = generator.findConstructor();
generator.init();
try {
return constructor.invoke();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
/**
* Called by {@linkplain com.jetbrains.bootstrap.JBRApiBootstrap#MODULES registry classes}
* to register a new mapping for corresponding modules.

View File

@@ -78,7 +78,7 @@ class ProxyGenerator {
*/
ProxyGenerator(ProxyInfo info) {
this.info = info;
generateBridge = info.type != ProxyInfo.Type.CLIENT_PROXY;
generateBridge = info.type.isPublicApi();
int nameId = nameCounter.getAndIncrement();
proxyName = Type.getInternalName(info.interFace) + "$$JBRApiProxy$" + nameId;
bridgeName = generateBridge ? info.apiModule.lookupClass().getPackageName().replace('.', '/') + "/" +
@@ -153,7 +153,7 @@ class ProxyGenerator {
} else {
MethodHandle c = generatedProxy.findConstructor(generatedProxy.lookupClass(),
MethodType.methodType(void.class, Object.class));
if (info.type == ProxyInfo.Type.SERVICE) {
if (info.type.isService()) {
try {
return MethodHandles.foldArguments(c, info.target.findConstructor(info.target.lookupClass(),
MethodType.methodType(void.class)).asType(MethodType.methodType(Object.class)));

View File

@@ -66,7 +66,7 @@ class ProxyInfo {
}
Lookup getInterfaceLookup() {
return type == Type.CLIENT_PROXY ? apiModule : JBRApi.outerLookup;
return type == Type.CLIENT_PROXY || type == Type.INTERNAL_SERVICE ? apiModule : JBRApi.outerLookup;
}
Lookup getTargetLookup() {
@@ -74,12 +74,18 @@ class ProxyInfo {
}
private Lookup lookup(Lookup lookup, String clazz) {
try {
return MethodHandles.privateLookupIn(lookup.findClass(clazz), lookup);
} catch (ClassNotFoundException | IllegalAccessException e) {
if (lookup == JBRApi.outerLookup) return null;
else throw new RuntimeException(e);
String[] nestedClasses = clazz.split("\\$");
clazz = "";
for (int i = 0; i < nestedClasses.length; i++) {
try {
if (i != 0) clazz += "$";
clazz += nestedClasses[i];
lookup = MethodHandles.privateLookupIn(lookup.findClass(clazz), lookup);
} catch (ClassNotFoundException | IllegalAccessException ignore) {
return null;
}
}
return lookup;
}
static class StaticMethodMapping {
@@ -98,6 +104,15 @@ class ProxyInfo {
enum Type {
PROXY,
SERVICE,
CLIENT_PROXY
CLIENT_PROXY,
INTERNAL_SERVICE;
public boolean isPublicApi() {
return this == PROXY || this == SERVICE;
}
public boolean isService() {
return this == SERVICE || this == INTERNAL_SERVICE;
}
}
}

View File

@@ -32,10 +32,9 @@ import java.beans.*;
import java.lang.annotation.Native;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.PrivilegedAction;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import javax.accessibility.*;
@@ -46,24 +45,18 @@ import sun.awt.AWTAccessor;
class CAccessibility implements PropertyChangeListener {
private static Set<String> ignoredRoles;
private static final int INVOKE_TIMEOUT_SECONDS_DEFAULT = 1;
private static final int INVOKE_TIMEOUT_SECONDS;
static {
AtomicInteger invokeTimeoutSecondsRef = new AtomicInteger();
// Need to load the native library for this code.
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
System.loadLibrary("awt");
invokeTimeoutSecondsRef.set(
// (-1) for the infinite timeout
Integer.getInteger("sun.lwawt.macosx.CAccessibility.invokeTimeoutSeconds",
INVOKE_TIMEOUT_SECONDS_DEFAULT));
return null;
}
// (-1) for the infinite timeout
@SuppressWarnings("removal")
int value = java.security.AccessController.doPrivileged(
(PrivilegedAction<Integer>) () -> {
// Need to load the native library for this code.
System.loadLibrary("awt");
return Integer.getInteger("sun.lwawt.macosx.CAccessibility.invokeTimeoutSeconds", 1);
});
INVOKE_TIMEOUT_SECONDS = invokeTimeoutSecondsRef.get();
INVOKE_TIMEOUT_SECONDS = value;
}
static CAccessibility sAccessibility;
@@ -634,7 +627,7 @@ class CAccessibility implements PropertyChangeListener {
int rowChildIndex = rowChildContext.getAccessibleIndexInParent();
int accessibleColumnCount = ((AccessibleTable) ac).getAccessibleColumnCount();
if (accessibleColumnCount <= 0) return -1;
return rowChildIndex / accessibleColumnCount;
};
int firstVisibleRow = calcRowIndex.apply(location1);

View File

@@ -270,9 +270,8 @@ final class CPlatformResponder {
LWCToolkit lwcToolkit = (LWCToolkit)Toolkit.getDefaultToolkit();
if ((lwcToolkit.getLockingKeyState(KeyEvent.VK_CAPS_LOCK) &&
Locale.SIMPLIFIED_CHINESE.equals(lwcToolkit.getDefaultKeyboardLocale())) ||
(LWCToolkit.isLocaleUSInternationalPC(lwcToolkit.getDefaultKeyboardLocale()) &&
LWCToolkit.isCharModifierKeyInUSInternationalPC(testChar) &&
(testChar != testCharIgnoringModifiers))) {
((testChar != testCharIgnoringModifiers) &&
LWCToolkit.isCharModifierKeyInLocale(lwcToolkit.getDefaultKeyboardLocale(), testChar))) {
testChar = testCharIgnoringModifiers;
}

View File

@@ -89,6 +89,7 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.Callable;
@@ -188,7 +189,7 @@ public final class LWCToolkit extends LWToolkit {
*/
private static final boolean inAWT;
private static final PlatformLogger log = PlatformLogger.getLogger("sun.lwawt.macosx.LWCToolkit");
private static final PlatformLogger log = PlatformLogger.getLogger(LWCToolkit.class.getName());
public LWCToolkit() {
areExtraMouseButtonsEnabled = Boolean.parseBoolean(System.getProperty("sun.awt.enableExtraMouseButtons", "true"));
@@ -490,9 +491,8 @@ public final class LWCToolkit extends LWToolkit {
if (!(gd instanceof CGraphicsDevice)) {
return super.getScreenInsets(gc);
}
// Avoid deadlock with input methods
CGraphicsDevice cgd = (CGraphicsDevice) gd;
return AWTThreading.executeWaitToolkit(cgd::getScreenInsets);
return cgd.getScreenInsets();
}
@Override
@@ -660,30 +660,12 @@ public final class LWCToolkit extends LWToolkit {
return invokeAndWait(callable, component, -1);
}
static <T> T invokeAndWait(final Callable<T> callable, Component component, int timeoutSeconds) throws Exception {
public static <T> T invokeAndWait(final Callable<T> callable, Component component, int timeoutSeconds) throws Exception {
final CallableWrapper<T> wrapper = new CallableWrapper<>(callable);
invokeAndWait(wrapper, component, false, timeoutSeconds);
return wrapper.getResult();
}
static final class CancelableRunnable implements Runnable {
volatile Runnable runnable;
public CancelableRunnable(Runnable runnable) {
this.runnable = runnable;
}
@Override
public void run() {
Runnable r = runnable;
if (r != null) r.run();
}
public void cancel() {
runnable = null;
}
}
static final class CallableWrapper<T> implements Runnable {
final Callable<T> callable;
T object;
@@ -761,15 +743,28 @@ public final class LWCToolkit extends LWToolkit {
invokeAndWait(runnable, component, false, -1);
}
static void invokeAndWait(Runnable runnable, Component component, boolean processEvents, int timeoutSeconds)
public static void invokeAndWait(Runnable runnable, Component component, boolean processEvents, int timeoutSeconds)
throws InvocationTargetException
{
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("invokeAndWait started: " + runnable);
}
if (isBlockingEventDispatchThread()) {
String msg = "invokeAndWait is discarded as the EventDispatch thread is currently blocked";
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine(msg, new Throwable());
log.fine(AWTThreading.getInstance(component).printEventDispatchThreadStackTrace().toString());
} else if (log.isLoggable(PlatformLogger.Level.INFO)) {
StackTraceElement[] stack = new Throwable().getStackTrace();
log.info(msg + ". Originated at " + stack[stack.length - 1]);
Thread dispatchThread = AWTThreading.getInstance(component).getEventDispatchThread();
log.info(dispatchThread.getName() + " at: " + dispatchThread.getStackTrace()[0].toString());
}
return;
}
boolean nonBlockingRunLoop;
CancelableRunnable cancelableRunnable = new CancelableRunnable(runnable);
if (!processEvents) {
synchronized (priorityInvocationPending) {
@@ -781,17 +776,11 @@ public final class LWCToolkit extends LWToolkit {
nonBlockingRunLoop = true;
}
final long mediator = createAWTRunLoopMediator();
AWTThreading.TrackedInvocationEvent invocationEvent =
AWTThreading.createAndTrackInvocationEvent(component, runnable, true);
InvocationEvent invocationEvent =
AWTThreading.createAndTrackInvocationEvent(component,
cancelableRunnable,
() -> {
if (mediator != 0) {
stopAWTRunLoop(mediator);
}
},
true);
long mediator = createAWTRunLoopMediator();
invocationEvent.onDone(() -> stopAWTRunLoop(mediator));
if (component != null) {
AppContext appContext = SunToolkit.targetToAppContext(component);
@@ -805,10 +794,21 @@ public final class LWCToolkit extends LWToolkit {
((LWCToolkit)Toolkit.getDefaultToolkit()).getSystemEventQueueForInvokeAndWait().postEvent(invocationEvent);
}
CompletableFuture<Void> eventDispatchThreadFreeFuture =
AWTThreading.getInstance(component).onEventDispatchThreadFree(() -> {
if (!invocationEvent.isDone()) {
// EventQueue is now empty but the posted InvocationEvent is still not dispatched,
// consider it lost then.
invocationEvent.dispose("InvocationEvent was lost");
}
});
invocationEvent.onDone(() -> eventDispatchThreadFreeFuture.cancel(false));
if (!doAWTRunLoop(mediator, nonBlockingRunLoop, timeoutSeconds)) {
new Throwable("Invocation timed out (" + timeoutSeconds + "sec)").printStackTrace();
cancelableRunnable.cancel();
invocationEvent.dispose("InvocationEvent has timed out");
}
if (!nonBlockingRunLoop) blockingRunLoopCounter.decrementAndGet();
if (log.isLoggable(PlatformLogger.Level.FINE)) {
@@ -818,6 +818,8 @@ public final class LWCToolkit extends LWToolkit {
checkException(invocationEvent);
}
private static native boolean isBlockingEventDispatchThread();
public static void invokeLater(Runnable event, Component component)
throws InvocationTargetException {
Objects.requireNonNull(component, "Null component provided to invokeLater");
@@ -860,6 +862,11 @@ public final class LWCToolkit extends LWToolkit {
*/
static native void performOnMainThreadAfterDelay(Runnable r, long delay);
/**
* Schedules a {@code Runnable} execution on the Appkit thread and waits for completion.
*/
public static native void performOnMainThreadAndWait(Runnable r);
// DnD support
@Override
@@ -914,21 +921,8 @@ public final class LWCToolkit extends LWToolkit {
return locale;
}
public static boolean isLocaleUSInternationalPC(Locale locale) {
return (locale != null ?
locale.toString().equals("_US_UserDefined_15000") : false);
}
public static boolean isCharModifierKeyInUSInternationalPC(char ch) {
// 5 characters: APOSTROPHE, QUOTATION MARK, ACCENT GRAVE, SMALL TILDE,
// CIRCUMFLEX ACCENT
final char[] modifierKeys = {'\'', '"', '`', '\u02DC', '\u02C6'};
for (char modKey : modifierKeys) {
if (modKey == ch) {
return true;
}
}
return false;
public static boolean isCharModifierKeyInLocale(Locale locale, char ch) {
return KeyboardCombiningCharacters.isCharacterCombiningInKeyboardLocale(ch, locale);
}
@Override
@@ -1164,3 +1158,233 @@ public final class LWCToolkit extends LWToolkit {
}
}
}
final class KeyboardCombiningCharacters {
@SuppressWarnings("ConstantConditions")
public static boolean isCharacterCombiningInKeyboardLocale(final char ch, final Locale locale) {
if (locale == null) {
return false;
}
final String localeStr = locale.toString();
if (localeStr == null) {
return false;
}
final char[] localeCombiningChars = keyboardCombiningCharacters.get(localeStr);
if (localeCombiningChars == null) {
return false;
}
for (final char combiningChar : localeCombiningChars) {
if (ch == combiningChar) {
return true;
}
}
return false;
}
private static final Map<String, char[]> keyboardCombiningCharacters;
static {
/*
* All arrays the map contains MUST be sorted into ASCENDING order (for binary search).
* (UPD: the binary search proved to be slower than linear on the such small arrays so the linear is used now)
*
* Also, the syntax '<backslash>u<hex-digits>' is not used to avoid the traps about
* early parsing of the unicode sequences.
*/
// [U+0027 ('''), U+0060 ('`'), U+02c6, U+0022 ('"'), U+02dc, U+00b4, U+00a8]
final char[] ENGLISH_US_INTERNATIONAL_PC =
{(char)0x0022, (char)0x0027, (char)0x0060, (char)0x00A8,
(char)0x00B4, (char)0x02C6, (char)0x02DC};
// [U+0027 ('''), U+00a8, U+00b0, U+005e ('^'), U+02c7,
// U+002c (','), U+002d ('-'), U+0022 ('"'), U+007e ('~')]
final char[] CZECH_CZECH =
{(char)0x0022, (char)0x0027, (char)0x002C, (char)0x002D,
(char)0x005E, (char)0x007E, (char)0x00A8, (char)0x00B0, (char)0x02C7};
// [U+0027 ('''), U+00a8, U+02c7, U+00b0, U+005e ('^'),
// U+002c (','), U+002d ('-'), U+0022 ('"'), U+007e ('~')]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] CZECH_CZECH_QWERTY = CZECH_CZECH;
// [U+00b4, U+00a8, U+0060 ('`'), U+005e ('^'), U+007e ('~')]
final char[] DANISH_DANISH =
{(char)0x005E, (char)0x0060, (char)0x007E, (char)0x00A8, (char)0x00B4};
// [U+0060 ('`'), U+00b4, U+00a8, U+005e ('^'), U+007e ('~')]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] DUTCH_DUTCH = DANISH_DANISH;
// [U+005e ('^'), U+0060 ('`'), U+00a8, U+007e ('~'), U+00b4]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] DUTCH_BELGIAN = DUTCH_DUTCH;
// [U+00b4, U+00a8, U+0060 ('`'), U+005e ('^'), U+007e ('~')]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] FINNISH_FINNISH = DANISH_DANISH;
// [U+00b4, U+00a8, U+0060 ('`'), U+02c6, U+2038, U+02dd, U+002c (','),
// U+02c7, U+02d8, U+02c0, U+02bc, U+02d9, U+02da, U+00af, U+002e ('.'),
// U+02db, U+00a0, U+0330, U+02dc, U+002d ('-'), U+02cd, U+00b8, U+2116, U+0294]
final char[] FINNISH_FINNISH_EXTENDED =
{(char)0x002C, (char)0x002D, (char)0x002E, (char)0x0060,
(char)0x00A0, (char)0x00A8, (char)0x00AF, (char)0x00B4, (char)0x00B8,
(char)0x0294, (char)0x02BC, (char)0x02C0, (char)0x02C6,
(char)0x02C7, (char)0x02CD, (char)0x02D8, (char)0x02D9,
(char)0x02DA, (char)0x02DB, (char)0x02DC, (char)0x02DD,
(char)0x0330, (char)0x2038, (char)0x2116};
// [U+00b4, U+00a8, U+0060 ('`'), U+02c6, U+2038, U+02dd, U+002c (','),
// U+02c7, U+02d8, U+02c0, U+02d9, U+02da, U+02db, U+00a0, U+0330, U+002d ('-'), U+02cd]
final char[] FINNISH_SAMI_PC =
{(char)0x002C, (char)0x002D, (char)0x0060, (char)0x00A0, (char)0x00A8, (char)0x00B4,
(char)0x02C0, (char)0x02C6, (char)0x02C7, (char)0x02CD,
(char)0x02D8, (char)0x02D9, (char)0x02DA, (char)0x02DB, (char)0x02DD,
(char)0x0330, (char)0x2038};
// [U+005e ('^'), U+0060 ('`'), U+00a8, U+007e ('~'), U+00b4]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] FRENCH_ABC_AZERTY = DANISH_DANISH;
// [U+005e ('^'), U+00a8, U+0060 ('`'), U+007e ('~'), U+00b4]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] FRENCH_CANADIAN_FRENCH_CSA = FRENCH_ABC_AZERTY;
// [U+005e ('^'), U+0060 ('`'), U+00a8, U+007e ('~'), U+00b4]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] FRENCH_FRENCH = FRENCH_ABC_AZERTY;
// [U+005e ('^'), U+0060 ('`'), U+00a8, U+007e ('~'), U+00b4]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] FRENCH_FRENCH_NUMERICAL = FRENCH_ABC_AZERTY;
// [U+005e ('^'), U+00a8, U+007e ('~'), U+0060 ('`'), U+00b4]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] FRENCH_FRENCH_PC = FRENCH_ABC_AZERTY;
// [U+005e ('^'), U+00a8, U+0060 ('`'), U+00b4, U+007e ('~')]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] FRENCH_SWISS_FRENCH = FRENCH_ABC_AZERTY;
// [U+00b4, U+0060 ('`'), U+00a8, U+007e ('~'), U+005e ('^')]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] GERMAN_ABC_QWERTZ = DANISH_DANISH;
// [U+00b4, U+0060 ('`'), U+00a8, U+007e ('~'), U+005e ('^')]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] GERMAN_AUSTRIAN = GERMAN_ABC_QWERTZ;
// [U+00b4, U+0060 ('`'), U+00a8, U+007e ('~'), U+005e ('^')]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] GERMAN_GERMAN = GERMAN_ABC_QWERTZ;
// [U+00b4, U+0060 ('`'), U+02d9, U+02c7, U+02dd, U+007e ('~'),
// U+00a8, U+02d8, U+00af, U+02da, U+02c0, U+02bc,
// U+02cd, U+00b8, U+002c (','), U+02db, U+002e ('.'), U+002d ('-')]
final char[] GERMAN_GERMAN_STANDARD =
{(char)0x002C, (char)0x002D, (char)0x002E, (char)0x0060, (char)0x007E,
(char)0x00A8, (char)0x00AF, (char)0x00B4, (char)0x00B8,
(char)0x02BC, (char)0x02C0, (char)0x02C7, (char)0x02CD,
(char)0x02D8, (char)0x02D9, (char)0x02DA, (char)0x02DB, (char)0x02DD};
// [U+005e ('^'), U+00a8, U+0060 ('`'), U+00b4, U+007e ('~')]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] GERMAN_SWISS_GERMAN = GERMAN_ABC_QWERTZ;
// [U+00b4, U+007e ('~'), U+00a8, U+0060 ('`'), U+005e ('^')]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] PORTUGUESE_BRAZILIAN_ABNT2 = DANISH_DANISH;
// [U+0060 ('`'), U+0027 ('''), U+02dc, U+02c6, U+0022 ('"'), U+00b4, U+00a8]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] PORTUGUESE_BRAZILIAN_PRO = ENGLISH_US_INTERNATIONAL_PC;
// [U+00b4, U+00a8, U+007e ('~'), U+0060 ('`'), U+005e ('^')]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] SPANISH_LATIN_AMERICA = DANISH_DANISH;
// [U+00b4, U+0060 ('`'), U+00a8, U+005e ('^'), U+007e ('~')]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] SPANISH_SPANISH = SPANISH_LATIN_AMERICA;
// [U+0060 ('`'), U+00b4, U+005e ('^'), U+00a8, U+007e ('~')]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] SPANISH_SPANISH_ISO = SPANISH_LATIN_AMERICA;
// [U+00b4, U+00a8, U+0060 ('`'), U+005e ('^'), U+007e ('~')]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] SWEDISH_SWEDISH = DANISH_DANISH;
// [U+00b4, U+00a8, U+0060 ('`'), U+005e ('^'), U+007e ('~')]
@SuppressWarnings("UnnecessaryLocalVariable")
final char[] SWEDISH_SWEDISH_PRO = SWEDISH_SWEDISH;
// [U+00b4, U+00a8, U+0060 ('`'), U+02c6, U+2038, U+02dd, U+002c (','), U+02c7,
// U+02d8, U+02c0, U+02d9, U+02da, U+02db, <U+00a0, U+0330>, U+002d ('-'), U+02cd]
final char[] SWEDISH_SWEDISH_SAMI_PC =
{(char)0x002C, (char)0x002D, (char)0x0060, (char)0x00A0, (char)0x00A8, (char)0x00B4,
(char)0x02C0, (char)0x02C6, (char)0x02C7, (char)0x02CD,
(char)0x02D8, (char)0x02D9, (char)0x02DA, (char)0x02DB, (char)0x02DD,
(char)0x0330, (char)0x2038};
final Map<String, char[]> keyboardCombiningCharactersInitializer = new HashMap<>();
keyboardCombiningCharactersInitializer.put( "_US_UserDefined_15000", ENGLISH_US_INTERNATIONAL_PC);
keyboardCombiningCharactersInitializer.put( "cs", CZECH_CZECH);
keyboardCombiningCharactersInitializer.put( "cs__QWERTY", CZECH_CZECH_QWERTY);
keyboardCombiningCharactersInitializer.put( "da", DANISH_DANISH);
keyboardCombiningCharactersInitializer.put( "nl_BE", DUTCH_BELGIAN);
keyboardCombiningCharactersInitializer.put( "nl", DUTCH_DUTCH);
keyboardCombiningCharactersInitializer.put( "fi", FINNISH_FINNISH);
keyboardCombiningCharactersInitializer.put( "fi__Extended", FINNISH_FINNISH_EXTENDED);
keyboardCombiningCharactersInitializer.put( "_US_UserDefined_-18", FINNISH_SAMI_PC);
keyboardCombiningCharactersInitializer.put( "_US_UserDefined_251", FRENCH_ABC_AZERTY);
keyboardCombiningCharactersInitializer.put( "fr_CA", FRENCH_CANADIAN_FRENCH_CSA);
keyboardCombiningCharactersInitializer.put( "fr", FRENCH_FRENCH);
keyboardCombiningCharactersInitializer.put( "fr__numerical", FRENCH_FRENCH_NUMERICAL);
keyboardCombiningCharactersInitializer.put( "_US_UserDefined_60", FRENCH_FRENCH_PC);
keyboardCombiningCharactersInitializer.put( "fr_CH", FRENCH_SWISS_FRENCH);
keyboardCombiningCharactersInitializer.put( "_US_UserDefined_253", GERMAN_ABC_QWERTZ);
keyboardCombiningCharactersInitializer.put( "de_AT", GERMAN_AUSTRIAN);
keyboardCombiningCharactersInitializer.put( "de", GERMAN_GERMAN);
keyboardCombiningCharactersInitializer.put("_US_UserDefined_-18133", GERMAN_GERMAN_STANDARD);
keyboardCombiningCharactersInitializer.put( "de_CH", GERMAN_SWISS_GERMAN);
keyboardCombiningCharactersInitializer.put( "_US_UserDefined_128", PORTUGUESE_BRAZILIAN_ABNT2);
keyboardCombiningCharactersInitializer.put( "_US_UserDefined_72", PORTUGUESE_BRAZILIAN_PRO);
keyboardCombiningCharactersInitializer.put( "_US_UserDefined_89", SPANISH_LATIN_AMERICA);
keyboardCombiningCharactersInitializer.put( "es", SPANISH_SPANISH);
keyboardCombiningCharactersInitializer.put( "es__ISO", SPANISH_SPANISH_ISO);
keyboardCombiningCharactersInitializer.put( "sv", SWEDISH_SWEDISH);
keyboardCombiningCharactersInitializer.put( "sv__Pro", SWEDISH_SWEDISH_PRO);
keyboardCombiningCharactersInitializer.put( "_US_UserDefined_-15", SWEDISH_SWEDISH_SAMI_PC);
keyboardCombiningCharacters = keyboardCombiningCharactersInitializer;
}
}

View File

@@ -1011,7 +1011,9 @@ AWT_ASSERT_APPKIT_THREAD;
// the new key window
NSWindow *keyWindow = [NSApp keyWindow];
AWTWindow *opposite = nil;
opposite = (AWTWindow *)[keyWindow delegate];
if (keyWindow != self.nsWindow) {
opposite = (AWTWindow *)[keyWindow delegate];
}
[AWTWindow setLastKeyWindow: self];
[self _deliverWindowFocusEvent:NO oppositeWindow: opposite];

View File

@@ -593,6 +593,8 @@ AWT_ASSERT_APPKIT_THREAD;
jboolean result = JNI_TRUE;
JNI_COCOA_ENTER(env);
if (ThreadUtilities.blockingEventDispatchThread) return JNI_FALSE;
AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
if (mediatorObject == nil) return JNI_TRUE;
@@ -627,6 +629,17 @@ JNI_COCOA_EXIT(env);
return result;
}
/*
* Class: sun_lwawt_macosx_LWCToolkit
* Method: isBlockingEventDispatchThread
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isBlockingEventDispatchThread
(JNIEnv *env, jclass clz)
{
return ThreadUtilities.blockingEventDispatchThread;
}
/*
* Class: sun_lwawt_macosx_LWCToolkit
* Method: stopAWTRunLoop
@@ -639,6 +652,8 @@ JNI_COCOA_ENTER(env);
AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
if (mediatorObject == nil) return;
[ThreadUtilities performOnMainThread:@selector(endRunLoop) on:mediatorObject withObject:nil waitUntilDone:NO];
[mediatorObject release];
@@ -664,6 +679,23 @@ JNI_COCOA_ENTER(env);
JNI_COCOA_EXIT(env);
}
/*
* Class: sun_lwawt_macosx_LWCToolkit
* Method: performOnMainThreadAndWait
* Signature: (Ljava/lang/Runnable)V
*/
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_performOnMainThreadAndWait
(JNIEnv *env, jclass clz, jobject runnable)
{
JNI_COCOA_ENTER(env);
jobject gRunnable = (*env)->NewGlobalRef(env, runnable);
CHECK_NULL(gRunnable);
[ThreadUtilities performOnMainThreadWaiting:YES block:^() {
JavaRunnable* performer = [[JavaRunnable alloc] initWithRunnable:gRunnable];
[performer perform];
}];
JNI_COCOA_EXIT(env);
}
/*
* Class: sun_lwawt_macosx_LWCToolkit

View File

@@ -127,6 +127,13 @@ do { \
__attribute__((visibility("default")))
@interface ThreadUtilities : NSObject { } /* Extend NSObject so can call performSelectorOnMainThread */
/*
* When a blocking performSelectorOnMainThread is executed from the EventDispatch thread,
* and the executed code triggers an opposite blocking a11y call (via LWCToolkit.invokeAndWait)
* this is a deadlock case, and then this property is used to discard LWCToolkit.invokeAndWait.
*/
@property (class, atomic, readonly) BOOL blockingEventDispatchThread;
+ (JNIEnv*)getJNIEnv;
+ (JNIEnv*)getJNIEnvUncached;
+ (void)detachCurrentThread;

View File

@@ -50,6 +50,23 @@ static inline void attachCurrentThread(void** env) {
@implementation ThreadUtilities
// A backing store for the [blockingEventDispatchThread] class level property
static BOOL _blockingEventDispatchThread = NO;
static long eventDispatchThreadPtr = (long)nil;
// The [blockingEventDispatchThread] property is readonly, so we implement a private setter
static void setBlockingEventDispatchThread(BOOL value) {
@synchronized([ThreadUtilities class]) {
_blockingEventDispatchThread = value;
}
}
+ (BOOL) blockingEventDispatchThread {
@synchronized([ThreadUtilities class]) {
return _blockingEventDispatchThread;
}
}
+ (void)initialize {
/* All the standard modes plus ours */
javaModes = [[NSArray alloc] initWithObjects:NSDefaultRunLoopMode,
@@ -116,7 +133,12 @@ AWT_ASSERT_APPKIT_THREAD;
if ([NSThread isMainThread] && wait == YES) {
[target performSelector:aSelector withObject:arg];
} else {
[target performSelectorOnMainThread:aSelector withObject:arg waitUntilDone:wait modes:javaModes];
setBlockingEventDispatchThread((long)[NSThread currentThread] == eventDispatchThreadPtr);
@try {
[target performSelectorOnMainThread:aSelector withObject:arg waitUntilDone:wait modes:javaModes];
} @finally {
setBlockingEventDispatchThread(NO);
}
}
}
@@ -143,3 +165,16 @@ JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CThreading_isMainThread
return [NSThread isMainThread];
}
/*
* Class: sun_awt_AWTThreading
* Method: notifyEventDispatchThreadStartedNative
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_sun_awt_AWTThreading_notifyEventDispatchThreadStartedNative
(JNIEnv *env, jclass c)
{
@synchronized([ThreadUtilities class]) {
eventDispatchThreadPtr = (long)[NSThread currentThread];
}
}

View File

@@ -30,6 +30,7 @@ public class JBRApiModule {
.withStatic("getSubpixelResolution", "sun.font.FontUtilities")
.service("com.jetbrains.JBRFileDialogService", null)
.withStatic("getFileDialog", "com.jetbrains.desktop.JBRFileDialog", "get")
.proxy("com.jetbrains.JBRFileDialog", "com.jetbrains.desktop.JBRFileDialog");
.proxy("com.jetbrains.JBRFileDialog", "com.jetbrains.desktop.JBRFileDialog")
.service("com.jetbrains.CustomWindowDecoration", "java.awt.Window$CustomWindowDecoration");
}
}

View File

@@ -31,6 +31,7 @@ import java.awt.event.WindowEvent;
import java.util.ArrayList;
import sun.awt.AWTThreading;
import sun.util.logging.PlatformLogger;
import sun.awt.dnd.SunDragSourceContextPeer;
@@ -86,6 +87,7 @@ class EventDispatchThread extends Thread {
}
public void run() {
AWTThreading.getInstance(Thread.currentThread()).notifyEventDispatchThreadStarted();
try {
pumpEvents(new Conditional() {
public boolean evaluate() {
@@ -97,6 +99,8 @@ class EventDispatchThread extends Thread {
}
}
private static native void registerEventDispatchThread();
void pumpEvents(Conditional cond) {
pumpEvents(ANY_EVENT, cond);
}

View File

@@ -357,6 +357,7 @@ public class EventQueue {
if (shouldNotify) {
if (theEvent.getSource() != AWTAutoShutdown.getInstance()) {
AWTAutoShutdown.getInstance().notifyThreadBusy(dispatchThread);
AWTThreading.getInstance(dispatchThread).notifyEventDispatchThreadBusy();
}
pushPopCond.signalAll();
} else if (notifyID) {
@@ -569,6 +570,7 @@ public class EventQueue {
return event;
}
AWTAutoShutdown.getInstance().notifyThreadFree(dispatchThread);
AWTThreading.getInstance(dispatchThread).notifyEventDispatchThreadFree();
pushPopCond.await();
} finally {
pushPopLock.unlock();
@@ -1126,6 +1128,7 @@ public class EventQueue {
t.setPriority(Thread.NORM_PRIORITY + 1);
t.setDaemon(false);
AWTAutoShutdown.getInstance().notifyThreadBusy(t);
AWTThreading.getInstance(t).notifyEventDispatchThreadBusy();
return t;
}
}
@@ -1156,6 +1159,7 @@ public class EventQueue {
dispatchThread = null;
}
AWTAutoShutdown.getInstance().notifyThreadFree(edt);
AWTThreading.getInstance(edt).notifyEventDispatchThreadFree();
/*
* Event was posted after EDT events pumping had stopped, so start
* another EDT to handle this event

View File

@@ -43,6 +43,9 @@ import sun.awt.SunToolkit;
import sun.awt.image.SunWritableRaster;
import sun.java2d.SunGraphicsEnvironment;
import static sun.java2d.SunGraphicsEnvironment.toDeviceSpace;
import static sun.java2d.SunGraphicsEnvironment.toDeviceSpaceAbs;
/**
* This class is used to generate native system input events
* for the purposes of test automation, self-running demos, and
@@ -377,13 +380,9 @@ public class Robot {
*/
public synchronized Color getPixelColor(int x, int y) {
checkScreenCaptureAllowed();
AffineTransform tx = GraphicsEnvironment.
getLocalGraphicsEnvironment().getDefaultScreenDevice().
getDefaultConfiguration().getDefaultTransform();
x = (int) (x * tx.getScaleX());
y = (int) (y * tx.getScaleY());
Color color = new Color(peer.getRGBPixel(x, y));
return color;
Point point = peer.useAbsoluteCoordinates() ? toDeviceSpaceAbs(x, y)
: toDeviceSpace(x, y);
return new Color(peer.getRGBPixel(point.x, point.y));
}
/**
@@ -516,16 +515,17 @@ public class Robot {
} else {
int sX = (int) Math.floor(screenRect.x * uiScaleX);
int sY = (int) Math.floor(screenRect.y * uiScaleY);
int sWidth = (int) Math.ceil(screenRect.width * uiScaleX);
int sHeight = (int) Math.ceil(screenRect.height * uiScaleY);
int temppixels[];
Rectangle scaledRect = new Rectangle(sX, sY, sWidth, sHeight);
temppixels = peer.getRGBPixels(scaledRect);
Rectangle scaledRect;
if (peer.useAbsoluteCoordinates()) {
scaledRect = toDeviceSpaceAbs(gc, screenRect.x,
screenRect.y, screenRect.width, screenRect.height);
} else {
scaledRect = toDeviceSpace(gc, screenRect.x,
screenRect.y, screenRect.width, screenRect.height);
}
// HighResolutionImage
pixels = temppixels;
pixels = peer.getRGBPixels(scaledRect);
buffer = new DataBufferInt(pixels, pixels.length);
raster = Raster.createPackedRaster(buffer, scaledRect.width,
scaledRect.height, scaledRect.width, bandmasks, null);

View File

@@ -45,13 +45,16 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.io.Serializable;
import java.lang.annotation.Native;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EventListener;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.Vector;
@@ -3989,7 +3992,52 @@ public class Window extends Container implements Accessible {
}
private volatile boolean hasCustomDecoration;
private volatile List<Map.Entry<Shape, Integer>> customDecorHitTestSpots;
private volatile int customDecorTitleBarHeight = -1; // 0 can be a legal value when no title bar is expected
// called from native
private int hitTestCustomDecoration(int x, int y) {
var spots = customDecorHitTestSpots;
if (spots == null) return CustomWindowDecoration.NO_HIT_SPOT;
for (var spot : spots) {
if (spot.getKey().contains(x, y)) return spot.getValue();
}
return CustomWindowDecoration.NO_HIT_SPOT;
}
private static class CustomWindowDecoration {
@Native public static final int
NO_HIT_SPOT = 0,
OTHER_HIT_SPOT = 1,
MINIMIZE_BUTTON = 2,
MAXIMIZE_BUTTON = 3,
CLOSE_BUTTON = 4,
MENU_BAR = 5;
void setCustomDecorationEnabled(Window window, boolean enabled) {
window.hasCustomDecoration = enabled;
}
boolean isCustomDecorationEnabled(Window window) {
return window.hasCustomDecoration;
}
void setCustomDecorationHitTestSpots(Window window, List<Map.Entry<Shape, Integer>> spots) {
window.customDecorHitTestSpots = List.copyOf(spots);
}
List<Map.Entry<Shape, Integer>> getCustomDecorationHitTestSpots(Window window) {
return window.customDecorHitTestSpots;
}
void setCustomDecorationTitleBarHeight(Window window, int height) {
if (height >= 0) window.customDecorTitleBarHeight = height;
}
int getCustomDecorationTitleBarHeight(Window window) {
return window.customDecorTitleBarHeight;
}
}
@Deprecated
boolean hasCustomDecoration() {
return hasCustomDecoration;
}
@@ -3997,6 +4045,7 @@ public class Window extends Container implements Accessible {
/**
* Set via reflection (JB JdkEx API).
*/
@Deprecated
void setHasCustomDecoration() {
hasCustomDecoration = true;
}

View File

@@ -117,4 +117,14 @@ public interface RobotPeer
* @see Robot#createScreenCapture(Rectangle)
*/
int[] getRGBPixels(Rectangle bounds);
/**
* Determines if absolute coordinates should be used by this peer.
*
* @return {@code true} if absolute coordinates should be used,
* {@code false} otherwise
*/
default boolean useAbsoluteCoordinates() {
return false;
}
}

View File

@@ -5,18 +5,33 @@ import sun.util.logging.PlatformLogger;
import java.awt.*;
import java.awt.event.InvocationEvent;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.Stack;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Consumer;
/**
* Used to perform a cross threads (EventDispatch, Toolkit) execution so that the execution does not cause a deadlock.
*
* Note: the log messages are tested by jdk/jb/java/awt/Toolkit/LWCToolkitInvokeAndWaitTest.java
*/
public class AWTThreading {
private static final PlatformLogger logger = PlatformLogger.getLogger("sun.awt.AWTThreading");
private static final PlatformLogger logger = PlatformLogger.getLogger(AWTThreading.class.getName());
private static final Runnable EMPTY_RUNNABLE = () -> {};
private static final AtomicReference<Function<Thread, AWTThreading>> theAWTThreadingFactory =
new AtomicReference<>(AWTThreading::new);
private final Thread eventDispatchThread;
private ExecutorService executor;
// every invokeAndWait() pushes a queue of invocations
@@ -24,12 +39,20 @@ public class AWTThreading {
private int level; // re-entrance level
private final List<CompletableFuture<Void>> eventDispatchThreadStateNotifiers = new ArrayList<>();
private volatile boolean isEventDispatchThreadFree;
// invocations should be dispatched on proper EDT (per AppContext)
private static final Map<Thread, AWTThreading> EDT_TO_INSTANCE_MAP = new ConcurrentHashMap<>();
private static class TrackingQueue extends LinkedBlockingQueue<InvocationEvent> {}
private AWTThreading() {}
/**
* WARNING: for testing purpose, use {@link AWTThreading#getInstance(Thread)} instead.
*/
public AWTThreading(Thread edt) {
eventDispatchThread = edt;
}
/**
* Executes a callable from EventDispatch thread (EDT). It's assumed the callable either performs a blocking execution on Toolkit
@@ -54,14 +77,25 @@ public class AWTThreading {
}
/**
* Same as {@link #executeWaitToolkit(Callable)}, but without returning a value. If requested (as indicated by
* the passed parameter), the invoked native method is supposed to wait for the result of invocation on AppKit
* thread, and vice versa.
* A boolean value passed to the consumer indicates whether the consumer should perform
* a synchronous invocation on the Toolkit thread and wait, or return immediately.
*
* @see #executeWaitToolkit(Callable).
*/
public static void executeWaitToolkit(Task runnable) {
public static void executeWaitToolkit(Consumer<Boolean> consumer) {
boolean wait = EventQueue.isDispatchThread();
executeWaitToolkit(() -> {
runnable.run(wait);
consumer.accept(wait);
return null;
});
}
/**
* @see #executeWaitToolkit(Callable).
*/
public static void executeWaitToolkit(Runnable runnable) {
executeWaitToolkit(() -> {
runnable.run();
return null;
});
}
@@ -81,12 +115,6 @@ public class AWTThreading {
}
}
if (!isEDT && logger.isLoggable(PlatformLogger.Level.FINE)) {
// this can cause deadlock if calling thread is holding a lock which EDT might require (e.g. AWT tree lock)
logger.fine("AWTThreading.executeWaitToolkit invoked from non-EDT thread", new Throwable());
}
// fallback to default
try {
return callable.call();
} catch (Exception e) {
@@ -176,8 +204,15 @@ public class AWTThreading {
* <li>If the event is first dispatched from EventQueue - it gets removed from the tracking queue.
* <li>If the event is first dispatched from the tracking queue - its dispatching on EventQueue will be noop.
* <ul>
*
* @param source the source of the event
* @param onDispatched called back on event dispatching
* @param catchThrowables should catch Throwable's or propagate to the EventDispatch thread's loop
*/
public static InvocationEvent createAndTrackInvocationEvent(Object source, Runnable runnable, Runnable listener, boolean catchThrowables) {
public static TrackedInvocationEvent createAndTrackInvocationEvent(Object source,
Runnable onDispatched,
boolean catchThrowables)
{
AWTThreading instance = getInstance(source);
if (instance != null) {
synchronized (instance.invocations) {
@@ -185,68 +220,225 @@ public class AWTThreading {
instance.invocations.push(new TrackingQueue());
}
final TrackingQueue queue = instance.invocations.peek();
final InvocationEvent[] eventRef = new InvocationEvent[1];
final AtomicReference<TrackedInvocationEvent> eventRef = new AtomicReference<>();
queue.add(eventRef[0] = new InvocationEvent(
source,
runnable,
// Wrap the original completion listener so that it:
// - guarantees a single run either from dispatch or dispose
// - removes the invocation event from the tracking queue
new Runnable() {
WeakReference<TrackingQueue> queueRef = new WeakReference<>(queue);
eventRef.set(TrackedInvocationEvent.create(
source,
onDispatched,
new Runnable() {
final WeakReference<TrackingQueue> queueRef = new WeakReference<>(queue);
@Override
public void run() {
if (queueRef != null) {
if (listener != null) {
listener.run();
}
TrackingQueue q = queueRef.get();
if (q != null) {
q.remove(eventRef[0]);
}
queueRef = null;
}
@Override
public void run() {
TrackingQueue queue = queueRef.get();
queueRef.clear();
if (queue != null) {
queue.remove(eventRef.get());
}
},
catchThrowables)
{
@Override
public void dispatch() {
if (!isDispatched()) {
super.dispatch();
}
}
});
return eventRef[0];
},
catchThrowables));
queue.add(eventRef.get());
return eventRef.get();
}
}
return new InvocationEvent(source, runnable, listener, catchThrowables);
return TrackedInvocationEvent.create(source, onDispatched, () -> {}, catchThrowables);
}
private static AWTThreading getInstance(Object obj) {
if (obj == null) return null;
@SuppressWarnings("serial")
public static class TrackedInvocationEvent extends InvocationEvent {
private final long creationTime = System.currentTimeMillis();
private final Throwable throwable = new Throwable();
private final CompletableFuture<Void> futureResult = new CompletableFuture<>();
// dispatched or disposed
private final AtomicBoolean isFinished = new AtomicBoolean(false);
static TrackedInvocationEvent create(Object source,
Runnable onDispatch,
Runnable onDone,
boolean catchThrowables)
{
final AtomicReference<TrackedInvocationEvent> eventRef = new AtomicReference<>();
eventRef.set(new TrackedInvocationEvent(
source,
onDispatch,
() -> {
if (onDone != null) {
onDone.run();
}
TrackedInvocationEvent thisEvent = eventRef.get();
if (!thisEvent.isDispatched()) {
// If we're here - this {onDone} is being disposed.
thisEvent.finishIfNotYet(() ->
// If we're here - this {onDone} is called by the outer AWTAccessor.getInvocationEventAccessor().dispose()
// which we do not control, so complete here.
thisEvent.futureResult.completeExceptionally(new Throwable("InvocationEvent was disposed"))
);
}
},
catchThrowables));
return eventRef.get();
}
protected TrackedInvocationEvent(Object source, Runnable onDispatched, Runnable onDone, boolean catchThrowables) {
super(source,
Optional.of(onDispatched).orElse(EMPTY_RUNNABLE),
Optional.of(onDone).orElse(EMPTY_RUNNABLE),
catchThrowables);
futureResult.whenComplete((r, ex) -> {
if (ex != null) {
String message = ex.getMessage() + " (awaiting " + (System.currentTimeMillis() - creationTime) + " ms)";
if (logger.isLoggable(PlatformLogger.Level.FINE)) {
ex.initCause(throwable);
logger.fine(message, ex);
} else if (logger.isLoggable(PlatformLogger.Level.INFO)) {
StackTraceElement[] stack = throwable.getStackTrace();
logger.info(message + ". Originated at " + stack[stack.length - 1]);
}
}
});
}
@Override
public void dispatch() {
finishIfNotYet(super::dispatch);
futureResult.complete(null);
}
public void dispose(String reason) {
finishIfNotYet(() -> AWTAccessor.getInvocationEventAccessor().dispose(this));
futureResult.completeExceptionally(new Throwable(reason));
}
private void finishIfNotYet(Runnable finish) {
if (!isFinished.getAndSet(true)) {
finish.run();
}
}
/**
* Returns whether the event is dispatched or disposed.
*/
public boolean isDone() {
return futureResult.isDone();
}
/**
* Calls the runnable when it's done (immediately if it's done).
*/
public void onDone(Runnable runnable) {
futureResult.whenComplete((r, ex) -> Optional.of(runnable).orElse(EMPTY_RUNNABLE).run());
}
}
public static AWTThreading getInstance(Object obj) {
if (obj == null) {
return getInstance(Toolkit.getDefaultToolkit().getSystemEventQueue());
}
AppContext appContext = SunToolkit.targetToAppContext(obj);
if (appContext == null) return null;
if (appContext == null) {
return getInstance(Toolkit.getDefaultToolkit().getSystemEventQueue());
}
return getInstance((EventQueue)appContext.get(AppContext.EVENT_QUEUE_KEY));
}
private static AWTThreading getInstance(EventQueue eq) {
public static AWTThreading getInstance(EventQueue eq) {
if (eq == null) return null;
return getInstance(AWTAccessor.getEventQueueAccessor().getDispatchThread(eq));
}
private static AWTThreading getInstance(Thread edt) {
if (edt == null) return null;
public static AWTThreading getInstance(Thread edt) {
assert edt != null;
return EDT_TO_INSTANCE_MAP.computeIfAbsent(edt, key -> new AWTThreading());
return EDT_TO_INSTANCE_MAP.computeIfAbsent(edt, key -> theAWTThreadingFactory.get().apply(edt));
}
public interface Task {
void run(boolean wait);
public Thread getEventDispatchThread() {
return eventDispatchThread;
}
public StringWriter printEventDispatchThreadStackTrace() {
return printEventDispatchThreadStackTrace(new StringWriter());
}
public StringWriter printEventDispatchThreadStackTrace(StringWriter writer) {
assert writer != null;
var printer = new PrintWriter(writer);
Thread dispatchThread = getEventDispatchThread();
printer.println(dispatchThread.getName());
Arrays.asList(dispatchThread.getStackTrace()).forEach(frame -> printer.append("\tat ").println(frame));
return writer;
}
/**
* Sets {@code AWTThreading} instance factory.
* WARNING: for testing purpose.
*/
public static void setAWTThreadingFactory(Function<Thread, AWTThreading> factory) {
theAWTThreadingFactory.set(factory);
}
/**
* Must be called on the EventDispatch thread.
*/
public void notifyEventDispatchThreadStarted() {
if (FontUtilities.isMacOSX) notifyEventDispatchThreadStartedNative();
}
private static native void notifyEventDispatchThreadStartedNative();
public void notifyEventDispatchThreadFree() {
List<CompletableFuture<Void>> notifiers = Collections.emptyList();
synchronized (eventDispatchThreadStateNotifiers) {
isEventDispatchThreadFree = true;
if (eventDispatchThreadStateNotifiers.size() > 0) {
notifiers = List.copyOf(eventDispatchThreadStateNotifiers);
}
}
if (logger.isLoggable(PlatformLogger.Level.FINER)) {
logger.finer("notifyEventDispatchThreadFree");
}
// notify callbacks out of the synchronization block
notifiers.forEach(f -> f.complete(null));
}
public void notifyEventDispatchThreadBusy() {
synchronized (eventDispatchThreadStateNotifiers) {
isEventDispatchThreadFree = false;
}
if (logger.isLoggable(PlatformLogger.Level.FINER)) {
logger.finer("notifyEventDispatchThreadBusy");
}
}
/**
* Sets a callback and returns a {@code CompletableFuture} reporting the case when the associated EventDispatch thread
* has gone sleeping and stopped dispatching events because of empty EventQueue. If the EventDispatch thread is free
* at the moment then the callback is called immediately on the caller's thread and the future completes.
*/
public CompletableFuture<Void> onEventDispatchThreadFree(Runnable runnable) {
CompletableFuture<Void> future = new CompletableFuture<>();
future.thenRun(Optional.of(runnable).orElse(EMPTY_RUNNABLE));
if (!isEventDispatchThreadFree) {
synchronized (eventDispatchThreadStateNotifiers) {
if (!isEventDispatchThreadFree) {
eventDispatchThreadStateNotifiers.add(future);
future.whenComplete((r, ex) -> eventDispatchThreadStateNotifiers.remove(future));
return future;
}
}
}
if (logger.isLoggable(PlatformLogger.Level.FINER)) {
logger.finer("onEventDispatchThreadFree: free at the moment");
}
future.complete(null);
return future;
}
}

View File

@@ -1,15 +0,0 @@
package sun.awt;
import java.util.concurrent.Callable;
/**
* TODO: remove it in JBR 202
*
* @deprecated
* @see AWTThreading
*/
public class InvokeOnToolkitHelper {
public static <T> T invokeAndBlock(Callable<T> callable) {
return AWTThreading.executeWaitToolkit(callable);
}
}

View File

@@ -27,6 +27,7 @@ package sun.java2d;
import java.awt.AWTError;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
@@ -452,54 +453,125 @@ public abstract class SunGraphicsEnvironment extends GraphicsEnvironment
return current;
}
/**
* Returns the bounds of the graphics configuration in device space.
*
* @param config the graphics configuration which bounds are requested
* @return the bounds of the area covered by this
* {@code GraphicsConfiguration} in device space (pixels)
*/
public static Rectangle getGCDeviceBounds(GraphicsConfiguration config) {
AffineTransform tx = config.getDefaultTransform();
Rectangle bounds = config.getBounds();
bounds.width *= tx.getScaleX();
bounds.height *= tx.getScaleY();
return bounds;
}
/**
* Converts the size (w, h) from the device space to the user's space using
* passed graphics configuration.
*
* @param gc the graphics configuration to be used for transformation
* @param w the width in the device space
* @param h the height in the device space
* @return the size in the user's space
*/
public static Dimension toUserSpace(GraphicsConfiguration gc,
int w, int h) {
AffineTransform tx = gc.getDefaultTransform();
return new Dimension(
Region.clipRound(w / tx.getScaleX()),
Region.clipRound(h / tx.getScaleY())
);
}
/**
* Converts absolute coordinates from the user's space to the device space
* using appropriate device transformation.
*
* @param x absolute coordinate in the user's space
* @param y absolute coordinate in the user's space
* @return the point which uses device space (pixels)
*/
public static Point toDeviceSpaceAbs(int x, int y) {
GraphicsConfiguration gc = getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration();
gc = getGraphicsConfigurationAtPoint(gc, x, y);
return toDeviceSpaceAbs(gc, x, y, 0, 0).getLocation();
}
/**
* Converts the rectangle from the user's space to the device space using
* appropriate device transformation.
*
* @param rect the rectangle in the user's space
* @return the rectangle which uses device space (pixels)
*/
public static Rectangle toDeviceSpaceAbs(Rectangle rect) {
GraphicsConfiguration gc = getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration();
gc = getGraphicsConfigurationAtPoint(gc, rect.x, rect.y);
return toDeviceSpaceAbs(gc, rect.x, rect.y, rect.width, rect.height);
}
/**
* Converts absolute coordinates (x, y) and the size (w, h) from the user's
* space to the device space using passed graphics configuration.
*
* @param gc the graphics configuration to be used for transformation
* @param x absolute coordinate in the user's space
* @param y absolute coordinate in the user's space
* @param w the width in the user's space
* @param h the height in the user's space
* @return the rectangle which uses device space (pixels)
*/
public static Rectangle toDeviceSpaceAbs(GraphicsConfiguration gc,
int x, int y, int w, int h) {
AffineTransform tx = gc.getDefaultTransform();
Rectangle screen = gc.getBounds();
return new Rectangle(
screen.x + Region.clipRound((x - screen.x) * tx.getScaleX()),
screen.y + Region.clipRound((y - screen.y) * tx.getScaleY()),
Region.clipRound(w * tx.getScaleX()),
Region.clipRound(h * tx.getScaleY())
);
}
/**
* Converts coordinates from the user's space to the device space using
* appropriate device transformation.
*
* @param x coordinate in the user space
* @param y coordinate in the user space
* @return the point which uses device space(pixels)
* @param x coordinate in the user's space
* @param y coordinate in the user's space
* @return the point which uses device space (pixels)
*/
public static Point convertToDeviceSpace(double x, double y) {
GraphicsConfiguration gc = getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration();
gc = getGraphicsConfigurationAtPoint(gc, x, y);
AffineTransform tx = gc.getDefaultTransform();
x = Region.clipRound(x * tx.getScaleX());
y = Region.clipRound(y * tx.getScaleY());
return new Point((int) x, (int) y);
}
/**
* Converts bounds from the user's space to the device space using
* appropriate device transformation.
*
* @param bounds the rectangle in the user space
* @return the rectangle which uses device space(pixels)
*/
public static Rectangle convertToDeviceSpace(Rectangle bounds) {
public static Point toDeviceSpace(int x, int y) {
GraphicsConfiguration gc = getLocalGraphicsEnvironment()
.getDefaultScreenDevice().getDefaultConfiguration();
gc = getGraphicsConfigurationAtPoint(gc, bounds.x, bounds.y);
return convertToDeviceSpace(gc, bounds);
gc = getGraphicsConfigurationAtPoint(gc, x, y);
return toDeviceSpace(gc, x, y, 0, 0).getLocation();
}
/**
* Converts bounds from the user's space to the device space using
* appropriate device transformation of the passed graphics configuration.
* Converts coordinates (x, y) and the size (w, h) from the user's
* space to the device space using passed graphics configuration.
*
* @param bounds the rectangle in the user space
* @return the rectangle which uses device space(pixels)
* @param gc the graphics configuration to be used for transformation
* @param x coordinate in the user's space
* @param y coordinate in the user's space
* @param w the width in the user's space
* @param h the height in the user's space
* @return the rectangle which uses device space (pixels)
*/
public static Rectangle convertToDeviceSpace(GraphicsConfiguration gc,
Rectangle bounds) {
public static Rectangle toDeviceSpace(GraphicsConfiguration gc,
int x, int y, int w, int h) {
AffineTransform tx = gc.getDefaultTransform();
return new Rectangle(
Region.clipRound(bounds.x * tx.getScaleX()),
Region.clipRound(bounds.y * tx.getScaleY()),
Region.clipRound(bounds.width * tx.getScaleX()),
Region.clipRound(bounds.height * tx.getScaleY())
Region.clipRound(x * tx.getScaleX()),
Region.clipRound(y * tx.getScaleY()),
Region.clipRound(w * tx.getScaleX()),
Region.clipRound(h * tx.getScaleY())
);
}
}

View File

@@ -463,7 +463,7 @@ public class Win32GraphicsDevice extends GraphicsDevice implements
// display mode
Rectangle screenBounds = getDefaultConfiguration().getBounds();
w.setBounds(screenBounds.x, screenBounds.y,
dm.getWidth(), dm.getHeight());
screenBounds.width, screenBounds.height);
// Note: no call to replaceSurfaceData is required here since
// replacement will be caused by an upcoming display change event
} else {

View File

@@ -520,7 +520,11 @@ public abstract class WComponentPeer extends WObjectPeer
@Override
public boolean updateGraphicsData(GraphicsConfiguration gc) {
var old = getGraphicsConfiguration().getDefaultTransform();
winGraphicsConfig = (Win32GraphicsConfig)gc;
if (gc != null && !old.equals(gc.getDefaultTransform())) {
syncBounds(); // the bounds of the peer depend on the DPI
}
try {
replaceSurfaceData();
} catch (InvalidPipeException e) {
@@ -529,6 +533,15 @@ public abstract class WComponentPeer extends WObjectPeer
return false;
}
/**
* Make sure that the native peer's coordinates are in sync with the target.
*/
void syncBounds() {
Rectangle r = ((Component) target).getBounds();
setBounds(r.x, r.y, r.width, r.height, SET_BOUNDS);
}
//This will return null for Components not yet added to a Container
@Override
public ColorModel getColorModel() {

View File

@@ -36,6 +36,8 @@ import java.awt.peer.DialogPeer;
import sun.awt.AWTAccessor;
import sun.awt.im.InputMethodManager;
import static sun.java2d.SunGraphicsEnvironment.toUserSpace;
final class WDialogPeer extends WWindowPeer implements DialogPeer {
// Toolkit & peer internals
@@ -117,8 +119,8 @@ final class WDialogPeer extends WWindowPeer implements DialogPeer {
if (((Dialog)target).isUndecorated()) {
return super.getMinimumSize();
}
return new Dimension(scaleDownX(getSysMinWidth()),
scaleDownY(getSysMinHeight()));
return toUserSpace(getGraphicsConfiguration(),
getSysMinWidth(), getSysMinHeight());
}
@Override

View File

@@ -39,7 +39,9 @@ import sun.awt.SunToolkit;
import sun.awt.im.InputMethodManager;
import sun.security.action.GetPropertyAction;
import static sun.java2d.SunGraphicsEnvironment.convertToDeviceSpace;
import static sun.java2d.SunGraphicsEnvironment.getGCDeviceBounds;
import static sun.java2d.SunGraphicsEnvironment.toDeviceSpaceAbs;
import static sun.java2d.SunGraphicsEnvironment.toUserSpace;
class WFramePeer extends WWindowPeer implements FramePeer {
@@ -98,10 +100,10 @@ class WFramePeer extends WWindowPeer implements FramePeer {
*/
private Rectangle adjustMaximizedBounds(Rectangle bounds) {
// All calculations should be done in the device space
bounds = convertToDeviceSpace(bounds);
bounds = toDeviceSpaceAbs(bounds);
GraphicsConfiguration gc = getGraphicsConfiguration();
Rectangle currentDevBounds = convertToDeviceSpace(gc, gc.getBounds());
Rectangle currentDevBounds = getGCDeviceBounds(gc);
// Prepare data for WM_GETMINMAXINFO message.
// ptMaxPosition should be in coordinate system of the current monitor,
// not the main monitor, or monitor on which we maximize the window.
@@ -126,16 +128,6 @@ class WFramePeer extends WWindowPeer implements FramePeer {
public void displayChanged() {
super.displayChanged();
SunToolkit.executeOnEventHandlerThread(target, this::updateIcon);
if (!screenChangedFlag &&
(getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 &&
(getExtendedState() & Frame.ICONIFIED) == 0) {
// A workaround to update the maximized state of the frame
SunToolkit.executeOnEventHandlerThread(target, ()->{
int state = getExtendedState();
setState(Frame.NORMAL);
setState(state);
});
}
}
private native void updateIcon();
@@ -167,13 +159,13 @@ class WFramePeer extends WWindowPeer implements FramePeer {
@Override
public final Dimension getMinimumSize() {
GraphicsConfiguration gc = getGraphicsConfiguration();
Dimension d = new Dimension();
if (!((Frame)target).isUndecorated()) {
d.setSize(scaleDownX(getSysMinWidth()),
scaleDownY(getSysMinHeight()));
d.setSize(toUserSpace(gc, getSysMinWidth(), getSysMinHeight()));
}
if (((Frame)target).getMenuBar() != null) {
d.height += scaleDownY(getSysMenuHeight());
d.height += toUserSpace(gc, 0, getSysMenuHeight()).height;
}
return d;
}

View File

@@ -30,7 +30,7 @@ import java.awt.Point;
import java.awt.Rectangle;
import java.awt.peer.RobotPeer;
import sun.java2d.SunGraphicsEnvironment;
import static sun.java2d.SunGraphicsEnvironment.toDeviceSpaceAbs;
final class WRobotPeer implements RobotPeer {
@@ -40,7 +40,7 @@ final class WRobotPeer implements RobotPeer {
public native void mouseMoveImpl(int x, int y);
@Override
public void mouseMove(int x, int y) {
Point point = SunGraphicsEnvironment.convertToDeviceSpace(x, y);
Point point = toDeviceSpaceAbs(x, y);
mouseMoveImpl(point.x, point.y);
}
@Override
@@ -61,6 +61,11 @@ final class WRobotPeer implements RobotPeer {
return getRGBPixels(new Rectangle(x, y, 1, 1))[0];
}
@Override
public boolean useAbsoluteCoordinates() {
return true;
}
@Override
public int [] getRGBPixels(Rectangle bounds) {
int pixelArray[] = new int[bounds.width*bounds.height];

View File

@@ -37,7 +37,6 @@ import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.IllegalComponentStateException;
import java.awt.Image;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
@@ -58,8 +57,11 @@ import java.awt.image.DataBufferInt;
import java.awt.peer.WindowPeer;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import sun.awt.AWTAccessor;
@@ -75,6 +77,8 @@ import sun.java2d.pipe.Region;
import sun.java2d.SunGraphics2D;
import sun.util.logging.PlatformLogger;
import static sun.java2d.SunGraphicsEnvironment.toUserSpace;
public class WWindowPeer extends WPanelPeer implements WindowPeer,
DisplayChangedListener
{
@@ -90,9 +94,6 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer,
private TranslucentWindowPainter painter;
private int screenNum = 0;
protected boolean screenChangedFlag;
/*
* A key used for storing a list of active windows in AppContext. The value
* is a list of windows, sorted by the time of activation: later a window is
@@ -121,8 +122,6 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer,
private WindowListener windowListener;
private MouseMotionListener mouseMotionListener;
private MouseListener mouseListener;
private float scaleX;
private float scaleY;
private Insets sysInsets; // set from native updateInsets
@@ -216,13 +215,6 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer,
super(target);
// update GC based on the current bounds
updateGC();
try {
screenNum = getScreenImOn();
} catch (IllegalComponentStateException e) {
if (log.isLoggable(PlatformLogger.Level.WARNING)) {
log.warning(e.getMessage());
}
}
}
@Override
@@ -247,8 +239,6 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer,
GraphicsConfiguration gc = getGraphicsConfiguration();
Win32GraphicsDevice gd = (Win32GraphicsDevice) gc.getDevice();
gd.addDisplayChangedListener(this);
scaleX = gd.getDefaultScaleX();
scaleY = gd.getDefaultScaleY();
initActiveWindowsTracking((Window)target);
@@ -335,6 +325,12 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer,
}
}
@Override
final void syncBounds() {
// Windows will take care of the top-level window/frame/dialog, and
// update the location/size when DPI changes.
}
// Synchronize the insets members (here & in helper) with actual window
// state.
native void updateInsets(Insets i);
@@ -516,9 +512,10 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer,
minimumSize = ((Component)target).getMinimumSize();
}
if (minimumSize != null) {
int w = Math.max(minimumSize.width, scaleDownX(getSysMinWidth()));
int h = Math.max(minimumSize.height, scaleDownY(getSysMinHeight()));
setMinSize(w, h);
Dimension sysMin = toUserSpace(getGraphicsConfiguration(),
getSysMinWidth(), getSysMinHeight());
setMinSize(Math.max(minimumSize.width, sysMin.width),
Math.max(minimumSize.height, sysMin.height));
} else {
setMinSize(0, 0);
}
@@ -676,23 +673,8 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer,
AWTAccessor.getComponentAccessor().
setGraphicsConfiguration((Component)target, winGraphicsConfig);
// checkDPIChange(oldDev, newDev);
}
/*private void checkDPIChange(Win32GraphicsDevice oldDev,
Win32GraphicsDevice newDev) {
float newScaleX = newDev.getDefaultScaleX();
float newScaleY = newDev.getDefaultScaleY();
if (scaleX != newScaleX || scaleY != newScaleY) {
windowDPIChange(oldDev.getScreen(), scaleX, scaleY,
newDev.getScreen(), newScaleX, newScaleY);
scaleX = newScaleX;
scaleY = newScaleY;
}
}*/
/**
* From the DisplayChangedListener interface.
*
@@ -706,17 +688,9 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer,
*/
@Override
public void displayChanged() {
int scrn = getScreenImOn();
screenChangedFlag = scrn != screenNum;
screenNum = scrn;
SunToolkit.executeOnEventHandlerThread(target, ()->{
updateGC();
adjustBoundsOnDPIChange();
});
SunToolkit.executeOnEventHandlerThread(target, this::updateGC);
}
private native void adjustBoundsOnDPIChange();
/**
* Part of the DisplayChangedListener interface: components
* do not need to react to this event
@@ -752,78 +726,9 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer,
return true;
}
// These are the peer bounds. They get updated at:
// 1. the WWindowPeer.setBounds() method.
// 2. the native code (on WM_SIZE/WM_MOVE)
private volatile int sysX = 0;
private volatile int sysY = 0;
private volatile int sysW = 0;
private volatile int sysH = 0;
@Override
public native void repositionSecurityWarning();
@Override
public void setBounds(int x, int y, int width, int height, int op) {
sysX = x;
sysY = y;
sysW = width;
sysH = height;
/* [tav] JDK-8176097 fixed by JRE-119
int cx = x + width / 2;
int cy = y + height / 2;
GraphicsConfiguration current = getGraphicsConfiguration();
GraphicsConfiguration other = SunGraphicsEnvironment
.getGraphicsConfigurationAtPoint(current, cx, cy);
if (!current.equals(other)) {
AffineTransform tx = other.getDefaultTransform();
double otherScaleX = tx.getScaleX();
double otherScaleY = tx.getScaleY();
initScales();
if (scaleX != otherScaleX || scaleY != otherScaleY) {
x = (int) Math.floor(x * otherScaleX / scaleX);
y = (int) Math.floor(y * otherScaleY / scaleY);
}
}*/
super.setBounds(x, y, width, height, op);
}
private void initScales() {
if (scaleX >= 1 && scaleY >= 1) {
return;
}
GraphicsConfiguration gc = getGraphicsConfiguration();
if (gc instanceof Win32GraphicsConfig) {
Win32GraphicsDevice gd = ((Win32GraphicsConfig) gc).getDevice();
scaleX = gd.getDefaultScaleX();
scaleY = gd.getDefaultScaleY();
} else {
AffineTransform tx = gc.getDefaultTransform();
scaleX = (float) tx.getScaleX();
scaleY = (float) tx.getScaleY();
}
}
final int scaleUpX(int x) {
return Region.clipRound(x * scaleX);
}
final int scaleUpY(int y) {
return Region.clipRound(y * scaleY);
}
final int scaleDownX(int x) {
return Region.clipRound(x / scaleX);
}
final int scaleDownY(int y) {
return Region.clipRound(y / scaleY);
}
@Override
public void print(Graphics g) {
// We assume we print the whole frame,
@@ -992,9 +897,6 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer,
}
}
/*native void windowDPIChange(int prevScreen, float prevScaleX, float prevScaleY,
int newScreen, float newScaleX, float newScaleY);*/
/*
* The method maps the list of the active windows to the window's AppContext,
* then the method registers ActiveWindowListener, GuiDisposedListener listeners;
@@ -1146,31 +1048,29 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer,
return err;
}
private volatile List<Rectangle> customDecorHitTestSpots;
private volatile int customDecorTitleBarHeight = -1; // 0 can be a legal value when no title bar is expected
// called from client via reflection
@Deprecated
private void setCustomDecorationHitTestSpots(List<Rectangle> hitTestSpots) {
this.customDecorHitTestSpots = new CopyOnWriteArrayList<>(hitTestSpots);
List<Map.Entry<Shape, Integer>> spots = new ArrayList<>();
for (Rectangle spot : hitTestSpots) spots.add(Map.entry(spot, 1));
try {
Field f = Window.class.getDeclaredField("customDecorHitTestSpots");
f.setAccessible(true);
f.set(target, spots);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
}
// called from client via reflection
@Deprecated
private void setCustomDecorationTitleBarHeight(int height) {
if (height >= 0) customDecorTitleBarHeight = height;
}
// called from native
private boolean hitTestCustomDecoration(int x, int y) {
List<Rectangle> spots = customDecorHitTestSpots;
if (spots == null) return false;
for (Rectangle spot : spots) {
if (spot.contains(x, y)) return true;
try {
Field f = Window.class.getDeclaredField("customDecorTitleBarHeight");
f.setAccessible(true);
f.set(target, height);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new Error(e);
}
return false;
}
// called from native
private int getCustomDecorationTitleBarHeight() {
return customDecorTitleBarHeight;
}
}

View File

@@ -86,7 +86,6 @@ Java_sun_awt_windows_WMouseInfoPeer_fillPointWithCoords(JNIEnv *env, jclass cls,
POINT pt;
VERIFY(::GetCursorPos(&pt));
AwtWin32GraphicsDevice::ScaleDownDPoint(&pt);
if (pointClass == NULL) {
jclass pointClassLocal = env->FindClass("java/awt/Point");
DASSERT(pointClassLocal != NULL);
@@ -107,6 +106,9 @@ Java_sun_awt_windows_WMouseInfoPeer_fillPointWithCoords(JNIEnv *env, jclass cls,
yID = env->GetFieldID(pointClass, "y", "I");
CHECK_NULL_RETURN(yID, (jint)0);
int x = (device == NULL) ? pt.x : device->ScaleDownAbsX(pt.x);
int y = (device == NULL) ? pt.y : device->ScaleDownAbsY(pt.y);
env->SetIntField(point, xID, pt.x);
env->SetIntField(point, yID, pt.y);

View File

@@ -45,7 +45,6 @@
#include "awt_Toolkit.h"
#include "awt_Window.h"
#include "awt_Win32GraphicsDevice.h"
#include "awt_Win32GraphicsConfig.h"
#include "Hashtable.h"
#include "ComCtl32Util.h"
@@ -598,7 +597,7 @@ AwtComponent::CreateHWnd(JNIEnv *env, LPCWSTR title,
/*
* Fix for 4046446.
*/
SetWindowPos(GetHWnd(), 0, x, y, w, h, SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOACTIVATE);
Reshape(x, y, w, h);
/* Set default colors. */
m_colorForeground = colorForeground;
@@ -964,11 +963,11 @@ AwtComponent::SetWindowPos(HWND wnd, HWND after,
}
void AwtComponent::Reshape(int x, int y, int w, int h) {
/* ReshapeNoScale(ScaleUpX(x), ScaleUpY(y), ScaleUpX(w), ScaleUpY(h));
ReshapeNoScale(ScaleUpX(x), ScaleUpY(y), ScaleUpX(w), ScaleUpY(h));
}
void AwtComponent::ReshapeNoScale(int x, int y, int w, int h)
{*/
{
#if defined(DEBUG)
RECT rc;
::GetWindowRect(GetHWnd(), &rc);
@@ -976,57 +975,9 @@ void AwtComponent::ReshapeNoScale(int x, int y, int w, int h)
DTRACE_PRINTLN4("AwtComponent::Reshape from %d, %d, %d, %d", rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top);
#endif
int usrX = x;
int usrY = y;
AwtWin32GraphicsDevice* device = UGetDeviceByBounds(URectBounds(x, y, w, h, USER_SPACE), this);
x = device->ScaleUpX(x);
y = device->ScaleUpY(y);
w = device->ScaleUpX(w);
h = device->ScaleUpY(h);
AwtWindow* container = GetContainer();
AwtComponent* parent = GetParent();
// [tav] Handle the fact that an owned window is most likely positioned relative to its owner, and it may
// require pixel-perfect alignment. For that, compensate rounding errors (caused by converting from the device
// space to the integer user space and back) for the owner's origin and for the owner's client area origin.
if (IsTopLevel() && parent != NULL &&
(device->GetScaleX() > 1 || device->GetScaleY() > 1))
{
RECT parentInsets;
parent->GetInsets(&parentInsets);
// Convert the owner's client area origin to user space
int parentInsetsUsrX = device->ScaleDownX(parentInsets.left);
int parentInsetsUsrY = device->ScaleDownY(parentInsets.top);
RECT parentRect;
VERIFY(::GetWindowRect(parent->GetHWnd(), &parentRect));
// Convert the owner's origin to user space
int parentUsrX = device->ScaleDownX(parentRect.left);
int parentUsrY = device->ScaleDownY(parentRect.top);
// Calc the offset from the owner's client area in user space
int offsetUsrX = usrX - parentUsrX - parentInsetsUsrX;
int offsetUsrY = usrY - parentUsrY - parentInsetsUsrY;
// Convert the offset to device space
int offsetDevX = device->ScaleUpX(offsetUsrX);
int offsetDevY = device->ScaleUpY(offsetUsrY);
// Finally calc the window's location based on the frame's and its insets system numbers.
int devX = parentRect.left + parentInsets.left + offsetDevX;
int devY = parentRect.top + parentInsets.top + offsetDevY;
// Check the toplevel is not going to be moved to another screen.
::SetRect(&parentRect, devX, devY, devX + w, devY + h);
HMONITOR hmon = ::MonitorFromRect(&parentRect, MONITOR_DEFAULTTONEAREST);
if (hmon != NULL && AwtWin32GraphicsDevice::GetScreenFromHMONITOR(hmon) == device->GetDeviceIndex()) {
x = devX;
y = devY;
}
}
if (container != NULL && container == parent) {
container->SubtractInsetPoint(x, y);
}
@@ -1133,6 +1084,7 @@ void SpyWinMessage(HWND hwnd, UINT message, LPCTSTR szComment) {
WIN_MSG(WM_DESTROY)
WIN_MSG(WM_MOVE)
WIN_MSG(WM_SIZE)
WIN_MSG(WM_DPICHANGED)
WIN_MSG(WM_ACTIVATE)
WIN_MSG(WM_SETFOCUS)
WIN_MSG(WM_KILLFOCUS)
@@ -1571,9 +1523,9 @@ LRESULT AwtComponent::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
case WM_SIZE:
{
RECT r;
// fix 4128317 : use GetClientRect for full 32-bit int precision and
// fix 4128317 : use GetWindowRect for full 32-bit int precision and
// to avoid negative client area dimensions overflowing 16-bit params - robi
::GetClientRect( GetHWnd(), &r );
::GetWindowRect(GetHWnd(), &r);
mr = WmSize(static_cast<UINT>(wParam), r.right - r.left, r.bottom - r.top);
//mr = WmSize(wParam, LOWORD(lParam), HIWORD(lParam));
SetCompositionWindow(r);
@@ -1590,11 +1542,9 @@ LRESULT AwtComponent::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
break;
case WM_ENTERSIZEMOVE:
m_inMoveResizeLoop = TRUE;
mr = WmEnterSizeMove();
break;
case WM_EXITSIZEMOVE:
m_inMoveResizeLoop = FALSE;
mr = WmExitSizeMove();
break;
// Bug #4039858 (Selecting menu item causes bogus mouse click event)
@@ -1699,6 +1649,9 @@ LRESULT AwtComponent::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
case WM_NCRBUTTONDOWN:
mr = WmNcMouseDown(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), RIGHT_BUTTON);
break;
case WM_NCMOUSEMOVE:
mr = WmNcMouseMove(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
break;
case WM_LBUTTONUP:
if (ignoreNextLBTNUP) {
ignoreNextLBTNUP = FALSE;
@@ -2015,10 +1968,6 @@ LRESULT AwtComponent::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
mr = WmContextMenu((HWND)wParam,
GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
break;
#define WM_DPICHANGED 0x02E0 // Since Win 8.1 in WinUser.h
case WM_DPICHANGED:
mr = WmDPIChanged(HIWORD(wParam), LOWORD(wParam), (RECT*)lParam);
break;
/*
* These messages are used to route Win32 calls to the
@@ -2387,6 +2336,9 @@ MsgRouting AwtComponent::WmNcMouseDown(WPARAM hitTest, int x, int y, int button)
MsgRouting AwtComponent::WmNcMouseUp(WPARAM hitTest, int x, int y, int button) {
return mrDoDefault;
}
MsgRouting AwtComponent::WmNcMouseMove(WPARAM hitTest, int x, int y) {
return mrDoDefault;
}
MsgRouting AwtComponent::WmWindowPosChanging(LPARAM windowPos) {
return mrDoDefault;
@@ -4057,8 +4009,8 @@ void AwtComponent::OpenCandidateWindow(int x, int y)
}
HWND hTop = GetTopLevelParentForWindow(hWnd);
::ClientToScreen(hTop, &p);
int sx = ScaleUpX(x, ABSOLUTE_COORD) - p.x;
int sy = ScaleUpY(y, ABSOLUTE_COORD) - p.y;
int sx = ScaleUpAbsX(x) - p.x;
int sy = ScaleUpAbsY(y) - p.y;
if (!m_bitsCandType) {
SetCandidateWindow(m_bitsCandType, sx, sy);
return;
@@ -4944,64 +4896,70 @@ void AwtComponent::FillAlpha(void *bitmapBits, SIZE &size, BYTE alpha)
}
}
int AwtComponent::ScaleUpX(int x, const UCoordRelativity& relativity) {
if (relativity == ABSOLUTE_COORD) return ScaleUpDX(x);
int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd());
int AwtComponent::GetScreenImOn() {
HWND hWindow = GetAncestor(GetHWnd(), GA_ROOT);
AwtComponent *comp = AwtComponent::GetComponent(hWindow);
if (comp && comp->IsTopLevel()) {
return comp->GetScreenImOn();
}
return AwtWin32GraphicsDevice::DeviceIndexForWindow(hWindow);
}
int AwtComponent::ScaleUpX(int x) {
int screen = GetScreenImOn();
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
return device == NULL ? x : device->ScaleUpX(x);
}
int AwtComponent::ScaleUpDX(int x) {
int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd());
int AwtComponent::ScaleUpAbsX(int x) {
int screen = GetScreenImOn();
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
return device == NULL ? x : device->ScaleUpDX(x);
return device == NULL ? x : device->ScaleUpAbsX(x);
}
int AwtComponent::ScaleUpY(int y, const UCoordRelativity& relativity) {
if (relativity == ABSOLUTE_COORD) return ScaleUpDY(y);
int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd());
int AwtComponent::ScaleUpY(int y) {
int screen = GetScreenImOn();
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
return device == NULL ? y : device->ScaleUpY(y);
}
int AwtComponent::ScaleUpDY(int y) {
int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd());
int AwtComponent::ScaleUpAbsY(int y) {
int screen = GetScreenImOn();
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
return device == NULL ? y : device->ScaleUpDY(y);
return device == NULL ? y : device->ScaleUpAbsY(y);
}
int AwtComponent::ScaleDownX(int x, const UCoordRelativity& relativity) {
if (relativity == ABSOLUTE_COORD) return ScaleDownDX(x);
int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd());
int AwtComponent::ScaleDownX(int x) {
int screen = GetScreenImOn();
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
return device == NULL ? x : device->ScaleDownX(x);
}
int AwtComponent::ScaleDownDX(int x) {
int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd());
int AwtComponent::ScaleDownAbsX(int x) {
int screen = GetScreenImOn();
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
return device == NULL ? x : device->ScaleDownDX(x);
return device == NULL ? x : device->ScaleDownAbsX(x);
}
int AwtComponent::ScaleDownY(int y, const UCoordRelativity& relativity) {
if (relativity == ABSOLUTE_COORD) return ScaleUpDY(y);
int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd());
int AwtComponent::ScaleDownY(int y) {
int screen = GetScreenImOn();
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
return device == NULL ? y : device->ScaleDownY(y);
}
int AwtComponent::ScaleDownDY(int y) {
int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd());
int AwtComponent::ScaleDownAbsY(int y) {
int screen = GetScreenImOn();
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
return device == NULL ? y : device->ScaleDownDY(y);
return device == NULL ? y : device->ScaleDownAbsY(y);
}
void AwtComponent::ScaleDownRect(RECT& r) {
@@ -5309,7 +5267,7 @@ void AwtComponent::SendMouseEvent(jint id, jlong when, jint x, jint y,
id, when, modifiers,
ScaleDownX(x + insets.left),
ScaleDownY(y + insets.top),
ScaleDownX(xAbs), ScaleDownY(yAbs),
ScaleDownAbsX(xAbs), ScaleDownAbsY(yAbs),
clickCount, popupTrigger, button);
if (safe_ExceptionOccurred(env)) {
@@ -5382,8 +5340,8 @@ AwtComponent::SendMouseWheelEvent(jint id, jlong when, jint x, jint y,
id, when, modifiers,
ScaleDownX(x + insets.left),
ScaleDownY(y + insets.top),
ScaleDownX(xAbs),
ScaleDownY(yAbs),
ScaleDownAbsX(xAbs),
ScaleDownAbsY(yAbs),
clickCount, popupTrigger,
scrollType, scrollAmount,
roundedWheelRotation, preciseWheelRotation);
@@ -5893,8 +5851,8 @@ jobject AwtComponent::_GetLocationOnScreen(void *param)
RECT rect;
VERIFY(::GetWindowRect(p->GetHWnd(),&rect));
result = JNU_NewObjectByName(env, "java/awt/Point", "(II)V",
p->ScaleDownX(rect.left),
p->ScaleDownY(rect.top));
p->ScaleDownAbsX(rect.left),
p->ScaleDownAbsY(rect.top));
}
ret:
env->DeleteGlobalRef(self);
@@ -7494,8 +7452,8 @@ void AwtComponent::VerifyState()
target = parent;
}
x = ScaleUpX(x, RELATIVITY_FOR_COMP_XY(this));
y = ScaleUpY(y, RELATIVITY_FOR_COMP_XY(this));
x = ScaleUpX(x);
y = ScaleUpY(y);
width = ScaleUpX(width);
height = ScaleUpY(height);

View File

@@ -32,7 +32,6 @@
#include "awt_Brush.h"
#include "awt_Pen.h"
#include "awt_Win32GraphicsDevice.h"
#include "awt_Util.h"
#include "GDIWindowSurfaceData.h"
@@ -272,10 +271,11 @@ public:
/*
* methods on this component
*/
virtual int GetScreenImOn();
virtual void Show();
virtual void Hide();
virtual void Reshape(int x, int y, int w, int h);
// void ReshapeNoScale(int x, int y, int w, int h);
void ReshapeNoScale(int x, int y, int w, int h);
/*
* Fix for 4046446.
@@ -526,6 +526,7 @@ public:
int wheelRotation, BOOL isHorizontal);
virtual MsgRouting WmNcMouseDown(WPARAM hitTest, int x, int y, int button);
virtual MsgRouting WmNcMouseUp(WPARAM hitTest, int x, int y, int button);
virtual MsgRouting WmNcMouseMove(WPARAM hitTest, int x, int y);
virtual MsgRouting WmWindowPosChanging(LPARAM windowPos);
virtual MsgRouting WmWindowPosChanged(LPARAM windowPos);
virtual void WmTouch(WPARAM wParam, LPARAM lParam);
@@ -605,10 +606,6 @@ public:
return mrDoDefault;
}
virtual MsgRouting WmDPIChanged(UINT xDPI, UINT yDPI, RECT* bounds) {
return mrDoDefault;
}
void UpdateColorModel();
jintArray CreatePrintedPixels(SIZE &loc, SIZE &size, int alpha);
@@ -731,14 +728,14 @@ public:
return m_bPauseDestroy;
}
int ScaleUpX(int x, const UCoordRelativity& relativity = RELATIVE_COORD);
int ScaleUpY(int y, const UCoordRelativity& relativity = RELATIVE_COORD);
int ScaleDownX(int x, const UCoordRelativity& relativity = RELATIVE_COORD);
int ScaleDownY(int y, const UCoordRelativity& relativity = RELATIVE_COORD);
int ScaleUpDX(int x);
int ScaleUpDY(int y);
int ScaleDownDX(int x);
int ScaleDownDY(int y);
int ScaleUpX(int x);
int ScaleUpAbsX(int x);
int ScaleUpY(int y);
int ScaleUpAbsY(int y);
int ScaleDownX(int x);
int ScaleDownAbsX(int x);
int ScaleDownY(int y);
int ScaleDownAbsY(int y);
void ScaleDownRect(RECT& r);
//void ScaleDownDRect(RECT& r);
@@ -755,8 +752,6 @@ protected:
static BOOL sm_suppressFocusAndActivation;
static BOOL sm_restoreFocusAndActivation;
INLINE BOOL IsInMoveResizeLoop() { return m_inMoveResizeLoop; }
/*
* The function sets the focus-restore flag ON/OFF.
* When the flag is ON, focus is restored immidiately after the proxy loses it.
@@ -853,8 +848,6 @@ private:
int m_wheelRotationAmountX;
int m_wheelRotationAmountY;
BOOL m_inMoveResizeLoop;
BOOL deadKeyActive;
/*

View File

@@ -473,9 +473,14 @@ Java_sun_awt_windows_WGlobalCursorManager_getCursorPos(JNIEnv *env,
POINT p;
::GetCursorPos(&p);
AwtWin32GraphicsDevice::ScaleDownDPoint(&p);
env->SetIntField(point, AwtCursor::pointXID, (jint)p.x);
env->SetIntField(point, AwtCursor::pointYID, (jint)p.y);
HMONITOR monitor = MonitorFromPoint(p, MONITOR_DEFAULTTOPRIMARY);
int screen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(monitor);
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
int x = (device == NULL) ? p.x : device->ScaleDownAbsX(p.x);
int y = (device == NULL) ? p.y : device->ScaleDownAbsY(p.y);
env->SetIntField(point, AwtCursor::pointXID, x);
env->SetIntField(point, AwtCursor::pointYID, y);
CATCH_BAD_ALLOC;
}

View File

@@ -1178,14 +1178,14 @@ HRESULT __stdcall AwtDragSource::GetProcessId(FORMATETC __RPC_FAR *pFormatEtc, S
return S_OK;
}
static void ScaleDown(POINT &pt) {
static void ScaleDownAbs(POINT &pt) {
HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
int screen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(monitor);
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
if (device) {
pt.x = device->ScaleDownX(pt.x);
pt.y = device->ScaleDownY(pt.y);
pt.x = device->ScaleDownAbsX(pt.x);
pt.y = device->ScaleDownAbsY(pt.y);
}
}
@@ -1194,7 +1194,7 @@ DECLARE_JAVA_CLASS(dSCClazz, "sun/awt/windows/WDragSourceContextPeer")
void
AwtDragSource::call_dSCenter(JNIEnv* env, jobject self, jint targetActions,
jint modifiers, POINT pt) {
ScaleDown(pt);
ScaleDownAbs(pt);
DECLARE_VOID_JAVA_METHOD(dSCenter, dSCClazz, "dragEnter", "(IIII)V");
DASSERT(!JNU_IsNull(env, self));
env->CallVoidMethod(self, dSCenter, targetActions, modifiers, pt.x, pt.y);
@@ -1207,7 +1207,7 @@ AwtDragSource::call_dSCenter(JNIEnv* env, jobject self, jint targetActions,
void
AwtDragSource::call_dSCmotion(JNIEnv* env, jobject self, jint targetActions,
jint modifiers, POINT pt) {
ScaleDown(pt);
ScaleDownAbs(pt);
DECLARE_VOID_JAVA_METHOD(dSCmotion, dSCClazz, "dragMotion", "(IIII)V");
DASSERT(!JNU_IsNull(env, self));
env->CallVoidMethod(self, dSCmotion, targetActions, modifiers, pt.x, pt.y);
@@ -1220,7 +1220,7 @@ AwtDragSource::call_dSCmotion(JNIEnv* env, jobject self, jint targetActions,
void
AwtDragSource::call_dSCchanged(JNIEnv* env, jobject self, jint targetActions,
jint modifiers, POINT pt) {
ScaleDown(pt);
ScaleDownAbs(pt);
DECLARE_VOID_JAVA_METHOD(dSCchanged, dSCClazz, "operationChanged",
"(IIII)V");
DASSERT(!JNU_IsNull(env, self));
@@ -1233,7 +1233,7 @@ AwtDragSource::call_dSCchanged(JNIEnv* env, jobject self, jint targetActions,
void
AwtDragSource::call_dSCexit(JNIEnv* env, jobject self, POINT pt) {
ScaleDown(pt);
ScaleDownAbs(pt);
DECLARE_VOID_JAVA_METHOD(dSCexit, dSCClazz, "dragExit", "(II)V");
DASSERT(!JNU_IsNull(env, self));
env->CallVoidMethod(self, dSCexit, pt.x, pt.y);
@@ -1246,7 +1246,7 @@ AwtDragSource::call_dSCexit(JNIEnv* env, jobject self, POINT pt) {
void
AwtDragSource::call_dSCddfinished(JNIEnv* env, jobject self, jboolean success,
jint operations, POINT pt) {
ScaleDown(pt);
ScaleDownAbs(pt);
DECLARE_VOID_JAVA_METHOD(dSCddfinished, dSCClazz, "dragDropFinished", "(ZIII)V");
DASSERT(!JNU_IsNull(env, self));
env->CallVoidMethod(self, dSCddfinished, success, operations, pt.x, pt.y);
@@ -1259,7 +1259,7 @@ AwtDragSource::call_dSCddfinished(JNIEnv* env, jobject self, jboolean success,
void
AwtDragSource::call_dSCmouseMoved(JNIEnv* env, jobject self, jint targetActions,
jint modifiers, POINT pt) {
ScaleDown(pt);
ScaleDownAbs(pt);
DECLARE_VOID_JAVA_METHOD(dSCmouseMoved, dSCClazz, "dragMouseMoved",
"(IIII)V");
DASSERT(!JNU_IsNull(env, self));

View File

@@ -1320,18 +1320,18 @@ Java_sun_awt_windows_WFileDialogPeer_toBack(JNIEnv *env, jobject peer)
CATCH_BAD_ALLOC;
}
int ScaleDownX(int x, HWND hwnd) {
int ScaleDownAbsX(int x, HWND hwnd) {
int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(hwnd);
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
return device == NULL ? x : device->ScaleDownX(x);
return device == NULL ? x : device->ScaleDownAbsX(x);
}
int ScaleDownY(int y, HWND hwnd) {
int ScaleDownAbsY(int y, HWND hwnd) {
int screen = AwtWin32GraphicsDevice::DeviceIndexForWindow(hwnd);
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
return device == NULL ? y : device->ScaleDownY(y);
return device == NULL ? y : device->ScaleDownAbsY(y);
}
jobject AwtFileDialog::_GetLocationOnScreen(void *param)
@@ -1346,7 +1346,8 @@ jobject AwtFileDialog::_GetLocationOnScreen(void *param)
RECT rect;
VERIFY(::GetWindowRect(hwnd, &rect));
result = JNU_NewObjectByName(env, "java/awt/Point", "(II)V",
ScaleDownX(rect.left, hwnd), ScaleDownY(rect.top, hwnd));
ScaleDownAbsX(rect.left, hwnd),
ScaleDownAbsY(rect.top, hwnd));
}
if (result != NULL)

View File

@@ -37,6 +37,7 @@
#include <dwmapi.h>
#include <java_lang_Integer.h>
#include <java_awt_Window_CustomWindowDecoration.h>
#include <sun_awt_windows_WEmbeddedFrame.h>
#include <sun_awt_windows_WEmbeddedFramePeer.h>
@@ -332,17 +333,13 @@ AwtFrame* AwtFrame::Create(jobject self, jobject parent)
frame->CreateHWnd(env, L"",
style,
exStyle,
0, 0, 0, 0,
x, y, width, height,
hwndParent,
NULL,
::GetSysColor(COLOR_WINDOWTEXT),
::GetSysColor(COLOR_WINDOWFRAME),
self);
/*
* Reshape here instead of during create, so that a
* WM_NCCALCSIZE is sent.
*/
frame->Reshape(x, y, width, height);
frame->RecalcNonClient();
}
}
} catch (...) {
@@ -626,6 +623,23 @@ MsgRouting AwtFrame::WmNcMouseDown(WPARAM hitTest, int x, int y, int button) {
if (m_grabbedWindow != NULL/* && !m_grabbedWindow->IsOneOfOwnersOf(this)*/) {
m_grabbedWindow->Ungrab();
}
// For windows with custom decorations, handle caption-related mouse events
// Do not handle events from caption itself to preserve native drag behavior
if (HasCustomDecoration()) {
switch (hitTest) {
case HTMINBUTTON:
case HTMAXBUTTON:
case HTCLOSE:
case HTMENU:
RECT rcWindow;
GetWindowRect(GetHWnd(), &rcWindow);
WmMouseDown(GetButtonMK(button),
x - rcWindow.left,
y - rcWindow.top,
button);
return mrConsume;
}
}
if (!IsFocusableWindow() && (button & LEFT_BUTTON)) {
switch (hitTest) {
case HTTOP:
@@ -657,6 +671,24 @@ MsgRouting AwtFrame::WmNcMouseDown(WPARAM hitTest, int x, int y, int button) {
return AwtWindow::WmNcMouseDown(hitTest, x, y, button);
}
MsgRouting AwtFrame::WmNcMouseMove(WPARAM hitTest, int x, int y) {
// For windows with custom decorations, handle caption-related mouse events
if (HasCustomDecoration()) {
switch (hitTest) {
case HTMINBUTTON:
case HTMAXBUTTON:
case HTCLOSE:
case HTMENU:
case HTCAPTION:
RECT rcWindow;
GetWindowRect(GetHWnd(), &rcWindow);
WmMouseMove(0, x - rcWindow.left, y - rcWindow.top);
if (hitTest != HTCAPTION) return mrConsume; // Preserve default window drag for HTCAPTION
}
}
return AwtWindow::WmNcMouseMove(hitTest, x, y);
}
// Override AwtWindow::Reshape() to handle minimized/maximized
// frames (see 6525850, 4065534)
void AwtFrame::Reshape(int x, int y, int width, int height)
@@ -1250,22 +1282,6 @@ MsgRouting AwtFrame::WmSysCommand(UINT uCmdType, int xPos, int yPos)
return AwtWindow::WmSysCommand(uCmdType, xPos, yPos);
}
MsgRouting AwtFrame::WmDPIChanged(UINT xDPI, UINT yDPI, RECT* bounds) {
if (isZoomed() && !m_maxBoundsSet) {
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice* device = devices->GetDevice(AwtWin32GraphicsDevice::DeviceIndexForWindow(GetHWnd()));
if (device) {
float factorX = xDPI / 96.0f / device->GetScaleX();
float factorY = yDPI / 96.0f / device->GetScaleY();
// adjust rcNormalPosition for the zoomed frame
AwtFrame::__SetState(this, AwtFrame::__GetState(this), factorX, factorY);
return mrConsume;
}
}
return AwtWindow::WmDPIChanged(xDPI, yDPI, bounds);
}
LRESULT AwtFrame::WinThreadExecProc(ExecuteArgs * args)
{
switch( args->cmdId ) {
@@ -1376,15 +1392,6 @@ void AwtFrame::_SetState(void *param)
PDATA pData;
JNI_CHECK_PEER_GOTO(self, ret);
f = (AwtFrame *)pData;
AwtFrame::__SetState(f, state);
ret:
env->DeleteGlobalRef(self);
delete sss;
}
void AwtFrame::__SetState(AwtFrame* f, int state, float factorX, float factorY)
{
HWND hwnd = f->GetHWnd();
if (::IsWindow(hwnd))
{
@@ -1408,11 +1415,6 @@ void AwtFrame::__SetState(AwtFrame* f, int state, float factorX, float factorY)
wp.length = sizeof(wp);
::GetWindowPlacement(hwnd, &wp);
wp.rcNormalPosition.left *= factorX;
wp.rcNormalPosition.right *= factorX;
wp.rcNormalPosition.top *= factorY;
wp.rcNormalPosition.bottom *= factorY;
// Iconify first.
// If both iconify & zoom are TRUE, handle this case
// with wp.flags field below.
@@ -1446,6 +1448,10 @@ void AwtFrame::__SetState(AwtFrame* f, int state, float factorX, float factorY)
f->setZoomed(zoom);
}
}
ret:
env->DeleteGlobalRef(self);
delete sss;
}
jint AwtFrame::_GetState(void *param)
@@ -1454,21 +1460,12 @@ jint AwtFrame::_GetState(void *param)
jobject self = (jobject)param;
jint result = java_awt_Frame_NORMAL;
AwtFrame *f = NULL;
PDATA pData;
JNI_CHECK_PEER_GOTO(self, ret);
f = (AwtFrame *)pData;
jint result = AwtFrame::__GetState(f);
ret:
env->DeleteGlobalRef(self);
return result;
}
jint AwtFrame::__GetState(AwtFrame* f)
{
jint result = java_awt_Frame_NORMAL;
if (::IsWindow(f->GetHWnd()))
{
DASSERT(!::IsBadReadPtr(f, sizeof(AwtFrame)));
@@ -1483,6 +1480,9 @@ jint AwtFrame::__GetState(AwtFrame* f)
f->isIconic() ? " iconic" : "",
f->isZoomed() ? " zoomed" : "");
}
ret:
env->DeleteGlobalRef(self);
return result;
}
@@ -1692,7 +1692,7 @@ BOOL AwtFrame::HasCustomDecoration()
if (!m_pHasCustomDecoration) {
m_pHasCustomDecoration = new BOOL;
JNIEnv *env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);
*m_pHasCustomDecoration = JNU_CallMethodByName(env, NULL, GetTarget(env), "hasCustomDecoration", "()Z").z;
*m_pHasCustomDecoration = JNU_GetFieldByName(env, NULL, GetTarget(env), "hasCustomDecoration", "Z").z;
}
return *m_pHasCustomDecoration;
}
@@ -1734,10 +1734,8 @@ LRESULT HitTestNCA(AwtFrame* frame, int x, int y) {
AdjustWindowRectEx(&rcFrame, WS_OVERLAPPEDWINDOW & ~WS_CAPTION, FALSE, NULL);
JNIEnv *env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);
int titleHeight = (int)JNU_CallMethodByName(env, NULL, frame->GetPeer(env),
"getCustomDecorationTitleBarHeight", "()I",
frame->ScaleDownX(x - rcWindow.left),
frame->ScaleDownY(y - rcWindow.top)).i;
int titleHeight = (int)JNU_GetFieldByName(env, NULL, frame->GetTarget(env),
"customDecorTitleBarHeight", "I").i;
if (titleHeight >= 0) {
titleHeight = frame->ScaleUpY(titleHeight);
insets.top = titleHeight; // otherwise leave default
@@ -1750,12 +1748,23 @@ LRESULT HitTestNCA(AwtFrame* frame, int x, int y) {
if (y >= rcWindow.top &&
y < rcWindow.top + insets.top)
{
if (JNU_CallMethodByName(env, NULL, frame->GetPeer(env),
"hitTestCustomDecoration", "(II)Z",
frame->ScaleDownX(x - rcWindow.left),
frame->ScaleDownY(y - rcWindow.top)).z)
{
return HTNOWHERE;
jint customSpot = JNU_CallMethodByName(env, NULL, frame->GetTarget(env),
"hitTestCustomDecoration", "(II)I",
frame->ScaleDownX(x - rcWindow.left),
frame->ScaleDownY(y - rcWindow.top)).i;
switch (customSpot) {
case java_awt_Window_CustomWindowDecoration_NO_HIT_SPOT:
break; // Nothing
case java_awt_Window_CustomWindowDecoration_MINIMIZE_BUTTON:
return HTMINBUTTON;
case java_awt_Window_CustomWindowDecoration_MAXIMIZE_BUTTON:
return HTMAXBUTTON;
case java_awt_Window_CustomWindowDecoration_CLOSE_BUTTON:
return HTCLOSE;
case java_awt_Window_CustomWindowDecoration_MENU_BAR:
return HTMENU;
default:
return HTNOWHERE;
}
fOnResizeBorder = (y < (rcWindow.top - rcFrame.top));
uRow = 0;

View File

@@ -111,9 +111,9 @@ public:
MsgRouting WmMouseMove(UINT flags, int x, int y);
MsgRouting WmNcMouseDown(WPARAM hitTest, int x, int y, int button);
MsgRouting WmNcMouseUp(WPARAM hitTest, int x, int y, int button);
MsgRouting WmNcMouseMove(WPARAM hitTest, int x, int y);
MsgRouting WmGetIcon(WPARAM iconType, LRESULT& retVal);
MsgRouting WmShowWindow(BOOL show, UINT status);
MsgRouting WmDPIChanged(UINT xDPI, UINT yDPI, RECT* bounds);
MsgRouting WmNcCalcSize(BOOL fCalcValidRects, LPNCCALCSIZE_PARAMS lpncsp, LRESULT& retVal);
MsgRouting WmNcHitTest(int x, int y, LRESULT& retVal);
MsgRouting WmWindowPosChanging(LPARAM windowPos);
@@ -141,9 +141,7 @@ public:
// some methods called on Toolkit thread
static void _SetState(void *param);
static void __SetState(AwtFrame* f, int state, float factorX = 1, float factorY = 1);
static jint _GetState(void *param);
static jint __GetState(AwtFrame* f);
static void _SetMaximizedBounds(void *param);
static void _ClearMaximizedBounds(void *param);
static void _SetMenuBar(void *param);

View File

@@ -288,14 +288,17 @@ void AwtList::SetMultiSelect(BOOL ms) {
UnsubclassHWND();
AwtToolkit::DestroyComponentHWND(m_hwnd);
CreateHWnd(env, L"", style, exStyle,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
CreateHWnd(env, L"", style, exStyle, 0, 0, 0, 0,
parentHWnd,
NULL,
::GetSysColor(COLOR_WINDOWTEXT),
::GetSysColor(COLOR_WINDOW),
peer);
SetWindowPos(GetHWnd(), 0,
rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOACTIVATE);
SendListMessage(WM_SETFONT, (WPARAM)font, (LPARAM)FALSE);
SendListMessage(LB_SETITEMHEIGHT, 0, MAKELPARAM(itemHeight, 0));
SendListMessage(LB_RESETCONTENT);

View File

@@ -1,32 +0,0 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
#include "awt_Util.h"
#include "awt_Component.h"
#include "awt_Win32GraphicsDevice.h"
#include "awt_Win32GraphicsConfig.h"
AwtWin32GraphicsDevice* UGetDeviceByBounds(const URectBounds& bounds, AwtComponent* comp)
{
URectBounds _bounds(bounds);
if (comp != NULL && !comp->IsTopLevel()) {
// use the ancestor window to match the device
_bounds = UGetWindowRectBounds(::GetAncestor(comp->GetHWnd(), GA_ROOT));
}
Devices::InstanceAccess devices;
POINT center = {_bounds.x + _bounds.width / 2, _bounds.y + _bounds.height / 2};
for (int i = 0; i < devices->GetNumDevices(); i++) {
RECT rect = AwtWin32GraphicsConfig::getMonitorBounds(i, _bounds.space);
if (::PtInRect(&rect, center)) {
return devices->GetDevice(i);
}
}
// default to the hwnd's device
return comp != NULL ? devices->GetDevice(AwtWin32GraphicsDevice::DeviceIndexForWindow(comp->GetHWnd())) : NULL;
}
URectBounds UGetWindowRectBounds(HWND hwnd)
{
RECT r;
::GetWindowRect(hwnd, &r);
return URectBounds(r.left, r.top, r.right - r.left, r.bottom - r.top, DEVICE_SPACE);
}

View File

@@ -1,47 +0,0 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
#ifndef NATIVE_AWT_UTIL_H
#define NATIVE_AWT_UTIL_H
#include <windows.h>
typedef enum {
DEVICE_SPACE,
USER_SPACE
} UCoordSpace;
typedef enum {
RELATIVE_COORD,
ABSOLUTE_COORD
} UCoordRelativity;
#define RELATIVITY_FOR_COMP_XY(compPtr) compPtr->IsTopLevel() ? ABSOLUTE_COORD : RELATIVE_COORD
typedef struct URectBounds {
int x;
int y;
int width;
int height;
UCoordSpace space;
URectBounds(int _x, int _y, int _w, int _h, const UCoordSpace& _space) : x(_x), y(_y), width(_w), height(_h), space(_space) {}
URectBounds(const URectBounds& b) : x(b.x), y(b.y), width(b.width), height(b.height), space(b.space) {}
URectBounds& operator=(const URectBounds& b) {
x = b.x;
y = b.y;
width = b.width;
height = b.height;
space = b.space;
return *this;
}
} URectBounds;
class AwtComponent;
class AwtWin32GraphicsDevice;
URectBounds UGetWindowRectBounds(HWND hwnd);
AwtWin32GraphicsDevice* UGetDeviceByBounds(const URectBounds& bounds, AwtComponent* comp = NULL);
#endif //NATIVE_AWT_UTIL_H

View File

@@ -77,36 +77,6 @@ inline int shiftToMask(int numBits, int shift) {
return mask;
}
RECT AwtWin32GraphicsConfig::getMonitorBounds(int screen, const UCoordSpace& space)
{
RECT rRW = {0, 0, 0, 0};
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
if (TRUE == MonitorBounds(AwtWin32GraphicsDevice::GetMonitor(screen), &rRW)) {
int x = (device == NULL || space == DEVICE_SPACE) ? rRW.left
: device->ScaleDownX(rRW.left);
int y = (device == NULL || space == DEVICE_SPACE) ? rRW.top
: device->ScaleDownY(rRW.top);
int w = (device == NULL || space == DEVICE_SPACE) ? rRW.right - rRW.left
: device->ScaleDownX(rRW.right - rRW.left);
int h = (device == NULL || space == DEVICE_SPACE) ? rRW.bottom - rRW.top
: device->ScaleDownY(rRW.bottom - rRW.top);
::SetRect(&rRW, x, y, x + w, y + h);
}
else {
// 4910760 - don't return a null bounds, return the bounds of the
// primary screen
int w = ::GetSystemMetrics(SM_CXSCREEN);
int h = ::GetSystemMetrics(SM_CYSCREEN);
::SetRect(&rRW, 0, 0, device == NULL || space == DEVICE_SPACE ? w : device->ScaleDownX(w),
device == NULL || space == DEVICE_SPACE ? h : device->ScaleDownY(h));
}
return rRW;
}
/*
* Class: sun_awt_Win32GraphicsConfig
* Method: getBounds
@@ -124,8 +94,30 @@ JNIEXPORT jobject JNICALL
CHECK_NULL_RETURN(clazz, NULL);
mid = env->GetMethodID(clazz, "<init>", "(IIII)V");
if (mid != 0) {
RECT r = AwtWin32GraphicsConfig::getMonitorBounds((int)screen, USER_SPACE);
bounds = env->NewObject(clazz, mid, r.left, r.top, r.right - r.left, r.bottom - r.top);
RECT rRW = {0, 0, 0, 0};
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
if (TRUE == MonitorBounds(AwtWin32GraphicsDevice::GetMonitor(screen), &rRW)) {
int w = (device == NULL) ? rRW.right - rRW.left
: device->ScaleDownX(rRW.right - rRW.left);
int h = (device == NULL) ? rRW.bottom - rRW.top
: device->ScaleDownY(rRW.bottom - rRW.top);
bounds = env->NewObject(clazz, mid, rRW.left, rRW.top, w, h);
}
else {
// 4910760 - don't return a null bounds, return the bounds of the
// primary screen
int w = ::GetSystemMetrics(SM_CXSCREEN);
int h = ::GetSystemMetrics(SM_CYSCREEN);
bounds = env->NewObject(clazz, mid,
0, 0,
device == NULL ? w : device->ScaleDownX(w),
device == NULL ? h : device->ScaleDownY(h));
}
if (safe_ExceptionOccurred(env)) {
return 0;
}

View File

@@ -33,7 +33,6 @@ class AwtWin32GraphicsConfig {
public:
/* sun.awt.Win32GraphicsConfig fields and method IDs */
static jfieldID win32GCVisualID;
static RECT getMonitorBounds(int screen, const UCoordSpace& space);
};
#endif /* AWT_WIN32GRAPHICSCONFIG_H */

View File

@@ -41,7 +41,6 @@
#include <sun_awt_Win32GraphicsDevice.h>
#include "awt_Canvas.h"
#include "awt_Win32GraphicsDevice.h"
#include "awt_Win32GraphicsConfig.h"
#include "awt_Window.h"
#include "java_awt_Transparency.h"
#include "java_awt_color_ColorSpace.h"
@@ -78,6 +77,7 @@ AwtWin32GraphicsDevice::AwtWin32GraphicsDevice(int screen,
this->devicesArray = arr;
this->scaleX = 1;
this->scaleY = 1;
disableScaleAutoRefresh = FALSE;
javaDevice = NULL;
colorData = new ImgColorData;
colorData->grayscale = GS_NOTGRAY;
@@ -629,47 +629,51 @@ void AwtWin32GraphicsDevice::SetScale(float sx, float sy)
scaleY = sy;
}
int AwtWin32GraphicsDevice::ScaleUpX(int x, const UCoordRelativity& relativity)
int AwtWin32GraphicsDevice::ScaleUpX(int x)
{
return relativity == RELATIVE_COORD ? ClipRound(x * scaleX) : ScaleUpDX(x);
return ClipRound(x * scaleX);
}
// scale up the delta [x - device.x]
int AwtWin32GraphicsDevice::ScaleUpDX(int x)
int AwtWin32GraphicsDevice::ScaleUpAbsX(int x)
{
RECT devBounds = AwtWin32GraphicsConfig::getMonitorBounds(screen, DEVICE_SPACE);
return devBounds.left + ClipRound((x - devBounds.left) * scaleX);
LONG screen = pMonitorInfo->rcMonitor.left;
return screen + ClipRound((x - screen) * scaleX);
}
int AwtWin32GraphicsDevice::ScaleUpY(int y, const UCoordRelativity& relativity)
int AwtWin32GraphicsDevice::ScaleUpY(int y)
{
return relativity == RELATIVE_COORD ? ClipRound(y * scaleY) : ScaleUpDY(y);
return ClipRound(y * scaleY);
}
// scale up the delta [y - device.y]
int AwtWin32GraphicsDevice::ScaleUpDY(int y)
int AwtWin32GraphicsDevice::ScaleUpAbsY(int y)
{
RECT devBounds = AwtWin32GraphicsConfig::getMonitorBounds(screen, DEVICE_SPACE);
return devBounds.top + ClipRound((y - devBounds.top) * scaleY);
LONG screen = pMonitorInfo->rcMonitor.top;
return screen + ClipRound((y - screen) * scaleY);
}
int AwtWin32GraphicsDevice::ScaleDownX(int x, const UCoordRelativity& relativity)
int AwtWin32GraphicsDevice::ScaleDownX(int x)
{
return relativity == RELATIVE_COORD ? ClipRound(x / scaleX) : ScaleDownDX(x);
return ClipRound(x / scaleX);
}
// scale down the delta [x - device.x]
int AwtWin32GraphicsDevice::ScaleDownDX(int x)
int AwtWin32GraphicsDevice::ScaleDownAbsX(int x)
{
RECT devBounds = AwtWin32GraphicsConfig::getMonitorBounds(screen, USER_SPACE);
return devBounds.left + ClipRound((x - devBounds.left) / scaleX);
LONG screen = pMonitorInfo->rcMonitor.left;
return screen + ClipRound((x - screen) / scaleX);
}
int AwtWin32GraphicsDevice::ScaleDownY(int y, const UCoordRelativity& relativity)
int AwtWin32GraphicsDevice::ScaleDownY(int y)
{
return relativity == RELATIVE_COORD ? ClipRound(y / scaleY) : ScaleDownDY(y);
return ClipRound(y / scaleY);
}
int AwtWin32GraphicsDevice::ScaleDownAbsY(int y)
{
LONG screen = pMonitorInfo->rcMonitor.top;
return screen + ClipRound((y - screen) / scaleY);
}
int AwtWin32GraphicsDevice::ClipRound(double value)
{
value -= 0.5;
@@ -686,13 +690,6 @@ int AwtWin32GraphicsDevice::ClipRound(double value)
return (int)ceil(value);
}
// scale down the delta [y - device.y]
int AwtWin32GraphicsDevice::ScaleDownDY(int y)
{
RECT devBounds = AwtWin32GraphicsConfig::getMonitorBounds(screen, USER_SPACE);
return devBounds.top + ClipRound((y - devBounds.top) / scaleY);
}
// scale down the delta [pt.xy - device.xy]
void AwtWin32GraphicsDevice::ScaleDownDPoint(POINT *pt)
{
@@ -709,11 +706,13 @@ void AwtWin32GraphicsDevice::ScaleDownDPoint(POINT *pt)
void AwtWin32GraphicsDevice::InitDesktopScales()
{
float dpiX = -1.0f;
float dpiY = -1.0f;
GetScreenDpi(GetMonitor(), &dpiX, &dpiY);
if (dpiX > 0 && dpiY > 0) {
SetScale(dpiX / 96, dpiY / 96);
if (!disableScaleAutoRefresh) {
float dpiX = -1.0f;
float dpiY = -1.0f;
GetScreenDpi(GetMonitor(), &dpiX, &dpiY);
if (dpiX > 0 && dpiY > 0) {
SetScale(dpiX / 96, dpiY / 96);
}
}
}
@@ -737,6 +736,12 @@ void AwtWin32GraphicsDevice::DisableOffscreenAcceleration()
// REMIND: noop for now
}
void AwtWin32GraphicsDevice::DisableScaleAutoRefresh()
{
disableScaleAutoRefresh = TRUE;
}
/**
* Invalidates the GraphicsDevice object associated with this
* device by disabling offscreen acceleration and calling
@@ -797,6 +802,22 @@ void AwtWin32GraphicsDevice::ResetAllMonitorInfo()
}
}
/**
* This function updates the scale factor for all monitors on the system.
*/
void AwtWin32GraphicsDevice::ResetAllDesktopScales()
{
if (!Devices::GetInstance()){
return;
}
Devices::InstanceAccess devices;
int devicesNum = devices->GetNumDevices();
for (int deviceIndex = 0; deviceIndex < devicesNum; deviceIndex++) {
devices->GetDevice(deviceIndex)->InitDesktopScales();
}
}
void AwtWin32GraphicsDevice::DisableOffscreenAccelerationForDevice(
HMONITOR hMonitor)
{
@@ -1444,6 +1465,7 @@ JNIEXPORT void JNICALL
AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
if (device != NULL ) {
device->DisableScaleAutoRefresh();
device->SetScale(scaleX, scaleY);
}
}

View File

@@ -33,7 +33,6 @@ extern "C" {
#include "colordata.h"
#include "awt_Palette.h"
#include "Devices.h"
#include "awt_Util.h"
class AwtPalette;
class Devices;
@@ -66,19 +65,20 @@ public:
int GetDeviceIndex() { return screen; }
void Release();
void DisableOffscreenAcceleration();
void DisableScaleAutoRefresh();
void Invalidate(JNIEnv *env);
void InitDesktopScales();
void SetScale(float scaleX, float scaleY);
float GetScaleX();
float GetScaleY();
int ScaleUpX(int x, const UCoordRelativity& relativity = RELATIVE_COORD);
int ScaleUpY(int y, const UCoordRelativity& relativity = RELATIVE_COORD);
int ScaleDownX(int x, const UCoordRelativity& relativity = RELATIVE_COORD);
int ScaleDownY(int y, const UCoordRelativity& relativity = RELATIVE_COORD);
int ScaleUpDX(int x);
int ScaleUpDY(int y);
int ScaleDownDX(int x);
int ScaleDownDY(int y);
int ScaleUpX(int x);
int ScaleUpAbsX(int x);
int ScaleUpY(int y);
int ScaleUpAbsY(int y);
int ScaleDownX(int x);
int ScaleDownAbsX(int x);
int ScaleDownY(int y);
int ScaleDownAbsY(int y);
static void ScaleDownDPoint(POINT *pt);
static int DeviceIndexForWindow(HWND hWnd);
@@ -94,6 +94,7 @@ public:
static HMONITOR GetMonitor(int deviceIndex);
static LPMONITORINFO GetMonitorInfo(int deviceIndex);
static void ResetAllMonitorInfo();
static void ResetAllDesktopScales();
static BOOL IsPrimaryPalettized() { return primaryPalettized; }
static int GetDefaultDeviceIndex() { return primaryIndex; }
static void DisableOffscreenAccelerationForDevice(HMONITOR hMonitor);
@@ -124,6 +125,7 @@ private:
Devices *devicesArray;
float scaleX;
float scaleY;
BOOL disableScaleAutoRefresh;
static HDC MakeDCFromMonitor(HMONITOR);
static int ClipRound(double value);

View File

@@ -40,7 +40,6 @@
#include "awt_IconCursor.h"
#include "ComCtl32Util.h"
#include "math.h"
#include "awt_Util.h"
#include "java_awt_Insets.h"
#include <java_awt_Container.h>
@@ -155,17 +154,6 @@ struct SetFullScreenExclusiveModeStateStruct {
jboolean isFSEMState;
};
/* // struct for _WindowDPIChange() method
struct ScaleStruct {
jobject window;
jint prevScreen;
jfloat prevScaleX;
jfloat prevScaleY;
jint screen;
jfloat scaleX;
jfloat scaleY;
}; */
struct OverrideHandle {
jobject frame;
HWND handle;
@@ -181,10 +169,6 @@ jfieldID AwtWindow::autoRequestFocusID;
jfieldID AwtWindow::securityWarningWidthID;
jfieldID AwtWindow::securityWarningHeightID;
jfieldID AwtWindow::sysXID;
jfieldID AwtWindow::sysYID;
jfieldID AwtWindow::sysWID;
jfieldID AwtWindow::sysHID;
jfieldID AwtWindow::windowTypeID;
jfieldID AwtWindow::sysInsetsID;
jmethodID AwtWindow::notifyWindowStateChangedMID;
@@ -248,13 +232,11 @@ AwtWindow::AwtWindow() {
m_alwaysOnTop = false;
fullScreenExclusiveModeState = FALSE;
/*m_winSizeMove = FALSE;
m_winSizeMove = FALSE;
prevScaleRec.screen = -1;
prevScaleRec.scaleX = -1.0f;
prevScaleRec.scaleY = -1.0f;*/
prevScaleRec.scaleY = -1.0f;
m_overriddenHwnd = NULL;
::SetRect(&m_boundsOnDPIChange, 0, 0, 0, 0);
}
AwtWindow::~AwtWindow()
@@ -983,51 +965,6 @@ MsgRouting AwtWindow::WmTimer(UINT_PTR timerID)
return mrConsume;
}
MsgRouting AwtWindow::WmDPIChanged(UINT xDPI, UINT yDPI, RECT* bounds) {
if (!::IsWindowVisible(GetHWnd())) {
// may diverge with Component::Reshape in this state
return mrDoDefault;
}
if (IsInMoveResizeLoop()) {
// Dragged with mouse to new screen. In this case the new bounds must be set immediately
// or otherwise OS will reset it back to the previous values.
::SetWindowPos(GetHWnd(), NULL,
bounds->left, bounds->top,
bounds->right - bounds->left, bounds->bottom - bounds->top,
SWP_NOZORDER | SWP_NOACTIVATE);
} else {
// Either DPI of this screen changed, or the window moved to a new screen by a shortcut (shift+meta+arrow).
// Store the new bounds for async update.
::CopyRect(&m_boundsOnDPIChange, bounds);
}
return mrConsume;
}
void AwtWindow::_AdjustBoundsOnDPIChange(void* param)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
jobject jwnd = (jobject)param;
PDATA pData;
JNI_CHECK_PEER_GOTO(jwnd, done);
AwtWindow *window = (AwtWindow *)pData;
int x = window->m_boundsOnDPIChange.left;
int y = window->m_boundsOnDPIChange.top;
int width = window->m_boundsOnDPIChange.right - window->m_boundsOnDPIChange.left;
int height = window->m_boundsOnDPIChange.bottom - window->m_boundsOnDPIChange.top;
if (width > 0 && height > 0) {
::SetRect(&window->m_boundsOnDPIChange, 0, 0, 0, 0); // drop it
::SetWindowPos(window->GetHWnd(), NULL,
x, y, width, height,
SWP_NOZORDER | SWP_NOACTIVATE);
}
done:
env->DeleteGlobalRef(jwnd);
}
// The security warning is visible if:
// 1. The window has the keyboard window focus, OR
// 2. The mouse pointer is located within the window bounds,
@@ -1197,20 +1134,20 @@ AwtWindow* AwtWindow::Create(jobject self, jobject parent)
// specify WS_EX_TOOLWINDOW to remove parentless windows from taskbar
exStyle |= WS_EX_TOOLWINDOW;
}
jint x = env->GetIntField(target, AwtComponent::xID);
jint y = env->GetIntField(target, AwtComponent::yID);
jint width = env->GetIntField(target, AwtComponent::widthID);
jint height = env->GetIntField(target, AwtComponent::heightID);
window->CreateHWnd(env, L"",
style, exStyle,
0, 0, 0, 0,
x, y, width, height,
(awtParent != NULL) ? awtParent->GetHWnd() : NULL,
NULL,
::GetSysColor(COLOR_WINDOWTEXT),
::GetSysColor(COLOR_WINDOW),
self);
jint x = env->GetIntField(target, AwtComponent::xID);
jint y = env->GetIntField(target, AwtComponent::yID);
jint width = env->GetIntField(target, AwtComponent::widthID);
jint height = env->GetIntField(target, AwtComponent::heightID);
/*
* Initialize icon as inherited from parent if it exists
*/
@@ -1220,13 +1157,7 @@ AwtWindow* AwtWindow::Create(jobject self, jobject parent)
window->m_iconInherited = TRUE;
}
window->DoUpdateIcon();
/*
* Reshape here instead of during create, so that a WM_NCCALCSIZE
* is sent.
*/
window->Reshape(x, y, width, height);
window->RecalcNonClient();
}
} catch (...) {
env->DeleteLocalRef(target);
@@ -1284,6 +1215,48 @@ void AwtWindow::moveToDefaultLocation() {
VERIFY(::SetWindowPos(GetHWnd(), NULL, defLoc.left, defLoc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER));
}
/**
* Override AwtComponent::Reshape() to handle absolute screen coordinates used
* by the top-level windows.
*/
void AwtWindow::Reshape(int x, int y, int w, int h) {
if (IsEmbeddedFrame()) {
// Not the "real" top level window
return AwtComponent::Reshape(x, y, w, h);
}
// Yes, use x,y in user's space to find the nearest monitor in device space.
POINT pt = {x + w / 2, y + h / 2};
Devices::InstanceAccess devices;
HMONITOR monitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
int screen = AwtWin32GraphicsDevice::GetScreenFromHMONITOR(monitor);
AwtWin32GraphicsDevice *device = devices->GetDevice(screen);
// Try to set the correct size and jump to the correct location, even if it is
// on the different monitor. Note that for the "size" we use the current
// monitor, so the WM_DPICHANGED will adjust it for the "target" monitor.
int scaleUpAbsX = device == NULL ? x : device->ScaleUpAbsX(x);
int scaleUpAbsY = device == NULL ? y : device->ScaleUpAbsY(y);
ReshapeNoScale(scaleUpAbsX, scaleUpAbsY, ScaleUpX(w), ScaleUpY(h));
// The window manager may tweak the size for different reasons, so try
// to make sure our window has the correct size in the user's space.
// NOOP if the size was changed already or changing is in progress.
RECT rc;
::GetWindowRect(GetHWnd(), &rc);
ReshapeNoScale(rc.left, rc.top, ScaleUpX(w), ScaleUpY(h));
// the window manager may ignore our "SetWindowPos" request, in this,
// case the WmMove/WmSize will not come and we need to manually resync
// the "java.awt.Window" locations, because "java.awt.Window" already
// uses location ignored by the window manager.
::GetWindowRect(GetHWnd(), &rc);
if (x != ScaleDownAbsX(rc.left) || y != ScaleDownAbsY(rc.top)) {
WmMove(rc.left, rc.top);
}
int userW = ScaleDownX(rc.right - rc.left);
int userH = ScaleDownY(rc.bottom - rc.top);
if (w != userW || h != userH) {
WmSize(SIZENORMAL, rc.right - rc.left, rc.bottom - rc.top);
}
}
void AwtWindow::Show()
{
m_visible = true;
@@ -1870,6 +1843,16 @@ MsgRouting AwtWindow::WmShowWindow(BOOL show, UINT status)
return AwtCanvas::WmShowWindow(show, status);
}
void AwtWindow::WmDPIChanged(const LPARAM &lParam) {
// need to update the scales now, otherwise the ReshapeNoScale() will
// calculate the bounds wrongly
AwtWin32GraphicsDevice::ResetAllDesktopScales();
RECT *r = (RECT *) lParam;
ReshapeNoScale(r->left, r->top, r->right - r->left, r->bottom - r->top);
CheckIfOnNewScreen(true);
}
MsgRouting AwtWindow::WmEraseBkgnd(HDC hDC, BOOL& didErase)
{
if (!IsUndecorated()) {
@@ -1900,61 +1883,21 @@ MsgRouting AwtWindow::WmMove(int x, int y)
return mrDoDefault;
}
if (m_screenNum == -1) {
// Set initial value
m_screenNum = GetScreenImOn();
}
else if (CheckIfOnNewScreen()) {
DoUpdateIcon();
}
// Check for the new screen and update the java peer
CheckIfOnNewScreen(false); // postpone if different DPI
/* Update the java AWT target component's fields directly */
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
if (env->EnsureLocalCapacity(1) < 0) {
return mrConsume;
}
jobject peer = GetPeer(env);
jobject target = env->GetObjectField(peer, AwtObject::targetID);
jobject target = GetTarget(env);
URectBounds rect = UGetWindowRectBounds(GetHWnd());
AwtWin32GraphicsDevice* device = UGetDeviceByBounds(rect, this);
RECT rect;
::GetWindowRect(GetHWnd(), &rect);
int usrX = device->ScaleDownX(rect.x);
int usrY = device->ScaleDownY(rect.y);
// [tav] Convert x/y to user space, asymmetrically to AwtComponent::Reshape.
AwtComponent* parent = GetParent();
if (parent != NULL && (device->GetScaleX() > 1 || device->GetScaleY() > 1)) {
RECT parentInsets;
parent->GetInsets(&parentInsets);
// Convert the owner's client area origin to user space
int parentInsetsUsrX = device->ScaleDownX(parentInsets.left);
int parentInsetsUsrY = device->ScaleDownY(parentInsets.top);
RECT parentRect;
VERIFY(::GetWindowRect(parent->GetHWnd(), &parentRect));
// Convert the owner's origin to user space
int parentUsrX = device->ScaleDownX(parentRect.left);
int parentUsrY = device->ScaleDownY(parentRect.top);
// Calc the offset from the owner's client area in device space
int offsetDevX = rect.x - parentRect.left - parentInsets.left;
int offsetDevY = rect.y - parentRect.top - parentInsets.top;
// Convert the offset to user space
int offsetUsrX = device->ScaleDownX(offsetDevX);
int offsetUsrY = device->ScaleDownY(offsetDevY);
// Finally calc the window's location based on the frame's and its insets user space values.
usrX = parentUsrX + parentInsetsUsrX + offsetUsrX;
usrY = parentUsrY + parentInsetsUsrY + offsetUsrY;
}
(env)->SetIntField(target, AwtComponent::xID, usrX);
(env)->SetIntField(target, AwtComponent::yID, usrY);
(env)->SetIntField(peer, AwtWindow::sysXID, rect.x);
(env)->SetIntField(peer, AwtWindow::sysYID, rect.y);
(env)->SetIntField(target, AwtComponent::xID, ScaleDownAbsX(rect.left));
(env)->SetIntField(target, AwtComponent::yID, ScaleDownAbsY(rect.top));
SendComponentEvent(java_awt_event_ComponentEvent_COMPONENT_MOVED);
env->DeleteLocalRef(target);
@@ -1996,18 +1939,27 @@ MsgRouting AwtWindow::WmSizing()
return mrDoDefault;
}
/*MsgRouting AwtWindow::WmEnterSizeMove()
MsgRouting AwtWindow::WmEnterSizeMove()
{
m_winSizeMove = TRUE;
// Below is a workaround, see CheckWindowDPIChange
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice* device = devices->GetDevice(m_screenNum);
if (device) {
prevScaleRec.screen = m_screenNum;
prevScaleRec.scaleX = device->GetScaleX();
prevScaleRec.scaleY = device->GetScaleY();
}
// Above is a workaround
return mrDoDefault;
}
MsgRouting AwtWindow::WmExitSizeMove()
{
m_winSizeMove = FALSE;
CheckWindowDPIChange();
CheckWindowDPIChange(); // workaround
return mrDoDefault;
}*/
}
/*
* Override AwtComponent's size handling to first update the
@@ -2022,6 +1974,8 @@ MsgRouting AwtWindow::WmSize(UINT type, int w, int h)
UpdateSecurityWarningVisibility();
return mrDoDefault;
}
// Check for the new screen and update the java peer
CheckIfOnNewScreen(false); // postpone if different DPI
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
if (env->EnsureLocalCapacity(1) < 0)
@@ -2029,16 +1983,8 @@ MsgRouting AwtWindow::WmSize(UINT type, int w, int h)
jobject target = GetTarget(env);
// fix 4167248 : ensure the insets are up-to-date before using
BOOL insetsChanged = UpdateInsets(NULL);
int newWidth = w + m_insets.left + m_insets.right;
int newHeight = h + m_insets.top + m_insets.bottom;
URectBounds rect = UGetWindowRectBounds(GetHWnd());
AwtWin32GraphicsDevice* device = UGetDeviceByBounds(rect, this);
(env)->SetIntField(target, AwtComponent::widthID, device->ScaleDownX(newWidth));
(env)->SetIntField(target, AwtComponent::heightID, device->ScaleDownY(newHeight));
jobject peer = GetPeer(env);
(env)->SetIntField(peer, AwtWindow::sysWID, newWidth);
(env)->SetIntField(peer, AwtWindow::sysHID, newHeight);
(env)->SetIntField(target, AwtComponent::widthID, ScaleDownX(w));
(env)->SetIntField(target, AwtComponent::heightID, ScaleDownY(h));
if (!AwtWindow::IsResizing()) {
WindowResized();
@@ -2120,6 +2066,11 @@ LRESULT AwtWindow::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
LRESULT retValue = 0L;
switch(message) {
case WM_DPICHANGED: {
WmDPIChanged(lParam);
mr = mrConsume;
break;
}
case WM_GETICON:
mr = WmGetIcon(wParam, retValue);
break;
@@ -2272,98 +2223,81 @@ int AwtWindow::GetScreenImOn() {
return scrnNum;
}
/* Check to see if we've been moved onto another screen.
/*
* Check to see if we've been moved onto another screen.
* If so, update internal data, surfaces, etc.
*/
BOOL AwtWindow::CheckIfOnNewScreen() {
void AwtWindow::CheckIfOnNewScreen(BOOL force) {
int curScrn = GetScreenImOn();
if (curScrn != m_screenNum) { // we've been moved
// if moved from one monitor to another with different DPI, we should
// update the m_screenNum only if the size was updated as well in the
// WM_DPICHANGED.
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice* oldDevice = devices->GetDevice(m_screenNum);
AwtWin32GraphicsDevice* newDevice = devices->GetDevice(curScrn);
if (!force && m_winSizeMove && oldDevice && newDevice) {
if (oldDevice->GetScaleX() != newDevice->GetScaleX()
|| oldDevice->GetScaleY() != newDevice->GetScaleY()) {
// scales are different, wait for WM_DPICHANGED
return;
}
}
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
jclass peerCls = env->GetObjectClass(m_peerObject);
DASSERT(peerCls);
CHECK_NULL_RETURN(peerCls, TRUE);
CHECK_NULL(peerCls);
jmethodID draggedID = env->GetMethodID(peerCls, "draggedToNewScreen",
"()V");
DASSERT(draggedID);
if (draggedID == NULL) {
env->DeleteLocalRef(peerCls);
return TRUE;
return;
}
env->CallVoidMethod(m_peerObject, draggedID);
m_screenNum = curScrn;
env->DeleteLocalRef(peerCls);
return TRUE;
DoUpdateIcon();
}
return FALSE;
return;
}
/* void AwtWindow::CheckWindowDPIChange() {
if (prevScaleRec.screen != -1 ) {
float prevScaleX = prevScaleRec.scaleX;
float prevScaleY = prevScaleRec.scaleY;
if (prevScaleX >= 1 && prevScaleY >= 1) {
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice* device = devices->GetDevice(m_screenNum);
if (device) {
float scaleX = device->GetScaleX();
float scaleY = device->GetScaleY();
if (prevScaleX != scaleX || prevScaleY != scaleY) {
WindowDPIChange(prevScaleRec.screen, prevScaleX, prevScaleY,
m_screenNum, scaleX, scaleY);
void AwtWindow::CheckWindowDPIChange() {
if (prevScaleRec.screen != -1 && prevScaleRec.screen != m_screenNum) {
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice *device = devices->GetDevice(m_screenNum);
if (device) {
if (prevScaleRec.scaleX != device->GetScaleX()
|| prevScaleRec.scaleY != device->GetScaleY()) {
RECT rect;
::GetWindowRect(GetHWnd(), &rect);
int x = rect.left;
int y = rect.top;
int w = rect.right - rect.left;
int h = rect.bottom - rect.top;
RECT bounds;
if (MonitorBounds(device->GetMonitor(), &bounds)) {
x = x < bounds.left ? bounds.left : x;
y = y < bounds.top ? bounds.top : y;
x = (x + w > bounds.right) ? bounds.right - w : x;
y = (y + h > bounds.bottom) ? bounds.bottom - h : y;
}
ReshapeNoScale(x, y, w, h);
}
}
prevScaleRec.screen = -1;
prevScaleRec.scaleX = -1.0f;
prevScaleRec.scaleY = -1.0f;
}
}
void AwtWindow::WindowDPIChange(int prevScreen,
float prevScaleX, float prevScaleY,
int screen, float scaleX,
float scaleY)
{
int x;
int y;
int w;
int h;
RECT rect;
if (prevScaleX == scaleX && prevScaleY == scaleY) {
return;
}
::GetWindowRect(GetHWnd(), &rect);
x = rect.left;
y = rect.top;
w = (rect.right - rect.left) * scaleX / prevScaleX;
h = (rect.bottom - rect.top) * scaleY / prevScaleY;
if (prevScreen != screen) {
Devices::InstanceAccess devices;
AwtWin32GraphicsDevice* device = devices->GetDevice(screen);
if (device) {
RECT bounds;
if (MonitorBounds(device->GetMonitor(), &bounds)) {
x = x < bounds.left ? bounds.left : x;
y = y < bounds.top ? bounds.top : y;
x = (x + w > bounds.right) ? bounds.right - w : x;
y = (y + h > bounds.bottom) ? bounds.bottom - h : y;
}
}
}
ReshapeNoScale(x, y, w, h);
} */
BOOL AwtWindow::IsFocusableWindow() {
/*
* For Window/Frame/Dialog to accept focus it should:
@@ -2750,15 +2684,11 @@ void AwtWindow::_ReshapeFrame(void *param)
{
env->SetIntField(target, AwtComponent::widthID,
w = minWidth);
env->SetIntField(peer, AwtWindow::sysWID,
w);
}
if (h < minHeight)
{
env->SetIntField(target, AwtComponent::heightID,
h = minHeight);
env->SetIntField(peer, AwtWindow::sysHID,
h);
}
}
env->DeleteLocalRef(target);
@@ -3425,40 +3355,6 @@ void AwtWindow::_GetNativeWindowSize(void* param) {
env->DeleteGlobalRef(self);
}
/*void AwtWindow::_WindowDPIChange(void* param)
{
JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
ScaleStruct *ss = (ScaleStruct *)param;
jobject self = ss->window;
jint prevScreen = ss->prevScreen;
jfloat prevScaleX = ss->prevScaleX;
jfloat prevScaleY = ss->prevScaleY;
jint screen = ss->screen;
jfloat scaleX = ss->scaleX;
jfloat scaleY = ss->scaleY;
PDATA pData;
JNI_CHECK_PEER_GOTO(self, ret);
AwtWindow *window = (AwtWindow *)pData;
if (window->m_winSizeMove) {
if (window->prevScaleRec.screen == -1) {
window->prevScaleRec.screen = prevScreen;
window->prevScaleRec.scaleX = prevScaleX;
window->prevScaleRec.scaleY = prevScaleY;
}
}
else {
window->WindowDPIChange(prevScreen, prevScaleX, prevScaleY,
screen, scaleX, scaleY);
}
ret:
env->DeleteGlobalRef(self);
delete ss;
}*/
extern "C" int getSystemMetricValue(int msgType);
extern "C" {
@@ -3515,11 +3411,6 @@ Java_sun_awt_windows_WWindowPeer_initIDs(JNIEnv *env, jclass cls)
{
TRY;
CHECK_NULL(AwtWindow::sysXID = env->GetFieldID(cls, "sysX", "I"));
CHECK_NULL(AwtWindow::sysYID = env->GetFieldID(cls, "sysY", "I"));
CHECK_NULL(AwtWindow::sysWID = env->GetFieldID(cls, "sysW", "I"));
CHECK_NULL(AwtWindow::sysHID = env->GetFieldID(cls, "sysH", "I"));
CHECK_NULL(AwtWindow::sysInsetsID = env->GetFieldID(cls, "sysInsets", "Ljava/awt/Insets;"));
AwtWindow::windowTypeID = env->GetFieldID(cls, "windowType",
@@ -4161,49 +4052,6 @@ Java_sun_awt_windows_WWindowPeer_repositionSecurityWarning(JNIEnv *env,
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WWindowPeer
* Method: _AdjustBoundsOnDPIChange
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_sun_awt_windows_WWindowPeer_adjustBoundsOnDPIChange(JNIEnv *env, jobject self)
{
TRY;
AwtToolkit::GetInstance().InvokeFunction(AwtWindow::_AdjustBoundsOnDPIChange, (void*)env->NewGlobalRef(self));
// global refs deleted in _AdjustBoundsOnDisplayChange
CATCH_BAD_ALLOC;
}
/*
* Class: sun_awt_windows_WWindowPeer
* Method: windowDPIChange
* Signature: (IFFIFF)V
*/
/*JNIEXPORT void JNICALL
Java_sun_awt_windows_WWindowPeer_windowDPIChange(JNIEnv *env, jobject self,
jint prevScreen, jfloat prevScaleX, jfloat prevScaleY,
jint screen, jfloat scaleX, jfloat scaleY)
{
TRY;
ScaleStruct *ss = new ScaleStruct;
ss->window = env->NewGlobalRef(self);
ss->prevScreen = prevScreen;
ss->prevScaleX = prevScaleX;
ss->prevScaleY = prevScaleY;
ss->screen = screen;
ss->scaleX = scaleX;
ss->scaleY = scaleY;
AwtToolkit::GetInstance().InvokeFunction(AwtWindow::_WindowDPIChange, ss);
// global refs and ss are deleted in _WindowDPIChange
CATCH_BAD_ALLOC;
}*/
/*
* Class: sun_awt_windows_WLightweightFramePeer
* Method: overrideNativeHandle

View File

@@ -58,12 +58,6 @@ public:
static jfieldID securityWarningHeightID;
/* sun.awt.windows.WWindowPeer field and method IDs */
// The coordinates at the peer.
static jfieldID sysXID;
static jfieldID sysYID;
static jfieldID sysWID;
static jfieldID sysHID;
static jfieldID sysInsetsID;
static jfieldID windowTypeID;
@@ -131,6 +125,7 @@ public:
return FALSE;
}
virtual void Reshape(int x, int y, int w, int h);
virtual void Invalidate(RECT* r);
virtual void Show();
virtual void SetResizable(BOOL isResizable);
@@ -138,7 +133,7 @@ public:
virtual void RecalcNonClient();
virtual void RedrawNonClient();
virtual int GetScreenImOn();
virtual BOOL CheckIfOnNewScreen();
virtual void CheckIfOnNewScreen(BOOL force);
virtual void Grab();
virtual void Ungrab();
virtual void Ungrab(BOOL doPost);
@@ -180,8 +175,8 @@ public:
virtual MsgRouting WmMove(int x, int y);
virtual MsgRouting WmSize(UINT type, int w, int h);
virtual MsgRouting WmSizing();
/*virtual MsgRouting WmEnterSizeMove();
virtual MsgRouting WmExitSizeMove();*/
virtual MsgRouting WmEnterSizeMove();
virtual MsgRouting WmExitSizeMove();
virtual MsgRouting WmPaint(HDC hDC);
virtual MsgRouting WmSettingChange(UINT wFlag, LPCTSTR pszSection);
virtual MsgRouting WmNcCalcSize(BOOL fCalcValidRects,
@@ -197,8 +192,6 @@ public:
virtual MsgRouting HandleEvent(MSG *msg, BOOL synthetic);
virtual void WindowResized();
MsgRouting WmDPIChanged(UINT xDPI, UINT yDPI, RECT* bounds);
static jboolean _RequestWindowFocus(void *param);
virtual BOOL AwtSetActiveWindow(BOOL isMouseEventCause = FALSE, UINT hittest = HTCLIENT);
@@ -255,8 +248,6 @@ public:
static void _RepositionSecurityWarning(void* param);
static void _SetFullScreenExclusiveModeState(void* param);
static void _GetNativeWindowSize(void* param);
// static void _WindowDPIChange(void* param);
static void _AdjustBoundsOnDPIChange(void* param);
static void _OverrideHandle(void *param);
inline static BOOL IsResizing() {
@@ -296,7 +287,6 @@ private:
// from its hierarchy when shown. Currently applied to instances of
// javax/swing/Popup$HeavyWeightWindow class.
BOOL m_isIgnoringMouseEvents; // Make window transparent for mouse events (used for JCEF)
RECT m_boundsOnDPIChange; /* bounds to asynchronously adjust on DPI change */
// SetTranslucency() is the setter for the following two fields
BYTE m_opacity; // The opacity level. == 0xff by default (when opacity mode is disabled)
@@ -412,19 +402,18 @@ protected:
private:
int m_screenNum;
/*typedef struct {
typedef struct {
jint screen;
jfloat scaleX;
jfloat scaleY;
} ScaleRec;
BOOL m_winSizeMove;
ScaleRec prevScaleRec;*/
ScaleRec prevScaleRec;
void InitOwner(AwtWindow *owner);
/*void CheckWindowDPIChange();
void WindowDPIChange(int prevScreen, float prevScaleX, float prevScaleY,
int newScreen, float scaleX, float scaleY); */
void CheckWindowDPIChange();
void WmDPIChanged(const LPARAM &lParam);
Type m_windowType;
void InitType(JNIEnv *env, jobject peer);

View File

@@ -41,6 +41,10 @@ extern const UINT SYSCOMMAND_IMM;
* See winuser.h for details.
*/
#ifndef WM_DPICHANGED
#define WM_DPICHANGED 0x02E0
#endif //WM_DPICHANGED
#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL 0x020A
#endif //WM_MOUSEWHEEL

View File

@@ -614,10 +614,6 @@ class Stream<T> extends ExchangeImpl<T> {
if (contentLength > 0) {
h.setHeader("content-length", Long.toString(contentLength));
}
URI uri = request.uri();
if (uri != null) {
h.setHeader("host", Utils.hostString(request));
}
HttpHeaders sysh = filterHeaders(h.build());
HttpHeaders userh = filterHeaders(request.getUserHeaders());
// Filter context restricted from userHeaders

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2000-2021 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains;
import java.awt.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public interface CustomWindowDecoration {
/*CONST java.awt.Window.*_HIT_SPOT*/
/*CONST java.awt.Window.*_BUTTON*/
/*CONST java.awt.Window.MENU_BAR*/
void setCustomDecorationEnabled(Window window, boolean enabled);
boolean isCustomDecorationEnabled(Window window);
void setCustomDecorationHitTestSpots(Window window, List<Map.Entry<Shape, Integer>> spots);
List<Map.Entry<Shape, Integer>> getCustomDecorationHitTestSpots(Window window);
void setCustomDecorationTitleBarHeight(Window window, int height);
int getCustomDecorationTitleBarHeight(Window window);
@SuppressWarnings("all")
class __Fallback implements CustomWindowDecoration {
private final Method
hasCustomDecoration,
setHasCustomDecoration,
setCustomDecorationHitTestSpots,
setCustomDecorationTitleBarHeight;
private final Field peer;
private final Class<?> wpeer;
__Fallback() throws Exception {
hasCustomDecoration = Window.class.getDeclaredMethod("hasCustomDecoration");
hasCustomDecoration.setAccessible(true);
setHasCustomDecoration = Window.class.getDeclaredMethod("setHasCustomDecoration");
setHasCustomDecoration.setAccessible(true);
wpeer = Class.forName("sun.awt.windows.WWindowPeer");
setCustomDecorationHitTestSpots = wpeer.getDeclaredMethod("setCustomDecorationHitTestSpots", List.class);
setCustomDecorationHitTestSpots.setAccessible(true);
setCustomDecorationTitleBarHeight = wpeer.getDeclaredMethod("setCustomDecorationTitleBarHeight", int.class);
setCustomDecorationTitleBarHeight.setAccessible(true);
peer = Component.class.getDeclaredField("peer");
peer.setAccessible(true);
}
@Override
public void setCustomDecorationEnabled(Window window, boolean enabled) {
if (enabled) {
try {
setHasCustomDecoration.invoke(window);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
@Override
public boolean isCustomDecorationEnabled(Window window) {
try {
return (boolean) hasCustomDecoration.invoke(window);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
return false;
}
}
@Override
public void setCustomDecorationHitTestSpots(Window window, List<Map.Entry<Shape, Integer>> spots) {
List<Rectangle> hitTestSpots = spots.stream().map(e -> e.getKey().getBounds()).collect(Collectors.toList());
try {
setCustomDecorationHitTestSpots.invoke(wpeer.cast(peer.get(window)), hitTestSpots);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
} catch(ClassCastException | NullPointerException ignore) {}
}
@Override
public List<Map.Entry<Shape, Integer>> getCustomDecorationHitTestSpots(Window window) {
return List.of();
}
@Override
public void setCustomDecorationTitleBarHeight(Window window, int height) {
try {
setCustomDecorationTitleBarHeight.invoke(wpeer.cast(peer.get(window)), height);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
} catch(ClassCastException | NullPointerException ignore) {}
}
@Override
public int getCustomDecorationTitleBarHeight(Window window) {
return 0;
}
}
}

View File

@@ -66,6 +66,15 @@ public class JBR {
private JBR() {}
private static <T> T getService(Class<T> interFace, FallbackSupplier<T> fallback) {
T service = getService(interFace);
try {
return service != null ? service : fallback != null ? fallback.get() : null;
} catch (Throwable ignore) {
return null;
}
}
static <T> T getService(Class<T> interFace) {
return api == null ? null : api.getService(interFace);
}
@@ -94,6 +103,11 @@ public class JBR {
<T> T getService(Class<T> interFace);
}
@FunctionalInterface
private interface FallbackSupplier<T> {
T get() throws Throwable;
}
// ========================== Generated metadata ==========================
/**

View File

@@ -104,22 +104,30 @@ public class Gensrc {
for (Path module : modules.paths) {
Path f = module.resolve("share/classes").resolve(file);
if (Files.exists(f)) {
Pattern namePattern = compile(name.replaceAll("\\*", "[a-zA-Z0-9_]+") + "\\s*=");
Pattern statementPattern = compile("((?:(?:public|protected|private|static|final)\\s+){2,3})([a-zA-Z0-9]+)\\s+([^;]+);");
Pattern namePattern = compile(name.replaceAll("\\*", "\\\\w+"));
Pattern statementPattern = compile(
"((?:(?:MODS) ){2,3})([a-zA-Z0-9]+) (FIELD(?:, FIELD)*);"
.replaceAll("MODS", "public|protected|private|static|final")
.replaceAll("FIELD", "\\\\w+ = [\\\\w\"']+ ")
.replaceAll(" ", "\\\\s+")
.replaceAll(" ", "\\\\s*")
);
Matcher statementMatcher = statementPattern.matcher(Files.readString(f));
while (statementMatcher.find()) {
String mods = statementMatcher.group(1);
if (!mods.contains("static") || !mods.contains("final")) continue;
for (String s : statementMatcher.group(3).split(",")) {
if (!namePattern.matcher(s).find()) continue;
statements.add("public static final " + statementMatcher.group(2) + " " + s.strip() + ";");
s = s.strip();
String nm = s.substring(0, s.indexOf('=')).strip();
if (!namePattern.matcher(nm).matches()) continue;
statements.add("public static final " + statementMatcher.group(2) + " " + s + ";");
}
}
break;
}
}
if (statements.isEmpty()) throw new RuntimeException("Constant not found: " + placeholder);
content = replaceTemplate(content, placeholder, statements, true);
content = replaceTemplate(content, placeholder, statements);
}
}
@@ -129,7 +137,7 @@ public class Gensrc {
return matcher.group(1);
}
private static String replaceTemplate(String src, String placeholder, Iterable<String> statements, boolean compact) {
private static String replaceTemplate(String src, String placeholder, Iterable<String> statements) {
int placeholderIndex = src.indexOf(placeholder);
int indent = 0;
while (placeholderIndex - indent >= 1 && src.charAt(placeholderIndex - indent - 1) == ' ') indent++;
@@ -137,11 +145,8 @@ public class Gensrc {
if (nextLineIndex == 0) nextLineIndex = placeholderIndex + placeholder.length();
String before = src.substring(0, placeholderIndex - indent), after = src.substring(nextLineIndex);
StringBuilder sb = new StringBuilder(before);
boolean firstStatement = true;
for (String s : statements) {
if (!firstStatement && !compact) sb.append('\n');
sb.append(s);
firstStatement = false;
sb.append(s).append('\n');
}
sb.append(after);
return sb.toString();
@@ -156,7 +161,7 @@ public class Gensrc {
Service[] interfaces = findPublicServiceInterfaces();
List<String> statements = new ArrayList<>();
for (Service i : interfaces) statements.add(generateMethodsForService(i));
content = replaceTemplate(content, "/*GENERATED_METHODS*/", statements, false);
content = replaceTemplate(content, "/*GENERATED_METHODS*/", statements);
content = content.replace("/*KNOWN_SERVICES*/",
modules.services.stream().map(s -> "\"" + s + "\"").collect(Collectors.joining(", ")));
content = content.replace("/*KNOWN_PROXIES*/",
@@ -188,7 +193,8 @@ public class Gensrc {
javadocEnd = 0;
}
return new Service(name, javadoc,
content.substring(javadocEnd, indexOfDeclaration).contains("@Deprecated"));
content.substring(javadocEnd, indexOfDeclaration).contains("@Deprecated"),
content.contains("__Fallback"));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
@@ -198,7 +204,7 @@ public class Gensrc {
private static String generateMethodsForService(Service service) {
return ("private static class $__Holder {<DEPRECATED>\n" +
" private static final $ INSTANCE = api != null ? api.getService($.class) : null;\n" +
" private static final $ INSTANCE = getService($.class, <FALLBACK>);\n" +
"}\n" +
"/**\n" +
" * @return true if current runtime has implementation for all methods in {@link $}\n" +
@@ -214,6 +220,7 @@ public class Gensrc {
"public static $ get$() {\n" +
" return $__Holder.INSTANCE;\n" +
"}")
.replace("<FALLBACK>", service.hasFallback ? "$.__Fallback::new" : "null")
.replaceAll("\\$", service.name)
.replace("<JAVADOC>", service.javadoc)
.replaceAll("<DEPRECATED>", service.deprecated ? "\n@Deprecated" : "");
@@ -223,11 +230,13 @@ public class Gensrc {
private final String name;
private final String javadoc;
private final boolean deprecated;
private final boolean hasFallback;
private Service(String name, String javadoc, boolean deprecated) {
private Service(String name, String javadoc, boolean deprecated, boolean hasFallback) {
this.name = name;
this.javadoc = javadoc;
this.deprecated = deprecated;
this.hasFallback = hasFallback;
}
}
}

View File

@@ -6,9 +6,9 @@
# 2. When only new API is added, or some existing API was @Deprecated - increment MINOR, reset PATCH to 0
# 3. For major backwards incompatible API changes - increment MAJOR, reset MINOR and PATCH to 0
VERSION = 1.0.1
VERSION = 1.1.2
# Hash is used to track changes to jetbrains.api, so you would not forget to update version when needed.
# When you make any changes, "make jbr-api" will fail and ask you to update hash and version number here.
HASH = B5362229733433BFF4CE11BB91E9DA3F
HASH = C6871CD21E54119BBB76A1E8D3BD1BBA

View File

@@ -0,0 +1,120 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* 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 java.awt.Button;
import java.awt.Canvas;
import java.awt.Checkbox;
import java.awt.Choice;
import java.awt.Component;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Label;
import java.awt.List;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.ScrollPane;
import java.awt.Scrollbar;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.Window;
/**
* @test
* @key headful
* @bug 8211999
* @run main/othervm SetComponentsBounds
* @run main/othervm -Dsun.java2d.uiScale=1 SetComponentsBounds
* @run main/othervm -Dsun.java2d.uiScale=2.25 SetComponentsBounds
*/
public final class SetComponentsBounds {
private static final int X = 111;
private static final int Y = 222;
private static final int WIDTH = 321;
private static final int HEIGHT = 123;
public static void main(String[] args) throws Exception {
var ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
for (GraphicsDevice gd : ge.getScreenDevices()) {
test(gd.getDefaultConfiguration(), true);
test(gd.getDefaultConfiguration(), false);
}
}
private static void test(GraphicsConfiguration gc, boolean visible) throws Exception {
Rectangle screen = gc.getBounds();
Window frame = new Frame();
try {
frame.setLayout(null); // trigger use the minimum size of
// the peer
frame.setBounds(screen.x + 100, screen.y + 100, 500, 500);
frame.add(new Button());
frame.add(new Canvas());
frame.add(new Checkbox());
frame.add(new Choice());
frame.add(new Label());
frame.add(new List());
frame.add(new Scrollbar());
frame.add(new ScrollPane());
frame.add(new TextArea());
frame.add(new TextField());
for (Component comp : frame.getComponents()) {
comp.setBounds(X, Y, WIDTH, HEIGHT);
}
if (visible) {
frame.setVisible(true);
} else {
frame.pack();
}
Robot robot = new Robot();
robot.waitForIdle();
checkGC(gc, frame);
for (Component comp : frame.getComponents()) {
Rectangle bounds = comp.getBounds();
if (bounds.x != X || bounds.y != Y || bounds.width != WIDTH) {
System.err.println("Screen bounds:" + screen);
System.err.println("Component:" + comp);
throw new RuntimeException("Wrong bounds:" + bounds);
}
if (bounds.height > HEIGHT) {
// different check for HEIGHT, it depends on the font
throw new RuntimeException("Wrong height:" + bounds.height);
}
checkGC(gc, comp);
}
} finally {
frame.dispose();
}
}
private static void checkGC(GraphicsConfiguration gc, Component comp) {
GraphicsConfiguration compGC = comp.getGraphicsConfiguration();
if (compGC != gc) {
System.err.println("Expected GC:" + gc);
System.err.println("Actual GC:" + compGC);
throw new RuntimeException();
}
}
}

View File

@@ -24,7 +24,7 @@
/**
* @test
* @key headful
* @bug 6345003 8171363
* @bug 6345003 8171363 8211999
* @summary grab problems with EmbeddedFrame
* @requires (os.family == "windows")
* @modules java.desktop/java.awt.peer
@@ -67,6 +67,7 @@ public class EmbeddedFrameGrabTest {
final Frame frame = new Frame("AWT Frame");
frame.pack();
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
FramePeer frame_peer = AWTAccessor.getComponentAccessor()
.getPeer(frame);
Class comp_peer_class
@@ -88,6 +89,7 @@ public class EmbeddedFrameGrabTest {
final Panel p = new Panel();
p.setLayout(new BorderLayout());
embedded_frame.add(p, BorderLayout.CENTER);
embedded_frame.setBounds(0, 0, 150, 150);
embedded_frame.validate();
p.add(combo);
p.validate();

View File

@@ -21,6 +21,7 @@
* questions.
*/
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
@@ -30,7 +31,7 @@ import java.awt.Toolkit;
/**
* @test
* @bug 8176359 8231564
* @bug 8176359 8231564 8211999
* @key headful
* @requires (os.family == "windows" | os.family == "mac")
* @summary setMaximizedBounds() should work if set to the screen other than
@@ -58,15 +59,23 @@ public final class MaximizedToOppositeScreenSmall {
Rectangle framAt = gd1.getDefaultConfiguration().getBounds();
framAt.grow(-framAt.width / 2 + 100, -framAt.height / 2 + 100);
for (GraphicsDevice gd2 : gds) {
Rectangle maxTo = gd2.getDefaultConfiguration().getBounds();
maxTo.grow(-maxTo.width / 2 + 120, -maxTo.height / 2 + 120);
Frame frame = new Frame(gd1.getDefaultConfiguration());
try {
frame.setLayout(null); // trigger use the minimum size of
// the peer
frame.setBounds(framAt);
frame.setVisible(true);
robot.waitForIdle();
robot.delay(1000);
Dimension minimumSize = frame.getMinimumSize();
minimumSize.width = Math.max(minimumSize.width, 120);
minimumSize.height = Math.max(minimumSize.height, 120);
Rectangle maxTo = gd2.getDefaultConfiguration().getBounds();
maxTo.grow(-maxTo.width / 2 + minimumSize.width,
-maxTo.height / 2 + minimumSize.height);
frame.setMaximizedBounds(maxTo);
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
robot.waitForIdle();

View File

@@ -0,0 +1,102 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* 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 java.awt.Color;
import java.awt.DisplayMode;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
/**
* @test
* @bug 8211999
* @key headful
* @summary verifies the full-screen window bounds and graphics configuration
*/
public final class FullscreenWindowProps {
public static void main(String[] args) throws Exception {
var ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
if (!gd.isFullScreenSupported()) {
return;
}
Frame frame = new Frame() {
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(Color.GREEN);
g.fillRect(0, 0, getWidth(), getHeight());
}
};
try {
frame.setBackground(Color.MAGENTA);
frame.setVisible(true);
gd.setFullScreenWindow(frame);
Thread.sleep(4000);
for (DisplayMode dm : gd.getDisplayModes()) {
if (dm.getWidth() == 1024 && dm.getHeight() == 768) {
gd.setDisplayMode(dm);
Thread.sleep(4000);
break;
}
}
GraphicsConfiguration frameGC = frame.getGraphicsConfiguration();
Rectangle frameBounds = frame.getBounds();
GraphicsConfiguration screenGC = gd.getDefaultConfiguration();
Rectangle screenBounds = screenGC.getBounds();
if (frameGC != screenGC) {
System.err.println("Expected: " + screenGC);
System.err.println("Actual: " + frameGC);
throw new RuntimeException();
}
checkSize(frameBounds.x, screenBounds.x, "x");
checkSize(frameBounds.y, screenBounds.y, "Y");
checkSize(frameBounds.width, screenBounds.width, "width");
checkSize(frameBounds.height, screenBounds.height, "height");
} finally {
gd.setFullScreenWindow(null);
frame.dispose();
Thread.sleep(10000);
}
}
private static void checkSize(int actual, int expected, String prop) {
if (Math.abs(actual - expected) > 30) { // let's allow size variation,
// the bug is reproduced anyway
System.err.println("Expected: " + expected);
System.err.println("Actual: " + actual);
throw new RuntimeException(prop + " is wrong");
}
}
}

View File

@@ -0,0 +1,152 @@
/*
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* 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 java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.List;
import java.awt.Panel;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.InputEvent;
/**
* @test
* @bug 4909485 8211999
* @key headful
*/
public final class ListMultipleSelectTest {
private static List aList;
private static Panel panel;
private static Point p;
private static Robot robot;
private static int[] selected;
public static void main(String[] args) throws Exception {
Frame frame = new Frame();
try {
panel = new Panel();
aList = new List();
aList.add("Test item1");
aList.add("Test item2");
aList.add("Test item3");
aList.add("Test item4");
frame.setLayout(new FlowLayout()); //list should fill whole frame's space
panel.add(aList);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
panel.setVisible(true);
frame.add(panel);
frame.setVisible(true);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(" InterruptedException. Test failed. ");
}
Dimension listSize = aList.getSize();
p = aList.getLocationOnScreen();
int stepY = listSize.height / aList.getItemCount();
// System.out.println("itemCount = "+aList.getItemCount());
// System.out.println("Size Of aList="+ listSize);
// System.out.println("stepY = "+stepY);
// System.out.println("Point:" +p);
System.out.println("Multiple mode is ON");
aList.setMultipleMode(true);
//=================================================
robot = new Robot();
robot.setAutoDelay(0);
robot.setAutoWaitForIdle(false);
robot.delay(10);
robot.waitForIdle();
//=================================================
for (int i = 0; i < aList.getItemCount(); i++) {
//select all items in the List
mousePress(p.x + listSize.width / 2, p.y + stepY / 2 + stepY * i);
}
selected = aList.getSelectedIndexes();
System.out.println("Multiple mode is ON");
aList.setMultipleMode(true);
int[] newSelected = aList.getSelectedIndexes();
if (!java.util.Arrays.equals(newSelected, selected)) {
throw new RuntimeException(" Incorrect item remains selected " +
"after setMultipleMode(true). ");
}
aList.setMultipleMode(false);
System.out.println("Multiple mode is OFF");
selected = aList.getSelectedIndexes();
if (selected[0] != 3 || selected.length != 1) {
throw new RuntimeException(" Incorrect item remains selected " +
"after setMultipleMode(false) or it is more then one " +
"item remaining.Forward. ");
}
System.out.println("Multiple mode is ON");
aList.setMultipleMode(true);
if (selected[0] != 3 || selected.length != 1) {
throw new RuntimeException(" Incorrect item remains selected " +
"after setMultipleMode(true) or it is more then one " +
"item remaining. ");
}
deselectAll();
for (int i = aList.getItemCount() - 1; i >= 0; i--) {
mousePress(p.x + listSize.width / 2, p.y + stepY / 2 + stepY * i);
}
System.out.println("Multiple mode is OFF");
aList.setMultipleMode(false);
selected = aList.getSelectedIndexes();
if (selected[0] != 0 || selected.length != 1) {
throw new RuntimeException(" Incorrect item remains selected " +
"after setMultipleMode(false) or it is more then one " +
"item remaining.Backward. ");
}
System.out.println("Test succeeded.");
} finally {
frame.dispose();
}
}
private static void mousePress(int x, int y) {
robot.mouseMove(x, y);
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
robot.delay(1000);
}
private static void deselectAll() {
for (int i = 0; i < selected.length; i++) {
aList.deselect(selected[i]);
}
}
}

View File

@@ -24,7 +24,7 @@
/*
@test
@key headful
@bug 8017472
@bug 8017472 8211999
@summary MouseEvent has wrong coordinates when using multiple monitors
@run main MouseEventTest
*/

View File

@@ -24,6 +24,8 @@
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
@@ -38,7 +40,7 @@ import javax.imageio.ImageIO;
/**
* @test
* @key headful
* @bug 8215105
* @bug 8215105 8211999
* @summary tests that Robot can capture the common colors without artifacts
*/
public final class CheckCommonColors {
@@ -48,16 +50,20 @@ public final class CheckCommonColors {
public static void main(final String[] args) throws Exception {
robot = new Robot();
try {
test();
} finally {
frame.dispose();
var ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
for (GraphicsDevice gd : ge.getScreenDevices()) {
try {
test(gd.getDefaultConfiguration().getBounds());
} finally {
frame.dispose();
}
}
}
private static void test() {
private static void test(Rectangle screen) {
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setLocation((int)screen.getCenterX() - 200,
(int)screen.getCenterY() - 200);
frame.setUndecorated(true);
for (final Color color : List.of(Color.WHITE, Color.LIGHT_GRAY,
Color.GRAY, Color.DARK_GRAY,
@@ -69,16 +75,24 @@ public final class CheckCommonColors {
robot.waitForIdle();
frame.setBackground(color);
frame.setVisible(true);
checkPixels(color);
checkPixels(color, true);
checkPixels(color, false);
}
}
private static void checkPixels(final Color color) {
private static void checkPixels(final Color color, boolean useRect) {
System.out.println("color = " + color + ", useRect = " + useRect);
int attempt = 0;
while (true) {
Point p = frame.getLocationOnScreen();
p.translate(frame.getWidth() / 2, frame.getHeight() / 2);
Color pixel = robot.getPixelColor(p.x, p.y);
Color pixel;
Rectangle rect = new Rectangle(p.x, p.y, 1, 1);
if (useRect) {
BufferedImage bi = robot.createScreenCapture(rect);
pixel = new Color(bi.getRGB(0, 0));
} else {
pixel = robot.getPixelColor(rect.x, rect.y);
}
if (color.equals(pixel)) {
return;
}

View File

@@ -32,7 +32,7 @@ import java.awt.Robot;
/**
* @test
* @key headful
* @bug 8201364 8232433
* @bug 8201364 8232433 8211999
* @summary Component.getLocation() should returns correct location if
* Component.setBounds() was ignored by the OS
*/
@@ -64,8 +64,11 @@ public final class LocationAtScreenCorner {
Rectangle bounds = device.getDefaultConfiguration().getBounds();
test(robot, frame, bounds.x, bounds.y);
test(robot, frame, bounds.width, bounds.y);
test(robot, frame, bounds.x + bounds.width, bounds.y);
test(robot, frame, bounds.x, bounds.height);
test(robot, frame, bounds.x, bounds.y + bounds.height);
test(robot, frame, bounds.width, bounds.height);
test(robot, frame, bounds.x + bounds.width, bounds.y + bounds.height);
}
frame.dispose();
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* 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 java.awt.Dialog;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.Window;
/**
* @test
* @key headful
* @bug 8211999
* @run main/timeout=300 SlowMotion
* @summary places the window on the screen outside of any insets, and waits to
* catch any strange window moving
*/
public final class SlowMotion {
// some additional space, if getScreenInsets() does not work, say on Linux
private static final int SAFE = 100;
private static final int HEIGHT = 350;
private static final int WIDTH = 279;
private static Robot robot;
public static void main(final String[] args) throws Exception {
robot = new Robot();
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] sds = ge.getScreenDevices();
for (GraphicsDevice sd : sds) {
GraphicsConfiguration gc = sd.getDefaultConfiguration();
Rectangle bounds = gc.getBounds();
bounds.translate(SAFE, SAFE);
Point point = new Point(bounds.x, bounds.y);
Insets insets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
while (point.y < bounds.y + bounds.height - insets.bottom - HEIGHT - SAFE * 2) {
while (point.x < bounds.x + bounds.width - insets.right - WIDTH - SAFE * 2) {
test(point, new Frame());
test(point, new Window(null));
test(point, new Dialog((Dialog) null));
point.translate(bounds.width / 6, 0);
}
point.setLocation(bounds.x, point.y + bounds.height / 5);
}
}
}
private static void test(final Point loc, Window window) {
try {
window.setBounds(loc.x, loc.y, WIDTH, HEIGHT);
window.setVisible(true);
robot.delay(1000); // intentionally very slow, we try to catch
// very very last suspicion event
Rectangle bounds = window.getBounds();
if (loc.x != bounds.x || loc.y != bounds.y
|| bounds.width != WIDTH || bounds.height != HEIGHT) {
System.err.println("Component = " + window);
System.err.println("Actual bounds = " + bounds);
System.err.println("Expected location = " + loc);
System.err.println("Expected width = " + WIDTH);
System.err.println("Expected height = " + HEIGHT);
throw new RuntimeException();
}
} finally {
window.dispose();
}
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* 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 java.awt.Color;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Window;
/**
* @test
* @key headful
* @bug 8211999
* @summary The test creates the packed/unpacked top level components on the
* different screens and compares their bounds
* @run main/othervm WindowSizeDifferentScreens
* @run main/othervm -Dsun.java2d.uiScale=1 WindowSizeDifferentScreens
* @run main/othervm -Dsun.java2d.uiScale=1.25 WindowSizeDifferentScreens
*/
public final class WindowSizeDifferentScreens {
public static void main(String[] args) throws Exception {
test("window");
test("dialog");
test("frame");
}
private static void test(String top) throws Exception {
var ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
Robot robot = new Robot();
Window main = getTopLevel(top);
try {
main.setVisible(true);
robot.waitForIdle();
main.setSize(500, 500);
robot.waitForIdle();
for (GraphicsDevice gd : ge.getScreenDevices()) {
Rectangle bounds = gd.getDefaultConfiguration().getBounds();
Point point = bounds.getLocation();
point.translate(100, 100);
main.setLocation(point);
main.setBackground(Color.RED);
robot.waitForIdle();
Window packed = getTopLevel(top);
Window unpacked = getTopLevel(top);
try {
packed.pack();
robot.waitForIdle();
packed.setBackground(Color.GREEN);
unpacked.setBackground(Color.BLUE);
packed.setSize(500, 500);
unpacked.setSize(500, 500);
packed.setLocation(point);
unpacked.setLocation(point);
robot.waitForIdle();
packed.setVisible(true);
unpacked.setVisible(true);
robot.waitForIdle();
Rectangle mBounds = main.getBounds();
Rectangle pBounds = packed.getBounds();
Rectangle uBounds = unpacked.getBounds();
if (!mBounds.equals(uBounds) ||
!mBounds.equals(pBounds)) {
System.err.println("Expected bounds: " + mBounds);
System.err.println("Actual unpacked: " + uBounds);
System.err.println("Actual packed: " + pBounds);
throw new RuntimeException();
}
} finally {
packed.dispose();
unpacked.dispose();
}
}
} finally {
main.dispose();
}
}
private static Window getTopLevel(String top) {
switch (top) {
case "window": return new Window(null);
case "dialog": return new Dialog((Dialog) null);
case "frame" : return new Frame();
default: throw new IllegalArgumentException("Unexpected: " + top);
}
}
}

View File

@@ -23,10 +23,10 @@
import java.awt.Color;
import java.awt.Frame;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.datatransfer.StringSelection;
import java.awt.dnd.DnDConstants;
@@ -47,7 +47,7 @@ import test.java.awt.regtesthelpers.Util;
/**
* @test
* @key headful
* @bug 4955110 8238575
* @bug 4955110 8238575 8211999
* @summary tests that DragSourceDragEvent.getDropAction() accords to its new
* spec (does not depend on the user drop action)
* @library ../../regtesthelpers
@@ -57,6 +57,7 @@ import test.java.awt.regtesthelpers.Util;
*/
public final class Button2DragTest {
private static final int SIZE = 200;
private volatile boolean dropSuccess;
private volatile boolean locationValid = true;
@@ -79,8 +80,8 @@ public final class Button2DragTest {
final DragSourceListener dragSourceListener = new DragSourceListener() {
private void checkLocation(DragSourceEvent dsde) {
if (!frame.getBounds().contains(dsde.getLocation())) {
System.err.println("Expected in" + frame.getBounds());
System.err.println("Actual" + dsde.getLocation());
System.err.println("Expected in: " + frame.getBounds());
System.err.println("Actual: " + dsde.getLocation());
locationValid = false;
}
}
@@ -130,8 +131,10 @@ public final class Button2DragTest {
frame.setBackground(Color.GREEN);
frame.setUndecorated(true);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
Rectangle screen = frame.getGraphicsConfiguration().getBounds();
int x = (int) (screen.getCenterX() - SIZE / 2);
int y = (int) (screen.getCenterY() - SIZE / 2);
frame.setBounds(x, y, SIZE, SIZE);
frame.setVisible(true);
Robot robot = Util.createRobot();

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,7 @@
* @test
* @summary Verify that some special headers - such as User-Agent
* can be specified by the caller.
* @bug 8203771
* @bug 8203771 8218546
* @modules java.base/sun.net.www.http
* java.net.http/jdk.internal.net.http.common
* java.net.http/jdk.internal.net.http.frame
@@ -64,8 +64,6 @@ import java.net.http.HttpHeaders;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
@@ -74,13 +72,13 @@ import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import static java.lang.System.err;
import static java.lang.System.out;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static java.net.http.HttpClient.Version.HTTP_2;
import static java.nio.charset.StandardCharsets.US_ASCII;
import org.testng.Assert;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
public class SpecialHeadersTest implements HttpServerAdapters {
@@ -151,7 +149,11 @@ public class SpecialHeadersTest implements HttpServerAdapters {
"USER-AGENT", u -> userAgent(), "HOST", u -> u.getRawAuthority());
@Test(dataProvider = "variants")
void test(String uriString, String headerNameAndValue, boolean sameClient) throws Exception {
void test(String uriString,
String headerNameAndValue,
boolean sameClient)
throws Exception
{
out.println("\n--- Starting ");
int index = headerNameAndValue.indexOf(":");
@@ -183,21 +185,41 @@ public class SpecialHeadersTest implements HttpServerAdapters {
assertEquals(resp.statusCode(), 200,
"Expected 200, got:" + resp.statusCode());
String receivedHeaderString = value == null ? null
: resp.headers().firstValue("X-"+key).get();
out.println("Got X-" + key + ": " + resp.headers().allValues("X-"+key));
if (value != null) {
assertEquals(receivedHeaderString, value);
assertEquals(resp.headers().allValues("X-"+key), List.of(value));
} else {
assertEquals(resp.headers().allValues("X-"+key).size(), 0);
}
boolean isInitialRequest = i == 0;
boolean isSecure = uri.getScheme().equalsIgnoreCase("https");
boolean isHTTP2 = resp.version() == HTTP_2;
boolean isNotH2CUpgrade = isSecure || (sameClient == true && !isInitialRequest);
boolean isDefaultHostHeader = name.equalsIgnoreCase("host") && useDefault;
// By default, HTTP/2 sets the `:authority:` pseudo-header, instead
// of the `Host` header. Therefore, there should be no "X-Host"
// header in the response, except the response to the h2c Upgrade
// request which will have been sent through HTTP/1.1.
if (isDefaultHostHeader && isHTTP2 && isNotH2CUpgrade) {
assertTrue(resp.headers().firstValue("X-" + key).isEmpty());
assertTrue(resp.headers().allValues("X-" + key).isEmpty());
out.println("No X-" + key + " header received, as expected");
} else {
String receivedHeaderString = value == null ? null
: resp.headers().firstValue("X-"+key).get();
out.println("Got X-" + key + ": " + resp.headers().allValues("X-"+key));
if (value != null) {
assertEquals(receivedHeaderString, value);
assertEquals(resp.headers().allValues("X-"+key), List.of(value));
} else {
assertEquals(resp.headers().allValues("X-"+key).size(), 0);
}
}
}
}
@Test(dataProvider = "variants")
void testHomeMadeIllegalHeader(String uriString, String headerNameAndValue, boolean sameClient) throws Exception {
void testHomeMadeIllegalHeader(String uriString,
String headerNameAndValue,
boolean sameClient)
throws Exception
{
out.println("\n--- Starting ");
final URI uri = URI.create(uriString);
@@ -266,6 +288,11 @@ public class SpecialHeadersTest implements HttpServerAdapters {
}
HttpRequest request = requestBuilder.build();
boolean isInitialRequest = i == 0;
boolean isSecure = uri.getScheme().equalsIgnoreCase("https");
boolean isNotH2CUpgrade = isSecure || (sameClient == true && !isInitialRequest);
boolean isDefaultHostHeader = name.equalsIgnoreCase("host") && useDefault;
client.sendAsync(request, BodyHandlers.ofString())
.thenApply(response -> {
out.println("Got response: " + response);
@@ -273,15 +300,27 @@ public class SpecialHeadersTest implements HttpServerAdapters {
assertEquals(response.statusCode(), 200);
return response;})
.thenAccept(resp -> {
String receivedHeaderString = value == null ? null
: resp.headers().firstValue("X-"+key).get();
out.println("Got X-" + key + ": " + resp.headers().allValues("X-"+key));
if (value != null) {
assertEquals(receivedHeaderString, value);
assertEquals(resp.headers().allValues("X-" + key), List.of(value));
// By default, HTTP/2 sets the `:authority:` pseudo-header, instead
// of the `Host` header. Therefore, there should be no "X-Host"
// header in the response, except the response to the h2c Upgrade
// request which will have been sent through HTTP/1.1.
if (isDefaultHostHeader && resp.version() == HTTP_2 && isNotH2CUpgrade) {
assertTrue(resp.headers().firstValue("X-" + key).isEmpty());
assertTrue(resp.headers().allValues("X-" + key).isEmpty());
out.println("No X-" + key + " header received, as expected");
} else {
assertEquals(resp.headers().allValues("X-" + key).size(), 1);
} })
String receivedHeaderString = value == null ? null
: resp.headers().firstValue("X-"+key).get();
out.println("Got X-" + key + ": " + resp.headers().allValues("X-"+key));
if (value != null) {
assertEquals(receivedHeaderString, value);
assertEquals(resp.headers().allValues("X-" + key), List.of(value));
} else {
assertEquals(resp.headers().allValues("X-" + key).size(), 1);
}
}
})
.join();
}
}

View File

@@ -24,7 +24,10 @@
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.InputEvent;
import javax.swing.JFrame;
@@ -35,13 +38,15 @@ import javax.swing.SwingUtilities;
/**
* @test
* @key headful
* @bug 8149849
* @bug 8149849 8211999
* @summary [hidpi] DnD issues (cannot DnD from JFileChooser to JEditorPane or
* other text component) when scale > 1
* @run main/othervm DNDTextToScaledArea
* @run main/othervm -Dsun.java2d.uiScale=2 DNDTextToScaledArea
*/
public class DNDTextToScaledArea {
private static final int SIZE = 300;
private static final String TEXT = "ABCDEFGH";
private static JFrame frame;
private static JTextArea srcTextArea;
@@ -51,10 +56,17 @@ public class DNDTextToScaledArea {
private static volatile boolean passed = false;
public static void main(String[] args) throws Exception {
Robot robot = new Robot();
robot.setAutoDelay(50);
var lge = GraphicsEnvironment.getLocalGraphicsEnvironment();
for (GraphicsDevice device : lge.getScreenDevices()) {
test(device);
}
}
SwingUtilities.invokeAndWait(DNDTextToScaledArea::createAndShowGUI);
private static void test(GraphicsDevice device) throws Exception {
Robot robot = new Robot();
robot.setAutoDelay(150);
SwingUtilities.invokeAndWait(() -> createAndShowGUI(device));
robot.waitForIdle();
SwingUtilities.invokeAndWait(() -> {
@@ -62,6 +74,11 @@ public class DNDTextToScaledArea {
dstPoint = getPoint(dstTextArea, 0.75);
});
robot.waitForIdle();
// check the destination
robot.mouseMove(dstPoint.x, dstPoint.y);
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
robot.waitForIdle();
dragAndDrop(robot, srcPoint, dstPoint);
robot.waitForIdle();
@@ -77,11 +94,12 @@ public class DNDTextToScaledArea {
}
}
private static void createAndShowGUI() {
frame = new JFrame();
frame.setSize(300, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
private static void createAndShowGUI(GraphicsDevice device) {
frame = new JFrame(device.getDefaultConfiguration());
Rectangle screen = device.getDefaultConfiguration().getBounds();
int x = (int) (screen.getCenterX() - SIZE / 2);
int y = (int) (screen.getCenterY() - SIZE / 2);
frame.setBounds(x, y, SIZE, SIZE);
JPanel panel = new JPanel(new BorderLayout());

View File

@@ -0,0 +1,120 @@
/*
* Copyright 2022 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.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
/**
* @test
* @requires !vm.graal.enabled
* @summary Verifies the list of available modules
* @library /test/lib
* @run main CheckJBRModules
*/
public class CheckJBRModules {
static final String moduleNames[] = {
"java.base",
"java.compiler",
"java.datatransfer",
"java.desktop",
"java.instrument",
"java.logging",
"java.management",
"java.management.rmi",
"java.naming",
"java.net.http",
"java.prefs",
"java.rmi",
"java.scripting",
"java.se",
"java.security.jgss",
"java.security.sasl",
"java.smartcardio",
"java.sql",
"java.sql.rowset",
"java.transaction.xa",
"java.xml",
"java.xml.crypto",
"jdk.accessibility",
"jdk.attach",
"jdk.charsets",
"jdk.compiler",
"jdk.crypto.cryptoki",
"jdk.crypto.ec",
"jdk.dynalink",
"jdk.httpserver",
"jdk.internal.ed",
"jdk.internal.le",
"jdk.internal.vm.ci",
"jdk.jdi",
"jdk.jdwp.agent",
"jdk.jfr",
"jdk.jsobject",
"jdk.localedata",
"jdk.management",
"jdk.management.agent",
"jdk.management.jfr",
"jdk.naming.dns",
"jdk.naming.rmi",
"jdk.net",
"jdk.pack",
"jdk.scripting.nashorn",
"jdk.scripting.nashorn.shell",
"jdk.sctp",
"jdk.security.auth",
"jdk.security.jgss",
"jdk.unsupported",
"jdk.unsupported.desktop",
"jdk.xml.dom",
"jdk.zipfs",
"jdk.hotspot.agent",
"jdk.jcmd" };
static final String moduleNames_x64[] = {
"jdk.aot",
"jdk.internal.vm.compiler",
"jdk.internal.vm.compiler.management" };
public static void main(String args[]) throws Exception {
final OutputAnalyzer oa = exec("--list-modules").shouldHaveExitValue(0);
for(String moduleName : moduleNames) {
oa.shouldContain(moduleName);
}
final boolean isArch_x64 = "x86_64".equals(System.getProperty("os.arch"));
if (isArch_x64) {
for(String moduleName : moduleNames_x64) {
oa.shouldContain(moduleName);
}
}
}
/**
* java args... returning the OutputAnalyzer to analyzer the output
*/
private static OutputAnalyzer exec(String... args) throws Exception {
return ProcessTools.executeTestJava(args)
.outputTo(System.out)
.errorTo(System.out);
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2022 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.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
/**
* @test
* @requires !vm.graal.enabled
* @requires (os.family == "windows")
* @summary Verifies the list of available modules specific to Windows
* @library /test/lib
* @run main CheckJBRModulesWindows
*/
public class CheckJBRModulesWindows {
static final String moduleNames[] = { "jdk.crypto.mscapi" };
public static void main(String args[]) throws Exception {
final OutputAnalyzer oa = exec("--list-modules").shouldHaveExitValue(0);
for(String moduleName : moduleNames) {
oa.shouldContain(moduleName);
}
}
/**
* java args... returning the OutputAnalyzer to analyzer the output
*/
private static OutputAnalyzer exec(String... args) throws Exception {
return ProcessTools.executeTestJava(args)
.outputTo(System.out)
.errorTo(System.out);
}
}

View File

@@ -35,13 +35,13 @@ public class ProxyInfoResolvingTest {
JBRApi.ModuleRegistry r = init();
// No mapping defined -> null
requireNull(getProxy(ProxyInfoResolvingTest.class));
// Invalid JBR-side target class -> error
// Invalid JBR-side target class -> null
r.proxy(InterfaceWithoutImplementation.class.getName(), "absentImpl");
mustFail(() -> getProxy(InterfaceWithoutImplementation.class), RuntimeException.class, ClassNotFoundException.class);
// Invalid JBR-side target static method mapping -> error
requireNull(getProxy(InterfaceWithoutImplementation.class));
// Invalid JBR-side target static method mapping -> null
r.service(ServiceWithoutImplementation.class.getName(), null)
.withStatic("someMethod", "NoClass");
mustFail(() -> getProxy(ServiceWithoutImplementation.class), RuntimeException.class, ClassNotFoundException.class);
requireNull(getProxy(ServiceWithoutImplementation.class));
// Service without target class or static method mapping -> null
r.service(EmptyService.class.getName(), null);
requireNull(getProxy(EmptyService.class));

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2022 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 javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
/**
* @test
* @summary Regression test for JBR-4281 Window losing focus isn't detected in some cases on macOS
* @key headful
* @requires (os.family == "mac")
*/
public class MacSpecialFocusLostCase {
private static Robot robot;
private static JFrame frame;
public static void main(String[] args) throws Exception {
robot = new Robot();
robot.setAutoDelay(50);
try {
SwingUtilities.invokeAndWait(MacSpecialFocusLostCase::initUI);
checkFocusedStatus(true);
pressCmdSpace(); // open Spotlight popup
checkFocusedStatus(false);
pressEsc(); // close Spotlight popup
checkFocusedStatus(true);
} finally {
pressEsc(); // make sure popup is closed in any case
SwingUtilities.invokeAndWait(MacSpecialFocusLostCase::disposeUI);
}
}
private static void initUI() {
frame = new JFrame("MacSpecialFocusLostCase");
frame.setBounds(200, 200, 300, 200);
frame.setVisible(true);
}
private static void disposeUI() {
if (frame != null) frame.dispose();
}
private static void checkFocusedStatus(boolean expected) throws Exception {
robot.delay(1000);
boolean[] result = new boolean[1];
SwingUtilities.invokeAndWait(() -> result[0] = frame.isFocused());
if (result[0] != expected) {
throw new RuntimeException(expected ? "Frame isn't focused" : "Frame is still focused");
}
}
private static void pressCmdSpace() {
robot.keyPress(KeyEvent.VK_META);
robot.keyPress(KeyEvent.VK_SPACE);
robot.keyRelease(KeyEvent.VK_SPACE);
robot.keyRelease(KeyEvent.VK_META);
}
private static void pressEsc() {
robot.keyPress(KeyEvent.VK_ESCAPE);
robot.keyRelease(KeyEvent.VK_ESCAPE);
}
}

View File

@@ -1,140 +1,137 @@
// Copyright 2000-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.Arrays;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier;
import sun.awt.InvokeOnToolkitHelper;
import sun.lwawt.macosx.CThreading;
import sun.lwawt.macosx.LWCToolkit;
import sun.awt.AWTThreading;
import javax.swing.*;
import static helper.ToolkitTestHelper.*;
/*
* @test
* @summary tests that AWTThreading can manage a stream of cross EDT/AppKit invocation requests
* @summary tests that AWTThreading can manage cross EDT/AppKit blocking invocation requests
* @requires (os.family == "mac")
* @compile --add-exports=java.desktop/sun.lwawt.macosx=ALL-UNNAMED --add-exports=java.desktop/sun.awt=ALL-UNNAMED AWTThreadingTest.java
* @run main/othervm --add-exports=java.desktop/sun.lwawt.macosx=ALL-UNNAMED --add-exports=java.desktop/sun.awt=ALL-UNNAMED AWTThreadingTest
* @modules java.desktop/sun.lwawt.macosx java.desktop/sun.awt
* @run main AWTThreadingTest
* @author Anton Tarasov
*/
@SuppressWarnings("ConstantConditions")
public class AWTThreadingTest {
static final ReentrantLock LOCK = new ReentrantLock();
static final Condition COND = LOCK.newCondition();
static final CountDownLatch LATCH = new CountDownLatch(1);
static final int TIMEOUT_SECONDS = 1;
static JFrame frame;
static Thread thread;
static final AtomicInteger ITER_COUNTER = new AtomicInteger();
static final AtomicBoolean DUMP_STACK = new AtomicBoolean(false);
final static AtomicBoolean passed = new AtomicBoolean(true);
final static AtomicInteger counter = new AtomicInteger(0);
static volatile Thread THREAD;
public static void main(String[] args) throws InterruptedException {
EventQueue.invokeLater(AWTThreadingTest::runGui);
public static void main(String[] args) {
DUMP_STACK.set(args.length > 0 && "dumpStack".equals(args[0]));
LATCH.await(5, TimeUnit.SECONDS);
initTest(AWTThreadingTest.class);
frame.dispose();
thread.interrupt();
testCase("certain threads superposition", AWTThreadingTest::test);
testCase("random threads superposition", AWTThreadingTest::test);
if (!passed.get()) {
throw new RuntimeException("Test FAILED!");
}
System.out.println("Test PASSED");
}
static void runGui() {
frame = new JFrame("frame");
frame.setLocationRelativeTo(null);
frame.setSize(200, 200);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowOpened(WindowEvent e) {
startThread();
static void test() {
ITER_COUNTER.set(0);
var timer = new TestTimer(TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
EventQueue.invokeLater(() -> startThread(() ->
FUTURE.isDone() ||
timer.hasFinished()));
tryRun(() -> {
if (!FUTURE.get(TIMEOUT_SECONDS * 4, TimeUnit.SECONDS)) {
throw new RuntimeException("Test FAILED! (negative result)");
}
});
frame.setVisible(true);
tryRun(THREAD::join);
System.out.println(ITER_COUNTER + " iterations passed");
}
static void startThread() {
thread = new Thread(() -> {
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
break;
}
static void startThread(Supplier<Boolean> shouldExitLoop) {
THREAD = new Thread(() -> {
while (!shouldExitLoop.get()) {
ITER_COUNTER.incrementAndGet();
var point_1 = new CountDownLatch(1);
var point_2 = new CountDownLatch(1);
var point_3 = new CountDownLatch(1);
var invocations = new CountDownLatch(2);
//
// 1. Execute invokeAndWait() from AppKit to EDT
// 1. Blocking invocation from AppKit to EDT
//
CThreading.executeOnAppKit(() -> {
try {
LWCToolkit.invokeAndWait(counter::incrementAndGet, Window.getWindows()[0]);
} catch (Exception e) {
fail(e);
}
// We're on AppKit, wait for the 2nd invocation to be on the AWTThreading-pool thread.
if (TEST_CASE == 1) await(point_1, TIMEOUT_SECONDS);
tryRun(() -> LWCToolkit.invokeAndWait(() -> {
// We're being dispatched on EDT.
if (TEST_CASE == 1) point_2.countDown();
// Wait for the 2nd invocation to be executed on AppKit.
if (TEST_CASE == 1) await(point_3, TIMEOUT_SECONDS);
}, FRAME));
invocations.countDown();
});
//
// 2. Execute invokeAndBlock() from EDT to AppKit
// 2. Blocking invocation from EDT to AppKit
//
EventQueue.invokeLater(() -> {
passed.set(false);
EventQueue.invokeLater(() -> AWTThreading.executeWaitToolkit(() -> {
// We're on the AWTThreading-pool thread.
if (TEST_CASE == 1) point_1.countDown();
Boolean success = InvokeOnToolkitHelper.invokeAndBlock(() -> {
try {
return CThreading.executeOnAppKit(() -> Boolean.TRUE);
} catch (Throwable e) {
fail(e);
// Wait for the 1st invocation to start NSRunLoop and be dispatched
if (TEST_CASE == 1) await(point_2, TIMEOUT_SECONDS);
// Perform in JavaRunLoopMode to be accepted by NSRunLoop started by LWCToolkit.invokeAndWait.
LWCToolkit.performOnMainThreadAndWait(() -> {
if (DUMP_STACK.get()) {
dumpAllThreads();
}
return null;
// We're being executed on AppKit.
if (TEST_CASE == 1) point_3.countDown();
});
System.out.println("Success: " + counter.get() + ": " + success);
passed.set(Boolean.TRUE.equals(success));
invocations.countDown();
}));
if (passed.get()) {
lock(COND::signal);
}
else {
fail(null);
}
});
await(invocations, TIMEOUT_SECONDS * 2);
} // while
lock(COND::await);
}
FUTURE.complete(true);
});
thread.setDaemon(true);
thread.start();
THREAD.setDaemon(true);
THREAD.start();
}
static void lock(MyRunnable runnable) {
LOCK.lock();
try {
try {
runnable.run();
} catch (Exception e) {
e.printStackTrace();
}
} finally {
LOCK.unlock();
static void await(CountDownLatch latch, int seconds) {
if (!tryCall(() -> latch.await(seconds, TimeUnit.SECONDS), false)) {
FUTURE.completeExceptionally(new Throwable("Awaiting has timed out"));
}
}
interface MyRunnable {
void run() throws Exception;
}
static void fail(Throwable e) {
if (e != null) e.printStackTrace();
passed.set(false);
LATCH.countDown();
static void dumpAllThreads() {
Thread.getAllStackTraces().keySet().forEach(t -> {
System.out.printf("%s\t%s\t%d\t%s\n", t.getName(), t.getState(), t.getPriority(), t.isDaemon() ? "Daemon" : "Normal");
Arrays.asList(t.getStackTrace()).forEach(frame -> System.out.println("\tat " + frame));
});
System.out.println("\n\n");
}
}

View File

@@ -0,0 +1,220 @@
// Copyright 2000-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
import java.awt.*;
import java.awt.event.InvocationEvent;
import java.util.concurrent.*;
import java.util.function.Consumer;
import java.util.logging.*;
import sun.awt.AWTAccessor;
import sun.awt.AWTThreading;
import sun.lwawt.macosx.CThreading;
import sun.lwawt.macosx.LWCToolkit;
import static helper.ToolkitTestHelper.*;
/*
* @test
* @summary Tests different scenarios for LWCToolkit.invokeAndWait().
* @requires (os.family == "mac")
* @modules java.desktop/sun.lwawt.macosx java.desktop/sun.awt
* @run main LWCToolkitInvokeAndWaitTest
* @run main/othervm -Dlog.level.FINER=true LWCToolkitInvokeAndWaitTest
* @author Anton Tarasov
*/
@SuppressWarnings("ConstantConditions")
public class LWCToolkitInvokeAndWaitTest {
// This property is used in {CAccessibility}
static final int INVOKE_TIMEOUT_SECONDS = Integer.getInteger("sun.lwawt.macosx.CAccessibility.invokeTimeoutSeconds", 1);
static final Runnable CONSUME_DISPATCHING = () -> FUTURE.completeExceptionally(new Throwable("Unexpected dispatching!"));
static final TestLogHandler LOG_HANDLER = new TestLogHandler();
static volatile CountDownLatch EDT_FAST_FREE_LATCH;
static {
AWTThreading.setAWTThreadingFactory(edt -> new AWTThreading(edt) {
@Override
public CompletableFuture<Void> onEventDispatchThreadFree(Runnable runnable) {
if (EDT_FAST_FREE_LATCH != null) {
// 1. wait for the invocation event to be dispatched
// 2. wait for EDT to become free
tryRun(EDT_FAST_FREE_LATCH::await);
EDT_FAST_FREE_LATCH = null;
}
// if EDT is free the runnable should be called immediately
return super.onEventDispatchThreadFree(runnable);
}
@Override
public void notifyEventDispatchThreadFree() {
if (EDT_FAST_FREE_LATCH != null &&
// if the invocation event is dispatched by now
EDT_FAST_FREE_LATCH.getCount() == 1)
{
EDT_FAST_FREE_LATCH.countDown();
}
super.notifyEventDispatchThreadFree();
}
});
}
public static void main(String[] args) {
loop(); // init the event threads
tryRun(() -> {
Consumer<Class<?>> setLog = cls -> {
Logger log = LogManager.getLogManager().getLogger(cls.getName());
log.setUseParentHandlers(false);
log.addHandler(LOG_HANDLER);
if (Boolean.getBoolean("log.level.FINER")) {
log.setLevel(Level.FINER);
}
};
setLog.accept(AWTThreading.class);
setLog.accept(LWCToolkit.class);
});
Consumer<InvocationEvent> noop = e -> {};
initTest(LWCToolkitInvokeAndWaitTest.class);
testCase("InvocationEvent is normally dispatched", () -> test1(
"",
noop,
() -> System.out.println("I'm dispatched")));
testCase("InvocationEvent is lost", () -> test1(
"lost",
noop,
CONSUME_DISPATCHING));
EDT_FAST_FREE_LATCH = new CountDownLatch(2);
testCase("InvocationEvent is lost (EDT becomes fast free)", () -> test1(
"lost",
// notify the invocationEvent has been dispatched
invocationEvent -> EDT_FAST_FREE_LATCH.countDown(),
CONSUME_DISPATCHING));
testCase("InvocationEvent is disposed", () -> test1(
"disposed",
invocationEvent -> AWTAccessor.getInvocationEventAccessor().dispose(invocationEvent),
CONSUME_DISPATCHING));
testCase("InvocationEvent is timed out (delayed before dispatching)", () -> test1(
"timed out",
invocationEvent -> sleep(INVOKE_TIMEOUT_SECONDS * 4),
CONSUME_DISPATCHING));
testCase("InvocationEvent is timed out (delayed during dispatching)", () -> test1(
"timed out",
noop,
() -> sleep(INVOKE_TIMEOUT_SECONDS * 4)));
testCase("invokeAndWait is discarded", () -> test2(
"discarded"));
System.out.println("Test PASSED");
}
static void test1(String expectedInLog,
Consumer<InvocationEvent> onBeforeDispatching,
Runnable onDispatching)
{
EventQueue.invokeLater(() -> subTest1(onBeforeDispatching, onDispatching));
check(expectedInLog);
}
static void subTest1(Consumer<InvocationEvent> onBeforeDispatching, Runnable onDispatching) {
Toolkit.getDefaultToolkit().getSystemEventQueue().push(new EventQueue() {
@Override
protected void dispatchEvent(AWTEvent event) {
//
// Intercept the invocation posted from Appkit.
//
if (event instanceof AWTThreading.TrackedInvocationEvent) {
System.out.println("Before dispatching: " + event);
onBeforeDispatching.accept((InvocationEvent)event);
if (onDispatching == CONSUME_DISPATCHING) {
System.out.println("Consuming: " + event);
return;
}
}
super.dispatchEvent(event);
}
});
CThreading.executeOnAppKit(() -> tryRun(() -> {
//
// Post an invocation from AppKit.
//
LWCToolkit.invokeAndWait(onDispatching, FRAME, false, INVOKE_TIMEOUT_SECONDS);
FUTURE.complete(true);
}));
}
static void test2(String expectedInLog) {
EventQueue.invokeLater(() ->
//
// Blocking EDT.
//
LWCToolkit.performOnMainThreadAndWait(() -> {
//
// The invocation from AppKit should be discarded.
//
tryRun(() -> LWCToolkit.invokeAndWait(EMPTY_RUNNABLE, FRAME, false, INVOKE_TIMEOUT_SECONDS * 4));
FUTURE.complete(true);
}));
check(expectedInLog);
}
static void check(String expectedInLog) {
tryRun(() -> {
if (!FUTURE.get(INVOKE_TIMEOUT_SECONDS * 2L, TimeUnit.SECONDS)) {
throw new RuntimeException("Test FAILED! (negative result)");
}
});
loop(); // wait for the logging to be printed
if (!LOG_HANDLER.testContains(expectedInLog)) {
throw new RuntimeException("Test FAILED! (not found in the log: \"" + expectedInLog + "\")");
}
}
static void loop() {
tryRun(() -> EventQueue.invokeAndWait(EMPTY_RUNNABLE));
var latch = new CountDownLatch(1);
CThreading.executeOnAppKit(latch::countDown);
tryRun(latch::await);
}
static void sleep(int seconds) {
tryRun(() -> Thread.sleep(seconds * 1000L));
}
static class TestLogHandler extends StreamHandler {
public StringBuilder buffer = new StringBuilder();
public TestLogHandler() {
// Use System.out to merge with the test printing.
super(System.out, new SimpleFormatter());
setLevel(Level.ALL);
}
@Override
public void publish(LogRecord record) {
buffer.append(record.getMessage());
super.publish(record);
flush();
}
public boolean testContains(String str) {
boolean contains = buffer.toString().contains(str);
buffer.setLength(0);
return contains;
}
}
}

View File

@@ -0,0 +1,120 @@
// Copyright 2000-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package helper;
import javax.swing.*;
import java.awt.*;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@SuppressWarnings("ConstantConditions")
public class ToolkitTestHelper {
public static final Runnable EMPTY_RUNNABLE = () -> {};
public static volatile CompletableFuture<Boolean> FUTURE;
public static volatile int TEST_CASE;
public static volatile JFrame FRAME;
private static volatile JLabel LABEL;
private static volatile Thread MAIN_THREAD;
private static final Runnable UPDATE_LABEL = new Runnable() {
final Random rand = new Random();
@Override
public void run() {
LABEL.setForeground(new Color(rand.nextFloat(), 0, rand.nextFloat()));
LABEL.setText("(" + TEST_CASE + ")");
}
};
public static void initTest(Class<?> testClass) {
MAIN_THREAD = Thread.currentThread();
assert MAIN_THREAD.getName().toLowerCase().contains("main");
Thread.UncaughtExceptionHandler handler = MAIN_THREAD.getUncaughtExceptionHandler();
MAIN_THREAD.setUncaughtExceptionHandler((t, e) -> {
if (FRAME != null) FRAME.dispose();
handler.uncaughtException(t, e);
});
tryRun(() -> EventQueue.invokeAndWait(() -> {
FRAME = new JFrame(testClass.getSimpleName());
LABEL = new JLabel("0");
LABEL.setFont(LABEL.getFont().deriveFont((float)30));
LABEL.setHorizontalAlignment(SwingConstants.CENTER);
FRAME.add(LABEL, BorderLayout.CENTER);
FRAME.getContentPane().setBackground(Color.green);
FRAME.setLocationRelativeTo(null);
FRAME.setSize(200, 200);
FRAME.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
FRAME.setVisible(true);
}));
Timer timer = new Timer(100, e -> UPDATE_LABEL.run());
timer.setRepeats(true);
timer.start();
new Thread(() -> {
try {
MAIN_THREAD.join();
} catch (InterruptedException ignored) {
}
if (FRAME != null) FRAME.dispose();
timer.stop();
}).start();
}
public static void testCase(String caseCaption, Runnable test) {
FUTURE = new CompletableFuture<>();
FUTURE.whenComplete((r, e) -> Optional.of(e).ifPresent(Throwable::printStackTrace));
//noinspection NonAtomicOperationOnVolatileField
String prefix = "(" + (++TEST_CASE) + ")";
System.out.println("\n" + prefix + " TEST: " + caseCaption);
UPDATE_LABEL.run();
test.run();
System.out.println(prefix + " SUCCEEDED\n");
}
public static void tryRun(ThrowableRunnable runnable) {
tryCall(() -> {
runnable.run();
return null;
}, null);
}
public static <T> T tryCall(Callable<T> callable, T defValue) {
try {
return callable.call();
} catch (Exception e) {
if (Thread.currentThread() == MAIN_THREAD) {
throw new RuntimeException("Test FAILED!", e);
} else {
FUTURE.completeExceptionally(e);
}
}
return defValue;
}
public interface ThrowableRunnable {
void run() throws Exception;
}
public static class TestTimer {
private final long finishTime;
public TestTimer(long timeFromNow, TimeUnit unit) {
finishTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(timeFromNow, unit);
}
public boolean hasFinished() {
return System.currentTimeMillis() >= finishTime;
}
}
}

View File

@@ -120,6 +120,7 @@ java/awt/Choice/ItemStateChangeTest/ItemStateChangeTest.java
java/awt/Choice/ChoiceMouseWheelTest/ChoiceMouseWheelTest.java 7100044 macosx-all,windows-all,linux-all
java/awt/Choice/ChoicePopupLocation/ChoicePopupLocation.java 8202931 macosx-all,linux-all,windows-all
java/awt/Choice/GrabLockTest/GrabLockTest.java JRE-839 windows-all,macosx-all,linux-all
java/awt/Choice/PopdownGeneratesMouseEvents/PopdownGeneratesMouseEvents.java 8194045б generic-all
java/awt/Choice/PopupPosTest/PopupPosTest.java 8192930 windows-all
java/awt/Choice/RemoveAllShrinkTest/RemoveAllShrinkTest.java 8047703 generic-all
java/awt/Choice/SelectCurrentItemTest/SelectCurrentItemTest.html 8014503,8192929 windows-all,macosx-all,linux-all
@@ -231,6 +232,7 @@ java/awt/LightweightComponent/LightweightEventTest/LightweightEventTest.java
java/awt/List/FirstItemRemoveTest/FirstItemRemoveTest.java 8169461 windows-all
java/awt/List/KeyEventsTest/KeyEventsTest.java 8047703 windows-all
java/awt/List/SingleModeDeselect/SingleModeDeselect.java 8196367 windows-all
java/awt/MenuBar/8007006/bug8007006.java 8202886 macosx-all
java/awt/Mixing/AWT_Mixing/HierarchyBoundsListenerMixingTest.java 8049405 macosx-all,windows-all,linux-all
java/awt/Mixing/AWT_Mixing/JButtonInGlassPaneOverlapping.java 8158801 windows-all,macosx-all,linux-all
java/awt/Mixing/AWT_Mixing/JButtonOverlapping.java 8158801 windows-all,macosx-all,linux-all