Compare commits
2644 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57b5e493a4 | ||
|
|
fc184984a0 | ||
|
|
2178ff1c48 | ||
|
|
ae1df38c88 | ||
|
|
162e498347 | ||
|
|
172023afca | ||
|
|
18c7ea50b4 | ||
|
|
bdddb238f8 | ||
|
|
8d46237935 | ||
|
|
3fcf83e685 | ||
|
|
d374af4341 | ||
|
|
f32ad617a2 | ||
|
|
62656b24eb | ||
|
|
12763e19d8 | ||
|
|
89fd726e07 | ||
|
|
0b428987b1 | ||
|
|
1e29fad5f0 | ||
|
|
19524a4459 | ||
|
|
7043d565c9 | ||
|
|
d6f856f2f2 | ||
|
|
24f0451c10 | ||
|
|
3c4460ca98 | ||
|
|
cbf33fa14b | ||
|
|
d782654819 | ||
|
|
3c39cbf333 | ||
|
|
bd746e5151 | ||
|
|
b32c346eed | ||
|
|
77b8e204ad | ||
|
|
93dfe19c35 | ||
|
|
6dc8df5178 | ||
|
|
237fd73702 | ||
|
|
5f2d0142d1 | ||
|
|
572d576d5b | ||
|
|
6606f51636 | ||
|
|
36da65120a | ||
|
|
b6c1e1a609 | ||
|
|
5d003ec772 | ||
|
|
b9210a2ba4 | ||
|
|
f3c559ea13 | ||
|
|
6cee6b6429 | ||
|
|
84f6aabf5b | ||
|
|
9ce947d6ea | ||
|
|
0f23ad0d7e | ||
|
|
1b760b6c53 | ||
|
|
cdf64bf016 | ||
|
|
f3726023c2 | ||
|
|
cb41683f47 | ||
|
|
629a8ad055 | ||
|
|
4ba0fa00b4 | ||
|
|
f1e73c015a | ||
|
|
926c3540ff | ||
|
|
1aebd83e45 | ||
|
|
8225351145 | ||
|
|
379add8d6f | ||
|
|
ea11ce8664 | ||
|
|
82e9e96f0c | ||
|
|
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 | ||
|
|
644ee646a2 | ||
|
|
8f1ec053ef | ||
|
|
2e3037ce3a | ||
|
|
109a856b91 | ||
|
|
eaec854bfb | ||
|
|
2c6cc6f047 | ||
|
|
f32ae10339 | ||
|
|
c46317f96f | ||
|
|
a67e4e550e | ||
|
|
65a7b19f40 | ||
|
|
1b5daf930a | ||
|
|
748ca81d4b | ||
|
|
0ea7d24ba4 | ||
|
|
6c9861cb76 | ||
|
|
a1125b5cc8 | ||
|
|
b4e189d2bf | ||
|
|
dbc763fca0 | ||
|
|
4c03ffeae2 | ||
|
|
2719ac7d01 | ||
|
|
219b81b5ee | ||
|
|
8e1443360f | ||
|
|
d55242aa82 | ||
|
|
ba531969ec | ||
|
|
c12e7587cb | ||
|
|
3aac0860a2 | ||
|
|
e4fd12001c | ||
|
|
3581ffe04b | ||
|
|
f6901e4a3d | ||
|
|
5a2cc4578e | ||
|
|
6b5acc4d3d | ||
|
|
43326c9bd0 | ||
|
|
96f3253e6d | ||
|
|
f5090c951a | ||
|
|
edacdc5e25 | ||
|
|
c67661af61 | ||
|
|
5e60329adf | ||
|
|
6b4f8408de | ||
|
|
ea1ef709c6 | ||
|
|
bbf247c5c0 | ||
|
|
8243fd505f | ||
|
|
ed9f9db1b0 | ||
|
|
74bed658e1 | ||
|
|
2f7bdc274d | ||
|
|
1347ff27d9 | ||
|
|
5bf4d520f5 | ||
|
|
cf8d665eb7 | ||
|
|
b2eee6df5c | ||
|
|
e8121b39ec | ||
|
|
5359abab76 | ||
|
|
01644ac3f0 | ||
|
|
8fbf552494 | ||
|
|
9c3390c5e6 | ||
|
|
24d45b99e3 | ||
|
|
936f2186cc | ||
|
|
c68b302fa2 | ||
|
|
ca7cab1a2d | ||
|
|
083b294659 | ||
|
|
bc24716476 | ||
|
|
4acab65016 | ||
|
|
f6b03f106c | ||
|
|
8f9bbeabd7 | ||
|
|
ac72ac7af2 | ||
|
|
68e00bc8e1 | ||
|
|
922d6b3b32 | ||
|
|
4dde98305e | ||
|
|
3593189a75 | ||
|
|
4f135be86c | ||
|
|
605b32f78a | ||
|
|
476a8b1429 | ||
|
|
da9165aebf | ||
|
|
455a1e9532 | ||
|
|
f12031e284 | ||
|
|
df551bc992 | ||
|
|
35fb702833 | ||
|
|
072cd29e3c | ||
|
|
624dd78460 | ||
|
|
51c4737a29 | ||
|
|
d63ea11cac | ||
|
|
3d307794be | ||
|
|
d32a05925f | ||
|
|
04006ea81e | ||
|
|
1723616968 | ||
|
|
23bc2171c9 | ||
|
|
0b3602f764 | ||
|
|
0e448347cd | ||
|
|
e0c66ea5ba | ||
|
|
268de9a9d5 | ||
|
|
74eb523178 | ||
|
|
8c540d5a46 | ||
|
|
2045055ae3 | ||
|
|
d8ed135b46 | ||
|
|
3a4ba7e834 | ||
|
|
fab57fbdf1 | ||
|
|
76a93b6cd6 | ||
|
|
f44c3a4606 | ||
|
|
714bee7f45 | ||
|
|
91673642b3 | ||
|
|
fee08d746c | ||
|
|
527ff0238a | ||
|
|
789d649b5c | ||
|
|
8020657d8c | ||
|
|
a15d3ff12d | ||
|
|
fbce95aeda | ||
|
|
9bc1b5a2d9 | ||
|
|
6a8b7bf92f | ||
|
|
48728bdfdc | ||
|
|
b6416cf8a9 | ||
|
|
053de5763a | ||
|
|
fdb915d0c8 | ||
|
|
c2769c9aed | ||
|
|
57deb19d0b | ||
|
|
321771b150 | ||
|
|
61dd2011f5 | ||
|
|
8a5f189213 | ||
|
|
14560b008a | ||
|
|
db85e07d41 | ||
|
|
07c838fe24 | ||
|
|
1583b1b0c6 | ||
|
|
d250555cd0 | ||
|
|
dbd0dab154 | ||
|
|
b479ea410d | ||
|
|
1db613e95b | ||
|
|
70071fe1f6 | ||
|
|
58688dda38 | ||
|
|
756d65c41e | ||
|
|
b9a08eacb0 | ||
|
|
b653711446 | ||
|
|
e725c647ef | ||
|
|
003f77b257 | ||
|
|
10b578e6d2 | ||
|
|
151c6c2c70 | ||
|
|
53e6de9bd9 | ||
|
|
66a455a079 | ||
|
|
f8855410e0 | ||
|
|
93e8074325 | ||
|
|
296f512bfb | ||
|
|
c8f20d0edf | ||
|
|
286f3630d7 | ||
|
|
38423505b1 | ||
|
|
9a3c621eb6 | ||
|
|
bcf847fe98 | ||
|
|
ee7a5eef99 | ||
|
|
7bed66a458 | ||
|
|
b26229c44e | ||
|
|
c497c0f96a | ||
|
|
6df64613f2 | ||
|
|
46dd648380 | ||
|
|
4cd5de78c7 | ||
|
|
32fa46d6eb | ||
|
|
902c2f6c88 | ||
|
|
2a5196873a | ||
|
|
d2067141d3 | ||
|
|
b5229ec73c | ||
|
|
3a2a16f54c | ||
|
|
b2d428618c | ||
|
|
97af84a063 | ||
|
|
18faee6250 | ||
|
|
fd90a3ddc3 | ||
|
|
a08931d84d | ||
|
|
2f0b6e24c9 | ||
|
|
33c5fc0fb6 | ||
|
|
71adb2dcf8 | ||
|
|
1ae324691d | ||
|
|
689d059517 | ||
|
|
18f0ab9e02 | ||
|
|
088ab5f137 | ||
|
|
3dd83975ba | ||
|
|
a796a6cbef | ||
|
|
4766d7de82 | ||
|
|
4a3686c16c | ||
|
|
b49105495c | ||
|
|
ca2eda2c5b | ||
|
|
4fc7c4aafc | ||
|
|
0981828493 | ||
|
|
6bc4f18a59 | ||
|
|
79532a315c | ||
|
|
7472a992ed | ||
|
|
e40616a0db | ||
|
|
dd8b97a86c | ||
|
|
5eff3897de | ||
|
|
530cf2eb14 | ||
|
|
2baa34beb8 | ||
|
|
87e2f7f86d | ||
|
|
e142083d53 | ||
|
|
2e850a0d0d | ||
|
|
aec9c31bca | ||
|
|
598a147500 | ||
|
|
ba201c4c92 | ||
|
|
5bc7cfaa43 | ||
|
|
2804e1ff81 | ||
|
|
ae27d36902 | ||
|
|
08182fbe4d | ||
|
|
778474f436 | ||
|
|
c6698ce305 | ||
|
|
21bc215313 | ||
|
|
88a9cc42d2 | ||
|
|
28f33a67cf | ||
|
|
6742fabfba | ||
|
|
b3806f4533 | ||
|
|
6d5fb4ccc6 | ||
|
|
2487f18f24 | ||
|
|
f59afff1d1 | ||
|
|
b92f95b092 | ||
|
|
d68ae01448 | ||
|
|
87eda834d3 | ||
|
|
c28ff5259a | ||
|
|
4619259e26 | ||
|
|
7bf0afa621 | ||
|
|
e3e02c7271 | ||
|
|
24e17cb7d8 | ||
|
|
769998adca | ||
|
|
9b5d88e92d | ||
|
|
6d7bebee3a | ||
|
|
1c38654321 | ||
|
|
da7a78691f | ||
|
|
1eaf7083ef | ||
|
|
79a6fcdc5c | ||
|
|
90bcbbf426 | ||
|
|
41049e2a40 | ||
|
|
c9b8510e9c | ||
|
|
d854fc1cd0 | ||
|
|
5f7bcae072 | ||
|
|
652eec3033 | ||
|
|
a9928ec98a | ||
|
|
e1dfb608ca | ||
|
|
2cee3a8809 | ||
|
|
804f72b31a | ||
|
|
e106dcc83b | ||
|
|
9347be639b | ||
|
|
ffc0c42a63 | ||
|
|
f5e855b3ad | ||
|
|
9af4dc4bff | ||
|
|
6f6180c34a | ||
|
|
6ddd751713 | ||
|
|
2c72b06012 | ||
|
|
88854c1aa4 | ||
|
|
5ad704469f | ||
|
|
c140e17a6f | ||
|
|
4fc733a9c8 | ||
|
|
c5c59e4c55 | ||
|
|
e0edef6719 | ||
|
|
0f46ce49e2 | ||
|
|
f9fd39b8c1 | ||
|
|
bd75157428 | ||
|
|
f987f3ba77 | ||
|
|
1c16806efd | ||
|
|
29155fef23 | ||
|
|
c05cbed5b2 | ||
|
|
4f85d4db71 | ||
|
|
468468ab9f | ||
|
|
1b4a6da577 | ||
|
|
aed7a197c7 | ||
|
|
c257b7ecfd | ||
|
|
d6072f6218 | ||
|
|
4ea72a2892 | ||
|
|
89b07a1c6b | ||
|
|
db0cd61807 | ||
|
|
6feb906953 | ||
|
|
7cbf8c63bc | ||
|
|
fae51d4e00 | ||
|
|
c510584afd | ||
|
|
738878c2ff | ||
|
|
d5682fe49a | ||
|
|
d8dd22048a | ||
|
|
737d8b96b6 | ||
|
|
c8627c9713 | ||
|
|
65fa7da24a | ||
|
|
c4c6ef799f | ||
|
|
0d18b91895 | ||
|
|
42089aaadc | ||
|
|
432d0f3318 | ||
|
|
4ade6587e8 | ||
|
|
1724f11429 | ||
|
|
75d66d1048 | ||
|
|
feccef67b9 | ||
|
|
a7d30a199d | ||
|
|
37bbfb11d3 | ||
|
|
79c80b4043 | ||
|
|
18a1402ae2 | ||
|
|
5d639b91b0 | ||
|
|
080c09d22e | ||
|
|
00c2670039 | ||
|
|
f113184165 | ||
|
|
d69f8af107 | ||
|
|
ad2bd1bf63 | ||
|
|
059786ab66 | ||
|
|
72dfa5e5e6 | ||
|
|
d84da6b7b8 | ||
|
|
6aa82d82ad | ||
|
|
a649eb2a48 | ||
|
|
83932c8099 | ||
|
|
95e36cece1 | ||
|
|
7075a7f1c0 | ||
|
|
3093610d26 | ||
|
|
86eed7f895 | ||
|
|
5dc14c5861 | ||
|
|
cab87f6fb2 | ||
|
|
fb3acd1f4a | ||
|
|
b247cef85c | ||
|
|
70445f33e9 | ||
|
|
cadb4f1389 | ||
|
|
bf75a4de61 | ||
|
|
400ab584ac | ||
|
|
9edad62144 | ||
|
|
e94205098f | ||
|
|
35ab7eb472 | ||
|
|
18619b14ac | ||
|
|
97e0b9b65a | ||
|
|
5175d9f4a6 | ||
|
|
a5aed0f4ab | ||
|
|
14d693a8ff | ||
|
|
645963cc24 | ||
|
|
20751c2807 | ||
|
|
dda886b97c | ||
|
|
b796ceecdd | ||
|
|
4def89ebb5 | ||
|
|
bd1c1839dd | ||
|
|
8739f698f9 | ||
|
|
b8c8482f20 | ||
|
|
4348ca8cc5 | ||
|
|
d1aa59080f | ||
|
|
bf0ffa80be | ||
|
|
2a89a0c87e | ||
|
|
de84489a20 | ||
|
|
40901778ad | ||
|
|
c1febea659 | ||
|
|
b42f4ac288 | ||
|
|
fae57a5868 | ||
|
|
7a02d09508 | ||
|
|
8ec6fe960d | ||
|
|
61a77e19ef | ||
|
|
82e88b54c7 | ||
|
|
ab8975f4da | ||
|
|
5e7fd21c8d | ||
|
|
30cd9b2ae4 | ||
|
|
4f7d12f3fd | ||
|
|
62da158823 | ||
|
|
2d19b87208 | ||
|
|
54ca775006 | ||
|
|
a64a0d64cb | ||
|
|
6634dc81fe | ||
|
|
a2d5eef398 | ||
|
|
8184ba246a | ||
|
|
54ad1d103a | ||
|
|
d92f8a90ef | ||
|
|
d33c287344 | ||
|
|
782eebf976 | ||
|
|
0fbf75b95a | ||
|
|
5781facf4b | ||
|
|
b710ffc403 | ||
|
|
7d8edccb04 | ||
|
|
3750d70173 | ||
|
|
025e0bb546 | ||
|
|
033367de91 | ||
|
|
f93b605b75 | ||
|
|
7b4141b0c8 | ||
|
|
90acef72dc | ||
|
|
43f200be77 | ||
|
|
2fbb167803 | ||
|
|
2b396262f0 | ||
|
|
74deca52b9 | ||
|
|
058d956464 | ||
|
|
0f38dc1bd1 | ||
|
|
576b03c047 | ||
|
|
59cb1ad1e0 | ||
|
|
f77acdce14 | ||
|
|
43a98a1df1 | ||
|
|
d1cf771c14 | ||
|
|
10e5fcc375 | ||
|
|
2112932d04 | ||
|
|
be505ce6e0 | ||
|
|
4d6472128c | ||
|
|
26b7b1ec63 | ||
|
|
9a8f0a9b5a | ||
|
|
16dbbf3a1e | ||
|
|
8492d0a264 | ||
|
|
d8f8f1f590 | ||
|
|
b762e81da0 | ||
|
|
8b3b68ea69 | ||
|
|
28525766a5 | ||
|
|
d8fc23c38f | ||
|
|
8cae70fa36 | ||
|
|
7f2b98fad7 | ||
|
|
2b6dde2ac5 | ||
|
|
0c254fa7c7 | ||
|
|
fe7375becc | ||
|
|
b6ee88056e | ||
|
|
96f691ad64 | ||
|
|
8cf3380a77 | ||
|
|
e8d7e3e98b | ||
|
|
8af0a8476f | ||
|
|
2de4248f76 | ||
|
|
8c73ab6afb | ||
|
|
ee9bd77dc4 | ||
|
|
81f31ca215 | ||
|
|
51eff372f8 | ||
|
|
087376b44d | ||
|
|
e49047ef7d | ||
|
|
c117f36009 | ||
|
|
5ec83ee759 | ||
|
|
410165c14d | ||
|
|
973ef9fcda | ||
|
|
396bfb8540 | ||
|
|
aaf597bffd | ||
|
|
63c4f64bf5 | ||
|
|
5745dc34f7 | ||
|
|
114b8cff93 | ||
|
|
261571bb69 | ||
|
|
497e2c9163 | ||
|
|
ac01ffaa54 | ||
|
|
eb9fe06d10 | ||
|
|
506f2f7db7 | ||
|
|
f476c56f91 | ||
|
|
474688888d | ||
|
|
7ddbb613fe | ||
|
|
adc1c06089 | ||
|
|
1f8631cc99 | ||
|
|
f9e4e71285 | ||
|
|
5529194c8d | ||
|
|
5b008163af | ||
|
|
802da7087c | ||
|
|
53ab7617ca | ||
|
|
4369255a25 | ||
|
|
b709ee6842 | ||
|
|
4a8f9efe79 | ||
|
|
19d36fbe69 | ||
|
|
d37e73db54 | ||
|
|
a8f6615380 | ||
|
|
97fe38fbbc | ||
|
|
3378175450 | ||
|
|
9f60d2aea3 | ||
|
|
46d49e18c0 | ||
|
|
43fc335be2 | ||
|
|
f68339c571 | ||
|
|
0d68b7078c | ||
|
|
2e71dde780 | ||
|
|
51ddd584d6 | ||
|
|
4b090259cd | ||
|
|
f3b9ff5f9f | ||
|
|
4ec1a8d9c3 | ||
|
|
350cfeb7ef | ||
|
|
97d379b13e | ||
|
|
2154d555e5 | ||
|
|
1389c36657 | ||
|
|
bd1e7d6942 | ||
|
|
aadab38487 | ||
|
|
91299a279b | ||
|
|
53275c9137 | ||
|
|
fb1c318a09 | ||
|
|
2a07b3f46a | ||
|
|
ffc4e99717 | ||
|
|
fe807dd66f | ||
|
|
40ce5db812 | ||
|
|
649a9f4a6f | ||
|
|
e17ce875be | ||
|
|
415701c58a | ||
|
|
f16f80b459 | ||
|
|
628e6cc87f | ||
|
|
8f9a225c3a | ||
|
|
7b41bc95e1 | ||
|
|
2d06ee5822 | ||
|
|
40b4fb966f | ||
|
|
be696a9d76 | ||
|
|
1ffe9bef35 | ||
|
|
ed70d95c44 | ||
|
|
49bf876f73 | ||
|
|
e512b4d3e3 | ||
|
|
e78b6c7c7c | ||
|
|
a7403c0005 | ||
|
|
5bed0dddaa | ||
|
|
bc302dad23 | ||
|
|
41b0f8852f | ||
|
|
2fadd3e7ba | ||
|
|
dcfd2b8d60 | ||
|
|
7fe63a7aad | ||
|
|
67f2ec2421 | ||
|
|
8e7a23a7b9 | ||
|
|
8e96a27d51 | ||
|
|
b337d4d9d5 | ||
|
|
d9404c0362 | ||
|
|
e4ba5f449f | ||
|
|
17fd749e8d | ||
|
|
f3be5b5e57 | ||
|
|
e619eb9cbb | ||
|
|
a65ee155c5 | ||
|
|
4037480d2b | ||
|
|
97e6978ed8 | ||
|
|
f05eefcc20 | ||
|
|
3f926dfd8b | ||
|
|
59f64c5e33 | ||
|
|
87059a2004 | ||
|
|
7c52dd2bd8 | ||
|
|
70c2765a6e | ||
|
|
3618f4d642 | ||
|
|
698569536d | ||
|
|
c398d3d16e | ||
|
|
9dbc54aad6 | ||
|
|
931cb223cd | ||
|
|
336ee53fea | ||
|
|
34350af61c | ||
|
|
72f7cb3b7d | ||
|
|
481047a446 | ||
|
|
3bf1d73b07 | ||
|
|
b3b830bb5f | ||
|
|
def0c55df3 | ||
|
|
b995e00850 | ||
|
|
32dfc94909 | ||
|
|
20f7118432 | ||
|
|
37e6c3f0cb | ||
|
|
0459dda5c2 | ||
|
|
a10c816c05 | ||
|
|
c0c7cfacc2 | ||
|
|
8cdb6647ed | ||
|
|
b68e3c85b9 | ||
|
|
ef25d5bbd5 | ||
|
|
278bf6e897 | ||
|
|
90a985b73a | ||
|
|
b016353809 | ||
|
|
bc222af2e2 | ||
|
|
44599c9460 | ||
|
|
902fc22670 | ||
|
|
d84e22fbf1 | ||
|
|
6cc720a350 | ||
|
|
c905978874 | ||
|
|
bfe595a8e8 | ||
|
|
94d56d1a9e | ||
|
|
bd3f0b5363 | ||
|
|
6fae3545c7 | ||
|
|
297963f6ab | ||
|
|
a0ceba407e | ||
|
|
35fa91fba2 | ||
|
|
a5d6cd169e | ||
|
|
06c2263657 | ||
|
|
472c6f6b8d | ||
|
|
afcaca85da | ||
|
|
2575349ec2 | ||
|
|
77054f688d | ||
|
|
c5cb24378f | ||
|
|
d66123dd14 | ||
|
|
4ef8f11f9e | ||
|
|
599be7ddc9 | ||
|
|
685497fc8c | ||
|
|
57542fc204 | ||
|
|
8d9323aec6 | ||
|
|
4984a25b19 | ||
|
|
3bdcb29e01 | ||
|
|
b9ab8e6348 | ||
|
|
4a851701e9 | ||
|
|
eafd8af937 | ||
|
|
28bb123be8 | ||
|
|
8e1ed2f8c3 | ||
|
|
4a0fcb3cff | ||
|
|
b5c2163238 | ||
|
|
d0ecdfb330 | ||
|
|
bdf7d98a36 | ||
|
|
e4b0980d15 | ||
|
|
d99c57aeae | ||
|
|
5f855ce547 | ||
|
|
68f3b6fbeb | ||
|
|
dc6d14943e | ||
|
|
05f7334db9 | ||
|
|
cd707e9335 | ||
|
|
505ed5e43b | ||
|
|
9dc8bf5f62 | ||
|
|
f8098662b6 | ||
|
|
de7a0e86c4 | ||
|
|
ca55438fda | ||
|
|
a4b60e7eef | ||
|
|
5d22e07738 | ||
|
|
bdade7e151 | ||
|
|
6958ded4c4 | ||
|
|
cc06104be6 | ||
|
|
059c34c345 | ||
|
|
9cbb726566 | ||
|
|
e8b4356a88 | ||
|
|
634138cd3b | ||
|
|
3f6b07de81 | ||
|
|
d218900b57 | ||
|
|
cab949a939 | ||
|
|
e350822f5e | ||
|
|
3d6fe93297 | ||
|
|
3a5baf2722 | ||
|
|
cdb472cb56 | ||
|
|
e634350e77 | ||
|
|
464a6c1441 | ||
|
|
d3750b2244 | ||
|
|
b8d2b76ee4 | ||
|
|
14fb7c3201 | ||
|
|
922bc62e0e | ||
|
|
00cae4ccd1 | ||
|
|
61df266df7 | ||
|
|
17751666f2 | ||
|
|
961b2c9b65 | ||
|
|
eaf6f06047 | ||
|
|
d0ac218c05 | ||
|
|
1a01bc15d8 | ||
|
|
cdbe2f13ca | ||
|
|
4f5264277e | ||
|
|
bed3adcc95 | ||
|
|
0c5d59bcf6 | ||
|
|
ff20acd8b8 | ||
|
|
d1cffcde68 | ||
|
|
546dd9b225 | ||
|
|
2a666620c8 | ||
|
|
db50985040 | ||
|
|
8c6d593c41 | ||
|
|
40086c8d1c | ||
|
|
38f73b9f5e | ||
|
|
49429b5f49 | ||
|
|
d9f90ef077 | ||
|
|
6a126fa18e | ||
|
|
14d703f5cc | ||
|
|
9850025f3a | ||
|
|
8daab0ba41 | ||
|
|
88f25ccc76 | ||
|
|
50e7351c73 | ||
|
|
51453a54e4 | ||
|
|
c02ba03f39 | ||
|
|
78dec3d698 | ||
|
|
1590c59131 | ||
|
|
b26f79998a | ||
|
|
489f3e6c9d | ||
|
|
5fef4726a7 | ||
|
|
51fdb8200a | ||
|
|
fb0d98ead1 | ||
|
|
4096caba64 | ||
|
|
38f77144fa | ||
|
|
b8041ce17f | ||
|
|
00575733c8 | ||
|
|
621c92780c | ||
|
|
37f03af4b7 | ||
|
|
bdc4558a43 | ||
|
|
46d483e8b2 | ||
|
|
5143b62a07 | ||
|
|
87ed774cb4 | ||
|
|
4e427d05b7 | ||
|
|
195d987331 | ||
|
|
9b58317fb1 | ||
|
|
1cb15dedac | ||
|
|
5521d6b623 | ||
|
|
f5cf6c1dcb | ||
|
|
89307727e7 | ||
|
|
c30c21b250 | ||
|
|
b6fb087d7e | ||
|
|
97c2b7c1fe | ||
|
|
81506640c8 | ||
|
|
8244f7cd58 | ||
|
|
63573d6e26 | ||
|
|
9368f7f670 | ||
|
|
b7a11035d5 | ||
|
|
2e1b5e2fa7 | ||
|
|
8e5cbafe63 | ||
|
|
eb006a6688 | ||
|
|
40b9049deb | ||
|
|
64ffca2d0a | ||
|
|
1f4b6d4e9a | ||
|
|
eb30ba7722 | ||
|
|
3e0f4d36bc | ||
|
|
1fb118edee | ||
|
|
ca39634880 | ||
|
|
f8418d68a6 | ||
|
|
c0000fb24e | ||
|
|
c169f7ae83 | ||
|
|
7ab5244bf5 | ||
|
|
7e36489034 | ||
|
|
d259b12ae7 | ||
|
|
8f8138d9bd | ||
|
|
a8668f5fbf | ||
|
|
e00e6bd1c2 | ||
|
|
0fc6c47556 | ||
|
|
d530dccb96 | ||
|
|
37bf0e81d2 | ||
|
|
f872f955b0 | ||
|
|
003cd0a203 | ||
|
|
e7173f8145 | ||
|
|
93af102bec | ||
|
|
67f23664c9 | ||
|
|
39f6071c68 | ||
|
|
edc8878632 | ||
|
|
93b62c4dfd | ||
|
|
c3e210c80f | ||
|
|
9a5cc8f650 | ||
|
|
5e7fbfefe9 | ||
|
|
29ad6fba22 | ||
|
|
c848c3ee0d | ||
|
|
547132131a | ||
|
|
40186fb54b | ||
|
|
5dc4e79c8d | ||
|
|
556992a117 | ||
|
|
a41ef8d513 | ||
|
|
6d96a89328 | ||
|
|
0fb1481038 | ||
|
|
4b77530c65 | ||
|
|
cdb493cf50 | ||
|
|
c9a574a764 | ||
|
|
bdb633a882 | ||
|
|
37aee7133d | ||
|
|
2cfd55f3ce | ||
|
|
2e8188e89d | ||
|
|
1e172caea3 | ||
|
|
6866cd0fb8 | ||
|
|
2719926870 | ||
|
|
406891abcc | ||
|
|
4be6e9009a | ||
|
|
6a31909557 | ||
|
|
f49f0d19f9 | ||
|
|
a2407f94e4 | ||
|
|
62a0be4a11 | ||
|
|
b9029f6c67 | ||
|
|
92496f42c9 | ||
|
|
841c907efc | ||
|
|
9bc288cd8e | ||
|
|
2ef0391b08 | ||
|
|
6db768d1a3 | ||
|
|
9b080950a9 | ||
|
|
3cd1fef4cb | ||
|
|
a491a801bf | ||
|
|
41ebb72276 | ||
|
|
e4c0e9073e | ||
|
|
efad733cc5 | ||
|
|
1fcd6e1811 | ||
|
|
75d697fa97 | ||
|
|
cde69670ba | ||
|
|
0be6c9cb97 | ||
|
|
8c9d2d3ca2 | ||
|
|
c84e73d8e6 | ||
|
|
3fb00f6c70 | ||
|
|
86305fdeb3 | ||
|
|
710e146d22 | ||
|
|
d5abc84bea | ||
|
|
6f8214c15c | ||
|
|
a56475e32f | ||
|
|
9dadd91887 | ||
|
|
df5a73bb35 | ||
|
|
a50bf59a2c | ||
|
|
241354ae52 | ||
|
|
c863116011 | ||
|
|
5cbd591a1e | ||
|
|
6bb78c9499 | ||
|
|
893f406a99 | ||
|
|
f722b898b0 | ||
|
|
18335b3eac | ||
|
|
4708b592e2 | ||
|
|
7598a2965c | ||
|
|
1b59761a19 | ||
|
|
7fd1053e0f | ||
|
|
e178e7f6a5 | ||
|
|
d0bf21ab16 | ||
|
|
c826965833 | ||
|
|
a76db39c19 | ||
|
|
323a538bdd | ||
|
|
2435feca3b | ||
|
|
29d059680c | ||
|
|
aaaf7f9d8b | ||
|
|
bc90ce569a | ||
|
|
27f9f1756b | ||
|
|
9135387cfa | ||
|
|
af3504e05c | ||
|
|
9c9eac9d46 | ||
|
|
f28383206a | ||
|
|
ba1c093d18 | ||
|
|
3ea3a85694 | ||
|
|
0e1a423889 | ||
|
|
3ce68ed53f | ||
|
|
90c10da398 | ||
|
|
630bf28c22 | ||
|
|
ff89c1d8e4 | ||
|
|
96703c23c8 | ||
|
|
2b78f5adad | ||
|
|
ab7df777eb | ||
|
|
adbc694ff8 | ||
|
|
7709cdb12b | ||
|
|
f6051f73f5 | ||
|
|
d5c2a21cdf | ||
|
|
8ffc841a3a | ||
|
|
6c478977bb | ||
|
|
07beb53b37 | ||
|
|
59df1a8647 | ||
|
|
fb98aa650d | ||
|
|
9981b5a9c6 | ||
|
|
603533e632 | ||
|
|
38bc90f724 | ||
|
|
721beb9202 | ||
|
|
2e99259589 | ||
|
|
83e6b75872 | ||
|
|
cf70946f50 | ||
|
|
f1c36aba83 | ||
|
|
2973cf15a4 | ||
|
|
55969bc3c5 | ||
|
|
a8504c971a | ||
|
|
1644de2083 | ||
|
|
dd1fb85f60 | ||
|
|
b945b19865 | ||
|
|
91bf9f2ec8 | ||
|
|
f151f4c165 | ||
|
|
95b9b2d373 | ||
|
|
8be698681c | ||
|
|
da3e1a3730 | ||
|
|
29f637c19a | ||
|
|
3d01565ba7 | ||
|
|
962291cc8e | ||
|
|
a662dace9c | ||
|
|
d34e0b325e | ||
|
|
4c242ec528 | ||
|
|
658be9405f | ||
|
|
15e8f6ad8a | ||
|
|
a5aca35f1c | ||
|
|
ae9df1ffac | ||
|
|
8bf2e098f5 | ||
|
|
7091d554b5 | ||
|
|
bada795320 | ||
|
|
7d385849fb | ||
|
|
78775b7f6b | ||
|
|
8d21a5b6b3 | ||
|
|
9da095ca50 | ||
|
|
0fafc07db0 | ||
|
|
65b95999c1 | ||
|
|
056012871f | ||
|
|
c7b377080c | ||
|
|
47acc9ff2f | ||
|
|
fe62700825 | ||
|
|
6bcab56988 | ||
|
|
fbc4d3e1be | ||
|
|
c89d1643f4 | ||
|
|
e2226a1509 | ||
|
|
656916e0fa | ||
|
|
4a55eb9e7f | ||
|
|
9c8cf04a80 | ||
|
|
e846bc9308 | ||
|
|
3c7a71772c | ||
|
|
857a53e80f | ||
|
|
84fcabe5cf | ||
|
|
9dc410c9fd | ||
|
|
8ebad06e7e | ||
|
|
5db1a07f81 | ||
|
|
48e5c8edb3 | ||
|
|
eefc21920e | ||
|
|
d7dd9c295b | ||
|
|
4faf71bd03 | ||
|
|
2751dbdb94 | ||
|
|
fe329cb4ab | ||
|
|
741e719ff8 | ||
|
|
866bb32046 | ||
|
|
7f01e758b3 | ||
|
|
03003d270e | ||
|
|
3d313016ba | ||
|
|
1292c752a8 | ||
|
|
4783947135 | ||
|
|
c9045788f5 | ||
|
|
4eed8463b3 | ||
|
|
303711ab8d | ||
|
|
98d7fc9f39 | ||
|
|
c0a96f2087 | ||
|
|
c509ecca52 | ||
|
|
e5d8eb7845 | ||
|
|
277f43aed6 | ||
|
|
f1494b64e5 | ||
|
|
6b9c71ec62 | ||
|
|
d12a4b0a1a | ||
|
|
a75d075dd1 | ||
|
|
8f8a37bf94 | ||
|
|
03f692fed8 | ||
|
|
614416d4b0 | ||
|
|
feb851716e | ||
|
|
2b095f720e | ||
|
|
d6e750727f | ||
|
|
961ff8633d | ||
|
|
8406d4a8f1 | ||
|
|
e25b64ae15 | ||
|
|
59205a4caf | ||
|
|
5001797179 | ||
|
|
b24dd69067 | ||
|
|
4f163338dd | ||
|
|
1e8f1f8cc6 | ||
|
|
5c3e2e6877 | ||
|
|
ff52ff7865 | ||
|
|
f6f55aaa9a | ||
|
|
6f1aecab99 | ||
|
|
f33205a490 | ||
|
|
2920638a3d | ||
|
|
1ae32b5742 | ||
|
|
4e9dabfb25 | ||
|
|
ca2c419c9b | ||
|
|
020c1311ca | ||
|
|
8193b2a44b | ||
|
|
324a000777 | ||
|
|
951b1c1e83 | ||
|
|
8e57fd93c6 | ||
|
|
d262f43b5b | ||
|
|
73224e7d95 | ||
|
|
19463c81a3 | ||
|
|
6f2bb31068 | ||
|
|
abf9c4117d | ||
|
|
39f50830d7 | ||
|
|
c2fd7005cb | ||
|
|
52f2ed33f5 | ||
|
|
a51239c6ae | ||
|
|
e6217e1428 |
@@ -1,94 +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: python3 test.py
|
||||
- run: make FAIL_WARN=-W man
|
||||
- run: make FAIL_WARN=-W html
|
||||
- run: python3 setup.py linux-package
|
||||
|
||||
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: ./asan-launcher 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: ./asan-launcher 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: $SW/bin/python3 setup.py build --debug --verbose
|
||||
- run: $SW/bin/python3 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: python3 test.py
|
||||
|
||||
mac-brew:
|
||||
macos:
|
||||
xcode: "10.1.0"
|
||||
environment:
|
||||
LC_ALL: en_US.UTF-8
|
||||
LANG: en_US.UTF-8
|
||||
steps:
|
||||
- checkout
|
||||
- run: brew bundle
|
||||
- run: python3 logo/make.py
|
||||
- run: python3 setup.py build --debug --verbose
|
||||
- run: python3 test.py
|
||||
- run: python3 setup.py macos-bundle
|
||||
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
all_jobs:
|
||||
jobs:
|
||||
- mac-bundle
|
||||
- lin-sys
|
||||
- lin-35
|
||||
- lin-37
|
||||
- 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
|
||||
|
||||
# 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
|
||||
8
.gitattributes
vendored
8
.gitattributes
vendored
@@ -1,13 +1,15 @@
|
||||
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
|
||||
kitty/unicode-data.c linguist-generated=true
|
||||
kitty/rgb.py linguist-generated=true
|
||||
kitty/gl-wrapper.* linguist-generated=true
|
||||
kitty/khrplatform.h linguist-generated=true
|
||||
kitty/glfw-wrapper.* linguist-generated=true
|
||||
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
|
||||
|
||||
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
patreon: kovidgoyal
|
||||
liberapay: kovidgoyal
|
||||
custom: https://sw.kovidgoyal.net/kitty/support.html
|
||||
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
|
||||
32
.gitignore
vendored
32
.gitignore
vendored
@@ -1,19 +1,21 @@
|
||||
*.so
|
||||
*.pyc
|
||||
*.pyo
|
||||
tags
|
||||
build
|
||||
linux-package
|
||||
kitty.app
|
||||
logo/*.iconset
|
||||
compile_commands.json
|
||||
glad/out
|
||||
asan-launcher
|
||||
asan-launcher.dSYM
|
||||
kitty-profile
|
||||
dev
|
||||
__pycache__
|
||||
glfw/wayland-*-client-protocol.[ch]
|
||||
docs/_build
|
||||
docs/generated
|
||||
*_stub.pyi
|
||||
/.dmypy.json
|
||||
/tags
|
||||
/build/
|
||||
/linux-package/
|
||||
/kitty.app/
|
||||
/compile_commands.json
|
||||
/link_commands.json
|
||||
/glad/out/
|
||||
/kitty/launcher/
|
||||
/*.dSYM/
|
||||
__pycache__/
|
||||
/glfw/wayland-*-client-protocol.[ch]
|
||||
/docs/_build/
|
||||
/docs/generated/
|
||||
/.mypy_cache
|
||||
.DS_Store
|
||||
bypy/b
|
||||
|
||||
12
Brewfile
12
Brewfile
@@ -1,6 +1,6 @@
|
||||
brew 'pkg-config'
|
||||
brew 'imagemagick'
|
||||
brew 'optipng'
|
||||
brew 'librsvg'
|
||||
brew 'harfbuzz'
|
||||
brew 'python'
|
||||
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>
|
||||
|
||||
11
Makefile
11
Makefile
@@ -1,8 +1,8 @@
|
||||
ifdef V
|
||||
VVAL=--verbose
|
||||
VVAL=--verbose
|
||||
endif
|
||||
ifdef VERBOSE
|
||||
VVAL=--verbose
|
||||
VVAL=--verbose
|
||||
endif
|
||||
|
||||
all:
|
||||
@@ -28,12 +28,7 @@ asan:
|
||||
profile:
|
||||
python3 setup.py build $(VVAL) --profile
|
||||
|
||||
logo/kitty.iconset/icon_256x256.png: logo/kitty.svg logo/make.py
|
||||
logo/make.py
|
||||
|
||||
rendered_logo: logo/kitty.iconset/icon_256x256.png
|
||||
|
||||
app: rendered_logo
|
||||
app:
|
||||
python3 setup.py kitty.app $(VVAL)
|
||||
|
||||
man:
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
= kitty - the fast, featureful, GPU based, terminal emulator
|
||||
= kitty - the fast, feature-rich, cross-platform, GPU based terminal
|
||||
|
||||
See https://sw.kovidgoyal.net/kitty
|
||||
See https://sw.kovidgoyal.net/kitty/[the kitty website].
|
||||
|
||||
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"]
|
||||
|
||||
https://sw.kovidgoyal.net/kitty/faq.html[Frequently Asked Questions]
|
||||
|
||||
To ask other 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.
|
||||
68
__main__.py
68
__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,11 +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'
|
||||
setattr(sys, 'kitty_ssl_env_var', 'SSL_CERT_FILE')
|
||||
elif os.path.isdir('/etc/ssl/certs'):
|
||||
os.environ['SSL_CERT_DIR'] = '/etc/ssl/certs'
|
||||
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]
|
||||
@@ -107,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:])
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* asan-launcher.c
|
||||
* Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#define MAX_ARGC 1024
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
wchar_t *argvw[MAX_ARGC + 1] = {0};
|
||||
argvw[0] = L"kitty";
|
||||
for (int i = 1; i < argc; i++) argvw[i] = Py_DecodeLocale(argv[i], NULL);
|
||||
int ret = Py_Main(argc, argvw);
|
||||
for (int i = 1; i < argc; i++) PyMem_RawFree(argvw[i]);
|
||||
return ret;
|
||||
}
|
||||
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', 'brotlicommon', 'brotlienc', 'brotlidec'
|
||||
))) + (
|
||||
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()
|
||||
284
bypy/sources.json
Normal file
284
bypy/sources.json
Normal file
@@ -0,0 +1,284 @@
|
||||
[
|
||||
{
|
||||
"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": "brotli",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "brotli-1.0.9.tar.gz",
|
||||
"hash": "sha256:f9e8d81d0405ba66d181529af42a3354f838c939095ff99930da6aa9cdf6fe46",
|
||||
"urls": ["https://github.com/google/brotli/archive/v1.0.9/{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}"]
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
@@ -1,2 +1,25 @@
|
||||
#!/bin/bash
|
||||
cloc --exclude-list-file <(echo -e 'kitty/wcwidth-std.h\nkitty/glfw.c\nkitty/keys.h\nkitty/charsets.c\nkitty/unicode-data.c\nkitty/key_encoding.py\nkitty/rgb.py\nkitty/gl.h\nkitty/gl-wrapper.h\nkitty/gl-wrapper.c\nkitty/khrplatform.h\nkitty/glfw-wrapper.h\nkitty/glfw-wrapper.c\nkitty/emoji.h\nkittens/unicode_input/names.h') kitty kittens
|
||||
#!/usr/bin/env python
|
||||
|
||||
import subprocess
|
||||
|
||||
files_to_exclude = '''\
|
||||
kitty/wcwidth-std.h
|
||||
kitty/charsets.c
|
||||
kitty/unicode-data.c
|
||||
kitty/key_encoding.py
|
||||
kitty/rgb.py
|
||||
kitty/gl.h
|
||||
kitty/gl-wrapper.h
|
||||
kitty/gl-wrapper.c
|
||||
kitty/glfw-wrapper.h
|
||||
kitty/glfw-wrapper.c
|
||||
kitty/emoji.h
|
||||
kittens/unicode_input/names.h
|
||||
kitty/parse-graphics-command.h
|
||||
'''
|
||||
|
||||
p = subprocess.Popen([
|
||||
'cloc', '--exclude-list-file', '/dev/stdin', 'kitty', 'kittens'
|
||||
], stdin=subprocess.PIPE)
|
||||
p.communicate(files_to_exclude.encode('utf-8'))
|
||||
raise SystemExit(p.wait())
|
||||
|
||||
6
docs/_static/custom.css
vendored
6
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;
|
||||
}
|
||||
@@ -78,6 +82,8 @@ body div.document {
|
||||
div.sphinxsidebar {
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#sidebartoc li {
|
||||
|
||||
126
docs/basic.rst
Normal file
126
docs/basic.rst
Normal file
@@ -0,0 +1,126 @@
|
||||
Tabs and Windows
|
||||
-------------------
|
||||
|
||||
|kitty| is capable of running multiple programs organized into tabs and
|
||||
windows. The top level of organization is the *Tab*. Each tab consists
|
||||
of one or more *windows*. The windows can be arranged in multiple
|
||||
different layouts, like windows are organized in a tiling window
|
||||
manager. The keyboard controls (which are all customizable) for tabs and
|
||||
windows are:
|
||||
|
||||
Scrolling
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
======================== =======================
|
||||
Action Shortcut
|
||||
======================== =======================
|
||||
Scroll line up :sc:`scroll_line_up` (also :kbd:`⌥+⌘+⇞` and :kbd:`⌘+↑` on macOS)
|
||||
Scroll line down :sc:`scroll_line_down` (also :kbd:`⌥+⌘+⇟` and :kbd:`⌘+↓` on macOS)
|
||||
Scroll page up :sc:`scroll_page_up` (also :kbd:`⌘+⇞` on macOS)
|
||||
Scroll page down :sc:`scroll_page_down` (also :kbd:`⌘+⇟` on macOS)
|
||||
Scroll to top :sc:`scroll_home` (also :kbd:`⌘+↖` on macOS)
|
||||
Scroll to bottom :sc:`scroll_end` (also :kbd:`⌘+↘` on macOS)
|
||||
======================== =======================
|
||||
|
||||
Tabs
|
||||
~~~~~~~~~~~
|
||||
|
||||
======================== =======================
|
||||
Action Shortcut
|
||||
======================== =======================
|
||||
New tab :sc:`new_tab` (also :kbd:`⌘+t` on macOS)
|
||||
Close tab :sc:`close_tab` (also :kbd:`⌘+w` on macOS)
|
||||
Next tab :sc:`next_tab` (also :kbd:`^+⇥` and :kbd:`⇧+⌘+]` on macOS)
|
||||
Previous tab :sc:`previous_tab` (also :kbd:`⇧+^+⇥` and :kbd:`⇧+⌘+[` on macOS)
|
||||
Next layout :sc:`next_layout`
|
||||
Move tab forward :sc:`move_tab_forward`
|
||||
Move tab backward :sc:`move_tab_backward`
|
||||
Set tab title :sc:`set_tab_title` (also :kbd:`⇧+⌘+i` on macOS)
|
||||
======================== =======================
|
||||
|
||||
|
||||
Windows
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
======================== =======================
|
||||
Action Shortcut
|
||||
======================== =======================
|
||||
New window :sc:`new_window` (also :kbd:`⌘+↩` on macOS)
|
||||
New OS window :sc:`new_os_window` (also :kbd:`⌘+n` on macOS)
|
||||
Close window :sc:`close_window` (also :kbd:`⇧+⌘+d` on macOS)
|
||||
Next window :sc:`next_window`
|
||||
Previous window :sc:`previous_window`
|
||||
Move window forward :sc:`move_window_forward`
|
||||
Move window backward :sc:`move_window_backward`
|
||||
Move window to top :sc:`move_window_to_top`
|
||||
Focus specific window :sc:`first_window`, :sc:`second_window` ... :sc:`tenth_window`
|
||||
(also :kbd:`⌘+1`, :kbd:`⌘+2` ... :kbd:`⌘+9` on macOS)
|
||||
(clockwise from the top-left)
|
||||
======================== =======================
|
||||
|
||||
Additionally, you can define shortcuts in :file:`kitty.conf` to focus neighboring
|
||||
windows and move windows around (similar to window movement in vim)::
|
||||
|
||||
map ctrl+left neighboring_window left
|
||||
map shift+left move_window right
|
||||
map ctrl+down neighboring_window down
|
||||
map shift+down move_window up
|
||||
...
|
||||
|
||||
You can also define a shortcut to switch to the previously active window::
|
||||
|
||||
map ctrl+p nth_window -1
|
||||
|
||||
``nth_window`` will focus the nth window for positive numbers and the
|
||||
previously active windows for negative numbers.
|
||||
|
||||
.. _detach_window:
|
||||
|
||||
You can define shortcuts to detach the current window and
|
||||
move it to another tab or another OS window::
|
||||
|
||||
# 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::
|
||||
|
||||
# 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
|
||||
----------------------------------
|
||||
|
||||
================================== =======================
|
||||
Action Shortcut
|
||||
================================== =======================
|
||||
Copy to clipboard :sc:`copy_to_clipboard` (also :kbd:`⌘+c` on macOS)
|
||||
Paste from clipboard :sc:`paste_from_clipboard` (also :kbd:`⌘+v` on macOS)
|
||||
Paste from selection :sc:`paste_from_selection`
|
||||
Increase font size :sc:`increase_font_size` (also :kbd:`⌘++` on macOS)
|
||||
Decrease font size :sc:`decrease_font_size` (also :kbd:`⌘+-` on macOS)
|
||||
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` (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`
|
||||
Edit |kitty| config file :sc:`edit_config_file`
|
||||
Open a |kitty| shell :sc:`kitty_shell`
|
||||
Increase background opacity :sc:`increase_background_opacity`
|
||||
Decrease background opacity :sc:`decrease_background_opacity`
|
||||
Full background opacity :sc:`full_background_opacity`
|
||||
Reset background opacity :sc:`reset_background_opacity`
|
||||
================================== =======================
|
||||
@@ -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,8 +18,10 @@ 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)
|
||||
* ImageMagick (optional, needed to use the ``kitty icat`` tool to display images in the terminal)
|
||||
* pygments (optional, need for syntax highlighting in ``kitty +kitten diff``)
|
||||
|
||||
@@ -28,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
|
||||
@@ -44,25 +46,17 @@ Now build the native code parts of |kitty| with the following command::
|
||||
|
||||
You can run |kitty|, as::
|
||||
|
||||
python3 .
|
||||
./kitty/launcher/kitty
|
||||
|
||||
If that works, you can create a script to launch |kitty|:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/usr/bin/env python3
|
||||
import runpy
|
||||
runpy.run_path('/path/to/kitty/dir', run_name='__main__')
|
||||
|
||||
And place it in :file:`~/bin` or :file:`/usr/bin` so that you can run |kitty| using
|
||||
If that works, you can create a symlink to the launcher in :file:`~/bin` or
|
||||
some other directory on your PATH so that you can run |kitty| using
|
||||
just ``kitty``.
|
||||
|
||||
|
||||
Building kitty.app on macOS from source
|
||||
-------------------------------------------
|
||||
|
||||
Install `imagemagick`, `optipng` and `librsvg` using `brew` or similar (needed
|
||||
for the logo generation step). And run::
|
||||
Run::
|
||||
|
||||
make app
|
||||
|
||||
@@ -70,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
|
||||
@@ -111,3 +127,9 @@ without needing to install all of |kitty|.
|
||||
|
||||
This applies to creating packages for |kitty| for macOS package managers such as
|
||||
brew or MacPorts as well.
|
||||
|
||||
|
||||
.. note::
|
||||
|kitty| has its own update check mechanism, if you would like to turn
|
||||
it off for your package, use
|
||||
``python3 setup.py linux-package --update-check-interval=0``
|
||||
|
||||
1057
docs/changelog.rst
1057
docs/changelog.rst
File diff suppressed because it is too large
Load Diff
182
docs/conf.py
182
docs/conf.py
@@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#!/usr/bin/env python3
|
||||
# vim:fileencoding=utf-8
|
||||
#
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
@@ -6,33 +7,40 @@
|
||||
# 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 -----------------------------------------------------
|
||||
|
||||
project = 'kitty'
|
||||
copyright = '2018, Kovid Goyal'
|
||||
copyright = time.strftime('%Y, Kovid Goyal')
|
||||
author = 'Kovid Goyal'
|
||||
building_man_pages = 'man' in sys.argv
|
||||
|
||||
@@ -75,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.
|
||||
@@ -132,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
|
||||
@@ -162,8 +167,8 @@ html_show_sourcelink = False
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('invocation', 'kitty', 'kitty Documentation',
|
||||
[author], 1)
|
||||
('invocation', 'kitty', 'kitty Documentation', [author], 1),
|
||||
('conf', 'kitty.conf', 'kitty.conf Documentation', [author], 5)
|
||||
]
|
||||
|
||||
|
||||
@@ -174,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'),
|
||||
]
|
||||
# }}}
|
||||
@@ -182,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)
|
||||
@@ -200,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)
|
||||
@@ -211,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)
|
||||
@@ -233,29 +240,40 @@ 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:
|
||||
f.write(option_spec_as_rst(
|
||||
appname='launch', ospec=launch_options_spec, heading_char='_',
|
||||
message='''\
|
||||
Launch an arbitrary program in a new kitty window/tab. Note that
|
||||
if you specify a program-to-run you can use the special placeholder
|
||||
:code:`@selection` which will be replaced by the current selection.
|
||||
'''
|
||||
))
|
||||
with open('generated/cli-kitty.rst', 'w') as f:
|
||||
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)
|
||||
@@ -274,6 +292,48 @@ def write_cli_docs(all_kitten_names):
|
||||
# }}}
|
||||
|
||||
|
||||
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: Callable, name: str, cmd: RemoteCommand) -> None:
|
||||
p(name)
|
||||
p('-' * 80)
|
||||
lines = (cmd.__doc__ or '').strip().splitlines()
|
||||
fields = []
|
||||
for line in lines:
|
||||
m = field_pat.match(line)
|
||||
if m is None:
|
||||
p(line)
|
||||
else:
|
||||
fields.append((m.group(1), m.group(2)))
|
||||
if fields:
|
||||
p('\nFields are:\n')
|
||||
for (name, desc) in fields:
|
||||
if '+' in name:
|
||||
title = name.replace('+', ' (required)')
|
||||
else:
|
||||
title = name
|
||||
defval = cmd.get_default(name.replace('-', '_'), cmd)
|
||||
if defval is not cmd:
|
||||
title = f'{title} (default: {defval})'
|
||||
else:
|
||||
title = f'{title} (optional)'
|
||||
p(f':code:`{title}`')
|
||||
p(' ', desc), p()
|
||||
p(), p()
|
||||
|
||||
with open('generated/rc.rst', 'w') as f:
|
||||
p = partial(print, file=f)
|
||||
for name in sorted(all_command_names()):
|
||||
cmd = command_for_name(name)
|
||||
if not cmd.__doc__:
|
||||
continue
|
||||
name = name.replace('_', '-')
|
||||
format_cmd(p, name, cmd)
|
||||
# }}}
|
||||
|
||||
|
||||
# config file docs {{{
|
||||
|
||||
class ConfLexer(RegexLexer):
|
||||
@@ -329,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)
|
||||
@@ -341,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 = ''
|
||||
@@ -380,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('')
|
||||
@@ -407,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:
|
||||
@@ -419,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('')
|
||||
@@ -438,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}'
|
||||
@@ -456,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:
|
||||
@@ -467,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
|
||||
@@ -475,20 +536,23 @@ 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
|
||||
full_name = conf_name + '.' + slug
|
||||
try:
|
||||
target, title = shortcut_slugs[full_name]
|
||||
target, stitle = shortcut_slugs[full_name]
|
||||
except KeyError:
|
||||
logger.warning('Unknown shortcut: {}'.format(target), location=refnode)
|
||||
else:
|
||||
if not has_explicit_title:
|
||||
title = stitle
|
||||
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",
|
||||
@@ -508,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)
|
||||
@@ -530,13 +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)
|
||||
write_conf_docs(app, all_kitten_names)
|
||||
app.add_lexer('session', SessionLexer())
|
||||
kn = all_kitten_names()
|
||||
write_cli_docs(kn)
|
||||
write_remote_control_protocol_docs()
|
||||
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'))
|
||||
|
||||
@@ -39,6 +39,16 @@ expanded, so :code:`${USER}.conf` becomes :file:`name.conf` if
|
||||
Sample kitty.conf
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can download a sample :file:`kitty.conf` file with all default settings and
|
||||
comments describing each setting by clicking: :download:`sample kitty.conf
|
||||
</generated/conf/kitty.conf>`.
|
||||
.. only:: html
|
||||
|
||||
You can download a sample :file:`kitty.conf` file with all default settings and
|
||||
comments describing each setting by clicking: :download:`sample kitty.conf
|
||||
</generated/conf/kitty.conf>`.
|
||||
|
||||
.. only:: man
|
||||
|
||||
You can edit a fully commented sample kitty.conf by pressing the
|
||||
:sc:`edit_config_file` shortcut in kitty. This will generate a config
|
||||
file with full documentation and all settings commented out. If you
|
||||
have a pre-existing kitty.conf, then that will be used instead, delete
|
||||
it to see the sample file.
|
||||
|
||||
223
docs/faq.rst
223
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)::
|
||||
|
||||
@@ -63,6 +74,18 @@ Really, the correct solution for this is to convince the OpenSSH maintainers to
|
||||
have ssh do this automatically, if possible, when connecting to a server, so that
|
||||
all terminals work transparently.
|
||||
|
||||
If the server is running FreeBSD, or another system that relies on termcap
|
||||
rather than terminfo, you will need to convert the terminfo file on your local
|
||||
machine by running (on local machine with |kitty|)::
|
||||
|
||||
infocmp -C xterm-kitty
|
||||
|
||||
The output of this command is the termcap description, which should be appended
|
||||
to :file:`/usr/share/misc/termcap` on the remote server. Then run the following
|
||||
command to apply your change (on the server)::
|
||||
|
||||
cap_mkdb /usr/share/misc/termcap
|
||||
|
||||
|
||||
Keys such as arrow keys, backspace, delete, home/end, etc. do not work when using su or sudo?
|
||||
-------------------------------------------------------------------------------------------------
|
||||
@@ -97,12 +120,39 @@ How do I change the colors in a running kitty instance?
|
||||
|
||||
You can either use the
|
||||
`OSC terminal escape codes <https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Operating-System-Commands>`_
|
||||
to set colors or you can enable :doc:`remote control <remote-control>`
|
||||
for |kitty| and use :ref:`at_set-colors`.
|
||||
to set colors or you can define keyboard shortcuts to set colors, for example::
|
||||
|
||||
map f1 set_colors --configured /path/to/some/config/file/colors.conf
|
||||
|
||||
Or you can enable :doc:`remote control <remote-control>` for |kitty| and use :ref:`at_set-colors`.
|
||||
The shortcut mapping technique has the same syntax as the remote control
|
||||
command, for details, see :ref:`at_set-colors`.
|
||||
|
||||
A list of pre-made color themes for kitty is available at:
|
||||
`kitty-themes <https://github.com/dexpota/kitty-themes>`_
|
||||
|
||||
Examples of using OSC escape codes to set colors::
|
||||
|
||||
Change the default foreground color:
|
||||
printf '\x1b]10;#ff0000\x1b\\'
|
||||
Change the default background color:
|
||||
printf '\x1b]11;blue\x1b\\'
|
||||
Change the cursor color:
|
||||
printf '\x1b]12;blue\x1b\\'
|
||||
Change the selection background color:
|
||||
printf '\x1b]17;blue\x1b\\'
|
||||
Change the selection foreground color:
|
||||
printf '\x1b]19;blue\x1b\\'
|
||||
Change the nth color (0 - 255):
|
||||
printf '\x1b]4;n;green\x1b\\'
|
||||
|
||||
You can use various syntaxes/names for color specifications in the above
|
||||
examples. See `XParseColor <https://linux.die.net/man/3/xparsecolor>`_
|
||||
for full details.
|
||||
|
||||
If a ``?`` is given rather than a color specification, kitty will respond
|
||||
with the current value for the specified color.
|
||||
|
||||
|
||||
How do I specify command line options for kitty on macOS?
|
||||
---------------------------------------------------------------
|
||||
@@ -126,11 +176,39 @@ 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 spacing=100
|
||||
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
|
||||
monospace font does not have ``spacing=100`` you can override it by using the
|
||||
following :file:`~/.config/fontconfig/fonts.conf`::
|
||||
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||
<fontconfig>
|
||||
<match target="scan">
|
||||
<test name="family">
|
||||
<string>Your Font Family Name</string>
|
||||
</test>
|
||||
<edit name="spacing">
|
||||
<int>100</int>
|
||||
</edit>
|
||||
</match>
|
||||
</fontconfig>
|
||||
|
||||
After creating (or modifying) this file, you may need to run the following
|
||||
command to rebuild your fontconfig cache::
|
||||
|
||||
fc-cache -r
|
||||
|
||||
Then, the font will be available in ``kitty list-fonts``.
|
||||
|
||||
|
||||
How can I assign a single global shortcut to bring up the kitty terminal?
|
||||
@@ -140,3 +218,134 @@ Bringing up applications on a single key press is the job of the window
|
||||
manager/desktop environment. For ways to do it with kitty (or indeed any
|
||||
terminal) in different environments,
|
||||
see `here <https://github.com/kovidgoyal/kitty/issues/45>`_.
|
||||
|
||||
|
||||
How do I map key presses in kitty to different keys in the terminal program?
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
This is accomplished by using ``map`` with :sc:`send_text <send_text>` in :file:`kitty.conf`.
|
||||
For example::
|
||||
|
||||
map alt+s send_text all \x13
|
||||
|
||||
This maps :kbd:`alt+s` to :kbd:`ctrl+s`. To figure out what bytes to use for
|
||||
the :sc:`send_text <send_text>` you can use the ``showkey`` utility. Run::
|
||||
|
||||
showkey -a
|
||||
|
||||
Then press the key you want to emulate. On macOS, this utility is currently not
|
||||
available. The manual way to figure it out is:
|
||||
|
||||
1. Look up your key's decimal value in the table at the bottom of `this
|
||||
page <http://ascii-table.com/ansi-escape-sequences.php>`_ or any
|
||||
ANSI escape sequence table. There are different modifiers for :kbd:`ctrl`,
|
||||
:kbd:`alt`, etc. For e.g., for :kbd:`ctrl+s`, find the ``S`` row and look at
|
||||
the third column value, ``19``.
|
||||
|
||||
2. Convert the decimal value to hex with ``kitty +runpy "print(hex(19))"``.
|
||||
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
|
||||
------------------
|
||||
@@ -107,7 +121,8 @@ features of the graphics protocol:
|
||||
sys.stdout.flush()
|
||||
cmd.clear()
|
||||
|
||||
write_chunked({'a': 'T', 'f': 100}, open(sys.argv[-1], 'rb').read())
|
||||
with open(sys.argv[-1], 'rb') as f:
|
||||
write_chunked({'a': 'T', 'f': 100}, f.read())
|
||||
|
||||
|
||||
Save this script as :file:`png.py`, then you can use it to display any PNG
|
||||
@@ -133,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
|
||||
--------------------------
|
||||
|
||||
@@ -152,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>\
|
||||
|
||||
@@ -178,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
|
||||
@@ -253,13 +272,16 @@ 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.
|
||||
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
|
||||
received. Finally, terminals must not display anything, until the entire sequence is
|
||||
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
|
||||
@@ -285,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>\
|
||||
|
||||
@@ -308,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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -330,7 +392,22 @@ Finally, you can specify the image *z-index*, i.e. the vertical stacking order.
|
||||
placed in the same location with different z-index values will be blended if
|
||||
they are semi-transparent. You can specify z-index values using the ``z`` key.
|
||||
Negative z-index values mean that the images will be drawn under the text. This
|
||||
allows rendering of text on top of images.
|
||||
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
|
||||
---------------------
|
||||
@@ -348,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
|
||||
@@ -387,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.
|
||||
@@ -400,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
|
||||
``z`` Integer ``0`` The *z-index* vertical stacking order of the image
|
||||
``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)``.
|
||||
======= ==================== ========= =================
|
||||
|
||||
|
||||
|
||||
245
docs/index.rst
245
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
|
||||
@@ -53,18 +55,19 @@ Quickstart
|
||||
--------------
|
||||
|
||||
Pre-built binaries of |kitty| are available for both macOS and Linux.
|
||||
See the :doc:`binary install instructions </binary>`. You can also
|
||||
See the :doc:`binary install instructions </binary>`. You can
|
||||
:doc:`build from source </build>`.
|
||||
|
||||
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#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/>`_.
|
||||
|
||||
@@ -96,163 +99,33 @@ styled (curly) underlines. One of the design goals of |kitty| is to be
|
||||
easily extensible so that new features can be added in the future with
|
||||
relatively little effort.
|
||||
|
||||
Tabs and Windows
|
||||
-------------------
|
||||
|
||||
|kitty| is capable of running multiple programs organized into tabs and
|
||||
windows. The top level of organization is the *Tab*. Each tab consists
|
||||
of one or more *windows*. The windows can be arranged in multiple
|
||||
different layouts, like windows are organized in a tiling window
|
||||
manager. The keyboard controls (which are all customizable) for tabs and
|
||||
windows are:
|
||||
|
||||
Scrolling
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
======================== =======================
|
||||
Action Shortcut
|
||||
======================== =======================
|
||||
Scroll line up :sc:`scroll_line_up` (also :kbd:`⌥+⌘+⇞` and :kbd:`⌘+↑` on macOS)
|
||||
Scroll line down :sc:`scroll_line_down` (also :kbd:`⌥+⌘+⇟` and :kbd:`⌘+↓` on macOS)
|
||||
Scroll page up :sc:`scroll_page_up` (also :kbd:`⌘+⇞` on macOS)
|
||||
Scroll page down :sc:`scroll_page_down` (also :kbd:`⌘+⇟` on macOS)
|
||||
Scroll to top :sc:`scroll_home` (also :kbd:`⌘+↖` on macOS)
|
||||
Scroll to bottom :sc:`scroll_end` (also :kbd:`⌘+↘` on macOS)
|
||||
======================== =======================
|
||||
|
||||
Tabs
|
||||
~~~~~~~~~~~
|
||||
|
||||
======================== =======================
|
||||
Action Shortcut
|
||||
======================== =======================
|
||||
New tab :sc:`new_tab` (also :kbd:`⌘+t` on macOS)
|
||||
Close tab :sc:`close_tab` (also :kbd:`⌘+w` on macOS)
|
||||
Next tab :sc:`next_tab` (also :kbd:`^+⇥` and :kbd:`⇧+⌘+]` on macOS)
|
||||
Previous tab :sc:`previous_tab` (also :kbd:`⇧+^+⇥` and :kbd:`⇧+⌘+[` on macOS)
|
||||
Next layout :sc:`next_layout`
|
||||
Move tab forward :sc:`move_tab_forward`
|
||||
Move tab backward :sc:`move_tab_backward`
|
||||
Set tab title :sc:`set_tab_title` (also :kbd:`⇧+⌘+i` on macOS)
|
||||
======================== =======================
|
||||
|
||||
|
||||
Windows
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
======================== =======================
|
||||
Action Shortcut
|
||||
======================== =======================
|
||||
New window :sc:`new_window` (also :kbd:`⌘+↩` on macOS)
|
||||
New OS window :sc:`new_os_window` (also :kbd:`⌘+n` on macOS)
|
||||
Close window :sc:`close_window` (also :kbd:`⇧+⌘+d` on macOS)
|
||||
Next window :sc:`next_window`
|
||||
Previous window :sc:`previous_window`
|
||||
Move window forward :sc:`move_window_forward`
|
||||
Move window backward :sc:`move_window_backward`
|
||||
Move window to top :sc:`move_window_to_top`
|
||||
Focus specific window :sc:`first_window`, :sc:`second_window` ... :sc:`tenth_window`
|
||||
(also :kbd:`⌘+1`, :kbd:`⌘+2` ... :kbd:`⌘+9` on macOS)
|
||||
(clockwise from the top-left)
|
||||
======================== =======================
|
||||
|
||||
Additionally, you can define shortcuts in :file:`kitty.conf` to focus neighboring
|
||||
windows and move windows around (similar to window movement in vim)::
|
||||
|
||||
map ctrl+left neighboring_window left
|
||||
map shift+left move_window right
|
||||
map ctrl+down neighboring_window down
|
||||
map shift+down move_window up
|
||||
...
|
||||
|
||||
You can also define a shortcut to switch to the previously active window::
|
||||
|
||||
map ctrl+p nth_window -1
|
||||
|
||||
``nth_window`` will focus the nth window for positive numbers and the
|
||||
previously active windows for negative numbers.
|
||||
|
||||
|
||||
Other keyboard shortcuts
|
||||
----------------------------------
|
||||
|
||||
================================== =======================
|
||||
Action Shortcut
|
||||
================================== =======================
|
||||
Copy to clipboard :sc:`copy_to_clipboard` (also :kbd:`⌘+c` on macOS)
|
||||
Paste from clipboard :sc:`paste_from_clipboard` (also :kbd:`⌘+v` on macOS)
|
||||
Paste from selection :sc:`paste_from_selection`
|
||||
Increase font size :sc:`increase_font_size` (also :kbd:`⌘++` on macOS)
|
||||
Decrease font size :sc:`decrease_font_size` (also :kbd:`⌘+-` on macOS)
|
||||
Restore font size :sc:`reset_font_size` (also :kbd:`⌘+0` on macOS)
|
||||
Toggle fullscreen :sc:`toggle_fullscreen` (also :kbd:`^+⌘+f` on macOS)
|
||||
Input unicode character :sc:`input_unicode_character`
|
||||
Click URL using the keyboard :sc:`open_url`
|
||||
Reset the terminal :sc:`reset_terminal`
|
||||
Pass current selection to program :sc:`pass_selection_to_program`
|
||||
Edit |kitty| config file :sc:`edit_config_file`
|
||||
Open a |kitty| shell :sc:`kitty_shell`
|
||||
Increase background opacity :sc:`increase_background_opacity`
|
||||
Decrease background opacity :sc:`decrease_background_opacity`
|
||||
Full background opacity :sc:`full_background_opacity`
|
||||
Reset background opacity :sc:`reset_background_opacity`
|
||||
================================== =======================
|
||||
|
||||
.. include:: basic.rst
|
||||
|
||||
.. _layouts:
|
||||
|
||||
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:
|
||||
|
||||
* **Stack** -- Only a single maximized window is shown at a time
|
||||
* **Tall** -- One window is shown full height on the left, the rest of the windows are shown one below the other on the right
|
||||
* **Fat** -- One window is shown full width on the top, the rest of the windows are shown side-by-side on the bottom
|
||||
* **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`` option 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
|
||||
|
||||
This will make the tall window occupy ``70%`` 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:
|
||||
|
||||
@@ -279,13 +152,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.
|
||||
|
||||
@@ -296,8 +186,9 @@ Configuring kitty
|
||||
-------------------
|
||||
|
||||
|kitty| is highly configurable, everything from keyboard shortcuts to
|
||||
painting frames-per-second. For details and a sample :file:`kitty.conf`,
|
||||
see the :doc:`configuration docs <conf>`.
|
||||
painting frames-per-second. Press :sc:`edit_config_file` in kitty
|
||||
to open its fully commented sample config file in your text editor.
|
||||
For details see the :doc:`configuration docs <conf>`.
|
||||
|
||||
|
||||
Remote control
|
||||
@@ -329,7 +220,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
|
||||
@@ -349,11 +240,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
|
||||
-------------------
|
||||
@@ -363,9 +260,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 (see also
|
||||
:opt:`terminal_select_modifiers`).
|
||||
|
||||
|
||||
Font control
|
||||
@@ -398,11 +298,15 @@ comfortably within the pager.
|
||||
Additionally, you can pipe the contents of the scrollback buffer to an
|
||||
arbitrary, command running in a new window, tab or overlay, for example::
|
||||
|
||||
map f1 pipe @ansi window less +G -R
|
||||
map f1 launch --stdin-source=@screen_scrollback --stdin-add-formatting less +G -R
|
||||
|
||||
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.
|
||||
@@ -424,6 +328,14 @@ internal buffer named ``a`` and :kbd:`F2` to paste from that buffer. The buffer
|
||||
names are arbitrary strings, so you can define as many such buffers as you
|
||||
need.
|
||||
|
||||
Marks
|
||||
-------------
|
||||
|
||||
kitty has the ability to mark text on the screen based on regular expressions.
|
||||
This can be useful to highlight words or phrases when browsing output from long
|
||||
running programs or similar. To learn how this feature works, see :doc:`marks`.
|
||||
|
||||
|
||||
Frequently Asked Questions
|
||||
---------------------------------
|
||||
|
||||
@@ -431,6 +343,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
|
||||
---------------------------------
|
||||
|
||||
@@ -446,6 +367,14 @@ Add the following to your :file:`~/.bashrc`
|
||||
|
||||
source <(kitty + complete setup bash)
|
||||
|
||||
Older versions of bash (for example, v3.2) do not support
|
||||
process substitution with the source command, in which
|
||||
case you can try an alternative:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
source /dev/stdin <<<"$(kitty + complete setup bash)"
|
||||
|
||||
|
||||
zsh
|
||||
~~~~~~~~~
|
||||
@@ -466,7 +395,8 @@ completions happens after the call to :file:`compinit`.
|
||||
fish
|
||||
~~~~~~~~
|
||||
|
||||
Add the following to your :file:`~/.config/fish/config.fish`
|
||||
For versions of fish earlier than 3.0.0, add the following to your
|
||||
:file:`~/.config/fish/config.fish`. Later versions source completions by default.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
@@ -484,3 +414,4 @@ See :doc:`changelog`.
|
||||
|
||||
*
|
||||
kittens/*
|
||||
generated/rc
|
||||
|
||||
@@ -20,6 +20,10 @@ import tempfile
|
||||
py3 = sys.version_info[0] > 2
|
||||
is64bit = platform.architecture()[0] == '64bit'
|
||||
is_macos = 'darwin' in sys.platform.lower()
|
||||
if is_macos:
|
||||
mac_ver = tuple(map(int, platform.mac_ver()[0].split('.')))
|
||||
if mac_ver[:2] < (10, 12):
|
||||
raise SystemExit('Your version of macOS is too old, at least 10.12 is required')
|
||||
|
||||
try:
|
||||
__file__
|
||||
@@ -225,7 +229,8 @@ def script_launch():
|
||||
|
||||
def update_intaller_wrapper():
|
||||
# To run: python3 -c "import runpy; runpy.run_path('installer.py', run_name='update_wrapper')" installer.sh
|
||||
src = open(__file__, 'rb').read().decode('utf-8')
|
||||
with open(__file__, 'rb') as f:
|
||||
src = f.read().decode('utf-8')
|
||||
wrapper = sys.argv[-1]
|
||||
with open(wrapper, 'r+b') as f:
|
||||
raw = f.read().decode('utf-8')
|
||||
|
||||
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
|
||||
@@ -4,3 +4,10 @@ The kitty command line interface
|
||||
.. program:: kitty
|
||||
|
||||
.. include:: generated/cli-kitty.rst
|
||||
|
||||
.. include:: basic.rst
|
||||
|
||||
See also
|
||||
-----------
|
||||
|
||||
See kitty.conf(5)
|
||||
|
||||
@@ -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
|
||||
@@ -128,7 +134,56 @@ layout, by simply adding the line::
|
||||
boss.toggle_fullscreen()
|
||||
|
||||
|
||||
to the ``handle_result()`` function, above.
|
||||
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
|
||||
--------------------
|
||||
|
||||
The part of the kitten that runs in ``main()`` is just a normal program and
|
||||
the output of print statements will be visible in the kitten window. Or
|
||||
alternately, you can use::
|
||||
|
||||
from kittens.tui.loop import debug
|
||||
debug('whatever')
|
||||
|
||||
The ``debug()`` function is just like ``print()`` except that the output
|
||||
will appear in the ``STDOUT`` of the kitty process inside which the kitten is
|
||||
running.
|
||||
|
||||
The ``handle_result()`` part of the kitten runs inside the kitty process.
|
||||
The output of print statements will go to the ``STDOUT`` of the kitty process.
|
||||
So if you run kitty from another kitty instance, the output will be visible
|
||||
in the first kitty instance.
|
||||
|
||||
.. _external_kittens:
|
||||
|
||||
@@ -136,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.
|
||||
|
||||
@@ -18,11 +18,77 @@ select anything that looks like a path or filename and then insert it into the
|
||||
terminal, very useful for picking files from the output of a ``git`` or ``ls`` command and
|
||||
adding them to the command line for the next command.
|
||||
|
||||
You can also press :sc:`goto_file_line` to select anything that looks
|
||||
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}
|
||||
|
||||
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
|
||||
---------------------------------------------------------------
|
||||
|
||||
The hints kitten supports writing simple python scripts that can be used to
|
||||
completely customize how it finds matches and what happens when a match is
|
||||
selected. This allows the hints kitten to provide the user interface, while
|
||||
you can provide the logic for finding matches and performing actions on them.
|
||||
This is best illustrated with an example. Create the file
|
||||
:file:`custom-hints.py` in the kitty config directory with the following
|
||||
contents:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import re
|
||||
|
||||
def mark(text, args, Mark, extra_cli_args, *a):
|
||||
# This function is responsible for finding all
|
||||
# matching text. extra_cli_args are any extra arguments
|
||||
# passed on the command line when invoking the kitten.
|
||||
# We mark all individual word for potential selection
|
||||
for idx, m in enumerate(re.finditer(r'\w+', text)):
|
||||
start, end = m.span()
|
||||
mark_text = text[start:end].replace('\n', '').replace('\0', '')
|
||||
# The empty dictionary below will be available as groupdicts
|
||||
# in handle_result() and can contain arbitrary data.
|
||||
yield Mark(idx, start, end, mark_text, {})
|
||||
|
||||
|
||||
def handle_result(args, data, target_window_id, boss, extra_cli_args, *a):
|
||||
# This function is responsible for performing some
|
||||
# action on the selected text.
|
||||
# matches is a list of the selected entries and groupdicts contains
|
||||
# the arbitrary data associated with each entry in mark() above
|
||||
matches, groupdicts = [], []
|
||||
for m, g in zip(data['match'], data['groupdicts']):
|
||||
if m:
|
||||
matches.append(m), groupdicts.append(g)
|
||||
for word, match_data in zip(matches, groupdicts):
|
||||
# Lookup the word in a dictionary, the open_url function
|
||||
# will open the provided url in the system browser
|
||||
boss.open_url(f'https://www.google.com/search?q=define:{word}')
|
||||
|
||||
Now run kitty with::
|
||||
|
||||
kitty -o 'map f1 kitten hints --customize-processing custom-hints.py'
|
||||
|
||||
When you press the :kbd:`F1` key you will be able to select a word to
|
||||
look it up in the Google dictionary.
|
||||
|
||||
|
||||
Command Line Interface
|
||||
-------------------------
|
||||
|
||||
|
||||
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,7 +18,16 @@ 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
|
||||
-------------------------
|
||||
|
||||
.. include:: ../generated/cli-kitten-unicode_input.rst
|
||||
|
||||
109
docs/launch.rst
Normal file
109
docs/launch.rst
Normal file
@@ -0,0 +1,109 @@
|
||||
Launching programs in new windows/tabs
|
||||
========================================
|
||||
|
||||
.. program:: launch
|
||||
|
||||
|
||||
|kitty| has a :code:`launch` action that can be used to run arbitrary programs
|
||||
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.
|
||||
|
||||
In the simplest form, you can use it to open a new kitty window running the
|
||||
shell, as shown below::
|
||||
|
||||
map f1 launch
|
||||
|
||||
To run a different program simply pass the command line as arguments to
|
||||
launch::
|
||||
|
||||
map f1 launch vim path/to/some/file
|
||||
|
||||
|
||||
To open a new window with the same working directory as the currently
|
||||
active window::
|
||||
|
||||
map f1 launch --cwd=current
|
||||
|
||||
To open the new window in a new tab::
|
||||
|
||||
map f1 launch --type=tab
|
||||
|
||||
To pass the contents of the current screen and scrollback to the started process::
|
||||
|
||||
map f1 launch --stdin-source=@screen_scrollback less
|
||||
|
||||
There are many more powerful options, refer to the complete list below.
|
||||
|
||||
The piping environment
|
||||
--------------------------
|
||||
|
||||
When using :option:`launch --stdin-source`, the program to which the data is
|
||||
piped has a special environment variable declared, ``KITTY_PIPE_DATA`` whose
|
||||
contents are::
|
||||
|
||||
KITTY_PIPE_DATA={scrolled_by}:{cursor_x},{cursor_y}:{lines},{columns}
|
||||
|
||||
where ``scrolled_by`` is the number of lines kitty is currently scrolled by,
|
||||
``cursor_(x|y)`` is the position of the cursor on the screen with ``(1,1)``
|
||||
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
|
||||
------------------
|
||||
|
||||
.. include:: /generated/launch.rst
|
||||
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!
|
||||
125
docs/marks.rst
Normal file
125
docs/marks.rst
Normal file
@@ -0,0 +1,125 @@
|
||||
Marks
|
||||
=================
|
||||
|
||||
|
||||
kitty has the ability to mark text on the screen based on regular expressions.
|
||||
This can be useful to highlight words or phrases when browsing output from long
|
||||
running programs or similar. Lets start with a few examples:
|
||||
|
||||
Suppose we want to be able to highlight the word ERROR in the current window.
|
||||
Add the following to :file:`kitty.conf`::
|
||||
|
||||
map f1 toggle_marker text 1 ERROR
|
||||
|
||||
Now when you press :kbd:`F1` all instances of the word :code:`ERROR` will be
|
||||
highlighted. To turn off the highlighting, press :kbd:`F1` again.
|
||||
If you want to make it case-insensitive, use::
|
||||
|
||||
map f1 toggle_marker itext 1 ERROR
|
||||
|
||||
To make it match only complete words, use::
|
||||
|
||||
map f1 toggle_marker regex 1 \\bERROR\\b
|
||||
|
||||
Suppose you want to highlight both :code:`ERROR` and :code:`WARNING`, case
|
||||
insensitively::
|
||||
|
||||
map f1 toggle_marker iregex 1 \\bERROR\\b 2 \\bWARNING\\b
|
||||
|
||||
kitty supports up to 3 mark groups (the numbers in the commands above). You
|
||||
can control the colors used for these groups in :file:`kitty.conf` with::
|
||||
|
||||
mark1_foreground red
|
||||
mark1_background gray
|
||||
mark2_foreground green
|
||||
...
|
||||
|
||||
|
||||
.. note::
|
||||
For performance reasons, matching is done per line only, and only when that line is
|
||||
altered in any way. So you cannot match text that stretches across multiple
|
||||
lines.
|
||||
|
||||
|
||||
Creating markers dynamically
|
||||
---------------------------------
|
||||
|
||||
If you want to create markers dynamically rather than pre-defining them in
|
||||
:file:`kitty.conf` you can do so as follows::
|
||||
|
||||
map f1 create_marker
|
||||
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. ``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.
|
||||
|
||||
|
||||
Scrolling to marks
|
||||
--------------------
|
||||
|
||||
kitty has an action to scroll to the next line that contains a mark. You can
|
||||
use it by mapping it to some shortcut in :file:`kitty.conf`::
|
||||
|
||||
map ctrl+p scroll_to_mark prev
|
||||
map ctrl+n scroll_to_mark next
|
||||
|
||||
Then pressing :kbd:`ctrl+p` will scroll to the first line in the scrollback
|
||||
buffer above the current top line that contains a mark. Pressing :kbd:`ctrl+n`
|
||||
will scroll to show the first line below the current last line that contains
|
||||
a mark. If you wish to jump to a mark of a specific type, you can add that to
|
||||
the mapping::
|
||||
|
||||
map ctrl+1 scroll_to_mark prev 1
|
||||
|
||||
Which will scroll only to marks of type 1.
|
||||
|
||||
|
||||
The full syntax for creating marks
|
||||
-------------------------------------
|
||||
|
||||
The syntax of the :code:`toggle_marker` command is::
|
||||
|
||||
toggle_marker <marker-type> <specification>
|
||||
|
||||
Here :code:`marker-type` is one of:
|
||||
|
||||
* :code:`text` - simple substring matching
|
||||
* :code:`itext` - case-insensitive substring matching
|
||||
* :code:`regex` - A python regular expression
|
||||
* :code:`iregex` - A case-insensitive python regular expression
|
||||
* :code:`function` - An arbitrary function defined in a python file, see
|
||||
:ref:`marker_funcs`.
|
||||
|
||||
.. _marker_funcs:
|
||||
|
||||
Arbitrary marker functions
|
||||
-----------------------------
|
||||
|
||||
You can create your own marker functions. Create a python file named
|
||||
:file:`mymarker.py` and in it create a :code:`marker` function. This
|
||||
function receives the text of the line as input and must yield three numbers,
|
||||
the starting character position, the ending character position and the mark
|
||||
group (1-3). For example:
|
||||
|
||||
.. code-block::
|
||||
|
||||
def marker(text):
|
||||
# Function to highlight the letter X
|
||||
for i, ch in enumerate(text):
|
||||
if ch.lower() == 'x':
|
||||
yield i, i, 3
|
||||
|
||||
|
||||
Save this file somewhere and in :file:`kitty.conf`, use::
|
||||
|
||||
map f1 toggle_marker function /path/to/mymarker.py
|
||||
|
||||
If you save the file in the kitty config directory, you can use::
|
||||
|
||||
map f1 toggle_marker function mymarker.py
|
||||
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``
|
||||
@@ -14,12 +14,12 @@ render loop to reduce CPU usage. See :ref:`conf-kitty-performance` for details.
|
||||
See also the :opt:`sync_to_monitor` option to further decrease latency at the cost
|
||||
of some `tearing <https://en.wikipedia.org/wiki/Screen_tearing>`_ while scrolling.
|
||||
|
||||
You can generate detailed per-function performance data using
|
||||
`gperftools <https://github.com/gperftools/gperftools>`_. Build |kitty| with
|
||||
`make profile` which will create an executable called `kitty-profile`. Run
|
||||
that and perform the task you want to analyse, for example, scrolling a large
|
||||
file with `less`. After you quit, function call statistics will be printed to
|
||||
`stdout` and you can use tools like *kcachegrind* for more detailed analysis.
|
||||
You can generate detailed per-function performance data using `gperftools
|
||||
<https://github.com/gperftools/gperftools>`_. Build |kitty| with `make
|
||||
profile`. Run kitty and perform the task you want to analyse, for example,
|
||||
scrolling a large file with `less`. After you quit, function call statistics
|
||||
will be printed to `stdout` and you can use tools like *kcachegrind* for more
|
||||
detailed analysis.
|
||||
|
||||
Here are some CPU usage numbers for the task of scrolling a file continuously
|
||||
in less. The CPU usage is for the terminal process and X together and is
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
Working with the screen and history buffer contents
|
||||
======================================================
|
||||
|
||||
.. warning::
|
||||
The pipe action has been deprecated in favor of the
|
||||
:doc:`launch <launch>` action which is more powerful.
|
||||
|
||||
You can pipe the contents of the current screen and history buffer as
|
||||
:file:`STDIN` to an arbitrary program using the ``pipe`` function. The program
|
||||
@@ -43,6 +46,10 @@ You can choose where to run the pipe program:
|
||||
``tab``
|
||||
A new window in a new tab
|
||||
|
||||
``clipboard, primary``
|
||||
Copy the text directly to the clipboard. In this case the specified program
|
||||
is not run, so use some dummy program name for it.
|
||||
|
||||
``none``
|
||||
Run it in the background
|
||||
|
||||
@@ -52,6 +59,9 @@ Input placeholders
|
||||
|
||||
There are various different kinds of placeholders
|
||||
|
||||
``@selection``
|
||||
Plain text, currently selected text
|
||||
|
||||
``@text``
|
||||
Plain text, current screen + scrollback buffer
|
||||
|
||||
|
||||
@@ -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,174 @@ 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.
|
||||
|
||||
|
||||
.. _unscroll:
|
||||
|
||||
Unscrolling the screen
|
||||
-----------------------
|
||||
|
||||
This is a small extension to the `SD (Pan up) escape code
|
||||
<https://vt100.net/docs/vt510-rm/SD.html>`_ from the VT-420 terminal. The
|
||||
``SD`` escape code normally causes the text on screen to scroll down by the
|
||||
specified number of lines, with empty lines appearing at the top of the screen.
|
||||
This extension allows the new lines to be filled in from the scrollback buffer
|
||||
instead of being blank.
|
||||
|
||||
The motivation for this is that many modern shells will show completions in a
|
||||
block of lines under the cursor, this causes some of the on-screen text to be
|
||||
lost even after the completion is completed, because it has scrolled off
|
||||
screen. This escape code allows that text to be restored.
|
||||
|
||||
If the scrollback buffer is empty or there is no scrollback buffer, such as for
|
||||
the alternate screen, then the newly inserted lines must be empty, just as with
|
||||
the original ``SD`` escape code. The maximum number of lines that can be
|
||||
scrolled down is the height of the screen, i.e. the number of lines on the screen.
|
||||
If the escape code requests more than that number of lines, the terminal *must*
|
||||
clip the request to the current screen size.
|
||||
|
||||
The syntax of the escape code is identical to that of ``SD`` except that it has
|
||||
a trailing ``+`` modifier. This is legal under the `ECMA 48 standard
|
||||
<https://www.ecma-international.org/publications-and-standards/standards/ecma-48/>`_
|
||||
and unused for any other purpose as far as I can tell. So for example, to
|
||||
unscroll three lines, the escape code would be::
|
||||
|
||||
CSI 3 + T
|
||||
|
||||
See `discussion here
|
||||
<https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/30>`_.
|
||||
|
||||
.. versionadded:: 0.20.2
|
||||
|
||||
|
||||
.. _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.
|
||||
|
||||
41
docs/rc_protocol.rst
Normal file
41
docs/rc_protocol.rst
Normal file
@@ -0,0 +1,41 @@
|
||||
Documentation for the kitty remote control protocol
|
||||
======================================================
|
||||
|
||||
The kitty remote control protocol is a simple protocol that involves sending
|
||||
data to kitty in the form of JSON. Any individual command of kitty has the
|
||||
form::
|
||||
|
||||
<ESC>P@kitty-cmd<JSON object><ESC>\
|
||||
|
||||
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>,
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
@@ -126,7 +128,7 @@ If you do not want to allow all programs running in |kitty| to control it, you c
|
||||
enable remote control for only some |kitty| windows. Simply create a shortcut
|
||||
such as::
|
||||
|
||||
map ctrl+k new_window @ some_program
|
||||
map ctrl+k launch --allow-remote-control some_program
|
||||
|
||||
Then programs running in windows created with that shortcut can use ``kitty @``
|
||||
to control kitty. Note that any program with the right level of permissions can
|
||||
@@ -134,5 +136,46 @@ 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
|
||||
-----------------------------------------------
|
||||
|
||||
If you wish to develop your own client to talk to |kitty|, you
|
||||
can use the :doc:`rc_protocol`.
|
||||
|
||||
.. include:: generated/cli-kitty-at.rst
|
||||
|
||||
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)
|
||||
@@ -93,7 +106,7 @@ def generate(function_name, callback_name, report_name, keymap, command_class, i
|
||||
report_cmd = cmd_for_report(report_name, keymap, type_map, payload_allowed)
|
||||
if payload_allowed:
|
||||
payload_after_value = "case ';': state = PAYLOAD; break;"
|
||||
payload = payload = ', PAYLOAD'
|
||||
payload = ', PAYLOAD'
|
||||
parr = 'static uint8_t payload[4096];'
|
||||
payload_case = f'''
|
||||
case PAYLOAD: {{
|
||||
@@ -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()
|
||||
272
gen-wcwidth.py
272
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,15 +28,17 @@ 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)
|
||||
if os.path.exists(local):
|
||||
data = open(local, 'rb').read()
|
||||
with open(local, 'rb') as f:
|
||||
data = f.read()
|
||||
else:
|
||||
data = urlopen(url).read()
|
||||
open(local, 'wb').write(data)
|
||||
with open(local, 'wb') as f:
|
||||
f.write(data)
|
||||
for line in data.decode('utf-8').splitlines():
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#'):
|
||||
@@ -40,23 +46,25 @@ def get_data(fname, folder='UCD'):
|
||||
|
||||
|
||||
# Map of class names to set of codepoints in class
|
||||
class_maps = {}
|
||||
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))
|
||||
@@ -74,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
|
||||
@@ -87,50 +95,119 @@ def parse_ucd():
|
||||
not_assigned.discard(codepoint)
|
||||
if category.startswith('M'):
|
||||
marks.add(codepoint)
|
||||
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':
|
||||
@@ -146,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))
|
||||
@@ -157,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:
|
||||
@@ -165,25 +242,24 @@ def write_case(spec, p):
|
||||
|
||||
|
||||
@contextmanager
|
||||
def create_header(path, include_data_types=True):
|
||||
f = open(path, 'w')
|
||||
p = partial(print, file=f)
|
||||
p('// unicode data, built from the unicode standard on:', date.today())
|
||||
p('// see gen-wcwidth.py')
|
||||
if path.endswith('.h'):
|
||||
p('#pragma once')
|
||||
if include_data_types:
|
||||
p('#include "data-types.h"\n')
|
||||
p('START_ALLOW_CASE_RANGE')
|
||||
p()
|
||||
yield p
|
||||
p()
|
||||
if include_data_types:
|
||||
p('END_ALLOW_CASE_RANGE')
|
||||
f.close()
|
||||
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())
|
||||
p('// see gen-wcwidth.py')
|
||||
if path.endswith('.h'):
|
||||
p('#pragma once')
|
||||
if include_data_types:
|
||||
p('#include "data-types.h"\n')
|
||||
p('START_ALLOW_CASE_RANGE')
|
||||
p()
|
||||
yield p
|
||||
p()
|
||||
if include_data_types:
|
||||
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) {')
|
||||
@@ -193,9 +269,10 @@ def gen_emoji():
|
||||
p('\t\tdefault: return false;')
|
||||
p('\t}')
|
||||
p('\treturn false;\n}')
|
||||
p('static inline bool\nis_emoji_modifier(char_type code) {')
|
||||
|
||||
p('static inline bool\nis_symbol(char_type code) {')
|
||||
p('\tswitch(code) {')
|
||||
for spec in get_ranges(list(emoji_categories['Emoji_Modifier'])):
|
||||
for spec in get_ranges(list(all_symbols)):
|
||||
write_case(spec, p)
|
||||
p('\t\t\treturn true;')
|
||||
p('\t\tdefault: return false;')
|
||||
@@ -203,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):
|
||||
@@ -235,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:
|
||||
@@ -256,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"')
|
||||
@@ -265,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)')
|
||||
@@ -282,7 +378,11 @@ def gen_ucd():
|
||||
p('combining_type mark_for_codepoint(char_type c) {')
|
||||
rmap = codepoint_to_mark_map(p, mark_map)
|
||||
p('}\n')
|
||||
expected = int(re.search(r'^#define VS15 (\d+)', open('kitty/unicode-data.h').read(), re.M).group(1))
|
||||
with open('kitty/unicode-data.h') as f:
|
||||
unicode_data = f.read()
|
||||
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]
|
||||
@@ -291,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)}
|
||||
@@ -345,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)
|
||||
@@ -392,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)' + ' {{' '{')
|
||||
@@ -405,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)
|
||||
|
||||
@@ -423,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)
|
||||
|
||||
@@ -8,9 +8,11 @@ import shlex
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
|
||||
cmdline = (
|
||||
'glad --profile core --out-path {dest} --api gl=3.3 --generator {generator} --spec gl'
|
||||
' --extensions GL_ARB_texture_storage,GL_ARB_copy_image,GL_ARB_multisample,GL_ARB_robustness,GL_KHR_debug'
|
||||
'glad --out-path {dest} --api gl:core=3.3 '
|
||||
' --extensions GL_ARB_texture_storage,GL_ARB_copy_image,GL_ARB_multisample,GL_ARB_robustness,GL_KHR_debug '
|
||||
'c --header-only --debug'
|
||||
)
|
||||
|
||||
|
||||
@@ -23,7 +25,7 @@ def regenerate():
|
||||
clean('out')
|
||||
|
||||
subprocess.check_call(
|
||||
shlex.split(cmdline.format(dest='out', generator='c-debug'))
|
||||
shlex.split(cmdline.format(dest='out'))
|
||||
)
|
||||
|
||||
|
||||
@@ -32,33 +34,12 @@ def strip_trailing_whitespace(c):
|
||||
|
||||
|
||||
def export():
|
||||
c = open('out/src/glad.c', 'rb').read().decode('utf-8')
|
||||
functions = []
|
||||
with open('out/include/glad/gl.h', 'r', encoding='utf-8') as source:
|
||||
data = source.read()
|
||||
data = strip_trailing_whitespace(data)
|
||||
|
||||
def sub(m):
|
||||
functions.append(m.group(2))
|
||||
return m.group()
|
||||
|
||||
c = re.sub(r'^([A-Z0-9]+) glad_debug_([a-zA-Z0-9]+) = glad_debug_impl_\2;$', sub, c, flags=re.M)
|
||||
switch = ['glad_debug_{0} = glad_{0}'.format(f) for f in functions]
|
||||
c = c.replace('<glad/glad.h>', '"gl-wrapper.h"', 1)
|
||||
c = '#pragma GCC diagnostic ignored "-Wpedantic"\n' + c
|
||||
c += '''
|
||||
int
|
||||
init_glad(GLADloadproc load, int debug) {
|
||||
int ret = gladLoadGLLoader(load);
|
||||
if (ret && !debug) {
|
||||
SUB;
|
||||
}
|
||||
return ret;
|
||||
}'''.replace('SUB', ';\n '.join(switch), 1)
|
||||
open('../kitty/gl-wrapper.c', 'w').write(strip_trailing_whitespace(c))
|
||||
raw = open('out/include/glad/glad.h').read()
|
||||
raw = raw.replace('<KHR/khrplatform.h>', '"khrplatform.h"')
|
||||
raw += '\nint init_glad(GLADloadproc, int);\n'
|
||||
open('../kitty/gl-wrapper.h', 'w').write(strip_trailing_whitespace(raw))
|
||||
raw = open('out/include/KHR/khrplatform.h', 'rb').read().decode('utf-8')
|
||||
open('../kitty/khrplatform.h', 'w').write(strip_trailing_whitespace(raw))
|
||||
with open('../kitty/gl-wrapper.h', 'w', encoding='utf-8') as dest:
|
||||
dest.write(data)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
0
glfw/__init__.py
Normal file
0
glfw/__init__.py
Normal file
261
glfw/backend_utils.c
vendored
261
glfw/backend_utils.c
vendored
@@ -8,9 +8,12 @@
|
||||
#define _GNU_SOURCE
|
||||
#include "backend_utils.h"
|
||||
#include "internal.h"
|
||||
#include "memfd.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <float.h>
|
||||
#include <time.h>
|
||||
@@ -20,19 +23,6 @@
|
||||
#define ppoll pollts
|
||||
#endif
|
||||
|
||||
static inline double
|
||||
monotonic(void) {
|
||||
struct timespec ts = {0};
|
||||
#ifdef CLOCK_HIGHRES
|
||||
clock_gettime(CLOCK_HIGHRES, &ts);
|
||||
#elif CLOCK_MONOTONIC_RAW
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
|
||||
#else
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
#endif
|
||||
return (((double)ts.tv_nsec) / 1e9) + (double)ts.tv_sec;
|
||||
}
|
||||
|
||||
void
|
||||
update_fds(EventLoopData *eld) {
|
||||
for (nfds_t i = 0; i < eld->watches_count; i++) {
|
||||
@@ -107,7 +97,7 @@ update_timers(EventLoopData *eld) {
|
||||
}
|
||||
|
||||
id_type
|
||||
addTimer(EventLoopData *eld, const char *name, double interval, int enabled, bool repeats, timer_callback_func cb, void *cb_data, GLFWuserdatafreefun free) {
|
||||
addTimer(EventLoopData *eld, const char *name, monotonic_t interval, int enabled, bool repeats, timer_callback_func cb, void *cb_data, GLFWuserdatafreefun free) {
|
||||
if (eld->timers_count >= sizeof(eld->timers)/sizeof(eld->timers[0])) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Too many timers added");
|
||||
return 0;
|
||||
@@ -115,7 +105,7 @@ addTimer(EventLoopData *eld, const char *name, double interval, int enabled, boo
|
||||
Timer *t = eld->timers + eld->timers_count++;
|
||||
t->interval = interval;
|
||||
t->name = name;
|
||||
t->trigger_at = enabled ? monotonic() + interval : DBL_MAX;
|
||||
t->trigger_at = enabled ? monotonic() + interval : MONOTONIC_T_MAX;
|
||||
t->repeats = repeats;
|
||||
t->callback = cb;
|
||||
t->callback_data = cb_data;
|
||||
@@ -142,7 +132,7 @@ void
|
||||
toggleTimer(EventLoopData *eld, id_type timer_id, int enabled) {
|
||||
for (nfds_t i = 0; i < eld->timers_count; i++) {
|
||||
if (eld->timers[i].id == timer_id) {
|
||||
double trigger_at = enabled ? (monotonic() + eld->timers[i].interval) : DBL_MAX;
|
||||
monotonic_t trigger_at = enabled ? (monotonic() + eld->timers[i].interval) : MONOTONIC_T_MAX;
|
||||
if (trigger_at != eld->timers[i].trigger_at) {
|
||||
eld->timers[i].trigger_at = trigger_at;
|
||||
update_timers(eld);
|
||||
@@ -153,7 +143,7 @@ toggleTimer(EventLoopData *eld, id_type timer_id, int enabled) {
|
||||
}
|
||||
|
||||
void
|
||||
changeTimerInterval(EventLoopData *eld, id_type timer_id, double interval) {
|
||||
changeTimerInterval(EventLoopData *eld, id_type timer_id, monotonic_t interval) {
|
||||
for (nfds_t i = 0; i < eld->timers_count; i++) {
|
||||
if (eld->timers[i].id == timer_id) {
|
||||
eld->timers[i].interval = interval;
|
||||
@@ -163,22 +153,28 @@ changeTimerInterval(EventLoopData *eld, id_type timer_id, double interval) {
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
prepareForPoll(EventLoopData *eld, double timeout) {
|
||||
monotonic_t
|
||||
prepareForPoll(EventLoopData *eld, monotonic_t timeout) {
|
||||
for (nfds_t i = 0; i < eld->watches_count; i++) eld->fds[i].revents = 0;
|
||||
if (!eld->timers_count || eld->timers[0].trigger_at == DBL_MAX) return timeout;
|
||||
double now = monotonic(), next_repeat_at = eld->timers[0].trigger_at;
|
||||
if (!eld->timers_count || eld->timers[0].trigger_at == MONOTONIC_T_MAX) return timeout;
|
||||
monotonic_t now = monotonic(), next_repeat_at = eld->timers[0].trigger_at;
|
||||
if (timeout < 0 || now + timeout > next_repeat_at) {
|
||||
timeout = next_repeat_at <= now ? 0 : next_repeat_at - now;
|
||||
}
|
||||
return timeout;
|
||||
}
|
||||
|
||||
static inline struct timespec
|
||||
calc_time(monotonic_t nsec) {
|
||||
struct timespec result;
|
||||
result.tv_sec = nsec / (1000LL * 1000LL * 1000LL);
|
||||
result.tv_nsec = nsec % (1000LL * 1000LL * 1000LL);
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
pollWithTimeout(struct pollfd *fds, nfds_t nfds, double timeout) {
|
||||
const long seconds = (long) timeout;
|
||||
const long nanoseconds = (long) ((timeout - seconds) * 1e9);
|
||||
struct timespec tv = { seconds, nanoseconds };
|
||||
pollWithTimeout(struct pollfd *fds, nfds_t nfds, monotonic_t timeout) {
|
||||
struct timespec tv = calc_time(timeout);
|
||||
return ppoll(fds, nfds, &tv, NULL);
|
||||
}
|
||||
|
||||
@@ -196,10 +192,10 @@ dispatchEvents(EventLoopData *eld) {
|
||||
|
||||
unsigned
|
||||
dispatchTimers(EventLoopData *eld) {
|
||||
if (!eld->timers_count || eld->timers[0].trigger_at == DBL_MAX) return 0;
|
||||
if (!eld->timers_count || eld->timers[0].trigger_at == MONOTONIC_T_MAX) return 0;
|
||||
static struct { timer_callback_func func; id_type id; void* data; bool repeats; } dispatches[sizeof(eld->timers)/sizeof(eld->timers[0])];
|
||||
unsigned num_dispatches = 0;
|
||||
double now = monotonic();
|
||||
monotonic_t now = monotonic();
|
||||
for (nfds_t i = 0; i < eld->timers_count && eld->timers[i].trigger_at <= now; i++) {
|
||||
eld->timers[i].trigger_at = now + eld->timers[i].interval;
|
||||
dispatches[num_dispatches].func = eld->timers[i].callback;
|
||||
@@ -220,30 +216,97 @@ dispatchTimers(EventLoopData *eld) {
|
||||
}
|
||||
|
||||
static void
|
||||
drain_wakeup_fd(int fd, int events, void* data) {
|
||||
drain_wakeup_fd(int fd, EventLoopData* eld) {
|
||||
static char drain_buf[64];
|
||||
while(read(fd, drain_buf, sizeof(drain_buf)) < 0 && errno == EINTR);
|
||||
eld->wakeup_data_read = false;
|
||||
while(true) {
|
||||
ssize_t ret = read(fd, drain_buf, sizeof(drain_buf));
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
break;
|
||||
}
|
||||
if (ret > 0) { eld->wakeup_data_read = true; continue; }
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mark_wakep_fd_ready(int fd UNUSED, int events UNUSED, void *data) {
|
||||
((EventLoopData*)(data))->wakeup_fd_ready = true;
|
||||
}
|
||||
|
||||
bool
|
||||
initPollData(EventLoopData *eld, int display_fd) {
|
||||
if (!addWatch(eld, "display", display_fd, POLLIN, 1, NULL, NULL)) return false;
|
||||
#ifdef HAS_EVENT_FD
|
||||
eld->wakeupFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
if (eld->wakeupFd == -1) return false;
|
||||
const int wakeup_fd = eld->wakeupFd;
|
||||
#else
|
||||
if (pipe2(eld->wakeupFds, O_CLOEXEC | O_NONBLOCK) != 0) return false;
|
||||
const int wakeup_fd = eld->wakeupFds[0];
|
||||
#endif
|
||||
if (!addWatch(eld, "wakeup", wakeup_fd, POLLIN, 1, mark_wakep_fd_ready, eld)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
initPollData(EventLoopData *eld, int wakeup_fd, int display_fd) {
|
||||
addWatch(eld, "display", display_fd, POLLIN, 1, NULL, NULL);
|
||||
addWatch(eld, "wakeup", wakeup_fd, POLLIN, 1, drain_wakeup_fd, NULL);
|
||||
check_for_wakeup_events(EventLoopData *eld) {
|
||||
#ifdef HAS_EVENT_FD
|
||||
int fd = eld->wakeupFd;
|
||||
#else
|
||||
int fd = eld->wakeupFds[0];
|
||||
#endif
|
||||
drain_wakeup_fd(fd, eld);
|
||||
}
|
||||
|
||||
void
|
||||
wakeupEventLoop(EventLoopData *eld) {
|
||||
#ifdef HAS_EVENT_FD
|
||||
static const uint64_t value = 1;
|
||||
while (write(eld->wakeupFd, &value, sizeof value) < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
#else
|
||||
while (write(eld->wakeupFds[1], "w", 1) < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef HAS_EVENT_FD
|
||||
static inline void
|
||||
closeFds(int *fds, size_t count) {
|
||||
while(count--) {
|
||||
if (*fds > 0) {
|
||||
close(*fds);
|
||||
*fds = -1;
|
||||
}
|
||||
fds++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
finalizePollData(EventLoopData *eld) {
|
||||
#ifdef HAS_EVENT_FD
|
||||
close(eld->wakeupFd); eld->wakeupFd = -1;
|
||||
#else
|
||||
closeFds(eld->wakeupFds, arraysz(eld->wakeupFds));
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
pollForEvents(EventLoopData *eld, double timeout) {
|
||||
pollForEvents(EventLoopData *eld, monotonic_t timeout, watch_callback_func display_callback) {
|
||||
int read_ok = 0;
|
||||
timeout = prepareForPoll(eld, timeout);
|
||||
EVDBG("pollForEvents final timeout: %.3f", monotonic_t_to_s_double(timeout));
|
||||
int result;
|
||||
double end_time = monotonic() + timeout;
|
||||
monotonic_t end_time = monotonic() + timeout;
|
||||
eld->wakeup_fd_ready = false;
|
||||
|
||||
while(1) {
|
||||
if (timeout >= 0) {
|
||||
errno = 0;
|
||||
result = pollWithTimeout(eld->fds, eld->watches_count, timeout);
|
||||
int saved_errno = errno;
|
||||
if (display_callback) display_callback(result, eld->fds[0].revents && eld->watches[0].events, NULL);
|
||||
dispatchTimers(eld);
|
||||
if (result > 0) {
|
||||
dispatchEvents(eld);
|
||||
@@ -258,6 +321,7 @@ pollForEvents(EventLoopData *eld, double timeout) {
|
||||
errno = 0;
|
||||
result = poll(eld->fds, eld->watches_count, -1);
|
||||
int saved_errno = errno;
|
||||
if (display_callback) display_callback(result, eld->fds[0].revents && eld->watches[0].events, NULL);
|
||||
dispatchTimers(eld);
|
||||
if (result > 0) {
|
||||
dispatchEvents(eld);
|
||||
@@ -270,64 +334,89 @@ pollForEvents(EventLoopData *eld, double timeout) {
|
||||
return read_ok;
|
||||
}
|
||||
|
||||
void
|
||||
closeFds(int *fds, size_t count) {
|
||||
while(count--) {
|
||||
if (*fds > 0) {
|
||||
close(*fds);
|
||||
*fds = -1;
|
||||
// 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;
|
||||
}
|
||||
fds++;
|
||||
}
|
||||
|
||||
char* result = malloc(length + 1);
|
||||
memcpy(result, source, length);
|
||||
result[length] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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;
|
||||
/*
|
||||
* 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;
|
||||
|
||||
*count = 0;
|
||||
|
||||
while ((line = strtok(text, "\r\n")))
|
||||
path = getenv("XDG_RUNTIME_DIR");
|
||||
if (!path)
|
||||
{
|
||||
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++;
|
||||
}
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return paths;
|
||||
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;
|
||||
}
|
||||
|
||||
42
glfw/backend_utils.h
vendored
42
glfw/backend_utils.h
vendored
@@ -1,7 +1,7 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 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
|
||||
@@ -25,9 +25,21 @@
|
||||
//========================================================================
|
||||
|
||||
#pragma once
|
||||
#include "../kitty/monotonic.h"
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include(<sys/eventfd.h>)
|
||||
#define HAS_EVENT_FD
|
||||
#include <sys/eventfd.h>
|
||||
#endif
|
||||
#else
|
||||
#define HAS_EVENT_FD
|
||||
#include <sys/eventfd.h>
|
||||
#endif
|
||||
|
||||
typedef unsigned long long id_type;
|
||||
typedef void(*watch_callback_func)(int, int, void*);
|
||||
@@ -45,7 +57,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
id_type id;
|
||||
double interval, trigger_at;
|
||||
monotonic_t interval, trigger_at;
|
||||
timer_callback_func callback;
|
||||
void *callback_data;
|
||||
GLFWuserdatafreefun free;
|
||||
@@ -56,25 +68,33 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
struct pollfd fds[32];
|
||||
#ifdef HAS_EVENT_FD
|
||||
int wakeupFd;
|
||||
#else
|
||||
int wakeupFds[2];
|
||||
#endif
|
||||
bool wakeup_data_read, wakeup_fd_ready;
|
||||
nfds_t watches_count, timers_count;
|
||||
Watch watches[32];
|
||||
Timer timers[128];
|
||||
} EventLoopData;
|
||||
|
||||
|
||||
void check_for_wakeup_events(EventLoopData *eld);
|
||||
id_type addWatch(EventLoopData *eld, const char *name, int fd, int events, int enabled, watch_callback_func cb, void *cb_data);
|
||||
void removeWatch(EventLoopData *eld, id_type watch_id);
|
||||
void toggleWatch(EventLoopData *eld, id_type watch_id, int enabled);
|
||||
id_type addTimer(EventLoopData *eld, const char *name, double interval, int enabled, bool repeats, timer_callback_func cb, void *cb_data, GLFWuserdatafreefun free);
|
||||
id_type addTimer(EventLoopData *eld, const char *name, monotonic_t interval, int enabled, bool repeats, timer_callback_func cb, void *cb_data, GLFWuserdatafreefun free);
|
||||
void removeTimer(EventLoopData *eld, id_type timer_id);
|
||||
void removeAllTimers(EventLoopData *eld);
|
||||
void toggleTimer(EventLoopData *eld, id_type timer_id, int enabled);
|
||||
void changeTimerInterval(EventLoopData *eld, id_type timer_id, double interval);
|
||||
double prepareForPoll(EventLoopData *eld, double timeout);
|
||||
int pollWithTimeout(struct pollfd *fds, nfds_t nfds, double timeout);
|
||||
int pollForEvents(EventLoopData *eld, double timeout);
|
||||
void changeTimerInterval(EventLoopData *eld, id_type timer_id, monotonic_t interval);
|
||||
monotonic_t prepareForPoll(EventLoopData *eld, monotonic_t timeout);
|
||||
int pollWithTimeout(struct pollfd *fds, nfds_t nfds, monotonic_t timeout);
|
||||
int pollForEvents(EventLoopData *eld, monotonic_t timeout, watch_callback_func);
|
||||
unsigned dispatchTimers(EventLoopData *eld);
|
||||
void closeFds(int *fds, size_t count);
|
||||
void initPollData(EventLoopData *eld, int wakeup_fd, int display_fd);
|
||||
char** parseUriList(char* text, int* count);
|
||||
void finalizePollData(EventLoopData *eld);
|
||||
bool initPollData(EventLoopData *eld, int display_fd);
|
||||
void wakeupEventLoop(EventLoopData *eld);
|
||||
char* utf_8_strndup(const char* source, size_t max_length);
|
||||
int createAnonymousFile(off_t size);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 macOS - www.glfw.org
|
||||
// GLFW 3.4 macOS - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2009-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -23,9 +23,16 @@
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
// It is fine to use C99 in this file because it will not be built with VS
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
#include <sys/param.h> // For MAXPATHLEN
|
||||
#include <pthread.h>
|
||||
|
||||
// Needed for _NSGetProgname
|
||||
#include <crt_externs.h>
|
||||
|
||||
// Change to our application bundle's resources directory, if present
|
||||
//
|
||||
@@ -63,141 +70,114 @@ static void changeToResourcesDirectory(void)
|
||||
chdir(resourcesPath);
|
||||
}
|
||||
|
||||
// Create key code translation tables
|
||||
// Set up the menu bar (manually)
|
||||
// This is nasty, nasty stuff -- calls to undocumented semi-private APIs that
|
||||
// could go away at any moment, lots of stuff that really should be
|
||||
// localize(d|able), etc. Add a nib to save us this horror.
|
||||
//
|
||||
static void createKeyTables(void)
|
||||
static void createMenuBar(void)
|
||||
{
|
||||
int scancode;
|
||||
|
||||
memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes));
|
||||
memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes));
|
||||
|
||||
_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 (scancode = 0; scancode < 256; scancode++)
|
||||
size_t i;
|
||||
NSString* appName = nil;
|
||||
NSDictionary* bundleInfo = [[NSBundle mainBundle] infoDictionary];
|
||||
NSString* nameKeys[] =
|
||||
{
|
||||
// Store the reverse translation for faster key name lookup
|
||||
if (_glfw.ns.keycodes[scancode] >= 0)
|
||||
_glfw.ns.scancodes[_glfw.ns.keycodes[scancode]] = scancode;
|
||||
@"CFBundleDisplayName",
|
||||
@"CFBundleName",
|
||||
@"CFBundleExecutable",
|
||||
};
|
||||
|
||||
// Try to figure out what the calling application is called
|
||||
|
||||
for (i = 0; i < sizeof(nameKeys) / sizeof(nameKeys[0]); i++)
|
||||
{
|
||||
id name = bundleInfo[nameKeys[i]];
|
||||
if (name &&
|
||||
[name isKindOfClass:[NSString class]] &&
|
||||
![name isEqualToString:@""])
|
||||
{
|
||||
appName = name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!appName)
|
||||
{
|
||||
char** progname = _NSGetProgname();
|
||||
if (progname && *progname)
|
||||
appName = @(*progname);
|
||||
else
|
||||
appName = @"GLFW Application";
|
||||
}
|
||||
|
||||
NSMenu* bar = [[NSMenu alloc] init];
|
||||
[NSApp setMainMenu:bar];
|
||||
|
||||
NSMenuItem* appMenuItem =
|
||||
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||
NSMenu* appMenu = [[NSMenu alloc] init];
|
||||
[appMenuItem setSubmenu:appMenu];
|
||||
|
||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName]
|
||||
action:@selector(orderFrontStandardAboutPanel:)
|
||||
keyEquivalent:@""];
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
NSMenu* servicesMenu = [[NSMenu alloc] init];
|
||||
[NSApp setServicesMenu:servicesMenu];
|
||||
[[appMenu addItemWithTitle:@"Services"
|
||||
action:NULL
|
||||
keyEquivalent:@""] setSubmenu:servicesMenu];
|
||||
[servicesMenu release];
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName]
|
||||
action:@selector(hide:)
|
||||
keyEquivalent:@"h"];
|
||||
[[appMenu addItemWithTitle:@"Hide Others"
|
||||
action:@selector(hideOtherApplications:)
|
||||
keyEquivalent:@"h"]
|
||||
setKeyEquivalentModifierMask:NSEventModifierFlagOption | NSEventModifierFlagCommand];
|
||||
[appMenu addItemWithTitle:@"Show All"
|
||||
action:@selector(unhideAllApplications:)
|
||||
keyEquivalent:@""];
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
[appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName]
|
||||
action:@selector(terminate:)
|
||||
keyEquivalent:@"q"];
|
||||
|
||||
NSMenuItem* windowMenuItem =
|
||||
[bar addItemWithTitle:@"" action:NULL keyEquivalent:@""];
|
||||
[bar release];
|
||||
NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
|
||||
[NSApp setWindowsMenu:windowMenu];
|
||||
[windowMenuItem setSubmenu:windowMenu];
|
||||
|
||||
[windowMenu addItemWithTitle:@"Minimize"
|
||||
action:@selector(performMiniaturize:)
|
||||
keyEquivalent:@"m"];
|
||||
[windowMenu addItemWithTitle:@"Zoom"
|
||||
action:@selector(performZoom:)
|
||||
keyEquivalent:@""];
|
||||
[windowMenu addItem:[NSMenuItem separatorItem]];
|
||||
[windowMenu addItemWithTitle:@"Bring All to Front"
|
||||
action:@selector(arrangeInFront:)
|
||||
keyEquivalent:@""];
|
||||
|
||||
// TODO: Make this appear at the bottom of the menu (for consistency)
|
||||
[windowMenu addItem:[NSMenuItem separatorItem]];
|
||||
[[windowMenu addItemWithTitle:@"Enter Full Screen"
|
||||
action:@selector(toggleFullScreen:)
|
||||
keyEquivalent:@"f"]
|
||||
setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand];
|
||||
|
||||
// Prior to Snow Leopard, we need to use this oddly-named semi-private API
|
||||
// to get the application menu working properly.
|
||||
SEL setAppleMenuSelector = NSSelectorFromString(@"setAppleMenu:");
|
||||
[NSApp performSelector:setAppleMenuSelector withObject:appMenu];
|
||||
}
|
||||
|
||||
// Retrieve Unicode data for the current keyboard layout
|
||||
//
|
||||
static GLFWbool updateUnicodeDataNS(void)
|
||||
static bool updateUnicodeDataNS(void)
|
||||
{
|
||||
if (_glfw.ns.inputSource)
|
||||
{
|
||||
@@ -214,7 +194,7 @@ static GLFWbool updateUnicodeDataNS(void)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to retrieve keyboard layout input source");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
_glfw.ns.unicodeData =
|
||||
@@ -224,15 +204,15 @@ static GLFWbool updateUnicodeDataNS(void)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to retrieve keyboard layout Unicode data");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load HIToolbox.framework and the TIS symbols we need from it
|
||||
//
|
||||
static GLFWbool initializeTIS(void)
|
||||
static bool initializeTIS(void)
|
||||
{
|
||||
// This works only because Cocoa has already loaded it properly
|
||||
_glfw.ns.tis.bundle =
|
||||
@@ -241,19 +221,19 @@ static GLFWbool initializeTIS(void)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to load HIToolbox.framework");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
CFStringRef* kPropertyUnicodeKeyLayoutData =
|
||||
CFBundleGetDataPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("kTISPropertyUnicodeKeyLayoutData"));
|
||||
_glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource =
|
||||
*(void **)&_glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("TISCopyCurrentKeyboardLayoutInputSource"));
|
||||
_glfw.ns.tis.GetInputSourceProperty =
|
||||
*(void **)&_glfw.ns.tis.GetInputSourceProperty =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("TISGetInputSourceProperty"));
|
||||
_glfw.ns.tis.GetKbdType =
|
||||
*(void **)&_glfw.ns.tis.GetKbdType =
|
||||
CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle,
|
||||
CFSTR("LMGetKbdType"));
|
||||
|
||||
@@ -264,7 +244,7 @@ static GLFWbool initializeTIS(void)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to load TIS API symbols");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
_glfw.ns.tis.kPropertyUnicodeKeyLayoutData =
|
||||
@@ -273,6 +253,17 @@ static GLFWbool initializeTIS(void)
|
||||
return updateUnicodeDataNS();
|
||||
}
|
||||
|
||||
static void
|
||||
display_reconfigured(CGDirectDisplayID display UNUSED, CGDisplayChangeSummaryFlags flags, void *userInfo UNUSED)
|
||||
{
|
||||
if (flags & kCGDisplayBeginConfigurationFlag) {
|
||||
return;
|
||||
}
|
||||
if (flags & kCGDisplaySetModeFlag) {
|
||||
// GPU possibly changed
|
||||
}
|
||||
}
|
||||
|
||||
@interface GLFWHelper : NSObject
|
||||
@end
|
||||
|
||||
@@ -280,14 +271,138 @@ static GLFWbool initializeTIS(void)
|
||||
|
||||
- (void)selectedKeyboardInputSourceChanged:(NSObject* )object
|
||||
{
|
||||
(void)object;
|
||||
updateUnicodeDataNS();
|
||||
}
|
||||
|
||||
- (void)doNothing:(id)object
|
||||
{
|
||||
(void)object;
|
||||
}
|
||||
|
||||
@end // GLFWHelper
|
||||
@end // GLFWHelper
|
||||
|
||||
// Delegate for application related notifications {{{
|
||||
|
||||
@interface GLFWApplicationDelegate : NSObject <NSApplicationDelegate>
|
||||
@end
|
||||
|
||||
@implementation GLFWApplicationDelegate
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
|
||||
{
|
||||
(void)sender;
|
||||
if (_glfw.callbacks.application_close) _glfw.callbacks.application_close(0);
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
|
||||
static GLFWapplicationshouldhandlereopenfun handle_reopen_callback = NULL;
|
||||
|
||||
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)flag
|
||||
{
|
||||
(void)sender;
|
||||
if (!handle_reopen_callback) return YES;
|
||||
if (handle_reopen_callback(flag)) return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)applicationDidChangeScreenParameters:(NSNotification *) notification
|
||||
{
|
||||
(void)notification;
|
||||
_GLFWwindow* window;
|
||||
|
||||
for (window = _glfw.windowListHead; window; window = window->next)
|
||||
{
|
||||
if (window->context.client != GLFW_NO_API)
|
||||
[window->context.nsgl.object update];
|
||||
}
|
||||
|
||||
_glfwPollMonitorsNS();
|
||||
}
|
||||
|
||||
static GLFWapplicationwillfinishlaunchingfun finish_launching_callback = NULL;
|
||||
|
||||
- (void)applicationWillFinishLaunching:(NSNotification *)notification
|
||||
{
|
||||
(void)notification;
|
||||
if (_glfw.hints.init.ns.menubar)
|
||||
{
|
||||
// In case we are unbundled, make us a proper UI application
|
||||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
|
||||
// Menu bar setup must go between sharedApplication and finishLaunching
|
||||
// in order to properly emulate the behavior of NSApplicationMain
|
||||
|
||||
if ([[NSBundle mainBundle] pathForResource:@"MainMenu" ofType:@"nib"])
|
||||
{
|
||||
[[NSBundle mainBundle] loadNibNamed:@"MainMenu"
|
||||
owner:NSApp
|
||||
topLevelObjects:&_glfw.ns.nibObjects];
|
||||
}
|
||||
else
|
||||
createMenuBar();
|
||||
}
|
||||
if (finish_launching_callback)
|
||||
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();
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||
{
|
||||
(void)aNotification;
|
||||
CGDisplayRemoveReconfigurationCallback(display_reconfigured, NULL);
|
||||
}
|
||||
|
||||
- (void)applicationDidHide:(NSNotification *)notification
|
||||
{
|
||||
(void)notification;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < _glfw.monitorCount; i++)
|
||||
_glfwRestoreVideoModeNS(_glfw.monitors[i]);
|
||||
}
|
||||
|
||||
@end // GLFWApplicationDelegate
|
||||
// }}}
|
||||
|
||||
|
||||
@interface GLFWApplication : NSApplication
|
||||
- (void)tick_callback;
|
||||
@@ -307,32 +422,92 @@ static GLFWbool initializeTIS(void)
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void* _glfwLoadLocalVulkanLoaderNS(void)
|
||||
{
|
||||
CFBundleRef bundle = CFBundleGetMainBundle();
|
||||
if (!bundle)
|
||||
return NULL;
|
||||
|
||||
CFURLRef url =
|
||||
CFBundleCopyAuxiliaryExecutableURL(bundle, CFSTR("libvulkan.1.dylib"));
|
||||
if (!url)
|
||||
return NULL;
|
||||
|
||||
char path[PATH_MAX];
|
||||
void* handle = NULL;
|
||||
|
||||
if (CFURLGetFileSystemRepresentation(url, true, (UInt8*) path, sizeof(path) - 1))
|
||||
handle = _glfw_dlopen(path);
|
||||
|
||||
CFRelease(url);
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static inline bool
|
||||
is_ctrl_tab(NSEvent *event, NSEventModifierFlags modifierFlags) {
|
||||
return event.keyCode == kVK_Tab && (modifierFlags == NSEventModifierFlagControl || modifierFlags == (
|
||||
NSEventModifierFlagControl | NSEventModifierFlagShift));
|
||||
if (
|
||||
(modifierFlags == NSEventModifierFlagControl &&
|
||||
[event.charactersIgnoringModifiers isEqualToString:@"\t"]) ||
|
||||
(modifierFlags == (NSEventModifierFlagControl | NSEventModifierFlagShift) &&
|
||||
[event.charactersIgnoringModifiers isEqualToString:@"\x19"])
|
||||
) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
is_cmd_period(NSEvent *event, NSEventModifierFlags modifierFlags) {
|
||||
return event.keyCode == kVK_ANSI_Period && modifierFlags == NSEventModifierFlagCommand;
|
||||
if (modifierFlags != NSEventModifierFlagCommand) return false;
|
||||
if ([event.charactersIgnoringModifiers isEqualToString:@"."]) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback) {
|
||||
GLFWapplicationshouldhandlereopenfun previous = handle_reopen_callback;
|
||||
handle_reopen_callback = callback;
|
||||
return previous;
|
||||
}
|
||||
|
||||
GLFWAPI GLFWapplicationwillfinishlaunchingfun glfwSetApplicationWillFinishLaunching(GLFWapplicationwillfinishlaunchingfun callback) {
|
||||
GLFWapplicationwillfinishlaunchingfun previous = finish_launching_callback;
|
||||
finish_launching_callback = callback;
|
||||
return previous;
|
||||
}
|
||||
|
||||
int _glfwPlatformInit(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
_glfw.ns.helper = [[GLFWHelper alloc] init];
|
||||
|
||||
[NSThread detachNewThreadSelector:@selector(doNothing:)
|
||||
toTarget:_glfw.ns.helper
|
||||
withObject:nil];
|
||||
|
||||
if (NSApp)
|
||||
_glfw.ns.finishedLaunching = true;
|
||||
|
||||
[GLFWApplication sharedApplication];
|
||||
|
||||
_glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init];
|
||||
if (_glfw.ns.delegate == nil)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to create application delegate");
|
||||
return false;
|
||||
}
|
||||
|
||||
[NSApp setDelegate:_glfw.ns.delegate];
|
||||
|
||||
NSEvent* (^keydown_block)(NSEvent*) = ^ NSEvent* (NSEvent* event)
|
||||
{
|
||||
NSEventModifierFlags modifierFlags = [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask;
|
||||
@@ -367,44 +542,44 @@ int _glfwPlatformInit(void)
|
||||
_glfw.ns.keyDownMonitor =
|
||||
[NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskKeyDown
|
||||
handler:keydown_block];
|
||||
|
||||
if (_glfw.hints.init.ns.chdir)
|
||||
changeToResourcesDirectory();
|
||||
|
||||
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]
|
||||
addObserver:_glfw.ns.helper
|
||||
selector:@selector(selectedKeyboardInputSourceChanged:)
|
||||
name:NSTextInputContextKeyboardSelectionDidChangeNotification
|
||||
object:nil];
|
||||
|
||||
createKeyTables();
|
||||
|
||||
_glfw.ns.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
if (!_glfw.ns.eventSource)
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
|
||||
CGEventSourceSetLocalEventsSuppressionInterval(_glfw.ns.eventSource, 0.0);
|
||||
|
||||
if (!initializeTIS())
|
||||
return GLFW_FALSE;
|
||||
|
||||
_glfw.ns.displayLinks.lock = [NSLock new];
|
||||
_glfwInitTimerNS();
|
||||
_glfwInitJoysticksNS();
|
||||
return false;
|
||||
|
||||
_glfwPollMonitorsNS();
|
||||
}
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
void _glfwPlatformTerminate(void)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
if (_glfw.ns.displayLinks.lock) {
|
||||
_glfwClearDisplayLinks();
|
||||
[_glfw.ns.displayLinks.lock release];
|
||||
_glfw.ns.displayLinks.lock = nil;
|
||||
}
|
||||
_glfwClearDisplayLinks();
|
||||
|
||||
if (_glfw.ns.inputSource)
|
||||
{
|
||||
@@ -437,6 +612,7 @@ void _glfwPlatformTerminate(void)
|
||||
[_glfw.ns.helper release];
|
||||
_glfw.ns.helper = nil;
|
||||
}
|
||||
|
||||
if (_glfw.ns.keyUpMonitor)
|
||||
[NSEvent removeMonitor:_glfw.ns.keyUpMonitor];
|
||||
if (_glfw.ns.keyDownMonitor)
|
||||
@@ -445,8 +621,8 @@ void _glfwPlatformTerminate(void)
|
||||
free(_glfw.ns.clipboardString);
|
||||
|
||||
_glfwTerminateNSGL();
|
||||
_glfwTerminateJoysticksNS();
|
||||
}
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
const char* _glfwPlatformGetVersionString(void)
|
||||
@@ -461,32 +637,56 @@ const char* _glfwPlatformGetVersionString(void)
|
||||
static GLFWtickcallback tick_callback = NULL;
|
||||
static void* tick_callback_data = NULL;
|
||||
static bool tick_callback_requested = false;
|
||||
static pthread_t main_thread;
|
||||
static NSLock *tick_lock = NULL;
|
||||
|
||||
|
||||
void _glfwDispatchTickCallback() {
|
||||
if (tick_callback) {
|
||||
tick_callback_requested = false;
|
||||
tick_callback(tick_callback_data);
|
||||
if (tick_lock && tick_callback) {
|
||||
[tick_lock lock];
|
||||
while(tick_callback_requested) {
|
||||
tick_callback_requested = false;
|
||||
tick_callback(tick_callback_data);
|
||||
}
|
||||
[tick_lock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformRequestTickCallback() {
|
||||
static void
|
||||
request_tick_callback() {
|
||||
if (!tick_callback_requested) {
|
||||
tick_callback_requested = true;
|
||||
[NSApp performSelectorOnMainThread:@selector(tick_callback) withObject:nil waitUntilDone:NO];
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformPostEmptyEvent(void)
|
||||
{
|
||||
if (pthread_equal(pthread_self(), main_thread)) {
|
||||
request_tick_callback();
|
||||
} else if (tick_lock) {
|
||||
[tick_lock lock];
|
||||
request_tick_callback();
|
||||
[tick_lock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void _glfwPlatformStopMainLoop(void) {
|
||||
tick_callback = NULL;
|
||||
[NSApp stop:nil];
|
||||
_glfwPlatformPostEmptyEvent();
|
||||
_glfwCocoaPostEmptyEvent();
|
||||
}
|
||||
|
||||
void _glfwPlatformRunMainLoop(GLFWtickcallback callback, void* data) {
|
||||
main_thread = pthread_self();
|
||||
tick_callback = callback;
|
||||
tick_callback_data = data;
|
||||
tick_lock = [NSLock new];
|
||||
[NSApp run];
|
||||
[tick_lock release];
|
||||
tick_lock = NULL;
|
||||
tick_callback = NULL;
|
||||
tick_callback_data = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -494,7 +694,7 @@ typedef struct {
|
||||
NSTimer *os_timer;
|
||||
unsigned long long id;
|
||||
bool repeats;
|
||||
double interval;
|
||||
monotonic_t interval;
|
||||
GLFWuserdatafun callback;
|
||||
void *callback_data;
|
||||
GLFWuserdatafun free_callback_data;
|
||||
@@ -509,15 +709,12 @@ remove_timer_at(size_t idx) {
|
||||
Timer *t = timers + idx;
|
||||
if (t->os_timer) { [t->os_timer invalidate]; t->os_timer = NULL; }
|
||||
if (t->callback_data && t->free_callback_data) { t->free_callback_data(t->id, t->callback_data); t->callback_data = NULL; }
|
||||
num_timers--;
|
||||
if (idx < num_timers) {
|
||||
memmove(timers + idx, timers + idx + 1, sizeof(timers[0]) * (num_timers - idx));
|
||||
}
|
||||
remove_i_from_array(timers, idx, num_timers);
|
||||
}
|
||||
}
|
||||
|
||||
static void schedule_timer(Timer *t) {
|
||||
t->os_timer = [NSTimer scheduledTimerWithTimeInterval:t->interval repeats:(t->repeats ? YES: NO) block:^(NSTimer *os_timer) {
|
||||
t->os_timer = [NSTimer scheduledTimerWithTimeInterval:monotonic_t_to_s_double(t->interval) repeats:(t->repeats ? YES: NO) block:^(NSTimer *os_timer) {
|
||||
for (size_t i = 0; i < num_timers; i++) {
|
||||
if (timers[i].os_timer == os_timer) {
|
||||
timers[i].callback(timers[i].id, timers[i].callback_data);
|
||||
@@ -528,7 +725,7 @@ static void schedule_timer(Timer *t) {
|
||||
}];
|
||||
}
|
||||
|
||||
unsigned long long _glfwPlatformAddTimer(double interval, bool repeats, GLFWuserdatafun callback, void *callback_data, GLFWuserdatafun free_callback) {
|
||||
unsigned long long _glfwPlatformAddTimer(monotonic_t interval, bool repeats, GLFWuserdatafun callback, void *callback_data, GLFWuserdatafun free_callback) {
|
||||
static unsigned long long timer_counter = 0;
|
||||
if (num_timers >= sizeof(timers)/sizeof(timers[0]) - 1) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Too many timers added");
|
||||
@@ -554,7 +751,7 @@ void _glfwPlatformRemoveTimer(unsigned long long timer_id) {
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformUpdateTimer(unsigned long long timer_id, double interval, GLFWbool enabled) {
|
||||
void _glfwPlatformUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled) {
|
||||
for (size_t i = 0; i < num_timers; i++) {
|
||||
if (timers[i].id == timer_id) {
|
||||
Timer *t = timers + i;
|
||||
|
||||
8
glfw/cocoa_joystick.h
vendored
8
glfw/cocoa_joystick.h
vendored
@@ -1,7 +1,7 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 Cocoa - www.glfw.org
|
||||
// GLFW 3.4 Cocoa - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -44,7 +44,3 @@ typedef struct _GLFWjoystickNS
|
||||
CFMutableArrayRef hats;
|
||||
} _GLFWjoystickNS;
|
||||
|
||||
|
||||
void _glfwInitJoysticksNS(void);
|
||||
void _glfwTerminateJoysticksNS(void);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 Cocoa - www.glfw.org
|
||||
// GLFW 3.4 Cocoa - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2009-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2012 Torsten Walluhn <tw@mad-cad.net>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
@@ -24,6 +24,8 @@
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
// It is fine to use C99 in this file because it will not be built with VS
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@@ -75,7 +77,7 @@ static long getElementValue(_GLFWjoystick* js, _GLFWjoyelementNS* element)
|
||||
//
|
||||
static CFComparisonResult compareElements(const void* fp,
|
||||
const void* sp,
|
||||
void* user)
|
||||
void* user UNUSED)
|
||||
{
|
||||
const _GLFWjoyelementNS* fe = fp;
|
||||
const _GLFWjoyelementNS* se = sp;
|
||||
@@ -117,9 +119,9 @@ static void closeJoystick(_GLFWjoystick* js)
|
||||
|
||||
// Callback for user-initiated joystick addition
|
||||
//
|
||||
static void matchCallback(void* context,
|
||||
IOReturn result,
|
||||
void* sender,
|
||||
static void matchCallback(void* context UNUSED,
|
||||
IOReturn result UNUSED,
|
||||
void* sender UNUSED,
|
||||
IOHIDDeviceRef device)
|
||||
{
|
||||
int jid;
|
||||
@@ -231,6 +233,19 @@ static void matchCallback(void* context,
|
||||
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;
|
||||
|
||||
@@ -270,9 +285,9 @@ static void matchCallback(void* context,
|
||||
|
||||
// Callback for user-initiated joystick removal
|
||||
//
|
||||
static void removeCallback(void* context,
|
||||
IOReturn result,
|
||||
void* sender,
|
||||
static void removeCallback(void* context UNUSED,
|
||||
IOReturn result UNUSED,
|
||||
void* sender UNUSED,
|
||||
IOHIDDeviceRef device)
|
||||
{
|
||||
int jid;
|
||||
@@ -289,12 +304,10 @@ static void removeCallback(void* context,
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialize joystick interface
|
||||
//
|
||||
void _glfwInitJoysticksNS(void)
|
||||
bool _glfwPlatformInitJoysticks(void)
|
||||
{
|
||||
CFMutableArrayRef matching;
|
||||
const long usages[] =
|
||||
@@ -313,10 +326,10 @@ void _glfwInitJoysticksNS(void)
|
||||
if (!matching)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create array");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sizeof(usages) / sizeof(long); i++)
|
||||
for (size_t i = 0; i < sizeof(usages) / sizeof(long); i++)
|
||||
{
|
||||
const long page = kHIDPage_GenericDesktop;
|
||||
|
||||
@@ -368,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)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 macOS - www.glfw.org
|
||||
// GLFW 3.4 macOS - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2002-2006 Marcus Geelnard
|
||||
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -24,6 +24,8 @@
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
// It is fine to use C99 in this file because it will not be built with VS
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@@ -87,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;
|
||||
}
|
||||
|
||||
@@ -116,16 +118,16 @@ static char* getDisplayName(CGDirectDisplayID displayID)
|
||||
|
||||
// Check whether the display mode should be included in enumeration
|
||||
//
|
||||
static GLFWbool modeIsGood(CGDisplayModeRef mode)
|
||||
static bool modeIsGood(CGDisplayModeRef mode)
|
||||
{
|
||||
uint32_t flags = CGDisplayModeGetIOFlags(mode);
|
||||
|
||||
if (!(flags & kDisplayModeValidFlag) || !(flags & kDisplayModeSafeFlag))
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
if (flags & kDisplayModeInterlacedFlag)
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
if (flags & kDisplayModeStretchedFlag)
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100
|
||||
CFStringRef format = CGDisplayModeCopyPixelEncoding(mode);
|
||||
@@ -133,18 +135,18 @@ static GLFWbool modeIsGood(CGDisplayModeRef mode)
|
||||
CFStringCompare(format, CFSTR(IO32BitDirectPixels), 0))
|
||||
{
|
||||
CFRelease(format);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
CFRelease(format);
|
||||
#endif /* MAC_OS_X_VERSION_MAX_ALLOWED */
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convert Core Graphics display mode to GLFW video mode
|
||||
//
|
||||
static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode,
|
||||
CVDisplayLinkRef link)
|
||||
double fallbackRefreshRate)
|
||||
{
|
||||
GLFWvidmode result;
|
||||
result.width = (int) CGDisplayModeGetWidth(mode);
|
||||
@@ -152,11 +154,7 @@ static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode,
|
||||
result.refreshRate = (int) round(CGDisplayModeGetRefreshRate(mode));
|
||||
|
||||
if (result.refreshRate == 0)
|
||||
{
|
||||
const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link);
|
||||
if (!(time.flags & kCVTimeIsIndefinite))
|
||||
result.refreshRate = (int) (time.timeScale / (double) time.timeValue);
|
||||
}
|
||||
result.refreshRate = (int) round(fallbackRefreshRate);
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED <= 101100
|
||||
CFStringRef format = CGDisplayModeCopyPixelEncoding(mode);
|
||||
@@ -215,12 +213,12 @@ static void endFadeReservation(CGDisplayFadeReservationToken token)
|
||||
|
||||
// Finds and caches the NSScreen corresponding to the specified monitor
|
||||
//
|
||||
GLFWbool refreshMonitorScreen(_GLFWmonitor* monitor)
|
||||
static bool refreshMonitorScreen(_GLFWmonitor* monitor)
|
||||
{
|
||||
if (monitor->ns.screen)
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
|
||||
for (NSScreen* screen in [NSScreen screens])
|
||||
for (NSScreen* screen in [NSScreen screens])
|
||||
{
|
||||
NSNumber* displayID = [screen deviceDescription][@"NSScreenNumber"];
|
||||
|
||||
@@ -230,62 +228,112 @@ GLFWbool refreshMonitorScreen(_GLFWmonitor* monitor)
|
||||
if (monitor->ns.unitNumber == CGDisplayUnitNumber([displayID unsignedIntValue]))
|
||||
{
|
||||
monitor->ns.screen = screen;
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to find a screen for monitor");
|
||||
return GLFW_FALSE;
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to find a screen for monitor");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns the display refresh rate queried from the I/O registry
|
||||
//
|
||||
static double getFallbackRefreshRate(CGDirectDisplayID displayID)
|
||||
{
|
||||
double refreshRate = 60.0;
|
||||
|
||||
io_iterator_t it;
|
||||
io_service_t service;
|
||||
|
||||
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
|
||||
IOServiceMatching("IOFramebuffer"),
|
||||
&it) != 0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwClearDisplayLinks() {
|
||||
[_glfw.ns.displayLinks.lock lock];
|
||||
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
|
||||
if (_glfw.ns.displayLinks.entries[i].displayLinkStarted) {
|
||||
CVDisplayLinkStop(_glfw.ns.displayLinks.entries[i].displayLink);
|
||||
_glfw.ns.displayLinks.entries[i].displayLinkStarted = GLFW_FALSE;
|
||||
}
|
||||
if (_glfw.ns.displayLinks.entries[i].displayLink) {
|
||||
CVDisplayLinkStop(_glfw.ns.displayLinks.entries[i].displayLink);
|
||||
CVDisplayLinkRelease(_glfw.ns.displayLinks.entries[i].displayLink);
|
||||
_glfw.ns.displayLinks.entries[i].displayLink = nil;
|
||||
_glfw.ns.displayLinks.entries[i].lastRenderFrameRequestedAt = 0;
|
||||
}
|
||||
}
|
||||
_glfw.ns.displayLinks.count = 0;
|
||||
[_glfw.ns.displayLinks.lock unlock];
|
||||
}
|
||||
|
||||
static CVReturn displayLinkCallback(
|
||||
CVDisplayLinkRef displayLink,
|
||||
const CVTimeStamp* now, const CVTimeStamp* outputTime,
|
||||
CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* userInfo)
|
||||
CVDisplayLinkRef displayLink UNUSED,
|
||||
const CVTimeStamp* now UNUSED, const CVTimeStamp* outputTime UNUSED,
|
||||
CVOptionFlags flagsIn UNUSED, CVOptionFlags* flagsOut UNUSED, void* userInfo)
|
||||
{
|
||||
CGDirectDisplayID displayID = (CGDirectDisplayID)userInfo;
|
||||
[_glfw.ns.displayLinks.lock lock];
|
||||
GLFWbool notify = GLFW_FALSE;
|
||||
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
|
||||
if (_glfw.ns.displayLinks.entries[i].displayID == displayID) {
|
||||
if (_glfw.ns.displayLinks.entries[i].renderFrameRequested) {
|
||||
notify = GLFW_TRUE;
|
||||
_glfw.ns.displayLinks.entries[i].renderFrameRequested = GLFW_FALSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
[_glfw.ns.displayLinks.lock unlock];
|
||||
if (notify) {
|
||||
NSNumber *arg = [NSNumber numberWithUnsignedInt:displayID];
|
||||
[NSApp performSelectorOnMainThread:@selector(render_frame_received:) withObject:arg waitUntilDone:NO];
|
||||
[arg release];
|
||||
}
|
||||
CGDirectDisplayID displayID = (uintptr_t)userInfo;
|
||||
NSNumber *arg = [NSNumber numberWithUnsignedInt:displayID];
|
||||
[NSApp performSelectorOnMainThread:@selector(render_frame_received:) withObject:arg waitUntilDone:NO];
|
||||
[arg release];
|
||||
return kCVReturnSuccess;
|
||||
}
|
||||
|
||||
static inline void createDisplayLink(CGDirectDisplayID displayID) {
|
||||
[_glfw.ns.displayLinks.lock lock];
|
||||
if (_glfw.ns.displayLinks.count >= sizeof(_glfw.ns.displayLinks.entries)/sizeof(_glfw.ns.displayLinks.entries[0]) - 1) return;
|
||||
for (size_t i = 0; i < _glfw.ns.displayLinks.count; i++) {
|
||||
if (_glfw.ns.displayLinks.entries[i].displayID == displayID) return;
|
||||
@@ -295,26 +343,24 @@ static inline void createDisplayLink(CGDirectDisplayID displayID) {
|
||||
entry->displayID = displayID;
|
||||
CVDisplayLinkCreateWithCGDisplay(displayID, &entry->displayLink);
|
||||
CVDisplayLinkSetOutputCallback(entry->displayLink, &displayLinkCallback, (void*)(uintptr_t)displayID);
|
||||
[_glfw.ns.displayLinks.lock unlock];
|
||||
}
|
||||
|
||||
// Poll for changes in the set of connected monitors
|
||||
//
|
||||
void _glfwPollMonitorsNS(void)
|
||||
{
|
||||
uint32_t i, j, displayCount, disconnectedCount;
|
||||
CGDirectDisplayID* displays;
|
||||
_GLFWmonitor** disconnected = NULL;
|
||||
uint32_t displayCount;
|
||||
|
||||
CGGetOnlineDisplayList(0, NULL, &displayCount);
|
||||
displays = calloc(displayCount, sizeof(CGDirectDisplayID));
|
||||
CGDirectDisplayID* displays = calloc(displayCount, sizeof(CGDirectDisplayID));
|
||||
CGGetOnlineDisplayList(displayCount, displays, &displayCount);
|
||||
_glfwClearDisplayLinks();
|
||||
|
||||
for (i = 0; i < _glfw.monitorCount; i++)
|
||||
for (int i = 0; i < _glfw.monitorCount; i++)
|
||||
_glfw.monitors[i]->ns.screen = nil;
|
||||
|
||||
disconnectedCount = _glfw.monitorCount;
|
||||
_GLFWmonitor** disconnected = NULL;
|
||||
uint32_t disconnectedCount = _glfw.monitorCount;
|
||||
if (disconnectedCount)
|
||||
{
|
||||
disconnected = calloc(_glfw.monitorCount, sizeof(_GLFWmonitor*));
|
||||
@@ -323,19 +369,18 @@ void _glfwPollMonitorsNS(void)
|
||||
_glfw.monitorCount * sizeof(_GLFWmonitor*));
|
||||
}
|
||||
|
||||
for (i = 0; i < displayCount; i++)
|
||||
for (uint32_t i = 0; i < displayCount; i++)
|
||||
{
|
||||
_GLFWmonitor* monitor;
|
||||
const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]);
|
||||
|
||||
if (CGDisplayIsAsleep(displays[i]))
|
||||
continue;
|
||||
|
||||
for (j = 0; j < disconnectedCount; j++)
|
||||
// HACK: Compare unit numbers instead of display IDs to work around
|
||||
// display replacement on machines with automatic graphics
|
||||
// switching
|
||||
const uint32_t unitNumber = CGDisplayUnitNumber(displays[i]);
|
||||
|
||||
for (uint32_t j = 0; j < disconnectedCount; j++)
|
||||
{
|
||||
// HACK: Compare unit numbers instead of display IDs to work around
|
||||
// display replacement on machines with automatic graphics
|
||||
// switching
|
||||
if (disconnected[j] && disconnected[j]->ns.unitNumber == unitNumber)
|
||||
{
|
||||
disconnected[j] = NULL;
|
||||
@@ -348,17 +393,22 @@ void _glfwPollMonitorsNS(void)
|
||||
if (!name)
|
||||
name = _glfw_strdup("Unknown");
|
||||
|
||||
monitor = _glfwAllocMonitor(name, size.width, size.height);
|
||||
_GLFWmonitor* monitor = _glfwAllocMonitor(name, (int)size.width, (int)size.height);
|
||||
monitor->ns.displayID = displays[i];
|
||||
monitor->ns.unitNumber = unitNumber;
|
||||
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);
|
||||
}
|
||||
|
||||
for (i = 0; i < disconnectedCount; i++)
|
||||
for (uint32_t i = 0; i < disconnectedCount; i++)
|
||||
{
|
||||
if (disconnected[i])
|
||||
_glfwInputMonitor(disconnected[i], GLFW_DISCONNECTED, 0);
|
||||
@@ -366,36 +416,32 @@ void _glfwPollMonitorsNS(void)
|
||||
|
||||
free(disconnected);
|
||||
free(displays);
|
||||
_glfwRestartDisplayLinks();
|
||||
}
|
||||
|
||||
// Change the current video mode
|
||||
//
|
||||
void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)
|
||||
{
|
||||
CFArrayRef modes;
|
||||
CFIndex count, i;
|
||||
CVDisplayLinkRef link;
|
||||
CGDisplayModeRef native = NULL;
|
||||
GLFWvidmode current;
|
||||
const GLFWvidmode* best;
|
||||
|
||||
best = _glfwChooseVideoMode(monitor, desired);
|
||||
_glfwPlatformGetVideoMode(monitor, ¤t);
|
||||
|
||||
const GLFWvidmode* best = _glfwChooseVideoMode(monitor, desired);
|
||||
if (_glfwCompareVideoModes(¤t, best) == 0)
|
||||
return;
|
||||
|
||||
CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link);
|
||||
CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
|
||||
const CFIndex count = CFArrayGetCount(modes);
|
||||
CGDisplayModeRef native = NULL;
|
||||
|
||||
modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
|
||||
count = CFArrayGetCount(modes);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
for (CFIndex i = 0; i < count; i++)
|
||||
{
|
||||
CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
|
||||
if (!modeIsGood(dm))
|
||||
continue;
|
||||
|
||||
const GLFWvidmode mode = vidmodeFromCGDisplayMode(dm, link);
|
||||
const GLFWvidmode mode =
|
||||
vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate);
|
||||
if (_glfwCompareVideoModes(best, &mode) == 0)
|
||||
{
|
||||
native = dm;
|
||||
@@ -414,7 +460,6 @@ void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired)
|
||||
}
|
||||
|
||||
CFRelease(modes);
|
||||
CVDisplayLinkRelease(link);
|
||||
}
|
||||
|
||||
// Restore the previously saved (original) video mode
|
||||
@@ -438,7 +483,7 @@ void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor)
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor)
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor UNUSED)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -467,7 +512,9 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
|
||||
*yscale = (float) (pixels.size.height / points.size.height);
|
||||
}
|
||||
|
||||
void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int *width, int *height)
|
||||
void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor,
|
||||
int* xpos, int* ypos,
|
||||
int* width, int* height)
|
||||
{
|
||||
if (!refreshMonitorScreen(monitor))
|
||||
return;
|
||||
@@ -475,40 +522,33 @@ void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos
|
||||
const NSRect frameRect = [monitor->ns.screen visibleFrame];
|
||||
|
||||
if (xpos)
|
||||
*xpos = frameRect.origin.x;
|
||||
*xpos = (int)frameRect.origin.x;
|
||||
if (ypos)
|
||||
*ypos = _glfwTransformYNS(frameRect.origin.y + frameRect.size.height - 1);
|
||||
*ypos = (int)_glfwTransformYNS(frameRect.origin.y + frameRect.size.height - 1);
|
||||
if (width)
|
||||
*width = frameRect.size.width;
|
||||
*width = (int)frameRect.size.width;
|
||||
if (height)
|
||||
*height = frameRect.size.height;
|
||||
|
||||
*height = (int)frameRect.size.height;
|
||||
}
|
||||
|
||||
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
|
||||
{
|
||||
CFArrayRef modes;
|
||||
CFIndex found, i, j;
|
||||
GLFWvidmode* result;
|
||||
CVDisplayLinkRef link;
|
||||
|
||||
*count = 0;
|
||||
|
||||
CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link);
|
||||
CFArrayRef modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
|
||||
const CFIndex found = CFArrayGetCount(modes);
|
||||
GLFWvidmode* result = calloc(found, sizeof(GLFWvidmode));
|
||||
|
||||
modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL);
|
||||
found = CFArrayGetCount(modes);
|
||||
result = calloc(found, sizeof(GLFWvidmode));
|
||||
|
||||
for (i = 0; i < found; i++)
|
||||
for (CFIndex i = 0; i < found; i++)
|
||||
{
|
||||
CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i);
|
||||
if (!modeIsGood(dm))
|
||||
continue;
|
||||
|
||||
const GLFWvidmode mode = vidmodeFromCGDisplayMode(dm, link);
|
||||
const GLFWvidmode mode =
|
||||
vidmodeFromCGDisplayMode(dm, monitor->ns.fallbackRefreshRate);
|
||||
|
||||
for (j = 0; j < *count; j++)
|
||||
for (CFIndex j = 0; j < *count; j++)
|
||||
{
|
||||
if (_glfwCompareVideoModes(result + j, &mode) == 0)
|
||||
break;
|
||||
@@ -523,27 +563,19 @@ GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count)
|
||||
}
|
||||
|
||||
CFRelease(modes);
|
||||
CVDisplayLinkRelease(link);
|
||||
return result;
|
||||
}
|
||||
|
||||
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode)
|
||||
{
|
||||
CGDisplayModeRef displayMode;
|
||||
CVDisplayLinkRef link;
|
||||
|
||||
CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link);
|
||||
|
||||
displayMode = CGDisplayCopyDisplayMode(monitor->ns.displayID);
|
||||
*mode = vidmodeFromCGDisplayMode(displayMode, link);
|
||||
CGDisplayModeRelease(displayMode);
|
||||
|
||||
CVDisplayLinkRelease(link);
|
||||
CGDisplayModeRef native = CGDisplayCopyDisplayMode(monitor->ns.displayID);
|
||||
*mode = vidmodeFromCGDisplayMode(native, monitor->ns.fallbackRefreshRate);
|
||||
CGDisplayModeRelease(native);
|
||||
}
|
||||
|
||||
GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
{
|
||||
uint32_t i, size = CGDisplayGammaTableCapacity(monitor->ns.displayID);
|
||||
uint32_t size = CGDisplayGammaTableCapacity(monitor->ns.displayID);
|
||||
CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue));
|
||||
|
||||
CGGetDisplayTransferByTable(monitor->ns.displayID,
|
||||
@@ -555,7 +587,7 @@ GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
|
||||
_glfwAllocGammaArrays(ramp, size);
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
for (uint32_t i = 0; i < size; i++)
|
||||
{
|
||||
ramp->red[i] = (unsigned short) (values[i] * 65535);
|
||||
ramp->green[i] = (unsigned short) (values[i + size] * 65535);
|
||||
@@ -563,15 +595,14 @@ GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp)
|
||||
}
|
||||
|
||||
free(values);
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp)
|
||||
{
|
||||
int i;
|
||||
CGGammaValue* values = calloc(ramp->size * 3, sizeof(CGGammaValue));
|
||||
|
||||
for (i = 0; i < ramp->size; i++)
|
||||
for (unsigned int i = 0; i < ramp->size; i++)
|
||||
{
|
||||
values[i] = ramp->red[i] / 65535.f;
|
||||
values[i + ramp->size] = ramp->green[i] / 65535.f;
|
||||
|
||||
63
glfw/cocoa_platform.h
vendored
63
glfw/cocoa_platform.h
vendored
@@ -1,7 +1,7 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 macOS - www.glfw.org
|
||||
// GLFW 3.4 macOS - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2009-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -36,6 +36,9 @@ typedef void* id;
|
||||
typedef void* CVDisplayLinkRef;
|
||||
#endif
|
||||
|
||||
// NOTE: Many Cocoa enum values have been renamed and we need to build across
|
||||
// SDK versions where one is unavailable or the other deprecated
|
||||
// We use the newer names in code and these macros to handle compatibility
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200
|
||||
#define NSBitmapFormatAlphaNonpremultiplied NSAlphaNonpremultipliedBitmapFormat
|
||||
#define NSEventMaskAny NSAnyEventMask
|
||||
@@ -62,12 +65,16 @@ typedef void* CVDisplayLinkRef;
|
||||
#endif
|
||||
|
||||
|
||||
typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
|
||||
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int, unsigned long);
|
||||
typedef int (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef int (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef bool (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef bool (* GLFWhandlefileopen)(const char*);
|
||||
typedef void (* GLFWapplicationwillfinishlaunchingfun)(void);
|
||||
typedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
|
||||
|
||||
typedef VkFlags VkMacOSSurfaceCreateFlagsMVK;
|
||||
typedef VkFlags VkMetalSurfaceCreateFlagsEXT;
|
||||
|
||||
typedef struct VkMacOSSurfaceCreateInfoMVK
|
||||
{
|
||||
VkStructureType sType;
|
||||
@@ -76,21 +83,25 @@ typedef struct VkMacOSSurfaceCreateInfoMVK
|
||||
const void* pView;
|
||||
} VkMacOSSurfaceCreateInfoMVK;
|
||||
|
||||
typedef struct VkMetalSurfaceCreateInfoEXT
|
||||
{
|
||||
VkStructureType sType;
|
||||
const void* pNext;
|
||||
VkMetalSurfaceCreateFlagsEXT flags;
|
||||
const void* pLayer;
|
||||
} VkMetalSurfaceCreateInfoEXT;
|
||||
|
||||
typedef VkResult (APIENTRY *PFN_vkCreateMacOSSurfaceMVK)(VkInstance,const VkMacOSSurfaceCreateInfoMVK*,const VkAllocationCallbacks*,VkSurfaceKHR*);
|
||||
typedef VkResult (APIENTRY *PFN_vkCreateMetalSurfaceEXT)(VkInstance,const VkMetalSurfaceCreateInfoEXT*,const VkAllocationCallbacks*,VkSurfaceKHR*);
|
||||
|
||||
#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
|
||||
@@ -116,8 +127,11 @@ typedef struct _GLFWwindowNS
|
||||
id view;
|
||||
id layer;
|
||||
|
||||
GLFWbool maximized;
|
||||
GLFWbool retina;
|
||||
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;
|
||||
@@ -136,7 +150,7 @@ typedef struct _GLFWwindowNS
|
||||
// Dead key state
|
||||
UInt32 deadKeyState;
|
||||
// Whether a render frame has been requested for this window
|
||||
GLFWbool renderFrameRequested;
|
||||
bool renderFrameRequested;
|
||||
GLFWcocoarenderframefun renderFrameCallback;
|
||||
} _GLFWwindowNS;
|
||||
|
||||
@@ -144,8 +158,7 @@ typedef struct _GLFWDisplayLinkNS
|
||||
{
|
||||
CVDisplayLinkRef displayLink;
|
||||
CGDirectDisplayID displayID;
|
||||
GLFWbool displayLinkStarted;
|
||||
GLFWbool renderFrameRequested;
|
||||
monotonic_t lastRenderFrameRequestedAt;
|
||||
} _GLFWDisplayLinkNS;
|
||||
|
||||
// Cocoa-specific global data
|
||||
@@ -154,18 +167,18 @@ typedef struct _GLFWlibraryNS
|
||||
{
|
||||
CGEventSourceRef eventSource;
|
||||
id delegate;
|
||||
GLFWbool cursorHidden;
|
||||
bool finishedLaunching;
|
||||
bool cursorHidden;
|
||||
TISInputSourceRef inputSource;
|
||||
IOHIDManagerRef hidManager;
|
||||
id unicodeData;
|
||||
id helper;
|
||||
id keyUpMonitor;
|
||||
id keyDownMonitor;
|
||||
id nibObjects;
|
||||
|
||||
char keyName[64];
|
||||
char text[256];
|
||||
short int keycodes[256];
|
||||
short int scancodes[GLFW_KEY_LAST + 1];
|
||||
char* clipboardString;
|
||||
CGPoint cascadePoint;
|
||||
// Where to place the cursor when re-enabled
|
||||
@@ -184,8 +197,9 @@ typedef struct _GLFWlibraryNS
|
||||
struct {
|
||||
_GLFWDisplayLinkNS entries[256];
|
||||
size_t count;
|
||||
id lock;
|
||||
} displayLinks;
|
||||
// the callback to handle file open events
|
||||
GLFWhandlefileopen file_open_callback;
|
||||
|
||||
} _GLFWlibraryNS;
|
||||
|
||||
@@ -197,6 +211,7 @@ typedef struct _GLFWmonitorNS
|
||||
CGDisplayModeRef previousMode;
|
||||
uint32_t unitNumber;
|
||||
id screen;
|
||||
double fallbackRefreshRate;
|
||||
|
||||
} _GLFWmonitorNS;
|
||||
|
||||
@@ -217,13 +232,17 @@ typedef struct _GLFWtimerNS
|
||||
} _GLFWtimerNS;
|
||||
|
||||
|
||||
void _glfwInitTimerNS(void);
|
||||
|
||||
void _glfwPollMonitorsNS(void);
|
||||
void _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired);
|
||||
void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor);
|
||||
|
||||
float _glfwTransformYNS(float y);
|
||||
|
||||
void* _glfwLoadLocalVulkanLoaderNS(void);
|
||||
|
||||
void _glfwClearDisplayLinks(void);
|
||||
void _glfwCocoaPostEmptyEvent(short subtype, long data1, bool at_start);
|
||||
void _glfwRestartDisplayLinks(void);
|
||||
void _glfwDispatchTickCallback(void);
|
||||
void _glfwDispatchRenderFrame(CGDirectDisplayID);
|
||||
void _glfwShutdownCVDisplayLink(unsigned long long, void*);
|
||||
void _glfwCocoaPostEmptyEvent(void);
|
||||
|
||||
60
glfw/cocoa_time.c
vendored
60
glfw/cocoa_time.c
vendored
@@ -1,60 +0,0 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 macOS - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2009-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would
|
||||
// be appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not
|
||||
// be misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialise timer
|
||||
//
|
||||
void _glfwInitTimerNS(void)
|
||||
{
|
||||
mach_timebase_info_data_t info;
|
||||
mach_timebase_info(&info);
|
||||
|
||||
_glfw.timer.ns.frequency = (info.denom * 1e9) / info.numer;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW platform API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint64_t _glfwPlatformGetTimerValue(void)
|
||||
{
|
||||
return mach_absolute_time();
|
||||
}
|
||||
|
||||
uint64_t _glfwPlatformGetTimerFrequency(void)
|
||||
{
|
||||
return _glfw.timer.ns.frequency;
|
||||
}
|
||||
|
||||
1713
glfw/cocoa_window.m
1713
glfw/cocoa_window.m
File diff suppressed because it is too large
Load Diff
238
glfw/context.c
vendored
238
glfw/context.c
vendored
@@ -1,5 +1,5 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 - www.glfw.org
|
||||
// GLFW 3.4 - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2002-2006 Marcus Geelnard
|
||||
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
@@ -24,6 +24,8 @@
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
// Please use C89 style variable declarations in this file because VS 2010
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@@ -44,7 +46,7 @@
|
||||
// exists and whether all relevant options have supported and non-conflicting
|
||||
// values
|
||||
//
|
||||
GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
|
||||
bool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
|
||||
{
|
||||
if (ctxconfig->share)
|
||||
{
|
||||
@@ -52,7 +54,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
|
||||
ctxconfig->share->context.client == GLFW_NO_API)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +65,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
|
||||
_glfwInputError(GLFW_INVALID_ENUM,
|
||||
"Invalid context creation API 0x%08X",
|
||||
ctxconfig->source);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctxconfig->client != GLFW_NO_API &&
|
||||
@@ -73,7 +75,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
|
||||
_glfwInputError(GLFW_INVALID_ENUM,
|
||||
"Invalid client API 0x%08X",
|
||||
ctxconfig->client);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctxconfig->client == GLFW_OPENGL_API)
|
||||
@@ -92,7 +94,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
|
||||
_glfwInputError(GLFW_INVALID_VALUE,
|
||||
"Invalid OpenGL version %i.%i",
|
||||
ctxconfig->major, ctxconfig->minor);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctxconfig->profile)
|
||||
@@ -103,7 +105,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
|
||||
_glfwInputError(GLFW_INVALID_ENUM,
|
||||
"Invalid OpenGL profile 0x%08X",
|
||||
ctxconfig->profile);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctxconfig->major <= 2 ||
|
||||
@@ -114,7 +116,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
|
||||
|
||||
_glfwInputError(GLFW_INVALID_VALUE,
|
||||
"Context profiles are only defined for OpenGL version 3.2 and above");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +125,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
|
||||
// Forward-compatible contexts are only defined for OpenGL version 3.0 and above
|
||||
_glfwInputError(GLFW_INVALID_VALUE,
|
||||
"Forward-compatibility is only defined for OpenGL version 3.0 and above");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (ctxconfig->client == GLFW_OPENGL_ES_API)
|
||||
@@ -140,7 +142,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
|
||||
_glfwInputError(GLFW_INVALID_VALUE,
|
||||
"Invalid OpenGL ES version %i.%i",
|
||||
ctxconfig->major, ctxconfig->minor);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +154,7 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
|
||||
_glfwInputError(GLFW_INVALID_ENUM,
|
||||
"Invalid context robustness mode 0x%08X",
|
||||
ctxconfig->robustness);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,184 +166,16 @@ GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig)
|
||||
_glfwInputError(GLFW_INVALID_ENUM,
|
||||
"Invalid context release behavior 0x%08X",
|
||||
ctxconfig->release);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return GLFW_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;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Retrieves the attributes of the current context
|
||||
//
|
||||
GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
bool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig)
|
||||
{
|
||||
int i;
|
||||
@@ -369,7 +203,7 @@ GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken");
|
||||
glfwMakeContextCurrent((GLFWwindow*) previous);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
version = (const char*) window->context.GetString(GL_VERSION);
|
||||
@@ -387,7 +221,7 @@ GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
}
|
||||
|
||||
glfwMakeContextCurrent((GLFWwindow*) previous);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; prefixes[i]; i++)
|
||||
@@ -419,7 +253,7 @@ GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
}
|
||||
|
||||
glfwMakeContextCurrent((GLFWwindow*) previous);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (window->context.major < ctxconfig->major ||
|
||||
@@ -449,7 +283,7 @@ GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
}
|
||||
|
||||
glfwMakeContextCurrent((GLFWwindow*) previous);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (window->context.major >= 3)
|
||||
@@ -465,7 +299,7 @@ GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Entry point retrieval is broken");
|
||||
glfwMakeContextCurrent((GLFWwindow*) previous);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,21 +312,21 @@ GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
window->context.GetIntegerv(GL_CONTEXT_FLAGS, &flags);
|
||||
|
||||
if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT)
|
||||
window->context.forward = GLFW_TRUE;
|
||||
window->context.forward = true;
|
||||
|
||||
if (flags & GL_CONTEXT_FLAG_DEBUG_BIT)
|
||||
window->context.debug = GLFW_TRUE;
|
||||
window->context.debug = true;
|
||||
else if (glfwExtensionSupported("GL_ARB_debug_output") &&
|
||||
ctxconfig->debug)
|
||||
{
|
||||
// HACK: This is a workaround for older drivers (pre KHR_debug)
|
||||
// not setting the debug bit in the context flags for
|
||||
// debug contexts
|
||||
window->context.debug = GLFW_TRUE;
|
||||
window->context.debug = true;
|
||||
}
|
||||
|
||||
if (flags & GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR)
|
||||
window->context.noerror = GLFW_TRUE;
|
||||
window->context.noerror = true;
|
||||
}
|
||||
|
||||
// Read back OpenGL context profile (OpenGL 3.2 and above)
|
||||
@@ -572,12 +406,12 @@ GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
}
|
||||
|
||||
glfwMakeContextCurrent((GLFWwindow*) previous);
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Searches an extension string for the specified extension
|
||||
//
|
||||
GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions)
|
||||
bool _glfwStringInExtensionString(const char* string, const char* extensions)
|
||||
{
|
||||
const char* start = extensions;
|
||||
|
||||
@@ -588,7 +422,7 @@ GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions
|
||||
|
||||
where = strstr(start, string);
|
||||
if (!where)
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
|
||||
terminator = where + strlen(string);
|
||||
if (where == start || *(where - 1) == ' ')
|
||||
@@ -600,7 +434,7 @@ GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions
|
||||
start = terminator;
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -677,20 +511,20 @@ GLFWAPI int glfwExtensionSupported(const char* extension)
|
||||
_GLFWwindow* window;
|
||||
assert(extension != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(false);
|
||||
|
||||
window = _glfwPlatformGetTls(&_glfw.contextSlot);
|
||||
if (!window)
|
||||
{
|
||||
_glfwInputError(GLFW_NO_CURRENT_CONTEXT,
|
||||
"Cannot query extension without a current OpenGL or OpenGL ES context");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*extension == '\0')
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_VALUE, "Extension name cannot be an empty string");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (window->context.major >= 3)
|
||||
@@ -710,11 +544,11 @@ GLFWAPI int glfwExtensionSupported(const char* extension)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Extension string retrieval is broken");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(en, extension) == 0)
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -727,11 +561,11 @@ GLFWAPI int glfwExtensionSupported(const char* extension)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Extension string retrieval is broken");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_glfwStringInExtensionString(extension, extensions))
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if extension is in the platform-specific string
|
||||
|
||||
55
glfw/dbus_glfw.c
vendored
55
glfw/dbus_glfw.c
vendored
@@ -1,5 +1,5 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 XKB - www.glfw.org
|
||||
// GLFW 3.4 XKB - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2018 Kovid Goyal <kovid@kovidgoyal.net>
|
||||
//
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "internal.h"
|
||||
#include "dbus_glfw.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -45,15 +46,15 @@ report_error(DBusError *err, const char *fmt, ...) {
|
||||
static _GLFWDBUSData *dbus_data = NULL;
|
||||
static DBusConnection *session_bus = NULL;
|
||||
|
||||
GLFWbool
|
||||
bool
|
||||
glfw_dbus_init(_GLFWDBUSData *dbus, EventLoopData *eld) {
|
||||
dbus->eld = eld;
|
||||
dbus_data = dbus;
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
on_dbus_watch_ready(int fd, int events, void *data) {
|
||||
on_dbus_watch_ready(int fd UNUSED, int events, void *data) {
|
||||
DBusWatch *watch = (DBusWatch*)data;
|
||||
unsigned int flags = 0;
|
||||
if (events & POLLERR) flags |= DBUS_WATCH_ERROR;
|
||||
@@ -84,19 +85,19 @@ add_dbus_watch(DBusWatch *watch, void *data) {
|
||||
}
|
||||
|
||||
static void
|
||||
remove_dbus_watch(DBusWatch *watch, void *data) {
|
||||
remove_dbus_watch(DBusWatch *watch, void *data UNUSED) {
|
||||
id_type *idp = dbus_watch_get_data(watch);
|
||||
if (idp) removeWatch(dbus_data->eld, *idp);
|
||||
}
|
||||
|
||||
static void
|
||||
toggle_dbus_watch(DBusWatch *watch, void *data) {
|
||||
toggle_dbus_watch(DBusWatch *watch, void *data UNUSED) {
|
||||
id_type *idp = dbus_watch_get_data(watch);
|
||||
if (idp) toggleWatch(dbus_data->eld, *idp, dbus_watch_get_enabled(watch));
|
||||
}
|
||||
|
||||
static void
|
||||
on_dbus_timer_ready(id_type timer_id, void *data) {
|
||||
on_dbus_timer_ready(id_type timer_id UNUSED, void *data) {
|
||||
if (data) {
|
||||
DBusTimeout *t = (DBusTimeout*)data;
|
||||
dbus_timeout_handle(t);
|
||||
@@ -107,7 +108,7 @@ on_dbus_timer_ready(id_type timer_id, void *data) {
|
||||
static dbus_bool_t
|
||||
add_dbus_timeout(DBusTimeout *timeout, void *data) {
|
||||
int enabled = dbus_timeout_get_enabled(timeout) ? 1 : 0;
|
||||
double interval = ((double)dbus_timeout_get_interval(timeout)) / 1000.0;
|
||||
monotonic_t interval = ms_to_monotonic_t(dbus_timeout_get_interval(timeout));
|
||||
if (interval < 0) return FALSE;
|
||||
id_type timer_id = addTimer(dbus_data->eld, data, interval, enabled, true, on_dbus_timer_ready, timeout, NULL);
|
||||
if (!timer_id) return FALSE;
|
||||
@@ -123,20 +124,20 @@ add_dbus_timeout(DBusTimeout *timeout, void *data) {
|
||||
}
|
||||
|
||||
static void
|
||||
remove_dbus_timeout(DBusTimeout *timeout, void *data) {
|
||||
remove_dbus_timeout(DBusTimeout *timeout, void *data UNUSED) {
|
||||
id_type *idp = dbus_timeout_get_data(timeout);
|
||||
if (idp) removeTimer(dbus_data->eld, *idp);
|
||||
}
|
||||
|
||||
static void
|
||||
toggle_dbus_timeout(DBusTimeout *timeout, void *data) {
|
||||
toggle_dbus_timeout(DBusTimeout *timeout, void *data UNUSED) {
|
||||
id_type *idp = dbus_timeout_get_data(timeout);
|
||||
if (idp) toggleTimer(dbus_data->eld, *idp, dbus_timeout_get_enabled(timeout));
|
||||
}
|
||||
|
||||
|
||||
DBusConnection*
|
||||
glfw_dbus_connect_to(const char *path, const char* err_msg, const char *name, GLFWbool register_on_bus) {
|
||||
glfw_dbus_connect_to(const char *path, const char* err_msg, const char *name, bool register_on_bus) {
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
DBusConnection *ans = dbus_connection_open_private(path, &err);
|
||||
@@ -178,7 +179,7 @@ glfw_dbus_session_bus_dispatch() {
|
||||
}
|
||||
|
||||
void
|
||||
glfw_dbus_terminate(_GLFWDBUSData *dbus) {
|
||||
glfw_dbus_terminate(_GLFWDBUSData *dbus UNUSED) {
|
||||
if (dbus_data) {
|
||||
dbus_data->eld = NULL;
|
||||
dbus_data = NULL;
|
||||
@@ -195,14 +196,14 @@ glfw_dbus_close_connection(DBusConnection *conn) {
|
||||
dbus_connection_unref(conn);
|
||||
}
|
||||
|
||||
GLFWbool
|
||||
bool
|
||||
glfw_dbus_get_args(DBusMessage *msg, const char *failmsg, ...) {
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
va_list ap;
|
||||
va_start(ap, failmsg);
|
||||
int firstarg = va_arg(ap, int);
|
||||
GLFWbool ret = dbus_message_get_args_valist(msg, &err, firstarg, ap) ? GLFW_TRUE : GLFW_FALSE;
|
||||
bool ret = dbus_message_get_args_valist(msg, &err, firstarg, ap) ? true : false;
|
||||
va_end(ap);
|
||||
if (!ret) report_error(&err, failmsg);
|
||||
return ret;
|
||||
@@ -233,25 +234,25 @@ method_reply_received(DBusPendingCall *pending, void *user_data) {
|
||||
}
|
||||
}
|
||||
|
||||
GLFWbool
|
||||
bool
|
||||
call_method_with_msg(DBusConnection *conn, DBusMessage *msg, int timeout, dbus_pending_callback callback, void *user_data) {
|
||||
GLFWbool retval = GLFW_FALSE;
|
||||
bool retval = false;
|
||||
#define REPORT(errs) _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to call DBUS method: node=%s path=%s interface=%s method=%s, with error: %s", dbus_message_get_destination(msg), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), errs)
|
||||
if (callback) {
|
||||
DBusPendingCall *pending = NULL;
|
||||
if (dbus_connection_send_with_reply(conn, msg, &pending, timeout)) {
|
||||
MethodResponse *res = malloc(sizeof(MethodResponse));
|
||||
if (!res) return GLFW_FALSE;
|
||||
if (!res) return false;
|
||||
res->callback = callback;
|
||||
res->user_data = user_data;
|
||||
dbus_pending_call_set_notify(pending, method_reply_received, res, free);
|
||||
retval = GLFW_TRUE;
|
||||
retval = true;
|
||||
} else {
|
||||
REPORT("out of memory");
|
||||
}
|
||||
} else {
|
||||
if (dbus_connection_send(conn, msg, NULL)) {
|
||||
retval = GLFW_TRUE;
|
||||
retval = true;
|
||||
} else {
|
||||
REPORT("out of memory");
|
||||
}
|
||||
@@ -260,12 +261,12 @@ call_method_with_msg(DBusConnection *conn, DBusMessage *msg, int timeout, dbus_p
|
||||
#undef REPORT
|
||||
}
|
||||
|
||||
static GLFWbool
|
||||
static bool
|
||||
call_method(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, int timeout, dbus_pending_callback callback, void *user_data, va_list ap) {
|
||||
if (!conn) return GLFW_FALSE;
|
||||
if (!conn) return false;
|
||||
DBusMessage *msg = dbus_message_new_method_call(node, path, interface, method);
|
||||
if (!msg) return GLFW_FALSE;
|
||||
GLFWbool retval = GLFW_FALSE;
|
||||
if (!msg) return false;
|
||||
bool retval = false;
|
||||
|
||||
int firstarg = va_arg(ap, int);
|
||||
if ((firstarg == DBUS_TYPE_INVALID) || dbus_message_append_args_valist(msg, firstarg, ap)) {
|
||||
@@ -278,9 +279,9 @@ call_method(DBusConnection *conn, const char *node, const char *path, const char
|
||||
return retval;
|
||||
}
|
||||
|
||||
GLFWbool
|
||||
bool
|
||||
glfw_dbus_call_method_with_reply(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, int timeout, dbus_pending_callback callback, void* user_data, ...) {
|
||||
GLFWbool retval;
|
||||
bool retval;
|
||||
va_list ap;
|
||||
va_start(ap, user_data);
|
||||
retval = call_method(conn, node, path, interface, method, timeout, callback, user_data, ap);
|
||||
@@ -288,9 +289,9 @@ glfw_dbus_call_method_with_reply(DBusConnection *conn, const char *node, const c
|
||||
return retval;
|
||||
}
|
||||
|
||||
GLFWbool
|
||||
bool
|
||||
glfw_dbus_call_method_no_reply(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...) {
|
||||
GLFWbool retval;
|
||||
bool retval;
|
||||
va_list ap;
|
||||
va_start(ap, method);
|
||||
retval = call_method(conn, node, path, interface, method, DBUS_TIMEOUT_USE_DEFAULT, NULL, NULL, ap);
|
||||
|
||||
14
glfw/dbus_glfw.h
vendored
14
glfw/dbus_glfw.h
vendored
@@ -1,5 +1,5 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 XKB - www.glfw.org
|
||||
// GLFW 3.4 XKB - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2018 Kovid Goyal <kovid@kovidgoyal.net>
|
||||
//
|
||||
@@ -37,18 +37,18 @@ typedef struct {
|
||||
} _GLFWDBUSData;
|
||||
|
||||
|
||||
GLFWbool glfw_dbus_init(_GLFWDBUSData *dbus, EventLoopData *eld);
|
||||
bool glfw_dbus_init(_GLFWDBUSData *dbus, EventLoopData *eld);
|
||||
void glfw_dbus_terminate(_GLFWDBUSData *dbus);
|
||||
DBusConnection* glfw_dbus_connect_to(const char *path, const char* err_msg, const char* name, GLFWbool register_on_bus);
|
||||
DBusConnection* glfw_dbus_connect_to(const char *path, const char* err_msg, const char* name, bool register_on_bus);
|
||||
void glfw_dbus_close_connection(DBusConnection *conn);
|
||||
GLFWbool
|
||||
bool
|
||||
call_method_with_msg(DBusConnection *conn, DBusMessage *msg, int timeout, dbus_pending_callback callback, void *user_data);
|
||||
GLFWbool
|
||||
bool
|
||||
glfw_dbus_call_method_no_reply(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, ...);
|
||||
GLFWbool
|
||||
bool
|
||||
glfw_dbus_call_method_with_reply(DBusConnection *conn, const char *node, const char *path, const char *interface, const char *method, int timeout_ms, dbus_pending_callback callback, void *user_data, ...);
|
||||
void glfw_dbus_dispatch(DBusConnection *);
|
||||
void glfw_dbus_session_bus_dispatch(void);
|
||||
GLFWbool glfw_dbus_get_args(DBusMessage *msg, const char *failmsg, ...);
|
||||
bool glfw_dbus_get_args(DBusMessage *msg, const char *failmsg, ...);
|
||||
int glfw_dbus_match_signal(DBusMessage *msg, const char *interface, ...);
|
||||
DBusConnection* glfw_dbus_session_bus(void);
|
||||
|
||||
321
glfw/egl_context.c
vendored
321
glfw/egl_context.c
vendored
@@ -1,8 +1,8 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 EGL - www.glfw.org
|
||||
// GLFW 3.4 EGL - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2002-2006 Marcus Geelnard
|
||||
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -24,6 +24,8 @@
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
// Please use C89 style variable declarations in this file because VS 2010
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@@ -74,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)
|
||||
@@ -82,108 +85,78 @@ 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
|
||||
//
|
||||
static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig,
|
||||
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 GLFW_FALSE;
|
||||
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)
|
||||
XVisualInfo vi = {0};
|
||||
|
||||
// Only consider EGLConfigs with associated Visuals
|
||||
vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID);
|
||||
if (!vi.visualid)
|
||||
continue;
|
||||
|
||||
if (desired->transparent)
|
||||
{
|
||||
int count;
|
||||
XVisualInfo* vis = XGetVisualInfo(_glfw.x11.display,
|
||||
VisualIDMask, &vi,
|
||||
&count);
|
||||
if (vis)
|
||||
const EGLConfig n = configs[i];
|
||||
XVisualInfo vi = {0};
|
||||
|
||||
// Only consider EGLConfigs with associated Visuals
|
||||
vi.visualid = getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID);
|
||||
if (!vi.visualid)
|
||||
continue;
|
||||
|
||||
if (desired->transparent)
|
||||
{
|
||||
u->transparent = _glfwIsVisualTransparentX11(vis[0].visual);
|
||||
XFree(vis);
|
||||
int count;
|
||||
XVisualInfo* vis =
|
||||
XGetVisualInfo(_glfw.x11.display, VisualIDMask, &vi, &count);
|
||||
if (vis)
|
||||
{
|
||||
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 = GLFW_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)
|
||||
@@ -241,10 +214,10 @@ static int extensionSupportedEGL(const char* extension)
|
||||
if (extensions)
|
||||
{
|
||||
if (_glfwStringInExtensionString(extension, extensions))
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
static GLFWglproc getProcAddressEGL(const char* procname)
|
||||
@@ -253,8 +226,8 @@ static GLFWglproc getProcAddressEGL(const char* procname)
|
||||
|
||||
if (window->context.egl.client)
|
||||
{
|
||||
GLFWglproc proc = (GLFWglproc) _glfw_dlsym(window->context.egl.client,
|
||||
procname);
|
||||
GLFWglproc proc = NULL;
|
||||
glfw_dlsym(proc, window->context.egl.client, procname);
|
||||
if (proc)
|
||||
return proc;
|
||||
}
|
||||
@@ -297,9 +270,11 @@ static void destroyContextEGL(_GLFWwindow* window)
|
||||
|
||||
// Initialize EGL
|
||||
//
|
||||
GLFWbool _glfwInitEGL(void)
|
||||
bool _glfwInitEGL(void)
|
||||
{
|
||||
int i;
|
||||
EGLint* attribs = NULL;
|
||||
const char* extensions;
|
||||
const char* sonames[] =
|
||||
{
|
||||
#if defined(_GLFW_EGL_LIBRARY)
|
||||
@@ -318,7 +293,7 @@ GLFWbool _glfwInitEGL(void)
|
||||
};
|
||||
|
||||
if (_glfw.egl.handle)
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
|
||||
for (i = 0; sonames[i]; i++)
|
||||
{
|
||||
@@ -330,46 +305,32 @@ GLFWbool _glfwInitEGL(void)
|
||||
if (!_glfw.egl.handle)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Library not found");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
_glfw.egl.prefix = (strncmp(sonames[i], "lib", 3) == 0);
|
||||
|
||||
_glfw.egl.GetConfigAttrib = (PFN_eglGetConfigAttrib)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglGetConfigAttrib");
|
||||
_glfw.egl.GetConfigs = (PFN_eglGetConfigs)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglGetConfigs");
|
||||
_glfw.egl.GetDisplay = (PFN_eglGetDisplay)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglGetDisplay");
|
||||
_glfw.egl.GetError = (PFN_eglGetError)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglGetError");
|
||||
_glfw.egl.Initialize = (PFN_eglInitialize)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglInitialize");
|
||||
_glfw.egl.Terminate = (PFN_eglTerminate)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglTerminate");
|
||||
_glfw.egl.BindAPI = (PFN_eglBindAPI)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglBindAPI");
|
||||
_glfw.egl.CreateContext = (PFN_eglCreateContext)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglCreateContext");
|
||||
_glfw.egl.DestroySurface = (PFN_eglDestroySurface)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglDestroySurface");
|
||||
_glfw.egl.DestroyContext = (PFN_eglDestroyContext)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglDestroyContext");
|
||||
_glfw.egl.CreateWindowSurface = (PFN_eglCreateWindowSurface)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglCreateWindowSurface");
|
||||
_glfw.egl.MakeCurrent = (PFN_eglMakeCurrent)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglMakeCurrent");
|
||||
_glfw.egl.SwapBuffers = (PFN_eglSwapBuffers)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglSwapBuffers");
|
||||
_glfw.egl.SwapInterval = (PFN_eglSwapInterval)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglSwapInterval");
|
||||
_glfw.egl.QueryString = (PFN_eglQueryString)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglQueryString");
|
||||
_glfw.egl.GetProcAddress = (PFN_eglGetProcAddress)
|
||||
_glfw_dlsym(_glfw.egl.handle, "eglGetProcAddress");
|
||||
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");
|
||||
glfw_dlsym(_glfw.egl.Terminate, _glfw.egl.handle, "eglTerminate");
|
||||
glfw_dlsym(_glfw.egl.BindAPI, _glfw.egl.handle, "eglBindAPI");
|
||||
glfw_dlsym(_glfw.egl.CreateContext, _glfw.egl.handle, "eglCreateContext");
|
||||
glfw_dlsym(_glfw.egl.DestroySurface, _glfw.egl.handle, "eglDestroySurface");
|
||||
glfw_dlsym(_glfw.egl.DestroyContext, _glfw.egl.handle, "eglDestroyContext");
|
||||
glfw_dlsym(_glfw.egl.CreateWindowSurface, _glfw.egl.handle, "eglCreateWindowSurface");
|
||||
glfw_dlsym(_glfw.egl.MakeCurrent, _glfw.egl.handle, "eglMakeCurrent");
|
||||
glfw_dlsym(_glfw.egl.SwapBuffers, _glfw.egl.handle, "eglSwapBuffers");
|
||||
glfw_dlsym(_glfw.egl.SwapInterval, _glfw.egl.handle, "eglSwapInterval");
|
||||
glfw_dlsym(_glfw.egl.QueryString, _glfw.egl.handle, "eglQueryString");
|
||||
glfw_dlsym(_glfw.egl.GetProcAddress, _glfw.egl.handle, "eglGetProcAddress");
|
||||
|
||||
if (!_glfw.egl.GetConfigAttrib ||
|
||||
!_glfw.egl.GetConfigs ||
|
||||
!_glfw.egl.ChooseConfig ||
|
||||
!_glfw.egl.GetDisplay ||
|
||||
!_glfw.egl.GetError ||
|
||||
!_glfw.egl.Initialize ||
|
||||
@@ -389,10 +350,54 @@ GLFWbool _glfwInitEGL(void)
|
||||
"EGL: Failed to load required entry points");
|
||||
|
||||
_glfwTerminateEGL();
|
||||
return GLFW_FALSE;
|
||||
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,
|
||||
@@ -400,7 +405,7 @@ GLFWbool _glfwInitEGL(void)
|
||||
getEGLErrorString(eglGetError()));
|
||||
|
||||
_glfwTerminateEGL();
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!eglInitialize(_glfw.egl.display, &_glfw.egl.major, &_glfw.egl.minor))
|
||||
@@ -410,7 +415,7 @@ GLFWbool _glfwInitEGL(void)
|
||||
getEGLErrorString(eglGetError()));
|
||||
|
||||
_glfwTerminateEGL();
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
_glfw.egl.KHR_create_context =
|
||||
@@ -424,7 +429,7 @@ GLFWbool _glfwInitEGL(void)
|
||||
_glfw.egl.KHR_context_flush_control =
|
||||
extensionSupportedEGL("EGL_KHR_context_flush_control");
|
||||
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Terminate EGL
|
||||
@@ -446,26 +451,27 @@ void _glfwTerminateEGL(void)
|
||||
|
||||
#define setAttrib(a, v) \
|
||||
{ \
|
||||
assert((size_t) (index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
|
||||
assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
|
||||
attribs[index++] = a; \
|
||||
attribs[index++] = v; \
|
||||
}
|
||||
|
||||
// Create the OpenGL or OpenGL ES context
|
||||
//
|
||||
GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
bool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig)
|
||||
{
|
||||
EGLint attribs[40];
|
||||
EGLConfig config;
|
||||
EGLContext share = NULL;
|
||||
EGLNativeWindowType native;
|
||||
int index = 0;
|
||||
|
||||
if (!_glfw.egl.display)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "EGL: API not available");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctxconfig->share)
|
||||
@@ -475,7 +481,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
{
|
||||
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
|
||||
"EGL: Failed to find a suitable EGLConfig");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctxconfig->client == GLFW_OPENGL_ES_API)
|
||||
@@ -485,7 +491,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"EGL: Failed to bind OpenGL ES: %s",
|
||||
getEGLErrorString(eglGetError()));
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -495,7 +501,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"EGL: Failed to bind OpenGL: %s",
|
||||
getEGLErrorString(eglGetError()));
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,7 +542,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
if (ctxconfig->noerror)
|
||||
{
|
||||
if (_glfw.egl.KHR_create_context_no_error)
|
||||
setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, GLFW_TRUE);
|
||||
setAttrib(EGL_CONTEXT_OPENGL_NO_ERROR_KHR, true);
|
||||
}
|
||||
|
||||
if (ctxconfig->major != 1 || ctxconfig->minor != 0)
|
||||
@@ -581,33 +587,40 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"EGL: Failed to create context: %s",
|
||||
getEGLErrorString(eglGetError()));
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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,
|
||||
"EGL: Failed to create window surface: %s",
|
||||
getEGLErrorString(eglGetError()));
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
window->context.egl.config = config;
|
||||
@@ -695,7 +708,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"EGL: Failed to load client library");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -706,7 +719,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
window->context.getProcAddress = getProcAddressEGL;
|
||||
window->context.destroy = destroyContextEGL;
|
||||
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef setAttrib
|
||||
@@ -714,7 +727,7 @@ GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
// Returns the Visual and depth of the chosen EGLConfig
|
||||
//
|
||||
#if defined(_GLFW_X11)
|
||||
GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig,
|
||||
bool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig UNUSED,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig,
|
||||
Visual** visual, int* depth)
|
||||
@@ -729,7 +742,7 @@ GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig,
|
||||
{
|
||||
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
|
||||
"EGL: Failed to find a suitable EGLConfig");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
eglGetConfigAttrib(_glfw.egl.display, native,
|
||||
@@ -743,14 +756,14 @@ GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig,
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"EGL: Failed to retrieve Visual for EGLConfig");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
*visual = result->visual;
|
||||
*depth = result->depth;
|
||||
|
||||
XFree(result);
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
#endif // _GLFW_X11
|
||||
|
||||
|
||||
79
glfw/egl_context.h
vendored
79
glfw/egl_context.h
vendored
@@ -1,8 +1,8 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 EGL - www.glfw.org
|
||||
// GLFW 3.4 EGL - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2002-2006 Marcus Geelnard
|
||||
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -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,20 +172,31 @@ typedef struct _GLFWcontextEGL
|
||||
//
|
||||
typedef struct _GLFWlibraryEGL
|
||||
{
|
||||
EGLenum platform;
|
||||
EGLDisplay display;
|
||||
EGLint major, minor;
|
||||
GLFWbool prefix;
|
||||
bool prefix;
|
||||
|
||||
GLFWbool KHR_create_context;
|
||||
GLFWbool KHR_create_context_no_error;
|
||||
GLFWbool KHR_gl_colorspace;
|
||||
GLFWbool KHR_get_all_proc_addresses;
|
||||
GLFWbool KHR_context_flush_control;
|
||||
bool KHR_create_context;
|
||||
bool KHR_create_context_no_error;
|
||||
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,16 +212,19 @@ typedef struct _GLFWlibraryEGL
|
||||
PFN_eglQueryString QueryString;
|
||||
PFN_eglGetProcAddress GetProcAddress;
|
||||
|
||||
PFNEGLGETPLATFORMDISPLAYEXTPROC GetPlatformDisplayEXT;
|
||||
PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC CreatePlatformWindowSurfaceEXT;
|
||||
|
||||
} _GLFWlibraryEGL;
|
||||
|
||||
|
||||
GLFWbool _glfwInitEGL(void);
|
||||
bool _glfwInitEGL(void);
|
||||
void _glfwTerminateEGL(void);
|
||||
GLFWbool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
bool _glfwCreateContextEGL(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig);
|
||||
#if defined(_GLFW_X11)
|
||||
GLFWbool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig,
|
||||
bool _glfwChooseVisualEGL(const _GLFWwndconfig* wndconfig,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig,
|
||||
Visual** visual, int* depth);
|
||||
|
||||
153
glfw/glfw.py
153
glfw/glfw.py
@@ -6,53 +6,81 @@ import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from typing import Callable, Dict, List, Optional, Tuple
|
||||
|
||||
_plat = sys.platform.lower()
|
||||
is_macos = 'darwin' in _plat
|
||||
is_freebsd = 'freebsd' in _plat
|
||||
is_netbsd = 'netbsd' in _plat
|
||||
is_dragonflybsd = 'dragonfly' in _plat
|
||||
is_bsd = is_freebsd or is_netbsd or is_dragonflybsd
|
||||
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 = [
|
||||
x for x in ans.cflags
|
||||
if x not in '-Wpedantic -Wextra -pedantic-errors'.split()
|
||||
]
|
||||
if not is_macos:
|
||||
ans.cflags.append('-pthread')
|
||||
ans.ldpaths.append('-pthread')
|
||||
ans.cflags.append('-fpic')
|
||||
ans.cflags.append('-fPIC')
|
||||
ans.cppflags.append('-D_GLFW_' + module.upper())
|
||||
ans.cppflags.append('-D_GLFW_BUILD_DLL')
|
||||
|
||||
if is_macos:
|
||||
ans.cppflags.append('-DGL_SILENCE_DEPRECATION')
|
||||
ans.ldpaths.extend(
|
||||
"-framework Cocoa -framework IOKit -framework CoreFoundation -framework CoreVideo".
|
||||
split()
|
||||
)
|
||||
else:
|
||||
ans.ldpaths.extend('-lrt -lm -ldl'.split())
|
||||
sinfo = json.load(open(os.path.join(base, 'source-info.json')))
|
||||
with open(os.path.join(base, 'source-info.json')) as f:
|
||||
sinfo = json.load(f)
|
||||
module_sources = list(sinfo[module]['sources'])
|
||||
if module in ('x11', 'wayland'):
|
||||
remove = 'linux_joystick.c' if is_bsd else 'null_joystick.c'
|
||||
remove = 'null_joystick.c' if is_linux else 'linux_joystick.c'
|
||||
module_sources.remove(remove)
|
||||
|
||||
ans.sources = sinfo['common']['sources'] + module_sources
|
||||
ans.all_headers = [x for x in os.listdir(base) if x.endswith('.h')]
|
||||
|
||||
if module in ('x11', 'wayland'):
|
||||
at_least_version('xkbcommon', 0, 5)
|
||||
ans.cflags.append('-pthread')
|
||||
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():
|
||||
@@ -60,8 +88,9 @@ def init_env(env, pkg_config, at_least_version, test_compile, module='x11'):
|
||||
ans.ldpaths.extend(pkg_config(dep, '--libs'))
|
||||
|
||||
elif module == 'cocoa':
|
||||
for f in 'Cocoa IOKit CoreFoundation CoreVideo'.split():
|
||||
ans.ldpaths.extend(('-framework', f))
|
||||
ans.cppflags.append('-DGL_SILENCE_DEPRECATION')
|
||||
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'])
|
||||
@@ -73,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
|
||||
@@ -88,7 +117,8 @@ def init_env(env, pkg_config, at_least_version, test_compile, module='x11'):
|
||||
return ans
|
||||
|
||||
|
||||
def build_wayland_protocols(env, run_tool, 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)
|
||||
if not os.path.exists(src):
|
||||
@@ -98,13 +128,17 @@ def build_wayland_protocols(env, run_tool, emphasis, newer, dest_dir):
|
||||
dest = os.path.join(dest_dir, dest)
|
||||
if newer(dest, src):
|
||||
q = 'client-header' if ext == 'h' else env.wayland_scanner_code
|
||||
run_tool([env.wayland_scanner, q, src, dest],
|
||||
desc='Generating {} ...'.format(emphasis(os.path.basename(dest))))
|
||||
items.append(Command(
|
||||
'Generating {} ...'.format(emphasis(os.path.basename(dest))),
|
||||
[env.wayland_scanner, q, src, dest],
|
||||
lambda: True, None, None, None))
|
||||
if items:
|
||||
parallel_run(items)
|
||||
|
||||
|
||||
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()
|
||||
@@ -112,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
|
||||
@@ -137,26 +171,26 @@ 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):
|
||||
src = open(glfw_header).read()
|
||||
def generate_wrappers(glfw_header: str) -> None:
|
||||
with open(glfw_header) as f:
|
||||
src = f.read()
|
||||
functions = []
|
||||
first = None
|
||||
for m in re.finditer(r'^GLFWAPI\s+(.+[)]);\s*$', src, flags=re.MULTILINE):
|
||||
@@ -168,19 +202,23 @@ 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)
|
||||
void glfwGetCocoaKeyEquivalent(int glfw_key, int glfw_mods, void* cocoa_key, void* cocoa_mods)
|
||||
GLFWapplicationwillfinishlaunchingfun glfwSetApplicationWillFinishLaunching(GLFWapplicationwillfinishlaunchingfun callback)
|
||||
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)
|
||||
void glfwSetPrimarySelectionString(GLFWwindow* window, const char* string)
|
||||
const char* glfwGetPrimarySelectionString(GLFWwindow* window, void)
|
||||
int glfwGetXKBScancode(const char* key_name, int case_sensitive)
|
||||
int glfwGetNativeKeyForName(const char* key_name, int case_sensitive)
|
||||
void glfwRequestWaylandFrameEvent(GLFWwindow *handle, unsigned long long id, GLFWwaylandframecallbackfunc callback)
|
||||
bool glfwWaylandSetTitlebarColor(GLFWwindow *handle, uint32_t color, bool use_system_color)
|
||||
unsigned long long glfwDBusUserNotify(const char *app_name, const char* icon, const char *summary, const char *body, \
|
||||
const char *action_text, int32_t timeout, GLFWDBusnotificationcreatedfun callback, void *data)
|
||||
void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler)
|
||||
@@ -193,15 +231,27 @@ const char *action_text, int32_t timeout, GLFWDBusnotificationcreatedfun callbac
|
||||
p = src.find('*/', p)
|
||||
preamble = src[p + 2:first]
|
||||
header = '''\
|
||||
//
|
||||
// THIS FILE IS GENERATED BY glfw.py
|
||||
//
|
||||
// SAVE YOURSELF SOME TIME, DO NOT MANUALLY EDIT
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "monotonic.h"
|
||||
|
||||
#ifndef GFW_EXTERN
|
||||
#define GFW_EXTERN extern
|
||||
#endif
|
||||
{}
|
||||
|
||||
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int,unsigned long);
|
||||
typedef int (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef int (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef bool (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef bool (* GLFWhandlefileopen)(const char*);
|
||||
typedef void (* GLFWapplicationwillfinishlaunchingfun)(void);
|
||||
typedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
|
||||
typedef void (*GLFWwaylandframecallbackfunc)(unsigned long long id);
|
||||
typedef void (*GLFWDBusnotificationcreatedfun)(unsigned long long, uint32_t, void*);
|
||||
@@ -214,6 +264,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>
|
||||
@@ -243,7 +294,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')
|
||||
|
||||
|
||||
1469
glfw/glfw3.h
vendored
1469
glfw/glfw3.h
vendored
File diff suppressed because it is too large
Load Diff
258
glfw/glx_context.c
vendored
258
glfw/glx_context.c
vendored
@@ -1,8 +1,8 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 GLX - www.glfw.org
|
||||
// GLFW 3.4 GLX - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2002-2006 Marcus Geelnard
|
||||
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -24,6 +24,8 @@
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
// It is fine to use C99 in this file because it will not be built with VS
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@@ -45,104 +47,92 @@ 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 GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired,
|
||||
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;
|
||||
GLFWbool trustWindowBit = GLFW_TRUE;
|
||||
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 = GLFW_FALSE;
|
||||
|
||||
nativeConfigs =
|
||||
glXGetFBConfigs(_glfw.x11.display, _glfw.x11.screen, &nativeCount);
|
||||
trustWindowBit = false;
|
||||
nativeConfigs = choose_fb_config(desired, trustWindowBit, &nativeCount);
|
||||
if (!nativeConfigs || !nativeCount)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "GLX: No GLXFBConfigs returned");
|
||||
return GLFW_FALSE;
|
||||
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 = GLFW_TRUE;
|
||||
if (getGLXFBConfigAttrib(n, GLX_DOUBLEBUFFER))
|
||||
u->doublebuffer = GLFW_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
|
||||
//
|
||||
static GLXContext createLegacyContextGLX(_GLFWwindow* window,
|
||||
static GLXContext createLegacyContextGLX(_GLFWwindow* window UNUSED,
|
||||
GLXFBConfig fbconfig,
|
||||
GLXContext share)
|
||||
{
|
||||
@@ -210,10 +200,10 @@ static int extensionSupportedGLX(const char* extension)
|
||||
if (extensions)
|
||||
{
|
||||
if (_glfwStringInExtensionString(extension, extensions))
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
static GLFWglproc getProcAddressGLX(const char* procname)
|
||||
@@ -222,12 +212,13 @@ static GLFWglproc getProcAddressGLX(const char* procname)
|
||||
return _glfw.glx.GetProcAddress((const GLubyte*) procname);
|
||||
else if (_glfw.glx.GetProcAddressARB)
|
||||
return _glfw.glx.GetProcAddressARB((const GLubyte*) procname);
|
||||
else
|
||||
return _glfw_dlsym(_glfw.glx.handle, procname);
|
||||
else {
|
||||
GLFWglproc ans = NULL;
|
||||
glfw_dlsym(ans, _glfw.glx.handle, procname);
|
||||
return ans;
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy the OpenGL context
|
||||
//
|
||||
static void destroyContextGLX(_GLFWwindow* window)
|
||||
{
|
||||
if (window->context.glx.window)
|
||||
@@ -250,7 +241,7 @@ static void destroyContextGLX(_GLFWwindow* window)
|
||||
|
||||
// Initialize GLX
|
||||
//
|
||||
GLFWbool _glfwInitGLX(void)
|
||||
bool _glfwInitGLX(void)
|
||||
{
|
||||
int i;
|
||||
const char* sonames[] =
|
||||
@@ -267,7 +258,7 @@ GLFWbool _glfwInitGLX(void)
|
||||
};
|
||||
|
||||
if (_glfw.glx.handle)
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
|
||||
for (i = 0; sonames[i]; i++)
|
||||
{
|
||||
@@ -279,39 +270,25 @@ GLFWbool _glfwInitGLX(void)
|
||||
if (!_glfw.glx.handle)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "GLX: Failed to load GLX");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
_glfw.glx.GetFBConfigs =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigs");
|
||||
_glfw.glx.GetFBConfigAttrib =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXGetFBConfigAttrib");
|
||||
_glfw.glx.GetClientString =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXGetClientString");
|
||||
_glfw.glx.QueryExtension =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXQueryExtension");
|
||||
_glfw.glx.QueryVersion =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXQueryVersion");
|
||||
_glfw.glx.DestroyContext =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXDestroyContext");
|
||||
_glfw.glx.MakeCurrent =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXMakeCurrent");
|
||||
_glfw.glx.SwapBuffers =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXSwapBuffers");
|
||||
_glfw.glx.QueryExtensionsString =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXQueryExtensionsString");
|
||||
_glfw.glx.CreateNewContext =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXCreateNewContext");
|
||||
_glfw.glx.CreateWindow =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXCreateWindow");
|
||||
_glfw.glx.DestroyWindow =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXDestroyWindow");
|
||||
_glfw.glx.GetProcAddress =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXGetProcAddress");
|
||||
_glfw.glx.GetProcAddressARB =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXGetProcAddressARB");
|
||||
_glfw.glx.GetVisualFromFBConfig =
|
||||
_glfw_dlsym(_glfw.glx.handle, "glXGetVisualFromFBConfig");
|
||||
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");
|
||||
glfw_dlsym(_glfw.glx.DestroyContext, _glfw.glx.handle, "glXDestroyContext");
|
||||
glfw_dlsym(_glfw.glx.MakeCurrent, _glfw.glx.handle, "glXMakeCurrent");
|
||||
glfw_dlsym(_glfw.glx.SwapBuffers, _glfw.glx.handle, "glXSwapBuffers");
|
||||
glfw_dlsym(_glfw.glx.QueryExtensionsString, _glfw.glx.handle, "glXQueryExtensionsString");
|
||||
glfw_dlsym(_glfw.glx.CreateNewContext, _glfw.glx.handle, "glXCreateNewContext");
|
||||
glfw_dlsym(_glfw.glx.CreateWindow, _glfw.glx.handle, "glXCreateWindow");
|
||||
glfw_dlsym(_glfw.glx.DestroyWindow, _glfw.glx.handle, "glXDestroyWindow");
|
||||
glfw_dlsym(_glfw.glx.GetProcAddress, _glfw.glx.handle, "glXGetProcAddress");
|
||||
glfw_dlsym(_glfw.glx.GetProcAddressARB, _glfw.glx.handle, "glXGetProcAddressARB");
|
||||
glfw_dlsym(_glfw.glx.GetVisualFromFBConfig, _glfw.glx.handle, "glXGetVisualFromFBConfig");
|
||||
|
||||
if (!_glfw.glx.GetFBConfigs ||
|
||||
!_glfw.glx.GetFBConfigAttrib ||
|
||||
@@ -331,7 +308,7 @@ GLFWbool _glfwInitGLX(void)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"GLX: Failed to load required entry points");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!glXQueryExtension(_glfw.x11.display,
|
||||
@@ -339,21 +316,21 @@ GLFWbool _glfwInitGLX(void)
|
||||
&_glfw.glx.eventBase))
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE, "GLX: GLX extension not found");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!glXQueryVersion(_glfw.x11.display, &_glfw.glx.major, &_glfw.glx.minor))
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"GLX: Failed to query GLX version");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_glfw.glx.major == 1 && _glfw.glx.minor < 3)
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"GLX: GLX version 1.3 is required");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (extensionSupportedGLX("GLX_EXT_swap_control"))
|
||||
@@ -362,7 +339,7 @@ GLFWbool _glfwInitGLX(void)
|
||||
getProcAddressGLX("glXSwapIntervalEXT");
|
||||
|
||||
if (_glfw.glx.SwapIntervalEXT)
|
||||
_glfw.glx.EXT_swap_control = GLFW_TRUE;
|
||||
_glfw.glx.EXT_swap_control = true;
|
||||
}
|
||||
|
||||
if (extensionSupportedGLX("GLX_SGI_swap_control"))
|
||||
@@ -371,7 +348,7 @@ GLFWbool _glfwInitGLX(void)
|
||||
getProcAddressGLX("glXSwapIntervalSGI");
|
||||
|
||||
if (_glfw.glx.SwapIntervalSGI)
|
||||
_glfw.glx.SGI_swap_control = GLFW_TRUE;
|
||||
_glfw.glx.SGI_swap_control = true;
|
||||
}
|
||||
|
||||
if (extensionSupportedGLX("GLX_MESA_swap_control"))
|
||||
@@ -380,17 +357,17 @@ GLFWbool _glfwInitGLX(void)
|
||||
getProcAddressGLX("glXSwapIntervalMESA");
|
||||
|
||||
if (_glfw.glx.SwapIntervalMESA)
|
||||
_glfw.glx.MESA_swap_control = GLFW_TRUE;
|
||||
_glfw.glx.MESA_swap_control = true;
|
||||
}
|
||||
|
||||
if (extensionSupportedGLX("GLX_ARB_multisample"))
|
||||
_glfw.glx.ARB_multisample = GLFW_TRUE;
|
||||
_glfw.glx.ARB_multisample = true;
|
||||
|
||||
if (extensionSupportedGLX("GLX_ARB_framebuffer_sRGB"))
|
||||
_glfw.glx.ARB_framebuffer_sRGB = GLFW_TRUE;
|
||||
_glfw.glx.ARB_framebuffer_sRGB = true;
|
||||
|
||||
if (extensionSupportedGLX("GLX_EXT_framebuffer_sRGB"))
|
||||
_glfw.glx.EXT_framebuffer_sRGB = GLFW_TRUE;
|
||||
_glfw.glx.EXT_framebuffer_sRGB = true;
|
||||
|
||||
if (extensionSupportedGLX("GLX_ARB_create_context"))
|
||||
{
|
||||
@@ -398,25 +375,25 @@ GLFWbool _glfwInitGLX(void)
|
||||
getProcAddressGLX("glXCreateContextAttribsARB");
|
||||
|
||||
if (_glfw.glx.CreateContextAttribsARB)
|
||||
_glfw.glx.ARB_create_context = GLFW_TRUE;
|
||||
_glfw.glx.ARB_create_context = true;
|
||||
}
|
||||
|
||||
if (extensionSupportedGLX("GLX_ARB_create_context_robustness"))
|
||||
_glfw.glx.ARB_create_context_robustness = GLFW_TRUE;
|
||||
_glfw.glx.ARB_create_context_robustness = true;
|
||||
|
||||
if (extensionSupportedGLX("GLX_ARB_create_context_profile"))
|
||||
_glfw.glx.ARB_create_context_profile = GLFW_TRUE;
|
||||
_glfw.glx.ARB_create_context_profile = true;
|
||||
|
||||
if (extensionSupportedGLX("GLX_EXT_create_context_es2_profile"))
|
||||
_glfw.glx.EXT_create_context_es2_profile = GLFW_TRUE;
|
||||
_glfw.glx.EXT_create_context_es2_profile = true;
|
||||
|
||||
if (extensionSupportedGLX("GLX_ARB_create_context_no_error"))
|
||||
_glfw.glx.ARB_create_context_no_error = GLFW_TRUE;
|
||||
_glfw.glx.ARB_create_context_no_error = true;
|
||||
|
||||
if (extensionSupportedGLX("GLX_ARB_context_flush_control"))
|
||||
_glfw.glx.ARB_context_flush_control = GLFW_TRUE;
|
||||
_glfw.glx.ARB_context_flush_control = true;
|
||||
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Terminate GLX
|
||||
@@ -435,14 +412,14 @@ void _glfwTerminateGLX(void)
|
||||
|
||||
#define setAttrib(a, v) \
|
||||
{ \
|
||||
assert((size_t) (index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
|
||||
assert(((size_t) index + 1) < sizeof(attribs) / sizeof(attribs[0])); \
|
||||
attribs[index++] = a; \
|
||||
attribs[index++] = v; \
|
||||
}
|
||||
|
||||
// Create the OpenGL or OpenGL ES context
|
||||
//
|
||||
GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
|
||||
bool _glfwCreateContextGLX(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig)
|
||||
{
|
||||
@@ -457,7 +434,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
|
||||
{
|
||||
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
|
||||
"GLX: Failed to find a suitable GLXFBConfig");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctxconfig->client == GLFW_OPENGL_ES_API)
|
||||
@@ -468,7 +445,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
|
||||
{
|
||||
_glfwInputError(GLFW_API_UNAVAILABLE,
|
||||
"GLX: OpenGL ES requested but GLX_EXT_create_context_es2_profile is unavailable");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,7 +455,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
|
||||
{
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"GLX: Forward compatibility requested but GLX_ARB_create_context_profile is unavailable");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,7 +466,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
|
||||
{
|
||||
_glfwInputError(GLFW_VERSION_UNAVAILABLE,
|
||||
"GLX: An OpenGL profile requested but GLX_ARB_create_context_profile is unavailable");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,7 +531,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
|
||||
if (ctxconfig->noerror)
|
||||
{
|
||||
if (_glfw.glx.ARB_create_context_no_error)
|
||||
setAttrib(GLX_CONTEXT_OPENGL_NO_ERROR_ARB, GLFW_TRUE);
|
||||
setAttrib(GLX_CONTEXT_OPENGL_NO_ERROR_ARB, true);
|
||||
}
|
||||
|
||||
// NOTE: Only request an explicitly versioned context when necessary, as
|
||||
@@ -590,7 +567,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
|
||||
if (_glfw.x11.errorCode == _glfw.glx.errorBase + GLXBadProfileARB &&
|
||||
ctxconfig->client == GLFW_OPENGL_API &&
|
||||
ctxconfig->profile == GLFW_OPENGL_ANY_PROFILE &&
|
||||
ctxconfig->forward == GLFW_FALSE)
|
||||
ctxconfig->forward == false)
|
||||
{
|
||||
window->context.glx.handle =
|
||||
createLegacyContextGLX(window, native, share);
|
||||
@@ -608,7 +585,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
|
||||
if (!window->context.glx.handle)
|
||||
{
|
||||
_glfwInputErrorX11(GLFW_VERSION_UNAVAILABLE, "GLX: Failed to create context");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
window->context.glx.window =
|
||||
@@ -616,7 +593,7 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
|
||||
if (!window->context.glx.window)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "GLX: Failed to create window");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
window->context.makeCurrent = makeContextCurrentGLX;
|
||||
@@ -626,15 +603,15 @@ GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
|
||||
window->context.getProcAddress = getProcAddressGLX;
|
||||
window->context.destroy = destroyContextGLX;
|
||||
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef setAttrib
|
||||
|
||||
// Returns the Visual and depth of the chosen GLXFBConfig
|
||||
//
|
||||
GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
bool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig UNUSED,
|
||||
const _GLFWctxconfig* ctxconfig UNUSED,
|
||||
const _GLFWfbconfig* fbconfig,
|
||||
Visual** visual, int* depth)
|
||||
{
|
||||
@@ -645,7 +622,7 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig,
|
||||
{
|
||||
_glfwInputError(GLFW_FORMAT_UNAVAILABLE,
|
||||
"GLX: Failed to find a suitable GLXFBConfig");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
result = glXGetVisualFromFBConfig(_glfw.x11.display, native);
|
||||
@@ -653,14 +630,14 @@ GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig,
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"GLX: Failed to retrieve Visual for GLXFBConfig");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
*visual = result->visual;
|
||||
*depth = result->depth;
|
||||
|
||||
XFree(result);
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -695,4 +672,3 @@ GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* handle)
|
||||
|
||||
return window->context.glx.window;
|
||||
}
|
||||
|
||||
|
||||
42
glfw/glx_context.h
vendored
42
glfw/glx_context.h
vendored
@@ -1,8 +1,8 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 GLX - www.glfw.org
|
||||
// GLFW 3.4 GLX - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2002-2006 Marcus Geelnard
|
||||
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -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
|
||||
@@ -107,8 +109,8 @@ typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLX
|
||||
#define glXCreateWindow _glfw.glx.CreateWindow
|
||||
#define glXDestroyWindow _glfw.glx.DestroyWindow
|
||||
|
||||
#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextGLX glx
|
||||
#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryGLX glx
|
||||
#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextGLX glx;
|
||||
#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryGLX glx;
|
||||
|
||||
|
||||
// GLX-specific per-context data
|
||||
@@ -134,6 +136,7 @@ typedef struct _GLFWlibraryGLX
|
||||
// GLX 1.3 functions
|
||||
PFNGLXGETFBCONFIGSPROC GetFBConfigs;
|
||||
PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib;
|
||||
PFNGLXCHOOSEFBCONFIGPROC ChooseFBConfig;
|
||||
PFNGLXGETCLIENTSTRINGPROC GetClientString;
|
||||
PFNGLXQUERYEXTENSIONPROC QueryExtension;
|
||||
PFNGLXQUERYVERSIONPROC QueryVersion;
|
||||
@@ -153,29 +156,28 @@ typedef struct _GLFWlibraryGLX
|
||||
PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT;
|
||||
PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA;
|
||||
PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB;
|
||||
GLFWbool SGI_swap_control;
|
||||
GLFWbool EXT_swap_control;
|
||||
GLFWbool MESA_swap_control;
|
||||
GLFWbool ARB_multisample;
|
||||
GLFWbool ARB_framebuffer_sRGB;
|
||||
GLFWbool EXT_framebuffer_sRGB;
|
||||
GLFWbool ARB_create_context;
|
||||
GLFWbool ARB_create_context_profile;
|
||||
GLFWbool ARB_create_context_robustness;
|
||||
GLFWbool EXT_create_context_es2_profile;
|
||||
GLFWbool ARB_create_context_no_error;
|
||||
GLFWbool ARB_context_flush_control;
|
||||
bool SGI_swap_control;
|
||||
bool EXT_swap_control;
|
||||
bool MESA_swap_control;
|
||||
bool ARB_multisample;
|
||||
bool ARB_framebuffer_sRGB;
|
||||
bool EXT_framebuffer_sRGB;
|
||||
bool ARB_create_context;
|
||||
bool ARB_create_context_profile;
|
||||
bool ARB_create_context_robustness;
|
||||
bool EXT_create_context_es2_profile;
|
||||
bool ARB_create_context_no_error;
|
||||
bool ARB_context_flush_control;
|
||||
|
||||
} _GLFWlibraryGLX;
|
||||
|
||||
GLFWbool _glfwInitGLX(void);
|
||||
bool _glfwInitGLX(void);
|
||||
void _glfwTerminateGLX(void);
|
||||
GLFWbool _glfwCreateContextGLX(_GLFWwindow* window,
|
||||
bool _glfwCreateContextGLX(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig);
|
||||
void _glfwDestroyContextGLX(_GLFWwindow* window);
|
||||
GLFWbool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig,
|
||||
bool _glfwChooseVisualGLX(const _GLFWwndconfig* wndconfig,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig,
|
||||
Visual** visual, int* depth);
|
||||
|
||||
|
||||
109
glfw/ibus_glfw.c
vendored
109
glfw/ibus_glfw.c
vendored
@@ -1,5 +1,5 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 XKB - www.glfw.org
|
||||
// GLFW 3.4 XKB - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2018 Kovid Goyal <kovid@kovidgoyal.net>
|
||||
//
|
||||
@@ -50,14 +50,14 @@ enum Capabilities {
|
||||
};
|
||||
|
||||
|
||||
static inline GLFWbool
|
||||
static inline bool
|
||||
test_env_var(const char *name, const char *val) {
|
||||
const char *q = getenv(name);
|
||||
return (q && strcmp(q, val) == 0) ? GLFW_TRUE : GLFW_FALSE;
|
||||
return (q && strcmp(q, val) == 0) ? true : false;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
MIN(size_t a, size_t b) {
|
||||
GLFW_MIN(size_t a, size_t b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
@@ -107,17 +107,20 @@ get_ibus_text_from_message(DBusMessage *msg) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
send_text(const char *text, int state) {
|
||||
send_text(const char *text, GLFWIMEState ime_state) {
|
||||
_GLFWwindow *w = _glfwFocusedWindow();
|
||||
if (w && w->callbacks.keyboard) {
|
||||
w->callbacks.keyboard((GLFWwindow*) w, GLFW_KEY_UNKNOWN, 0, GLFW_PRESS, 0, text, state);
|
||||
GLFWkeyevent fake_ev = {.action = GLFW_PRESS};
|
||||
fake_ev.text = text;
|
||||
fake_ev.ime_state = ime_state;
|
||||
w->callbacks.keyboard((GLFWwindow*) w, &fake_ev);
|
||||
}
|
||||
}
|
||||
|
||||
// Connection handling {{{
|
||||
|
||||
static DBusHandlerResult
|
||||
message_handler(DBusConnection *conn, DBusMessage *msg, void *user_data) {
|
||||
message_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data) {
|
||||
// To monitor signals from IBUS, use
|
||||
// dbus-monitor --address `ibus address` "type='signal',interface='org.freedesktop.IBus.InputContext'"
|
||||
_GLFWIBUSData *ibus = (_GLFWIBUSData*)user_data;
|
||||
@@ -127,11 +130,11 @@ message_handler(DBusConnection *conn, 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:
|
||||
@@ -151,7 +154,7 @@ get_ibus_address_file_name(void) {
|
||||
addr = getenv("IBUS_ADDRESS");
|
||||
int offset = 0;
|
||||
if (addr && addr[0]) {
|
||||
memcpy(ans, addr, MIN(strlen(addr), sizeof(ans)));
|
||||
memcpy(ans, addr, GLFW_MIN(strlen(addr), sizeof(ans)));
|
||||
return ans;
|
||||
}
|
||||
|
||||
@@ -193,39 +196,39 @@ get_ibus_address_file_name(void) {
|
||||
}
|
||||
|
||||
|
||||
static inline GLFWbool
|
||||
static inline bool
|
||||
read_ibus_address(_GLFWIBUSData *ibus) {
|
||||
static char buf[1024];
|
||||
struct stat s;
|
||||
FILE *addr_file = fopen(ibus->address_file_name, "r");
|
||||
if (!addr_file) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to open IBUS address file: %s with error: %s", ibus->address_file_name, strerror(errno));
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
int stat_result = fstat(fileno(addr_file), &s);
|
||||
GLFWbool found = GLFW_FALSE;
|
||||
bool found = false;
|
||||
while (fgets(buf, sizeof(buf), addr_file)) {
|
||||
if (strncmp(buf, "IBUS_ADDRESS=", sizeof("IBUS_ADDRESS=")-1) == 0) {
|
||||
size_t sz = strlen(buf);
|
||||
if (buf[sz-1] == '\n') buf[sz-1] = 0;
|
||||
if (buf[sz-2] == '\r') buf[sz-2] = 0;
|
||||
found = GLFW_TRUE;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(addr_file); addr_file = NULL;
|
||||
if (stat_result != 0) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to stat IBUS address file: %s with error: %s", ibus->address_file_name, strerror(errno));
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
ibus->address_file_mtime = s.st_mtime;
|
||||
if (found) {
|
||||
free((void*)ibus->address);
|
||||
ibus->address = _glfw_strdup(buf + sizeof("IBUS_ADDRESS=") - 1);
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Could not find IBUS_ADDRESS in %s", ibus->address_file_name);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -245,35 +248,35 @@ input_context_created(DBusMessage *msg, const char* errmsg, void *data) {
|
||||
dbus_connection_try_register_object_path(ibus->conn, ibus->input_ctx_path, &ibus_vtable, ibus, NULL);
|
||||
enum Capabilities caps = IBUS_CAP_FOCUS | IBUS_CAP_PREEDIT_TEXT;
|
||||
if (!glfw_dbus_call_method_no_reply(ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, "SetCapabilities", DBUS_TYPE_UINT32, &caps, DBUS_TYPE_INVALID)) return;
|
||||
ibus->ok = GLFW_TRUE;
|
||||
glfw_ibus_set_focused(ibus, GLFW_FALSE);
|
||||
ibus->ok = true;
|
||||
glfw_ibus_set_focused(ibus, false);
|
||||
glfw_ibus_set_cursor_geometry(ibus, 0, 0, 0, 0);
|
||||
debug("Connected to IBUS daemon for IME input management\n");
|
||||
}
|
||||
|
||||
GLFWbool
|
||||
bool
|
||||
setup_connection(_GLFWIBUSData *ibus) {
|
||||
const char *client_name = "GLFW_Application";
|
||||
const char *address_file_name = get_ibus_address_file_name();
|
||||
ibus->ok = GLFW_FALSE;
|
||||
if (!address_file_name) return GLFW_FALSE;
|
||||
ibus->ok = false;
|
||||
if (!address_file_name) return false;
|
||||
free((void*)ibus->address_file_name);
|
||||
ibus->address_file_name = _glfw_strdup(address_file_name);
|
||||
if (!read_ibus_address(ibus)) return GLFW_FALSE;
|
||||
if (!read_ibus_address(ibus)) return false;
|
||||
if (ibus->conn) {
|
||||
glfw_dbus_close_connection(ibus->conn);
|
||||
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", GLFW_FALSE);
|
||||
if (!ibus->conn) return GLFW_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(
|
||||
ibus->conn, IBUS_SERVICE, IBUS_PATH, IBUS_INTERFACE, "CreateInputContext", DBUS_TIMEOUT_USE_DEFAULT, input_context_created, ibus,
|
||||
DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID)) {
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -281,7 +284,7 @@ void
|
||||
glfw_connect_to_ibus(_GLFWIBUSData *ibus) {
|
||||
if (ibus->inited) return;
|
||||
if (!test_env_var("GLFW_IM_MODULE", "ibus")) return;
|
||||
ibus->inited = GLFW_TRUE;
|
||||
ibus->inited = true;
|
||||
setup_connection(ibus);
|
||||
}
|
||||
|
||||
@@ -297,21 +300,21 @@ glfw_ibus_terminate(_GLFWIBUSData *ibus) {
|
||||
F(address_file_name);
|
||||
#undef F
|
||||
|
||||
ibus->ok = GLFW_FALSE;
|
||||
ibus->ok = false;
|
||||
}
|
||||
|
||||
static GLFWbool
|
||||
static bool
|
||||
check_connection(_GLFWIBUSData *ibus) {
|
||||
if (!ibus->inited) return GLFW_FALSE;
|
||||
if (!ibus->inited) return false;
|
||||
if (ibus->conn && dbus_connection_get_is_connected(ibus->conn)) {
|
||||
return ibus->ok;
|
||||
}
|
||||
struct stat s;
|
||||
if (stat(ibus->address_file_name, &s) != 0 || s.st_mtime != ibus->address_file_mtime) {
|
||||
if (!read_ibus_address(ibus)) return GLFW_FALSE;
|
||||
if (!read_ibus_address(ibus)) return false;
|
||||
setup_connection(ibus);
|
||||
}
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -329,7 +332,7 @@ simple_message(_GLFWIBUSData *ibus, const char *method) {
|
||||
}
|
||||
|
||||
void
|
||||
glfw_ibus_set_focused(_GLFWIBUSData *ibus, GLFWbool focused) {
|
||||
glfw_ibus_set_focused(_GLFWIBUSData *ibus, bool focused) {
|
||||
simple_message(ibus, focused ? "FocusIn" : "FocusOut");
|
||||
}
|
||||
|
||||
@@ -386,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;
|
||||
}
|
||||
@@ -393,34 +397,39 @@ ibus_key_state(unsigned int glfw_modifiers, int action) {
|
||||
void
|
||||
key_event_processed(DBusMessage *msg, const char* errmsg, void *data) {
|
||||
uint32_t handled = 0;
|
||||
KeyEvent *ev = (KeyEvent*)data;
|
||||
GLFWbool is_release = ev->action == GLFW_RELEASE;
|
||||
GLFWbool failed = GLFW_FALSE;
|
||||
_GLFWIBUSKeyEvent *ev = (_GLFWIBUSKeyEvent*)data;
|
||||
// Restore key's text from the text embedded in the structure.
|
||||
ev->glfw_ev.text = ev->__embedded_text;
|
||||
bool is_release = ev->glfw_ev.action == GLFW_RELEASE;
|
||||
bool failed = false;
|
||||
if (errmsg) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "IBUS: Failed to process key with error: %s", errmsg);
|
||||
failed = GLFW_TRUE;
|
||||
failed = true;
|
||||
} else {
|
||||
glfw_dbus_get_args(msg, "Failed to get IBUS handled key from reply", DBUS_TYPE_BOOLEAN, &handled, DBUS_TYPE_INVALID);
|
||||
debug("IBUS processed scancode: 0x%x release: %d handled: %u\n", ev->keycode, is_release, handled);
|
||||
debug("IBUS processed native_key: 0x%x release: %d handled: %u\n", ev->glfw_ev.native_key, is_release, handled);
|
||||
}
|
||||
glfw_xkb_key_from_ime(ev, handled ? GLFW_TRUE : GLFW_FALSE, failed);
|
||||
glfw_xkb_key_from_ime(ev, handled ? true : false, failed);
|
||||
free(ev);
|
||||
}
|
||||
|
||||
GLFWbool
|
||||
ibus_process_key(const KeyEvent *ev_, _GLFWIBUSData *ibus) {
|
||||
if (!check_connection(ibus)) return GLFW_FALSE;
|
||||
KeyEvent *ev = malloc(sizeof(KeyEvent));
|
||||
if (!ev) return GLFW_FALSE;
|
||||
memcpy(ev, ev_, sizeof(KeyEvent));
|
||||
uint32_t state = ibus_key_state(ev->glfw_modifiers, ev->action);
|
||||
bool
|
||||
ibus_process_key(const _GLFWIBUSKeyEvent *ev_, _GLFWIBUSData *ibus) {
|
||||
if (!check_connection(ibus)) return false;
|
||||
_GLFWIBUSKeyEvent *ev = calloc(1, sizeof(_GLFWIBUSKeyEvent));
|
||||
if (!ev) return false;
|
||||
memcpy(ev, ev_, sizeof(_GLFWIBUSKeyEvent));
|
||||
// Put the key's text in a field IN the structure, for proper serialization.
|
||||
if (ev->glfw_ev.text) strncpy(ev->__embedded_text, ev->glfw_ev.text, sizeof(ev->__embedded_text) - 1);
|
||||
ev->glfw_ev.text = NULL;
|
||||
uint32_t state = ibus_key_state(ev->glfw_ev.mods, ev->glfw_ev.action);
|
||||
if (!glfw_dbus_call_method_with_reply(
|
||||
ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, "ProcessKeyEvent",
|
||||
3000, key_event_processed, ev,
|
||||
DBUS_TYPE_UINT32, &ev->ibus_sym, DBUS_TYPE_UINT32, &ev->ibus_keycode, DBUS_TYPE_UINT32,
|
||||
DBUS_TYPE_UINT32, &ev->ibus_keysym, DBUS_TYPE_UINT32, &ev->ibus_keycode, DBUS_TYPE_UINT32,
|
||||
&state, DBUS_TYPE_INVALID)) {
|
||||
free(ev);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
21
glfw/ibus_glfw.h
vendored
21
glfw/ibus_glfw.h
vendored
@@ -1,5 +1,5 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 XKB - www.glfw.org
|
||||
// GLFW 3.4 XKB - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2018 Kovid Goyal <kovid@kovidgoyal.net>
|
||||
//
|
||||
@@ -27,29 +27,28 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "internal.h"
|
||||
#include "dbus_glfw.h"
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
|
||||
typedef struct {
|
||||
GLFWbool ok, inited;
|
||||
bool ok, inited;
|
||||
time_t address_file_mtime;
|
||||
DBusConnection *conn;
|
||||
const char *input_ctx_path, *address_file_name, *address;
|
||||
} _GLFWIBUSData;
|
||||
|
||||
typedef struct {
|
||||
xkb_keycode_t keycode, ibus_keycode;
|
||||
xkb_keysym_t keysym, ibus_sym;
|
||||
unsigned int glfw_modifiers;
|
||||
int action;
|
||||
xkb_keycode_t ibus_keycode;
|
||||
xkb_keysym_t ibus_keysym;
|
||||
GLFWid window_id;
|
||||
int glfw_keycode;
|
||||
char text[64];
|
||||
} KeyEvent;
|
||||
GLFWkeyevent glfw_ev;
|
||||
char __embedded_text[64];
|
||||
} _GLFWIBUSKeyEvent;
|
||||
|
||||
void glfw_connect_to_ibus(_GLFWIBUSData *ibus);
|
||||
void glfw_ibus_terminate(_GLFWIBUSData *ibus);
|
||||
void glfw_ibus_set_focused(_GLFWIBUSData *ibus, GLFWbool focused);
|
||||
void glfw_ibus_set_focused(_GLFWIBUSData *ibus, bool focused);
|
||||
void glfw_ibus_dispatch(_GLFWIBUSData *ibus);
|
||||
GLFWbool ibus_process_key(const KeyEvent *ev_, _GLFWIBUSData *ibus);
|
||||
bool ibus_process_key(const _GLFWIBUSKeyEvent *ev_, _GLFWIBUSData *ibus);
|
||||
void glfw_ibus_set_cursor_geometry(_GLFWIBUSData *ibus, int x, int y, int w, int h);
|
||||
|
||||
80
glfw/init.c
vendored
80
glfw/init.c
vendored
@@ -1,8 +1,8 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 - www.glfw.org
|
||||
// GLFW 3.4 - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2002-2006 Marcus Geelnard
|
||||
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2006-2018 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -24,6 +24,8 @@
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
// Please use C89 style variable declarations in this file because VS 2010
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
#include "mappings.h"
|
||||
@@ -41,21 +43,21 @@
|
||||
|
||||
// Global state shared between compilation units of GLFW
|
||||
//
|
||||
_GLFWlibrary _glfw = { GLFW_FALSE };
|
||||
_GLFWlibrary _glfw = { false };
|
||||
|
||||
// These are outside of _glfw so they can be used before initialization and
|
||||
// after termination
|
||||
//
|
||||
static _GLFWerror _glfwMainThreadError;
|
||||
static GLFWerrorfun _glfwErrorCallback;
|
||||
static _GLFWinitconfig _glfwInitHints =
|
||||
{
|
||||
GLFW_TRUE, // hat buttons
|
||||
GLFW_FALSE, // debug keyboard
|
||||
GLFW_TRUE, // enable joystick
|
||||
{
|
||||
GLFW_TRUE, // macOS menu bar
|
||||
GLFW_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
|
||||
}
|
||||
};
|
||||
|
||||
@@ -90,9 +92,10 @@ static void terminate(void)
|
||||
_glfw.mappingCount = 0;
|
||||
|
||||
_glfwTerminateVulkan();
|
||||
_glfwPlatformTerminateJoysticks();
|
||||
_glfwPlatformTerminate();
|
||||
|
||||
_glfw.initialized = GLFW_FALSE;
|
||||
_glfw.initialized = false;
|
||||
|
||||
while (_glfw.errorListHead)
|
||||
{
|
||||
@@ -166,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");
|
||||
}
|
||||
@@ -199,7 +206,7 @@ _glfwDebug(const char *format, ...) {
|
||||
{
|
||||
va_list vl;
|
||||
|
||||
fprintf(stderr, "[%.4f] ", glfwGetTime());
|
||||
fprintf(stderr, "[%.4f] ", monotonic_t_to_s_double(glfwGetTime()));
|
||||
va_start(vl, format);
|
||||
vfprintf(stderr, format, vl);
|
||||
va_end(vl);
|
||||
@@ -212,10 +219,11 @@ _glfwDebug(const char *format, ...) {
|
||||
////// GLFW public API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWAPI int glfwInit(void)
|
||||
GLFWAPI int glfwInit(monotonic_t start_time)
|
||||
{
|
||||
if (_glfw.initialized)
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
monotonic_start_time = start_time;
|
||||
|
||||
memset(&_glfw, 0, sizeof(_glfw));
|
||||
_glfw.hints.init = _glfwInitHints;
|
||||
@@ -223,7 +231,7 @@ GLFWAPI int glfwInit(void)
|
||||
if (!_glfwPlatformInit())
|
||||
{
|
||||
terminate();
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_glfwPlatformCreateMutex(&_glfw.errorLock) ||
|
||||
@@ -231,13 +239,12 @@ GLFWAPI int glfwInit(void)
|
||||
!_glfwPlatformCreateTls(&_glfw.contextSlot))
|
||||
{
|
||||
terminate();
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
_glfwPlatformSetTls(&_glfw.errorSlot, &_glfwMainThreadError);
|
||||
|
||||
_glfw.initialized = GLFW_TRUE;
|
||||
_glfw.timer.offset = _glfwPlatformGetTimerValue();
|
||||
_glfw.initialized = true;
|
||||
|
||||
glfwDefaultWindowHints();
|
||||
|
||||
@@ -249,12 +256,12 @@ GLFWAPI int glfwInit(void)
|
||||
if (!glfwUpdateGamepadMappings(_glfwDefaultMappings[i]))
|
||||
{
|
||||
terminate();
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwTerminate(void)
|
||||
@@ -269,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;
|
||||
@@ -342,26 +352,36 @@ GLFWAPI void glfwRunMainLoop(GLFWtickcallback callback, void *data)
|
||||
_glfwPlatformRunMainLoop(callback, data);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwRequestTickCallback(void) {
|
||||
_glfwPlatformRequestTickCallback();
|
||||
}
|
||||
|
||||
GLFWAPI void glfwStopMainLoop(void) {
|
||||
_GLFW_REQUIRE_INIT();
|
||||
_glfwPlatformStopMainLoop();
|
||||
}
|
||||
|
||||
GLFWAPI unsigned long long glfwAddTimer(
|
||||
double interval, bool repeats, GLFWuserdatafun callback,
|
||||
monotonic_t interval, bool repeats, GLFWuserdatafun callback,
|
||||
void *callback_data, GLFWuserdatafun free_callback)
|
||||
{
|
||||
return _glfwPlatformAddTimer(interval, repeats, callback, callback_data, free_callback);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwUpdateTimer(unsigned long long timer_id, double interval, bool enabled) {
|
||||
GLFWAPI void glfwUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled) {
|
||||
_glfwPlatformUpdateTimer(timer_id, interval, enabled);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
631
glfw/input.c
vendored
631
glfw/input.c
vendored
@@ -1,8 +1,8 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 - www.glfw.org
|
||||
// GLFW 3.4 - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2002-2006 Marcus Geelnard
|
||||
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -24,8 +24,11 @@
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
// Please use C89 style variable declarations in this file because VS 2010
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <float.h>
|
||||
@@ -41,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)
|
||||
@@ -58,17 +77,17 @@ static _GLFWmapping* findMapping(const char* guid)
|
||||
|
||||
// Checks whether a gamepad mapping element is present in the hardware
|
||||
//
|
||||
static GLFWbool isValidElementForJoystick(const _GLFWmapelement* e,
|
||||
static bool isValidElementForJoystick(const _GLFWmapelement* e,
|
||||
const _GLFWjoystick* js)
|
||||
{
|
||||
if (e->type == _GLFW_JOYSTICK_HATBIT && (e->index >> 4) >= js->hatCount)
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
else if (e->type == _GLFW_JOYSTICK_BUTTON && e->index >= js->buttonCount)
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
else if (e->type == _GLFW_JOYSTICK_AXIS && e->index >= js->axisCount)
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Finds a mapping based on joystick GUID and verifies element indices
|
||||
@@ -110,7 +129,7 @@ static _GLFWmapping* findValidMapping(const _GLFWjoystick* js)
|
||||
|
||||
// Parses an SDL_GameControllerDB line and adds it to the mapping list
|
||||
//
|
||||
static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
static bool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
{
|
||||
const char* c = string;
|
||||
size_t i, length;
|
||||
@@ -148,7 +167,7 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
if (length != 32 || c[length] != ',')
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_VALUE, NULL);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(mapping->guid, c, length);
|
||||
@@ -158,7 +177,7 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
if (length >= sizeof(mapping->name) || c[length] != ',')
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_VALUE, NULL);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(mapping->name, c, length);
|
||||
@@ -168,7 +187,7 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
{
|
||||
// TODO: Implement output modifiers
|
||||
if (*c == '+' || *c == '-')
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
|
||||
for (i = 0; i < sizeof(fields) / sizeof(fields[0]); i++)
|
||||
{
|
||||
@@ -229,7 +248,7 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
{
|
||||
length = strlen(_GLFW_PLATFORM_MAPPING_NAME);
|
||||
if (strncmp(c, _GLFW_PLATFORM_MAPPING_NAME, length) != 0)
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -246,7 +265,7 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
}
|
||||
|
||||
_glfwPlatformUpdateGamepadGUID(mapping->guid);
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -254,42 +273,81 @@ static GLFWbool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
////// GLFW event API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Notifies shared code of a key event
|
||||
//
|
||||
void _glfwInputKeyboard(_GLFWwindow* window, int key, int scancode, int action, int mods, const char* text, int state)
|
||||
{
|
||||
if (key >= 0 && key <= GLFW_KEY_LAST)
|
||||
{
|
||||
GLFWbool repeated = GLFW_FALSE;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (action == GLFW_RELEASE && window->keys[key] == GLFW_RELEASE)
|
||||
// Notifies shared code of a physical key event
|
||||
//
|
||||
void _glfwInputKeyboard(_GLFWwindow* window, GLFWkeyevent* ev)
|
||||
{
|
||||
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 && current_action == GLFW_RELEASE)
|
||||
return;
|
||||
|
||||
if (action == GLFW_PRESS && window->keys[key] == GLFW_PRESS)
|
||||
repeated = GLFW_TRUE;
|
||||
if (ev->action == GLFW_PRESS && current_action == GLFW_PRESS)
|
||||
repeated = true;
|
||||
|
||||
if (action == GLFW_RELEASE && window->stickyKeys)
|
||||
window->keys[key] = _GLFW_STICK;
|
||||
if (ev->action == GLFW_RELEASE && window->stickyKeys)
|
||||
set_key_action(window, ev->key, _GLFW_STICK, idx);
|
||||
else
|
||||
window->keys[key] = (char) action;
|
||||
set_key_action(window, ev->key, ev->action, idx);
|
||||
|
||||
if (repeated)
|
||||
action = GLFW_REPEAT;
|
||||
ev->action = GLFW_REPEAT;
|
||||
}
|
||||
|
||||
|
||||
// FIXME: will need to update ev->virtual_mods here too?
|
||||
if (window->callbacks.keyboard) {
|
||||
if (!window->lockKeyMods) mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
|
||||
window->callbacks.keyboard((GLFWwindow*) window, key, scancode, action, mods, text, state);
|
||||
if (!window->lockKeyMods) ev->mods &= ~(GLFW_MOD_CAPS_LOCK | GLFW_MOD_NUM_LOCK);
|
||||
window->callbacks.keyboard((GLFWwindow*) window, 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
|
||||
@@ -328,7 +386,7 @@ void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos)
|
||||
|
||||
// Notifies shared code of a cursor enter/leave event
|
||||
//
|
||||
void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered)
|
||||
void _glfwInputCursorEnter(_GLFWwindow* window, bool entered)
|
||||
{
|
||||
if (window->callbacks.cursorEnter)
|
||||
window->callbacks.cursorEnter((GLFWwindow*) window, entered);
|
||||
@@ -336,10 +394,11 @@ void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool 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
|
||||
@@ -406,10 +465,10 @@ _GLFWjoystick* _glfwAllocJoystick(const char* name,
|
||||
return NULL;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
js->present = GLFW_TRUE;
|
||||
js->present = true;
|
||||
js->name = _glfw_strdup(name);
|
||||
js->axes = calloc(axisCount, sizeof(float));
|
||||
js->buttons = calloc(buttonCount + hatCount * 4, 1);
|
||||
js->buttons = calloc(buttonCount + (size_t) hatCount * 4, 1);
|
||||
js->hats = calloc(hatCount, 1);
|
||||
js->axisCount = axisCount;
|
||||
js->buttonCount = buttonCount;
|
||||
@@ -432,148 +491,169 @@ 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_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_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_0: return "0";
|
||||
case GLFW_KEY_SPACE: return "SPACE";
|
||||
case GLFW_KEY_MINUS: return "MINUS";
|
||||
case GLFW_KEY_EQUAL: return "EQUAL";
|
||||
case GLFW_KEY_LEFT_BRACKET: return "LEFT BRACKET";
|
||||
case GLFW_KEY_RIGHT_BRACKET: return "RIGHT BRACKET";
|
||||
case GLFW_KEY_BACKSLASH: return "BACKSLASH";
|
||||
case GLFW_KEY_SEMICOLON: return "SEMICOLON";
|
||||
case GLFW_KEY_APOSTROPHE: return "APOSTROPHE";
|
||||
case GLFW_KEY_GRAVE_ACCENT: return "GRAVE ACCENT";
|
||||
case GLFW_KEY_COMMA: return "COMMA";
|
||||
case GLFW_KEY_PERIOD: return "PERIOD";
|
||||
case GLFW_KEY_SLASH: return "SLASH";
|
||||
case GLFW_KEY_WORLD_1: return "WORLD 1";
|
||||
case GLFW_KEY_WORLD_2: return "WORLD 2";
|
||||
case GLFW_KEY_PLUS: return "PLUS";
|
||||
|
||||
// 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 MULTPLY";
|
||||
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 middle of the content area of the specified window
|
||||
// Center the cursor in the content area of the specified window
|
||||
//
|
||||
void _glfwCenterCursorInContentArea(_GLFWwindow* window)
|
||||
{
|
||||
int width, height;
|
||||
|
||||
_glfwPlatformGetWindowSize(window, &width, &height);
|
||||
_glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW public API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -595,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);
|
||||
@@ -632,19 +714,21 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
|
||||
}
|
||||
else if (mode == GLFW_STICKY_KEYS)
|
||||
{
|
||||
value = value ? GLFW_TRUE : GLFW_FALSE;
|
||||
value = value ? true : false;
|
||||
if (window->stickyKeys == value)
|
||||
return;
|
||||
|
||||
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]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,7 +736,7 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
|
||||
}
|
||||
else if (mode == GLFW_STICKY_MOUSE_BUTTONS)
|
||||
{
|
||||
value = value ? GLFW_TRUE : GLFW_FALSE;
|
||||
value = value ? true : false;
|
||||
if (window->stickyMouseButtons == value)
|
||||
return;
|
||||
|
||||
@@ -671,64 +755,79 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
|
||||
window->stickyMouseButtons = value;
|
||||
}
|
||||
else if (mode == GLFW_LOCK_KEY_MODS)
|
||||
window->lockKeyMods = value ? GLFW_TRUE : GLFW_FALSE;
|
||||
{
|
||||
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 scancode)
|
||||
GLFWAPI int glfwRawMouseMotionSupported(void)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
|
||||
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_WORLD_2))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
scancode = _glfwPlatformGetKeyScancode(key);
|
||||
}
|
||||
|
||||
return _glfwPlatformGetScancodeName(scancode);
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(false);
|
||||
return _glfwPlatformRawMouseMotionSupported();
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetKeyScancode(int key)
|
||||
GLFWAPI const char* glfwGetKeyName(uint32_t key, int native_key)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
if (key) return _glfwGetKeyName(key);
|
||||
|
||||
native_key = _glfwPlatformGetNativeKeyForKey(key);
|
||||
return _glfwPlatformGetNativeKeyName(native_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 _glfwPlatformGetKeyScancode(key);
|
||||
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)
|
||||
@@ -914,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
|
||||
@@ -987,17 +1086,20 @@ GLFWAPI int glfwJoystickPresent(int jid)
|
||||
assert(jid >= GLFW_JOYSTICK_1);
|
||||
assert(jid <= GLFW_JOYSTICK_LAST);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(false);
|
||||
|
||||
if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initJoysticks())
|
||||
return false;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
if (!js->present)
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
|
||||
return _glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE);
|
||||
}
|
||||
@@ -1020,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;
|
||||
@@ -1049,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;
|
||||
@@ -1082,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;
|
||||
@@ -1108,6 +1219,9 @@ GLFWAPI const char* glfwGetJoystickName(int jid)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!initJoysticks())
|
||||
return NULL;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
if (!js->present)
|
||||
return NULL;
|
||||
@@ -1133,6 +1247,9 @@ GLFWAPI const char* glfwGetJoystickGUID(int jid)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!initJoysticks())
|
||||
return NULL;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
if (!js->present)
|
||||
return NULL;
|
||||
@@ -1178,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;
|
||||
}
|
||||
@@ -1189,7 +1310,7 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string)
|
||||
|
||||
assert(string != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(false);
|
||||
|
||||
while (*c)
|
||||
{
|
||||
@@ -1239,7 +1360,7 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string)
|
||||
js->mapping = findValidMapping(js);
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
GLFWAPI int glfwJoystickIsGamepad(int jid)
|
||||
@@ -1249,20 +1370,23 @@ GLFWAPI int glfwJoystickIsGamepad(int jid)
|
||||
assert(jid >= GLFW_JOYSTICK_1);
|
||||
assert(jid <= GLFW_JOYSTICK_LAST);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(false);
|
||||
|
||||
if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initJoysticks())
|
||||
return false;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
if (!js->present)
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
|
||||
if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_PRESENCE))
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
|
||||
return js->mapping != NULL;
|
||||
}
|
||||
@@ -1282,6 +1406,9 @@ GLFWAPI const char* glfwGetGamepadName(int jid)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!initJoysticks())
|
||||
return NULL;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
if (!js->present)
|
||||
return NULL;
|
||||
@@ -1306,23 +1433,26 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
|
||||
|
||||
memset(state, 0, sizeof(GLFWgamepadstate));
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE);
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(false);
|
||||
|
||||
if (jid < 0 || jid > GLFW_JOYSTICK_LAST)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick ID %i", jid);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!initJoysticks())
|
||||
return false;
|
||||
|
||||
js = _glfw.joysticks + jid;
|
||||
if (!js->present)
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
|
||||
if (!_glfwPlatformPollJoystick(js, _GLFW_POLL_ALL))
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
|
||||
if (!js->mapping)
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
|
||||
for (i = 0; i <= GLFW_GAMEPAD_BUTTON_LAST; i++)
|
||||
{
|
||||
@@ -1330,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)
|
||||
{
|
||||
@@ -1358,15 +1498,17 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state)
|
||||
const unsigned int bit = e->index & 0xf;
|
||||
if (js->hats[hat] & bit)
|
||||
state->axes[i] = 1.f;
|
||||
else
|
||||
state->axes[i] = -1.f;
|
||||
}
|
||||
else if (e->type == _GLFW_JOYSTICK_BUTTON)
|
||||
state->axes[i] = (float) js->buttons[e->index];
|
||||
state->axes[i] = js->buttons[e->index] * 2.f - 1.f;
|
||||
}
|
||||
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string)
|
||||
GLFWAPI void glfwSetClipboardString(GLFWwindow* handle UNUSED, const char* string)
|
||||
{
|
||||
assert(string != NULL);
|
||||
|
||||
@@ -1374,14 +1516,14 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string)
|
||||
_glfwPlatformSetClipboardString(string);
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle)
|
||||
GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle UNUSED)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
return _glfwPlatformGetClipboardString();
|
||||
}
|
||||
|
||||
#if defined(_GLFW_X11) || defined(_GLFW_WAYLAND)
|
||||
GLFWAPI void glfwSetPrimarySelectionString(GLFWwindow* handle, const char* string)
|
||||
GLFWAPI void glfwSetPrimarySelectionString(GLFWwindow* handle UNUSED, const char* string)
|
||||
{
|
||||
assert(string != NULL);
|
||||
|
||||
@@ -1389,42 +1531,15 @@ GLFWAPI void glfwSetPrimarySelectionString(GLFWwindow* handle, const char* strin
|
||||
_glfwPlatformSetPrimarySelectionString(string);
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetPrimarySelectionString(GLFWwindow* handle)
|
||||
GLFWAPI const char* glfwGetPrimarySelectionString(GLFWwindow* handle UNUSED)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
return _glfwPlatformGetPrimarySelectionString();
|
||||
}
|
||||
#endif
|
||||
|
||||
GLFWAPI double glfwGetTime(void)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(0.0);
|
||||
return (double) (_glfwPlatformGetTimerValue() - _glfw.timer.offset) /
|
||||
_glfwPlatformGetTimerFrequency();
|
||||
}
|
||||
|
||||
GLFWAPI void glfwSetTime(double time)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT();
|
||||
|
||||
if (time != time || time < 0.0 || time > 18446744073.0)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", time);
|
||||
return;
|
||||
}
|
||||
|
||||
_glfw.timer.offset = _glfwPlatformGetTimerValue() -
|
||||
(uint64_t) (time * _glfwPlatformGetTimerFrequency());
|
||||
}
|
||||
|
||||
GLFWAPI uint64_t glfwGetTimerValue(void)
|
||||
GLFWAPI monotonic_t glfwGetTime(void)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(0);
|
||||
return _glfwPlatformGetTimerValue();
|
||||
}
|
||||
|
||||
GLFWAPI uint64_t glfwGetTimerFrequency(void)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(0);
|
||||
return _glfwPlatformGetTimerFrequency();
|
||||
return monotonic();
|
||||
}
|
||||
|
||||
235
glfw/internal.h
vendored
235
glfw/internal.h
vendored
@@ -1,8 +1,8 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 - www.glfw.org
|
||||
// GLFW 3.4 - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2002-2006 Marcus Geelnard
|
||||
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#if defined(_GLFW_USE_CONFIG_H)
|
||||
#include "glfw_config.h"
|
||||
#endif
|
||||
@@ -60,7 +62,6 @@
|
||||
|
||||
#define _GLFW_MESSAGE_SIZE 1024
|
||||
|
||||
typedef bool GLFWbool;
|
||||
typedef unsigned long long GLFWid;
|
||||
|
||||
typedef struct _GLFWerror _GLFWerror;
|
||||
@@ -131,6 +132,7 @@ typedef enum VkStructureType
|
||||
VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000,
|
||||
VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000,
|
||||
VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000,
|
||||
VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT = 1000217000,
|
||||
VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
|
||||
} VkStructureType;
|
||||
|
||||
@@ -196,6 +198,16 @@ 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)) { \
|
||||
memmove((array) + (i), (array) + (i) + 1, sizeof((array)[0]) * ((count) - (i))); \
|
||||
}}
|
||||
|
||||
|
||||
// Constructs a version number string from the public header macros
|
||||
#define _GLFW_CONCAT_VERSION(m, n, r) #m "." #n "." #r
|
||||
#define _GLFW_MAKE_VERSION(m, n, r) _GLFW_CONCAT_VERSION(m, n, r)
|
||||
@@ -219,12 +231,32 @@ typedef void (APIENTRY * PFN_vkVoidFunction)(void);
|
||||
|
||||
// Swaps the provided pointers
|
||||
#define _GLFW_SWAP_POINTERS(x, y) \
|
||||
{ \
|
||||
void* t; \
|
||||
do{ \
|
||||
__typeof__(x) t; \
|
||||
t = x; \
|
||||
x = y; \
|
||||
y = t; \
|
||||
}
|
||||
}while(0)
|
||||
|
||||
|
||||
// Suppress some pedantic warnings
|
||||
#ifdef __clang__
|
||||
#define START_ALLOW_CASE_RANGE _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wpedantic\"")
|
||||
#define END_ALLOW_CASE_RANGE _Pragma("clang diagnostic pop")
|
||||
#define ALLOW_UNUSED_RESULT _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wunused-result\"")
|
||||
#define END_ALLOW_UNUSED_RESULT _Pragma("clang diagnostic pop")
|
||||
#else
|
||||
#define START_ALLOW_CASE_RANGE _Pragma("GCC diagnostic ignored \"-Wpedantic\"")
|
||||
#define END_ALLOW_CASE_RANGE _Pragma("GCC diagnostic pop")
|
||||
#define ALLOW_UNUSED_RESULT _Pragma("GCC diagnostic ignored \"-Wunused-result\"")
|
||||
#define END_ALLOW_UNUSED_RESULT _Pragma("GCC diagnostic pop")
|
||||
#endif
|
||||
|
||||
// dlsym that works with -Wpedantic
|
||||
#define glfw_dlsym(dest, handle, name) do {*(void **)&(dest) = _glfw_dlsym(handle, name);}while (0)
|
||||
|
||||
// Mark function arguments as unused
|
||||
#define UNUSED __attribute__ ((unused))
|
||||
|
||||
// Per-thread error structure
|
||||
//
|
||||
@@ -241,12 +273,13 @@ struct _GLFWerror
|
||||
//
|
||||
struct _GLFWinitconfig
|
||||
{
|
||||
GLFWbool hatButtons;
|
||||
GLFWbool debugKeyboard;
|
||||
GLFWbool enableJoysticks;
|
||||
bool hatButtons;
|
||||
int angleType;
|
||||
bool debugKeyboard;
|
||||
bool debugRendering;
|
||||
struct {
|
||||
GLFWbool menubar;
|
||||
GLFWbool chdir;
|
||||
bool menubar;
|
||||
bool chdir;
|
||||
} ns;
|
||||
};
|
||||
|
||||
@@ -261,18 +294,19 @@ struct _GLFWwndconfig
|
||||
int width;
|
||||
int height;
|
||||
const char* title;
|
||||
GLFWbool resizable;
|
||||
GLFWbool visible;
|
||||
GLFWbool decorated;
|
||||
GLFWbool focused;
|
||||
GLFWbool autoIconify;
|
||||
GLFWbool floating;
|
||||
GLFWbool maximized;
|
||||
GLFWbool centerCursor;
|
||||
GLFWbool focusOnShow;
|
||||
GLFWbool scaleToMonitor;
|
||||
bool resizable;
|
||||
bool visible;
|
||||
bool decorated;
|
||||
bool focused;
|
||||
bool autoIconify;
|
||||
bool floating;
|
||||
bool maximized;
|
||||
bool centerCursor;
|
||||
bool focusOnShow;
|
||||
bool mousePassthrough;
|
||||
bool scaleToMonitor;
|
||||
struct {
|
||||
GLFWbool retina;
|
||||
bool retina;
|
||||
char frameName[256];
|
||||
} ns;
|
||||
struct {
|
||||
@@ -296,15 +330,15 @@ struct _GLFWctxconfig
|
||||
int source;
|
||||
int major;
|
||||
int minor;
|
||||
GLFWbool forward;
|
||||
GLFWbool debug;
|
||||
GLFWbool noerror;
|
||||
bool forward;
|
||||
bool debug;
|
||||
bool noerror;
|
||||
int profile;
|
||||
int robustness;
|
||||
int release;
|
||||
_GLFWwindow* share;
|
||||
struct {
|
||||
GLFWbool offline;
|
||||
bool offline;
|
||||
} nsgl;
|
||||
};
|
||||
|
||||
@@ -329,11 +363,11 @@ struct _GLFWfbconfig
|
||||
int accumBlueBits;
|
||||
int accumAlphaBits;
|
||||
int auxBuffers;
|
||||
GLFWbool stereo;
|
||||
bool stereo;
|
||||
int samples;
|
||||
GLFWbool sRGB;
|
||||
GLFWbool doublebuffer;
|
||||
GLFWbool transparent;
|
||||
bool sRGB;
|
||||
bool doublebuffer;
|
||||
bool transparent;
|
||||
uintptr_t handle;
|
||||
};
|
||||
|
||||
@@ -344,7 +378,7 @@ struct _GLFWcontext
|
||||
int client;
|
||||
int source;
|
||||
int major, minor, revision;
|
||||
GLFWbool forward, debug, noerror;
|
||||
bool forward, debug, noerror;
|
||||
int profile;
|
||||
int robustness;
|
||||
int release;
|
||||
@@ -361,13 +395,18 @@ struct _GLFWcontext
|
||||
_GLFWdestroycontextfun destroy;
|
||||
|
||||
// This is defined in the context API's context.h
|
||||
_GLFW_PLATFORM_CONTEXT_STATE;
|
||||
_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
|
||||
@@ -375,12 +414,13 @@ struct _GLFWwindow
|
||||
struct _GLFWwindow* next;
|
||||
|
||||
// Window settings and state
|
||||
GLFWbool resizable;
|
||||
GLFWbool decorated;
|
||||
GLFWbool autoIconify;
|
||||
GLFWbool floating;
|
||||
GLFWbool focusOnShow;
|
||||
GLFWbool shouldClose;
|
||||
bool resizable;
|
||||
bool decorated;
|
||||
bool autoIconify;
|
||||
bool floating;
|
||||
bool focusOnShow;
|
||||
bool mousePassthrough;
|
||||
bool shouldClose;
|
||||
void* userPointer;
|
||||
GLFWid id;
|
||||
GLFWvidmode videoMode;
|
||||
@@ -390,15 +430,17 @@ struct _GLFWwindow
|
||||
int minwidth, minheight;
|
||||
int maxwidth, maxheight;
|
||||
int numer, denom;
|
||||
int widthincr, heightincr;
|
||||
|
||||
GLFWbool stickyKeys;
|
||||
GLFWbool stickyMouseButtons;
|
||||
GLFWbool lockKeyMods;
|
||||
bool stickyKeys;
|
||||
bool stickyMouseButtons;
|
||||
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;
|
||||
|
||||
@@ -484,7 +526,7 @@ struct _GLFWmapping
|
||||
//
|
||||
struct _GLFWjoystick
|
||||
{
|
||||
GLFWbool present;
|
||||
bool present;
|
||||
float* axes;
|
||||
int axisCount;
|
||||
unsigned char* buttons;
|
||||
@@ -520,7 +562,7 @@ struct _GLFWmutex
|
||||
//
|
||||
struct _GLFWlibrary
|
||||
{
|
||||
GLFWbool initialized;
|
||||
bool initialized;
|
||||
|
||||
struct {
|
||||
_GLFWinitconfig init;
|
||||
@@ -538,6 +580,7 @@ struct _GLFWlibrary
|
||||
_GLFWmonitor** monitors;
|
||||
int monitorCount;
|
||||
|
||||
bool joysticksInitialized;
|
||||
_GLFWjoystick joysticks[GLFW_JOYSTICK_LAST + 1];
|
||||
_GLFWmapping* mappings;
|
||||
int mappingCount;
|
||||
@@ -547,47 +590,45 @@ struct _GLFWlibrary
|
||||
_GLFWmutex errorLock;
|
||||
|
||||
struct {
|
||||
uint64_t offset;
|
||||
// This is defined in the platform's time.h
|
||||
_GLFW_PLATFORM_LIBRARY_TIMER_STATE;
|
||||
} timer;
|
||||
|
||||
struct {
|
||||
GLFWbool available;
|
||||
bool available;
|
||||
void* handle;
|
||||
char* extensions[2];
|
||||
#if !defined(_GLFW_VULKAN_STATIC)
|
||||
PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties;
|
||||
PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
|
||||
#endif
|
||||
GLFWbool KHR_surface;
|
||||
bool KHR_surface;
|
||||
#if defined(_GLFW_WIN32)
|
||||
GLFWbool KHR_win32_surface;
|
||||
bool KHR_win32_surface;
|
||||
#elif defined(_GLFW_COCOA)
|
||||
GLFWbool MVK_macos_surface;
|
||||
bool MVK_macos_surface;
|
||||
bool EXT_metal_surface;
|
||||
#elif defined(_GLFW_X11)
|
||||
GLFWbool KHR_xlib_surface;
|
||||
GLFWbool KHR_xcb_surface;
|
||||
bool KHR_xlib_surface;
|
||||
bool KHR_xcb_surface;
|
||||
#elif defined(_GLFW_WAYLAND)
|
||||
GLFWbool KHR_wayland_surface;
|
||||
bool KHR_wayland_surface;
|
||||
#endif
|
||||
} vk;
|
||||
|
||||
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
|
||||
_GLFW_PLATFORM_LIBRARY_CONTEXT_STATE;
|
||||
_GLFW_PLATFORM_LIBRARY_CONTEXT_STATE
|
||||
// This is defined in the platform's joystick.h
|
||||
_GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE;
|
||||
_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
|
||||
@@ -606,14 +647,16 @@ 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);
|
||||
void _glfwPlatformDestroyCursor(_GLFWcursor* cursor);
|
||||
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor);
|
||||
|
||||
const char* _glfwPlatformGetScancodeName(int scancode);
|
||||
int _glfwPlatformGetKeyScancode(int key);
|
||||
const char* _glfwPlatformGetNativeKeyName(int native_key);
|
||||
int _glfwPlatformGetNativeKeyForKey(uint32_t key);
|
||||
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor);
|
||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos);
|
||||
@@ -622,7 +665,7 @@ void _glfwPlatformGetMonitorContentScale(_GLFWmonitor* monitor,
|
||||
void _glfwPlatformGetMonitorWorkarea(_GLFWmonitor* monitor, int* xpos, int* ypos, int *width, int *height);
|
||||
GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count);
|
||||
void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode);
|
||||
GLFWbool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp);
|
||||
bool _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp);
|
||||
void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp);
|
||||
|
||||
void _glfwPlatformSetClipboardString(const char* string);
|
||||
@@ -632,12 +675,11 @@ 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);
|
||||
|
||||
uint64_t _glfwPlatformGetTimerValue(void);
|
||||
uint64_t _glfwPlatformGetTimerFrequency(void);
|
||||
|
||||
int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
||||
const _GLFWwndconfig* wndconfig,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
@@ -654,6 +696,7 @@ void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window,
|
||||
int minwidth, int minheight,
|
||||
int maxwidth, int maxheight);
|
||||
void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom);
|
||||
void _glfwPlatformSetWindowSizeIncrements(_GLFWwindow* window, int widthincr, int heightincr);
|
||||
void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height);
|
||||
void _glfwInputLiveResize(_GLFWwindow* window, bool started);
|
||||
void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
|
||||
@@ -661,7 +704,7 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
|
||||
int* right, int* bottom);
|
||||
void _glfwPlatformGetWindowContentScale(_GLFWwindow* window,
|
||||
float* xscale, float* yscale);
|
||||
double _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window);
|
||||
monotonic_t _glfwPlatformGetDoubleClickInterval(_GLFWwindow* window);
|
||||
void _glfwPlatformIconifyWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformRestoreWindow(_GLFWwindow* window);
|
||||
void _glfwPlatformMaximizeWindow(_GLFWwindow* window);
|
||||
@@ -682,17 +725,23 @@ int _glfwPlatformWindowMaximized(_GLFWwindow* window);
|
||||
int _glfwPlatformWindowHovered(_GLFWwindow* window);
|
||||
int _glfwPlatformFramebufferTransparent(_GLFWwindow* window);
|
||||
float _glfwPlatformGetWindowOpacity(_GLFWwindow* window);
|
||||
void _glfwPlatformSetWindowResizable(_GLFWwindow* window, GLFWbool enabled);
|
||||
void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, GLFWbool enabled);
|
||||
void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled);
|
||||
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(double timeout);
|
||||
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,
|
||||
@@ -702,12 +751,12 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
const VkAllocationCallbacks* allocator,
|
||||
VkSurfaceKHR* surface);
|
||||
|
||||
GLFWbool _glfwPlatformCreateTls(_GLFWtls* tls);
|
||||
bool _glfwPlatformCreateTls(_GLFWtls* tls);
|
||||
void _glfwPlatformDestroyTls(_GLFWtls* tls);
|
||||
void* _glfwPlatformGetTls(_GLFWtls* tls);
|
||||
void _glfwPlatformSetTls(_GLFWtls* tls, void* value);
|
||||
|
||||
GLFWbool _glfwPlatformCreateMutex(_GLFWmutex* mutex);
|
||||
bool _glfwPlatformCreateMutex(_GLFWmutex* mutex);
|
||||
void _glfwPlatformDestroyMutex(_GLFWmutex* mutex);
|
||||
void _glfwPlatformLockMutex(_GLFWmutex* mutex);
|
||||
void _glfwPlatformUnlockMutex(_GLFWmutex* mutex);
|
||||
@@ -717,25 +766,25 @@ void _glfwPlatformUnlockMutex(_GLFWmutex* mutex);
|
||||
////// GLFW event API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused);
|
||||
void _glfwInputWindowOcclusion(_GLFWwindow* window, GLFWbool occluded);
|
||||
void _glfwInputWindowFocus(_GLFWwindow* window, bool focused);
|
||||
void _glfwInputWindowOcclusion(_GLFWwindow* window, bool occluded);
|
||||
void _glfwInputWindowPos(_GLFWwindow* window, int xpos, int ypos);
|
||||
void _glfwInputWindowSize(_GLFWwindow* window, int width, int height);
|
||||
void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height);
|
||||
void _glfwInputWindowContentScale(_GLFWwindow* window,
|
||||
float xscale, float yscale);
|
||||
void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified);
|
||||
void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized);
|
||||
void _glfwInputWindowIconify(_GLFWwindow* window, bool iconified);
|
||||
void _glfwInputWindowMaximize(_GLFWwindow* window, bool maximized);
|
||||
void _glfwInputWindowDamage(_GLFWwindow* window);
|
||||
void _glfwInputWindowCloseRequest(_GLFWwindow* window);
|
||||
void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
|
||||
|
||||
void _glfwInputKeyboard(_GLFWwindow* window, int key, int scancode, int action, int mods, const char* text, int state);
|
||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags);
|
||||
void _glfwInputKeyboard(_GLFWwindow *window, GLFWkeyevent *ev);
|
||||
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, GLFWbool entered);
|
||||
void _glfwInputDrop(_GLFWwindow* window, int count, const char** names);
|
||||
void _glfwInputCursorEnter(_GLFWwindow* window, bool entered);
|
||||
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);
|
||||
@@ -764,13 +813,10 @@ void _glfwDebug(const char* format, ...);
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions);
|
||||
const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired,
|
||||
const _GLFWfbconfig* alternatives,
|
||||
unsigned int count);
|
||||
GLFWbool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
bool _glfwStringInExtensionString(const char* string, const char* extensions);
|
||||
bool _glfwRefreshContextAttribs(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig);
|
||||
GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig);
|
||||
bool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig);
|
||||
|
||||
const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor,
|
||||
const GLFWvidmode* desired);
|
||||
@@ -790,16 +836,15 @@ void _glfwFreeJoystick(_GLFWjoystick* js);
|
||||
const char* _glfwGetKeyName(int key);
|
||||
void _glfwCenterCursorInContentArea(_GLFWwindow* window);
|
||||
|
||||
GLFWbool _glfwInitVulkan(int mode);
|
||||
bool _glfwInitVulkan(int mode);
|
||||
void _glfwTerminateVulkan(void);
|
||||
const char* _glfwGetVulkanResultString(VkResult result);
|
||||
_GLFWwindow* _glfwFocusedWindow(void);
|
||||
_GLFWwindow* _glfwWindowForId(GLFWid id);
|
||||
void _glfwPlatformRunMainLoop(GLFWtickcallback, void*);
|
||||
void _glfwPlatformRequestTickCallback(void);
|
||||
void _glfwPlatformStopMainLoop(void);
|
||||
unsigned long long _glfwPlatformAddTimer(double interval, bool repeats, GLFWuserdatafun callback, void *callback_data, GLFWuserdatafun free_callback);
|
||||
void _glfwPlatformUpdateTimer(unsigned long long timer_id, double interval, GLFWbool enabled);
|
||||
unsigned long long _glfwPlatformAddTimer(monotonic_t interval, bool repeats, GLFWuserdatafun callback, void *callback_data, GLFWuserdatafun free_callback);
|
||||
void _glfwPlatformUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled);
|
||||
void _glfwPlatformRemoveTimer(unsigned long long timer_id);
|
||||
|
||||
char* _glfw_strdup(const char* source);
|
||||
|
||||
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);
|
||||
186
glfw/linux_joystick.c
vendored
186
glfw/linux_joystick.c
vendored
@@ -1,8 +1,8 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 Linux - www.glfw.org
|
||||
// GLFW 3.4 Linux - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2002-2006 Marcus Geelnard
|
||||
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2006-2017 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -24,6 +24,8 @@
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
// It is fine to use C99 in this file because it will not be built with VS
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@@ -104,9 +106,7 @@ static void handleAbsEvent(_GLFWjoystick* js, int code, int value)
|
||||
//
|
||||
static void pollAbsState(_GLFWjoystick* js)
|
||||
{
|
||||
int code;
|
||||
|
||||
for (code = 0; code < ABS_CNT; code++)
|
||||
for (int code = 0; code < ABS_CNT; code++)
|
||||
{
|
||||
if (js->linjs.absMap[code] < 0)
|
||||
continue;
|
||||
@@ -124,53 +124,52 @@ static void pollAbsState(_GLFWjoystick* js)
|
||||
|
||||
// Attempt to open the specified joystick device
|
||||
//
|
||||
static GLFWbool openJoystickDevice(const char* path)
|
||||
static bool openJoystickDevice(const char* path)
|
||||
{
|
||||
int jid, code;
|
||||
char name[256] = "";
|
||||
char guid[33] = "";
|
||||
char evBits[(EV_CNT + 7) / 8] = {0};
|
||||
char keyBits[(KEY_CNT + 7) / 8] = {0};
|
||||
char absBits[(ABS_CNT + 7) / 8] = {0};
|
||||
int axisCount = 0, buttonCount = 0, hatCount = 0;
|
||||
struct input_id id;
|
||||
_GLFWjoystickLinux linjs = {0};
|
||||
_GLFWjoystick* js = NULL;
|
||||
|
||||
for (jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
|
||||
for (int jid = 0; jid <= GLFW_JOYSTICK_LAST; jid++)
|
||||
{
|
||||
if (!_glfw.joysticks[jid].present)
|
||||
continue;
|
||||
if (strcmp(_glfw.joysticks[jid].linjs.path, path) == 0)
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
_GLFWjoystickLinux linjs = {0};
|
||||
linjs.fd = open(path, O_RDONLY | O_NONBLOCK);
|
||||
if (linjs.fd == -1)
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
|
||||
if (ioctl(linjs.fd, EVIOCGBIT(0, sizeof(evBits)), evBits) < 0 ||
|
||||
ioctl(linjs.fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 ||
|
||||
ioctl(linjs.fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0 ||
|
||||
ioctl(linjs.fd, EVIOCGID, &id) < 0)
|
||||
char evBits[(EV_CNT + 7) / 8] = {0};
|
||||
char keyBits[(KEY_CNT + 7) / 8] = {0};
|
||||
char absBits[(ABS_CNT + 7) / 8] = {0};
|
||||
struct input_id id;
|
||||
|
||||
if (ioctl(linjs.fd, (int32_t)EVIOCGBIT(0, sizeof(evBits)), evBits) < 0 ||
|
||||
ioctl(linjs.fd, (int32_t)EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) < 0 ||
|
||||
ioctl(linjs.fd, (int32_t)EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) < 0 ||
|
||||
ioctl(linjs.fd, (int32_t)EVIOCGID, &id) < 0)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Linux: Failed to query input device: %s",
|
||||
strerror(errno));
|
||||
close(linjs.fd);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure this device supports the events expected of a joystick
|
||||
if (!isBitSet(EV_KEY, evBits) || !isBitSet(EV_ABS, evBits))
|
||||
{
|
||||
close(linjs.fd);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ioctl(linjs.fd, EVIOCGNAME(sizeof(name)), name) < 0)
|
||||
char name[256] = "";
|
||||
|
||||
if (ioctl(linjs.fd, (int32_t)EVIOCGNAME(sizeof(name)), name) < 0)
|
||||
strncpy(name, "Unknown", sizeof(name));
|
||||
|
||||
char guid[33] = "";
|
||||
|
||||
// Generate a joystick GUID that matches the SDL 2.0.5+ one
|
||||
if (id.vendor && id.product && id.version)
|
||||
{
|
||||
@@ -189,7 +188,9 @@ static GLFWbool openJoystickDevice(const char* path)
|
||||
name[8], name[9], name[10]);
|
||||
}
|
||||
|
||||
for (code = BTN_MISC; code < KEY_CNT; code++)
|
||||
int axisCount = 0, buttonCount = 0, hatCount = 0;
|
||||
|
||||
for (int code = BTN_MISC; code < KEY_CNT; code++)
|
||||
{
|
||||
if (!isBitSet(code, keyBits))
|
||||
continue;
|
||||
@@ -198,7 +199,7 @@ static GLFWbool openJoystickDevice(const char* path)
|
||||
buttonCount++;
|
||||
}
|
||||
|
||||
for (code = 0; code < ABS_CNT; code++)
|
||||
for (int code = 0; code < ABS_CNT; code++)
|
||||
{
|
||||
linjs.absMap[code] = -1;
|
||||
if (!isBitSet(code, absBits))
|
||||
@@ -221,11 +222,12 @@ static GLFWbool openJoystickDevice(const char* path)
|
||||
}
|
||||
}
|
||||
|
||||
js = _glfwAllocJoystick(name, guid, axisCount, buttonCount, hatCount);
|
||||
_GLFWjoystick* js =
|
||||
_glfwAllocJoystick(name, guid, axisCount, buttonCount, hatCount);
|
||||
if (!js)
|
||||
{
|
||||
close(linjs.fd);
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
strncpy(linjs.path, path, sizeof(linjs.path) - 1);
|
||||
@@ -234,7 +236,7 @@ static GLFWbool openJoystickDevice(const char* path)
|
||||
pollAbsState(js);
|
||||
|
||||
_glfwInputJoystick(js, GLFW_CONNECTED);
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef isBitSet
|
||||
@@ -262,12 +264,51 @@ static int compareJoysticks(const void* fp, const void* sp)
|
||||
////// GLFW internal API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialize joystick interface
|
||||
//
|
||||
GLFWbool _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)
|
||||
{
|
||||
DIR* dir;
|
||||
int count = 0;
|
||||
const char* dirname = "/dev/input";
|
||||
|
||||
_glfw.linjs.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||
@@ -286,10 +327,12 @@ GLFWbool _glfwInitJoysticksLinux(void)
|
||||
if (regcomp(&_glfw.linjs.regex, "^event[0-9]\\+$", 0) != 0)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Linux: Failed to compile regex");
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
dir = opendir(dirname);
|
||||
int count = 0;
|
||||
|
||||
DIR* dir = opendir(dirname);
|
||||
if (dir)
|
||||
{
|
||||
struct dirent* entry;
|
||||
@@ -314,13 +357,11 @@ GLFWbool _glfwInitJoysticksLinux(void)
|
||||
|
||||
// Continue with no joysticks if enumeration fails
|
||||
|
||||
qsort(_glfw.joysticks, count, sizeof(_GLFWjoystick), compareJoysticks);
|
||||
return GLFW_TRUE;
|
||||
qsort(_glfw.joysticks, count, sizeof(_glfw.joysticks[0]), compareJoysticks);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Close all opened joystick handles
|
||||
//
|
||||
void _glfwTerminateJoysticksLinux(void)
|
||||
void _glfwPlatformTerminateJoysticks(void)
|
||||
{
|
||||
int jid;
|
||||
|
||||
@@ -331,64 +372,17 @@ 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)
|
||||
{
|
||||
ssize_t offset = 0;
|
||||
char buffer[16384];
|
||||
|
||||
if (_glfw.linjs.inotify <= 0)
|
||||
return;
|
||||
|
||||
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)
|
||||
{
|
||||
int jid;
|
||||
|
||||
for (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)
|
||||
int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode UNUSED)
|
||||
{
|
||||
// Read all queued events (non-blocking)
|
||||
for (;;)
|
||||
@@ -408,10 +402,10 @@ int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode)
|
||||
if (e.type == EV_SYN)
|
||||
{
|
||||
if (e.code == SYN_DROPPED)
|
||||
_glfw.linjs.dropped = GLFW_TRUE;
|
||||
_glfw.linjs.dropped = true;
|
||||
else if (e.code == SYN_REPORT)
|
||||
{
|
||||
_glfw.linjs.dropped = GLFW_FALSE;
|
||||
_glfw.linjs.dropped = false;
|
||||
pollAbsState(js);
|
||||
}
|
||||
}
|
||||
@@ -428,6 +422,6 @@ int _glfwPlatformPollJoystick(_GLFWjoystick* js, int mode)
|
||||
return js->present;
|
||||
}
|
||||
|
||||
void _glfwPlatformUpdateGamepadGUID(char* guid)
|
||||
void _glfwPlatformUpdateGamepadGUID(char* guid UNUSED)
|
||||
{
|
||||
}
|
||||
|
||||
10
glfw/linux_joystick.h
vendored
10
glfw/linux_joystick.h
vendored
@@ -1,5 +1,5 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 Linux - www.glfw.org
|
||||
// GLFW 3.4 Linux - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>
|
||||
//
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <regex.h>
|
||||
|
||||
#define _GLFW_PLATFORM_JOYSTICK_STATE _GLFWjoystickLinux linjs
|
||||
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWlibraryLinux linjs
|
||||
#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWlibraryLinux linjs;
|
||||
|
||||
#define _GLFW_PLATFORM_MAPPING_NAME "Linux"
|
||||
|
||||
@@ -52,11 +52,7 @@ typedef struct _GLFWlibraryLinux
|
||||
int inotify;
|
||||
int watch;
|
||||
regex_t regex;
|
||||
GLFWbool dropped;
|
||||
bool dropped;
|
||||
} _GLFWlibraryLinux;
|
||||
|
||||
|
||||
GLFWbool _glfwInitJoysticksLinux(void);
|
||||
void _glfwTerminateJoysticksLinux(void);
|
||||
void _glfwDetectJoystickConnectionLinux(void);
|
||||
|
||||
|
||||
3
glfw/linux_notify.c
vendored
3
glfw/linux_notify.c
vendored
@@ -43,8 +43,7 @@ notification_created(DBusMessage *msg, const char* errmsg, void *data) {
|
||||
}
|
||||
|
||||
static DBusHandlerResult
|
||||
message_handler(DBusConnection *conn, DBusMessage *msg, void *user_data) {
|
||||
(void)(user_data);
|
||||
message_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data UNUSED) {
|
||||
/* printf("session_bus message_handler invoked interface: %s member: %s\n", dbus_message_get_interface(msg), dbus_message_get_member(msg)); */
|
||||
if (dbus_message_is_signal(msg, NOTIFICATIONS_IFACE, "ActionInvoked")) {
|
||||
uint32_t notification_id;
|
||||
|
||||
22
glfw/main_loop.h
vendored
22
glfw/main_loop.h
vendored
@@ -7,33 +7,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "internal.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#ifndef GLFW_LOOP_BACKEND
|
||||
#define GLFW_LOOP_BACKEND x11
|
||||
#endif
|
||||
|
||||
static GLFWbool keep_going = GLFW_FALSE;
|
||||
static bool keep_going = false;
|
||||
|
||||
void _glfwPlatformRequestTickCallback() {
|
||||
}
|
||||
|
||||
void _glfwPlatformStopMainLoop(void) {
|
||||
if (keep_going) {
|
||||
keep_going = GLFW_FALSE;
|
||||
keep_going = false;
|
||||
_glfwPlatformPostEmptyEvent();
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformRunMainLoop(GLFWtickcallback tick_callback, void* data) {
|
||||
keep_going = GLFW_TRUE;
|
||||
keep_going = 1;
|
||||
EventLoopData *eld = &_glfw.GLFW_LOOP_BACKEND.eventLoopData;
|
||||
while(keep_going) {
|
||||
_glfwPlatformWaitEvents();
|
||||
EVDBG("loop tick");
|
||||
tick_callback(data);
|
||||
EVDBG("--------- loop tick, wakeups_happened: %d ----------", eld->wakeup_data_read);
|
||||
if (eld->wakeup_data_read) {
|
||||
eld->wakeup_data_read = false;
|
||||
tick_callback(data);
|
||||
}
|
||||
}
|
||||
EVDBG("main loop exiting");
|
||||
}
|
||||
|
||||
unsigned long long _glfwPlatformAddTimer(double interval, bool repeats, GLFWuserdatafreefun callback, void *callback_data, GLFWuserdatafreefun free_callback) {
|
||||
unsigned long long _glfwPlatformAddTimer(monotonic_t interval, bool repeats, GLFWuserdatafreefun callback, void *callback_data, GLFWuserdatafreefun free_callback) {
|
||||
return addTimer(&_glfw.GLFW_LOOP_BACKEND.eventLoopData, "user timer", interval, 1, repeats, callback, callback_data, free_callback);
|
||||
}
|
||||
|
||||
@@ -41,7 +45,7 @@ void _glfwPlatformRemoveTimer(unsigned long long timer_id) {
|
||||
removeTimer(&_glfw.GLFW_LOOP_BACKEND.eventLoopData, timer_id);
|
||||
}
|
||||
|
||||
void _glfwPlatformUpdateTimer(unsigned long long timer_id, double interval, GLFWbool enabled) {
|
||||
void _glfwPlatformUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled) {
|
||||
changeTimerInterval(&_glfw.GLFW_LOOP_BACKEND.eventLoopData, timer_id, interval);
|
||||
toggleTimer(&_glfw.GLFW_LOOP_BACKEND.eventLoopData, timer_id, enabled);
|
||||
}
|
||||
|
||||
4
glfw/mappings.h
vendored
4
glfw/mappings.h
vendored
@@ -1,7 +1,7 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 - www.glfw.org
|
||||
// GLFW 3.4 - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2006-2018 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
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;
|
||||
|
||||
|
||||
38
glfw/monitor.c
vendored
38
glfw/monitor.c
vendored
@@ -1,8 +1,8 @@
|
||||
//========================================================================
|
||||
// GLFW 3.3 - www.glfw.org
|
||||
// GLFW 3.4 - www.glfw.org
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2002-2006 Marcus Geelnard
|
||||
// Copyright (c) 2006-2016 Camilla Löwy <elmindreda@glfw.org>
|
||||
// Copyright (c) 2006-2019 Camilla Löwy <elmindreda@glfw.org>
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -24,6 +24,8 @@
|
||||
// distribution.
|
||||
//
|
||||
//========================================================================
|
||||
// Please use C89 style variable declarations in this file because VS 2010
|
||||
//========================================================================
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@@ -54,31 +56,35 @@ static int compareVideoModes(const void* fp, const void* sp)
|
||||
if (farea != sarea)
|
||||
return farea - sarea;
|
||||
|
||||
// Then sort on width
|
||||
if (fm->width != sm->width)
|
||||
return fm->width - sm->width;
|
||||
|
||||
// Lastly sort on refresh rate
|
||||
return fm->refreshRate - sm->refreshRate;
|
||||
}
|
||||
|
||||
// Retrieves the available modes for the specified monitor
|
||||
//
|
||||
static GLFWbool refreshVideoModes(_GLFWmonitor* monitor)
|
||||
static bool refreshVideoModes(_GLFWmonitor* monitor)
|
||||
{
|
||||
int modeCount;
|
||||
GLFWvidmode* modes;
|
||||
|
||||
if (monitor->modes)
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
|
||||
modes = _glfwPlatformGetVideoModes(monitor, &modeCount);
|
||||
if (!modes)
|
||||
return GLFW_FALSE;
|
||||
return false;
|
||||
|
||||
qsort(modes, modeCount, sizeof(GLFWvidmode), compareVideoModes);
|
||||
qsort(modes, modeCount, sizeof(modes[0]), compareVideoModes);
|
||||
|
||||
free(monitor->modes);
|
||||
monitor->modes = modes;
|
||||
monitor->modeCount = modeCount;
|
||||
|
||||
return GLFW_TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,7 +106,7 @@ void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement)
|
||||
{
|
||||
memmove(_glfw.monitors + 1,
|
||||
_glfw.monitors,
|
||||
(_glfw.monitorCount - 1) * sizeof(_GLFWmonitor*));
|
||||
((size_t) _glfw.monitorCount - 1) * sizeof(_GLFWmonitor*));
|
||||
_glfw.monitors[0] = monitor;
|
||||
}
|
||||
else
|
||||
@@ -127,10 +133,7 @@ void _glfwInputMonitor(_GLFWmonitor* monitor, int action, int placement)
|
||||
{
|
||||
if (_glfw.monitors[i] == monitor)
|
||||
{
|
||||
_glfw.monitorCount--;
|
||||
memmove(_glfw.monitors + i,
|
||||
_glfw.monitors + i + 1,
|
||||
(_glfw.monitorCount - i) * sizeof(_GLFWmonitor*));
|
||||
remove_i_from_array(_glfw.monitors, i, _glfw.monitorCount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -330,7 +333,9 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos)
|
||||
_glfwPlatformGetMonitorPos(monitor, xpos, ypos);
|
||||
}
|
||||
|
||||
GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* handle, int* xpos, int* ypos, int* width, int* height)
|
||||
GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* handle,
|
||||
int* xpos, int* ypos,
|
||||
int* width, int* height)
|
||||
{
|
||||
_GLFWmonitor* monitor = (_GLFWmonitor*) handle;
|
||||
assert(monitor != NULL);
|
||||
@@ -451,7 +456,7 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma)
|
||||
GLFWgammaramp ramp;
|
||||
const GLFWgammaramp* original;
|
||||
assert(handle != NULL);
|
||||
assert(gamma >= 0.f);
|
||||
assert(gamma > 0.f);
|
||||
assert(gamma <= FLT_MAX);
|
||||
|
||||
_GLFW_REQUIRE_INIT();
|
||||
@@ -461,6 +466,7 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma)
|
||||
_glfwInputError(GLFW_INVALID_VALUE, "Invalid gamma value %f", gamma);
|
||||
return;
|
||||
}
|
||||
|
||||
original = glfwGetGammaRamp(handle);
|
||||
if (!original)
|
||||
return;
|
||||
@@ -475,10 +481,8 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma)
|
||||
value = i / (float) (original->size - 1);
|
||||
// Apply gamma curve
|
||||
value = powf(value, 1.f / gamma) * 65535.f + 0.5f;
|
||||
|
||||
// Clamp to value range
|
||||
if (value > 65535.f)
|
||||
value = 65535.f;
|
||||
value = fminf(value, 65535.f);
|
||||
|
||||
values[i] = (unsigned short) value;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user