More work on the DnD protocol
This commit is contained in:
parent
3ee440b004
commit
4f7855aede
8 changed files with 54 additions and 14 deletions
|
|
@ -128,10 +128,13 @@ send an escape code of the form::
|
|||
That is, it must send a request for data with no MIME type specified. The
|
||||
terminal emulator must then inform the OS that the drop is completed.
|
||||
|
||||
Dropping from remote machines
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In order to support dropping of files from remote machines or to remote
|
||||
machines, clients can first request the text/uri-list MIME type to get a list
|
||||
of dropped URIs. For every ``file://`` URI they can send the terminal emulator
|
||||
a data request of the form::
|
||||
machines, clients can first request the :rfc:`text/uri-list <2483>` MIME
|
||||
type to get a list of dropped URIs. For every URI in the list, they can
|
||||
send the terminal emulator a data request of the form::
|
||||
|
||||
OSC _dnd_code ; t=s ; text/uri-list:idx ST
|
||||
|
||||
|
|
@ -142,18 +145,20 @@ transmit the data as for a normal MIME data request.
|
|||
Terminals must reply with ``t=R ; ENOENT`` if the index is out of bounds.
|
||||
If the client does not first request the ``text/uri-list`` MIME type or that
|
||||
MIME type is not present in the drop, the terminal must reply with
|
||||
``t=R ; EINVAL``. Similarly if the client requests an entry that is not a
|
||||
``file://`` URI the terminal must reply with ``EUNKNOWN``.
|
||||
``t=R ; EINVAL``. Terminals must support at least ``file://`` URIs.
|
||||
If the client requests an entry that is not a supported URI type the
|
||||
terminal must reply with ``t=R ; EUNKNOWN``.
|
||||
|
||||
Terminals must ONLY send data for regular files. Symbolic links must be
|
||||
resolved and the corresponding file read. If the terminal does not have
|
||||
permission to read the file it must reply with ``t=R ; EPERM``. Terminals
|
||||
must respond with ``t=R ; EINVAL`` if the file is not a regular file after
|
||||
resolving symlinks and ``t=R ; ENOENT`` if the file does not exist.
|
||||
resolving symlinks and ``t=R ; ENOENT`` if the file does not exist. If an
|
||||
I/O error occurs the terminal must send ``t=R ; EIO``.
|
||||
|
||||
|
||||
Dropping directories
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Reading remote directories
|
||||
+++++++++++++++++++++++++++
|
||||
|
||||
If the file is actually a directory the terminal must respond with ``t=d:x=idx ; payload``.
|
||||
Here payload is a null byte separated list of entries in the directory that are
|
||||
|
|
@ -162,7 +167,8 @@ encoded and might be chunked if the directory has a lot of entries. The first
|
|||
entry in the list must be a unique identifier for the directory, to prevent
|
||||
symlink loops. Terminals may use whatever identifier is most suitable for their platforms, clients should
|
||||
not re-request the contents of a directory whose identifier they have seen
|
||||
before.
|
||||
before. On POSIX platforms the identifier would typically be device and inode
|
||||
number.
|
||||
|
||||
``idx`` is an arbitrary 32 bit integer that acts as a handle to this
|
||||
directory. The client can now read the files in this directory using requests of the form
|
||||
|
|
@ -194,6 +200,7 @@ Key Value Default Description
|
|||
``M`` - a drop dropped event
|
||||
``r`` - request dropped data
|
||||
``R`` - report an error while retrieving data
|
||||
``s`` - request data from the URI list entry
|
||||
``d`` - send directory contents
|
||||
|
||||
``m`` Chunking indicator ``0`` ``0`` or ``i``
|
||||
|
|
|
|||
|
|
@ -331,7 +331,7 @@ def parsers() -> None:
|
|||
write_header(text, 'kitty/parse-multicell-command.h')
|
||||
|
||||
keymap = {
|
||||
't': ('type', flag('aAmMrRd')),
|
||||
't': ('type', flag('aAmMrRsd')),
|
||||
'm': ('more', 'uint'),
|
||||
'i': ('client_id', 'uint'),
|
||||
'o': ('operation', 'uint'),
|
||||
|
|
|
|||
|
|
@ -3716,3 +3716,28 @@ def search_scrollback_in_active(self) -> None:
|
|||
def copy_or_noop(self) -> None:
|
||||
if w := self.active_window:
|
||||
w.copy_or_noop()
|
||||
|
||||
def nth_decoded_file_url(self, n: int, text_uri_list: bytes) -> tuple[bool, str]:
|
||||
' Return the abspath of the nth url in text_uri_list if it is a valid file:// URL. Appropriate error code returned otherwise '
|
||||
from urllib.parse import unquote, urlparse
|
||||
for line in text_uri_list.decode().splitlines():
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
if n <= 0:
|
||||
break
|
||||
n -= 1
|
||||
else:
|
||||
return False, 'ENOENT'
|
||||
try:
|
||||
purl = urlparse(line.strip())
|
||||
if purl.scheme != 'file':
|
||||
return False, 'EUNKNOWN'
|
||||
if not purl.path:
|
||||
return False, 'EINVAL'
|
||||
path = unquote(purl.path)
|
||||
if not os.path.isabs(path):
|
||||
return False, 'EINVAL'
|
||||
path = os.path.abspath(os.path.realpath(path))
|
||||
return True, path
|
||||
except Exception:
|
||||
return False, 'EINVAL'
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ drop_free_data(Window *w) {
|
|||
drop_free_accepted_mimes(w);
|
||||
free_pending(&w->drop.pending);
|
||||
free(w->drop.registered_mimes); w->drop.registered_mimes = NULL;
|
||||
free(w->drop.uri_list); w->drop.uri_list = NULL;
|
||||
free(w->drop.getting_data_for_mime); w->drop.getting_data_for_mime = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -330,12 +331,18 @@ drop_request_data(Window *w, const char *mime) {
|
|||
}
|
||||
|
||||
void
|
||||
drop_dispatch_data(Window *w, const char *data, ssize_t sz) {
|
||||
drop_dispatch_data(Window *w, const char *mime, const char *data, ssize_t sz) {
|
||||
if (sz < 0) drop_send_error(w, -sz);
|
||||
else {
|
||||
char buf[128];
|
||||
int header_size = snprintf(buf, sizeof(buf), "\x1b]%d;t=r", DND_CODE);
|
||||
queue_payload_to_child(w->id, w->drop.client_id, &w->drop.pending, buf, header_size, sz ? data : NULL, sz, true);
|
||||
if (strcmp(mime, "text/uri-list") == 0) {
|
||||
w->drop.uri_list_sz += sz;
|
||||
w->drop.uri_list = realloc(w->drop.uri_list, w->drop.uri_list_sz);
|
||||
if (w->drop.uri_list) memcpy(w->drop.uri_list + w->drop.uri_list_sz - sz, data, sz);
|
||||
else w->drop.uri_list_sz = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,5 +15,5 @@ void drop_free_data(Window *w);
|
|||
void drop_request_data(Window *w, const char *mime);
|
||||
void drop_set_status(Window *w, int operation, const char *payload, size_t payload_sz, bool more);
|
||||
size_t drop_update_mimes(Window *w, const char **allowed_mimes, size_t allowed_mimes_count);
|
||||
void drop_dispatch_data(Window *w, const char *data, ssize_t sz);
|
||||
void drop_dispatch_data(Window *w, const char *mime_type, const char *data, ssize_t sz);
|
||||
void drop_finish(Window *w);
|
||||
|
|
|
|||
|
|
@ -857,7 +857,7 @@ on_drop(GLFWwindow *window, GLFWDropEvent *ev) {
|
|||
if (w->drop.getting_data_for_mime && strcmp(w->drop.getting_data_for_mime, ev->mimes[0]) == 0) {
|
||||
char buf[3072];
|
||||
ssize_t ret = ev->read_data(window, ev, buf, sizeof(buf));
|
||||
drop_dispatch_data(w, buf, ret);
|
||||
drop_dispatch_data(w, ev->mimes[0], buf, ret);
|
||||
if (ret < 0) ev->finish_drop(window, GLFW_DRAG_OPERATION_GENERIC);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ static inline void parse_dnd_code(PS *self, uint8_t *parser_buf,
|
|||
case type: {
|
||||
g.type = parser_buf[pos++];
|
||||
if (g.type != 'A' && g.type != 'M' && g.type != 'R' && g.type != 'a' &&
|
||||
g.type != 'm' && g.type != 'r') {
|
||||
g.type != 'd' && g.type != 'm' && g.type != 'r' && g.type != 's') {
|
||||
REPORT_ERROR("Malformed DnDCommand control block, unknown flag value "
|
||||
"for type: 0x%x",
|
||||
g.type);
|
||||
|
|
|
|||
|
|
@ -261,6 +261,7 @@ typedef struct Window {
|
|||
bool wanted, hovered, dropped;
|
||||
uint32_t client_id;
|
||||
char *registered_mimes;
|
||||
char *uri_list; size_t uri_list_sz;
|
||||
PendingData pending;
|
||||
|
||||
const char **offerred_mimes; size_t num_offerred_mimes, offered_mimes_total_size;
|
||||
|
|
|
|||
Loading…
Reference in a new issue