Gtk2-Ex-ErrorTextDialog

 view release on metacpan or  search on metacpan

lib/Gtk2/Ex/ErrorTextDialog/Handler.pm  view on Meta::CPAN


my $_idle_another_message;
my $_idle_recursions = 0;
my $_idle_handler_id;

our $exception_handler_depth = 0;

sub exception_handler {
  my ($msg) = @_;
  if (DEBUG) { print STDERR "exception_handler() $exception_handler_depth\n"; }

  # Normally $SIG handlers run with themselves shadowed out, and the Glib
  # exception handler doesn't re-invoke, so suspect warnings or errors in
  # the code here won't recurse normally, but have this as some protection
  # just in case.
  #
  if ($exception_handler_depth >= 3) {
    return 1; # stay installed
  }
  if ($exception_handler_depth >= 2) {
    print STDERR "ErrorTextDialog Handler: ignoring recursive exception_handler calls\n";
    return 1; # stay installed
  }
  local $exception_handler_depth = $exception_handler_depth + 1;
  if (DEBUG) { print STDERR "  depth now $exception_handler_depth\n"; }

  #--------------------------------------------

  if (_fh_prints_wide('STDERR')) {
    $msg = _maybe_locale_bytes_to_wide ($msg);
  }
  print STDERR $msg;

  #--------------------------------------------

  if ($_idle_recursions == 4) {
    $_idle_recursions++;
    print STDERR "ErrorTextDialog Handler: repeated messages adding to dialog, skip GUI from now on\n";

  } elsif ($_idle_recursions < 4
           && ! Devel::GlobalDestruction::in_global_destruction()) {
    $_idle_another_message = 1;
    push @Gtk2::Ex::ErrorTextDialog::_instance_pending, $msg;

    # try to protect against unbounded growth of @_instance_pending
    if (@Gtk2::Ex::ErrorTextDialog::_instance_pending > 500) {
      splice @Gtk2::Ex::ErrorTextDialog::_instance_pending, 0, -500,
        '[Big slew of pending messages truncated ...]';
    }

    $_idle_handler_id ||= Glib::Idle->add
      (\&_idle_handler, undef, Glib::G_PRIORITY_HIGH);
  }

  if (DEBUG) { print STDERR "exception_handler() end\n"; }
  return 1; # stay installed
}

# $_idle_handler_id is zapped at the start so exception_handler() will add
# another _idle_handler() for any further messages generated within the
# present _idle_handler() run.  Anything before popup_add_message() will be
# covered by the present run, but anything after it needs another run.
#
# $_idle_recursions is incremented at the start as a worst case assumption
# that the code will die.  Then if the code runs successfully to the end it
# can be cleared.  It's cleared only if there were no further messages
# generated from within _idle_handler().  Further messages are noted by
# exception_handler() setting $_idle_another_message.
#
sub _idle_handler {
  $_idle_recursions++;
  $_idle_another_message = 0;
  undef $_idle_handler_id;
  if (DEBUG) { print STDERR "idle_handler() runs $_idle_recursions\n"; }

  require Gtk2::Ex::ErrorTextDialog;
  Gtk2::Ex::ErrorTextDialog->popup_add_message (undef);

  if (! $_idle_another_message) {
    $_idle_recursions = 0;
  }
  if (DEBUG) {
    print STDERR "idle_handler() end, recursions now $_idle_recursions\n";
  }
  return 0; # Glib::SOURCE_REMOVE
}

sub log_handler {
  require Gtk2::Ex::ErrorTextDialog;
  exception_handler (Gtk2::Ex::ErrorTextDialog::_log_to_string (@_));
}

#-----------------------------------------------------------------------------
# generic helpers

# _fh_prints_wide($fh) returns true if wide chars can be printed to file
# handle $fh.
#
# PerlIO::get_layers() is pre-loaded, probably, but PerlIO::F_UTF8() from
# PerlIO.pm is not.
#
sub _fh_prints_wide {
  my ($fh) = @_;
  return (PerlIO::get_layers($fh, output => 1, details => 1))[-1] # top flags
    & PerlIO::F_UTF8();
}

# If $str is not wide, and it has some non-ascii, then try to decode them in
# the locale charset.  PERLQQ means bad stuff is escaped.
sub _maybe_locale_bytes_to_wide {
  my ($str) = @_;
  if (! utf8::is_utf8 ($str) && $str =~ /[^[:ascii:]]/) {
    require Encode;
    $str = Encode::decode (_locale_charset_or_ascii(),
                           $str, Encode::FB_PERLQQ());
  }
  return $str;
}

# _locale_charset_or_ascii() returns the locale charset from I18N::Langinfo,
# or 'ASCII' if nl_langinfo() is not available.
#
# langinfo() croaks "nl_langinfo() not implemented on this architecture" if
# not available.  Though anywhere able to run Gtk would have nl_langinfo(),
# wouldn't it?
#
my $_locale_charset_or_ascii;
sub _locale_charset_or_ascii {
  goto $_locale_charset_or_ascii;
}
BEGIN {
  $_locale_charset_or_ascii = sub {
    my $subr = sub { I18N::Langinfo::langinfo(I18N::Langinfo::CODESET()) };
    if (! eval { &$subr(); 1 }) {
      $subr = sub { 'ASCII' };
    }
    goto ($_locale_charset_or_ascii = $subr);



( run in 0.782 second using v1.01-cache-2.11-cpan-364913b4093 )