#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <Imlib2.h>
#include <Evas.h>
#include <Ecore.h>
#include <Ecore_File.h>
#include <Ecore_X.h>
#include <Ecore_X_Cursor.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#include "config.h"
#include "egrab.h"
/* Function Prototypes */
static void _eg_print_help(void);
static void _eg_print_version(void);
static void _eg_free_options(void);
static void _eg_get_filename(void);
static void _eg_do_shot(void);
static void _eg_do_screen(void);
static void _eg_do_window(void);
static void _eg_do_thumb(void *data);
static int _eg_cb_mouse_up(void *data, int type, void *event);
/* Global Variables */
static Options *opts = NULL;
static Ecore_X_Window input_window = 0;
static Ecore_Event_Handler *mouse_hdl = 0;
static Ecore_Timer *timer = NULL;
/* Main Program Function */
int
main(int argc, char **argv)
{
int i, qual, delay;
int tw, th, ts;
char *app, *file;
/* allocate a structure to hold our options */
opts = calloc(1, sizeof(Options));
/* parse the options provided by user */
for (i = 1; i < argc; i++)
{
if (!strcmp(argv[i], "--beep"))
opts->beep = 1;
else if ((!strcmp(argv[i], "--delay")) && (i < (argc - 1)))
{
i++;
if (sscanf(argv[i], "%i", &delay) == 1)
opts->delay = delay;
}
else if (!strcmp(argv[i], "--prompt"))
opts->prompt = 1;
else if ((!strcmp(argv[i], "--app")) && (i < (argc - 1)))
{
i++;
if (sscanf(argv[i], "%s", &app) == 1)
opts->app = evas_stringshare_add(app);
}
else if ((!strcmp(argv[i], "--thumb")) && (i < (argc - 1)))
{
i++;
if (sscanf(argv[i], "%s", &file) == 1)
{
opts->use_thumb = 1;
opts->thumb.filename = evas_stringshare_add(file);
}
}
else if ((!strcmp(argv[i], "--thumb-geom")) && (i < (argc - 1)))
{
i++;
if (sscanf(argv[i], "%ix%i", &tw, &th) == 2)
{
opts->use_thumb = 1;
opts->thumb.width = tw;
opts->thumb.height = th;
}
else if (sscanf(argv[i], "%i", &ts) == 1)
{
if (ts < 1) ts = 1;
else if (ts > 100) ts = 100;
opts->use_thumb = 1;
opts->thumb.size = ts;
}
}
else if ((!strcmp(argv[i], "--quality")) && (i < (argc - 1)))
{
i++;
if (sscanf(argv[i], "%i", &qual) == 1)
opts->quality = qual;
}
else if (!strcmp(argv[i], "--region"))
opts->region = 1;
else if (!strcmp(argv[i], "--window"))
opts->window = 1;
else if (!strcmp(argv[i], "--help"))
{
_eg_print_help();
_eg_free_options();
exit(0);
}
else if (!strcmp(argv[i], "--version"))
{
_eg_print_version();
_eg_free_options();
exit(0);
}
else if (i < argc)
{
while (i < argc)
{
if (!opts->filename)
opts->filename = evas_stringshare_add(argv[i]);
else
{
printf("Unknown Option: %s\n", argv[i]);
_eg_print_help();
_eg_free_options();
exit(0);
}
i++;
}
}
}
/* initialize ecore */
if (!ecore_init()) return -1;
/* make sure Ecore_X is initialized */
ecore_x_init(NULL);
/* set app arguments */
ecore_app_args_set(argc, (const char **)argv);
/* parse the filename for '%', or create one if no filename is given */
_eg_get_filename();
/* start screenshot process */
_eg_do_shot();
/* begin the ecore main loop which will keep our app running */
ecore_main_loop_begin();
/* free our option structure */
_eg_free_options();
/* shutdown ecore_x */
ecore_x_shutdown();
/* shutdown ecore */
ecore_shutdown();
return 0;
}
static void
_eg_print_help(void)
{
printf("Usage: egrab [OPTIONS]... FILE\n"
"Where FILE is the target for the screenshot.\n"
"If no FILE is specified, a date-stamped file will be saved in "
"the current directory.\n"
"\t--help\t\t\tDisplay this help\n"
"\t--beep\t\t\tBeep before taking screenshot\n"
"\t--delay NUM\t\tWait NUM seconds before taking screenshot\n"
"\t--prompt\t\tPrompt for FILE\n"
"\t--app APP\t\tLaunch APP after taking screenshot\n"
"\t--thumb THUMB\t\tGenerate a thumbnail as THUMB\n"
"\t--thumb-geom NUM\tGeometry to use for thumbnail\n"
"\t\t\t\tNUM can be a percentage of the original size OR\n"
"\t\t\t\tthe actual geometry to use for the thumbnail,\n"
"\t\t\t\tie: 100x100\n"
"\t--quality NUM\t\tImage quality of screenshot\n"
"\t--region\t\tSelect a specific screen region\n"
"\t--window\t\tSelect a specifc window to grab\n"
);
}
static void
_eg_print_version(void)
{
printf("egrab version "VERSION"\n");
}
static void
_eg_free_options(void)
{
/* cleanup our options structure */
if (!opts) return;
if (opts->app) evas_stringshare_del(opts->app);
if (opts->filename) evas_stringshare_del(opts->filename);
if (opts->thumb.filename) evas_stringshare_del(opts->thumb.filename);
free(opts);
opts = NULL;
}
static void
_eg_get_filename(void)
{
Ecore_List *fl = NULL;
char *dir = NULL, *ext = NULL, *file = NULL;
const char *f;
char buf[256];
struct tm *loctime;
time_t t;
int c = 0;
/* get the current time in local format */
t = time(NULL);
loctime = localtime(&t);
/* if no filename given, create one based on time */
if ((!opts->filename) || (opts->filename == NULL))
strftime(buf, sizeof(buf), "%Y-%m-%d-%H%M%S.png", loctime);
else
{
/* filename was given, check for '%' signs to
* format based on strftime */
if (strstr(opts->filename, "%"))
strftime(buf, sizeof(buf), opts->filename, loctime);
else
{
/* no '%' signs, check for files existing in the directory */
if (ecore_file_exists(opts->filename))
{
/* get the directory */
dir = ecore_file_dir_get(opts->filename);
/* get the filename */
f = ecore_file_file_get(opts->filename);
/* if these two match, then no dir was passed in,
* use current dir */
if (!strcmp(dir, f)) dir = getenv("PWD");
/* strip the extension for searches */
ext = ecore_file_strip_ext(opts->filename);
/* list files in this directory & count them */
fl = ecore_file_ls(dir);
ecore_list_first_goto(fl);
while ((file = ecore_list_next(fl)) != NULL)
{
/* skip "thumb" files in the count */
if (strstr(file, "thumb")) continue;
if (strstr(file, ext)) c++;
}
/* destroy the file list */
if (fl) ecore_list_destroy(fl);
/* assemble new filename based on count */
if (c > 0)
{
c++;
/* strip the extension from filename */
file = ecore_file_strip_ext(opts->filename);
/* get the actual extension */
ext = strrchr(opts->filename, '.');
if (!ext) ext = strdup(".png");
/* assemble new filename */
snprintf(buf, sizeof(buf), "%s%i%s", file, c, ext);
}
else return;
}
else return;
}
}
/* set the new filename */
if (opts->filename) evas_stringshare_del(opts->filename);
opts->filename = evas_stringshare_add(buf);
}
static void
_eg_do_shot(void)
{
if (opts->delay > 0)
{
}
else
{
/* are we taking a region, window or whole screen shot? */
if (opts->region)
printf("Take Region\n");
else if (opts->window)
_eg_do_window();
else
_eg_do_screen();
}
}
static void
_eg_do_screen(void)
{
Imlib_Image *im = NULL;
Imlib_Load_Error err;
Ecore_X_Display *disp;
Ecore_X_Window root;
int w, h;
/* get the display */
disp = ecore_x_display_get();
/* get the root window */
root = RootWindow(disp, 0);
/* get the size of the root window */
ecore_x_window_size_get(root, &w, &h);
/* setup imlib in preparation for shot */
imlib_context_set_display(disp);
imlib_context_set_drawable(root);
imlib_context_set_visual(DefaultVisual(disp, DefaultScreen(disp)));
/* if user wanted a beep, then beep there shall be */
if (opts->beep) XBell(disp, 0);
/* actually create the shot */
im = imlib_create_image_from_drawable(0, 0, 0, w, h, 1);
if (!im) return;
/* set imlib context to our current shot:
* this tells imlib what image we are working with */
imlib_context_set_image(im);
/* if user passed in any quality settings, attach them to the image */
if (opts->quality)
imlib_image_attach_data_value("quality", NULL, opts->quality, NULL);
/* save the image to disk */
imlib_save_image_with_error_return(opts->filename, &err);
if (err)
{
printf("Error taking screenshot: %s\n", opts->filename);
imlib_free_image_and_decache();
return;
}
/* if user wanted a thumbnail, do it */
if (opts->use_thumb) _eg_do_thumb(im);
/* cleanup imlib */
imlib_context_set_image(im);
imlib_free_image_and_decache();
/* quit the main ecore loop */
ecore_main_loop_quit();
}
static void
_eg_do_window(void)
{
Ecore_X_Display *disp;
Ecore_X_Window root;
int x, y, w, h;
/* get the display */
disp = ecore_x_display_get();
/* get the root window */
root = RootWindow(disp, 0);
/* get the size of the current root window */
ecore_x_window_geometry_get(root, &x, &y, &w, &h);
/* check if our input window already exists & delete it */
if (input_window) ecore_x_window_del(input_window);
/* create a new input window to recieve click event */
input_window = ecore_x_window_input_new(root, x, y, w, h);
/* move and resize the input window */
ecore_x_window_move(input_window, 0, 0);
ecore_x_window_resize(input_window, w, h);
/* show the input window */
ecore_x_window_show(input_window);
ecore_x_window_raise(input_window);
/* set the mouse pointer */
ecore_x_window_cursor_set(input_window, ECORE_X_CURSOR_PLUS);
ecore_x_window_cursor_show(input_window, 1);
/* grab the mouse
if (!ecore_x_pointer_confine_grab(input_window))
printf("Cannot grab mouse\n");
*/
/* setup handler to recieve click event */
mouse_hdl = ecore_event_handler_add(ECORE_X_EVENT_MOUSE_BUTTON_UP,
_eg_cb_mouse_up, NULL);
}
static void
_eg_do_thumb(void *data)
{
Imlib_Image *im = NULL, *thumb = NULL;
Imlib_Load_Error err;
char *ext = NULL;
char buf[4096];
int w, h, tw, th;
if (!(im = data)) return;
/* set imlib context to our current shot */
imlib_context_set_image(im);
/* get the image width & height */
w = imlib_image_get_width();
h = imlib_image_get_height();
/* calculate thumbnail size */
if ((opts->thumb.width > 0) && (opts->thumb.height > 0))
{
tw = w * opts->thumb.width / 100;
th = h * opts->thumb.height / 100;
}
else if (opts->thumb.size > 0)
{
tw = w * opts->thumb.size / 100;
th = h * opts->thumb.size / 100;
}
else
{
/* by default, create a 50% thumbnail */
tw = w * 50 / 100;
th = h * 50 / 100;
}
/* actually create the thumbnail of our image */
thumb = imlib_create_cropped_scaled_image(0, 0, w, h, tw, th);
if (!thumb)
printf("Error creating thumbnail: %s\n", opts->filename);
else
{
/* set imlib context to the thumbnail */
imlib_context_set_image(thumb);
/* check for user-supplied thumbnail filename */
if (!opts->thumb.filename)
{
/* no thumbname filename supplied, create one */
ext = ecore_file_strip_ext(opts->filename);
snprintf(buf, sizeof(buf), "%s-thumb", ext);
/* check for extension */
ext = strrchr(opts->filename, '.');
if (ext)
snprintf(buf, sizeof(buf), "%s%s", strdup(buf), ext);
else
snprintf(buf, sizeof(buf), "%s.png", strdup(buf));
opts->thumb.filename = evas_stringshare_add(buf);
}
/* actually save the thumbnail */
imlib_save_image_with_error_return(opts->thumb.filename, &err);
if (err)
printf("Error saving thumbnail: %s\n", opts->thumb.filename);
/* cleanup imlib */
imlib_free_image_and_decache();
}
}
static int
_eg_cb_mouse_up(void *data, int type, void *event)
{
Ecore_X_Event_Mouse_Button_Up *ev;
Ecore_X_Display *disp;
Ecore_X_Window win, root;
Imlib_Image *im = NULL;
Imlib_Load_Error err;
int x, y, w, h;
ev = event;
/* check for correct mouse button */
if (ev->button != 1) return 1;
/* check for correct window */
if (ev->win != input_window) return 1;
/* get last known pointer position */
ecore_x_pointer_last_xy_get(&x, &y);
/* delete the event handler */
ecore_event_handler_del(mouse_hdl);
mouse_hdl = 0;
/* delete the input window */
ecore_x_window_del(input_window);
input_window = 0;
/* release the pointer
ecore_x_pointer_ungrab();
*/
/* get the window which was clicked */
win = ecore_x_window_at_xy_get(x, y);
/* get the display */
disp = ecore_x_display_get();
/* E uses many parent windows...grab the correct one */
root = RootWindow(disp, 0);
while (win != root)
{
if (ecore_x_window_parent_get(win) == root) break;
win = ecore_x_window_parent_get(win);
}
/* get this window's dimensions */
ecore_x_window_geometry_get(win, &x, &y, &w, &h);
/* setup imlib in preparation for shot */
imlib_context_set_display(disp);
imlib_context_set_drawable(root);
imlib_context_set_visual(DefaultVisual(disp, DefaultScreen(disp)));
/* if user wanted a beep, then beep there shall be */
if (opts->beep) XBell(disp, 0);
/* actually create the shot */
im = imlib_create_image_from_drawable(0, x, y, w, h, 1);
if (!im) return -1;
/* set imlib context to our current shot:
* this tells imlib what image we are working with */
imlib_context_set_image(im);
/* if user passed in any quality settings, attach them to the image */
if (opts->quality)
imlib_image_attach_data_value("quality", NULL, opts->quality, NULL);
/* save the image to disk */
imlib_save_image_with_error_return(opts->filename, &err);
if (err)
{
printf("Error taking screenshot: %s\n", opts->filename);
imlib_free_image_and_decache();
return -1;
}
/* if user wanted a thumbnail, do it */
if (opts->use_thumb) _eg_do_thumb(im);
/* cleanup imlib */
imlib_context_set_image(im);
imlib_free_image_and_decache();
/* quit the main ecore loop */
ecore_main_loop_quit();
return 0;
}