Older blog entries for dan (starting at number 168)

Finding a Way(land)

In the next round we shall be sending it the messages we have so lovingly composed from whole cloth and see if it reacts the same way as it did when the same bytes were sent from weston-info

And the answer is … yes, pretty much. We had to fix up our string parsing to make sense of the replies, but watch:

psadan.core> (def connection (conn/open-connection "/home/dan/private/wayland-0"))
#'psadan.core/connection
psadan.core> (defn test-send-message []
  (let [registry
        (conn/remember-object
         connection
         {:id 2 :interface (proto/find-interface-by-name :wl_registry)})
        done-cb
        (conn/remember-object
         connection
         {:id 3 :interface (proto/find-interface-by-name :wl_callback)})
        ]
    (conn/write-buffer connection
                       (buf/pack-message connection (:display connection)
                                         :requests :get_registry [registry]))
    (conn/write-buffer connection
                       (buf/pack-message connection (:display connection)
                                         :requests :sync [done-cb]))
    registry))

#'psadan.core/test-send-message
psadan.core> (test-send-message)
{:id 2, :interface {:index 1, :name :wl_registry, :requests ({:index 0, :name :bind, :summary "bind an object to the display", :args ({:name "name", :type :uint, :interface nil} {:name "id", :type :new_id, :interface nil})}), :events ({:index 0, :name :global, :summary "announce global object", :args ({:name "name", :type :uint, :interface nil} {:name "interface", :type :string, :interface nil} {:name "version", :type :uint, :interface nil})} {:index 1, :name :global_remove, :summary "announce removal of global object", :args ({:name "name", :type :uint, :interface nil})}), :enums ()}}
psadan.core> (def received (read-buffer connection))
#'psadan.core/received
psadan.core> (pprint (map (fn [x] [(:object-id x) (:message x) (:args x)]) (buf/parse-messages received connection :events)))
nil
([2 :global (1 "wl_display" 1)]
 [2 :global (2 "wl_compositor" 2)]
 [2 :global (3 "screenshooter" 1)]
 [2 :global (4 "text_cursor_position" 1)]
 [2 :global (5 "text_model_factory" 1)]
 [2 :global (6 "wl_data_device_manager" 1)]
 [2 :global (7 "wl_shm" 1)]
 [2 :global (8 "wl_seat" 1)]
 [2 :global (9 "input_method" 1)]
 [2 :global (10 "wl_output" 1)]
 [2 :global (11 "wl_drm" 1)]
 [2 :global (12 "wl_shell" 1)]
 [2 :global (13 "desktop_shell" 1)]
 [2 :global (14 "screensaver" 1)]
 [2 :global (15 "input_panel" 1)]
 [2 :global (16 "workspace_manager" 1)]
 [3 :done (58)]
 [1 :delete_id (3)])

My interpretation of what’s happening here is that we’re sending to the server a ‘tell object 2 about all your global objects’ message, followed by a ‘tell object 3 done when you’re finished doing stuff’ message, and as you can see from the output, the reply is a bunch of ids for global objects sent to object 2, a done event sent to object 3, and then a delete_id event for object 3 sent to object 1. I’m actually not sure why that last one triggers, as I don’t think I asked it to. Perhaps it’s just tidying up for me.

I’m also handwaving – if not actually handdrowning – a litle bit, because really … are these :global messages sent to object 2 or from object 2? For the moment, I am using the two directions interchangeably, which is probably not a recipe for an easier future life, but in the meantime I can continue to tread water.

It’s instructive, or at least reassuring, to compare this stuff with what weston-info says:

interface: 'wl_display', version: 1, name: 1
interface: 'wl_compositor', version: 2, name: 2
interface: 'screenshooter', version: 1, name: 3
interface: 'text_cursor_position', version: 1, name: 4
interface: 'text_model_factory', version: 1, name: 5
interface: 'wl_data_device_manager', version: 1, name: 6
interface: 'wl_shm', version: 1, name: 7
	formats: XRGB8888 ARGB8888
interface: 'wl_seat', version: 1, name: 8
	capabilities: pointer keyboard
interface: 'input_method', version: 1, name: 9
interface: 'wl_output', version: 1, name: 10
	x: 0, y: 0,
	physical_width: 1024 mm, physical_height: 640 mm,
	make: 'xwayland', model: 'none',
	subpixel_orientation: unknown, output_tranform: normal,
	mode:
		width: 1024 px, height: 640 px, refresh: 60 Hz,
		flags: current preferred
interface: 'wl_drm', version: 1, name: 11
interface: 'wl_shell', version: 1, name: 12
interface: 'desktop_shell', version: 1, name: 13
interface: 'screensaver', version: 1, name: 14
interface: 'input_panel', version: 1, name: 15
interface: 'workspace_manager', version: 1, name: 16

Top Wayland tip for today: it appears to be the case that you can make the C library clients log protocol exchanges to stderr by putting WAYLAND_DEBUG=client in the environment. When doing that it’s clear to see that weston-info is making a couple of additional requests that we’re not. We could add them, but I think the more pressing concern is to make it do something with the events we’re getting already – if it’s sending us details of global objects that we might need to know about, we should at a minimum be storing those details somewhere instead of throwing them away …

Syndicated 2013-03-14 13:32:01 from diary at Telent Netowrks

One more step along the Wayland

Yesterday’s lunchtime hacking was all about splitting the project into multiple files and getting it into git and onto Github – note that the mere fact of it being publically browsable does not imply that it will run, build, walk, make tea, perform any other useful function, or even forbear from exploding inside your computer and rendering the SSD to molten slag. Nor that I’m not still ashamed of it. It just keeps me slightly more honest.

Today I implemented enough of pack-message to be able to recreate the initial client→compositor message that we observed weston-info send last week. Still taking extraordinary liberties with signed vs unsigned longs, and plase note that all this code will work only on little-endian machines (there are any big-endian machines left?).

Lessons, puzzles

Leiningen does not need you to list the source files in your repository individually: it finds them magically. I believed otherwise for a while, but it turned out (slightly embarrassingly) that I had a parenthesis i the wrong place. My working hypothesis is that it assumes there is one namespace for each file, and any reference to a namespace it doesn’t know about it can be satisfied by loading a file with that name.

If I type (in-ns 'psadan.core) at the repl and that ns does not include a (:refer-clojure) form, I can’t use the symbols in clojure.core at the repl. I have not observed a similar issue wrt uses of clojure.core/foo in core.clj itself, just at the repl.

atoms! An atom is dead simple, really – conceptually at least, if not also under the hood: it’s a wrapper for an object that lets you look inside with deref and lets you change what’s inside with swap!. For each connection we use an atom holding a mapping from object ids to the corresponding objects, which starts out holding the singleton object for wl_display and then needs to be updated each time we generate an object locally and each time we learn of a new object from the peer.

(defn open-connection [name]
  (let [s (cx.ath.matthew.unix.UnixSocket. name)
        in (. s getInputStream)
        out (. s getOutputStream)
        wl-display (global-object-factory)
        ]
    {:socket s
     :input in
     :output out
     :display wl-display
     :objects (atom (assoc {} 1 wl-display))
     }))

(defn remember-object [conn id object]
  ;; (swap r fn args...) gets the current value of the atom inside r,
  ;; which for the sake of argument we shall call oldval, then sets the atom
  ;; to the result of calling (fn oldval args...)
  (swap! (:objects conn) assoc id object)
  object)

(defn get-object [conn id]
  ;; @foo is another way to write (deref foo)
  (let [o (get @(:objects conn) id)]
    o))

I have probably not chosen the fastest possible way of building up the messages I plan to send, in terms of fiddling around sticking vectors of bytes together. Will worry about that later if it turns out to be a bottleneck (but suggestions are welcome).

There was not a lot of Wayland learning this time. In the next round we shall be sending it the messages we have so lovingly composed from whole cloth and see if it reacts the same way as it did when the same bytes were sent from weston-info

Syndicated 2013-03-12 13:57:03 from diary at Telent Netowrks

Still some way(land) to go

Ignoring, because I can, this whole Ubuntu Mir thing completely, I have begun to learn stuff about the Wayland protocol (and about Clojure, with which I am still at the constantly-having-to-google-stuff stage). Some random notes on what I have learnt so far

First off: Java has no builtin support for talking to unix-domain (AF_FILE) sockets. And nobody seems to make a Maven-packaged library that does it either. This is a shame because Leiningen makes Maven manageable, but anything unmavened involved tedious mucking around. Eventually I did

:; apt-get install libunixsocket-java
:; cat project.clj
(defproject psadan "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :resource-paths ["/usr/share/java/unix-0.5.jar"]
  :url "http://example.com/FIXME"
  :jvm-opts ["-Djava.library.path=native/:/usr/lib/jni/"]
  :main psadan.core
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.3.0"]
                 [org.clojure/data.zip "0.1.1"]
                 ])

which seems to be holding up. Then I opened a socket and tried reading from it in the hope of getting some lovely protocoly stuff

(defn open-socket [name]
  (let [s (cx.ath.matthew.unix.UnixSocket. name)
        in (. s getInputStream)
        out (. s getOutputStream)
        ]
    {:socket s :input in :output out}))

(def socket (open-socket “/home/dan/private/wayland-0”))

(defn rd [] (let [buf (byte-array 1024)] (. (:input socket) (read buf)) buf))

and watched it hang. After some time looking at documentation and mucking about with strace to see if it was trying to read the full buffer instead of doing the dhort read I wanted it to, I eventually thought to use socat instead of clojure. It turns out that the client is expected to make the first request before the server sends anything, and with the use of strace weston-info I was able to find out what.

26335 sendmsg(3, {msg_name(0)=NULL, msg_iov(1)=[{"\1\0\0\0\1\0\f\0\2\0\0\0\1\0\0\0\0\0\f\0\3\0\0\0", 24}], msg_controllen=0, msg_flags=0}, MSG_DONTWAIT|MSG_NOSIGNAL) = 24

Time to start writing some code to parse wayland.xml so we can actually find out what this means. The usual way to do XML parsing in clojure seems to be using zippers and the easy examples seem to be somewhat lacking or slightly ungooglable. You need a project dependency on org.clojure/data.zip and a bunch of package requires, then you call clojure.zip/xml-zip on the return value of clojure.xml/parse and that gets you a zipper

(ns psadan.core
  (:require [clojure.xml]
            [clojure.data.zip :as dz]
            [clojure.data.zip.xml :as x]
            [clojure.walk]
            [clojure.zip :as z]))

;; [...]

(def the-protocol
  (->
   "/home/dan/wayland/source/wayland/protocol/wayland.xml"
   clojure.xml/parse
   z/xml-zip
   parse-protocol))

where I have conveniently left out the definition of parse-protocol and everything it calls because it’s longwinded and tedious (but the code will be on github as soon as I’m not ashamed of it) but it might hypothetically do things like

(x/xml-> my-zipper :protocol :interface :request)

to descend the tree through <protocol> <interface> <request> and return all the elements. Use the similarly named x/xml1-> to get the first matching element. The return values from these things are themselves zippers and you can call up, down etc – or xml-> again – to traverse the tree further, then eventually call node when you want to get the element itself. So e.g.

(defn parse-interface [i n]
  ;; n is a badly named zippered xml object thingy
  (let [el (z/node n)
        requests (map-indexed parse-message (x/xml-> n :request))
        events (map-indexed parse-message (x/xml-> n :event))
        enums (map parse-enum (x/xml-> n :enum))]
    {:index i
     :name (:name (:attrs el))
     :requests requests
     :events events
     :enums enums
     }))

So let’s handwave over the details and take it on trust that I have parsed the whole file. There are two other things I discovered - mostly thanks to the #wayland IRC channel participants – about the wayland wire protocol that the docs don’t mention:

  • where it says “The first word is the sender’s object id (32-bit)”, when it’s describing a message sent from client to compositor, what it means is “The first word is the target object’s id (32-bit)”.
  • object id 1 is special: it refers to the core global singleton object, which implements the interface wl_display

Given which, we can attempt to parse that first client→compositor message

psadan.core> (parse-messages-from-buf (vec (.getBytes "\1\0\0\0\1\0\f\0\2\0\0\0\1\0\0\0\0\0\f\0\3\0\0\0")) :requests)
({:object-id 1, :bytes 12, :interface "wl_display", :message "get_registry", :opcode 1, :args (2)} {:object-id 1, :bytes 12, :interface "wl_display", :message "sync", :opcode 0, :args (3)})

Looks plausible so far …

Next up, some code to create messages. And something, which may involve an atom, to map object ids to their corresponding interfaces as we learn them. After that, we find out what the Wayland FAQ really means by “shareable buffer”

Syndicated 2013-03-07 22:01:54 from diary at Telent Netowrks

Puppets at work

Another huge long gap between updates. I’d say that you might as well get used to it, but you probably are already. This is a short note to say that I have developed a Capistrano extension which runs Puppet as part of an app deploy, and you can read about it on the Simply Business tech blog

In other news, having confirmed that the X11 touchpad support is broken by design – the choice of whether to emulate mouse events (left click, middle click, drag etc) is set at a global level in the driver and cannot be overridden per client, so legacy mouse-only clients cannot meaningfully coexist with clients that usefully could handle touch events directly – I am working on a client implementation of the Wayland protocol in Clojure. I hope this will eventually will turn into a habitable text editor.

Syndicated 2013-03-01 11:12:54 from diary at Telent Netowrks

Something old, something new

Sorry for the silence. This has been a scheduling thing mostly: I managed to schedule ‘breaking my arm’ for about three days after ‘starting a new job’, and together with the ongoing project of ‘being a new Dad’ (I plan to pivot that one into just ‘being a Dad’ if and when I finally feel like I know what I’m doing – so maybe in about 20 years or so) this has left not much time for discretionary writing.

Something old: a couple of months ago Vsevolod Dyomkin emailed me to ask if I wanted to be featured in his series of interviews with Lisp hackers (or, in my case, former Lisp hackers). After keeping him waiting for unsnsionably long – partly due, of course, to the aforementioned bone breakage – finally I have sent him my answers and he has posted them

(I can’t decide if I’m actually serious about writing something justifying the existence of ASDF, or if I’m just going to let it lie. It can probably be summarised as “it made sense at the time”)

Something new:

  • New job: I’m now working at Simply Business, nominally as a Ruby programmer but in practice so far mostly doing Puppet and Vagrant and general devops-y deployment-y stuff. It’s slightly odd having a commute further than the spare bedroom (which is no longer actually spare now, please note) but I seem to have adjusted mostly to having to get up in the morning, and the walk to work is podcast time. Except when it’s Spotify time. Or when I take a bike, but for a 2km journey that often seems a bit like overkill.
  • New consumer toys. After dropping the Desire S and breaking the screen on it twice, I gave up and sniped a second hand Galaxy Nexus on Ebay. It’s a bit like running an Android phone, except that it has no stupid skins or preloaded apps, and it gets timely updates to the newest OS version. Seriously, there is a certain irony in the fact that a phone I chose because it was open and easy to reflash or root turns out to be the first Android phone I’ve owned that I haven’t needed to reflash or root. Flushed with that success (and, for the first time in a while, flush also with some ready cash), I bagged a tablet too. Nexus 7, Jelly Bean again, it’s basically like a big version of the phone except that it doesn’t make phone calls.

Syndicated 2012-10-08 18:30:47 from diary at Telent Netowrks

Debian on the Samsung Series 9 NP900X3B

There are other guides to getting Linux going on the Samsung Series 9 NP900X3B, but both that I’ve found are for Fedora. Mostly it’s the same in Debian, but here are some of the things I’ve spotted.

Install media

Recent versions of Debian install media images are created as hybrid ISO images, which means you can download them and dd them directly to a USB stick. This is what I did, with the Squeeze netinst image. The computer’s BIOS settings needed changing to look at USB before the internal SSD, but that’s not hard. I deselected all packages which resulted in a very minimal basic install, then added xfce4 and a few essential utilities using apt-get

Networking

The wired network works out of the box.

The wireless networking is courtesy of an Intel 6230 adapter “Intel Corporation Centrino Advanced-N 6230 [8086:0091] (rev 34)” (apparently this also does Bluetooth, but I haven’t tried that yet). This is not supported in Squeeze’s default kernel, but is available in Wheezy. After much swearing at backports I decided to do the apt-get upgrade dance

Touchpad

Touchpad handling worked in Squeeze and partially broke when I upgraded to get working wifi. Pointer movement worked fine, but tapping (for the uninitiated, “tapping” on the touchpad is simulating button presses by briefly touching the pressure-sensitive area instead of the hardware buttons below it) didn’t. On this system tapping is infinitely preferable to the hardware buttons, because it appears impossible to move the pointer while one of the hardware buttons is pressed – this makes window placement pretty tricky. The fix here is

synclient TapButton1=1 
synclient TapButton2=2 
synclient TapButton2=3 
synclient PalmDetect=1

which means you can use one finger to simulate button 1, two fingers simultaneously (note: not double-clicking, as I foolishly initially thought) to simulate button 2, and three for button 3. It also turns on palm detection, so that accidentally brushing the pad as you type won’t send your cursor off into the wild blue yonder.

This affect the current session only. To make it permanent you need to edit files: copy /usr/share/X11/xorg.conf.d/50-synaptics.conf to /etc/X11/xorg.conf.d and add the lines

        Option "TapButton1" "1"
        Option "TapButton2" "2"
        Option "TapButton3" "3"
        Option "PalmDetect" "1"

in the first Section "InputClass" stanza

Suspend and hibernate

I had the same problem with suspend as John Teslade : it appears not to resume but in fact it works perfectly except for the display backlight. However, I had it harder because my Fn-F2 and Fn-F3 keys didn’t do anything. After determining with acpi_listen that Linux is listening to those keys (they send ACPI events video/brightnessdown and video/brightnessup respectively) and is capable of controlling the brightness (try e.g. xrandr --output eDP1 --set BACKLIGHT 1) I decided this must clearly be a 90% solved problem and that the missing link was probably somewhere in the Debian package archive. It was, it was xfce4-power-manager

After that, suspend and hibernate are both usable.

Battery life

Pretty poor right now (looks like about 3 hours), but I’ve just installed laptop-mode-tools which has turned most of the PowerTOP tunables from “Bad” to “Good”, so I will be disappointed if that doesn’t make a significant difference. We’ll see …

Syndicated 2012-05-28 10:40:28 from diary at Telent Netowrks

Changing sort order with ActiveRecord find_in_batches

The subject is Googlebait pure and simple, because the short version is that you can’t. The very slightly longer and significantly more defensible version is that you may be able to but they don’t want you to apparently because you might get the wrong answer if you mutate the data as you’re traversing it (and because it’s fast in MySql).

Personally I think the answer there is Well Don’t Do That Then (and who cares about MySql) but that’s just my opinion. If you want to order by, say, created_at descending, and perhaps you want to paginate the results, the only sensible conclusion to draw is that find_in_batches is just not intended for this use.

But it’s not an unreasonable use. So I wrote ar-as-batches which lets you do

Users.where(country_id: 44).order(:joined_at).offset(200).as_batches do |user|
  user.party_all_night!
end

and it all works as you’d expect. I should of course caution you that

  • you might get the wrong answer if you mutate the data as you’re traversing it (so Don’t Do That Then), and that
  • ordering by something other than id ascending may be slower in MySql.

I don’t know whether to be proud or ashamed of the tests , which check the generated queries by assigning a StringIO logger to ActiveRecord::Base.logger and then matching regexps in it after each test runs. There ought to be a better way. Perhaps there is a better way. Don’t know what though.

Syndicated 2012-05-04 06:29:34 from diary at Telent Netowrks

Listening to a Polar Bluetooth HRM in Linux

My new toy, as of last Friday, is a Polar WearLink®+ transmitter with Bluetooth® because I wanted to track my heart rate from Android. Absent some initial glitches which turned out to be due to the battery it was shipped with having almost no charge left, it works pretty well with the open source Google My Tracks application.

But, but. A significant part of my exercise regime consists of riding a stationary bicycle until I feel sick. I do this in the same room as my computer: not only are GPS traces rather uninformative for this activity, but getting satellite coverage in the first place is tricky while indoors. So I thought it would be potentially useful and at least slightly interesting to work out how to access it directly from my desktop.

My first port of call was the source code for My Tracks. Digging into src/com/google/android/apps/mytracks/services/sensors/PolarMessageParser.java we find a helpful comment revealing that, notwithstanding Polar’s ridiculous stance on giving out development info (they don’t, is the summary) the Wearlink packet format is actually quite simple.

 *  Polar Bluetooth Wearlink packet example;
 *   Hdr Len Chk Seq Status HeartRate RRInterval_16-bits
 *    FE  08  F7  06   F1      48          03 64
 *   where; 
 *      Hdr always = 254 (0xFE), 
 *      Chk = 255 - Len
 *      Seq range 0 to 15
 *      Status = Upper nibble may be battery voltage
 *               bit 0 is Beat Detection flag.

While we’re looking at Android for clues, we also find the very useful information in the API docs for BluetoothSocket that “The most common type of Bluetooth socket is RFCOMM, which is the type supported by the Android APIs. RFCOMM is a connection-oriented, streaming transport over Bluetooth. It is also known as the Serial Port Profile (SPP)”. So, all we need to do is figure out how to do the same in Linux

Doing anything with Bluetooth in Linux inevitably turns into an exercise in yak epilation, especially for the kind of retrocomputing grouch (that’s me) who doesn’t have a full GNOME or KDE desktop with all the D buses and applets and stuff that come with it. In this case, I found that XFCE and the Debian blueman package were sufficient to get my bluetooth dongle registered, and to find and pair with the HRM. It also included a natty wizard thing which claimed to be able to create an rfcomm connection in /dev/rfcomm0. I say “claimed” not because it didn’t – it did, so … – but because for no readily apparent reason I could never get more than a single packet from this device without disconnecting, unpairing and repairing. Perhaps there was weird flow control stuff going on or perhaps it was something else, I don’t know, but in any case this is not ideal at 180bpm.

So, time for an alternative approach: thanks to Albert Huang, we find that apparently you can work with rfcomm sockets using actual, y’know, sockets . The rfcomm-client.c example on that we page worked perfectly, modulo the obvious point that sending data to a heart rate monitor strap is a peculiarly pointless endeavour, but really we want to write our code in Ruby not in C. This turns out to be easier than we might expect. Ruby’s socket library wraps the C socket interface sufficently closely that we can use pack to forge sockaddr structures for any protocol the kernel supports, if we know the layout in memory and the values of the constants.

How do we find “the layout in memory and the values of the constants”? With gdb. First we start it

:; gdb rfcomm-client
[...]
(gdb) break 21
Breakpoint 1 at 0x804865e: file rfcomm-client.c, line 21.
(gdb) run
Starting program: /home/dan/rfcomm-client

Breakpoint 1, main (argc=1, argv=0xbffff954) at rfcomm-client.c:22
22          status = connect(s, (struct sockaddr *)&addr, sizeof(addr));

then we check the values of the things

(gdb) print sizeof addr
$2 = 10
(gdb) print addr.rc_family 
$3 = 31
(gdb) p/x addr.rc_bdaddr
$4 = {b = {0xab, 0x89, 0x67, 0x45, 0x23, 0x1}}

then we look at the offsets

(gdb) p/x &addr
$5 = 0xbffff88e
(gdb) p/x &(addr.rc_family)
$6 = 0xbffff88e
(gdb) p/x &(addr.rc_bdaddr)
$7 = 0xbffff890
(gdb) p/x &(addr.rc_channel)
$8 = 0xbffff896

So, overall length 10, rc_family is at offset 0, rc_bdaddr at 2, and rc_channel at 8. And the undocumented (as far as I can see) str2ba function results in the octets of the bluetooth address going right-to-left into memory locations, so that should be easy to replicate in Ruby.

  def connect_bt address_str,channel=1
    bytes=address_str.split(/:/).map {|x| x.to_i(16) }
    s=Socket.new(AF_BLUETOOTH, :STREAM, BTPROTO_RFCOMM)
    sockaddr=[AF_BLUETOOTH,0, *bytes.reverse, channel,0 ].pack("C*")
    s.connect(sockaddr)    
    s
  end

The only thing left to do is the actual decoding. Considerations here are that we need to deal with short reads and that the start of a packet may not be at the start of the buffer we get – so we keep reading buffers and catenating them until decode says it’s found a packet, then we start again from where decode says the end of the packet should be. Because this logic is slightly complicated we wrap it in an Enumerator so that our caller gets one packet only each and every time they call Enumerator#next

The complete example code is at https://gist.github.com/2500413 and the licence is “do what you like with it”. What I will like to do with it is (1) log the data, (2) put up a window in the middle of the display showing my instantaneous heart rate and zone so that I know if I’m trying, (3) later, do some interesting graphing and analysis stuff. But your mileage may vary.

Syndicated 2012-05-03 21:10:29 from diary at Telent Netowrks

Sharpening the sawfish

My son is two weeks old today. I don’t usually go a bundle on putting personal info on the public web – I keep that for Facebook, where they at least pretend to keep it private for me - but I mention this to explain why I’m using my laptop a lot more than my desktop lately.

The problem with my laptop is the mouse pointer. It’s one of those pointing stick devices technically known (apparently) as an isometric joystick and more commonly known as a nipple , and when the room is warm the little rubber cap gets slippery very quickly. So I decided to invest a little time in a few keyboard shortcuts.

As an Emacs user I know I’m supposed to like tiling window managers, but I don’t. My editor windows are windows onto text files that may be any size and shape but in which it’s a fairly safe bet (see “locality of reference”) that the spot I want to edit next is usually spatially close to the spot I’m currently looking at. The other ‘windows’ on my screen are things like web browsers and GUI programs where there’s no such guarantee, and the only way to make them work is to allow them to take the size and shape that their authors wanted them to have. So after a brief experiment with awesome I punted it and went looking for a programable window manager that was designed for overlapping windows.

And ended up back with Sawfish, which I used to use back when it was fashionable. Sawfish customization is a two-phase process: first you write commands in Lisp, then you use the sawfish-ui program to assign them to keystrokes. A bit like Emacs, really, and perhaps not surprisingly.

First I needed some shortcuts to focus particular windows (Emacs, Firefox, xterms). Happily, someone has done the work for this already: I just had to download the Gimme script and set up bindings for it

Then I needed something to chuck windows around the screen. The requirement is pretty simple here: every window on my screen is aligned against an edge, so I just need commands to pan a window to each edge. Here is the finished script in which the points I would like to draw attention to are

  • I use focus-follows-mouse mode, or whatever it’s called these days. This means that if I move a window under the pointer I need to move the pointer too otherwise it will go out of focus. The warp-cursor-to-window function does this: I needed to calculate the pointer position relative to window, which for some reason isn’t a builtin.
  • window-frame-dimensions is window-dimensions plus the decorations. We need these dimenstions for throwing windows rightwards or downwards, otherwise they end up slightly offscreen.
  • define-command is the magic that makes our new functions show up in the sawfish-ui dialog. The "%f" sigil means to pass the current window into the function.

And that’s about it. Put the file somewhere that sawfish will find it – for me, ~/.sawfish/lisp seems to be a good place – add the lines

(require 'gimme)
(setq warp-to-window-enabled t)
(require 'throw-window)

to .sawfishrc, and then set up your keys in sawfish-ui. I assigned them to Windows-key shortcuts: at last, I have a use for the Windows key.

If you hadn’t spotted in amongst all that, I have githubbed my dotfiles. More for my convenience than for your edification, but feel free to rummage. If you are one of the three other remaining XTerm users, have a look at the XTerm*VT100*translations in my .Xdefaults - I stole that “press Shift+RET to background the command” trick from Malcolm Beattie nearly twenty years ago and have been using it ever since.

Syndicated 2012-02-22 20:25:20 from diary at Telent Netowrks

ANN Twitling: a Twitter link digest tool

Problem: I can’t keep up with the Internet

I often check Twitter on my phone. When I see tweets with links in them I tend to skip over them intending to return later when I’m on a computer with a full-size screen, and then forget about them either because I find something else to look at or I can’t be bothered with scrolling all the way down again. And looking through old tweets is nearly as bad on the full-size twitter web site as it is in a mobile client.

Proposed solution: I need a computer program to read the Internet for me

Thus, Twitling: a small script consisting of Ruby and Sinatra and OmniAuth and the Twitter gem and Typhoeus to grab links in parallel, the function of which is to read one’s timeline and display the resolved URL, the title and an excerpt from the text of each link that was posted. Source code is on Github.

I haven’t really used it myself yet in anger: the first thing I notice while testing it is that there are a whole lot more links in my feed than I thought there were, and the original plan to produce a 24 hour digest might become very unwieldy.

Possible further development ideas include

  • speed it up, by prefetching, better caching, or fetching the links asynchronously and client-side
  • an “older” link at the bottom of the page
  • Atom/RSS output so it gets fed to me every so often and I don’t have to remember to check it
  • email output (for the same reason)
  • some css gradient fills just to make it look modern (hey, I already used text-shadow, what do you want, round button borders?)
  • your suggestion here: email dan@telent.net or open an issue on Github. Bug reports too.

Try not to break it, please.

Syndicated 2012-02-01 12:54:57 from diary at Telent Netowrks

159 older entries...

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!