From 7227aaeb813fddb6a42da848152bfecf4a9497e8 Mon Sep 17 00:00:00 2001 From: "Carsten Haitzler (Rasterman)" Date: Wed, 18 Dec 2019 19:01:40 +0000 Subject: [PATCH] shot - move save of shot and upload to slave binary tool and tidy code this gets rid of that annoying freeze in e due to the sync save and possibly sync dns lookups in curl. slave binary handles it now and talks back to e via stdout. this also splits up the code into more files for specific purposes to make it easier to add features to and maintain. given on the todo is the ability to explicitly select a crop region and do some basic "drawing" too, this sets the stage for that and it made it easier to do the above upload/save tool. --- src/modules/shot/e_mod_main.c | 819 +------------------------------ src/modules/shot/e_mod_main.h | 28 ++ src/modules/shot/e_mod_preview.c | 291 +++++++++++ src/modules/shot/e_mod_save.c | 273 +++++++++++ src/modules/shot/e_mod_share.c | 217 ++++++++ src/modules/shot/meson.build | 18 +- src/modules/shot/upload.c | 208 ++++++++ 7 files changed, 1053 insertions(+), 801 deletions(-) create mode 100644 src/modules/shot/e_mod_main.h create mode 100644 src/modules/shot/e_mod_preview.c create mode 100644 src/modules/shot/e_mod_save.c create mode 100644 src/modules/shot/e_mod_share.c create mode 100644 src/modules/shot/upload.c diff --git a/src/modules/shot/e_mod_main.c b/src/modules/shot/e_mod_main.c index f0ff786d2..ac3f9bcab 100644 --- a/src/modules/shot/e_mod_main.c +++ b/src/modules/shot/e_mod_main.c @@ -8,807 +8,28 @@ * * @} */ -#include "e.h" -#include -#include +#include "e_mod_main.h" -#if defined(__FreeBSD__) || defined(__DragonFly__) -#include -#include -#endif - -static E_Module *shot_module = NULL; +E_Module *shot_module = NULL; static E_Action *border_act = NULL, *act = NULL; static E_Int_Menu_Augmentation *maug = NULL; static Ecore_Timer *timer, *border_timer = NULL; -static Evas_Object *win = NULL; static Evas_Object *snap = NULL; -E_Confirm_Dialog *cd = NULL; -static Evas_Object *o_bg = NULL, *o_box = NULL, *o_content = NULL; -static Evas_Object *o_event = NULL, *o_img = NULL, *o_hlist = NULL; -static int quality = 90; -static int screen = -1; -#define MAXZONES 64 -static Evas_Object *o_rectdim[MAXZONES] = { NULL }; -static Evas_Object *o_radio_all = NULL; -static Evas_Object *o_radio[MAXZONES] = { NULL }; -static Evas_Object *o_fsel = NULL; -static Evas_Object *o_label = NULL; -static Evas_Object *o_entry = NULL; -static unsigned char *fdata = NULL; -static int fsize = 0; -static Ecore_Con_Url *url_up = NULL; -static Eina_List *handlers = NULL; -static char *url_ret = NULL; -static E_Dialog *fsel_dia = NULL; static E_Client_Menu_Hook *border_hook = NULL; static E_Client *shot_ec = NULL; static E_Zone *shot_zone = NULL; static char *shot_params; -static void _file_select_ok_cb(void *data EINA_UNUSED, E_Dialog *dia); -static void _file_select_cancel_cb(void *data EINA_UNUSED, E_Dialog *dia); - -static void -_win_cancel_cb(void *data EINA_UNUSED, void *data2 EINA_UNUSED) -{ - E_FREE_FUNC(win, evas_object_del); -} - -static void -_win_delete_cb() -{ - win = NULL; -} - -static void -_on_focus_cb(void *data EINA_UNUSED, Evas_Object *obj) -{ - if (obj == o_content) e_widget_focused_object_clear(o_box); - else if (o_content) e_widget_focused_object_clear(o_content); -} - -static void -_key_down_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event) -{ - Evas_Event_Key_Down *ev = event; - - if (!strcmp(ev->key, "Tab")) - { - if (evas_key_modifier_is_set(evas_key_modifier_get(evas_object_evas_get(win)), "Shift")) - { - if (e_widget_focus_get(o_box)) - { - if (!e_widget_focus_jump(o_box, 0)) - { - e_widget_focus_set(o_content, 0); - if (!e_widget_focus_get(o_content)) - e_widget_focus_set(o_box, 0); - } - } - else - { - if (!e_widget_focus_jump(o_content, 0)) - e_widget_focus_set(o_box, 0); - } - } - else - { - if (e_widget_focus_get(o_box)) - { - if (!e_widget_focus_jump(o_box, 1)) - { - e_widget_focus_set(o_content, 1); - if (!e_widget_focus_get(o_content)) - e_widget_focus_set(o_box, 1); - } - } - else - { - if (!e_widget_focus_jump(o_content, 1)) - e_widget_focus_set(o_box, 1); - } - } - } - else if (((!strcmp(ev->key, "Return")) || - (!strcmp(ev->key, "KP_Enter")) || - (!strcmp(ev->key, "space")))) - { - Evas_Object *o = NULL; - - if ((o_content) && (e_widget_focus_get(o_content))) - o = e_widget_focused_object_get(o_content); - else - o = e_widget_focused_object_get(o_box); - if (o) e_widget_activate(o); - } - else if (!strcmp(ev->key, "Escape")) - _win_cancel_cb(NULL, NULL); -} - -static void -_save_key_down_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event) -{ - Evas_Event_Key_Down *ev = event; - - if ((!strcmp(ev->key, "Return")) || (!strcmp(ev->key, "KP_Enter"))) - _file_select_ok_cb(NULL, fsel_dia); - else if (!strcmp(ev->key, "Escape")) - _file_select_cancel_cb(NULL, fsel_dia); -} - -static void -_screen_change_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) -{ - Eina_List *l; - E_Zone *z; - - EINA_LIST_FOREACH(e_comp->zones, l, z) - { - if (screen == -1) - evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 0); - else if (screen == (int)z->num) - evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 0); - else - evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 200); - } -} - -static void -_save_to(const char *file) -{ - char opts[256]; - - if (eina_str_has_extension(file, ".png")) - snprintf(opts, sizeof(opts), "compress=%i", 9); - else - snprintf(opts, sizeof(opts), "quality=%i", quality); - if (screen == -1) - { - if (o_img) - { - if (!evas_object_image_save(o_img, file, NULL, opts)) - e_util_dialog_show(_("Error saving screenshot file"), - _("Path: %s"), file); - } - } - else - { - Evas_Object *o; - Eina_List *l; - E_Zone *z = NULL; - - EINA_LIST_FOREACH(e_comp->zones, l, z) - { - if (screen == (int)z->num) break; - z = NULL; - } - if (z) - { - unsigned char *src, *dst, *s, *d; - int sstd, dstd, y; - - o = evas_object_image_add(evas_object_evas_get(o_img)); - evas_object_image_colorspace_set(o, EVAS_COLORSPACE_ARGB8888); - evas_object_image_alpha_set(o, EINA_FALSE); - evas_object_image_size_set(o, z->w, z->h); - dstd = evas_object_image_stride_get(o); - src = evas_object_image_data_get(o_img, EINA_FALSE); - sstd = evas_object_image_stride_get(o_img); - dst = evas_object_image_data_get(o, EINA_TRUE); - if ((dstd > 0) && (sstd > 0) && (src) && (dst)) - { - d = dst; - for (y = z->y; y < z->y + z->h; y++) - { - s = src + (sstd * y) + (z->x * 4); - memcpy(d, s, z->w * 4); - d += dstd; - } - if (!evas_object_image_save(o, file, NULL, opts)) - e_util_dialog_show(_("Error saving screenshot file"), - _("Path: %s"), file); - } - - evas_object_del(o); - } - } -} - -static void -_file_select_ok_cb(void *data EINA_UNUSED, E_Dialog *dia) -{ - const char *file; - - dia = fsel_dia; - file = e_widget_fsel_selection_path_get(o_fsel); - if ((!file) || (!file[0]) || - ((!eina_str_has_extension(file, ".jpg")) && - (!eina_str_has_extension(file, ".png")))) - { - e_util_dialog_show - (_("Error - Unknown format"), - _("File has an unspecified extension." - "Please use '.jpg' or '.png' extensions" - "only as other formats are not" - "supported currently.")); - return; - } - _save_to(file); - if (dia) e_util_defer_object_del(E_OBJECT(dia)); - E_FREE_FUNC(win, evas_object_del); - fsel_dia = NULL; -} - -static void -_file_select_cancel_cb(void *data EINA_UNUSED, E_Dialog *dia) -{ - if (dia) e_util_defer_object_del(E_OBJECT(dia)); - fsel_dia = NULL; -} - -static void -_file_select_del_cb(void *d EINA_UNUSED) -{ - fsel_dia = NULL; -} - -static void -_win_save_cb(void *data EINA_UNUSED, void *data2 EINA_UNUSED) -{ - E_Dialog *dia; - Evas_Object *o; - Evas_Coord mw, mh; - int mask = 0; - time_t tt; - struct tm *tm; - char buf[PATH_MAX]; - - time(&tt); - tm = localtime(&tt); - if (quality == 100) - strftime(buf, sizeof(buf), "shot-%Y-%m-%d_%H-%M-%S.png", tm); - else - strftime(buf, sizeof(buf), "shot-%Y-%m-%d_%H-%M-%S.jpg", tm); - fsel_dia = dia = e_dialog_new(NULL, "E", "_e_shot_fsel"); - e_dialog_resizable_set(dia, EINA_TRUE); - e_dialog_title_set(dia, _("Select screenshot save location")); - o = e_widget_fsel_add(evas_object_evas_get(dia->win), "desktop", "/", - buf, NULL, NULL, NULL, NULL, NULL, 1); - e_object_del_attach_func_set(E_OBJECT(dia), _file_select_del_cb); - e_widget_fsel_window_set(o, dia->win); - o_fsel = o; - evas_object_show(o); - e_widget_size_min_get(o, &mw, &mh); - e_dialog_content_set(dia, o, mw, mh); - e_dialog_button_add(dia, _("Save"), NULL, - _file_select_ok_cb, NULL); - e_dialog_button_add(dia, _("Cancel"), NULL, - _file_select_cancel_cb, NULL); - elm_win_center(dia->win, 1, 1); - o = evas_object_rectangle_add(evas_object_evas_get(dia->win)); - if (!evas_object_key_grab(o, "Return", mask, ~mask, 0)) - printf("grab err\n"); - mask = 0; - if (!evas_object_key_grab(o, "KP_Enter", mask, ~mask, 0)) - printf("grab err\n"); - mask = 0; - if (!evas_object_key_grab(o, "Escape", mask, ~mask, 0)) - printf("grab err\n"); - evas_object_event_callback_add(o, EVAS_CALLBACK_KEY_DOWN, - _save_key_down_cb, NULL); - e_dialog_show(dia); -} - -static void -_share_done(void) -{ - E_FREE_LIST(handlers, ecore_event_handler_del); - o_label = NULL; - E_FREE(url_ret); - E_FREE_FUNC(url_up, ecore_con_url_free); - url_up = NULL; -} - -static void -_upload_ok_cb(void *data EINA_UNUSED, E_Dialog *dia) -{ - // ok just hides dialog and does background upload - o_label = NULL; - if (dia) e_util_defer_object_del(E_OBJECT(dia)); - if (!win) return; - E_FREE_FUNC(win, evas_object_del); -} - -static void -_upload_cancel_cb(void *data EINA_UNUSED, E_Dialog *dia) -{ - o_label = NULL; - if (dia) e_util_defer_object_del(E_OBJECT(dia)); - E_FREE_FUNC(win, evas_object_del); - _share_done(); -} - -static Eina_Bool -_upload_data_cb(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event) -{ - Ecore_Con_Event_Url_Data *ev = event; - - if (ev->url_con != url_up) return EINA_TRUE; - if ((o_label) && (ev->size < 1024)) - { - char *txt = alloca(ev->size + 1); - - memcpy(txt, ev->data, ev->size); - txt[ev->size] = 0; -/* - printf("GOT %i bytes: '%s'\n", ev->size, txt); - int i; - for (i = 0; i < ev->size; i++) printf("%02x.", ev->data[i]); - printf("\n"); - */ - if (!url_ret) url_ret = strdup(txt); - else - { - char *n; - - n = malloc(strlen(url_ret) + ev->size + 1); - if (n) - { - strcpy(n, url_ret); - free(url_ret); - strcat(n, txt); - url_ret = n; - } - } - } - return EINA_FALSE; -} - -static Eina_Bool -_upload_progress_cb(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event) -{ - size_t total, current; - Ecore_Con_Event_Url_Progress *ev = event; - - if (ev->url_con != url_up) return ECORE_CALLBACK_RENEW; - total = ev->up.total; - current = ev->up.now; - if (o_label) - { - char buf[1024]; - char *buf_now, *buf_total; - - buf_now = e_util_size_string_get(current); - buf_total = e_util_size_string_get(total); - snprintf(buf, sizeof(buf), _("Uploaded %s / %s"), buf_now, buf_total); - E_FREE(buf_now); - E_FREE(buf_total); - e_widget_label_text_set(o_label, buf); - } - return ECORE_CALLBACK_RENEW; -} - -static Eina_Bool -_upload_complete_cb(void *data, int ev_type EINA_UNUSED, void *event) -{ - int status; - Ecore_Con_Event_Url_Complete *ev = event; - - if (ev->url_con != url_up) return ECORE_CALLBACK_RENEW; - status = ev->status; - - if (data) - e_widget_disabled_set(data, 1); - if (status != 200) - { - e_util_dialog_show(_("Error - Upload Failed"), - _("Upload failed with status code:" - "%i"), status); - _share_done(); - return EINA_FALSE; - } - if ((o_entry) && (url_ret)) - e_widget_entry_text_set(o_entry, url_ret); - _share_done(); - return ECORE_CALLBACK_RENEW; -} - -static void -_win_share_del(void *data EINA_UNUSED) -{ - if (handlers) - ecore_event_handler_data_set(eina_list_last_data_get(handlers), NULL); - _upload_cancel_cb(NULL, NULL); - if (cd) e_object_del(E_OBJECT(cd)); -} - -static void -_win_share_cb(void *data EINA_UNUSED, void *data2 EINA_UNUSED) -{ - E_Dialog *dia; - Evas_Object *o, *ol; - Evas_Coord mw, mh; - char buf[PATH_MAX]; - FILE *f; - int i, fd = -1; - - srand(time(NULL)); - for (i = 0; i < 10240; i++) - { - int v = rand(); - - if (quality == 100) - snprintf(buf, sizeof(buf), "/tmp/e-shot-%x.png", v); - else - snprintf(buf, sizeof(buf), "/tmp/e-shot-%x.jpg", v); - fd = open(buf, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); - if (fd >= 0) break; - } - if (fd < 0) - { - e_util_dialog_show(_("Error - Can't create file"), - _("Cannot create temporary file '%s': %s"), - buf, strerror(errno)); - E_FREE_FUNC(win, evas_object_del); - return; - } - _save_to(buf); - E_FREE_FUNC(win, evas_object_del); - f = fdopen(fd, "rb"); - if (!f) - { - e_util_dialog_show(_("Error - Can't open file"), - _("Cannot open temporary file '%s': %s"), - buf, strerror(errno)); - return; - } - fseek(f, 0, SEEK_END); - fsize = ftell(f); - if (fsize < 1) - { - e_util_dialog_show(_("Error - Bad size"), - _("Cannot get size of file '%s'"), - buf); - fclose(f); - return; - } - rewind(f); - free(fdata); - fdata = malloc(fsize); - if (!fdata) - { - e_util_dialog_show(_("Error - Can't allocate memory"), - _("Cannot allocate memory for picture: %s"), - strerror(errno)); - fclose(f); - return; - } - if (fread(fdata, fsize, 1, f) != 1) - { - e_util_dialog_show(_("Error - Can't read picture"), - _("Cannot read picture")); - E_FREE(fdata); - fclose(f); - return; - } - fclose(f); - ecore_file_unlink(buf); - - _share_done(); - - E_LIST_HANDLER_APPEND(handlers, ECORE_CON_EVENT_URL_DATA, - _upload_data_cb, NULL); - E_LIST_HANDLER_APPEND(handlers, ECORE_CON_EVENT_URL_PROGRESS, - _upload_progress_cb, NULL); - - url_up = ecore_con_url_new("https://www.enlightenment.org/shot.php"); - // why use http 1.1? proxies like squid don't handle 1.1 posts with expect - // like curl uses by default, so go to 1.0 and this all works dandily - // out of the box - ecore_con_url_http_version_set(url_up, ECORE_CON_URL_HTTP_VERSION_1_0); - ecore_con_url_post(url_up, fdata, fsize, "application/x-e-shot"); - dia = e_dialog_new(NULL, "E", "_e_shot_share"); - e_dialog_resizable_set(dia, EINA_TRUE); - e_dialog_title_set(dia, _("Uploading screenshot")); - - o = e_widget_list_add(evas_object_evas_get(dia->win), 0, 0); - ol = o; - - o = e_widget_label_add(evas_object_evas_get(dia->win), _("Uploading ...")); - o_label = o; - e_widget_list_object_append(ol, o, 0, 0, 0.5); - - o = e_widget_label_add(evas_object_evas_get(dia->win), - _("Screenshot is available at this location:")); - e_widget_list_object_append(ol, o, 0, 0, 0.5); - - o = e_widget_entry_add(dia->win, NULL, NULL, NULL, NULL); - o_entry = o; - e_widget_list_object_append(ol, o, 1, 0, 0.5); - - e_widget_size_min_get(ol, &mw, &mh); - e_dialog_content_set(dia, ol, mw, mh); - e_dialog_button_add(dia, _("Hide"), NULL, _upload_ok_cb, NULL); - e_dialog_button_add(dia, _("Cancel"), NULL, _upload_cancel_cb, NULL); - e_object_del_attach_func_set(E_OBJECT(dia), _win_share_del); - E_LIST_HANDLER_APPEND(handlers, ECORE_CON_EVENT_URL_COMPLETE, - _upload_complete_cb, - eina_list_last_data_get(dia->buttons)); - elm_win_center(dia->win, 1, 1); - e_dialog_show(dia); -} - -static void -_win_share_confirm_del(void *d EINA_UNUSED) -{ - cd = NULL; -} - -static void -_win_share_confirm_yes(void *d EINA_UNUSED) -{ - _win_share_cb(NULL, NULL); -} - -static void -_win_share_confirm_cb(void *d EINA_UNUSED, void *d2 EINA_UNUSED) -{ - if (cd) return; - cd = e_confirm_dialog_show(_("Confirm Share"), NULL, - _("This image will be uploaded" - "to enlightenment.org. It will be publicly visible."), - _("Confirm"), _("Cancel"), - _win_share_confirm_yes, NULL, - NULL, NULL, _win_share_confirm_del, NULL); -} - -static void -_rect_down_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) -{ - Evas_Event_Mouse_Down *ev = event_info; - Eina_List *l; - E_Zone *z; - - if (ev->button != 1) return; - - e_widget_radio_toggle_set(o_radio_all, 0); - EINA_LIST_FOREACH(e_comp->zones, l, z) - { - if (obj == o_rectdim[z->num]) - { - screen = z->num; - e_widget_radio_toggle_set(o_radio[z->num], 1); - } - else - e_widget_radio_toggle_set(o_radio[z->num], 0); - } - - EINA_LIST_FOREACH(e_comp->zones, l, z) - { - if (screen == -1) - evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 0); - else if (screen == (int)z->num) - evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 0); - else - evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 200); - } -} - -static void -_save_dialog_show(E_Zone *zone, E_Client *ec, const char *params, void *dst, int sw, int sh) -{ - Evas *evas, *evas2; - Evas_Object *o, *oa, *op, *ol; - Evas_Modifier_Mask mask; - E_Radio_Group *rg; - int w, h; - char smode[128], squal[128], sscreen[128]; - - win = elm_win_add(NULL, NULL, ELM_WIN_BASIC); - - evas = evas_object_evas_get(win); - elm_win_title_set(win, _("Where to put Screenshot...")); - evas_object_event_callback_add(win, EVAS_CALLBACK_DEL, _win_delete_cb, NULL); - elm_win_center(win, 1, 1); - ecore_evas_name_class_set(e_win_ee_get(win), "E", "_shot_dialog"); - - o = elm_layout_add(e_win_evas_win_get(evas)); - elm_win_resize_object_add(win, o); - o_bg = o;; - e_theme_edje_object_set(o, "base/theme/dialog", "e/widgets/dialog/main"); - evas_object_show(o); - - o = e_widget_list_add(evas, 0, 0); - o_content = o; - elm_object_part_content_set(o_bg, "e.swallow.content", o); - evas_object_show(o); - - w = sw / 4; - if (w < 220) w = 220; - h = (w * sh) / sw; - - o = e_widget_aspect_add(evas, w, h); - oa = o; - o = e_widget_preview_add(evas, w, h); - op = o; - - evas2 = e_widget_preview_evas_get(op); - - o = evas_object_image_filled_add(evas2); - o_img = o; - evas_object_image_colorspace_set(o, EVAS_COLORSPACE_ARGB8888); - evas_object_image_alpha_set(o, EINA_FALSE); - evas_object_image_size_set(o, sw, sh); - evas_object_image_data_copy_set(o, dst); - - evas_object_image_data_update_add(o, 0, 0, sw, sh); - e_widget_preview_extern_object_set(op, o); - evas_object_show(o); - - evas_object_show(op); - evas_object_show(oa); - - e_widget_aspect_child_set(oa, op); - e_widget_list_object_append(o_content, oa, 0, 0, 0.5); - - o = e_widget_list_add(evas, 1, 1); - o_hlist = o; - - o = e_widget_framelist_add(evas, _("Quality"), 0); - ol = o; - - rg = e_widget_radio_group_new(&quality); - o = e_widget_radio_add(evas, _("Perfect"), 100, rg); - e_widget_framelist_object_append(ol, o); - o = e_widget_radio_add(evas, _("High"), 90, rg); - e_widget_framelist_object_append(ol, o); - o = e_widget_radio_add(evas, _("Medium"), 70, rg); - e_widget_framelist_object_append(ol, o); - o = e_widget_radio_add(evas, _("Low"), 50, rg); - e_widget_framelist_object_append(ol, o); - - e_widget_list_object_append(o_hlist, ol, 1, 0, 0.5); - - if (zone) - { - screen = -1; - if (eina_list_count(e_comp->zones) > 1) - { - Eina_List *l; - E_Zone *z; - int i; - - o = e_widget_framelist_add(evas, _("Screen"), 0); - ol = o; - - rg = e_widget_radio_group_new(&screen); - o = e_widget_radio_add(evas, _("All"), -1, rg); - o_radio_all = o; - evas_object_smart_callback_add(o, "changed", - _screen_change_cb, NULL); - e_widget_framelist_object_append(ol, o); - i = 0; - EINA_LIST_FOREACH(e_comp->zones, l, z) - { - char buf[32]; - - if (z->num >= MAXZONES) continue; - snprintf(buf, sizeof(buf), "%i", z->num); - o = e_widget_radio_add(evas, buf, z->num, rg); - o_radio[z->num] = o; - evas_object_smart_callback_add(o, "changed", - _screen_change_cb, NULL); - e_widget_framelist_object_append(ol, o); - - o = evas_object_rectangle_add(evas2); - evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN, - _rect_down_cb, NULL); - o_rectdim[z->num] = o; - evas_object_color_set(o, 0, 0, 0, 0); - evas_object_show(o); - evas_object_geometry_get(o_img, NULL, NULL, &w, &h); - evas_object_move(o, (z->x * w) / sw, (z->y * h) / sh); - evas_object_resize(o, (z->w * w) / sw, (z->h * h) / sh); - i++; - } - - e_widget_list_object_append(o_hlist, ol, 1, 0, 0.5); - } - - } - e_widget_list_object_append(o_content, o_hlist, 0, 0, 0.5); - - o = o_content; - e_widget_size_min_get(o, &w, &h); - evas_object_size_hint_min_set(o, w, h); - elm_object_part_content_set(o_bg, "e.swallow.content", o); - evas_object_show(o); - - /////////////////////////////////////////////////////////////////////// - - o = e_widget_list_add(evas, 1, 1); - o_box = o; - e_widget_on_focus_hook_set(o, _on_focus_cb, NULL); - elm_object_part_content_set(o_bg, "e.swallow.buttons", o); - - o = e_widget_button_add(evas, _("Save"), NULL, _win_save_cb, win, NULL); - e_widget_list_object_append(o_box, o, 1, 0, 0.5); - o = e_widget_button_add(evas, _("Share"), NULL, - _win_share_confirm_cb, win, NULL); - e_widget_list_object_append(o_box, o, 1, 0, 0.5); - o = e_widget_button_add(evas, _("Cancel"), NULL, _win_cancel_cb, win, NULL); - e_widget_list_object_append(o_box, o, 1, 0, 0.5); - - o = o_box; - e_widget_size_min_get(o, &w, &h); - evas_object_size_hint_min_set(o, w, h); - elm_object_part_content_set(o_bg, "e.swallow.buttons", o); - - o = evas_object_rectangle_add(evas); - o_event = o; - mask = 0; - if (!evas_object_key_grab(o, "Tab", mask, ~mask, 0)) printf("grab err\n"); - mask = evas_key_modifier_mask_get(evas, "Shift"); - if (!evas_object_key_grab(o, "Tab", mask, ~mask, 0)) printf("grab err\n"); - mask = 0; - if (!evas_object_key_grab(o, "Return", mask, ~mask, 0)) printf("grab err\n"); - mask = 0; - if (!evas_object_key_grab(o, "KP_Enter", mask, ~mask, 0)) printf("grab err\n"); - mask = 0; - if (!evas_object_key_grab(o, "space", mask, ~mask, 0)) printf("grab err\n"); - mask = 0; - if (!evas_object_key_grab(o, "Escape", mask, ~mask, 0)) printf("grab err\n"); - evas_object_event_callback_add(o, EVAS_CALLBACK_KEY_DOWN, _key_down_cb, NULL); - - evas_object_size_hint_min_get(o_bg, &w, &h); - evas_object_resize(o_bg, w, h); - evas_object_resize(win, w, h); - evas_object_size_hint_min_set(win, w, h); - evas_object_size_hint_max_set(win, 99999, 99999); - - if ((params) && - (sscanf(params, "%100s %100s %100s", smode, squal, sscreen) == 3)) - { - screen = -1; - if ((zone) && (!strcmp(sscreen, "current"))) screen = zone->num; - else if (!strcmp(sscreen, "all")) screen = -1; - else screen = atoi(sscreen); - - quality = 90; - if (!strcmp(squal, "perfect")) quality = 100; - else if (!strcmp(squal, "high")) quality = 90; - else if (!strcmp(squal, "medium")) quality = 70; - else if (!strcmp(squal, "low")) quality = 50; - else quality = atoi(squal); - - if (!strcmp(smode, "save")) _win_save_cb(NULL, NULL); - else if (!strcmp(smode, "share")) _win_share_cb(NULL, NULL); - } - else - { - evas_object_show(win); - e_win_client_icon_set(win, "screenshot"); - - if (!e_widget_focus_get(o_bg)) e_widget_focus_set(o_box, 1); - if (ec) - { - E_Client *c = e_win_client_get(win); - - if (c) evas_object_layer_set(c->frame, ec->layer); - } - } -} - static void _shot_post(void *buffer EINA_UNUSED, Evas *e EINA_UNUSED, void *event EINA_UNUSED) { int w, h; evas_object_geometry_get(snap, NULL, NULL, &w, &h); evas_event_callback_del(e_comp->evas, EVAS_CALLBACK_RENDER_POST, _shot_post); - _save_dialog_show(shot_zone, shot_ec, shot_params, (void*)evas_object_image_data_get(snap, 0), w, h); + preview_dialog_show(shot_zone, shot_ec, shot_params, + (void *)evas_object_image_data_get(snap, 0), w, h); E_FREE_FUNC(snap, evas_object_del); shot_ec = NULL; shot_zone = NULL; @@ -819,7 +40,7 @@ static void _shot_now(E_Zone *zone, E_Client *ec, const char *params) { int x, y, w, h; - if ((win) || (url_up) || snap) return; + if (preview_have() || save_have() || share_have() || (snap)) return; if ((!zone) && (!ec)) return; if (zone) { @@ -852,7 +73,9 @@ _shot_now(E_Zone *zone, E_Client *ec, const char *params) } if (eina_streq(ecore_evas_engine_name_get(e_comp->ee), "buffer")) { - _save_dialog_show(zone, ec, params, (void*)ecore_evas_buffer_pixels_get(e_comp->ee), w, h); + preview_dialog_show(zone, ec, params, + (void *)ecore_evas_buffer_pixels_get(e_comp->ee), + w, h); return; } shot_ec = ec; @@ -979,12 +202,9 @@ _e_mod_action_cb(E_Object *obj, const char *params) if (obj) { - if (obj->type == E_COMP_TYPE) - zone = e_zone_current_get(); - else if (obj->type == E_ZONE_TYPE) - zone = ((void *)obj); - else - zone = e_zone_current_get(); + if (obj->type == E_COMP_TYPE) zone = e_zone_current_get(); + else if (obj->type == E_ZONE_TYPE) zone = ((void *)obj); + else zone = e_zone_current_get(); } if (!zone) zone = e_zone_current_get(); if (!zone) return; @@ -993,9 +213,8 @@ _e_mod_action_cb(E_Object *obj, const char *params) e_object_ref(E_OBJECT(zone)); ds->zone = zone; ds->params = params ? strdup(params) : NULL; - /* forced main loop iteration in screenshots causes bugs if the action - * executes immediately - */ + // forced main loop iteration in screenshots causes bugs if the action + // executes immediately ecore_job_add(_delayed_shot, ds); } @@ -1010,9 +229,11 @@ _bd_hook(void *d EINA_UNUSED, E_Client *ec) if (ec->iconic || (ec->desk != e_desk_current_get(ec->zone))) return; m = ec->border_menu; - /* position menu item just before first separator */ + // position menu item just before first separator EINA_LIST_FOREACH(m->items, l, mi) - if (mi->separator) break; + { + if (mi->separator) break; + } if ((!mi) || (!mi->separator)) return; l = eina_list_prev(l); mi = eina_list_data_get(l); @@ -1083,9 +304,9 @@ e_modapi_init(E_Module *m) E_API int e_modapi_shutdown(E_Module *m EINA_UNUSED) { - _share_done(); - E_FREE_FUNC(win, evas_object_del); - E_FREE_FUNC(cd, e_object_del); + share_abort(); + save_abort(); + preview_abort(); if (timer) { ecore_timer_del(timer); diff --git a/src/modules/shot/e_mod_main.h b/src/modules/shot/e_mod_main.h new file mode 100644 index 000000000..1a3ce1a09 --- /dev/null +++ b/src/modules/shot/e_mod_main.h @@ -0,0 +1,28 @@ +#include "e.h" +#include +#include + +#if defined(__FreeBSD__) || defined(__DragonFly__) +# include +# include +#endif + +extern E_Module *shot_module; + +#define MAXZONES 64 + +void share_save (const char *cmd); +void share_write_end_watch (void *data); +void share_write_status_watch(void *data); +void share_dialog_show (void); +void share_confirm (void); +Eina_Bool share_have (void); +void share_abort (void); +void preview_dialog_show (E_Zone *zone, E_Client *ec, const char *params, void *dst, int sw, int sh); +Eina_Bool preview_have (void); +void preview_abort (void); +Evas_Object *preview_image_get (void); +void save_to (const char *file); +void save_dialog_show (void); +Eina_Bool save_have (void); +void save_abort (void); diff --git a/src/modules/shot/e_mod_preview.c b/src/modules/shot/e_mod_preview.c new file mode 100644 index 000000000..9232a655d --- /dev/null +++ b/src/modules/shot/e_mod_preview.c @@ -0,0 +1,291 @@ +#include "e_mod_main.h" + +static Evas_Object *win = NULL; +static Evas_Object *o_bg = NULL, *o_box = NULL, *o_content = NULL; +static Evas_Object *o_event = NULL, *o_img = NULL, *o_hlist = NULL; +static Evas_Object *o_rectdim[MAXZONES] = { NULL }; +static Evas_Object *o_radio_all = NULL; +static Evas_Object *o_radio[MAXZONES] = { NULL }; +static int quality = 90; +static int screen = -1; + +static void +_win_save_cb(void *data EINA_UNUSED, void *data2 EINA_UNUSED) +{ + save_dialog_show(); +} + +static void +_win_share_cb(void *d EINA_UNUSED, void *d2 EINA_UNUSED) +{ + share_confirm(); +} + +static void +_win_cancel_cb(void *data EINA_UNUSED, void *data2 EINA_UNUSED) +{ + E_FREE_FUNC(win, evas_object_del); +} + +static void +_win_delete_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED) +{ + win = NULL; +} + +static void +_screen_change_cb(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED) +{ + Eina_List *l; + E_Zone *z; + + EINA_LIST_FOREACH(e_comp->zones, l, z) + { + if (screen == -1) + evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 0); + else if (screen == (int)z->num) + evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 0); + else + evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 200); + } +} + +static void +_rect_down_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info) +{ + Evas_Event_Mouse_Down *ev = event_info; + Eina_List *l; + E_Zone *z; + + if (ev->button != 1) return; + + e_widget_radio_toggle_set(o_radio_all, 0); + EINA_LIST_FOREACH(e_comp->zones, l, z) + { + if (obj == o_rectdim[z->num]) + { + screen = z->num; + e_widget_radio_toggle_set(o_radio[z->num], 1); + } + else + e_widget_radio_toggle_set(o_radio[z->num], 0); + } + + EINA_LIST_FOREACH(e_comp->zones, l, z) + { + if (screen == -1) + evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 0); + else if (screen == (int)z->num) + evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 0); + else + evas_object_color_set(o_rectdim[z->num], 0, 0, 0, 200); + } +} + +void +preview_dialog_show(E_Zone *zone, E_Client *ec, const char *params, void *dst, + int sw, int sh) +{ + Evas *evas, *evas2; + Evas_Object *o, *oa, *op, *ol; + E_Radio_Group *rg; + int w, h; + char smode[128], squal[128], sscreen[128]; + + win = elm_win_add(NULL, NULL, ELM_WIN_BASIC); + + evas = evas_object_evas_get(win); + elm_win_title_set(win, _("Select action to take with screenshot")); + evas_object_event_callback_add(win, EVAS_CALLBACK_DEL, _win_delete_cb, NULL); + elm_win_center(win, 1, 1); + ecore_evas_name_class_set(e_win_ee_get(win), "E", "_shot_dialog"); + + o = elm_layout_add(e_win_evas_win_get(evas)); + elm_win_resize_object_add(win, o); + o_bg = o; + e_theme_edje_object_set(o, "base/theme/dialog", "e/widgets/dialog/main"); + evas_object_show(o); + + o = e_widget_list_add(evas, 0, 0); + o_content = o; + elm_object_part_content_set(o_bg, "e.swallow.content", o); + evas_object_show(o); + + w = sw / 4; + if (w < 220) w = 220; + h = (w * sh) / sw; + + o = e_widget_aspect_add(evas, w, h); + oa = o; + o = e_widget_preview_add(evas, w, h); + op = o; + + evas2 = e_widget_preview_evas_get(op); + + o = evas_object_image_filled_add(evas2); + o_img = o; + evas_object_image_colorspace_set(o, EVAS_COLORSPACE_ARGB8888); + evas_object_image_alpha_set(o, EINA_FALSE); + evas_object_image_size_set(o, sw, sh); + evas_object_image_data_copy_set(o, dst); + + evas_object_image_data_update_add(o, 0, 0, sw, sh); + e_widget_preview_extern_object_set(op, o); + evas_object_show(o); + + evas_object_show(op); + evas_object_show(oa); + + e_widget_aspect_child_set(oa, op); + e_widget_list_object_append(o_content, oa, 0, 0, 0.5); + + o = e_widget_list_add(evas, 1, 1); + o_hlist = o; + + o = e_widget_framelist_add(evas, _("Quality"), 0); + ol = o; + + rg = e_widget_radio_group_new(&quality); + o = e_widget_radio_add(evas, _("Perfect"), 100, rg); + e_widget_framelist_object_append(ol, o); + o = e_widget_radio_add(evas, _("High"), 90, rg); + e_widget_framelist_object_append(ol, o); + o = e_widget_radio_add(evas, _("Medium"), 70, rg); + e_widget_framelist_object_append(ol, o); + o = e_widget_radio_add(evas, _("Low"), 50, rg); + e_widget_framelist_object_append(ol, o); + + e_widget_list_object_append(o_hlist, ol, 1, 0, 0.5); + + if (zone) + { + screen = -1; + if (eina_list_count(e_comp->zones) > 1) + { + Eina_List *l; + E_Zone *z; + int i; + + o = e_widget_framelist_add(evas, _("Screen"), 0); + ol = o; + + rg = e_widget_radio_group_new(&screen); + o = e_widget_radio_add(evas, _("All"), -1, rg); + o_radio_all = o; + evas_object_smart_callback_add(o, "changed", + _screen_change_cb, NULL); + e_widget_framelist_object_append(ol, o); + i = 0; + EINA_LIST_FOREACH(e_comp->zones, l, z) + { + char buf[32]; + + if (z->num >= MAXZONES) continue; + snprintf(buf, sizeof(buf), "%i", z->num); + o = e_widget_radio_add(evas, buf, z->num, rg); + o_radio[z->num] = o; + evas_object_smart_callback_add(o, "changed", + _screen_change_cb, NULL); + e_widget_framelist_object_append(ol, o); + + o = evas_object_rectangle_add(evas2); + evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN, + _rect_down_cb, NULL); + o_rectdim[z->num] = o; + evas_object_color_set(o, 0, 0, 0, 0); + evas_object_show(o); + evas_object_geometry_get(o_img, NULL, NULL, &w, &h); + evas_object_move(o, (z->x * w) / sw, (z->y * h) / sh); + evas_object_resize(o, (z->w * w) / sw, (z->h * h) / sh); + i++; + } + e_widget_list_object_append(o_hlist, ol, 1, 0, 0.5); + } + + } + e_widget_list_object_append(o_content, o_hlist, 0, 0, 0.5); + + o = o_content; + e_widget_size_min_get(o, &w, &h); + evas_object_size_hint_min_set(o, w, h); + elm_object_part_content_set(o_bg, "e.swallow.content", o); + evas_object_show(o); + + /////////////////////////////////////////////////////////////////////// + + o = e_widget_list_add(evas, 1, 1); + o_box = o; + elm_object_part_content_set(o_bg, "e.swallow.buttons", o); + + o = e_widget_button_add(evas, _("Save"), NULL, _win_save_cb, win, NULL); + e_widget_list_object_append(o_box, o, 1, 0, 0.5); + o = e_widget_button_add(evas, _("Share"), NULL, _win_share_cb, win, NULL); + e_widget_list_object_append(o_box, o, 1, 0, 0.5); + o = e_widget_button_add(evas, _("Cancel"), NULL, _win_cancel_cb, win, NULL); + e_widget_list_object_append(o_box, o, 1, 0, 0.5); + + o = o_box; + e_widget_size_min_get(o, &w, &h); + evas_object_size_hint_min_set(o, w, h); + elm_object_part_content_set(o_bg, "e.swallow.buttons", o); + + o = evas_object_rectangle_add(evas); + o_event = o; + + evas_object_size_hint_min_get(o_bg, &w, &h); + evas_object_resize(o_bg, w, h); + evas_object_resize(win, w, h); + evas_object_size_hint_min_set(win, w, h); + evas_object_size_hint_max_set(win, 99999, 99999); + + if ((params) && + (sscanf(params, "%100s %100s %100s", smode, squal, sscreen) == 3)) + { + screen = -1; + if ((zone) && (!strcmp(sscreen, "current"))) screen = zone->num; + else if (!strcmp(sscreen, "all")) screen = -1; + else screen = atoi(sscreen); + + quality = 90; + if (!strcmp(squal, "perfect")) quality = 100; + else if (!strcmp(squal, "high")) quality = 90; + else if (!strcmp(squal, "medium")) quality = 70; + else if (!strcmp(squal, "low")) quality = 50; + else quality = atoi(squal); + + if (!strcmp(smode, "save")) _win_save_cb(NULL, NULL); + else if (!strcmp(smode, "share")) _win_share_cb(NULL, NULL); + } + else + { + evas_object_show(win); + e_win_client_icon_set(win, "screenshot"); + + if (!e_widget_focus_get(o_bg)) e_widget_focus_set(o_box, 1); + if (ec) + { + E_Client *c = e_win_client_get(win); + + if (c) evas_object_layer_set(c->frame, ec->layer); + } + } +} + +Eina_Bool +preview_have(void) +{ + if (win) return EINA_TRUE; + else return EINA_FALSE; +} + +void +preview_abort(void) +{ + E_FREE_FUNC(win, evas_object_del); +} + +Evas_Object * +preview_image_get(void) +{ + return o_img; +} diff --git a/src/modules/shot/e_mod_save.c b/src/modules/shot/e_mod_save.c new file mode 100644 index 000000000..9736f0d2c --- /dev/null +++ b/src/modules/shot/e_mod_save.c @@ -0,0 +1,273 @@ +#include "e_mod_main.h" + +static int quality = 90; +static int screen = -1; +static Evas_Object *o_fsel = NULL; +static E_Dialog *fsel_dia = NULL; + +static void _file_select_ok_cb(void *data EINA_UNUSED, E_Dialog *dia); +static void _file_select_cancel_cb(void *data EINA_UNUSED, E_Dialog *dia); + +typedef struct +{ + char *path, *outfile; + void *data; + int w, h, stride, quality; + size_t size; + int fd; +} Rgba_Writer_Data; + +static void +_rgba_data_free(Rgba_Writer_Data *rdata) +{ + free(rdata->path); + free(rdata->outfile); + free(rdata->data); + close(rdata->fd); + free(rdata); +} + +static void +_cb_rgba_writer_do(void *data, Ecore_Thread *th EINA_UNUSED) +{ + Rgba_Writer_Data *rdata = data; + if (write(rdata->fd, rdata->data, rdata->size) < 0) + ERR("Write of shot rgba data failed"); +} + +static void +_cb_rgba_writer_done(void *data, Ecore_Thread *th EINA_UNUSED) +{ + Rgba_Writer_Data *rdata = data; + char buf[PATH_MAX]; + + if (rdata->outfile) + snprintf(buf, sizeof(buf), "%s/%s/upload '%s' %i %i %i %i '%s'", + e_module_dir_get(shot_module), MODULE_ARCH, + rdata->path, rdata->w, rdata->h, rdata->stride, + rdata->quality, rdata->outfile); + else + snprintf(buf, sizeof(buf), "%s/%s/upload '%s' %i %i %i %i", + e_module_dir_get(shot_module), MODULE_ARCH, + rdata->path, rdata->w, rdata->h, rdata->stride, + rdata->quality); + share_save(buf); + _rgba_data_free(rdata); +} + +static void +_cb_rgba_writer_cancel(void *data, Ecore_Thread *th EINA_UNUSED) +{ + Rgba_Writer_Data *rdata = data; + _rgba_data_free(rdata); +} + +void +save_to(const char *file) +{ + int fd; + char tmpf[256] = "e-shot-rgba-XXXXXX"; + Eina_Tmpstr *path = NULL; + int imw = 0, imh = 0, imstride; + + fd = eina_file_mkstemp(tmpf, &path); + if (fd >= 0) + { + unsigned char *data = NULL; + Rgba_Writer_Data *thdat = NULL; + size_t size = 0; + Evas_Object *img = preview_image_get(); + + if (screen == -1) + { + if (img) + { + int w = 0, h = 0; + int stride = evas_object_image_stride_get(img); + unsigned char *src_data = evas_object_image_data_get(img, EINA_FALSE); + + evas_object_image_size_get(img, &w, &h); + if ((stride > 0) && (src_data) && (h > 0)) + { + imw = w; + imh = h; + imstride = stride; + size = stride * h; + data = malloc(size); + if (data) memcpy(data, src_data, size); + } + } + } + else + { + if (img) + { + int w = 0, h = 0; + int stride = evas_object_image_stride_get(img); + unsigned char *src_data = evas_object_image_data_get(img, EINA_FALSE); + + evas_object_image_size_get(img, &w, &h); + if ((stride > 0) && (src_data) && (h > 0)) + { + Eina_List *l; + E_Zone *z = NULL; + + EINA_LIST_FOREACH(e_comp->zones, l, z) + { + if (screen == (int)z->num) break; + z = NULL; + } + if (z) + { + size = z->w * z->h * 4; + data = malloc(size); + if (data) + { + int y; + unsigned char *s, *d; + + imw = z->w; + imh = z->h; + imstride = imw * 4; + d = data; + for (y = z->y; y < (z->y + z->h); y++) + { + s = src_data + (stride * y) + (z->x * 4); + memcpy(d, s, z->w * 4); + d += z->w * 4; + } + } + } + } + } + } + if (data) + { + thdat = calloc(1, sizeof(Rgba_Writer_Data)); + if (thdat) + { + thdat->path = strdup(path); + if (file) thdat->outfile = strdup(file); + if ((thdat->path) && + (((file) && (thdat->outfile)) || + (!file))) + { + thdat->data = data; + thdat->size = size; + thdat->fd = fd; + thdat->w = imw; + thdat->h = imh; + thdat->stride = imstride; + thdat->quality = quality; + ecore_thread_run(_cb_rgba_writer_do, + _cb_rgba_writer_done, + _cb_rgba_writer_cancel, thdat); + } + else + { + free(thdat->path); + free(thdat->outfile); + free(thdat); + thdat = NULL; + } + } + else + { + close(fd); + free(data); + } + } + if (!thdat) close(fd); + eina_tmpstr_del(path); + } + return; +} + +static void +_file_select_ok_cb(void *data EINA_UNUSED, E_Dialog *dia) +{ + const char *file; + + dia = fsel_dia; + file = e_widget_fsel_selection_path_get(o_fsel); + if ((!file) || (!file[0]) || + ((!eina_str_has_extension(file, ".jpg")) && + (!eina_str_has_extension(file, ".png")))) + { + e_util_dialog_show + (_("Error - Unknown format"), + _("File has an unspecified extension." + "Please use '.jpg' or '.png' extensions" + "only as other formats are not" + "supported currently.")); + return; + } + save_to(file); + if (dia) e_util_defer_object_del(E_OBJECT(dia)); + preview_abort(); + fsel_dia = NULL; +} + +static void +_file_select_cancel_cb(void *data EINA_UNUSED, E_Dialog *dia) +{ + if (dia) e_util_defer_object_del(E_OBJECT(dia)); + preview_abort(); + fsel_dia = NULL; +} + +static void +_file_select_del_cb(void *d EINA_UNUSED) +{ + preview_abort(); + fsel_dia = NULL; +} + +void +save_dialog_show(void) +{ + E_Dialog *dia; + Evas_Object *o; + Evas_Coord mw, mh; + time_t tt; + struct tm *tm; + char buf[PATH_MAX]; + + time(&tt); + tm = localtime(&tt); + if (quality == 100) + strftime(buf, sizeof(buf), "shot-%Y-%m-%d_%H-%M-%S.png", tm); + else + strftime(buf, sizeof(buf), "shot-%Y-%m-%d_%H-%M-%S.jpg", tm); + fsel_dia = dia = e_dialog_new(NULL, "E", "_e_shot_fsel"); + e_dialog_resizable_set(dia, EINA_TRUE); + e_dialog_title_set(dia, _("Select screenshot save location")); + o = e_widget_fsel_add(evas_object_evas_get(dia->win), "desktop", "/", + buf, NULL, NULL, NULL, NULL, NULL, 1); + e_object_del_attach_func_set(E_OBJECT(dia), _file_select_del_cb); + e_widget_fsel_window_set(o, dia->win); + o_fsel = o; + evas_object_show(o); + e_widget_size_min_get(o, &mw, &mh); + e_dialog_content_set(dia, o, mw, mh); + e_dialog_button_add(dia, _("Save"), NULL, + _file_select_ok_cb, NULL); + e_dialog_button_add(dia, _("Cancel"), NULL, + _file_select_cancel_cb, NULL); + elm_win_center(dia->win, 1, 1); + o = evas_object_rectangle_add(evas_object_evas_get(dia->win)); + e_dialog_show(dia); +} + +Eina_Bool +save_have(void) +{ + if (fsel_dia) return EINA_TRUE; + return EINA_FALSE; +} + +void +save_abort(void) +{ + E_FREE_FUNC(fsel_dia, e_object_del); +} diff --git a/src/modules/shot/e_mod_share.c b/src/modules/shot/e_mod_share.c new file mode 100644 index 000000000..2024f7fc9 --- /dev/null +++ b/src/modules/shot/e_mod_share.c @@ -0,0 +1,217 @@ +#include "e_mod_main.h" + +static Evas_Object *win = NULL; +static E_Confirm_Dialog *cd = NULL; +static Ecore_Exe *img_write_exe = NULL; +static Evas_Object *o_label = NULL; +static Evas_Object *o_entry = NULL; +static Eina_List *handlers = NULL; +static char *url_ret = NULL; + +// clean up and be done +static void +_share_done(void) +{ + E_FREE_LIST(handlers, ecore_event_handler_del); + free(url_ret); + o_label = NULL; + img_write_exe = NULL; + url_ret = NULL; + preview_abort(); +} + +// the upload dialog +static void +_upload_ok_cb(void *data EINA_UNUSED, E_Dialog *dia) +{ + // ok just hides dialog and does background upload + o_label = NULL; + if (dia) e_util_defer_object_del(E_OBJECT(dia)); + if (!win) return; + E_FREE_FUNC(win, evas_object_del); +} + +static void +_upload_cancel_cb(void *data EINA_UNUSED, E_Dialog *dia) +{ + o_label = NULL; + if (dia) e_util_defer_object_del(E_OBJECT(dia)); + E_FREE_FUNC(win, evas_object_del); + _share_done(); +} + +static Eina_Bool +_img_write_end_cb(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event) +{ + Ecore_Exe_Event_Del *ev = event; + + if (ev->exe != img_write_exe) return EINA_TRUE; + _share_done(); + return EINA_FALSE; +} + +static Eina_Bool +_img_write_out_cb(void *data, int ev_type EINA_UNUSED, void *event) +{ + Ecore_Exe_Event_Data *ev = event; + int i; + + if (ev->exe != img_write_exe) return EINA_TRUE; + if (!((ev->lines) && (ev->lines[0].line))) goto done; + for (i = 0; ev->lines[i].line; i++) + { + const char *l = ev->lines[i].line; + + if ((l[0] == 'U') && (l[1] == ' ')) + { + int v = atoi(l + 2); + if ((v >= 0) && (v <= 1000)) + { + char buf[128]; + // update gui... + snprintf(buf, sizeof(buf), _("Uploaded %i%%"), (v * 100) / 1000); + e_widget_label_text_set(o_label, buf); + } + } + else if ((l[0] == 'R') && (l[1] == ' ')) + { + const char *r = l + 2; + // finished - got final url + if (!url_ret) url_ret = strdup(r); + } + else if ((l[0] == 'E') && (l[1] == ' ')) + { + int err = atoi(l + 2); + if (data) e_widget_disabled_set(data, 1); + e_util_dialog_show(_("Error - Upload Failed"), + _("Upload failed with status code:%i"), + err); + _share_done(); + break; + } + else if ((l[0] == 'O')) + { + if (data) e_widget_disabled_set(data, 1); + if ((o_entry) && (url_ret)) + e_widget_entry_text_set(o_entry, url_ret); + _share_done(); + break; + } + } +done: + return EINA_FALSE; +} + +void +share_save(const char *cmd) +{ + share_write_end_watch(NULL); + img_write_exe = ecore_exe_pipe_run + (cmd, ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_READ_LINE_BUFFERED | + ECORE_EXE_NOT_LEADER | ECORE_EXE_TERM_WITH_PARENT, NULL); +} + +void +share_write_end_watch(void *data) +{ + E_LIST_HANDLER_APPEND(handlers, ECORE_EXE_EVENT_DEL, + _img_write_end_cb, data); +} + +void +share_write_status_watch(void *data) +{ + E_LIST_HANDLER_APPEND(handlers, ECORE_EXE_EVENT_DATA, + _img_write_out_cb, data); +} + +static void +_win_share_del(void *data EINA_UNUSED) +{ + if (handlers) + ecore_event_handler_data_set(eina_list_last_data_get(handlers), NULL); + _upload_cancel_cb(NULL, NULL); + if (cd) e_object_del(E_OBJECT(cd)); +} + +void +share_dialog_show(void) +{ + E_Dialog *dia; + Evas_Object *o, *ol; + Evas_Coord mw, mh; + + E_FREE_LIST(handlers, ecore_event_handler_del); + + save_to(NULL); + + E_FREE_FUNC(win, evas_object_del); + + dia = e_dialog_new(NULL, "E", "_e_shot_share"); + e_dialog_resizable_set(dia, EINA_TRUE); + e_dialog_title_set(dia, _("Uploading screenshot")); + + o = e_widget_list_add(evas_object_evas_get(dia->win), 0, 0); + ol = o; + + o = e_widget_label_add(evas_object_evas_get(dia->win), _("Uploading ...")); + o_label = o; + e_widget_list_object_append(ol, o, 0, 0, 0.5); + + o = e_widget_label_add(evas_object_evas_get(dia->win), + _("Screenshot is available at this location:")); + e_widget_list_object_append(ol, o, 0, 0, 0.5); + + o = e_widget_entry_add(dia->win, NULL, NULL, NULL, NULL); + o_entry = o; + e_widget_list_object_append(ol, o, 1, 0, 0.5); + + e_widget_size_min_get(ol, &mw, &mh); + e_dialog_content_set(dia, ol, mw, mh); + e_dialog_button_add(dia, _("Hide"), NULL, _upload_ok_cb, NULL); + e_dialog_button_add(dia, _("Cancel"), NULL, _upload_cancel_cb, NULL); + e_object_del_attach_func_set(E_OBJECT(dia), _win_share_del); + share_write_status_watch(eina_list_last_data_get(dia->buttons)); + elm_win_center(dia->win, 1, 1); + e_dialog_show(dia); +} + +// confirm dialog that it's ok to share +static void +_win_share_confirm_del(void *d EINA_UNUSED) +{ + cd = NULL; +} + +static void +_win_share_confirm_yes(void *d EINA_UNUSED) +{ + share_dialog_show(); +} + +void +share_confirm(void) +{ + if (cd) return; + cd = e_confirm_dialog_show + (_("Confirm Share"), NULL, + _("This image will be uploaded" + "to enlightenment.org. It will be publicly visible."), + _("Confirm"), _("Cancel"), + _win_share_confirm_yes, NULL, + NULL, NULL, _win_share_confirm_del, NULL); +} + +Eina_Bool +share_have(void) +{ + if (img_write_exe) return EINA_TRUE; + return EINA_FALSE; +} + +void +share_abort(void) +{ + E_FREE_FUNC(cd, e_object_del); + E_FREE_FUNC(win, evas_object_del); +} diff --git a/src/modules/shot/meson.build b/src/modules/shot/meson.build index 42119f40e..132fc98a3 100644 --- a/src/modules/shot/meson.build +++ b/src/modules/shot/meson.build @@ -1,3 +1,17 @@ src = files( - 'e_mod_main.c' - ) + 'e_mod_main.c', + 'e_mod_main.h', + 'e_mod_preview.c', + 'e_mod_save.c', + 'e_mod_share.c' +) + +if get_option(m) == true + executable('upload', + 'upload.c', + include_directories: include_directories(module_includes), + dependencies : [ dep_elementary ], + install_dir : _dir_bin, + install : true + ) +endif diff --git a/src/modules/shot/upload.c b/src/modules/shot/upload.c new file mode 100644 index 000000000..b7236a30c --- /dev/null +++ b/src/modules/shot/upload.c @@ -0,0 +1,208 @@ +#include +#include +#include +#include + +static Ecore_Con_Url *url_up = NULL; + +static Eina_Bool +_upload_data_cb(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event) +{ + Ecore_Con_Event_Url_Data *ev = event; + + if (ev->url_con != url_up) return EINA_TRUE; + if (ev->size < 1024) + { + char *txt = alloca(ev->size + 1); + + memcpy(txt, ev->data, ev->size); + txt[ev->size] = 0; + printf("R %s\n", txt); + fflush(stdout); + } + return EINA_FALSE; +} + +static Eina_Bool +_upload_progress_cb(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event) +{ + size_t total, current; + Ecore_Con_Event_Url_Progress *ev = event; + + if (ev->url_con != url_up) return EINA_TRUE; + total = ev->up.total; + current = ev->up.now; + if (total > 0) + { + printf("U %i\n", (int)((current * 1000) / total)); + fflush(stdout); + } + return EINA_FALSE; +} + +static Eina_Bool +_upload_complete_cb(void *data EINA_UNUSED, int ev_type EINA_UNUSED, void *event) +{ + Ecore_Con_Event_Url_Complete *ev = event; + + if (ev->url_con != url_up) return EINA_TRUE; + if (ev->status != 200) printf("E %i\n", ev->status); + else printf("O\n"); + fflush(stdout); + elm_exit(); + return EINA_FALSE; +} + +static Eina_Bool +find_tmpfile(int quality, char *buf, size_t buf_size) +{ + int i; + + // come up with a tmp file - not really that critical as its due for + // sharing to the internet as a whole + srand(time(NULL)); + for (i = 0; i < 100; i++) + { + int fd, v = rand(); + + if (quality == 100) snprintf(buf, buf_size, "/tmp/e-shot-%x.png", v); + else snprintf(buf, buf_size, "/tmp/e-shot-%x.jpg", v); + fd = open(buf, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + if (fd >= 0) + { + close(fd); + return EINA_TRUE; + } + close(fd); + } + return EINA_FALSE; +} + +EAPI int +elm_main(int argc, char **argv) +{ + Evas_Object *win, *image; + Eina_File *infile; + void *fdata; + size_t fsize; + Eina_Bool upload = EINA_FALSE; + const char *rgba_file, *out_file = NULL; + int w, h, stride, quality, image_stride, y; + char *image_data, *src; + char opts[256]; + + if (argc < 6) return 1; + + rgba_file = argv[1]; // path to raw linear memory format rgba32 pixel data + w = atoi(argv[2]); // width in pixels + h = atoi(argv[3]); // height in pixels + stride = atoi(argv[4]); // stride per line in bytes + quality = atoi(argv[5]); // qwuality to save out as (100 == lossless png) + if (argc >= 7) out_file = eina_stringshare_add(argv[6]); // out file path + + // set up buffer window as scratch space + elm_config_preferred_engine_set("buffer"); + win = elm_win_add(NULL, "Shot-Upload", ELM_WIN_BASIC); + elm_win_norender_push(win); + + // come up with tmp out file if no dest out provided + if (!out_file) + { + char buf[PATH_MAX]; + + upload = EINA_TRUE; + if (find_tmpfile(quality, buf, sizeof(buf))) + out_file = eina_stringshare_add(buf); + } + // open raw rgba data file which we willl mmap + infile = eina_file_open(rgba_file, EINA_FALSE); + if (!infile) return 2; + fsize = eina_file_size_get(infile); + fdata = eina_file_map_all(infile, EINA_FILE_SEQUENTIAL); + if (!((fsize > 0) && (fdata))) + { + ecore_file_unlink(rgba_file); + return 3; + } + + // create image objectfor saving out with right format and size + image = evas_object_image_add(evas_object_evas_get(win)); + evas_object_image_colorspace_set(image, EVAS_COLORSPACE_ARGB8888); + evas_object_image_alpha_set(image, EINA_FALSE); + evas_object_image_size_set(image, w, h); + image_stride = evas_object_image_stride_get(image); + image_data = evas_object_image_data_get(image, EINA_TRUE); + if (!((image_stride > 0) && (image_data))) + { + ecore_file_unlink(rgba_file); + return 4; + } + // copy data into output image (could also set data straight in + src = fdata; + for (y = 0; y < h; y++) + { + memcpy(image_data, src, w * 4); + image_data += image_stride; + src += stride; + } + if (quality == 100) + snprintf(opts, sizeof(opts), "compress=%i", 9); + else + snprintf(opts, sizeof(opts), "quality=%i", quality); + eina_file_close(infile); + ecore_file_unlink(rgba_file); + // save the file + if (!evas_object_image_save(image, out_file, NULL, opts)) + return 5; + + // if we have to upload it, open our output file, mmap it and upload + if (upload) + { + infile = eina_file_open(out_file, EINA_FALSE); + if (infile) + { + fsize = eina_file_size_get(infile); + fdata = eina_file_map_all(infile, EINA_FILE_SEQUENTIAL); + if ((fsize > 0) && (fdata)) + { + Ecore_Event_Handler *h1, *h2, *h3; + + h1 = ecore_event_handler_add(ECORE_CON_EVENT_URL_DATA, + _upload_data_cb, NULL); + h2 = ecore_event_handler_add(ECORE_CON_EVENT_URL_PROGRESS, + _upload_progress_cb, NULL); + h3 = ecore_event_handler_add(ECORE_CON_EVENT_URL_COMPLETE, + _upload_complete_cb, NULL); + if ((h1) && (h2) && (h3)) + { + url_up = ecore_con_url_new + ("https://www.enlightenment.org/shot.php"); + if (url_up) + { + // why use http 1.1? proxies like squid don't + // handle 1.1 posts with expect like curl uses + // by default, so go to 1.0 and this all works + // dandily out of the box + ecore_con_url_http_version_set + (url_up, ECORE_CON_URL_HTTP_VERSION_1_0); + ecore_con_url_post + (url_up, fdata, fsize, "application/x-e-shot"); + // need loop to run to drive the uploading + elm_run(); + } + } + ecore_event_handler_del(h1); + ecore_event_handler_del(h2); + ecore_event_handler_del(h3); + } + if (fdata) eina_file_map_free(infile, fdata); + eina_file_close(infile); + } + // output was temporary here + ecore_file_unlink(out_file); + } + evas_object_del(win); + eina_stringshare_del(out_file); + return 0; +} +ELM_MAIN()