9 def recv_loop(socket, urwid_pipe_write_fd, server_output):
10 """Loop to receive messages from socket and deliver them to urwid.
12 The message transfer to urwid is a bit weird. The urwid developers warn
13 against sharing urwid resources among threads, and recommend using urwid's
14 watch_pipe mechanism: using a pipe from non-urwid threads into a single
15 urwid thread. We could pipe socket.recv output directly, but then we get
16 complicated buffering situations here as well as in the urwid code that
17 receives the pipe content. It's much easier to update a third resource
18 (server_output, which references an object that's also known to the urwid
19 code) to contain the new message, and then just use the urwid pipe
20 (urwid_pipe_write_fd) to trigger the urwid code to pull the message in from
21 that third resource. We send a single b' ' through the pipe to trigger it.
24 for msg in plom_socket_io.recv(socket):
25 server_output[0] = msg
26 os.write(urwid_pipe_write_fd, b' ')
30 """Helps delivering data from other thread to widget via message_container.
32 The whole class only exists to provide handle_input as a bound method, with
33 widget and message_container pre-set, as (bound) handle_input is used as a
34 callback in urwid's watch_pipe – which merely provides its callback target
35 with one parameter for a pipe to read data from an urwid-external thread.
38 def __init__(self, widget, message_container):
40 self.message_container = message_container
42 def handle_input(self, trigger):
43 """On input from other thread, either quit, or write to widget text.
45 Serves as a receiver to urwid's watch_pipe mechanism, with trigger the
46 data that a pipe defined by watch_pipe delivers. To avoid buffering
47 trouble, we don't care for that data beyond the fact that its receival
48 triggers this function: The sender is to write the data it wants to
49 deliver into the container referenced by self.message_container, and
50 just pipe the trigger to inform us about this.
52 If the message delivered is 'BYE', quits Urbit.
54 if self.message_container[0] == 'BYE':
55 raise urwid.ExitMainLoop()
57 self.widget.set_text('SERVER: ' + self.message_container[0])
60 class SocketInputWidget(urwid.Filler):
62 def __init__(self, socket, *args, **kwargs):
63 super().__init__(*args, **kwargs)
66 def keypress(self, size, key):
67 """Act like super(), except on Enter: send .edit_text, and empty it."""
69 return super().keypress(size, key)
70 plom_socket_io.send(self.socket, edit.edit_text)
74 s = socket.create_connection(('127.0.0.1', 5000))
76 edit = urwid.Edit('SEND: ')
78 pile = urwid.Pile([edit, txt])
79 fill = SocketInputWidget(s, pile)
80 loop = urwid.MainLoop(fill)
83 write_fd = loop.watch_pipe(getattr(InputHandler(txt, server_output),
85 thread = threading.Thread(target=recv_loop,
86 kwargs={'socket': s, 'server_output': server_output,
87 'urwid_pipe_write_fd': write_fd})