clouseau/src/lib/clouseau_app.c

506 lines
13 KiB
C

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "clouseau_private.h"
#include <Elementary.h>
#include <Ecore_X.h>
static Ecore_Con_Server *econ_server = NULL;
static Ecore_Con_Eet *eet_svr = NULL;
static Eina_Stringshare *_my_appname = NULL;
static Clouseau_Object *_clouseau_object_information_get(Clouseau_Tree_Item *treeit);
static void
libclouseau_item_add(Evas_Object *o, Clouseau_Tree_Item *parent)
{
Clouseau_Tree_Item *treeit;
Eina_List *children;
Evas_Object *child;
treeit = calloc(1, sizeof(Clouseau_Tree_Item));
if (!treeit) return ;
treeit->ptr = (uintptr_t) o;
treeit->is_obj = EINA_TRUE;
treeit->name = eina_stringshare_add(efl_class_name_get(o));
/* FIXME: Hack to work with old smart inheritance.
* Check if the class *is* (not just implements) Evas_Smart. */
if (efl_class_get(o) == EFL_CANVAS_GROUP_CLASS)
{
char buf[1024];
snprintf(buf, sizeof(buf), "%s (%s)", evas_object_type_get(o), treeit->name);
eina_stringshare_replace(&treeit->name, buf);
}
treeit->is_clipper = !!evas_object_clipees_get(o);
treeit->is_visible = evas_object_visible_get(o);
treeit->info = _clouseau_object_information_get(treeit);
parent->children = eina_list_append(parent->children, treeit);
/* if (!evas_object_smart_data_get(o)) return ; */
/* Do this only for smart object */
if (efl_isa(o, EFL_CANVAS_GROUP_CLASS))
{
children = evas_object_smart_members_get(o);
EINA_LIST_FREE(children, child)
libclouseau_item_add(child, treeit);
}
}
static void *
_canvas_bmp_get(Ecore_Evas *ee, Evas_Coord *w_out, Evas_Coord *h_out)
{
Ecore_X_Image *img;
Ecore_X_Window_Attributes att;
unsigned char *src;
unsigned int *dst;
int bpl = 0, rows = 0, bpp = 0;
Evas_Coord w, h;
/* Check that this window still exists */
Eina_List *eeitr, *ees = ecore_evas_ecore_evas_list_get();
Ecore_Evas *eel;
Eina_Bool found_evas = EINA_FALSE;
EINA_LIST_FOREACH(ees, eeitr, eel)
if (eel == ee)
{
found_evas = EINA_TRUE;
break;
}
Ecore_X_Window xwin = (found_evas) ?
(Ecore_X_Window) ecore_evas_window_get(ee) : 0;
if (!xwin)
{
printf("Can't grab X window.\n");
*w_out = *h_out = 0;
return NULL;
}
Evas *e = ecore_evas_get(ee);
evas_output_size_get(e, &w, &h);
memset(&att, 0, sizeof(Ecore_X_Window_Attributes));
ecore_x_window_attributes_get(xwin, &att);
img = ecore_x_image_new(w, h, att.visual, att.depth);
ecore_x_image_get(img, xwin, 0, 0, 0, 0, w, h);
src = ecore_x_image_data_get(img, &bpl, &rows, &bpp);
dst = malloc(w * h * sizeof(int)); /* Will be freed by the user */
if (!ecore_x_image_is_argb32_get(img))
{ /* Fill dst buffer with image convert */
ecore_x_image_to_argb_convert(src, bpp, bpl,
att.colormap, att.visual,
0, 0, w, h,
dst, (w * sizeof(int)), 0, 0);
}
else
{ /* Fill dst buffer by copy */
memcpy(dst, src, (w * h * sizeof(int)));
}
/* dst now holds window bitmap */
ecore_x_image_free(img);
*w_out = w;
*h_out = h;
return (void *) dst;
}
static Eina_List *
_load_list(void)
{
Eina_List *tree = NULL;
Eina_List *ees;
Ecore_Evas *ee;
ees = ecore_evas_ecore_evas_list_get();
EINA_LIST_FREE(ees, ee)
{
Eina_List *objs;
Evas_Object *obj;
Clouseau_Tree_Item *treeit;
Evas *e;
int w, h;
e = ecore_evas_get(ee);
evas_output_size_get(e, &w, &h);
treeit = calloc(1, sizeof(Clouseau_Tree_Item));
if (!treeit) continue ;
treeit->name = eina_stringshare_add(ecore_evas_title_get(ee));
treeit->ptr = (uintptr_t) ee;
tree = eina_list_append(tree, treeit);
objs = evas_objects_in_rectangle_get(e, SHRT_MIN, SHRT_MIN,
USHRT_MAX, USHRT_MAX, EINA_TRUE, EINA_TRUE);
EINA_LIST_FREE(objs, obj)
libclouseau_item_add(obj, treeit);
}
return tree; /* User has to call clouseau_tree_free() */
}
/* Highlight functions. */
static Eina_Bool
_clouseau_highlight_fade(void *_rect)
{
Evas_Object *rect = _rect;
int r, g, b, a;
double na;
evas_object_color_get(rect, &r, &g, &b, &a);
if (a < 20)
{
evas_object_del(rect);
/* The del callback of the object will destroy the animator */
return EINA_TRUE;
}
na = a - 5;
r = na / a * r;
g = na / a * g;
b = na / a * b;
evas_object_color_set(rect, r, g, b, na);
return EINA_TRUE;
}
static Evas_Object *
_clouseau_verify_e_children(Evas_Object *obj, Evas_Object *ptr)
{
/* Verify that obj still exists (can be found on evas canvas) */
Evas_Object *child;
Evas_Object *p = NULL;
Eina_List *children;
if (ptr == obj)
return ptr;
if (efl_isa(obj, EFL_CANVAS_GROUP_CLASS))
{
children = evas_object_smart_members_get(obj);
EINA_LIST_FREE(children, child)
{
p = _clouseau_verify_e_children(child, ptr);
if (p) break;
}
eina_list_free(children);
}
return p;
}
static Evas_Object *
_clouseau_verify_e_obj(Evas_Object *obj)
{
/* Verify that obj still exists (can be found on evas canvas) */
Evas_Object *o;
Eina_List *ees;
Ecore_Evas *ee;
Evas_Object *rt = NULL;
ees = ecore_evas_ecore_evas_list_get();
EINA_LIST_FREE(ees, ee)
{
Evas *e;
Eina_List *objs;
e = ecore_evas_get(ee);
objs = evas_objects_in_rectangle_get(e, SHRT_MIN, SHRT_MIN,
USHRT_MAX, USHRT_MAX,
EINA_TRUE, EINA_TRUE);
EINA_LIST_FREE(objs, o)
{
rt = _clouseau_verify_e_children(o, obj);
if (rt) break;
}
eina_list_free(objs);
if (rt) break;
}
eina_list_free(ees);
return rt;
}
static void
_clouseau_highlight_del(void *data,
EINA_UNUSED Evas *e,
EINA_UNUSED Evas_Object *obj,
EINA_UNUSED void *event_info)
{ /* Delete timer for this rect */
ecore_animator_del(data);
}
EAPI void
clouseau_data_object_highlight(Evas_Object *obj)
{
const Evas_Map *map;
Ecore_Animator *t;
Evas *e = NULL;
Evas_Object *r;
int x, y, wd, ht;
e = evas_object_evas_get(obj);
r = evas_object_polygon_add(e);
if ((map = evas_object_map_get(obj)))
{
int i = evas_map_count_get(map);
for ( ; i > 0 ; i--)
{
Evas_Coord mx, my;
evas_map_point_coord_get(map, i - 1, &mx, &my, NULL);
evas_object_polygon_point_add(r, mx, my);
}
}
else
{
evas_object_geometry_get(obj, &x, &y, &wd, &ht);
evas_object_polygon_point_add(r, x, y);
evas_object_polygon_point_add(r, x + wd, y);
evas_object_polygon_point_add(r, x + wd, y + ht);
evas_object_polygon_point_add(r, x, y + ht);
}
evas_object_layer_set(r, EVAS_LAYER_MAX);
evas_object_color_set(r,
HIGHLIGHT_R, HIGHLIGHT_G, HIGHLIGHT_B, HIGHLIGHT_A);
evas_object_show(r);
/* Add Timer for fade and a callback to delete timer on obj del */
t = ecore_animator_add(_clouseau_highlight_fade, r);
evas_object_event_callback_add(r, EVAS_CALLBACK_DEL,
_clouseau_highlight_del, t);
}
static Clouseau_Object *
_clouseau_object_information_get(Clouseau_Tree_Item *treeit)
{
Evas_Object *obj = (void*) (uintptr_t) treeit->ptr;
Efl_Dbg_Info *efl_dbg_info;
if (!treeit->is_obj)
return NULL;
efl_dbg_info = EFL_DBG_INFO_LIST_APPEND(NULL, "");
efl_dbg_info_get(obj, efl_dbg_info);
/* XXX: Edje information that should be here because Evas objects can't
* depend on Edje. This should be removed in the future. */
{
const char *part_name = edje_object_part_object_name_get(obj);
if (part_name)
{
Efl_Dbg_Info *group = EFL_DBG_INFO_LIST_APPEND(efl_dbg_info, "Edje_Part");
EFL_DBG_INFO_APPEND(group, "Part name", EINA_VALUE_TYPE_STRING, part_name);
}
}
treeit->eo_info = clouseau_eo_to_legacy_convert(efl_dbg_info);
efl_dbg_info_free(efl_dbg_info); /* Free original list */
return NULL;
}
Eina_Bool
_add(EINA_UNUSED void *data, Ecore_Con_Reply *reply,
EINA_UNUSED Ecore_Con_Server *conn)
{
/* ecore_con_server_data_size_max_set(conn, -1); */
connect_st t = { getpid(), _my_appname };
ecore_con_eet_send(reply, CLOUSEAU_APP_CLIENT_CONNECT_STR, &t);
return ECORE_CALLBACK_RENEW;
}
Eina_Bool
_del(EINA_UNUSED void *data, EINA_UNUSED Ecore_Con_Reply *reply,
Ecore_Con_Server *conn)
{
if (!conn)
{
printf("Failed to establish connection to the server.\n");
}
printf("Lost server with ip <%s>\n", ecore_con_server_ip_get(conn));
ecore_con_server_del(conn);
return ECORE_CALLBACK_RENEW;
}
void
_data_req_cb(EINA_UNUSED void *data, Ecore_Con_Reply *reply,
EINA_UNUSED const char *protocol_name, void *value)
{ /* data req includes ptr to GUI, to tell which client asking */
data_req_st *req = value;
tree_data_st t;
t.gui = req->gui; /* GUI client requesting data from daemon */
t.app = req->app; /* APP client sending data to daemon */
t.tree = _load_list();
if (t.tree)
{ /* Reply with tree data to data request */
ecore_con_eet_send(reply, CLOUSEAU_TREE_DATA_STR, &t);
clouseau_data_tree_free(t.tree);
}
}
void
_highlight_cb(EINA_UNUSED void *data, EINA_UNUSED Ecore_Con_Reply *reply,
EINA_UNUSED const char *protocol_name, void *value)
{ /* Highlight msg contains PTR of object to highlight */
highlight_st *ht = value;
Evas_Object *obj = (Evas_Object *) (uintptr_t) ht->object;
if (!_clouseau_verify_e_obj(obj))
{
printf("Evas Object not found <%p> (probably deleted)\n", obj);
return;
}
clouseau_data_object_highlight(obj);
}
void
_bmp_req_cb(EINA_UNUSED void *data, EINA_UNUSED Ecore_Con_Reply *reply,
EINA_UNUSED const char *protocol_name, void *value)
{ /* Bitmap req msg contains PTR of Ecore Evas */
bmp_req_st *req = value;
Evas_Coord w, h;
unsigned int size = 0;
void *bmp = _canvas_bmp_get((Ecore_Evas *) (uintptr_t)
req->object, &w, &h);
bmp_info_st t = { req->gui,
req->app, req->object , req->ctr, w, h,
NULL,NULL, NULL, 1.0,
NULL, NULL, NULL, NULL, NULL, NULL };
void *p = clouseau_data_packet_compose(CLOUSEAU_BMP_DATA_STR,
&t, &size, bmp, (w * h * sizeof(int)));
if (p)
{
ecore_con_eet_raw_send(reply, CLOUSEAU_BMP_DATA_STR, "BMP", p, size);
free(p);
}
if (bmp)
free(bmp);
}
typedef struct
{
Ecore_Event_Handler *ee_handle;
Ecore_Exe *daemon_exe;
} Msg_From_Daemon_Data;
static void
_msg_from_daemon_data_free(Msg_From_Daemon_Data *msg_data)
{
ecore_event_handler_del(msg_data->ee_handle);
ecore_exe_free(msg_data->daemon_exe);
free(msg_data);
}
static Eina_Bool
_msg_from_daemon(void *data, int type EINA_UNUSED, void *event)
{
Ecore_Exe_Event_Data *msg = (Ecore_Exe_Event_Data *)event;
if (!strncmp(msg->data, CLOUSEAUD_READY_STR, sizeof(CLOUSEAUD_READY_STR)))
{
const char *address = LOCALHOST;
if (eet_svr)
{
fprintf(stderr, "Clouseau: Trying to connect to daemon although already supposedly connected. Error.\n");
return ECORE_CALLBACK_DONE;
}
econ_server = ecore_con_server_connect(ECORE_CON_REMOTE_TCP,
LOCALHOST, PORT, NULL);
if (!econ_server)
{
printf("could not connect to the server: %s, port %d.\n",
address, PORT);
return ECORE_CALLBACK_DONE;
}
eet_svr = ecore_con_eet_client_new(econ_server);
if (!eet_svr)
{
printf("could not create con_eet client.\n");
return ECORE_CALLBACK_DONE;
}
clouseau_register_descs(eet_svr);
/* Register callbacks for ecore_con_eet */
ecore_con_eet_server_connect_callback_add(eet_svr, _add, NULL);
ecore_con_eet_server_disconnect_callback_add(eet_svr, _del, NULL);
ecore_con_eet_data_callback_add(eet_svr, CLOUSEAU_DATA_REQ_STR,
_data_req_cb, NULL);
ecore_con_eet_data_callback_add(eet_svr, CLOUSEAU_HIGHLIGHT_STR,
_highlight_cb, NULL);
ecore_con_eet_data_callback_add(eet_svr, CLOUSEAU_BMP_REQ_STR,
_bmp_req_cb, NULL);
_msg_from_daemon_data_free(data);
}
return ECORE_CALLBACK_DONE;
}
void
clouseau_app_disconnect(void)
{
ecore_con_eet_server_free(eet_svr);
eet_svr = NULL;
ecore_con_server_del(econ_server);
econ_server = NULL;
}
EAPI Eina_Bool
clouseau_app_connect(const char *appname)
{
Msg_From_Daemon_Data *msg_data = calloc(1, sizeof(*msg_data));
eina_stringshare_replace(&_my_appname, appname);
msg_data->ee_handle = ecore_event_handler_add(ECORE_EXE_EVENT_DATA, _msg_from_daemon, msg_data);
/* FIXME: Possibly have a better way to get rid of preload. */
msg_data->daemon_exe = ecore_exe_pipe_run("LD_PRELOAD='' " CLOUSEAUD_PATH,
ECORE_EXE_PIPE_READ_LINE_BUFFERED |
ECORE_EXE_PIPE_READ, NULL);
if (!msg_data->daemon_exe)
{
_msg_from_daemon_data_free(msg_data);
fprintf(stderr, "Could not start the daemon.!\n");
return EINA_FALSE;
}
return EINA_TRUE;
}