mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-26 19:29:42 +01:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f757a39090 | ||
|
|
1039653b97 | ||
|
|
65fa801231 | ||
|
|
3d9ae4dbe8 | ||
|
|
ef8e01b0d4 | ||
|
|
eaa9c1618e | ||
|
|
ee3c7edd84 | ||
|
|
8a19c38728 | ||
|
|
4fcd80acf0 | ||
|
|
6f5dd836de | ||
|
|
2ff21b425e |
26
README.md
26
README.md
@@ -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/).
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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) { \
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
110
src/java.desktop/unix/native/libawt_xawt/xawt/keycode_cache.c
Normal file
110
src/java.desktop/unix/native/libawt_xawt/xawt/keycode_cache.c
Normal 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 */
|
||||
@@ -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 */
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
87
test/jdk/java/awt/Debug/X11Trace.java
Normal file
87
test/jdk/java/awt/Debug/X11Trace.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
69
test/jdk/java/awt/font/JNICheck/FreeTypeScalerJNICheck.java
Normal file
69
test/jdk/java/awt/font/JNICheck/FreeTypeScalerJNICheck.java
Normal 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");
|
||||
}
|
||||
}
|
||||
|
||||
52
test/jdk/tools/launcher/MuslCheck.java
Normal file
52
test/jdk/tools/launcher/MuslCheck.java
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user