2019-06-15 22:22:44 +02:00
|
|
|
#include "osc.h"
|
2019-07-18 19:26:24 +02:00
|
|
|
|
2021-02-13 13:41:27 +01:00
|
|
|
#include <stdlib.h>
|
2019-07-18 19:26:24 +02:00
|
|
|
#include <string.h>
|
2019-07-05 09:46:48 +02:00
|
|
|
#include <ctype.h>
|
2020-08-08 20:34:30 +01:00
|
|
|
#include <errno.h>
|
2019-06-15 22:22:44 +02:00
|
|
|
|
2024-06-08 08:39:47 +02:00
|
|
|
#include <sys/epoll.h>
|
|
|
|
|
|
2019-06-15 22:22:44 +02:00
|
|
|
#define LOG_MODULE "osc"
|
2025-01-02 09:12:11 +01:00
|
|
|
#define LOG_ENABLE_DBG 0
|
2019-06-15 22:22:44 +02:00
|
|
|
#include "log.h"
|
2019-07-19 11:13:07 +02:00
|
|
|
#include "base64.h"
|
2020-11-26 18:08:28 +01:00
|
|
|
#include "config.h"
|
2021-10-20 17:05:36 +01:00
|
|
|
#include "macros.h"
|
2020-12-10 18:06:24 +01:00
|
|
|
#include "notify.h"
|
2019-07-19 11:13:07 +02:00
|
|
|
#include "selection.h"
|
2019-07-18 19:26:24 +02:00
|
|
|
#include "terminal.h"
|
2020-10-28 19:11:22 +01:00
|
|
|
#include "uri.h"
|
2021-02-14 13:28:42 +01:00
|
|
|
#include "util.h"
|
2020-08-08 20:34:30 +01:00
|
|
|
#include "xmalloc.h"
|
2021-01-14 21:30:06 +00:00
|
|
|
#include "xsnprintf.h"
|
2019-07-18 19:26:24 +02:00
|
|
|
|
2019-11-29 23:59:24 +01:00
|
|
|
#define UNHANDLED() LOG_DBG("unhandled: OSC: %.*s", (int)term->vt.osc.idx, term->vt.osc.data)
|
2019-07-30 21:42:46 +02:00
|
|
|
|
2019-07-19 11:13:07 +02:00
|
|
|
static void
|
2019-07-19 14:20:38 +02:00
|
|
|
osc_to_clipboard(struct terminal *term, const char *target,
|
|
|
|
|
const char *base64_data)
|
2019-07-19 11:13:07 +02:00
|
|
|
{
|
2019-11-28 20:56:55 +01:00
|
|
|
bool to_clipboard = false;
|
|
|
|
|
bool to_primary = false;
|
|
|
|
|
|
|
|
|
|
if (target[0] == '\0')
|
|
|
|
|
to_clipboard = true;
|
|
|
|
|
|
2019-07-19 14:20:38 +02:00
|
|
|
for (const char *t = target; *t != '\0'; t++) {
|
|
|
|
|
switch (*t) {
|
2019-11-28 20:56:55 +01:00
|
|
|
case 'c':
|
|
|
|
|
to_clipboard = true;
|
2019-07-19 14:20:38 +02:00
|
|
|
break;
|
|
|
|
|
|
2019-11-19 14:05:31 +01:00
|
|
|
case 's':
|
2019-11-28 20:56:55 +01:00
|
|
|
case 'p':
|
|
|
|
|
to_primary = true;
|
2019-08-09 21:27:51 +02:00
|
|
|
break;
|
|
|
|
|
|
2019-07-19 14:20:38 +02:00
|
|
|
default:
|
|
|
|
|
LOG_WARN("unimplemented: clipboard target '%c'", *t);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-08 19:28:08 +02:00
|
|
|
/* Find a seat in which the terminal has focus */
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
struct seat *seat = term_first_focused_seat(term);
|
2020-07-08 19:28:08 +02:00
|
|
|
if (seat == NULL) {
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
LOG_WARN("OSC-52: client tried to write to clipboard data while window was unfocused");
|
2020-07-08 19:28:08 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
if (!term_osc_copy_allowed(term)) {
|
|
|
|
|
LOG_DBG("OSC-52: ignoring copy request: disabled in configuration");
|
2024-12-21 06:52:00 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 11:29:05 +02:00
|
|
|
char *decoded = base64_decode(base64_data, NULL);
|
2025-06-09 09:12:08 +02:00
|
|
|
if (decoded == NULL || decoded[0] == '\0') {
|
|
|
|
|
if (decoded == NULL) {
|
|
|
|
|
if (errno == EINVAL)
|
|
|
|
|
LOG_WARN("OSC: invalid clipboard data: %s", base64_data);
|
|
|
|
|
else
|
|
|
|
|
LOG_ERRNO("base64_decode() failed");
|
|
|
|
|
}
|
2020-09-09 18:46:39 +02:00
|
|
|
|
|
|
|
|
if (to_clipboard)
|
|
|
|
|
selection_clipboard_unset(seat);
|
|
|
|
|
if (to_primary)
|
|
|
|
|
selection_primary_unset(seat);
|
2025-06-09 09:12:08 +02:00
|
|
|
free(decoded);
|
2020-09-09 18:46:39 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG_DBG("decoded: %s", decoded);
|
|
|
|
|
|
2019-11-28 20:56:55 +01:00
|
|
|
if (to_clipboard) {
|
2020-08-08 20:34:30 +01:00
|
|
|
char *copy = xstrdup(decoded);
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
if (!text_to_clipboard(seat, term, copy, NULL, seat->kbd.serial))
|
2019-11-28 20:56:55 +01:00
|
|
|
free(copy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (to_primary) {
|
2020-08-08 20:34:30 +01:00
|
|
|
char *copy = xstrdup(decoded);
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
if (!text_to_primary(seat, term, copy, NULL, seat->kbd.serial))
|
2019-11-28 20:56:55 +01:00
|
|
|
free(copy);
|
|
|
|
|
}
|
2020-07-08 19:28:08 +02:00
|
|
|
|
2019-07-19 14:20:38 +02:00
|
|
|
free(decoded);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct clip_context {
|
2020-07-08 19:28:08 +02:00
|
|
|
struct seat *seat;
|
2019-07-19 14:20:38 +02:00
|
|
|
struct terminal *term;
|
|
|
|
|
uint8_t buf[3];
|
|
|
|
|
int idx;
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
|
|
|
|
|
struct {
|
|
|
|
|
bool is_kitty;
|
|
|
|
|
bool from_primary;
|
|
|
|
|
size_t chunk_bytes_written; /* Bytes written so far, in current chunk */
|
|
|
|
|
char *encoded_mime_type; /* Currently "active" mime-type, base64 encoded */
|
|
|
|
|
tll(char *) mime_types; /* Remaining mime-types */
|
|
|
|
|
} kitty;
|
2019-07-19 14:20:38 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void
|
2020-10-28 19:11:22 +01:00
|
|
|
from_clipboard_cb(char *text, size_t size, void *user)
|
2019-07-19 14:20:38 +02:00
|
|
|
{
|
|
|
|
|
struct clip_context *ctx = user;
|
|
|
|
|
struct terminal *term = ctx->term;
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(ctx->idx >= 0 && ctx->idx <= 2);
|
2019-07-19 14:20:38 +02:00
|
|
|
|
|
|
|
|
const char *t = text;
|
|
|
|
|
size_t left = size;
|
|
|
|
|
|
|
|
|
|
if (ctx->idx > 0) {
|
|
|
|
|
for (size_t i = ctx->idx; i < 3 && left > 0; i++, t++, left--)
|
|
|
|
|
ctx->buf[ctx->idx++] = *t;
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(ctx->idx <= 3);
|
2019-07-19 14:20:38 +02:00
|
|
|
if (ctx->idx == 3) {
|
|
|
|
|
char *chunk = base64_encode(ctx->buf, 3);
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(chunk != NULL);
|
|
|
|
|
xassert(strlen(chunk) == 4);
|
2019-07-19 14:20:38 +02:00
|
|
|
|
2024-06-08 08:39:47 +02:00
|
|
|
term_paste_data_to_slave(term, chunk, 4);
|
2019-07-19 14:20:38 +02:00
|
|
|
free(chunk);
|
|
|
|
|
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
ctx->kitty.chunk_bytes_written += 3;
|
2019-07-19 14:20:38 +02:00
|
|
|
ctx->idx = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (left == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(ctx->idx == 0);
|
2019-08-03 21:30:06 +02:00
|
|
|
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
const int remaining = left % 3;
|
2019-07-19 14:20:38 +02:00
|
|
|
for (int i = remaining; i > 0; i--)
|
2019-08-03 21:30:06 +02:00
|
|
|
ctx->buf[ctx->idx++] = text[size - i];
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(ctx->idx == remaining);
|
2019-07-19 14:20:38 +02:00
|
|
|
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
const size_t count = left / 3 * 3;
|
|
|
|
|
char *chunk = base64_encode(t, count);
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(chunk != NULL);
|
|
|
|
|
xassert(strlen(chunk) % 4 == 0);
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
|
|
|
|
|
if (unlikely(ctx->kitty.is_kitty && ctx->kitty.chunk_bytes_written + count > 4096)) {
|
|
|
|
|
/* New chunk */
|
|
|
|
|
term_paste_data_to_slave(term, "\033\\", 2);
|
|
|
|
|
term_paste_data_to_slave(term, "\033]5522;type=read:status=DATA:mime=", 34);
|
|
|
|
|
term_paste_data_to_slave(term, ctx->kitty.encoded_mime_type,
|
|
|
|
|
strlen(ctx->kitty.encoded_mime_type));
|
|
|
|
|
term_paste_data_to_slave(term, ";", 1);
|
|
|
|
|
ctx->kitty.chunk_bytes_written = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-08 08:39:47 +02:00
|
|
|
term_paste_data_to_slave(term, chunk, strlen(chunk));
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
ctx->kitty.chunk_bytes_written += count;
|
2019-07-19 14:20:38 +02:00
|
|
|
free(chunk);
|
|
|
|
|
}
|
2020-07-08 19:28:08 +02:00
|
|
|
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
static void kitty_clipboard_read_next_mime_type(struct clip_context *ctx);
|
|
|
|
|
|
2019-11-05 08:49:32 +01:00
|
|
|
static void
|
|
|
|
|
from_clipboard_done(void *user)
|
|
|
|
|
{
|
|
|
|
|
struct clip_context *ctx = user;
|
|
|
|
|
struct terminal *term = ctx->term;
|
|
|
|
|
|
|
|
|
|
if (ctx->idx > 0) {
|
|
|
|
|
char res[4];
|
|
|
|
|
base64_encode_final(ctx->buf, ctx->idx, res);
|
2024-06-08 08:39:47 +02:00
|
|
|
term_paste_data_to_slave(term, res, 4);
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
ctx->idx = 0;
|
2019-11-05 08:49:32 +01:00
|
|
|
}
|
|
|
|
|
|
2021-10-20 12:48:37 +01:00
|
|
|
if (term->vt.osc.bel)
|
2024-06-08 08:39:47 +02:00
|
|
|
term_paste_data_to_slave(term, "\a", 1);
|
2021-10-20 12:48:37 +01:00
|
|
|
else
|
2024-06-08 08:39:47 +02:00
|
|
|
term_paste_data_to_slave(term, "\033\\", 2);
|
|
|
|
|
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
if (ctx->kitty.is_kitty) {
|
|
|
|
|
free(ctx->kitty.encoded_mime_type);
|
|
|
|
|
ctx->kitty.encoded_mime_type = NULL;
|
|
|
|
|
|
|
|
|
|
/* Continue with the next mime-type, if there are any left */
|
|
|
|
|
if (tll_length(ctx->kitty.mime_types) > 0) {
|
|
|
|
|
kitty_clipboard_read_next_mime_type(ctx);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If not, we're done! */
|
|
|
|
|
term_paste_data_to_slave(term, "\033]5522;type=read:status=DONE\033\\", 30);
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-08 08:39:47 +02:00
|
|
|
term->is_sending_paste_data = false;
|
|
|
|
|
|
|
|
|
|
/* Make sure we send any queued up non-paste data */
|
|
|
|
|
if (tll_length(term->ptmx_buffers) > 0)
|
|
|
|
|
fdm_event_add(term->fdm, term->ptmx, EPOLLOUT);
|
2021-10-20 12:48:37 +01:00
|
|
|
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
|
2019-11-05 08:49:32 +01:00
|
|
|
free(ctx);
|
|
|
|
|
}
|
2020-07-08 19:28:08 +02:00
|
|
|
|
2019-07-19 14:20:38 +02:00
|
|
|
static void
|
|
|
|
|
osc_from_clipboard(struct terminal *term, const char *source)
|
|
|
|
|
{
|
2020-07-08 19:28:08 +02:00
|
|
|
/* Find a seat in which the terminal has focus */
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
struct seat *seat = term_first_focused_seat(term);
|
2020-07-08 19:28:08 +02:00
|
|
|
if (seat == NULL) {
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
LOG_WARN("OSC-52: client tried to read clipboard data while window was unfocused");
|
2020-07-08 19:28:08 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
if (!term_osc_paste_allowed(term)) {
|
|
|
|
|
LOG_DBG("OSC-52: ignoring paste request: disabled in configuration");
|
2024-12-21 06:52:00 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 18:46:58 +02:00
|
|
|
/* Use clipboard if no source has been specified */
|
|
|
|
|
char src = source[0] == '\0' ? 'c' : 0;
|
|
|
|
|
bool from_clipboard = src == 'c';
|
|
|
|
|
bool from_primary = false;
|
|
|
|
|
|
|
|
|
|
for (const char *s = source;
|
|
|
|
|
*s != '\0' && !from_clipboard && !from_primary;
|
|
|
|
|
s++)
|
|
|
|
|
{
|
|
|
|
|
if (*s == 'c' || *s == 'p' || *s == 's') {
|
|
|
|
|
src = *s;
|
|
|
|
|
|
|
|
|
|
switch (src) {
|
|
|
|
|
case 'c':
|
|
|
|
|
from_clipboard = selection_clipboard_has_data(seat);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 's':
|
|
|
|
|
case 'p':
|
|
|
|
|
from_primary = selection_primary_has_data(seat);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
LOG_WARN("unimplemented: clipboard source '%c'", *s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!from_clipboard && !from_primary)
|
|
|
|
|
return;
|
|
|
|
|
|
2024-06-08 08:39:47 +02:00
|
|
|
if (term->is_sending_paste_data) {
|
|
|
|
|
/* FIXME: we should wait for the paste to end, then continue
|
|
|
|
|
with the OSC-52 reply */
|
|
|
|
|
term_to_slave(term, "\033]52;", 5);
|
|
|
|
|
term_to_slave(term, &src, 1);
|
|
|
|
|
term_to_slave(term, ";", 1);
|
|
|
|
|
if (term->vt.osc.bel)
|
|
|
|
|
term_to_slave(term, "\a", 1);
|
|
|
|
|
else
|
|
|
|
|
term_to_slave(term, "\033\\", 2);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
term->is_sending_paste_data = true;
|
|
|
|
|
|
|
|
|
|
term_paste_data_to_slave(term, "\033]52;", 5);
|
|
|
|
|
term_paste_data_to_slave(term, &src, 1);
|
|
|
|
|
term_paste_data_to_slave(term, ";", 1);
|
2019-07-19 14:20:38 +02:00
|
|
|
|
2020-08-08 20:34:30 +01:00
|
|
|
struct clip_context *ctx = xmalloc(sizeof(*ctx));
|
2020-07-08 19:28:08 +02:00
|
|
|
*ctx = (struct clip_context) {.seat = seat, .term = term};
|
2019-07-19 14:20:38 +02:00
|
|
|
|
2020-09-09 18:46:58 +02:00
|
|
|
if (from_clipboard) {
|
2019-11-05 08:49:32 +01:00
|
|
|
text_from_clipboard(
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
seat, term, true, &from_clipboard_cb, &from_clipboard_done, ctx, NULL);
|
2020-09-09 18:46:58 +02:00
|
|
|
}
|
2019-07-19 14:20:38 +02:00
|
|
|
|
2020-09-09 18:46:58 +02:00
|
|
|
if (from_primary) {
|
2020-07-08 19:28:08 +02:00
|
|
|
text_from_primary(
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
seat, term, true, &from_clipboard_cb, &from_clipboard_done, ctx, NULL);
|
2019-07-19 14:20:38 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
osc_selection(struct terminal *term, char *string)
|
|
|
|
|
{
|
|
|
|
|
char *p = string;
|
2019-07-19 11:13:07 +02:00
|
|
|
bool clipboard_done = false;
|
|
|
|
|
|
2019-07-19 14:20:38 +02:00
|
|
|
/* The first parameter is a string of clipbard sources/targets */
|
2019-07-19 11:13:07 +02:00
|
|
|
while (*p != '\0' && !clipboard_done) {
|
|
|
|
|
switch (*p) {
|
|
|
|
|
case ';':
|
|
|
|
|
clipboard_done = true;
|
2019-07-19 14:20:38 +02:00
|
|
|
*p = '\0';
|
2019-07-19 11:13:07 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p++;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-19 14:20:38 +02:00
|
|
|
LOG_DBG("clipboard: target = %s data = %s", string, p);
|
2019-07-19 11:13:07 +02:00
|
|
|
|
2022-04-06 01:24:34 +01:00
|
|
|
if (p[0] == '?' && p[1] == '\0')
|
2019-07-19 14:20:38 +02:00
|
|
|
osc_from_clipboard(term, string);
|
|
|
|
|
else
|
|
|
|
|
osc_to_clipboard(term, string, p);
|
2019-07-19 11:13:07 +02:00
|
|
|
}
|
|
|
|
|
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
void
|
|
|
|
|
kitty_clipboard_query(struct terminal *term, bool primary)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Enumerate the available mime-types
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
LOG_DBG("OSC-5522: query mime-types: primary=%d", primary);
|
|
|
|
|
|
2026-05-22 11:59:58 +02:00
|
|
|
if (!term_osc_paste_allowed(term)) {
|
|
|
|
|
LOG_DBG("OSC-5522: ignoring paste request: disabled in configuration");
|
|
|
|
|
term_to_slave(term, "\033]5522;type=read:status=EPERM\033\\", 31);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
/* Find a seat in which the terminal has focus */
|
|
|
|
|
struct seat *seat = term_first_focused_seat(term);
|
|
|
|
|
if (seat == NULL) {
|
|
|
|
|
LOG_WARN("OSC-5522: client tried to read clipboard data "
|
|
|
|
|
"while window was unfocused");
|
|
|
|
|
term_to_slave(term, "\033]5522;type=read:status=ENOSYS\033\\", 32);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const mime_list_t *mime_list = !primary
|
|
|
|
|
? &seat->clipboard.all_mime_types
|
|
|
|
|
: &seat->primary.all_mime_types;
|
|
|
|
|
|
|
|
|
|
term_to_slave(term, "\033]5522;type=read:", 17);
|
|
|
|
|
if (primary)
|
|
|
|
|
term_to_slave(term, "loc=primary:", 12);
|
|
|
|
|
term_to_slave(term, "status=OK\033\\", 11);
|
|
|
|
|
term_to_slave(term, "\033]5522;type=read:status=DATA:mime=Lg==;", 39); /* base64(".") == "Lg==" */
|
|
|
|
|
|
|
|
|
|
char *mime_types = NULL;
|
|
|
|
|
size_t len = 0;
|
|
|
|
|
size_t pos = 0;
|
|
|
|
|
|
|
|
|
|
/* Calculate total length of the mime list */
|
|
|
|
|
tll_foreach(*mime_list, it)
|
|
|
|
|
len += strlen(it->item) + 1;
|
|
|
|
|
|
|
|
|
|
mime_types = xmalloc(len + 1);
|
|
|
|
|
mime_types[0] = '\0';
|
|
|
|
|
|
|
|
|
|
tll_foreach(*mime_list, it) {
|
|
|
|
|
strcpy(mime_types + pos, it->item);
|
|
|
|
|
pos += strlen(it->item);
|
|
|
|
|
mime_types[pos++] = ' ';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (len > 0) {
|
|
|
|
|
/* Shave off the last ' ' */
|
|
|
|
|
len--;
|
|
|
|
|
mime_types[len] = '\0';
|
|
|
|
|
} else {
|
|
|
|
|
mime_types[0] = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *encoded_mime_list = base64_encode_oneshot(mime_types, len);
|
|
|
|
|
term_to_slave(term, encoded_mime_list, strlen(encoded_mime_list));
|
|
|
|
|
free(encoded_mime_list);
|
|
|
|
|
|
|
|
|
|
free(mime_types);
|
|
|
|
|
term_to_slave(term, "\033\\", 2);
|
|
|
|
|
term_to_slave(term, "\033]5522;type=read:status=DONE\033\\", 30);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
kitty_clipboard_read_next_mime_type(struct clip_context *ctx)
|
|
|
|
|
{
|
|
|
|
|
struct terminal *term = ctx->term;
|
|
|
|
|
struct seat *seat = ctx->seat;
|
|
|
|
|
|
|
|
|
|
xassert(term->is_sending_paste_data);
|
|
|
|
|
xassert(ctx->kitty.encoded_mime_type == NULL);
|
|
|
|
|
xassert(tll_length(ctx->kitty.mime_types) > 0);
|
|
|
|
|
|
|
|
|
|
/* Pop next mime-type to read */
|
|
|
|
|
char *mime_type = tll_pop_front(ctx->kitty.mime_types);
|
|
|
|
|
const size_t mime_len = strlen(mime_type);
|
|
|
|
|
|
|
|
|
|
ctx->kitty.encoded_mime_type = base64_encode_oneshot(mime_type, mime_len);
|
|
|
|
|
|
|
|
|
|
term_paste_data_to_slave(term, "\033]5522;type=read:status=OK\033\\", 28);
|
|
|
|
|
term_paste_data_to_slave(term, "\033]5522;type=read:status=DATA:mime=", 34);
|
|
|
|
|
term_paste_data_to_slave(term, ctx->kitty.encoded_mime_type,
|
|
|
|
|
strlen(ctx->kitty.encoded_mime_type));
|
|
|
|
|
term_paste_data_to_slave(term, ";", 1);
|
|
|
|
|
|
|
|
|
|
if (!ctx->kitty.from_primary) {
|
|
|
|
|
text_from_clipboard(
|
|
|
|
|
seat, term, true, &from_clipboard_cb, &from_clipboard_done,
|
|
|
|
|
ctx, mime_type);
|
|
|
|
|
} else {
|
|
|
|
|
text_from_primary(
|
|
|
|
|
seat, term, true, &from_clipboard_cb, &from_clipboard_done,
|
|
|
|
|
ctx, mime_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(mime_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
kitty_clipboard_read(struct terminal *term, bool primary, char *mime_types)
|
|
|
|
|
{
|
|
|
|
|
LOG_DBG("OSC-5522: read: primary=%d, mime-type=%s", primary, mime_types);
|
|
|
|
|
|
|
|
|
|
if (!term_osc_paste_allowed(term)) {
|
|
|
|
|
LOG_DBG("OSC-5522: ignoring paste request: disabled in configuration");
|
|
|
|
|
term_to_slave(term, "\033]5522;type=read:status=EPERM\033\\", 31);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (term->is_sending_paste_data) {
|
|
|
|
|
term_to_slave(term, "\033]5522;type=read:status=EBUSY\033\\", 31);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Find a seat in which the terminal has focus */
|
|
|
|
|
struct seat *seat = term_first_focused_seat(term);
|
|
|
|
|
if (seat == NULL) {
|
|
|
|
|
LOG_WARN("OSC-5522: client tried to read clipboard data "
|
|
|
|
|
"while window was unfocused");
|
|
|
|
|
term_to_slave(term, "\033]5522;type=read:status=ENOSYS\033\\", 32);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct clip_context *ctx = xmalloc(sizeof(*ctx));
|
|
|
|
|
*ctx = (struct clip_context) {
|
|
|
|
|
.seat = seat,
|
|
|
|
|
.term = term,
|
|
|
|
|
.kitty = {
|
|
|
|
|
.is_kitty = true,
|
|
|
|
|
.from_primary = primary,
|
|
|
|
|
.mime_types = tll_init(),
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Split upt the space-separated list of mime-types we're about to read */
|
|
|
|
|
for (char *save = NULL, *mime_type = strtok_r(mime_types, " ", &save);
|
|
|
|
|
mime_type != NULL;
|
|
|
|
|
mime_type = strtok_r(NULL, " ", &save))
|
|
|
|
|
{
|
|
|
|
|
tll_push_back(ctx->kitty.mime_types, xstrdup(mime_type));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Start reading the first mime-type */
|
|
|
|
|
term->is_sending_paste_data = true;
|
|
|
|
|
kitty_clipboard_read_next_mime_type(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
kitty_clipboard_reset(struct terminal *term)
|
|
|
|
|
{
|
|
|
|
|
free(term->kitty_clipboard.active_mime_type);
|
|
|
|
|
free(term->kitty_clipboard.data);
|
|
|
|
|
|
|
|
|
|
tll_foreach(term->kitty_clipboard.committed_mime_data, it) {
|
|
|
|
|
struct kitty_mime_data *mime_data = &it->item;
|
|
|
|
|
free(mime_data->mime_type);
|
|
|
|
|
free(mime_data->data);
|
|
|
|
|
tll_remove(term->kitty_clipboard.committed_mime_data, it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tll_foreach(term->kitty_clipboard.mime_aliases, it) {
|
|
|
|
|
struct kitty_mime_alias *alias = &it->item;
|
|
|
|
|
free(alias->target);
|
|
|
|
|
free(alias->alias);
|
|
|
|
|
tll_remove(term->kitty_clipboard.mime_aliases, it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
term->kitty_clipboard.for_primary = false;
|
|
|
|
|
term->kitty_clipboard.has_error = false;
|
|
|
|
|
term->kitty_clipboard.error = NULL;
|
|
|
|
|
term->kitty_clipboard.active_mime_type = NULL;
|
|
|
|
|
term->kitty_clipboard.data = NULL;
|
|
|
|
|
term->kitty_clipboard.data_len = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
kitty_clipboard_wdata_commit(struct terminal *term)
|
|
|
|
|
{
|
|
|
|
|
if (term->kitty_clipboard.active_mime_type == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
LOG_DBG("committing %zu bytes of %s data",
|
|
|
|
|
term->kitty_clipboard.data_len,
|
|
|
|
|
term->kitty_clipboard.active_mime_type);
|
|
|
|
|
|
|
|
|
|
tll_push_back(term->kitty_clipboard.committed_mime_data,
|
|
|
|
|
((struct kitty_mime_data){
|
|
|
|
|
.mime_type = term->kitty_clipboard.active_mime_type,
|
|
|
|
|
.data = term->kitty_clipboard.data,
|
|
|
|
|
.data_len = term->kitty_clipboard.data_len}));
|
|
|
|
|
|
|
|
|
|
term->kitty_clipboard.active_mime_type = NULL;
|
|
|
|
|
term->kitty_clipboard.data = NULL;
|
|
|
|
|
term->kitty_clipboard.data_len = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
kitty_clipboard_write_finish(struct terminal *term)
|
|
|
|
|
{
|
|
|
|
|
if (term->kitty_clipboard.has_error) {
|
|
|
|
|
/* There were error(s) along the way - report the first error */
|
|
|
|
|
char reply[64];
|
|
|
|
|
int n = snprintf(reply, sizeof(reply),
|
|
|
|
|
"\033]5522;type=write:status=%s\033\\",
|
|
|
|
|
term->kitty_clipboard.error);
|
|
|
|
|
term_to_slave(term, reply, n);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* No errors, now it's time to actually write to the clipboard
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
struct seat *seat = term_first_focused_seat(term);
|
|
|
|
|
if (seat == NULL) {
|
|
|
|
|
LOG_WARN("OSC-5522: client tried to write6 clipboard data "
|
|
|
|
|
"while window was unfocused");
|
|
|
|
|
term_to_slave(term, "\033]5522;type=write:status=ENOSYS\033\\", 33);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const size_t data_count = tll_length(term->kitty_clipboard.committed_mime_data);
|
|
|
|
|
const size_t mime_count = data_count + tll_length(term->kitty_clipboard.mime_aliases);
|
|
|
|
|
|
|
|
|
|
/* Package clipboard contents for text_to_{clipboard,primary}() */
|
|
|
|
|
struct kitty_clipboard_offer clip;
|
|
|
|
|
clip.data = xmalloc(data_count * sizeof(seat->clipboard.kitty.data[0]));
|
|
|
|
|
clip.data_len = xmalloc(data_count * sizeof(seat->clipboard.kitty.data_len[0]));
|
|
|
|
|
clip.data_count = data_count;
|
|
|
|
|
clip.mime_data_map = xmalloc(mime_count * sizeof(seat->clipboard.kitty.mime_data_map[0]));
|
|
|
|
|
clip.mime_data_map_count = mime_count;
|
|
|
|
|
|
|
|
|
|
size_t i = 0;
|
|
|
|
|
tll_foreach(term->kitty_clipboard.committed_mime_data, it) {
|
|
|
|
|
clip.data[i] = it->item.data;
|
|
|
|
|
clip.data_len[i] = it->item.data_len;
|
|
|
|
|
clip.mime_data_map[i].mime_type = it->item.mime_type;
|
|
|
|
|
clip.mime_data_map[i].data_idx = i;
|
|
|
|
|
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
|
|
/* Remove without freeing, data is now owned by clip */
|
|
|
|
|
tll_remove(term->kitty_clipboard.committed_mime_data, it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tll_foreach(term->kitty_clipboard.mime_aliases, it) {
|
|
|
|
|
const struct kitty_mime_alias *alias = &it->item;
|
|
|
|
|
|
|
|
|
|
/* Find target index */
|
|
|
|
|
ssize_t idx = -1;
|
|
|
|
|
for (size_t j = 0; j < data_count; j++) {
|
|
|
|
|
if (streq(clip.mime_data_map[j].mime_type, alias->target)) {
|
|
|
|
|
idx = j;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
xassert(idx >= 0);
|
|
|
|
|
xassert(idx < data_count);
|
|
|
|
|
|
|
|
|
|
clip.mime_data_map[i].mime_type = alias->alias;
|
|
|
|
|
clip.mime_data_map[i].data_idx = idx;
|
|
|
|
|
|
|
|
|
|
/* TODO: can we make target point to the original mime-type, so that we don't have to free it here? */
|
|
|
|
|
free(alias->target);
|
|
|
|
|
tll_remove(term->kitty_clipboard.mime_aliases, it);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!term->kitty_clipboard.for_primary)
|
|
|
|
|
text_to_clipboard(seat, term, NULL, &clip, seat->kbd.serial);
|
|
|
|
|
else
|
|
|
|
|
text_to_primary(seat, term, NULL, &clip, seat->kbd.serial);
|
|
|
|
|
|
|
|
|
|
term_to_slave(term, "\033]5522;type=write:status=DONE\033\\", 31);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
kitty_clipboard(struct terminal *term, char *string)
|
|
|
|
|
{
|
|
|
|
|
char *ctx = NULL;
|
|
|
|
|
char *params = strtok_r(string, ";", &ctx);
|
|
|
|
|
char *encoded_payload = strtok_r(NULL, ";", &ctx);
|
|
|
|
|
|
|
|
|
|
enum { UNSPECIFIED, READ, WRITE, WDATA, WALIAS } type = UNSPECIFIED;
|
|
|
|
|
enum { CLIPBOARD, PRIMARY } location = CLIPBOARD;
|
|
|
|
|
const char *encoded_mime_type = NULL;
|
|
|
|
|
|
|
|
|
|
/* Parse the parameter (key/value pairs) section */
|
|
|
|
|
ctx = NULL;
|
|
|
|
|
for (char *p = strtok_r(params, ":", &ctx);
|
|
|
|
|
p != NULL;
|
|
|
|
|
p = strtok_r(NULL, ":", &ctx))
|
|
|
|
|
{
|
|
|
|
|
char *value = strchr(p, '=');
|
|
|
|
|
if (value == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
*value = '\0';
|
|
|
|
|
value++;
|
|
|
|
|
|
|
|
|
|
LOG_DBG("OSC-5522: param: %s=%s", p, value);
|
|
|
|
|
|
|
|
|
|
if (streq(p, "type")) {
|
|
|
|
|
if (streq(value, "read"))
|
|
|
|
|
type = READ;
|
|
|
|
|
else if (streq(value, "write"))
|
|
|
|
|
type = WRITE;
|
|
|
|
|
else if (streq(value, "wdata"))
|
|
|
|
|
type = WDATA;
|
|
|
|
|
else if (streq(value, "walias"))
|
|
|
|
|
type = WALIAS;
|
|
|
|
|
else
|
|
|
|
|
LOG_WARN("OSC-5522: invalid 'type': %s", value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (streq(p, "loc")) {
|
|
|
|
|
if (streq(value, "primary"))
|
|
|
|
|
location = PRIMARY;
|
|
|
|
|
else
|
|
|
|
|
LOG_WARN("OSC-5522: invalid 'loc': %s", value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (streq(p, "mime")) {
|
|
|
|
|
encoded_mime_type = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else if (streq(p, "pw"))
|
|
|
|
|
;
|
|
|
|
|
else if (streq(p, "name"))
|
|
|
|
|
;
|
|
|
|
|
else
|
|
|
|
|
LOG_WARN("OSC-5522: unrecognized key/value parameter: %s=%s",
|
|
|
|
|
p, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (type == UNSPECIFIED) {
|
|
|
|
|
LOG_WARN("OSC-5522: 'type' not specified");
|
|
|
|
|
/* Can't reply with status=EINVAL since we don't have a valid type */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *mime_type = encoded_mime_type != NULL
|
|
|
|
|
? base64_decode(encoded_mime_type, NULL)
|
|
|
|
|
: NULL;
|
|
|
|
|
|
|
|
|
|
size_t payload_len = 0;
|
|
|
|
|
char *payload = encoded_payload != NULL
|
|
|
|
|
? base64_decode(encoded_payload, &payload_len)
|
|
|
|
|
: NULL;
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case READ:
|
|
|
|
|
if (unlikely(payload == NULL))
|
|
|
|
|
term_to_slave(term, "\033]5522;type=read:status=EINVAL\033\\", 32);
|
|
|
|
|
else {
|
|
|
|
|
if (streq(payload, "."))
|
|
|
|
|
kitty_clipboard_query(term, location == PRIMARY);
|
|
|
|
|
else
|
|
|
|
|
kitty_clipboard_read(term, location == PRIMARY, payload);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WRITE: {
|
|
|
|
|
/* Reset */
|
|
|
|
|
kitty_clipboard_reset(term);
|
|
|
|
|
|
|
|
|
|
if (!term_osc_copy_allowed(term)) {
|
|
|
|
|
LOG_DBG("OSC-5522: ignoring copy request: disabled in configuration");
|
|
|
|
|
term->kitty_clipboard.has_error = true;
|
|
|
|
|
term->kitty_clipboard.error = "EPERM";
|
|
|
|
|
} else if (term->is_sending_paste_data) {
|
|
|
|
|
term->kitty_clipboard.has_error = true;
|
|
|
|
|
term->kitty_clipboard.error = "EBUSY";
|
|
|
|
|
} else
|
|
|
|
|
term->kitty_clipboard.for_primary = location == PRIMARY;
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case WDATA:
|
|
|
|
|
if (encoded_mime_type == NULL && encoded_payload == NULL) {
|
|
|
|
|
/* This was the last packet */
|
|
|
|
|
kitty_clipboard_wdata_commit(term);
|
|
|
|
|
kitty_clipboard_write_finish(term);
|
|
|
|
|
kitty_clipboard_reset(term);
|
|
|
|
|
} else if (!term->kitty_clipboard.has_error) {
|
|
|
|
|
if (unlikely(mime_type == NULL || payload == NULL)) {
|
|
|
|
|
term->kitty_clipboard.has_error = true;
|
|
|
|
|
term->kitty_clipboard.error = "EINVAL";
|
|
|
|
|
} else {
|
|
|
|
|
if (term->kitty_clipboard.active_mime_type == NULL ||
|
|
|
|
|
!streq(term->kitty_clipboard.active_mime_type, mime_type))
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* This chunk has a different mime-type than the
|
|
|
|
|
* last one. We need to commit the last one, start
|
|
|
|
|
* a new buffer for the new mime-type.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (term->kitty_clipboard.active_mime_type != NULL)
|
|
|
|
|
kitty_clipboard_wdata_commit(term);
|
|
|
|
|
|
|
|
|
|
term->kitty_clipboard.active_mime_type = mime_type;
|
|
|
|
|
mime_type = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const size_t old_len = term->kitty_clipboard.data_len;
|
|
|
|
|
const size_t new_len = old_len + payload_len;
|
|
|
|
|
term->kitty_clipboard.data = xrealloc(term->kitty_clipboard.data, new_len);
|
|
|
|
|
|
|
|
|
|
memcpy(&term->kitty_clipboard.data[old_len], payload, payload_len);
|
|
|
|
|
term->kitty_clipboard.data_len = new_len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case WALIAS:
|
|
|
|
|
if (!term->kitty_clipboard.has_error) {
|
|
|
|
|
if (unlikely(mime_type == NULL || payload == NULL)) {
|
|
|
|
|
term->kitty_clipboard.has_error = true;
|
|
|
|
|
term->kitty_clipboard.error = "EINVAL";
|
|
|
|
|
} else {
|
|
|
|
|
/* Aliases is a space separated list of mime-types */
|
|
|
|
|
char *save = NULL;
|
|
|
|
|
for (const char *alias = strtok_r(payload, " ", &save);
|
|
|
|
|
alias != NULL;
|
|
|
|
|
alias = strtok_r(NULL, " ", &save))
|
|
|
|
|
{
|
|
|
|
|
tll_push_back(
|
|
|
|
|
term->kitty_clipboard.mime_aliases,
|
|
|
|
|
((struct kitty_mime_alias){
|
|
|
|
|
.target = xstrdup(mime_type),
|
|
|
|
|
.alias = xstrdup(alias)}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case UNSPECIFIED:
|
|
|
|
|
BUG("'type' is unspecified");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(mime_type);
|
|
|
|
|
free(payload);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-22 19:10:15 +02:00
|
|
|
static void
|
|
|
|
|
osc_flash(struct terminal *term)
|
|
|
|
|
{
|
|
|
|
|
/* Our own private - flash */
|
2019-07-30 22:08:58 +02:00
|
|
|
term_flash(term, 50);
|
2019-07-22 19:10:15 +02:00
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:29:03 +02:00
|
|
|
static bool
|
2021-04-05 22:05:50 +02:00
|
|
|
parse_legacy_color(const char *string, uint32_t *color, bool *_have_alpha,
|
|
|
|
|
uint16_t *_alpha)
|
2019-08-28 17:29:03 +02:00
|
|
|
{
|
2021-04-05 22:05:50 +02:00
|
|
|
bool have_alpha = false;
|
|
|
|
|
uint16_t alpha = 0xffff;
|
|
|
|
|
|
|
|
|
|
if (string[0] == '[') {
|
2021-04-07 08:00:39 +02:00
|
|
|
/* e.g. \E]11;[50]#00ff00 */
|
2021-04-05 22:05:50 +02:00
|
|
|
const char *start = &string[1];
|
|
|
|
|
|
2021-04-07 08:00:39 +02:00
|
|
|
errno = 0;
|
|
|
|
|
char *end;
|
|
|
|
|
unsigned long percent = strtoul(start, &end, 10);
|
|
|
|
|
|
|
|
|
|
if (errno != 0 || *end != ']')
|
2021-04-05 22:05:50 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
have_alpha = true;
|
2021-04-07 08:00:39 +02:00
|
|
|
alpha = (0xffff * min(percent, 100) + 50) / 100;
|
2021-04-05 22:05:50 +02:00
|
|
|
|
|
|
|
|
string = end + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-28 17:29:03 +02:00
|
|
|
if (string[0] != '#')
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
string++;
|
|
|
|
|
const size_t len = strlen(string);
|
|
|
|
|
|
|
|
|
|
if (len % 3 != 0)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
const int digits = len / 3;
|
|
|
|
|
|
|
|
|
|
int rgb[3];
|
|
|
|
|
for (size_t i = 0; i < 3; i++) {
|
|
|
|
|
rgb[i] = 0;
|
|
|
|
|
for (size_t j = 0; j < digits; j++) {
|
|
|
|
|
size_t idx = i * digits + j;
|
|
|
|
|
char c = string[idx];
|
|
|
|
|
rgb[i] <<= 4;
|
|
|
|
|
|
|
|
|
|
if (!isxdigit(c))
|
|
|
|
|
rgb[i] |= 0;
|
|
|
|
|
else
|
|
|
|
|
rgb[i] |= c >= '0' && c <= '9' ? c - '0' :
|
|
|
|
|
c >= 'a' && c <= 'f' ? c - 'a' + 10 : c - 'A' + 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Values with less than 16 bits represent the *most
|
|
|
|
|
* significant bits*. I.e. the values are *not* scaled */
|
|
|
|
|
rgb[i] <<= 16 - (4 * digits);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Re-scale to 8-bit */
|
2020-05-02 22:57:12 +02:00
|
|
|
uint8_t r = 256 * (rgb[0] / 65536.);
|
|
|
|
|
uint8_t g = 256 * (rgb[1] / 65536.);
|
|
|
|
|
uint8_t b = 256 * (rgb[2] / 65536.);
|
2019-08-28 17:29:03 +02:00
|
|
|
|
2021-04-05 22:05:50 +02:00
|
|
|
LOG_DBG("legacy: %02x%02x%02x (alpha=%04x)", r, g, b,
|
|
|
|
|
have_alpha ? alpha : 0xffff);
|
|
|
|
|
|
2019-08-28 17:29:03 +02:00
|
|
|
*color = r << 16 | g << 8 | b;
|
2021-04-05 22:05:50 +02:00
|
|
|
|
|
|
|
|
if (_have_alpha != NULL)
|
|
|
|
|
*_have_alpha = have_alpha;
|
|
|
|
|
if (_alpha != NULL)
|
|
|
|
|
*_alpha = alpha;
|
2019-08-28 17:29:03 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-21 17:57:02 +02:00
|
|
|
static bool
|
2021-04-05 22:05:50 +02:00
|
|
|
parse_rgb(const char *string, uint32_t *color, bool *_have_alpha,
|
|
|
|
|
uint16_t *_alpha)
|
2019-08-21 17:57:02 +02:00
|
|
|
{
|
|
|
|
|
size_t len = strlen(string);
|
2021-04-05 22:05:50 +02:00
|
|
|
bool have_alpha = len >= 4 && strncmp(string, "rgba", 4) == 0;
|
2019-08-21 17:57:02 +02:00
|
|
|
|
2021-04-05 22:05:50 +02:00
|
|
|
/* Verify we have the minimum required length (for "") */
|
|
|
|
|
if (have_alpha) {
|
2021-10-20 17:05:36 +01:00
|
|
|
if (len < STRLEN("rgba:x/x/x/x"))
|
2021-04-05 22:05:50 +02:00
|
|
|
return false;
|
|
|
|
|
} else {
|
2021-10-20 17:05:36 +01:00
|
|
|
if (len < STRLEN("rgb:x/x/x"))
|
2021-04-05 22:05:50 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2019-08-21 17:57:02 +02:00
|
|
|
|
2024-02-06 12:36:45 +01:00
|
|
|
/* Verify prefix is "rgb:" or "rgba:" */
|
2021-04-05 22:05:50 +02:00
|
|
|
if (have_alpha) {
|
|
|
|
|
if (strncmp(string, "rgba:", 5) != 0)
|
|
|
|
|
return false;
|
|
|
|
|
string += 5;
|
|
|
|
|
len -= 5;
|
|
|
|
|
} else {
|
|
|
|
|
if (strncmp(string, "rgb:", 4) != 0)
|
|
|
|
|
return false;
|
|
|
|
|
string += 4;
|
|
|
|
|
len -= 4;
|
|
|
|
|
}
|
2019-08-21 17:57:02 +02:00
|
|
|
|
2021-04-05 22:05:50 +02:00
|
|
|
int rgb[4];
|
|
|
|
|
int digits[4];
|
2019-08-21 17:57:02 +02:00
|
|
|
|
2021-04-05 22:05:50 +02:00
|
|
|
for (size_t i = 0; i < (have_alpha ? 4 : 3); i++) {
|
2019-08-21 17:57:02 +02:00
|
|
|
for (rgb[i] = 0, digits[i] = 0;
|
|
|
|
|
len > 0 && *string != '/';
|
|
|
|
|
len--, string++, digits[i]++)
|
|
|
|
|
{
|
|
|
|
|
char c = *string;
|
|
|
|
|
rgb[i] <<= 4;
|
|
|
|
|
|
|
|
|
|
if (!isxdigit(c))
|
|
|
|
|
rgb[i] |= 0;
|
|
|
|
|
else
|
|
|
|
|
rgb[i] |= c >= '0' && c <= '9' ? c - '0' :
|
|
|
|
|
c >= 'a' && c <= 'f' ? c - 'a' + 10 : c - 'A' + 10;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-05 22:05:50 +02:00
|
|
|
if (i >= (have_alpha ? 3 : 2))
|
2019-08-21 17:57:02 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (len == 0 || *string != '/')
|
|
|
|
|
return false;
|
|
|
|
|
string++; len--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Re-scale to 8-bit */
|
2020-05-02 23:07:26 +02:00
|
|
|
uint8_t r = 256 * (rgb[0] / (double)(1 << (4 * digits[0])));
|
|
|
|
|
uint8_t g = 256 * (rgb[1] / (double)(1 << (4 * digits[1])));
|
|
|
|
|
uint8_t b = 256 * (rgb[2] / (double)(1 << (4 * digits[2])));
|
2019-08-21 17:57:02 +02:00
|
|
|
|
2021-04-05 22:31:09 +02:00
|
|
|
uint16_t alpha = 0xffff;
|
2021-04-05 22:05:50 +02:00
|
|
|
if (have_alpha)
|
|
|
|
|
alpha = 65536 * (rgb[3] / (double)(1 << (4 * digits[3])));
|
|
|
|
|
|
|
|
|
|
if (have_alpha)
|
|
|
|
|
LOG_DBG("rgba: %02x%02x%02x (alpha=%04x)", r, g, b, alpha);
|
|
|
|
|
else
|
|
|
|
|
LOG_DBG("rgb: %02x%02x%02x", r, g, b);
|
|
|
|
|
|
|
|
|
|
if (_have_alpha != NULL)
|
|
|
|
|
*_have_alpha = have_alpha;
|
|
|
|
|
if (_alpha != NULL)
|
|
|
|
|
*_alpha = alpha;
|
|
|
|
|
|
2019-08-21 17:57:02 +02:00
|
|
|
*color = r << 16 | g << 8 | b;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-21 19:42:59 +01:00
|
|
|
static void
|
|
|
|
|
osc_set_pwd(struct terminal *term, char *string)
|
|
|
|
|
{
|
|
|
|
|
LOG_DBG("PWD: URI: %s", string);
|
|
|
|
|
|
2020-10-28 19:11:22 +01:00
|
|
|
char *scheme, *host, *path;
|
|
|
|
|
if (!uri_parse(string, strlen(string), &scheme, NULL, NULL, &host, NULL, &path, NULL, NULL)) {
|
|
|
|
|
LOG_ERR("OSC7: invalid URI: %s", string);
|
2020-04-03 20:15:54 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-24 23:17:28 +00:00
|
|
|
if (streq(scheme, "file") && hostname_is_localhost(host)) {
|
2020-10-28 19:11:22 +01:00
|
|
|
LOG_DBG("OSC7: pwd: %s", path);
|
|
|
|
|
free(term->cwd);
|
|
|
|
|
term->cwd = path;
|
|
|
|
|
} else
|
|
|
|
|
free(path);
|
2019-12-21 19:42:59 +01:00
|
|
|
|
2020-10-28 19:11:22 +01:00
|
|
|
free(scheme);
|
|
|
|
|
free(host);
|
2019-12-21 19:42:59 +01:00
|
|
|
}
|
|
|
|
|
|
2021-02-13 12:41:57 +01:00
|
|
|
static void
|
|
|
|
|
osc_uri(struct terminal *term, char *string)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* \E]8;<params>;URI\e\\
|
|
|
|
|
*
|
2024-02-06 12:36:45 +01:00
|
|
|
* Params are key=value pairs, separated by ':'.
|
2021-02-13 12:41:57 +01:00
|
|
|
*
|
2024-02-06 12:36:45 +01:00
|
|
|
* The only defined key (as of 2020-05-31) is 'id', which is used
|
2021-02-13 12:41:57 +01:00
|
|
|
* to group split-up URIs:
|
|
|
|
|
*
|
|
|
|
|
* ╔═ file1 ════╗
|
|
|
|
|
* ║ ╔═ file2 ═══╗
|
|
|
|
|
* ║http://exa║Lorem ipsum║
|
|
|
|
|
* ║le.com ║ dolor sit ║
|
|
|
|
|
* ║ ║amet, conse║
|
|
|
|
|
* ╚══════════║ctetur adip║
|
|
|
|
|
* ╚═══════════╝
|
|
|
|
|
*
|
|
|
|
|
* This lets a terminal emulator highlight both parts at the same
|
|
|
|
|
* time (e.g. when hovering over one of the parts with the mouse).
|
|
|
|
|
*/
|
|
|
|
|
|
2021-02-13 13:41:27 +01:00
|
|
|
char *params = string;
|
2021-02-13 12:41:57 +01:00
|
|
|
char *params_end = strchr(params, ';');
|
|
|
|
|
if (params_end == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
*params_end = '\0';
|
|
|
|
|
const char *uri = params_end + 1;
|
2021-02-13 13:41:27 +01:00
|
|
|
uint64_t id = (uint64_t)rand() << 32 | rand();
|
2021-02-13 12:41:57 +01:00
|
|
|
|
2021-02-13 13:41:27 +01:00
|
|
|
char *ctx = NULL;
|
|
|
|
|
for (const char *key_value = strtok_r(params, ":", &ctx);
|
|
|
|
|
key_value != NULL;
|
|
|
|
|
key_value = strtok_r(NULL, ":", &ctx))
|
|
|
|
|
{
|
|
|
|
|
const char *key = key_value;
|
2025-12-16 22:10:39 -05:00
|
|
|
char *operator = (char *)strchr(key_value, '=');
|
2021-02-13 13:41:27 +01:00
|
|
|
|
|
|
|
|
if (operator == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
*operator = '\0';
|
|
|
|
|
|
|
|
|
|
const char *value = operator + 1;
|
|
|
|
|
|
2024-01-24 23:17:28 +00:00
|
|
|
if (streq(key, "id"))
|
2021-02-14 13:28:42 +01:00
|
|
|
id = sdbm_hash(value);
|
2021-02-13 13:41:27 +01:00
|
|
|
}
|
|
|
|
|
|
2021-02-14 13:28:42 +01:00
|
|
|
|
2026-02-02 11:19:07 +01:00
|
|
|
if (uri[0] == '\0') {
|
|
|
|
|
LOG_DBG("OSC-8: close");
|
2021-02-13 12:41:57 +01:00
|
|
|
term_osc8_close(term);
|
2026-02-02 11:19:07 +01:00
|
|
|
} else {
|
|
|
|
|
LOG_DBG("OSC-8: URL=%s, id=%" PRIu64, uri, id);
|
2021-02-13 13:44:07 +01:00
|
|
|
term_osc8_open(term, id, uri);
|
2026-02-02 11:19:07 +01:00
|
|
|
}
|
2021-02-13 12:41:57 +01:00
|
|
|
}
|
|
|
|
|
|
2019-12-19 07:28:10 +01:00
|
|
|
static void
|
|
|
|
|
osc_notify(struct terminal *term, char *string)
|
|
|
|
|
{
|
2020-12-08 19:19:55 +01:00
|
|
|
/*
|
|
|
|
|
* The 'notify' perl extension
|
|
|
|
|
* (https://pub.phyks.me/scripts/urxvt/notify) is very simple:
|
|
|
|
|
*
|
|
|
|
|
* #!/usr/bin/perl
|
2021-11-20 16:29:57 +01:00
|
|
|
*
|
2020-12-08 19:19:55 +01:00
|
|
|
* sub on_osc_seq_perl {
|
|
|
|
|
* my ($term, $osc, $resp) = @_;
|
|
|
|
|
* if ($osc =~ /^notify;(\S+);(.*)$/) {
|
|
|
|
|
* system("notify-send '$1' '$2'");
|
|
|
|
|
* }
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* As can be seen, the notification text is not encoded in any
|
|
|
|
|
* way. The regex does a greedy match of the ';' separator. Thus,
|
|
|
|
|
* any extra ';' will end up being part of the title. There's no
|
|
|
|
|
* way to have a ';' in the message body.
|
|
|
|
|
*
|
|
|
|
|
* I've changed that behavior slightly in; we split the title from
|
|
|
|
|
* body on the *first* ';', allowing us to have semicolons in the
|
|
|
|
|
* message body, but *not* in the title.
|
|
|
|
|
*/
|
2019-12-19 07:28:10 +01:00
|
|
|
char *ctx = NULL;
|
2020-12-08 19:19:55 +01:00
|
|
|
const char *title = strtok_r(string, ";", &ctx);
|
|
|
|
|
const char *msg = strtok_r(NULL, "\x00", &ctx);
|
2019-12-19 07:28:10 +01:00
|
|
|
|
2024-04-15 16:02:54 +02:00
|
|
|
if (title == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
2024-08-20 07:14:53 +02:00
|
|
|
if (mbsntoc32(NULL, title, strlen(title), 0) == (size_t)-1) {
|
2024-04-15 16:02:54 +02:00
|
|
|
LOG_WARN("%s: notification title is not valid UTF-8, ignoring", title);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-20 07:14:53 +02:00
|
|
|
if (msg != NULL && mbsntoc32(NULL, msg, strlen(msg), 0) == (size_t)-1) {
|
2024-04-15 16:02:54 +02:00
|
|
|
LOG_WARN("%s: notification message is not valid UTF-8, ignoring", msg);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-28 17:52:09 +00:00
|
|
|
char *msgdup = NULL;
|
|
|
|
|
if (msg != NULL)
|
|
|
|
|
msgdup = xstrdup(msg);
|
|
|
|
|
|
osc: kitty notifications: implement focus|report
This patch adds support for window focusing, and sending events back
to the client application when a notification is closed.
* Refactor notification related configuration options:
- add desktop-notifications sub-section
- deprecate 'notify' in favor of 'desktop-notifications.command'
- deprecate 'notify-focus-inhibit' in favor of
'desktop-notifications.inhibit-when-focused'
* Refactor: rename 'struct kitty_notification' to 'struct
notification'
* Pass a 'struct notification' to notify_notify(), instead of many
arguments.
* notify_notify() now registers a reaper callback. When the notifier
process has terminated, the notification is considered closed, and we
either try to focus (activate) the window, or send an event to the
client application, depending on the notification setting.
* For the window activation, we need an XDG activation token. For now,
assume *everything* written on stdout is part of the token.
* Refactor: remove much of the warnings from OSC-99; we don't
typically log anything when an OSC/CSI has invalid values.
* Add icon support to OSC-99. This isn't part of the upstream
spec. Foot's implementation:
- uses the 'I' parameter
- the value is expected to be a symbolic icon name
- a quick check for absolute paths is done, and such icon requests
are ignored.
* Added ${icon} to the 'desktop-notifications.command' template. Uses
the icon specified in the notification, or ${app-id} if not set.
2024-07-23 06:59:46 +02:00
|
|
|
notify_notify(term, &(struct notification){
|
2024-09-05 07:13:53 +02:00
|
|
|
.title = xstrdup(title),
|
2024-10-28 17:52:09 +00:00
|
|
|
.body = msgdup,
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
.expire_time = -1,
|
2024-09-05 07:13:53 +02:00
|
|
|
.focus = true,
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
});
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
}
|
|
|
|
|
|
2024-08-03 11:05:58 +02:00
|
|
|
IGNORE_WARNING("-Wpedantic")
|
|
|
|
|
static bool
|
|
|
|
|
verify_kitty_id_is_valid(const char *id)
|
|
|
|
|
{
|
|
|
|
|
const size_t len = strlen(id);
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
|
|
|
|
switch (id[i]) {
|
|
|
|
|
case 'a' ... 'z':
|
|
|
|
|
case 'A' ... 'Z':
|
|
|
|
|
case '0' ... '9':
|
|
|
|
|
case '_':
|
|
|
|
|
case '-':
|
|
|
|
|
case '+':
|
|
|
|
|
case '.':
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
UNIGNORE_WARNINGS
|
|
|
|
|
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
static void
|
|
|
|
|
kitty_notification(struct terminal *term, char *string)
|
|
|
|
|
{
|
|
|
|
|
/* https://sw.kovidgoyal.net/kitty/desktop-notifications */
|
|
|
|
|
|
2024-07-23 18:32:23 +02:00
|
|
|
char *payload_raw = strchr(string, ';');
|
|
|
|
|
if (payload_raw == NULL)
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
char *parameters = string;
|
2024-07-23 18:32:23 +02:00
|
|
|
*payload_raw = '\0';
|
|
|
|
|
payload_raw++;
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
char *id = NULL; /* The 'i' parameter */
|
|
|
|
|
char *app_id = NULL; /* The 'f' parameter */
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
char *icon_cache_id = NULL; /* The 'g' parameter */
|
2024-07-23 11:29:05 +02:00
|
|
|
char *symbolic_icon = NULL; /* The 'n' parameter */
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
char *category = NULL; /* The 't' parameter */
|
2024-08-04 14:16:56 +02:00
|
|
|
char *sound_name = NULL; /* The 's' parameter */
|
2024-07-23 18:32:23 +02:00
|
|
|
char *payload = NULL;
|
|
|
|
|
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
bool focus = true; /* The 'a' parameter */
|
2024-07-25 18:47:23 +02:00
|
|
|
bool report_activated = false; /* The 'a' parameter */
|
|
|
|
|
bool report_closed = false; /* The 'c' parameter */
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
bool done = true; /* The 'd' parameter */
|
|
|
|
|
bool base64 = false; /* The 'e' parameter */
|
2024-07-23 11:29:05 +02:00
|
|
|
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
int32_t expire_time = -1; /* The 'w' parameter */
|
|
|
|
|
|
2024-07-23 11:29:05 +02:00
|
|
|
size_t payload_size;
|
|
|
|
|
enum {
|
|
|
|
|
PAYLOAD_TITLE,
|
|
|
|
|
PAYLOAD_BODY,
|
2024-07-25 19:24:28 +02:00
|
|
|
PAYLOAD_CLOSE,
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
PAYLOAD_ALIVE,
|
2024-07-23 11:29:05 +02:00
|
|
|
PAYLOAD_ICON,
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
PAYLOAD_BUTTON,
|
2024-07-23 11:29:05 +02:00
|
|
|
} payload_type = PAYLOAD_TITLE; /* The 'p' parameter */
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
|
|
|
|
|
enum notify_when when = NOTIFY_ALWAYS;
|
|
|
|
|
enum notify_urgency urgency = NOTIFY_URGENCY_NORMAL;
|
|
|
|
|
|
|
|
|
|
bool have_a = false;
|
2024-07-25 18:47:23 +02:00
|
|
|
bool have_c = false;
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
bool have_o = false;
|
|
|
|
|
bool have_u = false;
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
bool have_w = false;
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
|
|
|
|
|
char *ctx = NULL;
|
|
|
|
|
for (char *param = strtok_r(parameters, ":", &ctx);
|
|
|
|
|
param != NULL;
|
|
|
|
|
param = strtok_r(NULL, ":", &ctx))
|
|
|
|
|
{
|
|
|
|
|
/* All parameters are on the form X=value, where X is always
|
|
|
|
|
exactly one character */
|
osc: kitty notifications: implement focus|report
This patch adds support for window focusing, and sending events back
to the client application when a notification is closed.
* Refactor notification related configuration options:
- add desktop-notifications sub-section
- deprecate 'notify' in favor of 'desktop-notifications.command'
- deprecate 'notify-focus-inhibit' in favor of
'desktop-notifications.inhibit-when-focused'
* Refactor: rename 'struct kitty_notification' to 'struct
notification'
* Pass a 'struct notification' to notify_notify(), instead of many
arguments.
* notify_notify() now registers a reaper callback. When the notifier
process has terminated, the notification is considered closed, and we
either try to focus (activate) the window, or send an event to the
client application, depending on the notification setting.
* For the window activation, we need an XDG activation token. For now,
assume *everything* written on stdout is part of the token.
* Refactor: remove much of the warnings from OSC-99; we don't
typically log anything when an OSC/CSI has invalid values.
* Add icon support to OSC-99. This isn't part of the upstream
spec. Foot's implementation:
- uses the 'I' parameter
- the value is expected to be a symbolic icon name
- a quick check for absolute paths is done, and such icon requests
are ignored.
* Added ${icon} to the 'desktop-notifications.command' template. Uses
the icon specified in the notification, or ${app-id} if not set.
2024-07-23 06:59:46 +02:00
|
|
|
if (param[0] == '\0' || param[1] != '=')
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
char *value = ¶m[2];
|
|
|
|
|
|
|
|
|
|
switch (param[0]) {
|
|
|
|
|
case 'a': {
|
|
|
|
|
/* notification activation action: focus|report|-focus|-report */
|
|
|
|
|
have_a = true;
|
|
|
|
|
char *a_ctx = NULL;
|
osc: kitty notifications: implement focus|report
This patch adds support for window focusing, and sending events back
to the client application when a notification is closed.
* Refactor notification related configuration options:
- add desktop-notifications sub-section
- deprecate 'notify' in favor of 'desktop-notifications.command'
- deprecate 'notify-focus-inhibit' in favor of
'desktop-notifications.inhibit-when-focused'
* Refactor: rename 'struct kitty_notification' to 'struct
notification'
* Pass a 'struct notification' to notify_notify(), instead of many
arguments.
* notify_notify() now registers a reaper callback. When the notifier
process has terminated, the notification is considered closed, and we
either try to focus (activate) the window, or send an event to the
client application, depending on the notification setting.
* For the window activation, we need an XDG activation token. For now,
assume *everything* written on stdout is part of the token.
* Refactor: remove much of the warnings from OSC-99; we don't
typically log anything when an OSC/CSI has invalid values.
* Add icon support to OSC-99. This isn't part of the upstream
spec. Foot's implementation:
- uses the 'I' parameter
- the value is expected to be a symbolic icon name
- a quick check for absolute paths is done, and such icon requests
are ignored.
* Added ${icon} to the 'desktop-notifications.command' template. Uses
the icon specified in the notification, or ${app-id} if not set.
2024-07-23 06:59:46 +02:00
|
|
|
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
for (const char *v = strtok_r(value, ",", &a_ctx);
|
|
|
|
|
v != NULL;
|
|
|
|
|
v = strtok_r(NULL, ",", &a_ctx))
|
|
|
|
|
{
|
|
|
|
|
bool reverse = v[0] == '-';
|
|
|
|
|
if (reverse)
|
|
|
|
|
v++;
|
|
|
|
|
|
2024-07-24 16:04:14 +02:00
|
|
|
if (streq(v, "focus"))
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
focus = !reverse;
|
2024-07-24 16:04:14 +02:00
|
|
|
else if (streq(v, "report"))
|
2024-07-25 18:47:23 +02:00
|
|
|
report_activated = !reverse;
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-25 18:47:23 +02:00
|
|
|
case 'c':
|
|
|
|
|
if (value[0] == '1' && value[1] == '\0')
|
|
|
|
|
report_closed = true;
|
|
|
|
|
else if (value[0] == '0' && value[1] == '\0')
|
|
|
|
|
report_closed = false;
|
|
|
|
|
have_c = true;
|
|
|
|
|
break;
|
|
|
|
|
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
case 'd':
|
|
|
|
|
/* done: 0|1 */
|
|
|
|
|
if (value[0] == '0' && value[1] == '\0')
|
|
|
|
|
done = false;
|
|
|
|
|
else if (value[0] == '1' && value[1] == '\0')
|
|
|
|
|
done = true;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'e':
|
2024-07-23 12:12:38 +02:00
|
|
|
/* base64 (payload encoding): 0=utf8, 1=base64(utf8) */
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
if (value[0] == '0' && value[1] == '\0')
|
|
|
|
|
base64 = false;
|
|
|
|
|
else if (value[0] == '1' && value[1] == '\0')
|
|
|
|
|
base64 = true;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'i':
|
|
|
|
|
/* id */
|
2024-08-03 11:05:58 +02:00
|
|
|
if (verify_kitty_id_is_valid(value)) {
|
|
|
|
|
free(id);
|
|
|
|
|
id = xstrdup(value);
|
|
|
|
|
} else
|
|
|
|
|
LOG_WARN("OSC-99: ignoring invalid 'i' identifier");
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'p':
|
|
|
|
|
/* payload content: title|body */
|
2024-07-24 16:04:14 +02:00
|
|
|
if (streq(value, "title"))
|
2024-07-23 11:29:05 +02:00
|
|
|
payload_type = PAYLOAD_TITLE;
|
2024-07-24 16:04:14 +02:00
|
|
|
else if (streq(value, "body"))
|
2024-07-23 11:29:05 +02:00
|
|
|
payload_type = PAYLOAD_BODY;
|
2024-07-25 19:24:28 +02:00
|
|
|
else if (streq(value, "close"))
|
|
|
|
|
payload_type = PAYLOAD_CLOSE;
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
else if (streq(value, "alive"))
|
|
|
|
|
payload_type = PAYLOAD_ALIVE;
|
2024-07-24 16:04:14 +02:00
|
|
|
else if (streq(value, "icon"))
|
2024-07-23 11:29:05 +02:00
|
|
|
payload_type = PAYLOAD_ICON;
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
else if (streq(value, "buttons"))
|
|
|
|
|
payload_type = PAYLOAD_BUTTON;
|
2024-07-24 16:04:14 +02:00
|
|
|
else if (streq(value, "?")) {
|
2024-07-23 18:32:23 +02:00
|
|
|
/* Query capabilities */
|
|
|
|
|
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
const char *reply_id = id != NULL ? id : "0";
|
|
|
|
|
|
|
|
|
|
const char *p_caps = "title,body,?,close,alive,icon,buttons";
|
|
|
|
|
const char *a_caps = "focus,report";
|
|
|
|
|
const char *u_caps = "0,1,2";
|
|
|
|
|
|
|
|
|
|
char when_caps[64];
|
|
|
|
|
strcpy(when_caps, "unfocused");
|
2024-07-23 18:32:23 +02:00
|
|
|
if (!term->conf->desktop_notifications.inhibit_when_focused)
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
strcat(when_caps, ",always");
|
2024-07-23 18:32:23 +02:00
|
|
|
|
|
|
|
|
const char *terminator = term->vt.osc.bel ? "\a" : "\033\\";
|
|
|
|
|
|
2026-05-14 12:41:58 +02:00
|
|
|
char reply[512];
|
2024-09-11 20:13:30 +01:00
|
|
|
size_t n = xsnprintf(
|
2024-07-23 18:32:23 +02:00
|
|
|
reply, sizeof(reply),
|
2024-08-09 08:25:36 +02:00
|
|
|
"\033]99;i=%s:p=?;p=%s:a=%s:o=%s:u=%s:c=1:w=1:s=system,silent,error,warn,warning,info,question%s",
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
reply_id, p_caps, a_caps, when_caps, u_caps, terminator);
|
2024-07-23 18:32:23 +02:00
|
|
|
|
|
|
|
|
term_to_slave(term, reply, n);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'o':
|
|
|
|
|
/* honor when: always|unfocused|invisible */
|
|
|
|
|
have_o = true;
|
2024-07-24 16:04:14 +02:00
|
|
|
if (streq(value, "always"))
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
when = NOTIFY_ALWAYS;
|
2024-07-24 16:04:14 +02:00
|
|
|
else if (streq(value, "unfocused"))
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
when = NOTIFY_UNFOCUSED;
|
2024-07-24 16:04:14 +02:00
|
|
|
else if (streq(value, "invisible"))
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
when = NOTIFY_INVISIBLE;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'u':
|
|
|
|
|
/* urgency: 0=low, 1=normal, 2=critical */
|
|
|
|
|
have_u = true;
|
|
|
|
|
if (value[0] == '0' && value[1] == '\0')
|
|
|
|
|
urgency = NOTIFY_URGENCY_LOW;
|
|
|
|
|
else if (value[0] == '1' && value[1] == '\0')
|
|
|
|
|
urgency = NOTIFY_URGENCY_NORMAL;
|
|
|
|
|
else if (value[0] == '2' && value[1] == '\0')
|
|
|
|
|
urgency = NOTIFY_URGENCY_CRITICAL;
|
|
|
|
|
break;
|
|
|
|
|
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
case 'w': {
|
|
|
|
|
/* Notification timeout */
|
|
|
|
|
errno = 0;
|
|
|
|
|
char *end = NULL;
|
|
|
|
|
long timeout = strtol(value, &end, 10);
|
|
|
|
|
|
|
|
|
|
if (errno == 0 && *end == '\0' && timeout <= INT32_MAX) {
|
|
|
|
|
expire_time = timeout;
|
|
|
|
|
have_w = true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-04 14:16:56 +02:00
|
|
|
case 'f': {
|
|
|
|
|
/* App-name */
|
|
|
|
|
char *decoded = base64_decode(value, NULL);
|
|
|
|
|
if (decoded != NULL) {
|
|
|
|
|
free(app_id);
|
|
|
|
|
app_id = decoded;
|
|
|
|
|
}
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
break;
|
2024-08-04 14:16:56 +02:00
|
|
|
}
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
|
|
|
|
|
case 't': {
|
|
|
|
|
/* Type (category) */
|
|
|
|
|
char *decoded = base64_decode(value, NULL);
|
|
|
|
|
if (decoded != NULL) {
|
|
|
|
|
if (category == NULL)
|
|
|
|
|
category = decoded;
|
|
|
|
|
else {
|
|
|
|
|
/* Append, comma separated */
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
char *old_category = category;
|
2024-08-03 08:12:13 +01:00
|
|
|
category = xstrjoin3(old_category, ",", decoded);
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
free(decoded);
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
free(old_category);
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-04 14:16:56 +02:00
|
|
|
case 's': {
|
|
|
|
|
/* Sound */
|
|
|
|
|
char *decoded = base64_decode(value, NULL);
|
|
|
|
|
if (decoded != NULL) {
|
|
|
|
|
free(sound_name);
|
|
|
|
|
sound_name = decoded;
|
2024-08-09 08:25:36 +02:00
|
|
|
|
|
|
|
|
const char *translated_name = NULL;
|
|
|
|
|
|
|
|
|
|
if (streq(decoded, "error"))
|
|
|
|
|
translated_name = "dialog-error";
|
|
|
|
|
else if (streq(decoded, "warn") || streq(decoded, "warning"))
|
|
|
|
|
translated_name = "dialog-warning";
|
|
|
|
|
else if (streq(decoded, "info"))
|
|
|
|
|
translated_name = "dialog-information";
|
|
|
|
|
else if (streq(decoded, "question"))
|
|
|
|
|
translated_name = "dialog-question";
|
|
|
|
|
|
|
|
|
|
if (translated_name != NULL) {
|
|
|
|
|
free(sound_name);
|
|
|
|
|
sound_name = xstrdup(translated_name);
|
|
|
|
|
}
|
2024-08-04 14:16:56 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 11:29:05 +02:00
|
|
|
case 'g':
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
/* graphical ID (see 'n' and 'p=icon') */
|
|
|
|
|
free(icon_cache_id);
|
|
|
|
|
icon_cache_id = xstrdup(value);
|
2024-07-23 11:29:05 +02:00
|
|
|
break;
|
|
|
|
|
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
case 'n': {
|
|
|
|
|
/* Symbolic icon name, may used with 'g' */
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
/*
|
|
|
|
|
* Sigh, protocol says 'n' can be used multiple times, and
|
|
|
|
|
* that the terminal picks the first one that it can
|
|
|
|
|
* resolve.
|
|
|
|
|
*
|
|
|
|
|
* We can't resolve any icons at all. So, enter
|
|
|
|
|
* heuristics... let's pick the *shortest* symbolic
|
|
|
|
|
* name. The idea is that icon *names* are typically
|
|
|
|
|
* shorter than .desktop names, and macOS bundle
|
|
|
|
|
* identifiers.
|
|
|
|
|
*/
|
|
|
|
|
char *maybe_new_symbolic_icon = base64_decode(value, NULL);
|
|
|
|
|
if (maybe_new_symbolic_icon == NULL)
|
|
|
|
|
break;
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
if (symbolic_icon == NULL ||
|
|
|
|
|
strlen(maybe_new_symbolic_icon) < strlen(symbolic_icon))
|
|
|
|
|
{
|
|
|
|
|
free(symbolic_icon);
|
|
|
|
|
symbolic_icon = maybe_new_symbolic_icon;
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
/* Translate OSC-99 "special" names */
|
|
|
|
|
if (symbolic_icon != NULL) {
|
|
|
|
|
const char *translated_name = NULL;
|
|
|
|
|
|
|
|
|
|
if (streq(symbolic_icon, "error"))
|
|
|
|
|
translated_name = "dialog-error";
|
|
|
|
|
else if (streq(symbolic_icon, "warn") ||
|
|
|
|
|
streq(symbolic_icon, "warning"))
|
|
|
|
|
translated_name = "dialog-warning";
|
|
|
|
|
else if (streq(symbolic_icon, "info"))
|
|
|
|
|
translated_name = "dialog-information";
|
|
|
|
|
else if (streq(symbolic_icon, "question"))
|
|
|
|
|
translated_name = "dialog-question";
|
|
|
|
|
else if (streq(symbolic_icon, "help"))
|
|
|
|
|
translated_name = "system-help";
|
|
|
|
|
else if (streq(symbolic_icon, "file-manager"))
|
|
|
|
|
translated_name = "system-file-manager";
|
|
|
|
|
else if (streq(symbolic_icon, "system-monitor"))
|
|
|
|
|
translated_name = "utilities-system-monitor";
|
|
|
|
|
else if (streq(symbolic_icon, "text-editor"))
|
|
|
|
|
translated_name = "text-editor";
|
|
|
|
|
|
|
|
|
|
if (translated_name != NULL) {
|
|
|
|
|
free(symbolic_icon);
|
|
|
|
|
symbolic_icon = xstrdup(translated_name);
|
|
|
|
|
}
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
}
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
} else {
|
|
|
|
|
free(maybe_new_symbolic_icon);
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
}
|
osc: kitty notifications: implement focus|report
This patch adds support for window focusing, and sending events back
to the client application when a notification is closed.
* Refactor notification related configuration options:
- add desktop-notifications sub-section
- deprecate 'notify' in favor of 'desktop-notifications.command'
- deprecate 'notify-focus-inhibit' in favor of
'desktop-notifications.inhibit-when-focused'
* Refactor: rename 'struct kitty_notification' to 'struct
notification'
* Pass a 'struct notification' to notify_notify(), instead of many
arguments.
* notify_notify() now registers a reaper callback. When the notifier
process has terminated, the notification is considered closed, and we
either try to focus (activate) the window, or send an event to the
client application, depending on the notification setting.
* For the window activation, we need an XDG activation token. For now,
assume *everything* written on stdout is part of the token.
* Refactor: remove much of the warnings from OSC-99; we don't
typically log anything when an OSC/CSI has invalid values.
* Add icon support to OSC-99. This isn't part of the upstream
spec. Foot's implementation:
- uses the 'I' parameter
- the value is expected to be a symbolic icon name
- a quick check for absolute paths is done, and such icon requests
are ignored.
* Added ${icon} to the 'desktop-notifications.command' template. Uses
the icon specified in the notification, or ${app-id} if not set.
2024-07-23 06:59:46 +02:00
|
|
|
break;
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
}
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
}
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-23 11:29:05 +02:00
|
|
|
if (base64) {
|
2024-07-23 18:32:23 +02:00
|
|
|
payload = base64_decode(payload_raw, &payload_size);
|
2024-07-23 11:29:05 +02:00
|
|
|
if (payload == NULL)
|
|
|
|
|
goto out;
|
|
|
|
|
} else {
|
2024-07-23 18:32:23 +02:00
|
|
|
payload = xstrdup(payload_raw);
|
2024-07-23 11:29:05 +02:00
|
|
|
payload_size = strlen(payload);
|
|
|
|
|
}
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
/* Append metadata to previous notification chunk */
|
|
|
|
|
struct notification *notif = &term->kitty_notification;
|
|
|
|
|
|
|
|
|
|
if (!((id == NULL && notif->id == NULL) ||
|
|
|
|
|
(id != NULL && notif->id != NULL && streq(id, notif->id))) ||
|
|
|
|
|
!notif->may_be_programatically_closed) /* Free:d notification has this as false... */
|
|
|
|
|
{
|
|
|
|
|
/* ID mismatch, ignore previous notification state */
|
|
|
|
|
notify_free(term, notif);
|
|
|
|
|
|
|
|
|
|
notif->id = id;
|
|
|
|
|
notif->when = when;
|
|
|
|
|
notif->urgency = urgency;
|
|
|
|
|
notif->expire_time = expire_time;
|
|
|
|
|
notif->focus = focus;
|
|
|
|
|
notif->may_be_programatically_closed = true;
|
|
|
|
|
notif->report_activated = report_activated;
|
|
|
|
|
notif->report_closed = report_closed;
|
|
|
|
|
|
|
|
|
|
id = NULL; /* Prevent double free */
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (have_a) {
|
|
|
|
|
notif->focus = focus;
|
2024-07-25 18:47:23 +02:00
|
|
|
notif->report_activated = report_activated;
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-25 18:47:23 +02:00
|
|
|
if (have_c)
|
|
|
|
|
notif->report_closed = report_closed;
|
|
|
|
|
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
if (have_o)
|
|
|
|
|
notif->when = when;
|
|
|
|
|
if (have_u)
|
|
|
|
|
notif->urgency = urgency;
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
if (have_w)
|
|
|
|
|
notif->expire_time = expire_time;
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
if (icon_cache_id != NULL) {
|
|
|
|
|
free(notif->icon_cache_id);
|
|
|
|
|
notif->icon_cache_id = icon_cache_id;
|
|
|
|
|
icon_cache_id = NULL; /* Prevent double free */
|
osc: kitty notifications: implement focus|report
This patch adds support for window focusing, and sending events back
to the client application when a notification is closed.
* Refactor notification related configuration options:
- add desktop-notifications sub-section
- deprecate 'notify' in favor of 'desktop-notifications.command'
- deprecate 'notify-focus-inhibit' in favor of
'desktop-notifications.inhibit-when-focused'
* Refactor: rename 'struct kitty_notification' to 'struct
notification'
* Pass a 'struct notification' to notify_notify(), instead of many
arguments.
* notify_notify() now registers a reaper callback. When the notifier
process has terminated, the notification is considered closed, and we
either try to focus (activate) the window, or send an event to the
client application, depending on the notification setting.
* For the window activation, we need an XDG activation token. For now,
assume *everything* written on stdout is part of the token.
* Refactor: remove much of the warnings from OSC-99; we don't
typically log anything when an OSC/CSI has invalid values.
* Add icon support to OSC-99. This isn't part of the upstream
spec. Foot's implementation:
- uses the 'I' parameter
- the value is expected to be a symbolic icon name
- a quick check for absolute paths is done, and such icon requests
are ignored.
* Added ${icon} to the 'desktop-notifications.command' template. Uses
the icon specified in the notification, or ${app-id} if not set.
2024-07-23 06:59:46 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-23 11:29:05 +02:00
|
|
|
if (symbolic_icon != NULL) {
|
|
|
|
|
free(notif->icon_symbolic_name);
|
|
|
|
|
notif->icon_symbolic_name = symbolic_icon;
|
|
|
|
|
symbolic_icon = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
if (app_id != NULL) {
|
|
|
|
|
free(notif->app_id);
|
|
|
|
|
notif->app_id = app_id;
|
|
|
|
|
app_id = NULL; /* Prevent double free */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (category != NULL) {
|
|
|
|
|
if (notif->category == NULL) {
|
|
|
|
|
notif->category = category;
|
|
|
|
|
category = NULL; /* Prevent double free */
|
|
|
|
|
} else {
|
|
|
|
|
/* Append, comma separated */
|
2024-08-03 08:12:13 +01:00
|
|
|
char *new_category = xstrjoin3(notif->category, ",", category);
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
free(notif->category);
|
|
|
|
|
notif->category = new_category;
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-04 14:16:56 +02:00
|
|
|
if (sound_name != NULL) {
|
|
|
|
|
notif->muted = streq(sound_name, "silent");
|
2024-08-04 15:21:06 +02:00
|
|
|
|
|
|
|
|
if (notif->muted || streq(sound_name, "system")) {
|
|
|
|
|
free(notif->sound_name);
|
|
|
|
|
notif->sound_name = NULL;
|
|
|
|
|
} else {
|
|
|
|
|
free(notif->sound_name);
|
|
|
|
|
notif->sound_name = sound_name;
|
|
|
|
|
sound_name = NULL; /* Prevent double free */
|
|
|
|
|
}
|
2024-08-04 14:16:56 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-23 11:29:05 +02:00
|
|
|
/* Handled chunked payload - append to existing metadata */
|
|
|
|
|
switch (payload_type) {
|
|
|
|
|
case PAYLOAD_TITLE:
|
|
|
|
|
case PAYLOAD_BODY: {
|
|
|
|
|
char **ptr = payload_type == PAYLOAD_TITLE
|
|
|
|
|
? ¬if->title
|
|
|
|
|
: ¬if->body;
|
|
|
|
|
|
|
|
|
|
if (*ptr == NULL) {
|
|
|
|
|
*ptr = payload;
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
payload = NULL;
|
|
|
|
|
} else {
|
2024-07-23 11:29:05 +02:00
|
|
|
char *old = *ptr;
|
2024-08-03 08:12:13 +01:00
|
|
|
*ptr = xstrjoin(old, payload);
|
2024-07-23 11:29:05 +02:00
|
|
|
free(old);
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
}
|
2024-07-23 11:29:05 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-25 19:24:28 +02:00
|
|
|
case PAYLOAD_CLOSE:
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
case PAYLOAD_ALIVE:
|
2024-07-25 19:24:28 +02:00
|
|
|
/* Ignore payload */
|
|
|
|
|
break;
|
|
|
|
|
|
2024-07-23 11:29:05 +02:00
|
|
|
case PAYLOAD_ICON:
|
|
|
|
|
if (notif->icon_data == NULL) {
|
|
|
|
|
notif->icon_data = (uint8_t *)payload;
|
|
|
|
|
notif->icon_data_sz = payload_size;
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
payload = NULL;
|
|
|
|
|
} else {
|
2024-07-23 11:29:05 +02:00
|
|
|
notif->icon_data = xrealloc(
|
|
|
|
|
notif->icon_data, notif->icon_data_sz + payload_size);
|
|
|
|
|
memcpy(¬if->icon_data[notif->icon_data_sz], payload, payload_size);
|
|
|
|
|
notif->icon_data_sz += payload_size;
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
}
|
2024-07-23 11:29:05 +02:00
|
|
|
break;
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
|
|
|
|
|
case PAYLOAD_BUTTON: {
|
|
|
|
|
char *ctx = NULL;
|
|
|
|
|
for (const char *button = strtok_r(payload, "\u2028", &ctx);
|
|
|
|
|
button != NULL;
|
|
|
|
|
button = strtok_r(NULL, "\u2028", &ctx))
|
|
|
|
|
{
|
|
|
|
|
if (button[0] != '\0') {
|
|
|
|
|
tll_push_back(notif->actions, xstrdup(button));
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-08-03 11:05:58 +02:00
|
|
|
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
break;
|
|
|
|
|
}
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (done) {
|
2024-07-23 11:29:05 +02:00
|
|
|
/* Update icon cache, if necessary */
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
if (notif->icon_cache_id != NULL &&
|
2024-07-23 11:29:05 +02:00
|
|
|
(notif->icon_symbolic_name != NULL || notif->icon_data != NULL))
|
|
|
|
|
{
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
notify_icon_del(term, notif->icon_cache_id);
|
|
|
|
|
notify_icon_add(term, notif->icon_cache_id,
|
2024-07-23 11:29:05 +02:00
|
|
|
notif->icon_symbolic_name,
|
|
|
|
|
notif->icon_data, notif->icon_data_sz);
|
|
|
|
|
|
|
|
|
|
/* Don't need this anymore */
|
|
|
|
|
free(notif->icon_symbolic_name);
|
|
|
|
|
free(notif->icon_data);
|
|
|
|
|
notif->icon_symbolic_name = NULL;
|
|
|
|
|
notif->icon_data = NULL;
|
|
|
|
|
notif->icon_data_sz = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-25 19:24:28 +02:00
|
|
|
if (payload_type == PAYLOAD_CLOSE) {
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
if (notif->id != NULL)
|
|
|
|
|
notify_close(term, notif->id);
|
|
|
|
|
} else if (payload_type == PAYLOAD_ALIVE) {
|
|
|
|
|
char *alive_ids = NULL;
|
|
|
|
|
|
|
|
|
|
tll_foreach(term->active_notifications, it) {
|
|
|
|
|
/* TODO: check with kitty: use "0" for all
|
|
|
|
|
notifications with no ID? */
|
|
|
|
|
|
|
|
|
|
const char *item_id = it->item.id != NULL ? it->item.id : "0";
|
|
|
|
|
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
if (alive_ids == NULL)
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
alive_ids = xstrdup(item_id);
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
else {
|
|
|
|
|
char *old_alive_ids = alive_ids;
|
2024-08-03 08:12:13 +01:00
|
|
|
alive_ids = xstrjoin3(old_alive_ids, ",", item_id);
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
free(old_alive_ids);
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *reply = xasprintf(
|
|
|
|
|
"\033]99;i=%s:p=alive;%s\033\\",
|
|
|
|
|
notif->id != NULL ? notif->id : "0",
|
|
|
|
|
alive_ids != NULL ? alive_ids : "");
|
|
|
|
|
|
|
|
|
|
term_to_slave(term, reply, strlen(reply));
|
|
|
|
|
free(reply);
|
|
|
|
|
free(alive_ids);
|
2024-07-25 19:24:28 +02:00
|
|
|
} else {
|
|
|
|
|
/*
|
|
|
|
|
* Show notification.
|
|
|
|
|
*
|
|
|
|
|
* The checks for title|body is to handle notifications that
|
|
|
|
|
* only load icon data into the icon cache
|
|
|
|
|
*/
|
|
|
|
|
if (notif->title != NULL || notif->body != NULL) {
|
|
|
|
|
notify_notify(term, notif);
|
|
|
|
|
}
|
2024-07-23 11:53:30 +02:00
|
|
|
}
|
|
|
|
|
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
notify_free(term, notif);
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
}
|
osc: kitty notifications: implement focus|report
This patch adds support for window focusing, and sending events back
to the client application when a notification is closed.
* Refactor notification related configuration options:
- add desktop-notifications sub-section
- deprecate 'notify' in favor of 'desktop-notifications.command'
- deprecate 'notify-focus-inhibit' in favor of
'desktop-notifications.inhibit-when-focused'
* Refactor: rename 'struct kitty_notification' to 'struct
notification'
* Pass a 'struct notification' to notify_notify(), instead of many
arguments.
* notify_notify() now registers a reaper callback. When the notifier
process has terminated, the notification is considered closed, and we
either try to focus (activate) the window, or send an event to the
client application, depending on the notification setting.
* For the window activation, we need an XDG activation token. For now,
assume *everything* written on stdout is part of the token.
* Refactor: remove much of the warnings from OSC-99; we don't
typically log anything when an OSC/CSI has invalid values.
* Add icon support to OSC-99. This isn't part of the upstream
spec. Foot's implementation:
- uses the 'I' parameter
- the value is expected to be a symbolic icon name
- a quick check for absolute paths is done, and such icon requests
are ignored.
* Added ${icon} to the 'desktop-notifications.command' template. Uses
the icon specified in the notification, or ${app-id} if not set.
2024-07-23 06:59:46 +02:00
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
free(id);
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
free(app_id);
|
osc: kitty notifications: cleanup and update to latest version of spec
* Don't store a list of unfinished notifications. Use a single one. If
the notification ID of the 'current' notification doesn't match the
previous, unfinished one, the 'current' notification replaces the
previous one, instead of updating it.
* Update xstrjoin() to take an optional delimiter (for example ','),
and use that when joining categories and 'alive IDs'.
* Rename ${action-arg} to ${action-argument}
* Update handling of the 'n' parameter (symbolic icon name); the spec
allows it to be used multiple times, and the terminal is supposed to
pick the first one it can resolve. Foot can't resolve icons at all,
neither can 'notify-send' or 'fyi' (which is what foot typically
executes to display a notification); it's the notification daemon that
resolves icons.
The spec _could_ be interpreted to mean the terminal should lookup
.desktop files, and use the value of the 'Icon' key from the first
matching .desktop files. But foot doesn't read .desktop files, and I
don't intend to implement XDG directory scanning and parsing of
.desktop files just to figure out which icon to use.
Instead, use a simple heuristics; use the *shortest* symbolic
names. The idea is pretty simple: plain icon names are typically
shorter than .desktop file IDs.
2024-08-02 08:07:13 +02:00
|
|
|
free(icon_cache_id);
|
2024-07-23 11:29:05 +02:00
|
|
|
free(symbolic_icon);
|
osc: kitty notifications: implement focus|report
This patch adds support for window focusing, and sending events back
to the client application when a notification is closed.
* Refactor notification related configuration options:
- add desktop-notifications sub-section
- deprecate 'notify' in favor of 'desktop-notifications.command'
- deprecate 'notify-focus-inhibit' in favor of
'desktop-notifications.inhibit-when-focused'
* Refactor: rename 'struct kitty_notification' to 'struct
notification'
* Pass a 'struct notification' to notify_notify(), instead of many
arguments.
* notify_notify() now registers a reaper callback. When the notifier
process has terminated, the notification is considered closed, and we
either try to focus (activate) the window, or send an event to the
client application, depending on the notification setting.
* For the window activation, we need an XDG activation token. For now,
assume *everything* written on stdout is part of the token.
* Refactor: remove much of the warnings from OSC-99; we don't
typically log anything when an OSC/CSI has invalid values.
* Add icon support to OSC-99. This isn't part of the upstream
spec. Foot's implementation:
- uses the 'I' parameter
- the value is expected to be a symbolic icon name
- a quick check for absolute paths is done, and such icon requests
are ignored.
* Added ${icon} to the 'desktop-notifications.command' template. Uses
the icon specified in the notification, or ${app-id} if not set.
2024-07-23 06:59:46 +02:00
|
|
|
free(payload);
|
osc: kitty notifications: buttons, icons, app-name, categories etc
First, icons have been finalized in the specification. There were only
three things we needed to adjust:
* symbolic names are base64 encoded
* there are a couple of OSC-99 defined symbolic names that need to be
translated to the corresponding XDG icon name.
* allow in-band icons without a cache ID (that is, allow applications
to use p=icon without having to cache the icon first).
Second, add support for the following new additions to the protocol:
* 'f': custom app-name, overrides the terminal's app-id
* 't': categories
* 'p=alive': lets applications poll for currently active notifications
* 'id' is now 'unset' by default, rather than "0"
* 'w': expire time (i.e. notification timeout)
* "buttons": aka actions. This lets applications add additional (to
the terminal defined "default" action) actions. The 'activated' event
has been updated to report which button/action was used to activate
the notification.
To support button/actions, desktop-notifications.command had to be
reworked a bit.
There's now a new config option:
desktop-notifications.command-action-arg. It has two template
arguments ${action-name} and ${action-label}.
command-action-arg gets expanded for *each* action.
${action-name} and ${action-label} has been replaced by ${action-arg}
in command. This is a somewhat special template, in that it gets
replaced by *all* instances of the expanded actions.
2024-07-31 16:22:17 +02:00
|
|
|
free(category);
|
2024-08-04 14:16:56 +02:00
|
|
|
free(sound_name);
|
2019-12-19 07:28:10 +01:00
|
|
|
}
|
|
|
|
|
|
osc: wip: kitty text size protocol
This brings initial support for the new kitty text-sizing
protocol. Note hat only the width-parameter ('w') is supported. That
is, no font scaling, and no multi-line cells.
For now, only explicit widths are supported. That is, w=0 does not yet
work.
There are a couple of changes to the renderer, to handle e.g.
OSC 66 ; w=6 ; foobar ST
There are two ways this can get rendered, depending on whether
grapheme shaping has been enabled. We either shape it, and get an
array of glyphs back that we render. Or, we rasterize each codepoint
ourselves, and render each resulting glyph.
The two cases ends up in two different renderer loops, that worked
somewhat different. In particular, the first case has probably never
been tested/used at all...
With this patch, both are changed, and now uses some heuristic to
differentiate between multi-cell text strings (like in the example
above), or single-cell combining characters. The difference is mainly
in which offset to use for the secondary glyphs.
In a multi-cell string, each glyph is mapped to its own cell, while in
the combining case, we try to map all glyphs to the same cell.
2025-01-25 14:09:35 +01:00
|
|
|
static void
|
|
|
|
|
kitty_text_size(struct terminal *term, char *string)
|
|
|
|
|
{
|
|
|
|
|
char *text = strchr(string, ';');
|
|
|
|
|
if (text == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
char *parameters = string;
|
|
|
|
|
*text = '\0';
|
|
|
|
|
text++;
|
|
|
|
|
|
|
|
|
|
char32_t *wchars = ambstoc32(text);
|
|
|
|
|
if (wchars == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
2025-01-26 07:36:11 +01:00
|
|
|
int forced_width = 0;
|
osc: wip: kitty text size protocol
This brings initial support for the new kitty text-sizing
protocol. Note hat only the width-parameter ('w') is supported. That
is, no font scaling, and no multi-line cells.
For now, only explicit widths are supported. That is, w=0 does not yet
work.
There are a couple of changes to the renderer, to handle e.g.
OSC 66 ; w=6 ; foobar ST
There are two ways this can get rendered, depending on whether
grapheme shaping has been enabled. We either shape it, and get an
array of glyphs back that we render. Or, we rasterize each codepoint
ourselves, and render each resulting glyph.
The two cases ends up in two different renderer loops, that worked
somewhat different. In particular, the first case has probably never
been tested/used at all...
With this patch, both are changed, and now uses some heuristic to
differentiate between multi-cell text strings (like in the example
above), or single-cell combining characters. The difference is mainly
in which offset to use for the secondary glyphs.
In a multi-cell string, each glyph is mapped to its own cell, while in
the combining case, we try to map all glyphs to the same cell.
2025-01-25 14:09:35 +01:00
|
|
|
|
|
|
|
|
char *ctx = NULL;
|
|
|
|
|
for (char *param = strtok_r(parameters, ":", &ctx);
|
|
|
|
|
param != NULL;
|
|
|
|
|
param = strtok_r(NULL, ":", &ctx))
|
|
|
|
|
{
|
|
|
|
|
/* All parameters are on the form X=value, where X is always
|
|
|
|
|
exactly one character */
|
|
|
|
|
if (param[0] == '\0' || param[1] != '=')
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
char *value = ¶m[2];
|
|
|
|
|
|
|
|
|
|
switch (param[0]) {
|
|
|
|
|
case 'w': {
|
|
|
|
|
errno = 0;
|
|
|
|
|
char *end = NULL;
|
|
|
|
|
unsigned long w = strtoul(value, &end, 10);
|
|
|
|
|
|
|
|
|
|
if (*end == '\0' && errno == 0 && w <= 7) {
|
2025-01-26 07:36:11 +01:00
|
|
|
forced_width = (int)w;
|
osc: wip: kitty text size protocol
This brings initial support for the new kitty text-sizing
protocol. Note hat only the width-parameter ('w') is supported. That
is, no font scaling, and no multi-line cells.
For now, only explicit widths are supported. That is, w=0 does not yet
work.
There are a couple of changes to the renderer, to handle e.g.
OSC 66 ; w=6 ; foobar ST
There are two ways this can get rendered, depending on whether
grapheme shaping has been enabled. We either shape it, and get an
array of glyphs back that we render. Or, we rasterize each codepoint
ourselves, and render each resulting glyph.
The two cases ends up in two different renderer loops, that worked
somewhat different. In particular, the first case has probably never
been tested/used at all...
With this patch, both are changed, and now uses some heuristic to
differentiate between multi-cell text strings (like in the example
above), or single-cell combining characters. The difference is mainly
in which offset to use for the secondary glyphs.
In a multi-cell string, each glyph is mapped to its own cell, while in
the combining case, we try to map all glyphs to the same cell.
2025-01-25 14:09:35 +01:00
|
|
|
break;
|
|
|
|
|
} else
|
|
|
|
|
LOG_ERR("OSC-66: invalid 'w' value, ignoring");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 's':
|
|
|
|
|
case 'n':
|
|
|
|
|
case 'd':
|
|
|
|
|
case 'v':
|
|
|
|
|
LOG_WARN("OSC-66: unsupported: '%c' parameter, ignoring", param[0]);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const size_t len = c32len(wchars);
|
2025-01-26 07:36:11 +01:00
|
|
|
|
|
|
|
|
if (forced_width == 0) {
|
|
|
|
|
/*
|
|
|
|
|
* w=0 means we split the text up as we'd normally do... Since
|
|
|
|
|
* we don't support any other parameters of the text-sizing
|
|
|
|
|
* protocol, that means we just process the string as if it
|
|
|
|
|
* has been printed without this OSC.
|
|
|
|
|
*/
|
|
|
|
|
for (size_t i = 0; i < len; i++)
|
|
|
|
|
term_process_and_print_non_ascii(term, wchars[i]);
|
|
|
|
|
free(wchars);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t max_cp_width = 0;
|
|
|
|
|
size_t all_cp_width = 0;
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < len; i++) {
|
|
|
|
|
const size_t cp_width = c32width(wchars[i]);
|
|
|
|
|
all_cp_width += cp_width;
|
|
|
|
|
max_cp_width = max(max_cp_width, cp_width);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t calculated_width = 0;
|
|
|
|
|
switch (term->conf->tweak.grapheme_width_method) {
|
|
|
|
|
case GRAPHEME_WIDTH_WCSWIDTH: calculated_width = all_cp_width; break;
|
|
|
|
|
case GRAPHEME_WIDTH_MAX: calculated_width = max_cp_width; break;
|
|
|
|
|
case GRAPHEME_WIDTH_DOUBLE: calculated_width = min(max_cp_width, 2); break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const size_t width = forced_width == 0 ? calculated_width : forced_width;
|
|
|
|
|
|
|
|
|
|
LOG_DBG("len=%zu, forced=%d, calculated=%zu, using=%zu",
|
|
|
|
|
len, forced_width, calculated_width, width);
|
|
|
|
|
|
2025-02-03 15:30:00 +01:00
|
|
|
#if 0
|
2025-01-26 07:36:11 +01:00
|
|
|
if (len == 1 && calculated_width == forced_width) {
|
|
|
|
|
/*
|
|
|
|
|
* Optimization: if there's a single codepoint, and either
|
|
|
|
|
* w=0, or the 'w' matches the calculated width, print
|
|
|
|
|
* codepoint directly instead of creating a combining
|
|
|
|
|
* character.
|
|
|
|
|
*/
|
|
|
|
|
term_print(term, wchars[0], width);
|
|
|
|
|
free(wchars);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-02-03 15:30:00 +01:00
|
|
|
#endif
|
2025-01-26 07:36:11 +01:00
|
|
|
|
osc: wip: kitty text size protocol
This brings initial support for the new kitty text-sizing
protocol. Note hat only the width-parameter ('w') is supported. That
is, no font scaling, and no multi-line cells.
For now, only explicit widths are supported. That is, w=0 does not yet
work.
There are a couple of changes to the renderer, to handle e.g.
OSC 66 ; w=6 ; foobar ST
There are two ways this can get rendered, depending on whether
grapheme shaping has been enabled. We either shape it, and get an
array of glyphs back that we render. Or, we rasterize each codepoint
ourselves, and render each resulting glyph.
The two cases ends up in two different renderer loops, that worked
somewhat different. In particular, the first case has probably never
been tested/used at all...
With this patch, both are changed, and now uses some heuristic to
differentiate between multi-cell text strings (like in the example
above), or single-cell combining characters. The difference is mainly
in which offset to use for the secondary glyphs.
In a multi-cell string, each glyph is mapped to its own cell, while in
the combining case, we try to map all glyphs to the same cell.
2025-01-25 14:09:35 +01:00
|
|
|
uint32_t key = composed_key_from_chars(wchars, len);
|
|
|
|
|
|
|
|
|
|
const struct composed *composed = composed_lookup_without_collision(
|
2025-01-26 07:36:11 +01:00
|
|
|
term->composed, &key, wchars, len - 1, wchars[len - 1], forced_width);
|
osc: wip: kitty text size protocol
This brings initial support for the new kitty text-sizing
protocol. Note hat only the width-parameter ('w') is supported. That
is, no font scaling, and no multi-line cells.
For now, only explicit widths are supported. That is, w=0 does not yet
work.
There are a couple of changes to the renderer, to handle e.g.
OSC 66 ; w=6 ; foobar ST
There are two ways this can get rendered, depending on whether
grapheme shaping has been enabled. We either shape it, and get an
array of glyphs back that we render. Or, we rasterize each codepoint
ourselves, and render each resulting glyph.
The two cases ends up in two different renderer loops, that worked
somewhat different. In particular, the first case has probably never
been tested/used at all...
With this patch, both are changed, and now uses some heuristic to
differentiate between multi-cell text strings (like in the example
above), or single-cell combining characters. The difference is mainly
in which offset to use for the secondary glyphs.
In a multi-cell string, each glyph is mapped to its own cell, while in
the combining case, we try to map all glyphs to the same cell.
2025-01-25 14:09:35 +01:00
|
|
|
|
|
|
|
|
if (composed == NULL) {
|
|
|
|
|
struct composed *new_cc = xmalloc(sizeof(*new_cc));
|
|
|
|
|
new_cc->chars = wchars;
|
|
|
|
|
new_cc->count = len;
|
|
|
|
|
new_cc->key = key;
|
|
|
|
|
new_cc->width = width;
|
2025-01-26 07:36:11 +01:00
|
|
|
new_cc->forced_width = forced_width;
|
osc: wip: kitty text size protocol
This brings initial support for the new kitty text-sizing
protocol. Note hat only the width-parameter ('w') is supported. That
is, no font scaling, and no multi-line cells.
For now, only explicit widths are supported. That is, w=0 does not yet
work.
There are a couple of changes to the renderer, to handle e.g.
OSC 66 ; w=6 ; foobar ST
There are two ways this can get rendered, depending on whether
grapheme shaping has been enabled. We either shape it, and get an
array of glyphs back that we render. Or, we rasterize each codepoint
ourselves, and render each resulting glyph.
The two cases ends up in two different renderer loops, that worked
somewhat different. In particular, the first case has probably never
been tested/used at all...
With this patch, both are changed, and now uses some heuristic to
differentiate between multi-cell text strings (like in the example
above), or single-cell combining characters. The difference is mainly
in which offset to use for the secondary glyphs.
In a multi-cell string, each glyph is mapped to its own cell, while in
the combining case, we try to map all glyphs to the same cell.
2025-01-25 14:09:35 +01:00
|
|
|
|
|
|
|
|
term->composed_count++;
|
|
|
|
|
composed_insert(&term->composed, new_cc);
|
|
|
|
|
composed = new_cc;
|
|
|
|
|
} else if (composed->width == width) {
|
|
|
|
|
free(wchars);
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-26 07:33:53 +01:00
|
|
|
term_print(
|
|
|
|
|
term, CELL_COMB_CHARS_LO + composed->key,
|
|
|
|
|
composed->forced_width > 0 ? composed->forced_width : composed->width,
|
|
|
|
|
false);
|
osc: wip: kitty text size protocol
This brings initial support for the new kitty text-sizing
protocol. Note hat only the width-parameter ('w') is supported. That
is, no font scaling, and no multi-line cells.
For now, only explicit widths are supported. That is, w=0 does not yet
work.
There are a couple of changes to the renderer, to handle e.g.
OSC 66 ; w=6 ; foobar ST
There are two ways this can get rendered, depending on whether
grapheme shaping has been enabled. We either shape it, and get an
array of glyphs back that we render. Or, we rasterize each codepoint
ourselves, and render each resulting glyph.
The two cases ends up in two different renderer loops, that worked
somewhat different. In particular, the first case has probably never
been tested/used at all...
With this patch, both are changed, and now uses some heuristic to
differentiate between multi-cell text strings (like in the example
above), or single-cell combining characters. The difference is mainly
in which offset to use for the secondary glyphs.
In a multi-cell string, each glyph is mapped to its own cell, while in
the combining case, we try to map all glyphs to the same cell.
2025-01-25 14:09:35 +01:00
|
|
|
}
|
|
|
|
|
|
2019-07-07 16:32:18 +02:00
|
|
|
void
|
2019-06-15 22:22:44 +02:00
|
|
|
osc_dispatch(struct terminal *term)
|
|
|
|
|
{
|
2019-07-18 19:26:24 +02:00
|
|
|
unsigned param = 0;
|
2019-07-05 09:46:48 +02:00
|
|
|
int data_ofs = 0;
|
|
|
|
|
|
2019-08-21 17:57:02 +02:00
|
|
|
for (size_t i = 0; i < term->vt.osc.idx; i++, data_ofs++) {
|
2019-07-18 19:26:24 +02:00
|
|
|
char c = term->vt.osc.data[i];
|
2019-07-05 09:46:48 +02:00
|
|
|
|
|
|
|
|
if (c == ';') {
|
2019-08-21 17:57:02 +02:00
|
|
|
data_ofs++;
|
2019-07-05 09:46:48 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isdigit(c)) {
|
2019-07-30 21:42:46 +02:00
|
|
|
UNHANDLED();
|
|
|
|
|
return;
|
2019-07-05 09:46:48 +02:00
|
|
|
}
|
|
|
|
|
|
2019-07-05 19:04:09 +02:00
|
|
|
param *= 10;
|
2019-07-05 09:46:48 +02:00
|
|
|
param += c - '0';
|
|
|
|
|
}
|
2019-07-30 21:42:46 +02:00
|
|
|
|
2021-07-24 01:58:34 +01:00
|
|
|
LOG_DBG("OSC: %.*s (param = %d)",
|
2019-07-05 09:46:48 +02:00
|
|
|
(int)term->vt.osc.idx, term->vt.osc.data, param);
|
|
|
|
|
|
2019-07-19 14:20:38 +02:00
|
|
|
char *string = (char *)&term->vt.osc.data[data_ofs];
|
2019-07-05 09:46:48 +02:00
|
|
|
|
|
|
|
|
switch (param) {
|
2024-09-10 18:53:38 +02:00
|
|
|
case 0: /* icon + title */
|
|
|
|
|
term_set_window_title(term, string);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 1: /* icon */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 2: /* title */
|
|
|
|
|
term_set_window_title(term, string);
|
|
|
|
|
break;
|
2019-07-23 17:55:25 +02:00
|
|
|
|
2019-08-21 17:57:02 +02:00
|
|
|
case 4: {
|
|
|
|
|
/* Set color<idx> */
|
|
|
|
|
|
2020-04-04 14:27:44 +02:00
|
|
|
string--;
|
2020-06-09 17:33:26 +02:00
|
|
|
if (*string != ';')
|
|
|
|
|
break;
|
|
|
|
|
|
2021-01-16 20:16:00 +00:00
|
|
|
xassert(*string == ';');
|
2019-08-21 17:57:02 +02:00
|
|
|
|
2020-04-04 14:27:44 +02:00
|
|
|
for (const char *s_idx = strtok(string, ";"), *s_color = strtok(NULL, ";");
|
|
|
|
|
s_idx != NULL && s_color != NULL;
|
|
|
|
|
s_idx = strtok(NULL, ";"), s_color = strtok(NULL, ";"))
|
|
|
|
|
{
|
|
|
|
|
/* Parse <idx> parameter */
|
|
|
|
|
unsigned idx = 0;
|
|
|
|
|
for (; *s_idx != '\0'; s_idx++) {
|
|
|
|
|
char c = *s_idx;
|
|
|
|
|
idx *= 10;
|
|
|
|
|
idx += c - '0';
|
|
|
|
|
}
|
2019-08-21 17:57:02 +02:00
|
|
|
|
2021-04-05 21:06:48 +02:00
|
|
|
if (idx >= ALEN(term->colors.table)) {
|
|
|
|
|
LOG_WARN("invalid OSC 4 color index: %u", idx);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-04 14:27:44 +02:00
|
|
|
/* Client queried for current value */
|
2022-04-06 01:24:34 +01:00
|
|
|
if (s_color[0] == '?' && s_color[1] == '\0') {
|
2020-04-04 14:27:44 +02:00
|
|
|
uint32_t color = term->colors.table[idx];
|
|
|
|
|
uint8_t r = (color >> 16) & 0xff;
|
|
|
|
|
uint8_t g = (color >> 8) & 0xff;
|
|
|
|
|
uint8_t b = (color >> 0) & 0xff;
|
2021-10-20 12:48:37 +01:00
|
|
|
const char *terminator = term->vt.osc.bel ? "\a" : "\033\\";
|
2020-04-04 14:27:44 +02:00
|
|
|
|
|
|
|
|
char reply[32];
|
2021-10-20 12:48:37 +01:00
|
|
|
size_t n = xsnprintf(
|
2022-03-13 09:05:55 +01:00
|
|
|
reply, sizeof(reply),
|
|
|
|
|
"\033]4;%u;rgb:%02hhx%02hhx/%02hhx%02hhx/%02hhx%02hhx%s",
|
|
|
|
|
idx, r, r, g, g, b, b, terminator);
|
2021-01-14 21:30:06 +00:00
|
|
|
term_to_slave(term, reply, n);
|
2020-04-04 14:27:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
uint32_t color;
|
2021-04-05 22:05:50 +02:00
|
|
|
bool color_is_valid = s_color[0] == '#' || s_color[0] == '['
|
|
|
|
|
? parse_legacy_color(s_color, &color, NULL, NULL)
|
|
|
|
|
: parse_rgb(s_color, &color, NULL, NULL);
|
2020-04-04 14:27:44 +02:00
|
|
|
|
|
|
|
|
if (!color_is_valid)
|
|
|
|
|
continue;
|
|
|
|
|
|
2020-06-11 17:13:32 +02:00
|
|
|
LOG_DBG("change color definition for #%u from %06x to %06x",
|
|
|
|
|
idx, term->colors.table[idx], color);
|
|
|
|
|
|
2020-04-04 14:27:44 +02:00
|
|
|
term->colors.table[idx] = color;
|
2024-07-03 10:53:33 +02:00
|
|
|
term_damage_color(term, COLOR_BASE256, idx);
|
2020-04-04 14:27:44 +02:00
|
|
|
}
|
2019-08-21 17:57:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-21 19:42:59 +01:00
|
|
|
case 7:
|
|
|
|
|
/* Update terminal's understanding of PWD */
|
|
|
|
|
osc_set_pwd(term, string);
|
|
|
|
|
break;
|
|
|
|
|
|
2021-02-13 12:41:57 +01:00
|
|
|
case 8:
|
|
|
|
|
osc_uri(term, string);
|
|
|
|
|
break;
|
|
|
|
|
|
2024-11-02 08:10:25 +01:00
|
|
|
case 9: {
|
2021-06-02 17:50:29 +02:00
|
|
|
/* iTerm2 Growl notifications */
|
2024-11-02 08:10:25 +01:00
|
|
|
const char *sep = strchr(string, ';');
|
|
|
|
|
if (sep != NULL) {
|
|
|
|
|
errno = 0;
|
|
|
|
|
char *end = NULL;
|
|
|
|
|
strtoul(string, &end, 10);
|
|
|
|
|
if (end == sep && errno == 0) {
|
|
|
|
|
/* Ignore ConEmu/Windows Terminal escape */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-02 17:50:29 +02:00
|
|
|
osc_notify(term, string);
|
|
|
|
|
break;
|
2024-11-02 08:10:25 +01:00
|
|
|
}
|
2021-06-02 17:50:29 +02:00
|
|
|
|
2024-07-01 17:24:50 +02:00
|
|
|
case 10: /* fg */
|
|
|
|
|
case 11: /* bg */
|
|
|
|
|
case 12: /* cursor */
|
|
|
|
|
case 17: /* highlight (selection) fg */
|
|
|
|
|
case 19: { /* highlight (selection) bg */
|
2021-04-07 08:09:40 +02:00
|
|
|
/* Set default foreground/background/highlight-bg/highlight-fg color */
|
2019-08-21 17:57:02 +02:00
|
|
|
|
|
|
|
|
/* Client queried for current value */
|
2022-04-06 01:24:34 +01:00
|
|
|
if (string[0] == '?' && string[1] == '\0') {
|
2024-07-01 17:24:50 +02:00
|
|
|
uint32_t color = param == 10
|
|
|
|
|
? term->colors.fg
|
|
|
|
|
: param == 11
|
|
|
|
|
? term->colors.bg
|
|
|
|
|
: param == 12
|
|
|
|
|
? term->colors.cursor_bg
|
|
|
|
|
: param == 17
|
|
|
|
|
? term->colors.selection_bg
|
|
|
|
|
: term->colors.selection_fg;
|
|
|
|
|
|
2019-08-21 17:57:02 +02:00
|
|
|
uint8_t r = (color >> 16) & 0xff;
|
|
|
|
|
uint8_t g = (color >> 8) & 0xff;
|
|
|
|
|
uint8_t b = (color >> 0) & 0xff;
|
2021-10-20 12:48:37 +01:00
|
|
|
const char *terminator = term->vt.osc.bel ? "\a" : "\033\\";
|
2019-08-21 17:57:02 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Reply in XParseColor format
|
2020-08-02 23:54:04 +01:00
|
|
|
* E.g. for color 0xdcdccc we reply "\033]10;rgb:dc/dc/cc\033\\"
|
2019-08-21 17:57:02 +02:00
|
|
|
*/
|
|
|
|
|
char reply[32];
|
2021-01-14 21:30:06 +00:00
|
|
|
size_t n = xsnprintf(
|
2022-03-13 09:05:55 +01:00
|
|
|
reply, sizeof(reply),
|
|
|
|
|
"\033]%u;rgb:%02hhx%02hhx/%02hhx%02hhx/%02hhx%02hhx%s",
|
|
|
|
|
param, r, r, g, g, b, b, terminator);
|
2019-08-21 17:57:02 +02:00
|
|
|
|
2021-01-14 21:30:06 +00:00
|
|
|
term_to_slave(term, reply, n);
|
2019-08-21 17:57:02 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t color;
|
2021-04-05 22:05:50 +02:00
|
|
|
bool have_alpha = false;
|
|
|
|
|
uint16_t alpha = 0xffff;
|
|
|
|
|
|
|
|
|
|
if (string[0] == '#' || string[0] == '['
|
|
|
|
|
? !parse_legacy_color(string, &color, &have_alpha, &alpha)
|
|
|
|
|
: !parse_rgb(string, &color, &have_alpha, &alpha))
|
|
|
|
|
{
|
2019-08-21 17:57:02 +02:00
|
|
|
break;
|
2021-04-05 22:05:50 +02:00
|
|
|
}
|
2019-08-21 17:57:02 +02:00
|
|
|
|
2019-08-21 18:54:12 +02:00
|
|
|
LOG_DBG("change color definition for %s to %06x",
|
2021-04-07 08:09:40 +02:00
|
|
|
param == 10 ? "foreground" :
|
|
|
|
|
param == 11 ? "background" :
|
2024-07-01 17:24:50 +02:00
|
|
|
param == 12 ? "cursor" :
|
2021-04-07 08:09:40 +02:00
|
|
|
param == 17 ? "selection background" :
|
|
|
|
|
"selection foreground",
|
|
|
|
|
color);
|
2019-08-21 18:54:12 +02:00
|
|
|
|
2019-08-21 17:57:02 +02:00
|
|
|
switch (param) {
|
2021-04-05 22:05:50 +02:00
|
|
|
case 10:
|
|
|
|
|
term->colors.fg = color;
|
2024-07-03 10:53:33 +02:00
|
|
|
term_damage_color(term, COLOR_DEFAULT, 0);
|
2021-04-05 22:05:50 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 11:
|
|
|
|
|
term->colors.bg = color;
|
2025-10-16 13:43:33 +02:00
|
|
|
if (!have_alpha)
|
|
|
|
|
alpha = term_theme_get(term)->alpha;
|
2025-05-02 08:53:43 +02:00
|
|
|
|
|
|
|
|
const bool changed = term->colors.alpha != alpha;
|
|
|
|
|
term->colors.alpha = alpha;
|
|
|
|
|
|
|
|
|
|
if (changed) {
|
|
|
|
|
wayl_win_alpha_changed(term->window);
|
|
|
|
|
term_font_subpixel_changed(term);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-03 10:53:33 +02:00
|
|
|
term_damage_color(term, COLOR_DEFAULT, 0);
|
2024-07-01 19:29:28 +02:00
|
|
|
term_damage_margins(term);
|
2021-04-05 22:05:50 +02:00
|
|
|
break;
|
2021-04-07 08:09:40 +02:00
|
|
|
|
2024-07-01 17:24:50 +02:00
|
|
|
case 12:
|
|
|
|
|
term->colors.cursor_bg = 1u << 31 | color;
|
|
|
|
|
term_damage_cursor(term);
|
|
|
|
|
break;
|
|
|
|
|
|
2021-04-07 08:09:40 +02:00
|
|
|
case 17:
|
|
|
|
|
term->colors.selection_bg = color;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 19:
|
|
|
|
|
term->colors.selection_fg = color;
|
|
|
|
|
break;
|
2019-08-21 17:57:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-01 13:58:42 +01:00
|
|
|
case 22: /* Set mouse cursor */
|
|
|
|
|
term_set_user_mouse_cursor(term, string);
|
|
|
|
|
break;
|
|
|
|
|
|
2019-07-23 17:55:25 +02:00
|
|
|
case 30: /* Set tab title */
|
|
|
|
|
break;
|
2019-07-30 21:42:46 +02:00
|
|
|
|
2019-07-23 17:55:25 +02:00
|
|
|
case 52: /* Copy to/from clipboard/primary */
|
|
|
|
|
osc_selection(term, string);
|
|
|
|
|
break;
|
osc: implement OSC-5522, kitty's extended version of OSC-52
OSC-5522[^1] gives the terminal application access to all offered
mime-types when reading the clipboard, and allows setting multiple
mime-types when writing to the clipboard (with different contents, if
it so wishes).
In addition to the base protocol, we also implement the _event
extension_[^2], where a paste action (e.g. ctrl+shift+v) results in an
unsolicited mime-type listing being sent to the terminal
application (as if it had issued a mime-type query itself), instead of
the clipboard content being pasted directly. The application follows
up with an explicit read request (or chooses to ignore the event).
The protocol supports "passwords", as a way of bypassing terminal
popups asking the user for approval, after the first popup has been
approved by the user. Foot doesn't implement this kind of user
approval, but all read and write requests are denied with EPERM if the
user has disabled OSC copy/pasting with the security.osc52
configuration option. In addition, if disabled, event reporting cannot
be enabled at all (i.e. 'CSI ? 5522 h' is ignored).
[^1]: https://sw.kovidgoyal.net/kitty/clipboard/
[^2]: https://rockorager.dev/misc/bracketed-paste-mime/
2026-05-15 18:35:51 +02:00
|
|
|
case 5522:
|
|
|
|
|
kitty_clipboard(term, string);
|
|
|
|
|
break;
|
2019-07-05 09:46:48 +02:00
|
|
|
|
osc: wip: kitty text size protocol
This brings initial support for the new kitty text-sizing
protocol. Note hat only the width-parameter ('w') is supported. That
is, no font scaling, and no multi-line cells.
For now, only explicit widths are supported. That is, w=0 does not yet
work.
There are a couple of changes to the renderer, to handle e.g.
OSC 66 ; w=6 ; foobar ST
There are two ways this can get rendered, depending on whether
grapheme shaping has been enabled. We either shape it, and get an
array of glyphs back that we render. Or, we rasterize each codepoint
ourselves, and render each resulting glyph.
The two cases ends up in two different renderer loops, that worked
somewhat different. In particular, the first case has probably never
been tested/used at all...
With this patch, both are changed, and now uses some heuristic to
differentiate between multi-cell text strings (like in the example
above), or single-cell combining characters. The difference is mainly
in which offset to use for the secondary glyphs.
In a multi-cell string, each glyph is mapped to its own cell, while in
the combining case, we try to map all glyphs to the same cell.
2025-01-25 14:09:35 +01:00
|
|
|
case 66: /* text-size protocol (kitty) */
|
|
|
|
|
kitty_text_size(term, string);
|
|
|
|
|
break;
|
|
|
|
|
|
osc/notify: add support for OSC-99, kitty desktop notifications
This adds limited support for OSC-99, kitty desktop notifications[^1]. We
support everything defined by the "protocol", except:
* 'a': action to perform on notification activation. Since we don't
trigger the notification ourselves (over D-Bus), we don't know a)
which ID the notification got, or b) when it is clicked.
* ... and that's it. Everything else is supported
To be explicit, we *do* support:
* Chunked notifications (d=0|1), allowing the application to append
data to a notification in chunks, before it's finally displayed.
* Plain UTF-8, or base64-encoded UTF-8 payload (e=0|1).
* Notification identifier (i=xyz).
* Payload type (p=title|body).
* When to honor the notification (o=always|unfocused|invisible), with
the following quirks:
- we don't know when the window is invisible, thus it's treated as
'unfocused'.
- the foot option 'notify-focus-inhibit' overrides 'always'
* Urgency (u=0|1|2)
[^1]: https://sw.kovidgoyal.net/kitty/desktop-notifications/
2024-07-19 15:04:28 +02:00
|
|
|
case 99: /* Kitty notifications */
|
|
|
|
|
kitty_notification(term, string);
|
|
|
|
|
break;
|
|
|
|
|
|
2019-08-21 17:57:02 +02:00
|
|
|
case 104: {
|
|
|
|
|
/* Reset Color Number 'c' (whole table if no parameter) */
|
|
|
|
|
|
2025-10-16 13:43:33 +02:00
|
|
|
const struct color_theme *theme = term_theme_get(term);
|
2025-07-22 13:30:28 +02:00
|
|
|
|
2022-04-06 01:24:34 +01:00
|
|
|
if (string[0] == '\0') {
|
2019-08-21 18:54:12 +02:00
|
|
|
LOG_DBG("resetting all colors");
|
2025-07-22 13:30:28 +02:00
|
|
|
memcpy(term->colors.table, theme->table, sizeof(term->colors.table));
|
2024-07-03 10:53:33 +02:00
|
|
|
term_damage_view(term);
|
2020-04-04 14:30:50 +02:00
|
|
|
}
|
2019-08-21 17:57:02 +02:00
|
|
|
|
2020-04-04 14:30:50 +02:00
|
|
|
else {
|
|
|
|
|
for (const char *s_idx = strtok(string, ";");
|
|
|
|
|
s_idx != NULL;
|
|
|
|
|
s_idx = strtok(NULL, ";"))
|
|
|
|
|
{
|
|
|
|
|
unsigned idx = 0;
|
|
|
|
|
for (; *s_idx != '\0'; s_idx++) {
|
|
|
|
|
char c = *s_idx;
|
|
|
|
|
idx *= 10;
|
|
|
|
|
idx += c - '0';
|
2019-08-21 17:57:02 +02:00
|
|
|
}
|
|
|
|
|
|
2021-04-05 21:06:48 +02:00
|
|
|
if (idx >= ALEN(term->colors.table)) {
|
|
|
|
|
LOG_WARN("invalid OSC 104 color index: %u", idx);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-04 14:30:50 +02:00
|
|
|
LOG_DBG("resetting color #%u", idx);
|
2025-07-22 13:30:28 +02:00
|
|
|
term->colors.table[idx] = theme->table[idx];
|
2024-07-03 10:53:33 +02:00
|
|
|
term_damage_color(term, COLOR_BASE256, idx);
|
2019-08-21 17:57:02 +02:00
|
|
|
}
|
2021-12-25 17:13:50 +01:00
|
|
|
|
2019-08-21 17:57:02 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-05 19:04:34 +02:00
|
|
|
case 105: /* Reset Special Color Number 'c' */
|
2019-08-21 17:57:02 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 110: /* Reset default text foreground color */
|
2021-04-07 08:07:43 +02:00
|
|
|
LOG_DBG("resetting foreground color");
|
2025-07-22 13:30:28 +02:00
|
|
|
|
2025-10-16 13:43:33 +02:00
|
|
|
const struct color_theme *theme = term_theme_get(term);
|
2025-07-22 13:30:28 +02:00
|
|
|
term->colors.fg = theme->fg;
|
2024-07-03 10:53:33 +02:00
|
|
|
term_damage_color(term, COLOR_DEFAULT, 0);
|
2019-08-21 17:57:02 +02:00
|
|
|
break;
|
|
|
|
|
|
2024-08-14 08:50:44 +02:00
|
|
|
case 111: { /* Reset default text background color */
|
2021-04-07 08:07:43 +02:00
|
|
|
LOG_DBG("resetting background color");
|
2024-08-14 08:50:44 +02:00
|
|
|
|
2025-10-16 13:43:33 +02:00
|
|
|
const struct color_theme *theme = term_theme_get(term);
|
2025-07-22 13:30:28 +02:00
|
|
|
bool alpha_changed = term->colors.alpha != theme->alpha;
|
|
|
|
|
|
|
|
|
|
term->colors.bg = theme->bg;
|
|
|
|
|
term->colors.alpha = theme->alpha;
|
2024-08-14 08:50:44 +02:00
|
|
|
|
|
|
|
|
if (alpha_changed) {
|
|
|
|
|
wayl_win_alpha_changed(term->window);
|
|
|
|
|
term_font_subpixel_changed(term);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-03 10:53:33 +02:00
|
|
|
term_damage_color(term, COLOR_DEFAULT, 0);
|
2020-09-29 10:05:52 +02:00
|
|
|
term_damage_margins(term);
|
2019-07-05 19:04:34 +02:00
|
|
|
break;
|
2024-08-14 08:50:44 +02:00
|
|
|
}
|
2019-07-05 19:04:34 +02:00
|
|
|
|
2025-07-22 13:30:28 +02:00
|
|
|
case 112: {
|
2020-01-20 18:36:19 +01:00
|
|
|
LOG_DBG("resetting cursor color");
|
2025-07-22 13:30:28 +02:00
|
|
|
|
2025-10-16 13:43:33 +02:00
|
|
|
const struct color_theme *theme = term_theme_get(term);
|
2025-07-22 13:30:28 +02:00
|
|
|
term->colors.cursor_fg = theme->cursor.text;
|
|
|
|
|
term->colors.cursor_bg = theme->cursor.cursor;
|
2025-04-20 07:16:18 +02:00
|
|
|
|
config: add [colors-dark] and [colors-light], replacing [colors] and [colors2]
The main reason for having two color sections is to be able to switch
between dark and light. Thus, it's better if the section names reflect
this, rather than the more generic 'colors' and 'colors2' (which was
the dark one and which was the light one, now again?)
When the second color section was added, we kept the original name,
colors, to make sure we didn't break existing configurations, and
third-party themes.
However, in the long run, it's probably better to be specific in the
section naming, to avoid confusion.
So, add 'colors-dark', and 'colors-light'. Keep 'colors' and 'colors2'
as aliases for now, but mark them as deprecated. They WILL be removed
in a future release.
Also rename the option values for initial-color-theme, from 1/2, to
dark/light. Keep the old ones for now, marked as deprecated.
Update all bundled themes to use the new names. In the light-only
themes (i.e. themes that define a single, light, theme), use
colors-light, and set initial-color-theme=light.
Possible improvements: disable color switching if only one color
section has been explicitly configured (todo: figure out how to handle
the default color theme values...)
2025-12-19 09:29:06 +01:00
|
|
|
if (term->conf->colors_dark.use_custom.cursor) {
|
2025-04-20 07:16:18 +02:00
|
|
|
term->colors.cursor_fg |= 1u << 31;
|
|
|
|
|
term->colors.cursor_bg |= 1u << 31;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-29 10:05:52 +02:00
|
|
|
term_damage_cursor(term);
|
2020-01-20 18:36:19 +01:00
|
|
|
break;
|
2025-07-22 13:30:28 +02:00
|
|
|
}
|
2020-01-20 18:36:19 +01:00
|
|
|
|
2025-07-22 13:30:28 +02:00
|
|
|
case 117: {
|
2021-04-07 08:09:40 +02:00
|
|
|
LOG_DBG("resetting selection background color");
|
|
|
|
|
|
2025-10-16 13:43:33 +02:00
|
|
|
const struct color_theme *theme = term_theme_get(term);
|
2025-07-22 13:30:28 +02:00
|
|
|
term->colors.selection_bg = theme->selection_bg;
|
2021-04-07 08:09:40 +02:00
|
|
|
break;
|
2025-07-22 13:30:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 119: {
|
|
|
|
|
LOG_DBG("resetting selection foreground color");
|
|
|
|
|
|
2025-10-16 13:43:33 +02:00
|
|
|
const struct color_theme *theme = term_theme_get(term);
|
2025-07-22 13:30:28 +02:00
|
|
|
term->colors.selection_fg = theme->selection_fg;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-04-07 08:09:40 +02:00
|
|
|
|
osc: add support for OSC 133;A (prompt markers)
This patch adds support for the OSC-133;A sequence, introduced by
FinalTerm and implemented by iTerm2, Kitty and more. See
https://iterm2.com/documentation-one-page.html#documentation-escape-codes.html.
The shell emits the OSC just before printing the prompt. This lets the
terminal know where, in the scrollback, there are prompts.
We implement this using a simple boolean in the row struct ("this row
has a prompt"). The prompt marker must be reflowed along with the text
on window resizes.
In an ideal world, erasing, or overwriting the cell where the OSC was
emitted, would remove the prompt mark. Since we don't store this
information in the cell struct, we can't do that. The best we can do
is reset it in erase_line(). This works well enough in the "normal"
screen, when used with a "normal" shell. It doesn't really work in
fullscreen apps, on the alt screen. But that doesn't matter since we
don't support jumping between prompts on the alt screen anyway.
To be able to jump between prompts, two new key bindings have been
added: prompt-prev and prompt-next, bound to ctrl+shift+z and
ctrl+shift+x respectively.
prompt-prev will jump to the previous, not currently visible, prompt,
by moving the viewport, ensuring the prompt is at the top of the
screen.
prompt-next jumps to the next prompt, visible or not. Again, by moving
the viewport to ensure the prompt is at the top of the screen. If
we're at the bottom of the scrollback, the viewport is instead moved
as far down as possible.
Closes #30
2022-06-15 18:44:23 +02:00
|
|
|
case 133:
|
|
|
|
|
/*
|
|
|
|
|
* Shell integration; see
|
|
|
|
|
* https://iterm2.com/documentation-escape-codes.html (Shell
|
|
|
|
|
* Integration/FinalTerm)
|
|
|
|
|
*
|
|
|
|
|
* [PROMPT]prompt% [COMMAND_START] ls -l
|
|
|
|
|
* [COMMAND_EXECUTED]
|
|
|
|
|
* -rw-r--r-- 1 user group 127 May 1 2016 filename
|
|
|
|
|
* [COMMAND_FINISHED]
|
|
|
|
|
*/
|
|
|
|
|
switch (string[0]) {
|
|
|
|
|
case 'A':
|
|
|
|
|
LOG_DBG("FTCS_PROMPT: %dx%d",
|
|
|
|
|
term->grid->cursor.point.row,
|
|
|
|
|
term->grid->cursor.point.col);
|
|
|
|
|
|
2022-12-08 10:35:30 +01:00
|
|
|
term->grid->cur_row->shell_integration.prompt_marker = true;
|
osc: add support for OSC 133;A (prompt markers)
This patch adds support for the OSC-133;A sequence, introduced by
FinalTerm and implemented by iTerm2, Kitty and more. See
https://iterm2.com/documentation-one-page.html#documentation-escape-codes.html.
The shell emits the OSC just before printing the prompt. This lets the
terminal know where, in the scrollback, there are prompts.
We implement this using a simple boolean in the row struct ("this row
has a prompt"). The prompt marker must be reflowed along with the text
on window resizes.
In an ideal world, erasing, or overwriting the cell where the OSC was
emitted, would remove the prompt mark. Since we don't store this
information in the cell struct, we can't do that. The best we can do
is reset it in erase_line(). This works well enough in the "normal"
screen, when used with a "normal" shell. It doesn't really work in
fullscreen apps, on the alt screen. But that doesn't matter since we
don't support jumping between prompts on the alt screen anyway.
To be able to jump between prompts, two new key bindings have been
added: prompt-prev and prompt-next, bound to ctrl+shift+z and
ctrl+shift+x respectively.
prompt-prev will jump to the previous, not currently visible, prompt,
by moving the viewport, ensuring the prompt is at the top of the
screen.
prompt-next jumps to the next prompt, visible or not. Again, by moving
the viewport to ensure the prompt is at the top of the screen. If
we're at the bottom of the scrollback, the viewport is instead moved
as far down as possible.
Closes #30
2022-06-15 18:44:23 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'B':
|
|
|
|
|
LOG_DBG("FTCS_COMMAND_START");
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'C':
|
2022-12-08 10:46:46 +01:00
|
|
|
LOG_DBG("FTCS_COMMAND_EXECUTED: %dx%d",
|
|
|
|
|
term->grid->cursor.point.row,
|
|
|
|
|
term->grid->cursor.point.col);
|
|
|
|
|
term->grid->cur_row->shell_integration.cmd_start = term->grid->cursor.point.col;
|
osc: add support for OSC 133;A (prompt markers)
This patch adds support for the OSC-133;A sequence, introduced by
FinalTerm and implemented by iTerm2, Kitty and more. See
https://iterm2.com/documentation-one-page.html#documentation-escape-codes.html.
The shell emits the OSC just before printing the prompt. This lets the
terminal know where, in the scrollback, there are prompts.
We implement this using a simple boolean in the row struct ("this row
has a prompt"). The prompt marker must be reflowed along with the text
on window resizes.
In an ideal world, erasing, or overwriting the cell where the OSC was
emitted, would remove the prompt mark. Since we don't store this
information in the cell struct, we can't do that. The best we can do
is reset it in erase_line(). This works well enough in the "normal"
screen, when used with a "normal" shell. It doesn't really work in
fullscreen apps, on the alt screen. But that doesn't matter since we
don't support jumping between prompts on the alt screen anyway.
To be able to jump between prompts, two new key bindings have been
added: prompt-prev and prompt-next, bound to ctrl+shift+z and
ctrl+shift+x respectively.
prompt-prev will jump to the previous, not currently visible, prompt,
by moving the viewport, ensuring the prompt is at the top of the
screen.
prompt-next jumps to the next prompt, visible or not. Again, by moving
the viewport to ensure the prompt is at the top of the screen. If
we're at the bottom of the scrollback, the viewport is instead moved
as far down as possible.
Closes #30
2022-06-15 18:44:23 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'D':
|
2022-12-08 10:46:46 +01:00
|
|
|
LOG_DBG("FTCS_COMMAND_FINISHED: %dx%d",
|
|
|
|
|
term->grid->cursor.point.row,
|
|
|
|
|
term->grid->cursor.point.col);
|
|
|
|
|
term->grid->cur_row->shell_integration.cmd_end = term->grid->cursor.point.col;
|
osc: add support for OSC 133;A (prompt markers)
This patch adds support for the OSC-133;A sequence, introduced by
FinalTerm and implemented by iTerm2, Kitty and more. See
https://iterm2.com/documentation-one-page.html#documentation-escape-codes.html.
The shell emits the OSC just before printing the prompt. This lets the
terminal know where, in the scrollback, there are prompts.
We implement this using a simple boolean in the row struct ("this row
has a prompt"). The prompt marker must be reflowed along with the text
on window resizes.
In an ideal world, erasing, or overwriting the cell where the OSC was
emitted, would remove the prompt mark. Since we don't store this
information in the cell struct, we can't do that. The best we can do
is reset it in erase_line(). This works well enough in the "normal"
screen, when used with a "normal" shell. It doesn't really work in
fullscreen apps, on the alt screen. But that doesn't matter since we
don't support jumping between prompts on the alt screen anyway.
To be able to jump between prompts, two new key bindings have been
added: prompt-prev and prompt-next, bound to ctrl+shift+z and
ctrl+shift+x respectively.
prompt-prev will jump to the previous, not currently visible, prompt,
by moving the viewport, ensuring the prompt is at the top of the
screen.
prompt-next jumps to the next prompt, visible or not. Again, by moving
the viewport to ensure the prompt is at the top of the screen. If
we're at the bottom of the scrollback, the viewport is instead moved
as far down as possible.
Closes #30
2022-06-15 18:44:23 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2023-09-04 14:02:05 +02:00
|
|
|
case 176:
|
2024-02-06 13:55:30 +01:00
|
|
|
if (string[0] == '?' && string[1] == '\0') {
|
2025-01-07 13:00:10 +01:00
|
|
|
#if 0 /* Disabled for now, see #1894 */
|
2024-02-06 13:55:30 +01:00
|
|
|
const char *terminator = term->vt.osc.bel ? "\a" : "\033\\";
|
|
|
|
|
char *reply = xasprintf(
|
|
|
|
|
"\033]176;%s%s",
|
|
|
|
|
term->app_id != NULL ? term->app_id : term->conf->app_id,
|
|
|
|
|
terminator);
|
|
|
|
|
|
|
|
|
|
term_to_slave(term, reply, strlen(reply));
|
|
|
|
|
free(reply);
|
2025-01-07 13:00:10 +01:00
|
|
|
#else
|
|
|
|
|
LOG_WARN("OSC-176 app-id query ignored");
|
|
|
|
|
#endif
|
2024-02-06 13:55:30 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-04 14:02:05 +02:00
|
|
|
term_set_app_id(term, string);
|
|
|
|
|
break;
|
|
|
|
|
|
2019-07-22 19:10:15 +02:00
|
|
|
case 555:
|
|
|
|
|
osc_flash(term);
|
|
|
|
|
break;
|
|
|
|
|
|
2020-12-08 19:19:55 +01:00
|
|
|
case 777: {
|
|
|
|
|
/*
|
|
|
|
|
* OSC 777 is an URxvt generic escape used to send commands to
|
|
|
|
|
* perl extensions. The generic syntax is: \E]777;<command>;<string>ST
|
|
|
|
|
*
|
|
|
|
|
* We only recognize the 'notify' command, which is, if not
|
|
|
|
|
* well established, at least fairly well known.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
char *param_brk = strchr(string, ';');
|
|
|
|
|
if (param_brk == NULL) {
|
|
|
|
|
UNHANDLED();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strncmp(string, "notify", param_brk - string) == 0)
|
|
|
|
|
osc_notify(term, param_brk + 1);
|
|
|
|
|
else
|
|
|
|
|
UNHANDLED();
|
2019-12-19 07:28:10 +01:00
|
|
|
break;
|
2020-12-08 19:19:55 +01:00
|
|
|
}
|
2019-12-19 07:28:10 +01:00
|
|
|
|
2019-07-05 09:46:48 +02:00
|
|
|
default:
|
2019-07-30 21:42:46 +02:00
|
|
|
UNHANDLED();
|
2019-07-07 16:32:18 +02:00
|
|
|
break;
|
2019-07-05 09:46:48 +02:00
|
|
|
}
|
2019-06-15 22:22:44 +02:00
|
|
|
}
|
2019-07-19 08:59:35 +02:00
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
osc_ensure_size(struct terminal *term, size_t required_size)
|
|
|
|
|
{
|
2022-03-02 01:55:25 +00:00
|
|
|
if (likely(required_size <= term->vt.osc.size))
|
2019-07-19 08:59:35 +02:00
|
|
|
return true;
|
|
|
|
|
|
2022-03-02 01:55:25 +00:00
|
|
|
const size_t pow2_max = ~(SIZE_MAX >> 1);
|
|
|
|
|
if (unlikely(required_size > pow2_max)) {
|
|
|
|
|
LOG_ERR("required OSC buffer size (%zu) exceeds limit (%zu)",
|
|
|
|
|
required_size, pow2_max);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t new_size = max(term->vt.osc.size, 4096);
|
|
|
|
|
while (new_size < required_size) {
|
|
|
|
|
new_size <<= 1;
|
|
|
|
|
}
|
2019-07-19 08:59:35 +02:00
|
|
|
|
|
|
|
|
uint8_t *new_data = realloc(term->vt.osc.data, new_size);
|
|
|
|
|
if (new_data == NULL) {
|
|
|
|
|
LOG_ERRNO("failed to increase size of OSC buffer");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-02 01:55:25 +00:00
|
|
|
LOG_DBG("resized OSC buffer: %zu", new_size);
|
2019-07-19 08:59:35 +02:00
|
|
|
term->vt.osc.data = new_data;
|
|
|
|
|
term->vt.osc.size = new_size;
|
|
|
|
|
return true;
|
|
|
|
|
}
|