lib/standard/stream: Renamed streams for more explicit denomination
[nit.git] / lib / mnit / mnit_injected_input.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Injection of input events that are read frm a file.
16 # This cloud be used to replay an execution of a mnit application to debug or to benchmark it.
17 #
18 # The input file is given through the environment variable `MNIT_READ_INPUT`
19 #
20 # In order to reproduce executions, the behavior of the application must be deterministic
21 # for a given sequence of inputs.
22 # The main source of differences in executions is caused by the `rand` function,
23 # Set the environment variable `MNIT_SRAND` to a value to force srand to be initialized with this value.
24 #
25 # The input event file is made of event descriptions, one event by line.
26 #
27 # ~~~raw
28 # 10 click 10.0 20.0
29 # 20 quit
30 # ~~~
31 #
32 # The first field, an integer, is the delay (in frame count) since the previous event
33 # 0 means the event is launched in the same frame that the previous one.
34 #
35 # The second field, a string, is the kind of the event.
36 # Currently only `click` for PointerEvent and `quit` for QuitEvent are recognized.
37 #
38 # The following fields are the arguments that specific for each kind of event.
39 #
40 # * `quit` does not have arguments
41 # * `click` has 2 float arguments: `PointerEvent::x` and `PointerEvent::y`
42 module mnit_injected_input
43
44 import mnit_app
45
46 # Concrete event objects that are manually instantiated.
47 # Most InputEvent are extern classes and specific to one platform.
48 #
49 # However, subclasses of this `DummyInputEvent` are genuine Nit
50 # classes and can be instantiated, and more easily manipulated by the programmer.
51 interface DummyInputEvent
52 super InputEvent
53 end
54
55 # A concrete QuitEvent
56 class DummyQuitEvent
57 super DummyInputEvent
58 super QuitEvent
59 redef fun to_s do return "quit"
60 end
61
62 # A concrete PointerEvent
63 class DummyPointerEvent
64 super DummyInputEvent
65 super PointerEvent
66 redef var x: Float
67 redef var y: Float
68 redef fun pressed do return true
69 redef fun to_s do return "click {x} {y}"
70 end
71
72 redef class App
73 # The stream where injected inputs are read
74 private var injected_input_stream: nullable Reader = null
75
76 redef fun setup
77 do
78 var env = "MNIT_SRAND".environ
79 if env != "" then
80 srand_from(env.to_i)
81 else
82 srand_from(0)
83 end
84
85 var input = "MNIT_READ_INPUT".environ
86 if input != "" then
87 injected_input_stream = new FileReader.open(input)
88 print "GET injected_input_stream {input}"
89 end
90
91 super
92 end
93
94 # Number of frames before the next injected input
95 private var wait_next_input = 0
96
97 # What is the input to inject when `wait_next_input` become 0
98 private var next_input: nullable DummyInputEvent
99
100 redef fun full_frame
101 do
102 if injected_input_stream != null then generate_injected_input
103 super
104 end
105
106 # Internal method to generate injected input events
107 # Is called before each frame
108 # Return `true` is an input event was injexted
109 fun generate_injected_input: Bool
110 do
111 var res = false
112 loop
113 if wait_next_input > 0 then
114 wait_next_input -= 1
115 return res
116 end
117 var n = next_input
118 if n != null then
119 print "INPUT {n}"
120 res = true
121 input(n)
122 end
123 var l = injected_input_stream.read_line
124 if l == "" then
125 print "END OF INPUTS"
126 injected_input_stream.close
127 injected_input_stream = null
128 input(new DummyQuitEvent)
129 return true
130 end
131 print "read {l}"
132 var fs = l.split(" ")
133 if fs.length < 2 then
134 print "BAD EVENT SPEC {l}"
135 res = true
136 input(new DummyQuitEvent)
137 end
138 wait_next_input = fs[0].to_i
139 if fs[1] == "click" then
140 next_input = new DummyPointerEvent(fs[2].to_f, fs[3].to_f)
141 else if fs[1] == "quit" then
142 next_input = new DummyQuitEvent
143 else
144 print "UNKNOWN EVENT {fs[1]} (on {l})"
145 res = true
146 input(new DummyQuitEvent)
147 return true
148 end
149 print "WAIT {wait_next_input} for {next_input.to_s}"
150 end
151 end
152 end