ntk 8.7.0 → 8.8.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/lib/app.js +26 -1
- package/lib/gl.js +62 -0
- package/lib/glx.js +19 -3
- package/lib/renderingcontext_cgl.js +20 -1
- package/lib/renderingcontext_gles.js +19 -1
- package/lib/renderingcontext_opengl.js +11 -0
- package/lib/window.js +89 -0
- package/package.json +1 -1
package/lib/app.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { connectionGone } from './cleanup.js';
|
|
2
2
|
import Clipboard from './clipboard.js';
|
|
3
3
|
import { CursorCache } from './cursor.js';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
DIRECT_SAMPLES,
|
|
6
|
+
GLError,
|
|
7
|
+
backendFor,
|
|
8
|
+
glCapabilities,
|
|
9
|
+
glError,
|
|
10
|
+
nativeRefreshRate,
|
|
11
|
+
requestedSamples,
|
|
12
|
+
resolveGLPolicy,
|
|
13
|
+
warnDirectSamples
|
|
14
|
+
} from './gl.js';
|
|
5
15
|
import { chooseGLXConfig } from './glx.js';
|
|
6
16
|
import Picture from './picture.js';
|
|
7
17
|
import Pixmap from './pixmap.js';
|
|
@@ -620,6 +630,13 @@ export default class App {
|
|
|
620
630
|
* fbconfig with an alpha channel. Direct needs no round trip to answer —
|
|
621
631
|
* there are no fbconfigs in it, only a window the GPU draws for.
|
|
622
632
|
*
|
|
633
|
+
* What a backend cannot do, the answer says rather than drops: `samples`
|
|
634
|
+
* is the colour samples per pixel the returned config really has, which
|
|
635
|
+
* on the direct backend is 0 today whatever the spec asked for (issue
|
|
636
|
+
* #341, `DIRECT_SAMPLES` in lib/gl.js). A `SAMPLES`/`SAMPLE_BUFFERS`
|
|
637
|
+
* request that cannot be met also warns once per connection, because an
|
|
638
|
+
* aliased edge is otherwise found months later in a screenshot.
|
|
639
|
+
*
|
|
623
640
|
* @param {object} [spec] GLX attributes, e.g. `{ DEPTH_SIZE: 24 }`
|
|
624
641
|
*/
|
|
625
642
|
async chooseGLConfig(spec = {}) {
|
|
@@ -638,6 +655,11 @@ export default class App {
|
|
|
638
655
|
`direct rendering: screen ${screenNum} has no 32-bit visual, so a GL window cannot have an alpha channel`
|
|
639
656
|
);
|
|
640
657
|
}
|
|
658
|
+
// asked for multisampling this backend cannot give? say so once, then
|
|
659
|
+
// answer honestly below rather than dropping the attribute (issue #341)
|
|
660
|
+
const wantSamples = requestedSamples(spec);
|
|
661
|
+
if (wantSamples > DIRECT_SAMPLES) warnDirectSamples(this, caps.flavor, wantSamples);
|
|
662
|
+
|
|
641
663
|
return {
|
|
642
664
|
backend: 'direct',
|
|
643
665
|
// which direct pipeline this connection runs: 'dri3' or 'appledri'
|
|
@@ -649,6 +671,9 @@ export default class App {
|
|
|
649
671
|
class: 4, // TrueColor; the only class these buffers can be read as
|
|
650
672
|
doubleBuffer: true, // a swap chain, always
|
|
651
673
|
depthSize: spec.DEPTH_SIZE ?? 16,
|
|
674
|
+
// no flavor can allocate a multisampled window buffer yet; said out
|
|
675
|
+
// loud so a caller can supersample instead of assuming MSAA
|
|
676
|
+
samples: DIRECT_SAMPLES,
|
|
652
677
|
screen: screenNum,
|
|
653
678
|
fbconfig: null,
|
|
654
679
|
device: caps.device,
|
package/lib/gl.js
CHANGED
|
@@ -467,6 +467,68 @@ export function glCapabilities(app) {
|
|
|
467
467
|
return app._glCaps;
|
|
468
468
|
}
|
|
469
469
|
|
|
470
|
+
/**
|
|
471
|
+
* How many colour samples per pixel a direct-backend window has. Zero, on
|
|
472
|
+
* both flavors, and this is the one place that says so.
|
|
473
|
+
*
|
|
474
|
+
* Not because multisampling is exotic on a GPU — it is nearly free there —
|
|
475
|
+
* but because the sample count belongs to the pixel format the `x11-dri`
|
|
476
|
+
* addon builds one layer below ntk: `EGL_SAMPLES` on the EGLConfig behind
|
|
477
|
+
* the GBM surface (`dri3`), `kCGLPFASamples`/`kCGLPFASampleBuffers` on the
|
|
478
|
+
* CGL pixel format (`appledri`). Its `Gpu` and `apple.Context` take a depth
|
|
479
|
+
* size and no sample count, and its GL table has neither
|
|
480
|
+
* `renderbufferStorageMultisample` nor `blitFramebuffer`, so there is not a
|
|
481
|
+
* multisampled framebuffer for ntk to resolve by hand either. Nothing here
|
|
482
|
+
* can conjure one — what it can do is not pretend, which is why
|
|
483
|
+
* `chooseGLConfig` reports `samples` on every backend and says so out loud
|
|
484
|
+
* when the spec asked for more than it got (issue #341).
|
|
485
|
+
*
|
|
486
|
+
* When the addon grows the option this becomes what it reports, and the
|
|
487
|
+
* request travels the rest of the way without another change here.
|
|
488
|
+
*/
|
|
489
|
+
export const DIRECT_SAMPLES = 0;
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* The sample count a GLX-vocabulary spec asks for: `SAMPLES` when it names
|
|
493
|
+
* one, 1 for a bare `SAMPLE_BUFFERS` (multisample, width up to the driver),
|
|
494
|
+
* and 0 when it asks for no multisampling at all — which is also what a
|
|
495
|
+
* config object from `chooseGLConfig` reads as, since it carries neither.
|
|
496
|
+
*/
|
|
497
|
+
export function requestedSamples(spec = {}) {
|
|
498
|
+
const samples = Number(spec.SAMPLES) || 0;
|
|
499
|
+
if (samples > 0) return samples;
|
|
500
|
+
return Number(spec.SAMPLE_BUFFERS) > 0 ? 1 : 0;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Say — once per connection, on the console — that a multisample request
|
|
505
|
+
* cannot be honoured here.
|
|
506
|
+
*
|
|
507
|
+
* A downgrade rather than a failure: the window renders, its edges alias.
|
|
508
|
+
* That is exactly the kind of thing an app finds out about six months later
|
|
509
|
+
* from a screenshot, so it is worth one warning and a `samples` field to
|
|
510
|
+
* branch on. Once per connection because every window would otherwise say
|
|
511
|
+
* it again.
|
|
512
|
+
*/
|
|
513
|
+
export function warnDirectSamples(app, flavor, wanted) {
|
|
514
|
+
if (app._warnedDirectSamples) return;
|
|
515
|
+
app._warnedDirectSamples = true;
|
|
516
|
+
console.warn(
|
|
517
|
+
`ntk: this GL request asks for SAMPLES=${wanted}, and the direct backend` +
|
|
518
|
+
`${flavor ? ` (${flavor} flavor)` : ''} cannot give a window a multisampled buffer, so it has ` +
|
|
519
|
+
'samples: 0 and its edges will alias.\n' +
|
|
520
|
+
'\n' +
|
|
521
|
+
' The sample count belongs to the pixel format x11-dri builds one layer below\n' +
|
|
522
|
+
' ntk (EGL_SAMPLES on dri3, kCGLPFASamples on appledri), and it takes no such\n' +
|
|
523
|
+
' option yet — there is nothing here to ask with.\n' +
|
|
524
|
+
'\n' +
|
|
525
|
+
' Branch on config.samples (or gl.samples) rather than on having asked. For\n' +
|
|
526
|
+
" multisampling today: indirect GLX honours SAMPLES (glPolicy: 'indirect'),\n" +
|
|
527
|
+
' where the server picks an fbconfig that has it — or supersample in your own\n' +
|
|
528
|
+
' draw code. See docs/context-gles.md#multisampling.'
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
|
|
470
532
|
/**
|
|
471
533
|
* The backend `getContext('opengl')` should use right now, synchronously.
|
|
472
534
|
*
|
package/lib/glx.js
CHANGED
|
@@ -131,7 +131,15 @@ const legacyProps = {
|
|
|
131
131
|
BLUE_SIZE: 'blueBits',
|
|
132
132
|
ALPHA_SIZE: 'alphaBits',
|
|
133
133
|
AUX_BUFFERS: 'numAuxBuffers',
|
|
134
|
-
LEVEL: 'level'
|
|
134
|
+
LEVEL: 'level',
|
|
135
|
+
// Not among GetVisualConfigs' fixed properties, but a server that has
|
|
136
|
+
// multisample visuals sends these as (tag, value) pairs after them, which
|
|
137
|
+
// x11 decodes under their attribute names. Mapped to themselves so a
|
|
138
|
+
// SAMPLES request is filtered on this path too rather than passing
|
|
139
|
+
// through unexamined — a spec that asks for multisampling and gets a
|
|
140
|
+
// visual without it is the failure this whole field exists to stop.
|
|
141
|
+
SAMPLE_BUFFERS: 'SAMPLE_BUFFERS',
|
|
142
|
+
SAMPLES: 'SAMPLES'
|
|
135
143
|
};
|
|
136
144
|
|
|
137
145
|
// attributes compared as "at least this much"; the rest must match exactly
|
|
@@ -219,8 +227,11 @@ function matchLegacy(configs, spec) {
|
|
|
219
227
|
* `null` means "don't care". `screen` picks the X screen (default 0),
|
|
220
228
|
* `visual` pins a specific visual id and skips the search.
|
|
221
229
|
* @returns {Promise<{visual: number, depth: number, class: number,
|
|
222
|
-
* doubleBuffer: boolean, depthSize: number,
|
|
223
|
-
* config: object}>}
|
|
230
|
+
* doubleBuffer: boolean, depthSize: number, samples: number|null,
|
|
231
|
+
* fbconfig: number|null, config: object}>} `samples` is the colour
|
|
232
|
+
* samples per pixel the chosen config has — 0 for no multisampling, and
|
|
233
|
+
* `null` only in the pinned-`visual` case, where no fbconfig was looked
|
|
234
|
+
* at to know.
|
|
224
235
|
*/
|
|
225
236
|
export async function chooseGLXConfig(app, spec = {}) {
|
|
226
237
|
const GLX = app.display.GLX;
|
|
@@ -255,6 +266,9 @@ export async function chooseGLXConfig(app, spec = {}) {
|
|
|
255
266
|
class: info.visual.class,
|
|
256
267
|
doubleBuffer: !!toNumber(want.DOUBLEBUFFER),
|
|
257
268
|
depthSize: toNumber(want.DEPTH_SIZE) || 0,
|
|
269
|
+
// the caller pinned the visual, so nothing was chosen and no fbconfig
|
|
270
|
+
// was read: unknown, which is not the same claim as "none"
|
|
271
|
+
samples: null,
|
|
258
272
|
screen: screenNum,
|
|
259
273
|
fbconfig: null,
|
|
260
274
|
config: {}
|
|
@@ -272,6 +286,7 @@ export async function chooseGLXConfig(app, spec = {}) {
|
|
|
272
286
|
class: info.visual.class,
|
|
273
287
|
doubleBuffer: !!cfg.DOUBLEBUFFER,
|
|
274
288
|
depthSize: cfg.DEPTH_SIZE || 0,
|
|
289
|
+
samples: cfg.SAMPLES || 0,
|
|
275
290
|
screen: screenNum,
|
|
276
291
|
fbconfig: cfg.FBCONFIG_ID,
|
|
277
292
|
config: cfg
|
|
@@ -292,6 +307,7 @@ export async function chooseGLXConfig(app, spec = {}) {
|
|
|
292
307
|
class: info.visual.class,
|
|
293
308
|
doubleBuffer: !!cfg.doubleBufferMode,
|
|
294
309
|
depthSize: cfg.depthBits || 0,
|
|
310
|
+
samples: cfg.SAMPLES || 0,
|
|
295
311
|
screen: screenNum,
|
|
296
312
|
fbconfig: null,
|
|
297
313
|
config: cfg
|
|
@@ -43,7 +43,15 @@
|
|
|
43
43
|
|
|
44
44
|
import { connectionGone } from './cleanup.js';
|
|
45
45
|
import Drawable from './drawable.js';
|
|
46
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
DIRECT_SAMPLES,
|
|
48
|
+
GLError,
|
|
49
|
+
backendFor,
|
|
50
|
+
glError,
|
|
51
|
+
loadDriAddon,
|
|
52
|
+
requestedSamples,
|
|
53
|
+
warnDirectSamples
|
|
54
|
+
} from './gl.js';
|
|
47
55
|
|
|
48
56
|
/** pacing fallback where the display's rate is not known (see App#frameInterval) */
|
|
49
57
|
const FALLBACK_FRAME_INTERVAL = 1000 / 60;
|
|
@@ -115,6 +123,17 @@ does not make it retroactive. Either:
|
|
|
115
123
|
);
|
|
116
124
|
}
|
|
117
125
|
this.depth = depth;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Colour samples per pixel — 0 here, as on every direct flavor
|
|
129
|
+
* (`DIRECT_SAMPLES`). A config asking for more is a request this
|
|
130
|
+
* pipeline cannot pass on, so it is answered rather than dropped: the
|
|
131
|
+
* warning fires once per connection, and draw code branches on this.
|
|
132
|
+
*/
|
|
133
|
+
this.samples = DIRECT_SAMPLES;
|
|
134
|
+
const wantSamples = Math.max(requestedSamples(config), Number(config.samples) || 0);
|
|
135
|
+
if (wantSamples > DIRECT_SAMPLES) warnDirectSamples(app, 'appledri', wantSamples);
|
|
136
|
+
|
|
118
137
|
this._screen = config.screen ?? 0;
|
|
119
138
|
this._clientId = caps.appleClientId ?? dri.apple.clientId();
|
|
120
139
|
|
|
@@ -17,7 +17,15 @@
|
|
|
17
17
|
// is proven.
|
|
18
18
|
|
|
19
19
|
import Drawable from './drawable.js';
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
DIRECT_SAMPLES,
|
|
22
|
+
GLError,
|
|
23
|
+
backendFor,
|
|
24
|
+
glError,
|
|
25
|
+
loadDriAddon,
|
|
26
|
+
requestedSamples,
|
|
27
|
+
warnDirectSamples
|
|
28
|
+
} from './gl.js';
|
|
21
29
|
import { GLSwapchain } from './glswapchain.js';
|
|
22
30
|
|
|
23
31
|
/**
|
|
@@ -98,6 +106,16 @@ does not make it retroactive. Either:
|
|
|
98
106
|
}
|
|
99
107
|
this.depth = depth;
|
|
100
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Colour samples per pixel — 0 here, as on every direct flavor
|
|
111
|
+
* (`DIRECT_SAMPLES`). A config asking for more is a request this
|
|
112
|
+
* pipeline cannot pass on, so it is answered rather than dropped: the
|
|
113
|
+
* warning fires once per connection, and draw code branches on this.
|
|
114
|
+
*/
|
|
115
|
+
this.samples = DIRECT_SAMPLES;
|
|
116
|
+
const wantSamples = Math.max(requestedSamples(config), Number(config.samples) || 0);
|
|
117
|
+
if (wantSamples > DIRECT_SAMPLES) warnDirectSamples(app, caps.flavor, wantSamples);
|
|
118
|
+
|
|
101
119
|
const policy = app.glPolicy;
|
|
102
120
|
this.gpu = sharedGpu(app, dri, {
|
|
103
121
|
format: depth === 32 ? dri.FORMAT.ARGB8888 : dri.FORMAT.XRGB8888,
|
|
@@ -30,6 +30,14 @@ class RenderingContextOpenGL {
|
|
|
30
30
|
this.contextId = 0;
|
|
31
31
|
/** the config chosen (or passed in), see app.chooseGLXConfig */
|
|
32
32
|
this.config = null;
|
|
33
|
+
/**
|
|
34
|
+
* Colour samples per pixel the config in use has: 0 until the config is
|
|
35
|
+
* resolved, and 0 afterwards unless the spec asked for multisampling and
|
|
36
|
+
* the server had an fbconfig with it. The direct contexts carry the same
|
|
37
|
+
* field, so draw code can decide whether to antialias itself without
|
|
38
|
+
* knowing which backend it is on.
|
|
39
|
+
*/
|
|
40
|
+
this.samples = 0;
|
|
33
41
|
this.visual = 0;
|
|
34
42
|
this.error = null;
|
|
35
43
|
|
|
@@ -75,6 +83,9 @@ class RenderingContextOpenGL {
|
|
|
75
83
|
this.ready = (async () => {
|
|
76
84
|
const cfg = await this._resolveConfig(config);
|
|
77
85
|
this.config = cfg;
|
|
86
|
+
// a config passed in by hand may be the fbconfig itself rather than a
|
|
87
|
+
// chooseGLXConfig result, so read either shape
|
|
88
|
+
this.samples = cfg.samples ?? cfg.config?.SAMPLES ?? cfg.SAMPLES ?? 0;
|
|
78
89
|
this.visual = cfg.visual;
|
|
79
90
|
if (window.visual && window.visual !== cfg.visual) {
|
|
80
91
|
console.warn(
|
package/lib/window.js
CHANGED
|
@@ -3719,6 +3719,9 @@ export default class Window extends Drawable {
|
|
|
3719
3719
|
* connection selected and nothing else adds to it behind the caller's
|
|
3720
3720
|
* back. It resolves rather than returning undefined because the promise is
|
|
3721
3721
|
* the whole interface: an awaited no-op still has to mean "you have it".
|
|
3722
|
+
*
|
|
3723
|
+
* `deselectInput(mask)` is the inverse, and the only thing that lowers the
|
|
3724
|
+
* mask again — `off()` drops the listener and leaves the selection.
|
|
3722
3725
|
*/
|
|
3723
3726
|
selectInput(mask) {
|
|
3724
3727
|
const added = mask & ~this.eventMask;
|
|
@@ -3736,6 +3739,92 @@ export default class Window extends Drawable {
|
|
|
3736
3739
|
});
|
|
3737
3740
|
}
|
|
3738
3741
|
|
|
3742
|
+
/**
|
|
3743
|
+
* The bits of the event mask something in this process still needs: the
|
|
3744
|
+
* union over every event name with a live listener, plus what ntk's own
|
|
3745
|
+
* machinery requires. `deselectInput` refuses exactly these, so
|
|
3746
|
+
* `mask & wnd.heldEventMask` asks "would that be refused?" without making
|
|
3747
|
+
* the request.
|
|
3748
|
+
*
|
|
3749
|
+
* Not a subset of `eventMask`. A listener implies a bit whether or not
|
|
3750
|
+
* this connection ever selected it — an adopted window selects nothing
|
|
3751
|
+
* (issue #322), and ntk's own map/unmap/destroy handlers listen without
|
|
3752
|
+
* asking for anything.
|
|
3753
|
+
*/
|
|
3754
|
+
get heldEventMask() {
|
|
3755
|
+
let held = 0;
|
|
3756
|
+
for (const name in xevents.mask) {
|
|
3757
|
+
const bit = xevents.mask[name];
|
|
3758
|
+
// 0 marks an event that arrives regardless of any selection
|
|
3759
|
+
if (bit && this.listenerCount(name) > 0) held |= bit;
|
|
3760
|
+
}
|
|
3761
|
+
// the redraw cycle is driven by intercepting Expose, whether or not
|
|
3762
|
+
// anyone listens for it: a double-buffered window that stops selecting
|
|
3763
|
+
// Exposure stops repainting (see _enableBackingStore)
|
|
3764
|
+
if (this._backing) held |= x11.eventMask.Exposure;
|
|
3765
|
+
return held;
|
|
3766
|
+
}
|
|
3767
|
+
|
|
3768
|
+
/**
|
|
3769
|
+
* Stop selecting `mask` — the inverse of `selectInput`, and the only way a
|
|
3770
|
+
* window's event mask ever goes down (issue #318).
|
|
3771
|
+
*
|
|
3772
|
+
* `on(name, fn)` raises the mask as a side effect and `off()` does not
|
|
3773
|
+
* lower it again, so a window that stops needing an event goes on being
|
|
3774
|
+
* sent it until it is destroyed. For most masks that is invisible; for
|
|
3775
|
+
* PointerMotion it is 32 bytes per motion event — 2 KB/s at 60 Hz, and
|
|
3776
|
+
* 8 KB/s where the motion is XI2's — for as long as the pointer is over
|
|
3777
|
+
* the window. A viewer that hides its chrome, a toolbar that unmounts, a
|
|
3778
|
+
* window that goes to a background tab: they know when they stop needing
|
|
3779
|
+
* hover, and this is how they say so.
|
|
3780
|
+
*
|
|
3781
|
+
* Bits a live listener still needs are kept rather than cleared, because
|
|
3782
|
+
* dropping PointerMotion out from under an `on('mousemove')` is the one
|
|
3783
|
+
* bug this API can have. Several names share one bit — `map`, `unmap`,
|
|
3784
|
+
* `resize`, `reparent`, `gravity`, `circulate` and `destroy` are all
|
|
3785
|
+
* StructureNotify — so losing the last `map` listener does not make the
|
|
3786
|
+
* bit free. It resolves with the bits it kept for that reason, `0` when it
|
|
3787
|
+
* cleared everything it was asked to:
|
|
3788
|
+
*
|
|
3789
|
+
* const kept = await wnd.deselectInput(x11.eventMask.PointerMotion);
|
|
3790
|
+
* if (kept) ... // something in this process is still listening
|
|
3791
|
+
*
|
|
3792
|
+
* Two selections it will therefore not clear: StructureNotify, held by
|
|
3793
|
+
* ntk's own map/unmap/destroy bookkeeping on every window, and Exposure on
|
|
3794
|
+
* a double-buffered one. XI2 is selected separately and keeps its own
|
|
3795
|
+
* inverse — `selectXI2([])` (see lib/xi2.js).
|
|
3796
|
+
*/
|
|
3797
|
+
deselectInput(mask) {
|
|
3798
|
+
const clear = mask & this.eventMask & ~this.heldEventMask;
|
|
3799
|
+
const kept = mask & this.eventMask & ~clear;
|
|
3800
|
+
// asking to drop what is not selected, or only what is spoken for, is a
|
|
3801
|
+
// request that would change nothing
|
|
3802
|
+
if (clear === 0) return Promise.resolve(kept);
|
|
3803
|
+
this.eventMask &= ~clear;
|
|
3804
|
+
// no motion means no hints, so no frame owes the server the QueryPointer
|
|
3805
|
+
// that re-arms one. A poll already in flight is left alone, as
|
|
3806
|
+
// setMouseHintOnly leaves it: its answer is a real position.
|
|
3807
|
+
if (clear & (x11.eventMask.PointerMotion | x11.eventMask.PointerMotionHint)) {
|
|
3808
|
+
this._hintRearm = false;
|
|
3809
|
+
}
|
|
3810
|
+
// A window the server has already taken, or a connection on its way out:
|
|
3811
|
+
// there is no selection left to lower, and node-x11 throws at a request
|
|
3812
|
+
// from then on (issue #321). The tracked mask is lowered all the same —
|
|
3813
|
+
// it says what this connection holds, and it holds nothing.
|
|
3814
|
+
if (this._destroyed || connectionGone(this.X)) return Promise.resolve(kept);
|
|
3815
|
+
return new Promise((resolve, reject) => {
|
|
3816
|
+
this.X.ChangeWindowAttributes(this.id, { eventMask: this.eventMask }, (err) => {
|
|
3817
|
+
if (!err) return resolve(kept);
|
|
3818
|
+
// the request failed as a whole, so the server kept the mask it had:
|
|
3819
|
+
// take the bits back rather than leave `eventMask` denying a
|
|
3820
|
+
// selection this connection still holds — the next on() reads it to
|
|
3821
|
+
// decide whether a write can be skipped
|
|
3822
|
+
this.eventMask |= clear;
|
|
3823
|
+
reject(err);
|
|
3824
|
+
});
|
|
3825
|
+
});
|
|
3826
|
+
}
|
|
3827
|
+
|
|
3739
3828
|
/**
|
|
3740
3829
|
* Add this window to our save-set (X ChangeSaveSet). A window manager
|
|
3741
3830
|
* reparents clients into frames it owns; without the save-set, the
|