556 lines
13 KiB
C
556 lines
13 KiB
C
#include "private.h"
|
|
#include <Ecore_X.h>
|
|
|
|
static Eina_Bool has_xinerama = EINA_TRUE;
|
|
static Eina_Bool windows_visible = EINA_TRUE;
|
|
static Eina_List *zones;
|
|
static Eina_List *roots;
|
|
static Eina_List *key_grabs;
|
|
static const Zone *grabbed_zone;
|
|
static Ecore_X_Window temp_xwin;
|
|
static Ecore_Event_Handler *mouse_out_handler;
|
|
static Ecore_Event_Handler *key_down_handler;
|
|
|
|
typedef struct _X_Key_Grab X_Key_Grab;
|
|
struct _X_Key_Grab {
|
|
const char *keyname;
|
|
void (*cb)(void *data, const char *keyname);
|
|
const void *cb_data;
|
|
};
|
|
|
|
static X_Key_Grab *
|
|
_x_key_grab_new(const char *keyname, void (*cb)(void *data, const char *keyname), const void *data)
|
|
{
|
|
X_Key_Grab *kg = malloc(sizeof(X_Key_Grab));
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(kg, NULL);
|
|
kg->keyname = eina_stringshare_add(keyname);
|
|
kg->cb = cb;
|
|
kg->cb_data = data;
|
|
return kg;
|
|
}
|
|
|
|
static void
|
|
_x_key_grab_free(X_Key_Grab *kg)
|
|
{
|
|
eina_stringshare_del(kg->keyname);
|
|
free(kg);
|
|
}
|
|
|
|
static Eina_Bool
|
|
_x_zone_screen_copy_do(Zone *zone, Evas_Object *img)
|
|
{
|
|
Evas_Object *win = zone_win_get(zone);
|
|
Ecore_X_Window xid = elm_win_xwindow_get(win);
|
|
Ecore_X_Window root = ecore_x_window_root_get(xid);
|
|
Ecore_X_Image *capture;
|
|
Ecore_X_Window_Attributes watt;
|
|
int bpl = 0, rows = 0, bpp = 0, x, y, w, h;
|
|
Ecore_X_Colormap cmap;
|
|
Ecore_X_Screen *screen;
|
|
Ecore_X_Display *display;
|
|
void *src, *dst;
|
|
Eina_Bool ret;
|
|
|
|
if (!ecore_x_window_attributes_get(root, &watt))
|
|
return EINA_FALSE;
|
|
|
|
zone_geometry_get(zone, &x, &y, &w, &h);
|
|
|
|
capture = ecore_x_image_new(w, h, watt.visual, watt.depth);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(capture, EINA_FALSE);
|
|
|
|
ret = ecore_x_image_get(capture, root, x, y, 0, 0, w, h);
|
|
EINA_SAFETY_ON_FALSE_GOTO(ret, error_clean_capture);
|
|
|
|
src = ecore_x_image_data_get(capture, &bpl, &rows, &bpp);
|
|
EINA_SAFETY_ON_NULL_GOTO(src, error_clean_capture);
|
|
|
|
display = ecore_x_display_get();
|
|
screen = ecore_x_default_screen_get();
|
|
cmap = ecore_x_default_colormap_get(display, screen);
|
|
|
|
evas_object_image_colorspace_set(img, EVAS_COLORSPACE_ARGB8888);
|
|
evas_object_image_alpha_set(img, EINA_FALSE);
|
|
evas_object_image_size_set(img, w, h);
|
|
dst = evas_object_image_data_get(img, EINA_TRUE);
|
|
|
|
ret = ecore_x_image_to_argb_convert(src, bpp, bpl, cmap, watt.visual,
|
|
0, 0, w, h,
|
|
dst, w * 4, 0, 0);
|
|
EINA_SAFETY_ON_FALSE_GOTO(ret, error_clean_dst);
|
|
|
|
evas_object_image_data_update_add(img, 0, 0, w, h);
|
|
evas_object_image_data_set(img, dst);
|
|
ecore_x_image_free(capture);
|
|
return EINA_TRUE;
|
|
|
|
error_clean_dst:
|
|
evas_object_image_data_set(img, dst);
|
|
error_clean_capture:
|
|
ecore_x_image_free(capture);
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
static void
|
|
_x_grab(const Zone *zone)
|
|
{
|
|
Evas_Object *win = zone_win_get(zone);
|
|
Ecore_X_Window xid = elm_win_xwindow_get(win);
|
|
|
|
if (!ecore_x_pointer_grab(xid))
|
|
{
|
|
CRI("Could not grab pointer to xwin %#x", xid);
|
|
show_gui_error("ERuler couldn't grab the mouse pointer");
|
|
return;
|
|
}
|
|
|
|
if (!ecore_x_keyboard_grab(xid))
|
|
{
|
|
CRI("Could not grab keyboard to xwin %#x", xid);
|
|
show_gui_error("ERuler couldn't grab the keyboard");
|
|
ecore_x_pointer_ungrab();
|
|
return;
|
|
}
|
|
|
|
DBG("Grabbed pointer and keyboard to win %#x (elm_win %p)", xid, win);
|
|
grabbed_zone = zone;
|
|
}
|
|
|
|
static void
|
|
_x_ungrab(void)
|
|
{
|
|
if (!grabbed_zone) return;
|
|
ecore_x_pointer_ungrab();
|
|
ecore_x_keyboard_ungrab();
|
|
grabbed_zone = NULL;
|
|
}
|
|
|
|
static Eina_Bool
|
|
_x_mouse_out(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
|
|
{
|
|
Ecore_X_Event_Mouse_Out *ev = event;
|
|
const Eina_List *l;
|
|
const Zone *zone;
|
|
|
|
DBG("Mouse out of zone %p, check new zone containing %d,%d",
|
|
grabbed_zone, ev->root.x, ev->root.y);
|
|
|
|
EINA_LIST_FOREACH(zones, l, zone)
|
|
{
|
|
int x, y, w, h;
|
|
|
|
if (zone == grabbed_zone) continue;
|
|
|
|
zone_geometry_get(zone, &x, &y, &w, &h);
|
|
if (((ev->root.x >= x) && (ev->root.x < x + w)) &&
|
|
((ev->root.y >= y) && (ev->root.y < y + h)))
|
|
{
|
|
DBG("Mouse now at zone %p (%d,%d + %dx%d)", zone, x, y, w, h);
|
|
_x_ungrab();
|
|
if (windows_visible)
|
|
_x_grab(zone);
|
|
return EINA_TRUE;
|
|
}
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static void
|
|
_x_grab_current(void)
|
|
{
|
|
const Eina_List *l;
|
|
const Zone *zone;
|
|
int mx, my;
|
|
|
|
ecore_x_pointer_xy_get(ecore_x_window_root_first_get(), &mx, &my);
|
|
|
|
EINA_LIST_FOREACH(zones, l, zone)
|
|
{
|
|
int x, y, w, h;
|
|
|
|
zone_geometry_get(zone, &x, &y, &w, &h);
|
|
if (((mx >= x) && (mx < x + w)) &&
|
|
((my >= y) && (my < y + h)))
|
|
{
|
|
DBG("Current zone for mouse at %d,%d is %p (%d,%d + %dx%d)",
|
|
mx, my, zone, x, y, w, h);
|
|
_x_grab(zone);
|
|
return;
|
|
}
|
|
}
|
|
|
|
ERR("Couldn't find current zone for mouse at %d,%d", mx, my);
|
|
}
|
|
|
|
static void
|
|
_x_temp_grab(void)
|
|
{
|
|
if (mouse_out_handler)
|
|
{
|
|
ecore_event_handler_del(mouse_out_handler);
|
|
mouse_out_handler = NULL;
|
|
}
|
|
|
|
if (!temp_xwin)
|
|
temp_xwin = ecore_x_window_input_new(0, 0, 0, 1, 1);
|
|
|
|
ecore_x_window_show(temp_xwin);
|
|
|
|
ecore_x_pointer_ungrab();
|
|
ecore_x_keyboard_ungrab();
|
|
ecore_x_pointer_grab(temp_xwin);
|
|
ecore_x_keyboard_grab(temp_xwin);
|
|
|
|
DBG("Temporary grab for input window %#x", temp_xwin);
|
|
}
|
|
|
|
static void
|
|
_x_temp_ungrab(void)
|
|
{
|
|
DBG("Ungrab temporary for input window %#x", temp_xwin);
|
|
|
|
if ((eina_list_count(zones) > 1) && (!mouse_out_handler))
|
|
{
|
|
mouse_out_handler = ecore_event_handler_add(ECORE_X_EVENT_MOUSE_OUT,
|
|
_x_mouse_out, NULL);
|
|
}
|
|
|
|
if (temp_xwin)
|
|
{
|
|
ecore_x_pointer_ungrab();
|
|
ecore_x_keyboard_ungrab();
|
|
ecore_x_window_free(temp_xwin);
|
|
temp_xwin = 0;
|
|
}
|
|
|
|
_x_grab_current();
|
|
}
|
|
|
|
static Eina_Bool
|
|
x_pre_setup(void)
|
|
{
|
|
if (!ecore_x_screen_is_composited(0))
|
|
{
|
|
show_gui_error("ERuler needs a X11 Composite Manager!<br><br>"
|
|
"Consider running <hilight>xcompmgr</hilight> if your "
|
|
"Window Manager or Desktop Environment doesn't offer "
|
|
"you such feature.");
|
|
}
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
x_post_setup(void)
|
|
{
|
|
const Eina_List *l;
|
|
const Zone *zone;
|
|
|
|
if (eina_list_count(zones) > 1)
|
|
{
|
|
mouse_out_handler = ecore_event_handler_add(ECORE_X_EVENT_MOUSE_OUT,
|
|
_x_mouse_out, NULL);
|
|
}
|
|
|
|
EINA_LIST_FOREACH(zones, l, zone)
|
|
{
|
|
Evas_Object *win = zone_win_get(zone);
|
|
Ecore_X_Window xid = elm_win_xwindow_get(win);
|
|
Ecore_X_Window root_xid;
|
|
int x, y, w, h;
|
|
|
|
zone_geometry_get(zone, &x, &y, &w, &h);
|
|
evas_object_show(win);
|
|
ecore_x_window_move_resize(xid, x, y, w, h);
|
|
|
|
root_xid = ecore_x_window_root_get(xid);
|
|
if (!eina_list_data_find(roots, (void*)(long)root_xid))
|
|
roots = eina_list_append(roots, (void*)(long)root_xid);
|
|
}
|
|
|
|
_x_grab_current();
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static int
|
|
x_zones_count(void)
|
|
{
|
|
int c = ecore_x_xinerama_screen_count_get();
|
|
|
|
if (c < 1)
|
|
{
|
|
ERR("No Xinerama support, assume single screen");
|
|
has_xinerama = EINA_FALSE;
|
|
c = 1;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
static Eina_Bool
|
|
x_zone_geometry_get(int idx, int *x, int *y, int *w, int *h)
|
|
{
|
|
if (!has_xinerama)
|
|
{
|
|
if (x) *x = 0;
|
|
if (y) *y = 0;
|
|
ecore_x_screen_size_get(ecore_x_default_screen_get(), w, h);
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
return ecore_x_xinerama_screen_geometry_get(idx, x, y, w, h);
|
|
}
|
|
|
|
static Eina_Bool
|
|
x_zone_setup(Zone *zone)
|
|
{
|
|
Evas_Object *win = zone_win_get(zone);
|
|
Evas_Object *img;
|
|
|
|
DBG("setup for zone %p", zone);
|
|
|
|
elm_win_override_set(win, EINA_TRUE);
|
|
|
|
img = zone_screen_copy_object_get(zone);
|
|
if (img)
|
|
zone_screen_copy_ready_set(zone, _x_zone_screen_copy_do(zone, img));
|
|
|
|
zones = eina_list_append(zones, zone);
|
|
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
typedef struct _X_Zone_Screen_Copy_Data X_Zone_Screen_Copy_Data;
|
|
struct _X_Zone_Screen_Copy_Data
|
|
{
|
|
Zone *zone;
|
|
Evas_Object *img;
|
|
Ecore_X_Window xid;
|
|
void (*cb)(void *data, Eina_Bool success);
|
|
const void *cb_data;
|
|
Ecore_Timer *shot_timer;
|
|
};
|
|
|
|
static Eina_Bool
|
|
_x_zone_screen_copy_shot_timer_cb(void *data)
|
|
{
|
|
X_Zone_Screen_Copy_Data *ctx = data;
|
|
Eina_Bool ret;
|
|
|
|
DBG("do the screen shot to image %p", ctx->img);
|
|
ret = _x_zone_screen_copy_do(ctx->zone, ctx->img);
|
|
|
|
DBG("Screen shot done with ret=%d, show %#x, ungrab temporary %#x",
|
|
ret, ctx->xid, temp_xwin);
|
|
|
|
ecore_x_window_show(ctx->xid);
|
|
_x_temp_ungrab();
|
|
|
|
ctx->cb((void *)ctx->cb_data, ret);
|
|
free(ctx);
|
|
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
static void
|
|
x_zone_screen_copy(Zone *zone, Evas_Object *img, void (*cb)(void *data, Eina_Bool success), const void *data)
|
|
{
|
|
X_Zone_Screen_Copy_Data *ctx;
|
|
Evas_Object *win = zone_win_get(zone);
|
|
Ecore_X_Window xid = elm_win_xwindow_get(win);
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(cb);
|
|
|
|
ctx = calloc(1, sizeof(X_Zone_Screen_Copy_Data));
|
|
EINA_SAFETY_ON_NULL_GOTO(ctx, error);
|
|
|
|
ctx->zone = zone;
|
|
ctx->img = img;
|
|
ctx->xid = xid;
|
|
ctx->cb = cb;
|
|
ctx->cb_data = data;
|
|
|
|
ctx->shot_timer = ecore_timer_add(1.0, _x_zone_screen_copy_shot_timer_cb,
|
|
ctx);
|
|
EINA_SAFETY_ON_NULL_GOTO(ctx->shot_timer, error_free_ctx);
|
|
|
|
_x_temp_grab();
|
|
ecore_x_window_hide(xid);
|
|
|
|
DBG("xid %#x hidden, grab to temporary %#x, start 1.0s shot timer %p",
|
|
xid, temp_xwin, ctx->shot_timer);
|
|
return;
|
|
|
|
error_free_ctx:
|
|
free(ctx);
|
|
error:
|
|
cb((void *)data, EINA_FALSE);
|
|
}
|
|
|
|
static void
|
|
_x_global_key_ungrab(const char *keyname)
|
|
{
|
|
const Eina_List *l;
|
|
const void *ptr;
|
|
|
|
EINA_LIST_FOREACH(roots, l, ptr)
|
|
{
|
|
Ecore_X_Window xid = (Ecore_X_Window)(long)ptr;
|
|
ecore_x_window_key_ungrab(xid, keyname, 0, 0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
x_pre_teardown(void)
|
|
{
|
|
X_Key_Grab *kg;
|
|
|
|
_x_ungrab();
|
|
|
|
if (temp_xwin)
|
|
{
|
|
ecore_x_window_free(temp_xwin);
|
|
temp_xwin = 0;
|
|
}
|
|
|
|
if (mouse_out_handler)
|
|
{
|
|
ecore_event_handler_del(mouse_out_handler);
|
|
mouse_out_handler = NULL;
|
|
}
|
|
|
|
if (key_down_handler)
|
|
{
|
|
ecore_event_handler_del(key_down_handler);
|
|
key_down_handler = NULL;
|
|
}
|
|
|
|
EINA_LIST_FREE(key_grabs, kg)
|
|
{
|
|
_x_global_key_ungrab(kg->keyname);
|
|
_x_key_grab_free(kg);
|
|
}
|
|
|
|
eina_list_free(roots);
|
|
roots = NULL;
|
|
eina_list_free(zones);
|
|
zones = NULL;
|
|
grabbed_zone = NULL;
|
|
}
|
|
|
|
static void
|
|
x_mouse_move_by(Zone *zone, int dx, int dy)
|
|
{
|
|
Evas_Object *win = zone_win_get(zone);
|
|
Ecore_X_Window xid = elm_win_xwindow_get(win);
|
|
int x, y, zx, zy, zw, zh;
|
|
|
|
ecore_x_pointer_xy_get(xid, &x, &y);
|
|
zone_geometry_get(zone, &zx, &zy, &zw, &zh);
|
|
|
|
x += dx;
|
|
y += dy;
|
|
|
|
if (x < 0)
|
|
x = 0;
|
|
if (y < 0)
|
|
y = 0;
|
|
|
|
if (x >= zw)
|
|
x = zw - 1;
|
|
|
|
if (y >= zh)
|
|
y = zh - 1;
|
|
|
|
ecore_x_pointer_warp(xid, x, y);
|
|
}
|
|
|
|
static void
|
|
x_windows_visibility_set(Eina_Bool visible)
|
|
{
|
|
const Zone *zone;
|
|
const Eina_List *l;
|
|
|
|
windows_visible = visible;
|
|
|
|
if (!visible)
|
|
_x_ungrab();
|
|
|
|
EINA_LIST_FOREACH(zones, l, zone)
|
|
{
|
|
Evas_Object *win = zone_win_get(zone);
|
|
Ecore_X_Window xid = elm_win_xwindow_get(win);
|
|
|
|
if (visible)
|
|
ecore_x_window_show(xid);
|
|
else
|
|
ecore_x_window_hide(xid);
|
|
}
|
|
|
|
if (visible)
|
|
_x_grab_current();
|
|
}
|
|
|
|
static Eina_Bool
|
|
_x_key_down(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
|
|
{
|
|
Ecore_Event_Key *ev = event;
|
|
const Eina_List *l;
|
|
const X_Key_Grab *kg;
|
|
|
|
if (!ev->key)
|
|
return ECORE_CALLBACK_PASS_ON;
|
|
|
|
EINA_LIST_FOREACH(key_grabs, l, kg)
|
|
{
|
|
if (strcmp(ev->key, kg->keyname) == 0)
|
|
kg->cb((void *)kg->cb_data, kg->keyname);
|
|
}
|
|
|
|
return ECORE_CALLBACK_PASS_ON;
|
|
}
|
|
|
|
static void
|
|
x_global_key_grab(const char *keyname, void (*cb)(void *data, const char *keyname), const void *data)
|
|
{
|
|
X_Key_Grab *kg;
|
|
const Eina_List *l;
|
|
const void *ptr;
|
|
|
|
if (!key_down_handler)
|
|
{
|
|
key_down_handler = ecore_event_handler_add(ECORE_EVENT_KEY_DOWN,
|
|
_x_key_down, NULL);
|
|
}
|
|
|
|
EINA_LIST_FOREACH(roots, l, ptr)
|
|
{
|
|
Ecore_X_Window xid = (Ecore_X_Window)(long)ptr;
|
|
ecore_x_window_key_grab(xid, keyname, 0, 0);
|
|
ecore_x_event_mask_set(xid, ECORE_X_EVENT_MASK_KEY_DOWN);
|
|
}
|
|
|
|
kg = _x_key_grab_new(keyname, cb, data);
|
|
key_grabs = eina_list_append(key_grabs, kg);
|
|
}
|
|
|
|
const Platform_Funcs *
|
|
platform_funcs_x_get(void)
|
|
{
|
|
static const Platform_Funcs funcs = {
|
|
x_pre_setup,
|
|
x_post_setup,
|
|
x_zones_count,
|
|
x_zone_geometry_get,
|
|
x_zone_setup,
|
|
x_zone_screen_copy,
|
|
x_pre_teardown,
|
|
x_mouse_move_by,
|
|
x_windows_visibility_set,
|
|
x_global_key_grab,
|
|
};
|
|
return &funcs;
|
|
}
|