code cleanup, Fixed MacOS SystemTray popup trigger (it now will show

on left and right click, but THERE IS NO DISTINCTION between which
button caused it to show, it will always say it was left mouse)
This commit is contained in:
nathan 2016-10-16 21:07:30 +02:00
parent a26522411e
commit c01b560c06

View File

@ -17,15 +17,15 @@ package dorkbox.systemTray.util;
import static dorkbox.systemTray.SystemTray.logger;
import java.awt.Robot;
import java.awt.AWTException;
import java.awt.event.KeyEvent;
import java.util.Locale;
import dorkbox.systemTray.SystemTray;
import dorkbox.util.BootStrapClassLoader;
import dorkbox.util.OS;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
/**
@ -37,10 +37,6 @@ class SystemTrayFixes {
// oh my. Java likes to think that ALL windows tray icons are 16x16.... Lets fix that!
// https://stackoverflow.com/questions/16378886/java-trayicon-right-click-disabled-on-mac-osx/35919788#35919788
public static void fixWindows() {
if (!OS.isWindows()) {
return;
}
String vendor = System.getProperty("java.vendor").toLowerCase(Locale.US);
// spaces at the end to make sure we check for words
if (!(vendor.contains("sun ") || vendor.contains("oracle "))) {
@ -100,7 +96,7 @@ class SystemTrayFixes {
try {
// necessary to initialize sun.awt.windows.WObjectPeer native initIDs()
@SuppressWarnings("unused")
Robot robot = new Robot();
java.awt.Robot robot = new java.awt.Robot();
ClassPool pool = ClassPool.getDefault();
@ -128,62 +124,62 @@ class SystemTrayFixes {
int TRAY_MASK = (ImageUtils.TRAY_SIZE * ImageUtils.TRAY_SIZE) / 8;
ctMethodCreate.setBody("{" +
"java.awt.image.BufferedImage bufferedImage = $1;\n" +
"java.awt.image.BufferedImage bufferedImage = $1;" +
"java.awt.image.Raster rasterImage = bufferedImage.getRaster();\n" +
"final byte[] mask = new byte[" + TRAY_MASK + "];\n" +
"final int pixels[] = ((java.awt.image.DataBufferInt)rasterImage.getDataBuffer()).getData();\n" +
"java.awt.image.Raster rasterImage = bufferedImage.getRaster();" +
"final byte[] mask = new byte[" + TRAY_MASK + "];" +
"final int pixels[] = ((java.awt.image.DataBufferInt)rasterImage.getDataBuffer()).getData();" +
"int numberOfPixels = pixels.length;\n" +
"int rasterImageWidth = rasterImage.getWidth();\n" +
"int numberOfPixels = pixels.length;" +
"int rasterImageWidth = rasterImage.getWidth();" +
"for (int i = 0; i < numberOfPixels; i++) {\n" +
" int iByte = i / 8;\n" +
" int augmentMask = 1 << (7 - (i % 8));\n" +
" if ((pixels[i] & 0xFF000000) == 0) {\n" +
" if (iByte < mask.length) {\n" +
" mask[iByte] |= augmentMask;\n" +
" }\n" +
" }\n" +
"}\n" +
"for (int i = 0; i < numberOfPixels; i++) {" +
" int iByte = i / 8;" +
" int augmentMask = 1 << (7 - (i % 8));" +
" if ((pixels[i] & 0xFF000000) == 0) {" +
" if (iByte < mask.length) {" +
" mask[iByte] |= augmentMask;" +
" }" +
" }" +
"}" +
"if (rasterImage instanceof sun.awt.image.IntegerComponentRaster) {\n" +
" rasterImageWidth = ((sun.awt.image.IntegerComponentRaster)rasterImage).getScanlineStride();\n" +
"}\n" +
"if (rasterImage instanceof sun.awt.image.IntegerComponentRaster) {" +
" rasterImageWidth = ((sun.awt.image.IntegerComponentRaster)rasterImage).getScanlineStride();" +
"}" +
"setNativeIcon(((java.awt.image.DataBufferInt)bufferedImage.getRaster().getDataBuffer()).getData(), " +
"mask, rasterImageWidth, rasterImage.getWidth(), rasterImage.getHeight());\n" +
"mask, rasterImageWidth, rasterImage.getWidth(), rasterImage.getHeight());" +
"}");
ctMethodUpdate.setBody("{" +
"java.awt.Image image = $1;\n" +
"java.awt.Image image = $1;" +
"if (isDisposed()) {\n" +
" return;\n" +
"}\n" +
"if (isDisposed()) {" +
" return;" +
"}" +
"int imageWidth = image.getWidth(observer);\n" +
"int imageHeight = image.getWidth(observer);\n" +
"int imageWidth = image.getWidth(observer);" +
"int imageHeight = image.getWidth(observer);" +
"java.awt.image.BufferedImage trayIcon = new java.awt.image.BufferedImage(imageWidth, imageHeight, java.awt.image.BufferedImage.TYPE_INT_ARGB);\n" +
"java.awt.Graphics2D g = trayIcon.createGraphics();\n" +
"java.awt.image.BufferedImage trayIcon = new java.awt.image.BufferedImage(imageWidth, imageHeight, java.awt.image.BufferedImage.TYPE_INT_ARGB);" +
"java.awt.Graphics2D g = trayIcon.createGraphics();" +
"if (g != null) {\n" +
" try {\n" +
"if (g != null) {" +
" try {" +
// this will render the image "nicely"
" g.addRenderingHints(new java.awt.RenderingHints(java.awt.RenderingHints.KEY_RENDERING," +
"java.awt.RenderingHints.VALUE_RENDER_QUALITY));\n" +
" g.drawImage(image, 0, 0, imageWidth, imageHeight, observer);\n" +
"java.awt.RenderingHints.VALUE_RENDER_QUALITY));" +
" g.drawImage(image, 0, 0, imageWidth, imageHeight, observer);" +
" createNativeImage(trayIcon);\n" +
" createNativeImage(trayIcon);" +
" updateNativeIcon(!firstUpdate);\n" +
" updateNativeIcon(!firstUpdate);" +
" if (firstUpdate) {" +
" firstUpdate = false;\n" +
" }\n" +
" } finally {\n" +
" g.dispose();\n" +
" }\n" +
" firstUpdate = false;" +
" }" +
" } finally {" +
" g.dispose();" +
" }" +
"}" +
"}");
@ -206,10 +202,6 @@ class SystemTrayFixes {
// https://stackoverflow.com/questions/16378886/java-trayicon-right-click-disabled-on-mac-osx/35919788#35919788
// https://bugs.openjdk.java.net/browse/JDK-7158615
public static void fixMacOS() {
if (!OS.isWindows()) {
return;
}
String vendor = System.getProperty("java.vendor").toLowerCase(Locale.US);
// spaces at the end to make sure we check for words
if (!(vendor.contains("sun ") || vendor.contains("oracle "))) {
@ -218,7 +210,6 @@ class SystemTrayFixes {
}
boolean isMacSwingTrayLoaded = false;
try {
@ -239,6 +230,118 @@ class SystemTrayFixes {
if (isMacSwingTrayLoaded) {
throw new RuntimeException("Unable to initialize the swing tray in windows, it has already been created!");
}
try {
java.awt.Robot robot = new java.awt.Robot();
robot.mousePress(java.awt.event.InputEvent.BUTTON1_DOWN_MASK);
} catch (AWTException e) {
e.printStackTrace();
}
ClassPool pool = ClassPool.getDefault();
byte[] mouseEventBytes;
try {
CtClass trayClass = pool.get("sun.lwawt.macosx.CTrayIcon");
// now have to make a new "system tray" (that is null) in order to init/load this class completely
// have to modify the SystemTray.getIconSize as well.
trayClass.setModifiers(trayClass.getModifiers() & javassist.Modifier.PUBLIC);
trayClass.getConstructors()[0].setModifiers(trayClass.getConstructors()[0].getModifiers() & javassist.Modifier.PUBLIC);
CtClass robotClass = pool.get("java.awt.Robot");
CtField ctField = new CtField(robotClass, "robot", trayClass);
trayClass.addField(ctField);
CtMethod ctMethodGet = trayClass.getDeclaredMethod("handleMouseEvent");
ctMethodGet.setBody("{" +
"sun.lwawt.macosx.NSEvent event = $1;" +
"sun.awt.SunToolkit toolKit = (sun.awt.SunToolkit)java.awt.Toolkit.getDefaultToolkit();" +
"int button = event.getButtonNumber();" +
"if ((button <= 2 || toolKit.areExtraMouseButtonsEnabled()) && button <= toolKit.getNumberOfButtons() - 1) {" +
"int eventType = sun.lwawt.macosx.NSEvent.nsToJavaEventType(event.getType());" +
"int jButton = 0;" +
"int jClickCount = 0;" +
// "java.lang.System.err.println(\"Click \" + button + \" event: \" + eventType);" +
"if (eventType != 503) {" +
"jButton = sun.lwawt.macosx.NSEvent.nsToJavaButton(button);" +
"jClickCount = event.getClickCount();" +
"}" +
"int mouseMods = sun.lwawt.macosx.NSEvent.nsToJavaMouseModifiers(button, event.getModifierFlags());" +
// surprisingly, this is false when the popup is showing
"boolean popupTrigger = sun.lwawt.macosx.NSEvent.isPopupTrigger(mouseMods);" +
"int mouseMask = jButton > 0 ? java.awt.event.MouseEvent.getMaskForButton(jButton) : 0;" +
"long event0 = System.currentTimeMillis();" +
"if(eventType == 501) {" +
"mouseClickButtons |= mouseMask;" +
"} else if(eventType == 506) {" +
"mouseClickButtons = 0;" +
"}" +
// have to swallow + re-dispatch events in specific cases. (right click)
"if (eventType == 501 && popupTrigger && button == 1) {" +
// "java.lang.System.err.println(\"HAS POPUP \" + popupTrigger + \" event: \" + eventType);" +
// we use Robot to click where we clicked, in order to "fool" the native part to show the popup
// For what it's worth, this is the only way to get the native bits to behave.
"if (robot == null) {" +
"try {" +
"robot = new java.awt.Robot();" +
"} catch (java.awt.AWTException e) {" +
"e.printStackTrace();" +
"}" +
"}" +
"robot.mousePress(java.awt.event.InputEvent.BUTTON1_DOWN_MASK);" +
"return;" +
"}" +
"int mouseX = event.getAbsX();" +
"int mouseY = event.getAbsY();" +
"java.awt.event.MouseEvent mEvent = new java.awt.event.MouseEvent(this.dummyFrame, eventType, event0, mouseMods, mouseX, mouseY, mouseX, mouseY, jClickCount, popupTrigger, jButton);" +
"mEvent.setSource(this.target);" +
"this.postEvent(mEvent);" +
// mouse press
"if (eventType == 501) {" +
"if (popupTrigger) {" +
"String event5 = this.target.getActionCommand();" +
"java.awt.event.ActionEvent event6 = new java.awt.event.ActionEvent(this.target, 1001, event5);" +
"this.postEvent(event6);" +
"}" +
"}" +
// mouse release
"if (eventType == 502) {" +
"if ((mouseClickButtons & mouseMask) != 0) {" +
"java.awt.event.MouseEvent event7 = new java.awt.event.MouseEvent(this.dummyFrame, 500, event0, mouseMods, mouseX, mouseY, mouseX, mouseY, jClickCount, popupTrigger, jButton);" +
"event7.setSource(this.target);" +
"this.postEvent(event7);" +
"}" +
"mouseClickButtons &= ~mouseMask;" +
"}" +
"}" +
"}");
mouseEventBytes = trayClass.toBytecode();
// whoosh, past the classloader and directly into memory.
BootStrapClassLoader.defineClass(mouseEventBytes);
if (SystemTray.DEBUG) {
logger.debug("Successfully changed mouse trigger");
}
} catch (Exception e) {
logger.error("Error changing SystemTray mouse trigger.", e);
}
}
/**