mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-04-14 08:21:27 -04:00
feat: clsoe dialog
This commit is contained in:
parent
72d9a13c0c
commit
41ed7a81a2
15 changed files with 601 additions and 4 deletions
87
config.c
87
config.c
|
|
@ -14,6 +14,7 @@
|
|||
#include <sys/stat.h>
|
||||
|
||||
#include <linux/input-event-codes.h>
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <fontconfig/fontconfig.h>
|
||||
|
||||
|
|
@ -204,12 +205,20 @@ static const char *const url_binding_action_map[] = {
|
|||
[BIND_ACTION_URL_TOGGLE_URL_ON_JUMP_LABEL] = "toggle-url-visible",
|
||||
};
|
||||
|
||||
static const char *const msg_binding_action_map[] = {
|
||||
[BIND_ACTION_CLOSE_MESSAGE_NONE] = NULL,
|
||||
[BIND_ACTION_CLOSE_MESSAGE_CANCEL] = "cancel",
|
||||
[BIND_ACTION_CLOSE_MESSAGE_ACCEPT] = "accept",
|
||||
};
|
||||
|
||||
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(url_binding_action_map) == BIND_ACTION_URL_COUNT,
|
||||
"URL binding action map size mismatch");
|
||||
static_assert(ALEN(msg_binding_action_map) == BIND_ACTION_MESSAGE_COUNT,
|
||||
"Message binding action map size mismatch");
|
||||
|
||||
struct context {
|
||||
struct config *conf;
|
||||
|
|
@ -964,6 +973,20 @@ parse_section_main(struct context *ctx)
|
|||
return true;
|
||||
}
|
||||
|
||||
else if (streq(key, "close-policy")) {
|
||||
if (strcasecmp(value, "always") == 0)
|
||||
conf->close_policy = CLOSE_ALWAYS;
|
||||
else if (strcasecmp(value, "never") == 0)
|
||||
conf->close_policy = CLOSE_NEVER;
|
||||
else if (strcasecmp(value, "message-always") == 0)
|
||||
conf->close_policy = CLOSE_MESSAGE_ALWAYS;
|
||||
else if (strcasecmp(value, "message-demand") == 0)
|
||||
conf->close_policy = CLOSE_MESSAGE_DEMAND;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
else if (streq(key, "resize-delay-ms"))
|
||||
return value_to_uint16(ctx, 10, &conf->resize_delay_ms);
|
||||
|
||||
|
|
@ -1282,6 +1305,15 @@ parse_section_url(struct context *ctx)
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_section_msg(struct context *ctx)
|
||||
{
|
||||
const char *key = ctx->key;
|
||||
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_section_regex(struct context *ctx)
|
||||
{
|
||||
|
|
@ -1416,6 +1448,20 @@ parse_color_theme(struct context *ctx, struct color_theme *theme)
|
|||
return true;
|
||||
}
|
||||
|
||||
else if (streq(key, "msg_style")) {
|
||||
if (!value_to_two_colors(
|
||||
ctx,
|
||||
&theme->msg_style.fg,
|
||||
&theme->msg_style.bg,
|
||||
false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
theme->use_custom.msg_style = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
else if (streq(key, "scrollback-indicator")) {
|
||||
if (!value_to_two_colors(
|
||||
ctx,
|
||||
|
|
@ -2381,6 +2427,15 @@ parse_section_url_bindings(struct context *ctx)
|
|||
&ctx->conf->bindings.url);
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_section_msg_bindings(struct context *ctx)
|
||||
{
|
||||
return parse_key_binding_section(
|
||||
ctx,
|
||||
BIND_ACTION_MESSAGE_COUNT, msg_binding_action_map,
|
||||
&ctx->conf->bindings.msg);
|
||||
}
|
||||
|
||||
static bool NOINLINE
|
||||
resolve_key_binding_collisions(struct config *conf, const char *section_name,
|
||||
const char *const action_map[],
|
||||
|
|
@ -2927,6 +2982,7 @@ enum section {
|
|||
SECTION_DESKTOP_NOTIFICATIONS,
|
||||
SECTION_SCROLLBACK,
|
||||
SECTION_URL,
|
||||
SECTION_MSG,
|
||||
SECTION_REGEX,
|
||||
SECTION_COLORS,
|
||||
SECTION_COLORS2,
|
||||
|
|
@ -2936,6 +2992,7 @@ enum section {
|
|||
SECTION_KEY_BINDINGS,
|
||||
SECTION_SEARCH_BINDINGS,
|
||||
SECTION_URL_BINDINGS,
|
||||
SECTION_MSG_BINDINGS,
|
||||
SECTION_MOUSE_BINDINGS,
|
||||
SECTION_TEXT_BINDINGS,
|
||||
SECTION_ENVIRONMENT,
|
||||
|
|
@ -2958,6 +3015,7 @@ static const struct {
|
|||
[SECTION_DESKTOP_NOTIFICATIONS] = {&parse_section_desktop_notifications, "desktop-notifications"},
|
||||
[SECTION_SCROLLBACK] = {&parse_section_scrollback, "scrollback"},
|
||||
[SECTION_URL] = {&parse_section_url, "url"},
|
||||
[SECTION_MSG] = {&parse_section_msg, "msg"},
|
||||
[SECTION_REGEX] = {&parse_section_regex, "regex", true},
|
||||
[SECTION_COLORS] = {&parse_section_colors, "colors"},
|
||||
[SECTION_COLORS2] = {&parse_section_colors2, "colors2"},
|
||||
|
|
@ -2967,6 +3025,7 @@ static const struct {
|
|||
[SECTION_KEY_BINDINGS] = {&parse_section_key_bindings, "key-bindings"},
|
||||
[SECTION_SEARCH_BINDINGS] = {&parse_section_search_bindings, "search-bindings"},
|
||||
[SECTION_URL_BINDINGS] = {&parse_section_url_bindings, "url-bindings"},
|
||||
[SECTION_MSG_BINDINGS] = {&parse_section_msg_bindings, "msg-bindings"},
|
||||
[SECTION_MOUSE_BINDINGS] = {&parse_section_mouse_bindings, "mouse-bindings"},
|
||||
[SECTION_TEXT_BINDINGS] = {&parse_section_text_bindings, "text-bindings"},
|
||||
[SECTION_ENVIRONMENT] = {&parse_section_environment, "environment"},
|
||||
|
|
@ -3293,6 +3352,24 @@ add_default_url_bindings(struct config *conf)
|
|||
conf->bindings.url.arr = xmemdup(bindings, sizeof(bindings));
|
||||
}
|
||||
|
||||
static void
|
||||
add_default_msg_bindings(struct config *conf)
|
||||
{
|
||||
const struct config_key_binding bindings[] = {
|
||||
{BIND_ACTION_CLOSE_MESSAGE_CANCEL, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_c}}},
|
||||
{BIND_ACTION_CLOSE_MESSAGE_CANCEL, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_g}}},
|
||||
{BIND_ACTION_CLOSE_MESSAGE_CANCEL, m(XKB_MOD_NAME_CTRL), {{XKB_KEY_d}}},
|
||||
{BIND_ACTION_CLOSE_MESSAGE_CANCEL, m("none"), {{XKB_KEY_Escape}}},
|
||||
{BIND_ACTION_CLOSE_MESSAGE_CANCEL, m("none"), {{XKB_KEY_n}}},
|
||||
{BIND_ACTION_CLOSE_MESSAGE_ACCEPT, m("none"), {{XKB_KEY_q}}},
|
||||
{BIND_ACTION_CLOSE_MESSAGE_ACCEPT, m("none"), {{XKB_KEY_y}}},
|
||||
{BIND_ACTION_CLOSE_MESSAGE_ACCEPT, m("none"), {{XKB_KEY_Return}}},
|
||||
};
|
||||
|
||||
conf->bindings.msg.count = ALEN(bindings);
|
||||
conf->bindings.msg.arr = xmemdup(bindings, sizeof(bindings));
|
||||
}
|
||||
|
||||
static void
|
||||
add_default_mouse_bindings(struct config *conf)
|
||||
{
|
||||
|
|
@ -3353,6 +3430,7 @@ config_load(struct config *conf, const char *conf_path,
|
|||
.pad_x = 0,
|
||||
.pad_y = 0,
|
||||
.center_when = CENTER_MAXIMIZED_AND_FULLSCREEN,
|
||||
.close_policy = CLOSE_ALWAYS,
|
||||
.resize_by_cells = true,
|
||||
.resize_keep_grid = true,
|
||||
.resize_delay_ms = 100,
|
||||
|
|
@ -3420,6 +3498,7 @@ config_load(struct config *conf, const char *conf_path,
|
|||
.jump_label = false,
|
||||
.scrollback_indicator = false,
|
||||
.url = false,
|
||||
.msg_style = false,
|
||||
},
|
||||
},
|
||||
.initial_color_theme = COLOR_THEME1,
|
||||
|
|
@ -3567,6 +3646,7 @@ config_load(struct config *conf, const char *conf_path,
|
|||
add_default_key_bindings(conf);
|
||||
add_default_search_bindings(conf);
|
||||
add_default_url_bindings(conf);
|
||||
add_default_msg_bindings(conf);
|
||||
add_default_mouse_bindings(conf);
|
||||
|
||||
struct config_file conf_file = {.path = NULL, .fd = -1};
|
||||
|
|
@ -3628,6 +3708,8 @@ config_load(struct config *conf, const char *conf_path,
|
|||
xassert(conf->bindings.search.arr[i].action != BIND_ACTION_SEARCH_NONE);
|
||||
for (size_t i = 0; i < conf->bindings.url.count; i++)
|
||||
xassert(conf->bindings.url.arr[i].action != BIND_ACTION_URL_NONE);
|
||||
for (size_t i = 0; i < conf->bindings.msg.count; i++)
|
||||
xassert(conf->bindings.msg.arr[i].action != BIND_ACTION_CLOSE_MESSAGE_NONE);
|
||||
#endif
|
||||
|
||||
free(conf_file.path);
|
||||
|
|
@ -3706,6 +3788,9 @@ config_override_apply(struct config *conf, config_override_t *overrides,
|
|||
resolve_key_binding_collisions(
|
||||
conf, section_info[SECTION_URL_BINDINGS].name,
|
||||
url_binding_action_map, &conf->bindings.url, KEY_BINDING) &&
|
||||
resolve_key_binding_collisions(
|
||||
conf, section_info[SECTION_MSG_BINDINGS].name,
|
||||
msg_binding_action_map, &conf->bindings.msg, KEY_BINDING) &&
|
||||
resolve_key_binding_collisions(
|
||||
conf, section_info[SECTION_MOUSE_BINDINGS].name,
|
||||
binding_action_map, &conf->bindings.mouse, MOUSE_BINDING);
|
||||
|
|
@ -3827,6 +3912,7 @@ 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.url, &old->bindings.url);
|
||||
key_binding_list_clone(&conf->bindings.msg, &old->bindings.msg);
|
||||
key_binding_list_clone(&conf->bindings.mouse, &old->bindings.mouse);
|
||||
|
||||
conf->env_vars.length = 0;
|
||||
|
|
@ -3918,6 +4004,7 @@ 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.url);
|
||||
free_key_binding_list(&conf->bindings.msg);
|
||||
free_key_binding_list(&conf->bindings.mouse);
|
||||
tll_free_and_free(conf->mouse.selection_override_modifiers, free);
|
||||
|
||||
|
|
|
|||
16
config.h
16
config.h
|
|
@ -161,6 +161,11 @@ struct color_theme {
|
|||
uint32_t bg;
|
||||
} jump_label;
|
||||
|
||||
struct {
|
||||
uint32_t fg;
|
||||
uint32_t bg;
|
||||
} msg_style;
|
||||
|
||||
struct {
|
||||
uint32_t fg;
|
||||
uint32_t bg;
|
||||
|
|
@ -183,6 +188,7 @@ struct color_theme {
|
|||
bool jump_label:1;
|
||||
bool scrollback_indicator:1;
|
||||
bool url:1;
|
||||
bool msg_style:1;
|
||||
bool search_box_no_match:1;
|
||||
bool search_box_match:1;
|
||||
uint8_t dim;
|
||||
|
|
@ -209,6 +215,13 @@ enum center_when {
|
|||
CENTER_ALWAYS,
|
||||
};
|
||||
|
||||
enum close_policy {
|
||||
CLOSE_ALWAYS,
|
||||
CLOSE_NEVER,
|
||||
CLOSE_MESSAGE_ALWAYS,
|
||||
CLOSE_MESSAGE_DEMAND,
|
||||
};
|
||||
|
||||
struct config {
|
||||
char *term;
|
||||
char *shell;
|
||||
|
|
@ -227,6 +240,7 @@ struct config {
|
|||
unsigned pad_x;
|
||||
unsigned pad_y;
|
||||
enum center_when center_when;
|
||||
enum close_policy close_policy;
|
||||
|
||||
bool resize_by_cells;
|
||||
bool resize_keep_grid;
|
||||
|
|
@ -357,6 +371,8 @@ struct config {
|
|||
|
||||
/* While showing URL jump labels */
|
||||
struct config_key_binding_list url;
|
||||
|
||||
struct config_key_binding_list msg;
|
||||
} bindings;
|
||||
|
||||
struct {
|
||||
|
|
|
|||
1
csi.c
1
csi.c
|
|
@ -584,6 +584,7 @@ decset_decrst(struct terminal *term, unsigned param, bool enable)
|
|||
else {
|
||||
term_ime_disable(term);
|
||||
term->ime_reenable_after_url_mode = false;
|
||||
term->ime_reenable_after_msg_mode = false;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
|||
13
input.c
13
input.c
|
|
@ -38,6 +38,7 @@
|
|||
#include "tokenize.h"
|
||||
#include "unicode-mode.h"
|
||||
#include "url-mode.h"
|
||||
#include "message.h"
|
||||
#include "util.h"
|
||||
#include "vt.h"
|
||||
#include "xkbcommon-vmod.h"
|
||||
|
|
@ -1632,7 +1633,17 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
|
|||
xassert(bindings != NULL);
|
||||
|
||||
if (pressed) {
|
||||
if (term->unicode_mode.active) {
|
||||
if (msgs_mode_is_active(term)) {
|
||||
if (should_repeat)
|
||||
start_repeater(seat, key);
|
||||
|
||||
msgs_input(
|
||||
seat, term, bindings, key, sym, mods, consumed,
|
||||
raw_syms, raw_count, serial);
|
||||
return;
|
||||
}
|
||||
|
||||
else if (term->unicode_mode.active) {
|
||||
unicode_mode_input(seat, term, sym);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ key_binding_new_for_seat(struct key_binding_manager *mgr,
|
|||
.key = tll_init(),
|
||||
.search = tll_init(),
|
||||
.url = tll_init(),
|
||||
.msg = tll_init(),
|
||||
.mouse = tll_init(),
|
||||
},
|
||||
.conf = it->item->conf,
|
||||
|
|
@ -154,6 +155,7 @@ key_binding_new_for_conf(struct key_binding_manager *mgr,
|
|||
.key = tll_init(),
|
||||
.search = tll_init(),
|
||||
.url = tll_init(),
|
||||
.msg = tll_init(),
|
||||
.mouse = tll_init(),
|
||||
},
|
||||
.conf = conf,
|
||||
|
|
@ -553,6 +555,17 @@ convert_url_bindings(struct key_set *set)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
convert_msg_bindings(struct key_set *set)
|
||||
{
|
||||
const struct config *conf = set->conf;
|
||||
|
||||
for (size_t i = 0; i < conf->bindings.msg.count; i++) {
|
||||
const struct config_key_binding *binding = &conf->bindings.msg.arr[i];
|
||||
convert_key_binding(set, binding, &set->public.msg);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
convert_mouse_binding(struct key_set *set,
|
||||
const struct config_key_binding *conf_binding)
|
||||
|
|
@ -599,6 +612,7 @@ load_keymap(struct key_set *set)
|
|||
convert_key_bindings(set);
|
||||
convert_search_bindings(set);
|
||||
convert_url_bindings(set);
|
||||
convert_msg_bindings(set);
|
||||
convert_mouse_bindings(set);
|
||||
|
||||
set->public.selection_overrides = mods_to_mask(
|
||||
|
|
@ -640,6 +654,7 @@ unload_keymap(struct key_set *set)
|
|||
key_bindings_destroy(&set->public.key);
|
||||
key_bindings_destroy(&set->public.search);
|
||||
key_bindings_destroy(&set->public.url);
|
||||
key_bindings_destroy(&set->public.msg);
|
||||
key_bindings_destroy(&set->public.mouse);
|
||||
set->public.selection_overrides = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,6 +110,13 @@ enum bind_action_url {
|
|||
BIND_ACTION_URL_COUNT,
|
||||
};
|
||||
|
||||
enum bind_action_close_message{
|
||||
BIND_ACTION_CLOSE_MESSAGE_NONE,
|
||||
BIND_ACTION_CLOSE_MESSAGE_CANCEL,
|
||||
BIND_ACTION_CLOSE_MESSAGE_ACCEPT,
|
||||
BIND_ACTION_MESSAGE_COUNT,
|
||||
};
|
||||
|
||||
typedef tll(xkb_keycode_t) xkb_keycode_list_t;
|
||||
|
||||
struct key_binding {
|
||||
|
|
@ -142,6 +149,7 @@ struct key_binding_set {
|
|||
key_binding_list_t key;
|
||||
key_binding_list_t search;
|
||||
key_binding_list_t url;
|
||||
key_binding_list_t msg;
|
||||
key_binding_list_t mouse;
|
||||
xkb_mod_mask_t selection_overrides;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -323,6 +323,7 @@ executable(
|
|||
'tokenize.c', 'tokenize.h',
|
||||
'unicode-mode.c', 'unicode-mode.h',
|
||||
'url-mode.c', 'url-mode.h',
|
||||
'message.c', 'message.h',
|
||||
'user-notification.c', 'user-notification.h',
|
||||
'wayland.c', 'wayland.h', 'shm-formats.h',
|
||||
'xkbcommon-vmod.h',
|
||||
|
|
|
|||
185
message.c
Normal file
185
message.c
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
#include "message.h"
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wctype.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define LOG_MODULE "message"
|
||||
#define LOG_ENABLE_DBG 0
|
||||
#include "log.h"
|
||||
#include "char32.h"
|
||||
#include "key-binding.h"
|
||||
#include "quirks.h"
|
||||
#include "render.h"
|
||||
#include "terminal.h"
|
||||
#include "util.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
|
||||
static bool
|
||||
execute_binding(struct seat *seat, struct terminal *term,
|
||||
const struct key_binding *binding, uint32_t serial)
|
||||
{
|
||||
const enum bind_action_close_message action = binding->action;
|
||||
|
||||
switch (action) {
|
||||
case BIND_ACTION_CLOSE_MESSAGE_NONE:
|
||||
return false;
|
||||
|
||||
case BIND_ACTION_CLOSE_MESSAGE_CANCEL:
|
||||
msgs_reset(term);
|
||||
return true;
|
||||
|
||||
case BIND_ACTION_CLOSE_MESSAGE_ACCEPT:
|
||||
term_shutdown(term);
|
||||
return true;
|
||||
|
||||
case BIND_ACTION_MESSAGE_COUNT:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
msgs_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)
|
||||
{
|
||||
|
||||
tll_foreach(bindings->msg, it) {
|
||||
const struct key_binding *bind = &it->item;
|
||||
if (bind->mods != mods || bind->mods == 0)
|
||||
continue;
|
||||
|
||||
for (size_t i = 0; i < raw_count; i++) {
|
||||
if (bind->k.sym == raw_syms[i]) {
|
||||
execute_binding(seat, term, bind, serial);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Match translated symbol */
|
||||
tll_foreach(bindings->msg, it) {
|
||||
const struct key_binding *bind = &it->item;
|
||||
|
||||
if (bind->k.sym == sym &&
|
||||
bind->mods == (mods & ~consumed))
|
||||
{
|
||||
execute_binding(seat, term, bind, serial);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Match raw key code */
|
||||
tll_foreach(bindings->msg, it) {
|
||||
const struct key_binding *bind = &it->item;
|
||||
if (bind->mods != mods || bind->mods == 0)
|
||||
continue;
|
||||
|
||||
/* Match raw key code */
|
||||
tll_foreach(bind->k.key_codes, code) {
|
||||
if (code->item == key) {
|
||||
execute_binding(seat, term, bind, serial);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
close_message(struct terminal *term)
|
||||
{
|
||||
char *strs[] = {"Do you want to close this client?", "Confirm or cancel."};
|
||||
for (int i=0; i<2; i++){
|
||||
tll_push_back(term->msgs, ((struct msg){
|
||||
.id = (uint64_t)rand() << 32 | rand(),
|
||||
.text = ambstoc32(strs[i]),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
msgs_render(struct terminal *term)
|
||||
{
|
||||
struct wl_window *win = term->window;
|
||||
|
||||
if (tll_length(win->term->msgs) == 0)
|
||||
return;
|
||||
|
||||
if (term_ime_is_enabled(term)) {
|
||||
term->ime_reenable_after_msg_mode = true;
|
||||
term_ime_disable(term);
|
||||
}
|
||||
|
||||
/* Dirty the last cursor, to ensure it is erased */
|
||||
{
|
||||
struct row *cursor_row = term->render.last_cursor.row;
|
||||
if (cursor_row != NULL) {
|
||||
struct cell *cell = &cursor_row->cells[term->render.last_cursor.col];
|
||||
cell->attrs.clean = 0;
|
||||
cursor_row->dirty = true;
|
||||
}
|
||||
}
|
||||
term->render.last_cursor.row = NULL;
|
||||
|
||||
/* Clear scroll damage, to ensure we don't apply it twice (once on
|
||||
* the snapshot:ed grid, and then later again on the real grid) */
|
||||
tll_free(term->grid->scroll_damage);
|
||||
|
||||
term_damage_view(term);
|
||||
|
||||
|
||||
xassert(tll_length(win->msgs) == 0);
|
||||
tll_foreach(win->term->msgs, it) {
|
||||
struct wl_msg msg = {.msg = &it->item};
|
||||
wayl_win_subsurface_new(win, &msg.surf, false);
|
||||
|
||||
tll_push_back(win->msgs, msg);
|
||||
}
|
||||
|
||||
render_refresh_msgs(term);
|
||||
render_refresh(term);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
msg_destroy(struct msg *msg)
|
||||
{
|
||||
free(msg->text);
|
||||
}
|
||||
|
||||
void
|
||||
msgs_reset(struct terminal *term)
|
||||
{
|
||||
term->render.last_cursor.row = NULL;
|
||||
|
||||
if (term->window != NULL) {
|
||||
tll_foreach(term->window->msgs, it) {
|
||||
wayl_win_subsurface_destroy(&it->item.surf);
|
||||
tll_remove(term->window->msgs, it);
|
||||
}
|
||||
}
|
||||
|
||||
tll_foreach(term->msgs, it) {
|
||||
msg_destroy(&it->item);
|
||||
tll_remove(term->msgs, it);
|
||||
}
|
||||
|
||||
/* Re-enable IME, if it was enabled before we entered URL-mode */
|
||||
if (term->ime_reenable_after_msg_mode) {
|
||||
term->ime_reenable_after_msg_mode = false;
|
||||
term_ime_enable(term);
|
||||
}
|
||||
|
||||
render_refresh(term);
|
||||
}
|
||||
25
message.h
Normal file
25
message.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <tllist.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "key-binding.h"
|
||||
#include "terminal.h"
|
||||
|
||||
static inline bool msgs_mode_is_active(const struct terminal *term)
|
||||
{
|
||||
return tll_length(term->msgs) > 0;
|
||||
}
|
||||
|
||||
void close_message(struct terminal *term);
|
||||
void msgs_reset(struct terminal *term);
|
||||
void msgs_render(struct terminal *term);
|
||||
|
||||
void
|
||||
msgs_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);
|
||||
164
render.c
164
render.c
|
|
@ -12,6 +12,7 @@
|
|||
#include <pthread.h>
|
||||
|
||||
#include "macros.h"
|
||||
#include "terminal.h"
|
||||
#if HAS_INCLUDE(<pthread_np.h>)
|
||||
#include <pthread_np.h>
|
||||
#define pthread_setname_np(thread, name) (pthread_set_name_np(thread, name), 0)
|
||||
|
|
@ -42,6 +43,7 @@
|
|||
#include "sixel.h"
|
||||
#include "srgb.h"
|
||||
#include "url-mode.h"
|
||||
#include "message.h"
|
||||
#include "util.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
|
|
@ -1959,6 +1961,7 @@ render_overlay(struct terminal *term)
|
|||
|
||||
const enum overlay_style style =
|
||||
term->is_searching ? OVERLAY_SEARCH :
|
||||
msgs_mode_is_active(term) ? OVERLAY_MESSAGE :
|
||||
term->flash.active ? OVERLAY_FLASH :
|
||||
unicode_mode_active ? OVERLAY_UNICODE_MODE :
|
||||
OVERLAY_NONE;
|
||||
|
|
@ -1978,6 +1981,7 @@ render_overlay(struct terminal *term)
|
|||
|
||||
switch (style) {
|
||||
case OVERLAY_SEARCH:
|
||||
case OVERLAY_MESSAGE:
|
||||
case OVERLAY_UNICODE_MODE:
|
||||
color = (pixman_color_t){0, 0, 0, 0x7fff};
|
||||
break;
|
||||
|
|
@ -4175,6 +4179,143 @@ render_urls(struct terminal *term)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
render_msgs(struct terminal *term)
|
||||
{
|
||||
struct wl_window *win = term->window;
|
||||
xassert(tll_length(win->msgs) > 0);
|
||||
|
||||
const float scale = term->scale;
|
||||
const int x_margin = (int)roundf(2 * scale);
|
||||
const int y_margin = (int)roundf(1 * scale);
|
||||
|
||||
struct {
|
||||
const struct wl_msg *msg;
|
||||
char32_t *text;
|
||||
int x;
|
||||
int y;
|
||||
} info[tll_length(win->msgs)];
|
||||
|
||||
/* For shm_get_many() */
|
||||
int widths[tll_length(win->msgs)];
|
||||
int heights[tll_length(win->msgs)];
|
||||
|
||||
int render_count = 0;
|
||||
int row = term->rows - 1 - tll_length(win->msgs);
|
||||
const int max_width = term->width - term->margins.left - term->margins.right;
|
||||
const int max_cols = max_width / term->cell_width;
|
||||
|
||||
tll_foreach(win->msgs, it) {
|
||||
const struct msg *msg = it->item.msg;
|
||||
const char32_t *text = msg->text;
|
||||
|
||||
struct wl_surface *surf = it->item.surf.surface.surf;
|
||||
struct wl_subsurface *sub_surf = it->item.surf.sub;
|
||||
|
||||
if (surf == NULL || sub_surf == NULL)
|
||||
continue;
|
||||
|
||||
if (row < 0) {
|
||||
wl_surface_attach(surf, NULL, 0, 0);
|
||||
wl_surface_commit(surf);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
const size_t text_len = c32len(text);
|
||||
char32_t label[text_len + 1];
|
||||
label[text_len] = U'\0';
|
||||
c32ncpy(label, text, text_len);
|
||||
|
||||
|
||||
int col = max_cols - c32swidth(label, c32len(label)) - 1;
|
||||
if (col < 0)
|
||||
col = 0;
|
||||
int x = col * term->cell_width;
|
||||
int y = row * term->cell_height;
|
||||
|
||||
/* Don't position it outside our window */
|
||||
if (x < -term->margins.left)
|
||||
x = -term->margins.left;
|
||||
if (y < -term->margins.top)
|
||||
y = -term->margins.top;
|
||||
|
||||
|
||||
int cols = 0;
|
||||
|
||||
for (size_t i = 0; i <= c32len(label); i++) {
|
||||
int _cols = c32swidth(label, i);
|
||||
|
||||
if (_cols == (size_t)-1)
|
||||
continue;
|
||||
|
||||
if (_cols >= max_cols) {
|
||||
if (i > 0)
|
||||
label[i - 1] = U'…';
|
||||
label[i] = U'\0';
|
||||
cols = max_cols;
|
||||
break;
|
||||
}
|
||||
cols = _cols;
|
||||
}
|
||||
|
||||
if (cols == 0)
|
||||
continue;
|
||||
|
||||
int width = x_margin + cols * term->cell_width + x_margin;
|
||||
int height = y_margin + term->cell_height + y_margin;
|
||||
|
||||
width = roundf(scale * ceilf(width / scale));
|
||||
height = roundf(scale * ceilf(height / scale));
|
||||
|
||||
info[render_count].msg = &it->item;
|
||||
info[render_count].text = xc32dup(label);
|
||||
info[render_count].x = x;
|
||||
info[render_count].y = y;
|
||||
|
||||
widths[render_count] = width;
|
||||
heights[render_count] = height;
|
||||
|
||||
row++;
|
||||
render_count++;
|
||||
}
|
||||
|
||||
struct buffer_chain *chain = term->render.chains.msg;
|
||||
struct buffer *bufs[render_count];
|
||||
shm_get_many(chain, render_count, widths, heights, bufs, false);
|
||||
|
||||
render_overlay(term);
|
||||
|
||||
uint32_t fg = term->conf->colors.use_custom.msg_style
|
||||
? term->conf->colors.msg_style.fg
|
||||
: term->colors.table[0];
|
||||
uint32_t bg = term->conf->colors.use_custom.msg_style
|
||||
? term->conf->colors.msg_style.bg
|
||||
: term->colors.table[3];
|
||||
|
||||
for (size_t i = 0; i < render_count; i++) {
|
||||
const struct wayl_sub_surface *sub_surf = &info[i].msg->surf;
|
||||
|
||||
const char32_t *label = info[i].text;
|
||||
const int x = info[i].x;
|
||||
const int y = info[i].y;
|
||||
|
||||
xassert(sub_surf->surface.surf != NULL);
|
||||
xassert(sub_surf->sub != NULL);
|
||||
|
||||
wl_subsurface_set_position(
|
||||
sub_surf->sub,
|
||||
roundf((term->margins.left + x) / scale),
|
||||
roundf((term->margins.top + y) / scale));
|
||||
|
||||
render_osd(
|
||||
term, sub_surf, term->fonts[0], bufs[i], label,
|
||||
fg, 0xffu << 24 | bg, x_margin);
|
||||
|
||||
free(info[i].text);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
render_update_title(struct terminal *term)
|
||||
{
|
||||
|
|
@ -4205,11 +4346,13 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da
|
|||
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 msgs = msgs_mode_is_active(term) > 0 && term->render.pending.msgs;
|
||||
|
||||
term->render.pending.grid = false;
|
||||
term->render.pending.csd = false;
|
||||
term->render.pending.search = false;
|
||||
term->render.pending.urls = false;
|
||||
term->render.pending.msgs = false;
|
||||
|
||||
struct grid *original_grid = term->grid;
|
||||
if (urls_mode_is_active(term)) {
|
||||
|
|
@ -4229,6 +4372,9 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da
|
|||
if (urls)
|
||||
render_urls(term);
|
||||
|
||||
if (msgs)
|
||||
render_msgs(term);
|
||||
|
||||
if ((grid && !term->delayed_render_timer.is_armed) || (csd | search | urls))
|
||||
grid_render(term);
|
||||
|
||||
|
|
@ -4812,6 +4958,8 @@ render_resize(struct terminal *term, int width, int height, uint8_t opts)
|
|||
|
||||
term->render.last_cursor.row = NULL;
|
||||
|
||||
render_refresh_msgs(term);
|
||||
|
||||
damage_view:
|
||||
/* Signal TIOCSWINSZ */
|
||||
send_dimensions_to_client(term);
|
||||
|
|
@ -5022,8 +5170,9 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
|
|||
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 msgs = msgs_mode_is_active(term) && term->render.refresh.msgs;
|
||||
|
||||
if (!(grid | csd | search | urls))
|
||||
if (!(grid | csd | search | urls | msgs))
|
||||
continue;
|
||||
|
||||
if (term->render.app_sync_updates.enabled && !(csd | search | urls))
|
||||
|
|
@ -5033,6 +5182,7 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
|
|||
term->render.refresh.csd = false;
|
||||
term->render.refresh.search = false;
|
||||
term->render.refresh.urls = false;
|
||||
term->render.refresh.msgs = false;
|
||||
|
||||
if (term->window->frame_callback == NULL) {
|
||||
struct grid *original_grid = term->grid;
|
||||
|
|
@ -5050,7 +5200,9 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
|
|||
render_search_box(term);
|
||||
if (urls)
|
||||
render_urls(term);
|
||||
if (grid | csd | search | urls)
|
||||
if (msgs)
|
||||
render_msgs(term);
|
||||
if (grid | csd | search | urls | msgs)
|
||||
grid_render(term);
|
||||
|
||||
tll_foreach(term->wl->seats, it) {
|
||||
|
|
@ -5065,6 +5217,7 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
|
|||
term->render.pending.csd |= csd;
|
||||
term->render.pending.search |= search;
|
||||
term->render.pending.urls |= urls;
|
||||
term->render.pending.msgs |= msgs;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5195,6 +5348,13 @@ render_refresh_urls(struct terminal *term)
|
|||
term->render.refresh.urls = true;
|
||||
}
|
||||
|
||||
void
|
||||
render_refresh_msgs(struct terminal *term)
|
||||
{
|
||||
if (msgs_mode_is_active(term))
|
||||
term->render.refresh.msgs = true;
|
||||
}
|
||||
|
||||
bool
|
||||
render_xcursor_set(struct seat *seat, struct terminal *term,
|
||||
enum cursor_shape shape)
|
||||
|
|
|
|||
1
render.h
1
render.h
|
|
@ -27,6 +27,7 @@ void render_refresh_csd(struct terminal *term);
|
|||
void render_refresh_search(struct terminal *term);
|
||||
void render_refresh_title(struct terminal *term);
|
||||
void render_refresh_urls(struct terminal *term);
|
||||
void render_refresh_msgs(struct terminal *term);
|
||||
bool render_xcursor_set(
|
||||
struct seat *seat, struct terminal *term, enum cursor_shape shape);
|
||||
bool render_xcursor_is_valid(const struct seat *seat, const char *cursor);
|
||||
|
|
|
|||
|
|
@ -1361,6 +1361,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
|||
.scrollback_indicator = shm_chain_new(wayl, false, 1, desired_bit_depth),
|
||||
.render_timer = shm_chain_new(wayl, false, 1, desired_bit_depth),
|
||||
.url = shm_chain_new(wayl, false, 1, desired_bit_depth),
|
||||
.msg = shm_chain_new(wayl, false, 1, desired_bit_depth),
|
||||
.csd = shm_chain_new(wayl, false, 1, desired_bit_depth),
|
||||
.overlay = shm_chain_new(wayl, false, 1, desired_bit_depth),
|
||||
},
|
||||
|
|
@ -1905,6 +1906,7 @@ term_destroy(struct terminal *term)
|
|||
shm_chain_free(term->render.chains.scrollback_indicator);
|
||||
shm_chain_free(term->render.chains.render_timer);
|
||||
shm_chain_free(term->render.chains.url);
|
||||
shm_chain_free(term->render.chains.msg);
|
||||
shm_chain_free(term->render.chains.csd);
|
||||
shm_chain_free(term->render.chains.overlay);
|
||||
pixman_region32_fini(&term->render.last_overlay_clip);
|
||||
|
|
|
|||
14
terminal.h
14
terminal.h
|
|
@ -375,6 +375,7 @@ enum term_surface {
|
|||
enum overlay_style {
|
||||
OVERLAY_NONE,
|
||||
OVERLAY_SEARCH,
|
||||
OVERLAY_MESSAGE,
|
||||
OVERLAY_FLASH,
|
||||
OVERLAY_UNICODE_MODE,
|
||||
};
|
||||
|
|
@ -394,6 +395,13 @@ struct url {
|
|||
};
|
||||
typedef tll(struct url) url_list_t;
|
||||
|
||||
enum close_message_action {CLOSE_MESSAGE_ACCEPT};
|
||||
struct msg {
|
||||
uint64_t id;
|
||||
char32_t *text;
|
||||
};
|
||||
typedef tll(struct msg) msg_list_t;
|
||||
|
||||
|
||||
struct colors {
|
||||
uint32_t fg;
|
||||
|
|
@ -652,6 +660,7 @@ struct terminal {
|
|||
struct buffer_chain *scrollback_indicator;
|
||||
struct buffer_chain *render_timer;
|
||||
struct buffer_chain *url;
|
||||
struct buffer_chain *msg;
|
||||
struct buffer_chain *csd;
|
||||
struct buffer_chain *overlay;
|
||||
} chains;
|
||||
|
|
@ -662,6 +671,7 @@ struct terminal {
|
|||
bool csd;
|
||||
bool search;
|
||||
bool urls;
|
||||
bool msgs;
|
||||
} refresh;
|
||||
|
||||
/* Scheduled for rendering, in the next frame callback */
|
||||
|
|
@ -670,6 +680,7 @@ struct terminal {
|
|||
bool csd;
|
||||
bool search;
|
||||
bool urls;
|
||||
bool msgs;
|
||||
} pending;
|
||||
|
||||
bool margins; /* Someone explicitly requested a refresh of the margins */
|
||||
|
|
@ -797,6 +808,9 @@ struct terminal {
|
|||
bool ime_reenable_after_url_mode;
|
||||
const struct config_spawn_template *url_launch;
|
||||
|
||||
msg_list_t msgs;
|
||||
bool ime_reenable_after_msg_mode;
|
||||
|
||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||
bool ime_enabled;
|
||||
#endif
|
||||
|
|
|
|||
67
wayland.c
67
wayland.c
|
|
@ -34,6 +34,7 @@
|
|||
#include "shm-formats.h"
|
||||
#include "util.h"
|
||||
#include "xmalloc.h"
|
||||
#include "message.h"
|
||||
|
||||
static void
|
||||
csd_reload_font(struct wl_window *win, float old_scale)
|
||||
|
|
@ -958,8 +959,61 @@ xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
|
|||
{
|
||||
struct wl_window *win = data;
|
||||
struct terminal *term = win->term;
|
||||
|
||||
LOG_DBG("xdg-toplevel: close");
|
||||
term_shutdown(term);
|
||||
|
||||
switch (term->conf->close_policy){
|
||||
case CLOSE_ALWAYS:
|
||||
term_shutdown(term);
|
||||
case CLOSE_NEVER:
|
||||
break;
|
||||
case CLOSE_MESSAGE_ALWAYS:
|
||||
if (!msgs_mode_is_active(term)){
|
||||
close_message(term);
|
||||
msgs_render(term);
|
||||
}
|
||||
break;
|
||||
case CLOSE_MESSAGE_DEMAND: ;
|
||||
/* ifs here check whether it's safe to close */
|
||||
pid_t fgpid = tcgetpgrp(term->ptmx);
|
||||
|
||||
|
||||
/* No foreground process ( --hold mode) */
|
||||
if (fgpid < 0){
|
||||
term_shutdown(term);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* foreground process is the slave process which typcally is
|
||||
* of some shell kind. Therefore shell integration helps to
|
||||
* ensure that it's really a shell.
|
||||
*/
|
||||
|
||||
/* Shells or meant slave process should communicate with
|
||||
* terminal whether they are ok to be therminated or not?
|
||||
* for shells, checking if they have child processes or not
|
||||
* should be enough. This probably can be done through
|
||||
* shell integration, informing their jobs status.
|
||||
*/
|
||||
|
||||
// TODO multi line prompt_marker ???
|
||||
bool at_prompt = term->grid->cur_row->shell_integration.prompt_marker;
|
||||
|
||||
if (fgpid == term->slave && at_prompt){
|
||||
// TODO make sure there's no background jobs
|
||||
term_shutdown(term);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* by here, a close dialog should be necessary */
|
||||
if (!msgs_mode_is_active(term)) {
|
||||
close_message(term);
|
||||
msgs_render(term);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION)
|
||||
|
|
@ -2119,6 +2173,11 @@ wayl_win_destroy(struct wl_window *win)
|
|||
wl_surface_commit(it->item.surf.surface.surf);
|
||||
}
|
||||
|
||||
tll_foreach(win->msgs, it) {
|
||||
wl_surface_attach(it->item.surf.surface.surf, NULL, 0, 0);
|
||||
wl_surface_commit(it->item.surf.surface.surf);
|
||||
}
|
||||
|
||||
/* CSD */
|
||||
for (size_t i = 0; i < ALEN(win->csd.surface); i++) {
|
||||
if (win->csd.surface[i].surface.surf != NULL) {
|
||||
|
|
@ -2142,6 +2201,11 @@ wayl_win_destroy(struct wl_window *win)
|
|||
tll_remove(win->urls, it);
|
||||
}
|
||||
|
||||
tll_foreach(win->msgs, it) {
|
||||
wayl_win_subsurface_destroy(&it->item.surf);
|
||||
tll_remove(win->msgs, it);
|
||||
}
|
||||
|
||||
csd_destroy(win);
|
||||
wayl_win_subsurface_destroy(&win->search);
|
||||
wayl_win_subsurface_destroy(&win->scrollback_indicator);
|
||||
|
|
@ -2153,6 +2217,7 @@ wayl_win_destroy(struct wl_window *win)
|
|||
shm_purge(term->render.chains.render_timer);
|
||||
shm_purge(term->render.chains.grid);
|
||||
shm_purge(term->render.chains.url);
|
||||
shm_purge(term->render.chains.msg);
|
||||
shm_purge(term->render.chains.csd);
|
||||
|
||||
tll_foreach(win->xdg_tokens, it) {
|
||||
|
|
|
|||
|
|
@ -337,6 +337,11 @@ struct wl_url {
|
|||
struct wayl_sub_surface surf;
|
||||
};
|
||||
|
||||
struct wl_msg {
|
||||
const struct msg *msg;
|
||||
struct wayl_sub_surface surf;
|
||||
};
|
||||
|
||||
enum csd_mode {CSD_UNKNOWN, CSD_NO, CSD_YES};
|
||||
|
||||
typedef void (*activation_token_cb_t)(const char *token, void *data);
|
||||
|
|
@ -393,6 +398,7 @@ struct wl_window {
|
|||
|
||||
tll(const struct monitor *) on_outputs; /* Outputs we're mapped on */
|
||||
tll(struct wl_url) urls;
|
||||
tll(struct wl_msg) msgs;
|
||||
|
||||
bool is_configured;
|
||||
bool is_fullscreen;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue