mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-05 04:06:08 -05:00
osc: add support for osc 176 (app ID)
This adds support for a new OSC escape sequence: OSC 176, that lets terminal programs tell the terminal the name of the app that is running. foot then sets the app ID of the toplevel to that ID, which lets the compositor know which app is running, and typically sets the appropriate icon, window grouping, ... See: https://gist.github.com/delthas/d451e2cc1573bb2364839849c7117239
This commit is contained in:
parent
4801d3a305
commit
6c56b04b3f
8 changed files with 101 additions and 2 deletions
|
|
@ -536,6 +536,7 @@ with the terminal emulator itself. Foot implements the following OSCs:
|
||||||
* `OSC 117` - reset highlight background color
|
* `OSC 117` - reset highlight background color
|
||||||
* `OSC 119` - reset highlight foreground color
|
* `OSC 119` - reset highlight foreground color
|
||||||
* `OSC 133` - [shell integration](#shell-integration)
|
* `OSC 133` - [shell integration](#shell-integration)
|
||||||
|
* `OSC 176` - set app ID
|
||||||
* `OSC 555` - flash screen (**foot specific**)
|
* `OSC 555` - flash screen (**foot specific**)
|
||||||
* `OSC 777` - desktop notification (only the `;notify` sub-command of
|
* `OSC 777` - desktop notification (only the `;notify` sub-command of
|
||||||
OSC 777 is supported.)
|
OSC 777 is supported.)
|
||||||
|
|
|
||||||
|
|
@ -693,6 +693,11 @@ All _OSC_ sequences begin with *\\E]*, sometimes abbreviated _OSC_.
|
||||||
| \\E] 133 ; D \\E\\
|
| \\E] 133 ; D \\E\\
|
||||||
: FinalTerm
|
: FinalTerm
|
||||||
: Mark end of command output
|
: Mark end of command output
|
||||||
|
| \\E] 176 ; _app-id_ \\E\\
|
||||||
|
: foot
|
||||||
|
: Set app ID. _app-id_ is optional; if assigned,
|
||||||
|
the terminal window App ID will be set to the value.
|
||||||
|
An empty App ID resets the value to the default.
|
||||||
| \\E] 555 \\E\\
|
| \\E] 555 \\E\\
|
||||||
: foot
|
: foot
|
||||||
: Flash the entire terminal (foot extension)
|
: Flash the entire terminal (foot extension)
|
||||||
|
|
|
||||||
2
notify.c
2
notify.c
|
|
@ -36,7 +36,7 @@ notify_notify(const struct terminal *term, const char *title, const char *body)
|
||||||
if (!spawn_expand_template(
|
if (!spawn_expand_template(
|
||||||
&term->conf->notify, 4,
|
&term->conf->notify, 4,
|
||||||
(const char *[]){"app-id", "window-title", "title", "body"},
|
(const char *[]){"app-id", "window-title", "title", "body"},
|
||||||
(const char *[]){term->conf->app_id, term->window_title, title, body},
|
(const char *[]){term->app_id ? term->app_id : term->conf->app_id, term->window_title, title, body},
|
||||||
&argc, &argv))
|
&argc, &argv))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
4
osc.c
4
osc.c
|
|
@ -916,6 +916,10 @@ osc_dispatch(struct terminal *term)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 176:
|
||||||
|
term_set_app_id(term, string);
|
||||||
|
break;
|
||||||
|
|
||||||
case 555:
|
case 555:
|
||||||
osc_flash(term);
|
osc_flash(term);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
22
render.c
22
render.c
|
|
@ -4529,6 +4529,28 @@ render_refresh_title(struct terminal *term)
|
||||||
render_refresh_csd(term);
|
render_refresh_csd(term);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
render_refresh_app_id(struct terminal *term)
|
||||||
|
{
|
||||||
|
struct timespec now;
|
||||||
|
if (clock_gettime(CLOCK_MONOTONIC, &now) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct timespec diff;
|
||||||
|
timespec_sub(&now, &term->render.app_id.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.app_id.timer_fd, 0, &timeout, NULL);
|
||||||
|
} else {
|
||||||
|
term->render.app_id.last_update = now;
|
||||||
|
xdg_toplevel_set_app_id(term->window->xdg_toplevel, term->app_id ? term->app_id : term->conf->app_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
render_refresh(struct terminal *term)
|
render_refresh(struct terminal *term)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
1
render.h
1
render.h
|
|
@ -21,6 +21,7 @@ bool render_resize(
|
||||||
struct terminal *term, int width, int height, uint8_t resize_options);
|
struct terminal *term, int width, int height, uint8_t resize_options);
|
||||||
|
|
||||||
void render_refresh(struct terminal *term);
|
void render_refresh(struct terminal *term);
|
||||||
|
void render_refresh_app_id(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);
|
||||||
|
|
|
||||||
61
terminal.c
61
terminal.c
|
|
@ -627,6 +627,30 @@ fdm_title_update_timeout(struct fdm *fdm, int fd, int events, void *data)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
fdm_app_id_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.app_id.timer_fd, &unused, sizeof(unused));
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
return true;
|
||||||
|
LOG_ERRNO("failed to read app ID update throttle timer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct itimerspec reset = {{0}};
|
||||||
|
timerfd_settime(term->render.app_id.timer_fd, 0, &reset, NULL);
|
||||||
|
|
||||||
|
render_refresh_app_id(term);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
initialize_render_workers(struct terminal *term)
|
initialize_render_workers(struct terminal *term)
|
||||||
{
|
{
|
||||||
|
|
@ -1050,6 +1074,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 app_id_update_fd = -1;
|
||||||
|
|
||||||
struct terminal *term = malloc(sizeof(*term));
|
struct terminal *term = malloc(sizeof(*term));
|
||||||
if (unlikely(term == NULL)) {
|
if (unlikely(term == NULL)) {
|
||||||
|
|
@ -1084,6 +1109,12 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
goto close_fds;
|
goto close_fds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
goto close_fds;
|
||||||
|
}
|
||||||
|
|
||||||
if (ioctl(ptmx, (unsigned int)TIOCSWINSZ,
|
if (ioctl(ptmx, (unsigned int)TIOCSWINSZ,
|
||||||
&(struct winsize){.ws_row = 24, .ws_col = 80}) < 0)
|
&(struct winsize){.ws_row = 24, .ws_col = 80}) < 0)
|
||||||
{
|
{
|
||||||
|
|
@ -1114,7 +1145,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
!fdm_add(fdm, delay_lower_fd, EPOLLIN, &fdm_delayed_render, term) ||
|
!fdm_add(fdm, delay_lower_fd, EPOLLIN, &fdm_delayed_render, term) ||
|
||||||
!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, app_id_update_fd, EPOLLIN, &fdm_app_id_update_timeout, term))
|
||||||
{
|
{
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
@ -1210,6 +1242,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,
|
||||||
},
|
},
|
||||||
|
.app_id = {
|
||||||
|
.timer_fd = app_id_update_fd,
|
||||||
|
},
|
||||||
.workers = {
|
.workers = {
|
||||||
.count = conf->render_worker_count,
|
.count = conf->render_worker_count,
|
||||||
.queue = tll_init(),
|
.queue = tll_init(),
|
||||||
|
|
@ -1318,6 +1353,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, app_id_update_fd);
|
||||||
|
|
||||||
free(term);
|
free(term);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -1510,6 +1546,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.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);
|
||||||
|
|
@ -1548,6 +1585,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.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;
|
||||||
|
|
@ -1601,6 +1639,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.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);
|
||||||
|
|
@ -1644,6 +1683,7 @@ term_destroy(struct terminal *term)
|
||||||
|
|
||||||
composed_free(term->composed);
|
composed_free(term->composed);
|
||||||
|
|
||||||
|
free(term->app_id);
|
||||||
free(term->window_title);
|
free(term->window_title);
|
||||||
tll_free_and_free(term->window_title_stack, free);
|
tll_free_and_free(term->window_title_stack, free);
|
||||||
|
|
||||||
|
|
@ -3260,6 +3300,25 @@ term_set_window_title(struct terminal *term, const char *title)
|
||||||
term->window_title_has_been_set = true;
|
term->window_title_has_been_set = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
term_set_app_id(struct terminal *term, const char *app_id)
|
||||||
|
{
|
||||||
|
if (app_id != NULL && *app_id == '\0')
|
||||||
|
app_id = NULL;
|
||||||
|
if (term->app_id == NULL && app_id == NULL)
|
||||||
|
return;
|
||||||
|
if (term->app_id != NULL && app_id != NULL && strcmp(term->app_id, app_id) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
free(term->app_id);
|
||||||
|
if (app_id != NULL) {
|
||||||
|
term->app_id = xstrdup(app_id);
|
||||||
|
} else {
|
||||||
|
term->app_id = NULL;
|
||||||
|
}
|
||||||
|
render_refresh_app_id(term);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
term_flash(struct terminal *term, unsigned duration_ms)
|
term_flash(struct terminal *term, unsigned duration_ms)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -483,6 +483,7 @@ 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 *app_id;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool active;
|
bool active;
|
||||||
|
|
@ -606,6 +607,11 @@ struct terminal {
|
||||||
int timer_fd;
|
int timer_fd;
|
||||||
} title;
|
} title;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct timespec last_update;
|
||||||
|
int timer_fd;
|
||||||
|
} app_id;
|
||||||
|
|
||||||
uint32_t scrollback_lines; /* Number of scrollback lines, from conf (TODO: move out from render struct?) */
|
uint32_t scrollback_lines; /* Number of scrollback lines, from conf (TODO: move out from render struct?) */
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
|
|
@ -832,6 +838,7 @@ void term_xcursor_update_for_seat(struct terminal *term, struct seat *seat);
|
||||||
void term_set_user_mouse_cursor(struct terminal *term, const char *cursor);
|
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_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