From: Jean Privat Date: Tue, 1 Jul 2014 19:05:24 +0000 (-0400) Subject: lib/mnit: introduce `mnit_injected_input` to inject events into mnit applications X-Git-Tag: v0.6.6~5^2~6 X-Git-Url: http://nitlanguage.org lib/mnit: introduce `mnit_injected_input` to inject events into mnit applications Signed-off-by: Jean Privat --- diff --git a/lib/mnit/mnit_injected_input.nit b/lib/mnit/mnit_injected_input.nit new file mode 100644 index 0000000..feff6ca --- /dev/null +++ b/lib/mnit/mnit_injected_input.nit @@ -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