Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Initial Check-In of V0.1 changes to support Snarl V37 features
git-svn-id: http://ruby-snarl.rubyforge.org/svn/trunk@10 4cdc1f07-391a-0410-901e-8ba36eb30a96
  • Loading branch information
Michael Letterle committed Mar 27, 2008
1 parent 6c0a8b9 commit 37d3091
Showing 1 changed file with 138 additions and 16 deletions.
154 changes: 138 additions & 16 deletions lib/snarl.rb
@@ -1,5 +1,6 @@
require 'dl/import'
require 'dl/struct'
require 'dl/win32'

# Snarl (http://www.fullphat.net/snarl.html) is a simple notification system,
# similar to Growl under OSX. This is a simple pure Ruby wrapper to the
Expand All @@ -17,18 +18,46 @@ module SnarlAPI
extern "HWND FindWindow(const char*, const char*)"
extern "BOOL IsWindow(HWND)"
extern "int SendMessage(HWND, uint, uint, void*)"
#extern "HWND CreateWindowEx(DWORD, LPCSTR, LPCSTR, DWORD, int, int, HWND, HMENU, HINSTANCE, LPVOID)"

CreateWindow = Win32API.new("user32", "CreateWindowExA", ['L', 'p', 'p', 'l', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'p'], 'L')
DestroyWindow = Win32API.new("user32", "DestroyWindow", ['L'], 'L')

#WIN32API
HWND_MESSAGE = 0x84
WM_USER = 0x400

#Global Event Ids
SNARL_GLOBAL_MSG = "SnarlGlobalEvent"
SNARL_LAUNCHED = 1
SNARL_QUIT = 2
SNARL_ASK_APPLET_VER = 3 #introduced in V36
SNARL_SHOW_APP_UP = 4 #introduced in V37

#Message Event Ids
SNARL_NOTIFICATION_CLICKED = 32
SNARL_NOTIFICATION_TIMED_OUT = 33
SNARL_NOTIFICATION_ACK = 34
SNARL_NOTIFICATION_CANCLED = SNARL_NOTIFICATION_CLICKED #yes that's right.

#Snarl Commands
SNARL_SHOW = 1
SNARL_HIDE = 2
SNARL_UPDATE = 3
SNARL_IS_VISIBLE = 4
SNARL_GET_VERSION = 5
SNARL_REGISTER_CONFIG_WINDOW = 6
SNARL_REVOKE_CONFIG_WINDOW = 7
SNARL_REGISTER_ALERT = 8
SNARL_REVOKE_ALERT = 9
SNARL_REGISTER_CONFIG_WINDOW_2 = 10
SNARL_GET_VERSION_EX = 11
SNARL_SET_TIMEOUT = 12
SNARL_EX_SHOW = 32
SNARL_TEXT_LENGTH = 1024
WM_COPYDATA = 0x4a

SnarlStruct = struct [
BaseSnarlStruct = [
"int cmd",
"long id",
"long timeout",
Expand All @@ -38,12 +67,22 @@ module SnarlAPI
"char icon[#{SNARL_TEXT_LENGTH}]",
]

SnarlStruct = struct BaseSnarlStruct

SnarlStructEx = struct BaseSnarlStruct + [
"char snarl_class[#{SNARL_TEXT_LENGTH}]",
"char extra[#{SNARL_TEXT_LENGTH}]",
"char extra2[#{SNARL_TEXT_LENGTH}]",
"int reserved1",
"int reserved2"]

CopyDataStruct = struct [
"long dwData",
"long cbData",
"void* lpData",
]



# character array hoop jumping, we take the passed string and convert
# it into an array of integers, padded out to the correct length
# to_cha --> to character array
Expand Down Expand Up @@ -77,9 +116,17 @@ def self.send(ss)
# path. The timeout file has a default value (DEFAULT_TIMEOUT -> 3 seconds)
# but can be set to Snarl::NO_TIMEOUT, to force a manual acknowledgement
# of the notification.
def initialize(title, msg=" ", icon=nil, timeout=DEFAULT_TIMEOUT)
@ss = SnarlStruct.malloc
show(title, msg, icon, timeout)
def initialize(title, options = {:snarl_class => nil, :msg => " ", :timeout => DEFAULT_TIMEOUT, :icon => nil, :extra => nil})

if options[:extra] && options[:snarl_class].nil? then raise ArgumentError.new("Must specificy a snarl_class to use sound notifications") end

if options[:snarl_class].nil? then
@ss = SnarlStruct.malloc
show(title, options)
else
@ss = SnarlStructEx.malloc
show(title, options)
end
end

# a quick and easy method to create a new message, when you don't care
Expand All @@ -88,8 +135,8 @@ def initialize(title, msg=" ", icon=nil, timeout=DEFAULT_TIMEOUT)
# path. The timeout file has a default value (DEFAULT_TIMEOUT -> 3 seconds)
# but can be set to Snarl::NO_TIMEOUT, to force a manual acknowledgement
# of the notification.
def self.show_message(title, msg=" ", icon=nil, timeout=DEFAULT_TIMEOUT)
Snarl.new(title, msg, icon, timeout)
def self.show_message(title, options = {:snarl_class => nil, :msg => " ", :timeout => DEFAULT_TIMEOUT, :icon => nil, :extra => nil})
Snarl.new(title, options)
end

# Update an existing message, it will return true/false depending upon
Expand Down Expand Up @@ -131,27 +178,102 @@ def self.version
version = SnarlAPI.send(ss)
"#{version >> 16}.#{version & 0xffff}"
end

# Return the current build number of snarl (not the snarl gem)
# If zero will call the original version.
def self.versionex
ssx = SnarlAPI::SnarlStructEx.malloc
ssx.cmd = SNARL_GET_VERSION_EX
versionex = SnarlAPI.send(ssx);
if versionex == 0 then
self.version
else
"#{versionex}"
end
end

protected
# Return the internal snarl id
def id
@ss.id
end


#Register an application, and optionally an icon for it
#We return the message_only window we create.
#NOTE: We do not support config windows.
def self.registerconfig(title, icon=nil)
ss = SnarlAPI::SnarlStruct.malloc
ss.title = SnarlAPI.to_cha(title)
ss.cmd = SNARL_REGISTER_CONFIG_WINDOW
ss.id = WM_USER
if not icon.nil? then
ss.icon = SnarlAPI.to_cha(icon) if File.exist?(icon)
ss.cmd = SNARL_REGISTER_CONFIG_WINDOW_2
end

win = SnarlAPI::CreateWindow.call(0, "Message", 0, 0 ,0 ,0 ,0 ,0 ,HWND_MESSAGE, 0, 0, 0)
ss.data2 = win
SnarlAPI.send(ss)
win
end

#Unregister application, passing in the value returned from registerconfig
def self.revokeconfig(hWnd)
ss = SnarlAPI::SnarlStruct.malloc
ss.data2 = hWnd
ss.cmd = SNARL_REVOKE_CONFIG_WINDOW
SnarlAPI.send(ss)
Snarl::DestroyWindow.call(hWnd)
end

#Register an alert for [app] using the name [text]
def self.registeralert(app, text)
ss = SnarlAPI::SnarlStruct.malloc
ss.title = SnarlAPI.to_cha(app)
ss.text = SnarlAPI.to_cha(text)
ss.cmd = SNARL_REGSITER_ALERT
SnarlAPI.send(ss)
end


# exactly like the contructor -- this will create a new message, loosing
# the original
def show(title,msg=" ", icon=nil, timeout=DEFAULT_TIMEOUT)
def show(title, options = {:snarl_class => nil, :msg => " ", :timeout => DEFAULT_TIMEOUT, :icon => nil, :extra => nil})

options[:timeout] = DEFAULT_TIMEOUT if options[:timeout].nil?
options[:msg] = " " if options[:msg].nil?

if options[:snarl_class].nil? then
@ss.cmd = SNARL_SHOW
else
@ss.cmd = SNARL_EX_SHOW
@ss.snarl_class = options[:snarl_class]
end

@ss.title = SnarlAPI.to_cha(title)
@ss.text = SnarlAPI.to_cha(msg)
if icon
icon = File.expand_path(icon)
@ss.icon = SnarlAPI.to_cha(icon) if File.exist?(icon.to_s)
@ss.text = SnarlAPI.to_cha(options[:msg])

if options[:icon]
#Expand Path in Cygwin causes the cygwin path to be returned (ie /cygdrive/c/blah) this is not what we want
#as Snarl is running in windows and expects a C:\blah path. We've told them to use an absolute path anyway.
#options[:icon] = File.expand_path(options[:icon])
@ss.icon = SnarlAPI.to_cha(options[:icon]) if File.exist?(options[:icon].to_s)
end
@ss.timeout = timeout
@ss.cmd = SNARL_SHOW

if options[:extra]
unless options[:extra][0] == 43
#options[:extra] = File.expand_path(options[:extra])
@ss.extra = SnarlAPI.to_cha(options[:extra]) if File.exist?(options[:extra].to_s)
else
@ss.extra = SnarlAPI.to_cha(options[:extra])
end
end

@ss.timeout = options[:timeout]
@ss.id = send
end


# Send the snarl structure, return the unfiltered result
def send
SnarlAPI.send(@ss)
Expand Down

0 comments on commit 37d3091

Please sign in to comment.