View Issue Details

IDProjectCategoryView StatusLast Update
0038909LazarusLCLpublic2021-06-13 12:29
ReporterDavid Assigned ToJuha Manninen  
PrioritynormalSeveritymajorReproducibilityalways
Status closedResolutionfixed 
Platformx86, x86_64OSDebian 
Product Version2.1 (SVN) 
Summary0038909: Support Ayatana AppIndicator for TrayIcon
DescriptionA patch is provided to allow Debian Bullseye Gnome users display the TrayIcon.

On Gnome Desktop, the end user needs to install libappindicator3-1 and gnome-shell-extension-appindicator to be able to display the Tray Icon.

Debian is about to release Bullseye but have recently removed libappindicator3-1 from their repository, declaring libayatana-appindicator3-1 its replacement. However, while the new library behaves the same, it has a different name. That means that our TrayIcon will not be able to find it. So, Gnome users will not be able to use the Lazarus TrayIcon.

The many Debian derivative distributions will have the same problem, except, perhaps Ubuntu.

The patch cause the UnityWSCtrls unit to try to load the Ayatana library if (and only if) it fails to load the Unity / Canonical libappindicator. And, as before, if the traditional System Tray is available, that will be used as first choice.

The choice to use an AppIndicator has also been updated, instead of having a list of bad desktops, we now call some X methods, more reliable and lower maintenance.

And to support more than one AppIndicator, the env var driven debugging has been tweaked.

Steps To ReproduceRun any Lazarus application using TrayIcon on Debian Bullseye Gnome. As in all cases involving Gnome, the plugin gnome-shell-extension-appindicator must be installed (and desktop restarted) and then enabled. On Bullseye Gnome, the icon does not show.

This patch is not needed for other desktops such as Mate, xfce, plasma etc desktops but has been tested and found to do no harm.

At present, the other major distributions do not seem to have moved to Ayatana but given libappindicator's links to Ubuntu, its seems likely that it may become more widespread.
Additional InformationHas been tested on the following platforms -
U16.04 Mate (T),
U20.04 Gnome (L), Mate (L)
Debian Buster Gnome (A), Plasma (T)
MXLinux193 XFCe (T) , Plasma (T)

key : (T) - Traditional SysTray, (A) Ayanata and AppIndicator Plugin, (U) Unity or Canonical Libappindicator3 and AppIndicator Plugin

I will submit a similar patch for GTK3 is this one is used. Qt5 seems to be a problem ....
TagsNo tags attached.
Fixed in Revisionr65119, r65122, r65214
LazTarget-
WidgetsetGTK 2
Attached Files

Activities

David

2021-05-19 11:23

reporter  

ayatana.patch (4,848 bytes)   
--- lcl/interfaces/gtk2/unitywsctrls.pas	2021-05-19 20:01:55.089486165 +1000
+++ lcl/interfaces/gtk2/unitywsctrls.pas-new	2021-05-19 20:19:31.396310452 +1000
@@ -20,6 +20,10 @@
 { Copyleft implementation of TTrayIcon originally for Unity applications indicators
   Original version 2015 by Anthony Walter sysrpl@gmail.com
 
+  Updated May 2021 to try first the Canonical libappindicator3-1 (Fedora Gnome)
+  then libayatana-appindicator3-1 (Debian Bullseye). Ayatana is a fork of the
+  Canonical one, does not mention Unity or Ubuntu (very much). DRB
+
   Changed October 2019, we now try and identify those Linux distributions that
   need to use LibAppIndicator3 and allow the remainder to use the older and
   more functional SystemTray. Only a few old distributions can use LibAppIndicator_1
@@ -27,7 +31,7 @@
 
   The 'look up table' in NeedAppIndicator() can be overridden.
   Introduce an optional env var, LAZUSEAPPIND that can be unset or set to
-  YES, NO or INFO - YES forces an attempt to use LibAppIndicator3, NO prevents
+  YES, NO or INFO - YES forces an attempt to use one of the AppIndicator3, NO prevents
   an attempt, any non blank value (eg INFO) displays to std out what is happening.
 
   Note we assume this env var will only be used in Linux were its always safe to
@@ -65,14 +69,17 @@
     class function GetPosition(const {%H-}ATrayIcon: TCustomTrayIcon): TPoint; override;
   end;
 
-{ UnityAppIndicatorInit returns true if libappindicator_3 library can be loaded }
+{ UnityAppIndicatorInit returns true if an AppIndicator library can be loaded }
 
 function UnityAppIndicatorInit: Boolean;
 
 implementation
 
+uses gtk2extra, x, xlib, Types;               // Testing for traditional SysTray available
+
 const
-  libappindicator_3 = 'libappindicator3.so.1';
+  libappindicator_3 = 'libappindicator3.so.1';              // Canonical's Unity Appindicator3 library
+  LibAyatanaAppIndicator = 'libayatana-appindicator3.so.1'; // Ayatana - typically called libayatana-appindicator3-1
 
 {const
   APP_INDICATOR_SIGNAL_NEW_ICON = 'new-icon';
@@ -273,15 +280,12 @@
 
   function NeedAppIndicator: boolean;
   var
-    DeskTop : String;
+        A : TAtom;
+        XDisplay: PDisplay;
   begin
-    DeskTop := GetEnvironmentVariableUTF8('XDG_CURRENT_DESKTOP');
-    // See the wiki for details of what extras these desktops require !!
-    if (Desktop = 'GNOME')
-      or (DeskTop = 'Unity')
-      or (Desktop = 'Enlightenment')
-      or (Desktop = 'ubuntu:GNOME') then exit(True);
-    Result := False;
+        XDisplay := gdk_display;
+        A := XInternAtom(XDisplay, '_NET_SYSTEM_TRAY_S0', False);
+        result := (XGetSelectionOwner(XDisplay, A) = 0);
   end;
 
   function TryLoad(const ProcName: string; var Proc: Pointer): Boolean;
@@ -301,23 +305,33 @@
   if UseAppInd = 'NO' then
     begin
     Initialized := False;
-    writeln('APPIND Debug : Choosing to not try AppIndicator3');
+    writeln('APPIND Debug : Choosing to use Traditional SysTray');
     Exit;
   end;
   if (UseAppInd <> 'YES') and (not NeedAppIndicator()) then    // ie its NO or blank or INFO
   begin
     Initialized := False;
     if UseAppInd <> '' then
-       writeln('APPIND Debug : Will not use AppIndicator3');
+       writeln('APPIND Debug : Will use Traditional SysTray');
     Exit;
   end;
   if UseAppInd = 'YES' then                                    // either a YES or OS needs it
      writeln('APPIND Debug : Will try to force AppIndicator3')
   else
-     if UseAppInd <> '' then writeln('APPIND Debug : OS and Desktop request AppIndicator3');
+     if UseAppInd <> '' then writeln('APPIND Debug : OS and Desktop request AppIndicator');
+
   Module := LoadLibrary(libappindicator_3);        // might have several package names, see wiki
-  if Module = 0 then
-     Exit;
+  if Module = 0 then begin
+     if UseAppInd <> '' then                                    // either a YES or OS needs it
+        writeln('APPIND Debug : Failed to load Unity AppIndicator, will try Ayatana');
+     Module := LoadLibrary(LibAyatanaAppIndicator);
+     if Module = 0 then begin
+        if UseAppInd <> '' then
+            writeln('APPIND Debug : Failed to load Ayatana AppIndicator, its likely no SysTray available.');
+        exit(False);
+     end;
+  end;
+
   Result :=
     TryLoad('app_indicator_get_type', @app_indicator_get_type) and
     TryLoad('app_indicator_new', @app_indicator_new) and
@@ -340,7 +354,7 @@
     TryLoad('app_indicator_get_label_guide', @app_indicator_get_label_guide) and
     TryLoad('app_indicator_get_ordering_index', @app_indicator_get_ordering_index);
   if UseAppInd <> '' then
-     writeln('APPIND Debug : AppIndicator3 has loaded ' + booltostr(Result, True));
+     writeln('APPIND Debug : An AppIndicator has loaded ' + booltostr(Result, True));
   Initialized := Result;
 end;
 
ayatana.patch (4,848 bytes)   

CudaText man

2021-05-19 14:58

reporter   ~0130960

In the patch you use indent= 6 spaces in few places. not nice.

Juha Manninen

2021-05-19 17:48

developer   ~0130963

David, you added a dependency to X-Window. It will not compile with Wayland and other alternative display servers. At least the code should be inside an IFDEF.
Wayland will replace X slowly but surely in many distros.

David

2021-05-19 23:59

reporter   ~0130966

Juha, the patched code runs fine on Waylnd systems, but I have not tried to compile on a wayland system. I assumed that if it would run, then the necessary libraries must be there, therefore the dev files must be available. But I agree, was a bad thing to do to have the two changes in the one bug report / patch.

I will make a new patch that address just the new libayatana-appindicator and post it here.

I don't know how to make an ifdef thats dependent on a particular library ?? Sounds very useful !

I will investigate the X content a lot more fully and make that subject of a new bug report if I can establish its viable. Its possible that distros are including the x libraries in a legacy mode during transition ? If so, then the the time will come when that call will break at run time. Catching that exception would tell me what I need, traditional SysTray is not going to work. But sooner or later the code won't build, and that is useless.

Further research is indicated ....

Davo

David

2021-05-20 05:53

reporter   ~0130967

OK, here is a new version of the patch, still doing the two things at once, sorry ! Truth is, it is trivial to make my first patch aware of a {$define HASX}. And these two issues are so closely related !

Now, I cannot find a Linux (with a GUI) that does not support xlib at the moment to test on. It will come I guess but for now, we have XWayland and that guarantees a workable Xlib. If someone undefines HASX at build time, then my method will instruct the unit to use one of the AppIndicators, That is likely to be a safe outcome.

If someone builds on a system with X (and HASX defined) but the binary is then run on a system without xlib, I am unsure of what will happen. I suspect it will crash long before a TrayIcon is tried ! There are close to 600 uses of the HASX define in LCL.

HASX is defined if we are on Unix in all except DARWIN where it might be defined. GTK2 on DARWIN is certainly possible, but ....

The patch is called ayatana-2.patch, use -p0 in the top of the Lazarus tree.

CudaTextMan, I have manually set the patch to two space indents, my IDE is again not doing indent as I like it. Two spaces does not suit me !

Davo
ayatana-2.patch (5,158 bytes)   
--- lcl/interfaces/gtk2/unitywsctrls.pas	2021-05-20 15:37:00.908202998 +1000
+++ lcl/interfaces/gtk2/unitywsctrls.pas-new	2021-05-20 15:34:54.036993288 +1000
@@ -11,6 +11,8 @@
 interface
 
 {$mode delphi}
+{$I gtk2defines.inc}
+
 uses
   GLib2, Gtk2, Gdk2Pixbuf,
   Classes, SysUtils, dynlibs,
@@ -20,6 +22,10 @@
 { Copyleft implementation of TTrayIcon originally for Unity applications indicators
   Original version 2015 by Anthony Walter sysrpl@gmail.com
 
+  Updated May 2021 to try first the Canonical libappindicator3-1 (Fedora Gnome)
+  then libayatana-appindicator3-1 (Debian Bullseye). Ayatana is a fork of the
+  Canonical one, does not mention Unity or Ubuntu (very much). DRB
+
   Changed October 2019, we now try and identify those Linux distributions that
   need to use LibAppIndicator3 and allow the remainder to use the older and
   more functional SystemTray. Only a few old distributions can use LibAppIndicator_1
@@ -27,7 +33,7 @@
 
   The 'look up table' in NeedAppIndicator() can be overridden.
   Introduce an optional env var, LAZUSEAPPIND that can be unset or set to
-  YES, NO or INFO - YES forces an attempt to use LibAppIndicator3, NO prevents
+  YES, NO or INFO - YES forces an attempt to use one of the AppIndicator3, NO prevents
   an attempt, any non blank value (eg INFO) displays to std out what is happening.
 
   Note we assume this env var will only be used in Linux were its always safe to
@@ -65,14 +71,17 @@
     class function GetPosition(const {%H-}ATrayIcon: TCustomTrayIcon): TPoint; override;
   end;
 
-{ UnityAppIndicatorInit returns true if libappindicator_3 library can be loaded }
+{ UnityAppIndicatorInit returns true if an AppIndicator library can be loaded }
 
 function UnityAppIndicatorInit: Boolean;
 
 implementation
 
+{$IFDEF HASX}uses gtk2extra, x, xlib, Types;{$ENDIF}        // Testing for traditional SysTray available
+
 const
-  libappindicator_3 = 'libappindicator3.so.1';
+  libappindicator_3 = 'libappindicator3.so.1';              // Canonical's Unity Appindicator3 library
+  LibAyatanaAppIndicator = 'libayatana-appindicator3.so.1'; // Ayatana - typically called libayatana-appindicator3-1
 
 {const
   APP_INDICATOR_SIGNAL_NEW_ICON = 'new-icon';
@@ -272,16 +281,19 @@
   UseAppInd : string;
 
   function NeedAppIndicator: boolean;
+  {$IFDEF HASX}
+  // Here we assume if no X, its pure eg wayland so no traditional SysTray either.
   var
-    DeskTop : String;
+    A : TAtom;
+    XDisplay: PDisplay;
+  {$endif}
   begin
-    DeskTop := GetEnvironmentVariableUTF8('XDG_CURRENT_DESKTOP');
-    // See the wiki for details of what extras these desktops require !!
-    if (Desktop = 'GNOME')
-      or (DeskTop = 'Unity')
-      or (Desktop = 'Enlightenment')
-      or (Desktop = 'ubuntu:GNOME') then exit(True);
-    Result := False;
+    Result := True;
+    {$IFDEF HASX}
+    XDisplay := gdk_display;
+    A := XInternAtom(XDisplay, '_NET_SYSTEM_TRAY_S0', False);
+    result := (XGetSelectionOwner(XDisplay, A) = 0);
+    {$endif}
   end;
 
   function TryLoad(const ProcName: string; var Proc: Pointer): Boolean;
@@ -301,23 +313,33 @@
   if UseAppInd = 'NO' then
     begin
     Initialized := False;
-    writeln('APPIND Debug : Choosing to not try AppIndicator3');
+    writeln('APPIND Debug : Choosing to use Traditional SysTray');
     Exit;
   end;
   if (UseAppInd <> 'YES') and (not NeedAppIndicator()) then    // ie its NO or blank or INFO
   begin
     Initialized := False;
     if UseAppInd <> '' then
-       writeln('APPIND Debug : Will not use AppIndicator3');
+       writeln('APPIND Debug : Will use Traditional SysTray');
     Exit;
   end;
   if UseAppInd = 'YES' then                                    // either a YES or OS needs it
      writeln('APPIND Debug : Will try to force AppIndicator3')
   else
-     if UseAppInd <> '' then writeln('APPIND Debug : OS and Desktop request AppIndicator3');
+     if UseAppInd <> '' then writeln('APPIND Debug : OS and Desktop request AppIndicator');
+
   Module := LoadLibrary(libappindicator_3);        // might have several package names, see wiki
-  if Module = 0 then
-     Exit;
+  if Module = 0 then begin
+    if UseAppInd <> '' then                                    // either a YES or OS needs it
+       writeln('APPIND Debug : Failed to load Unity AppIndicator, will try Ayatana');
+    Module := LoadLibrary(LibAyatanaAppIndicator);
+    if Module = 0 then begin
+       if UseAppInd <> '' then
+         writeln('APPIND Debug : Failed to load Ayatana AppIndicator, its likely no SysTray available.');
+       exit(False);
+    end;
+  end;
+
   Result :=
     TryLoad('app_indicator_get_type', @app_indicator_get_type) and
     TryLoad('app_indicator_new', @app_indicator_new) and
@@ -340,7 +362,7 @@
     TryLoad('app_indicator_get_label_guide', @app_indicator_get_label_guide) and
     TryLoad('app_indicator_get_ordering_index', @app_indicator_get_ordering_index);
   if UseAppInd <> '' then
-     writeln('APPIND Debug : AppIndicator3 has loaded ' + booltostr(Result, True));
+     writeln('APPIND Debug : An AppIndicator has loaded ' + booltostr(Result, True));
   Initialized := Result;
 end;
 
ayatana-2.patch (5,158 bytes)   

Anton Kavalenka

2021-05-20 07:37

reporter   ~0130968

@David
This X-dependent code runs fine when you run your application from shell. This works due to xwayland installed and due to mercy of shell developers.

Try to run app with your patches from command line.
Try to unset DISPLAY variable and run.

David

2021-05-21 01:20

reporter   ~0130980

@Anton
Sorry Anton, I don't understand what you are getting at.
I would expect unsetting DISPLAY should make it impossible to run any GUI app.
So, I tried it with apps built with and without my patch, results are identical, they all crash. GTK ones with a warning about lack of DISPLAY and QT5 ones (unaffected by my patch) complain about xcb, the "sort of" modern replacement / helper for xlib. These crashes happen identically in code that was built before I added my patch, code that does not use the patched code and code that would use the patched code but does not get that far.
LCL using both GTK and Qt5 rely on X, there is a {$define HASX} in defines, if you unset that, gtk2 apps will not build. I expect Qt5 is the same.

On Wayland systems, Qt5 apps do run using xwayland but GTK2 ones appear to be Wayland compliant. But strace tells us that xcb is still being used as is the "usual suspects" X11*, xi, xt etc. And that applies to apps built both with and without my patch.

If its possible to make a GTK2 app without any x or X calls, I suspect there might be a lot of work required. Qt5, even more so.

In the case of my patch, Wayland very specifically does not support the SysTray. Any SysTray on a Wayland / Gnome system happens despite the Wayland / Gnome developers. So, on an all Wayland system, where HASX has not been defined, the method my patch focuses on always tells the unit to use AppIndicator. So, it is fully complaint. But as I noted, lots of other things break before we get anywhere near that unit. If we fix the other things, the unit in question will be fine. As long as HASX is not defined.

Sorry for the long response.

Davo

David

2021-05-21 02:12

reporter   ~0130981

OK, because the addition of ayatana to our list of possible TrayIcon models is somewhat urgent and the implementation of NeedAppIndicator() just a matter of neatness, here is a third patch. Yep, I know I should have done it like thing in the first place !

ayatana-3.patch does only one thing, if the unit does try and load the well know libappindicator3 library and fails, it now tries libayatana-appindicator3. I have also altered slightly the debug messages to allow for the fact that we may now load one of two different libraries.

I will wait for this to be dealt with and reconsider the NeedAppIndicator() issue, its no where near as important. I just don't like the current model (which I wrote) because it depends on us knowing the names of every uncooperative Desktop in Linux land.

Davo
ayatana-3.patch (4,152 bytes)   
--- lcl/interfaces/gtk2/unitywsctrls.pas	2021-05-21 11:48:57.310361523 +1000
+++ lcl/interfaces/gtk2/unitywsctrls.pas-new	2021-05-21 11:48:46.890074415 +1000
@@ -11,6 +11,7 @@
 interface
 
 {$mode delphi}
+
 uses
   GLib2, Gtk2, Gdk2Pixbuf,
   Classes, SysUtils, dynlibs,
@@ -20,6 +21,10 @@
 { Copyleft implementation of TTrayIcon originally for Unity applications indicators
   Original version 2015 by Anthony Walter sysrpl@gmail.com
 
+  Updated May 2021 to try first the Canonical libappindicator3-1 (Fedora Gnome)
+  then libayatana-appindicator3-1 (Debian Bullseye). Ayatana is a fork of the
+  Canonical one, does not mention Unity or Ubuntu (very much). DRB
+
   Changed October 2019, we now try and identify those Linux distributions that
   need to use LibAppIndicator3 and allow the remainder to use the older and
   more functional SystemTray. Only a few old distributions can use LibAppIndicator_1
@@ -27,7 +32,7 @@
 
   The 'look up table' in NeedAppIndicator() can be overridden.
   Introduce an optional env var, LAZUSEAPPIND that can be unset or set to
-  YES, NO or INFO - YES forces an attempt to use LibAppIndicator3, NO prevents
+  YES, NO or INFO - YES forces an attempt to use one of the AppIndicator3, NO prevents
   an attempt, any non blank value (eg INFO) displays to std out what is happening.
 
   Note we assume this env var will only be used in Linux were its always safe to
@@ -65,14 +70,15 @@
     class function GetPosition(const {%H-}ATrayIcon: TCustomTrayIcon): TPoint; override;
   end;
 
-{ UnityAppIndicatorInit returns true if libappindicator_3 library can be loaded }
+{ UnityAppIndicatorInit returns true if an AppIndicator library can be loaded }
 
 function UnityAppIndicatorInit: Boolean;
 
 implementation
 
 const
-  libappindicator_3 = 'libappindicator3.so.1';
+  libappindicator_3 = 'libappindicator3.so.1';              // Canonical's Unity Appindicator3 library
+  LibAyatanaAppIndicator = 'libayatana-appindicator3.so.1'; // Ayatana - typically called libayatana-appindicator3-1
 
 {const
   APP_INDICATOR_SIGNAL_NEW_ICON = 'new-icon';
@@ -301,23 +307,33 @@
   if UseAppInd = 'NO' then
     begin
     Initialized := False;
-    writeln('APPIND Debug : Choosing to not try AppIndicator3');
+    writeln('APPIND Debug : Choosing to use Traditional SysTray');
     Exit;
   end;
   if (UseAppInd <> 'YES') and (not NeedAppIndicator()) then    // ie its NO or blank or INFO
   begin
     Initialized := False;
     if UseAppInd <> '' then
-       writeln('APPIND Debug : Will not use AppIndicator3');
+       writeln('APPIND Debug : Will use Traditional SysTray');
     Exit;
   end;
   if UseAppInd = 'YES' then                                    // either a YES or OS needs it
      writeln('APPIND Debug : Will try to force AppIndicator3')
   else
-     if UseAppInd <> '' then writeln('APPIND Debug : OS and Desktop request AppIndicator3');
+     if UseAppInd <> '' then writeln('APPIND Debug : OS and Desktop request AppIndicator');
+
   Module := LoadLibrary(libappindicator_3);        // might have several package names, see wiki
-  if Module = 0 then
-     Exit;
+  if Module = 0 then begin
+    if UseAppInd <> '' then                                    // either a YES or OS needs it
+       writeln('APPIND Debug : Failed to load Unity AppIndicator, will try Ayatana');
+    Module := LoadLibrary(LibAyatanaAppIndicator);
+    if Module = 0 then begin
+       if UseAppInd <> '' then
+         writeln('APPIND Debug : Failed to load Ayatana AppIndicator, its likely no SysTray available.');
+       exit(False);
+    end;
+  end;
+
   Result :=
     TryLoad('app_indicator_get_type', @app_indicator_get_type) and
     TryLoad('app_indicator_new', @app_indicator_new) and
@@ -340,7 +356,7 @@
     TryLoad('app_indicator_get_label_guide', @app_indicator_get_label_guide) and
     TryLoad('app_indicator_get_ordering_index', @app_indicator_get_ordering_index);
   if UseAppInd <> '' then
-     writeln('APPIND Debug : AppIndicator3 has loaded ' + booltostr(Result, True));
+     writeln('APPIND Debug : An AppIndicator has loaded ' + booltostr(Result, True));
   Initialized := Result;
 end;
 
ayatana-3.patch (4,152 bytes)   

Juha Manninen

2021-05-21 09:38

developer   ~0130982

You create patches faster than I can study them. :)
The last patch looks good. I applied it in r65119. I leave this report open for your planned new NeedAppIndicator() patch.

David

2021-05-22 00:44

reporter   ~0130991

Right Juha, of course I can generate patches faster than you can review. I use one command to make that patch, you have to read it line by line making sure I have not done something particularly stupid ! And a good thing you do that too !

OK, NeedAppIndicator.patch is the patch that just replaces NeedAppIndicator(). You should use it because its a generic solution to the problem rather than my first cut at a hand maintained look up table.

Perhaps you should not use because its adds a dependency on X.

As well as the points I raised above, its worth noting that gtk2widgetset.inc, gtk2wsforms.pp, gtk2proc.pp, gtk2int.pas and gtk2wsextractrls.pp all use x, xlib and they all do so with it wrapped in a {$ifdef HASX}. And that is what I have done in this patch too. In the event that HASX is not set, unitywsctrl.pas will still compile and act appropriately.

I'll look at GTK3 now.
NeedAppIndicator.patch (1,520 bytes)   
--- lcl/interfaces/gtk2/unitywsctrls.pas	2021-05-21 23:09:02.921778996 +1000
+++ lcl/interfaces/gtk2/unitywsctrls.pas-new	2021-05-21 23:08:27.012977065 +1000
@@ -11,6 +11,7 @@
 interface
 
 {$mode delphi}
+{$I gtk2defines.inc}
 
 uses
   GLib2, Gtk2, Gdk2Pixbuf,
@@ -76,6 +77,8 @@
 
 implementation
 
+{$IFDEF HASX}uses gtk2extra, x, xlib, Types;{$ENDIF}        // Testing for traditional SysTray being available
+
 const
   libappindicator_3 = 'libappindicator3.so.1';              // Canonical's Unity Appindicator3 library
   LibAyatanaAppIndicator = 'libayatana-appindicator3.so.1'; // Ayatana - typically called libayatana-appindicator3-1
@@ -278,16 +281,19 @@
   UseAppInd : string;
 
   function NeedAppIndicator: boolean;
+  {$IFDEF HASX}
+  // Here we assume if no X, its pure eg wayland so no traditional SysTray either.
   var
-    DeskTop : String;
+    A : TAtom;
+    XDisplay: PDisplay;
+  {$endif}
   begin
-    DeskTop := GetEnvironmentVariableUTF8('XDG_CURRENT_DESKTOP');
-    // See the wiki for details of what extras these desktops require !!
-    if (Desktop = 'GNOME')
-      or (DeskTop = 'Unity')
-      or (Desktop = 'Enlightenment')
-      or (Desktop = 'ubuntu:GNOME') then exit(True);
-    Result := False;
+    Result := True;
+    {$IFDEF HASX}
+    XDisplay := gdk_display;
+    A := XInternAtom(XDisplay, '_NET_SYSTEM_TRAY_S0', False);
+    result := (XGetSelectionOwner(XDisplay, A) = 0);
+    {$endif}
   end;
 
   function TryLoad(const ProcName: string; var Proc: Pointer): Boolean;
NeedAppIndicator.patch (1,520 bytes)   

Anton Kavalenka

2021-05-22 06:00

reporter   ~0130992

Last edited: 2021-05-22 06:02

View 2 revisions

GTK3 does not use Xlib. So I proposes unset DISPLAY.
It binds Xlib or Wayland dynamically and also can use other backends (e.g. GTK3 application can be run inside a browser)

ldd on GTK3 LCL application run on wayland system DOES not show any Xlib in dependencies.

Juha Manninen

2021-05-22 10:25

developer   ~0130996

Last edited: 2021-05-22 17:54

View 2 revisions

I applied the AppIndicator patch in r65122. Looks good. Types was not needed in uses section, I removed it.
I realized that a system using GTK2 always has X or at least X emulation. Anyway using HASX is good for consistency.
 [Edit] Wrong again. GTK2 can run on Windows without X, thus the {$IFDEF HASX} is needed. Anyways, it is good now.

I keep this open for a GTK3 patch.

David

2021-05-22 12:54

reporter   ~0131004

Not sure I can make any sense of GTK3 at the moment, my test app crashes gloriously ! Works OK on my U20.04 but very unstable on Bullseye so I cannot be sure its not something to do with the TrayIcon, I will need to have a play.

David

2021-05-22 13:01

reporter   ~0131005

@anton

> ldd on GTK3 LCL application run on wayland system DOES not show any Xlib in dependencies.

I think that GTK3 talks to xcb directly. These days, most xlib functions are just wrappers around xcb which is considerably faster but better to bypass xlib and go to xcb, faster still. Does require a bit of extra effort. On my U20.04, a gtk3 app does use libxcb.so.1

David

2021-05-27 11:08

reporter   ~0131045

Here is a patch for GTK3 that makes it ayatana aware as well. I have tested this on Ubuntu, Debian, Fedora and MXLinux, Gnome, XFCe, Mate and Plasma.
My (very simple) test app struggled on the Gnome systems but I am convinced that's Lazarus GTK3 issues rather than ones relating to the patch. And MXLinux Plasma DT displays a default icon rather than the intended one, however, so does libAppIndicator3-1 (fortunately still available).

Otherwise, seems to work well and does no harm.

I have left a couple of debug lines, hidden by $ifdefs that may be useful given GTK3 development status.

Davo
ayatana-gtk3.patch (3,508 bytes)   
--- lcl/interfaces/gtk3/gtk3wstrayicon.pas	2021-05-27 20:28:12.900792452 +1000
+++ lcl/interfaces/gtk3/gtk3wstrayicon.pas-new	2021-05-27 20:38:15.414703252 +1000
@@ -13,10 +13,10 @@
   for details about the license.
  *****************************************************************************
 
-A unit that uses LibAppIndicator3 to display a TrayIcon in GTK3.   Based on a
-GTK2 version by Anthony Walter, now works with many common Linux systems "out
-of the box" and almost all of the remainder with addition of LibAppIndicator3
-and TopIconsPlus or similar Gnome Extension.
+A unit that uses LibAppIndicator3-1 or libAyatana-AppIndicator3-1 to display a 
+TrayIcon in GTK3.   Based on a GTK2 version by Anthony Walter, now works with 
+many common Linux systems "out of the box" or with the addition of one of the
+above mentioned libraries and possibly gnome-shell-extension-appindicator.
 
 See Wiki for details and Limitations (Menu only, one Icon only....)
 Also refer to discussion in ../gtk2/UnityWSCtrls.pas
@@ -43,15 +43,20 @@
     class function GetPosition(const {%H-}ATrayIcon: TCustomTrayIcon): TPoint; override;
   end;
 
-{ Gtk3AppIndicatorInit returns true if LibAppIndicator_3 library has been loaded }
+{ Gtk3AppIndicatorInit returns true if a LibAppIndicator3 library has been loaded }
 function Gtk3AppIndicatorInit: Boolean;
 
 implementation
 
+{X$define DEBUGAPPIND}
+
 uses gtk3objects;     // TGtk3Image
 
 const
-  libappindicator_3 = 'libappindicator3.so.1';
+  libappindicator_3 = 'libappindicator3.so.1';              // Unity or Canonical libappindicator3-1
+  LibAyatanaAppIndicator = 'libayatana-appindicator3.so.1'; // Ayatana - typically called libayatana-appindicator3-1
+  IconThemePath = '/tmp/appindicators/';                    // We must write our icon to a file.
+  IconType = 'png';
 
 type
   TAppIndicatorCategory = (
@@ -109,9 +114,6 @@
     procedure Update;
   end;
 
-const
-  IconThemePath = '/tmp/appindicators/'; // We must write our icon to a file.
-  IconType = 'png';
 
 var
   GlobalAppIndicator: PAppIndicator;
@@ -148,6 +150,14 @@
     GlobalAppIndicator := app_indicator_new_with_path(PChar(FName), PChar(FIconName),
       APP_INDICATOR_CATEGORY_APPLICATION_STATUS, IconThemePath);
   Update;
+  {$ifdef DEBUGAPPIND}
+  case app_indicator_get_status(GlobalAppIndicator) of
+	      APP_INDICATOR_STATUS_PASSIVE : writeln('AppInd statis is Passive');
+	      APP_INDICATOR_STATUS_ACTIVE : writeln('AppInd status is Active');
+	      APP_INDICATOR_STATUS_ATTENTION  : writeln('AppInd is Attention');
+  else writeln('AppInd status is unknown');
+  end;
+  {$endif}
 end;
 
 destructor TAppIndTrayIconHandle.Destroy;
@@ -249,9 +259,16 @@
   Loaded := True;
   if Initialized then
     Exit(True);
-  Module := LoadLibrary(libappindicator_3);        // might have several package names, see wiki
-  if Module = 0 then
-     Exit;
+  Module := LoadLibrary(libappindicator_3);
+  if Module = 0 then begin
+    Module := LoadLibrary(LibAyatanaAppIndicator);
+    if Module = 0 then begin                    // Sorry, no TrayIcon !
+      {$ifdef DEBUGAPPIND}
+      writeln('Failed to load an appindicator library');{$endif}
+      Exit;
+    end
+    {$ifdef DEBUGAPPIND} else writeln('Loaded ' + LibAyatanaAppIndicator){$endif};
+  end {$ifdef DEBUGAPPIND} else writeln('Loaded ' + libappindicator_3){$endif};
   Result :=
     TryLoad('app_indicator_get_type', @app_indicator_get_type) and
     TryLoad('app_indicator_new', @app_indicator_new) and
ayatana-gtk3.patch (3,508 bytes)   

Juha Manninen

2021-06-11 06:28

developer   ~0131240

Applied, thanks.

David

2021-06-13 12:29

reporter   ~0131286

Yep, thanks Juha, that looks OK.
GTK3 now tries to load ayatana if libappindicator3.so.1 is not present.
Closing...

Issue History

Date Modified Username Field Change
2021-05-19 11:23 David New Issue
2021-05-19 11:23 David File Added: ayatana.patch
2021-05-19 14:58 CudaText man Note Added: 0130960
2021-05-19 17:30 Juha Manninen Assigned To => Juha Manninen
2021-05-19 17:30 Juha Manninen Status new => assigned
2021-05-19 17:48 Juha Manninen Note Added: 0130963
2021-05-19 23:59 David Note Added: 0130966
2021-05-20 05:53 David Note Added: 0130967
2021-05-20 05:53 David File Added: ayatana-2.patch
2021-05-20 07:37 Anton Kavalenka Note Added: 0130968
2021-05-21 01:20 David Note Added: 0130980
2021-05-21 02:12 David Note Added: 0130981
2021-05-21 02:12 David File Added: ayatana-3.patch
2021-05-21 09:38 Juha Manninen Note Added: 0130982
2021-05-21 09:38 Juha Manninen Fixed in Revision => r65119
2021-05-21 09:38 Juha Manninen LazTarget => -
2021-05-21 09:38 Juha Manninen Widgetset GTK 2 => GTK 2
2021-05-22 00:44 David Note Added: 0130991
2021-05-22 00:44 David File Added: NeedAppIndicator.patch
2021-05-22 06:00 Anton Kavalenka Note Added: 0130992
2021-05-22 06:02 Anton Kavalenka Note Edited: 0130992 View Revisions
2021-05-22 10:25 Juha Manninen Note Added: 0130996
2021-05-22 10:26 Juha Manninen Fixed in Revision r65119 => r65119, r65122
2021-05-22 10:26 Juha Manninen Widgetset GTK 2 => GTK 2
2021-05-22 12:54 David Note Added: 0131004
2021-05-22 13:01 David Note Added: 0131005
2021-05-22 17:54 Juha Manninen Note Edited: 0130996 View Revisions
2021-05-27 11:08 David Note Added: 0131045
2021-05-27 11:08 David File Added: ayatana-gtk3.patch
2021-06-11 06:28 Juha Manninen Status assigned => resolved
2021-06-11 06:28 Juha Manninen Resolution open => fixed
2021-06-11 06:28 Juha Manninen Fixed in Revision r65119, r65122 => r65119, r65122, r65214
2021-06-11 06:28 Juha Manninen Widgetset GTK 2 => GTK 2
2021-06-11 06:28 Juha Manninen Note Added: 0131240
2021-06-13 12:29 David Status resolved => closed
2021-06-13 12:29 David Note Added: 0131286