mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-04-12 08:21:02 -04:00
toplevel-icon: implement OSC-1, CSI 20/21/22/23 t
* The toplevel icon is now set to the app-id, unless "overridden" by OSC-1 or OSC-0. * Implemented OSC-1 * OSC-0 extended to also set the icon * Implemented CSI 20 t - report window icon * Implemented CSI 21 t - report window title * Implemented CSI 22 ; 1 t - push window icon * Implemented CS 23 ; 1 t - pop window icon * Extended CSI 22/23 ; 0 t to also push/pop the icon * Verify app-id set by OSC-176 is valid UTF-8 * Verify icon set by OSC-0/1 is valid UTF-8
This commit is contained in:
parent
3f8a1fc85b
commit
97ec375c67
10 changed files with 220 additions and 32 deletions
|
|
@ -62,6 +62,11 @@
|
||||||
([#1807][1807]).
|
([#1807][1807]).
|
||||||
* `strikeout-thickness` option.
|
* `strikeout-thickness` option.
|
||||||
* Implemented the new `xdg-toplevel-icon-v1` protocol.
|
* Implemented the new `xdg-toplevel-icon-v1` protocol.
|
||||||
|
* Implemented `CSI 20 t`: report window icon.
|
||||||
|
* Implemented `CSI 21 t`: report window title.
|
||||||
|
* Implemented `CSI 22 ; 1 t`: push window icon.
|
||||||
|
* Implemented `CSI 23 ; 1 t`: pop window icon.
|
||||||
|
* Implemented `OSC 1`: set window icon.
|
||||||
|
|
||||||
[1807]: https://codeberg.org/dnkl/foot/issues/1807
|
[1807]: https://codeberg.org/dnkl/foot/issues/1807
|
||||||
|
|
||||||
|
|
|
||||||
31
csi.c
31
csi.c
|
|
@ -1249,8 +1249,6 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
||||||
case 8: LOG_WARN("unimplemented: resize window in chars"); break;
|
case 8: LOG_WARN("unimplemented: resize window in chars"); break;
|
||||||
case 9: LOG_WARN("unimplemented: maximize/unmaximize window"); break;
|
case 9: LOG_WARN("unimplemented: maximize/unmaximize window"); break;
|
||||||
case 10: LOG_WARN("unimplemented: to/from full screen"); break;
|
case 10: LOG_WARN("unimplemented: to/from full screen"); break;
|
||||||
case 20: LOG_WARN("unimplemented: report icon label"); break;
|
|
||||||
case 21: LOG_WARN("unimplemented: report window title"); break;
|
|
||||||
case 24: LOG_WARN("unimplemented: resize window (DECSLPP)"); break;
|
case 24: LOG_WARN("unimplemented: resize window (DECSLPP)"); break;
|
||||||
|
|
||||||
case 11: /* report if window is iconified */
|
case 11: /* report if window is iconified */
|
||||||
|
|
@ -1354,6 +1352,24 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 20: {
|
||||||
|
const char *icon = term_icon(term);
|
||||||
|
|
||||||
|
char reply[3 + strlen(icon) + 2 + 1];
|
||||||
|
int chars = xsnprintf(
|
||||||
|
reply, sizeof(reply), "\033]L%s\033\\", icon);
|
||||||
|
term_to_slave(term, reply, chars);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 21: {
|
||||||
|
char reply[3 + strlen(term->window_title) + 2 + 1];
|
||||||
|
int chars = xsnprintf(
|
||||||
|
reply, sizeof(reply), "\033]l%s\033\\", term->window_title);
|
||||||
|
term_to_slave(term, reply, chars);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 22: { /* push window title */
|
case 22: { /* push window title */
|
||||||
/* 0 - icon + title, 1 - icon, 2 - title */
|
/* 0 - icon + title, 1 - icon, 2 - title */
|
||||||
unsigned what = vt_param_get(term, 1, 0);
|
unsigned what = vt_param_get(term, 1, 0);
|
||||||
|
|
@ -1361,6 +1377,10 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
||||||
tll_push_back(
|
tll_push_back(
|
||||||
term->window_title_stack, xstrdup(term->window_title));
|
term->window_title_stack, xstrdup(term->window_title));
|
||||||
}
|
}
|
||||||
|
if (what == 0 || what == 1) {
|
||||||
|
tll_push_back(
|
||||||
|
term->window_icon_stack, xstrdup(term->window_icon));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1374,6 +1394,13 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
||||||
free(title);
|
free(title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (what == 0 || what == 1) {
|
||||||
|
if (tll_length(term->window_icon_stack) > 0) {
|
||||||
|
char *icon = tll_pop_back(term->window_icon_stack);
|
||||||
|
term_set_icon(term, icon);
|
||||||
|
free(icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -388,15 +388,27 @@ manipulation sequences. The generic format is:
|
||||||
| 19
|
| 19
|
||||||
: -
|
: -
|
||||||
: Report screen size, in characters.
|
: Report screen size, in characters.
|
||||||
|
| 20
|
||||||
|
: -
|
||||||
|
: Report icon label.
|
||||||
|
| 21
|
||||||
|
: -
|
||||||
|
: Report window title.
|
||||||
| 22
|
| 22
|
||||||
: -
|
: -
|
||||||
: Push window title+icon. Foot does not support pushing the icon.
|
: Push window title+icon.
|
||||||
|
| 22
|
||||||
|
: 1
|
||||||
|
: Push window icon.
|
||||||
| 22
|
| 22
|
||||||
: 2
|
: 2
|
||||||
: Push window title.
|
: Push window title.
|
||||||
| 23
|
| 23
|
||||||
: -
|
: -
|
||||||
: Pop window title+icon. Foot does not support popping the icon.
|
: Pop window title+icon.
|
||||||
|
| 23
|
||||||
|
: 1
|
||||||
|
: Pop window icon.
|
||||||
| 23
|
| 23
|
||||||
: 2
|
: 2
|
||||||
: Pop window title.
|
: Pop window title.
|
||||||
|
|
@ -659,8 +671,10 @@ All _OSC_ sequences begin with *\\E]*, sometimes abbreviated _OSC_.
|
||||||
:< *Description*
|
:< *Description*
|
||||||
| \\E] 0 ; _Pt_ \\E\\
|
| \\E] 0 ; _Pt_ \\E\\
|
||||||
: xterm
|
: xterm
|
||||||
: Set window icon and title to _Pt_ (foot does not support setting the
|
: Set window icon and title to _Pt_.
|
||||||
icon)
|
| \\E] 1 ; _Pt_ \\E\\
|
||||||
|
: xterm
|
||||||
|
: Set window icon to _Pt_.
|
||||||
| \\E] 2 ; _Pt_ \\E\\
|
| \\E] 2 ; _Pt_ \\E\\
|
||||||
: xterm
|
: xterm
|
||||||
: Set window title to _Pt_
|
: Set window title to _Pt_
|
||||||
|
|
|
||||||
7
misc.c
7
misc.c
|
|
@ -42,3 +42,10 @@ timespec_sub(const struct timespec *a, const struct timespec *b,
|
||||||
res->tv_nsec += one_sec_in_ns;
|
res->tv_nsec += one_sec_in_ns;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
is_valid_utf8(const char *value)
|
||||||
|
{
|
||||||
|
return value != NULL &&
|
||||||
|
mbsntoc32(NULL, value, strlen(value), 0) != (size_t)-1;
|
||||||
|
}
|
||||||
|
|
|
||||||
2
misc.h
2
misc.h
|
|
@ -8,3 +8,5 @@ bool isword(char32_t wc, bool spaces_only, const char32_t *delimiters);
|
||||||
|
|
||||||
void timespec_add(const struct timespec *a, const struct timespec *b, struct timespec *res);
|
void timespec_add(const struct timespec *a, const struct timespec *b, struct timespec *res);
|
||||||
void timespec_sub(const struct timespec *a, const struct timespec *b, struct timespec *res);
|
void timespec_sub(const struct timespec *a, const struct timespec *b, struct timespec *res);
|
||||||
|
|
||||||
|
bool is_valid_utf8(const char *value);
|
||||||
|
|
|
||||||
15
osc.c
15
osc.c
|
|
@ -1145,9 +1145,18 @@ osc_dispatch(struct terminal *term)
|
||||||
char *string = (char *)&term->vt.osc.data[data_ofs];
|
char *string = (char *)&term->vt.osc.data[data_ofs];
|
||||||
|
|
||||||
switch (param) {
|
switch (param) {
|
||||||
case 0: term_set_window_title(term, string); break; /* icon + title */
|
case 0: /* icon + title */
|
||||||
case 1: break; /* icon */
|
term_set_window_title(term, string);
|
||||||
case 2: term_set_window_title(term, string); break; /* title */
|
term_set_icon(term, string);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: /* icon */
|
||||||
|
term_set_icon(term, string);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: /* title */
|
||||||
|
term_set_window_title(term, string);
|
||||||
|
break;
|
||||||
|
|
||||||
case 4: {
|
case 4: {
|
||||||
/* Set color<idx> */
|
/* Set color<idx> */
|
||||||
|
|
|
||||||
69
render.c
69
render.c
|
|
@ -12,15 +12,19 @@
|
||||||
|
|
||||||
#include "macros.h"
|
#include "macros.h"
|
||||||
#if HAS_INCLUDE(<pthread_np.h>)
|
#if HAS_INCLUDE(<pthread_np.h>)
|
||||||
#include <pthread_np.h>
|
#include <pthread_np.h>
|
||||||
#define pthread_setname_np(thread, name) (pthread_set_name_np(thread, name), 0)
|
#define pthread_setname_np(thread, name) (pthread_set_name_np(thread, name), 0)
|
||||||
#elif defined(__NetBSD__)
|
#elif defined(__NetBSD__)
|
||||||
#define pthread_setname_np(thread, name) pthread_setname_np(thread, "%s", (void *)name)
|
#define pthread_setname_np(thread, name) pthread_setname_np(thread, "%s", (void *)name)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <presentation-time.h>
|
||||||
#include <wayland-cursor.h>
|
#include <wayland-cursor.h>
|
||||||
#include <xdg-shell.h>
|
#include <xdg-shell.h>
|
||||||
#include <presentation-time.h>
|
|
||||||
|
#if defined(HAVE_XDG_TOPLEVEL_ICON)
|
||||||
|
#include <xdg-toplevel-icon-v1.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <fcft/fcft.h>
|
#include <fcft/fcft.h>
|
||||||
|
|
||||||
|
|
@ -4951,26 +4955,49 @@ render_refresh_app_id(struct terminal *term)
|
||||||
term->app_id != NULL ? term->app_id : term->conf->app_id;
|
term->app_id != NULL ? term->app_id : term->conf->app_id;
|
||||||
|
|
||||||
xdg_toplevel_set_app_id(term->window->xdg_toplevel, app_id);
|
xdg_toplevel_set_app_id(term->window->xdg_toplevel, app_id);
|
||||||
|
|
||||||
#if defined(HAVE_XDG_TOPLEVEL_ICON)
|
|
||||||
if (term->wl->toplevel_icon_manager != NULL) {
|
|
||||||
struct xdg_toplevel_icon_v1 *icon =
|
|
||||||
xdg_toplevel_icon_manager_v1_create_icon(
|
|
||||||
term->wl->toplevel_icon_manager);
|
|
||||||
|
|
||||||
xdg_toplevel_icon_v1_set_name(
|
|
||||||
icon, streq(app_id, "footclient") ? "foot" : app_id);
|
|
||||||
|
|
||||||
xdg_toplevel_icon_manager_v1_set_icon(
|
|
||||||
term->wl->toplevel_icon_manager, term->window->xdg_toplevel, icon);
|
|
||||||
|
|
||||||
xdg_toplevel_icon_v1_destroy(icon);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
term->render.app_id.last_update = now;
|
term->render.app_id.last_update = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
render_refresh_icon(struct terminal *term)
|
||||||
|
{
|
||||||
|
#if defined(HAVE_XDG_TOPLEVEL_ICON)
|
||||||
|
if (term->wl->toplevel_icon_manager == NULL) {
|
||||||
|
LOG_DBG("compositor does not implement xdg-toplevel-icon: "
|
||||||
|
"ignoring request to refresh window icon");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct timespec now;
|
||||||
|
if (clock_gettime(CLOCK_MONOTONIC, &now) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct timespec diff;
|
||||||
|
timespec_sub(&now, &term->render.icon.last_update, &diff);
|
||||||
|
|
||||||
|
if (diff.tv_sec == 0 && diff.tv_nsec < 8333 * 1000) {
|
||||||
|
const struct itimerspec timeout = {
|
||||||
|
.it_value = {.tv_nsec = 8333 * 1000 - diff.tv_nsec},
|
||||||
|
};
|
||||||
|
|
||||||
|
timerfd_settime(term->render.icon.timer_fd, 0, &timeout, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *icon_name = term_icon(term);
|
||||||
|
LOG_DBG("setting toplevel icon: %s", icon_name);
|
||||||
|
|
||||||
|
struct xdg_toplevel_icon_v1 *icon =
|
||||||
|
xdg_toplevel_icon_manager_v1_create_icon(term->wl->toplevel_icon_manager);
|
||||||
|
xdg_toplevel_icon_v1_set_name(icon, icon_name);
|
||||||
|
xdg_toplevel_icon_manager_v1_set_icon(
|
||||||
|
term->wl->toplevel_icon_manager, term->window->xdg_toplevel, icon);
|
||||||
|
xdg_toplevel_icon_v1_destroy(icon);
|
||||||
|
|
||||||
|
term->render.icon.last_update = now;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
render_refresh(struct terminal *term)
|
render_refresh(struct terminal *term)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
1
render.h
1
render.h
|
|
@ -22,6 +22,7 @@ bool render_resize(
|
||||||
|
|
||||||
void render_refresh(struct terminal *term);
|
void render_refresh(struct terminal *term);
|
||||||
void render_refresh_app_id(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_csd(struct terminal *term);
|
||||||
void render_refresh_search(struct terminal *term);
|
void render_refresh_search(struct terminal *term);
|
||||||
void render_refresh_title(struct terminal *term);
|
void render_refresh_title(struct terminal *term);
|
||||||
|
|
|
||||||
91
terminal.c
91
terminal.c
|
|
@ -639,6 +639,30 @@ fdm_title_update_timeout(struct fdm *fdm, int fd, int events, void *data)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
fdm_icon_update_timeout(struct fdm *fdm, int fd, int events, void *data)
|
||||||
|
{
|
||||||
|
if (events & EPOLLHUP)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
struct terminal *term = data;
|
||||||
|
uint64_t unused;
|
||||||
|
ssize_t ret = read(term->render.icon.timer_fd, &unused, sizeof(unused));
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return true;
|
||||||
|
LOG_ERRNO("failed to read icon update throttle timer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct itimerspec reset = {{0}};
|
||||||
|
timerfd_settime(term->render.icon.timer_fd, 0, &reset, NULL);
|
||||||
|
|
||||||
|
render_refresh_icon(term);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
fdm_app_id_update_timeout(struct fdm *fdm, int fd, int events, void *data)
|
fdm_app_id_update_timeout(struct fdm *fdm, int fd, int events, void *data)
|
||||||
{
|
{
|
||||||
|
|
@ -1114,6 +1138,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
int delay_upper_fd = -1;
|
int delay_upper_fd = -1;
|
||||||
int app_sync_updates_fd = -1;
|
int app_sync_updates_fd = -1;
|
||||||
int title_update_fd = -1;
|
int title_update_fd = -1;
|
||||||
|
int icon_update_fd = -1;
|
||||||
int app_id_update_fd = -1;
|
int app_id_update_fd = -1;
|
||||||
|
|
||||||
struct terminal *term = malloc(sizeof(*term));
|
struct terminal *term = malloc(sizeof(*term));
|
||||||
|
|
@ -1150,6 +1175,12 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
goto close_fds;
|
goto close_fds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((icon_update_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)) < 0)
|
||||||
|
{
|
||||||
|
LOG_ERRNO("failed to create icon update throttle timer FD");
|
||||||
|
goto close_fds;
|
||||||
|
}
|
||||||
|
|
||||||
if ((app_id_update_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)) < 0)
|
if ((app_id_update_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)) < 0)
|
||||||
{
|
{
|
||||||
LOG_ERRNO("failed to create app ID update throttle timer FD");
|
LOG_ERRNO("failed to create app ID update throttle timer FD");
|
||||||
|
|
@ -1187,6 +1218,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
!fdm_add(fdm, delay_upper_fd, EPOLLIN, &fdm_delayed_render, term) ||
|
!fdm_add(fdm, delay_upper_fd, EPOLLIN, &fdm_delayed_render, term) ||
|
||||||
!fdm_add(fdm, app_sync_updates_fd, EPOLLIN, &fdm_app_sync_updates_timeout, term) ||
|
!fdm_add(fdm, app_sync_updates_fd, EPOLLIN, &fdm_app_sync_updates_timeout, term) ||
|
||||||
!fdm_add(fdm, title_update_fd, EPOLLIN, &fdm_title_update_timeout, term) ||
|
!fdm_add(fdm, title_update_fd, EPOLLIN, &fdm_title_update_timeout, term) ||
|
||||||
|
!fdm_add(fdm, icon_update_fd, EPOLLIN, &fdm_icon_update_timeout, term) ||
|
||||||
!fdm_add(fdm, app_id_update_fd, EPOLLIN, &fdm_app_id_update_timeout, term))
|
!fdm_add(fdm, app_id_update_fd, EPOLLIN, &fdm_app_id_update_timeout, term))
|
||||||
{
|
{
|
||||||
goto err;
|
goto err;
|
||||||
|
|
@ -1288,6 +1320,9 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
.title = {
|
.title = {
|
||||||
.timer_fd = title_update_fd,
|
.timer_fd = title_update_fd,
|
||||||
},
|
},
|
||||||
|
.icon = {
|
||||||
|
.timer_fd = icon_update_fd,
|
||||||
|
},
|
||||||
.app_id = {
|
.app_id = {
|
||||||
.timer_fd = app_id_update_fd,
|
.timer_fd = app_id_update_fd,
|
||||||
},
|
},
|
||||||
|
|
@ -1406,6 +1441,7 @@ close_fds:
|
||||||
fdm_del(fdm, delay_upper_fd);
|
fdm_del(fdm, delay_upper_fd);
|
||||||
fdm_del(fdm, app_sync_updates_fd);
|
fdm_del(fdm, app_sync_updates_fd);
|
||||||
fdm_del(fdm, title_update_fd);
|
fdm_del(fdm, title_update_fd);
|
||||||
|
fdm_del(fdm, icon_update_fd);
|
||||||
fdm_del(fdm, app_id_update_fd);
|
fdm_del(fdm, app_id_update_fd);
|
||||||
|
|
||||||
free(term);
|
free(term);
|
||||||
|
|
@ -1626,6 +1662,7 @@ term_shutdown(struct terminal *term)
|
||||||
fdm_del(term->fdm, term->selection.auto_scroll.fd);
|
fdm_del(term->fdm, term->selection.auto_scroll.fd);
|
||||||
fdm_del(term->fdm, term->render.app_sync_updates.timer_fd);
|
fdm_del(term->fdm, term->render.app_sync_updates.timer_fd);
|
||||||
fdm_del(term->fdm, term->render.app_id.timer_fd);
|
fdm_del(term->fdm, term->render.app_id.timer_fd);
|
||||||
|
fdm_del(term->fdm, term->render.icon.timer_fd);
|
||||||
fdm_del(term->fdm, term->render.title.timer_fd);
|
fdm_del(term->fdm, term->render.title.timer_fd);
|
||||||
fdm_del(term->fdm, term->delayed_render_timer.lower_fd);
|
fdm_del(term->fdm, term->delayed_render_timer.lower_fd);
|
||||||
fdm_del(term->fdm, term->delayed_render_timer.upper_fd);
|
fdm_del(term->fdm, term->delayed_render_timer.upper_fd);
|
||||||
|
|
@ -1677,6 +1714,7 @@ term_shutdown(struct terminal *term)
|
||||||
term->selection.auto_scroll.fd = -1;
|
term->selection.auto_scroll.fd = -1;
|
||||||
term->render.app_sync_updates.timer_fd = -1;
|
term->render.app_sync_updates.timer_fd = -1;
|
||||||
term->render.app_id.timer_fd = -1;
|
term->render.app_id.timer_fd = -1;
|
||||||
|
term->render.icon.timer_fd = -1;
|
||||||
term->render.title.timer_fd = -1;
|
term->render.title.timer_fd = -1;
|
||||||
term->delayed_render_timer.lower_fd = -1;
|
term->delayed_render_timer.lower_fd = -1;
|
||||||
term->delayed_render_timer.upper_fd = -1;
|
term->delayed_render_timer.upper_fd = -1;
|
||||||
|
|
@ -1731,6 +1769,7 @@ term_destroy(struct terminal *term)
|
||||||
fdm_del(term->fdm, term->selection.auto_scroll.fd);
|
fdm_del(term->fdm, term->selection.auto_scroll.fd);
|
||||||
fdm_del(term->fdm, term->render.app_sync_updates.timer_fd);
|
fdm_del(term->fdm, term->render.app_sync_updates.timer_fd);
|
||||||
fdm_del(term->fdm, term->render.app_id.timer_fd);
|
fdm_del(term->fdm, term->render.app_id.timer_fd);
|
||||||
|
fdm_del(term->fdm, term->render.icon.timer_fd);
|
||||||
fdm_del(term->fdm, term->render.title.timer_fd);
|
fdm_del(term->fdm, term->render.title.timer_fd);
|
||||||
fdm_del(term->fdm, term->delayed_render_timer.lower_fd);
|
fdm_del(term->fdm, term->delayed_render_timer.lower_fd);
|
||||||
fdm_del(term->fdm, term->delayed_render_timer.upper_fd);
|
fdm_del(term->fdm, term->delayed_render_timer.upper_fd);
|
||||||
|
|
@ -1775,7 +1814,9 @@ term_destroy(struct terminal *term)
|
||||||
composed_free(term->composed);
|
composed_free(term->composed);
|
||||||
|
|
||||||
free(term->app_id);
|
free(term->app_id);
|
||||||
|
free(term->window_icon);
|
||||||
free(term->window_title);
|
free(term->window_title);
|
||||||
|
tll_free_and_free(term->window_icon_stack, free);
|
||||||
tll_free_and_free(term->window_title_stack, free);
|
tll_free_and_free(term->window_title_stack, free);
|
||||||
|
|
||||||
for (size_t i = 0; i < sizeof(term->fonts) / sizeof(term->fonts[0]); i++)
|
for (size_t i = 0; i < sizeof(term->fonts) / sizeof(term->fonts[0]); i++)
|
||||||
|
|
@ -2007,6 +2048,9 @@ term_reset(struct terminal *term, bool hard)
|
||||||
term->saved_charsets = term->charsets;
|
term->saved_charsets = term->charsets;
|
||||||
tll_free_and_free(term->window_title_stack, free);
|
tll_free_and_free(term->window_title_stack, free);
|
||||||
term_set_window_title(term, term->conf->title);
|
term_set_window_title(term, term->conf->title);
|
||||||
|
tll_free_and_free(term->window_icon_stack, free);
|
||||||
|
term_set_app_id(term, NULL);
|
||||||
|
term_set_icon(term, NULL);
|
||||||
|
|
||||||
term_set_user_mouse_cursor(term, NULL);
|
term_set_user_mouse_cursor(term, NULL);
|
||||||
|
|
||||||
|
|
@ -3528,7 +3572,7 @@ term_set_window_title(struct terminal *term, const char *title)
|
||||||
if (term->window_title != NULL && streq(term->window_title, title))
|
if (term->window_title != NULL && streq(term->window_title, title))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (mbsntoc32(NULL, title, strlen(title), 0) == (size_t)-1) {
|
if (!is_valid_utf8(title)) {
|
||||||
/* It's an xdg_toplevel::set_title() protocol violation to set
|
/* It's an xdg_toplevel::set_title() protocol violation to set
|
||||||
a title with an invalid UTF-8 sequence */
|
a title with an invalid UTF-8 sequence */
|
||||||
LOG_WARN("%s: title is not valid UTF-8, ignoring", title);
|
LOG_WARN("%s: title is not valid UTF-8, ignoring", title);
|
||||||
|
|
@ -3548,9 +3592,14 @@ term_set_app_id(struct terminal *term, const char *app_id)
|
||||||
app_id = NULL;
|
app_id = NULL;
|
||||||
if (term->app_id == NULL && app_id == NULL)
|
if (term->app_id == NULL && app_id == NULL)
|
||||||
return;
|
return;
|
||||||
if (term->app_id != NULL && app_id != NULL && strcmp(term->app_id, app_id) == 0)
|
if (term->app_id != NULL && app_id != NULL && streq(term->app_id, app_id))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (app_id != NULL && !is_valid_utf8(app_id)) {
|
||||||
|
LOG_WARN("%s: app-id is not valid UTF-8, ignoring", app_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
free(term->app_id);
|
free(term->app_id);
|
||||||
if (app_id != NULL) {
|
if (app_id != NULL) {
|
||||||
term->app_id = xstrdup(app_id);
|
term->app_id = xstrdup(app_id);
|
||||||
|
|
@ -3558,6 +3607,44 @@ term_set_app_id(struct terminal *term, const char *app_id)
|
||||||
term->app_id = NULL;
|
term->app_id = NULL;
|
||||||
}
|
}
|
||||||
render_refresh_app_id(term);
|
render_refresh_app_id(term);
|
||||||
|
render_refresh_icon(term);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
term_set_icon(struct terminal *term, const char *icon)
|
||||||
|
{
|
||||||
|
if (icon != NULL && *icon == '\0')
|
||||||
|
icon = NULL;
|
||||||
|
if (term->window_icon == NULL && icon == NULL)
|
||||||
|
return;
|
||||||
|
if (term->window_icon != NULL && icon != NULL && streq(term->window_icon, icon))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (icon != NULL && !is_valid_utf8(icon)) {
|
||||||
|
LOG_WARN("%s: icon label is not valid UTF-8, ignoring", icon);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(term->window_icon);
|
||||||
|
if (icon != NULL) {
|
||||||
|
term->window_icon = xstrdup(icon);
|
||||||
|
} else {
|
||||||
|
term->window_icon = NULL;
|
||||||
|
}
|
||||||
|
render_refresh_icon(term);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
term_icon(const struct terminal *term)
|
||||||
|
{
|
||||||
|
const char *app_id =
|
||||||
|
term->app_id != NULL ? term->app_id : term->conf->app_id;
|
||||||
|
|
||||||
|
return term->window_icon != NULL
|
||||||
|
? term->window_icon
|
||||||
|
: streq(app_id, "footclient")
|
||||||
|
? "foot"
|
||||||
|
: app_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -552,6 +552,8 @@ struct terminal {
|
||||||
bool window_title_has_been_set;
|
bool window_title_has_been_set;
|
||||||
char *window_title;
|
char *window_title;
|
||||||
tll(char *) window_title_stack;
|
tll(char *) window_title_stack;
|
||||||
|
char *window_icon;
|
||||||
|
tll(char *)window_icon_stack;
|
||||||
char *app_id;
|
char *app_id;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
@ -670,6 +672,11 @@ struct terminal {
|
||||||
int timer_fd;
|
int timer_fd;
|
||||||
} title;
|
} title;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct timespec last_update;
|
||||||
|
int timer_fd;
|
||||||
|
} icon;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
struct timespec last_update;
|
struct timespec last_update;
|
||||||
int timer_fd;
|
int timer_fd;
|
||||||
|
|
@ -925,6 +932,8 @@ void term_set_user_mouse_cursor(struct terminal *term, const char *cursor);
|
||||||
|
|
||||||
void term_set_window_title(struct terminal *term, const char *title);
|
void term_set_window_title(struct terminal *term, const char *title);
|
||||||
void term_set_app_id(struct terminal *term, const char *app_id);
|
void term_set_app_id(struct terminal *term, const char *app_id);
|
||||||
|
void term_set_icon(struct terminal *term, const char *icon);
|
||||||
|
const char *term_icon(const struct terminal *term);
|
||||||
void term_flash(struct terminal *term, unsigned duration_ms);
|
void term_flash(struct terminal *term, unsigned duration_ms);
|
||||||
void term_bell(struct terminal *term);
|
void term_bell(struct terminal *term);
|
||||||
bool term_spawn_new(const struct terminal *term);
|
bool term_spawn_new(const struct terminal *term);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue