Betterbird / thunderbird-patches

Betterbird is a fork of Mozilla Thunderbird. Here are the patches that provide all the goodness.
Other
468 stars 20 forks source link

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

Closed dark-penguin closed 11 months ago

dark-penguin commented 1 year ago

This happens at least on KDE https://github.com/Betterbird/thunderbird-patches/issues/198#issuecomment-1712515855 , on MATE https://github.com/Betterbird/thunderbird-patches/issues/198#issuecomment-1712483412 , and on Gnome https://github.com/Betterbird/thunderbird-patches/issues/198#issuecomment-1712555247 , regardless of whether the window was previously minimized or not https://github.com/Betterbird/thunderbird-patches/issues/198#issuecomment-1712512456 .

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

https://github.com/Betterbird/thunderbird-patches/issues/198#issuecomment-1712515855 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 commented 1 year ago

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 commented 1 year ago

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:

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 commented 1 year ago

Interesting research. We tried gtk_window_deiconify and it didn't do the job (https://github.com/Betterbird/thunderbird-patches/issues/111#issuecomment-1694683944).

dark-penguin commented 1 year ago

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 commented 1 year ago

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

Betterbird commented 1 year ago

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 commented 1 year ago

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

Betterbird commented 1 year ago

Haha: image image

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

dark-penguin commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

Without having read the linked articles, how does the call know that we're not faking the timestamp? Or the WM logs all clicks and we need to refer to a valid one? By how many milli/micro/nanoseconds?

We have no access to the click event, activation comes via a callback that triggers when libayatana-appindicator receives activation from the system tray. The click event is about two levels away.

https://github.com/Betterbird/thunderbird-patches/blob/main/115/features/13-feature-linux-systray-activate.patch https://github.com/Betterbird/thunderbird-patches/blob/main/115/features/13-feature-linux-systray-activate-xfce.patch https://github.com/Betterbird/thunderbird-patches/blob/1570b1a3ac4f99edb5e0a531c9f8baec03da892a/115/features/13-feature-linux-minimise-to-tray.patch#L368

dark-penguin commented 1 year ago

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 commented 1 year ago

libayatana-appindicator doesn't see the event either, it gets activated via the system tray app which does process the click (or double-click on Gnome), see the first patch I quoted. From their code into ours should be in the realm of nanoseconds. So no, neither us nor libayatana-appindicator has access to the raw event.

We can try the "with time" variant with a fake timestamp of the time of the call. That shouldn't be much later than the time of the click. However, if the activation happens on button down and there is an immediate button up or the user clicks and moves, there may be an event in between. That could make matters worse. We'll try it.

What's the deal with GTK4?

dark-penguin commented 1 year ago

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 commented 1 year ago

I meant: when does GTK4 ship?

dark-penguin commented 1 year ago

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

ebassi commented 1 year ago

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

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 commented 1 year ago

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 commented 1 year ago

...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 commented 1 year ago

...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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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

Betterbird commented 1 year ago

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 commented 11 months ago

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 commented 11 months ago

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 commented 11 months ago

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 commented 11 months ago

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 commented 11 months ago

I've added another patch on top, will merge that eventually: https://github.com/Betterbird/thunderbird-patches/commit/14fe9f1e2e3084f34f487f6491aba83b5edbd1bd

Betterbird commented 11 months ago

Try this: https://www.betterbird.eu/downloads/LinuxArchive/betterbird-115.3.0-bb13-latest-build.en-US.linux-x86_64.tar.bz2 I've only tested Xfce where it works as before.

dark-penguin commented 11 months ago

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 commented 11 months ago

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.