Skip to content

Commit

Permalink
Add System Tray workaround for Big Sur (qzind#745)
Browse files Browse the repository at this point in the history
Fix TrayIcon on Big Sur without JDK-8252015 (qzind#748)
  • Loading branch information
tresf authored Jan 29, 2021
1 parent da25ec6 commit 9785106
Show file tree
Hide file tree
Showing 14 changed files with 747 additions and 21 deletions.
2 changes: 1 addition & 1 deletion ant/unix/unix-launcher.sh.in
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ if command -v java &>/dev/null; then
else
prefix="../../" # back two directories, e.g. postinstall
fi
java $LAUNCH_OPTS -Xdock:name="$ABOUT_TITLE" -Xdock:icon="$ICON_PATH" -jar -Dapple.awt.UIElement="true" "${prefix}$PROPS_FILE.jar" -NSRequiresAquaSystemAppearance False "$@"
java $LAUNCH_OPTS -Xdock:name="$ABOUT_TITLE" -Xdock:icon="$ICON_PATH" -jar -Dapple.awt.UIElement="true" -Dapple.awt.enableTemplateImages="true" "${prefix}$PROPS_FILE.jar" -NSRequiresAquaSystemAppearance False "$@"
else
java $LAUNCH_OPTS -jar "$PROPS_FILE.jar" "$@"
fi
Expand Down
96 changes: 96 additions & 0 deletions src/org/dyorgio/jna/platform/mac/ActionCallback.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* The MIT License
*
* Copyright 2020 dyorgio.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.dyorgio.jna.platform.mac;

import com.sun.jna.Callback;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import java.util.HashMap;

/**
*
* @author dyorgio
*/
@SuppressWarnings("Convert2Lambda")
public final class ActionCallback extends NSObject {

private static final NativeLong actionCallbackClass = Foundation.INSTANCE.objc_allocateClassPair(objectClass, ActionCallback.class.getSimpleName(), 0);
private static final Pointer actionCallbackSel = Foundation.INSTANCE.sel_registerName("actionCallback");
private static final Pointer setTargetSel = Foundation.INSTANCE.sel_registerName("setTarget:");
private static final Pointer setActionSel = Foundation.INSTANCE.sel_registerName("setAction:");
private static final Callback registerActionCallback;

static {
startNativeAppMainThread();
registerActionCallback = new Callback() {
@SuppressWarnings("unused")
public void callback(Pointer self, Pointer selector) {
if (selector.equals(actionCallbackSel)) {
ActionCallback action;

synchronized (callbackMap) {
action = callbackMap.get(Pointer.nativeValue(self));
}

if (action != null) {
action.runnable.run();
}
}
}
};

if (!Foundation.INSTANCE.class_addMethod(actionCallbackClass,
actionCallbackSel, registerActionCallback, "v@:")) {
throw new RuntimeException("Error initializing ActionCallback as a objective C class");
}

Foundation.INSTANCE.objc_registerClassPair(actionCallbackClass);
}

private static final HashMap<Long, ActionCallback> callbackMap = new HashMap<Long, ActionCallback>();

private final Runnable runnable;

@SuppressWarnings("LeakingThisInConstructor")
public ActionCallback(Runnable callable) {
super(Foundation.INSTANCE.class_createInstance(actionCallbackClass, 0));
this.runnable = callable;
synchronized (callbackMap) {
callbackMap.put(getId().longValue(), this);
}
}

@Override
public void release() {
synchronized (callbackMap) {
callbackMap.remove(getId().longValue());
}
super.release();
}

public void installActionOnNSControl(NativeLong nsControl) {
Foundation.INSTANCE.objc_msgSend(nsControl, setTargetSel, id);
Foundation.INSTANCE.objc_msgSend(nsControl, setActionSel, actionCallbackSel);
}
}
57 changes: 57 additions & 0 deletions src/org/dyorgio/jna/platform/mac/Foundation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* The MIT License
*
* Copyright 2020 dyorgio.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.dyorgio.jna.platform.mac;

import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;

/**
*
* @author dyorgio
*/
public interface Foundation extends Library {

public static final Foundation INSTANCE = Native.load("Foundation", Foundation.class);

NativeLong class_getInstanceVariable(NativeLong classPointer, String name);

NativeLong object_getIvar(NativeLong target, NativeLong ivar);

NativeLong objc_getClass(String className);

NativeLong objc_allocateClassPair(NativeLong superClass, String name, long extraBytes);

void objc_registerClassPair(NativeLong clazz);

NativeLong class_createInstance(NativeLong clazz, int extraBytes);

boolean class_addMethod(NativeLong clazz, Pointer selector, Callback callback, String types);

NativeLong objc_msgSend(NativeLong receiver, Pointer selector, Object... args);

Pointer sel_registerName(String selectorName);
}
88 changes: 88 additions & 0 deletions src/org/dyorgio/jna/platform/mac/FoundationUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* The MIT License
*
* Copyright 2020 dyorgio.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.dyorgio.jna.platform.mac;

import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
*
* @author dyorgio
*/
public final class FoundationUtil {

private static final Foundation FOUNDATION = Foundation.INSTANCE;

public static final NativeLong NULL = new NativeLong(0l);

private FoundationUtil() {
}

public static boolean isNull(NativeLong id) {
return NULL.equals(id);
}

public static boolean isNull(NSObject object) {
return NULL.equals(object.id);
}

public static boolean isFalse(NativeLong id) {
return NULL.equals(id);
}

public static boolean isTrue(NativeLong id) {
return !NULL.equals(id);
}

public static NativeLong invoke(NativeLong id, String selector, Object... args) {
return FOUNDATION.objc_msgSend(id, Foundation.INSTANCE.sel_registerName(selector), args);
}

public static NativeLong invoke(NativeLong id, Pointer selectorPointer, Object... args) {
return FOUNDATION.objc_msgSend(id, selectorPointer, args);
}

public static void runOnMainThreadAndWait(Runnable runnable) throws InterruptedException, ExecutionException {
runOnMainThread(runnable, true);
}

public static FutureTask runOnMainThread(Runnable runnable, boolean waitUntilDone) {
FutureTask futureTask = new FutureTask(runnable, null);
FutureTaskCallback.performOnMainThread(futureTask, waitUntilDone);
return futureTask;
}

public static <T> T callOnMainThreadAndWait(Callable<T> callable) throws InterruptedException, ExecutionException {
return callOnMainThread(callable, true).get();
}

public static <T> FutureTask<T> callOnMainThread(Callable<T> callable, boolean waitUntilDone) {
FutureTask<T> futureTask = new FutureTask(callable);
FutureTaskCallback.performOnMainThread(futureTask, waitUntilDone);
return futureTask;
}
}
94 changes: 94 additions & 0 deletions src/org/dyorgio/jna/platform/mac/FutureTaskCallback.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* The MIT License
*
* Copyright 2020 dyorgio.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.dyorgio.jna.platform.mac;

import com.sun.jna.Callback;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import java.util.HashMap;
import java.util.concurrent.FutureTask;

/**
*
* @author dyorgio
*/
@SuppressWarnings("Convert2Lambda")
class FutureTaskCallback<T> extends NSObject {

private static final NativeLong futureTaskCallbackClass = Foundation.INSTANCE.objc_allocateClassPair(objectClass, FutureTaskCallback.class.getSimpleName(), 0);
private static final Pointer futureTaskCallbackSel = Foundation.INSTANCE.sel_registerName("futureTaskCallback");
private static final Callback registerFutureTaskCallback;

static {
startNativeAppMainThread();
registerFutureTaskCallback = new Callback() {
@SuppressWarnings("unused")
public void callback(Pointer self, Pointer selector) {
if (selector.equals(futureTaskCallbackSel)) {
FutureTaskCallback action;

synchronized (callbackMap) {
action = callbackMap.remove(Pointer.nativeValue(self));
}

if (action != null) {
action.callable.run();
}
}
}
};

if (!Foundation.INSTANCE.class_addMethod(futureTaskCallbackClass,
futureTaskCallbackSel, registerFutureTaskCallback, "v@:")) {
throw new RuntimeException("Error initializing FutureTaskCallback as a objective C class");
}

Foundation.INSTANCE.objc_registerClassPair(futureTaskCallbackClass);
}

private static final HashMap<Long, FutureTaskCallback> callbackMap = new HashMap<Long, FutureTaskCallback>();

private final FutureTask<T> callable;

@SuppressWarnings("LeakingThisInConstructor")
private FutureTaskCallback(FutureTask<T> callable) {
super(Foundation.INSTANCE.class_createInstance(futureTaskCallbackClass, 0));
this.callable = callable;
synchronized (callbackMap) {
callbackMap.put(getId().longValue(), this);
}
}

@Override
public void release() {
synchronized (callbackMap) {
callbackMap.remove(getId().longValue());
}
super.release();
}

static <T> void performOnMainThread(FutureTask<T> futureTask, boolean waitUntilDone) {
new FutureTaskCallback(futureTask).performSelectorOnMainThread(futureTaskCallbackSel, null, waitUntilDone);
}
}
50 changes: 50 additions & 0 deletions src/org/dyorgio/jna/platform/mac/NSDictionary.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* The MIT License
*
* Copyright 2020 dyorgio.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.dyorgio.jna.platform.mac;

import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;

/**
*
* @author dyorgio
*/
public class NSDictionary extends NSObject {

private static final NativeLong dictionaryClass = Foundation.INSTANCE.objc_getClass("NSDictionary");
private static final Pointer dictionaryWithContentsOfFileSel = Foundation.INSTANCE.sel_registerName("dictionaryWithContentsOfFile:");
private static final Pointer objectForKeySel = Foundation.INSTANCE.sel_registerName("objectForKey:");

public NSDictionary(NativeLong id) {
super(id);
}

public static NSDictionary dictionaryWithContentsOfFile(NSString file) {
return new NSDictionary(Foundation.INSTANCE.objc_msgSend(dictionaryClass, dictionaryWithContentsOfFileSel, file.id));
}

public NSObject objectForKey(NSObject key) {
return new NSString(FoundationUtil.invoke(id, objectForKeySel, key.id));
}
}
Loading

0 comments on commit 9785106

Please sign in to comment.