Re: GUI thoughts
John Swensen <[email protected]> Sat, 11 Mar 2006 11:54:28 -0700
| Newsgroups | gmane.comp.gnu.octave.graphics |
|---|---|
| Message-ID | <[email protected]> |
A couple of weeks ago, I sent out a server.h file that had a framework=20 for a singleton class that would contain all the data to be transmitted=20 between a GUI and octave. It contains a mutex to protect access to the=20 data. Once the data has been updated, the UI is sent a signal to=20 indicate new data is available. I have a little bit of the class implemented (e.g. the variable info and=20 the history list), but once it gets more mature, I'll let you know. -John P.S. jwe, thanks for pointing me to the VTE patch. I have modified my=20 GTK GUI to use it and now the UI and octave both show up as the same=20 process AND share a memory space (so I can access the server class I=20 mentioned above directly, rather than writing a pipe or socket server on=20 top of it). As of about a month ago, VTE under cygwin is broken. It=20 has somethign to do with changes to cygwin itself and how it handles=20 PTYs. I was going to make a preliminary release of my UI, but now I am=20 going to wait until cygwin VTE/PTYs is fixed. Sebastien Loisel wrote: > John, > > I just returned from Italy and I've started digging around for your=20 > method of communicating with Octave. It looks as if openpty does not=20 > exist in MinGW, so I won't be able to use it. I don't know if there's=20 > going to be an issue with the DLL's fd 1 being different from the=20 > EXE's fd 1, I'll see when I try it. I think MinGW has pipes, I don't=20 > know what having a PTY instead of a pipe for Octave will do, do you=20 > suppose that's ok? > > From time to time, the GUI wants to go and pry in Octave's innards.=20 > While ideally Octave would be thread-safe, for now it would be nice to=20 > be backward compatible and not rely on this. One simple workaround is=20 > to only pry into Octave's innards when it is safe to do so. > > One way of doing this would be to have Octave call a function before=20 > each prompt. From within this function, I could dump all the info from=20 > Octave into my GUI. Another method would be to be to read a variable=20 > or call a function which would tell me whether Octave is running code=20 > or waiting for input. Or, if Octave has an idle() function (like GLUT)=20 > which it calls whenever it has some spare cycles, I could hook into=20 > that to update the GUI. Are there backwards-compatible ways of doing=20 > this? > > Cheers, > > S=E9bastien Loisel > > On 2/28/06, *John W. Eaton* <[email protected]=20 > <mailto:[email protected]>> wrote: > > I have some thoughts and questions about how we would like a GUI fo= r > Octave to work. Mostly, they are about how the GUI and Octave shou= ld > 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 f= ew > things like handling the diary, automatic function updating based o= n > 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 interact= ive > Octave session (for one thing, embedding Octave this way turns off > the > internal flag that tells it that it is running in an interactive > session). You could probably work around that too, but I think the= re > must be a better way. > > Also, if you are embedding Octave with eval_string, I think your GU= I > will be unresponsive while Octave is performing computations. Unle= ss > you are running Octave in a separate thread, I think you have to wa= it > until eval_string returns to continue with the GUI operations. If = you > are running Octave in a separate thread and then having the GUI tou= ch > 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 to > 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 wi= ll > 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 = for > 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 = the > GUI and Octave have to pass some extra information around. I'm sur= e > that this can be done, but it will mean some extra effort. Will yo= u > 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 t= he > eval_string approach) and a pty implementation. The idea is that > instead of spawning a subprocess, you set up some ptys, dup some fi= le > descriptors, start Octave in a separate thread, then attach the > master > 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 relie= s > 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 pre= fer > 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 ju= st > 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 i= s a > bit tricky since you need to first patch the VTE sources and build > the > 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 th= e > necessary changes rolled into the default vte (or other) terminal > widget(s). If not, then I suppose we could provide our own termina= l > 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 function= s > in the vte library. > > This function was written by Eric Smith <[email protected] > <mailto:[email protected]>>, > archived here: > > =20 > http://www.brouhaha.com/~eric/software/vte/vte-0.11.10-add-pty.patc= h > <http://www.brouhaha.com/%7Eeric/software/vte/vte-0.11.10-add-pty.p= atch> > > as a patch and was submitted to the Gnome project here: > > http://bugzilla.gnome.org/show_bug.cgi?id=3D135230 > <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 poin= t > is to demonstrate using the terminal widget to embed an interactive > application without using a coprocess. The next step would be to a= dd > 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; > } > >