diff options
author | pgen <p.gen.progs@gmail.com> | 2022-08-24 00:39:29 +0200 |
---|---|---|
committer | pgen <p.gen.progs@gmail.com> | 2022-08-31 22:51:16 +0200 |
commit | 0d009e8304793c50a6784111c2ea77b0087cff21 (patch) | |
tree | 8cac73f421f20d6b79ee6bf898cf0c1b2b644b5c /smenu.c | |
parent | 637fc27e86627239a5b5197a5e6669624443fab9 (diff) |
Add mouse support
- Direct selection of words by clicking.
- Tagging/untagging of words by clicking.
- Scrolling by clicks and wheel.
- Validation of the selection with a double-click.
- Shifting the content of the windows to the left or to the right,
also added new keyboard keys < and > for that.
Three mouse tracking protocols are supported:
X11 (1000) and X11 SGR (1006) and urxvt (1015).
The X11 protocol limits lines and columns to 223 (e.g. screen < 4.7.0)
The other supported protocols don't have this limitation.
New options to disable the mouse, change the double-click delay and remap
the buttons of the mouse are available.
On (virtualised?) (Free)BSD it may be necessary to disable buttons
8 and 9, refer to the manual for details.
Diffstat (limited to 'smenu.c')
-rw-r--r-- | smenu.c | 868 |
1 files changed, 809 insertions, 59 deletions
@@ -63,6 +63,7 @@ long * line_nb_of_word_a; /* array containing the line number (from 0) * | of each word read. */ long * first_word_in_line_a; /* array containing the index of the first * | word of each lines. */ +long * shift_right_sym_pos_a; int forgotten_timer = -1; int help_timer = -1; @@ -98,6 +99,11 @@ char * sbar_arr_down = "\xe2\x96\xbc"; /* ▼ black_down_pointing_triangle. * char * msg_arr_down = "\xe2\x96\xbc"; /* ▼ black_down_pointing_triangle. */ /* clang-format on */ +/* Mouse tracking. */ +/* """"""""""""""" */ +char * mouse_trk_on; +char * mouse_trk_off; + /* Variables used to manage the direct access entries. */ /* """"""""""""""""""""""""""""""""""""""""""""""""""" */ daccess_t daccess; @@ -467,7 +473,8 @@ apply_attr(term_t * term, attrib_t attr) /* ===================================================== */ int ini_cb(win_t * win, term_t * term, limit_t * limits, ticker_t * timers, - misc_t * misc, const char * section, const char * name, char * value) + misc_t * misc, mouse_t * mouse, const char * section, const char * name, + char * value) { int error = 0; int has_colors = term->colors > 7; @@ -685,6 +692,20 @@ ini_cb(win_t * win, term_t * term, limit_t * limits, ticker_t * timers, timers->search = v; } } + else if (strcmp(section, "mouse") == 0) + { + int v; + + /* [mouse] section. */ + /* """"""""""""""" */ + if (strcmp(name, "double_click_delay") == 0) + { + if ((error = !(sscanf(value, "%d", &v) == 1 && v > 0))) + goto out; + else + mouse->double_click_delay = v; + } + } else if (strcmp(section, "misc") == 0) { /* [misc] section. */ @@ -720,10 +741,10 @@ out: /* ======================================================================== */ int ini_load(const char * filename, win_t * win, term_t * term, limit_t * limits, - ticker_t * timers, misc_t * misc, + ticker_t * timers, misc_t * misc, mouse_t * mouse, int (*report)(win_t * win, term_t * term, limit_t * limits, - ticker_t * timers, misc_t * misc, const char * section, - const char * name, char * value)) + ticker_t * timers, misc_t * misc, mouse_t * mouse, + const char * section, const char * name, char * value)) { char name[64] = ""; char value[256] = ""; @@ -775,7 +796,8 @@ ini_load(const char * filename, win_t * win, term_t * term, limit_t * limits, /* Callback function calling. */ /* """""""""""""""""""""""""" */ - error = report(win, term, limits, timers, misc, section, name, value); + error = report(win, term, limits, timers, misc, mouse, section, name, + value); if (error) goto out; @@ -2396,6 +2418,10 @@ get_scancode(unsigned char * s, size_t max) size_t i = 1; struct termios original_ts, nowait_ts; + /* Flush non-transmitted, non-read input data. */ + /* """"""""""""""""""""""""""""""""""""""""""" */ + tcflush(0, TCIFLUSH); + if ((c = my_fgetc(stdin)) == EOF) return 0; @@ -3726,6 +3752,7 @@ disp_lines(win_t * win, toggle_t * toggles, long current, long count, char scroll_symbol[5]; long len; long display_bar; + long first_start; sigset_t mask; @@ -3740,7 +3767,18 @@ disp_lines(win_t * win, toggle_t * toggles, long current, long count, scroll_symbol[0] = ' '; scroll_symbol[1] = '\0'; - lines_disp = 1; + lines_disp = 1; + first_start = -1; + + /* Initialize the truncates lines flag. */ + /* """""""""""""""""""""""""""""""""""" */ + win->has_truncated_lines = 0; + + /* Initialize the selectable column guard to its maximum position. */ + /* for the first window line. */ + /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */ + shift_right_sym_pos_a[line_nb_of_word_a[win->start] + lines_disp - 1] = + term->ncolumns; (void)tputs(TPARM1(save_cursor), 1, outch); @@ -3777,6 +3815,9 @@ disp_lines(win_t * win, toggle_t * toggles, long current, long count, strcpy(scroll_symbol, shift_left_sym); else strcpy(scroll_symbol, "<"); + + if (!win->has_truncated_lines) + win->has_truncated_lines = 1; } } else @@ -3799,6 +3840,9 @@ disp_lines(win_t * win, toggle_t * toggles, long current, long count, if (word_a[i].start >= win->first_column && word_a[i].end < len + win->first_column) { + if (first_start < 0) + first_start = word_a[i].start; + disp_word(i, search_mode, search_data, term, win, tmp_word); /* If there are more element to be displayed after the right margin. */ @@ -3806,6 +3850,9 @@ disp_lines(win_t * win, toggle_t * toggles, long current, long count, if ((win->col_mode || win->line_mode) && i < count - 1 && word_a[i + 1].end >= len + win->first_column) { + if (!win->has_truncated_lines) + win->has_truncated_lines = 1; + apply_attr(term, win->shift_attr); if (langinfo->utf8) @@ -3814,6 +3861,12 @@ disp_lines(win_t * win, toggle_t * toggles, long current, long count, fputc_safe('>', stdout); (void)tputs(TPARM1(exit_attribute_mode), 1, outch); + + /* Adjust the selectable column guard to the column just after */ + /* the last displayed word. */ + /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */ + shift_right_sym_pos_a[line_nb_of_word_a[i]] = word_a[i].end + 1 + - first_start + 2; } /* If we want to display the gutter. */ @@ -3856,6 +3909,10 @@ disp_lines(win_t * win, toggle_t * toggles, long current, long count, if (display_bar && !toggles->no_scrollbar && (lines_disp > 1 || i < count - 1)) { + /* Store the scroll bar column position. */ + /* """"""""""""""""""""""""""""""""""""" */ + win->sb_column = win->offset + win->max_width + 1; + /* Display the next element of the scrollbar. */ /* """""""""""""""""""""""""""""""""""""""""" */ if (line_nb_of_word_a[i] == 0) @@ -3912,7 +3969,16 @@ disp_lines(win_t * win, toggle_t * toggles, long current, long count, /* a premature end of input. */ /* """""""""""""""""""""""""""""""""""""""""""""""""" */ if (i < count - 1) + { lines_disp++; + first_start = -1; + + /* Initialize the selectable column guard to its maximum position. */ + /* for the next window line. */ + /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */ + shift_right_sym_pos_a[line_nb_of_word_a[win->start] + lines_disp + - 1] = term->ncolumns; + } if (win->max_lines == 1) break; @@ -4376,6 +4442,52 @@ move_left(win_t * win, term_t * term, toggle_t * toggles, term, last_line, tmp_word, langinfo); } +/* ============================= */ +/* Shift the window to the left. */ +/* ============================= */ +void +shift_left(win_t * win, term_t * term, toggle_t * toggles, + search_data_t * search_data, langinfo_t * langinfo, long * nl, + long last_line, char * tmp_word, long line) +{ + long pos; + long len; + long start; + + /* No lines to shift if not in lire or column mode. */ + /* """""""""""""""""""""""""""""""""""""""""""""""" */ + if (!win->col_mode && !win->line_mode) + return; + + /* Do nothing if we already are on the left side of if the line */ + /* is already fully displayed. */ + /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */ + if (win->first_column == 0) + return; + + /* Find the first word to be displayed in this line. */ + /* """"""""""""""""""""""""""""""""""""""""""""""""" */ + pos = first_word_in_line_a[line]; + + while (pos < count - 1 && word_a[pos].start < win->first_column + && word_a[pos + 1].start > 0) + pos++; + + if (word_a[pos].start >= win->first_column) + pos--; + + /* Make sure the word under the cursor remains visible. */ + /* """""""""""""""""""""""""""""""""""""""""""""""""""" */ + len = term->ncolumns - 3; + if (word_a[current].end > len + word_a[pos].start) + return; + + win->first_column = word_a[pos].start; + + *nl = disp_lines(win, toggles, current, count, search_mode, search_data, term, + last_line, tmp_word, langinfo); +} + /* ======================= */ /* Moves the cursor right. */ /* ======================= */ @@ -4475,6 +4587,68 @@ move_right(win_t * win, term_t * term, toggle_t * toggles, term, last_line, tmp_word, langinfo); } +/* ============================== */ +/* Shift the window to the right. */ +/* ============================== */ +void +shift_right(win_t * win, term_t * term, toggle_t * toggles, + search_data_t * search_data, langinfo_t * langinfo, long * nl, + long last_line, char * tmp_word, long line) +{ + long pos; + long len; + + /* No lines to shift if not in lire or column mode. */ + /* """""""""""""""""""""""""""""""""""""""""""""""" */ + if (!win->col_mode && !win->line_mode) + return; + + /* Do nothing if we already are on the right side and all or lines */ + /* are already fully displayed. */ + /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */ + if (shift_right_sym_pos_a[line] == term->ncolumns) + { + int found = 0; + + /* Search for at least one line not fully displayed. */ + /* ''''''''''''''''''''''''''''''''''''''''''''''''' */ + for (long l = line_nb_of_word_a[win->start]; + l < line_nb_of_word_a[win->start] + win->max_lines; l++) + { + if (shift_right_sym_pos_a[l] == term->ncolumns) /* fully displayed. */ + continue; + + found = 1; /* Use this line instead of line to calculate the shifting. */ + + break; + } + + /* Do nothing if all lines are fully displayed. */ + /* '''''''''''''''''''''''''''''''''''''''''''' */ + if (!found) + return; + } + + /* Find the first word to be displayed in this line. */ + /* """"""""""""""""""""""""""""""""""""""""""""""""" */ + pos = first_word_in_line_a[line]; + + while (pos < count - 1 && word_a[pos].start < win->first_column + && word_a[pos + 1].start > 0) + pos++; + + if (pos < count - 1 && word_a[pos + 1].start > 0) + pos++; + + if (word_a[pos].start <= word_a[current].start) + win->first_column = word_a[pos].start; + else + return; + + *nl = disp_lines(win, toggles, current, count, search_mode, search_data, term, + last_line, tmp_word, langinfo); +} + /* ================================================================== */ /* Get the last word of a line after it has been formed to fit in the */ /* terminal. */ @@ -4977,7 +5151,7 @@ move_down(win_t * win, term_t * term, toggle_t * toggles, void init_main_ds(attrib_t * init_attr, win_t * win, limit_t * limits, ticker_t * timers, toggle_t * toggles, misc_t * misc, - timeout_t * timeout, daccess_t * daccess) + mouse_t * mouse, timeout_t * timeout, daccess_t * daccess) { int i; @@ -5009,6 +5183,7 @@ init_main_ds(attrib_t * init_attr, win_t * win, limit_t * limits, win->line_mode = 0; win->first_column = 0; win->real_max_width = 0; + win->sb_column = -1; win->cursor_attr = *init_attr; win->cursor_on_tag_attr = *init_attr; @@ -5060,12 +5235,21 @@ init_main_ds(attrib_t * init_attr, win_t * win, limit_t * limits, toggles->pinable = 0; toggles->visual_bell = 0; toggles->incremental_search = 0; + toggles->no_mouse = 0; /* Misc default values. */ /* """""""""""""""""""" */ misc->default_search_method = NONE; misc->ignore_quotes = 0; + /* Mouse values. */ + /* """"""""""""" */ + mouse->button[0] = 1; + mouse->button[1] = 0; + mouse->button[2] = 3; + + mouse->double_click_delay = 150; + /* Set the default timeout to 0 (no expiration). */ /* """"""""""""""""""""""""""""""""""""""""""""" */ timeout->initial_value = 0; @@ -5305,6 +5489,8 @@ toggle_action(char * ctx_name, char * opt_name, char * param, int nb_values, toggles->noautotag = 1; else if (strcmp(opt_name, "incremental_search") == 0) toggles->incremental_search = 1; + else if (strcmp(opt_name, "no_mouse") == 0) + toggles->no_mouse = 1; } void @@ -6238,6 +6424,67 @@ forgotten_action(char * ctx_name, char * opt_name, char * param, int nb_values, free(p); } +void +button_action(char * ctx_name, char * opt_name, char * param, int nb_values, + char ** values, int nb_opt_data, void ** opt_data, + int nb_ctx_data, void ** ctx_data) +{ + mouse_t * mouse = opt_data[0]; + + char * endptr; + char * p; + long val; + + if (nb_values != 2) + { + fprintf(stderr, "Remapping of buttons 1 and 3 expected.\n"); + ctxopt_ctx_disp_usage(ctx_name, exit_after); + } + + int index[2] = { 0, 2 }; + int ind; + + mouse->button[0] = 0; + mouse->button[1] = 0; + mouse->button[2] = 0; + + for (int i = 0; i < nb_values; i++) + { + ind = index[i]; + errno = 0; + p = values[i]; + val = strtol(p, &endptr, 0); + if (errno == ERANGE || endptr == p || *endptr != '\0' || val < 1 || val > 3) + { + fprintf(stderr, "%s: Invalid button number.\n", values[0]); + ctxopt_ctx_disp_usage(ctx_name, exit_after); + } + mouse->button[(int)val - 1] = ind + 1; + } +} + +void +double_click_action(char * ctx_name, char * opt_name, char * param, + int nb_values, char ** values, int nb_opt_data, + void ** opt_data, int nb_ctx_data, void ** ctx_data) +{ + mouse_t * mouse = opt_data[0]; + int * ddc = opt_data[1]; + + char * endptr; + char * p = values[0]; + long val; + + errno = 0; + val = strtol(p, &endptr, 0); + if (errno == ERANGE || endptr == p || *endptr != '\0') + return; /* default value (150 ~ 1/6.66th second) is set in main(). */ + else if (mouse->double_click_delay == 0) + *ddc = 1; /* disable double_click; */ + else if (mouse->double_click_delay >= 100 || mouse->double_click_delay <= 500) + mouse->double_click_delay = val; +} + /* =================================================================== */ /* Cancels a search. Called when ESC is hit or a new search session is */ /* initiated and the incremental_search option is not used. */ @@ -6303,6 +6550,150 @@ is_in_foreground_process_group(void) return fg; } +/* ===================================================================== */ +/* Get the new index of a word based on the mouse click position. */ +/* Returns a new index if a word could be selected or the original index */ +/* The error flag is set if the word was not selectable or if the user */ +/* did not click on a word. */ +/* ===================================================================== */ +long +get_clicked_index(win_t * win, term_t * term, int line_click, int column_click, + int * error) +{ + long new_current; /* Future new current word on success. */ + long clicked_line; /* Number of the clicked line in smenu internal * + | representation on the screen. */ + int delta; /* Compensation due to the alignment of the first * + | visible word of the selected line in the window. */ + + /* Make sure the error indicator is initialized. */ + /* """"""""""""""""""""""""""""""""""""""""""""" */ + *error = 0; + + /* Get the internal line number of the click. */ + /* """""""""""""""""""""""""""""""""""""""""" */ + clicked_line = line_nb_of_word_a[win->start] + line_click - term->curs_line; + + /* Ignore clicks after the last word on a line. */ + /* """""""""""""""""""""""""""""""""""""""""""" */ + if (column_click >= shift_right_sym_pos_a[clicked_line]) + return current; + + new_current = first_word_in_line_a[clicked_line]; + + if (new_current == count - 1) + return new_current; /* The users clicked on the last word; */ + else + { + int offset; /* in column or line mode lines can be larger than the * + | terminal size, in this case a space is added at the * + | start of the lines to possibly put the shift arrow * + | indicator, this space must be taken into account. */ + int found; /* Flag to indicate if a word could have been clicked. */ + + /* Take into account the presence of shift arrows if any. */ + /* """""""""""""""""""""""""""""""""""""""""""""""""""""" */ + if (win->has_truncated_lines) + offset = 1; + else + offset = 0; + + /* Compensates for the offset of the window in the case of a centred */ + /* window and the the additional space indtuduced by the display of */ + /* trucated lines. */ + /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */ + column_click -= win->offset; + + /* Forbid click on column 1 if the windows has truncated lines. */ + /* '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' */ + if (column_click == 1 && offset == 1) + return current; + + /* Normalize column_click. */ + /* """"""""""""""""""""""" */ + column_click -= offset; + + /* Determine the offset of the first character of the */ + /* first visible word in the clicked line. */ + /* """""""""""""""""""""""""""""""""""""""""""""""""" */ + while (word_a[new_current].start < win->first_column) + { + new_current++; + + if (new_current == count || word_a[new_current].start == 0) + return current; + } + + delta = word_a[new_current].start - win->first_column; + + /* Find the right clicked word if any. */ + /* """"""""""""""""""""""""""""""""""" */ + found = 0; + + while (1) + { + if ((column_click - 1 + >= word_a[new_current].start - win->first_column - delta) + && (column_click - 1 + <= word_a[new_current].end - win->first_column - delta)) + { + found = 1; + break; + } + + new_current++; + + if (new_current == count || word_a[new_current].start == 0) + break; + } + + if (!found) + { + *error = 1; + new_current = current; + } + } + + if (!word_a[new_current].is_selectable) + { + *error = 1; + return current; + } + + return new_current; +} + +/* ==================================================================== */ +/* Get the number of the clicked shift arrow if any. */ +/* arrow will contain 0 if the left arrow as clicked and 1 if the right */ +/* arrow was clicked. */ +/* Returns 0 is no arrow wa clicked else 1. */ +/* ==================================================================== */ +int +shift_arrow_clicked(win_t * win, term_t * term, int line_click, + int column_click, long * clicked_line, int * arrow) +{ + /* Get the internal line number of the click. */ + /* """""""""""""""""""""""""""""""""""""""""" */ + *clicked_line = line_nb_of_word_a[win->start] + line_click - term->curs_line; + + if (column_click > shift_right_sym_pos_a[*clicked_line]) + return 0; + + /* Compensates for the offset of the window */ + /* in the case of a centred window. */ + /* """""""""""""""""""""""""""""""""""""""" */ + if (column_click == 1) + *arrow = 0; /* Left arrow. */ + else if (shift_right_sym_pos_a[*clicked_line] < term->ncolumns + && column_click == shift_right_sym_pos_a[*clicked_line]) + *arrow = 1; /* Right arrow. */ + else + return 0; + + return 1; +} + /* ================= */ /* Main entry point. */ /* ================= */ @@ -6575,6 +6966,7 @@ main(int argc, char * argv[]) limit_t limits; /* set of various limitations. */ ticker_t timers; /* timers contents. */ misc_t misc; /* misc contents. */ + mouse_t mouse; /* mouse default values. */ toggle_t toggles; /* set of binary indicators. */ int old_fd0; /* backups of the old stdin file descriptor. */ @@ -6615,7 +7007,7 @@ main(int argc, char * argv[]) char * pre_selection_index = NULL; /* pattern used to set the initial * | cursor position. */ - unsigned char buffer[16]; /* Input buffer. */ + unsigned char buffer[32]; /* Input buffer. */ search_data_t search_data; search_data.buf = NULL; /* Search buffer */ @@ -6678,10 +7070,12 @@ main(int argc, char * argv[]) int row; /* absolute line position in terminal (1...) */ int col; /* absolute column position in terminal (1...) */ + int mouse_proto = -1; + /* Initialize some internal data structures. */ /* """"""""""""""""""""""""""""""""""""""""" */ - init_main_ds(&init_attr, &win, &limits, &timers, &toggles, &misc, &timeout, - &daccess); + init_main_ds(&init_attr, &win, &limits, &timers, &toggles, &misc, &mouse, + &timeout, &daccess); /* direct access variable initialization. */ /* """""""""""""""""""""""""""""""""""""" */ @@ -6714,6 +7108,12 @@ main(int argc, char * argv[]) /* """"""""""""""""""""""""""""""""""""" */ long tagged_words = 0; + /* Double-click related variables. */ + /* """"""""""""""""""""""""""""""" */ + int disable_double_click = 0; + int click_nr = 0; + struct timespec last_click_ts; + /* Get the current locale. */ /* """"""""""""""""""""""" */ setlocale(LC_ALL, ""); @@ -6821,10 +7221,12 @@ main(int argc, char * argv[]) /* Set the attributes from the configuration file if possible. */ /* """"""""""""""""""""""""""""""""""""""""""""""""""""""""""" */ - if (ini_load(home_ini_file, &win, &term, &limits, &timers, &misc, ini_cb)) + if (ini_load(home_ini_file, &win, &term, &limits, &timers, &misc, &mouse, + ini_cb)) exit(EXIT_FAILURE); - if (ini_load(local_ini_file, &win, &term, &limits, &timers, &misc, ini_cb)) + if (ini_load(local_ini_file, &win, &term, &limits, &timers, &misc, &mouse, + ini_cb)) exit(EXIT_FAILURE); free(home_ini_file); @@ -6836,46 +7238,48 @@ main(int argc, char * argv[]) "allow_abbreviations=No " "display_usage_on_error=Yes "); - common_options = - "[*help] " - "[*usage] " - "[include_re... #regex] " - "[exclude_re... #regex] " - "[title #message] " - "[int [#string]] " - "[attributes #prefix:attr...] " - "[special_level_1 #...<3] " - "[special_level_2 #...<3] " - "[special_level_3 #...<3] " - "[special_level_4 #...<3] " - "[special_level_5 #...<3] " - "[special_level_6 #...<3] " - "[special_level_7 #...<3] " - "[special_level_8 #...<3] " - "[special_level_9 #...<3] " - "[zapped_glyphs #bytes] " - "[lines [#height]] " - "[blank_nonprintable] " - "[*invalid_character #invalid_char_subst] " - "[center_mode] " - "[clean] " - "[keep_spaces] " - "[word_separators #bytes] " - "[no_scroll_bar] " - "[early_subst_all... #/regex/repl/opts] " - "[post_subst_all... #/regex/repl/opts] " - "[post_subst_included... #/regex/repl/opts] " - "[post_subst_excluded... #/regex/repl/opts] " - "[search_method #prefix|substring|fuzzy] " - "[start_pattern #pattern] " - "[timeout #...] " - "[hidden_timeout #...] " - "[validate_in_search_mode] " - "[visual_bell] " - "[ignore_quotes] " - "[incremental_search] " - "[limits... #limit:value...] " - "[forgotten_timeout #timeout] "; /* don't remove this space! */ + common_options = "[*help] " + "[*usage] " + "[include_re... #regex] " + "[exclude_re... #regex] " + "[title #message] " + "[int [#string]] " + "[attributes #prefix:attr...] " + "[special_level_1 #...<3] " + "[special_level_2 #...<3] " + "[special_level_3 #...<3] " + "[special_level_4 #...<3] " + "[special_level_5 #...<3] " + "[special_level_6 #...<3] " + "[special_level_7 #...<3] " + "[special_level_8 #...<3] " + "[special_level_9 #...<3] " + "[zapped_glyphs #bytes] " + "[lines [#height]] " + "[blank_nonprintable] " + "[*invalid_character #invalid_char_subst] " + "[center_mode] " + "[clean] " + "[keep_spaces] " + "[word_separators #bytes] " + "[no_scroll_bar] " + "[early_subst_all... #/regex/repl/opts] " + "[post_subst_all... #/regex/repl/opts] " + "[post_subst_included... #/regex/repl/opts] " + "[post_subst_excluded... #/regex/repl/opts] " + "[search_method #prefix|substring|fuzzy] " + "[start_pattern #pattern] " + "[timeout #...] " + "[hidden_timeout #...] " + "[validate_in_search_mode] " + "[visual_bell] " + "[ignore_quotes] " + "[incremental_search] " + "[limits... #limit:value...] " + "[forgotten_timeout #timeout] " + "[double_click_delay #delay] " + "[button_remapping #mapping...] " + "[no_mouse] "; /* don't remove this space! */ main_spec_options = "[*copyright] " "[*version] " @@ -7035,6 +7439,11 @@ main(int argc, char * argv[]) ctxopt_add_opt_settings(parameters, "limits", "-lim -limits"); ctxopt_add_opt_settings(parameters, "forgotten_timeout", "-f -forgotten_timeout -global_timeout"); + ctxopt_add_opt_settings(parameters, "no_mouse", "-nm -no_mouse"); + ctxopt_add_opt_settings(parameters, "button_remapping", + "-br -buttons -button_remapping"); + ctxopt_add_opt_settings(parameters, "double_click_delay", + "-dc -dcd -double_click -double_click_delay"); /* ctxopt options incompatibilities. */ /* """"""""""""""""""""""""""""""""" */ @@ -7044,6 +7453,10 @@ main(int argc, char * argv[]) ctxopt_add_ctx_settings(incompatibilities, "Main", "tag_mode pin_mode"); ctxopt_add_ctx_settings(incompatibilities, "Main", "help usage"); ctxopt_add_ctx_settings(incompatibilities, "Main", "timeout hidden_timeout"); + ctxopt_add_ctx_settings(incompatibilities, "Main", + "no_mouse button_remapping"); + ctxopt_add_ctx_settings(incompatibilities, "Main", + "no_mouse double_click_delay"); /* ctxopt options requirements. */ /* """""""""""""""""""""""""""" */ @@ -7186,6 +7599,12 @@ main(int argc, char * argv[]) ctxopt_add_opt_settings(actions, "limits", limits_action, &limits, (char *)0); ctxopt_add_opt_settings(actions, "forgotten_timeout", forgotten_action, &timers, (char *)0); + ctxopt_add_opt_settings(actions, "button_remapping", button_action, &mouse, + (char *)0); + ctxopt_add_opt_settings(actions, "double_click_delay", double_click_action, + &mouse, &disable_double_click, (char *)0); + ctxopt_add_opt_settings(actions, "no_mouse", toggle_action, &toggles, + (char *)0); /* ctxopt constraints. */ /* """"""""""""""""""" */ @@ -7311,6 +7730,8 @@ main(int argc, char * argv[]) term.has_invis = (str == (char *)-1 || str == NULL) ? 0 : 1; str = tigetstr("blink"); term.has_blink = (str == (char *)-1 || str == NULL) ? 0 : 1; + str = tigetstr("kmous"); + term.has_kmous = (str == (char *)-1 || str == NULL) ? 0 : 1; } if (!term.has_cursor_up || !term.has_cursor_down || !term.has_cursor_left @@ -8866,7 +9287,7 @@ main(int argc, char * argv[]) } else if (daccess.length > 0) { - /* make sure that the prefix of empty word is blank */ + /* Make sure that the prefix of empty word is blank */ /* as they may be display in column mode. */ /* """""""""""""""""""""""""""""""""""""""""""""""" */ for (i = 0; i < daccess.flength; i++) @@ -9169,8 +9590,9 @@ main(int argc, char * argv[]) /* Allocate the space for the satellites arrays. */ /* """"""""""""""""""""""""""""""""""""""""""""" */ - line_nb_of_word_a = xmalloc(count * sizeof(long)); - first_word_in_line_a = xmalloc(count * sizeof(long)); + line_nb_of_word_a = xmalloc(count * sizeof(long)); + first_word_in_line_a = xmalloc(count * sizeof(long)); + shift_right_sym_pos_a = xmalloc(count * sizeof(long)); /* Fourth pass: */ /* When in column or tabulating mode, we need to adjust the length of */ @@ -9324,6 +9746,12 @@ main(int argc, char * argv[]) /* """""""""""""""""""""""""""""""""""" */ last_line = build_metadata(&term, count, &win); + /* Adjust the max number of lines in the windows */ + /* if it has not be explicitly set. */ + /* """"""""""""""""""""""""""""""""""""""""""""" */ + if (line_nb_of_word_a[count - 1] < win.max_lines) + win.max_lines = win.asked_max_lines = line_nb_of_word_a[count - 1] + 1; + /* Index of the selected element in the array words */ /* The string can be: */ /* "last" The string "last" put the cursor on the last word */ @@ -9608,6 +10036,20 @@ main(int argc, char * argv[]) (void)tputs(TPARM1(cursor_up), 1, outch); } + /* Enable the reporting of the mouse events. */ + /* """"""""""""""""""""""""""""""""""""""""" */ + if (!toggles.no_mouse) + { + if (term.has_kmous && (strncmp(tigetstr("kmous"), "\E[<", 4) == 0)) + mouse_trk_on = "\e[?1000;1006h"; + else + mouse_trk_on = "\e[?1000;1006;1015h"; + + mouse_trk_off = "\e[?1000;1006;1015l"; + + printf("%s", mouse_trk_on); + } + /* Save again the cursor current line and column positions so that we */ /* will be able to put the terminal cursor back here. */ /* """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" */ @@ -9933,7 +10375,7 @@ main(int argc, char * argv[]) page = 1; /* Default number of lines to do down/up * | with PgDn/PgUp. */ - sc = get_scancode(buffer, 15); + sc = get_scancode(buffer, 31); if (sc && winch_timer < 0) /* Do not allow input when a window * | refresh is scheduled. */ @@ -10156,6 +10598,46 @@ main(int argc, char * argv[]) /* """"""""""""""""""""""""""""" */ goto ksol; + if (!toggles.no_mouse) + { + double res = 5000; /* 5 s, arbitrary value. */ + struct timespec res_ts; + + /* The minimal resolution for double-click must be 1/10 s. */ + /* """"""""""""""""""""""""""""""""""""""""""""""""""""""" */ + if (clock_getres(CLOCK_MONOTONIC, &res_ts) == -1) + disable_double_click = 1; + else + res = 1000.0 * res_ts.tv_sec + 1e-6 * res_ts.tv_nsec; + + if (res > 100) + disable_double_click = 1; + + /* Detect the mouse protocol. */ + /* """""""""""""""""""""""""" */ + if (memcmp("\x1b[<", buffer, 3) == 0) + { + mouse_proto = MOUSE1006; + goto kmouse; + } + + if (memcmp("\x1b[M", buffer, 3) == 0) + { + mouse_proto = MOUSE1000; + goto kmouse; + } + + if (memcmp("\x1b[32;", buffer, 5) == 0 + || memcmp("\x1b[33;", buffer, 5) == 0 + || memcmp("\x1b[34;", buffer, 5) == 0 + || memcmp("\x1b[96;", buffer, 5) == 0 + || memcmp("\x1b[97;", buffer, 5) == 0) + { + mouse_proto = MOUSE1015; + goto kmouse; + } + } + if (buffer[0] == 0x1b && buffer[1] == '\0') { /* ESC key has been pressed. */ @@ -10174,8 +10656,9 @@ main(int argc, char * argv[]) case 'q': case 'Q': case 3: /* ^C */ - /* q or Q of ^C has been pressed. */ - /* """""""""""""""""""""""""""""" */ + /* q or Q of ^C has been pressed. */ + /* """""""""""""""""""""""""""""" */ + if (search_mode != NONE && buffer[0] != 3) goto special_cmds_when_searching; @@ -10207,11 +10690,16 @@ main(int argc, char * argv[]) } } + /* Disable the reporting of the mouse events. */ + /* """""""""""""""""""""""""""""""""""""""""" */ + if (!toggles.no_mouse) + printf("%s", mouse_trk_off); + /* Restore the visibility of the cursor. */ /* """"""""""""""""""""""""""""""""""""" */ (void)tputs(TPARM1(cursor_normal), 1, outch); - if (buffer[0] == 3) + if (buffer[0] == 3) /* ^C */ { if (int_string != NULL) fprintf(old_stdout, "%s", int_string); @@ -10622,6 +11110,11 @@ main(int argc, char * argv[]) exit: + /* Disable mouse reporting. */ + /* '''''''''''''''''''''''' */ + if (!toggles.no_mouse) + printf("%s", mouse_trk_off); + /* Restore the visibility of the cursor. */ /* """"""""""""""""""""""""""""""""""""" */ (void)tputs(TPARM1(cursor_normal), 1, outch); @@ -10680,6 +11173,17 @@ main(int argc, char * argv[]) break; + /* shift the window to the left if possible. */ + /* """"""""""""""""""""""""""""""""""""""""" */ + case '<': + if (search_mode == NONE) + shift_left(&win, &term, &toggles, &search_data, &langinfo, &nl, + last_line, tmp_word, line_nb_of_word_a[current]); + else + goto special_cmds_when_searching; + + break; + keol: /* Go to the end of the line. */ /* """""""""""""""""""""""""" */ @@ -10727,6 +11231,17 @@ main(int argc, char * argv[]) break; + /* shift the window to the left if possible. */ + /* """"""""""""""""""""""""""""""""""""""""" */ + case '>': + if (search_mode == NONE) + shift_right(&win, &term, &toggles, &search_data, &langinfo, &nl, + last_line, tmp_word, line_nb_of_word_a[current]); + else + goto special_cmds_when_searching; + + break; + case 0x0b: /* ^K key has been pressed. */ /* """""""""""""""""""""""" */ @@ -11285,6 +11800,241 @@ main(int argc, char * argv[]) goto special_cmds_when_searching; break; + kmouse: + { |