2012-08-12 12:18:43 +02:00
|
|
|
|
#undef I3__FILE__
|
|
|
|
|
#define I3__FILE__ "ipc.c"
|
2009-07-28 20:51:29 +02:00
|
|
|
|
/*
|
2010-11-21 22:03:55 +01:00
|
|
|
|
* vim:ts=4:sw=4:expandtab
|
2009-07-28 20:51:29 +02:00
|
|
|
|
*
|
|
|
|
|
* i3 - an improved dynamic tiling window manager
|
2012-01-21 19:35:15 +01:00
|
|
|
|
* © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
|
2009-07-28 20:51:29 +02:00
|
|
|
|
*
|
2011-10-23 00:40:02 +02:00
|
|
|
|
* ipc.c: UNIX domain socket IPC (initialization, client handling, protocol).
|
2009-07-28 20:51:29 +02:00
|
|
|
|
*
|
|
|
|
|
*/
|
2011-10-23 00:40:02 +02:00
|
|
|
|
#include "all.h"
|
2012-11-03 12:17:28 +01:00
|
|
|
|
#include "yajl_utils.h"
|
2011-10-23 00:40:02 +02:00
|
|
|
|
|
2009-07-28 20:51:29 +02:00
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/un.h>
|
|
|
|
|
#include <fcntl.h>
|
2010-11-15 02:01:04 +01:00
|
|
|
|
#include <libgen.h>
|
2009-07-28 20:51:29 +02:00
|
|
|
|
#include <ev.h>
|
2010-03-11 15:58:39 +01:00
|
|
|
|
#include <yajl/yajl_gen.h>
|
2010-03-12 21:05:05 +01:00
|
|
|
|
#include <yajl/yajl_parse.h>
|
2009-07-28 20:51:29 +02:00
|
|
|
|
|
2011-03-20 15:34:34 +01:00
|
|
|
|
char *current_socketpath = NULL;
|
|
|
|
|
|
2009-07-28 20:51:29 +02:00
|
|
|
|
TAILQ_HEAD(ipc_client_head, ipc_client) all_clients = TAILQ_HEAD_INITIALIZER(all_clients);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Puts the given socket file descriptor into non-blocking mode or dies if
|
|
|
|
|
* setting O_NONBLOCK failed. Non-blocking sockets are a good idea for our
|
|
|
|
|
* IPC model because we should by no means block the window manager.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static void set_nonblock(int sockfd) {
|
2010-11-21 22:03:55 +01:00
|
|
|
|
int flags = fcntl(sockfd, F_GETFL, 0);
|
|
|
|
|
flags |= O_NONBLOCK;
|
|
|
|
|
if (fcntl(sockfd, F_SETFL, flags) < 0)
|
|
|
|
|
err(-1, "Could not set O_NONBLOCK");
|
2009-07-28 20:51:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
2010-11-15 02:01:04 +01:00
|
|
|
|
/*
|
|
|
|
|
* Emulates mkdir -p (creates any missing folders)
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static bool mkdirp(const char *path) {
|
2010-11-21 22:03:55 +01:00
|
|
|
|
if (mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == 0)
|
|
|
|
|
return true;
|
|
|
|
|
if (errno != ENOENT) {
|
|
|
|
|
ELOG("mkdir(%s) failed: %s\n", path, strerror(errno));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2012-09-21 16:47:43 +02:00
|
|
|
|
char *copy = sstrdup(path);
|
2010-11-21 22:03:55 +01:00
|
|
|
|
/* strip trailing slashes, if any */
|
|
|
|
|
while (copy[strlen(copy)-1] == '/')
|
|
|
|
|
copy[strlen(copy)-1] = '\0';
|
|
|
|
|
|
|
|
|
|
char *sep = strrchr(copy, '/');
|
2011-08-31 14:44:48 +02:00
|
|
|
|
if (sep == NULL) {
|
|
|
|
|
FREE(copy);
|
2010-11-21 22:03:55 +01:00
|
|
|
|
return false;
|
2011-08-31 14:44:48 +02:00
|
|
|
|
}
|
2010-11-21 22:03:55 +01:00
|
|
|
|
*sep = '\0';
|
|
|
|
|
bool result = false;
|
|
|
|
|
if (mkdirp(copy))
|
|
|
|
|
result = mkdirp(path);
|
|
|
|
|
free(copy);
|
|
|
|
|
|
|
|
|
|
return result;
|
2010-11-15 02:01:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-03-12 21:05:05 +01:00
|
|
|
|
/*
|
|
|
|
|
* Sends the specified event to all IPC clients which are currently connected
|
|
|
|
|
* and subscribed to this kind of event.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
void ipc_send_event(const char *event, uint32_t message_type, const char *payload) {
|
2010-11-21 22:03:55 +01:00
|
|
|
|
ipc_client *current;
|
|
|
|
|
TAILQ_FOREACH(current, &all_clients, clients) {
|
|
|
|
|
/* see if this client is interested in this event */
|
|
|
|
|
bool interested = false;
|
|
|
|
|
for (int i = 0; i < current->num_events; i++) {
|
|
|
|
|
if (strcasecmp(current->events[i], event) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
interested = true;
|
|
|
|
|
break;
|
2010-03-12 21:05:05 +01:00
|
|
|
|
}
|
2010-11-21 22:03:55 +01:00
|
|
|
|
if (!interested)
|
|
|
|
|
continue;
|
|
|
|
|
|
2011-10-02 19:33:10 +02:00
|
|
|
|
ipc_send_message(current->fd, strlen(payload), message_type, (const uint8_t*)payload);
|
2010-11-21 22:03:55 +01:00
|
|
|
|
}
|
2010-03-12 21:05:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-03-16 02:44:47 +01:00
|
|
|
|
/*
|
|
|
|
|
* Calls shutdown() on each socket and closes it. This function to be called
|
|
|
|
|
* when exiting or restarting only!
|
|
|
|
|
*
|
|
|
|
|
*/
|
2012-03-31 10:53:04 +02:00
|
|
|
|
void ipc_shutdown(void) {
|
2010-11-21 22:03:55 +01:00
|
|
|
|
ipc_client *current;
|
2011-11-07 22:34:39 +01:00
|
|
|
|
while (!TAILQ_EMPTY(&all_clients)) {
|
|
|
|
|
current = TAILQ_FIRST(&all_clients);
|
2010-11-21 22:03:55 +01:00
|
|
|
|
shutdown(current->fd, SHUT_RDWR);
|
|
|
|
|
close(current->fd);
|
2011-11-07 22:34:39 +01:00
|
|
|
|
TAILQ_REMOVE(&all_clients, current, clients);
|
|
|
|
|
free(current);
|
2010-11-21 22:03:55 +01:00
|
|
|
|
}
|
2010-03-16 02:44:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-03-12 21:05:05 +01:00
|
|
|
|
/*
|
|
|
|
|
* Executes the command and returns whether it could be successfully parsed
|
|
|
|
|
* or not (at the moment, always returns true).
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
IPC_HANDLER(command) {
|
2010-11-21 22:03:55 +01:00
|
|
|
|
/* To get a properly terminated buffer, we copy
|
|
|
|
|
* message_size bytes out of the buffer */
|
|
|
|
|
char *command = scalloc(message_size + 1);
|
|
|
|
|
strncpy(command, (const char*)message, message_size);
|
|
|
|
|
LOG("IPC: received: *%s*\n", command);
|
2012-02-07 23:38:21 +01:00
|
|
|
|
struct CommandResult *command_output = parse_command((const char*)command);
|
2010-11-21 22:03:55 +01:00
|
|
|
|
free(command);
|
|
|
|
|
|
2012-02-07 23:38:21 +01:00
|
|
|
|
if (command_output->needs_tree_render)
|
|
|
|
|
tree_render();
|
|
|
|
|
|
2012-05-02 22:01:50 +02:00
|
|
|
|
const unsigned char *reply;
|
2012-11-03 12:17:28 +01:00
|
|
|
|
ylength length;
|
2012-05-02 22:01:50 +02:00
|
|
|
|
yajl_gen_get_buf(command_output->json_gen, &reply, &length);
|
|
|
|
|
|
|
|
|
|
ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_COMMAND,
|
|
|
|
|
(const uint8_t*)reply);
|
|
|
|
|
|
|
|
|
|
yajl_gen_free(command_output->json_gen);
|
2010-03-12 21:05:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-11-28 17:20:29 +01:00
|
|
|
|
static void dump_rect(yajl_gen gen, const char *name, Rect r) {
|
|
|
|
|
ystr(name);
|
|
|
|
|
y(map_open);
|
|
|
|
|
ystr("x");
|
|
|
|
|
y(integer, r.x);
|
|
|
|
|
ystr("y");
|
|
|
|
|
y(integer, r.y);
|
|
|
|
|
ystr("width");
|
|
|
|
|
y(integer, r.width);
|
|
|
|
|
ystr("height");
|
|
|
|
|
y(integer, r.height);
|
|
|
|
|
y(map_close);
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-27 15:25:51 +01:00
|
|
|
|
void dump_node(yajl_gen gen, struct Con *con, bool inplace_restart) {
|
2010-11-21 22:03:55 +01:00
|
|
|
|
y(map_open);
|
|
|
|
|
ystr("id");
|
|
|
|
|
y(integer, (long int)con);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
ystr("type");
|
2013-12-14 14:50:44 +01:00
|
|
|
|
switch (con->type) {
|
|
|
|
|
case CT_ROOT:
|
|
|
|
|
ystr("root");
|
|
|
|
|
break;
|
|
|
|
|
case CT_OUTPUT:
|
|
|
|
|
ystr("output");
|
|
|
|
|
break;
|
|
|
|
|
case CT_CON:
|
|
|
|
|
ystr("con");
|
|
|
|
|
break;
|
|
|
|
|
case CT_FLOATING_CON:
|
|
|
|
|
ystr("floating_con");
|
|
|
|
|
break;
|
|
|
|
|
case CT_WORKSPACE:
|
|
|
|
|
ystr("workspace");
|
|
|
|
|
break;
|
|
|
|
|
case CT_DOCKAREA:
|
|
|
|
|
ystr("dockarea");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
DLOG("About to dump unknown container type=%d. This is a bug.\n", con->type);
|
|
|
|
|
assert(false);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2010-03-27 15:25:51 +01:00
|
|
|
|
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
/* provided for backwards compatibility only. */
|
2010-11-21 22:03:55 +01:00
|
|
|
|
ystr("orientation");
|
2012-10-03 19:10:48 +02:00
|
|
|
|
if (!con_is_split(con))
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
ystr("none");
|
|
|
|
|
else {
|
|
|
|
|
if (con_orientation(con) == HORIZ)
|
2011-01-07 20:48:01 +01:00
|
|
|
|
ystr("horizontal");
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
else ystr("vertical");
|
2011-01-07 20:48:01 +01:00
|
|
|
|
}
|
2010-03-27 15:25:51 +01:00
|
|
|
|
|
2011-12-22 00:15:32 +01:00
|
|
|
|
ystr("scratchpad_state");
|
|
|
|
|
switch (con->scratchpad_state) {
|
|
|
|
|
case SCRATCHPAD_NONE:
|
|
|
|
|
ystr("none");
|
|
|
|
|
break;
|
|
|
|
|
case SCRATCHPAD_FRESH:
|
|
|
|
|
ystr("fresh");
|
|
|
|
|
break;
|
|
|
|
|
case SCRATCHPAD_CHANGED:
|
|
|
|
|
ystr("changed");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-29 11:10:17 +01:00
|
|
|
|
ystr("percent");
|
2011-07-24 14:47:28 +02:00
|
|
|
|
if (con->percent == 0.0)
|
|
|
|
|
y(null);
|
|
|
|
|
else y(double, con->percent);
|
2010-11-29 11:10:17 +01:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
ystr("urgent");
|
2011-07-24 14:54:30 +02:00
|
|
|
|
y(bool, con->urgent);
|
2010-06-02 17:51:58 +02:00
|
|
|
|
|
2011-08-07 18:42:23 +02:00
|
|
|
|
if (con->mark != NULL) {
|
|
|
|
|
ystr("mark");
|
|
|
|
|
ystr(con->mark);
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
ystr("focused");
|
2011-07-24 15:00:09 +02:00
|
|
|
|
y(bool, (con == focused));
|
2010-07-04 22:17:18 +02:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
ystr("layout");
|
2011-06-02 17:12:18 +02:00
|
|
|
|
switch (con->layout) {
|
|
|
|
|
case L_DEFAULT:
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
DLOG("About to dump layout=default, this is a bug in the code.\n");
|
|
|
|
|
assert(false);
|
|
|
|
|
break;
|
|
|
|
|
case L_SPLITV:
|
|
|
|
|
ystr("splitv");
|
|
|
|
|
break;
|
|
|
|
|
case L_SPLITH:
|
|
|
|
|
ystr("splith");
|
2011-06-02 17:12:18 +02:00
|
|
|
|
break;
|
|
|
|
|
case L_STACKED:
|
|
|
|
|
ystr("stacked");
|
|
|
|
|
break;
|
|
|
|
|
case L_TABBED:
|
|
|
|
|
ystr("tabbed");
|
|
|
|
|
break;
|
|
|
|
|
case L_DOCKAREA:
|
|
|
|
|
ystr("dockarea");
|
|
|
|
|
break;
|
|
|
|
|
case L_OUTPUT:
|
|
|
|
|
ystr("output");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2010-03-27 15:25:51 +01:00
|
|
|
|
|
2012-09-16 22:53:41 +02:00
|
|
|
|
ystr("workspace_layout");
|
|
|
|
|
switch (con->workspace_layout) {
|
|
|
|
|
case L_DEFAULT:
|
|
|
|
|
ystr("default");
|
|
|
|
|
break;
|
|
|
|
|
case L_STACKED:
|
|
|
|
|
ystr("stacked");
|
|
|
|
|
break;
|
|
|
|
|
case L_TABBED:
|
|
|
|
|
ystr("tabbed");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
DLOG("About to dump workspace_layout=%d (none of default/stacked/tabbed), this is a bug.\n", con->workspace_layout);
|
|
|
|
|
assert(false);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
Introduce splith/splitv layouts, remove orientation
With this commit, the "default" layout is replaced by the splith and
splitv layouts. splith is equivalent to default with orientation
horizontal and splitv is equivalent to default with orientation
vertical.
The "split h" and "split v" commands continue to work as before, they
split the current container and you will end up in a split container
with layout splith (after "split h") or splitv (after "split v").
To change a splith container into a splitv container, use either "layout
splitv" or "layout toggle split". The latter command is used in the
default config as mod+l (previously "layout default"). In case you have
"layout default" in your config file, it is recommended to just replace
it by "layout toggle split", which will work as "layout default" did
before when pressing it once, but toggle between horizontal/vertical
when pressing it repeatedly.
The rationale behind this commit is that it’s cleaner to have all
parameters that influence how windows are rendered in the layout itself
rather than having a special parameter in combination with only one
layout. This enables us to change existing split containers in all cases
without breaking existing features (see ticket #464). Also, users should
feel more confident about whether they are actually splitting or just
changing an existing split container now.
As a nice side-effect, this commit brings back the "layout toggle"
feature we once had in i3 version 3 (see the userguide).
AFAIK, it is safe to use in-place restart to upgrade into versions
after this commit (switching to an older version will break your layout,
though).
Fixes #464
2012-08-04 03:04:00 +02:00
|
|
|
|
ystr("last_split_layout");
|
|
|
|
|
switch (con->layout) {
|
|
|
|
|
case L_SPLITV:
|
|
|
|
|
ystr("splitv");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ystr("splith");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
ystr("border");
|
2011-05-11 20:39:18 +02:00
|
|
|
|
switch (con->border_style) {
|
|
|
|
|
case BS_NORMAL:
|
|
|
|
|
ystr("normal");
|
|
|
|
|
break;
|
|
|
|
|
case BS_NONE:
|
|
|
|
|
ystr("none");
|
|
|
|
|
break;
|
2012-09-24 01:14:00 +02:00
|
|
|
|
case BS_PIXEL:
|
|
|
|
|
ystr("pixel");
|
2011-05-11 20:39:18 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
2010-11-12 19:16:38 +01:00
|
|
|
|
|
2012-09-24 01:14:00 +02:00
|
|
|
|
ystr("current_border_width");
|
|
|
|
|
y(integer, con->current_border_width);
|
|
|
|
|
|
2010-11-28 17:20:29 +01:00
|
|
|
|
dump_rect(gen, "rect", con->rect);
|
|
|
|
|
dump_rect(gen, "window_rect", con->window_rect);
|
2011-02-21 03:01:55 +01:00
|
|
|
|
dump_rect(gen, "geometry", con->geometry);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
ystr("name");
|
2012-08-07 21:23:06 +02:00
|
|
|
|
if (con->window && con->window->name)
|
|
|
|
|
ystr(i3string_as_utf8(con->window->name));
|
2011-08-21 22:19:53 +02:00
|
|
|
|
else
|
|
|
|
|
ystr(con->name);
|
2010-06-01 20:50:23 +02:00
|
|
|
|
|
2010-12-27 22:28:59 +01:00
|
|
|
|
if (con->type == CT_WORKSPACE) {
|
|
|
|
|
ystr("num");
|
|
|
|
|
y(integer, con->num);
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
ystr("window");
|
|
|
|
|
if (con->window)
|
|
|
|
|
y(integer, con->window->id);
|
|
|
|
|
else y(null);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
|
2013-12-15 21:29:05 +01:00
|
|
|
|
if (con->window && !inplace_restart) {
|
|
|
|
|
/* Window properties are useless to preserve when restarting because
|
|
|
|
|
* they will be queried again anyway. However, for i3-save-tree(1),
|
|
|
|
|
* they are very useful and save i3-save-tree dealing with X11. */
|
|
|
|
|
ystr("window_properties");
|
|
|
|
|
y(map_open);
|
|
|
|
|
|
|
|
|
|
#define DUMP_PROPERTY(key, prop_name) do { \
|
|
|
|
|
if (con->window->prop_name != NULL) { \
|
|
|
|
|
ystr(key); \
|
|
|
|
|
ystr(con->window->prop_name); \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
DUMP_PROPERTY("class", class_class);
|
|
|
|
|
DUMP_PROPERTY("instance", class_instance);
|
|
|
|
|
DUMP_PROPERTY("window_role", role);
|
|
|
|
|
|
|
|
|
|
if (con->window->name != NULL) {
|
|
|
|
|
ystr("title");
|
|
|
|
|
ystr(i3string_as_utf8(con->window->name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
y(map_close);
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
ystr("nodes");
|
|
|
|
|
y(array_open);
|
|
|
|
|
Con *node;
|
2011-02-21 03:01:55 +01:00
|
|
|
|
if (con->type != CT_DOCKAREA || !inplace_restart) {
|
|
|
|
|
TAILQ_FOREACH(node, &(con->nodes_head), nodes) {
|
|
|
|
|
dump_node(gen, node, inplace_restart);
|
|
|
|
|
}
|
2010-11-21 22:03:55 +01:00
|
|
|
|
}
|
|
|
|
|
y(array_close);
|
|
|
|
|
|
|
|
|
|
ystr("floating_nodes");
|
|
|
|
|
y(array_open);
|
|
|
|
|
TAILQ_FOREACH(node, &(con->floating_head), floating_windows) {
|
|
|
|
|
dump_node(gen, node, inplace_restart);
|
|
|
|
|
}
|
|
|
|
|
y(array_close);
|
|
|
|
|
|
|
|
|
|
ystr("focus");
|
|
|
|
|
y(array_open);
|
2011-09-26 21:11:47 +02:00
|
|
|
|
TAILQ_FOREACH(node, &(con->focus_head), focused) {
|
2010-11-21 22:03:55 +01:00
|
|
|
|
y(integer, (long int)node);
|
|
|
|
|
}
|
|
|
|
|
y(array_close);
|
|
|
|
|
|
|
|
|
|
ystr("fullscreen_mode");
|
|
|
|
|
y(integer, con->fullscreen_mode);
|
|
|
|
|
|
2012-01-05 22:39:18 +01:00
|
|
|
|
ystr("floating");
|
|
|
|
|
switch (con->floating) {
|
|
|
|
|
case FLOATING_AUTO_OFF:
|
|
|
|
|
ystr("auto_off");
|
|
|
|
|
break;
|
|
|
|
|
case FLOATING_AUTO_ON:
|
|
|
|
|
ystr("auto_on");
|
|
|
|
|
break;
|
|
|
|
|
case FLOATING_USER_OFF:
|
|
|
|
|
ystr("user_off");
|
|
|
|
|
break;
|
|
|
|
|
case FLOATING_USER_ON:
|
|
|
|
|
ystr("user_on");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 03:01:55 +01:00
|
|
|
|
ystr("swallows");
|
|
|
|
|
y(array_open);
|
|
|
|
|
Match *match;
|
|
|
|
|
TAILQ_FOREACH(match, &(con->swallow_head), matches) {
|
|
|
|
|
if (match->dock != -1) {
|
|
|
|
|
y(map_open);
|
|
|
|
|
ystr("dock");
|
|
|
|
|
y(integer, match->dock);
|
|
|
|
|
ystr("insert_where");
|
|
|
|
|
y(integer, match->insert_where);
|
|
|
|
|
y(map_close);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TODO: the other swallow keys */
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
if (inplace_restart) {
|
|
|
|
|
if (con->window != NULL) {
|
|
|
|
|
y(map_open);
|
|
|
|
|
ystr("id");
|
|
|
|
|
y(integer, con->window->id);
|
2012-01-21 19:35:15 +01:00
|
|
|
|
ystr("restart_mode");
|
|
|
|
|
y(bool, true);
|
2010-11-21 22:03:55 +01:00
|
|
|
|
y(map_close);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
2010-11-21 22:03:55 +01:00
|
|
|
|
}
|
2011-02-21 03:01:55 +01:00
|
|
|
|
y(array_close);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
|
2013-03-15 15:40:26 +01:00
|
|
|
|
if (inplace_restart && con->window != NULL) {
|
|
|
|
|
ystr("depth");
|
|
|
|
|
y(integer, con->depth);
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
y(map_close);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IPC_HANDLER(tree) {
|
2010-11-29 11:10:17 +01:00
|
|
|
|
setlocale(LC_NUMERIC, "C");
|
2012-11-03 12:17:28 +01:00
|
|
|
|
yajl_gen gen = ygenalloc();
|
2010-11-21 22:03:55 +01:00
|
|
|
|
dump_node(gen, croot, false);
|
2010-11-29 11:10:17 +01:00
|
|
|
|
setlocale(LC_NUMERIC, "");
|
2010-03-27 15:25:51 +01:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
const unsigned char *payload;
|
2012-11-03 12:17:28 +01:00
|
|
|
|
ylength length;
|
2010-11-21 22:03:55 +01:00
|
|
|
|
y(get_buf, &payload, &length);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
|
2011-10-02 19:33:10 +02:00
|
|
|
|
ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_TREE, payload);
|
2010-11-21 22:03:55 +01:00
|
|
|
|
y(free);
|
2010-03-27 15:25:51 +01:00
|
|
|
|
}
|
|
|
|
|
|
2011-08-06 20:23:18 +02:00
|
|
|
|
|
2010-03-11 15:58:39 +01:00
|
|
|
|
/*
|
|
|
|
|
* Formats the reply message for a GET_WORKSPACES request and sends it to the
|
|
|
|
|
* client
|
|
|
|
|
*
|
|
|
|
|
*/
|
2010-03-12 21:05:05 +01:00
|
|
|
|
IPC_HANDLER(get_workspaces) {
|
2012-11-03 12:17:28 +01:00
|
|
|
|
yajl_gen gen = ygenalloc();
|
2010-11-20 22:30:53 +01:00
|
|
|
|
y(array_open);
|
2010-03-12 03:01:34 +01:00
|
|
|
|
|
2010-11-20 22:30:53 +01:00
|
|
|
|
Con *focused_ws = con_get_workspace(focused);
|
2010-03-11 15:58:39 +01:00
|
|
|
|
|
2010-11-20 22:30:53 +01:00
|
|
|
|
Con *output;
|
|
|
|
|
TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
|
2012-09-23 14:56:56 +02:00
|
|
|
|
if (con_is_internal(output))
|
2011-12-22 00:15:32 +01:00
|
|
|
|
continue;
|
2011-02-21 01:55:36 +01:00
|
|
|
|
Con *ws;
|
|
|
|
|
TAILQ_FOREACH(ws, &(output_get_content(output)->nodes_head), nodes) {
|
|
|
|
|
assert(ws->type == CT_WORKSPACE);
|
|
|
|
|
y(map_open);
|
|
|
|
|
|
|
|
|
|
ystr("num");
|
|
|
|
|
if (ws->num == -1)
|
|
|
|
|
y(null);
|
|
|
|
|
else y(integer, ws->num);
|
|
|
|
|
|
|
|
|
|
ystr("name");
|
|
|
|
|
ystr(ws->name);
|
|
|
|
|
|
|
|
|
|
ystr("visible");
|
|
|
|
|
y(bool, workspace_is_visible(ws));
|
|
|
|
|
|
|
|
|
|
ystr("focused");
|
|
|
|
|
y(bool, ws == focused_ws);
|
2011-02-21 00:54:29 +01:00
|
|
|
|
|
2011-02-21 01:55:36 +01:00
|
|
|
|
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(output->name);
|
|
|
|
|
|
|
|
|
|
ystr("urgent");
|
|
|
|
|
y(bool, ws->urgent);
|
|
|
|
|
|
|
|
|
|
y(map_close);
|
2010-03-11 15:58:39 +01:00
|
|
|
|
}
|
2010-11-20 22:30:53 +01:00
|
|
|
|
}
|
2010-03-11 15:58:39 +01:00
|
|
|
|
|
2010-11-20 22:30:53 +01:00
|
|
|
|
y(array_close);
|
2010-03-11 15:58:39 +01:00
|
|
|
|
|
2010-11-20 22:30:53 +01:00
|
|
|
|
const unsigned char *payload;
|
2012-11-03 12:17:28 +01:00
|
|
|
|
ylength length;
|
2010-11-20 22:30:53 +01:00
|
|
|
|
y(get_buf, &payload, &length);
|
2010-03-11 15:58:39 +01:00
|
|
|
|
|
2011-10-02 19:33:10 +02:00
|
|
|
|
ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_WORKSPACES, payload);
|
2010-11-20 22:30:53 +01:00
|
|
|
|
y(free);
|
2010-03-11 15:58:39 +01:00
|
|
|
|
}
|
|
|
|
|
|
2010-03-19 22:24:52 +01:00
|
|
|
|
/*
|
|
|
|
|
* Formats the reply message for a GET_OUTPUTS request and sends it to the
|
|
|
|
|
* client
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
IPC_HANDLER(get_outputs) {
|
2012-11-03 12:17:28 +01:00
|
|
|
|
yajl_gen gen = ygenalloc();
|
2010-11-20 22:30:53 +01:00
|
|
|
|
y(array_open);
|
2010-03-19 22:24:52 +01:00
|
|
|
|
|
2010-11-20 22:30:53 +01:00
|
|
|
|
Output *output;
|
|
|
|
|
TAILQ_FOREACH(output, &outputs, outputs) {
|
|
|
|
|
y(map_open);
|
2010-03-19 22:24:52 +01:00
|
|
|
|
|
2010-11-20 22:30:53 +01:00
|
|
|
|
ystr("name");
|
|
|
|
|
ystr(output->name);
|
2010-03-19 22:24:52 +01:00
|
|
|
|
|
2010-11-20 22:30:53 +01:00
|
|
|
|
ystr("active");
|
|
|
|
|
y(bool, output->active);
|
2010-03-19 22:24:52 +01:00
|
|
|
|
|
2012-04-01 16:07:15 +02:00
|
|
|
|
ystr("primary");
|
|
|
|
|
y(bool, output->primary);
|
|
|
|
|
|
2010-11-20 22:30:53 +01:00
|
|
|
|
ystr("rect");
|
|
|
|
|
y(map_open);
|
|
|
|
|
ystr("x");
|
|
|
|
|
y(integer, output->rect.x);
|
|
|
|
|
ystr("y");
|
|
|
|
|
y(integer, output->rect.y);
|
|
|
|
|
ystr("width");
|
|
|
|
|
y(integer, output->rect.width);
|
|
|
|
|
ystr("height");
|
|
|
|
|
y(integer, output->rect.height);
|
|
|
|
|
y(map_close);
|
2010-03-19 22:24:52 +01:00
|
|
|
|
|
2010-11-20 22:30:53 +01:00
|
|
|
|
ystr("current_workspace");
|
2010-11-21 22:12:34 +01:00
|
|
|
|
Con *ws = NULL;
|
2011-06-10 18:27:20 +02:00
|
|
|
|
if (output->con && (ws = con_get_fullscreen_con(output->con, CF_OUTPUT)))
|
2010-11-21 22:12:34 +01:00
|
|
|
|
ystr(ws->name);
|
|
|
|
|
else y(null);
|
2010-11-20 22:30:53 +01:00
|
|
|
|
|
|
|
|
|
y(map_close);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
y(array_close);
|
|
|
|
|
|
|
|
|
|
const unsigned char *payload;
|
2012-11-03 12:17:28 +01:00
|
|
|
|
ylength length;
|
2010-11-20 22:30:53 +01:00
|
|
|
|
y(get_buf, &payload, &length);
|
|
|
|
|
|
2011-10-02 19:33:10 +02:00
|
|
|
|
ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_OUTPUTS, payload);
|
2010-11-20 22:30:53 +01:00
|
|
|
|
y(free);
|
2010-03-19 22:24:52 +01:00
|
|
|
|
}
|
|
|
|
|
|
2011-08-06 20:23:18 +02:00
|
|
|
|
/*
|
|
|
|
|
* Formats the reply message for a GET_MARKS request and sends it to the
|
|
|
|
|
* client
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
IPC_HANDLER(get_marks) {
|
2012-11-03 12:17:28 +01:00
|
|
|
|
yajl_gen gen = ygenalloc();
|
2011-08-06 20:23:18 +02:00
|
|
|
|
y(array_open);
|
|
|
|
|
|
|
|
|
|
Con *con;
|
|
|
|
|
TAILQ_FOREACH(con, &all_cons, all_cons)
|
|
|
|
|
if (con->mark != NULL)
|
|
|
|
|
ystr(con->mark);
|
|
|
|
|
|
|
|
|
|
y(array_close);
|
|
|
|
|
|
|
|
|
|
const unsigned char *payload;
|
2012-11-03 12:17:28 +01:00
|
|
|
|
ylength length;
|
2011-08-06 20:23:18 +02:00
|
|
|
|
y(get_buf, &payload, &length);
|
|
|
|
|
|
2011-10-02 19:33:10 +02:00
|
|
|
|
ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_MARKS, payload);
|
2011-08-06 20:23:18 +02:00
|
|
|
|
y(free);
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-05 14:29:19 +02:00
|
|
|
|
/*
|
|
|
|
|
* Returns the version of i3
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
IPC_HANDLER(get_version) {
|
2012-11-03 12:17:28 +01:00
|
|
|
|
yajl_gen gen = ygenalloc();
|
2012-08-05 14:29:19 +02:00
|
|
|
|
y(map_open);
|
|
|
|
|
|
|
|
|
|
ystr("major");
|
|
|
|
|
y(integer, MAJOR_VERSION);
|
|
|
|
|
|
|
|
|
|
ystr("minor");
|
|
|
|
|
y(integer, MINOR_VERSION);
|
|
|
|
|
|
|
|
|
|
ystr("patch");
|
|
|
|
|
y(integer, PATCH_VERSION);
|
|
|
|
|
|
|
|
|
|
ystr("human_readable");
|
|
|
|
|
ystr(I3_VERSION);
|
|
|
|
|
|
|
|
|
|
y(map_close);
|
|
|
|
|
|
|
|
|
|
const unsigned char *payload;
|
2012-11-03 12:17:28 +01:00
|
|
|
|
ylength length;
|
2012-08-05 14:29:19 +02:00
|
|
|
|
y(get_buf, &payload, &length);
|
|
|
|
|
|
|
|
|
|
ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_VERSION, payload);
|
|
|
|
|
y(free);
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-13 00:23:09 +02:00
|
|
|
|
/*
|
|
|
|
|
* Formats the reply message for a GET_BAR_CONFIG request and sends it to the
|
|
|
|
|
* client.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
IPC_HANDLER(get_bar_config) {
|
2012-11-03 12:17:28 +01:00
|
|
|
|
yajl_gen gen = ygenalloc();
|
2011-10-18 23:12:46 +02:00
|
|
|
|
|
|
|
|
|
/* If no ID was passed, we return a JSON array with all IDs */
|
|
|
|
|
if (message_size == 0) {
|
|
|
|
|
y(array_open);
|
|
|
|
|
Barconfig *current;
|
|
|
|
|
TAILQ_FOREACH(current, &barconfigs, configs) {
|
|
|
|
|
ystr(current->id);
|
|
|
|
|
}
|
|
|
|
|
y(array_close);
|
|
|
|
|
|
|
|
|
|
const unsigned char *payload;
|
2012-11-03 12:17:28 +01:00
|
|
|
|
ylength length;
|
2011-10-18 23:12:46 +02:00
|
|
|
|
y(get_buf, &payload, &length);
|
|
|
|
|
|
|
|
|
|
ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
|
|
|
|
|
y(free);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-13 00:23:09 +02:00
|
|
|
|
/* To get a properly terminated buffer, we copy
|
|
|
|
|
* message_size bytes out of the buffer */
|
|
|
|
|
char *bar_id = scalloc(message_size + 1);
|
|
|
|
|
strncpy(bar_id, (const char*)message, message_size);
|
|
|
|
|
LOG("IPC: looking for config for bar ID \"%s\"\n", bar_id);
|
|
|
|
|
Barconfig *current, *config = NULL;
|
2011-10-18 23:11:27 +02:00
|
|
|
|
TAILQ_FOREACH(current, &barconfigs, configs) {
|
2011-10-13 00:23:09 +02:00
|
|
|
|
if (strcmp(current->id, bar_id) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
config = current;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
y(map_open);
|
|
|
|
|
|
|
|
|
|
if (!config) {
|
|
|
|
|
/* If we did not find a config for the given ID, the reply will contain
|
|
|
|
|
* a null 'id' field. */
|
|
|
|
|
ystr("id");
|
|
|
|
|
y(null);
|
|
|
|
|
} else {
|
|
|
|
|
ystr("id");
|
|
|
|
|
ystr(config->id);
|
|
|
|
|
|
|
|
|
|
if (config->num_outputs > 0) {
|
|
|
|
|
ystr("outputs");
|
|
|
|
|
y(array_open);
|
|
|
|
|
for (int c = 0; c < config->num_outputs; c++)
|
|
|
|
|
ystr(config->outputs[c]);
|
|
|
|
|
y(array_close);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define YSTR_IF_SET(name) \
|
|
|
|
|
do { \
|
|
|
|
|
if (config->name) { \
|
|
|
|
|
ystr( # name); \
|
|
|
|
|
ystr(config->name); \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
YSTR_IF_SET(tray_output);
|
|
|
|
|
YSTR_IF_SET(socket_path);
|
|
|
|
|
|
|
|
|
|
ystr("mode");
|
introduced i3 command for changing the hidden state and the mode of i3bar
The hidden_state and mode of each i3bar instance can now be controlled from within i3.
Therefore, two new i3 command were introduced:
_
bar hidden_state show|hide|toggle [<bar_id>]
show: always show the bar
hide: normal hide mode
toggle: toggle between show and hide (individually for each bar)
_
bar mode dock|hide|invisible|toggle [<bar_id>]
hide,dock: like before
invisible: always keep the bar hidden
toggle: toggle between dock and hide (individually for each bar)
This patch introduces a hidden_state ("hidden_state hide|show") in the
barconfig, which indicates the current hidden_state of each i3bar
instance. It only affects the bar when in hide mode. Additionally, a new
invisible mode was introduced. In order to change the hidden_state or
mode of the bar from i3, a barconfig-update event was introduced, for
which a bar can subscribe and the bar then gets notified about the
currently set hidden_state and mode in its barconfig.
For convenience, an id field ("id <bar_id>") was added to the barconfig, where one can
set the desired id for the corresponding bar. If the id is not specified, i3 will
deterministically choose an id; otherwise, with the previous random approach for finding
a new id, which is actually not shared with i3bar, as it would determine its id on
startup, the event-subscription would be destroyed on reload. Still, this issue remains
when manually changing the bar_id in the config and then reloading.
fixes #833, #651
2013-05-25 14:30:00 +02:00
|
|
|
|
switch (config->mode) {
|
|
|
|
|
case M_HIDE:
|
|
|
|
|
ystr("hide");
|
|
|
|
|
break;
|
|
|
|
|
case M_INVISIBLE:
|
|
|
|
|
ystr("invisible");
|
|
|
|
|
break;
|
|
|
|
|
case M_DOCK:
|
|
|
|
|
default:
|
|
|
|
|
ystr("dock");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ystr("hidden_state");
|
|
|
|
|
switch (config->hidden_state) {
|
|
|
|
|
case S_SHOW:
|
|
|
|
|
ystr("show");
|
|
|
|
|
break;
|
|
|
|
|
case S_HIDE:
|
|
|
|
|
default:
|
|
|
|
|
ystr("hide");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-10-13 00:23:09 +02:00
|
|
|
|
|
2011-12-12 06:58:57 +01:00
|
|
|
|
ystr("modifier");
|
|
|
|
|
switch (config->modifier) {
|
|
|
|
|
case M_CONTROL:
|
|
|
|
|
ystr("ctrl");
|
|
|
|
|
break;
|
|
|
|
|
case M_SHIFT:
|
|
|
|
|
ystr("shift");
|
|
|
|
|
break;
|
|
|
|
|
case M_MOD1:
|
|
|
|
|
ystr("Mod1");
|
|
|
|
|
break;
|
|
|
|
|
case M_MOD2:
|
|
|
|
|
ystr("Mod2");
|
|
|
|
|
break;
|
|
|
|
|
case M_MOD3:
|
|
|
|
|
ystr("Mod3");
|
|
|
|
|
break;
|
|
|
|
|
/*
|
|
|
|
|
case M_MOD4:
|
|
|
|
|
ystr("Mod4");
|
|
|
|
|
break;
|
|
|
|
|
*/
|
|
|
|
|
case M_MOD5:
|
|
|
|
|
ystr("Mod5");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ystr("Mod4");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-13 00:23:09 +02:00
|
|
|
|
ystr("position");
|
|
|
|
|
if (config->position == P_BOTTOM)
|
|
|
|
|
ystr("bottom");
|
|
|
|
|
else ystr("top");
|
|
|
|
|
|
|
|
|
|
YSTR_IF_SET(status_command);
|
|
|
|
|
YSTR_IF_SET(font);
|
|
|
|
|
|
|
|
|
|
ystr("workspace_buttons");
|
|
|
|
|
y(bool, !config->hide_workspace_buttons);
|
|
|
|
|
|
2013-08-09 05:30:14 +02:00
|
|
|
|
ystr("binding_mode_indicator");
|
|
|
|
|
y(bool, !config->hide_binding_mode_indicator);
|
|
|
|
|
|
2011-10-13 00:23:09 +02:00
|
|
|
|
ystr("verbose");
|
|
|
|
|
y(bool, config->verbose);
|
|
|
|
|
|
|
|
|
|
#undef YSTR_IF_SET
|
|
|
|
|
#define YSTR_IF_SET(name) \
|
|
|
|
|
do { \
|
|
|
|
|
if (config->colors.name) { \
|
|
|
|
|
ystr( # name); \
|
|
|
|
|
ystr(config->colors.name); \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
|
|
ystr("colors");
|
|
|
|
|
y(map_open);
|
|
|
|
|
YSTR_IF_SET(background);
|
|
|
|
|
YSTR_IF_SET(statusline);
|
2013-01-27 21:27:21 +01:00
|
|
|
|
YSTR_IF_SET(separator);
|
2012-01-20 22:36:50 +01:00
|
|
|
|
YSTR_IF_SET(focused_workspace_border);
|
2011-10-13 00:23:09 +02:00
|
|
|
|
YSTR_IF_SET(focused_workspace_bg);
|
2012-01-20 22:36:50 +01:00
|
|
|
|
YSTR_IF_SET(focused_workspace_text);
|
|
|
|
|
YSTR_IF_SET(active_workspace_border);
|
2011-10-13 00:23:09 +02:00
|
|
|
|
YSTR_IF_SET(active_workspace_bg);
|
2012-01-20 22:36:50 +01:00
|
|
|
|
YSTR_IF_SET(active_workspace_text);
|
|
|
|
|
YSTR_IF_SET(inactive_workspace_border);
|
2011-10-13 00:23:09 +02:00
|
|
|
|
YSTR_IF_SET(inactive_workspace_bg);
|
2012-01-20 22:36:50 +01:00
|
|
|
|
YSTR_IF_SET(inactive_workspace_text);
|
|
|
|
|
YSTR_IF_SET(urgent_workspace_border);
|
2011-10-13 00:23:09 +02:00
|
|
|
|
YSTR_IF_SET(urgent_workspace_bg);
|
2012-01-20 22:36:50 +01:00
|
|
|
|
YSTR_IF_SET(urgent_workspace_text);
|
2011-10-18 23:11:44 +02:00
|
|
|
|
y(map_close);
|
2011-10-13 00:23:09 +02:00
|
|
|
|
|
|
|
|
|
#undef YSTR_IF_SET
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
y(map_close);
|
|
|
|
|
|
|
|
|
|
const unsigned char *payload;
|
2012-11-03 12:17:28 +01:00
|
|
|
|
ylength length;
|
2011-10-13 00:23:09 +02:00
|
|
|
|
y(get_buf, &payload, &length);
|
|
|
|
|
|
|
|
|
|
ipc_send_message(fd, length, I3_IPC_REPLY_TYPE_BAR_CONFIG, payload);
|
|
|
|
|
y(free);
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-28 20:51:29 +02:00
|
|
|
|
/*
|
2010-03-12 21:05:05 +01:00
|
|
|
|
* Callback for the YAJL parser (will be called when a string is parsed).
|
2009-07-28 20:51:29 +02:00
|
|
|
|
*
|
2010-03-12 21:05:05 +01:00
|
|
|
|
*/
|
2011-04-27 19:52:53 +02:00
|
|
|
|
static int add_subscription(void *extra, const unsigned char *s,
|
2012-11-03 12:17:28 +01:00
|
|
|
|
ylength len) {
|
2010-11-21 22:03:55 +01:00
|
|
|
|
ipc_client *client = extra;
|
2010-03-12 21:05:05 +01:00
|
|
|
|
|
2012-08-05 16:34:38 +02:00
|
|
|
|
DLOG("should add subscription to extra %p, sub %.*s\n", client, (int)len, s);
|
2010-11-21 22:03:55 +01:00
|
|
|
|
int event = client->num_events;
|
2010-03-12 21:05:05 +01:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
client->num_events++;
|
|
|
|
|
client->events = realloc(client->events, client->num_events * sizeof(char*));
|
|
|
|
|
/* We copy the string because it is not null-terminated and strndup()
|
|
|
|
|
* is missing on some BSD systems */
|
|
|
|
|
client->events[event] = scalloc(len+1);
|
|
|
|
|
memcpy(client->events[event], s, len);
|
2010-03-12 21:05:05 +01:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
DLOG("client is now subscribed to:\n");
|
|
|
|
|
for (int i = 0; i < client->num_events; i++)
|
|
|
|
|
DLOG("event %s\n", client->events[i]);
|
|
|
|
|
DLOG("(done)\n");
|
2010-03-12 21:05:05 +01:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
return 1;
|
2010-03-12 21:05:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Subscribes this connection to the event types which were given as a JSON
|
|
|
|
|
* serialized array in the payload field of the message.
|
2009-07-28 20:51:29 +02:00
|
|
|
|
*
|
|
|
|
|
*/
|
2010-03-12 21:05:05 +01:00
|
|
|
|
IPC_HANDLER(subscribe) {
|
2010-11-21 22:03:55 +01:00
|
|
|
|
yajl_handle p;
|
|
|
|
|
yajl_callbacks callbacks;
|
|
|
|
|
yajl_status stat;
|
|
|
|
|
ipc_client *current, *client = NULL;
|
|
|
|
|
|
|
|
|
|
/* Search the ipc_client structure for this connection */
|
|
|
|
|
TAILQ_FOREACH(current, &all_clients, clients) {
|
|
|
|
|
if (current->fd != fd)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
client = current;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2010-03-12 15:29:44 +01:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
if (client == NULL) {
|
|
|
|
|
ELOG("Could not find ipc_client data structure for fd %d\n", fd);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-03-12 21:05:05 +01:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
/* Setup the JSON parser */
|
|
|
|
|
memset(&callbacks, 0, sizeof(yajl_callbacks));
|
|
|
|
|
callbacks.yajl_string = add_subscription;
|
2010-03-12 21:05:05 +01:00
|
|
|
|
|
2012-11-03 12:17:28 +01:00
|
|
|
|
p = yalloc(&callbacks, (void*)client);
|
2010-11-21 22:03:55 +01:00
|
|
|
|
stat = yajl_parse(p, (const unsigned char*)message, message_size);
|
|
|
|
|
if (stat != yajl_status_ok) {
|
|
|
|
|
unsigned char *err;
|
|
|
|
|
err = yajl_get_error(p, true, (const unsigned char*)message,
|
|
|
|
|
message_size);
|
|
|
|
|
ELOG("YAJL parse error: %s\n", err);
|
|
|
|
|
yajl_free_error(p, err);
|
2010-03-12 21:05:05 +01:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
const char *reply = "{\"success\":false}";
|
2011-10-02 19:33:10 +02:00
|
|
|
|
ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t*)reply);
|
2010-11-21 22:03:55 +01:00
|
|
|
|
yajl_free(p);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
yajl_free(p);
|
|
|
|
|
const char *reply = "{\"success\":true}";
|
2011-10-02 19:33:10 +02:00
|
|
|
|
ipc_send_message(fd, strlen(reply), I3_IPC_REPLY_TYPE_SUBSCRIBE, (const uint8_t*)reply);
|
2009-07-28 20:51:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
2010-03-19 22:24:52 +01:00
|
|
|
|
/* The index of each callback function corresponds to the numeric
|
|
|
|
|
* value of the message type (see include/i3/ipc.h) */
|
2012-08-05 14:29:19 +02:00
|
|
|
|
handler_t handlers[8] = {
|
2010-11-21 22:03:55 +01:00
|
|
|
|
handle_command,
|
|
|
|
|
handle_get_workspaces,
|
|
|
|
|
handle_subscribe,
|
|
|
|
|
handle_get_outputs,
|
2011-08-06 20:23:18 +02:00
|
|
|
|
handle_tree,
|
2011-10-13 00:23:09 +02:00
|
|
|
|
handle_get_marks,
|
2011-12-10 12:16:32 +01:00
|
|
|
|
handle_get_bar_config,
|
2012-08-05 14:29:19 +02:00
|
|
|
|
handle_get_version,
|
2010-03-12 21:05:05 +01:00
|
|
|
|
};
|
|
|
|
|
|
2009-07-28 20:51:29 +02:00
|
|
|
|
/*
|
|
|
|
|
* Handler for activity on a client connection, receives a message from a
|
|
|
|
|
* client.
|
|
|
|
|
*
|
|
|
|
|
* For now, the maximum message size is 2048. I’m not sure for what the
|
|
|
|
|
* IPC interface will be used in the future, thus I’m not implementing a
|
|
|
|
|
* mechanism for arbitrarily long messages, as it seems like overkill
|
|
|
|
|
* at the moment.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static void ipc_receive_message(EV_P_ struct ev_io *w, int revents) {
|
2013-01-23 18:59:36 +01:00
|
|
|
|
uint32_t message_type;
|
|
|
|
|
uint32_t message_length;
|
|
|
|
|
uint8_t *message;
|
|
|
|
|
|
|
|
|
|
int ret = ipc_recv_message(w->fd, &message_type, &message_length, &message);
|
|
|
|
|
/* EOF or other error */
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
/* Was this a spurious read? See ev(3) */
|
|
|
|
|
if (ret == -1 && errno == EAGAIN)
|
|
|
|
|
return;
|
2009-07-28 20:51:29 +02:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
/* If not, there was some kind of error. We don’t bother
|
|
|
|
|
* and close the connection */
|
|
|
|
|
close(w->fd);
|
2009-07-28 20:51:29 +02:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
/* Delete the client from the list of clients */
|
|
|
|
|
ipc_client *current;
|
|
|
|
|
TAILQ_FOREACH(current, &all_clients, clients) {
|
|
|
|
|
if (current->fd != w->fd)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < current->num_events; i++)
|
|
|
|
|
free(current->events[i]);
|
|
|
|
|
/* We can call TAILQ_REMOVE because we break out of the
|
|
|
|
|
* TAILQ_FOREACH afterwards */
|
|
|
|
|
TAILQ_REMOVE(&all_clients, current, clients);
|
2011-11-07 22:34:39 +01:00
|
|
|
|
free(current);
|
2010-11-21 22:03:55 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
2009-07-28 20:51:29 +02:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
ev_io_stop(EV_A_ w);
|
2011-11-07 22:34:39 +01:00
|
|
|
|
free(w);
|
2009-07-28 20:51:29 +02:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
DLOG("IPC: client disconnected\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-07-28 20:51:29 +02:00
|
|
|
|
|
2013-01-23 18:59:36 +01:00
|
|
|
|
if (message_type >= (sizeof(handlers) / sizeof(handler_t)))
|
|
|
|
|
DLOG("Unhandled message type: %d\n", message_type);
|
|
|
|
|
else {
|
|
|
|
|
handler_t h = handlers[message_type];
|
|
|
|
|
h(w->fd, message, 0, message_length, message_type);
|
2010-11-21 22:03:55 +01:00
|
|
|
|
}
|
2009-07-28 20:51:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Handler for activity on the listening socket, meaning that a new client
|
|
|
|
|
* has just connected and we should accept() him. Sets up the event handler
|
|
|
|
|
* for activity on the new connection and inserts the file descriptor into
|
|
|
|
|
* the list of clients.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
void ipc_new_client(EV_P_ struct ev_io *w, int revents) {
|
2010-11-21 22:03:55 +01:00
|
|
|
|
struct sockaddr_un peer;
|
|
|
|
|
socklen_t len = sizeof(struct sockaddr_un);
|
|
|
|
|
int client;
|
|
|
|
|
if ((client = accept(w->fd, (struct sockaddr*)&peer, &len)) < 0) {
|
|
|
|
|
if (errno == EINTR)
|
|
|
|
|
return;
|
|
|
|
|
else perror("accept()");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-07-28 20:51:29 +02:00
|
|
|
|
|
2011-11-16 00:39:43 +01:00
|
|
|
|
/* Close this file descriptor on exec() */
|
|
|
|
|
(void)fcntl(client, F_SETFD, FD_CLOEXEC);
|
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
set_nonblock(client);
|
2009-07-28 20:51:29 +02:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
struct ev_io *package = scalloc(sizeof(struct ev_io));
|
|
|
|
|
ev_io_init(package, ipc_receive_message, client, EV_READ);
|
|
|
|
|
ev_io_start(EV_A_ package);
|
2009-07-28 20:51:29 +02:00
|
|
|
|
|
2011-08-10 15:55:27 +02:00
|
|
|
|
DLOG("IPC: new client connected on fd %d\n", w->fd);
|
2009-07-28 20:51:29 +02:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
ipc_client *new = scalloc(sizeof(ipc_client));
|
|
|
|
|
new->fd = client;
|
2009-07-28 20:51:29 +02:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
TAILQ_INSERT_TAIL(&all_clients, new, clients);
|
2009-07-28 20:51:29 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Creates the UNIX domain socket at the given path, sets it to non-blocking
|
|
|
|
|
* mode, bind()s and listen()s on it.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
int ipc_create_socket(const char *filename) {
|
2010-11-21 22:03:55 +01:00
|
|
|
|
int sockfd;
|
2009-07-28 20:51:29 +02:00
|
|
|
|
|
2011-03-20 15:34:34 +01:00
|
|
|
|
FREE(current_socketpath);
|
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
char *resolved = resolve_tilde(filename);
|
|
|
|
|
DLOG("Creating IPC-socket at %s\n", resolved);
|
|
|
|
|
char *copy = sstrdup(resolved);
|
|
|
|
|
const char *dir = dirname(copy);
|
|
|
|
|
if (!path_exists(dir))
|
|
|
|
|
mkdirp(dir);
|
|
|
|
|
free(copy);
|
2010-01-23 00:34:29 +01:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
/* Unlink the unix domain socket before */
|
|
|
|
|
unlink(resolved);
|
2009-07-28 20:51:29 +02:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
|
|
|
|
|
perror("socket()");
|
2010-11-15 02:01:04 +01:00
|
|
|
|
free(resolved);
|
2010-11-21 22:03:55 +01:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
2009-07-28 20:51:29 +02:00
|
|
|
|
|
2010-11-21 22:03:55 +01:00
|
|
|
|
(void)fcntl(sockfd, F_SETFD, FD_CLOEXEC);
|
|
|
|
|
|
|
|
|
|
struct sockaddr_un addr;
|
|
|
|
|
memset(&addr, 0, sizeof(struct sockaddr_un));
|
|
|
|
|
addr.sun_family = AF_LOCAL;
|
|
|
|
|
strncpy(addr.sun_path, resolved, sizeof(addr.sun_path) - 1);
|
|
|
|
|
if (bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)) < 0) {
|
|
|
|
|
perror("bind()");
|
|
|
|
|
free(resolved);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set_nonblock(sockfd);
|
|
|
|
|
|
|
|
|
|
if (listen(sockfd, 5) < 0) {
|
|
|
|
|
perror("listen()");
|
2011-03-20 15:34:34 +01:00
|
|
|
|
free(resolved);
|
2010-11-21 22:03:55 +01:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
2009-07-28 20:51:29 +02:00
|
|
|
|
|
2011-03-20 15:34:34 +01:00
|
|
|
|
current_socketpath = resolved;
|
2010-11-21 22:03:55 +01:00
|
|
|
|
return sockfd;
|
2009-07-28 20:51:29 +02:00
|
|
|
|
}
|