[PowerPC] Collect some CallLowering arguments into a struct. [NFC]
[llvm-project.git] / lldb / utils / lui / cui.py
blobfffb812fbb9f630d8ecd8eee2a20ba7e9fcfe291
1 ##===-- cui.py -----------------------------------------------*- Python -*-===##
2 ##
3 # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 # See https://llvm.org/LICENSE.txt for license information.
5 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 ##
7 ##===----------------------------------------------------------------------===##
9 import curses
10 import curses.ascii
11 import threading
14 class CursesWin(object):
16 def __init__(self, x, y, w, h):
17 self.win = curses.newwin(h, w, y, x)
18 self.focus = False
20 def setFocus(self, focus):
21 self.focus = focus
23 def getFocus(self):
24 return self.focus
26 def canFocus(self):
27 return True
29 def handleEvent(self, event):
30 return
32 def draw(self):
33 return
36 class TextWin(CursesWin):
38 def __init__(self, x, y, w):
39 super(TextWin, self).__init__(x, y, w, 1)
40 self.win.bkgd(curses.color_pair(1))
41 self.text = ''
42 self.reverse = False
44 def canFocus(self):
45 return False
47 def draw(self):
48 w = self.win.getmaxyx()[1]
49 text = self.text
50 if len(text) > w:
51 #trunc_length = len(text) - w
52 text = text[-w + 1:]
53 if self.reverse:
54 self.win.addstr(0, 0, text, curses.A_REVERSE)
55 else:
56 self.win.addstr(0, 0, text)
57 self.win.noutrefresh()
59 def setReverse(self, reverse):
60 self.reverse = reverse
62 def setText(self, text):
63 self.text = text
66 class TitledWin(CursesWin):
68 def __init__(self, x, y, w, h, title):
69 super(TitledWin, self).__init__(x, y + 1, w, h - 1)
70 self.title = title
71 self.title_win = TextWin(x, y, w)
72 self.title_win.setText(title)
73 self.draw()
75 def setTitle(self, title):
76 self.title_win.setText(title)
78 def draw(self):
79 self.title_win.setReverse(self.getFocus())
80 self.title_win.draw()
81 self.win.noutrefresh()
84 class ListWin(CursesWin):
86 def __init__(self, x, y, w, h):
87 super(ListWin, self).__init__(x, y, w, h)
88 self.items = []
89 self.selected = 0
90 self.first_drawn = 0
91 self.win.leaveok(True)
93 def draw(self):
94 if len(self.items) == 0:
95 self.win.erase()
96 return
98 h, w = self.win.getmaxyx()
100 allLines = []
101 firstSelected = -1
102 lastSelected = -1
103 for i, item in enumerate(self.items):
104 lines = self.items[i].split('\n')
105 lines = lines if lines[len(lines) - 1] != '' else lines[:-1]
106 if len(lines) == 0:
107 lines = ['']
109 if i == self.getSelected():
110 firstSelected = len(allLines)
111 allLines.extend(lines)
112 if i == self.selected:
113 lastSelected = len(allLines) - 1
115 if firstSelected < self.first_drawn:
116 self.first_drawn = firstSelected
117 elif lastSelected >= self.first_drawn + h:
118 self.first_drawn = lastSelected - h + 1
120 self.win.erase()
122 begin = self.first_drawn
123 end = begin + h
125 y = 0
126 for i, line in list(enumerate(allLines))[begin:end]:
127 attr = curses.A_NORMAL
128 if i >= firstSelected and i <= lastSelected:
129 attr = curses.A_REVERSE
130 line = '{0:{width}}'.format(line, width=w - 1)
132 # Ignore the error we get from drawing over the bottom-right char.
133 try:
134 self.win.addstr(y, 0, line[:w], attr)
135 except curses.error:
136 pass
137 y += 1
138 self.win.noutrefresh()
140 def getSelected(self):
141 if self.items:
142 return self.selected
143 return -1
145 def setSelected(self, selected):
146 self.selected = selected
147 if self.selected < 0:
148 self.selected = 0
149 elif self.selected >= len(self.items):
150 self.selected = len(self.items) - 1
152 def handleEvent(self, event):
153 if isinstance(event, int):
154 if len(self.items) > 0:
155 if event == curses.KEY_UP:
156 self.setSelected(self.selected - 1)
157 if event == curses.KEY_DOWN:
158 self.setSelected(self.selected + 1)
159 if event == curses.ascii.NL:
160 self.handleSelect(self.selected)
162 def addItem(self, item):
163 self.items.append(item)
165 def clearItems(self):
166 self.items = []
168 def handleSelect(self, index):
169 return
172 class InputHandler(threading.Thread):
174 def __init__(self, screen, queue):
175 super(InputHandler, self).__init__()
176 self.screen = screen
177 self.queue = queue
179 def run(self):
180 while True:
181 c = self.screen.getch()
182 self.queue.put(c)
185 class CursesUI(object):
186 """ Responsible for updating the console UI with curses. """
188 def __init__(self, screen, event_queue):
189 self.screen = screen
190 self.event_queue = event_queue
192 curses.start_color()
193 curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE)
194 curses.init_pair(2, curses.COLOR_YELLOW, curses.COLOR_BLACK)
195 curses.init_pair(3, curses.COLOR_RED, curses.COLOR_BLACK)
196 self.screen.bkgd(curses.color_pair(1))
197 self.screen.clear()
199 self.input_handler = InputHandler(self.screen, self.event_queue)
200 self.input_handler.daemon = True
202 self.focus = 0
204 self.screen.refresh()
206 def focusNext(self):
207 self.wins[self.focus].setFocus(False)
208 old = self.focus
209 while True:
210 self.focus += 1
211 if self.focus >= len(self.wins):
212 self.focus = 0
213 if self.wins[self.focus].canFocus():
214 break
215 self.wins[self.focus].setFocus(True)
217 def handleEvent(self, event):
218 if isinstance(event, int):
219 if event == curses.KEY_F3:
220 self.focusNext()
222 def eventLoop(self):
224 self.input_handler.start()
225 self.wins[self.focus].setFocus(True)
227 while True:
228 self.screen.noutrefresh()
230 for i, win in enumerate(self.wins):
231 if i != self.focus:
232 win.draw()
233 # Draw the focused window last so that the cursor shows up.
234 if self.wins:
235 self.wins[self.focus].draw()
236 curses.doupdate() # redraw the physical screen
238 event = self.event_queue.get()
240 for win in self.wins:
241 if isinstance(event, int):
242 if win.getFocus() or not win.canFocus():
243 win.handleEvent(event)
244 else:
245 win.handleEvent(event)
246 self.handleEvent(event)
249 class CursesEditLine(object):
250 """ Embed an 'editline'-compatible prompt inside a CursesWin. """
252 def __init__(self, win, history, enterCallback, tabCompleteCallback):
253 self.win = win
254 self.history = history
255 self.enterCallback = enterCallback
256 self.tabCompleteCallback = tabCompleteCallback
258 self.prompt = ''
259 self.content = ''
260 self.index = 0
261 self.startx = -1
262 self.starty = -1
264 def draw(self, prompt=None):
265 if not prompt:
266 prompt = self.prompt
267 (h, w) = self.win.getmaxyx()
268 if (len(prompt) + len(self.content)) / w + self.starty >= h - 1:
269 self.win.scroll(1)
270 self.starty -= 1
271 if self.starty < 0:
272 raise RuntimeError('Input too long; aborting')
273 (y, x) = (self.starty, self.startx)
275 self.win.move(y, x)
276 self.win.clrtobot()
277 self.win.addstr(y, x, prompt)
278 remain = self.content
279 self.win.addstr(remain[:w - len(prompt)])
280 remain = remain[w - len(prompt):]
281 while remain != '':
282 y += 1
283 self.win.addstr(y, 0, remain[:w])
284 remain = remain[w:]
286 length = self.index + len(prompt)
287 self.win.move(self.starty + length / w, length % w)
289 def showPrompt(self, y, x, prompt=None):
290 self.content = ''
291 self.index = 0
292 self.startx = x
293 self.starty = y
294 self.draw(prompt)
296 def handleEvent(self, event):
297 if not isinstance(event, int):
298 return # not handled
299 key = event
301 if self.startx == -1:
302 raise RuntimeError('Trying to handle input without prompt')
304 if key == curses.ascii.NL:
305 self.enterCallback(self.content)
306 elif key == curses.ascii.TAB:
307 self.tabCompleteCallback(self.content)
308 elif curses.ascii.isprint(key):
309 self.content = self.content[:self.index] + \
310 chr(key) + self.content[self.index:]
311 self.index += 1
312 elif key == curses.KEY_BACKSPACE or key == curses.ascii.BS:
313 if self.index > 0:
314 self.index -= 1
315 self.content = self.content[
316 :self.index] + self.content[self.index + 1:]
317 elif key == curses.KEY_DC or key == curses.ascii.DEL or key == curses.ascii.EOT:
318 self.content = self.content[
319 :self.index] + self.content[self.index + 1:]
320 elif key == curses.ascii.VT: # CTRL-K
321 self.content = self.content[:self.index]
322 elif key == curses.KEY_LEFT or key == curses.ascii.STX: # left or CTRL-B
323 if self.index > 0:
324 self.index -= 1
325 elif key == curses.KEY_RIGHT or key == curses.ascii.ACK: # right or CTRL-F
326 if self.index < len(self.content):
327 self.index += 1
328 elif key == curses.ascii.SOH: # CTRL-A
329 self.index = 0
330 elif key == curses.ascii.ENQ: # CTRL-E
331 self.index = len(self.content)
332 elif key == curses.KEY_UP or key == curses.ascii.DLE: # up or CTRL-P
333 self.content = self.history.previous(self.content)
334 self.index = len(self.content)
335 elif key == curses.KEY_DOWN or key == curses.ascii.SO: # down or CTRL-N
336 self.content = self.history.next()
337 self.index = len(self.content)
338 self.draw()