3

I am working on a small terminal-based UI and I want to use Reactive Banana for describing interactions. The only external event I am interested in, is whether the user has pressed a key.

From what I gathered from the Frameworks documentation, I can either use polling to get the state of the terminal input buffer or pass an "event handler" to Reactive Banana. I'd rather prefer the latter, but I find the event handling section confusing. How exactly would I describe an event-driven version of getChar? In the end, I'd like to operate on an "Event Key" stream.

I have looked at the SDL and wx bindings, but I don't find them of much help, since they are littered with library related idiosyncracies, and I'd much appreciate a more concise explanation how event handlers, event loops and Reactive Banana mix together.

ftl
  • 647
  • 3
  • 13
  • There are plenty of examples/tutorials to be found on the internet. What have you tried and how did it not work? (The function you want for dealing with user input events like keypresses is [`fromAddHandler`](https://hackage.haskell.org/package/reactive-banana-1.1.0.1/docs/Reactive-Banana-Frameworks.html#v:fromAddHandler)) – user2407038 Jun 19 '17 at 21:46
  • Yup, I think I have figured it out, just after posting. It seems that I just needed a bit of rest. I'll post my answer, in case there is any interest, otherwise the question can be closed. Thank you. – ftl Jun 19 '17 at 22:06
  • 1
    There is no need to close it - edit the question to include some code showing where you originally got stuck, and then post the answer containing the solution you found. Self-answered questions are a perfectly valid format for SO! You don't do this for any particular person who has an interest; it is for the posterity of *all* future readers. – user2407038 Jun 19 '17 at 22:10

1 Answers1

2

As it turns out, dealing with external events is remarkably flexible and simple with Reactive Banana. All that is needed is to create an event handler with "newAddHandler" which consists of two pieces, an "AddHandler" from which events can be created using the reactimate function, and a Handler which has to be invoked by the glue code (binding) that bridges Reactive Banana to the framework (in my case, I just feed the result of getChar to the handler).

Here is an example code that echos keystrokes:

echo = do
  (keyEventHandler, fire) <- newAddHandler

  -- Network Specification (echo keyboard input)
  let networkDescription =
      fromAddHandler keyEventHandler >>= -- Create event stream from handler
      reactimate . fmap print -- Map print over event stream

  compile networkDescription >>= actuate 

  -- Event Loop
  hSetBuffering stdin NoBuffering   
  forever $ do
      ready <- hReady stdin
      if ready
          then getChar >>= fire -- Create keyboad event
          else return ()

The nice thing about reactive banana is that the callback function "fire" can be invoked from any context. Thus, the event loop could as well run in a thread or be invoked from a C library. More about this can be found by Heinrich's answer to How to implement a game loop in reactive-banana?.

However, catching arbitrary keyboard input events without polling is not as easy as I thought, POSIX won't allow it (if I am not mistaken) and even if it did the code would not be portable. Also note that on Windows machines, you have to hit enter, I still have to find a way around that problem. More on this issue is found at the Haskell bugtracker

ftl
  • 647
  • 3
  • 13