Updated to better support gnome-shell desktop environments.
This commit is contained in:
parent
ac74d8bdd7
commit
c8fac55967
46
README.md
46
README.md
|
@ -8,24 +8,44 @@ This libraries only purpose is to show *reasonably* decent system-tray icons and
|
|||
There are a number of problems on Linux with the Swing (and SWT) system-tray icons, namely that:
|
||||
|
||||
1. Swing system-tray icons on linux **do not** support transparent backgrounds (they have a white background)
|
||||
2. Swing/SWT **do not** support app-indicators, which are necessary on more recent versions of linux
|
||||
2. Swing/SWT **do not** support app-indicators, which are necessary on more recent versions of gnu/linux distros.
|
||||
3. Swing popup menus look like crap
|
||||
- swing-based system-tray uses a JMenuPopup, which looks nicer than the java 'regular' one.
|
||||
- app-indicators use native popups (a system limitation).
|
||||
- app-indicators use native popups.
|
||||
- gtk-indicators use native popups.
|
||||
|
||||
|
||||
This is for cross-platform use, specifically - linux 32/64, mac 32/64, and windows 32/64. Java 6+
|
||||
|
||||
|
||||
```
|
||||
To customize the delay (for hiding the popup) when the cursor is "moused out" of the
|
||||
popup menu, change the value of 'SystemTrayMenuPopup.hidePopupDelay'
|
||||
Customization parameters:
|
||||
|
||||
SystemTrayMenuPopup.hidePopupDelay (type long, default value '1000L')
|
||||
- Allows you to customize the delay (for hiding the popup) when the cursor is "moused out" of the popup menu
|
||||
|
||||
GnomeShellExtension.ENABLE_SHELL_RESTART (type boolean, default value 'true')
|
||||
- Permit the gnome-shell to be restarted when the extension is installed.
|
||||
|
||||
|
||||
GnomeShellExtension.SHELL_RESTART_TIMEOUT_MILLIS (type long, default value '5000L')
|
||||
- Default timeout to wait for the gnome-shell to completely restart. This is a best-guess estimate.
|
||||
|
||||
|
||||
GnomeShellExtension.SHELL_RESTART_COMMAND (type String, default value 'gnome-shell --replace &')
|
||||
- Command to restart the gnome-shell. It is recommended to start it in the background (hence '&')
|
||||
|
||||
|
||||
SystemTray.TRAY_SIZE (type int, default value '24')
|
||||
- Size of the tray, so that the icon can properly scale based on OS. (if it's not exact)
|
||||
- NOTE: Must be set after any other customization options, as a static call to SystemTray will cause initialization of the library.
|
||||
|
||||
|
||||
SystemTray.ICON_PATH (type String, default value '')
|
||||
- Location of the icon (to make it easier when specifying icons)
|
||||
- NOTE: Must be set after any other customization options, as a static call to SystemTray will cause initialization of the library.
|
||||
|
||||
Not all system tray icons are the same size (default is 22px), so to properly scale the icon
|
||||
to fit, change the value of 'SystemTray.TRAY_SIZE'
|
||||
|
||||
You might want to specify the root location of the icons used (to make it easier when
|
||||
specifying icons), change the value of 'SystemTray.ICON_PATH'
|
||||
|
||||
|
||||
A *simple* example is as follows:
|
||||
|
@ -63,4 +83,14 @@ Note: This project was heavily influence by the excellent Lantern project,
|
|||
*Many* thanks to them for figuring out AppIndicators via JNA.
|
||||
https://github.com/getlantern/lantern
|
||||
```
|
||||
```
|
||||
Note: Gnome-shell users will experience an extension install to also support this
|
||||
functionality. Additionally, a shell restart is necessary for the extension
|
||||
to be noticed by the shell. You can disable the restart behavior if you like,
|
||||
and the 'system tray' functinality will be picked up on log out/in, or a
|
||||
manual restart.
|
||||
|
||||
Also, screw you gnome-project leads, for making it such a pain-in-the-ass
|
||||
to do something so incredibly simple and basic.
|
||||
```
|
||||
|
||||
|
|
|
@ -19,13 +19,23 @@ import dorkbox.util.NamedThreadFactory;
|
|||
import dorkbox.util.OS;
|
||||
import dorkbox.util.jna.linux.AppIndicator;
|
||||
import dorkbox.util.jna.linux.GtkSupport;
|
||||
import dorkbox.util.process.ShellProcessBuilder;
|
||||
import dorkbox.util.tray.linux.AppIndicatorTray;
|
||||
import dorkbox.util.tray.linux.GnomeShellExtension;
|
||||
import dorkbox.util.tray.linux.GtkSystemTray;
|
||||
import dorkbox.util.tray.swing.SwingSystemTray;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -54,7 +64,7 @@ class SystemTray {
|
|||
public static int TRAY_SIZE = 22;
|
||||
|
||||
/**
|
||||
* Location of the icon
|
||||
* Location of the icon (to make it easier when specifying icons)
|
||||
*/
|
||||
public static String ICON_PATH = "";
|
||||
|
||||
|
@ -72,13 +82,38 @@ class SystemTray {
|
|||
if (GtkSupport.isSupported) {
|
||||
// quick check, because we know that unity uses app-indicator. Maybe REALLY old versions do not. We support 14.04 LTE at least
|
||||
String getenv = System.getenv("XDG_CURRENT_DESKTOP");
|
||||
if (getenv != null && getenv.equals("Unity")) {
|
||||
if ("Unity".equalsIgnoreCase(getenv)) {
|
||||
try {
|
||||
trayType = AppIndicatorTray.class;
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
} else if ("GNOME".equalsIgnoreCase(getenv)) {
|
||||
// if the "topicons" extension is installed, don't install us (because it will override what we do, where ours
|
||||
// is more specialized - so it only modified our tray icon (instead of ALL tray icons)
|
||||
|
||||
try {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(8196);
|
||||
PrintStream outputStream = new PrintStream(byteArrayOutputStream);
|
||||
|
||||
// gnome-shell --version
|
||||
final ShellProcessBuilder shellVersion = new ShellProcessBuilder(outputStream);
|
||||
shellVersion.setExecutable("gnome-shell");
|
||||
shellVersion.addArgument("--version");
|
||||
shellVersion.start();
|
||||
|
||||
String output = ShellProcessBuilder.getOutput(byteArrayOutputStream);
|
||||
|
||||
if (!output.isEmpty()) {
|
||||
GnomeShellExtension.install(logger, output);
|
||||
trayType = GtkSystemTray.class;
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
trayType = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Try to autodetect if we can use app indicators (or if we need to fallback to GTK indicators)
|
||||
if (trayType == null) {
|
||||
BufferedReader bin = null;
|
||||
try {
|
||||
|
@ -89,6 +124,7 @@ class SystemTray {
|
|||
if (listFiles != null) {
|
||||
for (File procs : listFiles) {
|
||||
String name = procs.getName();
|
||||
|
||||
if (!Character.isDigit(name.charAt(0))) {
|
||||
continue;
|
||||
}
|
||||
|
@ -101,6 +137,7 @@ class SystemTray {
|
|||
try {
|
||||
bin = new BufferedReader(new FileReader(status));
|
||||
String readLine = bin.readLine();
|
||||
|
||||
if (readLine != null && readLine.contains("indicator-app")) {
|
||||
// make sure we can also load the library (it might be the wrong version)
|
||||
try {
|
||||
|
@ -119,6 +156,16 @@ class SystemTray {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make one last ditch effort
|
||||
if (trayType == null) {
|
||||
try {
|
||||
final AppIndicator instance = AppIndicator.INSTANCE;
|
||||
trayType = AppIndicatorTray.class;
|
||||
} catch (Throwable ignored) {
|
||||
logger.error("AppIndicator support detected, but unable to load the library. Falling back to GTK");
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
} finally {
|
||||
if (bin != null) {
|
||||
|
@ -246,11 +293,15 @@ class SystemTray {
|
|||
digest.reset();
|
||||
digest.update(bytes);
|
||||
|
||||
|
||||
// For KDE4, it must also be unique across runs
|
||||
String getenv = System.getenv("XDG_CURRENT_DESKTOP");
|
||||
if (getenv != null && getenv.contains("kde")) {
|
||||
byte[] longBytes = new byte[8];
|
||||
ByteBuffer wrap = ByteBuffer.wrap(longBytes);
|
||||
wrap.putLong(runtimeRandom);
|
||||
digest.update(longBytes);
|
||||
}
|
||||
|
||||
byte[] hashBytes = digest.digest();
|
||||
String hash = new BigInteger(1, hashBytes).toString(32);
|
||||
|
|
193
src/dorkbox/util/tray/linux/GnomeShellExtension.java
Normal file
193
src/dorkbox/util/tray/linux/GnomeShellExtension.java
Normal file
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Copyright 2015 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package dorkbox.util.tray.linux;
|
||||
|
||||
import dorkbox.util.process.ShellProcessBuilder;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
public
|
||||
class GnomeShellExtension {
|
||||
private static final String UID = "SystemTray@dorkbox";
|
||||
|
||||
/**
|
||||
* Permit the gnome-shell to be restarted when the extension is installed.
|
||||
*/
|
||||
public static boolean ENABLE_SHELL_RESTART = true;
|
||||
|
||||
/**
|
||||
* Default timeout to wait for the gnome-shell to completely restart. This is a best-guess estimate.
|
||||
*/
|
||||
public static long SHELL_RESTART_TIMEOUT_MILLIS = 5000L;
|
||||
|
||||
/**
|
||||
* Command to restart the gnome-shell. It is recommended to start it in the background (hence '&')
|
||||
*/
|
||||
public static String SHELL_RESTART_COMMAND = "gnome-shell --replace &";
|
||||
|
||||
public static void install(final Logger logger, final String shellVersionString) throws IOException {
|
||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(8196);
|
||||
PrintStream outputStream = new PrintStream(byteArrayOutputStream);
|
||||
|
||||
// gsettings get org.gnome.shell enabled-extensions
|
||||
final ShellProcessBuilder gsettings = new ShellProcessBuilder(outputStream);
|
||||
gsettings.setExecutable("gsettings");
|
||||
gsettings.addArgument("get");
|
||||
gsettings.addArgument("org.gnome.shell");
|
||||
gsettings.addArgument("enabled-extensions");
|
||||
gsettings.start();
|
||||
|
||||
String output = ShellProcessBuilder.getOutput(byteArrayOutputStream);
|
||||
|
||||
boolean hasTopIcons = output.contains("topIcons@adel.gadllah@gmail.com");
|
||||
boolean hasSystemTray = output.contains(UID);
|
||||
|
||||
// topIcons will convert ALL icons to be at the top of the screen, so there is no reason to have both installed
|
||||
if (!hasTopIcons && !hasSystemTray) {
|
||||
// have to copy the extension over and enable it.
|
||||
String userHome = System.getProperty("user.home");
|
||||
|
||||
final File file = new File(userHome + "/.local/share/gnome-shell/extensions/" + UID);
|
||||
if (!file.isDirectory()) {
|
||||
final boolean mkdirs = file.mkdirs();
|
||||
if (!mkdirs) {
|
||||
final String msg = "Unable to create extension location: " + file;
|
||||
logger.error(msg);
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
InputStream reader = null;
|
||||
FileOutputStream fileOutputStream = null;
|
||||
try {
|
||||
fileOutputStream = new FileOutputStream(new File(file, "extension.js"));
|
||||
reader = GnomeShellExtension.class.getResourceAsStream("extension.js");
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
int read;
|
||||
while ((read = reader.read(buffer)) > 0) {
|
||||
fileOutputStream.write(buffer, 0, read);
|
||||
}
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
if (fileOutputStream != null) {
|
||||
try {
|
||||
fileOutputStream.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// have to create the metadata.json file (and make it so that it's **always** current).
|
||||
// we do this via getting the shell version
|
||||
|
||||
|
||||
// GNOME Shell 3.14.1
|
||||
String versionOutput = shellVersionString.replaceAll("[^\\d.]", ""); // should just be 3.14.1
|
||||
|
||||
// now change to major version only (only if applicable)
|
||||
final int indexOf = versionOutput.indexOf('.');
|
||||
final int lastIndexOf = versionOutput.lastIndexOf('.');
|
||||
if (indexOf < lastIndexOf) {
|
||||
versionOutput = versionOutput.substring(0, lastIndexOf);
|
||||
}
|
||||
|
||||
String metadata = "{\n" +
|
||||
" \"description\": \"Shows a java tray icon on the top notification tray\",\n" +
|
||||
" \"name\": \"Dorkbox SystemTray\",\n" +
|
||||
" \"shell-version\": [\n" +
|
||||
" \"" + versionOutput + "\"\n" +
|
||||
" ],\n" +
|
||||
" \"url\": \"https://github.com/dorkbox/SystemTray\",\n" +
|
||||
" \"uuid\": \"SystemTray@dorkbox\",\n" +
|
||||
" \"version\": 1\n" +
|
||||
"}";
|
||||
|
||||
|
||||
BufferedWriter outputWriter = null;
|
||||
try {
|
||||
outputWriter = new BufferedWriter(new FileWriter(new File(file, "metadata.json"), false));
|
||||
// FileWriter always assumes default encoding is OK
|
||||
outputWriter.write(metadata);
|
||||
outputWriter.flush();
|
||||
outputWriter.close();
|
||||
} catch (Exception e) {
|
||||
if (outputWriter != null) {
|
||||
try {
|
||||
outputWriter.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now we have to enable us
|
||||
final StringBuilder stringBuilder = new StringBuilder(output);
|
||||
stringBuilder.delete(0, 4);
|
||||
stringBuilder.delete(stringBuilder.length() - 2, stringBuilder.length());
|
||||
if (stringBuilder.length() > 2) {
|
||||
stringBuilder.append(", ");
|
||||
}
|
||||
stringBuilder.append("'")
|
||||
.append(UID)
|
||||
.append("'");
|
||||
|
||||
stringBuilder.append("]");
|
||||
|
||||
// gsettings set org.gnome.shell enabled-extensions "['SystemTray@dorkbox']"
|
||||
// gsettings set org.gnome.shell enabled-extensions "['xyz', 'SystemTray@dorkbox']"
|
||||
final ShellProcessBuilder setGsettings = new ShellProcessBuilder(outputStream);
|
||||
setGsettings.setExecutable("gsettings");
|
||||
setGsettings.addArgument("set");
|
||||
setGsettings.addArgument("org.gnome.shell");
|
||||
setGsettings.addArgument("enabled-extensions");
|
||||
setGsettings.addArgument(stringBuilder.toString());
|
||||
setGsettings.start();
|
||||
|
||||
|
||||
if (ENABLE_SHELL_RESTART) {
|
||||
logger.info("Restarting gnome-shell, so tray notification changes can be applied.");
|
||||
|
||||
// now we have to restart the gnome shell via bash
|
||||
final ShellProcessBuilder restartShell = new ShellProcessBuilder();
|
||||
// restart shell in background process
|
||||
restartShell.addArgument(SHELL_RESTART_COMMAND);
|
||||
restartShell.start();
|
||||
|
||||
// have to give the shell time to restart
|
||||
try {
|
||||
Thread.sleep(SHELL_RESTART_TIMEOUT_MILLIS);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
logger.info("Shell restarted.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
271
src/dorkbox/util/tray/linux/extension.js
Normal file
271
src/dorkbox/util/tray/linux/extension.js
Normal file
|
@ -0,0 +1,271 @@
|
|||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
||||
/*
|
||||
* Copyright 2015 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*
|
||||
* This is heavily modified from an online email from Vladimir Khrustalev.
|
||||
*
|
||||
* The source material is NOT GPLx/MIT/BSD/Apache/etc, because those licenses
|
||||
* were not specified in accordance with those license requirements (there
|
||||
* was no license specified or implied). As such, this is to be considered as
|
||||
* released by the original sources as public domain.
|
||||
*/
|
||||
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const Shell = imports.gi.Shell;
|
||||
const St = imports.gi.St;
|
||||
const Main = imports.ui.main;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Lang = imports.lang;
|
||||
const Panel = imports.ui.panel;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const Meta = imports.gi.Meta;
|
||||
const Mainloop = imports.mainloop;
|
||||
const NotificationDaemon = imports.ui.notificationDaemon;
|
||||
|
||||
let trayAddedId = 0;
|
||||
let orig_onTrayIconAdded;
|
||||
|
||||
let trayRemovedId = 0;
|
||||
let orig_onTrayIconRemoved;
|
||||
|
||||
let orig_getSource = null;
|
||||
let icons = [];
|
||||
|
||||
let notificationDaemon;
|
||||
|
||||
|
||||
// this value is hardcoded into the display manager
|
||||
const PANEL_ICON_SIZE = 24;
|
||||
|
||||
function init() {
|
||||
if (Main.legacyTray) {
|
||||
notificationDaemon = Main.legacyTray;
|
||||
NotificationDaemon.STANDARD_TRAY_ICON_IMPLEMENTATIONS = imports.ui.legacyTray.STANDARD_TRAY_ICON_IMPLEMENTATIONS;
|
||||
}
|
||||
else if (Main.notificationDaemon._fdoNotificationDaemon) {
|
||||
notificationDaemon = Main.notificationDaemon._fdoNotificationDaemon;
|
||||
orig_getSource = Lang.bind(notificationDaemon, NotificationDaemon.FdoNotificationDaemon.prototype._getSource);
|
||||
}
|
||||
else {
|
||||
notificationDaemon = Main.notificationDaemon;
|
||||
orig_getSource = Lang.bind(notificationDaemon, NotificationDaemon.NotificationDaemon.prototype._getSource);
|
||||
}
|
||||
}
|
||||
|
||||
function enable() {
|
||||
GLib.idle_add(GLib.PRIORITY_LOW, installHook);
|
||||
}
|
||||
|
||||
function disable() {
|
||||
if (trayAddedId != 0) {
|
||||
notificationDaemon._trayManager.disconnect(trayAddedId);
|
||||
trayAddedId = 0;
|
||||
}
|
||||
|
||||
if (trayRemovedId != 0) {
|
||||
notificationDaemon._trayManager.disconnect(trayRemovedId);
|
||||
trayRemovedId = 0;
|
||||
}
|
||||
|
||||
notificationDaemon._trayIconAddedId = notificationDaemon._trayManager.connect('tray-icon-added', orig_onTrayIconAdded);
|
||||
notificationDaemon._trayIconRemovedId = notificationDaemon._trayManager.connect('tray-icon-removed', orig_onTrayIconRemoved);
|
||||
|
||||
notificationDaemon._getSource = orig_getSource;
|
||||
|
||||
for (let i = 0; i < icons.length; i++) {
|
||||
let icon = icons[i];
|
||||
let parent = icon.get_parent();
|
||||
if (icon._clicked) {
|
||||
icon.disconnect(icon._clicked);
|
||||
}
|
||||
icon._clicked = undefined;
|
||||
|
||||
if (icon._proxyAlloc) {
|
||||
Main.panel._rightBox.disconnect(icon._proxyAlloc);
|
||||
}
|
||||
|
||||
icon._clickProxy.destroy();
|
||||
|
||||
parent.remove_actor(icon);
|
||||
parent.destroy();
|
||||
notificationDaemon._onTrayIconAdded(notificationDaemon, icon);
|
||||
}
|
||||
|
||||
icons = [];
|
||||
}
|
||||
|
||||
|
||||
|
||||
function installHook() {
|
||||
//global.log("Installing hook")
|
||||
|
||||
// disable the "normal" method of adding icons
|
||||
notificationDaemon._trayManager.disconnect(notificationDaemon._trayIconAddedId);
|
||||
notificationDaemon._trayManager.disconnect(notificationDaemon._trayIconRemovedId);
|
||||
|
||||
// save the original method
|
||||
orig_onTrayIconAdded = Lang.bind(notificationDaemon, notificationDaemon._onTrayIconAdded);
|
||||
orig_onTrayIconRemoved = Lang.bind(notificationDaemon, notificationDaemon._onTrayIconRemoved)
|
||||
|
||||
// add our hook. If our icon doesn't have our specific title, it calls the original method
|
||||
trayAddedId = notificationDaemon._trayManager.connect('tray-icon-added', onTrayIconAdded);
|
||||
trayRemovedId = notificationDaemon._trayManager.connect('tray-icon-removed', onTrayIconRemoved);
|
||||
|
||||
notificationDaemon._getSource = getSourceHook;
|
||||
|
||||
// move icons to top
|
||||
let toDestroy = [];
|
||||
if (notificationDaemon._sources) {
|
||||
for (let i = 0; i < notificationDaemon._sources.length; i++) {
|
||||
let source = notificationDaemon._sources[i];
|
||||
|
||||
if (!source.trayIcon) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let icon = source.trayIcon;
|
||||
|
||||
// we could set the title in java, HOWEVER because of race conditions, it's not consistent. So we check for 'java'
|
||||
if (icon.title !== "java") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let parent = icon.get_parent();
|
||||
parent.remove_actor(icon);
|
||||
|
||||
onTrayIconAdded(this, icon);
|
||||
toDestroy.push(source);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < notificationDaemon._iconBox.get_n_children(); i++) {
|
||||
let button = notificationDaemon._iconBox.get_child_at_index(i);
|
||||
let icon = button.child;
|
||||
|
||||
// we could set the title in java, HOWEVER because of race conditions, it's not consistent. So we check for 'java'
|
||||
if (icon.title !== "java") {
|
||||
continue;
|
||||
}
|
||||
|
||||
button.remove_actor(icon);
|
||||
onTrayIconAdded(this, icon);
|
||||
|
||||
toDestroy.push(button);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < toDestroy.length; i++) {
|
||||
toDestroy[i].destroy();
|
||||
}
|
||||
}
|
||||
|
||||
function getSourceHook (title, pid, ndata, sender, trayIcon) {
|
||||
// we could set the title in java, HOWEVER because of race conditions, it's not consistent. So we check for 'java'
|
||||
if (trayIcon && title === "java") {
|
||||
//global.log("create source");
|
||||
onTrayIconAdded(this, trayIcon);
|
||||
return null;
|
||||
}
|
||||
|
||||
return getSource(title, pid, ndata, sender, trayIcon);
|
||||
}
|
||||
|
||||
// this is the hook that lets us only add ourselves.
|
||||
function onTrayIconAdded(o, icon) {
|
||||
//global.log("adding tray icon 1 " + icon.title);
|
||||
|
||||
let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
|
||||
if (NotificationDaemon.STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass] !== undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we could set the title in java, HOWEVER because of race conditions, it's not consistent. So we check for 'java'
|
||||
if (icon.title !== "java") {
|
||||
orig_onTrayIconAdded(o, icon);
|
||||
return;
|
||||
}
|
||||
|
||||
//global.log("adding tray icon 2 " + icon.title);
|
||||
|
||||
let buttonBox = new PanelMenu.ButtonBox();
|
||||
let box = buttonBox.actor;
|
||||
let parent = box.get_parent();
|
||||
|
||||
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
|
||||
let iconSize = PANEL_ICON_SIZE * scaleFactor;
|
||||
|
||||
icon.set_size(iconSize, iconSize);
|
||||
box.add_actor(icon);
|
||||
|
||||
// Reactive actors will receive events.
|
||||
icon.set_reactive(true);
|
||||
|
||||
if (parent) {
|
||||
// remove from the (if present) "collapsy tab icon thing"
|
||||
parent.remove_actor(box);
|
||||
}
|
||||
|
||||
// setup proxy for handling click notifications, make it a little larger than the icon
|
||||
let clickProxy = new St.Bin({ width: iconSize + 4, height: iconSize + 4});
|
||||
clickProxy.set_reactive(true);
|
||||
|
||||
icon._proxyAlloc = Main.panel._rightBox.connect('allocation-changed', function() {
|
||||
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, function() {
|
||||
let [x, y] = icon.get_transformed_position();
|
||||
// need to offset the proxy, so the icon is centered inside the click handler
|
||||
clickProxy.set_position(x - 2, y);
|
||||
});
|
||||
});
|
||||
|
||||
icon.connect("destroy", function() {
|
||||
Main.panel._rightBox.disconnect(icon._proxyAlloc);
|
||||
clickProxy.destroy();
|
||||
});
|
||||
|
||||
clickProxy.connect('button-release-event', function(actor, event) {
|
||||
icon.click(event);
|
||||
});
|
||||
|
||||
icon._clickProxy = clickProxy;
|
||||
|
||||
|
||||
Main.uiGroup.add_actor(clickProxy);
|
||||
|
||||
// add the box to the right panel, always at position 0
|
||||
Main.panel._rightBox.insert_child_at_index(box, 0);
|
||||
|
||||
icons.push(icon);
|
||||
}
|
||||
|
||||
function onTrayIconRemoved(o, icon) {
|
||||
//global.log("removing tray icon " + icon.title);
|
||||
|
||||
// we could set the title in java, HOWEVER because of race conditions, it's not consistent. So we check for 'java'
|
||||
if (icon.title !== "java") {
|
||||
orig_onTrayIconRemoved(o, icon);
|
||||
return;
|
||||
}
|
||||
|
||||
let parent = icon.get_parent();
|
||||
|
||||
if (parent) {
|
||||
parent.destroy();
|
||||
}
|
||||
|
||||
icon.destroy();
|
||||
icons.splice(icons.indexOf(icon), 1);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user