Compare commits

..

14 Commits

Author SHA1 Message Date
Maxim Kartashev
07b83bad84 JBR-6002 Linux: maximized window goes fullscreen after being moved between monitors 2023-09-01 10:00:31 +04:00
Maxim Kartashev
703e87cc3a JBR-5971 JBR API hash update
Updated the JBR API hash because of changes in comments
in the WindowMove API.
2023-08-30 11:52:39 +04:00
Dmitrii Morskii
dabd52c312 JBR-5844: fix case with non-scalable face 2023-08-29 14:36:46 +02:00
Dmitrii Morskii
574c6d97e9 JBR-5804: refactoring of freetypeScaler and moving fontconfig's logic in separate file 2023-08-29 14:36:02 +02:00
Maxim Kartashev
8ff96bc79f JBR-5971 Wayland: support WindowMove JBR API 2023-08-29 10:48:18 +04:00
Vitaly Provodin
f600c73328 update exclude list on results of 21_b205.2 test runs 2023-08-25 17:33:22 +07:00
Nikita Tsarev
94d6344b43 JBR-5963: Fix RobotKeyboard test and implement getLockingKeyState 2023-08-24 20:06:00 +02:00
Maxim Kartashev
3834f6c24e JBR-5962 Wayland: fix the main event loop to allow for secondary queues
Return READ_RESULT_FINISHED_NO_EVENTS from WLToolkit.readEvents() in
case of poll returning with no new data (i.e. via timeout).
2023-08-23 15:32:12 +04:00
Sergei Tachenov
a4e4430a5a JBR-5824 Ensure popup menus are on the correct screen
This is a very old bug, JDK-6415065.

What happens here is that when the position
of a popup menu is calculated, it can expand
above or below, depending on the position
of the parent menu, the item being expanded
and the size of the submenu and screen resolution.

If the menu decides to expand above,
the position calculation in JMenu.getPopupMenuOrigin
may yield a coordinate above the current screen.
Later, JPopupMenu.adjustPopupLocationToFitScreen
tries to fit the entire menu into the screen.
However, it has no idea which screen is correct,
as all it has is an (x, y) location. If that
location is invalid, it may correct it by
fitting it into the screen. However, if it is
valid, but located on an incorrect screen,
then the whole logic goes awry and the menu
is fitted into the wrong screen.

Fix by pre-adjusting the Y location to fit
into the correct screen in JMenu.getPopupMenuOrigin,
where the correct screen is still known.
The resulting location may still not be final,
as the menu's height needs to be taken into
account as well, but that's exactly what
JPopupMenu.adjustPopupLocationToFitScreen does.
Since the coordinate is on the correct screen now,
it fits the menu into the same screen, which
guarantees it'll be the correct one.
2023-08-22 12:19:23 +04:00
Dmitry Batrak
f85dae83df JBR-5953 If hieroglyph typing isn't finalised, focusing another component inserts the composed text there
(cherry picked from commit ff8fa489f8e1e16cb055cc56f1b441018c2f495c)
2023-08-21 13:39:53 +03:00
Vitaly Provodin
6b64fe9b77 update exclude list on results of 21_b202.1 test runs 2023-08-18 05:07:02 +07:00
Nikita Tsarev
85fb38c9a9 Regenerate wakefield-client-protocol using an older wayland-scanner to temporarily fix build problems 2023-08-17 15:56:48 +02:00
Dmitry Batrak
bcd4b38663 JBR-5946 Allow to disable painting of composed text in Swing text components using TextLayout.draw
(cherry picked from commit 81d531fb9f7496498fcab1f0e76554885941f16c)
2023-08-16 15:27:02 +03:00
Nikita Tsarev
77bccaf640 JBR-5676: Support emulating input events in Wakefield 2023-08-15 12:37:27 +02:00
37 changed files with 2084 additions and 2299 deletions

12
jb/generate-wakefield.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
if [[ -z "$1" ]]; then
SCANNER=wayland-scanner
else
SCANNER="$1"
fi
set -ex
"$SCANNER" client-header src/java.desktop/share/native/libwakefield/protocol/wakefield.xml src/java.desktop/unix/native/libawt_wlawt/wakefield-client-protocol.h
"$SCANNER" private-code src/java.desktop/share/native/libwakefield/protocol/wakefield.xml src/java.desktop/unix/native/libawt_wlawt/wakefield-client-protocol.c

View File

@@ -660,12 +660,10 @@ ifeq ($(call isTargetOs, windows), true)
else ifeq ($(call isTargetOs, macosx), true)
LIBFONTMANAGER_EXCLUDE_FILES += X11FontScaler.c \
X11TextRenderer.c \
fontpath.c \
lcdglyph.c \
lcdglyphDW.cpp
else
LIBFONTMANAGER_EXCLUDE_FILES += fontpath.c \
lcdglyph.c \
LIBFONTMANAGER_EXCLUDE_FILES += lcdglyph.c \
lcdglyphDW.cpp
endif

View File

@@ -62,6 +62,7 @@ public class AquaCaret extends DefaultCaret
public void deinstall(final JTextComponent c) {
c.removePropertyChangeListener(this);
super.deinstall(c);
mFocused = false;
}
@Override

View File

@@ -51,7 +51,6 @@ public class CInputMethod extends InputMethodAdapter {
private volatile Component fAwtFocussedComponent;
private LWComponentPeer<?, ?> fAwtFocussedComponentPeer;
private boolean isActive;
private boolean isTemporarilyDeactivated;
private static Map<TextAttribute, Integer>[] sHighlightStyles;
@@ -236,12 +235,10 @@ public class CInputMethod extends InputMethodAdapter {
*/
public void activate() {
isActive = true;
isTemporarilyDeactivated = false;
}
public void deactivate(boolean isTemporary) {
isActive = false;
isTemporarilyDeactivated = isTemporary;
}
/**
@@ -269,7 +266,7 @@ public class CInputMethod extends InputMethodAdapter {
public void removeNotify() {
if (fAwtFocussedComponentPeer != null) {
long modelPtr = getNativeViewPtr(fAwtFocussedComponentPeer);
nativeEndComposition(modelPtr);
nativeEndComposition(modelPtr, fAwtFocussedComponent);
nativeNotifyPeer(modelPtr, null);
}
@@ -284,10 +281,10 @@ public class CInputMethod extends InputMethodAdapter {
* to talk to when responding to key events.
*/
protected void setAWTFocussedComponent(Component component) {
if ((isTemporarilyDeactivated && component == null) || component == fAwtFocussedComponent) {
if (component == null || component == fAwtFocussedComponent) {
// Sometimes input happens for the natively unfocused window
// (e.g. in case of system emoji picker),
// so we don't reset last focused component on temporary focus lost.
// so we don't reset last focused component on focus lost.
return;
}
@@ -355,7 +352,7 @@ public class CInputMethod extends InputMethodAdapter {
*/
public void endComposition() {
if (fAwtFocussedComponentPeer != null)
nativeEndComposition(getNativeViewPtr(fAwtFocussedComponentPeer));
nativeEndComposition(getNativeViewPtr(fAwtFocussedComponentPeer), fAwtFocussedComponent);
}
/**
@@ -563,18 +560,21 @@ public class CInputMethod extends InputMethodAdapter {
/**
* Frequent callbacks from NSTextInput. I think we're supposed to commit it here?
*/
private synchronized void unmarkText() {
if (fCurrentText == null || fAwtFocussedComponent == null) return;
private synchronized void unmarkText(Component component) {
if (component == null) {
component = fAwtFocussedComponent;
}
if (fCurrentText == null || component == null) return;
TextHitInfo theCaret = TextHitInfo.afterOffset(fCurrentTextLength);
TextHitInfo visiblePosition = theCaret;
InputMethodEvent event = new InputMethodEvent(fAwtFocussedComponent,
InputMethodEvent event = new InputMethodEvent(component,
InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
fCurrentText.getIterator(),
fCurrentTextLength,
theCaret,
visiblePosition);
LWCToolkit.postEvent(LWCToolkit.targetToAppContext(fAwtFocussedComponent), event);
LWCToolkit.postEvent(LWCToolkit.targetToAppContext(component), event);
fCurrentText = null;
fCurrentTextAsString = null;
fCurrentTextLength = 0;
@@ -807,7 +807,7 @@ public class CInputMethod extends InputMethodAdapter {
// Note that if nativePeer isn't something that normally accepts keystrokes (i.e., a CPanel)
// these calls will be ignored.
private native void nativeNotifyPeer(long nativePeer, CInputMethod imInstance);
private native void nativeEndComposition(long nativePeer);
private native void nativeEndComposition(long nativePeer, Component component);
private native void nativeHandleEvent(LWComponentPeer<?, ?> peer, AWTEvent event);
// Returns the locale of the active input method.

View File

@@ -67,6 +67,6 @@
// Input method-related events
- (void)setInputMethod:(jobject)inputMethod;
- (void)abandonInput;
- (void)abandonInput:(jobject) component;
@end

View File

@@ -393,7 +393,7 @@ static void debugPrintNSEvent(NSEvent* event, const char* comment) {
case kVK_End:
// Abandon input to reset IM and unblock input after
// canceling input accented symbols
[self abandonInput];
[self abandonInput:nil];
break;
}
}
@@ -1157,7 +1157,7 @@ static jclass jc_CInputMethod = NULL;
// Abandon input to reset IM and unblock input after entering accented
// symbols
[self abandonInput];
[self abandonInput:nil];
}
+ (void)keyboardInputSourceChanged:(NSNotification *)notification
@@ -1251,7 +1251,11 @@ static jclass jc_CInputMethod = NULL;
}
}
- (void) unmarkText
- (void) unmarkText {
[self unmarkText:nil];
}
- (void) unmarkText:(jobject) component
{
#ifdef IM_DEBUG
fprintf(stderr, "AWTView InputMethod Selector Called : [unmarkText]\n");
@@ -1264,8 +1268,8 @@ static jclass jc_CInputMethod = NULL;
// unmarkText cancels any input in progress and commits it to the text field.
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CIM_CLASS();
DECLARE_METHOD(jm_unmarkText, jc_CInputMethod, "unmarkText", "()V");
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_unmarkText);
DECLARE_METHOD(jm_unmarkText, jc_CInputMethod, "unmarkText", "(Ljava/awt/Component;)V");
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_unmarkText, component);
CHECK_EXCEPTION();
}
@@ -1526,14 +1530,14 @@ static jclass jc_CInputMethod = NULL;
object:nil];
}
- (void)abandonInput
- (void)abandonInput:(jobject) component
{
#ifdef IM_DEBUG
fprintf(stderr, "AWTView InputMethod Selector Called : [abandonInput]\n");
#endif // IM_DEBUG
[ThreadUtilities performOnMainThread:@selector(markedTextAbandoned:) on:[NSInputManager currentInputManager] withObject:self waitUntilDone:YES];
[self unmarkText];
[self unmarkText:component];
}
/******************************** END NSTextInputClient Protocol ********************************/

View File

@@ -118,13 +118,6 @@ static void initializeInputMethodController() {
[view setInputMethod:inputMethod]; // inputMethod is a GlobalRef or null to disable.
}
+ (void) _nativeEndComposition:(AWTView *)view {
if (view == nil) return;
[view abandonInput];
}
@end
/*
@@ -186,16 +179,21 @@ JNI_COCOA_EXIT(env);
/*
* Class: sun_lwawt_macosx_CInputMethod
* Method: nativeEndComposition
* Signature: (J)V
* Signature: (JLjava/awt/Component;)V
*/
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CInputMethod_nativeEndComposition
(JNIEnv *env, jobject this, jlong nativePeer)
(JNIEnv *env, jobject this, jlong nativePeer, jobject component)
{
JNI_COCOA_ENTER(env);
AWTView *view = (AWTView *)jlong_to_ptr(nativePeer);
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
[CInputMethod _nativeEndComposition:view];
AWTView *view = (AWTView *)jlong_to_ptr(nativePeer);
if (!view) return;
jobject componentRef = (*env)->NewGlobalRef(env, component);
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
[view abandonInput:componentRef];
if (componentRef) {
JNIEnv *env = [ThreadUtilities getJNIEnv];
(*env)->DeleteGlobalRef(env, componentRef);
}
}];
JNI_COCOA_EXIT(env);

View File

@@ -62,7 +62,7 @@ public class JBRApiModule {
.withStatic("deriveFontWithFeatures", "deriveFont", "java.awt.Font")
.withStatic("getFeaturesAsString", "getFeaturesAsString", "com.jetbrains.desktop.FontExtensions")
.clientProxy("java.awt.Font$Features", "com.jetbrains.FontExtensions$Features")
.service("com.jetbrains.WindowMove", "sun.awt.X11.XWindowPeer$WindowMoveService")
.service("com.jetbrains.WindowMove", "java.awt.Window$WindowMoveService")
;
}
}

View File

@@ -59,6 +59,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.HashMap;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.Vector;
@@ -4257,6 +4258,67 @@ public class Window extends Container implements Accessible {
}
}
private interface WindowMovePeer {
void startMovingWindowTogetherWithMouse(Window window, int mouseButton);
}
private interface WindowMovePeerX11 extends WindowMovePeer {
WindowMovePeerX11 INSTANCE = (WindowMovePeerX11) JBRApi.internalServiceBuilder(MethodHandles.lookup())
.withStatic("startMovingWindowTogetherWithMouse",
"startMovingWindowTogetherWithMouse",
"sun.awt.X11.XWindowPeer")
.build();
}
private interface WindowMovePeerWayland extends WindowMovePeer {
WindowMovePeerWayland INSTANCE = (WindowMovePeerWayland) JBRApi.internalServiceBuilder(MethodHandles.lookup())
.withStatic("startMovingWindowTogetherWithMouse",
"startMovingWindowTogetherWithMouse",
"sun.awt.wl.WLComponentPeer")
.build();
}
private static class WindowMoveService {
WindowMovePeer windowMovePeer;
WindowMoveService() {
var toolkit = Toolkit.getDefaultToolkit();
var ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
if (toolkit == null || ge == null) {
throw new JBRApi.ServiceNotAvailableException("Supported only with a Toolkit present");
}
boolean isWayland = objectIsInstanceOf(toolkit, "sun.awt.wl.WLToolkit");
if (isWayland) {
if (!objectIsInstanceOf(ge,"sun.awt.wl.WLGraphicsEnvironment")) {
throw new JBRApi.ServiceNotAvailableException("On Wayland, supported only with WLGraphicsEnvironment");
}
} else {
if (!objectIsInstanceOf(toolkit, "sun.awt.X11.XToolkit")
|| !objectIsInstanceOf(ge, "sun.awt.X11GraphicsEnvironment")) {
throw new JBRApi.ServiceNotAvailableException("Supported only with XToolkit and X11GraphicsEnvironment");
}
}
if (isWayland) {
windowMovePeer = WindowMovePeerWayland.INSTANCE;
} else {
// This will throw if the service is not supported by the underlying WM
windowMovePeer = WindowMovePeerX11.INSTANCE;
}
}
boolean objectIsInstanceOf(Object o, String className) {
Objects.requireNonNull(o);
return o.getClass().getName().equals(className);
}
void startMovingTogetherWithMouse(Window window, int mouseButton) {
Objects.requireNonNull(window);
windowMovePeer.startMovingWindowTogetherWithMouse(window, mouseButton);
}
}
// ************************** JBR stuff *******************************
private volatile boolean ignoreMouseEvents;

View File

@@ -478,6 +478,16 @@ public class JMenu extends JMenuItem implements Accessible,MenuElement
y = 0 - yOffset - pmSize.height; // Otherwise drop 'up'
}
}
// Note that the y position may be later adjusted to fit the menu into the screen if possible.
// However, the code that does it (JPopupMenu.adjustPopupLocationToFitScreen) has no idea which screen
// to fit into, and determines it by the position calculated here, so we need to make sure it's on
// the correct screen, otherwise the menu may appear on the wrong screen (JDK-6415065).
if (position.y + y < screenBounds.y) { // Above the current screen?
y = screenBounds.y - position.y; // Fit into the screen, relative to our origin.
}
if (position.y + y >= screenBounds.y + screenBounds.height) { // Below the current screen?
y = screenBounds.y + screenBounds.height - 1 - position.y; // Fit into the screen, relative to our origin.
}
return new Point(x,y);
}

View File

@@ -1010,7 +1010,11 @@ public class SwingUtilities2 {
} else {
layout = new TextLayout(iterator, frc);
}
layout.draw(g2d, x, y);
if (Boolean.TRUE.equals(Toolkit.getDefaultToolkit().getDesktopProperty("jb.swing.avoid.text.layout"))) {
g2d.drawString(iterator, x, y);
} else {
layout.draw(g2d, x, y);
}
retVal = layout.getAdvance();
}

File diff suppressed because it is too large Load Diff

View File

@@ -67,12 +67,8 @@
#endif
#ifndef DISABLE_FONTCONFIG
/* Use bundled fontconfig.h for now */
#include "fontconfig.h"
#endif
#ifndef FC_LCD_FILTER
#define FC_LCD_FILTER "lcdfilter"
#include "fontconfigmanager.h"
#include <fontconfig/fontconfig.h>
#endif
#ifndef FC_LCD_NONE
@@ -100,16 +96,8 @@
#define FT26Dot6ToIntRound(x) (((int)(x + (1 << 5))) >> 6)
#define FT26Dot6ToIntCeil(x) (((int)(x - 1 + (1 << 6))) >> 6)
#define IntToFT26Dot6(x) (((FT_Fixed)(x)) << 6)
#define DEFAULT_DPI 72
#define MAX_DPI 1024
#define ADJUST_FONT_SIZE(X, DPI) (((X)*DEFAULT_DPI + ((DPI)>>1))/(DPI))
#define FLOOR_DIV(X, Y) ((X) >= 0 ? (X) / (Y) : ((X) - (Y) + 1) / (Y))
#ifndef DISABLE_FONTCONFIG
#define FONTCONFIG_DLL JNI_LIB_NAME("fontconfig")
#define FONTCONFIG_DLL_VERSIONED VERSIONED_JNI_LIB_NAME("fontconfig", "1")
#endif
#ifndef FALSE
#define FALSE 0
#endif
@@ -215,78 +203,14 @@ static jmethodID invalidateScalerMID;
static jboolean debugFonts; // Stores the value of FontUtilities.debugFonts()
static jmethodID getDefaultToolkitMID;
static jclass tkClass;
static jmethodID getScreenResolutionMID;
static jfieldID platNameFID;
static jfieldID familyNameFID;
#ifndef DISABLE_FONTCONFIG
typedef FcBool (*FcPatternAddPtrType) (FcPattern *p, const char *object, FcValue value, FcBool append);
typedef FcBool (*FcPatternAddBoolPtrType) (FcPattern *p, const char *object, FcBool b);
typedef FcBool (*FcPatternAddDoublePtrType) (FcPattern *p, const char *object, double d);
typedef FcBool (*FcConfigSubstitutePtrType) (FcConfig *config, FcPattern *p, FcMatchKind kind);
typedef void (*FcDefaultSubstitutePtrType) (FcPattern *pattern);
typedef FcPattern* (*FcPatternCreatePtrType) ();
typedef FcPattern* (*FcFontMatchPtrType) (FcConfig *config, FcPattern *p, FcResult *result);
typedef void (*FcPatternDestroyPtrType) (FcPattern *p);
typedef FcResult (*FcPatternGetBoolPtrType) (const FcPattern *p, const char *object, int n, FcBool *b);
typedef FcResult (*FcPatternGetIntegerPtrType) (const FcPattern *p, const char *object, int n, int *i);
typedef FT_Error (*FtLibrarySetLcdFilterPtrType) (FT_Library library, FT_LcdFilter filter);
typedef FcBool (*FcConfigParseAndLoadPtrType) (FcConfig *config, const FcChar8 *file, FcBool complain);
typedef FcBool (*FcConfigSetCurrentPtrType) (FcConfig *config);
typedef FcConfig * (*FcInitLoadConfigAndFontsPtrType)();
typedef int (*FcGetVersionPtrType)();
#endif
static void *libFontConfig = NULL;
static jboolean logFC = JNI_FALSE;
static jboolean logFFS = JNI_FALSE;
#ifndef DISABLE_FONTCONFIG
static FcPatternAddPtrType FcPatternAddPtr;
static FcPatternAddBoolPtrType FcPatternAddBoolPtr;
static FcPatternAddDoublePtrType FcPatternAddDoublePtr;
static FcConfigSubstitutePtrType FcConfigSubstitutePtr;
static FcDefaultSubstitutePtrType FcDefaultSubstitutePtr;
static FcPatternCreatePtrType FcPatternCreatePtr;
static FcFontMatchPtrType FcFontMatchPtr;
static FcPatternDestroyPtrType FcPatternDestroyPtr;
static FcPatternGetBoolPtrType FcPatternGetBoolPtr;
static FcPatternGetIntegerPtrType FcPatternGetIntegerPtr;
static FcConfigParseAndLoadPtrType FcConfigParseAndLoadPtr;
static FcConfigSetCurrentPtrType FcConfigSetCurrentPtr;
static FcInitLoadConfigAndFontsPtrType FcInitLoadConfigAndFontsPtr;
static FcGetVersionPtrType FcGetVersionPtr;
#endif
static FT_UnitVector subpixelGlyphResolution;
static void* openFontConfig() {
void* libfontconfig = NULL;
#ifndef DISABLE_FONTCONFIG
char *fcLogEnabled = getenv("OPENJDK_FFS_LOG_FC");
if (fcLogEnabled != NULL && !strcmp(fcLogEnabled, "yes")) {
logFC = JNI_TRUE;
}
char *useFC = getenv("OPENJDK_FFS_USE_FC");
if (useFC != NULL && !strcmp(useFC, "no")) {
if (logFC) fprintf(stderr, "FC_LOG: fontconfig disabled in freetypescaler\n");
return NULL;
}
libfontconfig = dlopen(FONTCONFIG_DLL_VERSIONED, RTLD_LOCAL | RTLD_LAZY);
if (libfontconfig == NULL) {
libfontconfig = dlopen(FONTCONFIG_DLL, RTLD_LOCAL | RTLD_LAZY);
if (libfontconfig == NULL) {
if (logFC) fprintf(stderr, "FC_LOG: cannot open %s\n", FONTCONFIG_DLL);
return NULL;
}
}
#endif
return libfontconfig;
}
JNIEXPORT void JNICALL
Java_sun_font_FreetypeFontScaler_initIDs(
JNIEnv *env, jobject scaler, jclass FFSClass, jclass TKClass,
@@ -315,57 +239,14 @@ Java_sun_font_FreetypeFontScaler_initIDs(
getDefaultToolkitMID =
(*env)->GetStaticMethodID(env, TKClass, "getDefaultToolkit",
"()Ljava/awt/Toolkit;");
getScreenResolutionMID =
(*env)->GetMethodID(env, TKClass, "getScreenResolution", "()I");
tkClass = (*env)->NewGlobalRef(env, TKClass);
platNameFID = (*env)->GetFieldID(env, PFClass, "platName", "Ljava/lang/String;");
familyNameFID = (*env)->GetFieldID(env, PFClass, "familyName", "Ljava/lang/String;");
libFontConfig = openFontConfig();
#ifndef DISABLE_FONTCONFIG
if (libFontConfig) {
FcConfig* fcConfig;
FcBool result;
FcPatternAddPtr = (FcPatternAddPtrType) dlsym(libFontConfig, "FcPatternAdd");
FcPatternAddBoolPtr = (FcPatternAddBoolPtrType) dlsym(libFontConfig, "FcPatternAddBool");
FcPatternAddDoublePtr = (FcPatternAddDoublePtrType) dlsym(libFontConfig, "FcPatternAddDouble");
FcConfigSubstitutePtr = (FcConfigSubstitutePtrType) dlsym(libFontConfig, "FcConfigSubstitute");
FcDefaultSubstitutePtr = (FcDefaultSubstitutePtrType) dlsym(libFontConfig, "FcDefaultSubstitute");
FcPatternCreatePtr = (FcPatternCreatePtrType) dlsym(libFontConfig, "FcPatternCreate");
FcFontMatchPtr = (FcFontMatchPtrType) dlsym(libFontConfig, "FcFontMatch");
FcPatternDestroyPtr = (FcPatternDestroyPtrType) dlsym(libFontConfig, "FcPatternDestroy");
FcPatternGetBoolPtr = (FcPatternGetBoolPtrType) dlsym(libFontConfig, "FcPatternGetBool");
FcPatternGetIntegerPtr = (FcPatternGetIntegerPtrType) dlsym(libFontConfig, "FcPatternGetInteger");
FcConfigParseAndLoadPtr = (FcConfigParseAndLoadPtrType) dlsym(libFontConfig, "FcConfigParseAndLoad");
FcConfigSetCurrentPtr = (FcConfigSetCurrentPtrType) dlsym(libFontConfig, "FcConfigSetCurrent");
FcInitLoadConfigAndFontsPtr = (FcInitLoadConfigAndFontsPtrType) dlsym(libFontConfig, "FcInitLoadConfigAndFonts");
FcGetVersionPtr = (FcGetVersionPtrType) dlsym(libFontConfig, "FcGetVersion");
if (logFC) fprintf(stderr, "FC_LOG: fontconfig version %d \n", (*FcGetVersionPtr)());
fcConfig = (*FcInitLoadConfigAndFontsPtr)();
if (fcConfig != NULL && fontConf != NULL) {
result = (*FcConfigParseAndLoadPtr)(fcConfig, (const FcChar8 *) fontConf, FcFalse);
if (logFC) fprintf(stderr, "FC_LOG: FcConfigParseAndLoad %d \n", result);
result = (*FcConfigSetCurrentPtr)(fcConfig);
if (logFC) fprintf(stderr, "FC_LOG: FcConfigSetCurrent %d \n", result);
}
else {
if (logFC) {
if (fontConf) {
fprintf(stderr, "FC_LOG: FcInitLoadConfigAndFonts failed\n");
}
else {
fprintf(stderr, "FC_LOG: FcInitLoadConfigAndFonts disabled\n");
}
}
}
}
#endif
if (fontConf) {
(*env)->ReleaseStringUTFChars(env, jreFontConfName, fontConf);
}
}
typedef FT_Error (*FtLibrarySetLcdFilterPtrType) (FT_Library library, FT_LcdFilter filter);
static FT_Error FT_Library_SetLcdFilter_Proxy(FT_Library library, FT_LcdFilter filter) {
#ifndef DISABLE_FONTCONFIG
static FtLibrarySetLcdFilterPtrType FtLibrarySetLcdFilterPtr = NULL;
@@ -388,46 +269,6 @@ static FT_Error FT_Library_SetLcdFilter_Proxy(FT_Library library, FT_LcdFilter
#endif
}
static int getScreenResolution(JNIEnv *env) {
/*
* Actual screen dpi is necessary only for fontconfig requests
*/
#ifndef DISABLE_FONTCONFIG
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)
* Fallback to default DPI otherwise
*/
exc = (*env)->ExceptionOccurred(env);
if (exc) {
(*env)->ExceptionClear(env);
return DEFAULT_DPI;
}
/* Some configurations report invalid dpi settings */
if (dpi > MAX_DPI) {
if (logFFS) {
fprintf(stderr, "FFS_LOG: Invalid dpi reported (%d) replaced with default (%d)\n", dpi, DEFAULT_DPI);
}
return DEFAULT_DPI;
}
if (logFFS) {
fprintf(stderr, "FFS_LOG: Screen Resolution (%d) dpi\n", dpi);
}
return dpi;
#else
return DEFAULT_DPI;
#endif
}
static void freeNativeResources(JNIEnv *env, FTScalerInfo* scalerInfo) {
if (scalerInfo == NULL)
@@ -928,25 +769,33 @@ static void setDefaultScalerSettings(FTScalerContext *context) {
context->renderFlags = FT_LOAD_TARGET_MODE(context->loadFlags);
}
#ifndef DISABLE_FONTCONFIG
static FcBool getRenderingSettingsField(int *target, int value) {
*target = value;
return (value != -1) ? FcTrue : FcFalse;
}
#endif
static int setupFTContext(JNIEnv *env, jobject font2D, FTScalerInfo *scalerInfo, FTScalerContext *context,
FT_Bool configureFont) {
FT_Matrix matrix;
int errCode = 0;
scalerInfo->env = env;
scalerInfo->font2D = font2D;
const char *cfontFamilyName = NULL;
const char *cfontPath = NULL;
jstring jfontFamilyName = NULL;
jstring jfontPath = NULL;
if (context != NULL) {
context->colorFont = FT_HAS_COLOR(scalerInfo->face) || !FT_IS_SCALABLE(scalerInfo->face);
setupTransform(&matrix, context);
FT_Set_Transform(scalerInfo->face, &matrix, NULL);
FT_UInt dpi = (FT_UInt) getScreenResolution(env);
if (FT_IS_SCALABLE(scalerInfo->face)) { // Standard scalable face
context->fixedSizeIndex = -1;
errCode = FT_Set_Char_Size(scalerInfo->face, 0,
ADJUST_FONT_SIZE(context->ptsz, dpi),
dpi, dpi);
errCode = FT_Set_Char_Size(scalerInfo->face, 0, context->ptsz, 72, 72);
} else { // Non-scalable face (that should only be bitmap faces)
const int ptsz = context->ptsz;
// Best size is smallest, but not smaller than requested
@@ -963,35 +812,33 @@ static int setupFTContext(JNIEnv *env, jobject font2D, FTScalerInfo *scalerInfo,
}
}
context->fixedSizeIndex = bestSizeIndex;
errCode = FT_Set_Char_Size(scalerInfo->face, 0,
ADJUST_FONT_SIZE(bestSize, dpi),
dpi, dpi);
errCode = FT_Set_Char_Size(scalerInfo->face, 0, bestSize, 72, 72);
}
if (errCode) return errCode;
errCode = FT_Activate_Size(scalerInfo->face->size);
if (errCode) return errCode;
if (configureFont) {
context->renderFlags = FT_RENDER_MODE_NORMAL;
context->lcdFilter = FT_LCD_FILTER_NONE;
context->loadFlags = FT_LOAD_DEFAULT;
if (libFontConfig == NULL) {
setDefaultScalerSettings(context);
return 0;
}
#ifndef DISABLE_FONTCONFIG
jstring jfontFamilyName = (*env)->GetObjectField(env, font2D, familyNameFID);
const char *cfontFamilyName = (*env)->GetStringUTFChars(env, jfontFamilyName, NULL);
#ifdef DISABLE_FONTCONFIG
setDefaultScalerSettings(context);
return 0;
#else
jfontFamilyName = (*env)->GetObjectField(env, font2D, familyNameFID);
cfontFamilyName = (*env)->GetStringUTFChars(env, jfontFamilyName, NULL);
jfontPath = (*env)->GetObjectField(env, font2D, platNameFID);
cfontPath = (*env)->GetStringUTFChars(env, jfontPath, NULL);
if (logFC) {
jstring jfontPath = (*env)->GetObjectField(env, font2D, platNameFID);
char *cfontPath = (char*)(*env)->GetStringUTFChars(env, jfontPath, NULL);
fprintf(stderr, "FC_LOG: %s %s ", cfontFamilyName, cfontPath);
(*env)->ReleaseStringUTFChars(env, jfontPath, cfontPath);
}
double fcSize = FT26Dot6ToDouble(ADJUST_FONT_SIZE(context->ptsz, dpi));
double fcSize = FT26Dot6ToDouble(context->ptsz);
if (logFC) fprintf(stderr, " size=%f", fcSize);
// Find cached value
@@ -1005,55 +852,33 @@ static int setupFTContext(JNIEnv *env, jobject font2D, FTScalerInfo *scalerInfo,
}
if (cachedMatch.fcSize == 0) {
cachedMatch.fcSize = fcSize;
clock_t begin = logFC ? clock() : 0;
// Setup query
FcPattern *fcPattern = 0;
fcPattern = (*FcPatternCreatePtr)();
FcValue fcValue;
fcValue.type = FcTypeString;
fcValue.u.s = (const FcChar8 *) cfontFamilyName;
(*FcPatternAddPtr)(fcPattern, FC_FAMILY, fcValue, FcTrue);
(*FcPatternAddBoolPtr)(fcPattern, FC_SCALABLE, FcTrue);
(*FcPatternAddDoublePtr)(fcPattern, FC_SIZE, fcSize);
(*FcConfigSubstitutePtr)(0, fcPattern, FcMatchPattern);
(*FcDefaultSubstitutePtr)(fcPattern);
FcResult matchResult = FcResultNoMatch;
// Match on pattern
FcPattern *resultPattern = 0;
resultPattern = (*FcFontMatchPtr)(0, fcPattern, &matchResult);
if (logFC) {
clock_t end = clock();
double time_spent = (double)(end - begin) * 1000.0 / CLOCKS_PER_SEC;
fprintf(stderr, " in %f ms", time_spent);
}
if (matchResult != FcResultMatch) {
(*FcPatternDestroyPtr)(fcPattern);
if (logFC) fprintf(stderr, " - NOT FOUND\n");
RenderingFontHints renderingFontHints;
int status = setupRenderingFontHints(cfontFamilyName, NULL, fcSize, &renderingFontHints);
if (status != 0) {
if (cfontPath) {
(*env)->ReleaseStringUTFChars(env, jfontPath, cfontPath);
}
if (cfontFamilyName) {
(*env)->ReleaseStringUTFChars(env, jfontFamilyName, cfontFamilyName);
}
setDefaultScalerSettings(context);
return 0;
}
(*FcPatternDestroyPtr)(fcPattern);
FcPattern *pattern = resultPattern;
// Extract values from result
cachedMatch.fcHintingSet = (*FcPatternGetBoolPtr)(pattern, FC_HINTING, 0, &cachedMatch.fcHinting) == FcResultMatch;
cachedMatch.fcSize = fcSize;
cachedMatch.fcHintingSet =
getRenderingSettingsField(&cachedMatch.fcHinting, renderingFontHints.fcHinting);
cachedMatch.fcHintStyleSet =
(*FcPatternGetIntegerPtr)(pattern, FC_HINT_STYLE, 0, &cachedMatch.fcHintStyle) == FcResultMatch;
cachedMatch.fcAntialiasSet = (*FcPatternGetBoolPtr)(pattern, FC_ANTIALIAS, 0, &cachedMatch.fcAntialias) == FcResultMatch;
cachedMatch.fcAutohintSet = (*FcPatternGetBoolPtr)(pattern, FC_AUTOHINT, 0, &cachedMatch.fcAutohint) == FcResultMatch;
getRenderingSettingsField(&cachedMatch.fcHintStyle, renderingFontHints.fcHintStyle);
cachedMatch.fcAntialiasSet =
getRenderingSettingsField(&cachedMatch.fcAntialias, renderingFontHints.fcAntialias);
cachedMatch.fcAutohintSet =
getRenderingSettingsField(&cachedMatch.fcAutohint, renderingFontHints.fcAutohint);
cachedMatch.fcLCDFilterSet =
(*FcPatternGetIntegerPtr)(pattern, FC_LCD_FILTER, 0, &cachedMatch.fcLCDFilter) == FcResultMatch;
cachedMatch.fcRGBASet = (*FcPatternGetIntegerPtr)(pattern, FC_RGBA, 0, &cachedMatch.fcRGBA) == FcResultMatch;
(*FcPatternDestroyPtr)(pattern);
getRenderingSettingsField(&cachedMatch.fcRGBA, renderingFontHints.fcRGBA);
cachedMatch.fcRGBASet =
getRenderingSettingsField(&cachedMatch.fcLCDFilter, renderingFontHints.fcLCDFilter);
if (NUM_CACHED_VALUES > 0) {
int nextCacheIdx = scalerInfo->nextCacheIdx;
@@ -1205,8 +1030,14 @@ static int setupFTContext(JNIEnv *env, jobject font2D, FTScalerInfo *scalerInfo,
}
}
(*env)->ReleaseStringUTFChars(env, jfontFamilyName, cfontFamilyName);
if (logFC) fprintf(stderr, "\n");
if (cfontPath) {
(*env)->ReleaseStringUTFChars(env, jfontPath, cfontPath);
}
if (cfontFamilyName) {
(*env)->ReleaseStringUTFChars(env, jfontFamilyName, cfontFamilyName);
}
#endif
}
@@ -2723,13 +2554,4 @@ Java_sun_font_FreetypeFontScaler_getGlyphPointNative(
return (*env)->NewObject(env, sunFontIDs.pt2DFloatClass,
sunFontIDs.pt2DFloatCtr, x, y);
}
JNIEXPORT void JNICALL
JNI_OnUnload(JavaVM *vm, void *reserved) {
if (libFontConfig != NULL) {
#ifndef DISABLE_FONTCONFIG
dlclose(libFontConfig);
#endif
}
}
}

View File

@@ -84,6 +84,37 @@
<arg name="error_code" type="uint" enum="error"/>
</event>
<request name="send_key">
<description summary="facilitates implementation of Robot.keyPress/Robot.keyRelease">
This requests an emulation of a key press by its Linux event key code.
</description>
<arg name="key" type="uint" />
<arg name="state" type="uint" />
</request>
<request name="send_cursor">
<description summary="facilitates implementation of Robot.mouseMove">
This requests an emulation of the mouse cursor being moved to the specified screen coordinates.
</description>
<arg name="x" type="int" />
<arg name="y" type="int" />
</request>
<request name="send_button">
<description summary="facilitates implementation of Robot.mousePress/Robot.mouseRelease">
This requests an emulation of a mouse button press by its Linux event code.
</description>
<arg name="button" type="uint" />
<arg name="state" type="uint" />
</request>
<request name="send_wheel">
<description summary="facilitates implementation of Robot.mouseWheel">
This requests an emulation of a rotation of a mouse scroll wheel.
</description>
<arg name="amount" type="int" />
</request>
<enum name="error">
<entry name="no_error" value="0" summary="error code 0 reserved for the absence of error"/>
<entry name="invalid_coordinates" value="1" summary="supplied absolute coordinates point

View File

@@ -44,6 +44,37 @@ struct wakefield {
struct weston_log_scope *log;
};
#define DEFAULT_AXIS_STEP_DISTANCE 10
// These functions are part of Weston's private backend API (libweston/backend.h)
void
notify_axis(struct weston_seat *seat, const struct timespec *time,
struct weston_pointer_axis_event *event);
void
notify_axis_source(struct weston_seat *seat, uint32_t source);
void
notify_button(struct weston_seat *seat, const struct timespec *time,
int32_t button, enum wl_pointer_button_state state);
void
notify_key(struct weston_seat *seat, const struct timespec *time, uint32_t key,
enum wl_keyboard_key_state state,
enum weston_key_state_update update_state);
void
notify_motion(struct weston_seat *seat, const struct timespec *time,
struct weston_pointer_motion_event *event);
void
notify_motion_absolute(struct weston_seat *seat, const struct timespec *time,
double x, double y);
void
notify_pointer_frame(struct weston_seat *seat);
static struct weston_output*
get_output_for_point(struct wakefield* wakefield, int32_t x, int32_t y)
{
@@ -482,11 +513,96 @@ wakefield_capture_create(struct wl_client *client,
wakefield_send_capture_ready(resource, buffer_resource, WAKEFIELD_ERROR_NO_ERROR);
}
static void
wakefield_send_key(struct wl_client *client,
struct wl_resource *resource,
uint32_t key,
uint32_t state)
{
struct wakefield *wakefield = wl_resource_get_user_data(resource);
struct weston_compositor *compositor = wakefield->compositor;
struct timespec time;
weston_compositor_get_time(&time);
struct weston_seat *seat;
wl_list_for_each(seat, &compositor->seat_list, link) {
notify_key(seat, &time, key,
state ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED,
STATE_UPDATE_AUTOMATIC);
}
}
static void wakefield_send_cursor(struct wl_client* client,
struct wl_resource* resource,
int32_t x, int32_t y)
{
struct wakefield *wakefield = wl_resource_get_user_data(resource);
struct weston_compositor *compositor = wakefield->compositor;
struct timespec time;
weston_compositor_get_time(&time);
struct weston_seat *seat;
wl_list_for_each(seat, &compositor->seat_list, link) {
notify_motion_absolute(seat, &time, (double)x, (double)y);
notify_pointer_frame(seat);
}
}
static void wakefield_send_button(struct wl_client* client,
struct wl_resource* resource,
uint32_t button,
uint32_t state)
{
struct wakefield *wakefield = wl_resource_get_user_data(resource);
struct weston_compositor *compositor = wakefield->compositor;
struct timespec time;
weston_compositor_get_time(&time);
struct weston_seat *seat;
wl_list_for_each(seat, &compositor->seat_list, link) {
notify_button(seat, &time, (int32_t)button,
state ? WL_POINTER_BUTTON_STATE_PRESSED : WL_POINTER_BUTTON_STATE_RELEASED);
notify_pointer_frame(seat);
}
}
static void wakefield_send_wheel(struct wl_client* client,
struct wl_resource* resource,
int32_t amount)
{
struct wakefield *wakefield = wl_resource_get_user_data(resource);
struct weston_compositor *compositor = wakefield->compositor;
struct timespec time;
weston_compositor_get_time(&time);
struct weston_pointer_axis_event event = {
.axis = WL_POINTER_AXIS_VERTICAL_SCROLL,
.value = DEFAULT_AXIS_STEP_DISTANCE * amount,
.has_discrete = true,
.discrete = amount
};
struct weston_seat *seat;
wl_list_for_each(seat, &compositor->seat_list, link) {
notify_axis(seat, &time, &event);
notify_pointer_frame(seat);
}
}
static const struct wakefield_interface wakefield_implementation = {
.get_surface_location = wakefield_get_surface_location,
.move_surface = wakefield_move_surface,
.get_pixel_color = wakefield_get_pixel_color,
.capture_create = wakefield_capture_create
.capture_create = wakefield_capture_create,
.send_key = wakefield_send_key,
.send_cursor = wakefield_send_cursor,
.send_button = wakefield_send_button,
.send_wheel = wakefield_send_wheel,
};
static void

View File

@@ -1423,7 +1423,14 @@ abstract class XDecoratedPeer extends XWindowPeer {
if (syncSizeOnly && dimensions != null) {
dimensions.setSize(r.width, r.height);
dimensions.setInsets(ins);
xSetSize(r.width, r.height);
boolean isMaximized = target instanceof Frame f && (f.getExtendedState() & Frame.MAXIMIZED_BOTH) != 0;
// When a window is maximized, affirming its size through an explicit request to the X server
// may make the window fullscreen, which has undesirable consequences. Also, when a window
// already has the maximized attributes, it is already properly sized, so no need to
// resize explicitly.
if (!isMaximized) {
xSetSize(r.width, r.height);
}
} else {
dimensions = new WindowDimensions(r, ins, false);
xSetBounds(r.x, r.y, r.width, r.height);

View File

@@ -2655,6 +2655,16 @@ class XWindowPeer extends XPanelPeer implements WindowPeer,
XWM.getWM().startMovingWindowTogetherWithMouse(getParentTopLevel().getWindow(), getLastButtonPressAbsLocation(), mouseButton);
}
private static void startMovingWindowTogetherWithMouse(Window window, int mouseButton) {
final AWTAccessor.ComponentAccessor acc = AWTAccessor.getComponentAccessor();
ComponentPeer peer = acc.getPeer(window);
if (peer instanceof XWindowPeer xWindowPeer) {
xWindowPeer.startMovingTogetherWithMouse(mouseButton);
} else {
throw new IllegalArgumentException("AWT window must have XWindowPeer as its peer");
}
}
private static class WindowMoveService {
WindowMoveService() {
final var toolkit = Toolkit.getDefaultToolkit();

View File

@@ -1132,4 +1132,15 @@ public class WLComponentPeer implements ComponentPeer {
WLToolkit.awtLock();
}
}
private static void startMovingWindowTogetherWithMouse(Window window, int mouseButton)
{
final AWTAccessor.ComponentAccessor acc = AWTAccessor.getComponentAccessor();
ComponentPeer peer = acc.getPeer(window);
if (peer instanceof WLComponentPeer wlComponentPeer) {
wlComponentPeer.startDrag();
} else {
throw new IllegalArgumentException("AWT window must have WLComponentPeer as its peer");
}
}
}

View File

@@ -38,6 +38,7 @@ package sun.awt.wl;
* @param pointerButtonPressedEvent null or the latest PointerButtonEvent such that getIsButtonPressed() == true
* @param modifiers a bit set of modifiers reflecting currently pressed keys (@see WLInputState.getNewModifiers())
* @param surfaceForKeyboardInput represents 'struct wl_surface*' that keyboards events should go to
* @param lockingKeyState a bit set of locking modifiers currently active
*/
record WLInputState(WLPointerEvent eventWithSurface,
WLPointerEvent eventWithSerial,
@@ -46,7 +47,8 @@ record WLInputState(WLPointerEvent eventWithSurface,
PointerButtonEvent pointerButtonPressedEvent,
int modifiers,
long surfaceForKeyboardInput,
boolean isPointerOverSurface) {
boolean isPointerOverSurface,
int lockingKeyState) {
/**
* Groups together information about a mouse pointer button event.
* @param surface 'struct wl_surface*' the button was pressed over
@@ -60,7 +62,7 @@ record WLInputState(WLPointerEvent eventWithSurface,
static WLInputState initialState() {
return new WLInputState(null, null, null, null,
null, 0, 0, false);
null, 0, 0, false, 0);
}
/**
@@ -92,7 +94,8 @@ record WLInputState(WLPointerEvent eventWithSurface,
newPointerButtonEvent,
newModifiers,
surfaceForKeyboardInput,
newPointerOverSurface);
newPointerOverSurface,
lockingKeyState);
}
public WLInputState updatedFromKeyboardEnterEvent(long serial, long surfacePtr) {
@@ -105,10 +108,11 @@ record WLInputState(WLPointerEvent eventWithSurface,
pointerButtonPressedEvent,
modifiers,
surfacePtr,
isPointerOverSurface);
isPointerOverSurface,
lockingKeyState);
}
public WLInputState updatedFromKeyboardModifiersEvent(long serial, int keyboardModifiers) {
public WLInputState updatedFromKeyboardModifiersEvent(long serial, int keyboardModifiers, int newLockingKeyState) {
// "The compositor must send the wl_keyboard.modifiers event after this event".
final int oldPointerModifiers = modifiers & WLPointerEvent.PointerButtonCodes.combinedMask();
final int newModifiers = oldPointerModifiers | keyboardModifiers;
@@ -120,7 +124,8 @@ record WLInputState(WLPointerEvent eventWithSurface,
pointerButtonPressedEvent,
newModifiers,
surfaceForKeyboardInput,
isPointerOverSurface);
isPointerOverSurface,
newLockingKeyState);
}
public WLInputState updatedFromKeyboardLeaveEvent(long serial, long surfacePtr) {
@@ -135,7 +140,8 @@ record WLInputState(WLPointerEvent eventWithSurface,
pointerButtonPressedEvent,
newModifiers,
0,
isPointerOverSurface);
isPointerOverSurface,
lockingKeyState);
}
public WLInputState resetPointerState() {
@@ -147,7 +153,8 @@ record WLInputState(WLPointerEvent eventWithSurface,
pointerButtonPressedEvent,
0,
surfaceForKeyboardInput,
false);
false,
lockingKeyState);
}
private PointerButtonEvent getNewPointerButtonEvent(WLPointerEvent pointerEvent,

View File

@@ -42,32 +42,56 @@ public class WLRobotPeer implements RobotPeer {
@Override
public void mouseMove(int x, int y) {
throw new UnsupportedOperationException("Not implemented: WLRobotPeer.mouseMove()");
checkExtensionPresent();
synchronized (WLRobotPeer.class) {
mouseMoveImpl(x, y);
}
}
@Override
public void mousePress(int buttons) {
throw new UnsupportedOperationException("Not implemented: WLRobotPeer.mousePress()");
checkExtensionPresent();
synchronized (WLRobotPeer.class) {
sendMouseButtonImpl(buttons, true);
}
}
@Override
public void mouseRelease(int buttons) {
throw new UnsupportedOperationException("Not implemented: WLRobotPeer.mouseRelease()");
checkExtensionPresent();
synchronized (WLRobotPeer.class) {
sendMouseButtonImpl(buttons, false);
}
}
@Override
public void mouseWheel(int wheelAmt) {
throw new UnsupportedOperationException("Not implemented: WLRobotPeer.mouseWheel()");
checkExtensionPresent();
synchronized (WLRobotPeer.class) {
mouseWheelImpl(wheelAmt);
}
}
@Override
public void keyPress(int keycode) {
throw new UnsupportedOperationException("Not implemented: WLRobotPeer.keyPress()");
checkExtensionPresent();
synchronized (WLRobotPeer.class) {
sendJavaKeyImpl(keycode, true);
}
}
@Override
public void keyRelease(int keycode) {
throw new UnsupportedOperationException("Not implemented: WLRobotPeer.keyRelease()");
checkExtensionPresent();
synchronized (WLRobotPeer.class) {
sendJavaKeyImpl(keycode, false);
}
}
@Override
@@ -139,4 +163,8 @@ public class WLRobotPeer implements RobotPeer {
private static native int[] getRGBPixelsImpl(int x, int y, int width, int height);
private static native Point getLocationOfWLSurfaceImpl(long wlSurfacePtr);
private static native void setLocationOfWLSurfaceImpl(long wlSurfacePtr, int x, int y);
private static native void sendJavaKeyImpl(int javaKeyCode, boolean pressed);
private static native void mouseMoveImpl(int x, int y);
private static native void sendMouseButtonImpl(int buttons, boolean pressed);
private static native void mouseWheelImpl(int amount);
}

View File

@@ -133,6 +133,9 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
private static final int MOUSE_BUTTONS_COUNT = 3;
private static final int AWT_MULTICLICK_DEFAULT_TIME_MS = 500;
private static final int CAPS_LOCK_MASK = 0x01;
private static final int NUM_LOCK_MASK = 0x02;
private static boolean initialized = false;
private static native void initIDs();
@@ -451,7 +454,9 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
boolean isShiftActive,
boolean isAltActive,
boolean isCtrlActive,
boolean isMetaActive) {
boolean isMetaActive,
boolean isCapsActive,
boolean isNumActive) {
// Invoked from the native code
assert EventQueue.isDispatchThread();
@@ -460,12 +465,17 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
| (isAltActive ? InputEvent.ALT_DOWN_MASK : 0)
| (isCtrlActive ? InputEvent.CTRL_DOWN_MASK : 0)
| (isMetaActive ? InputEvent.META_DOWN_MASK : 0);
final int newLockingKeyState =
(isCapsActive ? CAPS_LOCK_MASK : 0)
| (isNumActive ? NUM_LOCK_MASK : 0);
if (logKeys.isLoggable(PlatformLogger.Level.FINE)) {
logKeys.fine("dispatchKeyboardModifiersEvent: new modifiers 0x"
+ Integer.toHexString(newModifiers));
}
inputState = inputState.updatedFromKeyboardModifiersEvent(serial, newModifiers);
inputState = inputState.updatedFromKeyboardModifiersEvent(serial, newModifiers, newLockingKeyState);
}
private static void dispatchKeyboardEnterEvent(long serial, long surfacePtr) {
@@ -760,8 +770,13 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
}
@Override
public boolean getLockingKeyState(int key) {
log.info("Not implemented: WLToolkit.getLockingKeyState()");
return false;
return switch (key) {
case KeyEvent.VK_CAPS_LOCK -> (inputState.lockingKeyState() & CAPS_LOCK_MASK) != 0;
case KeyEvent.VK_NUM_LOCK -> (inputState.lockingKeyState() & NUM_LOCK_MASK) != 0;
case KeyEvent.VK_SCROLL_LOCK, KeyEvent.VK_KANA_LOCK ->
throw new UnsupportedOperationException("getting locking key state is not supported for this key");
default -> throw new IllegalArgumentException("invalid key for Toolkit.getLockingKeyState");
};
}
@Override

View File

@@ -128,7 +128,7 @@ public class FontConfigManager {
if (FontUtilities.isWindows) {
return null;
} else {
int hint = getFontConfigAASettings(getFCLocaleStr(), fcFamily);
int hint = getFontConfigAASettings(fcFamily);
if (hint < 0) {
return null;
} else {
@@ -188,7 +188,7 @@ public class FontConfigManager {
fontArr[i].jdkName = FontUtilities.mapFcName(fontArr[i].fcFamily);
fontArr[i].style = i % 4; // depends on array order.
}
getFontConfig(getFCLocaleStr(), fcInfo, fontArr, includeFallbacks);
setupFontConfigFonts(getFCLocaleStr(), fcInfo, fontArr, includeFallbacks);
FontConfigFont anyFont = null;
/* If don't find anything (eg no libfontconfig), then just return */
for (int i = 0; i< fontArr.length; i++) {
@@ -432,10 +432,12 @@ public class FontConfigManager {
return fontConfigFonts;
}
public static native String getFontProperty(String name, String pattern);
/* Return an array of FcCompFont structs describing the primary
* font located for each of fontconfig/GTK/Pango's logical font names.
*/
private static native void getFontConfig(String locale,
private static native void setupFontConfigFonts(String locale,
FontConfigInfo fcInfo,
FcCompFont[] fonts,
boolean includeFallbacks);
@@ -454,7 +456,9 @@ public class FontConfigManager {
return fcInfo;
}
private static native int getFontConfigAASettings(String locale, String fcFamily);
private static native int getFontConfigAASettings(String fcFamily, String locale);
public static native String getFontProperty(String name, String pattern);
private static int getFontConfigAASettings(String fcFamily) {
return getFontConfigAASettings(fcFamily, getFCLocaleStr());
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 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. 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 JETBRAINSRUNTIME_FONTCONFIGMANAGER_H
#define JETBRAINSRUNTIME_FONTCONFIGMANAGER_H
typedef struct RenderingFontHints {
int fcHinting;
int fcHintStyle;
int fcAntialias;
int fcAutohint;
int fcRGBA;
int fcLCDFilter;
} RenderingFontHints;
void openFontConfig();
char **getFontConfigLocations();
int setupRenderingFontHints(const char* fcName, const char* locale, double size, RenderingFontHints *renderingFontHints);
#endif //JETBRAINSRUNTIME_FONTCONFIGMANAGER_H

View File

@@ -35,6 +35,8 @@
#include <fcntl.h>
#include <unistd.h>
#include "fontconfigmanager.h"
#include <jni.h>
#include <jni_util.h>
#include <jvm_md.h>
@@ -89,8 +91,6 @@ static char *fullAixFontPath[] = {
};
#endif
static char **getFontConfigLocations();
typedef struct {
const char *name[MAXFDIRS];
int num;
@@ -384,933 +384,3 @@ JNIEXPORT jstring JNICALL Java_sun_awt_FcFontManager_getFontPathNative
ret = (*env)->NewStringUTF(env, ptr);
return ret;
}
#include <dlfcn.h>
#include <fontconfig/fontconfig.h>
static void* openFontConfig() {
char *homeEnv;
static char *homeEnvStr = "HOME="; /* must be static */
void* libfontconfig = NULL;
/* Private workaround to not use fontconfig library.
* May be useful during testing/debugging
*/
char *useFC = getenv("USE_J2D_FONTCONFIG");
if (useFC != NULL && !strcmp(useFC, "no")) {
return NULL;
}
#if defined(_AIX)
/* On AIX, fontconfig is not a standard package supported by IBM.
* instead it has to be installed from the "AIX Toolbox for Linux Applications"
* site http://www-03.ibm.com/systems/power/software/aix/linux/toolbox/alpha.html
* and will be installed under /opt/freeware/lib/libfontconfig.a.
* Notice that the archive contains the real 32- and 64-bit shared libraries.
* We first try to load 'libfontconfig.so' from the default library path in the
* case the user has installed a private version of the library and if that
* doesn't succeed, we try the version from /opt/freeware/lib/libfontconfig.a
*/
libfontconfig = dlopen("libfontconfig.so", RTLD_LOCAL|RTLD_LAZY);
if (libfontconfig == NULL) {
libfontconfig = dlopen("/opt/freeware/lib/libfontconfig.a(libfontconfig.so.1)", RTLD_MEMBER|RTLD_LOCAL|RTLD_LAZY);
if (libfontconfig == NULL) {
return NULL;
}
}
#else
/* 64 bit sparc should pick up the right version from the lib path.
* New features may be added to libfontconfig, this is expected to
* be compatible with old features, but we may need to start
* distinguishing the library version, to know whether to expect
* certain symbols - and functionality - to be available.
* Also add explicit search for .so.1 in case .so symlink doesn't exist.
*/
libfontconfig = dlopen(FONTCONFIG_DLL_VERSIONED, RTLD_LOCAL|RTLD_LAZY);
if (libfontconfig == NULL) {
libfontconfig = dlopen(FONTCONFIG_DLL, RTLD_LOCAL|RTLD_LAZY);
if (libfontconfig == NULL) {
return NULL;
}
}
#endif
/* Version 1.0 of libfontconfig crashes if HOME isn't defined in
* the environment. This should generally never happen, but we can't
* control it, and can't control the version of fontconfig, so iff
* its not defined we set it to an empty value which is sufficient
* to prevent a crash. I considered unsetting it before exit, but
* it doesn't appear to work on Solaris, so I will leave it set.
*/
homeEnv = getenv("HOME");
if (homeEnv == NULL) {
putenv(homeEnvStr);
}
return libfontconfig;
}
typedef void* (FcFiniFuncType)();
static void closeFontConfig(void* libfontconfig, jboolean fcFini) {
/* NB FcFini is not in (eg) the Solaris 10 version of fontconfig. Its not
* clear if this means we are really leaking resources in those cases
* but it seems we should call this function when its available.
* But since the Swing GTK code may be still accessing the lib, its probably
* safest for now to just let this "leak" rather than potentially
* concurrently free global data still in use by other code.
*/
#if 0
if (fcFini) { /* release resources */
FcFiniFuncType FcFini = (FcFiniFuncType)dlsym(libfontconfig, "FcFini");
if (FcFini != NULL) {
(*FcFini)();
}
}
#endif
dlclose(libfontconfig);
}
typedef FcConfig* (*FcInitLoadConfigFuncType)();
typedef FcPattern* (*FcPatternBuildFuncType)(FcPattern *orig, ...);
typedef FcObjectSet* (*FcObjectSetFuncType)(const char *first, ...);
typedef FcFontSet* (*FcFontListFuncType)(FcConfig *config,
FcPattern *p,
FcObjectSet *os);
typedef FcResult (*FcPatternGetBoolFuncType)(const FcPattern *p,
const char *object,
int n,
FcBool *b);
typedef FcResult (*FcPatternGetIntegerFuncType)(const FcPattern *p,
const char *object,
int n,
int *i);
typedef FcResult (*FcPatternGetStringFuncType)(const FcPattern *p,
const char *object,
int n,
FcChar8 ** s);
typedef FcChar8* (*FcStrDirnameFuncType)(const FcChar8 *file);
typedef void (*FcPatternDestroyFuncType)(FcPattern *p);
typedef void (*FcObjectSetDestroyFuncType)(FcObjectSet *os);
typedef void (*FcFontSetDestroyFuncType)(FcFontSet *s);
typedef FcPattern* (*FcNameParseFuncType)(const FcChar8 *name);
typedef FcBool (*FcPatternAddStringFuncType)(FcPattern *p,
const char *object,
const FcChar8 *s);
typedef void (*FcDefaultSubstituteFuncType)(FcPattern *p);
typedef FcBool (*FcConfigSubstituteFuncType)(FcConfig *config,
FcPattern *p,
FcMatchKind kind);
typedef FcPattern* (*FcFontMatchFuncType)(FcConfig *config,
FcPattern *p,
FcResult *result);
typedef FcFontSet* (*FcFontSetCreateFuncType)();
typedef FcBool (*FcFontSetAddFuncType)(FcFontSet *s, FcPattern *font);
typedef void (*FcStrFreeFuncType)(FcChar8 *str);
typedef FcResult (*FcPatternGetCharSetFuncType)(FcPattern *p,
const char *object,
int n,
FcCharSet **c);
typedef FcFontSet* (*FcFontSortFuncType)(FcConfig *config,
FcPattern *p,
FcBool trim,
FcCharSet **csp,
FcResult *result);
typedef FcCharSet* (*FcCharSetUnionFuncType)(const FcCharSet *a,
const FcCharSet *b);
typedef FcCharSet* (*FcCharSetDestroyFuncType)(FcCharSet *fcs);
typedef FcChar32 (*FcCharSetSubtractCountFuncType)(const FcCharSet *a,
const FcCharSet *b);
typedef int (*FcGetVersionFuncType)();
typedef FcStrList* (*FcConfigGetCacheDirsFuncType)(FcConfig *config);
typedef FcChar8* (*FcStrListNextFuncType)(FcStrList *list);
typedef FcChar8* (*FcStrListDoneFuncType)(FcStrList *list);
typedef unsigned int (*FcFreeTypeQueryAllFuncType)(const FcChar8 *file, unsigned int id, FcBlanks *blanks,
int *count, FcFontSet *set);
typedef FcChar8* (*FcPatternFormatFuncType)(FcPattern *pat, const FcChar8 *format);
static char **getFontConfigLocations() {
char **fontdirs;
int numdirs = 0;
FcInitLoadConfigFuncType FcInitLoadConfig;
FcPatternBuildFuncType FcPatternBuild;
FcObjectSetFuncType FcObjectSetBuild;
FcFontListFuncType FcFontList;
FcPatternGetStringFuncType FcPatternGetString;
FcStrDirnameFuncType FcStrDirname;
FcPatternDestroyFuncType FcPatternDestroy;
FcObjectSetDestroyFuncType FcObjectSetDestroy;
FcFontSetDestroyFuncType FcFontSetDestroy;
FcConfig *fontconfig;
FcPattern *pattern;
FcObjectSet *objset;
FcFontSet *fontSet;
FcStrList *strList;
FcChar8 *str;
int i, f, found, len=0;
char **fontPath;
void* libfontconfig = openFontConfig();
if (libfontconfig == NULL) {
return NULL;
}
FcPatternBuild =
(FcPatternBuildFuncType)dlsym(libfontconfig, "FcPatternBuild");
FcObjectSetBuild =
(FcObjectSetFuncType)dlsym(libfontconfig, "FcObjectSetBuild");
FcFontList =
(FcFontListFuncType)dlsym(libfontconfig, "FcFontList");
FcPatternGetString =
(FcPatternGetStringFuncType)dlsym(libfontconfig, "FcPatternGetString");
FcStrDirname =
(FcStrDirnameFuncType)dlsym(libfontconfig, "FcStrDirname");
FcPatternDestroy =
(FcPatternDestroyFuncType)dlsym(libfontconfig, "FcPatternDestroy");
FcObjectSetDestroy =
(FcObjectSetDestroyFuncType)dlsym(libfontconfig, "FcObjectSetDestroy");
FcFontSetDestroy =
(FcFontSetDestroyFuncType)dlsym(libfontconfig, "FcFontSetDestroy");
if (FcPatternBuild == NULL ||
FcObjectSetBuild == NULL ||
FcPatternGetString == NULL ||
FcFontList == NULL ||
FcStrDirname == NULL ||
FcPatternDestroy == NULL ||
FcObjectSetDestroy == NULL ||
FcFontSetDestroy == NULL) { /* problem with the library: return. */
closeFontConfig(libfontconfig, JNI_FALSE);
return NULL;
}
/* Make calls into the fontconfig library to build a search for
* outline fonts, and to get the set of full file paths from the matches.
* This set is returned from the call to FcFontList(..)
* We allocate an array of char* pointers sufficient to hold all
* the matches + 1 extra which ensures there will be a NULL after all
* valid entries.
* We call FcStrDirname strip the file name from the path, and
* check if we have yet seen this directory. If not we add a pointer to
* it into our array of char*. Note that FcStrDirname returns newly
* allocated storage so we can use this in the return char** value.
* Finally we clean up, freeing allocated resources, and return the
* array of unique directories.
*/
pattern = (*FcPatternBuild)(NULL, FC_OUTLINE, FcTypeBool, FcTrue, NULL);
objset = (*FcObjectSetBuild)(FC_FILE, NULL);
fontSet = (*FcFontList)(NULL, pattern, objset);
if (fontSet == NULL) {
/* FcFontList() may return NULL if fonts are not installed. */
fontdirs = NULL;
} else {
fontdirs = (char**)calloc(fontSet->nfont+1, sizeof(char*));
if (fontdirs == NULL) {
(*FcFontSetDestroy)(fontSet);
goto cleanup;
}
for (f=0; f < fontSet->nfont; f++) {
FcChar8 *file;
FcChar8 *dir;
if ((*FcPatternGetString)(fontSet->fonts[f], FC_FILE, 0, &file) ==
FcResultMatch) {
dir = (*FcStrDirname)(file);
found = 0;
for (i=0;i<numdirs; i++) {
if (strcmp(fontdirs[i], (char*)dir) == 0) {
found = 1;
break;
}
}
if (!found) {
fontdirs[numdirs++] = (char*)dir;
} else {
free((char*)dir);
}
}
}
/* Free fontset if one was returned */
(*FcFontSetDestroy)(fontSet);
}
cleanup:
/* Free memory and close the ".so" */
(*FcObjectSetDestroy)(objset);
(*FcPatternDestroy)(pattern);
closeFontConfig(libfontconfig, JNI_TRUE);
return fontdirs;
}
/* These are copied from sun.awt.SunHints.
* Consider initialising them as ints using JNI for more robustness.
*/
#define TEXT_AA_OFF 1
#define TEXT_AA_ON 2
#define TEXT_AA_LCD_HRGB 4
#define TEXT_AA_LCD_HBGR 5
#define TEXT_AA_LCD_VRGB 6
#define TEXT_AA_LCD_VBGR 7
JNIEXPORT jint JNICALL
Java_sun_font_FontConfigManager_getFontConfigAASettings
(JNIEnv *env, jclass obj, jstring localeStr, jstring fcNameStr) {
FcNameParseFuncType FcNameParse;
FcPatternAddStringFuncType FcPatternAddString;
FcConfigSubstituteFuncType FcConfigSubstitute;
FcDefaultSubstituteFuncType FcDefaultSubstitute;
FcFontMatchFuncType FcFontMatch;
FcPatternGetBoolFuncType FcPatternGetBool;
FcPatternGetIntegerFuncType FcPatternGetInteger;
FcPatternDestroyFuncType FcPatternDestroy;
FcPattern *pattern, *matchPattern;
FcResult result;
FcBool antialias = FcFalse;
int rgba = 0;
const char *locale=NULL, *fcName=NULL;
void* libfontconfig;
if (fcNameStr == NULL || localeStr == NULL) {
return -1;
}
fcName = (*env)->GetStringUTFChars(env, fcNameStr, 0);
if (fcName == NULL) {
return -1;
}
locale = (*env)->GetStringUTFChars(env, localeStr, 0);
if ((libfontconfig = openFontConfig()) == NULL) {
(*env)->ReleaseStringUTFChars(env, fcNameStr, (const char*)fcName);
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr,(const char*)locale);
}
return -1;
}
FcNameParse = (FcNameParseFuncType)dlsym(libfontconfig, "FcNameParse");
FcPatternAddString =
(FcPatternAddStringFuncType)dlsym(libfontconfig, "FcPatternAddString");
FcConfigSubstitute =
(FcConfigSubstituteFuncType)dlsym(libfontconfig, "FcConfigSubstitute");
FcDefaultSubstitute = (FcDefaultSubstituteFuncType)
dlsym(libfontconfig, "FcDefaultSubstitute");
FcFontMatch = (FcFontMatchFuncType)dlsym(libfontconfig, "FcFontMatch");
FcPatternGetBool = (FcPatternGetBoolFuncType)
dlsym(libfontconfig, "FcPatternGetBool");
FcPatternGetInteger = (FcPatternGetIntegerFuncType)
dlsym(libfontconfig, "FcPatternGetInteger");
FcPatternDestroy =
(FcPatternDestroyFuncType)dlsym(libfontconfig, "FcPatternDestroy");
if (FcNameParse == NULL ||
FcPatternAddString == NULL ||
FcConfigSubstitute == NULL ||
FcDefaultSubstitute == NULL ||
FcFontMatch == NULL ||
FcPatternGetBool == NULL ||
FcPatternGetInteger == NULL ||
FcPatternDestroy == NULL) { /* problem with the library: return. */
(*env)->ReleaseStringUTFChars(env, fcNameStr, (const char*)fcName);
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr,(const char*)locale);
}
closeFontConfig(libfontconfig, JNI_FALSE);
return -1;
}
pattern = (*FcNameParse)((FcChar8 *)fcName);
if (locale != NULL) {
(*FcPatternAddString)(pattern, FC_LANG, (unsigned char*)locale);
}
(*FcConfigSubstitute)(NULL, pattern, FcMatchPattern);
(*FcDefaultSubstitute)(pattern);
matchPattern = (*FcFontMatch)(NULL, pattern, &result);
/* Perhaps should call FcFontRenderPrepare() here as some pattern
* elements might change as a result of that call, but I'm not seeing
* any difference in testing.
*/
if (matchPattern) {
(*FcPatternGetBool)(matchPattern, FC_ANTIALIAS, 0, &antialias);
(*FcPatternGetInteger)(matchPattern, FC_RGBA, 0, &rgba);
(*FcPatternDestroy)(matchPattern);
}
(*FcPatternDestroy)(pattern);
(*env)->ReleaseStringUTFChars(env, fcNameStr, (const char*)fcName);
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
closeFontConfig(libfontconfig, JNI_TRUE);
if (antialias == FcFalse) {
return TEXT_AA_OFF;
} else if (rgba <= FC_RGBA_UNKNOWN || rgba >= FC_RGBA_NONE) {
return TEXT_AA_ON;
} else {
switch (rgba) {
case FC_RGBA_RGB : return TEXT_AA_LCD_HRGB;
case FC_RGBA_BGR : return TEXT_AA_LCD_HBGR;
case FC_RGBA_VRGB : return TEXT_AA_LCD_VRGB;
case FC_RGBA_VBGR : return TEXT_AA_LCD_VBGR;
default : return TEXT_AA_LCD_HRGB; // should not get here.
}
}
}
JNIEXPORT jint JNICALL
Java_sun_font_FontConfigManager_getFontConfigVersion
(JNIEnv *env, jclass obj) {
void* libfontconfig;
FcGetVersionFuncType FcGetVersion;
int version = 0;
if ((libfontconfig = openFontConfig()) == NULL) {
return 0;
}
FcGetVersion = (FcGetVersionFuncType)dlsym(libfontconfig, "FcGetVersion");
if (FcGetVersion == NULL) {
closeFontConfig(libfontconfig, JNI_FALSE);
return 0;
}
version = (*FcGetVersion)();
closeFontConfig(libfontconfig, JNI_FALSE);
return version;
}
JNIEXPORT jstring JNICALL
Java_sun_font_FontConfigManager_getFontProperty
(JNIEnv *env, jclass obj, jstring query, jstring property) {
void* libfontconfig = NULL;
FcNameParseFuncType FcNameParse;
FcPatternFormatFuncType FcPatternFormat;
FcConfigSubstituteFuncType FcConfigSubstitute;
FcDefaultSubstituteFuncType FcDefaultSubstitute;
FcFontMatchFuncType FcFontMatch;
FcStrFreeFuncType FcStrFree;
const char *queryPtr = NULL;
const char *propertyPtr = NULL;
FcChar8 *fontFamily = NULL;
FcChar8 *fontPath = NULL;
jstring res = NULL;
if ((libfontconfig = openFontConfig()) == NULL) {
goto cleanup;
}
FcPatternFormat = (FcPatternFormatFuncType)dlsym(libfontconfig, "FcPatternFormat");
FcNameParse = (FcNameParseFuncType)dlsym(libfontconfig, "FcNameParse");
FcConfigSubstitute = (FcConfigSubstituteFuncType)dlsym(libfontconfig, "FcConfigSubstitute");
FcDefaultSubstitute = (FcDefaultSubstituteFuncType)dlsym(libfontconfig, "FcDefaultSubstitute");
FcFontMatch = (FcFontMatchFuncType)dlsym(libfontconfig, "FcFontMatch");
FcStrFree = (FcStrFreeFuncType)dlsym(libfontconfig, "FcStrFree");
queryPtr = (*env)->GetStringUTFChars(env, query, 0);
propertyPtr = (*env)->GetStringUTFChars(env, property, 0);
if (queryPtr == NULL || propertyPtr == NULL) {
goto cleanup;
}
FcPattern *pattern = (*FcNameParse)((FcChar8 *) queryPtr);
if (pattern == NULL) {
goto cleanup;
}
(*FcConfigSubstitute)(NULL, pattern, FcMatchScan);
(*FcDefaultSubstitute)(pattern);
FcResult fcResult;
FcPattern *match = (*FcFontMatch)(0, pattern, &fcResult);
if (match == NULL || fcResult != FcResultMatch) {
goto cleanup;
}
fontFamily = (FcPatternFormat)(match, (FcChar8*) "%{family}");
if (fontFamily == NULL) {
goto cleanup;
}
// result of foundFontName could be set of families, so we left only first family
char *commaPos = strchr((char *) fontFamily, ',');
if (commaPos != NULL) {
*commaPos = '\0';
}
if (strstr(queryPtr, (char *) fontFamily) == NULL) {
goto cleanup;
}
fontPath = (FcPatternFormat)(match, (FcChar8*) propertyPtr);
if (fontPath == NULL) {
goto cleanup;
}
res = (*env)->NewStringUTF(env, (char *) fontPath);
cleanup:
if (fontPath) {
(FcStrFree)(fontPath);
}
if (fontFamily) {
(FcStrFree)(fontFamily);
}
if (propertyPtr) {
(*env)->ReleaseStringUTFChars(env, property, (const char*)propertyPtr);
}
if (queryPtr) {
(*env)->ReleaseStringUTFChars(env, query, (const char*)queryPtr);
}
if (libfontconfig) {
closeFontConfig(libfontconfig, JNI_FALSE);
}
return res;
}
JNIEXPORT void JNICALL
Java_sun_font_FontConfigManager_getFontConfig
(JNIEnv *env, jclass obj, jstring localeStr, jobject fcInfoObj,
jobjectArray fcCompFontArray, jboolean includeFallbacks) {
FcNameParseFuncType FcNameParse;
FcPatternAddStringFuncType FcPatternAddString;
FcConfigSubstituteFuncType FcConfigSubstitute;
FcDefaultSubstituteFuncType FcDefaultSubstitute;
FcFontMatchFuncType FcFontMatch;
FcPatternGetStringFuncType FcPatternGetString;
FcPatternDestroyFuncType FcPatternDestroy;
FcPatternGetCharSetFuncType FcPatternGetCharSet;
FcFontSortFuncType FcFontSort;
FcFontSetDestroyFuncType FcFontSetDestroy;
FcCharSetUnionFuncType FcCharSetUnion;
FcCharSetDestroyFuncType FcCharSetDestroy;
FcCharSetSubtractCountFuncType FcCharSetSubtractCount;
FcGetVersionFuncType FcGetVersion;
FcConfigGetCacheDirsFuncType FcConfigGetCacheDirs;
FcStrListNextFuncType FcStrListNext;
FcStrListDoneFuncType FcStrListDone;
int i, arrlen;
jobject fcCompFontObj;
jstring fcNameStr, jstr;
const char *locale, *fcName;
FcPattern *pattern;
FcResult result;
void* libfontconfig;
jfieldID fcNameID, fcFirstFontID, fcAllFontsID, fcVersionID, fcCacheDirsID;
jfieldID familyNameID, styleNameID, fullNameID, fontFileID;
jmethodID fcFontCons;
char* debugMinGlyphsStr = getenv("J2D_DEBUG_MIN_GLYPHS");
jclass fcInfoClass;
jclass fcCompFontClass;
jclass fcFontClass;
CHECK_NULL(fcInfoObj);
CHECK_NULL(fcCompFontArray);
fcInfoClass =
(*env)->FindClass(env, "sun/font/FontConfigManager$FontConfigInfo");
CHECK_NULL(fcInfoClass);
fcCompFontClass =
(*env)->FindClass(env, "sun/font/FontConfigManager$FcCompFont");
CHECK_NULL(fcCompFontClass);
fcFontClass =
(*env)->FindClass(env, "sun/font/FontConfigManager$FontConfigFont");
CHECK_NULL(fcFontClass);
CHECK_NULL(fcVersionID = (*env)->GetFieldID(env, fcInfoClass, "fcVersion", "I"));
CHECK_NULL(fcCacheDirsID = (*env)->GetFieldID(env, fcInfoClass, "cacheDirs",
"[Ljava/lang/String;"));
CHECK_NULL(fcNameID = (*env)->GetFieldID(env, fcCompFontClass,
"fcName", "Ljava/lang/String;"));
CHECK_NULL(fcFirstFontID = (*env)->GetFieldID(env, fcCompFontClass, "firstFont",
"Lsun/font/FontConfigManager$FontConfigFont;"));
CHECK_NULL(fcAllFontsID = (*env)->GetFieldID(env, fcCompFontClass, "allFonts",
"[Lsun/font/FontConfigManager$FontConfigFont;"));
CHECK_NULL(fcFontCons = (*env)->GetMethodID(env, fcFontClass, "<init>", "()V"));
CHECK_NULL(familyNameID = (*env)->GetFieldID(env, fcFontClass,
"familyName", "Ljava/lang/String;"));
CHECK_NULL(styleNameID = (*env)->GetFieldID(env, fcFontClass,
"styleStr", "Ljava/lang/String;"));
CHECK_NULL(fullNameID = (*env)->GetFieldID(env, fcFontClass,
"fullName", "Ljava/lang/String;"));
CHECK_NULL(fontFileID = (*env)->GetFieldID(env, fcFontClass,
"fontFile", "Ljava/lang/String;"));
if ((libfontconfig = openFontConfig()) == NULL) {
return;
}
FcNameParse = (FcNameParseFuncType)dlsym(libfontconfig, "FcNameParse");
FcPatternAddString =
(FcPatternAddStringFuncType)dlsym(libfontconfig, "FcPatternAddString");
FcConfigSubstitute =
(FcConfigSubstituteFuncType)dlsym(libfontconfig, "FcConfigSubstitute");
FcDefaultSubstitute = (FcDefaultSubstituteFuncType)
dlsym(libfontconfig, "FcDefaultSubstitute");
FcFontMatch = (FcFontMatchFuncType)dlsym(libfontconfig, "FcFontMatch");
FcPatternGetString =
(FcPatternGetStringFuncType)dlsym(libfontconfig, "FcPatternGetString");
FcPatternDestroy =
(FcPatternDestroyFuncType)dlsym(libfontconfig, "FcPatternDestroy");
FcPatternGetCharSet =
(FcPatternGetCharSetFuncType)dlsym(libfontconfig,
"FcPatternGetCharSet");
FcFontSort =
(FcFontSortFuncType)dlsym(libfontconfig, "FcFontSort");
FcFontSetDestroy =
(FcFontSetDestroyFuncType)dlsym(libfontconfig, "FcFontSetDestroy");
FcCharSetUnion =
(FcCharSetUnionFuncType)dlsym(libfontconfig, "FcCharSetUnion");
FcCharSetDestroy =
(FcCharSetDestroyFuncType)dlsym(libfontconfig, "FcCharSetDestroy");
FcCharSetSubtractCount =
(FcCharSetSubtractCountFuncType)dlsym(libfontconfig,
"FcCharSetSubtractCount");
FcGetVersion = (FcGetVersionFuncType)dlsym(libfontconfig, "FcGetVersion");
if (FcNameParse == NULL ||
FcPatternAddString == NULL ||
FcConfigSubstitute == NULL ||
FcDefaultSubstitute == NULL ||
FcFontMatch == NULL ||
FcPatternGetString == NULL ||
FcPatternDestroy == NULL ||
FcPatternGetCharSet == NULL ||
FcFontSetDestroy == NULL ||
FcCharSetUnion == NULL ||
FcCharSetDestroy == NULL ||
FcGetVersion == NULL ||
FcCharSetSubtractCount == NULL) {/* problem with the library: return.*/
closeFontConfig(libfontconfig, JNI_FALSE);
return;
}
(*env)->SetIntField(env, fcInfoObj, fcVersionID, (*FcGetVersion)());
/* Optionally get the cache dir locations. This isn't
* available until v 2.4.x, but this is OK since on those later versions
* we can check the time stamps on the cache dirs to see if we
* are out of date. There are a couple of assumptions here. First
* that the time stamp on the directory changes when the contents are
* updated. Secondly that the locations don't change. The latter is
* most likely if a new version of fontconfig is installed, but we also
* invalidate the cache if we detect that. Arguably even that is "rare",
* and most likely is tied to an OS upgrade which gets a new file anyway.
*/
FcConfigGetCacheDirs =
(FcConfigGetCacheDirsFuncType)dlsym(libfontconfig,
"FcConfigGetCacheDirs");
FcStrListNext =
(FcStrListNextFuncType)dlsym(libfontconfig, "FcStrListNext");
FcStrListDone =
(FcStrListDoneFuncType)dlsym(libfontconfig, "FcStrListDone");
if (FcStrListNext != NULL && FcStrListDone != NULL &&
FcConfigGetCacheDirs != NULL) {
FcStrList* cacheDirs;
FcChar8* cacheDir;
int cnt = 0;
jobject cacheDirArray =
(*env)->GetObjectField(env, fcInfoObj, fcCacheDirsID);
int max = (*env)->GetArrayLength(env, cacheDirArray);
cacheDirs = (*FcConfigGetCacheDirs)(NULL);
if (cacheDirs != NULL) {
while ((cnt < max) && (cacheDir = (*FcStrListNext)(cacheDirs))) {
jstr = (*env)->NewStringUTF(env, (const char*)cacheDir);
if (IS_NULL(jstr)) {
(*FcStrListDone)(cacheDirs);
return;
}
(*env)->SetObjectArrayElement(env, cacheDirArray, cnt++, jstr);
(*env)->DeleteLocalRef(env, jstr);
}
(*FcStrListDone)(cacheDirs);
}
}
locale = (*env)->GetStringUTFChars(env, localeStr, 0);
if (locale == NULL) {
(*env)->ExceptionClear(env);
JNU_ThrowOutOfMemoryError(env, "Could not create locale");
return;
}
arrlen = (*env)->GetArrayLength(env, fcCompFontArray);
for (i=0; i<arrlen; i++) {
FcFontSet* fontset;
int fn, j, fontCount, nfonts;
unsigned int minGlyphs;
FcChar8 **family, **styleStr, **fullname, **file;
jarray fcFontArr = NULL;
FcCharSet *unionCharset = NULL;
FcCharSet *prevUnionCharset = NULL;
fcCompFontObj = (*env)->GetObjectArrayElement(env, fcCompFontArray, i);
fcNameStr =
(jstring)((*env)->GetObjectField(env, fcCompFontObj, fcNameID));
fcName = (*env)->GetStringUTFChars(env, fcNameStr, 0);
if (fcName == NULL) {
(*env)->DeleteLocalRef(env, fcCompFontObj);
(*env)->DeleteLocalRef(env, fcNameStr);
continue;
}
pattern = (*FcNameParse)((FcChar8 *)fcName);
(*env)->ReleaseStringUTFChars(env, fcNameStr, (const char*)fcName);
(*env)->DeleteLocalRef(env, fcNameStr);
if (pattern == NULL) {
closeFontConfig(libfontconfig, JNI_FALSE);
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
return;
}
/* locale may not usually be necessary as fontconfig appears to apply
* this anyway based on the user's environment. However we want
* to use the value of the JDK startup locale so this should take
* care of it.
*/
if (locale != NULL) {
(*FcPatternAddString)(pattern, FC_LANG, (unsigned char*)locale);
}
(*FcConfigSubstitute)(NULL, pattern, FcMatchPattern);
(*FcDefaultSubstitute)(pattern);
fontset = (*FcFontSort)(NULL, pattern, FcTrue, NULL, &result);
if (fontset == NULL) {
(*FcPatternDestroy)(pattern);
closeFontConfig(libfontconfig, JNI_FALSE);
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
return;
}
/* fontconfig returned us "nfonts". If we are just getting the
* first font, we set nfont to zero. Otherwise we use "nfonts".
* Next create separate C arrays of length nfonts for family file etc.
* Inspect the returned fonts and the ones we like (adds enough glyphs)
* are added to the arrays and we increment 'fontCount'.
*/
nfonts = fontset->nfont;
family = (FcChar8**)calloc(nfonts, sizeof(FcChar8*));
styleStr = (FcChar8**)calloc(nfonts, sizeof(FcChar8*));
fullname = (FcChar8**)calloc(nfonts, sizeof(FcChar8*));
file = (FcChar8**)calloc(nfonts, sizeof(FcChar8*));
if (family == NULL || styleStr == NULL ||
fullname == NULL || file == NULL) {
if (family != NULL) {
free(family);
}
if (styleStr != NULL) {
free(styleStr);
}
if (fullname != NULL) {
free(fullname);
}
if (file != NULL) {
free(file);
}
(*FcPatternDestroy)(pattern);
(*FcFontSetDestroy)(fontset);
closeFontConfig(libfontconfig, JNI_FALSE);
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
return;
}
fontCount = 0;
minGlyphs = 20;
if (debugMinGlyphsStr != NULL) {
int val = minGlyphs;
sscanf(debugMinGlyphsStr, "%5d", &val);
if (val >= 0 && val <= 65536) {
minGlyphs = val;
}
}
for (j=0; j<nfonts; j++) {
FcPattern *fontPattern = fontset->fonts[j];
FcChar8 *fontformat;
FcCharSet *charset = NULL;
fontformat = NULL;
(*FcPatternGetString)(fontPattern, FC_FONTFORMAT, 0, &fontformat);
/* We only want TrueType fonts but some Linuxes still depend
* on Type 1 fonts for some Locale support, so we'll allow
* them there.
*/
if (fontformat != NULL
&& (strcmp((char*)fontformat, "TrueType") != 0)
#if defined(__linux__) || defined(_AIX)
&& (strcmp((char*)fontformat, "Type 1") != 0)
&& (strcmp((char*)fontformat, "CFF") != 0)
#endif
) {
continue;
}
result = (*FcPatternGetCharSet)(fontPattern,
FC_CHARSET, 0, &charset);
if (result != FcResultMatch) {
free(family);
free(fullname);
free(styleStr);
free(file);
(*FcPatternDestroy)(pattern);
(*FcFontSetDestroy)(fontset);
if (prevUnionCharset != NULL) {
(*FcCharSetDestroy)(prevUnionCharset);
}
closeFontConfig(libfontconfig, JNI_FALSE);
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
return;
}
/* We don't want 20 or 30 fonts, so once we hit 10 fonts,
* then require that they really be adding value. Too many
* adversely affects load time for minimal value-add.
* This is still likely far more than we've had in the past.
*/
if (j==10) {
minGlyphs = 50;
}
if (unionCharset == NULL) {
unionCharset = charset;
} else {
if ((*FcCharSetSubtractCount)(charset, unionCharset)
> minGlyphs) {
unionCharset = (* FcCharSetUnion)(unionCharset, charset);
if (prevUnionCharset != NULL) {
(*FcCharSetDestroy)(prevUnionCharset);
}
prevUnionCharset = unionCharset;
} else {
continue;
}
}
fontCount++; // found a font we will use.
(*FcPatternGetString)(fontPattern, FC_FILE, 0, &file[j]);
(*FcPatternGetString)(fontPattern, FC_FAMILY, 0, &family[j]);
(*FcPatternGetString)(fontPattern, FC_STYLE, 0, &styleStr[j]);
(*FcPatternGetString)(fontPattern, FC_FULLNAME, 0, &fullname[j]);
if (!includeFallbacks) {
break;
}
if (fontCount == 254) {
break; // CompositeFont will only use up to 254 slots from here.
}
}
// Release last instance of CharSet union
if (prevUnionCharset != NULL) {
(*FcCharSetDestroy)(prevUnionCharset);
}
/* Once we get here 'fontCount' is the number of returned fonts
* we actually want to use, so we create 'fcFontArr' of that length.
* The non-null entries of "family[]" etc are those fonts.
* Then loop again over all nfonts adding just those non-null ones
* to 'fcFontArr'. If its null (we didn't want the font)
* then we don't enter the main body.
* So we should never get more than 'fontCount' entries.
*/
if (includeFallbacks) {
fcFontArr =
(*env)->NewObjectArray(env, fontCount, fcFontClass, NULL);
if (IS_NULL(fcFontArr)) {
free(family);
free(fullname);
free(styleStr);
free(file);
(*FcPatternDestroy)(pattern);
(*FcFontSetDestroy)(fontset);
closeFontConfig(libfontconfig, JNI_FALSE);
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
return;
}
(*env)->SetObjectField(env,fcCompFontObj, fcAllFontsID, fcFontArr);
}
fn=0;
for (j=0;j<nfonts;j++) {
if (family[j] != NULL) {
jobject fcFont =
(*env)->NewObject(env, fcFontClass, fcFontCons);
if (IS_NULL(fcFont)) break;
jstr = (*env)->NewStringUTF(env, (const char*)family[j]);
if (IS_NULL(jstr)) break;
(*env)->SetObjectField(env, fcFont, familyNameID, jstr);
(*env)->DeleteLocalRef(env, jstr);
if (file[j] != NULL) {
jstr = (*env)->NewStringUTF(env, (const char*)file[j]);
if (IS_NULL(jstr)) break;
(*env)->SetObjectField(env, fcFont, fontFileID, jstr);
(*env)->DeleteLocalRef(env, jstr);
}
if (styleStr[j] != NULL) {
jstr = (*env)->NewStringUTF(env, (const char*)styleStr[j]);
if (IS_NULL(jstr)) break;
(*env)->SetObjectField(env, fcFont, styleNameID, jstr);
(*env)->DeleteLocalRef(env, jstr);
}
if (fullname[j] != NULL) {
jstr = (*env)->NewStringUTF(env, (const char*)fullname[j]);
if (IS_NULL(jstr)) break;
(*env)->SetObjectField(env, fcFont, fullNameID, jstr);
(*env)->DeleteLocalRef(env, jstr);
}
if (fn==0) {
(*env)->SetObjectField(env, fcCompFontObj,
fcFirstFontID, fcFont);
}
if (includeFallbacks) {
(*env)->SetObjectArrayElement(env, fcFontArr, fn++,fcFont);
} else {
(*env)->DeleteLocalRef(env, fcFont);
break;
}
(*env)->DeleteLocalRef(env, fcFont);
}
}
if (includeFallbacks) {
(*env)->DeleteLocalRef(env, fcFontArr);
}
(*env)->DeleteLocalRef(env, fcCompFontObj);
(*FcFontSetDestroy)(fontset);
(*FcPatternDestroy)(pattern);
free(family);
free(styleStr);
free(fullname);
free(file);
}
/* release resources and close the ".so" */
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
closeFontConfig(libfontconfig, JNI_TRUE);
}

View File

@@ -32,6 +32,15 @@
#include <jvm.h>
#include "gdefs.h"
#include "sun_awt_PlatformGraphicsInfo.h"
#if defined(_WIN32) || defined(MACOSX)
#define DISABLE_FONTCONFIG
#endif
#ifndef DISABLE_FONTCONFIG
#include "fontconfigmanager.h"
#endif
#include <sys/param.h>
#include <sys/utsname.h>
@@ -196,5 +205,8 @@ AWT_OnLoad(JavaVM *vm, void *reserved)
JNIEXPORT jint JNICALL
DEF_JNI_OnLoad(JavaVM *vm, void *reserved)
{
#ifndef DISABLE_FONTCONFIG
openFontConfig();
#endif
return AWT_OnLoad(vm, reserved);
}

View File

@@ -0,0 +1,884 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 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. 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 <jni.h>
#include <jni_util.h>
#include <jvm_md.h>
#include <sizecalc.h>
#if defined(MACOSX)
#define DISABLE_FONTCONFIG
#endif
#ifndef DISABLE_FONTCONFIG
#if defined(__linux__)
#include <string.h>
#endif /* __linux__ */
#include <dlfcn.h>
#include <fontconfig/fontconfig.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include "fontconfigmanager.h"
#ifndef HEADLESS
#include <awt.h>
#else
/* locks ought to be included from awt.h */
#define AWT_LOCK()
#define AWT_UNLOCK()
#endif /* !HEADLESS */
#define FONTCONFIG_DLL_VERSIONED VERSIONED_JNI_LIB_NAME("fontconfig", "1")
#define FONTCONFIG_DLL JNI_LIB_NAME("fontconfig")
#if defined( __linux__)
/* All the known interesting locations we have discovered on
* various flavors of Linux
*/
static char *fullLinuxFontPath[] = {
"/usr/X11R6/lib/X11/fonts/TrueType", /* RH 7.1+ */
"/usr/X11R6/lib/X11/fonts/truetype", /* SuSE */
"/usr/X11R6/lib/X11/fonts/tt",
"/usr/X11R6/lib/X11/fonts/TTF",
"/usr/X11R6/lib/X11/fonts/OTF", /* RH 9.0 (but empty!) */
"/usr/share/fonts/ja/TrueType", /* RH 7.2+ */
"/usr/share/fonts/truetype",
"/usr/share/fonts/ko/TrueType", /* RH 9.0 */
"/usr/share/fonts/zh_CN/TrueType", /* RH 9.0 */
"/usr/share/fonts/zh_TW/TrueType", /* RH 9.0 */
"/var/lib/defoma/x-ttcidfont-conf.d/dirs/TrueType", /* Debian */
"/usr/X11R6/lib/X11/fonts/Type1",
"/usr/share/fonts/default/Type1", /* RH 9.0 */
NULL, /* terminates the list */
};
#elif defined(_AIX)
static char *fullAixFontPath[] = {
"/usr/lpp/X11/lib/X11/fonts/Type1", /* from X11.fnt.iso_T1 */
"/usr/lpp/X11/lib/X11/fonts/TrueType", /* from X11.fnt.ucs.ttf */
NULL, /* terminates the list */
};
#endif
typedef FcConfig* (*FcInitLoadConfigFuncType)();
typedef FcPattern* (*FcPatternBuildFuncType)(FcPattern *orig, ...);
typedef FcObjectSet* (*FcObjectSetFuncType)(const char *first, ...);
typedef FcFontSet* (*FcFontListFuncType)(FcConfig *config, FcPattern *p, FcObjectSet *os);
typedef FcResult (*FcPatternGetBoolFuncType)(const FcPattern *p, const char *object, int n, FcBool *b);
typedef FcResult (*FcPatternGetIntegerFuncType)(const FcPattern *p, const char *object, int n, int *i);
typedef FcResult (*FcPatternGetStringFuncType)(const FcPattern *p, const char *object, int n, FcChar8 ** s);
typedef FcChar8* (*FcStrDirnameFuncType)(const FcChar8 *file);
typedef void (*FcPatternDestroyFuncType)(FcPattern *p);
typedef void (*FcObjectSetDestroyFuncType)(FcObjectSet *os);
typedef void (*FcFontSetDestroyFuncType)(FcFontSet *s);
typedef FcPattern* (*FcNameParseFuncType)(const FcChar8 *name);
typedef FcBool (*FcPatternAddStringFuncType)(FcPattern *p, const char *object, const FcChar8 *s);
typedef FcBool (*FcPatternAddDoubleFuncType)(FcPattern *p, const char *object, double v);
typedef void (*FcDefaultSubstituteFuncType)(FcPattern *p);
typedef FcBool (*FcConfigSubstituteFuncType)(FcConfig *config, FcPattern *p, FcMatchKind kind);
typedef FcPattern* (*FcFontMatchFuncType)(FcConfig *config, FcPattern *p, FcResult *result);
typedef FcFontSet* (*FcFontSetCreateFuncType)();
typedef FcBool (*FcFontSetAddFuncType)(FcFontSet *s, FcPattern *font);
typedef FcResult (*FcPatternGetCharSetFuncType)(FcPattern *p, const char *object, int n, FcCharSet **c);
typedef FcFontSet* (*FcFontSortFuncType)(FcConfig *config, FcPattern *p, FcBool trim, FcCharSet **csp, FcResult *result);
typedef FcCharSet* (*FcCharSetUnionFuncType)(const FcCharSet *a, const FcCharSet *b);
typedef FcCharSet* (*FcCharSetDestroyFuncType)(FcCharSet *fcs);
typedef FcChar32 (*FcCharSetSubtractCountFuncType)(const FcCharSet *a, const FcCharSet *b);
typedef int (*FcGetVersionFuncType)();
typedef FcStrList* (*FcConfigGetCacheDirsFuncType)(FcConfig *config);
typedef FcChar8* (*FcStrListNextFuncType)(FcStrList *list);
typedef FcChar8* (*FcStrListDoneFuncType)(FcStrList *list);
typedef FcChar8* (*FcPatternFormatFuncType)(FcPattern *pat, const FcChar8 *format);
typedef void (*FcStrFreeFuncType)(FcChar8 *str);
static FcInitLoadConfigFuncType fcInitLoadConfig;
static FcPatternBuildFuncType fcPatternBuild;
static FcObjectSetFuncType fcObjectSetBuild;
static FcFontListFuncType fcFontList;
static FcStrDirnameFuncType fcStrDirname;
static FcObjectSetDestroyFuncType fcObjectSetDestroy;
static FcPatternGetBoolFuncType fcPatternGetBool;
static FcPatternGetIntegerFuncType fcPatternGetInteger;
static FcNameParseFuncType fcNameParse;
static FcPatternAddStringFuncType fcPatternAddString;
static FcPatternAddDoubleFuncType fcPatternAddDouble;
static FcConfigSubstituteFuncType fcConfigSubstitute;
static FcDefaultSubstituteFuncType fcDefaultSubstitute;
static FcFontMatchFuncType fcFontMatch;
static FcPatternGetStringFuncType fcPatternGetString;
static FcPatternDestroyFuncType fcPatternDestroy;
static FcPatternGetCharSetFuncType fcPatternGetCharSet;
static FcFontSortFuncType fcFontSort;
static FcFontSetDestroyFuncType fcFontSetDestroy;
static FcCharSetUnionFuncType fcCharSetUnion;
static FcCharSetDestroyFuncType fcCharSetDestroy;
static FcCharSetSubtractCountFuncType fcCharSetSubtractCount;
static FcGetVersionFuncType fcGetVersion;
static FcConfigGetCacheDirsFuncType fcConfigGetCacheDirs;
static FcStrListNextFuncType fcStrListNext;
static FcStrListDoneFuncType fcStrListDone;
static FcPatternFormatFuncType fcPatternFormat;
static FcStrFreeFuncType fcStrFree;
static void *libfontconfig = NULL;
static void closeFontConfig() {
if (libfontconfig != NULL) {
dlclose(libfontconfig);
libfontconfig = NULL;
}
}
void openFontConfig() {
char *homeEnv;
static char *homeEnvStr = "HOME="; /* must be static */
/* Private workaround to not use fontconfig library.
* May be useful during testing/debugging
*/
char *useFC = getenv("USE_J2D_FONTCONFIG");
if (useFC != NULL && !strcmp(useFC, "no")) {
return;
}
#if defined(_AIX)
/* On AIX, fontconfig is not a standard package supported by IBM.
* instead it has to be installed from the "AIX Toolbox for Linux Applications"
* site http://www-03.ibm.com/systems/power/software/aix/linux/toolbox/alpha.html
* and will be installed under /opt/freeware/lib/libfontconfig.a.
* Notice that the archive contains the real 32- and 64-bit shared libraries.
* We first try to load 'libfontconfig.so' from the default library path in the
* case the user has installed a private version of the library and if that
* doesn't succeed, we try the version from /opt/freeware/lib/libfontconfig.a
*/
libfontconfig = dlopen("libfontconfig.so", RTLD_LOCAL|RTLD_LAZY);
if (libfontconfig == NULL) {
libfontconfig = dlopen("/opt/freeware/lib/libfontconfig.a(libfontconfig.so.1)", RTLD_MEMBER|RTLD_LOCAL|RTLD_LAZY);
if (libfontconfig == NULL) {
return;
}
}
#else
/* 64 bit sparc should pick up the right version from the lib path.
* New features may be added to libfontconfig, this is expected to
* be compatible with old features, but we may need to start
* distinguishing the library version, to know whether to expect
* certain symbols - and functionality - to be available.
* Also add explicit search for .so.1 in case .so symlink doesn't exist.
*/
libfontconfig = dlopen(FONTCONFIG_DLL_VERSIONED, RTLD_LOCAL|RTLD_LAZY);
if (libfontconfig == NULL) {
libfontconfig = dlopen(FONTCONFIG_DLL, RTLD_LOCAL|RTLD_LAZY);
if (libfontconfig == NULL) {
return;
}
}
#endif
/* Version 1.0 of libfontconfig crashes if HOME isn't defined in
* the environment. This should generally never happen, but we can't
* control it, and can't control the version of fontconfig, so iff
* its not defined we set it to an empty value which is sufficient
* to prevent a crash. I considered unsetting it before exit, but
* it doesn't appear to work on Solaris, so I will leave it set.
*/
homeEnv = getenv("HOME");
if (homeEnv == NULL) {
putenv(homeEnvStr);
}
fcPatternBuild = (FcPatternBuildFuncType)dlsym(libfontconfig, "FcPatternBuild");
fcObjectSetBuild = (FcObjectSetFuncType)dlsym(libfontconfig, "FcObjectSetBuild");
fcFontList = (FcFontListFuncType)dlsym(libfontconfig, "FcFontList");
fcStrDirname = (FcStrDirnameFuncType)dlsym(libfontconfig, "FcStrDirname");
fcObjectSetDestroy = (FcObjectSetDestroyFuncType)dlsym(libfontconfig, "FcObjectSetDestroy");
fcPatternGetBool = (FcPatternGetBoolFuncType) dlsym(libfontconfig, "FcPatternGetBool");
fcPatternGetInteger = (FcPatternGetIntegerFuncType)dlsym(libfontconfig, "FcPatternGetInteger");
fcNameParse = (FcNameParseFuncType)dlsym(libfontconfig, "FcNameParse");
fcPatternAddString = (FcPatternAddStringFuncType)dlsym(libfontconfig, "FcPatternAddString");
fcPatternAddDouble = (FcPatternAddDoubleFuncType)dlsym(libfontconfig, "FcPatternAddDouble");
fcConfigSubstitute = (FcConfigSubstituteFuncType)dlsym(libfontconfig, "FcConfigSubstitute");
fcDefaultSubstitute = (FcDefaultSubstituteFuncType)dlsym(libfontconfig, "FcDefaultSubstitute");
fcFontMatch = (FcFontMatchFuncType)dlsym(libfontconfig, "FcFontMatch");
fcPatternGetString = (FcPatternGetStringFuncType)dlsym(libfontconfig, "FcPatternGetString");
fcPatternDestroy = (FcPatternDestroyFuncType)dlsym(libfontconfig, "FcPatternDestroy");
fcPatternGetCharSet = (FcPatternGetCharSetFuncType)dlsym(libfontconfig, "FcPatternGetCharSet");
fcFontSort = (FcFontSortFuncType)dlsym(libfontconfig, "FcFontSort");
fcFontSetDestroy = (FcFontSetDestroyFuncType)dlsym(libfontconfig, "FcFontSetDestroy");
fcCharSetUnion = (FcCharSetUnionFuncType)dlsym(libfontconfig, "FcCharSetUnion");
fcCharSetDestroy = (FcCharSetDestroyFuncType)dlsym(libfontconfig, "FcCharSetDestroy");
fcCharSetSubtractCount = (FcCharSetSubtractCountFuncType)dlsym(libfontconfig, "FcCharSetSubtractCount");
fcGetVersion = (FcGetVersionFuncType)dlsym(libfontconfig, "FcGetVersion");
fcConfigGetCacheDirs = (FcConfigGetCacheDirsFuncType)dlsym(libfontconfig, "FcConfigGetCacheDirs");
fcStrListNext = (FcStrListNextFuncType)dlsym(libfontconfig, "FcStrListNext");
fcStrListDone = (FcStrListDoneFuncType)dlsym(libfontconfig, "FcStrListDone");
fcPatternFormat = (FcPatternFormatFuncType)dlsym(libfontconfig, "FcPatternFormat");
fcStrFree = (FcStrFreeFuncType)dlsym(libfontconfig, "FcStrFree");
if (fcPatternBuild == NULL || fcObjectSetBuild == NULL || fcFontList == NULL || fcStrDirname == NULL ||
fcObjectSetDestroy == NULL || fcPatternGetBool == NULL || fcPatternGetInteger == NULL || fcNameParse == NULL ||
fcPatternAddString == NULL || fcConfigSubstitute == NULL || fcDefaultSubstitute == NULL || fcFontMatch == NULL ||
fcPatternGetString == NULL || fcPatternDestroy == NULL || fcPatternGetCharSet == NULL || fcFontSort == NULL ||
fcFontSetDestroy == NULL || fcCharSetUnion == NULL || fcCharSetDestroy == NULL || fcCharSetSubtractCount == NULL ||
fcGetVersion == NULL || fcConfigGetCacheDirs == NULL || fcStrListNext == NULL || fcStrListDone == NULL ||
fcPatternAddDouble == NULL || fcPatternFormat == NULL || fcStrFree == NULL) {
closeFontConfig();
}
}
static bool usingFontConfig() {
return (libfontconfig != NULL) ? true : false;
}
JNIEXPORT void JNICALL
JNI_OnUnload(JavaVM *vm, void *reserved) {
closeFontConfig();
}
/* These are copied from sun.awt.SunHints.
* Consider initialising them as ints using JNI for more robustness.
*/
#define TEXT_AA_OFF 1
#define TEXT_AA_ON 2
#define TEXT_AA_LCD_HRGB 4
#define TEXT_AA_LCD_HBGR 5
#define TEXT_AA_LCD_VRGB 6
#define TEXT_AA_LCD_VBGR 7
static void setRenderingFontHintsField(FcPattern* matchPattern, const char* property, int* value) {
if (FcResultMatch != (*fcPatternGetBool)(matchPattern, property, 0, value)) {
*value = -1;
}
}
JNIEXPORT int setupRenderingFontHints
(const char* fcName, const char* locale, double size, RenderingFontHints *renderingFontHints) {
FcPattern *pattern, *matchPattern;
FcResult result;
if (fcName == NULL) {
return -1;
}
pattern = (*fcNameParse)((FcChar8 *)fcName);
if (locale != NULL) {
(*fcPatternAddString)(pattern, FC_LANG, (unsigned char*)locale);
}
if (size != 0) {
(*fcPatternAddDouble)(pattern, FC_SIZE, size);
}
(*fcConfigSubstitute)(NULL, pattern, FcMatchPattern);
(*fcDefaultSubstitute)(pattern);
matchPattern = (*fcFontMatch)(NULL, pattern, &result);
/* Perhaps should call FcFontRenderPrepare() here as some pattern
* elements might change as a result of that call, but I'm not seeing
* any difference in testing.
*/
if (matchPattern) {
// Extract values from result
setRenderingFontHintsField(matchPattern, FC_HINTING, &renderingFontHints->fcHinting);
setRenderingFontHintsField(matchPattern, FC_HINT_STYLE, &renderingFontHints->fcHintStyle);
setRenderingFontHintsField(matchPattern, FC_ANTIALIAS, &renderingFontHints->fcAntialias);
setRenderingFontHintsField(matchPattern, FC_AUTOHINT, &renderingFontHints->fcAutohint);
setRenderingFontHintsField(matchPattern, FC_LCD_FILTER, &renderingFontHints->fcRGBA);
setRenderingFontHintsField(matchPattern, FC_RGBA, &renderingFontHints->fcLCDFilter);
(*fcPatternDestroy)(matchPattern);
}
(*fcPatternDestroy)(pattern);
return 0;
}
#endif
JNIEXPORT char **getFontConfigLocations() {
#ifdef DISABLE_FONTCONFIG
return NULL;
#else
if (usingFontConfig() == false) {
return NULL;
}
char **fontdirs;
int numdirs = 0;
FcPattern *pattern;
FcObjectSet *objset;
FcFontSet *fontSet;
int i, f, found;
/* Make calls into the fontconfig library to build a search for
* outline fonts, and to get the set of full file paths from the matches.
* This set is returned from the call to fcFontList(..)
* We allocate an array of char* pointers sufficient to hold all
* the matches + 1 extra which ensures there will be a NULL after all
* valid entries.
* We call fcStrDirname strip the file name from the path, and
* check if we have yet seen this directory. If not we add a pointer to
* it into our array of char*. Note that fcStrDirname returns newly
* allocated storage so we can use this in the return char** value.
* Finally we clean up, freeing allocated resources, and return the
* array of unique directories.
*/
pattern = (*fcPatternBuild)(NULL, FC_OUTLINE, FcTypeBool, FcTrue, NULL);
objset = (*fcObjectSetBuild)(FC_FILE, NULL);
fontSet = (*fcFontList)(NULL, pattern, objset);
if (fontSet == NULL) {
/* fcFontList() may return NULL if fonts are not installed. */
fontdirs = NULL;
} else {
fontdirs = (char**)calloc(fontSet->nfont+1, sizeof(char*));
if (fontdirs == NULL) {
(*fcFontSetDestroy)(fontSet);
goto cleanup;
}
for (f=0; f < fontSet->nfont; f++) {
FcChar8 *file;
FcChar8 *dir;
if ((*fcPatternGetString)(fontSet->fonts[f], FC_FILE, 0, &file) == FcResultMatch) {
dir = (*fcStrDirname)(file);
found = 0;
for (i=0;i<numdirs; i++) {
if (strcmp(fontdirs[i], (char*)dir) == 0) {
found = 1;
break;
}
}
if (!found) {
fontdirs[numdirs++] = (char*)dir;
} else {
free((char*)dir);
}
}
}
/* Free fontset if one was returned */
(*fcFontSetDestroy)(fontSet);
}
cleanup:
/* Free memory and close the ".so" */
(*fcObjectSetDestroy)(objset);
(*fcPatternDestroy)(pattern);
return fontdirs;
#endif
}
JNIEXPORT jint JNICALL
Java_sun_font_FontConfigManager_getFontConfigVersion
(JNIEnv *env, jclass obj) {
#ifdef DISABLE_FONTCONFIG
return 0;
#else
if (usingFontConfig() == false) {
return 0;
}
return (*fcGetVersion)();
#endif
}
JNIEXPORT void JNICALL
Java_sun_font_FontConfigManager_setupFontConfigFonts
(JNIEnv *env, jclass obj, jstring localeStr, jobject fcInfoObj,
jobjectArray fcCompFontArray, jboolean includeFallbacks) {
#ifdef DISABLE_FONTCONFIG
return;
#else
if (usingFontConfig() == false) {
return;
}
int i, arrlen;
jobject fcCompFontObj;
jstring fcNameStr, jstr;
const char *locale, *fcName;
FcPattern *pattern;
FcResult result;
jfieldID fcNameID, fcFirstFontID, fcAllFontsID, fcVersionID, fcCacheDirsID;
jfieldID familyNameID, styleNameID, fullNameID, fontFileID;
jmethodID fcFontCons;
char* debugMinGlyphsStr = getenv("J2D_DEBUG_MIN_GLYPHS");
jclass fcInfoClass;
jclass fcCompFontClass;
jclass fcFontClass;
CHECK_NULL(fcInfoObj);
CHECK_NULL(fcCompFontArray);
CHECK_NULL(fcInfoClass = (*env)->FindClass(env, "sun/font/FontConfigManager$FontConfigInfo"));
CHECK_NULL(fcCompFontClass = (*env)->FindClass(env, "sun/font/FontConfigManager$FcCompFont"));
CHECK_NULL(fcFontClass = (*env)->FindClass(env, "sun/font/FontConfigManager$FontConfigFont"));
CHECK_NULL(fcVersionID = (*env)->GetFieldID(env, fcInfoClass, "fcVersion", "I"));
CHECK_NULL(fcCacheDirsID = (*env)->GetFieldID(env, fcInfoClass, "cacheDirs", "[Ljava/lang/String;"));
CHECK_NULL(fcNameID = (*env)->GetFieldID(env, fcCompFontClass, "fcName", "Ljava/lang/String;"));
CHECK_NULL(fcFirstFontID = (*env)->GetFieldID(env, fcCompFontClass, "firstFont", "Lsun/font/FontConfigManager$FontConfigFont;"));
CHECK_NULL(fcAllFontsID = (*env)->GetFieldID(env, fcCompFontClass, "allFonts", "[Lsun/font/FontConfigManager$FontConfigFont;"));
CHECK_NULL(fcFontCons = (*env)->GetMethodID(env, fcFontClass, "<init>", "()V"));
CHECK_NULL(familyNameID = (*env)->GetFieldID(env, fcFontClass, "familyName", "Ljava/lang/String;"));
CHECK_NULL(styleNameID = (*env)->GetFieldID(env, fcFontClass, "styleStr", "Ljava/lang/String;"));
CHECK_NULL(fullNameID = (*env)->GetFieldID(env, fcFontClass, "fullName", "Ljava/lang/String;"));
CHECK_NULL(fontFileID = (*env)->GetFieldID(env, fcFontClass, "fontFile", "Ljava/lang/String;"));
(*env)->SetIntField(env, fcInfoObj, fcVersionID, (*fcGetVersion)());
/* Optionally get the cache dir locations. This isn't
* available until v 2.4.x, but this is OK since on those later versions
* we can check the time stamps on the cache dirs to see if we
* are out of date. There are a couple of assumptions here. First
* that the time stamp on the directory changes when the contents are
* updated. Secondly that the locations don't change. The latter is
* most likely if a new version of fontconfig is installed, but we also
* invalidate the cache if we detect that. Arguably even that is "rare",
* and most likely is tied to an OS upgrade which gets a new file anyway.
*/
if (fcStrListNext != NULL && fcStrListDone != NULL &&
fcConfigGetCacheDirs != NULL) {
FcStrList* cacheDirs;
FcChar8* cacheDir;
int cnt = 0;
jobject cacheDirArray = (*env)->GetObjectField(env, fcInfoObj, fcCacheDirsID);
int max = (*env)->GetArrayLength(env, cacheDirArray);
cacheDirs = (*fcConfigGetCacheDirs)(NULL);
if (cacheDirs != NULL) {
while ((cnt < max) && (cacheDir = (*fcStrListNext)(cacheDirs))) {
jstr = (*env)->NewStringUTF(env, (const char*)cacheDir);
if (IS_NULL(jstr)) {
(*fcStrListDone)(cacheDirs);
return;
}
(*env)->SetObjectArrayElement(env, cacheDirArray, cnt++, jstr);
(*env)->DeleteLocalRef(env, jstr);
}
(*fcStrListDone)(cacheDirs);
}
}
locale = (*env)->GetStringUTFChars(env, localeStr, 0);
if (locale == NULL) {
(*env)->ExceptionClear(env);
JNU_ThrowOutOfMemoryError(env, "Could not create locale");
return;
}
arrlen = (*env)->GetArrayLength(env, fcCompFontArray);
for (i=0; i<arrlen; i++) {
FcFontSet* fontset;
int fn, j, fontCount, nfonts;
unsigned int minGlyphs;
FcChar8 **family, **styleStr, **fullname, **file;
jarray fcFontArr = NULL;
FcCharSet *unionCharset = NULL;
FcCharSet *prevUnionCharset = NULL;
fcCompFontObj = (*env)->GetObjectArrayElement(env, fcCompFontArray, i);
fcNameStr =
(jstring)((*env)->GetObjectField(env, fcCompFontObj, fcNameID));
fcName = (*env)->GetStringUTFChars(env, fcNameStr, 0);
if (fcName == NULL) {
(*env)->DeleteLocalRef(env, fcCompFontObj);
(*env)->DeleteLocalRef(env, fcNameStr);
continue;
}
pattern = (*fcNameParse)((FcChar8 *)fcName);
(*env)->ReleaseStringUTFChars(env, fcNameStr, (const char*)fcName);
(*env)->DeleteLocalRef(env, fcNameStr);
if (pattern == NULL) {
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
return;
}
/* locale may not usually be necessary as fontconfig appears to apply
* this anyway based on the user's environment. However we want
* to use the value of the JDK startup locale so this should take
* care of it.
*/
if (locale != NULL) {
(*fcPatternAddString)(pattern, FC_LANG, (unsigned char*)locale);
}
(*fcConfigSubstitute)(NULL, pattern, FcMatchPattern);
(*fcDefaultSubstitute)(pattern);
fontset = (*fcFontSort)(NULL, pattern, FcTrue, NULL, &result);
if (fontset == NULL) {
(*fcPatternDestroy)(pattern);
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
return;
}
/* fontconfig returned us "nfonts". If we are just getting the
* first font, we set nfont to zero. Otherwise we use "nfonts".
* Next create separate C arrays of length nfonts for family file etc.
* Inspect the returned fonts and the ones we like (adds enough glyphs)
* are added to the arrays and we increment 'fontCount'.
*/
nfonts = fontset->nfont;
family = (FcChar8**)calloc(nfonts, sizeof(FcChar8*));
styleStr = (FcChar8**)calloc(nfonts, sizeof(FcChar8*));
fullname = (FcChar8**)calloc(nfonts, sizeof(FcChar8*));
file = (FcChar8**)calloc(nfonts, sizeof(FcChar8*));
if (family == NULL || styleStr == NULL ||
fullname == NULL || file == NULL) {
if (family != NULL) {
free(family);
}
if (styleStr != NULL) {
free(styleStr);
}
if (fullname != NULL) {
free(fullname);
}
if (file != NULL) {
free(file);
}
(*fcPatternDestroy)(pattern);
(*fcFontSetDestroy)(fontset);
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
return;
}
fontCount = 0;
minGlyphs = 20;
if (debugMinGlyphsStr != NULL) {
int val = minGlyphs;
sscanf(debugMinGlyphsStr, "%5d", &val);
if (val >= 0 && val <= 65536) {
minGlyphs = val;
}
}
for (j=0; j<nfonts; j++) {
FcPattern *fontPattern = fontset->fonts[j];
FcChar8 *fontformat;
FcCharSet *charset = NULL;
fontformat = NULL;
(*fcPatternGetString)(fontPattern, FC_FONTFORMAT, 0, &fontformat);
/* We only want TrueType fonts but some Linuxes still depend
* on Type 1 fonts for some Locale support, so we'll allow
* them there.
*/
if (fontformat != NULL
&& (strcmp((char*)fontformat, "TrueType") != 0)
#if defined(__linux__) || defined(_AIX)
&& (strcmp((char*)fontformat, "Type 1") != 0)
&& (strcmp((char*)fontformat, "CFF") != 0)
#endif
) {
continue;
}
result = (*fcPatternGetCharSet)(fontPattern, FC_CHARSET, 0, &charset);
if (result != FcResultMatch) {
free(family);
free(fullname);
free(styleStr);
free(file);
(*fcPatternDestroy)(pattern);
(*fcFontSetDestroy)(fontset);
if (prevUnionCharset != NULL) {
(*fcCharSetDestroy)(prevUnionCharset);
}
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
return;
}
/* We don't want 20 or 30 fonts, so once we hit 10 fonts,
* then require that they really be adding value. Too many
* adversely affects load time for minimal value-add.
* This is still likely far more than we've had in the past.
*/
if (j==10) {
minGlyphs = 50;
}
if (unionCharset == NULL) {
unionCharset = charset;
} else {
if ((*fcCharSetSubtractCount)(charset, unionCharset)
> minGlyphs) {
unionCharset = (* fcCharSetUnion)(unionCharset, charset);
if (prevUnionCharset != NULL) {
(*fcCharSetDestroy)(prevUnionCharset);
}
prevUnionCharset = unionCharset;
} else {
continue;
}
}
fontCount++; // found a font we will use.
(*fcPatternGetString)(fontPattern, FC_FILE, 0, &file[j]);
(*fcPatternGetString)(fontPattern, FC_FAMILY, 0, &family[j]);
(*fcPatternGetString)(fontPattern, FC_STYLE, 0, &styleStr[j]);
(*fcPatternGetString)(fontPattern, FC_FULLNAME, 0, &fullname[j]);
if (!includeFallbacks) {
break;
}
if (fontCount == 254) {
break; // CompositeFont will only use up to 254 slots from here.
}
}
// Release last instance of CharSet union
if (prevUnionCharset != NULL) {
(*fcCharSetDestroy)(prevUnionCharset);
}
/* Once we get here 'fontCount' is the number of returned fonts
* we actually want to use, so we create 'fcFontArr' of that length.
* The non-null entries of "family[]" etc are those fonts.
* Then loop again over all nfonts adding just those non-null ones
* to 'fcFontArr'. If its null (we didn't want the font)
* then we don't enter the main body.
* So we should never get more than 'fontCount' entries.
*/
if (includeFallbacks) {
fcFontArr =
(*env)->NewObjectArray(env, fontCount, fcFontClass, NULL);
if (IS_NULL(fcFontArr)) {
free(family);
free(fullname);
free(styleStr);
free(file);
(*fcPatternDestroy)(pattern);
(*fcFontSetDestroy)(fontset);
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
return;
}
(*env)->SetObjectField(env,fcCompFontObj, fcAllFontsID, fcFontArr);
}
fn=0;
for (j=0;j<nfonts;j++) {
if (family[j] != NULL) {
jobject fcFont = (*env)->NewObject(env, fcFontClass, fcFontCons);
if (IS_NULL(fcFont)) break;
jstr = (*env)->NewStringUTF(env, (const char*)family[j]);
if (IS_NULL(jstr)) break;
(*env)->SetObjectField(env, fcFont, familyNameID, jstr);
(*env)->DeleteLocalRef(env, jstr);
if (file[j] != NULL) {
jstr = (*env)->NewStringUTF(env, (const char*)file[j]);
if (IS_NULL(jstr)) break;
(*env)->SetObjectField(env, fcFont, fontFileID, jstr);
(*env)->DeleteLocalRef(env, jstr);
}
if (styleStr[j] != NULL) {
jstr = (*env)->NewStringUTF(env, (const char*)styleStr[j]);
if (IS_NULL(jstr)) break;
(*env)->SetObjectField(env, fcFont, styleNameID, jstr);
(*env)->DeleteLocalRef(env, jstr);
}
if (fullname[j] != NULL) {
jstr = (*env)->NewStringUTF(env, (const char*)fullname[j]);
if (IS_NULL(jstr)) break;
(*env)->SetObjectField(env, fcFont, fullNameID, jstr);
(*env)->DeleteLocalRef(env, jstr);
}
if (fn==0) {
(*env)->SetObjectField(env, fcCompFontObj,
fcFirstFontID, fcFont);
}
if (includeFallbacks) {
(*env)->SetObjectArrayElement(env, fcFontArr, fn++,fcFont);
} else {
(*env)->DeleteLocalRef(env, fcFont);
break;
}
(*env)->DeleteLocalRef(env, fcFont);
}
}
if (includeFallbacks) {
(*env)->DeleteLocalRef(env, fcFontArr);
}
(*env)->DeleteLocalRef(env, fcCompFontObj);
(*fcFontSetDestroy)(fontset);
(*fcPatternDestroy)(pattern);
free(family);
free(styleStr);
free(fullname);
free(file);
}
/* release resources and close the ".so" */
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
#endif
}
JNIEXPORT jint JNICALL
Java_sun_font_FontConfigManager_getFontConfigAASettings
(JNIEnv *env, jclass obj, jstring fcNameStr, jstring localeStr) {
#ifdef DISABLE_FONTCONFIG
return -1;
#else
if (usingFontConfig() == false) {
return -1;
}
int rgba = 0;
const char *locale=NULL, *fcName=NULL;
if (fcNameStr == NULL || localeStr == NULL) {
return -1;
}
fcName = (*env)->GetStringUTFChars(env, fcNameStr, 0);
locale = (*env)->GetStringUTFChars(env, localeStr, 0);
int status = 0;
RenderingFontHints renderingFontHints;
if (fcName && locale) {
status = setupRenderingFontHints(fcName, locale, 0, &renderingFontHints);
} else {
status = -1;
}
if (locale) {
(*env)->ReleaseStringUTFChars(env, localeStr, (const char*)locale);
}
if (fcName) {
(*env)->ReleaseStringUTFChars(env, fcNameStr, (const char*)fcName);
}
if (status) {
return status;
}
if (renderingFontHints.fcAntialias == FcFalse) {
return TEXT_AA_OFF;
} else if (renderingFontHints.fcRGBA <= FC_RGBA_UNKNOWN || renderingFontHints.fcRGBA >= FC_RGBA_NONE) {
return TEXT_AA_ON;
} else {
switch (renderingFontHints.fcRGBA) {
case FC_RGBA_RGB : return TEXT_AA_LCD_HRGB;
case FC_RGBA_BGR : return TEXT_AA_LCD_HBGR;
case FC_RGBA_VRGB : return TEXT_AA_LCD_VRGB;
case FC_RGBA_VBGR : return TEXT_AA_LCD_VBGR;
default : return TEXT_AA_LCD_HRGB; // should not get here.
}
}
#endif
}
JNIEXPORT jstring JNICALL
Java_sun_font_FontConfigManager_getFontProperty
(JNIEnv *env, jclass obj, jstring query, jstring property) {
#ifdef DISABLE_FONTCONFIG
return NULL;
#else
if (usingFontConfig() == false) {
return NULL;
}
const char *queryPtr = NULL;
const char *propertyPtr = NULL;
FcChar8 *fontFamily = NULL;
FcChar8 *fontPath = NULL;
jstring res = NULL;
queryPtr = (*env)->GetStringUTFChars(env, query, 0);
propertyPtr = (*env)->GetStringUTFChars(env, property, 0);
if (queryPtr == NULL || propertyPtr == NULL) {
goto cleanup;
}
FcPattern *pattern = (*fcNameParse)((FcChar8 *) queryPtr);
if (pattern == NULL) {
goto cleanup;
}
(*fcConfigSubstitute)(NULL, pattern, FcMatchScan);
(*fcDefaultSubstitute)(pattern);
FcResult fcResult;
FcPattern *match = (*fcFontMatch)(0, pattern, &fcResult);
if (match == NULL || fcResult != FcResultMatch) {
goto cleanup;
}
fontFamily = (*fcPatternFormat)(match, (FcChar8*) "%{family}");
if (fontFamily == NULL) {
goto cleanup;
}
// result of foundFontName could be set of families, so we left only first family
char *commaPos = strchr((char *) fontFamily, ',');
if (commaPos != NULL) {
*commaPos = '\0';
}
if (strstr(queryPtr, (char *) fontFamily) == NULL) {
goto cleanup;
}
fontPath = (*fcPatternFormat)(match, (FcChar8*) propertyPtr);
if (fontPath == NULL) {
goto cleanup;
}
res = (*env)->NewStringUTF(env, (char *) fontPath);
cleanup:
if (fontPath) {
(*fcStrFree)(fontPath);
}
if (fontFamily) {
(*fcStrFree)(fontFamily);
}
if (propertyPtr) {
(*env)->ReleaseStringUTFChars(env, property, (const char*)propertyPtr);
}
if (queryPtr) {
(*env)->ReleaseStringUTFChars(env, query, (const char*)queryPtr);
}
return res;
#endif
}

View File

@@ -31,6 +31,8 @@
#include <Trace.h>
#include <jni_util.h>
#include <java_awt_event_KeyEvent.h>
#include <java_awt_event_InputEvent.h>
#include "sun_awt_wl_WLRobotPeer.h"
#include "WLToolkit.h"
@@ -116,6 +118,158 @@ init_mutex_and_cond(JNIEnv *env, pthread_mutex_t *mutex, pthread_cond_t *cond)
static void
handle_wakefield_error(JNIEnv *env, uint32_t error_code);
struct wayland_keycode_map_item {
int java_key_code;
int wayland_key_code;
};
// Key codes correspond to the Linux event codes:
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h
static struct wayland_keycode_map_item wayland_keycode_map[] = {
{ java_awt_event_KeyEvent_VK_ESCAPE, 1 },
{ java_awt_event_KeyEvent_VK_1, 2 },
{ java_awt_event_KeyEvent_VK_2, 3 },
{ java_awt_event_KeyEvent_VK_3, 4 },
{ java_awt_event_KeyEvent_VK_4, 5 },
{ java_awt_event_KeyEvent_VK_5, 6 },
{ java_awt_event_KeyEvent_VK_6, 7 },
{ java_awt_event_KeyEvent_VK_7, 8 },
{ java_awt_event_KeyEvent_VK_8, 9 },
{ java_awt_event_KeyEvent_VK_9, 10 },
{ java_awt_event_KeyEvent_VK_0, 11 },
{ java_awt_event_KeyEvent_VK_MINUS, 12 },
{ java_awt_event_KeyEvent_VK_EQUALS, 13 },
{ java_awt_event_KeyEvent_VK_BACK_SPACE, 14 },
{ java_awt_event_KeyEvent_VK_TAB, 15 },
{ java_awt_event_KeyEvent_VK_Q, 16 },
{ java_awt_event_KeyEvent_VK_W, 17 },
{ java_awt_event_KeyEvent_VK_E, 18 },
{ java_awt_event_KeyEvent_VK_R, 19 },
{ java_awt_event_KeyEvent_VK_T, 20 },
{ java_awt_event_KeyEvent_VK_Y, 21 },
{ java_awt_event_KeyEvent_VK_U, 22 },
{ java_awt_event_KeyEvent_VK_I, 23 },
{ java_awt_event_KeyEvent_VK_O, 24 },
{ java_awt_event_KeyEvent_VK_P, 25 },
{ java_awt_event_KeyEvent_VK_OPEN_BRACKET, 26 },
{ java_awt_event_KeyEvent_VK_CLOSE_BRACKET, 27 },
{ java_awt_event_KeyEvent_VK_ENTER, 28 },
{ java_awt_event_KeyEvent_VK_CONTROL, 29 },
{ java_awt_event_KeyEvent_VK_A, 30 },
{ java_awt_event_KeyEvent_VK_S, 31 },
{ java_awt_event_KeyEvent_VK_D, 32 },
{ java_awt_event_KeyEvent_VK_F, 33 },
{ java_awt_event_KeyEvent_VK_G, 34 },
{ java_awt_event_KeyEvent_VK_H, 35 },
{ java_awt_event_KeyEvent_VK_J, 36 },
{ java_awt_event_KeyEvent_VK_K, 37 },
{ java_awt_event_KeyEvent_VK_L, 38 },
{ java_awt_event_KeyEvent_VK_SEMICOLON, 39 },
{ java_awt_event_KeyEvent_VK_QUOTE, 40 },
{ java_awt_event_KeyEvent_VK_BACK_QUOTE, 41 },
{ java_awt_event_KeyEvent_VK_SHIFT, 42 },
{ java_awt_event_KeyEvent_VK_BACK_SLASH, 43 },
{ java_awt_event_KeyEvent_VK_Z, 44 },
{ java_awt_event_KeyEvent_VK_X, 45 },
{ java_awt_event_KeyEvent_VK_C, 46 },
{ java_awt_event_KeyEvent_VK_V, 47 },
{ java_awt_event_KeyEvent_VK_B, 48 },
{ java_awt_event_KeyEvent_VK_N, 49 },
{ java_awt_event_KeyEvent_VK_M, 50 },
{ java_awt_event_KeyEvent_VK_COMMA, 51 },
{ java_awt_event_KeyEvent_VK_PERIOD, 52 },
{ java_awt_event_KeyEvent_VK_SLASH, 53 },
{ java_awt_event_KeyEvent_VK_MULTIPLY, 55 },
{ java_awt_event_KeyEvent_VK_ALT, 56 },
{ java_awt_event_KeyEvent_VK_SPACE, 57 },
{ java_awt_event_KeyEvent_VK_CAPS_LOCK, 58 },
{ java_awt_event_KeyEvent_VK_F1, 59 },
{ java_awt_event_KeyEvent_VK_F2, 60 },
{ java_awt_event_KeyEvent_VK_F3, 61 },
{ java_awt_event_KeyEvent_VK_F4, 62 },
{ java_awt_event_KeyEvent_VK_F5, 63 },
{ java_awt_event_KeyEvent_VK_F6, 64 },
{ java_awt_event_KeyEvent_VK_F7, 65 },
{ java_awt_event_KeyEvent_VK_F8, 66 },
{ java_awt_event_KeyEvent_VK_F9, 67 },
{ java_awt_event_KeyEvent_VK_F10, 68 },
{ java_awt_event_KeyEvent_VK_NUM_LOCK, 69 },
{ java_awt_event_KeyEvent_VK_SCROLL_LOCK, 70 },
{ java_awt_event_KeyEvent_VK_NUMPAD7, 71 },
{ java_awt_event_KeyEvent_VK_KP_UP, 72 },
{ java_awt_event_KeyEvent_VK_NUMPAD8, 72 },
{ java_awt_event_KeyEvent_VK_NUMPAD9, 73 },
{ java_awt_event_KeyEvent_VK_SUBTRACT, 74 },
{ java_awt_event_KeyEvent_VK_KP_LEFT, 75 },
{ java_awt_event_KeyEvent_VK_NUMPAD4, 75 },
{ java_awt_event_KeyEvent_VK_NUMPAD5, 76 },
{ java_awt_event_KeyEvent_VK_KP_RIGHT, 77 },
{ java_awt_event_KeyEvent_VK_NUMPAD6, 77 },
{ java_awt_event_KeyEvent_VK_ADD, 78 },
{ java_awt_event_KeyEvent_VK_NUMPAD1, 79 },
{ java_awt_event_KeyEvent_VK_KP_DOWN, 80 },
{ java_awt_event_KeyEvent_VK_NUMPAD2, 80 },
{ java_awt_event_KeyEvent_VK_NUMPAD3, 81 },
{ java_awt_event_KeyEvent_VK_NUMPAD0, 82 },
{ java_awt_event_KeyEvent_VK_DECIMAL, 83 },
{ java_awt_event_KeyEvent_VK_LESS, 86 },
{ java_awt_event_KeyEvent_VK_F11, 87 },
{ java_awt_event_KeyEvent_VK_F12, 88 },
{ java_awt_event_KeyEvent_VK_KATAKANA, 90 },
{ java_awt_event_KeyEvent_VK_HIRAGANA, 91 },
{ java_awt_event_KeyEvent_VK_INPUT_METHOD_ON_OFF, 92 },
{ java_awt_event_KeyEvent_VK_NONCONVERT, 94 },
{ java_awt_event_KeyEvent_VK_DIVIDE, 98 },
{ java_awt_event_KeyEvent_VK_PRINTSCREEN, 99 },
{ java_awt_event_KeyEvent_VK_ALT_GRAPH, 100 },
{ java_awt_event_KeyEvent_VK_HOME, 102 },
{ java_awt_event_KeyEvent_VK_UP, 103 },
{ java_awt_event_KeyEvent_VK_PAGE_UP, 104 },
{ java_awt_event_KeyEvent_VK_LEFT, 105 },
{ java_awt_event_KeyEvent_VK_RIGHT, 106 },
{ java_awt_event_KeyEvent_VK_END, 107 },
{ java_awt_event_KeyEvent_VK_DOWN, 108 },
{ java_awt_event_KeyEvent_VK_PAGE_DOWN, 109 },
{ java_awt_event_KeyEvent_VK_INSERT, 110 },
{ java_awt_event_KeyEvent_VK_DELETE, 111 },
{ java_awt_event_KeyEvent_VK_PAUSE, 119 },
{ java_awt_event_KeyEvent_VK_DECIMAL, 121 },
{ java_awt_event_KeyEvent_VK_META, 125 },
{ java_awt_event_KeyEvent_VK_WINDOWS, 125 },
{ java_awt_event_KeyEvent_VK_STOP, 128 },
{ java_awt_event_KeyEvent_VK_AGAIN, 129 },
{ java_awt_event_KeyEvent_VK_UNDO, 131 },
{ java_awt_event_KeyEvent_VK_FIND, 136 },
{ java_awt_event_KeyEvent_VK_HELP, 138 },
{ java_awt_event_KeyEvent_VK_LEFT_PARENTHESIS, 179 },
{ java_awt_event_KeyEvent_VK_RIGHT_PARENTHESIS, 180 },
{ java_awt_event_KeyEvent_VK_F13, 183 },
{ java_awt_event_KeyEvent_VK_F14, 184 },
{ java_awt_event_KeyEvent_VK_F15, 185 },
{ java_awt_event_KeyEvent_VK_F16, 186 },
{ java_awt_event_KeyEvent_VK_F17, 187 },
{ java_awt_event_KeyEvent_VK_F18, 188 },
{ java_awt_event_KeyEvent_VK_F19, 189 },
{ java_awt_event_KeyEvent_VK_F20, 190 },
{ java_awt_event_KeyEvent_VK_F21, 191 },
{ java_awt_event_KeyEvent_VK_F22, 192 },
{ java_awt_event_KeyEvent_VK_F23, 193 },
{ java_awt_event_KeyEvent_VK_F24, 194 },
{ java_awt_event_KeyEvent_VK_UNDEFINED, -1 }
};
struct wayland_button_map_item {
int java_button_mask;
int wayland_button_code;
};
static struct wayland_button_map_item wayland_button_map[] = {
{ java_awt_event_InputEvent_BUTTON1_DOWN_MASK | java_awt_event_InputEvent_BUTTON1_MASK, 0x110 },
{ java_awt_event_InputEvent_BUTTON2_DOWN_MASK | java_awt_event_InputEvent_BUTTON2_MASK, 0x112 },
{ java_awt_event_InputEvent_BUTTON3_DOWN_MASK | java_awt_event_InputEvent_BUTTON3_MASK, 0x111 },
{ -1, -1 },
};
#endif
static jclass pointClass; // java.awt.Point
@@ -177,7 +331,7 @@ Java_sun_awt_wl_WLRobotPeer_getRGBPixelImpl(JNIEnv *env, jclass clazz, jint x, j
WAKEFIELD_REQUEST_INIT(pixel_color_request);
wakefield_get_pixel_color(wakefield, x, y);
wl_flush_to_server(env); // the event will be delivered on a dedicated thread, see wakefield_pixel_color()
wlFlushToServer(env); // the event will be delivered on a dedicated thread, see wakefield_pixel_color()
WAKEFIELD_REQUEST_WAIT_START(pixel_color_request);
const uint32_t error_code = pixel_color_request.error_code;
@@ -208,7 +362,7 @@ Java_sun_awt_wl_WLRobotPeer_getLocationOfWLSurfaceImpl
struct wl_surface * const surface = (struct wl_surface*) wlSurfacePtr;
wakefield_get_surface_location(wakefield, surface);
wl_flush_to_server(env); // the event will be delivered on a dedicated thread, see wakefield_surface_location()
wlFlushToServer(env); // the event will be delivered on a dedicated thread, see wakefield_surface_location()
WAKEFIELD_REQUEST_WAIT_START(surface_location_request);
const uint32_t error_code = surface_location_request.error_code;
@@ -243,7 +397,7 @@ Java_sun_awt_wl_WLRobotPeer_setLocationOfWLSurfaceImpl
struct wl_surface * const surface = (struct wl_surface*) wlSurfacePtr;
wakefield_move_surface(wakefield, surface, x, y);
wl_surface_commit(surface);
wl_flush_to_server(env);
wlFlushToServer(env);
#endif
}
@@ -298,6 +452,79 @@ Java_sun_awt_wl_WLRobotPeer_getRGBPixelsImpl
#endif
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_WLRobotPeer_sendJavaKeyImpl
(JNIEnv *env, jclass clazz, jint javaKeyCode, jboolean pressed)
{
#ifdef WAKEFIELD_ROBOT
if (!wakefield) {
JNU_ThrowByName(env, "java/awt/AWTError", "no 'wakefield' protocol extension");
return;
}
uint32_t key = 0;
for (struct wayland_keycode_map_item* item = wayland_keycode_map; item->wayland_key_code != -1; ++item) {
if (item->java_key_code == javaKeyCode) {
key = item->wayland_key_code;
break;
}
}
if (!key) {
return;
}
wakefield_send_key(wakefield, key, pressed ? 1 : 0);
#endif
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_WLRobotPeer_mouseMoveImpl
(JNIEnv *env, jclass clazz, jint x, jint y)
{
#ifdef WAKEFIELD_ROBOT
if (!wakefield) {
JNU_ThrowByName(env, "java/awt/AWTError", "no 'wakefield' protocol extension");
return;
}
wakefield_send_cursor(wakefield, x, y);
#endif
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_WLRobotPeer_sendMouseButtonImpl
(JNIEnv *env, jclass clazz, jint buttons, jboolean pressed)
{
#ifdef WAKEFIELD_ROBOT
if (!wakefield) {
JNU_ThrowByName(env, "java/awt/AWTError", "no 'wakefield' protocol extension");
return;
}
for (struct wayland_button_map_item* item = wayland_button_map; item->wayland_button_code != -1; ++item) {
if (item->java_button_mask & buttons) {
wakefield_send_button(wakefield, item->wayland_button_code, pressed ? 1 : 0);
}
}
#endif
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_WLRobotPeer_mouseWheelImpl
(JNIEnv *env, jclass clazz, jint amount)
{
#ifdef WAKEFIELD_ROBOT
if (!wakefield) {
JNU_ThrowByName(env, "java/awt/AWTError", "no 'wakefield' protocol extension");
return;
}
wakefield_send_wheel(wakefield, amount);
#endif
}
#ifdef WAKEFIELD_ROBOT
static void wakefield_surface_location(void *data, struct wakefield *wakefield,

View File

@@ -356,13 +356,13 @@ wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
}
static inline void
reset_pointer_event(struct pointer_event_cumulative *e)
resetPointerEvent(struct pointer_event_cumulative *e)
{
memset(e, 0, sizeof(struct pointer_event_cumulative));
}
static void
fill_java_pointer_event(JNIEnv* env, jobject pointerEventRef)
fillJavaPointerEvent(JNIEnv* env, jobject pointerEventRef)
{
(*env)->SetBooleanField(env, pointerEventRef, hasEnterEventFID, pointer_event.has_enter_event);
(*env)->SetBooleanField(env, pointerEventRef, hasLeaveEventFID, pointer_event.has_leave_event);
@@ -396,14 +396,14 @@ wl_pointer_frame(void *data, struct wl_pointer *wl_pointer)
pointerEventFactoryMID);
JNU_CHECK_EXCEPTION(env);
fill_java_pointer_event(env, pointerEventRef);
fillJavaPointerEvent(env, pointerEventRef);
(*env)->CallStaticVoidMethod(env,
tkClass,
dispatchPointerEventMID,
pointerEventRef);
JNU_CHECK_EXCEPTION(env);
reset_pointer_event(&pointer_event);
resetPointerEvent(&pointer_event);
}
static const struct wl_pointer_listener wl_pointer_listener = {
@@ -546,6 +546,16 @@ wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
XKB_MOD_NAME_LOGO,
XKB_STATE_MODS_EFFECTIVE);
const bool is_caps_active
= xkb_ifs.xkb_state_mod_name_is_active(xkb_state,
XKB_MOD_NAME_CAPS,
XKB_STATE_MODS_EFFECTIVE);
const bool is_num_active
= xkb_ifs.xkb_state_mod_name_is_active(xkb_state,
XKB_MOD_NAME_NUM,
XKB_STATE_MODS_EFFECTIVE);
(*env)->CallStaticVoidMethod(env,
tkClass,
dispatchKeyboardModifiersEventMID,
@@ -553,7 +563,9 @@ wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
is_shift_active,
is_alt_active,
is_ctrl_active,
is_meta_active);
is_meta_active,
is_caps_active,
is_num_active);
JNU_CHECK_EXCEPTION(env);
}
@@ -776,7 +788,7 @@ initJavaRefs(JNIEnv *env, jclass clazz)
JNI_FALSE);
CHECK_NULL_RETURN(dispatchKeyboardModifiersEventMID = (*env)->GetStaticMethodID(env, tkClass,
"dispatchKeyboardModifiersEvent",
"(JZZZZ)V"),
"(JZZZZZZ)V"),
JNI_FALSE);
CHECK_NULL_RETURN(keyRepeatRateFID = (*env)->GetStaticFieldID(env, tkClass,
@@ -854,7 +866,7 @@ initCursors() {
}
static void
finalize_init(JNIEnv *env) {
finalizeInit(JNIEnv *env) {
// NB: we are NOT on EDT here so shouldn't dispatch EDT-sensitive stuff
while (num_of_outstanding_sync > 0) {
// There are outstanding events that carry information essential for the toolkit
@@ -902,7 +914,7 @@ Java_sun_awt_wl_WLToolkit_initIDs
initCursors();
finalize_init(env);
finalizeInit(env);
}
JNIEXPORT void JNICALL
@@ -915,8 +927,12 @@ Java_sun_awt_wl_WLToolkit_dispatchEventsOnEDT
wl_display_dispatch_pending(wl_display);
}
/**
* Waits for poll_timeout ms for an event on the Wayland server socket.
* Returns -1 in case of error and 'revents' (see poll(2)) otherwise.
*/
static int
wl_display_poll(struct wl_display *display, int events, int poll_timeout)
wlDisplayPoll(struct wl_display *display, int events, int poll_timeout)
{
int rc = 0;
struct pollfd pfd[1] = { {.fd = wl_display_get_fd(display), .events = events} };
@@ -924,11 +940,12 @@ wl_display_poll(struct wl_display *display, int events, int poll_timeout)
errno = 0;
rc = poll(pfd, 1, poll_timeout);
} while (rc == -1 && errno == EINTR);
return rc;
return rc == -1 ? -1 : (pfd[0].revents & 0xffff);
}
int
wl_flush_to_server(JNIEnv *env)
wlFlushToServer(JNIEnv *env)
{
int rc = 0;
@@ -939,7 +956,7 @@ wl_flush_to_server(JNIEnv *env)
break;
}
rc = wl_display_poll(wl_display, POLLOUT, -1);
rc = wlDisplayPoll(wl_display, POLLOUT, -1);
if (rc == -1) {
JNU_ThrowByName(env, "java/awt/AWTError", "Wayland display error polling out to the server");
return sun_awt_wl_WLToolkit_READ_RESULT_ERROR;
@@ -958,7 +975,7 @@ JNIEXPORT void JNICALL
Java_sun_awt_wl_WLToolkit_flushImpl
(JNIEnv *env, jobject obj)
{
(void) wl_flush_to_server(env);
(void) wlFlushToServer(env);
}
JNIEXPORT void JNICALL
@@ -974,16 +991,7 @@ Java_sun_awt_wl_WLToolkit_dispatchNonDefaultQueuesImpl
while (rc >= 0) {
// Dispatch pending events on the wakefield queue
while (wl_display_prepare_read_queue(wl_display, robot_queue) != 0 && rc >= 0) {
rc = wl_display_dispatch_queue_pending(wl_display, robot_queue);
}
if (rc < 0) {
wl_display_cancel_read(wl_display);
break;
}
// Wait for new events, wl_display_read_events is a synchronization point between all threads reading events.
rc = wl_display_read_events(wl_display);
rc = wl_display_dispatch_queue(wl_display, robot_queue);
}
// Simply return in case of any error; the actual error reporting (exception)
@@ -1007,7 +1015,7 @@ Java_sun_awt_wl_WLToolkit_readEvents
return sun_awt_wl_WLToolkit_READ_RESULT_FINISHED_WITH_EVENTS;
}
rc = wl_flush_to_server(env);
rc = wlFlushToServer(env);
if (rc != 0) {
wl_display_cancel_read(wl_display);
return sun_awt_wl_WLToolkit_READ_RESULT_ERROR;
@@ -1016,7 +1024,7 @@ Java_sun_awt_wl_WLToolkit_readEvents
// Wait for new data *from* the server.
// Specify some timeout because otherwise 'flush' above that sends data
// to the server will have to wait too long.
rc = wl_display_poll(wl_display, POLLIN,
rc = wlDisplayPoll(wl_display, POLLIN,
sun_awt_wl_WLToolkit_WAYLAND_DISPLAY_INTERACTION_TIMEOUT_MS);
if (rc == -1) {
wl_display_cancel_read(wl_display);
@@ -1024,7 +1032,14 @@ Java_sun_awt_wl_WLToolkit_readEvents
return sun_awt_wl_WLToolkit_READ_RESULT_ERROR;
}
// Transform the data read by the above call into events on the corresponding queues of the display.
const bool hasMoreData = (rc & POLLIN);
if (!hasMoreData) {
wl_display_cancel_read(wl_display);
return sun_awt_wl_WLToolkit_READ_RESULT_FINISHED_NO_EVENTS;
}
// Read new data from Wayland and transform them into events
// on the corresponding queues of the display.
rc = wl_display_read_events(wl_display);
if (rc == -1) { // display disconnect has likely happened
return sun_awt_wl_WLToolkit_READ_RESULT_ERROR;
@@ -1216,7 +1231,7 @@ Java_sun_awt_SunToolkit_closeSplashScreen(JNIEnv *env, jclass cls)
void awt_output_flush()
{
wl_flush_to_server(getEnv());
wlFlushToServer(getEnv());
}
static void

View File

@@ -55,6 +55,6 @@ extern uint32_t last_pointer_enter_serial;
JNIEnv *getEnv();
int wl_flush_to_server(JNIEnv* env);
int wlFlushToServer(JNIEnv* env);
struct wl_shm_pool *CreateShmPool(size_t size, const char *name, void **data);

View File

@@ -28,6 +28,16 @@
#include <stdint.h>
#include "wayland-util.h"
#ifndef __has_attribute
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
#endif
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
#else
#define WL_PRIVATE
#endif
extern const struct wl_interface wl_buffer_interface;
extern const struct wl_interface wl_surface_interface;
@@ -56,6 +66,10 @@ static const struct wl_message wakefield_requests[] = {
{ "move_surface", "oii", wakefield_types + 4 },
{ "get_surface_location", "o", wakefield_types + 7 },
{ "get_pixel_color", "ii", wakefield_types + 0 },
{ "send_key", "uu", wakefield_types + 0 },
{ "send_cursor", "ii", wakefield_types + 0 },
{ "send_button", "uu", wakefield_types + 0 },
{ "send_wheel", "i", wakefield_types + 0 },
{ "capture_create", "oii", wakefield_types + 8 },
};
@@ -65,9 +79,9 @@ static const struct wl_message wakefield_events[] = {
{ "capture_ready", "ou", wakefield_types + 15 },
};
WL_EXPORT const struct wl_interface wakefield_interface = {
WL_PRIVATE const struct wl_interface wakefield_interface = {
"wakefield", 1,
5, wakefield_requests,
9, wakefield_requests,
3, wakefield_events,
};

View File

@@ -142,7 +142,11 @@ wakefield_add_listener(struct wakefield *wakefield,
#define WAKEFIELD_MOVE_SURFACE 1
#define WAKEFIELD_GET_SURFACE_LOCATION 2
#define WAKEFIELD_GET_PIXEL_COLOR 3
#define WAKEFIELD_CAPTURE_CREATE 4
#define WAKEFIELD_SEND_KEY 4
#define WAKEFIELD_SEND_CURSOR 5
#define WAKEFIELD_SEND_BUTTON 6
#define WAKEFIELD_SEND_WHEEL 7
#define WAKEFIELD_CAPTURE_CREATE 8
/**
* @ingroup iface_wakefield
@@ -173,6 +177,22 @@ wakefield_add_listener(struct wakefield *wakefield,
* @ingroup iface_wakefield
*/
#define WAKEFIELD_GET_PIXEL_COLOR_SINCE_VERSION 1
/**
* @ingroup iface_wakefield
*/
#define WAKEFIELD_SEND_KEY_SINCE_VERSION 1
/**
* @ingroup iface_wakefield
*/
#define WAKEFIELD_SEND_CURSOR_SINCE_VERSION 1
/**
* @ingroup iface_wakefield
*/
#define WAKEFIELD_SEND_BUTTON_SINCE_VERSION 1
/**
* @ingroup iface_wakefield
*/
#define WAKEFIELD_SEND_WHEEL_SINCE_VERSION 1
/**
* @ingroup iface_wakefield
*/
@@ -249,6 +269,54 @@ wakefield_get_pixel_color(struct wakefield *wakefield, int32_t x, int32_t y)
WAKEFIELD_GET_PIXEL_COLOR, x, y);
}
/**
* @ingroup iface_wakefield
*
* This requests an emulation of a key press by its Linux event key code.
*/
static inline void
wakefield_send_key(struct wakefield *wakefield, uint32_t key, uint32_t state)
{
wl_proxy_marshal((struct wl_proxy *) wakefield,
WAKEFIELD_SEND_KEY, key, state);
}
/**
* @ingroup iface_wakefield
*
* This requests an emulation of the mouse cursor being moved to the specified screen coordinates.
*/
static inline void
wakefield_send_cursor(struct wakefield *wakefield, int32_t x, int32_t y)
{
wl_proxy_marshal((struct wl_proxy *) wakefield,
WAKEFIELD_SEND_CURSOR, x, y);
}
/**
* @ingroup iface_wakefield
*
* This requests an emulation of a mouse button press by its Linux event code.
*/
static inline void
wakefield_send_button(struct wakefield *wakefield, uint32_t button, uint32_t state)
{
wl_proxy_marshal((struct wl_proxy *) wakefield,
WAKEFIELD_SEND_BUTTON, button, state);
}
/**
* @ingroup iface_wakefield
*
* This requests an emulation of a rotation of a mouse scroll wheel.
*/
static inline void
wakefield_send_wheel(struct wakefield *wakefield, int32_t amount)
{
wl_proxy_marshal((struct wl_proxy *) wakefield,
WAKEFIELD_SEND_WHEEL, amount);
}
/**
* @ingroup iface_wakefield
*/

View File

@@ -33,7 +33,9 @@ public interface WindowMove {
* The intended use is to facilitate the implementation of window management similar to the way
* it is done natively on the platform.
*
* Preconditions for calling this method:
* The service is implemented both for X11 and Wayland toolkits.
*
* Preconditions for calling this method with the X11 toolkit:
* <ul>
* <li>WM supports _NET_WM_MOVE_RESIZE (this is checked automatically when an implementation
* of this interface is obtained).</li>
@@ -41,13 +43,20 @@ public interface WindowMove {
* <li>The mouse button specified by {@code mouseButton} is pressed.</li>
* </ul>
*
* Preconditions for calling this method with the Wayland toolkit:
* <ul>
* <li>Mouse pointer is within this window's bounds.</li>
* </ul>
* The {@code mouseButton} is ignored for Wayland; the latest mouse press event is used
* automatically; this is enforced by the Wayland protocol.
*
* Calling this method will make the window start moving together with the mouse pointer until
* the specified mouse button is released or Esc is pressed. The conditions for cancelling
* the move may differ between WMs.
*
* @param mouseButton indicates the mouse button that was pressed to start moving the window;
* must be one of {@code MouseEvent.BUTTON1}, {@code MouseEvent.BUTTON2},
* or {@code MouseEvent.BUTTON3}.
* or {@code MouseEvent.BUTTON3}. This argument is ignored for Wayland.
*/
void startMovingTogetherWithMouse(Window window, int mouseButton);
}

View File

@@ -11,4 +11,4 @@ VERSION = 0.0.14
# Hash is used to track changes to jetbrains.api, so you would not forget to update version when needed.
# When you make any changes, "make jbr-api" will fail and ask you to update hash and version number here.
HASH = A522CCED3246461BB9CC92E620DEE3AA
HASH = D28F22789F62EA4F8B8F4E7EC8B342E5

View File

@@ -0,0 +1,299 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 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.
*/
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
/**
* @test
* @key headful
* @summary JBR-5676 Wayland: support generation of input events by AWT Robot in weston plugin
* @requires (os.family == "linux")
* @library /test/lib
* @build RobotKeyboard
* @run driver WakefieldTestDriver -timeout 60 RobotKeyboard
*/
public class RobotKeyboard {
private static String ordinaryKeyNames[] = {
"VK_0",
"VK_1",
"VK_2",
"VK_3",
"VK_4",
"VK_5",
"VK_6",
"VK_7",
"VK_8",
"VK_9",
"VK_A",
"VK_ADD",
"VK_AGAIN",
"VK_ALT",
// TODO: WLToolkit doesn't differentiate VK_ALT and VK_ALT_GRAPH for now
// "VK_ALT_GRAPH",
"VK_B",
"VK_BACK_QUOTE",
"VK_BACK_SLASH",
"VK_BACK_SPACE",
"VK_C",
"VK_CLOSE_BRACKET",
"VK_COMMA",
"VK_CONTROL",
"VK_D",
"VK_DECIMAL",
"VK_DELETE",
"VK_DIVIDE",
"VK_DOWN",
"VK_E",
"VK_END",
"VK_ENTER",
"VK_EQUALS",
"VK_ESCAPE",
"VK_F",
"VK_F1",
"VK_F2",
"VK_F3",
"VK_F4",
"VK_F5",
"VK_F6",
"VK_F7",
"VK_F8",
"VK_F9",
"VK_F10",
"VK_F11",
"VK_F12",
// TODO: WLToolkit ignores F13..F24 due to the XKB issues presumably
// "VK_F13",
// "VK_F14",
// "VK_F15",
// "VK_F16",
// "VK_F17",
// "VK_F18",
// "VK_F19",
// "VK_F20",
// "VK_F21",
// "VK_F22",
// "VK_F23",
// "VK_F24",
"VK_FIND",
"VK_G",
"VK_H",
"VK_HELP",
"VK_HIRAGANA",
"VK_HOME",
"VK_I",
"VK_INPUT_METHOD_ON_OFF",
"VK_INSERT",
"VK_J",
"VK_K",
"VK_KATAKANA",
"VK_L",
"VK_LEFT",
"VK_LEFT_PARENTHESIS",
"VK_LESS",
"VK_M",
// TODO: WLToolkit reports the Meta key as VK_WINDOWS
// "VK_META",
"VK_MINUS",
"VK_MULTIPLY",
"VK_N",
"VK_NONCONVERT",
"VK_NUMPAD0",
"VK_NUMPAD1",
"VK_NUMPAD2",
"VK_NUMPAD3",
"VK_NUMPAD4",
"VK_NUMPAD5",
"VK_NUMPAD6",
"VK_NUMPAD7",
"VK_NUMPAD8",
"VK_NUMPAD9",
"VK_O",
"VK_OPEN_BRACKET",
"VK_P",
"VK_PAGE_DOWN",
"VK_PAGE_UP",
"VK_PAUSE",
"VK_PERIOD",
"VK_PRINTSCREEN",
"VK_Q",
"VK_QUOTE",
"VK_R",
"VK_RIGHT",
"VK_RIGHT_PARENTHESIS",
"VK_S",
"VK_SEMICOLON",
"VK_SHIFT",
"VK_SLASH",
"VK_SPACE",
"VK_STOP",
"VK_SUBTRACT",
"VK_T",
"VK_TAB",
"VK_U",
"VK_UNDO",
"VK_UP",
"VK_V",
"VK_W",
"VK_WINDOWS",
"VK_X",
"VK_Y",
"VK_Z",
};
private static String lockingKeyNames[] = {
"VK_CAPS_LOCK",
"VK_NUM_LOCK",
"VK_SCROLL_LOCK",
};
private static Robot robot;
private static JFrame frame;
private static JTextArea textArea;
private static final List<KeyEvent> events = new ArrayList<>();
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(() -> {
frame = new JFrame("test");
textArea = new JTextArea("");
textArea.setEditable(false);
frame.add(new JScrollPane(textArea));
frame.setSize(500, 500);
frame.setVisible(true);
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(
new KeyEventDispatcher() {
@Override
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_PRESSED || e.getID() == KeyEvent.KEY_RELEASED) {
events.add(e);
}
return false;
}
}
);
});
robot = new Robot();
robot.setAutoDelay(50);
robot.delay(500);
if (Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_CAPS_LOCK)) {
// Disable caps lock
robot.keyPress(KeyEvent.VK_CAPS_LOCK);
robot.keyRelease(KeyEvent.VK_CAPS_LOCK);
}
if (!Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_NUM_LOCK)) {
// Enable num lock
robot.keyPress(KeyEvent.VK_NUM_LOCK);
robot.keyRelease(KeyEvent.VK_NUM_LOCK);
}
boolean ok = true;
for (String key : ordinaryKeyNames) {
ok &= processKey(key);
}
for (String key : lockingKeyNames) {
ok &= processKey(key);
// reset the locking state to the previous one
int keyCode = getKeyCodeByName(key);
robot.keyPress(keyCode);
robot.keyRelease(keyCode);
robot.waitForIdle();
}
System.err.println("===== TEST RESULT =====");
System.err.println(ok ? "TEST PASSED" : "TEST FAILED");
System.err.println("===== FULL LOG =====");
System.err.println(textArea.getText());
frame.dispose();
// Due to some reason that probably has something to do with the implementation
// of the test driver, it's necessary to manually call System.exit() here
System.exit(ok ? 0 : 1);
}
private static int getKeyCodeByName(String name) {
try {
return KeyEvent.class.getDeclaredField(name).getInt(KeyEvent.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static void checkKey(String name) {
int keyCode = getKeyCodeByName(name);
events.clear();
textArea.grabFocus();
robot.waitForIdle();
robot.keyPress(keyCode);
robot.keyRelease(keyCode);
robot.waitForIdle();
if (events.size() != 2) {
throw new RuntimeException("Expected two events, got: " + events.size());
}
if (events.get(0).getID() != KeyEvent.KEY_PRESSED || events.get(1).getID() != KeyEvent.KEY_RELEASED) {
throw new RuntimeException("Expected one KEY_PRESSED and one KEY_RELEASED");
}
if (events.get(0).getKeyCode() != keyCode) {
throw new RuntimeException("KEY_PRESSED keyCode is " + events.get(0).getKeyCode() + ", expected " + keyCode);
}
if (events.get(1).getKeyCode() != keyCode) {
throw new RuntimeException("KEY_RELEASED keyCode is " + events.get(1).getKeyCode() + ", expected " + keyCode);
}
}
private static void log(String what) {
textArea.append(what);
textArea.setCaretPosition(textArea.getDocument().getLength());
System.err.print(what);
}
private static boolean processKey(String name) {
log(name + ": ");
try {
checkKey(name);
log("OK\n");
return true;
} catch (RuntimeException e) {
log(e.getMessage() + "\n");
return false;
}
}
}

View File

@@ -40,8 +40,8 @@ import java.io.IOException;
* @requires (os.family == "linux")
* @library /test/lib
* @build ScreenCapture
* @run driver WakefieldTestDriver 1x1400x800 ScreenCapture
* @run driver WakefieldTestDriver 2x830x800 ScreenCapture
* @run driver WakefieldTestDriver -resolution 1x1400x800 ScreenCapture
* @run driver WakefieldTestDriver -resolution 2x830x800 ScreenCapture
*/
public class ScreenCapture {

View File

@@ -37,11 +37,11 @@ public class WakefieldTestDriver {
final static int DEFAULT_NUMBER_OF_SCREENS = 1;
final static int DEFAULT_SCREEN_WIDTH = 1280;
final static int DEFAULT_SCREEN_HEIGHT = 800;
final static int TEST_TIMEOUT_SECONDS = 10;
static int testTimeoutSeconds = 10;
static void usage() {
System.out.println(
"""
WakefieldTestDriver [NxWxH] ClassName [args]
WakefieldTestDriver [-resolution NxWxH] [-timeout SECONDS] ClassName [args]
where
N - number of Weston outputs (screens); defaults to 1
W - width of each screen in pixels; defaults to 1280
@@ -65,24 +65,37 @@ public class WakefieldTestDriver {
int nScreens = DEFAULT_NUMBER_OF_SCREENS;
int screenWidth = DEFAULT_SCREEN_WIDTH;
int screenHeight = DEFAULT_SCREEN_HEIGHT;
final String firstArg = args[0];
if (Character.isDigit(firstArg.charAt(0))) {
try {
final int firstXIndex = firstArg.indexOf("x", 0);
final int secondXIndex = firstArg.indexOf("x", firstXIndex + 1);
nScreens = Integer.valueOf(firstArg.substring(0, firstXIndex));
screenWidth = Integer.valueOf(firstArg.substring(firstXIndex + 1, secondXIndex));
screenHeight = Integer.valueOf(firstArg.substring(secondXIndex + 1, firstArg.length()));
for (int i = 1; i < args.length; ++i) {
jvmArgs.add(args[i]);
for (int i = 0; i < args.length; ++i) {
if (args[i].equals("-resolution") && i + 1 < args.length) {
final String arg = args[i + 1];
if (Character.isDigit(arg.charAt(0))) {
try {
final int firstXIndex = arg.indexOf("x", 0);
final int secondXIndex = arg.indexOf("x", firstXIndex + 1);
nScreens = Integer.valueOf(arg.substring(0, firstXIndex));
screenWidth = Integer.valueOf(arg.substring(firstXIndex + 1, secondXIndex));
screenHeight = Integer.valueOf(arg.substring(secondXIndex + 1, arg.length()));
} catch (IndexOutOfBoundsException | NumberFormatException ignored) {
usage();
throw new RuntimeException("Error parsing the first argument of the test driver");
}
}
} catch (IndexOutOfBoundsException | NumberFormatException ignored) {
usage();
throw new RuntimeException("Error parsing the first argument of the test driver");
++i;
continue;
}
} else {
jvmArgs.addAll(Arrays.asList(args));
if (args[i].equals("-timeout") && i + 1 < args.length) {
final String arg = args[i + 1];
testTimeoutSeconds = Integer.valueOf(arg);
++i;
continue;
}
for (int j = i; j < args.length; ++j) {
jvmArgs.add(args[j]);
}
break;
}
final String socketName = SOCKET_NAME_PREFIX + ProcessHandle.current().pid();
@@ -95,13 +108,13 @@ public class WakefieldTestDriver {
final Process p = pb.start();
final OutputAnalyzer output = new OutputAnalyzer(p);
final boolean exited = p.waitFor(TEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
final boolean exited = p.waitFor(testTimeoutSeconds, TimeUnit.SECONDS);
if (!exited) p.destroy();
System.out.println("Test finished. Output: [[[");
System.out.println(output.getOutput());
System.out.println("]]]");
if (!exited) {
throw new RuntimeException("Test timed out after " + TEST_TIMEOUT_SECONDS + " seconds");
throw new RuntimeException("Test timed out after " + testTimeoutSeconds + " seconds");
}
if (exited && output.getExitValue() != 0) {

View File

@@ -142,7 +142,7 @@ java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion_2.java 820420
java/awt/event/MouseWheelEvent/InfiniteRecursion/InfiniteRecursion_4.java 8204200 windows-all,macosx-all,linux-all
java/awt/dnd/DropTargetEnterExitTest/ExtraDragEnterTest.java 8029680 generic-all
java/awt/dnd/DropTargetEnterExitTest/MissedDragExitTest.java JBR-5730 linux-all
java/awt/dnd/MissingDragExitEventTest/MissingDragExitEventTest.java JBR-5727 linux-all
java/awt/dnd/MissingDragExitEventTest/MissingDragExitEventTest.java 8288839,JBR-5727,JBR-5959 windows-x64,linux-all, windows-aarch64
java/awt/dnd/URIListBetweenJVMsTest/URIListBetweenJVMsTest.java 8171510,JBR-881 macosx-all,linux-all
javax/swing/dnd/7171812/bug7171812.java 8041447,8253184 macosx-all,windows-all
java/awt/Focus/ChildWindowFocusTest/ChildWindowFocusTest.java JBR-5545 linux-all,windows-all
@@ -867,7 +867,7 @@ javax/sound/sampled/Clip/ClipIsRunningAfterStop.java 8307574,JBR-4455 linux-x64,
############################################################################
javax/imageio/plugins/external_plugin_tests/TestClassPathPlugin.sh JBR-5363 window-all
javax/imageio/plugins/external_plugin_tests/TestClassPathPlugin.sh JBR-5363 windows-all
# jdk_swing