Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clicking the tray icon does not raise the window on Linux #202

Closed
dark-penguin opened this issue Sep 9, 2023 · 47 comments
Closed

Clicking the tray icon does not raise the window on Linux #202

dark-penguin opened this issue Sep 9, 2023 · 47 comments
Labels

Comments

@dark-penguin
Copy link

dark-penguin commented Sep 9, 2023

This happens at least on KDE #198 (comment) , on MATE #198 (comment) , and on Gnome #198 (comment) , regardless of whether the window was previously minimized or not #198 (comment) .

According to Qt docs I remember once reading, functions like gtk_windows_present() don't work regardless of the library - probably because they use the same call that window managers ignore for sake of "focus steal prevention". The recommended solution from that same documentation is to create a child window (a simple messagebox will do fine) and then hide it a few milliseconds later (in my experience, 10 ms is enough, but any less might not be). That would raise the window on any window manager.

@Betterbird
Copy link
Owner

The first line of the report isn't accurate. For KDE/Gnome the window was always brought forward when minimised before. This will have improved via Issue #200 for Gnome and KDE, so even non-minimised windows will now be brought forward. The one missing out is Mate (and likely Cinnamon), it works on Xfce.

@Betterbird
Copy link
Owner

So I've been playing with my Mate installation. Setting gsettings set org.mate.panel enable-sni-support true for me leads to the total loss of the BB icon in the system tray 😢. Ignoring that, I do this:

  1. Start a new composition to myself.
  2. Put a terminal window on top of the main window, than minimise the latter. Effect, main window gone, no icon.
  3. Send message.
  4. Wait for notification, click it: Main window comes back at the front and is focused.

gsettings set org.mate.panel enable-sni-support false brings back the system tray icon after logoff/login.

So this is a can of worms of Mate, its windows manager (likely I'm using a different one, IIRC I'm using lightdm since this came with the original Mint/Xfce installation) and the gsettings 👎 💢 🤯

@dark-penguin
Copy link
Author

dark-penguin commented Sep 10, 2023

I have two machines (a real one and a test one) running different versions of Debian with MATE, and never encountered the "no icon" situation. Are you sure you did a logoff/login after enabling SNI support, and not only after disabling it?

But the issue with the window appearing below other windows or not being raised properly is not always reproducible - sometimes it would work a few times, and sometimes it wouldn't. %) Let's see...

Hm, it seems like it works correctly at first (appears raised), but it stops working (starts appearing below other windows) after something happens. I did some testing, and I think what causes it to break is "clicking the interface"!

Testing setup: I open one folder (one window), start a terminal (one more window), then launch BB from the terminal.

-> It appears properly, with an icon (I have "always" enabled)

I minimize BB, "do the shuffle" (click folder, click terminal, click folder, click terminal), click tray icon

-> BB appears on top of other windows, i.e. properly

(This can be repeated multiple times, and it works every time)

Next, I click the tray icon (BB appears properly), then click any button in BB ("Write", "Quick Filter", or even simply a different mail folder). Then I minimize BB, "do the shuffle" (this step is important! otherwise no magic happens!), and click tray icon again

-> BB appears below the topmost window - between the topmost window and the "next" window!

@Betterbird
Copy link
Owner

Are you sure you did a logoff/login after enabling SNI support, and not only after disabling it?

gsettings set org.mate.panel enable-sni-support true, restart, icon lost, the activation experiment, gsettings set org.mate.panel enable-sni-support false, logoff/logon, icons back.

I don't see why you need a restart, the UI should be set up after logon. Be that as it may, icons disappeared after restart, reappeared after logoff/logon.

I really don't think the experience you've described in the previous comment justifies any more hacky coding effort (of creating and hiding unwanted windows just to pull others forward) on the overall flaky Mate desktop. How about you start a discussion with its maintainers?

@Betterbird
Copy link
Owner

We're more interested in why gsettings set org.mate.panel enable-sni-support true loses the icon completely. That the current recommendation for Mate which we have to withdraw since it breaks the thing completely.

@dark-penguin
Copy link
Author

If MATE was the only one with this issue, then I would agree that it doesn't make much sense to pursue. However, people were reporting the same things from both Gnome and KDE too.

I'm setting up more test machines. On KDE, the icon does not appear. On Gnome, not only the icon does not appear, but I can't even figure out how to minimize a window. Everything you are describing - including "no icon" - is applicable to all three of them. I'll continue trying other environments.

@Betterbird
Copy link
Owner

No. KDE works fine, activation, window to the foreground. Based on our own testing and user feedback in related tickets.
For Gnome you need install the "AppIndicator and KStatusNotifierItem Support" extension. It works there as well, only Gnome is missing the tooltip (by design) and activation doesn't move the window forward. Xfce works as well as KDE.

For now we have no intention to action this ticket.

@dark-penguin
Copy link
Author

dark-penguin commented Sep 10, 2023

#198 (comment)
This is the behaviour I see on KDE. If the window is already out, then it's highlighted in the taskbar, but not raised. If the window is minimized, then it's restored (yes, above all windows) and highlighted, but not actually focused (that's not easy to spot with the default KDE theme - select some message to see whether it's actually active or not). Don't forget to 1) click another message or folder or button, and 2) "do the window shuffle", because until you do, everything pretends to work!

@dark-penguin
Copy link
Author

A trick to confirm that the window is not focused when restored: focus a terminal window before you click the tray icon. After you restore the BB window, press arrow keys on the keyboard, and you'll see that it does not move the highlighting on messages - instead, it falls to the terminal, which appears to be in the background.

I'd say MATE does a better job by at least correctly showing the focused window actually on top, which is why this is easier to spot in MATE. :)

@dark-penguin
Copy link
Author

https://docs.gtk.org/gtk3/method.Window.present.html says:

[gtk_window_present] This function should not be used as when it is called, it is too late to gather a valid timestamp to allow focus stealing prevention to work correctly.

Unfortunately, it does not say what to use instead. So far I've tried:

  • gtk_window_present
  • gtk_window_set_keep_above
  • gtk_window_deiconify
  • gtk_widget_show_all
  • gtk_widget_grab_focus

None of those work. %)

I'm looking for a way to raise the window in GTK - something that, allegedly, "just works" in the Python bindings for GTK.

@Betterbird
Copy link
Owner

Interesting research. We tried gtk_window_deiconify and it didn't do the job (#111 (comment)).

@dark-penguin
Copy link
Author

Here is the code I'm using to test what helps. Just put any attempts into the first function.

(Is it possible to hide it under a spoiler or something?..)

#include <gtk/gtk.h>

// Install libgtk-3-dev
// Compile this file with:
// gcc `pkg-config --cflags gtk+-3.0` -o test test.c `pkg-config --libs gtk+-3.0` -Wno-incompatible-pointer-types

// All the other attempts to raise a window
static gboolean above(GtkWindow* window) 
{
  g_print("Raising...\n");

  // gtk_window_set_keep_above(GTK_WINDOW(window), TRUE);
  // gtk_widget_show_all (window);
  // gtk_window_set_keep_above(GTK_WINDOW(window), FALSE);

  // gtk_widget_grab_focus(window);
  // gtk_window_deiconify(GTK_WINDOW(window));

  gtk_window_present(GTK_WINDOW(window));

  return FALSE;
}

// Attempts to raise a window using a popup
static gboolean popup(GtkWindow* window)
{
  g_print("Popup...\n");
  GtkMessageDialog *dialog = gtk_message_dialog_new (GTK_WINDOW(window),
                                  GTK_DIALOG_MODAL,
                                  GTK_MESSAGE_INFO,
                                  GTK_BUTTONS_CLOSE,
                                  "Temporary Popup");

  // Non-blocking
  g_signal_connect(dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog);
  gtk_widget_show_all(GTK_WINDOW(dialog));

  // Scheduling destruction
  g_print("Scheduling a dialog destruction...\n");
  g_timeout_add(1000, G_CALLBACK(gtk_widget_destroy), dialog);

  return FALSE;  // Stop re-running the timeout
}

// Minimize a window and set a 3-second timeout to do "The Shuffle" before raising it back
static gboolean button_clicked(GtkWidget* button, GtkWindow* window)
{
  g_print("Minimizing...\n");
  gtk_window_iconify(GTK_WINDOW(window));

  // Proof that "gtk_window_present" does not raise the window properly
  // g_timeout_add(3000, G_CALLBACK(gtk_window_present), GTK_WINDOW(window));

  // A popup does not work either!..
  // g_print("Scheduling a raise via popup...\n");
  // g_timeout_add(3000, G_CALLBACK(popup), GTK_WINDOW(window));

  g_print("Scheduling a raise via set_keep_above...\n");
  g_timeout_add(3000, G_CALLBACK(above), GTK_WINDOW(window));

  return FALSE;  // Stop re-running the timeout
}

// Apparently you need this in a separate function to automatically pass a style
static void activate(GtkApplication* app, gpointer user_data)
{
  GtkWidget *window;

  window = gtk_application_window_new (app);
  gtk_window_set_title (GTK_WINDOW (window), "CUNT");
  gtk_window_resize (GTK_WINDOW (window), 800, 600);
  gtk_window_move(GTK_WINDOW (window), 600, 300);
  gtk_widget_show_all (window);

  GtkWidget *button = gtk_button_new_with_label("Minimize and then restore");
  g_signal_connect(GTK_BUTTON(button), "clicked", G_CALLBACK(button_clicked), GTK_WINDOW(window));

  gtk_container_add(GTK_CONTAINER(window), button);
  gtk_widget_show(button);
}

// Entrypoint
int main(int argc, char **argv)
{
  GtkApplication *app;
  int retval;

  app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);

  retval = g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);

  return retval;
}

@dark-penguin
Copy link
Author

Let's see what the GTK developers can say about this...
https://gitlab.gnome.org/GNOME/gtk/-/issues/6095

@Betterbird
Copy link
Owner

I used to have a Gnome/GIMP account ages ago, now they've changed everything. Please let me know if this ticket moves forward.

@dark-penguin
Copy link
Author

Their Gitlab accepts "Log in with Github", if you want to join. :)

@Betterbird
Copy link
Owner

Haha:
image
image

External identity providers such as Google and GitHub have been disabled due to an influx of spam.

@dark-penguin
Copy link
Author

Hm, that's strange, I just registered there with Github... Maybe I already had an existing account from long ago, that was already bound to Github?..

@dark-penguin
Copy link
Author

There is an update: https://gitlab.gnome.org/GNOME/gtk/-/issues/6095#note_1843146
Basically, GTK not having a working and documented function to raise a window means "there's something wrong with your code, go ask on the forums". :)
So I did: https://discourse.gnome.org/t/how-to-raise-a-window-in-gtk3-i-e-unminimize-it-bring-it-on-top-of-other-windows-and-focus-it/17100

@Betterbird
Copy link
Owner

Betterbird commented Sep 12, 2023

The Gnome response is disappointing. As you rightly pointed out, the documentation is incomplete and should be fixed. Also, if Gtk offers that function, what would have been wrong in saying: Do x, y and z and for further details, go to the forums.

KDE is more helpful, here https://bugs.kde.org/show_bug.cgi?id=464264#c9 they helped me to fix libappindicator, and here https://bugs.kde.org/show_bug.cgi?id=465438#c4 they also suggested to take it to a forum.

A few days ago I tested on KDE again. Without minimising BB, so just putting some other window on top, the activation is patchy. Sometimes the window is raised to the front, sometimes the (Windows speak) taskbar button is focused and the window stays where it was. I wonder whether it's worth filing another KDE bug. That said, I think it may be a problem of the window manager. As I wrote, on my test VM I'm using lightdm since that came with Xfce initially. So it doesn't make sense to point the finger at the desktop when it's used with a non-compatible windows manager.

EDIT: Note https://bugs.kde.org/show_bug.cgi?id=465438#c8.

@dark-penguin
Copy link
Author

The Gnome response is disappointing. As you rightly pointed out, the documentation is incomplete and should be fixed. Also, if Gtk offers that function, what would have been wrong in saying: Do x, y and z and for further details, go to the forums.

Actually, they did after my clarification. And there is a response on the forum, with a link to StackExchange. So, as I understand it, you are supposed to grab the timestamp from the tray-icon-click event, and pass it to present_with_time(), then the window manager can make a decision about focusing the window. Otherwise, apparently the window manager thinks "by default, this is an already outdated request, so no focus for you". For now, I can't figure out enough of the sources to grab the click event and pass its timestamp, but maybe it's more clear for you.

I assume this problem is with GTK, and it does not depend on the window manager. Using present() does not present the window, as mentioned in the documentation, so there is little difference what WM is used - it won't work without providing a timestamp. If we get the GTK call correct, then it should work regardless of the WM - except in situations where it really does make sense to not raise the window due to focus steal prevention working correctly.

@Betterbird
Copy link
Owner

Betterbird commented Sep 12, 2023 via email

@dark-penguin
Copy link
Author

I'd assume the WM does not care if you're lying. But the timestamp must be between the last event and the current time, otherwise it's either outdated or invalid.

Well, would it be possible to traverse those two levels? There are NULLs that seem perfectly suitable to pass it as the only parameter, and you can modify libayatana to do that if I understand correctly. :)

This situation is already fixed in GTK4, and present() apparently simply works as expected there.

@Betterbird
Copy link
Owner

Betterbird commented Sep 12, 2023 via email

@dark-penguin
Copy link
Author

dark-penguin commented Sep 12, 2023

GTK4 "just works":

For GTK4 we don't want people to use present_with_time(): present() should do the right thing by default, and most of the time people don't have access to windowing system events and extract their serial.

I don't know the specifics of the timestamp stuff - those are just my guesses based on common sense: the timestamp shouldn't be in the future, and it shouldn't be too long in the past. I can't imagine why would the WM check your timestamp for anything else. So I think anywhere between the click and current time should be fine.

@Betterbird
Copy link
Owner

Betterbird commented Sep 12, 2023 via email

@dark-penguin
Copy link
Author

December 16, 2020. But the question is not "when does it ship", but "when does Thunderbird migrate to it". %)

@ebassi
Copy link

ebassi commented Sep 12, 2023

Just to clarify, from the perspective of a GTK development team member:

  • GTK 4 is already available; 4.0 was released in December 2020
  • GTK 4 does not have tray icons in the API
  • GTK 3 is mainly X11-oriented in terms of API; that's why there's both a present_with_time() and a present() methods; additionally, there are more event-based API
  • GTK 4 is mainly Wayland-oriented, and events are de-emphasises; this means that it's harder to get an event serial, and internally the toolkit should be able to deal with that, rather than deferring to the app developer.

We are aware that the documentation is a bit iffy, at the moment; the GTK3 docs should be fixed to reflect the present_with_time()/present() split, and the GTK4 docs should concentrate on the present() method. We are also discussing deprecating gtk_window_present_with_time() for GTK4.

For the time being and for GTK3, we recommend using gtk_window_present_with_time() whenever you have a GdkEvent, and gtk_window_present() if you don't. Ideally, the window manager should be able to cope with that, especially when it comes to events generated by the tray icon; the main problem is that tray icons defer everything to the application's process, but since those icons are a form of IPC that is not under the usual windowing system event loop, the toolkit does not have a full view of the event serials. This gets worse when dealing with things like libappindicator, which uses a completely different IPC mechanism, and may or may not fall back to X11-specific protocols like XEMBED depending on platform capabilities.

@ebassi
Copy link

ebassi commented Sep 12, 2023

As a side note: applications should never fake event serials. Either they should use a serial from the latest event they have, or they should use 0 as a way to match the latest event known to the window manager.

@dark-penguin
Copy link
Author

...And by specifying 0, you get the same behaviour as the default present().

So could it be possible to grab a serial from some other event, like for example... the window unminimizing? And then pass that serial to "...and also raise it".

@ebassi
Copy link

ebassi commented Sep 12, 2023

...And by specifying 0, you get the same behaviour as the default present().

Yes.

So could it be possible to grab a serial from some other event, like for example... the window unminimizing?

No, because that particular action doesn't necessarily come with an event. You can look at the serial for the event at the top of the event queue, using g_get_current_event_time() in GTK3, but that would not do anything for things like activation through notifications, for instance. I'm also not sure if that would work with actions coming from libappindicator, since, as I said, those don't involve the windowing system.

@dark-penguin
Copy link
Author

So that's nearly impossible. But how come window activation works just fine in Thunderbird 45 with FireTray... T_T

https://github.com/foudfou/FireTray/blob/275a3c9f648787f80f16e8ba33fd99de59c9cd67/src/modules/linux/FiretrayWindow.jsm#L414C3-L414C3

It's just using present(), and it just works every time!

@ebassi
Copy link

ebassi commented Sep 12, 2023

That code looks X11-specific, and I assume it relies on the tray icon being a real windowing system surface embedded into another window using the XEMBED protocol; whereas the crux of this issue seems to be that you're using libappindicator, which normally does not embed windowing system surfaces (except as a fallback) and instead activates processes through D-Bus.

@dark-penguin
Copy link
Author

So we are royally out of luck.

Come to think of it... Why do my PyQt applications properly raise when I create a modal messagebox? I mean, of course there might be some Qt magic sauce that would take too much effort to figure out, but isn't a modal messagebox supposed to raise a window? Maybe that would generate some events we could capture? Failing that, maybe there is something else we can do to generate a suitable event?

And by the way, how did you solve this problem in GTK4? Maybe that could help us build some workaround.

@dark-penguin
Copy link
Author

the crux of this issue seems to be that you're using libappindicator, which normally does not embed windowing system surfaces (except as a fallback) and instead activates processes through D-Bus.

Hmm, "except as a fallback"... Another idea is to try and force that fallback - which should also fix the issue with sometimes not working without SNI, and sometimes not working with SNI.

@dark-penguin
Copy link
Author

Or we could avoid using ayatana and use native GTK facilities for a tray icon - they not removed yet in GTK3, are they?.. I can't find any examples newer than GTK2 though. %)

@Betterbird
Copy link
Owner

Look, Betterbird is about improving Thunderbird, and we have a lot of expertise in this area. We don't have any expertise in GTK, D-Bus or all the the other stuff mentioned here. We've chosen libayatana-appindicator beacuse the API was rather simple and it had tooltips and even some activation callback.

If you want to replace the code with a different library, feel free to supply patches that do that. The patches we're currently using are all here and they start with 12- and 13-:

Patches to add libayatana-appindicator as third party code, make it compile, add tooltips and a sample program:
12-feature-linux-systray.patch
12-feature-linux-systray-compile.patch
12-feature-linux-systray-tooltip.patch
12-feature-linux-systray-no-root.patch
12-feature-linux-systray-example.patch

Integration with BB, mostly in nsMessengerUnixIntegration.cpp:
12-feature-linux-systray-betterbird.patch

These two patches add activation to libayatana-appindicator, they are from PR 71
13-feature-linux-systray-activate.patch
13-feature-linux-systray-activate-xfce.patch

Finally we have
13-feature-linux-minimise-to-tray.patch ### Depends on 1848420
13-feature-linux-minimise-to-tray-restore-window.patch
13-feature-linux-minimise-to-tray-fix-activate.patch
which will soon be folded into one and which integrate the activation from the other 13- patches into BB, again, mostly in nsMessengerUnixIntegration.cpp.

So if you want to switch from libayatana-appindicator to a different library and adjust nsMessengerUnixIntegration.cpp accordingly, go right ahead.

@dark-penguin
Copy link
Author

Yeah, I'm considering trying it - I have no expertise in GTK or C, but I don't mind learning that as it's generally relevant to me as a heavy Linux user/developer . This is likely not the last time I'm fixing a GTK application. :)

Seems like those patches go on top of each other. Is there some development command you are using to apply all patches correctly and then store changes back into patches? It does not look like you're using quilt, so I wonder about your development workflow. Storing fork changes as patch-series is something I don't often encounter except in Debian packages maintained with quilt.

@dark-penguin
Copy link
Author

@ebassi Do you have any suggestions about what we could do?

  • What events produce suitable timeserials? Maybe a messagebox popup? Or something else we could get automatically.
  • Use native GTK3 tray API? It's not removed yet, is it? Any documentation/examples? (I've only found 2 outdated examples)
  • Could ayatana be forced into that "fallback mode" that would be suitable?
  • Or maybe there's another library you are aware of that has a way to do this?
  • Maybe there's some insight in how you've solved this problem for GTK4 ?

@Betterbird
Copy link
Owner

Is there some development command you are using to apply all patches correctly ...

You basically need to build the entire application: https://github.com/Betterbird/thunderbird-patches/blob/main/README.md. That uses Mercurial queues and you can replace patches. My suggestion would be to remove the ones you don't want and add changed to the top of the queue. We can do the integration later.

@dark-penguin
Copy link
Author

An interesting observation: I've switched to the "Thunderbird notification" instead of the system one, and now clicking the notification raises the window correctly.

So, once we substitute a popup from a separate application with a popup owned by BB, it suddenly works with exactly the same code. I would assume the timestamp gets automatically passed to get_window_present(), but seems like it is not, since it's suggested to pass the timestamp manually. I wonder what's actually going on there.

@Betterbird
Copy link
Owner

I have no idea how the "system notification" interfaces with TB/BB. On Windows, it isn't any good, so I don't use it.

@dark-penguin
Copy link
Author

I've tried everything I could, and now I'm out of ideas.

The problem here is GTK - they have a vision that "the future of computing does not have tray icons", so they've removed tray icon support. There is libayatana to partly make up for that, but it can't send the necessary data to the main application. There is no other library to replace it, and doing it "the old way" via XEMBED is said to "work poorly". I've found some code that seems to create an invisible tray icon, but I don't know how to assign an actual icon to it, or put any actions on that icon. And it seems too complex anyway.

#include <stdio.h>
#include <gtk/gtk.h>
#include <xcb/xcb.h>
#include <xcb/xcb_icccm.h>

static xcb_atom_t get_atom_from_name(xcb_connection_t *xc, char* name){
	xcb_intern_atom_cookie_t cookie = xcb_intern_atom(xc,0,strlen(name),name);
	xcb_intern_atom_reply_t *reply;
	xcb_generic_error_t *error;
	reply = xcb_intern_atom_reply(xc,cookie, &error);
	if(error != NULL){ printf("%d\n", error->response_type); }
	free(error);
	return reply->atom;
}

static xcb_window_t get_manager_selection_owner(xcb_connection_t *xc, int screen){
    #define	TRAY_SEL_ATOM "_NET_SYSTEM_TRAY_S"
	char		*tray_sel_atom_name;
	if((tray_sel_atom_name = (char *)malloc(strlen(TRAY_SEL_ATOM) + 2)) == NULL) {
		exit(EXIT_FAILURE);
	}
	snprintf(tray_sel_atom_name, strlen(TRAY_SEL_ATOM) + 2, "%s%u", TRAY_SEL_ATOM, screen);
	xcb_atom_t atom = get_atom_from_name(xc,tray_sel_atom_name);
	xcb_get_selection_owner_cookie_t cookie1 = xcb_get_selection_owner(xc,  get_atom_from_name(xc,tray_sel_atom_name));
	free(tray_sel_atom_name);

	xcb_get_selection_owner_reply_t *reply1;
	reply1 = xcb_get_selection_owner_reply(xc,cookie1,NULL);
	xcb_window_t owner = reply1->owner;
	free(reply1);
	return owner;
}

static void xcb_systray_message_send(xcb_connection_t *connection, long message, long window, long data2, long data3){
    xcb_client_message_event_t ev;
    xcb_window_t tray = get_manager_selection_owner(connection, 0); // atom

    memset(&ev, 0, sizeof(ev));
    ev.response_type = XCB_CLIENT_MESSAGE;
    ev.window = tray;
    ev.type = get_atom_from_name(connection,"_NET_SYSTEM_TRAY_OPCODE");
    //printf("Viestin tyyppi on %d\n", ev.type);
    ev.format = 32;
    ev.data.data32[0] = XCB_CURRENT_TIME;
    ev.data.data32[1] = message;
    ev.data.data32[2] = window; // <--- your window is only here
    ev.data.data32[3] = data2;
    ev.data.data32[4] = data3;

    xcb_send_event(connection, 0, tray, XCB_EVENT_MASK_NO_EVENT,(const char *)&ev);

    // For older KDE's ...
    long atom_data = 1;
    xcb_atom_t tray_atom = get_atom_from_name(connection, "KWM_DOCKWINDOW");
    xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, tray_atom, tray_atom, 32, 1, (unsigned char*) &atom_data);

    // For more recent KDE's...
    tray_atom = get_atom_from_name(connection, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR");
    long xa_window_atom  = get_atom_from_name(connection, "XA_WINDOW");
    xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, tray_atom, xa_window_atom, 32, 1, (unsigned char*) &window);

    // A minimum size must be specified for GNOME and Xfce, otherwise the icon is displayed with a width of 1
	xcb_size_hints_t hints;
	xcb_icccm_size_hints_set_min_size(&hints,22,22);
	xcb_icccm_set_wm_normal_hints(connection,window,&hints);

    usleep(100000); // Wait for signal to get to bar.
}

static xcb_window_t xcb_status_icon_create(){
    xcb_connection_t *connection = xcb_connect(NULL, NULL);
    const xcb_setup_t *xcb_setup = xcb_get_setup (connection);
    xcb_screen_t *xcb_screen = xcb_setup_roots_iterator(xcb_setup).data;
    xcb_window_t xcb_window = xcb_generate_id (connection);
    xcb_create_window(connection, XCB_COPY_FROM_PARENT, xcb_window, xcb_screen->root, 0, 0, 22, 22, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, xcb_screen->root_visual, 0, NULL);
    xcb_systray_message_send(connection, 0, xcb_window, 0, 0);
    xcb_flush(connection);
    return xcb_window;
}

// Apparently you need this in a separate function to automatically pass a style
static void activate(GtkApplication* app, gpointer user_data) {
    GtkWidget *window;

    window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (window), "Tray icon test");
    gtk_window_resize (GTK_WINDOW (window), 800, 600);
    gtk_window_move (GTK_WINDOW (window), 600, 300);
    gtk_widget_show_all (window);

    GtkWidget *button = gtk_button_new_with_label("Minimize and then restore");
    //g_signal_connect(GTK_BUTTON(button), "clicked", G_CALLBACK(button_clicked), GTK_WINDOW(window));
    // That code is deleted!

    gtk_container_add(GTK_CONTAINER(window), button);
    gtk_widget_show(button);

    // Apparently "deprecated" means "not working anymore"!
//    GtkStatusIcon *trayIcon  = gtk_status_icon_new_from_file ("icon.png");
//    gtk_status_icon_set_visible(trayIcon, FALSE); //set icon initially invisible

    // Try the stuff I've found
    xcb_status_icon_create();
}

// Entrypoint
int main(int argc, char **argv) {
    GtkApplication *app;
    int retval;

    app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);

    retval = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);

    return retval;
}

@dark-penguin
Copy link
Author

And old friend helped me, and seems like this works! I've tried it on two MATE machines and one KDE machine - it works every time!

I've only tried my examples and not actually rebuilt BB, but the change is trivial - and it was actually kinda your idea in the very beginning. :)

@Betterbird
Copy link
Owner

I've added another patch on top, will merge that eventually:
14fe9f1

@Betterbird
Copy link
Owner

@dark-penguin
Copy link
Author

It works!! On MATE and KDE at least! On MATE, with SNI (gsettings set org.mate.panel enable-sni-support true). Clicking the icon properly opens the window, which is above other windows and focused, even if you shuffle windows before opening it.

@Betterbird
Copy link
Owner

Be our guest!

Thanks for the contribution, the insistance, the research, the testing, etc. This will ship officially in 115.3.1 likely next week.

ma8ma added a commit to JDimproved/JDim that referenced this issue Mar 30, 2024
起動オプションがマルチモード(--multi)ではなく、メインプロセスでもない
場合はメインプロセスのウインドウを最前面に表示するように起動処理を
変更します。

これはGNOMEをはじめとするGTKアプリケーションに導入されている
アプリケーションの一意性の管理(多重起動の防止)に基づいた挙動です。

注意
「ウインドウを最前面に表示する」動作はデスクトップ環境によって
挙動が異なる可能性があります。

参考文献
Betterbird/thunderbird-patches#202
ma8ma added a commit to JDimproved/JDim that referenced this issue Mar 30, 2024
…#1371)

起動オプションがマルチモード(--multi)ではなく、メインプロセスでもない
場合はメインプロセスのウインドウを最前面に表示するように起動処理を
変更します。

これはGNOMEをはじめとするGTKアプリケーションに導入されている
アプリケーションの一意性の管理(多重起動の防止)に基づいた挙動です。

注意
「ウインドウを最前面に表示する」動作はデスクトップ環境によって
挙動が異なる可能性があります。

参考文献
Betterbird/thunderbird-patches#202
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants