More work on dnd kitten

This commit is contained in:
Kovid Goyal
2026-05-11 21:51:23 +05:30
parent 43b028bd6a
commit 634f13e65f
4 changed files with 79 additions and 61 deletions

View File

@@ -391,7 +391,7 @@ examine the :ref:`machine_id` sent with the enable drag offers
from the URI list. To request data for a particular entry, terminals send an from the URI list. To request data for a particular entry, terminals send an
escape code of the form:: escape code of the form::
OSC _dnd_code ; t=k:x=idx ; base64 encoded file data ST OSC _dnd_code ; t=k:x=idx ST
Here ``idx`` is the one based index into the list of entries in the Here ``idx`` is the one based index into the list of entries in the
``text/uri-list`` MIME type. Then the client can respond with the data ``text/uri-list`` MIME type. Then the client can respond with the data

View File

@@ -23,7 +23,6 @@ var _ = fmt.Print
type data_request struct { type data_request struct {
drag_source *drag_source drag_source *drag_source
send_remote_data bool
index int index int
write_id loop.IdType write_id loop.IdType
base64 streaming_base64.StreamingBase64Encoder base64 streaming_base64.StreamingBase64Encoder
@@ -51,7 +50,7 @@ type drag_status struct {
current_remote_file *remote_data_item current_remote_file *remote_data_item
dir_handle_counter int dir_handle_counter int
remote_item_write_id loop.IdType remote_item_write_id loop.IdType
remote_data_was_sent bool remote_data_requests []int
} }
func find_drag_image(drag_sources map[string]*drag_source) image.Image { func find_drag_image(drag_sources map[string]*drag_source) image.Image {
@@ -66,12 +65,10 @@ func find_drag_image(drag_sources map[string]*drag_source) image.Image {
} }
} }
var uri_list []string var uri_list []string
if ds := drag_sources["text/uri-list"]; ds != nil && len(ds.data) > 0 { if ds := drag_sources["text/uri-list"]; ds != nil {
if q, err := parse_uri_list(string(ds.data)); err == nil { for _, e := range ds.uri_list {
for _, path := range q { if e.path != "" {
if path != "" { uri_list = append(uri_list, e.path)
uri_list = append(uri_list, path)
}
} }
} }
} }
@@ -102,16 +99,14 @@ func (dnd *dnd) set_drag_image_text() (err error) {
} }
} }
if icon == "" { if icon == "" {
if ds := dnd.drag_sources["text/uri-list"]; ds != nil && len(ds.data) > 0 { if ds := dnd.drag_sources["text/uri-list"]; ds != nil {
if q, err := parse_uri_list(string(ds.data)); err == nil { for _, e := range ds.uri_list {
for _, path := range q { if e.path != "" && from_path(e.path) {
if path != "" && from_path(path) {
break break
} }
} }
} }
} }
}
if icon == "" { if icon == "" {
icon = strings.TrimSpace("󰮐 ") icon = strings.TrimSpace("󰮐 ")
} }
@@ -219,7 +214,7 @@ func (dnd *dnd) reset_drag() {
dnd.drag_status = drag_status{} dnd.drag_status = drag_status{}
} }
func (dnd *dnd) on_drag_event(x, y, operation, Y int) (err error) { func (dnd *dnd) on_drag_event(x, y, operation int) (err error) {
switch x { switch x {
case 1: case 1:
dnd.drag_status.accepted_mime = y dnd.drag_status.accepted_mime = y
@@ -230,14 +225,14 @@ func (dnd *dnd) on_drag_event(x, y, operation, Y int) (err error) {
case 4: case 4:
was_dropped := dnd.drag_status.dropped was_dropped := dnd.drag_status.dropped
was_move := dnd.drag_status.accepted_operation == 2 was_move := dnd.drag_status.accepted_operation == 2
was_remote := dnd.drag_status.remote_data_was_sent
dnd.reset_drag() dnd.reset_drag()
if was_dropped && dnd.has_exit_on("drag-finish") { if was_dropped && dnd.has_exit_on("drag-finish") {
dnd.lp.Quit(0) dnd.lp.Quit(0)
} }
if was_dropped && was_move && was_remote { if was_dropped && was_move {
if ds := dnd.drag_sources["text/uri-list"]; ds != nil { if ds := dnd.drag_sources["text/uri-list"]; ds != nil {
for _, item := range ds.uri_list { for _, item := range ds.uri_list {
if item.was_sent {
if item.metadata.IsDir() { if item.metadata.IsDir() {
err = os.RemoveAll(item.path) err = os.RemoveAll(item.path)
} else { } else {
@@ -248,6 +243,7 @@ func (dnd *dnd) on_drag_event(x, y, operation, Y int) (err error) {
} }
} }
} }
}
dnd.drag_sources = nil dnd.drag_sources = nil
dnd.allow_drags = false dnd.allow_drags = false
dnd.lp.StopOfferingDrags() dnd.lp.StopOfferingDrags()
@@ -256,7 +252,7 @@ func (dnd *dnd) on_drag_event(x, y, operation, Y int) (err error) {
} }
} }
case 5: case 5:
if err = dnd.handle_data_request(y, Y == 1); err != nil { if err = dnd.handle_data_request(y); err != nil {
return err return err
} }
} }
@@ -272,28 +268,24 @@ func (dnd *dnd) finish_drag(errname string) {
dnd.reset_drag() dnd.reset_drag()
} }
func (dnd *dnd) handle_data_request(idx int, send_remote_data bool) (err error) { func (dnd *dnd) handle_data_request(idx int) (err error) {
if idx < 0 || idx >= len(dnd.drag_status.offered_mimes) { if idx < 0 || idx >= len(dnd.drag_status.offered_mimes) {
dnd.finish_drag("EINVAL") dnd.finish_drag("EINVAL")
return fmt.Errorf("terminal asked for drag data from MIME list with out of bounds index: %d", idx) return fmt.Errorf("terminal asked for drag data from MIME list with out of bounds index: %d", idx)
} }
mime := dnd.drag_status.offered_mimes[idx] mime := dnd.drag_status.offered_mimes[idx]
ds := dnd.drag_sources[mime] ds := dnd.drag_sources[mime]
send_remote_data = send_remote_data && mime == "text/uri-list" && len(ds.uri_list) > 0
for _, dr := range dnd.drag_status.data_requests { for _, dr := range dnd.drag_status.data_requests {
if dr.index == idx { if dr.index == idx {
dnd.finish_drag("EINVAL") dnd.finish_drag("EINVAL")
return fmt.Errorf("terminal sent a duplicate drag data request") return fmt.Errorf("terminal sent a duplicate drag data request")
} }
} }
dr := &data_request{drag_source: ds, send_remote_data: send_remote_data, index: idx} dr := &data_request{drag_source: ds, index: idx}
if ds.path == "" { if ds.path == "" {
dnd.lp.QueueDnDData(DC{Type: 'e', Y: idx, Payload: utils.UnsafeStringToBytes(base64.RawStdEncoding.EncodeToString(ds.data))}) dnd.lp.QueueDnDData(DC{Type: 'e', Y: idx, Payload: utils.UnsafeStringToBytes(base64.RawStdEncoding.EncodeToString(ds.data))})
dnd.lp.QueueDnDData(DC{Type: 'e', Y: idx}) // EOF dnd.lp.QueueDnDData(DC{Type: 'e', Y: idx}) // EOF
if !dr.send_remote_data {
return return
}
return dnd.start_remote_data_send(ds)
} else { } else {
if ds.file != nil { if ds.file != nil {
ds.file.Close() ds.file.Close()
@@ -356,9 +348,7 @@ func (dnd *dnd) on_data_request_finished(i int) (err error) {
dr.drag_source.file = nil dr.drag_source.file = nil
} }
dnd.drag_status.data_requests = slices.Delete(dnd.drag_status.data_requests, i, i+1) dnd.drag_status.data_requests = slices.Delete(dnd.drag_status.data_requests, i, i+1)
if dr.send_remote_data { if len(dnd.drag_status.data_requests) > 0 {
err = dnd.start_remote_data_send(dr.drag_source)
} else if len(dnd.drag_status.data_requests) > 0 {
err = dnd.send_data_for_data_request(0) err = dnd.send_data_for_data_request(0)
} }
return return
@@ -386,6 +376,9 @@ func (dnd *dnd) send_remote_dir(path string, idx_in_uri_list, parent_dir_handle,
dnd.finish_drag("EIO") dnd.finish_drag("EIO")
return nil, err return nil, err
} }
for dnd.drag_status.dir_handle_counter < 2 {
dnd.drag_status.dir_handle_counter++
}
handle := dnd.drag_status.dir_handle_counter handle := dnd.drag_status.dir_handle_counter
dnd.drag_status.dir_handle_counter++ dnd.drag_status.dir_handle_counter++
names := make([]string, 0, len(entries)) names := make([]string, 0, len(entries))
@@ -447,7 +440,11 @@ func (dnd *dnd) send_next_file_chunk() (err error) {
func (dnd *dnd) next_remote_item() (err error) { func (dnd *dnd) next_remote_item() (err error) {
if len(dnd.drag_status.remote_items) < 1 { if len(dnd.drag_status.remote_items) < 1 {
dnd.lp.QueueDnDData(DC{Type: 'k'}) // inform terminal remote data is finished // current remote data request finished
dnd.drag_status.remote_data_requests = dnd.drag_status.remote_data_requests[1:]
if len(dnd.drag_status.remote_data_requests) > 0 {
return dnd.send_next_remote_data_request()
}
if len(dnd.drag_status.data_requests) > 0 { if len(dnd.drag_status.data_requests) > 0 {
err = dnd.send_data_for_data_request(0) err = dnd.send_data_for_data_request(0)
} }
@@ -478,12 +475,31 @@ func (dnd *dnd) next_remote_item() (err error) {
return return
} }
func (dnd *dnd) start_remote_data_send(ds *drag_source) (err error) { func (dnd *dnd) on_drag_remote_data_request(idx int) (err error) {
dnd.drag_status.dir_handle_counter = 2 ds := dnd.drag_sources["text/uri_list"]
dnd.drag_status.remote_item_write_id = 0 if ds == nil || len(ds.uri_list) < 1 {
dnd.drag_status.remote_data_was_sent = true dnd.finish_drag("EINVAL")
return fmt.Errorf("terminal asked for drag data from URI list but no list present")
}
if idx < 0 || idx >= len(ds.uri_list) {
dnd.finish_drag("EINVAL")
return fmt.Errorf("terminal asked for drag data from URI list with out of bounds index: %d", idx)
}
ds.uri_list[idx].was_sent = true
dnd.drag_status.remote_data_requests = append(dnd.drag_status.remote_data_requests, idx)
if len(dnd.drag_status.remote_data_requests) == 1 {
err = dnd.send_next_remote_data_request()
}
return
}
func (dnd *dnd) send_next_remote_data_request() (err error) {
if len(dnd.drag_status.remote_data_requests) == 0 {
return nil
}
i := dnd.drag_status.remote_data_requests[0]
x := dnd.drag_sources["text/uri-list"].uri_list[i]
items := []*remote_data_item{} items := []*remote_data_item{}
for i, x := range ds.uri_list {
if x.metadata.IsDir() { if x.metadata.IsDir() {
if children, err := dnd.send_remote_dir(x.path, i, 0, i); err != nil { if children, err := dnd.send_remote_dir(x.path, i, 0, i); err != nil {
return err return err
@@ -498,7 +514,6 @@ func (dnd *dnd) start_remote_data_send(ds *drag_source) (err error) {
f := remote_data_item{idx_in_parent: i, idx_in_uri_list: i, metadata: x.metadata, path: x.path} f := remote_data_item{idx_in_parent: i, idx_in_uri_list: i, metadata: x.metadata, path: x.path}
dnd.drag_status.remote_items = append(dnd.drag_status.remote_items, &f) dnd.drag_status.remote_items = append(dnd.drag_status.remote_items, &f)
} }
}
dnd.drag_status.remote_items = append(dnd.drag_status.remote_items, items...) dnd.drag_status.remote_items = append(dnd.drag_status.remote_items, items...)
if dnd.drag_status.remote_item_write_id == 0 { if dnd.drag_status.remote_item_write_id == 0 {
return dnd.next_remote_item() return dnd.next_remote_item()

View File

@@ -31,6 +31,7 @@ type uri_list_item struct {
path, uri, human_name string path, uri, human_name string
file *os.File file *os.File
metadata os.FileInfo metadata os.FileInfo
was_sent bool
} }
type drag_source struct { type drag_source struct {
@@ -105,7 +106,7 @@ func (dnd *dnd) send_test_response(payload string) {
} }
func (dnd *dnd) has_exit_on(event string) bool { func (dnd *dnd) has_exit_on(event string) bool {
for _, e := range strings.Split(dnd.opts.ExitOn, ",") { for e := range strings.SplitSeq(dnd.opts.ExitOn, ",") {
if strings.TrimSpace(e) == event { if strings.TrimSpace(e) == event {
return true return true
} }
@@ -276,7 +277,9 @@ func (dnd *dnd) run_loop() (err error) {
case 'E': case 'E':
return dnd.on_drag_error(cmd) return dnd.on_drag_error(cmd)
case 'e': case 'e':
return dnd.on_drag_event(cmd.X, cmd.Y, cmd.Operation, cmd.Yp) return dnd.on_drag_event(cmd.X, cmd.Y, cmd.Operation)
case 'k':
return dnd.on_drag_remote_data_request(cmd.X - 1)
} }
return nil return nil
} }

View File

@@ -426,7 +426,7 @@ class PTY:
if isinstance(data, str): if isinstance(data, str):
data = data.encode('utf-8') data = data.encode('utf-8')
if self.log_data_flow: if self.log_data_flow:
print('t -> c:', data) print('t -> c:', bytes(data))
self.write_buf += data self.write_buf += data
if flush: if flush:
self.process_input_from_child(0) self.process_input_from_child(0)