initial vimode implementation

This commit is contained in:
Piotr Kocia 2025-03-20 17:25:37 +01:00
parent be19ca2b20
commit e2c4ea3535
17 changed files with 1579 additions and 318 deletions

213
config.c
View file

@ -14,6 +14,8 @@
#include <sys/stat.h>
#include <linux/input-event-codes.h>
#include <xkbcommon/xkbcommon-keysyms.h>
#include <xkbcommon/xkbcommon-names.h>
#include <xkbcommon/xkbcommon.h>
#include <fontconfig/fontconfig.h>
@ -120,7 +122,8 @@ static const char *const binding_action_map[] = {
[BIND_ACTION_CLIPBOARD_COPY] = "clipboard-copy",
[BIND_ACTION_CLIPBOARD_PASTE] = "clipboard-paste",
[BIND_ACTION_PRIMARY_PASTE] = "primary-paste",
[BIND_ACTION_SEARCH_START] = "search-start",
[BIND_ACTION_START_VIMODE] = "start-vimode",
[BIND_ACTION_START_VIMODE_SEARCH] = "start-vimode-search",
[BIND_ACTION_FONT_SIZE_UP] = "font-increase",
[BIND_ACTION_FONT_SIZE_DOWN] = "font-decrease",
[BIND_ACTION_FONT_SIZE_RESET] = "font-reset",
@ -159,43 +162,37 @@ static const char *const binding_action_map[] = {
[BIND_ACTION_SELECT_ROW] = "select-row",
};
static const char *const search_binding_action_map[] = {
[BIND_ACTION_SEARCH_NONE] = NULL,
[BIND_ACTION_SEARCH_SCROLLBACK_UP_PAGE] = "scrollback-up-page",
[BIND_ACTION_SEARCH_SCROLLBACK_UP_HALF_PAGE] = "scrollback-up-half-page",
[BIND_ACTION_SEARCH_SCROLLBACK_UP_LINE] = "scrollback-up-line",
[BIND_ACTION_SEARCH_SCROLLBACK_DOWN_PAGE] = "scrollback-down-page",
[BIND_ACTION_SEARCH_SCROLLBACK_DOWN_HALF_PAGE] = "scrollback-down-half-page",
[BIND_ACTION_SEARCH_SCROLLBACK_DOWN_LINE] = "scrollback-down-line",
[BIND_ACTION_SEARCH_SCROLLBACK_HOME] = "scrollback-home",
[BIND_ACTION_SEARCH_SCROLLBACK_END] = "scrollback-end",
[BIND_ACTION_SEARCH_CANCEL] = "cancel",
[BIND_ACTION_SEARCH_COMMIT] = "commit",
[BIND_ACTION_SEARCH_FIND_PREV] = "find-prev",
[BIND_ACTION_SEARCH_FIND_NEXT] = "find-next",
[BIND_ACTION_SEARCH_EDIT_LEFT] = "cursor-left",
[BIND_ACTION_SEARCH_EDIT_LEFT_WORD] = "cursor-left-word",
[BIND_ACTION_SEARCH_EDIT_RIGHT] = "cursor-right",
[BIND_ACTION_SEARCH_EDIT_RIGHT_WORD] = "cursor-right-word",
[BIND_ACTION_SEARCH_EDIT_HOME] = "cursor-home",
[BIND_ACTION_SEARCH_EDIT_END] = "cursor-end",
[BIND_ACTION_SEARCH_DELETE_PREV] = "delete-prev",
[BIND_ACTION_SEARCH_DELETE_PREV_WORD] = "delete-prev-word",
[BIND_ACTION_SEARCH_DELETE_NEXT] = "delete-next",
[BIND_ACTION_SEARCH_DELETE_NEXT_WORD] = "delete-next-word",
[BIND_ACTION_SEARCH_DELETE_TO_START] = "delete-to-start",
[BIND_ACTION_SEARCH_DELETE_TO_END] = "delete-to-end",
[BIND_ACTION_SEARCH_EXTEND_CHAR] = "extend-char",
[BIND_ACTION_SEARCH_EXTEND_WORD] = "extend-to-word-boundary",
[BIND_ACTION_SEARCH_EXTEND_WORD_WS] = "extend-to-next-whitespace",
[BIND_ACTION_SEARCH_EXTEND_LINE_DOWN] = "extend-line-down",
[BIND_ACTION_SEARCH_EXTEND_BACKWARD_CHAR] = "extend-backward-char",
[BIND_ACTION_SEARCH_EXTEND_BACKWARD_WORD] = "extend-backward-to-word-boundary",
[BIND_ACTION_SEARCH_EXTEND_BACKWARD_WORD_WS] = "extend-backward-to-next-whitespace",
[BIND_ACTION_SEARCH_EXTEND_LINE_UP] = "extend-line-up",
[BIND_ACTION_SEARCH_CLIPBOARD_PASTE] = "clipboard-paste",
[BIND_ACTION_SEARCH_PRIMARY_PASTE] = "primary-paste",
[BIND_ACTION_SEARCH_UNICODE_INPUT] = "unicode-input",
static const char *const vimode_binding_action_map[] = {
[BIND_ACTION_VIMODE_NONE] = NULL,
[BIND_ACTION_VIMODE_UP] = "vimode-up",
[BIND_ACTION_VIMODE_DOWN] = "vimode-down",
[BIND_ACTION_VIMODE_LEFT] = "vimode-left",
[BIND_ACTION_VIMODE_RIGHT] = "vimode-right",
[BIND_ACTION_VIMODE_UP_PAGE] = "vimode-up-page",
[BIND_ACTION_VIMODE_DOWN_PAGE] = "vimode-down-page",
[BIND_ACTION_VIMODE_UP_HALF_PAGE] = "vimode-up-half-page",
[BIND_ACTION_VIMODE_DOWN_HALF_PAGE] = "vimode-down-half-page",
[BIND_ACTION_VIMODE_UP_LINE] = "vimode-up-line",
[BIND_ACTION_VIMODE_DOWN_LINE] = "vimode-down-line",
[BIND_ACTION_VIMODE_FIRST_LINE] = "vimode-first-line",
[BIND_ACTION_VIMODE_LAST_LINE] = "vimode-last-line",
[BIND_ACTION_VIMODE_CANCEL] = "vimode-cancel",
[BIND_ACTION_VIMODE_START_SEARCH] = "vimode-start-search",
[BIND_ACTION_VIMODE_FIND_NEXT] = "vimode-find-next",
[BIND_ACTION_VIMODE_FIND_PREV] = "vimode-find-prev",
[BIND_ACTION_VIMODE_ENTER_VISUAL] = "vimode-enter-visual",
[BIND_ACTION_VIMODE_ENTER_VLINE] = "vimode-enter-visual-line",
[BIND_ACTION_VIMODE_ENTER_VBLOCK] = "vimode-enter-visual-block",
[BIND_ACTION_VIMODE_YANK] = "vimode-yank",
};
static const char *const vimode_search_binding_action_map[] = {
[BIND_ACTION_VIMODE_NONE] = NULL,
[BIND_ACTION_VIMODE_SEARCH_CANCEL] = "vimode-search-cancel",
[BIND_ACTION_VIMODE_SEARCH_CONFIRM] = "vimode-search-confirm",
[BIND_ACTION_VIMODE_SEARCH_DELETE_PREV_CHAR] = "vimode-search-delete-prev",
[BIND_ACTION_VIMODE_SEARCH_LEFT] = "vimode-search-left",
[BIND_ACTION_VIMODE_SEARCH_RIGHT] = "vimode-search-right",
};
static const char *const url_binding_action_map[] = {
@ -206,8 +203,10 @@ static const char *const url_binding_action_map[] = {
static_assert(ALEN(binding_action_map) == BIND_ACTION_COUNT,
"binding action map size mismatch");
static_assert(ALEN(search_binding_action_map) == BIND_ACTION_SEARCH_COUNT,
"search binding action map size mismatch");
static_assert(ALEN(vimode_binding_action_map) == BIND_ACTION_VIMODE_COUNT,
"vimode binding action map size mismatch");
static_assert(ALEN(vimode_search_binding_action_map) == BIND_ACTION_VIMODE_SEARCH_COUNT,
"vimode-search binding action map size mismatch");
static_assert(ALEN(url_binding_action_map) == BIND_ACTION_URL_COUNT,
"URL binding action map size mismatch");
@ -2388,12 +2387,21 @@ parse_section_key_bindings(struct context *ctx)
}
static bool
parse_section_search_bindings(struct context *ctx)
parse_section_vimode_bindings(struct context *ctx)
{
return parse_key_binding_section(
ctx,
BIND_ACTION_SEARCH_COUNT, search_binding_action_map,
&ctx->conf->bindings.search);
BIND_ACTION_VIMODE_COUNT, vimode_binding_action_map,
&ctx->conf->bindings.vimode);
}
static bool
parse_section_vimode_search_bindings(struct context *ctx)
{
return parse_key_binding_section(
ctx,
BIND_ACTION_VIMODE_SEARCH_COUNT, vimode_search_binding_action_map,
&ctx->conf->bindings.vimode_search);
}
static bool
@ -2964,7 +2972,8 @@ enum section {
SECTION_MOUSE,
SECTION_CSD,
SECTION_KEY_BINDINGS,
SECTION_SEARCH_BINDINGS,
SECTION_VIMODE_BINDINGS,
SECTION_VIMODE_SEARCH_BINDINGS,
SECTION_URL_BINDINGS,
SECTION_MOUSE_BINDINGS,
SECTION_TEXT_BINDINGS,
@ -2995,7 +3004,8 @@ static const struct {
[SECTION_MOUSE] = {&parse_section_mouse, "mouse"},
[SECTION_CSD] = {&parse_section_csd, "csd"},
[SECTION_KEY_BINDINGS] = {&parse_section_key_bindings, "key-bindings"},
[SECTION_SEARCH_BINDINGS] = {&parse_section_search_bindings, "search-bindings"},
[SECTION_VIMODE_BINDINGS] = {&parse_section_vimode_bindings, "vimode-bindings"},
[SECTION_VIMODE_SEARCH_BINDINGS] = {&parse_section_vimode_search_bindings, "vimode-search-bindings"},
[SECTION_URL_BINDINGS] = {&parse_section_url_bindings, "url-bindings"},
[SECTION_MOUSE_BINDINGS] = {&parse_section_mouse_bindings, "mouse-bindings"},
[SECTION_TEXT_BINDINGS] = {&parse_section_text_bindings, "text-bindings"},
@ -3234,7 +3244,8 @@ add_default_key_bindings(struct config *conf)
{BIND_ACTION_CLIPBOARD_PASTE, m(XKB_MOD_NAME_CTRL "+" XKB_MOD_NAME_SHIFT), {{XKB_KEY_v}}},
{BIND_ACTION_CLIPBOARD_PASTE, m("none"), {{XKB_KEY_XF86Paste}}},
{BIND_ACTION_PRIMARY_PASTE, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_Insert}}},
{BIND_ACTION_SEARCH_START, m(XKB_MOD_NAME_CTRL "+" XKB_MOD_NAME_SHIFT), {{XKB_KEY_r}}},
{BIND_ACTION_START_VIMODE, m(XKB_MOD_NAME_CTRL "+" XKB_MOD_NAME_SHIFT), {{XKB_KEY_r}}},
{BIND_ACTION_START_VIMODE_SEARCH, m(XKB_MOD_NAME_CTRL "+" XKB_MOD_NAME_SHIFT), {{XKB_KEY_slash}}},
{BIND_ACTION_FONT_SIZE_UP, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_plus}}},
{BIND_ACTION_FONT_SIZE_UP, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_equal}}},
{BIND_ACTION_FONT_SIZE_UP, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_KP_Add}}},
@ -3255,57 +3266,51 @@ add_default_key_bindings(struct config *conf)
static void
add_default_search_bindings(struct config *conf)
add_default_vimode_bindings(struct config *conf)
{
const struct config_key_binding bindings[] = {
{BIND_ACTION_SEARCH_SCROLLBACK_UP_PAGE, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_Prior}}},
{BIND_ACTION_SEARCH_SCROLLBACK_UP_PAGE, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_KP_Prior}}},
{BIND_ACTION_SEARCH_SCROLLBACK_DOWN_PAGE, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_Next}}},
{BIND_ACTION_SEARCH_SCROLLBACK_DOWN_PAGE, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_KP_Next}}},
{BIND_ACTION_SEARCH_CANCEL, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_c}}},
{BIND_ACTION_SEARCH_CANCEL, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_g}}},
{BIND_ACTION_SEARCH_CANCEL, m("none"), {{XKB_KEY_Escape}}},
{BIND_ACTION_SEARCH_COMMIT, m("none"), {{XKB_KEY_Return}}},
{BIND_ACTION_SEARCH_COMMIT, m("none"), {{XKB_KEY_KP_Enter}}},
{BIND_ACTION_SEARCH_FIND_PREV, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_r}}},
{BIND_ACTION_SEARCH_FIND_NEXT, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_s}}},
{BIND_ACTION_SEARCH_EDIT_LEFT, m("none"), {{XKB_KEY_Left}}},
{BIND_ACTION_SEARCH_EDIT_LEFT, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_b}}},
{BIND_ACTION_SEARCH_EDIT_LEFT_WORD, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_Left}}},
{BIND_ACTION_SEARCH_EDIT_LEFT_WORD, m(XKB_MOD_NAME_ALT), {{XKB_KEY_b}}},
{BIND_ACTION_SEARCH_EDIT_RIGHT, m("none"), {{XKB_KEY_Right}}},
{BIND_ACTION_SEARCH_EDIT_RIGHT, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_f}}},
{BIND_ACTION_SEARCH_EDIT_RIGHT_WORD, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_Right}}},
{BIND_ACTION_SEARCH_EDIT_RIGHT_WORD, m(XKB_MOD_NAME_ALT), {{XKB_KEY_f}}},
{BIND_ACTION_SEARCH_EDIT_HOME, m("none"), {{XKB_KEY_Home}}},
{BIND_ACTION_SEARCH_EDIT_HOME, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_a}}},
{BIND_ACTION_SEARCH_EDIT_END, m("none"), {{XKB_KEY_End}}},
{BIND_ACTION_SEARCH_EDIT_END, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_e}}},
{BIND_ACTION_SEARCH_DELETE_PREV, m("none"), {{XKB_KEY_BackSpace}}},
{BIND_ACTION_SEARCH_DELETE_PREV_WORD, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_BackSpace}}},
{BIND_ACTION_SEARCH_DELETE_PREV_WORD, m(XKB_MOD_NAME_ALT), {{XKB_KEY_BackSpace}}},
{BIND_ACTION_SEARCH_DELETE_NEXT, m("none"), {{XKB_KEY_Delete}}},
{BIND_ACTION_SEARCH_DELETE_NEXT_WORD, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_Delete}}},
{BIND_ACTION_SEARCH_DELETE_NEXT_WORD, m(XKB_MOD_NAME_ALT), {{XKB_KEY_d}}},
{BIND_ACTION_SEARCH_DELETE_TO_START, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_u}}},
{BIND_ACTION_SEARCH_DELETE_TO_END, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_k}}},
{BIND_ACTION_SEARCH_EXTEND_CHAR, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_Right}}},
{BIND_ACTION_SEARCH_EXTEND_WORD, m(XKB_MOD_NAME_CTRL "+" XKB_MOD_NAME_SHIFT), {{XKB_KEY_Right}}},
{BIND_ACTION_SEARCH_EXTEND_WORD, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_w}}},
{BIND_ACTION_SEARCH_EXTEND_WORD_WS, m(XKB_MOD_NAME_CTRL "+" XKB_MOD_NAME_SHIFT), {{XKB_KEY_w}}},
{BIND_ACTION_SEARCH_EXTEND_LINE_DOWN, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_Down}}},
{BIND_ACTION_SEARCH_EXTEND_BACKWARD_CHAR, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_Left}}},
{BIND_ACTION_SEARCH_EXTEND_BACKWARD_WORD, m(XKB_MOD_NAME_CTRL "+" XKB_MOD_NAME_SHIFT), {{XKB_KEY_Left}}},
{BIND_ACTION_SEARCH_EXTEND_LINE_UP, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_Up}}},
{BIND_ACTION_SEARCH_CLIPBOARD_PASTE, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_v}}},
{BIND_ACTION_SEARCH_CLIPBOARD_PASTE, m(XKB_MOD_NAME_CTRL "+" XKB_MOD_NAME_SHIFT), {{XKB_KEY_v}}},
{BIND_ACTION_SEARCH_CLIPBOARD_PASTE, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_y}}},
{BIND_ACTION_SEARCH_CLIPBOARD_PASTE, m("none"), {{XKB_KEY_XF86Paste}}},
{BIND_ACTION_SEARCH_PRIMARY_PASTE, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_Insert}}},
{BIND_ACTION_VIMODE_UP, m("none"), {{XKB_KEY_k}}},
{BIND_ACTION_VIMODE_DOWN, m("none"), {{XKB_KEY_j}}},
{BIND_ACTION_VIMODE_LEFT, m("none"), {{XKB_KEY_h}}},
{BIND_ACTION_VIMODE_RIGHT, m("none"), {{XKB_KEY_l}}},
{BIND_ACTION_VIMODE_UP_PAGE, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_b}}},
{BIND_ACTION_VIMODE_DOWN_PAGE, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_f}}},
{BIND_ACTION_VIMODE_UP_HALF_PAGE, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_u}}},
{BIND_ACTION_VIMODE_DOWN_HALF_PAGE, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_d}}},
{BIND_ACTION_VIMODE_UP_LINE, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_y}}},
{BIND_ACTION_VIMODE_DOWN_LINE, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_e}}},
{BIND_ACTION_VIMODE_FIRST_LINE, m("none"), {{XKB_KEY_g}}},
{BIND_ACTION_VIMODE_LAST_LINE, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_g}}},
{BIND_ACTION_VIMODE_CANCEL, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_c}}},
{BIND_ACTION_VIMODE_CANCEL, m("none"), {{XKB_KEY_Escape}}},
{BIND_ACTION_VIMODE_START_SEARCH, m("none"), {{XKB_KEY_slash}}},
{BIND_ACTION_VIMODE_FIND_NEXT, m("none"), {{XKB_KEY_n}}},
{BIND_ACTION_VIMODE_FIND_PREV, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_n}}},
{BIND_ACTION_VIMODE_ENTER_VISUAL, m("none"), {{XKB_KEY_v}}},
{BIND_ACTION_VIMODE_ENTER_VLINE, m(XKB_MOD_NAME_SHIFT), {{XKB_KEY_v}}},
{BIND_ACTION_VIMODE_ENTER_VBLOCK, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_v}}},
{BIND_ACTION_VIMODE_YANK, m("none"), {{XKB_KEY_y}}},
};
conf->bindings.search.count = ALEN(bindings);
conf->bindings.search.arr = xmemdup(bindings, sizeof(bindings));
conf->bindings.vimode.count = ALEN(bindings);
conf->bindings.vimode.arr = xmemdup(bindings, sizeof(bindings));
}
static void
add_default_vimode_search_bindings(struct config *conf)
{
const struct config_key_binding bindings[] = {
{BIND_ACTION_VIMODE_SEARCH_CONFIRM, m("none"), {{XKB_KEY_Return}}},
{BIND_ACTION_VIMODE_SEARCH_CANCEL, m("none"), {{XKB_KEY_Escape}}},
{BIND_ACTION_VIMODE_SEARCH_DELETE_PREV_CHAR, m("none"), {{XKB_KEY_BackSpace}}},
{BIND_ACTION_VIMODE_SEARCH_LEFT, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_h}}},
{BIND_ACTION_VIMODE_SEARCH_LEFT, m("none"), {{XKB_KEY_leftarrow}}},
{BIND_ACTION_VIMODE_SEARCH_RIGHT, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_l}}},
{BIND_ACTION_VIMODE_SEARCH_RIGHT, m("none"), {{XKB_KEY_rightarrow}}},
};
conf->bindings.vimode_search.count = ALEN(bindings);
conf->bindings.vimode_search.arr = xmemdup(bindings, sizeof(bindings));
}
static void
@ -3602,7 +3607,8 @@ config_load(struct config *conf, const char *conf_path,
}
add_default_key_bindings(conf);
add_default_search_bindings(conf);
add_default_vimode_bindings(conf);
add_default_vimode_search_bindings(conf);
add_default_url_bindings(conf);
add_default_mouse_bindings(conf);
@ -3661,8 +3667,10 @@ config_load(struct config *conf, const char *conf_path,
#if defined(_DEBUG)
for (size_t i = 0; i < conf->bindings.key.count; i++)
xassert(conf->bindings.key.arr[i].action != BIND_ACTION_NONE);
for (size_t i = 0; i < conf->bindings.search.count; i++)
xassert(conf->bindings.search.arr[i].action != BIND_ACTION_SEARCH_NONE);
for (size_t i = 0; i < conf->bindings.vimode.count; i++)
xassert(conf->bindings.vimode.arr[i].action != BIND_ACTION_VIMODE_NONE);
for (size_t i = 0; i < conf->bindings.vimode_search.count; i++)
xassert(conf->bindings.vimode_search.arr[i].action != BIND_ACTION_VIMODE_SEARCH_NONE);
for (size_t i = 0; i < conf->bindings.url.count; i++)
xassert(conf->bindings.url.arr[i].action != BIND_ACTION_URL_NONE);
#endif
@ -3738,8 +3746,11 @@ config_override_apply(struct config *conf, config_override_t *overrides,
conf, section_info[SECTION_KEY_BINDINGS].name,
binding_action_map, &conf->bindings.key, KEY_BINDING) &&
resolve_key_binding_collisions(
conf, section_info[SECTION_SEARCH_BINDINGS].name,
search_binding_action_map, &conf->bindings.search, KEY_BINDING) &&
conf, section_info[SECTION_VIMODE_BINDINGS].name,
vimode_binding_action_map, &conf->bindings.vimode, KEY_BINDING) &&
resolve_key_binding_collisions(
conf, section_info[SECTION_VIMODE_SEARCH_BINDINGS].name,
vimode_search_binding_action_map, &conf->bindings.vimode_search, KEY_BINDING) &&
resolve_key_binding_collisions(
conf, section_info[SECTION_URL_BINDINGS].name,
url_binding_action_map, &conf->bindings.url, KEY_BINDING) &&
@ -3863,7 +3874,8 @@ config_clone(const struct config *old)
}
key_binding_list_clone(&conf->bindings.key, &old->bindings.key);
key_binding_list_clone(&conf->bindings.search, &old->bindings.search);
key_binding_list_clone(&conf->bindings.vimode, &old->bindings.vimode);
key_binding_list_clone(&conf->bindings.vimode_search, &old->bindings.vimode_search);
key_binding_list_clone(&conf->bindings.url, &old->bindings.url);
key_binding_list_clone(&conf->bindings.mouse, &old->bindings.mouse);
@ -3955,7 +3967,8 @@ config_free(struct config *conf)
}
free_key_binding_list(&conf->bindings.key);
free_key_binding_list(&conf->bindings.search);
free_key_binding_list(&conf->bindings.vimode);
free_key_binding_list(&conf->bindings.vimode_search);
free_key_binding_list(&conf->bindings.url);
free_key_binding_list(&conf->bindings.mouse);
tll_free_and_free(conf->mouse.selection_override_modifiers, free);

View file

@ -88,6 +88,7 @@ enum key_binding_type {
typedef tll(char *) config_modifier_list_t;
struct config_key_binding {
// TODO (kociap): from wayland.h?
int action; /* One of the various bind_action_* enums from wayland.h */
//struct config_key_modifiers modifiers;
config_modifier_list_t modifiers;
@ -358,9 +359,18 @@ struct config {
* Special modes
*/
/* While searching (not - action to *start* a search is in the
* 'key' bindings above */
struct config_key_binding_list search;
/*
* Bindings for vimode.
* Note: action to enter vimode is in the 'key' bindings
* above.
*/
struct config_key_binding_list vimode;
/*
* Bindings for the search mode within vimode.
* Actions to enter the search mode are in the 'key' and
* 'vimode' bindings.
*/
struct config_key_binding_list vimode_search;
/* While showing URL jump labels */
struct config_key_binding_list url;

2
grid.c
View file

@ -819,6 +819,7 @@ grid_resize_and_reflow(
size_t tracking_points_count,
struct coord *const _tracking_points[static tracking_points_count])
{
printf("GRID REFLOW\n");
#if defined(TIME_REFLOW) && TIME_REFLOW
struct timespec start;
clock_gettime(CLOCK_MONOTONIC, &start);
@ -859,6 +860,7 @@ grid_resize_and_reflow(
saved_cursor.row += grid->offset;
saved_cursor.row &= old_rows - 1;
// TODO (kociap): add the vimode cursor and selection start.
size_t tp_count =
tracking_points_count +
1 + /* cursor */

25
ime.c
View file

@ -177,8 +177,10 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
ime_reset_preedit(seat);
if (term != NULL) {
if (term->is_searching)
render_refresh_search(term);
if (term->is_vimming)
// TODO (kociap): refresh
// render_refresh_search(term);
(void)0;
else
render_refresh(term);
}
@ -198,9 +200,11 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
size_t len = strlen(text);
if (term != NULL) {
if (term->is_searching) {
search_add_chars(term, text, len);
render_refresh_search(term);
if (term->is_vimming) {
// TODO (kociap): input and refresh
// search_add_chars(term, text, len);
// render_refresh_search(term);
(void)0;
} else
term_to_slave(term, text, len);
}
@ -367,8 +371,10 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
ime_reset_pending_preedit(seat);
if (term != NULL) {
if (term->is_searching)
render_refresh_search(term);
if (term->is_vimming)
// TODO (kociap): refresh
// render_refresh_search(term);
(void)0;
else
render_refresh(term);
}
@ -474,8 +480,9 @@ ime_update_cursor_rect(struct seat *seat)
goto update;
/* Set in render_search_box() */
if (term->is_searching)
goto update;
// TODO (kociap): in vimode this most likely is not necessary.
// if (term->is_searching)
// goto update;
int x, y, width, height;
int col = term->grid->cursor.point.col;

21
input.c
View file

@ -31,7 +31,7 @@
#include "macros.h"
#include "quirks.h"
#include "render.h"
#include "search.h"
#include "vimode.h"
#include "selection.h"
#include "spawn.h"
#include "terminal.h"
@ -183,8 +183,12 @@ execute_binding(struct seat *seat, struct terminal *term,
term_reset_view(term);
return true;
case BIND_ACTION_SEARCH_START:
search_begin(term);
case BIND_ACTION_START_VIMODE:
vimode_begin(term);
return true;
case BIND_ACTION_START_VIMODE_SEARCH:
vimode_search_begin(term);
return true;
case BIND_ACTION_FONT_SIZE_UP:
@ -1579,7 +1583,6 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
uint32_t key, uint32_t state)
{
xassert(serial != 0);
seat->kbd.serial = serial;
if (seat->kbd.xkb == NULL ||
seat->kbd.xkb_keymap == NULL ||
@ -1632,15 +1635,17 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
if (pressed) {
if (term->unicode_mode.active) {
printf("UNICODE INPUT\n");
unicode_mode_input(seat, term, sym);
return;
}
else if (term->is_searching) {
else if (term->is_vimming) {
printf("VIMODE INPUT\n");
if (should_repeat)
start_repeater(seat, key);
search_input(
vimode_input(
seat, term, bindings, key, sym, mods, consumed,
raw_syms, raw_count, serial);
return;
@ -2744,7 +2749,7 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
selection_stop_scroll_timer(term);
/* Update selection */
if (!term->is_searching) {
if (!term->is_vimming) {
if (auto_scroll_direction != SELECTION_SCROLL_NOT) {
/*
* Start 'selection auto-scrolling'
@ -3207,7 +3212,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
break;
case TERM_SURF_GRID: {
search_cancel(term);
vimode_cancel(term);
urls_reset(term);
bool cursor_is_on_grid = seat->mouse.col >= 0 && seat->mouse.row >= 0;

View file

@ -111,7 +111,7 @@ key_binding_new_for_seat(struct key_binding_manager *mgr,
struct key_set set = {
.public = {
.key = tll_init(),
.search = tll_init(),
.vimode = tll_init(),
.url = tll_init(),
.mouse = tll_init(),
},
@ -152,7 +152,7 @@ key_binding_new_for_conf(struct key_binding_manager *mgr,
struct key_set set = {
.public = {
.key = tll_init(),
.search = tll_init(),
.vimode = tll_init(),
.url = tll_init(),
.mouse = tll_init(),
},
@ -532,13 +532,25 @@ convert_key_bindings(struct key_set *set)
}
static void
convert_search_bindings(struct key_set *set)
convert_vimode_bindings(struct key_set *set)
{
const struct config *conf = set->conf;
for (size_t i = 0; i < conf->bindings.search.count; i++) {
const struct config_key_binding *binding = &conf->bindings.search.arr[i];
convert_key_binding(set, binding, &set->public.search);
for (size_t i = 0; i < conf->bindings.vimode.count; i++) {
const struct config_key_binding *binding = &conf->bindings.vimode.arr[i];
convert_key_binding(set, binding, &set->public.vimode);
}
}
static void
convert_vimode_search_bindings(struct key_set *set)
{
const struct config *conf = set->conf;
for (size_t i = 0; i < conf->bindings.vimode_search.count; i++) {
const struct config_key_binding *binding =
&conf->bindings.vimode_search.arr[i];
convert_key_binding(set, binding, &set->public.vimode_search);
}
}
@ -597,7 +609,8 @@ load_keymap(struct key_set *set)
}
convert_key_bindings(set);
convert_search_bindings(set);
convert_vimode_bindings(set);
convert_vimode_search_bindings(set);
convert_url_bindings(set);
convert_mouse_bindings(set);
@ -638,7 +651,8 @@ static void NOINLINE
unload_keymap(struct key_set *set)
{
key_bindings_destroy(&set->public.key);
key_bindings_destroy(&set->public.search);
key_bindings_destroy(&set->public.vimode);
key_bindings_destroy(&set->public.vimode_search);
key_bindings_destroy(&set->public.url);
key_bindings_destroy(&set->public.mouse);
set->public.selection_overrides = 0;

View file

@ -21,7 +21,8 @@ enum bind_action_normal {
BIND_ACTION_CLIPBOARD_COPY,
BIND_ACTION_CLIPBOARD_PASTE,
BIND_ACTION_PRIMARY_PASTE,
BIND_ACTION_SEARCH_START,
BIND_ACTION_START_VIMODE,
BIND_ACTION_START_VIMODE_SEARCH,
BIND_ACTION_FONT_SIZE_UP,
BIND_ACTION_FONT_SIZE_DOWN,
BIND_ACTION_FONT_SIZE_RESET,
@ -63,44 +64,41 @@ enum bind_action_normal {
BIND_ACTION_COUNT = BIND_ACTION_SELECT_ROW + 1,
};
enum bind_action_search {
BIND_ACTION_SEARCH_NONE,
BIND_ACTION_SEARCH_SCROLLBACK_UP_PAGE,
BIND_ACTION_SEARCH_SCROLLBACK_UP_HALF_PAGE,
BIND_ACTION_SEARCH_SCROLLBACK_UP_LINE,
BIND_ACTION_SEARCH_SCROLLBACK_DOWN_PAGE,
BIND_ACTION_SEARCH_SCROLLBACK_DOWN_HALF_PAGE,
BIND_ACTION_SEARCH_SCROLLBACK_DOWN_LINE,
BIND_ACTION_SEARCH_SCROLLBACK_HOME,
BIND_ACTION_SEARCH_SCROLLBACK_END,
BIND_ACTION_SEARCH_CANCEL,
BIND_ACTION_SEARCH_COMMIT,
BIND_ACTION_SEARCH_FIND_PREV,
BIND_ACTION_SEARCH_FIND_NEXT,
BIND_ACTION_SEARCH_EDIT_LEFT,
BIND_ACTION_SEARCH_EDIT_LEFT_WORD,
BIND_ACTION_SEARCH_EDIT_RIGHT,
BIND_ACTION_SEARCH_EDIT_RIGHT_WORD,
BIND_ACTION_SEARCH_EDIT_HOME,
BIND_ACTION_SEARCH_EDIT_END,
BIND_ACTION_SEARCH_DELETE_PREV,
BIND_ACTION_SEARCH_DELETE_PREV_WORD,
BIND_ACTION_SEARCH_DELETE_NEXT,
BIND_ACTION_SEARCH_DELETE_NEXT_WORD,
BIND_ACTION_SEARCH_DELETE_TO_START,
BIND_ACTION_SEARCH_DELETE_TO_END,
BIND_ACTION_SEARCH_EXTEND_CHAR,
BIND_ACTION_SEARCH_EXTEND_WORD,
BIND_ACTION_SEARCH_EXTEND_WORD_WS,
BIND_ACTION_SEARCH_EXTEND_LINE_DOWN,
BIND_ACTION_SEARCH_EXTEND_BACKWARD_CHAR,
BIND_ACTION_SEARCH_EXTEND_BACKWARD_WORD,
BIND_ACTION_SEARCH_EXTEND_BACKWARD_WORD_WS,
BIND_ACTION_SEARCH_EXTEND_LINE_UP,
BIND_ACTION_SEARCH_CLIPBOARD_PASTE,
BIND_ACTION_SEARCH_PRIMARY_PASTE,
BIND_ACTION_SEARCH_UNICODE_INPUT,
BIND_ACTION_SEARCH_COUNT,
enum bind_action_vimode {
BIND_ACTION_VIMODE_NONE,
BIND_ACTION_VIMODE_UP,
BIND_ACTION_VIMODE_DOWN,
BIND_ACTION_VIMODE_LEFT,
BIND_ACTION_VIMODE_RIGHT,
BIND_ACTION_VIMODE_UP_PAGE,
BIND_ACTION_VIMODE_DOWN_PAGE,
BIND_ACTION_VIMODE_UP_HALF_PAGE,
BIND_ACTION_VIMODE_DOWN_HALF_PAGE,
BIND_ACTION_VIMODE_UP_LINE,
BIND_ACTION_VIMODE_DOWN_LINE,
BIND_ACTION_VIMODE_FIRST_LINE,
BIND_ACTION_VIMODE_LAST_LINE,
BIND_ACTION_VIMODE_CANCEL,
BIND_ACTION_VIMODE_START_SEARCH,
BIND_ACTION_VIMODE_FIND_NEXT,
BIND_ACTION_VIMODE_FIND_PREV,
BIND_ACTION_VIMODE_ENTER_VISUAL,
BIND_ACTION_VIMODE_ENTER_VLINE,
BIND_ACTION_VIMODE_ENTER_VBLOCK,
BIND_ACTION_VIMODE_YANK,
BIND_ACTION_VIMODE_COUNT,
};
enum bind_action_vimode_search {
BIND_ACTION_VIMODE_SEARCH_NONE,
BIND_ACTION_VIMODE_SEARCH_CONFIRM,
BIND_ACTION_VIMODE_SEARCH_CANCEL,
BIND_ACTION_VIMODE_SEARCH_DELETE_PREV_CHAR,
BIND_ACTION_VIMODE_SEARCH_LEFT,
BIND_ACTION_VIMODE_SEARCH_RIGHT,
BIND_ACTION_VIMODE_SEARCH_COUNT,
};
enum bind_action_url {
@ -140,7 +138,8 @@ struct wayland;
struct key_binding_set {
key_binding_list_t key;
key_binding_list_t search;
key_binding_list_t vimode;
key_binding_list_t vimode_search;
key_binding_list_t url;
key_binding_list_t mouse;
xkb_mod_mask_t selection_overrides;

View file

@ -320,7 +320,7 @@ executable(
'quirks.c', 'quirks.h',
'reaper.c', 'reaper.h',
'render.c', 'render.h',
'search.c', 'search.h',
'vimode.c', 'vimode.h',
'server.c', 'server.h', 'client-protocol.h',
'shm.c', 'shm.h',
'slave.c', 'slave.h',

247
render.c
View file

@ -36,7 +36,7 @@
#include "grid.h"
#include "ime.h"
#include "quirks.h"
#include "search.h"
#include "vimode.h"
#include "selection.h"
#include "shm.h"
#include "sixel.h"
@ -598,6 +598,10 @@ draw_strikeout(const struct terminal *term, pixman_image_t *pix,
cols * term->cell_width, thickness});
}
/*
* TODO (kociap): The cell parameter is not used? We could make this
* function more generic to be reusable in the search box.
*/
static void
cursor_colors_for_cell(const struct terminal *term, const struct cell *cell,
const pixman_color_t *fg, const pixman_color_t *bg,
@ -701,11 +705,12 @@ render_cell(struct terminal *term, pixman_image_t *pix,
const int x = term->margins.left + col * width;
const int y = term->margins.top + row_no * height;
const bool is_selected = cell->attrs.selected;
uint32_t _fg = 0;
uint32_t _bg = 0;
uint16_t alpha = 0xffff;
const bool is_selected = cell->attrs.selected;
/* Use cell specific color, if set, otherwise the default colors (possible reversed) */
switch (cell->attrs.fg_src) {
@ -762,19 +767,29 @@ render_cell(struct terminal *term, pixman_image_t *pix,
_fg = cell_bg;
}
// Cursor is always inverted, thus we want to invert it when
// the vi mode is visual or visual block for visibility
// (otherwise it is difficult to tell its position within the
// row).
const bool mode_not_vline = term->vimode.mode == VI_MODE_VISUAL ||
term->vimode.mode == VI_MODE_VBLOCK;
if (has_cursor && mode_not_vline) {
uint32_t swap = _fg;
_fg = _bg;
_bg = swap;
}
if (unlikely(_fg == _bg)) {
/* Invert bg when selected/highlighted text has same fg/bg */
_bg = ~_bg;
alpha = 0xffff;
}
} else {
if (unlikely(cell->attrs.reverse)) {
uint32_t swap = _fg;
_fg = _bg;
_bg = swap;
}
else if (!term->window->is_fullscreen && term->colors.alpha != 0xffff) {
switch (term->conf->colors.alpha_mode) {
case ALPHA_MODE_DEFAULT: {
@ -1722,7 +1737,7 @@ render_ime_preedit_for_seat(struct terminal *term, struct seat *seat,
if (likely(seat->ime.preedit.cells == NULL))
return;
if (unlikely(term->is_searching))
if (unlikely(term->is_vimming))
return;
const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf);
@ -1965,7 +1980,7 @@ render_overlay(struct terminal *term)
const bool unicode_mode_active = term->unicode_mode.active;
const enum overlay_style style =
term->is_searching ? OVERLAY_SEARCH :
term->is_vimming ? OVERLAY_SEARCH :
term->flash.active ? OVERLAY_FLASH :
unicode_mode_active ? OVERLAY_UNICODE_MODE :
OVERLAY_NONE;
@ -2065,33 +2080,34 @@ render_overlay(struct terminal *term)
pixman_region32_clear(see_through);
/* Build region consisting of all current search matches */
struct search_match_iterator iter = search_matches_new_iter(term);
for (struct range match = search_matches_next(&iter);
match.start.row >= 0;
match = search_matches_next(&iter))
{
int r = match.start.row;
int start_col = match.start.col;
const int end_row = match.end.row;
while (true) {
const int end_col =
r == end_row ? match.end.col : term->cols - 1;
int x = term->margins.left + start_col * term->cell_width;
int y = term->margins.top + r * term->cell_height;
int width = (end_col + 1 - start_col) * term->cell_width;
int height = 1 * term->cell_height;
pixman_region32_union_rect(
see_through, see_through, x, y, width, height);
if (++r > end_row)
break;
start_col = 0;
}
}
// TODO (kociap): commented this because no search atm.
// struct search_match_iterator iter = search_matches_new_iter(term);
// for (struct range match = search_matches_next(&iter);
// match.start.row >= 0;
// match = search_matches_next(&iter))
// {
// int r = match.start.row;
// int start_col = match.start.col;
// const int end_row = match.end.row;
//
// while (true) {
// const int end_col =
// r == end_row ? match.end.col : term->cols - 1;
//
// int x = term->margins.left + start_col * term->cell_width;
// int y = term->margins.top + r * term->cell_height;
// int width = (end_col + 1 - start_col) * term->cell_width;
// int height = 1 * term->cell_height;
//
// pixman_region32_union_rect(
// see_through, see_through, x, y, width, height);
//
// if (++r > end_row)
// break;
//
// start_col = 0;
// }
// }
/* Areas that need to be cleared: cells that were dimmed in
* the last frame but is now see-through */
@ -2198,9 +2214,17 @@ render_worker_thread(void *_ctx)
bool frame_done = false;
/* Translate offset-relative cursor row to view-relative */
/*
* We always show the vimode cursor when in vimode as that is
* not dependent on the terminal state.
*/
struct coord cursor = {-1, -1};
if (!term->hide_cursor) {
if (term->is_vimming) {
cursor = term->vimode.cursor;
cursor.row += term->grid->offset;
cursor.row -= term->grid->view;
cursor.row &= term->grid->num_rows - 1;
} else if (!term->hide_cursor) {
cursor = term->grid->cursor.point;
cursor.row += term->grid->offset;
cursor.row -= term->grid->view;
@ -3084,10 +3108,11 @@ render_scrollback_position(struct terminal *term)
case SCROLLBACK_INDICATOR_POSITION_RELATIVE: {
int lines = term->rows - 2; /* Avoid using first and last rows */
if (term->is_searching) {
/* Make sure we don't collide with the scrollback search box */
lines--;
}
// TODO (kociap): whatever this does
// if (term->is_searching) {
// /* Make sure we don't collide with the scrollback search box */
// lines--;
// }
lines = max(lines, 0);
@ -3303,7 +3328,6 @@ dirty_cursor(struct terminal *term)
return;
const struct coord *cursor = &term->grid->cursor.point;
struct row *row = grid_row(term->grid, cursor->row);
struct cell *cell = &row->cells[cursor->col];
cell->attrs.clean = 0;
@ -3603,7 +3627,8 @@ grid_render(struct terminal *term)
pixman_region32_fini(&damage);
render_overlay(term);
// TODO (kociap): we want to eventually render the overlays.
// render_overlay(term);
render_ime_preedit(term, buf);
render_scrollback_position(term);
@ -3709,9 +3734,20 @@ grid_render(struct terminal *term)
wl_surface_commit(term->window->surface.surf);
}
static void
render_search_box_cursor(
struct terminal* const term, pixman_image_t* const pix, pixman_color_t fg,
int x, int y)
{
pixman_image_fill_rectangles(
PIXMAN_OP_SRC, pix, &fg, 1,
&(pixman_rectangle16_t){x, y, term->cell_width, term->cell_height});
}
static void
render_search_box(struct terminal *term)
{
printf("REDRAWING SEARCH BOX\n");
xassert(term->window->search.sub != NULL);
/*
@ -3735,7 +3771,7 @@ render_search_box(struct terminal *term)
}
}
size_t text_len = term->search.len;
size_t text_len = term->vimode.search.len;
if (ime_seat != NULL && ime_seat->ime.preedit.text != NULL)
text_len += c32len(ime_seat->ime.preedit.text);
@ -3743,19 +3779,19 @@ render_search_box(struct terminal *term)
text[0] = U'\0';
/* Copy everything up to the cursor */
c32ncpy(text, term->search.buf, term->search.cursor);
text[term->search.cursor] = U'\0';
c32ncpy(text, term->vimode.search.buf, term->vimode.search.cursor);
text[term->vimode.search.cursor] = U'\0';
/* Insert pre-edit text at cursor */
if (ime_seat != NULL && ime_seat->ime.preedit.text != NULL)
c32cat(text, ime_seat->ime.preedit.text);
/* And finally everything after the cursor */
c32ncat(text, &term->search.buf[term->search.cursor],
term->search.len - term->search.cursor);
c32ncat(text, &term->vimode.search.buf[term->vimode.search.cursor],
term->vimode.search.len - term->vimode.search.cursor);
#else
const char32_t *text = term->search.buf;
const size_t text_len = term->search.len;
const char32_t *text = term->vimode.search.buf;
const size_t text_len = term->vimode.search.len;
#endif
/* Calculate the width of each character */
@ -3769,21 +3805,15 @@ render_search_box(struct terminal *term)
const float scale = term->scale;
xassert(scale >= 1.);
const size_t margin = (size_t)roundf(3 * scale);
size_t width = term->width - 2 * margin;
size_t height = min(
term->height - 2 * margin,
margin + 1 * term->cell_height + margin);
const size_t width = roundf(scale * ceilf(term->width / scale));
const size_t height =
roundf(scale * ceilf(min(term->height, term->cell_height) / scale));
width = roundf(scale * ceilf((term->width - 2 * margin) / scale));
height = roundf(scale * ceilf(height / scale));
const size_t visible_width =
min(term->width, wanted_visible_cells * term->cell_width);
size_t visible_width = min(
term->width - 2 * margin,
margin + wanted_visible_cells * term->cell_width + margin);
const size_t visible_cells = (visible_width - 2 * margin) / term->cell_width;
const size_t visible_cells = (visible_width) / term->cell_width;
size_t glyph_offset = term->render.search_glyph_offset;
struct buffer_chain *chain = term->render.chains.search;
@ -3794,52 +3824,46 @@ render_search_box(struct terminal *term)
pixman_image_set_clip_region32(buf->pix[0], &clip);
pixman_region32_fini(&clip);
#define WINDOW_X(x) (margin + x)
#define WINDOW_Y(y) (term->height - margin - height + y)
#define WINDOW_X(x) (x)
#define WINDOW_Y(y) (term->height - height + y)
const bool is_match = term->search.match_len == text_len;
const bool is_match = term->vimode.search.match_len == text_len;
const bool custom_colors = is_match
? term->conf->colors.use_custom.search_box_match
: term->conf->colors.use_custom.search_box_no_match;
/* Background - yellow on empty/match, red on mismatch (default) */
const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf);
const pixman_color_t color = color_hex_to_pixman(
const pixman_color_t bg = color_hex_to_pixman(
is_match
? (custom_colors
? term->conf->colors.search_box.match.bg
: term->colors.table[3])
: term->colors.bg)
: (custom_colors
? term->conf->colors.search_box.no_match.bg
: term->colors.table[1]),
: term->colors.bg),
gamma_correct);
pixman_image_fill_rectangles(
PIXMAN_OP_SRC, buf->pix[0], &color,
1, &(pixman_rectangle16_t){width - visible_width, 0, visible_width, height});
pixman_color_t transparent = color_hex_to_pixman_with_alpha(0, 0, gamma_correct);
pixman_image_fill_rectangles(
PIXMAN_OP_SRC, buf->pix[0], &transparent,
1, &(pixman_rectangle16_t){0, 0, width - visible_width, height});
PIXMAN_OP_SRC, buf->pix[0], &bg, 1,
&(pixman_rectangle16_t){0, 0, width, height});
struct fcft_font *font = term->fonts[0];
const int x_left = width - visible_width + margin;
const int x_ofs = term->font_x_ofs;
int x = x_left;
int y = margin;
int x = 0;
int y = 0;
pixman_color_t fg = color_hex_to_pixman(
custom_colors
? (is_match
? term->conf->colors.search_box.match.fg
: term->conf->colors.search_box.no_match.fg)
: term->colors.table[0],
: term->colors.table[244],
gamma_correct);
/* Move offset we start rendering at, to ensure the cursor is visible */
for (size_t i = 0, cell_idx = 0; i <= term->search.cursor; cell_idx += widths[i], i++) {
if (i != term->search.cursor)
for (size_t i = 0, cell_idx = 0; i <= term->vimode.search.cursor; cell_idx += widths[i], i++) {
if (i != term->vimode.search.cursor)
continue;
#if (FOOT_IME_ENABLED) && FOOT_IME_ENABLED
@ -3905,7 +3929,8 @@ render_search_box(struct terminal *term)
{
/* Convert subsurface coordinates to window coordinates*/
/* Render cursor */
if (i == term->search.cursor) {
bool const cursor_cell = i == term->vimode.search.cursor;
if (cursor_cell) {
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
bool have_preedit =
ime_seat != NULL && ime_seat->ime.preedit.cells != NULL;
@ -3933,8 +3958,7 @@ render_search_box(struct terminal *term)
/* Bar-styled cursor, if in the visible area */
if (start >= 0 && start <= visible_cells) {
draw_beam_cursor(
term, buf->pix[0], font, &fg,
render_search_box_cursor(term, buf->pix[0], fg,
x + start * term->cell_width, y);
}
@ -3966,7 +3990,7 @@ render_search_box(struct terminal *term)
/* Cursor *should* be in the visible area */
xassert(cell_idx >= glyph_offset);
xassert(cell_idx <= glyph_offset + visible_cells);
draw_beam_cursor(term, buf->pix[0], font, &fg, x, y);
render_search_box_cursor(term, buf->pix[0], fg, x, y);
term_ime_set_cursor_rect(
term, WINDOW_X(x), WINDOW_Y(y), 1, term->cell_height);
}
@ -4002,7 +4026,8 @@ render_search_box(struct terminal *term)
? width * term->cell_width
: (width - 1) * term->cell_width)
: 0; /* Not a zero-width character - no additional offset */
pixman_image_t *src = pixman_image_create_solid_fill(&fg);
pixman_color_t color = cursor_cell ? bg : fg;
pixman_image_t *src = pixman_image_create_solid_fill(&color);
pixman_image_composite32(
PIXMAN_OP_OVER, src, glyph->pix, buf->pix[0], 0, 0, 0, 0,
x + x_ofs + combining_ofs + glyph->x,
@ -4020,8 +4045,8 @@ render_search_box(struct terminal *term)
/* Already rendered */;
else
#endif
if (term->search.cursor >= term->search.len) {
draw_beam_cursor(term, buf->pix[0], font, &fg, x, y);
if (term->vimode.search.cursor >= term->vimode.search.len) {
render_search_box_cursor(term, buf->pix[0], fg, x, y);
term_ime_set_cursor_rect(
term, WINDOW_X(x), WINDOW_Y(y), 1, term->cell_height);
}
@ -4031,8 +4056,8 @@ render_search_box(struct terminal *term)
/* TODO: this is only necessary on a window resize */
wl_subsurface_set_position(
term->window->search.sub,
roundf(margin / scale),
roundf(max(0, (int32_t)term->height - height - margin) / scale));
0,
roundf(max(0, (int32_t)term->height - height) / scale));
wayl_surface_scale(term->window, &term->window->search.surface, buf, scale);
wl_surface_attach(term->window->search.surface.surf, buf->wl_buf, 0, 0);
@ -4040,7 +4065,7 @@ render_search_box(struct terminal *term)
struct wl_region *region = wl_compositor_create_region(term->wl->compositor);
if (region != NULL) {
wl_region_add(region, width - visible_width, 0, visible_width, height);
wl_region_add(region, 0, 0, width, height);
wl_surface_set_opaque_region(term->window->search.surface.surf, region);
wl_region_destroy(region);
}
@ -4310,14 +4335,14 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da
wl_callback_destroy(wl_callback);
term->window->frame_callback = NULL;
bool grid = term->render.pending.grid;
bool csd = term->render.pending.csd;
bool search = term->is_searching && term->render.pending.search;
bool urls = urls_mode_is_active(term) > 0 && term->render.pending.urls;
bool const grid = term->render.pending.grid;
bool const csd = term->render.pending.csd;
bool const search_box = term->render.pending.vimode_search_box;
bool const urls = urls_mode_is_active(term) > 0 && term->render.pending.urls;
term->render.pending.grid = false;
term->render.pending.csd = false;
term->render.pending.search = false;
term->render.pending.vimode_search_box = false;
term->render.pending.urls = false;
struct grid *original_grid = term->grid;
@ -4332,13 +4357,13 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da
quirk_weston_csd_off(term);
}
if (search)
if (search_box)
render_search_box(term);
if (urls)
render_urls(term);
if ((grid && !term->delayed_render_timer.is_armed) || (csd | search | urls))
if ((grid && !term->delayed_render_timer.is_armed) || (csd | search_box | urls))
grid_render(term);
tll_foreach(term->wl->seats, it) {
@ -4984,7 +5009,7 @@ damage_view:
term->render.last_buf = NULL;
term_damage_view(term);
render_refresh_csd(term);
render_refresh_search(term);
render_refresh_vimode_search_box(term);
render_refresh(term);
return true;
@ -5129,20 +5154,20 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
if (unlikely(term->shutdown.in_progress || !term->window->is_configured))
continue;
bool grid = term->render.refresh.grid;
bool csd = term->render.refresh.csd;
bool search = term->is_searching && term->render.refresh.search;
bool urls = urls_mode_is_active(term) && term->render.refresh.urls;
bool const grid = term->render.refresh.grid;
bool const csd = term->render.refresh.csd;
bool const search_box = term->render.refresh.vimode_search_box;
bool const urls = urls_mode_is_active(term) && term->render.refresh.urls;
if (!(grid | csd | search | urls))
if (!(grid | csd | search_box | urls))
continue;
if (term->render.app_sync_updates.enabled && !(csd | search | urls))
if (term->render.app_sync_updates.enabled && !(csd | search_box | urls))
continue;
term->render.refresh.grid = false;
term->render.refresh.csd = false;
term->render.refresh.search = false;
term->render.refresh.vimode_search_box = false;
term->render.refresh.urls = false;
if (term->window->frame_callback == NULL) {
@ -5157,11 +5182,11 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
render_csd(term);
quirk_weston_csd_off(term);
}
if (search)
if (search_box)
render_search_box(term);
if (urls)
render_urls(term);
if (grid | csd | search | urls)
if (grid | csd | search_box | urls)
grid_render(term);
tll_foreach(term->wl->seats, it) {
@ -5174,7 +5199,7 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
/* Tells the frame callback to render again */
term->render.pending.grid |= grid;
term->render.pending.csd |= csd;
term->render.pending.search |= search;
term->render.pending.vimode_search_box |= search_box;
term->render.pending.urls |= urls;
}
}
@ -5292,11 +5317,13 @@ render_refresh_csd(struct terminal *term)
term->render.refresh.csd = true;
}
// TODO (kociap): Rename to something more indicative, e.g.
// render_refresh_vimode_search_box_search_box
void
render_refresh_search(struct terminal *term)
render_refresh_vimode_search_box(struct terminal *term)
{
if (term->is_searching)
term->render.refresh.search = true;
if (term->is_vimming && term->vimode.is_searching)
term->render.refresh.vimode_search_box = true;
}
void

View file

@ -24,7 +24,7 @@ void render_refresh(struct terminal *term);
void render_refresh_app_id(struct terminal *term);
void render_refresh_icon(struct terminal *term);
void render_refresh_csd(struct terminal *term);
void render_refresh_search(struct terminal *term);
void render_refresh_vimode_search_box(struct terminal *term);
void render_refresh_title(struct terminal *term);
void render_refresh_urls(struct terminal *term);
bool render_xcursor_set(

View file

@ -24,7 +24,7 @@
#include "grid.h"
#include "misc.h"
#include "render.h"
#include "search.h"
#include "vimode.h"
#include "uri.h"
#include "util.h"
#include "vt.h"
@ -1615,8 +1615,6 @@ selection_cancel(struct terminal *term)
term->selection.pivot.end = (struct coord){-1, -1};
term->selection.direction = SELECTION_UNDIR;
term->selection.ongoing = false;
search_selection_cancelled(term);
}
bool

View file

@ -46,6 +46,7 @@
#include "vt.h"
#include "xmalloc.h"
#include "xsnprintf.h"
#include "vimode.h"
#define PTMX_TIMING 0
@ -1886,8 +1887,9 @@ term_destroy(struct terminal *term)
free_custom_glyphs(
&term->custom_glyphs.octants, GLYPH_OCTANTS_COUNT);
free(term->search.buf);
free(term->search.last.buf);
// TODO (kociap): Free the vimode search buffers.
// free(term->search.buf);
// free(term->search.last.buf);
if (term->render.workers.threads != NULL) {
for (size_t i = 0; i < term->render.workers.count; i++) {
@ -2477,6 +2479,18 @@ term_font_baseline(const struct terminal *term)
return term->font_y_ofs + line_height - glyph_top_y - font->descent;
}
void
term_damage_cell_in_view(struct terminal* const term, int const row, int const col)
{
if(col >= term->grid->num_cols || col < 0) {
return;
}
struct row* const r = grid_row_in_view(term->grid, row);
r->dirty = true;
r->cells[col].attrs.clean = 0;
}
void
term_damage_rows(struct terminal *term, int start, int end)
{
@ -3056,6 +3070,7 @@ selection_on_bottom_region(const struct terminal *term,
void
term_scroll_partial(struct terminal *term, struct scroll_region region, int rows)
{
printf("SCROLL PARTIAL [rows=%d, region.start=%d, region.end=%d]\n", rows, region.start, region.end);
LOG_DBG("scroll: rows=%d, region.start=%d, region.end=%d",
rows, region.start, region.end);
@ -3113,6 +3128,7 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows
}
term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);
vimode_view_down(term, rows);
#if defined(_DEBUG)
for (int r = 0; r < term->rows; r++)
@ -3798,8 +3814,8 @@ term_enable_app_sync_updates(struct terminal *term)
/* Disable pending refresh *iff* the grid is the *only* thing
* scheduled to be re-rendered */
if (!term->render.refresh.csd && !term->render.refresh.search &&
!term->render.pending.csd && !term->render.pending.search)
if (!term->render.refresh.csd && !term->render.refresh.vimode_search_box &&
!term->render.pending.csd && !term->render.pending.vimode_search_box)
{
term->render.refresh.grid = false;
term->render.pending.grid = false;
@ -3960,6 +3976,7 @@ term_fill(struct terminal *term, int r, int c, uint8_t data, size_t count,
void
term_print(struct terminal *term, char32_t wc, int width, bool insert_mode_disable)
{
printf("TERM PRINT\n");
xassert(width > 0);
struct grid *grid = term->grid;

View file

@ -374,11 +374,19 @@ enum term_surface {
enum overlay_style {
OVERLAY_NONE,
// TODO (kociap): rename to OVERLAY_VIMODE
OVERLAY_SEARCH,
OVERLAY_FLASH,
OVERLAY_UNICODE_MODE,
};
enum vi_mode {
VI_MODE_NORMAL,
VI_MODE_VISUAL,
VI_MODE_VLINE,
VI_MODE_VBLOCK,
};
typedef tll(struct ptmx_buffer) ptmx_buffer_list_t;
enum url_action { URL_ACTION_COPY, URL_ACTION_LAUNCH, URL_ACTION_PERSISTENT };
@ -621,23 +629,34 @@ struct terminal {
} auto_scroll;
} selection;
bool is_searching;
bool is_vimming;
struct {
char32_t *buf;
size_t len;
size_t sz;
size_t cursor;
enum vi_mode mode;
struct coord cursor;
bool is_searching;
int original_view;
bool view_followed_offset;
struct coord match;
size_t match_len;
struct {
struct coord start;
} selection;
struct vimode_search {
char32_t *buf;
size_t len;
size_t sz;
size_t cursor;
enum search_direction direction;
int original_view;
struct coord match;
size_t match_len;
} search;
struct {
char32_t *buf;
size_t len;
} last;
} search;
enum search_direction direction;
} confirmed_search;
} vimode;
struct wayland *wl;
struct wl_window *window;
@ -660,7 +679,7 @@ struct terminal {
struct {
bool grid;
bool csd;
bool search;
bool vimode_search_box;
bool urls;
} refresh;
@ -668,7 +687,7 @@ struct terminal {
struct {
bool grid;
bool csd;
bool search;
bool vimode_search_box;
bool urls;
} pending;
@ -878,6 +897,7 @@ int term_pt_or_px_as_pixels(
void term_window_configured(struct terminal *term);
void term_damage_cell_in_view(struct terminal* term, int row, int col);
void term_damage_rows(struct terminal *term, int start, int end);
void term_damage_rows_in_view(struct terminal *term, int start, int end);

View file

@ -1165,22 +1165,22 @@ test_section_key_bindings_collisions(void)
}
static void
test_section_search_bindings(void)
test_section_vimode_bindings(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "search-bindings", .path = "unittest"};
.conf = &conf, .section = "vimode-bindings", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_search_bindings, "invalid-key");
test_invalid_key(&ctx, &parse_section_vimode_bindings, "invalid-key");
for (int action = 0; action < BIND_ACTION_SEARCH_COUNT; action++) {
if (search_binding_action_map[action] == NULL)
for (int action = 0; action < BIND_ACTION_VIMODE_COUNT; action++) {
if (vimode_binding_action_map[action] == NULL)
continue;
test_key_binding(
&ctx, &parse_section_search_bindings,
action, BIND_ACTION_SEARCH_COUNT - 1,
search_binding_action_map, &conf.bindings.search, KEY_BINDING,
&ctx, &parse_section_vimode_bindings,
action, BIND_ACTION_VIMODE_COUNT - 1,
vimode_binding_action_map, &conf.bindings.vimode, KEY_BINDING,
false, false);
}
@ -1188,15 +1188,52 @@ test_section_search_bindings(void)
}
static void
test_section_search_bindings_collisions(void)
test_section_vimode_search_bindings(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "search-bindings", .path = "unittest"};
.conf = &conf, .section = "vimode-search-bindings", .path = "unittest"};
test_invalid_key(&ctx, &parse_section_vimode_search_bindings, "invalid-key");
for (int action = 0; action < BIND_ACTION_VIMODE_COUNT; action++) {
if (vimode_search_binding_action_map[action] == NULL)
continue;
test_key_binding(
&ctx, &parse_section_vimode_search_bindings,
action, BIND_ACTION_VIMODE_COUNT - 1,
vimode_search_binding_action_map, &conf.bindings.vimode_search, KEY_BINDING,
false, false);
}
config_free(&conf);
}
static void
test_section_vimode_bindings_collisions(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "vimode-bindings", .path = "unittest"};
test_binding_collisions(
&ctx,
BIND_ACTION_SEARCH_COUNT - 1, search_binding_action_map, KEY_BINDING);
BIND_ACTION_VIMODE_COUNT - 1, vimode_binding_action_map, KEY_BINDING);
config_free(&conf);
}
static void
test_section_vimode_search_bindings_collisions(void)
{
struct config conf = {0};
struct context ctx = {
.conf = &conf, .section = "vimode-search-bindings", .path = "unittest"};
test_binding_collisions(
&ctx,
BIND_ACTION_VIMODE_COUNT - 1, vimode_search_binding_action_map, KEY_BINDING);
config_free(&conf);
}
@ -1448,8 +1485,10 @@ main(int argc, const char *const *argv)
test_section_csd();
test_section_key_bindings();
test_section_key_bindings_collisions();
test_section_search_bindings();
test_section_search_bindings_collisions();
test_section_vimode_bindings();
test_section_vimode_search_bindings();
test_section_vimode_bindings_collisions();
test_section_vimode_search_bindings_collisions();
test_section_url_bindings();
test_section_url_bindings_collisions();
test_section_mouse_bindings();

View file

@ -33,9 +33,11 @@ unicode_mode_updated(struct terminal *term)
{
if (term == NULL)
return;
if (term->is_searching)
render_refresh_search(term);
printf("UNICODE UPDATE\n");
if (term->is_vimming)
// TODO (kociap): refresh
// render_refresh_search(term);
(void)0;
else
render_refresh(term);
}
@ -44,6 +46,7 @@ void
unicode_mode_input(struct seat *seat, struct terminal *term,
xkb_keysym_t sym)
{
printf("UNICODE INPUT\n");
if (sym == XKB_KEY_Return ||
sym == XKB_KEY_space ||
sym == XKB_KEY_KP_Enter ||
@ -57,8 +60,10 @@ unicode_mode_input(struct seat *seat, struct terminal *term,
term->unicode_mode.character, (int)chars, utf8);
if (chars != (size_t)-1) {
if (term->is_searching)
search_add_chars(term, utf8, chars);
if (term->is_vimming)
// TODO (kociap): input
// search_add_chars(term, utf8, chars);
(void)0;
else
term_to_slave(term, utf8, chars);
}

1072
vimode.c Normal file

File diff suppressed because it is too large Load diff

33
vimode.h Normal file
View file

@ -0,0 +1,33 @@
#pragma once
#include <xkbcommon/xkbcommon.h>
#include "key-binding.h"
#include "terminal.h"
void vimode_begin(struct terminal *term);
/* vimode_search_begin
*
* Enter search mode directly without needing to interact with the
* vimode first. Enters vimode as well.
*/
void vimode_search_begin(struct terminal *term);
void vimode_cancel(struct terminal *term);
void vimode_input(struct seat *seat, struct terminal *term,
const struct key_binding_set *bindings, uint32_t key,
xkb_keysym_t sym, xkb_mod_mask_t mods,
xkb_mod_mask_t consumed, const xkb_keysym_t *raw_syms,
size_t raw_count, uint32_t serial);
// void search_add_chars(struct terminal *term, const char *text, size_t len);
struct search_match_iterator {
struct terminal *term;
struct coord start;
};
struct search_match_iterator search_matches_new_iter(struct terminal *term);
struct range search_matches_next(struct search_match_iterator *iter);
void vimode_view_down(struct terminal *term, int delta);