diff options
author | Zandr Martin <zandrmartin@gmail.com> | 2016-09-18 16:41:08 -0500 |
---|---|---|
committer | Zandr Martin <zandrmartin@gmail.com> | 2016-09-18 16:41:08 -0500 |
commit | 0516dba3f68b47c0c49aa236bbba7c3c6b0b8dfd (patch) | |
tree | 78146ff883ee17c5f56610cce67ae658a2d2412d /swaygrab | |
parent | Merge pull request #907 from zandrmartin/fix-json-tree-focus-property (diff) | |
download | sway-0516dba3f68b47c0c49aa236bbba7c3c6b0b8dfd.tar.gz sway-0516dba3f68b47c0c49aa236bbba7c3c6b0b8dfd.tar.zst sway-0516dba3f68b47c0c49aa236bbba7c3c6b0b8dfd.zip |
implement "focused container" feature for swaygrab
Diffstat (limited to 'swaygrab')
-rw-r--r-- | swaygrab/CMakeLists.txt | 1 | ||||
-rw-r--r-- | swaygrab/json.c | 123 | ||||
-rw-r--r-- | swaygrab/main.c | 99 |
3 files changed, 183 insertions, 40 deletions
diff --git a/swaygrab/CMakeLists.txt b/swaygrab/CMakeLists.txt index b4aee357..a5e91e9c 100644 --- a/swaygrab/CMakeLists.txt +++ b/swaygrab/CMakeLists.txt | |||
@@ -6,6 +6,7 @@ include_directories( | |||
6 | 6 | ||
7 | add_executable(swaygrab | 7 | add_executable(swaygrab |
8 | main.c | 8 | main.c |
9 | json.c | ||
9 | ) | 10 | ) |
10 | 11 | ||
11 | target_link_libraries(swaygrab | 12 | target_link_libraries(swaygrab |
diff --git a/swaygrab/json.c b/swaygrab/json.c new file mode 100644 index 00000000..7cd73cbe --- /dev/null +++ b/swaygrab/json.c | |||
@@ -0,0 +1,123 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdbool.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <unistd.h> | ||
5 | #include "ipc-client.h" | ||
6 | #include "swaygrab/json.h" | ||
7 | |||
8 | static json_object *tree; | ||
9 | |||
10 | void init_json_tree(int socketfd) { | ||
11 | uint32_t len = 0; | ||
12 | char *res = ipc_single_command(socketfd, IPC_GET_TREE, NULL, &len); | ||
13 | tree = json_tokener_parse(res); | ||
14 | } | ||
15 | |||
16 | void free_json_tree() { | ||
17 | json_object_put(tree); | ||
18 | } | ||
19 | |||
20 | static bool is_focused(json_object *c) { | ||
21 | json_object *focused; | ||
22 | json_object_object_get_ex(c, "focused", &focused); | ||
23 | return json_object_get_boolean(focused); | ||
24 | } | ||
25 | |||
26 | static json_object *get_focused_container_r(json_object *c) { | ||
27 | json_object *name; | ||
28 | json_object_object_get_ex(c, "name", &name); | ||
29 | if (is_focused(c)) { | ||
30 | return c; | ||
31 | } else { | ||
32 | json_object *nodes, *node, *child; | ||
33 | json_object_object_get_ex(c, "nodes", &nodes); | ||
34 | int i; | ||
35 | for (i = 0; i < json_object_array_length(nodes); i++) { | ||
36 | node = json_object_array_get_idx(nodes, i); | ||
37 | |||
38 | if ((child = get_focused_container_r(node))) { | ||
39 | return child; | ||
40 | } | ||
41 | } | ||
42 | |||
43 | json_object_object_get_ex(c, "floating_nodes", &nodes); | ||
44 | for (i = 0; i < json_object_array_length(nodes); i++) { | ||
45 | node = json_object_array_get_idx(nodes, i); | ||
46 | |||
47 | if ((child = get_focused_container_r(node))) { | ||
48 | return child; | ||
49 | } | ||
50 | } | ||
51 | |||
52 | } | ||
53 | |||
54 | return NULL; | ||
55 | } | ||
56 | |||
57 | json_object *get_focused_container() { | ||
58 | return get_focused_container_r(tree); | ||
59 | } | ||
60 | |||
61 | char *get_focused_output() { | ||
62 | json_object *outputs, *output, *name; | ||
63 | json_object_object_get_ex(tree, "nodes", &outputs); | ||
64 | |||
65 | for (int i = 0; i < json_object_array_length(outputs); i++) { | ||
66 | output = json_object_array_get_idx(outputs, i); | ||
67 | |||
68 | if (get_focused_container_r(output)) { | ||
69 | json_object_object_get_ex(output, "name", &name); | ||
70 | return strdup(json_object_get_string(name)); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | return NULL; | ||
75 | } | ||
76 | |||
77 | char *create_payload(const char *output, struct wlc_geometry *g) { | ||
78 | char *payload_str = malloc(256); | ||
79 | json_object *payload = json_object_new_object(); | ||
80 | |||
81 | json_object_object_add(payload, "output", json_object_new_string(output)); | ||
82 | json_object_object_add(payload, "x", json_object_new_int(g->origin.x)); | ||
83 | json_object_object_add(payload, "y", json_object_new_int(g->origin.y)); | ||
84 | json_object_object_add(payload, "w", json_object_new_int(g->size.w)); | ||
85 | json_object_object_add(payload, "h", json_object_new_int(g->size.h)); | ||
86 | |||
87 | snprintf(payload_str, 256, "%s", json_object_to_json_string(payload)); | ||
88 | return strdup(payload_str); | ||
89 | } | ||
90 | |||
91 | struct wlc_geometry *get_container_geometry(json_object *container) { | ||
92 | struct wlc_geometry *geo = malloc(sizeof(struct wlc_geometry)); | ||
93 | json_object *rect, *x, *y, *w, *h; | ||
94 | |||
95 | json_object_object_get_ex(container, "rect", &rect); | ||
96 | json_object_object_get_ex(rect, "x", &x); | ||
97 | json_object_object_get_ex(rect, "y", &y); | ||
98 | json_object_object_get_ex(rect, "width", &w); | ||
99 | json_object_object_get_ex(rect, "height", &h); | ||
100 | |||
101 | geo->origin.x = json_object_get_int(x); | ||
102 | geo->origin.y = json_object_get_int(y); | ||
103 | geo->size.w = json_object_get_int(w); | ||
104 | geo->size.h = json_object_get_int(h); | ||
105 | |||
106 | return geo; | ||
107 | } | ||
108 | |||
109 | json_object *get_output_container(const char *output) { | ||
110 | json_object *outputs, *json_output, *name; | ||
111 | json_object_object_get_ex(tree, "nodes", &outputs); | ||
112 | |||
113 | for (int i = 0; i < json_object_array_length(outputs); i++) { | ||
114 | json_output = json_object_array_get_idx(outputs, i); | ||
115 | json_object_object_get_ex(json_output, "name", &name); | ||
116 | |||
117 | if (strcmp(json_object_get_string(name), output) == 0) { | ||
118 | return json_output; | ||
119 | } | ||
120 | } | ||
121 | |||
122 | return NULL; | ||
123 | } | ||
diff --git a/swaygrab/main.c b/swaygrab/main.c index 96290957..a88a6bcc 100644 --- a/swaygrab/main.c +++ b/swaygrab/main.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include <stdio.h> | 1 | #include <stdio.h> |
2 | #include <stdbool.h> | ||
2 | #include <stdlib.h> | 3 | #include <stdlib.h> |
3 | #include <string.h> | 4 | #include <string.h> |
4 | #include <getopt.h> | 5 | #include <getopt.h> |
@@ -10,16 +11,17 @@ | |||
10 | #include "log.h" | 11 | #include "log.h" |
11 | #include "ipc-client.h" | 12 | #include "ipc-client.h" |
12 | #include "util.h" | 13 | #include "util.h" |
14 | #include "swaygrab/json.h" | ||
13 | 15 | ||
14 | void sway_terminate(int exit_code) { | 16 | void sway_terminate(int exit_code) { |
15 | exit(exit_code); | 17 | exit(exit_code); |
16 | } | 18 | } |
17 | 19 | ||
18 | void grab_and_apply_magick(const char *file, const char *output, | 20 | void grab_and_apply_magick(const char *file, const char *payload, |
19 | int socketfd, int raw) { | 21 | int socketfd, int raw) { |
20 | uint32_t len = strlen(output); | 22 | uint32_t len = strlen(payload); |
21 | char *pixels = ipc_single_command(socketfd, | 23 | char *pixels = ipc_single_command(socketfd, |
22 | IPC_SWAY_GET_PIXELS, output, &len); | 24 | IPC_SWAY_GET_PIXELS, payload, &len); |
23 | uint32_t *u32pixels = (uint32_t *)(pixels + 1); | 25 | uint32_t *u32pixels = (uint32_t *)(pixels + 1); |
24 | uint32_t width = u32pixels[0]; | 26 | uint32_t width = u32pixels[0]; |
25 | uint32_t height = u32pixels[1]; | 27 | uint32_t height = u32pixels[1]; |
@@ -27,7 +29,13 @@ void grab_and_apply_magick(const char *file, const char *output, | |||
27 | pixels += 9; | 29 | pixels += 9; |
28 | 30 | ||
29 | if (width == 0 || height == 0) { | 31 | if (width == 0 || height == 0) { |
30 | sway_abort("Unknown output %s.", output); | 32 | // indicates geometry was clamped by WLC because it was outside of the output's area |
33 | json_object *obj = json_tokener_parse(payload); | ||
34 | json_object *output; | ||
35 | json_object_object_get_ex(obj, "output", &output); | ||
36 | const char *name = json_object_get_string(output); | ||
37 | json_object_put(obj); | ||
38 | sway_abort("Unknown output %s.", name); | ||
31 | } | 39 | } |
32 | 40 | ||
33 | if (raw) { | 41 | if (raw) { |
@@ -50,22 +58,28 @@ void grab_and_apply_magick(const char *file, const char *output, | |||
50 | free(cmd); | 58 | free(cmd); |
51 | } | 59 | } |
52 | 60 | ||
53 | void grab_and_apply_movie_magic(const char *file, const char *output, | 61 | void grab_and_apply_movie_magic(const char *file, const char *payload, |
54 | int socketfd, int raw, int framerate) { | 62 | int socketfd, int raw, int framerate) { |
55 | if (raw) { | 63 | if (raw) { |
56 | sway_log(L_ERROR, "Raw capture data is not yet supported. Proceeding with ffmpeg normally."); | 64 | sway_log(L_ERROR, "Raw capture data is not yet supported. Proceeding with ffmpeg normally."); |
57 | } | 65 | } |
58 | 66 | ||
59 | uint32_t len = strlen(output); | 67 | uint32_t len = strlen(payload); |
60 | char *pixels = ipc_single_command(socketfd, | 68 | char *pixels = ipc_single_command(socketfd, |
61 | IPC_SWAY_GET_PIXELS, output, &len); | 69 | IPC_SWAY_GET_PIXELS, payload, &len); |
62 | uint32_t *u32pixels = (uint32_t *)(pixels + 1); | 70 | uint32_t *u32pixels = (uint32_t *)(pixels + 1); |
63 | uint32_t width = u32pixels[0]; | 71 | uint32_t width = u32pixels[0]; |
64 | uint32_t height = u32pixels[1]; | 72 | uint32_t height = u32pixels[1]; |
65 | pixels += 9; | 73 | pixels += 9; |
66 | 74 | ||
67 | if (width == 0 || height == 0) { | 75 | if (width == 0 || height == 0) { |
68 | sway_abort("Unknown output %s.", output); | 76 | // indicates geometry was clamped by WLC because it was outside of the output's area |
77 | json_object *obj = json_tokener_parse(payload); | ||
78 | json_object *output; | ||
79 | json_object_object_get_ex(obj, "output", &output); | ||
80 | const char *name = json_object_get_string(output); | ||
81 | json_object_put(obj); | ||
82 | sway_abort("Unknown output %s.", name); | ||
69 | } | 83 | } |
70 | 84 | ||
71 | const char *fmt = "ffmpeg -f rawvideo -framerate %d " | 85 | const char *fmt = "ffmpeg -f rawvideo -framerate %d " |
@@ -86,9 +100,9 @@ void grab_and_apply_movie_magic(const char *file, const char *output, | |||
86 | int sleep = 0; | 100 | int sleep = 0; |
87 | while (sleep != -1) { | 101 | while (sleep != -1) { |
88 | clock_gettime(CLOCK_MONOTONIC, &start); | 102 | clock_gettime(CLOCK_MONOTONIC, &start); |
89 | len = strlen(output); | 103 | len = strlen(payload); |
90 | pixels = ipc_single_command(socketfd, | 104 | pixels = ipc_single_command(socketfd, |
91 | IPC_SWAY_GET_PIXELS, output, &len); | 105 | IPC_SWAY_GET_PIXELS, payload, &len); |
92 | pixels += 9; | 106 | pixels += 9; |
93 | len -= 9; | 107 | len -= 9; |
94 | 108 | ||
@@ -112,30 +126,6 @@ void grab_and_apply_movie_magic(const char *file, const char *output, | |||
112 | free(cmd); | 126 | free(cmd); |
113 | } | 127 | } |
114 | 128 | ||
115 | char *get_focused_output(int socketfd) { | ||
116 | uint32_t len = 0; | ||
117 | char *res = ipc_single_command(socketfd, IPC_GET_WORKSPACES, NULL, &len); | ||
118 | json_object *workspaces = json_tokener_parse(res); | ||
119 | |||
120 | int length = json_object_array_length(workspaces); | ||
121 | json_object *workspace, *focused, *json_output; | ||
122 | char *output = NULL; | ||
123 | int i; | ||
124 | for (i = 0; i < length; ++i) { | ||
125 | workspace = json_object_array_get_idx(workspaces, i); | ||
126 | json_object_object_get_ex(workspace, "focused", &focused); | ||
127 | if (json_object_get_boolean(focused) == TRUE) { | ||
128 | json_object_object_get_ex(workspace, "output", &json_output); | ||
129 | output = strdup(json_object_get_string(json_output)); | ||
130 | break; | ||
131 | } | ||
132 | } | ||
133 | |||
134 | json_object_put(workspaces); | ||
135 | free(res); | ||
136 | return output; | ||
137 | } | ||
138 | |||
139 | char *default_filename(const char *extension) { | 129 | char *default_filename(const char *extension) { |
140 | int ext_len = strlen(extension); | 130 | int ext_len = strlen(extension); |
141 | int len = 28 + ext_len; // format: "2015-12-17-180040_swaygrab.ext" | 131 | int len = 28 + ext_len; // format: "2015-12-17-180040_swaygrab.ext" |
@@ -154,6 +144,7 @@ int main(int argc, char **argv) { | |||
154 | char *socket_path = NULL; | 144 | char *socket_path = NULL; |
155 | char *output = NULL; | 145 | char *output = NULL; |
156 | int framerate = 30; | 146 | int framerate = 30; |
147 | bool grab_focused = false; | ||
157 | 148 | ||
158 | init_log(L_INFO); | 149 | init_log(L_INFO); |
159 | 150 | ||
@@ -165,6 +156,7 @@ int main(int argc, char **argv) { | |||
165 | {"socket", required_argument, NULL, 's'}, | 156 | {"socket", required_argument, NULL, 's'}, |
166 | {"raw", no_argument, NULL, 'r'}, | 157 | {"raw", no_argument, NULL, 'r'}, |
167 | {"rate", required_argument, NULL, 'R'}, | 158 | {"rate", required_argument, NULL, 'R'}, |
159 | {"focused", no_argument, NULL, 'f'}, | ||
168 | {0, 0, 0, 0} | 160 | {0, 0, 0, 0} |
169 | }; | 161 | }; |
170 | 162 | ||
@@ -177,16 +169,20 @@ int main(int argc, char **argv) { | |||
177 | " -v, --version Show the version number and quit.\n" | 169 | " -v, --version Show the version number and quit.\n" |
178 | " -s, --socket <socket> Use the specified socket.\n" | 170 | " -s, --socket <socket> Use the specified socket.\n" |
179 | " -R, --rate <rate> Specify framerate (default: 30)\n" | 171 | " -R, --rate <rate> Specify framerate (default: 30)\n" |
180 | " -r, --raw Write raw rgba data to stdout.\n"; | 172 | " -r, --raw Write raw rgba data to stdout.\n" |
173 | " -f, --focused Grab the focused container.\n"; | ||
181 | 174 | ||
182 | int c; | 175 | int c; |
183 | while (1) { | 176 | while (1) { |
184 | int option_index = 0; | 177 | int option_index = 0; |
185 | c = getopt_long(argc, argv, "hco:vs:R:r", long_options, &option_index); | 178 | c = getopt_long(argc, argv, "hco:vs:R:rf", long_options, &option_index); |
186 | if (c == -1) { | 179 | if (c == -1) { |
187 | break; | 180 | break; |
188 | } | 181 | } |
189 | switch (c) { | 182 | switch (c) { |
183 | case 'f': | ||
184 | grab_focused = true; | ||
185 | break; | ||
190 | case 's': // Socket | 186 | case 's': // Socket |
191 | socket_path = strdup(optarg); | 187 | socket_path = strdup(optarg); |
192 | break; | 188 | break; |
@@ -235,10 +231,32 @@ int main(int argc, char **argv) { | |||
235 | int socketfd = ipc_open_socket(socket_path); | 231 | int socketfd = ipc_open_socket(socket_path); |
236 | free(socket_path); | 232 | free(socket_path); |
237 | 233 | ||
238 | if (!output) { | 234 | init_json_tree(socketfd); |
239 | output = get_focused_output(socketfd); | 235 | |
236 | struct wlc_geometry *geo; | ||
237 | |||
238 | if (grab_focused) { | ||
239 | output = get_focused_output(); | ||
240 | json_object *con = get_focused_container(); | ||
241 | json_object *name; | ||
242 | json_object_object_get_ex(con, "name", &name); | ||
243 | geo = get_container_geometry(con); | ||
244 | free(con); | ||
245 | } else { | ||
246 | if (!output) { | ||
247 | output = get_focused_output(); | ||
248 | } | ||
249 | geo = get_container_geometry(get_output_container(output)); | ||
250 | // the geometry of the output in the get_tree response is relative to a global (0, 0). | ||
251 | // we need it to be relative to itself, so set origin to (0, 0) always. | ||
252 | geo->origin.x = 0; | ||
253 | geo->origin.y = 0; | ||
240 | } | 254 | } |
241 | 255 | ||
256 | const char *payload = create_payload(output, geo); | ||
257 | |||
258 | free(geo); | ||
259 | |||
242 | if (!file) { | 260 | if (!file) { |
243 | if (!capture) { | 261 | if (!capture) { |
244 | file = default_filename("png"); | 262 | file = default_filename("png"); |
@@ -248,11 +266,12 @@ int main(int argc, char **argv) { | |||
248 | } | 266 | } |
249 | 267 | ||
250 | if (!capture) { | 268 | if (!capture) { |
251 | grab_and_apply_magick(file, output, socketfd, raw); | 269 | grab_and_apply_magick(file, payload, socketfd, raw); |
252 | } else { | 270 | } else { |
253 | grab_and_apply_movie_magic(file, output, socketfd, raw, framerate); | 271 | grab_and_apply_movie_magic(file, payload, socketfd, raw, framerate); |
254 | } | 272 | } |
255 | 273 | ||
274 | free_json_tree(); | ||
256 | free(output); | 275 | free(output); |
257 | free(file); | 276 | free(file); |
258 | close(socketfd); | 277 | close(socketfd); |