ntk 2.1.1 → 3.0.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/README.md +115 -110
- package/lib/app.js +48 -15
- package/lib/cleanup.js +14 -0
- package/lib/drawable.js +10 -16
- package/lib/events_map.js +81 -87
- package/lib/fontconfig.js +122 -14
- package/lib/glyphset.js +36 -18
- package/lib/image.js +138 -0
- package/lib/index.js +110 -52
- package/lib/path.js +734 -0
- package/lib/picture.js +76 -38
- package/lib/pixmap.js +34 -25
- package/lib/rasterize.js +178 -0
- package/lib/renderingcontext_2d.js +994 -517
- package/lib/renderingcontext_opengl.js +38 -31
- package/lib/renderingcontext_x11.js +56 -64
- package/lib/text/font.js +196 -0
- package/lib/text/fontmanager.js +233 -0
- package/lib/text/fontsource.js +179 -0
- package/lib/text/glyphs.js +456 -0
- package/lib/text/layout.js +356 -0
- package/lib/text/shape.js +141 -0
- package/lib/trapezoid.js +131 -0
- package/lib/widgets/css.js +676 -0
- package/lib/widgets/highlight.js +97 -0
- package/lib/widgets/htmlview.js +879 -0
- package/lib/widgets/markdown.js +148 -0
- package/lib/widgets/markdownview.js +487 -0
- package/lib/widgets/mermaid.js +720 -0
- package/lib/widgets/svgview.js +528 -0
- package/lib/widgets/tex.js +681 -0
- package/lib/window.js +568 -246
- package/package.json +35 -32
- package/.npmignore +0 -15
- package/examples/example-await.js +0 -23
- package/examples/example-co.js +0 -13
- package/examples/example.js +0 -14
- package/examples/fill-styles.js +0 -39
- package/examples/glclock.js +0 -100
- package/examples/glxpixmap.js +0 -12
- package/examples/gradient.js +0 -41
- package/examples/package.json +0 -17
- package/examples/simpletex.js +0 -72
- package/examples/teapot.js +0 -104
- package/examples/teapot.json +0 -6
- package/examples/text-rendering.js +0 -44
- package/lib/fontface.js +0 -109
- package/lib/widgets/webview.js +0 -33
package/README.md
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
ntk
|
|
2
2
|
===
|
|
3
3
|
|
|
4
|
-
node.js desktop UI toolkit
|
|
4
|
+
node.js desktop UI toolkit for X11
|
|
5
5
|
|
|
6
|
-
A set of wrappers around low level [node-x11](https://github.com/sidorares/node-x11)
|
|
6
|
+
A set of wrappers around the low level [node-x11](https://github.com/sidorares/node-x11)
|
|
7
|
+
module to simplify X Window UI programming — window creation, DOM-style event
|
|
8
|
+
handling, 2d/3d graphics — using API concepts you already know from the web.
|
|
9
|
+
|
|
10
|
+
Everything, including font rasterization, is pure JavaScript: `npm install`
|
|
11
|
+
never compiles anything.
|
|
12
|
+
|
|
13
|
+
**Docs & live playground:** <https://sidorares.github.io/ntk/> — the
|
|
14
|
+
playground runs ordinary ntk code in your browser against node-x11's
|
|
15
|
+
in-browser pure-JS X server (XRender included) with bundled fonts. The same server also works headless in node — see
|
|
16
|
+
[docs/xserver.md](docs/xserver.md) — so ntk apps and tests can run with no
|
|
17
|
+
real X server at all.
|
|
7
18
|
|
|
8
19
|
# Installation
|
|
9
20
|
|
|
@@ -11,141 +22,135 @@ A set of wrappers around low level [node-x11](https://github.com/sidorares/node-
|
|
|
11
22
|
npm install ntk
|
|
12
23
|
```
|
|
13
24
|
|
|
25
|
+
Requires Node.js >= 20.19 and an X server. Full documentation lives in
|
|
26
|
+
[docs/](docs/README.md).
|
|
27
|
+
|
|
14
28
|
## Basic usage
|
|
15
29
|
|
|
16
30
|
```js
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
});
|
|
31
|
+
import { createClient } from 'ntk';
|
|
32
|
+
|
|
33
|
+
const app = await createClient();
|
|
34
|
+
const wnd = app.createWindow({ width: 500, height: 300, title: 'Hello' });
|
|
35
|
+
wnd.on('mousedown', (ev) => wnd.setTitle(`click: ${ev.x},${ev.y}`));
|
|
36
|
+
wnd.map();
|
|
22
37
|
```
|
|
23
38
|
|
|
24
39
|
## 2d graphics
|
|
25
40
|
|
|
26
|
-
Each window (
|
|
41
|
+
Each window (or pixmap) can create a 2d canvas implementing the HTML
|
|
42
|
+
[context2d api](https://html.spec.whatwg.org/multipage/canvas.html#2dcontext)
|
|
43
|
+
via the
|
|
44
|
+
[XRender extension](https://www.x.org/releases/X11R7.7/doc/renderproto/renderproto.txt) —
|
|
45
|
+
paths (arcs, beziers, `Path2D` with SVG path data, non-zero/even-odd fill
|
|
46
|
+
rules), transforms with `save()`/`restore()`, clipping, `globalAlpha` and
|
|
47
|
+
Porter-Duff composite ops ([docs/context-2d.md](docs/context-2d.md)).
|
|
48
|
+
Most operations are performed on the X server side (image composition,
|
|
49
|
+
scaling, blur, text composition, gradients etc). Text is fully shaped in
|
|
50
|
+
pure JS — OpenType kerning/ligatures and complex scripts (fontkit), bidi
|
|
51
|
+
(bidi-js), automatic font fallback — rasterized by a built-in scanline
|
|
52
|
+
rasterizer and cached server-side as XRender glyphs, so drawing a line of
|
|
53
|
+
text costs about a byte per glyph on the wire. Font names resolve through
|
|
54
|
+
fontconfig (`fc-match`). Very large and continuously animated sizes render
|
|
55
|
+
as server-side trapezoids instead of cached bitmaps. A `TextLayout` engine
|
|
56
|
+
wraps styled text to a target width, a `MarkdownView` widget renders
|
|
57
|
+
markdown (with syntax-highlighted code fences, mermaid flowchart/sequence
|
|
58
|
+
diagrams, and KaTeX math via `TexView`) on top of it — see
|
|
59
|
+
[docs/text.md](docs/text.md), [docs/mermaid.md](docs/mermaid.md) and
|
|
60
|
+
[docs/tex.md](docs/tex.md).
|
|
61
|
+
|
|
62
|
+
PNG/JPEG images decode client-side (`loadImage`) and composite server-side
|
|
63
|
+
via `ctx.drawImage` ([docs/images.md](docs/images.md)). An `HtmlView`
|
|
64
|
+
widget renders a static HTML + CSS subset — block flow and flexbox laid
|
|
65
|
+
out by yoga-layout, no scripts, no network — with app-controlled link
|
|
66
|
+
navigation ([docs/html.md](docs/html.md)). An `SvgView` widget renders
|
|
67
|
+
static SVG (shapes, gradients, transforms, `use`) through the same 2d
|
|
68
|
+
pipeline ([docs/svg.md](docs/svg.md)).
|
|
27
69
|
|
|
28
70
|
```js
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
wnd.map();
|
|
71
|
+
import { createClient } from 'ntk';
|
|
72
|
+
|
|
73
|
+
const app = await createClient();
|
|
74
|
+
const wnd = app.createWindow({ width: 800, height: 600 });
|
|
75
|
+
const ctx = wnd.getContext('2d');
|
|
76
|
+
|
|
77
|
+
wnd.on('mousemove', (ev) => {
|
|
78
|
+
const gradient = ctx.createRadialGradient(0, 0, 10, ev.x, ev.y, 500);
|
|
79
|
+
gradient.addColorStop(0, 'red');
|
|
80
|
+
gradient.addColorStop(0.5, 'green');
|
|
81
|
+
gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
|
|
82
|
+
ctx.fillStyle = gradient;
|
|
83
|
+
ctx.fillRect(0, 0, ctx.width, ctx.height);
|
|
45
84
|
});
|
|
85
|
+
|
|
86
|
+
wnd.map();
|
|
46
87
|
```
|
|
47
88
|
|
|
48
|
-
`ctx.drawImage()` also accepts [node-canvas](https://github.com/Automattic/node-canvas)
|
|
89
|
+
`ctx.drawImage()` also accepts a [node-canvas](https://github.com/Automattic/node-canvas)
|
|
90
|
+
canvas as a source — for images with lots of drawing calls it might be more
|
|
91
|
+
efficient to draw locally and transfer pixels to the server when ready.
|
|
49
92
|
|
|
50
|
-
|
|
51
|
-
var ntk = require('ntk');
|
|
52
|
-
var Canvas = require('canvas');
|
|
93
|
+
## Frame pacing & networked displays
|
|
53
94
|
|
|
54
|
-
|
|
55
|
-
|
|
95
|
+
Noisy events (`resize`, `mousemove`, `expose`) are coalesced into paced
|
|
96
|
+
frames — the latest state wins, nothing queues up — and each frame is
|
|
97
|
+
fenced with a server round-trip, so rendering automatically slows to the
|
|
98
|
+
connection's real throughput instead of drawing a trail of stale updates
|
|
99
|
+
over ssh-forwarded displays. Animation uses the DOM-style
|
|
100
|
+
`requestAnimationFrame`:
|
|
56
101
|
|
|
57
|
-
|
|
58
|
-
|
|
102
|
+
```js
|
|
103
|
+
function frame(now) {
|
|
104
|
+
// ... draw ...
|
|
105
|
+
wnd.requestAnimationFrame(frame); // ~60fps locally, RTT-paced remotely
|
|
59
106
|
}
|
|
107
|
+
wnd.requestAnimationFrame(frame);
|
|
108
|
+
```
|
|
60
109
|
|
|
61
|
-
|
|
62
|
-
|
|
110
|
+
See [docs/window.md](docs/window.md) for the knobs (`frameInterval`,
|
|
111
|
+
`frameSync`, `coalesceEvents`) and the raw uncoalesced event stream.
|
|
63
112
|
|
|
64
|
-
|
|
113
|
+
## Resource management
|
|
65
114
|
|
|
66
|
-
|
|
67
|
-
drawSomething(canvasCtx);
|
|
68
|
-
ctx.drawImage(canvas);
|
|
69
|
-
}, 20);
|
|
115
|
+
Server-side resources support `using` / `await using` (Node 24+):
|
|
70
116
|
|
|
71
|
-
|
|
72
|
-
|
|
117
|
+
```js
|
|
118
|
+
{
|
|
119
|
+
await using app = await createClient();
|
|
120
|
+
using pixmap = app.createPixmap({ width: 256, height: 256, depth: 24 });
|
|
121
|
+
// ... draw ...
|
|
122
|
+
} // pixmap freed, connection closed
|
|
73
123
|
```
|
|
74
124
|
|
|
75
125
|
## 3d graphics
|
|
76
126
|
|
|
77
|
-
|
|
127
|
+
Only indirect GLX is supported, with most of the OpenGL 1.4 api implemented.
|
|
128
|
+
Note that on many systems indirect GLX is disabled by default —
|
|
129
|
+
[you'll need to enable it for gl to work](https://github.com/sidorares/node-x11/issues/117#issuecomment-214762185).
|
|
78
130
|
|
|
79
131
|
```js
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
function draw(gl) {
|
|
97
|
-
gl.MatrixMode(gl.MODELVIEW);
|
|
98
|
-
|
|
99
|
-
gl.ClearColor(0.3,0.3,0.3,0.0);
|
|
100
|
-
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
101
|
-
|
|
102
|
-
gl.LoadIdentity();
|
|
103
|
-
gl.Rotatef(-90 + 360*angle/width, 0, 0, 1);
|
|
104
|
-
gl.Begin(gl.TRIANGLE_STRIP);
|
|
105
|
-
gl.TexCoord2f(0.0,0.0);
|
|
106
|
-
gl.Vertex3f(-1, -1, 0);
|
|
107
|
-
|
|
108
|
-
gl.TexCoord2f(1.0, 0.0);
|
|
109
|
-
gl.Vertex3f(1, -1, 0);
|
|
110
|
-
|
|
111
|
-
gl.TexCoord2f(0.0, 1.0);
|
|
112
|
-
gl.Vertex3f(-1, 1, 0);
|
|
113
|
-
|
|
114
|
-
gl.TexCoord2f(1.0,1.0);
|
|
115
|
-
gl.Vertex3f(1, 1, 0);
|
|
116
|
-
gl.End();
|
|
117
|
-
gl.SwapBuffers();
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
var texture = require('baboon-image');
|
|
121
|
-
|
|
122
|
-
ntk.createClient(function(err, app) {
|
|
123
|
-
|
|
124
|
-
var wnd = app.createWindow({width: width, height: height});
|
|
125
|
-
wnd.map();
|
|
126
|
-
var gl = wnd.getContext('opengl');
|
|
127
|
-
gl.Enable(gl.TEXTURE_2D);
|
|
128
|
-
gl.GenTextures(1, function(err, textures) {
|
|
129
|
-
gl.BindTexture(gl.TEXTURE_2D, textures[0]);
|
|
130
|
-
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
131
|
-
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
132
|
-
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB, 512, 512, 0, gl.RGB, gl.UNSIGNED_BYTE, texture.data);
|
|
133
|
-
draw(gl);
|
|
134
|
-
|
|
135
|
-
wnd.on('resize', function(ev) {
|
|
136
|
-
width = ev.width;
|
|
137
|
-
height = ev.height;
|
|
138
|
-
resize(gl);
|
|
139
|
-
draw(gl);
|
|
140
|
-
}).on('mousemove', function(ev) {
|
|
141
|
-
angle = ev.x;
|
|
142
|
-
draw(gl);
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
132
|
+
import { createClient } from 'ntk';
|
|
133
|
+
|
|
134
|
+
const app = await createClient();
|
|
135
|
+
const wnd = app.createWindow({ width: 300, height: 300 });
|
|
136
|
+
wnd.map();
|
|
137
|
+
|
|
138
|
+
const gl = wnd.getContext('opengl');
|
|
139
|
+
gl.ClearColor(0.3, 0.3, 0.3, 0.0);
|
|
140
|
+
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
141
|
+
gl.Begin(gl.TRIANGLES);
|
|
142
|
+
gl.Color3f(1, 0, 0);
|
|
143
|
+
gl.Vertex3f(-1, -1, 0);
|
|
144
|
+
gl.Vertex3f(1, -1, 0);
|
|
145
|
+
gl.Vertex3f(0, 1, 0);
|
|
146
|
+
gl.End();
|
|
147
|
+
gl.SwapBuffers();
|
|
147
148
|
```
|
|
148
149
|
|
|
150
|
+
See [examples/](examples/) for more (teapot, GL clock, textures, text
|
|
151
|
+
rendering).
|
|
152
|
+
|
|
149
153
|
## High level widgets / layout management etc
|
|
150
154
|
|
|
151
|
-
Likely to be implemented outside as part of
|
|
155
|
+
Likely to be implemented outside as part of a react renderer
|
|
156
|
+
([react-x11](https://github.com/sidorares/react-x11)).
|
package/lib/app.js
CHANGED
|
@@ -1,21 +1,54 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import Pixmap from './pixmap.js';
|
|
2
|
+
import FontManager from './text/fontmanager.js';
|
|
3
|
+
import Window from './window.js';
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
/**
|
|
6
|
+
* A connection to an X server. Owns the underlying node-x11 client
|
|
7
|
+
* (`app.X`) and acts as a factory for windows and pixmaps.
|
|
8
|
+
*/
|
|
9
|
+
export default class App {
|
|
10
|
+
/**
|
|
11
|
+
* @param {object} display node-x11 display
|
|
12
|
+
* @param {object} [options] environment hooks: { fontSource (see
|
|
13
|
+
* text/fontsource.js), glxVisual (visual id for getContext('opengl')
|
|
14
|
+
* when glxinfo can't be shelled out to) }
|
|
15
|
+
*/
|
|
16
|
+
constructor(display, options = {}) {
|
|
17
|
+
this.display = display;
|
|
18
|
+
this.X = display.client;
|
|
19
|
+
this.options = options;
|
|
20
|
+
this._fonts = null;
|
|
21
|
+
}
|
|
5
22
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
23
|
+
/** the text API entry point: font matching/loading, shaping, layout */
|
|
24
|
+
get fonts() {
|
|
25
|
+
if (!this._fonts) this._fonts = new FontManager({ source: this.options.fontSource });
|
|
26
|
+
return this._fonts;
|
|
27
|
+
}
|
|
10
28
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
29
|
+
createWindow(args) {
|
|
30
|
+
return new Window(this, args);
|
|
31
|
+
}
|
|
14
32
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
33
|
+
rootWindow() {
|
|
34
|
+
return new Window(this, { id: this.display.screen[0].root });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
createPixmap(args) {
|
|
38
|
+
return new Pixmap(this, args);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// flush pending requests and close the connection
|
|
42
|
+
close() {
|
|
43
|
+
return new Promise((resolve) => this.X.close(resolve));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async [Symbol.asyncDispose]() {
|
|
47
|
+
await this.close();
|
|
48
|
+
}
|
|
18
49
|
|
|
19
|
-
|
|
20
|
-
|
|
50
|
+
// sync variant: terminates without waiting for a flush round-trip
|
|
51
|
+
[Symbol.dispose]() {
|
|
52
|
+
this.X.terminate();
|
|
53
|
+
}
|
|
21
54
|
}
|
package/lib/cleanup.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Release a server-side resource, tolerating a connection that is closing or
|
|
2
|
+
// already gone — the X server frees all of a client's resources on disconnect,
|
|
3
|
+
// so there is nothing left to do and nothing worth throwing about. This
|
|
4
|
+
// matters for FinalizationRegistry callbacks, which run after app.close() if
|
|
5
|
+
// wrappers get garbage collected late and have no user code around them to
|
|
6
|
+
// catch.
|
|
7
|
+
export function safeRelease(X, fn) {
|
|
8
|
+
if (X._closing || !X.stream || X.stream.destroyed || X.stream.writableEnded) return;
|
|
9
|
+
try {
|
|
10
|
+
fn();
|
|
11
|
+
} catch {
|
|
12
|
+
// connection raced shut between the check and the request
|
|
13
|
+
}
|
|
14
|
+
}
|
package/lib/drawable.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
|
|
3
|
+
export default class Drawable extends EventEmitter {
|
|
4
|
+
getContext(name, ...args) {
|
|
5
|
+
const factory = Drawable.renderingContextFactory[name];
|
|
6
|
+
if (!factory) throw new Error(`Unknown rendering context: ${name}`);
|
|
7
|
+
return factory(this, ...args);
|
|
8
|
+
}
|
|
8
9
|
}
|
|
9
|
-
util.inherits(Drawable, EventEmitter);
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Drawable.prototype.getContext = function(name, arg1, arg2, arg3) {
|
|
14
|
-
var renderingContextFactory = this.renderingContextFactory[name];
|
|
15
|
-
if (!renderingContextFactory)
|
|
16
|
-
throw new Error('Unknown rendering context:', name);
|
|
17
|
-
return renderingContextFactory(this, arg1, arg2, arg3);
|
|
18
|
-
}
|
|
11
|
+
// populated by the renderingcontext_* modules on import
|
|
12
|
+
Drawable.renderingContextFactory = {};
|
package/lib/events_map.js
CHANGED
|
@@ -1,98 +1,92 @@
|
|
|
1
|
-
|
|
1
|
+
import x11 from 'x11';
|
|
2
2
|
|
|
3
3
|
// todo: move event names to node-x11, it should be part of event object
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
4
|
+
export const eventName = [];
|
|
5
|
+
eventName[2] = 'keydown';
|
|
6
|
+
eventName[3] = 'keyup';
|
|
7
|
+
eventName[4] = 'mousedown';
|
|
8
|
+
eventName[5] = 'mouseup';
|
|
9
|
+
eventName[6] = 'mousemove';
|
|
10
|
+
eventName[7] = 'mouseover';
|
|
11
|
+
eventName[8] = 'mouseout';
|
|
12
|
+
eventName[12] = 'expose';
|
|
13
|
+
eventName[17] = 'destroy';
|
|
14
|
+
eventName[18] = 'unmap';
|
|
15
|
+
eventName[19] = 'map';
|
|
16
|
+
eventName[20] = 'map_request';
|
|
17
|
+
eventName[21] = 'reparent';
|
|
18
|
+
eventName[22] = 'resize';
|
|
19
|
+
eventName[23] = 'configure_request';
|
|
20
20
|
// 25 - gravity
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
eventName[25] = 'resize_request';
|
|
22
|
+
eventName[26] = 'circulate';
|
|
23
|
+
eventName[27] = 'circulate_request';
|
|
24
|
+
eventName[28] = 'property';
|
|
25
|
+
eventName[29] = 'selection_clear';
|
|
26
|
+
eventName[30] = 'selection_request';
|
|
27
|
+
eventName[31] = 'selection';
|
|
28
|
+
eventName[33] = 'message';
|
|
29
29
|
|
|
30
30
|
// event mask you need to select to receive event
|
|
31
31
|
// 0 = no need to express interest, event is sent regardless of mask
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
32
|
+
export const mask = {
|
|
33
|
+
mousemove: x11.eventMask.PointerMotion,
|
|
34
|
+
mouseover: x11.eventMask.EnterWindow,
|
|
35
|
+
mouseout: x11.eventMask.LeaveWindow,
|
|
36
|
+
mousedown: x11.eventMask.ButtonPress,
|
|
37
|
+
mouseup: x11.eventMask.ButtonRelease,
|
|
38
|
+
map: x11.eventMask.StructureNotify,
|
|
39
|
+
unmap: x11.eventMask.StructureNotify,
|
|
40
|
+
resize: x11.eventMask.StructureNotify,
|
|
41
|
+
reparent: x11.eventMask.StructureNotify,
|
|
42
|
+
keydown: x11.eventMask.KeyPress,
|
|
43
|
+
expose: x11.eventMask.Exposure,
|
|
44
|
+
map_request: x11.eventMask.SubstructureRedirect,
|
|
45
|
+
configure_request: x11.eventMask.SubstructureRedirect,
|
|
46
|
+
destroy: x11.eventMask.StructureNotify,
|
|
47
|
+
keyup: x11.eventMask.KeyRelease,
|
|
48
|
+
property: x11.eventMask.PropertyChange,
|
|
49
|
+
selection: 0,
|
|
50
|
+
selection_request: 0,
|
|
51
|
+
message: 0
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
'
|
|
64
|
-
'onKeyDown': x11.eventMask.KeyPress,
|
|
65
|
-
'onExpose': x11.eventMask.Exposure,
|
|
66
|
-
'onMapRequest': x11.eventMask.SubstructureRedirect,
|
|
67
|
-
'onConfigureRequest': x11.eventMask.SubstructureRedirect,
|
|
68
|
-
'onDestroy': x11.eventMask.StructureNotify,
|
|
69
|
-
'onKeyUp': x11.eventMask.KeyRelease,
|
|
70
|
-
'onPropertyChange': x11.eventMask.PropertyChange,
|
|
71
|
-
// TODO
|
|
72
|
-
'onSelection': 0,
|
|
73
|
-
'onSelectionRequest': 0,
|
|
74
|
-
'onMessage': 0
|
|
54
|
+
// events that are noisy by nature: intermediate occurrences carry no
|
|
55
|
+
// information the final one doesn't, so Window merges bursts into a single
|
|
56
|
+
// event per paced frame (see docs/window.md). 'last' — the newest event
|
|
57
|
+
// wins (older ones ride along in ev.coalesced); 'union' — damage
|
|
58
|
+
// rectangles accumulate (bounding box in ev.x/y/width/height, every rect
|
|
59
|
+
// in ev.rects).
|
|
60
|
+
export const coalesce = {
|
|
61
|
+
mousemove: 'last',
|
|
62
|
+
resize: 'last',
|
|
63
|
+
expose: 'union'
|
|
75
64
|
};
|
|
76
65
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
'onMessage': 'message'
|
|
66
|
+
export const toSnake = {
|
|
67
|
+
onMouseMove: 'mousemove',
|
|
68
|
+
onMouseOver: 'mouseover',
|
|
69
|
+
onMouseOut: 'mouseout',
|
|
70
|
+
onMouseDown: 'mousedown',
|
|
71
|
+
onMouseUp: 'mouseup',
|
|
72
|
+
onMap: 'map',
|
|
73
|
+
onUnmap: 'unmap',
|
|
74
|
+
onResize: 'resize',
|
|
75
|
+
onReparent: 'reparent',
|
|
76
|
+
onKeyDown: 'keydown',
|
|
77
|
+
onExpose: 'expose',
|
|
78
|
+
onMapRequest: 'map_request',
|
|
79
|
+
onConfigureRequest: 'configure_request',
|
|
80
|
+
onDestroy: 'destroy',
|
|
81
|
+
onKeyUp: 'keyup',
|
|
82
|
+
onPropertyChange: 'property',
|
|
83
|
+
onSelection: 'selection',
|
|
84
|
+
onSelectionRequest: 'selection_request',
|
|
85
|
+
onMessage: 'message'
|
|
98
86
|
};
|
|
87
|
+
|
|
88
|
+
export const maskCamelCase = Object.fromEntries(
|
|
89
|
+
Object.entries(toSnake).map(([camel, snake]) => [camel, mask[snake]])
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
export default { eventName, mask, maskCamelCase, toSnake, coalesce };
|