view NOTES @ 19:9e2211f5f293 default tip

NOTES: more updates
author John W. Eaton <jwe@octave.org>
date Tue, 28 May 2019 23:06:08 -0400
parents 7e93b74bd6c2
children
line wrap: on
line source

To build:

  qmake calc.pro
  make

Current terminal widget:

  * Run Octave in a separate thread.  On Unixy systems, interact
    through pty.  On Windows systems, use hidden console and mirror
    contents in a Qt widget.

    The Octave interpreter sees input directly, same as for normal
    terminal application.  This approach is good, because no change in
    the way Octave accepts and parses input is necessary.  But it is
    bad because GUI is not in control of input and must arrange to
    communicate across threads.

    The terminal window can (in theory) be used to run other
    applications started from the system command (for example).  This
    feature works well enough for us to use "less" running as a
    separate process to handle paging output, but it doesn't work for
    all applications.  For example, I am not able to run editors like
    Emacs or vi in the GUI terminal window, even on Unixy systems.

    To make "less" work as the pager on Unixy systems, the Octave
    process must fork, call setsid in the child to detach from the
    controlling terminal, and then exec the GUI.

    The GUI must deal with threads when communicating with the
    interpreter.

Proposal:

  * Create a new GUI terminal widget that is in control of input.

    Use the callback interface for readline to handle command line
    editing.  As the internal line changes, it is the job of the new
    GUI terminal widget to redisplay the updated text of the line that
    readline is used to edit.

    Use a push parser to parse lines as they become available.  The
    push parser will return the status of the parse as either complete
    or needing more input.

    The GUI terminal widget uses the following readline callbacks to
    manage input and update the display.  We may need more in the
    future to handle other features.

      rl_getc_function
      rl_redisplay_function
      rl_completion_display_matches_hook

      rl_prep_term_function (do nothing)
      rl_deprep_term_function (do nothing)

   Call rl_callback_handler_install to setup readline's callback mode.
   This function sets the initial prompt and provides a pointer to a
   function that readline should call when a complete line of input is
   available.

   Call rl_callback_read_char when a character is available for
   readline to handle.  We currently store the character to read in a
   global value, then rl_get_function returns that character to
   readline.

   Call rl_callback_handler_remove when shutting down.

   To avoid global variables, it would be helpful if we could use
   lambda functions/functors as callbacks instead of plain C function
   pointers.

Questions:

  * With this arrangement, would the interpreter have to run in a
    separate thread?  As the example shows, it's not absolutely
    necessary.  It could offer some advantages, but only if it is
    possible for the GUI to do useful things while the interpreter is
    busy.

  * If the interpreter is not running in a separate thread but but the
    graphics engine is, then what happens to graphics callbacks when the
    parser has partially evaluated an expression?  Or is this not an
    issue because separate parsers can be used even if there is only one
    evaluator?  Could it ultimately be possible to have the evaluator
    running in multiple threads?

  * If the interpreter does run in a separate thread, we still must
    wait for it to calculate and return a result so we can synchronize
    input and output.  Otherwise, we may print the next prompt before
    the output from the previous expression is evaluated and printed.
    You'll see this behavior if you build the example program with
    CALC_USE_INTERPRETER_THREAD defined.

  * The example parser currently also performs evaluations and
    computes results immediately so it doesn't properly handle
    expression lists that are split across multiple lines.  Octave
    wouldn't have this problem because we already build a parse tree
    then execute it once it is complete.

  * Do we need text position markers to keep track of the prompt position
    (beginning of current line) when inserting or clearing text?  This
    doesn't seem necessary in the current example, but it doesn't have
    functions that can clear the screen or otherwise redraw prior output
    that would cause the position of the cursor in the window to change.

  * The example program doesn't attempt handle multi-line prompts or
    prompts with invisible characters (color specifications, for
    example).  Fixing that will make the redisplay function
    significantly more complex.  See, for example, how complicated the
    default rl_redisplay function is in the readline library.  Unless
    we actually write a terminal emulator (like the current terminal
    widgets) then it is not possible to use readline's rl_redisplay
    function directly.

  * We'll need to implement a pager ourselves, since "less" won't work
    in this simplified terminal widget.

  * The system function may need to be modified so that external
    programs that expect to be running in a terminal will continue to
    work properly.  On Unixy systems, this job can be done with ptys.
    I guess Windows systems can use a hidden console?  But if these
    things are required, are we more or less back to were we were
    before since we used a pty and hidden console to implement the
    terminal widgets?  I believe the Emacs start-process function must
    do similar things, so we might be able to reuse that code.

  * If readline runs in the terminal widget, who owns the command-line
    history?  Either way, if the GUI is in control of keyboard input,
    it will need access to the history list and Octave will also need
    access for the history functions.

readline callback functions:

// -- Variable: Re_getc_func_t * rl_getc_function
//     If non-zero, Readline will call indirectly through this pointer to
//     get a character from the input stream.  By default, it is set to
//     `rl_getc', the default Readline character input function (*note
//     Character Input::).

// -- Variable: rl_voidfunc_t * rl_redisplay_function
//     If non-zero, Readline will call indirectly through this pointer to
//     update the display with the current contents of the editing buffer.
//     By default, it is set to `rl_redisplay', the default Readline
//     redisplay function (*note Redisplay::).

// -- Variable: rl_vintfunc_t * rl_prep_term_function
//     If non-zero, Readline will call indirectly through this pointer to
//     initialize the terminal.  The function takes a single argument, an
//     `int' flag that says whether or not to use eight-bit characters.
//     By default, this is set to `rl_prep_terminal' (*note Terminal
//     Management::).

// -- Variable: rl_voidfunc_t * rl_deprep_term_function
//     If non-zero, Readline will call indirectly through this pointer to
//     reset the terminal.  This function should undo the effects of
//     `rl_prep_term_function'.  By default, this is set to
//     `rl_deprep_terminal' (*note Terminal Management::).

// -- Function: void rl_callback_handler_install (const char *prompt,
//          rl_vcpfunc_t *lhandler)
//     Set up the terminal for readline I/O and display the initial
//     expanded value of PROMPT.  Save the value of LHANDLER to use as a
//     function to call when a complete line of input has been entered.
//     The function takes the text of the line as an argument.

// -- Function: void rl_callback_read_char (void)
//     Whenever an application determines that keyboard input is
//     available, it should call `rl_callback_read_char()', which will
//     read the next character from the current input source.  If that
//     character completes the line, `rl_callback_read_char' will invoke
//     the LHANDLER function saved by `rl_callback_handler_install' to
//     process the line.  Before calling the LHANDLER function, the
//     terminal settings are reset to the values they had before calling
//     `rl_callback_handler_install'.  If the LHANDLER function returns,
//     the terminal settings are modified for Readline's use again.
//     `EOF' is  indicated by calling LHANDLER with a `NULL' line.

// -- Function: void rl_callback_handler_remove (void)
//     Restore the terminal to its initial state and remove the line
//     handler.  This may be called from within a callback as well as
//     independently.  If the LHANDLER installed by
//     `rl_callback_handler_install' does not exit the program, either
//     this function or the function referred to by the value of
//     `rl_deprep_term_function' should be called before the program
//     exits to reset the terminal settings.

// -- Variable: rl_compdisp_func_t * rl_completion_display_matches_hook
//     If non-zero, then this is the address of a function to call when
//     completing a word would normally display the list of possible
//     matches.  This function is called in lieu of Readline displaying
//     the list.  It takes three arguments: (`char **'MATCHES, `int'
//     NUM_MATCHES, `int' MAX_LENGTH) where MATCHES is the array of
//     matching strings, NUM_MATCHES is the number of strings in that
//     array, and MAX_LENGTH is the length of the longest string in that
//     array.  Readline provides a convenience function,
//     `rl_display_match_list', that takes care of doing the display to
//     Readline's output stream.  That function may be called from this
//     hook.