Compare commits
1690 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c4e426534 | ||
|
|
636771e566 | ||
|
|
363b20bdd2 | ||
|
|
b16317e2ca | ||
|
|
2c5c869228 | ||
|
|
3f5f218e0b | ||
|
|
c9cc425aad | ||
|
|
9a2b0b6995 | ||
|
|
50f8aeeaf6 | ||
|
|
fe291dd769 | ||
|
|
58850a0e14 | ||
|
|
518057489c | ||
|
|
cf0b2389a3 | ||
|
|
6179cfc670 | ||
|
|
e6ccc2d178 | ||
|
|
db719eafac | ||
|
|
a4db27a807 | ||
|
|
111c123435 | ||
|
|
75d067e411 | ||
|
|
fbeb991f7d | ||
|
|
ea2db51bbb | ||
|
|
15f4c476c3 | ||
|
|
a7c5b8c634 | ||
|
|
1aeafcaebf | ||
|
|
8d743e9511 | ||
|
|
958d4d8a6a | ||
|
|
05fb09f56c | ||
|
|
cdcf8ed3bd | ||
|
|
572466eed1 | ||
|
|
4c644b8556 | ||
|
|
c989a7198b | ||
|
|
084b028eac | ||
|
|
699d88e393 | ||
|
|
8c6566580a | ||
|
|
effe30ad3f | ||
|
|
e91b8d4556 | ||
|
|
76fca0641e | ||
|
|
7ec803222f | ||
|
|
03f0f065ee | ||
|
|
63e5be6ff0 | ||
|
|
4ccd2afd78 | ||
|
|
98519bf326 | ||
|
|
82d11b80dd | ||
|
|
cf3662442f | ||
|
|
0be5347e6a | ||
|
|
1b39c42e92 | ||
|
|
5b4f5a1e2a | ||
|
|
4eb8b1db01 | ||
|
|
b5555b9a8d | ||
|
|
67bb426eb2 | ||
|
|
cf3a088a53 | ||
|
|
a79d024c09 | ||
|
|
253de1eeca | ||
|
|
4e6d5d3f1e | ||
|
|
82a5733ec5 | ||
|
|
4822fa989e | ||
|
|
fe0e1adb2a | ||
|
|
850a8218db | ||
|
|
bf3fc5fb92 | ||
|
|
a19d1fc140 | ||
|
|
593eb1d952 | ||
|
|
75ec801941 | ||
|
|
5a1bd93518 | ||
|
|
f849f383cb | ||
|
|
ac3080f0e2 | ||
|
|
0935dd053c | ||
|
|
7e78f30739 | ||
|
|
d0f34e99a6 | ||
|
|
c781825b00 | ||
|
|
7199c62b76 | ||
|
|
2074a0d958 | ||
|
|
3e2b626107 | ||
|
|
abf515ece9 | ||
|
|
07de9c021d | ||
|
|
ff420b6495 | ||
|
|
78908e5b93 | ||
|
|
bd6643a523 | ||
|
|
71fddeb1f5 | ||
|
|
9f708be8fd | ||
|
|
a13f815591 | ||
|
|
b5ac06e652 | ||
|
|
e56d259b8e | ||
|
|
11d13c406a | ||
|
|
d5448e6902 | ||
|
|
777b9eb68e | ||
|
|
ff1585acfe | ||
|
|
cb515157b3 | ||
|
|
da39fb2880 | ||
|
|
eb3a2126ee | ||
|
|
9c1c399716 | ||
|
|
e209c3329b | ||
|
|
802147e255 | ||
|
|
1a7291276c | ||
|
|
6d6273af48 | ||
|
|
b7d967b6bc | ||
|
|
779a06fa72 | ||
|
|
0656a5079a | ||
|
|
a1669267b3 | ||
|
|
bbb40f2a21 | ||
|
|
c5b3e43b6c | ||
|
|
55dc354e68 | ||
|
|
0ec7e6c36f | ||
|
|
b1da76eefa | ||
|
|
5c6e55d37f | ||
|
|
5d496216e0 | ||
|
|
e92ed67021 | ||
|
|
b074533b29 | ||
|
|
f3665ddfab | ||
|
|
dd2b6119b3 | ||
|
|
ada3f9e547 | ||
|
|
0372242d12 | ||
|
|
9838a336d5 | ||
|
|
debd9fe419 | ||
|
|
73a06b0e9e | ||
|
|
fdfba4ea04 | ||
|
|
953253de99 | ||
|
|
da0009a46b | ||
|
|
d7e1e53450 | ||
|
|
4a3c57054f | ||
|
|
41b9495588 | ||
|
|
cdbc096990 | ||
|
|
53f2df115e | ||
|
|
0593158a86 | ||
|
|
c1f8372efc | ||
|
|
6e3db0be3d | ||
|
|
0f084faca5 | ||
|
|
c6829766bb | ||
|
|
e7931b0121 | ||
|
|
aeb82e7fb0 | ||
|
|
1e6fe7785a | ||
|
|
4edbe0ea4f | ||
|
|
0788b98ae4 | ||
|
|
8a267894f3 | ||
|
|
0510cd47f4 | ||
|
|
5dbbed3dcc | ||
|
|
7b130dc220 | ||
|
|
d28caf15b2 | ||
|
|
44df11c443 | ||
|
|
63a50ec066 | ||
|
|
3d0cff1f47 | ||
|
|
97a8b718dd | ||
|
|
fc8e147e4a | ||
|
|
af4083a8b7 | ||
|
|
7c7933efa9 | ||
|
|
317ecbc9fa | ||
|
|
67445a22d3 | ||
|
|
62997956a0 | ||
|
|
df7790fdfe | ||
|
|
34d06fa3e9 | ||
|
|
a981b46ec9 | ||
|
|
84dcf8fd27 | ||
|
|
11268ffa16 | ||
|
|
c0ec60c113 | ||
|
|
ca7587c084 | ||
|
|
33ecfc6f84 | ||
|
|
83bbcf0aa1 | ||
|
|
ba32e481ca | ||
|
|
af9da2e495 | ||
|
|
77f7672e98 | ||
|
|
9dd8185f37 | ||
|
|
12f61fd24f | ||
|
|
c9864c994f | ||
|
|
9e476127d3 | ||
|
|
fae99a24bf | ||
|
|
8d9ab15c40 | ||
|
|
ccafdacc6f | ||
|
|
4a996c1f76 | ||
|
|
d743aff4bc | ||
|
|
0f3ff4e2d9 | ||
|
|
a4d9d948ab | ||
|
|
32e6c0c6a6 | ||
|
|
ba2efaacd8 | ||
|
|
37e3e29c8c | ||
|
|
90722ecbe7 | ||
|
|
5a2eaa24d0 | ||
|
|
071986138b | ||
|
|
d61c4a9569 | ||
|
|
9fff829ab4 | ||
|
|
4682da83c1 | ||
|
|
c93c87e8fe | ||
|
|
f3e2a42c29 | ||
|
|
b063c8cda1 | ||
|
|
d360d077d1 | ||
|
|
a548c31d50 | ||
|
|
6c44b4f451 | ||
|
|
b76491ba82 | ||
|
|
e10919496f | ||
|
|
543b7dcf94 | ||
|
|
271c39cc9d | ||
|
|
18186da90e | ||
|
|
02d29d4816 | ||
|
|
82934b84d6 | ||
|
|
6177a7d71f | ||
|
|
1f4fdb174a | ||
|
|
a94b94cab0 | ||
|
|
399c700939 | ||
|
|
077097c112 | ||
|
|
f70c9842f5 | ||
|
|
0f020d5b37 | ||
|
|
2eccc81fee | ||
|
|
9fdaef1da6 | ||
|
|
eb42edff09 | ||
|
|
07f27d0f54 | ||
|
|
48d2aa14c8 | ||
|
|
3c1c8ecd20 | ||
|
|
dcb8fcece4 | ||
|
|
958ccadc09 | ||
|
|
c48a42bec5 | ||
|
|
50858322d6 | ||
|
|
62912a800a | ||
|
|
1ded78a98c | ||
|
|
90c6c2a366 | ||
|
|
fbe84698c4 | ||
|
|
2c642406d8 | ||
|
|
b811976018 | ||
|
|
3ee7e5f800 | ||
|
|
945cbca387 | ||
|
|
30c7ff341f | ||
|
|
12d7c7314a | ||
|
|
ce3cd691cc | ||
|
|
81f144df15 | ||
|
|
78854d4a10 | ||
|
|
dc3ad80d24 | ||
|
|
ed1188dc61 | ||
|
|
52347ced85 | ||
|
|
5fd6c6b9a1 | ||
|
|
9e7031f5d8 | ||
|
|
cca590c20a | ||
|
|
863fff8620 | ||
|
|
18f42fe4c1 | ||
|
|
2a3cc71641 | ||
|
|
bd67814485 | ||
|
|
40dbf7f1c8 | ||
|
|
42a7023203 | ||
|
|
14b33a845e | ||
|
|
56fcbb95ab | ||
|
|
de100ac67c | ||
|
|
2a96d2621c | ||
|
|
a097f5df23 | ||
|
|
6a901ea048 | ||
|
|
dbc1ade5a9 | ||
|
|
c2a924a5ea | ||
|
|
e06d40cb31 | ||
|
|
45d89cfe55 | ||
|
|
d45a8309cb | ||
|
|
9cd8637e9c | ||
|
|
38992e25d7 | ||
|
|
f25b3c2aee | ||
|
|
e3e2261005 | ||
|
|
38a7e77058 | ||
|
|
a920919b13 | ||
|
|
625f0f77bb | ||
|
|
12604072fc | ||
|
|
0341b64748 | ||
|
|
e0620fbb1d | ||
|
|
9114bda24c | ||
|
|
4cf73204a2 | ||
|
|
55e7f69768 | ||
|
|
d2a16bc8e8 | ||
|
|
5fdeb3b3b4 | ||
|
|
9ad5ef8b2d | ||
|
|
c244bcd978 | ||
|
|
7eb8e1df79 | ||
|
|
74c4e94c3d | ||
|
|
275b8ce2e7 | ||
|
|
ff1b0a7e1b | ||
|
|
39026bc45c | ||
|
|
3d9a95d0cd | ||
|
|
085bc09dfb | ||
|
|
886309850f | ||
|
|
ba005e991a | ||
|
|
912c46fc57 | ||
|
|
5675944c0c | ||
|
|
6b9c280214 | ||
|
|
19b882a0bd | ||
|
|
323d5dba7e | ||
|
|
cd9755ffb3 | ||
|
|
fbcdf352ac | ||
|
|
015fe9054e | ||
|
|
9a031c5b0c | ||
|
|
de0f225c45 | ||
|
|
41533bffdd | ||
|
|
cdf7c4bf97 | ||
|
|
57db384953 | ||
|
|
9f0785a160 | ||
|
|
4541cf8807 | ||
|
|
fe07306ff1 | ||
|
|
726d736aac | ||
|
|
db0360dc81 | ||
|
|
4ba35d4a93 | ||
|
|
1d51fca879 | ||
|
|
67ca1902a0 | ||
|
|
1ef326e220 | ||
|
|
0e483f81ac | ||
|
|
e969dea3b7 | ||
|
|
89b4f59efa | ||
|
|
95dc87cf04 | ||
|
|
3fc29e0d11 | ||
|
|
cc3b71154d | ||
|
|
e4e9e29949 | ||
|
|
7a1785e1e8 | ||
|
|
5d2baaac38 | ||
|
|
b479510f6c | ||
|
|
8033e9fef5 | ||
|
|
83b8c8ea12 | ||
|
|
a447ba7d76 | ||
|
|
450e1c2f95 | ||
|
|
cf3a01f502 | ||
|
|
3925e18964 | ||
|
|
2650c7dd9e | ||
|
|
0f18fedf13 | ||
|
|
f18a56682f | ||
|
|
af94e07618 | ||
|
|
7eba3b6cbc | ||
|
|
e5ef9d9062 | ||
|
|
b72dbc973d | ||
|
|
55d0e23c38 | ||
|
|
d3489712a6 | ||
|
|
56193f6902 | ||
|
|
2dad179285 | ||
|
|
19f0e2303d | ||
|
|
c152624634 | ||
|
|
212906cf7d | ||
|
|
bb5c5a8e4f | ||
|
|
9cf5348c36 | ||
|
|
edf4e14e4c | ||
|
|
8fa737f01c | ||
|
|
f16e22fbec | ||
|
|
19e0406459 | ||
|
|
354d7c2740 | ||
|
|
00edb6058e | ||
|
|
597267d1d0 | ||
|
|
ad4665e638 | ||
|
|
b21850dd15 | ||
|
|
4098f2d383 | ||
|
|
5eba754c60 | ||
|
|
b35084062d | ||
|
|
1d20472759 | ||
|
|
c0e336a971 | ||
|
|
d76e757d53 | ||
|
|
43ea58ef48 | ||
|
|
17f485d614 | ||
|
|
35f3312a1e | ||
|
|
ca3c49099f | ||
|
|
6631bb0eac | ||
|
|
0adbbc6d62 | ||
|
|
cc4f9ddad8 | ||
|
|
5a182d3d13 | ||
|
|
c60d3af60b | ||
|
|
4e67a92ec4 | ||
|
|
e43c3118d4 | ||
|
|
ffc0919790 | ||
|
|
2af145879e | ||
|
|
35517d3e6f | ||
|
|
e9e8ef7210 | ||
|
|
ee198ca863 | ||
|
|
0681e6a9b6 | ||
|
|
965a6dfd30 | ||
|
|
d55fde9eea | ||
|
|
231f054bc3 | ||
|
|
43fafd85f8 | ||
|
|
8a5a7b8807 | ||
|
|
2c24cc1341 | ||
|
|
0fc93118f2 | ||
|
|
ac2a33d09f | ||
|
|
68423b3603 | ||
|
|
6d18223f17 | ||
|
|
0443e5ad7f | ||
|
|
4619921780 | ||
|
|
df89266c03 | ||
|
|
36ca3838a6 | ||
|
|
60ce88cf25 | ||
|
|
5a2f2767ad | ||
|
|
6e73d3fac8 | ||
|
|
d08ae5466d | ||
|
|
fc175732e1 | ||
|
|
dfe41217f8 | ||
|
|
4870d07cbb | ||
|
|
2dd9fd83a3 | ||
|
|
0b93153656 | ||
|
|
714d64c417 | ||
|
|
b823af00ff | ||
|
|
77f456bd9c | ||
|
|
874fe2ed72 | ||
|
|
786f8c7b1f | ||
|
|
0c784346cc | ||
|
|
2ab0ff74c6 | ||
|
|
22cb9b9798 | ||
|
|
2ee1a366fd | ||
|
|
f9844ba3b0 | ||
|
|
7cea233de3 | ||
|
|
e4d8aac3d5 | ||
|
|
354d381372 | ||
|
|
60e589f775 | ||
|
|
f6d4b8e0b0 | ||
|
|
5fafd70b0f | ||
|
|
dcfeab8461 | ||
|
|
2a8e8d4b38 | ||
|
|
e6644aebcb | ||
|
|
654cd54363 | ||
|
|
16005d43e8 | ||
|
|
b08c93032b | ||
|
|
dd08896c70 | ||
|
|
f0ae5dfc73 | ||
|
|
0adc7e4d4b | ||
|
|
5c2056a7cb | ||
|
|
f0e7d26409 | ||
|
|
95ebeebb84 | ||
|
|
a88875f98e | ||
|
|
39da92d8ab | ||
|
|
089d358031 | ||
|
|
6a4885df17 | ||
|
|
d45d553eaf | ||
|
|
2cbbd84987 | ||
|
|
1016586611 | ||
|
|
6bbf014152 | ||
|
|
4fea7bd8ef | ||
|
|
aa63bf71cf | ||
|
|
4c9bd368c6 | ||
|
|
b1c1c497e4 | ||
|
|
86ce72e725 | ||
|
|
ae37b4b09b | ||
|
|
5ffbee1e8c | ||
|
|
19a3635c64 | ||
|
|
3c648c05ec | ||
|
|
5607a3c2ed | ||
|
|
cf04a5a036 | ||
|
|
c12680895e | ||
|
|
0163bf3edb | ||
|
|
af0a24c702 | ||
|
|
7ea1deba8f | ||
|
|
b60c84ecdc | ||
|
|
e09ed038e2 | ||
|
|
9c6ba213f9 | ||
|
|
66341111e6 | ||
|
|
abc1e3f289 | ||
|
|
027c5a57f1 | ||
|
|
0714fd376b | ||
|
|
39f41faf9f | ||
|
|
6b2ffc774f | ||
|
|
f94a5f4e5c | ||
|
|
c421fd56be | ||
|
|
11de87d9d9 | ||
|
|
8f5d42fa11 | ||
|
|
e14fcf5590 | ||
|
|
f5b81c7ed6 | ||
|
|
47a901385f | ||
|
|
819bd5cd70 | ||
|
|
753ad68ca9 | ||
|
|
5498f4e526 | ||
|
|
83a01b6bf4 | ||
|
|
c519013b20 | ||
|
|
78d45eb161 | ||
|
|
eeacac227a | ||
|
|
529e9f83f7 | ||
|
|
5297f7474d | ||
|
|
e06b48a344 | ||
|
|
7ba57ebf62 | ||
|
|
1ec9a8b777 | ||
|
|
1690718710 | ||
|
|
774a6c8c8b | ||
|
|
f06eee8fe1 | ||
|
|
2b12bcc07f | ||
|
|
a30ea2b7f8 | ||
|
|
295e8db04c | ||
|
|
b63ae10a09 | ||
|
|
c8a9336160 | ||
|
|
c0b6078438 | ||
|
|
ccce8c32a3 | ||
|
|
8f8be9c2b1 | ||
|
|
eb8e2225e5 | ||
|
|
397d7d044b | ||
|
|
a681162326 | ||
|
|
f00ec0ff60 | ||
|
|
35982a9e58 | ||
|
|
cf0bdf4d59 | ||
|
|
8a9d43e84d | ||
|
|
578bf963e3 | ||
|
|
292c4ce450 | ||
|
|
8ea589e5a3 | ||
|
|
ad09ccb0f2 | ||
|
|
edfc6903ce | ||
|
|
b94d2b27f4 | ||
|
|
31cb68840a | ||
|
|
89c62377da | ||
|
|
85afda8832 | ||
|
|
5184e4bde3 | ||
|
|
133b580262 | ||
|
|
3e76cf87ce | ||
|
|
f72ff73344 | ||
|
|
9aae346ab5 | ||
|
|
ca65ad6fa3 | ||
|
|
0f4156c564 | ||
|
|
3a0d3dc0b3 | ||
|
|
d68c338304 | ||
|
|
82f8c59bc7 | ||
|
|
c55f485166 | ||
|
|
2762488c81 | ||
|
|
ac13ae727f | ||
|
|
1b59a1c421 | ||
|
|
df00e9545d | ||
|
|
d1496f1eb5 | ||
|
|
14d9bcb08a | ||
|
|
c869774c58 | ||
|
|
18679348b2 | ||
|
|
b3ed4c3f40 | ||
|
|
8f58140419 | ||
|
|
bc8a351558 | ||
|
|
9ae198ef8f | ||
|
|
bc86bc91f7 | ||
|
|
a60f347648 | ||
|
|
dbe44c9e4b | ||
|
|
fb8b9b3933 | ||
|
|
56ebc9fa92 | ||
|
|
7f73609c77 | ||
|
|
3ac70042c1 | ||
|
|
61637a7a74 | ||
|
|
49562e96cc | ||
|
|
58b1868536 | ||
|
|
1cad40e977 | ||
|
|
9fbf23fdcc | ||
|
|
55e53db184 | ||
|
|
f790e8bd47 | ||
|
|
54de2d177e | ||
|
|
ec96771e4c | ||
|
|
99d2647335 | ||
|
|
c346cbc252 | ||
|
|
3c77290c2c | ||
|
|
72d193e852 | ||
|
|
1f7499e841 | ||
|
|
c486fbb7c3 | ||
|
|
fe1050fc0e | ||
|
|
3162d391cc | ||
|
|
e2d61ab5be | ||
|
|
2a026de526 | ||
|
|
c1b7023e3a | ||
|
|
175ff4b955 | ||
|
|
65361d56c4 | ||
|
|
08fae7a0ac | ||
|
|
a7e9030c12 | ||
|
|
aa8282f8f2 | ||
|
|
7155400eb5 | ||
|
|
6760e38d93 | ||
|
|
6bbe995d64 | ||
|
|
85bc8b61df | ||
|
|
a235509fa1 | ||
|
|
2e5b595a82 | ||
|
|
5f8dee8384 | ||
|
|
e97f1a4310 | ||
|
|
c3c5a5446f | ||
|
|
3ce04c3e4b | ||
|
|
3b5951eb2f | ||
|
|
aa8dd4ddf4 | ||
|
|
4cd14aee42 | ||
|
|
0b2726ec33 | ||
|
|
bc7f689165 | ||
|
|
6681652c21 | ||
|
|
ed4b5f179e | ||
|
|
e48e166584 | ||
|
|
e7675e8bab | ||
|
|
cf7eaea88c | ||
|
|
3e0855ff45 | ||
|
|
822c9367c6 | ||
|
|
0e46e2a6a7 | ||
|
|
ade7ef19e7 | ||
|
|
e82a6dedd9 | ||
|
|
2fd6bf7442 | ||
|
|
d10681f27f | ||
|
|
e96ff19a7a | ||
|
|
09e75ea329 | ||
|
|
84b03e9f77 | ||
|
|
55597dcea5 | ||
|
|
3290bf3c35 | ||
|
|
6771429911 | ||
|
|
e428858ad6 | ||
|
|
3e73f860d1 | ||
|
|
4cf5bd8d70 | ||
|
|
bffe0f4a6c | ||
|
|
3b8be26cc7 | ||
|
|
9e95939c47 | ||
|
|
0af7af95cf | ||
|
|
2b3e2ea6f8 | ||
|
|
19870983ca | ||
|
|
6e83b4c1bc | ||
|
|
14f5e10fd6 | ||
|
|
ed41e3cd25 | ||
|
|
4cc58e8535 | ||
|
|
e1d9686eb5 | ||
|
|
c5cb4f9e69 | ||
|
|
4a60413e0b | ||
|
|
245f5d60b5 | ||
|
|
23420adfa6 | ||
|
|
c9828dfece | ||
|
|
f52c843fa2 | ||
|
|
b5e704a934 | ||
|
|
0173959e64 | ||
|
|
b00cd5cbc3 | ||
|
|
00aba7c646 | ||
|
|
34db18ea0a | ||
|
|
457c292d3c | ||
|
|
f481c17732 | ||
|
|
4a049b14ca | ||
|
|
ed2dae5884 | ||
|
|
df61ee4ac3 | ||
|
|
850bc12929 | ||
|
|
559e17eb13 | ||
|
|
4c4f6983d1 | ||
|
|
2dbeb151f3 | ||
|
|
82c137878c | ||
|
|
8845bd6ed3 | ||
|
|
c89d861c0e | ||
|
|
3ed536ace1 | ||
|
|
d13ad4ec4b | ||
|
|
cb5ac1fcf8 | ||
|
|
53797b9ead | ||
|
|
cb164e5cac | ||
|
|
c46ebebbb3 | ||
|
|
ff96036f62 | ||
|
|
6409786f8d | ||
|
|
05eb07caf5 | ||
|
|
f5b2a60549 | ||
|
|
620eaf1ada | ||
|
|
9193a20b44 | ||
|
|
bdcac9aed3 | ||
|
|
e9a28550cc | ||
|
|
f3cd3e74ae | ||
|
|
b853fc30c0 | ||
|
|
63e32a94c0 | ||
|
|
ef596f6a8b | ||
|
|
c830097511 | ||
|
|
d8790a4628 | ||
|
|
e06ef732d8 | ||
|
|
792a3e7db8 | ||
|
|
9b32842f66 | ||
|
|
9816979169 | ||
|
|
a74679dd95 | ||
|
|
a40059729e | ||
|
|
6bab967586 | ||
|
|
bb88b33469 | ||
|
|
c440470276 | ||
|
|
ef6164eecb | ||
|
|
7ad360d872 | ||
|
|
0870a33956 | ||
|
|
3ccc2f538d | ||
|
|
17375f0be2 | ||
|
|
5a987a162e | ||
|
|
5058fb9919 | ||
|
|
3335f8602b | ||
|
|
87ab1bbe54 | ||
|
|
f8d52b33f4 | ||
|
|
bc2dcdf9db | ||
|
|
93d1aacfb6 | ||
|
|
3ddaa4aff9 | ||
|
|
75488d568a | ||
|
|
3df80545bd | ||
|
|
c605fd9783 | ||
|
|
60719e7bff | ||
|
|
e9faa3277a | ||
|
|
35f7388725 | ||
|
|
ef1486a779 | ||
|
|
95c44165e9 | ||
|
|
7b816bb96f | ||
|
|
d2ac51aa4a | ||
|
|
26974b1f3a | ||
|
|
3960c6678d | ||
|
|
30b8991a73 | ||
|
|
75a94bcd96 | ||
|
|
e160cbf32b | ||
|
|
2da2e63245 | ||
|
|
f160ea6012 | ||
|
|
8057bc639d | ||
|
|
6f5ec5aa03 | ||
|
|
e343c9445c | ||
|
|
c73e374f1a | ||
|
|
d9b292f808 | ||
|
|
46ef2f313b | ||
|
|
92a705c79e | ||
|
|
12ebdd2574 | ||
|
|
ed2e951032 | ||
|
|
01a34a8846 | ||
|
|
b13174cb9c | ||
|
|
03ac6cab99 | ||
|
|
532eebaefb | ||
|
|
f825345b14 | ||
|
|
1409477ed7 | ||
|
|
d8e4294432 | ||
|
|
5d13908776 | ||
|
|
2832a73285 | ||
|
|
4152ed5809 | ||
|
|
8a90ed70ee | ||
|
|
e2646c08a5 | ||
|
|
7122ba820f | ||
|
|
efca2658ea | ||
|
|
91a714947c | ||
|
|
5d3ac649ba | ||
|
|
6443e726a6 | ||
|
|
6a755bda45 | ||
|
|
dab555ea3b | ||
|
|
a3bbad0060 | ||
|
|
73db717a0c | ||
|
|
f1552c8354 | ||
|
|
fb87fc32f0 | ||
|
|
b04b0c670d | ||
|
|
b33845df8a | ||
|
|
12c9d8d8f8 | ||
|
|
fe97bbcbbf | ||
|
|
10533c3eba | ||
|
|
b4415c90f9 | ||
|
|
2970bbdf6f | ||
|
|
95a420534d | ||
|
|
be56a21574 | ||
|
|
3741a235fe | ||
|
|
523b6c4c2d | ||
|
|
c3b2300310 | ||
|
|
1c4a20d86f | ||
|
|
f5af475012 | ||
|
|
f149b74332 | ||
|
|
8cd51386cb | ||
|
|
fb2cca88df | ||
|
|
031d4dc85a | ||
|
|
bf66d1c0f5 | ||
|
|
0edfa88755 | ||
|
|
dba1f3bcbc | ||
|
|
e204cc8f12 | ||
|
|
9e3c60f43f | ||
|
|
b03dd5b6b2 | ||
|
|
2aa9886ebb | ||
|
|
225e52b8b9 | ||
|
|
0ae1f9906f | ||
|
|
a5918b50f8 | ||
|
|
fa0374ee61 | ||
|
|
5b1e1aee3d | ||
|
|
3ba11d08fb | ||
|
|
35d0f42d07 | ||
|
|
f08f222337 | ||
|
|
7c4ad278d5 | ||
|
|
d3d2930bd2 | ||
|
|
b571c20acc | ||
|
|
f6cc0302c4 | ||
|
|
d9abcf0c83 | ||
|
|
5346849439 | ||
|
|
9742b0c5b1 | ||
|
|
41cfe26898 | ||
|
|
05a254eee9 | ||
|
|
8ef53aa363 | ||
|
|
470abc25e4 | ||
|
|
7fe6c7e78b | ||
|
|
6c8a4f8d9f | ||
|
|
2a8c8c0cbb | ||
|
|
12bbe5459d | ||
|
|
e0d9e05fd4 | ||
|
|
8bea61cb99 | ||
|
|
d09666aba9 | ||
|
|
48e0ba1d3d | ||
|
|
fb72be41f4 | ||
|
|
ef1db6d089 | ||
|
|
9d4246a285 | ||
|
|
e0f5c39297 | ||
|
|
e4d353b105 | ||
|
|
0d665495b8 | ||
|
|
fdaf857885 | ||
|
|
987b41d2cb | ||
|
|
444080f320 | ||
|
|
b5007ba276 | ||
|
|
bf19d2b156 | ||
|
|
a78515e5bf | ||
|
|
581126c748 | ||
|
|
7b3e345a2a | ||
|
|
cc7cefd3ed | ||
|
|
de6528b7d8 | ||
|
|
9efdfe0de4 | ||
|
|
f936918278 | ||
|
|
00125577a9 | ||
|
|
083c47b0f0 | ||
|
|
8021686154 | ||
|
|
f55ca296da | ||
|
|
0d6bca3e5d | ||
|
|
be1ff61e4a | ||
|
|
e36d41b46f | ||
|
|
b4693dc1b3 | ||
|
|
ca6010789e | ||
|
|
f8a80ccf5f | ||
|
|
6461dccbdc | ||
|
|
b06f4f2574 | ||
|
|
7695e3a9c8 | ||
|
|
ae475b408f | ||
|
|
6d9d048287 | ||
|
|
09e4d101c5 | ||
|
|
36340caf4c | ||
|
|
9a80ab0700 | ||
|
|
339d972d8d | ||
|
|
6d1e1030bd | ||
|
|
8a3665ce55 | ||
|
|
8351f0beab | ||
|
|
c26808c59e | ||
|
|
50b1dfc746 | ||
|
|
2ec8ec2327 | ||
|
|
168480b0ee | ||
|
|
58f78afc2e | ||
|
|
d60020f5ac | ||
|
|
437efe5473 | ||
|
|
5737904cec | ||
|
|
431a8af08b | ||
|
|
be29c4a243 | ||
|
|
0a027fad9a | ||
|
|
e21a8e3cc2 | ||
|
|
4279d6514d | ||
|
|
457db5994b | ||
|
|
71c942d290 | ||
|
|
ee5bc8523b | ||
|
|
70f4112643 | ||
|
|
174f6b7ea8 | ||
|
|
b14fb6f3c4 | ||
|
|
d6e27e776b | ||
|
|
e6839b45e3 | ||
|
|
229134cf31 | ||
|
|
b552173185 | ||
|
|
399a1f8fee | ||
|
|
9293d9b0ed | ||
|
|
4a75952fc7 | ||
|
|
c19906eb87 | ||
|
|
00ea189074 | ||
|
|
c8e2061e2a | ||
|
|
78dc93721d | ||
|
|
33beecddda | ||
|
|
e99d93ca30 | ||
|
|
0cc54484a4 | ||
|
|
4df7d59de0 | ||
|
|
9a54da84dc | ||
|
|
83315f7999 | ||
|
|
eb5ebfd304 | ||
|
|
77c9124a3d | ||
|
|
317da76ced | ||
|
|
e8d8ced006 | ||
|
|
0321f9bddf | ||
|
|
5937892ebc | ||
|
|
e429bea2f4 | ||
|
|
d33ad5c616 | ||
|
|
3e0614a293 | ||
|
|
80e39212b8 | ||
|
|
d7a6ceb3a6 | ||
|
|
9d3a9e9d1e | ||
|
|
e95c789d4b | ||
|
|
a508161265 | ||
|
|
a935c80adc | ||
|
|
1d607bf13e | ||
|
|
ad57467c93 | ||
|
|
430eaefc3e | ||
|
|
3d60724651 | ||
|
|
31d9f663fc | ||
|
|
f324697c7c | ||
|
|
befae7e677 | ||
|
|
7a2c2587ba | ||
|
|
cf3f9d68b3 | ||
|
|
c99f7c554f | ||
|
|
f01c0945da | ||
|
|
1bcd0c4471 | ||
|
|
07f28e6ec7 | ||
|
|
7074f9f776 | ||
|
|
5029a9ad5a | ||
|
|
0bff30e954 | ||
|
|
601d37aa3d | ||
|
|
e2b1ca10c9 | ||
|
|
11942ed6dc | ||
|
|
f65914599b | ||
|
|
392c31f5fe | ||
|
|
f4ddaacb3c | ||
|
|
db64aef666 | ||
|
|
95c8bbd7fb | ||
|
|
67b4f37d53 | ||
|
|
427288c16b | ||
|
|
4202eee084 | ||
|
|
eca53bfab0 | ||
|
|
cd76d109f5 | ||
|
|
2787f9f19b | ||
|
|
4e3c6e52aa | ||
|
|
ae5ceedfe9 | ||
|
|
022e2eb1db | ||
|
|
2fe1ef4774 | ||
|
|
70afb2dd4d | ||
|
|
efa02e3f94 | ||
|
|
5eefd41059 | ||
|
|
6ca1b7c240 | ||
|
|
31191fd48c | ||
|
|
a6ed54ed48 | ||
|
|
95132b4fe1 | ||
|
|
df0c5d99e6 | ||
|
|
4b0724530e | ||
|
|
e5681a93f9 | ||
|
|
fb838a694e | ||
|
|
8c6f188767 | ||
|
|
55e15dc79e | ||
|
|
514073cb75 | ||
|
|
f6ece6522e | ||
|
|
817f0997cf | ||
|
|
1d651270d3 | ||
|
|
6ea58fac1a | ||
|
|
c7eb63d4ff | ||
|
|
4062365b19 | ||
|
|
7f48a927de | ||
|
|
4a740cc556 | ||
|
|
628b92f20b | ||
|
|
8f9616c230 | ||
|
|
df475dfde5 | ||
|
|
a835b56a51 | ||
|
|
5555a66638 | ||
|
|
dad355b932 | ||
|
|
90a097d45c | ||
|
|
dba01bc014 | ||
|
|
1d0a3ce998 | ||
|
|
076ad850bb | ||
|
|
868546abe4 | ||
|
|
6b421fabd1 | ||
|
|
ab3c9658bd | ||
|
|
8c7886d429 | ||
|
|
110a3e7e0f | ||
|
|
ee0b70f51b | ||
|
|
df48061a8f | ||
|
|
b1387d50f0 | ||
|
|
73dd218a95 | ||
|
|
ee4cedcc81 | ||
|
|
d1b2f9c6a9 | ||
|
|
40dee44978 | ||
|
|
db04c70d4a | ||
|
|
7401c6ac97 | ||
|
|
f7a1222cc3 | ||
|
|
069f09c43c | ||
|
|
35f821f6a1 | ||
|
|
67f60847ee | ||
|
|
65f6b142a5 | ||
|
|
81bda736d8 | ||
|
|
ec3998a9b8 | ||
|
|
01be77e0bc | ||
|
|
6f40b8d0a1 | ||
|
|
a083aa04b2 | ||
|
|
1f33348cf6 | ||
|
|
a45651933c | ||
|
|
ab57fbff73 | ||
|
|
6b3f774f05 | ||
|
|
1ef2880c6c | ||
|
|
ace152d8a3 | ||
|
|
8d08036a9d | ||
|
|
b0876fae64 | ||
|
|
01633776d3 | ||
|
|
e4202228eb | ||
|
|
79d5049273 | ||
|
|
9120e3b790 | ||
|
|
974e6140b3 | ||
|
|
1fd338cc6c | ||
|
|
bc61a81d7a | ||
|
|
06b30d1cc3 | ||
|
|
785973bb5e | ||
|
|
78efefcea1 | ||
|
|
1bb6242020 | ||
|
|
1af426bf84 | ||
|
|
dc4762a69a | ||
|
|
a785b77da9 | ||
|
|
1342767805 | ||
|
|
a3fc3233cd | ||
|
|
8093d70643 | ||
|
|
9d9ec84b54 | ||
|
|
6b26088c27 | ||
|
|
43af715caa | ||
|
|
c6eea488b1 | ||
|
|
b1c3999a81 | ||
|
|
4f19403518 | ||
|
|
4d1b537f78 | ||
|
|
dd54db47a9 | ||
|
|
44775e4644 | ||
|
|
805921d6a3 | ||
|
|
d7cd6edaa5 | ||
|
|
6a472bd6da | ||
|
|
d0c6ce9fd7 | ||
|
|
df48e2f9e2 | ||
|
|
2b4b3bbf18 | ||
|
|
6ea5027bf6 | ||
|
|
384ccb4fc7 | ||
|
|
ff763b099e | ||
|
|
741ae4c04b | ||
|
|
d84ec941c0 | ||
|
|
f0b5dd9799 | ||
|
|
cec4371b51 | ||
|
|
c0ff39c59d | ||
|
|
022634f2d9 | ||
|
|
392f576d07 | ||
|
|
6d59e2125e | ||
|
|
6476a1d2e0 | ||
|
|
ca998fb4b0 | ||
|
|
48631c4ea6 | ||
|
|
294be2a772 | ||
|
|
533688a67a | ||
|
|
4309e2371d | ||
|
|
ea30c84240 | ||
|
|
d4c2b13dda | ||
|
|
1decae2938 | ||
|
|
145ec86d3b | ||
|
|
e17c96b20f | ||
|
|
2cfcefd9eb | ||
|
|
356e950122 | ||
|
|
64dee63b73 | ||
|
|
431d013d32 | ||
|
|
40eb8455d0 | ||
|
|
c46b36509b | ||
|
|
f7ff63132d | ||
|
|
e17036a7a5 | ||
|
|
e678c41f56 | ||
|
|
ee3462592a | ||
|
|
521f921424 | ||
|
|
ee47a10b37 | ||
|
|
b81700144d | ||
|
|
9f28dd61d3 | ||
|
|
dad7d366b3 | ||
|
|
209b78a3ec | ||
|
|
17f34d1f95 | ||
|
|
26699cd9ef | ||
|
|
907db09a58 | ||
|
|
d506fa0565 | ||
|
|
de1a0d48a6 | ||
|
|
a20e5fefd3 | ||
|
|
44558560f9 | ||
|
|
677f154f03 | ||
|
|
0adaf064e7 | ||
|
|
3c828feef2 | ||
|
|
4d3c5e61fd | ||
|
|
67baefb52a | ||
|
|
d509a4fa8c | ||
|
|
cad1c12b7b | ||
|
|
9f751d8715 | ||
|
|
cf8a1dff33 | ||
|
|
973d98457f | ||
|
|
ce262f810d | ||
|
|
bd31c2b484 | ||
|
|
ef0e610802 | ||
|
|
3040d4c83f | ||
|
|
c9252e8639 | ||
|
|
e2e701460b | ||
|
|
f0c3641ebd | ||
|
|
025f6d7701 | ||
|
|
c68516ca8d | ||
|
|
a9e739e08d | ||
|
|
cc15ff73b7 | ||
|
|
fd3a4536e7 | ||
|
|
75caafb5ac | ||
|
|
cf2f8165eb | ||
|
|
a2f80740d7 | ||
|
|
c7703791d1 | ||
|
|
c39e9e9c61 | ||
|
|
713059319c | ||
|
|
d7a7509da6 | ||
|
|
57e6e7b2c5 | ||
|
|
d675977f10 | ||
|
|
7ef911e17d | ||
|
|
9a384c5045 | ||
|
|
2b3753d99c | ||
|
|
d173ab9f45 | ||
|
|
fbd0e8e26a | ||
|
|
50414b333a | ||
|
|
ee2fdb3d25 | ||
|
|
2b884377d4 | ||
|
|
36a20f7b00 | ||
|
|
35cbf49b08 | ||
|
|
21836cedda | ||
|
|
0a892b72e6 | ||
|
|
2c6e5a6e73 | ||
|
|
2cb25cf5a8 | ||
|
|
e07916425e | ||
|
|
f62e2374e4 | ||
|
|
eb4ee2f0af | ||
|
|
be3c76da20 | ||
|
|
b15e58621c | ||
|
|
601a6c9e3d | ||
|
|
3ff184348e | ||
|
|
c8efcd2be1 | ||
|
|
9c116a2975 | ||
|
|
8818b5d8df | ||
|
|
e96dfadae7 | ||
|
|
deb564e5a6 | ||
|
|
ffc71c348c | ||
|
|
19df7b94ea | ||
|
|
a3ecc79f0d | ||
|
|
b140b15119 | ||
|
|
b7b2b6075a | ||
|
|
7525e152b8 | ||
|
|
bc9dfec615 | ||
|
|
7824739034 | ||
|
|
e7b60a3628 | ||
|
|
97c3061b8f | ||
|
|
1a042c873c | ||
|
|
064aa3a150 | ||
|
|
8a178ebe55 | ||
|
|
9a97f0bced | ||
|
|
5c7471910d | ||
|
|
27b6517594 | ||
|
|
cd4dacb406 | ||
|
|
5f6faa1bf0 | ||
|
|
cfc5a952f8 | ||
|
|
10b186957b | ||
|
|
f597481736 | ||
|
|
9fa97e6187 | ||
|
|
3844321f20 | ||
|
|
8efe08c45b | ||
|
|
c78563b452 | ||
|
|
122e172092 | ||
|
|
8f85c0d167 | ||
|
|
ac6d1ce174 | ||
|
|
03e0bf4c0e | ||
|
|
1cb183f5e1 | ||
|
|
d78a553910 | ||
|
|
1326532850 | ||
|
|
408508247d | ||
|
|
e2be09e028 | ||
|
|
c7c14f184c | ||
|
|
24ca919f33 | ||
|
|
85fe783652 | ||
|
|
04b6bf3b74 | ||
|
|
8b2fb105d0 | ||
|
|
558caa232d | ||
|
|
d8886edbeb | ||
|
|
f72ecf1791 | ||
|
|
45d1b978d8 | ||
|
|
0b780cc065 | ||
|
|
5263f6598c | ||
|
|
f9e60e7ef6 | ||
|
|
45c00d0b1f | ||
|
|
c70e2f7d3e | ||
|
|
1ec0bed1ce | ||
|
|
46b5e4513a | ||
|
|
ffb5196f3b | ||
|
|
107f1af3c4 | ||
|
|
03b1484c96 | ||
|
|
aaf64ad63e | ||
|
|
f548e795d5 | ||
|
|
767da68239 | ||
|
|
f44e0ff4bd | ||
|
|
7f226f2ea1 | ||
|
|
1c82a39a6e | ||
|
|
b70cf276b4 | ||
|
|
80b2df2ef4 | ||
|
|
ad27452275 | ||
|
|
196a0b6b7e | ||
|
|
f356f63994 | ||
|
|
4947fb6b57 | ||
|
|
622f12b84e | ||
|
|
c1f84ce603 | ||
|
|
14912461ad | ||
|
|
a9f265e8b7 | ||
|
|
199ee20adb | ||
|
|
7c3c87abf6 | ||
|
|
b0ad44bcf2 | ||
|
|
9ba808ec3c | ||
|
|
eacf849e3a | ||
|
|
42c2791182 | ||
|
|
390e883ecf | ||
|
|
9fe631ee3f | ||
|
|
82d8c4b230 | ||
|
|
9775bb0a9d | ||
|
|
139c9a2d6d | ||
|
|
542880f87b | ||
|
|
3846286bee | ||
|
|
72be402a2d | ||
|
|
c6b9cb759a | ||
|
|
13ccf269a6 | ||
|
|
fcc7a0255a | ||
|
|
e7447f38cf | ||
|
|
7ae35631a6 | ||
|
|
55ad228166 | ||
|
|
48fb051253 | ||
|
|
f97e8b7eeb | ||
|
|
84f914742b | ||
|
|
0d704f3715 | ||
|
|
d8896eb210 | ||
|
|
b9d74e2012 | ||
|
|
803fba9cfd | ||
|
|
581548ed67 | ||
|
|
9a8c0f8e7e | ||
|
|
328f569c16 | ||
|
|
7931654a7c | ||
|
|
6eb0c10339 | ||
|
|
269f294d19 | ||
|
|
d19e2f1350 | ||
|
|
3b745b82db | ||
|
|
02f30c2a03 | ||
|
|
54002b386a | ||
|
|
0e2ff077af | ||
|
|
012cf70321 | ||
|
|
9da0ba52db | ||
|
|
7bfb07e59e | ||
|
|
9cb2c4b1ff | ||
|
|
8c088fad0f | ||
|
|
392722aed0 | ||
|
|
fabfb30fc4 | ||
|
|
c7cb021e92 | ||
|
|
be05d9c3e9 | ||
|
|
e38690e8d9 | ||
|
|
7172115559 | ||
|
|
7314019832 | ||
|
|
b9684941c4 | ||
|
|
a27289352c | ||
|
|
dc77009e55 | ||
|
|
d12e10830d | ||
|
|
936f6c22f0 | ||
|
|
9712be903d | ||
|
|
7441cefdbb | ||
|
|
7bf83603c8 | ||
|
|
6768bc1705 | ||
|
|
707cb37212 | ||
|
|
1cf0a8b78e | ||
|
|
3d2cb37af0 | ||
|
|
09fbd6f5e0 | ||
|
|
474bef65cb | ||
|
|
e97454c4a5 | ||
|
|
936dc74a27 | ||
|
|
362e4266bb | ||
|
|
d08f2e10f1 | ||
|
|
f1e5ee74a6 | ||
|
|
7832d33348 | ||
|
|
de25f94fae | ||
|
|
98625e9c36 | ||
|
|
5768cc98b6 | ||
|
|
da1cc6c1c5 | ||
|
|
50384fac97 | ||
|
|
424bd8a6cd | ||
|
|
421fae2b57 | ||
|
|
8bb8615a5a | ||
|
|
80f8692e92 | ||
|
|
25623c758d | ||
|
|
e1a0a195a9 | ||
|
|
f8cf49a90c | ||
|
|
ac9f8595c9 | ||
|
|
97f22bbff6 | ||
|
|
d6ddfa9d18 | ||
|
|
5e48ae8bac | ||
|
|
cdd943ded4 | ||
|
|
39b2bf963c | ||
|
|
06827fd437 | ||
|
|
70ccc1cf6d | ||
|
|
f801c47d1e | ||
|
|
01c0e8da93 | ||
|
|
e844ad6db3 | ||
|
|
3f8c042bc3 | ||
|
|
707ad705a4 | ||
|
|
79a780004f | ||
|
|
81b28bc1bd | ||
|
|
e9c4d540b1 | ||
|
|
50d9718c68 | ||
|
|
e39da2b2bb | ||
|
|
f08ec1522a | ||
|
|
e61e93712c | ||
|
|
c9e198e02e | ||
|
|
0ce181b578 | ||
|
|
99950fe448 | ||
|
|
d8daf84a09 | ||
|
|
3d32202b3a | ||
|
|
4f4d562670 | ||
|
|
28fc9c69da | ||
|
|
6edf00e699 | ||
|
|
a0d6445dda | ||
|
|
e74797037c | ||
|
|
3fb125dba7 | ||
|
|
3c8640f9ad | ||
|
|
2e63a4c83f | ||
|
|
9da5bee178 | ||
|
|
2fc6f3b542 | ||
|
|
4754f42e51 | ||
|
|
941fe918fb | ||
|
|
f4e82e972e | ||
|
|
f66dbf111d | ||
|
|
1a2da5c49d | ||
|
|
8327dabb29 | ||
|
|
195ea6c140 | ||
|
|
fcc0707174 | ||
|
|
aa9c3cd634 | ||
|
|
b541341681 | ||
|
|
e5d50aca6d | ||
|
|
b738f4d4ff | ||
|
|
5c38aabf0e | ||
|
|
03e9ed6947 | ||
|
|
63493fad22 | ||
|
|
696b857f91 | ||
|
|
53ae2bbe0d | ||
|
|
01069e919d | ||
|
|
fc0095f505 | ||
|
|
421528e130 | ||
|
|
7b70356515 | ||
|
|
e803505aad | ||
|
|
ad1ff455a0 | ||
|
|
7692cb5c3d | ||
|
|
a94e208db3 | ||
|
|
ba9d5cf3ff | ||
|
|
0dff0d03f9 | ||
|
|
c92aca5d19 | ||
|
|
5762baeed7 | ||
|
|
601cf01222 | ||
|
|
0eef48b516 | ||
|
|
3c1954f329 | ||
|
|
cc29640604 | ||
|
|
d0329460b8 | ||
|
|
717c0c96de | ||
|
|
b3f161efce | ||
|
|
0286940163 | ||
|
|
d4a4ee386f | ||
|
|
33498f9133 | ||
|
|
da3d4d1bef | ||
|
|
c69b8870d2 | ||
|
|
85b55b31b6 | ||
|
|
948919e42b | ||
|
|
85c40090ed | ||
|
|
98b50ec145 | ||
|
|
1ab4f7d2d0 | ||
|
|
98801b6fa6 | ||
|
|
bdd68284b5 | ||
|
|
a61d70ea9a | ||
|
|
e271719dd2 | ||
|
|
a08efa1fc5 | ||
|
|
ffac3600e0 | ||
|
|
6499f8ac24 | ||
|
|
63307cb1d8 | ||
|
|
0a6fb362f6 | ||
|
|
457fcbfb90 | ||
|
|
3b6277b9e2 | ||
|
|
e6e61028b1 | ||
|
|
e9be66126b | ||
|
|
681fe7f2f2 | ||
|
|
0196920533 | ||
|
|
5f5bb3ace0 | ||
|
|
3c970e0122 | ||
|
|
43af6e3b8a | ||
|
|
ac8cfc12fb | ||
|
|
b19472e0f5 | ||
|
|
b4d08044a0 | ||
|
|
b2fe02e63f | ||
|
|
d1a5d30233 | ||
|
|
bba30138e1 | ||
|
|
082546a1e7 | ||
|
|
41ffd58e26 | ||
|
|
b4f24e4e1e | ||
|
|
75c8ffd715 | ||
|
|
688606ff7c | ||
|
|
2e3f9dffa7 | ||
|
|
859033415b | ||
|
|
1f1b52b580 | ||
|
|
4e7bf80447 | ||
|
|
e39df50884 | ||
|
|
8d6a9d45c2 | ||
|
|
0862e85577 | ||
|
|
0b9a37139e | ||
|
|
6357f7b811 | ||
|
|
24197dc422 | ||
|
|
9bc2ab3245 | ||
|
|
bf4e8c490c | ||
|
|
590a4d70ee | ||
|
|
40341d2ddd | ||
|
|
0cd2c41ee3 | ||
|
|
5d65cdb6a8 | ||
|
|
759a15ccbb | ||
|
|
5aead5eebe | ||
|
|
a325a5fdee | ||
|
|
931e91f1a7 | ||
|
|
6e13509720 | ||
|
|
5b4844e71e | ||
|
|
66cbbea9bb | ||
|
|
b65119b8a9 | ||
|
|
86703da917 | ||
|
|
610e9afdf0 | ||
|
|
e3af9f68d3 | ||
|
|
747ac85e7c | ||
|
|
8c23f9e526 | ||
|
|
e6e339fcd3 | ||
|
|
f04680ac47 | ||
|
|
90985cc846 | ||
|
|
63917944f2 | ||
|
|
793605f80f | ||
|
|
93a281c7e3 | ||
|
|
8f1c6c4d74 | ||
|
|
1610dba7ab | ||
|
|
d9d419991c | ||
|
|
f9e86b19aa | ||
|
|
fffb976e43 | ||
|
|
0da566b49f | ||
|
|
da55717d20 | ||
|
|
254836902c | ||
|
|
d01f8d1865 | ||
|
|
a46c3f7007 | ||
|
|
4e58062025 | ||
|
|
aa3be0535f | ||
|
|
8ebf5e007c | ||
|
|
c2957e7f12 | ||
|
|
bf7c26d357 | ||
|
|
0547102bdd | ||
|
|
a07369ce69 | ||
|
|
21c5610431 | ||
|
|
6dbdf72f40 | ||
|
|
76a6bba643 | ||
|
|
b0b8222a96 | ||
|
|
0d87b8fac5 | ||
|
|
5227b1f98b | ||
|
|
c4f7c5c1cd | ||
|
|
33a84a609c | ||
|
|
037111715a | ||
|
|
93271b93e6 | ||
|
|
04488ada06 | ||
|
|
21c7002c68 | ||
|
|
97cae0e355 | ||
|
|
85fc0a59b1 | ||
|
|
eddd45bbc3 | ||
|
|
e2339697b9 | ||
|
|
cfe9b408ce | ||
|
|
83e8018787 | ||
|
|
372d2008a6 | ||
|
|
ab9a36f8c1 | ||
|
|
2458c3a7c6 | ||
|
|
e827e6fa21 | ||
|
|
342f0981d8 | ||
|
|
9c19e88288 | ||
|
|
7329bd4910 | ||
|
|
b9ad62226e | ||
|
|
3bff3bcbe4 | ||
|
|
a06fdad034 | ||
|
|
a49bd98ec6 | ||
|
|
90f41108d3 | ||
|
|
382c31ddf2 | ||
|
|
871ca4dda6 | ||
|
|
fa003f8233 | ||
|
|
12f37bcfad | ||
|
|
e86c712424 | ||
|
|
e8a9935cb2 | ||
|
|
f353131b44 | ||
|
|
7c4a2ba329 | ||
|
|
dfb543bd94 | ||
|
|
c912eef040 | ||
|
|
5f06d3a427 | ||
|
|
a70fa9abc5 | ||
|
|
5956277863 | ||
|
|
d3f37eeba4 | ||
|
|
ea48332f46 | ||
|
|
eb12511646 | ||
|
|
aa11ddf32c | ||
|
|
a348f64833 | ||
|
|
626a96e20f | ||
|
|
60995ff04b | ||
|
|
ba4e711715 | ||
|
|
891b184195 | ||
|
|
5414dc398e | ||
|
|
917559f883 | ||
|
|
9f2fb76309 | ||
|
|
b6692849d6 | ||
|
|
b27f6d5957 | ||
|
|
cda1e28b32 | ||
|
|
8a3e71d3d0 | ||
|
|
149ae2866a | ||
|
|
21707171bb | ||
|
|
2ebdf738ca | ||
|
|
0e871a89aa | ||
|
|
2d4f194f0d | ||
|
|
ce94a9b2df | ||
|
|
bfbb3c7068 | ||
|
|
6d1fd20542 | ||
|
|
bb8cab3a02 | ||
|
|
10435c23c2 | ||
|
|
01142cdc8c | ||
|
|
fc0adfd965 | ||
|
|
8803eeb890 | ||
|
|
25be705bcf | ||
|
|
d8f214a267 | ||
|
|
f74fb846e5 | ||
|
|
8ae110691e | ||
|
|
abff2292c1 | ||
|
|
7d690bbad2 | ||
|
|
7298de646a | ||
|
|
3c2f3ecd8e | ||
|
|
ac2c21e046 | ||
|
|
c817ba9eae | ||
|
|
92827ae647 | ||
|
|
5bdb405635 | ||
|
|
9beae321d7 | ||
|
|
353db678a2 | ||
|
|
9b973ef99c | ||
|
|
308d171dae | ||
|
|
7057bc663e | ||
|
|
d84cff0baf | ||
|
|
645890ccc8 | ||
|
|
cc1336a616 | ||
|
|
9b32f18109 | ||
|
|
a0321376d5 | ||
|
|
0a6e264d09 | ||
|
|
afec07b124 | ||
|
|
8a34fede55 | ||
|
|
686b5f297e | ||
|
|
6609d219f4 | ||
|
|
804998ca69 | ||
|
|
de5254469c | ||
|
|
ef879bab9c | ||
|
|
f729985346 | ||
|
|
1e998b6a3f | ||
|
|
77e9460eea | ||
|
|
81a58186c6 | ||
|
|
ac149be2bb | ||
|
|
a76a163db5 | ||
|
|
4ae59d3f77 | ||
|
|
d83a450260 | ||
|
|
61acc0784e | ||
|
|
f05890719d | ||
|
|
0f4e7921ee | ||
|
|
ec8c96b8e6 | ||
|
|
f0b29e15c3 | ||
|
|
8ad62106e0 | ||
|
|
36eb52424f | ||
|
|
c9ce2f47dc | ||
|
|
5035ed61ee | ||
|
|
a4cc10c41b | ||
|
|
5730ce5f53 | ||
|
|
27524bfc3d | ||
|
|
8843ded1c9 | ||
|
|
64b497589f | ||
|
|
ee48fd7151 | ||
|
|
291cd5e23f | ||
|
|
be668cfd9a | ||
|
|
f889c1c75a | ||
|
|
55c5f3e9fd | ||
|
|
242d335095 | ||
|
|
c763db94ce | ||
|
|
a391732600 | ||
|
|
06b45fcace | ||
|
|
bdac5f7d5b | ||
|
|
74865efddb | ||
|
|
b1f4d16692 | ||
|
|
01b8e2c3f5 | ||
|
|
8c1ef94b26 | ||
|
|
507fbc7abf | ||
|
|
2f8002c567 | ||
|
|
551e28d8e0 | ||
|
|
e84163e838 | ||
|
|
3a64b835fb | ||
|
|
08a44ed4dd | ||
|
|
4b8cc23a86 | ||
|
|
c4b2de8939 | ||
|
|
e2a603dcda | ||
|
|
294d01def6 | ||
|
|
05f289f912 | ||
|
|
4343c4067e | ||
|
|
c167dcc4e9 | ||
|
|
9a67176719 | ||
|
|
42d1a4755a | ||
|
|
1690e6933f | ||
|
|
5bbc15583e | ||
|
|
e03aabd972 | ||
|
|
6f5968eeb0 | ||
|
|
71b6e8f12a | ||
|
|
9c486f6e69 | ||
|
|
2f24588c66 | ||
|
|
6b1ea326a0 | ||
|
|
4512d0dbac | ||
|
|
511c4da6a3 | ||
|
|
af8fdf1dbe | ||
|
|
b7d12de0a7 | ||
|
|
2d870fc84a | ||
|
|
6e87929603 | ||
|
|
2b441df9e9 | ||
|
|
3e672ac178 | ||
|
|
12a24d5c86 | ||
|
|
dec1a0886d | ||
|
|
a82e45ad71 | ||
|
|
82e5750d71 | ||
|
|
6a3eb3f3d7 | ||
|
|
533a739e2b | ||
|
|
b12a8331f1 | ||
|
|
8f4ec036e7 | ||
|
|
cba36f2240 | ||
|
|
b54ffbefe4 | ||
|
|
6d470e4da0 | ||
|
|
4e1ebec8c2 | ||
|
|
e9779258e9 | ||
|
|
5c83f45bc6 | ||
|
|
eb65aca1f4 | ||
|
|
5d7e393c93 | ||
|
|
d6d01102fe | ||
|
|
1ad802b194 | ||
|
|
4a191b65bb | ||
|
|
65cc9f4e1b | ||
|
|
9f4b13b523 | ||
|
|
97e1b62a1e | ||
|
|
3ac966a38b | ||
|
|
6f1fda0a48 | ||
|
|
c00b486864 | ||
|
|
7928fff645 | ||
|
|
0285f5a1ad | ||
|
|
56e5c8be32 | ||
|
|
ef569976cb | ||
|
|
f8ae048d60 | ||
|
|
c51f4df0ca | ||
|
|
84453bf15c | ||
|
|
917350f058 | ||
|
|
0c58662eb6 | ||
|
|
52ca75855e | ||
|
|
49a07651b9 | ||
|
|
90a8ba3789 | ||
|
|
734c3199f0 | ||
|
|
64567646d9 | ||
|
|
1a1638cc7f | ||
|
|
fdf055312c | ||
|
|
b0b8b06b56 | ||
|
|
9c1ffb1adf | ||
|
|
563bafea08 | ||
|
|
efe4c32ffd | ||
|
|
0f003a9a77 | ||
|
|
3eff929652 | ||
|
|
3010ebfef5 | ||
|
|
ff14f47781 | ||
|
|
0b99a5caae | ||
|
|
467b96f2f2 | ||
|
|
18751a05c0 | ||
|
|
9007623d0c | ||
|
|
c1abb7038d | ||
|
|
53f7eebc4d | ||
|
|
d54d29122d | ||
|
|
1d71791b3c | ||
|
|
0c6145d4f2 | ||
|
|
62e273c5bb | ||
|
|
d82f399be7 | ||
|
|
20a7050564 | ||
|
|
b235f411b0 | ||
|
|
99faeeacfc | ||
|
|
08ad4f389d | ||
|
|
edba63d0d9 | ||
|
|
e33f882abc | ||
|
|
53e059f4f7 | ||
|
|
830364e026 | ||
|
|
332714a925 | ||
|
|
1eee6deb22 | ||
|
|
547f138703 | ||
|
|
b6c345fc3c | ||
|
|
29c40a4957 | ||
|
|
72af821e1b | ||
|
|
5fe4d10f38 | ||
|
|
3b36470e84 | ||
|
|
f9e8a4050c | ||
|
|
ce308286df | ||
|
|
b670973226 | ||
|
|
01b11b8a53 | ||
|
|
75c47a8659 | ||
|
|
5b4e0ed483 | ||
|
|
7432a7f346 | ||
|
|
0830fc3b5a | ||
|
|
ab2c901e85 | ||
|
|
3774cc54c7 | ||
|
|
330ed8e4ae | ||
|
|
eddae44d6d | ||
|
|
226488717a | ||
|
|
2f9dabd344 | ||
|
|
cf1b7c0adb | ||
|
|
eb57d0d431 | ||
|
|
8116ee015a | ||
|
|
0fa3cec61f | ||
|
|
eb60464177 | ||
|
|
44b2651a0f | ||
|
|
f2e91b5ed2 | ||
|
|
e72b60c882 | ||
|
|
5263babc9a | ||
|
|
9034b50b3a | ||
|
|
fdd7fe1948 | ||
|
|
8e7b8b70dc | ||
|
|
9bf24bdff5 | ||
|
|
ee53edd96d | ||
|
|
1bc5d7038d | ||
|
|
8a6b8b415c | ||
|
|
86174c81c8 | ||
|
|
374500d7b0 | ||
|
|
319a141967 | ||
|
|
c0d0062ef8 | ||
|
|
aaaa34e119 | ||
|
|
fba9502a9d | ||
|
|
6670cc1534 | ||
|
|
970729dca0 | ||
|
|
6627586d6e | ||
|
|
9f364209af | ||
|
|
c31a39a052 | ||
|
|
36fda90814 | ||
|
|
7ccfbc16e9 | ||
|
|
ad3b527bc4 | ||
|
|
f7eef3f456 | ||
|
|
1870364888 | ||
|
|
d7dfc85cf7 | ||
|
|
c047e5f16a | ||
|
|
8d21f63ec2 | ||
|
|
85b6a91e5c | ||
|
|
c788e30b66 | ||
|
|
a99caed693 | ||
|
|
9589fb5275 | ||
|
|
1a6dc530a4 | ||
|
|
60835871eb |
@@ -1,104 +0,0 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
|
||||
lin-sys:
|
||||
docker:
|
||||
- image: kovidgoyal/kitty-test:latest
|
||||
steps:
|
||||
- checkout
|
||||
- run: if grep -Inr '\s$' kitty kitty_tests kittens docs *.py *.asciidoc *.rst .gitattributes .gitignore; then echo Trailing whitespace found, aborting.; exit 1; fi
|
||||
- run: python3 -m flake8 --count .
|
||||
- run: python3 setup.py build --debug --verbose
|
||||
- run: ./kitty/launcher/kitty +launch test.py
|
||||
- run: make FAIL_WARN=-W man
|
||||
- run: make FAIL_WARN=-W html
|
||||
|
||||
lin-package:
|
||||
docker:
|
||||
- image: kovidgoyal/kitty-test:latest
|
||||
steps:
|
||||
- checkout
|
||||
- run: python3 setup.py linux-package --update-check-interval=0
|
||||
|
||||
lin-35:
|
||||
docker:
|
||||
- image: kovidgoyal/kitty-test:latest
|
||||
environment:
|
||||
CC: gcc
|
||||
CFLAGS: -funsigned-char
|
||||
LD_LIBRARY_PATH: /opt/py3.5/lib
|
||||
ASAN_OPTIONS: leak_check_at_exit=0
|
||||
steps:
|
||||
- checkout
|
||||
- run: /opt/py3.5/bin/python3 setup.py build --debug --verbose --sanitize
|
||||
- run: ./kitty/launcher/kitty +launch test.py
|
||||
|
||||
lin-37:
|
||||
docker:
|
||||
- image: kovidgoyal/kitty-test:latest
|
||||
environment:
|
||||
CC: clang
|
||||
LD_LIBRARY_PATH: /opt/py3.7/lib
|
||||
ASAN_OPTIONS: leak_check_at_exit=0
|
||||
steps:
|
||||
- checkout
|
||||
- run: /opt/py3.7/bin/python3 setup.py build --debug --verbose --sanitize
|
||||
- run: ./kitty/launcher/kitty +launch test.py
|
||||
|
||||
lin-bundle:
|
||||
docker:
|
||||
- image: kovidgoyal/kitty-test:latest
|
||||
environment:
|
||||
ASAN_OPTIONS: leak_check_at_exit=0
|
||||
steps:
|
||||
- checkout
|
||||
- run: echo "export LD_LIBRARY_PATH=$SW/lib" >> $BASH_ENV
|
||||
- run: echo "export PKG_CONFIG_PATH=$SW/lib/pkgconfig" >> $BASH_ENV
|
||||
- run: echo "export PYTHONHOME=$SW" >> $BASH_ENV
|
||||
- run: $SW/bin/python3 setup.py build --debug --verbose
|
||||
- run: ./kitty/launcher/kitty +launch test.py
|
||||
|
||||
mac-bundle:
|
||||
macos:
|
||||
xcode: "10.1.0"
|
||||
environment:
|
||||
LC_ALL: en_US.UTF-8
|
||||
LANG: en_US.UTF-8
|
||||
SW: /Users/Shared/buildbot/sw/sw
|
||||
steps:
|
||||
- checkout
|
||||
- run: mkdir -p $SW
|
||||
- run: curl https://download.calibre-ebook.com/travis/kitty/osx.tar.xz | tar xJ -C $SW
|
||||
- run: echo "export PATH=$SW/bin:$PATH" >> $BASH_ENV
|
||||
- run: python3 setup.py build --debug --verbose
|
||||
- run: ./kitty/launcher/kitty +launch test.py
|
||||
|
||||
mac-brew:
|
||||
macos:
|
||||
xcode: "10.2.0"
|
||||
environment:
|
||||
LC_ALL: en_US.UTF-8
|
||||
LANG: en_US.UTF-8
|
||||
steps:
|
||||
- checkout
|
||||
- run: brew bundle
|
||||
- run: python3 setup.py build --debug --verbose
|
||||
- run: ./kitty/launcher/kitty +launch test.py
|
||||
- run: echo "export PATH=/usr/local/opt/sphinx-doc/bin:$PATH" >> $BASH_ENV
|
||||
- run: python3 setup.py linux-package --verbose
|
||||
- run: python3 setup.py kitty.app --verbose
|
||||
- run: kitty.app/Contents/MacOS/kitty +runpy "from kitty.constants import *; print(kitty_exe())"
|
||||
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
all_jobs:
|
||||
jobs:
|
||||
- mac-bundle
|
||||
- lin-sys
|
||||
- lin-35
|
||||
- lin-37
|
||||
- lin-package
|
||||
- lin-bundle
|
||||
- mac-brew
|
||||
@@ -1,55 +0,0 @@
|
||||
# When changing this file, go to
|
||||
# https://cloud.docker.com/repository/docker/kovidgoyal/kitty-test/builds and
|
||||
# click the "Trigger" button under Automated builds to rebuild
|
||||
FROM ubuntu:latest
|
||||
|
||||
# make Apt non-interactive
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
RUN echo 'APT::Get::Assume-Yes "true";' > /etc/apt/apt.conf.d/90circleci \
|
||||
&& echo 'DPkg::Options "--force-confnew";' >> /etc/apt/apt.conf.d/90circleci \
|
||||
&& echo 'Acquire::Languages "none";' >> /etc/apt/apt.conf.d/90circleci
|
||||
|
||||
# System setup
|
||||
RUN apt-get update && mkdir -p /usr/share/man/man1 && apt-get install -y python3-flake8 python3-pip python3-pil clang git apt locales sudo openssh-client ca-certificates tar gzip parallel net-tools netcat unzip zip bzip2 gnupg curl wget build-essential apt-utils
|
||||
|
||||
# Setup circleci user
|
||||
RUN groupadd --gid 3434 circleci \
|
||||
&& useradd --uid 3434 --gid circleci --shell /bin/bash --create-home circleci \
|
||||
&& echo 'circleci ALL=NOPASSWD: ALL' >> /etc/sudoers.d/50-circleci \
|
||||
&& echo 'Defaults env_keep += "DEBIAN_FRONTEND"' >> /etc/sudoers.d/env_keep \
|
||||
&& echo 'Defaults env_keep += "SW"' >> /etc/sudoers.d/env_keep \
|
||||
&& echo 'Defaults env_keep += "LANG"' >> /etc/sudoers.d/env_keep
|
||||
|
||||
# Set timezone to UTC by default
|
||||
RUN ln -sf /usr/share/zoneinfo/Etc/UTC /etc/localtime
|
||||
|
||||
# Use unicode
|
||||
RUN locale-gen en_US.UTF-8 || true
|
||||
ENV LANG=en_US.UTF-8
|
||||
|
||||
# Deps needed for building python
|
||||
RUN apt-get install -y libreadline-dev zlib1g-dev libssl-dev libbz2-dev libsqlite3-dev libffi-dev
|
||||
|
||||
# kitty deps
|
||||
RUN apt-get install -y libgl1-mesa-dev libxi-dev libxrandr-dev libxinerama-dev libxcursor-dev libxcb-xkb-dev libdbus-1-dev libxkbcommon-dev libharfbuzz-dev libpng-dev libfontconfig-dev libpython3-dev libxkbcommon-x11-dev python3-pygments libcanberra-dev
|
||||
|
||||
# Needed to build kitty docs
|
||||
RUN pip3 install sphinx
|
||||
|
||||
# Install multiple pythons
|
||||
ADD install-python.py /tmp/install-python.py
|
||||
RUN python3 /tmp/install-python.py py3.5 https://www.python.org/ftp/python/3.5.6/Python-3.5.6.tar.xz
|
||||
RUN python3 /tmp/install-python.py py3.7 https://www.python.org/ftp/python/3.7.2/Python-3.7.2.tar.xz
|
||||
|
||||
# Install kitty bundle
|
||||
ENV SW=/home/circleci/sw
|
||||
RUN python3 /tmp/install-python.py bundle https://download.calibre-ebook.com/travis/kitty/linux-64.tar.xz
|
||||
RUN echo SW=$SW >> /etc/environment
|
||||
|
||||
# Clean unused files
|
||||
RUN apt-get clean -y
|
||||
|
||||
USER circleci
|
||||
|
||||
LABEL com.circleci.preserve-entrypoint=true
|
||||
ENTRYPOINT sleep 2h
|
||||
@@ -1,60 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import io
|
||||
import os
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
from urllib.request import urlopen
|
||||
|
||||
PY, URL = sys.argv[1], sys.argv[2]
|
||||
if PY == 'bundle':
|
||||
SW = os.environ['SW']
|
||||
os.mkdir(SW)
|
||||
os.chdir(SW)
|
||||
else:
|
||||
os.chdir('/usr/src')
|
||||
|
||||
|
||||
def run(cmd):
|
||||
cmd = shlex.split(cmd)
|
||||
p = subprocess.Popen(cmd)
|
||||
if p.wait() != 0:
|
||||
raise SystemExit(p.returncode)
|
||||
|
||||
|
||||
with urlopen(URL) as f:
|
||||
data = f.read()
|
||||
|
||||
with tarfile.open(fileobj=io.BytesIO(data), mode='r:xz') as tf:
|
||||
tf.extractall()
|
||||
|
||||
|
||||
def replace_in_file(path, src, dest):
|
||||
with open(path, 'r+') as f:
|
||||
n = f.read().replace(src, dest)
|
||||
f.seek(0), f.truncate()
|
||||
f.write(n)
|
||||
|
||||
|
||||
if PY == 'bundle':
|
||||
replaced = 0
|
||||
for dirpath, dirnames, filenames in os.walk(SW):
|
||||
for f in filenames:
|
||||
if f.endswith('.pc') or (f.endswith('.py') and f.startswith('_sysconfig')):
|
||||
replace_in_file(os.path.join(dirpath, f), '/sw/sw', SW)
|
||||
replaced += 1
|
||||
if replaced < 2:
|
||||
raise SystemExit('Failed to replace path to SW in bundle')
|
||||
else:
|
||||
src = os.path.abspath(tuple(os.listdir('.'))[0])
|
||||
os.chdir(src)
|
||||
run(f'./configure --prefix=/opt/{PY} --enable-shared --with-system-expat --without-ensurepip')
|
||||
run(f'make -j {os.cpu_count()}')
|
||||
run('make install')
|
||||
os.chdir('/')
|
||||
shutil.rmtree(src)
|
||||
21
.editorconfig
Normal file
21
.editorconfig
Normal file
@@ -0,0 +1,21 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = spaces
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[{Makefile,*.terminfo}]
|
||||
indent_style = tab
|
||||
|
||||
# Autogenerated files with tabs below this line.
|
||||
|
||||
[kitty/{unicode-data.c,emoji.h,wcwidth-std.h}]
|
||||
indent_style = tab
|
||||
|
||||
[kittens/unicode_input/names.h]
|
||||
indent_style = tab
|
||||
|
||||
[glfw/wayland-*-protocol.{c,h}]
|
||||
indent_style = tab
|
||||
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,6 +1,5 @@
|
||||
kitty/wcwidth-std.h linguist-generated=true
|
||||
kitty/emoji.h linguist-generated=true
|
||||
kitty/keys.h linguist-generated=true
|
||||
kitty/charsets.c linguist-generated=true
|
||||
kitty/key_encoding.py linguist-generated=true
|
||||
kitty/unicode-data.c linguist-generated=true
|
||||
@@ -11,3 +10,6 @@ kitty/parse-graphics-command.h linguist-generated=true
|
||||
glfw/*.c linguist-vendored=true
|
||||
glfw/*.h linguist-vendored=true
|
||||
kittens/unicode_input/names.h linguist-generated=true
|
||||
|
||||
*.py text diff=python
|
||||
*.m text diff=objc
|
||||
|
||||
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a bug report
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. XXX
|
||||
2. YYY
|
||||
3. ZZZ
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Environment details**
|
||||
OS: Name and version of operating system(s)
|
||||
|
||||
```
|
||||
Output of kitty --debug-config
|
||||
```
|
||||
|
||||
**Additional context**
|
||||
Try to reproduce the problem with `kitty --config NONE` if you cannot then post a minimal kitty.conf that reproduces the problem. If the problem involves interaction with some other terminal program post a minimal config for that program to reproduce the problem as well.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
132
.github/workflows/ci.py
vendored
Normal file
132
.github/workflows/ci.py
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import io
|
||||
import os
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
from urllib.request import urlopen
|
||||
|
||||
is_bundle = os.environ.get('KITTY_BUNDLE') == '1'
|
||||
is_macos = 'darwin' in sys.platform.lower()
|
||||
SW = None
|
||||
|
||||
|
||||
def run(*a):
|
||||
if len(a) == 1:
|
||||
a = shlex.split(a[0])
|
||||
print(' '.join(map(shlex.quote, a)))
|
||||
sys.stdout.flush()
|
||||
ret = subprocess.Popen(a).wait()
|
||||
if ret != 0:
|
||||
raise SystemExit(ret)
|
||||
|
||||
|
||||
def install_deps():
|
||||
print('Installing kitty dependencies...')
|
||||
sys.stdout.flush()
|
||||
if is_macos:
|
||||
items = (x.strip() for x in open('Brewfile').readlines() if not x.startswith('#'))
|
||||
run('brew', 'install', *items)
|
||||
else:
|
||||
run('sudo apt-get update')
|
||||
run('sudo apt-get install -y libgl1-mesa-dev libxi-dev libxrandr-dev libxinerama-dev'
|
||||
' libxcursor-dev libxcb-xkb-dev libdbus-1-dev libxkbcommon-dev libharfbuzz-dev libx11-xcb-dev'
|
||||
' libpng-dev liblcms2-dev libfontconfig-dev libxkbcommon-x11-dev libcanberra-dev uuid-dev')
|
||||
if is_bundle:
|
||||
install_bundle()
|
||||
else:
|
||||
if is_macos:
|
||||
# needed for zlib for pillow, should not be needed after pillow 8.0
|
||||
os.environ['PKG_CONFIG_PATH'] = '/usr/local/opt/zlib/lib/pkgconfig'
|
||||
cmd = 'pip3 install Pillow pygments'
|
||||
if sys.version_info[:2] < (3, 7):
|
||||
cmd += ' importlib-resources'
|
||||
run(cmd)
|
||||
|
||||
|
||||
def build_kitty():
|
||||
python = shutil.which('python3') if is_bundle else sys.executable
|
||||
cmd = '{} setup.py build --verbose'.format(python)
|
||||
if os.environ.get('KITTY_SANITIZE') == '1':
|
||||
cmd += ' --debug --sanitize'
|
||||
run(cmd)
|
||||
|
||||
|
||||
def test_kitty():
|
||||
run('./kitty/launcher/kitty +launch test.py')
|
||||
|
||||
|
||||
def package_kitty():
|
||||
py = 'python3' if is_macos else 'python'
|
||||
run(py + ' setup.py linux-package --update-check-interval=0 --verbose')
|
||||
if is_macos:
|
||||
run('python3 setup.py kitty.app --update-check-interval=0 --verbose')
|
||||
run('kitty.app/Contents/MacOS/kitty +runpy "from kitty.constants import *; print(kitty_exe())"')
|
||||
|
||||
|
||||
def replace_in_file(path, src, dest):
|
||||
with open(path, 'r+') as f:
|
||||
n = f.read().replace(src, dest)
|
||||
f.seek(0), f.truncate()
|
||||
f.write(n)
|
||||
|
||||
|
||||
def setup_bundle_env():
|
||||
global SW
|
||||
os.environ['SW'] = SW = '/Users/Shared/kitty-build/sw/sw' if is_macos else os.path.join(os.environ['GITHUB_WORKSPACE'], 'sw')
|
||||
os.environ['PKG_CONFIG_PATH'] = SW + '/lib/pkgconfig'
|
||||
if is_macos:
|
||||
os.environ['PATH'] = '{}:{}'.format('/usr/local/opt/sphinx-doc/bin', os.environ['PATH'])
|
||||
else:
|
||||
os.environ['LD_LIBRARY_PATH'] = SW + '/lib'
|
||||
os.environ['PYTHONHOME'] = SW
|
||||
os.environ['PATH'] = '{}:{}'.format(os.path.join(SW, 'bin'), os.environ['PATH'])
|
||||
|
||||
|
||||
def install_bundle():
|
||||
cwd = os.getcwd()
|
||||
os.makedirs(SW)
|
||||
os.chdir(SW)
|
||||
with urlopen('https://download.calibre-ebook.com/ci/kitty/{}-64.tar.xz'.format(
|
||||
'macos' if is_macos else 'linux')) as f:
|
||||
data = f.read()
|
||||
with tarfile.open(fileobj=io.BytesIO(data), mode='r:xz') as tf:
|
||||
tf.extractall()
|
||||
if not is_macos:
|
||||
replaced = 0
|
||||
for dirpath, dirnames, filenames in os.walk('.'):
|
||||
for f in filenames:
|
||||
if f.endswith('.pc') or (f.endswith('.py') and f.startswith('_sysconfig')):
|
||||
replace_in_file(os.path.join(dirpath, f), '/sw/sw', SW)
|
||||
replaced += 1
|
||||
if replaced < 2:
|
||||
raise SystemExit('Failed to replace path to SW in bundle')
|
||||
os.chdir(cwd)
|
||||
|
||||
|
||||
def main():
|
||||
if is_bundle:
|
||||
setup_bundle_env()
|
||||
else:
|
||||
if not is_macos and 'pythonLocation' in os.environ:
|
||||
os.environ['LD_LIBRARY_PATH'] = '{}/lib'.format(os.environ['pythonLocation'])
|
||||
action = sys.argv[-1]
|
||||
if action in ('build', 'package'):
|
||||
install_deps()
|
||||
if action == 'build':
|
||||
build_kitty()
|
||||
elif action == 'package':
|
||||
package_kitty()
|
||||
elif action == 'test':
|
||||
test_kitty()
|
||||
else:
|
||||
raise SystemExit('Unknown action: ' + action)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
132
.github/workflows/ci.yml
vendored
Normal file
132
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
name: CI
|
||||
on: [push, pull_request]
|
||||
env:
|
||||
CI: 'true'
|
||||
ASAN_OPTIONS: leak_check_at_exit=0
|
||||
LC_ALL: en_US.UTF-8
|
||||
LANG: en_US.UTF-8
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
name: Linux (python=${{ matrix.pyver }} cc=${{ matrix.cc }} sanitize=${{ matrix.sanitize }})
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CC: ${{ matrix.cc }}
|
||||
KITTY_SANITIZE: ${{ matrix.sanitize }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
python: [a, b, c]
|
||||
cc: [gcc, clang]
|
||||
include:
|
||||
- python: a
|
||||
pyver: 3.6
|
||||
sanitize: 0
|
||||
|
||||
- python: b
|
||||
pyver: 3.7
|
||||
sanitize: 1
|
||||
|
||||
- python: c
|
||||
pyver: 3.8
|
||||
sanitize: 1
|
||||
|
||||
|
||||
exclude:
|
||||
- python: a
|
||||
cc: clang
|
||||
- python: b
|
||||
cc: clang
|
||||
- python: c
|
||||
cc: gcc
|
||||
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
fetch-depth: 10
|
||||
- name: Set up Python ${{ matrix.pyver }}
|
||||
uses: actions/setup-python@master
|
||||
with:
|
||||
python-version: ${{ matrix.pyver }}
|
||||
|
||||
- name: Build kitty
|
||||
run: python .github/workflows/ci.py build
|
||||
|
||||
- name: Test kitty
|
||||
run: python .github/workflows/ci.py test
|
||||
|
||||
linux-package:
|
||||
name: Linux package
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CFLAGS: -funsigned-char
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
fetch-depth: 10
|
||||
|
||||
- name: Test for trailing whitespace
|
||||
run: if grep -Inr '\s$' kitty kitty_tests kittens docs *.py *.asciidoc *.rst .gitattributes .gitignore; then echo Trailing whitespace found, aborting.; exit 1; fi
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@master
|
||||
with:
|
||||
python-version: 3.8
|
||||
|
||||
- name: Install build-only deps
|
||||
run: pip install flake8 mypy sphinx
|
||||
|
||||
- name: Run pyflakes
|
||||
run: python -m flake8 --count .
|
||||
|
||||
- name: Build kitty package
|
||||
run: python .github/workflows/ci.py package
|
||||
|
||||
- name: Run mypy
|
||||
run: python test.py mypy
|
||||
|
||||
- name: Build man page
|
||||
run: make FAIL_WARN=-W man
|
||||
|
||||
- name: Build HTML docs
|
||||
run: make FAIL_WARN=-W html
|
||||
|
||||
bundle:
|
||||
name: Bundle test (${{ matrix.os }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
env:
|
||||
KITTY_BUNDLE: 1
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
fetch-depth: 10
|
||||
|
||||
- name: Build kitty
|
||||
run: which python3 && python3 .github/workflows/ci.py build
|
||||
|
||||
- name: Test kitty
|
||||
run: python3 .github/workflows/ci.py test
|
||||
|
||||
brew:
|
||||
name: macOS Brew
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
fetch-depth: 10
|
||||
|
||||
- name: Build kitty
|
||||
run: python3 .github/workflows/ci.py build
|
||||
|
||||
- name: Test kitty
|
||||
run: python3 .github/workflows/ci.py test
|
||||
|
||||
- name: Build kitty package
|
||||
run: python3 .github/workflows/ci.py package
|
||||
41
.github/workflows/codeql-analysis.yml
vendored
Normal file
41
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: "Code scanning - action"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [master]
|
||||
schedule:
|
||||
- cron: '0 22 * * 5'
|
||||
|
||||
jobs:
|
||||
CodeQL-Build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
|
||||
- name: Build kitty
|
||||
run: python3 .github/workflows/ci.py build
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,6 +1,8 @@
|
||||
*.so
|
||||
*.pyc
|
||||
*.pyo
|
||||
*_stub.pyi
|
||||
/.dmypy.json
|
||||
/tags
|
||||
/build/
|
||||
/linux-package/
|
||||
@@ -14,4 +16,6 @@ __pycache__/
|
||||
/glfw/wayland-*-client-protocol.[ch]
|
||||
/docs/_build/
|
||||
/docs/generated/
|
||||
/.mypy_cache
|
||||
.DS_Store
|
||||
bypy/b
|
||||
|
||||
11
Brewfile
11
Brewfile
@@ -1,5 +1,6 @@
|
||||
brew 'pkg-config'
|
||||
brew 'imagemagick'
|
||||
brew 'harfbuzz'
|
||||
brew 'python'
|
||||
brew 'sphinx-doc'
|
||||
pkg-config
|
||||
zlib
|
||||
python
|
||||
imagemagick
|
||||
harfbuzz
|
||||
sphinx-doc
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
### Reporting bugs
|
||||
|
||||
Please first search existing bug reports (especially closed ones) for a report
|
||||
that matches your issue.
|
||||
that matches your issue.
|
||||
|
||||
When reporting a bug, provide full details of your environment, that means, at
|
||||
a minimum, kitty version, OS and OS version, kitty config (ideally a minimal
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
To build from source:
|
||||
To build from source: <https://sw.kovidgoyal.net/kitty/build.html>
|
||||
|
||||
https://sw.kovidgoyal.net/kitty/build.html
|
||||
|
||||
Pre-built binaries:
|
||||
|
||||
https://sw.kovidgoyal.net/kitty/binary.html
|
||||
Pre-built binaries: <https://sw.kovidgoyal.net/kitty/binary.html>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
= kitty - the fast, featureful, GPU based, terminal emulator
|
||||
= kitty - the fast, feature-rich, cross-platform, GPU based terminal
|
||||
|
||||
See https://sw.kovidgoyal.net/kitty/
|
||||
|
||||
image:https://circleci.com/gh/kovidgoyal/kitty.svg?style=svg["Build status", link="https://circleci.com/gh/kovidgoyal/kitty"]
|
||||
image:https://github.com/kovidgoyal/kitty/workflows/CI/badge.svg["Build status", link="https://github.com/kovidgoyal/kitty/actions?query=workflow%3ACI"]
|
||||
|
||||
To ask questions about kitty usage, use either the https://github.com/kovidgoyal/kitty/discussions/[discussions on GitHub] or the
|
||||
https://www.reddit.com/r/KittyTerminal[Reddit community]
|
||||
|
||||
Packaging status in various repositories:
|
||||
|
||||
image:https://repology.org/badge/vertical-allrepos/kitty.svg[https://repology.org/project/kitty/versions]
|
||||
|
||||
12
SECURITY.md
Normal file
12
SECURITY.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
There are no security specific releases of kitty. Security bugs are fixed
|
||||
and released just like all other bugs.
|
||||
|
||||
## Reporting a vulnerability
|
||||
|
||||
Preferably send an email to kovid at kovidgoyal.net or open an issue in the
|
||||
GitHub repository, though the latter means you are disclosing the vulnerability
|
||||
publicly before it can be fixed.
|
||||
70
__main__.py
70
__main__.py
@@ -4,54 +4,62 @@
|
||||
|
||||
import sys
|
||||
import os
|
||||
from typing import List
|
||||
|
||||
|
||||
def icat(args):
|
||||
from kittens.runner import run_kitten
|
||||
def icat(args: List[str]) -> None:
|
||||
from kittens.runner import run_kitten as rk
|
||||
sys.argv = args
|
||||
run_kitten('icat')
|
||||
rk('icat')
|
||||
|
||||
|
||||
def list_fonts(args):
|
||||
from kitty.fonts.list import main
|
||||
main(args)
|
||||
def list_fonts(args: List[str]) -> None:
|
||||
from kitty.fonts.list import main as list_main
|
||||
list_main(args)
|
||||
|
||||
|
||||
def remote_control(args):
|
||||
from kitty.remote_control import main
|
||||
main(args)
|
||||
def remote_control(args: List[str]) -> None:
|
||||
from kitty.remote_control import main as rc_main
|
||||
rc_main(args)
|
||||
|
||||
|
||||
def runpy(args):
|
||||
def runpy(args: List[str]) -> None:
|
||||
sys.argv = ['kitty'] + args[2:]
|
||||
exec(args[1])
|
||||
|
||||
|
||||
def hold(args):
|
||||
def hold(args: List[str]) -> None:
|
||||
import subprocess
|
||||
from contextlib import suppress
|
||||
import tty
|
||||
ret = subprocess.Popen(args[1:]).wait()
|
||||
sys.stdin.read()
|
||||
with suppress(BaseException):
|
||||
print('\n\x1b[1;32mPress any key to exit', end='', flush=True)
|
||||
with suppress(BaseException):
|
||||
tty.setraw(sys.stdin.fileno())
|
||||
sys.stdin.buffer.read(1)
|
||||
raise SystemExit(ret)
|
||||
|
||||
|
||||
def complete(args):
|
||||
from kitty.complete import main
|
||||
main(args[1:], entry_points, namespaced_entry_points)
|
||||
def complete(args: List[str]) -> None:
|
||||
from kitty.complete import main as complete_main
|
||||
complete_main(args[1:], entry_points, namespaced_entry_points)
|
||||
|
||||
|
||||
def launch(args):
|
||||
def launch(args: List[str]) -> None:
|
||||
import runpy
|
||||
sys.argv = args[1:]
|
||||
exe = args[1]
|
||||
if exe.startswith(':'):
|
||||
import shutil
|
||||
exe = shutil.which(exe[1:])
|
||||
if not exe:
|
||||
q = shutil.which(exe[1:])
|
||||
if not q:
|
||||
raise SystemExit('{} not found in PATH'.format(args[1][1:]))
|
||||
exe = q
|
||||
runpy.run_path(exe, run_name='__main__')
|
||||
|
||||
|
||||
def run_kitten(args):
|
||||
def run_kitten(args: List[str]) -> None:
|
||||
try:
|
||||
kitten = args[1]
|
||||
except IndexError:
|
||||
@@ -59,11 +67,16 @@ def run_kitten(args):
|
||||
list_kittens()
|
||||
raise SystemExit(1)
|
||||
sys.argv = args[1:]
|
||||
from kittens.runner import run_kitten
|
||||
run_kitten(kitten)
|
||||
from kittens.runner import run_kitten as rk
|
||||
rk(kitten)
|
||||
|
||||
|
||||
def namespaced(args):
|
||||
def edit_config_file(args: List[str]) -> None:
|
||||
from kitty.utils import edit_config_file as f
|
||||
f()
|
||||
|
||||
|
||||
def namespaced(args: List[str]) -> None:
|
||||
func = namespaced_entry_points[args[1]]
|
||||
func(args[1:])
|
||||
|
||||
@@ -75,6 +88,7 @@ entry_points = {
|
||||
'runpy': runpy,
|
||||
'launch': launch,
|
||||
'kitten': run_kitten,
|
||||
'edit-config': edit_config_file,
|
||||
|
||||
'@': remote_control,
|
||||
'+': namespaced,
|
||||
@@ -84,7 +98,7 @@ namespaced_entry_points['hold'] = hold
|
||||
namespaced_entry_points['complete'] = complete
|
||||
|
||||
|
||||
def setup_openssl_environment():
|
||||
def setup_openssl_environment() -> None:
|
||||
# Workaround for Linux distros that have still failed to get their heads
|
||||
# out of their asses and implement a common location for SSL certificates.
|
||||
# It's not that hard people, there exists a wonderful tool called the symlink
|
||||
@@ -92,13 +106,13 @@ def setup_openssl_environment():
|
||||
if 'SSL_CERT_FILE' not in os.environ and 'SSL_CERT_DIR' not in os.environ:
|
||||
if os.access('/etc/pki/tls/certs/ca-bundle.crt', os.R_OK):
|
||||
os.environ['SSL_CERT_FILE'] = '/etc/pki/tls/certs/ca-bundle.crt'
|
||||
sys.kitty_ssl_env_var = 'SSL_CERT_FILE'
|
||||
setattr(sys, 'kitty_ssl_env_var', 'SSL_CERT_FILE')
|
||||
elif os.path.isdir('/etc/ssl/certs'):
|
||||
os.environ['SSL_CERT_DIR'] = '/etc/ssl/certs'
|
||||
sys.kitty_ssl_env_var = 'SSL_CERT_DIR'
|
||||
setattr(sys, 'kitty_ssl_env_var', 'SSL_CERT_DIR')
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
if getattr(sys, 'frozen', False) and 'darwin' not in sys.platform.lower():
|
||||
setup_openssl_environment()
|
||||
first_arg = '' if len(sys.argv) < 2 else sys.argv[1]
|
||||
@@ -109,8 +123,8 @@ def main():
|
||||
elif first_arg.startswith('+'):
|
||||
namespaced(['+', first_arg[1:]] + sys.argv[2:])
|
||||
else:
|
||||
from kitty.main import main
|
||||
main()
|
||||
from kitty.main import main as kitty_main
|
||||
kitty_main()
|
||||
else:
|
||||
func(sys.argv[1:])
|
||||
|
||||
|
||||
107
bypy/init_env.py
Normal file
107
bypy/init_env.py
Normal file
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from contextlib import suppress
|
||||
|
||||
from bypy.constants import (
|
||||
LIBDIR, PREFIX, PYTHON, SRC as KITTY_DIR, ismacos, worker_env
|
||||
)
|
||||
from bypy.utils import run_shell, walk
|
||||
|
||||
|
||||
def read_src_file(name):
|
||||
with open(os.path.join(KITTY_DIR, 'kitty', name), 'rb') as f:
|
||||
return f.read().decode('utf-8')
|
||||
|
||||
|
||||
def initialize_constants():
|
||||
kitty_constants = {}
|
||||
src = read_src_file('constants.py')
|
||||
nv = re.search(r'Version\((\d+), (\d+), (\d+)\)', src)
|
||||
kitty_constants['version'] = '%s.%s.%s' % (nv.group(1), nv.group(2), nv.group(3))
|
||||
kitty_constants['appname'] = re.search(
|
||||
r'appname: str\s+=\s+(u{0,1})[\'"]([^\'"]+)[\'"]', src
|
||||
).group(2)
|
||||
return kitty_constants
|
||||
|
||||
|
||||
def run(*args, **extra_env):
|
||||
env = os.environ.copy()
|
||||
env.update(worker_env)
|
||||
env.update(extra_env)
|
||||
env['SW'] = PREFIX
|
||||
env['LD_LIBRARY_PATH'] = LIBDIR
|
||||
if ismacos:
|
||||
env['PKGCONFIG_EXE'] = os.path.join(PREFIX, 'bin', 'pkg-config')
|
||||
cwd = env.pop('cwd', KITTY_DIR)
|
||||
print(' '.join(map(shlex.quote, args)), flush=True)
|
||||
return subprocess.call(list(args), env=env, cwd=cwd)
|
||||
|
||||
|
||||
def build_frozen_launcher(extra_include_dirs):
|
||||
inc_dirs = [f'--extra-include-dirs={x}' for x in extra_include_dirs]
|
||||
cmd = [PYTHON, 'setup.py', '--prefix', build_frozen_launcher.prefix] + inc_dirs + ['build-frozen-launcher']
|
||||
if run(*cmd, cwd=build_frozen_launcher.writeable_src_dir) != 0:
|
||||
print('Building of frozen kitty launcher failed', file=sys.stderr)
|
||||
os.chdir(KITTY_DIR)
|
||||
run_shell()
|
||||
raise SystemExit('Building of kitty launcher failed')
|
||||
return build_frozen_launcher.writeable_src_dir
|
||||
|
||||
|
||||
def run_tests(kitty_exe):
|
||||
with tempfile.TemporaryDirectory() as tdir:
|
||||
env = {
|
||||
'KITTY_CONFIG_DIRECTORY': os.path.join(tdir, 'conf'),
|
||||
'KITTY_CACHE_DIRECTORY': os.path.join(tdir, 'cache')
|
||||
}
|
||||
[os.mkdir(x) for x in env.values()]
|
||||
cmd = [kitty_exe, '+runpy', 'from kitty_tests.main import run_tests; run_tests()']
|
||||
print(*map(shlex.quote, cmd), flush=True)
|
||||
if subprocess.call(cmd, env=env) != 0:
|
||||
print('Checking of kitty build failed', file=sys.stderr)
|
||||
os.chdir(os.path.dirname(kitty_exe))
|
||||
run_shell()
|
||||
raise SystemExit('Checking of kitty build failed')
|
||||
|
||||
|
||||
def sanitize_source_folder(path: str) -> None:
|
||||
for q in walk(path):
|
||||
if os.path.splitext(q)[1] not in ('.py', '.glsl', '.ttf', '.otf'):
|
||||
os.unlink(q)
|
||||
|
||||
|
||||
def build_c_extensions(ext_dir, args):
|
||||
writeable_src_dir = os.path.join(ext_dir, 'src')
|
||||
build_frozen_launcher.writeable_src_dir = writeable_src_dir
|
||||
shutil.copytree(
|
||||
KITTY_DIR, writeable_src_dir, symlinks=True,
|
||||
ignore=shutil.ignore_patterns('b', 'build', 'dist', '*_commands.json', '*.o', '*.so', '*.dylib', '*.pyd'))
|
||||
|
||||
with suppress(FileNotFoundError):
|
||||
os.unlink(os.path.join(writeable_src_dir, 'kitty', 'launcher', 'kitty'))
|
||||
|
||||
cmd = [PYTHON, 'setup.py', 'macos-freeze' if ismacos else 'linux-freeze']
|
||||
if args.dont_strip:
|
||||
cmd.append('--debug')
|
||||
dest = kitty_constants['appname'] + ('.app' if ismacos else '')
|
||||
dest = build_frozen_launcher.prefix = os.path.join(ext_dir, dest)
|
||||
cmd += ['--prefix', dest, '--full']
|
||||
if run(*cmd, cwd=writeable_src_dir) != 0:
|
||||
print('Building of kitty package failed', file=sys.stderr)
|
||||
os.chdir(writeable_src_dir)
|
||||
run_shell()
|
||||
raise SystemExit('Building of kitty package failed')
|
||||
return ext_dir
|
||||
|
||||
|
||||
if __name__ == 'program':
|
||||
kitty_constants = initialize_constants()
|
||||
3
bypy/linux.conf
Normal file
3
bypy/linux.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
image 'https://partner-images.canonical.com/core/xenial/current/ubuntu-xenial-core-cloudimg-{}-root.tar.gz'
|
||||
|
||||
deps 'bison flex libxcursor-dev libxrandr-dev libxi-dev libxinerama-dev libgl1-mesa-dev libxcb-xkb-dev libfontconfig1-dev libdbus-1-dev'
|
||||
229
bypy/linux/__main__.py
Normal file
229
bypy/linux/__main__.py
Normal file
@@ -0,0 +1,229 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import errno
|
||||
import os
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
import tarfile
|
||||
import time
|
||||
|
||||
from bypy.constants import (
|
||||
OUTPUT_DIR, PREFIX, is64bit, python_major_minor_version
|
||||
)
|
||||
from bypy.freeze import (
|
||||
extract_extension_modules, freeze_python, path_to_freeze_dir
|
||||
)
|
||||
from bypy.utils import get_dll_path, mkdtemp, py_compile, walk
|
||||
|
||||
j = os.path.join
|
||||
arch = 'x86_64' if is64bit else 'i686'
|
||||
self_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
py_ver = '.'.join(map(str, python_major_minor_version()))
|
||||
iv = globals()['init_env']
|
||||
kitty_constants = iv['kitty_constants']
|
||||
|
||||
|
||||
def binary_includes():
|
||||
return tuple(map(get_dll_path, (
|
||||
'expat', 'sqlite3', 'ffi', 'z', 'lzma', 'png16', 'lcms2', 'crypt',
|
||||
'iconv', 'pcre', 'graphite2', 'glib-2.0', 'freetype',
|
||||
'harfbuzz', 'xkbcommon', 'xkbcommon-x11',
|
||||
'ncursesw', 'readline',
|
||||
))) + (
|
||||
get_dll_path('bz2', 2), get_dll_path('ssl', 2), get_dll_path('crypto', 2),
|
||||
get_dll_path('python' + py_ver, 2),
|
||||
)
|
||||
|
||||
|
||||
class Env:
|
||||
|
||||
def __init__(self, package_dir):
|
||||
self.base = package_dir
|
||||
self.lib_dir = j(self.base, 'lib')
|
||||
self.py_dir = j(self.lib_dir, 'python' + py_ver)
|
||||
os.makedirs(self.py_dir)
|
||||
self.bin_dir = j(self.base, 'bin')
|
||||
self.obj_dir = mkdtemp('launchers-')
|
||||
|
||||
|
||||
def ignore_in_lib(base, items, ignored_dirs=None):
|
||||
ans = []
|
||||
if ignored_dirs is None:
|
||||
ignored_dirs = {'.svn', '.bzr', '.git', 'test', 'tests', 'testing'}
|
||||
for name in items:
|
||||
path = j(base, name)
|
||||
if os.path.isdir(path):
|
||||
if name in ignored_dirs or not os.path.exists(j(path, '__init__.py')):
|
||||
if name != 'plugins':
|
||||
ans.append(name)
|
||||
else:
|
||||
if name.rpartition('.')[-1] not in ('so', 'py'):
|
||||
ans.append(name)
|
||||
return ans
|
||||
|
||||
|
||||
def import_site_packages(srcdir, dest):
|
||||
if not os.path.exists(dest):
|
||||
os.mkdir(dest)
|
||||
for x in os.listdir(srcdir):
|
||||
ext = x.rpartition('.')[-1]
|
||||
f = j(srcdir, x)
|
||||
if ext in ('py', 'so'):
|
||||
shutil.copy2(f, dest)
|
||||
elif ext == 'pth' and x != 'setuptools.pth':
|
||||
for line in open(f):
|
||||
src = os.path.abspath(j(srcdir, line))
|
||||
if os.path.exists(src) and os.path.isdir(src):
|
||||
import_site_packages(src, dest)
|
||||
elif os.path.exists(j(f, '__init__.py')):
|
||||
shutil.copytree(f, j(dest, x), ignore=ignore_in_lib)
|
||||
|
||||
|
||||
def copy_libs(env):
|
||||
print('Copying libs...')
|
||||
|
||||
for x in binary_includes():
|
||||
dest = env.bin_dir if '/bin/' in x else env.lib_dir
|
||||
shutil.copy2(x, dest)
|
||||
|
||||
|
||||
def copy_python(env):
|
||||
print('Copying python...')
|
||||
srcdir = j(PREFIX, 'lib/python' + py_ver)
|
||||
|
||||
for x in os.listdir(srcdir):
|
||||
y = j(srcdir, x)
|
||||
ext = os.path.splitext(x)[1]
|
||||
if os.path.isdir(y) and x not in ('test', 'hotshot', 'distutils', 'tkinter', 'turtledemo',
|
||||
'site-packages', 'idlelib', 'lib2to3', 'dist-packages'):
|
||||
shutil.copytree(y, j(env.py_dir, x), ignore=ignore_in_lib)
|
||||
if os.path.isfile(y) and ext in ('.py', '.so'):
|
||||
shutil.copy2(y, env.py_dir)
|
||||
|
||||
srcdir = j(srcdir, 'site-packages')
|
||||
site_packages_dir = j(env.py_dir, 'site-packages')
|
||||
import_site_packages(srcdir, site_packages_dir)
|
||||
|
||||
pdir = os.path.join(env.lib_dir, 'kitty-extensions')
|
||||
os.makedirs(pdir, exist_ok=True)
|
||||
kitty_dir = os.path.join(env.lib_dir, 'kitty')
|
||||
bases = ('kitty', 'kittens', 'kitty_tests')
|
||||
for x in bases:
|
||||
dest = os.path.join(env.py_dir, x)
|
||||
os.rename(os.path.join(kitty_dir, x), dest)
|
||||
if x == 'kitty':
|
||||
shutil.rmtree(os.path.join(dest, 'launcher'))
|
||||
os.rename(os.path.join(kitty_dir, '__main__.py'), os.path.join(env.py_dir, 'kitty_main.py'))
|
||||
shutil.rmtree(os.path.join(kitty_dir, '__pycache__'))
|
||||
print('Extracting extension modules from', env.py_dir, 'to', pdir)
|
||||
ext_map = extract_extension_modules(env.py_dir, pdir)
|
||||
shutil.copy(os.path.join(os.path.dirname(self_dir), 'site.py'), os.path.join(env.py_dir, 'site.py'))
|
||||
for x in bases:
|
||||
iv['sanitize_source_folder'](os.path.join(env.py_dir, x))
|
||||
py_compile(env.py_dir)
|
||||
freeze_python(env.py_dir, pdir, env.obj_dir, ext_map, develop_mode_env_var='KITTY_DEVELOP_FROM')
|
||||
|
||||
|
||||
def build_launcher(env):
|
||||
iv['build_frozen_launcher']([path_to_freeze_dir(), env.obj_dir])
|
||||
|
||||
|
||||
def is_elf(path):
|
||||
with open(path, 'rb') as f:
|
||||
return f.read(4) == b'\x7fELF'
|
||||
|
||||
|
||||
def fix_permissions(files):
|
||||
for path in files:
|
||||
os.chmod(path, 0o755)
|
||||
|
||||
|
||||
STRIPCMD = ['strip']
|
||||
|
||||
|
||||
def find_binaries(env):
|
||||
files = {j(env.bin_dir, x) for x in os.listdir(env.bin_dir)} | {
|
||||
x for x in {
|
||||
j(os.path.dirname(env.bin_dir), x) for x in os.listdir(env.bin_dir)} if os.path.exists(x)}
|
||||
for x in walk(env.lib_dir):
|
||||
x = os.path.realpath(x)
|
||||
if x not in files and is_elf(x):
|
||||
files.add(x)
|
||||
return files
|
||||
|
||||
|
||||
def strip_files(files, argv_max=(256 * 1024)):
|
||||
""" Strip a list of files """
|
||||
while files:
|
||||
cmd = list(STRIPCMD)
|
||||
pathlen = sum(len(s) + 1 for s in cmd)
|
||||
while pathlen < argv_max and files:
|
||||
f = files.pop()
|
||||
cmd.append(f)
|
||||
pathlen += len(f) + 1
|
||||
if len(cmd) > len(STRIPCMD):
|
||||
all_files = cmd[len(STRIPCMD):]
|
||||
unwritable_files = tuple(filter(None, (None if os.access(x, os.W_OK) else (x, os.stat(x).st_mode) for x in all_files)))
|
||||
[os.chmod(x, stat.S_IWRITE | old_mode) for x, old_mode in unwritable_files]
|
||||
subprocess.check_call(cmd)
|
||||
[os.chmod(x, old_mode) for x, old_mode in unwritable_files]
|
||||
|
||||
|
||||
def strip_binaries(files):
|
||||
print('Stripping %d files...' % len(files))
|
||||
before = sum(os.path.getsize(x) for x in files)
|
||||
strip_files(files)
|
||||
after = sum(os.path.getsize(x) for x in files)
|
||||
print('Stripped %.1f MB' % ((before - after) / (1024 * 1024.)))
|
||||
|
||||
|
||||
def create_tarfile(env, compression_level='9'):
|
||||
print('Creating archive...')
|
||||
base = OUTPUT_DIR
|
||||
try:
|
||||
shutil.rmtree(base)
|
||||
except OSError as err:
|
||||
if err.errno != errno.ENOENT:
|
||||
raise
|
||||
os.mkdir(base)
|
||||
dist = os.path.join(base, '%s-%s-%s.tar' % (kitty_constants['appname'], kitty_constants['version'], arch))
|
||||
with tarfile.open(dist, mode='w', format=tarfile.PAX_FORMAT) as tf:
|
||||
cwd = os.getcwd()
|
||||
os.chdir(env.base)
|
||||
try:
|
||||
for x in os.listdir('.'):
|
||||
tf.add(x)
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
print('Compressing archive...')
|
||||
ans = dist.rpartition('.')[0] + '.txz'
|
||||
start_time = time.time()
|
||||
subprocess.check_call(['xz', '--threads=0', '-f', '-' + compression_level, dist])
|
||||
secs = time.time() - start_time
|
||||
print('Compressed in %d minutes %d seconds' % (secs // 60, secs % 60))
|
||||
os.rename(dist + '.xz', ans)
|
||||
print('Archive %s created: %.2f MB' % (
|
||||
os.path.basename(ans), os.stat(ans).st_size / (1024.**2)))
|
||||
|
||||
|
||||
def main():
|
||||
args = globals()['args']
|
||||
ext_dir = globals()['ext_dir']
|
||||
env = Env(os.path.join(ext_dir, kitty_constants['appname']))
|
||||
copy_libs(env)
|
||||
copy_python(env)
|
||||
build_launcher(env)
|
||||
files = find_binaries(env)
|
||||
fix_permissions(files)
|
||||
if not args.dont_strip:
|
||||
strip_binaries(files)
|
||||
if not args.skip_tests:
|
||||
iv['run_tests'](os.path.join(env.base, 'bin', 'kitty'))
|
||||
create_tarfile(env, args.compression_level)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
6
bypy/macos.conf
Normal file
6
bypy/macos.conf
Normal file
@@ -0,0 +1,6 @@
|
||||
# Requires installation of XCode 10.3 and Python 3 and
|
||||
# python3 -m pip install certifi
|
||||
|
||||
vm_name 'macos-kitty-build'
|
||||
root '/Users/Shared/kitty-build'
|
||||
python '/usr/local/bin/python3'
|
||||
482
bypy/macos/__main__.py
Normal file
482
bypy/macos/__main__.py
Normal file
@@ -0,0 +1,482 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import zipfile
|
||||
|
||||
from bypy.constants import PREFIX, PYTHON, SW, python_major_minor_version
|
||||
from bypy.freeze import (
|
||||
extract_extension_modules, freeze_python, path_to_freeze_dir
|
||||
)
|
||||
from bypy.macos_sign import (
|
||||
codesign, create_entitlements_file, make_certificate_useable, notarize_app,
|
||||
verify_signature
|
||||
)
|
||||
from bypy.utils import (
|
||||
current_dir, mkdtemp, py_compile, run_shell, timeit, walk
|
||||
)
|
||||
|
||||
iv = globals()['init_env']
|
||||
kitty_constants = iv['kitty_constants']
|
||||
self_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
join = os.path.join
|
||||
basename = os.path.basename
|
||||
dirname = os.path.dirname
|
||||
abspath = os.path.abspath
|
||||
APPNAME = kitty_constants['appname']
|
||||
VERSION = kitty_constants['version']
|
||||
py_ver = '.'.join(map(str, python_major_minor_version()))
|
||||
|
||||
|
||||
def flush(func):
|
||||
def ff(*args, **kwargs):
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
ret = func(*args, **kwargs)
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
return ret
|
||||
|
||||
return ff
|
||||
|
||||
|
||||
def flipwritable(fn, mode=None):
|
||||
"""
|
||||
Flip the writability of a file and return the old mode. Returns None
|
||||
if the file is already writable.
|
||||
"""
|
||||
if os.access(fn, os.W_OK):
|
||||
return None
|
||||
old_mode = os.stat(fn).st_mode
|
||||
os.chmod(fn, stat.S_IWRITE | old_mode)
|
||||
return old_mode
|
||||
|
||||
|
||||
STRIPCMD = ('/usr/bin/strip', '-x', '-S', '-')
|
||||
|
||||
|
||||
def strip_files(files, argv_max=(256 * 1024)):
|
||||
"""
|
||||
Strip a list of files
|
||||
"""
|
||||
tostrip = [(fn, flipwritable(fn)) for fn in files if os.path.exists(fn)]
|
||||
while tostrip:
|
||||
cmd = list(STRIPCMD)
|
||||
flips = []
|
||||
pathlen = sum(len(s) + 1 for s in cmd)
|
||||
while pathlen < argv_max:
|
||||
if not tostrip:
|
||||
break
|
||||
added, flip = tostrip.pop()
|
||||
pathlen += len(added) + 1
|
||||
cmd.append(added)
|
||||
flips.append((added, flip))
|
||||
else:
|
||||
cmd.pop()
|
||||
tostrip.append(flips.pop())
|
||||
os.spawnv(os.P_WAIT, cmd[0], cmd)
|
||||
for args in flips:
|
||||
flipwritable(*args)
|
||||
|
||||
|
||||
def files_in(folder):
|
||||
for record in os.walk(folder):
|
||||
for f in record[-1]:
|
||||
yield os.path.join(record[0], f)
|
||||
|
||||
|
||||
def expand_dirs(items, exclude=lambda x: x.endswith('.so')):
|
||||
items = set(items)
|
||||
dirs = set(x for x in items if os.path.isdir(x))
|
||||
items.difference_update(dirs)
|
||||
for x in dirs:
|
||||
items.update({y for y in files_in(x) if not exclude(y)})
|
||||
return items
|
||||
|
||||
|
||||
def do_sign(app_dir):
|
||||
with current_dir(os.path.join(app_dir, 'Contents')):
|
||||
# Sign all .so files
|
||||
so_files = {x for x in files_in('.') if x.endswith('.so')}
|
||||
codesign(so_files)
|
||||
# Sign everything else in Frameworks
|
||||
with current_dir('Frameworks'):
|
||||
fw = set(glob.glob('*.framework'))
|
||||
codesign(fw)
|
||||
items = set(os.listdir('.')) - fw
|
||||
codesign(expand_dirs(items))
|
||||
|
||||
# Now sign the main app
|
||||
codesign(app_dir)
|
||||
verify_signature(app_dir)
|
||||
|
||||
|
||||
def sign_app(app_dir, notarize):
|
||||
# Copied from iTerm2: https://github.com/gnachman/iTerm2/blob/master/iTerm2.entitlements
|
||||
create_entitlements_file({
|
||||
'com.apple.security.automation.apple-events': True,
|
||||
'com.apple.security.cs.allow-jit': True,
|
||||
'com.apple.security.device.audio-input': True,
|
||||
'com.apple.security.device.camera': True,
|
||||
'com.apple.security.personal-information.addressbook': True,
|
||||
'com.apple.security.personal-information.calendars': True,
|
||||
'com.apple.security.personal-information.location': True,
|
||||
'com.apple.security.personal-information.photos-library': True,
|
||||
})
|
||||
with make_certificate_useable():
|
||||
do_sign(app_dir)
|
||||
if notarize:
|
||||
notarize_app(app_dir)
|
||||
|
||||
|
||||
class Freeze(object):
|
||||
|
||||
FID = '@executable_path/../Frameworks'
|
||||
|
||||
def __init__(self, build_dir, dont_strip=False, sign_installers=False, notarize=False, skip_tests=False):
|
||||
self.build_dir = build_dir
|
||||
self.skip_tests = skip_tests
|
||||
self.sign_installers = sign_installers
|
||||
self.notarize = notarize
|
||||
self.dont_strip = dont_strip
|
||||
self.contents_dir = join(self.build_dir, 'Contents')
|
||||
self.resources_dir = join(self.contents_dir, 'Resources')
|
||||
self.frameworks_dir = join(self.contents_dir, 'Frameworks')
|
||||
self.to_strip = []
|
||||
self.warnings = []
|
||||
self.py_ver = py_ver
|
||||
self.python_stdlib = join(self.resources_dir, 'Python', 'lib', 'python' + self.py_ver)
|
||||
self.site_packages = self.python_stdlib # hack to avoid needing to add site-packages to path
|
||||
self.obj_dir = mkdtemp('launchers-')
|
||||
|
||||
self.run()
|
||||
|
||||
def run_shell(self):
|
||||
with current_dir(self.contents_dir):
|
||||
run_shell()
|
||||
|
||||
def run(self):
|
||||
ret = 0
|
||||
self.add_python_framework()
|
||||
self.add_site_packages()
|
||||
self.add_stdlib()
|
||||
self.add_misc_libraries()
|
||||
self.freeze_python()
|
||||
if not self.dont_strip:
|
||||
self.strip_files()
|
||||
if not self.skip_tests:
|
||||
self.run_tests()
|
||||
# self.run_shell()
|
||||
|
||||
ret = self.makedmg(self.build_dir, APPNAME + '-' + VERSION)
|
||||
|
||||
return ret
|
||||
|
||||
@flush
|
||||
def strip_files(self):
|
||||
print('\nStripping files...')
|
||||
strip_files(self.to_strip)
|
||||
|
||||
@flush
|
||||
def run_tests(self):
|
||||
iv['run_tests'](os.path.join(self.contents_dir, 'MacOS', 'kitty'))
|
||||
|
||||
@flush
|
||||
def set_id(self, path_to_lib, new_id):
|
||||
old_mode = flipwritable(path_to_lib)
|
||||
subprocess.check_call(
|
||||
['install_name_tool', '-id', new_id, path_to_lib])
|
||||
if old_mode is not None:
|
||||
flipwritable(path_to_lib, old_mode)
|
||||
|
||||
@flush
|
||||
def get_dependencies(self, path_to_lib):
|
||||
install_name = subprocess.check_output(
|
||||
['otool', '-D', path_to_lib]).decode('utf-8').splitlines()[-1].strip()
|
||||
raw = subprocess.check_output(['otool', '-L', path_to_lib]).decode('utf-8')
|
||||
for line in raw.splitlines():
|
||||
if 'compatibility' not in line or line.strip().endswith(':'):
|
||||
continue
|
||||
idx = line.find('(')
|
||||
path = line[:idx].strip()
|
||||
yield path, path == install_name
|
||||
|
||||
@flush
|
||||
def get_local_dependencies(self, path_to_lib):
|
||||
for x, is_id in self.get_dependencies(path_to_lib):
|
||||
for y in (PREFIX + '/lib/', PREFIX + '/python/Python.framework/'):
|
||||
if x.startswith(y):
|
||||
if y == PREFIX + '/python/Python.framework/':
|
||||
y = PREFIX + '/python/'
|
||||
yield x, x[len(y):], is_id
|
||||
break
|
||||
|
||||
@flush
|
||||
def change_dep(self, old_dep, new_dep, is_id, path_to_lib):
|
||||
cmd = ['-id', new_dep] if is_id else ['-change', old_dep, new_dep]
|
||||
subprocess.check_call(['install_name_tool'] + cmd + [path_to_lib])
|
||||
|
||||
@flush
|
||||
def fix_dependencies_in_lib(self, path_to_lib):
|
||||
self.to_strip.append(path_to_lib)
|
||||
old_mode = flipwritable(path_to_lib)
|
||||
for dep, bname, is_id in self.get_local_dependencies(path_to_lib):
|
||||
ndep = self.FID + '/' + bname
|
||||
self.change_dep(dep, ndep, is_id, path_to_lib)
|
||||
ldeps = list(self.get_local_dependencies(path_to_lib))
|
||||
if ldeps:
|
||||
print('\nFailed to fix dependencies in', path_to_lib)
|
||||
print('Remaining local dependencies:', ldeps)
|
||||
raise SystemExit(1)
|
||||
if old_mode is not None:
|
||||
flipwritable(path_to_lib, old_mode)
|
||||
|
||||
@flush
|
||||
def add_python_framework(self):
|
||||
print('\nAdding Python framework')
|
||||
src = join(PREFIX + '/python', 'Python.framework')
|
||||
x = join(self.frameworks_dir, 'Python.framework')
|
||||
curr = os.path.realpath(join(src, 'Versions', 'Current'))
|
||||
currd = join(x, 'Versions', basename(curr))
|
||||
rd = join(currd, 'Resources')
|
||||
os.makedirs(rd)
|
||||
shutil.copy2(join(curr, 'Resources', 'Info.plist'), rd)
|
||||
shutil.copy2(join(curr, 'Python'), currd)
|
||||
self.set_id(
|
||||
join(currd, 'Python'),
|
||||
self.FID + '/Python.framework/Versions/%s/Python' % basename(curr))
|
||||
# The following is needed for codesign
|
||||
with current_dir(x):
|
||||
os.symlink(basename(curr), 'Versions/Current')
|
||||
for y in ('Python', 'Resources'):
|
||||
os.symlink('Versions/Current/%s' % y, y)
|
||||
|
||||
@flush
|
||||
def install_dylib(self, path, set_id=True):
|
||||
shutil.copy2(path, self.frameworks_dir)
|
||||
if set_id:
|
||||
self.set_id(
|
||||
join(self.frameworks_dir, basename(path)),
|
||||
self.FID + '/' + basename(path))
|
||||
self.fix_dependencies_in_lib(join(self.frameworks_dir, basename(path)))
|
||||
|
||||
@flush
|
||||
def add_misc_libraries(self):
|
||||
for x in (
|
||||
'sqlite3.0',
|
||||
'z.1',
|
||||
'harfbuzz.0',
|
||||
'png16.16',
|
||||
'lcms2.2',
|
||||
'crypto.1.1',
|
||||
'ssl.1.1',
|
||||
'ffi.7',
|
||||
):
|
||||
print('\nAdding', x)
|
||||
x = 'lib%s.dylib' % x
|
||||
src = join(PREFIX, 'lib', x)
|
||||
shutil.copy2(src, self.frameworks_dir)
|
||||
dest = join(self.frameworks_dir, x)
|
||||
self.set_id(dest, self.FID + '/' + x)
|
||||
self.fix_dependencies_in_lib(dest)
|
||||
|
||||
@flush
|
||||
def add_package_dir(self, x, dest=None):
|
||||
def ignore(root, files):
|
||||
ans = []
|
||||
for y in files:
|
||||
ext = os.path.splitext(y)[1]
|
||||
if ext not in ('', '.py', '.so') or \
|
||||
(not ext and not os.path.isdir(join(root, y))):
|
||||
ans.append(y)
|
||||
|
||||
return ans
|
||||
|
||||
if dest is None:
|
||||
dest = self.site_packages
|
||||
dest = join(dest, basename(x))
|
||||
shutil.copytree(x, dest, symlinks=True, ignore=ignore)
|
||||
for f in walk(dest):
|
||||
if f.endswith('.so'):
|
||||
self.fix_dependencies_in_lib(f)
|
||||
|
||||
@flush
|
||||
def add_stdlib(self):
|
||||
print('\nAdding python stdlib')
|
||||
src = PREFIX + '/python/Python.framework/Versions/Current/lib/python' + self.py_ver
|
||||
dest = self.python_stdlib
|
||||
if not os.path.exists(dest):
|
||||
os.makedirs(dest)
|
||||
for x in os.listdir(src):
|
||||
if x in ('site-packages', 'config', 'test', 'lib2to3', 'lib-tk',
|
||||
'lib-old', 'idlelib', 'plat-mac', 'plat-darwin',
|
||||
'site.py', 'distutils', 'turtledemo', 'tkinter'):
|
||||
continue
|
||||
x = join(src, x)
|
||||
if os.path.isdir(x):
|
||||
self.add_package_dir(x, dest)
|
||||
elif os.path.splitext(x)[1] in ('.so', '.py'):
|
||||
shutil.copy2(x, dest)
|
||||
dest2 = join(dest, basename(x))
|
||||
if dest2.endswith('.so'):
|
||||
self.fix_dependencies_in_lib(dest2)
|
||||
|
||||
@flush
|
||||
def freeze_python(self):
|
||||
print('\nFreezing python')
|
||||
kitty_dir = join(self.resources_dir, 'kitty')
|
||||
bases = ('kitty', 'kittens', 'kitty_tests')
|
||||
for x in bases:
|
||||
dest = os.path.join(self.python_stdlib, x)
|
||||
os.rename(os.path.join(kitty_dir, x), dest)
|
||||
if x == 'kitty':
|
||||
shutil.rmtree(os.path.join(dest, 'launcher'))
|
||||
os.rename(os.path.join(kitty_dir, '__main__.py'), os.path.join(self.python_stdlib, 'kitty_main.py'))
|
||||
shutil.rmtree(os.path.join(kitty_dir, '__pycache__'))
|
||||
pdir = os.path.join(dirname(self.python_stdlib), 'kitty-extensions')
|
||||
os.mkdir(pdir)
|
||||
print('Extracting extension modules from', self.python_stdlib, 'to', pdir)
|
||||
ext_map = extract_extension_modules(self.python_stdlib, pdir)
|
||||
shutil.copy(os.path.join(os.path.dirname(self_dir), 'site.py'), os.path.join(self.python_stdlib, 'site.py'))
|
||||
for x in bases:
|
||||
iv['sanitize_source_folder'](os.path.join(self.python_stdlib, x))
|
||||
self.compile_py_modules()
|
||||
freeze_python(self.python_stdlib, pdir, self.obj_dir, ext_map, develop_mode_env_var='KITTY_DEVELOP_FROM')
|
||||
iv['build_frozen_launcher']([path_to_freeze_dir(), self.obj_dir])
|
||||
os.rename(join(dirname(self.contents_dir), 'bin', 'kitty'), join(self.contents_dir, 'MacOS', 'kitty'))
|
||||
shutil.rmtree(join(dirname(self.contents_dir), 'bin'))
|
||||
self.fix_dependencies_in_lib(join(self.contents_dir, 'MacOS', 'kitty'))
|
||||
for f in walk(pdir):
|
||||
if f.endswith('.so') or f.endswith('.dylib'):
|
||||
self.fix_dependencies_in_lib(f)
|
||||
|
||||
@flush
|
||||
def add_site_packages(self):
|
||||
print('\nAdding site-packages')
|
||||
os.makedirs(self.site_packages)
|
||||
sys_path = json.loads(subprocess.check_output([
|
||||
PYTHON, '-c', 'import sys, json; json.dump(sys.path, sys.stdout)']))
|
||||
paths = reversed(tuple(map(abspath, [x for x in sys_path if x.startswith('/') and not x.startswith('/Library/')])))
|
||||
upaths = []
|
||||
for x in paths:
|
||||
if x not in upaths and (x.endswith('.egg') or x.endswith('/site-packages')):
|
||||
upaths.append(x)
|
||||
for x in upaths:
|
||||
print('\t', x)
|
||||
tdir = None
|
||||
try:
|
||||
if not os.path.isdir(x):
|
||||
zf = zipfile.ZipFile(x)
|
||||
tdir = tempfile.mkdtemp()
|
||||
zf.extractall(tdir)
|
||||
x = tdir
|
||||
self.add_modules_from_dir(x)
|
||||
self.add_packages_from_dir(x)
|
||||
finally:
|
||||
if tdir is not None:
|
||||
shutil.rmtree(tdir)
|
||||
self.remove_bytecode(self.site_packages)
|
||||
|
||||
@flush
|
||||
def add_modules_from_dir(self, src):
|
||||
for x in glob.glob(join(src, '*.py')) + glob.glob(join(src, '*.so')):
|
||||
shutil.copy2(x, self.site_packages)
|
||||
if x.endswith('.so'):
|
||||
self.fix_dependencies_in_lib(x)
|
||||
|
||||
@flush
|
||||
def add_packages_from_dir(self, src):
|
||||
for x in os.listdir(src):
|
||||
x = join(src, x)
|
||||
if os.path.isdir(x) and os.path.exists(join(x, '__init__.py')):
|
||||
if self.filter_package(basename(x)):
|
||||
continue
|
||||
self.add_package_dir(x)
|
||||
|
||||
@flush
|
||||
def filter_package(self, name):
|
||||
return name in ('Cython', 'modulegraph', 'macholib', 'py2app',
|
||||
'bdist_mpkg', 'altgraph')
|
||||
|
||||
@flush
|
||||
def remove_bytecode(self, dest):
|
||||
for x in os.walk(dest):
|
||||
root = x[0]
|
||||
for f in x[-1]:
|
||||
if os.path.splitext(f) == '.pyc':
|
||||
os.remove(join(root, f))
|
||||
|
||||
@flush
|
||||
def compile_py_modules(self):
|
||||
self.remove_bytecode(join(self.resources_dir, 'Python'))
|
||||
py_compile(join(self.resources_dir, 'Python'))
|
||||
|
||||
@flush
|
||||
def makedmg(self, d, volname, internet_enable=True, format='ULFO'):
|
||||
''' Copy a directory d into a dmg named volname '''
|
||||
print('\nMaking dmg...')
|
||||
sys.stdout.flush()
|
||||
destdir = os.path.join(SW, 'dist')
|
||||
try:
|
||||
shutil.rmtree(destdir)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
os.mkdir(destdir)
|
||||
dmg = os.path.join(destdir, volname + '.dmg')
|
||||
if os.path.exists(dmg):
|
||||
os.unlink(dmg)
|
||||
tdir = tempfile.mkdtemp()
|
||||
appdir = os.path.join(tdir, os.path.basename(d))
|
||||
shutil.copytree(d, appdir, symlinks=True)
|
||||
if self.sign_installers:
|
||||
with timeit() as times:
|
||||
sign_app(appdir, self.notarize)
|
||||
print('Signing completed in %d minutes %d seconds' % tuple(times))
|
||||
os.symlink('/Applications', os.path.join(tdir, 'Applications'))
|
||||
size_in_mb = int(
|
||||
subprocess.check_output(['du', '-s', '-k', tdir]).decode('utf-8')
|
||||
.split()[0]) / 1024.
|
||||
cmd = [
|
||||
'/usr/bin/hdiutil', 'create', '-srcfolder', tdir, '-volname',
|
||||
volname, '-format', format
|
||||
]
|
||||
if 190 < size_in_mb < 250:
|
||||
# We need -size 255m because of a bug in hdiutil. When the size of
|
||||
# srcfolder is close to 200MB hdiutil fails with
|
||||
# diskimages-helper: resize request is above maximum size allowed.
|
||||
cmd += ['-size', '255m']
|
||||
print('\nCreating dmg...')
|
||||
with timeit() as times:
|
||||
subprocess.check_call(cmd + [dmg])
|
||||
if internet_enable:
|
||||
subprocess.check_call(
|
||||
['/usr/bin/hdiutil', 'internet-enable', '-yes', dmg])
|
||||
print('dmg created in %d minutes and %d seconds' % tuple(times))
|
||||
shutil.rmtree(tdir)
|
||||
size = os.stat(dmg).st_size / (1024 * 1024.)
|
||||
print('\nInstaller size: %.2fMB\n' % size)
|
||||
return dmg
|
||||
|
||||
|
||||
def main():
|
||||
args = globals()['args']
|
||||
ext_dir = globals()['ext_dir']
|
||||
Freeze(
|
||||
os.path.join(ext_dir, kitty_constants['appname'] + '.app'),
|
||||
dont_strip=args.dont_strip,
|
||||
sign_installers=args.sign_installers,
|
||||
notarize=args.notarize,
|
||||
skip_tests=args.skip_tests
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
1
bypy/rsync.conf
Normal file
1
bypy/rsync.conf
Normal file
@@ -0,0 +1 @@
|
||||
to_vm_excludes '/build /dist /kitty/launcher/kitty /.build-cache /tags __pycache__ /*_commands.json *.so *.pyd *.pyc'
|
||||
31
bypy/site.py
Normal file
31
bypy/site.py
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import builtins
|
||||
import sys
|
||||
|
||||
import _sitebuiltins
|
||||
|
||||
|
||||
def set_quit():
|
||||
eof = 'Ctrl-D (i.e. EOF)'
|
||||
builtins.quit = _sitebuiltins.Quitter('quit', eof)
|
||||
builtins.exit = _sitebuiltins.Quitter('exit', eof)
|
||||
|
||||
|
||||
def set_helper():
|
||||
builtins.help = _sitebuiltins._Helper()
|
||||
|
||||
|
||||
def main():
|
||||
sys.argv[0] = sys.calibre_basename
|
||||
set_helper()
|
||||
set_quit()
|
||||
mod = __import__(sys.calibre_module, fromlist=[1])
|
||||
func = getattr(mod, sys.calibre_function)
|
||||
return func()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
274
bypy/sources.json
Normal file
274
bypy/sources.json
Normal file
@@ -0,0 +1,274 @@
|
||||
[
|
||||
{
|
||||
"name": "zlib",
|
||||
"unix": {
|
||||
"filename": "zlib-1.2.11.tar.xz",
|
||||
"hash": "sha256:4ff941449631ace0d4d203e3483be9dbc9da454084111f97ea0a2114e19bf066",
|
||||
"urls": ["https://zlib.net/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "bzip2",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "bzip2-1.0.8.tar.gz",
|
||||
"hash": "sha256:ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269",
|
||||
"urls": ["https://www.sourceware.org/pub/bzip2/bzip2-latest.tar.gz"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "pkg-config",
|
||||
"os": "macos",
|
||||
"unix": {
|
||||
"filename": "pkg-config-0.29.2.tar.gz",
|
||||
"hash": "sha256:6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591",
|
||||
"urls": ["https://pkg-config.freedesktop.org/releases/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"name": "expat",
|
||||
"unix": {
|
||||
"filename": "expat-2.2.10.tar.bz2",
|
||||
"hash": "sha256:b2c160f1b60e92da69de8e12333096aeb0c3bf692d41c60794de278af72135a5",
|
||||
"urls": ["https://github.com/libexpat/libexpat/releases/download/R_2_2_10/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"name": "libxml2",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "libxml2-2.9.10.tar.gz",
|
||||
"hash": "sha256:aafee193ffb8fe0c82d4afef6ef91972cbaf5feea100edc2f262750611b4be1f",
|
||||
"urls": ["ftp://xmlsoft.org/libxml2/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"name": "xkbcommon",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "libxkbcommon-1.0.3.tar.xz",
|
||||
"hash": "sha256:a2202f851e072b84e64a395212cbd976ee18a8ee602008b0bad02a13247dbc52",
|
||||
"urls": ["http://xkbcommon.org/download/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sqlite",
|
||||
"unix": {
|
||||
"filename": "sqlite-autoconf-3340100.tar.gz",
|
||||
"hash": "sha256:2a3bca581117b3b88e5361d0ef3803ba6d8da604b1c1a47d902ef785c1b53e89",
|
||||
"urls": ["https://www.sqlite.org/2021/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"name": "libffi",
|
||||
"unix": {
|
||||
"filename": "libffi-3.3.0.tar.gz",
|
||||
"hash": "sha256:72fba7922703ddfa7a028d513ac15a85c8d54c8d67f55fa5a4802885dc652056",
|
||||
"urls": ["https://github.com/libffi/libffi/releases/download/v3.3/libffi-3.3.tar.gz"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "openssl",
|
||||
"unix": {
|
||||
"filename": "openssl-1.1.1i.tar.gz",
|
||||
"hash": "sha256:e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242",
|
||||
"urls": ["https://www.openssl.org/source/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "ncurses",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "ncurses-6.2.tar.gz",
|
||||
"hash": "sha256:30306e0c76e0f9f1f0de987cf1c82a5c21e1ce6568b9227f7da5b71cbea86c9d",
|
||||
"urls": ["https://invisible-mirror.net/archives/ncurses/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "readline",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "readline-8.1.tar.gz",
|
||||
"hash": "sha256:f8ceb4ee131e3232226a17f51b164afc46cd0b9e6cef344be87c65962cb82b02",
|
||||
"urls": ["http://ftp.gnu.org/gnu/readline/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "xz",
|
||||
"unix": {
|
||||
"filename": "xz-5.2.5.tar.gz",
|
||||
"hash": "sha256:f6f4910fd033078738bd82bfba4f49219d03b17eb0794eb91efbae419f4aba10",
|
||||
"urls": ["https://tukaani.org/xz/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "xcrypt",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "xcrypt-4.4.17.tar.gz",
|
||||
"hash": "sha256:7665168d0409574a03f7b484682e68334764c29c21ca5df438955a381384ca07",
|
||||
"urls": ["https://github.com/besser82/libxcrypt/archive/v4.4.17.tar.gz"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "python",
|
||||
"unix": {
|
||||
"filename": "Python-3.9.1.tar.xz",
|
||||
"hash": "sha256:991c3f8ac97992f3d308fefeb03a64db462574eadbff34ce8bc5bb583d9903ff",
|
||||
"urls": ["https://www.python.org/ftp/python/3.9.1/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "setuptools",
|
||||
"unix": {
|
||||
"filename": "setuptools-53.0.0.tar.gz",
|
||||
"hash": "sha256:1b18ef17d74ba97ac9c0e4b4265f123f07a8ae85d9cd093949fa056d3eeeead5",
|
||||
"urls": ["pypi"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "pygments",
|
||||
"unix": {
|
||||
"filename": "Pygments-2.7.4.tar.gz",
|
||||
"hash": "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337",
|
||||
"urls": ["pypi"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "cmake",
|
||||
"os": "macos",
|
||||
"unix": {
|
||||
"filename": "cmake-3.19.4.tar.gz",
|
||||
"hash": "sha256:7d0232b9f1c57e8de81f38071ef8203e6820fe7eec8ae46a1df125d88dbcc2e1",
|
||||
"urls": ["https://cmake.org/files/v3.19/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "libpng",
|
||||
"unix": {
|
||||
"filename": "libpng-1.6.37.tar.xz",
|
||||
"hash": "sha256:505e70834d35383537b6491e7ae8641f1a4bed1876dbfe361201fc80868d88ca",
|
||||
"urls": ["http://downloads.sourceforge.net/sourceforge/libpng/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "lcms2",
|
||||
"unix": {
|
||||
"filename": "lcms2-2.12.tar.gz",
|
||||
"hash": "sha256:e501f1482fc424550ef3abbf86bf1c66090e1661249e89552d39ed5bf935df66",
|
||||
"urls": ["https://github.com/mm2/Little-CMS/archive/2.12/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "graphite",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "graphite2-1.3.14.tgz",
|
||||
"hash": "sha256:f99d1c13aa5fa296898a181dff9b82fb25f6cc0933dbaa7a475d8109bd54209d",
|
||||
"urls": ["http://downloads.sourceforge.net/silgraphite/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "pcre",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "pcre-8.44.tar.bz2",
|
||||
"hash": "sha256:19108658b23b3ec5058edc9f66ac545ea19f9537234be1ec62b714c84399366d",
|
||||
"urls": ["https://ftp.pcre.org/pub/pcre/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "iconv",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "libiconv-1.16.tar.gz",
|
||||
"hash": "sha256:e6a1b1b589654277ee790cce3734f07876ac4ccfaecbee8afa0b649cf529cc04",
|
||||
"urls": ["https://ftp.gnu.org/pub/gnu/libiconv/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "glib",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "glib-2.66.6.tar.xz",
|
||||
"hash": "sha256:80fff9c63d2725834328071c42003c311f77f91caf2285195c587c62f5638329",
|
||||
"urls": ["https://ftp.gnome.org/pub/GNOME/sources/glib/2.66/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "freetype",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "freetype-2.10.4.tar.xz",
|
||||
"hash": "sha256:86a854d8905b19698bbc8f23b860bc104246ce4854dcea8e3b0fb21284f75784",
|
||||
"urls": ["https://download.savannah.gnu.org/releases/freetype/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "fontconfig",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "fontconfig-2.13.1.tar.bz2",
|
||||
"hash": "sha256:f655dd2a986d7aa97e052261b36aa67b0a64989496361eca8d604e6414006741",
|
||||
"urls": ["http://www.fontconfig.org/release/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "harfbuzz",
|
||||
"unix": {
|
||||
"filename": "harfbuzz-2.7.4.tar.xz",
|
||||
"hash": "sha256:6ad11d653347bd25d8317589df4e431a2de372c0cf9be3543368e07ec23bb8e7",
|
||||
"urls": ["https://github.com/harfbuzz/harfbuzz/releases/download/2.7.4/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "wayland",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "wayland-1.19.0.tar.xz",
|
||||
"hash": "sha256:baccd902300d354581cd5ad3cc49daa4921d55fb416a5883e218750fef166d15",
|
||||
"urls": ["https://wayland.freedesktop.org/releases/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "wayland-protocols",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "wayland-protocols-1.20.tar.xz",
|
||||
"hash": "sha256:9782b7a1a863d82d7c92478497d13c758f52e7da4f197aa16443f73de77e4de7",
|
||||
"urls": ["https://wayland.freedesktop.org/releases/{filename}"]
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
@@ -4,8 +4,6 @@ import subprocess
|
||||
|
||||
files_to_exclude = '''\
|
||||
kitty/wcwidth-std.h
|
||||
kitty/glfw.c
|
||||
kitty/keys.h
|
||||
kitty/charsets.c
|
||||
kitty/unicode-data.c
|
||||
kitty/key_encoding.py
|
||||
|
||||
4
docs/_static/custom.css
vendored
4
docs/_static/custom.css
vendored
@@ -21,6 +21,10 @@ div.body p, div.body dd, div.body li, div.body blockquote {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
div.body {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
@@ -44,9 +44,9 @@ particular desktop, but it should work for most major desktop environments.
|
||||
# your PATH)
|
||||
ln -s ~/.local/kitty.app/bin/kitty ~/.local/bin/
|
||||
# Place the kitty.desktop file somewhere it can be found by the OS
|
||||
cp ~/.local/kitty.app/share/applications/kitty.desktop ~/.local/share/applications
|
||||
cp ~/.local/kitty.app/share/applications/kitty.desktop ~/.local/share/applications/
|
||||
# Update the path to the kitty icon in the kitty.desktop file
|
||||
sed -i "s/Icon\=kitty/Icon\=\/home\/$USER\/.local\/kitty.app\/share\/icons\/hicolor\/256x256\/apps\/kitty.png/g" ~/.local/share/applications/kitty.desktop
|
||||
sed -i "s|Icon=kitty|Icon=/home/$USER/.local/kitty.app/share/icons/hicolor/256x256/apps/kitty.png|g" ~/.local/share/applications/kitty.desktop
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
Building kitty from source
|
||||
==============================
|
||||
|
||||
.. image:: https://circleci.com/gh/kovidgoyal/kitty.svg?style=svg
|
||||
.. image:: https://github.com/kovidgoyal/kitty/workflows/CI/badge.svg
|
||||
:alt: Build status
|
||||
:target: https://circleci.com/gh/kovidgoyal/kitty
|
||||
:target: https://github.com/kovidgoyal/kitty/actions?query=workflow%3ACI
|
||||
|
||||
|
||||
|kitty| is designed to run from source, for easy hackability. Make sure
|
||||
@@ -18,6 +18,7 @@ Run-time dependencies:
|
||||
* harfbuzz >= 1.5.0
|
||||
* zlib
|
||||
* libpng
|
||||
* liblcms2
|
||||
* freetype (not needed on macOS)
|
||||
* fontconfig (not needed on macOS)
|
||||
* libcanberra (not needed on macOS)
|
||||
@@ -29,7 +30,7 @@ Build-time dependencies:
|
||||
* gcc or clang
|
||||
* pkg-config
|
||||
* For building on Linux in addition to the above dependencies you might also need to install the ``-dev`` packages for:
|
||||
``libdbus-1-dev``, ``libxcursor-dev``, ``libxrandr-dev``, ``libxi-dev``, ``libxinerama-dev``, ``libgl1-mesa-dev``, ``libxkbcommon-x11-dev``, ``libfontconfig-dev``, and ``libpython-dev``,
|
||||
``libdbus-1-dev``, ``libxcursor-dev``, ``libxrandr-dev``, ``libxi-dev``, ``libxinerama-dev``, ``libgl1-mesa-dev``, ``libxkbcommon-x11-dev``, ``libfontconfig-dev``, ``libx11-xcb-dev``, ``liblcms2-dev``, and ``libpython3-dev``,
|
||||
if they are not already installed by your distro.
|
||||
|
||||
Install and run from source
|
||||
@@ -63,10 +64,32 @@ This :file:`kitty.app` unlike the released one does not include its own copy of
|
||||
python and the other dependencies. So if you ever un-install/upgrade those dependencies
|
||||
you might have to rebuild the app.
|
||||
|
||||
Note that the released :file:`kitty.dmg` includes all dependencies, unlike the
|
||||
:file:`kitty.app` built above and is built automatically by using the :file:`kitty` branch of
|
||||
`build-calibre <https://github.com/kovidgoyal/build-calibre>`_ however, that
|
||||
is designed to run on Linux and is not for the faint of heart.
|
||||
.. note::
|
||||
The released :file:`kitty.dmg` includes all dependencies, unlike the
|
||||
:file:`kitty.app` built above and is built automatically by using the
|
||||
:file:`kitty` branch of `build-calibre
|
||||
<https://github.com/kovidgoyal/build-calibre>`_ however, that is designed to
|
||||
run on Linux and is not for the faint of heart.
|
||||
|
||||
|
||||
.. note::
|
||||
Apple disallows certain functionality, such as notifications for unsigned applications.
|
||||
If you need this functionality, you can try signing the built kitty.app with
|
||||
a self signed certificate, see for example, `here
|
||||
<https://stackoverflow.com/questions/27474751/how-can-i-codesign-an-app-without-being-in-the-mac-developer-program/27474942>`_.
|
||||
|
||||
Build and run from source with Nix
|
||||
-------------------------------------------
|
||||
|
||||
On NixOS or any other Linux or macOS system with the Nix package manager
|
||||
installed, execute `nix-shell
|
||||
<https://nixos.org/guides/nix-pills/developing-with-nix-shell.html>`_ to create
|
||||
the correct environment to build kitty or use ``nix-shell --pure`` instead to
|
||||
eliminate most of the influence of the outside system, e.g. globally installed
|
||||
packages. ``nix-shell`` will automatically fetch all required dependencies and
|
||||
make them available in the newly spawned shell.
|
||||
|
||||
Then proceed with ``make`` or ``make app`` according to the platform specific instructions above.
|
||||
|
||||
|
||||
Note for Linux/macOS packagers
|
||||
|
||||
@@ -1,9 +1,683 @@
|
||||
Changelog
|
||||
==============
|
||||
|
||||
|kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
||||
|kitty| is a feature-rich, cross-platform, *fast*, GPU based terminal.
|
||||
To update |kitty|, :doc:`follow the instructions <binary>`.
|
||||
|
||||
0.20.0 [2021-04-19]
|
||||
----------------------
|
||||
|
||||
- Support display of animated images ``kitty +kitten icat animation.gif``. See
|
||||
:ref:`animation_protocol` for details on animation support in the kitty
|
||||
graphics protocol.
|
||||
|
||||
- A new keyboard reporting protocol with various advanced features that can be
|
||||
used by full screen terminal programs and even games, see
|
||||
:doc:`keyboard-protocol` (:iss:`3248`)
|
||||
|
||||
- **Backward incompatibility**: Session files now use the full :doc:`launch <launch>`
|
||||
command with all its capabilities. However, the syntax of the command is
|
||||
slightly different from before. In particular watchers are now specified
|
||||
directly on launch and environment variables are set using ``--env``.
|
||||
|
||||
- Allow setting colors when creating windows using the :doc:`launch <launch>` command.
|
||||
|
||||
- A new option :opt:`tab_powerline_style` to control the appearance of the tab
|
||||
bar when using the powerline tab bar style.
|
||||
|
||||
- A new option :opt:`scrollback_fill_enlarged_window` to fill extra lines in
|
||||
the window when the window is expanded with lines from the scrollback
|
||||
(:pull:`3371`)
|
||||
|
||||
- diff kitten: Implement recursive diff over SSH (:iss:`3268`)
|
||||
|
||||
- ssh kitten: Allow using python instead of the shell on the server, useful if
|
||||
the shell used is a non-POSIX compliant one, such as fish (:iss:`3277`)
|
||||
|
||||
- Add support for the color settings stack that XTerm copied from us without
|
||||
acknowledgement and decided to use incompatible escape codes for.
|
||||
|
||||
- Add entries to the terminfo file for some user capabilities that are shared
|
||||
with XTerm (:pull:`3193`)
|
||||
|
||||
- The launch command now does more sophisticated resolving of executables to
|
||||
run. The system-wide PATH is used first, then system specific default paths,
|
||||
and finally the PATH inside the shell.
|
||||
|
||||
- Double clicking on empty tab bar area now opens a new tab (:iss:`3201`)
|
||||
|
||||
- kitty @ ls: Show only environment variables that are different for each
|
||||
window, by default.
|
||||
|
||||
- When passing a directory or a non-executable file as the program to run to
|
||||
kitty opens it with the shell or by parsing the shebang, instead of just failing.
|
||||
|
||||
- Linux: Fix rendering of emoji followed by the graphics variation selector not
|
||||
being colored with some fonts (:iss:`3211`)
|
||||
|
||||
- Unicode input: Fix using index in select by name mode not working for indices
|
||||
larger than 16. Also using an index does not filter the list of matches. (:pull:`3219`)
|
||||
|
||||
- Wayland: Add support for the text input protocol (:iss:`3410`)
|
||||
|
||||
- Wayland: Fix mouse handling when using client side decorations
|
||||
|
||||
- Wayland: Fix un-maximizing a window not restoring its size to what it was
|
||||
before being maximized
|
||||
|
||||
- GNOME/Wayland: Improve window decorations the titlebar now shows the window
|
||||
title. Allow running under Wayland on GNOME by default. (:iss:`3284`)
|
||||
|
||||
- Panel kitten: Allow setting WM_CLASS (:iss:`3233`)
|
||||
|
||||
- macOS: Add menu items to close the OS window and the current tab (:pull:`3240`, :iss:`3246`)
|
||||
|
||||
- macOS: Allow opening script and command files with kitty (:iss:`3366`)
|
||||
|
||||
- Also detect ``gemini://`` URLs when hovering with the mouse (:iss:`3370`)
|
||||
|
||||
- When using a non-US keyboard layout and pressing :kbd:`ctrl+key` when
|
||||
the key matches an English key, send that to the program running in the
|
||||
terminal automatically (:iss:`2000`)
|
||||
|
||||
- When matching shortcuts, also match on shifted keys, so a shortcut defined as
|
||||
:kbd:`ctrl+plus` will match a keyboard where you have to press
|
||||
:kbd:`shift+equal` to get the plus key (:iss:`2000`)
|
||||
|
||||
- Fix extra space at bottom of OS window when using the fat layout with the tab bar at the
|
||||
top (:iss:`3258`)
|
||||
|
||||
- Fix window icon not working on X11 with 64bits (:iss:`3260`)
|
||||
|
||||
- Fix OS window sizes under 100px resulting in scaled display (:iss:`3307`)
|
||||
|
||||
- Fix rendering of ligatures in the latest release of Cascadia code, which for
|
||||
some reason puts empty glyphs after the ligature glyph rather than before it
|
||||
(:iss:`3313`)
|
||||
|
||||
- Improve handling of infinite length ligatures in newer versions of FiraCode
|
||||
and CascadiaCode. Now such ligatures are detected based on glyph naming
|
||||
convention. This removes the gap in the ligatures at cell boundaries (:iss:`2695`)
|
||||
|
||||
- macOS: Disable the native operating system tabs as they are non-functional
|
||||
and can be confusing (:iss:`3325`)
|
||||
|
||||
- hints kitten: When using the linenumber action with a background action,
|
||||
preserve the working directory (:iss:`3352`)
|
||||
|
||||
- Graphics protocol: Fix suppression of responses not working for chunked
|
||||
transmission (:iss:`3375`)
|
||||
|
||||
- Fix inactive tab closing causing active tab to change (:iss:`3398`)
|
||||
|
||||
- Fix a crash on systems using musl as libc (:iss:`3395`)
|
||||
|
||||
- Improve rendering of rounded corners by using a rectircle equation rather
|
||||
than a cubic bezier (:iss:`3409`)
|
||||
|
||||
- Graphics protocol: Add a control to allow clients to specify that the cursor
|
||||
should not move when displaying an image (:iss:`3411`)
|
||||
|
||||
- Fix marking of text not working on lines that contain zero cells
|
||||
(:iss:`3403`)
|
||||
|
||||
- Fix the selection getting changed if the screen contents scroll while
|
||||
the selection is in progress (:iss:`3431`)
|
||||
|
||||
- X11: Fix :opt:`resize_in_steps` being applied even when window is maximized
|
||||
(:iss:`3473`)
|
||||
|
||||
|
||||
0.19.3 [2020-12-19]
|
||||
-------------------
|
||||
|
||||
- Happy holidays to all kitty users!
|
||||
|
||||
- A new :doc:`broadcast <kittens/broadcast>` kitten to type in all kitty windows
|
||||
simultaneously (:iss:`1569`)
|
||||
|
||||
- Add a new mappable `select_tab` action to choose a tab to switch to even
|
||||
when the tab bar is hidden (:iss:`3115`)
|
||||
|
||||
- Allow specifying text formatting in :opt:`tab_title_template` (:iss:`3146`)
|
||||
|
||||
- Linux: Read :opt:`font_features` from the FontConfig database as well, so
|
||||
that they can be configured in a single, central location (:pull:`3174`)
|
||||
|
||||
- Graphics protocol: Add support for giving individual image placements their
|
||||
own ids and for asking the terminal emulator to assign ids for images. Also
|
||||
allow suppressing responses from the terminal to commands.
|
||||
These are backwards compatible protocol extensions. (:iss:`3133`,
|
||||
:iss:`3163`)
|
||||
|
||||
- Distribute extra pixels among all eight-blocks rather than adding them
|
||||
all to the last block (:iss:`3097`)
|
||||
|
||||
- Fix drawing of a few sextant characters incorrect (:pull:`3105`)
|
||||
|
||||
- macOS: Fix minimize not working for chromeless windows (:iss:`3112`)
|
||||
|
||||
- Preserve lines in the scrollback if a scrolling region is defined that
|
||||
is contiguous with the top of the screen (:iss:`3113`)
|
||||
|
||||
- Wayland: Fix key repeat being stopped by the release of an unrelated key
|
||||
(:iss:`2191`)
|
||||
|
||||
- Add an option, :opt:`detect_urls` to control whether kitty will detect URLs
|
||||
when the mouse moves over them (:pull:`3118`)
|
||||
|
||||
- Graphics protocol: Dont return filename in the error message when opening file
|
||||
fails, since filenames can contain control characters (:iss:`3128`)
|
||||
|
||||
- macOS: Partial fix for traditional fullscreen not working on Big Sur
|
||||
(:iss:`3100`)
|
||||
|
||||
- Fix one ANSI formatting escape code not being removed from the pager history
|
||||
buffer when piping it as plain text (:iss:`3132`)
|
||||
|
||||
- Match the save/restore cursor behavior of other terminals, for the sake of
|
||||
interoperability. This means that doing a DECRC without a prior DECSC is now
|
||||
undefined (:iss:`1264`)
|
||||
|
||||
- Fix mapping ``remote_control send-text`` not working (:iss:`3147`)
|
||||
|
||||
- Add a ``right`` option for :opt:`tab_switch_strategy` (:pull:`3155`)
|
||||
|
||||
- Fix a regression in 0.19.0 that caused a rare crash when using the optional
|
||||
:opt:`scrollback_pager_history_size` (:iss:`3049`)
|
||||
|
||||
- Full screen kittens: Fix incorrect cursor position after kitten quits
|
||||
(:iss:`3176`)
|
||||
|
||||
|
||||
0.19.2 [2020-11-13]
|
||||
-------------------
|
||||
|
||||
- A new :doc:`kittens/query_terminal` kitten to easily query the running kitty
|
||||
via escape codes to detect its version, and the values of
|
||||
configuration options that enable or disable terminal features.
|
||||
|
||||
- Options to control mouse pointer shape, :opt:`default_pointer_shape`, and
|
||||
:opt:`pointer_shape_when_dragging` (:pull:`3041`)
|
||||
|
||||
- Font independent rendering for braille characters, which ensures they are properly
|
||||
aligned at all font sizes.
|
||||
|
||||
- Fix a regression in 0.19.0 that caused borders not to be drawn when setting
|
||||
:opt:`window_margin_width` and keeping :opt:`draw_minimal_borders` on
|
||||
(:iss:`3017`)
|
||||
|
||||
- Fix a regression in 0.19.0 that broke rendering of one-eight bar unicode
|
||||
characters at very small font sizes (:iss:`3025`)
|
||||
|
||||
- Wayland: Fix a crash under GNOME when using multiple OS windows
|
||||
(:pull:`3066`)
|
||||
|
||||
- Fix selections created by dragging upwards not being auto-cleared when
|
||||
screen contents change (:pull:`3028`)
|
||||
|
||||
- macOS: Fix kitty not being added to PATH automatically when using pre-built
|
||||
binaries (:iss:`3063`)
|
||||
|
||||
- Allow adding MIME definitions to kitty by placing a ``mime.types`` file in
|
||||
the kitty config directory (:iss:`3056`)
|
||||
|
||||
- Dont ignore :option:`--title` when using a session file that defines no
|
||||
windows (:iss:`3055`)
|
||||
|
||||
- Fix the send_text action not working in URL handlers (:iss:`3081`)
|
||||
|
||||
- Fix last character of URL not being detected if it is the only character on a
|
||||
new line (:iss:`3088`)
|
||||
|
||||
- Don't restrict the ICH,DCH,REP control codes to only the current scroll region (:iss:`3090`, :iss:`3096`)
|
||||
|
||||
|
||||
0.19.1 [2020-10-06]
|
||||
-------------------
|
||||
|
||||
- hints kitten: Add an ``ip`` type for easy selection of IP addresses
|
||||
(:pull:`3009`)
|
||||
|
||||
- Fix a regression that caused a segfault when using
|
||||
:opt:`scrollback_pager_history_size` and it needs to be expanded (:iss:`3011`)
|
||||
|
||||
- Fix update available notifications repeating (:pull:`3006`)
|
||||
|
||||
|
||||
0.19.0 [2020-10-04]
|
||||
-------------------
|
||||
|
||||
- Add support for `hyperlinks from terminal programs
|
||||
<https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>`_.
|
||||
Controlled via :opt:`allow_hyperlinks` (:iss:`68`)
|
||||
|
||||
- Add support for easily editing or downloading files over SSH sessions
|
||||
without the need for any special software, see :doc:`kittens/remote_file`
|
||||
|
||||
- A new :doc:`kittens/hyperlinked_grep` kitten to easily search files and open
|
||||
the results at the matched line by clicking on them.
|
||||
|
||||
- Allow customizing the :doc:`actions kitty takes <open_actions>` when clicking on URLs
|
||||
|
||||
- Improve rendering of borders when using minimal borders. Use less space and
|
||||
do not display a box around active windows
|
||||
|
||||
- Add a new extensible escape code to allow terminal programs to trigger
|
||||
desktop notifications. See :ref:`desktop_notifications` (:iss:`1474`)
|
||||
|
||||
- Implement special rendering for various characters from the set of "Symbols
|
||||
for Legacy Computing" from the Unicode 13 standard
|
||||
|
||||
- Unicode input kitten: Allow choosing symbols from the NERD font as well.
|
||||
These are mostly Private Use symbols not in any standard, however are common. (:iss:`2972`)
|
||||
|
||||
- Allow specifying border sizes in either pts or pixels. Change the default to
|
||||
0.5pt borders as this works best with the new minimal border style
|
||||
|
||||
- Add support for displaying correct colors with non-sRGB PNG files (Adds a
|
||||
dependency on liblcms2)
|
||||
|
||||
- hints kitten: Add a new :option:`kitty +kitten hints --type` of ``hyperlink`` useful
|
||||
for activating hyperlinks using just the keyboard
|
||||
|
||||
- Allow tracking focus change events in watchers (:iss:`2918`)
|
||||
|
||||
- Allow specifying watchers in session files and via a command line argument
|
||||
(:iss:`2933`)
|
||||
|
||||
- Add a setting :opt:`tab_activity_symbol` to show a symbol in the tab title
|
||||
if one of the windows has some activity after it was last focused
|
||||
(:iss:`2515`)
|
||||
|
||||
- macOS: Switch to using the User Notifications framework for notifications.
|
||||
The current notifications framework has been deprecated in Big Sur. The new
|
||||
framework only allows notifications from signed and notarized applications,
|
||||
so people using kitty from homebrew/source are out of luck. Complain to
|
||||
Apple.
|
||||
|
||||
- When in the main screen and a program grabs the mouse, do not use the scroll
|
||||
wheel events to scroll the scrollback buffer, instead send them to the
|
||||
program (:iss:`2939`)
|
||||
|
||||
- Fix unfocused windows in which a bell occurs not changing their border color
|
||||
to red until a relayout
|
||||
|
||||
- Linux: Fix automatic detection of bold/italic faces for fonts such as IBM
|
||||
Plex Mono that have the regular face with a full name that is the same as the
|
||||
family name (:iss:`2951`)
|
||||
|
||||
- Fix a regression that broke :opt:`kitten_alias` (:iss:`2952`)
|
||||
|
||||
- Fix a regression that broke the ``move_window_to_top`` action (:pull:`2953`)
|
||||
|
||||
- Fix a memory leak when changing font sizes
|
||||
|
||||
- Fix some lines in the scrollback buffer not being properly rendered after a
|
||||
window resize/font size change (:iss:`2619`)
|
||||
|
||||
|
||||
0.18.3 [2020-08-11]
|
||||
-------------------
|
||||
|
||||
- hints kitten: Allow customizing hint colors (:pull:`2894`)
|
||||
|
||||
- Wayland: Fix a typo in the previous release that broke reading mouse cursor size (:iss:`2895`)
|
||||
|
||||
- Fix a regression in the previous release that could cause an exception during
|
||||
startup in rare circumstances (:iss:`2896`)
|
||||
|
||||
- Fix image leaving behind a black rectangle when switch away and back to
|
||||
alternate screen (:iss:`2901`)
|
||||
|
||||
- Fix one pixel mis-alignment of rounded corners when either the cell
|
||||
dimensions or the thickness of the line is an odd number of pixels
|
||||
(:iss:`2907`)
|
||||
|
||||
- Fix a regression that broke specifying OS window size in the session file
|
||||
(:iss:`2908`)
|
||||
|
||||
|
||||
0.18.2 [2020-07-28]
|
||||
--------------------
|
||||
|
||||
- X11: Improve handling of multiple keyboards. Now pressing a modifier key in
|
||||
one keyboard and a normal key in another works (:iss:`2362`). Don't rebuild
|
||||
keymaps on new keyboard events that only change geometry (:iss:`2787`).
|
||||
Better handling of multiple keyboards with incompatible layouts (:iss:`2726`)
|
||||
|
||||
- Improve anti-aliasing of triangular box drawing characters, noticeable on
|
||||
low-resolution screens (:iss:`2844`)
|
||||
|
||||
- Fix ``kitty @ send-text`` not working reliably when using a socket for remote
|
||||
control (:iss:`2852`)
|
||||
|
||||
- Implement support for box drawing rounded-corners characters (:iss:`2240`)
|
||||
|
||||
- Allow setting the class for new OS windows in a session file
|
||||
|
||||
- When a character from the Unicode Dingbat block is followed by a space, use
|
||||
the extra space to render a larger version of the character (:iss:`2850`)
|
||||
|
||||
- macOS: Fix the LC_CTYPE env var being set to UTF-8 on systems in which the
|
||||
language and country code do not form a valid locale (:iss:`1233`)
|
||||
|
||||
- macOS: Fix :kbd:`cmd+plus` not changing font size (:iss:`2839`)
|
||||
|
||||
- Make neighboring window selection in grid and splits layouts more intelligent
|
||||
(:pull:`2840`)
|
||||
|
||||
- Allow passing the current selection to kittens (:iss:`2796`)
|
||||
|
||||
- Fix pre-edit text not always being cleared with ibus input (:iss:`2862`)
|
||||
|
||||
- Allow setting the :opt:`background_opacity` of new OS windows created via
|
||||
:option:`kitty --single-instance` using the :option:`kitty --override` command line
|
||||
argument (:iss:`2806`)
|
||||
|
||||
- Fix the CSI J (Erase in display ED) escape code not removing line continued
|
||||
markers (:iss:`2809`)
|
||||
|
||||
- hints kitten: In linenumber mode expand paths that starts with ~
|
||||
(:iss:`2822`)
|
||||
|
||||
- Fix ``launch --location=last`` not working (:iss:`2841`)
|
||||
|
||||
- Fix incorrect centering when a PUA or symbol glyph is followed by more than one space
|
||||
|
||||
- Have the :opt:`confirm_os_window_close` option also apply when closing tabs
|
||||
with multiple windows (:iss:`2857`)
|
||||
|
||||
- Add support for legacy DECSET codes 47, 1047 and 1048 (:pull:`2871`)
|
||||
|
||||
- macOS: no longer render emoji 20% below the baseline. This caused some emoji
|
||||
to be cut-off and also look misaligned with very high cells (:iss:`2873`)
|
||||
|
||||
- macOS: Make the window id of OS windows available in the ``WINDOWID``
|
||||
environment variable (:pull:`2877`)
|
||||
|
||||
- Wayland: Fix a regression in 0.18.0 that could cause crashes related to mouse
|
||||
cursors in some rare circumstances (:iss:`2810`)
|
||||
|
||||
- Fix change in window size that does not change number of cells not being
|
||||
reported to the kernel (:iss:`2880`)
|
||||
|
||||
|
||||
0.18.1 [2020-06-23]
|
||||
--------------------
|
||||
|
||||
- macOS: Fix for diff kitten not working with python 3.8 (:iss:`2780`)
|
||||
|
||||
|
||||
0.18.0 [2020-06-20]
|
||||
--------------------
|
||||
|
||||
- Allow multiple overlay windows per normal window
|
||||
|
||||
- Add an option :opt:`confirm_os_window_close` to ask for confirmation
|
||||
when closing an OS window with multiple kitty windows.
|
||||
|
||||
- Tall and Fat layouts: Add a ``mirrored`` option to put the full size window
|
||||
on the opposite edge of the screen (:iss:`2654`)
|
||||
|
||||
- Tall and Fat layouts: Add mappable actions to increase or decrease the number
|
||||
of full size windows (:iss:`2688`)
|
||||
|
||||
- Allow sending arbitrary signals to the current foreground process in a window
|
||||
using either a mapping in kitty.conf or via remote control (:iss:`2778`)
|
||||
|
||||
- Allow sending the back and forward mouse buttons to terminal applications
|
||||
(:pull:`2742`)
|
||||
|
||||
- **Backwards incompatibility**: The numbers used to encode mouse buttons
|
||||
for the ``send_mouse_event`` function that can be used in kittens have
|
||||
been changed (see :ref:`send_mouse_event`).
|
||||
|
||||
- Add a new mappable ``quit`` action to quit kitty completely.
|
||||
|
||||
- Fix marks using different colors with regexes using only a single color
|
||||
(:pull:`2663`)
|
||||
|
||||
- Linux: Workaround for broken Nvidia drivers for old cards (:iss:`456`)
|
||||
|
||||
- Wayland: Fix kitty being killed on some Wayland compositors if a hidden window
|
||||
has a lot of output (:iss:`2329`)
|
||||
|
||||
- BSD: Fix controlling terminal not being established (:pull:`2686`)
|
||||
|
||||
- Add support for the CSI REP escape code (:pull:`2702`)
|
||||
|
||||
- Wayland: Fix mouse cursor rendering on HiDPI screens (:pull:`2709`)
|
||||
|
||||
- X11: Recompile keymaps on XkbNewKeyboardNotify events (:iss:`2726`)
|
||||
|
||||
- X11: Reduce startup time by ~25% by only querying GLX for framebuffer
|
||||
configurations once (:iss:`2754`)
|
||||
|
||||
- macOS: Notarize the kitty application bundle (:iss:`2040`)
|
||||
|
||||
- Fix the kitty shell launched via a mapping needlessly requiring
|
||||
:opt:`allow_remote_control` to be turned on.
|
||||
|
||||
|
||||
0.17.4 [2020-05-09]
|
||||
--------------------
|
||||
|
||||
- Allow showing the name of the current layout and the number of windows
|
||||
in tab titles (:iss:`2634`)
|
||||
|
||||
- macOS: Fix a regression in the previous release that caused ligatures to be
|
||||
not be centered horizontally (:iss:`2591`)
|
||||
|
||||
- By default, double clicking no longer considers the : as part of words, see
|
||||
:opt:`select_by_word_characters` (:iss:`2602`)
|
||||
|
||||
- Fix a regression that caused clicking in the padding/margins of windows in
|
||||
the stack layout to switch the window to the first window (:iss:`2604`)
|
||||
|
||||
- macOS: Fix a regression that broke drag and drop (:iss:`2605`)
|
||||
|
||||
- Report modifier key state when sending wheel events to the terminal program
|
||||
|
||||
- Fix kitty @ send-text not working with text larger than 1024 bytes when using
|
||||
:option:`kitty --listen-on` (:iss:`2607`)
|
||||
|
||||
- Wayland: Fix OS window title not updating for hidden windows (:iss:`2629`)
|
||||
|
||||
- Fix :opt:`background_tint` making the window semi-transparent (:iss:`2618`)
|
||||
|
||||
|
||||
0.17.3 [2020-04-23]
|
||||
--------------------
|
||||
|
||||
- Allow individually setting margins and padding for each edge (left, right,
|
||||
top, bottom). Margins can also be controlled per window via remote control
|
||||
(:iss:`2546`)
|
||||
|
||||
- Fix reverse video not being rendered correctly when using transparency or a
|
||||
background image (:iss:`2419`)
|
||||
|
||||
- Allow mapping arbitrary remote control commands to key presses in
|
||||
:file:`kitty.conf`
|
||||
|
||||
- X11: Fix crash when doing drag and drop from some applications (:iss:`2505`)
|
||||
|
||||
- Fix :option:`launch --stdin-add-formatting` not working (:iss:`2512`)
|
||||
|
||||
- Update to Unicode 13.0 (:iss:`2513`)
|
||||
|
||||
- Render country flags designated by a pair of unicode codepoints
|
||||
in two cells instead of four.
|
||||
|
||||
- diff kitten: New option to control the background color for filler lines in
|
||||
the margin (:iss:`2518`)
|
||||
|
||||
- Fix specifying options for layouts in the startup session file not working
|
||||
(:iss:`2520`)
|
||||
|
||||
- macOS: Fix incorrect horizontal positioning of some full-width East Asian characters
|
||||
(:iss:`1457`)
|
||||
|
||||
- macOS: Render multi-cell PUA characters centered, matching behavior on other
|
||||
platforms
|
||||
|
||||
- Linux: Ignore keys if they are designated as layout/group/mode switch keys
|
||||
(:iss:`2519`)
|
||||
|
||||
- Marks: Fix marks not handling wide characters and tab characters correctly
|
||||
(:iss:`2534`)
|
||||
|
||||
- Add a new :opt:`listen_on` option in kitty.conf to set :option:`kitty --listen-on`
|
||||
globally. Also allow using environment variables in this option (:iss:`2569`).
|
||||
|
||||
- Allow sending mouse events in kittens (:pull:`2538`)
|
||||
|
||||
- icat kitten: Fix display of 16-bit depth images (:iss:`2542`)
|
||||
|
||||
- Add ncurses specific terminfo definitions for strikethrough (:pull:`2567`)
|
||||
|
||||
- Fix a regression in 0.17 that broke displaying graphics over SSH
|
||||
(:iss:`2568`)
|
||||
|
||||
- Fix :option:`--title` not being applied at window creation time (:iss:`2570`)
|
||||
|
||||
0.17.2 [2020-03-29]
|
||||
--------------------
|
||||
|
||||
- Add a :option:`launch --watcher` option that allows defining callbacks
|
||||
that are called for various events in the window's life-cycle (:iss:`2440`)
|
||||
|
||||
- Fix a regression in 0.17 that broke drawing of borders with non-minimal
|
||||
borders (:iss:`2474`)
|
||||
|
||||
- Hints kitten: Allow copying to primary selection as well as clipboard
|
||||
(:pull:`2487`)
|
||||
|
||||
- Add a new mappable action ``close_other_windows_in_tab`` to close all but the
|
||||
active window (:iss:`2484`)
|
||||
|
||||
- Hints kitten: Adjust the default regex used to detect line numbers to handle
|
||||
line+column numbers (:iss:`2268`)
|
||||
|
||||
- Fix blank space at the start of tab bar in the powerline style when first tab is
|
||||
inactive (:iss:`2478`)
|
||||
|
||||
- Fix regression causing incorrect rendering of separators in tab bar when
|
||||
defining a tab bar background color (:pull:`2480`)
|
||||
|
||||
- Fix a regression in 0.17 that broke the kitty @ launch remote command and
|
||||
also broke the --tab-title option when creating a new tab. (:iss:`2488`)
|
||||
|
||||
- Linux: Fix selection of fonts with multiple width variants not preferring
|
||||
the normal width faces (:iss:`2491`)
|
||||
|
||||
|
||||
0.17.1 [2020-03-24]
|
||||
--------------------
|
||||
|
||||
- Fix :opt:`cursor_underline_thickness` not working (:iss:`2465`)
|
||||
|
||||
- Fix a regression in 0.17 that caused tab bar background to be rendered after
|
||||
the last tab as well (:iss:`2464`)
|
||||
|
||||
- macOS: Fix a regression in 0.17 that caused incorrect variants to be
|
||||
automatically selected for some fonts (:iss:`2462`)
|
||||
|
||||
- Fix a regression in 0.17 that caused kitty @ set-colors to require setting
|
||||
cursor_text_color (:iss:`2470`)
|
||||
|
||||
|
||||
0.17.0 [2020-03-24]
|
||||
--------------------
|
||||
|
||||
- :ref:`splits_layout` to arrange windows in arbitrary splits
|
||||
(:iss:`2308`)
|
||||
|
||||
- Add support for specifying a background image, see :opt:`background_image`
|
||||
(:iss:`163` and :pull:`2326`; thanks to Fredrick Brennan.)
|
||||
|
||||
- A new :opt:`background_tint` option to darken the background under the text
|
||||
area when using background images and/or transparent windows.
|
||||
|
||||
- Allow selection of single cells with the mouse. Also improve mouse selection
|
||||
to follow semantics common to most programs (:iss:`945`)
|
||||
|
||||
- New options :opt:`cursor_beam_thickness` and :opt:`cursor_underline_thickness` to control the thickness of the
|
||||
beam and underline cursors (:iss:`2337` and :pull:`2342`)
|
||||
|
||||
- When the application running in the terminal grabs the mouse, pass middle
|
||||
clicks to the application unless :opt:`terminal_select_modifiers` are
|
||||
pressed (:iss:`2368`)
|
||||
|
||||
- A new ``copy_and_clear_or_interrupt`` function (:iss:`2403`)
|
||||
|
||||
- X11: Fix arrow mouse cursor using right pointing instead of the default left
|
||||
pointing arrow (:iss:`2341`)
|
||||
|
||||
- Allow passing the currently active kitty window id in the launch command
|
||||
(:iss:`2391`)
|
||||
|
||||
- unicode input kitten: Allow pressing :kbd:`ctrl+tab` to change the input mode
|
||||
(:iss:`2343`)
|
||||
|
||||
- Fix a bug that prevented using custom functions with the new marks feature
|
||||
(:iss:`2344`)
|
||||
|
||||
- Make the set of URL prefixes that are recognized while hovering with the
|
||||
mouse configurable (:iss:`2416`)
|
||||
|
||||
- Fix border/margin/padding sizes not being recalculated on DPI change
|
||||
(:iss:`2346`)
|
||||
|
||||
- diff kitten: Fix directory diffing with removed binary files failing
|
||||
(:iss:`2378`)
|
||||
|
||||
- macOS: Fix menubar title not updating on OS Window focus change (:iss:`2350`)
|
||||
|
||||
- Fix rendering of combining characters with fonts that have glyphs for
|
||||
precomposed characters but not decomposed versions (:iss:`2365`)
|
||||
|
||||
- Fix incorrect rendering of selection when using rectangular select and
|
||||
scrolling (:iss:`2351`)
|
||||
|
||||
- Allow setting WM_CLASS and WM_NAME when creating new OS windows with the
|
||||
launch command (:option:`launch --os-window-class`)
|
||||
|
||||
- macOS: When switching input method while a pending multi-key input is in
|
||||
progress, clear the pending input (:iss:`2358`)
|
||||
|
||||
- Fix a regression in the previous release that broke switching to neighboring windows
|
||||
in the Grid layout when there are less than four windows (:iss:`2377`)
|
||||
|
||||
- Fix colors in scrollback pager off if the window has redefined terminal
|
||||
colors using escape codes (:iss:`2381`)
|
||||
|
||||
- Fix selection not updating properly while scrolling (:iss:`2442`)
|
||||
|
||||
- Allow extending selections by dragging with right button pressed
|
||||
(:iss:`2445`)
|
||||
|
||||
- Workaround for bug in less that causes colors to reset at wrapped lines
|
||||
(:iss:`2381`)
|
||||
|
||||
- X11/Wayland: Allow drag and drop of text/plain in addition to text/uri-list
|
||||
(:iss:`2441`)
|
||||
|
||||
- Dont strip :code:`&` and :code:`-` from the end of URLs (:iss:`2436`)
|
||||
|
||||
- Fix ``@selection`` placeholder not working with launch command (:iss:`2417`)
|
||||
|
||||
- Drop support for python 3.5
|
||||
|
||||
- Wayland: Fix a crash when drag and dropping into kitty (:iss:`2432`)
|
||||
|
||||
- diff kitten: Fix images lingering as blank rectangles after the kitten quits
|
||||
(:iss:`2449`)
|
||||
|
||||
- diff kitten: Fix images losing position when scrolling using mouse
|
||||
wheel/touchpad
|
||||
|
||||
|
||||
0.16.0 [2020-01-28]
|
||||
--------------------
|
||||
|
||||
|
||||
128
docs/conf.py
128
docs/conf.py
@@ -7,28 +7,34 @@
|
||||
# full list see the documentation:
|
||||
# https://www.sphinx-doc.org/en/master/config
|
||||
|
||||
import importlib
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from functools import partial
|
||||
from typing import (
|
||||
Any, Callable, Dict, Iterable, List, Match, Optional, Sequence, Tuple,
|
||||
Union
|
||||
)
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst.roles import set_classes
|
||||
from pygments.lexer import RegexLexer, bygroups
|
||||
from pygments.token import (
|
||||
from pygments.lexer import RegexLexer, bygroups # type: ignore
|
||||
from pygments.token import ( # type: ignore
|
||||
Comment, Keyword, Literal, Name, Number, String, Whitespace
|
||||
)
|
||||
from sphinx import addnodes
|
||||
from sphinx.environment.adapters.toctree import TocTree
|
||||
from sphinx.util.logging import getLogger
|
||||
from sphinx import addnodes, version_info # type: ignore
|
||||
from sphinx.environment.adapters.toctree import TocTree # type: ignore
|
||||
from sphinx.util.logging import getLogger # type: ignore
|
||||
|
||||
kitty_src = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if kitty_src not in sys.path:
|
||||
sys.path.insert(0, kitty_src)
|
||||
str_version = importlib.import_module('kitty.constants').str_version
|
||||
|
||||
from kitty.conf.definition import Option, Shortcut # noqa
|
||||
from kitty.constants import str_version # noqa
|
||||
|
||||
|
||||
# config {{{
|
||||
# -- Project information -----------------------------------------------------
|
||||
@@ -77,7 +83,7 @@ master_doc = 'index'
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
language: Optional[str] = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
@@ -134,9 +140,6 @@ html_theme_options = {
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static', '../logo/kitty.png']
|
||||
html_context = {
|
||||
'css_files': ['_static/custom.css']
|
||||
}
|
||||
html_favicon = '../logo/kitty.png'
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
@@ -176,7 +179,7 @@ man_pages = [
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'kitty', 'kitty Documentation',
|
||||
author, 'kitty', 'A cross-platform, fast, feature full, GPU based terminal emulator',
|
||||
author, 'kitty', 'Cross-platform, fast, feature-rich, GPU based terminal',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
# }}}
|
||||
@@ -184,7 +187,7 @@ texinfo_documents = [
|
||||
|
||||
# GitHub linking inline roles {{{
|
||||
|
||||
def num_role(which, name, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
def num_role(which: str, name: str, rawtext: str, text: str, lineno: int, inliner: Any, options: Any = {}, content: Any = []) -> Tuple[List, List]:
|
||||
' Link to a github issue '
|
||||
try:
|
||||
issue_num = int(text)
|
||||
@@ -202,10 +205,11 @@ def num_role(which, name, rawtext, text, lineno, inliner, options={}, content=[]
|
||||
return [node], []
|
||||
|
||||
|
||||
def commit_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
def commit_role(name: str, rawtext: str, text: str, lineno: int, inliner: Any, options: Any = {}, content: Any = []) -> Tuple[List, List]:
|
||||
' Link to a github commit '
|
||||
try:
|
||||
commit_id = subprocess.check_output(f'git rev-list --max-count=1 --skip=# {text}'.split()).decode('utf-8').strip()
|
||||
commit_id = subprocess.check_output(
|
||||
f'git rev-list --max-count=1 --skip=# {text}'.split()).decode('utf-8').strip()
|
||||
except Exception:
|
||||
msg = inliner.reporter.error(
|
||||
f'GitHub commit id "{text}" not recognized.', line=lineno)
|
||||
@@ -213,21 +217,22 @@ def commit_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
return [prb], [msg]
|
||||
url = f'https://github.com/kovidgoyal/kitty/commit/{commit_id}'
|
||||
set_classes(options)
|
||||
short_id = subprocess.check_output(f'git rev-list --max-count=1 --abbrev-commit --skip=# {commit_id}'.split()).decode('utf-8').strip()
|
||||
short_id = subprocess.check_output(
|
||||
f'git rev-list --max-count=1 --abbrev-commit --skip=# {commit_id}'.split()).decode('utf-8').strip()
|
||||
node = nodes.reference(rawtext, f'commit: {short_id}', refuri=url, **options)
|
||||
return [node], []
|
||||
# }}}
|
||||
|
||||
|
||||
# Sidebar ToC {{{
|
||||
def create_toc(app, pagename):
|
||||
def create_toc(app: Any, pagename: str) -> Optional[Any]:
|
||||
tt = TocTree(app.env)
|
||||
toctree = tt.get_toc_for(pagename, app.builder)
|
||||
if toctree is not None:
|
||||
subtree = toctree[toctree.first_child_matching_class(nodes.list_item)]
|
||||
bl = subtree.first_child_matching_class(nodes.bullet_list)
|
||||
if bl is None:
|
||||
return # Empty ToC
|
||||
return None # Empty ToC
|
||||
subtree = subtree[bl]
|
||||
# for li in subtree.traverse(nodes.list_item):
|
||||
# modify_li(li)
|
||||
@@ -235,14 +240,14 @@ def create_toc(app, pagename):
|
||||
return app.builder.render_partial(subtree)['fragment']
|
||||
|
||||
|
||||
def add_html_context(app, pagename, templatename, context, *args):
|
||||
def add_html_context(app: Any, pagename: str, templatename: str, context: Any, *args: Any) -> None:
|
||||
if 'toc' in context:
|
||||
context['toc'] = create_toc(app, pagename) or context['toc']
|
||||
# }}}
|
||||
|
||||
|
||||
# CLI docs {{{
|
||||
def write_cli_docs(all_kitten_names):
|
||||
def write_cli_docs(all_kitten_names: Iterable[str]) -> None:
|
||||
from kitty.launch import options_spec as launch_options_spec
|
||||
from kitty.cli import option_spec_as_rst
|
||||
with open('generated/launch.rst', 'w') as f:
|
||||
@@ -258,16 +263,17 @@ if you specify a program-to-run you can use the special placeholder
|
||||
f.write(option_spec_as_rst(appname='kitty').replace(
|
||||
'kitty --to', 'kitty @ --to'))
|
||||
as_rst = partial(option_spec_as_rst, heading_char='_')
|
||||
from kitty.remote_control import global_options_spec, cli_msg, cmap, all_commands
|
||||
from kitty.rc.base import all_command_names, command_for_name
|
||||
from kitty.remote_control import global_options_spec, cli_msg
|
||||
with open('generated/cli-kitty-at.rst', 'w') as f:
|
||||
p = partial(print, file=f)
|
||||
p('kitty @\n' + '-' * 80)
|
||||
p('.. program::', 'kitty @')
|
||||
p('\n\n' + as_rst(
|
||||
global_options_spec, message=cli_msg, usage='command ...', appname='kitty @'))
|
||||
from kitty.cmds import cli_params_for
|
||||
for cmd_name in all_commands:
|
||||
func = cmap[cmd_name]
|
||||
from kitty.rc.base import cli_params_for
|
||||
for cmd_name in sorted(all_command_names()):
|
||||
func = command_for_name(cmd_name)
|
||||
p(f'.. _at_{func.name}:\n')
|
||||
p('kitty @', func.name + '\n' + '-' * 120)
|
||||
p('.. program::', 'kitty @', func.name)
|
||||
@@ -286,14 +292,14 @@ if you specify a program-to-run you can use the special placeholder
|
||||
# }}}
|
||||
|
||||
|
||||
def write_remote_control_protocol_docs(): # {{{
|
||||
from kitty.cmds import cmap
|
||||
def write_remote_control_protocol_docs() -> None: # {{{
|
||||
from kitty.rc.base import all_command_names, command_for_name, RemoteCommand
|
||||
field_pat = re.compile(r'\s*([a-zA-Z0-9_+]+)\s*:\s*(.+)')
|
||||
|
||||
def format_cmd(p, name, cmd):
|
||||
def format_cmd(p: Callable, name: str, cmd: RemoteCommand) -> None:
|
||||
p(name)
|
||||
p('-' * 80)
|
||||
lines = cmd.__doc__.strip().splitlines()
|
||||
lines = (cmd.__doc__ or '').strip().splitlines()
|
||||
fields = []
|
||||
for line in lines:
|
||||
m = field_pat.match(line)
|
||||
@@ -317,10 +323,10 @@ def write_remote_control_protocol_docs(): # {{{
|
||||
p(' ', desc), p()
|
||||
p(), p()
|
||||
|
||||
with open(f'generated/rc.rst', 'w') as f:
|
||||
with open('generated/rc.rst', 'w') as f:
|
||||
p = partial(print, file=f)
|
||||
for name in sorted(cmap):
|
||||
cmd = cmap[name]
|
||||
for name in sorted(all_command_names()):
|
||||
cmd = command_for_name(name)
|
||||
if not cmd.__doc__:
|
||||
continue
|
||||
name = name.replace('_', '-')
|
||||
@@ -383,7 +389,7 @@ class SessionLexer(RegexLexer):
|
||||
}
|
||||
|
||||
|
||||
def link_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
def link_role(name: str, rawtext: str, text: str, lineno: int, inliner: Any, options: Any = {}, content: Any = []) -> Tuple[List, List]:
|
||||
m = re.match(r'(.+)\s+<(.+?)>', text)
|
||||
if m is None:
|
||||
msg = inliner.reporter.error(f'link "{text}" not recognized', line=lineno)
|
||||
@@ -395,24 +401,24 @@ def link_role(name, rawtext, text, lineno, inliner, options={}, content=[]):
|
||||
return [node], []
|
||||
|
||||
|
||||
def expand_opt_references(conf_name, text):
|
||||
def expand_opt_references(conf_name: str, text: str) -> str:
|
||||
conf_name += '.'
|
||||
|
||||
def expand(m):
|
||||
def expand(m: Match) -> str:
|
||||
ref = m.group(1)
|
||||
if '<' not in ref and '.' not in ref:
|
||||
full_ref = conf_name + ref
|
||||
return ':opt:`{} <{}>`'.format(ref, full_ref)
|
||||
return m.group()
|
||||
return str(m.group())
|
||||
|
||||
return re.sub(r':opt:`(.+?)`', expand, text)
|
||||
|
||||
|
||||
opt_aliases = {}
|
||||
shortcut_slugs = {}
|
||||
opt_aliases: Dict[str, str] = {}
|
||||
shortcut_slugs: Dict[str, Tuple[str, str]] = {}
|
||||
|
||||
|
||||
def parse_opt_node(env, sig, signode):
|
||||
def parse_opt_node(env: Any, sig: str, signode: Any) -> str:
|
||||
"""Transform an option description into RST nodes."""
|
||||
count = 0
|
||||
firstname = ''
|
||||
@@ -434,22 +440,22 @@ def parse_opt_node(env, sig, signode):
|
||||
return firstname
|
||||
|
||||
|
||||
def parse_shortcut_node(env, sig, signode):
|
||||
def parse_shortcut_node(env: Any, sig: str, signode: Any) -> str:
|
||||
"""Transform a shortcut description into RST nodes."""
|
||||
conf_name, text = sig.split('.', 1)
|
||||
signode += addnodes.desc_name(text, text)
|
||||
return sig
|
||||
|
||||
|
||||
def render_conf(conf_name, all_options):
|
||||
from kitty.conf.definition import merged_opts, Option
|
||||
def render_conf(conf_name: str, all_options: Iterable[Union['Option', Sequence['Shortcut']]]) -> str:
|
||||
from kitty.conf.definition import merged_opts, Option, Group
|
||||
ans = ['.. default-domain:: conf', '']
|
||||
a = ans.append
|
||||
current_group = None
|
||||
all_options = list(all_options)
|
||||
current_group: Optional[Group] = None
|
||||
all_options_ = list(all_options)
|
||||
kitty_mod = 'kitty_mod'
|
||||
|
||||
def render_group(group):
|
||||
def render_group(group: Group) -> None:
|
||||
a('')
|
||||
a(f'.. _conf-{conf_name}-{group.name}:')
|
||||
a('')
|
||||
@@ -461,11 +467,12 @@ def render_conf(conf_name, all_options):
|
||||
a(group.start_text)
|
||||
a('')
|
||||
|
||||
def handle_group_end(group):
|
||||
def handle_group_end(group: Group) -> None:
|
||||
if group.end_text:
|
||||
assert current_group is not None
|
||||
a(''), a(current_group.end_text)
|
||||
|
||||
def handle_group(new_group, new_group_is_shortcut=False):
|
||||
def handle_group(new_group: Group, new_group_is_shortcut: bool = False) -> None:
|
||||
nonlocal current_group
|
||||
if new_group is not current_group:
|
||||
if current_group:
|
||||
@@ -473,14 +480,14 @@ def render_conf(conf_name, all_options):
|
||||
current_group = new_group
|
||||
render_group(current_group)
|
||||
|
||||
def handle_option(i, opt):
|
||||
def handle_option(i: int, opt: Option) -> None:
|
||||
nonlocal kitty_mod
|
||||
if not opt.long_text or not opt.add_to_docs:
|
||||
return
|
||||
handle_group(opt.group)
|
||||
if opt.name == 'kitty_mod':
|
||||
kitty_mod = opt.defval_as_string
|
||||
mopts = list(merged_opts(all_options, opt, i))
|
||||
mopts = list(merged_opts(all_options_, opt, i))
|
||||
a('.. opt:: ' + ', '.join(conf_name + '.' + mo.name for mo in mopts))
|
||||
a('.. code-block:: conf')
|
||||
a('')
|
||||
@@ -492,7 +499,7 @@ def render_conf(conf_name, all_options):
|
||||
a(expand_opt_references(conf_name, opt.long_text))
|
||||
a('')
|
||||
|
||||
def handle_shortcuts(shortcuts):
|
||||
def handle_shortcuts(shortcuts: Sequence[Shortcut]) -> None:
|
||||
sc = shortcuts[0]
|
||||
handle_group(sc.group, True)
|
||||
sc_text = f'{conf_name}.{sc.short_text}'
|
||||
@@ -510,7 +517,7 @@ def render_conf(conf_name, all_options):
|
||||
a(expand_opt_references(conf_name, sc.long_text))
|
||||
a('')
|
||||
|
||||
for i, opt in enumerate(all_options):
|
||||
for i, opt in enumerate(all_options_):
|
||||
if isinstance(opt, Option):
|
||||
handle_option(i, opt)
|
||||
else:
|
||||
@@ -521,7 +528,7 @@ def render_conf(conf_name, all_options):
|
||||
return '\n'.join(ans)
|
||||
|
||||
|
||||
def process_opt_link(env, refnode, has_explicit_title, title, target):
|
||||
def process_opt_link(env: Any, refnode: Any, has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:
|
||||
conf_name, opt = target.partition('.')[::2]
|
||||
if not opt:
|
||||
conf_name, opt = 'kitty', conf_name
|
||||
@@ -529,7 +536,7 @@ def process_opt_link(env, refnode, has_explicit_title, title, target):
|
||||
return title, opt_aliases.get(full_name, full_name)
|
||||
|
||||
|
||||
def process_shortcut_link(env, refnode, has_explicit_title, title, target):
|
||||
def process_shortcut_link(env: Any, refnode: Any, has_explicit_title: bool, title: str, target: str) -> Tuple[str, str]:
|
||||
conf_name, slug = target.partition('.')[::2]
|
||||
if not slug:
|
||||
conf_name, slug = 'kitty', conf_name
|
||||
@@ -544,8 +551,8 @@ def process_shortcut_link(env, refnode, has_explicit_title, title, target):
|
||||
return title, target
|
||||
|
||||
|
||||
def write_conf_docs(app, all_kitten_names):
|
||||
app.add_lexer('conf', ConfLexer())
|
||||
def write_conf_docs(app: Any, all_kitten_names: Iterable[str]) -> None:
|
||||
app.add_lexer('conf', ConfLexer() if version_info[0] < 3 else ConfLexer)
|
||||
app.add_object_type(
|
||||
'opt', 'opt',
|
||||
indextemplate="pair: %s; Config Setting",
|
||||
@@ -565,7 +572,7 @@ def write_conf_docs(app, all_kitten_names):
|
||||
sc_role.warn_dangling = True
|
||||
sc_role.process_link = process_shortcut_link
|
||||
|
||||
def generate_default_config(all_options, name):
|
||||
def generate_default_config(all_options: Dict[str, Union[Option, Sequence[Shortcut]]], name: str) -> None:
|
||||
from kitty.conf.definition import as_conf_file
|
||||
with open(f'generated/conf-{name}.rst', 'w', encoding='utf-8') as f:
|
||||
print('.. highlight:: conf\n', file=f)
|
||||
@@ -587,14 +594,15 @@ def write_conf_docs(app, all_kitten_names):
|
||||
# }}}
|
||||
|
||||
|
||||
def setup(app):
|
||||
def setup(app: Any) -> None:
|
||||
os.makedirs('generated/conf', exist_ok=True)
|
||||
from kittens.runner import all_kitten_names
|
||||
all_kitten_names = all_kitten_names()
|
||||
write_cli_docs(all_kitten_names)
|
||||
kn = all_kitten_names()
|
||||
write_cli_docs(kn)
|
||||
write_remote_control_protocol_docs()
|
||||
write_conf_docs(app, all_kitten_names)
|
||||
app.add_lexer('session', SessionLexer())
|
||||
write_conf_docs(app, kn)
|
||||
app.add_css_file('custom.css')
|
||||
app.add_lexer('session', SessionLexer() if version_info[0] < 3 else SessionLexer)
|
||||
app.add_role('link', link_role)
|
||||
app.add_role('iss', partial(num_role, 'issues'))
|
||||
app.add_role('pull', partial(num_role, 'pull'))
|
||||
|
||||
127
docs/faq.rst
127
docs/faq.rst
@@ -47,8 +47,19 @@ terminfo files to the server::
|
||||
|
||||
kitty +kitten ssh myserver
|
||||
|
||||
This ssh kitten takes all the same command line arguments
|
||||
as ssh, you can alias it to ssh in your shell's rc files to avoid having to
|
||||
type it each time::
|
||||
|
||||
alias ssh="kitty +kitten ssh"
|
||||
|
||||
If for some reason that does not work (typically because the server is using a
|
||||
non POSIX compliant shell), you can use the following one-liner instead (it
|
||||
non POSIX compliant shell), you can try using it with python instead::
|
||||
|
||||
kitty +kitten ssh use-python myserver
|
||||
|
||||
If that also fails, perhaps because python is not installed on the remote
|
||||
server, use the following one-liner instead (it
|
||||
is slower as it needs to ssh into the server twice, but will work with most
|
||||
servers)::
|
||||
|
||||
@@ -165,11 +176,14 @@ kitty is not able to use my favorite font?
|
||||
|kitty| achieves its stellar performance by caching alpha masks of each rendered
|
||||
character on the GPU, so that every character needs to be rendered only once.
|
||||
This means it is a strictly character cell based display. As such it can use
|
||||
only monospace fonts, since every cell in the grid has to be the same size. If
|
||||
your font is not listed in ``kitty list-fonts`` it means that it is not
|
||||
monospace. On Linux you can list all monospace fonts with::
|
||||
only monospace fonts, since every cell in the grid has to be the same size.
|
||||
Furthermore, it needs fonts to be freely resizable, so it does not support
|
||||
bitmapped fonts.
|
||||
|
||||
fc-list : family spacing | grep -e spacing=100 -e spacing=90
|
||||
If your font is not listed in ``kitty list-fonts`` it means that it is not
|
||||
monospace or is a bitmapped font. On Linux you can list all monospace fonts with::
|
||||
|
||||
fc-list : family spacing outline scalable | grep -e spacing=100 -e spacing=90 | grep -e outline=True | grep -e scalable=True
|
||||
|
||||
Note that the spacing property is calculated by fontconfig based on actual
|
||||
glyph widths in the font. If for some reason fontconfig concludes your favorite
|
||||
@@ -232,3 +246,106 @@ available. The manual way to figure it out is:
|
||||
This shows the hex value, ``13`` in this case.
|
||||
|
||||
3. Use ``\x(hexval)`` in your ``send_text`` command in kitty. So in this example, ``\x13``
|
||||
|
||||
How do I open a new window or tab with the same working directory as the current window?
|
||||
--------------------------------------------------------------------------------------------
|
||||
|
||||
In :file:`kitty.conf` add the following::
|
||||
|
||||
map f1 launch --cwd=current
|
||||
map f2 launch --cwd=current --type=tab
|
||||
|
||||
Pressing :kbd:`F1` will open a new kitty window with the same working directory
|
||||
as the current window. The :doc:`launch command <launch>` is very powerful,
|
||||
explore :doc:`its documentation <launch>`.
|
||||
|
||||
|
||||
Things behave differently when running kitty from system launcher vs. from another terminal?
|
||||
-----------------------------------------------------------------------------------------------
|
||||
|
||||
This will be because of environment variables. When you run kitty from the
|
||||
system launcher, it gets a default set of system environment variables. When
|
||||
you run kitty from another terminal, you are actually running it from a shell,
|
||||
and the shell's rc files will have setup a whole different set of environment
|
||||
variables which kitty will now inherit.
|
||||
|
||||
You need to make sure that the environment variables you define in your shell's
|
||||
rc files are either also defined system wide or via the :opt:`env` directive in
|
||||
:file:`kitty.conf`. Common environment variables that cause issues are those
|
||||
related to localization, such as ``LANG, LC_*`` and loading of configuration
|
||||
files such as ``XDG_*, KITTY_CONFIG_DIRECTORY``.
|
||||
|
||||
To see the environment variables that kitty sees, you can add the following
|
||||
mapping to :file:`kitty.conf`::
|
||||
|
||||
map f1 show_kitty_env_vars
|
||||
|
||||
then pressing :kbd:`F1` will show you the environment variables kitty sees.
|
||||
|
||||
This problem is most common on macOS, as Apple makes it exceedingly difficult to
|
||||
setup environment variables system-wide, so people end up putting them in all
|
||||
sorts of places where they may or may not work.
|
||||
|
||||
|
||||
I am using tmux and have a problem
|
||||
--------------------------------------
|
||||
|
||||
First, terminal multiplexers are `a bad idea
|
||||
<https://github.com/kovidgoyal/kitty/issues/391#issuecomment-638320745>`_, do
|
||||
not use them, if at all possible. kitty contains features that do all of what
|
||||
tmux does, but better, with the exception of remote persistence (:iss:`391`).
|
||||
If you still want to use tmux, read on.
|
||||
|
||||
Image display will not work, see `tmux issue
|
||||
<https://github.com/tmux/tmux/issues/1391>`_.
|
||||
|
||||
If you are using tmux with multiple terminals or you start it under one
|
||||
terminal and then switch to another and these terminals have different TERM
|
||||
variables, tmux will break. You will need to restart it as tmux does not
|
||||
support multiple terminfo definitions.
|
||||
|
||||
Copying to clipboard via OSC 52 will not work, because tmux does not support
|
||||
the extended version of that protocol, you will need to add ``no-append`` to
|
||||
:opt:`clipboard_control` in kitty.conf.
|
||||
|
||||
If you use any of the advanced features that kitty has innovated, such as
|
||||
styled underlines, desktop notifications, extended keyboard support, etc.
|
||||
they may or may not work, depending on the whims of tmux's maintainer, your
|
||||
version of tmux, etc.
|
||||
|
||||
|
||||
I opened and closed a lot of windows/tabs and top shows kitty's memory usage is very high?
|
||||
-------------------------------------------------------------------------------------------
|
||||
|
||||
``top`` is not a good way to measure process memory usage. That is because on
|
||||
modern systems, when allocating memory to a process, the C library functions
|
||||
will typically allocate memory in large blocks, and give the process chunks of
|
||||
these blocks. When the process frees a chunk, the C library will not
|
||||
necessarily release the underlying block back to the OS. So even though the
|
||||
application has released the memory, ``top`` will still claim the process is
|
||||
using it.
|
||||
|
||||
To check for memory leaks, instead use a tool like ``valgrind``. Run::
|
||||
|
||||
PYTHONMALLOC=malloc valgrind --tool=massif kitty
|
||||
|
||||
Now open lots of tabs/windows, generate lots of output using tools like find/yes
|
||||
etc. Then close all but one window. Do some random work for a few seconds in
|
||||
that window, maybe run yes or find again. Then quit kitty and run::
|
||||
|
||||
massif-visualizer massif.out.*
|
||||
|
||||
You will see the allocations graph goes up when you opened the windows, then
|
||||
goes back down when you closed them, indicating there were no memory leaks.
|
||||
|
||||
For those interested, you can get a similar profile out of ``valgrind`` as you get
|
||||
with ``top`` by adding ``--pages-as-heap=yes`` then you will see that memory
|
||||
allocated in malloc is not freed in free. This can be further refined if you
|
||||
use `glibc`` as your C library by setting the environment variable
|
||||
``MALLOC_MMAP_THRESHOLD_=64``. This will cause free to actually free memory
|
||||
allocated in sizes of more than 64 bytes. With this set, memory usage will
|
||||
climb high, then fall when closing windows, but not fall all the way back. The
|
||||
remaining used memory can be investigated using valgrind again, and it will
|
||||
come from arenas in the GPU drivers and the per thread arenas glibc's malloc
|
||||
maintains. These too allocate memory in large blocks and dont release it back
|
||||
to the OS immediately.
|
||||
|
||||
@@ -28,14 +28,21 @@ alpha-blending and text over graphics.
|
||||
:alt: Demo of graphics rendering in kitty
|
||||
:align: center
|
||||
|
||||
Some programs that use the kitty graphics protocol:
|
||||
Some programs and libraries that use the kitty graphics protocol:
|
||||
|
||||
* `termpdf <https://github.com/dsanson/termpdf>`_ - a terminal PDF/DJVU/CBR viewer
|
||||
* `termpdf.py <https://github.com/dsanson/termpdf.py>`_ - a terminal PDF/DJVU/CBR viewer
|
||||
* `ranger <https://github.com/ranger/ranger>`_ - a terminal file manager, with
|
||||
image previews, see this `PR <https://github.com/ranger/ranger/pull/1077>`_
|
||||
* :doc:`kitty-diff <kittens/diff>` - a side-by-side terminal diff program with support for images
|
||||
* `pixcat <https://github.com/mirukana/pixcat>`_ - a third party CLI and python library that wraps the graphics protocol
|
||||
* `neofetch <https://github.com/dylanaraps/neofetch>`_ - A command line system
|
||||
information tool
|
||||
* `viu <https://github.com/atanunq/viu>`_ - a terminal image viewer
|
||||
* `glkitty <https://github.com/michaeljclark/glkitty>`_ - C library to draw OpenGL shaders in the terminal with a glgears demo
|
||||
* `ctx.graphics <https://ctx.graphics/>`_ - Library for drawing graphics
|
||||
* `timg <https://github.com/hzeller/timg>`_ - a terminal image and video viewer
|
||||
* `notcurses <https://github.com/dankamongmen/notcurses>`_ - C library for terminal graphics with bindings for C++, Rust and Python
|
||||
* `rasterm <https://github.com/BourgeoisBear/rasterm>`_ - Go library to display images in the the terminal
|
||||
|
||||
|
||||
.. contents::
|
||||
@@ -53,27 +60,34 @@ In C:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct ttysize ts;
|
||||
ioctl(0, TIOCGWINSZ, &ts);
|
||||
printf("number of columns: %i, number of rows: %i, screen width: %i, screen height: %i\n", sz.ws_col, sz.ws_row, sz.ws_xpixel, sz.ws_ypixel);
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
struct winsize sz;
|
||||
ioctl(0, TIOCGWINSZ, &sz);
|
||||
printf("number of rows: %i, number of columns: %i, screen width: %i, screen height: %i\n", sz.ws_row, sz.ws_col, sz.ws_xpixel, sz.ws_ypixel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
In Python:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import array, fcntl, termios
|
||||
import array, fcntl, sys, termios
|
||||
buf = array.array('H', [0, 0, 0, 0])
|
||||
fcntl.ioctl(sys.stdout, termios.TIOCGWINSZ, buf)
|
||||
print('number of columns: {}, number of rows: {}, screen width: {}, screen height: {}'.format(*buf))
|
||||
print('number of rows: {}, number of columns: {}, screen width: {}, screen height: {}'.format(*buf))
|
||||
|
||||
Note that some terminals return ``0`` for the width and height values. Such
|
||||
terminals should be modified to return the correct values. Examples of
|
||||
terminals that return correct values: ``kitty, xterm``
|
||||
|
||||
You can also use the *CSI t* escape code to get the screen size. Send
|
||||
``<ESC>[14t`` to *stdout* and kitty will reply on *stdin* with
|
||||
``<ESC>[4;<height>;<width>t`` where *height* and *width* are the window size in
|
||||
pixels. This escape code is supported in many terminals, not just kitty.
|
||||
``<ESC>[14t`` to ``STDOUT`` and kitty will reply on ``STDIN`` with
|
||||
``<ESC>[4;<height>;<width>t`` where ``height`` and ``width`` are the window
|
||||
size in pixels. This escape code is supported in many terminals, not just
|
||||
kitty.
|
||||
|
||||
A minimal example
|
||||
------------------
|
||||
@@ -134,6 +148,8 @@ The meaning of the payload is interpreted based on the control data.
|
||||
|
||||
The first step is to transmit the actual image data.
|
||||
|
||||
.. _transferring_pixel_data:
|
||||
|
||||
Transferring pixel data
|
||||
--------------------------
|
||||
|
||||
@@ -153,9 +169,10 @@ of transmitting paletted images.
|
||||
RGB and RGBA data
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In these formats the pixel data is stored directly as 3 or 4 bytes per pixel, respectively.
|
||||
When specifying images in this format, the image dimensions **must** be sent in the control data.
|
||||
For example::
|
||||
In these formats the pixel data is stored directly as 3 or 4 bytes per pixel,
|
||||
respectively. The colors in the data **must** be in the *sRGB color space*. When
|
||||
specifying images in this format, the image dimensions **must** be sent in the
|
||||
control data. For example::
|
||||
|
||||
<ESC>_Gf=24,s=10,v=20;<payload><ESC>\
|
||||
|
||||
@@ -179,16 +196,17 @@ compression, then you must provide the ``S`` key with the size of the PNG data.
|
||||
Compression
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
The client can send compressed image data to the terminal emulator, by specifying the
|
||||
``o`` key. Currently, only zlib based deflate compression is supported, which is specified using
|
||||
``o=z``. For example::
|
||||
The client can send compressed image data to the terminal emulator, by
|
||||
specifying the ``o`` key. Currently, only :rfc:`1950` ZLIB based deflate
|
||||
compression is supported, which is specified using ``o=z``. For example::
|
||||
|
||||
<ESC>_Gf=24,s=10,v=20,o=z;<payload><ESC>\
|
||||
|
||||
This is the same as the example from the RGB data section, except that the
|
||||
payload is now compressed using deflate. The terminal emulator will decompress
|
||||
it before rendering. You can specify compression for any format. The terminal
|
||||
emulator will decompress before interpreting the pixel data.
|
||||
payload is now compressed using deflate (this occurs prior to base64-encoding).
|
||||
The terminal emulator will decompress it before rendering. You can specify
|
||||
compression for any format. The terminal emulator will decompress before
|
||||
interpreting the pixel data.
|
||||
|
||||
|
||||
The transmission medium
|
||||
@@ -254,7 +272,7 @@ sequence of escape codes to the terminal emulator::
|
||||
<ESC>_Gm=0;<encoded pixel data last chunk><ESC>\
|
||||
|
||||
Note that only the first escape code needs to have the full set of control
|
||||
codes such as width, height, format etc. Subsequent chunks must have
|
||||
codes such as width, height, format etc. Subsequent chunks **must** have
|
||||
only the ``m`` key. The client **must** finish sending all chunks for a single image
|
||||
before sending any other graphics related escape codes. Note that the cursor
|
||||
position used to display the image **must** be the position when the final chunk is
|
||||
@@ -262,8 +280,8 @@ received. Finally, terminals must not display anything, until the entire sequenc
|
||||
received and validated.
|
||||
|
||||
|
||||
Detecting available transmission mediums
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Querying support and available transmission mediums
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Since a client has no a-priori knowledge of whether it shares a filesystem/shared memory
|
||||
with the terminal emulator, it can send an id with the control data, using the ``i`` key
|
||||
@@ -289,17 +307,39 @@ use the *query action*, set ``a=q``. Then the terminal emulator will try to load
|
||||
the image and respond with either OK or an error, as above, but it will not
|
||||
replace an existing image with the same id, nor will it store the image.
|
||||
|
||||
While as of May 2020, kitty is the only terminal emulator to support this
|
||||
graphics protocol, we intend that any terminal emulator that wishes to support
|
||||
it can. To check if a terminal emulator supports the graphics protocol the best way
|
||||
is to send the above *query action* followed by a request for the
|
||||
`primary device attributes <https://vt100.net/docs/vt510-rm/DA1.html>`. If you
|
||||
get back an answer for the device attributes without getting back an answer for
|
||||
the *query action* the terminal emulator does not support the graphics
|
||||
protocol.
|
||||
|
||||
This means that terminal emulators that support the graphics protocol, **must**
|
||||
reply to *query actions* immediately without processing other input. Most
|
||||
terminal emulators handle input in a FIFO manner, anyway.
|
||||
|
||||
So for example, you could send::
|
||||
|
||||
<ESC>_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA<ESC>\<ESC>[c
|
||||
|
||||
If you get back a response to the graphics query, the terminal emulator supports
|
||||
the protocol, if you get back a response to the device attributes query without
|
||||
a response to the graphics query, it does not.
|
||||
|
||||
|
||||
Display images on screen
|
||||
-----------------------------
|
||||
|
||||
Every transmitted image can be displayed an arbitrary number of times on the
|
||||
screen, in different locations, using different parts of the source image, as
|
||||
needed. You can either simultaneously transmit and display an image using the
|
||||
action ``a=T``, or first transmit the image with a id, such as ``i=10`` and then display
|
||||
it with ``a=p,i=10`` which will display the previously transmitted image at the current
|
||||
cursor position. When specifying an image id, the terminal emulator will reply with an
|
||||
acknowledgement code, which will be either::
|
||||
needed. Each such display of an image is called a *placement*. You can either
|
||||
simultaneously transmit and display an image using the action ``a=T``, or first
|
||||
transmit the image with a id, such as ``i=10`` and then display it with
|
||||
``a=p,i=10`` which will display the previously transmitted image at the current
|
||||
cursor position. When specifying an image id, the terminal emulator will reply
|
||||
to the placement request with an acknowledgement code, which will be either::
|
||||
|
||||
<ESC>_Gi=<id>;OK<ESC>\
|
||||
|
||||
@@ -312,6 +352,24 @@ scheme described above for querying available transmission media, except that
|
||||
here we are querying if the image with the specified id is available or needs to
|
||||
be re-transmitted.
|
||||
|
||||
Since there can be many placements per image, you can also give placements an
|
||||
id. To do so add the ``p`` key with a number between ``1`` and ``4294967295``.
|
||||
When you specify a placement id, it will be added to the acknowledgement code
|
||||
above. Every placement is uniquely identified by the pair of the ``image id``
|
||||
and the ``placement id``. If you specify a placement id for an image that does
|
||||
not have an id, it will be ignored. An example response::
|
||||
|
||||
<ESC>_Gi=<image id>,p=<placement id>;OK<ESC>\
|
||||
|
||||
If you send two placements with the same ``image id`` and ``placement id`` the
|
||||
second one will replace the first. This can be used to resize or move
|
||||
placements around the screen, without flicker.
|
||||
|
||||
|
||||
.. versionadded:: 0.19.3
|
||||
Support for specifying placement ids (see :doc:`kittens/query_terminal` to query kitty version)
|
||||
|
||||
|
||||
Controlling displayed image layout
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -338,6 +396,19 @@ allows rendering of text on top of images. Negative z-index values below
|
||||
INT32_MIN/2 (-1,073,741,824) will be drawn under cells with non-default background
|
||||
colors.
|
||||
|
||||
.. note:: After placing an image on the screen the cursor must be moved to the
|
||||
right by the number of cols in the image placement rectangle and down by the
|
||||
number of rows in the image placement rectangle. If either of these cause
|
||||
the cursor to leave either the screen or the scroll area, the exact
|
||||
positioning of the cursor is undefined, and up to implementations.
|
||||
The client can ask the terminal emulator to not move the cursor at all
|
||||
by specifying ``C=1`` in the command, which sets the cursor movement policy
|
||||
to no movement for placing the current image.
|
||||
|
||||
.. versionadded:: 0.20.0
|
||||
Support for the C=1 cursor movement policy
|
||||
|
||||
|
||||
Deleting images
|
||||
---------------------
|
||||
|
||||
@@ -354,33 +425,218 @@ scrollback buffer. The values of the ``x`` and ``y`` keys are the same as cursor
|
||||
================= ============
|
||||
Value of ``d`` Meaning
|
||||
================= ============
|
||||
``a`` or ``A`` Delete all images visible on screen
|
||||
``i`` or ``I`` Delete all images with the specified id, specified using the ``i`` key.
|
||||
``c`` or ``C`` Delete all images that intersect with the current cursor position.
|
||||
``p`` or ``P`` Delete all images that intersect a specific cell, the cell is specified using the ``x`` and ``y`` keys
|
||||
``q`` or ``Q`` Delete all images that intersect a specific cell having a specific z-index. The cell and z-index is specified using the ``x``, ``y`` and ``z`` keys.
|
||||
``x`` or ``X`` Delete all images that intersect the specified column, specified using the ``x`` key.
|
||||
``y`` or ``Y`` Delete all images that intersect the specified row, specified using the ``y`` key.
|
||||
``z`` or ``Z`` Delete all images that have the specified z-index, specified using the ``z`` key.
|
||||
``a`` or ``A`` Delete all placements visible on screen
|
||||
``i`` or ``I`` Delete all images with the specified id, specified using the ``i`` key. If you specify a ``p`` key for the placement id as well, then only the placement with the specified image id and placement id will be deleted.
|
||||
``n`` or ``N`` Delete newest image with the specified number, specified using the ``I`` key. If you specify a ``p`` key for the
|
||||
placement id as well, then only the placement with the specified number and placement id will be deleted.
|
||||
``c`` or ``C`` Delete all placements that intersect with the current cursor position.
|
||||
``f`` or ``F`` Delete animation frames.
|
||||
``p`` or ``P`` Delete all placements that intersect a specific cell, the cell is specified using the ``x`` and ``y`` keys
|
||||
``q`` or ``Q`` Delete all placements that intersect a specific cell having a specific z-index. The cell and z-index is specified using the ``x``, ``y`` and ``z`` keys.
|
||||
``x`` or ``X`` Delete all placements that intersect the specified column, specified using the ``x`` key.
|
||||
``y`` or ``Y`` Delete all placements that intersect the specified row, specified using the ``y`` key.
|
||||
``z`` or ``Z`` Delete all placements that have the specified z-index, specified using the ``z`` key.
|
||||
================= ============
|
||||
|
||||
|
||||
Note when all placements for an image have been deleted, the image is also
|
||||
deleted, if the capital letter form above is specified. Also, when the terminal
|
||||
is running out of quota space for new images, existing images without
|
||||
placements will be preferentially deleted.
|
||||
|
||||
Some examples::
|
||||
|
||||
<ESC>_Ga=d<ESC>\ # delete all visible images
|
||||
<ESC>_Ga=d,i=10<ESC>\ # delete the image with id=10
|
||||
<ESC>_Ga=Z,z=-1<ESC>\ # delete the images with z-index -1, also freeing up image data
|
||||
<ESC>_Ga=P,x=3,y=4<ESC>\ # delete all images that intersect the cell at (3, 4)
|
||||
<ESC>_Ga=d<ESC>\ # delete all visible placements
|
||||
<ESC>_Ga=d,d=i,i=10<ESC>\ # delete the image with id=10, without freeing data
|
||||
<ESC>_Ga=d,d=i,i=10,p=7<ESC>\ # delete the image with id=10 and placement id=7, without freeing data
|
||||
<ESC>_Ga=d,d=Z,z=-1<ESC>\ # delete the placements with z-index -1, also freeing up image data
|
||||
<ESC>_Ga=d,d=p,x=3,y=4<ESC>\ # delete all placements that intersect the cell at (3, 4), without freeing data
|
||||
|
||||
|
||||
Suppressing responses from the terminal
|
||||
-------------------------------------------
|
||||
|
||||
If you are using the graphics protocol from a limited client, such as a shell
|
||||
script, it might be useful to avoid having to process responses from the
|
||||
terminal. For this, you can use the ``q`` key. Set it to ``1`` to suppress
|
||||
``OK`` responses and to ``2`` to suppress failure responses.
|
||||
|
||||
.. versionadded:: 0.19.3
|
||||
The ability to suppress responses (see :doc:`kittens/query_terminal` to query kitty version)
|
||||
|
||||
|
||||
Requesting image ids from the terminal
|
||||
-------------------------------------------
|
||||
|
||||
If you are writing a program that is going to share the screen with other
|
||||
programs and you still want to use image ids, it is not possible to know
|
||||
what image ids are free to use. In this case, instead of using the ``i``
|
||||
key to specify and image id use the ``I`` key to specify and image number
|
||||
instead. These numbers are not unique.
|
||||
When creating a new image, even if an existing image has the same number a new
|
||||
one is created. And the terminal will reply with the id of the newly created
|
||||
image. For example, when creating an image with ``I=13``, the terminal will
|
||||
send the response::
|
||||
|
||||
<ESC>_Gi=99,I=13;OK<ESC>\
|
||||
|
||||
Here, the value of ``i`` is the id for the newly created image and the value of
|
||||
``I`` is the same as was sent in the creation command.
|
||||
|
||||
All future commands that refer to images using the image number, such as
|
||||
creating placements or deleting images, will act on only the newest image with
|
||||
that number. This allows the client program to send a bunch of commands dealing
|
||||
with an image by image number without waiting for a response from the terminal
|
||||
with the image id. Once such a response is received, the client program should
|
||||
use the ``i`` key with the image id for all future communication.
|
||||
|
||||
.. note:: Specifying both ``i`` and ``I`` keys in any command is an error. The
|
||||
terminal must reply with an EINVAL error message, unless silenced.
|
||||
|
||||
.. versionadded:: 0.19.3
|
||||
The ability to use image numbers (see :doc:`kittens/query_terminal` to query kitty version)
|
||||
|
||||
|
||||
.. _animation_protocol:
|
||||
|
||||
Animation
|
||||
-------------------------------------------
|
||||
|
||||
.. versionadded:: 0.20.0
|
||||
Animation support (see :doc:`kittens/query_terminal` to query kitty version)
|
||||
|
||||
When designing support for animation, the two main considerations were:
|
||||
|
||||
#. There should be a way for both client and terminal driven animations.
|
||||
Since there is unknown and variable latency between client and terminal,
|
||||
especially over SSH, client driven animations are not sufficient.
|
||||
|
||||
#. Animations often consist of small changes from one frame to the next, the
|
||||
protocol should thus allow transmitting these deltas for efficiency and
|
||||
performance reasons.
|
||||
|
||||
Animation support is added to the protocol by adding two new modes for the
|
||||
``a`` (action) key. A ``f`` mode for transmitting frame data and an ``a`` mode
|
||||
for controlling the animation of an image. Animation proceeds in two steps,
|
||||
first a normal image is created as described earlier. Then animation frames are
|
||||
added to the image to make it into an animation. Since every animation is
|
||||
associated with a single image, all animation escape codes must specify either
|
||||
the ``i`` or ``I`` keys to identify the image being operated on.
|
||||
|
||||
|
||||
Transferring animation frame data
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Transferring animation frame data is very similar to
|
||||
:ref:`transferring_pixel_data` above. The main difference is that the image
|
||||
the frame belongs to must be specified and it is possible to transmit data for
|
||||
only part of a frame, declaring the rest of the frame to be filled in by data
|
||||
from a previous frame, or left blank. To transfer frame data the ``a=f``
|
||||
key must be used in all escape codes.
|
||||
|
||||
First, to transfer a simple frame that has data for the full image area, the
|
||||
escape codes used are exactly the same as for transferring image data, with the
|
||||
addition of: ``a=f,i=<image id>`` or ``a=f,I=<image number>``.
|
||||
|
||||
If the frame has data for only a part of the image, you can specify the
|
||||
rectangle for it using the ``x, y, s, v`` keys, for example::
|
||||
|
||||
x=10,y=5,s=100,v=200 # A 100x200 rectangle with its top left corner at (10, 5)
|
||||
|
||||
Frames are created by composing the transmitted data onto a background canvas.
|
||||
This canvas can be either a single color, or the pixels from a previous frame.
|
||||
The composition can be of two types, either a simple replacement (``X=1``) key
|
||||
or a full alpha blend (the default).
|
||||
|
||||
To use a background color for the canvas, specify the ``Y`` key as a 32-bit
|
||||
RGBA color. For example::
|
||||
|
||||
Y=4278190335 # 0xff0000ff opaque red
|
||||
Y=16711816 # 0x00ff0088 translucent green (alpha=0.53)
|
||||
|
||||
The default background color when none is specified is ``0`` i.e. a black,
|
||||
transparent pixel.
|
||||
|
||||
To use the data from a previous frame, specify the ``c`` key which is a 1-based
|
||||
frame number. Thus ``c=1`` refers to the root frame (the base image data),
|
||||
``c=2`` refers to the second frame and so on.
|
||||
|
||||
If the frame is composed of multiple rectangular blocks, these can be expressed
|
||||
by using the ``r`` key. When specifying the ``r`` key the data for an existing
|
||||
frame is edited. The same composition operation as above happens, but now the
|
||||
background canvas is the existing frame itself. ``r`` is a 1-based index, so
|
||||
``r=1`` is the root frame (base image data), ``r=2`` is the second frame and so
|
||||
on.
|
||||
|
||||
Finally, while transferring frame data, the frame *gap* can also be specified
|
||||
using the ``z`` key. The gap is the number of milliseconds to wait before
|
||||
displaying the next frame when the animation is running. A value of ``z=0`` is
|
||||
ignored, ``z=positive number`` sets the gap to the specified number of
|
||||
milliseconds and ``z=negative number`` creates a *gapless* frame. Gapless
|
||||
frames are not displayed to the user since they are instantly skipped over,
|
||||
however they can be useful as the base data for subsequent frames. For example,
|
||||
for an animation where the background remains the same and a small object or two
|
||||
move.
|
||||
|
||||
Controlling animations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Clients can control animations by using the ``a=a`` key in the escape code sent
|
||||
to the terminal.
|
||||
|
||||
The simplest is client driven animations, where the client transmits the frame
|
||||
data and the also instructs the terminal to make a particular frame the current
|
||||
frame. To change the current frame, use the ``c`` key::
|
||||
|
||||
<ESC>_Ga=a,i=3,c=7<ESC>\
|
||||
|
||||
This will make the seventh frame in the image with id ``3`` the current frame.
|
||||
|
||||
However, client driven animations can be sub-optimal, since the latency between
|
||||
the client and terminal is unknown and variable especially over the network.
|
||||
Also they require the client to remain running for the lifetime of the
|
||||
animation, which is not desirable for cat like utilities.
|
||||
|
||||
Terminal driven animations are achieved by the client specifying *gaps* (time
|
||||
in milliseconds) between frames and instructing the terminal to stop or start
|
||||
the animation.
|
||||
|
||||
The animation state is controlled by the ``s`` key. ``s=1`` stops the
|
||||
animation. ``s=2`` runs the animation, but in *loading* mode, in this mode when
|
||||
reaching the last frame, instead of looping, the terminal will wait for the
|
||||
arrival of more frames. ``s=3`` runs the animation normally, after the last
|
||||
frame, the terminal loops back to the first frame. The number of loops can be
|
||||
controlled by the ``v`` key. ``v=0`` is ignored, ``v=1`` is loop infinitely,
|
||||
and any other positive number is loop ``number - 1`` times. Note that stopping
|
||||
the animation resets the loop counter.
|
||||
|
||||
Finally, the *gap* for frames can be set using the ``z`` key. This can be
|
||||
specified either when the frame is created as part of the transmit escape code
|
||||
or separately using the animation control escape code. The *gap* is the time in
|
||||
milliseconds to wait before displaying the next frame in the animation.
|
||||
For example::
|
||||
|
||||
<ESC>_Ga=a,i=7,r=3,z=48<ESC>\
|
||||
|
||||
This sets the gap for the third frame of the image with id ``7`` to ``48``
|
||||
milliseconds. Note that *gapless* frames are not displayed to the user since
|
||||
the next frame comes immediately, however they can be useful to store base data
|
||||
for subsequent frames, such as in an animation with an object moving against a
|
||||
static background.
|
||||
|
||||
In particular, the first frame or *root frame* is created with the base image
|
||||
data and has no gap, so its gap must be set using this control code.
|
||||
|
||||
Image persistence and storage quotas
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
-----------------------------------------
|
||||
|
||||
In order to avoid *Denial-of-Service* attacks, terminal emulators should have a
|
||||
maximum storage quota for image data. It should allow at least a few full
|
||||
screen images. For example the quota in kitty is 320MB per buffer. When adding
|
||||
a new image, if the total size exceeds the quota, the terminal emulator should
|
||||
delete older images to make space for the new one.
|
||||
delete older images to make space for the new one. In kitty, for animations,
|
||||
the additional frame data is stored on disk and has a separate, larger quota of
|
||||
five times the base quota.
|
||||
|
||||
|
||||
Control data reference
|
||||
@@ -393,7 +649,13 @@ take, and the default value they take when missing. All integers are 32-bit.
|
||||
Key Value Default Description
|
||||
======= ==================== ========= =================
|
||||
``a`` Single character. ``t`` The overall action this graphics command is performing.
|
||||
``(t, T, q, p, d)``
|
||||
``(t, T, q, p, d)`` ``t`` - transmit data, ``T`` - transmit data and display image,
|
||||
``q`` - query terminal, ``p`` - put (display) previous transmitted image,
|
||||
``d`` - delete image, ``f`` - transmit data for animation frames,
|
||||
``a`` - control animation
|
||||
|
||||
``q`` ``0, 1, 2`` ``0`` Suppress responses from the terminal to this graphics command.
|
||||
|
||||
**Keys for image transmission**
|
||||
-----------------------------------------------------------
|
||||
``f`` Positive integer. ``32`` The format in which the image data is sent.
|
||||
@@ -406,26 +668,63 @@ Key Value Default Description
|
||||
``O`` Positive integer. ``0`` The offset from which to read data from a file.
|
||||
``i`` Positive integer.
|
||||
``(0 - 4294967295)`` ``0`` The image id
|
||||
``I`` Positive integer.
|
||||
``(0 - 4294967295)`` ``0`` The image number
|
||||
``p`` Positive integer.
|
||||
``(0 - 4294967295)`` ``0`` The placement id
|
||||
``o`` Single character. ``null`` The type of data compression.
|
||||
``only z``
|
||||
``m`` zero or one ``0`` Whether there is more chunked data available.
|
||||
|
||||
**Keys for image display**
|
||||
-----------------------------------------------------------
|
||||
``x`` Positive integer ``0`` The left edge (in pixels) of the image area to display
|
||||
``y`` Positive integer ``0`` The top edge (in pixels) of the image area to display
|
||||
``w`` Positive integer ``0`` The width (in pixels) of the image area to display. By default, the entire width is used.
|
||||
``w`` Positive integer ``0`` The width (in pixels) of the image area to display. By default, the entire width is used
|
||||
``h`` Positive integer ``0`` The height (in pixels) of the image area to display. By default, the entire height is used
|
||||
``X`` Positive integer ``0`` The x-offset within the first cell at which to start displaying the image
|
||||
``Y`` Positive integer ``0`` The y-offset within the first cell at which to start displaying the image
|
||||
``c`` Positive integer ``0`` The number of columns to display the image over
|
||||
``r`` Positive integer ``0`` The number of rows to display the image over
|
||||
``C`` Positive integer ``0`` Cursor movement policy. ``0`` is the default, to move the cursor to after the image.
|
||||
``1`` is to not move the cursor at all when placing the image.
|
||||
``z`` 32-bit integer ``0`` The *z-index* vertical stacking order of the image
|
||||
|
||||
**Keys for animation frame loading**
|
||||
-----------------------------------------------------------
|
||||
``x`` Positive integer ``0`` The left edge (in pixels) of where the frame data should be updated
|
||||
``y`` Positive integer ``0`` The top edge (in pixels) of where the frame data should be updated
|
||||
``c`` Positive integer ``0`` The 1-based frame number of the frame whose image data serves as the base data
|
||||
when creating a new frame, by default the base data is black, fully transparent pixels
|
||||
``r`` Positive integer ``0`` The 1-based frame number of the frame that is being edited. By default, a new frame is created
|
||||
``z`` 32-bit integer ``0`` The gap (in milliseconds) of this frame from the next one. A value of
|
||||
zero is ignored. Negative values create a *gapless* frame. If not specified,
|
||||
frames have a default gap of ``40ms``. The root frame defaults to zero gap.
|
||||
``X`` Positive integer ``0`` The composition mode for blending pixels when creating a new frame or
|
||||
editing a frame's data. The default is full alpha blending. ``1`` means a
|
||||
simple overwrite.
|
||||
``Y`` Positive integer ``0`` The background color for pixels not
|
||||
specified in the frame data. Must be in 32-bit RGBA format
|
||||
|
||||
|
||||
**Keys for animation control**
|
||||
-----------------------------------------------------------
|
||||
``s`` Positive integer ``0`` ``1`` - stop animation, ``2`` - run animation, but wait for new frames, ``3`` - run animation
|
||||
``r`` Positive integer ``0`` The 1-based frame number of the frame that is being affected
|
||||
``z`` 32-bit integer ``0`` The gap (in milliseconds) of this frame from the next one. A value of
|
||||
zero is ignored. Negative values create a *gapless* frame.
|
||||
``c`` Positive integer ``0`` The 1-based frame number of the frame that should be made the current frame
|
||||
``v`` Positive integer ``0`` The number of loops to play. ``0`` is
|
||||
ignored, ``1`` is play infinite and is the default and larger number
|
||||
means play that number ``-1`` loops
|
||||
|
||||
|
||||
**Keys for deleting images**
|
||||
-----------------------------------------------------------
|
||||
``d`` Single character. ``a`` What to delete.
|
||||
``(a, A, c, C, i,
|
||||
I, p, P, q, Q, x, X,
|
||||
y, Y, z, Z)``.
|
||||
``(a, A, c, C, n, N,
|
||||
i, I, p, P, q, Q, x,
|
||||
X, y, Y, z, Z)``.
|
||||
======= ==================== ========= =================
|
||||
|
||||
|
||||
|
||||
131
docs/index.rst
131
docs/index.rst
@@ -10,10 +10,12 @@ kitty - the fast, featureful, GPU based terminal emulator
|
||||
buttery smooth scrolling. Uses threaded rendering to minimize input latency.
|
||||
|
||||
* Supports all modern terminal features: :doc:`graphics (images)
|
||||
<graphics-protocol>`, unicode, true-color, OpenType ligatures, mouse
|
||||
protocol, focus tracking, `bracketed paste
|
||||
<https://cirw.in/blog/bracketed-paste>`_ and several :doc:`new terminal
|
||||
protocol extensions <protocol-extensions>`.
|
||||
<graphics-protocol>`, unicode, `true-color
|
||||
<https://gist.github.com/XVilka/8346728>`_,
|
||||
OpenType ligatures, mouse protocol, :doc:`hyperlinks <open_actions>`,
|
||||
focus tracking, `bracketed paste <https://cirw.in/blog/bracketed-paste>`_
|
||||
and several :doc:`new terminal protocol extensions
|
||||
<protocol-extensions>`.
|
||||
|
||||
* Supports tiling multiple terminal windows side by side in different
|
||||
:ref:`layouts <layouts>` without needing to use an extra program like tmux
|
||||
@@ -59,13 +61,13 @@ See the :doc:`binary install instructions </binary>`. You can
|
||||
You can also use your favorite package manager to install the |kitty| package.
|
||||
|kitty| packages are available for:
|
||||
`macOS with Homebrew (Cask) <https://formulae.brew.sh/cask/kitty>`_,
|
||||
`macOS and Linux with Nix <https://nixos.org/nixos/packages.html?channel=nixpkgs-unstable&query=kitty>`_,
|
||||
`macOS and Linux with Nix <https://search.nixos.org/packages?channel=unstable&show=kitty&sort=relevance&query=kitty>`_,
|
||||
`Ubuntu <https://launchpad.net/ubuntu/+source/kitty>`_,
|
||||
`Debian <https://packages.debian.org/buster/kitty>`_,
|
||||
`openSUSE <https://build.opensuse.org/package/show/X11:terminals/kitty>`_,
|
||||
`Arch Linux <https://www.archlinux.org/packages/community/x86_64/kitty/>`_,
|
||||
`Gentoo <https://packages.gentoo.org/packages/x11-terms/kitty>`_,
|
||||
`Fedora <https://copr.fedorainfracloud.org/coprs/gagbo/kitty-latest/>`_,
|
||||
`Fedora <https://src.fedoraproject.org/rpms/kitty>`_,
|
||||
`Void Linux <https://github.com/void-linux/void-packages/blob/master/srcpkgs/kitty/template>`_,
|
||||
and `Solus <https://dev.getsol.us/source/kitty/>`_.
|
||||
|
||||
@@ -178,14 +180,25 @@ previously active windows for negative numbers.
|
||||
You can define shortcuts to detach the current window and
|
||||
move it to another tab or another OS window::
|
||||
|
||||
map ctrl+f2 detach_window # moves the window into a new OS window
|
||||
map ctrl+f3 detach_window new-tab # moves the window into a new Tab
|
||||
map ctrl+f4 detach_window ask # asks which tab to move the window into
|
||||
# moves the window into a new OS window
|
||||
map ctrl+f2 detach_window
|
||||
# moves the window into a new Tab
|
||||
map ctrl+f3 detach_window new-tab
|
||||
# asks which tab to move the window into
|
||||
map ctrl+f4 detach_window ask
|
||||
|
||||
Similarly, you can detach the current tab, with::
|
||||
|
||||
map ctrl+f2 detach_tab # moves the tab into a new OS window
|
||||
map ctrl+f4 detach_tab ask # asks which OS Window to move the tab into
|
||||
# moves the tab into a new OS window
|
||||
map ctrl+f2 detach_tab
|
||||
# asks which OS Window to move the tab into
|
||||
map ctrl+f4 detach_tab ask
|
||||
|
||||
Finally, you can define a shortcut to close all windows in a tab other than
|
||||
the currently active window::
|
||||
|
||||
map f9 close_other_windows_in_tab
|
||||
|
||||
|
||||
Other keyboard shortcuts
|
||||
----------------------------------
|
||||
@@ -201,7 +214,7 @@ Decrease font size :sc:`decrease_font_size` (also :kbd:`⌘+-`
|
||||
Restore font size :sc:`reset_font_size` (also :kbd:`⌘+0` on macOS)
|
||||
Toggle fullscreen :sc:`toggle_fullscreen` (also :kbd:`^+⌘+f` on macOS)
|
||||
Toggle maximized :sc:`toggle_maximized`
|
||||
Input unicode character :sc:`input_unicode_character`
|
||||
Input unicode character :sc:`input_unicode_character` (also :kbd:`^+⌘+space` on macOS)
|
||||
Click URL using the keyboard :sc:`open_url`
|
||||
Reset the terminal :sc:`reset_terminal`
|
||||
Pass current selection to program :sc:`pass_selection_to_program`
|
||||
@@ -219,57 +232,26 @@ Reset background opacity :sc:`reset_background_opacity`
|
||||
Layouts
|
||||
----------
|
||||
|
||||
A layout is an arrangement of multiple *windows*. You can create a new window
|
||||
A layout is an arrangement of multiple kitty *windows* inside a top-level OS window. You can create a new window
|
||||
using the :sc:`new_window` key combination.
|
||||
|
||||
Currently, there are five layouts available,
|
||||
Currently, there are six layouts available:
|
||||
|
||||
* **Fat** -- One (or optionally more) windows are shown full width on the top, the rest of the windows are shown side-by-side on the bottom
|
||||
* **Grid** -- All windows are shown in a grid
|
||||
* **Horizontal** -- All windows are shown side-by-side
|
||||
* **Splits** -- Windows arranged in arbitrary patterns created using horizontal and vertical splits
|
||||
* **Stack** -- Only a single maximized window is shown at a time
|
||||
* **Tall** -- One (or optionally more) windows are shown full height on the left, the rest of the windows are shown one below the other on the right
|
||||
* **Vertical** -- All windows are shown one below the other
|
||||
|
||||
You can switch between layouts using the :sc:`next_layout` key combination. You can
|
||||
also create shortcuts to select particular layouts, and choose which layouts
|
||||
you want to enable/disable, see :ref:`conf-kitty-shortcuts.layout` for examples.
|
||||
By default, all layouts are enabled and you can switch between layouts using
|
||||
the :sc:`next_layout` key combination. You can also create shortcuts to select
|
||||
particular layouts, and choose which layouts you want to enable/disable, see
|
||||
:ref:`conf-kitty-shortcuts.layout` for examples. The first layout listed in
|
||||
:opt:`enabled_layouts` becomes the default layout.
|
||||
|
||||
You can resize windows inside layouts. Press :sc:`start_resizing_window` (also :kbd:`⌘+r` on macOS) to
|
||||
enter resizing mode and follow the on-screen instructions. In a given window
|
||||
layout only some operations may be possible for a particular window. For
|
||||
example, in the Tall layout you can make the first window wider/narrower, but
|
||||
not taller/shorter. Note that what you are resizing is actually not a window,
|
||||
but a row/column in the layout, all windows in that row/column will be resized.
|
||||
|
||||
You can also define shortcuts in :file:`kitty.conf` to make the active window
|
||||
wider, narrower, taller, or shorter by mapping to the ``resize_window``
|
||||
action, for example::
|
||||
|
||||
map ctrl+left resize_window narrower
|
||||
map ctrl+right resize_window wider
|
||||
map ctrl+up resize_window taller
|
||||
map ctrl+down resize_window shorter 3
|
||||
|
||||
The ``resize_window`` action has a second, optional argument to control
|
||||
the resizing increment (a positive integer that defaults to 1).
|
||||
|
||||
|
||||
Some layouts take options to control their behavior. For example, the ``fat``
|
||||
and ``tall`` layouts accept the ``bias`` and ``full_size`` options to control
|
||||
how the available space is split up.
|
||||
To specify the option, in :opt:`kitty.conf <enabled_layouts>` use::
|
||||
|
||||
enabled_layouts tall:bias=70;full_size=2
|
||||
|
||||
This will have ``2`` instead of a single tall window, that occupy ``70%``
|
||||
instead of ``50%`` of available width. ``bias`` can be any number between 10
|
||||
and 90.
|
||||
|
||||
Writing a new layout only requires about a hundred lines of code, so if there
|
||||
is some layout you want, take a look at `layout.py
|
||||
<https://github.com/kovidgoyal/kitty/blob/master/kitty/layout.py>`_ and submit
|
||||
a pull request!
|
||||
For more details on the layouts and how to use them see :doc:`layouts`.
|
||||
|
||||
.. _kittens:
|
||||
|
||||
@@ -296,13 +278,30 @@ Some prominent kittens:
|
||||
|
||||
:doc:`Hints <kittens/hints>`
|
||||
Select and open/paste/insert arbitrary text snippets such as URLs,
|
||||
filenames, words, lines, etc from the terminal screen.
|
||||
filenames, words, lines, etc. from the terminal screen.
|
||||
|
||||
|
||||
:doc:`Remote file <kittens/remote_file>`
|
||||
Edit, open, or download remote files over SSH easily, by simply clicking on
|
||||
the filename.
|
||||
|
||||
|
||||
:doc:`Hyperlinked grep <kittens/hyperlinked_grep>`
|
||||
Search your files using `ripgrep <https://github.com/BurntSushi/ripgrep>`_
|
||||
and open the results directly in your favorite editor in the terminal,
|
||||
at the line containing the search result, simply by clicking on the result you want.
|
||||
|
||||
|
||||
:doc:`Broadcast <kittens/broadcast>`
|
||||
Type in one kitty window and have it broadcast to all (or a subset) of
|
||||
other kitty windows.
|
||||
|
||||
|
||||
:doc:`Panel <kittens/panel>`
|
||||
Draw a GPU accelerated dock panel on your desktop showing the output
|
||||
from an arbitrary terminal program.
|
||||
|
||||
|
||||
:doc:`Clipboard <kittens/clipboard>`
|
||||
Copy/paste to the clipboard from shell scripts, even over SSH.
|
||||
|
||||
@@ -346,7 +345,7 @@ For example:
|
||||
launch zsh
|
||||
# Create a window with some environment variables set and run
|
||||
# vim in it
|
||||
launch env FOO=BAR vim
|
||||
launch --env FOO=BAR vim
|
||||
# Set the title for the next window
|
||||
title Chat with x
|
||||
launch irssi --profile x
|
||||
@@ -366,11 +365,17 @@ For example:
|
||||
new_os_window
|
||||
# set new window size to 80x25 cells
|
||||
os_window_size 80c 25c
|
||||
# set the --class for the new OS window
|
||||
os_window_class mywindow
|
||||
launch sh
|
||||
# Make the current window the active (focused) window
|
||||
focus
|
||||
launch emacs
|
||||
|
||||
.. note::
|
||||
The :doc:`launch <launch>` command when used in a session file
|
||||
cannot create new OS windows, or tabs.
|
||||
|
||||
|
||||
Mouse features
|
||||
-------------------
|
||||
@@ -380,11 +385,12 @@ Mouse features
|
||||
* You can triple click to select a line and then drag to select more lines.
|
||||
* You can right click to extend a previous selection.
|
||||
* You can hold down :kbd:`ctrl+alt` and drag with the mouse to select in
|
||||
columns.
|
||||
columns (see also :opt:`rectangle_select_modifiers`).
|
||||
* Selecting text automatically copies it to the primary clipboard (on
|
||||
platforms with a primary clipboard).
|
||||
* You can select text with kitty even when a terminal program has grabbed
|
||||
the mouse by holding down the :kbd:`shift` key.
|
||||
the mouse by holding down the :kbd:`shift` key (see also
|
||||
:opt:`terminal_select_modifiers`).
|
||||
|
||||
|
||||
Font control
|
||||
@@ -422,6 +428,10 @@ arbitrary, command running in a new window, tab or overlay, for example::
|
||||
Would open the scrollback buffer in a new window when you press the :kbd:`F1`
|
||||
key. See :sc:`show_scrollback` for details.
|
||||
|
||||
If you want to use it with an editor such as vim to get more powerful features,
|
||||
you can see tips for doing so, in
|
||||
`this thread <https://github.com/kovidgoyal/kitty/issues/719>`_.
|
||||
|
||||
If you wish to store very large amounts of scrollback to view using the piping or
|
||||
:sc:`show_scrollback` features, you can use the :opt:`scrollback_pager_history_size`
|
||||
option.
|
||||
@@ -458,6 +468,15 @@ The list of Frequently Asked Questions (*FAQ*) is :doc:`available here <faq>`.
|
||||
|
||||
.. _completion:
|
||||
|
||||
|
||||
Cool integrations for kitty with other CLI tools
|
||||
--------------------------------------------------
|
||||
|
||||
kitty provides extremely powerful interfaces such as :doc:`remote-control` and
|
||||
:doc:`kittens/custom` and :doc:`kittens/icat`
|
||||
that allow it to be integrated with other tools seamlessly. For a list of such
|
||||
user created integrations, see: :doc:`integrations`.
|
||||
|
||||
Completion for kitty
|
||||
---------------------------------
|
||||
|
||||
|
||||
149
docs/integrations.rst
Normal file
149
docs/integrations.rst
Normal file
@@ -0,0 +1,149 @@
|
||||
Integrations with other tools
|
||||
================================
|
||||
|
||||
kitty provides extremely powerful interfaces such as :doc:`remote-control` and
|
||||
:doc:`kittens/custom` and :doc:`kittens/icat`
|
||||
that allow it to be integrated with other tools seamlessly.
|
||||
|
||||
|
||||
Image and document viewers
|
||||
----------------------------
|
||||
|
||||
Powered by kitty's :doc:`graphics-protocol` there exist many tools for viewing
|
||||
images and other types of documents directly in your terminal, even over SSH.
|
||||
|
||||
`termpdf.py <https://github.com/dsanson/termpdf.py>`_
|
||||
a terminal PDF/DJVU/CBR viewer
|
||||
|
||||
`mdcat <https://github.com/lunaryorn/mdcat>`_
|
||||
Display markdown files nicely formatted with images in the terminal
|
||||
|
||||
`ranger <https://github.com/ranger/ranger>`_
|
||||
a terminal file manager, with previews of file contents powered by kitty's graphics protocol.
|
||||
|
||||
`nnn <https://github.com/jarun/nnn/>`_
|
||||
another terminal file manager, with previews of file contents powered by kitty's graphics protocol.
|
||||
|
||||
`hunter <https://github.com/rabite0/hunter>`_
|
||||
another terminal file manager, with previews of file contents powered by kitty's graphics protocol.
|
||||
|
||||
`koneko <https://github.com/twenty5151/koneko>`_
|
||||
browse images from the pixiv artist community directly in kitty.
|
||||
|
||||
`viu <https://github.com/atanunq/viu>`_
|
||||
view images in the terminal, similar to kitty's icat.
|
||||
|
||||
`nb <https://github.com/xwmx/nb>`_
|
||||
command line and local web note-taking, bookmarking, archiving, and
|
||||
knowledge base application that uses kitty's graphics protocol for images.
|
||||
|
||||
`w3m <https://github.com/tats/w3m>`_
|
||||
A text mode WWW browser that supports kitty's graphics protocol to display
|
||||
images.
|
||||
|
||||
`timg <https://github.com/hzeller/timg>`_
|
||||
A terminal image and video viewer, that displays static and animated
|
||||
images or plays videos. Fast multi-threaded loading, JPEG exif rotation,
|
||||
grid view and connecting to the webcam make it a versatile terminal utility.
|
||||
|
||||
|
||||
System and data visualisation tools
|
||||
---------------------------------------
|
||||
|
||||
`neofetch <https://github.com/dylanaraps/neofetch>`_
|
||||
A command line system information tool that shows images using kitty's graphics protocol
|
||||
|
||||
`matplotlib <https://github.com/jktr/matplotlib-backend-kitty>`_
|
||||
show matplotlib plots directly in kitty
|
||||
|
||||
`KittyTerminalImages.jl <https://github.com/simonschoelly/KittyTerminalImages.jl>`_
|
||||
show images from Julia directly in kitty
|
||||
|
||||
`gnuplot <http://www.gnuplot.info/>`_
|
||||
a graphing and data visualization tool that can be made to display its
|
||||
output in kitty with the following bash snippet::
|
||||
|
||||
function iplot {
|
||||
cat <<EOF | gnuplot
|
||||
set terminal pngcairo enhanced font 'Fira Sans,10'
|
||||
set autoscale
|
||||
set samples 1000
|
||||
set output '|kitty +kitten icat --stdin yes'
|
||||
set object 1 rectangle from screen 0,0 to screen 1,1 fillcolor rgb"#fdf6e3" behind
|
||||
plot $@
|
||||
set output '/dev/null'
|
||||
EOF
|
||||
}
|
||||
|
||||
Add this to bashrc and then to plot a function, simply do::
|
||||
|
||||
iplot 'sin(x*3)*exp(x*.2)'
|
||||
|
||||
`onefetch <https://github.com/o2sh/onefetch>`_
|
||||
a tool to fetch information about your git repositories
|
||||
|
||||
`wttr.in <https://github.com/chubin/wttr.in>`_
|
||||
a tool to display weather information in your terminal with curl
|
||||
|
||||
`wl-clipboard-manager <https://github.com/maximbaz/wl-clipboard-manager>`_
|
||||
view and manage the system clipboard under Wayland in your kitty terminal
|
||||
|
||||
`dmenu-term <https://github.com/maximbaz/dmenu-term>`_
|
||||
run applications on your system with fuzzy find inside a kitty window
|
||||
|
||||
|
||||
Editor integration
|
||||
-----------------------
|
||||
|
||||
kitty can be integrated into many different terminal editors to add features
|
||||
such a split windows, previews, REPLs etc.
|
||||
|
||||
|
||||
`kakoune <https://kakoune.org/>`_
|
||||
integrates with kitty to use native kitty windows for its windows/panels and REPLs.
|
||||
|
||||
`vim-slime <https://github.com/jpalardy/vim-slime#kitty>`_
|
||||
uses kitty remote control for a Lisp REPL.
|
||||
|
||||
`vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_
|
||||
allows you to navigate seamlessly between vim and kitty splits using a consistent set of hotkeys.
|
||||
|
||||
`vim-test <https://github.com/vim-test/vim-test>`_
|
||||
allows easily running tests in a terminal window
|
||||
|
||||
|
||||
Scrollback manipulation
|
||||
-------------------------
|
||||
|
||||
`kitty-search <https://github.com/trygveaa/kitty-kitten-search>`_
|
||||
Live incremental search of the scrollback buffer.
|
||||
|
||||
`kitty-grab <https://github.com/yurikhan/kitty_grab>`_
|
||||
keyboard based text selection for the kitty scrollback buffer.
|
||||
|
||||
|
||||
|
||||
Miscellaneous
|
||||
------------------
|
||||
|
||||
`kitty-smart-tab <https://github.com/yurikhan/kitty-smart-tab>`_
|
||||
use keys to either control tabs or pass them onto running applications if
|
||||
no tabs are present
|
||||
|
||||
`kitty-smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_
|
||||
use keys to either scroll or pass them onto running applications if
|
||||
no scrollback buffer is present
|
||||
|
||||
`reload keybindings <https://github.com/kovidgoyal/kitty/issues/1292#issuecomment-582388769>`_
|
||||
reload key bindings from :file:`kitty.conf` without needing to restart
|
||||
kitty
|
||||
|
||||
`kitti3 <https://github.com/LandingEllipse/kitti3>`_
|
||||
allow using kitty as a drop-down terminal under the i3 window manager
|
||||
|
||||
`weechat-hints <https://github.com/GermainZ/kitty-weechat-hints>`_
|
||||
URL hints kitten for WeeChat that works without having to use WeeChat's
|
||||
raw-mode.
|
||||
|
||||
`glkitty <https://github.com/michaeljclark/glkitty>`_
|
||||
C library to draw OpenGL shaders in the terminal with a glgears demo
|
||||
@@ -1,128 +0,0 @@
|
||||
Key encoding for extended keyboard protocol
|
||||
===============================================
|
||||
|
||||
See :ref:`extended-key-protocol` for more information and `this table in JSON
|
||||
format <https://github.com/kovidgoyal/kitty/blob/master/key_encoding.json>`_.
|
||||
|
||||
===================== ======================
|
||||
Name Encoded representation
|
||||
0 ``G``
|
||||
1 ``H``
|
||||
2 ``I``
|
||||
3 ``J``
|
||||
4 ``K``
|
||||
5 ``L``
|
||||
6 ``M``
|
||||
7 ``N``
|
||||
8 ``O``
|
||||
9 ``P``
|
||||
A ``S``
|
||||
APOSTROPHE ``B``
|
||||
B ``T``
|
||||
BACKSLASH ``t``
|
||||
BACKSPACE ``1``
|
||||
C ``U``
|
||||
CAPS LOCK ``:``
|
||||
COMMA ``C``
|
||||
D ``V``
|
||||
DELETE ``3``
|
||||
DOWN ``6``
|
||||
E ``W``
|
||||
END ``-``
|
||||
ENTER ``z``
|
||||
EQUAL ``R``
|
||||
ESCAPE ``y``
|
||||
F ``X``
|
||||
F1 ``/``
|
||||
F10 ``]``
|
||||
F11 ``{``
|
||||
F12 ``}``
|
||||
F13 ``@``
|
||||
F14 ``%``
|
||||
F15 ``$``
|
||||
F16 ``#``
|
||||
F17 ``BA``
|
||||
F18 ``BB``
|
||||
F19 ``BC``
|
||||
F2 ``*``
|
||||
F20 ``BD``
|
||||
F21 ``BE``
|
||||
F22 ``BF``
|
||||
F23 ``BG``
|
||||
F24 ``BH``
|
||||
F25 ``BI``
|
||||
F3 ``?``
|
||||
F4 ``&``
|
||||
F5 ``<``
|
||||
F6 ``>``
|
||||
F7 ``(``
|
||||
F8 ``)``
|
||||
F9 ``[``
|
||||
G ``Y``
|
||||
GRAVE ACCENT ``v``
|
||||
H ``Z``
|
||||
HOME ``.``
|
||||
I ``a``
|
||||
INSERT ``2``
|
||||
J ``b``
|
||||
K ``c``
|
||||
KP 0 ``BJ``
|
||||
KP 1 ``BK``
|
||||
KP 2 ``BL``
|
||||
KP 3 ``BM``
|
||||
KP 4 ``BN``
|
||||
KP 5 ``BO``
|
||||
KP 6 ``BP``
|
||||
KP 7 ``BQ``
|
||||
KP 8 ``BR``
|
||||
KP 9 ``BS``
|
||||
KP ADD ``BX``
|
||||
KP DECIMAL ``BT``
|
||||
KP DIVIDE ``BU``
|
||||
KP ENTER ``BY``
|
||||
KP EQUAL ``BZ``
|
||||
KP MULTIPLY ``BV``
|
||||
KP SUBTRACT ``BW``
|
||||
L ``d``
|
||||
LEFT ``5``
|
||||
LEFT ALT ``Bc``
|
||||
LEFT BRACKET ``s``
|
||||
LEFT CONTROL ``Bb``
|
||||
LEFT SHIFT ``Ba``
|
||||
LEFT SUPER ``Bd``
|
||||
M ``e``
|
||||
MINUS ``D``
|
||||
N ``f``
|
||||
NUM LOCK ``=``
|
||||
O ``g``
|
||||
P ``h``
|
||||
PAGE DOWN ``9``
|
||||
PAGE UP ``8``
|
||||
PAUSE ``!``
|
||||
PERIOD ``E``
|
||||
PRINT SCREEN ``^``
|
||||
Q ``i``
|
||||
R ``j``
|
||||
RIGHT ``4``
|
||||
RIGHT ALT ``Bg``
|
||||
RIGHT BRACKET ``u``
|
||||
RIGHT CONTROL ``Bf``
|
||||
RIGHT SHIFT ``Be``
|
||||
RIGHT SUPER ``Bh``
|
||||
S ``k``
|
||||
SCROLL LOCK ``+``
|
||||
SEMICOLON ``Q``
|
||||
SLASH ``F``
|
||||
SPACE ``A``
|
||||
T ``l``
|
||||
TAB ``0``
|
||||
U ``m``
|
||||
UP ``7``
|
||||
V ``n``
|
||||
W ``o``
|
||||
WORLD 1 ``w``
|
||||
WORLD 2 ``x``
|
||||
X ``p``
|
||||
Y ``q``
|
||||
Z ``r``
|
||||
===================== ======================
|
||||
654
docs/keyboard-protocol.rst
Normal file
654
docs/keyboard-protocol.rst
Normal file
@@ -0,0 +1,654 @@
|
||||
A protocol for comprehensive keyboard handling in terminals
|
||||
=================================================================
|
||||
|
||||
There are various problems with the current state of keyboard handling in
|
||||
terminals. They include:
|
||||
|
||||
* No way to use modifiers other than ``ctrl`` and ``alt``
|
||||
|
||||
* No way to reliably use multiple modifier keys, other than, ``shift+alt`` and
|
||||
``ctrl+alt``.
|
||||
|
||||
* Many of the existing escape codes used to encode these events are ambiguous
|
||||
with different key presses mapping to the same escape code.
|
||||
|
||||
* No way to handle different types of keyboard events, such as press, release or repeat
|
||||
|
||||
* No reliable way to distinguish single ``Esc`` key presses from the start of a
|
||||
escape sequence. Currently, client programs use fragile timing related hacks
|
||||
for this, leading to bugs, for example:
|
||||
`neovim #2035 <https://github.com/neovim/neovim/issues/2035>`_.
|
||||
|
||||
To solve these issues and others, kitty has created a new keyboard protocol,
|
||||
that is backward compatible but allows applications to opt-in to support more
|
||||
advanced usages. The protocol is based on initial work in `fixterms
|
||||
<http://www.leonerd.org.uk/hacks/fixterms/>`_, however, it corrects various
|
||||
issues in that proposal, listed at the :ref:`bottom of this document
|
||||
<fixterms_bugs>`. For public discussion of this spec, see :iss:`3248`.
|
||||
|
||||
You can see this protocol with all enhancements in action by running::
|
||||
|
||||
kitty +kitten key_demo
|
||||
|
||||
inside the kitty terminal to report key events.
|
||||
|
||||
.. versionadded:: 0.20.0
|
||||
|
||||
Quickstart
|
||||
---------------
|
||||
|
||||
If you are an application or library developer just interested in using this
|
||||
protocol to make keyboard handling simpler and more robust in your application,
|
||||
without too many changes, do the following:
|
||||
|
||||
#. Emit the escape code ``CSI > 1 u`` at application startup or when entering
|
||||
alternate screen mode
|
||||
#. All key events will now be sent in only a few forms to your application,
|
||||
that are easy to parse unambiguously.
|
||||
#. Emit the escape sequence ``CSI < u`` at application exit or just before
|
||||
leaving alternate screen mode to restore the previously used keyboard mode.
|
||||
|
||||
Key events will all be delivered to your application either as plain UTF-8
|
||||
text, or using the following escape codes, for those keys that do not produce
|
||||
text (``CSI`` is the bytes ``0x1b 0x5b``)::
|
||||
|
||||
CSI number ; modifiers [u~]
|
||||
CSI 1; modifiers [ABCDEFHPQRS]
|
||||
0x0d - for the Enter key
|
||||
0x7f or 0x08 - for Backspace
|
||||
0x09 - for Tab
|
||||
|
||||
The ``number`` in the first form above will be either the Unicode codepoint for a
|
||||
key, such as ``97`` for the :kbd:`a` key, or one of the numbers from the
|
||||
:ref:`functional` table below. The ``modifiers`` optional parameter encodes any
|
||||
modifiers pressed for the key event. The encoding is described in the
|
||||
:ref:`modifiers` section.
|
||||
|
||||
The second form is used for a few functional keys, such as the :kbd:`Home, End,
|
||||
Arrow keys and F1-F4`, they are enumerated in the :ref:`functional` table below.
|
||||
Note that if no modifiers are present the parameters are omitted entirely
|
||||
giving an escape code of the form ``CSI [ABCDEFHPQRS]``.
|
||||
|
||||
If you want support for more advanced features such as repeat and release
|
||||
events, alternate keys for shortcut matching et cetera, these can be turned on
|
||||
using :ref:`progressive_enhancement` as documented in the rest of this
|
||||
specification.
|
||||
|
||||
An overview
|
||||
------------------
|
||||
|
||||
Key events are divided into two types, those that produce text and those that
|
||||
do not. When a key event produces text, the text is sent directly as UTF-8
|
||||
encoded bytes. This is safe as UTF-8 contains no C0 control codes.
|
||||
When the key event does not have text, the key event is encoded as an escape code. In
|
||||
legacy compatibility mode (the default) this uses legacy escape codes, so old terminal
|
||||
applications continue to work. Key events that could not be represented in
|
||||
legacy mode are encoded using a ``CSI u`` escape code, that most terminal
|
||||
programs should just ignore. For more advanced features, such as release/repeat
|
||||
reporting etc., applications can tell the terminal they want this information by
|
||||
sending an escape code to :ref:`progressively enhance <progressive_enhancement>` the data reported for
|
||||
key events.
|
||||
|
||||
The central escape code used to encode key events is::
|
||||
|
||||
CSI unicode-key-code:alternate-key-codes ; modifiers:event-type ; text-as-codepoints u
|
||||
|
||||
Spaces in the above definition are present for clarity and should be ignored.
|
||||
``CSI`` is the bytes ``0x1b 0x5b``. All parameters are decimal numbers. Fields
|
||||
are separated by the semi-colon and sub-fields by the colon. Only the
|
||||
``unicode-key-code`` field is mandatory, everything else is optional. The
|
||||
escape code is terminated by the ``u`` character (the byte ``0x75``).
|
||||
|
||||
|
||||
.. _key_codes:
|
||||
|
||||
Key codes
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The ``unicode-key-code`` above is the Unicode codepoint representing the key, as a
|
||||
decimal number. For example, the :kbd:`A` key is represented as ``97`` which is
|
||||
the unicode code for lowercase ``a``. Note that the codepoint used is *always*
|
||||
the lower-case (or more technically, un-shifted) version of the key. If the
|
||||
user presses, for example, :kbd:`ctrl+shift+a` the escape code would be ``CSI
|
||||
97;modifiers u``. It *must not* by ``CSI 65; modifiers u``.
|
||||
|
||||
If *alternate key reporting* is requested by the program running in the
|
||||
terminal, the terminal can send two additional Unicode codepoints, the
|
||||
*shifted key* and *base layout key*, separated by colons.
|
||||
The shifted key is simply the upper-case version of ``unicode-codepoint``, or
|
||||
more technically, the shifted version. So `a` becomes `A` and so on, based on
|
||||
the current keyboard layout. This is needed to be able to match against a
|
||||
shortcut such as :kbd:`ctrl+plus` which depending on the type of keyboard could
|
||||
be either :kbd:`ctrl+shift+equal` or :kbd:`ctrl+plus`. Note that the shifted
|
||||
key must be present only if shift is also present in the modifiers.
|
||||
|
||||
The *base layout key* is the key corresponding to the physical key in the
|
||||
standard PC-101 key layout. So for example, if the user is using a Cyrillic
|
||||
keyboard with a Cyrillic keyboard layout pressing the :kbd:`ctrl+С` key will
|
||||
be :kbd:`ctrl+c` in the standard layout. So the terminal should send the *base
|
||||
layout key* as ``99`` corresponding to the ``c`` key.
|
||||
|
||||
If only one alternate key is present, it is the *shifted key* if the terminal
|
||||
wants to send only a base layout key but no shifted key, it must use an empty
|
||||
sub-field for the shifted key, like this::
|
||||
|
||||
CSI unicode-key-code::base-layout-key
|
||||
|
||||
|
||||
.. _modifiers:
|
||||
|
||||
Modifiers
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
This protocol supports six modifier keys, :kbd:`shift, alt, ctrl, super, hyper
|
||||
and meta` as well as :kbd:`num_lock and caps_lock`. Here :kbd:`super` is either
|
||||
the *Windows/Linux* key or the *Cmd* key on mac keyboards. :kbd:`hyper` and
|
||||
:kbd:`meta` are typically present only on X11 based systems with special XKB
|
||||
rules. Modifiers are encoded as a bit field with::
|
||||
|
||||
shift 0b1 (1)
|
||||
alt 0b10 (2)
|
||||
ctrl 0b100 (4)
|
||||
super 0b1000 (8)
|
||||
hyper 0b10000 (16)
|
||||
meta 0b100000 (32)
|
||||
caps_lock 0b1000000 (64)
|
||||
num_lock 0b10000000 (128)
|
||||
|
||||
In the escape code, the modifier value is encoded as a decimal number which is
|
||||
``1 + actual modifiers``. So to represent :kbd:`shift` only, the value would be ``1 +
|
||||
1 = 2``, to represent :kbd:`ctrl+shift` the value would be ``1 + 0b101 = 6``
|
||||
and so on. If the modifier field is not present in the escape code, its default
|
||||
value is ``1`` which means no modifiers.
|
||||
|
||||
|
||||
.. _event_types:
|
||||
|
||||
Event types
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
There are three key event types: ``press, repeat and release``. They are
|
||||
reported (if requested ``0b10``) as a sub-field of the modifiers field
|
||||
(separated by a colon). If no modifiers are present, the modifiers field must
|
||||
have the value ``1`` and the event type sub-field the type of event. The
|
||||
``press`` event type has value ``1`` and is the default if no event type sub
|
||||
field is present. The ``repeat`` type is ``2`` and the ``release`` type is
|
||||
``3``. So for example::
|
||||
|
||||
CSI key-code # this is a press event
|
||||
CSI key-code;modifier # this is a press event
|
||||
CSI key-code;modifier:1 # this is a press event
|
||||
CSI key-code;modifier:2 # this is a repeat event
|
||||
CSI key-code;modifier:3 # this is a release event
|
||||
|
||||
|
||||
.. note:: Key events that result in text are reported as plain UTF-8 text, so
|
||||
events are not supported for them, unless the application requests *key
|
||||
report mode*, see below.
|
||||
|
||||
.. _text_as_codepoints:
|
||||
|
||||
Text as code points
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The terminal can optionally send the text associated with key events as a
|
||||
sequence of Unicode code points. This behavior is opt-in by the :ref:`progressive
|
||||
enhancement <progressive_enhancement>` mechanism described below. Some examples::
|
||||
|
||||
shift+a -> CSI 97 ; 2 ; 65 u # The text 'A' is reported as 65
|
||||
option+a -> CSI 97 ; ; 229 u # The text 'å' is reported as 229
|
||||
|
||||
If multiple code points are present, they must be separated by colons.
|
||||
If no known key is associated with the text the key number ``0`` must be used.
|
||||
|
||||
|
||||
Non-Unicode keys
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There are many keys that don't correspond to letters from human languages, and
|
||||
thus aren't represented in Unicode. Think of functional keys, such as
|
||||
:kbd:`Escape, Play, Pause, F1, Home, etc`. These are encoded using Unicode code
|
||||
points from the Private Use Area (``57344 - 63743``). The mapping of key
|
||||
names to code points for these keys is in the
|
||||
:ref:`Functional key definition table below <functional>`.
|
||||
|
||||
|
||||
.. _progressive_enhancement:
|
||||
|
||||
Progressive enhancement
|
||||
--------------------------
|
||||
|
||||
While, in theory, every key event could be completely represented by this
|
||||
protocol and all would be hunk-dory, in reality there is a vast universe of
|
||||
existing terminal programs that expect legacy control codes for key events and
|
||||
that are not likely to ever be updated. To support these, in default mode,
|
||||
the terminal will emit legacy escape codes for compatibility. If a terminal
|
||||
program wants more robust key handling, it can request it from the terminal,
|
||||
via the mechanism described here. Each enhancement is described in detail
|
||||
below. The escape code for requesting enhancements is::
|
||||
|
||||
CSI = flags ; mode u
|
||||
|
||||
Here ``flags`` is a decimal encoded integer to specify a set of bit-flags. The
|
||||
meanings of the flags are given below. The second, ``mode`` parameter is
|
||||
optional (defaulting to ``1``) and specifies how the flags are applied.
|
||||
The value ``1`` means all set bits are set and all unset bits are reset.
|
||||
The value ``2`` means all set bits are set, unset bits are left unchanged.
|
||||
The value ``3`` means all set bits are reset, unset bits are left unchanged.
|
||||
|
||||
.. csv-table:: The progressive enhancement flags
|
||||
:header: "Bit", "Meaning"
|
||||
|
||||
"0b1 (1)", ":ref:`disambiguate`"
|
||||
"0b10 (2)", ":ref:`report_events`"
|
||||
"0b100 (4)", ":ref:`report_alternates`"
|
||||
"0b1000 (8)", ":ref:`report_all_keys`"
|
||||
"0b10000 (16)", ":ref:`report_text`"
|
||||
|
||||
The program running in the terminal can query the terminal for the
|
||||
current values of the flags by sending::
|
||||
|
||||
CSI ? u
|
||||
|
||||
The terminal will reply with::
|
||||
|
||||
CSI ? flags u
|
||||
|
||||
The program can also push/pop the current flags onto a stack in the
|
||||
terminal with::
|
||||
|
||||
CSI > flags u # for push, if flags ommitted default to zero
|
||||
CSI < number u # to pop number entries, defaulting to 1 if unspecified
|
||||
|
||||
Terminals should limit the size of the stack as appropriate, to prevent
|
||||
Denial-of-Service attacks. Terminals must maintain separate stacks for the main
|
||||
and alternate screens. If a pop request is received that empties the stack,
|
||||
all flags are reset. If a push request is received and the stack is full, the
|
||||
oldest entry from the stack must be evicted.
|
||||
|
||||
.. note:: In the interests of interoperation, the XTerm specific sequences
|
||||
`CSI > 4; 1 m` and `CSI > 4; 0 m` are treated as `CSI > 1 u` and `CSI < 1 u`.
|
||||
These codes cause XTerm to use the CSI u encoding for more keys and are therefore
|
||||
treated as similar to the disambiguate progressive enhancement.
|
||||
|
||||
.. _disambiguate:
|
||||
|
||||
Disambiguate escape codes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This type of progressive enhancement (``0b1``) fixes the problem of some legacy key press
|
||||
encodings overlapping with other control codes. For instance, pressing the
|
||||
:kbd:`Esc` key generates the byte ``0x1b`` which also is used to indicate the
|
||||
start of an escape code. Similarly pressing the key :kbd:`alt+[` will generate
|
||||
the bytes used for CSI control codes.
|
||||
|
||||
Turning on this flag will cause the terminal to report the :kbd:`Esc, alt+key,
|
||||
ctrl+key, ctrl+alt+key, shift+alt+key` keys using ``CSI u`` sequences instead
|
||||
of legacy ones. Here key is any ASCII key as described in :ref:`legacy_text`.
|
||||
Additionally, all keypad keys will be reported as separate keys with ``CSI u``
|
||||
encoding, using dedicated numbers from the :ref:`table below <functional>`.
|
||||
|
||||
With this flag turned on, all key events that do not generate text are
|
||||
represented in one of the following two forms::
|
||||
|
||||
CSI number; modifier u
|
||||
CSI 1; modifier [~ABCDEFHPQRS]
|
||||
|
||||
This makes it very easy to parse key events in an application. In particular,
|
||||
:kbd:`ctrl+c` will no longer generate the ``SIGINT`` signal, but instead be
|
||||
delivers as a ``CSI u`` escape code. This has the nice side effect of making it
|
||||
much easier to integrate into the application event loop. The only exceptions
|
||||
are the :kbd:`Enter, Tab and Backspace` keys which still generate the same
|
||||
bytes as in legacy mode this is to allow the user to type and execute commands
|
||||
in the shell such as ``reset`` after a program that sets this mode crashes
|
||||
without clearing it.
|
||||
|
||||
.. _report_events:
|
||||
|
||||
Report event types
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This progressive enhancement (``0b10``) causes the terminal to report key repeat
|
||||
and key release events. Normally only key press events are reported and key
|
||||
repeat events are treated as key press events. See :ref:`event_types` for
|
||||
details on how these are reported.
|
||||
|
||||
.. _report_alternates:
|
||||
|
||||
Report alternate keys
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This progressive enhancement (``0b100``) causes the terminal to report
|
||||
alternate key values in addition to the main value, to aid in shortcut
|
||||
matching. See :ref:`key_codes` for details on how these are reported.
|
||||
|
||||
.. _report_all_keys:
|
||||
|
||||
Report all keys as escape codes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Key events that generate text, such as plain key presses without modifiers,
|
||||
result in just the text being sent, in the legacy protocol. There is no way to
|
||||
be notified of key repeat/release events. These types of events are needed for
|
||||
some applications, such as games (think of movement using the ``WASD`` keys).
|
||||
|
||||
This progressive enhancement (``0b1000``) turns on key reporting even for key
|
||||
events that generate text. When it is enabled, text will not be sent, instead
|
||||
only key events are sent. If the text is needed as well, combine with the
|
||||
Report associated text enhancement below.
|
||||
|
||||
Additionally, with this mode, events for pressing modifier keys are reported.
|
||||
Note that *all* keys are reported as escape codes, including :kbd:`Enter, Tab,
|
||||
Backspace` etc.
|
||||
|
||||
.. _report_text:
|
||||
|
||||
Report associated text
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This progressive enhancement (``0b10000``) causes key events that generate text
|
||||
to be reported as ``CSI u`` escape codes with the text embedded in the escape
|
||||
code. See :ref:`text_as_codepoints` above for details on the mechanism.
|
||||
|
||||
|
||||
.. _detection:
|
||||
|
||||
Detection of support for this protocol
|
||||
------------------------------------------
|
||||
|
||||
An application can query the terminal for support of this protocol by sending
|
||||
the escape code querying for the :ref:`current progressive enhancement
|
||||
<progressive_enhancement>` status
|
||||
followed by request for the `primary device attributes
|
||||
<https://vt100.net/docs/vt510-rm/DA1.html>`. If an answer for the device
|
||||
attributes is received without getting back an answer for the progressive
|
||||
enhancement the terminal does not support this protocol.
|
||||
|
||||
|
||||
Legacy key event encoding
|
||||
--------------------------------
|
||||
|
||||
In the default mode, the terminal uses a legacy encoding for key events. In
|
||||
this encoding, only key press and repeat events are sent and there is no
|
||||
way to distinguish between them. Text is sent directly as UTF-8 bytes.
|
||||
|
||||
Any key events not described in this section are sent using the standard
|
||||
``CSI u`` encoding. This includes keys that are not encodable in the legacy
|
||||
encoding, thereby increasing the space of usable key combinations even without
|
||||
progressive enhancement.
|
||||
|
||||
Legacy functional keys
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
These keys are encoded using three schemes::
|
||||
|
||||
CSI number ; modifier ~
|
||||
CSI 1 ; modifier {ABCDEFHPQRS}
|
||||
SS3 {ABCDEFHPQRS}
|
||||
|
||||
In the above, if there are no modifiers, the modifier parameter is omitted.
|
||||
The modifier value is encoded as described in the :ref:`modifiers` section,
|
||||
above. When the second form is used, the number is always ``1`` and must be
|
||||
omitted if the modifiers field is also absent. The third form becomes the
|
||||
second form when modifiers are present (``SS3 is the bytes 0x1b 0x4f``).
|
||||
|
||||
These sequences must match entries in the terminfo database for maximum
|
||||
compatibility. The table below lists the key, its terminfo entry name and
|
||||
the escape code used for it by kitty. A different terminal would use whatever
|
||||
escape code is present in its terminfo database for the key.
|
||||
Some keys have an alternate representation when the terminal is in *cursor key
|
||||
mode* (the ``smkx/rmkx`` terminfo capabilities). This form is used only in
|
||||
*cursor key mode* and only when no modifiers are present.
|
||||
|
||||
.. csv-table:: Legacy functional encoding
|
||||
:header: "Name", "Terminfo name", "Escape code"
|
||||
|
||||
"INSERT", "kich1", "CSI 2 ~"
|
||||
"DELETE", "kdch1", "CSI 3 ~"
|
||||
"PAGE_UP", "kpp", "CSI 5 ~"
|
||||
"PAGE_DOWN", "knp", "CSI 6 ~"
|
||||
"UP", "cuu1,kcuu1", "CSI A, SS3 A"
|
||||
"DOWN", "cud1,kcud1", "CSI B, SS3 B"
|
||||
"RIGHT", "cuf1,kcuf1", "CSI C, SS3 C"
|
||||
"LEFT", "cub1,kcub1", "CSI D, SS3 D"
|
||||
"HOME", "home,khome", "CSI H, SS3 H"
|
||||
"END", "-,kend", "CSI F, SS3 F"
|
||||
"F1", "kf1", "SS3 P"
|
||||
"F2", "kf2", "SS3 Q"
|
||||
"F3", "kf3", "SS3 R"
|
||||
"F4", "kf4", "SS3 S"
|
||||
"F5", "kf5", "CSI 15 ~"
|
||||
"F6", "kf6", "CSI 17 ~"
|
||||
"F7", "kf7", "CSI 18 ~"
|
||||
"F8", "kf8", "CSI 19 ~"
|
||||
"F9", "kf9", "CSI 20 ~"
|
||||
"F10", "kf10", "CSI 21 ~"
|
||||
"F11", "kf11", "CSI 23 ~"
|
||||
"F12", "kf12", "CSI 24 ~"
|
||||
|
||||
There are a few more functional keys that have special cased legacy encodings.
|
||||
These are present because they are commonly used and for the sake of legacy
|
||||
terminal applications that get confused when seeing CSI u escape codes:
|
||||
|
||||
.. csv-table:: C0 controls
|
||||
:header: "Key", "No mods", "Ctrl", "Alt", "Shift", "Ctrl + Shift", "Alt + Shift", "Ctrl + Alt"
|
||||
|
||||
"Enter", "0xd", "0xd", "0x1b 0xd", "0xd", "0xd", "0x1b 0xd", "0x1b 0xd"
|
||||
"Escape", "0x1b", "0x1b", "0x1b 0x1b", "0x1b", "0x1b", "0x1b 0x1b", "0x1b 0x1b"
|
||||
"Backspace", "0x7f", "0x8", "0x1b 0x7f", "0x7f", "0x8", "0x1b 0x7f", "0x1b 0x8"
|
||||
"Tab", "0x9", "0x9", "0x1b 0x9", "CSI Z", "CSI Z", "0x1b CSI Z", "0x1b 0x9"
|
||||
"Space", "0x20", "0x0", "0x1b 0x20", "0x20", "0x0", "0x1b 0x20", "0x1b 0x0"
|
||||
|
||||
Note that :kbd:`Backspace` and :kbd:`ctrl+Backspace` are swapped in some
|
||||
terminals, this can be detected using the ``kbs`` terminfo property that
|
||||
must correspond to the :kbd:`Backspace` key.
|
||||
|
||||
All keypad keys are reported as their equivalent non-keypad keys. To
|
||||
distinguish these, use the :ref:`disambiguate <disambiguate>` flag.
|
||||
|
||||
.. _legacy_text:
|
||||
|
||||
Legacy text keys
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For legacy compatibility, the keys
|
||||
:kbd:`a-z 0-9 \` - = [ ] \ ; ' , . /` with the modifiers
|
||||
:kbd:`shift, alt, ctrl, shift+alt, ctrl+alt` are output using the
|
||||
following algorithm:
|
||||
|
||||
#. If the :kbd:`alt` key is pressed output the byte for ``ESC (0x1b)``
|
||||
#. If the :kbd:`ctrl` modifier is pressed map the key using the table
|
||||
in :ref:`ctrl_mapping`.
|
||||
#. Otherwise, if the :kbd:`shift` modifier is pressed, output the shifted key,
|
||||
for example, ``A`` for ``a`` and ``$`` for ``4``.
|
||||
#. Otherwise, output the key unmodified
|
||||
|
||||
Additionally, :kbd:`ctrl+space` is output as the NULL byte ``(0x0)``.
|
||||
|
||||
Any other combination of modifiers with these keys is output as the appropriate
|
||||
``CSI u`` escape code.
|
||||
|
||||
.. csv-table:: Example encodings
|
||||
:header: "Key", "Plain", "shift", "alt", "ctrl", "shift+alt", "alt+ctrl", "ctrl+shift"
|
||||
|
||||
"i", "i (105)", "I (73)", "ESC i", ") (41)", "ESC I", "ESC )", "CSI 105; 6 u"
|
||||
"3", "3 (51)", "# (35)", "ESC 3", "3 (51)", "ESC #", "ESC 3", "CSI 51; 6 u"
|
||||
";", "; (59)", ": (58)", "ESC ;", "; (59)", "ESC :", "ESC ;", "CSI 59; 6 u"
|
||||
|
||||
.. note::
|
||||
Many of the legacy escape codes are ambiguous with multiple different key
|
||||
presses yielding the same escape code(s), for example, :kbd:`ctrl+i` is the
|
||||
same as :kbd:`tab`, :kbd:`ctrl+m` is the same as :kbd:`Enter`, :kbd:`ctrl+r`
|
||||
is the same :kbd:`ctrl+shift+r`, etc. To resolve these use the
|
||||
:ref:`disambiguate progressive enhancement <disambiguate>`.
|
||||
|
||||
|
||||
.. _functional:
|
||||
|
||||
Functional key definitions
|
||||
----------------------------
|
||||
|
||||
All numbers are in the Unicode Private Use Area (``57344 - 63743``) except
|
||||
for a handful of keys that use numbers under 32 and 127 (C0 control codes) for legacy
|
||||
compatibility reasons.
|
||||
|
||||
.. {{{
|
||||
.. start functional key table (auto generated by gen-key-constants.py do not edit)
|
||||
|
||||
.. csv-table:: Functional key codes
|
||||
:header: "Name", "CSI", "Name", "CSI"
|
||||
|
||||
"ESCAPE", "``27 u``", "ENTER", "``13 u``"
|
||||
"TAB", "``9 u``", "BACKSPACE", "``127 u``"
|
||||
"INSERT", "``2 ~``", "DELETE", "``3 ~``"
|
||||
"LEFT", "``1 D``", "RIGHT", "``1 C``"
|
||||
"UP", "``1 A``", "DOWN", "``1 B``"
|
||||
"PAGE_UP", "``5 ~``", "PAGE_DOWN", "``6 ~``"
|
||||
"HOME", "``1 H or 7 ~``", "END", "``1 F or 8 ~``"
|
||||
"CAPS_LOCK", "``57358 u``", "SCROLL_LOCK", "``57359 u``"
|
||||
"NUM_LOCK", "``57360 u``", "PRINT_SCREEN", "``57361 u``"
|
||||
"PAUSE", "``57362 u``", "MENU", "``57363 u``"
|
||||
"F1", "``1 P or 11 ~``", "F2", "``1 Q or 12 ~``"
|
||||
"F3", "``1 R or 13 ~``", "F4", "``1 S or 14 ~``"
|
||||
"F5", "``15 ~``", "F6", "``17 ~``"
|
||||
"F7", "``18 ~``", "F8", "``19 ~``"
|
||||
"F9", "``20 ~``", "F10", "``21 ~``"
|
||||
"F11", "``23 ~``", "F12", "``24 ~``"
|
||||
"F13", "``57376 u``", "F14", "``57377 u``"
|
||||
"F15", "``57378 u``", "F16", "``57379 u``"
|
||||
"F17", "``57380 u``", "F18", "``57381 u``"
|
||||
"F19", "``57382 u``", "F20", "``57383 u``"
|
||||
"F21", "``57384 u``", "F22", "``57385 u``"
|
||||
"F23", "``57386 u``", "F24", "``57387 u``"
|
||||
"F25", "``57388 u``", "F26", "``57389 u``"
|
||||
"F27", "``57390 u``", "F28", "``57391 u``"
|
||||
"F29", "``57392 u``", "F30", "``57393 u``"
|
||||
"F31", "``57394 u``", "F32", "``57395 u``"
|
||||
"F33", "``57396 u``", "F34", "``57397 u``"
|
||||
"F35", "``57398 u``", "KP_0", "``57399 u``"
|
||||
"KP_1", "``57400 u``", "KP_2", "``57401 u``"
|
||||
"KP_3", "``57402 u``", "KP_4", "``57403 u``"
|
||||
"KP_5", "``57404 u``", "KP_6", "``57405 u``"
|
||||
"KP_7", "``57406 u``", "KP_8", "``57407 u``"
|
||||
"KP_9", "``57408 u``", "KP_DECIMAL", "``57409 u``"
|
||||
"KP_DIVIDE", "``57410 u``", "KP_MULTIPLY", "``57411 u``"
|
||||
"KP_SUBTRACT", "``57412 u``", "KP_ADD", "``57413 u``"
|
||||
"KP_ENTER", "``57414 u``", "KP_EQUAL", "``57415 u``"
|
||||
"KP_SEPARATOR", "``57416 u``", "KP_LEFT", "``57417 u``"
|
||||
"KP_RIGHT", "``57418 u``", "KP_UP", "``57419 u``"
|
||||
"KP_DOWN", "``57420 u``", "KP_PAGE_UP", "``57421 u``"
|
||||
"KP_PAGE_DOWN", "``57422 u``", "KP_HOME", "``57423 u``"
|
||||
"KP_END", "``57424 u``", "KP_INSERT", "``57425 u``"
|
||||
"KP_DELETE", "``57426 u``", "KP_BEGIN", "``1 E or 57427 ~``"
|
||||
"MEDIA_PLAY", "``57428 u``", "MEDIA_PAUSE", "``57429 u``"
|
||||
"MEDIA_PLAY_PAUSE", "``57430 u``", "MEDIA_REVERSE", "``57431 u``"
|
||||
"MEDIA_STOP", "``57432 u``", "MEDIA_FAST_FORWARD", "``57433 u``"
|
||||
"MEDIA_REWIND", "``57434 u``", "MEDIA_TRACK_NEXT", "``57435 u``"
|
||||
"MEDIA_TRACK_PREVIOUS", "``57436 u``", "MEDIA_RECORD", "``57437 u``"
|
||||
"LOWER_VOLUME", "``57438 u``", "RAISE_VOLUME", "``57439 u``"
|
||||
"MUTE_VOLUME", "``57440 u``", "LEFT_SHIFT", "``57441 u``"
|
||||
"LEFT_CONTROL", "``57442 u``", "LEFT_ALT", "``57443 u``"
|
||||
"LEFT_SUPER", "``57444 u``", "LEFT_HYPER", "``57445 u``"
|
||||
"LEFT_META", "``57446 u``", "RIGHT_SHIFT", "``57447 u``"
|
||||
"RIGHT_CONTROL", "``57448 u``", "RIGHT_ALT", "``57449 u``"
|
||||
"RIGHT_SUPER", "``57450 u``", "RIGHT_HYPER", "``57451 u``"
|
||||
"RIGHT_META", "``57452 u``", "ISO_LEVEL3_SHIFT", "``57453 u``"
|
||||
"ISO_LEVEL5_SHIFT", "``57454 u``"
|
||||
|
||||
.. end functional key table
|
||||
.. }}}
|
||||
|
||||
Note that the escape codes above of the form ``CSI 1 letter`` will omit the
|
||||
``1`` if there are no modifiers, since ``1`` is the default value.
|
||||
|
||||
.. _ctrl_mapping:
|
||||
|
||||
Legacy :kbd:`ctrl` mapping of ASCII keys
|
||||
------------------------------------------
|
||||
|
||||
When the :kbd:`ctrl` key and another key are pressed on the keyboard, terminals
|
||||
map the result *for some keys* to a *C0 control code* i.e. an value from ``0 -
|
||||
31``. This mapping was historically dependent on the layout of hardware
|
||||
terminal keyboards and is not specified anywhere, completely. The best known
|
||||
reference is `Table 3-5 in the VT-100 docs <https://vt100.net/docs/vt100-ug/chapter3.html>`_.
|
||||
|
||||
The table below provides a mapping that is a commonly used superset of the table above.
|
||||
Any ASCII keys not in the table must be left untouched by :kbd:`ctrl`.
|
||||
|
||||
.. {{{
|
||||
.. start ctrl mapping (auto generated by gen-key-constants.py do not edit)
|
||||
.. csv-table:: Emitted bytes when :kbd:`ctrl` is held down and a key is pressed
|
||||
:header: "Key", "Byte", "Key", "Byte", "Key", "Byte"
|
||||
|
||||
"SPC ", "0", "/", "31", "0", "48"
|
||||
"1", "49", "2", "0", "3", "27"
|
||||
"4", "28", "5", "29", "6", "30"
|
||||
"7", "31", "8", "127", "9", "57"
|
||||
"?", "127", "@", "0", "[", "27"
|
||||
"\\", "28", "]", "29", "^", "30"
|
||||
"_", "31", "a", "1", "b", "2"
|
||||
"c", "3", "d", "4", "e", "5"
|
||||
"f", "6", "g", "7", "h", "8"
|
||||
"i", "9", "j", "10", "k", "11"
|
||||
"l", "12", "m", "13", "n", "14"
|
||||
"o", "15", "p", "16", "q", "17"
|
||||
"r", "18", "s", "19", "t", "20"
|
||||
"u", "21", "v", "22", "w", "23"
|
||||
"x", "24", "y", "25", "z", "26"
|
||||
"~", "30"
|
||||
|
||||
.. end ctrl mapping
|
||||
.. }}}
|
||||
|
||||
.. _fixterms_bugs:
|
||||
|
||||
Bugs in fixterms
|
||||
-------------------
|
||||
|
||||
The following is a list of errata in the `original fixterms proposal
|
||||
<http://www.leonerd.org.uk/hacks/fixterms/>`_, corrected in this
|
||||
specification.
|
||||
|
||||
* No way to disambiguate :kbd:`Esc` key presses, other than using 8-bit controls
|
||||
which are undesirable for other reasons
|
||||
|
||||
* Incorrectly claims special keys are sometimes encoded using ``CSI letter`` encodings when it
|
||||
is actually ``SS3 letter`` in all terminals newer than a VT-52, which is
|
||||
pretty much everything.
|
||||
|
||||
* :kbd:`ctrl+shift+tab` should be ``CSI 9 ; 6 u`` not ``CSI 1 ; 5 Z``
|
||||
(shift+tab is not a separate key from tab)
|
||||
|
||||
* No support for the :kbd:`super` modifier.
|
||||
|
||||
* Makes no mention of cursor key mode and how it changes encodings
|
||||
|
||||
* Incorrectly encoding shifted keys when shift modifier is used, for instance,
|
||||
for :kbd:`ctrl+shift+i` is encoded as :kbd:`ctrl+I`.
|
||||
|
||||
* No way to have non-conflicting escape codes for :kbd:`alt+letter,
|
||||
ctrl+letter, ctrl+alt+letter` key presses
|
||||
|
||||
* No way to specify both shifted and unshifted keys for robust shortcut
|
||||
matching (think matching :kbd:`ctrl+shift+equal` and :kbd:`ctrl+plus`)
|
||||
|
||||
* No way to specify alternate layout key. This is useful for keyboard layouts
|
||||
such as Cyrillic where you want the shortcut :kbd:`ctrl+c` to work when
|
||||
pressing the :kbd:`ctrl+С` on the keyboard.
|
||||
|
||||
* No way to report repeat and release key events, only key press events
|
||||
|
||||
* No way to report key events for presses that generate text, useful for
|
||||
gaming. Think of using the :kbd:`WASD` keys to control movement.
|
||||
|
||||
* Only a small subset of all possible functional keys are assigned numbers.
|
||||
|
||||
* Claims the ``CSI u`` escape code has no fixed meaning, but has been used for
|
||||
decades as ``SCORC`` for instance by xterm and ansi.sys and `DECSMBV
|
||||
<https://vt100.net/docs/vt510-rm/DECSMBV.html>`_ by the VT-510 hardware
|
||||
terminal. This doesn't really matter since these uses are for communication
|
||||
to the terminal not from the terminal.
|
||||
|
||||
* Handwaves that :kbd:`ctrl` *tends to* mask with ``0x1f``. In actual fact it
|
||||
does this only for some keys. The action of :kbd:`ctrl` is not specified and
|
||||
varies between terminals, historically because of different keyboard layouts.
|
||||
23
docs/kittens/broadcast.rst
Normal file
23
docs/kittens/broadcast.rst
Normal file
@@ -0,0 +1,23 @@
|
||||
broadcast - type text in all kitty windows
|
||||
==================================================
|
||||
|
||||
The ``broadcast`` kitten can be used to type text simultaneously in
|
||||
all kitty windows (or a subset as desired).
|
||||
|
||||
To use it, simply create a mapping in :file:`kitty.conf` such as::
|
||||
|
||||
map F1 launch --allow-remote-control kitty +kitten broadcast
|
||||
|
||||
Then press the :kbd:`F1` key and whatever you type in the newly created widow
|
||||
will be sent to all kitty windows.
|
||||
|
||||
You can use the options described below to control which windows
|
||||
are selected.
|
||||
|
||||
.. program:: kitty +kitten broadcast
|
||||
|
||||
|
||||
Command Line Interface
|
||||
--------------------------
|
||||
|
||||
.. include:: /generated/cli-kitten-broadcast.rst
|
||||
@@ -20,24 +20,27 @@ your machine).
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def main(args):
|
||||
# this is the main entry point of the kitten, it will be executed in
|
||||
# the overlay window when the kitten is launched
|
||||
answer = input('Enter some text: ')
|
||||
# whatever this function returns will be available in the
|
||||
# handle_result() function
|
||||
return answer
|
||||
from typing import List
|
||||
from kitty.boss import Boss
|
||||
|
||||
def handle_result(args, answer, target_window_id, boss):
|
||||
# get the kitty window into which to paste answer
|
||||
w = boss.window_id_map.get(target_window_id)
|
||||
if w is not None:
|
||||
w.paste(answer)
|
||||
def main(args: List[str]) -> str:
|
||||
# this is the main entry point of the kitten, it will be executed in
|
||||
# the overlay window when the kitten is launched
|
||||
answer = input('Enter some text: ')
|
||||
# whatever this function returns will be available in the
|
||||
# handle_result() function
|
||||
return answer
|
||||
|
||||
def handle_result(args: List[str], answer: str, target_window_id: int, boss: Boss) -> None:
|
||||
# get the kitty window into which to paste answer
|
||||
w = boss.window_id_map.get(target_window_id)
|
||||
if w is not None:
|
||||
w.paste(answer)
|
||||
|
||||
|
||||
Now in :file:`kitty.conf` add the lines::
|
||||
|
||||
map ctrl+k kitten mykitten.py
|
||||
map ctrl+k kitten mykitten.py
|
||||
|
||||
|
||||
Start kitty and press :kbd:`ctrl+k` and you should see the kitten running.
|
||||
@@ -53,12 +56,13 @@ Passing arguments to kittens
|
||||
You can pass arguments to kittens by defining them in the map directive in
|
||||
:file:`kitty.conf`. For example::
|
||||
|
||||
map ctrl+k kitten mykitten.py arg1 arg2
|
||||
map ctrl+k kitten mykitten.py arg1 arg2
|
||||
|
||||
These will be available as the ``args`` parameter in the ``main()`` and
|
||||
``handle_result()`` functions. Note also that the current working directory
|
||||
of the kitten is set to the working directory of whatever program is
|
||||
running in the active kitty window.
|
||||
running in the active kitty window. The special argument ``@selection``
|
||||
is replaced by the currently selected text in the active kitty window.
|
||||
|
||||
|
||||
Passing the contents of the screen to the kitten
|
||||
@@ -70,23 +74,25 @@ function, telling kitty what kind of input your kitten would like. For example:
|
||||
|
||||
.. code-block:: py
|
||||
|
||||
# in main, STDIN is for the kitten process and will contain
|
||||
# the contents of the screen
|
||||
def main(args):
|
||||
return sys.stdin.read()
|
||||
# in main, STDIN is for the kitten process and will contain
|
||||
# the contents of the screen
|
||||
def main(args):
|
||||
return sys.stdin.read()
|
||||
|
||||
# in handle_result, STDIN is for the kitty process itself, rather
|
||||
# than the kitten process and should not be read from.
|
||||
def handle_result(args, stdin_data, target_window_id, boss):
|
||||
pass
|
||||
# in handle_result, STDIN is for the kitty process itself, rather
|
||||
# than the kitten process and should not be read from.
|
||||
from kittens.tui.handler import result_handler
|
||||
@result_handler(type_of_input='text')
|
||||
def handle_result(args, stdin_data, target_window_id, boss):
|
||||
pass
|
||||
|
||||
handle_result.type_of_input = 'text'
|
||||
|
||||
This will send the plain text of the active window to the kitten's
|
||||
:file:`STDIN`. For text with formatting escape codes, use ``ansi``
|
||||
instead. If you want line wrap markers as well, use ``screen-ansi``
|
||||
or just ``screen``. For the scrollback buffer as well, use
|
||||
``history``, ``ansi-history`` or ``screen-history``.
|
||||
``history``, ``ansi-history`` or ``screen-history``. To get
|
||||
the currently selected text, use ``selection``.
|
||||
|
||||
|
||||
Using kittens to script kitty, without any terminal UI
|
||||
@@ -103,23 +109,23 @@ Create a file in the kitty config folder, :file:`~/.config/kitty/zoom_toggle.py`
|
||||
|
||||
.. code-block:: py
|
||||
|
||||
def main(args):
|
||||
pass
|
||||
def main(args):
|
||||
pass
|
||||
|
||||
def handle_result(args, answer, target_window_id, boss):
|
||||
tab = boss.active_tab
|
||||
if tab is not None:
|
||||
if tab.current_layout.name == 'stack':
|
||||
tab.last_used_layout()
|
||||
else:
|
||||
tab.goto_layout('stack')
|
||||
|
||||
handle_result.no_ui = True
|
||||
from kittens.tui.handler import result_handler
|
||||
@result_handler(no_ui=True)
|
||||
def handle_result(args, answer, target_window_id, boss):
|
||||
tab = boss.active_tab
|
||||
if tab is not None:
|
||||
if tab.current_layout.name == 'stack':
|
||||
tab.last_used_layout()
|
||||
else:
|
||||
tab.goto_layout('stack')
|
||||
|
||||
|
||||
Now in kitty.conf add::
|
||||
|
||||
map f11 kitten zoom_toggle.py
|
||||
map f11 kitten zoom_toggle.py
|
||||
|
||||
Pressing :kbd:`F11` will now act as a zoom toggle function. You can get even
|
||||
more fancy, switching the kitty OS window to fullscreen as well as changing the
|
||||
@@ -131,6 +137,35 @@ layout, by simply adding the line::
|
||||
To the ``handle_result()`` function, above.
|
||||
|
||||
|
||||
.. _send_mouse_event:
|
||||
|
||||
Sending mouse events
|
||||
--------------------
|
||||
|
||||
If the program running in a window is receiving mouse events you can simulate
|
||||
those using::
|
||||
|
||||
from kitty.fast_data_types import send_mouse_event
|
||||
send_mouse_event(screen, x, y, button, action, mods)
|
||||
|
||||
``screen`` is the ``screen`` attribute of the window you want to send the event
|
||||
to. ``x`` and ``y`` are the 0-indexed coordinates. ``button`` is a number using
|
||||
the same numbering as X11 (left: ``1``, middle: ``2``, right: ``3``, scroll up:
|
||||
``4``, scroll down: ``5``, scroll left: ``6``, scroll right: ``7``, back:
|
||||
``8``, forward: ``9``). ``action`` is one of ``PRESS``, ``RELEASE``, ``DRAG``
|
||||
or ``MOVE``. ``mods`` is a bitmask of ``GLFW_MOD_{mod}`` where ``{mod}`` is one
|
||||
of ``SHIFT``, ``CONTROL`` or ``ALT``. All the mentioned constants are imported
|
||||
from ``kitty.fast_data_types``.
|
||||
|
||||
For example, to send a left click at position x: 2, y: 3 to the active window::
|
||||
|
||||
from kitty.fast_data_types import send_mouse_event, PRESS
|
||||
send_mouse_event(boss.active_window.screen, 2, 3, 1, PRESS, 0)
|
||||
|
||||
The function will only send the event if the program is receiving events of
|
||||
that type, and will return ``True`` if it sent the event, and ``False`` if not.
|
||||
|
||||
|
||||
Debugging kittens
|
||||
--------------------
|
||||
|
||||
@@ -156,11 +191,15 @@ Kittens created by kitty users
|
||||
---------------------------------------------
|
||||
|
||||
`vim-kitty-navigator <https://github.com/knubie/vim-kitty-navigator>`_
|
||||
Allows you to navigate seamlessly between vim and kitty splits using a consistent set of hotkeys.
|
||||
Allows you to navigate seamlessly between vim and kitty splits using a consistent set of hotkeys.
|
||||
|
||||
`smart-scroll <https://github.com/yurikhan/kitty-smart-scroll>`_
|
||||
Makes the kitty scroll bindings work in full screen applications
|
||||
Makes the kitty scroll bindings work in full screen applications
|
||||
|
||||
`insert password <https://github.com/kovidgoyal/kitty/issues/1222>`_
|
||||
Insert a password from a CLI password manager, taking care to only do it at
|
||||
a password prompt.
|
||||
Insert a password from a CLI password manager, taking care to only do it at
|
||||
a password prompt.
|
||||
|
||||
`weechat-hints <https://github.com/GermainZ/kitty-weechat-hints>`_
|
||||
URL hints kitten for WeeChat that works without having to use WeeChat's
|
||||
raw-mode.
|
||||
|
||||
@@ -23,16 +23,22 @@ like a path or filename followed by a colon and a line number and open
|
||||
the file in vim at the specified line number. The patterns and editor
|
||||
to be used can be modified using options passed to the kitten. For example::
|
||||
|
||||
map ctrl+g kitten hints --type=linenum --linenum-action=tab nvim +{line} +{path}
|
||||
map ctrl+g kitten hints --type=linenum --linenum-action=tab nvim +{line} {path}
|
||||
|
||||
will open the selected file in a new tab inside neovim when you press
|
||||
:kbd:`ctrl+g`.
|
||||
|
||||
Pressing :sc:`open_selected_hyperlink` will open hyperlinks, i.e. a URL
|
||||
that has been marked as such by the program running in the terminal,
|
||||
for example, by ``ls --hyperlink=auto``. You can also :doc:`customize what actions are
|
||||
taken for different types of URLs <../open_actions>`.
|
||||
|
||||
The hints kitten is very powerful to see more detailed help on its various
|
||||
options and modes of operation, see below. You can use these options to
|
||||
create mappings in :file:`kitty.conf` to select various different text
|
||||
snippets. See :sc:`insert_selected_path` for examples.
|
||||
|
||||
|
||||
Completely customizing the matching and actions of the kitten
|
||||
---------------------------------------------------------------
|
||||
|
||||
|
||||
71
docs/kittens/hyperlinked_grep.rst
Normal file
71
docs/kittens/hyperlinked_grep.rst
Normal file
@@ -0,0 +1,71 @@
|
||||
Hyperlinked grep
|
||||
=================
|
||||
|
||||
|
||||
This kitten allows you to search your files using `ripgrep
|
||||
<https://www.google.com/search?q=ripgrep>`_ and open the results
|
||||
directly in your favorite editor in the terminal, at the line containing
|
||||
the search result, simply by clicking on the result you want.
|
||||
|
||||
.. versionadded:: 0.19.0
|
||||
|
||||
To set it up, first create :file:`~/.config/kitty/open-actions.conf` with the
|
||||
following contents:
|
||||
|
||||
.. code:: conf
|
||||
|
||||
# Open any file with a fragment in vim, fragments are generated
|
||||
# by the hyperlink_grep kitten and nothing else so far.
|
||||
protocol file
|
||||
fragment_matches [0-9]+
|
||||
action launch --type=overlay vim +${FRAGMENT} ${FILE_PATH}
|
||||
|
||||
# Open text files without fragments in the editor
|
||||
protocol file
|
||||
mime text/*
|
||||
action launch --type=overlay ${EDITOR} ${FILE_PATH}
|
||||
|
||||
|
||||
Now, run a search with::
|
||||
|
||||
kitty +kitten hyperlinked_grep something
|
||||
|
||||
Hold down the :kbd:`ctrl+shift` keys and click on any of the
|
||||
result lines, to open the file in vim at the matching line. If
|
||||
you use some editor other than vim, you should adjust the
|
||||
:file:`open-actions.conf` file accordingly.
|
||||
|
||||
Finally, add an alias to your shell's rc files to invoke the kitten as ``hg``::
|
||||
|
||||
alias hg='kitty +kitten hyperlinked_grep'
|
||||
|
||||
|
||||
You can now run searches with::
|
||||
|
||||
hg some-search-term
|
||||
|
||||
If you want to enable completion, for the kitten, you can delegate completion
|
||||
to rg. For that, instead of using an alias create a simple wrapper script named
|
||||
:file:`hg` somewhere in your ``PATH``:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/bin/sh
|
||||
exec kitty +kitten hyperlinked_grep "$@"
|
||||
|
||||
Then, for example, for ZSH, add the following to :file:`.zshrc`::
|
||||
|
||||
compdef _rg hg
|
||||
|
||||
To learn more about kitty's powerful framework for customizing URL click
|
||||
actions, :doc:`see here <../open_actions>`.
|
||||
|
||||
Hopefully, someday this functionality will make it into some `upstream grep
|
||||
<https://github.com/BurntSushi/ripgrep/issues/665>`_
|
||||
program directly removing the need for this kitten.
|
||||
|
||||
|
||||
.. note::
|
||||
While you can pass any of ripgrep's comand line options to the kitten and
|
||||
they will be forwarded to rg, do not use options that change the output
|
||||
formatting as the kitten works by parsing the output from ripgrep.
|
||||
@@ -21,6 +21,12 @@ Then you can simply use ``icat image.png`` to view images.
|
||||
`ImageMagick <https://www.imagemagick.org>`_ must be installed for ``icat`` to
|
||||
work.
|
||||
|
||||
.. note::
|
||||
|
||||
kitty's image display protocol may not work when used within a terminal
|
||||
multiplexer such as ``screen`` or ``tmux``, depending on whether the
|
||||
multiplexer has added support for it or not.
|
||||
|
||||
|
||||
.. program:: kitty +kitten icat
|
||||
|
||||
|
||||
21
docs/kittens/query_terminal.rst
Normal file
21
docs/kittens/query_terminal.rst
Normal file
@@ -0,0 +1,21 @@
|
||||
Query terminal
|
||||
=================
|
||||
|
||||
Used to query kitty from terminal programs about version, values of various
|
||||
runtime options controlling its features, etc.
|
||||
|
||||
The querying is done using the (*semi*) standard XTGETTCAP escape sequence
|
||||
pioneered by XTerm, so it works over SSH as well. The downside is that it
|
||||
is slow, since it requires a roundtrip to the terminal emulator and back.
|
||||
|
||||
If you want to do some of the same querying in your terminal program without
|
||||
depending on the kitten, you can do so, by processing the same escape codes.
|
||||
Search `this page <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html>`_
|
||||
for *XTGETTCAP* to see the syntax for the escape code and read the source
|
||||
of this kitten to find the values of the keys for the various queries.
|
||||
|
||||
|
||||
Command Line Interface
|
||||
-------------------------
|
||||
|
||||
.. include:: ../generated/cli-kitten-query_terminal.rst
|
||||
37
docs/kittens/remote_file.rst
Normal file
37
docs/kittens/remote_file.rst
Normal file
@@ -0,0 +1,37 @@
|
||||
Remote files
|
||||
==============
|
||||
|
||||
|kitty| has the ability to easily *Edit*, *Open* or *Download* files
|
||||
from a computer into which you are SSHed. In your SSH session run::
|
||||
|
||||
ls --hyperlink=auto
|
||||
|
||||
Then hold down :kbd:`ctrl+shift` and click the name of the file.
|
||||
|
||||
.. figure:: ../screenshots/remote_file.png
|
||||
:alt: Remote file actions
|
||||
:align: center
|
||||
:scale: 100%
|
||||
|
||||
Remote file actions
|
||||
|
||||
|kitty| will ask you what you want to do with the remote file. You can choose
|
||||
to *Edit* it in which case kitty will download it and open it locally in your
|
||||
``EDITOR``. As you make changes to the file, they are automatically transferred
|
||||
to the remote computer. Note that this happens without needing to install *any*
|
||||
special software on the server, beyond ``ls`` that supports hyperlinks.
|
||||
|
||||
.. versionadded:: 0.19.0
|
||||
|
||||
.. note::
|
||||
Nested SSH sessions are not supported. The kitten will always try to copy
|
||||
remote files from the first SSH host. This is because there is no way for
|
||||
|kitty| to detect and follow a nested SSH session robustly.
|
||||
|
||||
.. note::
|
||||
If you have not setup automatic password-less SSH access, then, when
|
||||
editing starts you will be asked to enter your password just once,
|
||||
thereafter the SSH connection will be re-used.
|
||||
|
||||
Similarly, you can choose to save the file to the local computer or download
|
||||
and open it in its default file handler.
|
||||
@@ -18,10 +18,14 @@ typing a leading period and then the two character index and pressing Enter.
|
||||
The up and down arrow keys can be used to choose the previous and next unicode
|
||||
symbol respectively.
|
||||
|
||||
In :guilabel:`Name` mode you instead type words from the character name and use the arrow
|
||||
keys/tab to select the character from the displayed matches. You can also type
|
||||
a leading period and the index for the match if you don't like to use arrow
|
||||
keys.
|
||||
In :guilabel:`Name` mode you instead type words from the character name and use
|
||||
the arrow keys/tab to select the character from the displayed matches. You can
|
||||
also type a space followed by a period and the index for the match if you don't
|
||||
like to use arrow keys.
|
||||
|
||||
You can switch between modes using either the function keys or by pressing
|
||||
:kbd:`Ctrl+[` and :kbd:`Ctrl+]`.
|
||||
|
||||
|
||||
Command Line Interface
|
||||
-------------------------
|
||||
|
||||
@@ -5,7 +5,7 @@ Launching programs in new windows/tabs
|
||||
|
||||
|
||||
|kitty| has a :code:`launch` action that can be used to run arbitrary programs
|
||||
in news windows/tabs. It can be mapped to user defined shortcuts in kitty.conf.
|
||||
in new windows/tabs. It can be mapped to user defined shortcuts in kitty.conf.
|
||||
It is very powerful and allows sending the contents of
|
||||
the current window to the launched program, as well as many other options.
|
||||
|
||||
@@ -50,6 +50,59 @@ being the top left corner and ``{lines},{columns}`` being the number of rows
|
||||
and columns of the screen.
|
||||
|
||||
|
||||
Special arguments
|
||||
-------------------
|
||||
|
||||
There are a few special placeholder arguments that can be specified as part of
|
||||
the command line. Namely ``@selection`` which is replaced by the current
|
||||
selection and ``@active-kitty-window-id`` which is replaced by the id of the
|
||||
currently active kitty window. For example::
|
||||
|
||||
map f1 launch my-program @active-kitty-window-id
|
||||
|
||||
|
||||
Watching launched windows
|
||||
---------------------------
|
||||
|
||||
The :option:`launch --watcher` option allows you to specify python functions
|
||||
that will be called at specific events, such as when the window is resized or
|
||||
closed. Simply specify the path to a python module that specifies callback
|
||||
functions for the events you are interested in, for example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def on_resize(boss, window, data):
|
||||
# Here data will contain old_geometry and new_geometry
|
||||
|
||||
def on_focus_change(boss, window, data):
|
||||
# Here data kill contain focused
|
||||
|
||||
def on_close(boss, window, data):
|
||||
# called when window is closed, typically when the program running in
|
||||
# it exits.
|
||||
|
||||
|
||||
Every callback is passed a reference to the global ``Boss`` object as well as
|
||||
the ``Window`` object the action is occurring on. The ``data`` object is
|
||||
mapping that contains event dependent data. Some useful methods and attributes
|
||||
for the ``Window`` object are: ``as_text(as_ans=False, add_history=False,
|
||||
add_wrap_markers=False, alternate_screen=False)`` with which you can get the
|
||||
contents of the window and its scrollback buffer. Similarly,
|
||||
``window.child.pid`` is the PID of the processes that was launched
|
||||
in the window and ``window.id`` is the internal kitty ``id`` of the
|
||||
window.
|
||||
|
||||
|
||||
Finding executables
|
||||
-----------------------
|
||||
|
||||
When you specify a command to run as just a name rather than an absolute path,
|
||||
it is searched for in the system-wide ``PATH`` environment variable. Note that
|
||||
this **may not** be the value of ``PATH`` inside a shell, as shell startup scripts
|
||||
often change the value of this variable. If it is not found there, then a
|
||||
system specific list of default paths is searched. If it is still not found,
|
||||
then your shell is run and the value of ``PATH`` inside the shell is used.
|
||||
|
||||
Syntax reference
|
||||
------------------
|
||||
|
||||
|
||||
260
docs/layouts.rst
Normal file
260
docs/layouts.rst
Normal file
@@ -0,0 +1,260 @@
|
||||
Layouts
|
||||
============
|
||||
|
||||
kitty has the ability to define its own windows that can be tiled next to each
|
||||
other in arbitrary arrangements, based on *Layouts*, see below for examples:
|
||||
|
||||
|
||||
.. figure:: screenshots/screenshot.png
|
||||
:alt: Screenshot, showing three programs in the 'Tall' layout
|
||||
:align: center
|
||||
:scale: 100%
|
||||
|
||||
Screenshot, showing vim, tig and git running in |kitty| with the 'Tall' layout
|
||||
|
||||
|
||||
.. figure:: screenshots/splits.png
|
||||
:alt: Screenshot, showing windows in the 'Splits' layout
|
||||
:align: center
|
||||
:scale: 100%
|
||||
|
||||
Screenshot, showing windows with arbitrary arrangement in the 'Splits'
|
||||
layout
|
||||
|
||||
|
||||
There are many different layouts available. They are all enabled by default,
|
||||
you can switch layouts using :sc:`next_layout`. To control which layouts
|
||||
are available use :opt:`enabled_layouts`, the first listed layout becomes
|
||||
the default. Individual layouts and how to use them are described below.
|
||||
|
||||
.. contents::
|
||||
|
||||
|
||||
The Stack Layout
|
||||
------------------
|
||||
|
||||
This is the simplest layout it displays a single window using all available
|
||||
space, other windows are hidden behind it. It has no options::
|
||||
|
||||
enabled_layouts stack
|
||||
|
||||
|
||||
The Tall Layout
|
||||
------------------
|
||||
|
||||
Displays one (or optionally more) full height windows on the left half of the
|
||||
screen. Remaining windows are tiled vertically on the right half of the screen.
|
||||
There are options to control how the screen is split horizontally ``bias``
|
||||
(an integer between ``10`` and ``90``) and options to control how many
|
||||
full-height windows there are ``full_size`` (a positive integer). The
|
||||
``mirrored`` option when set to ``true`` will cause the short windows to be
|
||||
on the left side of the screen instead of the right. The syntax
|
||||
for the options is shown below::
|
||||
|
||||
enabled_layouts tall:bias=50;full_size=1;mirrored=false
|
||||
|
||||
┌──────────────┬───────────────┐
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ ├───────────────┤
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ ├───────────────┤
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
└──────────────┴───────────────┘
|
||||
|
||||
In addition, you can map keys to increase or decrease the number of full size
|
||||
windows, for example::
|
||||
|
||||
map ctrl+[ layout_action decrease_num_full_size_windows
|
||||
map ctrl+] layout_action increase_num_full_size_windows
|
||||
|
||||
|
||||
The Fat Layout
|
||||
----------------
|
||||
|
||||
Displays one (or optionally more) full width windows on the top half of the
|
||||
screen. Remaining windows are tiled horizontally on the bottom half of the screen.
|
||||
There are options to control how the screen is split vertically ``bias``
|
||||
(an integer between ``10`` and ``90``) and options to control how many
|
||||
full-height windows there are ``full_size`` (a positive integer). The
|
||||
``mirrored`` option when set to ``true`` will cause the narrow windows to be
|
||||
on the top of the screen instead of the bottom. The syntax for the options is
|
||||
shown below::
|
||||
|
||||
enabled_layouts fat:bias=50;full_size=1;mirrored=false
|
||||
|
||||
┌──────────────────────────────┐
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
├─────────┬──────────┬─────────┤
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
└─────────┴──────────┴─────────┘
|
||||
|
||||
|
||||
The Grid Layout
|
||||
--------------------
|
||||
|
||||
Display windows in a balanced grid with all windows the same size except the
|
||||
last column if there are not enough windows to fill the grid. Has no options::
|
||||
|
||||
enabled_layouts grid
|
||||
|
||||
┌─────────┬──────────┬─────────┐
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
├─────────┼──────────┼─────────┤
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
└─────────┴──────────┴─────────┘
|
||||
|
||||
|
||||
.. _splits_layout:
|
||||
|
||||
The Splits Layout
|
||||
--------------------
|
||||
|
||||
This is the most flexible layout. You can create any arrangement of windows
|
||||
by splitting exiting windows repeatedly. To best use this layout you should
|
||||
define a few extra keybindings in :file:`kitty.conf`::
|
||||
|
||||
map F5 launch --location=hsplit
|
||||
map F6 launch --location=vsplit
|
||||
map F7 layout_action rotate
|
||||
|
||||
map shift+up move_window up
|
||||
map shift+left move_window left
|
||||
map shift+right move_window right
|
||||
map shift+down move_window down
|
||||
|
||||
map ctrl+left neighboring_window left
|
||||
map ctrl+right neighboring_window right
|
||||
map ctrl+up neighboring_window up
|
||||
map ctrl+down neighboring_window down
|
||||
|
||||
Now you can create horizontal and vertical splits by using :kbd:`F5` and
|
||||
:kbd:`F6`. You can move them around using :kbd:`shift+arrow keys`
|
||||
and you can move focus to neighboring windows using :kbd:`ctrl+arrow keys`.
|
||||
You can switch an existing split from horizontal to vertical and vice versa
|
||||
using :kbd:`F7`. Finally, windows can be resized using :ref:`window_resizing`.
|
||||
|
||||
This layout takes one option, ``split_axis`` that controls whether new windows
|
||||
are placed into vertical or horizontal splits, by default::
|
||||
|
||||
enabled_layouts splits:split_axis=horizontal
|
||||
|
||||
┌──────────────┬───────────────┐
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ ├───────┬───────┤
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ ├───────┴───────┤
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
└──────────────┴───────────────┘
|
||||
|
||||
.. versionadded:: 0.17.0
|
||||
The Splits layout
|
||||
|
||||
|
||||
The Horizontal Layout
|
||||
------------------------
|
||||
|
||||
All windows are shown side by side. Has no options::
|
||||
|
||||
enabled_layouts horizontal
|
||||
|
||||
┌─────────┬──────────┬─────────┐
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
│ │ │ │
|
||||
└─────────┴──────────┴─────────┘
|
||||
|
||||
|
||||
The Vertical Layout
|
||||
-----------------------
|
||||
|
||||
All windows are shown one below the other. Has no options::
|
||||
|
||||
enabled_layouts vertical
|
||||
|
||||
┌──────────────────────────────┐
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
├──────────────────────────────┤
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
├──────────────────────────────┤
|
||||
│ │
|
||||
│ │
|
||||
│ │
|
||||
└──────────────────────────────┘
|
||||
|
||||
|
||||
.. _window_resizing:
|
||||
|
||||
Resizing windows
|
||||
------------------
|
||||
|
||||
You can resize windows inside layouts. Press :sc:`start_resizing_window` (also
|
||||
:kbd:`⌘+r` on macOS) to enter resizing mode and follow the on-screen
|
||||
instructions. In a given window layout only some operations may be possible
|
||||
for a particular window. For example, in the Tall layout you can make the first
|
||||
window wider/narrower, but not taller/shorter. Note that what you are resizing
|
||||
is actually not a window, but a row/column in the layout, all windows in that
|
||||
row/column will be resized.
|
||||
|
||||
You can also define shortcuts in :file:`kitty.conf` to make the active window
|
||||
wider, narrower, taller, or shorter by mapping to the ``resize_window``
|
||||
action, for example::
|
||||
|
||||
map ctrl+left resize_window narrower
|
||||
map ctrl+right resize_window wider
|
||||
map ctrl+up resize_window taller
|
||||
map ctrl+down resize_window shorter 3
|
||||
|
||||
The ``resize_window`` action has a second, optional argument to control
|
||||
the resizing increment (a positive integer that defaults to 1).
|
||||
|
||||
|
||||
Some layouts take options to control their behavior. For example, the ``fat``
|
||||
and ``tall`` layouts accept the ``bias`` and ``full_size`` options to control
|
||||
how the available space is split up.
|
||||
To specify the option, in :opt:`kitty.conf <enabled_layouts>` use::
|
||||
|
||||
enabled_layouts tall:bias=70;full_size=2
|
||||
|
||||
This will have ``2`` instead of a single tall window, that occupy ``70%``
|
||||
instead of ``50%`` of available width. ``bias`` can be any number between 10
|
||||
and 90.
|
||||
|
||||
Writing a new layout only requires about two hundred lines of code, so if there
|
||||
is some layout you want, take a look at one of the existing layouts in the
|
||||
`layout <https://github.com/kovidgoyal/kitty/tree/master/kitty/layout>`_
|
||||
package and submit a pull request!
|
||||
@@ -51,7 +51,10 @@ If you want to create markers dynamically rather than pre-defining them in
|
||||
map f2 remove_marker
|
||||
|
||||
Then pressing :kbd:`F1` will allow you to enter the marker definition and set
|
||||
it and pressing :kbd:`F2` will remove the marker.
|
||||
it and pressing :kbd:`F2` will remove the marker. ``create_marker`` accepts
|
||||
the same syntax as ``toggle_marker`` above. Note that while creating
|
||||
markers, the prompt has history so you can easily re-use previous marker
|
||||
expressions.
|
||||
|
||||
You can also use the facilities for :doc:`remote-control` to dynamically
|
||||
add/remove markers.
|
||||
|
||||
95
docs/open_actions.rst
Normal file
95
docs/open_actions.rst
Normal file
@@ -0,0 +1,95 @@
|
||||
Customizing the actions taken when clicking on links
|
||||
======================================================
|
||||
|
||||
|kitty| has support for `terminal hyperlinks <https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda>`_. These
|
||||
are generated by many terminal programs, such as ``ls``, ``gcc``, ``systemd``,
|
||||
``mdcat``, etc. You can customize exactly what happens when clicking on these hyperlinks
|
||||
in |kitty|.
|
||||
|
||||
You can tell kitty to take arbitrarily many, complex actions
|
||||
when a link is clicked. Let us illustrate with some examples, first. Create
|
||||
the file :file:`~/.config/kitty/open-actions.conf` with the following:
|
||||
|
||||
.. code:: conf
|
||||
|
||||
# Open any image in the full kitty window by clicking on it
|
||||
protocol file
|
||||
mime image/*
|
||||
action launch --type=overlay kitty +kitten icat --hold ${FILE_PATH}
|
||||
|
||||
Now, run ``ls --hyperlink=auto`` in kitty and click on the filename of an
|
||||
image, holding down :kbd:`ctrl+shift`. It will be opened over the current
|
||||
window. Press any key to close it.
|
||||
|
||||
Each entry in :file:`open-actions.conf` consists of one or more
|
||||
:ref:`matching_criteria`, such as ``protocol`` and ``mime`` and one or more
|
||||
``action`` entries. In the example above kitty uses the :doc:`launch <launch>`
|
||||
action which can be used to run external programs. Entries are separated by
|
||||
blank lines.
|
||||
|
||||
Actions are very powerful, anything that you can map to a key combination in
|
||||
`kitty.conf` can be used as an action. You can specify more than one action per
|
||||
entry if you like, for example:
|
||||
|
||||
|
||||
.. code:: conf
|
||||
|
||||
# Tail a log file (*.log) in a new OS Window and reduce its font size
|
||||
protocol file
|
||||
ext log
|
||||
action launch --title ${FILE} --type=os-window tail -f ${FILE_PATH}
|
||||
action change_font_size current -2
|
||||
|
||||
|
||||
In the action specification you can expand environment variables, as shown in
|
||||
the examples above. In addition to regular environment variables, there are
|
||||
some special variables, documented below:
|
||||
|
||||
``URL``
|
||||
The full URL being opened
|
||||
|
||||
``FILE_PATH``
|
||||
The path portion of the URL (unquoted)
|
||||
|
||||
``FILE``
|
||||
The file portion of the path of the URL (unquoted)
|
||||
|
||||
``FRAGMENT``
|
||||
The fragment (unquoted), if any of the URL or the empty string.
|
||||
|
||||
.. _matching_criteria:
|
||||
|
||||
Matching criteria
|
||||
------------------
|
||||
|
||||
An entry in :file:`open-actions.conf` must have one or more matching criteria.
|
||||
URLs that match all criteria for an entry will trigger that entries' actions.
|
||||
Processing stops at the first matching entry, so put more specific matching
|
||||
criteria at the start of the list. Entries in the file are separated by blank
|
||||
lines. The various available criteria are:
|
||||
|
||||
``protocol``
|
||||
A comma separated list of protocols, for example: ``http, https``. If
|
||||
absent, there is no constraint on protocol
|
||||
|
||||
``url``
|
||||
A regular expression that must match against the entire (unquoted) URL
|
||||
|
||||
``fragment_matches``
|
||||
A regular expression that must match against the fragment (part after #) in
|
||||
the URL
|
||||
|
||||
``mime``
|
||||
A comma separated list of MIME types, for example: ``text/*, image/*,
|
||||
application/pdf``. You can add MIME types to kitty by creating the
|
||||
:file:`mime.types` in the kitty configuration directory. Useful if your
|
||||
system MIME database does not have definitions you need. This file is
|
||||
in the standard format of one definition per line, like: ``text/plain rst
|
||||
md``.
|
||||
|
||||
``ext``
|
||||
A comma separated list of file extensions, for example: ``jpeg, tar.gz``
|
||||
|
||||
``file``
|
||||
A shell glob pattern that must match the filename, for example:
|
||||
``image-??.png``
|
||||
@@ -69,91 +69,9 @@ of this protocol to enable drawing of arbitrary raster images in the terminal.
|
||||
Keyboard handling
|
||||
-------------------
|
||||
|
||||
There are various problems with the current state of keyboard handling. They
|
||||
include:
|
||||
|
||||
* No way to use modifiers other than ``Ctrl`` and ``Alt``
|
||||
|
||||
* No way to reliably use multiple modifier keys, other than, ``Shift+Alt``.
|
||||
|
||||
* No way to handle different types of keyboard events, such as press, release or repeat
|
||||
|
||||
* No reliable way to distinguish single ``Esc`` keypresses from the start of a
|
||||
escape sequence. Currently, client programs use fragile timing related hacks
|
||||
for this, leading to bugs, for example:
|
||||
`neovim #2035 <https://github.com/neovim/neovim/issues/2035>`_.
|
||||
|
||||
There are already two distinct keyboard handling modes, *normal mode* and
|
||||
*application mode*. These modes generate different escape sequences for the
|
||||
various special keys (arrow keys, function keys, home/end etc.) Most terminals
|
||||
start out in normal mode, however, most shell programs like ``bash`` switch them to
|
||||
application mode. We propose adding a third mode, named *full mode* that addresses
|
||||
the shortcomings listed above.
|
||||
|
||||
Switching to the new *full mode* is accomplished using the standard private
|
||||
mode DECSET escape sequence::
|
||||
|
||||
<ESC>[?2017h
|
||||
|
||||
and to leave *full mode*, use DECRST::
|
||||
|
||||
<ESC>[?2017l
|
||||
|
||||
The number ``2017`` above is not used for any existing modes, as far as I know.
|
||||
Client programs can query if the terminal emulator is in *full mode* by using
|
||||
the standard `DECRQM <https://vt100.net/docs/vt510-rm/DECRQM.html>`_ escape sequence.
|
||||
|
||||
The new mode works as follows:
|
||||
|
||||
* All printable key presses without modifier keys are sent just as in the
|
||||
*normal mode*. This means all printable ASCII characters and in addition,
|
||||
``Enter``, ``Space`` and ``Backspace``. Also any unicode characters generated by
|
||||
platform specific extended input modes, such as using the ``AltGr`` key. This
|
||||
is done so that client programs that are not aware of this mode can still
|
||||
handle basic text entry, so if a *full mode* using program crashes and does
|
||||
not reset, the user can still issue a ``reset`` command in the shell to restore
|
||||
normal key handling. Note that this includes pressing the ``Shift`` modifier
|
||||
and printable keys. Note that this means there are no repeat and release
|
||||
events for these keys and also for the left and right shift keys.
|
||||
|
||||
* For non printable keys and key combinations including one or more modifiers,
|
||||
an escape sequence encoding the key event is sent. For details on the
|
||||
escape sequence, see below.
|
||||
|
||||
The escape sequence encodes the following properties:
|
||||
|
||||
* Type of event: ``press,repeat,release``
|
||||
* Modifiers pressed at the time of the event
|
||||
* The actual key being pressed
|
||||
|
||||
Schematically::
|
||||
|
||||
<ESC>_K<type><modifiers><key><ESC>\
|
||||
|
||||
Where ``<type>`` is one of ``p`` -- press, ``r`` -- release and ``t`` -- repeat.
|
||||
Modifiers is a bitmask represented as a single base64 digit. Shift -- ``0x1``,
|
||||
Alt -- ``0x2``, Control -- ``0x4`` and Super -- ``0x8``. ``<key>`` is a number
|
||||
(encoded in base85) corresponding to the key pressed. The key name to number
|
||||
mapping is defined in :doc:`this table <key-encoding>`.
|
||||
|
||||
Client programs must ignore events for keys they do not know. The mapping in
|
||||
the above table is stable and will never change, however, new codes might be
|
||||
added to it in the future, for new keys.
|
||||
|
||||
For example::
|
||||
|
||||
<ESC>_KpGp<ESC>\ is <Ctrl>+<Alt>+x (press)
|
||||
<ESC>_KrP8<ESC>\ is <Ctrl>+<Alt>+<Shift>+<Super>+PageUp (release)
|
||||
|
||||
This encoding means each key event is represented by 8 or 9 printable ascii
|
||||
only bytes, for maximum robustness.
|
||||
|
||||
To see the full mode in action, run::
|
||||
|
||||
kitty +kitten key_demo
|
||||
|
||||
Support for this mode is indicated by the ``fullkbd`` boolean capability
|
||||
in the terminfo database, in case querying for it via DECQRM is inconvenient.
|
||||
kitty has a :doc:`keyboard protocol <keyboard-protocol>` for reporting key
|
||||
presses to terminal applications that solves all key handling issues in
|
||||
terminal applications.
|
||||
|
||||
.. _ext_styles:
|
||||
|
||||
@@ -180,23 +98,32 @@ rectangular region of the screen from (3, 4) to (10, 11), you use::
|
||||
<ESC>[2*x<ESC>[4;3;11;10;44$r<ESC>[*x
|
||||
|
||||
|
||||
Saving and restoring the default foreground/background/selection/cursor colors
|
||||
Saving and restoring colors
|
||||
---------------------------------------------------------------------------------
|
||||
|
||||
It is often useful for a full screen application with its own color themes
|
||||
to set the default foreground, background, selection and cursor colors. This
|
||||
allows for various performance optimizations when drawing the screen. The
|
||||
problem is that if the user previously used the escape codes to change these
|
||||
colors herself, then running the full screen application will lose her
|
||||
changes even after it exits. To avoid this, kitty introduces a new pair of
|
||||
*OSC* escape codes to push and pop the current color values from a stack::
|
||||
It is often useful for a full screen application with its own color themes to
|
||||
set the default foreground, background, selection and cursor colors and the
|
||||
ANSI color table. This allows for various performance optimizations when
|
||||
drawing the screen. The problem is that if the user previously used the escape
|
||||
codes to change these colors herself, then running the full screen application
|
||||
will lose her changes even after it exits. To avoid this, kitty introduces a
|
||||
new pair of *OSC* escape codes to push and pop the current color values from a
|
||||
stack::
|
||||
|
||||
<ESC>]30001<ESC>\ # push onto stack
|
||||
<ESC>]30101<ESC>\ # pop from stack
|
||||
|
||||
These escape codes save/restore the so called *dynamic colors*, default
|
||||
These escape codes save/restore the colors, default
|
||||
background, default foreground, selection background, selection foreground and
|
||||
cursor color.
|
||||
cursor color and the 256 colors of the ANSI color table.
|
||||
|
||||
.. note:: In July 2020, after several years, XTerm copied this protocol
|
||||
extension, without acknowledgement, and using incompatible escape codes
|
||||
(XTPUSHCOLORS, XTPOPCOLORS, XTREPORTCOLORS). And they decided to save not
|
||||
just the dynamic colors but the entire ANSI color table. In the interests of
|
||||
promoting interoperability, kitty added support for XTerm's escape codes as
|
||||
well, and changed this extension to also save/restore the entire ANSI color
|
||||
table.
|
||||
|
||||
|
||||
Pasting to clipboard
|
||||
@@ -219,7 +146,136 @@ first, for example::
|
||||
<ESC>]52;c;!<ESC>\
|
||||
|
||||
Here ``!`` is not valid base64 encoded text, so it clears the clipboard.
|
||||
Further, since it is invalid, it should be ignored by terminal emulators
|
||||
that do not support this extension, thereby making it safe to use, simply
|
||||
always send it before starting a new OSC 52 paste, even if you aren't chunking
|
||||
up large pastes, that way kitty won't concatenate your paste, and it will have
|
||||
no ill-effects in other terminal emulators.
|
||||
|
||||
In case you're using software that can't be easily adapted to this
|
||||
protocol extension, it can be disabled by specifying ``no-append`` to the
|
||||
:opt:`clipboard_control` setting.
|
||||
|
||||
|
||||
.. _desktop_notifications:
|
||||
|
||||
|
||||
Desktop notifications
|
||||
---------------------------------
|
||||
|
||||
|kitty| implements an extensible escape code (OSC 99) to show desktop
|
||||
notifications. It is easy to use from shell scripts and fully extensible to
|
||||
show title and body. Clicking on the notification can optionally focus the
|
||||
window it came from, and/or send an escape code back to the application running
|
||||
in that window.
|
||||
|
||||
The design of the escape code is partially based on the discussion in
|
||||
the defunct
|
||||
`terminal-wg <https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/13>`_
|
||||
|
||||
The escape code has the form::
|
||||
|
||||
<OSC> 99 ; metadata ; payload <terminator>
|
||||
|
||||
Here ``<OSC>`` is :code:`<ESC>]` and ``<terminator>`` is
|
||||
:code:`<ESC><backslash>`. The metadata is a section of colon separated
|
||||
:code:`key=value` pairs. Every key must be a single character from the set
|
||||
:code:`a-zA-Z` and every value must be a word consisting of characters from
|
||||
the set :code:`a-zA-Z0-9-_/\+.,(){}[]*&^%$#@!`~`. The payload must be
|
||||
interpreted based on the metadata section. The two semi-colons *must* always be
|
||||
present even when no metadata is present.
|
||||
|
||||
Before going into details, lets see how one can display a simple, single line
|
||||
notification from a shell script::
|
||||
|
||||
printf '\x1b]99;;Hello world\x1b\\'
|
||||
|
||||
To show a message with a title and a body::
|
||||
|
||||
printf '\x1b]99;i=1:d=0;Hello world\x1b\\'
|
||||
printf '\x1b]99;i=1:d=1:p=body;This is cool\x1b\\'
|
||||
|
||||
The most important key in the metadata is the ``p`` key, it controls how the
|
||||
payload is interpreted. A value of ``title`` means the payload is setting the
|
||||
title for the notification. A value of ``body`` means it is setting the body,
|
||||
and so on, see the table below for full details.
|
||||
|
||||
The design of the escape code is fundamentally chunked, this is because
|
||||
different terminal emulators have different limits on how large a single escape
|
||||
code can be. Chunking is accomplished by the ``i`` and ``d`` keys. The ``i``
|
||||
key is the *notification id* which can be any string containing the characters
|
||||
``[a-zA-Z0-9_-+.]``. The ``d`` key stands for *done* and
|
||||
can only take the values ``0`` and ``1``. A value of ``0`` means the
|
||||
notification is not yet done and the terminal emulator should hold off
|
||||
displaying it. A value of ``1`` means the notification is done, and should be
|
||||
displayed. You can specify the title or body multiple times and the terminal
|
||||
emulator will concatenate them, thereby allowing arbitrarily long text
|
||||
(terminal emulators are free to impose a sensible limit to avoid
|
||||
Denial-of-Service attacks).
|
||||
|
||||
Both the ``title`` and ``body`` payloads must be either UTF-8 encoded plain
|
||||
text with no embedded escape codes, or UTF-8 text that is base64 encoded, in
|
||||
which case there must be an ``e=1`` key in the metadata to indicate the payload
|
||||
is base64 encoded.
|
||||
|
||||
When the user clicks the notification, a couple of things can happen, the
|
||||
terminal emulator can focus the window from which the notification came, and/or
|
||||
it can send back an escape code to the application indicating the notification
|
||||
was activated. This is controlled by the ``a`` key which takes a comma
|
||||
separated set of values, ``report`` and ``focus``. The value ``focus`` means
|
||||
focus the window from which the notification was issued and is the default.
|
||||
``report`` means send an escape code back to the application. The format of the
|
||||
returned escape code is::
|
||||
|
||||
<OSC> 99 ; i=identifier ; <terminator>
|
||||
|
||||
The value of ``identifier`` comes from the ``i`` key in the escape code sent by
|
||||
the application. If the application sends no identifier, then the terminal
|
||||
*must* use ``i=0``. Actions can be preceded by a negative sign to turn them
|
||||
off, so for example if you do not want any action, turn off the default
|
||||
``focus`` action with::
|
||||
|
||||
a=-focus
|
||||
|
||||
Complete specification of all the metadata keys is in the table below. If a
|
||||
terminal emulator encounters a key in the metadata it does not understand,
|
||||
the key *must* be ignored, to allow for future extensibility of this escape
|
||||
code. Similarly if values for known keys are unknown, the terminal emulator
|
||||
*should* either ignore the entire escape code or perform a best guess effort
|
||||
to display it based on what it does understand.
|
||||
|
||||
.. note::
|
||||
It is possible to extend this escape code to allow specifying an icon for
|
||||
the notification, however, given that some platforms, such as macOS, dont
|
||||
allow displaying custom icons on a notification, at all, it was decided to
|
||||
leave it out of the spec for the time being.
|
||||
|
||||
Similarly, features such as scheduled notifications could be added in future
|
||||
revisions.
|
||||
|
||||
|
||||
======= ==================== ========= =================
|
||||
Key Value Default Description
|
||||
======= ==================== ========= =================
|
||||
``a`` Comma separated list ``focus`` What action to perform when the
|
||||
of ``report``, notification is clicked
|
||||
``focus``, with
|
||||
optional leading
|
||||
``-``
|
||||
|
||||
``d`` ``0`` or ``1`` ``1`` Indicates if the notification is
|
||||
complete or not.
|
||||
|
||||
``e`` ``0`` or ``1`` ``0`` If set to ``1`` means the payload is base64 encoded UTF-8,
|
||||
otherwise it is plain UTF-8 text with no C0 control codes in it
|
||||
|
||||
``i`` ``[a-zA-Z0-9-_+.]`` ``0`` Identifier for the notification
|
||||
|
||||
``p`` One of ``title`` or ``title`` Whether the payload is the notification title or body. If a
|
||||
``body``. notification has no title, the body will be used as title.
|
||||
======= ==================== ========= =================
|
||||
|
||||
|
||||
.. note::
|
||||
|kitty| also supports the legacy OSC 9 protocol developed by iTerm2 for
|
||||
desktop notifications.
|
||||
|
||||
@@ -10,20 +10,32 @@ form::
|
||||
Where ``<ESC>`` is the byte ``0x1b``. The JSON object has the form::
|
||||
|
||||
{
|
||||
'cmd': "command name",
|
||||
'version': "kitty version",
|
||||
'no_response': Optional Boolean,
|
||||
'payload': <Optional JSON object>,
|
||||
"cmd": "command name",
|
||||
"version": <kitty version>,
|
||||
"no_response": <Optional Boolean>,
|
||||
"payload": <Optional JSON object>,
|
||||
}
|
||||
|
||||
The ``version`` above is a string of the form :code:`0.14.2`. If you are developing a
|
||||
The ``version`` above is an array of the form :code:`[0, 14, 2]`. If you are developing a
|
||||
standalone client, use the kitty version that you are developing against. Using
|
||||
a version greater than the version of the kitty instance you are talking to,
|
||||
will cause a failure.
|
||||
|
||||
Set ``no_response`` to True if you don't want a response from kitty.
|
||||
Set ``no_response`` to ``true`` if you don't want a response from kitty.
|
||||
|
||||
The optional payload is a JSON object that is specific to the actual command being sent.
|
||||
The fields in the object for every command are documented below.
|
||||
|
||||
As a quick example showing how easy to use this protocol is, we will implement
|
||||
the ``@ ls`` command from the shell using only shell tools. First, run kitty
|
||||
as::
|
||||
|
||||
kitty -o allow_remote_control=socket-only --listen-on unix:/tmp/test
|
||||
|
||||
Now, in a different terminal, you can get the pretty printed ``@ ls`` output
|
||||
with the following command line::
|
||||
|
||||
echo -en '\eP@kitty-cmd{"cmd":"ls","version":[0,14,2]}\e\' | socat - unix:/tmp/test | awk '{ print substr($0, 13, length($0) - 14) }' | jq -c '.data | fromjson' | jq .
|
||||
|
||||
|
||||
.. include:: generated/rc.rst
|
||||
|
||||
@@ -21,7 +21,7 @@ In order for control to work, :opt:`allow_remote_control` must be enabled in
|
||||
|
||||
Now, in the new |kitty| window, enter the command::
|
||||
|
||||
kitty @ new-window --title Output --keep-focus cat
|
||||
kitty @ launch --title Output --keep-focus cat
|
||||
|
||||
This will open a new window, running the ``cat`` program that will appear next
|
||||
to the current window.
|
||||
@@ -54,7 +54,7 @@ window. Type ``Ctrl+D`` when you are ready to stop.
|
||||
|
||||
Now, let's open a new tab::
|
||||
|
||||
kitty @ new-window --new-tab --tab-title "My Tab" --keep-focus bash
|
||||
kitty @ launch --type=tab --tab-title "My Tab" --keep-focus bash
|
||||
|
||||
This will open a new tab running the bash shell with the title "My Tab".
|
||||
We can change the title of the tab with::
|
||||
@@ -115,8 +115,10 @@ shell. Run ``kitty @`` with no arguments and you will be dropped into the |kitty
|
||||
shell with completion for |kitty| command names and options.
|
||||
|
||||
You can even open the |kitty| shell inside a running |kitty| using a simple
|
||||
keyboard shortcut (:sc:`kitty_shell` by default). This has the added
|
||||
advantage that you don't need to use ``allow_remote_control`` to make it work.
|
||||
keyboard shortcut (:sc:`kitty_shell` by default).
|
||||
|
||||
.. note:: This has the added advantage that you don't need to use
|
||||
``allow_remote_control`` to make it work.
|
||||
|
||||
|
||||
Allowing only some windows to control kitty
|
||||
@@ -134,6 +136,42 @@ still write to the pipes of any other program on the same computer and
|
||||
therefore can control |kitty|. It can, however, be useful to block programs
|
||||
running on other computers (for example, over ssh) or as other users.
|
||||
|
||||
.. note:: You dont need ``allow_remote_control`` to make this work as it is
|
||||
limited to only programs running in that specific window. Be careful with
|
||||
what programs you run in such windows, since they can effectively control
|
||||
kitty, as if you were running with ``allow_remote_control`` turned on.
|
||||
|
||||
|
||||
Mapping key presses to remote control commands
|
||||
--------------------------------------------------
|
||||
|
||||
If you wish to trigger a remote control command easily with just a keypress,
|
||||
you can map it in :file:`kitty.conf`. For example::
|
||||
|
||||
map F1 remote_control set-spacing margin=30
|
||||
|
||||
Then pressing the :kbd:`F1` key will set the active window margins to 30.
|
||||
The syntax for what follows :code:`remote_control` is exactly the same
|
||||
as the syntax for what follows :code:`kitty @` above.
|
||||
|
||||
.. note:: You do not need ``allow_remote_control`` to use these mappings,
|
||||
as they are not actual remote programs, but are simply a way to resuse
|
||||
the remote control infrastructure via keybings.
|
||||
|
||||
|
||||
Broadcasting what you type to all kitty windows
|
||||
--------------------------------------------------
|
||||
|
||||
As a simple illustration of the power of remote control, lets
|
||||
have what we type sent to all open kitty windows. To do that define the
|
||||
following mapping in :file:`kitty.conf`::
|
||||
|
||||
map F1 launch --allow-remote-control kitty +kitten broadcast
|
||||
|
||||
Now press, F1 and start typing, what you type will be sent to all windows,
|
||||
live, as you type it.
|
||||
|
||||
|
||||
Documentation for the remote control protocol
|
||||
-----------------------------------------------
|
||||
|
||||
|
||||
BIN
docs/screenshots/remote_file.png
Normal file
BIN
docs/screenshots/remote_file.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
BIN
docs/screenshots/splits.png
Normal file
BIN
docs/screenshots/splits.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@@ -4,18 +4,23 @@
|
||||
|
||||
import subprocess
|
||||
from collections import defaultdict
|
||||
from typing import Any, DefaultDict, Dict, FrozenSet, List, Tuple, Union
|
||||
|
||||
KeymapType = Dict[str, Tuple[str, Union[FrozenSet[str], str]]]
|
||||
|
||||
|
||||
def resolve_keys(keymap):
|
||||
ans = defaultdict(list)
|
||||
def resolve_keys(keymap: KeymapType) -> DefaultDict[str, List[str]]:
|
||||
ans: DefaultDict[str, List[str]] = defaultdict(list)
|
||||
for ch, (attr, atype) in keymap.items():
|
||||
if atype not in ('int', 'uint'):
|
||||
atype = 'flag'
|
||||
ans[atype].append(ch)
|
||||
if isinstance(atype, str) and atype in ('int', 'uint'):
|
||||
q = atype
|
||||
else:
|
||||
q = 'flag'
|
||||
ans[q].append(ch)
|
||||
return ans
|
||||
|
||||
|
||||
def enum(keymap):
|
||||
def enum(keymap: KeymapType) -> str:
|
||||
lines = []
|
||||
for ch, (attr, atype) in keymap.items():
|
||||
lines.append(f"{attr}='{ch}'")
|
||||
@@ -26,15 +31,15 @@ def enum(keymap):
|
||||
'''.format(',\n'.join(lines))
|
||||
|
||||
|
||||
def parse_key(keymap):
|
||||
def parse_key(keymap: KeymapType) -> str:
|
||||
lines = []
|
||||
for attr, atype in keymap.values():
|
||||
vs = atype.upper() if atype in ('uint', 'int') else 'FLAG'
|
||||
vs = atype.upper() if isinstance(atype, str) and atype in ('uint', 'int') else 'FLAG'
|
||||
lines.append(f'case {attr}: value_state = {vs}; break;')
|
||||
return ' \n'.join(lines)
|
||||
|
||||
|
||||
def parse_flag(keymap, type_map, command_class):
|
||||
def parse_flag(keymap: KeymapType, type_map: Dict[str, Any], command_class: str) -> str:
|
||||
lines = []
|
||||
for ch in type_map['flag']:
|
||||
attr, allowed_values = keymap[ch]
|
||||
@@ -52,14 +57,14 @@ def parse_flag(keymap, type_map, command_class):
|
||||
return ' \n'.join(lines)
|
||||
|
||||
|
||||
def parse_number(keymap):
|
||||
def parse_number(keymap: KeymapType) -> Tuple[str, str]:
|
||||
int_keys = [f'I({attr})' for attr, atype in keymap.values() if atype == 'int']
|
||||
uint_keys = [f'U({attr})' for attr, atype in keymap.values() if atype == 'uint']
|
||||
return '; '.join(int_keys), '; '.join(uint_keys)
|
||||
|
||||
|
||||
def cmd_for_report(report_name, keymap, type_map, payload_allowed):
|
||||
def group(atype, conv):
|
||||
def cmd_for_report(report_name: str, keymap: KeymapType, type_map: Dict[str, Any], payload_allowed: bool) -> str:
|
||||
def group(atype: str, conv: str) -> Tuple[str, str]:
|
||||
flag_fmt, flag_attrs = [], []
|
||||
cv = {'flag': 'c', 'int': 'i', 'uint': 'I'}[atype]
|
||||
for ch in type_map[atype]:
|
||||
@@ -84,7 +89,15 @@ def cmd_for_report(report_name, keymap, type_map, payload_allowed):
|
||||
return '\n'.join(ans)
|
||||
|
||||
|
||||
def generate(function_name, callback_name, report_name, keymap, command_class, initial_key='a', payload_allowed=True):
|
||||
def generate(
|
||||
function_name: str,
|
||||
callback_name: str,
|
||||
report_name: str,
|
||||
keymap: KeymapType,
|
||||
command_class: str,
|
||||
initial_key: str = 'a',
|
||||
payload_allowed: bool = True
|
||||
) -> str:
|
||||
type_map = resolve_keys(keymap)
|
||||
keys_enum = enum(keymap)
|
||||
handle_key = parse_key(keymap)
|
||||
@@ -124,6 +137,7 @@ static inline void
|
||||
{parr}
|
||||
{keys_enum}
|
||||
enum KEYS key = '{initial_key}';
|
||||
if (screen->parser_buf[pos] == ';') state = AFTER_VALUE;
|
||||
|
||||
while (pos < screen->parser_buf_pos) {{
|
||||
switch(state) {{
|
||||
@@ -225,7 +239,7 @@ static inline void
|
||||
'''
|
||||
|
||||
|
||||
def write_header(text, path):
|
||||
def write_header(text: str, path: str) -> None:
|
||||
with open(path, 'w') as f:
|
||||
print(f'// This file is generated by {__file__} do not edit!', file=f, end='\n\n')
|
||||
print('#pragma once', file=f)
|
||||
@@ -233,16 +247,19 @@ def write_header(text, path):
|
||||
subprocess.check_call(['clang-format', '-i', path])
|
||||
|
||||
|
||||
def graphics_parser():
|
||||
def graphics_parser() -> None:
|
||||
flag = frozenset
|
||||
keymap = {
|
||||
'a': ('action', flag('tTqpd')),
|
||||
'd': ('delete_action', flag('aAiIcCpPqQxXyYzZ')),
|
||||
keymap: KeymapType = {
|
||||
'a': ('action', flag('tTqpdfa')),
|
||||
'd': ('delete_action', flag('aAiIcCfFnNpPqQxXyYzZ')),
|
||||
't': ('transmission_type', flag('dfts')),
|
||||
'o': ('compressed', flag('z')),
|
||||
'f': ('format', 'uint'),
|
||||
'm': ('more', 'uint'),
|
||||
'i': ('id', 'uint'),
|
||||
'I': ('image_number', 'uint'),
|
||||
'p': ('placement_id', 'uint'),
|
||||
'q': ('quiet', 'uint'),
|
||||
'w': ('width', 'uint'),
|
||||
'h': ('height', 'uint'),
|
||||
'x': ('x_offset', 'uint'),
|
||||
@@ -256,6 +273,7 @@ def graphics_parser():
|
||||
'X': ('cell_x_offset', 'uint'),
|
||||
'Y': ('cell_y_offset', 'uint'),
|
||||
'z': ('z_index', 'int'),
|
||||
'C': ('cursor_movement', 'uint'),
|
||||
}
|
||||
text = generate('parse_graphics_code', 'screen_handle_graphics_command', 'graphics_command', keymap, 'GraphicsCommand')
|
||||
write_header(text, 'kitty/parse-graphics-command.h')
|
||||
|
||||
412
gen-key-constants.py
Executable file
412
gen-key-constants.py
Executable file
@@ -0,0 +1,412 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import string
|
||||
from typing import Dict, List, Any
|
||||
from pprint import pformat
|
||||
|
||||
functional_key_defs = '''# {{{
|
||||
# kitty XKB macVK macU
|
||||
escape Escape 0x35 -
|
||||
enter Return 0x24 NSCarriageReturnCharacter
|
||||
tab Tab 0x30 NSTabCharacter
|
||||
backspace BackSpace 0x33 NSBackspaceCharacter
|
||||
insert Insert 0x72 Insert
|
||||
delete Delete 0x75 Delete
|
||||
left Left 0x7B LeftArrow
|
||||
right Right 0x7C RightArrow
|
||||
up Up 0x7E UpArrow
|
||||
down Down 0x7D DownArrow
|
||||
page_up Page_Up 0x74 PageUp
|
||||
page_down Page_Down 0x79 PageDown
|
||||
home Home 0x73 Home
|
||||
end End 0x77 End
|
||||
caps_lock Caps_Lock 0x39 -
|
||||
scroll_lock Scroll_Lock - ScrollLock
|
||||
num_lock Num_Lock 0x47 ClearLine
|
||||
print_screen Print - PrintScreen
|
||||
pause Pause - Pause
|
||||
menu Menu 0x6E Menu
|
||||
f1 F1 0x7A F1
|
||||
f2 F2 0x78 F2
|
||||
f3 F3 0x63 F3
|
||||
f4 F4 0x76 F4
|
||||
f5 F5 0x60 F5
|
||||
f6 F6 0x61 F6
|
||||
f7 F7 0x62 F7
|
||||
f8 F8 0x64 F8
|
||||
f9 F9 0x65 F9
|
||||
f10 F10 0x6D F10
|
||||
f11 F11 0x67 F11
|
||||
f12 F12 0x6F F12
|
||||
f13 F13 0x69 F13
|
||||
f14 F14 0x6B F14
|
||||
f15 F15 0x71 F15
|
||||
f16 F16 0x6A F16
|
||||
f17 F17 0x40 F17
|
||||
f18 F18 0x4F F18
|
||||
f19 F19 0x50 F19
|
||||
f20 F20 0x5A F20
|
||||
f21 F21 - F21
|
||||
f22 F22 - F22
|
||||
f23 F23 - F23
|
||||
f24 F24 - F24
|
||||
f25 F25 - F25
|
||||
f26 F26 - F26
|
||||
f27 F27 - F27
|
||||
f28 F28 - F28
|
||||
f29 F29 - F29
|
||||
f30 F30 - F30
|
||||
f31 F31 - F31
|
||||
f32 F32 - F32
|
||||
f33 F33 - F33
|
||||
f34 F34 - F34
|
||||
f35 F35 - F35
|
||||
kp_0 KP_0 0x52 -
|
||||
kp_1 KP_1 0x53 -
|
||||
kp_2 KP_2 0x54 -
|
||||
kp_3 KP_3 0x55 -
|
||||
kp_4 KP_4 0x56 -
|
||||
kp_5 KP_5 0x57 -
|
||||
kp_6 KP_6 0x58 -
|
||||
kp_7 KP_7 0x59 -
|
||||
kp_8 KP_8 0x5B -
|
||||
kp_9 KP_9 0x5C -
|
||||
kp_decimal KP_Decimal 0x41 -
|
||||
kp_divide KP_Divide 0x4B -
|
||||
kp_multiply KP_Multiply 0x43 -
|
||||
kp_subtract KP_Subtract 0x4E -
|
||||
kp_add KP_Add 0x45 -
|
||||
kp_enter KP_Enter 0x4C NSEnterCharacter
|
||||
kp_equal KP_Equal 0x51 -
|
||||
kp_separator KP_Separator - -
|
||||
kp_left KP_Left - -
|
||||
kp_right KP_Right - -
|
||||
kp_up KP_Up - -
|
||||
kp_down KP_Down - -
|
||||
kp_page_up KP_Page_Up - -
|
||||
kp_page_down KP_Page_Down - -
|
||||
kp_home KP_Home - -
|
||||
kp_end KP_End - -
|
||||
kp_insert KP_Insert - -
|
||||
kp_delete KP_Delete - -
|
||||
kp_begin KP_Begin - -
|
||||
media_play XF86AudioPlay - -
|
||||
media_pause XF86AudioPause - -
|
||||
media_play_pause - - -
|
||||
media_reverse - - -
|
||||
media_stop XF86AudioStop - -
|
||||
media_fast_forward XF86AudioForward - -
|
||||
media_rewind XF86AudioRewind - -
|
||||
media_track_next XF86AudioNext - -
|
||||
media_track_previous XF86AudioPrev - -
|
||||
media_record XF86AudioRecord - -
|
||||
lower_volume XF86AudioLowerVolume - -
|
||||
raise_volume XF86AudioRaiseVolume - -
|
||||
mute_volume XF86AudioMute - -
|
||||
left_shift Shift_L 0x38 -
|
||||
left_control Control_L 0x3B -
|
||||
left_alt Alt_L 0x3A -
|
||||
left_super Super_L 0x37 -
|
||||
left_hyper Hyper_L - -
|
||||
left_meta Meta_L - -
|
||||
right_shift Shift_R 0x3C -
|
||||
right_control Control_R 0x3E -
|
||||
right_alt Alt_R 0x3D -
|
||||
right_super Super_R 0x36 -
|
||||
right_hyper Hyper_R - -
|
||||
right_meta Meta_R - -
|
||||
iso_level3_shift ISO_Level3_Shift - -
|
||||
iso_level5_shift ISO_Level5_Shift - -
|
||||
''' # }}}
|
||||
|
||||
shift_map = {x[0]: x[1] for x in '`~ 1! 2@ 3# 4$ 5% 6^ 7& 8* 9( 0) -_ =+ [{ ]} \\| ;: \'" ,< .> /?'.split()}
|
||||
shift_map.update({x: x.upper() for x in string.ascii_lowercase})
|
||||
functional_encoding_overrides = {
|
||||
'insert': 2, 'delete': 3, 'page_up': 5, 'page_down': 6,
|
||||
'home': 7, 'end': 8, 'tab': 9, 'f1': 11, 'f2': 12, 'f3': 13, 'enter': 13, 'f4': 14,
|
||||
'f5': 15, 'f6': 17, 'f7': 18, 'f8': 19, 'f9': 20, 'f10': 21,
|
||||
'f11': 23, 'f12': 24, 'escape': 27, 'backspace': 127
|
||||
}
|
||||
different_trailer_functionals = {
|
||||
'up': 'A', 'down': 'B', 'right': 'C', 'left': 'D', 'kp_begin': 'E', 'end': 'F', 'home': 'H',
|
||||
'f1': 'P', 'f2': 'Q', 'f3': 'R', 'f4': 'S', 'enter': 'u', 'tab': 'u',
|
||||
'backspace': 'u', 'escape': 'u'
|
||||
}
|
||||
|
||||
macos_ansi_key_codes = { # {{{
|
||||
0x1D: ord('0'),
|
||||
0x12: ord('1'),
|
||||
0x13: ord('2'),
|
||||
0x14: ord('3'),
|
||||
0x15: ord('4'),
|
||||
0x17: ord('5'),
|
||||
0x16: ord('6'),
|
||||
0x1A: ord('7'),
|
||||
0x1C: ord('8'),
|
||||
0x19: ord('9'),
|
||||
0x00: ord('a'),
|
||||
0x0B: ord('b'),
|
||||
0x08: ord('c'),
|
||||
0x02: ord('d'),
|
||||
0x0E: ord('e'),
|
||||
0x03: ord('f'),
|
||||
0x05: ord('g'),
|
||||
0x04: ord('h'),
|
||||
0x22: ord('i'),
|
||||
0x26: ord('j'),
|
||||
0x28: ord('k'),
|
||||
0x25: ord('l'),
|
||||
0x2E: ord('m'),
|
||||
0x2D: ord('n'),
|
||||
0x1F: ord('o'),
|
||||
0x23: ord('p'),
|
||||
0x0C: ord('q'),
|
||||
0x0F: ord('r'),
|
||||
0x01: ord('s'),
|
||||
0x11: ord('t'),
|
||||
0x20: ord('u'),
|
||||
0x09: ord('v'),
|
||||
0x0D: ord('w'),
|
||||
0x07: ord('x'),
|
||||
0x10: ord('y'),
|
||||
0x06: ord('z'),
|
||||
|
||||
0x27: ord('\''),
|
||||
0x2A: ord('\\'),
|
||||
0x2B: ord(','),
|
||||
0x18: ord('='),
|
||||
0x32: ord('`'),
|
||||
0x21: ord('['),
|
||||
0x1B: ord('-'),
|
||||
0x2F: ord('.'),
|
||||
0x1E: ord(']'),
|
||||
0x29: ord(';'),
|
||||
0x2C: ord('/'),
|
||||
0x31: ord(' '),
|
||||
} # }}}
|
||||
|
||||
functional_key_names: List[str] = []
|
||||
name_to_code: Dict[str, int] = {}
|
||||
name_to_xkb: Dict[str, str] = {}
|
||||
name_to_vk: Dict[str, int] = {}
|
||||
name_to_macu: Dict[str, str] = {}
|
||||
start_code = 0xe000
|
||||
for line in functional_key_defs.splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
parts = line.split()
|
||||
name = parts[0]
|
||||
functional_key_names.append(name)
|
||||
name_to_code[name] = len(name_to_code) + start_code
|
||||
if parts[1] != '-':
|
||||
name_to_xkb[name] = parts[1]
|
||||
if parts[2] != '-':
|
||||
name_to_vk[name] = int(parts[2], 16)
|
||||
if parts[3] != '-':
|
||||
val = parts[3]
|
||||
if not val.startswith('NS'):
|
||||
val = f'NS{val}FunctionKey'
|
||||
name_to_macu[name] = val
|
||||
last_code = start_code + len(functional_key_names) - 1
|
||||
ctrl_mapping = {
|
||||
' ': 0, '@': 0, 'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7,
|
||||
'h': 8, 'i': 9, 'j': 10, 'k': 11, 'l': 12, 'm': 13, 'n': 14, 'o': 15, 'p': 16,
|
||||
'q': 17, 'r': 18, 's': 19, 't': 20, 'u': 21, 'v': 22, 'w': 23, 'x': 24,
|
||||
'y': 25, 'z': 26, '[': 27, '\\': 28, ']': 29, '^': 30, '~': 30, '/': 31,
|
||||
'_': 31, '?': 127, '0': 48, '1': 49, '2': 0, '3': 27, '4': 28,
|
||||
'5': 29, '6': 30, '7': 31, '8': 127, '9': 57
|
||||
}
|
||||
|
||||
|
||||
def patch_file(path: str, what: str, text: str, start_marker: str = '/* ', end_marker: str = ' */') -> None:
|
||||
simple_start_q = f'{start_marker}start {what}{end_marker}'
|
||||
start_q = f'{start_marker}start {what} (auto generated by gen-key-constants.py do not edit){end_marker}'
|
||||
end_q = f'{start_marker}end {what}{end_marker}'
|
||||
|
||||
with open(path, 'r+') as f:
|
||||
raw = f.read()
|
||||
try:
|
||||
start = raw.index(start_q)
|
||||
except ValueError:
|
||||
try:
|
||||
start = raw.index(simple_start_q)
|
||||
except ValueError:
|
||||
raise SystemExit(f'Failed to find "{simple_start_q}" in {path}')
|
||||
try:
|
||||
end = raw.index(end_q)
|
||||
except ValueError:
|
||||
raise SystemExit(f'Failed to find "{end_q}" in {path}')
|
||||
raw = raw[:start] + start_q + '\n' + text + '\n' + raw[end:]
|
||||
f.seek(0)
|
||||
f.truncate(0)
|
||||
f.write(raw)
|
||||
|
||||
|
||||
def serialize_dict(x: dict) -> str:
|
||||
return pformat(x, indent=4).replace('{', '{\n ', 1)
|
||||
|
||||
|
||||
def generate_glfw_header() -> None:
|
||||
lines = [
|
||||
'typedef enum {',
|
||||
f' GLFW_FKEY_FIRST = 0x{start_code:x}u,',
|
||||
]
|
||||
klines, pyi, names, knames = [], [], [], []
|
||||
for name, code in name_to_code.items():
|
||||
lines.append(f' GLFW_FKEY_{name.upper()} = 0x{code:x}u,')
|
||||
klines.append(f' ADDC(GLFW_FKEY_{name.upper()});')
|
||||
pyi.append(f'GLFW_FKEY_{name.upper()}: int')
|
||||
names.append(f' case GLFW_FKEY_{name.upper()}: return "{name.upper()}";')
|
||||
knames.append(f' case GLFW_FKEY_{name.upper()}: return PyUnicode_FromString("{name}");')
|
||||
lines.append(f' GLFW_FKEY_LAST = 0x{last_code:x}u')
|
||||
lines.append('} GLFWFunctionKey;')
|
||||
patch_file('glfw/glfw3.h', 'functional key names', '\n'.join(lines))
|
||||
patch_file('kitty/glfw.c', 'glfw functional keys', '\n'.join(klines))
|
||||
patch_file('kitty/fast_data_types.pyi', 'glfw functional keys', '\n'.join(pyi), start_marker='# ', end_marker='')
|
||||
patch_file('glfw/input.c', 'functional key names', '\n'.join(names))
|
||||
patch_file('kitty/glfw.c', 'glfw functional key names', '\n'.join(knames))
|
||||
|
||||
|
||||
def generate_xkb_mapping() -> None:
|
||||
lines, rlines = [], []
|
||||
for name, xkb in name_to_xkb.items():
|
||||
lines.append(f' case XKB_KEY_{xkb}: return GLFW_FKEY_{name.upper()};')
|
||||
rlines.append(f' case GLFW_FKEY_{name.upper()}: return XKB_KEY_{xkb};')
|
||||
patch_file('glfw/xkb_glfw.c', 'xkb to glfw', '\n'.join(lines))
|
||||
patch_file('glfw/xkb_glfw.c', 'glfw to xkb', '\n'.join(rlines))
|
||||
|
||||
|
||||
def generate_functional_table() -> None:
|
||||
lines = [
|
||||
'',
|
||||
'.. csv-table:: Functional key codes',
|
||||
' :header: "Name", "CSI", "Name", "CSI"',
|
||||
''
|
||||
]
|
||||
line_items = []
|
||||
enc_lines = []
|
||||
tilde_trailers = set()
|
||||
for name, code in name_to_code.items():
|
||||
if name in functional_encoding_overrides or name in different_trailer_functionals:
|
||||
trailer = different_trailer_functionals.get(name, '~')
|
||||
if trailer == '~':
|
||||
tilde_trailers.add(code)
|
||||
code = oc = functional_encoding_overrides.get(name, code)
|
||||
code = code if trailer in '~u' else 1
|
||||
enc_lines.append((' ' * 8) + f"case GLFW_FKEY_{name.upper()}: S({code}, '{trailer}');")
|
||||
if code == 1 and name not in ('up', 'down', 'left', 'right'):
|
||||
trailer += f' or {oc} ~'
|
||||
else:
|
||||
trailer = 'u'
|
||||
line_items.append(name.upper())
|
||||
line_items.append(f'``{code}\xa0{trailer}``')
|
||||
for li in chunks(line_items, 4):
|
||||
lines.append(' ' + ', '.join(f'"{x}"' for x in li))
|
||||
lines.append('')
|
||||
patch_file('docs/keyboard-protocol.rst', 'functional key table', '\n'.join(lines), start_marker='.. ', end_marker='')
|
||||
patch_file('kitty/key_encoding.c', 'special numbers', '\n'.join(enc_lines))
|
||||
code_to_name = {v: k.upper() for k, v in name_to_code.items()}
|
||||
csi_map = {v: name_to_code[k] for k, v in functional_encoding_overrides.items()}
|
||||
letter_trailer_codes = {
|
||||
v: functional_encoding_overrides.get(k, name_to_code.get(k))
|
||||
for k, v in different_trailer_functionals.items() if v in 'ABCDEHFPQRSZ'}
|
||||
text = f'functional_key_number_to_name_map = {serialize_dict(code_to_name)}'
|
||||
text += f'\ncsi_number_to_functional_number_map = {serialize_dict(csi_map)}'
|
||||
text += f'\nletter_trailer_to_csi_number_map = {letter_trailer_codes!r}'
|
||||
text += f'\ntilde_trailers = {tilde_trailers!r}'
|
||||
patch_file('kitty/key_encoding.py', 'csi mapping', text, start_marker='# ', end_marker='')
|
||||
|
||||
|
||||
def generate_legacy_text_key_maps() -> None:
|
||||
tests = []
|
||||
tp = ' ' * 8
|
||||
shift, alt, ctrl = 1, 2, 4
|
||||
|
||||
def simple(c: str) -> None:
|
||||
shifted = shift_map.get(c, c)
|
||||
ctrled = chr(ctrl_mapping.get(c, ord(c)))
|
||||
call = f'enc(ord({c!r}), shifted_key=ord({shifted!r})'
|
||||
for m in range(16):
|
||||
if m == 0:
|
||||
tests.append(f'{tp}ae({call}), {c!r})')
|
||||
elif m == shift:
|
||||
tests.append(f'{tp}ae({call}, mods=shift), {shifted!r})')
|
||||
elif m == alt:
|
||||
tests.append(f'{tp}ae({call}, mods=alt), "\\x1b" + {c!r})')
|
||||
elif m == ctrl:
|
||||
tests.append(f'{tp}ae({call}, mods=ctrl), {ctrled!r})')
|
||||
elif m == shift | alt:
|
||||
tests.append(f'{tp}ae({call}, mods=shift | alt), "\\x1b" + {shifted!r})')
|
||||
elif m == ctrl | alt:
|
||||
tests.append(f'{tp}ae({call}, mods=ctrl | alt), "\\x1b" + {ctrled!r})')
|
||||
|
||||
for k in shift_map:
|
||||
simple(k)
|
||||
|
||||
patch_file('kitty_tests/keys.py', 'legacy letter tests', '\n'.join(tests), start_marker='# ', end_marker='')
|
||||
|
||||
|
||||
def chunks(lst: List, n: int) -> Any:
|
||||
"""Yield successive n-sized chunks from lst."""
|
||||
for i in range(0, len(lst), n):
|
||||
yield lst[i:i + n]
|
||||
|
||||
|
||||
def generate_ctrl_mapping() -> None:
|
||||
lines = [
|
||||
'.. csv-table:: Emitted bytes when :kbd:`ctrl` is held down and a key is pressed',
|
||||
' :header: "Key", "Byte", "Key", "Byte", "Key", "Byte"',
|
||||
''
|
||||
]
|
||||
items = []
|
||||
mi = []
|
||||
for k in sorted(ctrl_mapping):
|
||||
prefix = '\\' if k == '\\' else ('SPC' if k == ' ' else '')
|
||||
items.append(prefix + k)
|
||||
val = str(ctrl_mapping[k])
|
||||
items.append(val)
|
||||
if k in "\\'":
|
||||
k = '\\' + k
|
||||
mi.append(f" case '{k}': return {val};")
|
||||
|
||||
for line_items in chunks(items, 6):
|
||||
lines.append(' ' + ', '.join(f'"{x}"' for x in line_items))
|
||||
lines.append('')
|
||||
patch_file('docs/keyboard-protocol.rst', 'ctrl mapping', '\n'.join(lines), start_marker='.. ', end_marker='')
|
||||
patch_file('kitty/key_encoding.c', 'ctrl mapping', '\n'.join(mi))
|
||||
|
||||
|
||||
def generate_macos_mapping() -> None:
|
||||
lines = []
|
||||
for k in sorted(macos_ansi_key_codes):
|
||||
v = macos_ansi_key_codes[k]
|
||||
lines.append(f' case 0x{k:x}: return 0x{v:x};')
|
||||
patch_file('glfw/cocoa_window.m', 'vk to unicode', '\n'.join(lines))
|
||||
lines = []
|
||||
for name, vk in name_to_vk.items():
|
||||
lines.append(f' case 0x{vk:x}: return GLFW_FKEY_{name.upper()};')
|
||||
patch_file('glfw/cocoa_window.m', 'vk to functional', '\n'.join(lines))
|
||||
lines = []
|
||||
for name, mac in name_to_macu.items():
|
||||
lines.append(f' case {mac}: return GLFW_FKEY_{name.upper()};')
|
||||
patch_file('glfw/cocoa_window.m', 'macu to functional', '\n'.join(lines))
|
||||
lines = []
|
||||
for name, mac in name_to_macu.items():
|
||||
lines.append(f' case GLFW_FKEY_{name.upper()}: return {mac};')
|
||||
patch_file('glfw/cocoa_window.m', 'functional to macu', '\n'.join(lines))
|
||||
|
||||
|
||||
def main() -> None:
|
||||
generate_glfw_header()
|
||||
generate_xkb_mapping()
|
||||
generate_functional_table()
|
||||
generate_legacy_text_key_maps()
|
||||
generate_ctrl_mapping()
|
||||
generate_macos_mapping()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
238
gen-wcwidth.py
238
gen-wcwidth.py
@@ -12,6 +12,10 @@ from functools import partial
|
||||
from html.entities import html5
|
||||
from itertools import groupby
|
||||
from operator import itemgetter
|
||||
from typing import (
|
||||
Callable, DefaultDict, Dict, FrozenSet, Generator, Iterable, List,
|
||||
Optional, Set, Tuple, Union
|
||||
)
|
||||
from urllib.request import urlopen
|
||||
|
||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||
@@ -24,7 +28,7 @@ if len(non_characters) != 66:
|
||||
emoji_skin_tone_modifiers = frozenset(range(0x1f3fb, 0x1F3FF + 1))
|
||||
|
||||
|
||||
def get_data(fname, folder='UCD'):
|
||||
def get_data(fname: str, folder: str = 'UCD') -> Iterable[str]:
|
||||
url = f'https://www.unicode.org/Public/{folder}/latest/{fname}'
|
||||
bn = os.path.basename(url)
|
||||
local = os.path.join('/tmp', bn)
|
||||
@@ -42,24 +46,25 @@ def get_data(fname, folder='UCD'):
|
||||
|
||||
|
||||
# Map of class names to set of codepoints in class
|
||||
class_maps = {}
|
||||
all_symbols = set()
|
||||
name_map = {}
|
||||
word_search_map = defaultdict(set)
|
||||
class_maps: Dict[str, Set[int]] = {}
|
||||
all_symbols: Set[int] = set()
|
||||
name_map: Dict[int, str] = {}
|
||||
word_search_map: DefaultDict[str, Set[int]] = defaultdict(set)
|
||||
zwj = 0x200d
|
||||
marks = set(emoji_skin_tone_modifiers) | {zwj}
|
||||
flag_codepoints = frozenset(range(0x1F1E6, 0x1F1E6 + 26))
|
||||
marks = set(emoji_skin_tone_modifiers) | {zwj} | flag_codepoints
|
||||
not_assigned = set(range(0, sys.maxunicode))
|
||||
|
||||
|
||||
def parse_ucd():
|
||||
def parse_ucd() -> None:
|
||||
|
||||
def add_word(w, c):
|
||||
def add_word(w: str, c: int) -> None:
|
||||
if c <= 32 or c == 127 or 128 <= c <= 159:
|
||||
return
|
||||
if len(w) > 1:
|
||||
word_search_map[w.lower()].add(c)
|
||||
|
||||
first = None
|
||||
first: Optional[int] = None
|
||||
for word, c in html5.items():
|
||||
if len(c) == 1:
|
||||
add_word(word.rstrip(';'), ord(c))
|
||||
@@ -77,7 +82,7 @@ def parse_ucd():
|
||||
category = parts[2]
|
||||
s = class_maps.setdefault(category, set())
|
||||
desc = parts[1]
|
||||
codepoints = (codepoint,)
|
||||
codepoints: Union[Tuple[int, ...], Iterable[int]] = (codepoint,)
|
||||
if first is None:
|
||||
if desc.endswith(', First>'):
|
||||
first = codepoint
|
||||
@@ -93,49 +98,116 @@ def parse_ucd():
|
||||
elif category.startswith('S'):
|
||||
all_symbols.add(codepoint)
|
||||
|
||||
with open('nerd-fonts-glyphs.txt') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
code, category, name = line.split(' ', 2)
|
||||
codepoint = int(code, 16)
|
||||
if name and codepoint not in name_map:
|
||||
name_map[codepoint] = name.upper()
|
||||
for word in name.lower().split():
|
||||
add_word(word, codepoint)
|
||||
|
||||
# Some common synonyms
|
||||
word_search_map['bee'] |= word_search_map['honeybee']
|
||||
word_search_map['lambda'] |= word_search_map['lamda']
|
||||
word_search_map['lamda'] |= word_search_map['lambda']
|
||||
word_search_map['diamond'] |= word_search_map['gem']
|
||||
|
||||
|
||||
def split_two(line):
|
||||
spec, rest = line.split(';', 1)
|
||||
spec, rest = spec.strip(), rest.strip().split(' ', 1)[0].strip()
|
||||
def parse_range_spec(spec: str) -> Set[int]:
|
||||
spec = spec.strip()
|
||||
if '..' in spec:
|
||||
chars = tuple(map(lambda x: int(x, 16), filter(None, spec.split('.'))))
|
||||
chars = set(range(chars[0], chars[1] + 1))
|
||||
chars_ = tuple(map(lambda x: int(x, 16), filter(None, spec.split('.'))))
|
||||
chars = set(range(chars_[0], chars_[1] + 1))
|
||||
else:
|
||||
chars = {int(spec, 16)}
|
||||
return chars, rest
|
||||
return chars
|
||||
|
||||
|
||||
all_emoji = set()
|
||||
emoji_categories = {}
|
||||
emoji_presentation_bases = set()
|
||||
def split_two(line: str) -> Tuple[Set[int], str]:
|
||||
spec, rest = line.split(';', 1)
|
||||
spec, rest = spec.strip(), rest.strip().split(' ', 1)[0].strip()
|
||||
return parse_range_spec(spec), rest
|
||||
|
||||
|
||||
def parse_emoji():
|
||||
for line in get_data('emoji-data.txt', 'emoji'):
|
||||
chars, rest = split_two(line)
|
||||
s = emoji_categories.setdefault(rest, set())
|
||||
s.update(chars)
|
||||
all_emoji.update(chars)
|
||||
for line in get_data('emoji-variation-sequences.txt', 'emoji'):
|
||||
base, var, *rest = line.split()
|
||||
if base.startswith('#'):
|
||||
all_emoji: Set[int] = set()
|
||||
emoji_presentation_bases: Set[int] = set()
|
||||
narrow_emoji: Set[int] = set()
|
||||
wide_emoji: Set[int] = set()
|
||||
flags: Dict[int, List[int]] = {}
|
||||
|
||||
|
||||
def parse_basic_emoji(spec: str) -> None:
|
||||
parts = list(filter(None, spec.split()))
|
||||
has_emoji_presentation = len(parts) < 2
|
||||
chars = parse_range_spec(parts[0])
|
||||
all_emoji.update(chars)
|
||||
emoji_presentation_bases.update(chars)
|
||||
(wide_emoji if has_emoji_presentation else narrow_emoji).update(chars)
|
||||
|
||||
|
||||
def parse_keycap_sequence(spec: str) -> None:
|
||||
base, fe0f, cc = list(filter(None, spec.split()))
|
||||
chars = parse_range_spec(base)
|
||||
all_emoji.update(chars)
|
||||
emoji_presentation_bases.update(chars)
|
||||
narrow_emoji.update(chars)
|
||||
|
||||
|
||||
def parse_flag_emoji_sequence(spec: str) -> None:
|
||||
a, b = list(filter(None, spec.split()))
|
||||
left, right = int(a, 16), int(b, 16)
|
||||
chars = {left, right}
|
||||
all_emoji.update(chars)
|
||||
wide_emoji.update(chars)
|
||||
emoji_presentation_bases.update(chars)
|
||||
flags.setdefault(left, []).append(right)
|
||||
|
||||
|
||||
def parse_emoji_tag_sequence(spec: str) -> None:
|
||||
a = int(spec.split()[0], 16)
|
||||
all_emoji.add(a)
|
||||
wide_emoji.add(a)
|
||||
emoji_presentation_bases.add(a)
|
||||
|
||||
|
||||
def parse_emoji_modifier_sequence(spec: str) -> None:
|
||||
a, b = list(filter(None, spec.split()))
|
||||
char, mod = int(a, 16), int(b, 16)
|
||||
mod
|
||||
all_emoji.add(char)
|
||||
wide_emoji.add(char)
|
||||
emoji_presentation_bases.add(char)
|
||||
|
||||
|
||||
def parse_emoji() -> None:
|
||||
for line in get_data('emoji-sequences.txt', 'emoji'):
|
||||
parts = [x.strip() for x in line.split(';')]
|
||||
if len(parts) < 2:
|
||||
continue
|
||||
base = int(base, 16)
|
||||
if var.upper() == 'FE0F':
|
||||
emoji_presentation_bases.add(base)
|
||||
data, etype = parts[:2]
|
||||
if etype == 'Basic_Emoji':
|
||||
parse_basic_emoji(data)
|
||||
elif etype == 'Emoji_Keycap_Sequence':
|
||||
parse_keycap_sequence(data)
|
||||
elif etype == 'RGI_Emoji_Flag_Sequence':
|
||||
parse_flag_emoji_sequence(data)
|
||||
elif etype == 'RGI_Emoji_Tag_Sequence':
|
||||
parse_emoji_tag_sequence(data)
|
||||
elif etype == 'RGI_Emoji_Modifier_Sequence':
|
||||
parse_emoji_modifier_sequence(data)
|
||||
|
||||
|
||||
doublewidth, ambiguous = set(), set()
|
||||
doublewidth: Set[int] = set()
|
||||
ambiguous: Set[int] = set()
|
||||
|
||||
|
||||
def parse_eaw():
|
||||
def parse_eaw() -> None:
|
||||
global doublewidth, ambiguous
|
||||
seen = set()
|
||||
seen: Set[int] = set()
|
||||
for line in get_data('ucd/EastAsianWidth.txt'):
|
||||
chars, eaw = split_two(line)
|
||||
if eaw == 'A':
|
||||
@@ -151,7 +223,7 @@ def parse_eaw():
|
||||
doublewidth |= set(range(0x30000, 0x3FFFD + 1)) - seen
|
||||
|
||||
|
||||
def get_ranges(items):
|
||||
def get_ranges(items: List[int]) -> Generator[Union[int, Tuple[int, int]], None, None]:
|
||||
items.sort()
|
||||
for k, g in groupby(enumerate(items), lambda m: m[0]-m[1]):
|
||||
group = tuple(map(itemgetter(1), g))
|
||||
@@ -162,7 +234,7 @@ def get_ranges(items):
|
||||
yield a, b
|
||||
|
||||
|
||||
def write_case(spec, p):
|
||||
def write_case(spec: Union[Tuple, int], p: Callable) -> None:
|
||||
if isinstance(spec, tuple):
|
||||
p('\t\tcase 0x{:x} ... 0x{:x}:'.format(*spec))
|
||||
else:
|
||||
@@ -170,7 +242,7 @@ def write_case(spec, p):
|
||||
|
||||
|
||||
@contextmanager
|
||||
def create_header(path, include_data_types=True):
|
||||
def create_header(path: str, include_data_types: bool = True) -> Generator[Callable, None, None]:
|
||||
with open(path, 'w') as f:
|
||||
p = partial(print, file=f)
|
||||
p('// unicode data, built from the unicode standard on:', date.today())
|
||||
@@ -187,7 +259,7 @@ def create_header(path, include_data_types=True):
|
||||
p('END_ALLOW_CASE_RANGE')
|
||||
|
||||
|
||||
def gen_emoji():
|
||||
def gen_emoji() -> None:
|
||||
with create_header('kitty/emoji.h') as p:
|
||||
p('static inline bool\nis_emoji(char_type code) {')
|
||||
p('\tswitch(code) {')
|
||||
@@ -198,15 +270,6 @@ def gen_emoji():
|
||||
p('\t}')
|
||||
p('\treturn false;\n}')
|
||||
|
||||
p('static inline bool\nis_emoji_modifier(char_type code) {')
|
||||
p('\tswitch(code) {')
|
||||
for spec in get_ranges(list(emoji_categories['Emoji_Modifier'])):
|
||||
write_case(spec, p)
|
||||
p('\t\t\treturn true;')
|
||||
p('\t\tdefault: return false;')
|
||||
p('\t}')
|
||||
p('\treturn false;\n}')
|
||||
|
||||
p('static inline bool\nis_symbol(char_type code) {')
|
||||
p('\tswitch(code) {')
|
||||
for spec in get_ranges(list(all_symbols)):
|
||||
@@ -217,24 +280,39 @@ def gen_emoji():
|
||||
p('\treturn false;\n}')
|
||||
|
||||
|
||||
def category_test(name, p, classes, comment, static=False, extra_chars=frozenset(), exclude=frozenset()):
|
||||
static = 'static inline ' if static else ''
|
||||
chars = set()
|
||||
def category_test(
|
||||
name: str,
|
||||
p: Callable,
|
||||
classes: Iterable[str],
|
||||
comment: str,
|
||||
use_static: bool = False,
|
||||
extra_chars: Union[FrozenSet[int], Set[int]] = frozenset(),
|
||||
exclude: Union[Set[int], FrozenSet[int]] = frozenset(),
|
||||
least_check_return: Optional[str] = None,
|
||||
ascii_range: Optional[str] = None
|
||||
) -> None:
|
||||
static = 'static inline ' if use_static else ''
|
||||
chars: Set[int] = set()
|
||||
for c in classes:
|
||||
chars |= class_maps[c]
|
||||
chars |= extra_chars
|
||||
chars -= exclude
|
||||
p(f'{static}bool\n{name}(char_type code) {{')
|
||||
p(f'\t// {comment} ({len(chars)} codepoints)' + ' {{' '{')
|
||||
if least_check_return is not None:
|
||||
least = min(chars)
|
||||
p(f'\tif (LIKELY(code < {least})) return {least_check_return};')
|
||||
if ascii_range is not None:
|
||||
p(f'\tif (LIKELY(0x20 <= code && code <= 0x7e)) return {ascii_range};')
|
||||
p('\tswitch(code) {')
|
||||
for spec in get_ranges(list(chars)):
|
||||
write_case(spec, p)
|
||||
p(f'\t\t\treturn true;')
|
||||
p('\t\t\treturn true;')
|
||||
p('\t} // }}}\n')
|
||||
p('\treturn false;\n}\n')
|
||||
|
||||
|
||||
def codepoint_to_mark_map(p, mark_map):
|
||||
def codepoint_to_mark_map(p: Callable, mark_map: List[int]) -> Dict[int, int]:
|
||||
p('\tswitch(c) { // {{{')
|
||||
rmap = {c: m for m, c in enumerate(mark_map)}
|
||||
for spec in get_ranges(mark_map):
|
||||
@@ -249,14 +327,14 @@ def codepoint_to_mark_map(p, mark_map):
|
||||
return rmap
|
||||
|
||||
|
||||
def classes_to_regex(classes, exclude=''):
|
||||
chars = set()
|
||||
def classes_to_regex(classes: Iterable[str], exclude: str = '') -> Iterable[str]:
|
||||
chars: Set[int] = set()
|
||||
for c in classes:
|
||||
chars |= class_maps[c]
|
||||
for c in map(ord, exclude):
|
||||
chars.discard(c)
|
||||
for x in map(ord, exclude):
|
||||
chars.discard(x)
|
||||
|
||||
def as_string(codepoint):
|
||||
def as_string(codepoint: int) -> str:
|
||||
if codepoint < 256:
|
||||
return r'\x{:02x}'.format(codepoint)
|
||||
if codepoint <= 0xffff:
|
||||
@@ -270,7 +348,7 @@ def classes_to_regex(classes, exclude=''):
|
||||
yield as_string(spec)
|
||||
|
||||
|
||||
def gen_ucd():
|
||||
def gen_ucd() -> None:
|
||||
cz = {c for c in class_maps if c[0] in 'CZ'}
|
||||
with create_header('kitty/unicode-data.c') as p:
|
||||
p('#include "unicode-data.h"')
|
||||
@@ -279,11 +357,15 @@ def gen_ucd():
|
||||
{c for c in class_maps if c.startswith('M')},
|
||||
'M category (marks)',
|
||||
# See https://github.com/harfbuzz/harfbuzz/issues/169
|
||||
extra_chars=emoji_skin_tone_modifiers | {zwj}
|
||||
extra_chars=emoji_skin_tone_modifiers | {zwj},
|
||||
least_check_return='false'
|
||||
)
|
||||
category_test(
|
||||
'is_ignored_char', p, 'Cc Cf Cs'.split(),
|
||||
'Control characters and non-characters', extra_chars=non_characters, exclude={zwj})
|
||||
'Control characters and non-characters',
|
||||
extra_chars=non_characters, exclude={zwj},
|
||||
ascii_range='false'
|
||||
)
|
||||
category_test('is_word_char', p, {c for c in class_maps if c[0] in 'LN'}, 'L and N categories')
|
||||
category_test('is_CZ_category', p, cz, 'C and Z categories')
|
||||
category_test('is_P_category', p, {c for c in class_maps if c[0] == 'P'}, 'P category (punctuation)')
|
||||
@@ -298,7 +380,9 @@ def gen_ucd():
|
||||
p('}\n')
|
||||
with open('kitty/unicode-data.h') as f:
|
||||
unicode_data = f.read()
|
||||
expected = int(re.search(r'^#define VS15 (\d+)', unicode_data, re.M).group(1))
|
||||
m = re.search(r'^#define VS15 (\d+)', unicode_data, re.M)
|
||||
if m is not None:
|
||||
expected = int(m.group(1))
|
||||
if rmap[0xfe0e] != expected:
|
||||
raise ValueError('The mark for 0xfe0e has changed, you have to update VS15 to {} and VS16 to {} in unicode-data.h'.format(
|
||||
rmap[0xfe0e], rmap[0xfe0f]
|
||||
@@ -307,7 +391,7 @@ def gen_ucd():
|
||||
f.write("url_delimiters = '{}' # noqa".format(''.join(classes_to_regex(cz, exclude='\n'))))
|
||||
|
||||
|
||||
def gen_names():
|
||||
def gen_names() -> None:
|
||||
with create_header('kittens/unicode_input/names.h') as p:
|
||||
mark_to_cp = list(sorted(name_map))
|
||||
cp_to_mark = {cp: m for m, cp in enumerate(mark_to_cp)}
|
||||
@@ -361,29 +445,29 @@ def gen_names():
|
||||
p('}; // }}}\n')
|
||||
|
||||
# The trie
|
||||
p(f'typedef struct {{ uint32_t children_offset; uint32_t match_offset; }} word_trie;\n')
|
||||
all_trie_nodes = []
|
||||
p('typedef struct { uint32_t children_offset; uint32_t match_offset; } word_trie;\n')
|
||||
all_trie_nodes: List['TrieNode'] = [] # noqa
|
||||
|
||||
class TrieNode:
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.match_offset = 0
|
||||
self.children_offset = 0
|
||||
self.children = {}
|
||||
self.children: Dict[int, int] = {}
|
||||
|
||||
def add_letter(self, letter):
|
||||
def add_letter(self, letter: int) -> int:
|
||||
if letter not in self.children:
|
||||
self.children[letter] = len(all_trie_nodes)
|
||||
all_trie_nodes.append(TrieNode())
|
||||
return self.children[letter]
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return f'{{ .children_offset={self.children_offset}, .match_offset={self.match_offset} }}'
|
||||
|
||||
root = TrieNode()
|
||||
all_trie_nodes.append(root)
|
||||
|
||||
def add_word(word_idx, word):
|
||||
def add_word(word_idx: int, word: str) -> None:
|
||||
parent = root
|
||||
for letter in map(ord, word):
|
||||
idx = parent.add_letter(letter)
|
||||
@@ -408,10 +492,10 @@ def gen_names():
|
||||
p('}; // }}}\n')
|
||||
|
||||
|
||||
def gen_wcwidth():
|
||||
seen = set()
|
||||
def gen_wcwidth() -> None:
|
||||
seen: Set[int] = set()
|
||||
|
||||
def add(p, comment, chars_, ret):
|
||||
def add(p: Callable, comment: str, chars_: Union[Set[int], FrozenSet[int]], ret: int) -> None:
|
||||
chars = chars_ - seen
|
||||
seen.update(chars)
|
||||
p(f'\t\t// {comment} ({len(chars)} codepoints)' + ' {{' '{')
|
||||
@@ -421,17 +505,19 @@ def gen_wcwidth():
|
||||
p('\t\t// }}}\n')
|
||||
|
||||
with create_header('kitty/wcwidth-std.h') as p:
|
||||
p('static int\nwcwidth_std(int32_t code) {')
|
||||
p('static inline int\nwcwidth_std(int32_t code) {')
|
||||
p('\tif (LIKELY(0x20 <= code && code <= 0x7e)) return 1;')
|
||||
p('\tswitch(code) {')
|
||||
|
||||
non_printing = class_maps['Cc'] | class_maps['Cf'] | class_maps['Cs']
|
||||
add(p, 'Flags', flag_codepoints, 2)
|
||||
add(p, 'Marks', marks | {0}, 0)
|
||||
add(p, 'Non-printing characters', non_printing, -1)
|
||||
add(p, 'Private use', class_maps['Co'], -3)
|
||||
add(p, 'Text Presentation', emoji_categories['Emoji'] - emoji_categories['Emoji_Presentation'], 1)
|
||||
add(p, 'Text Presentation', narrow_emoji, 1)
|
||||
add(p, 'East Asian ambiguous width', ambiguous, -2)
|
||||
add(p, 'East Asian double width', doublewidth, 2)
|
||||
add(p, 'Emoji Presentation', emoji_categories['Emoji_Presentation'], 2)
|
||||
add(p, 'Emoji Presentation', wide_emoji, 2)
|
||||
|
||||
add(p, 'Not assigned in the unicode character database', not_assigned, -4)
|
||||
|
||||
@@ -439,7 +525,7 @@ def gen_wcwidth():
|
||||
p('\t}')
|
||||
p('\treturn 1;\n}')
|
||||
|
||||
p('static bool\nis_emoji_presentation_base(uint32_t code) {')
|
||||
p('static inline bool\nis_emoji_presentation_base(uint32_t code) {')
|
||||
p('\tswitch(code) {')
|
||||
for spec in get_ranges(list(emoji_presentation_bases)):
|
||||
write_case(spec, p)
|
||||
|
||||
0
glfw/__init__.py
Normal file
0
glfw/__init__.py
Normal file
129
glfw/backend_utils.c
vendored
129
glfw/backend_utils.c
vendored
@@ -8,6 +8,7 @@
|
||||
#define _GNU_SOURCE
|
||||
#include "backend_utils.h"
|
||||
#include "internal.h"
|
||||
#include "memfd.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -333,53 +334,89 @@ pollForEvents(EventLoopData *eld, monotonic_t timeout, watch_callback_func displ
|
||||
return read_ok;
|
||||
}
|
||||
|
||||
// Splits and translates a text/uri-list into separate file paths
|
||||
// NOTE: This function destroys the provided string
|
||||
//
|
||||
char** parseUriList(char* text, int* count)
|
||||
{
|
||||
const char* prefix = "file://";
|
||||
char** paths = NULL;
|
||||
char* line;
|
||||
|
||||
*count = 0;
|
||||
|
||||
while ((line = strtok(text, "\r\n")))
|
||||
{
|
||||
text = NULL;
|
||||
|
||||
if (line[0] == '#')
|
||||
continue;
|
||||
|
||||
if (strncmp(line, prefix, strlen(prefix)) == 0)
|
||||
{
|
||||
line += strlen(prefix);
|
||||
// TODO: Validate hostname
|
||||
while (*line != '/')
|
||||
line++;
|
||||
}
|
||||
|
||||
(*count)++;
|
||||
|
||||
char* path = calloc(strlen(line) + 1, 1);
|
||||
paths = realloc(paths, *count * sizeof(char*));
|
||||
paths[*count - 1] = path;
|
||||
|
||||
while (*line)
|
||||
{
|
||||
if (line[0] == '%' && line[1] && line[2])
|
||||
{
|
||||
const char digits[3] = { line[1], line[2], '\0' };
|
||||
*path = strtol(digits, NULL, 16);
|
||||
line += 2;
|
||||
}
|
||||
else
|
||||
*path = *line;
|
||||
|
||||
path++;
|
||||
line++;
|
||||
// Duplicate a UTF-8 encoded string
|
||||
// but cut it so that it has at most max_length bytes plus the null byte.
|
||||
// This does not take combining characters into account.
|
||||
GLFWAPI char* utf_8_strndup(const char* source, size_t max_length) {
|
||||
if (!source) return NULL;
|
||||
size_t length = strnlen(source, max_length);
|
||||
if (length >= max_length) {
|
||||
for (length = max_length; length > 0; length--) {
|
||||
if ((source[length] & 0xC0) != 0x80) break;
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
char* result = malloc(length + 1);
|
||||
memcpy(result, source, length);
|
||||
result[length] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new, unique, anonymous file of the given size, and
|
||||
* return the file descriptor for it. The file descriptor is set
|
||||
* CLOEXEC. The file is immediately suitable for mmap()'ing
|
||||
* the given size at offset zero.
|
||||
*
|
||||
* The file should not have a permanent backing store like a disk,
|
||||
* but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
|
||||
*
|
||||
* The file name is deleted from the file system.
|
||||
*
|
||||
* The file is suitable for buffer sharing between processes by
|
||||
* transmitting the file descriptor over Unix sockets using the
|
||||
* SCM_RIGHTS methods.
|
||||
*
|
||||
* posix_fallocate() is used to guarantee that disk space is available
|
||||
* for the file at the given size. If disk space is insufficient, errno
|
||||
* is set to ENOSPC. If posix_fallocate() is not supported, program may
|
||||
* receive SIGBUS on accessing mmap()'ed file contents instead.
|
||||
*/
|
||||
int createAnonymousFile(off_t size) {
|
||||
int ret, fd = -1, shm_anon = 0;
|
||||
#ifdef HAS_MEMFD_CREATE
|
||||
fd = memfd_create("glfw-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
||||
if (fd < 0) return -1;
|
||||
// We can add this seal before calling posix_fallocate(), as the file
|
||||
// is currently zero-sized anyway.
|
||||
//
|
||||
// There is also no need to check for the return value, we couldn’t do
|
||||
// anything with it anyway.
|
||||
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
|
||||
#elif defined(SHM_ANON)
|
||||
fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600);
|
||||
if (fd < 0) return -1;
|
||||
shm_anon = 1;
|
||||
#else
|
||||
static const char template[] = "/glfw-shared-XXXXXX";
|
||||
const char* path;
|
||||
char* name;
|
||||
|
||||
path = getenv("XDG_RUNTIME_DIR");
|
||||
if (!path)
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
name = calloc(strlen(path) + sizeof(template), 1);
|
||||
strcpy(name, path);
|
||||
strcat(name, template);
|
||||
|
||||
fd = createTmpfileCloexec(name);
|
||||
|
||||
free(name);
|
||||
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
#endif
|
||||
// posix_fallocate does not work on SHM descriptors
|
||||
ret = shm_anon ? ftruncate(fd, size) : posix_fallocate(fd, 0, size);
|
||||
if (ret != 0)
|
||||
{
|
||||
close(fd);
|
||||
errno = ret;
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
8
glfw/backend_utils.h
vendored
8
glfw/backend_utils.h
vendored
@@ -1,7 +1,7 @@
|
||||
//========================================================================
|
||||
// GLFW 3.4 Wayland - www.glfw.org
|
||||
// GLFW 3.4
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>
|
||||
// Copyright (c) 2014 Kovid Goyal
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include(<sys/eventfd.h>)
|
||||
@@ -94,5 +95,6 @@ int pollForEvents(EventLoopData *eld, monotonic_t timeout, watch_callback_func);
|
||||
unsigned dispatchTimers(EventLoopData *eld);
|
||||
void finalizePollData(EventLoopData *eld);
|
||||
bool initPollData(EventLoopData *eld, int display_fd);
|
||||
char** parseUriList(char* text, int* count);
|
||||
void wakeupEventLoop(EventLoopData *eld);
|
||||
char* utf_8_strndup(const char* source, size_t max_length);
|
||||
int createAnonymousFile(off_t size);
|
||||
|
||||
@@ -175,138 +175,6 @@ static void createMenuBar(void)
|
||||
[NSApp performSelector:setAppleMenuSelector withObject:appMenu];
|
||||
}
|
||||
|
||||
// Create key code translation tables
|
||||
//
|
||||
static void createKeyTables(void)
|
||||
{
|
||||
int keycode;
|
||||
|
||||
memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes));
|
||||
memset(_glfw.ns.key_to_keycode, -1, sizeof(_glfw.ns.key_to_keycode));
|
||||
|
||||
_glfw.ns.keycodes[0x1D] = GLFW_KEY_0;
|
||||
_glfw.ns.keycodes[0x12] = GLFW_KEY_1;
|
||||
_glfw.ns.keycodes[0x13] = GLFW_KEY_2;
|
||||
_glfw.ns.keycodes[0x14] = GLFW_KEY_3;
|
||||
_glfw.ns.keycodes[0x15] = GLFW_KEY_4;
|
||||
_glfw.ns.keycodes[0x17] = GLFW_KEY_5;
|
||||
_glfw.ns.keycodes[0x16] = GLFW_KEY_6;
|
||||
_glfw.ns.keycodes[0x1A] = GLFW_KEY_7;
|
||||
_glfw.ns.keycodes[0x1C] = GLFW_KEY_8;
|
||||
_glfw.ns.keycodes[0x19] = GLFW_KEY_9;
|
||||
_glfw.ns.keycodes[0x00] = GLFW_KEY_A;
|
||||
_glfw.ns.keycodes[0x0B] = GLFW_KEY_B;
|
||||
_glfw.ns.keycodes[0x08] = GLFW_KEY_C;
|
||||
_glfw.ns.keycodes[0x02] = GLFW_KEY_D;
|
||||
_glfw.ns.keycodes[0x0E] = GLFW_KEY_E;
|
||||
_glfw.ns.keycodes[0x03] = GLFW_KEY_F;
|
||||
_glfw.ns.keycodes[0x05] = GLFW_KEY_G;
|
||||
_glfw.ns.keycodes[0x04] = GLFW_KEY_H;
|
||||
_glfw.ns.keycodes[0x22] = GLFW_KEY_I;
|
||||
_glfw.ns.keycodes[0x26] = GLFW_KEY_J;
|
||||
_glfw.ns.keycodes[0x28] = GLFW_KEY_K;
|
||||
_glfw.ns.keycodes[0x25] = GLFW_KEY_L;
|
||||
_glfw.ns.keycodes[0x2E] = GLFW_KEY_M;
|
||||
_glfw.ns.keycodes[0x2D] = GLFW_KEY_N;
|
||||
_glfw.ns.keycodes[0x1F] = GLFW_KEY_O;
|
||||
_glfw.ns.keycodes[0x23] = GLFW_KEY_P;
|
||||
_glfw.ns.keycodes[0x0C] = GLFW_KEY_Q;
|
||||
_glfw.ns.keycodes[0x0F] = GLFW_KEY_R;
|
||||
_glfw.ns.keycodes[0x01] = GLFW_KEY_S;
|
||||
_glfw.ns.keycodes[0x11] = GLFW_KEY_T;
|
||||
_glfw.ns.keycodes[0x20] = GLFW_KEY_U;
|
||||
_glfw.ns.keycodes[0x09] = GLFW_KEY_V;
|
||||
_glfw.ns.keycodes[0x0D] = GLFW_KEY_W;
|
||||
_glfw.ns.keycodes[0x07] = GLFW_KEY_X;
|
||||
_glfw.ns.keycodes[0x10] = GLFW_KEY_Y;
|
||||
_glfw.ns.keycodes[0x06] = GLFW_KEY_Z;
|
||||
|
||||
_glfw.ns.keycodes[0x27] = GLFW_KEY_APOSTROPHE;
|
||||
_glfw.ns.keycodes[0x2A] = GLFW_KEY_BACKSLASH;
|
||||
_glfw.ns.keycodes[0x2B] = GLFW_KEY_COMMA;
|
||||
_glfw.ns.keycodes[0x18] = GLFW_KEY_EQUAL;
|
||||
_glfw.ns.keycodes[0x32] = GLFW_KEY_GRAVE_ACCENT;
|
||||
_glfw.ns.keycodes[0x21] = GLFW_KEY_LEFT_BRACKET;
|
||||
_glfw.ns.keycodes[0x1B] = GLFW_KEY_MINUS;
|
||||
_glfw.ns.keycodes[0x2F] = GLFW_KEY_PERIOD;
|
||||
_glfw.ns.keycodes[0x1E] = GLFW_KEY_RIGHT_BRACKET;
|
||||
_glfw.ns.keycodes[0x29] = GLFW_KEY_SEMICOLON;
|
||||
_glfw.ns.keycodes[0x2C] = GLFW_KEY_SLASH;
|
||||
_glfw.ns.keycodes[0x0A] = GLFW_KEY_WORLD_1;
|
||||
|
||||
_glfw.ns.keycodes[0x33] = GLFW_KEY_BACKSPACE;
|
||||
_glfw.ns.keycodes[0x39] = GLFW_KEY_CAPS_LOCK;
|
||||
_glfw.ns.keycodes[0x75] = GLFW_KEY_DELETE;
|
||||
_glfw.ns.keycodes[0x7D] = GLFW_KEY_DOWN;
|
||||
_glfw.ns.keycodes[0x77] = GLFW_KEY_END;
|
||||
_glfw.ns.keycodes[0x24] = GLFW_KEY_ENTER;
|
||||
_glfw.ns.keycodes[0x35] = GLFW_KEY_ESCAPE;
|
||||
_glfw.ns.keycodes[0x7A] = GLFW_KEY_F1;
|
||||
_glfw.ns.keycodes[0x78] = GLFW_KEY_F2;
|
||||
_glfw.ns.keycodes[0x63] = GLFW_KEY_F3;
|
||||
_glfw.ns.keycodes[0x76] = GLFW_KEY_F4;
|
||||
_glfw.ns.keycodes[0x60] = GLFW_KEY_F5;
|
||||
_glfw.ns.keycodes[0x61] = GLFW_KEY_F6;
|
||||
_glfw.ns.keycodes[0x62] = GLFW_KEY_F7;
|
||||
_glfw.ns.keycodes[0x64] = GLFW_KEY_F8;
|
||||
_glfw.ns.keycodes[0x65] = GLFW_KEY_F9;
|
||||
_glfw.ns.keycodes[0x6D] = GLFW_KEY_F10;
|
||||
_glfw.ns.keycodes[0x67] = GLFW_KEY_F11;
|
||||
_glfw.ns.keycodes[0x6F] = GLFW_KEY_F12;
|
||||
_glfw.ns.keycodes[0x69] = GLFW_KEY_F13;
|
||||
_glfw.ns.keycodes[0x6B] = GLFW_KEY_F14;
|
||||
_glfw.ns.keycodes[0x71] = GLFW_KEY_F15;
|
||||
_glfw.ns.keycodes[0x6A] = GLFW_KEY_F16;
|
||||
_glfw.ns.keycodes[0x40] = GLFW_KEY_F17;
|
||||
_glfw.ns.keycodes[0x4F] = GLFW_KEY_F18;
|
||||
_glfw.ns.keycodes[0x50] = GLFW_KEY_F19;
|
||||
_glfw.ns.keycodes[0x5A] = GLFW_KEY_F20;
|
||||
_glfw.ns.keycodes[0x73] = GLFW_KEY_HOME;
|
||||
_glfw.ns.keycodes[0x72] = GLFW_KEY_INSERT;
|
||||
_glfw.ns.keycodes[0x7B] = GLFW_KEY_LEFT;
|
||||
_glfw.ns.keycodes[0x3A] = GLFW_KEY_LEFT_ALT;
|
||||
_glfw.ns.keycodes[0x3B] = GLFW_KEY_LEFT_CONTROL;
|
||||
_glfw.ns.keycodes[0x38] = GLFW_KEY_LEFT_SHIFT;
|
||||
_glfw.ns.keycodes[0x37] = GLFW_KEY_LEFT_SUPER;
|
||||
_glfw.ns.keycodes[0x6E] = GLFW_KEY_MENU;
|
||||
_glfw.ns.keycodes[0x47] = GLFW_KEY_NUM_LOCK;
|
||||
_glfw.ns.keycodes[0x79] = GLFW_KEY_PAGE_DOWN;
|
||||
_glfw.ns.keycodes[0x74] = GLFW_KEY_PAGE_UP;
|
||||
_glfw.ns.keycodes[0x7C] = GLFW_KEY_RIGHT;
|
||||
_glfw.ns.keycodes[0x3D] = GLFW_KEY_RIGHT_ALT;
|
||||
_glfw.ns.keycodes[0x3E] = GLFW_KEY_RIGHT_CONTROL;
|
||||
_glfw.ns.keycodes[0x3C] = GLFW_KEY_RIGHT_SHIFT;
|
||||
_glfw.ns.keycodes[0x36] = GLFW_KEY_RIGHT_SUPER;
|
||||
_glfw.ns.keycodes[0x31] = GLFW_KEY_SPACE;
|
||||
_glfw.ns.keycodes[0x30] = GLFW_KEY_TAB;
|
||||
_glfw.ns.keycodes[0x7E] = GLFW_KEY_UP;
|
||||
|
||||
_glfw.ns.keycodes[0x52] = GLFW_KEY_KP_0;
|
||||
_glfw.ns.keycodes[0x53] = GLFW_KEY_KP_1;
|
||||
_glfw.ns.keycodes[0x54] = GLFW_KEY_KP_2;
|
||||
_glfw.ns.keycodes[0x55] = GLFW_KEY_KP_3;
|
||||
_glfw.ns.keycodes[0x56] = GLFW_KEY_KP_4;
|
||||
_glfw.ns.keycodes[0x57] = GLFW_KEY_KP_5;
|
||||
_glfw.ns.keycodes[0x58] = GLFW_KEY_KP_6;
|
||||
_glfw.ns.keycodes[0x59] = GLFW_KEY_KP_7;
|
||||
_glfw.ns.keycodes[0x5B] = GLFW_KEY_KP_8;
|
||||
_glfw.ns.keycodes[0x5C] = GLFW_KEY_KP_9;
|
||||
_glfw.ns.keycodes[0x45] = GLFW_KEY_KP_ADD;
|
||||
_glfw.ns.keycodes[0x41] = GLFW_KEY_KP_DECIMAL;
|
||||
_glfw.ns.keycodes[0x4B] = GLFW_KEY_KP_DIVIDE;
|
||||
_glfw.ns.keycodes[0x4C] = GLFW_KEY_KP_ENTER;
|
||||
_glfw.ns.keycodes[0x51] = GLFW_KEY_KP_EQUAL;
|
||||
_glfw.ns.keycodes[0x43] = GLFW_KEY_KP_MULTIPLY;
|
||||
_glfw.ns.keycodes[0x4E] = GLFW_KEY_KP_SUBTRACT;
|
||||
|
||||
for (keycode = 0; keycode < 256; keycode++)
|
||||
{
|
||||
// Store the reverse translation for faster key name lookup
|
||||
if (_glfw.ns.keycodes[keycode] >= 0)
|
||||
_glfw.ns.key_to_keycode[_glfw.ns.keycodes[keycode]] = keycode;
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve Unicode data for the current keyboard layout
|
||||
//
|
||||
static bool updateUnicodeDataNS(void)
|
||||
@@ -424,11 +292,7 @@ display_reconfigured(CGDirectDisplayID display UNUSED, CGDisplayChangeSummaryFla
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
|
||||
{
|
||||
(void)sender;
|
||||
_GLFWwindow* window;
|
||||
|
||||
for (window = _glfw.windowListHead; window; window = window->next)
|
||||
_glfwInputWindowCloseRequest(window);
|
||||
|
||||
if (_glfw.callbacks.application_close) _glfw.callbacks.application_close(0);
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
|
||||
@@ -482,10 +346,40 @@ static GLFWapplicationwillfinishlaunchingfun finish_launching_callback = NULL;
|
||||
finish_launching_callback();
|
||||
}
|
||||
|
||||
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename {
|
||||
(void)theApplication;
|
||||
if (!filename || !_glfw.ns.file_open_callback) return NO;
|
||||
const char *path = NULL;
|
||||
@try {
|
||||
path = [[NSFileManager defaultManager] fileSystemRepresentationWithPath: filename];
|
||||
} @catch(NSException *exc) {
|
||||
NSLog(@"Converting openFile filename: %@ failed with error: %@", filename, exc.reason);
|
||||
return NO;
|
||||
}
|
||||
if (!path) return NO;
|
||||
return _glfw.ns.file_open_callback(path);
|
||||
}
|
||||
|
||||
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames {
|
||||
(void)sender;
|
||||
if (!_glfw.ns.file_open_callback || !filenames) return;
|
||||
for (id x in filenames) {
|
||||
NSString *filename = x;
|
||||
const char *path = NULL;
|
||||
@try {
|
||||
path = [[NSFileManager defaultManager] fileSystemRepresentationWithPath: filename];
|
||||
} @catch(NSException *exc) {
|
||||
NSLog(@"Converting openFiles filename: %@ failed with error: %@", filename, exc.reason);
|
||||
}
|
||||
if (path) _glfw.ns.file_open_callback(path);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)notification
|
||||
{
|
||||
(void)notification;
|
||||
[NSApp stop:nil];
|
||||
if (_glfw.ns.file_open_callback) _glfw.ns.file_open_callback(":cocoa::application launched::");
|
||||
|
||||
CGDisplayRegisterReconfigurationCallback(display_reconfigured, NULL);
|
||||
_glfwCocoaPostEmptyEvent();
|
||||
@@ -652,8 +546,12 @@ int _glfwPlatformInit(void)
|
||||
if (_glfw.hints.init.ns.chdir)
|
||||
changeToResourcesDirectory();
|
||||
|
||||
// Press and Hold prevents some keys from emitting repeated characters
|
||||
NSDictionary* defaults = @{@"ApplePressAndHoldEnabled":@NO};
|
||||
NSDictionary* defaults = @{
|
||||
// Press and Hold prevents some keys from emitting repeated characters
|
||||
@"ApplePressAndHoldEnabled": @NO,
|
||||
// Dont generate openFile events from command line arguments
|
||||
@"NSTreatUnknownArgumentsAsOpen": @"NO",
|
||||
};
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
|
||||
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
@@ -662,8 +560,6 @@ int _glfwPlatformInit(void)
|
||||
name:NSTextInputContextKeyboardSelectionDidChangeNotification
|
||||
object:nil];
|
||||
|
||||
createKeyTables();
|
||||
|
||||
_glfw.ns.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
if (!_glfw.ns.eventSource)
|
||||
return false;
|
||||
@@ -673,8 +569,6 @@ int _glfwPlatformInit(void)
|
||||
if (!initializeTIS())
|
||||
return false;
|
||||
|
||||
_glfwInitJoysticksNS();
|
||||
|
||||
_glfwPollMonitorsNS();
|
||||
return true;
|
||||
|
||||
@@ -727,7 +621,6 @@ void _glfwPlatformTerminate(void)
|
||||
free(_glfw.ns.clipboardString);
|
||||
|
||||
_glfwTerminateNSGL();
|
||||
_glfwTerminateJoysticksNS();
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
4
glfw/cocoa_joystick.h
vendored
4
glfw/cocoa_joystick.h
vendored
@@ -44,7 +44,3 @@ typedef struct _GLFWjoystickNS
|
||||
CFMutableArrayRef hats;
|
||||
} _GLFWjoystickNS;
|
||||
|
||||
|
||||
void _glfwInitJoysticksNS(void);
|
||||
void _glfwTerminateJoysticksNS(void);
|
||||
|
||||
|
||||
@@ -233,6 +233,19 @@ static void matchCallback(void* context UNUSED,
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (page == kHIDPage_Simulation)
|
||||
{
|
||||
switch (usage)
|
||||
{
|
||||
case kHIDUsage_Sim_Accelerator:
|
||||
case kHIDUsage_Sim_Brake:
|
||||
case kHIDUsage_Sim_Throttle:
|
||||
case kHIDUsage_Sim_Rudder:
|
||||
case kHIDUsage_Sim_Steering:
|
||||
target = axes;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (page == kHIDPage_Button || page == kHIDPage_Consumer)
|
||||
target = buttons;
|
||||
|
||||
@@ -291,12 +304,10 @@ static void removeCallback(void* context UNUSED,
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialize joystick interface
|
||||
//
|
||||
void _glfwInitJoysticksNS(void)
|
||||
bool _glfwPlatformInitJoysticks(void)
|
||||
{
|
||||
CFMutableArrayRef matching;
|
||||
const long usages[] =
|
||||
@@ -315,7 +326,7 @@ void _glfwInitJoysticksNS(void)
|
||||
if (!matching)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(usages) / sizeof(long); i++)
|
||||
@@ -370,26 +381,24 @@ void _glfwInitJoysticksNS(void)
|
||||
// Execute the run loop once in order to register any initially-attached
|
||||
// joysticks
|
||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Close all opened joystick handles
|
||||
//
|
||||
void _glfwTerminateJoysticksNS(void)
|
||||
void _glfwPlatformTerminateJoysticks(void)
|
||||
{
|
||||
int jid;
|
||||
|
||||
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
|
||||
closeJoystick(_glfw.joysticks + jid);
|
||||
|
||||
CFRelease(_glfw.ns.hidManager);
|
||||
_glfw.ns.hidManager = NULL;
|
||||
if (_glfw.ns.hidManager)
|
||||
{
|
||||
CFRelease(_glfw.ns.hidManager);
|
||||
_glfw.ns.hidManager = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode)
|
||||
{
|
||||
if (mode & _GLFW_POLL_AXES)
|
||||
|
||||
@@ -89,7 +89,7 @@ static char* getDisplayName(CGDirectDisplayID displayID)
|
||||
if (!service)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to find service port for display");
|
||||
"Cocoa: Failed to find service port for display, cannot get its name, using Unknown");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -236,26 +236,71 @@ static bool refreshMonitorScreen(_GLFWmonitor* monitor)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns a fallback refresh rate for when Core Graphics says it is zero
|
||||
// Returns the display refresh rate queried from the I/O registry
|
||||
//
|
||||
static double getFallbackRefreshRate(_GLFWmonitor* monitor)
|
||||
static double getFallbackRefreshRate(CGDirectDisplayID displayID)
|
||||
{
|
||||
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(monitor->ns.displayID);
|
||||
double refreshRate = CGDisplayModeGetRefreshRate(mode);
|
||||
CGDisplayModeRelease(mode);
|
||||
double refreshRate = 60.0;
|
||||
|
||||
if (refreshRate == 0.0)
|
||||
io_iterator_t it;
|
||||
io_service_t service;
|
||||
|
||||
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
|
||||
IOServiceMatching("IOFramebuffer"),
|
||||
&it) != 0)
|
||||
{
|
||||
CVDisplayLinkRef link = NULL;
|
||||
CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link);
|
||||
|
||||
const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link);
|
||||
if (!(time.flags & kCVTimeIsIndefinite))
|
||||
refreshRate = (int) (time.timeScale / (double) time.timeValue);
|
||||
|
||||
CVDisplayLinkRelease(link);
|
||||
return refreshRate;
|
||||
}
|
||||
|
||||
while ((service = IOIteratorNext(it)) != 0)
|
||||
{
|
||||
const CFNumberRef indexRef =
|
||||
IORegistryEntryCreateCFProperty(service,
|
||||
CFSTR("IOFramebufferOpenGLIndex"),
|
||||
kCFAllocatorDefault,
|
||||
kNilOptions);
|
||||
if (!indexRef)
|
||||
continue;
|
||||
|
||||
uint32_t index = 0;
|
||||
CFNumberGetValue(indexRef, kCFNumberIntType, &index);
|
||||
CFRelease(indexRef);
|
||||
|
||||
if (CGOpenGLDisplayMaskToDisplayID(1 << index) != displayID)
|
||||
continue;
|
||||
|
||||
const CFNumberRef clockRef =
|
||||
IORegistryEntryCreateCFProperty(service,
|
||||
CFSTR("IOFBCurrentPixelClock"),
|
||||
kCFAllocatorDefault,
|
||||
kNilOptions);
|
||||
const CFNumberRef countRef =
|
||||
IORegistryEntryCreateCFProperty(service,
|
||||
CFSTR("IOFBCurrentPixelCount"),
|
||||
kCFAllocatorDefault,
|
||||
kNilOptions);
|
||||
|
||||
uint32_t clock = 0, count = 0;
|
||||
|
||||
if (clockRef)
|
||||
{
|
||||
CFNumberGetValue(clockRef, kCFNumberIntType, &clock);
|
||||
CFRelease(clockRef);
|
||||
}
|
||||
|
||||
if (countRef)
|
||||
{
|
||||
CFNumberGetValue(countRef, kCFNumberIntType, &count);
|
||||
CFRelease(countRef);
|
||||
}
|
||||
|
||||
if (clock > 0 && count > 0)
|
||||
refreshRate = clock / (double) count;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
IOObjectRelease(it);
|
||||
return refreshRate;
|
||||
}
|
||||
|
||||
@@ -281,7 +326,7 @@ static CVReturn displayLinkCallback(
|
||||
const CVTimeStamp* now UNUSED, const CVTimeStamp* outputTime UNUSED,
|
||||
CVOptionFlags flagsIn UNUSED, CVOptionFlags* flagsOut UNUSED, void* userInfo)
|
||||
{
|
||||
CGDirectDisplayID displayID = (CGDirectDisplayID)userInfo;
|
||||
CGDirectDisplayID displayID = (uintptr_t)userInfo;
|
||||
NSNumber *arg = [NSNumber numberWithUnsignedInt:displayID];
|
||||
[NSApp performSelectorOnMainThread:@selector(render_frame_received:) withObject:arg waitUntilDone:NO];
|
||||
[arg release];
|
||||
@@ -351,11 +396,15 @@ void _glfwPollMonitorsNS(void)
|
||||
_GLFWmonitor* monitor = _glfwAllocMonitor(name, (int)size.width, (int)size.height);
|
||||
monitor->ns.displayID = displays[i];
|
||||
monitor->ns.unitNumber = unitNumber;
|
||||
monitor->ns.fallbackRefreshRate = getFallbackRefreshRate(monitor);
|
||||
createDisplayLink(monitor->ns.displayID);
|
||||
|
||||
free(name);
|
||||
|
||||
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displays[i]);
|
||||
if (CGDisplayModeGetRefreshRate(mode) == 0.0)
|
||||
monitor->ns.fallbackRefreshRate = getFallbackRefreshRate(displays[i]);
|
||||
CGDisplayModeRelease(mode);
|
||||
|
||||
_glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_LAST);
|
||||
}
|
||||
|
||||
@@ -480,7 +529,6 @@ void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor,
|
||||
*width = (int)frameRect.size.width;
|
||||
if (height)
|
||||
*height = (int)frameRect.size.height;
|
||||
|
||||
}
|
||||
|
||||
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
|
||||
|
||||
13
glfw/cocoa_platform.h
vendored
13
glfw/cocoa_platform.h
vendored
@@ -67,6 +67,7 @@ typedef void* CVDisplayLinkRef;
|
||||
|
||||
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int, unsigned long);
|
||||
typedef bool (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef bool (* GLFWhandlefileopen)(const char*);
|
||||
typedef void (* GLFWapplicationwillfinishlaunchingfun)(void);
|
||||
typedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
|
||||
@@ -96,16 +97,11 @@ typedef VkResult (APIENTRY *PFN_vkCreateMetalSurfaceEXT)(VkInstance,const VkMeta
|
||||
#include "posix_thread.h"
|
||||
#include "cocoa_joystick.h"
|
||||
#include "nsgl_context.h"
|
||||
#include "egl_context.h"
|
||||
#include "osmesa_context.h"
|
||||
|
||||
#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
|
||||
#define _glfw_dlclose(handle) dlclose(handle)
|
||||
#define _glfw_dlsym(handle, name) dlsym(handle, name)
|
||||
|
||||
#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->ns.view)
|
||||
#define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY
|
||||
|
||||
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNS ns
|
||||
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns
|
||||
#define _GLFW_PLATFORM_LIBRARY_TIMER_STATE _GLFWtimerNS ns
|
||||
@@ -133,6 +129,9 @@ typedef struct _GLFWwindowNS
|
||||
|
||||
bool maximized;
|
||||
bool retina;
|
||||
bool in_traditional_fullscreen;
|
||||
bool titlebar_hidden;
|
||||
unsigned long pre_full_screen_style_mask;
|
||||
|
||||
// Cached window properties to filter out duplicate events
|
||||
int width, height;
|
||||
@@ -180,8 +179,6 @@ typedef struct _GLFWlibraryNS
|
||||
|
||||
char keyName[64];
|
||||
char text[256];
|
||||
short int keycodes[256];
|
||||
short int key_to_keycode[GLFW_KEY_LAST + 1];
|
||||
char* clipboardString;
|
||||
CGPoint cascadePoint;
|
||||
// Where to place the cursor when re-enabled
|
||||
@@ -201,6 +198,8 @@ typedef struct _GLFWlibraryNS
|
||||
_GLFWDisplayLinkNS entries[256];
|
||||
size_t count;
|
||||
} displayLinks;
|
||||
// the callback to handle file open events
|
||||
GLFWhandlefileopen file_open_callback;
|
||||
|
||||
} _GLFWlibraryNS;
|
||||
|
||||
|
||||
1092
glfw/cocoa_window.m
1092
glfw/cocoa_window.m
File diff suppressed because it is too large
Load Diff
168
glfw/context.c
vendored
168
glfw/context.c
vendored
@@ -173,174 +173,6 @@ bool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Chooses the framebuffer config that best matches the desired one
|
||||
//
|
||||
const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired,
|
||||
const _GLFWfbconfig* alternatives,
|
||||
unsigned int count)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int missing, leastMissing = UINT_MAX;
|
||||
unsigned int colorDiff, leastColorDiff = UINT_MAX;
|
||||
unsigned int extraDiff, leastExtraDiff = UINT_MAX;
|
||||
const _GLFWfbconfig* current;
|
||||
const _GLFWfbconfig* closest = NULL;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
current = alternatives + i;
|
||||
|
||||
if (desired->stereo > 0 && current->stereo == 0)
|
||||
{
|
||||
// Stereo is a hard constraint
|
||||
continue;
|
||||
}
|
||||
|
||||
if (desired->doublebuffer != current->doublebuffer)
|
||||
{
|
||||
// Double buffering is a hard constraint
|
||||
continue;
|
||||
}
|
||||
|
||||
// Count number of missing buffers
|
||||
{
|
||||
missing = 0;
|
||||
|
||||
if (desired->alphaBits > 0 && current->alphaBits == 0)
|
||||
missing++;
|
||||
|
||||
if (desired->depthBits > 0 && current->depthBits == 0)
|
||||
missing++;
|
||||
|
||||
if (desired->stencilBits > 0 && current->stencilBits == 0)
|
||||
missing++;
|
||||
|
||||
if (desired->auxBuffers > 0 &&
|
||||
current->auxBuffers < desired->auxBuffers)
|
||||
{
|
||||
missing += desired->auxBuffers - current->auxBuffers;
|
||||
}
|
||||
|
||||
if (desired->samples > 0 && current->samples == 0)
|
||||
{
|
||||
// Technically, several multisampling buffers could be
|
||||
// involved, but that's a lower level implementation detail and
|
||||
// not important to us here, so we count them as one
|
||||
missing++;
|
||||
}
|
||||
|
||||
if (desired->transparent != current->transparent)
|
||||
missing++;
|
||||
}
|
||||
|
||||
// These polynomials make many small channel size differences matter
|
||||
// less than one large channel size difference
|
||||
|
||||
// Calculate color channel size difference value
|
||||
{
|
||||
colorDiff = 0;
|
||||
|
||||
if (desired->redBits != GLFW_DONT_CARE)
|
||||
{
|
||||
colorDiff += (desired->redBits - current->redBits) *
|
||||
(desired->redBits - current->redBits);
|
||||
}
|
||||
|
||||
if (desired->greenBits != GLFW_DONT_CARE)
|
||||
{
|
||||
colorDiff += (desired->greenBits - current->greenBits) *
|
||||
(desired->greenBits - current->greenBits);
|
||||
}
|
||||
|
||||
if (desired->blueBits != GLFW_DONT_CARE)
|
||||
{
|
||||
colorDiff += (desired->blueBits - current->blueBits) *
|
||||
(desired->blueBits - current->blueBits);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate non-color channel size difference value
|
||||
{
|
||||
extraDiff = 0;
|
||||
|
||||
if (desired->alphaBits != GLFW_DONT_CARE)
|
||||
{
|
||||
extraDiff += (desired->alphaBits - current->alphaBits) *
|
||||
(desired->alphaBits - current->alphaBits);
|
||||
}
|
||||
|
||||
if (desired->depthBits != GLFW_DONT_CARE)
|
||||
{
|
||||
extraDiff += (desired->depthBits - current->depthBits) *
|
||||
(desired->depthBits - current->depthBits);
|
||||
}
|
||||
|
||||
if (desired->stencilBits != GLFW_DONT_CARE)
|
||||
{
|
||||
extraDiff += (desired->stencilBits - current->stencilBits) *
|
||||
(desired->stencilBits - current->stencilBits);
|
||||
}
|
||||
|
||||
if (desired->accumRedBits != GLFW_DONT_CARE)
|
||||
{
|
||||
extraDiff += (desired->accumRedBits - current->accumRedBits) *
|
||||
(desired->accumRedBits - current->accumRedBits);
|
||||
}
|
||||
|
||||
if (desired->accumGreenBits != GLFW_DONT_CARE)
|
||||
{
|
||||
extraDiff += (desired->accumGreenBits - current->accumGreenBits) *
|
||||
(desired->accumGreenBits - current->accumGreenBits);
|
||||
}
|
||||
|
||||
if (desired->accumBlueBits != GLFW_DONT_CARE)
|
||||
{
|
||||
extraDiff += (desired->accumBlueBits - current->accumBlueBits) *
|
||||
(desired->accumBlueBits - current->accumBlueBits);
|
||||
}
|
||||
|
||||
if (desired->accumAlphaBits != GLFW_DONT_CARE)
|
||||
{
|
||||
extraDiff += (desired->accumAlphaBits - current->accumAlphaBits) *
|
||||
(desired->accumAlphaBits - current->accumAlphaBits);
|
||||
}
|
||||
|
||||
if (desired->samples != GLFW_DONT_CARE)
|
||||
{
|
||||
extraDiff += (desired->samples - current->samples) *
|
||||
(desired->samples - current->samples);
|
||||
}
|
||||
|
||||
if (desired->sRGB && !current->sRGB)
|
||||
extraDiff++;
|
||||
}
|
||||
|
||||
// Figure out if the current one is better than the best one found so far
|
||||
// Least number of missing buffers is the most important heuristic,
|
||||
// then color buffer size match and lastly size match for other buffers
|
||||
|
||||
if (missing < leastMissing)
|
||||
closest = current;
|
||||
else if (missing == leastMissing)
|
||||
{
|
||||
if ((colorDiff < leastColorDiff) ||
|
||||
(colorDiff == leastColorDiff && extraDiff < leastExtraDiff))
|
||||
{
|
||||
closest = current;
|
||||
}
|
||||
}
|
||||
|
||||
if (current == closest)
|
||||
{
|
||||
leastMissing = missing;
|
||||
leastColorDiff = colorDiff;
|
||||
leastExtraDiff = extraDiff;
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
// Retrieves the attributes of the current context
|
||||
//
|
||||
bool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
|
||||
182
glfw/egl_context.c
vendored
182
glfw/egl_context.c
vendored
@@ -76,6 +76,7 @@ static const char* getEGLErrorString(EGLint error)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _GLFW_X11
|
||||
// Returns the specified attribute of the specified EGLConfig
|
||||
//
|
||||
static int getEGLConfigAttrib(EGLConfig config, int attrib)
|
||||
@@ -84,6 +85,7 @@ static int getEGLConfigAttrib(EGLConfig config, int attrib)
|
||||
eglGetConfigAttrib(_glfw.egl.display, config, attrib, &value);
|
||||
return value;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Return the EGLConfig most closely matching the specified hints
|
||||
//
|
||||
@@ -91,39 +93,44 @@ static bool chooseEGLConfig(const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* desired,
|
||||
EGLConfig* result)
|
||||
{
|
||||
EGLConfig* nativeConfigs;
|
||||
_GLFWfbconfig* usableConfigs;
|
||||
const _GLFWfbconfig* closest;
|
||||
int i, nativeCount, usableCount;
|
||||
EGLConfig configs[512];
|
||||
int i = 0, nativeCount = 0, ans_idx = 0;
|
||||
EGLint attributes[64];
|
||||
#define ATTR(k, v) { attributes[i++] = k; attributes[i++] = v; }
|
||||
ATTR(EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER);
|
||||
ATTR(EGL_SURFACE_TYPE, EGL_WINDOW_BIT);
|
||||
if (ctxconfig->client == GLFW_OPENGL_ES_API) {
|
||||
if (ctxconfig->major == 1) ATTR(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT)
|
||||
else ATTR(EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT);
|
||||
}
|
||||
else if (ctxconfig->client == GLFW_OPENGL_API) ATTR(EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT);
|
||||
if (desired->samples > 0) ATTR(EGL_SAMPLES, desired->samples);
|
||||
if (desired->depthBits > 0) ATTR(EGL_DEPTH_SIZE, desired->depthBits);
|
||||
if (desired->stencilBits > 0) ATTR(EGL_STENCIL_SIZE, desired->stencilBits);
|
||||
if (desired->redBits > 0) ATTR(EGL_RED_SIZE, desired->redBits);
|
||||
if (desired->greenBits > 0) ATTR(EGL_GREEN_SIZE, desired->greenBits);
|
||||
if (desired->blueBits > 0) ATTR(EGL_BLUE_SIZE, desired->blueBits);
|
||||
if (desired->alphaBits > 0) ATTR(EGL_ALPHA_SIZE, desired->alphaBits);
|
||||
ATTR(EGL_NONE, EGL_NONE);
|
||||
#undef ATTR
|
||||
if (!eglChooseConfig(_glfw.egl.display, attributes, configs, sizeof(configs)/sizeof(configs[0]), &nativeCount)) {
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "EGL: eglChooseConfig failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount);
|
||||
if (!nativeCount)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "EGL: No EGLConfigs returned");
|
||||
return false;
|
||||
}
|
||||
|
||||
nativeConfigs = calloc(nativeCount, sizeof(EGLConfig));
|
||||
eglGetConfigs(_glfw.egl.display, nativeConfigs, nativeCount, &nativeCount);
|
||||
|
||||
usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig));
|
||||
usableCount = 0;
|
||||
|
||||
for (i = 0; i < nativeCount; i++)
|
||||
{
|
||||
const EGLConfig n = nativeConfigs[i];
|
||||
_GLFWfbconfig* u = usableConfigs + usableCount;
|
||||
|
||||
// Only consider RGB(A) EGLConfigs
|
||||
if (getEGLConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) != EGL_RGB_BUFFER)
|
||||
continue;
|
||||
|
||||
// Only consider window EGLConfigs
|
||||
if (!(getEGLConfigAttrib(n, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT))
|
||||
continue;
|
||||
|
||||
#if defined(_GLFW_X11)
|
||||
{
|
||||
const EGLConfig n = configs[i];
|
||||
XVisualInfo vi = {0};
|
||||
|
||||
// Only consider EGLConfigs with associated Visuals
|
||||
@@ -138,55 +145,18 @@ static bool chooseEGLConfig(const _GLFWctxconfig* ctxconfig,
|
||||
XGetVisualInfo(_glfw.x11.display, VisualIDMask, &vi, &count);
|
||||
if (vis)
|
||||
{
|
||||
u->transparent = _glfwIsVisualTransparentX11(vis[0].visual);
|
||||
bool transparent = _glfwIsVisualTransparentX11(vis[0].visual);
|
||||
XFree(vis);
|
||||
if (!transparent) continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // _GLFW_X11
|
||||
|
||||
if (ctxconfig->client == GLFW_OPENGL_ES_API)
|
||||
{
|
||||
if (ctxconfig->major == 1)
|
||||
{
|
||||
if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES_BIT))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (ctxconfig->client == GLFW_OPENGL_API)
|
||||
{
|
||||
if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_BIT))
|
||||
continue;
|
||||
}
|
||||
|
||||
u->redBits = getEGLConfigAttrib(n, EGL_RED_SIZE);
|
||||
u->greenBits = getEGLConfigAttrib(n, EGL_GREEN_SIZE);
|
||||
u->blueBits = getEGLConfigAttrib(n, EGL_BLUE_SIZE);
|
||||
|
||||
u->alphaBits = getEGLConfigAttrib(n, EGL_ALPHA_SIZE);
|
||||
u->depthBits = getEGLConfigAttrib(n, EGL_DEPTH_SIZE);
|
||||
u->stencilBits = getEGLConfigAttrib(n, EGL_STENCIL_SIZE);
|
||||
|
||||
u->samples = getEGLConfigAttrib(n, EGL_SAMPLES);
|
||||
u->doublebuffer = true;
|
||||
|
||||
u->handle = (uintptr_t) n;
|
||||
usableCount++;
|
||||
ans_idx = i;
|
||||
break;
|
||||
}
|
||||
|
||||
closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount);
|
||||
if (closest)
|
||||
*result = (EGLConfig) closest->handle;
|
||||
|
||||
free(nativeConfigs);
|
||||
free(usableConfigs);
|
||||
|
||||
return closest != NULL;
|
||||
*result = configs[ans_idx];
|
||||
return true;
|
||||
}
|
||||
|
||||
static void makeContextCurrentEGL(_GLFWwindow* window)
|
||||
@@ -303,6 +273,8 @@ static void destroyContextEGL(_GLFWwindow* window)
|
||||
bool _glfwInitEGL(void)
|
||||
{
|
||||
int i;
|
||||
EGLint* attribs = NULL;
|
||||
const char* extensions;
|
||||
const char* sonames[] =
|
||||
{
|
||||
#if defined(_GLFW_EGL_LIBRARY)
|
||||
@@ -340,6 +312,7 @@ bool _glfwInitEGL(void)
|
||||
|
||||
glfw_dlsym(_glfw.egl.GetConfigAttrib, _glfw.egl.handle, "eglGetConfigAttrib");
|
||||
glfw_dlsym(_glfw.egl.GetConfigs, _glfw.egl.handle, "eglGetConfigs");
|
||||
glfw_dlsym(_glfw.egl.ChooseConfig, _glfw.egl.handle, "eglChooseConfig");
|
||||
glfw_dlsym(_glfw.egl.GetDisplay, _glfw.egl.handle, "eglGetDisplay");
|
||||
glfw_dlsym(_glfw.egl.GetError, _glfw.egl.handle, "eglGetError");
|
||||
glfw_dlsym(_glfw.egl.Initialize, _glfw.egl.handle, "eglInitialize");
|
||||
@@ -357,6 +330,7 @@ bool _glfwInitEGL(void)
|
||||
|
||||
if (!_glfw.egl.GetConfigAttrib ||
|
||||
!_glfw.egl.GetConfigs ||
|
||||
!_glfw.egl.ChooseConfig ||
|
||||
!_glfw.egl.GetDisplay ||
|
||||
!_glfw.egl.GetError ||
|
||||
!_glfw.egl.Initialize ||
|
||||
@@ -379,7 +353,51 @@ bool _glfwInitEGL(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
_glfw.egl.display = eglGetDisplay(_GLFW_EGL_NATIVE_DISPLAY);
|
||||
extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
||||
if (extensions && eglGetError() == EGL_SUCCESS)
|
||||
_glfw.egl.EXT_client_extensions = true;
|
||||
|
||||
if (_glfw.egl.EXT_client_extensions)
|
||||
{
|
||||
_glfw.egl.EXT_platform_base =
|
||||
_glfwStringInExtensionString("EGL_EXT_platform_base", extensions);
|
||||
_glfw.egl.EXT_platform_x11 =
|
||||
_glfwStringInExtensionString("EGL_EXT_platform_x11", extensions);
|
||||
_glfw.egl.EXT_platform_wayland =
|
||||
_glfwStringInExtensionString("EGL_EXT_platform_wayland", extensions);
|
||||
_glfw.egl.ANGLE_platform_angle =
|
||||
_glfwStringInExtensionString("EGL_ANGLE_platform_angle", extensions);
|
||||
_glfw.egl.ANGLE_platform_angle_opengl =
|
||||
_glfwStringInExtensionString("EGL_ANGLE_platform_angle_opengl", extensions);
|
||||
_glfw.egl.ANGLE_platform_angle_d3d =
|
||||
_glfwStringInExtensionString("EGL_ANGLE_platform_angle_d3d", extensions);
|
||||
_glfw.egl.ANGLE_platform_angle_vulkan =
|
||||
_glfwStringInExtensionString("EGL_ANGLE_platform_angle_vulkan", extensions);
|
||||
_glfw.egl.ANGLE_platform_angle_metal =
|
||||
_glfwStringInExtensionString("EGL_ANGLE_platform_angle_metal", extensions);
|
||||
}
|
||||
|
||||
if (_glfw.egl.EXT_platform_base)
|
||||
{
|
||||
_glfw.egl.GetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)
|
||||
eglGetProcAddress("eglGetPlatformDisplayEXT");
|
||||
_glfw.egl.CreatePlatformWindowSurfaceEXT = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)
|
||||
eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
|
||||
}
|
||||
|
||||
_glfw.egl.platform = _glfwPlatformGetEGLPlatform(&attribs);
|
||||
if (_glfw.egl.platform)
|
||||
{
|
||||
_glfw.egl.display =
|
||||
eglGetPlatformDisplayEXT(_glfw.egl.platform,
|
||||
_glfwPlatformGetEGLNativeDisplay(),
|
||||
attribs);
|
||||
}
|
||||
else
|
||||
_glfw.egl.display = eglGetDisplay(_glfwPlatformGetEGLNativeDisplay());
|
||||
|
||||
free(attribs);
|
||||
|
||||
if (_glfw.egl.display == EGL_NO_DISPLAY)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
@@ -447,6 +465,7 @@ bool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
EGLint attribs[40];
|
||||
EGLConfig config;
|
||||
EGLContext share = NULL;
|
||||
EGLNativeWindowType native;
|
||||
int index = 0;
|
||||
|
||||
if (!_glfw.egl.display)
|
||||
@@ -572,23 +591,30 @@ bool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
}
|
||||
|
||||
// Set up attributes for surface creation
|
||||
index = 0;
|
||||
|
||||
if (fbconfig->sRGB)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
if (fbconfig->sRGB)
|
||||
{
|
||||
if (_glfw.egl.KHR_gl_colorspace)
|
||||
setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR);
|
||||
}
|
||||
|
||||
setAttrib(EGL_NONE, EGL_NONE);
|
||||
if (_glfw.egl.KHR_gl_colorspace)
|
||||
setAttrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR);
|
||||
}
|
||||
|
||||
setAttrib(EGL_NONE, EGL_NONE);
|
||||
|
||||
native = _glfwPlatformGetEGLNativeWindow(window);
|
||||
// HACK: ANGLE does not implement eglCreatePlatformWindowSurfaceEXT
|
||||
// despite reporting EGL_EXT_platform_base
|
||||
if (_glfw.egl.platform && _glfw.egl.platform != EGL_PLATFORM_ANGLE_ANGLE)
|
||||
{
|
||||
window->context.egl.surface =
|
||||
eglCreatePlatformWindowSurfaceEXT(_glfw.egl.display, config, native, attribs);
|
||||
}
|
||||
else
|
||||
{
|
||||
window->context.egl.surface =
|
||||
eglCreateWindowSurface(_glfw.egl.display, config, native, attribs);
|
||||
}
|
||||
|
||||
window->context.egl.surface =
|
||||
eglCreateWindowSurface(_glfw.egl.display,
|
||||
config,
|
||||
_GLFW_EGL_NATIVE_WINDOW,
|
||||
attribs);
|
||||
if (window->context.egl.surface == EGL_NO_SURFACE)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
|
||||
57
glfw/egl_context.h
vendored
57
glfw/egl_context.h
vendored
@@ -25,26 +25,10 @@
|
||||
//
|
||||
//========================================================================
|
||||
|
||||
#if defined(_GLFW_USE_EGLPLATFORM_H)
|
||||
#include <EGL/eglplatform.h>
|
||||
#elif defined(_GLFW_WIN32)
|
||||
#if defined(_GLFW_WIN32)
|
||||
#define EGLAPIENTRY __stdcall
|
||||
typedef HDC EGLNativeDisplayType;
|
||||
typedef HWND EGLNativeWindowType;
|
||||
#elif defined(_GLFW_COCOA)
|
||||
#define EGLAPIENTRY
|
||||
typedef void* EGLNativeDisplayType;
|
||||
typedef id EGLNativeWindowType;
|
||||
#elif defined(_GLFW_X11)
|
||||
#define EGLAPIENTRY
|
||||
typedef Display* EGLNativeDisplayType;
|
||||
typedef Window EGLNativeWindowType;
|
||||
#elif defined(_GLFW_WAYLAND)
|
||||
#define EGLAPIENTRY
|
||||
typedef struct wl_display* EGLNativeDisplayType;
|
||||
typedef struct wl_egl_window* EGLNativeWindowType;
|
||||
#else
|
||||
#error "No supported EGL platform selected"
|
||||
#define EGLAPIENTRY
|
||||
#endif
|
||||
|
||||
#define EGL_SUCCESS 0x3000
|
||||
@@ -108,6 +92,17 @@ typedef struct wl_egl_window* EGLNativeWindowType;
|
||||
#define EGL_CONTEXT_RELEASE_BEHAVIOR_KHR 0x2097
|
||||
#define EGL_CONTEXT_RELEASE_BEHAVIOR_NONE_KHR 0
|
||||
#define EGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR 0x2098
|
||||
#define EGL_PLATFORM_X11_EXT 0x31d5
|
||||
#define EGL_PLATFORM_WAYLAND_EXT 0x31d8
|
||||
#define EGL_PLATFORM_ANGLE_ANGLE 0x3202
|
||||
#define EGL_PLATFORM_ANGLE_TYPE_ANGLE 0x3203
|
||||
#define EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE 0x320d
|
||||
#define EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE 0x320e
|
||||
#define EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE 0x3207
|
||||
#define EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE 0x3208
|
||||
#define EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE 0x3450
|
||||
#define EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE 0x3489
|
||||
#define EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE 0x348f
|
||||
|
||||
typedef int EGLint;
|
||||
typedef unsigned int EGLBoolean;
|
||||
@@ -117,9 +112,13 @@ typedef void* EGLContext;
|
||||
typedef void* EGLDisplay;
|
||||
typedef void* EGLSurface;
|
||||
|
||||
typedef void* EGLNativeDisplayType;
|
||||
typedef void* EGLNativeWindowType;
|
||||
|
||||
// EGL function pointer typedefs
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigAttrib)(EGLDisplay,EGLConfig,EGLint,EGLint*);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglGetConfigs)(EGLDisplay,EGLConfig*,EGLint,EGLint*);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglChooseConfig)(EGLDisplay,EGLint const*,EGLConfig*,EGLint,EGLint*);
|
||||
typedef EGLDisplay (EGLAPIENTRY * PFN_eglGetDisplay)(EGLNativeDisplayType);
|
||||
typedef EGLint (EGLAPIENTRY * PFN_eglGetError)(void);
|
||||
typedef EGLBoolean (EGLAPIENTRY * PFN_eglInitialize)(EGLDisplay,EGLint*,EGLint*);
|
||||
@@ -136,6 +135,7 @@ typedef const char* (EGLAPIENTRY * PFN_eglQueryString)(EGLDisplay,EGLint);
|
||||
typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*);
|
||||
#define eglGetConfigAttrib _glfw.egl.GetConfigAttrib
|
||||
#define eglGetConfigs _glfw.egl.GetConfigs
|
||||
#define eglChooseConfig _glfw.egl.ChooseConfig
|
||||
#define eglGetDisplay _glfw.egl.GetDisplay
|
||||
#define eglGetError _glfw.egl.GetError
|
||||
#define eglInitialize _glfw.egl.Initialize
|
||||
@@ -151,9 +151,10 @@ typedef GLFWglproc (EGLAPIENTRY * PFN_eglGetProcAddress)(const char*);
|
||||
#define eglQueryString _glfw.egl.QueryString
|
||||
#define eglGetProcAddress _glfw.egl.GetProcAddress
|
||||
|
||||
#define _GLFW_EGL_CONTEXT_STATE _GLFWcontextEGL egl
|
||||
#define _GLFW_EGL_LIBRARY_CONTEXT_STATE _GLFWlibraryEGL egl
|
||||
|
||||
typedef EGLDisplay (EGLAPIENTRY * PFNEGLGETPLATFORMDISPLAYEXTPROC)(EGLenum,void*,const EGLint*);
|
||||
typedef EGLSurface (EGLAPIENTRY * PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)(EGLDisplay,EGLConfig,void*,const EGLint*);
|
||||
#define eglGetPlatformDisplayEXT _glfw.egl.GetPlatformDisplayEXT
|
||||
#define eglCreatePlatformWindowSurfaceEXT _glfw.egl.CreatePlatformWindowSurfaceEXT
|
||||
|
||||
// EGL-specific per-context data
|
||||
//
|
||||
@@ -171,6 +172,7 @@ typedef struct _GLFWcontextEGL
|
||||
//
|
||||
typedef struct _GLFWlibraryEGL
|
||||
{
|
||||
EGLenum platform;
|
||||
EGLDisplay display;
|
||||
EGLint major, minor;
|
||||
bool prefix;
|
||||
@@ -180,11 +182,21 @@ typedef struct _GLFWlibraryEGL
|
||||
bool KHR_gl_colorspace;
|
||||
bool KHR_get_all_proc_addresses;
|
||||
bool KHR_context_flush_control;
|
||||
bool EXT_client_extensions;
|
||||
bool EXT_platform_base;
|
||||
bool EXT_platform_x11;
|
||||
bool EXT_platform_wayland;
|
||||
bool ANGLE_platform_angle;
|
||||
bool ANGLE_platform_angle_opengl;
|
||||
bool ANGLE_platform_angle_d3d;
|
||||
bool ANGLE_platform_angle_vulkan;
|
||||
bool ANGLE_platform_angle_metal;
|
||||
|
||||
void* handle;
|
||||
|
||||
PFN_eglGetConfigAttrib GetConfigAttrib;
|
||||
PFN_eglGetConfigs GetConfigs;
|
||||
PFN_eglChooseConfig ChooseConfig;
|
||||
PFN_eglGetDisplay GetDisplay;
|
||||
PFN_eglGetError GetError;
|
||||
PFN_eglInitialize Initialize;
|
||||
@@ -200,6 +212,9 @@ typedef struct _GLFWlibraryEGL
|
||||
PFN_eglQueryString QueryString;
|
||||
PFN_eglGetProcAddress GetProcAddress;
|
||||
|
||||
PFNEGLGETPLATFORMDISPLAYEXTPROC GetPlatformDisplayEXT;
|
||||
PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC CreatePlatformWindowSurfaceEXT;
|
||||
|
||||
} _GLFWlibraryEGL;
|
||||
|
||||
|
||||
|
||||
99
glfw/glfw.py
99
glfw/glfw.py
@@ -6,20 +6,58 @@ import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from typing import Callable, Dict, List, Optional, Tuple
|
||||
|
||||
_plat = sys.platform.lower()
|
||||
is_linux = 'linux' in _plat
|
||||
is_openbsd = 'openbsd' in _plat
|
||||
base = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def wayland_protocol_file_name(base, ext='c'):
|
||||
class Env:
|
||||
|
||||
cc: str = ''
|
||||
cppflags: List[str] = []
|
||||
cflags: List[str] = []
|
||||
ldflags: List[str] = []
|
||||
library_paths: Dict[str, List[str]] = {}
|
||||
ldpaths: List[str] = []
|
||||
ccver: Tuple[int, int]
|
||||
|
||||
# glfw stuff
|
||||
all_headers: List[str] = []
|
||||
sources: List[str] = []
|
||||
wayland_packagedir: str = ''
|
||||
wayland_scanner: str = ''
|
||||
wayland_scanner_code: str = ''
|
||||
wayland_protocols: Tuple[str, ...] = ()
|
||||
|
||||
def __init__(
|
||||
self, cc: str = '', cppflags: List[str] = [], cflags: List[str] = [], ldflags: List[str] = [],
|
||||
library_paths: Dict[str, List[str]] = {}, ldpaths: Optional[List[str]] = None, ccver: Tuple[int, int] = (0, 0)
|
||||
):
|
||||
self.cc, self.cppflags, self.cflags, self.ldflags, self.library_paths = cc, cppflags, cflags, ldflags, library_paths
|
||||
self.ldpaths, self.ccver = [] if ldpaths is None else ldpaths, ccver
|
||||
|
||||
def copy(self) -> 'Env':
|
||||
ans = Env(self.cc, list(self.cppflags), list(self.cflags), list(self.ldflags), dict(self.library_paths), list(self.ldpaths), self.ccver)
|
||||
ans.all_headers = list(self.all_headers)
|
||||
ans.sources = list(self.sources)
|
||||
ans.wayland_packagedir = self.wayland_packagedir
|
||||
ans.wayland_scanner = self.wayland_scanner
|
||||
ans.wayland_scanner_code = self.wayland_scanner_code
|
||||
ans.wayland_protocols = self.wayland_protocols
|
||||
return ans
|
||||
|
||||
|
||||
def wayland_protocol_file_name(base: str, ext: str = 'c') -> str:
|
||||
base = os.path.basename(base).rpartition('.')[0]
|
||||
return 'wayland-{}-client-protocol.{}'.format(base, ext)
|
||||
|
||||
|
||||
def init_env(env, pkg_config, at_least_version, test_compile, module='x11'):
|
||||
def init_env(env: Env, pkg_config: Callable, pkg_version: Callable, at_least_version: Callable, test_compile: Callable, module: str = 'x11') -> Env:
|
||||
ans = env.copy()
|
||||
ans.cflags.append('-fpic')
|
||||
ans.cflags.append('-fPIC')
|
||||
ans.cppflags.append('-D_GLFW_' + module.upper())
|
||||
ans.cppflags.append('-D_GLFW_BUILD_DLL')
|
||||
|
||||
@@ -35,9 +73,14 @@ def init_env(env, pkg_config, at_least_version, test_compile, module='x11'):
|
||||
|
||||
if module in ('x11', 'wayland'):
|
||||
ans.cflags.append('-pthread')
|
||||
ans.ldpaths.append('-pthread')
|
||||
ans.ldpaths.extend('-lrt -lm -ldl'.split())
|
||||
at_least_version('xkbcommon', 0, 5)
|
||||
ans.ldpaths.extend('-pthread -lm'.split())
|
||||
if not is_openbsd:
|
||||
ans.ldpaths.extend('-lrt -ldl'.split())
|
||||
major, minor = pkg_version('xkbcommon')
|
||||
if (major, minor) < (0, 5):
|
||||
raise SystemExit('libxkbcommon >= 0.5 required')
|
||||
if major < 1:
|
||||
ans.cflags.append('-DXKB_HAS_NO_UTF32')
|
||||
|
||||
if module == 'x11':
|
||||
for dep in 'x11 xrandr xinerama xcursor xkbcommon xkbcommon-x11 x11-xcb dbus-1'.split():
|
||||
@@ -46,8 +89,8 @@ def init_env(env, pkg_config, at_least_version, test_compile, module='x11'):
|
||||
|
||||
elif module == 'cocoa':
|
||||
ans.cppflags.append('-DGL_SILENCE_DEPRECATION')
|
||||
for f in 'Cocoa IOKit CoreFoundation CoreVideo'.split():
|
||||
ans.ldpaths.extend(('-framework', f))
|
||||
for f_ in 'Cocoa Carbon IOKit CoreFoundation CoreVideo'.split():
|
||||
ans.ldpaths.extend(('-framework', f_))
|
||||
|
||||
elif module == 'wayland':
|
||||
at_least_version('wayland-protocols', *sinfo['wayland_protocols'])
|
||||
@@ -59,7 +102,7 @@ def init_env(env, pkg_config, at_least_version, test_compile, module='x11'):
|
||||
for p in ans.wayland_protocols:
|
||||
ans.sources.append(wayland_protocol_file_name(p))
|
||||
ans.all_headers.append(wayland_protocol_file_name(p, 'h'))
|
||||
for dep in 'wayland-egl wayland-client wayland-cursor xkbcommon dbus-1'.split():
|
||||
for dep in 'wayland-client wayland-cursor xkbcommon dbus-1'.split():
|
||||
ans.cflags.extend(pkg_config(dep, '--cflags-only-I'))
|
||||
ans.ldpaths.extend(pkg_config(dep, '--libs'))
|
||||
has_memfd_create = test_compile(env.cc, '-Werror', src='''#define _GNU_SOURCE
|
||||
@@ -74,7 +117,7 @@ def init_env(env, pkg_config, at_least_version, test_compile, module='x11'):
|
||||
return ans
|
||||
|
||||
|
||||
def build_wayland_protocols(env, Command, parallel_run, emphasis, newer, dest_dir):
|
||||
def build_wayland_protocols(env: Env, Command: Callable, parallel_run: Callable, emphasis: Callable, newer: Callable, dest_dir: str) -> None:
|
||||
items = []
|
||||
for protocol in env.wayland_protocols:
|
||||
src = os.path.join(env.wayland_packagedir, protocol)
|
||||
@@ -95,7 +138,7 @@ def build_wayland_protocols(env, Command, parallel_run, emphasis, newer, dest_di
|
||||
|
||||
class Arg:
|
||||
|
||||
def __init__(self, decl):
|
||||
def __init__(self, decl: str):
|
||||
self.type, self.name = decl.rsplit(' ', 1)
|
||||
self.type = self.type.strip()
|
||||
self.name = self.name.strip()
|
||||
@@ -103,13 +146,13 @@ class Arg:
|
||||
self.name = self.name[1:]
|
||||
self.type = self.type + '*'
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return 'Arg({}, {})'.format(self.type, self.name)
|
||||
|
||||
|
||||
class Function:
|
||||
|
||||
def __init__(self, declaration, check_fail=True):
|
||||
def __init__(self, declaration: str, check_fail: bool = True):
|
||||
self.check_fail = check_fail
|
||||
m = re.match(
|
||||
r'(.+?)\s+(glfw[A-Z][a-zA-Z0-9]+)[(](.+)[)]$', declaration
|
||||
@@ -128,25 +171,24 @@ class Function:
|
||||
if not self.args:
|
||||
self.args = [Arg('void v')]
|
||||
|
||||
def declaration(self):
|
||||
return 'typedef {restype} (*{name}_func)({args});\n{name}_func {name}_impl;\n#define {name} {name}_impl'.format(
|
||||
def declaration(self) -> str:
|
||||
return 'typedef {restype} (*{name}_func)({args});\nGFW_EXTERN {name}_func {name}_impl;\n#define {name} {name}_impl'.format(
|
||||
restype=self.restype,
|
||||
name=self.name,
|
||||
args=', '.join(a.type for a in self.args)
|
||||
)
|
||||
|
||||
def load(self):
|
||||
ans = '*(void **) (&{name}_impl) = dlsym(handle, "{name}");'.format(
|
||||
name=self.name
|
||||
)
|
||||
def load(self) -> str:
|
||||
ans = f'*(void **) (&{self.name}_impl) = dlsym(handle, "{self.name}");'
|
||||
ans += f'\n if ({self.name}_impl == NULL) '
|
||||
if self.check_fail:
|
||||
ans += '\n if ({name}_impl == NULL) fail("Failed to load glfw function {name} with error: %s", dlerror());'.format(
|
||||
name=self.name
|
||||
)
|
||||
ans += f'fail("Failed to load glfw function {self.name} with error: %s", dlerror());'
|
||||
else:
|
||||
ans += 'dlerror(); // clear error indicator'
|
||||
return ans
|
||||
|
||||
|
||||
def generate_wrappers(glfw_header):
|
||||
def generate_wrappers(glfw_header: str) -> None:
|
||||
with open(glfw_header) as f:
|
||||
src = f.read()
|
||||
functions = []
|
||||
@@ -160,13 +202,15 @@ def generate_wrappers(glfw_header):
|
||||
functions.append(Function(decl))
|
||||
for line in '''\
|
||||
void* glfwGetCocoaWindow(GLFWwindow* window)
|
||||
void glfwHideCocoaTitlebar(GLFWwindow* window, bool yes)
|
||||
void* glfwGetNSGLContext(GLFWwindow *window)
|
||||
uint32_t glfwGetCocoaMonitor(GLFWmonitor* monitor)
|
||||
GLFWcocoatextinputfilterfun glfwSetCocoaTextInputFilter(GLFWwindow* window, GLFWcocoatextinputfilterfun callback)
|
||||
GLFWhandlefileopen glfwSetCocoaFileOpenCallback(GLFWhandlefileopen callback)
|
||||
GLFWcocoatogglefullscreenfun glfwSetCocoaToggleFullscreenIntercept(GLFWwindow *window, GLFWcocoatogglefullscreenfun callback)
|
||||
GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback)
|
||||
GLFWapplicationwillfinishlaunchingfun glfwSetApplicationWillFinishLaunching(GLFWapplicationwillfinishlaunchingfun callback)
|
||||
void glfwGetCocoaKeyEquivalent(int glfw_key, int glfw_mods, char* cocoa_key, size_t key_sz, int* cocoa_mods)
|
||||
uint32_t glfwGetCocoaKeyEquivalent(uint32_t glfw_key, int glfw_mods, int* cocoa_mods)
|
||||
void glfwCocoaRequestRenderFrame(GLFWwindow *w, GLFWcocoarenderframefun callback)
|
||||
void* glfwGetX11Display(void)
|
||||
int32_t glfwGetX11Window(GLFWwindow* window)
|
||||
@@ -197,10 +241,14 @@ const char *action_text, int32_t timeout, GLFWDBusnotificationcreatedfun callbac
|
||||
#include <stdint.h>
|
||||
#include "monotonic.h"
|
||||
|
||||
#ifndef GFW_EXTERN
|
||||
#define GFW_EXTERN extern
|
||||
#endif
|
||||
{}
|
||||
|
||||
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int,unsigned long);
|
||||
typedef bool (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef bool (* GLFWhandlefileopen)(const char*);
|
||||
typedef void (* GLFWapplicationwillfinishlaunchingfun)(void);
|
||||
typedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
|
||||
@@ -215,6 +263,7 @@ const char* load_glfw(const char* path);
|
||||
f.write(header)
|
||||
|
||||
code = '''
|
||||
#define GFW_EXTERN
|
||||
#include "data-types.h"
|
||||
#include "glfw-wrapper.h"
|
||||
#include <dlfcn.h>
|
||||
@@ -244,7 +293,7 @@ unload_glfw(void) {
|
||||
f.write(code)
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||
generate_wrappers('glfw3.h')
|
||||
|
||||
|
||||
757
glfw/glfw3.h
vendored
757
glfw/glfw3.h
vendored
File diff suppressed because it is too large
Load Diff
111
glfw/glx_context.c
vendored
111
glfw/glx_context.c
vendored
@@ -47,99 +47,87 @@ static int getGLXFBConfigAttrib(GLXFBConfig fbconfig, int attrib)
|
||||
return value;
|
||||
}
|
||||
|
||||
static GLXFBConfig*
|
||||
choose_fb_config(const _GLFWfbconfig* desired, bool trust_window_bit, int *nelements) {
|
||||
int attrib_list[64];
|
||||
int pos = 0;
|
||||
#define ATTR(x, y) { attrib_list[pos++] = x; attrib_list[pos++] = y; }
|
||||
|
||||
ATTR(GLX_DOUBLEBUFFER, desired->doublebuffer ? True : False);
|
||||
if (desired->stereo > 0) ATTR(GLX_STEREO, desired->stereo ? True : False);
|
||||
if (desired->auxBuffers > 0) ATTR(GLX_AUX_BUFFERS, desired->auxBuffers);
|
||||
if (_glfw.glx.ARB_multisample && desired->samples > 0) ATTR(GLX_SAMPLES, desired->samples);
|
||||
if (desired->depthBits != GLFW_DONT_CARE) ATTR(GLX_DEPTH_SIZE, desired->depthBits);
|
||||
if (desired->stencilBits != GLFW_DONT_CARE) ATTR(GLX_STENCIL_SIZE, desired->stencilBits);
|
||||
if (desired->redBits != GLFW_DONT_CARE) ATTR(GLX_RED_SIZE, desired->redBits);
|
||||
if (desired->greenBits != GLFW_DONT_CARE) ATTR(GLX_GREEN_SIZE, desired->greenBits);
|
||||
if (desired->blueBits != GLFW_DONT_CARE) ATTR(GLX_BLUE_SIZE, desired->blueBits);
|
||||
if (desired->alphaBits != GLFW_DONT_CARE) ATTR(GLX_ALPHA_SIZE, desired->alphaBits);
|
||||
if (desired->accumRedBits != GLFW_DONT_CARE) ATTR(GLX_ACCUM_RED_SIZE, desired->accumRedBits);
|
||||
if (desired->accumGreenBits != GLFW_DONT_CARE) ATTR(GLX_ACCUM_GREEN_SIZE, desired->accumGreenBits);
|
||||
if (desired->accumBlueBits != GLFW_DONT_CARE) ATTR(GLX_ACCUM_BLUE_SIZE, desired->accumBlueBits);
|
||||
if (desired->accumAlphaBits != GLFW_DONT_CARE) ATTR(GLX_ACCUM_ALPHA_SIZE, desired->accumAlphaBits);
|
||||
if (!trust_window_bit) ATTR(GLX_DRAWABLE_TYPE, 0);
|
||||
ATTR(None, None);
|
||||
return glXChooseFBConfig(_glfw.x11.display, _glfw.x11.screen, attrib_list, nelements);
|
||||
#undef ATTR
|
||||
}
|
||||
|
||||
|
||||
// Return the GLXFBConfig most closely matching the specified hints
|
||||
//
|
||||
static bool chooseGLXFBConfig(const _GLFWfbconfig* desired,
|
||||
GLXFBConfig* result)
|
||||
{
|
||||
GLXFBConfig* nativeConfigs;
|
||||
_GLFWfbconfig* usableConfigs;
|
||||
const _GLFWfbconfig* closest;
|
||||
int i, nativeCount, usableCount;
|
||||
int i, nativeCount, ans_idx = 0;
|
||||
const char* vendor;
|
||||
bool trustWindowBit = true;
|
||||
static _GLFWfbconfig prev_desired = {0};
|
||||
static GLXFBConfig prev_result = 0;
|
||||
if (prev_result != 0 && memcmp(&prev_desired, desired, sizeof(_GLFWfbconfig)) == 0) {
|
||||
*result = prev_result;
|
||||
return true;
|
||||
}
|
||||
prev_desired = *desired;
|
||||
|
||||
// HACK: This is a (hopefully temporary) workaround for Chromium
|
||||
// (VirtualBox GL) not setting the window bit on any GLXFBConfigs
|
||||
vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR);
|
||||
if (vendor && strcmp(vendor, "Chromium") == 0)
|
||||
trustWindowBit = false;
|
||||
|
||||
nativeConfigs =
|
||||
glXGetFBConfigs(_glfw.x11.display, _glfw.x11.screen, &nativeCount);
|
||||
nativeConfigs = choose_fb_config(desired, trustWindowBit, &nativeCount);
|
||||
if (!nativeConfigs || !nativeCount)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "GLX: No GLXFBConfigs returned");
|
||||
return false;
|
||||
}
|
||||
|
||||
usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig));
|
||||
usableCount = 0;
|
||||
|
||||
for (i = 0; i < nativeCount; i++)
|
||||
{
|
||||
const GLXFBConfig n = nativeConfigs[i];
|
||||
_GLFWfbconfig* u = usableConfigs + usableCount;
|
||||
|
||||
// Only consider RGBA GLXFBConfigs
|
||||
if (!(getGLXFBConfigAttrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT))
|
||||
continue;
|
||||
|
||||
// Only consider window GLXFBConfigs
|
||||
if (!(getGLXFBConfigAttrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT))
|
||||
{
|
||||
if (trustWindowBit)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (desired->transparent)
|
||||
{
|
||||
bool transparency_matches = true, srgb_matches = true;
|
||||
if (desired->transparent) {
|
||||
transparency_matches = false;
|
||||
XVisualInfo* vi = glXGetVisualFromFBConfig(_glfw.x11.display, n);
|
||||
if (vi)
|
||||
{
|
||||
u->transparent = _glfwIsVisualTransparentX11(vi->visual);
|
||||
XFree(vi);
|
||||
}
|
||||
if (vi && _glfwIsVisualTransparentX11(vi->visual)) transparency_matches = true;
|
||||
}
|
||||
|
||||
u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE);
|
||||
u->greenBits = getGLXFBConfigAttrib(n, GLX_GREEN_SIZE);
|
||||
u->blueBits = getGLXFBConfigAttrib(n, GLX_BLUE_SIZE);
|
||||
if (desired->sRGB && (_glfw.glx.ARB_framebuffer_sRGB || _glfw.glx.EXT_framebuffer_sRGB)) {
|
||||
srgb_matches = getGLXFBConfigAttrib(n, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB) ? true : false;
|
||||
}
|
||||
|
||||
u->alphaBits = getGLXFBConfigAttrib(n, GLX_ALPHA_SIZE);
|
||||
u->depthBits = getGLXFBConfigAttrib(n, GLX_DEPTH_SIZE);
|
||||
u->stencilBits = getGLXFBConfigAttrib(n, GLX_STENCIL_SIZE);
|
||||
if (transparency_matches && srgb_matches) {
|
||||
ans_idx = i; break;
|
||||
}
|
||||
|
||||
u->accumRedBits = getGLXFBConfigAttrib(n, GLX_ACCUM_RED_SIZE);
|
||||
u->accumGreenBits = getGLXFBConfigAttrib(n, GLX_ACCUM_GREEN_SIZE);
|
||||
u->accumBlueBits = getGLXFBConfigAttrib(n, GLX_ACCUM_BLUE_SIZE);
|
||||
u->accumAlphaBits = getGLXFBConfigAttrib(n, GLX_ACCUM_ALPHA_SIZE);
|
||||
|
||||
u->auxBuffers = getGLXFBConfigAttrib(n, GLX_AUX_BUFFERS);
|
||||
|
||||
if (getGLXFBConfigAttrib(n, GLX_STEREO))
|
||||
u->stereo = true;
|
||||
if (getGLXFBConfigAttrib(n, GLX_DOUBLEBUFFER))
|
||||
u->doublebuffer = true;
|
||||
|
||||
if (_glfw.glx.ARB_multisample)
|
||||
u->samples = getGLXFBConfigAttrib(n, GLX_SAMPLES);
|
||||
|
||||
if (_glfw.glx.ARB_framebuffer_sRGB || _glfw.glx.EXT_framebuffer_sRGB)
|
||||
u->sRGB = getGLXFBConfigAttrib(n, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB);
|
||||
|
||||
u->handle = (uintptr_t) n;
|
||||
usableCount++;
|
||||
}
|
||||
|
||||
closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount);
|
||||
if (closest)
|
||||
*result = (GLXFBConfig) closest->handle;
|
||||
*result = nativeConfigs[ans_idx];
|
||||
prev_result = nativeConfigs[ans_idx];
|
||||
|
||||
XFree(nativeConfigs);
|
||||
free(usableConfigs);
|
||||
|
||||
return closest != NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create the OpenGL context using legacy API
|
||||
@@ -287,6 +275,7 @@ bool _glfwInitGLX(void)
|
||||
|
||||
glfw_dlsym(_glfw.glx.GetFBConfigs, _glfw.glx.handle, "glXGetFBConfigs");
|
||||
glfw_dlsym(_glfw.glx.GetFBConfigAttrib, _glfw.glx.handle, "glXGetFBConfigAttrib");
|
||||
glfw_dlsym(_glfw.glx.ChooseFBConfig, _glfw.glx.handle, "glXChooseFBConfig");
|
||||
glfw_dlsym(_glfw.glx.GetClientString, _glfw.glx.handle, "glXGetClientString");
|
||||
glfw_dlsym(_glfw.glx.QueryExtension, _glfw.glx.handle, "glXQueryExtension");
|
||||
glfw_dlsym(_glfw.glx.QueryVersion, _glfw.glx.handle, "glXQueryVersion");
|
||||
|
||||
3
glfw/glx_context.h
vendored
3
glfw/glx_context.h
vendored
@@ -73,6 +73,7 @@ typedef struct __GLXcontext* GLXContext;
|
||||
typedef void (*__GLXextproc)(void);
|
||||
|
||||
typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*);
|
||||
typedef GLXFBConfig* (*PFNGLXCHOOSEFBCONFIGPROC)(Display*,int,const int*,int*);
|
||||
typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int);
|
||||
typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*);
|
||||
typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*);
|
||||
@@ -95,6 +96,7 @@ typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLX
|
||||
// libGL.so function pointer typedefs
|
||||
#define glXGetFBConfigs _glfw.glx.GetFBConfigs
|
||||
#define glXGetFBConfigAttrib _glfw.glx.GetFBConfigAttrib
|
||||
#define glXChooseFBConfig _glfw.glx.ChooseFBConfig
|
||||
#define glXGetClientString _glfw.glx.GetClientString
|
||||
#define glXQueryExtension _glfw.glx.QueryExtension
|
||||
#define glXQueryVersion _glfw.glx.QueryVersion
|
||||
@@ -134,6 +136,7 @@ typedef struct _GLFWlibraryGLX
|
||||
// GLX 1.3 functions
|
||||
PFNGLXGETFBCONFIGSPROC GetFBConfigs;
|
||||
PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib;
|
||||
PFNGLXCHOOSEFBCONFIGPROC ChooseFBConfig;
|
||||
PFNGLXGETCLIENTSTRINGPROC GetClientString;
|
||||
PFNGLXQUERYEXTENSIONPROC QueryExtension;
|
||||
PFNGLXQUERYVERSIONPROC QueryVersion;
|
||||
|
||||
12
glfw/ibus_glfw.c
vendored
12
glfw/ibus_glfw.c
vendored
@@ -107,11 +107,10 @@ get_ibus_text_from_message(DBusMessage *msg) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
send_text(const char *text, int ime_state) {
|
||||
send_text(const char *text, GLFWIMEState ime_state) {
|
||||
_GLFWwindow *w = _glfwFocusedWindow();
|
||||
if (w && w->callbacks.keyboard) {
|
||||
GLFWkeyevent fake_ev;
|
||||
_glfwInitializeKeyEvent(&fake_ev, GLFW_KEY_UNKNOWN, 0, GLFW_PRESS, 0);
|
||||
GLFWkeyevent fake_ev = {.action = GLFW_PRESS};
|
||||
fake_ev.text = text;
|
||||
fake_ev.ime_state = ime_state;
|
||||
w->callbacks.keyboard((GLFWwindow*) w, &fake_ev);
|
||||
@@ -131,11 +130,11 @@ message_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data)
|
||||
case 0:
|
||||
text = get_ibus_text_from_message(msg);
|
||||
debug("IBUS: CommitText: '%s'\n", text ? text : "(nil)");
|
||||
send_text(text, 2);
|
||||
send_text(text, GLFW_IME_COMMIT_TEXT);
|
||||
break;
|
||||
case 1:
|
||||
text = get_ibus_text_from_message(msg);
|
||||
send_text(text, 1);
|
||||
send_text(text, GLFW_IME_PREEDIT_CHANGED);
|
||||
debug("IBUS: UpdatePreeditText: '%s'\n", text ? text : "(nil)");
|
||||
break;
|
||||
case 2:
|
||||
@@ -269,7 +268,7 @@ setup_connection(_GLFWIBUSData *ibus) {
|
||||
ibus->conn = NULL;
|
||||
}
|
||||
debug("Connecting to IBUS daemon @ %s for IME input management\n", ibus->address);
|
||||
ibus->conn = glfw_dbus_connect_to(ibus->address, "Failed to connect to the IBUS daemon, with error", "ibus", false);
|
||||
ibus->conn = glfw_dbus_connect_to(ibus->address, "Failed to connect to the IBUS daemon, with error", "ibus", true);
|
||||
if (!ibus->conn) return false;
|
||||
free((void*)ibus->input_ctx_path); ibus->input_ctx_path = NULL;
|
||||
if (!glfw_dbus_call_method_with_reply(
|
||||
@@ -390,6 +389,7 @@ ibus_key_state(unsigned int glfw_modifiers, int action) {
|
||||
M(ALT, IBUS_MOD1_MASK);
|
||||
M(NUM_LOCK, IBUS_MOD2_MASK);
|
||||
M(SUPER, IBUS_MOD4_MASK);
|
||||
/* To do: figure out how to get super/hyper/meta */
|
||||
#undef M
|
||||
return ans;
|
||||
}
|
||||
|
||||
44
glfw/init.c
vendored
44
glfw/init.c
vendored
@@ -50,14 +50,14 @@ _GLFWlibrary _glfw = { false };
|
||||
//
|
||||
static _GLFWerror _glfwMainThreadError;
|
||||
static GLFWerrorfun _glfwErrorCallback;
|
||||
static _GLFWinitconfig _glfwInitHints =
|
||||
{
|
||||
true, // hat buttons
|
||||
false, // debug keyboard
|
||||
true, // enable joystick
|
||||
{
|
||||
true, // macOS menu bar
|
||||
true // macOS bundle chdir
|
||||
static _GLFWinitconfig _glfwInitHints = {
|
||||
.hatButtons = true,
|
||||
.angleType = GLFW_ANGLE_PLATFORM_TYPE_NONE,
|
||||
.debugKeyboard = false,
|
||||
.debugRendering = false,
|
||||
.ns = {
|
||||
.menubar = true, // macOS menu bar
|
||||
.chdir = true // macOS bundle chdir
|
||||
}
|
||||
};
|
||||
|
||||
@@ -92,6 +92,7 @@ static void terminate(void)
|
||||
_glfw.mappingCount = 0;
|
||||
|
||||
_glfwTerminateVulkan();
|
||||
_glfwPlatformTerminateJoysticks();
|
||||
_glfwPlatformTerminate();
|
||||
|
||||
_glfw.initialized = false;
|
||||
@@ -168,6 +169,10 @@ void _glfwInputError(int code, const char* format, ...)
|
||||
strcpy(description, "The requested format is unavailable");
|
||||
else if (code == GLFW_NO_WINDOW_CONTEXT)
|
||||
strcpy(description, "The specified window has no context");
|
||||
else if (code == GLFW_FEATURE_UNAVAILABLE)
|
||||
strcpy(description, "The requested feature cannot be implemented for this platform");
|
||||
else if (code == GLFW_FEATURE_UNIMPLEMENTED)
|
||||
strcpy(description, "The requested feature has not yet been implemented for this platform");
|
||||
else
|
||||
strcpy(description, "ERROR: UNKNOWN GLFW ERROR");
|
||||
}
|
||||
@@ -271,15 +276,18 @@ GLFWAPI void glfwInitHint(int hint, int value)
|
||||
{
|
||||
switch (hint)
|
||||
{
|
||||
case GLFW_ENABLE_JOYSTICKS:
|
||||
_glfwInitHints.enableJoysticks = value;
|
||||
return;
|
||||
case GLFW_JOYSTICK_HAT_BUTTONS:
|
||||
_glfwInitHints.hatButtons = value;
|
||||
return;
|
||||
case GLFW_ANGLE_PLATFORM_TYPE:
|
||||
_glfwInitHints.angleType = value;
|
||||
return;
|
||||
case GLFW_DEBUG_KEYBOARD:
|
||||
_glfwInitHints.debugKeyboard = value;
|
||||
return;
|
||||
case GLFW_DEBUG_RENDERING:
|
||||
_glfwInitHints.debugRendering = value;
|
||||
return;
|
||||
case GLFW_COCOA_CHDIR_RESOURCES:
|
||||
_glfwInitHints.ns.chdir = value;
|
||||
return;
|
||||
@@ -363,3 +371,17 @@ GLFWAPI void glfwUpdateTimer(unsigned long long timer_id, monotonic_t interval,
|
||||
GLFWAPI void glfwRemoveTimer(unsigned long long timer_id) {
|
||||
_glfwPlatformRemoveTimer(timer_id);
|
||||
}
|
||||
|
||||
GLFWAPI GLFWapplicationclosefun glfwSetApplicationCloseCallback(GLFWapplicationclosefun cbfun)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(_glfw.callbacks.application_close, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun cbfun)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
_GLFW_SWAP_POINTERS(_glfw.callbacks.draw_text, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
549
glfw/input.c
vendored
549
glfw/input.c
vendored
@@ -44,6 +44,22 @@
|
||||
#define _GLFW_JOYSTICK_BUTTON 2
|
||||
#define _GLFW_JOYSTICK_HATBIT 3
|
||||
|
||||
// Initializes the platform joystick API if it has not been already
|
||||
//
|
||||
static bool initJoysticks(void)
|
||||
{
|
||||
if (!_glfw.joysticksInitialized)
|
||||
{
|
||||
if (!_glfwPlatformInitJoysticks())
|
||||
{
|
||||
_glfwPlatformTerminateJoysticks();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return _glfw.joysticksInitialized = true;
|
||||
}
|
||||
|
||||
// Finds a mapping based on joystick GUID
|
||||
//
|
||||
static _GLFWmapping* findMapping(const char* guid)
|
||||
@@ -257,34 +273,62 @@ static bool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
////// GLFW event API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwInitializeKeyEvent(GLFWkeyevent *ev, int key, int native_key, int action, int mods)
|
||||
{
|
||||
ev->key = key;
|
||||
ev->native_key = native_key;
|
||||
ev->action = action;
|
||||
ev->mods = mods;
|
||||
ev->text = NULL;
|
||||
ev->ime_state = 0;
|
||||
static void
|
||||
set_key_action(_GLFWwindow *window, uint32_t key, int val, int idx) {
|
||||
const unsigned sz = arraysz(window->activated_keys);
|
||||
if (idx < 0) {
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
if (window->activated_keys[i].key == 0) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (idx < 0) {
|
||||
idx = sz - 1;
|
||||
memmove(window->activated_keys, window->activated_keys + 1, sizeof(window->activated_keys[0]) * (sz - 1));
|
||||
window->activated_keys[sz - 1].key = key;
|
||||
}
|
||||
}
|
||||
if (val == GLFW_RELEASE) {
|
||||
memset(window->activated_keys + idx, 0, sizeof(window->activated_keys[0]));
|
||||
if (idx < (int)sz - 1) {
|
||||
memmove(window->activated_keys + idx, window->activated_keys + idx + 1, sizeof(window->activated_keys[0]) * (sz - 1 - idx));
|
||||
memset(window->activated_keys + sz - 1, 0, sizeof(window->activated_keys[0]));
|
||||
}
|
||||
} else {
|
||||
window->activated_keys[idx].key = key;
|
||||
window->activated_keys[idx].action = val;
|
||||
}
|
||||
}
|
||||
|
||||
// Notifies shared code of a physical key event
|
||||
//
|
||||
void _glfwInputKeyboard(_GLFWwindow* window, GLFWkeyevent* ev)
|
||||
{
|
||||
if (ev->key >= 0 && ev->key <= GLFW_KEY_LAST)
|
||||
if (ev->key > 0)
|
||||
{
|
||||
bool repeated = false;
|
||||
int idx = -1;
|
||||
int current_action = GLFW_RELEASE;
|
||||
const unsigned sz = arraysz(window->activated_keys);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
if (window->activated_keys[i].key == ev->key) {
|
||||
idx = i;
|
||||
current_action = window->activated_keys[i].action;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ev->action == GLFW_RELEASE && window->keys[ev->key] == GLFW_RELEASE)
|
||||
if (ev->action == GLFW_RELEASE && current_action == GLFW_RELEASE)
|
||||
return;
|
||||
|
||||
if (ev->action == GLFW_PRESS && window->keys[ev->key] == GLFW_PRESS)
|
||||
if (ev->action == GLFW_PRESS && current_action == GLFW_PRESS)
|
||||
repeated = true;
|
||||
|
||||
if (ev->action == GLFW_RELEASE && window->stickyKeys)
|
||||
window->keys[ev->key] = _GLFW_STICK;
|
||||
set_key_action(window, ev->key, _GLFW_STICK, idx);
|
||||
else
|
||||
window->keys[ev->key] = (char) ev->action;
|
||||
set_key_action(window, ev->key, ev->action, idx);
|
||||
|
||||
if (repeated)
|
||||
ev->action = GLFW_REPEAT;
|
||||
@@ -300,10 +344,10 @@ void _glfwInputKeyboard(_GLFWwindow* window, GLFWkeyevent* ev)
|
||||
|
||||
// Notifies shared code of a scroll event
|
||||
//
|
||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags)
|
||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags, int mods)
|
||||
{
|
||||
if (window->callbacks.scroll)
|
||||
window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset, flags);
|
||||
window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset, flags, mods);
|
||||
}
|
||||
|
||||
// Notifies shared code of a mouse button click event
|
||||
@@ -350,10 +394,11 @@ void _glfwInputCursorEnter(_GLFWwindow* window, bool entered)
|
||||
|
||||
// Notifies shared code of files or directories dropped on a window
|
||||
//
|
||||
void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths)
|
||||
int _glfwInputDrop(_GLFWwindow* window, const char *mime, const char *text, size_t sz)
|
||||
{
|
||||
if (window->callbacks.drop)
|
||||
window->callbacks.drop((GLFWwindow*) window, count, paths);
|
||||
return window->callbacks.drop((GLFWwindow*) window, mime, text, sz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Notifies shared code of a joystick connection or disconnection
|
||||
@@ -446,200 +491,156 @@ void _glfwFreeJoystick(_GLFWjoystick* js)
|
||||
memset(js, 0, sizeof(_GLFWjoystick));
|
||||
}
|
||||
|
||||
const char* _glfwGetKeyName(int key)
|
||||
unsigned int
|
||||
encode_utf8(uint32_t ch, char* dest) {
|
||||
if (ch < 0x80) {
|
||||
dest[0] = (char)ch;
|
||||
return 1;
|
||||
}
|
||||
if (ch < 0x800) {
|
||||
dest[0] = (ch>>6) | 0xC0;
|
||||
dest[1] = (ch & 0x3F) | 0x80;
|
||||
return 2;
|
||||
}
|
||||
if (ch < 0x10000) {
|
||||
dest[0] = (ch>>12) | 0xE0;
|
||||
dest[1] = ((ch>>6) & 0x3F) | 0x80;
|
||||
dest[2] = (ch & 0x3F) | 0x80;
|
||||
return 3;
|
||||
}
|
||||
if (ch < 0x110000) {
|
||||
dest[0] = (ch>>18) | 0xF0;
|
||||
dest[1] = ((ch>>12) & 0x3F) | 0x80;
|
||||
dest[2] = ((ch>>6) & 0x3F) | 0x80;
|
||||
dest[3] = (ch & 0x3F) | 0x80;
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char*
|
||||
_glfwGetKeyName(int key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
// Printable keys
|
||||
case GLFW_KEY_SPACE: return "SPACE";
|
||||
case GLFW_KEY_EXCLAM: return "EXCLAM";
|
||||
case GLFW_KEY_DOUBLE_QUOTE: return "DOUBLE_QUOTE";
|
||||
case GLFW_KEY_NUMBER_SIGN: return "NUMBER_SIGN";
|
||||
case GLFW_KEY_DOLLAR: return "DOLLAR";
|
||||
case GLFW_KEY_AMPERSAND: return "AMPERSAND";
|
||||
case GLFW_KEY_APOSTROPHE: return "APOSTROPHE";
|
||||
case GLFW_KEY_PARENTHESIS_LEFT: return "PARENTHESIS_LEFT";
|
||||
case GLFW_KEY_PARENTHESIS_RIGHT: return "PARENTHESIS_RIGHT";
|
||||
case GLFW_KEY_PLUS: return "PLUS";
|
||||
case GLFW_KEY_COMMA: return "COMMA";
|
||||
case GLFW_KEY_MINUS: return "MINUS";
|
||||
case GLFW_KEY_PERIOD: return "PERIOD";
|
||||
case GLFW_KEY_SLASH: return "SLASH";
|
||||
case GLFW_KEY_0: return "0";
|
||||
case GLFW_KEY_1: return "1";
|
||||
case GLFW_KEY_2: return "2";
|
||||
case GLFW_KEY_3: return "3";
|
||||
case GLFW_KEY_4: return "4";
|
||||
case GLFW_KEY_5: return "5";
|
||||
case GLFW_KEY_6: return "6";
|
||||
case GLFW_KEY_7: return "7";
|
||||
case GLFW_KEY_8: return "8";
|
||||
case GLFW_KEY_9: return "9";
|
||||
case GLFW_KEY_COLON: return "COLON";
|
||||
case GLFW_KEY_SEMICOLON: return "SEMICOLON";
|
||||
case GLFW_KEY_LESS: return "LESS";
|
||||
case GLFW_KEY_EQUAL: return "EQUAL";
|
||||
case GLFW_KEY_GREATER: return "GREATER";
|
||||
case GLFW_KEY_AT: return "AT";
|
||||
case GLFW_KEY_A: return "A";
|
||||
case GLFW_KEY_B: return "B";
|
||||
case GLFW_KEY_C: return "C";
|
||||
case GLFW_KEY_D: return "D";
|
||||
case GLFW_KEY_E: return "E";
|
||||
case GLFW_KEY_F: return "F";
|
||||
case GLFW_KEY_G: return "G";
|
||||
case GLFW_KEY_H: return "H";
|
||||
case GLFW_KEY_I: return "I";
|
||||
case GLFW_KEY_J: return "J";
|
||||
case GLFW_KEY_K: return "K";
|
||||
case GLFW_KEY_L: return "L";
|
||||
case GLFW_KEY_M: return "M";
|
||||
case GLFW_KEY_N: return "N";
|
||||
case GLFW_KEY_O: return "O";
|
||||
case GLFW_KEY_P: return "P";
|
||||
case GLFW_KEY_Q: return "Q";
|
||||
case GLFW_KEY_R: return "R";
|
||||
case GLFW_KEY_S: return "S";
|
||||
case GLFW_KEY_T: return "T";
|
||||
case GLFW_KEY_U: return "U";
|
||||
case GLFW_KEY_V: return "V";
|
||||
case GLFW_KEY_W: return "W";
|
||||
case GLFW_KEY_X: return "X";
|
||||
case GLFW_KEY_Y: return "Y";
|
||||
case GLFW_KEY_Z: return "Z";
|
||||
case GLFW_KEY_LEFT_BRACKET: return "LEFT_BRACKET";
|
||||
case GLFW_KEY_BACKSLASH: return "BACKSLASH";
|
||||
case GLFW_KEY_RIGHT_BRACKET: return "RIGHT_BRACKET";
|
||||
case GLFW_KEY_CIRCUMFLEX: return "CIRCUMFLEX";
|
||||
case GLFW_KEY_UNDERSCORE: return "UNDERSCORE";
|
||||
case GLFW_KEY_GRAVE_ACCENT: return "GRAVE_ACCENT";
|
||||
case GLFW_KEY_WORLD_1: return "WORLD_1";
|
||||
case GLFW_KEY_WORLD_2: return "WORLD_2";
|
||||
case GLFW_KEY_PARAGRAPH: return "PARAGRAPH";
|
||||
case GLFW_KEY_MASCULINE: return "MASCULINE";
|
||||
case GLFW_KEY_A_GRAVE: return "A_GRAVE";
|
||||
case GLFW_KEY_A_DIAERESIS: return "A_DIAERESIS";
|
||||
case GLFW_KEY_A_RING: return "A_RING";
|
||||
case GLFW_KEY_AE: return "AE";
|
||||
case GLFW_KEY_C_CEDILLA: return "C_CEDILLA";
|
||||
case GLFW_KEY_E_GRAVE: return "E_GRAVE";
|
||||
case GLFW_KEY_E_ACUTE: return "E_ACUTE";
|
||||
case GLFW_KEY_I_GRAVE: return "I_GRAVE";
|
||||
case GLFW_KEY_N_TILDE: return "N_TILDE";
|
||||
case GLFW_KEY_O_GRAVE: return "O_GRAVE";
|
||||
case GLFW_KEY_O_DIAERESIS: return "O_DIAERESIS";
|
||||
case GLFW_KEY_O_SLASH: return "O_SLASH";
|
||||
case GLFW_KEY_U_GRAVE: return "U_GRAVE";
|
||||
case GLFW_KEY_U_DIAERESIS: return "U_DIAERESIS";
|
||||
case GLFW_KEY_S_SHARP: return "S_SHARP";
|
||||
case GLFW_KEY_CYRILLIC_A: return "CYRILLIC_A";
|
||||
case GLFW_KEY_CYRILLIC_BE: return "CYRILLIC_BE";
|
||||
case GLFW_KEY_CYRILLIC_VE: return "CYRILLIC_VE";
|
||||
case GLFW_KEY_CYRILLIC_GHE: return "CYRILLIC_GHE";
|
||||
case GLFW_KEY_CYRILLIC_DE: return "CYRILLIC_DE";
|
||||
case GLFW_KEY_CYRILLIC_IE: return "CYRILLIC_IE";
|
||||
case GLFW_KEY_CYRILLIC_ZHE: return "CYRILLIC_ZHE";
|
||||
case GLFW_KEY_CYRILLIC_ZE: return "CYRILLIC_ZE";
|
||||
case GLFW_KEY_CYRILLIC_I: return "CYRILLIC_I";
|
||||
case GLFW_KEY_CYRILLIC_SHORT_I: return "CYRILLIC_SHORT_I";
|
||||
case GLFW_KEY_CYRILLIC_KA: return "CYRILLIC_KA";
|
||||
case GLFW_KEY_CYRILLIC_EL: return "CYRILLIC_EL";
|
||||
case GLFW_KEY_CYRILLIC_EM: return "CYRILLIC_EM";
|
||||
case GLFW_KEY_CYRILLIC_EN: return "CYRILLIC_EN";
|
||||
case GLFW_KEY_CYRILLIC_O: return "CYRILLIC_O";
|
||||
case GLFW_KEY_CYRILLIC_PE: return "CYRILLIC_PE";
|
||||
case GLFW_KEY_CYRILLIC_ER: return "CYRILLIC_ER";
|
||||
case GLFW_KEY_CYRILLIC_ES: return "CYRILLIC_ES";
|
||||
case GLFW_KEY_CYRILLIC_TE: return "CYRILLIC_TE";
|
||||
case GLFW_KEY_CYRILLIC_U: return "CYRILLIC_U";
|
||||
case GLFW_KEY_CYRILLIC_EF: return "CYRILLIC_EF";
|
||||
case GLFW_KEY_CYRILLIC_HA: return "CYRILLIC_HA";
|
||||
case GLFW_KEY_CYRILLIC_TSE: return "CYRILLIC_TSE";
|
||||
case GLFW_KEY_CYRILLIC_CHE: return "CYRILLIC_CHE";
|
||||
case GLFW_KEY_CYRILLIC_SHA: return "CYRILLIC_SHA";
|
||||
case GLFW_KEY_CYRILLIC_SHCHA: return "CYRILLIC_SHCHA";
|
||||
case GLFW_KEY_CYRILLIC_HARD_SIGN: return "CYRILLIC_HARD_SIGN";
|
||||
case GLFW_KEY_CYRILLIC_YERU: return "CYRILLIC_YERU";
|
||||
case GLFW_KEY_CYRILLIC_SOFT_SIGN: return "CYRILLIC_SOFT_SIGN";
|
||||
case GLFW_KEY_CYRILLIC_E: return "CYRILLIC_E";
|
||||
case GLFW_KEY_CYRILLIC_YU: return "CYRILLIC_YU";
|
||||
case GLFW_KEY_CYRILLIC_YA: return "CYRILLIC_YA";
|
||||
case GLFW_KEY_CYRILLIC_IO: return "CYRILLIC_IO";
|
||||
|
||||
// Function keys
|
||||
case GLFW_KEY_ESCAPE: return "ESCAPE";
|
||||
case GLFW_KEY_F1: return "F1";
|
||||
case GLFW_KEY_F2: return "F2";
|
||||
case GLFW_KEY_F3: return "F3";
|
||||
case GLFW_KEY_F4: return "F4";
|
||||
case GLFW_KEY_F5: return "F5";
|
||||
case GLFW_KEY_F6: return "F6";
|
||||
case GLFW_KEY_F7: return "F7";
|
||||
case GLFW_KEY_F8: return "F8";
|
||||
case GLFW_KEY_F9: return "F9";
|
||||
case GLFW_KEY_F10: return "F10";
|
||||
case GLFW_KEY_F11: return "F11";
|
||||
case GLFW_KEY_F12: return "F12";
|
||||
case GLFW_KEY_F13: return "F13";
|
||||
case GLFW_KEY_F14: return "F14";
|
||||
case GLFW_KEY_F15: return "F15";
|
||||
case GLFW_KEY_F16: return "F16";
|
||||
case GLFW_KEY_F17: return "F17";
|
||||
case GLFW_KEY_F18: return "F18";
|
||||
case GLFW_KEY_F19: return "F19";
|
||||
case GLFW_KEY_F20: return "F20";
|
||||
case GLFW_KEY_F21: return "F21";
|
||||
case GLFW_KEY_F22: return "F22";
|
||||
case GLFW_KEY_F23: return "F23";
|
||||
case GLFW_KEY_F24: return "F24";
|
||||
case GLFW_KEY_F25: return "F25";
|
||||
case GLFW_KEY_UP: return "UP";
|
||||
case GLFW_KEY_DOWN: return "DOWN";
|
||||
case GLFW_KEY_LEFT: return "LEFT";
|
||||
case GLFW_KEY_RIGHT: return "RIGHT";
|
||||
case GLFW_KEY_LEFT_SHIFT: return "LEFT SHIFT";
|
||||
case GLFW_KEY_RIGHT_SHIFT: return "RIGHT SHIFT";
|
||||
case GLFW_KEY_LEFT_CONTROL: return "LEFT CONTROL";
|
||||
case GLFW_KEY_RIGHT_CONTROL: return "RIGHT CONTROL";
|
||||
case GLFW_KEY_LEFT_ALT: return "LEFT ALT";
|
||||
case GLFW_KEY_RIGHT_ALT: return "RIGHT ALT";
|
||||
case GLFW_KEY_TAB: return "TAB";
|
||||
case GLFW_KEY_ENTER: return "ENTER";
|
||||
case GLFW_KEY_BACKSPACE: return "BACKSPACE";
|
||||
case GLFW_KEY_INSERT: return "INSERT";
|
||||
case GLFW_KEY_DELETE: return "DELETE";
|
||||
case GLFW_KEY_PAGE_UP: return "PAGE UP";
|
||||
case GLFW_KEY_PAGE_DOWN: return "PAGE DOWN";
|
||||
case GLFW_KEY_HOME: return "HOME";
|
||||
case GLFW_KEY_END: return "END";
|
||||
case GLFW_KEY_KP_0: return "KEYPAD 0";
|
||||
case GLFW_KEY_KP_1: return "KEYPAD 1";
|
||||
case GLFW_KEY_KP_2: return "KEYPAD 2";
|
||||
case GLFW_KEY_KP_3: return "KEYPAD 3";
|
||||
case GLFW_KEY_KP_4: return "KEYPAD 4";
|
||||
case GLFW_KEY_KP_5: return "KEYPAD 5";
|
||||
case GLFW_KEY_KP_6: return "KEYPAD 6";
|
||||
case GLFW_KEY_KP_7: return "KEYPAD 7";
|
||||
case GLFW_KEY_KP_8: return "KEYPAD 8";
|
||||
case GLFW_KEY_KP_9: return "KEYPAD 9";
|
||||
case GLFW_KEY_KP_DIVIDE: return "KEYPAD DIVIDE";
|
||||
case GLFW_KEY_KP_MULTIPLY: return "KEYPAD MULTIPLY";
|
||||
case GLFW_KEY_KP_SUBTRACT: return "KEYPAD SUBTRACT";
|
||||
case GLFW_KEY_KP_ADD: return "KEYPAD ADD";
|
||||
case GLFW_KEY_KP_DECIMAL: return "KEYPAD DECIMAL";
|
||||
case GLFW_KEY_KP_EQUAL: return "KEYPAD EQUAL";
|
||||
case GLFW_KEY_KP_ENTER: return "KEYPAD ENTER";
|
||||
case GLFW_KEY_PRINT_SCREEN: return "PRINT SCREEN";
|
||||
case GLFW_KEY_NUM_LOCK: return "NUM LOCK";
|
||||
case GLFW_KEY_CAPS_LOCK: return "CAPS LOCK";
|
||||
case GLFW_KEY_SCROLL_LOCK: return "SCROLL LOCK";
|
||||
case GLFW_KEY_PAUSE: return "PAUSE";
|
||||
case GLFW_KEY_LEFT_SUPER: return "LEFT SUPER";
|
||||
case GLFW_KEY_RIGHT_SUPER: return "RIGHT SUPER";
|
||||
case GLFW_KEY_MENU: return "MENU";
|
||||
|
||||
default: return "UNKNOWN";
|
||||
/* start functional key names (auto generated by gen-key-constants.py do not edit) */
|
||||
case GLFW_FKEY_ESCAPE: return "ESCAPE";
|
||||
case GLFW_FKEY_ENTER: return "ENTER";
|
||||
case GLFW_FKEY_TAB: return "TAB";
|
||||
case GLFW_FKEY_BACKSPACE: return "BACKSPACE";
|
||||
case GLFW_FKEY_INSERT: return "INSERT";
|
||||
case GLFW_FKEY_DELETE: return "DELETE";
|
||||
case GLFW_FKEY_LEFT: return "LEFT";
|
||||
case GLFW_FKEY_RIGHT: return "RIGHT";
|
||||
case GLFW_FKEY_UP: return "UP";
|
||||
case GLFW_FKEY_DOWN: return "DOWN";
|
||||
case GLFW_FKEY_PAGE_UP: return "PAGE_UP";
|
||||
case GLFW_FKEY_PAGE_DOWN: return "PAGE_DOWN";
|
||||
case GLFW_FKEY_HOME: return "HOME";
|
||||
case GLFW_FKEY_END: return "END";
|
||||
case GLFW_FKEY_CAPS_LOCK: return "CAPS_LOCK";
|
||||
case GLFW_FKEY_SCROLL_LOCK: return "SCROLL_LOCK";
|
||||
case GLFW_FKEY_NUM_LOCK: return "NUM_LOCK";
|
||||
case GLFW_FKEY_PRINT_SCREEN: return "PRINT_SCREEN";
|
||||
case GLFW_FKEY_PAUSE: return "PAUSE";
|
||||
case GLFW_FKEY_MENU: return "MENU";
|
||||
case GLFW_FKEY_F1: return "F1";
|
||||
case GLFW_FKEY_F2: return "F2";
|
||||
case GLFW_FKEY_F3: return "F3";
|
||||
case GLFW_FKEY_F4: return "F4";
|
||||
case GLFW_FKEY_F5: return "F5";
|
||||
case GLFW_FKEY_F6: return "F6";
|
||||
case GLFW_FKEY_F7: return "F7";
|
||||
case GLFW_FKEY_F8: return "F8";
|
||||
case GLFW_FKEY_F9: return "F9";
|
||||
case GLFW_FKEY_F10: return "F10";
|
||||
case GLFW_FKEY_F11: return "F11";
|
||||
case GLFW_FKEY_F12: return "F12";
|
||||
case GLFW_FKEY_F13: return "F13";
|
||||
case GLFW_FKEY_F14: return "F14";
|
||||
case GLFW_FKEY_F15: return "F15";
|
||||
case GLFW_FKEY_F16: return "F16";
|
||||
case GLFW_FKEY_F17: return "F17";
|
||||
case GLFW_FKEY_F18: return "F18";
|
||||
case GLFW_FKEY_F19: return "F19";
|
||||
case GLFW_FKEY_F20: return "F20";
|
||||
case GLFW_FKEY_F21: return "F21";
|
||||
case GLFW_FKEY_F22: return "F22";
|
||||
case GLFW_FKEY_F23: return "F23";
|
||||
case GLFW_FKEY_F24: return "F24";
|
||||
case GLFW_FKEY_F25: return "F25";
|
||||
case GLFW_FKEY_F26: return "F26";
|
||||
case GLFW_FKEY_F27: return "F27";
|
||||
case GLFW_FKEY_F28: return "F28";
|
||||
case GLFW_FKEY_F29: return "F29";
|
||||
case GLFW_FKEY_F30: return "F30";
|
||||
case GLFW_FKEY_F31: return "F31";
|
||||
case GLFW_FKEY_F32: return "F32";
|
||||
case GLFW_FKEY_F33: return "F33";
|
||||
case GLFW_FKEY_F34: return "F34";
|
||||
case GLFW_FKEY_F35: return "F35";
|
||||
case GLFW_FKEY_KP_0: return "KP_0";
|
||||
case GLFW_FKEY_KP_1: return "KP_1";
|
||||
case GLFW_FKEY_KP_2: return "KP_2";
|
||||
case GLFW_FKEY_KP_3: return "KP_3";
|
||||
case GLFW_FKEY_KP_4: return "KP_4";
|
||||
case GLFW_FKEY_KP_5: return "KP_5";
|
||||
case GLFW_FKEY_KP_6: return "KP_6";
|
||||
case GLFW_FKEY_KP_7: return "KP_7";
|
||||
case GLFW_FKEY_KP_8: return "KP_8";
|
||||
case GLFW_FKEY_KP_9: return "KP_9";
|
||||
case GLFW_FKEY_KP_DECIMAL: return "KP_DECIMAL";
|
||||
case GLFW_FKEY_KP_DIVIDE: return "KP_DIVIDE";
|
||||
case GLFW_FKEY_KP_MULTIPLY: return "KP_MULTIPLY";
|
||||
case GLFW_FKEY_KP_SUBTRACT: return "KP_SUBTRACT";
|
||||
case GLFW_FKEY_KP_ADD: return "KP_ADD";
|
||||
case GLFW_FKEY_KP_ENTER: return "KP_ENTER";
|
||||
case GLFW_FKEY_KP_EQUAL: return "KP_EQUAL";
|
||||
case GLFW_FKEY_KP_SEPARATOR: return "KP_SEPARATOR";
|
||||
case GLFW_FKEY_KP_LEFT: return "KP_LEFT";
|
||||
case GLFW_FKEY_KP_RIGHT: return "KP_RIGHT";
|
||||
case GLFW_FKEY_KP_UP: return "KP_UP";
|
||||
case GLFW_FKEY_KP_DOWN: return "KP_DOWN";
|
||||
case GLFW_FKEY_KP_PAGE_UP: return "KP_PAGE_UP";
|
||||
case GLFW_FKEY_KP_PAGE_DOWN: return "KP_PAGE_DOWN";
|
||||
case GLFW_FKEY_KP_HOME: return "KP_HOME";
|
||||
case GLFW_FKEY_KP_END: return "KP_END";
|
||||
case GLFW_FKEY_KP_INSERT: return "KP_INSERT";
|
||||
case GLFW_FKEY_KP_DELETE: return "KP_DELETE";
|
||||
case GLFW_FKEY_KP_BEGIN: return "KP_BEGIN";
|
||||
case GLFW_FKEY_MEDIA_PLAY: return "MEDIA_PLAY";
|
||||
case GLFW_FKEY_MEDIA_PAUSE: return "MEDIA_PAUSE";
|
||||
case GLFW_FKEY_MEDIA_PLAY_PAUSE: return "MEDIA_PLAY_PAUSE";
|
||||
case GLFW_FKEY_MEDIA_REVERSE: return "MEDIA_REVERSE";
|
||||
case GLFW_FKEY_MEDIA_STOP: return "MEDIA_STOP";
|
||||
case GLFW_FKEY_MEDIA_FAST_FORWARD: return "MEDIA_FAST_FORWARD";
|
||||
case GLFW_FKEY_MEDIA_REWIND: return "MEDIA_REWIND";
|
||||
case GLFW_FKEY_MEDIA_TRACK_NEXT: return "MEDIA_TRACK_NEXT";
|
||||
case GLFW_FKEY_MEDIA_TRACK_PREVIOUS: return "MEDIA_TRACK_PREVIOUS";
|
||||
case GLFW_FKEY_MEDIA_RECORD: return "MEDIA_RECORD";
|
||||
case GLFW_FKEY_LOWER_VOLUME: return "LOWER_VOLUME";
|
||||
case GLFW_FKEY_RAISE_VOLUME: return "RAISE_VOLUME";
|
||||
case GLFW_FKEY_MUTE_VOLUME: return "MUTE_VOLUME";
|
||||
case GLFW_FKEY_LEFT_SHIFT: return "LEFT_SHIFT";
|
||||
case GLFW_FKEY_LEFT_CONTROL: return "LEFT_CONTROL";
|
||||
case GLFW_FKEY_LEFT_ALT: return "LEFT_ALT";
|
||||
case GLFW_FKEY_LEFT_SUPER: return "LEFT_SUPER";
|
||||
case GLFW_FKEY_LEFT_HYPER: return "LEFT_HYPER";
|
||||
case GLFW_FKEY_LEFT_META: return "LEFT_META";
|
||||
case GLFW_FKEY_RIGHT_SHIFT: return "RIGHT_SHIFT";
|
||||
case GLFW_FKEY_RIGHT_CONTROL: return "RIGHT_CONTROL";
|
||||
case GLFW_FKEY_RIGHT_ALT: return "RIGHT_ALT";
|
||||
case GLFW_FKEY_RIGHT_SUPER: return "RIGHT_SUPER";
|
||||
case GLFW_FKEY_RIGHT_HYPER: return "RIGHT_HYPER";
|
||||
case GLFW_FKEY_RIGHT_META: return "RIGHT_META";
|
||||
case GLFW_FKEY_ISO_LEVEL3_SHIFT: return "ISO_LEVEL3_SHIFT";
|
||||
case GLFW_FKEY_ISO_LEVEL5_SHIFT: return "ISO_LEVEL5_SHIFT";
|
||||
/* end functional key names */
|
||||
case 0: return "UNKNOWN";
|
||||
}
|
||||
static char buf[16];
|
||||
encode_utf8(key, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
// Center the cursor in the content area of the specified window
|
||||
@@ -674,6 +675,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode)
|
||||
return window->stickyMouseButtons;
|
||||
case GLFW_LOCK_KEY_MODS:
|
||||
return window->lockKeyMods;
|
||||
case GLFW_RAW_MOUSE_MOTION:
|
||||
return window->rawMouseMotion;
|
||||
}
|
||||
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
|
||||
@@ -717,13 +720,15 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
|
||||
|
||||
if (!value)
|
||||
{
|
||||
int i;
|
||||
|
||||
// Release all sticky keys
|
||||
for (i = 0; i <= GLFW_KEY_LAST; i++)
|
||||
for (unsigned i = arraysz(window->activated_keys) - 1; i-- > 0;)
|
||||
{
|
||||
if (window->keys[i] == _GLFW_STICK)
|
||||
window->keys[i] = GLFW_RELEASE;
|
||||
if (window->activated_keys[i].action == _GLFW_STICK) {
|
||||
if (i < arraysz(window->activated_keys) - 1) {
|
||||
memmove(window->activated_keys + i, window->activated_keys + i + 1, sizeof(window->activated_keys[0]) * (arraysz(window->activated_keys) - 1 - i));
|
||||
}
|
||||
memset(window->activated_keys + arraysz(window->activated_keys) - 1, 0, sizeof(window->activated_keys[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -753,63 +758,76 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
|
||||
{
|
||||
window->lockKeyMods = value ? true : false;
|
||||
}
|
||||
else if (mode == GLFW_RAW_MOUSE_MOTION)
|
||||
{
|
||||
if (!_glfwPlatformRawMouseMotionSupported())
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Raw mouse motion is not supported on this system");
|
||||
return;
|
||||
}
|
||||
|
||||
value = value ? true : false;
|
||||
if (window->rawMouseMotion == value)
|
||||
return;
|
||||
|
||||
window->rawMouseMotion = value;
|
||||
_glfwPlatformSetRawMouseMotion(window, value);
|
||||
}
|
||||
else
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode);
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetKeyName(int key, int native_key)
|
||||
GLFWAPI int glfwRawMouseMotionSupported(void)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(false);
|
||||
return _glfwPlatformRawMouseMotionSupported();
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetKeyName(uint32_t key, int native_key)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
if (key) return _glfwGetKeyName(key);
|
||||
|
||||
if (key != GLFW_KEY_UNKNOWN)
|
||||
{
|
||||
if (key != GLFW_KEY_KP_EQUAL &&
|
||||
(key < GLFW_KEY_KP_0 || key > GLFW_KEY_KP_ADD) &&
|
||||
(key < GLFW_KEY_APOSTROPHE || key > GLFW_KEY_LAST_PRINTABLE))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
native_key = _glfwPlatformGetNativeKeyForKey(key);
|
||||
}
|
||||
|
||||
native_key = _glfwPlatformGetNativeKeyForKey(key);
|
||||
return _glfwPlatformGetNativeKeyName(native_key);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetNativeKeyForKey(int key)
|
||||
GLFWAPI int glfwGetNativeKeyForKey(uint32_t key)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(-1);
|
||||
|
||||
if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key);
|
||||
return GLFW_RELEASE;
|
||||
}
|
||||
|
||||
return _glfwPlatformGetNativeKeyForKey(key);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetKey(GLFWwindow* handle, int key)
|
||||
GLFWAPI GLFWKeyAction glfwGetKey(GLFWwindow* handle, uint32_t key)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE);
|
||||
if (!key) return GLFW_RELEASE;
|
||||
|
||||
if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key);
|
||||
return GLFW_RELEASE;
|
||||
int current_action = GLFW_RELEASE;
|
||||
const unsigned sz = arraysz(window->activated_keys);
|
||||
int idx = -1;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
if (window->activated_keys[i].key == key) {
|
||||
idx = i;
|
||||
current_action = window->activated_keys[i].action;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (window->keys[key] == _GLFW_STICK)
|
||||
|
||||
if (current_action == _GLFW_STICK)
|
||||
{
|
||||
// Sticky mode: release key now
|
||||
window->keys[key] = GLFW_RELEASE;
|
||||
return GLFW_PRESS;
|
||||
set_key_action(window, key, GLFW_RELEASE, idx);
|
||||
current_action = GLFW_PRESS;
|
||||
}
|
||||
|
||||
return (int) window->keys[key];
|
||||
return current_action;
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetMouseButton(GLFWwindow* handle, int button)
|
||||
@@ -995,13 +1013,13 @@ GLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* handle, GLFWkeyboard
|
||||
return cbfun;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwUpdateIMEState(GLFWwindow* handle, int which, int a, int b, int c, int d) {
|
||||
GLFWAPI void glfwUpdateIMEState(GLFWwindow* handle, const GLFWIMEUpdateEvent *ev) {
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND) || defined(_GLFW_COCOA)
|
||||
_glfwPlatformUpdateIMEState(window, which, a, b, c, d);
|
||||
_glfwPlatformUpdateIMEState(window, ev);
|
||||
#else
|
||||
(void)window; (void)which; (void)a; (void)b; (void)c; (void)d;
|
||||
#endif
|
||||
@@ -1076,6 +1094,9 @@ GLFWAPI int glfwJoystickPresent(int jid)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initJoysticks())
|
||||
return false;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
if (!js->present)
|
||||
return false;
|
||||
@@ -1101,6 +1122,9 @@ GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!initJoysticks())
|
||||
return NULL;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
if (!js->present)
|
||||
return NULL;
|
||||
@@ -1130,6 +1154,9 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!initJoysticks())
|
||||
return NULL;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
if (!js->present)
|
||||
return NULL;
|
||||
@@ -1163,6 +1190,9 @@ GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!initJoysticks())
|
||||
return NULL;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
if (!js->present)
|
||||
return NULL;
|
||||
@@ -1189,6 +1219,9 @@ GLFWAPI const char* glfwGetJoystickName(int jid)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!initJoysticks())
|
||||
return NULL;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
if (!js->present)
|
||||
return NULL;
|
||||
@@ -1214,6 +1247,9 @@ GLFWAPI const char* glfwGetJoystickGUID(int jid)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!initJoysticks())
|
||||
return NULL;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
if (!js->present)
|
||||
return NULL;
|
||||
@@ -1259,6 +1295,10 @@ GLFWAPI void* glfwGetJoystickUserPointer(int jid)
|
||||
GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
if (!initJoysticks())
|
||||
return NULL;
|
||||
|
||||
_GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, cbfun);
|
||||
return cbfun;
|
||||
}
|
||||
@@ -1338,6 +1378,9 @@ GLFWAPI int glfwJoystickIsGamepad(int jid)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initJoysticks())
|
||||
return false;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
if (!js->present)
|
||||
return false;
|
||||
@@ -1363,6 +1406,9 @@ GLFWAPI const char* glfwGetGamepadName(int jid)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!initJoysticks())
|
||||
return NULL;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
if (!js->present)
|
||||
return NULL;
|
||||
@@ -1395,6 +1441,9 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initJoysticks())
|
||||
return false;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
if (!js->present)
|
||||
return false;
|
||||
@@ -1411,8 +1460,18 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
|
||||
if (e->type == _GLFW_JOYSTICK_AXIS)
|
||||
{
|
||||
const float value = js->axes[e->index] * e->axisScale + e->axisOffset;
|
||||
if (value > 0.f)
|
||||
state->buttons[i] = GLFW_PRESS;
|
||||
// HACK: This should be baked into the value transform
|
||||
// TODO: Bake into transform when implementing output modifiers
|
||||
if (e->axisOffset < 0 || (e->axisOffset == 0 && e->axisScale > 0))
|
||||
{
|
||||
if (value >= 0.f)
|
||||
state->buttons[i] = GLFW_PRESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value <= 0.f)
|
||||
state->buttons[i] = GLFW_PRESS;
|
||||
}
|
||||
}
|
||||
else if (e->type == _GLFW_JOYSTICK_HATBIT)
|
||||
{
|
||||
|
||||
50
glfw/internal.h
vendored
50
glfw/internal.h
vendored
@@ -198,6 +198,9 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void);
|
||||
#error "No supported window creation API selected"
|
||||
#endif
|
||||
|
||||
#include "egl_context.h"
|
||||
#include "osmesa_context.h"
|
||||
|
||||
#define remove_i_from_array(array, i, count) { \
|
||||
(count)--; \
|
||||
if ((i) < (count)) { \
|
||||
@@ -271,8 +274,9 @@ struct _GLFWerror
|
||||
struct _GLFWinitconfig
|
||||
{
|
||||
bool hatButtons;
|
||||
int angleType;
|
||||
bool debugKeyboard;
|
||||
bool enableJoysticks;
|
||||
bool debugRendering;
|
||||
struct {
|
||||
bool menubar;
|
||||
bool chdir;
|
||||
@@ -299,6 +303,7 @@ struct _GLFWwndconfig
|
||||
bool maximized;
|
||||
bool centerCursor;
|
||||
bool focusOnShow;
|
||||
bool mousePassthrough;
|
||||
bool scaleToMonitor;
|
||||
struct {
|
||||
bool retina;
|
||||
@@ -392,11 +397,16 @@ struct _GLFWcontext
|
||||
// This is defined in the context API's context.h
|
||||
_GLFW_PLATFORM_CONTEXT_STATE
|
||||
// This is defined in egl_context.h
|
||||
_GLFW_EGL_CONTEXT_STATE;
|
||||
_GLFWcontextEGL egl;
|
||||
// This is defined in osmesa_context.h
|
||||
_GLFW_OSMESA_CONTEXT_STATE;
|
||||
_GLFWcontextOSMesa osmesa;
|
||||
};
|
||||
|
||||
typedef struct GLFWKeyState {
|
||||
uint32_t key;
|
||||
char action;
|
||||
} GLFWKeyState;
|
||||
|
||||
// Window and context structure
|
||||
//
|
||||
struct _GLFWwindow
|
||||
@@ -409,6 +419,7 @@ struct _GLFWwindow
|
||||
bool autoIconify;
|
||||
bool floating;
|
||||
bool focusOnShow;
|
||||
bool mousePassthrough;
|
||||
bool shouldClose;
|
||||
void* userPointer;
|
||||
GLFWid id;
|
||||
@@ -426,9 +437,10 @@ struct _GLFWwindow
|
||||
bool lockKeyMods;
|
||||
int cursorMode;
|
||||
char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1];
|
||||
char keys[GLFW_KEY_LAST + 1];
|
||||
GLFWKeyState activated_keys[16];
|
||||
// Virtual cursor position when cursor is disabled
|
||||
double virtualCursorPosX, virtualCursorPosY;
|
||||
bool rawMouseMotion;
|
||||
|
||||
_GLFWcontext context;
|
||||
|
||||
@@ -568,6 +580,7 @@ struct _GLFWlibrary
|
||||
_GLFWmonitor** monitors;
|
||||
int monitorCount;
|
||||
|
||||
bool joysticksInitialized;
|
||||
_GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1];
|
||||
_GLFWmapping* mappings;
|
||||
int mappingCount;
|
||||
@@ -601,8 +614,11 @@ struct _GLFWlibrary
|
||||
struct {
|
||||
GLFWmonitorfun monitor;
|
||||
GLFWjoystickfun joystick;
|
||||
GLFWapplicationclosefun application_close;
|
||||
GLFWdrawtextfun draw_text;
|
||||
} callbacks;
|
||||
|
||||
|
||||
// This is defined in the window API's platform.h
|
||||
_GLFW_PLATFORM_LIBRARY_WINDOW_STATE;
|
||||
// This is defined in the context API's context.h
|
||||
@@ -610,9 +626,9 @@ struct _GLFWlibrary
|
||||
// This is defined in the platform's joystick.h
|
||||
_GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE
|
||||
// This is defined in egl_context.h
|
||||
_GLFW_EGL_LIBRARY_CONTEXT_STATE;
|
||||
_GLFWlibraryEGL egl;
|
||||
// This is defined in osmesa_context.h
|
||||
_GLFW_OSMESA_LIBRARY_CONTEXT_STATE;
|
||||
_GLFWlibraryOSMesa osmesa;
|
||||
};
|
||||
|
||||
// Global state shared between compilation units of GLFW
|
||||
@@ -631,6 +647,8 @@ const char* _glfwPlatformGetVersionString(void);
|
||||
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos);
|
||||
void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos);
|
||||
void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode);
|
||||
void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, bool enabled);
|
||||
bool _glfwPlatformRawMouseMotionSupported(void);
|
||||
int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||
const GLFWimage* image, int xhot, int yhot, int count);
|
||||
int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape);
|
||||
@@ -638,7 +656,7 @@ void _glfwPlatformDestroyCursor(_GLFWcursor* cursor);
|
||||
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor);
|
||||
|
||||
const char* _glfwPlatformGetNativeKeyName(int native_key);
|
||||
int _glfwPlatformGetNativeKeyForKey(int key);
|
||||
int _glfwPlatformGetNativeKeyForKey(uint32_t key);
|
||||
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor);
|
||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos);
|
||||
@@ -657,6 +675,8 @@ void _glfwPlatformSetPrimarySelectionString(const char* string);
|
||||
const char* _glfwPlatformGetPrimarySelectionString(void);
|
||||
#endif
|
||||
|
||||
bool _glfwPlatformInitJoysticks(void);
|
||||
void _glfwPlatformTerminateJoysticks(void);
|
||||
int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode);
|
||||
void _glfwPlatformUpdateGamepadGUID(char* guid);
|
||||
|
||||
@@ -708,14 +728,20 @@ float _glfwPlatformGetWindowOpacity(_GLFWwindow* window);
|
||||
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, bool enabled);
|
||||
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled);
|
||||
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled);
|
||||
void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window, bool enabled);
|
||||
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity);
|
||||
void _glfwPlatformUpdateIMEState(_GLFWwindow *w, int which, int a, int b, int c, int d);
|
||||
void _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev);
|
||||
void _glfwPlatformChangeCursorTheme(void);
|
||||
|
||||
void _glfwPlatformPollEvents(void);
|
||||
void _glfwPlatformWaitEvents(void);
|
||||
void _glfwPlatformWaitEventsTimeout(monotonic_t timeout);
|
||||
void _glfwPlatformPostEmptyEvent(void);
|
||||
|
||||
EGLenum _glfwPlatformGetEGLPlatform(EGLint** attribs);
|
||||
EGLNativeDisplayType _glfwPlatformGetEGLNativeDisplay(void);
|
||||
EGLNativeWindowType _glfwPlatformGetEGLNativeWindow(_GLFWwindow* window);
|
||||
|
||||
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions);
|
||||
int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance,
|
||||
VkPhysicalDevice device,
|
||||
@@ -753,13 +779,12 @@ void _glfwInputWindowDamage(_GLFWwindow* window);
|
||||
void _glfwInputWindowCloseRequest(_GLFWwindow* window);
|
||||
void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
|
||||
|
||||
void _glfwInitializeKeyEvent(GLFWkeyevent *ev, int key, int native_key, int action, int mods);
|
||||
void _glfwInputKeyboard(_GLFWwindow *window, GLFWkeyevent *ev);
|
||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags);
|
||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags, int mods);
|
||||
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
|
||||
void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos);
|
||||
void _glfwInputCursorEnter(_GLFWwindow* window, bool entered);
|
||||
void _glfwInputDrop(_GLFWwindow* window, int count, const char** names);
|
||||
int _glfwInputDrop(_GLFWwindow* window, const char *mime, const char *text, size_t sz);
|
||||
void _glfwInputJoystick(_GLFWjoystick* js, int event);
|
||||
void _glfwInputJoystickAxis(_GLFWjoystick* js, int axis, float value);
|
||||
void _glfwInputJoystickButton(_GLFWjoystick* js, int button, char value);
|
||||
@@ -789,9 +814,6 @@ void _glfwDebug(const char* format, ...);
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool _glfwStringInExtensionString(const char* string, const char* extensions);
|
||||
const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired,
|
||||
const _GLFWfbconfig* alternatives,
|
||||
unsigned int count);
|
||||
bool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig);
|
||||
bool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig);
|
||||
|
||||
109
glfw/linux_desktop_settings.c
vendored
Normal file
109
glfw/linux_desktop_settings.c
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* linux_cursor_settings.c
|
||||
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include "linux_desktop_settings.h"
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
|
||||
static const char *DESKTOP_SERVICE = "org.freedesktop.portal.Desktop";
|
||||
static const char *DESKTOP_PATH = "/org/freedesktop/portal/desktop";
|
||||
static const char *DESKTOP_INTERFACE = "org.freedesktop.portal.Settings";
|
||||
static const char *GNOME_DESKTOP_NAMESPACE = "org.gnome.desktop.interface";
|
||||
|
||||
|
||||
static char theme_name[64] = {0};
|
||||
static int theme_size = -1;
|
||||
static bool gnome_cursor_theme_read = false, gnome_cursor_size_read = false;
|
||||
|
||||
static bool
|
||||
parse_dbus_message_for_type(DBusMessage *const reply, const char *errmsg, const int type, void *value) {
|
||||
DBusMessageIter iter[3];
|
||||
dbus_message_iter_init(reply, &iter[0]);
|
||||
#define FAIL { _glfwInputError(GLFW_PLATFORM_ERROR, "%s", errmsg); return false; }
|
||||
if (dbus_message_iter_get_arg_type(&iter[0]) != DBUS_TYPE_VARIANT) FAIL;
|
||||
dbus_message_iter_recurse(&iter[0], &iter[1]);
|
||||
if (dbus_message_iter_get_arg_type(&iter[1]) != DBUS_TYPE_VARIANT) FAIL;
|
||||
dbus_message_iter_recurse(&iter[1], &iter[2]);
|
||||
if (dbus_message_iter_get_arg_type(&iter[2]) != type) FAIL;
|
||||
dbus_message_iter_get_basic(&iter[2], value);
|
||||
return true;
|
||||
#undef FAIL
|
||||
}
|
||||
|
||||
#define HANDLER(name) void name(DBusMessage *msg, const char* errmsg, void *data) { \
|
||||
(void)data; \
|
||||
if (errmsg) { \
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "%s: failed with error: %s", #name, errmsg); \
|
||||
return; \
|
||||
}
|
||||
|
||||
HANDLER(on_gnome_cursor_theme_read)
|
||||
const char *name;
|
||||
if (!parse_dbus_message_for_type(msg, "Failed to get cursor theme name from reply", DBUS_TYPE_STRING, &name)) return;
|
||||
if (name && name[0]) {
|
||||
gnome_cursor_theme_read = true;
|
||||
strncpy(theme_name, name, sizeof(theme_name) - 1);
|
||||
if (gnome_cursor_size_read) _glfwPlatformChangeCursorTheme();
|
||||
}
|
||||
}
|
||||
|
||||
HANDLER(on_gnome_cursor_size_read)
|
||||
int32_t sz;
|
||||
if (!parse_dbus_message_for_type(msg, "Failed to get cursor theme size from reply", DBUS_TYPE_INT32, &sz)) return;
|
||||
gnome_cursor_size_read = true;
|
||||
theme_size = sz;
|
||||
if (gnome_cursor_theme_read) _glfwPlatformChangeCursorTheme();
|
||||
}
|
||||
#undef HANDLER
|
||||
|
||||
|
||||
static bool
|
||||
call_read(DBusConnection *session_bus, dbus_pending_callback callback, const char *namespace, const char *key) {
|
||||
return glfw_dbus_call_method_with_reply(
|
||||
session_bus, DESKTOP_SERVICE, DESKTOP_PATH, DESKTOP_INTERFACE, "Read", DBUS_TIMEOUT_USE_DEFAULT,
|
||||
callback, NULL, DBUS_TYPE_STRING, &namespace, DBUS_TYPE_STRING, &key, DBUS_TYPE_INVALID);
|
||||
}
|
||||
|
||||
static void
|
||||
get_from_gnome(void) {
|
||||
theme_size = 32;
|
||||
DBusConnection *session_bus = glfw_dbus_session_bus();
|
||||
if (session_bus) {
|
||||
const char *theme_key = "cursor-theme";
|
||||
call_read(session_bus, on_gnome_cursor_theme_read, GNOME_DESKTOP_NAMESPACE, theme_key);
|
||||
const char *size_key = "cursor-size";
|
||||
call_read(session_bus, on_gnome_cursor_size_read, GNOME_DESKTOP_NAMESPACE, size_key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
glfw_current_cursor_theme(const char **theme, int *size) {
|
||||
*theme = theme_name[0] ? theme_name : NULL;
|
||||
*size = (theme_size > 0 && theme_size < 2048) ? theme_size : 32;
|
||||
}
|
||||
|
||||
static void
|
||||
get_cursor_theme_from_env(void) {
|
||||
const char *q = getenv("XCURSOR_THEME");
|
||||
if (q) strncpy(theme_name, q, sizeof(theme_name)-1);
|
||||
const char *env = getenv("XCURSOR_SIZE");
|
||||
theme_size = 32;
|
||||
if (env) {
|
||||
const int retval = atoi(env);
|
||||
if (retval > 0 && retval < 2048) theme_size = retval;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
glfw_initialize_desktop_settings(void) {
|
||||
get_cursor_theme_from_env();
|
||||
const char *desktop = getenv("XDG_CURRENT_DESKTOP");
|
||||
bool is_gnome = desktop && strncasecmp(desktop, "GNOME", sizeof("GNOME") - 1) == 0;
|
||||
if (is_gnome) get_from_gnome();
|
||||
}
|
||||
14
glfw/linux_desktop_settings.h
vendored
Normal file
14
glfw/linux_desktop_settings.h
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dbus_glfw.h"
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
void glfw_initialize_desktop_settings(void);
|
||||
void glfw_current_cursor_theme(const char **theme, int *size);
|
||||
98
glfw/linux_joystick.c
vendored
98
glfw/linux_joystick.c
vendored
@@ -264,9 +264,50 @@ static int compareJoysticks(const void* fp, const void* sp)
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialize joystick interface
|
||||
//
|
||||
bool _glfwInitJoysticksLinux(void)
|
||||
void _glfwDetectJoystickConnectionLinux(void)
|
||||
{
|
||||
if (_glfw.linjs.inotify <= 0)
|
||||
return;
|
||||
|
||||
ssize_t offset = 0;
|
||||
char buffer[16384];
|
||||
const ssize_t size = read(_glfw.linjs.inotify, buffer, sizeof(buffer));
|
||||
|
||||
while (size > offset)
|
||||
{
|
||||
regmatch_t match;
|
||||
const struct inotify_event* e = (struct inotify_event*) (buffer + offset);
|
||||
|
||||
offset += sizeof(struct inotify_event) + e->len;
|
||||
|
||||
if (regexec(&_glfw.linjs.regex, e->name, 1, &match, 0) != 0)
|
||||
continue;
|
||||
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), "/dev/input/%s", e->name);
|
||||
|
||||
if (e->mask & (IN_CREATE | IN_ATTRIB))
|
||||
openJoystickDevice(path);
|
||||
else if (e->mask & IN_DELETE)
|
||||
{
|
||||
for (int jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
|
||||
{
|
||||
if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0)
|
||||
{
|
||||
closeJoystick(_glfw.joysticks + jid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool _glfwPlatformInitJoysticks(void)
|
||||
{
|
||||
const char* dirname = "/dev/input";
|
||||
|
||||
@@ -320,9 +361,7 @@ bool _glfwInitJoysticksLinux(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Close all opened joystick handles
|
||||
//
|
||||
void _glfwTerminateJoysticksLinux(void)
|
||||
void _glfwPlatformTerminateJoysticks(void)
|
||||
{
|
||||
int jid;
|
||||
|
||||
@@ -333,61 +372,16 @@ void _glfwTerminateJoysticksLinux(void)
|
||||
closeJoystick(js);
|
||||
}
|
||||
|
||||
regfree(&_glfw.linjs.regex);
|
||||
|
||||
if (_glfw.linjs.inotify > 0)
|
||||
{
|
||||
if (_glfw.linjs.watch > 0)
|
||||
inotify_rm_watch(_glfw.linjs.inotify, _glfw.linjs.watch);
|
||||
|
||||
close(_glfw.linjs.inotify);
|
||||
regfree(&_glfw.linjs.regex);
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwDetectJoystickConnectionLinux(void)
|
||||
{
|
||||
|
||||
if (_glfw.linjs.inotify <= 0)
|
||||
return;
|
||||
|
||||
ssize_t offset = 0;
|
||||
char buffer[16384];
|
||||
const ssize_t size = read(_glfw.linjs.inotify, buffer, sizeof(buffer));
|
||||
|
||||
while (size > offset)
|
||||
{
|
||||
regmatch_t match;
|
||||
const struct inotify_event* e = (struct inotify_event*) (buffer + offset);
|
||||
|
||||
offset += sizeof(struct inotify_event) + e->len;
|
||||
|
||||
if (regexec(&_glfw.linjs.regex, e->name, 1, &match, 0) != 0)
|
||||
continue;
|
||||
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), "/dev/input/%s", e->name);
|
||||
|
||||
if (e->mask & (IN_CREATE | IN_ATTRIB))
|
||||
openJoystickDevice(path);
|
||||
else if (e->mask & IN_DELETE)
|
||||
{
|
||||
for (int jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
|
||||
{
|
||||
if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0)
|
||||
{
|
||||
closeJoystick(_glfw.joysticks + jid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode UNUSED)
|
||||
{
|
||||
// Read all queued events (non-blocking)
|
||||
|
||||
3
glfw/linux_joystick.h
vendored
3
glfw/linux_joystick.h
vendored
@@ -55,7 +55,4 @@ typedef struct _GLFWlibraryLinux
|
||||
bool dropped;
|
||||
} _GLFWlibraryLinux;
|
||||
|
||||
|
||||
bool _glfwInitJoysticksLinux(void);
|
||||
void _glfwTerminateJoysticksLinux(void);
|
||||
void _glfwDetectJoystickConnectionLinux(void);
|
||||
|
||||
3
glfw/memfd.h
vendored
3
glfw/memfd.h
vendored
@@ -39,8 +39,7 @@ static inline int memfd_create(const char *name, unsigned int flags) {
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
static inline int
|
||||
createTmpfileCloexec(char* tmpname)
|
||||
static inline int createTmpfileCloexec(char* tmpname)
|
||||
{
|
||||
int fd;
|
||||
|
||||
|
||||
5
glfw/null_init.c
vendored
5
glfw/null_init.c
vendored
@@ -29,6 +29,8 @@
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
@@ -36,11 +38,14 @@
|
||||
|
||||
int _glfwPlatformInit(void)
|
||||
{
|
||||
_glfwPollMonitorsNull();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _glfwPlatformTerminate(void)
|
||||
{
|
||||
free(_glfw.null.clipboardString);
|
||||
_glfwTerminateOSMesa();
|
||||
}
|
||||
|
||||
|
||||
9
glfw/null_joystick.c
vendored
9
glfw/null_joystick.c
vendored
@@ -33,6 +33,15 @@
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool _glfwPlatformInitJoysticks(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void _glfwPlatformTerminateJoysticks(void)
|
||||
{
|
||||
}
|
||||
|
||||
int _glfwPlatformPollJoystick(_GLFWjoystick* js UNUSED, int mode UNUSED)
|
||||
{
|
||||
return false;
|
||||
|
||||
102
glfw/null_monitor.c
vendored
102
glfw/null_monitor.c
vendored
@@ -29,17 +29,53 @@
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
// The the sole (fake) video mode of our (sole) fake monitor
|
||||
//
|
||||
static GLFWvidmode getVideoMode(void)
|
||||
{
|
||||
GLFWvidmode mode;
|
||||
mode.width = 1920;
|
||||
mode.height = 1080;
|
||||
mode.redBits = 8;
|
||||
mode.greenBits = 8;
|
||||
mode.blueBits = 8;
|
||||
mode.refreshRate = 60;
|
||||
return mode;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwPollMonitorsNull(void)
|
||||
{
|
||||
const float dpi = 141.f;
|
||||
const GLFWvidmode mode = getVideoMode();
|
||||
_GLFWmonitor* monitor = _glfwAllocMonitor("Null SuperNoop 0",
|
||||
(int) (mode.width * 25.4f / dpi),
|
||||
(int) (mode.height * 25.4f / dpi));
|
||||
_glfwInputMonitor(monitor, GLFW_CONNECTED, _GLFW_INSERT_FIRST);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor UNUSED)
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||
{
|
||||
_glfwFreeGammaArrays(&monitor->null.ramp);
|
||||
}
|
||||
|
||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor UNUSED, int* xpos UNUSED, int* ypos UNUSED)
|
||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor UNUSED, int* xpos, int* ypos)
|
||||
{
|
||||
if (xpos)
|
||||
*xpos = 0;
|
||||
if (ypos)
|
||||
*ypos = 0;
|
||||
}
|
||||
|
||||
void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor UNUSED,
|
||||
@@ -52,26 +88,72 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor UNUSED,
|
||||
}
|
||||
|
||||
void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor UNUSED,
|
||||
int* xpos UNUSED, int* ypos UNUSED,
|
||||
int* width UNUSED, int* height UNUSED)
|
||||
int* xpos, int* ypos,
|
||||
int* width, int* height)
|
||||
{
|
||||
const GLFWvidmode mode = getVideoMode();
|
||||
|
||||
if (xpos)
|
||||
*xpos = 0;
|
||||
if (ypos)
|
||||
*ypos = 10;
|
||||
if (width)
|
||||
*width = mode.width;
|
||||
if (height)
|
||||
*height = mode.height - 10;
|
||||
}
|
||||
|
||||
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor UNUSED, int* found UNUSED)
|
||||
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor UNUSED, int* found)
|
||||
{
|
||||
return NULL;
|
||||
GLFWvidmode* mode = calloc(1, sizeof(GLFWvidmode));
|
||||
*mode = getVideoMode();
|
||||
*found = 1;
|
||||
return mode;
|
||||
}
|
||||
|
||||
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor UNUSED, GLFWvidmode* mode UNUSED)
|
||||
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor UNUSED, GLFWvidmode* mode)
|
||||
{
|
||||
*mode = getVideoMode();
|
||||
}
|
||||
|
||||
bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor UNUSED, GLFWgammaramp* ramp UNUSED)
|
||||
bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
{
|
||||
return false;
|
||||
if (!monitor->null.ramp.size)
|
||||
{
|
||||
_glfwAllocGammaArrays(&monitor->null.ramp, 256);
|
||||
|
||||
for (unsigned int i = 0; i < monitor->null.ramp.size; i++)
|
||||
{
|
||||
const float gamma = 2.2f;
|
||||
float value;
|
||||
value = i / (float) (monitor->null.ramp.size - 1);
|
||||
value = powf(value, 1.f / gamma) * 65535.f + 0.5f;
|
||||
value = _glfw_fminf(value, 65535.f);
|
||||
|
||||
monitor->null.ramp.red[i] = (unsigned short) value;
|
||||
monitor->null.ramp.green[i] = (unsigned short) value;
|
||||
monitor->null.ramp.blue[i] = (unsigned short) value;
|
||||
}
|
||||
}
|
||||
|
||||
_glfwAllocGammaArrays(ramp, monitor->null.ramp.size);
|
||||
memcpy(ramp->red, monitor->null.ramp.red, sizeof(short) * ramp->size);
|
||||
memcpy(ramp->green, monitor->null.ramp.green, sizeof(short) * ramp->size);
|
||||
memcpy(ramp->blue, monitor->null.ramp.blue, sizeof(short) * ramp->size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor UNUSED, const GLFWgammaramp* ramp UNUSED)
|
||||
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
|
||||
{
|
||||
if (monitor->null.ramp.size != ramp->size)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Null: Gamma ramp size must match current ramp size");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(monitor->null.ramp.red, ramp->red, sizeof(short) * ramp->size);
|
||||
memcpy(monitor->null.ramp.green, ramp->green, sizeof(short) * ramp->size);
|
||||
memcpy(monitor->null.ramp.blue, ramp->blue, sizeof(short) * ramp->size);
|
||||
}
|
||||
|
||||
|
||||
42
glfw/null_platform.h
vendored
42
glfw/null_platform.h
vendored
@@ -27,17 +27,14 @@
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNull null
|
||||
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNull null
|
||||
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNull null
|
||||
#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNull null
|
||||
|
||||
#define _GLFW_PLATFORM_CONTEXT_STATE
|
||||
#define _GLFW_PLATFORM_MONITOR_STATE
|
||||
#define _GLFW_PLATFORM_CURSOR_STATE
|
||||
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE
|
||||
#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE
|
||||
#define _GLFW_EGL_CONTEXT_STATE
|
||||
#define _GLFW_EGL_LIBRARY_CONTEXT_STATE
|
||||
|
||||
#include "osmesa_context.h"
|
||||
#include "posix_time.h"
|
||||
#include "posix_thread.h"
|
||||
#include "null_joystick.h"
|
||||
@@ -56,7 +53,36 @@
|
||||
//
|
||||
typedef struct _GLFWwindowNull
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int xpos;
|
||||
int ypos;
|
||||
int width;
|
||||
int height;
|
||||
char* title;
|
||||
bool visible;
|
||||
bool iconified;
|
||||
bool maximized;
|
||||
bool resizable;
|
||||
bool decorated;
|
||||
bool floating;
|
||||
bool transparent;
|
||||
float opacity;
|
||||
} _GLFWwindowNull;
|
||||
|
||||
// Null-specific per-monitor data
|
||||
//
|
||||
typedef struct _GLFWmonitorNull
|
||||
{
|
||||
GLFWgammaramp ramp;
|
||||
} _GLFWmonitorNull;
|
||||
|
||||
// Null-specific global data
|
||||
//
|
||||
typedef struct _GLFWlibraryNull
|
||||
{
|
||||
int xcursor;
|
||||
int ycursor;
|
||||
char* clipboardString;
|
||||
_GLFWwindow* focusedWindow;
|
||||
} _GLFWlibraryNull;
|
||||
|
||||
void _glfwPollMonitorsNull(void);
|
||||
|
||||
509
glfw/null_window.c
vendored
509
glfw/null_window.c
vendored
@@ -30,12 +30,71 @@
|
||||
#include "internal.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static void applySizeLimits(_GLFWwindow* window, int* width, int* height)
|
||||
{
|
||||
if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
|
||||
{
|
||||
const float ratio = (float) window->numer / (float) window->denom;
|
||||
*height = (int) (*width / ratio);
|
||||
}
|
||||
|
||||
if (window->minwidth != GLFW_DONT_CARE && *width < window->minwidth)
|
||||
*width = window->minwidth;
|
||||
else if (window->maxwidth != GLFW_DONT_CARE && *width > window->maxwidth)
|
||||
*width = window->maxwidth;
|
||||
|
||||
if (window->minheight != GLFW_DONT_CARE && *height < window->minheight)
|
||||
*height = window->minheight;
|
||||
else if (window->maxheight != GLFW_DONT_CARE && *height > window->maxheight)
|
||||
*height = window->maxheight;
|
||||
}
|
||||
|
||||
static void fitToMonitor(_GLFWwindow* window)
|
||||
{
|
||||
GLFWvidmode mode;
|
||||
_glfwPlatformGetVideoMode(window->monitor, &mode);
|
||||
_glfwPlatformGetMonitorPos(window->monitor,
|
||||
&window->null.xpos,
|
||||
&window->null.ypos);
|
||||
window->null.width = mode.width;
|
||||
window->null.height = mode.height;
|
||||
}
|
||||
|
||||
static void acquireMonitor(_GLFWwindow* window)
|
||||
{
|
||||
_glfwInputMonitorWindow(window->monitor, window);
|
||||
}
|
||||
|
||||
static void releaseMonitor(_GLFWwindow* window)
|
||||
{
|
||||
if (window->monitor->window != window)
|
||||
return;
|
||||
|
||||
_glfwInputMonitorWindow(window->monitor, NULL);
|
||||
}
|
||||
|
||||
static int createNativeWindow(_GLFWwindow* window,
|
||||
const _GLFWwndconfig* wndconfig)
|
||||
const _GLFWwndconfig* wndconfig,
|
||||
const _GLFWfbconfig* fbconfig)
|
||||
{
|
||||
window->null.width = wndconfig->width;
|
||||
window->null.height = wndconfig->height;
|
||||
if (window->monitor)
|
||||
fitToMonitor(window);
|
||||
else
|
||||
{
|
||||
window->null.xpos = 17;
|
||||
window->null.ypos = 17;
|
||||
window->null.width = wndconfig->width;
|
||||
window->null.height = wndconfig->height;
|
||||
}
|
||||
|
||||
window->null.visible = wndconfig->visible;
|
||||
window->null.decorated = wndconfig->decorated;
|
||||
window->null.maximized = wndconfig->maximized;
|
||||
window->null.floating = wndconfig->floating;
|
||||
window->null.transparent = fbconfig->transparent;
|
||||
window->null.opacity = 1.f;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -50,7 +109,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig)
|
||||
{
|
||||
if (!createNativeWindow(window, wndconfig))
|
||||
if (!createNativeWindow(window, wndconfig, fbconfig))
|
||||
return false;
|
||||
|
||||
if (ctxconfig->client != GLFW_NO_API)
|
||||
@@ -70,11 +129,24 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
||||
}
|
||||
}
|
||||
|
||||
if (window->monitor)
|
||||
{
|
||||
_glfwPlatformShowWindow(window);
|
||||
_glfwPlatformFocusWindow(window);
|
||||
acquireMonitor(window);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
||||
{
|
||||
if (window->monitor)
|
||||
releaseMonitor(window);
|
||||
|
||||
if (_glfw.null.focusedWindow == window)
|
||||
_glfw.null.focusedWindow = NULL;
|
||||
|
||||
if (window->context.destroy)
|
||||
window->context.destroy(window);
|
||||
}
|
||||
@@ -88,20 +160,60 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window UNUSED, int count UNUSED,
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window UNUSED,
|
||||
_GLFWmonitor* monitor UNUSED,
|
||||
int xpos UNUSED, int ypos UNUSED,
|
||||
int width UNUSED, int height UNUSED,
|
||||
void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
|
||||
_GLFWmonitor* monitor,
|
||||
int xpos, int ypos,
|
||||
int width, int height,
|
||||
int refreshRate UNUSED)
|
||||
{
|
||||
if (window->monitor == monitor)
|
||||
{
|
||||
if (!monitor)
|
||||
{
|
||||
_glfwPlatformSetWindowPos(window, xpos, ypos);
|
||||
_glfwPlatformSetWindowSize(window, width, height);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (window->monitor)
|
||||
releaseMonitor(window);
|
||||
|
||||
_glfwInputWindowMonitor(window, monitor);
|
||||
|
||||
if (window->monitor)
|
||||
{
|
||||
window->null.visible = true;
|
||||
acquireMonitor(window);
|
||||
fitToMonitor(window);
|
||||
}
|
||||
else
|
||||
{
|
||||
_glfwPlatformSetWindowPos(window, xpos, ypos);
|
||||
_glfwPlatformSetWindowSize(window, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformGetWindowPos(_GLFWwindow* window UNUSED, int* xpos UNUSED, int* ypos UNUSED)
|
||||
void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos)
|
||||
{
|
||||
if (xpos)
|
||||
*xpos = window->null.xpos;
|
||||
if (ypos)
|
||||
*ypos = window->null.ypos;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowPos(_GLFWwindow* window UNUSED, int xpos UNUSED, int ypos UNUSED)
|
||||
void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos)
|
||||
{
|
||||
if (window->monitor)
|
||||
return;
|
||||
|
||||
if (window->null.xpos != xpos || window->null.ypos != ypos)
|
||||
{
|
||||
window->null.xpos = xpos;
|
||||
window->null.ypos = ypos;
|
||||
_glfwInputWindowPos(window, xpos, ypos);
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
|
||||
@@ -114,18 +226,34 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
|
||||
|
||||
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
|
||||
{
|
||||
window->null.width = width;
|
||||
window->null.height = height;
|
||||
if (window->monitor)
|
||||
return;
|
||||
|
||||
if (window->null.width != width || window->null.height != height)
|
||||
{
|
||||
window->null.width = width;
|
||||
window->null.height = height;
|
||||
_glfwInputWindowSize(window, width, height);
|
||||
_glfwInputFramebufferSize(window, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window UNUSED,
|
||||
void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
|
||||
int minwidth UNUSED, int minheight UNUSED,
|
||||
int maxwidth UNUSED, int maxheight UNUSED)
|
||||
{
|
||||
int width = window->null.width;
|
||||
int height = window->null.height;
|
||||
applySizeLimits(window, &width, &height);
|
||||
_glfwPlatformSetWindowSize(window, width, height);
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window UNUSED, int n UNUSED, int d UNUSED)
|
||||
void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int n UNUSED, int d UNUSED)
|
||||
{
|
||||
int width = window->null.width;
|
||||
int height = window->null.height;
|
||||
applySizeLimits(window, &width, &height);
|
||||
_glfwPlatformSetWindowSize(window, width, height);
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window UNUSED, int widthincr UNUSED, int heightincr UNUSED)
|
||||
@@ -140,10 +268,32 @@ void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* heigh
|
||||
*height = window->null.height;
|
||||
}
|
||||
|
||||
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window UNUSED,
|
||||
int* left UNUSED, int* top UNUSED,
|
||||
int* right UNUSED, int* bottom UNUSED)
|
||||
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
|
||||
int* left, int* top,
|
||||
int* right, int* bottom)
|
||||
{
|
||||
if (window->null.decorated && !window->monitor)
|
||||
{
|
||||
if (left)
|
||||
*left = 1;
|
||||
if (top)
|
||||
*top = 10;
|
||||
if (right)
|
||||
*right = 1;
|
||||
if (bottom)
|
||||
*bottom = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (left)
|
||||
*left = 0;
|
||||
if (top)
|
||||
*top = 0;
|
||||
if (right)
|
||||
*right = 0;
|
||||
if (bottom)
|
||||
*bottom = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window UNUSED,
|
||||
@@ -160,59 +310,111 @@ monotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window UNUSED)
|
||||
return ms_to_monotonic_t(500ll);
|
||||
}
|
||||
|
||||
void _glfwPlatformIconifyWindow(_GLFWwindow* window UNUSED)
|
||||
void _glfwPlatformIconifyWindow(_GLFWwindow* window)
|
||||
{
|
||||
if (_glfw.null.focusedWindow == window)
|
||||
{
|
||||
_glfw.null.focusedWindow = NULL;
|
||||
_glfwInputWindowFocus(window, false);
|
||||
}
|
||||
|
||||
if (!window->null.iconified)
|
||||
{
|
||||
window->null.iconified = true;
|
||||
_glfwInputWindowIconify(window, true);
|
||||
|
||||
if (window->monitor)
|
||||
releaseMonitor(window);
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformRestoreWindow(_GLFWwindow* window)
|
||||
{
|
||||
if (window->null.iconified)
|
||||
{
|
||||
window->null.iconified = false;
|
||||
_glfwInputWindowIconify(window, false);
|
||||
|
||||
if (window->monitor)
|
||||
acquireMonitor(window);
|
||||
}
|
||||
else if (window->null.maximized)
|
||||
{
|
||||
window->null.maximized = false;
|
||||
_glfwInputWindowMaximize(window, false);
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
|
||||
{
|
||||
if (!window->null.maximized)
|
||||
{
|
||||
window->null.maximized = true;
|
||||
_glfwInputWindowMaximize(window, true);
|
||||
}
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowMaximized(_GLFWwindow* window)
|
||||
{
|
||||
return window->null.maximized;
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowHovered(_GLFWwindow* window)
|
||||
{
|
||||
return _glfw.null.xcursor >= window->null.xpos &&
|
||||
_glfw.null.ycursor >= window->null.ypos &&
|
||||
_glfw.null.xcursor <= window->null.xpos + window->null.width - 1 &&
|
||||
_glfw.null.ycursor <= window->null.ypos + window->null.height - 1;
|
||||
}
|
||||
|
||||
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window)
|
||||
{
|
||||
return window->null.transparent;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, bool enabled)
|
||||
{
|
||||
window->null.resizable = enabled;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled)
|
||||
{
|
||||
window->null.decorated = enabled;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, bool enabled)
|
||||
{
|
||||
window->null.floating = enabled;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowMousePassthrough(_GLFWwindow* window UNUSED, bool enabled UNUSED)
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformRestoreWindow(_GLFWwindow* window UNUSED)
|
||||
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window)
|
||||
{
|
||||
return window->null.opacity;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity)
|
||||
{
|
||||
window->null.opacity = opacity;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window UNUSED, bool enabled UNUSED)
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformMaximizeWindow(_GLFWwindow* window UNUSED)
|
||||
bool _glfwPlatformRawMouseMotionSupported(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowMaximized(_GLFWwindow* window UNUSED)
|
||||
void _glfwPlatformShowWindow(_GLFWwindow* window)
|
||||
{
|
||||
return false;
|
||||
window->null.visible = true;
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowHovered(_GLFWwindow* window UNUSED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window UNUSED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowResizable(_GLFWwindow* window UNUSED, bool enabled UNUSED)
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window UNUSED, bool enabled UNUSED)
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowFloating(_GLFWwindow* window UNUSED, bool enabled UNUSED)
|
||||
{
|
||||
}
|
||||
|
||||
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window UNUSED)
|
||||
{
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowOpacity(_GLFWwindow* window UNUSED, float opacity UNUSED)
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformShowWindow(_GLFWwindow* window UNUSED)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void _glfwPlatformRequestWindowAttention(_GLFWwindow* window UNUSED)
|
||||
{
|
||||
}
|
||||
@@ -222,21 +424,41 @@ int _glfwPlatformWindowBell(_GLFWwindow* window UNUSED)
|
||||
return false;
|
||||
}
|
||||
|
||||
void _glfwPlatformUnhideWindow(_GLFWwindow* window UNUSED)
|
||||
void _glfwPlatformHideWindow(_GLFWwindow* window)
|
||||
{
|
||||
if (_glfw.null.focusedWindow == window)
|
||||
{
|
||||
_glfw.null.focusedWindow = NULL;
|
||||
_glfwInputWindowFocus(window, false);
|
||||
}
|
||||
|
||||
window->null.visible = false;
|
||||
}
|
||||
|
||||
void _glfwPlatformHideWindow(_GLFWwindow* window UNUSED)
|
||||
void _glfwPlatformFocusWindow(_GLFWwindow* window)
|
||||
{
|
||||
if (_glfw.null.focusedWindow == window)
|
||||
return;
|
||||
|
||||
if (!window->null.visible)
|
||||
return;
|
||||
|
||||
_GLFWwindow* previous = _glfw.null.focusedWindow;
|
||||
_glfw.null.focusedWindow = window;
|
||||
|
||||
if (previous)
|
||||
{
|
||||
_glfwInputWindowFocus(previous, false);
|
||||
if (previous->monitor && previous->autoIconify)
|
||||
_glfwPlatformIconifyWindow(previous);
|
||||
}
|
||||
|
||||
_glfwInputWindowFocus(window, true);
|
||||
}
|
||||
|
||||
void _glfwPlatformFocusWindow(_GLFWwindow* window UNUSED)
|
||||
int _glfwPlatformWindowFocused(_GLFWwindow* window)
|
||||
{
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowFocused(_GLFWwindow* window UNUSED)
|
||||
{
|
||||
return false;
|
||||
return _glfw.null.focusedWindow == window;
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowOccluded(_GLFWwindow* window UNUSED)
|
||||
@@ -244,14 +466,14 @@ int _glfwPlatformWindowOccluded(_GLFWwindow* window UNUSED)
|
||||
return false;
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowIconified(_GLFWwindow* window UNUSED)
|
||||
int _glfwPlatformWindowIconified(_GLFWwindow* window)
|
||||
{
|
||||
return false;
|
||||
return window->null.iconified;
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowVisible(_GLFWwindow* window UNUSED)
|
||||
int _glfwPlatformWindowVisible(_GLFWwindow* window)
|
||||
{
|
||||
return false;
|
||||
return window->null.visible;
|
||||
}
|
||||
|
||||
void _glfwPlatformPollEvents(void)
|
||||
@@ -270,12 +492,18 @@ void _glfwPlatformPostEmptyEvent(void)
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformGetCursorPos(_GLFWwindow* window UNUSED, double* xpos UNUSED, double* ypos UNUSED)
|
||||
void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)
|
||||
{
|
||||
if (xpos)
|
||||
*xpos = _glfw.null.xcursor - window->null.xpos;
|
||||
if (ypos)
|
||||
*ypos = _glfw.null.ycursor - window->null.ypos;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetCursorPos(_GLFWwindow* window UNUSED, double x UNUSED, double y UNUSED)
|
||||
void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
|
||||
{
|
||||
_glfw.null.xcursor = window->null.xpos + (int) x;
|
||||
_glfw.null.ycursor = window->null.ypos + (int) y;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetCursorMode(_GLFWwindow* window UNUSED, int mode UNUSED)
|
||||
@@ -302,23 +530,142 @@ void _glfwPlatformSetCursor(_GLFWwindow* window UNUSED, _GLFWcursor* cursor UNUS
|
||||
{
|
||||
}
|
||||
|
||||
void _glfwPlatformSetClipboardString(const char* string UNUSED)
|
||||
void _glfwPlatformSetClipboardString(const char* string)
|
||||
{
|
||||
char* copy = _glfw_strdup(string);
|
||||
free(_glfw.null.clipboardString);
|
||||
_glfw.null.clipboardString = copy;
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetClipboardString(void)
|
||||
{
|
||||
return _glfw.null.clipboardString;
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetNativeKeyName(int native_key)
|
||||
{
|
||||
switch (scancode)
|
||||
{
|
||||
case GLFW_KEY_APOSTROPHE:
|
||||
return "'";
|
||||
case GLFW_KEY_COMMA:
|
||||
return ",";
|
||||
case GLFW_KEY_MINUS:
|
||||
case GLFW_KEY_KP_SUBTRACT:
|
||||
return "-";
|
||||
case GLFW_KEY_PERIOD:
|
||||
case GLFW_KEY_KP_DECIMAL:
|
||||
return ".";
|
||||
case GLFW_KEY_SLASH:
|
||||
case GLFW_KEY_KP_DIVIDE:
|
||||
return "/";
|
||||
case GLFW_KEY_SEMICOLON:
|
||||
return ";";
|
||||
case GLFW_KEY_EQUAL:
|
||||
case GLFW_KEY_KP_EQUAL:
|
||||
return "=";
|
||||
case GLFW_KEY_LEFT_BRACKET:
|
||||
return "[";
|
||||
case GLFW_KEY_RIGHT_BRACKET:
|
||||
return "]";
|
||||
case GLFW_KEY_KP_MULTIPLY:
|
||||
return "*";
|
||||
case GLFW_KEY_KP_ADD:
|
||||
return "+";
|
||||
case GLFW_KEY_BACKSLASH:
|
||||
case GLFW_KEY_WORLD_1:
|
||||
case GLFW_KEY_WORLD_2:
|
||||
return "\\";
|
||||
case GLFW_KEY_0:
|
||||
case GLFW_KEY_KP_0:
|
||||
return "0";
|
||||
case GLFW_KEY_1:
|
||||
case GLFW_KEY_KP_1:
|
||||
return "1";
|
||||
case GLFW_KEY_2:
|
||||
case GLFW_KEY_KP_2:
|
||||
return "2";
|
||||
case GLFW_KEY_3:
|
||||
case GLFW_KEY_KP_3:
|
||||
return "3";
|
||||
case GLFW_KEY_4:
|
||||
case GLFW_KEY_KP_4:
|
||||
return "4";
|
||||
case GLFW_KEY_5:
|
||||
case GLFW_KEY_KP_5:
|
||||
return "5";
|
||||
case GLFW_KEY_6:
|
||||
case GLFW_KEY_KP_6:
|
||||
return "6";
|
||||
case GLFW_KEY_7:
|
||||
case GLFW_KEY_KP_7:
|
||||
return "7";
|
||||
case GLFW_KEY_8:
|
||||
case GLFW_KEY_KP_8:
|
||||
return "8";
|
||||
case GLFW_KEY_9:
|
||||
case GLFW_KEY_KP_9:
|
||||
return "9";
|
||||
case GLFW_KEY_A:
|
||||
return "a";
|
||||
case GLFW_KEY_B:
|
||||
return "b";
|
||||
case GLFW_KEY_C:
|
||||
return "c";
|
||||
case GLFW_KEY_D:
|
||||
return "d";
|
||||
case GLFW_KEY_E:
|
||||
return "e";
|
||||
case GLFW_KEY_F:
|
||||
return "f";
|
||||
case GLFW_KEY_G:
|
||||
return "g";
|
||||
case GLFW_KEY_H:
|
||||
return "h";
|
||||
case GLFW_KEY_I:
|
||||
return "i";
|
||||
case GLFW_KEY_J:
|
||||
return "j";
|
||||
case GLFW_KEY_K:
|
||||
return "k";
|
||||
case GLFW_KEY_L:
|
||||
return "l";
|
||||
case GLFW_KEY_M:
|
||||
return "m";
|
||||
case GLFW_KEY_N:
|
||||
return "n";
|
||||
case GLFW_KEY_O:
|
||||
return "o";
|
||||
case GLFW_KEY_P:
|
||||
return "p";
|
||||
case GLFW_KEY_Q:
|
||||
return "q";
|
||||
case GLFW_KEY_R:
|
||||
return "r";
|
||||
case GLFW_KEY_S:
|
||||
return "s";
|
||||
case GLFW_KEY_T:
|
||||
return "t";
|
||||
case GLFW_KEY_U:
|
||||
return "u";
|
||||
case GLFW_KEY_V:
|
||||
return "v";
|
||||
case GLFW_KEY_W:
|
||||
return "w";
|
||||
case GLFW_KEY_X:
|
||||
return "x";
|
||||
case GLFW_KEY_Y:
|
||||
return "y";
|
||||
case GLFW_KEY_Z:
|
||||
return "z";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetNativeKeyName(int native_key UNUSED)
|
||||
int _glfwPlatformGetNativeKeyForKey(int key)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
int _glfwPlatformGetNativeKeyForKey(int key UNUSED)
|
||||
{
|
||||
return -1;
|
||||
return key;
|
||||
}
|
||||
|
||||
void _glfwPlatformGetRequiredInstanceExtensions(char** extensions UNUSED)
|
||||
@@ -338,6 +685,6 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance UNUSED,
|
||||
VkSurfaceKHR* surface UNUSED)
|
||||
{
|
||||
// This seems like the most appropriate error to return here
|
||||
return VK_ERROR_INITIALIZATION_FAILED;
|
||||
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||
}
|
||||
|
||||
|
||||
4
glfw/osmesa_context.h
vendored
4
glfw/osmesa_context.h
vendored
@@ -54,10 +54,6 @@ typedef GLFWglproc (GLAPIENTRY * PFN_OSMesaGetProcAddress)(const char*);
|
||||
#define OSMesaGetDepthBuffer _glfw.osmesa.GetDepthBuffer
|
||||
#define OSMesaGetProcAddress _glfw.osmesa.GetProcAddress
|
||||
|
||||
#define _GLFW_OSMESA_CONTEXT_STATE _GLFWcontextOSMesa osmesa
|
||||
#define _GLFW_OSMESA_LIBRARY_CONTEXT_STATE _GLFWlibraryOSMesa osmesa
|
||||
|
||||
|
||||
// OSMesa-specific per-context data
|
||||
//
|
||||
typedef struct _GLFWcontextOSMesa
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
"headers": [
|
||||
"wl_platform.h",
|
||||
"posix_thread.h",
|
||||
"wl_cursors.h",
|
||||
"wl_text_input.h",
|
||||
"xkb_glfw.h",
|
||||
"dbus_glfw.h",
|
||||
"ibus_glfw.h",
|
||||
@@ -63,6 +65,8 @@
|
||||
"linux_joystick.h",
|
||||
"null_joystick.h",
|
||||
"linux_notify.h",
|
||||
"linux_desktop_settings.h",
|
||||
"wl_client_side_decorations.h",
|
||||
"main_loop.h"
|
||||
],
|
||||
"protocols": [
|
||||
@@ -72,12 +76,16 @@
|
||||
"unstable/pointer-constraints/pointer-constraints-unstable-v1.xml",
|
||||
"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml",
|
||||
"unstable/xdg-decoration/xdg-decoration-unstable-v1.xml",
|
||||
"unstable/primary-selection/primary-selection-unstable-v1.xml"
|
||||
"unstable/primary-selection/primary-selection-unstable-v1.xml",
|
||||
"unstable/text-input/text-input-unstable-v3.xml"
|
||||
],
|
||||
"sources": [
|
||||
"wl_init.c",
|
||||
"wl_monitor.c",
|
||||
"wl_window.c",
|
||||
"wl_cursors.c",
|
||||
"wl_text_input.c",
|
||||
"wl_client_side_decorations.c",
|
||||
"posix_thread.c",
|
||||
"xkb_glfw.c",
|
||||
"dbus_glfw.c",
|
||||
@@ -86,6 +94,7 @@
|
||||
"osmesa_context.c",
|
||||
"backend_utils.c",
|
||||
"linux_joystick.c",
|
||||
"linux_desktop_settings.c",
|
||||
"null_joystick.c",
|
||||
"linux_notify.c"
|
||||
]
|
||||
|
||||
47
glfw/window.c
vendored
47
glfw/window.c
vendored
@@ -50,21 +50,19 @@ void _glfwInputWindowFocus(_GLFWwindow* window, bool focused)
|
||||
|
||||
if (!focused)
|
||||
{
|
||||
int key, button;
|
||||
_glfw.focusedWindowId = 0;
|
||||
|
||||
for (key = 0; key <= GLFW_KEY_LAST; key++)
|
||||
for (unsigned i = 0; i < arraysz(window->activated_keys); i++)
|
||||
{
|
||||
if (window->keys[key] == GLFW_PRESS)
|
||||
if (window->activated_keys[i].key > 0 && window->activated_keys[i].action == GLFW_PRESS)
|
||||
{
|
||||
const int native_key = _glfwPlatformGetNativeKeyForKey(key);
|
||||
GLFWkeyevent ev;
|
||||
_glfwInitializeKeyEvent(&ev, key, native_key, GLFW_RELEASE, 0);
|
||||
const int native_key = _glfwPlatformGetNativeKeyForKey(window->activated_keys[i].key);
|
||||
GLFWkeyevent ev = {.key = window->activated_keys[i].key, .native_key = native_key, .action = GLFW_RELEASE};
|
||||
_glfwInputKeyboard(window, &ev);
|
||||
}
|
||||
}
|
||||
|
||||
for (button = 0; button <= GLFW_MOUSE_BUTTON_LAST; button++)
|
||||
for (int button = 0; button <= GLFW_MOUSE_BUTTON_LAST; button++)
|
||||
{
|
||||
if (window->mouseButtons[button] == GLFW_PRESS)
|
||||
_glfwInputMouseClick(window, button, GLFW_RELEASE, 0);
|
||||
@@ -240,13 +238,14 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
|
||||
window->videoMode.blueBits = fbconfig.blueBits;
|
||||
window->videoMode.refreshRate = _glfw.hints.refreshRate;
|
||||
|
||||
window->monitor = (_GLFWmonitor*) monitor;
|
||||
window->resizable = wndconfig.resizable;
|
||||
window->decorated = wndconfig.decorated;
|
||||
window->autoIconify = wndconfig.autoIconify;
|
||||
window->floating = wndconfig.floating;
|
||||
window->focusOnShow = wndconfig.focusOnShow;
|
||||
window->cursorMode = GLFW_CURSOR_NORMAL;
|
||||
window->monitor = (_GLFWmonitor*) monitor;
|
||||
window->resizable = wndconfig.resizable;
|
||||
window->decorated = wndconfig.decorated;
|
||||
window->autoIconify = wndconfig.autoIconify;
|
||||
window->floating = wndconfig.floating;
|
||||
window->focusOnShow = wndconfig.focusOnShow;
|
||||
window->mousePassthrough = wndconfig.mousePassthrough;
|
||||
window->cursorMode = GLFW_CURSOR_NORMAL;
|
||||
|
||||
window->minwidth = GLFW_DONT_CARE;
|
||||
window->minheight = GLFW_DONT_CARE;
|
||||
@@ -273,6 +272,9 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height,
|
||||
}
|
||||
}
|
||||
|
||||
if (wndconfig.mousePassthrough)
|
||||
_glfwPlatformSetWindowMousePassthrough(window, true);
|
||||
|
||||
if (window->monitor)
|
||||
{
|
||||
if (wndconfig.centerCursor)
|
||||
@@ -422,6 +424,9 @@ GLFWAPI void glfwWindowHint(int hint, int value)
|
||||
case GLFW_FOCUS_ON_SHOW:
|
||||
_glfw.hints.window.focusOnShow = value ? true : false;
|
||||
return;
|
||||
case GLFW_MOUSE_PASSTHROUGH:
|
||||
_glfw.hints.window.mousePassthrough = value ? true : false;
|
||||
return;
|
||||
case GLFW_CLIENT_API:
|
||||
_glfw.hints.context.client = value;
|
||||
return;
|
||||
@@ -440,7 +445,7 @@ GLFWAPI void glfwWindowHint(int hint, int value)
|
||||
case GLFW_OPENGL_FORWARD_COMPAT:
|
||||
_glfw.hints.context.forward = value ? true : false;
|
||||
return;
|
||||
case GLFW_OPENGL_DEBUG_CONTEXT:
|
||||
case GLFW_CONTEXT_DEBUG:
|
||||
_glfw.hints.context.debug = value ? true : false;
|
||||
return;
|
||||
case GLFW_CONTEXT_NO_ERROR:
|
||||
@@ -905,6 +910,8 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib)
|
||||
return _glfwPlatformWindowHovered(window);
|
||||
case GLFW_FOCUS_ON_SHOW:
|
||||
return window->focusOnShow;
|
||||
case GLFW_MOUSE_PASSTHROUGH:
|
||||
return window->mousePassthrough;
|
||||
case GLFW_TRANSPARENT_FRAMEBUFFER:
|
||||
return _glfwPlatformFramebufferTransparent(window);
|
||||
case GLFW_OCCLUDED:
|
||||
@@ -931,7 +938,7 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib)
|
||||
return window->context.robustness;
|
||||
case GLFW_OPENGL_FORWARD_COMPAT:
|
||||
return window->context.forward;
|
||||
case GLFW_OPENGL_DEBUG_CONTEXT:
|
||||
case GLFW_CONTEXT_DEBUG:
|
||||
return window->context.debug;
|
||||
case GLFW_OPENGL_PROFILE:
|
||||
return window->context.profile;
|
||||
@@ -985,6 +992,14 @@ GLFWAPI void glfwSetWindowAttrib(GLFWwindow* handle, int attrib, int value)
|
||||
}
|
||||
else if (attrib == GLFW_FOCUS_ON_SHOW)
|
||||
window->focusOnShow = value;
|
||||
else if (attrib == GLFW_MOUSE_PASSTHROUGH)
|
||||
{
|
||||
if (window->mousePassthrough == value)
|
||||
return;
|
||||
|
||||
window->mousePassthrough = value;
|
||||
_glfwPlatformSetWindowMousePassthrough(window, value);
|
||||
}
|
||||
else
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute 0x%08X", attrib);
|
||||
}
|
||||
|
||||
415
glfw/wl_client_side_decorations.c
vendored
Normal file
415
glfw/wl_client_side_decorations.c
vendored
Normal file
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* wl_client_side_decorations.c
|
||||
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include "wl_client_side_decorations.h"
|
||||
|
||||
#include "backend_utils.h"
|
||||
#include <sys/mman.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define debug(...) if (_glfw.hints.init.debugRendering) fprintf(stderr, __VA_ARGS__);
|
||||
#define decs window->wl.decorations
|
||||
|
||||
#define ARGB(a, r, g, b) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
|
||||
#define A(x) (((x) >> 24) & 0xff)
|
||||
#define R(x) (((x) >> 16) & 0xff)
|
||||
#define G(x) (((x) >> 8) & 0xff)
|
||||
#define B(x) ((x) & 0xff)
|
||||
#define SWAP(x, y) do { __typeof__(x) SWAP = x; x = y; y = SWAP; } while (0)
|
||||
|
||||
static const uint32_t passive_bg_color = 0xffeeeeee;
|
||||
static const uint32_t active_bg_color = 0xffdddad6;
|
||||
typedef float kernel_type;
|
||||
|
||||
static void
|
||||
build_blur_kernel(kernel_type *blur_kernel, const size_t size, kernel_type sigma) {
|
||||
// 1D Normalized Gaussian
|
||||
const kernel_type half = size / (kernel_type)2;
|
||||
kernel_type sum = 0;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
kernel_type f = (i - half);
|
||||
blur_kernel[i] = (kernel_type)exp(- f * f / sigma);
|
||||
sum += blur_kernel[i];
|
||||
}
|
||||
for (size_t i = 0; i < size; i++) blur_kernel[i] /= sum;
|
||||
}
|
||||
|
||||
static void
|
||||
blur_mask(kernel_type *image_data, ssize_t width, ssize_t height, ssize_t kernel_size, kernel_type sigma, kernel_type *scratch, kernel_type *blur_kernel, ssize_t margin) {
|
||||
(void)margin;
|
||||
build_blur_kernel(blur_kernel, kernel_size, sigma);
|
||||
const size_t half = kernel_size / 2;
|
||||
|
||||
for (ssize_t y = 0; y < height; y++) {
|
||||
kernel_type *s = image_data + y * width, *d = scratch + y * width;
|
||||
for (ssize_t x = 0; x < width; x++) {
|
||||
kernel_type a = 0;
|
||||
for (ssize_t k = 0; k < kernel_size; k++) {
|
||||
const ssize_t px = x + k - half;
|
||||
if (0 <= px && px < width) a += s[px] * blur_kernel[k];
|
||||
}
|
||||
d[x] = a;
|
||||
}
|
||||
}
|
||||
|
||||
for (ssize_t y = 0; y < height; y++) {
|
||||
kernel_type *d = image_data + y * width;
|
||||
for (ssize_t x = 0; x < width; x++) {
|
||||
kernel_type a = 0;
|
||||
for (ssize_t k = 0; k < kernel_size; k++) {
|
||||
const ssize_t py = y + k - half;
|
||||
if (0 <= py && py < height) {
|
||||
kernel_type *s = scratch + py * width;
|
||||
a += s[x] * blur_kernel[k];
|
||||
}
|
||||
}
|
||||
d[x] = a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static kernel_type*
|
||||
create_shadow_mask(size_t width, size_t height, size_t margin, size_t kernel_size, kernel_type base_alpha, kernel_type sigma) {
|
||||
kernel_type *mask = calloc(sizeof(kernel_type), 2 * width * height + kernel_size);
|
||||
if (!mask) return NULL;
|
||||
for (size_t y = margin; y < height - margin; y++) {
|
||||
kernel_type *row = mask + y * width;
|
||||
for (size_t x = margin; x < width - margin; x++) row[x] = base_alpha;
|
||||
}
|
||||
blur_mask(mask, width, height, kernel_size, sigma, mask + width * height, (kernel_type*)(mask + 2 * width * height), margin);
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void
|
||||
swap_buffers(_GLFWWaylandBufferPair *pair) {
|
||||
SWAP(pair->front, pair->back);
|
||||
SWAP(pair->data.front, pair->data.back);
|
||||
}
|
||||
|
||||
static size_t
|
||||
init_buffer_pair(_GLFWWaylandBufferPair *pair, size_t width, size_t height, unsigned scale) {
|
||||
memset(pair, 0, sizeof(_GLFWWaylandBufferPair));
|
||||
pair->width = width * scale;
|
||||
pair->height = height * scale;
|
||||
pair->stride = 4 * pair->width;
|
||||
pair->size_in_bytes = pair->stride * pair->height;
|
||||
return 2 * pair->size_in_bytes;
|
||||
}
|
||||
|
||||
static void
|
||||
alloc_buffer_pair(_GLFWWaylandBufferPair *pair, struct wl_shm_pool *pool, uint8_t *data, size_t *offset) {
|
||||
pair->data.a = data + *offset;
|
||||
pair->a = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);
|
||||
*offset += pair->size_in_bytes;
|
||||
pair->data.b = data + *offset;
|
||||
pair->b = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);
|
||||
*offset += pair->size_in_bytes;
|
||||
pair->front = pair->a; pair->back = pair->b;
|
||||
pair->data.front = pair->data.a; pair->data.back = pair->data.b;
|
||||
}
|
||||
|
||||
#define st decs.shadow_tile
|
||||
|
||||
void
|
||||
initialize_csd_metrics(_GLFWwindow *window) {
|
||||
decs.metrics.width = 12;
|
||||
decs.metrics.top = 36;
|
||||
decs.metrics.visible_titlebar_height = window->wl.decorations.metrics.top - window->wl.decorations.metrics.width;
|
||||
decs.metrics.horizontal = 2 * window->wl.decorations.metrics.width;
|
||||
decs.metrics.vertical = window->wl.decorations.metrics.width + window->wl.decorations.metrics.top;
|
||||
}
|
||||
|
||||
static size_t
|
||||
create_shadow_tile(_GLFWwindow *window) {
|
||||
const size_t margin = decs.bottom.buffer.height;
|
||||
if (st.data && st.for_decoration_size == margin) return margin;
|
||||
st.for_decoration_size = margin;
|
||||
free(st.data);
|
||||
st.segments = 7;
|
||||
st.stride = st.segments * margin;
|
||||
st.corner_size = margin * (st.segments - 1) / 2;
|
||||
kernel_type* mask = create_shadow_mask(st.stride, st.stride, margin, 2 * margin + 1, (kernel_type)0.7, 32 * margin);
|
||||
st.data = malloc(sizeof(uint32_t) * st.stride * st.stride);
|
||||
if (st.data) for (size_t i = 0; i < st.stride * st.stride; i++) st.data[i] = ((uint8_t)(mask[i] * 255)) << 24;
|
||||
free(mask);
|
||||
return margin;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
render_title_bar(_GLFWwindow *window, bool to_front_buffer) {
|
||||
const bool is_focused = window->id == _glfw.focusedWindowId;
|
||||
uint32_t bg_color = is_focused ? active_bg_color : passive_bg_color;
|
||||
uint8_t *output = to_front_buffer ? decs.top.buffer.data.front : decs.top.buffer.data.back;
|
||||
|
||||
// render shadow part
|
||||
const size_t margin = create_shadow_tile(window);
|
||||
const size_t edge_segment_size = st.corner_size - margin;
|
||||
const uint8_t divisor = is_focused ? 1 : 2;
|
||||
for (size_t y = 0; y < margin; y++) {
|
||||
// left segment
|
||||
uint32_t *s = st.data + y * st.stride + margin;
|
||||
uint32_t *d = (uint32_t*)(output + y * decs.top.buffer.stride);
|
||||
for (size_t x = 0; x < edge_segment_size; x++) d[x] = (A(s[x]) / divisor) << 24;
|
||||
// middle segment
|
||||
s += edge_segment_size;
|
||||
size_t limit = decs.top.buffer.width > edge_segment_size ? decs.top.buffer.width - edge_segment_size : 0;
|
||||
for (size_t x = edge_segment_size, sx = 0; x < limit; x++, sx = (sx + 1) % margin) d[x] = (A(s[sx]) / divisor) << 24;
|
||||
// right segment
|
||||
s += margin;
|
||||
for (size_t x = limit; x < decs.top.buffer.width; x++, s++) d[x] = (A(*s) / divisor) << 24;
|
||||
}
|
||||
|
||||
// render text part
|
||||
output += decs.top.buffer.stride * margin;
|
||||
if (window->wl.title && window->wl.title[0] && _glfw.callbacks.draw_text) {
|
||||
uint32_t fg_color = is_focused ? 0xff444444 : 0xff888888;
|
||||
if (_glfw.callbacks.draw_text((GLFWwindow*)window, window->wl.title, fg_color, bg_color, output, decs.top.buffer.width, decs.top.buffer.height - margin, 0, 0, 0)) return;
|
||||
}
|
||||
for (uint32_t *px = (uint32_t*)output, *end = (uint32_t*)(output + decs.top.buffer.size_in_bytes); px < end; px++) {
|
||||
*px = bg_color;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_title_bar(_GLFWwindow *window) {
|
||||
render_title_bar(window, false);
|
||||
swap_buffers(&decs.top.buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
render_edges(_GLFWwindow *window) {
|
||||
const size_t margin = create_shadow_tile(window);
|
||||
if (!st.data) return; // out of memory
|
||||
|
||||
// bottom edge
|
||||
uint32_t *src = st.data + (st.segments - 1) * margin * st.stride;
|
||||
for (size_t y = 0; y < margin; y++) {
|
||||
uint32_t *d = (uint32_t*)(decs.bottom.buffer.data.front + y * decs.bottom.buffer.stride);
|
||||
uint32_t *s = src + st.stride * y;
|
||||
// left corner
|
||||
for (size_t x = 0; x < st.corner_size && x < decs.bottom.buffer.width; x++) d[x] = s[x];
|
||||
// middle
|
||||
size_t pos = st.corner_size, limit = decs.bottom.buffer.width > st.corner_size ? decs.bottom.buffer.width - st.corner_size : 0;
|
||||
s += st.corner_size;
|
||||
while (pos < limit) {
|
||||
uint32_t *p = d + pos;
|
||||
for (size_t x = 0; x < margin && pos + x < limit; x++) p[x] = s[x];
|
||||
pos += margin;
|
||||
}
|
||||
// right corner
|
||||
s += margin;
|
||||
for (size_t x = 0; x < st.corner_size && limit + x < decs.bottom.buffer.width; x++) d[limit + x] = s[x];
|
||||
}
|
||||
|
||||
// upper corners
|
||||
for (size_t y = 0; y < st.corner_size && y < decs.left.buffer.height; y++) {
|
||||
uint32_t *left_src = st.data + st.stride * y;
|
||||
uint32_t *left_dest = (uint32_t*)(decs.left.buffer.data.front + y * decs.left.buffer.stride);
|
||||
memcpy(left_dest, left_src, margin * sizeof(uint32_t));
|
||||
uint32_t *right_src = left_src + 2 * st.corner_size;
|
||||
uint32_t *right_dest = (uint32_t*)(decs.right.buffer.data.front + y * decs.right.buffer.stride);
|
||||
memcpy(right_dest, right_src, margin * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
// lower corners
|
||||
size_t src_height = st.corner_size - margin;
|
||||
size_t dest_top = decs.left.buffer.height > src_height ? decs.left.buffer.height - src_height : 0;
|
||||
size_t src_top = st.corner_size + margin;
|
||||
for (size_t src_y = src_top, dest_y = dest_top; src_y < src_top + src_height && dest_y < decs.left.buffer.height; src_y++, dest_y++) {
|
||||
uint32_t *s = st.data + st.stride * src_y;
|
||||
uint32_t *d = (uint32_t*)(decs.left.buffer.data.front + dest_y * decs.left.buffer.stride);
|
||||
memcpy(d, s, margin * sizeof(uint32_t));
|
||||
s += 2 * st.corner_size;
|
||||
d = (uint32_t*)(decs.right.buffer.data.front + dest_y * decs.left.buffer.stride);
|
||||
memcpy(d, s, margin * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
// sides
|
||||
size_t limit = decs.left.buffer.height > src_height ? decs.left.buffer.height - src_height : 0;
|
||||
for (size_t dest_y = st.corner_size, src_y = 0; dest_y < limit; dest_y++, src_y = (src_y + 1) % margin) {
|
||||
uint32_t *src = st.data + (st.corner_size + src_y) * st.stride;
|
||||
uint32_t *left_dest = (uint32_t*)(decs.left.buffer.data.front + dest_y * decs.left.buffer.stride);
|
||||
memcpy(left_dest, src, margin * sizeof(uint32_t));
|
||||
src += 2 * st.corner_size;
|
||||
uint32_t *right_dest = (uint32_t*)(decs.right.buffer.data.front + dest_y * decs.right.buffer.stride);
|
||||
memcpy(right_dest, src, margin * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
#define copy(which) for (uint32_t *src = (uint32_t*)decs.which.buffer.data.front, *dest = (uint32_t*)decs.which.buffer.data.back; src < (uint32_t*)(decs.which.buffer.data.front + decs.which.buffer.size_in_bytes); src++, dest++) *dest = (A(*src) / 2 ) << 24;
|
||||
copy(left); copy(bottom); copy(right);
|
||||
#undef copy
|
||||
|
||||
}
|
||||
#undef st
|
||||
|
||||
static bool
|
||||
create_shm_buffers(_GLFWwindow* window) {
|
||||
const unsigned scale = window->wl.scale >= 1 ? window->wl.scale : 1;
|
||||
|
||||
const size_t vertical_width = decs.metrics.width, vertical_height = window->wl.height + decs.metrics.top;
|
||||
const size_t horizontal_height = decs.metrics.width, horizontal_width = window->wl.width + 2 * decs.metrics.width;
|
||||
|
||||
decs.mapping.size = 0;
|
||||
decs.mapping.size += init_buffer_pair(&decs.top.buffer, window->wl.width, decs.metrics.top, scale);
|
||||
decs.mapping.size += init_buffer_pair(&decs.left.buffer, vertical_width, vertical_height, scale);
|
||||
decs.mapping.size += init_buffer_pair(&decs.bottom.buffer, horizontal_width, horizontal_height, scale);
|
||||
decs.mapping.size += init_buffer_pair(&decs.right.buffer, vertical_width, vertical_height, scale);
|
||||
|
||||
int fd = createAnonymousFile(decs.mapping.size);
|
||||
if (fd < 0) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Creating a buffer file for %zu B failed: %s",
|
||||
decs.mapping.size, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
decs.mapping.data = mmap(NULL, decs.mapping.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (decs.mapping.data == MAP_FAILED) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: mmap failed: %s", strerror(errno));
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, decs.mapping.size);
|
||||
close(fd);
|
||||
size_t offset = 0;
|
||||
#define a(which) alloc_buffer_pair(&decs.which.buffer, pool, decs.mapping.data, &offset)
|
||||
a(top); a(left); a(bottom); a(right);
|
||||
#undef a
|
||||
wl_shm_pool_destroy(pool);
|
||||
create_shadow_tile(window);
|
||||
render_title_bar(window, true);
|
||||
render_edges(window);
|
||||
debug("Created decoration buffers at scale: %u vertical_height: %zu horizontal_width: %zu\n", scale, vertical_height, horizontal_width);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
free_csd_surfaces(_GLFWwindow *window) {
|
||||
#define d(which) {\
|
||||
if (decs.which.subsurface) wl_subsurface_destroy(decs.which.subsurface); \
|
||||
decs.which.subsurface = NULL; \
|
||||
if (decs.which.surface) wl_surface_destroy(decs.which.surface); \
|
||||
decs.which.surface = NULL; \
|
||||
}
|
||||
d(left); d(top); d(right); d(bottom);
|
||||
#undef d
|
||||
}
|
||||
|
||||
static void
|
||||
free_csd_buffers(_GLFWwindow *window) {
|
||||
#define d(which) { \
|
||||
if (decs.which.buffer.a) wl_buffer_destroy(decs.which.buffer.a); \
|
||||
if (decs.which.buffer.b) wl_buffer_destroy(decs.which.buffer.b); \
|
||||
memset(&decs.which.buffer, 0, sizeof(_GLFWWaylandBufferPair)); \
|
||||
}
|
||||
d(left); d(top); d(right); d(bottom);
|
||||
#undef d
|
||||
if (decs.mapping.data) munmap(decs.mapping.data, decs.mapping.size);
|
||||
decs.mapping.data = NULL; decs.mapping.size = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
position_csd_surface(_GLFWWaylandCSDEdge *s, int x, int y, int scale) {
|
||||
wl_surface_set_buffer_scale(s->surface, scale);
|
||||
s->x = x; s->y = y;
|
||||
wl_subsurface_set_position(s->subsurface, s->x, s->y);
|
||||
}
|
||||
|
||||
static void
|
||||
create_csd_surfaces(_GLFWwindow *window, _GLFWWaylandCSDEdge *s) {
|
||||
s->surface = wl_compositor_create_surface(_glfw.wl.compositor);
|
||||
s->subsurface = wl_subcompositor_get_subsurface(_glfw.wl.subcompositor, s->surface, window->wl.surface);
|
||||
}
|
||||
|
||||
#define damage_csd(which, xbuffer) \
|
||||
wl_surface_attach(decs.which.surface, xbuffer, 0, 0); \
|
||||
wl_surface_damage(decs.which.surface, 0, 0, decs.which.buffer.width, decs.which.buffer.height); \
|
||||
wl_surface_commit(decs.which.surface)
|
||||
|
||||
bool
|
||||
ensure_csd_resources(_GLFWwindow *window) {
|
||||
if (!window->decorated || window->wl.decorations.serverSide) return false;
|
||||
const bool is_focused = window->id == _glfw.focusedWindowId;
|
||||
const bool focus_changed = is_focused != decs.for_window_state.focused;
|
||||
const bool size_changed = (
|
||||
decs.for_window_state.width != window->wl.width ||
|
||||
decs.for_window_state.height != window->wl.height ||
|
||||
decs.for_window_state.scale != window->wl.scale ||
|
||||
!decs.mapping.data
|
||||
);
|
||||
const bool needs_update = focus_changed || size_changed || !decs.left.surface;
|
||||
if (!needs_update) return false;
|
||||
if (size_changed) {
|
||||
free_csd_buffers(window);
|
||||
if (!create_shm_buffers(window)) return false;
|
||||
}
|
||||
|
||||
int32_t x, y, scale = window->wl.scale < 1 ? 1 : window->wl.scale;
|
||||
x = 0; y = -decs.metrics.top;
|
||||
if (!decs.top.surface) create_csd_surfaces(window, &decs.top);
|
||||
position_csd_surface(&decs.top, x, y, scale);
|
||||
|
||||
x = -decs.metrics.width; y = -decs.metrics.top;
|
||||
if (!decs.left.surface) create_csd_surfaces(window, &decs.left);
|
||||
position_csd_surface(&decs.left, x, y, scale);
|
||||
|
||||
x = -decs.metrics.width; y = window->wl.height;
|
||||
if (!decs.bottom.surface) create_csd_surfaces(window, &decs.bottom);
|
||||
position_csd_surface(&decs.bottom, x, y, scale);
|
||||
|
||||
x = window->wl.width; y = -decs.metrics.top;
|
||||
if (!decs.right.surface) create_csd_surfaces(window, &decs.right);
|
||||
position_csd_surface(&decs.right, x, y, scale);
|
||||
|
||||
if (focus_changed) update_title_bar(window);
|
||||
damage_csd(top, decs.top.buffer.front);
|
||||
damage_csd(left, is_focused ? decs.left.buffer.front : decs.left.buffer.back);
|
||||
damage_csd(bottom, is_focused ? decs.bottom.buffer.front : decs.bottom.buffer.back);
|
||||
damage_csd(right, is_focused ? decs.right.buffer.front : decs.right.buffer.back);
|
||||
|
||||
decs.for_window_state.width = window->wl.width;
|
||||
decs.for_window_state.height = window->wl.height;
|
||||
decs.for_window_state.scale = window->wl.scale;
|
||||
decs.for_window_state.focused = is_focused;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
free_all_csd_resources(_GLFWwindow *window) {
|
||||
free_csd_surfaces(window);
|
||||
free_csd_buffers(window);
|
||||
if (decs.shadow_tile.data) free(decs.shadow_tile.data);
|
||||
decs.shadow_tile.data = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
change_csd_title(_GLFWwindow *window) {
|
||||
if (!window->decorated || window->wl.decorations.serverSide) return;
|
||||
if (ensure_csd_resources(window)) return; // CSD were re-rendered for other reasons
|
||||
if (decs.top.surface) {
|
||||
update_title_bar(window);
|
||||
damage_csd(top, decs.top.buffer.front);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_csd_window_geometry(_GLFWwindow *window, int32_t *width, int32_t *height) {
|
||||
bool has_csd = window->decorated && !window->wl.decorations.serverSide && window->wl.decorations.left.surface && !(window->wl.toplevel_states & TOPLEVEL_STATE_FULLSCREEN);
|
||||
bool size_specified_by_compositor = *width > 0 && *height > 0;
|
||||
if (!size_specified_by_compositor) {
|
||||
*width = window->wl.user_requested_content_size.width;
|
||||
*height = window->wl.user_requested_content_size.height;
|
||||
if (has_csd) *height += decs.metrics.visible_titlebar_height;
|
||||
}
|
||||
decs.geometry.x = 0; decs.geometry.y = 0;
|
||||
decs.geometry.width = *width; decs.geometry.height = *height;
|
||||
if (has_csd) {
|
||||
decs.geometry.y = -decs.metrics.visible_titlebar_height;
|
||||
*height -= decs.metrics.visible_titlebar_height;
|
||||
}
|
||||
}
|
||||
16
glfw/wl_client_side_decorations.h
vendored
Normal file
16
glfw/wl_client_side_decorations.h
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
void initialize_csd_metrics(_GLFWwindow *window);
|
||||
void free_all_csd_resources(_GLFWwindow *window);
|
||||
void free_csd_surfaces(_GLFWwindow *window);
|
||||
void change_csd_title(_GLFWwindow *window);
|
||||
bool ensure_csd_resources(_GLFWwindow *window);
|
||||
void set_csd_window_geometry(_GLFWwindow *window, int32_t *width, int32_t *height);
|
||||
61
glfw/wl_cursors.c
vendored
Normal file
61
glfw/wl_cursors.c
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// Future devs supporting whatever Wayland protocol stabilizes for cursor selection: see _themeAdd.
|
||||
|
||||
#include "internal.h"
|
||||
#include "linux_desktop_settings.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static GLFWWLCursorThemes cursor_themes;
|
||||
|
||||
static int
|
||||
pixels_from_scale(int scale) {
|
||||
int factor;
|
||||
const char* name;
|
||||
glfw_current_cursor_theme(&name, &factor);
|
||||
return factor * scale;
|
||||
}
|
||||
|
||||
|
||||
struct wl_cursor_theme*
|
||||
glfw_wlc_theme_for_scale(int scale) {
|
||||
for (size_t i = 0; i < cursor_themes.count; i++) {
|
||||
if (cursor_themes.themes[i].scale == scale) return cursor_themes.themes[i].theme;
|
||||
}
|
||||
|
||||
if (cursor_themes.count >= cursor_themes.capacity) {
|
||||
cursor_themes.themes = realloc(cursor_themes.themes, sizeof(GLFWWLCursorTheme) * (cursor_themes.count + 16));
|
||||
if (!cursor_themes.themes) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Out of memory allocating space for cursor themes");
|
||||
return NULL;
|
||||
}
|
||||
cursor_themes.capacity = cursor_themes.count + 16;
|
||||
}
|
||||
int factor;
|
||||
const char* name;
|
||||
glfw_current_cursor_theme(&name, &factor);
|
||||
struct wl_cursor_theme *ans = wl_cursor_theme_load(name, pixels_from_scale(scale), _glfw.wl.shm);
|
||||
if (!ans) {
|
||||
_glfwInputError(
|
||||
GLFW_PLATFORM_ERROR, "Wayland: wl_cursor_theme_load failed at scale: %d pixels: %d",
|
||||
scale, pixels_from_scale(scale)
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
GLFWWLCursorTheme *theme = cursor_themes.themes + cursor_themes.count++;
|
||||
theme->scale = scale;
|
||||
theme->theme = ans;
|
||||
return ans;
|
||||
}
|
||||
|
||||
void
|
||||
glfw_wlc_destroy(void) {
|
||||
for (size_t i = 0; i < cursor_themes.count; i++) {
|
||||
wl_cursor_theme_destroy(cursor_themes.themes[i].theme);
|
||||
}
|
||||
free(cursor_themes.themes);
|
||||
cursor_themes.themes = NULL; cursor_themes.capacity = 0; cursor_themes.count = 0;
|
||||
}
|
||||
18
glfw/wl_cursors.h
vendored
Normal file
18
glfw/wl_cursors.h
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
// Declarations for a HiDPI-aware cursor theme manager.
|
||||
|
||||
#include <wayland-cursor.h>
|
||||
|
||||
typedef struct {
|
||||
struct wl_cursor_theme *theme;
|
||||
int scale;
|
||||
} GLFWWLCursorTheme;
|
||||
|
||||
|
||||
typedef struct {
|
||||
GLFWWLCursorTheme *themes;
|
||||
size_t count, capacity;
|
||||
} GLFWWLCursorThemes;
|
||||
|
||||
|
||||
struct wl_cursor_theme* glfw_wlc_theme_for_scale(int scale);
|
||||
void glfw_wlc_destroy(void);
|
||||
243
glfw/wl_init.c
vendored
243
glfw/wl_init.c
vendored
@@ -29,6 +29,8 @@
|
||||
#define _GNU_SOURCE
|
||||
#include "internal.h"
|
||||
#include "backend_utils.h"
|
||||
#include "wl_client_side_decorations.h"
|
||||
#include "linux_desktop_settings.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#include <assert.h>
|
||||
@@ -58,37 +60,21 @@ static inline int min(int n1, int n2)
|
||||
return n1 < n2 ? n1 : n2;
|
||||
}
|
||||
|
||||
static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface,
|
||||
int* which)
|
||||
static _GLFWwindow*
|
||||
findWindowFromDecorationSurface(struct wl_surface* surface, _GLFWdecorationSideWayland* which)
|
||||
{
|
||||
int focus;
|
||||
_GLFWdecorationSideWayland focus;
|
||||
if (!which) which = &focus;
|
||||
_GLFWwindow* window = _glfw.windowListHead;
|
||||
if (!which)
|
||||
which = &focus;
|
||||
while (window)
|
||||
{
|
||||
if (surface == window->wl.decorations.top.surface)
|
||||
{
|
||||
*which = topDecoration;
|
||||
break;
|
||||
}
|
||||
if (surface == window->wl.decorations.left.surface)
|
||||
{
|
||||
*which = leftDecoration;
|
||||
break;
|
||||
}
|
||||
if (surface == window->wl.decorations.right.surface)
|
||||
{
|
||||
*which = rightDecoration;
|
||||
break;
|
||||
}
|
||||
if (surface == window->wl.decorations.bottom.surface)
|
||||
{
|
||||
*which = bottomDecoration;
|
||||
break;
|
||||
}
|
||||
#define q(edge, result) if (surface == window->wl.decorations.edge.surface) { *which = result; break; }
|
||||
while (window) {
|
||||
q(top, TOP_DECORATION);
|
||||
q(left, LEFT_DECORATION);
|
||||
q(right, RIGHT_DECORATION);
|
||||
q(bottom, BOTTOM_DECORATION);
|
||||
window = window->next;
|
||||
}
|
||||
#undef q
|
||||
return window;
|
||||
}
|
||||
|
||||
@@ -103,7 +89,7 @@ static void pointerHandleEnter(void* data UNUSED,
|
||||
if (!surface)
|
||||
return;
|
||||
|
||||
int focus = 0;
|
||||
_GLFWdecorationSideWayland focus = CENTRAL_WINDOW;
|
||||
_GLFWwindow* window = wl_surface_get_user_data(surface);
|
||||
if (!window)
|
||||
{
|
||||
@@ -113,7 +99,7 @@ static void pointerHandleEnter(void* data UNUSED,
|
||||
}
|
||||
|
||||
window->wl.decorations.focus = focus;
|
||||
_glfw.wl.pointerSerial = serial;
|
||||
_glfw.wl.serial = serial;
|
||||
_glfw.wl.pointerFocus = window;
|
||||
|
||||
window->wl.hovered = true;
|
||||
@@ -134,20 +120,23 @@ static void pointerHandleLeave(void* data UNUSED,
|
||||
|
||||
window->wl.hovered = false;
|
||||
|
||||
_glfw.wl.pointerSerial = serial;
|
||||
_glfw.wl.serial = serial;
|
||||
_glfw.wl.pointerFocus = NULL;
|
||||
_glfwInputCursorEnter(window, false);
|
||||
_glfw.wl.cursorPreviousShape = GLFW_INVALID_CURSOR;
|
||||
}
|
||||
|
||||
static void setCursor(GLFWCursorShape shape)
|
||||
static void setCursor(GLFWCursorShape shape, _GLFWwindow* window)
|
||||
{
|
||||
struct wl_buffer* buffer;
|
||||
struct wl_cursor* cursor;
|
||||
struct wl_cursor_image* image;
|
||||
struct wl_surface* surface = _glfw.wl.cursorSurface;
|
||||
const int scale = window->wl.scale;
|
||||
|
||||
cursor = _glfwLoadCursor(shape);
|
||||
struct wl_cursor_theme *theme = glfw_wlc_theme_for_scale(scale);
|
||||
if (!theme) return;
|
||||
cursor = _glfwLoadCursor(shape, theme);
|
||||
if (!cursor) return;
|
||||
// TODO: handle animated cursors too.
|
||||
image = cursor->images[0];
|
||||
@@ -158,10 +147,11 @@ static void setCursor(GLFWCursorShape shape)
|
||||
buffer = wl_cursor_image_get_buffer(image);
|
||||
if (!buffer)
|
||||
return;
|
||||
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial,
|
||||
wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.serial,
|
||||
surface,
|
||||
image->hotspot_x,
|
||||
image->hotspot_y);
|
||||
image->hotspot_x / scale,
|
||||
image->hotspot_y / scale);
|
||||
wl_surface_set_buffer_scale(surface, scale);
|
||||
wl_surface_attach(surface, buffer, 0, 0);
|
||||
wl_surface_damage(surface, 0, 0,
|
||||
image->width, image->height);
|
||||
@@ -169,6 +159,8 @@ static void setCursor(GLFWCursorShape shape)
|
||||
_glfw.wl.cursorPreviousShape = shape;
|
||||
}
|
||||
|
||||
#define x window->wl.allCursorPosX
|
||||
#define y window->wl.allCursorPosY
|
||||
static void pointerHandleMotion(void* data UNUSED,
|
||||
struct wl_pointer* pointer UNUSED,
|
||||
uint32_t time UNUSED,
|
||||
@@ -177,46 +169,45 @@ static void pointerHandleMotion(void* data UNUSED,
|
||||
{
|
||||
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
||||
GLFWCursorShape cursorShape = GLFW_ARROW_CURSOR;
|
||||
double x, y;
|
||||
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||
return;
|
||||
x = wl_fixed_to_double(sx);
|
||||
y = wl_fixed_to_double(sy);
|
||||
window->wl.allCursorPosX = wl_fixed_to_double(sx);
|
||||
window->wl.allCursorPosY = wl_fixed_to_double(sy);
|
||||
|
||||
switch (window->wl.decorations.focus)
|
||||
{
|
||||
case mainWindow:
|
||||
case CENTRAL_WINDOW:
|
||||
window->wl.cursorPosX = x;
|
||||
window->wl.cursorPosY = y;
|
||||
_glfwInputCursorPos(window, x, y);
|
||||
_glfw.wl.cursorPreviousShape = GLFW_INVALID_CURSOR;
|
||||
return;
|
||||
case topDecoration:
|
||||
if (y < _GLFW_DECORATION_WIDTH)
|
||||
case TOP_DECORATION:
|
||||
if (y < window->wl.decorations.metrics.width)
|
||||
cursorShape = GLFW_VRESIZE_CURSOR;
|
||||
else
|
||||
cursorShape = GLFW_ARROW_CURSOR;
|
||||
break;
|
||||
case leftDecoration:
|
||||
if (y < _GLFW_DECORATION_WIDTH)
|
||||
case LEFT_DECORATION:
|
||||
if (y < window->wl.decorations.metrics.width)
|
||||
cursorShape = GLFW_NW_RESIZE_CURSOR;
|
||||
else
|
||||
cursorShape = GLFW_HRESIZE_CURSOR;
|
||||
break;
|
||||
case rightDecoration:
|
||||
if (y < _GLFW_DECORATION_WIDTH)
|
||||
case RIGHT_DECORATION:
|
||||
if (y < window->wl.decorations.metrics.width)
|
||||
cursorShape = GLFW_NE_RESIZE_CURSOR;
|
||||
else
|
||||
cursorShape = GLFW_HRESIZE_CURSOR;
|
||||
break;
|
||||
case bottomDecoration:
|
||||
if (x < _GLFW_DECORATION_WIDTH)
|
||||
case BOTTOM_DECORATION:
|
||||
if (x < window->wl.decorations.metrics.width)
|
||||
cursorShape = GLFW_SW_RESIZE_CURSOR;
|
||||
else if (x > window->wl.width + _GLFW_DECORATION_WIDTH)
|
||||
else if (x > window->wl.width + window->wl.decorations.metrics.width)
|
||||
cursorShape = GLFW_SE_RESIZE_CURSOR;
|
||||
else
|
||||
cursorShape = GLFW_VRESIZE_CURSOR;
|
||||
@@ -225,7 +216,7 @@ static void pointerHandleMotion(void* data UNUSED,
|
||||
assert(0);
|
||||
}
|
||||
if (_glfw.wl.cursorPreviousShape != cursorShape)
|
||||
setCursor(cursorShape);
|
||||
setCursor(cursorShape, window);
|
||||
}
|
||||
|
||||
static void pointerHandleButton(void* data UNUSED,
|
||||
@@ -245,33 +236,46 @@ static void pointerHandleButton(void* data UNUSED,
|
||||
{
|
||||
switch (window->wl.decorations.focus)
|
||||
{
|
||||
case mainWindow:
|
||||
case CENTRAL_WINDOW:
|
||||
break;
|
||||
case topDecoration:
|
||||
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||
case TOP_DECORATION:
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
monotonic_t last_click_at = window->wl.decorations.last_click_on_top_decoration_at;
|
||||
window->wl.decorations.last_click_on_top_decoration_at = monotonic();
|
||||
if (window->wl.decorations.last_click_on_top_decoration_at - last_click_at <= _glfwPlatformGetDoubleClickInterval(window)) {
|
||||
window->wl.decorations.last_click_on_top_decoration_at = 0;
|
||||
if (window->wl.toplevel_states & TOPLEVEL_STATE_MAXIMIZED)
|
||||
xdg_toplevel_unset_maximized(window->wl.xdg.toplevel);
|
||||
else
|
||||
xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (y < window->wl.decorations.metrics.width)
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
|
||||
else
|
||||
{
|
||||
if (window->wl.xdg.toplevel)
|
||||
xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial);
|
||||
}
|
||||
|
||||
break;
|
||||
case leftDecoration:
|
||||
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||
case LEFT_DECORATION:
|
||||
if (y < window->wl.decorations.metrics.width)
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
|
||||
else
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
|
||||
break;
|
||||
case rightDecoration:
|
||||
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||
case RIGHT_DECORATION:
|
||||
if (y < window->wl.decorations.metrics.width)
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
|
||||
else
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
|
||||
break;
|
||||
case bottomDecoration:
|
||||
if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH)
|
||||
case BOTTOM_DECORATION:
|
||||
if (x < window->wl.decorations.metrics.width)
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
|
||||
else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH)
|
||||
else if (x > window->wl.width + window->wl.decorations.metrics.width)
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
|
||||
else
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
|
||||
@@ -287,21 +291,18 @@ static void pointerHandleButton(void* data UNUSED,
|
||||
}
|
||||
else if (button == BTN_RIGHT)
|
||||
{
|
||||
if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel)
|
||||
if (window->wl.decorations.focus != CENTRAL_WINDOW && window->wl.xdg.toplevel)
|
||||
{
|
||||
xdg_toplevel_show_window_menu(window->wl.xdg.toplevel,
|
||||
_glfw.wl.seat, serial,
|
||||
(int32_t)window->wl.cursorPosX,
|
||||
(int32_t)window->wl.cursorPosY);
|
||||
xdg_toplevel_show_window_menu(window->wl.xdg.toplevel, _glfw.wl.seat, serial, (int32_t)x, (int32_t)y - window->wl.decorations.metrics.top);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Don’t pass the button to the user if it was related to a decoration.
|
||||
if (window->wl.decorations.focus != mainWindow)
|
||||
if (window->wl.decorations.focus != CENTRAL_WINDOW)
|
||||
return;
|
||||
|
||||
_glfw.wl.pointerSerial = serial;
|
||||
_glfw.wl.serial = serial;
|
||||
|
||||
/* Makes left, right and middle 0, 1 and 2. Overall order follows evdev
|
||||
* codes. */
|
||||
@@ -314,6 +315,8 @@ static void pointerHandleButton(void* data UNUSED,
|
||||
: GLFW_RELEASE,
|
||||
_glfw.wl.xkb.states.modifiers);
|
||||
}
|
||||
#undef x
|
||||
#undef y
|
||||
|
||||
static void pointerHandleAxis(void* data UNUSED,
|
||||
struct wl_pointer* pointer UNUSED,
|
||||
@@ -330,11 +333,11 @@ static void pointerHandleAxis(void* data UNUSED,
|
||||
axis == WL_POINTER_AXIS_VERTICAL_SCROLL);
|
||||
|
||||
if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL)
|
||||
x = wl_fixed_to_double(value) * -1;
|
||||
x = -wl_fixed_to_double(value);
|
||||
else if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL)
|
||||
y = wl_fixed_to_double(value) * -1;
|
||||
y = -wl_fixed_to_double(value);
|
||||
|
||||
_glfwInputScroll(window, x, y, 1);
|
||||
_glfwInputScroll(window, x, y, 1, _glfw.wl.xkb.states.modifiers);
|
||||
}
|
||||
|
||||
static const struct wl_pointer_listener pointerListener = {
|
||||
@@ -372,7 +375,7 @@ static void keyboardHandleKeymap(void* data UNUSED,
|
||||
|
||||
static void keyboardHandleEnter(void* data UNUSED,
|
||||
struct wl_keyboard* keyboard UNUSED,
|
||||
uint32_t serial UNUSED,
|
||||
uint32_t serial,
|
||||
struct wl_surface* surface,
|
||||
struct wl_array* keys)
|
||||
{
|
||||
@@ -388,7 +391,8 @@ static void keyboardHandleEnter(void* data UNUSED,
|
||||
return;
|
||||
}
|
||||
|
||||
_glfw.wl.keyboardFocus = window;
|
||||
_glfw.wl.serial = serial;
|
||||
_glfw.wl.keyboardFocusId = window->id;
|
||||
_glfwInputWindowFocus(window, true);
|
||||
uint32_t* key;
|
||||
if (keys && _glfw.wl.keyRepeatInfo.key) {
|
||||
@@ -403,23 +407,26 @@ static void keyboardHandleEnter(void* data UNUSED,
|
||||
|
||||
static void keyboardHandleLeave(void* data UNUSED,
|
||||
struct wl_keyboard* keyboard UNUSED,
|
||||
uint32_t serial UNUSED,
|
||||
uint32_t serial,
|
||||
struct wl_surface* surface UNUSED)
|
||||
{
|
||||
_GLFWwindow* window = _glfw.wl.keyboardFocus;
|
||||
_GLFWwindow* window = _glfwWindowForId(_glfw.wl.keyboardFocusId);
|
||||
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
_glfw.wl.keyboardFocus = NULL;
|
||||
_glfw.wl.serial = serial;
|
||||
_glfw.wl.keyboardFocusId = 0;
|
||||
_glfwInputWindowFocus(window, false);
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
dispatchPendingKeyRepeats(id_type timer_id UNUSED, void *data UNUSED) {
|
||||
if (_glfw.wl.keyRepeatInfo.keyboardFocus != _glfw.wl.keyboardFocus || _glfw.wl.keyboardRepeatRate == 0) return;
|
||||
glfw_xkb_handle_key_event(_glfw.wl.keyRepeatInfo.keyboardFocus, &_glfw.wl.xkb, _glfw.wl.keyRepeatInfo.key, GLFW_REPEAT);
|
||||
if (_glfw.wl.keyRepeatInfo.keyboardFocusId != _glfw.wl.keyboardFocusId || _glfw.wl.keyboardRepeatRate == 0) return;
|
||||
_GLFWwindow* window = _glfwWindowForId(_glfw.wl.keyboardFocusId);
|
||||
if (!window) return;
|
||||
glfw_xkb_handle_key_event(window, &_glfw.wl.xkb, _glfw.wl.keyRepeatInfo.key, GLFW_REPEAT);
|
||||
changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, (s_to_monotonic_t(1ll) / (monotonic_t)_glfw.wl.keyboardRepeatRate));
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 1);
|
||||
}
|
||||
@@ -427,39 +434,40 @@ dispatchPendingKeyRepeats(id_type timer_id UNUSED, void *data UNUSED) {
|
||||
|
||||
static void keyboardHandleKey(void* data UNUSED,
|
||||
struct wl_keyboard* keyboard UNUSED,
|
||||
uint32_t serial UNUSED,
|
||||
uint32_t serial,
|
||||
uint32_t time UNUSED,
|
||||
uint32_t key,
|
||||
uint32_t state)
|
||||
{
|
||||
_GLFWwindow* window = _glfw.wl.keyboardFocus;
|
||||
_GLFWwindow* window = _glfwWindowForId(_glfw.wl.keyboardFocusId);
|
||||
if (!window)
|
||||
return;
|
||||
int action = state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE;
|
||||
|
||||
_glfw.wl.serial = serial;
|
||||
glfw_xkb_handle_key_event(window, &_glfw.wl.xkb, key, action);
|
||||
bool repeatable = false;
|
||||
_glfw.wl.keyRepeatInfo.key = 0;
|
||||
|
||||
if (action == GLFW_PRESS && _glfw.wl.keyboardRepeatRate > 0 && glfw_xkb_should_repeat(&_glfw.wl.xkb, key))
|
||||
{
|
||||
_glfw.wl.keyRepeatInfo.key = key;
|
||||
repeatable = true;
|
||||
_glfw.wl.keyRepeatInfo.keyboardFocus = window;
|
||||
}
|
||||
if (repeatable) {
|
||||
_glfw.wl.keyRepeatInfo.keyboardFocusId = window->id;
|
||||
changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, _glfw.wl.keyboardRepeatDelay);
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 1);
|
||||
} else if (action == GLFW_RELEASE && key == _glfw.wl.keyRepeatInfo.key) {
|
||||
_glfw.wl.keyRepeatInfo.key = 0;
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 0);
|
||||
}
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, repeatable ? 1 : 0);
|
||||
}
|
||||
|
||||
static void keyboardHandleModifiers(void* data UNUSED,
|
||||
struct wl_keyboard* keyboard UNUSED,
|
||||
uint32_t serial UNUSED,
|
||||
uint32_t serial,
|
||||
uint32_t modsDepressed,
|
||||
uint32_t modsLatched,
|
||||
uint32_t modsLocked,
|
||||
uint32_t group)
|
||||
{
|
||||
_glfw.wl.serial = serial;
|
||||
glfw_xkb_update_modifiers(&_glfw.wl.xkb, modsDepressed, modsLatched, modsLocked, 0, 0, group);
|
||||
}
|
||||
|
||||
@@ -575,6 +583,7 @@ static void registryHandleGlobal(void* data UNUSED,
|
||||
if (_glfw.wl.primarySelectionDeviceManager && !_glfw.wl.primarySelectionDevice) {
|
||||
_glfwSetupWaylandPrimarySelectionDevice();
|
||||
}
|
||||
_glfwWaylandInitTextInput();
|
||||
}
|
||||
}
|
||||
else if (strcmp(interface, "xdg_wm_base") == 0)
|
||||
@@ -589,11 +598,6 @@ static void registryHandleGlobal(void* data UNUSED,
|
||||
wl_registry_bind(registry, name,
|
||||
&zxdg_decoration_manager_v1_interface, 1);
|
||||
}
|
||||
else if (strcmp(interface, "wp_viewporter") == 0)
|
||||
{
|
||||
_glfw.wl.viewporter =
|
||||
wl_registry_bind(registry, name, &wp_viewporter_interface, 1);
|
||||
}
|
||||
else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0)
|
||||
{
|
||||
_glfw.wl.relativePointerManager =
|
||||
@@ -608,6 +612,11 @@ static void registryHandleGlobal(void* data UNUSED,
|
||||
&zwp_pointer_constraints_v1_interface,
|
||||
1);
|
||||
}
|
||||
else if (strcmp(interface, GLFW_WAYLAND_TEXT_INPUT_INTERFACE_NAME) == 0)
|
||||
{
|
||||
_glfwWaylandBindTextInput(registry, name);
|
||||
_glfwWaylandInitTextInput();
|
||||
}
|
||||
else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0)
|
||||
{
|
||||
_glfw.wl.idleInhibitManager =
|
||||
@@ -708,6 +717,9 @@ glfwWaylandCheckForServerSideDecorations(void) {
|
||||
|
||||
int _glfwPlatformInit(void)
|
||||
{
|
||||
int i;
|
||||
_GLFWmonitor* monitor;
|
||||
|
||||
_glfw.wl.cursor.handle = _glfw_dlopen("libwayland-cursor.so.0");
|
||||
if (!_glfw.wl.cursor.handle)
|
||||
{
|
||||
@@ -745,6 +757,7 @@ int _glfwPlatformInit(void)
|
||||
"Wayland: Failed to initialize event loop data");
|
||||
}
|
||||
glfw_dbus_init(&_glfw.wl.dbus, &_glfw.wl.eventLoopData);
|
||||
glfw_initialize_desktop_settings();
|
||||
_glfw.wl.keyRepeatInfo.keyRepeatTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-key-repeat", ms_to_monotonic_t(500ll), 0, true, dispatchPendingKeyRepeats, NULL, NULL);
|
||||
_glfw.wl.cursorAnimationTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-cursor-animation", ms_to_monotonic_t(500ll), 0, true, animateCursorImage, NULL, NULL);
|
||||
|
||||
@@ -759,12 +772,16 @@ int _glfwPlatformInit(void)
|
||||
// Sync so we got all initial output events
|
||||
wl_display_roundtrip(_glfw.wl.display);
|
||||
|
||||
#ifdef __linux__
|
||||
if (_glfw.hints.init.enableJoysticks) {
|
||||
if (!_glfwInitJoysticksLinux())
|
||||
return false;
|
||||
for (i = 0; i < _glfw.monitorCount; ++i)
|
||||
{
|
||||
monitor = _glfw.monitors[i];
|
||||
if (monitor->widthMM <= 0 || monitor->heightMM <= 0)
|
||||
{
|
||||
// If Wayland does not provide a physical size, assume the default 96 DPI
|
||||
monitor->widthMM = (int) (monitor->modes[monitor->wl.currentMode].width * 25.4f / 96.f);
|
||||
monitor->heightMM = (int) (monitor->modes[monitor->wl.currentMode].height * 25.4f / 96.f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!_glfw.wl.wmBase)
|
||||
{
|
||||
@@ -775,35 +792,21 @@ int _glfwPlatformInit(void)
|
||||
|
||||
if (_glfw.wl.shm)
|
||||
{
|
||||
const char *cursorTheme = getenv("XCURSOR_THEME"), *cursorSizeStr = getenv("XCURSOR_SIZE");
|
||||
char *cursorSizeEnd;
|
||||
int cursorSize = 32;
|
||||
if (cursorSizeStr)
|
||||
{
|
||||
errno = 0;
|
||||
long cursorSizeLong = strtol(cursorSizeStr, &cursorSizeEnd, 10);
|
||||
if (!*cursorSizeEnd && !errno && cursorSizeLong > 0 && cursorSizeLong <= INT_MAX)
|
||||
cursorSize = (int)cursorSizeLong;
|
||||
}
|
||||
_glfw.wl.cursorTheme = wl_cursor_theme_load(cursorTheme, cursorSize, _glfw.wl.shm);
|
||||
if (!_glfw.wl.cursorTheme)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Unable to load default cursor theme");
|
||||
return false;
|
||||
}
|
||||
_glfw.wl.cursorSurface =
|
||||
wl_compositor_create_surface(_glfw.wl.compositor);
|
||||
}
|
||||
else
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Failed to find Wayland SHM");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _glfwPlatformTerminate(void)
|
||||
{
|
||||
#ifdef __linux__
|
||||
_glfwTerminateJoysticksLinux();
|
||||
#endif
|
||||
_glfwTerminateEGL();
|
||||
if (_glfw.wl.egl.handle)
|
||||
{
|
||||
@@ -814,8 +817,7 @@ void _glfwPlatformTerminate(void)
|
||||
glfw_xkb_release(&_glfw.wl.xkb);
|
||||
glfw_dbus_terminate(&_glfw.wl.dbus);
|
||||
|
||||
if (_glfw.wl.cursorTheme)
|
||||
wl_cursor_theme_destroy(_glfw.wl.cursorTheme);
|
||||
glfw_wlc_destroy();
|
||||
if (_glfw.wl.cursor.handle)
|
||||
{
|
||||
_glfw_dlclose(_glfw.wl.cursor.handle);
|
||||
@@ -830,8 +832,6 @@ void _glfwPlatformTerminate(void)
|
||||
wl_compositor_destroy(_glfw.wl.compositor);
|
||||
if (_glfw.wl.shm)
|
||||
wl_shm_destroy(_glfw.wl.shm);
|
||||
if (_glfw.wl.viewporter)
|
||||
wp_viewporter_destroy(_glfw.wl.viewporter);
|
||||
if (_glfw.wl.decorationManager)
|
||||
zxdg_decoration_manager_v1_destroy(_glfw.wl.decorationManager);
|
||||
if (_glfw.wl.wmBase)
|
||||
@@ -846,13 +846,14 @@ void _glfwPlatformTerminate(void)
|
||||
zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager);
|
||||
if (_glfw.wl.pointerConstraints)
|
||||
zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints);
|
||||
_glfwWaylandDestroyTextInput();
|
||||
if (_glfw.wl.idleInhibitManager)
|
||||
zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager);
|
||||
if (_glfw.wl.dataSourceForClipboard)
|
||||
wl_data_source_destroy(_glfw.wl.dataSourceForClipboard);
|
||||
for (size_t doi=0; doi < arraysz(_glfw.wl.dataOffers); doi++) {
|
||||
if (_glfw.wl.dataOffers[doi].id) {
|
||||
wl_data_offer_destroy(_glfw.wl.dataOffers[doi].id);
|
||||
destroy_data_offer(&_glfw.wl.dataOffers[doi]);
|
||||
}
|
||||
}
|
||||
if (_glfw.wl.dataDevice)
|
||||
|
||||
4
glfw/wl_monitor.c
vendored
4
glfw/wl_monitor.c
vendored
@@ -202,7 +202,7 @@ void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode)
|
||||
|
||||
bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor UNUSED, GLFWgammaramp* ramp UNUSED)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||
"Wayland: Gamma ramp access is not available");
|
||||
return false;
|
||||
}
|
||||
@@ -210,7 +210,7 @@ bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor UNUSED, GLFWgammaramp* ramp
|
||||
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor UNUSED,
|
||||
const GLFWgammaramp* ramp UNUSED)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||
"Wayland: Gamma ramp access is not available");
|
||||
}
|
||||
|
||||
|
||||
158
glfw/wl_platform.h
vendored
158
glfw/wl_platform.h
vendored
@@ -50,24 +50,20 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
|
||||
#endif
|
||||
#include "backend_utils.h"
|
||||
#include "xkb_glfw.h"
|
||||
#include "egl_context.h"
|
||||
#include "osmesa_context.h"
|
||||
#include "wl_cursors.h"
|
||||
|
||||
#include "wayland-xdg-shell-client-protocol.h"
|
||||
#include "wayland-viewporter-client-protocol.h"
|
||||
#include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
||||
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
|
||||
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
|
||||
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
|
||||
#include "wayland-primary-selection-unstable-v1-client-protocol.h"
|
||||
#include "wl_text_input.h"
|
||||
|
||||
#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
|
||||
#define _glfw_dlclose(handle) dlclose(handle)
|
||||
#define _glfw_dlsym(handle, name) dlsym(handle, name)
|
||||
|
||||
#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->wl.native)
|
||||
#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.wl.display)
|
||||
|
||||
#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWayland wl
|
||||
#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWayland wl
|
||||
#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWayland wl
|
||||
@@ -76,18 +72,6 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
|
||||
#define _GLFW_PLATFORM_CONTEXT_STATE
|
||||
#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE
|
||||
|
||||
struct wl_cursor_image {
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t hotspot_x;
|
||||
uint32_t hotspot_y;
|
||||
uint32_t delay;
|
||||
};
|
||||
struct wl_cursor {
|
||||
unsigned int image_count;
|
||||
struct wl_cursor_image** images;
|
||||
char* name;
|
||||
};
|
||||
typedef struct wl_cursor_theme* (* PFN_wl_cursor_theme_load)(const char*, int, struct wl_shm*);
|
||||
typedef void (* PFN_wl_cursor_theme_destroy)(struct wl_cursor_theme*);
|
||||
typedef struct wl_cursor* (* PFN_wl_cursor_theme_get_cursor)(struct wl_cursor_theme*, const char*);
|
||||
@@ -104,28 +88,45 @@ typedef void (* PFN_wl_egl_window_resize)(struct wl_egl_window*, int, int, int,
|
||||
#define wl_egl_window_destroy _glfw.wl.egl.window_destroy
|
||||
#define wl_egl_window_resize _glfw.wl.egl.window_resize
|
||||
|
||||
#define _GLFW_DECORATION_WIDTH 4
|
||||
#define _GLFW_DECORATION_TOP 24
|
||||
#define _GLFW_DECORATION_VERTICAL (_GLFW_DECORATION_TOP + _GLFW_DECORATION_WIDTH)
|
||||
#define _GLFW_DECORATION_HORIZONTAL (2 * _GLFW_DECORATION_WIDTH)
|
||||
|
||||
typedef enum _GLFWdecorationSideWayland
|
||||
{
|
||||
mainWindow,
|
||||
topDecoration,
|
||||
leftDecoration,
|
||||
rightDecoration,
|
||||
bottomDecoration,
|
||||
|
||||
CENTRAL_WINDOW,
|
||||
TOP_DECORATION,
|
||||
LEFT_DECORATION,
|
||||
RIGHT_DECORATION,
|
||||
BOTTOM_DECORATION,
|
||||
} _GLFWdecorationSideWayland;
|
||||
|
||||
typedef struct _GLFWdecorationWayland
|
||||
{
|
||||
struct wl_surface* surface;
|
||||
struct wl_subsurface* subsurface;
|
||||
struct wp_viewport* viewport;
|
||||
typedef struct _GLFWWaylandBufferPair {
|
||||
struct wl_buffer *a, *b, *front, *back;
|
||||
struct { uint8_t *a, *b, *front, *back; } data;
|
||||
bool has_pending_update;
|
||||
size_t size_in_bytes, width, height, stride;
|
||||
} _GLFWWaylandBufferPair;
|
||||
|
||||
typedef struct _GLFWWaylandCSDEdge {
|
||||
struct wl_surface *surface;
|
||||
struct wl_subsurface *subsurface;
|
||||
_GLFWWaylandBufferPair buffer;
|
||||
int x, y;
|
||||
} _GLFWWaylandCSDEdge;
|
||||
|
||||
typedef enum WaylandWindowState {
|
||||
|
||||
TOPLEVEL_STATE_NONE = 0,
|
||||
TOPLEVEL_STATE_MAXIMIZED = 1,
|
||||
TOPLEVEL_STATE_FULLSCREEN = 2,
|
||||
TOPLEVEL_STATE_RESIZING = 4,
|
||||
TOPLEVEL_STATE_ACTIVATED = 8,
|
||||
TOPLEVEL_STATE_TILED_LEFT = 16,
|
||||
TOPLEVEL_STATE_TILED_RIGHT = 32,
|
||||
TOPLEVEL_STATE_TILED_TOP = 64,
|
||||
TOPLEVEL_STATE_TILED_BOTTOM = 128,
|
||||
} WaylandWindowState;
|
||||
|
||||
|
||||
static const WaylandWindowState TOPLEVEL_STATE_DOCKED = TOPLEVEL_STATE_MAXIMIZED | TOPLEVEL_STATE_FULLSCREEN | TOPLEVEL_STATE_TILED_TOP | TOPLEVEL_STATE_TILED_LEFT | TOPLEVEL_STATE_TILED_RIGHT | TOPLEVEL_STATE_TILED_BOTTOM;
|
||||
|
||||
} _GLFWdecorationWayland;
|
||||
|
||||
// Wayland-specific per-window data
|
||||
//
|
||||
@@ -133,7 +134,6 @@ typedef struct _GLFWwindowWayland
|
||||
{
|
||||
int width, height;
|
||||
bool visible;
|
||||
bool maximized;
|
||||
bool hovered;
|
||||
bool transparent;
|
||||
struct wl_surface* surface;
|
||||
@@ -147,7 +147,7 @@ typedef struct _GLFWwindowWayland
|
||||
} xdg;
|
||||
|
||||
_GLFWcursor* currentCursor;
|
||||
double cursorPosX, cursorPosY;
|
||||
double cursorPosX, cursorPosY, allCursorPosX, allCursorPosY;
|
||||
|
||||
char* title;
|
||||
char appId[256];
|
||||
@@ -167,13 +167,34 @@ typedef struct _GLFWwindowWayland
|
||||
|
||||
struct zwp_idle_inhibitor_v1* idleInhibitor;
|
||||
|
||||
bool fullscreened;
|
||||
|
||||
struct {
|
||||
bool serverSide;
|
||||
struct wl_buffer* buffer;
|
||||
_GLFWdecorationWayland top, left, right, bottom;
|
||||
int focus;
|
||||
bool serverSide;
|
||||
_GLFWdecorationSideWayland focus;
|
||||
_GLFWWaylandCSDEdge top, left, right, bottom;
|
||||
|
||||
struct {
|
||||
uint8_t *data;
|
||||
size_t size;
|
||||
} mapping;
|
||||
|
||||
struct {
|
||||
int width, height, scale;
|
||||
bool focused;
|
||||
} for_window_state;
|
||||
|
||||
struct {
|
||||
unsigned int width, top, horizontal, vertical, visible_titlebar_height;
|
||||
} metrics;
|
||||
|
||||
struct {
|
||||
int32_t x, y, width, height;
|
||||
} geometry;
|
||||
|
||||
struct {
|
||||
uint32_t *data;
|
||||
size_t for_decoration_size, stride, segments, corner_size;
|
||||
} shadow_tile;
|
||||
monotonic_t last_click_on_top_decoration_at;
|
||||
} decorations;
|
||||
|
||||
struct {
|
||||
@@ -182,6 +203,12 @@ typedef struct _GLFWwindowWayland
|
||||
struct wl_callback *current_wl_callback;
|
||||
} frameCallbackData;
|
||||
|
||||
struct {
|
||||
int32_t width, height;
|
||||
} user_requested_content_size;
|
||||
|
||||
uint32_t toplevel_states;
|
||||
bool maximize_on_first_show;
|
||||
|
||||
} _GLFWwindowWayland;
|
||||
|
||||
@@ -195,28 +222,19 @@ typedef enum _GLFWWaylandOfferType
|
||||
|
||||
typedef struct _GLFWWaylandDataOffer
|
||||
{
|
||||
struct wl_data_offer *id;
|
||||
const char *mime;
|
||||
void *id;
|
||||
_GLFWWaylandOfferType offer_type;
|
||||
size_t idx;
|
||||
int is_self_offer;
|
||||
int has_uri_list;
|
||||
bool is_self_offer;
|
||||
bool is_primary;
|
||||
const char *plain_text_mime, *mime_for_drop;
|
||||
uint32_t source_actions;
|
||||
uint32_t dnd_action;
|
||||
struct wl_surface *surface;
|
||||
const char **mimes;
|
||||
size_t mimes_capacity, mimes_count;
|
||||
} _GLFWWaylandDataOffer;
|
||||
|
||||
typedef struct _GLFWWaylandPrimaryOffer
|
||||
{
|
||||
struct zwp_primary_selection_offer_v1 *id;
|
||||
const char *mime;
|
||||
_GLFWWaylandOfferType offer_type;
|
||||
size_t idx;
|
||||
int is_self_offer;
|
||||
int has_uri_list;
|
||||
struct wl_surface *surface;
|
||||
} _GLFWWaylandPrimaryOffer;
|
||||
|
||||
// Wayland-specific global data
|
||||
//
|
||||
typedef struct _GLFWlibraryWayland
|
||||
@@ -229,14 +247,13 @@ typedef struct _GLFWlibraryWayland
|
||||
struct wl_seat* seat;
|
||||
struct wl_pointer* pointer;
|
||||
struct wl_keyboard* keyboard;
|
||||
struct wl_data_device_manager* dataDeviceManager;
|
||||
struct wl_data_device* dataDevice;
|
||||
struct xdg_wm_base* wmBase;
|
||||
struct zxdg_decoration_manager_v1* decorationManager;
|
||||
struct wp_viewporter* viewporter;
|
||||
struct zwp_relative_pointer_manager_v1* relativePointerManager;
|
||||
struct zwp_pointer_constraints_v1* pointerConstraints;
|
||||
struct zwp_idle_inhibit_manager_v1* idleInhibitManager;
|
||||
struct wl_data_device_manager* dataDeviceManager;
|
||||
struct wl_data_device* dataDevice;
|
||||
struct wl_data_source* dataSourceForClipboard;
|
||||
struct zwp_primary_selection_device_manager_v1* primarySelectionDeviceManager;
|
||||
struct zwp_primary_selection_device_v1* primarySelectionDevice;
|
||||
@@ -245,24 +262,24 @@ typedef struct _GLFWlibraryWayland
|
||||
int compositorVersion;
|
||||
int seatVersion;
|
||||
|
||||
struct wl_cursor_theme* cursorTheme;
|
||||
struct wl_surface* cursorSurface;
|
||||
GLFWCursorShape cursorPreviousShape;
|
||||
uint32_t pointerSerial;
|
||||
uint32_t serial;
|
||||
|
||||
int32_t keyboardRepeatRate;
|
||||
monotonic_t keyboardRepeatDelay;
|
||||
|
||||
struct {
|
||||
uint32_t key;
|
||||
id_type keyRepeatTimer;
|
||||
_GLFWwindow* keyboardFocus;
|
||||
GLFWid keyboardFocusId;
|
||||
} keyRepeatInfo;
|
||||
id_type cursorAnimationTimer;
|
||||
_GLFWXKBData xkb;
|
||||
_GLFWDBUSData dbus;
|
||||
|
||||
_GLFWwindow* pointerFocus;
|
||||
_GLFWwindow* keyboardFocus;
|
||||
GLFWid keyboardFocusId;
|
||||
|
||||
struct {
|
||||
void* handle;
|
||||
@@ -287,8 +304,6 @@ typedef struct _GLFWlibraryWayland
|
||||
size_t dataOffersCounter;
|
||||
_GLFWWaylandDataOffer dataOffers[8];
|
||||
char* primarySelectionString;
|
||||
size_t primarySelectionOffersCounter;
|
||||
_GLFWWaylandPrimaryOffer primarySelectionOffers[8];
|
||||
} _GLFWlibraryWayland;
|
||||
|
||||
// Wayland-specific per-monitor data
|
||||
@@ -313,7 +328,11 @@ typedef struct _GLFWcursorWayland
|
||||
struct wl_buffer* buffer;
|
||||
int width, height;
|
||||
int xhot, yhot;
|
||||
int currentImage;
|
||||
unsigned int currentImage;
|
||||
/** The scale of the cursor, or 0 if the cursor should be loaded late, or -1 if the cursor variable itself is unused. */
|
||||
int scale;
|
||||
/** Cursor shape stored to allow late cursor loading in setCursorImage. */
|
||||
GLFWCursorShape shape;
|
||||
} _GLFWcursorWayland;
|
||||
|
||||
|
||||
@@ -321,4 +340,5 @@ void _glfwAddOutputWayland(uint32_t name, uint32_t version);
|
||||
void _glfwSetupWaylandDataDevice(void);
|
||||
void _glfwSetupWaylandPrimarySelectionDevice(void);
|
||||
void animateCursorImage(id_type timer_id, void *data);
|
||||
struct wl_cursor* _glfwLoadCursor(GLFWCursorShape);
|
||||
struct wl_cursor* _glfwLoadCursor(GLFWCursorShape, struct wl_cursor_theme*);
|
||||
void destroy_data_offer(_GLFWWaylandDataOffer*);
|
||||
|
||||
154
glfw/wl_text_input.c
vendored
Normal file
154
glfw/wl_text_input.c
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* wl_text_input.c
|
||||
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include "wl_text_input.h"
|
||||
#include "internal.h"
|
||||
#include "wayland-text-input-unstable-v3-client-protocol.h"
|
||||
#include <stdlib.h>
|
||||
#define debug(...) if (_glfw.hints.init.debugKeyboard) printf(__VA_ARGS__);
|
||||
|
||||
static struct zwp_text_input_v3* text_input;
|
||||
static struct zwp_text_input_manager_v3* text_input_manager;
|
||||
static char *pending_pre_edit = NULL;
|
||||
static char *pending_commit = NULL;
|
||||
uint32_t commit_serial = 0;
|
||||
|
||||
static void commit(void) {
|
||||
if (text_input) {
|
||||
zwp_text_input_v3_commit (text_input);
|
||||
commit_serial++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_enter(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, struct wl_surface *surface UNUSED) {
|
||||
debug("text-input: enter event\n");
|
||||
if (text_input) {
|
||||
zwp_text_input_v3_enable(text_input);
|
||||
zwp_text_input_v3_set_content_type(text_input, ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL);
|
||||
commit();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_leave(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, struct wl_surface *surface UNUSED) {
|
||||
debug("text-input: leave event\n");
|
||||
if (text_input) {
|
||||
zwp_text_input_v3_disable(text_input);
|
||||
commit();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
send_text(const char *text, GLFWIMEState ime_state) {
|
||||
_GLFWwindow *w = _glfwFocusedWindow();
|
||||
if (w && w->callbacks.keyboard) {
|
||||
GLFWkeyevent fake_ev = {.action = GLFW_PRESS};
|
||||
fake_ev.text = text;
|
||||
fake_ev.ime_state = ime_state;
|
||||
w->callbacks.keyboard((GLFWwindow*) w, &fake_ev);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_preedit_string(
|
||||
void *data UNUSED,
|
||||
struct zwp_text_input_v3 *text_input UNUSED,
|
||||
const char *text,
|
||||
int32_t cursor_begin,
|
||||
int32_t cursor_end
|
||||
) {
|
||||
debug("text-input: preedit_string event: text: %s cursor_begin: %d cursor_end: %d\n", text, cursor_begin, cursor_end);
|
||||
free(pending_pre_edit);
|
||||
pending_pre_edit = text ? _glfw_strdup(text) : NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_commit_string(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, const char *text) {
|
||||
debug("text-input: commit_string event: text: %s\n", text);
|
||||
free(pending_commit);
|
||||
pending_commit = text ? _glfw_strdup(text) : NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_delete_surrounding_text(
|
||||
void *data UNUSED,
|
||||
struct zwp_text_input_v3 *zwp_text_input_v3 UNUSED,
|
||||
uint32_t before_length,
|
||||
uint32_t after_length) {
|
||||
debug("text-input: delete_surrounding_text event: before_length: %u after_length: %u\n", before_length, after_length);
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_done(void *data UNUSED, struct zwp_text_input_v3 *zwp_text_input_v3 UNUSED, uint32_t serial UNUSED) {
|
||||
debug("text-input: done event: serial: %u current_commit_serial: %u\n", serial, commit_serial);
|
||||
if (serial != commit_serial) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: text_input_done serial mismatch, expected=%u got=%u\n", commit_serial, serial);
|
||||
return;
|
||||
}
|
||||
if (pending_pre_edit) {
|
||||
send_text(pending_pre_edit, GLFW_IME_PREEDIT_CHANGED);
|
||||
free(pending_pre_edit); pending_pre_edit = NULL;
|
||||
}
|
||||
if (pending_commit) {
|
||||
send_text(pending_commit, GLFW_IME_COMMIT_TEXT);
|
||||
free(pending_commit); pending_commit = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_glfwWaylandBindTextInput(struct wl_registry* registry, uint32_t name) {
|
||||
if (!text_input_manager) text_input_manager = wl_registry_bind(registry, name, &zwp_text_input_manager_v3_interface, 1);
|
||||
}
|
||||
|
||||
void
|
||||
_glfwWaylandInitTextInput(void) {
|
||||
static const struct zwp_text_input_v3_listener text_input_listener = {
|
||||
.enter = text_input_enter,
|
||||
.leave = text_input_leave,
|
||||
.preedit_string = text_input_preedit_string,
|
||||
.commit_string = text_input_commit_string,
|
||||
.delete_surrounding_text = text_input_delete_surrounding_text,
|
||||
.done = text_input_done,
|
||||
};
|
||||
if (!text_input) {
|
||||
if (text_input_manager && _glfw.wl.seat) {
|
||||
text_input = zwp_text_input_manager_v3_get_text_input(
|
||||
text_input_manager, _glfw.wl.seat);
|
||||
if (text_input) zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_glfwWaylandDestroyTextInput(void) {
|
||||
if (text_input) zwp_text_input_v3_destroy(text_input);
|
||||
if (text_input_manager) zwp_text_input_manager_v3_destroy(text_input_manager);
|
||||
text_input = NULL; text_input_manager = NULL;
|
||||
free(pending_pre_edit); pending_pre_edit = NULL;
|
||||
free(pending_commit); pending_commit = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
_glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
|
||||
if (!text_input) return;
|
||||
switch(ev->type) {
|
||||
case GLFW_IME_UPDATE_FOCUS:
|
||||
debug("\ntext-input: updating IME focus state, focused: %d\n", ev->focused);
|
||||
if (ev->focused) zwp_text_input_v3_enable(text_input); else zwp_text_input_v3_disable(text_input);
|
||||
commit();
|
||||
break;
|
||||
case GLFW_IME_UPDATE_CURSOR_POSITION: {
|
||||
const int scale = w->wl.scale;
|
||||
const int left = ev->cursor.left / scale, top = ev->cursor.top / scale, width = ev->cursor.width / scale, height = ev->cursor.height / scale;
|
||||
debug("\ntext-input: updating cursor position: left=%d top=%d width=%d height=%d\n", left, top, width, height);
|
||||
zwp_text_input_v3_set_cursor_rectangle(text_input, left, top, width, height);
|
||||
commit();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
14
glfw/wl_text_input.h
vendored
Normal file
14
glfw/wl_text_input.h
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <wayland-client.h>
|
||||
|
||||
#define GLFW_WAYLAND_TEXT_INPUT_INTERFACE_NAME "zwp_text_input_manager_v3"
|
||||
|
||||
void _glfwWaylandBindTextInput(struct wl_registry* registry, uint32_t name);
|
||||
void _glfwWaylandInitTextInput(void);
|
||||
void _glfwWaylandDestroyTextInput(void);
|
||||
987
glfw/wl_window.c
vendored
987
glfw/wl_window.c
vendored
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user