diff --git a/src/bin/meson.build b/src/bin/meson.build index 44ea008e..c179e628 100644 --- a/src/bin/meson.build +++ b/src/bin/meson.build @@ -23,6 +23,7 @@ terminology_sources = ['private.h', 'coverity.h', 'termio.c', 'termio.h', 'termcmd.c', 'termcmd.h', 'term_container.h', + 'termiointernals.c', 'termiointernals.h', 'termiolink.c', 'termiolink.h', 'termpty.c', 'termpty.h', 'termptydbl.c', 'termptydbl.h', @@ -54,9 +55,13 @@ tyfuzz_sources = ['termptyesc.c', 'termptyesc.h', 'termptyext.c', 'termptyext.h', 'termptygfx.c', 'termptygfx.h', 'termpty.c', 'termpty.h', + 'termiointernals.c', 'termiointernals.h', + 'termiolink.c', 'termiolink.h', 'config.c', 'config.h', 'col.c', 'col.h', 'sb.c', 'sb.h', + 'utf8.c', 'utf8.h', + 'utils.c', 'utils.h', 'tyfuzz.c'] tytest_sources = ['termptyesc.c', 'termptyesc.h', 'termptysave.c', 'termptysave.h', @@ -65,9 +70,13 @@ tytest_sources = ['termptyesc.c', 'termptyesc.h', 'termptyext.c', 'termptyext.h', 'termptygfx.c', 'termptygfx.h', 'termpty.c', 'termpty.h', + 'termiointernals.c', 'termiointernals.h', + 'termiolink.c', 'termiolink.h', 'config.c', 'config.h', 'col.c', 'col.h', 'sb.c', 'sb.h', + 'utf8.c', 'utf8.h', + 'utils.c', 'utils.h', 'md5/md5.c', 'md5/md5.h', 'tytest.c', 'tytest.h'] diff --git a/src/bin/termio.c b/src/bin/termio.c index 8b78ccfd..c2fe45ee 100644 --- a/src/bin/termio.c +++ b/src/bin/termio.c @@ -24,225 +24,21 @@ # include #endif -typedef struct _Termio Termio; - -struct _Termio -{ - Evas_Object_Smart_Clipped_Data __clipped_data; - struct { - const char *name; - int size; - int chw, chh; - } font; - struct { - int w, h; - Evas_Object *obj; - } grid; - struct { - Evas_Object *top, *bottom, *theme; - } sel; - struct { - Evas_Object *obj; - int x, y; - Cursor_Shape shape; - } cursor; - struct { - int cx, cy; - int button; - } mouse; - struct { - const char *string; - int x1, y1, x2, y2; - int suspend; - uint16_t id; - Eina_List *objs; - struct { - Evas_Object *dndobj; - Evas_Coord x, y; - unsigned char down : 1; - unsigned char dnd : 1; - unsigned char dndobjdel : 1; - } down; - } link; - struct { - const char *file; - FILE *f; - double progress; - unsigned long long total, size; - Eina_Bool active : 1; - } sendfile; - Evas_Object *ctxpopup; - int zoom_fontsize_start; - int scroll; - Evas_Object *self; - Evas_Object *event; - Term *term; - - Termpty *pty; - Ecore_Animator *anim; - Ecore_Timer *delayed_size_timer; - Ecore_Timer *link_do_timer; - Ecore_Timer *mouse_selection_scroll_timer; - Ecore_Job *mouse_move_job; - Ecore_Timer *mouseover_delay; - Evas_Object *win, *theme, *glayer; - Config *config; - const char *sel_str; - Eina_List *cur_chids; - Ecore_Job *sel_reset_job; - double set_sel_at; - Elm_Sel_Type sel_type; - unsigned char jump_on_change : 1; - unsigned char jump_on_keypress : 1; - unsigned char have_sel : 1; - unsigned char noreqsize : 1; - unsigned char didclick : 1; - unsigned char moved : 1; - unsigned char bottom_right : 1; - unsigned char top_left : 1; - unsigned char reset_sel : 1; - unsigned char cb_added : 1; - double gesture_zoom_start_size; -}; - -#define INT_SWAP(_a, _b) do { \ - int _swap = _a; _a = _b; _b = _swap; \ -} while (0) - static Evas_Smart *_smart = NULL; static Evas_Smart_Class _parent_sc = EVAS_SMART_CLASS_INIT_NULL; static Eina_List *terms = NULL; -static void _sel_set(Termio *sd, Eina_Bool enable); static void _remove_links(Termio *sd); -static void _smart_update_queue(Termio *sd); 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 void _take_selection_text(Termio *sd, Elm_Sel_Type type, const char *text); -static void _smart_xy_to_cursor(Termio *sd, Evas_Coord x, Evas_Coord y, int *cx, int *cy); static Eina_Bool _mouse_in_selection(Termio *sd, int cx, int cy); -static void _selection_get(const Evas_Object *obj, - int c1x, int c1y, int c2x, int c2y, - struct ty_sb *sb, - Eina_Bool rtrim); /* {{{ Helpers */ -static void -_termio_scroll_selection(Termio *sd, Termpty *ty, - int direction, int start_y, int end_y) -{ - if (!ty->selection.is_active) - return; - - int sel_start_x = ty->selection.start.x; - int sel_start_y = ty->selection.start.y; - int sel_end_x = ty->selection.end.x; - int sel_end_y = ty->selection.end.y; - - int left_margin = ty->termstate.left_margin; - int right_margin = ty->termstate.right_margin; - - if (!ty->selection.is_top_to_bottom) - { - INT_SWAP(sel_start_y, sel_end_y); - INT_SWAP(sel_start_x, sel_end_x); - } - - if (start_y <= sel_start_y && - sel_end_y <= end_y) - { - if (ty->termstate.left_margin) - { - if ((ty->selection.is_box) || (sel_start_y == sel_end_y)) - { - /* if selection outside scrolling area */ - if ((sel_end_x <= left_margin) || - (sel_start_x >= right_margin)) - { - return; - } - /* if selection not within scrolling area */ - if (!((sel_start_x >= left_margin) && - (sel_end_x <= right_margin))) - { - _sel_set(sd, EINA_FALSE); - return; - } - } - else - { - _sel_set(sd, EINA_FALSE); - return; - } - } - - ty->selection.orig.y += direction; - ty->selection.start.y += direction; - ty->selection.end.y += direction; - sel_start_y += direction; - sel_end_y += direction; - if (!(start_y <= sel_start_y && - sel_end_y <= end_y)) - { - _sel_set(sd, EINA_FALSE); - } - } - else if (!((start_y > sel_end_y) || - (end_y < sel_start_y))) - { - if (ty->termstate.left_margin) - { - if ((ty->selection.is_box) || (sel_start_y == sel_end_y)) - { - /* if selection outside scrolling area */ - if ((sel_end_x <= left_margin) || - (sel_start_x >= right_margin)) - { - return; - } - } - } - _sel_set(sd, EINA_FALSE); - } - else if (sd->scroll > 0) - { - ty->selection.orig.y += direction; - ty->selection.start.y += direction; - ty->selection.end.y += direction; - } -} - -void -termio_scroll(Evas_Object *obj, int direction, - int start_y, int end_y) -{ - Termio *sd = evas_object_smart_data_get(obj); - Termpty *ty; - - EINA_SAFETY_ON_NULL_RETURN(sd); - - ty = sd->pty; - - if ((!sd->jump_on_change) && // if NOT scroll to bottom on updates - (sd->scroll > 0)) - { - Evas_Object *mv = term_miniview_get(sd->term); - if (mv) - { - miniview_position_offset(mv, direction, EINA_FALSE); - } - // adjust scroll position for added scrollback - sd->scroll -= direction; - } - - _termio_scroll_selection(sd, ty, direction, start_y, end_y); -} - static void _win_obj_del(void *data, Evas *_e EINA_UNUSED, @@ -281,7 +77,7 @@ termio_mouseover_suspend_pushpop(Evas_Object *obj, int dir) if (sd->anim) ecore_animator_del(sd->anim); sd->anim = NULL; } - _smart_update_queue(sd); + termio_smart_update_queue(sd); } void @@ -316,7 +112,7 @@ termio_scroll_delta(Evas_Object *obj, int delta, int by_page) sd->scroll += delta; if (delta <= 0 && sd->scroll < 0) sd->scroll = 0; - _smart_update_queue(sd); + termio_smart_update_queue(sd); miniview_position_offset(term_miniview_get(sd->term), -delta, EINA_TRUE); } @@ -1012,6 +808,87 @@ _cb_ctxp_link_open(void *data, evas_object_del(obj); } +static void _lost_selection(void *data, Elm_Sel_Type selection); + +static void +_lost_selection_reset_job(void *data) +{ + Termio *sd = evas_object_smart_data_get(data); + EINA_SAFETY_ON_NULL_RETURN(sd); + + sd->sel_reset_job = NULL; + if (sd->sel_str) + { + elm_cnp_selection_set(sd->win, sd->sel_type, + ELM_SEL_FORMAT_TEXT, + sd->sel_str, strlen(sd->sel_str)); + elm_cnp_selection_loss_callback_set(sd->win, sd->sel_type, + _lost_selection, data); + } +} + +static void +_lost_selection(void *data, Elm_Sel_Type selection) +{ + Eina_List *l; + Evas_Object *obj; + double t = ecore_time_get(); + + EINA_LIST_FOREACH(terms, l, obj) + { + Termio *sd = evas_object_smart_data_get(obj); + if (!sd) continue; + if ((t - sd->set_sel_at) < 0.2) /// hack + { + if ((sd->have_sel) && (sd->sel_str) && (!sd->reset_sel)) + { + sd->reset_sel = EINA_TRUE; + if (sd->sel_reset_job) ecore_job_del(sd->sel_reset_job); + sd->sel_reset_job = ecore_job_add + (_lost_selection_reset_job, data); + } + continue; + } + if (sd->have_sel) + { + if (sd->sel_str) + { + eina_stringshare_del(sd->sel_str); + sd->sel_str = NULL; + } + termio_sel_set(sd, EINA_FALSE); + elm_object_cnp_selection_clear(sd->win, selection); + termio_smart_update_queue(sd); + sd->have_sel = EINA_FALSE; + } + } +} + + + +void +termio_take_selection_text(Termio *sd, Elm_Sel_Type type, const char *text) +{ + EINA_SAFETY_ON_NULL_RETURN(sd); + + text = eina_stringshare_add(text); + + sd->have_sel = EINA_FALSE; + sd->reset_sel = EINA_FALSE; + sd->set_sel_at = ecore_time_get(); // hack + sd->sel_type = type; + + elm_cnp_selection_set(sd->win, type, + ELM_SEL_FORMAT_TEXT, + text, + eina_stringshare_strlen(text)); + elm_cnp_selection_loss_callback_set(sd->win, type, + _lost_selection, sd->self); + sd->have_sel = EINA_TRUE; + if (sd->sel_str) eina_stringshare_del(sd->sel_str); + sd->sel_str = text; +} + static void _cb_ctxp_link_content_copy(void *data, Evas_Object *obj, @@ -1028,18 +905,18 @@ _cb_ctxp_link_content_copy(void *data, if (!hl->url) return; - _take_selection_text(sd, ELM_SEL_TYPE_CLIPBOARD, hl->url); + termio_take_selection_text(sd, ELM_SEL_TYPE_CLIPBOARD, hl->url); } else { struct ty_sb sb = {.buf = NULL, .len = 0, .alloc = 0}; - _selection_get(term, - sd->link.x1, sd->link.y1, - sd->link.x2, sd->link.y2, - &sb, EINA_FALSE); + termio_selection_get(sd, + sd->link.x1, sd->link.y1, + sd->link.x2, sd->link.y2, + &sb, EINA_FALSE); raw_link = ty_sb_steal_buf(&sb); - _take_selection_text(sd, ELM_SEL_TYPE_CLIPBOARD, raw_link); + termio_take_selection_text(sd, ELM_SEL_TYPE_CLIPBOARD, raw_link); } sd->ctxpopup = NULL; @@ -1055,12 +932,123 @@ _cb_ctxp_link_copy(void *data, Termio *sd = evas_object_smart_data_get(term); EINA_SAFETY_ON_NULL_RETURN(sd); EINA_SAFETY_ON_NULL_RETURN(sd->link.string); - _take_selection_text(sd, ELM_SEL_TYPE_CLIPBOARD, sd->link.string); + termio_take_selection_text(sd, ELM_SEL_TYPE_CLIPBOARD, sd->link.string); sd->ctxpopup = NULL; evas_object_del(obj); } +static Eina_Bool +_mouse_in_selection(Termio *sd, int cx, int cy) +{ + int start_x = 0, start_y = 0, end_x = 0, end_y = 0; + + if (!sd->pty->selection.is_active) + return EINA_FALSE; + + start_x = sd->pty->selection.start.x; + start_y = sd->pty->selection.start.y; + end_x = sd->pty->selection.end.x; + end_y = sd->pty->selection.end.y; + + if (!sd->pty->selection.is_top_to_bottom) + { + INT_SWAP(start_y, end_y); + INT_SWAP(start_x, end_x); + } + if (sd->pty->selection.is_box) + { + if ((start_y <= cy) && (cy <= end_y) && + (start_x <= cx) && (cx <= end_x) ) + return EINA_TRUE; + } + else + { + if ((cy < start_y) || (cy > end_y)) + return EINA_FALSE; + if (((cy == start_y) && (cx < start_x)) || + ((cy == end_y) && (cx > end_x))) + return EINA_FALSE; + return EINA_TRUE; + } + return EINA_FALSE; +} + +static Eina_Bool +_getsel_cb(void *data, + Evas_Object *_obj EINA_UNUSED, + Elm_Selection_Data *ev) +{ + Termio *sd = evas_object_smart_data_get(data); + + EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE); + + if (ev->format == ELM_SEL_FORMAT_TEXT) + { + char *tmp; + + if (ev->len <= 0) return EINA_TRUE; + + tmp = malloc(ev->len); + if (tmp) + { + char *s = ev->data; + size_t i; + + // apparently we have to convert \n into \r in terminal land. + for (i = 0; i < ev->len && s[i]; i++) + { + tmp[i] = s[i]; + if (tmp[i] == '\n') tmp[i] = '\r'; + } + if (i) + { + + if (sd->pty->bracketed_paste) + termpty_write(sd->pty, "\x1b[200~", + sizeof("\x1b[200~") - 1); + + termpty_write(sd->pty, tmp, i); + + if (sd->pty->bracketed_paste) + termpty_write(sd->pty, "\x1b[201~", + sizeof("\x1b[201~") - 1); + } + + free(tmp); + } + } + else + { + const char *fmt = "UNKNOWN"; + switch (ev->format) + { + case ELM_SEL_FORMAT_TARGETS: fmt = "TARGETS"; break; /* shouldn't happen */ + case ELM_SEL_FORMAT_NONE: fmt = "NONE"; break; + case ELM_SEL_FORMAT_TEXT: fmt = "TEXT"; break; + case ELM_SEL_FORMAT_MARKUP: fmt = "MARKUP"; break; + case ELM_SEL_FORMAT_IMAGE: fmt = "IMAGE"; break; + case ELM_SEL_FORMAT_VCARD: fmt = "VCARD"; break; + case ELM_SEL_FORMAT_HTML: fmt = "HTML"; break; + } + WRN(_("unsupported selection format '%s'"), fmt); + } + return EINA_TRUE; +} + + +void +termio_paste_selection(Evas_Object *obj, Elm_Sel_Type type) +{ + Termio *sd = evas_object_smart_data_get(obj); + EINA_SAFETY_ON_NULL_RETURN(sd); + if (!sd->win) + return; + elm_cnp_selection_get(sd->win, type, ELM_SEL_FORMAT_TEXT, + _getsel_cb, obj); +} + + static void _cb_link_down(void *data, Evas *_e EINA_UNUSED, @@ -1098,7 +1086,7 @@ _cb_link_down(void *data, { int cx = 0, cy = 0; - _smart_xy_to_cursor(sd, ev->canvas.x, ev->canvas.y, &cx, &cy); + termio_cursor_to_xy(sd, ev->canvas.x, ev->canvas.y, &cx, &cy); if (_mouse_in_selection(sd, cx, cy)) return; } @@ -1114,13 +1102,13 @@ _cb_link_down(void *data, else { struct ty_sb sb = {.buf = NULL, .len = 0, .alloc = 0}; - _selection_get(sd->self, - sd->link.x1, - sd->link.y1, - sd->link.x2, - sd->link.y2, - &sb, - EINA_FALSE); + termio_selection_get(sd, + sd->link.x1, + sd->link.y1, + sd->link.x2, + sd->link.y2, + &sb, + EINA_FALSE); len = sb.len; raw_link = ty_sb_steal_buf(&sb); } @@ -2223,1586 +2211,6 @@ _block_obj_del(Termblock *blk) blk->obj = NULL; } -/* }}} */ -/* {{{ Selection */ - -static Eina_Bool -_mouse_in_selection(Termio *sd, int cx, int cy) -{ - int start_x = 0, start_y = 0, end_x = 0, end_y = 0; - - if (!sd->pty->selection.is_active) - return EINA_FALSE; - - start_x = sd->pty->selection.start.x; - start_y = sd->pty->selection.start.y; - end_x = sd->pty->selection.end.x; - end_y = sd->pty->selection.end.y; - - if (!sd->pty->selection.is_top_to_bottom) - { - INT_SWAP(start_y, end_y); - INT_SWAP(start_x, end_x); - } - if (sd->pty->selection.is_box) - { - if ((start_y <= cy) && (cy <= end_y) && - (start_x <= cx) && (cx <= end_x) ) - return EINA_TRUE; - } - else - { - if ((cy < start_y) || (cy > end_y)) - return EINA_FALSE; - if (((cy == start_y) && (cx < start_x)) || - ((cy == end_y) && (cx > end_x))) - return EINA_FALSE; - return EINA_TRUE; - } - return EINA_FALSE; -} - -static void -_selection_get(const Evas_Object *obj, - int c1x, int c1y, int c2x, int c2y, - struct ty_sb *sb, - Eina_Bool rtrim) -{ - Termio *sd = evas_object_smart_data_get(obj); - int x, y; - - EINA_SAFETY_ON_NULL_RETURN(sd); - termpty_backlog_lock(); - for (y = c1y; y <= c2y; y++) - { - Termcell *cells; - ssize_t w; - int last0, v, start_x, end_x; - - w = 0; - last0 = -1; - cells = termpty_cellrow_get(sd->pty, y, &w); - if (!cells || !w) - { - if (ty_sb_add(sb, "\n", 1) < 0) goto err; - continue; - } - if (w > sd->grid.w) w = sd->grid.w; - if (y == c1y && c1x >= w) - { - if (rtrim) - ty_sb_spaces_rtrim(sb); - if (ty_sb_add(sb, "\n", 1) < 0) goto err; - continue; - } - start_x = c1x; - end_x = (c2x >= w) ? w - 1 : c2x; - if (c1y != c2y) - { - if (y == c1y) end_x = w - 1; - else if (y == c2y) start_x = 0; - else - { - start_x = 0; - end_x = w - 1; - } - } - for (x = start_x; x <= end_x; x++) - { - if ((cells[x].codepoint == 0) && (cells[x].att.dblwidth)) - { - if (x < end_x) x++; - else break; - } - if (x >= w) break; - if (cells[x].att.newline) - { - last0 = -1; - if ((y != c2y) || (x != end_x)) - { - if (rtrim) - ty_sb_spaces_rtrim(sb); - if (ty_sb_add(sb, "\n", 1) < 0) goto err; - } - break; - } - else if (cells[x].codepoint == 0) - { - if (last0 < 0) last0 = x; - } - else - { - char txt[8]; - int txtlen; - - if (last0 >= 0) - { - v = x - last0 - 1; - last0 = -1; - while (v >= 0) - { - if (ty_sb_add(sb, " ", 1) < 0) goto err; - v--; - } - } - txtlen = codepoint_to_utf8(cells[x].codepoint, txt); - if (txtlen > 0) - if (ty_sb_add(sb, txt, txtlen) < 0) goto err; - if ((x == (w - 1)) && - ((x != c2x) || (y != c2y))) - { - if (!cells[x].att.autowrapped) - { - if (rtrim) - ty_sb_spaces_rtrim(sb); - if (ty_sb_add(sb, "\n", 1) < 0) goto err; - } - } - } - } - if (last0 >= 0) - { - if (y == c2y) - { - Eina_Bool have_more = EINA_FALSE; - - for (x = end_x + 1; x < w; x++) - { - if ((cells[x].codepoint == 0) && - (cells[x].att.dblwidth)) - { - if (x < (w - 1)) x++; - else break; - } - if (((cells[x].codepoint != 0) && - (cells[x].codepoint != ' ')) || - (cells[x].att.newline)) - { - have_more = EINA_TRUE; - break; - } - } - if (!have_more) - { - if (rtrim) - ty_sb_spaces_rtrim(sb); - if (ty_sb_add(sb, "\n", 1) < 0) goto err; - } - else - { - for (x = last0; x <= end_x; x++) - { - if ((cells[x].codepoint == 0) && - (cells[x].att.dblwidth)) - { - if (x < (w - 1)) x++; - else break; - } - if (x >= w) break; - if (ty_sb_add(sb, " ", 1) < 0) goto err; - } - } - } - else - { - if (rtrim) - ty_sb_spaces_rtrim(sb); - if (ty_sb_add(sb, "\n", 1) < 0) goto err; - } - } - } - termpty_backlog_unlock(); - - if (rtrim) - ty_sb_spaces_rtrim(sb); - - return; - -err: - ty_sb_free(sb); -} - - -static void -_sel_set(Termio *sd, Eina_Bool enable) -{ - if (sd->pty->selection.is_active == enable) - return; - sd->pty->selection.is_active = enable; - if (enable) - evas_object_smart_callback_call(sd->win, "selection,on", NULL); - else - { - evas_object_smart_callback_call(sd->win, "selection,off", NULL); - sd->pty->selection.by_word = EINA_FALSE; - sd->pty->selection.by_line = EINA_FALSE; - free(sd->pty->selection.codepoints); - sd->pty->selection.codepoints = NULL; - } -} - -static void _lost_selection(void *data, Elm_Sel_Type selection); - -static void -_lost_selection_reset_job(void *data) -{ - Termio *sd = evas_object_smart_data_get(data); - EINA_SAFETY_ON_NULL_RETURN(sd); - - sd->sel_reset_job = NULL; - if (sd->sel_str) - { - elm_cnp_selection_set(sd->win, sd->sel_type, - ELM_SEL_FORMAT_TEXT, - sd->sel_str, strlen(sd->sel_str)); - elm_cnp_selection_loss_callback_set(sd->win, sd->sel_type, - _lost_selection, data); - } -} - -static void -_lost_selection(void *data, Elm_Sel_Type selection) -{ - Eina_List *l; - Evas_Object *obj; - double t = ecore_time_get(); - - EINA_LIST_FOREACH(terms, l, obj) - { - Termio *sd = evas_object_smart_data_get(obj); - if (!sd) continue; - if ((t - sd->set_sel_at) < 0.2) /// hack - { - if ((sd->have_sel) && (sd->sel_str) && (!sd->reset_sel)) - { - sd->reset_sel = EINA_TRUE; - if (sd->sel_reset_job) ecore_job_del(sd->sel_reset_job); - sd->sel_reset_job = ecore_job_add - (_lost_selection_reset_job, data); - } - continue; - } - if (sd->have_sel) - { - if (sd->sel_str) - { - eina_stringshare_del(sd->sel_str); - sd->sel_str = NULL; - } - _sel_set(sd, EINA_FALSE); - elm_object_cnp_selection_clear(sd->win, selection); - _smart_update_queue(sd); - sd->have_sel = EINA_FALSE; - } - } -} - -static void -_take_selection_text(Termio *sd, Elm_Sel_Type type, const char *text) -{ - EINA_SAFETY_ON_NULL_RETURN(sd); - - text = eina_stringshare_add(text); - - sd->have_sel = EINA_FALSE; - sd->reset_sel = EINA_FALSE; - sd->set_sel_at = ecore_time_get(); // hack - sd->sel_type = type; - - elm_cnp_selection_set(sd->win, type, - ELM_SEL_FORMAT_TEXT, - text, - eina_stringshare_strlen(text)); - elm_cnp_selection_loss_callback_set(sd->win, type, - _lost_selection, sd->self); - sd->have_sel = EINA_TRUE; - if (sd->sel_str) eina_stringshare_del(sd->sel_str); - sd->sel_str = text; -} - -struct Codepoints_Buf { - Eina_Unicode *codepoints; - size_t len; - size_t size; -}; - -static int -_codepoint_buf_append(struct Codepoints_Buf *buf, - Eina_Unicode u) -{ - if (EINA_UNLIKELY(buf->len == buf->size)) - { - buf->size *= 2; - Eina_Unicode *codepoints = realloc(buf->codepoints, - buf->size * sizeof(Eina_Unicode)); - if (EINA_UNLIKELY(!codepoints)) - { - free(buf->codepoints); - buf->len = buf->size = 0; - return -1; - } - buf->codepoints = codepoints; - } - buf->codepoints[buf->len++] = u; - return 0; -} - -static void -_sel_codepoints_get(const Termio *sd, - struct Codepoints_Buf *buf, - int c1x, int c1y, int c2x, int c2y) -{ - int x, y; - -#define TRY(ACTION) do { \ - if (EINA_UNLIKELY(ACTION < 0)) \ - { \ - goto err; \ - } \ -} while (0) - - termpty_backlog_lock(); - for (y = c1y; y <= c2y; y++) - { - Termcell *cells; - ssize_t w = 0; - int start_x, end_x; - - cells = termpty_cellrow_get(sd->pty, y, &w); - if (!cells || !w || (y == c1y && c1x >= w)) - { - w = 0; - } - - /* Compute @start_x, @end_x */ - start_x = c1x; - end_x = c2x; - if (c1y != c2y) - { - if (y == c1y) - { - end_x = sd->grid.w - 1; - } - else if (y == c2y) - { - start_x = 0; - } - else - { - start_x = 0; - end_x = sd->grid.w - 1; - } - } - /* Lookup every cell in that line */ - for (x = start_x; x <= end_x; x++) - { - if (x >= w) - { - /* Selection outside of current line of "text" */ - TRY(_codepoint_buf_append(buf, ' ')); - } - else if (cells[x].codepoint == 0) - { - TRY(_codepoint_buf_append(buf, ' ')); - } - else - { - TRY(_codepoint_buf_append(buf, cells[x].codepoint)); - } - } - } -err: - termpty_backlog_unlock(); -} - - -static void -_sel_fill_in_codepoints_array(Termio *sd) -{ - int start_x = 0, start_y = 0, end_x = 0, end_y = 0; - struct Codepoints_Buf buf = { - .codepoints = NULL, - .len = 0, - .size = 256, - }; - - free(sd->pty->selection.codepoints); - sd->pty->selection.codepoints = NULL; - - if (!sd->pty->selection.is_active) - return; - - buf.codepoints = malloc(sizeof(Eina_Unicode) * buf.size); - if (!buf.codepoints) - return; - - start_x = sd->pty->selection.start.x; - start_y = sd->pty->selection.start.y; - end_x = sd->pty->selection.end.x; - end_y = sd->pty->selection.end.y; - - if (!sd->pty->selection.is_top_to_bottom) - { - INT_SWAP(start_y, end_y); - INT_SWAP(start_x, end_x); - } - - if (sd->pty->selection.is_box) - { - int i; - - for (i = start_y; i <= end_y; i++) - { - _sel_codepoints_get(sd, &buf, start_x, i, end_x, i); - } - } - else - { - _sel_codepoints_get(sd, &buf, start_x, start_y, end_x, end_y); - } - sd->pty->selection.codepoints = buf.codepoints; -} - -Eina_Bool -termio_take_selection(Evas_Object *obj, Elm_Sel_Type type) -{ - Termio *sd = evas_object_smart_data_get(obj); - int start_x = 0, start_y = 0, end_x = 0, end_y = 0; - const char *s = NULL; - size_t len = 0; - - EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE); - if (sd->pty->selection.is_active) - { - start_x = sd->pty->selection.start.x; - start_y = sd->pty->selection.start.y; - end_x = sd->pty->selection.end.x; - end_y = sd->pty->selection.end.y; - - if (!sd->pty->selection.is_top_to_bottom) - { - INT_SWAP(start_y, end_y); - INT_SWAP(start_x, end_x); - } - } - else - { - if (sd->link.string) - { - len = strlen(sd->link.string); - s = eina_stringshare_add_length(sd->link.string, len); - } - goto end; - } - - if (sd->pty->selection.is_box) - { - int i; - struct ty_sb sb = {.buf = NULL, .len = 0, .alloc = 0}; - - for (i = start_y; i <= end_y; i++) - { - struct ty_sb isb = {.buf = NULL, .len = 0, .alloc = 0}; - _selection_get(obj, start_x, i, end_x, i, - &isb, EINA_TRUE); - - if (isb.len) - { - if (isb.buf[sb.len - 1] != '\n' && i != end_y) - ty_sb_add(&isb, "\n", 1); - ty_sb_add(&sb, isb.buf, isb.len); - } - ty_sb_free(&isb); - } - len = sb.len; - s = ty_sb_steal_buf(&sb); - s = eina_stringshare_add_length(s, len); - ty_sb_free(&sb); - } - else - { - struct ty_sb sb = {.buf = NULL, .len = 0, .alloc = 0}; - - _selection_get(obj, start_x, start_y, end_x, end_y, &sb, EINA_TRUE); - len = sb.len; - s = eina_stringshare_add_length(ty_sb_steal_buf(&sb), len); - ty_sb_free(&sb); - } - -end: - if (s) - { - if ((sd->win) && (len > 0)) - _take_selection_text(sd, type, s); - eina_stringshare_del(s); - return EINA_TRUE; - } - return EINA_FALSE; -} - -static Eina_Bool -_getsel_cb(void *data, - Evas_Object *_obj EINA_UNUSED, - Elm_Selection_Data *ev) -{ - Termio *sd = evas_object_smart_data_get(data); - - EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE); - - if (ev->format == ELM_SEL_FORMAT_TEXT) - { - char *tmp; - - if (ev->len <= 0) return EINA_TRUE; - - tmp = malloc(ev->len); - if (tmp) - { - char *s = ev->data; - size_t i; - - // apparently we have to convert \n into \r in terminal land. - for (i = 0; i < ev->len && s[i]; i++) - { - tmp[i] = s[i]; - if (tmp[i] == '\n') tmp[i] = '\r'; - } - if (i) - { - - if (sd->pty->bracketed_paste) - termpty_write(sd->pty, "\x1b[200~", - sizeof("\x1b[200~") - 1); - - termpty_write(sd->pty, tmp, i); - - if (sd->pty->bracketed_paste) - termpty_write(sd->pty, "\x1b[201~", - sizeof("\x1b[201~") - 1); - } - - free(tmp); - } - } - else - { - const char *fmt = "UNKNOWN"; - switch (ev->format) - { - case ELM_SEL_FORMAT_TARGETS: fmt = "TARGETS"; break; /* shouldn't happen */ - case ELM_SEL_FORMAT_NONE: fmt = "NONE"; break; - case ELM_SEL_FORMAT_TEXT: fmt = "TEXT"; break; - case ELM_SEL_FORMAT_MARKUP: fmt = "MARKUP"; break; - case ELM_SEL_FORMAT_IMAGE: fmt = "IMAGE"; break; - case ELM_SEL_FORMAT_VCARD: fmt = "VCARD"; break; - case ELM_SEL_FORMAT_HTML: fmt = "HTML"; break; - } - WRN(_("unsupported selection format '%s'"), fmt); - } - return EINA_TRUE; -} - -void -termio_paste_selection(Evas_Object *obj, Elm_Sel_Type type) -{ - Termio *sd = evas_object_smart_data_get(obj); - EINA_SAFETY_ON_NULL_RETURN(sd); - if (!sd->win) return; - elm_cnp_selection_get(sd->win, type, ELM_SEL_FORMAT_TEXT, - _getsel_cb, obj); -} - -static void -_sel_line(Termio *sd, int cy) -{ - int y; - ssize_t w = 0; - Termcell *cells; - - termpty_backlog_lock(); - - _sel_set(sd, EINA_TRUE); - sd->pty->selection.makesel = EINA_FALSE; - - sd->pty->selection.start.x = 0; - sd->pty->selection.start.y = cy; - sd->pty->selection.end.x = sd->grid.w - 1; - sd->pty->selection.end.y = cy; - - /* check lines above */ - y = cy; - for (;;) - { - cells = termpty_cellrow_get(sd->pty, y - 1, &w); - if (!cells || !cells[w-1].att.autowrapped) break; - - y--; - } - sd->pty->selection.start.y = y; - y = cy; - - /* check lines below */ - for (;;) - { - cells = termpty_cellrow_get(sd->pty, y, &w); - if (!cells || !cells[w-1].att.autowrapped) break; - - sd->pty->selection.end.x = w - 1; - y++; - } - sd->pty->selection.end.y = y; - - sd->pty->selection.by_line = EINA_TRUE; - sd->pty->selection.is_top_to_bottom = EINA_TRUE; - - termpty_backlog_unlock(); -} - -static void -_sel_line_to(Termio *sd, int cy, Eina_Bool extend) -{ - int start_y, end_y, c_start_y, c_end_y, - orig_y, orig_start_y, orig_end_y; - - EINA_SAFETY_ON_NULL_RETURN(sd); - - /* Only change the end position */ - orig_y = sd->pty->selection.orig.y; - start_y = sd->pty->selection.start.y; - end_y = sd->pty->selection.end.y; - - if (!sd->pty->selection.is_top_to_bottom) - INT_SWAP(start_y, end_y); - - _sel_line(sd, cy); - c_start_y = sd->pty->selection.start.y; - c_end_y = sd->pty->selection.end.y; - - _sel_line(sd, orig_y); - orig_start_y = sd->pty->selection.start.y; - orig_end_y = sd->pty->selection.end.y; - - if (sd->pty->selection.is_box) - { - if (extend) - { - if (start_y <= cy && cy <= end_y ) - { - start_y = MIN(c_start_y, orig_start_y); - end_y = MAX(c_end_y, orig_end_y); - } - else - { - if (c_end_y > end_y) - { - orig_y = start_y; - end_y = c_end_y; - } - if (c_start_y < start_y) - { - orig_y = end_y; - start_y = c_start_y; - } - } - goto end; - } - else - { - start_y = MIN(c_start_y, orig_start_y); - end_y = MAX(c_end_y, orig_end_y); - goto end; - } - } - else - { - if (c_start_y < start_y) - { - /* orig is at bottom */ - if (extend) - { - orig_y = end_y; - } - sd->pty->selection.is_top_to_bottom = EINA_FALSE; - end_y = c_start_y; - start_y = orig_end_y; - } - else if (c_end_y > end_y) - { - if (extend) - { - orig_y = start_y; - } - sd->pty->selection.is_top_to_bottom = EINA_TRUE; - start_y = orig_start_y; - end_y = c_end_y; - } - else - { - if (c_start_y < orig_start_y) - { - sd->pty->selection.is_top_to_bottom = EINA_FALSE; - start_y = orig_end_y; - end_y = c_start_y; - } - else - { - sd->pty->selection.is_top_to_bottom = EINA_TRUE; - start_y = orig_start_y; - end_y = c_end_y; - } - } - } -end: - sd->pty->selection.orig.y = orig_y; - sd->pty->selection.start.y = start_y; - sd->pty->selection.end.y = end_y; - if (sd->pty->selection.is_top_to_bottom) - { - sd->pty->selection.start.x = 0; - sd->pty->selection.end.x = sd->grid.w - 1; - } - else - { - sd->pty->selection.end.x = 0; - sd->pty->selection.start.x = sd->grid.w - 1; - } -} - -static Eina_Bool -_codepoint_is_wordsep(const Eina_Unicode g) -{ - /* TODO: use bitmaps to speed things up */ - // http://en.wikipedia.org/wiki/Asterisk - // http://en.wikipedia.org/wiki/Comma - // http://en.wikipedia.org/wiki/Interpunct - // http://en.wikipedia.org/wiki/Bracket - static const Eina_Unicode wordsep[] = - { - 0, - ' ', - '!', - '"', - '#', - '$', - '\'', - '(', - ')', - '*', - ',', - ';', - '=', - '?', - '[', - '\\', - ']', - '^', - '`', - '{', - '|', - '}', - 0x00a0, - 0x00ab, - 0x00b7, - 0x00bb, - 0x0294, - 0x02bb, - 0x02bd, - 0x02d0, - 0x0312, - 0x0313, - 0x0314, - 0x0315, - 0x0326, - 0x0387, - 0x055d, - 0x055e, - 0x060c, - 0x061f, - 0x066d, - 0x07fb, - 0x1363, - 0x1367, - 0x14fe, - 0x1680, - 0x1802, - 0x1808, - 0x180e, - 0x2000, - 0x2001, - 0x2002, - 0x2003, - 0x2004, - 0x2005, - 0x2006, - 0x2007, - 0x2008, - 0x2009, - 0x200a, - 0x200b, - 0x2018, - 0x2019, - 0x201a, - 0x201b, - 0x201c, - 0x201d, - 0x201e, - 0x201f, - 0x2022, - 0x2027, - 0x202f, - 0x2039, - 0x203a, - 0x203b, - 0x203d, - 0x2047, - 0x2048, - 0x2049, - 0x204e, - 0x205f, - 0x2217, - 0x225f, - 0x2308, - 0x2309, - 0x2420, - 0x2422, - 0x2423, - 0x2722, - 0x2723, - 0x2724, - 0x2725, - 0x2731, - 0x2732, - 0x2733, - 0x273a, - 0x273b, - 0x273c, - 0x273d, - 0x2743, - 0x2749, - 0x274a, - 0x274b, - 0x2a7b, - 0x2a7c, - 0x2cfa, - 0x2e2e, - 0x2e2e, - 0x3000, - 0x3001, - 0x3008, - 0x3009, - 0x300a, - 0x300b, - 0x300c, - 0x300c, - 0x300d, - 0x300d, - 0x300e, - 0x300f, - 0x3010, - 0x3011, - 0x301d, - 0x301e, - 0x301f, - 0x30fb, - 0xa60d, - 0xa60f, - 0xa6f5, - 0xe0a0, - 0xe0b0, - 0xe0b2, - 0xfe10, - 0xfe41, - 0xfe42, - 0xfe43, - 0xfe44, - 0xfe50, - 0xfe51, - 0xfe56, - 0xfe61, - 0xfe62, - 0xfe63, - 0xfeff, - 0xff02, - 0xff07, - 0xff08, - 0xff09, - 0xff0a, - 0xff0c, - 0xff1b, - 0xff1c, - 0xff1e, - 0xff1f, - 0xff3b, - 0xff3d, - 0xff5b, - 0xff5d, - 0xff62, - 0xff63, - 0xff64, - 0xff65, - 0xe002a - }; - size_t imax = (sizeof(wordsep) / sizeof(wordsep[0])) - 1, - imin = 0; - size_t imaxmax = imax; - - if (g & 0x80000000) - return EINA_TRUE; - - while (imax >= imin) - { - size_t imid = (imin + imax) / 2; - - if (wordsep[imid] == g) return EINA_TRUE; - else if (wordsep[imid] < g) imin = imid + 1; - else imax = imid - 1; - if (imax > imaxmax) break; - } - return EINA_FALSE; -} - -static Eina_Bool -_to_trim(Eina_Unicode codepoint, Eina_Bool right_trim) -{ - static const Eina_Unicode trim_chars[] = - { - ':', - '<', - '>', - '.' - }; - size_t i = 0, len; - len = sizeof(trim_chars)/sizeof((trim_chars)[0]); - if (right_trim) - len--; /* do not right trim . */ - - for (i = 0; i < len; i++) - if (codepoint == trim_chars[i]) - return EINA_TRUE; - return EINA_FALSE; -} - -static void -_trim_sel_word(Termio *sd) -{ - Termpty *pty = sd->pty; - Termcell *cells; - int start = 0, end = 0, y = 0; - ssize_t w; - - /* 1st step: trim from the start */ - start = pty->selection.start.x; - for (y = pty->selection.start.y; - y <= pty->selection.end.y; - y++) - { - cells = termpty_cellrow_get(pty, y, &w); - if (!cells) - return; - - while (start < w && _to_trim(cells[start].codepoint, EINA_TRUE)) - start++; - - if (start < w) - break; - - start = 0; - } - /* check validy of the selection */ - if ((y > pty->selection.end.y) || - ((y == pty->selection.end.y) && - (start > pty->selection.end.x))) - { - pty->selection.start.y = pty->selection.end.y; - pty->selection.start.x = pty->selection.end.x; - return; - } - pty->selection.start.y = y; - pty->selection.start.x = start; - - /* 2nd step: trim from the end */ - end = pty->selection.end.x; - for (y = pty->selection.end.y; - y >= pty->selection.start.y; - y--) - { - cells = termpty_cellrow_get(pty, y, &w); - if (!cells) - return; - - while (end >= 0 && _to_trim(cells[end].codepoint, EINA_FALSE)) - end--; - - if (end >= 0) - break; - } - if (end < 0) - { - return; - } - /* check validy of the selection */ - if ((y < pty->selection.start.y) || - ((y == pty->selection.start.y) && - (end < pty->selection.start.x))) - { - pty->selection.end.x = pty->selection.start.x; - pty->selection.end.y = pty->selection.start.y; - return; - } - pty->selection.end.x = end; - pty->selection.end.y = y; -} - -static void -_sel_word(Termio *sd, int cx, int cy) -{ - Termcell *cells; - int x, y; - ssize_t w = 0; - Eina_Bool done = EINA_FALSE; - - termpty_backlog_lock(); - - _sel_set(sd, EINA_TRUE); - sd->pty->selection.makesel = EINA_TRUE; - sd->pty->selection.orig.x = cx; - sd->pty->selection.orig.y = cy; - sd->pty->selection.start.x = cx; - sd->pty->selection.start.y = cy; - sd->pty->selection.end.x = cx; - sd->pty->selection.end.y = cy; - x = cx; - y = cy; - - if (sd->link.string && - (sd->link.x1 <= cx) && (cx <= sd->link.x2) && - (sd->link.y1 <= cy) && (cy <= sd->link.y2)) - { - sd->pty->selection.start.x = sd->link.x1; - sd->pty->selection.start.y = sd->link.y1; - sd->pty->selection.end.x = sd->link.x2; - sd->pty->selection.end.y = sd->link.y2; - goto end; - } - cells = termpty_cellrow_get(sd->pty, y, &w); - if (!cells) goto end; - if (x >= w) x = w - 1; - - do - { - for (; x >= 0; x--) - { - if ((cells[x].codepoint == 0) && (cells[x].att.dblwidth) && - (x > 0)) - x--; - if (_codepoint_is_wordsep(cells[x].codepoint)) - { - done = EINA_TRUE; - break; - } - sd->pty->selection.start.x = x; - sd->pty->selection.start.y = y; - } - if (!done) - { - Termcell *old_cells = cells; - - cells = termpty_cellrow_get(sd->pty, y - 1, &w); - if (!cells || !cells[w-1].att.autowrapped) - { - x = 0; - cells = old_cells; - done = EINA_TRUE; - } - else - { - y--; - x = w - 1; - } - } - } - while (!done); - - done = EINA_FALSE; - if (cy != y) - { - y = cy; - cells = termpty_cellrow_get(sd->pty, y, &w); - if (!cells) goto end; - } - x = cx; - - do - { - for (; x < w; x++) - { - if ((cells[x].codepoint == 0) && (cells[x].att.dblwidth) && - (x < (w - 1))) - { - sd->pty->selection.end.x = x; - x++; - } - if (_codepoint_is_wordsep(cells[x].codepoint)) - { - done = EINA_TRUE; - break; - } - sd->pty->selection.end.x = x; - sd->pty->selection.end.y = y; - } - if (!done) - { - if (!cells[w - 1].att.autowrapped) goto end; - y++; - x = 0; - cells = termpty_cellrow_get(sd->pty, y, &w); - if (!cells) goto end; - } - } - while (!done); - - end: - - sd->pty->selection.by_word = EINA_TRUE; - sd->pty->selection.is_top_to_bottom = EINA_TRUE; - _trim_sel_word(sd); - - termpty_backlog_unlock(); -} - -static void -_sel_word_to(Termio *sd, int cx, int cy, Eina_Bool extend) -{ - int start_x, start_y, end_x, end_y, orig_x, orig_y, - c_start_x, c_start_y, c_end_x, c_end_y, - orig_start_x, orig_start_y, orig_end_x, orig_end_y; - - EINA_SAFETY_ON_NULL_RETURN(sd); - - orig_x = sd->pty->selection.orig.x; - orig_y = sd->pty->selection.orig.y; - start_x = sd->pty->selection.start.x; - start_y = sd->pty->selection.start.y; - end_x = sd->pty->selection.end.x; - end_y = sd->pty->selection.end.y; - - if (!sd->pty->selection.is_top_to_bottom) - { - INT_SWAP(start_x, end_x); - INT_SWAP(start_y, end_y); - } - - _sel_word(sd, cx, cy); - c_start_x = sd->pty->selection.start.x; - c_start_y = sd->pty->selection.start.y; - c_end_x = sd->pty->selection.end.x; - c_end_y = sd->pty->selection.end.y; - - _sel_word(sd, orig_x, orig_y); - orig_start_x = sd->pty->selection.start.x; - orig_start_y = sd->pty->selection.start.y; - orig_end_x = sd->pty->selection.end.x; - orig_end_y = sd->pty->selection.end.y; - - if (sd->pty->selection.is_box) - { - if (extend) - { - /* special case: kind of line selection */ - if (c_start_y != c_end_y) - { - start_x = 0; - end_x = sd->grid.w - 1; - if (start_y <= cy && cy <= end_y ) - { - start_y = MIN(c_start_y, orig_start_y); - end_y = MAX(c_end_y, orig_end_y); - } - else - { - if (c_end_y > end_y) - { - orig_y = start_y; - end_y = c_end_y; - } - if (c_start_y < start_y) - { - orig_y = end_y; - start_y = c_start_y; - } - } - goto end; - } - if ((start_y <= cy && cy <= end_y ) && - (start_x <= cx && cx <= end_x )) - { - start_x = MIN(c_start_x, orig_start_x); - end_x = MAX(c_end_x, orig_end_x); - start_y = MIN(c_start_y, orig_start_y); - end_y = MAX(c_end_y, orig_end_y); - } - else - { - if (c_end_x > end_x) - { - orig_x = start_x; - end_x = c_end_x; - } - if (c_start_x < start_x) - { - orig_x = end_x; - start_x = c_start_x; - } - if (c_end_y > end_y) - { - orig_y = start_y; - end_y = c_end_y; - } - if (c_start_y < start_y) - { - orig_y = end_y; - start_y = c_start_y; - } - end_x = MAX(c_end_x, end_x); - start_y = MIN(c_start_y, start_y); - end_y = MAX(c_end_y, end_y); - } - } - else - { - /* special case: kind of line selection */ - if (c_start_y != c_end_y || orig_start_y != orig_end_y) - { - start_x = 0; - end_x = sd->grid.w - 1; - start_y = MIN(c_start_y, orig_start_y); - end_y = MAX(c_end_y, orig_end_y); - goto end; - } - - start_x = MIN(c_start_x, orig_start_x); - end_x = MAX(c_end_x, orig_end_x); - start_y = MIN(c_start_y, orig_start_y); - end_y = MAX(c_end_y, orig_end_y); - } - } - else - { - if (c_start_y < start_y || - (c_start_y == start_y && - c_start_x < start_x)) - { - /* orig is at bottom */ - if (extend) - { - orig_x = end_x; - orig_y = end_y; - } - sd->pty->selection.is_top_to_bottom = EINA_FALSE; - end_x = c_start_x; - end_y = c_start_y; - start_x = orig_end_x; - start_y = orig_end_y; - } - else if (c_end_y > end_y || - (c_end_y == end_y && c_end_x >= end_x)) - { - if (extend) - { - orig_x = start_x; - orig_y = start_y; - } - sd->pty->selection.is_top_to_bottom = EINA_TRUE; - start_x = orig_start_x; - start_y = orig_start_y; - end_x = c_end_x; - end_y = c_end_y; - } - else - { - if (c_start_y < orig_start_y || - (c_start_y == orig_start_y && c_start_x <= orig_start_x)) - { - sd->pty->selection.is_top_to_bottom = EINA_FALSE; - start_x = orig_end_x; - start_y = orig_end_y; - end_x = c_start_x; - end_y = c_start_y; - } - else - { - sd->pty->selection.is_top_to_bottom = EINA_TRUE; - start_x = orig_start_x; - start_y = orig_start_y; - end_x = c_end_x; - end_y = c_end_y; - } - } - } - -end: - sd->pty->selection.orig.x = orig_x; - sd->pty->selection.orig.y = orig_y; - sd->pty->selection.start.x = start_x; - sd->pty->selection.start.y = start_y; - sd->pty->selection.end.x = end_x; - sd->pty->selection.end.y = end_y; -} - -static void -_sel_to(Termio *sd, int cx, int cy, Eina_Bool extend) -{ - int start_x, start_y, end_x, end_y, orig_x, orig_y; - - EINA_SAFETY_ON_NULL_RETURN(sd); - - orig_x = sd->pty->selection.orig.x; - orig_y = sd->pty->selection.orig.y; - start_x = sd->pty->selection.start.x; - start_y = sd->pty->selection.start.y; - end_x = sd->pty->selection.end.x; - end_y = sd->pty->selection.end.y; - - if (sd->pty->selection.is_box) - { - if (!sd->pty->selection.is_top_to_bottom) - INT_SWAP(start_y, end_y); - if (start_x > end_x) - INT_SWAP(start_x, end_x); - - if (cy < start_y) - { - start_y = cy; - } - else if (cy > end_y) - { - end_y = cy; - } - else - { - start_y = orig_y; - end_y = cy; - } - - if (cx < start_x) - { - start_x = cx; - } - else if (cx > end_x) - { - end_x = cx; - } - else - { - start_x = orig_x; - end_x = cx; - } - sd->pty->selection.is_top_to_bottom = (end_y > start_y); - if (sd->pty->selection.is_top_to_bottom) - { - if (start_x > end_x) - INT_SWAP(start_x, end_x); - } - else - { - if (start_x < end_x) - INT_SWAP(start_x, end_x); - } - } - else - { - if (!sd->pty->selection.is_top_to_bottom) - { - INT_SWAP(start_x, end_x); - INT_SWAP(start_y, end_y); - } - if (cy < start_y || - (cy == start_y && - cx < start_x)) - { - /* orig is at bottom */ - if (sd->pty->selection.is_top_to_bottom && extend) - { - orig_x = end_x; - orig_y = end_y; - } - sd->pty->selection.is_top_to_bottom = EINA_FALSE; - } - else if (cy > end_y || - (cy == end_y && cx >= end_x)) - { - if (!sd->pty->selection.is_top_to_bottom && extend) - { - orig_x = start_x; - orig_y = start_y; - } - sd->pty->selection.is_top_to_bottom = EINA_TRUE; - } - else - { - sd->pty->selection.is_top_to_bottom = - (cy > orig_y) || (cy == orig_y && cx > orig_x); - } - start_x = orig_x; - start_y = orig_y; - end_x = cx; - end_y = cy; - } - - sd->pty->selection.orig.x = orig_x; - sd->pty->selection.orig.y = orig_y; - sd->pty->selection.start.x = start_x; - sd->pty->selection.start.y = start_y; - sd->pty->selection.end.x = end_x; - sd->pty->selection.end.y = end_y; -} - -static void -_selection_dbl_fix(Termio *sd) -{ - int start_x, start_y, end_x, end_y; - ssize_t w = 0; - Termcell *cells; - /* Only change the end position */ - - EINA_SAFETY_ON_NULL_RETURN(sd); - - start_x = sd->pty->selection.start.x; - start_y = sd->pty->selection.start.y; - end_x = sd->pty->selection.end.x; - end_y = sd->pty->selection.end.y; - if (!sd->pty->selection.is_top_to_bottom) - { - INT_SWAP(start_y, end_y); - INT_SWAP(start_x, end_x); - } - - termpty_backlog_lock(); - cells = termpty_cellrow_get(sd->pty, end_y - sd->scroll, &w); - if (cells) - { - // if sel2 after sel1 - if ((end_y > start_y) || - ((end_y == start_y) && - (end_x >= start_x))) - { - if (end_x < (w - 1)) - { - if ((cells[end_x].codepoint != 0) && - (cells[end_x].att.dblwidth)) - end_x++; - } - } - // else sel1 after sel 2 - else - { - if (end_x > 0) - { - if ((cells[end_x].codepoint == 0) && - (cells[end_x].att.dblwidth)) - end_x--; - } - } - } - cells = termpty_cellrow_get(sd->pty, start_y - sd->scroll, &w); - if (cells) - { - // if sel2 after sel1 - if ((end_y > start_y) || - ((end_y == start_y) && - (end_x >= start_x))) - { - if (start_x > 0) - { - if ((cells[start_x].codepoint == 0) && - (cells[start_x].att.dblwidth)) - start_x--; - } - } - // else sel1 after sel 2 - else - { - if (start_x < (w - 1)) - { - if ((cells[start_x].codepoint != 0) && - (cells[start_x].att.dblwidth)) - start_x++; - } - } - } - termpty_backlog_unlock(); - - if (!sd->pty->selection.is_top_to_bottom) - { - INT_SWAP(start_y, end_y); - INT_SWAP(start_x, end_x); - } - sd->pty->selection.start.x = start_x; - sd->pty->selection.start.y = start_y; - sd->pty->selection.end.x = end_x; - sd->pty->selection.end.y = end_y; -} - -static void -_selection_newline_extend_fix(Evas_Object *obj) -{ - int start_x, start_y, end_x, end_y; - Termio *sd; - ssize_t w; - - sd = evas_object_smart_data_get(obj); - - if ((sd->top_left) || (sd->bottom_right) || (sd->pty->selection.is_box)) - return; - - termpty_backlog_lock(); - - start_x = sd->pty->selection.start.x; - start_y = sd->pty->selection.start.y; - end_x = sd->pty->selection.end.x; - end_y = sd->pty->selection.end.y; - if (!sd->pty->selection.is_top_to_bottom) - { - INT_SWAP(start_y, end_y); - INT_SWAP(start_x, end_x); - } - - if ((end_y > start_y) || - ((end_y == start_y) && - (end_x >= start_x))) - { - /* going down/right */ - w = termpty_row_length(sd->pty, start_y); - if (w < start_x) - start_x = w; - w = termpty_row_length(sd->pty, end_y); - if (w <= end_x) - end_x = sd->pty->w; - } - else - { - /* going up/left */ - w = termpty_row_length(sd->pty, end_y); - if (w < end_x) - end_x = w; - w = termpty_row_length(sd->pty, start_y); - if (w <= start_x) - start_x = sd->pty->w; - } - - if (!sd->pty->selection.is_top_to_bottom) - { - INT_SWAP(start_y, end_y); - INT_SWAP(start_x, end_x); - } - sd->pty->selection.start.x = start_x; - sd->pty->selection.start.y = start_y; - sd->pty->selection.end.x = end_x; - sd->pty->selection.end.y = end_y; - - termpty_backlog_unlock(); -} - /* }}} */ /* {{{ Mouse */ @@ -3942,300 +2350,6 @@ _smart_mouseover_apply(Evas_Object *obj) _update_link(sd, same_geom); } -static void -_smart_xy_to_cursor(Termio *sd, Evas_Coord x, Evas_Coord y, - int *cx, int *cy) -{ - Evas_Coord ox, oy; - EINA_SAFETY_ON_NULL_RETURN(sd); - - evas_object_geometry_get(sd->self, &ox, &oy, NULL, NULL); - *cx = (x - ox) / sd->font.chw; - *cy = (y - oy) / sd->font.chh; - if (*cx < 0) *cx = 0; - else if (*cx >= sd->grid.w) *cx = sd->grid.w - 1; - if (*cy < 0) *cy = 0; - else if (*cy >= sd->grid.h) *cy = sd->grid.h - 1; -} - -static Eina_Bool -_rep_mouse_down(Termio *sd, Evas_Event_Mouse_Down *ev, int cx, int cy) -{ - char buf[64]; - Eina_Bool ret = EINA_FALSE; - int btn; - - if (sd->pty->mouse_mode == MOUSE_OFF) return EINA_FALSE; - if (!sd->mouse.button) - { - /* Need to remember the first button pressed for terminal handling */ - sd->mouse.button = ev->button; - } - - btn = ev->button - 1; - switch (sd->pty->mouse_ext) - { - case MOUSE_EXT_NONE: - if ((cx < (0xff - ' ')) && (cy < (0xff - ' '))) - { - if (sd->pty->mouse_mode == MOUSE_X10) - { - if (btn <= 2) - { - buf[0] = 0x1b; - buf[1] = '['; - buf[2] = 'M'; - buf[3] = btn + ' '; - buf[4] = cx + 1 + ' '; - buf[5] = cy + 1 + ' '; - buf[6] = 0; - termpty_write(sd->pty, buf, strlen(buf)); - ret = EINA_TRUE; - } - } - else - { - int meta = evas_key_modifier_is_set(ev->modifiers, "Alt") ? 8 : 0; - - if (btn > 2) btn = 0; - buf[0] = 0x1b; - buf[1] = '['; - buf[2] = 'M'; - buf[3] = (btn | meta) + ' '; - buf[4] = cx + 1 + ' '; - buf[5] = cy + 1 + ' '; - buf[6] = 0; - termpty_write(sd->pty, buf, strlen(buf)); - ret = EINA_TRUE; - } - } - break; - case MOUSE_EXT_UTF8: // ESC.[.M.BTN/FLGS.XUTF8.YUTF8 - { - int meta = evas_key_modifier_is_set(ev->modifiers, "Alt") ? 8 : 0; - int v, i; - - if (btn > 2) btn = 0; - buf[0] = 0x1b; - buf[1] = '['; - buf[2] = 'M'; - buf[3] = (btn | meta) + ' '; - i = 4; - v = cx + 1 + ' '; - if (v <= 127) buf[i++] = v; - else - { // 14 bits for cx/cy - enough i think - buf[i++] = 0xc0 + (v >> 6); - buf[i++] = 0x80 + (v & 0x3f); - } - v = cy + 1 + ' '; - if (v <= 127) buf[i++] = v; - else - { // 14 bits for cx/cy - enough i think - buf[i++] = 0xc0 + (v >> 6); - buf[i++] = 0x80 + (v & 0x3f); - } - buf[i] = 0; - termpty_write(sd->pty, buf, strlen(buf)); - ret = EINA_TRUE; - } - break; - case MOUSE_EXT_SGR: // ESC.[.<.NUM.;.NUM.;.NUM.M - { - int meta = evas_key_modifier_is_set(ev->modifiers, "Alt") ? 8 : 0; - - if (btn > 2) btn = 0; - snprintf(buf, sizeof(buf), "%c[<%i;%i;%iM", 0x1b, - (btn | meta), cx + 1, cy + 1); - termpty_write(sd->pty, buf, strlen(buf)); - ret = EINA_TRUE; - } - break; - case MOUSE_EXT_URXVT: // ESC.[.NUM.;.NUM.;.NUM.M - { - int meta = evas_key_modifier_is_set(ev->modifiers, "Alt") ? 8 : 0; - - if (btn > 2) btn = 0; - snprintf(buf, sizeof(buf), "%c[%i;%i;%iM", 0x1b, - (btn | meta) + ' ', - cx + 1, cy + 1); - termpty_write(sd->pty, buf, strlen(buf)); - ret = EINA_TRUE; - } - break; - default: - break; - } - return ret; -} - -static Eina_Bool -_rep_mouse_up(Termio *sd, Evas_Event_Mouse_Up *ev, int cx, int cy) -{ - char buf[64]; - Eina_Bool ret = EINA_FALSE; - int meta; - - if ((sd->pty->mouse_mode == MOUSE_OFF) || - (sd->pty->mouse_mode == MOUSE_X10)) - return EINA_FALSE; - if (sd->mouse.button == ev->button) - sd->mouse.button = 0; - - meta = evas_key_modifier_is_set(ev->modifiers, "Alt") ? 8 : 0; - - switch (sd->pty->mouse_ext) - { - case MOUSE_EXT_NONE: - if ((cx < (0xff - ' ')) && (cy < (0xff - ' '))) - { - buf[0] = 0x1b; - buf[1] = '['; - buf[2] = 'M'; - buf[3] = (3 | meta) + ' '; - buf[4] = cx + 1 + ' '; - buf[5] = cy + 1 + ' '; - buf[6] = 0; - termpty_write(sd->pty, buf, strlen(buf)); - ret = EINA_TRUE; - } - break; - case MOUSE_EXT_UTF8: // ESC.[.M.BTN/FLGS.XUTF8.YUTF8 - { - int v, i; - - buf[0] = 0x1b; - buf[1] = '['; - buf[2] = 'M'; - buf[3] = (3 | meta) + ' '; - i = 4; - v = cx + 1 + ' '; - if (v <= 127) buf[i++] = v; - else - { // 14 bits for cx/cy - enough i think - buf[i++] = 0xc0 + (v >> 6); - buf[i++] = 0x80 + (v & 0x3f); - } - v = cy + 1 + ' '; - if (v <= 127) buf[i++] = v; - else - { // 14 bits for cx/cy - enough i think - buf[i++] = 0xc0 + (v >> 6); - buf[i++] = 0x80 + (v & 0x3f); - } - buf[i] = 0; - termpty_write(sd->pty, buf, strlen(buf)); - ret = EINA_TRUE; - } - break; - case MOUSE_EXT_SGR: // ESC.[.<.NUM.;.NUM.;.NUM.m - { - int btn = ev->button - 1; - if (btn > 2) btn = 0; - snprintf(buf, sizeof(buf), "%c[<%i;%i;%im", 0x1b, - (btn | meta), cx + 1, cy + 1); - termpty_write(sd->pty, buf, strlen(buf)); - ret = EINA_TRUE; - } - break; - case MOUSE_EXT_URXVT: // ESC.[.NUM.;.NUM.;.NUM.M - { - snprintf(buf, sizeof(buf), "%c[%i;%i;%iM", 0x1b, - (3 | meta) + ' ', - cx + 1, cy + 1); - termpty_write(sd->pty, buf, strlen(buf)); - ret = EINA_TRUE; - } - break; - default: - break; - } - return ret; -} - -static Eina_Bool -_rep_mouse_move(Termio *sd, int cx, int cy) -{ - char buf[64]; - Eina_Bool ret = EINA_FALSE; - int btn; - - if ((sd->pty->mouse_mode == MOUSE_OFF) || - (sd->pty->mouse_mode == MOUSE_X10) || - (sd->pty->mouse_mode == MOUSE_NORMAL)) - return EINA_FALSE; - - if ((!sd->mouse.button) && (sd->pty->mouse_mode == MOUSE_NORMAL_BTN_MOVE)) - return EINA_FALSE; - - btn = sd->mouse.button - 1; - - switch (sd->pty->mouse_ext) - { - case MOUSE_EXT_NONE: - if ((cx < (0xff - ' ')) && (cy < (0xff - ' '))) - { - buf[0] = 0x1b; - buf[1] = '['; - buf[2] = 'M'; - buf[3] = btn + 32 + ' '; - buf[4] = cx + 1 + ' '; - buf[5] = cy + 1 + ' '; - buf[6] = 0; - termpty_write(sd->pty, buf, strlen(buf)); - ret = EINA_TRUE; - } - break; - case MOUSE_EXT_UTF8: // ESC.[.M.BTN/FLGS.XUTF8.YUTF8 - { - int v, i; - - buf[0] = 0x1b; - buf[1] = '['; - buf[2] = 'M'; - buf[3] = btn + 32 + ' '; - i = 4; - v = cx + 1 + ' '; - if (v <= 127) buf[i++] = v; - else - { // 14 bits for cx/cy - enough i think - buf[i++] = 0xc0 + (v >> 6); - buf[i++] = 0x80 + (v & 0x3f); - } - v = cy + 1 + ' '; - if (v <= 127) buf[i++] = v; - else - { // 14 bits for cx/cy - enough i think - buf[i++] = 0xc0 + (v >> 6); - buf[i++] = 0x80 + (v & 0x3f); - } - buf[i] = 0; - termpty_write(sd->pty, buf, strlen(buf)); - ret = EINA_TRUE; - } - break; - case MOUSE_EXT_SGR: // ESC.[.<.NUM.;.NUM.;.NUM.M - { - snprintf(buf, sizeof(buf), "%c[<%i;%i;%iM", 0x1b, - btn + 32, cx + 1, cy + 1); - termpty_write(sd->pty, buf, strlen(buf)); - ret = EINA_TRUE; - } - break; - case MOUSE_EXT_URXVT: // ESC.[.NUM.;.NUM.;.NUM.M - { - snprintf(buf, sizeof(buf), "%c[%i;%i;%iM", 0x1b, - btn + 32 + ' ', - cx + 1, cy + 1); - termpty_write(sd->pty, buf, strlen(buf)); - ret = EINA_TRUE; - } - break; - default: - break; - } - return ret; -} static Eina_Bool _smart_mouseover_delay(void *data) @@ -4249,10 +2363,10 @@ _smart_mouseover_delay(void *data) } -static void -_smart_cb_mouse_move_job(void *data) +void +termio_smart_cb_mouse_move_job(void *data) { - Termio *sd = evas_object_smart_data_get(data); + Termio *sd = data; EINA_SAFETY_ON_NULL_RETURN(sd); sd->mouse_move_job = NULL; @@ -4262,6 +2376,7 @@ _smart_cb_mouse_move_job(void *data) sd->mouseover_delay = ecore_timer_add(0.05, _smart_mouseover_delay, data); } + static void _edje_cb_bottom_right_in(void *data, Evas_Object *_obj EINA_UNUSED, @@ -4306,109 +2421,6 @@ _edje_cb_top_left_out(void *data, sd->top_left = EINA_FALSE; } -static void -_handle_mouse_down_single_click(Termio *sd, - int cx, int cy, - int ctrl, int alt, int shift) -{ - sd->didclick = EINA_FALSE; - /* SINGLE CLICK */ - if (sd->pty->selection.is_active && - (sd->top_left || sd->bottom_right)) - { - /* stretch selection */ - int start_x, start_y, end_x, end_y; - - start_x = sd->pty->selection.start.x; - start_y = sd->pty->selection.start.y; - end_x = sd->pty->selection.end.x; - end_y = sd->pty->selection.end.y; - - if (!sd->pty->selection.is_top_to_bottom) - { - INT_SWAP(start_y, end_y); - INT_SWAP(start_x, end_x); - } - - cy -= sd->scroll; - - sd->pty->selection.makesel = EINA_TRUE; - - if (sd->pty->selection.is_box) - { - if (end_x < start_x) - INT_SWAP(end_x, start_x); - } - if (sd->top_left) - { - sd->pty->selection.orig.x = end_x; - sd->pty->selection.orig.y = end_y; - sd->pty->selection.is_top_to_bottom = EINA_FALSE; - } - else - { - /* sd->bottom_right */ - sd->pty->selection.orig.x = start_x; - sd->pty->selection.orig.y = start_y; - sd->pty->selection.is_top_to_bottom = EINA_TRUE; - } - - sd->pty->selection.start.x = sd->pty->selection.orig.x; - sd->pty->selection.start.y = sd->pty->selection.orig.y; - sd->pty->selection.end.x = cx; - sd->pty->selection.end.y = cy; - _selection_dbl_fix(sd); - } - else if (!shift && alt && !sd->pty->selection.is_active - && (sd->pty->mouse_mode == MOUSE_OFF)) - { - /* move cursor to position */ - termpty_move_cursor(sd->pty, cx, cy); - } - else if (!shift && !sd->pty->selection.is_active) - { - /* New selection */ - sd->moved = EINA_FALSE; - _sel_set(sd, EINA_FALSE); - sd->pty->selection.is_box = (ctrl || alt); - sd->pty->selection.start.x = cx; - sd->pty->selection.start.y = cy - sd->scroll; - sd->pty->selection.orig.x = sd->pty->selection.start.x; - sd->pty->selection.orig.y = sd->pty->selection.start.y; - sd->pty->selection.end.x = cx; - sd->pty->selection.end.y = cy - sd->scroll; - sd->pty->selection.makesel = EINA_TRUE; - sd->pty->selection.by_line = EINA_FALSE; - sd->pty->selection.by_word = EINA_FALSE; - _selection_dbl_fix(sd); - } - else if (shift && sd->pty->selection.is_active) - { - /* let cb_up handle it */ - /* do nothing */ - return; - } - else if (shift && - (time(NULL) - sd->pty->selection.last_click) <= 5) - { - sd->pty->selection.is_box = ctrl; - _sel_to(sd, cx, cy - sd->scroll, EINA_FALSE); - sd->pty->selection.is_active = EINA_TRUE; - _selection_dbl_fix(sd); - } - else - { - sd->pty->selection.is_box = ctrl; - sd->pty->selection.start.x = sd->pty->selection.end.x = cx; - sd->pty->selection.orig.x = cx; - sd->pty->selection.start.y = sd->pty->selection.end.y = cy - sd->scroll; - sd->pty->selection.orig.y = cy - sd->scroll; - sd->pty->selection.makesel = EINA_TRUE; - sd->didclick = !sd->pty->selection.is_active; - sd->pty->selection.is_active = EINA_FALSE; - _sel_set(sd, EINA_FALSE); - } -} static void _cb_ctxp_sel_copy(void *data, @@ -4485,9 +2497,9 @@ end: } -static void -_handle_right_click(Evas_Object *obj, Evas_Event_Mouse_Down *ev, Termio *sd, - int cx, int cy) +void +termio_handle_right_click(Evas_Event_Mouse_Down *ev, Termio *sd, + int cx, int cy) { if (_mouse_in_selection(sd, cx, cy)) { @@ -4508,7 +2520,7 @@ _handle_right_click(Evas_Object *obj, Evas_Event_Mouse_Down *ev, Termio *sd, return; } if (!sd->link.string) - evas_object_smart_callback_call(obj, "options", NULL); + evas_object_smart_callback_call(sd->self, "options", NULL); } static void @@ -4519,77 +2531,10 @@ _smart_cb_mouse_down(void *data, { Evas_Event_Mouse_Down *ev = event; Termio *sd = evas_object_smart_data_get(data); - int cx = 0, cy = 0; - int shift, ctrl, alt; EINA_SAFETY_ON_NULL_RETURN(sd); - shift = evas_key_modifier_is_set(ev->modifiers, "Shift"); - ctrl = evas_key_modifier_is_set(ev->modifiers, "Control"); - alt = evas_key_modifier_is_set(ev->modifiers, "Alt"); - _smart_xy_to_cursor(sd, ev->canvas.x, ev->canvas.y, &cx, &cy); - - if ((ev->button == 3) && ctrl) - { - _handle_right_click(data, ev, sd, cx, cy); - return; - } - if (!shift && !ctrl) - if (_rep_mouse_down(sd, ev, cx, cy)) - { - if (sd->pty->selection.is_active) - { - _sel_set(sd, EINA_FALSE); - _smart_update_queue(sd); - } - return; - } - if (ev->button == 1) - { - sd->pty->selection.makesel = EINA_TRUE; - if (ev->flags & EVAS_BUTTON_TRIPLE_CLICK) - { - if (shift && sd->pty->selection.is_active) - _sel_line_to(sd, cy - sd->scroll, EINA_TRUE); - else - _sel_line(sd, cy - sd->scroll); - if (sd->pty->selection.is_active) - { - termio_take_selection(data, ELM_SEL_TYPE_PRIMARY); - } - sd->didclick = EINA_TRUE; - _sel_fill_in_codepoints_array(sd); - } - else if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK) - { - if (!sd->pty->selection.is_active && sd->didclick) - sd->pty->selection.is_active = EINA_TRUE; - if (shift && sd->pty->selection.is_active) - _sel_word_to(sd, cx, cy - sd->scroll, EINA_TRUE); - else - _sel_word(sd, cx, cy - sd->scroll); - if (sd->pty->selection.is_active) - { - if (!termio_take_selection(data, ELM_SEL_TYPE_PRIMARY)) - _sel_set(sd, EINA_FALSE); - } - sd->didclick = EINA_TRUE; - _sel_fill_in_codepoints_array(sd); - } - else - { - _handle_mouse_down_single_click(sd, cx, cy, ctrl, alt, shift); - } - _smart_update_queue(sd); - } - else if (ev->button == 2) - { - termio_paste_selection(data, ELM_SEL_TYPE_PRIMARY); - } - else if (ev->button == 3) - { - _handle_right_click(data, ev, sd, cx, cy); - } + termio_internal_mouse_down(sd, ev); } static void @@ -4600,118 +2545,10 @@ _smart_cb_mouse_up(void *data, { Evas_Event_Mouse_Up *ev = event; Termio *sd = evas_object_smart_data_get(data); - int cx = 0, cy = 0; - int shift, ctrl; EINA_SAFETY_ON_NULL_RETURN(sd); - shift = evas_key_modifier_is_set(ev->modifiers, "Shift"); - ctrl = evas_key_modifier_is_set(ev->modifiers, "Control"); - - _smart_xy_to_cursor(sd, ev->canvas.x, ev->canvas.y, &cx, &cy); - if (!shift && !ctrl && !sd->pty->selection.makesel) - if (_rep_mouse_up(sd, ev, cx, cy)) - { - if (sd->pty->selection.is_active) - { - _sel_set(sd, EINA_FALSE); - _smart_update_queue(sd); - } - return; - } - if (sd->link.down.dnd) return; - if (sd->pty->selection.makesel) - { - if (sd->mouse_selection_scroll_timer) - { - ecore_timer_del(sd->mouse_selection_scroll_timer); - sd->mouse_selection_scroll_timer = NULL; - } - sd->pty->selection.makesel = EINA_FALSE; - - if (!sd->pty->selection.is_active) - { - /* Only change the end position */ - if (((sd->pty->selection.start.x == sd->pty->selection.end.x) && - (sd->pty->selection.start.y == sd->pty->selection.end.y))) - { - _sel_set(sd, EINA_FALSE); - sd->didclick = EINA_FALSE; - sd->pty->selection.last_click = time(NULL); - sd->pty->selection.by_line = EINA_FALSE; - sd->pty->selection.by_word = EINA_FALSE; - _sel_fill_in_codepoints_array(sd); - _smart_update_queue(sd); - return; - } - } - else - { - if (sd->pty->selection.by_line) - { - _sel_line_to(sd, cy - sd->scroll, shift); - } - else if (sd->pty->selection.by_word) - { - _sel_word_to(sd, cx, cy - sd->scroll, shift); - } - else - { - if (shift) - { - /* extend selection */ - _sel_to(sd, cx, cy - sd->scroll, EINA_TRUE); - } - else - { - sd->didclick = EINA_TRUE; - _sel_to(sd, cx, cy - sd->scroll, EINA_FALSE); - } - } - _selection_dbl_fix(sd); - _selection_newline_extend_fix(data); - _smart_update_queue(sd); - termio_take_selection(data, ELM_SEL_TYPE_PRIMARY); - _sel_fill_in_codepoints_array(sd); - sd->pty->selection.makesel = EINA_FALSE; - } - } -} - -static Eina_Bool -_mouse_selection_scroll(void *data) -{ - Evas_Object *obj = data; - Termio *sd = evas_object_smart_data_get(obj); - Evas_Coord oy, my; - int cy; - float fcy; - - if (!sd->pty->selection.makesel) return EINA_FALSE; - - evas_pointer_canvas_xy_get(evas_object_evas_get(obj), NULL, &my); - evas_object_geometry_get(data, NULL, &oy, NULL, NULL); - fcy = (my - oy) / (float)sd->font.chh; - cy = fcy; - if (fcy < 0.3) - { - if (cy == 0) - cy = -1; - sd->scroll -= cy; - sd->pty->selection.end.y = -sd->scroll; - _smart_update_queue(sd); - } - else if (fcy >= (sd->grid.h - 0.3)) - { - if (cy <= sd->grid.h) - cy = sd->grid.h + 1; - sd->scroll -= cy - sd->grid.h; - if (sd->scroll < 0) sd->scroll = 0; - sd->pty->selection.end.y = sd->scroll + sd->grid.h - 1; - _smart_update_queue(sd); - } - - return EINA_TRUE; + termio_internal_mouse_up(sd, ev); } static void @@ -4722,100 +2559,9 @@ _smart_cb_mouse_move(void *data, { Evas_Event_Mouse_Move *ev = event; Termio *sd = evas_object_smart_data_get(data); - int cx, cy; - float fcy; - Evas_Coord ox, oy; - Eina_Bool scroll = EINA_FALSE; - int shift, ctrl; - - shift = evas_key_modifier_is_set(ev->modifiers, "Shift"); - ctrl = evas_key_modifier_is_set(ev->modifiers, "Control"); - EINA_SAFETY_ON_NULL_RETURN(sd); - evas_object_geometry_get(data, &ox, &oy, NULL, NULL); - cx = (ev->cur.canvas.x - ox) / sd->font.chw; - fcy = (ev->cur.canvas.y - oy) / (float)sd->font.chh; - cy = fcy; - if (cx < 0) cx = 0; - else if (cx >= sd->grid.w) cx = sd->grid.w - 1; - if (fcy < 0.3) - { - cy = 0; - if (sd->pty->selection.makesel) - scroll = EINA_TRUE; - } - else if (fcy >= (sd->grid.h - 0.3)) - { - cy = sd->grid.h - 1; - if (sd->pty->selection.makesel) - scroll = EINA_TRUE; - } - if (scroll == EINA_TRUE) - { - if (!sd->mouse_selection_scroll_timer) - { - sd->mouse_selection_scroll_timer - = ecore_timer_add(0.05, _mouse_selection_scroll, data); - } - return; - } - else if (sd->mouse_selection_scroll_timer) - { - ecore_timer_del(sd->mouse_selection_scroll_timer); - sd->mouse_selection_scroll_timer = NULL; - } - - if ((sd->mouse.cx == cx) && (sd->mouse.cy == cy)) return; - - sd->mouse.cx = cx; - sd->mouse.cy = cy; - if (!shift && !ctrl) - if (_rep_mouse_move(sd, cx, cy)) return; - if (sd->link.down.dnd) - { - sd->pty->selection.makesel = EINA_FALSE; - _sel_set(sd, EINA_FALSE); - _smart_update_queue(sd); - return; - } - if (sd->pty->selection.makesel) - { - int start_x, start_y; - - /* Only change the end position */ - start_x = sd->pty->selection.start.x; - start_y = sd->pty->selection.start.y; - - if (!sd->pty->selection.is_active) - { - if ((cx != start_x) || - ((cy - sd->scroll) != start_y)) - _sel_set(sd, EINA_TRUE); - } - - if (sd->pty->selection.by_line) - { - _sel_line_to(sd, cy - sd->scroll, EINA_FALSE); - } - else if (sd->pty->selection.by_word) - { - _sel_word_to(sd, cx, cy - sd->scroll, EINA_FALSE); - } - else - { - _sel_to(sd, cx, cy - sd->scroll, EINA_FALSE); - } - - _selection_dbl_fix(sd); - if (!sd->pty->selection.is_box) - _selection_newline_extend_fix(data); - _smart_update_queue(sd); - sd->moved = EINA_TRUE; - } - /* TODO: make the following useless */ - if (sd->mouse_move_job) ecore_job_del(sd->mouse_move_job); - sd->mouse_move_job = ecore_job_add(_smart_cb_mouse_move_job, data); + termio_internal_mouse_move(sd, ev); } static void @@ -4829,7 +2575,7 @@ _smart_cb_mouse_in(void *data, Termio *sd = evas_object_smart_data_get(data); EINA_SAFETY_ON_NULL_RETURN(sd); - _smart_xy_to_cursor(sd, ev->canvas.x, ev->canvas.y, &cx, &cy); + termio_cursor_to_xy(sd, ev->canvas.x, ev->canvas.y, &cx, &cy); sd->mouse.cx = cx; sd->mouse.cy = cy; termio_mouseover_suspend_pushpop(data, -1); @@ -4859,7 +2605,7 @@ _smart_cb_mouse_out(void *data, { int cx = 0, cy = 0; - _smart_xy_to_cursor(sd, ev->canvas.x, ev->canvas.y, &cx, &cy); + termio_cursor_to_xy(sd, ev->canvas.x, ev->canvas.y, &cx, &cy); sd->mouse.cx = cx; sd->mouse.cy = cy; } @@ -4904,18 +2650,18 @@ _smart_cb_mouse_wheel(void *data, sd->scroll -= (ev->z * 4); if (sd->scroll < 0) sd->scroll = 0; - _smart_update_queue(sd); + termio_smart_update_queue(sd); miniview_position_offset(term_miniview_get(sd->term), ev->z * 4, EINA_TRUE); - _smart_cb_mouse_move_job(data); + termio_smart_cb_mouse_move_job(sd); } } else { int cx = 0, cy = 0; - _smart_xy_to_cursor(sd, ev->canvas.x, ev->canvas.y, &cx, &cy); + termio_cursor_to_xy(sd, ev->canvas.x, ev->canvas.y, &cx, &cy); switch (sd->pty->mouse_ext) { @@ -5262,7 +3008,7 @@ _smart_apply(Evas_Object *obj) if (u && *u != ' ') { - _sel_set(sd, EINA_FALSE); + termio_sel_set(sd, EINA_FALSE); u = cp = NULL; } } @@ -5301,7 +3047,7 @@ _smart_apply(Evas_Object *obj) } if (u && *u != ' ') { - _sel_set(sd, EINA_FALSE); + termio_sel_set(sd, EINA_FALSE); u = cp = NULL; } } @@ -5328,7 +3074,7 @@ _smart_apply(Evas_Object *obj) ch2 = x; if (u && *u != ' ') { - _sel_set(sd, EINA_FALSE); + termio_sel_set(sd, EINA_FALSE); u = cp = NULL; } } @@ -5416,7 +3162,7 @@ _smart_apply(Evas_Object *obj) // cells[x].att.blink2 if (u && *u != codepoint) { - _sel_set(sd, EINA_FALSE); + termio_sel_set(sd, EINA_FALSE); u = cp = NULL; } } @@ -5637,7 +3383,7 @@ _smart_size(Evas_Object *obj, int w, int h, Eina_Bool force) evas_object_size_hint_request_set(obj, sd->font.chw * sd->grid.w, sd->font.chh * sd->grid.h); - _sel_set(sd, EINA_FALSE); + termio_sel_set(sd, EINA_FALSE); termpty_resize(sd->pty, w, h); _smart_calculate(obj); @@ -5678,14 +3424,33 @@ _smart_cb_change(void *data) return EINA_FALSE; } -static void -_smart_update_queue(Termio *sd) +void +termio_smart_update_queue(Termio *sd) { if (sd->anim) return; sd->anim = ecore_animator_add(_smart_cb_change, sd->self); } +void +termio_sel_set(Termio *sd, Eina_Bool enable) +{ + if (sd->pty->selection.is_active == enable) + return; + sd->pty->selection.is_active = enable; + if (enable) + evas_object_smart_callback_call(sd->win, "selection,on", NULL); + else + { + evas_object_smart_callback_call(sd->win, "selection,off", NULL); + sd->pty->selection.by_word = EINA_FALSE; + sd->pty->selection.by_line = EINA_FALSE; + free(sd->pty->selection.codepoints); + sd->pty->selection.codepoints = NULL; + } +} + + static void _cursor_cb_move(void *data, Evas *_e EINA_UNUSED, @@ -5939,7 +3704,7 @@ _smart_pty_change(void *data) // if scroll to bottom on updates if (sd->jump_on_change) sd->scroll = 0; - _smart_update_queue(sd); + termio_smart_update_queue(sd); } static void @@ -5974,9 +3739,9 @@ _smart_pty_cancel_sel(void *data) EINA_SAFETY_ON_NULL_RETURN(sd); if (sd->pty->selection.is_active) { - _sel_set(sd, EINA_FALSE); + termio_sel_set(sd, EINA_FALSE); sd->pty->selection.makesel = EINA_FALSE; - _smart_update_queue(sd); + termio_smart_update_queue(sd); } } @@ -6607,7 +4372,7 @@ termio_key_down(Evas_Object *termio, if (!key_is_modifier(ev->key)) { sd->scroll = 0; - _smart_update_queue(sd); + termio_smart_update_queue(sd); } } if (sd->config->flicker_on_key) diff --git a/src/bin/termio.h b/src/bin/termio.h index 53cbd07e..9fe6c983 100644 --- a/src/bin/termio.h +++ b/src/bin/termio.h @@ -6,6 +6,7 @@ #include "col.h" #include "termpty.h" #include "win.h" +#include "termiointernals.h" Evas_Object *termio_add(Evas_Object *parent, Config *config, const char *cmd, Eina_Bool login_shell, const char *cd, int w, int h, @@ -13,12 +14,15 @@ Evas_Object *termio_add(Evas_Object *parent, Config *config, const char *cmd, void termio_win_set(Evas_Object *obj, Evas_Object *win); void termio_theme_set(Evas_Object *obj, Evas_Object *theme); Eina_Bool termio_selection_exists(const Evas_Object *obj); +void termio_take_selection_text(Termio *sd, Elm_Sel_Type type, const char *text); void termio_scroll_top_backlog(Evas_Object *obj); void termio_scroll_delta(Evas_Object *obj, int delta, int by_page); void termio_scroll_set(Evas_Object *obj, int scroll); -void termio_scroll(Evas_Object *obj, int direction, int start_y, int end_y); void termio_content_change(Evas_Object *obj, Evas_Coord x, Evas_Coord y, int n); +void +termio_handle_right_click(Evas_Event_Mouse_Down *ev, Termio *sd, + int cx, int cy); void termio_config_update(Evas_Object *obj); void termio_font_update(Evas_Object *obj); Config *termio_config_get(const Evas_Object *obj); @@ -60,5 +64,12 @@ void termio_key_down(Evas_Object *termio, const Evas_Event_Key_Down *ev, Eina_Bool action_handled); void termio_focus_in(Evas_Object *termio); void termio_focus_out(Evas_Object *termio); - +void termio_smart_update_queue(Termio *sd); +void termio_object_geometry_get(Termio *sd, + Evas_Coord *x, Evas_Coord *y, + Evas_Coord *w, Evas_Coord *h); +void +termio_sel_set(Termio *sd, Eina_Bool enable); +void +termio_smart_cb_mouse_move_job(void *data); #endif diff --git a/src/bin/termiointernals.c b/src/bin/termiointernals.c new file mode 100644 index 00000000..6f4a3db1 --- /dev/null +++ b/src/bin/termiointernals.c @@ -0,0 +1,2192 @@ +#include "private.h" + +#include + +#include "tytest.h" +#include "termio.h" +#include "miniview.h" +#include "termpty.h" +#include "termptyops.h" +#include "termiointernals.h" +#include "utf8.h" + +/* {{{ Selection */ + +void +termio_selection_get(Termio *sd, + int c1x, int c1y, int c2x, int c2y, + struct ty_sb *sb, + Eina_Bool rtrim) +{ + int x, y; + + termpty_backlog_lock(); + for (y = c1y; y <= c2y; y++) + { + Termcell *cells; + ssize_t w; + int last0, v, start_x, end_x; + + w = 0; + last0 = -1; + cells = termpty_cellrow_get(sd->pty, y, &w); + if (!cells || !w) + { + if (ty_sb_add(sb, "\n", 1) < 0) goto err; + continue; + } + if (w > sd->grid.w) w = sd->grid.w; + if (y == c1y && c1x >= w) + { + if (rtrim) + ty_sb_spaces_rtrim(sb); + if (ty_sb_add(sb, "\n", 1) < 0) goto err; + continue; + } + start_x = c1x; + end_x = (c2x >= w) ? w - 1 : c2x; + if (c1y != c2y) + { + if (y == c1y) end_x = w - 1; + else if (y == c2y) start_x = 0; + else + { + start_x = 0; + end_x = w - 1; + } + } + for (x = start_x; x <= end_x; x++) + { + if ((cells[x].codepoint == 0) && (cells[x].att.dblwidth)) + { + if (x < end_x) x++; + else break; + } + if (x >= w) break; + if (cells[x].att.newline) + { + last0 = -1; + if ((y != c2y) || (x != end_x)) + { + if (rtrim) + ty_sb_spaces_rtrim(sb); + if (ty_sb_add(sb, "\n", 1) < 0) goto err; + } + break; + } + else if (cells[x].codepoint == 0) + { + if (last0 < 0) last0 = x; + } + else + { + char txt[8]; + int txtlen; + + if (last0 >= 0) + { + v = x - last0 - 1; + last0 = -1; + while (v >= 0) + { + if (ty_sb_add(sb, " ", 1) < 0) goto err; + v--; + } + } + txtlen = codepoint_to_utf8(cells[x].codepoint, txt); + if (txtlen > 0) + if (ty_sb_add(sb, txt, txtlen) < 0) goto err; + if ((x == (w - 1)) && + ((x != c2x) || (y != c2y))) + { + if (!cells[x].att.autowrapped) + { + if (rtrim) + ty_sb_spaces_rtrim(sb); + if (ty_sb_add(sb, "\n", 1) < 0) goto err; + } + } + } + } + if (last0 >= 0) + { + if (y == c2y) + { + Eina_Bool have_more = EINA_FALSE; + + for (x = end_x + 1; x < w; x++) + { + if ((cells[x].codepoint == 0) && + (cells[x].att.dblwidth)) + { + if (x < (w - 1)) x++; + else break; + } + if (((cells[x].codepoint != 0) && + (cells[x].codepoint != ' ')) || + (cells[x].att.newline)) + { + have_more = EINA_TRUE; + break; + } + } + if (!have_more) + { + if (rtrim) + ty_sb_spaces_rtrim(sb); + if (ty_sb_add(sb, "\n", 1) < 0) goto err; + } + else + { + for (x = last0; x <= end_x; x++) + { + if ((cells[x].codepoint == 0) && + (cells[x].att.dblwidth)) + { + if (x < (w - 1)) x++; + else break; + } + if (x >= w) break; + if (ty_sb_add(sb, " ", 1) < 0) goto err; + } + } + } + else + { + if (rtrim) + ty_sb_spaces_rtrim(sb); + if (ty_sb_add(sb, "\n", 1) < 0) goto err; + } + } + } + termpty_backlog_unlock(); + + if (rtrim) + ty_sb_spaces_rtrim(sb); + + return; + +err: + ty_sb_free(sb); +} + + +struct Codepoints_Buf { + Eina_Unicode *codepoints; + size_t len; + size_t size; +}; + +static int +_codepoint_buf_append(struct Codepoints_Buf *buf, + Eina_Unicode u) +{ + if (EINA_UNLIKELY(buf->len == buf->size)) + { + buf->size *= 2; + Eina_Unicode *codepoints = realloc(buf->codepoints, + buf->size * sizeof(Eina_Unicode)); + if (EINA_UNLIKELY(!codepoints)) + { + free(buf->codepoints); + buf->len = buf->size = 0; + return -1; + } + buf->codepoints = codepoints; + } + buf->codepoints[buf->len++] = u; + return 0; +} + +static void +_sel_codepoints_get(const Termio *sd, + struct Codepoints_Buf *buf, + int c1x, int c1y, int c2x, int c2y) +{ + int x, y; + +#define TRY(ACTION) do { \ + if (EINA_UNLIKELY(ACTION < 0)) \ + { \ + goto err; \ + } \ +} while (0) + + termpty_backlog_lock(); + for (y = c1y; y <= c2y; y++) + { + Termcell *cells; + ssize_t w = 0; + int start_x, end_x; + + cells = termpty_cellrow_get(sd->pty, y, &w); + if (!cells || !w || (y == c1y && c1x >= w)) + { + w = 0; + } + + /* Compute @start_x, @end_x */ + start_x = c1x; + end_x = c2x; + if (c1y != c2y) + { + if (y == c1y) + { + end_x = sd->grid.w - 1; + } + else if (y == c2y) + { + start_x = 0; + } + else + { + start_x = 0; + end_x = sd->grid.w - 1; + } + } + /* Lookup every cell in that line */ + for (x = start_x; x <= end_x; x++) + { + if (x >= w) + { + /* Selection outside of current line of "text" */ + TRY(_codepoint_buf_append(buf, ' ')); + } + else if (cells[x].codepoint == 0) + { + TRY(_codepoint_buf_append(buf, ' ')); + } + else + { + TRY(_codepoint_buf_append(buf, cells[x].codepoint)); + } + } + } +err: + termpty_backlog_unlock(); +} + + +static void +_sel_fill_in_codepoints_array(Termio *sd) +{ + int start_x = 0, start_y = 0, end_x = 0, end_y = 0; + struct Codepoints_Buf buf = { + .codepoints = NULL, + .len = 0, + .size = 256, + }; + + free(sd->pty->selection.codepoints); + sd->pty->selection.codepoints = NULL; + + if (!sd->pty->selection.is_active) + return; + + buf.codepoints = malloc(sizeof(Eina_Unicode) * buf.size); + if (!buf.codepoints) + return; + + start_x = sd->pty->selection.start.x; + start_y = sd->pty->selection.start.y; + end_x = sd->pty->selection.end.x; + end_y = sd->pty->selection.end.y; + + if (!sd->pty->selection.is_top_to_bottom) + { + INT_SWAP(start_y, end_y); + INT_SWAP(start_x, end_x); + } + + if (sd->pty->selection.is_box) + { + int i; + + for (i = start_y; i <= end_y; i++) + { + _sel_codepoints_get(sd, &buf, start_x, i, end_x, i); + } + } + else + { + _sel_codepoints_get(sd, &buf, start_x, start_y, end_x, end_y); + } + sd->pty->selection.codepoints = buf.codepoints; +} + +Eina_Bool +termio_take_selection(Evas_Object *obj, Elm_Sel_Type type) +{ + Termio *sd = evas_object_smart_data_get(obj); + int start_x = 0, start_y = 0, end_x = 0, end_y = 0; + const char *s = NULL; + size_t len = 0; + + EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE); + if (sd->pty->selection.is_active) + { + start_x = sd->pty->selection.start.x; + start_y = sd->pty->selection.start.y; + end_x = sd->pty->selection.end.x; + end_y = sd->pty->selection.end.y; + + if (!sd->pty->selection.is_top_to_bottom) + { + INT_SWAP(start_y, end_y); + INT_SWAP(start_x, end_x); + } + } + else + { + if (sd->link.string) + { + len = strlen(sd->link.string); + s = eina_stringshare_add_length(sd->link.string, len); + } + goto end; + } + + if (sd->pty->selection.is_box) + { + int i; + struct ty_sb sb = {.buf = NULL, .len = 0, .alloc = 0}; + + for (i = start_y; i <= end_y; i++) + { + struct ty_sb isb = {.buf = NULL, .len = 0, .alloc = 0}; + termio_selection_get(sd, start_x, i, end_x, i, + &isb, EINA_TRUE); + + if (isb.len) + { + if (isb.buf[sb.len - 1] != '\n' && i != end_y) + ty_sb_add(&isb, "\n", 1); + ty_sb_add(&sb, isb.buf, isb.len); + } + ty_sb_free(&isb); + } + len = sb.len; + s = ty_sb_steal_buf(&sb); + s = eina_stringshare_add_length(s, len); + ty_sb_free(&sb); + } + else + { + struct ty_sb sb = {.buf = NULL, .len = 0, .alloc = 0}; + + termio_selection_get(sd, start_x, start_y, end_x, end_y, &sb, EINA_TRUE); + len = sb.len; + s = eina_stringshare_add_length(ty_sb_steal_buf(&sb), len); + ty_sb_free(&sb); + } + +end: + if (s) + { + if ((sd->win) && (len > 0)) + termio_take_selection_text(sd, type, s); + eina_stringshare_del(s); + return EINA_TRUE; + } + return EINA_FALSE; +} + +static void +_sel_line(Termio *sd, int cy) +{ + int y; + ssize_t w = 0; + Termcell *cells; + + termpty_backlog_lock(); + + termio_sel_set(sd, EINA_TRUE); + sd->pty->selection.makesel = EINA_FALSE; + + sd->pty->selection.start.x = 0; + sd->pty->selection.start.y = cy; + sd->pty->selection.end.x = sd->grid.w - 1; + sd->pty->selection.end.y = cy; + + /* check lines above */ + y = cy; + for (;;) + { + cells = termpty_cellrow_get(sd->pty, y - 1, &w); + if (!cells || !cells[w-1].att.autowrapped) break; + + y--; + } + sd->pty->selection.start.y = y; + y = cy; + + /* check lines below */ + for (;;) + { + cells = termpty_cellrow_get(sd->pty, y, &w); + if (!cells || !cells[w-1].att.autowrapped) break; + + sd->pty->selection.end.x = w - 1; + y++; + } + sd->pty->selection.end.y = y; + + sd->pty->selection.by_line = EINA_TRUE; + sd->pty->selection.is_top_to_bottom = EINA_TRUE; + + termpty_backlog_unlock(); +} + +static void +_sel_line_to(Termio *sd, int cy, Eina_Bool extend) +{ + int start_y, end_y, c_start_y, c_end_y, + orig_y, orig_start_y, orig_end_y; + + EINA_SAFETY_ON_NULL_RETURN(sd); + + /* Only change the end position */ + orig_y = sd->pty->selection.orig.y; + start_y = sd->pty->selection.start.y; + end_y = sd->pty->selection.end.y; + + if (!sd->pty->selection.is_top_to_bottom) + INT_SWAP(start_y, end_y); + + _sel_line(sd, cy); + c_start_y = sd->pty->selection.start.y; + c_end_y = sd->pty->selection.end.y; + + _sel_line(sd, orig_y); + orig_start_y = sd->pty->selection.start.y; + orig_end_y = sd->pty->selection.end.y; + + if (sd->pty->selection.is_box) + { + if (extend) + { + if (start_y <= cy && cy <= end_y ) + { + start_y = MIN(c_start_y, orig_start_y); + end_y = MAX(c_end_y, orig_end_y); + } + else + { + if (c_end_y > end_y) + { + orig_y = start_y; + end_y = c_end_y; + } + if (c_start_y < start_y) + { + orig_y = end_y; + start_y = c_start_y; + } + } + goto end; + } + else + { + start_y = MIN(c_start_y, orig_start_y); + end_y = MAX(c_end_y, orig_end_y); + goto end; + } + } + else + { + if (c_start_y < start_y) + { + /* orig is at bottom */ + if (extend) + { + orig_y = end_y; + } + sd->pty->selection.is_top_to_bottom = EINA_FALSE; + end_y = c_start_y; + start_y = orig_end_y; + } + else if (c_end_y > end_y) + { + if (extend) + { + orig_y = start_y; + } + sd->pty->selection.is_top_to_bottom = EINA_TRUE; + start_y = orig_start_y; + end_y = c_end_y; + } + else + { + if (c_start_y < orig_start_y) + { + sd->pty->selection.is_top_to_bottom = EINA_FALSE; + start_y = orig_end_y; + end_y = c_start_y; + } + else + { + sd->pty->selection.is_top_to_bottom = EINA_TRUE; + start_y = orig_start_y; + end_y = c_end_y; + } + } + } +end: + sd->pty->selection.orig.y = orig_y; + sd->pty->selection.start.y = start_y; + sd->pty->selection.end.y = end_y; + if (sd->pty->selection.is_top_to_bottom) + { + sd->pty->selection.start.x = 0; + sd->pty->selection.end.x = sd->grid.w - 1; + } + else + { + sd->pty->selection.end.x = 0; + sd->pty->selection.start.x = sd->grid.w - 1; + } +} + +static Eina_Bool +_codepoint_is_wordsep(const Eina_Unicode g) +{ + /* TODO: use bitmaps to speed things up */ + // http://en.wikipedia.org/wiki/Asterisk + // http://en.wikipedia.org/wiki/Comma + // http://en.wikipedia.org/wiki/Interpunct + // http://en.wikipedia.org/wiki/Bracket + static const Eina_Unicode wordsep[] = + { + 0, + ' ', + '!', + '"', + '#', + '$', + '\'', + '(', + ')', + '*', + ',', + ';', + '=', + '?', + '[', + '\\', + ']', + '^', + '`', + '{', + '|', + '}', + 0x00a0, + 0x00ab, + 0x00b7, + 0x00bb, + 0x0294, + 0x02bb, + 0x02bd, + 0x02d0, + 0x0312, + 0x0313, + 0x0314, + 0x0315, + 0x0326, + 0x0387, + 0x055d, + 0x055e, + 0x060c, + 0x061f, + 0x066d, + 0x07fb, + 0x1363, + 0x1367, + 0x14fe, + 0x1680, + 0x1802, + 0x1808, + 0x180e, + 0x2000, + 0x2001, + 0x2002, + 0x2003, + 0x2004, + 0x2005, + 0x2006, + 0x2007, + 0x2008, + 0x2009, + 0x200a, + 0x200b, + 0x2018, + 0x2019, + 0x201a, + 0x201b, + 0x201c, + 0x201d, + 0x201e, + 0x201f, + 0x2022, + 0x2027, + 0x202f, + 0x2039, + 0x203a, + 0x203b, + 0x203d, + 0x2047, + 0x2048, + 0x2049, + 0x204e, + 0x205f, + 0x2217, + 0x225f, + 0x2308, + 0x2309, + 0x2420, + 0x2422, + 0x2423, + 0x2722, + 0x2723, + 0x2724, + 0x2725, + 0x2731, + 0x2732, + 0x2733, + 0x273a, + 0x273b, + 0x273c, + 0x273d, + 0x2743, + 0x2749, + 0x274a, + 0x274b, + 0x2a7b, + 0x2a7c, + 0x2cfa, + 0x2e2e, + 0x2e2e, + 0x3000, + 0x3001, + 0x3008, + 0x3009, + 0x300a, + 0x300b, + 0x300c, + 0x300c, + 0x300d, + 0x300d, + 0x300e, + 0x300f, + 0x3010, + 0x3011, + 0x301d, + 0x301e, + 0x301f, + 0x30fb, + 0xa60d, + 0xa60f, + 0xa6f5, + 0xe0a0, + 0xe0b0, + 0xe0b2, + 0xfe10, + 0xfe41, + 0xfe42, + 0xfe43, + 0xfe44, + 0xfe50, + 0xfe51, + 0xfe56, + 0xfe61, + 0xfe62, + 0xfe63, + 0xfeff, + 0xff02, + 0xff07, + 0xff08, + 0xff09, + 0xff0a, + 0xff0c, + 0xff1b, + 0xff1c, + 0xff1e, + 0xff1f, + 0xff3b, + 0xff3d, + 0xff5b, + 0xff5d, + 0xff62, + 0xff63, + 0xff64, + 0xff65, + 0xe002a + }; + size_t imax = (sizeof(wordsep) / sizeof(wordsep[0])) - 1, + imin = 0; + size_t imaxmax = imax; + + if (g & 0x80000000) + return EINA_TRUE; + + while (imax >= imin) + { + size_t imid = (imin + imax) / 2; + + if (wordsep[imid] == g) return EINA_TRUE; + else if (wordsep[imid] < g) imin = imid + 1; + else imax = imid - 1; + if (imax > imaxmax) break; + } + return EINA_FALSE; +} + +static Eina_Bool +_to_trim(Eina_Unicode codepoint, Eina_Bool right_trim) +{ + static const Eina_Unicode trim_chars[] = + { + ':', + '<', + '>', + '.' + }; + size_t i = 0, len; + len = sizeof(trim_chars)/sizeof((trim_chars)[0]); + if (right_trim) + len--; /* do not right trim . */ + + for (i = 0; i < len; i++) + if (codepoint == trim_chars[i]) + return EINA_TRUE; + return EINA_FALSE; +} + +static void +_trim_sel_word(Termio *sd) +{ + Termpty *pty = sd->pty; + Termcell *cells; + int start = 0, end = 0, y = 0; + ssize_t w; + + /* 1st step: trim from the start */ + start = pty->selection.start.x; + for (y = pty->selection.start.y; + y <= pty->selection.end.y; + y++) + { + cells = termpty_cellrow_get(pty, y, &w); + if (!cells) + return; + + while (start < w && _to_trim(cells[start].codepoint, EINA_TRUE)) + start++; + + if (start < w) + break; + + start = 0; + } + /* check validy of the selection */ + if ((y > pty->selection.end.y) || + ((y == pty->selection.end.y) && + (start > pty->selection.end.x))) + { + pty->selection.start.y = pty->selection.end.y; + pty->selection.start.x = pty->selection.end.x; + return; + } + pty->selection.start.y = y; + pty->selection.start.x = start; + + /* 2nd step: trim from the end */ + end = pty->selection.end.x; + for (y = pty->selection.end.y; + y >= pty->selection.start.y; + y--) + { + cells = termpty_cellrow_get(pty, y, &w); + if (!cells) + return; + + while (end >= 0 && _to_trim(cells[end].codepoint, EINA_FALSE)) + end--; + + if (end >= 0) + break; + } + if (end < 0) + { + return; + } + /* check validy of the selection */ + if ((y < pty->selection.start.y) || + ((y == pty->selection.start.y) && + (end < pty->selection.start.x))) + { + pty->selection.end.x = pty->selection.start.x; + pty->selection.end.y = pty->selection.start.y; + return; + } + pty->selection.end.x = end; + pty->selection.end.y = y; +} + +static void +_sel_word(Termio *sd, int cx, int cy) +{ + Termcell *cells; + int x, y; + ssize_t w = 0; + Eina_Bool done = EINA_FALSE; + + termpty_backlog_lock(); + + termio_sel_set(sd, EINA_TRUE); + sd->pty->selection.makesel = EINA_TRUE; + sd->pty->selection.orig.x = cx; + sd->pty->selection.orig.y = cy; + sd->pty->selection.start.x = cx; + sd->pty->selection.start.y = cy; + sd->pty->selection.end.x = cx; + sd->pty->selection.end.y = cy; + x = cx; + y = cy; + + if (sd->link.string && + (sd->link.x1 <= cx) && (cx <= sd->link.x2) && + (sd->link.y1 <= cy) && (cy <= sd->link.y2)) + { + sd->pty->selection.start.x = sd->link.x1; + sd->pty->selection.start.y = sd->link.y1; + sd->pty->selection.end.x = sd->link.x2; + sd->pty->selection.end.y = sd->link.y2; + goto end; + } + cells = termpty_cellrow_get(sd->pty, y, &w); + if (!cells) goto end; + if (x >= w) x = w - 1; + + do + { + for (; x >= 0; x--) + { + if ((cells[x].codepoint == 0) && (cells[x].att.dblwidth) && + (x > 0)) + x--; + if (_codepoint_is_wordsep(cells[x].codepoint)) + { + done = EINA_TRUE; + break; + } + sd->pty->selection.start.x = x; + sd->pty->selection.start.y = y; + } + if (!done) + { + Termcell *old_cells = cells; + + cells = termpty_cellrow_get(sd->pty, y - 1, &w); + if (!cells || !cells[w-1].att.autowrapped) + { + x = 0; + cells = old_cells; + done = EINA_TRUE; + } + else + { + y--; + x = w - 1; + } + } + } + while (!done); + + done = EINA_FALSE; + if (cy != y) + { + y = cy; + cells = termpty_cellrow_get(sd->pty, y, &w); + if (!cells) goto end; + } + x = cx; + + do + { + for (; x < w; x++) + { + if ((cells[x].codepoint == 0) && (cells[x].att.dblwidth) && + (x < (w - 1))) + { + sd->pty->selection.end.x = x; + x++; + } + if (_codepoint_is_wordsep(cells[x].codepoint)) + { + done = EINA_TRUE; + break; + } + sd->pty->selection.end.x = x; + sd->pty->selection.end.y = y; + } + if (!done) + { + if (!cells[w - 1].att.autowrapped) goto end; + y++; + x = 0; + cells = termpty_cellrow_get(sd->pty, y, &w); + if (!cells) goto end; + } + } + while (!done); + + end: + + sd->pty->selection.by_word = EINA_TRUE; + sd->pty->selection.is_top_to_bottom = EINA_TRUE; + _trim_sel_word(sd); + + termpty_backlog_unlock(); +} + +static void +_sel_word_to(Termio *sd, int cx, int cy, Eina_Bool extend) +{ + int start_x, start_y, end_x, end_y, orig_x, orig_y, + c_start_x, c_start_y, c_end_x, c_end_y, + orig_start_x, orig_start_y, orig_end_x, orig_end_y; + + EINA_SAFETY_ON_NULL_RETURN(sd); + + orig_x = sd->pty->selection.orig.x; + orig_y = sd->pty->selection.orig.y; + start_x = sd->pty->selection.start.x; + start_y = sd->pty->selection.start.y; + end_x = sd->pty->selection.end.x; + end_y = sd->pty->selection.end.y; + + if (!sd->pty->selection.is_top_to_bottom) + { + INT_SWAP(start_x, end_x); + INT_SWAP(start_y, end_y); + } + + _sel_word(sd, cx, cy); + c_start_x = sd->pty->selection.start.x; + c_start_y = sd->pty->selection.start.y; + c_end_x = sd->pty->selection.end.x; + c_end_y = sd->pty->selection.end.y; + + _sel_word(sd, orig_x, orig_y); + orig_start_x = sd->pty->selection.start.x; + orig_start_y = sd->pty->selection.start.y; + orig_end_x = sd->pty->selection.end.x; + orig_end_y = sd->pty->selection.end.y; + + if (sd->pty->selection.is_box) + { + if (extend) + { + /* special case: kind of line selection */ + if (c_start_y != c_end_y) + { + start_x = 0; + end_x = sd->grid.w - 1; + if (start_y <= cy && cy <= end_y ) + { + start_y = MIN(c_start_y, orig_start_y); + end_y = MAX(c_end_y, orig_end_y); + } + else + { + if (c_end_y > end_y) + { + orig_y = start_y; + end_y = c_end_y; + } + if (c_start_y < start_y) + { + orig_y = end_y; + start_y = c_start_y; + } + } + goto end; + } + if ((start_y <= cy && cy <= end_y ) && + (start_x <= cx && cx <= end_x )) + { + start_x = MIN(c_start_x, orig_start_x); + end_x = MAX(c_end_x, orig_end_x); + start_y = MIN(c_start_y, orig_start_y); + end_y = MAX(c_end_y, orig_end_y); + } + else + { + if (c_end_x > end_x) + { + orig_x = start_x; + end_x = c_end_x; + } + if (c_start_x < start_x) + { + orig_x = end_x; + start_x = c_start_x; + } + if (c_end_y > end_y) + { + orig_y = start_y; + end_y = c_end_y; + } + if (c_start_y < start_y) + { + orig_y = end_y; + start_y = c_start_y; + } + end_x = MAX(c_end_x, end_x); + start_y = MIN(c_start_y, start_y); + end_y = MAX(c_end_y, end_y); + } + } + else + { + /* special case: kind of line selection */ + if (c_start_y != c_end_y || orig_start_y != orig_end_y) + { + start_x = 0; + end_x = sd->grid.w - 1; + start_y = MIN(c_start_y, orig_start_y); + end_y = MAX(c_end_y, orig_end_y); + goto end; + } + + start_x = MIN(c_start_x, orig_start_x); + end_x = MAX(c_end_x, orig_end_x); + start_y = MIN(c_start_y, orig_start_y); + end_y = MAX(c_end_y, orig_end_y); + } + } + else + { + if (c_start_y < start_y || + (c_start_y == start_y && + c_start_x < start_x)) + { + /* orig is at bottom */ + if (extend) + { + orig_x = end_x; + orig_y = end_y; + } + sd->pty->selection.is_top_to_bottom = EINA_FALSE; + end_x = c_start_x; + end_y = c_start_y; + start_x = orig_end_x; + start_y = orig_end_y; + } + else if (c_end_y > end_y || + (c_end_y == end_y && c_end_x >= end_x)) + { + if (extend) + { + orig_x = start_x; + orig_y = start_y; + } + sd->pty->selection.is_top_to_bottom = EINA_TRUE; + start_x = orig_start_x; + start_y = orig_start_y; + end_x = c_end_x; + end_y = c_end_y; + } + else + { + if (c_start_y < orig_start_y || + (c_start_y == orig_start_y && c_start_x <= orig_start_x)) + { + sd->pty->selection.is_top_to_bottom = EINA_FALSE; + start_x = orig_end_x; + start_y = orig_end_y; + end_x = c_start_x; + end_y = c_start_y; + } + else + { + sd->pty->selection.is_top_to_bottom = EINA_TRUE; + start_x = orig_start_x; + start_y = orig_start_y; + end_x = c_end_x; + end_y = c_end_y; + } + } + } + +end: + sd->pty->selection.orig.x = orig_x; + sd->pty->selection.orig.y = orig_y; + sd->pty->selection.start.x = start_x; + sd->pty->selection.start.y = start_y; + sd->pty->selection.end.x = end_x; + sd->pty->selection.end.y = end_y; +} + +static void +_sel_to(Termio *sd, int cx, int cy, Eina_Bool extend) +{ + int start_x, start_y, end_x, end_y, orig_x, orig_y; + + EINA_SAFETY_ON_NULL_RETURN(sd); + + orig_x = sd->pty->selection.orig.x; + orig_y = sd->pty->selection.orig.y; + start_x = sd->pty->selection.start.x; + start_y = sd->pty->selection.start.y; + end_x = sd->pty->selection.end.x; + end_y = sd->pty->selection.end.y; + + if (sd->pty->selection.is_box) + { + if (!sd->pty->selection.is_top_to_bottom) + INT_SWAP(start_y, end_y); + if (start_x > end_x) + INT_SWAP(start_x, end_x); + + if (cy < start_y) + { + start_y = cy; + } + else if (cy > end_y) + { + end_y = cy; + } + else + { + start_y = orig_y; + end_y = cy; + } + + if (cx < start_x) + { + start_x = cx; + } + else if (cx > end_x) + { + end_x = cx; + } + else + { + start_x = orig_x; + end_x = cx; + } + sd->pty->selection.is_top_to_bottom = (end_y > start_y); + if (sd->pty->selection.is_top_to_bottom) + { + if (start_x > end_x) + INT_SWAP(start_x, end_x); + } + else + { + if (start_x < end_x) + INT_SWAP(start_x, end_x); + } + } + else + { + if (!sd->pty->selection.is_top_to_bottom) + { + INT_SWAP(start_x, end_x); + INT_SWAP(start_y, end_y); + } + if (cy < start_y || + (cy == start_y && + cx < start_x)) + { + /* orig is at bottom */ + if (sd->pty->selection.is_top_to_bottom && extend) + { + orig_x = end_x; + orig_y = end_y; + } + sd->pty->selection.is_top_to_bottom = EINA_FALSE; + } + else if (cy > end_y || + (cy == end_y && cx >= end_x)) + { + if (!sd->pty->selection.is_top_to_bottom && extend) + { + orig_x = start_x; + orig_y = start_y; + } + sd->pty->selection.is_top_to_bottom = EINA_TRUE; + } + else + { + sd->pty->selection.is_top_to_bottom = + (cy > orig_y) || (cy == orig_y && cx > orig_x); + } + start_x = orig_x; + start_y = orig_y; + end_x = cx; + end_y = cy; + } + + sd->pty->selection.orig.x = orig_x; + sd->pty->selection.orig.y = orig_y; + sd->pty->selection.start.x = start_x; + sd->pty->selection.start.y = start_y; + sd->pty->selection.end.x = end_x; + sd->pty->selection.end.y = end_y; +} + +static void +_selection_newline_extend_fix(Evas_Object *obj) +{ + int start_x, start_y, end_x, end_y; + Termio *sd; + ssize_t w; + + sd = evas_object_smart_data_get(obj); + + if ((sd->top_left) || (sd->bottom_right) || (sd->pty->selection.is_box)) + return; + + termpty_backlog_lock(); + + start_x = sd->pty->selection.start.x; + start_y = sd->pty->selection.start.y; + end_x = sd->pty->selection.end.x; + end_y = sd->pty->selection.end.y; + if (!sd->pty->selection.is_top_to_bottom) + { + INT_SWAP(start_y, end_y); + INT_SWAP(start_x, end_x); + } + + if ((end_y > start_y) || + ((end_y == start_y) && + (end_x >= start_x))) + { + /* going down/right */ + w = termpty_row_length(sd->pty, start_y); + if (w < start_x) + start_x = w; + w = termpty_row_length(sd->pty, end_y); + if (w <= end_x) + end_x = sd->pty->w; + } + else + { + /* going up/left */ + w = termpty_row_length(sd->pty, end_y); + if (w < end_x) + end_x = w; + w = termpty_row_length(sd->pty, start_y); + if (w <= start_x) + start_x = sd->pty->w; + } + + if (!sd->pty->selection.is_top_to_bottom) + { + INT_SWAP(start_y, end_y); + INT_SWAP(start_x, end_x); + } + sd->pty->selection.start.x = start_x; + sd->pty->selection.start.y = start_y; + sd->pty->selection.end.x = end_x; + sd->pty->selection.end.y = end_y; + + termpty_backlog_unlock(); +} + +/* }}} */ + +void +termio_selection_dbl_fix(Termio *sd) +{ + int start_x, start_y, end_x, end_y; + ssize_t w = 0; + Termcell *cells; + /* Only change the end position */ + + EINA_SAFETY_ON_NULL_RETURN(sd); + + start_x = sd->pty->selection.start.x; + start_y = sd->pty->selection.start.y; + end_x = sd->pty->selection.end.x; + end_y = sd->pty->selection.end.y; + if (!sd->pty->selection.is_top_to_bottom) + { + INT_SWAP(start_y, end_y); + INT_SWAP(start_x, end_x); + } + + termpty_backlog_lock(); + cells = termpty_cellrow_get(sd->pty, end_y - sd->scroll, &w); + if (cells) + { + // if sel2 after sel1 + if ((end_y > start_y) || + ((end_y == start_y) && + (end_x >= start_x))) + { + if (end_x < (w - 1)) + { + if ((cells[end_x].codepoint != 0) && + (cells[end_x].att.dblwidth)) + end_x++; + } + } + // else sel1 after sel 2 + else + { + if (end_x > 0) + { + if ((cells[end_x].codepoint == 0) && + (cells[end_x].att.dblwidth)) + end_x--; + } + } + } + cells = termpty_cellrow_get(sd->pty, start_y - sd->scroll, &w); + if (cells) + { + // if sel2 after sel1 + if ((end_y > start_y) || + ((end_y == start_y) && + (end_x >= start_x))) + { + if (start_x > 0) + { + if ((cells[start_x].codepoint == 0) && + (cells[start_x].att.dblwidth)) + start_x--; + } + } + // else sel1 after sel 2 + else + { + if (start_x < (w - 1)) + { + if ((cells[start_x].codepoint != 0) && + (cells[start_x].att.dblwidth)) + start_x++; + } + } + } + termpty_backlog_unlock(); + + if (!sd->pty->selection.is_top_to_bottom) + { + INT_SWAP(start_y, end_y); + INT_SWAP(start_x, end_x); + } + sd->pty->selection.start.x = start_x; + sd->pty->selection.start.y = start_y; + sd->pty->selection.end.x = end_x; + sd->pty->selection.end.y = end_y; +} + + +static void +_handle_mouse_down_single_click(Termio *sd, + int cx, int cy, + int ctrl, int alt, int shift) +{ + sd->didclick = EINA_FALSE; + /* SINGLE CLICK */ + if (sd->pty->selection.is_active && + (sd->top_left || sd->bottom_right)) + { + /* stretch selection */ + int start_x, start_y, end_x, end_y; + + start_x = sd->pty->selection.start.x; + start_y = sd->pty->selection.start.y; + end_x = sd->pty->selection.end.x; + end_y = sd->pty->selection.end.y; + + if (!sd->pty->selection.is_top_to_bottom) + { + INT_SWAP(start_y, end_y); + INT_SWAP(start_x, end_x); + } + + cy -= sd->scroll; + + sd->pty->selection.makesel = EINA_TRUE; + + if (sd->pty->selection.is_box) + { + if (end_x < start_x) + INT_SWAP(end_x, start_x); + } + if (sd->top_left) + { + sd->pty->selection.orig.x = end_x; + sd->pty->selection.orig.y = end_y; + sd->pty->selection.is_top_to_bottom = EINA_FALSE; + } + else + { + /* sd->bottom_right */ + sd->pty->selection.orig.x = start_x; + sd->pty->selection.orig.y = start_y; + sd->pty->selection.is_top_to_bottom = EINA_TRUE; + } + + sd->pty->selection.start.x = sd->pty->selection.orig.x; + sd->pty->selection.start.y = sd->pty->selection.orig.y; + sd->pty->selection.end.x = cx; + sd->pty->selection.end.y = cy; + termio_selection_dbl_fix(sd); + } + else if (!shift && alt && !sd->pty->selection.is_active + && (sd->pty->mouse_mode == MOUSE_OFF)) + { + /* move cursor to position */ + termpty_move_cursor(sd->pty, cx, cy); + } + else if (!shift && !sd->pty->selection.is_active) + { + /* New selection */ + sd->moved = EINA_FALSE; + termio_sel_set(sd, EINA_FALSE); + sd->pty->selection.is_box = (ctrl || alt); + sd->pty->selection.start.x = cx; + sd->pty->selection.start.y = cy - sd->scroll; + sd->pty->selection.orig.x = sd->pty->selection.start.x; + sd->pty->selection.orig.y = sd->pty->selection.start.y; + sd->pty->selection.end.x = cx; + sd->pty->selection.end.y = cy - sd->scroll; + sd->pty->selection.makesel = EINA_TRUE; + sd->pty->selection.by_line = EINA_FALSE; + sd->pty->selection.by_word = EINA_FALSE; + termio_selection_dbl_fix(sd); + } + else if (shift && sd->pty->selection.is_active) + { + /* let cb_up handle it */ + /* do nothing */ + return; + } + else if (shift && + (time(NULL) - sd->pty->selection.last_click) <= 5) + { + sd->pty->selection.is_box = ctrl; + _sel_to(sd, cx, cy - sd->scroll, EINA_FALSE); + sd->pty->selection.is_active = EINA_TRUE; + termio_selection_dbl_fix(sd); + } + else + { + sd->pty->selection.is_box = ctrl; + sd->pty->selection.start.x = sd->pty->selection.end.x = cx; + sd->pty->selection.orig.x = cx; + sd->pty->selection.start.y = sd->pty->selection.end.y = cy - sd->scroll; + sd->pty->selection.orig.y = cy - sd->scroll; + sd->pty->selection.makesel = EINA_TRUE; + sd->didclick = !sd->pty->selection.is_active; + sd->pty->selection.is_active = EINA_FALSE; + termio_sel_set(sd, EINA_FALSE); + } +} + +static Eina_Bool +_rep_mouse_down(Termio *sd, Evas_Event_Mouse_Down *ev, int cx, int cy) +{ + char buf[64]; + Eina_Bool ret = EINA_FALSE; + int btn; + + if (sd->pty->mouse_mode == MOUSE_OFF) return EINA_FALSE; + if (!sd->mouse.button) + { + /* Need to remember the first button pressed for terminal handling */ + sd->mouse.button = ev->button; + } + + btn = ev->button - 1; + switch (sd->pty->mouse_ext) + { + case MOUSE_EXT_NONE: + if ((cx < (0xff - ' ')) && (cy < (0xff - ' '))) + { + if (sd->pty->mouse_mode == MOUSE_X10) + { + if (btn <= 2) + { + buf[0] = 0x1b; + buf[1] = '['; + buf[2] = 'M'; + buf[3] = btn + ' '; + buf[4] = cx + 1 + ' '; + buf[5] = cy + 1 + ' '; + buf[6] = 0; + termpty_write(sd->pty, buf, strlen(buf)); + ret = EINA_TRUE; + } + } + else + { + int meta = evas_key_modifier_is_set(ev->modifiers, "Alt") ? 8 : 0; + + if (btn > 2) btn = 0; + buf[0] = 0x1b; + buf[1] = '['; + buf[2] = 'M'; + buf[3] = (btn | meta) + ' '; + buf[4] = cx + 1 + ' '; + buf[5] = cy + 1 + ' '; + buf[6] = 0; + termpty_write(sd->pty, buf, strlen(buf)); + ret = EINA_TRUE; + } + } + break; + case MOUSE_EXT_UTF8: // ESC.[.M.BTN/FLGS.XUTF8.YUTF8 + { + int meta = evas_key_modifier_is_set(ev->modifiers, "Alt") ? 8 : 0; + int v, i; + + if (btn > 2) btn = 0; + buf[0] = 0x1b; + buf[1] = '['; + buf[2] = 'M'; + buf[3] = (btn | meta) + ' '; + i = 4; + v = cx + 1 + ' '; + if (v <= 127) buf[i++] = v; + else + { // 14 bits for cx/cy - enough i think + buf[i++] = 0xc0 + (v >> 6); + buf[i++] = 0x80 + (v & 0x3f); + } + v = cy + 1 + ' '; + if (v <= 127) buf[i++] = v; + else + { // 14 bits for cx/cy - enough i think + buf[i++] = 0xc0 + (v >> 6); + buf[i++] = 0x80 + (v & 0x3f); + } + buf[i] = 0; + termpty_write(sd->pty, buf, strlen(buf)); + ret = EINA_TRUE; + } + break; + case MOUSE_EXT_SGR: // ESC.[.<.NUM.;.NUM.;.NUM.M + { + int meta = evas_key_modifier_is_set(ev->modifiers, "Alt") ? 8 : 0; + + if (btn > 2) btn = 0; + snprintf(buf, sizeof(buf), "%c[<%i;%i;%iM", 0x1b, + (btn | meta), cx + 1, cy + 1); + termpty_write(sd->pty, buf, strlen(buf)); + ret = EINA_TRUE; + } + break; + case MOUSE_EXT_URXVT: // ESC.[.NUM.;.NUM.;.NUM.M + { + int meta = evas_key_modifier_is_set(ev->modifiers, "Alt") ? 8 : 0; + + if (btn > 2) btn = 0; + snprintf(buf, sizeof(buf), "%c[%i;%i;%iM", 0x1b, + (btn | meta) + ' ', + cx + 1, cy + 1); + termpty_write(sd->pty, buf, strlen(buf)); + ret = EINA_TRUE; + } + break; + default: + break; + } + return ret; +} + + +void +termio_internal_mouse_down(Termio *sd, + Evas_Event_Mouse_Down *ev) +{ + int cx, cy; + int shift, ctrl, alt; + + termio_cursor_to_xy(sd, ev->canvas.x, ev->canvas.y, &cx, &cy); + + shift = evas_key_modifier_is_set(ev->modifiers, "Shift"); + ctrl = evas_key_modifier_is_set(ev->modifiers, "Control"); + alt = evas_key_modifier_is_set(ev->modifiers, "Alt"); + + if ((ev->button == 3) && ctrl) + { + termio_handle_right_click(ev, sd, cx, cy); + return; + } + if (!shift && !ctrl) + if (_rep_mouse_down(sd, ev, cx, cy)) + { + if (sd->pty->selection.is_active) + { + termio_sel_set(sd, EINA_FALSE); + termio_smart_update_queue(sd); + } + return; + } + if (ev->button == 1) + { + sd->pty->selection.makesel = EINA_TRUE; + if (ev->flags & EVAS_BUTTON_TRIPLE_CLICK) + { + if (shift && sd->pty->selection.is_active) + _sel_line_to(sd, cy - sd->scroll, EINA_TRUE); + else + _sel_line(sd, cy - sd->scroll); + if (sd->pty->selection.is_active) + { + termio_take_selection(sd->self, ELM_SEL_TYPE_PRIMARY); + } + sd->didclick = EINA_TRUE; + _sel_fill_in_codepoints_array(sd); + } + else if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK) + { + if (!sd->pty->selection.is_active && sd->didclick) + sd->pty->selection.is_active = EINA_TRUE; + if (shift && sd->pty->selection.is_active) + _sel_word_to(sd, cx, cy - sd->scroll, EINA_TRUE); + else + _sel_word(sd, cx, cy - sd->scroll); + if (sd->pty->selection.is_active) + { + if (!termio_take_selection(sd->self, ELM_SEL_TYPE_PRIMARY)) + termio_sel_set(sd, EINA_FALSE); + } + sd->didclick = EINA_TRUE; + _sel_fill_in_codepoints_array(sd); + } + else + { + _handle_mouse_down_single_click(sd, cx, cy, ctrl, alt, shift); + } + termio_smart_update_queue(sd); + } + else if (ev->button == 2) + { + termio_paste_selection(sd->self, ELM_SEL_TYPE_PRIMARY); + } + else if (ev->button == 3) + { + termio_handle_right_click(ev, sd, cx, cy); + } +} + +static Eina_Bool +_rep_mouse_up(Termio *sd, Evas_Event_Mouse_Up *ev, int cx, int cy) +{ + char buf[64]; + Eina_Bool ret = EINA_FALSE; + int meta; + + if ((sd->pty->mouse_mode == MOUSE_OFF) || + (sd->pty->mouse_mode == MOUSE_X10)) + return EINA_FALSE; + if (sd->mouse.button == ev->button) + sd->mouse.button = 0; + + meta = evas_key_modifier_is_set(ev->modifiers, "Alt") ? 8 : 0; + + switch (sd->pty->mouse_ext) + { + case MOUSE_EXT_NONE: + if ((cx < (0xff - ' ')) && (cy < (0xff - ' '))) + { + buf[0] = 0x1b; + buf[1] = '['; + buf[2] = 'M'; + buf[3] = (3 | meta) + ' '; + buf[4] = cx + 1 + ' '; + buf[5] = cy + 1 + ' '; + buf[6] = 0; + termpty_write(sd->pty, buf, strlen(buf)); + ret = EINA_TRUE; + } + break; + case MOUSE_EXT_UTF8: // ESC.[.M.BTN/FLGS.XUTF8.YUTF8 + { + int v, i; + + buf[0] = 0x1b; + buf[1] = '['; + buf[2] = 'M'; + buf[3] = (3 | meta) + ' '; + i = 4; + v = cx + 1 + ' '; + if (v <= 127) buf[i++] = v; + else + { // 14 bits for cx/cy - enough i think + buf[i++] = 0xc0 + (v >> 6); + buf[i++] = 0x80 + (v & 0x3f); + } + v = cy + 1 + ' '; + if (v <= 127) buf[i++] = v; + else + { // 14 bits for cx/cy - enough i think + buf[i++] = 0xc0 + (v >> 6); + buf[i++] = 0x80 + (v & 0x3f); + } + buf[i] = 0; + termpty_write(sd->pty, buf, strlen(buf)); + ret = EINA_TRUE; + } + break; + case MOUSE_EXT_SGR: // ESC.[.<.NUM.;.NUM.;.NUM.m + { + int btn = ev->button - 1; + if (btn > 2) btn = 0; + snprintf(buf, sizeof(buf), "%c[<%i;%i;%im", 0x1b, + (btn | meta), cx + 1, cy + 1); + termpty_write(sd->pty, buf, strlen(buf)); + ret = EINA_TRUE; + } + break; + case MOUSE_EXT_URXVT: // ESC.[.NUM.;.NUM.;.NUM.M + { + snprintf(buf, sizeof(buf), "%c[%i;%i;%iM", 0x1b, + (3 | meta) + ' ', + cx + 1, cy + 1); + termpty_write(sd->pty, buf, strlen(buf)); + ret = EINA_TRUE; + } + break; + default: + break; + } + return ret; +} + +static Eina_Bool +_rep_mouse_move(Termio *sd, int cx, int cy) +{ + char buf[64]; + Eina_Bool ret = EINA_FALSE; + int btn; + + if ((sd->pty->mouse_mode == MOUSE_OFF) || + (sd->pty->mouse_mode == MOUSE_X10) || + (sd->pty->mouse_mode == MOUSE_NORMAL)) + return EINA_FALSE; + + if ((!sd->mouse.button) && (sd->pty->mouse_mode == MOUSE_NORMAL_BTN_MOVE)) + return EINA_FALSE; + + btn = sd->mouse.button - 1; + + switch (sd->pty->mouse_ext) + { + case MOUSE_EXT_NONE: + if ((cx < (0xff - ' ')) && (cy < (0xff - ' '))) + { + buf[0] = 0x1b; + buf[1] = '['; + buf[2] = 'M'; + buf[3] = btn + 32 + ' '; + buf[4] = cx + 1 + ' '; + buf[5] = cy + 1 + ' '; + buf[6] = 0; + termpty_write(sd->pty, buf, strlen(buf)); + ret = EINA_TRUE; + } + break; + case MOUSE_EXT_UTF8: // ESC.[.M.BTN/FLGS.XUTF8.YUTF8 + { + int v, i; + + buf[0] = 0x1b; + buf[1] = '['; + buf[2] = 'M'; + buf[3] = btn + 32 + ' '; + i = 4; + v = cx + 1 + ' '; + if (v <= 127) buf[i++] = v; + else + { // 14 bits for cx/cy - enough i think + buf[i++] = 0xc0 + (v >> 6); + buf[i++] = 0x80 + (v & 0x3f); + } + v = cy + 1 + ' '; + if (v <= 127) buf[i++] = v; + else + { // 14 bits for cx/cy - enough i think + buf[i++] = 0xc0 + (v >> 6); + buf[i++] = 0x80 + (v & 0x3f); + } + buf[i] = 0; + termpty_write(sd->pty, buf, strlen(buf)); + ret = EINA_TRUE; + } + break; + case MOUSE_EXT_SGR: // ESC.[.<.NUM.;.NUM.;.NUM.M + { + snprintf(buf, sizeof(buf), "%c[<%i;%i;%iM", 0x1b, + btn + 32, cx + 1, cy + 1); + termpty_write(sd->pty, buf, strlen(buf)); + ret = EINA_TRUE; + } + break; + case MOUSE_EXT_URXVT: // ESC.[.NUM.;.NUM.;.NUM.M + { + snprintf(buf, sizeof(buf), "%c[%i;%i;%iM", 0x1b, + btn + 32 + ' ', + cx + 1, cy + 1); + termpty_write(sd->pty, buf, strlen(buf)); + ret = EINA_TRUE; + } + break; + default: + break; + } + return ret; +} + + +void +termio_internal_mouse_up(Termio *sd, + Evas_Event_Mouse_Up *ev) +{ + int shift, ctrl; + int cx = 0, cy = 0; + + termio_cursor_to_xy(sd, ev->canvas.x, ev->canvas.y, &cx, &cy); + + shift = evas_key_modifier_is_set(ev->modifiers, "Shift"); + ctrl = evas_key_modifier_is_set(ev->modifiers, "Control"); + + if (!shift && !ctrl && !sd->pty->selection.makesel) + if (_rep_mouse_up(sd, ev, cx, cy)) + { + if (sd->pty->selection.is_active) + { + termio_sel_set(sd, EINA_FALSE); + termio_smart_update_queue(sd); + } + return; + } + if (sd->link.down.dnd) return; + if (sd->pty->selection.makesel) + { + if (sd->mouse_selection_scroll_timer) + { + ecore_timer_del(sd->mouse_selection_scroll_timer); + sd->mouse_selection_scroll_timer = NULL; + } + sd->pty->selection.makesel = EINA_FALSE; + + if (!sd->pty->selection.is_active) + { + /* Only change the end position */ + if (((sd->pty->selection.start.x == sd->pty->selection.end.x) && + (sd->pty->selection.start.y == sd->pty->selection.end.y))) + { + termio_sel_set(sd, EINA_FALSE); + sd->didclick = EINA_FALSE; + sd->pty->selection.last_click = time(NULL); + sd->pty->selection.by_line = EINA_FALSE; + sd->pty->selection.by_word = EINA_FALSE; + _sel_fill_in_codepoints_array(sd); + termio_smart_update_queue(sd); + return; + } + } + else + { + if (sd->pty->selection.by_line) + { + _sel_line_to(sd, cy - sd->scroll, shift); + } + else if (sd->pty->selection.by_word) + { + _sel_word_to(sd, cx, cy - sd->scroll, shift); + } + else + { + if (shift) + { + /* extend selection */ + _sel_to(sd, cx, cy - sd->scroll, EINA_TRUE); + } + else + { + sd->didclick = EINA_TRUE; + _sel_to(sd, cx, cy - sd->scroll, EINA_FALSE); + } + } + termio_selection_dbl_fix(sd); + _selection_newline_extend_fix(sd->self); + termio_smart_update_queue(sd); + termio_take_selection(sd->self, ELM_SEL_TYPE_PRIMARY); + _sel_fill_in_codepoints_array(sd); + sd->pty->selection.makesel = EINA_FALSE; + } + } +} + +static Eina_Bool +_mouse_selection_scroll(void *data) +{ + Termio *sd = data; + Evas_Coord oy, my; + int cy; + float fcy; + + if (!sd->pty->selection.makesel) return EINA_FALSE; + + evas_pointer_canvas_xy_get(sd->self, NULL, &my); + termio_object_geometry_get(sd, NULL, &oy, NULL, NULL); + fcy = (my - oy) / (float)sd->font.chh; + cy = fcy; + if (fcy < 0.3) + { + if (cy == 0) + cy = -1; + sd->scroll -= cy; + sd->pty->selection.end.y = -sd->scroll; + termio_smart_update_queue(sd); + } + else if (fcy >= (sd->grid.h - 0.3)) + { + if (cy <= sd->grid.h) + cy = sd->grid.h + 1; + sd->scroll -= cy - sd->grid.h; + if (sd->scroll < 0) sd->scroll = 0; + sd->pty->selection.end.y = sd->scroll + sd->grid.h - 1; + termio_smart_update_queue(sd); + } + + return EINA_TRUE; +} + +void +termio_cursor_to_xy(Termio *sd, Evas_Coord x, Evas_Coord y, + int *cx, int *cy) +{ + Evas_Coord ox, oy; + + termio_object_geometry_get(sd, &ox, &oy, NULL, NULL); + *cx = (x - ox) / sd->font.chw; + *cy = (y - oy) / sd->font.chh; + if (*cx < 0) *cx = 0; + else if (*cx >= sd->grid.w) *cx = sd->grid.w - 1; + if (*cy < 0) *cy = 0; + else if (*cy >= sd->grid.h) *cy = sd->grid.h - 1; +} + + +void +termio_internal_mouse_move(Termio *sd, + Evas_Event_Mouse_Move *ev) +{ + int cx, cy; + float fcy; + Evas_Coord ox, oy; + int shift, ctrl; + Eina_Bool scroll = EINA_FALSE; + + termio_object_geometry_get(sd, &ox, &oy, NULL, NULL); + cx = (ev->cur.canvas.x - ox) / sd->font.chw; + fcy = (ev->cur.canvas.y - oy) / (float)sd->font.chh; + + shift = evas_key_modifier_is_set(ev->modifiers, "Shift"); + ctrl = evas_key_modifier_is_set(ev->modifiers, "Control"); + + cy = fcy; + if (cx < 0) cx = 0; + else if (cx >= sd->grid.w) cx = sd->grid.w - 1; + if (fcy < 0.3) + { + cy = 0; + if (sd->pty->selection.makesel) + scroll = EINA_TRUE; + } + else if (fcy >= (sd->grid.h - 0.3)) + { + cy = sd->grid.h - 1; + if (sd->pty->selection.makesel) + scroll = EINA_TRUE; + } + if (scroll == EINA_TRUE) + { + if (!sd->mouse_selection_scroll_timer) + { + sd->mouse_selection_scroll_timer + = ecore_timer_add(0.05, _mouse_selection_scroll, sd); + } + return; + } + else if (sd->mouse_selection_scroll_timer) + { + ecore_timer_del(sd->mouse_selection_scroll_timer); + sd->mouse_selection_scroll_timer = NULL; + } + + if ((sd->mouse.cx == cx) && (sd->mouse.cy == cy)) return; + + sd->mouse.cx = cx; + sd->mouse.cy = cy; + if (!shift && !ctrl) + if (_rep_mouse_move(sd, cx, cy)) return; + if (sd->link.down.dnd) + { + sd->pty->selection.makesel = EINA_FALSE; + termio_sel_set(sd, EINA_FALSE); + termio_smart_update_queue(sd); + return; + } + if (sd->pty->selection.makesel) + { + int start_x, start_y; + + /* Only change the end position */ + start_x = sd->pty->selection.start.x; + start_y = sd->pty->selection.start.y; + + if (!sd->pty->selection.is_active) + { + if ((cx != start_x) || + ((cy - sd->scroll) != start_y)) + termio_sel_set(sd, EINA_TRUE); + } + + if (sd->pty->selection.by_line) + { + _sel_line_to(sd, cy - sd->scroll, EINA_FALSE); + } + else if (sd->pty->selection.by_word) + { + _sel_word_to(sd, cx, cy - sd->scroll, EINA_FALSE); + } + else + { + _sel_to(sd, cx, cy - sd->scroll, EINA_FALSE); + } + + termio_selection_dbl_fix(sd); + if (!sd->pty->selection.is_box) + _selection_newline_extend_fix(sd->self); + termio_smart_update_queue(sd); + sd->moved = EINA_TRUE; + } + /* TODO: make the following useless */ + if (sd->mouse_move_job) + ecore_job_del(sd->mouse_move_job); + sd->mouse_move_job = ecore_job_add(termio_smart_cb_mouse_move_job, sd); +} + +static void +_termio_scroll_selection(Termio *sd, Termpty *ty, + int direction, int start_y, int end_y) +{ + if (!ty->selection.is_active) + return; + + int sel_start_x = ty->selection.start.x; + int sel_start_y = ty->selection.start.y; + int sel_end_x = ty->selection.end.x; + int sel_end_y = ty->selection.end.y; + + int left_margin = ty->termstate.left_margin; + int right_margin = ty->termstate.right_margin; + + if (!ty->selection.is_top_to_bottom) + { + INT_SWAP(sel_start_y, sel_end_y); + INT_SWAP(sel_start_x, sel_end_x); + } + + if (start_y <= sel_start_y && + sel_end_y <= end_y) + { + if (ty->termstate.left_margin) + { + if ((ty->selection.is_box) || (sel_start_y == sel_end_y)) + { + /* if selection outside scrolling area */ + if ((sel_end_x <= left_margin) || + (sel_start_x >= right_margin)) + { + return; + } + /* if selection not within scrolling area */ + if (!((sel_start_x >= left_margin) && + (sel_end_x <= right_margin))) + { + termio_sel_set(sd, EINA_FALSE); + return; + } + } + else + { + termio_sel_set(sd, EINA_FALSE); + return; + } + } + + ty->selection.orig.y += direction; + ty->selection.start.y += direction; + ty->selection.end.y += direction; + sel_start_y += direction; + sel_end_y += direction; + if (!(start_y <= sel_start_y && + sel_end_y <= end_y)) + { + termio_sel_set(sd, EINA_FALSE); + } + } + else if (!((start_y > sel_end_y) || + (end_y < sel_start_y))) + { + if (ty->termstate.left_margin) + { + if ((ty->selection.is_box) || (sel_start_y == sel_end_y)) + { + /* if selection outside scrolling area */ + if ((sel_end_x <= left_margin) || + (sel_start_x >= right_margin)) + { + return; + } + } + } + termio_sel_set(sd, EINA_FALSE); + } + else if (sd->scroll > 0) + { + ty->selection.orig.y += direction; + ty->selection.start.y += direction; + ty->selection.end.y += direction; + } +} + + +void +termio_scroll(Evas_Object *obj, int direction, + int start_y, int end_y) +{ + Termio *sd = evas_object_smart_data_get(obj); + Termpty *ty; + + EINA_SAFETY_ON_NULL_RETURN(sd); + + ty = sd->pty; + + if ((!sd->jump_on_change) && // if NOT scroll to bottom on updates + (sd->scroll > 0)) + { + Evas_Object *mv = term_miniview_get(sd->term); + if (mv) + { + miniview_position_offset(mv, direction, EINA_FALSE); + } + // adjust scroll position for added scrollback + sd->scroll -= direction; + } + + _termio_scroll_selection(sd, ty, direction, start_y, end_y); +} + diff --git a/src/bin/termiointernals.h b/src/bin/termiointernals.h new file mode 100644 index 00000000..4fe899d0 --- /dev/null +++ b/src/bin/termiointernals.h @@ -0,0 +1,109 @@ +#ifndef _TERMIOINTERNALS_H__ +#define _TERMIOINTERNALS_H__ 1 + +typedef struct _Termio Termio; + +struct _Termio +{ + Evas_Object_Smart_Clipped_Data __clipped_data; + struct { + const char *name; + int size; + int chw, chh; + } font; + struct { + int w, h; + Evas_Object *obj; + } grid; + struct { + Evas_Object *top, *bottom, *theme; + } sel; + struct { + Evas_Object *obj; + int x, y; + Cursor_Shape shape; + } cursor; + struct { + int cx, cy; + int button; + } mouse; + struct { + const char *string; + int x1, y1, x2, y2; + int suspend; + uint16_t id; + Eina_List *objs; + struct { + Evas_Object *dndobj; + Evas_Coord x, y; + unsigned char down : 1; + unsigned char dnd : 1; + unsigned char dndobjdel : 1; + } down; + } link; + struct { + const char *file; + FILE *f; + double progress; + unsigned long long total, size; + Eina_Bool active : 1; + } sendfile; + Evas_Object *ctxpopup; + int zoom_fontsize_start; + int scroll; + Evas_Object *self; + Evas_Object *event; + Term *term; + + Termpty *pty; + Ecore_Animator *anim; + Ecore_Timer *delayed_size_timer; + Ecore_Timer *link_do_timer; + Ecore_Timer *mouse_selection_scroll_timer; + Ecore_Job *mouse_move_job; + Ecore_Timer *mouseover_delay; + Evas_Object *win, *theme, *glayer; + Config *config; + const char *sel_str; + Eina_List *cur_chids; + Ecore_Job *sel_reset_job; + double set_sel_at; + Elm_Sel_Type sel_type; + unsigned char jump_on_change : 1; + unsigned char jump_on_keypress : 1; + unsigned char have_sel : 1; + unsigned char noreqsize : 1; + unsigned char didclick : 1; + unsigned char moved : 1; + unsigned char bottom_right : 1; + unsigned char top_left : 1; + unsigned char reset_sel : 1; + unsigned char cb_added : 1; + double gesture_zoom_start_size; +}; + +#define INT_SWAP(_a, _b) do { \ + int _swap = _a; _a = _b; _b = _swap; \ +} while (0) + + +void +termio_internal_mouse_down(Termio *sd, Evas_Event_Mouse_Down *ev); +void +termio_internal_mouse_up(Termio *sd, Evas_Event_Mouse_Up *ev); +void +termio_internal_mouse_move(Termio *sd, Evas_Event_Mouse_Move *ev); + +void +termio_selection_dbl_fix(Termio *sd); +void +termio_selection_get(Termio *sd, + int c1x, int c1y, int c2x, int c2y, + struct ty_sb *sb, + Eina_Bool rtrim); +void +termio_scroll(Evas_Object *obj, int direction, int start_y, int end_y); +void +termio_cursor_to_xy(Termio *sd, Evas_Coord x, Evas_Coord y, + int *cx, int *cy); +#endif