Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
249 changes: 161 additions & 88 deletions bin/shutter
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ my $goocanvas = TRUE;
my $exiftool = TRUE;
my $appindicator = TRUE;
my $pathclass = TRUE;
my $xapp_supported = TRUE;
fct_init_depend();

#--------------------------------------
Expand Down Expand Up @@ -380,9 +381,11 @@ my $trans_backg;
my $trans_check;
my $trans_custom_btn;
my $trans_custom;
my $tray_click_locked = 0;
my $tray_libappindicator;
my $tray_legacy;
my $tray_menu;
my $tray_xapp;
my $tray;
my $visible_windows_active;
my $window;
Expand Down Expand Up @@ -3560,19 +3563,47 @@ modes are currently experimental, your desktop environment might fall back to an
return TRUE;
}

sub evt_activate_systray_statusicon {
my ($widget, $data, $tray) = @_;
if ($sc->get_debug) {
print "\n$data was emitted by widget $widget\n";
}
sub evt_activate_systray_statusicon {
my ($widget, $data, $tray) = @_;

unless ($is_hidden) {
fct_control_main_window('hide');
} else {
fct_control_main_window('show');
}
return TRUE;
}
#debounce: ignore double events triggered by buggy panels using GTK timeout
if ($tray_click_locked) {
return TRUE;
}

#lock the click for 250 milliseconds
$tray_click_locked = 1;
Glib::Timeout->add(250, sub {
$tray_click_locked = 0;
return FALSE; # FALSE means the timeout is removed and doesn't repeat
});

#check actual window visibility
my $is_really_visible = $window->get_visible();
my $is_iconified = 0;

#under x11 a window can be visible but iconified to the taskbar
if ($window->get_window()) {
my $state = $window->get_window()->get_state();
if ($state >= 'iconified') {
$is_iconified = 1;
}
}

#hide if visible and not iconified
if ($is_really_visible && !$is_iconified) {
fct_control_main_window('hide');
}
#show in all other cases (hidden or iconified)
else {
fct_control_main_window('show');

#force window manager to bring window to front
$window->present();
}

return TRUE;
}

sub evt_accounts {
my ($tree, $path, $column) = @_;
Expand Down Expand Up @@ -4210,55 +4241,74 @@ modes are currently experimental, your desktop environment might fall back to an
#--------------------------------------

sub fct_try_init_tray {
if ($x11_supported) {
$tray_legacy = Gtk3::StatusIcon->new();
$tray_legacy->set_from_icon_name("shutter-panel");
$tray_legacy->set_visible(1);

# If we already have an active tray instance, do NOTHING.
# This prevents spawning multiple icons when fct_try_init_tray is called repeatedly.
return if ($tray);

my $prefer_legacy_on_x11 = ($x11_supported && !$ENV{WAYLAND_DISPLAY});

if ($prefer_legacy_on_x11) {
# Init legacy Gtk3 status icon for pure X11 (only once)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. if this should be done only once, then instead of such condition, simply don't call this function in a loop
  2. the loop was necessary because depending on the order of programs startup during system boot, if shutter launched before tray itself, the icon is lost. Did something change to fix it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Sorry and thanks for pointing that out, I think, $x11_supported is completely sufficient, no need to define a new variable. I'll change that.
  2. Good to know, I didn't know it and thus didn't test. I'll look into it!

if ($x11_supported && !$tray_legacy) {
$tray_legacy = Gtk3::StatusIcon->new();
$tray_legacy->set_from_icon_name("shutter-panel");
$tray_legacy->set_visible(1);
$tray = $tray_legacy;
}
} else {
$tray_legacy = undef;
# Try to init XApp status icon first for Wayland/Cinnamon (only once)
if ($xapp_supported && !$tray_xapp) {
$tray_xapp = eval { XApp::StatusIcon->new() };

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that if the dependency is installed, but the running DE is gnome instead of linux mint's one, the icon won't be shown?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to rework the comment. It works in Cinnamon and KDE for sure, in Gnome I couldn't make the Gnome Shell extension showing tray icons work so far...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Photon89 Photon89 Aug 23, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Damn, I just retested in KDE and the newest variant (which is what is inside the PR) doesn't show a tray icon in KDE, though it worked earlier. I need to look into this again, sorry!

Regarding the xapp issue, at least in Cinnamon the xapp tray icon was able to do the left click (show/hide main window) and the right click (show context menu) correctly, so maybe this old issue is fixed already?

Sorry, I need to read more carefully. I'm not sure whether it's a Shutter xapp issue or an issue of showing to tray icons in Gnome at all. I'll try to figure it out.

if ($tray_xapp) {
$tray_xapp->set_name("Shutter");
$tray_xapp->set_icon_name("shutter-panel");
$tray_xapp->set_secondary_menu($tray_menu);
$tray = $tray_xapp;
}
}
}

fct_update_gui();

fct_update_gui();

if ($tray_legacy && $tray_legacy->is_embedded) {

if ($tray_libappindicator) {
$tray_libappindicator->set_status('passive');
$tray_libappindicator = undef;
}
$tray = $tray_legacy;
$tray->{'hid'} = $tray->signal_connect(
'popup-menu' => sub { evt_show_systray_statusicon(@_); },
$tray
);
$tray->{'hid2'} = $tray->signal_connect(
'activate' => sub {
evt_activate_systray_statusicon(@_);
$tray;
},
$tray
);
} else {
if ($tray_legacy) {
$tray_legacy->set_visible(0);
$tray_legacy = undef;
}

if ($appindicator && !$tray_libappindicator) {
# Fallback to AppIndicator. This one doesn't allow left-click signal, but it seems to be the only option for Gnome on Ubuntu 21.04...
$tray_libappindicator = AppIndicator::Indicator->new("Shutter", "shutter-panel", 'application-status');
$tray_libappindicator->set_menu($tray_menu);
$tray_libappindicator->set_status('active');
}
# Connect signals only if we successfully established a tray and haven't connected yet
if ($tray) {
if ($tray->isa('XApp::StatusIcon')) {
unless (defined $tray->{'hid2'}) {
$tray->{'hid2'} = $tray->signal_connect(
'activate' => sub {
evt_activate_systray_statusicon(@_);
$tray;
},
$tray
);
}
} elsif ($tray->isa('Gtk3::StatusIcon')) {
unless (defined $tray->{'hid'}) {
$tray->{'hid'} = $tray->signal_connect(
'popup-menu' => sub { evt_show_systray_statusicon(@_); },
$tray
);
}
unless (defined $tray->{'hid2'}) {
$tray->{'hid2'} = $tray->signal_connect(
'activate' => sub {
evt_activate_systray_statusicon(@_);
$tray;
},
$tray
);
}
}
} elsif ($appindicator && !$tray_libappindicator) {
# Fallback to AppIndicator if nothing else worked
$tray_libappindicator = AppIndicator::Indicator->new("Shutter", "shutter-panel", 'application-status');
$tray_libappindicator->set_menu($tray_menu);
$tray_libappindicator->set_status('active');
$tray = $tray_libappindicator;
}
}

if ($tray_libappindicator) {
$tray = $tray_libappindicator;
} else {
$tray = undef;
}
}
}

sub fct_create_session_notebook {

Expand Down Expand Up @@ -4506,17 +4556,17 @@ modes are currently experimental, your desktop environment might fall back to an
}
}

#and block status icon handler
if ($tray && $tray->isa('Gtk3::StatusIcon')) {
if ($tray->signal_handler_is_connected($tray->{'hid'})) {
$tray->signal_handler_block($tray->{'hid'});
}
if ($tray->signal_handler_is_connected($tray->{'hid2'})) {
$tray->signal_handler_block($tray->{'hid2'});
}
} elsif ($tray && $tray->isa('AppIndicator::Indicator')) {
$tray->set_status('passive');
}
#and block status icon handler for legacy and xapp
if ($tray && ($tray->isa('Gtk3::StatusIcon') || $tray->isa('XApp::StatusIcon'))) {
if (defined $tray->{'hid'} && $tray->signal_handler_is_connected($tray->{'hid'})) {
$tray->signal_handler_block($tray->{'hid'});
}
if (defined $tray->{'hid2'} && $tray->signal_handler_is_connected($tray->{'hid2'})) {
$tray->signal_handler_block($tray->{'hid2'});
}
} elsif ($tray && $tray->isa('AppIndicator::Indicator')) {
$tray->set_status('passive');
}
} elsif ($action eq 'unblock') {

$sensitive = TRUE;
Expand All @@ -4528,17 +4578,17 @@ modes are currently experimental, your desktop environment might fall back to an
}
}

#and unblock status icon handler
if ($tray && $tray->isa('Gtk3::StatusIcon')) {
if ($tray->signal_handler_is_connected($tray->{'hid'})) {
$tray->signal_handler_unblock($tray->{'hid'});
}
if ($tray->signal_handler_is_connected($tray->{'hid2'})) {
$tray->signal_handler_unblock($tray->{'hid2'});
}
} elsif ($tray && $tray->isa('AppIndicator::Indicator')) {
$tray->set_status('active');
}
#and unblock status icon handler for legacy and xapp
if ($tray && ($tray->isa('Gtk3::StatusIcon') || $tray->isa('XApp::StatusIcon'))) {
if (defined $tray->{'hid'} && $tray->signal_handler_is_connected($tray->{'hid'})) {
$tray->signal_handler_unblock($tray->{'hid'});
}
if (defined $tray->{'hid2'} && $tray->signal_handler_is_connected($tray->{'hid2'})) {
$tray->signal_handler_unblock($tray->{'hid2'});
}
} elsif ($tray && $tray->isa('AppIndicator::Indicator')) {
$tray->set_status('active');
}
}

#enable/disable controls
Expand Down Expand Up @@ -7444,16 +7494,26 @@ modes are currently experimental, your desktop environment might fall back to an
}

#TRAY TOOLTIP
#--------------------------------------
if ($combobox_settings_profiles) {
if ($tray && $tray->isa('Gtk3::StatusIcon')) {
if ($combobox_settings_profiles->get_active_text) {
$tray->set_tooltip_text($d->get("Current profile") . ": " . $combobox_settings_profiles->get_active_text);
} else {
$tray->set_tooltip_text(SHUTTER_NAME . " " . SHUTTER_VERSION);
}
}
}
#--------------------------------------
if ($combobox_settings_profiles) {
if ($tray && ($tray->isa('Gtk3::StatusIcon') || $tray->isa('XApp::StatusIcon'))) {

#determine the string that SHOULD be shown
my $new_tooltip_text = "";
if ($combobox_settings_profiles->get_active_text) {
$new_tooltip_text = $d->get("Current profile") . ": " . $combobox_settings_profiles->get_active_text;
} else {
$new_tooltip_text = SHUTTER_NAME . " " . SHUTTER_VERSION;
}

#only update the tray (and trigger dbus) if the text actually changed
if (!defined $tray->{'_cached_tooltip'} || $tray->{'_cached_tooltip'} ne $new_tooltip_text) {

$tray->set_tooltip_text($new_tooltip_text);
$tray->{'_cached_tooltip'} = $new_tooltip_text; #save to cache
}
}
}

return TRUE;
}
Expand Down Expand Up @@ -9580,6 +9640,19 @@ modes are currently experimental, your desktop environment might fall back to an
$exiftool = FALSE;
}

#xapp-statusicon
eval {
Glib::Object::Introspection->setup(
basename => 'XApp',
version => '1.0',
package => 'XApp',
);
};
if ($@) {
warn "WARNING: XApp is missing --> falling back to legacy StatusIcon or AppIndicator for system tray!\n\n";
$xapp_supported = FALSE;
}

#Path::Class
eval { require Path::Class };
if ($@) {
Expand Down
Loading