From 7e0193de850854fb310574655fe9adf8c88b2aa1 Mon Sep 17 00:00:00 2001 From: Photon89 Date: Sat, 9 May 2026 07:50:55 +0200 Subject: [PATCH 1/3] Make Shutter read the xpm cursors x_hot and y_hot coordinates --- .../modules/Shutter/Draw/DrawingTool.pm | 82 +++++++++++++++---- 1 file changed, 68 insertions(+), 14 deletions(-) diff --git a/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm b/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm index c398d200..f98d87c5 100644 --- a/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm +++ b/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm @@ -196,6 +196,40 @@ sub new { #~ print "$self dying at\n"; #~ } +# Workaround for broken xpm parsing in glycin +sub parse_xpm_hotspot { + my ($xpm_path) = @_; + my ($x_hot, $y_hot); + + open my $fh, '<', $xpm_path or do { + print "ERROR: Cannot open $xpm_path: $!\n"; + return (undef, undef); + }; + + while (my $line = <$fh>) { + chomp($line); + + # Look for the XPM header line with format: + # "width height ncolors chars_per_pixel [x_hot y_hot]" + # Example: "32 32 3 1 4 4" + if ($line =~ /"(\d+)\s+(\d+)\s+(\d+)\s+(\d+)(?:\s+(\d+)\s+(\d+))?/) { + my ($width, $height, $ncolors, $cpp, $xh, $yh) = ($1, $2, $3, $4, $5, $6); + + if (defined($xh) && defined($yh)) { + $x_hot = $xh; + $y_hot = $yh; + } else { + print "DEBUG: No hotspot in header\n"; + } + + last; # Header is on the first data line + } + } + close $fh; + + return ($x_hot, $y_hot); +} + sub show { my $self = shift; @@ -265,13 +299,29 @@ sub show { #http://www.inkscape.org my @cursors = bsd_glob($self->{_dicons} . "/cursor/*"); foreach my $cursor_path (@cursors) { - my ($cname, $folder, $type) = fileparse($cursor_path, qr/\.[^.]*/); - $self->{_cursors}{$cname} = Gtk3::Gdk::Pixbuf->new_from_file($cursor_path); + my ($cname, $folder, $type) = fileparse($cursor_path, qr/\.[^.]*/); + my $pixbuf = Gtk3::Gdk::Pixbuf->new_from_file($cursor_path); + + if ($pixbuf) { + my $width = $pixbuf->get_width(); + my $height = $pixbuf->get_height(); + + # Parse hotspot from file + my ($x_hot, $y_hot) = parse_xpm_hotspot($cursor_path); + + # Fallback to center if not found + $x_hot //= $width / 2; + $y_hot //= $height / 2; - #see 'man xcursor' for a detailed description - #of these values - $self->{_cursors}{$cname}{'x_hot'} = $self->{_cursors}{$cname}->get_option('x_hot'); - $self->{_cursors}{$cname}{'y_hot'} = $self->{_cursors}{$cname}->get_option('y_hot'); + # Store as a hash with pixbuf and hotspot data + $self->{_cursors}{$cname} = { + 'pixbuf' => $pixbuf, + 'x_hot' => $x_hot, + 'y_hot' => $y_hot, + }; + } else { + print "ERROR: Failed to load pixbuf\n"; + } } #setu ui @@ -1147,17 +1197,21 @@ sub change_drawing_tool_cb { $self->{_btn_ok_c}->grab_focus; } - + if ($self->{_canvas} && $self->{_canvas}->get_window) { - if (exists $self->{_cursors}{$self->{_current_mode_descr}}) { - $cursor = Gtk3::Gdk::Cursor->new_from_pixbuf( - Gtk3::Gdk::Display::get_default(), $self->{_cursors}{$self->{_current_mode_descr}}, - $self->{_cursors}{$self->{_current_mode_descr}}{'x_hot'}, $self->{_cursors}{$self->{_current_mode_descr}}{'y_hot'}, - ); - } + if (exists $self->{_cursors}{$self->{_current_mode_descr}}) { + my $cursor_data = $self->{_cursors}{$self->{_current_mode_descr}}; + + $cursor = Gtk3::Gdk::Cursor->new_from_pixbuf( + Gtk3::Gdk::Display::get_default(), + $cursor_data->{'pixbuf'}, + $cursor_data->{'x_hot'}, + $cursor_data->{'y_hot'}, + ); + } - $self->{_canvas}->get_window->set_cursor($cursor); + $self->{_canvas}->get_window->set_cursor($cursor); } return TRUE; From 4edb99ccae1f4d5aa7b6ec0c65ee247e4158bf1e Mon Sep 17 00:00:00 2001 From: Michael Kogan Date: Sat, 9 May 2026 09:17:32 +0200 Subject: [PATCH 2/3] Minur restructuring and formatting fixes in DrawingTool.pm --- .../modules/Shutter/Draw/DrawingTool.pm | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm b/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm index f98d87c5..7c93a197 100644 --- a/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm +++ b/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm @@ -196,7 +196,8 @@ sub new { #~ print "$self dying at\n"; #~ } -# Workaround for broken xpm parsing in glycin +# Workaround for broken xpm parsing in glycin: +# https://gitlab.gnome.org/GNOME/glycin/-/work_items/291 sub parse_xpm_hotspot { my ($xpm_path) = @_; my ($x_hot, $y_hot); @@ -219,7 +220,7 @@ sub parse_xpm_hotspot { $x_hot = $xh; $y_hot = $yh; } else { - print "DEBUG: No hotspot in header\n"; + print "DEBUG: No hotspot in header in $xpm_path\n"; } last; # Header is on the first data line @@ -302,10 +303,13 @@ sub show { my ($cname, $folder, $type) = fileparse($cursor_path, qr/\.[^.]*/); my $pixbuf = Gtk3::Gdk::Pixbuf->new_from_file($cursor_path); - if ($pixbuf) { + if (!$pixbuf) { + print "ERROR: Failed to load pixbuf from $cursor_path\n"; + next; + } my $width = $pixbuf->get_width(); my $height = $pixbuf->get_height(); - + # Parse hotspot from file my ($x_hot, $y_hot) = parse_xpm_hotspot($cursor_path); @@ -315,13 +319,10 @@ sub show { # Store as a hash with pixbuf and hotspot data $self->{_cursors}{$cname} = { - 'pixbuf' => $pixbuf, - 'x_hot' => $x_hot, - 'y_hot' => $y_hot, + 'pixbuf' => $pixbuf, + 'x_hot' => $x_hot, + 'y_hot' => $y_hot, }; - } else { - print "ERROR: Failed to load pixbuf\n"; - } } #setu ui @@ -1197,7 +1198,7 @@ sub change_drawing_tool_cb { $self->{_btn_ok_c}->grab_focus; } - + if ($self->{_canvas} && $self->{_canvas}->get_window) { if (exists $self->{_cursors}{$self->{_current_mode_descr}}) { From 005867eaafcc1968ebf9e5e6caed4ddd42cbadf7 Mon Sep 17 00:00:00 2001 From: Michael Kogan Date: Sat, 9 May 2026 10:03:55 +0200 Subject: [PATCH 3/3] Fix whitespaces in DrawingTool.pm --- share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm b/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm index 7c93a197..92eaaf7a 100644 --- a/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm +++ b/share/shutter/resources/modules/Shutter/Draw/DrawingTool.pm @@ -303,7 +303,7 @@ sub show { my ($cname, $folder, $type) = fileparse($cursor_path, qr/\.[^.]*/); my $pixbuf = Gtk3::Gdk::Pixbuf->new_from_file($cursor_path); - if (!$pixbuf) { + if (!$pixbuf) { print "ERROR: Failed to load pixbuf from $cursor_path\n"; next; } @@ -1201,7 +1201,7 @@ sub change_drawing_tool_cb { if ($self->{_canvas} && $self->{_canvas}->get_window) { - if (exists $self->{_cursors}{$self->{_current_mode_descr}}) { + if (exists $self->{_cursors}{$self->{_current_mode_descr}}) { my $cursor_data = $self->{_cursors}{$self->{_current_mode_descr}}; $cursor = Gtk3::Gdk::Cursor->new_from_pixbuf(