lib/mnit: introduce `mnit_injected_input` to inject events into mnit applications
authorJean Privat <jean@pryen.org>
Tue, 1 Jul 2014 19:05:24 +0000 (15:05 -0400)
committerJean Privat <jean@pryen.org>
Tue, 1 Jul 2014 19:05:24 +0000 (15:05 -0400)
Signed-off-by: Jean Privat <jean@pryen.org>

lib/mnit/mnit_injected_input.nit [new file with mode: 0644]

diff --git a/lib/mnit/mnit_injected_input.nit b/lib/mnit/mnit_injected_input.nit
new file mode 100644 (file)
index 0000000..feff6ca
--- /dev/null
@@ -0,0 +1,149 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Injection of input events that are read frm a file.
+# This cloud be used to replay an execution of a mnit application to debug or to benchmark it.
+#
+# The input file is given through the environment variable `MNIT_READ_INPUT`
+#
+# In order to reproduce executions, the behavior of the application must be deterministic
+# for a given sequence of inputs.
+# The main source of differences in executions is caused by the `rand` function,
+# Set the environment variable `MNIT_SRAND` to a value to force srand to be initialized with this value.
+#
+# The input event file is made of event descriptions, one event by line.
+#
+# ~~~
+# 10 click 10.0 20.0
+# 20 quit
+# ~~~
+#
+# The first field, an integer, is the delay (in frame count) since the previous event
+# 0 means the event is launched in the same frame that the previous one.
+#
+# The second field, a string, is the kind of the event.
+# Currently only `click` for PointerEvent and `quit` for QuitEvent are recognized.
+#
+# The following fields are the arguments that specific for each kind of event.
+#
+# * `quit` does not have arguments
+# * `click` has 2 float arguments: `PointerEvent::x` and `PointerEvent::y`
+module mnit_injected_input
+
+import mnit_app
+
+# Concrete event objects that are manually instantiated.
+# Most InputEvent are extern classes and specific to one platform.
+#
+# However, subclasses of this `DummyInputEvent` are genuine Nit
+# classes and can be instantiated, and more easily manipulated by the programmer.
+interface DummyInputEvent
+       super InputEvent
+end
+
+# A concrete QuitEvent
+class DummyQuitEvent
+       super DummyInputEvent
+       super QuitEvent
+       redef fun to_s do return "quit"
+end
+
+# A concrete PointerEvent
+class DummyPointerEvent
+       super DummyInputEvent
+       super PointerEvent
+       redef var x: Float
+       redef var y: Float
+       redef fun pressed do return true
+       redef fun to_s do return "click {x} {y}"
+end
+
+redef class App
+       # The stream where injected inputs are read
+       private var injected_input_stream: nullable IStream = null
+
+       redef fun setup
+       do
+               var env = "MNIT_SRAND".environ
+               if env != null and env != "" then
+                       srand_from(env.to_i)
+               end
+
+               var input = "MNIT_READ_INPUT".environ
+               if input != null and input != "" then
+                       injected_input_stream = new IFStream.open(input)
+                       print "GET injected_input_stream {input}"
+               end
+
+               super
+       end
+
+       # Number of frames before the next injected input
+       private var wait_next_input = 0
+
+       # What is the input to inject when `wait_next_input` become 0
+       private var next_input: nullable DummyInputEvent
+
+       redef fun full_frame
+       do
+               if injected_input_stream != null then generate_injected_input
+               super
+       end
+
+       # Internal method to generate injected input events
+       # Is called before each frame
+       # Return `true` is an input event was injexted
+       fun generate_injected_input: Bool
+       do
+               var res = false
+               loop
+                       if wait_next_input > 0 then
+                               wait_next_input -= 1
+                               return res
+                       end
+                       var n = next_input
+                       if n != null then
+                               print "INPUT {n}"
+                               res = true
+                               input(n)
+                       end
+                       var l = injected_input_stream.read_line
+                       if l == "" then
+                               print "END OF INPUTS"
+                               injected_input_stream.close
+                               injected_input_stream = null
+                               input(new DummyQuitEvent)
+                               return true
+                       end
+                       print "read {l}"
+                       var fs = l.split(" ")
+                       if fs.length < 2 then
+                               print "BAD EVENT SPEC {l}"
+                               res = true
+                               input(new DummyQuitEvent)
+                       end
+                       wait_next_input = fs[0].to_i
+                       if fs[1] == "click" then
+                               next_input = new DummyPointerEvent(fs[2].to_f, fs[3].to_f)
+                       else if fs[1] == "quit" then
+                               next_input = new DummyQuitEvent
+                       else
+                               print "UNKNOWN EVENT {fs[1]} (on {l})"
+                               res = true
+                               input(new DummyQuitEvent)
+                       end
+                       print "WAIT {wait_next_input} for {next_input}"
+               end
+       end
+end