forked from moonlight-stream/moonlight-chrome
-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathinput.cpp
287 lines (236 loc) · 9.66 KB
/
input.cpp
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
#include "moonlight.hpp"
#include "ppapi/c/ppb_input_event.h"
#include "ppapi/cpp/input_event.h"
#include <Limelight.h>
#include <math.h>
#define KEY_PREFIX 0x80
#define TOUCH_DEAD_ZONE_DELAY 0.250
#define TOUCH_DEAD_ZONE_RADIUS 50
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
static int ConvertPPButtonToLiButton(PP_InputEvent_MouseButton ppButton) {
switch (ppButton) {
case PP_INPUTEVENT_MOUSEBUTTON_LEFT:
return BUTTON_LEFT;
case PP_INPUTEVENT_MOUSEBUTTON_MIDDLE:
return BUTTON_MIDDLE;
case PP_INPUTEVENT_MOUSEBUTTON_RIGHT:
return BUTTON_RIGHT;
default:
return 0;
}
}
void MoonlightInstance::DidLockMouse(int32_t result) {
m_MouseLocked = (result == PP_OK);
if (m_MouseLocked) {
// Request an IDR frame to dump the frame queue that may have
// built up from the GL pipeline being stalled.
g_Instance->m_RequestIdrFrame = true;
}
}
void MoonlightInstance::MouseLockLost() {
m_MouseLocked = false;
}
static char GetModifierFlags(const pp::InputEvent& event) {
uint32_t modifiers = event.GetModifiers();
char flags = 0;
if (modifiers & PP_INPUTEVENT_MODIFIER_SHIFTKEY) {
flags |= MODIFIER_SHIFT;
}
if (modifiers & PP_INPUTEVENT_MODIFIER_CONTROLKEY) {
flags |= MODIFIER_CTRL;
}
if (modifiers & PP_INPUTEVENT_MODIFIER_ALTKEY) {
flags |= MODIFIER_ALT;
}
return flags;
}
static uint32_t GetTranslatedKeyCode(const pp::KeyboardInputEvent& event) {
// For some reason, NaCl won't give us the real left and right
// VK codes for modifiers and instead gives us modifier flags
// to indicate whether the key is left or right. We have to
// convert these back to the original VK codes to before
// sending them to the PC.
switch (event.GetKeyCode()) {
// VK_SHIFT
case 0x10:
if (event.GetModifiers() & PP_INPUTEVENT_MODIFIER_ISLEFT) {
// VK_LSHIFT
return 0xA0;
}
else if (event.GetModifiers() & PP_INPUTEVENT_MODIFIER_ISRIGHT) {
// VK_RSHIFT
return 0xA1;
}
break;
// VK_CONTROL
case 0x11:
if (event.GetModifiers() & PP_INPUTEVENT_MODIFIER_ISLEFT) {
// VK_LCONTROL
return 0xA2;
}
else if (event.GetModifiers() & PP_INPUTEVENT_MODIFIER_ISRIGHT) {
// VK_RCONTROL
return 0xA3;
}
break;
// VK_MENU (Alt)
case 0x12:
if (event.GetModifiers() & PP_INPUTEVENT_MODIFIER_ISLEFT) {
// VK_LMENU
return 0xA4;
}
else if (event.GetModifiers() & PP_INPUTEVENT_MODIFIER_ISRIGHT) {
// VK_RMENU
return 0xA5;
}
break;
default:
break;
}
// We have to handle the ISKEYPAD modifier on macOS, and convert them
// to the correct numpad keycodes for Windows.
int32_t num = event.GetKeyCode() - 0x30;
if ((event.GetModifiers() & PP_INPUTEVENT_MODIFIER_ISKEYPAD) &&
num >= 0 && num <= 9) {
// Offset with numpad 0's virtual keycode
return num + 0x60;
}
return event.GetKeyCode();
}
void MoonlightInstance::ReportMouseMovement() {
if (m_MouseDeltaX != 0 || m_MouseDeltaY != 0) {
LiSendMouseMoveEvent(m_MouseDeltaX, m_MouseDeltaY);
m_MouseDeltaX = m_MouseDeltaY = 0;
}
if (m_AccumulatedTicks != 0) {
// We can have fractional ticks here, so multiply by WHEEL_DELTA
// to get actual scroll distance and use the high-res variant.
LiSendHighResScrollEvent(m_AccumulatedTicks * 120);
m_AccumulatedTicks = 0;
}
}
bool MoonlightInstance::HandleInputEvent(const pp::InputEvent& event) {
switch (event.GetType()) {
case PP_INPUTEVENT_TYPE_MOUSEDOWN: {
// Lock the mouse cursor when the user clicks on the stream
if (!m_MouseLocked) {
LockMouse(m_CallbackFactory.NewCallback(&MoonlightInstance::DidLockMouse));
// Assume it worked until we get a callback telling us otherwise
m_MouseLocked = true;
return true;
}
pp::MouseInputEvent mouseEvent(event);
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, ConvertPPButtonToLiButton(mouseEvent.GetButton()));
return true;
}
case PP_INPUTEVENT_TYPE_MOUSEMOVE: {
if (!m_MouseLocked) {
return false;
}
pp::MouseInputEvent mouseEvent(event);
pp::Point posDelta = mouseEvent.GetMovement();
// Wait to report mouse movement until the next input polling window
// to allow batching to occur which reduces overall input lag.
m_MouseDeltaX += posDelta.x();
m_MouseDeltaY += posDelta.y();
return true;
}
case PP_INPUTEVENT_TYPE_MOUSEUP: {
if (!m_MouseLocked) {
return false;
}
pp::MouseInputEvent mouseEvent(event);
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, ConvertPPButtonToLiButton(mouseEvent.GetButton()));
return true;
}
case PP_INPUTEVENT_TYPE_WHEEL: {
if (!m_MouseLocked) {
return false;
}
pp::WheelInputEvent wheelEvent(event);
// Accumulate the current tick value
m_AccumulatedTicks += wheelEvent.GetTicks().y();
return true;
}
case PP_INPUTEVENT_TYPE_KEYDOWN: {
if (!m_MouseLocked) {
return false;
}
pp::KeyboardInputEvent keyboardEvent(event);
char modifiers = GetModifierFlags(event);
uint32_t keyCode = GetTranslatedKeyCode(keyboardEvent);
if (modifiers == (MODIFIER_ALT | MODIFIER_CTRL | MODIFIER_SHIFT)) {
if (keyCode == 0x51) { // Q key
// Terminate the connection
StopConnection();
return true;
}
else {
// Wait until these keys come up to unlock the mouse
m_WaitingForAllModifiersUp = true;
}
}
if (event.GetModifiers() & PP_INPUTEVENT_MODIFIER_ISAUTOREPEAT) {
return true;
}
LiSendKeyboardEvent(KEY_PREFIX << 8 | keyCode,
KEY_ACTION_DOWN, modifiers);
return true;
}
case PP_INPUTEVENT_TYPE_KEYUP: {
if (!m_MouseLocked) {
return false;
}
pp::KeyboardInputEvent keyboardEvent(event);
char modifiers = GetModifierFlags(event);
uint32_t keyCode = GetTranslatedKeyCode(keyboardEvent);
// Check if all modifiers are up now
if (m_WaitingForAllModifiersUp && modifiers == 0) {
UnlockMouse();
m_MouseLocked = false;
m_WaitingForAllModifiersUp = false;
}
LiSendKeyboardEvent(KEY_PREFIX << 8 | keyCode,
KEY_ACTION_UP, modifiers);
return true;
}
case PP_INPUTEVENT_TYPE_TOUCHMOVE:
case PP_INPUTEVENT_TYPE_TOUCHSTART: {
pp::TouchInputEvent touchEvent(event);
pp::FloatPoint touchPoint = touchEvent.GetTouchByIndex(PP_TOUCHLIST_TYPE_TARGETTOUCHES, 0).position();
// Create a small deadzone for touch downs to allow more precise double-clicks
if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHMOVE ||
event.GetTimeStamp() - m_LastTouchUpTime > TOUCH_DEAD_ZONE_DELAY ||
sqrt(pow(m_LastTouchUpPoint.x() - touchPoint.x(), 2) +
pow(m_LastTouchUpPoint.y() - touchPoint.y(), 2)) > TOUCH_DEAD_ZONE_RADIUS) {
// Scale the touch coordinates to the video rect
//
// For some reason, the x coordinate is already relative to the plugin rect,
// while the y coordinate is not. No clue why that is the case but oh well...
short x = MIN(MAX(touchPoint.x(), 0), m_PluginRect.width());
short y = MIN(MAX(touchPoint.y() - m_PluginRect.y(), 0), m_PluginRect.height());
// Update the mouse position prior to sending the button down
LiSendMousePositionEvent(x, y, m_PluginRect.width(), m_PluginRect.height());
}
if (event.GetType() == PP_INPUTEVENT_TYPE_TOUCHSTART &&
touchEvent.GetTouchCount(PP_TOUCHLIST_TYPE_TARGETTOUCHES) == 1) {
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT);
}
return true;
}
case PP_INPUTEVENT_TYPE_TOUCHCANCEL:
case PP_INPUTEVENT_TYPE_TOUCHEND: {
pp::TouchInputEvent touchEvent(event);
if (touchEvent.GetTouchCount(PP_TOUCHLIST_TYPE_TARGETTOUCHES) == 1) {
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT);
m_LastTouchUpTime = event.GetTimeStamp();
m_LastTouchUpPoint = touchEvent.GetTouchByIndex(PP_TOUCHLIST_TYPE_TARGETTOUCHES, 0).position();
}
return true;
}
default: {
return false;
}
}
}