Compare commits

..

11 Commits
72 ... jb17-b84

Author SHA1 Message Date
Dmitry Batrak
f757a39090 JBR-3504 a11y focus is set on the wrong element when opening popups
(cherry-picked from commit a69e12e0d2)
2021-09-10 17:49:14 +03:00
Dmitry Batrak
1039653b97 JBR-3726 Modal windows 'disappear' on minimize in KDE
restrict change to KDE only, as it causes problems on GNOME (JBR-3750)

(cherry picked from commit 9c2841028f)
2021-09-10 17:49:09 +03:00
Maxim Kartashev
65fa801231 JBR-3665 Typing is slow in remote X session
Only call XGetKeyboardMapping() once for all valid codes and cache the
resulting table. Use the cache on the subsequent calls to
keycodeToKeysym().
2021-09-10 12:35:45 +03:00
Nikita Gubarkov
3d9ae4dbe8 JBR-3638 Adjust subpixel glyph positions for correct rounding in CStrike#getGlyphImageBounds 2021-09-08 04:23:24 +03:00
Maxim Kartashev
ef8e01b0d4 JBR-2273 JBR musl port
Detect if we're running on a musl-based system by checking for the presence
of the libgcompat.so glibc compatibility library in the process' map.
If so, java is re-started with LD_LIBRARY_PATH set to point to the right
directory with libjvm.so. This works around the problem with the musl
dynamic library loader.

(based on commit 13a904ddb5)
2021-09-07 15:02:17 +03:00
Alexey Ushakov
eaa9c1618e JBR-3727 JBR17-Metal: Flickering on tooltip appearance
Used setOpaque() method to set correct background of platform window
2021-09-03 15:14:58 +02:00
Maxim Kartashev
ee3c7edd84 JBR-3664 Logging for communications with X server
Introduced logging controlled with -Dsun.awt.x11.trace.
Currently, only looks at the AWT lock and reports methods holding it
sorted by average hold time.

(based on commit 792a58ea0e)
(based on commit 770b4dc9c1)
2021-09-01 23:02:37 -07:00
Dmitry Batrak
8a19c38728 JBR-3726 Modal windows 'disappear' on minimize in KDE
(cherry picked from commit d9baf2d9db)
2021-09-01 16:19:38 +03:00
Maxim Kartashev
4fcd80acf0 JBR-3712 Add project creation instructions to JBR README 2021-08-31 14:21:38 +03:00
Maxim Kartashev
6f5dd836de 8269223: -Xcheck:jni WARNINGs working with fonts on Linux
Reviewed-by: prr, serb

(AKA JBR-3542 Fix -Xcheck:jni warnings)
(Based on commit 9bc023220f, includes additional fixes for JBR-specific code)
2021-08-31 13:12:36 +03:00
Maxim Kartashev
2ff21b425e 8267307: Introduce new client property for XAWT: xawt.mwm_decor_title
Reviewed-by: azvegint, serb

(AKA JBR-3416 Introduce new client property for Linux: xawt.mwm_decor_title)
2021-08-30 06:50:11 -07:00
26 changed files with 906 additions and 28 deletions

View File

@@ -16,6 +16,7 @@ It includes a number enhancements in font rendering, HiDPI support, ligatures, p
- [Ubuntu Linux](#ubuntu-linux)
- [Windows](#build-windows)
- [macOS](#macos)
- [Developing](#developing)
- [Contributing](#contributing)
- [Resources](#resources)
@@ -130,6 +131,31 @@ $ make images
```
This will build the release configuration under `./build/macosx-x86_64-server-release/`.
## Developing
You can use [CLion](https://www.jetbrains.com/clion/) to develop native parts of the JetBrains Runtime and
[IntelliJ IDEA](https://www.jetbrains.com/idea/) for the parts written in Java.
Both require projects to be created.
### CLion
Run
```
$ make compile-commands
```
in the git root and open the resulting `build/.../compile_commands.json` file as a project.
Then use `Tools | Compilation Database | Change Project Root` to point to git root of this repository.
See also this detailed step-by-step tutorial for all platforms:
[How to develop OpenJDK with CLion](https://blog.jetbrains.com/clion/2020/03/openjdk-with-clion/).
### IDEA
Run
```
$ sh ./bin/idea.sh
```
in the git root to generate project files (add `--help` for options). If you have multiple
configurations (for example, `release` and `fastdebug`), supply the `--conf <conf_name>` argument.
Then open the git root directory as a project in IDEA.
## Contributing
We are happy to receive your pull requests!
Before you submit one, please sign our [Contributor License Agreement (CLA)](https://www.jetbrains.com/agreements/cla/).

View File

@@ -27,6 +27,7 @@
#include "jvm_md.h"
#include <dirent.h>
#include <dlfcn.h>
#include <link.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
@@ -220,6 +221,39 @@ ContainsLibJVM(const char *env) {
return JNI_FALSE;
}
static int
HaveGLibCCompatLibrary(struct dl_phdr_info* info, size_t size, void* data)
{
static const char * const GLIBC_COMPAT_LIBRARY_NAME = "libgcompat.so";
const char * const so_pathname = info->dlpi_name;
if (so_pathname != NULL && so_pathname[0] != 0) {
const char * const last_slash = JLI_StrRChr(so_pathname, '/');
const char * const so_basename = (last_slash != NULL) ? last_slash + 1 : so_pathname;
if (JLI_StrNCmp(so_basename, GLIBC_COMPAT_LIBRARY_NAME, JLI_StrLen(GLIBC_COMPAT_LIBRARY_NAME)) == 0) {
return JNI_TRUE;
}
}
return 0; /* also means continue to iterate */
}
static jboolean
UsingMusl(void) {
const jlong start = CurrentTimeMicros();
const int found_gcompat = dl_iterate_phdr(HaveGLibCCompatLibrary, NULL);
if (JLI_IsTraceLauncher()) {
const jlong end = CurrentTimeMicros();
JLI_TraceLauncher("%ld micro seconds to check for the musl compatibility layer for glibc\n",
(long)(end - start));
}
return (found_gcompat != 0);
}
/*
* Test whether the environment variable needs to be set, see flowchart.
*/
@@ -243,6 +277,10 @@ RequiresSetenv(const char *jvmpath) {
return JNI_TRUE;
#endif
if (UsingMusl()) {
return JNI_TRUE;
}
llp = getenv("LD_LIBRARY_PATH");
/* no environment variable is a good environment variable */
if (llp == NULL && dmllp == NULL) {

View File

@@ -32,6 +32,7 @@ import java.awt.geom.*;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import sun.lwawt.macosx.CThreading;
import static sun.awt.SunHints.*;
public final class CStrike extends PhysicalStrike {
@@ -205,7 +206,15 @@ public final class CStrike extends PhysicalStrike {
return;
}
result.setRect(floatRect.x + pt.x, floatRect.y + pt.y, floatRect.width, floatRect.height);
boolean subpixel = desc.aaHint == INTVAL_TEXT_ANTIALIAS_ON &&
desc.fmHint == INTVAL_FRACTIONALMETRICS_ON;
float subpixelResolutionX = subpixel ? FontUtilities.subpixelResolution.width : 1;
float subpixelResolutionY = subpixel ? FontUtilities.subpixelResolution.height : 1;
// Before rendering, glyph positions are offset by 0.5 pixels, take into consideration
float x = ((int) (pt.x * subpixelResolutionX + 0.5f)) / subpixelResolutionX;
float y = ((int) (pt.y * subpixelResolutionY + 0.5f)) / subpixelResolutionY;
result.setRect(floatRect.x + x, floatRect.y + y, floatRect.width, floatRect.height);
}
private void getGlyphImageBounds(int glyphCode, float x, float y, Rectangle2D.Float floatRect) {

View File

@@ -291,6 +291,10 @@ public class LWWindowPeer
updateFocusableWindowState();
super.setVisibleImpl(visible);
// TODO: update graphicsConfig, see 4868278
if (visible) {
// Set correct background for a window before making it visible
platformWindow.setOpaque(!isTranslucent());
}
platformWindow.setVisible(visible);
}

View File

@@ -422,15 +422,15 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
protected int getInitialStyleBits() {
// defaults style bits
int styleBits = DECORATED | HAS_SHADOW | CLOSEABLE | MINIMIZABLE | ZOOMABLE | RESIZABLE | TITLE_VISIBLE;
int styleBits = DECORATED | HAS_SHADOW | CLOSEABLE | ZOOMABLE | RESIZABLE | TITLE_VISIBLE;
styleBits |= getFocusableStyleBits();
final boolean isFrame = (target instanceof Frame);
final boolean isDialog = (target instanceof Dialog);
final boolean isPopup = (target.getType() == Window.Type.POPUP);
if (isDialog) {
styleBits = SET(styleBits, MINIMIZABLE, false);
if (isFrame) {
styleBits = SET(styleBits, MINIMIZABLE, true);
}
// Either java.awt.Frame or java.awt.Dialog can be undecorated, however java.awt.Window always is undecorated.

View File

@@ -635,6 +635,7 @@ AWT_ASSERT_APPKIT_THREAD;
DECLARE_CLASS(jc_CCursorManager, "sun/lwawt/macosx/CCursorManager");
DECLARE_STATIC_METHOD(sjm_resetCurrentCursor, jc_CCursorManager, "resetCurrentCursor", "()V");
(*env)->CallStaticVoidMethod(env, jc_CCursorManager, sjm_resetCurrentCursor);
CHECK_EXCEPTION();
}
- (BOOL) canBecomeMainWindow {

View File

@@ -129,6 +129,7 @@ JNI_COCOA_ENTER(env);
JNI_COCOA_ENTER(blockEnv);
// call the user's runnable
(*blockEnv)->CallVoidMethod(blockEnv, runnableRef, jm_run);
CHECK_EXCEPTION_IN_ENV(blockEnv);
(*blockEnv)->DeleteGlobalRef(blockEnv, runnableRef);
JNI_COCOA_EXIT(blockEnv);
});

View File

@@ -183,23 +183,25 @@
* or maybe a way for the app to continue running depending on the exact
* nature of the problem that has been detected and how survivable it is.
*/
#define CHECK_EXCEPTION() \
if ((*env)->ExceptionOccurred(env) != NULL) { \
#define CHECK_EXCEPTION_IN_ENV(env) \
if ((*(env))->ExceptionOccurred(env) != NULL) { \
if ([NSThread isMainThread] == YES) { \
if (getenv("JNU_APPKIT_TRACE")) { \
(*env)->ExceptionDescribe(env); \
(*(env))->ExceptionDescribe(env); \
NSLog(@"%@",[NSThread callStackSymbols]); \
} else { \
(*env)->ExceptionClear(env); \
(*(env))->ExceptionClear(env); \
} \
} \
if (getenv("JNU_NO_COCOA_EXCEPTION") == NULL) { \
[NSException raise:NSGenericException format:@"Java Exception"]; \
} else { \
(*env)->ExceptionClear(env); \
(*(env))->ExceptionClear(env); \
} \
};
#define CHECK_EXCEPTION() CHECK_EXCEPTION_IN_ENV(env)
#define CHECK_EXCEPTION_NULL_RETURN(x, y) \
CHECK_EXCEPTION(); \
if ((x) == NULL) { \

View File

@@ -235,15 +235,38 @@ public abstract class SunToolkit extends Toolkit
private static final ReentrantLock AWT_LOCK = new ReentrantLock();
private static final Condition AWT_LOCK_COND = AWT_LOCK.newCondition();
public interface AwtLockListener {
void afterAwtLocked();
void beforeAwtUnlocked();
}
private static java.util.List<AwtLockListener> awtLockListeners;
public static synchronized void addAwtLockListener(AwtLockListener l) {
if (awtLockListeners == null) {
awtLockListeners = Collections.synchronizedList(new ArrayList<>());
}
awtLockListeners.add(l);
}
public static final void awtLock() {
AWT_LOCK.lock();
if (awtLockListeners != null) {
awtLockListeners.forEach(AwtLockListener::afterAwtLocked);
}
}
public static final boolean awtTryLock() {
return AWT_LOCK.tryLock();
final boolean wasLocked = AWT_LOCK.tryLock();
if (wasLocked && awtLockListeners != null) {
awtLockListeners.forEach(AwtLockListener::afterAwtLocked);
}
return wasLocked;
}
public static final void awtUnlock() {
if (awtLockListeners != null) {
awtLockListeners.forEach(AwtLockListener::beforeAwtUnlocked);
}
AWT_LOCK.unlock();
}

View File

@@ -73,6 +73,12 @@
#include "fontscaler.h"
#define CHECK_EXCEPTION(env, describe) \
if ((*(env))->ExceptionCheck(env)) { \
if (describe) (*(env))->ExceptionDescribe(env);\
else (*(env))->ExceptionClear(env); \
}
#define ftFixed1 (FT_Fixed) (1 << 16)
#define FloatToFTFixed(f) (FT_Fixed)((f) * (float)(ftFixed1))
#define FTFixedToFloat(x) ((x) / (float)(ftFixed1))
@@ -164,6 +170,7 @@ static jclass tkClass;
static jmethodID getScreenResolutionMID;
static jfieldID platNameFID;
static jfieldID familyNameFID;
static jboolean debugFonts; // Stores the value of FontUtilities.debugFonts()
#ifndef DISABLE_FONTCONFIG
typedef FcBool (*FcPatternAddPtrType) (FcPattern *p, const char *object, FcValue value, FcBool append);
@@ -253,6 +260,11 @@ Java_sun_font_FreetypeFontScaler_initIDs(
invalidateScalerMID =
(*env)->GetMethodID(env, FFSClass, "invalidateScaler", "()V");
jboolean ignoreException;
debugFonts = JNU_CallStaticMethodByName(env, &ignoreException,
"sun/font/FontUtilities",
"debugFonts", "()Z").z;
getDefaultToolkitMID =
(*env)->GetStaticMethodID(env, TKClass, "getDefaultToolkit",
"()Ljava/awt/Toolkit;");
@@ -337,6 +349,11 @@ static int getScreenResolution(JNIEnv *env) {
jthrowable exc;
jclass tk = (*env)->CallStaticObjectMethod(
env, tkClass, getDefaultToolkitMID);
exc = (*env)->ExceptionOccurred(env);
if (exc) {
(*env)->ExceptionClear(env);
return DEFAULT_DPI;
}
int dpi = (*env)->CallIntMethod(env, tk, getScreenResolutionMID);
/* Test if there is no exception here (can get java.awt.HeadlessException)
@@ -396,6 +413,9 @@ static void invalidateJavaScaler(JNIEnv *env,
FTScalerInfo* scalerInfo) {
freeNativeResources(env, scalerInfo);
(*env)->CallVoidMethod(env, scaler, invalidateScalerMID);
// NB: Exceptions must not be cleared (and therefore no JNI calls
// performed) after calling this method because it intentionally
// leaves an exception pending.
}
/******************* I/O handlers ***************************/
@@ -446,6 +466,7 @@ static unsigned long ReadTTFontFileFunc(FT_Stream stream,
scalerInfo->font2D,
sunFontIDs.ttReadBlockMID,
bBuffer, offset, numBytes);
CHECK_EXCEPTION(env, debugFonts);
if (bread < 0) {
return 0;
} else {
@@ -465,7 +486,8 @@ static unsigned long ReadTTFontFileFunc(FT_Stream stream,
(*env)->CallObjectMethod(env, scalerInfo->font2D,
sunFontIDs.ttReadBytesMID,
offset, numBytes);
/* If there's an OutofMemoryError then byteArray will be null */
CHECK_EXCEPTION(env, debugFonts);
/* If there's an OutOfMemoryError then byteArray will be null */
if (byteArray == NULL) {
return 0;
} else {
@@ -498,6 +520,7 @@ static unsigned long ReadTTFontFileFunc(FT_Stream stream,
sunFontIDs.ttReadBlockMID,
bBuffer, offset,
scalerInfo->fontDataLength);
CHECK_EXCEPTION(env, debugFonts);
if (bread <= 0) {
return 0;
} else if ((unsigned long)bread < numBytes) {

View File

@@ -110,6 +110,13 @@ class XAtomList {
atoms.remove(atom);
}
/**
* Adds or removes (depending on {@code set} value) atom to the list. Returns {@code true} if the list contents
* has changed after the operation.
*/
public boolean update(XAtom atom, boolean set) {
return set ? atoms.add(atom) : atoms.remove(atom);
}
/**
* Returns size of the list

View File

@@ -32,6 +32,7 @@ import java.awt.event.WindowEvent;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import sun.awt.IconInfo;
import sun.util.logging.PlatformLogger;
@@ -340,7 +341,19 @@ abstract class XDecoratedPeer extends XWindowPeer {
|| ev.get_atom() == XWM.XA_NET_FRAME_EXTENTS.getAtom())
{
if (XWM.getWMID() != XWM.UNITY_COMPIZ_WM) {
getWMSetInsets(XAtom.get(ev.get_atom()));
if (getMWMDecorTitleProperty().isPresent()) {
// Insets might have changed "in-flight" if that property
// is present, so we need to get the actual values of
// insets from the WM and propagate them through all the
// proper channels.
wm_set_insets = null;
Insets in = getWMSetInsets(XAtom.get(ev.get_atom()));
if (in != null && !in.equals(dimensions.getInsets())) {
handleCorrectInsets(in);
}
} else {
getWMSetInsets(XAtom.get(ev.get_atom()));
}
} else {
if (!isReparented()) {
return;
@@ -1319,4 +1332,24 @@ abstract class XDecoratedPeer extends XWindowPeer {
}
super.handleWindowFocusOut(oppositeWindow, serial);
}
public static final String MWM_DECOR_TITLE_PROPERTY_NAME = "xawt.mwm_decor_title";
public final Optional<Boolean> getMWMDecorTitleProperty() {
Optional<Boolean> res = Optional.empty();
if (SunToolkit.isInstanceOf(target, "javax.swing.RootPaneContainer")) {
javax.swing.JRootPane rootpane = ((javax.swing.RootPaneContainer) target).getRootPane();
Object prop = rootpane.getClientProperty(MWM_DECOR_TITLE_PROPERTY_NAME);
if (prop != null) {
res = Optional.of(Boolean.parseBoolean(prop.toString()));
}
}
return res;
}
public final boolean getWindowTitleVisible() {
return getMWMDecorTitleProperty().orElse(true);
}
}

View File

@@ -63,11 +63,27 @@ class XDialogPeer extends XDecoratedPeer implements DialogPeer {
try {
Dialog target = (Dialog)this.target;
if (vis) {
if (target.getModalityType() != Dialog.ModalityType.MODELESS) {
boolean modal = target.getModalityType() != Dialog.ModalityType.MODELESS;
if (modal) {
if (!isModalBlocked()) {
XBaseWindow.ungrabInput();
}
}
if (XWM.getWMID() == XWM.KDE2_WM) {
// In case of KDE, we inform window manager that our window is a modal dialog
// so that it's minimized along with its parent window.
// This is not needed for other WMs, as it seems only KDE allows minimizing 'transient'
// windows, and setting _NET_WM_STATE_MODAL causes other WMs' undesirable behaviour.
// GNOME (mutter WM), for example, enforces centering of modal dialogs with respect
// to their parent, which breaks SiblingChildOrderTest.
XNETProtocol protocol = XWM.getWM().getNETProtocol();
if (protocol != null && protocol.doModalityProtocol()) {
XAtomList net_wm_state = getNETWMState();
if (net_wm_state.update(protocol.XA_NET_WM_STATE_MODAL, modal)) {
setNETWMState(net_wm_state);
}
}
}
} else {
restoreTransientFor(this);
prevTransientFor = null;

View File

@@ -34,6 +34,7 @@ import java.awt.Insets;
import java.awt.MenuBar;
import java.awt.Rectangle;
import java.awt.peer.FramePeer;
import sun.awt.SunToolkit;
import sun.util.logging.PlatformLogger;
import sun.awt.AWTAccessor;
@@ -66,11 +67,7 @@ class XFramePeer extends XDecoratedPeer implements FramePeer {
state = 0;
undecorated = Boolean.valueOf(target.isUndecorated());
winAttr.nativeDecor = !target.isUndecorated();
if (winAttr.nativeDecor) {
winAttr.decorations = XWindowAttributesData.AWT_DECOR_ALL;
} else {
winAttr.decorations = XWindowAttributesData.AWT_DECOR_NONE;
}
winAttr.decorations = getWindowDecorationBits();
winAttr.functions = MWMConstants.MWM_FUNC_ALL;
winAttr.isResizable = true; // target.isResizable();
winAttr.title = target.getTitle();
@@ -80,6 +77,38 @@ class XFramePeer extends XDecoratedPeer implements FramePeer {
Integer.valueOf(winAttr.decorations), Boolean.valueOf(winAttr.initialResizability),
Boolean.valueOf(!winAttr.nativeDecor), Integer.valueOf(winAttr.initialState));
}
registerWindowDecorationChangeListener();
}
private void registerWindowDecorationChangeListener() {
if (SunToolkit.isInstanceOf(target, "javax.swing.RootPaneContainer")) { // avoid unnecessary class loading
javax.swing.JRootPane rootpane = ((javax.swing.RootPaneContainer) target).getRootPane();
rootpane.addPropertyChangeListener(MWM_DECOR_TITLE_PROPERTY_NAME, e -> winAttr.decorations = getWindowDecorationBits() );
}
}
private int getWindowDecorationBits() {
int decorations = XWindowAttributesData.AWT_DECOR_NONE;
final Frame target = (Frame)(this.target);
final boolean useNativeDecor = !target.isUndecorated();
if (useNativeDecor) {
decorations = XWindowAttributesData.AWT_DECOR_ALL;
if (!getWindowTitleVisible()) {
// NB: the window must be [re-]mapped to make this change effective. Also, window insets will probably
// change and that'll be caught by one of the subsequent property change events in XDecoratedPeer
// (not necessarily the very next event, though).
decorations = XWindowAttributesData.AWT_DECOR_BORDER;
}
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Frame''s initial decorations affected by the client property {0}={1}",
MWM_DECOR_TITLE_PROPERTY_NAME, getMWMDecorTitleProperty());
}
}
return decorations;
}
void postInit(XCreateWindowParams params) {

View File

@@ -111,6 +111,9 @@ import java.awt.peer.TextFieldPeer;
import java.awt.peer.TrayIconPeer;
import java.awt.peer.WindowPeer;
import java.beans.PropertyChangeListener;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
@@ -125,6 +128,12 @@ import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Vector;
import java.util.concurrent.TimeUnit;
import java.util.Deque;
import java.util.ArrayDeque;
import java.util.AbstractMap;
import java.util.StringTokenizer;
import java.util.Optional;
import java.util.Set;
import javax.swing.LookAndFeel;
import javax.swing.UIDefaults;
@@ -159,6 +168,280 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
private static final PlatformLogger keyEventLog = PlatformLogger.getLogger("sun.awt.X11.kye.XToolkit");
private static final PlatformLogger backingStoreLog = PlatformLogger.getLogger("sun.awt.X11.backingStore.XToolkit");
public static final class Tracer {
private static int flags; // what to trace (see TRACE... below)
private static String fileName; // where to trace to (file or stderr if null)
private static String pattern; // limit tracing to method names containing this pattern (ignore case)
private static PrintStream outStream; // stream to trace to
private static long threshold = 0; // minimum time delta to record the event
private static boolean verbose = false; // verbose tracing
private static final int TRACELOG = 1;
private static final int TRACETIMESTAMP = 1 << 1;
private static final int TRACESTATS = 1 << 2;
private static void showTraceUsage() {
System.err.println("usage: -Dsun.awt.x11.trace=" +
"[log[,timestamp]],[stats],[name:<substr pattern>]," +
"[out:<filename>],[td=<threshold>],[help],[verbose]");
}
static {
final String trace = System.getProperty("sun.awt.x11.trace");
if (trace != null) {
int traceFlags = 0;
final StringTokenizer st = new StringTokenizer(trace, ",");
while (st.hasMoreTokens()) {
final String tok = st.nextToken();
if (tok.equalsIgnoreCase("stats")) {
traceFlags |= TRACESTATS;
} else if (tok.equalsIgnoreCase("log")) {
traceFlags |= TRACELOG;
} else if (tok.equalsIgnoreCase("timestamp")) {
traceFlags |= TRACETIMESTAMP;
} else if (tok.regionMatches(true, 0, "name:", 0, 5)) {
pattern = tok.substring(5).toUpperCase();
} else if (tok.equalsIgnoreCase("verbose")) {
verbose = true;
} else if (tok.regionMatches(true, 0, "out:", 0, 4)) {
fileName = tok.substring(4);
} else if (tok.regionMatches(true, 0, "td=", 0, 3)) {
try {
threshold = Long.max(Long.parseLong(tok.substring(3)), 0);
} catch (NumberFormatException e) {
showTraceUsage();
}
} else {
if (!tok.equalsIgnoreCase("help")) {
System.err.println("unrecognized token: " + tok);
}
showTraceUsage();
}
}
if (verbose) {
System.err.print("XToolkit logging ");
if ((traceFlags & TRACELOG) != 0) {
System.err.println("enabled");
System.err.print("XToolkit timestamps ");
if ((traceFlags & TRACETIMESTAMP) != 0) {
System.err.println("enabled");
} else {
System.err.println("disabled");
}
} else {
System.err.println("[and timestamps] disabled");
}
System.err.print("XToolkit invocation statistics at exit ");
if ((traceFlags & TRACESTATS) != 0) {
System.err.println("enabled");
} else {
System.err.println("disabled");
}
System.err.print("XToolkit trace output to ");
if (fileName == null) {
System.err.println("System.err");
} else {
System.err.println("file '" + fileName + "'");
}
if (pattern != null) {
System.err.println("XToolkit trace limited to " + pattern);
}
}
Tracer.flags = traceFlags;
}
}
public static boolean tracingEnabled() {
return (flags != 0);
}
private static synchronized PrintStream getTraceOutputFile() {
if (outStream == null) {
outStream = System.err;
if (fileName != null) {
try {
outStream = new PrintStream(new FileOutputStream(fileName), true);
} catch (FileNotFoundException e) {
}
}
}
return outStream;
}
private static long lastTimeMs = System.currentTimeMillis();
private static synchronized void outputTraceLine(String prefix, String line) {
final StringBuilder outStr = new StringBuilder(prefix);
outStr.append(' ');
if ((flags & TRACETIMESTAMP) != 0) {
final long curTimeMs = System.currentTimeMillis();
outStr.append(String.format("+ %1$03dms ", curTimeMs - lastTimeMs));
lastTimeMs = curTimeMs;
}
outStr.append(line);
getTraceOutputFile().println(outStr);
}
public static void traceRawLine(String line) {
getTraceOutputFile().println(line);
}
public static void traceLine(String line) {
outputTraceLine("[LOG] ", line);
}
public static void traceError(String msg) {
outputTraceLine("[ERR] ", msg);
}
private static boolean isInterestedInMethod(String mname) {
return (pattern == null || mname.toUpperCase().contains(pattern));
}
private static final class AwtLockerDescriptor {
public long startTimeMs; // when the locking has occurred
public StackWalker.StackFrame frame; // the frame that called awtLock()
public AwtLockerDescriptor(StackWalker.StackFrame frame, long start) {
this.startTimeMs = start;
this.frame = frame;
}
}
private static final Deque<AwtLockerDescriptor> awtLockersStack = new ArrayDeque<>();
private static void pushAwtLockCaller(StackWalker.StackFrame frame, long startTimeMs) {
// accessed under AWT lock so no need for additional synchronization
awtLockersStack.push(new AwtLockerDescriptor(frame, startTimeMs));
}
private static long popAwtLockCaller(StackWalker.StackFrame frame, long finishTimeMs) {
// accessed under AWT lock so no need for additional synchronization
try {
final AwtLockerDescriptor descr = awtLockersStack.pop();
if (descr.frame.getMethodName().compareTo(frame.getMethodName()) != 0) {
// Note: this often happens with XToolkit.waitForEvents()/XToolkit.run().
// traceError("Mismatching awtLock()/awtUnlock(): locked by " + descr.frame + ", unlocked by " + frame);
}
return finishTimeMs - descr.startTimeMs;
} catch(NoSuchElementException e) {
traceError("No matching awtLock() for awtUnlock(): " + frame);
}
return -1;
}
private static class AwtLockTracer implements SunToolkit.AwtLockListener {
private static final Set<String> awtLockerMethods = Set.of("awtLock", "awtUnlock", "awtTryLock");
private static StackWalker.StackFrame getLockCallerFrame() {
Optional<StackWalker.StackFrame> frame = StackWalker.getInstance().walk(
s -> s.dropWhile(stackFrame -> !awtLockerMethods.contains(stackFrame.getMethodName()))
.dropWhile(stackFrame -> awtLockerMethods.contains( stackFrame.getMethodName()))
.findFirst() );
return frame.orElse(null);
}
public void afterAwtLocked() {
final StackWalker.StackFrame awtLockerFrame = getLockCallerFrame();
if (awtLockerFrame != null) {
final String mname = awtLockerFrame.getMethodName();
if (isInterestedInMethod(mname)) {
pushAwtLockCaller(awtLockerFrame, System.currentTimeMillis());
}
}
}
public void beforeAwtUnlocked() {
final StackWalker.StackFrame awtLockerFrame = getLockCallerFrame();
if (awtLockerFrame != null) {
final String mname = awtLockerFrame.getMethodName();
if (isInterestedInMethod(mname)) {
final long timeSpentMs = popAwtLockCaller(awtLockerFrame, System.currentTimeMillis());
if (timeSpentMs >= threshold) {
updateStatistics(awtLockerFrame.toString(), timeSpentMs);
traceLine(String.format("%s held AWT lock for %dms", awtLockerFrame, timeSpentMs));
}
}
}
}
}
private static final class MethodStats implements Comparable<MethodStats> {
public long minTimeMs;
public long maxTimeMs;
public long count;
private long totalTimeMs;
MethodStats() {
this.minTimeMs = Long.MAX_VALUE;
}
public void update(long timeSpentMs) {
count++;
minTimeMs = Math.min(minTimeMs, timeSpentMs);
maxTimeMs = Math.max(maxTimeMs, timeSpentMs);
totalTimeMs += timeSpentMs;
}
public long averageTimeMs() {
return (long)((double)totalTimeMs / count);
}
@Override
public int compareTo(MethodStats other) {
return Long.compare(other.averageTimeMs(), this.averageTimeMs());
}
@Override
public String toString() {
return String.format("%dms (%dx[%d-%d]ms)", averageTimeMs(), count, minTimeMs, maxTimeMs);
}
}
private static HashMap<String, MethodStats> methodTimingTable;
private static synchronized void updateStatistics(String mname, long timeSpentMs) {
if ((flags & TRACESTATS) != 0) {
if (methodTimingTable == null) {
methodTimingTable = new HashMap<>(1024);
TraceReporter.setShutdownHook();
}
final MethodStats descr = methodTimingTable.computeIfAbsent(mname, k -> new MethodStats());
descr.update(timeSpentMs);
}
}
private static class TraceReporter implements Runnable {
public static void setShutdownHook() {
final Tracer.TraceReporter t = new Tracer.TraceReporter();
final Thread thread = new Thread(ThreadGroupUtils.getRootThreadGroup(), t,
"XToolkit TraceReporter", 0, false);
thread.setContextClassLoader(null);
Runtime.getRuntime().addShutdownHook(thread);
}
public void run() {
traceRawLine("");
traceRawLine("AWT Lock usage statistics");
traceRawLine("=========================");
final ArrayList<AbstractMap.SimpleImmutableEntry<String, MethodStats>> l;
synchronized(Tracer.class) { // in order to avoid methodTimingTable modifications during the traversal
l = new ArrayList<>(methodTimingTable.size());
methodTimingTable.forEach((fname, fdescr)
-> l.add(new AbstractMap.SimpleImmutableEntry<>(fname, fdescr)));
}
l.sort(Map.Entry.comparingByValue());
l.forEach(item -> traceRawLine(item.getValue() + " --- " + item.getKey()));
traceRawLine("Legend: <avg time> ( <times called> x [ <fastest time> - <slowest time> ] ms) --- <caller of XToolkit.awtUnlock()>");
}
}
}
//There is 400 ms is set by default on Windows and 500 by default on KDE and GNOME.
//We use the same hardcoded constant.
private static final int AWT_MULTICLICK_DEFAULT_TIME = 500;
@@ -419,6 +702,10 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
PerformanceLogger.setTime("XToolkit construction");
}
if (Tracer.tracingEnabled()) {
addAwtLockListener(new Tracer.AwtLockTracer());
}
if (!GraphicsEnvironment.isHeadless()) {
String mainClassName = null;

View File

@@ -47,6 +47,8 @@
#include "awt_p.h"
#include "awt_GraphicsEnv.h"
#include "keycode_cache.h"
#define XK_KATAKANA
#include <X11/keysym.h> /* standard X keysyms */
#include <X11/DECkeysym.h> /* DEC vendor-specific */
@@ -812,6 +814,7 @@ isXKBenabled(Display *display) {
return awt_UseXKB;
}
#ifndef USE_KEYCODE_CACHE
/*
* Map a keycode to the corresponding keysym.
* This replaces the deprecated X11 function XKeycodeToKeysym
@@ -836,6 +839,7 @@ keycodeToKeysym(Display *display, KeyCode keycode, int index) {
XFree(key_syms);
return ks;
}
#endif // USE_KEYCODE_CACHE
static Boolean
isKPevent(XEvent *event)

View File

@@ -36,6 +36,7 @@
#include "utility/rect.h"
#include "sun_awt_X11_XlibWrapper.h"
#include "keycode_cache.h"
#include <stdlib.h>
#include <string.h>
@@ -56,9 +57,6 @@
extern Bool statusWindowEventHandler(XEvent event);
#endif
// From XWindow.c
extern KeySym keycodeToKeysym(Display *display, KeyCode keycode, int index);
#if defined(DEBUG)
static jmethodID lockIsHeldMID = NULL;
@@ -179,6 +177,11 @@ JNIEXPORT void JNICALL
Java_sun_awt_X11_XlibWrapper_XCloseDisplay(JNIEnv *env, jclass clazz,
jlong display) {
AWT_CHECK_HAVE_LOCK();
#ifdef USE_KEYCODE_CACHE
resetKeyCodeCache();
#endif
XCloseDisplay((Display*) jlong_to_ptr(display));
}
@@ -2023,6 +2026,11 @@ JNIEXPORT void JNICALL Java_sun_awt_X11_XlibWrapper_XRefreshKeyboardMapping
(JNIEnv *env, jclass clazz, jlong event_ptr)
{
AWT_CHECK_HAVE_LOCK();
#ifdef USE_KEYCODE_CACHE
resetKeyCodeCache();
#endif
XRefreshKeyboardMapping((XMappingEvent*) jlong_to_ptr(event_ptr));
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2021 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "keycode_cache.h"
#include "awt.h"
#ifdef DEBUG
#include <stdio.h>
#endif
#ifdef USE_KEYCODE_CACHE
/**
* Keeps the KeyCode -> KeySym mapping.
*/
typedef struct {
KeySym* symbols; // array of KeySym indexed by the key code with min_code corresponding to index 0
int syms_per_code; // number of elements in 'symbols' corresponding to one key code
int min_code; // minimum valid key code (typically 8)
int max_code; // maximum valid key code (typically 255)
} KeyCodeCache;
static KeyCodeCache keycode_cache = {0};
#ifdef DEBUG
static void
dump_keycode_cache(const KeyCodeCache* cache) {
fprintf(stderr, "KeyCodeCache dump\n");
if (cache->symbols == NULL) {
fprintf(stderr, "-- empty --\n");
} else {
fprintf(stderr, "syms_per_code=%d, min_code=%d, max_code=%d\n",
cache->syms_per_code, cache->min_code, cache->max_code);
for(int i = cache->min_code; i <= cache->max_code; i++) {
fprintf(stderr, "0x%02x --", i);
for(int j = 0; j < cache->syms_per_code; j++) {
const int sym_index = (i - cache->min_code)*cache->syms_per_code + j;
fprintf(stderr, "%04d - ", cache->symbols[sym_index]);
}
fprintf(stderr, "\n");
}
}
}
#endif // DEBUG
/**
* Clears the cache and frees memory, if allocated.
*
* NB: not thread safe and is supposed to be called only when holding the AWT lock.
*/
extern void
resetKeyCodeCache(void) {
if (keycode_cache.symbols) {
XFree(keycode_cache.symbols);
}
keycode_cache = (KeyCodeCache){0};
}
/**
* Translates the given keycode to the corresponding KeySym at the given index.
* Caches the mapping for all valid key codes by using just one XGetKeyboardMapping() Xlib call,
* which greatly reduces delays when working with a remote X server.
*
* NB: not thread safe and is supposed to be called only when holding the AWT lock.
*/
extern KeySym
keycodeToKeysym(Display* display, KeyCode keycode, int index) {
if (!keycode_cache.symbols) {
XDisplayKeycodes(display, &keycode_cache.min_code, &keycode_cache.max_code);
const int count_all = keycode_cache.max_code - keycode_cache.min_code + 1;
keycode_cache.symbols = XGetKeyboardMapping(display, keycode_cache.min_code, count_all, &keycode_cache.syms_per_code);
// NB: this may not always get free'ed
}
if (keycode_cache.symbols) {
const Boolean code_within_range = (keycode >= keycode_cache.min_code && keycode <= keycode_cache.max_code);
const Boolean index_within_range = (index >= 0 && index < keycode_cache.syms_per_code);
if (code_within_range && index_within_range) {
const int sym_index = (keycode - keycode_cache.min_code)*keycode_cache.syms_per_code + index;
KeySym sym = keycode_cache.symbols[sym_index];
return sym;
}
}
return NoSymbol;
}
#endif /* USE_KEYCODE_CACHE */

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2021 JetBrains s.r.o.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef _KEYCODE_CACHE_H
#define _KEYCODE_CACHE_H
#include <X11/Xlib.h>
#define USE_KEYCODE_CACHE 1
void resetKeyCodeCache(void);
KeySym keycodeToKeysym(Display* display, KeyCode keycode, int index);
#endif /* _KEYCODE_CACHE_H */

View File

@@ -866,7 +866,11 @@ void D3DRQ_FlushBuffer(void *pParam)
if (!JNU_IsNull(env, pFlush->runnable)) {
J2dTraceLn(J2D_TRACE_VERBOSE, " executing runnable");
JNU_CallMethodByName(env, NULL, pFlush->runnable, "run", "()V");
jboolean hasException;
JNU_CallMethodByName(env, &hasException, pFlush->runnable, "run", "()V");
if (hasException) {
J2dTraceLn(J2D_TRACE_ERROR, " exception occurred while executing runnable");
}
}
}

View File

@@ -6706,11 +6706,11 @@ JNIEXPORT void JNICALL
Java_java_awt_Component_initIDs(JNIEnv *env, jclass cls)
{
TRY;
jclass inputEventClazz = env->FindClass("java/awt/event/InputEvent");
CHECK_NULL(inputEventClazz);
jmethodID getButtonDownMasksID = env->GetStaticMethodID(inputEventClazz, "getButtonDownMasks", "()[I");
CHECK_NULL(getButtonDownMasksID);
jintArray obj = (jintArray)env->CallStaticObjectMethod(inputEventClazz, getButtonDownMasksID);
jboolean ignoreException;
jintArray obj = (jintArray)JNU_CallStaticMethodByName(env, &ignoreException,
"java/awt/event/InputEvent",
"getButtonDownMasks", "()[I").l;
CHECK_NULL(obj);
jint * tmp = env->GetIntArrayElements(obj, JNI_FALSE);
CHECK_NULL(tmp);
jsize len = env->GetArrayLength(obj);

View File

@@ -823,6 +823,7 @@ void AwtDesktopProperties::SetStringProperty(LPCTSTR propName, LPTSTR value) {
key, jValue);
GetEnv()->DeleteLocalRef(jValue);
GetEnv()->DeleteLocalRef(key);
(void)safe_ExceptionOccurred(GetEnv());
}
void AwtDesktopProperties::SetIntegerProperty(LPCTSTR propName, int value) {
@@ -835,6 +836,7 @@ void AwtDesktopProperties::SetIntegerProperty(LPCTSTR propName, int value) {
AwtDesktopProperties::setIntegerPropertyID,
key, (jint)value);
GetEnv()->DeleteLocalRef(key);
(void)safe_ExceptionOccurred(GetEnv());
}
void AwtDesktopProperties::SetBooleanProperty(LPCTSTR propName, BOOL value) {
@@ -846,6 +848,7 @@ void AwtDesktopProperties::SetBooleanProperty(LPCTSTR propName, BOOL value) {
AwtDesktopProperties::setBooleanPropertyID,
key, value ? JNI_TRUE : JNI_FALSE);
GetEnv()->DeleteLocalRef(key);
(void)safe_ExceptionOccurred(GetEnv());
}
void AwtDesktopProperties::SetColorProperty(LPCTSTR propName, DWORD value) {
@@ -858,6 +861,7 @@ void AwtDesktopProperties::SetColorProperty(LPCTSTR propName, DWORD value) {
key, GetRValue(value), GetGValue(value),
GetBValue(value));
GetEnv()->DeleteLocalRef(key);
(void)safe_ExceptionOccurred(GetEnv());
}
void AwtDesktopProperties::SetFontProperty(HDC dc, int fontID,
@@ -920,6 +924,7 @@ void AwtDesktopProperties::SetFontProperty(HDC dc, int fontID,
key, fontName, style, pointSize);
GetEnv()->DeleteLocalRef(key);
GetEnv()->DeleteLocalRef(fontName);
(void)safe_ExceptionOccurred(GetEnv());
}
}
delete[] face;
@@ -967,6 +972,7 @@ void AwtDesktopProperties::SetFontProperty(LPCTSTR propName, const LOGFONT & fon
key, fontName, style, pointSize);
GetEnv()->DeleteLocalRef(key);
GetEnv()->DeleteLocalRef(fontName);
(void)safe_ExceptionOccurred(GetEnv());
}
void AwtDesktopProperties::SetSoundProperty(LPCTSTR propName, LPCTSTR winEventName) {
@@ -984,6 +990,7 @@ void AwtDesktopProperties::SetSoundProperty(LPCTSTR propName, LPCTSTR winEventNa
key, event);
GetEnv()->DeleteLocalRef(event);
GetEnv()->DeleteLocalRef(key);
(void)safe_ExceptionOccurred(GetEnv());
}
void AwtDesktopProperties::PlayWindowsSound(LPCTSTR event) {

View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @summary Verifies the -Dsun.awt.x11.trace option
* @requires (os.family == "linux")
* @library /test/lib
* @run main X11Trace
*/
import java.awt.AWTException;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.charset.StandardCharsets;
import java.util.stream.Stream;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
public class X11Trace {
public static void main(String[] args) throws Exception {
if (args.length > 0 && args[0].equals("runtest")) {
runTest();
} else {
OutputAnalyzer oa = ProcessTools.executeTestJvm("-Dsun.awt.x11.trace=log,timestamp,stats,td=0", X11Trace.class.getName(), "runtest");
oa.shouldContain("held AWT lock for").shouldContain("AWT Lock usage statistics").shouldHaveExitValue(0);
oa = ProcessTools.executeTestJvm("-Dsun.awt.x11.trace=log", X11Trace.class.getName(), "runtest");
oa.shouldContain("held AWT lock for").shouldNotContain("AWT Lock usage statistics").shouldHaveExitValue(0);
oa = ProcessTools.executeTestJvm("-Dsun.awt.x11.trace=log,timestamp,stats,td=0,out:mylog", X11Trace.class.getName(), "runtest");
oa.shouldHaveExitValue(0).stderrShouldBeEmpty();
final String logFileContents = Files.readString(Paths.get("mylog"));
OutputAnalyzer logOA = new OutputAnalyzer(logFileContents);
logOA.shouldContain("held AWT lock for").shouldContain("AWT Lock usage statistics");
}
}
public static void delay(int ms) {
try {
Thread.sleep(ms);
} catch(InterruptedException e) {
}
}
public static void runTest() {
runSwing(() -> {
final JFrame frame = new JFrame("test");
frame.setVisible(true);
delay(500);
frame.dispose();
});
}
private static void runSwing(Runnable r) {
try {
SwingUtilities.invokeAndWait(r);
} catch (InterruptedException e) {
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -52,7 +52,9 @@ public class SiblingChildOrderTest
frame.setVisible(true);
});
Robot robot = new Robot();
for (int i = 0; i < colors.length; i++) {
robot.delay(100);
int finalI = i;
SwingUtilities.invokeLater(() -> {
dlgs[finalI] = new JDialog(frame, "DLG " + finalI, true);
@@ -63,7 +65,6 @@ public class SiblingChildOrderTest
});
}
Robot robot = new Robot();
robot.waitForIdle();
robot.delay(1000);

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8269223
* @summary Verifies that -Xcheck:jni issues no warnings from freetypeScaler.c
* @library /test/lib
* @run main FreeTypeScalerJNICheck
*/
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GraphicsEnvironment;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
public class FreeTypeScalerJNICheck {
public static void main(String[] args) throws Exception {
if (args.length > 0 && args[0].equals("runtest")) {
runTest();
} else {
ProcessBuilder pb = ProcessTools.createTestJvm("-Xcheck:jni", FreeTypeScalerJNICheck.class.getName(), "runtest");
OutputAnalyzer oa = ProcessTools.executeProcess(pb);
oa.shouldContain("Done").shouldNotContain("WARNING").shouldHaveExitValue(0);
}
}
public static void runTest() {
String families[] = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bi.createGraphics();
for (String ff : families)
{
Font font = new Font(ff, Font.PLAIN, 12);
Rectangle2D bounds = font.getStringBounds("test", g2d.getFontRenderContext());
g2d.setFont(font);
FontMetrics metrics = g2d.getFontMetrics(font);
System.out.println(bounds.getHeight() + metrics.getHeight()); // use bounds and metrics
}
System.out.println("Done");
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) 2021, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @summary Verifies that the launcher doesn't unnecessarily set LD_LIBRARY_PATH on
* non-musl-based systems.
* @requires (os.family == "linux")
* @library /test/lib
* @run main MuslCheck
*/
import java.util.Map;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
public class MuslCheck extends TestHelper {
public static void main(String[] args) throws Exception {
final Map<String, String> envMap = Map.of("_JAVA_LAUNCHER_DEBUG", "true");
TestResult tr = doExec(envMap, javaCmd, "-help");
if (!tr.contains("mustsetenv:")) {
throw new RuntimeException("Test error: the necessary tracing is missing in the output");
}
if (!tr.contains("micro seconds to check for the musl compatibility layer for glibc")) {
throw new RuntimeException("The check for libgcompat seems to be missing");
}
if (tr.contains("mustsetenv: TRUE")) {
throw new RuntimeException("launcher is not supposed to set LD_LIBRARY_PATH");
}
}
}