use snapshot object for shot module

now this is the same under all display servers
This commit is contained in:
Mike Blumenkrantz 2017-07-21 16:20:03 -04:00
parent 29d6e4c9a5
commit f605dd6441
2 changed files with 42 additions and 406 deletions

View File

@ -17,21 +17,6 @@ src_modules_shot_module_la_LDFLAGS = $(MOD_LDFLAGS)
src_modules_shot_module_la_SOURCES = \ src_modules_shot_module_la_SOURCES = \
src/modules/shot/e_mod_main.c src/modules/shot/e_mod_main.c
if HAVE_WAYLAND
shot_wayland_sources = \
src/modules/shot/screenshooter-protocol.c \
src/modules/shot/screenshooter-client-protocol.h
nodist_src_modules_shot_module_la_SOURCES += \
$(shot_wayland_sources)
MAINTAINERCLEANFILES += \
$(shot_wayland_sources)
src/modules/shot/e_mod_main.c: \
src/modules/shot/screenshooter-client-protocol.h
endif
PHONIES += shot install-shot PHONIES += shot install-shot
shot: $(shotpkg_LTLIBRARIES) $(shot_DATA) shot: $(shotpkg_LTLIBRARIES) $(shot_DATA)
install-shot: install-shotDATA install-shotpkgLTLIBRARIES install-shot: install-shotDATA install-shotpkgLTLIBRARIES

View File

@ -9,9 +9,6 @@
* @} * @}
*/ */
#include "e.h" #include "e.h"
#ifdef HAVE_WAYLAND
# include "screenshooter-client-protocol.h"
#endif
#include <time.h> #include <time.h>
#include <sys/mman.h> #include <sys/mman.h>
@ -26,6 +23,7 @@ static E_Action *border_act = NULL, *act = NULL;
static E_Int_Menu_Augmentation *maug = NULL; static E_Int_Menu_Augmentation *maug = NULL;
static Ecore_Timer *timer, *border_timer = NULL; static Ecore_Timer *timer, *border_timer = NULL;
static Evas_Object *win = NULL; static Evas_Object *win = NULL;
static Evas_Object *snap = NULL;
E_Confirm_Dialog *cd = NULL; E_Confirm_Dialog *cd = NULL;
static Evas_Object *o_bg = NULL, *o_box = NULL, *o_content = 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_event = NULL, *o_img = NULL, *o_hlist = NULL;
@ -46,19 +44,9 @@ static char *url_ret = NULL;
static E_Dialog *fsel_dia = NULL; static E_Dialog *fsel_dia = NULL;
static E_Client_Menu_Hook *border_hook = NULL; static E_Client_Menu_Hook *border_hook = NULL;
#ifdef HAVE_WAYLAND static E_Client *shot_ec = NULL;
Eina_Bool copy_done = EINA_FALSE; static E_Zone *shot_zone = NULL;
static struct zwp_screenshooter *_wl_screenshooter; static char *shot_params;
static Eina_List *_outputs;
struct screenshooter_output
{
struct wl_output *output;
struct wl_buffer *buffer;
int x, y, w, h;
uint32_t id;
void *data;
};
#endif
static void _file_select_ok_cb(void *data EINA_UNUSED, E_Dialog *dia); 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 _file_select_cancel_cb(void *data EINA_UNUSED, E_Dialog *dia);
@ -69,66 +57,6 @@ _win_cancel_cb(void *data EINA_UNUSED, void *data2 EINA_UNUSED)
E_FREE_FUNC(win, evas_object_del); E_FREE_FUNC(win, evas_object_del);
} }
#ifdef HAVE_WAYLAND
static void
_wl_cb_screenshot_done(void *data EINA_UNUSED, struct zwp_screenshooter *screenshooter EINA_UNUSED)
{
copy_done = EINA_TRUE;
}
static const struct zwp_screenshooter_listener _screenshooter_listener =
{
_wl_cb_screenshot_done
};
static void
_wl_output_cb_handle_geometry(void *data EINA_UNUSED, struct wl_output *wl_output, int x, int y, int pw EINA_UNUSED, int ph EINA_UNUSED, int subpixel EINA_UNUSED, const char *make EINA_UNUSED, const char *model EINA_UNUSED, int transform EINA_UNUSED)
{
struct screenshooter_output *output;
output = wl_output_get_user_data(wl_output);
if ((output) && (wl_output == output->output))
{
output->x = x;
output->y = y;
}
}
static void
_wl_output_cb_handle_mode(void *data EINA_UNUSED, struct wl_output *wl_output, uint32_t flags, int w, int h, int refresh EINA_UNUSED)
{
struct screenshooter_output *output;
output = wl_output_get_user_data(wl_output);
if ((output) &&
((wl_output == output->output) && (flags & WL_OUTPUT_MODE_CURRENT)))
{
output->w = w;
output->h = h;
}
}
static void
_wl_output_cb_handle_done(void *data EINA_UNUSED, struct wl_output *output EINA_UNUSED)
{
}
static void
_wl_output_cb_handle_scale(void *data EINA_UNUSED, struct wl_output *output EINA_UNUSED, int32_t scale EINA_UNUSED)
{
}
static const struct wl_output_listener _output_listener =
{
_wl_output_cb_handle_geometry,
_wl_output_cb_handle_mode,
_wl_output_cb_handle_done,
_wl_output_cb_handle_scale
};
#endif
static void static void
_win_delete_cb() _win_delete_cb()
{ {
@ -877,271 +805,64 @@ _save_dialog_show(E_Zone *zone, E_Client *ec, const char *params, void *dst, int
} }
} }
#ifdef HAVE_WAYLAND
static struct wl_buffer *
_create_shm_buffer(struct wl_shm *_shm, int width, int height, void **data_out)
{
char filename[] = "wayland-shm-XXXXXX";
Eina_Tmpstr *tmpfile = NULL;
struct wl_shm_pool *pool;
struct wl_buffer *buffer;
int fd, size, stride;
void *data;
fd = eina_file_mkstemp(filename, &tmpfile);
if (fd < 0)
{
fprintf(stderr, "open %s failed: %m\n", tmpfile);
return NULL;
}
stride = width * 4;
size = stride * height;
if (ftruncate(fd, size) < 0)
{
fprintf(stderr, "ftruncate failed: %m\n");
close(fd);
return NULL;
}
data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
unlink(tmpfile);
eina_tmpstr_del(tmpfile);
if (data == MAP_FAILED)
{
fprintf(stderr, "mmap failed: %m\n");
close(fd);
return NULL;
}
pool = wl_shm_create_pool(_shm, fd, size);
close(fd);
buffer =
wl_shm_pool_create_buffer(pool, 0, width, height, stride,
WL_SHM_FORMAT_ARGB8888);
wl_shm_pool_destroy(pool);
*data_out = data;
return buffer;
}
#endif
static void static void
_wl_shot_now(E_Zone *zone, E_Client *ec, const char *params) _shot_post(void *buffer EINA_UNUSED, Evas *e EINA_UNUSED, void *event EINA_UNUSED)
{ {
#ifdef HAVE_WAYLAND int w, h;
Eina_List *l; evas_object_geometry_get(snap, NULL, NULL, &w, &h);
struct screenshooter_output *output; evas_event_callback_del(e_comp->evas, EVAS_CALLBACK_RENDER_POST, _shot_post);
struct wl_shm *shm; _save_dialog_show(shot_zone, shot_ec, shot_params, (void*)evas_object_image_data_get(snap, 0), w, h);
int x, y, sw, sh, i; E_FREE_FUNC(snap, evas_object_del);
int ostride, bstride; shot_ec = NULL;
unsigned char *dst, *d, *s; shot_zone = NULL;
E_FREE(shot_params);
if ((win) || (url_up)) return;
if ((!zone) && (!ec)) return;
if (zone)
{
sw = e_comp->w;
sh = e_comp->h;
x = y = 0;
}
else
{
x = ec->x, y = ec->y, sw = ec->w, sh = ec->h;
x = E_CLAMP(x, ec->zone->x, ec->zone->x + ec->zone->w);
y = E_CLAMP(y, ec->zone->y, ec->zone->y + ec->zone->h);
sw = E_CLAMP(sw, 1, ec->zone->x + ec->zone->w - x);
sh = E_CLAMP(sh, 1, ec->zone->y + ec->zone->h - y);
}
shm = e_comp_wl->wl.shm ?: ecore_wl2_display_shm_get(e_comp_wl->wl.client_disp);
e_bindings_disabled_set(1);
EINA_LIST_FOREACH(_outputs, l, output)
{
if ((!zone) &&
(!E_CONTAINS(output->x, output->y, output->w, output->h,
x, y, sw, sh)))
continue;
output->buffer =
_create_shm_buffer(shm, output->w, output->h, &output->data);
zwp_screenshooter_shoot(_wl_screenshooter,
output->output,
output->buffer);
copy_done = EINA_FALSE;
while (!copy_done)
ecore_main_loop_iterate();
}
e_bindings_disabled_set(0);
bstride = sw * sizeof(int);
dst = malloc(bstride * sh);
EINA_LIST_FOREACH(_outputs, l, output)
{
if ((!zone) &&
(!E_CONTAINS(output->x, output->y, output->w, output->h,
x, y, sw, sh)))
continue;
ostride = output->w * sizeof(int);
s = output->data;
if (zone)
{
d = dst + (output->y * bstride + output->x * sizeof(int));
for (i = 0; i < output->h; i++)
{
memcpy(d, s, ostride);
d += bstride;
s += ostride;
}
}
else
{
d = dst;
for (i = y; i < (y + sh); i++)
{
s = output->data;
s += (i * ostride) + (x * sizeof(int));
memcpy(d, s, bstride);
d += bstride;
}
}
}
_save_dialog_show(zone, ec, params, dst, sw, sh);
free(dst);
#else
(void)zone;
(void)ec;
(void)params;
#endif
} }
static void static void
_x_shot_now(E_Zone *zone, E_Client *ec, const char *params) _shot_now(E_Zone *zone, E_Client *ec, const char *params)
{ {
#ifdef HAVE_WAYLAND_ONLY
(void)zone;
(void)ec;
(void)params;
#else
Ecore_X_Image *img;
unsigned char *src;
unsigned int *dst;
int bpl = 0, rows = 0, bpp = 0, sw, sh;
int x, y, w, h; int x, y, w, h;
Ecore_X_Window xwin; if ((win) || (url_up) || snap) return;
Ecore_X_Visual visual;
Ecore_X_Display *display;
Ecore_X_Screen *scr;
Ecore_X_Window_Attributes watt;
Ecore_X_Colormap colormap;
int depth;
if ((win) || (url_up)) return;
if ((!zone) && (!ec)) return; if ((!zone) && (!ec)) return;
if (zone) if (zone)
{ {
xwin = e_comp->root; w = e_comp->w;
w = sw = e_comp->w; h = e_comp->h;
h = sh = e_comp->h;
x = y = 0; x = y = 0;
if (!ecore_x_window_attributes_get(xwin, &watt)) return;
visual = watt.visual;
depth = watt.depth;
} }
else else
{ {
xwin = e_comp->ee_win; x = ec->x, y = ec->y, w = ec->w, h = ec->h;
x = ec->x, y = ec->y, sw = ec->w, sh = ec->h; x = E_CLAMP(x, 0, e_comp->w);
w = sw; y = E_CLAMP(y, 0, e_comp->h);
h = sh; w = E_CLAMP(w, 1, e_comp->w);
x = E_CLAMP(x, ec->zone->x, ec->zone->x + ec->zone->w); h = E_CLAMP(h, 1, e_comp->h);
y = E_CLAMP(y, ec->zone->y, ec->zone->y + ec->zone->h);
sw = E_CLAMP(sw, 1, ec->zone->x + ec->zone->w - x);
sh = E_CLAMP(sh, 1, ec->zone->y + ec->zone->h - y);
visual = e_pixmap_visual_get(ec->pixmap);
depth = ec->depth;
} }
img = ecore_x_image_new(w, h, visual, depth); if (eina_streq(ecore_evas_engine_name_get(e_comp->ee), "buffer"))
if (!ecore_x_image_get(img, xwin, x, y, 0, 0, sw, sh))
{ {
Eina_Bool dialog = EINA_FALSE; _save_dialog_show(zone, ec, params, (void*)ecore_evas_buffer_pixels_get(e_comp->ee), w, h);
ecore_x_image_free(img);
#ifdef __linux__
FILE *f;
f = fopen("/proc/sys/kernel/shmmax", "r");
if (f)
{
long long unsigned int max = 0;
int n = fscanf(f, "%llu", &max);
if ((n > 0) && (max) && (max < (w * h * sizeof(int))))
{
e_util_dialog_show(_("Screenshot Error"),
_("SHMMAX is too small to take screenshot.<br>"
"Consider increasing /proc/sys/kernel/shmmax to a value larger than %llu"),
(long long unsigned int)(w * h * sizeof(int)));
dialog = EINA_TRUE;
}
fclose(f);
}
#elif defined(__FreeBSD__) || defined(__DragonFly__)
int max;
size_t len = sizeof(max);
if (!sysctlbyname("kern.ipc.shmmax", &max, &len, NULL, 0))
{
if (max && (max < (w * h * sizeof(int))))
{
e_util_dialog_show(_("Screenshot Error"),
_("SHMMAX is too small to take screenshot.<br>"
"Consider increasing kern.ipc.shmmax to a value larger than %llu"),
(long long unsigned int)(w * h * sizeof(int)));
dialog = EINA_TRUE;
}
}
#endif
if (!dialog)
e_util_dialog_show(_("Screenshot Error"),
_("SHM creation failed.<br>"
"Ensure your system has enough RAM free and your user has sufficient permissions."));
return; return;
} }
src = ecore_x_image_data_get(img, &bpl, &rows, &bpp); shot_ec = ec;
display = ecore_x_display_get(); shot_zone = zone;
scr = ecore_x_default_screen_get(); shot_params = eina_strdup(params);
colormap = ecore_x_default_colormap_get(display, scr); snap = evas_object_image_filled_add(e_comp->evas);
dst = malloc(sw * sh * sizeof(int)); evas_object_pass_events_set(snap, 1);
ecore_x_image_to_argb_convert(src, bpp, bpl, colormap, visual, evas_object_layer_set(snap, EVAS_LAYER_MAX);
0, 0, sw, sh, evas_object_image_snapshot_set(snap, 1);
dst, (sw * sizeof(int)), 0, 0); evas_object_geometry_set(snap, x, y, w, h);
evas_object_show(snap);
_save_dialog_show(zone, ec, params, dst, sw, sh); evas_object_image_data_update_add(snap, 0, 0, w, h);
evas_object_image_pixels_dirty_set(snap, 1);
free(dst); evas_event_callback_add(e_comp->evas, EVAS_CALLBACK_RENDER_POST, _shot_post, snap);
ecore_x_image_free(img); ecore_evas_manual_render(e_comp->ee);
#endif
} }
static Eina_Bool static Eina_Bool
_shot_delay(void *data) _shot_delay(void *data)
{ {
timer = NULL; timer = NULL;
if (e_comp->comp_type == E_PIXMAP_TYPE_X) _shot_now(data, NULL, NULL);
_x_shot_now(data, NULL, NULL);
else
_wl_shot_now(data, NULL, NULL);
return EINA_FALSE; return EINA_FALSE;
} }
@ -1150,10 +871,7 @@ static Eina_Bool
_shot_delay_border(void *data) _shot_delay_border(void *data)
{ {
border_timer = NULL; border_timer = NULL;
if (e_comp->comp_type == E_PIXMAP_TYPE_X) _shot_now(NULL, data, NULL);
_x_shot_now(NULL, data, NULL);
else
_wl_shot_now(NULL, data, NULL);
return EINA_FALSE; return EINA_FALSE;
} }
@ -1196,10 +914,7 @@ _e_mod_action_border_cb(E_Object *obj EINA_UNUSED, const char *params EINA_UNUSE
ecore_timer_del(border_timer); ecore_timer_del(border_timer);
border_timer = NULL; border_timer = NULL;
} }
if (e_comp->comp_type == E_PIXMAP_TYPE_X) _shot_now(NULL, ec, NULL);
_x_shot_now(NULL, ec, NULL);
else
_wl_shot_now(NULL, ec, NULL);
} }
typedef struct typedef struct
@ -1213,10 +928,7 @@ _delayed_shot(void *data)
{ {
Delayed_Shot *ds = data; Delayed_Shot *ds = data;
if (e_comp->comp_type == E_PIXMAP_TYPE_X) _shot_now(ds->zone, NULL, ds->params);
_x_shot_now(ds->zone, NULL, ds->params);
else
_wl_shot_now(ds->zone, NULL, ds->params);
e_object_unref(E_OBJECT(ds->zone)); e_object_unref(E_OBJECT(ds->zone));
free(ds->params); free(ds->params);
free(ds); free(ds);
@ -1286,60 +998,6 @@ _e_mod_menu_add(void *data EINA_UNUSED, E_Menu *m)
e_menu_item_callback_set(mi, _e_mod_menu_cb, NULL); e_menu_item_callback_set(mi, _e_mod_menu_cb, NULL);
} }
#ifdef HAVE_WAYLAND
static Ecore_Event_Handler *wl_global_handler;
static Eina_Bool
_wl_init()
{
Eina_Iterator *itr;
Ecore_Wl2_Global *global;
struct wl_registry *reg;
void *data;
reg = e_comp_wl->wl.registry ?: ecore_wl2_display_registry_get(e_comp_wl->wl.client_disp);
itr = ecore_wl2_display_globals_get(e_comp_wl->wl.client_disp);
EINA_ITERATOR_FOREACH(itr, data)
{
global = (Ecore_Wl2_Global *)data;
if ((!_wl_screenshooter) &&
(!strcmp(global->interface, "zwp_screenshooter")))
{
_wl_screenshooter =
wl_registry_bind(reg, global->id,
&zwp_screenshooter_interface, global->version);
if (_wl_screenshooter)
zwp_screenshooter_add_listener(_wl_screenshooter,
&_screenshooter_listener,
_wl_screenshooter);
}
else if (!strcmp(global->interface, "wl_output"))
{
struct screenshooter_output *output;
Eina_List *l;
EINA_LIST_FOREACH(_outputs, l, output)
if (output->id == global->id) return ECORE_CALLBACK_RENEW;
output = calloc(1, sizeof(*output));
if (output)
{
output->output =
wl_registry_bind(reg, global->id,
&wl_output_interface, global->version);
_outputs = eina_list_append(_outputs, output);
wl_output_add_listener(output->output,
&_output_listener, output);
}
}
}
eina_iterator_free(itr);
return ECORE_CALLBACK_RENEW;
}
#endif
/* module setup */ /* module setup */
E_API E_Module_Api e_modapi = E_API E_Module_Api e_modapi =
{ {
@ -1380,11 +1038,6 @@ e_modapi_init(E_Module *m)
("main/2", _("Take Screenshot"), _e_mod_menu_add, NULL, NULL, NULL); ("main/2", _("Take Screenshot"), _e_mod_menu_add, NULL, NULL, NULL);
border_hook = e_int_client_menu_hook_add(_bd_hook, NULL); border_hook = e_int_client_menu_hook_add(_bd_hook, NULL);
#ifdef HAVE_WAYLAND
if (e_comp->comp_type != E_PIXMAP_TYPE_X)
_wl_init();
#endif
return m; return m;
} }
@ -1410,9 +1063,7 @@ e_modapi_shutdown(E_Module *m EINA_UNUSED)
e_action_del("shot"); e_action_del("shot");
act = NULL; act = NULL;
} }
#ifdef HAVE_WAYLAND
E_FREE_FUNC(wl_global_handler, ecore_event_handler_del);
#endif
shot_module = NULL; shot_module = NULL;
e_int_client_menu_hook_del(border_hook); e_int_client_menu_hook_del(border_hook);
ecore_con_url_shutdown(); ecore_con_url_shutdown();