source: trunk/FACT++/src/ReadlineWindow.cc@ 20011

Last change on this file since 20011 was 16870, checked in by tbretz, 11 years ago
When we output something ourselves before the event hook is called in readline, we have to signal readline that we have started a new row, so that readline can properly update the prompt. I hope this finally solves the flickering issue on my laptop and/or the problematic display in the fadctrl.
File size: 10.4 KB
Line 
1// **************************************************************************
2/** @class ReadlineWindow
3
4@brief Helper to redirect readline's in- and output to an ncurses window
5
6The idea of this class is to allow the use of the readline functionality
7within a ncurses window. Therefore, several callback and hook function
8of the readline libarary are redirected, which allow to redirect the
9window's input stream to readline and the readline output to the
10window.
11
12After instantisation a pointer to a ncurses WINDOW must be given to the
13object to 'bind' readline to this window.
14
15In addition the color of the readline prompt can be set using the
16SetPromptColor() member function. Note that the given color must
17be the index of a COLOR_PAIR. For details on this see the ncurses
18documentation
19
20If ncurses will use more than one window on the screen it might
21be necessary to redraw the screen before the cursor is displayed.
22Therefore, the Refresh() function must be overwritten. It is called
23before the cursor is put to its final location in the readline line
24and after all update to the screen was performed.
25
26Refresh() can be used to force a redisplay of the current input line
27from a derived class. This might be necessary after changes top the
28screen or window size.
29
30@section References
31
32 - <A HREF="http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html">GNU Readline</A>
33 - <A HREF="http://www.gnu.org/software/ncurses">GNU Ncurses</A>
34
35**/
36// **************************************************************************
37#include "Shell.h"
38
39#include <sstream>
40#include <iostream>
41#include <string.h> // strlen
42
43#include "tools.h"
44
45using namespace std;
46
47// --------------------------------------------------------------------------
48//
49//! Propagate prgname to the Readline constructor.
50//! Initialize fPromtX and fPromtY to 0.
51//! Set fWindow to 0.
52//!
53//! @param prgname
54//! see Readline::Readline()
55//!
56//! @todo
57//! Maybe we should add sanity check for fWindow==0 in the functions?
58//!
59ReadlineWindow::ReadlineWindow(const char *prgname) : Readline(prgname),
60 fWindow(0), fPromptX(0), fPromptY(0)
61{
62}
63
64// --------------------------------------------------------------------------
65//
66//! Set the window in which the readline prompt and all readline output
67//! is displayed. Sets the readline screen size (rl_set_screen_size)
68//! accoring to the size of the window.
69//!
70//! Setup all necessry callback functions. To redirect readline input
71//! and output properly.
72//!
73//! @param w
74//! Pointer to a ncurses WINDOW.
75//
76void ReadlineWindow::SetWindow(WINDOW *w)
77{
78 if (!w)
79 return;
80
81 // Get size of the window
82 int width, height;
83 getmaxyx(w, height, width);
84
85 // Propagate the size to the readline library
86 Resize(width, height);
87
88 // Finally set the pointer to the panel in which we are supposed to
89 // operate
90 fWindow = w;
91}
92
93
94// --------------------------------------------------------------------------
95//
96//! Callback function for readlines rl_getc_function. Apart from redirecting
97//! the input from the window to the readline libarary, it also checks if
98//! the input forced scrolling of the window contents and in this case
99//! adapt fPromptY.
100//!
101//! @return
102//! the character read by wgetch
103//
104/*
105int ReadlineWindow::Getc(FILE *)
106{
107 /// ==== It seems this is obsolete because we will get teh scrolling
108 // from the continous call to event hook anyway
109
110 // Get size of the window
111 int lines, cols;
112 getmaxyx(fWindow, lines, cols);
113
114 // Get current cursor position
115 int x0, y0, y1, x1;
116 getyx(fWindow, y0, x0);
117
118 // Read a character from stream in window
119 const int c = wgetch(fWindow);
120
121 // Get new cursor position
122 getyx(fWindow, y1, x1);
123
124 // Find out whether the last character initiated a scroll
125 if (y0==lines-1 && y1==lines-1 && x1==0 && x0==cols-1)
126 fPromptY--;
127
128 // return character
129 return c;
130}
131*/
132
133// --------------------------------------------------------------------------
134//
135//! Store the current cursor position in fPromptX/Y
136//
137void ReadlineWindow::Startup()
138{
139 getyx(fWindow, fPromptY, fPromptX);
140}
141
142// --------------------------------------------------------------------------
143//
144//! Move the cursor to the position stored in fPromptX/Y which should
145//! correspond to the beginning of the output line
146//
147void ReadlineWindow::RewindCursor() const
148{
149 wmove(fWindow, fPromptY, fPromptX);
150}
151
152// --------------------------------------------------------------------------
153//
154//! The event hook which is called regularly when a readline call is in
155//! progress. We use this to synchronously upadte our prompt (mainly
156//! the current cursor position) and refresh the screen, so that all
157//! changes get displayed soon.
158//!
159//! By default, this will be called at most ten times a second if there
160//! is no keyboard input.
161//
162void ReadlineWindow::EventHook(bool)
163{
164 Readline::EventHook();
165 Redisplay();
166 /*
167 * This doesn't work if the contents of the line changes, e.g. when
168 * the prompt is replaced
169
170 // Refresh the screen
171 Refresh();
172
173 // Now move the cursor to its expected position
174 int lines, cols;
175 getmaxyx(fWindow, lines, cols);
176
177 const int pos = fPromptY*cols + fPromptX + GetAbsCursor();
178 wmove(fWindow, pos/cols, pos%cols);
179
180 // Make the cursor movement visible on the screen
181 wrefresh(fWindow);
182 */
183
184 // The lines above are just a simplified version of Redisplay()
185 // which skips all the output.
186}
187
188// --------------------------------------------------------------------------
189//
190//! This basically implement displaying the whole line, starting with the
191//! prompt and the rl_line_buffer. It also checks if displaying it
192//! results in a scroll of the window and adapt fPromptY accordingly.
193//!
194//! The prompt is displayed in the color defined by fColor.
195//!
196//! Before the cursor position is finally set a screen refresh (Refresh())
197//! is initiated to ensure that nothing afterwards will change the cursor
198//! position. It might be necessary to overwrite this function.
199//!
200//! @todo fix docu
201//
202void ReadlineWindow::Redisplay()
203{
204 // Move to the beginning of the output
205 wmove(fWindow, fPromptY, fPromptX);
206
207 // Get site of the window
208 int lines, cols;
209 getmaxyx(fWindow, lines, cols);
210
211 const string prompt = GetPrompt();
212 const string buffer = GetBuffer();
213
214 // Issue promt and redisplay text
215 wattron(fWindow, fColor);
216 wprintw(fWindow, "%s", prompt.c_str());
217 wattroff(fWindow, fColor);
218 wprintw(fWindow, "%s", buffer.c_str());
219
220 // Clear everything after that
221 wclrtobot(fWindow);
222
223 // Calculate absolute position in window or beginning of output
224 int xy = fPromptY*cols + fPromptX;
225
226 // Calculate position of end of prompt
227 xy += prompt.length();
228
229 // Calculate position of cursor and end of output
230 const int cur = xy + GetCursor();
231 const int end = xy + buffer.size();
232
233 // Calculate if the above output scrolled the window and by how many lines
234 int scrolls = end/cols - lines + 1;
235
236 if (scrolls<0)
237 scrolls = 0;
238
239 fPromptY -= scrolls;
240
241 // new position
242 const int px = cur%cols;
243 const int py = scrolls>=1 ? cur/cols - scrolls : cur/cols;
244
245 // Make sure that whatever happens while typing the correct
246 // screen is shown (otherwise the top-panel disappears when
247 // we scroll)
248 Refresh();
249
250 // Move the cursor to the cursor position
251 wmove(fWindow, py, px);
252
253 // Make changes visible on screen
254 wrefresh(fWindow);
255}
256
257// --------------------------------------------------------------------------
258//
259//! Callback function to display the finally compiled list of completion
260//! options. Adapts fPromtY for the number of scrolled lines.
261//!
262//! @param matches
263//! A list with the matches found. The first element contains
264//! what was completed. The length of the list is therefore
265//! num+1.
266//!
267//! @param num
268//! Number of possible completion entries in the list.
269//!
270//! @param max
271//! maximum length of the entries
272//!
273//! @todo
274//! Maybe we can use rl_outstream here if we find a way to redirect
275//! the stream to us instead of a file.
276//
277void ReadlineWindow::CompletionDisplay(char **matches, int num, int max)
278{
279 // Increase maximum size by two to get gaps in between the columns
280 max += 2; // Two whitespaces in between the output
281
282 // Get size of window
283 int lines, cols;
284 getmaxyx(fWindow, lines, cols);
285
286 // Allow an empty space at the end of the lines for a '\n'
287 cols--;
288
289 // calculate the final number columns
290 const int ncols = cols / max;
291
292 // Compile a proper format string
293 ostringstream fmt;
294 fmt << "%-" << max << 's';
295
296 // loop over all entries and display them
297 int l=0;
298 for (int i=0; i<num; i++)
299 {
300 // Check if we have to put a line-break
301 if (i%ncols==0)
302 {
303 if ((max+0)*ncols < cols)
304 wprintw(fWindow, "\n");
305 l++;
306 }
307
308 // Display an entry
309 wprintw(fWindow, fmt.str().c_str(), matches[i+1]);
310 }
311
312 // Display an empty line after the list
313 if ((num-1)%ncols>0)
314 wprintw(fWindow, "\n");
315 wprintw(fWindow, "\n");
316
317 // Get new cursor position
318 int x, y;
319 getyx(fWindow, y, x);
320
321 // Clear everything behind the list
322 wclrtobot(fWindow);
323
324 // Adapt fPromptY for the number of scrolled lines if any.
325 if (y==lines-1)
326 fPromptY = lines-1;
327
328 // Display anything
329 wrefresh(fWindow);
330}
331
332// --------------------------------------------------------------------------
333//
334//! Overwrites Shutdown() of Readline. After Readline::Prompt has
335//! returned a Redisplay() is forced to ensure a proper display of
336//! everything. Finally, the display line is ended by a \n.
337//!
338//! @param buf
339//! The buffer returned by the readline call
340//
341void ReadlineWindow::Shutdown(const char *buf)
342{
343 // Move the cursor to the end of the total line entered by the user
344 // (the user might have pressed return in the middle of the line)...
345 int lines, cols;
346 getmaxyx(fWindow, lines, cols);
347
348 // Calculate absolute position in window or beginning of output
349 // We can't take a pointer to the buffer because rl_end is not
350 // valid anymore at the end of a readline call
351 int xy = fPromptY*cols + fPromptX + GetPrompt().size() + strlen(buf);
352 wmove(fWindow, xy/cols, xy%cols);
353
354 // ...and output a newline. We have to do the manually.
355 wprintw(fWindow, "\n");
356
357 // refresh the screen
358 wrefresh(fWindow);
359
360 // This might have scrolled the window
361 if (xy/cols==lines-1)
362 fPromptY--;
363}
Note: See TracBrowser for help on using the repository browser.