Project

General

Profile

unexist.dev

/

subtle

Assorted tidbits and projects

Writing sublets » History » Version 9

Andrew Fricke, 02/09/2011 02:47 AM
Updated spec.config to include :def_value.

1 9 Andrew Fricke
h1. Writing sublets\015\012\015\012{{>toc}}\015\012\015\012h2. Hello world\015\012\015\012Starting a [[sublets|sublet]] from scratch is really easy, just create an empty [[sublets|sublet]] with [[sur]]: @sur template hello@\015\012\015\012This will create a folder *hello* with two files:\015\012\015\012|_. Filename    |_. Description                                                                              |\015\012| *hello.rb*    | The [[sublets|sublet]] file with the minimal required code                                 |\015\012| *hello.spec*  | The [[specification]] file with basic information for [[sur]] about the [[sublets|sublet]] |\015\012\015\012h3. Say hello\015\012\015\012Per default, the new [[sublets|sublet]] will do nothing and we need to add something do the data field:\015\012\015\012<pre>{{hide}}<code class="ruby"># Hello sublet file\015\012# Created with sur-0.2\015\012configure :hello do |s|\015\012  s.interval = 60\015\012end\015\012\015\012on :run do |s|\015\012  s.data = "Hello, world!"\015\012end</code></pre>\015\012\015\012h3. Test your sublet\015\012\015\012[[sur]] comes with a tester for [[sublets|sublet]] to ease the whole thing: @sur test hello/hello.rb\015\012\015\012The output looks like this:\015\012\015\012<pre>{{hide}}<code class="bash">------------------\015\012 hello\015\012------------------\015\012 @visible = true\015\012 @interval = 60\015\012 @config = {}\015\012 @name = hello\015\012 @data = Hello, world!\015\012------------------\015\012 (1) run \015\012 (0) Quit\015\012------------------\015\012>>> Select one method:\015\012>>> \015\012</code></pre>\015\012\015\012The lines starting with *&#64;* show the defined instanced variables, the lines with *(1)* the callable methods.\015\012\015\012h3. Install and submit\015\012\015\012Once you are satisfied with your work you need to build your [[sublets|sublet]]: @sur build hello/hello.spec@\015\012\015\012This will create a *hello-0.0.sublet* file that can either be installed locally @sur install ./hello-0.0.sublet@ or uploaded to the [[sur]] repository @sur submit ./hello-0.0.sublet@ and installed from there.\015\012\015\012h2. Components\015\012\015\012h3. Configure\015\012\015\012A [[sublets|sublet]] starts with the configure block, which tells [[subtle]] the name and does basic initialization like the interval time if it's used. Assignment of instance variables *must* be done like @s.value = "something"@ or otherwise the "DSL":http://en.wikipedia.org/wiki/Domain-specific_language can't keep track of it.\015\012\015\012<pre>{{hide}}<code class="ruby">configure :sublet do |s|\015\012  s.interval = 60\015\012  s.variable = "something"\015\012end</code></pre>\015\012\015\012Generally there are two different types of [[Sublets]]:\015\012\015\012* [[sublets]] that are updated by given interval in seconds (default 60s)\015\012* [[sublets]] that are updated when a file is modified (via "inotify":http://en.wikipedia.org/wiki/Inotify) or via socket\015\012* [[sublets]] that are updated via [[Hacking#SUBTLE_SUBLET_DATA|SUBTLE_SUBLET_DATA]] client message\015\012\015\012The [[sublets|sublet]] data *must* be of type "String":http://www.ruby-doc.org/core/classes/String.html, everything else will be ignored.\015\012\015\012h3. Helper\015\012\015\012Custom methods need to be written inside of a helper block to be used a instance method. Inside of this helpers, instance variables *must* be accessed via the *self* keyword:\015\012\015\012<pre>{{hide}}<code class="ruby">helper do\015\012  def something(test)\015\012    self.variable = test\015\012    self.data     = test\015\012  end\015\012end</code></pre>\015\012\015\012h3. Events\015\012\015\012[[Sublets]] are event driven, so there are some specific events only for [[sublets]]:\015\012\015\012| *&#58;mouse_over* | Whenever the pointer is over the [[sublets|sublet]] |\015\012| *&#58;mouse_down* | Whenever the pointer is pressed on the [[sublets|sublet]]. Can have three arguments: x, y, button. |\015\012| *&#58;mouse_out*  | Whenever the pointer leaves the [[sublets|sublet]] |\015\012| *&#58;run*        | Whenever either the interval time is expired |\015\012| *&#58;watch*      | Whenever the watched file is modified/socket has data ready |\015\012| *&#58;data*       | Whenever [[subtle]] receives a [[Hacking#SUBTLE_SUBLET_DATA|SUBTLE_SUBLET_DATA]] client message for this [[sublets|sublet]] |\015\012| *&#58;unload*     | Whenever the [[sublets|sublet]] is unloaded |\015\012\015\012h3. Hooks\015\012\015\012[[Sublets]] can use all [[hooks]] that are useable in the main config too, the syntax is almost the same:\015\012\015\012<pre>{{hide}}<code class="ruby">on :client_create do |s, c|\015\012  puts s.name #< Name of the sublet\015\012  puts c.name #< Name of the client\015\012end</code></pre>\015\012\015\012h3. Grabs\015\012\015\012Since r2608 [[sublets]] can provide [[grabs]] identified by symbols, that can be used in the main config.\015\012\015\012<pre>{{hide}}<code class="ruby">\015\012configure :grabby do |s|\015\012  s.interval = 5\015\012end\015\012\015\012grab :GrabbyGrab do |s, c|\015\012  puts "sublet name: %s" % [ s.name ]\015\012  puts "pressed on : %s" % [ c.name ]\015\012end\015\012</code></pre>\015\012\015\012_Please use reasonable names for the [[grabs]], there is no check or enforcement of names._\015\012\015\012h2. Configuration\015\012\015\012Configuration of a [[sublets|sublet]] can be done (since r2148) "Subtle::Subtle#config":http://api.subforge.org/classes/Subtle/Sublet.html#M000244, this returns a "hash":http://ruby-doc.org/core/classes/Hash.html and can be used as in the following example:\015\012\015\012<pre>{{hide}}<code class="ruby"># Config:\015\012sublet :configured do\015\012  interval    50\015\012  some_string "#00ff00"\015\012end\015\012\015\012# Sublet:\015\012configure :configured do |s|\015\012  s.interval    = s.config[:interval]    || 30\015\012  s.some_string = s.config[:some_string] || "default"\015\012\015\012  # Works with colors too\015\012  s.red = Subtlext::Color.new(s.config[:red] || "#ff0000")\015\012end\015\012\015\012on :run do |s|\015\012  s.data = s.red + s.some_string\015\012end</code></pre>\015\012\015\012The [[specification]] contains an info field that can be displayed via @sur config sublet@, it's basically an "array":http://ruby-doc.org/core/classes/Array.html of "hashes":http://ruby-doc.org/core/classes/Hash.html that contains information how to use it:\015\012\015\012<pre>{{hide}}<code class="ruby">\015\012spec.config = [\015\012  { :name => "format_string", :type => "string", :def_value => "%y/%m/%d %H:%M", :description => "Format of the clock (man date)" },\015\012]\015\012</code></pre>\015\012\015\012h2. Customization\015\012\015\012h3. Colors\015\012\015\012The color of the ouput of a [[sublet|Sublets]] can be changed in this way:\015\012\015\012<pre>{{hide}}<code class="ruby">configure :colorful do |s|\015\012  s.red        = Subtlext::Color.new("#ff0000")\015\012  s.green      = Subtlext::Color.new("#00ff00")\015\012  s.blue       = Subtlext::Color.new("#0000ff")\015\012  s.background = "#303030"\015\012end\015\012\015\012on :run do |s|\015\012  s.data = s.red + "su" + s.green + "bt" + s.blue + "le"\015\012end</code></pre>\015\012\015\012h3. Icons\015\012\015\012There is also a way to add a "X bitmap":http://en.wikipedia.org/wiki/XBM to a [[Sublets|sublet]]:\015\012\015\012<pre>{{hide}}<code class="ruby">configure :iconized do |s|\015\012  s.icon = Subtlext::Icon.new("/usr/share/icons/subtle.xbm")\015\012end\015\012\015\012on :run do |s|\015\012  s.data = @icon + "subtle"\015\012end</code></pre>\015\012\015\012A nice collection of this pixmap can be found "here":http://subforge.org/attachments/download/43/icons.xz\015\012\015\012_[[Subtle]] will add a padding of 3px left and right of the pixmap, so keep that in mind when using the click hooks._\015\012\015\012h2. Examples\015\012\015\012Below is the code of a shipped [[sublet|sublets]] that displays the time. It should be really straight forward:\015\012\015\012<pre>{{hide}}<code class="ruby">configure :clock do |s|\015\012  s.interval = 60 #< Set interval time\015\012end\015\012\015\012on :run do |s|\015\012  s.data = Time.now.strftime("%d%m%y%H%M") #< Set data\015\012end</code></pre>\015\012\015\012Another example for the "inotify":http://en.wikipedia.org/wiki/Inotify [[sublets|sublet]] which is also included within [[subtle]]:\015\012\015\012<pre>{{hide}}<code class="ruby">configure :notify do |s|\015\012  s.file = "/tmp/watch"\015\012  s.watch(s.file)\015\012end\015\012\015\012on :run do\015\012  begin\015\012    self.data = IO.readlines(@file).first.chop #< Read data and strip\015\012  rescue => err #< Catch error\015\012    puts err\015\012    self.data = "subtle"\015\012  end\015\012end</code></pre>\015\012\015\012The *watch* command also works with "Ruby":http://www.ruby-lang.org sockets, but be aware of blocking I/O:\015\012\015\012<pre>{{hide}}<code class="ruby">configure :socket do |s|\015\012  s.socket = TCPSocket.open("localhost", 6600)\015\012  s.watch(s.socket)\015\012end\015\012\015\012on :watch do |s|\015\012  begin\015\012    s.data = s.socket.readline.chop #< Read data and strip\015\012  rescue => err #< Catch error\015\012    puts err\015\012    s.data = "subtle"\015\012  end\015\012end\015\012\015\012on :run do |s|\015\012  #  Do nothing\015\012end</code></pre>\015\012\015\012_[[Subtle]] will automatically set sockets to O_NONBLOCK._\015\012\015\012And finally an example with a click callback:\015\012\015\012<pre>{{hide}}<code class="ruby">configure :click do |s|\015\012  s.interval = 999 #< Do nothing\015\012end\015\012\015\012on :mouse_down do |s, x, y, button|\015\012  Subtlext::Client["xterm"].raise\015\012  puts x, y, button\015\012end\015\012\015\012on :run do |s|\015\012  # Do nothing here\015\012end</code></pre>