inter-process communication with i3, the improved tiling window manager using node.js' asynchronous programming style.
i3, a tiling window manager, uses a simple message layout for its ipc interface:
"i3-ipc" <length> <type> <payload>
The response message is in the same format (see i3/docs/ipc for more info) and the payload is always some JSON:
{ "success": true }
This makes the format perfectly for use within a javascript environment.
node-i3 tries to build a simple API for asynchronous usage of this ipc.
Because I can?! :P
I really like the way node.js works (async!), I really like javascript and I needed something to work on; so I created this as a little fun project.
The code may not be perfect as I'm not a real pro in javascript and don't know all the idioms, but I like to learn, so fork and fix if you find something odd.
Do whatever you want with it. For example it's possible to build a web interface to control i3 (as soon as I switched to the tree branch, I may improve node-i3 due to the improved ipc of i3 itself).
I created a small web interface using Socket.IO (and Socket.IO-node) to switch workspaces using my smartphone: i3 web control poc.
The script is included in the examples dir.
All you need is node.js. node-i3 is tested on v0.3.6.
node-i3 is finally packaged and available via npm
npm install node-i3
But you can clone the repo and start using the code this way:
git clone https://github.com/badboy/node-i3.git
A quick example how to use node-i3:
var i3 = require('lib/node-i3');
var c = i3.connect('/home/badboy/.i3/ipc.sock');
c.on('connect', function() {
console.log('connected to i3 ipc');
});
// The client queues up any message, so just fire them off.
c.command('3'); // switch to workspace 3
c.on('data', function(data) {
console.log(data);
});
See examples/ for more.
For now the basic functionality is implemented. But keep in mind that the whole lib is in alpha for now and the API may change any time.
For a documentation of the available ipc commands for i3 see i3/docs/ipc.
Connects to the given unix domain socket and adds default events listeners. Some of them are proxied through, others are handled first.
Everything works on the returned object and all events are fired to it.
All of the following events are fired to the object returned by i3.connect
function() {}
node-i3 proxies the default socket event connect
, but makes sure to send any queued message first.
function() {}
This events are proxied without any internal handling.
function(data) {}
The data
event is fired, when node-i3 receives some input (which is not an event
). The input is then parsed and emitted to the assigned callback.
The function's argument is an object like this:
{ type: 0, length: 16, data: object }
where the intern data
is the JSON-parsed response.
type
is one of i3.TYPES
(as defined by i3):
{
COMMAND : 0,
GET_WORKSPACES : 1,
SUBSCRIBE : 2,
GET_OUTPUTS : 3,
}
If an error occured, data
will contain an error string describing what went wrong:
{ error: 'wrong magic code' }
function(data) {}
The event
event is fired whenever i3 responds to a former subscribed event (Whooo, a lot of "event" in here).
The data
object is the same as in the data
event, plus data.event
is set to true (and a lot of "data" of course).
Just executes the socket's end
-method, so it is defined as:
Half-closes the socket. I.E., it sends a FIN packet. It is possible the server will still send some data. If data is specified, it is equivalent to calling socket.write(data, encoding) followed by socket.end(). (from node.js docs)
Sends a command message through the socket with payload
.
payload
should already be json-encoded (this may change soon).
Gets the current workspaces. The reply received later will be a list of workspaces.
Gets the current outputs. The reply received later will be a list of outputs.
Subscribes your connection to certain events. See the original docu for a description of this message and the concept of events.
list
should be an Javascript array and will be formatted to JSON internally (this is why the command
API may change).
As the i3 ipc is using a synchronous connection, node-i3 makes sure to send new messages after the previous message got its response.
Each of the 4 message-sending methods returns the node-i3 object itself. This way you can chain the methods:
# c is the node-i3 object
c.command("3").command("h").command("m1").command("exec xterm");
Keep in mind that it may take some time for i3 to actually switch the workspace and grabbing the focus on the new window. Because of this the above line might not work as expected (I did not fully test this method chaining and may change this to a proper working solution some time).
If you really want to make sure that commands are executed in the proper order and taking the effect you want, fire the messages after receiving the previous response in the data
callback. This should work more reliable for now.
Once you've made your great commits:
- Fork node-i3.
- Create a topic branch -
git checkout -b my_branch
- Push to your branch -
git push origin my_branch
- Send me a pull request or create an Issue.
- That's it!
Copyright (c) 2011 Jan-Erik Rediger. See LICENSE for details.