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

Last change on this file was 16870, checked in by tbretz, 7 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.