Terminal emulator with all the bells and whistles https://www.enlightenment.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4275 lines
126 KiB

#include "private.h"
#include <Elementary.h>
#include <Elementary_Cursor.h>
#include <Ecore_Input.h>
#include "termio.h"
#include "termiolink.h"
#include "termpty.h"
#include "backlog.h"
#include "extns.h"
#include "termptyops.h"
#include "termcmd.h"
#include "termptydbl.h"
#include "utf8.h"
#include "colors.h"
#include "keyin.h"
#include "config.h"
#include "theme.h"
#include "media.h"
#include "miniview.h"
#include "gravatar.h"
#include "sb.h"
#include "utils.h"
#if defined (__MacOSX__) || (defined (__MACH__) && defined (__APPLE__))
# include <sys/proc_info.h>
# include <libproc.h>
#endif
static Evas_Smart *_smart = NULL;
static Evas_Smart_Class _parent_sc = EVAS_SMART_CLASS_INIT_NULL;
static Eina_List *terms = NULL;
static void _smart_apply(Evas_Object *obj);
static void _smart_size(Evas_Object *obj, int w, int h, Eina_Bool force);
static void _smart_calculate(Evas_Object *obj);
static Eina_Bool _mouse_in_selection(Termio *sd, int cx, int cy);
/* {{{ Helpers */
Termio *
termio_get_from_obj(Evas_Object *obj)
{
return evas_object_smart_data_get(obj);
}
void
termio_object_geometry_get(Termio *sd,
Evas_Coord *x, Evas_Coord *y,
Evas_Coord *w, Evas_Coord *h)
{
evas_object_geometry_get(sd->self, x, y, w, h);
}
static void
_win_obj_del(void *data,
Evas *_e EINA_UNUSED,
Evas_Object *obj,
void *_event EINA_UNUSED)
{
Termio *sd = evas_object_smart_data_get(data);
EINA_SAFETY_ON_NULL_RETURN(sd);
if (obj == sd->win)
{
evas_object_event_callback_del_full(sd->win, EVAS_CALLBACK_DEL,
_win_obj_del, data);
sd->win = NULL;
}
}
void
termio_theme_set(Evas_Object *obj, Evas_Object *theme)
{
Termio *sd = evas_object_smart_data_get(obj);
Evas_Object *bg;
EINA_SAFETY_ON_NULL_RETURN(sd);
EINA_SAFETY_ON_NULL_RETURN(sd->grid.obj);
EINA_SAFETY_ON_NULL_RETURN(theme);
sd->theme = theme;
bg = term_bg_get(sd->term);
EINA_SAFETY_ON_NULL_RETURN(bg);
edje_object_color_class_get(bg, "BG",
&sd->saved_bg.r,
&sd->saved_bg.g,
&sd->saved_bg.b,
&sd->saved_bg.a,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL);
edje_object_color_class_get(sd->cursor.obj, "CURSOR",
&sd->saved_cursor.r,
&sd->saved_cursor.g,
&sd->saved_cursor.b,
&sd->saved_cursor.a,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL);
evas_object_textgrid_palette_get(
sd->grid.obj,
EVAS_TEXTGRID_PALETTE_STANDARD, 0,
&sd->saved_fg.r,
&sd->saved_fg.g,
&sd->saved_fg.b,
&sd->saved_fg.a);
}
void
termio_reset_main_colors(Evas_Object *termio)
{
if (termio)
{
Termio *sd = evas_object_smart_data_get(termio);
Evas_Object *bg;
EINA_SAFETY_ON_NULL_RETURN(sd);
EINA_SAFETY_ON_NULL_RETURN(sd->grid.obj);
bg = term_bg_get(sd->term);
EINA_SAFETY_ON_NULL_RETURN(bg);
edje_object_color_class_set(bg, "BG",
sd->saved_bg.r,
sd->saved_bg.g,
sd->saved_bg.b,
sd->saved_bg.a,
sd->saved_bg.r,
sd->saved_bg.g,
sd->saved_bg.b,
sd->saved_bg.a,
sd->saved_bg.r,
sd->saved_bg.g,
sd->saved_bg.b,
sd->saved_bg.a);
edje_object_color_class_set(sd->cursor.obj, "CURSOR",
sd->saved_cursor.r,
sd->saved_cursor.g,
sd->saved_cursor.b,
sd->saved_cursor.a,
sd->saved_cursor.r,
sd->saved_cursor.g,
sd->saved_cursor.b,
sd->saved_cursor.a,
sd->saved_cursor.r,
sd->saved_cursor.g,
sd->saved_cursor.b,
sd->saved_cursor.a);
evas_object_textgrid_palette_set(
sd->grid.obj,
EVAS_TEXTGRID_PALETTE_STANDARD, 0,
sd->saved_fg.r,
sd->saved_fg.g,
sd->saved_fg.b,
sd->saved_fg.a);
}
}
void
termio_mouseover_suspend_pushpop(Evas_Object *obj, int dir)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN(sd);
sd->link.suspend += dir;
if (sd->link.suspend < 0) sd->link.suspend = 0;
if (sd->link.suspend)
{
if (sd->anim) ecore_animator_del(sd->anim);
sd->anim = NULL;
}
termio_smart_update_queue(sd);
}
void
5 years ago
termio_size_get(const Evas_Object *obj, int *w, int *h)
{
const Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN(sd);
if (w) *w = sd->grid.w;
if (h) *h = sd->grid.h;
}
void
termio_character_size_get(const Evas_Object *obj, int *w, int *h)
{
const Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN(sd);
if (w) *w = sd->font.chw;
if (h) *h = sd->font.chh;
}
int
5 years ago
termio_scroll_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, 0);
return sd->scroll;
}
void
termio_scroll_delta(Evas_Object *obj, int delta, int by_page)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN(sd);
if (by_page)
{
int by = sd->grid.h - 2;
if (by > 1)
delta *= by;
}
sd->scroll += delta;
if ((delta <= 0) && (sd->scroll < 0)) sd->scroll = 0;
termio_smart_update_queue(sd);
miniview_position_offset(term_miniview_get(sd->term), -delta, EINA_TRUE);
}
void
termio_scroll_set(Evas_Object *obj, int scroll)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN(sd);
sd->scroll = scroll;
termio_remove_links(sd);
_smart_apply(obj);
}
void
termio_scroll_top_backlog(Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN(sd);
sd->scroll = INT32_MAX;
termio_remove_links(sd);
_smart_apply(obj);
}
const char *
5 years ago
termio_title_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
if (sd->pty->prop.user_title)
return sd->pty->prop.user_title;
return sd->pty->prop.title;
}
const char *
termio_user_title_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->pty->prop.user_title;
}
void
termio_user_title_set(Evas_Object *obj, const char *title)
{
Termio *sd = evas_object_smart_data_get(obj);
size_t len = 0;
EINA_SAFETY_ON_NULL_RETURN(sd);
eina_stringshare_del(sd->pty->prop.user_title);
sd->pty->prop.user_title = NULL;
if (title)
{
len = strlen(title);
}
if (len)
sd->pty->prop.user_title = eina_stringshare_add_length(title, len);
if (sd->pty->cb.set_title.func)
sd->pty->cb.set_title.func(sd->pty->cb.set_title.data);
}
const char *
5 years ago
termio_icon_name_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->pty->prop.icon;
}
void
termio_media_mute_set(Evas_Object *obj, Eina_Bool mute)
{
Termio *sd = evas_object_smart_data_get(obj);
Eina_List *l;
Termblock *blk;
EINA_SAFETY_ON_NULL_RETURN(sd);
EINA_LIST_FOREACH(sd->pty->block.active, l, blk)
{
if (blk->obj && !blk->edje)
media_mute_set(blk->obj, mute);
}
}
void
termio_media_visualize_set(Evas_Object *obj, Eina_Bool visualize)
{
Termio *sd = evas_object_smart_data_get(obj);
Eina_List *l;
Termblock *blk;
EINA_SAFETY_ON_NULL_RETURN(sd);
EINA_LIST_FOREACH(sd->pty->block.active, l, blk)
{
if (blk->obj && !blk->edje)
media_visualize_set(blk->obj, visualize);
}
}
Eina_Bool
termio_selection_exists(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE);
return sd->pty->selection.is_active;
}
Termpty *
5 years ago
termio_pty_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->pty;
}
Evas_Object *
5 years ago
termio_miniview_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return term_miniview_get(sd->term);
}
Term*
5 years ago
termio_term_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->term;
}
Eina_Bool
termio_is_focused(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE);
return term_is_focused(sd->term);
}
Evas_Object *
termio_bg_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return term_bg_get(sd->term);
}
static void
_font_size_set(Evas_Object *obj, int size)
{
Termio *sd = evas_object_smart_data_get(obj);
Config *config;
EINA_SAFETY_ON_NULL_RETURN(sd);
int font_size_scale;
config = sd->config;
if (size < 5) size = 5;
else if (size > 100) size = 100;
font_size_scale = ELM_SCALE_SIZE(size);
if (sd->font_size_scale == font_size_scale) return;
sd->font_size_scale = font_size_scale;
if (config)
{
config->font.size = size;
sd->noreqsize = 1;
termio_config_update(obj);
sd->noreqsize = 0;
evas_object_data_del(obj, "sizedone");
}
}
void
termio_font_update(Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
Config *config;
EINA_SAFETY_ON_NULL_RETURN(sd);
config = sd->config;
if (config)
{
sd->noreqsize = 1;
termio_config_update(obj);
sd->noreqsize = 0;
evas_object_data_del(obj, "sizedone");
}
}
void
termio_font_size_set(Evas_Object *obj, int size)
{
_font_size_set(obj, size);
}
void
termio_grid_size_set(Evas_Object *obj, int w, int h)
{
Termio *sd = evas_object_smart_data_get(obj);
Evas_Coord mw = 1, mh = 1;
EINA_SAFETY_ON_NULL_RETURN(sd);
if (w < 1) w = 1;
if (h < 1) h = 1;
evas_object_size_hint_min_get(obj, &mw, &mh);
evas_object_data_del(obj, "sizedone");
evas_object_size_hint_request_set(obj, mw * w, mh * h);
}
pid_t
termio_pid_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, 0);
return termpty_pid_get(sd->pty);
}
Eina_Bool
termio_cwd_get(const Evas_Object *obj, char *buf, size_t size)
{
Termio *sd = evas_object_smart_data_get(obj);
pid_t pid;
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE);
pid = termpty_pid_get(sd->pty);
#if defined (__MacOSX__) || (defined (__MACH__) && defined (__APPLE__))
struct proc_vnodepathinfo vpi;
if (proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, 0, &vpi, sizeof(vpi)) <= 0)
{
ERR(_("Could not get working directory of pid %i: %s"),
pid, strerror(errno));
return EINA_FALSE;
}
memcpy(buf, vpi.pvi_cdir.vip_path, size);
#else
char procpath[PATH_MAX];
ssize_t siz;
snprintf(procpath, sizeof(procpath), "/proc/%ld/cwd", (long) pid);
siz = readlink(procpath, buf, size);
if ((siz == -1) || (siz >= (ssize_t)size))
{
ERR(_("Could not load working directory %s: %s"),
procpath, strerror(errno));
return EINA_FALSE;
}
buf[siz] = '\0';
#endif
buf[size -1] = '\0';
return EINA_TRUE;
}
Evas_Object *
5 years ago
termio_textgrid_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->grid.obj;
}
Evas_Object *
5 years ago
termio_win_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->win;
}
/* }}} */
/* {{{ Config */
void
termio_config_update(Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
Evas_Coord w, h, ow = 0, oh = 0;
EINA_SAFETY_ON_NULL_RETURN(sd);
eina_stringshare_del(sd->font.name);
sd->font.name = NULL;
if (sd->config->font.bitmap)
{
char buf[4096];
snprintf(buf, sizeof(buf), "%s/fonts/%s",
elm_app_data_dir_get(), sd->config->font.name);
sd->font.name = eina_stringshare_add(buf);
}
else
sd->font.name = eina_stringshare_add(sd->config->font.name);
sd->font.size = sd->config->font.size;
sd->jump_on_change = sd->config->jump_on_change;
sd->jump_on_keypress = sd->config->jump_on_keypress;
termpty_config_update(sd->pty, sd->config);
sd->scroll = 0;
colors_term_init(sd->grid.obj, sd->config->color_scheme);
evas_object_textgrid_font_set(sd->grid.obj, sd->font.name, sd->font.size);
evas_object_textgrid_cell_size_get(sd->grid.obj, &w, &h);
evas_object_scale_set(sd->grid.obj, elm_config_scale_get());
if (w < 1) w = 1;
if (h < 1) h = 1;
sd->font.chw = w;
sd->font.chh = h;
evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
termio_set_cursor_shape(obj, sd->config->cursor_shape);
_smart_size(obj, ow / w, oh / h, EINA_TRUE);
}
Config *
termio_config_get(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->config;
}
void
termio_config_set(Evas_Object *obj, Config *config)
{
Termio *sd = evas_object_smart_data_get(obj);
Evas_Coord w = 2, h = 2;
sd->config = config;
sd->jump_on_change = config->jump_on_change;
sd->jump_on_keypress = config->jump_on_keypress;
if (config->font.bitmap)
{
char buf[PATH_MAX];
snprintf(buf, sizeof(buf), "%s/fonts/%s",
elm_app_data_dir_get(), config->font.name);
sd->font.name = eina_stringshare_add(buf);
}
else
sd->font.name = eina_stringshare_add(config->font.name);
sd->font.size = config->font.size;
evas_object_textgrid_font_set(sd->grid.obj, sd->font.name, sd->font.size);
evas_object_textgrid_size_get(sd->grid.obj, &w, &h);
evas_object_scale_set(sd->grid.obj, elm_config_scale_get());
if (w < 1) w = 1;
if (h < 1) h = 1;
evas_object_textgrid_size_set(sd->grid.obj, w, h);
evas_object_textgrid_cell_size_get(sd->grid.obj, &w, &h);
if (w < 1) w = 1;
if (h < 1) h = 1;
sd->font.chw = w;
sd->font.chh = h;
termio_set_cursor_shape(obj, sd->cursor.shape);
theme_apply(sd->sel.theme, config, "terminology/selection",
NULL, NULL, EINA_FALSE);
theme_auto_reload_enable(sd->sel.theme);
edje_object_part_swallow(sd->sel.theme, "terminology.top_left", sd->sel.top);
edje_object_part_swallow(sd->sel.theme, "terminology.bottom_right", sd->sel.bottom);
}
static const char *
_cursor_shape_to_group_name(Cursor_Shape shape)
{
switch (shape)
{
case CURSOR_SHAPE_BLOCK: return "terminology/cursor";
case CURSOR_SHAPE_BAR: return "terminology/cursor_bar";
case CURSOR_SHAPE_UNDERLINE: return "terminology/cursor_underline";
}
return NULL;
}
Evas_Object *
termio_get_cursor(const Evas_Object *obj)
{
Termio *sd = evas_object_smart_data_get(obj);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd, NULL);
return sd->cursor.obj;
}
void
termio_set_cursor_shape(Evas_Object *obj, Cursor_Shape shape)
{
Termio *sd = evas_object_smart_data_get(obj);
Config *config;
EINA_SAFETY_ON_NULL_RETURN(sd);
config = sd->config;
theme_apply(sd->cursor.obj, config, _cursor_shape_to_group_name(shape),
NULL, NULL, EINA_FALSE);
theme_auto_reload_enable(sd->cursor.obj);
evas_object_resize(sd->cursor.obj, sd->font.chw, sd->font.chh);
evas_object_show(sd->cursor.obj);
sd->cursor.shape = shape;
if (term_is_focused(sd->term))
{
edje_object_signal_emit(sd->cursor.obj, "focus,out", "terminology");
if (sd->config->disable_cursor_blink)
edje_object_signal_emit(sd->cursor.obj, "focus,in,noblink", "terminology");
else
edje_object_signal_emit(sd->cursor.obj, "focus,in", "terminology");
}
}
/* }}} */
/* {{{ Links */
static Eina_Bool
_should_inline(const Evas_Object *obj)
{
const Config *config = termio_config_get(obj);
const Evas *e;
const Evas_Modifier *mods;
if (!config->helper.inline_please) return EINA_FALSE;
e = evas_object_evas_get(obj);
mods = evas_key_modifier_get(e);
if (evas_key_modifier_is_set(mods, "Control")) return EINA_FALSE;
return EINA_TRUE;
}
/*
* Returned string needs to be freed.
* Does not handle colors */
const char *
termio_link_get(const Evas_Object *obj,
Eina_Bool *from_escape_code)
{
Termio *sd = evas_object_smart_data_get(obj);