Lafydev/wingpanel-indicator-ayatana

Support for Wine systray icons

Closed this issue · 15 comments

First of all thanks for keeping this project alive. I find it extremely useful.
Do you think it's possible to add support for Wine systray icons like mentioned here:

https://github.com/eth-p/wingpanel-indicator-ayatana/issues/10

Or is something that needs to be rather fixed in Wine instead?
I tried the latest version from your repo (2.0.7) but Wine applications systray icons are still displayed in a separate window instead of wingpanel.

Thanks!

I think, it comes from wine's indicator.
But, to be sure, I'd like to know, is this special to elementary/Pantheon or not ?

I also suspect it may be how Wine is using the indicators.
Having said that I don't think this is exclusive to elementary/Pantheon, but I can confirm by testing in a Debian 10/Xfce 4 system I have available and report back.

It seems that in Debian 10/Xfce 4 the Wine app systray icon (Garmin Express in this case) is correctly added to the Xfce panel notification area (systray) next to the other indicators for native applications. In the example below, Transmission is running natively while Garmin Express is running in Wine.

image

Thanks, it's the good example, the xfce panel is developed with Gtk, like elementary. I'll try to find how they deal with it, if I can...

I currently use a trayer to hide the wine system tray.
Maybe is easy to debug and search how this work instead of xfce-panel

I am trying understand how wine system tray work, and I was successful in showing "A System Tray in openned" when I open battlenet
Is important run this app before open a app that generate Tray Icons

#include <gtk/gtk.h>
#include <gdk/gdkx.h>

void __GdkFilterFunc (GdkXEvent *xevent,
					  GdkEvent *event,
					  gpointer  data)
{
    g_print("A System Tray in openned\n");
}

void playground(GtkWidget *window)
{
    g_print("playground\n");
    Screen *xscreen = GDK_SCREEN_XSCREEN(gtk_widget_get_screen(GTK_WIDGET(window)));
    g_assert(xscreen != NULL);

    GdkScreen *screen = gdk_display_get_screen (gdk_x11_lookup_xdisplay (DisplayOfScreen (xscreen)),
                            XScreenNumberOfScreen (xscreen));
    g_assert(screen != NULL);

    GtkWidget *invisible = gtk_invisible_new_for_screen (screen);
    g_assert(invisible != NULL);

    gtk_widget_realize (invisible);

    gtk_widget_add_events (invisible, GDK_PROPERTY_CHANGE_MASK | GDK_STRUCTURE_MASK);
    char *selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d",
					 XScreenNumberOfScreen (xscreen));

    g_print("%s\n", selection_atom_name);

    Atom selection_atom = XInternAtom (DisplayOfScreen (xscreen), selection_atom_name, False);
    g_assert(selection_atom != NULL);
    g_free (selection_atom_name);

    GdkWindow *invisible_window = gtk_widget_get_window(invisible);

    guint32 timestamp = gdk_x11_get_server_time (invisible_window);
    g_print("timestamp: %u\n", timestamp);

    XID invisible_xid = gdk_x11_window_get_xid(invisible_window);

    XSetSelectionOwner (DisplayOfScreen (xscreen), selection_atom,
		     invisible_xid, timestamp);

    Window owner = XGetSelectionOwner (DisplayOfScreen (xscreen), selection_atom);

    if (owner == invisible_xid)
    {
        g_print("OK\n");
        gdk_window_add_filter(invisible_window, __GdkFilterFunc, NULL);
    }
    else
    {
        gtk_widget_destroy (invisible);
    }
}

static void
activate (GtkApplication* app,
          gpointer        user_data)
{
  GtkWidget *window;

  window = gtk_application_window_new (app);
  gtk_window_set_title (GTK_WINDOW (window), "Window");
  gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
  gtk_widget_show_all (window);

  playground(window);
}

int
main (int    argc,
      char **argv)
{
  GtkApplication *app;
  int status;

  app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  status = g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);

  return status;
}

And Makefile

CC ?= gcc
PKG_CONFIG ?= pkg-config

LIBS = $(shell $(PKG_CONFIG) --libs gtk+-3.0 gdk-pixbuf-2.0 x11)
INCS = $(shell $(PKG_CONFIG) --cflags gtk+-3.0 gdk-pixbuf-2.0 x11)
CFLAGS ?= -O2 -Wall

%.o: %.c
	$(CC) $(CPPFLAGS) $(CFLAGS) $(INCS) -c $<

SRC = main.c
OBJ = $(SRC:%.c=%.o)
DEP = $(SRC:%.c=%.dep)

TARGET = systray
$(TARGET): $(OBJ)
	$(CC) $(LDFLAGS) $(OBJ) -o $@ $(LIBS)

@msmaldi Thank you very much, if you compare with what marcussana found out in wine's code :
if ((systray_window = get_systray_selection_owner( display ))) dock_systray_icon( display, icon, systray_window ); else add_to_standalone_tray( icon );

the function 'get_systray_selection_owner' only calls and return the result of XGetSelectionOwner ( it's the same in your trayer's code) but in our case ( with elementary) the result seems to be None.

I'm not very sure about these x11 functions, but these line seek wine's tray
selection_atom_name = g_strdup_printf ("_NET_SYSTEM_TRAY_S%d", XScreenNumberOfScreen (screen)); Atom selection_atom = XInternAtom (DisplayOfScreen (screen), selection_atom_name, False) and then call a XSetSelectionOwner.
The same code is integrated to cinnamon

I try make a minimal tray, and get this:
gtk3-systray

it is xfce-systray with gtk3

I am trying find source code of cinammon or wine systray but not found

Hi, thank you very much for your help, I'll take a look and come back to you

In wine systray (found by marcussana, not me) I think the lines before would solve the problem here, but we can't easily modify so a big project : https://github.com/wine-mirror/wine/blob/6d801377055911d914226a3c6af8d8637a63fa13/dlls/winex11.drv/systray.c#L734-L737

I found the code from cinnamon in linux mint: https://github.com/linuxmint/cinnamon/blob/master/src/tray/na-tray-manager.c

I think we are a level under ayatana with these x functions, I don't see how I can integrate it in ayatana.
There's two other possibilities : a trayer like yours that would copy each new wine indicator in the panel and close wine's tray on an event (that I haven't yet found)
or an extension in javascript like Topiconsfix, but is this compatible with elementary ? https://extensions.gnome.org/extension/1674/topiconsfix/

Extensions in javascript is for gnome, elementary has no core.
I think is better create a WingPanel separated of Ayatana.
A WingPanel exclusive for legacy tray.
I gonna try make a gtk-window in Vala and import this C code adding valapi

I finished a wingpanel indicator for X11, based in mate desktop, it work, but have many work to do.
wingpanel-indicator-na-tray

I encourage you, but I can't follow you on this way, I like too much the wingpanel to replace it by another one... Perhaps would you consider to propose a PR to elementary/wingpanel ?

I encourage you, but I can't follow you on this way, I like too much the wingpanel to replace it by another one... Perhaps would you consider to propose a PR to elementary/wingpanel ?

Its work like ayatana and you can use it side by side

@msmaldi, I tried it and it works, you're right ! But there's a lot of warnings to work on, I'll try to help you... You made a lot of versions !

I added a link to your work in README, so we can close this issue. Thank you very much !