Discussion:
[xmonad] FZF integration
Thomas Droxler
2017-09-01 13:38:46 UTC
Permalink
Hi all,

I'm a big fan of FZF <https://github.com/junegunn/fzf> and I wanted to make
an xmonad integration, to work like Util.Dmenu
<https://hackage.haskell.org/package/xmonad-contrib-0.13/docs/XMonad-Util-Dmenu.html>.
I'm quite new to Haskell and I got some problems to do it.

The main problem I have is that fzf needs to run in a shell and when I run
an external terminal, it sucessfully pops up, but then the rest of the code
continue to be executed and I can't wait for the output of fzf. Moreover,
fzf give an output in the terminal, but then how do I get the output in my
haskell code?

I tried several think and the closest I got is to write the result of fzf
in a file and read it back, but I cannot wait until my terminal shutdown to
read my file, I have something like:

*(filePath, handle) <- openTempFile "*path*" "fzf"*
*hClose handle*
*(_, _, _, procHandle) <- runInteractiveCommand ("xterm -e '*dataForFZF* |
fzf >" ++ filePath ++"'")*
*-- Here I need to wait that my term close, I tried: waitForProcess
procHandle*
*-- But it just freeze my Xmonad, and not sure a while loop on
getProcessExitCode is better*
*str <- readFile filePath*
doSomethingWith* str*
*removeFile filePath*

Anyone has a hint to do this?

Thanks a lot, cheers.

Thomas Droxler
Brandon Allbery
2017-09-01 17:11:24 UTC
Permalink
Post by Thomas Droxler
I'm a big fan of FZF <https://github.com/junegunn/fzf> and I wanted to
make an xmonad integration, to work like Util.Dmenu
<https://hackage.haskell.org/package/xmonad-contrib-0.13/docs/XMonad-Util-Dmenu.html>.
I'm quite new to Haskell and I got some problems to do it.
This isn't so much about Haskell as it is about asynchronous programming
with the POSIX process model. Specifically: we disable POSIX's normal wait
behavior, so waitForProcess will not work (it will wait for all outstanding
processes matching the process specification and then fail, because
processes are being auto-reaped. See the POSIX documentation for SIGCHLD).

The ideal way to do this asynchronously is to run it in a separate process
and have it pass the result back to xmonad as an X11 client message you can
intercept in handleEventHook. But the current Haskell binding for
clientMessage events is less than ideal; it can still be done, but it is
more annoying than it should be.

If (and ONLY if) the subprocess is guaranteed to return quickly AND not map
a managed window, there is XMonad.Util.Run.runProcessWithInput or
runProcessWithInputAndWait. If it maps a window that needs to be managed,
this WILL hang. If some other process tries to map a window that needs to
be managed while this is waiting, it will fail to do so because if xmonad
is waiting on a subprocess, it is not managing windows.

We can't do this in subthreads because (a) X11 can't do proper multithread
(b) POSIX signals and subprocesses are per process, not per thread, so you
cannot launch and collect a subprocess in a separate thread without
changing the behavior of other threads. (And you cannot have multiple
Haskell threads manipulating the same xmonad state, because it is not
global. This is why you'd have to send a clientMessage to the main thread
with any response.)

Do remember that you are doing this in the program that manages new
windows; if you are making it wait for something else, it is not able to
manage new windows.
--
brandon s allbery kf8nh sine nomine associates
***@gmail.com ***@sinenomine.net
unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net
Thomas Droxler
2017-09-01 17:30:49 UTC
Permalink
Whaou thanks for the long and complete answer, I'll see what's possible to
do.

Cheers
Post by Brandon Allbery
Post by Thomas Droxler
I'm a big fan of FZF <https://github.com/junegunn/fzf> and I wanted to
make an xmonad integration, to work like Util.Dmenu
<https://hackage.haskell.org/package/xmonad-contrib-0.13/docs/XMonad-Util-Dmenu.html>.
I'm quite new to Haskell and I got some problems to do it.
This isn't so much about Haskell as it is about asynchronous programming
with the POSIX process model. Specifically: we disable POSIX's normal wait
behavior, so waitForProcess will not work (it will wait for all outstanding
processes matching the process specification and then fail, because
processes are being auto-reaped. See the POSIX documentation for SIGCHLD).
The ideal way to do this asynchronously is to run it in a separate process
and have it pass the result back to xmonad as an X11 client message you can
intercept in handleEventHook. But the current Haskell binding for
clientMessage events is less than ideal; it can still be done, but it is
more annoying than it should be.
If (and ONLY if) the subprocess is guaranteed to return quickly AND not
map a managed window, there is XMonad.Util.Run.runProcessWithInput or
runProcessWithInputAndWait. If it maps a window that needs to be managed,
this WILL hang. If some other process tries to map a window that needs to
be managed while this is waiting, it will fail to do so because if xmonad
is waiting on a subprocess, it is not managing windows.
We can't do this in subthreads because (a) X11 can't do proper multithread
(b) POSIX signals and subprocesses are per process, not per thread, so you
cannot launch and collect a subprocess in a separate thread without
changing the behavior of other threads. (And you cannot have multiple
Haskell threads manipulating the same xmonad state, because it is not
global. This is why you'd have to send a clientMessage to the main thread
with any response.)
Do remember that you are doing this in the program that manages new
windows; if you are making it wait for something else, it is not able to
manage new windows.
--
brandon s allbery kf8nh sine nomine associates
unix, openafs, kerberos, infrastructure, xmonad
http://sinenomine.net
Loading...