diff options
author | Drew DeVault <sir@cmpwn.com> | 2018-05-12 09:30:00 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-05-12 09:30:00 -0400 |
commit | e2b8eac4bfbe119eaeb20622b6f5326e76aafb0b (patch) | |
tree | 01ef02dca7eb448d9a6f2eb63bf60dfcd83ee86b /swaygrab | |
parent | 50298dc9018e0045f7173a380c3d618c5f89cd43 (diff) | |
parent | 867190e0828a139fa4529cde60d033a9389df42c (diff) |
Merge branch 'master' into wlroots-970
Diffstat (limited to 'swaygrab')
-rw-r--r-- | swaygrab/json.c | 144 | ||||
-rw-r--r-- | swaygrab/main.c | 298 | ||||
-rw-r--r-- | swaygrab/swaygrab.1.txt | 76 |
3 files changed, 0 insertions, 518 deletions
diff --git a/swaygrab/json.c b/swaygrab/json.c deleted file mode 100644 index 286085c3..00000000 --- a/swaygrab/json.c +++ /dev/null @@ -1,144 +0,0 @@ -#define _XOPEN_SOURCE 700 -#include <string.h> -#include <stdio.h> -#include <stdbool.h> -#include <stdlib.h> -#include <unistd.h> -#include "log.h" -#include "ipc-client.h" -#include "swaygrab/json.h" - -static json_object *tree; - -void init_json_tree(int socketfd) { - uint32_t len = 0; - char *res = ipc_single_command(socketfd, IPC_GET_TREE, NULL, &len); - struct json_tokener *tok = json_tokener_new_ex(256); - if (!tok) { - sway_abort("Unable to get json tokener."); - } - tree = json_tokener_parse_ex(tok, res, len); - if (!tree || tok->err != json_tokener_success) { - sway_abort("Unable to parse IPC response as JSON: %s", json_tokener_error_desc(tok->err)); - } - json_object *success; - json_object_object_get_ex(tree, "success", &success); - if (success && !json_object_get_boolean(success)) { - json_object *error; - json_object_object_get_ex(tree, "error", &error); - sway_abort("IPC request failed: %s", json_object_get_string(error)); - } - json_object_put(success); - json_tokener_free(tok); -} - -void free_json_tree() { - json_object_put(tree); -} - -static bool is_focused(json_object *c) { - json_object *focused; - json_object_object_get_ex(c, "focused", &focused); - return json_object_get_boolean(focused); -} - -static json_object *get_focused_container_r(json_object *c) { - json_object *name; - json_object_object_get_ex(c, "name", &name); - if (is_focused(c)) { - return c; - } else { - json_object *nodes, *node, *child; - json_object_object_get_ex(c, "nodes", &nodes); - int i; - for (i = 0; i < json_object_array_length(nodes); i++) { - node = json_object_array_get_idx(nodes, i); - - if ((child = get_focused_container_r(node))) { - return child; - } - } - - json_object_object_get_ex(c, "floating_nodes", &nodes); - for (i = 0; i < json_object_array_length(nodes); i++) { - node = json_object_array_get_idx(nodes, i); - - if ((child = get_focused_container_r(node))) { - return child; - } - } - - } - - return NULL; -} - -json_object *get_focused_container() { - return get_focused_container_r(tree); -} - -char *get_focused_output() { - json_object *outputs, *output, *name; - json_object_object_get_ex(tree, "nodes", &outputs); - if (!outputs) { - sway_abort("Unabled to get focused output. No nodes in tree."); - } - for (int i = 0; i < json_object_array_length(outputs); i++) { - output = json_object_array_get_idx(outputs, i); - - if (get_focused_container_r(output)) { - json_object_object_get_ex(output, "name", &name); - return strdup(json_object_get_string(name)); - } - } - - return NULL; -} - -char *create_payload(const char *output, struct wlc_geometry *g) { - char *payload_str = malloc(256); - json_object *payload = json_object_new_object(); - - json_object_object_add(payload, "output", json_object_new_string(output)); - json_object_object_add(payload, "x", json_object_new_int(g->origin.x)); - json_object_object_add(payload, "y", json_object_new_int(g->origin.y)); - json_object_object_add(payload, "w", json_object_new_int(g->size.w)); - json_object_object_add(payload, "h", json_object_new_int(g->size.h)); - - snprintf(payload_str, 256, "%s", json_object_to_json_string(payload)); - return strdup(payload_str); -} - -struct wlc_geometry *get_container_geometry(json_object *container) { - struct wlc_geometry *geo = malloc(sizeof(struct wlc_geometry)); - json_object *rect, *x, *y, *w, *h; - - json_object_object_get_ex(container, "rect", &rect); - json_object_object_get_ex(rect, "x", &x); - json_object_object_get_ex(rect, "y", &y); - json_object_object_get_ex(rect, "width", &w); - json_object_object_get_ex(rect, "height", &h); - - geo->origin.x = json_object_get_int(x); - geo->origin.y = json_object_get_int(y); - geo->size.w = json_object_get_int(w); - geo->size.h = json_object_get_int(h); - - return geo; -} - -json_object *get_output_container(const char *output) { - json_object *outputs, *json_output, *name; - json_object_object_get_ex(tree, "nodes", &outputs); - - for (int i = 0; i < json_object_array_length(outputs); i++) { - json_output = json_object_array_get_idx(outputs, i); - json_object_object_get_ex(json_output, "name", &name); - - if (strcmp(json_object_get_string(name), output) == 0) { - return json_output; - } - } - - return NULL; -} diff --git a/swaygrab/main.c b/swaygrab/main.c deleted file mode 100644 index 1b699bb9..00000000 --- a/swaygrab/main.c +++ /dev/null @@ -1,298 +0,0 @@ -#define _XOPEN_SOURCE 700 -#define _POSIX_C_SOURCE 199309L -#include <stdio.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <getopt.h> -#include <unistd.h> -#include <stdint.h> -#include <math.h> -#include <time.h> -#include <sys/wait.h> -#include <json-c/json.h> -#include "log.h" -#include "ipc-client.h" -#include "util.h" -#include "swaygrab/json.h" - -void sway_terminate(int exit_code) { - exit(exit_code); -} - -void grab_and_apply_magick(const char *file, const char *payload, - int socketfd, int raw) { - uint32_t len = strlen(payload); - char *pixels = ipc_single_command(socketfd, - IPC_SWAY_GET_PIXELS, payload, &len); - uint32_t *u32pixels = (uint32_t *)(pixels + 1); - uint32_t width = u32pixels[0]; - uint32_t height = u32pixels[1]; - len -= 9; - pixels += 9; - - if (width == 0 || height == 0) { - // indicates geometry was clamped by WLC because it was outside of the output's area - json_object *obj = json_tokener_parse(payload); - json_object *output; - json_object_object_get_ex(obj, "output", &output); - const char *name = json_object_get_string(output); - json_object_put(obj); - sway_abort("Unknown output %s.", name); - } - - if (raw) { - fwrite(pixels, 1, len, stdout); - fflush(stdout); - free(pixels - 9); - return; - } - - char size[10 + 1 + 10 + 2 + 1]; // int32_t are max 10 digits - sprintf(size, "%dx%d+0", width, height); - - pid_t child; - int fd[2]; - pipe(fd); - - if ((child = fork()) < 0) { - sway_log(L_ERROR, "Swaygrab failed to fork."); - exit(EXIT_FAILURE); - } else if (child != 0) { - close(fd[0]); - write(fd[1], pixels, len); - close(fd[1]); - free(pixels - 9); - waitpid(child, NULL, 0); - } else { - close(fd[1]); - if (dup2(fd[0], 0) != 0) { - sway_log(L_ERROR, "Could not fdup the pipe"); - } - close(fd[0]); - execlp("convert", "convert", "-depth", "8", "-size", size, "rgba:-", "-flip", file, NULL); - sway_log(L_ERROR, "Swaygrab could not run convert."); - exit(EXIT_FAILURE); - } -} - -void grab_and_apply_movie_magic(const char *file, const char *payload, - int socketfd, int raw, int framerate) { - if (raw) { - sway_log(L_ERROR, "Raw capture data is not yet supported. Proceeding with ffmpeg normally."); - } - - uint32_t len = strlen(payload); - char *pixels = ipc_single_command(socketfd, - IPC_SWAY_GET_PIXELS, payload, &len); - uint32_t *u32pixels = (uint32_t *)(pixels + 1); - uint32_t width = u32pixels[0]; - uint32_t height = u32pixels[1]; - pixels += 9; - - if (width == 0 || height == 0) { - // indicates geometry was clamped by WLC because it was outside of the output's area - json_object *obj = json_tokener_parse(payload); - json_object *output; - json_object_object_get_ex(obj, "output", &output); - const char *name = json_object_get_string(output); - json_object_put(obj); - sway_abort("Unknown output %s.", name); - } - - char *ffmpeg_opts = getenv("SWAYGRAB_FFMPEG_OPTS"); - if(!ffmpeg_opts) { - ffmpeg_opts = ""; - } - - const char *fmt = "ffmpeg %s -f rawvideo -framerate %d " - "-video_size %dx%d -pixel_format argb " - "-i pipe:0 -r %d -vf vflip %s"; - char *cmd = malloc(strlen(fmt) - 8 /*args*/ - + strlen(ffmpeg_opts) + numlen(width) + numlen(height) - + numlen(framerate) * 2 + strlen(file) + 1); - sprintf(cmd, fmt, ffmpeg_opts, framerate, width, height, framerate, file); - - long ns = (long)(1000000000 * (1.0 / framerate)); - struct timespec start, finish, ts; - ts.tv_sec = 0; - - FILE *f = popen(cmd, "w"); - fwrite(pixels, 1, len, f); - free(pixels - 9); - int sleep = 0; - while (sleep != -1) { - clock_gettime(CLOCK_MONOTONIC, &start); - len = strlen(payload); - pixels = ipc_single_command(socketfd, - IPC_SWAY_GET_PIXELS, payload, &len); - pixels += 9; - len -= 9; - - fwrite(pixels, 1, len, f); - - free(pixels - 9); - clock_gettime(CLOCK_MONOTONIC, &finish); - ts.tv_nsec = ns; - double fts = (double)finish.tv_sec + 1.0e-9*finish.tv_nsec; - double sts = (double)start.tv_sec + 1.0e-9*start.tv_nsec; - long diff = (fts - sts) * 1000000000; - ts.tv_nsec = ns - diff; - if (ts.tv_nsec < 0) { - ts.tv_nsec = 0; - } - sleep = nanosleep(&ts, NULL); - } - fflush(f); - - fclose(f); - free(cmd); -} - -char *default_filename(const char *extension) { - int ext_len = strlen(extension); - int len = 28 + ext_len; // format: "2015-12-17-180040_swaygrab.ext" - char *filename = malloc(len * sizeof(char)); - time_t t = time(NULL); - - struct tm *lt = localtime(&t); - strftime(filename, len, "%Y-%m-%d-%H%M%S_swaygrab.", lt); - strncat(filename, extension, ext_len); - - return filename; -} - -int main(int argc, char **argv) { - static int capture = 0, raw = 0; - char *socket_path = NULL; - char *output = NULL; - int framerate = 30; - bool grab_focused = false; - - init_log(L_INFO); - - static struct option long_options[] = { - {"help", no_argument, NULL, 'h'}, - {"capture", no_argument, NULL, 'c'}, - {"output", required_argument, NULL, 'o'}, - {"version", no_argument, NULL, 'v'}, - {"socket", required_argument, NULL, 's'}, - {"raw", no_argument, NULL, 'r'}, - {"rate", required_argument, NULL, 'R'}, - {"focused", no_argument, NULL, 'f'}, - {0, 0, 0, 0} - }; - - const char *usage = - "Usage: swaygrab [options] [file]\n" - "\n" - " -h, --help Show help message and quit.\n" - " -c, --capture Capture video.\n" - " -o, --output <output> Output source.\n" - " -v, --version Show the version number and quit.\n" - " -s, --socket <socket> Use the specified socket.\n" - " -R, --rate <rate> Specify framerate (default: 30)\n" - " -r, --raw Write raw rgba data to stdout.\n" - " -f, --focused Grab the focused container.\n"; - - int c; - while (1) { - int option_index = 0; - c = getopt_long(argc, argv, "hco:vs:R:rf", long_options, &option_index); - if (c == -1) { - break; - } - switch (c) { - case 'f': - grab_focused = true; - break; - case 's': // Socket - socket_path = strdup(optarg); - break; - case 'r': - raw = 1; - break; - case 'o': // output - output = strdup(optarg); - break; - case 'c': - capture = 1; - break; - case 'R': // Frame rate - framerate = atoi(optarg); - break; - case 'v': - fprintf(stdout, "sway version " SWAY_VERSION "\n"); - exit(EXIT_SUCCESS); - break; - default: - fprintf(stderr, "%s", usage); - exit(EXIT_FAILURE); - } - } - - if (!socket_path) { - socket_path = get_socketpath(); - if (!socket_path) { - sway_abort("Unable to retrieve socket path"); - } - } - - char *file = NULL; - if (raw) { - if (optind >= argc + 1) { - sway_abort("Invalid usage. See `man swaygrab` %d %d", argc, optind); - } - } else if (optind < argc) { - file = strdup(argv[optind]); - } - - int socketfd = ipc_open_socket(socket_path); - free(socket_path); - - init_json_tree(socketfd); - - struct wlc_geometry *geo; - - if (grab_focused) { - output = get_focused_output(); - json_object *con = get_focused_container(); - json_object *name; - json_object_object_get_ex(con, "name", &name); - geo = get_container_geometry(con); - free(con); - } else { - if (!output) { - output = get_focused_output(); - } - geo = get_container_geometry(get_output_container(output)); - // the geometry of the output in the get_tree response is relative to a global (0, 0). - // we need it to be relative to itself, so set origin to (0, 0) always. - geo->origin.x = 0; - geo->origin.y = 0; - } - - const char *payload = create_payload(output, geo); - - free(geo); - - if (!file) { - if (!capture) { - file = default_filename("png"); - } else { - file = default_filename("webm"); - } - } - - if (!capture) { - grab_and_apply_magick(file, payload, socketfd, raw); - } else { - grab_and_apply_movie_magic(file, payload, socketfd, raw, framerate); - } - - free_json_tree(); - free(output); - free(file); - close(socketfd); - return 0; -} diff --git a/swaygrab/swaygrab.1.txt b/swaygrab/swaygrab.1.txt deleted file mode 100644 index 8faf43f5..00000000 --- a/swaygrab/swaygrab.1.txt +++ /dev/null @@ -1,76 +0,0 @@ -///// -vim:set ts=4 sw=4 tw=82 noet: -///// -:quotes.~: - -swaygrab (1) -============ - -Name ----- -swaygrab - Grab image data from the current sway session. - -Synopsis --------- -'swaygrab' [options] [file] - -Grabs pixels from an output and writes them to _file_. The image will be passed to -ImageMagick convert for processing. - -Options -------- - -*-h, --help*:: - Show help message and quit. - -*-c, \--capture*:: - Captures multiple frames as video and passes them into ffmpeg. Continues until - you send SIGTERM (ctrl+c) to swaygrab. - -*-o, \--output* <output>:: - Use the specified _output_. If no output is defined the currently focused - output in sway will be used. - -*-v, \--version*:: - Print the version (of swaymsg) and quit. - -*-s, --socket* <path>:: - Use the specified socket path. Otherwise, swaymsg will ask sway where the - socket is (which is the value of $SWAYSOCK, then of $I3SOCK). - -*-R, --rate* <rate>:: - Specify a framerate (in frames per second). Used in combination with -c. - Default is 30. Must be an integer. - -*-r, --raw*:: - Instead of invoking ImageMagick or ffmpeg, dump raw rgba data to stdout. - -*-f, --focused*:: - Capture only the currently focused container. - -Environment Variables ---------------------- -swaygrab reads the following environment variables. - -*SWAYGRAB_FFMPEG_OPTS*:: - Pass additional arguments to FFmpeg when starting a capture. - -Examples --------- - -swaygrab output.png:: - Grab the contents of currently focused output and write to output.png. - -swaygrab -c -o HDMI-A-1 output.webm:: - Capture a webm of HDMI-A-1. - -SWAYGRAB_FFMPEG_OPTS="-f alsa -i pulse" swaygrab -c:: - Capture the focused output and encode audio from the default recording - device. - -Authors -------- - -Maintained by Drew DeVault <sir@cmpwn.com>, who is assisted by other open -source contributors. For more information about sway development, see -<https://github.com/swaywm/sway>. |