diff --git a/src/dorkbox/systemTray/util/SystemTrayFixes.java b/src/dorkbox/systemTray/util/SystemTrayFixes.java index 497859f..d16ce2f 100644 --- a/src/dorkbox/systemTray/util/SystemTrayFixes.java +++ b/src/dorkbox/systemTray/util/SystemTrayFixes.java @@ -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); + } } /**