App-ClusterSSH
view release on metacpan or search on metacpan
lib/App/ClusterSSH.pm view on Meta::CPAN
return $self;
}
sub get_font_size() {
my ($self) = @_;
$self->debug( 2, "Fetching font size" );
# get atom name<->number relations
my $quad_width = $xdisplay->atom("QUAD_WIDTH");
my $pixel_size = $xdisplay->atom("PIXEL_SIZE");
my $font = $xdisplay->new_rsrc;
my $terminal_font = $self->config->{terminal_font};
$xdisplay->OpenFont( $font, $terminal_font );
my %font_info;
eval { (%font_info) = $xdisplay->QueryFont($font); }
|| die( "Fatal: Unrecognised font used ($terminal_font).\n"
. "Please amend \$HOME/.clusterssh/config with a valid font (see man page).\n"
);
$self->config->{internal_font_width}
= $font_info{properties}{$quad_width};
$self->config->{internal_font_height}
= $font_info{properties}{$pixel_size};
if ( !$self->config->{internal_font_width}
|| !$self->config->{internal_font_height} )
{
die( "Fatal: Unrecognised font used ($terminal_font).\n"
. "Please amend \$HOME/.clusterssh/config with a valid font (see man page).\n"
);
}
$self->debug( 2, "Done with font size" );
return $self;
}
sub show_console() {
my ($self) = shift;
$self->debug( 2, "Sending console to front" );
$self->config->{internal_previous_state} = "mid-change";
# fudge the counter to drop a redraw event;
$self->config->{internal_map_count} -= 4;
$xdisplay->flush();
$windows{main_window}->update();
select( undef, undef, undef, 0.2 ); #sleep for a mo
$windows{main_window}->withdraw
if $windows{main_window}->state ne "withdrawn";
# Sleep for a moment to give WM time to bring console back
select( undef, undef, undef, 0.5 );
if ( $self->config->{menu_send_autotearoff} ) {
$menus{send}->menu->tearOffMenu()->raise;
}
if ( $self->config->{menu_host_autotearoff} ) {
$menus{hosts}->menu->tearOffMenu()->raise;
}
$windows{main_window}->deiconify;
$windows{main_window}->raise;
$windows{main_window}->focus( -force );
$windows{text_entry}->focus( -force );
$self->config->{internal_previous_state} = "normal";
# fvwm seems to need this (Debian #329440)
$windows{main_window}->MapWindow;
return $self;
}
# set the first argument to the second if the first is undefined
# the equivalent of //= but works in older Perls (e.g. 5.8)
sub slash_slash_equal(\$$) {
if ( !defined( ${ $_[0] } ) ) {
${ $_[0] } = $_[1];
}
return ${ $_[0] };
}
# leave function def open here so we can be flexible in how it's called
sub retile_hosts {
my ( $self, $force ) = @_;
$force ||= "";
$self->debug( 2, "Retiling windows" );
my %config;
if ( $self->config->{window_tiling} ne "yes" && !$force ) {
$self->debug( 3,
"Not meant to be tiling; just reshow windows as they were" );
foreach my $server ( reverse( keys(%servers) ) ) {
$xdisplay->req( 'MapWindow', $servers{$server}{wid} );
}
$xdisplay->flush();
$self->show_console();
return;
}
# ALL SIZES SHOULD BE IN PIXELS for consistency
$self->debug( 2, "Count is currently ", $self->config->{internal_total} );
if ( $self->config->{internal_total} == 0 ) {
# If nothing to tile, don't bother doing anything, just show console
return $self->show_console();
}
# work out terminal pixel size from terminal size & font size
# does not include any title bars or scroll bars - purely text area
$self->config->{internal_terminal_cols}
= ( $self->config->{terminal_size} =~ /(\d+)x.*/ )[0];
lib/App/ClusterSSH.pm view on Meta::CPAN
$self->open_client_windows(@names);
}
if ( defined $menus{listbox} && $menus{listbox}->curselection() ) {
my @hosts = $menus{listbox}->get( $menus{listbox}->curselection() );
$self->debug( 2, "host=", join( ' ', @hosts ) );
$self->open_client_windows( $self->resolve_names(@hosts) );
}
$self->build_hosts_menu();
$menus{host_entry} = "";
# retile, or bring console to front
if ( $self->config->{window_tiling} eq "yes" ) {
return $self->retile_hosts();
}
else {
return $self->show_console();
}
}
# attempt to re-add any hosts that have been closed since we started
# the session - either through errors or deliberate log-outs
sub re_add_closed_sessions() {
my ($self) = @_;
$self->debug( 2, "add closed sessions" );
return if ( scalar(@dead_hosts) == 0 );
my @new_hosts = @dead_hosts;
# clear out the list in case open fails
@dead_hosts = qw//;
# try to open
$self->open_client_windows(@new_hosts);
# update hosts list with current state
$self->build_hosts_menu();
# retile, or bring console to front
if ( $self->config->{window_tiling} eq "yes" ) {
return $self->retile_hosts();
}
else {
return $self->show_console();
}
}
sub build_hosts_menu() {
my ($self) = @_;
return if ( $self->config->{hide_menu} );
$self->debug( 2, "Building hosts menu" );
# first, empty the hosts menu from the last static entry + 1 on
my $menu = $menus{bar}->entrycget( 'Hosts', -menu );
$menu->delete( $host_menu_static_items, 'end' );
$self->debug( 3, "Menu deleted" );
# add back the separator
$menus{hosts}->separator;
$self->debug( 3, "Parsing list" );
my $menu_item_counter = $host_menu_static_items;
foreach my $svr ( $sort->( keys(%servers) ) ) {
$self->debug( 3, "Checking $svr and restoring active value" );
my $colbreak = 0;
if ( $menu_item_counter > $self->config->{max_host_menu_items} ) {
$colbreak = 1;
$menu_item_counter = 1;
}
$menus{hosts}->checkbutton(
-label => $svr,
-variable => \$servers{$svr}{active},
-columnbreak => $colbreak,
);
$menu_item_counter++;
}
$self->debug( 3, "Changing window title" );
$self->change_main_window_title();
$self->debug( 2, "Done" );
}
sub setup_repeat() {
my ($self) = @_;
$self->config->{internal_count} = 0;
# if this is too fast then we end up with queued invocations
# with no time to run anything else
$windows{main_window}->repeat(
500,
sub {
$self->config->{internal_count} = 0
if ( $self->config->{internal_count} > 60000 )
; # reset if too high
$self->config->{internal_count}++;
my $build_menu = 0;
$self->debug(
5,
"Running repeat;count=",
$self->config->{internal_count}
);
# See if there are any commands in the external command pipe
if ( defined $self->{external_command_pipe_fh} ) {
my $ext_cmd;
sysread( $self->{external_command_pipe_fh}, $ext_cmd, 400 );
if ($ext_cmd) {
my @external_commands = split( /\n/, $ext_cmd );
for my $cmd_line (@external_commands) {
chomp($cmd_line);
my ( $cmd, @tags ) = split /\s+/, $cmd_line;
$self->debug( 2,
"Got external command: $cmd -> @tags" );
for ($cmd) {
if (m/^open$/) {
lib/App/ClusterSSH.pm view on Meta::CPAN
$self->send_text_to_all_servers(
$self->config->{macro_servername} )
if ( $hotkey eq "key_clientname" );
$self->send_text_to_all_servers(
$self->config->{macro_hostname} )
if ( $hotkey eq "key_localname" );
$self->send_text_to_all_servers(
$self->config->{macro_username} )
if ( $hotkey eq "key_username" );
$self->add_host_by_name()
if ( $hotkey eq "key_addhost" );
$self->retile_hosts("force")
if ( $hotkey eq "key_retilehosts" );
$self->show_history() if ( $hotkey eq "key_history" );
$self->exit_prog() if ( $hotkey eq "key_quit" );
}
return;
}
}
}
# look for a <Control>-d and no hosts, so quit
$self->exit_prog()
if ( $state =~ /Control/ && $keysym eq "d" and !%servers );
$self->update_display_text( $keycodetosym{$keysymdec} )
if ( $event eq "KeyPress" && $keycodetosym{$keysymdec} );
# for all servers
foreach ( keys(%servers) ) {
# if active
if ( $servers{$_}{active} == 1 ) {
$self->debug( 3,
"Sending event $event with code $keycode (state=$state) to window $servers{$_}{wid}"
);
$xdisplay->SendEvent(
$servers{$_}{wid},
0,
$xdisplay->pack_event_mask($event),
$xdisplay->pack_event(
'name' => $event,
'detail' => $keycode,
'state' => $state,
'event' => $servers{$_}{wid},
'root' => $xdisplay->root(),
'same_screen' => 1,
)
) || warn("Error returned from SendEvent: $!");
}
}
$xdisplay->flush();
return $self;
}
sub create_menubar() {
my ($self) = @_;
$self->debug( 2, "create_menubar: started" );
$menus{bar} = $windows{main_window}->Menu();
$windows{main_window}->configure( -menu => $menus{bar}, )
unless $self->config->{hide_menu};
$menus{file} = $menus{bar}->cascade(
-label => 'File',
-menuitems => [
[ "command",
"Show History",
-command => sub { $self->show_history; },
-accelerator => $self->config->{key_history},
],
[ "command",
"Exit",
-command => sub { $self->exit_prog },
-accelerator => $self->config->{key_quit},
]
],
-tearoff => 0,
);
my $host_menu_items = [
[ "command",
"Retile Windows",
-command => sub { $self->retile_hosts },
-accelerator => $self->config->{key_retilehosts},
],
# [ "command", "Capture Terminal", -command => sub { $self->capture_terminal), ],
[ "command",
"Set all active",
-command => sub { $self->set_all_active() },
],
[ "command",
"Set half inactive",
-command => sub { $self->set_half_inactive() },
],
[ "command",
"Toggle active state",
-command => sub { $self->toggle_active_state() },
],
[ "command",
"Close inactive sessions",
-command => sub { $self->close_inactive_sessions() },
],
[ "command",
"Add Host(s) or Cluster(s)",
-command => sub { $self->add_host_by_name, },
-accelerator => $self->config->{key_addhost},
],
[ "command",
"Re-add closed session(s)",
-command => sub { $self->re_add_closed_sessions() },
],
'' # this is needed as build_host_menu always drops the
# last item
];
$menus{hosts} = $menus{bar}->cascade(
-label => 'Hosts',
-tearoff => 1,
-menuitems => $host_menu_items
);
$host_menu_static_items = scalar( @{$host_menu_items} );
$menus{send} = $menus{bar}->cascade(
-label => 'Send',
-tearoff => 1,
);
$self->populate_send_menu();
$menus{help} = $menus{bar}->cascade(
-label => 'Help',
-menuitems => [
[ 'command', "About", -command => sub { $windows{help}->Show } ],
[ 'command', "Documentation",
-command => sub { $windows{manpage}->Show }
],
],
-tearoff => 0,
);
$windows{main_window}->bind( '<KeyPress>' => [ $self => 'key_event' ], );
$windows{main_window}
->bind( '<KeyRelease>' => [ $self => 'key_event' ], );
$self->debug( 2, "create_menubar: completed" );
}
sub populate_send_menu_entries_from_xml {
my ( $self, $menu, $menu_xml ) = @_;
foreach my $menu_ref ( @{ $menu_xml->{menu} } ) {
if ( $menu_ref->{menu} ) {
$menus{ $menu_ref->{title} }
= $menu->cascade( -label => $menu_ref->{title}, );
$self->populate_send_menu_entries_from_xml(
$menus{ $menu_ref->{title} }, $menu_ref, );
if ( $menu_ref->{detach} && $menu_ref->{detach} =~ m/y/i ) {
$menus{ $menu_ref->{title} }->menu->tearOffMenu()->raise;
}
}
else {
my $accelerator = undef;
if ( $menu_ref->{accelerator} ) {
$accelerator = $menu_ref->{accelerator};
}
if ( $menu_ref->{toggle} ) {
$menus{send}->checkbutton(
-label => 'Use Macros',
-variable => \$self->config->{macros_enabled},
-offvalue => 'no',
-onvalue => 'yes',
-accelerator => $accelerator,
);
}
else {
my $command = undef;
if ( $menu_ref->{command} ) {
$command = sub {
$self->send_text_to_all_servers(
$menu_ref->{command}[0] );
};
}
$menu->command(
-label => $menu_ref->{title},
-command => $command,
-accelerator => $accelerator,
);
}
}
}
return $self;
}
sub populate_send_menu {
my ($self) = @_;
# my @menu_items = ();
if ( !-r $self->config->{send_menu_xml_file} ) {
$self->debug( 2, 'Using default send menu' );
$menus{send}->checkbutton(
-label => 'Use Macros',
-variable => \$self->config->{macros_enabled},
-offvalue => 'no',
-onvalue => 'yes',
-accelerator => $self->config->{key_macros_enable},
);
$menus{send}->command(
-label => 'Remote Hostname',
-command => sub {
$self->send_text_to_all_servers(
$self->config->{macro_servername} );
},
-accelerator => $self->config->{key_clientname},
);
$menus{send}->command(
-label => 'Local Hostname',
-command => sub {
$self->send_text_to_all_servers(
$self->config->{macro_hostname} );
},
-accelerator => $self->config->{key_localname},
);
$menus{send}->command(
-label => 'Username',
-command => sub {
$self->send_text_to_all_servers(
$self->config->{macro_username} );
},
-accelerator => $self->config->{key_username},
);
$menus{send}->command(
-label => 'Test Text',
-command => sub {
$self->send_text_to_all_servers( 'echo ClusterSSH Version: '
. $self->config->{macro_version}
. $self->config->{macro_newline} );
},
);
$menus{send}->command(
-label => 'Random Number',
-command => sub {
$self->send_variable_text_to_all_servers(
sub { int( rand(1024) ) } ),
;
},
);
}
else {
$self->debug(
2,
'Using xml send menu definition from ',
$self->config->{send_menu_xml_file}
);
eval { require XML::Simple; };
die 'Cannot load XML::Simple - has it been installed? ', $@ if ($@);
my $xml = XML::Simple->new( ForceArray => 1, );
my $menu_xml = $xml->XMLin( $self->config->{send_menu_xml_file} );
$self->debug( 3, 'xml send menu: ', $/, $xml->XMLout($menu_xml) );
if ( $menu_xml->{detach} && $menu_xml->{detach} =~ m/y/i ) {
$menus{send}->menu->tearOffMenu()->raise;
}
$self->populate_send_menu_entries_from_xml( $menus{send}, $menu_xml );
}
return $self;
}
sub run {
my ($self) = @_;
$self->getopts;
### main ###
# only get xdisplay if we got past usage and help stuff
$xdisplay = X11::Protocol->new();
if ( !$xdisplay ) {
die("Failed to get X connection\n");
}
$self->debug( 2, "VERSION: $VERSION" );
# only use ssh_args from options if config file ssh_args not set AND
# options is not the default value otherwise the default options
# value is used instead of the config file
if ( $self->config->{comms} eq 'ssh' ) {
if ( defined $self->config->{ssh_args} ) {
if ( $self->options->options
&& $self->options->options ne
$self->options->options_default )
{
$self->config->{ssh_args} = $self->options->options;
}
}
else {
$self->config->{ssh_args} = $self->options->options
if ( $self->options->options );
}
}
$self->config->{terminal_args} = $self->options->term_args
if ( $self->options->term_args );
if ( $self->config->{terminal_args} =~ /-class (\w+)/ ) {
$self->config->{terminal_allow_send_events}
= "-xrm '$1.VT100.allowSendEvents:true'";
}
# if the user has asked for natural sorting we need to include an extra
# module
if ( $self->config()->{'use_natural_sort'} ) {
eval { Module::Load::load('Sort::Naturally'); };
if ($@) {
warn(
"natural sorting requested but unable to load Sort::Naturally: $@\n"
);
}
else {
( run in 1.596 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )