Merge pull request #2861 from stapelberg/ipcconfig
Introduce the GET_CONFIG IPC request
This commit is contained in:
commit
e6682f862b
|
@ -1,5 +1,9 @@
|
||||||
Revision history for AnyEvent-I3
|
Revision history for AnyEvent-I3
|
||||||
|
|
||||||
|
0.18 2017-08-19
|
||||||
|
|
||||||
|
* support the GET_CONFIG command
|
||||||
|
|
||||||
0.17 2017-04-09
|
0.17 2017-04-09
|
||||||
|
|
||||||
* support the shutdown event
|
* support the shutdown event
|
||||||
|
|
|
@ -16,11 +16,11 @@ AnyEvent::I3 - communicate with the i3 window manager
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
our $VERSION = '0.17';
|
our $VERSION = '0.18';
|
||||||
|
|
||||||
=head1 VERSION
|
=head1 VERSION
|
||||||
|
|
||||||
Version 0.17
|
Version 0.18
|
||||||
|
|
||||||
=head1 SYNOPSIS
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
@ -95,10 +95,13 @@ use constant TYPE_GET_TREE => 4;
|
||||||
use constant TYPE_GET_MARKS => 5;
|
use constant TYPE_GET_MARKS => 5;
|
||||||
use constant TYPE_GET_BAR_CONFIG => 6;
|
use constant TYPE_GET_BAR_CONFIG => 6;
|
||||||
use constant TYPE_GET_VERSION => 7;
|
use constant TYPE_GET_VERSION => 7;
|
||||||
|
use constant TYPE_GET_BINDING_MODES => 8;
|
||||||
|
use constant TYPE_GET_CONFIG => 9;
|
||||||
|
|
||||||
our %EXPORT_TAGS = ( 'all' => [
|
our %EXPORT_TAGS = ( 'all' => [
|
||||||
qw(i3 TYPE_COMMAND TYPE_GET_WORKSPACES TYPE_SUBSCRIBE TYPE_GET_OUTPUTS
|
qw(i3 TYPE_COMMAND TYPE_GET_WORKSPACES TYPE_SUBSCRIBE TYPE_GET_OUTPUTS
|
||||||
TYPE_GET_TREE TYPE_GET_MARKS TYPE_GET_BAR_CONFIG TYPE_GET_VERSION)
|
TYPE_GET_TREE TYPE_GET_MARKS TYPE_GET_BAR_CONFIG TYPE_GET_VERSION
|
||||||
|
TYPE_GET_BINDING_MODES TYPE_GET_CONFIG)
|
||||||
] );
|
] );
|
||||||
|
|
||||||
our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } );
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{all} } );
|
||||||
|
@ -501,6 +504,20 @@ sub get_version {
|
||||||
return $cv;
|
return $cv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=head2 get_config
|
||||||
|
|
||||||
|
Gets the raw last read config from i3. Requires i3 >= 4.14
|
||||||
|
|
||||||
|
=cut
|
||||||
|
sub get_config {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
$self->_ensure_connection;
|
||||||
|
|
||||||
|
$self->message(TYPE_GET_CONFIG);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
=head2 command($content)
|
=head2 command($content)
|
||||||
|
|
||||||
Makes i3 execute the given command
|
Makes i3 execute the given command
|
||||||
|
|
|
@ -580,7 +580,7 @@ i3-config-parser.stamp: parser/$(dirstamp) generate-command-parser.pl parser-spe
|
||||||
# AnyEvent-I3 build process
|
# AnyEvent-I3 build process
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
anyevent-i3.stamp: generate-command-parser.pl parser-specs/config.spec
|
anyevent-i3.stamp: AnyEvent-I3/lib/AnyEvent/I3.pm
|
||||||
$(AM_V_BUILD) (cd $(top_srcdir)/AnyEvent-I3 && perl Makefile.PL && make)
|
$(AM_V_BUILD) (cd $(top_srcdir)/AnyEvent-I3 && perl Makefile.PL && make)
|
||||||
$(AM_V_at) touch $@
|
$(AM_V_at) touch $@
|
||||||
|
|
||||||
|
|
|
@ -119,6 +119,43 @@ static yajl_callbacks reply_callbacks = {
|
||||||
.yajl_end_map = reply_end_map_cb,
|
.yajl_end_map = reply_end_map_cb,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Config reply callbacks
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
static char *config_last_key = NULL;
|
||||||
|
|
||||||
|
static int config_string_cb(void *params, const unsigned char *val, size_t len) {
|
||||||
|
char *str = scalloc(len + 1, 1);
|
||||||
|
strncpy(str, (const char *)val, len);
|
||||||
|
if (strcmp(config_last_key, "config") == 0) {
|
||||||
|
fprintf(stdout, "%s", str);
|
||||||
|
}
|
||||||
|
free(str);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_start_map_cb(void *params) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_end_map_cb(void *params) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_map_key_cb(void *params, const unsigned char *keyVal, size_t keyLen) {
|
||||||
|
config_last_key = scalloc(keyLen + 1, 1);
|
||||||
|
strncpy(config_last_key, (const char *)keyVal, keyLen);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static yajl_callbacks config_callbacks = {
|
||||||
|
.yajl_string = config_string_cb,
|
||||||
|
.yajl_start_map = config_start_map_cb,
|
||||||
|
.yajl_map_key = config_map_key_cb,
|
||||||
|
.yajl_end_map = config_end_map_cb,
|
||||||
|
};
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
#if defined(__OpenBSD__)
|
#if defined(__OpenBSD__)
|
||||||
if (pledge("stdio rpath unix", NULL) == -1)
|
if (pledge("stdio rpath unix", NULL) == -1)
|
||||||
|
@ -150,25 +187,27 @@ int main(int argc, char *argv[]) {
|
||||||
free(socket_path);
|
free(socket_path);
|
||||||
socket_path = sstrdup(optarg);
|
socket_path = sstrdup(optarg);
|
||||||
} else if (o == 't') {
|
} else if (o == 't') {
|
||||||
if (strcasecmp(optarg, "command") == 0)
|
if (strcasecmp(optarg, "command") == 0) {
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_COMMAND;
|
message_type = I3_IPC_MESSAGE_TYPE_COMMAND;
|
||||||
else if (strcasecmp(optarg, "get_workspaces") == 0)
|
} else if (strcasecmp(optarg, "get_workspaces") == 0) {
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_WORKSPACES;
|
message_type = I3_IPC_MESSAGE_TYPE_GET_WORKSPACES;
|
||||||
else if (strcasecmp(optarg, "get_outputs") == 0)
|
} else if (strcasecmp(optarg, "get_outputs") == 0) {
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_OUTPUTS;
|
message_type = I3_IPC_MESSAGE_TYPE_GET_OUTPUTS;
|
||||||
else if (strcasecmp(optarg, "get_tree") == 0)
|
} else if (strcasecmp(optarg, "get_tree") == 0) {
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_TREE;
|
message_type = I3_IPC_MESSAGE_TYPE_GET_TREE;
|
||||||
else if (strcasecmp(optarg, "get_marks") == 0)
|
} else if (strcasecmp(optarg, "get_marks") == 0) {
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS;
|
message_type = I3_IPC_MESSAGE_TYPE_GET_MARKS;
|
||||||
else if (strcasecmp(optarg, "get_bar_config") == 0)
|
} else if (strcasecmp(optarg, "get_bar_config") == 0) {
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG;
|
message_type = I3_IPC_MESSAGE_TYPE_GET_BAR_CONFIG;
|
||||||
else if (strcasecmp(optarg, "get_binding_modes") == 0)
|
} else if (strcasecmp(optarg, "get_binding_modes") == 0) {
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_BINDING_MODES;
|
message_type = I3_IPC_MESSAGE_TYPE_GET_BINDING_MODES;
|
||||||
else if (strcasecmp(optarg, "get_version") == 0)
|
} else if (strcasecmp(optarg, "get_version") == 0) {
|
||||||
message_type = I3_IPC_MESSAGE_TYPE_GET_VERSION;
|
message_type = I3_IPC_MESSAGE_TYPE_GET_VERSION;
|
||||||
else {
|
} else if (strcasecmp(optarg, "get_config") == 0) {
|
||||||
|
message_type = I3_IPC_MESSAGE_TYPE_GET_CONFIG;
|
||||||
|
} else {
|
||||||
printf("Unknown message type\n");
|
printf("Unknown message type\n");
|
||||||
printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version\n");
|
printf("Known types: command, get_workspaces, get_outputs, get_tree, get_marks, get_bar_config, get_binding_modes, get_version, get_config\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
} else if (o == 'q') {
|
} else if (o == 'q') {
|
||||||
|
@ -241,7 +280,7 @@ int main(int argc, char *argv[]) {
|
||||||
errx(EXIT_FAILURE, "IPC: Received reply of type %d but expected %d", reply_type, message_type);
|
errx(EXIT_FAILURE, "IPC: Received reply of type %d but expected %d", reply_type, message_type);
|
||||||
/* For the reply of commands, have a look if that command was successful.
|
/* For the reply of commands, have a look if that command was successful.
|
||||||
* If not, nicely format the error message. */
|
* If not, nicely format the error message. */
|
||||||
if (reply_type == I3_IPC_MESSAGE_TYPE_COMMAND) {
|
if (reply_type == I3_IPC_REPLY_TYPE_COMMAND) {
|
||||||
yajl_handle handle = yajl_alloc(&reply_callbacks, NULL, NULL);
|
yajl_handle handle = yajl_alloc(&reply_callbacks, NULL, NULL);
|
||||||
yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
|
yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
|
||||||
yajl_free(handle);
|
yajl_free(handle);
|
||||||
|
@ -256,8 +295,24 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
/* NB: We still fall-through and print the reply, because even if one
|
/* NB: We still fall-through and print the reply, because even if one
|
||||||
* command failed, that doesn’t mean that all commands failed. */
|
* command failed, that doesn’t mean that all commands failed. */
|
||||||
|
} else if (reply_type == I3_IPC_REPLY_TYPE_CONFIG) {
|
||||||
|
yajl_handle handle = yajl_alloc(&config_callbacks, NULL, NULL);
|
||||||
|
yajl_status state = yajl_parse(handle, (const unsigned char *)reply, reply_length);
|
||||||
|
yajl_free(handle);
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case yajl_status_ok:
|
||||||
|
break;
|
||||||
|
case yajl_status_client_canceled:
|
||||||
|
case yajl_status_error:
|
||||||
|
errx(EXIT_FAILURE, "IPC: Could not parse JSON reply.");
|
||||||
|
}
|
||||||
|
|
||||||
|
goto exit;
|
||||||
}
|
}
|
||||||
printf("%.*s\n", reply_length, reply);
|
printf("%.*s\n", reply_length, reply);
|
||||||
|
|
||||||
|
exit:
|
||||||
free(reply);
|
free(reply);
|
||||||
|
|
||||||
close(sockfd);
|
close(sockfd);
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
typedef struct Config Config;
|
typedef struct Config Config;
|
||||||
typedef struct Barconfig Barconfig;
|
typedef struct Barconfig Barconfig;
|
||||||
extern char *current_configpath;
|
extern char *current_configpath;
|
||||||
|
extern char *current_config;
|
||||||
extern Config config;
|
extern Config config;
|
||||||
extern SLIST_HEAD(modes_head, Mode) modes;
|
extern SLIST_HEAD(modes_head, Mode) modes;
|
||||||
extern TAILQ_HEAD(barconfig_head, Barconfig) barconfigs;
|
extern TAILQ_HEAD(barconfig_head, Barconfig) barconfigs;
|
||||||
|
|
|
@ -54,6 +54,9 @@ typedef struct i3_ipc_header {
|
||||||
/** Request a list of configured binding modes. */
|
/** Request a list of configured binding modes. */
|
||||||
#define I3_IPC_MESSAGE_TYPE_GET_BINDING_MODES 8
|
#define I3_IPC_MESSAGE_TYPE_GET_BINDING_MODES 8
|
||||||
|
|
||||||
|
/** Request the raw last loaded i3 config. */
|
||||||
|
#define I3_IPC_MESSAGE_TYPE_GET_CONFIG 9
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Messages from i3 to clients
|
* Messages from i3 to clients
|
||||||
*
|
*
|
||||||
|
@ -67,6 +70,7 @@ typedef struct i3_ipc_header {
|
||||||
#define I3_IPC_REPLY_TYPE_BAR_CONFIG 6
|
#define I3_IPC_REPLY_TYPE_BAR_CONFIG 6
|
||||||
#define I3_IPC_REPLY_TYPE_VERSION 7
|
#define I3_IPC_REPLY_TYPE_VERSION 7
|
||||||
#define I3_IPC_REPLY_TYPE_BINDING_MODES 8
|
#define I3_IPC_REPLY_TYPE_BINDING_MODES 8
|
||||||
|
#define I3_IPC_REPLY_TYPE_CONFIG 9
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Events from i3 to clients. Events have the first bit set high.
|
* Events from i3 to clients. Events have the first bit set high.
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
|
||||||
char *current_configpath = NULL;
|
char *current_configpath = NULL;
|
||||||
|
char *current_config = NULL;
|
||||||
Config config;
|
Config config;
|
||||||
struct modes_head modes;
|
struct modes_head modes;
|
||||||
struct barconfig_head barconfigs = TAILQ_HEAD_INITIALIZER(barconfigs);
|
struct barconfig_head barconfigs = TAILQ_HEAD_INITIALIZER(barconfigs);
|
||||||
|
|
|
@ -898,6 +898,11 @@ bool parse_file(const char *f, bool use_nagbar) {
|
||||||
if ((fstr = fdopen(fd, "r")) == NULL)
|
if ((fstr = fdopen(fd, "r")) == NULL)
|
||||||
die("Could not fdopen: %s\n", strerror(errno));
|
die("Could not fdopen: %s\n", strerror(errno));
|
||||||
|
|
||||||
|
FREE(current_config);
|
||||||
|
current_config = scalloc(stbuf.st_size + 1, 1);
|
||||||
|
fread(current_config, 1, stbuf.st_size, fstr);
|
||||||
|
rewind(fstr);
|
||||||
|
|
||||||
while (!feof(fstr)) {
|
while (!feof(fstr)) {
|
||||||
if (!continuation)
|
if (!continuation)
|
||||||
continuation = buffer;
|
continuation = buffer;
|
||||||
|
|
24
src/ipc.c
24
src/ipc.c
|
@ -1087,9 +1087,30 @@ IPC_HANDLER(subscribe) {
|
||||||
ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
|
ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t *)reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the raw last loaded i3 configuration file contents.
|
||||||
|
*/
|
||||||
|
IPC_HANDLER(get_config) {
|
||||||
|
yajl_gen gen = ygenalloc();
|
||||||
|
|
||||||
|
y(map_open);
|
||||||
|
|
||||||
|
ystr("config");
|
||||||
|
ystr(current_config);
|
||||||
|
|
||||||
|
y(map_close);
|
||||||
|
|
||||||
|
const unsigned char *payload;
|
||||||
|
ylength length;
|
||||||
|
y(get_buf, &payload, &length);
|
||||||
|
|
||||||
|
ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_CONFIG, payload);
|
||||||
|
y(free);
|
||||||
|
}
|
||||||
|
|
||||||
/* The index of each callback function corresponds to the numeric
|
/* The index of each callback function corresponds to the numeric
|
||||||
* value of the message type (see include/i3/ipc.h) */
|
* value of the message type (see include/i3/ipc.h) */
|
||||||
handler_t handlers[9] = {
|
handler_t handlers[10] = {
|
||||||
handle_command,
|
handle_command,
|
||||||
handle_get_workspaces,
|
handle_get_workspaces,
|
||||||
handle_subscribe,
|
handle_subscribe,
|
||||||
|
@ -1099,6 +1120,7 @@ handler_t handlers[9] = {
|
||||||
handle_get_bar_config,
|
handle_get_bar_config,
|
||||||
handle_get_version,
|
handle_get_version,
|
||||||
handle_get_binding_modes,
|
handle_get_binding_modes,
|
||||||
|
handle_get_config,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
#!perl
|
||||||
|
# vim:ts=4:sw=4:expandtab
|
||||||
|
#
|
||||||
|
# Please read the following documents before working on tests:
|
||||||
|
# • http://build.i3wm.org/docs/testsuite.html
|
||||||
|
# (or docs/testsuite)
|
||||||
|
#
|
||||||
|
# • http://build.i3wm.org/docs/lib-i3test.html
|
||||||
|
# (alternatively: perldoc ./testcases/lib/i3test.pm)
|
||||||
|
#
|
||||||
|
# • http://build.i3wm.org/docs/ipc.html
|
||||||
|
# (or docs/ipc)
|
||||||
|
#
|
||||||
|
# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
|
||||||
|
# (unless you are already familiar with Perl)
|
||||||
|
#
|
||||||
|
# Verifies that the config file is returned raw via the IPC interface.
|
||||||
|
# Ticket: #2856
|
||||||
|
# Bug still in: 4.13-133-ge4da07e7
|
||||||
|
use i3test i3_autostart => 0;
|
||||||
|
use File::Temp qw(tempdir);
|
||||||
|
|
||||||
|
my $tmpdir = tempdir(CLEANUP => 1);
|
||||||
|
my $socketpath = $tmpdir . "/config.sock";
|
||||||
|
ok(! -e $socketpath, "$socketpath does not exist yet");
|
||||||
|
|
||||||
|
my $config = <<'EOT';
|
||||||
|
# i3 config file (v4)
|
||||||
|
font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
|
||||||
|
|
||||||
|
nop foo \
|
||||||
|
continued
|
||||||
|
|
||||||
|
set $var normal title
|
||||||
|
for_window [title="$vartest"] border none
|
||||||
|
EOT
|
||||||
|
|
||||||
|
$config .= "ipc-socket $socketpath";
|
||||||
|
|
||||||
|
my $pid = launch_with_config($config, dont_add_socket_path => 1, dont_create_temp_dir => 1);
|
||||||
|
get_socket_path(0);
|
||||||
|
my $i3 = i3(get_socket_path());
|
||||||
|
$i3->connect->recv;
|
||||||
|
|
||||||
|
my $cv = AE::cv;
|
||||||
|
my $timer = AE::timer 0.5, 0, sub { $cv->send(0); };
|
||||||
|
|
||||||
|
my $last_config = $i3->get_config()->recv;
|
||||||
|
chomp($last_config->{config});
|
||||||
|
is($last_config->{config}, $config,
|
||||||
|
'received config is not equal to written config');
|
||||||
|
|
||||||
|
exit_gracefully($pid);
|
||||||
|
|
||||||
|
done_testing;
|
Loading…
Reference in New Issue