efm2/src/efm/efm_back_end.c

854 lines
26 KiB
C

#include "cmd.h"
#include "sort.h"
#include "efm.h"
#include "efm_util.h"
#include "efm_back_end.h"
#include "efm_private.h"
void
_size_message(Evas_Object *o, double v)
{
Edje_Message_Float msg;
// display sqrt of 0.0-?1.0 so we don't have single huge files push every
// other bar out
if (v > 0.0) v = sqrt(v);
msg.val = v;
edje_object_message_send(o, EDJE_MESSAGE_FLOAT, 1, &msg);
edje_object_message_signal_process(o);
}
static void
_size_bars_update(Smart_Data *sd)
{
Eina_List *bl, *il;
Icon *icon;
Block *block;
const char *s;
if (sd->config.view_mode != EFM_VIEW_MODE_LIST_DETAILED) return;
EINA_LIST_FOREACH(sd->blocks, bl, block)
{
if (block->realized_num <= 0) continue;
EINA_LIST_FOREACH(block->icons, il, icon)
{
if (!icon->realized) continue;
s = cmd_key_find(icon->cmd, "size");
if (s)
{
unsigned long long size = atoll(s);
if (sd->file_max > 0)
_size_message(icon->o_list_detail_swallow2[0],
(double)size / (double)sd->file_max);
else _size_message(icon->o_list_detail_swallow2[0], 0.0);
}
}
}
}
static void
_cb_size_bars_update_job(void *data)
{
Smart_Data *sd = data;
sd->size_bars_update_job = NULL;
_size_bars_update(sd);
}
static void
_size_bars_update_queue(Smart_Data *sd)
{
if (sd->size_bars_update_job) ecore_job_del(sd->size_bars_update_job);
sd->size_bars_update_job = ecore_job_add(_cb_size_bars_update_job, sd);
}
static void
_cb_size_max_update_job(void *data)
{
Smart_Data *sd = data;
Eina_List *il;
Icon *icon;
const char *s;
unsigned long long new_max = 0;
sd->size_max_update_job = NULL;
EINA_LIST_FOREACH(sd->icons, il, icon)
{
s = cmd_key_find(icon->cmd, "size");
if (s)
{
unsigned long long size = atoll(s);
if (size > new_max) new_max = size;
}
}
if (sd->file_max != new_max)
{
sd->file_max = new_max;
_size_bars_update_queue(sd);
}
}
static void
_size_bars_max_update_queue(Smart_Data *sd)
{
if (sd->size_max_update_job) ecore_job_del(sd->size_max_update_job);
sd->size_max_update_job = ecore_job_add(_cb_size_max_update_job, sd);
}
static void
_command(Smart_Data *sd, const char *cmd)
{
Cmd *c = cmd_parse(cmd);
Msg *msg;
void *ref;
if (!c) return;
// send the cmd to our thread to deal with
msg = eina_thread_queue_send(sd->thread_data->thq, sizeof(Msg), &ref);
msg->c = c;
eina_thread_queue_send_done(sd->thread_data->thq, ref);
}
static void
_process(Smart_Data_Thread *std, Ecore_Thread *th, Eina_List *batch)
{ // process a batch of commands that come from the back-end open process
Cmd *c, *c_tmp = NULL;
Eina_List *batch_new = NULL;
Eina_List *batch_tmp = NULL;
if (!batch) return;
// sort batch into batch_new where each set of things like add, mod, del
// if they are the same cmd then the files are sorted by path. we do this
// to speed up inserts in the main loop so we can assume a sorted list
// per batch. this speeds up file adds a lot as we can just do a walk of
// the batch (as long as commands stay the same like file-add) and then also
// walk the current file/icon list at the same time and skip to the insert
// spot which makes inserts very fast as both lists are known to be
// pre-sorted so at worst we walk N file icons in the current icon list
// per batch (and loading a dir is probably a series of batches of N
// file-add's where N is white reasonable
EINA_LIST_FREE(batch, c)
{
c->sort_mode = std->sd->config.sort_mode;
if (!batch_tmp)
{
batch_tmp = eina_list_append(batch_tmp, c);
c_tmp = c;
}
else if ((!strcmp(c->command, c_tmp->command)) &&
(!strcmp(c->command, "file-add")))
{
// works for file-add, file-del, file-mod
batch_tmp = eina_list_sorted_insert(batch_tmp, sort_cmd, c);
}
else
{
batch_tmp = eina_list_append(batch_tmp, c);
batch_new = eina_list_merge(batch_new, batch_tmp);
batch_tmp = NULL;
}
}
if (batch_tmp)
{
batch_new = eina_list_merge(batch_new, batch_tmp);
batch_tmp = NULL;
}
if (batch_new) ecore_thread_feedback(th, batch_new);
}
Eina_Bool
_cb_exe_del(void *data, int ev_type EINA_UNUSED, void *event)
{
Smart_Data *sd = data;
Ecore_Exe_Event_Del *ev = event;
Eina_List *l;
Pending_Exe_Del *pend;
// remove this exited slave process from our pending exe deletions
// this list should be pretty small of pending deletions so we don't
// need to optimize this with a hash or whatever
EINA_LIST_FOREACH(_pending_exe_dels, l, pend)
{
if (pend->exe == ev->exe)
{
pend->exe = NULL;
if (pend->timer)
{
ecore_timer_del(pend->timer);
pend->timer = NULL;
}
free(pend);
_pending_exe_dels = eina_list_remove_list(_pending_exe_dels, l);
break;
}
}
if (ev->exe == sd->exe_open)
{ // this process exiting is the back-end open process for active view
printf("ERROR: back-end open process died unexpectedly\n");
return ECORE_CALLBACK_DONE;
}
return ECORE_CALLBACK_PASS_ON;
}
Eina_Bool
_cb_exe_data(void *data, int ev_type EINA_UNUSED, void *event)
{
Smart_Data *sd = data;
Ecore_Exe_Event_Data *ev = event;
int i;
// if this exe doesn't match the view it is for - pass it on and skip
if (ev->exe != sd->exe_open) return ECORE_CALLBACK_PASS_ON;
// this exe data is for thijs view
for (i = 0; ev->lines[i].line; i++) _command(sd, ev->lines[i].line);
return ECORE_CALLBACK_DONE;
}
Eina_Bool
_cb_exe_pending_timer(void *data)
{ // timeout trying to kill off back-end open process
Pending_Exe_Del *pend = data;
pend->timer = NULL;
_pending_exe_dels = eina_list_remove(_pending_exe_dels, pend);
if (pend->exe)
{ // forcibly kill the back-end process as it did not exit on its own
ecore_exe_kill(pend->exe);
pend->exe = NULL;
}
free(pend);
return EINA_FALSE;
}
void
_cb_thread_main(void *data, Ecore_Thread *th)
{ // thread sits processing commands read from stdout from the back-end open
Smart_Data_Thread *std = data;
Msg *msg;
void *ref;
Cmd *c;
Eina_Bool block = EINA_FALSE;
const char *prev_cmd = NULL;
Eina_List *batch = NULL;
for (;;)
{ // sit in a loop soaking up commands on the input queue
if (ecore_thread_check(th)) break;
if (!block) msg = eina_thread_queue_poll(std->thq, &ref);
else
{
usleep(4000); // wait 4ms to collect more msg's
msg = eina_thread_queue_wait(std->thq, &ref);
block = EINA_FALSE;
}
if (msg)
{
if (!batch)
{
batch = eina_list_append(batch, msg->c);
eina_stringshare_replace(&(prev_cmd), msg->c->command);
}
else if ((prev_cmd) && (!strcmp(msg->c->command, prev_cmd)))
{
batch = eina_list_append(batch, msg->c);
}
else
{
_process(std, th, batch);
batch = NULL;
eina_stringshare_replace(&(prev_cmd), NULL);
batch = eina_list_append(batch, msg->c);
}
eina_thread_queue_wait_done(std->thq, ref);
}
else
{
block = EINA_TRUE;
_process(std, th, batch);
batch = NULL;
eina_stringshare_replace(&(prev_cmd), NULL);
}
}
EINA_LIST_FREE(batch, c) { cmd_free(c); }
eina_stringshare_replace(&(prev_cmd), NULL);
}
static void
_icon_xy_wh_get(Smart_Data *sd, Icon *icon, Cmd *c)
{
const char *s;
int x, y, w, h, raw_x = INVALID, raw_y = INVALID;
s = cmd_key_find(c, "meta.xy");
if (s)
{
if (sscanf(s, "%i,%i", &x, &y) == 2)
{
raw_x = x;
raw_y = y;
x *= _scale_get(sd);
y *= _scale_get(sd);
icon->geom.x = x;
icon->geom.y = y;
}
}
s = cmd_key_find(c, "meta.wh");
if (s)
{
if (sscanf(s, "%ix%i", &w, &h) == 2)
{
if (raw_x == INVALID)
{
w = ((raw_x + w) * _scale_get(sd)) - icon->geom.x;
h = ((raw_y + h) * _scale_get(sd)) - icon->geom.y;
}
else
{
w *= _scale_get(sd);
h *= _scale_get(sd);
}
icon->geom.w = w;
icon->geom.h = h;
}
}
}
static void
_max_size_recalc(Smart_Data *sd)
{
Icon *icon;
Eina_List *l;
sd->file_max = 0;
EINA_LIST_FOREACH(sd->icons, l, icon)
{
if (icon->info.size > sd->file_max) sd->file_max = icon->info.size;
}
}
static void
_icon_add_mod_props_get(Icon *icon, Cmd *c, const char *label)
{
const char *s;
unsigned long long size;
Eina_Bool recalc = EINA_FALSE, queue = EINA_FALSE;
eina_stringshare_replace(&(icon->info.label), label);
s = cmd_key_find(c, "label-clicked");
if (!s) s = cmd_key_find(c, "link-label-clicked");
eina_stringshare_replace(&(icon->info.label_clicked), s);
s = cmd_key_find(c, "label-selected");
if (!s) s = cmd_key_find(c, "link-label-selected");
eina_stringshare_replace(&(icon->info.label_selected), s);
s = cmd_key_find(c, "mime");
if (s) eina_stringshare_replace(&(icon->info.mime), s);
if (s) printf("XXXXX mime=%s\n", icon->info.mime);
s = cmd_key_find(c, "desktop-icon");
if (!s) s = cmd_key_find(c, "link-desktop-icon");
if (s) eina_stringshare_replace(&(icon->info.pre_lookup_icon), s);
s = cmd_key_find(c, "desktop-icon.lookup");
if (!s) s = cmd_key_find(c, "link-desktop-icon.lookup");
if (!s) s = cmd_key_find(c, "icon");
if ((s) && (s[0] == '/')) eina_stringshare_replace(&(icon->info.icon), s);
s = cmd_key_find(c, "link-desktop-icon-clicked");
if (!s) s = cmd_key_find(c, "desktop-icon-clicked");
if (s) eina_stringshare_replace(&(icon->info.icon_clicked), s);
s = cmd_key_find(c, "link-desktop-icon-selected");
if (!s) s = cmd_key_find(c, "desktop-icon-selected");
if (s) eina_stringshare_replace(&(icon->info.icon_selected), s);
s = cmd_key_find(c, "mime-icon");
if (s) eina_stringshare_replace(&(icon->info.mime_icon), s);
s = cmd_key_find(c, "thumb");
if (s) eina_stringshare_replace(&(icon->info.thumb), s);
s = cmd_key_find(c, "broken-link");
if ((s) && (!strcmp(s, "true"))) icon->info.broken = EINA_TRUE;
else icon->info.broken = EINA_FALSE;
s = cmd_key_find(c, "size");
if (s)
{
size = atoll(s);
if ((icon->info.size == icon->sd->file_max) && // icon already max size
(size < icon->sd->file_max)) // new size is now less
{
recalc = EINA_TRUE; // need to recalc what is max
queue = EINA_TRUE; // we need to also update all bars
}
icon->info.size = size;
if (size > icon->sd->file_max)
{ // new size is bigger than prev max size
icon->sd->file_max = size;
queue = EINA_TRUE; // queue all size bar updates
}
if (recalc) _max_size_recalc(icon->sd);
if (queue) _size_bars_update_queue(icon->sd);
}
}
static Eina_Bool
_cb_smart_timer(void *data)
{
Smart_Data_Timer *st = data;
Eina_Strbuf *buf = cmd_strbuf_new("timer");
cmd_strbuf_append(buf, "name", st->name);
cmd_strbuf_exe_consume(buf, st->sd->exe_open);
if (st->repeat) return EINA_TRUE;
st->sd->timers = eina_list_remove(st->sd->timers, st);
eina_stringshare_del(st->name);
free(st);
return EINA_FALSE;
}
void
_cb_thread_notify(void *data, Ecore_Thread *th EINA_UNUSED, void *msg)
{ // handle data from the view thread to the UI - this will be a batch of cmds
Smart_Data_Thread *std = data;
Smart_Data *sd = std->sd;
Eina_List *batch = msg;
Cmd *c, *cprev = NULL;
Eina_List *l, *il2, *il = NULL;
Icon *icon, *icon2;
Block *block;
const char *path, *file, *label, *s;
int file_adds = 0, file_dels = 0, tries = 0;
Eina_Bool found;
if (!sd)
{ // on cusp point - view gone but buffered thread feedback exists
EINA_LIST_FREE(batch, c) cmd_free(c);
return;
}
EINA_LIST_FOREACH(batch, l, c)
{
c->sort_mode = sd->config.sort_mode;
#define CMD_DONE \
cmd_free(c); \
continue
if (!strcmp(c->command, "list-begin"))
{
printf("XXXXX LIST BEGIN\n");
edje_object_part_text_set(sd->o_overlay_info, "e.text.busy_label",
"Loading");
edje_object_signal_emit(sd->o_overlay_info, "e,state,busy,start", "e");
CMD_DONE;
}
else if (!strcmp(c->command, "list-end"))
{
printf("XXXXX LIST END\n");
edje_object_signal_emit(sd->o_overlay_info, "e,state,busy,stop", "e");
// reblock on end of listing
if (sd->reblock_job) ecore_job_del(sd->reblock_job);
sd->reblock_job = ecore_job_add(_cb_reblock, sd);
sd->listing_done_reblock = EINA_TRUE;
CMD_DONE;
}
else if (!strcmp(c->command, "timer-add"))
{
const char *name = cmd_key_find(c, "name");
const char *delay = cmd_key_find(c, "delay");
const char *repeat = cmd_key_find(c, "repeat");
if (name && delay)
{
Smart_Data_Timer *st = calloc(1, sizeof(Smart_Data_Timer));
if (st)
{
st->sd = sd;
st->name = eina_stringshare_add(name);
st->repeat = repeat ? EINA_TRUE : EINA_FALSE;
st->delay = (double)atoi(delay) / 1000.0;
st->timer = ecore_timer_add(st->delay, _cb_smart_timer, st);
sd->timers = eina_list_append(sd->timers, st);
}
}
CMD_DONE;
}
else if (!strcmp(c->command, "timer-del"))
{
const char *name = cmd_key_find(c, "name");
Smart_Data_Timer *st;
Eina_List *l;
if (name)
{
EINA_LIST_FOREACH(sd->timers, l, st)
{
if (!strcmp(st->name, name))
{
sd->timers = eina_list_remove_list(sd->timers, l);
eina_stringshare_del(st->name);
ecore_timer_del(st->timer);
free(st);
}
}
}
CMD_DONE;
}
else if (!strcmp(c->command, "backend-set"))
{ // *** must call before list-begin
const char *backend = cmd_key_find(c, "backend");
if (backend)
{
eina_stringshare_replace(&(sd->config.backend), backend);
_reset(sd);
}
CMD_DONE;
}
else if (!strcmp(c->command, "viewmode-set"))
{ // must call before list-begin
const char *mode = cmd_key_find(c, "mode");
if (mode)
{
Efm_View_Mode view_mode = sd->config.view_mode;
if (!strcmp(mode, "icons"))
view_mode = EFM_VIEW_MODE_ICONS;
else if (!strcmp(mode, "icons_custom"))
view_mode = EFM_VIEW_MODE_ICONS_CUSTOM;
else if (!strcmp(mode, "list"))
view_mode = EFM_VIEW_MODE_LIST;
else if (!strcmp(mode, "list_detailed"))
view_mode = EFM_VIEW_MODE_LIST_DETAILED;
if (view_mode != sd->config.view_mode)
{
int i;
sd->config.view_mode = view_mode;
for (i = 0; i < 6; i++)
{
if (sd->config.view_mode == EFM_VIEW_MODE_LIST_DETAILED)
evas_object_show(sd->o_list_detail[i]);
else evas_object_hide(sd->o_list_detail[i]);
}
if (sd->header_change_job)
ecore_job_del(sd->header_change_job);
sd->header_change_job = ecore_job_add(_cb_header_change, sd);
}
}
CMD_DONE;
}
else if (!strcmp(c->command, "detail-header-set"))
{
const char *col = cmd_key_find(c, "col");
if (col)
{
int colnum = atoi(col);
if ((colnum >= 0) && (colnum <= 6))
{
const char *label = cmd_key_find(c, "label");
const char *size = cmd_key_find(c, "size");
// XXX: support icon in future?
if (label)
{
eina_stringshare_replace
(&(sd->config.detail_heading[colnum]), label);
if (sd->o_detail_header_item[colnum])
elm_object_text_set(sd->o_detail_header_item[colnum],
sd->config.detail_heading[colnum]);
}
if ((size) && (colnum >= 1))
{ // can't set width of col 0 - always "rest of width"
int sz = atoi(size);
sd->config.detail_min_w[colnum - 1] = sz;
_redo_detail_sizes(sd);
}
}
}
CMD_DONE;
}
// below commands all send a path for a specific file
path = file = cmd_key_find(c, "path");
printf("XXXXX [%s] [%s]\n", c->command, file);
if (file)
{
s = strrchr(file, '/');
if (s) file = s + 1;
if (file[0] == '.') // XXX filter dot files or not
{
CMD_DONE;
}
}
if ((!file) || (!file[0]))
{ // somehow we didn't get a sane filename from the back-end
CMD_DONE;
}
if (!strcmp(c->command, "dir-request"))
{
char *path2;
path2 = _sanitize_dir(path);
eina_stringshare_replace(&sd->config.path, path2);
free(path2);
_reset(sd);
CMD_DONE;
}
label = cmd_key_find(c, "link-label");
if (!label) label = cmd_key_find(c, "label");
if ((!cprev) || (!!strcmp(cprev->command, c->command)))
{ // we start a new batch of commands - these are sorted
il = sd->icons;
}
if (!strcmp(c->command, "file-add"))
{
icon = calloc(1, sizeof(Icon));
if (!icon) abort();
file_adds++;
// initial "invalid" xy for custom location - meta overrides
icon->geom.x = INVALID;
icon->geom.y = INVALID;
icon->geom.w = INVALID;
icon->geom.h = INVALID;
icon->sd = sd;
icon->cmd = c;
icon->changed = EINA_TRUE;
icon->info.file = eina_stringshare_add(file);
_icon_add_mod_props_get(icon, c, label);
// handle raw "stat" type and set mime accordingly if needed
// this can't change on a file - it'd have to be deleted and
// added again to do this.
s = cmd_key_find(c, "type");
if (s)
{
if (!strcmp(s, "link"))
{
icon->info.link = EINA_TRUE;
s = cmd_key_find(c, "link-type");
}
if (s)
{
if (!strcmp(s, "dir"))
{
icon->info.dir = EINA_TRUE;
eina_stringshare_replace((&icon->info.mime),
"inode/directory");
}
else if (!strcmp(s, "block"))
{
icon->info.special = EINA_TRUE;
eina_stringshare_replace((&icon->info.mime),
"inode/blockdevice");
}
else if (!strcmp(s, "char"))
{
icon->info.special = EINA_TRUE;
eina_stringshare_replace((&icon->info.mime),
"inode/chardevice");
}
else if (!strcmp(s, "fifo"))
{
icon->info.special = EINA_TRUE;
eina_stringshare_replace((&icon->info.mime), "inode/fifo");
}
else if (!strcmp(s, "socket"))
{
icon->info.special = EINA_TRUE;
eina_stringshare_replace((&icon->info.mime),
"inode/socket");
}
}
}
if (!icon->info.mime)
eina_stringshare_replace((&icon->info.mime), "inode/file");
_icon_xy_wh_get(sd, icon, c);
// insert the icon in the right place in the list
for (; il; il = il->next)
{
icon2 = il->data;
if (!strcmp(file, icon2->info.file))
{ // handle the case we get an add for an existing file
file_dels++;
il2 = il->next;
sd->icons = eina_list_remove_list(sd->icons, il);
il = il2;
if (sd->last_focused)
{
// XXX: select prev or next icon
}
s = cmd_key_find(icon->cmd, "size");
if (s)
{
unsigned long long size = atoll(s);
if (size == sd->file_max) _size_bars_max_update_queue(sd);
}
_icon_free(icon2);
if (il) icon2 = il->data;
else break;
}
if (sort_cmd(icon2->cmd, icon->cmd) > 0)
{
sd->icons
= eina_list_prepend_relative_list(sd->icons, icon, il);
break;
}
}
if (!il) sd->icons = eina_list_append(sd->icons, icon);
}
else if (!strcmp(c->command, "file-mod"))
{
for (found = EINA_FALSE, tries = 0; (!found) && (tries < 2); tries++)
{
for (; il; il = il->next)
{
icon = il->data;
if (!strcmp(file, icon->info.file))
{
icon->changed = EINA_TRUE;
_icon_add_mod_props_get(icon, c, label);
_icon_xy_wh_get(sd, icon, c);
cmd_free(icon->cmd);
icon->cmd = c;
found = EINA_TRUE;
break;
}
}
if (!il) il = sd->icons;
}
}
else if (!strcmp(c->command, "file-del"))
{
for (found = EINA_FALSE, tries = 0; (!found) && (tries < 2); tries++)
{
for (; il; il = il->next)
{
icon = il->data;
if (!strcmp(file, icon->info.file))
{
file_dels++;
il2 = il->next;
sd->icons = eina_list_remove_list(sd->icons, il);
il = il2;
if (sd->last_focused)
{
// XXX: select prev or next icon
}
if (icon->info.size == icon->sd->file_max)
{ // this icon is at max size in dir
_max_size_recalc(icon->sd); // find new max
_size_bars_update_queue(icon->sd); // redo all bars
}
_icon_free(icon);
found = EINA_TRUE;
break;
}
}
if (!il) il = sd->icons;
}
cmd_free(c);
c = NULL;
}
else if (!strcmp(c->command, "file-detail"))
{
int n;
const char *theme_edj_file;
char **plist;
char bufdetail[16];
theme_edj_file
= elm_theme_group_path_find(NULL, "e/fileman/default/icon/fixed");
for (found = EINA_FALSE, tries = 0; (!found) && (tries < 2); tries++)
{
for (n = 0; il; il = il->next, n++)
{
icon = il->data;
if (!strcmp(file, icon->info.file))
{
int i;
s = cmd_key_find(icon->cmd, "detail-format");
if (s)
{
plist = eina_str_split(s, ",", 6);
if (plist)
{
for (i = 0; plist[i];) i++;
if (i >= 6)
{
for (i = 1; i < 7; i++)
{
snprintf(bufdetail, sizeof(bufdetail),
"detail%i", i);
s = cmd_key_find(c, bufdetail);
if (s)
{
icon->cmd
= cmd_modify(icon->cmd, bufdetail, s);
if (icon->realized)
_icon_detail_add(
icon, icon->sd,
evas_object_evas_get(icon->o_base),
theme_edj_file, i - 1, s,
plist[i - 1]);
}
}
}
free(*plist);
free(plist);
}
}
found = EINA_TRUE;
break;
}
}
cmd_free(c);
c = NULL;
}
}
cprev = c;
}
eina_list_free(batch);
if ((sd->config.view_mode >= EFM_VIEW_MODE_LIST)
&& ((file_adds > 0) || (file_dels > 0)))
{ // if it's one of the list modes, unrealize realized icons
EINA_LIST_FOREACH(sd->blocks, l, block)
{
if (block->realized_num <= 0) continue;
EINA_LIST_FOREACH(block->icons, il, icon)
{ // unrealize the icon - list odd/even forces this
if (!icon->realized) continue;
icon->realized = EINA_FALSE;
icon->block->realized_num--;
_icon_object_clear(icon);
}
}
}
if (sd->reblock_job) ecore_job_del(sd->reblock_job);
sd->reblock_job = ecore_job_add(_cb_reblock, sd);
}
void
_cb_thread_done(void *data, Ecore_Thread *th EINA_UNUSED)
{
Smart_Data_Thread *std = data;
if (std->sd) std->sd->thread_data = NULL;
if (std->thq)
{
eina_thread_queue_free(std->thq);
std->thq = NULL;
}
free(std);
}