* xcb-util-0.3.3 (2009-01-31)
* libev
* flex and bison
+ * yajl (the IPC interface uses JSON to serialize data)
* asciidoc >= 8.3.0 for docs/hacking-howto
* asciidoc, xmlto, docbook-xml for man/i3.man
* Xlib, the one that comes with your X-Server
http://libev.schmorp.de/
http://flex.sourceforge.net/
http://www.gnu.org/software/bison/
+http://lloyd.github.com/yajl/
http://i3.zekjur.net/i3lock/
http://tools.suckless.org/dmenu
LDFLAGS += -lxcb-xinerama
LDFLAGS += -lxcb-randr
LDFLAGS += -lxcb
+LDFLAGS += -lyajl
LDFLAGS += -lX11
LDFLAGS += -lev
LDFLAGS += -L/usr/local/lib -L/usr/pkg/lib
Priority: extra
Maintainer: Michael Stapelberg <michael@stapelberg.de>
DM-Upload-Allowed: yes
-Build-Depends: debhelper (>= 5), libx11-dev, libxcb-aux0-dev (>= 0.3.3), libxcb-keysyms1-dev, libxcb-xinerama0-dev (>= 1.1), libxcb-randr0-dev, libxcb-event1-dev (>= 0.3.3), libxcb-property1-dev (>= 0.3.3), libxcb-atom1-dev (>= 0.3.3), libxcb-icccm1-dev (>= 0.3.3), asciidoc (>= 8.4.4), xmlto, docbook-xml, pkg-config, libev-dev, flex, bison
+Build-Depends: debhelper (>= 5), libx11-dev, libxcb-aux0-dev (>= 0.3.3), libxcb-keysyms1-dev, libxcb-xinerama0-dev (>= 1.1), libxcb-randr0-dev, libxcb-event1-dev (>= 0.3.3), libxcb-property1-dev (>= 0.3.3), libxcb-atom1-dev (>= 0.3.3), libxcb-icccm1-dev (>= 0.3.3), asciidoc (>= 8.4.4), xmlto, docbook-xml, pkg-config, libev-dev, flex, bison, libyajl-dev
Standards-Version: 3.8.3
Homepage: http://i3.zekjur.net/
/** Number of this workspace, starting from 0 */
int num;
+ /** Name of the workspace (in UTF-8) */
+ char *utf8_name;
+
/** Name of the workspace (in UCS-2) */
char *name;
*
* i3 - an improved dynamic tiling window manager
*
- * © 2009 Michael Stapelberg and contributors
+ * © 2009-2010 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
#ifndef _I3_IPC_H
#define _I3_IPC_H
+/*
+ * Messages from clients to i3
+ *
+ */
+
/** Never change this, only on major IPC breakage (don’t do that) */
#define I3_IPC_MAGIC "i3-ipc"
/** The payload of the message will be interpreted as a command */
-#define I3_IPC_MESSAGE_TYPE_COMMAND 0
+#define I3_IPC_MESSAGE_TYPE_COMMAND 0
+
+/** Requests the current workspaces from i3 */
+#define I3_IPC_MESSAGE_TYPE_GET_WORKSPACES 1
+
+/*
+ * Messages from i3 to clients
+ *
+ */
+
+/** Workspaces reply type */
+#define I3_IPC_REPLY_TYPE_WORKSPACES 1
#endif
*
* i3 - an improved dynamic tiling window manager
*
- * © 2009 Michael Stapelberg and contributors
+ * © 2009-2010 Michael Stapelberg and contributors
*
* See file LICENSE for license information.
*
#include <stdint.h>
#include <stdio.h>
#include <ev.h>
+#include <yajl/yajl_gen.h>
#include "queue.h"
#include "i3/ipc.h"
#include "util.h"
#include "commands.h"
#include "log.h"
+#include "table.h"
+
+/* Shorter names for all those yajl_gen_* functions */
+#define y(x, ...) yajl_gen_ ## x (gen, ##__VA_ARGS__)
+#define ystr(str) yajl_gen_string(gen, (unsigned char*)str, strlen(str))
typedef struct ipc_client {
int fd;
}
#endif
+static void ipc_send_message(int fd, const unsigned char *payload,
+ int message_type, int message_size) {
+ int buffer_size = strlen("i3-ipc") + sizeof(uint32_t) +
+ sizeof(uint32_t) + message_size;
+ char msg[buffer_size];
+ char *walk = msg;
+
+ strcpy(walk, "i3-ipc");
+ walk += strlen("i3-ipc");
+ memcpy(walk, &message_size, sizeof(uint32_t));
+ walk += sizeof(uint32_t);
+ memcpy(walk, &message_type, sizeof(uint32_t));
+ walk += sizeof(uint32_t);
+ memcpy(walk, payload, message_size);
+
+ int sent_bytes = 0;
+ int bytes_to_go = buffer_size;
+ while (sent_bytes < bytes_to_go) {
+ int n = write(fd, msg + sent_bytes, bytes_to_go);
+ if (n == -1)
+ err(EXIT_FAILURE, "write() failed");
+
+ sent_bytes += n;
+ bytes_to_go -= n;
+ }
+}
+
+/*
+ * Formats the reply message for a GET_WORKSPACES request and sends it to the
+ * client
+ *
+ */
+static void ipc_send_workspaces(int fd) {
+ Workspace *ws;
+
+ yajl_gen gen = yajl_gen_alloc(NULL, NULL);
+ y(array_open);
+
+ TAILQ_FOREACH(ws, workspaces, workspaces) {
+ if (ws->output == NULL)
+ continue;
+
+ y(map_open);
+ ystr("num");
+ y(integer, ws->num);
+
+ ystr("name");
+ ystr(ws->utf8_name);
+
+ ystr("rect");
+ y(map_open);
+ ystr("x");
+ y(integer, ws->rect.x);
+ ystr("y");
+ y(integer, ws->rect.y);
+ ystr("width");
+ y(integer, ws->rect.width);
+ ystr("height");
+ y(integer, ws->rect.height);
+ y(map_close);
+
+ ystr("output");
+ ystr(ws->output->name);
+
+ y(map_close);
+ }
+
+ y(array_close);
+
+ const unsigned char *payload;
+ unsigned int length;
+ y(get_buf, &payload, &length);
+
+ ipc_send_message(fd, payload, I3_IPC_REPLY_TYPE_WORKSPACES, length);
+ y(free);
+}
+
/*
* Decides what to do with the received message.
*
* message_type is the type of the message as the sender specified it.
*
*/
-static void ipc_handle_message(uint8_t *message, int size,
- uint32_t message_size, uint32_t message_type) {
+static void ipc_handle_message(int fd, uint8_t *message, int size,
+ uint32_t message_size, uint32_t message_type) {
DLOG("handling message of size %d\n", size);
DLOG("sender specified size %d\n", message_size);
DLOG("sender specified type %d\n", message_type);
break;
}
+ case I3_IPC_MESSAGE_TYPE_GET_WORKSPACES:
+ ipc_send_workspaces(fd);
+ break;
default:
DLOG("unhandled ipc message\n");
break;
message += sizeof(uint32_t);
n -= sizeof(uint32_t);
- ipc_handle_message(message, n, message_size, message_type);
+ ipc_handle_message(w->fd, message, n, message_size, message_type);
n -= message_size;
message += message_size;
}
errx(1, "asprintf() failed");
FREE(ws->name);
+ FREE(ws->utf8_name);
ws->name = convert_utf8_to_ucs2(label, &(ws->name_len));
if (config.font != NULL)
ws->text_width = predict_text_width(global_conn, config.font, ws->name, ws->name_len);
else ws->text_width = 0;
-
- free(label);
+ ws->utf8_name = label;
}
/*
test:
- PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(1)" t/*.t
+ PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(1)" t/15*.t
all: test
--- /dev/null
+#!perl
+# vim:ts=4:sw=4:expandtab
+
+use Test::More tests => 8;
+use Test::Exception;
+use Data::Dumper;
+use JSON::XS;
+use List::MoreUtils qw(all);
+use FindBin;
+use lib "$FindBin::Bin/lib";
+use i3test;
+
+BEGIN {
+ use_ok('IO::Socket::UNIX') or BAIL_OUT('Cannot load IO::Socket::UNIX');
+ use_ok('X11::XCB::Connection') or BAIL_OUT('Cannot load X11::XCB::Connection');
+}
+
+my $sock = IO::Socket::UNIX->new(Peer => '/tmp/i3-ipc.sock');
+isa_ok($sock, 'IO::Socket::UNIX');
+
+####################
+# Request workspaces
+####################
+
+# message type 1 is GET_WORKSPACES
+my $message = "i3-ipc" . pack("LL", 0, 1);
+$sock->write($message);
+
+#######################################
+# Test the reply format for correctness
+#######################################
+
+# The following lines duplicate functionality from recv_ipc_command
+# to have it included in the test-suite.
+my $buffer;
+$sock->read($buffer, length($message));
+is(substr($buffer, 0, length("i3-ipc")), "i3-ipc", "ipc message received");
+my ($len, $type) = unpack("LL", substr($buffer, 6));
+is($type, 1, "correct reply type");
+
+# read the payload
+$sock->read($buffer, $len);
+my $workspaces;
+
+#########################
+# Actually test the reply
+#########################
+
+lives_ok { $workspaces = decode_json($buffer) } 'JSON could be decoded';
+
+ok(@{$workspaces} > 0, "More than zero workspaces found");
+
+my $name_exists = all { defined($_->{name}) } @{$workspaces};
+ok($name_exists, "All workspaces have a name");
+
+diag( "Testing i3, Perl $], $^X" );
return $message;
}
+sub recv_ipc_command {
+ my ($sock, $expected) = @_;
+
+ my $buffer;
+ # header is 14 bytes ("i3-ipc" + 32 bit + 32 bit)
+ $sock->read($buffer, 14);
+ return undef unless substr($buffer, 0, length("i3-ipc")) eq "i3-ipc";
+
+ my ($len, $type) = unpack("LL", substr($buffer, 6));
+
+ return undef unless $type == $expected;
+
+ # read the payload
+ $sock->read($buffer, $len);
+
+ decode_json($buffer)
+}
+
1