diff --git a/docs/ipc b/docs/ipc index 4a2b1df9..ff7c8aae 100644 --- a/docs/ipc +++ b/docs/ipc @@ -668,15 +668,16 @@ if ($is_event) { This event consists of a single serialized map containing a property +change (string)+ which indicates the type of the change ("focus", "init", -"empty", "urgent"). +"empty", "urgent"). A +current (object)+ property will be present with the +affected workspace whenever the type of event affects a workspace (otherwise, +it will be +null). -Moreover, when the change is "focus", an +old (object)+ and a +current -(object)+ properties will be present with the previous and current -workspace respectively. When the first switch occurs (when i3 focuses -the workspace visible at the beginning) there is no previous -workspace, and the +old+ property will be set to +null+. Also note -that if the previous is empty it will get destroyed when switching, -but will still be present in the "old" property. +When the change is "focus", an +old (object)+ property will be present with the +previous workspace. When the first switch occurs (when i3 focuses the +workspace visible at the beginning) there is no previous workspace, and the ++old+ property will be set to +null+. Also note that if the previous is empty +it will get destroyed when switching, but will still be present in the "old" +property. *Example:* --------------------- diff --git a/include/ipc.h b/include/ipc.h index 77dcad21..96a60a1f 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -89,11 +89,17 @@ void ipc_shutdown(void); void dump_node(yajl_gen gen, Con *con, bool inplace_restart); /** - * For the workspace "focus" event we send, along the usual "change" field, - * also the current and previous workspace, in "current" and "old" - * respectively. + * Generates a json workspace event. Returns a dynamically allocated yajl + * generator. Free with yajl_gen_free(). */ -void ipc_send_workspace_focus_event(Con *current, Con *old); +yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old); + +/** + * For the workspace events we send, along with the usual "change" field, also + * the workspace container in "current". For focus events, we send the + * previously focused workspace in "old". + */ +void ipc_send_workspace_event(const char *change, Con *current, Con *old); /** * For the window events we send, along the usual "change" field, diff --git a/src/commands.c b/src/commands.c index e87617c9..498c25c8 100644 --- a/src/commands.c +++ b/src/commands.c @@ -944,7 +944,7 @@ void cmd_append_layout(I3_CMD, char *path) { restore_open_placeholder_windows(parent); if (content == JSON_CONTENT_WORKSPACE) - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"restored\"}"); + ipc_send_workspace_event("restored", parent, NULL); cmd_output->needs_tree_render = true; } @@ -1313,7 +1313,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { create_workspace_on_output(current_output, ws->parent); /* notify the IPC listeners */ - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}"); + ipc_send_workspace_event("init", ws, NULL); } DLOG("Detaching\n"); @@ -1334,7 +1334,7 @@ void cmd_move_workspace_to_output(I3_CMD, char *name) { TAILQ_FOREACH(floating_con, &(ws->floating_head), floating_windows) floating_fix_coordinates(floating_con, &(old_content->rect), &(content->rect)); - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"move\"}"); + ipc_send_workspace_event("move", ws, NULL); if (workspace_was_visible) { /* Focus the moved workspace on the destination output. */ workspace_show(ws); @@ -1761,7 +1761,7 @@ void cmd_reload(I3_CMD) { load_configuration(conn, NULL, true); x_set_i3_atoms(); /* Send an IPC event just in case the ws names have changed */ - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"reload\"}"); + ipc_send_workspace_event("reload", NULL, NULL); /* Send an update event for the barconfig just in case it has changed */ update_barconfig(); @@ -2040,7 +2040,7 @@ void cmd_rename_workspace(I3_CMD, char *old_name, char *new_name) { cmd_output->needs_tree_render = true; ysuccess(true); - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"rename\"}"); + ipc_send_workspace_event("rename", workspace, NULL); ewmh_update_desktop_names(); ewmh_update_desktop_viewport(); ewmh_update_current_desktop(); diff --git a/src/con.c b/src/con.c index 3293a9fd..38ea0585 100644 --- a/src/con.c +++ b/src/con.c @@ -12,6 +12,7 @@ * */ #include "all.h" +#include "yajl_utils.h" static void con_on_remove_child(Con *con); @@ -1435,8 +1436,15 @@ static void con_on_remove_child(Con *con) { if (con->type == CT_WORKSPACE) { if (TAILQ_EMPTY(&(con->focus_head)) && !workspace_is_visible(con)) { LOG("Closing old workspace (%p / %s), it is empty\n", con, con->name); + yajl_gen gen = ipc_marshal_workspace_event("empty", con, NULL); tree_close(con, DONT_KILL_WINDOW, false, false); - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}"); + + const unsigned char *payload; + ylength length; + y(get_buf, &payload, &length); + ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload); + + y(free); } return; } diff --git a/src/ipc.c b/src/ipc.c index c70ec323..7dbb6632 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -1120,21 +1120,23 @@ int ipc_create_socket(const char *filename) { } /* - * For the workspace "focus" event we send, along the usual "change" field, - * also the current and previous workspace, in "current" and "old" - * respectively. + * Generates a json workspace event. Returns a dynamically allocated yajl + * generator. Free with yajl_gen_free(). */ -void ipc_send_workspace_focus_event(Con *current, Con *old) { +yajl_gen ipc_marshal_workspace_event(const char *change, Con *current, Con *old) { setlocale(LC_NUMERIC, "C"); yajl_gen gen = ygenalloc(); y(map_open); ystr("change"); - ystr("focus"); + ystr(change); ystr("current"); - dump_node(gen, current, false); + if (current == NULL) + y(null); + else + dump_node(gen, current, false); ystr("old"); if (old == NULL) @@ -1144,13 +1146,26 @@ void ipc_send_workspace_focus_event(Con *current, Con *old) { y(map_close); + setlocale(LC_NUMERIC, ""); + + return gen; +} + +/* + * For the workspace events we send, along with the usual "change" field, also + * the workspace container in "current". For focus events, we send the + * previously focused workspace in "old". + */ +void ipc_send_workspace_event(const char *change, Con *current, Con *old) { + yajl_gen gen = ipc_marshal_workspace_event(change, current, old); + const unsigned char *payload; ylength length; y(get_buf, &payload, &length); ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload); + y(free); - setlocale(LC_NUMERIC, ""); } /** diff --git a/src/move.c b/src/move.c index 9c0f310f..1999a1fe 100644 --- a/src/move.c +++ b/src/move.c @@ -128,7 +128,7 @@ static void move_to_output_directed(Con *con, direction_t direction) { tree_flatten(croot); - ipc_send_workspace_focus_event(ws, old_ws); + ipc_send_workspace_event("focus", ws, old_ws); } /* diff --git a/src/workspace.c b/src/workspace.c index 1bb619c3..a3056633 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -11,6 +11,7 @@ * */ #include "all.h" +#include "yajl_utils.h" /* Stores a copy of the name of the last used workspace for the workspace * back-and-forth switching. */ @@ -91,7 +92,7 @@ Con *workspace_get(const char *num, bool *created) { con_attach(workspace, content, false); - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"init\"}"); + ipc_send_workspace_event("init", workspace, NULL); ewmh_update_number_of_desktops(); ewmh_update_desktop_names(); ewmh_update_desktop_viewport(); @@ -409,7 +410,7 @@ static void _workspace_show(Con *workspace) { } else con_focus(next); - ipc_send_workspace_focus_event(workspace, current); + ipc_send_workspace_event("focus", workspace, current); DLOG("old = %p / %s\n", old, (old ? old->name : "(null)")); /* Close old workspace if necessary. This must be done *after* doing @@ -421,8 +422,16 @@ static void _workspace_show(Con *workspace) { /* check if this workspace is currently visible */ if (!workspace_is_visible(old)) { LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name); + yajl_gen gen = ipc_marshal_workspace_event("empty", old, NULL); tree_close(old, DONT_KILL_WINDOW, false, false); - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"empty\"}"); + + const unsigned char *payload; + ylength length; + y(get_buf, &payload, &length); + ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, (const char *)payload); + + y(free); + ewmh_update_number_of_desktops(); ewmh_update_desktop_names(); ewmh_update_desktop_viewport(); @@ -766,7 +775,7 @@ void workspace_update_urgent_flag(Con *ws) { DLOG("Workspace urgency flag changed from %d to %d\n", old_flag, ws->urgent); if (old_flag != ws->urgent) - ipc_send_event("workspace", I3_IPC_EVENT_WORKSPACE, "{\"change\":\"urgent\"}"); + ipc_send_workspace_event("urgent", ws, NULL); } /* diff --git a/testcases/t/115-ipc-workspaces.t b/testcases/t/115-ipc-workspaces.t index 29837430..2bcc6d60 100644 --- a/testcases/t/115-ipc-workspaces.t +++ b/testcases/t/115-ipc-workspaces.t @@ -23,7 +23,7 @@ $i3->connect()->recv; # Workspaces requests and events ################################ -my $focused = get_ws(focused_ws()); +my $old_ws = get_ws(focused_ws()); # Events @@ -36,15 +36,11 @@ $i3->subscribe({ workspace => sub { my ($event) = @_; if ($event->{change} eq 'init') { - $init->send(1); + $init->send($event); } elsif ($event->{change} eq 'focus') { - # Check that we have the old and new workspace - $focus->send( - $event->{current}->{name} == '2' && - $event->{old}->{name} == $focused->{name} - ); + $focus->send($event); } elsif ($event->{change} eq 'empty') { - $empty->send(1); + $empty->send($event); } } })->recv; @@ -61,8 +57,20 @@ $t = AnyEvent->timer( } ); -ok($init->recv, 'Workspace "init" event received'); -ok($focus->recv, 'Workspace "focus" event received'); -ok($empty->recv, 'Workspace "empty" event received'); +my $init_event = $init->recv; +my $focus_event = $focus->recv; +my $empty_event = $empty->recv; + +my $current_ws = get_ws(focused_ws()); + +ok($init_event, 'workspace "init" event received'); +is($init_event->{current}->{id}, $current_ws->{id}, 'the "current" property should contain the initted workspace con'); + +ok($focus_event, 'workspace "focus" event received'); +is($focus_event->{current}->{id}, $current_ws->{id}, 'the "current" property should contain the focused workspace con'); +is($focus_event->{old}->{id}, $old_ws->{id}, 'the "old" property should contain the workspace con that was focused last'); + +ok($empty_event, 'workspace "empty" event received'); +is($empty_event->{current}->{id}, $old_ws->{id}, 'the "current" property should contain the emptied workspace con'); done_testing; diff --git a/testcases/t/227-ipc-workspace-empty.t b/testcases/t/227-ipc-workspace-empty.t index 185910e8..0c67423e 100644 --- a/testcases/t/227-ipc-workspace-empty.t +++ b/testcases/t/227-ipc-workspace-empty.t @@ -50,6 +50,7 @@ subtest 'Workspace empty event upon switch', sub { my $event = $cond->recv; is($event->{change}, 'empty', '"Empty" event received upon workspace switch'); + is($event->{current}->{name}, $ws1, '"current" property should be set to the workspace con'); }; ################################################################################ @@ -116,6 +117,7 @@ subtest 'Workspace empty event upon window close', sub { my $event = $cond->recv; is($event->{change}, 'empty', '"Empty" event received upon window close'); + is($event->{current}->{name}, $ws1, '"current" property should be set to the workspace con'); }; }