forked from simonbuchan/node-not-the-systray
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
134 lines (121 loc) · 3.85 KB
/
index.js
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
const native = require('./notify_icon.node');
const { NotifyIcon, Icon, Menu } = native;
module.exports = { NotifyIcon, Icon, Menu };
Object.defineProperties(Menu, {
createTemplate: { value: createMenuTemplate, enumerable: true },
});
Object.defineProperties(Icon, {
ids: {
enumerable: true,
value: Object.create(null, {
app: { value: 32512, enumerable: true },
error: { value: 32513, enumerable: true },
question: { value: 32514, enumerable: true },
warning: { value: 32515, enumerable: true },
info: { value: 32516, enumerable: true },
winLogo: { value: 32517, enumerable: true },
shield: { value: 32518, enumerable: true },
}),
},
load: {
enumerable: true,
value: function Icon_load(pathOrId, size) {
switch (typeof pathOrId) {
default:
throw new Error("'pathOrId' should be either a file path or a property of Icon.ids.");
case "number":
return Icon.loadBuiltin(pathOrId, size);
case "string":
return Icon.loadFile(pathOrId, size);
}
},
},
});
function createMenuTemplate(items) {
// Generates a MENUEX binary resource structure to be
// loaded by LoadMenuIndirectW().
// Docs are pretty garbarge here, I found the wine resource
// compiler source helpful for some of the edge cases.
// https://github.com/wine-mirror/wine/blob/master/tools/wrc/genres.c
// struct MENUEX_TEMPLATE_HEADER {
// 0 uint16 version = 1;
// 2 uint16 offset = 4;
// 4 uint32 helpId = 0;
// };
const header = Buffer.alloc(8);
header.writeUInt16LE(1, 0); // version
header.writeUInt16LE(4, 2); // offset
const buffers = [header];
// Wrap everything in a menu item so the contents are a
// valid popup menu, otherwise it doesn't display right.
// No idea why it matters.
addItem({ text: "root", items }, true);
return Buffer.concat(buffers);
function addList(items) {
if (items.length < 1) {
addItem({ text: "Empty", disabled: true }, true);
return;
}
const startItems = items.slice();
const lastItem = startItems.pop();
for (const item of startItems) {
addItem(item);
}
addItem(lastItem, true);
}
function addItem({
id = 0,
text = "",
separator = false,
disabled = false,
checked = false,
items,
}, isLast = false) {
// A variable-length structure, that must be aligned to 4-bytes.
// struct MENUEX_TEMPLATE_ITEM {
// 0 uint32 type;
// 4 uint32 state;
// 8 uint32 id;
// 12 uint16 flags;
// 14 utf16[...] text; // '\0' terminated
// ?? padding to 4-byte boundary
// if (items) {
// ?? uint32 helpid;
// ?? MENUEX_TEMPLATE_ITEM[...] items;
// }
// }
let size = 14 + text.length * 2 + 2;
if (items) {
size += 4;
}
if (size % 4) size += 4 - (size % 4);
let type = 0;
if (separator) {
type |= 0x800;
}
let state = 0;
if (disabled) {
state |= 3;
}
if (checked) {
state |= 8;
}
let flags = 0;
if (isLast) {
flags |= 0x80;
}
if (items) {
flags |= 0x01;
}
const buffer = Buffer.alloc(size);
buffer.writeUInt32LE(type, 0);
buffer.writeUInt32LE(state, 4);
buffer.writeUInt32LE(id, 8);
buffer.writeUInt16LE(flags, 12);
buffer.write(text, 14, 'utf16le');
buffers.push(buffer);
if (items) {
addList(items);
}
}
}