Skip to content

Instantly share code, notes, and snippets.

@tarao
Created December 8, 2012 15:06
Show Gist options
  • Save tarao/4240667 to your computer and use it in GitHub Desktop.
Save tarao/4240667 to your computer and use it in GitHub Desktop.
trayapp - show tray icon with running something
#!/usr/bin/env ruby
require 'yaml'
require 'optparse'
require 'shellwords'
require 'logger'
require 'gtk2'
module Loggable
class Logger
def initialize(klass, *args)
@klass = klass
@logger = ::Logger.new(*args)
end
def method_missing(name, *args)
if [ :debug, :info, :warn, :error, :fatal ].include?(name)
@logger.__send__(name, @klass){ args[0] }
elsif block_given?
block = proc
@logger.__send__(name, *args, &block)
else
@logger.__send__(name, *args)
end
end
end
def log(*args)
if !args.empty? || @logger == nil
args << STDERR if args.empty?
classname = self.is_a?(Class) ? self.to_s : self.class.to_s
@logger = Logger.new(classname, *args)
if ENV['DEBUG'].to_i > 1 || $DEBUG
@logger.level = ::Logger::DEBUG
elsif ENV['DEBUG'].to_i > 0
@logger.level = ::Logger::INFO
else
@logger.level = ::Logger::WARN
end
end
return @logger
end
def debug(msg) log.debug(msg) end
def info(msg) log.info(msg) end
def warn(msg) log.warn(msg) end
def error(msg) log.error(msg) end
def fatal(msg) log.fatal(msg) end
end
class Class; include Loggable end
include Loggable
class Run
include Loggable
def initialize(cmd)
@cmd = Shellwords.split(cmd)
end
def run()
log.info("run '#{@cmd.join(' ')}'")
system(*@cmd)
end
class Async < Run
@@pids = []
def self.kill()
@@pids.each do |pid|
unless Process.waitpid2(pid, Process::WNOHANG)
log.info("kill #{pid}")
Process.kill(:KILL, pid)
end
end
@@pids = []
end
at_exit{ self.kill }
def run()
log.info("spawn '#{@cmd.join(' ')}'")
@@pids << spawn(*@cmd)
end
end
end
### commands
commands = []
quit_commands = []
### GTK
menu = Gtk::Menu.new
icon = Gtk::StatusIcon.new
icon_file = nil
tooltip = nil
icon.signal_connect('popup-menu') do |s,button,time|
menu.popup(nil, nil, button, time)
end
menu_quit = Gtk::MenuItem.new('quit')
menu_quit.signal_connect('activate') do |item|
quit_commands.each(&:run)
Gtk.main_quit
end
### setters
set_icon = proc do |i|
if File.exist?(i)
icon_file = i
else
begin
icon.stock = Gtk::Stock.const_get(i.upcase)
rescue
log.error("no such icon '#{i}'")
end
end
end
set_tooltip = proc{|text| tooltip = text}
add_run = proc{|cmd| commands << Run.new(cmd)}
add_spawn = proc{|cmd| commands << Run::Async.new(cmd)}
add_quit = proc{|cmd| quit_commands << Run.new(cmd)}
add_menu = proc do |m|
if m =~ /^([^;]*);(.*)$/
item = Gtk::MenuItem.new($1)
action = Run.new($2)
item.signal_connect('activate'){|m| action.run}
menu.append(item)
end
end
### options
opt = OptionParser.new
desc = 'Tray icon file or stock object name.'
opt.on('-i', '--icon FILE|NAME', desc, &set_icon)
desc = 'Tooltip to show on the tray icon.'
opt.on('-t', '--tooltip TEXT', desc, &set_tooltip)
opt.on('-r', '--run COMMAND', 'Run a command syncrhonously.', &add_run)
opt.on('-s', '--spawn COMMAND', 'Run a command asyncrhonously.', &add_spawn)
opt.on('-q', '--quit COMMAND', 'Run a command on quit.', &add_quit)
opt.on('-m', '--menu ITEM;COMMAND', 'Menu item.', &add_menu)
opt.on('-c', '--config FILE', 'Configuration file in YAML.') do |file|
log.info("load '#{file}'")
yaml = YAML.load(IO.read(file))
set_icon[yaml['icon']] if yaml['icon'] && !icon_file && !icon.stock
set_tooltip[yaml['tooltip']] if yaml['tooltip'] && !tooltip
if yaml['exec'].is_a?(Array)
yaml['exec'].each do |spec|
if spec =~ /^s\s+(.*)$/
add_spawn[$1]
elsif spec =~ /^(?:r\s+)?(.*)$/
add_run[$1]
end
end
end
yaml['quit'].each{|spec| add_quit[spec]} if yaml['quit'].is_a?(Array)
if yaml['menu'].is_a?(Array)
yaml['menu'].each{|spec| add_menu[spec['label']+';'+spec['action']]}
end
end
opt.parse!(ARGV)
icon.stock = Gtk::Stock::YES unless icon_file || icon.stock
icon.file = icon_file if icon_file
tooltip = File.basename($0) unless tooltip
icon.tooltip = tooltip
### main
commands.each(&:run)
menu.append(menu_quit)
menu.show_all
Gtk.main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment