#!/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 // 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 /^[^%]*$/ 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!("-", "-") @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)