From simon.urbanek at r-project.org  Wed Jun  1 15:50:52 2011
From: simon.urbanek at r-project.org (Simon Urbanek)
Date: Wed, 1 Jun 2011 09:50:52 -0400
Subject: [Rd] GUI hooks in R [Was: assignInNamespace and new bindings]
In-Reply-To: <201105312323.40199.thomas.friedrichsmeier@ruhr-uni-bochum.de>
References: <4DE4A82E.7060803@r-enthusiasts.com>
	<201105312211.41716.thomas.friedrichsmeier@ruhr-uni-bochum.de>
	<42ECBCC7-B920-444A-9BA4-014B994A0C38@r-project.org>
	<201105312323.40199.thomas.friedrichsmeier@ruhr-uni-bochum.de>
Message-ID: <8B0E89F3-1074-46FC-B80A-3E0111D234D2@r-project.org>


On May 31, 2011, at 5:23 PM, Thomas Friedrichsmeier wrote:

> On Tuesday 31 May 2011, Simon Urbanek wrote:
>> The history entries are somewhat in a grey area, because most GUIs use
>> their own implementation of history (and thus they are irrelevant) and the
>> *history() commands are documented to only use readline-backend. That
>> said, they could be easily used by all GUIs if the Windows code is
>> amended.
> 
> Actually, I would suggest to use a different solution, though: This is not 
> really a problem that needs to be addressed on a low level of pointers to C 
> functions. Perhaps it would make more sense to allow to set some R function(s) 
> to handle these via options() (or perhaps via a dedicated gui.options()).
> 

I suppose, yes, it's possible, but I see somewhat of an asymmetry if done that way : GUIs are like plug-ins in that there is a set of functions they have to implement to work properly. In the current state this is done using the C-level hooks, but they are incomplete in that some of the required hooks are not available on all platforms. However, if you introduce an additional layer of R function hooks, there will be two sets of competing ways for the GUI so register and some of them are simply not feasible on the R level (console handling, for example, which is why we have C-level hooks). It also makes the GUI unnecessary messy, since they will need to provide both C code and R code, where the R code essentially just points to C code, practically replicating what the C-level hooks would do (just more complicated as it requires embedded symbol registrations etc.).

Currently I'm more inclined to make the hooks cross-platform (maybe conditional on the GUI type set to "custom" or something like that). But if someone wants to devise some nice way of customizing parts using R callback, I won't oppose it.


>>> - utils::select.list() and utils::menu(): I want to show my own UI if
>>> graphics==TRUE. Currently, select.list() has special code for windows,
>>> "aqua" and tcltk; menu() essentially assumes the same code. Give me a
>>> way to run register my own UI.
>> 
>> ptr_do_selectlist provides the customization and could be extended to other
>> platforms.
> 
> See above. But yes, if at least it was cross-platform, that would be nice.
> 
>>> - base::system(), base::system2(): As you will be aware, capturing the
>>> output of system commands in a GUI is tricky on Unix. I do have a
>>> solution for that, but I need to run synchronization code at the start
>>> and end of system() and system2(), in order to get interleaving right.
>>> Give me a hook, each, at the start and end of these functions.
>> 
>> I'm not sure I understand your concern here. What exactly is your worry?
>> Capturing output is trivial since it simply involves creating a pipe when
>> you initialize R which you can read while R is running and the
>> synchronization is provided by the OS, no magic involved.
> 
> Is that pipe buffered?

No (although you could buffer it if you wanted).


> In my setup it is. And so consider code like this:
>   for (i in 1:10) {
>       print (i)
>       system (paste ("echo", i)) 
>   }
> How do you ensure that this results in
> 1
> 1
> 2
> 2
> ...
> rather than e.g.
> 1
> 2
> 3
> 1
> 2
> 4
> 3
> 5
> 6
> ...
> 

In R the output will be 
1
1
2
2
by design - system() is synchronous, so stdout will arrive between the WriteConsole calls - try in the the R for Mac GUI and you'll see:

>   for (i in 1:10) {
+       print (i)
+       system (paste ("echo", i)) 
+   }
[1] 1
1
[1] 2
2
[1] 3
3
[1] 4
4
[1] 5
5
[1] 6
6
[1] 7
7
[1] 8
8
[1] 9
9
[1] 10
10
> 

Obviously, for wait=FALSE all bets are off. [Note: although the Mac GUI uses a hook for system, it is to run commands as root, not to do any special handling of I/O].


> I could not get it to work for RKWard without adding code to make sure the "other" output is flushed.

If you have issues with stdout buffer before the pipe, use can use setvbuf() to disable buffering.


> (Note: I'm capturing regular R output via 
> R_WriteConsoleEx, since I am interested in the differentiation between output 
> types that this provides. So I cannot simply push that down the same pipe.).
> 
>>> - graphics::plot.new(): I need a hook *before* the new plot is started,
>>> in order to properly implement device-independent plot history. I would
>>> appreciate not having to implement my own graphics device just to be
>>> able to run some code at this point.
>>> 
>>> - grDevices::dev.off(): I need a hook before the device is closed. Also
>>> for plot history.
>>> 
>>> - grDevices::dev.set(): I need a hook after the new device has been set.
>>> Also for plot history.
>>> 
>>> - grid::newpage(): See graphics::plot.new(). Of course, even better, I
>>> would like to have a hook that is called every time before a new page /
>>> frame is started on a device.
>> 
>> For all of the above: all of them are already available a device callbacks
>> (newPage, close, activate and newPage again).
> 
> Quoting myself: I would appreciate not having to implement my own graphics 
> device just to be able to run some code at [these] point[s].
> 

Cautiously I would argue you have to the moment you're outside R, essentially since the built-in graphics devices are not guaranteed to work when embedded. In practice, I think if you write a GUI, you have to provide a GD - that is the only reasonable way you can seamlessly incorporate R graphics into your GUI.

That said, there is a precedent for graphics hooks. The problem I see is the same that became apparent with grid last time we added hooks - there are potentially multiple ways to get to the same C level point, and they are either easily forgotten, or even hard to enumerate. For example, dev.set() is not the only way to activate a device - it can be activated from the menu and other means via selectDevice() which is at C level, so there is no R code involved at all, thus setting a hook on dev.set() is will work only in a  subset of cases and thus you cannot expect consistency in your GUI.

(Note: none of my comments are specific to RKWard, since it is not even available for OS X so I don't know anything about it)

Cheers,
Simon



[Rd] GUI hooks in R [Was: assignInNamespace and new bindings]
Simon Urbanek