aboutsummaryrefslogblamecommitdiffstats
path: root/src/scrotty.c
blob: 6552abff94613e86269dd5da9b47c3f17849fec7 (plain) (tree)


























































































































































































                                                                                                   
/**
 * scrotty — Screenshot program for Linux's TTY
 * Copyright © 2014  Mattias Andrée (maandree@member.fsf.org)
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>


#ifndef PATH_MAX
# define PATH_MAX  4096
#endif
#ifndef DEVDIR
# define DEVDIR  "/dev"
#endif
#ifndef SYSDIR
# define SYSDIR  "/sys"
#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"
static const char* inttable[] =
  {
    LIST_0_9(""),  LIST_0_9("1"), LIST_0_9("2"), LIST_0_9("3"), LIST_0_9("4"),
    LIST_0_9("5"), LIST_0_9("6"), LIST_0_9("7"), LIST_0_9("8"), LIST_0_9("9"),
    
    LIST_0_9("10"), LIST_0_9("11"), LIST_0_9("12"), LIST_0_9("13"), LIST_0_9("14"),
    LIST_0_9("15"), LIST_0_9("16"), LIST_0_9("17"), LIST_0_9("18"), LIST_0_9("19"),
    
    LIST_0_9("20"), LIST_0_9("21"), LIST_0_9("22"), LIST_0_9("23"), LIST_0_9("24"),
    "250\n", "251\n", "252\n", "253\n", "254\n", "255\n"
  };


static int save_pnm(const char* fbpath, int fbno, int fd)
{
  char buf[PATH_MAX];
  FILE* file;
  int saved_errno, sizefd, fbfd, r, g, b;
  ssize_t got, off;
  
  sprintf(buf, SYSDIR "/class/graphics/fb%i/virtual_size", fbno);
  if (sizefd = open(buf, O_RDONLY), sizefd < 0)
    return -1;
  
  if (got = read(sizefd, buf, sizeof(buf) / sizeof(char) - 1), got < 0)
    return saved_errno = errno, close(sizefd), errno = saved_errno, -1;
  close(sizefd);
  buf[got] = '\0';
  *strchr(buf, ',') = ' ';
  
  fbfd = open(fbpath, O_RDONLY);
  if (fbfd < 0)
    return -1;
  
  file = fdopen(fd, "w");
  if (file == NULL)
    return saved_errno = errno, close(fbfd), errno = saved_errno, -1;
  fprintf(file, "P3\n%s255\n", buf);
  
  for (off = 0;;)
    {
      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;
      
      for (off = 0; off < got; off += 4)
	{
	  r = buf[off + 2] & 255;
	  g = buf[off + 1] & 255;
	  b = buf[off + 0] & 255;
	  fprintf(file, "%s%s%s", inttable[r], inttable[g], inttable[b]);
	}
      if (off != got)
	{
	  off -= 4;
	  memcpy(buf, buf + off, (got - off) * sizeof(char));
	  off = got - off;
	}
      else
	off = 0;
    }
  
  fflush(file);
  fclose(file);
  close(fbfd);
  return 0;
}


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;
  
  if (pipe(pipe_rw) < 0)
    return -1;
  
  if (pid = fork(), pid == -1)
    return saved_errno = errno, close(pipe_rw[0]), close(pipe_rw[1]), errno = saved_errno, -1;
  
  if (pid == 0)
    {
      close(pipe_rw[1]);
      if (pipe_rw[0] != STDIN_FILENO)
	{
	  close(STDIN_FILENO);
	  dup2(pipe_rw[0], STDIN_FILENO);
	  close(pipe_rw[0]);
	}
      execlp("convert", "convert", DEVDIR "/stdin", imgpath, NULL);
      perror(execname);
      exit(1);
    }
  
  close(pipe_rw[0]);
  
  if (save_pnm(fbpath, fbno, pipe_rw[1]) < 0)
    return saved_errno = errno, close(pipe_rw[1]), errno = saved_errno, -1;
  
  close(pipe_rw[1]);
  
  reaped = 0;
  while (reaped != pid)
    {
      reaped = waitpid(pid, &status, 0);
      if (reaped < 0)
	return -1;
    }
  
  return status == 0 ? 0 : -1;
}


int main(int argc, char* argv[])
{
  char fbpath[PATH_MAX];
  char imgpath[PATH_MAX];
  int i, fbno;
  
  (void) argc;
  
  for (fbno = 0;; fbno++)
    {
      sprintf(fbpath, DEVDIR "/fb%i", fbno);
      if (access(fbpath, F_OK))
	break;
      
      sprintf(imgpath, "fb%i.png", fbno);
      if (access(imgpath, F_OK) == 0)
	for (i = 2;; i++)
	  {
	    sprintf(imgpath, "fb%i.png.%i", fbno, i);
	    if (access(imgpath, F_OK))
	      break;
	  }
      
      if (save(fbpath, imgpath, fbno, *argv) < 0)
	return perror(*argv), 1;
      fprintf(stderr, "Saved framebuffer %i to %s\n", fbno, imgpath);
    }
  
  return 0;
}