You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4128 lines
88 KiB

#!/usr/bin/env ruby
# encoding: UTF-8
# coding: UTF-8
# -*- coding: UTF-8 -*-
#encoding: utf-8
require 'gtk2'
require 'rsvg2'
require 'socket'
require 'monitor'
require 'zlib'
# pour debug
$threads=[]
$currentNetName=""
# Pour un usage des threads avec GTK
# http://ruby-gnome2.sourceforge.jp/hiki.cgi?cmd=view&p=tips_threads
$home="https://opale.iut-clermont.uca.fr/info/wiki/doku.php?id=public:vdn:start"
$lock=false
def setDefaultEditor
if ENV['NANO_EDITOR'] == "1"; then
$editor="nano"
else
#$editor=ENV['EDITOR']
$editor="vi" #if $EDITOR == ""
end
ENV['EDITOR']=$editor
end
setDefaultEditor
#=begin
#p "[** MAIN prcess **] : #{Process.pid}"
Thread.new {
while true
$main.setTitle if $main
sleep(3)
begin
#p "[**WAIT parent:#{Process.pid}**]}"
#Process.wait2(-1, Process::WUNTRACED)
Process.wait()
rescue => e
#p e
next
end
#p "[**RET parent:#{Process.pid}**] #{$?}"
end
}
#=end
=begin
trap("CLD") do
p "TRAP CLD begin"
begin
cpid = Process.wait
rescue StandardError => e
p e
end
p "TRAP CLD end pid:#{cpid}"
end
=end
def mySystem(cmd)
#p "*** #{cmd[-1]}"
if cmd[-1] == "&"
cmd=cmd[0..-2]
end
#p "+++ #{cmd}"
#Gtk.idle_add {
#system(cmd)
#Thread.new {
# spawn(cmd)
#}
#return
Thread.new {
desc="mySystem"
$threads.push(desc)
#p "[parent : #{Process.pid}] : fork : #{cmd}"
pid=spawn(cmd)
#pid=fork {
# p "[child : #{Process.pid} #{Process.ppid}] :exec : #{cmd}"
# exec(cmd)
#}
sleep(0.5)
Process.detach(pid)
$threads.delete_at($threads.index(desc))
}
# false
#}
#fork {system(cmd)}
#pid = fork { system(cmd) }
#Thread.new {
# puts "system:#{cmd}"
# pid = fork { system(cmd) }
# puts "system end"
#}
end
module Gtk
GTK_PENDING_BLOCKS = []
GTK_PENDING_BLOCKS_LOCK = Monitor.new
def Gtk.queue &block
if Thread.current == Thread.main
block.call
else
GTK_PENDING_BLOCKS_LOCK.synchronize do
GTK_PENDING_BLOCKS << block
end
end
end
def Gtk.main_with_queue timeout
if $lock==false
Gtk.timeout_add timeout do
if $lock==false
begin
GTK_PENDING_BLOCKS_LOCK.synchronize do
for block in GTK_PENDING_BLOCKS
block.call
end
GTK_PENDING_BLOCKS.clear
end
rescue => e
p "$!"
puts e.backtrace
end
end
true
end
Gtk.main
end
end
end
# Gestion du graphique
module Svg
def svgInit
@white = Gdk::Color.parse("white")
@black = Gdk::Color.parse("black")
@grey = Gdk::Color.parse("grey")
@blue = Gdk::Color.parse("blue")
@red = Gdk::Color.parse("red")
@green = Gdk::Color.parse("green")
@yellow = Gdk::Color.parse("yellow")
@orange = Gdk::Color.parse("orange")
end
def draw(area)
return false if ! @gw
return false if ! @gh
width=@gw
height=@gh
a=area.allocation.to_a
aw=a[2]; ah=a[3]
width=width+100+2*@bx; height=height+100+2*@by
x=0; y=0
w0=(aw)-2*@bx; h0=(ah)-2*@by
if width>w0 || height>h0
width=w0; height=h0
end
w=width
h=height
awin=area.window
@gc=Gdk::GC.new(awin) if !@gc && awin!=nil
if @gc
gc=@gc
gc.fill = Gdk::GC::Fill::SOLID
gc.rgb_fg_color = @white
if awin!= nil
area.window.draw_rectangle(gc, true, 0, 0 , aw, ah)
end
end
return if w-2*@bx<=0 || h-2*@by<=0
rw=w0*1.0/@gw; rh=h0*1.0/@gh
if rw>rh
ratio=rh
else
ratio=rw
end
ratio=1.2 if ratio > 1.2
begin
if ! @pixbuf
@pixbuf=nil
svg=RSVG::Handle.new_from_file(@graphfile)
surface = Cairo::ImageSurface.new(Cairo::FORMAT_ARGB32, @gw*ratio, @gh*ratio)
context = Cairo::Context.new(surface)
context.scale(ratio, ratio);
context.render_rsvg_handle(svg)
file="#{ENV['TMPDIR']}/vdn-gui-#{ENV['USER']}-#{ENV['VDN_GUI_IDENT']}-graph"
context.target.write_to_png(file)
context.target.finish
@pixbuf=GdkPixbuf::Pixbuf.new(:file => file)
File.delete(file)
end
rescue
return
end
return if ! @pixbuf
pixbuf=@pixbuf
w=pixbuf.width
h=pixbuf.height
width=w; height=h
dx=(w0-w)/2; dy=(h0-h)/2
area.window.draw_pixbuf(nil, pixbuf, 0, 0, x+@bx+dx, y+@by+dy, width, height, Gdk::RGB::DITHER_MAX, 0, 0)
@x=x; @y=y; @w=w; @h=h; @w0=w0; @h0=h0; @dx=dx; @dy=dy; @w=w; @h=h
@width=width; @height=height
@cx=@gw/w.to_f; @cy=@gh/h.to_f
@cx=1.0/@cx; @cy=1.0/@cy
#drawBoxs
drawBoxs
updateSelect
#Gtk.idle_add { sleep 0.01; updateSelect; false; }
#drawSelRec
#updateSelRec
true
end
end
class Preview
include Svg
attr_reader :dx, :dy, :area, :gc
attr_reader :cx, :cy, :bx, :by
attr_reader :selColor
attr_reader :consoles, :combo
attr_accessor :hosts, :selection, :pixbuf
attr_accessor :lines, :graphfile
def initialize(main)
super()
@main=main
@gc=nil
@drawed=false
@gw=@gh=nil
svgInit
@area = Gtk::DrawingArea.new
@area.set_size_request(640,480)
@area.show
#@area.realize
@area.signal_connect("expose-event") do |widget, event|
#p "expose...#{ENV['VDN_GUI_IDENT']} #{event} #{@area}"
load(@dir) if @dir
false
end
@bx=20
@by=20
end
def draw(area)
#p "draw: #{area}"
end
def load(dir)
#p "load:#{dir}"
return if ! dir
@dir=dir if dir
return if ! @dir
if FileTest.exist?(@dir+"/net.svgz") && ENV['RAW_GRAPH']=="0"
@graphfile=@dir+"/net.svgz"
elsif FileTest.exist?(@dir+"/graph.svgz")
@graphfile=@dir+"/graph.svgz"
end
begin
svg=RSVG::Handle.new_from_file(@graphfile)
svg.close
rescue
return
end
@gw=svg.width
@gh=svg.height
#p "w: #{@gw} #{@gh}"
return false if ! @gw
return false if ! @gh
width=@gw
height=@gh
area=@area
a=area.allocation.to_a
aw=a[2]; ah=a[3]
width=width+100+2*@bx; height=height+100+2*@by
x=0; y=0
w0=(aw)-2*@bx; h0=(ah)-2*@by
if width>w0 || height>h0
width=w0; height=h0
end
w=width
h=height
awin=area.window
@gc=Gdk::GC.new(awin) if !@gc && awin!=nil
if @gc
gc=@gc
gc.fill = Gdk::GC::Fill::SOLID
gc.rgb_fg_color = @white
if awin!= nil
area.window.draw_rectangle(gc, true, 0, 0 , aw, ah)
end
end
return if w-2*@bx<=0 || h-2*@by<=0
rw=w0*1.0/@gw; rh=h0*1.0/@gh
if rw>rh
ratio=rh
else
ratio=rw
end
ratio=1.2 if ratio > 1.2
#return
@pixbuf=nil
begin
if ! @pixbuf
@pixbuf=nil
svg=RSVG::Handle.new_from_file(@graphfile)
surface = Cairo::ImageSurface.new(Cairo::FORMAT_ARGB32, @gw*ratio, @gh*ratio)
context = Cairo::Context.new(surface)
context.scale(ratio, ratio);
context.render_rsvg_handle(svg)
file="#{ENV['TMPDIR']}/vdn-gui-#{ENV['USER']}-#{ENV['VDN_GUI_IDENT']}-graph"
context.target.write_to_png(file)
context.target.finish
@pixbuf=GdkPixbuf::Pixbuf.new(:file => file)
File.delete(file)
end
rescue
return
end
return if ! @pixbuf
#p "pixbuf:#{@pixbuf}"
pixbuf=@pixbuf
w=pixbuf.width
h=pixbuf.height
width=w; height=h
dx=(w0-w)/2; dy=(h0-h)/2
area.window.draw_pixbuf(nil, pixbuf, 0, 0, x+@bx+dx, y+@by+dy, width, height, Gdk::RGB::DITHER_MAX, 0, 0)
@x=x; @y=y; @w=w; @h=h; @w0=w0; @h0=h0; @dx=dx; @dy=dy; @w=w; @h=h
@width=width; @height=height
@cx=@gw/w.to_f; @cy=@gh/h.to_f
@cx=1.0/@cx; @cy=1.0/@cy
#drawBoxs
#updateSelect
true
end
end
class Log < Gtk::ScrolledWindow
def initialize(main, fifo)
@main=main
@fifo=fifo
super()
set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
add(@textView=Gtk::TextView.new)
@buffer=@textView.buffer
@errorTag=@buffer.create_tag('error', {'foreground' => 'red'})
@normalTag=@buffer.create_tag('normal', {'foreground' => 'black'})
@buffer.signal_connect("changed") {
@eob_mark = @textView.buffer.create_mark(nil,@textView.buffer.start_iter.forward_to_end,false)
@textView.scroll_mark_onscreen(@eob_mark)
}
startLogListener
end
def startLogListener
Gtk.init_add do
Thread.new {
desc="startLogListener"
$threads.push(desc)
l=[]
while line=@fifo.gets
l=line.dup
#Gtk.queue do
eob_mark = @textView.buffer.create_mark(nil,@textView.buffer.start_iter.forward_to_end,false)
iter = @buffer.get_iter_at_mark(eob_mark)
if l.include? "rror"
tag=@errorTag
else
tag=@normalTag
end
@buffer.insert(iter, Time.now.to_s+ " "+l, tag)
#end
end
$threads.delete_at($threads.index(desc))
}
end
end
end
class BasicTerminal < Gtk::ScrolledWindow
attr_reader :pid
def initialize(command)
super()
set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_NEVER)
add_with_viewport(@vbox=Gtk::VBox.new)
@vbox.pack_start(@hbox=Gtk::HBox.new)
@vte=Vte::Terminal.new
@vte.signal_connect('child-exited') { |w| parent.destroy }
@scrollbar=Gtk::VScrollbar.new(@vte.adjustment)
@hbox.pack_start(@vte)#, false, false)
@hbox.pack_start(@scrollbar) #, false, false) if p
@vte.set_size(80,24)
@vte.set_word_chars(".a-zA-Z_0-9//-")
@vte.set_colors(Gdk::Color.parse("black"), Gdk::Color.parse("white"),[])
@vte.audible_bell=false
@vte.visible_bell=true
@vte.scrollback_lines=1000
@vte.set_font("Monospace 12", Vte::TerminalAntiAlias::USE_DEFAULT)
@pid=@vte.fork_command(:argv => args("bash -c \"#{command}\""))
show_all
end
def args(s)
r=[]
s.gsub(/'([^']*)'|"([^"]*)"|([^[:space:]]+)/) { |m|
r.push("#{$1}#{$2}#{$3}")
}
return r
end
end
class VdnTerminal < Gtk::Window
def initialize(name, cmd, read=true, background=true, center=true)
super()
set_title(name)
cmd="#{cmd}; echo; echo Press Enter to exit !; read" if read
t=BasicTerminal.new(cmd)
add(t)
realize
if(center)
x,y=$main.position
w,h=$main.size
x=x+w/2; y=y+h/2
w,h=size
x=x-w/2; y=y-h/2
move(x, y)
end
show_all
if !background
while `ps --no-headers -p #{t.pid} -o pid` != ""
Gtk.main_iteration while Gtk.events_pending?
sleep(0.05)
end
end
end
end
class Vdn < Gtk::Window
attr_reader :group, :accelGroup
def initialize
super()
$main=self
@group = Gtk::AccelGroup.new
add_accel_group(@group)
@consolesAccelGroup= Gtk::AccelGroup.new
@accelGroupGui=Gtk::AccelGroup.new
@accelGroupConsole=Gtk::AccelGroup.new
@accelGroup = @accelGroupGui
open
#mainDetector
end
def add_main_accel_group
if @consolesAccelGroup
remove_accel_group(@consolesAccelGroup)
end
add_accel_group(@group)
end
def add_consoles_accel_group
if @group
remove_accel_group(@group)
end
add_accel_group(@consolesAccelGroup)
end
def open
dir=ENV['NETWORK_DIR']
#p "Vdn OPEN dir:#{dir}"
@frame=VdnFrame.new
add(@frame)
show_all
#setTitle("")
set_default_size(720,480)
maximize if ENV["MAXIMIZE"] == "1"
signal_connect("delete-event") { r=whenQuit; Gtk::main_quit if r; true }
#p "frame.net:#{@frame.net}"
@net=@frame.net
@notebook=@frame.notebook
end
def setTitle
name=$currentNetName
release=ENV["VDN_RELEASE"]
if name==""
set_title("[#{ENV['USER']}] vdn-gui (#{release}) - [ nThreads:#{$threads.length} ]") #
else
set_title("[#{ENV['USER']}] vdn-gui (#{release}) - #{name} - [ nThreads:#{$threads.length} ]")
end
end
def clean
remove(@frame)
end
def startListener
Gtk.init_add do
Thread.new {
desc="startListener"
$threads.push(desc)
fifo="#{ENV['TMPDIR']}/vdn-gui-#{ENV['USER']}-fifo-ctrl"
if ! File.exist?(fifo)
mySystem("umask 077; mkfifo #{fifo}")
sleep 0.5
end
session = File.open(fifo, "r+")
rawLine=session.gets
while line=rawLine.chomp.split
cmd=line[0]
case cmd
when "quit"
exit(0)
when "start"
name=line[1]
#if mySystem("#{$VDN_PATH}/bin/vdn-alive #{name}")
# $stderr.puts "Abort starting #{name}., is alive !"
#else
expr=line[2..100]
if @net && @net.consoles
#cmd=$VDN_PATH+"/bin/vdn-start-wrapper -et -v \"#{expr}\" #{name}"
#puts "cmd:#{cmd}"
#Gtk.queue do
@net.consoles.addTerm(name)
@notebook.set_page(2)
#end
#sleep 1
else
cmd=$VDN_PATH+"/bin/vdn-start-wrapper -v \"#{expr}\" #{name}"
#$stderr.puts "cmd:#{cmd}"
mySystem(cmd)
end
#end
when "vnc-viewer"
name=line[1]
@net.startVnc(name)
when "spice-viewer"
name=line[1]
@net.startSpice(name)
end
rawLine=session.gets
#p "raw line : #{rawLine}"
end
$threads.delete_at($threads.index(desc))
}
end
end
=begin
def mainDetector
Gtk.timeout_add(500) {
begin
while pid = Process.wait(-1, Process::WNOHANG)
end
rescue
end
}
end
=end
def method_missing(m, *args, &block)
@frame.send(m, *args, &block)
end
end
class VdnFrame < Gtk::Frame
attr_reader :scriptsMenu, :scriptsTestsMenu, :notebook
attr_accessor :bback, :bforward, :bhome, :actionsMenu, :actions, :files, :filesMenu
attr_reader :mod, :net, :preview
attr_reader :main
def initialize()
super()
@main=$main
@preview=Preview.new(self)
@folder=nil
@testsToggle=false
@group = @main.group
@menubar = Gtk::MenuBar.new
@vbox=Gtk::VBox.new
add(@vbox)
networksMenu = Gtk::Menu.new
@scriptsMenu = Gtk::Menu.new
optionsMenu = Gtk::Menu.new
@setupMenu = Gtk::Menu.new
@setupMenu.append(Gtk::TearoffMenuItem.new)
s=Gtk::MenuItem.new("Edit default rc config (local)...");
@setupMenu.append(s)
s.signal_connect('activate') { |w|
startTerminal("#{$editor} config.rc.local",
"[ ! -e #{ENV['VDN_PATH']}/config.rc.local ] && \
cp #{ENV['VDN_PATH']}/config.rc #{ENV['VDN_PATH']}/config.rc.local; \
#{$editor} #{ENV['VDN_PATH']}/config.rc.local", false)
}
@setupMenu.append(Gtk::MenuItem.new())
s=Gtk::MenuItem.new("Edit default guest config (local) ...");
@setupMenu.append(s)
s.signal_connect('activate') { |w|
startTerminal("#{$editor} config.template.local",
"[ ! -e #{ENV['VDN_PATH']}/config.template.local ] && \
cp #{ENV['VDN_PATH']}/config.template #{ENV['VDN_PATH']}/config.template.local; \
#{$editor} #{ENV['VDN_PATH']}/config.template", false)
}
@setupMenu.append(Gtk::MenuItem.new)
s=Gtk::MenuItem.new("New network...");
@setupMenu.append(s)
s.signal_connect('activate') { |w|
startTerminal("vdn-new-network",
"#{$VDN_PATH}/bin/vdn-new-network", true, false)
}
@setupMenu.append(Gtk::MenuItem.new)
s=Gtk::MenuItem.new("Download network...");
@setupMenu.append(s)
s.signal_connect('activate') { |w|
startTerminal("vdn-download-network",
"#{$VDN_PATH}/bin/vdn-download-network", true, false)
}
@setupMenu.append(Gtk::MenuItem.new)
@setupMenu.append(s=Gtk::MenuItem.new("Terminal (in vdn directory...)"))
s.signal_connect('activate') {
startTerminal("", "cd #{$VDN_PATH}/files; bash", false)
}
#@setupMenu.show_all
@actionsMenu = Gtk::Menu.new
@filesMenu = Gtk::Menu.new
@filesMenu.append(Gtk::TearoffMenuItem.new)
@filesMenu.append(s=Gtk::MenuItem.new("Terminal (in files directory...)"))
s.signal_connect('activate') {
startTerminal("", "vdn-manage-files; cd #{$VDN_PATH}/files; bash", false)
}
@filesMenu.show_all
@scriptsTestsMenu = Gtk::Menu.new
networks = Gtk::MenuItem.new("Network")
options = Gtk::MenuItem.new("Preferences")
@scripts = Gtk::MenuItem.new("Scripts")
@setup = Gtk::MenuItem.new("Main setup")
@actions = Gtk::MenuItem.new("Network setup")
@scriptsTests = Gtk::MenuItem.new("All scripts")
@files = Gtk::MenuItem.new("Files (disks, ...)")
#p"vdn: @actions:#{@actions}"
#p"vdn: @actionsMenu:#{@actionsMenu}"
networks.submenu = networksMenu
options.submenu = optionsMenu
@scripts.submenu = @scriptsMenu
@actions.submenu = @actionsMenu
@setup.submenu = @setupMenu
@files.submenu = @filesMenu
@scriptsTests.submenu = @scriptsTestsMenu
@actions.signal_connect('activate') { |w,e|
@net.updateMoreMenu(@actionsMenu) if @net
@actionsMenu.show_all
false
}
@setup.signal_connect('activate') { |w,e|
@setupMenu.show_all
false
}
#@files.signal_connect('activate') { |w,e|
# @net.updateFilesMenu if @net
# false
#}
#@scriptsTests.submenu = @scriptsTestsMenu
networksMenu.append(Gtk::TearoffMenuItem.new)
# Create the Networks menu content.
@bopen = Gtk::ImageMenuItem.new(Gtk::Stock::OPEN, @group)
networksMenu.append(@bopen)
@bopen.signal_connect("activate") { |w| whenOpen }
@bclose = Gtk::ImageMenuItem.new(Gtk::Stock::CLOSE, @group)
networksMenu.append(@bclose)
@bclose.signal_connect("activate") { |w|
whenClose;
}
@bclose.sensitive=false
networksMenu.append(Gtk::MenuItem.new)
Gtk::Stock.add(Gtk::Stock::SELECT_ALL, "Select all", Gdk::Window::CONTROL_MASK, Gdk::Keyval::GDK_A)
@bselect = Gtk::ImageMenuItem.new(Gtk::Stock::SELECT_ALL, @group)
networksMenu.append(@bselect)
@bselect.signal_connect("activate") { |w| whenSelectAll }
@bselect.sensitive=false
Gtk::Stock.add(Gtk::Stock::YES, "Start selected", Gdk::Window::CONTROL_MASK, Gdk::Keyval::GDK_S)
@bstart = Gtk::ImageMenuItem.new(Gtk::Stock::YES, @group)
networksMenu.append(@bstart)
@bstart.signal_connect("activate") { |w| whenStartSelected }
@bstart.sensitive=false
Gtk::Stock.add(Gtk::Stock::NO, "Halt all", Gdk::Window::CONTROL_MASK, Gdk::Keyval::GDK_H)
@bhalt = Gtk::ImageMenuItem.new(Gtk::Stock::NO, @group)
networksMenu.append(@bhalt)
@bhalt.signal_connect("activate") { |w| whenHaltAll }
@bhalt.sensitive=false
Gtk::Stock.add(Gtk::Stock::REFRESH, "Restart selected", Gdk::Window::CONTROL_MASK, Gdk::Keyval::GDK_R)
@brestart = Gtk::ImageMenuItem.new(Gtk::Stock::REFRESH, @group)
networksMenu.append(@brestart)
@brestart.signal_connect("activate") { |w| whenRestartAll }
@brestart.sensitive=false
Gtk::Stock.add(Gtk::Stock::CLEAR, "Clean selected", Gdk::Window::CONTROL_MASK, Gdk::Keyval::GDK_C)
@bclean = Gtk::ImageMenuItem.new(Gtk::Stock::CLEAR, @group)
networksMenu.append(@bclean)
@bclean.signal_connect("activate") { |w| whenCleanAll }
@bclean.sensitive=false
Gtk::Stock.add(Gtk::Stock::DISCONNECT, "Kill all", Gdk::Window::CONTROL_MASK, Gdk::Keyval::GDK_K)
@bkill = Gtk::ImageMenuItem.new(Gtk::Stock::DISCONNECT, @group)
networksMenu.append(@bkill)
@bkill.signal_connect("activate") { |w| whenKillAll }
@bkill.sensitive=false
# Accel -> Tests mode
#@group.connect(Gdk::Keyval::GDK_M, Gdk::Window::CONTROL_MASK,
# Gtk::ACCEL_VISIBLE) {
# toggleTests
#}
networksMenu.append(Gtk::MenuItem.new)
Gtk::Stock.add(Gtk::Stock::COPY, "View all backups", Gdk::Window::CONTROL_MASK, Gdk::Keyval::GDK_B)
@bfiles = Gtk::ImageMenuItem.new(Gtk::Stock::COPY, @group)
networksMenu.append(@bfiles)
@bfiles.signal_connect("activate") { |w| whenBackups }
networksMenu.append(Gtk::MenuItem.new)
s=Gtk::MenuItem.new("Edit user rc file...");
networksMenu.append(s)
s.signal_connect('activate') { |w|
startTerminal("#{$editor} rc file",
"#{$editor} #{ENV['HOME']}/.vdnrc; \
echo \\\"Pour que les modifications soient effectives vous devez redémarrer VDN\\\" ", true)
}
networksMenu.append(Gtk::MenuItem.new)
quit = Gtk::ImageMenuItem.new(Gtk::Stock::QUIT, @group)
quit.signal_connect('activate') {
r=whenQuit
p "whenQuit r:#{r}"
Gtk::main_quit if r
}
networksMenu.append(quit)
@menubar.append(networks)
# Create the Options menu content.
item=Gtk::CheckMenuItem.new("Debug")
#puts("VDN_DEBUG=#{ENV["VDN_DEBUG"]}")
item.active=false
item.active=true if ENV["VDN_DEBUG"] == "1"
optionsMenu.append(item)
item.signal_connect("activate") { |w| whenDebug(w) }
#item=Gtk::CheckMenuItem.new("External ssh")
#item.active=true if ENV["VDN_EXTERNAL_SSH"] == "1"
#optionsMenu.append(item)
#item.signal_connect("activate") { |w| whenExternalSsh(w) }
item=Gtk::CheckMenuItem.new("Parallel")
item.active=true if ENV["RUN_PARALLEL"] == "1"
optionsMenu.append(item)
item.signal_connect("activate") { |w| whenParallel(w) }
item=Gtk::CheckMenuItem.new("Maximize at startup")
item.active=true if ENV["MAXIMIZE"] == "1"
optionsMenu.append(item)
item.signal_connect("activate") { |w| whenPreferenceGeneric(w, "MAXIMIZE") }
item=Gtk::CheckMenuItem.new("Use nano text editor")
item.active=true if ENV["NANO_EDITOR"] == "1"
optionsMenu.append(item)
item.signal_connect("activate") { |w| whenPreferenceNano(w, "NANO_EDITOR") }
item=Gtk::CheckMenuItem.new("Raw graph")
item.active=true if ENV["RAW_GRAPH"] == "1"
optionsMenu.append(item)
item.signal_connect("activate") { |w| whenRawGraph(w) }
item=Gtk::CheckMenuItem.new("Test mode")
optionsMenu.append(item)
item.signal_connect("activate") { |w| whenTestMode(w) }
@menubar.append(options)
@menubar.append(@scripts)
if ENV["TEST_MODE"] == "1"
item.active=true
@testsToggle=false
end
@vbox.pack_start(@menubar, false, false)
@vbox.pack_start(@notebook=Gtk::Notebook.new, true, true)
@notebook.scrollable=true
@notebook.show_border=false
@notebook.focus_chain=[]
# moz
@mozbox=Gtk::VBox.new
@mozbox.pack_start(@mozbar=Gtk::HBox.new, false, false)
@html="#{$home}"
@banner="#{$VDN_PATH}/doc/banner.svgz"
@moz=Gtk::Browser.new
@moz.banner = @banner
@moz.location = @html
@mozbox.add(@moz)
@notebook.append_page(@mozbox, Gtk::Label.new("Documentation"))
#@log=Log.new(@main, $LOG_FIFO)
#@notebook.append_page(@log, Gtk::Label.new("Log"))
show_all
if ENV["NETWORK_DIR"] != ""
@dir=ENV["NETWORK_DIR"]
if @dir == ""
@dir=$dir
ENV["NETWORK_DIR"]=$dir
end
openNet
else
closeNet
end
signal_connect('event') { |w,e|
if e.class.to_s == "Gdk::EventKey"
n=Gdk::Keyval.to_name(e.keyval)
if e.event_type == Gdk::Event::Type::KEY_PRESS
if n =~ /Control/
@mod="Control"
end
if n =~ /Shift/
@mod="Shift"
end
else
if n =~ /Control/ || n =~ /Shift/
@mod=nil
end
end
end
}
end
def startTerminal(name, cmd, read=true, background=true, big=false)
#VdnTerminal.new(name, cmd, read, background, center);
#return
#=begin
opts=""
# bg only need for xfce4-terminal
bg="--disable-server"
bg="" if background
longCmd="NETWORK_DIR=#{@dir} #{cmd}"
# xfce4-terminal
#geom=""
#geom="--geometry 140x43 --font 10" if big
# sajura terminal
#geom=""
#geom="-c 140 -r 43 --font 10" if big
# xterm
# xterm -fa 'Monospace' -fs 12 -geometry 80x24 -j -rightbar
#p "big:#{big}"
opts="-j -rightbar -sb -vb"
geom="-fa 'Monospace' -fs 12"
geom="-fa 'Monospace' -fs 10 -geometry 140x43" if big
#p "cmd:#{cmd} read:#{read} bg:#{bg}"
#c="cd; export NO_INTERACTIVE=1; xfce4-terminal #{geom} --disable-server -T \"#{name}\" #{bg} -e \"bash -c \'#{longCmd}; echo; echo Press Enter to exit !; read\'\""
#c="cd; export NO_INTERACTIVE=1; sakura #{geom} -t \"#{name}\" -e \"bash -c \'#{longCmd}; echo; echo Press Enter to exit !; read\'\" 2> /dev/null"
#c="cd; export NO_INTERACTIVE=1; xterm #{geom} -T \"#{name}\" -e \"bash -c \'#{longCmd}; echo; echo Press Enter to exit !; read\'\" 2> /dev/null"
prefix="cd; export NO_INTERACTIVE=1; "
extra=""
extra="; echo; echo Press Enter to exit !; read" if read
#c="cd; export NO_INTERACTIVE=1; xfce4-terminal #{geom} --disable-server -T \"#{name}\" #{bg } -e \"bash -c \'#{longCmd} #{extra}\'\""
#c="cd; export NO_INTERACTIVE=1; sakura #{geom} -t \"#{name}\" -e \"bash -c \'#{longCmd}\'\" 2> /dev/null"
#c="xterm ${opts} #{geom} -T \"#{name}\" -e \"bash -c \'#{prefix} #{longCmd} #{extra}\'\" &"
c="bash -c \'#{prefix} #{longCmd} #{extra}\'"
#puts "c:#{c} networkDir:#{ENV['NETWORK_DIR']}"
#c="#{c} &" # if background
#mySystem(c)
#puts "=== vdn-terminal \"#{c}\""
if big == true
mySystem("vdn-terminal -b \"#{c}\" &")
else
mySystem("vdn-terminal \"#{c}\" &")
end
#=end
end
def getPass
m=" Mot de passe VDN (mode edit) : "
dialog = Gtk::Dialog.new("Message", self,
Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT,
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_REJECT],
[Gtk::Stock::OK, Gtk::Dialog::RESPONSE_ACCEPT])
dialog.vbox.add(Gtk::Label.new(m))
dialog.vbox.add(e=Gtk::Entry.new)
e.visibility=false
e.signal_connect("activate") {
dialog.children[0].children.last.children.first.clicked
true
}
#e.max_length=10
dialog.show_all
r=dialog.run
if r==Gtk::Dialog::RESPONSE_ACCEPT
require 'bcrypt'
# #hash a user's password
# require'bcrypt'
# BCrypt::Password.create("secret")
hash="$2a$10$bxqjywHoKH5LI6MP6oX2SeJJX./rdZknVByguTj936bqgqZ.Lv/QW"
r2=(BCrypt::Password.new(hash)==e.text)
end
dialog.destroy
return (r==Gtk::Dialog::RESPONSE_ACCEPT) && r2
end
def toggleTests
if @testsToggle
@testsToggle=false
@menubar.remove(@setup)
@menubar.remove(@actions)
@menubar.remove(@scriptsTests)
@menubar.remove(@files)
else
#if (FileTest.writable?(ENV["NETWORK_DIR"]+"/scripts") || getPass )
#if (getPass )
@menubar.append(@setup)
@menubar.append(@actions)
@menubar.append(@scriptsTests)
@menubar.append(@files)
@scriptsTests.show
@setup.show
@actions.show
@files.show
@testsToggle=true
#end
end
end
def whenHaltAll
if @net != nil
@net.whenHaltAll
@notebook.set_page(1)
end
return true
end
def whenRestartAll
if @net != nil
@net.whenRestart
@notebook.set_page(1)
end
return true
end
def whenCleanAll
if @net != nil
@net.whenClean
end
return true
end
def whenKillAll
r=`#{$VDN_PATH}/bin/vdn-alives`
if r != ""
m="
Un ou plusieurs systèmes restent en fonctionnement !
Si vous validez ils seront \"brutalement\" arrêtés (pas de sauvegarde !)
"
dialog = Gtk::Dialog.new("Message", @main,
Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT,
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_REJECT],
[Gtk::Stock::OK, Gtk::Dialog::RESPONSE_ACCEPT])
dialog.vbox.add(Gtk::Label.new(m))
dialog.show_all
r=dialog.run
dialog.destroy
#p "r:#{r}"
#p "Gtk::Dialog::RESPONSE_ACCEPT:#{Gtk::Dialog::RESPONSE_ACCEPT}"
return false if r != Gtk::Dialog::RESPONSE_ACCEPT
end
if @net != nil
@net.whenKillAll
@notebook.set_page(1)
end
return true
end
def whenClose
#p "whenClose"
return false if ! whenKillAll
if @net
@net.close
@net=nil
closeNet
@dir=""
$currentNetName=""
end
@bclose.sensitive=false
@bselect.sensitive=false
@bstart.sensitive=false
@bhalt.sensitive=false
@brestart.sensitive=false
@bclean.sensitive=false
@bkill.sensitive=false
@bopen.sensitive=true
@scripts.sensitive=false
@scriptsTests.sensitive=false
@setup.sensitive=true
@actions.sensitive=false
#@files.sensitive=false
return true
end
def whenOpen
if @folder == nil
if ENV["NETWORK_DIR"] != ""
@folder=File.dirname(ENV["NETWORK_DIR"])
else
@folder=ENV["VDN_NETWORKS_BASE_DIR"]
end
end
dialog = Gtk::FileChooserDialog.new("Open network", $main,
Gtk::FileChooser::ACTION_OPEN,
nil,
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
[Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])
dialog.preview_widget=@main.preview.area
preview_widget_active=true
dialog.signal_connect('update-preview') { |w|
#p "update-preview #{w.preview_filename}"
@main.preview.load(w.preview_filename)
}
#p "dialog.filename:#{dialog.filename}"
#@main.preview.load(dialog.filename)
if @folder
dialog.current_folder=@folder
dialog.signal_connect('current-folder-changed') { |w|
Gtk.timeout_add(200) {
begin
if File.exist?(w.current_folder+"/network.vdn")
w.filename=w.current_folder+"/network.vdn"
w.response(Gtk::Dialog::RESPONSE_ACCEPT)
end
rescue
end
false
}
false
}
f = Gtk::FileFilter.new
f.name="Vdn network"
f.add_pattern("*.vdn")
dialog.set_filter(f)
n=nil
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
f=dialog.filename
n=File.dirname(f)
end
dialog.preview_widget=nil
dialog.destroy
else
@folder=ENV["VDN_NETWORKS_BASE_DIR"]
end
#p "n:#{n}"
if n
if @net != nil
r=whenClose
return false if !r
end
@dir=n
#mySystem($VDN_PATH+"/bin/vdn-busy.rb &")
=begin
message="\n\n\n Open #{@dir} network.\n\nPlease wait... \n\n\n"
dialog = Gtk::MessageDialog.new(
self,
Gtk::Dialog::MODAL | Gtk::Dialog::DESTROY_WITH_PARENT,
Gtk::MessageDialog::INFO,
Gtk::MessageDialog::BUTTONS_CLOSE,
message)
# Ensure that the dialog box is destroyed when the user responds.
dialog.signal_connect('response') { dialog.destroy }
# Add the message in a label, and show everything we've added to the dialog.
#dialog.vbox.add(Gtk::Label.new(message))
dialog.show_all
#Gtk.idle_add {
=end
mySystem($VDN_PATH+"/bin/vdn-set-var NETWORK_DIR \"#{@dir}\"")
ENV['NETWORK_DIR']=@dir
#@net.close if @net
#$main.destroy
$main.whenQuit
#$main.destroy
#mySystem($VDN_PATH+"/bin/vdn #{@dir} &")
#sleep(2)
#whenQuit
startNet
=begin
#}
=end
end
end
def whenSelectAll
@net.hosts.each_value { |i|
@net.selection.add(i) if ! i.isSel
}
end
def whenStartSelected
@net.whenStart
end
def whenRestartSelected
@net.whenRestart
end
#def whenTermOld
# #mySystem("cd #{@dir}; cd scripts; xfce4-terminal --disable-server &")
# mySystem("cd #{@dir}; cd scripts; sakura &")
#end
def whenBackups
startTerminal("", "vdn-manage-backups; bash", false)
#mySystem("xfce4-terminal --disable-server &")
end
def whenFiles
startTerminal("", "vdn-manage-files; cd $VDN_PATH/files; bash", false)
#mySystem("xfce4-terminal --disable-server &")
end
def whenQuit
mySystem($VDN_PATH+"/bin/vdn-set-var NETWORK_DIR \"#{@dir}\"")
if whenClose
begin
#Process.kill("SIGTERM", Process.ppid)
rescue
end
begin
File.delete("#{ENV['TMPDIR']}/vdn-#{ENV['USER']}-gui-log")
#File.delete("#{ENV['TMPDIR']}/vdn-gui-#{ENV['USER']}-fifo-ctrl")
rescue
end
#puts "Bye !"
#Gtk::main_quit
return true
end
return false
end
def openNet
#p "openNet:"
$dir=@dir if @dir && @dir != ""
@net.close if @net
@net=nil
@dir=$dir if @dir == ""
mySystem("#{$VDN_PATH}/bin/vdn-set-network-dir #{@dir}")
ENV["NETWORK_DIR"]=@dir
@net=Net.new(self, @dir)
@net.open
$currentNetName=File.basename(@dir)
$currentNetName="" if $currentNetName == nil
$main.setTitle
# Create the Scripts menu content.
@scripts.signal_connect('activate') { |w,e|
@net.updateScriptsMenu
false
}
@scriptsTests.signal_connect('activate') { |w,e|
@net.updateScriptsTestsMenu
false
}
@setup.signal_connect('activate') { |w,e|
false
}
@actions.signal_connect('activate') { |w,e|
@net.updateActionsMenu if @net
false
}
#@files.signal_connect('activate') { |w,e|
# p "files.activate"
# @net.updateFilesMenu if @net
# false
#}
show_all
@bclose.sensitive=true
#@bterm.sensitive=true
@bselect.sensitive=true
@bstart.sensitive=true
@bhalt.sensitive=true
@brestart.sensitive=true
@bclean.sensitive=true
@bkill.sensitive=true
#@bopen.sensitive=false
@scripts.sensitive=true
@scriptsTests.sensitive=true
@setup.sensitive=true
@actions.sensitive=true
@files.sensitive=true
@moz.banner=@banner
@moz.location=@html
@notebook.set_page(1)
end
def closeNet
@lastDateGraphFile=nil
if @net
@net.close
@net=nil
end
@dir=nil
ENV['NETWORK_DIR']=""
#mySystem($VDN_PATH+"/bin/vdn-set-var NETWORK_DIR \"\"")
@html="#{$home}"
@moz.banner=@banner
@moz.location=@html
#release=ENV["VDN_RELEASE"]
$main.setTitle
@bclose.sensitive=false
#@bterm.sensitive=false
@bselect.sensitive=false
@bstart.sensitive=false
@bhalt.sensitive=false
@brestart.sensitive=false
@bclean.sensitive=false
@bkill.sensitive=false
@bopen.sensitive=true
@scripts.sensitive=false
@actions.sensitive=false
#@setup.sensitive=false
@scriptsTests.sensitive=false
end
def whenDebug(w)
s="0"
s="1" if w.active?
ENV["VDN_DEBUG"]=s
mySystem($VDN_PATH+"/bin/vdn-set-var VDN_DEBUG #{s}")
end
def whenExternalSsh(w)
#p "whenExternalSsh: #{w} #{w.active?}"
s="0"
s="1" if w.active?
ENV["VDN_EXTERNAL_SSH"]=s
mySystem($VDN_PATH+"/bin/vdn-set-var VDN_EXTERNAL_SSH #{s}")
end
def whenPreferenceGeneric(w, varName)
s="0"
s="1" if w.active?
ENV[varName]=s
mySystem($VDN_PATH+"/bin/vdn-set-var #{varName} #{s}")
end
def whenPreferenceNano(w, varName)
s="0"
s="1" if w.active?
ENV[varName]=s
mySystem($VDN_PATH+"/bin/vdn-set-var #{varName} #{s}")
setDefaultEditor
end
def whenParallel(w)
s="0"
s="1" if w.active?
ENV["RUN_PARALLEL"]=s
mySystem($VDN_PATH+"/bin/vdn-set-var RUN_PARALLEL #{s}")
end
def whenRawGraph(w)
s="0"
s="1" if w.active?
ENV["RAW_GRAPH"]=s
mySystem($VDN_PATH+"/bin/vdn-set-var RAW_GRAPH #{s}")
return if ! @net
@net.lines=""
@net.graphfile=nil
@net.parseGraph
end
def whenTestMode(w)
s="0"
s="1" if w.active?
ENV["TEST_MODE"]=s
mySystem($VDN_PATH+"/bin/vdn-set-var TEST_MODE #{s}")
if ! w.active?
@testsToggle=false
@menubar.remove(@setup)
@menubar.remove(@actions)
@menubar.remove(@scriptsTests)
@menubar.remove(@files)
else
#if (FileTest.writable?(ENV["NETWORK_DIR"]+"/scripts") || getPass )
#if (getPass )
@menubar.append(@setup)
@menubar.append(@actions)
@menubar.append(@scriptsTests)
@menubar.append(@files)
@scriptsTests.show
@setup.show
@actions.show
@files.show
@testsToggle=true
#end
end
end
def whenTests(w)
s="0"
s="1" if w.active?
end
end
class Net < Gtk::VBox
include Svg
attr_reader :dx, :dy, :area, :gc
attr_reader :cx, :cy, :bx, :by
attr_reader :selColor
attr_reader :consoles, :combo
attr_accessor :hosts, :selection, :pixbuf
attr_accessor :lines, :graphfile, :lines
def initialize(main, dir)
super()
@main=main
@notebook=@main.notebook
@dir=dir
@graphTimeout=nil
@netTimeout=nil
@lines=nil
@graphfile=nil
@gc=nil
@drawed=false
@gw=@gh=nil
@consoles=nil
@ssh=nil
svgInit
@selColor=Gdk::Color.parse("yellow")
@bx=20
@by=20
@selColor=Gdk::Color.parse("yellow")
@white = Gdk::Color.parse("white")
@grey = Gdk::Color.parse("grey")
@blue = Gdk::Color.parse("blue")
@red = Gdk::Color.parse("red")
@green = Gdk::Color.parse("green")
@dx=0
@dy=0
@selection=Selection.new(self)
@menu=Gtk::Menu.new
createMenu(@menu)
@hosts=Hash.new
@states=Hash.new
@mx=@my=nil
@selRec=nil
@area = Gtk::DrawingArea.new
@eventbox=Gtk::EventBox.new
@eventbox.events=Gdk::Event::ALL_EVENTS_MASK
@eventbox.add(@area)
add(@eventbox);
@eventbox.signal_connect('event') { |w,e|
parseEvent(w,e)
false
}
@notebook.insert_page(1,self, Gtk::Label.new("Graph"))
@area.signal_connect("expose-event") do |widget, event|
#p "expose...#{ENV['VDN_GUI_IDENT']} #{event} #{@area}"
draw(@area)
# MARCHE PAS !@main.main.add_main_accel_group
# Il y a des fois ou dans l'onglet graphe les raccourcis claviers
# Ne fonctionne pas ! Ex Ctrl A pour sélectionner toutes les machines.
false
end
@area.signal_connect("configure-event") do |widget, event|
#p "resize...#{ENV['VDN_GUI_IDENT']} #{event} #{@area}"
@pixbuf=nil
false
end
@area.signal_connect("event") do |widget, event|
#p "event...#{ENV['VDN_GUI_IDENT']} #{event} #{@area}"
false
end
end
def parseGraph # net
if FileTest.exist?(@dir+"/net.svgz") && ENV['RAW_GRAPH']=="0"
@graphfile=@dir+"/net.svgz"
elsif FileTest.exist?(@dir+"/graph.svgz")
@graphfile=@dir+"/graph.svgz"
end
begin
f=File::open(@graphfile)
z=Zlib::GzipReader.open(f)
lines=z.readlines[1..20].join
z.close
f.close
rescue
return
end
if lines != @lines
@hosts={}
@selection.clear
@lines=lines
if lines.include?("nkscape") then
parseInkscape
elsif lines.include?("raphviz") then
parseGraphviz
else
$stderr.puts("Error : Svg parser not found !")
end
begin
draw(@area)
rescue
end
whenNewGraph
end
end
def netDetector
#p "add netDetector"
@netTimeout=Gtk.timeout_add(2000) {
#p "netDetector... #{Time.now}"
detect
#p "netDetector end #{Time.now}"
true
}
#Gtk.timeout_add(100) {
# begin
# @net.detect if @net
# rescue
# end
# false
#}
@graphTimeout=Gtk.timeout_add(200) {
if @dir && @dir != ""
if ! FileTest.exist?(@graphfile)
@graphfile=nil
end
if ! @graphfile
if FileTest.exist?(@dir+"/net.svgz") && ENV['RAW_GRAPH']=="0"
@graphfile=@dir+"/net.svgz"
elsif FileTest.exist?(@dir+"/graph.svgz")
@graphfile=@dir+"/graph.svgz"
end
end
if (@graphfile)
begin
@dateGraphFile=File.mtime(@graphfile)
rescue
end
#p @dateGraphFile
#p " "
if( ! @lastDateGraphFile || @dateGraphFile!=@lastDateGraphFile)
dir=@dir.dup
@lines=""
@pixbuf=nil
parseGraph
area.queue_draw
@lastDateGraphFile=@dateGraphFile
end
end
end
true
}
end
def whenNewGraph
#p "whenNewGraph..."
l=`#{$VDN_PATH}/bin/vdn-list | grep -v '^#'`
l=l.split("\n")
l=l.sort { |a,b|
if a.size > b.size
1
else
a <=> b
end
}
# consoles
#@consolesNotebook.set_page(0)
#while @consolesNotebook.n_pages > 0
# @consolesNotebook.remove_page(0)
#end
while @consolesNotebook.n_items > 0
@consolesNotebook.remove(@consolesNotebook.nth_item(0))
end
l.each { |i|
#@consolesNotebook.append_page(Gtk::Frame.new, Gtk::Button.new(i))
#@consolesNotebook.show_all
b=Gtk::ToolButton.new(nil, i)
#b.can_focus=false
@consolesNotebook.add(b)
b.signal_connect("clicked") {
#p "initBar : add console"
#@consoles.addTerm(i)
mySystem("vdn-viewer #{i} &")
}
}
@consolesNotebook.show_all
#@consoles.initBar # initBar consoles
# ssh
#while @sshNotebook.n_pages > 0
# @sshNotebook.remove_page(0)
#end
while @sshNotebook.n_items > 0
@sshNotebook.remove(@sshNotebook.nth_item(0))
end
l.each { |i|
#b=Gtk::Button.new(i)
b=Gtk::ToolButton.new(nil, i)
#b.can_focus=false
#b.relief=Gtk::RELIEF_NONE
#@sshNotebook.append_page(Gtk::Frame.new, b)
@sshNotebook.add(b)
b.signal_connect("clicked") {
#p "initBar : add ssh"
if ENV['VDN_EXTERNAL_SSH'] != "1"
#cmd=$VDN_PATH+"/bin/vdn-ssh-loop -X #{user}@#{i.name}"
#@ssh.addTerm(i.name, cmd)
@ssh.addTerm(i)
@notebook.set_page(3)
else
user=@main.net.combo.active_text
startTerminal("vdn-ssh-loop -X #{user}@#{i}",
"#{$VDN_PATH}/bin/vdn-ssh-loop -X #{user}@#{i}")
end
}
}
@sshNotebook.show_all
#@ssh.initBar # ssh initBar
#@notebook.set_page(3)
end
def open # class Net
return if ! @dir || @dir == ""
r=Dir.glob("#{@dir}/*.conf")
if Dir.glob("#{@dir}/*.conf").size == 0
Gtk.main_iteration while Gtk.events_pending?
#p "@dir:#{@dir}"
#p "ENV['NETWORK_DIR']:#{ENV['NETWORK_DIR']}"
startTerminal("vdn-build-network",
"#{$VDN_PATH}/bin/vdn-build-network", true, false)
end
if FileTest.exist?(@dir+"/net.svgz") && ENV['RAW_GRAPH']=="0"
@graphfile=@dir+"/net.svgz"
elsif FileTest.exist?(@dir+"/graph.svgz")
@graphfile=@dir+"/graph.svgz"
else
$stderr.puts("No SVGZ file found !")
return
end
Gtk.idle_add {
parseGraph
if (@graphfile)
@lastDateGraphFile=File.mtime(@graphfile)
end
#GC.start
false
}
#@consolesNotebook=Gtk::Notebook.new
#@consolesNotebook.scrollable=true
# Create console
@consoles=ConsolesPanel.new(@main, "vdn-start-wrapper -et ", "", \
"\nN'utilisez les consoles qu'en cas de perte de réseau ! \
Utilisez le panneau \"Ssh\".\n")
#@consolesNotebook.append_page(@consoles,
# Gtk::Label.new("ttyS0"))
@consolesNotebook=Gtk::Toolbar.new
@consolesNotebook.toolbar_style=Gtk::Toolbar::Style::TEXT
@consolesNotebook.can_focus=false
@consolesContainer=Gtk::VBox.new
@consolesContainerHBox=Gtk::HBox.new
@consolesContainerHBox.pack_start(Gtk::Label.new("Click to open display -> : "), false, false)
@consolesContainerHBox.pack_start(@consolesNotebook)
@consolesContainer.pack_start(@consolesContainerHBox, false, false)
@consolesContainer.pack_start(@consoles)
@notebook.insert_page(2,@consolesContainer,
Gtk::Label.new("Consoles"))
@consoles.setProps("fgColor", "white")
@consoles.setProps("bgColor", "black")
@consoles.setProps("canClose", "false")
@consoles.setProps("audibleBell", "false")
@consoles.setProps("gridFont", ENV["GRID_FONT"])
# Create ssh
@ssh=SshPanel.new(@main, "vdn-ssh-loop -X ")
#@sshNotebook=Gtk::Notebook.new
@sshNotebook=Gtk::Toolbar.new
#@sshNotebook.scrollable=true
@sshNotebook.toolbar_style=Gtk::Toolbar::Style::TEXT
@sshNotebook.can_focus=false
@sshContainer=Gtk::VBox.new
@sshContainerHBox=Gtk::HBox.new(false, 5)
@sshContainerHBox.pack_start(Gtk::Label.new("Ssh user : "), false, false)
combo = Gtk::ComboBoxEntry.new
combo.can_focus=false
combo.has_focus=false
combo.focus_on_click=false
%w( root test alice bob eve ).each do |val|
combo.append_text(val)
end
combo.set_size_request(120, -1)
combo.active = 0
@combo=combo
@sshContainerHBox.pack_start(@combo, false, false)
@sshContainerHBox.pack_start(Gtk::Label.new(" Click to open ssh -> : "), false, false)
@sshContainerHBox.pack_start(@sshNotebook)
@sshContainer.pack_start(@sshContainerHBox, false, false)
@sshContainer.pack_start(@ssh)
item=Gtk::CheckButton.new("New window")
item.active=true if ENV["VDN_EXTERNAL_SSH"] == "1"
item.signal_connect("clicked") { |w| @main.whenExternalSsh(w) }
@sshContainerHBox.pack_start(item, false, false)
@sshContainerHBox.pack_start(b=Gtk::Button.new("Remove all"), false, false)
b.signal_connect('clicked') {
puts "clear ssh..."
@ssh.close
}
@ssh.setProps("audibleBell", "false")
@ssh.setProps("gridFont", ENV["GRID_FONT"])
createMoreMenu(@main.actionsMenu)
@notebook.insert_page(3, @sshContainer, Gtk::Label.new("Ssh"))
whenNewGraph
netDetector
end
def detect
return if ! @graphfile
begin
f=File::open(@graphfile)
rescue
return
end
z=nil
begin
z=Zlib::GzipReader.open(f) if f != nil
rescue
z=nil
end
begin
lines=z.readlines[1..10].join if z != nil
rescue
return
end
begin
z.close if z != nil
f.close if f != nil
rescue
end
#if lines != @lines
# @main.whenClose
# @main.openNet
#end
#l=IO.popen("/bin/bash -c \""+$VDN_PATH+"/bin/vdn-alives"+"\"").read
io=IO.popen($VDN_PATH+"/bin/vdn-alives")
l=io.read
Thread.new {
desc="detect"
$threads.push(desc)
Process.wait(io.pid)
$threads.delete_at($threads.index(desc))
}
#p "l1:#{l}"
#cmd=$VDN_PATH+"/bin/vdn-alives"
#l=`#{cmd}`
#p "l2:#{l}"
l=l.split
#puts "detect : #{l}"
@states.each_key { |i|
v=@hosts[i]
if v
if l.include?(i)
@states[i]=true
v.draw(self, @green)
else
@states[i]=false
v.draw(self, @red)
if v.vncViewer
begin
v.vncViewer.destroy
rescue
end
v.vncViewer=nil
end
if v.spiceViewer
begin
v.spiceViewer.destroy
rescue
end
v.spiceViewer=nil
end
end
end
}
true
end
def runScript(name, script)
$stderr.puts "#{Time.now} Run: \"#{script}\""
cmd="#{$VDN_PATH}/bin/vdn-scripts -n -r #{script}"
#VdnTerminal.new(name, cmd, false)
startTerminal(name, cmd, false, true, true)
end
def editScript(name, script)
cmd="#{$VDN_PATH}/bin/vdn-scripts -e #{script}"
startTerminal(name, cmd, false)
#VdnTerminal.new(name, cmd, false, true, false)
end
def hideScript(file)
mySystem("chmod 644 #{file}")
end
def unhideScript(file)
mySystem("chmod 755 #{file}")
end
def isScript(f)
return true if FileTest.symlink?(f)
return false if
! FileTest.file?(f) || ! FileTest.executable?(f) # || ( f =~ /\/repair[^\/]*$/)
true
end
def updateScriptsMenu
@main.scriptsMenu.each { |c| @main.scriptsMenu.remove(c) }
@main.scriptsMenu.append(Gtk::TearoffMenuItem.new)
dir=@dir+"/scripts"
l=Dir["#{dir}/*"]
l.sort.each { |i|
#next if ! FileTest.file?(i)
#if ! @testsToggle
# next if ! FileTest.executable?(i)
# next if ( i =~ /\/test.*/)
#end
next if ! isScript(i)
f=File.basename(i)
s=Gtk::MenuItem.new(f)
s.signal_connect('activate') {
runScript("Script : #{f}", f)
}
@main.scriptsMenu.append(s)
}
@main.scriptsMenu.show_all
end
def isTestScript(f)
return false if isScript(f)
return false if
! FileTest.file?(f) || ! FileTest.executable?(f)
return true if ( f =~ /\/test.*/)
false
end
def updateActionsMenu
#@main.actionsMenu.each { |c| @main.actionsMenu.remove(c) }
#createMoreMenu(@main.actionsMenu)
#@main.actionsMenu.show
#@main.actionsMenu.show_all
#@main.actions.submenu=@main.actionsMenu
#@main.actions.show_all
#@main.actions.signal_connect('activate') { |w,e|
# updateActionsMenu
# false
#}
end
def updateScriptsTestsMenu
@main.scriptsTestsMenu.each { |c| @main.scriptsTestsMenu.remove(c) }
dir=@dir+"/scripts"
l=Dir["#{dir}/*"]
@main.scriptsTestsMenu.append(@tearoff=Gtk::TearoffMenuItem.new)
l.sort.each { |i|
#next if ! FileTest.file?(i)
#if ! @testsToggle
# next if ! FileTest.executable?(i)
# next if ( i =~ /\/test.*/)
#end
#next if ! isTestScript(i)
next if ! FileTest.file?(i)
f=File.basename(i)
s=Gtk::MenuItem.new(f)
s.signal_connect('activate') {
#editScript("Script : #{f}", f)
runScript("Script : #{f}", f)
}
@main.scriptsTestsMenu.append(s)
}
@main.scriptsTestsMenu.append(Gtk::MenuItem.new)
# edit sub menu
editMenu = Gtk::Menu.new
editMenu.append(Gtk::TearoffMenuItem.new)
l.sort.each { |i|
#next if ( ! isScript(i) && !isTestScript(i) )
next if ! FileTest.file?(i)
f=File.basename(i)
e=Gtk::MenuItem.new(f)
e.signal_connect('activate') {
editScript("Script : #{f}", f)
}
editMenu.append(e)
}
s=Gtk::MenuItem.new("Edit...")
s.submenu=editMenu
#editMenu.append(s)
#editMenu.show
@main.scriptsTestsMenu.append(s)
# hide sub menu
hideMenu = Gtk::Menu.new
hideMenu.append(Gtk::TearoffMenuItem.new)
l.sort.each { |i|
#next if ( ! isScript(i) && !isTestScript(i) )
next if ! FileTest.file?(i)
f=File.basename(i)
e=Gtk::MenuItem.new(f)
e.signal_connect('activate') {
hideScript(i)
}
hideMenu.append(e)
}
s=Gtk::MenuItem.new("Hide...")
s.submenu=hideMenu
@main.scriptsTestsMenu.append(s)
# unhide sub menu
unhideMenu = Gtk::Menu.new
unhideMenu.append(Gtk::TearoffMenuItem.new)
l.sort.each { |i|
#next if ( ! isScript(i) && !isTestScript(i) )
next if ! FileTest.file?(i)
f=File.basename(i)
e=Gtk::MenuItem.new(f)
e.signal_connect('activate') {
unhideScript(i)
}
unhideMenu.append(e)
}
s=Gtk::MenuItem.new("Unhide...")
s.submenu=unhideMenu
@main.scriptsTestsMenu.append(s)
s=Gtk::MenuItem.new("New script...")
s.signal_connect('activate') {
#mySystem("echo -en \"New script name ? : \"; read new; echo #{dir}/$new")
cmd="echo -en \\\"(new) New script name ? : \\\"; \
read new;
cat << EOF > #{dir}/\\$new;
#!/usr/bin/env bash
DESC=\\\"One line description.\\\"
run() {
# name=XXX
#
# requestSshGuest \\\$name
# ...
}
EOF
"
startTerminal("New script", cmd, false)
}
@main.scriptsTestsMenu.append(s)
# renameScript sub menu
renameScriptMenu = Gtk::Menu.new
renameScriptMenu.append(Gtk::TearoffMenuItem.new)
l.sort.each { |i|
#next if ( ! isScript(i) && !isTestScript(i) )
next if ! FileTest.file?(i)
f=File.basename(i)
e=Gtk::MenuItem.new(f)
e.signal_connect('activate') {
cmd="echo -en \\\"(rename) New script name ? : \\\"; read new; mv #{i} #{dir}/\\$new"
startTerminal("Rename #{i}", cmd, false)
}
renameScriptMenu.append(e)
}
s=Gtk::MenuItem.new("Rename...")
s.submenu=renameScriptMenu
@main.scriptsTestsMenu.append(s)
# copyScriptMenu sub menu
copyScriptMenu = Gtk::Menu.new
copyScriptMenu.append(Gtk::TearoffMenuItem.new)
l.sort.each { |i|
#next if ( ! isScript(i) && !isTestScript(i) )
next if ! FileTest.file?(i)
f=File.basename(i)
e=Gtk::MenuItem.new(f)
e.signal_connect('activate') {
cmd="echo -en \\\"(copy) New script name ? : \\\"; read new; cp #{i} #{dir}/\\$new"
startTerminal("Copy #{i}", cmd, false)
}
copyScriptMenu.append(e)
}
s=Gtk::MenuItem.new("Copy...")
s.submenu=copyScriptMenu
@main.scriptsTestsMenu.append(s)
# deleteScript sub menu
deleteScriptMenu = Gtk::Menu.new
deleteScriptMenu.append(Gtk::TearoffMenuItem.new)
l.sort.each { |i|
#next if ( ! isScript(i) && !isTestScript(i) )
next if ! FileTest.file?(i)
f=File.basename(i)
e=Gtk::MenuItem.new(f)
e.signal_connect('activate') {
cmd="rm -i #{i}"
startTerminal("Delete #{i}", cmd, false)
}
deleteScriptMenu.append(e)
}
s=Gtk::MenuItem.new("Delete...")
s.submenu=deleteScriptMenu
@main.scriptsTestsMenu.append(s)
@main.scriptsTestsMenu.append(Gtk::MenuItem.new)
s=Gtk::MenuItem.new("Terminal (in scripts directory)...")
s.signal_connect('activate') {
startTerminal("", "cd #{@dir}/scripts; bash", false)
}
@main.scriptsTestsMenu.append(s)
@main.scriptsTestsMenu.show_all
end
def updateIsosMenu(menu)
menu.each { |c| menu.remove(c) }
dir=$VDN_PATH+"/files/g"
l=Dir["#{dir}/*.iso"]
l.sort.each { |i|
f=File.basename(i)
s=Gtk::MenuItem.new(f)
s.signal_connect('activate') {
whenStartOnSelectedIso(i)
}
menu.append(s)
}
menu.show_all
end
def updateMenus
updateMainMenu
#updateMoreMenu
end
def updateMoreMenu(menu)
state=true
state=false if @selection.empty
l=[menu.downloadDisks,menu.uploadDisksAndIsos,menu.uploadDisks]
l.each { |i| i.sensitive=state }
end
def updateOtherBuildMenu
l=`vdn-list-networks -b`
#p "l:#{l}"
l.split.sort.each { |i|
f=File.basename(i)
e=Gtk::MenuItem.new(f)
e.signal_connect('activate') {
startTerminal("Edit and build network...",
"#{ENV['VDN_PATH']}/bin/vdn-build-network -e #{File.dirname(@dir)}/#{f}", true)
#startTerminal("#{$editor} #{f}",
#"#{$editor} #{File.dirname(@dir)}/#{f}/build", false)
}
@otherBuildMenu.append(e)
}
end
def createMoreMenu(menu)
#p "createMoreMenu:#{menu}"
menu.append(Gtk::TearoffMenuItem.new)
#p "add meth to menu : #{menu}"
class << menu
attr_accessor :downloadDisks, :uploadDisksAndIsos, :uploadDisks
end
menu.downloadDisks=Gtk::MenuItem.new("Download cdrom(s) and/or disk(s)...")
menu.append(menu.downloadDisks)
menu.downloadDisks.signal_connect('activate') { |w|
whenDownload
}
menu.uploadDisksAndIsos=Gtk::MenuItem.new("Upload cdrom(s) and/or disk(s)...")
menu.append(menu.uploadDisksAndIsos)
menu.uploadDisksAndIsos.signal_connect('activate') { |w|
whenUploadDisksAndIsos
}
menu.uploadDisks=Gtk::MenuItem.new("Upload only disk(s)...")
menu.append(menu.uploadDisks)
menu.uploadDisks.signal_connect('activate') { |w|
whenUploadDisks
}
menu.append(Gtk::MenuItem.new())
=begin
@editMainConfigTemplate=Gtk::MenuItem.new("Edit template config (local) ...");
menu.append(@editMainConfigTemplate)
@editMainConfigTemplate.signal_connect('activate') { |w|
startTerminal("#{$editor} config.template.local",
"[ ! -e #{ENV['VDN_PATH']}/config.template.local ] && \
cp #{ENV['VDN_PATH']}/config.template #{ENV['VDN_PATH']}/config.template.local; \
#{$editor} #{ENV['VDN_PATH']}/config.template", false)
}
=end
@editNetworkScript=Gtk::MenuItem.new("Edit network script...");
menu.append(@editNetworkScript)
@editNetworkScript.signal_connect('activate') { |w|
startTerminal("Build network...", "#{ENV['VDN_PATH']}/bin/vdn-build-network -e #{@dir}", false)
}
@buildNetwork=Gtk::MenuItem.new("(Re)build network...");
menu.append(@buildNetwork)
@buildNetwork.signal_connect('activate') { |w|
#p "@dir:#{@dir}"
#p "ENV['NETWORK_DIR']:#{ENV['NETWORK_DIR']}"
startTerminal("vdn-build-network",
"#{$VDN_PATH}/bin/vdn-build-network")
}
menu.append(Gtk::MenuItem.new())
@otherBuildMenu = Gtk::Menu.new
@otherBuildMenu.append(Gtk::TearoffMenuItem.new)
updateOtherBuildMenu
s=Gtk::MenuItem.new("Other \"build\" scripts...")
s.submenu=@otherBuildMenu
menu.append(s)
menu.append(Gtk::MenuItem.new())
@cloneNetwork=Gtk::MenuItem.new("Clone network...");
menu.append(@cloneNetwork)
@cloneNetwork.signal_connect('activate') { |w|
startTerminal("vdn-clone-network",
"#{$VDN_PATH}/bin/vdn-clone-network", true, false)
#mySystem("vdn-clone-network")
}
@cloneNetwork=Gtk::MenuItem.new("Delete network...");
menu.append(@cloneNetwork)
@cloneNetwork.signal_connect('activate') { |w|
startTerminal("vdn-clone-network",
"#{$VDN_PATH}/bin/vdn-delete-network")
#mySystem("vdn-clone-network")
@main.closeNet
}
menu.append(Gtk::MenuItem.new())
@editNetworkGraph=Gtk::MenuItem.new("Inkscape net.svgz...");
menu.append(@editNetworkGraph)
@editNetworkGraph.signal_connect('activate') { |w|
mySystem("inkscape #{@dir}/net.svgz &")
}
@editNetworkGraph=Gtk::MenuItem.new("Delete net.svgz...");
menu.append(@editNetworkGraph)
@editNetworkGraph.signal_connect('activate') { |w|
startTerminal("Delete #{@dir}/net.svgz", "rm -i #{@dir}/net.svgz", false)
}
menu.append(Gtk::MenuItem.new())
@s=Gtk::MenuItem.new("Publish network...");
menu.append(@s)
@s.signal_connect('activate') { |w|
startTerminal("vdn-publish-network",
"vdn-publish-network")
}
menu.append(Gtk::MenuItem.new)
s=Gtk::MenuItem.new("Terminal (in network directory)...")
s.signal_connect('activate') {
startTerminal("", "cd #{@dir}; bash", false)
}
menu.append(s)
end
def createMenu(menu)
menu.append(Gtk::MenuItem.new(""))
@tearoff=Gtk::TearoffMenuItem.new
menu.append(@tearoff)
#menu.append(Gtk::MenuItem.new(""))
@start=Gtk::MenuItem.new("Download disk(s)...")
menu.append(@start)
@start.signal_connect('activate') { |w| whenDownload }
menu.append(Gtk::MenuItem.new())
@start=Gtk::MenuItem.new("Start")
menu.append(@start)
@start.signal_connect('activate') { |w| whenStart }
@startOnCdrom=Gtk::MenuItem.new("Start on cdrom")
menu.append(@startOnCdrom)
@startOnCdrom.signal_connect('activate') { |w| whenStartOnCdrom }
@startSelectCdromMenu = Gtk::Menu.new
@startSelectCdrom=Gtk::MenuItem.new("Start on selected cdrom")
@startSelectCdrom.submenu=@startSelectCdromMenu
updateIsosMenu(@startSelectCdromMenu)
menu.append(@startSelectCdrom)
@halt=Gtk::MenuItem.new("Halt")
menu.append(@halt)
@halt.signal_connect('activate') { |w| whenHalt }
#@reboot=Gtk::MenuItem.new("Reboot")
#menu.append(@reboot)
#@reboot.signal_connect('activate') { |w| whenReboot }
#@reboot.sensitive=false
@save=Gtk::MenuItem.new("Save")
menu.append(@save)
@save.signal_connect('activate') { |w| whenSave }
@restart=Gtk::MenuItem.new("Restart")
menu.append(@restart)
@restart.signal_connect('activate') { |w| whenRestart }
@ssh=Gtk::MenuItem.new("Ssh")
menu.append(@ssh)
@ssh.signal_connect('activate') { |w| whenSsh }
@viewer=Gtk::MenuItem.new("Viewer")
menu.append(@viewer)
@viewer.signal_connect('activate') { |w| whenViewer }
#Gtk::Stock.add(Gtk::Stock::EXECUTE, "Terminal", Gdk::Window::CONTROL_MASK, Gdk::Keyval::GDK_T)
#@bviewer = Gtk::ImageMenuItem.new(Gtk::Stock::EXECUTE, @group)
#networksMenu.append(@bviewer)
#@bviewer.signal_connect("activate") { |w| whenViewer }
@config=Gtk::MenuItem.new("Config...")
menu.append(@config)
@config.signal_connect('activate') { |w| whenConfig }
@infos=Gtk::MenuItem.new("Infos")
menu.append(@infos)
@infos.signal_connect('activate') { |w| whenInfos }
@clean=Gtk::MenuItem.new("Clean")
menu.append(@clean)
@clean.signal_connect('activate') { |w| whenClean }
@kill=Gtk::MenuItem.new("Kill")
menu.append(@kill)
@kill.signal_connect('activate') { |w| whenKill }
menu.append(Gtk::MenuItem.new(""))
=begin
@moreMenu = Gtk::Menu.new
@more=Gtk::MenuItem.new("More...")
@more.submenu=@moreMenu
createMoreMenu(@moreMenu)
menu.append(@more)
@more.signal_connect('activate') { |w,e|
updateMoreMenu(menu)
false
}
sep1=Gtk::MenuItem.new()
menu.append(sep1)
=end
menu.show_all
end
def updateMainMenu
state=true
state=false if @selection.empty
@menu.children.each { |i| i.sensitive=state }
#@more.sensitive=true
@tearoff.sensitive=true
end
def showMenu
updateMainMenu
@menu.popup(nil, nil, 3, 0)
end
def startTerminal(name, cmd, read=true, background=true, big=false)
@main.startTerminal(name, cmd, read, background, big)
end
def whenStart
@selection.to_a.sort_by{ |i| i.name }.each { |i|
if ! @states[i.name]
p "Start #{i.name}"
if @consoles
cmd=$VDN_PATH+"/bin/vdn-start-wrapper -et #{i.name}"
@consoles.addTerm(i.name, cmd)
@notebook.set_page(2)
else
cmd=$VDN_PATH+"/bin/vdn-start-wrapper #{i.name}"
mySystem(cmd)
end
end
}
end
def whenStartOnCdrom
@selection.to_a.each { |i|
if ! @states[i.name]
p "Start #{i.name}"
if @consoles
cmd=$VDN_PATH+"/bin/vdn-start-wrapper -et -v BOOT_CDROM=1 #{i.name}"
p "addTerm:"
@consoles.addTerm(i.name, cmd)
@notebook.set_page(2)
else
cmd=$VDN_PATH+"/bin/vdn-start-wrapper -v BOOT_CDROM=1 #{i.name}"
mySystem(cmd)
end
end
}
end
def whenStartOnSelectedIso(iso)
@selection.to_a.each { |i|
if ! @states[i.name]
p "Start #{i.name}"
if @consoles
cmd=$VDN_PATH+"/bin/vdn-start-wrapper -et -v \"BOOT_CDROM=1;CDROM=#{iso}\" #{i.name}"
@consoles.addTerm(i.name, cmd)
@notebook.set_page(2)
else
cmd=$VDN_PATH+"/bin/vdn-start-wrapper -v \"BOOT_CDROM=1;CDROM=#{iso}\" #{i.name}"
mySystem(cmd)
end
end
}
end
def whenDownload
l=""
@selection.to_a.each { |i|
l="#{l} #{i.name}"
}
startTerminal("vdn-download-disks #{l}", "#{$VDN_PATH}/bin/vdn-download-disks #{l}")
end
def whenUploadDisks
l=""
@selection.to_a.each { |i|
l="#{l} #{i.name}"
}
startTerminal("vdn-upload-disks #{l}", "#{$VDN_PATH}/bin/vdn-upload-disks #{l}")
end
def whenUploadDisksAndIsos
l=""
@selection.to_a.each { |i|
l="#{l} #{i.name}"
}
startTerminal("vdn-upload-disks -o #{l}", "#{$VDN_PATH}/bin/vdn-upload-disks -o #{l}")
end
def whenHalt
@selection.to_a.each { |i|
if @states[i.name]
mySystem($VDN_PATH+"/bin/vdn-halt #{i.name} &")
end
}
end
def whenReboot
@selection.to_a.each { |i|
if @states[i.name]
mySystem($VDN_PATH+"/bin/vdn-ssh root@#{i.name} reboot &")
end
}
end
def whenRestart
@selection.to_a.each { |i|
if @states[i.name]
p "Restart #{i.name}..."
mySystem($VDN_PATH+"/bin/vdn-restart -g #{i.name} &")
end
}
end
def whenSave
@selection.to_a.each { |i|
if @states[i.name]
mySystem($VDN_PATH+"/bin/vdn-save #{i.name} &")
end
}
end
def whenSsh
@selection.to_a.each { |i|
user=@main.net.combo.active_text
if @ssh && ENV['VDN_EXTERNAL_SSH'] != "1"
cmd=$VDN_PATH+"/bin/vdn-ssh-loop -X #{user}@#{i.name}"
@ssh.addTerm(i.name, cmd)
@notebook.set_page(3)
else
startTerminal("vdn-ssh-loop -X #{user}@#{i.name}",
"#{$VDN_PATH}/bin/vdn-ssh-loop -X #{user}@#{i.name}")
end
}
end
def whenViewer
@selection.to_a.each { |i|
if @states[i.name]
mySystem($VDN_PATH+"/bin/vdn-spice-viewer #{i.name} &")
end
}
end
def whenConfig
@selection.to_a.each { |i|
startTerminal("#{$editor} #{i.name}.conf",
"#{$editor} #{@dir}/#{i.name}.conf", false)
}
end
def whenInfos
@selection.to_a.each { |i|
startTerminal("vdn-infos #{i.name}",
"#{$VDN_PATH}/bin/vdn-infos #{i.name}")
}
end
def whenClean
l=""
@selection.to_a.each { |i|
l="#{l} #{i.name}"
}
startTerminal("vdn-clean #{l}", "#{$VDN_PATH}/bin/vdn-clean #{l}", false) if l != ""
end
def whenHaltAll
@hosts.each_value { |i|
if @states[i.name]
mySystem($VDN_PATH+"/bin/vdn-halt #{i.name} &")
end
}
end
def whenKillAll
@hosts.each_value { |i|
if @states[i.name]
mySystem($VDN_PATH+"/bin/vdn-kill -s #{i.name} &")
end
}
end
def whenKill
@selection.to_a.each { |i|
if @states[i.name]
mySystem($VDN_PATH+"/bin/vdn-kill -s #{i.name} &")
end
}
end
def startVnc(name)
Gtk.queue do
@notebook.set_page(2)
end
cmdFifoPath="#{ENV['TMPDIR']}/vdn-vnc-#{ENV['USER']}-#{name}-fifo"
mySystem("mkfifo #{cmdFifoPath} 2> /dev/null")
mySystem("chmod 600 #{cmdFifoPath}")
vncSock="#{ENV['TMPDIR']}/vdn-vnc-#{ENV['USER']}-#{name}-socket"
=begin
Thread.new {
Gtk.queue do
label=Gtk::Label.new(name)
vnc=VncViewer.new(self, label, name, $VDN_PATH, vncSock, cmdFifoPath)
@hosts[name].vncViewer=vnc
@consolesNotebook.append_page(vnc, label)
@consolesNotebook.show_all
vnc.plug
@consolesNotebook.set_page(@consolesNotebook.n_pages-1)
end
}
=end
end
def startSpiceOld(name)
Gtk.queue do
@notebook.set_page(2)
end
#cmdFifoPath="#{ENV['TMPDIR']}/vdn-vnc-#{ENV['USER']}-#{name}-fifo"
#mySystem("mkfifo #{cmdFifoPath} 2> /dev/null")
#mySystem("chmod 600 #{cmdFifoPath}")
Thread.new {
desc="startSPiceOld"
$threads.push(desc)
Gtk.queue do
label=Gtk::Label.new(name)
#spice=Gtk::EventBox.new
spice=SpiceViewer.new(name)
sleep 2
#spice=Gtk::EventBox.new
#@hosts[name].spiceViewer=spice
#@consolesNotebook.append_page(spice, label)
#@consolesNotebook.show_all
#spice.plug
#@consolesNotebook.set_page(@consolesNotebook.n_pages-1)
end
$threads.delete_at($threads.index(desc))
}
end
def hostsSorted
systems=[]
@hosts.each_value { |i| systems.push(i) }
systems.sort! { |a,b|
r=0
if a.y < b.y+50 && a.y < b.y-50
r=-1
elsif a.y > b.y+50 && a.y > b.y-50
r=1
elsif a.x < b.x
r=-1
elsif a.x > b.x
r=1
end
r
}
return systems
end
def select(m, b)
if ! m && b!=3
@selection.clear
return
end
case @main.mod
when "Shift"
found=false
last=@selection.array[0]
return if !last
sorted=hostsSorted
il=sorted.index(last)
im=sorted.index(m)
sorted=sorted.reverse if im>il
sorted.each { |i|
if i==m
found=true
end
if found
if i.isSel
break;
else
@selection.add(i)
end
end
}
when "Control"
if m && ! m.isSel
@selection.add(m)
else
@selection.del(m) if b!=3
end
else
if m && ! m.isSel
@selection.clear
@selection.add(m)
end
end
if b==3
showMenu
end
end
def parseEvent(w, e)
case e.class.to_s
when "Gdk::EventButton"
if e.event_type == Gdk::Event::Type::BUTTON2_PRESS
rs=Gdk::Rectangle.new(e.x, e.y, 1, 1)
m=nil
@hosts.each_value { |i|
x=i.rx; y=i.ry; w=i.rw; h=i.rh
r=Gdk::Rectangle.new(
x*@cx+@dx+@bx,
y*@cy+@dy+@by,
w*@cx+1, h*@cy+1)
m=i if r.intersect(rs)
break if m
}
elsif e.event_type == Gdk::Event::Type::BUTTON_PRESS
rs=Gdk::Rectangle.new(e.x, e.y, 1, 1)
m=nil
@hosts.each_value { |i|
x=i.rx; y=i.ry; w=i.rw; h=i.rh
r=Gdk::Rectangle.new(
x*@cx+@dx+@bx,
y*@cy+@dy+@by,
w*@cx+1, h*@cy+1)
m=i if r.intersect(rs)
break if m
}
if m==nil
@mx=e.x
@my=e.y
else
@mx=@my=nil
end
if e.button == 3
showMenu
@mx=@my=nil
select(m, e.button) if @selection.empty
else
select(m, e.button)
end
else
if @selRec
undrawSelRec
end
@mx=@my=nil
end
when "Gdk::EventMotion"
if @mx
drawSelRec(@mx, @my, e.x, e.y)
updateSelRec
end
end
end
def updateSelect
@selection.array.each { |i|
i.sel(self)
}
end
def updateSelRec
@hosts.each_value { |i|
state=false
x=i.x; y=i.y; w=i.rw; h=i.rh
_x=x*@cx+@dx+@bx
_y=y*@cy+@dy+@by
_w=w*@cx+1
_h=h*@cy+1
r=Gdk::Rectangle.new(_x, _y, _w, _h)
s=@selRec
if r.intersect(@selRec).to_a==r.to_a
state=true
end
if state
@selection.add(i) if ! i.isSel
else
@selection.del(i) if i.isSel
end
}
end
def drawSelRec(x1, y1, x2, y2)
return if @gc==nil
if @selRec
undrawSelRec
end
if x1>x2
t=x2; x2=x1; x1=t
end
if y1>y2
t=y2; y2=y1; y1=t
end
@selRec=Gdk::Rectangle.new(x1, y1, x2-x1, y2-y1)
@gc.function = Gdk::GC::XOR
@gc.fill = Gdk::GC::Fill::SOLID
@gc.rgb_fg_color = @grey
@gc.set_line_attributes(1, Gdk::GC::LineStyle::ON_OFF_DASH, Gdk::GC::CapStyle::ROUND, Gdk::GC::JoinStyle::ROUND)
return if @area.window==nil
@area.window.draw_rectangle(@gc, false, x1, y1, x2-x1, y2-y1)
@gc.function = Gdk::GC::COPY
end
def undrawSelRec
return if @gc==nil
if @selRec
@gc.function = Gdk::GC::XOR
@gc.fill = Gdk::GC::Fill::SOLID
@gc.rgb_fg_color = @grey
@gc.set_line_attributes(1, Gdk::GC::LineStyle::ON_OFF_DASH, Gdk::GC::CapStyle::ROUND, Gdk::GC::JoinStyle::ROUND)
return if @area.window==nil
@area.window.draw_rectangle(@gc, false, @selRec.x, @selRec.y, @selRec.width, @selRec.height)
@gc.function = Gdk::GC::COPY
@selRec=nil
end
end
def drawBoxs
@hosts.each { |k,v|
if @states[k]
v.draw(self, @green)
else
v.draw(self, @red)
end
}
end
def parseInkscape
@gw=@gh=nil
Zlib::GzipReader.open( @graphfile) do |aFile|
r=false
x=y=width=height=label=nil
tx=ty=nil
aFile.each_line do |line|
case line
when /transform="translate/
if !tx
tx=line.chomp.sub(/^.*translate.([-0-9.]+).*$/, '\1').to_i
ty=line.chomp.sub(/^.*translate.*,([-0-9.]+).*$/, '\1').to_i
end
when /width=/
if ! @gw
@gw=line.chomp.sub(/^.*"([-0-9.]+)".*$/, '\1').to_i
else
width=line.gsub(/[^0-9.]/, "").to_i
end
when /height/
if ! @gh
@gh=line.chomp.sub(/^.*"([-0-9.]+)".*$/, '\1').to_i
else
height=line.gsub(/[^0-9.]/, "").to_i
end
when /<rect/
x=y=width=height=label=nil
r=true
else
next if r==false
r=false if line =~ /\/>/
case line
when /label=/
label=line.chomp.sub(/^.*"#([^"].*)".*$/, '\1')
when /x=/
x=line.gsub(/[^0-9.]/, "").to_i
when /y=/
y=line.gsub(/[^0-9.]/, "").to_i
end
if ( !x || !y || !width || !height ) && label!=nil
$stderr.puts "rec : #{label} incomplet !"
else
if label
@hosts[label]=Host.new(
label, x+tx, y+ty, width, height)
@states[label]=false
end
end
end
end
end
end
def parseGraphviz
@gw=@gh=tx=ty=gw=gh=nil
rw=1.0; rh=1.0
svg=RSVG::Handle.new_from_file(@graphfile)
svg.close
@gw=svg.width
@gh=svg.height
Zlib::GzipReader.open( @graphfile) do |aFile|
r=false
x=y=width=height=label=nil
aFile.each_line do |line|
case line
when /transform.*translate/
if !tx
tx=line.chomp.sub(/^.*translate.([-0-9.]+).*$/, '\1').to_i
ty=line.chomp.sub(/^.*translate.* ([-0-9.]+).*$/, '\1').to_i
end
#when /class=.*node.*title/
# label=line.chomp.sub(/^.*title>(.*)<.*$/, '\1')
when /^<title>[^%]*$/
label=line.chomp.sub(/^.*title>(.*)<.*$/, '\1')
when /polygon/
points=line.chomp.sub(/^.*points="(.*)".*$/, '\1')
points=points.split(" ")
x1=points[0].sub(/^([-0-9.]+).*$/, '\1').to_i
y1=points[0].sub(/^[-0-9.]+,([-0-9.]+)$/, '\1').to_i
x2=points[2].sub(/^([-0-9.]+).*$/, '\1').to_i
y2=points[2].sub(/^[-0-9.]+,([-0-9.]+)$/, '\1').to_i
if x1<x2
x=x1; width=x2-x1
else
x=x2; width=x1-x2
end
if y1<y2
y=y1; height=y2-y1
else
y=y2; height=y1-y2
end
if label
label.gsub!("&#45;", "-")
@hosts[label]=Host.new(
label, (x+tx)*rw, (y+ty)*rh, width*rw, height*rh)
@states[label]=false
else
gw=width; gh=height
rw=@gw*1.0/gw; rh=@gh*1.0/gh
end
end
end
end
end
def close
if @graphTimeout
Gtk.timeout_remove(@graphTimeout)
@graphTimeout=nil
end
if @netTimeout
#p "remove netDetector"
Gtk.timeout_remove(@netTimeout)
@netTimeout=nil
end
@notebook.remove_page(1)
if @consolesNotebook
if @consoles
#@consolesNotebook.remove_page(@consolesNotebook.page_num(@consoles))
@consoles.close
@consoles.destroy
@consoles=nil
end
if @ssh
#@notebook.remove_page(@notebook.page_num(@ssh))
@ssh.close
@ssh.destroy
@ssh=nil
end
end
@consolesNotebook=nil
@notebook.remove_page(1)
@notebook.remove_page(1)
@notebook.set_page(0)
@hosts={}
@states={}
@graphfile=nil
end
end
class Host
attr_reader :rectangle, :rectangleSmall, :name, :isSel
attr_reader :x, :y, :w, :h
attr_reader :rx, :ry, :rw, :rh
attr_accessor :vncViewer, :spiceViewer
def initialize(name, x, y, w, h)
@isSel=false
@name=name; @x=x; @y=y; @w=w; @h=h
@rx=@x; @ry=@y; @rw=@w; @rh=@h
@black = Gdk::Color.parse("black")
@vncViewer=nil
@spiceViewer=nil
end
def draw(panel, color)
@area=panel.area
@gc=panel.gc
return if !@gc || !panel.cx
@gc.function = Gdk::GC::COPY
@gc.fill = Gdk::GC::Fill::SOLID
@gc.rgb_fg_color = color
@gc.set_line_attributes(1, Gdk::GC::LineStyle::SOLID, Gdk::GC::CapStyle::ROUND, Gdk::GC::JoinStyle::ROUND)
cx=panel.cx; cy=panel.cy
dx=panel.dx; dy=panel.dy
bx=panel.bx; by=panel.by
return if @area.window==nil
begin
@area.window.draw_rectangle(@gc, false, @x*cx+dx+bx, @y*cy+dy+by, @w*cx+1, @h*cy+1)
rescue
p "Error in draw..."
p $!
end
@gc.rgb_fg_color = @black
end
def sel(panel)
color=panel.selColor
cx=panel.cx; cy=panel.cy
dx=panel.dx; dy=panel.dy
bx=panel.bx; by=panel.by
@isSel=true
@gc.function = Gdk::GC::XOR
@gc.fill = Gdk::GC::Fill::SOLID
@gc.rgb_fg_color = color
@gc.set_line_attributes(1, Gdk::GC::LineStyle::SOLID, Gdk::GC::CapStyle::ROUND, Gdk::GC::JoinStyle::ROUND)
return if @area.window==nil
#p "### update draw in sel method"
@area.window.draw_rectangle(@gc, true, @x*cx+dx+bx+2, @y*cy+dy+by+2, @w*cx+1-3, @h*cy+1-3)
@gc.function = Gdk::GC::COPY
end
def unsel(panel)
return if @gc==nil
color=panel.selColor
cx=panel.cx; cy=panel.cy
dx=panel.dx; dy=panel.dy
bx=panel.bx; by=panel.by
@isSel=false
@gc.function = Gdk::GC::XOR
@gc.fill = Gdk::GC::Fill::SOLID
@gc.rgb_fg_color = color
@gc.set_line_attributes(1, Gdk::GC::LineStyle::SOLID, Gdk::GC::CapStyle::ROUND, Gdk::GC::JoinStyle::ROUND)
return if @area.window==nil
@area.window.draw_rectangle(@gc, true, @x*cx+dx+bx+2, @y*cy+dy+by+2, @w*cx+1-3, @h*cy+1-3)
@gc.function = Gdk::GC::COPY
end
end
class Selection
def initialize(panel)
@sel=[]
@panel=panel
end
def add(m)
@sel.push(m) if ! @sel.include?(m)
m.sel(@panel)
@panel.updateMenus
end
def del(m)
@sel.delete(m)
m.unsel(@panel)
@panel.updateMenus
end
def clear
@sel.each { |i| i.unsel(@panel) }
@sel=[]
@panel.updateMenus
end
def array
return @sel
end
def empty
return @sel.size==0
end
def to_a
return @sel
end
end
class Gtk::Browser < Gtk::VBox
attr_accessor :banner
def initialize
super
pack_start(@text=Gtk::TextView.new, true, true)
@text.justification = Gtk::JUSTIFY_CENTER
@text.cursor_visible=false
end
def location=(url)
@url=url
@text.editable=true
@text.buffer.text="\n"
w=Gtk::Image.new(@banner)
w.show_all
bold = @text.buffer.create_tag(nil,
{
'weight' =>
Pango::Weight::BOLD
})
iter=@text.buffer.end_iter
anchor = @text.buffer.create_child_anchor(iter)
@text.add_child_at_anchor(w, anchor)
@text.buffer.insert(iter, "\n\nBienvenue !\n\n", bold)
@text.buffer.insert_at_cursor("\n\n Cliquez sur le bouton ci-dessous pour accéder à la documentation dans votre navigateur : \n\n")
iter=@text.buffer.end_iter
anchor = @text.buffer.create_child_anchor(iter)
b1=Gtk::Button.new("#{@url}")
b1.show
b1.signal_connect("clicked") { mySystem("xdg-open #{@url} &") }
@text.add_child_at_anchor(b1, anchor)
@text.buffer.insert_at_cursor("\n\n")
@text.editable=false
end
end
### begin: pterms
#!/usr/bin/env ruby
require 'gtk2'
if FileTest.exist?(ENV["VDN_PATH"]+"/distribs/hosts/"+ENV["HOST_RELEASE"]+"/vte.so")
#puts "load "+ENV["VDN_PATH"]+"/files/"+ENV["HOST_RELEASE"]+"/vte.so"
require ENV["VDN_PATH"]+"/distribs/hosts/"+ENV["HOST_RELEASE"]+"/vte.so"
else
require 'vte'
end
class String
def to_b; self.downcase=="true" || self=="1" ; end
end
class Console < Gtk::EventBox
attr_reader :eventbox, :name, :term, :vte, :isGrouped
attr_accessor :icoButton, :area, :barea
attr_accessor :x, :y
def initialize(term, stock)
@term=term
@panel=@term.panel
@stock=stock
super()
add(@frame=Gtk::Frame.new)
@frame.add(@vbox=Gtk::VBox.new)
@vbox.border_width=2
@vbox.pack_start(@eventbox=Gtk::EventBox.new,false,false)
@eventbox.height_request=18
@eventbox.add(@hbox=Gtk::HBox.new)
@hbox.pack_start(@label=Gtk::Label.new(), false, false)
@hbox.pack_start(f=Gtk::Frame.new, true, true)
f.shadow_type=Gtk::SHADOW_NONE
@bclose=addButton(@hbox, @panel.iclose,
"Close", "closeConsoleAccel", :whenCloseButton)
signal_connect('destroy') { |w| whenDestroy }
@vbox.pack_start(@hbox2=Gtk::HBox.new, true, true)
@vte=Vte::Terminal.new
@vte.signal_connect('child-exited') { |w| whenExited }
@vte.signal_connect('contents-changed') { |w|
Gtk.timeout_add(300) { @update=true; false; }
}
@scrollbar=Gtk::VScrollbar.new(@vte.adjustment)
p=props('rightBar').to_b
@hbox2.pack_start(@scrollbar, false, false) if !p
@hbox2.pack_start(@vte, true, true)
@hbox2.pack_start(@scrollbar, false, false) if p
signal_connect('enter_notify_event') { |w,e|
if @panel.props("focusFollowMouse").to_b
focus
end
}
signal_connect('leave-notify-event') { |w,e|
name="GDK_NOTIFY_ANCESTOR"
if e.detail.name == name
deselect(self)
end
}
signal_connect('button-press-event') { |w,e| focus }
# Button event (focus + paste clipboard)
@vte.signal_connect('button-press-event') { |w,e|
case e.button
when 1; focus
when 2; broadcastEvent(e)
end
false
}
# Button event (paste clipboard)
@vte.signal_connect('button-release-event') { |w,e|
broadcastEvent(e) if e.button == 2
}
# Key event
@leftAccel=Gtk::Accelerator.parse(
@panel.props("switchTermRightAccel"))
init(term)
end
def init(term)
@term=term
@pid=nil
@label.text=@term.name
@vte.set_size(80,24)
@vte.set_word_chars(".a-zA-Z_0-9//-")
@vte.set_colors(Gdk::Color.parse(props('fgColor')),
Gdk::Color.parse(props('bgColor')),[])
@vte.audible_bell=props('audibleBell').to_b
@vte.visible_bell=props('visibleBell').to_b
@vte.scrollback_lines=props('scrollbackLines').to_i
a=args(term.cmd)
#if ENV['GUI_INTERNAL'] == "1"
# @pid=@vte.fork_command(a[0], a)
#else
#$stderr.puts "a:#{a}"
@pid=@vte.fork_command(:argv => a)
#end
show_all
@bclose.hide if ! props("canClose").to_b
setFontArea
setHeader
end
def setHeader
if ! props("header").to_b ||
! @term.panel.props("globalHeaders").to_b
@eventbox.height_request=4
@hbox.hide
else
@eventbox.height_request=18
@hbox.show
end
end
def props(name)
@term.props(name)
end
def args(s)
r=[]
s.gsub(/'([^']*)'|"([^"]*)"|([^[:space:]]+)/) { |m|
r.push("#{$1}#{$2}#{$3}")
}
return r
end
def addButton(w,p,t,a,handler)
i=Gtk::Image.new(p)
w.pack_start(b=Gtk::ToolButton.new(i), false, false)
accel=props(a)
b.signal_connect('clicked') { |w,e| send(handler) }
return b
end
def broadcastEvent(e)
pterms=@term.panel
panelView=@term.panel
if @isGrouped && pterms.view.currentPanelView==panelView &&
self==panelView.selected
pterms.view.sendEvent(panelView, self, e)
end
false
end
def myClampPanel(wlen, adj, min, len)
value=adj.value
if min+len > value+wlen
delta=min+len-(value+wlen)
adj.value=value+delta
else
if min<value
adj.value=min
end
end
end
def clampPanel
a=allocation; x=a.x; y=a.y
p=parent.parent
myClampPanel(p.allocation.height, p.vadjustment, a.y, a.height)
myClampPanel(p.allocation.width, p.hadjustment, a.x, a.width)
end
def deselect(c)
return if @term.panel.selected!=c
color=@term.panel.cnormal
w=c.eventbox
w.modify_bg(Gtk::STATE_NORMAL,color) if ! w.destroyed?
@term.panel.selected=nil
term.panel.main.main.add_main_accel_group
end
def focus
@vte.has_focus=true
@vte.grab_focus
selected=@term.panel.selected
return if selected==self
deselect(selected) if selected
@term.panel.selected=self
color=@term.panel.cselect
@eventbox.modify_bg(Gtk::STATE_NORMAL,color)
term.panel.main.main.add_consoles_accel_group
clampPanel
end
def setFontArea
setFont(props("gridFont"))
end
def setFont(font)
@vte.set_font(font, Vte::TerminalAntiAlias::USE_DEFAULT)
end
def whenCloseButton
whenClose
end
def whenDestroy
whenClose
end
def whenExited
whenClose
end
def whenClose
if @term
@term.close
@term.panel.update
end
end
def close
return if ! parent # already closed (and stocked)
begin
Process.kill('TERM',@pid) if @pid && (@pid != -1)
rescue
end
deselect(self)
s=@term.panel.selected
@term.panel.selectTerm(self, "right") if !s||s==self
deselect(self) if @term.panel.selected==self
parent.remove(self)
@vte.reset(true, true)
@stock.push(self)
@term.panel.update
end
end
class ConsolesView < Gtk::Frame
attr_reader :main
attr_reader :iclose
attr_reader :cnormal, :cflash, :cselect
attr_reader :props, :stock, :accelsKeys
attr_accessor :selected
def initialize(main, list)
@main=main
super()
@selected=nil
@terms=[]
@stock=PtermsStock.new
@propsStack=[]
@x=@y=0
@props={
# Application property
"globalHeaders" => "true",
"focusFollowMouse" => "true",
# Main window properties
"tooltips" => "true",
# Panel properties
"gridNbCols" => "2",
# Terminal properties
"inGrid" => "true",
"gridFont" => "Monospace 8",
"zoomFont" => "Monospace 10",
"header" => "true",
"canClose" => "true",
"minWidth" => "100",
"minHeight" => "50",
"fgColor" => "black",
"bgColor" => "white",
"audibleBell" => "true",
"visibleBell" => "true",
"scrollbackLines" => "1000",
"rightBar" => "false",
# Accelerators
# Main window accels
"quitAccel" => "<Ctrl>x",
"toggleHeadersAccel" => "<Alt>h",
"toggleButtonsTextsAccel" => "<Alt>t",
"toggleFocusFollowMouseAccel" => "<Alt>f",
"switchTermLeftAccel" => "<Shift><Alt>Left",
"switchTermRightAccel" => "<Shift><Alt>Right",
"switchTermUpAccel" => "<Shift><Alt>Up",
"switchTermDownAccel" => "<Shift><Alt>Down",
"increaseZoomAccel" => "<Shift><Alt>KP_Add",
"decreaseZoomAccel" => "<Shift><Alt>KP_Subtract",
# Terminal accels
"groupToggleAccel" => "<Ctrl>g",
"moveAccel" => "<Ctrl>m",
"closeConsoleAccel" => "<Ctrl>q",
}
@accelsKeys=[]
@props.keys.each { |p|
next if (p=~/Accel/) == nil
@accelsKeys.push(Gtk::Accelerator.parse(props(p)))
}
# build small pixbufs (used by consoles)
w=9; h=9
@iclose=createPixbuf(Gtk::Stock::CLOSE, w, h)
@cnormal=Gdk::Color.parse("#E8E8E8")
@cselect=Gdk::Color.parse("#84D7E5")
@cflash=Gdk::Color.parse("#ff4242")
add(@grid=Gtk::ScrolledWindow.new)
@grid.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC)
@grid.add_with_viewport(@grid=Gtk::Table.new(1,1))
@grid.homogeneous=false
end
def props(name)
raise "Invalid property : #{name}" if ! @props[name]
@props[name]
end
def setProps(name, value)
raise "Invalid property : #{name}" if ! @props[name]
@props[name]=value
end
def createPixbuf(t,w,h)
return render_icon(t,s=Gtk::IconSize::MENU).scale(w, h)
end
def getPropsDup
return @props.dup
end
def addToGrid(widget, x, y)
@grid.attach(widget, x, x+1, y, y+1,
Gtk::EXPAND|Gtk::FILL,
Gtk::EXPAND|Gtk::FILL, 1, 1) if ! widget.parent
end
def selectTerm(c, direction)
end
def update
@terms.each { |t| redraw(t.view.vte) if t.view }
end
def redraw(widget)
Gtk.timeout_add(200) {
begin
widget.queue_draw_area(0, 0, 10000, 10000)
rescue
end
false
}
end
def addTerm(name, cmd)
#p "addTerm : #{name} #{cmd}"
@terms.push(t=Term.new(self, name, cmd))
n=props("gridNbCols").to_i
m=0
if ! @grid.children.empty?
@grid.children.each { |i|
x=i.term.x
y=i.term.y
if y*n+x > m
m=y*n+x
end
}
@y=m/n
@x=m%n+1
else
@x=@y=0
end
if @x >= n
@y=@y+1
@x=0
end
t.build(@x,@y)
update
redraw(t.view.vte)
@x=@x+1
if @x >= props("gridNbCols").to_i
@x=0; @y=@y+1
end
end
def close
@terms.each { |t|
t.close
}
end
end
class Term
attr_reader :name, :cmd, :panel
attr_reader :x, :y
attr_accessor :view
def initialize(panel, name, cmd)
@panel=panel
@props=panel.getPropsDup if panel
@name=name
@cmd=cmd
@view=nil
end
def props(name)
raise "Invalid property : #{name}" if ! @props[name]
@props[name]
end
def build(x,y)
@x=x; @y=y
@view=@panel.stock.pop(self)
@panel.addToGrid(@view, x, y)
w=@panel.props("minWidth").to_i
h=@panel.props("minHeight").to_i
@view.vte.set_size_request(w,h)
end
def close
@view.close if @view
@view=nil
end
end
class PtermsStock
def initialize
@stock=[]
end
def pop(term)
c=@stock.pop
if !c
c=Console.new(term, self)
else
c.init(term)
end
return c
end
def push(console)
@stock.push(console)
end
end
class ConsolesPanel < Gtk::EventBox
attr_reader :main
def initialize(main, cmd, list="", comment="")
@main=main
super()
@cmd=cmd
@list=list
@cv=ConsolesView.new(@main, list).show_all
add(@vbox=Gtk::VBox.new)
@comment=comment
signal_connect('leave-notify-event') { |w,e|
selected=@cv.selected
selected.deselect(selected) if selected
}
if @comment != ""
@vbox.pack_start(Gtk::Label.new(@comment), false, false)
end
@vbox.pack_start(@hbox=Gtk::HBox.new, false, false)
@hbox.pack_start(@toolbar=Gtk::HBox.new, true, true)
@vbox.add(@cv)
@cv.focus=true
#initBar
end
def addTerm(name, cmd=nil)
#p "addTerm name:#{name} cmd:#{cmd}"
cmd=@cmd+name if cmd == nil
#p "addTerm name:#{name} cmd:#{cmd}"
@cv.addTerm(name, cmd)
end
def setProps(name, value)
@cv.setProps(name, value)
end
def close
#p "close:cv:#{@cv}"
@cv.close
end
end
class SshPanel < ConsolesPanel
def initialize(main, cmd, list="", comment="")
super(main, cmd, list, comment)
end
def addTerm(name, cmd=nil)
#p "addTerm ssh:cv:#{@cv}"
user=@main.net.combo.active_text
#p "addTerm name:#{name} cmd:#{cmd}"
cmd=@cmd+user+"@"+name if cmd == nil
#p "addTerm name:#{name} cmd:#{cmd}"
@cv.addTerm(name, cmd)
end
end
def startNet
if ! $main
#p "Create network window"
net=Vdn.new
$main=net
net.startListener
trap("TERM") { r=net.whenQuit; Gtk::main_quit if r }
else
$main.clean
$main.open
end
end
### end: pterm
# main
#GC.disable
#p "start GC"
#$lock=true
#sleep 0.2
#GC.enable
#GC.start
#GC.disable
#$lock=false
#p "disable GC"
ENV['VDN_GUI']="1"
#puts "ENV['VDN_GUI']:#{ENV['VDN_GUI']}"
$LOG_FIFO = File.open("#{ENV['TMPDIR']}/vdn-#{ENV['USER']}-gui-log", "r+")
$VDN_PATH=File.dirname($0)
$VDN_PATH=File.dirname($VDN_PATH)
#require "#{$VDN_PATH}/bin/vdn-gui-pterms.rb"
#require "#{$VDN_PATH}/bin/vnc-viewer/vdn-vnc-viewer-lib.rb"
#require "#{$VDN_PATH}/bin/vdn-spice-viewer-lib.rb"
fifo="#{ENV['TMPDIR']}/vdn-gui-#{ENV['USER']}-fifo-ctrl"
#if File.exist?(fifo)
# mySystem("rm #{fifo}")
#end
startNet
Gtk.main_with_queue(100)