aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--swaygrab.1.txt7
-rw-r--r--swaygrab/CMakeLists.txt2
-rw-r--r--swaygrab/main.c112
3 files changed, 111 insertions, 10 deletions
diff --git a/swaygrab.1.txt b/swaygrab.1.txt
index 54a1c37a..44152430 100644
--- a/swaygrab.1.txt
+++ b/swaygrab.1.txt
@@ -31,6 +31,13 @@ Options
31 Use the specified socket path. Otherwise, swaymsg will ask sway where the 31 Use the specified socket path. Otherwise, swaymsg will ask sway where the
32 socket is (which is the value of $SWAYSOCK, then of $I3SOCK). 32 socket is (which is the value of $SWAYSOCK, then of $I3SOCK).
33 33
34*-r, --rate* <rate>::
35 Specify a framerate (in frames per second). Used in combination with -c.
36 Default is 30. Must be an integer.
37
38*--raw*::
39 Instead of invoking ImageMagick or ffmpeg, dump raw rgba data to stdout.
40
34Examples 41Examples
35-------- 42--------
36 43
diff --git a/swaygrab/CMakeLists.txt b/swaygrab/CMakeLists.txt
index 5b47a694..8bc8ed8b 100644
--- a/swaygrab/CMakeLists.txt
+++ b/swaygrab/CMakeLists.txt
@@ -10,6 +10,8 @@ add_executable(swaygrab
10 ${common} 10 ${common}
11) 11)
12 12
13TARGET_LINK_LIBRARIES(swaygrab rt)
14
13install( 15install(
14 TARGETS swaygrab 16 TARGETS swaygrab
15 RUNTIME DESTINATION bin 17 RUNTIME DESTINATION bin
diff --git a/swaygrab/main.c b/swaygrab/main.c
index c05f62cd..2c169017 100644
--- a/swaygrab/main.c
+++ b/swaygrab/main.c
@@ -4,6 +4,7 @@
4#include <getopt.h> 4#include <getopt.h>
5#include <unistd.h> 5#include <unistd.h>
6#include <math.h> 6#include <math.h>
7#include <time.h>
7#include "log.h" 8#include "log.h"
8#include "ipc-client.h" 9#include "ipc-client.h"
9 10
@@ -21,19 +22,28 @@ int numlen(int n) {
21 return 1; 22 return 1;
22} 23}
23 24
24void grab_and_apply_magick(const char *file, const char *output, int socketfd) { 25void grab_and_apply_magick(const char *file, const char *output,
26 int socketfd, int raw) {
25 uint32_t len = strlen(output); 27 uint32_t len = strlen(output);
26 char *pixels = ipc_single_command(socketfd, 28 char *pixels = ipc_single_command(socketfd,
27 IPC_SWAY_GET_PIXELS, output, &len); 29 IPC_SWAY_GET_PIXELS, output, &len);
28 uint32_t *u32pixels = (uint32_t *)(pixels + 1); 30 uint32_t *u32pixels = (uint32_t *)(pixels + 1);
29 uint32_t width = u32pixels[0]; 31 uint32_t width = u32pixels[0];
30 uint32_t height = u32pixels[1]; 32 uint32_t height = u32pixels[1];
33 len -= 9;
31 pixels += 9; 34 pixels += 9;
32 35
33 if (width == 0 || height == 0) { 36 if (width == 0 || height == 0) {
34 sway_abort("Unknown output %s.", output); 37 sway_abort("Unknown output %s.", output);
35 } 38 }
36 39
40 if (raw) {
41 fwrite(pixels, 1, len, stdout);
42 fflush(stdout);
43 free(pixels - 9);
44 return;
45 }
46
37 const char *fmt = "convert -depth 8 -size %dx%d+0 rgba:- -flip %s"; 47 const char *fmt = "convert -depth 8 -size %dx%d+0 rgba:- -flip %s";
38 char *cmd = malloc(strlen(fmt) - 6 /*args*/ 48 char *cmd = malloc(strlen(fmt) - 6 /*args*/
39 + numlen(width) + numlen(height) + strlen(file) + 1); 49 + numlen(width) + numlen(height) + strlen(file) + 1);
@@ -43,13 +53,76 @@ void grab_and_apply_magick(const char *file, const char *output, int socketfd) {
43 fwrite(pixels, 1, len, f); 53 fwrite(pixels, 1, len, f);
44 fflush(f); 54 fflush(f);
45 fclose(f); 55 fclose(f);
46 free(pixels); 56 free(pixels - 9);
57 free(cmd);
58}
59
60void grab_and_apply_movie_magic(const char *file, const char *output,
61 int socketfd, int raw, int framerate) {
62 if (raw) {
63 sway_log(L_ERROR, "Raw capture data is not yet supported. Proceeding with ffmpeg normally.");
64 }
65
66 uint32_t len = strlen(output);
67 char *pixels = ipc_single_command(socketfd,
68 IPC_SWAY_GET_PIXELS, output, &len);
69 uint32_t *u32pixels = (uint32_t *)(pixels + 1);
70 uint32_t width = u32pixels[0];
71 uint32_t height = u32pixels[1];
72 pixels += 9;
73
74 if (width == 0 || height == 0) {
75 sway_abort("Unknown output %s.", output);
76 }
77
78 const char *fmt = "ffmpeg -f rawvideo -framerate %d "
79 "-video_size %dx%d -pixel_format argb "
80 "-i pipe:0 -r %d -vf vflip %s";
81 char *cmd = malloc(strlen(fmt) - 8 /*args*/
82 + numlen(width) + numlen(height) + numlen(framerate) * 2
83 + strlen(file) + 1);
84 sprintf(cmd, fmt, framerate, width, height, framerate, file);
85
86 long ns = (long)(1000000000 * (1.0 / framerate));
87 struct timespec start, finish, ts;
88 ts.tv_sec = 0;
89
90 FILE *f = popen(cmd, "w");
91 fwrite(pixels, 1, len, f);
92 free(pixels - 9);
93 int sleep = 0;
94 while (sleep != -1) {
95 clock_gettime(CLOCK_MONOTONIC, &start);
96 len = strlen(output);
97 pixels = ipc_single_command(socketfd,
98 IPC_SWAY_GET_PIXELS, output, &len);
99 pixels += 9;
100 len -= 9;
101
102 fwrite(pixels, 1, len, f);
103
104 clock_gettime(CLOCK_MONOTONIC, &finish);
105 ts.tv_nsec = ns;
106 double fts = (double)finish.tv_sec + 1.0e-9*finish.tv_nsec;
107 double sts = (double)start.tv_sec + 1.0e-9*start.tv_nsec;
108 long diff = (fts - sts) * 1000000000;
109 sway_log(L_INFO, "%f %f %ld", sts, fts, diff);
110 ts.tv_nsec = ns - diff;
111 if (ts.tv_nsec < 0) {
112 ts.tv_nsec = 0;
113 }
114 sleep = nanosleep(&ts, NULL);
115 }
116 fflush(f);
117
118 fclose(f);
47 free(cmd); 119 free(cmd);
48} 120}
49 121
50int main(int argc, char **argv) { 122int main(int argc, char **argv) {
51 static int capture = 0; 123 static int capture = 0, raw = 0;
52 char *socket_path = NULL; 124 char *socket_path = NULL;
125 int framerate = 30;
53 126
54 init_log(L_INFO); 127 init_log(L_INFO);
55 128
@@ -57,13 +130,15 @@ int main(int argc, char **argv) {
57 {"capture", no_argument, &capture, 'c'}, 130 {"capture", no_argument, &capture, 'c'},
58 {"version", no_argument, NULL, 'v'}, 131 {"version", no_argument, NULL, 'v'},
59 {"socket", required_argument, NULL, 's'}, 132 {"socket", required_argument, NULL, 's'},
133 {"raw", no_argument, &raw, 'r'},
134 {"rate", required_argument, NULL, 'R'},
60 {0, 0, 0, 0} 135 {0, 0, 0, 0}
61 }; 136 };
62 137
63 int c; 138 int c;
64 while (1) { 139 while (1) {
65 int option_index = 0; 140 int option_index = 0;
66 c = getopt_long(argc, argv, "cvs:", long_options, &option_index); 141 c = getopt_long(argc, argv, "cvs:r", long_options, &option_index);
67 if (c == -1) { 142 if (c == -1) {
68 break; 143 break;
69 } 144 }
@@ -73,6 +148,15 @@ int main(int argc, char **argv) {
73 case 's': // Socket 148 case 's': // Socket
74 socket_path = strdup(optarg); 149 socket_path = strdup(optarg);
75 break; 150 break;
151 case 'r':
152 raw = 1;
153 break;
154 case 'c':
155 capture = 1;
156 break;
157 case 'R': // Frame rate
158 framerate = atoi(optarg);
159 break;
76 case 'v': 160 case 'v':
77#if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE 161#if defined SWAY_GIT_VERSION && defined SWAY_GIT_BRANCH && defined SWAY_VERSION_DATE
78 fprintf(stdout, "sway version %s (%s, branch \"%s\")\n", SWAY_GIT_VERSION, SWAY_VERSION_DATE, SWAY_GIT_BRANCH); 162 fprintf(stdout, "sway version %s (%s, branch \"%s\")\n", SWAY_GIT_VERSION, SWAY_VERSION_DATE, SWAY_GIT_BRANCH);
@@ -91,19 +175,27 @@ int main(int argc, char **argv) {
91 } 175 }
92 } 176 }
93 177
94 if (optind >= argc - 1) { 178 char *file, *output;
95 sway_abort("Expected output and file on command line. See `man swaygrab`"); 179 if (raw) {
180 if (optind >= argc) {
181 sway_abort("Invalid usage. See `man swaygrab` %d %d", argc, optind);
182 }
183 output = argv[optind];
184 } else {
185 if (optind >= argc - 1) {
186 sway_abort("Invalid usage. See `man swaygrab`");
187 }
188 file = argv[optind + 1];
189 output = argv[optind];
96 } 190 }
97 191
98 char *file = argv[optind + 1];
99 char *output = argv[optind];
100 int socketfd = ipc_open_socket(socket_path); 192 int socketfd = ipc_open_socket(socket_path);
101 free(socket_path); 193 free(socket_path);
102 194
103 if (!capture) { 195 if (!capture) {
104 grab_and_apply_magick(file, output, socketfd); 196 grab_and_apply_magick(file, output, socketfd, raw);
105 } else { 197 } else {
106 sway_abort("Capture is not yet supported"); 198 grab_and_apply_movie_magic(file, output, socketfd, raw, framerate);
107 } 199 }
108 200
109 close(socketfd); 201 close(socketfd);