home · contact · privacy
Add initial stage of curses client.
[plomrogue2-experiments] / new2 / rogue_chat_curses.py
1 #!/usr/bin/env python3
2 import curses
3 import socket
4 import queue
5 import threading
6 from plomrogue.io_tcp import PlomSocket
7
8 def recv_loop(plom_socket, q):
9     for msg in plom_socket.recv():
10         q.put(msg)
11
12 class TUI:
13
14     def __init__(self, socket, q):
15         self.queue = q
16         self.socket = socket
17         self.log = []
18         self.log_msg("hallo")
19         self.log_msg("welt")
20         self.do_refresh = True
21         curses.wrapper(self.loop)
22
23     def log_msg(self, msg):
24         self.log += [msg]
25         if len(self.log) > 100:
26             self.log = self.log[-100:]
27
28     def loop(self, stdscr):
29
30         def msg_into_lines_of_width(msg, width):
31             chunk = ''
32             lines = []
33             x = 0
34             for i in range(len(msg)):
35                 x += 1
36                 if x >= width or msg[i] == "\n":
37                     lines += [chunk]
38                     chunk = ''
39                     x = 0
40                 if msg[i] != "\n":
41                     chunk += msg[i]
42             lines += [chunk]
43             return lines
44
45         def reset_screen_size():
46             self.rows, self.cols = stdscr.getmaxyx()
47             self.cols -= 1  # ugly TODO ncurses bug workaround, FIXME
48             self.window_width = self.cols // 2
49
50         def recalc_input_lines():
51             self.input_lines = msg_into_lines_of_width(input_prompt +input_,
52                                                        self.window_width)
53
54         def draw_history():
55             lines = []
56             for line in self.log:
57                 lines += msg_into_lines_of_width(line, self.window_width)
58             lines.reverse()
59             max_y = self.rows - len(self.input_lines)
60             for i in range(len(lines)):
61                 if (i >= max_y):
62                     break
63                 stdscr.addstr(max_y - i - 1, self.window_width, lines[i])
64
65         def draw_input():
66             y = self.rows - len(self.input_lines)
67             for i in range(len(self.input_lines)):
68                 stdscr.addstr(y, self.window_width, self.input_lines[i])
69                 y += 1
70
71         curses.curs_set(False)  # hide cursor
72         stdscr.timeout(10)
73         reset_screen_size()
74         input_ = ''
75         input_prompt = '> '
76         while True:
77
78             if self.do_refresh:
79                 self.do_refresh = False
80                 stdscr.clear()
81                 recalc_input_lines()
82
83                 draw_input()
84                 draw_history()
85
86             while True:
87                 try:
88                     msg = self.queue.get(block=False)
89                     self.log_msg(msg)
90                     self.do_refresh = True
91                 except queue.Empty:
92                     break
93
94             try:
95                 key = stdscr.getkey()
96             except curses.error:
97                 continue
98             self.do_refresh = True
99
100             if key == 'KEY_RESIZE':
101                 reset_screen_size()
102             elif key == 'KEY_BACKSPACE':
103                 input_ = input_[:-1]
104             elif key == '\n':  # Return key
105                 self.socket.send(input_)
106                 input_ = ""
107             else:
108                 input_ += key
109                 # TODO find out why - 1 is necessary here
110                 max_length = self.window_width * self.rows - len(input_prompt) - 1
111                 if len(input_) > max_length:
112                     input_ = input_[:max_length]
113
114 s = socket.create_connection(('127.0.0.1', 5000))
115 plom_socket = PlomSocket(s)
116 q = queue.Queue()
117 t = threading.Thread(target=recv_loop, args=(plom_socket, q))
118 t.start()
119 TUI(plom_socket, q)