1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! Provides decoders for the keyboard-related signals.
//!
//! Keyboards, while components, do not expose any methods, so no component wrapper is provided in
//! this module.

use crate::helpers::decode_u32_from_signal;
use core::num::NonZeroU32;
use minicbor::{Decode, Decoder};
use oc_wasm_safe::Address;

/// Information about a key press or release event, excluding the name of the player who pressed or
/// released the key.
///
/// This structure, unlike [`KeySignal`](KeySignal), is fully owned. Thus, a typical application
/// which does not care about the player name can convert a [`KeySignal`](KeySignal) into a
/// `BasicKeySignal` in order to regain use of the decode buffer.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct BasicKeySignal {
	/// The address of the keyboard component.
	pub keyboard: Address,

	/// The character that was pressed or released, if any.
	///
	/// This is `None` if the event does not produce a character. Examples of
	/// non-character-producing key events are events related to lock keys, arrow keys, modifier
	/// keys, navigation keys, or other special keys.
	pub character: Option<char>,

	/// The keycode that was pressed or released, if any.
	///
	/// This is `None` if the event does not have a keycode. One example of a non-keycode-producing
	/// key event is a composed character; in this case, the individual keystrokes are not
	/// reported, but the composed character is reported as a character without associated keycode.
	/// Another example is a Windows key (aka super key) or Menu key, both of which (under some
	/// operating systems at least) send key signals with neither `character` nor `keycode` filled
	/// in.
	pub keycode: Option<NonZeroU32>,
}

impl<'buffer> From<&KeySignal<'buffer>> for BasicKeySignal {
	fn from(source: &KeySignal<'buffer>) -> Self {
		Self {
			keyboard: source.keyboard,
			character: source.character,
			keycode: source.keycode,
		}
	}
}

/// Information about a key press or release signal.
#[derive(Clone, Debug, Decode, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cbor(array)]
pub struct KeySignal<'buffer> {
	/// The address of the keyboard component.
	#[b(0)]
	pub keyboard: Address,

	/// The character that was pressed or released, if any.
	///
	/// This is `None` if the signal does not produce a character. Examples of
	/// non-character-producing key signals are signals related to lock keys, arrow keys, modifier
	/// keys, navigation keys, or other special keys.
	#[b(1)]
	#[cbor(decode_with = "KeySignal::decode_character")]
	pub character: Option<char>,

	/// The keycode that was pressed or released, if any.
	///
	/// This is `None` if the signal does not have a keycode. One example of a
	/// non-keycode-producing key signal is a composed character; in this case, the individual
	/// keystrokes are not reported, but the composed character is reported as a character without
	/// associated keycode. Another example is a Windows key (aka super key) or Menu key, both of
	/// which (under some operating systems at least) send key signals with neither `character` nor
	/// `keycode` filled in.
	#[b(2)]
	#[cbor(decode_with = "KeySignal::decode_keycode")]
	pub keycode: Option<NonZeroU32>,

	/// The name of the player who pressed or released the key.
	///
	/// This is `None` if reporting of player names is disabled in the mod configuration.
	#[b(3)]
	pub player: Option<&'buffer str>,
}

impl<'buffer> KeySignal<'buffer> {
	/// The name of the signal sent when a key is pressed.
	///
	/// This includes typematic repeats of held keys.
	pub const KEY_DOWN: &'static str = "key_down";

	/// The name of the signal sent when a key is released.
	pub const KEY_UP: &'static str = "key_up";

	/// Decodes the `character` field of a `KeySignal`.
	///
	/// This field is encoded as a double-precision floating point number in CBOR and needs to be
	/// converted to its proper type.
	fn decode_character<Context>(
		d: &mut Decoder<'buffer>,
		_: &mut Context,
	) -> Result<Option<char>, minicbor::decode::Error> {
		// Unicode code points are 0≤N≤0x10FFFF.
		let character = decode_u32_from_signal(d)?;
		if character == 0 {
			Ok(None)
		} else {
			Ok(char::from_u32(character))
		}
	}

	/// Decodes the `keycode` field of a `KeySignal`.
	///
	/// This field is encoded as a double-precision floating point number in CBOR and needs to be
	/// converted to its proper type.
	fn decode_keycode<Context>(
		d: &mut Decoder<'buffer>,
		_: &mut Context,
	) -> Result<Option<NonZeroU32>, minicbor::decode::Error> {
		// Keycodes are small nonnegative numbers.
		let keycode = decode_u32_from_signal(d)?;
		Ok(NonZeroU32::new(keycode))
	}

	/// Discards the player name information from this value.
	#[must_use]
	pub fn to_basic(&self) -> BasicKeySignal {
		BasicKeySignal::from(self)
	}
}

/// Information about a clipboard paste signal.
#[derive(Clone, Debug, Decode, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cbor(array)]
pub struct ClipboardSignal<'buffer> {
	/// The address of the keyboard component.
	#[b(0)]
	pub keyboard: Address,

	/// The text that was pasted.
	#[b(1)]
	pub text: &'buffer str,

	/// The name of the player who pasted the text.
	///
	/// This is `None` if reporting of player names is disabled in the mod configuration.
	#[b(2)]
	pub player: Option<&'buffer str>,
}

impl ClipboardSignal<'_> {
	/// The name of the signal sent when text is pasted from the clipboard.
	pub const SIGNAL: &'static str = "clipboard";
}