ipc: implement GET_WORKSPACES message type
This is the foundation to use dzen2 or similar as a complete replacement for the internal workspaces bar. A testcase is included, more documentation about the IPC interface will follow.
This commit is contained in:
parent
952914c3c5
commit
9a9ba1b859
2
DEPENDS
2
DEPENDS
|
@ -7,6 +7,7 @@ In that case, please try using the versions mentioned below until a fix is provi
|
|||
* 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
|
||||
|
@ -24,6 +25,7 @@ http://xcb.freedesktop.org/dist/xcb-util-0.3.5.tar.bz2
|
|||
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
|
||||
|
|
|
@ -40,6 +40,7 @@ LDFLAGS += -lxcb-icccm
|
|||
LDFLAGS += -lxcb-xinerama
|
||||
LDFLAGS += -lxcb-randr
|
||||
LDFLAGS += -lxcb
|
||||
LDFLAGS += -lyajl
|
||||
LDFLAGS += -lX11
|
||||
LDFLAGS += -lev
|
||||
LDFLAGS += -L/usr/local/lib -L/usr/pkg/lib
|
||||
|
|
|
@ -3,7 +3,7 @@ Section: utils
|
|||
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/
|
||||
|
||||
|
|
|
@ -177,6 +177,9 @@ struct Workspace {
|
|||
/** 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;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* i3 - an improved dynamic tiling window manager
|
||||
*
|
||||
* © 2009 Michael Stapelberg and contributors
|
||||
* © 2009-2010 Michael Stapelberg and contributors
|
||||
*
|
||||
* See file LICENSE for license information.
|
||||
*
|
||||
|
@ -15,10 +15,26 @@
|
|||
#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
|
||||
|
||||
/** 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
|
||||
|
|
92
src/ipc.c
92
src/ipc.c
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* i3 - an improved dynamic tiling window manager
|
||||
*
|
||||
* © 2009 Michael Stapelberg and contributors
|
||||
* © 2009-2010 Michael Stapelberg and contributors
|
||||
*
|
||||
* See file LICENSE for license information.
|
||||
*
|
||||
|
@ -22,6 +22,7 @@
|
|||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <ev.h>
|
||||
#include <yajl/yajl_gen.h>
|
||||
|
||||
#include "queue.h"
|
||||
#include "i3/ipc.h"
|
||||
|
@ -29,6 +30,11 @@
|
|||
#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;
|
||||
|
@ -60,6 +66,83 @@ void broadcast(EV_P_ struct ev_timer *t, int revents) {
|
|||
}
|
||||
#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.
|
||||
*
|
||||
|
@ -70,7 +153,7 @@ void broadcast(EV_P_ struct ev_timer *t, int revents) {
|
|||
* message_type is the type of the message as the sender specified it.
|
||||
*
|
||||
*/
|
||||
static void ipc_handle_message(uint8_t *message, int size,
|
||||
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);
|
||||
|
@ -88,6 +171,9 @@ static void ipc_handle_message(uint8_t *message, int size,
|
|||
|
||||
break;
|
||||
}
|
||||
case I3_IPC_MESSAGE_TYPE_GET_WORKSPACES:
|
||||
ipc_send_workspaces(fd);
|
||||
break;
|
||||
default:
|
||||
DLOG("unhandled ipc message\n");
|
||||
break;
|
||||
|
@ -175,7 +261,7 @@ static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -84,13 +84,13 @@ void workspace_set_name(Workspace *ws, const char *name) {
|
|||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
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
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
#!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" );
|
|
@ -40,4 +40,22 @@ sub format_ipc_command {
|
|||
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
|
||||
|
|
Loading…
Reference in New Issue