Compare commits

...

37 Commits
676 ... 722

Author SHA1 Message Date
Alexey Ushakov
1cc12bf142 JBR-2135 Use CoreText api to select the font with the most recent version
Corrected java security, parsing issues and native allocations

(cherry picked from commit 23f2c1c42d)
2020-02-14 14:59:52 +07:00
Alexey Ushakov
133cbf0d09 JBR-2135 Use CoreText api to select the font with the most recent version
Added a property to force loading bundled fonts: -Djava2d.font.noVersionCheck=true

(cherry picked from commit cbb148dff4)
2020-02-14 14:59:48 +07:00
Alexey Ushakov
7d1cddedcb JBR-2137 JetBrainsMono fonts update to v1.0.3
(cherry picked from commit a6e441828a)
2020-02-14 14:59:14 +07:00
Dmitry Batrak
a41a41f4dd JBR-2140 Backport JDK-8236996 from OpenJDK
apply corresponding change from OpenJDK

(cherry picked from commit 53744bf65f)
2020-02-14 14:58:49 +07:00
Dmitry Batrak
788c2f747c JBR-2140 Backport JDK-8236996 from OpenJDK
revert original JBR-1879 fix

(cherry picked from commit 430fdb17a8)
2020-02-14 14:58:45 +07:00
Anton Tarasov
ccfff09407 JBR-2146 improve InvokeOnToolkitHelper to cover more generic case 2020-02-14 09:42:58 +03:00
Anton Tarasov
1399ebeda8 JBR-2139 Idea freeze on dynamic plugin unloading
(cherry picked from commit e57bae4f66)
2020-02-13 14:31:20 +07:00
Vitaly Provodin
d115e7845f Revert "JBR-2084 modify scripts to sign Contents/MacOS/libjli.dylib as a a normal file"
This reverts commit 1f6b3428
2020-02-08 13:05:19 +07:00
Vitaly Provodin
5ee1e80568 Revert "8235687: Contents/MacOS/libjli.dylib cannot be a symlink Reviewed-by: tbell"
This reverts commit 276971e4
2020-02-08 13:05:01 +07:00
Elena Sayapina
0f895bf1b2 JBR-2041 Update regression test so it continues to run after one test case fail 2020-02-06 08:35:51 +07:00
Elena Sayapina
05af375909 JBR-2041 Fix regression test compilation on Windows 2020-02-06 07:26:02 +07:00
Elena Sayapina
92606f2c7f JBR-2014 Fix regression test compilation on Linux
error: 'for' loop initial declarations are only allowed in C99 mode
2020-02-06 12:59:52 +07:00
Elena Sayapina
2d587b3728 JBR-2041 Added new regression test (Touchscreen devices support) 2020-02-05 18:37:20 +07:00
Vitaly Provodin
48468b08d0 updated JTreg exclude list 2020-02-05 14:44:07 +07:00
Artem Bochkarev
31b590c16c JBR-2111: enable X11_DISABLE_OVERRIDE_FLAG by default 2020-02-03 12:05:00 +03:00
Vitaly Provodin
78bdb2e198 updated JTreg exclude list 2020-02-03 14:40:59 +07:00
Vitaly Provodin
c64c10cbf0 updated JTreg exclude list 2020-02-01 07:05:37 +07:00
Dmitry Batrak
ae91e1d7f1 JBR-2050 Issue with keycap emojis 2020-01-30 17:33:57 +03:00
Denis Konoplev
ca2209dd48 README: Toolchain configuration on Windows 2020-01-30 10:04:31 +03:00
Vitaly Provodin
354855edc2 updated JTreg exclude list 2020-01-29 09:51:38 +07:00
Vitaly Provodin
98087b0773 updated JTreg exclude list 2020-01-29 09:49:02 +07:00
Anton Tarasov
3dfb0aa16a JBR-2099 jb/java/jcef/JCEFStartupTest.java fails on Windows, Linux 2020-01-28 19:31:30 +03:00
Vitaly Provodin
18b9bf5b0b updated JTreg exclude list 2020-01-28 15:31:09 +07:00
Konstantin Bulenkov
6f4a13e46f Update JetBrains Mono to 1.0.2 2020-01-27 11:47:28 +01:00
Vitaly Provodin
eacfb7f301 updated JTreg exclude list 2020-01-27 15:46:55 +07:00
Vitaly Provodin
42a8da52d1 updated JTreg exclude list 2020-01-25 07:14:07 +07:00
Anton Tarasov
e8c2761f5b JBR-2093 create reg test for JCEF startup 2020-01-24 19:01:04 +03:00
Ivan Migalev
0c911b6ffe Fix a possible resource leak in ColorizationColorAffectsBorders 2020-01-24 14:14:51 +07:00
Ivan Migalev
06086f4a7e Refresh desktop properties on WM_DWMCOLORIZATIONCOLORCHANGED (JBR-2070) 2020-01-24 14:14:49 +07:00
Ivan Migalev
0330cab60b Extract the DWM colorization parameters from registry (JBR-2070) 2020-01-24 14:14:47 +07:00
Elena Sayapina
a4b373e631 JBR-2086 JetBrainsMono fonts update to v1.0.1 2020-01-22 18:16:29 +07:00
Vitaly Provodin
1f6b342856 JBR-2084 modify scripts to sign Contents/MacOS/libjli.dylib as a a normal file 2020-01-22 12:00:49 +07:00
Anton Tarasov
175a0b3a13 Merge remote-tracking branch 'origin/master' 2020-01-21 22:02:36 +03:00
Anton Tarasov
b31a41fb2f JBR-2082 Revealing taskbar does not work when "Automatically hide the taskbar" 2020-01-21 22:02:14 +03:00
Dmitry Batrak
5e77712607 fix merge issues after merge from OpenJDK 11.0.6 2020-01-21 12:54:35 +03:00
Dmitry Batrak
95131842f1 JBR-2078 Backport JDK-8224109 fix from OpenJDK
apply fix from OpenJDK
2020-01-20 18:52:33 +03:00
Dmitry Batrak
1d8cd6505e JBR-2078 Backport JDK-8224109 fix from OpenJDK
revert part of JBR-363, equivalent to JDK-8224109 fix
2020-01-20 18:51:20 +03:00
41 changed files with 2187 additions and 321 deletions

View File

@@ -75,7 +75,7 @@ First command will set env vars, the second will run cygwin shell with proper en
In cygwin shell
```
cd JetBrainsRuntime
./configure --disable-warnings-as-errors
bash configure --enable-option-checking=fatal --enable-option-checking=fatal --with-toolchain-version=2015 --with-boot-jdk="/cygdrive/c/Program Files/Java/jdk-11.0.5" --disable-warnings-as-errors
make images
```

View File

@@ -61,15 +61,17 @@ ifeq ($(OPENJDK_TARGET_OS), macosx)
FILES := $(call CacheFind, $(JRE_IMAGE_DIR)), \
))
$(eval $(call SetupCopyFiles, COPY_LIBJLI_JDK, \
FILES := $(JDK_IMAGE_DIR)/lib/jli/libjli.dylib, \
DEST := $(JDK_MACOSX_CONTENTS_DIR)/MacOS, \
))
$(JDK_MACOSX_CONTENTS_DIR)/MacOS/libjli.dylib:
$(call LogInfo, Creating link $(patsubst $(OUTPUTDIR)/%,%,$@))
$(MKDIR) -p $(@D)
$(RM) $@
$(LN) -s ../Home/lib/jli/libjli.dylib $@
$(eval $(call SetupCopyFiles, COPY_LIBJLI_JRE, \
FILES := $(JRE_IMAGE_DIR)/lib/jli/libjli.dylib, \
DEST := $(JRE_MACOSX_CONTENTS_DIR)/MacOS, \
))
$(JRE_MACOSX_CONTENTS_DIR)/MacOS/libjli.dylib:
$(call LogInfo, Creating link $(patsubst $(OUTPUTDIR)/%,%,$@))
$(MKDIR) -p $(@D)
$(RM) $@
$(LN) -s ../Home/lib/jli/libjli.dylib $@
$(eval $(call SetupTextFileProcessing, BUILD_JDK_PLIST, \
SOURCE_FILES := $(MACOSX_PLIST_SRC)/JDK-Info.plist, \
@@ -95,19 +97,13 @@ ifeq ($(OPENJDK_TARGET_OS), macosx)
@@VENDOR@@ => $(BUNDLE_VENDOR) , \
))
$(SUPPORT_OUTPUTDIR)/images/_jdk_bundle_attribute_set: $(COPY_JDK_IMAGE)
jdk-bundle: $(COPY_JDK_IMAGE) $(JDK_MACOSX_CONTENTS_DIR)/MacOS/libjli.dylib \
$(BUILD_JDK_PLIST)
$(SETFILE) -a B $(dir $(JDK_MACOSX_CONTENTS_DIR))
$(TOUCH) $@
$(SUPPORT_OUTPUTDIR)/images/_jre_bundle_attribute_set: $(COPY_JRE_IMAGE)
jre-bundle: $(COPY_JRE_IMAGE) $(JRE_MACOSX_CONTENTS_DIR)/MacOS/libjli.dylib \
$(BUILD_JRE_PLIST)
$(SETFILE) -a B $(dir $(JRE_MACOSX_CONTENTS_DIR))
$(TOUCH) $@
jdk-bundle: $(COPY_JDK_IMAGE) $(COPY_LIBJLI_JDK) \
$(BUILD_JDK_PLIST) $(SUPPORT_OUTPUTDIR)/images/_jdk_bundle_attribute_set
jre-bundle: $(COPY_JRE_IMAGE) $(COPY_LIBJLI_JRE) \
$(BUILD_JRE_PLIST) $(SUPPORT_OUTPUTDIR)/images/_jre_bundle_attribute_set
else # Not macosx

View File

@@ -42,13 +42,13 @@ allfonts.gujarati=Shruti
allfonts.gurmukhi=Raavi
allfonts.kannada=Tunga
allfonts.malayalam=Kartika
allfonts.myanmar=Myanmar Text
allfonts.oriya=Kalinga
allfonts.sinhala=Iskoola Pota
allfonts.tamil=Latha
allfonts.telugu=Gautami
allfonts.khmer=Khmer UI
allfonts.mongolian=Mongolian Baiti
allfonts.myanmar=Myanmar Text
allfonts.dingbats=Wingdings
allfonts.symbol=Symbol
allfonts.symbols=Segoe UI Symbol
@@ -252,7 +252,8 @@ sequence.fallback=symbols,\
chinese-ms950,chinese-hkscs,chinese-ms936,chinese-gb18030,\
japanese,korean,chinese-ms950-extb,chinese-ms936-extb,\
georgian,devanagari,bengali,gujarati,gurmukhi,kannada,\
malayalam,oriya,sinhala,tamil,telugu,thai,khmer,mongolian,myanmar
malayalam,oriya,sinhala,tamil,telugu,thai,khmer,mongolian,\
myanmar
# Exclusion Ranges
@@ -302,10 +303,6 @@ filename.MS_PMincho=MSMINCHO.TTC
filename.MS_Gothic=MSGOTHIC.TTC
filename.MS_PGothic=MSGOTHIC.TTC
filename.Gulim=gulim.TTC
filename.Batang=batang.TTC
filename.GulimChe=gulim.TTC
filename.Gautami=gautami.ttf
filename.Iskoola_Pota=iskpota.ttf
filename.Kalinga=kalinga.ttf
@@ -313,15 +310,14 @@ filename.Kartika=kartika.ttf
filename.Latha=latha.ttf
filename.Malgun_Gothic=malgun.ttf
filename.Mangal=MANGAL.TTF
filename.Myanmar_Text=mmrtext.ttf
filename.Raavi=raavi.ttf
filename.Shruti=shruti.ttf
filename.Tahoma=tahoma.ttf
filename.Tunga=TUNGA.TTF
filename.Vrinda=vrinda.ttf
filename.DokChampa=dokchamp.ttf
filename.Khmer_UI=KhmerUI.ttf
filename.Mongolian_Baiti=monbaiti.ttf
filename.Myanmar_Text=mmrtext.ttf
filename.Symbol=SYMBOL.TTF
filename.Wingdings=WINGDING.TTF

View File

@@ -72,6 +72,17 @@ else
BUILD_JDK_JTREG_EXCLUDE += libTestMainKeyWindow.c
endif
ifeq ($(OPENJDK_TARGET_OS), windows)
BUILD_JDK_JTREG_LIBRARIES_LIBS_libwindows_touch_robot := user32.lib
BUILD_JDK_JTREG_EXCLUDE += libtouchscreen_device.c
else
ifeq ($(OPENJDK_TARGET_OS), linux)
BUILD_JDK_JTREG_EXCLUDE += libwindows_touch_robot.c
else
BUILD_JDK_JTREG_EXCLUDE += libtouchscreen_device.c libwindows_touch_robot.c
endif
endif
$(eval $(call SetupTestFilesCompilation, BUILD_JDK_JTREG_LIBRARIES, \
TYPE := LIBRARY, \
SOURCE_DIRS := $(BUILD_JDK_JTREG_NATIVE_SRC), \

View File

@@ -27,7 +27,6 @@ package sun.font;
import java.awt.*;
import java.io.File;
import java.io.FilenameFilter;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
@@ -41,6 +40,7 @@ import javax.swing.plaf.FontUIResource;
import sun.awt.FontConfiguration;
import sun.awt.HeadlessToolkit;
import sun.lwawt.macosx.*;
import sun.util.logging.PlatformLogger;
public final class CFontManager extends SunFontManager {
private static Hashtable<String, Font2D> genericFonts = new Hashtable<String, Font2D>();
@@ -144,10 +144,39 @@ public final class CFontManager extends SunFontManager {
protected void registerJREFonts() {
String[] files = AccessController.doPrivileged((PrivilegedAction<String[]>) () ->
new File(jreFontDirName).list(getTrueTypeFilter()));
if (files != null) {
PlatformLogger logger = FontUtilities.getLogger();
int [] ver = new int[3];
for (String f : files) {
loadNativeDirFonts(jreFontDirName + File.separator + f);
boolean loadFont = true;
BundledFontInfo fi = getBundledFontInfo(f);
if (versionCheckEnabled) {
if (fi != null) {
String verStr = getNativeFontVersion(fi.getPsName());
if (logger != null) {
logger.info("Checking bundled " + fi.getPsName());
}
if (verStr != null && parseFontVersion(verStr, ver) && !fi.isNewerThan(ver)) {
if (logger != null) {
logger.info("Skip loading. Newer or same version platform font detected " +
fi.getPsName() + " " + verStr);
}
loadFont = false;
}
} else {
if (logger != null) {
FontUtilities.getLogger().warning("JREFonts: No BundledFontInfo for : " + f);
}
}
}
if (loadFont) {
String fontPath = jreFontDirName + File.separator + f;
loadNativeDirFonts(fontPath);
if (logger != null && fi != null) {
String verStr = getNativeFontVersion(fi.getPsName());
logger.info("Loaded " + fi.getPsName() + " (" + verStr + ")");
}
}
}
}
}
@@ -171,6 +200,7 @@ public final class CFontManager extends SunFontManager {
private native void loadNativeDirFonts(String fontPath);
private native void loadNativeFonts();
native String getNativeFontVersion(String psName);
void registerFont(String fontName, String fontFamilyName) {
// Use different family for specific font faces

View File

@@ -63,7 +63,7 @@ import com.apple.laf.ClientPropertyApplicator.Property;
import sun.awt.AWTAccessor;
import sun.awt.AWTAccessor.ComponentAccessor;
import sun.awt.AWTAccessor.WindowAccessor;
import sun.awt.InvokeOnToolkitHelper;
import sun.awt.AWTThreading;
import sun.java2d.SurfaceData;
import sun.java2d.opengl.CGLSurfaceData;
import sun.lwawt.LWLightweightFramePeer;
@@ -331,7 +331,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
long nativeWindowPtr = java.security.AccessController.doPrivileged(
(PrivilegedAction<Long>) () -> {
try {
return InvokeOnToolkitHelper.invokeAndBlock(() -> {
return AWTThreading.executeWaitToolkit(() -> {
AtomicLong ref = new AtomicLong();
contentView.execute(viewPtr -> {
boolean hasOwnerPtr = false;

View File

@@ -472,7 +472,7 @@ public final class LWCToolkit extends LWToolkit {
public Insets getScreenInsets(final GraphicsConfiguration gc) {
CGraphicsDevice gd = ((CGraphicsConfig) gc).getDevice();
// Avoid deadlock with input methods
return InvokeOnToolkitHelper.invokeAndBlock(gd::getScreenInsets);
return AWTThreading.executeWaitToolkit(gd::getScreenInsets);
}
@Override
@@ -726,7 +726,7 @@ public final class LWCToolkit extends LWToolkit {
final long mediator = createAWTRunLoopMediator();
InvocationEvent invocationEvent =
new InvocationEvent(component,
AWTThreading.createAndTrackInvocationEvent(component,
runnable,
() -> {
if (mediator != 0) {
@@ -735,17 +735,16 @@ public final class LWCToolkit extends LWToolkit {
},
true);
if (!InvokeOnToolkitHelper.offer(invocationEvent)) {
if (component != null) {
AppContext appContext = SunToolkit.targetToAppContext(component);
SunToolkit.postEvent(appContext, invocationEvent);
if (component != null) {
AppContext appContext = SunToolkit.targetToAppContext(component);
SunToolkit.postEvent(appContext, invocationEvent);
// 3746956 - flush events from PostEventQueue to prevent them from getting stuck and causing a deadlock
SunToolkit.flushPendingEvents(appContext);
} else {
// This should be the equivalent to EventQueue.invokeAndWait
((LWCToolkit) Toolkit.getDefaultToolkit()).getSystemEventQueueForInvokeAndWait().postEvent(invocationEvent);
}
// 3746956 - flush events from PostEventQueue to prevent them from getting stuck and causing a deadlock
SunToolkit.flushPendingEvents(appContext);
}
else {
// This should be the equivalent to EventQueue.invokeAndWait
((LWCToolkit)Toolkit.getDefaultToolkit()).getSystemEventQueueForInvokeAndWait().postEvent(invocationEvent);
}
doAWTRunLoop(mediator, nonBlockingRunLoop);

View File

@@ -1402,6 +1402,41 @@ JNF_COCOA_ENTER(env);
JNF_COCOA_EXIT(env);
}
/*
* Class: Java_sun_font_CFontManager_loadNativeDirFonts
* Method: getNativeFontVersion
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT JNICALL jstring
Java_sun_font_CFontManager_getNativeFontVersion
(JNIEnv *env, jclass clz, jstring psName)
{
jstring result = NULL;
JNF_COCOA_ENTER(env);
NSString *psNameStr = JNFJavaToNSString(env, psName);
CTFontRef sFont = CTFontCreateWithName(psNameStr, 13, nil);
if (sFont != NULL) {
CFStringRef sFontPSName = CTFontCopyName(sFont, kCTFontPostScriptNameKey);
// CTFontCreateWithName always returns some font,
// so we need to check if it is right one
if (sFontPSName != NULL && [psNameStr isEqualToString:sFontPSName]) {
CFStringRef fontVersionStr = CTFontCopyName(sFont,
kCTFontVersionNameKey);
if (fontVersionStr != NULL) {
result = JNFNSToJavaString(env, fontVersionStr);
CFRelease(fontVersionStr);
}
}
if (sFontPSName != NULL) {
CFRelease(sFontPSName);
}
CFRelease(sFont);
}
JNF_COCOA_EXIT(env);
return result;
}
/*
* Class: Java_sun_font_CFontManager_loadNativeDirFonts
* Method: loadNativeDirFonts

View File

@@ -0,0 +1,207 @@
package sun.awt;
import sun.font.FontUtilities;
import java.awt.*;
import java.awt.event.InvocationEvent;
import java.lang.ref.SoftReference;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.*;
/**
* Used to perform a cross threads (EventDispatch, Toolkit) execution so that the execution does not cause a deadlock.
*/
public class AWTThreading {
private ExecutorService executor;
// every invokeAndWait() pushes a queue of invocations
private final Stack<TrackingQueue> invocations = new Stack<>();
private int level; // re-entrance level
// 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() {}
/**
* Executes a callable from EventDispatch thread (EDT). It's assumed the callable either performs a blocking execution on Toolkit
* or waits a notification from Toolkit. The method is re-entrant.
* <p>
* Currently only macOS is supported. The callable can wrap a native obj-c selector. The selector should be executed via
* [JNFRunLoop performOnMainThreadWaiting:YES ...] so that doAWTRunLoop on AppKit (which is run in [JNFRunLoop javaRunLoopMode]) accepts it.
* The callable should not call any Java code that would normally be called on EDT.
* <p>
* A deadlock can happen when the callable triggers any blocking invocation from Toolkit to EDT, or when Toolkit already waits in
* such blocking invocation. To avoid that:
* <ul>
* <li>The callback execution is delegated to a dedicated pool thread.
* <li>All invocation events, initiated by Toolkit via invokeAndWait(), are tracked via a dedicated queue.
* <li>All the tracked invocation events are dispatched on EDT out of order (EventQueue) during the callback execution.
* <li>In case of a re-entrant method call, all the tracked invocation events coming after the call are dispatched first.
* </ul><p>
* When called on non-EDT, or on non-macOS, the method executes the callable just in place.
*/
public static <T> T executeWaitToolkit(Callable<T> callable) {
return executeWaitToolkit(callable, -1, null);
}
/**
* Same as {@link #executeWaitToolkit(Callable)} except that the method waits no longer than the specified timeout.
*/
public static <T> T executeWaitToolkit(Callable<T> callable, long timeout, TimeUnit unit) {
if (callable == null) return null;
if (FontUtilities.isMacOSX && EventQueue.isDispatchThread()) {
AWTThreading instance = getInstance(Thread.currentThread());
if (instance != null) {
return instance.execute(callable, timeout, unit);
}
}
// fallback to default
try {
return callable.call();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private <T> T execute(Callable<T> callable, long timeout, TimeUnit unit) {
assert EventQueue.isDispatchThread();
if (executor == null) {
// init on EDT
AccessController.doPrivileged((PrivilegedAction<?>)() ->
executor = new ThreadPoolExecutor(1, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<>(),
new ThreadFactory() {
private final ThreadFactory factory = Executors.privilegedThreadFactory();
@Override
public Thread newThread(Runnable r) {
Thread t = factory.newThread(r);
t.setDaemon(true);
t.setName("AWT-" + AWTThreading.class.getSimpleName() + " " + t.getName());
return t;
}
})
);
}
level++;
try {
TrackingQueue currentQueue;
synchronized (invocations) {
if (level == 1 && invocations.size() == 1) {
currentQueue = invocations.peek();
} else {
invocations.push(currentQueue = new TrackingQueue());
}
}
FutureTask<T> task = new FutureTask<>(callable) {
@Override
protected void done() {
synchronized (invocations) {
invocations.remove(currentQueue);
// add dummy event to wake up the queue
currentQueue.add(new InvocationEvent(new Object(), () -> {}));
}
}
};
executor.execute(task);
try {
while (!task.isDone() || !currentQueue.isEmpty()) {
InvocationEvent event;
if (timeout >= 0 && unit != null) {
event = currentQueue.poll(timeout, unit);
} else {
event = currentQueue.take();
}
if (event == null) {
task.cancel(false);
synchronized (invocations) {
invocations.remove(currentQueue);
}
new RuntimeException("Waiting for the invocation event timed out").printStackTrace();
break;
}
event.dispatch();
}
return task.isCancelled() ? null : task.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
} finally {
level--;
}
return null;
}
/**
* Used by implementation.
* Creates an invocation event and adds it to the tracking queue.
* It's assumed the event is then posted to EventQueue.
* The following is provided:
* <ul>
* <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>
*/
public static InvocationEvent createAndTrackInvocationEvent(Object source, Runnable runnable, Runnable listener, boolean catchThrowables) {
AWTThreading instance = getInstance(source);
if (instance != null) {
synchronized (instance.invocations) {
if (instance.invocations.isEmpty()) {
instance.invocations.push(new TrackingQueue());
}
final TrackingQueue queue = instance.invocations.peek();
InvocationEvent event = new InvocationEvent(source, runnable, listener, catchThrowables) {
final SoftReference<TrackingQueue> queueRef = new SoftReference<>(queue);
@Override
public void dispatch() {
if (!isDispatched()) {
super.dispatch();
TrackingQueue queue = queueRef.get();
if (queue != null) {
queue.remove(this);
queueRef.clear();
}
}
}
};
queue.add(event);
return event;
}
}
return new InvocationEvent(source, runnable, listener, catchThrowables);
}
private static AWTThreading getInstance(Object obj) {
if (obj == null) return null;
AppContext appContext = SunToolkit.targetToAppContext(obj);
if (appContext == null) return null;
return getInstance((EventQueue)appContext.get(AppContext.EVENT_QUEUE_KEY));
}
private 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;
return EDT_TO_INSTANCE_MAP.computeIfAbsent(edt, key -> new AWTThreading());
}
}

View File

@@ -1,157 +1,15 @@
package sun.awt;
import java.awt.*;
import java.awt.event.InvocationEvent;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.Stack;
import java.util.concurrent.*;
import java.util.concurrent.Callable;
/**
* Used to perform a blocking invocation on Toolkit thread from EDT, preventing a deadlock.
* The deadlock can happen when EDT and Toolkit perform blocking invocations at the same time.
* The following is performed to resolve it:
* 1) The invoker spins a nested event loop on EDT while waiting for the invocation to complete.
* 2) A separate pool thread is used to perform the invocation.
* TODO: remove it in JBR 202
*
* @deprecated
* @see AWTThreading
*/
public class InvokeOnToolkitHelper {
private ExecutorService executor;
// every invokeAndWait() pushes a queue of invocations
private final Stack<LinkedBlockingQueue<InvocationEvent>> invocations = new Stack<>();
// invocations should be dispatched on proper EDT (per AppContext)
private static final Map<Thread, InvokeOnToolkitHelper> edt2invokerMap = new ConcurrentHashMap<>();
private static final int WAIT_LIMIT_SECONDS = 5;
private InvokeOnToolkitHelper() {}
/**
* Invokes the callable on Toolkit thread and blocks until the completion. The method is re-entrant.
*
* On macOS it is assumed the callable wraps a native selector. The selector should be executed via [JNFRunLoop performOnMainThreadWaiting:YES ...]
* so that the doAWTRunLoop on AppKit (which is run in [JNFRunLoop javaRunLoopMode]) accepts it. The callable wrapper should not call any Java code
* which would normally be called on EDT.
* <p>
* If Toolkit posts invocation events caused by the callable, those events are intercepted and dispatched on EDT out of order.
* <p>
* When called on non-EDT, the method invokes the callable in place.
*/
public static <T> T invokeAndBlock(Callable<T> callable) {
if (callable == null) return null;
if (EventQueue.isDispatchThread()) {
InvokeOnToolkitHelper invoker = getInstance(Thread.currentThread());
if (invoker != null) {
return invoker.invoke(callable);
}
}
// fallback to default
try {
return callable.call();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private <T> T invoke(Callable<T> callable) {
assert EventQueue.isDispatchThread();
if (executor == null) {
// init on EDT
AccessController.doPrivileged((PrivilegedAction<?>)() ->
executor = new ThreadPoolExecutor(1, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<>(),
new ThreadFactory() {
private final ThreadFactory factory = Executors.privilegedThreadFactory();
@Override
public Thread newThread(Runnable r) {
Thread t = factory.newThread(r);
t.setDaemon(true);
t.setName("AWT-InvokeOnToolkitHelper " + t.getName());
return t;
}
})
);
}
LinkedBlockingQueue<InvocationEvent> currentQueue;
synchronized (invocations) {
invocations.push(currentQueue = new LinkedBlockingQueue<>());
}
FutureTask<T> task = new FutureTask<T>(callable) {
@Override
protected void done() {
synchronized (invocations) {
// Done with the current queue, wake it up.
invocations.pop().add(new InvocationEvent(executor, () -> {}));
}
}
};
executor.execute(task);
try {
while (!task.isDone() || !currentQueue.isEmpty()) {
InvocationEvent event = currentQueue.poll(WAIT_LIMIT_SECONDS, TimeUnit.SECONDS);
if (event == null) {
new RuntimeException("Waiting for the invocation event timed out").printStackTrace();
break;
}
event.dispatch();
}
return task.isDone() ? task.get() : null;
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return null;
}
/**
* Warning: the method is used by the implementation and should not be used by a client.
*
* Checks if there's an active InvokeOnToolkitHelper corresponding to the invocation's AppContext,
* adds the invocation to the InvokeOnToolkitHelper's queue and returns true.
* Otherwise does nothing and returns false.
*/
public static boolean offer(InvocationEvent invocation) {
Object source = invocation.getSource();
InvokeOnToolkitHelper invoker = (source instanceof Component) ?
getInstance((Component)source) :
getInstance(Toolkit.getDefaultToolkit().getSystemEventQueue());
if (invoker == null) return false;
synchronized (invoker.invocations) {
if (!invoker.invocations.isEmpty()) {
invoker.invocations.peek().add(invocation);
return true;
}
}
return false;
}
private static InvokeOnToolkitHelper getInstance(Component comp) {
if (comp == null) return null;
AppContext appContext = SunToolkit.targetToAppContext(comp);
if (appContext == null) return null;
return getInstance((EventQueue)appContext.get(AppContext.EVENT_QUEUE_KEY));
}
private static InvokeOnToolkitHelper getInstance(EventQueue eq) {
if (eq == null) return null;
return getInstance(AWTAccessor.getEventQueueAccessor().getDispatchThread(eq));
}
private static InvokeOnToolkitHelper getInstance(Thread edt) {
if (edt == null) return null;
return edt2invokerMap.computeIfAbsent(edt, key -> new InvokeOnToolkitHelper());
return AWTThreading.executeWaitToolkit(callable);
}
}

View File

@@ -111,10 +111,6 @@ public abstract class FileFont extends PhysicalFont {
return 1; // DEFAULT_CHARSET
}
int getFontDataSize() {
return fileSize;
}
/*
* This is the public interface. The subclasses need to implement
* this. The returned block may be longer than the requested length.

View File

@@ -442,12 +442,8 @@ public class FileFontStrike extends PhysicalStrike {
}
if (ptr == 0) {
boolean fm = desc.fmHint == INTVAL_FRACTIONALMETRICS_ON;
ptr = _getGlyphImageFromWindows(family, style, size, glyphCode, fm, rotation, charset,
fileFont.getFontDataSize());
if (ptr == 0 && FontUtilities.isLogging()) {
FontUtilities.getLogger().warning("Failed to render glyph via GDI: code=" + glyphCode
+ ", fontFamily=" + family + ", style=" + style + ", size=" + size + ", rotation=" + rotation);
}
ptr = _getGlyphImageFromWindows(family, style, size, glyphCode, fm,
rotation, charset, ((TrueTypeFont)fileFont).fontDataSize);
if (ptr != 0 && fm) {
Point2D.Float metrics = new Point2D.Float();
fileFont.getGlyphMetrics(pScalerContext, glyphCode, metrics);
@@ -456,6 +452,12 @@ public class FileFontStrike extends PhysicalStrike {
}
}
if (ptr == 0) {
if (FontUtilities.isLogging()) {
FontUtilities.getLogger().warning(
"Failed to render glyph using GDI: code=" + glyphCode
+ ", fontFamily=" + family + ", style=" + style
+ ", size=" + size);
}
ptr = fileFont.getGlyphImage(pScalerContext, glyphCode);
}
return ptr;

View File

@@ -132,7 +132,7 @@ public final class FontUtilities {
* where the caller interprets 'layout' to mean any case where
* one 'char' (ie the java type char) does not map to one glyph
*/
public static final int MAX_LAYOUT_CHARCODE = 0x206F;
public static final int MAX_LAYOUT_CHARCODE = 0x20F0;
/**
* Calls the private getFont2D() method in java.awt.Font objects.
@@ -299,6 +299,9 @@ public final class FontUtilities {
else if (code >= 0x206a && code <= 0x206f) { // directional control
return true;
}
else if (code >= 0x20d0) { // U+20D0 - U+20D0 combining diacritical marks for symbols
return true;
}
return false;
}

View File

@@ -64,6 +64,38 @@ import sun.util.logging.PlatformLogger;
* methods that have to be implemented by specific implementations.
*/
public abstract class SunFontManager implements FontSupport, FontManagerForSGE {
protected static class BundledFontInfo {
private String psName;
private int [] version;
public BundledFontInfo(String psName, int major, int minor, int bugfix) {
this.psName = psName;
version = new int[3];
version[0] = major;
version[1] = minor;
version[2] = bugfix;
}
public String getPsName() {
return psName;
}
public boolean isNewerThan(int[] version) {
for (int i = 0; i < 3; i++) {
if (this.version[i] < version[i]) {
return false;
} else if (this.version[i] > version[i]) {
return true;
}
}
return false;
}
@Override
public String toString() {
return psName + " v" + version[0] + "." + version[1] + "." + version[2];
}
}
private static class TTFilter implements FilenameFilter {
public boolean accept(File dir,String name) {
@@ -187,7 +219,7 @@ public abstract class SunFontManager implements FontSupport, FontManagerForSGE {
private boolean loaded1dot0Fonts = false;
boolean loadedAllFonts = false;
boolean loadedAllFontFiles = false;
private HashMap<String,String> jreFontMap;
private HashMap<String,BundledFontInfo> jreFontMap;
private HashSet<String> jreBundledFontFiles;
HashMap<String,String> jreFamilyMap;
String[] jreOtherFontFiles;
@@ -230,6 +262,7 @@ public abstract class SunFontManager implements FontSupport, FontManagerForSGE {
private Locale lastDefaultLocale;
public static boolean noType1Font;
public static boolean versionCheckEnabled;
/* Used to indicate required return type from toArray(..); */
private static String[] STR_ARRAY = new String[0];
@@ -279,60 +312,55 @@ public abstract class SunFontManager implements FontSupport, FontManagerForSGE {
* are rarely used. Consider removing the other mappings if there's
* no evidence they are useful in practice.
*/
jreFontMap = new HashMap<String, String>();
jreFontMap = new HashMap<>();
jreBundledFontFiles = new HashSet<String>();
jreFamilyMap = new HashMap<>();
/* Droid Sans Mono Family */
jreFontMap.put("droid sans0", "DroidSans.ttf");
jreFontMap.put("droid sans1", "DroidSans-Bold.ttf");
jreFontMap.put("droid sans bold1", "DroidSans-Bold.ttf");
jreFontMap.put("DroidSans.ttf", new BundledFontInfo("DroidSans", 1, 0, 0));
jreFontMap.put("DroidSans-Bold.ttf", new BundledFontInfo("DroidSans-Bold", 1, 0, 0));
/* Droid Sans Mono Family */
jreFontMap.put("droid sans mono0", "DroidSansMono.ttf");
jreFontMap.put("droid sans mono slashed0",
"DroidSansMonoSlashed.ttf");
jreFontMap.put("droid sans mono dotted0",
"DroidSansMonoDotted.ttf");
jreFontMap.put("DroidSansMono.ttf", new BundledFontInfo("DroidSansMono", 1, 0, 0));
jreFontMap.put("DroidSansMonoSlashed.ttf", new BundledFontInfo("DroidSansMonoSlashed", 1, 0, 0));
jreFontMap.put("DroidSansMonoDotted.ttf", new BundledFontInfo("DroidSansMonoDotted", 1, 0, 0));
/* Droid Serif Family */
jreFontMap.put("droid serif0", "DroidSerif-Regular.ttf");
jreFontMap.put("droid serif1", "DroidSerif-Bold.ttf");
jreFontMap.put("droid serif2", "DroidSerif-Italic.ttf");
jreFontMap.put("droid serif3", "DroidSerif-BoldItalic.ttf");
jreFontMap.put("droid serif bold1", "DroidSerif-Bold.ttf");
jreFontMap.put("droid serif bold italic3", "DroidSerif-BoldItalic.ttf");
jreFontMap.put("DroidSerif-Regular.ttf", new BundledFontInfo("DroidSerif", 1, 0, 0));
jreFontMap.put("DroidSerif-Bold.ttf", new BundledFontInfo("DroidSerif-Bold", 1, 0, 0));
jreFontMap.put("DroidSerif-Italic.ttf", new BundledFontInfo("DroidSerif-Italic", 1, 0, 0));
jreFontMap.put("DroidSerif-BoldItalic.ttf", new BundledFontInfo("DroidSerif-BoldItalic", 1, 0, 0));
/* Idea bundled fonts */
jreFontMap.put("FiraCode bold", "FiraCode-Bold.ttf");
jreFontMap.put("FiraCode light", "FiraCode-Light.ttf");
jreFontMap.put("FiraCode medium", "FiraCode-Medium.ttf");
jreFontMap.put("FiraCode retina", "FiraCode-Retina.ttf");
jreFontMap.put("FiraCode regular", "FiraCode-Regular.ttf");
jreFontMap.put("FiraCode-Bold.ttf", new BundledFontInfo("FiraCode-Bold", 1, 206, 0));
jreFontMap.put("FiraCode-Light.ttf", new BundledFontInfo("FiraCode-Light", 1, 206, 0));
jreFontMap.put("FiraCode-Medium.ttf", new BundledFontInfo("FiraCode-Medium", 1, 206, 0));
jreFontMap.put("FiraCode-Retina.ttf", new BundledFontInfo("FiraCode-Retina", 1, 206, 0));
jreFontMap.put("FiraCode-Regular.ttf", new BundledFontInfo("FiraCode-Regular", 1, 206, 0));
jreFamilyMap.put("FiraCode-Medium", "Fira Code Medium");
jreFamilyMap.put("FiraCode-Retina", "Fira Code Retina");
jreFamilyMap.put("FiraCode-Light", "Fira Code Light");
jreFontMap.put("SourceCodePro bold italic", "SourceCodePro-BoldIt.ttf");
jreFontMap.put("SourceCodePro regular", "SourceCodePro-Regular.ttf");
jreFontMap.put("SourceCodePro bold", "SourceCodePro-Bold.ttf");
jreFontMap.put("SourceCodePro italic", "SourceCodePro-It.ttf");
jreFontMap.put("SourceCodePro-BoldIt.ttf", new BundledFontInfo("SourceCodePro-BoldIt", 1, 30, 0));
jreFontMap.put("SourceCodePro-Regular.ttf", new BundledFontInfo("SourceCodePro-Regular", 2, 10, 0));
jreFontMap.put("SourceCodePro-Bold.ttf", new BundledFontInfo("SourceCodePro-Bold", 2, 10, 0));
jreFontMap.put("SourceCodePro-It.ttf", new BundledFontInfo("SourceCodePro-It", 1, 30, 0));
jreFontMap.put("Inconsolata", "Inconsolata.ttf");
jreFontMap.put("Inconsolata.ttf", new BundledFontInfo("Inconsolata", 1, 10, 0));
jreFontMap.put("Roboto light", "Roboto-Light.ttf");
jreFontMap.put("Roboto thin", "Roboto-Thin.ttf");
jreFontMap.put("Roboto-Light.ttf", new BundledFontInfo("Roboto-Light", 1, 100141, 0));
jreFontMap.put("Roboto-Thin.ttf", new BundledFontInfo("Roboto-Thin", 1, 100141, 0));
jreFamilyMap.put("Roboto-Light", "Roboto Light");
jreFamilyMap.put("Roboto-Thin", "Roboto Thin");
jreFontMap.put("JetBrains Mono Bold", "JetBrainsMono-Bold.ttf");
jreFontMap.put("JetBrains Mono Regular", "JetBrainsMono-Regular.ttf");
jreFontMap.put("JetBrains Mono Italic", "JetBrainsMono-Italic.ttf");
jreFontMap.put("JetBrains Mono Bold Italic", "JetBrainsMono-Bold-Italic.ttf");
jreFontMap.put("JetBrainsMono-Bold.ttf", new BundledFontInfo("JetBrainsMono-Bold", 1, 0, 2));
jreFontMap.put("JetBrainsMono-Regular.ttf", new BundledFontInfo("JetBrainsMono-Regular", 1, 0, 2));
jreFontMap.put("JetBrainsMono-Italic.ttf", new BundledFontInfo("JetBrainsMono-Italic", 1, 0, 2));
jreFontMap.put("JetBrainsMono-Bold-Italic.ttf", new BundledFontInfo("JetBrainsMono-BoldItalic", 1, 0, 2));
jreBundledFontFiles.addAll(jreFontMap.values());
jreBundledFontFiles.addAll(jreFontMap.keySet());
}
static {
@@ -353,6 +381,9 @@ public abstract class SunFontManager implements FontSupport, FontManagerForSGE {
default: throw new RuntimeException("Unexpected address size");
}
versionCheckEnabled =
!("true".equals(System.getProperty("java2d.font.noVersionCheck")));
noType1Font =
"true".equals(System.getProperty("sun.java2d.noType1Font"));
jreLibDirName =
@@ -3887,4 +3918,90 @@ public abstract class SunFontManager implements FontSupport, FontManagerForSGE {
public boolean areColorGlyphsSupported() {
return false;
}
protected BundledFontInfo getBundledFontInfo(String fileName) {
return jreFontMap.get(fileName);
}
protected static boolean parseFontVersion(String versionString, int[] version) {
int i = 0;
boolean foundDigit = false;
version[0] = version[1] = version[2] = 0;
// Skip prefix letters
while (i < versionString.length() &&
!(foundDigit = Character.isDigit(versionString.charAt(i)))) {
i++;
}
if (!foundDigit) {
return false;
}
StringBuilder buf = new StringBuilder();
boolean foundDot = false;
// Read major version
while (i < versionString.length() &&
!(foundDot = (versionString.charAt(i) == '.')) &&
Character.isDigit(versionString.charAt(i))) {
buf.append(versionString.charAt(i));
i++;
}
try {
version[0] = Integer.parseInt(buf.toString());
} catch (NumberFormatException e) {
return false;
}
if (!foundDot) {
return true;
}
buf.setLength(0);
i++;
foundDigit = false;
// Read minor version
while (i < versionString.length() &&
!(foundDot = (versionString.charAt(i) == '.')) &&
Character.isDigit(versionString.charAt(i))) {
buf.append(versionString.charAt(i));
foundDigit = true;
i++;
}
if (!foundDigit) {
return true;
}
try {
version[1] = Integer.parseInt(buf.toString());
} catch (NumberFormatException e) {
return true;
}
if (!foundDot) {
return true;
}
buf.setLength(0);
i++;
foundDigit = false;
// Read bugfix version
while (i < versionString.length() &&
!(versionString.charAt(i) == '.') &&
Character.isDigit(versionString.charAt(i))) {
buf.append(versionString.charAt(i));
foundDigit = true;
i++;
}
if (!foundDigit) {
return true;
}
try {
version[2] = Integer.parseInt(buf.toString());
} catch (NumberFormatException e) {
}
return true;
}
}

View File

@@ -37,7 +37,6 @@ import sun.java2d.DisposerRecord;
import java.awt.geom.Point2D;
import java.lang.ref.SoftReference;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Locale;
import java.util.WeakHashMap;
/*
@@ -152,32 +151,13 @@ public final class SunLayoutEngine implements LayoutEngine, LayoutEngineFactory
this.key = key;
}
static WeakHashMap<Font2D, Boolean> aatInfo = new WeakHashMap<>();
private static final WeakHashMap<Font2D, FaceRef> facePtr =
new WeakHashMap<>();
private static boolean isAAT(Font2D font) {
Boolean aatObj;
synchronized (aatInfo) {
aatObj = aatInfo.get(font);
}
if (aatObj != null) {
return aatObj.booleanValue();
}
boolean aat = false;
if (font instanceof TrueTypeFont) {
TrueTypeFont ttf = (TrueTypeFont)font;
aat = ttf.getDirectoryEntry(TrueTypeFont.morxTag) != null ||
ttf.getDirectoryEntry(TrueTypeFont.mortTag) != null;
} else if (font instanceof PhysicalFont) {
PhysicalFont pf = (PhysicalFont)font;
aat = pf.getTableBytes(TrueTypeFont.morxTag) != null ||
pf.getTableBytes(TrueTypeFont.mortTag) != null;
}
synchronized (aatInfo) {
aatInfo.put(font, Boolean.valueOf(aat));
}
return aat;
// CoreText layout code ignores fractional metrics font attribute
// also, using CoreText layout in Harfbuzz code leads to wrong advances for emoji glyphs
return false;
}
private long getFacePtr(Font2D font2D) {

View File

@@ -181,7 +181,15 @@ public class TrueTypeFont extends FileFont {
private String localeFullName;
private Byte supportedCharset;
private int fontDataSize;
/*
* Used on Windows to validate the font selected by GDI for (sub-pixel
* antialiased) rendering. For 'standalone' fonts it's equal to the font
* file size, for collection (TTC, OTC) members it's the number of bytes in
* the collection file from the start of this font's offset table till the
* end of the file.
*/
int fontDataSize;
public TrueTypeFont(String platname, Object nativeNames, int fIndex,
boolean javaRasterizer)
@@ -1783,11 +1791,6 @@ public class TrueTypeFont extends FileFont {
private static native void getSupportedCharsetsForFamily(String familyName, Map<String, Byte> supportedCharsets);
@Override
int getFontDataSize() {
return fontDataSize;
}
@Override
public String toString() {
return "** TrueType Font: Family="+familyName+ " Name="+fullName+

View File

@@ -25,6 +25,7 @@
package sun.java2d.opengl;
import sun.awt.AWTThreading;
import sun.awt.util.ThreadGroupUtils;
import sun.java2d.pipe.RenderBuffer;
import sun.java2d.pipe.RenderQueue;
@@ -32,6 +33,7 @@ import sun.java2d.pipe.RenderQueue;
import static sun.java2d.pipe.BufferedOpCodes.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.concurrent.TimeUnit;
/**
* OGL-specific implementation of RenderQueue. This class provides a
@@ -153,7 +155,7 @@ public class OGLRenderQueue extends RenderQueue {
}
private class QueueFlusher implements Runnable {
private boolean needsFlush;
private volatile boolean needsFlush;
private Runnable task;
private Error error;
private final Thread thread;
@@ -167,28 +169,49 @@ public class OGLRenderQueue extends RenderQueue {
thread.start();
}
public synchronized void flushNow() {
// wake up the flusher
needsFlush = true;
notify();
public void flushNow() {
flushNow(null);
}
// wait for flush to complete
while (needsFlush) {
private void flushNow(Runnable task) {
Error err;
synchronized (this) {
if (task != null) {
this.task = task;
}
// wake up the flusher
needsFlush = true;
notifyAll();
// wait for flush to complete
try {
wait();
wait(100);
} catch (InterruptedException e) {
}
err = error;
}
if (needsFlush) {
// if we still wait for flush then avoid potential deadlock
err = AWTThreading.executeWaitToolkit(() -> {
synchronized (QueueFlusher.this) {
while (needsFlush) {
try {
QueueFlusher.this.wait();
} catch (InterruptedException e) {
}
}
return error;
}
}, 5, TimeUnit.SECONDS);
}
// re-throw any error that may have occurred during the flush
if (error != null) {
throw error;
if (err != null) {
throw err;
}
}
public synchronized void flushAndInvokeNow(Runnable task) {
this.task = task;
flushNow();
public void flushAndInvokeNow(Runnable task) {
flushNow(task);
}
public synchronized void run() {
@@ -242,7 +265,7 @@ public class OGLRenderQueue extends RenderQueue {
task = null;
// allow the waiting thread to continue
needsFlush = false;
notify();
notifyAll();
}
}
}

View File

@@ -1432,11 +1432,11 @@ static jlong
}
if (context->fmType == TEXT_FM_ON) {
double advh = FTFixedToFloat(ftglyph->linearHoriAdvance);
float advh = FTFixedToFloat(ftglyph->linearHoriAdvance);
glyphInfo->advanceX =
(float) (advh * FTFixedToFloat(context->transform.xx));
glyphInfo->advanceY =
(float) (-advh * FTFixedToFloat(context->transform.yx));
(float) - (advh * FTFixedToFloat(context->transform.yx));
} else {
if (!ftglyph->advance.y) {
glyphInfo->advanceX =

View File

@@ -54,7 +54,7 @@ import sun.awt.X11GraphicsEnvironment;
import sun.java2d.pipe.Region;
import sun.util.logging.PlatformLogger;
import sun.security.action.GetBooleanAction;
import sun.security.action.GetPropertyAction;
class XWindowPeer extends XPanelPeer implements WindowPeer,
DisplayChangedListener {
@@ -66,11 +66,13 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
private static final PlatformLogger iconLog = PlatformLogger.getLogger("sun.awt.X11.icon.XWindowPeer");
// workaround for JBR-1680 (see https://youtrack.jetbrains.com/issue/JBR-1680#focus=streamItem-27-3821223.0-0)
private static final boolean X11_DISABLE_OVERRIDE_FLAG = AccessController.doPrivileged(
new GetBooleanAction("x11.disable.override.flag"));
private static final boolean X11_DISABLE_OVERRIDE_XWINDOWPEER = AccessController.doPrivileged(
new GetBooleanAction("x11.disable.override.xwindowpeer"));
//
// enable X11_DISABLE_OVERRIDE_FLAG by default (users haven't found side-effects), see
// https://youtrack.jetbrains.com/issue/JBR-1680#focus=streamItem-27-3924178.0-0
private static final boolean X11_DISABLE_OVERRIDE_FLAG =
GetPropertyAction.privilegedGetProperty("x11.disable.override.flag", "true").equalsIgnoreCase("true");
private static final boolean X11_DISABLE_OVERRIDE_XWINDOWPEER =
GetPropertyAction.privilegedGetProperty("x11.disable.override.xwindowpeer", "false").equalsIgnoreCase("true");
// should be synchronized on awtLock
private static Set<XWindowPeer> windows = new HashSet<XWindowPeer>();

View File

@@ -499,12 +499,38 @@ void CheckFontSmoothingSettings(HWND hWnd) {
}
}
BOOL GetDwmColorizationColorFromRegistry(DWORD& colorizationColor, DWORD& colorizationColorBalance) {
// DwmGetColorizationColor is unreliable: it doesn't extract the actual accent color value set by user. To get the
// actual value (usable e.g. to calculate the border colors), we have to use the registry.
DWORD colorizationColorBgr = 0;
DWORD dwordSize(sizeof(DWORD));
HKEY hKey = NULL;
if (::RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\DWM", 0, KEY_READ, &hKey) != ERROR_SUCCESS) return FALSE;
if (::RegQueryValueEx(hKey, L"ColorizationColor", NULL, NULL, reinterpret_cast<LPBYTE>(&colorizationColorBgr), &dwordSize) != ERROR_SUCCESS) {
RegCloseKey(hKey);
return FALSE;
}
int b = colorizationColorBgr & 0xFF;
int g = (colorizationColorBgr & 0xFF00) >> 8;
int r = (colorizationColorBgr & 0xFF0000) >> 16;
colorizationColor = RGB(r, g, b);
if (::RegQueryValueEx(hKey, L"ColorizationColorBalance", NULL, NULL, reinterpret_cast<LPBYTE>(&colorizationColorBalance), &dwordSize) != ERROR_SUCCESS) {
colorizationColorBalance = -1;
}
RegCloseKey(hKey);
return TRUE;
}
BOOL ColorizationColorAffectsBorders() {
DWORD result = 0;
DWORD bufSize(sizeof(DWORD));
HKEY hKey = NULL;
if (::RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\DWM"), 0, KEY_READ, &hKey) != ERROR_SUCCESS) return TRUE;
if (::RegQueryValueEx(hKey, _T("ColorPrevalence"), NULL, NULL, reinterpret_cast<LPBYTE>(&result), &bufSize) != ERROR_SUCCESS) return TRUE;
if (::RegQueryValueEx(hKey, _T("ColorPrevalence"), NULL, NULL, reinterpret_cast<LPBYTE>(&result), &bufSize) != ERROR_SUCCESS) result = 1;
RegCloseKey(hKey);
return result == 0 ? FALSE : TRUE;
}
@@ -530,11 +556,14 @@ void AwtDesktopProperties::GetColorParameters() {
BOOL enabled;
DwmIsCompositionEnabled(&enabled);
if (enabled) {
DWORD color;
BOOL opaque = FALSE;
// [tav] todo: listen WM_DWMCOLORIZATIONCOLORCHANGED
DwmGetColorizationColor(&color, &opaque);
SetColorProperty(TEXT("win.dwm.colorizationColor"), RGB(GetBValue(color), GetGValue(color), GetRValue(color)));
DWORD color = 0;
DWORD balance = 0;
if (GetDwmColorizationColorFromRegistry(color, balance)) {
SetColorProperty(TEXT("win.dwm.colorizationColor"), color);
SetIntegerProperty(TEXT("win.dwm.colorizationColorBalance"), balance);
}
SetBooleanProperty(TEXT("win.dwm.colorizationColor.affects.borders"), ColorizationColorAffectsBorders());
}

View File

@@ -1842,15 +1842,30 @@ MsgRouting AwtFrame::WmNcCalcSize(BOOL wParam, LPNCCALCSIZE_PARAMS lpncsp, LRESU
rect->top += insets.bottom;
// [moklev] Workaround for RIDER-27069, IDEA-211327
if (!this->IsUndecorated()) {
rect->right += this->ScaleUpX(1);
// [tav] Decrement NC bottom only when taskbar is bottom/autohide (JBR-1786)
APPBARDATA abData;
abData.uEdge = 0;
abData.cbSize = sizeof(abData);
if (::SHAppBarMessage(ABM_GETTASKBARPOS, &abData) &&
abData.uEdge == ABE_BOTTOM &&
::SHAppBarMessage(ABM_GETSTATE, &abData) == ABS_AUTOHIDE)
if (::SHAppBarMessage(ABM_GETSTATE, &abData) == ABS_AUTOHIDE &&
::SHAppBarMessage(ABM_GETTASKBARPOS, &abData))
{
rect->bottom -= 1;
// [tav] leave one pixel for autohide taskbar
switch (abData.uEdge) {
case ABE_TOP:
rect->top += 1;
break;
case ABE_LEFT:
rect->left += 1;
break;
case ABE_BOTTOM:
rect->bottom -= 1;
break;
case ABE_RIGHT:
rect->right -= 1;
break;
}
}
if (abData.uEdge != ABE_RIGHT) {
rect->right += this->ScaleUpX(1);
}
}
}

View File

@@ -1025,6 +1025,14 @@ LRESULT CALLBACK AwtToolkit::WndProc(HWND hWnd, UINT message,
}
// Remove this define when we move to newer (XP) version of SDK.
#define WM_THEMECHANGED 0x031A
#ifndef WM_DWMCOMPOSITIONCHANGED
#define WM_DWMCOMPOSITIONCHANGED 0x031E
#define WM_DWMNCRENDERINGCHANGED 0x031F
#define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320
#define WM_DWMWINDOWMAXIMIZEDCHANGED 0x0321
#endif // WM_DWMCOMPOSITIONCHANGED
case WM_DWMCOLORIZATIONCOLORCHANGED:
case WM_THEMECHANGED: {
/* Upcall to WToolkit when user changes configuration.
*
@@ -1040,12 +1048,7 @@ LRESULT CALLBACK AwtToolkit::WndProc(HWND hWnd, UINT message,
}
return 0;
}
#ifndef WM_DWMCOMPOSITIONCHANGED
#define WM_DWMCOMPOSITIONCHANGED 0x031E
#define WM_DWMNCRENDERINGCHANGED 0x031F
#define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320
#define WM_DWMWINDOWMAXIMIZEDCHANGED 0x0321
#endif // WM_DWMCOMPOSITIONCHANGED
case WM_DWMCOMPOSITIONCHANGED: {
DWMResetCompositionEnabled();
return 0;

View File

@@ -171,8 +171,9 @@ JNIEXPORT jboolean JNICALL
JNIEXPORT jlong JNICALL
Java_sun_font_FileFontStrike__1getGlyphImageFromWindows
(JNIEnv *env, jobject unused, jstring fontFamily, jint style, jint size,
jint glyphCode, jboolean fm, jint rotation, jbyte charset, jint fontDataSize) {
(JNIEnv *env, jobject unused,
jstring fontFamily, jint style, jint size, jint glyphCode, jboolean fm,
jint rotation, jbyte charset, jint fontDataSize) {
GLYPHMETRICS glyphMetrics;
LOGFONTW lf;
@@ -257,10 +258,10 @@ jint glyphCode, jboolean fm, jint rotation, jbyte charset, jint fontDataSize) {
oldFont = SelectObject(hMemoryDC, hFont);
if (fontDataSize > 0) {
// We cannot specify via GDI which font file to use,
// so we check that it picks the exact font we need by validating font size.
// If it doesn't match, we cannot proceed, as same glyph code can correspond
// to a completely different glyph in the selected font.
// GDI doesn't allow to select a specific font file for drawing, we can
// only check that it picks the file we need by validating font size.
// If it doesn't match, we cannot proceed, as the same glyph code can
// correspond to a completely different glyph in the selected font.
actualFontDataSize = GetFontData(hMemoryDC, 0, 0, NULL, 0);
if (actualFontDataSize != fontDataSize) {
FREE_AND_RETURN;

View File

@@ -0,0 +1,279 @@
/*
* 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.
*/
/*
* @test
* @bug 8224109
* @summary test for consistent text rotation.
*/
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import static java.awt.RenderingHints.*;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.text.AttributedString;
import java.util.Collections;
import javax.imageio.ImageIO;
public class RotatedFontTest {
static final String TEXT = "MMMM"; // Use a short homogenous string.
static final RenderingHints.Key AA_KEY = KEY_TEXT_ANTIALIASING;
static final Object AA_OFF = VALUE_TEXT_ANTIALIAS_OFF;
static final RenderingHints.Key FM_KEY = KEY_FRACTIONALMETRICS;
static final Object FM_ON = VALUE_FRACTIONALMETRICS_ON;
static final Object FM_OFF = VALUE_FRACTIONALMETRICS_OFF;
static final int DRAWSTRING = 0;
static final int TEXTLAYOUT = 1;
static final int GLYPHVECTOR = 2;
static final int LAYEDOUT_GLYPHVECTOR = 3;
public static void main(String... args) throws Exception {
/*
* First verify we have rotation by checking for text colored pixels
* several lines below the baseline of the text.
* Then for subsequent images, check that they are identical to the
* the previous image.
* Do this for both FM on and off.
*/
int x = 100;
int y = 10;
AffineTransform gtx = new AffineTransform();
/* Use monospaced because otherwise an API like TextLayout which
* lays out in a horizontal direction with hints applied might
* sometimes result in a pixel or so difference and cause a
* failure but an effect is not actually a failure of rotation.
* Monospaced needs to be monospaced for this to work, and there
* is also still some risk of this but we can try it.
* This - and fractional metrics is why we use a short string
* and count errors. A long string might have a small difference
* early on that causes a lot of subsequent pixels to be off-by-one.
* This isn't just theoretical. Despite best efforts the test can
* fail like this.
*/
Font font = new Font(Font.MONOSPACED, Font.PLAIN, 20);
String os = System.getProperty("os.name").toLowerCase();
if (os.startsWith("mac")) {
// Avoid a bug with AAT fonts on macos.
font = new Font("Courier New", Font.PLAIN, 20);
}
System.out.println(font);
AffineTransform at = AffineTransform.getRotateInstance(Math.PI / 2);
at.scale(2.0, 1.5);
Font rotFont = font.deriveFont(at);
test(FM_OFF, x, y, rotFont, gtx, "font-rotation-fm-off.png");
test(FM_ON, x, y, rotFont, gtx, "font-rotation-fm-on.png");
// Repeat with rotated graphics, unrotated font
gtx = at;
x = 10;
y = -100;
test(FM_OFF, x, y, font, gtx, "gx-rotation-fm-off.png");
test(FM_ON, x, y, font, gtx, "gx-rotation-fm-on.png");
// Repeat with rotated graphics, rotated font
gtx = AffineTransform.getRotateInstance(Math.PI / 4);
at = AffineTransform.getRotateInstance(Math.PI / 4);
at.scale(2.0, 1.5);
rotFont = font.deriveFont(at);
x = 140;
y = -100;
test(FM_OFF, x, y, rotFont, gtx, "gx-and-font-rotation-fm-off.png");
test(FM_ON, x, y, rotFont, gtx, "gx-and-font-rotation-fm-on.png");
}
static void test(Object fm, int x, int y, Font font,
AffineTransform gtx, String fileName) throws Exception {
BufferedImage img = createNewImage();
draw(img, DRAWSTRING, TEXT, x, y, font, gtx, fm);
ImageIO.write(img, "png", new File(fileName));
checkImageForRotation(img);
BufferedImage imageCopy = copyImage(img);
draw(img, TEXTLAYOUT, TEXT, x, y, font, gtx, fm);
compareImages(imageCopy, img);
draw(img, GLYPHVECTOR, TEXT, x, y, font, gtx, fm);
compareImages(imageCopy, img);
/*
This case needs to be fixed before the test can be enabled.
See bug 8236451.
draw(img, LAYEDOUT_GLYPHVECTOR, TEXT, x, y, font, gtx, fm);
compareImages(imageCopy, img);
*/
}
private static BufferedImage createNewImage() {
BufferedImage img = new BufferedImage(500, 500,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fillRect(0, 0, img.getWidth(), img.getHeight());
g2d.setColor(Color.BLACK);
g2d.dispose();
return img;
}
private static void checkImageForRotation(BufferedImage img)
throws Exception {
/*
* Some expectations are hardwired here.
*/
int firstRowWithBlackPixel = -1;
int lastRowWithBlackPixel = -1;
int width = img.getWidth(null);
int height = img.getHeight(null);
for (int x=0; x<width; x++) {
for (int y=0; y<height; y++) {
int rgb = img.getRGB(x, y);
if ((rgb & 0xffffff) == 0) {
lastRowWithBlackPixel = y;
if (firstRowWithBlackPixel == -1) {
firstRowWithBlackPixel = y;
}
}
}
}
if ((firstRowWithBlackPixel == -1) ||
(lastRowWithBlackPixel - firstRowWithBlackPixel < 40)) {
ImageIO.write(img, "png", new File("font-rotation-failed.png"));
throw new RuntimeException("no rotation " +
"first = " + firstRowWithBlackPixel +
" last = " + lastRowWithBlackPixel);
}
}
private static BufferedImage copyImage(BufferedImage origImg) {
int w = origImg.getWidth(null);
int h = origImg.getHeight(null);
BufferedImage newImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = newImg.createGraphics();
g2d.drawImage(origImg, 0, 0, null);
g2d.dispose();
return newImg;
}
private static void compareImages(BufferedImage i1, BufferedImage i2)
throws Exception {
final int MAXDIFFS = 40;
int maxDiffs = MAXDIFFS;
int diffCnt = 0;
boolean failed = false;
int width = i1.getWidth(null);
int height = i1.getHeight(null);
for (int x=0; x<width; x++) {
for (int y=0; y<height; y++) {
if (maxDiffs == MAXDIFFS) {
int b1 = i1.getRGB(x, y) & 0x0ff;
int b2 = i2.getRGB(x, y) & 0x0ff;
/* If request to use AA_OFF is ignored,
* too hard, give up.
*/
if ((b1 > 0 && b1 < 255) || (b2 > 0 && b2 < 255)) {
System.out.println("AA text, skip.");
return;
}
}
if (i1.getRGB(x, y) != i2.getRGB(x, y)) {
/* This is an attempt to mitigate against small
* differences, especially in the fractional metrics case.
*/
diffCnt++;
if (diffCnt > maxDiffs) {
failed = true;
}
}
}
}
if (failed) {
ImageIO.write(i2, "png", new File("font-rotation-failed.png"));
throw new RuntimeException("images differ, diffCnt="+diffCnt);
}
}
private static void draw(BufferedImage img, int api, String s, int x, int y,
Font font, AffineTransform gtx, Object fm) {
System.out.print("Font:" + font + " GTX:"+ gtx + " FM:" + fm + " using ");
Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.black);
g2d.transform(gtx);
g2d.setRenderingHint(AA_KEY, AA_OFF);
g2d.setRenderingHint(FM_KEY, fm);
g2d.setFont(font);
FontRenderContext frc = g2d.getFontRenderContext();
GlyphVector gv;
Rectangle2D bds = null;
char[] chs;
switch (api) {
case DRAWSTRING:
System.out.println("drawString");
g2d.drawString(s, x, y);
chs = s.toCharArray();
bds = font.getStringBounds(chs, 0, chs.length, frc);
System.out.println("drawString Bounds="+bds);
break;
case TEXTLAYOUT:
System.out.println("TextLayout");
TextLayout tl = new TextLayout(s, font, frc);
tl.draw(g2d, (float)x, (float)y);
System.out.println("TextLayout Bounds="+tl.getBounds());
System.out.println("TextLayout Pixel Bounds="+tl.getPixelBounds(frc, (float)x, (float)y));
break;
case GLYPHVECTOR:
System.out.println("GlyphVector");
gv = font.createGlyphVector(frc, s);
g2d.drawGlyphVector(gv, (float)x, (float)y);
System.out.println("Default GlyphVector Logical Bounds="+gv.getLogicalBounds());
System.out.println("Default GlyphVector Visual Bounds="+gv.getVisualBounds());
System.out.println("Default GlyphVector Pixel Bounds="+gv.getPixelBounds(frc, (float)x, (float)y));
break;
case LAYEDOUT_GLYPHVECTOR:
System.out.println("Layed out GlyphVector");
chs = s.toCharArray();
gv = font.layoutGlyphVector(frc, chs, 0, chs.length, 0);
g2d.drawGlyphVector(gv, (float)x, (float)y);
System.out.println("Layed out GlyphVector Logical Bounds="+gv.getLogicalBounds());
System.out.println("Layed out GlyphVector Visual Bounds="+gv.getVisualBounds());
System.out.println("Layed out GlyphVector Pixel Bounds="+gv.getPixelBounds(frc, (float)x, (float)y));
break;
default: /* do nothing */
}
g2d.dispose();
}
}

View File

@@ -0,0 +1,142 @@
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.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import sun.awt.InvokeOnToolkitHelper;
import sun.lwawt.macosx.CThreading;
import sun.lwawt.macosx.LWCToolkit;
import javax.swing.*;
/*
* @test
* @summary tests that AWTThreading can manage a stream of cross EDT/AppKit 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
* @author Anton Tarasov
*/
public class AWTThreadingTest {
static final ReentrantLock LOCK = new ReentrantLock();
static final Condition COND = LOCK.newCondition();
static final ReentrantLock LOCK_2 = new ReentrantLock();
static final Condition COND_2 = LOCK_2.newCondition();
static final CountDownLatch LATCH = new CountDownLatch(1);
static JFrame frame;
static Thread thread;
final static AtomicBoolean passed = new AtomicBoolean(true);
final static AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
EventQueue.invokeLater(AWTThreadingTest::runGui);
LATCH.await(5, TimeUnit.SECONDS);
frame.dispose();
thread.interrupt();
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();
}
});
frame.setVisible(true);
}
static void startThread() {
thread = new Thread(() -> {
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
break;
}
//
// 1. Execute invokeAndWait() from AppKit to EDT
//
CThreading.executeOnAppKit(() -> {
try {
LWCToolkit.invokeAndWait(counter::incrementAndGet, Window.getWindows()[0]);
} catch (Exception e) {
fail(e);
}
});
//
// 2. Execute invokeAndBlock() from EDT to AppKit
//
EventQueue.invokeLater(() -> {
passed.set(false);
Boolean success = InvokeOnToolkitHelper.invokeAndBlock(() -> {
try {
return CThreading.executeOnAppKit(() -> Boolean.TRUE);
} catch (Throwable e) {
fail(e);
}
return null;
});
System.out.println("Success: " + counter.get() + ": " + success);
passed.set(Boolean.TRUE.equals(success));
if (passed.get()) {
lock(COND::signal);
}
else {
fail(null);
}
});
lock(COND::await);
}
});
thread.setDaemon(true);
thread.start();
}
static void lock(MyRunnable runnable) {
LOCK.lock();
try {
try {
runnable.run();
} catch (Exception e) {
e.printStackTrace();
}
} finally {
LOCK.unlock();
}
}
interface MyRunnable {
void run() throws Exception;
}
static void fail(Throwable e) {
if (e != null) e.printStackTrace();
passed.set(false);
LATCH.countDown();
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2000-2020 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.
*/
import java.awt.AWTException;
import java.awt.GraphicsDevice;
import java.io.IOException;
public class LinuxTouchRobot extends TouchRobot {
private LinuxTouchScreenDevice device;
public LinuxTouchRobot() throws AWTException, IOException {
super();
device = new LinuxTouchScreenDevice();
}
public LinuxTouchRobot(GraphicsDevice screen) throws AWTException, IOException {
super(screen);
device = new LinuxTouchScreenDevice();
}
public void touchClick(int fingerId, int x, int y) throws IOException {
device.click(fingerId, x, y);
}
public void touchMove(int fingerId, int fromX, int fromY, int toX, int toY) throws IOException {
device.move(fingerId, fromX, fromY, toX, toY);
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2000-2020 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.
*/
import java.awt.Toolkit;
import java.io.IOException;
public class LinuxTouchScreenDevice implements AutoCloseable {
// TODO add product id
private int width;
private int height;
private int fileDescriptor;
static {
System.loadLibrary("touchscreen_device");
}
public LinuxTouchScreenDevice() throws IOException {
this(Toolkit.getDefaultToolkit().getScreenSize().width, Toolkit.getDefaultToolkit().getScreenSize().height);
}
public LinuxTouchScreenDevice(int width, int height) throws IOException {
this.width = width;
this.height = height;
fileDescriptor = create(getWidth(), getHeight());
checkCompletion(fileDescriptor,
"Failed to create virtual touchscreen device");
}
@Override
public void close() throws Exception {
checkCompletion(destroy(fileDescriptor),
"Failed to close touchscreen device");
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public void click(int trackingId, int x, int y) throws IOException {
checkCompletion(clickImpl(fileDescriptor, trackingId, x, y),
"Failed to click on touchscreen device");
}
public void move(int trackingId, int fromX, int fromY, int toX, int toY) throws IOException {
checkCompletion(moveImpl(fileDescriptor, trackingId, fromX, fromY, toX, toY),
"Failed to move on virtual touchscreen device");
}
private void checkCompletion(int code, String errorMessage) throws IOException {
if (code < 0) {
throw new IOException(errorMessage);
}
}
private native int create(int width, int height);
private native int destroy(int fd);
private native int clickImpl(int fd, int trackingId, int x, int y);
private native int moveImpl(int fd, int trackingId, int fromX, int fromY, int toX, int toY);
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2000-2020 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.
*/
import java.awt.AWTException;
import java.awt.GraphicsDevice;
import java.awt.Robot;
import java.io.IOException;
public abstract class TouchRobot extends Robot {
public TouchRobot() throws AWTException {
}
public TouchRobot(GraphicsDevice screen) throws AWTException {
super(screen);
}
public abstract void touchClick(int fingerId, int x, int y) throws IOException;
public abstract void touchMove(int fingerId, int fromX, int fromY, int toX, int toY) throws IOException;
}

View File

@@ -0,0 +1,369 @@
/*
* Copyright 2000-2020 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.
*/
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* @test
* @summary Regression test for JBR-2041: Touchscreen devices support
* @requires (jdk.version.major >= 11) & (os.family == "windows")
* @build WindowsTouchRobot TouchScreenEventsTest
* @run main/othervm/native TouchScreenEventsTest
*/
public class TouchScreenEventsTest {
static final int TIMEOUT = 2;
static final int PAUSE = 1000;
// TODO make this constants accessible within jdk
static final int TOUCH_BEGIN = 2;
static final int TOUCH_UPDATE = 3;
static final int TOUCH_END = 4;
public static void main(String[] args) throws Exception {
if(runTest(new TouchClickSuite())
& runTest(new TouchMoveSuite())
& runTest(new TouchTinyMoveSuite())
& runTest(new TouchAxesScrollSuite(TouchAxesScrollSuite.AXIS.X))
& runTest(new TouchAxesScrollSuite(TouchAxesScrollSuite.AXIS.Y))) {
System.out.println("TEST PASSED");
} else {
throw new RuntimeException("TEST FAILED");
}
}
private static boolean runTest(TouchTestSuite suite) throws Exception {
GUI gui = new GUI();
try {
TouchRobot robot = getTouchRobot();
SwingUtilities.invokeAndWait(gui::createAndShow);
suite.addListener(gui.frame);
robot.waitForIdle();
Thread.sleep(PAUSE);
suite.perform(gui, robot);
robot.waitForIdle();
return suite.passed();
} finally {
SwingUtilities.invokeLater(() -> {
if (gui.frame != null) {
gui.frame.dispose();
}
});
Thread.sleep(PAUSE);
}
}
private static TouchRobot getTouchRobot() throws IOException, AWTException {
String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("linux")) {
return new LinuxTouchRobot();
} else if (osName.contains("windows")) {
return new WindowsTouchRobot();
}
throw new RuntimeException("Touch robot for this platform isn't implemented");
}
}
class GUI {
JFrame frame;
Rectangle frameBounds;
void createAndShow() {
frame = new JFrame();
frame.setSize(640, 480);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frameBounds = frame.getBounds();
}
}
interface TouchTestSuite {
void addListener(JFrame frame);
void perform(GUI gui, TouchRobot robot) throws IOException;
boolean passed() throws InterruptedException;
}
class TouchClickSuite implements MouseListener, TouchTestSuite {
private volatile boolean pressed;
private volatile boolean released;
private volatile boolean clicked;
private final CountDownLatch latch = new CountDownLatch(3);
@Override
public void mouseClicked(MouseEvent e) {
if (!clicked) {
clicked = true;
latch.countDown();
}
}
@Override
public void mousePressed(MouseEvent e) {
pressed = true;
if (!pressed) {
pressed = true;
latch.countDown();
}
}
@Override
public void mouseReleased(MouseEvent e) {
if (!released) {
released = true;
latch.countDown();
}
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void perform(GUI gui, TouchRobot robot) throws IOException {
int x = gui.frameBounds.x + gui.frameBounds.width / 2;
int y = gui.frameBounds.y + gui.frameBounds.height / 2;
robot.touchClick(42, x, y);
}
@Override
public boolean passed() throws InterruptedException {
latch.await(TouchScreenEventsTest.TIMEOUT, TimeUnit.SECONDS);
if (!pressed || !released || !clicked) {
String error = (pressed ? "" : "pressed: " + pressed) +
(released ? "" : ", released: " + released) +
(clicked ? "" : ", clicked: " + clicked);
System.out.println("Touch click failed: " + error);
return false;
}
System.out.println("Touch click passed");
return true;
}
@Override
public void addListener(JFrame frame) {
frame.addMouseListener(this);
}
}
class TouchMoveSuite implements MouseWheelListener, TouchTestSuite {
private volatile boolean begin;
private volatile boolean update;
private volatile boolean end;
private final CountDownLatch latch = new CountDownLatch(3);
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getScrollType() == TouchScreenEventsTest.TOUCH_BEGIN) {
if (!begin) {
begin = true;
latch.countDown();
}
} else if (e.getScrollType() == TouchScreenEventsTest.TOUCH_UPDATE) {
if (!update) {
update = true;
latch.countDown();
}
} else if (e.getScrollType() == TouchScreenEventsTest.TOUCH_END) {
if (!end) {
end = true;
latch.countDown();
}
}
}
@Override
public void perform(GUI gui, TouchRobot robot) throws IOException {
int x1 = gui.frameBounds.x + gui.frameBounds.width / 4;
int y1 = gui.frameBounds.y + gui.frameBounds.height / 4;
int x2 = gui.frameBounds.x + gui.frameBounds.width / 2;
int y2 = gui.frameBounds.y + gui.frameBounds.height / 2;
robot.touchMove(42, x1, y1, x2, y2);
}
@Override
public boolean passed() throws InterruptedException {
latch.await(TouchScreenEventsTest.TIMEOUT, TimeUnit.SECONDS);
if (!begin || !update || !end) {
String error = (begin ? "" : "begin: " + begin) +
(update ? "" : ", update: " + update) +
(end ? "" : ", end: " + end);
System.out.println("Touch move failed: " + error);
return false;
}
System.out.println("Touch move passed");
return true;
}
@Override
public void addListener(JFrame frame) {
frame.addMouseWheelListener(this);
}
}
class TouchTinyMoveSuite implements MouseWheelListener, MouseListener, TouchTestSuite {
private volatile boolean scroll;
private volatile boolean click;
private final CountDownLatch latch = new CountDownLatch(1);
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getScrollType() == TouchScreenEventsTest.TOUCH_UPDATE) {
scroll = true;
latch.countDown();
}
}
@Override
public void perform(GUI gui, TouchRobot robot) throws IOException {
int x1 = gui.frameBounds.x + gui.frameBounds.width / 4;
int y1 = gui.frameBounds.y + gui.frameBounds.height / 4;
// move inside tiny area
int x2 = x1 + 1;
int y2 = y1 + 1;
robot.touchMove(42, x1, y1, x2, y2);
}
@Override
public boolean passed() throws InterruptedException {
latch.await(TouchScreenEventsTest.TIMEOUT, TimeUnit.SECONDS);
if (scroll || !click) {
String error = (scroll ? "scroll " + scroll : "") +
(click ? "" : ", click: " + click);
System.out.println("Tiny touch move failed: " + error);
return false;
}
System.out.println("Tiny touch move passed");
return true;
}
@Override
public void addListener(JFrame frame) {
frame.addMouseWheelListener(this);
frame.addMouseListener(this);
}
@Override
public void mouseClicked(MouseEvent e) {
click = true;
latch.countDown();
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
class TouchAxesScrollSuite implements MouseWheelListener, TouchTestSuite {
enum AXIS {X, Y}
private final AXIS axis;
private volatile boolean vertical;
private volatile boolean horizontal;
private final CountDownLatch latch = new CountDownLatch(1);
TouchAxesScrollSuite(AXIS axis) {
this.axis = axis;
}
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
if (e.getScrollType() == TouchScreenEventsTest.TOUCH_UPDATE) {
if (e.isShiftDown()) {
horizontal = true;
} else {
vertical = true;
}
latch.countDown();
}
}
@Override
public void perform(GUI gui, TouchRobot robot) throws IOException {
int x1 = gui.frameBounds.x + gui.frameBounds.width / 4;
int y1 = gui.frameBounds.y + gui.frameBounds.height / 4;
switch (axis) {
case X:
int x2 = gui.frameBounds.x + gui.frameBounds.width / 2;
robot.touchMove(42, x1, y1, x2, y1);
break;
case Y:
int y2 = gui.frameBounds.y + gui.frameBounds.height / 2;
robot.touchMove(42, x1, y1, x1, y2);
break;
}
}
@Override
public boolean passed() throws InterruptedException {
latch.await(TouchScreenEventsTest.TIMEOUT, TimeUnit.SECONDS);
String info = "horizontal: " + horizontal + ", vertical: " + vertical;
switch (axis) {
case X:
if (!horizontal || vertical) {
System.out.println("Touch axes failed: " + info);
return false;
}
break;
case Y:
if (horizontal || !vertical) {
System.out.println("Touch axes failed: " + info);
return false;
}
break;
}
System.out.println("Touch axes passed: " + info);
return true;
}
@Override
public void addListener(JFrame frame) {
frame.addMouseWheelListener(this);
}
}

View File

@@ -0,0 +1,48 @@
#!/bin/bash
#
# Copyright 2000-2020 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.
#
# @test
# @summary Regression test for JBR-2041: Touchscreen devices support
# @requires (jdk.version.major >= 11) & (os.family == "linux")
# @build TouchRobot LinuxTouchRobot LinuxTouchScreenDevice TouchScreenEventsTest
# @run shell TouchScreenEventsTestLinux.sh
# password for sudo
PASSWORD=${BUPWD}
echo "Preparing env for the test:"
echo "> sudo groupadd uinput"
echo ${PASSWORD} | sudo -S groupadd uinput
echo "result=$?"
echo "> sudo usermod -a -G uinput `whoami`"
echo ${PASSWORD} | sudo -S usermod -a -G uinput `whoami`
echo "result=$?"
echo "> sudo chmod g+rw /dev/uinput"
echo ${PASSWORD} | sudo -S chmod g+rw /dev/uinput
echo "result=$?"
echo "> sudo chgrp uinput /dev/uinput"
echo ${PASSWORD} | sudo -S chgrp uinput /dev/uinput
echo "result=$?"
echo "Launching TouchScreenEventsTest.java:"
echo "> $TESTJAVA/bin/java $TESTVMOPTS -cp $TESTCLASSES TouchScreenEventsTest"
$TESTJAVA/bin/java $TESTVMOPTS -cp $TESTCLASSES TouchScreenEventsTest

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2000-2020 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.
*/
import java.awt.AWTException;
import java.awt.GraphicsDevice;
import java.io.IOException;
public class WindowsTouchRobot extends TouchRobot {
static {
System.loadLibrary("windows_touch_robot");
}
public WindowsTouchRobot() throws AWTException, IOException {
super();
}
public WindowsTouchRobot(GraphicsDevice screen) throws AWTException, IOException {
super(screen);
}
@Override
public void touchClick(int fingerId, int x, int y) throws IOException {
// TODO unused finger id cause windows use different finger id model
clickImpl(x, y);
}
@Override
public void touchMove(int fingerId, int fromX, int fromY, int toX, int toY) throws IOException {
// TODO unused finger id cause windows use different finger id model
moveImpl(fromX, fromY, toX, toY);
}
private native void clickImpl(int x, int y);
private native void moveImpl(int fromX, int fromY, int toX, int toY);
}

View File

@@ -0,0 +1,221 @@
/*
* Copyright 2000-2020 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.
*/
#include <jni.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/input.h>
#include <linux/types.h>
#include <linux/uinput.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
// TODO sort includes
// TODO add casts
typedef struct {
__u16 type;
__u16 code;
__s32 value;
} TEventData;
static int set_bit(int fd, unsigned long int request, unsigned long int bit) {
return ioctl(fd, request, bit);
}
static int touch_begin(int fd, int tracking_id, int x, int y) {
struct input_event ev;
int i = 0;
const int cnt = 7;
TEventData eventData[7] = {{EV_ABS, ABS_MT_TRACKING_ID, tracking_id},
{EV_ABS, ABS_MT_POSITION_X, x},
{EV_ABS, ABS_MT_POSITION_Y, y},
{EV_KEY, BTN_TOUCH, 1},
{EV_ABS, ABS_X, x},
{EV_ABS, ABS_Y, y},
{EV_SYN, 0, 0}};
for (i = 0; i < cnt; i++) {
memset(&ev, 0, sizeof(struct input_event));
ev.type = eventData[i].type;
ev.code = eventData[i].code;
ev.value = eventData[i].value;
if (write(fd, &ev, sizeof(struct input_event)) < 0) {
return -1;
}
}
return 0;
}
static int touch_update(int fd, int x, int y) {
struct input_event ev;
int i = 0;
const int cnt = 5;
TEventData eventData[5] = {{EV_ABS, ABS_MT_POSITION_X, x},
{EV_ABS, ABS_MT_POSITION_Y, y},
{EV_ABS, ABS_X, x},
{EV_ABS, ABS_Y, y},
{EV_SYN, 0, 0}};
for (i = 0; i < cnt; i++) {
memset(&ev, 0, sizeof(struct input_event));
ev.type = eventData[i].type;
ev.code = eventData[i].code;
ev.value = eventData[i].value;
if (write(fd, &ev, sizeof(struct input_event)) < 0) {
return -1;
}
}
return 0;
}
// TODO consider different name in case of multitouch
static int touch_end(int fd) {
struct input_event ev;
int i = 0;
const int cnt = 3;
TEventData eventData[3] = {
{EV_ABS, ABS_MT_TRACKING_ID, -1}, {EV_KEY, BTN_TOUCH, 0}, {EV_SYN, 0, 0}};
for (i = 0; i < cnt; i++) {
memset(&ev, 0, sizeof(struct input_event));
ev.type = eventData[i].type;
ev.code = eventData[i].code;
ev.value = eventData[i].value;
if (write(fd, &ev, sizeof(struct input_event)) < 0) {
return -1;
}
}
return 0;
}
/*
* Class: TouchScreenDevice
* Method: create
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_LinuxTouchScreenDevice_create(JNIEnv *env,
jobject o,
jint width,
jint height) {
int productId = 123;
const int FAKE_VENDOR_ID = 0x453;
const int MAX_FINGER_COUNT = 9;
const int MAX_TRACKING_ID = 65535;
int fd = open("/dev/uinput", O_WRONLY | O_NONBLOCK);
if (fd < 0) {
return -1;
}
int ret_code = 0;
ret_code = set_bit(fd, UI_SET_EVBIT, EV_SYN);
ret_code = set_bit(fd, UI_SET_EVBIT, EV_KEY);
ret_code = set_bit(fd, UI_SET_KEYBIT, BTN_TOUCH);
ret_code = set_bit(fd, UI_SET_EVBIT, EV_ABS);
ret_code = set_bit(fd, UI_SET_ABSBIT, ABS_X);
ret_code = set_bit(fd, UI_SET_ABSBIT, ABS_Y);
ret_code = set_bit(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
ret_code = set_bit(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
ret_code = set_bit(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
ret_code = set_bit(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
ret_code = set_bit(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
if (ret_code < 0) {
return -1;
}
struct uinput_user_dev virtual_touch_device;
memset(&virtual_touch_device, 0, sizeof(virtual_touch_device));
snprintf(virtual_touch_device.name, UINPUT_MAX_NAME_SIZE,
"Virtual Touch Device - %#x", productId);
virtual_touch_device.id.bustype = BUS_VIRTUAL;
virtual_touch_device.id.vendor = FAKE_VENDOR_ID;
virtual_touch_device.id.product = productId;
virtual_touch_device.id.version = 1;
virtual_touch_device.absmin[ABS_X] = 0;
virtual_touch_device.absmax[ABS_X] = width;
virtual_touch_device.absmin[ABS_Y] = 0;
virtual_touch_device.absmax[ABS_Y] = height;
virtual_touch_device.absmin[ABS_MT_SLOT] = 0;
virtual_touch_device.absmax[ABS_MT_SLOT] = MAX_FINGER_COUNT;
virtual_touch_device.absmin[ABS_MT_POSITION_X] = 0;
virtual_touch_device.absmax[ABS_MT_POSITION_X] = width;
virtual_touch_device.absmin[ABS_MT_POSITION_Y] = 0;
virtual_touch_device.absmax[ABS_MT_POSITION_Y] = height;
virtual_touch_device.absmin[ABS_MT_TRACKING_ID] = 0;
virtual_touch_device.absmax[ABS_MT_TRACKING_ID] = MAX_TRACKING_ID;
ret_code = write(fd, &virtual_touch_device, sizeof(virtual_touch_device));
ret_code = ioctl(fd, UI_DEV_CREATE);
if (ret_code < 0) {
return -1;
}
return fd;
}
/*
* Class: TouchScreenDevice
* Method: destroy
* Signature: (I)V
*/
JNIEXPORT jint JNICALL Java_LinuxTouchScreenDevice_destroy(JNIEnv *env,
jobject o,
jint fd) {
if (ioctl(fd, UI_DEV_DESTROY) < 0) {
return -1;
}
return close(fd);
}
/*
* Class: TouchScreenDevice
* Method: clickImpl
* Signature: (IIII)V
*/
// TODO return code with checked exception
JNIEXPORT jint JNICALL Java_LinuxTouchScreenDevice_clickImpl(
JNIEnv *env, jobject o, jint fd, jint trackingId, jint x, jint y) {
touch_begin(fd, trackingId, x, y);
touch_end(fd);
return 0;
}
/*
* Class: TouchScreenDevice
* Method: moveImpl
* Signature: (IIIIII)V
*/
JNIEXPORT jint JNICALL Java_LinuxTouchScreenDevice_moveImpl(
JNIEnv *env, jobject o, jint fd, jint trackingId, jint fromX, jint fromY,
jint toX, jint toY) {
touch_begin(fd, trackingId, fromX, fromY);
touch_update(fd, toX, toY);
touch_end(fd);
return 0;
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2000-2020 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.
*/
#include <jni.h>
#include <windows.h>
#include <WinUser.h>
POINTER_TOUCH_INFO create_touch_info()
{
POINTER_TOUCH_INFO contact;
InitializeTouchInjection(1, TOUCH_FEEDBACK_DEFAULT); // Number of contact point
memset(&contact, 0, sizeof(POINTER_TOUCH_INFO));
contact.touchFlags = TOUCH_FLAG_NONE;
contact.touchMask = TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION | TOUCH_MASK_PRESSURE;
contact.orientation = 90; // Orientation of 90 means touching perpendicular to screen.
contact.pressure = 32000;
return contact;
}
void set_contact_area(POINTER_TOUCH_INFO *contact)
{
// 4 x 4 pixel contact area
contact->rcContact.top = contact->pointerInfo.ptPixelLocation.y - 2;
contact->rcContact.bottom = contact->pointerInfo.ptPixelLocation.y + 2;
contact->rcContact.left = contact->pointerInfo.ptPixelLocation.x - 2;
contact->rcContact.right = contact->pointerInfo.ptPixelLocation.x + 2;
}
void touchBegin(POINTER_TOUCH_INFO* contact, LONG x, LONG y)
{
contact->pointerInfo.pointerType = PT_TOUCH;
// primary finger pointerId == 0
contact->pointerInfo.pointerId = 0;
contact->pointerInfo.ptPixelLocation.x = x;
contact->pointerInfo.ptPixelLocation.y = y;
set_contact_area(contact);
contact->pointerInfo.pointerFlags = POINTER_FLAG_DOWN | POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT;
InjectTouchInput(1, contact);
}
void touchUpdate(POINTER_TOUCH_INFO* contact, LONG x, LONG y)
{
contact->pointerInfo.pointerFlags = POINTER_FLAG_UPDATE | POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT;
contact->pointerInfo.ptPixelLocation.x = x;
contact->pointerInfo.ptPixelLocation.y = y;
InjectTouchInput(1, contact);
}
void touchEnd(POINTER_TOUCH_INFO* contact)
{
contact->pointerInfo.pointerFlags = POINTER_FLAG_UP;
InjectTouchInput(1, contact);
}
/*
* Class: quality_util_WindowsTouchRobot
* Method: clickImpl
* Signature: (II)V
*/
JNIEXPORT void JNICALL
Java_WindowsTouchRobot_clickImpl(JNIEnv* env, jobject o,
jint x, jint y)
{
POINTER_TOUCH_INFO contact = create_touch_info();
touchBegin(&contact, x, y);
touchEnd(&contact);
}
/*
* Class: quality_util_WindowsTouchRobot
* Method: moveImpl
* Signature: (IIII)V
*/
JNIEXPORT void JNICALL
Java_WindowsTouchRobot_moveImpl(JNIEnv* env, jobject o,
jint fromX, jint fromY,
jint toX, jint toY)
{
POINTER_TOUCH_INFO contact = create_touch_info();
touchBegin(&contact, fromX, fromY);
touchUpdate(&contact, toX, toY);
touchEnd(&contact);
}

View File

@@ -0,0 +1,117 @@
// Copyright 2000-2020 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 org.cef.CefApp;
import org.cef.CefClient;
import org.cef.CefSettings;
import org.cef.handler.CefAppHandlerAdapter;
import java.awt.*;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author Anton Tarasov
*/
public abstract class JBCefApp {
public static class OS {
public static final boolean WINDOWS;
public static final boolean LINUX;
public static final boolean MAC;
public static final boolean UNKNOWN;
static {
String name = System.getProperty("os.name").toLowerCase();
WINDOWS = name.contains("windows");
LINUX = name.contains("linux");
MAC = name.contains("mac") || name.contains("darwin");
UNKNOWN = !(WINDOWS || LINUX || MAC);
}
}
private static JBCefApp INSTANCE;
private final CefApp myCefApp;
private static final AtomicBoolean ourInitialized = new AtomicBoolean(false);
private JBCefApp() {
CefApp.startup();
CefSettings settings = new CefSettings();
settings.windowless_rendering_enabled = false;
settings.log_severity = CefSettings.LogSeverity.LOGSEVERITY_ERROR;
String[] appArgs = applyPlatformSettings(settings);
CefApp.addAppHandler(new CefAppHandlerAdapter(appArgs) {});
myCefApp = CefApp.getInstance(settings);
}
public static JBCefApp getInstance() {
if (!ourInitialized.getAndSet(true)) {
if (OS.MAC) {
INSTANCE = new JBCefAppMac();
}
else if (OS.LINUX) {
INSTANCE = new JBCefAppLinux();
}
else if (OS.WINDOWS) {
INSTANCE = new JBCefAppWindows();
}
else {
INSTANCE = null;
assert false : "JBCefApp platform initialization failed";
}
}
return INSTANCE;
}
protected abstract String[] applyPlatformSettings(CefSettings settings);
private static class JBCefAppMac extends JBCefApp {
@Override
protected String[] applyPlatformSettings(CefSettings settings) {
String JCEF_FRAMEWORKS_PATH = System.getProperty("java.home") + "/Frameworks";
return new String[] {
"--framework-dir-path=" + JCEF_FRAMEWORKS_PATH + "/Chromium Embedded Framework.framework",
"--browser-subprocess-path=" + JCEF_FRAMEWORKS_PATH + "/jcef Helper.app/Contents/MacOS/jcef Helper",
"--disable-in-process-stack-traces"
};
}
}
private static class JBCefAppWindows extends JBCefApp {
@Override
protected String[] applyPlatformSettings(CefSettings settings) {
String JCEF_PATH = System.getProperty("java.home") + "/bin";
settings.resources_dir_path = JCEF_PATH;
settings.locales_dir_path = JCEF_PATH + "/locales";
settings.browser_subprocess_path = JCEF_PATH + "/jcef_helper";
return null;
}
}
private static class JBCefAppLinux extends JBCefApp {
@Override
protected String[] applyPlatformSettings(CefSettings settings) {
String JCEF_PATH = System.getProperty("java.home") + "/lib";
settings.resources_dir_path = JCEF_PATH;
settings.locales_dir_path = JCEF_PATH + "/locales";
settings.browser_subprocess_path = JCEF_PATH + "/jcef_helper";
return new String[] {
"--force-device-scale-factor=" + sysScale()
};
}
}
public CefClient createClient() {
return myCefApp.createClient();
}
public CefApp getCefApp() {
return myCefApp;
}
public static double sysScale() {
try {
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().getDefaultTransform().getScaleX();
} catch (NullPointerException ignore) {}
return 1.0;
}
}

View File

@@ -0,0 +1,75 @@
// Copyright 2000-2020 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 org.cef.CefClient;
import org.cef.browser.CefBrowser;
import org.cef.browser.CefFrame;
import org.cef.handler.CefLoadHandlerAdapter;
import org.cef.network.CefRequest;
import javax.swing.*;
import java.awt.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* @test
* @key headful
* @summary Tests that JCEF starts and loads empty page with no crash
* @author Anton Tarasov
* @run main JCEFStartupTest
*/
public class JCEFStartupTest {
static CefClient CEF_CLIENT = JBCefApp.getInstance().createClient();
static final CountDownLatch LATCH = new CountDownLatch(1);
static volatile boolean PASSED;
JCEFStartupTest() {
JFrame frame = new JFrame("JCEF");
CEF_CLIENT.addLoadHandler(new CefLoadHandlerAdapter() {
@Override
public void onLoadStart(CefBrowser cefBrowser, CefFrame cefFrame, CefRequest.TransitionType transitionType) {
System.out.println("onLoadStart");
}
@Override
public void onLoadEnd(CefBrowser cefBrowser, CefFrame cefFrame, int i) {
System.out.println("onLoadEnd");
// cefBrowser.close(false); <-- todo: makes jtreg fail on exit code
PASSED = true;
LATCH.countDown();
}
@Override
public void onLoadError(CefBrowser cefBrowser, CefFrame cefFrame, ErrorCode errorCode, String s, String s1) {
System.out.println("onLoadError");
}
});
CefBrowser browser = CEF_CLIENT.createBrowser("about:blank", false, false);
frame.add(browser.getUIComponent());
frame.setSize(640, 480);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(JCEFStartupTest::new);
try {
LATCH.await(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
CEF_CLIENT.dispose();
JBCefApp.getInstance().getCefApp().dispose();
if (!PASSED) {
throw new RuntimeException("Test FAILED!");
}
System.out.println("Test PASSED");
}
}

View File

@@ -146,7 +146,7 @@ java/awt/EventQueue/6980209/bug6980209.java
java/awt/FileDialog/8003399/bug8003399.java 8198334 windows-all
java/awt/FileDialog/FileDialogIconTest/FileDialogIconTest.java 8160558 windows-all
java/awt/FileDialog/FilenameFilterTest/FilenameFilterTest.html 8202882 linux-all
java/awt/FileDialog/ModalFocus/FileDialogModalFocusTest.java 8194751 linux-all
java/awt/FileDialog/ModalFocus/FileDialogModalFocusTest.java 8194751 linux-all,macosx-all
java/awt/Focus/8013611/JDK8013611.java 8175366,8213522 windows-all,macosx-all,linux-all
java/awt/Focus/6378278/InputVerifierTest.java 8198616,8047703 macosx-all,linux-4.15,windows-all
java/awt/Focus/6382144/EndlessLoopTest.java 8198617,8047703 generic-all
@@ -559,7 +559,7 @@ java/awt/Paint/PaintNativeOnUpdate.java
java/awt/Paint/bug8024864.java 8176512 windows-all,macosx-all,linux-all
java/awt/PrintJob/PrinterException.java 8194045 generic-all
java/awt/Robot/AcceptExtraMouseButtons/AcceptExtraMouseButtons.java 7107528 linux-all,macosx-all,windows-all
java/awt/Robot/CheckCommonColors/CheckCommonColors.java 8215105 macosx-all
java/awt/Robot/CheckCommonColors/CheckCommonColors.java 8237925 macosx-all,windows-all,linux-all
java/awt/Robot/HiDPIScreenCapture/ScreenCaptureTest.java 8215105 macosx-all,windows-all
java/awt/Robot/ModifierRobotKey/ModifierRobotKeyTest.java 8157173 generic-all
java/awt/Robot/RobotWheelTest/RobotWheelTest.java 8129827 generic-all
@@ -596,6 +596,7 @@ java/awt/Window/BackgroundIsNotUpdated/BackgroundIsNotUpdated.java
java/awt/Window/Grab/GrabTest.java 8196019 macosx-all,windows-all,linux-all
java/awt/Window/MultiWindowApp/ChildAlwaysOnTopTest.java 8215132,8194941 macosx-all,windows-all,linux-all
java/awt/Window/MultiWindowApp/MultiWindowAppTest.java 8159904 macosx-all,windows-all,linux-all
java/awt/Window/OwnedWindowsLeak/OwnedWindowsLeak.java 8225116 windows-all
java/awt/Window/ShapedAndTranslucentWindows/FocusAWTTest.java 8061236 macosx-all,windows-all,linux-all
java/awt/Window/ShapedAndTranslucentWindows/SetShape.java 8208509 macosx-all,windows-all,linux-all
java/awt/Window/ShapedAndTranslucentWindows/SetShapeAndClick.java 8197936 macosx-all,windows-all,linux-4.18.16-200.fc28.x86_64,linux-all (linux-all timeout reproduced with Adopt)
@@ -605,7 +606,7 @@ java/awt/Window/ShapedAndTranslucentWindows/ShapedByAPI.java
java/awt/Window/ShapedAndTranslucentWindows/ShapedTranslucent.java 8078999,8198690 macosx-all,linux-all,windows-all
java/awt/Window/ShapedAndTranslucentWindows/ShapedTranslucentWindowClick.java 8013450 macosx-all,linux-all,windows-all (windows: commit testing time out)
java/awt/Window/ShapedAndTranslucentWindows/StaticallyShaped.java 8198690,8165218 macosx-all,linux-all,windows-all
java/awt/Window/ShapedAndTranslucentWindows/Translucent.java 8222328 linux-all,windows-all
java/awt/Window/ShapedAndTranslucentWindows/Translucent.java 8222328 linux-all,windows-all,macosx-all
java/awt/Window/ShapedAndTranslucentWindows/TranslucentChoice.java 8194129 macosx-all,windows-all,linux-all
java/awt/Window/ShapedAndTranslucentWindows/TranslucentWindowClick.java 8194129 macosx-all,windows-all,linux-all
java/awt/Window/AlwaysOnTop/AutoTestOnTop.java 6847593,8194045 macosx-all,windows-all,linux-all
@@ -674,6 +675,7 @@ java/awt/image/DrawImage/IncorrectDestinationOffset.java
java/awt/image/DrawImage/IncorrectSourceOffset.java 8196086 windows-all
java/awt/image/DrawImage/IncorrectUnmanagedImageRotatedClip.java 8196087 windows-all
java/awt/image/MultiResolutionImage/MultiResolutionDrawImageWithTransformTest.java 8198390 generic-all
java/awt/image/multiresolution/MenuMultiresolutionIconTest.java 8151301 macosx-all,windows-all (windows - nobug commit testing)
java/awt/image/multiresolution/MultiresolutionIconTest.java 8169187 macosx-all,linux-all,windows-all
java/awt/image/multiresolution/MultiResolutionJOptionPaneIconTest.java 8204214,8193941 macosx-all,windows-all,linux-all
java/awt/image/VolatileImage/BitmaskVolatileImage.java 8133102 linux-all
@@ -831,6 +833,7 @@ sun/security/pkcs11/tls/TestKeyMaterial.java
sun/security/pkcs11/KeyStore/SecretKeysBasic.sh 8209398 generic-all
security/infra/java/security/cert/CertPathValidator/certification/ActalisCA.java 8224768 generic-all
security/infra/java/security/cert/CertPathValidator/certification/LuxTrustCA.java 8237888 generic-all
############################################################################
@@ -941,7 +944,7 @@ javax/swing/JPopupMenu/6987844/bug6987844.java
javax/swing/JPopupMenu/7156657/bug7156657.java 8171381 macosx-all,windows-all
javax/swing/JPopupMenu/8075063/ContextMenuScrollTest.java 8202880 linux-all,windows-all,macosx-all
javax/swing/JRadioButton/8033699/bug8033699.java 8197552,8064920 macosx-all,windows-all,linux-all
javax/swing/JRadioButton/8041561/bug8041561.java 8194941 linux-all
javax/swing/JRadioButton/8041561/bug8041561.java 8194941,8233555 linux-all,macosx-all,windows-all (windows - nobug commit testing)
javax/swing/JRadioButton/8075609/bug8075609.java 8197552 macosx-all,windows-all,linux-all
javax/swing/JRadioButton/ButtonGroupFocus/ButtonGroupFocusTest.java 8197552 macosx-all,windows-all,linux-all
javax/swing/JRadioButton/FocusTraversal/FocusTraversal.java 8221902 windows-all,linux-all (windows: commit testing)
@@ -1039,7 +1042,7 @@ javax/swing/text/TableView/TableViewLayoutTest.java
javax/swing/text/Utilities/8134721/bug8134721.java 8199062 generic-all
javax/swing/text/Utilities/8142966/SwingFontMetricsTest.java 8199529 windows-all
javax/swing/text/View/8014863/bug8014863.java 8072110 windows-all,linux-all
javax/swing/text/html/CSS/4530474/bug4530474.java 8194941 linux-all
javax/swing/text/html/CSS/4530474/bug4530474.java 8194941,8151301 linux-all,macosx-all,windows-all (windows - nobug commit testing)
javax/swing/text/html/HTMLEditorKit/5043626/bug5043626.java 8072110 windows-all,linux-all
javax/swing/text/html/StyleSheet/BackgroundImage/BackgroundImagePosition.java 8198409 generic-all
javax/swing/text/html/StyleSheet/bug4936917.java 8208569 macosx-all
@@ -1247,4 +1250,8 @@ javax/swing/JSpinner/WrongEditorTextFieldFont/FontSetToNull.java
javax/swing/JTextArea/4697612/bug4697612.java JBR-1977 linux-aarch64
javax/swing/LookAndFeel/8145547/DemandGTK.java JBR-1977 linux-aarch64
javax/swing/LookAndFeel/8146276/NimbusGlueTest.java JBR-1977 linux-aarch64
sanity/client/SwingSet/src/GridBagLayoutDemoTest.java JBR-1977 linux-aarch64
sanity/client/SwingSet/src/GridBagLayoutDemoTest.java JBR-1977 linux-aarch64
jb/java/jcef/JCEFStartupTest.java JBR-1996,JBR-2094 macosx-all,linux-i386,windows-x86
jb/java/awt/event/TouchScreenEvent/TouchScreenEventsTestLinux.sh generic-all
jb/java/awt/event/TouchScreenEvent/TouchScreenEventsTest.java generic-all

View File

@@ -144,7 +144,6 @@ java/awt/image/VolatileImage/DrawBufImgOp.java
java/awt/image/VolatileImage/DrawHugeImageTest.java nobug windows-all
java/awt/image/VolatileImage/TransparentVImage.java nobug windows-all
java/awt/image/multiresolution/Corrupted2XImageTest.java nobug macosx-all
java/awt/image/multiresolution/MenuMultiresolutionIconTest.java nobug macosx-all,windows-all
java/awt/keyboard/AltPlusNumberKeyCombinationsTest/AltPlusNumberKeyCombinationsTest.java nobug windows-all
java/awt/print/PaintSetEnabledDeadlock/PaintSetEnabledDeadlock.java nobug windows-all,linux-all
@@ -179,7 +178,6 @@ javax/swing/JPopupMenu/6544309/bug6544309.java
javax/swing/JPopupMenu/6691503/bug6691503.java nobug macosx-all
javax/swing/JPopupMenu/6827786/bug6827786.java nobug windows-all
javax/swing/JProgressBar/8015748/JProgressBarOrientationRobotTest.java nobug macosx-all,windows-all
javax/swing/JRadioButton/8041561/bug8041561.java nobug windows-all
javax/swing/JScrollBar/7163696/Test7163696.java nobug windows-all
javax/swing/JScrollBar/bug4202954/bug4202954.java nobug generic-all timeout
javax/swing/JSlider/6401380/bug6401380.java nobug windows-all
@@ -209,7 +207,6 @@ javax/swing/reliability/TaskZoomJFrameChangeState.java
javax/swing/reliability/TaskZoomJFrameRepaint.java nobug linux-all,windows-all
javax/swing/text/Caret/TestCaretPosition.java nobug linux-all
javax/swing/text/FlowView/LayoutTest.java nobug windows-all
javax/swing/text/html/CSS/4530474/bug4530474.java nobug windows-all
javax/swing/text/html/StyleSheet/bug4936917.java nobug macosx-all,windows-all
jb/javax/swing/JDialog/JDialog186.java nobug macosx-all,linux-all,windows-all