Created
December 8, 2012 15:06
-
-
Save tarao/4240667 to your computer and use it in GitHub Desktop.
trayapp - show tray icon with running something
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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