proc test_char_repeat { } {

	# the character-repeat test is too timing sensitive to run reliably on
	# Qemu
	if {[have_include "power_on/qemu"]} { return 0 }

	# the legacy PIT timer driver is too inaccurate to produce stable results
	# for the character-repeat test
	if {[binary_name_timer] == "pit_timer_drv"} { return 0 }

	return 1
}

#
# Build
#

set build_components {
	core init timer
	server/report_rom server/input_filter test/input_filter
}

build $build_components

create_boot_directory

#
# Generate config
#

append config {
<config prio_levels="2">
	<parent-provides>
		<service name="ROM"/>
		<service name="CPU"/>
		<service name="PD"/>
		<service name="LOG"/>
		<service name="IRQ"/>
		<service name="IO_MEM"/>
		<service name="IO_PORT"/>
	</parent-provides>

	<default-route>
		<any-service> <parent/> <any-child/> </any-service>
	</default-route>

	<default caps="100"/>

	<start name="timer">
		<resource name="RAM" quantum="1M"/>
		<provides><service name="Timer"/></provides>
	</start>

	<start name="report_rom" priority="-1">
		<resource name="RAM" quantum="2M"/>
		<provides> <service name="ROM"/> <service name="Report"/> </provides>
		<config>
			<policy label_prefix="input_filter -> config"
			        report="test-input_filter -> input_filter.config"/>
			<policy label_prefix="input_filter -> chargen_include"
			        report="test-input_filter -> chargen_include"/>
			<policy label_prefix="input_filter -> remap_include"
			        report="test-input_filter -> remap_include"/>
			<policy label_prefix="input_filter -> capslock"
			        report="test-input_filter -> capslock"/>
		</config>
	</start>

	<start name="input_filter" priority="-1">
		<resource name="RAM" quantum="2M"/>
		<provides> <service name="Input"/> </provides>
		<route>
			<service name="ROM" label="config">          <child name="report_rom"/> </service>
			<service name="ROM" label="chargen_include"> <child name="report_rom"/> </service>
			<service name="ROM" label="remap_include">   <child name="report_rom"/> </service>
			<service name="ROM" label="capslock">        <child name="report_rom"/> </service>
			<service name="Input"> <child name="test-input_filter"/> </service>
			<service name="Timer"> <child name="timer"/> </service>
			<any-service> <parent/> </any-service>
		</route>
	</start>

	<start name="test-input_filter" priority="-1">
		<resource name="RAM" quantum="4M"/>
		<provides> <service name="Input"/> </provides>
		<config>

			<message string="test merging of two input sources"/>

			<filter_config>
				<input label="ps2"/>
				<input label="usb"/>
				<output>
					<merge>
						<input name="ps2"/>
						<input name="usb"/>
					</merge>
				</output>
			</filter_config>
			<sleep ms="250"/>

			<usb> <press code="KEY_A"/> <release code="KEY_A"/> </usb>
			<expect_press   code="KEY_A"/>
			<expect_release code="KEY_A"/>

			<ps2> <press code="KEY_B"/> <release code="KEY_B"/> </ps2>
			<expect_press   code="KEY_B"/>
			<expect_release code="KEY_B"/>


			<message string="test key remapping"/>

			<filter_config>
				<input label="ps2"/>
				<input label="usb"/>
				<output>
					<remap>
						<merge>
							<input name="usb"/>
							<remap>
								<input name="ps2"/>
								<key name="KEY_A" to="KEY_C"/>
							</remap>
						</merge>
						<key name="KEY_A" to="KEY_B"/>
					</remap>
				</output>
			</filter_config>
			<sleep ms="250"/>

			<usb> <press code="KEY_A"/> <release code="KEY_A"/> </usb>
			<expect_press   code="KEY_B"/>
			<expect_release code="KEY_B"/>

			<ps2> <press code="KEY_A"/> <release code="KEY_A"/> </ps2>
			<expect_press   code="KEY_C"/>
			<expect_release code="KEY_C"/>


			<message string="test deferred reconfiguration"/>

			<!-- hold key while reconfiguring the filter, the remapping applies -->
			<usb> <press code="KEY_A"/> </usb>
			<expect_press code="KEY_B"/>

			<filter_config>
				<input label="usb"/>
				<output> <input name="usb"/> </output>
			</filter_config>
			<sleep ms="250"/>

			<!-- expect the remapping rules to persist until all keys are released -->
			<usb> <release code="KEY_A"/> </usb>
			<expect_release code="KEY_B"/>

			<!-- input_filter now requests a new input session from us -->
			<sleep ms="100"/>

			<!-- now the default configuration is expected to take effect -->
			<usb> <press code="KEY_A"/> <release code="KEY_A"/> </usb>
			<expect_press   code="KEY_A"/>
			<expect_release code="KEY_A"/>


			<message string="test emission of characters"/>

			<filter_config>
				<input label="usb"/>
				<output>
					<chargen>
						<input name="usb"/>
						<mod1>
							<key name="KEY_LEFTSHIFT"/> <key name="KEY_RIGHTSHIFT"/>
						</mod1>
						<map>            <key name="KEY_A" char="a"/> </map>
						<map mod1="yes"> <key name="KEY_A" char="A"/> </map>
					</chargen>
				</output>
			</filter_config>
			<sleep ms="250"/>
			<usb>
				<press   code="KEY_A"/> <release code="KEY_A"/>
				<press   code="KEY_LEFTSHIFT"/>
				<press   code="KEY_A"/> <release code="KEY_A"/>
				<release code="KEY_LEFTSHIFT"/>
				<press   code="KEY_RIGHTSHIFT"/>
				<press   code="KEY_A"/> <release code="KEY_A"/>
				<release code="KEY_RIGHTSHIFT"/>
			</usb>
			<expect_press   code="KEY_A" char="a"/>
			<expect_release code="KEY_A"/>
			<expect_press   code="KEY_LEFTSHIFT"/>
			<expect_press   code="KEY_A" char="A"/>
			<expect_release code="KEY_A"/>
			<expect_release code="KEY_LEFTSHIFT"/>
			<expect_press   code="KEY_RIGHTSHIFT"/>
			<expect_press   code="KEY_A" char="A"/>
			<expect_release code="KEY_A"/>
			<expect_release code="KEY_RIGHTSHIFT"/>}

append_if [test_char_repeat] config {

			<message string="test character repeat"/>

			<filter_config>
				<input label="usb"/>
				<output>
					<chargen>
						<input name="usb"/>
						<repeat delay_ms="600" rate_ms="200"/>
						<map> <key name="KEY_A" char="a"/> </map>
					</chargen>
				</output>
			</filter_config>
			<sleep ms="500"/>
			<usb> <press code="KEY_A"/> </usb>
			<sleep ms="1500"/>
			<usb> <release code="KEY_A"/> </usb>
			<!-- periodic characters should stop now -->
			<sleep ms="1000"/> 
			<usb> <press code="KEY_B"/> <release code="KEY_B"/> </usb>
			<expect_press   code="KEY_A"       char="a"/> <!-- original press           (0 ms) -->
			<expect_press   code="KEY_UNKNOWN" char="a"/> <!-- character after delay  (600 ms) -->
			<expect_press   code="KEY_UNKNOWN" char="a"/> <!-- periodic character     (800 ms) -->
			<expect_press   code="KEY_UNKNOWN" char="a"/> <!-- periodic character    (1000 ms) -->
			<expect_press   code="KEY_UNKNOWN" char="a"/> <!-- periodic character    (1200 ms) -->
			<expect_press   code="KEY_UNKNOWN" char="a"/> <!-- periodic character    (1400 ms) -->
			<expect_release code="KEY_A"/>
			<expect_press   code="KEY_B"/>
			<expect_release code="KEY_B"/>}

append config {

			<message string="capslock handling"/>

			<filter_config>
				<input label="usb"/>
				<output>
					<chargen>
						<remap>
							<input name="usb"/>
						</remap>
						<mod4> <rom name="capslock"/> </mod4>
						<map>            <key name="KEY_A" char="a"/> </map>
						<map mod4="yes"> <key name="KEY_A" char="A"/> </map>
					</chargen>
				</output>
			</filter_config>
			<!--
			     Leave the 'capslock' ROM initially undefined, which prompts
			     the input filter to complain about the modifier state being
			     unavailable. However, as soon as 'capslock' becomes defined,
			     the input filter is expected to re-processes its configuration.
			 -->
			<sleep ms="250"/>
			<capslock enabled="no"/>
			<sleep ms="250"/>
			<usb> <press code="KEY_A"/> <release code="KEY_A"/> </usb>
			<expect_press code="KEY_A" char="a"/> <expect_release code="KEY_A"/>
			<capslock enabled="yes"/>
			<sleep ms="250"/>
			<usb> <press code="KEY_A"/> <release code="KEY_A"/> </usb>
			<expect_press code="KEY_A" char="A"/> <expect_release code="KEY_A"/>
			<capslock enabled="no"/>
			<sleep ms="250"/>
			<usb> <press code="KEY_A"/> <release code="KEY_A"/> </usb>
			<expect_press code="KEY_A" char="a"/> <expect_release code="KEY_A"/>


			<message string="sequence handling"/>

			<filter_config>
				<input label="usb"/>
				<output>
					<chargen>
						<remap>
							<input name="usb"/>
						</remap>
						<map>
							<key name="KEY_GRAVE" code="0x0300"/> <!-- dead_grave -->
							<key name="KEY_A"     char="a"/>
							<key name="KEY_E"     char="e"/>
							<key name="KEY_X"     char="x"/>
						</map>
						<sequence first="0x0300" second="0x0061" code="0x00e0"/> <!-- LATIN SMALL LETTER A WITH GRAVE -->
						<sequence first="0x0300" second="0x0065" code="0x00e8"/> <!-- LATIN SMALL LETTER E WITH GRAVE -->
					</chargen>
				</output>
			</filter_config>
			<sleep ms="250"/>
			<usb>
				<press code="KEY_GRAVE"/> <release code="KEY_GRAVE"/> <!-- invalid char -->
				<press code="KEY_A"/>     <release code="KEY_A"/>     <!-- generate a-grave -->
				<press code="KEY_GRAVE"/> <release code="KEY_GRAVE"/> <!-- invalid char -->
				<press code="KEY_E"/>     <release code="KEY_E"/>     <!-- generate e-grave -->
				<press code="KEY_GRAVE"/> <release code="KEY_GRAVE"/> <!-- invalid char -->
				<press code="KEY_X"/>     <release code="KEY_X"/>     <!-- abort sequence (invalid char) -->
				<press code="KEY_X"/>     <release code="KEY_X"/>     <!-- generate x -->
			</usb>
			<expect_press   code="KEY_GRAVE" codepoint="0xfffe"/>
			<expect_release code="KEY_GRAVE"/>
			<expect_press   code="KEY_A"     codepoint="0x00e0"/>
			<expect_release code="KEY_A"/>
			<expect_press   code="KEY_GRAVE"/>
			<expect_release code="KEY_GRAVE"/>
			<expect_press   code="KEY_E"     codepoint="0x00e8"/>
			<expect_release code="KEY_E"/>
			<expect_press   code="KEY_GRAVE"/>
			<expect_release code="KEY_GRAVE"/>
			<message string="1"/>
			<expect_press   code="KEY_X"     codepoint="0xfffe"/>
			<expect_release code="KEY_X"/>
			<message string="2"/>
			<expect_press   code="KEY_X" char="x"/>
			<expect_release code="KEY_X"/>
			<message string="3"/>
			<sleep ms="250"/>


			<message string="button-scroll feature"/>

			<filter_config>
				<input label="usb"/>
				<output>
					<button-scroll>
						<input name="usb"/>
						<vertical   button="BTN_MIDDLE"    speed_percent="-50"/>
						<horizontal button="KEY_LEFTSHIFT" speed_percent="50"/>
					</button-scroll>
				</output>
			</filter_config>
			<sleep ms="100"/>
			<usb>
				<press code="BTN_MIDDLE"/> <release code="BTN_MIDDLE"/>
				<motion rx="10" ry="10"/>
				<press code="BTN_MIDDLE"/>
				<motion rx="1" ry="1"/>
				<motion rx="1" ry="1"/>
				<press code="KEY_LEFTSHIFT"/>
				<motion rx="1" ry="1"/>
				<motion rx="1" ry="1"/>
				<release code="KEY_LEFTSHIFT"/>
				<release code="BTN_MIDDLE"/>
				<motion rx="10" ry="10"/>
				<press code="BTN_MIDDLE"/> <release code="BTN_MIDDLE"/>
			</usb>
			<!-- press-release w/o motion is reported at release time -->
			<expect_press   code="BTN_MIDDLE"/>
			<expect_release code="BTN_MIDDLE"/>
			<expect_motion rx="10" ry="10"/>
			<expect_wheel rx="0" ry="-1"/>
			<expect_wheel rx="1" ry="-1"/>
			<expect_motion rx="10" ry="10"/>
			<expect_press   code="BTN_MIDDLE"/>
			<expect_release code="BTN_MIDDLE"/>


			<message string="accleration of relative motion events"/>

			<!-- linear acceleration -->
			<filter_config>
				<input label="usb"/>
				<output>
					<accelerate max="100" curve="0" sensitivity_percent="200">
						<input name="usb"/>
					</accelerate>
				</output>
			</filter_config>
			<sleep ms="100"/>
			<usb>
				<motion rx="1"/>
				<motion rx="20"/>
				<motion rx="100"/>
				<motion rx="-100"/>
			</usb>
			<expect_motion rx="1"/>
			<expect_motion rx="35"/>
			<expect_motion rx="178"/>
			<expect_motion rx="-178"/>

			<!-- non-linear acceleration -->
			<filter_config>
				<input label="usb"/>
				<output>
					<accelerate max="100" curve="127" sensitivity_percent="200">
						<input name="usb"/>
					</accelerate>
				</output>
			</filter_config>
			<sleep ms="100"/>
			<usb>
				<motion rx="1"/>
				<motion rx="20"/>
				<motion rx="100"/>
				<motion rx="-100"/>
			</usb>
			<expect_motion rx="1"/>
			<expect_motion rx="23"/>
			<expect_motion rx="191"/>
			<expect_motion rx="-191"/>


			<message string="survive deeply nested config"/>

			<deep_filter_config depth="50"/>
			<sleep ms="100"/>


			<message string="survive attempt to include non-existing ROM"/>

			<remap_include>   </remap_include>
			<chargen_include> </chargen_include>
			<filter_config>
				<input label="usb"/>
				<output>
					<chargen>
						<input name="usb"/>
						<include rom="nonexisting_include"/>
					</chargen>
				</output>
			</filter_config>
			<sleep ms="100"/>


			<message string="detect top-level node mismatch in included ROM"/>

			<filter_config>
				<input label="usb"/>
				<output>
					<chargen>
						<input name="usb"/>
						<include rom="remap_include"/>
					</chargen>
				</output>
			</filter_config>
			<sleep ms="100"/>


			<message string="survive include recursion"/>

			<chargen_include> <include rom="chargen_include"/> </chargen_include>
			<filter_config>
				<input label="usb"/>
				<output>
					<chargen>
						<input name="usb"/>
						<include rom="chargen_include"/>
					</chargen>
				</output>
			</filter_config>
			<sleep ms="100"/>


			<message string="include valid chargen rules"/>

			<chargen_include>
				<map> <key name="KEY_A" char="a"/> </map>
			</chargen_include>
			<filter_config>
				<input label="usb"/>
				<output>
					<chargen>
						<input name="usb"/>
						<include rom="chargen_include"/>
					</chargen>
				</output>
			</filter_config>
			<sleep ms="100"/>
			<usb> <press code="KEY_A"/> <release code="KEY_A"/> </usb>
			<expect_press code="KEY_A" char="a"/> <expect_release code="KEY_A"/>


			<message string="update included chargen ROM"/>

			<chargen_include>
				<map> <key name="KEY_A" char="b"/> </map>
			</chargen_include>
			<sleep ms="100"/>
			<usb> <press code="KEY_A"/> <release code="KEY_A"/> </usb>
			<expect_press code="KEY_A" char="b"/> <expect_release code="KEY_A"/>

		</config>
		<route>
			<service name="Input">  <child name="input_filter"/> </service>
			<service name="Report"> <child name="report_rom"/> </service>
			<service name="Timer">  <child name="timer"/> </service>
			<any-service> <parent/> </any-service>
		</route>
	</start>

</config>}

install_config $config

#
# Boot modules
#

set boot_modules { core ld.lib.so init timer report_rom
                   input_filter test-input_filter }

build_boot_image $boot_modules

append qemu_args " -nographic "

run_genode_until {.*child "test-input_filter" exited with exit value 0.*} 60

