mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-30 10:38:55 +00:00
parent
4491c070be
commit
ca850c787f
@ -22,7 +22,6 @@
|
|||||||
</remap>
|
</remap>
|
||||||
<mod1>
|
<mod1>
|
||||||
<key name="KEY_LEFTSHIFT"/> <key name="KEY_RIGHTSHIFT"/>
|
<key name="KEY_LEFTSHIFT"/> <key name="KEY_RIGHTSHIFT"/>
|
||||||
<rom name="capslock"/>
|
|
||||||
</mod1>
|
</mod1>
|
||||||
<mod2>
|
<mod2>
|
||||||
<key name="KEY_LEFTCTRL"/> <key name="KEY_RIGHTCTRL"/>
|
<key name="KEY_LEFTCTRL"/> <key name="KEY_RIGHTCTRL"/>
|
||||||
@ -30,6 +29,9 @@
|
|||||||
<mod3>
|
<mod3>
|
||||||
<key name="KEY_RIGHTALT"/> <!-- AltGr -->
|
<key name="KEY_RIGHTALT"/> <!-- AltGr -->
|
||||||
</mod3>
|
</mod3>
|
||||||
|
<mod4>
|
||||||
|
<rom name="capslock"/>
|
||||||
|
</mod4>
|
||||||
<repeat delay_ms="230" rate_ms="40"/>
|
<repeat delay_ms="230" rate_ms="40"/>
|
||||||
<include rom="en_us.chargen"/>
|
<include rom="en_us.chargen"/>
|
||||||
<include rom="special.chargen"/>
|
<include rom="special.chargen"/>
|
||||||
|
@ -241,9 +241,9 @@ append config {
|
|||||||
<remap>
|
<remap>
|
||||||
<input name="usb"/>
|
<input name="usb"/>
|
||||||
</remap>
|
</remap>
|
||||||
<mod1> <rom name="capslock"/> </mod1>
|
<mod4> <rom name="capslock"/> </mod4>
|
||||||
<map> <key name="KEY_A" char="a"/> </map>
|
<map> <key name="KEY_A" char="a"/> </map>
|
||||||
<map mod1="yes"> <key name="KEY_A" char="A"/> </map>
|
<map mod4="yes"> <key name="KEY_A" char="A"/> </map>
|
||||||
</chargen>
|
</chargen>
|
||||||
</output>
|
</output>
|
||||||
</filter_config>
|
</filter_config>
|
||||||
@ -268,6 +268,56 @@ append config {
|
|||||||
<expect_press code="KEY_A" char="a"/> <expect_release code="KEY_A"/>
|
<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"/>
|
<message string="button-scroll feature"/>
|
||||||
|
|
||||||
<filter_config>
|
<filter_config>
|
||||||
|
@ -78,14 +78,17 @@ sub nodes:
|
|||||||
:<mod1>/<mod2>/<mod3>/<mod4>:
|
:<mod1>/<mod2>/<mod3>/<mod4>:
|
||||||
|
|
||||||
Defines which physical keys are interpreted as modifier keys. Usually,
|
Defines which physical keys are interpreted as modifier keys. Usually,
|
||||||
'<mod1>' corresponds to shift, '<mod2>' to control, and '<mod3>' to altgr
|
'<mod1>' corresponds to shift, '<mod2>' to control, '<mod3>' to altgr
|
||||||
(on German keyboards). Each modifier node may host any number of '<key>'
|
(on German keyboards), and '<mod4>' to Caps Lock. Each modifier node
|
||||||
nodes with their corresponding 'name' attribute. For example:
|
may host any number of '<key>' nodes with their corresponding 'name'
|
||||||
|
attribute. For example:
|
||||||
|
|
||||||
! <mod1>
|
! <mod1>
|
||||||
! <key name="KEY_LEFTSHIFT"/> <key name="KEY_RIGHTSHIFT"/>
|
! <key name="KEY_LEFTSHIFT"/> <key name="KEY_RIGHTSHIFT"/>
|
||||||
! <rom name="capslock"/>
|
|
||||||
! </mod1>
|
! </mod1>
|
||||||
|
! <mod4>
|
||||||
|
! <rom name="capslock"/>
|
||||||
|
! </mod4>
|
||||||
|
|
||||||
The '<rom>' node incorporates the content of the ROM module of the
|
The '<rom>' node incorporates the content of the ROM module of the
|
||||||
specified name into the modifier state. If the ROM module contains a
|
specified name into the modifier state. If the ROM module contains a
|
||||||
@ -103,10 +106,48 @@ sub nodes:
|
|||||||
|
|
||||||
Each '<map>' may contain any number of '<key>' subnodes. Each '<key>'
|
Each '<map>' may contain any number of '<key>' subnodes. Each '<key>'
|
||||||
must have the key name as 'name' attribute. The to-be-emitted character
|
must have the key name as 'name' attribute. The to-be-emitted character
|
||||||
is defined by the attributes 'ascii', 'char', or 'b0/b1/b2/b3'. The
|
is defined by the attributes 'ascii', 'char', 'code', or 'b0/b1/b2/b3'.
|
||||||
'ascii' attribute accepts an integer value between 0 and 127, the
|
|
||||||
'char' attribute accepts a single ASCII character, the 'b0/b1/b2/b3'
|
:'ascii': accepts an integer value between 0 and 127
|
||||||
attributes define the individual bytes of an UTF-8 character.
|
:'char': accepts a single ASCII character
|
||||||
|
:'code': defines the Unicode codepoint as integer value
|
||||||
|
:'b0'/'b1'/'b2'/'b3': define the individual bytes of an UTF-8 character
|
||||||
|
|
||||||
|
:<sequence first="..." second="..." third="..." fourth="..." code="..."/>:
|
||||||
|
|
||||||
|
A sequence node permits the definition of dead-key/composing
|
||||||
|
character sequences. With such sequences the character is not
|
||||||
|
generated instantly on key press but only after the sequence is
|
||||||
|
completed. If an unfinished sequence can't be completed due to an
|
||||||
|
unmatched character, the sequence is aborted and no character is
|
||||||
|
generated. input_filter supports sequences of up to four characters.
|
||||||
|
|
||||||
|
For example, the French AZERTY keyboard layout [1] has a dead key
|
||||||
|
for Circumflex Accent "^" right of the P key (which is bracket left
|
||||||
|
"[" on US keyboards). When Circumflex is pressed no visible
|
||||||
|
character should be generated instantly but the accent must be
|
||||||
|
combined with a follow-up character, e.g., Circumflex plus "a"
|
||||||
|
generates â.
|
||||||
|
|
||||||
|
[1] https://docs.microsoft.com/en-us/globalization/keyboards/kbdfr.html
|
||||||
|
|
||||||
|
Dead keys can be defined in the <key> nodes of any <map> by using
|
||||||
|
codepoints not used for direct output, for example, Combining
|
||||||
|
Diacritical Marks beginning at U+0300. The French Circumflex example
|
||||||
|
can be configured like follows.
|
||||||
|
|
||||||
|
! <mod1>
|
||||||
|
! <key name="KEY_LEFTSHIFT"/> <key name="KEY_RIGHTSHIFT"/>
|
||||||
|
! </mod1>
|
||||||
|
! <map>
|
||||||
|
! <key name="KEY_Q" code="0x0061"/> <!-- a -->
|
||||||
|
! <key name="KEY_LEFTBRACE" code="0x0302"/> <!-- dead_circumflex -->
|
||||||
|
! </map>
|
||||||
|
! <map mod1="true">
|
||||||
|
! <key name="KEY_Q" code="0x0041"/> <!-- A -->
|
||||||
|
! </map>
|
||||||
|
! <sequence first="0x0302" second="0x0061" code="0x00e2"/> <!-- â -->
|
||||||
|
! <sequence first="0x0302" second="0x0041" code="0x00c2"/> <!-- Â -->
|
||||||
|
|
||||||
:<repeat delay_ms="500" rate_ms="250">:
|
:<repeat delay_ms="500" rate_ms="250">:
|
||||||
|
|
||||||
|
@ -238,6 +238,49 @@ class Input_filter::Chargen_source : public Source, Source::Sink
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Missing_character_definition { };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return Unicode codepoint defined in XML node attributes
|
||||||
|
*
|
||||||
|
* \throw Missing_character_definition
|
||||||
|
*/
|
||||||
|
static Codepoint _codepoint_from_xml_node(Xml_node node)
|
||||||
|
{
|
||||||
|
if (node.has_attribute("ascii"))
|
||||||
|
return Codepoint { node.attribute_value<uint32_t>("ascii", 0) };
|
||||||
|
|
||||||
|
if (node.has_attribute("code"))
|
||||||
|
return Codepoint { node.attribute_value<uint32_t>("code", 0) };
|
||||||
|
|
||||||
|
if (node.has_attribute("char")) {
|
||||||
|
|
||||||
|
typedef String<2> Value;
|
||||||
|
Value value = node.attribute_value("char", Value());
|
||||||
|
|
||||||
|
unsigned char const ascii = value.string()[0];
|
||||||
|
|
||||||
|
if (ascii < 128)
|
||||||
|
return Codepoint { ascii };
|
||||||
|
|
||||||
|
warning("char attribute with non-ascii character "
|
||||||
|
"'", value, "'");
|
||||||
|
throw Missing_character_definition();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.has_attribute("b0")) {
|
||||||
|
char const b0 = node.attribute_value("b0", 0L),
|
||||||
|
b1 = node.attribute_value("b1", 0L),
|
||||||
|
b2 = node.attribute_value("b2", 0L),
|
||||||
|
b3 = node.attribute_value("b3", 0L);
|
||||||
|
|
||||||
|
char const buf[5] { b0, b1, b2, b3, 0 };
|
||||||
|
return Utf8_ptr(buf).codepoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Missing_character_definition();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map of the states of the physical keys
|
* Map of the states of the physical keys
|
||||||
*/
|
*/
|
||||||
@ -286,49 +329,6 @@ class Input_filter::Chargen_source : public Source, Source::Sink
|
|||||||
: Key::Rule::Conditions::Modifier::RELEASED;
|
: Key::Rule::Conditions::Modifier::RELEASED;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Missing_character_definition { };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return UTF8 character defined in XML node attributes
|
|
||||||
*
|
|
||||||
* \throw Missing_character_definition
|
|
||||||
*/
|
|
||||||
static Codepoint _codepoint_from_xml_node(Xml_node node)
|
|
||||||
{
|
|
||||||
if (node.has_attribute("ascii"))
|
|
||||||
return Codepoint { node.attribute_value<uint32_t>("ascii", 0) };
|
|
||||||
|
|
||||||
if (node.has_attribute("code"))
|
|
||||||
return Codepoint { node.attribute_value<uint32_t>("code", 0) };
|
|
||||||
|
|
||||||
if (node.has_attribute("char")) {
|
|
||||||
|
|
||||||
typedef String<2> Value;
|
|
||||||
Value value = node.attribute_value("char", Value());
|
|
||||||
|
|
||||||
unsigned char const ascii = value.string()[0];
|
|
||||||
|
|
||||||
if (ascii < 128)
|
|
||||||
return Codepoint { ascii };
|
|
||||||
|
|
||||||
warning("char attribute with non-ascii character "
|
|
||||||
"'", value, "'");
|
|
||||||
throw Missing_character_definition();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.has_attribute("b0")) {
|
|
||||||
char const b0 = node.attribute_value("b0", 0L),
|
|
||||||
b1 = node.attribute_value("b1", 0L),
|
|
||||||
b2 = node.attribute_value("b2", 0L),
|
|
||||||
b3 = node.attribute_value("b3", 0L);
|
|
||||||
|
|
||||||
char const buf[5] { b0, b1, b2, b3, 0 };
|
|
||||||
return Utf8_ptr(buf).codepoint();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw Missing_character_definition();
|
|
||||||
}
|
|
||||||
|
|
||||||
void import_map(Xml_node map)
|
void import_map(Xml_node map)
|
||||||
{
|
{
|
||||||
/* obtain modifier conditions from map attributes */
|
/* obtain modifier conditions from map attributes */
|
||||||
@ -368,6 +368,150 @@ class Input_filter::Chargen_source : public Source, Source::Sink
|
|||||||
mod_rom.enabled(); });
|
mod_rom.enabled(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate characters from codepoint sequences
|
||||||
|
*/
|
||||||
|
class Sequencer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
Allocator &_alloc;
|
||||||
|
|
||||||
|
struct Sequence
|
||||||
|
{
|
||||||
|
Codepoint seq[4] { Codepoint::INVALID, Codepoint::INVALID,
|
||||||
|
Codepoint::INVALID, Codepoint::INVALID };
|
||||||
|
|
||||||
|
unsigned len { 0 };
|
||||||
|
|
||||||
|
enum Match { MISMATCH , UNFINISHED, COMPLETED };
|
||||||
|
|
||||||
|
Sequence() { }
|
||||||
|
|
||||||
|
Sequence(Codepoint c0, Codepoint c1, Codepoint c2, Codepoint c3)
|
||||||
|
: seq { c0, c1, c2, c3 }, len { 4 } { }
|
||||||
|
|
||||||
|
void append(Codepoint c)
|
||||||
|
{
|
||||||
|
/* excess codepoints are just dropped */
|
||||||
|
if (len < 4)
|
||||||
|
seq[len++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match 'other' to 'this' until first invalid codepoint in
|
||||||
|
* 'other', completion, or mismatch
|
||||||
|
*/
|
||||||
|
Match match(Sequence const &o) const
|
||||||
|
{
|
||||||
|
/* first codepoint must match */
|
||||||
|
if (o.seq[0].value != seq[0].value) return MISMATCH;
|
||||||
|
|
||||||
|
for (unsigned i = 1; i < sizeof(seq)/sizeof(*seq); ++i) {
|
||||||
|
/* end of this sequence means COMPLETED */
|
||||||
|
if (!seq[i].valid()) break;
|
||||||
|
|
||||||
|
/* end of other sequence means UNFINISHED */
|
||||||
|
if (!o.seq[i].valid()) return UNFINISHED;
|
||||||
|
|
||||||
|
if (o.seq[i].value != seq[i].value) return MISMATCH;
|
||||||
|
|
||||||
|
/* continue until completion with both valid and equal */
|
||||||
|
}
|
||||||
|
return COMPLETED;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Rule
|
||||||
|
{
|
||||||
|
typedef Sequence::Match Match;
|
||||||
|
|
||||||
|
Registry<Rule>::Element element;
|
||||||
|
Sequence const sequence;
|
||||||
|
Codepoint const code;
|
||||||
|
|
||||||
|
Rule(Registry<Rule> ®istry, Sequence const &sequence, Codepoint code)
|
||||||
|
:
|
||||||
|
element(registry, *this),
|
||||||
|
sequence(sequence),
|
||||||
|
code(code)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
Registry<Rule> _rules { };
|
||||||
|
|
||||||
|
Sequence _curr_sequence { };
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Sequencer(Allocator &alloc) : _alloc(alloc) { }
|
||||||
|
|
||||||
|
~Sequencer()
|
||||||
|
{
|
||||||
|
_rules.for_each([&] (Rule &rule) {
|
||||||
|
destroy(_alloc, &rule); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void import_sequence(Xml_node node)
|
||||||
|
{
|
||||||
|
unsigned const invalid { Codepoint::INVALID };
|
||||||
|
|
||||||
|
Sequence sequence {
|
||||||
|
Codepoint { node.attribute_value("first", invalid) },
|
||||||
|
Codepoint { node.attribute_value("second", invalid) },
|
||||||
|
Codepoint { node.attribute_value("third", invalid) },
|
||||||
|
Codepoint { node.attribute_value("fourth", invalid) } };
|
||||||
|
|
||||||
|
new (_alloc) Rule(_rules, sequence, _codepoint_from_xml_node(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
Codepoint process(Codepoint codepoint)
|
||||||
|
{
|
||||||
|
Codepoint const invalid { Codepoint::INVALID };
|
||||||
|
Rule::Match best_match { Sequence::MISMATCH };
|
||||||
|
Codepoint result { codepoint };
|
||||||
|
Sequence seq { _curr_sequence };
|
||||||
|
|
||||||
|
seq.append(codepoint);
|
||||||
|
|
||||||
|
_rules.for_each([&] (Rule const &rule) {
|
||||||
|
/* early return if completed match was found already */
|
||||||
|
if (best_match == Sequence::COMPLETED) return;
|
||||||
|
|
||||||
|
Rule::Match const match { rule.sequence.match(seq) };
|
||||||
|
switch (match) {
|
||||||
|
case Sequence::MISMATCH:
|
||||||
|
return;
|
||||||
|
case Sequence::UNFINISHED:
|
||||||
|
best_match = match;
|
||||||
|
result = invalid;
|
||||||
|
return;
|
||||||
|
case Sequence::COMPLETED:
|
||||||
|
best_match = match;
|
||||||
|
result = rule.code;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
switch (best_match) {
|
||||||
|
case Sequence::MISMATCH:
|
||||||
|
/* drop cancellation codepoint of unfinished sequence */
|
||||||
|
if (_curr_sequence.len > 0)
|
||||||
|
result = invalid;
|
||||||
|
_curr_sequence = Sequence();
|
||||||
|
break;
|
||||||
|
case Sequence::UNFINISHED:
|
||||||
|
_curr_sequence = seq;
|
||||||
|
break;
|
||||||
|
case Sequence::COMPLETED:
|
||||||
|
_curr_sequence = Sequence();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} _sequencer;
|
||||||
|
|
||||||
Owner _owner;
|
Owner _owner;
|
||||||
|
|
||||||
Source::Sink &_destination;
|
Source::Sink &_destination;
|
||||||
@ -442,6 +586,8 @@ class Input_filter::Chargen_source : public Source, Source::Sink
|
|||||||
/* supplement codepoint information to press event */
|
/* supplement codepoint information to press event */
|
||||||
key.apply_best_matching_rule(_mod_map, [&] (Codepoint codepoint) {
|
key.apply_best_matching_rule(_mod_map, [&] (Codepoint codepoint) {
|
||||||
|
|
||||||
|
codepoint = _sequencer.process(codepoint);
|
||||||
|
|
||||||
ev = Event(Input::Press_char{keycode, codepoint});
|
ev = Event(Input::Press_char{keycode, codepoint});
|
||||||
|
|
||||||
if (_char_repeater.constructed())
|
if (_char_repeater.constructed())
|
||||||
@ -499,9 +645,25 @@ class Input_filter::Chargen_source : public Source, Source::Sink
|
|||||||
* Handle map nodes
|
* Handle map nodes
|
||||||
*/
|
*/
|
||||||
if (node.type() == "map") {
|
if (node.type() == "map") {
|
||||||
|
try {
|
||||||
_key_map.import_map(node);
|
_key_map.import_map(node);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
catch (Missing_character_definition) {
|
||||||
|
throw Invalid_config(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle sequence nodes
|
||||||
|
*/
|
||||||
|
if (node.type() == "sequence") {
|
||||||
|
try {
|
||||||
|
_sequencer.import_sequence(node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (Missing_character_definition) {
|
||||||
|
throw Invalid_config(); }
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Instantiate character repeater on demand
|
* Instantiate character repeater on demand
|
||||||
@ -555,6 +717,7 @@ class Input_filter::Chargen_source : public Source, Source::Sink
|
|||||||
_timer_accessor(timer_accessor),
|
_timer_accessor(timer_accessor),
|
||||||
_include_accessor(include_accessor),
|
_include_accessor(include_accessor),
|
||||||
_key_map(_alloc),
|
_key_map(_alloc),
|
||||||
|
_sequencer(_alloc),
|
||||||
_owner(factory),
|
_owner(factory),
|
||||||
_destination(destination),
|
_destination(destination),
|
||||||
_source(factory.create_source(_owner, input_sub_node(config), *this))
|
_source(factory.create_source(_owner, input_sub_node(config), *this))
|
||||||
|
@ -375,11 +375,14 @@ struct Test::Main : Input_from_filter::Event_handler
|
|||||||
ev.handle_press([&] (Input::Keycode key, Codepoint codepoint) {
|
ev.handle_press([&] (Input::Keycode key, Codepoint codepoint) {
|
||||||
|
|
||||||
auto codepoint_of_step = [] (Xml_node step) {
|
auto codepoint_of_step = [] (Xml_node step) {
|
||||||
return Utf8_ptr(step.attribute_value("char", Value()).string()).codepoint(); };
|
if (step.has_attribute("codepoint"))
|
||||||
|
return Codepoint { step.attribute_value("codepoint", 0U) };
|
||||||
|
return Utf8_ptr(step.attribute_value("char", Value()).string()).codepoint();
|
||||||
|
};
|
||||||
|
|
||||||
if (step.type() == "expect_press"
|
if (step.type() == "expect_press"
|
||||||
&& step.attribute_value("code", Value()) == Input::key_name(key)
|
&& step.attribute_value("code", Value()) == Input::key_name(key)
|
||||||
&& (!step.has_attribute("char") ||
|
&& ((!step.has_attribute("char") && !step.has_attribute("codepoint")) ||
|
||||||
codepoint_of_step(step).value == codepoint.value))
|
codepoint_of_step(step).value == codepoint.value))
|
||||||
step_succeeded = true;
|
step_succeeded = true;
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user