Allow mocking remote clients

This commit is contained in:
Kovid Goyal 2026-04-19 20:42:43 +05:30
parent 51b0e9c4ad
commit 0ddbffdf79
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
6 changed files with 47 additions and 18 deletions

View file

@ -97,10 +97,10 @@ func run_loop(opts *Options, drop_dests map[string]drop_dest, drag_sources map[s
lp.OnInitialize = func() (string, error) {
lp.AllowLineWrapping(false)
if allow_drops {
lp.StartAcceptingDrops(slices.Collect(maps.Keys(drop_dests))...)
lp.StartAcceptingDrops(opts.MachineId, slices.Collect(maps.Keys(drop_dests))...)
}
if allow_drags {
lp.StartOfferingDrags()
lp.StartOfferingDrags(opts.MachineId)
}
lp.SetWindowTitle("Drag and drop")
return "", render_screen()

View file

@ -34,6 +34,10 @@ choices=copy,move,either
The set of allowed actions when dragging. If :code:`either` is chosen then the drop destination can pick
which action it wants. If the action is a move, then the dragged files are deleted when the drag finishes
and the kitten exits after the drag.
--machine-id
The machine id to use instead of the actual machine id.
'''.format
help_text = '''\

View file

@ -2184,9 +2184,18 @@ dnd_test_probe_state(PyObject *self UNUSED, PyObject *args) {
if (!PyArg_ParseTuple(args, "Ks", &window_id, &q)) return NULL;
Window *w = window_for_window_id((id_type)window_id);
if (!w) { PyErr_SetString(PyExc_ValueError, "Window not found"); return NULL; }
if (strcmp(q, "drop_wanted") == 0) {
return Py_NewRef(w->drop.wanted ? Py_True : Py_False);
}
if (strcmp(q, "drag_can_offer") == 0) {
return Py_NewRef(w->drag_source.can_offer ? Py_True : Py_False);
}
if (strcmp(q, "drop_is_remote_client") == 0) {
return Py_NewRef(w->drop.is_remote_client ? Py_True : Py_False);
}
if (strcmp(q, "drag_is_remote_client") == 0) {
return Py_NewRef(w->drag_source.is_remote_client ? Py_True : Py_False);
}
Py_RETURN_NONE;
}

View file

@ -51,13 +51,18 @@ class TestDnDKitten(BaseTest):
self.pty.write_to_child(chunk)
self.pty.write_to_child(b'\x1b\\', flush=is_last and flush)
def finish_setup(self, cmd=None):
cmd = cmd or [kitten_exe(), 'dnd']
def finish_setup(self, remote_client: bool = False):
cmd = [kitten_exe(), 'dnd']
if remote_client:
cmd.append('--machine-id=remote-client-for-test')
self.pty = self.enterContext(PTY(argv=cmd, rows=25, columns=80, window_id=self.capture.window_id))
self.pty.callbacks.printbuf = self
self.screen = self.pty.screen
self.pty.wait_till(lambda: bool(self.pty.callbacks.titlebuf))
self.assertFalse(self.probe_state('drop_is_remote_client'))
self.assertTrue(self.probe_state('drop_wanted'))
self.assertEqual(remote_client, self.probe_state('drop_is_remote_client'))
if self.probe_state('drag_can_offer'):
self.assertEqual(remote_client, self.probe_state('drag_is_remote_client'))
def append(self, text):
self.messages_from_kitten += text
@ -80,4 +85,6 @@ class TestDnDKitten(BaseTest):
del self.pty
def test_dnd_kitten_drop(self):
self.finish_setup()
for remote_client in (False, True):
with self.subTest(remote_client=remote_client):
self.finish_setup(remote_client=remote_client)

View file

@ -548,7 +548,7 @@ func Run(args []string) (rc int, err error) {
lp.OnInitialize = func() (string, error) {
lp.SetWindowTitle("kitty mouse features demo")
lp.SetCursorVisible(false)
lp.StartAcceptingDrops("text/plain", "text/uri-list")
lp.StartAcceptingDrops("", "text/plain", "text/uri-list")
draw_screen()
return "", nil
}

View file

@ -707,13 +707,24 @@ func (self *Loop) QueueDnDData(metadata map[string]string, payload string, as_ba
return ans
}
func (self *Loop) StartAcceptingDrops(mime_types ...string) {
self.QueueDnDData(map[string]string{"t": "a"}, strings.Join(mime_types, " "), false)
if ans, err := machine_id.MachineId(); err == nil {
func effective_machine_id(m string) string {
if m == "" {
if ans, err := machine_id.MachineId(); err == nil {
m = ans
}
}
if m != "" {
mac := hmac.New(sha256.New, []byte("tty-dnd-protocol-machine-id"))
mac.Write(utils.UnsafeStringToBytes(ans))
ans = "1:" + hex.EncodeToString(mac.Sum(nil))
self.QueueDnDData(map[string]string{"t": "a", "x": "1"}, ans, false)
mac.Write(utils.UnsafeStringToBytes(m))
m = "1:" + hex.EncodeToString(mac.Sum(nil))
}
return m
}
func (self *Loop) StartAcceptingDrops(machine_id string, mime_types ...string) {
self.QueueDnDData(map[string]string{"t": "a"}, strings.Join(mime_types, " "), false)
if m := effective_machine_id(machine_id); m != "" {
self.QueueDnDData(map[string]string{"t": "a", "x": "1"}, m, false)
}
}
@ -721,12 +732,10 @@ func (self *Loop) StopAcceptingDrops() {
self.QueueDnDData(map[string]string{"t": "A"}, "", false)
}
func (self *Loop) StartOfferingDrags() {
func (self *Loop) StartOfferingDrags(machine_id string) {
payload := ""
if ans, err := machine_id.MachineId(); err == nil {
mac := hmac.New(sha256.New, []byte("tty-dnd-protocol-machine-id"))
mac.Write(utils.UnsafeStringToBytes(ans))
payload = "1:" + hex.EncodeToString(mac.Sum(nil))
if m := effective_machine_id(machine_id); m != "" {
payload = m
}
self.QueueDnDData(map[string]string{"t": "o", "x": "1"}, payload, false)
}