From c96b2f257fb90b4a6bd349966454fd642a3e4cd3 Mon Sep 17 00:00:00 2001 From: Photon89 Date: Mon, 25 May 2026 17:07:23 +0200 Subject: [PATCH 1/5] Added Wayland support via interactive or non-interactive call --- bin/shutter | 505 ++++++++++-------- .../modules/Shutter/Screenshot/Wayland.pm | 23 +- 2 files changed, 297 insertions(+), 231 deletions(-) mode change 100755 => 100644 bin/shutter diff --git a/bin/shutter b/bin/shutter old mode 100755 new mode 100644 index fa38a4ba..bbd390cd --- a/bin/shutter +++ b/bin/shutter @@ -395,7 +395,7 @@ my $zoom_active; my %accounts; #hash to store account infos my %settings; #hash to store settings my @supported_formats; #hash to store available supported file formats - + my %supported_targets; #hash to store available targets for xdg-desktop-portal sub STARTUP { # This is called by $app->run below if another Shutter instance is not running. @@ -589,6 +589,21 @@ sub STARTUP { if ($ENV{XDG_SESSION_TYPE} eq "wayland") { $x11_supported = 0; + # Get targets supported by xdg-desktop-portal and hash them into %supported_targets + # Possible targets are: + # 1: Screen (corresponds "full") + # 2: Window (corresponds "window") + # 4: Area (corresponds "select") + # 8: Active Window (corresponds "awindow") + # See: https://github.com/flatpak/xdg-desktop-portal/pull/1981 + my $raw = Net::DBus->session + ->get_service("org.freedesktop.portal.Desktop") + ->get_object("/org/freedesktop/portal/desktop") + ->as_interface("org.freedesktop.DBus.Properties") + ->Get("org.freedesktop.portal.Screenshot", "AvailableTargets"); + my $val = (ref($raw) && $raw->can('value')) ? $raw->value : $raw; + my $targets = (ref($val) eq 'ARRAY') ? $val : []; + %supported_targets = map { $_ => 1 } @$targets; } if ($x11_supported) { @@ -655,7 +670,9 @@ sub STARTUP { $sm->{_menuitem_iclipboard}->signal_connect('activate', \&fct_clipboard_import); unless ($x11_supported) { - for my $name ('selection', 'awindow', 'window', 'menu', 'tooltip') { + # on Wayland selection/window/awindow go through the XDG portal; + # only menu/tooltip capture stay disabled (not supported by the portal) + for my $name ('menu', 'tooltip') { $sm->{"_menuitem_$name"}->set_sensitive(FALSE); } } @@ -1000,11 +1017,13 @@ sub STARTUP { $st->{_upload}->set_sensitive(FALSE); unless ($x11_supported) { + # on Wayland _select/_window use the XDG portal; menu/tooltip stay disabled my $tooltip = $d->get("Can't take screenshots without X11 server"); - for my $name ('_select', '_window', '_menu', '_tooltip') { + for my $name ('_menu', '_tooltip') { $st->{$name}->set_sensitive(FALSE); $st->{$name}->set_tooltip_text($tooltip); } + # dropdown menus (workspace list / window list) still need X11 to enumerate for my $name ('_full', '_window') { $st->{$name}->set_arrow_tooltip_text($tooltip); } @@ -3128,7 +3147,10 @@ sub STARTUP { #unblock signal handler fct_control_signals('unblock'); return TRUE; - } elsif (!$x11_supported && $data ne "full" && $data ne "tray_full") { + } elsif (!$x11_supported + && $data !~ /^(tray_)?(full|select|window|awindow)$/) { + # full/select/window/awindow are served via the XDG portal on Wayland; + # everything else (menu, tooltip, ...) still needs X11 my $sd = Shutter::App::SimpleDialogs->new; $sd->dlg_error_message($d->get("Can't take screenshots without X11 server"), $d->get("Failed")); fct_control_signals('unblock'); @@ -6183,10 +6205,37 @@ sub STARTUP { } } - #fullscreen screenshot - if ($data eq "full" || $data eq "tray_full") { - - if ($x11_supported) { + #wayland: route selection/window/active to xdg-desktop-portal + if (!$x11_supported) { + # For some period of time desktops won't support non-interactive targets, we need to deal with it. + my $target; + my $interactive; + # For fullscreen capture just use plain xdg_portal + if ($data eq "full" || $data eq "tray_full") { + $screenshot = Shutter::Screenshot::Wayland::xdg_portal($screenshooter); + } else { + # For each non-trivial capture mode (window, active window, selection) define a target + # to try and call the non-interactive portal interface + if ($data eq "window" || $data eq "tray_window") { + $target=2; + } elsif ($data eq "awindow" || $data eq "tray_awindow") { + $target=8 + } elsif ($data eq "select" || $data eq "tray_select") { + $target=4; + } + # For the target chosen by user check if it is supported non-interactively. + # If unsupported, fall back to an interactive call which makes use of the + # DE's interactive tool. + if ($supported_targets{$target}) { + $interactive=0; + } else { + $interactive=1; + } + $screenshot = Shutter::Screenshot::Wayland::xdg_portal($screenshooter, $interactive, $target); + } + } else { + #fullscreen screenshot + if ($data eq "full" || $data eq "tray_full") { $screenshooter = Shutter::Screenshot::Workspace->new( $sc, $include_cursor, $delay_value, $notify_timeout_active->get_active, @@ -6194,302 +6243,298 @@ sub STARTUP { undef, undef, $current_monitor_active->get_active ); $screenshot = $screenshooter->workspace(); - } else { - # TODO: support kwin directly, because it has more features than the xdg portal - $screenshot = Shutter::Screenshot::Wayland::xdg_portal($screenshooter); - } - - #window - } elsif ($data eq "window" - || $data eq "tray_window" - || $data eq "awindow" - || $data eq "tray_awindow" - || $data eq "section" - || $data eq "tray_section" - || $data eq "menu" - || $data eq "tray_menu" - || $data eq "tooltip" - || $data eq "tray_tooltip") - { + #window + } elsif ($data eq "window" + || $data eq "tray_window" + || $data eq "awindow" + || $data eq "tray_awindow" + || $data eq "section" + || $data eq "tray_section" + || $data eq "menu" + || $data eq "tray_menu" + || $data eq "tooltip" + || $data eq "tray_tooltip") + { - #control some wm related settings - my $curr_value = fct_control_wm_settings('start'); + #control some wm related settings + my $curr_value = fct_control_wm_settings('start'); - if (defined $extra && $extra) { + if (defined $extra && $extra) { - $screenshooter = Shutter::Screenshot::WindowName->new( - $sc, $include_cursor, $delay_value, $notify_timeout_active->get_active, - $border_active->get_active, $winresize_active->get_active, $winresize_w->get_value, $winresize_h->get_value, - $hide_time->get_value, $data, $autoshape_active->get_active - ); + $screenshooter = Shutter::Screenshot::WindowName->new( + $sc, $include_cursor, $delay_value, $notify_timeout_active->get_active, + $border_active->get_active, $winresize_active->get_active, $winresize_w->get_value, $winresize_h->get_value, + $hide_time->get_value, $data, $autoshape_active->get_active + ); - $screenshot = $screenshooter->window_find_by_name($extra); + $screenshot = $screenshooter->window_find_by_name($extra); - } else { + } else { - $screenshooter = Shutter::Screenshot::Window->new( - $sc, $include_cursor, $delay_value, $notify_timeout_active->get_active, - $border_active->get_active, $winresize_active->get_active, $winresize_w->get_value, $winresize_h->get_value, - $hide_time->get_value, $data, $autoshape_active->get_active, $is_hidden, - $visible_windows_active->get_active, $menu_waround_active->get_active - ); + $screenshooter = Shutter::Screenshot::Window->new( + $sc, $include_cursor, $delay_value, $notify_timeout_active->get_active, + $border_active->get_active, $winresize_active->get_active, $winresize_w->get_value, $winresize_h->get_value, + $hide_time->get_value, $data, $autoshape_active->get_active, $is_hidden, + $visible_windows_active->get_active, $menu_waround_active->get_active + ); - $screenshot = $screenshooter->window(); + $screenshot = $screenshooter->window(); - } + } - #control some wm related settings - if (defined $curr_value && $curr_value != -1) { - fct_control_wm_settings('stop', $curr_value); - } + #control some wm related settings + if (defined $curr_value && $curr_value != -1) { + fct_control_wm_settings('stop', $curr_value); + } - #selection - } elsif ($data eq "select" || $data eq "tray_select") { + #selection + } elsif ($data eq "select" || $data eq "tray_select") { - if (defined $extra && $extra) { + if (defined $extra && $extra) { - my @coords = split(',', $extra); + my @coords = split(',', $extra); - $screenshooter = Shutter::Screenshot::SelectorAuto->new($sc, $include_cursor, $delay_value, $notify_timeout_active->get_active,); + $screenshooter = Shutter::Screenshot::SelectorAuto->new($sc, $include_cursor, $delay_value, $notify_timeout_active->get_active,); - $screenshot = $screenshooter->select_auto($coords[0], $coords[1], $coords[2], $coords[3]); + $screenshot = $screenshooter->select_auto($coords[0], $coords[1], $coords[2], $coords[3]); - } else { + } else { - $screenshooter = Shutter::Screenshot::SelectorAdvanced->new( - $sc, $include_cursor, $delay_value, $notify_timeout_active->get_active, - $zoom_active->get_active, $hide_time->get_value, $as_help_active->get_active, $asel_size3->get_value, - $asel_size4->get_value, $asel_size1->get_value, $asel_size2->get_value, $as_confirmation_necessary->get_active, - ); + $screenshooter = Shutter::Screenshot::SelectorAdvanced->new( + $sc, $include_cursor, $delay_value, $notify_timeout_active->get_active, + $zoom_active->get_active, $hide_time->get_value, $as_help_active->get_active, $asel_size3->get_value, + $asel_size4->get_value, $asel_size1->get_value, $asel_size2->get_value, $as_confirmation_necessary->get_active, + ); - $screenshot = $screenshooter->select_advanced(); + $screenshot = $screenshooter->select_advanced(); - } + } - #web - } elsif ($data eq "web" || $data eq "tray_web") { + #web + } elsif ($data eq "web" || $data eq "tray_web") { - my $website_width = 1024; - if ($combobox_web_width->get_active_text =~ /(\d+)/) { - $website_width = $1; - } + my $website_width = 1024; + if ($combobox_web_width->get_active_text =~ /(\d+)/) { + $website_width = $1; + } - print "\nvirtual website width: $website_width\n" - if $sc->get_debug; + print "\nvirtual website width: $website_width\n" + if $sc->get_debug; - #determine timeout - my $web_menu = $st->{_web}->get_menu; - my @timeouts = $web_menu->get_children; - my $timeout = undef; - foreach my $to (@timeouts) { - if ($to->get_active) { - $timeout = $to->get_name; - $timeout =~ /([0-9]+)/; - $timeout = $1; - print $timeout. "\n" if $sc->get_debug; + #determine timeout + my $web_menu = $st->{_web}->get_menu; + my @timeouts = $web_menu->get_children; + my $timeout = undef; + foreach my $to (@timeouts) { + if ($to->get_active) { + $timeout = $to->get_name; + $timeout =~ /([0-9]+)/; + $timeout = $1; + print $timeout. "\n" if $sc->get_debug; + } } - } - $screenshooter = Shutter::Screenshot::Web->new($sc, $timeout, $website_width); - $screenshot = $screenshooter->dlg_website($extra); + $screenshooter = Shutter::Screenshot::Web->new($sc, $timeout, $website_width); + $screenshot = $screenshooter->dlg_website($extra); - #window by xid - } elsif ($data =~ /^shutter_window_direct(.*)/) { + #window by xid + } elsif ($data =~ /^shutter_window_direct(.*)/) { - my $xid = $1; - print "Selected xid: $xid\n" if $sc->get_debug; + my $xid = $1; + print "Selected xid: $xid\n" if $sc->get_debug; - #control some wm related settings - my $curr_value = fct_control_wm_settings('start'); + #control some wm related settings + my $curr_value = fct_control_wm_settings('start'); - #change mode (imitating selecting a window by mouse) - $data = "window"; + #change mode (imitating selecting a window by mouse) + $data = "window"; - $screenshooter = Shutter::Screenshot::WindowXid->new( - $sc, $include_cursor, $delay_value, $notify_timeout_active->get_active, - $border_active->get_active, $winresize_active->get_active, $winresize_w->get_value, $winresize_h->get_value, - $hide_time->get_value, $data, $autoshape_active->get_active - ); + $screenshooter = Shutter::Screenshot::WindowXid->new( + $sc, $include_cursor, $delay_value, $notify_timeout_active->get_active, + $border_active->get_active, $winresize_active->get_active, $winresize_w->get_value, $winresize_h->get_value, + $hide_time->get_value, $data, $autoshape_active->get_active + ); - $screenshot = $screenshooter->window_by_xid($xid); + $screenshot = $screenshooter->window_by_xid($xid); - #control some wm related settings - if (defined $curr_value && $curr_value != -1) { - fct_control_wm_settings('stop', $curr_value); - } + #control some wm related settings + if (defined $curr_value && $curr_value != -1) { + fct_control_wm_settings('stop', $curr_value); + } - } elsif ($data =~ /^shutter_wrksp_direct/) { + } elsif ($data =~ /^shutter_wrksp_direct/) { - #we need to handle different wm, e.g. metacity, compiz here - my $selected_workspace = undef; - my $vpx = undef; - my $vpy = undef; + #we need to handle different wm, e.g. metacity, compiz here + my $selected_workspace = undef; + my $vpx = undef; + my $vpy = undef; - #compiz - if ($data =~ /compiz(\d*)x(\d*)/) { - $vpx = $1; - $vpy = $2; - print "Sel. Viewport: $vpx, $vpy\n" if $sc->get_debug; - - #metacity etc. - } elsif ($data =~ /shutter_wrksp_direct(.*)/) { - $selected_workspace = $1; - print "Sel. Workspace: $selected_workspace\n" - if $sc->get_debug; + #compiz + if ($data =~ /compiz(\d*)x(\d*)/) { + $vpx = $1; + $vpy = $2; + print "Sel. Viewport: $vpx, $vpy\n" if $sc->get_debug; - #all workspaces - } elsif ($data =~ /shutter_wrksp_all/) { - print "Capturing all workspaces\n" - if $sc->get_debug; - $selected_workspace = 'all'; - } + #metacity etc. + } elsif ($data =~ /shutter_wrksp_direct(.*)/) { + $selected_workspace = $1; + print "Sel. Workspace: $selected_workspace\n" + if $sc->get_debug; - $screenshooter = - Shutter::Screenshot::Workspace->new($sc, $include_cursor, $delay_value, $notify_timeout_active->get_active, $selected_workspace, $vpx, $vpy, $current_monitor_active->get_active); + #all workspaces + } elsif ($data =~ /shutter_wrksp_all/) { + print "Capturing all workspaces\n" + if $sc->get_debug; + $selected_workspace = 'all'; + } - if ($selected_workspace eq 'all') { - $screenshot = $screenshooter->workspaces(); - } else { - $screenshot = $screenshooter->workspace(); - } + $screenshooter = + Shutter::Screenshot::Workspace->new($sc, $include_cursor, $delay_value, $notify_timeout_active->get_active, $selected_workspace, $vpx, $vpy, $current_monitor_active->get_active); + + if ($selected_workspace eq 'all') { + $screenshot = $screenshooter->workspaces(); + } else { + $screenshot = $screenshooter->workspace(); + } - } elsif ($data eq "redoshot") { + } elsif ($data eq "redoshot") { - #~ my $key = fct_get_last_capture(); - #~ if(defined $key && exists $session_screens{$key}->{'history'} && defined $session_screens{$key}->{'history'}){ - #~ $screenshooter = $session_screens{$key}->{'history'}; - #~ $screenshot = $screenshooter->redo_capture; - #~ }else{ - #~ $screenshot = 3; - #~ } + #~ my $key = fct_get_last_capture(); + #~ if(defined $key && exists $session_screens{$key}->{'history'} && defined $session_screens{$key}->{'history'}){ + #~ $screenshooter = $session_screens{$key}->{'history'}; + #~ $screenshot = $screenshooter->redo_capture; + #~ }else{ + #~ $screenshot = 3; + #~ } - if ($screenshooter = fct_get_last_capture()) { + if ($screenshooter = fct_get_last_capture()) { - #we need to handle menu and tooltip in a special way - if ($screenshooter->can('get_mode')) { - if (my $mode = $screenshooter->get_mode) { + #we need to handle menu and tooltip in a special way + if ($screenshooter->can('get_mode')) { + if (my $mode = $screenshooter->get_mode) { - #control some wm related settings - my $curr_value = undef; - if (($mode eq "window" || $mode eq "tray_window" || $mode eq "awindow" || $mode eq "tray_awindow" || $mode eq "section" || $mode eq "tray_section")) { - $curr_value = fct_control_wm_settings('start'); - } + #control some wm related settings + my $curr_value = undef; + if (($mode eq "window" || $mode eq "tray_window" || $mode eq "awindow" || $mode eq "tray_awindow" || $mode eq "section" || $mode eq "tray_section")) { + $curr_value = fct_control_wm_settings('start'); + } - if ($mode eq "menu" || $mode eq "tray_menu") { - $st->{_menu}->signal_emit('clicked'); - return FALSE; - } elsif ($mode eq "tooltip" || $mode eq "tray_tooltip") { - $st->{_tooltip}->signal_emit('clicked'); - return FALSE; + if ($mode eq "menu" || $mode eq "tray_menu") { + $st->{_menu}->signal_emit('clicked'); + return FALSE; + } elsif ($mode eq "tooltip" || $mode eq "tray_tooltip") { + $st->{_tooltip}->signal_emit('clicked'); + return FALSE; + } else { + $screenshot = $screenshooter->redo_capture; + } + + #control some wm related settings + if (($mode eq "window" || $mode eq "tray_window" || $mode eq "awindow" || $mode eq "tray_awindow" || $mode eq "section" || $mode eq "tray_section")) { + if (defined $curr_value && $curr_value != -1) { + fct_control_wm_settings('stop', $curr_value); + } + } + + #window by xid } else { + + #control some wm related settings + my $curr_value = fct_control_wm_settings('start'); $screenshot = $screenshooter->redo_capture; - } - #control some wm related settings - if (($mode eq "window" || $mode eq "tray_window" || $mode eq "awindow" || $mode eq "tray_awindow" || $mode eq "section" || $mode eq "tray_section")) { + #control some wm related settings if (defined $curr_value && $curr_value != -1) { fct_control_wm_settings('stop', $curr_value); } } - - #window by xid } else { - - #control some wm related settings - my $curr_value = fct_control_wm_settings('start'); $screenshot = $screenshooter->redo_capture; - - #control some wm related settings - if (defined $curr_value && $curr_value != -1) { - fct_control_wm_settings('stop', $curr_value); - } } } else { - $screenshot = $screenshooter->redo_capture; + $screenshot = 3; } - } else { - $screenshot = 3; - } - } elsif ($data eq "redoshot_this") { + } elsif ($data eq "redoshot_this") { - #get current screenshot (current notebook page) - my $key = fct_get_current_file(); + #get current screenshot (current notebook page) + my $key = fct_get_current_file(); - #or get the selected screenshot in the view - unless (defined $key) { - $session_start_screen{'first_page'}->{'view'}->selected_foreach( - sub { - my ($view, $path) = @_; - my $iter = $session_start_screen{'first_page'}->{'model'}->get_iter($path); - if (defined $iter) { - $key = $session_start_screen{'first_page'}->{'model'}->get_value($iter, 2); - } - }, - undef - ); - } + #or get the selected screenshot in the view + unless (defined $key) { + $session_start_screen{'first_page'}->{'view'}->selected_foreach( + sub { + my ($view, $path) = @_; + my $iter = $session_start_screen{'first_page'}->{'model'}->get_iter($path); + if (defined $iter) { + $key = $session_start_screen{'first_page'}->{'model'}->get_value($iter, 2); + } + }, + undef + ); + } - if ( defined $key - && exists $session_screens{$key}->{'history'} - && defined $session_screens{$key}->{'history'}) - { - $screenshooter = $session_screens{$key}->{'history'}; + if ( defined $key + && exists $session_screens{$key}->{'history'} + && defined $session_screens{$key}->{'history'}) + { + $screenshooter = $session_screens{$key}->{'history'}; - #we need to handle menu and tooltip in a special way - if ($screenshooter->can('get_mode')) { - if (my $mode = $screenshooter->get_mode) { + #we need to handle menu and tooltip in a special way + if ($screenshooter->can('get_mode')) { + if (my $mode = $screenshooter->get_mode) { - #control some wm related settings - my $curr_value = undef; - if (($mode eq "window" || $mode eq "tray_window" || $mode eq "awindow" || $mode eq "tray_awindow" || $mode eq "section" || $mode eq "tray_section")) { - $curr_value = fct_control_wm_settings('start'); - } + #control some wm related settings + my $curr_value = undef; + if (($mode eq "window" || $mode eq "tray_window" || $mode eq "awindow" || $mode eq "tray_awindow" || $mode eq "section" || $mode eq "tray_section")) { + $curr_value = fct_control_wm_settings('start'); + } - if ($mode eq "menu" || $mode eq "tray_menu") { - $st->{_menu}->signal_emit('clicked'); - return FALSE; - } elsif ($mode eq "tooltip" || $mode eq "tray_tooltip") { - $st->{_tooltip}->signal_emit('clicked'); - return FALSE; + if ($mode eq "menu" || $mode eq "tray_menu") { + $st->{_menu}->signal_emit('clicked'); + return FALSE; + } elsif ($mode eq "tooltip" || $mode eq "tray_tooltip") { + $st->{_tooltip}->signal_emit('clicked'); + return FALSE; + } else { + $screenshot = $screenshooter->redo_capture; + } + + #control some wm related settings + if (($mode eq "window" || $mode eq "tray_window" || $mode eq "awindow" || $mode eq "tray_awindow" || $mode eq "section" || $mode eq "tray_section")) { + if (defined $curr_value && $curr_value != -1) { + fct_control_wm_settings('stop', $curr_value); + } + } + + #window by xid } else { + + #control some wm related settings + my $curr_value = fct_control_wm_settings('start'); $screenshot = $screenshooter->redo_capture; - } - #control some wm related settings - if (($mode eq "window" || $mode eq "tray_window" || $mode eq "awindow" || $mode eq "tray_awindow" || $mode eq "section" || $mode eq "tray_section")) { + #control some wm related settings if (defined $curr_value && $curr_value != -1) { fct_control_wm_settings('stop', $curr_value); } } - - #window by xid } else { - - #control some wm related settings - my $curr_value = fct_control_wm_settings('start'); $screenshot = $screenshooter->redo_capture; - - #control some wm related settings - if (defined $curr_value && $curr_value != -1) { - fct_control_wm_settings('stop', $curr_value); - } } } else { - $screenshot = $screenshooter->redo_capture; + $screenshot = 3; } - } else { - $screenshot = 3; - } - } else { + } else { - #show error dialog - my $response = $sd->dlg_error_message($d->get("Triggered invalid screenshot action."), $d->get("Error while taking the screenshot.")); + #show error dialog + my $response = $sd->dlg_error_message($d->get("Triggered invalid screenshot action."), $d->get("Error while taking the screenshot.")); - fct_show_status_message(1, $d->get("Error while taking the screenshot.")); - fct_control_main_window('show'); - return FALSE; + fct_show_status_message(1, $d->get("Error while taking the screenshot.")); + fct_control_main_window('show'); + return FALSE; + } } #screenshot was taken at this stage... @@ -8655,7 +8700,8 @@ sub STARTUP { #selection my $menuitem_select = Gtk3::ImageMenuItem->new_with_mnemonic($d->get('_Selection')); - $menuitem_select->set_sensitive($x11_supported); + # served via interactive XDG portal on Wayland too + $menuitem_select->set_sensitive(TRUE); eval { my $ccursor_pb = Gtk3::Gdk::Cursor::new('left_ptr')->get_image->scale_simple($shf->icon_size('menu'), 'bilinear'); $menuitem_select->set_image(Gtk3::Image->new_from_pixbuf($ccursor_pb)); @@ -8704,7 +8750,8 @@ sub STARTUP { #window my $menuitem_window = Gtk3::ImageMenuItem->new_with_mnemonic($d->get('Window _under Cursor')); - $menuitem_window->set_sensitive($x11_supported); + # served via interactive XDG portal on Wayland too + $menuitem_window->set_sensitive(TRUE); if ($traytheme->has_icon('preferences-system-windows')) { $menuitem_window->set_image(Gtk3::Image->new_from_icon_name('preferences-system-windows', 'menu')); } else { diff --git a/share/shutter/resources/modules/Shutter/Screenshot/Wayland.pm b/share/shutter/resources/modules/Shutter/Screenshot/Wayland.pm index e6c5e16e..b3be7c10 100644 --- a/share/shutter/resources/modules/Shutter/Screenshot/Wayland.pm +++ b/share/shutter/resources/modules/Shutter/Screenshot/Wayland.pm @@ -1,13 +1,19 @@ use utf8; use strict; use warnings; -use Net::DBus; use Net::DBus::Reactor; +use Net::DBus; package Shutter::Screenshot::Wayland; sub xdg_portal { my $screenshooter = shift; + my $interactive = shift; + my $target = shift; + + # Fall back to fullscreen + $target = 1 unless defined $target; + my $reactor = Net::DBus::Reactor->main; my $bus = Net::DBus->find; my $me = $bus->get_unique_name; @@ -31,15 +37,28 @@ sub xdg_portal { $token =~ s/\.//g; my $request = $portal_service->get_object("/org/freedesktop/portal/desktop/request/$me/$token", 'org.freedesktop.portal.Request'); my $conn = $request->connect_to_signal(Response => $cb); - my $request_path = $portal->Screenshot('', {handle_token=>$token}); + + my %options = (handle_token => $token); + + $options{interactive} = Net::DBus::dbus_boolean($interactive); + if ($interactive eq 0) { + $options{target} = Net::DBus::dbus_uint32($target); + } + + my $request_path = $portal->Screenshot('', \%options); + if ($request->get_object_path ne $request_path) { $request->disconnect_from_signal(Response => $conn); $request = $portal_service->get_object($request_path, 'org.freedesktop.portal.Request'); $conn = $request->connect_to_signal(Response => $cb); } + $reactor->run; + $request->disconnect_from_signal(Response => $conn); if ($num != 0) { + # portal Response: 1 = user cancelled -> treat as abort (code 5), not error + return 5 if $num == 1; $screenshooter->{_error_text} = "Response $num from XDG portal"; return 9; } From bb3b2d4d842afd5404568587909fb45f4fcd440d Mon Sep 17 00:00:00 2001 From: Photon89 Date: Mon, 25 May 2026 17:42:48 +0200 Subject: [PATCH 2/5] Fixed file permissions --- bin/shutter | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 bin/shutter diff --git a/bin/shutter b/bin/shutter old mode 100644 new mode 100755 From c61459ba0ec95fcb515dbd27941c6d0b256f0ec5 Mon Sep 17 00:00:00 2001 From: Photon89 Date: Tue, 26 May 2026 08:28:25 +0200 Subject: [PATCH 3/5] Fixed getting available portal targets if target property undefined --- bin/shutter | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/bin/shutter b/bin/shutter index bbd390cd..dff97662 100755 --- a/bin/shutter +++ b/bin/shutter @@ -596,14 +596,23 @@ sub STARTUP { # 4: Area (corresponds "select") # 8: Active Window (corresponds "awindow") # See: https://github.com/flatpak/xdg-desktop-portal/pull/1981 - my $raw = Net::DBus->session - ->get_service("org.freedesktop.portal.Desktop") - ->get_object("/org/freedesktop/portal/desktop") - ->as_interface("org.freedesktop.DBus.Properties") - ->Get("org.freedesktop.portal.Screenshot", "AvailableTargets"); - my $val = (ref($raw) && $raw->can('value')) ? $raw->value : $raw; - my $targets = (ref($val) eq 'ARRAY') ? $val : []; - %supported_targets = map { $_ => 1 } @$targets; + my $targets_raw; + eval { + $targets_raw = Net::DBus->session + ->get_service("org.freedesktop.portal.Desktop") + ->get_object("/org/freedesktop/portal/desktop") + ->as_interface("org.freedesktop.DBus.Properties") + ->Get("org.freedesktop.portal.Screenshot", "AvailableTargets"); + }; + if ($@) { + # Fehler aufgetreten (Eigenschaft existiert nicht) -> Hash leeren + %supported_targets = (); + } else { + # Eigenschaft existiert -> Werte normal verarbeiten + my $targets_val = (ref($targets_raw) && $targets_raw->can('value')) ? $targets_raw->value : $targets_raw; + my $targets = (ref($targets_val) eq 'ARRAY') ? $targets_val : []; + %supported_targets = map { $_ => 1 } @$targets; + } } if ($x11_supported) { From ae11a3d20dc47201bec097ca3cb502907c98f0cb Mon Sep 17 00:00:00 2001 From: Photon89 Date: Tue, 26 May 2026 11:12:05 +0200 Subject: [PATCH 4/5] Fix retrieving ActiveTargets from portal --- bin/shutter | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/bin/shutter b/bin/shutter index dff97662..81c21495 100755 --- a/bin/shutter +++ b/bin/shutter @@ -604,14 +604,18 @@ sub STARTUP { ->as_interface("org.freedesktop.DBus.Properties") ->Get("org.freedesktop.portal.Screenshot", "AvailableTargets"); }; + if ($@) { - # Fehler aufgetreten (Eigenschaft existiert nicht) -> Hash leeren %supported_targets = (); } else { - # Eigenschaft existiert -> Werte normal verarbeiten - my $targets_val = (ref($targets_raw) && $targets_raw->can('value')) ? $targets_raw->value : $targets_raw; - my $targets = (ref($targets_val) eq 'ARRAY') ? $targets_val : []; - %supported_targets = map { $_ => 1 } @$targets; + if (defined $targets_raw) { + $supported_targets{'1'} = 1 if ($targets_raw & 1); + $supported_targets{'2'} = 1 if ($targets_raw & 2); + $supported_targets{'4'} = 1 if ($targets_raw & 4); + $supported_targets{'8'} = 1 if ($targets_raw & 8); + } else { + %supported_targets = (); + } } } From 5338d99f7aacb6c2f71a0b08588b157c1c5869b0 Mon Sep 17 00:00:00 2001 From: Photon89 Date: Wed, 27 May 2026 08:54:18 +0200 Subject: [PATCH 5/5] Update warning message --- bin/shutter | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/shutter b/bin/shutter index 81c21495..952a95bc 100755 --- a/bin/shutter +++ b/bin/shutter @@ -977,7 +977,8 @@ sub STARTUP { unless ($x11_supported) { my $wayland_warning = Gtk3::Label->new; $wayland_warning->set_line_wrap(TRUE); - $wayland_warning->set_markup($d->get("Wayland support is limited, for more advanced screenshots please switch back to Xorg. Click here for details.")); + $wayland_warning->set_markup($d->get("Wayland support is limited, for more advanced screenshots please switch back to Xorg. Click here for details. Selection and window +modes are currently experimental, your desktop environment might fall back to an interactive mode chooser or full screen capture.")); $vbox->pack_start($wayland_warning, FALSE, TRUE, 0); }