feat: clsoe dialog

This commit is contained in:
saeedark 2025-09-17 13:47:19 +03:30
parent 72d9a13c0c
commit 41ed7a81a2
15 changed files with 601 additions and 4 deletions

View file

@ -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);

View file

@ -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
View file

@ -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
View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
};

View file

@ -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
View 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
View 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
View file

@ -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)

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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) {

View file

@ -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;