From graphics-request at octave dot org Sat Mar 11 12:55:46 2006 Subject: Re: GUI thoughts From: John Swensen To: Sebastien Loisel CC: "John W. Eaton" , octave graphics and gui mailing list Date: Sat, 11 Mar 2006 11:54:28 -0700 A couple of weeks ago, I sent out a server.h file that had a framework for a singleton class that would contain all the data to be transmitted between a GUI and octave. It contains a mutex to protect access to the data. Once the data has been updated, the UI is sent a signal to indicate new data is available. I have a little bit of the class implemented (e.g. the variable info and 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 GTK GUI to use it and now the UI and octave both show up as the same process AND share a memory space (so I can access the server class I mentioned above directly, rather than writing a pipe or socket server on top of it). As of about a month ago, VTE under cygwin is broken. It has somethign to do with changes to cygwin itself and how it handles PTYs. I was going to make a preliminary release of my UI, but now I am 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 > method 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 be 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 instead 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. > While ideally Octave would be thread-safe, for now it would be nice to > be backward compatible and not rely on this. One simple workaround is > 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 > each prompt. From within this function, I could dump all the info from > Octave 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? > > Cheers, > > Sébastien Loisel > > On 2/28/06, *John W. Eaton* > 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 interactive > 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 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 you > 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 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 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 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 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 > 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 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 prefer > 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 > 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 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 >, > 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=135230 > > > (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 = GTK_WIDGET(terminal); > > if (terminal->pvt->pty_master != -1) { > _vte_pty_close(terminal->pvt->pty_master); > close(terminal->pvt->pty_master); > } > terminal->pvt->pty_master = 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=/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 > #include > #include > #include > #include > #include > #include > > #include > #include > > void > execute_line (char *line) > { > static char *err_hilite_on = "\e[01;31m"; > static char *err_hilite_off = "\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 = "FileMan"; > > /* Loop reading and executing lines until the user quits. */ > while (1) > { > char *line = 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 = gtk_window_new (GTK_WINDOW_TOPLEVEL); > > terminal = 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; > } > >