diff options
author | Mattias Andrée <maandree@operamail.com> | 2014-10-22 03:54:02 +0200 |
---|---|---|
committer | Mattias Andrée <maandree@operamail.com> | 2014-10-22 03:54:02 +0200 |
commit | 84581fe66983d8805548ae260d06da4023325933 (patch) | |
tree | 03eb2bed2928ea58d208aaa8c544fe39831ae3d3 | |
parent | m (diff) | |
download | scrotty-84581fe66983d8805548ae260d06da4023325933.tar.gz scrotty-84581fe66983d8805548ae260d06da4023325933.tar.bz2 scrotty-84581fe66983d8805548ae260d06da4023325933.tar.xz |
doc
Signed-off-by: Mattias Andrée <maandree@operamail.com>
-rw-r--r-- | src/scrotty.c | 91 |
1 files changed, 77 insertions, 14 deletions
diff --git a/src/scrotty.c b/src/scrotty.c index 6552abf..850e448 100644 --- a/src/scrotty.c +++ b/src/scrotty.c @@ -37,7 +37,17 @@ #endif + #define LIST_0_9(P) P"0\n", P"1\n", P"2\n", P"3\n", P"4\n", P"5\n", P"6\n", P"7\n", P"8\n", P"9\n" + +/** + * [0, 255]-integer-to-text convertion lookup table for faster conversion from + * raw framebuffer data to the PNM format. The values have a whitespace at the + * end for even faster conversion. + * Lines should not be longer than 70 (although most programs will probably + * work even if there are longer lines), therefore the selected whitespace + * is LF (new line). + */ static const char* inttable[] = { LIST_0_9(""), LIST_0_9("1"), LIST_0_9("2"), LIST_0_9("3"), LIST_0_9("4"), @@ -51,6 +61,14 @@ static const char* inttable[] = }; +/** + * Create an PNM-file that is sent to `convert` for convertion to a compressed format + * + * @param fbname The framebuffer device + * @param fbno The number of the framebuffer + * @param fd The file descriptor connected to `convert`'s stdin + * @return Zero on success, -1 on error + */ static int save_pnm(const char* fbpath, int fbno, int fd) { char buf[PATH_MAX]; @@ -58,40 +76,55 @@ static int save_pnm(const char* fbpath, int fbno, int fd) int saved_errno, sizefd, fbfd, r, g, b; ssize_t got, off; + /* Open the file with the framebuffer's dimensions. */ sprintf(buf, SYSDIR "/class/graphics/fb%i/virtual_size", fbno); if (sizefd = open(buf, O_RDONLY), sizefd < 0) return -1; + /* Get the dimensions of the framebuffer. */ if (got = read(sizefd, buf, sizeof(buf) / sizeof(char) - 1), got < 0) return saved_errno = errno, close(sizefd), errno = saved_errno, -1; close(sizefd); + /* The read content is formated as `%{1},%{2}\n`, we want it do be `%{1} %{2}\n\0`. */ buf[got] = '\0'; *strchr(buf, ',') = ' '; - fbfd = open(fbpath, O_RDONLY); - if (fbfd < 0) + /* Open the framebuffer device for reading. */ + if (fbfd = open(fbpath, O_RDONLY), fbfd < 0) return -1; - + + /* Create a FILE*, for writing, for the image file. */ file = fdopen(fd, "w"); if (file == NULL) return saved_errno = errno, close(fbfd), errno = saved_errno, -1; + + /* The PNM image should begin with `P3\n%{width} %{height}\n%{colour max=255}\n`. + ('\n' and ' ' can be exchanged at will.) */ fprintf(file, "P3\n%s255\n", buf); + /* Convert raw framebuffer data into an PNM image. */ for (off = 0;;) { + /* Read data from the framebuffer, we may have up to 3 bytes buffered. */ got = read(fbfd, buf + off, sizeof(buf) - off * sizeof(char)); if (got < 0) return saved_errno = errno, fclose(file), close(fbfd), errno = saved_errno, -1; if (got += off, got == 0) break; + /* Convert read pixels. */ for (off = 0; off < got; off += 4) { + /* A pixel in the framebuffer is formatted as `%{blue}%{green}%{red}%{x}` in binary. */ r = buf[off + 2] & 255; g = buf[off + 1] & 255; b = buf[off + 0] & 255; + /* A pixel in the PNM image is formatted as `%{red} %{green} %{blue} ` in text. */ fprintf(file, "%s%s%s", inttable[r], inttable[g], inttable[b]); } + + /* If we read a whole number of pixels, reset the buffer, otherwise, + move the unconverted bytes to the beginning of the buffer. */ if (off != got) { off -= 4; @@ -102,6 +135,7 @@ static int save_pnm(const char* fbpath, int fbno, int fd) off = 0; } + /* Close files and return successfully. */ fflush(file); fclose(file); close(fbfd); @@ -109,51 +143,76 @@ static int save_pnm(const char* fbpath, int fbno, int fd) } +/** + * Create an image of a framebuffer + * + * @param fbname The framebuffer device + * @param imgname The pathname of the output image + * @param fbno The number of the framebuffer + * @param execname `argv[0]` from `main` + * @return Zero on success, -1 on error + */ static int save(const char* fbpath, const char* imgpath, int fbno, const char* execname) { int pipe_rw[2]; pid_t pid, reaped; int saved_errno, status; + /* Create a pipe that for sending data into the `convert` program. */ if (pipe(pipe_rw) < 0) return -1; - + + /* Fork the process, the child will exec. `convert`. */ if (pid = fork(), pid == -1) return saved_errno = errno, close(pipe_rw[0]), close(pipe_rw[1]), errno = saved_errno, -1; - + + /* Child process: */ if (pid == 0) { + /* Close the write-end of the pipe. */ close(pipe_rw[1]); + /* Turn the read-end of the into stdin. */ if (pipe_rw[0] != STDIN_FILENO) { close(STDIN_FILENO); dup2(pipe_rw[0], STDIN_FILENO); close(pipe_rw[0]); } + /* Exec. `convert` to convert the PNM-image we create to a compressed image. */ execlp("convert", "convert", DEVDIR "/stdin", imgpath, NULL); perror(execname); exit(1); } + /* Parent process: */ + + /* Close the read-end of the pipe. */ close(pipe_rw[0]); + /* Create a PNM-image of the framebuffer. */ if (save_pnm(fbpath, fbno, pipe_rw[1]) < 0) return saved_errno = errno, close(pipe_rw[1]), errno = saved_errno, -1; + /* Close the write-end of the pipe. */ close(pipe_rw[1]); - - reaped = 0; - while (reaped != pid) - { - reaped = waitpid(pid, &status, 0); - if (reaped < 0) - return -1; - } - + + /* Wait for `convert` to exit. */ + for (reaped = 0; reaped != pid;) + if (reaped = waitpid(pid, &status, 0), reaped < 0) + return -1; + + /* Return successfully if and only if `convert` did. */ return status == 0 ? 0 : -1; } +/** + * Take a screenshow of all framebuffers + * + * @param argc The number of elements in `argv` + * @param argv Command line arguments + * @return Zero on and only on success + */ int main(int argc, char* argv[]) { char fbpath[PATH_MAX]; @@ -162,12 +221,15 @@ int main(int argc, char* argv[]) (void) argc; + /* The a screenshot of each framebuffer. */ for (fbno = 0;; fbno++) { + /* Get pathname for framebuffer, and stop if we have read all existing ones. */ sprintf(fbpath, DEVDIR "/fb%i", fbno); if (access(fbpath, F_OK)) break; + /* Get output pathname. */ sprintf(imgpath, "fb%i.png", fbno); if (access(imgpath, F_OK) == 0) for (i = 2;; i++) @@ -177,6 +239,7 @@ int main(int argc, char* argv[]) break; } + /* Take a screenshot of the current framebuffer. */ if (save(fbpath, imgpath, fbno, *argv) < 0) return perror(*argv), 1; fprintf(stderr, "Saved framebuffer %i to %s\n", fbno, imgpath); |