Re: GUI thoughts

"Shai Ayal" <[email protected]> Sat, 11 Mar 2006 20:32:05 +0200
Newsgroups gmane.comp.gnu.octave.graphics
Message-ID <[email protected]>
On 3/11/06, Sebastien Loisel <[email protected]> wrote:
> John,
>
> I just returned from Italy and I've started digging around for your met=
hod
> of communicating with Octave. It looks as if openpty does not exist in
> MinGW, so I won't be able to use it. I don't know if there's going to b=
e an
> issue with the DLL's fd 1 being different from the EXE's fd 1, I'll see=
 when
> I try it. I think MinGW has pipes, I don't know what having a PTY inste=
ad of
> a pipe for Octave will do, do you suppose that's ok?
>
> From time to time, the GUI wants to go and pry in Octave's innards. Whi=
le
> ideally Octave would be thread-safe, for now it would be nice to be bac=
kward
> compatible and not rely on this. One simple workaround is to only pry i=
nto
> Octave's innards when it is safe to do so.
>
> One way of doing this would be to have Octave call a function before ea=
ch
> prompt. From within this function, I could dump all the info from Octav=
e
> into my GUI. Another method would be to be to read a variable or call a
> function which would tell me whether Octave is running code or waiting =
for
> input. Or, if Octave has an idle() function (like GLUT) which it calls
> whenever it has some spare cycles, I could hook into that to update the=
 GUI.
> Are there backwards-compatible ways of doing this?

I think you might be looking for

input_event_hook

however, (John, correct me if I'm wrong) I think it is dependant on
readline, which I recall you don't use in workshop

Shai


> Cheers,
>
> S=E9bastien Loisel
>
>
> On 2/28/06, John W. Eaton <[email protected]> wrote:
> > I have some thoughts and questions about how we would like a GUI for
> > Octave to work.  Mostly, they are about how the GUI and Octave should
> > be linked together, who should be in charge of events, etc.
> >
> > In Sebastien's Octave Workshop, I think the GUI handles all events
> > (correct me if I'm wrong).  Octave is embedded in the GUI and gets
> > input via Octave's eval_string function.  Output is captured by
> > grabbing std::cout in some way.  This sort of works, but misses a few
> > things like handling the diary, automatic function updating based on
> > the last prompt time, the actual readline library for command-line
> > editing, etc.  If you want to stick with eval_string, then it would
> > still be possible to handle the all these things, but you have to
> > duplicate a lot of functionality that is already in Octave.  The
> > eval_string function was not really meant for embedding an interactiv=
e
> > Octave session (for one thing, embedding Octave this way turns off th=
e
> > internal flag that tells it that it is running in an interactive
> > session).  You could probably work around that too, but I think there
> > must be a better way.
> >
> > Also, if you are embedding Octave with eval_string, I think your GUI
> > will be unresponsive while Octave is performing computations.  Unless
> > you are running Octave in a separate thread, I think you have to wait
> > until eval_string returns to continue with the GUI operations.  If yo=
u
> > are running Octave in a separate thread and then having the GUI touch
> > Octave internals while eval_string is running, you will likely have
> > unexpected results since Octave is not currently thread safe (there
> > is no mechanism to prevent global data from being modified at the
> > wrong time).
> >
> > To avoid threads, you could start Octave as a coprocess to your GUI,
> > but you will still need some modifications to Octave so that it can
> > process requests while it is off running user code.  You won't want t=
o
> > use a pipe for the communication though, because that will not be
> > recognized as tty, so the interactive features won't work properly.
> > Opening a pty connection to a separate process is better (Octave will
> > see the pty as a tty, so it will run in interactive mode).
> >
> > There is a terminal widget for GTK (VTE) that would make it easy to
> > start Octave as a separate process and communicate with it using a
> > pty.  It provides a terminal window that is (more than) sufficient fo=
r
> > running Octave.  Running Octave this way, you get readline, Octave
> > thinks it is interactive, diary works, etc.  Perhaps there is
> > something similar for Qt?
> >
> > The difficulty with this approach is that if you want the GUI to do
> > more than you can get by running Octave in a terminal window, then th=
e
> > GUI and Octave have to pass some extra information around.  I'm sure
> > that this can be done, but it will mean some extra effort.  Will you
> > parse text data or design a protocol for passing binary data?  Will
> > Octave or the GUI be in charge of that, and how much overhead will it
> > add?  In terms of passing data back and forth, it would be much
> > simpler if the GUI and Octave were in the same process and the GUI
> > could just call internal Octave functions to do its thing.
> >
> > At least with GTK on Unixy systems, it should be possible to embed
> > Octave in a terminal widget and make Octave think it is talking to a
> > tty without having to run Octave as a separate process.  To do this,
> > you will need a thread-safe Octave (but you need that anyway with the
> > eval_string approach) and a pty implementation.  The idea is that
> > instead of spawning a subprocess, you set up some ptys, dup some file
> > descriptors, start Octave in a separate thread, then attach the maste=
r
> > pty to the terminal widget.
> >
> > Unfortunately, the VTE widget will not allow this mode of operation
> > out of the box.  Fixing it requires the addition of one small
> > function, but it must be added to the core library because it relies
> > on some private internal data to work.
> >
> > So, some questions and things to think about.
> >
> >   * Should we work to make Octave thread-safe?  I think I would prefe=
r
> >     to work on this instead of a protocol that could be used to
> >     communicate with a separate Octave process.
> >
> >   * Is there a terminal widget for Qt that could do the same thing as
> >     VTE?  I don't really want a GUI for Octave to be useful with just
> >     one toolkit.
> >
> >   * Would any of this work in a Windows environment?
> >
> > I'm appending a simple example program that illustrates this.  It
> > depends on the modified VTE terminal widget and GTK.  Building it is =
a
> > bit tricky since you need to first patch the VTE sources and build th=
e
> > modified VTE library.  Then you need to ensure that you use that
> > library for building and running the example program.
> >
> > I'm assuming that if we choose this sort of approach, we can get the
> > necessary changes rolled into the default vte (or other) terminal
> > widget(s).  If not, then I suppose we could provide our own terminal
> > widget with Octave (though that would definitely not be my
> > preference).
> >
> > jwe
> >
> >
> > First, here is the function that must be added to vte:
> >
> > /* We need the following additional function for vte:
> >
> >      int vte_terminal_set_pty(VteTerminal *terminal, int
> pty_master);
> >
> >    It must be added to vte because it calls static private functions
> >    in the vte library.
> >
> >    This function was written by Eric Smith <[email protected]>,
> >    archived here:
> >
> >
> http://www.brouhaha.com/~eric/software/vte/vte-0.11.10-add-pty.patch
> >
> >    as a patch and was submitted to the Gnome project here:
> >
> >      http://bugzilla.gnome.org/show_bug.cgi?id=3D135230
> >
> >    (that was Feb 2004, and still not accepted).
> > */
> >
> > int
> > vte_terminal_set_pty(VteTerminal *terminal, int
> pty_master)
> > {
> >         GtkWidget *widget;
> >
> >         g_return_val_if_fail(VTE_IS_TERMINAL(terminal),
> -1);
> >         widget =3D GTK_WIDGET(terminal);
> >
> >         if (terminal->pvt->pty_master !=3D -1) {
> >
> _vte_pty_close(terminal->pvt->pty_master);
> >                 close(terminal->pvt->pty_master);
> >         }
> >         terminal->pvt->pty_master =3D pty_master;
> >
> >         /* Open channels to listen for input on. */
> >         xvte_terminal_connect_pty_read(terminal);
> >
> >         /* Open channels to write output to. */
> >         xvte_terminal_connect_pty_write(terminal);
> > }
> >
> > Now the main program.  This is just a much-simplified version of an
> > example program that is distributed with readline.  I installed the
> > modified vte library in /home/jwe/vte, so I compile the example
> > program with the following command
> >
> >   gcc -I/home/jwe/vte/include -g $(pkg-config gtk+-2.0 --cflags) \
> >     example.c -o example \
> >     -L/home/jwe/vte/lib -lvte $(pkg-config gtk+-2.0 --libs) \
> >     -lreadline -lpthread -lutil
> >
> > and run it with
> >
> >   LD_LIBRARY_PATH=3D/home/jwe/vte/lib ./example
> >
> > This example is extremely simple and does almost nothing.  The point
> > is to demonstrate using the terminal widget to embed an interactive
> > application without using a coprocess.  The next step would be to add
> > a button or two that modifies some global data and protect the
> > accesses to the global data as needed.  Add in some signal handling
> > and I think that would demonstrate most of what Octave does.
> >
> > #include <stdio.h>
> > #include <sys/types.h>
> > #include <unistd.h>
> > #include <gtk/gtk.h>
> > #include <vte/vte.h>
> > #include <pthread.h>
> > #include <pty.h>
> >
> > #include <readline/readline.h>
> > #include <readline/history.h>
> >
> > void
> > execute_line (char *line)
> > {
> >   static char *err_hilite_on =3D "\e[01;31m";
> >   static char *err_hilite_off =3D "\e[0m";
> >
> >   if (! strcmp (line, "ls"))
> >     system ("ls -FClg");
> >   else if (! strcmp (line, "quit"))
> >     exit (0);
> >   else
> >     fprintf (stderr, "%sunknown command `%s'%s\n",
> >              err_hilite_on, line, err_hilite_off);
> > }
> >
> > void *
> > fileman_main (void *dummy)
> > {
> >   rl_readline_name =3D "FileMan";
> >
> >   /* Loop reading and executing lines until the user quits. */
> >   while (1)
> >     {
> >       char *line =3D readline ("FileMan: ");
> >
> >       if (! line)
> >         break;
> >
> >       if (*line)
> >         {
> >           add_history (line);
> >           execute_line (line);
> >         }
> >       else
> >         free (line);
> >     }
> >
> >   return 0;
> > }
> >
> > int
> > main (int argc, char **argv)
> > {
> > #ifndef FORK_COMMAND
> >   pthread_t fileman_thread;
> >
> >   int fdm;
> >   int fds;
> > #endif
> >
> >   /* GtkWidget is the storage type for widgets */
> >   GtkWidget *window;
> >   GtkWidget *terminal;
> >
> >   /* This is called in all GTK applications. Arguments are parsed
> >    * from the command line and are returned to the application. */
> >   gtk_init (&argc, &argv);
> >
> >   /* create a new window */
> >   window =3D gtk_window_new (GTK_WINDOW_TOPLEVEL);
> >
> >   terminal =3D vte_terminal_new ();
> >
> > #ifdef FORK_COMMAND
> >   vte_terminal_fork_command (VTE_TERMINAL (terminal),
> >                              "/usr/bin/octave", 0, 0, 0, 0, 0, 0);
> > #else
> >   if (openpty (&fdm, &fds, 0, 0, 0) < 0)
> >     fprintf (stderr, "oops!\n");
> >
> >   dup2 (fds, 0);
> >   dup2 (fds, 1);
> >   dup2 (fds, 2);
> >
> >   pthread_create (&fileman_thread, 0, fileman_main, 0);
> >
> >   vte_terminal_set_pty (VTE_TERMINAL (terminal), fdm);
> > #endif
> >
> >   /* This packs the terminal into the window (a gtk container). */
> >   gtk_container_add (GTK_CONTAINER (window), terminal);
> >
> >   vte_terminal_set_font_from_string (VTE_TERMINAL
> (terminal), "Fixed 11");
> >
> >   vte_terminal_set_size (VTE_TERMINAL (terminal), 80, 24);
> >
> >   vte_terminal_set_scrollback_lines (VTE_TERMINAL
> (terminal), 1024);
> >
> >   /* The final step is to display this newly created widget. */
> >   gtk_widget_show (terminal);
> >
> >   /* and the window */
> >   gtk_widget_show (window);
> >
> >   /* All GTK applications must have a gtk_main(). Control ends here
> >    * and waits for an event to occur (like a key press or
> >    * mouse event). */
> >   gtk_main ();
> >
> >   return 0;
> > }
> >
> >
>
>