Send IPC window events for focus and title changes
Patch status: needinfo
Patch by Marco Hunsicker
Long description:
This patch provides an implementation for ticket #1168: 'Add IPC
window events for focus and title changes'. To support more use cases
for client programming, i3 should send IPC events for window focus
and title changes.
With this patch, window events are send whenever a window gains focus
or changes its title.
All window events use the same, already established format. The change
field that holds the type of the window event. And the container field
that provides the serialized window container.
This patch supersedes the earlier patch 'Add IPC event for window title
changes' which only added support for window title change notification.
Unlike the old patch that added a whole new event type, this patch
merely extends the existing window event with new properties.
To apply this patch, use:
curl http://cr.i3wm.org/patch/387/raw.patch | git am
b/docs/ipc
34 |
@@ -1,7 +1,7 @@
|
35 |
IPC interface (interprocess communication)
|
36 |
==========================================
|
37 |
Michael Stapelberg <michael@i3wm.org>
|
38 |
-October 2012
|
39 |
+January 2014
|
40 |
|
41 |
This document describes how to interface with i3 from a separate process. This
|
42 |
is useful for example to remote-control i3 (to write test cases for example) or
|
43 |
@@ -632,7 +632,8 @@ mode (2)::
|
44 |
Sent whenever i3 changes its binding mode.
|
45 |
window (3)::
|
46 |
Sent when a client's window is successfully reparented (that is when i3
|
47 |
- has finished fitting it into a container).
|
48 |
+ has finished fitting it into a container), when a window received input
|
49 |
+ focus or when a window title was upated.
|
50 |
barconfig_update (4)::
|
51 |
Sent when the hidden_state or mode field in the barconfig of any bar
|
52 |
instance was updated.
|
53 |
@@ -712,14 +713,14 @@ mode is simply named default.
|
54 |
=== window event
|
55 |
|
56 |
This event consists of a single serialized map containing a property
|
57 |
-+change (string)+ which currently can indicate only that a new window
|
58 |
-has been successfully reparented (the value will be "new").
|
59 |
++change (string)+ which indicates the type of the change ("focus", "new",
|
60 |
+"title").
|
61 |
|
62 |
Additionally a +container (object)+ field will be present, which consists
|
63 |
-of the window's parent container. Be aware that the container will hold
|
64 |
-the initial name of the newly reparented window (e.g. if you run urxvt
|
65 |
-with a shell that changes the title, you will still at this point get the
|
66 |
-window title as "urxvt").
|
67 |
+of the window's parent container. Be aware that for the "new" event, the
|
68 |
+container will hold the initial name of the newly reparented window (e.g.
|
69 |
+if you run urxvt with a shell that changes the title, you will still at
|
70 |
+this point get the window title as "urxvt").
|
71 |
|
72 |
*Example:*
|
73 |
---------------------------
|
b/include/ipc.h
78 |
@@ -87,3 +87,21 @@ void dump_node(yajl_gen gen, Con *con, bool inplace_restart);
|
79 |
* respectively.
|
80 |
*/
|
81 |
void ipc_send_workspace_focus_event(Con *current, Con *old);
|
82 |
+
|
83 |
+/**
|
84 |
+ * For the window "focus" event we send, along the usual "change" field,
|
85 |
+ * also the window container, in "container".
|
86 |
+ */
|
87 |
+void ipc_send_window_focus_event(Con *con);
|
88 |
+
|
89 |
+/**
|
90 |
+ * For the window "new" event we send, along the usual "change" field,
|
91 |
+ * also the window container, in "container".
|
92 |
+ */
|
93 |
+void ipc_send_window_new_event(Con *con);
|
94 |
+
|
95 |
+/**
|
96 |
+ * For the window "title" event we send, along the usual "change" field,
|
97 |
+ * also the window container, in "container".
|
98 |
+ */
|
99 |
+void ipc_send_window_title_event(Con *con);
|
b/src/con.c
104 |
@@ -213,6 +213,8 @@ void con_focus(Con *con) {
|
105 |
assert(con != NULL);
|
106 |
DLOG("con_focus = %p\n", con);
|
107 |
|
108 |
+ bool already_focused = (con == focused);
|
109 |
+
|
110 |
/* 1: set focused-pointer to the new con */
|
111 |
/* 2: exchange the position of the container in focus stack of the parent all the way up */
|
112 |
TAILQ_REMOVE(&(con->parent->focus_head), con, focused);
|
113 |
@@ -221,15 +223,21 @@ void con_focus(Con *con) {
|
114 |
con_focus(con->parent);
|
115 |
|
116 |
focused = con;
|
117 |
+
|
118 |
+ if (con_is_leaf(con)) {
|
119 |
+ if (!already_focused)
|
120 |
+ ipc_send_window_focus_event(con);
|
121 |
+
|
122 |
/* We can't blindly reset non-leaf containers since they might have
|
123 |
* other urgent children. Therefore we only reset leafs and propagate
|
124 |
* the changes upwards via con_update_parents_urgency() which does proper
|
125 |
* checks before resetting the urgency.
|
126 |
*/
|
127 |
- if (con->urgent && con_is_leaf(con)) {
|
128 |
+ if (con->urgent) {
|
129 |
con->urgent = false;
|
130 |
con_update_parents_urgency(con);
|
131 |
workspace_update_urgent_flag(con_get_workspace(con));
|
132 |
+ }
|
133 |
}
|
134 |
}
|
135 |
|
b/src/handlers.c
140 |
@@ -22,6 +22,8 @@
|
141 |
|
142 |
int randr_base = -1;
|
143 |
|
144 |
+char *current_windowtitle = NULL;
|
145 |
+
|
146 |
/* After mapping/unmapping windows, a notify event is generated. However, we don’t want it,
|
147 |
since it’d trigger an infinite loop of switching between the different windows when
|
148 |
changing workspaces */
|
149 |
@@ -528,6 +530,30 @@ static void handle_destroy_notify_event(xcb_destroy_notify_event_t *event) {
|
150 |
handle_unmap_notify_event(&unmap);
|
151 |
}
|
152 |
|
153 |
+const char* get_title(Con *con) {
|
154 |
+ return (con != NULL && con->window != NULL && con->window->name != NULL) ? i3string_as_utf8(con->window->name) : "";
|
155 |
+}
|
156 |
+
|
157 |
+bool is_title_changed(Con *con) {
|
158 |
+ if (con == focused ) {
|
159 |
+ const char* title = get_title(con);
|
160 |
+
|
161 |
+ if (current_windowtitle != NULL && strcmp(current_windowtitle, title) == 0)
|
162 |
+ return FALSE;
|
163 |
+
|
164 |
+ FREE(current_windowtitle);
|
165 |
+
|
166 |
+ current_windowtitle = strdup(title);
|
167 |
+ }
|
168 |
+
|
169 |
+ return TRUE;
|
170 |
+}
|
171 |
+
|
172 |
+void send_window_title_event(Con *con) {
|
173 |
+ if (is_title_changed(con))
|
174 |
+ ipc_send_window_title_event(con);
|
175 |
+}
|
176 |
+
|
177 |
/*
|
178 |
* Called when a window changes its title
|
179 |
*
|
180 |
@@ -542,6 +568,8 @@ static bool handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t
|
181 |
|
182 |
x_push_changes(croot);
|
183 |
|
184 |
+ send_window_title_event(con);
|
185 |
+
|
186 |
return true;
|
187 |
}
|
188 |
|
189 |
@@ -560,6 +588,8 @@ static bool handle_windowname_change_legacy(void *data, xcb_connection_t *conn,
|
190 |
|
191 |
x_push_changes(croot);
|
192 |
|
193 |
+ send_window_title_event(con);
|
194 |
+
|
195 |
return true;
|
196 |
}
|
197 |
|
b/src/ipc.c
202 |
@@ -1056,3 +1056,39 @@ void ipc_send_workspace_focus_event(Con *current, Con *old) {
|
203 |
y(free);
|
204 |
setlocale(LC_NUMERIC, "");
|
205 |
}
|
206 |
+
|
207 |
+void ipc_send_window_event(const char *property, Con *con) {
|
208 |
+ setlocale(LC_NUMERIC, "C");
|
209 |
+ yajl_gen gen = ygenalloc();
|
210 |
+
|
211 |
+ y(map_open);
|
212 |
+
|
213 |
+ ystr("change");
|
214 |
+ ystr(property);
|
215 |
+
|
216 |
+ ystr("container");
|
217 |
+ dump_node(gen, con, false);
|
218 |
+
|
219 |
+ y(map_close);
|
220 |
+
|
221 |
+ const unsigned char *payload;
|
222 |
+ ylength length;
|
223 |
+ y(get_buf, &payload, &length);
|
224 |
+
|
225 |
+ ipc_send_event("window", I3_IPC_EVENT_WINDOW, (const char *)payload);
|
226 |
+ y(free);
|
227 |
+ setlocale(LC_NUMERIC, "");
|
228 |
+}
|
229 |
+
|
230 |
+void ipc_send_window_focus_event(Con *con) {
|
231 |
+ ipc_send_window_event("focus", con);
|
232 |
+}
|
233 |
+
|
234 |
+void ipc_send_window_new_event(Con *con) {
|
235 |
+ ipc_send_window_event("new", con);
|
236 |
+}
|
237 |
+
|
238 |
+void ipc_send_window_title_event(Con *con) {
|
239 |
+ ipc_send_window_event("title", con);
|
240 |
+}
|
241 |
+
|
b/src/manage.c
246 |
@@ -76,35 +76,6 @@ void restore_geometry(void) {
|
247 |
}
|
248 |
|
249 |
/*
|
250 |
- * The following function sends a new window event, which consists
|
251 |
- * of fields "change" and "container", the latter containing a dump
|
252 |
- * of the window's container.
|
253 |
- *
|
254 |
- */
|
255 |
-static void ipc_send_window_new_event(Con *con) {
|
256 |
- setlocale(LC_NUMERIC, "C");
|
257 |
- yajl_gen gen = ygenalloc();
|
258 |
-
|
259 |
- y(map_open);
|
260 |
-
|
261 |
- ystr("change");
|
262 |
- ystr("new");
|
263 |
-
|
264 |
- ystr("container");
|
265 |
- dump_node(gen, con, false);
|
266 |
-
|
267 |
- y(map_close);
|
268 |
-
|
269 |
- const unsigned char *payload;
|
270 |
- ylength length;
|
271 |
- y(get_buf, &payload, &length);
|
272 |
-
|
273 |
- ipc_send_event("window", I3_IPC_EVENT_WINDOW, (const char *)payload);
|
274 |
- y(free);
|
275 |
- setlocale(LC_NUMERIC, "");
|
276 |
-}
|
277 |
-
|
278 |
-/*
|
279 |
* Do some sanity checks and then reparent the window.
|
280 |
*
|
281 |
*/
|