react-x11 2.13.0 → 2.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +11 -8
- package/src/Reconciler.js +34 -21
- package/src/components/Select.js +8 -2
- package/src/events.js +8 -2
- package/src/index.d.ts +5 -0
- package/src/nodes/boxpaint.js +9 -0
- package/src/nodes/preedit.js +64 -14
- package/src/scale.js +52 -22
- package/src/screencolor.js +104 -17
- package/src/wayland/app.js +560 -0
- package/src/wayland/backendwindow.js +1123 -0
- package/src/wayland/clipboard.js +326 -0
- package/src/wayland/connection.js +482 -0
- package/src/wayland/context2d.js +2133 -0
- package/src/wayland/decorations.js +476 -0
- package/src/wayland/dmabuf.js +89 -0
- package/src/wayland/dnd.js +581 -0
- package/src/wayland/fdutil.js +108 -0
- package/src/wayland/framestyle.js +257 -0
- package/src/wayland/glarea.js +371 -0
- package/src/wayland/glcontext.js +415 -0
- package/src/wayland/glyphatlas.js +237 -0
- package/src/wayland/input.js +417 -0
- package/src/wayland/keysymnames.js +35 -0
- package/src/wayland/layershell.js +363 -0
- package/src/wayland/outputs.js +601 -0
- package/src/wayland/protocols/cursor-shape-v1.json +1 -0
- package/src/wayland/protocols/ext-idle-notify-v1.json +1 -0
- package/src/wayland/protocols/ext-image-capture-source-v1.json +1 -0
- package/src/wayland/protocols/ext-image-copy-capture-v1.json +1 -0
- package/src/wayland/protocols/fractional-scale-v1.json +1 -0
- package/src/wayland/protocols/index.json +127 -0
- package/src/wayland/protocols/keyboard-shortcuts-inhibit-unstable-v1.json +1 -0
- package/src/wayland/protocols/linux-dmabuf-v1.json +1 -0
- package/src/wayland/protocols/pointer-constraints-unstable-v1.json +1 -0
- package/src/wayland/protocols/presentation-time.json +1 -0
- package/src/wayland/protocols/primary-selection-unstable-v1.json +1 -0
- package/src/wayland/protocols/relative-pointer-unstable-v1.json +1 -0
- package/src/wayland/protocols/tablet-v2.json +1 -0
- package/src/wayland/protocols/text-input-unstable-v3.json +1 -0
- package/src/wayland/protocols/viewporter.json +1 -0
- package/src/wayland/protocols/wayland.json +1 -0
- package/src/wayland/protocols/wlr-layer-shell-unstable-v1.json +1 -0
- package/src/wayland/protocols/wlr-screencopy-unstable-v1.json +1 -0
- package/src/wayland/protocols/xdg-activation-v1.json +1 -0
- package/src/wayland/protocols/xdg-decoration-unstable-v1.json +1 -0
- package/src/wayland/protocols/xdg-output-unstable-v1.json +1 -0
- package/src/wayland/protocols/xdg-shell.json +1 -0
- package/src/wayland/protocols/xdg-toplevel-icon-v1.json +1 -0
- package/src/wayland/readback.js +99 -0
- package/src/wayland/screencopy.js +584 -0
- package/src/wayland/seat.js +584 -0
- package/src/wayland/shm.js +226 -0
- package/src/wayland/ssd.js +106 -0
- package/src/wayland/surface.js +123 -0
- package/src/wayland/swapchain.js +411 -0
- package/src/wayland/tablet.js +522 -0
- package/src/wayland/target.js +263 -0
- package/src/wayland/text.js +113 -0
- package/src/wayland/textinput.js +671 -0
- package/src/wayland/touch.js +284 -0
- package/src/wayland/window.js +827 -0
- package/src/wayland/xkb.js +425 -0
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
// Graphics tablets: `zwp_tablet_manager_v2`, the seat's tablet seat, and
|
|
2
|
+
// its tools.
|
|
3
|
+
//
|
|
4
|
+
// A pen is not a pointer to Wayland, and almost exactly one to react-x11.
|
|
5
|
+
// The protocol gives every `wl_seat` a *tablet seat* that announces
|
|
6
|
+
// tablets, tools and pads as objects of their own. A tool has a type (pen,
|
|
7
|
+
// eraser, airbrush, the puck the protocol calls a "mouse"), the set of axes
|
|
8
|
+
// it can report, and a life of its own: `proximity_in` names the surface it
|
|
9
|
+
// is over, `motion`/`pressure`/`tilt`/... describe the frame, `down`/`up`
|
|
10
|
+
// say whether it touches the tablet, `button` is a barrel button, and
|
|
11
|
+
// `frame(time)` closes the group. As with `wl_pointer`, the client owns the
|
|
12
|
+
// cursor while the tool is over its surface — through cursor-shape's
|
|
13
|
+
// tablet-tool device, with the same shape names the pointer uses.
|
|
14
|
+
//
|
|
15
|
+
// What this does with it is emulate the pointer, the way X does: on X11 a
|
|
16
|
+
// tablet is an XI2 slave of the master pointer and every control in the
|
|
17
|
+
// tree sees a pen as presses and motion with no idea a pen was involved. So
|
|
18
|
+
// proximity is an `enter`, `down`/`up` are button 1 carrying the down's
|
|
19
|
+
// serial (what `xdg_toplevel.move` wants when the pen lands on the
|
|
20
|
+
// titlebar), the barrel buttons are 3 and 2 — BTN_STYLUS and BTN_STYLUS2,
|
|
21
|
+
// the desktop's own mapping — a puck's buttons are themselves, and
|
|
22
|
+
// `proximity_out` releases anything still held before it leaves. The
|
|
23
|
+
// emulated events are the seat's own pointer events and take the same
|
|
24
|
+
// route through `input.js`; the axes ride along on them in the DOM's names
|
|
25
|
+
// and ranges — `pointerType`, `pressure`, `tiltX`/`tiltY`, `rotation`,
|
|
26
|
+
// `distance`, `tangentialPressure` — on these events only, so a mouse event
|
|
27
|
+
// is unchanged and the X11 backend never sees a field it did not send.
|
|
28
|
+
//
|
|
29
|
+
// Everything a frame reports is accumulated and emitted at `frame`, in the
|
|
30
|
+
// order a pointer would have produced it: enter, motion, presses, releases,
|
|
31
|
+
// leave. Pads (the buttons, rings and strips on the tablet itself) are
|
|
32
|
+
// accepted and left alone: the tree has no vocabulary for them.
|
|
33
|
+
|
|
34
|
+
import { BUTTON_NUMBER, BUTTON_MASK, CURSOR_SHAPES, PRESSED } from './seat.js';
|
|
35
|
+
import { BTN_TOUCH } from './touch.js';
|
|
36
|
+
|
|
37
|
+
/** `zwp_tablet_tool_v2.type`. */
|
|
38
|
+
export const TOOL_TYPE = {
|
|
39
|
+
PEN: 0x140,
|
|
40
|
+
ERASER: 0x141,
|
|
41
|
+
BRUSH: 0x142,
|
|
42
|
+
PENCIL: 0x143,
|
|
43
|
+
AIRBRUSH: 0x144,
|
|
44
|
+
FINGER: 0x145,
|
|
45
|
+
MOUSE: 0x146,
|
|
46
|
+
LENS: 0x147,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const TOOL_NAME = {
|
|
50
|
+
[TOOL_TYPE.PEN]: 'pen',
|
|
51
|
+
[TOOL_TYPE.ERASER]: 'eraser',
|
|
52
|
+
[TOOL_TYPE.BRUSH]: 'brush',
|
|
53
|
+
[TOOL_TYPE.PENCIL]: 'pencil',
|
|
54
|
+
[TOOL_TYPE.AIRBRUSH]: 'airbrush',
|
|
55
|
+
[TOOL_TYPE.FINGER]: 'finger',
|
|
56
|
+
[TOOL_TYPE.MOUSE]: 'mouse',
|
|
57
|
+
[TOOL_TYPE.LENS]: 'lens',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/** The DOM `pointerType` a tool type stands for. */
|
|
61
|
+
const POINTER_TYPE = {
|
|
62
|
+
pen: 'pen',
|
|
63
|
+
eraser: 'eraser',
|
|
64
|
+
brush: 'pen',
|
|
65
|
+
pencil: 'pen',
|
|
66
|
+
airbrush: 'pen',
|
|
67
|
+
finger: 'touch',
|
|
68
|
+
mouse: 'mouse',
|
|
69
|
+
lens: 'mouse',
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/** `zwp_tablet_tool_v2.capability`. */
|
|
73
|
+
export const TOOL_CAP = {
|
|
74
|
+
TILT: 1,
|
|
75
|
+
PRESSURE: 2,
|
|
76
|
+
DISTANCE: 3,
|
|
77
|
+
ROTATION: 4,
|
|
78
|
+
SLIDER: 5,
|
|
79
|
+
WHEEL: 6,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const CAP_NAME = {
|
|
83
|
+
[TOOL_CAP.TILT]: 'tilt',
|
|
84
|
+
[TOOL_CAP.PRESSURE]: 'pressure',
|
|
85
|
+
[TOOL_CAP.DISTANCE]: 'distance',
|
|
86
|
+
[TOOL_CAP.ROTATION]: 'rotation',
|
|
87
|
+
[TOOL_CAP.SLIDER]: 'slider',
|
|
88
|
+
[TOOL_CAP.WHEEL]: 'wheel',
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/** The stylus buttons, as evdev numbers them. */
|
|
92
|
+
export const BTN_STYLUS = 0x14b;
|
|
93
|
+
export const BTN_STYLUS2 = 0x14c;
|
|
94
|
+
export const BTN_STYLUS3 = 0x149;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Tool button -> the button number the tree speaks: the barrel buttons as
|
|
98
|
+
* the desktop maps them (side button is a right click, the second a middle
|
|
99
|
+
* click, a third the "back" button), a puck's as themselves.
|
|
100
|
+
*/
|
|
101
|
+
const TOOL_BUTTON_NUMBER = {
|
|
102
|
+
...BUTTON_NUMBER,
|
|
103
|
+
[BTN_STYLUS]: 3,
|
|
104
|
+
[BTN_STYLUS2]: 2,
|
|
105
|
+
[BTN_STYLUS3]: 8,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/** pressure, distance and the slider are normalised to this by the protocol */
|
|
109
|
+
const AXIS_MAX = 65535;
|
|
110
|
+
|
|
111
|
+
export class WaylandTablet {
|
|
112
|
+
/**
|
|
113
|
+
* Bind the manager and open the seat's tablet seat, or answer null where
|
|
114
|
+
* the compositor has no tablet protocol — which is a missing capability,
|
|
115
|
+
* not an error.
|
|
116
|
+
*
|
|
117
|
+
* @param {import('./connection.js').WaylandConnection} conn
|
|
118
|
+
* @param {import('./seat.js').WaylandSeat} seat
|
|
119
|
+
*/
|
|
120
|
+
static async bind(conn, seat) {
|
|
121
|
+
const manager = await conn.bind('zwp_tablet_manager_v2');
|
|
122
|
+
if (!manager) return null;
|
|
123
|
+
const tablet = new WaylandTablet(seat, manager);
|
|
124
|
+
seat.tablet = tablet;
|
|
125
|
+
return tablet;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
constructor(seat, manager) {
|
|
129
|
+
this.seat = seat;
|
|
130
|
+
this.manager = manager;
|
|
131
|
+
this.tabletSeat = manager.$.get_tablet_seat(seat.seat.id);
|
|
132
|
+
/** tablets by proxy id: `{ id, name, vid, pid, path, bustype }` */
|
|
133
|
+
this.tablets = new Map();
|
|
134
|
+
/** tools by proxy id, once their description is complete */
|
|
135
|
+
this.tools = new Map();
|
|
136
|
+
this._pads = new Set();
|
|
137
|
+
// the seat's cursor is the cursor of every pointing device: a tool in
|
|
138
|
+
// proximity follows what the tree (or the frame) last asked for
|
|
139
|
+
this._onCursor = (name) => {
|
|
140
|
+
for (const tool of this.tools.values()) tool.applyCursor(name);
|
|
141
|
+
};
|
|
142
|
+
seat.on('cursor', this._onCursor);
|
|
143
|
+
this._wire();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
_wire() {
|
|
147
|
+
const ts = this.tabletSeat;
|
|
148
|
+
ts.on('tablet_added', (proxy) => {
|
|
149
|
+
const info = {
|
|
150
|
+
id: proxy.id,
|
|
151
|
+
name: null,
|
|
152
|
+
vid: 0,
|
|
153
|
+
pid: 0,
|
|
154
|
+
path: null,
|
|
155
|
+
bustype: null,
|
|
156
|
+
};
|
|
157
|
+
proxy.on('name', (name) => (info.name = name));
|
|
158
|
+
proxy.on('id', (vid, pid) => Object.assign(info, { vid, pid }));
|
|
159
|
+
proxy.on('path', (path) => (info.path = path));
|
|
160
|
+
proxy.on('bustype', (bustype) => (info.bustype = bustype));
|
|
161
|
+
proxy.on('done', () => {
|
|
162
|
+
this.tablets.set(proxy.id, info);
|
|
163
|
+
this.seat.emit('tabletadded', info);
|
|
164
|
+
});
|
|
165
|
+
proxy.on('removed', () => {
|
|
166
|
+
this.tablets.delete(proxy.id);
|
|
167
|
+
proxy.$.destroy();
|
|
168
|
+
this.seat.emit('tabletremoved', info);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
ts.on('tool_added', (proxy) => new TabletTool(this, proxy));
|
|
172
|
+
ts.on('pad_added', (proxy) => {
|
|
173
|
+
this._pads.add(proxy);
|
|
174
|
+
proxy.on('removed', () => {
|
|
175
|
+
this._pads.delete(proxy);
|
|
176
|
+
proxy.$.destroy();
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
destroy() {
|
|
182
|
+
this.seat.off('cursor', this._onCursor);
|
|
183
|
+
for (const tool of [...this.tools.values()]) tool._destroy();
|
|
184
|
+
this.tools.clear();
|
|
185
|
+
this.tablets.clear();
|
|
186
|
+
this._pads.clear();
|
|
187
|
+
this.tabletSeat.removeAllListeners();
|
|
188
|
+
try {
|
|
189
|
+
this.tabletSeat.$.destroy();
|
|
190
|
+
} catch {
|
|
191
|
+
/* the connection is going */
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* One tool, from `tool_added` to `removed`. Its description (`type`,
|
|
198
|
+
* `capability`, the hardware ids) arrives first and is complete at `done`;
|
|
199
|
+
* only then is it listed on the tablet seat and announced as `tooladded`.
|
|
200
|
+
*/
|
|
201
|
+
export class TabletTool {
|
|
202
|
+
constructor(tablet, proxy) {
|
|
203
|
+
this.tablet = tablet;
|
|
204
|
+
this.seat = tablet.seat;
|
|
205
|
+
this.proxy = proxy;
|
|
206
|
+
this.id = proxy.id;
|
|
207
|
+
this.type = TOOL_TYPE.PEN;
|
|
208
|
+
/** 'pen' | 'eraser' | 'brush' | 'pencil' | 'airbrush' | 'finger' | 'mouse' | 'lens' */
|
|
209
|
+
this.typeName = 'pen';
|
|
210
|
+
/** the DOM `pointerType` the emulated events carry */
|
|
211
|
+
this.pointerType = 'pen';
|
|
212
|
+
/** axis names the tool reports: 'pressure', 'tilt', ... */
|
|
213
|
+
this.capabilities = new Set();
|
|
214
|
+
this.hardwareSerial = null;
|
|
215
|
+
this.wacomId = null;
|
|
216
|
+
/** the `wl_surface` id the tool is over, or null when out of proximity */
|
|
217
|
+
this.surface = null;
|
|
218
|
+
this.tabletId = null;
|
|
219
|
+
/** the `proximity_in` serial — what `set_cursor` for this tool needs */
|
|
220
|
+
this.proximitySerial = 0;
|
|
221
|
+
this.x = 0;
|
|
222
|
+
this.y = 0;
|
|
223
|
+
this.pressure = 0;
|
|
224
|
+
this.distance = 0;
|
|
225
|
+
this.tiltX = 0;
|
|
226
|
+
this.tiltY = 0;
|
|
227
|
+
this.rotation = 0;
|
|
228
|
+
this.slider = 0;
|
|
229
|
+
/** the tool touches the tablet */
|
|
230
|
+
this.down = false;
|
|
231
|
+
/** buttons held, in press order: `{ num, evdev }` */
|
|
232
|
+
this.held = [];
|
|
233
|
+
this.cursorDevice = this.seat._cursorShapes
|
|
234
|
+
? this.seat._cursorShapes.$.get_tablet_tool_v2(proxy.id)
|
|
235
|
+
: null;
|
|
236
|
+
this._frame = null;
|
|
237
|
+
this._wire();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
_wire() {
|
|
241
|
+
const p = this.proxy;
|
|
242
|
+
p.on('type', (type) => {
|
|
243
|
+
this.type = type;
|
|
244
|
+
this.typeName = TOOL_NAME[type] ?? 'pen';
|
|
245
|
+
this.pointerType = POINTER_TYPE[this.typeName] ?? 'pen';
|
|
246
|
+
});
|
|
247
|
+
p.on('hardware_serial', (hi, lo) => (this.hardwareSerial = hex64(hi, lo)));
|
|
248
|
+
p.on('hardware_id_wacom', (hi, lo) => (this.wacomId = hex64(hi, lo)));
|
|
249
|
+
p.on('capability', (cap) => this.capabilities.add(CAP_NAME[cap] ?? cap));
|
|
250
|
+
p.on('done', () => {
|
|
251
|
+
this.tablet.tools.set(this.id, this);
|
|
252
|
+
this.seat.emit('tooladded', this);
|
|
253
|
+
});
|
|
254
|
+
p.on('removed', () => this._remove());
|
|
255
|
+
|
|
256
|
+
// The frame's events, accumulated; `frame` turns them into pointer events.
|
|
257
|
+
p.on('proximity_in', (serial, tabletId, surfaceId) => {
|
|
258
|
+
const f = this._open();
|
|
259
|
+
f.proximityIn = serial;
|
|
260
|
+
f.tablet = tabletId;
|
|
261
|
+
f.surface = surfaceId;
|
|
262
|
+
});
|
|
263
|
+
p.on('proximity_out', () => (this._open().proximityOut = true));
|
|
264
|
+
p.on('down', (serial) => (this._open().down = serial));
|
|
265
|
+
p.on('up', () => (this._open().up = true));
|
|
266
|
+
p.on('motion', (x, y) => {
|
|
267
|
+
this.x = x;
|
|
268
|
+
this.y = y;
|
|
269
|
+
this._open().moved = true;
|
|
270
|
+
});
|
|
271
|
+
p.on('pressure', (v) => this._axis('pressure', v / AXIS_MAX));
|
|
272
|
+
p.on('distance', (v) => this._axis('distance', v / AXIS_MAX));
|
|
273
|
+
p.on('tilt', (tx, ty) => {
|
|
274
|
+
this._axis('tiltX', tx);
|
|
275
|
+
this._axis('tiltY', ty);
|
|
276
|
+
});
|
|
277
|
+
p.on('rotation', (degrees) => this._axis('rotation', degrees));
|
|
278
|
+
p.on('slider', (position) => this._axis('slider', position / AXIS_MAX));
|
|
279
|
+
p.on('wheel', (degrees, clicks) => {
|
|
280
|
+
const f = this._open();
|
|
281
|
+
f.wheel = {
|
|
282
|
+
degrees: (f.wheel?.degrees ?? 0) + degrees,
|
|
283
|
+
clicks: (f.wheel?.clicks ?? 0) + clicks,
|
|
284
|
+
};
|
|
285
|
+
});
|
|
286
|
+
p.on('button', (serial, button, state) =>
|
|
287
|
+
this._open().buttons.push({ serial, button, state }),
|
|
288
|
+
);
|
|
289
|
+
p.on('frame', (time) => this._flush(time));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
_open() {
|
|
293
|
+
return (this._frame ??= {
|
|
294
|
+
proximityIn: null,
|
|
295
|
+
proximityOut: false,
|
|
296
|
+
down: null,
|
|
297
|
+
up: false,
|
|
298
|
+
moved: false,
|
|
299
|
+
axes: false,
|
|
300
|
+
buttons: [],
|
|
301
|
+
wheel: null,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
_axis(name, value) {
|
|
306
|
+
this[name] = value;
|
|
307
|
+
this._open().axes = true;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** The axes as the emulated events carry them. */
|
|
311
|
+
_device() {
|
|
312
|
+
return {
|
|
313
|
+
pointerType: this.pointerType,
|
|
314
|
+
toolType: this.typeName,
|
|
315
|
+
// the DOM's answer for a tool without a pressure axis: half while it
|
|
316
|
+
// is in contact, none otherwise
|
|
317
|
+
pressure: this.capabilities.has('pressure')
|
|
318
|
+
? this.pressure
|
|
319
|
+
: this.held.length
|
|
320
|
+
? 0.5
|
|
321
|
+
: 0,
|
|
322
|
+
tiltX: this.tiltX,
|
|
323
|
+
tiltY: this.tiltY,
|
|
324
|
+
rotation: this.rotation,
|
|
325
|
+
distance: this.distance,
|
|
326
|
+
tangentialPressure: this.slider,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* The frame is closed: emit what it described, as the pointer events the
|
|
332
|
+
* router already routes, in the order a pointer would have sent them.
|
|
333
|
+
*/
|
|
334
|
+
_flush(time) {
|
|
335
|
+
const f = this._frame;
|
|
336
|
+
this._frame = null;
|
|
337
|
+
if (!f) return;
|
|
338
|
+
const seat = this.seat;
|
|
339
|
+
const batch = [];
|
|
340
|
+
if (f.proximityIn != null) {
|
|
341
|
+
seat.lastSerial = f.proximityIn;
|
|
342
|
+
this.surface = f.surface;
|
|
343
|
+
this.tabletId = f.tablet;
|
|
344
|
+
this.proximitySerial = f.proximityIn;
|
|
345
|
+
batch.push({
|
|
346
|
+
type: 'enter',
|
|
347
|
+
serial: f.proximityIn,
|
|
348
|
+
surface: this.surface,
|
|
349
|
+
x: this.x,
|
|
350
|
+
y: this.y,
|
|
351
|
+
device: this._device(),
|
|
352
|
+
});
|
|
353
|
+
this.applyCursor(seat._cursor);
|
|
354
|
+
}
|
|
355
|
+
// nothing to say about a tool that is not over one of our surfaces
|
|
356
|
+
if (this.surface == null) return;
|
|
357
|
+
if (f.moved || f.axes) {
|
|
358
|
+
batch.push({
|
|
359
|
+
type: 'motion',
|
|
360
|
+
time,
|
|
361
|
+
x: this.x,
|
|
362
|
+
y: this.y,
|
|
363
|
+
surface: this.surface,
|
|
364
|
+
device: this._device(),
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
if (f.down != null) {
|
|
368
|
+
seat.lastSerial = f.down;
|
|
369
|
+
seat.lastPressSerial = f.down;
|
|
370
|
+
this.down = true;
|
|
371
|
+
this._press(batch, 1, BTN_TOUCH, f.down, time);
|
|
372
|
+
}
|
|
373
|
+
for (const b of f.buttons) {
|
|
374
|
+
seat.lastSerial = b.serial;
|
|
375
|
+
const num = TOOL_BUTTON_NUMBER[b.button] ?? 0;
|
|
376
|
+
if (!num) continue;
|
|
377
|
+
if (b.state === PRESSED) {
|
|
378
|
+
seat.lastPressSerial = b.serial;
|
|
379
|
+
this._press(batch, num, b.button, b.serial, time);
|
|
380
|
+
} else {
|
|
381
|
+
this._release(batch, num, b.serial, time);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (f.up) {
|
|
385
|
+
this.down = false;
|
|
386
|
+
this._release(batch, 1, seat.lastSerial, time);
|
|
387
|
+
}
|
|
388
|
+
if (f.wheel) {
|
|
389
|
+
batch.push({
|
|
390
|
+
type: 'wheel',
|
|
391
|
+
time,
|
|
392
|
+
deltaX: 0,
|
|
393
|
+
deltaY: f.wheel.clicks,
|
|
394
|
+
pixelsX: 0,
|
|
395
|
+
pixelsY: f.wheel.degrees,
|
|
396
|
+
smooth: false,
|
|
397
|
+
x: this.x,
|
|
398
|
+
y: this.y,
|
|
399
|
+
surface: this.surface,
|
|
400
|
+
device: this._device(),
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
if (f.proximityOut) this._leave(batch, time);
|
|
404
|
+
this._emit(batch);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
_press(batch, num, evdev, serial, time) {
|
|
408
|
+
if (this.held.some((h) => h.num === num)) return;
|
|
409
|
+
const seat = this.seat;
|
|
410
|
+
const before = seat.buttonMask;
|
|
411
|
+
seat.buttonMask |= BUTTON_MASK[num] ?? 0;
|
|
412
|
+
this.held.push({ num, evdev });
|
|
413
|
+
batch.push(this._button('buttonpress', num, evdev, serial, time, before));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
_release(batch, num, serial, time) {
|
|
417
|
+
const i = this.held.findIndex((h) => h.num === num);
|
|
418
|
+
if (i < 0) return;
|
|
419
|
+
const seat = this.seat;
|
|
420
|
+
const before = seat.buttonMask;
|
|
421
|
+
seat.buttonMask &= ~(BUTTON_MASK[num] ?? 0);
|
|
422
|
+
const [{ evdev }] = this.held.splice(i, 1);
|
|
423
|
+
batch.push(this._button('buttonrelease', num, evdev, serial, time, before));
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
_button(type, num, evdev, serial, time, state) {
|
|
427
|
+
return {
|
|
428
|
+
type,
|
|
429
|
+
serial,
|
|
430
|
+
time,
|
|
431
|
+
button: num,
|
|
432
|
+
evdev,
|
|
433
|
+
x: this.x,
|
|
434
|
+
y: this.y,
|
|
435
|
+
surface: this.surface,
|
|
436
|
+
state,
|
|
437
|
+
device: this._device(),
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Out of proximity: whatever is still held is released first, newest
|
|
443
|
+
* first, then the pointer leaves — and if a real pointer is over one of
|
|
444
|
+
* our surfaces it is the pointer again, so the router's
|
|
445
|
+
* window-under-the-pointer is right.
|
|
446
|
+
*/
|
|
447
|
+
_leave(batch, time) {
|
|
448
|
+
const seat = this.seat;
|
|
449
|
+
for (const { num } of [...this.held].reverse())
|
|
450
|
+
this._release(batch, num, seat.lastSerial, time);
|
|
451
|
+
this.down = false;
|
|
452
|
+
batch.push({
|
|
453
|
+
type: 'leave',
|
|
454
|
+
serial: seat.lastSerial,
|
|
455
|
+
surface: this.surface,
|
|
456
|
+
device: this._device(),
|
|
457
|
+
});
|
|
458
|
+
this.surface = null;
|
|
459
|
+
this.tabletId = null;
|
|
460
|
+
this.proximitySerial = 0;
|
|
461
|
+
if (seat.pointerSurface != null) {
|
|
462
|
+
batch.push({
|
|
463
|
+
type: 'enter',
|
|
464
|
+
serial: seat.lastSerial,
|
|
465
|
+
surface: seat.pointerSurface,
|
|
466
|
+
x: seat.pointerX,
|
|
467
|
+
y: seat.pointerY,
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
_emit(batch) {
|
|
473
|
+
if (batch.length === 0) return;
|
|
474
|
+
for (const ev of batch) this.seat.emit(ev.type, ev);
|
|
475
|
+
this.seat.emit('frame', batch);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* The cursor over the surface the tool is in, by the same names the
|
|
480
|
+
* pointer uses. Without cursor-shape there is no way to name one, as for
|
|
481
|
+
* the pointer; `'none'` hides it with a null surface.
|
|
482
|
+
*/
|
|
483
|
+
applyCursor(name) {
|
|
484
|
+
if (this.surface == null) return;
|
|
485
|
+
if (name === 'none') {
|
|
486
|
+
this.proxy.$.set_cursor(this.proximitySerial, null, 0, 0);
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
if (!this.cursorDevice) return;
|
|
490
|
+
const shape = CURSOR_SHAPES[name] ?? CURSOR_SHAPES.default;
|
|
491
|
+
this.cursorDevice.$.set_shape(this.proximitySerial, shape);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/** The tool is gone (unplugged mid-stroke, say): finish what it held. */
|
|
495
|
+
_remove() {
|
|
496
|
+
if (this.surface != null) {
|
|
497
|
+
const batch = [];
|
|
498
|
+
this._leave(batch, this.seat.lastSerial);
|
|
499
|
+
this._emit(batch);
|
|
500
|
+
}
|
|
501
|
+
const known = this.tablet.tools.delete(this.id);
|
|
502
|
+
this._destroy();
|
|
503
|
+
if (known) this.seat.emit('toolremoved', this);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
_destroy() {
|
|
507
|
+
this.proxy.removeAllListeners();
|
|
508
|
+
try {
|
|
509
|
+
this.cursorDevice?.$.destroy();
|
|
510
|
+
this.proxy.$.destroy();
|
|
511
|
+
} catch {
|
|
512
|
+
/* the connection is going */
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/** Two 32-bit halves as one hex string, which is how the ids are read. */
|
|
518
|
+
function hex64(hi, lo) {
|
|
519
|
+
return hi
|
|
520
|
+
? hi.toString(16) + lo.toString(16).padStart(8, '0')
|
|
521
|
+
: lo.toString(16);
|
|
522
|
+
}
|