Compare commits
755 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 |
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,6 +1,5 @@
|
||||
kitty/wcwidth-std.h linguist-generated=true
|
||||
kitty/emoji.h linguist-generated=true
|
||||
kitty/keys.h linguist-generated=true
|
||||
kitty/charsets.c linguist-generated=true
|
||||
kitty/key_encoding.py linguist-generated=true
|
||||
kitty/unicode-data.c linguist-generated=true
|
||||
@@ -11,3 +10,6 @@ kitty/parse-graphics-command.h linguist-generated=true
|
||||
glfw/*.c linguist-vendored=true
|
||||
glfw/*.h linguist-vendored=true
|
||||
kittens/unicode_input/names.h linguist-generated=true
|
||||
|
||||
*.py text diff=python
|
||||
*.m text diff=objc
|
||||
|
||||
12
.github/workflows/ci.py
vendored
12
.github/workflows/ci.py
vendored
@@ -35,12 +35,18 @@ def install_deps():
|
||||
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'
|
||||
' libpng-dev liblcms2-dev libfontconfig-dev libxkbcommon-x11-dev libcanberra-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:
|
||||
run('pip3 install Pillow pygments')
|
||||
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():
|
||||
|
||||
@@ -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://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.
|
||||
14
__main__.py
14
__main__.py
@@ -30,8 +30,14 @@ def runpy(args: List[str]) -> None:
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@@ -65,6 +71,11 @@ def run_kitten(args: List[str]) -> None:
|
||||
rk(kitten)
|
||||
|
||||
|
||||
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:])
|
||||
@@ -77,6 +88,7 @@ entry_points = {
|
||||
'runpy': runpy,
|
||||
'launch': launch,
|
||||
'kitten': run_kitten,
|
||||
'edit-config': edit_config_file,
|
||||
|
||||
'@': remote_control,
|
||||
'+': namespaced,
|
||||
|
||||
@@ -4,15 +4,17 @@
|
||||
|
||||
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
|
||||
from bypy.utils import run_shell, walk
|
||||
|
||||
|
||||
def read_src_file(name):
|
||||
@@ -40,52 +42,66 @@ def run(*args, **extra_env):
|
||||
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'))
|
||||
ignore=shutil.ignore_patterns('b', 'build', 'dist', '*_commands.json', '*.o', '*.so', '*.dylib', '*.pyd'))
|
||||
|
||||
# Build the launcher as it is needed for the spawn test
|
||||
with suppress(FileNotFoundError):
|
||||
os.remove(os.path.join(writeable_src_dir, 'kitty', 'launcher', 'kitty'))
|
||||
if run(PYTHON, 'setup.py', 'build-launcher', cwd=writeable_src_dir) != 0:
|
||||
print('Building of kitty launcher failed', file=sys.stderr)
|
||||
os.chdir(KITTY_DIR)
|
||||
run_shell()
|
||||
raise SystemExit('Building of kitty launcher failed')
|
||||
os.unlink(os.path.join(writeable_src_dir, 'kitty', 'launcher', 'kitty'))
|
||||
|
||||
cmd = [PYTHON, 'setup.py']
|
||||
bundle = 'macos-freeze' if ismacos else 'linux-freeze'
|
||||
cmd.append(bundle)
|
||||
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 = os.path.join(ext_dir, dest)
|
||||
cmd += ['--prefix', dest]
|
||||
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(KITTY_DIR)
|
||||
os.chdir(writeable_src_dir)
|
||||
run_shell()
|
||||
raise SystemExit('Building of kitty package failed')
|
||||
return ext_dir
|
||||
|
||||
|
||||
def run_tests(path_to_kitty, cwd_on_failure):
|
||||
kw = {'cwd': cwd_on_failure}
|
||||
if not ismacos:
|
||||
# this is needed for the spawn test which starts an interpreter
|
||||
# using the kitty launcher.
|
||||
kw['PYTHONHOME'] = PREFIX
|
||||
ret = run(PYTHON, 'test.py', **kw)
|
||||
if ret != 0:
|
||||
os.chdir(cwd_on_failure)
|
||||
print(
|
||||
'running kitty tests failed with return code:', ret, file=sys.stderr)
|
||||
run_shell()
|
||||
raise SystemExit('running kitty tests failed')
|
||||
|
||||
|
||||
if __name__ == 'program':
|
||||
kitty_constants = initialize_constants()
|
||||
|
||||
@@ -13,12 +13,14 @@ import time
|
||||
from bypy.constants import (
|
||||
OUTPUT_DIR, PREFIX, is64bit, python_major_minor_version
|
||||
)
|
||||
from bypy.utils import get_dll_path, py_compile, walk
|
||||
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
|
||||
self_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
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']
|
||||
@@ -26,14 +28,10 @@ kitty_constants = iv['kitty_constants']
|
||||
|
||||
def binary_includes():
|
||||
return tuple(map(get_dll_path, (
|
||||
'expat', 'sqlite3', 'ffi', 'z', 'lzma', 'png16', 'lcms2',
|
||||
|
||||
# dont include freetype because fontconfig is closely coupled to it
|
||||
# and also distros often patch freetype
|
||||
# 'iconv.so.2', 'pcre.so.1', 'graphite2.so.3', 'glib-2.0.so.0', 'freetype.so.6',
|
||||
|
||||
'expat', 'sqlite3', 'ffi', 'z', 'lzma', 'png16', 'lcms2', 'crypt',
|
||||
'iconv', 'pcre', 'graphite2', 'glib-2.0', 'freetype',
|
||||
'harfbuzz', 'xkbcommon', 'xkbcommon-x11',
|
||||
'ncursesw', 'readline',
|
||||
'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),
|
||||
@@ -48,6 +46,7 @@ class Env:
|
||||
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):
|
||||
@@ -75,7 +74,7 @@ def import_site_packages(srcdir, dest):
|
||||
if ext in ('py', 'so'):
|
||||
shutil.copy2(f, dest)
|
||||
elif ext == 'pth' and x != 'setuptools.pth':
|
||||
for line in open(f, 'rb').read().splitlines():
|
||||
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)
|
||||
@@ -105,10 +104,31 @@ def copy_python(env):
|
||||
shutil.copy2(y, env.py_dir)
|
||||
|
||||
srcdir = j(srcdir, 'site-packages')
|
||||
dest = j(env.py_dir, 'site-packages')
|
||||
import_site_packages(srcdir, dest)
|
||||
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)
|
||||
py_compile(os.path.join(env.base, 'lib', 'kitty'))
|
||||
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):
|
||||
@@ -192,16 +212,16 @@ def create_tarfile(env, compression_level='9'):
|
||||
def main():
|
||||
args = globals()['args']
|
||||
ext_dir = globals()['ext_dir']
|
||||
if not args.skip_tests:
|
||||
run_tests = iv['run_tests']
|
||||
run_tests(None, os.path.join(ext_dir, 'src'))
|
||||
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)
|
||||
|
||||
|
||||
|
||||
@@ -13,16 +13,23 @@ 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, py_compile, run_shell, timeit, walk
|
||||
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']
|
||||
@@ -134,8 +141,9 @@ class Freeze(object):
|
||||
|
||||
FID = '@executable_path/../Frameworks'
|
||||
|
||||
def __init__(self, build_dir, dont_strip=False, sign_installers=False, notarize=False):
|
||||
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
|
||||
@@ -147,6 +155,7 @@ class Freeze(object):
|
||||
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()
|
||||
|
||||
@@ -160,10 +169,11 @@ class Freeze(object):
|
||||
self.add_site_packages()
|
||||
self.add_stdlib()
|
||||
self.add_misc_libraries()
|
||||
self.compile_py_modules()
|
||||
self.fix_dependencies_in_kitty()
|
||||
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)
|
||||
@@ -175,6 +185,10 @@ class Freeze(object):
|
||||
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)
|
||||
@@ -239,7 +253,6 @@ class Freeze(object):
|
||||
self.set_id(
|
||||
join(currd, 'Python'),
|
||||
self.FID + '/Python.framework/Versions/%s/Python' % basename(curr))
|
||||
self.fix_dependencies_in_lib(join(self.contents_dir, 'MacOS', 'kitty'))
|
||||
# The following is needed for codesign
|
||||
with current_dir(x):
|
||||
os.symlink(basename(curr), 'Versions/Current')
|
||||
@@ -265,6 +278,7 @@ class Freeze(object):
|
||||
'lcms2.2',
|
||||
'crypto.1.1',
|
||||
'ssl.1.1',
|
||||
'ffi.7',
|
||||
):
|
||||
print('\nAdding', x)
|
||||
x = 'lib%s.dylib' % x
|
||||
@@ -273,11 +287,6 @@ class Freeze(object):
|
||||
dest = join(self.frameworks_dir, x)
|
||||
self.set_id(dest, self.FID + '/' + x)
|
||||
self.fix_dependencies_in_lib(dest)
|
||||
base = join(self.frameworks_dir, 'kitty')
|
||||
for lib in walk(base):
|
||||
if lib.endswith('.so'):
|
||||
self.set_id(lib, self.FID + '/' + os.path.relpath(lib, self.frameworks_dir))
|
||||
self.fix_dependencies_in_lib(lib)
|
||||
|
||||
@flush
|
||||
def add_package_dir(self, x, dest=None):
|
||||
@@ -295,21 +304,10 @@ class Freeze(object):
|
||||
dest = self.site_packages
|
||||
dest = join(dest, basename(x))
|
||||
shutil.copytree(x, dest, symlinks=True, ignore=ignore)
|
||||
self.postprocess_package(x, dest)
|
||||
for f in walk(dest):
|
||||
if f.endswith('.so'):
|
||||
self.fix_dependencies_in_lib(f)
|
||||
|
||||
@flush
|
||||
def fix_dependencies_in_kitty(self):
|
||||
for f in walk(join(self.resources_dir, 'kitty')):
|
||||
if f.endswith('.so'):
|
||||
self.fix_dependencies_in_lib(f)
|
||||
|
||||
@flush
|
||||
def postprocess_package(self, src_path, dest_path):
|
||||
pass
|
||||
|
||||
@flush
|
||||
def add_stdlib(self):
|
||||
print('\nAdding python stdlib')
|
||||
@@ -331,6 +329,35 @@ class Freeze(object):
|
||||
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')
|
||||
@@ -389,11 +416,8 @@ class Freeze(object):
|
||||
|
||||
@flush
|
||||
def compile_py_modules(self):
|
||||
print('\nCompiling Python modules')
|
||||
self.remove_bytecode(join(self.resources_dir, 'Python'))
|
||||
py_compile(join(self.resources_dir, 'Python'))
|
||||
self.remove_bytecode(join(self.resources_dir, 'kitty'))
|
||||
py_compile(join(self.resources_dir, 'kitty'))
|
||||
|
||||
@flush
|
||||
def makedmg(self, d, volname, internet_enable=True, format='ULFO'):
|
||||
@@ -445,14 +469,12 @@ class Freeze(object):
|
||||
def main():
|
||||
args = globals()['args']
|
||||
ext_dir = globals()['ext_dir']
|
||||
if not args.skip_tests:
|
||||
run_tests = iv['run_tests']
|
||||
run_tests(None, os.path.join(ext_dir, 'src'))
|
||||
Freeze(
|
||||
os.path.join(ext_dir, kitty_constants['appname'] + '.app'),
|
||||
dont_strip=args.dont_strip,
|
||||
sign_installers=args.sign_installers,
|
||||
notarize=args.notarize
|
||||
notarize=args.notarize,
|
||||
skip_tests=args.skip_tests
|
||||
)
|
||||
|
||||
|
||||
|
||||
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()
|
||||
@@ -32,9 +32,20 @@
|
||||
{
|
||||
"name": "expat",
|
||||
"unix": {
|
||||
"filename": "expat-2.2.6.tar.bz2",
|
||||
"hash": "sha256:17b43c2716d521369f82fc2dc70f359860e90fa440bea65b3b85f0b246ea81f2",
|
||||
"urls": ["https://github.com/libexpat/libexpat/releases/download/R_2_2_6/{filename}"]
|
||||
"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}"]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -43,8 +54,8 @@
|
||||
"name": "xkbcommon",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "libxkbcommon-0.10.0.tar.xz",
|
||||
"hash": "sha256:57c3630cdc38fb4734cd57fa349e92244f5ae3862813e533cedbd86721a0b6f2",
|
||||
"filename": "libxkbcommon-1.0.3.tar.xz",
|
||||
"hash": "sha256:a2202f851e072b84e64a395212cbd976ee18a8ee602008b0bad02a13247dbc52",
|
||||
"urls": ["http://xkbcommon.org/download/{filename}"]
|
||||
}
|
||||
},
|
||||
@@ -52,9 +63,9 @@
|
||||
{
|
||||
"name": "sqlite",
|
||||
"unix": {
|
||||
"filename": "sqlite-autoconf-3310100.tar.gz",
|
||||
"hash": "sha1:0c30f5b22152a8166aa3bebb0f4bc1f3e9cc508b",
|
||||
"urls": ["https://www.sqlite.org/2020/{filename}"]
|
||||
"filename": "sqlite-autoconf-3340100.tar.gz",
|
||||
"hash": "sha256:2a3bca581117b3b88e5361d0ef3803ba6d8da604b1c1a47d902ef785c1b53e89",
|
||||
"urls": ["https://www.sqlite.org/2021/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -62,17 +73,17 @@
|
||||
{
|
||||
"name": "libffi",
|
||||
"unix": {
|
||||
"filename": "libffi-3.2.1.tar.gz",
|
||||
"hash": "sha1:280c265b789e041c02e5c97815793dfc283fb1e6",
|
||||
"urls": ["ftp://sourceware.org/pub/libffi/{filename}"]
|
||||
"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.1g.tar.gz",
|
||||
"hash": "sha256:ddb04774f1e32f0c49751e21b67216ac87852ceb056b75209af2443400636d46",
|
||||
"filename": "openssl-1.1.1i.tar.gz",
|
||||
"hash": "sha256:e8be6a35fe41d10603c3cc635e93289ed00bf34b79671a3a4de64fcee00d5242",
|
||||
"urls": ["https://www.openssl.org/source/{filename}"]
|
||||
}
|
||||
},
|
||||
@@ -91,15 +102,14 @@
|
||||
"name": "readline",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "readline-8.0.tar.gz",
|
||||
"hash": "sha256:e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461",
|
||||
"filename": "readline-8.1.tar.gz",
|
||||
"hash": "sha256:f8ceb4ee131e3232226a17f51b164afc46cd0b9e6cef344be87c65962cb82b02",
|
||||
"urls": ["http://ftp.gnu.org/gnu/readline/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "xz",
|
||||
"os": "macos,linux",
|
||||
"unix": {
|
||||
"filename": "xz-5.2.5.tar.gz",
|
||||
"hash": "sha256:f6f4910fd033078738bd82bfba4f49219d03b17eb0794eb91efbae419f4aba10",
|
||||
@@ -107,20 +117,30 @@
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"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.8.3.tar.xz",
|
||||
"hash": "sha256:dfab5ec723c218082fe3d5d7ae17ecbdebffa9a1aea4d64aa3a2ecdd2e795864",
|
||||
"urls": ["https://www.python.org/ftp/python/3.8.3/{filename}"]
|
||||
"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-41.0.1.zip",
|
||||
"hash": "sha256:a222d126f5471598053c9a77f4b5d4f26eaa1f150ad6e01dcf1a42e185d05613",
|
||||
"filename": "setuptools-53.0.0.tar.gz",
|
||||
"hash": "sha256:1b18ef17d74ba97ac9c0e4b4265f123f07a8ae85d9cd093949fa056d3eeeead5",
|
||||
"urls": ["pypi"]
|
||||
}
|
||||
},
|
||||
@@ -128,8 +148,8 @@
|
||||
{
|
||||
"name": "pygments",
|
||||
"unix": {
|
||||
"filename": "Pygments-2.6.1.tar.gz",
|
||||
"hash": "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44",
|
||||
"filename": "Pygments-2.7.4.tar.gz",
|
||||
"hash": "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337",
|
||||
"urls": ["pypi"]
|
||||
}
|
||||
},
|
||||
@@ -138,9 +158,9 @@
|
||||
"name": "cmake",
|
||||
"os": "macos",
|
||||
"unix": {
|
||||
"filename": "cmake-3.6.0.tar.gz",
|
||||
"hash": "sha1:6b0d2d7d93c2e74f8890c4b8b824deb3b36d24b6",
|
||||
"urls": ["https://cmake.org/files/v3.6/{filename}"]
|
||||
"filename": "cmake-3.19.4.tar.gz",
|
||||
"hash": "sha256:7d0232b9f1c57e8de81f38071ef8203e6820fe7eec8ae46a1df125d88dbcc2e1",
|
||||
"urls": ["https://cmake.org/files/v3.19/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -156,9 +176,9 @@
|
||||
{
|
||||
"name": "lcms2",
|
||||
"unix": {
|
||||
"filename": "lcms2-2.11.tar.gz",
|
||||
"hash": "sha256:478c9c3938d7a91b1171de4616f8b04308a8676d73eadc19505b7ace41327f28",
|
||||
"urls": ["https://github.com/mm2/Little-CMS/archive/2.11/{filename}"]
|
||||
"filename": "lcms2-2.12.tar.gz",
|
||||
"hash": "sha256:e501f1482fc424550ef3abbf86bf1c66090e1661249e89552d39ed5bf935df66",
|
||||
"urls": ["https://github.com/mm2/Little-CMS/archive/2.12/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -166,8 +186,8 @@
|
||||
"name": "graphite",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "graphite2-1.3.5.tgz",
|
||||
"hash": "md5:5b8d22a8bbf031838e31432868c0109c",
|
||||
"filename": "graphite2-1.3.14.tgz",
|
||||
"hash": "sha256:f99d1c13aa5fa296898a181dff9b82fb25f6cc0933dbaa7a475d8109bd54209d",
|
||||
"urls": ["http://downloads.sourceforge.net/silgraphite/{filename}"]
|
||||
}
|
||||
},
|
||||
@@ -176,8 +196,8 @@
|
||||
"name": "pcre",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "pcre-8.42.tar.bz2",
|
||||
"hash": "md5:085b6aa253e0f91cae70b3cdbe8c1ac2",
|
||||
"filename": "pcre-8.44.tar.bz2",
|
||||
"hash": "sha256:19108658b23b3ec5058edc9f66ac545ea19f9537234be1ec62b714c84399366d",
|
||||
"urls": ["https://ftp.pcre.org/pub/pcre/{filename}"]
|
||||
}
|
||||
},
|
||||
@@ -196,9 +216,19 @@
|
||||
"name": "glib",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "glib-2.65.1.tar.xz",
|
||||
"hash": "sha256:bc63bf6c32713e0ee1dddc28e03f23b4a20c78bcb9a2c5b0f4eea41e46fb9cee",
|
||||
"urls": ["https://ftp.gnome.org/pub/GNOME/sources/glib/2.65/{filename}"]
|
||||
"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}"]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -206,8 +236,8 @@
|
||||
"name": "freetype",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "freetype-2.9.1.tar.bz2",
|
||||
"hash": "md5:60ef7d8160cd4bf8cb118ee9d65367ca",
|
||||
"filename": "freetype-2.10.4.tar.xz",
|
||||
"hash": "sha256:86a854d8905b19698bbc8f23b860bc104246ce4854dcea8e3b0fb21284f75784",
|
||||
"urls": ["https://download.savannah.gnu.org/releases/freetype/{filename}"]
|
||||
}
|
||||
},
|
||||
@@ -216,8 +246,8 @@
|
||||
"name": "fontconfig",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "fontconfig-2.12.0.tar.bz2",
|
||||
"hash": "sha256:b433e4efff1f68fdd8aac221ed1df3ff1e580ffedbada020a703fe64017d8224",
|
||||
"filename": "fontconfig-2.13.1.tar.bz2",
|
||||
"hash": "sha256:f655dd2a986d7aa97e052261b36aa67b0a64989496361eca8d604e6414006741",
|
||||
"urls": ["http://www.fontconfig.org/release/{filename}"]
|
||||
}
|
||||
},
|
||||
@@ -225,19 +255,9 @@
|
||||
{
|
||||
"name": "harfbuzz",
|
||||
"unix": {
|
||||
"filename": "harfbuzz-1.7.2.tar.bz2",
|
||||
"hash": "sha256:a790585e35c1a87f0dcc23580c84b7cc2324e6f67a2946178d278c2a36c790cb",
|
||||
"urls": ["https://www.freedesktop.org/software/harfbuzz/release/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"name": "libxml2",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "libxml2-2.9.4.tar.gz",
|
||||
"hash": "sha1:958ae70baf186263a4bd801a81dd5d682aedd1db",
|
||||
"urls": ["ftp://xmlsoft.org/libxml2/{filename}"]
|
||||
"filename": "harfbuzz-2.7.4.tar.xz",
|
||||
"hash": "sha256:6ad11d653347bd25d8317589df4e431a2de372c0cf9be3543368e07ec23bb8e7",
|
||||
"urls": ["https://github.com/harfbuzz/harfbuzz/releases/download/2.7.4/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -245,8 +265,8 @@
|
||||
"name": "wayland",
|
||||
"os": "linux",
|
||||
"unix": {
|
||||
"filename": "wayland-1.18.0.tar.xz",
|
||||
"hash": "sha256:4675a79f091020817a98fd0484e7208c8762242266967f55a67776936c2e294d",
|
||||
"filename": "wayland-1.19.0.tar.xz",
|
||||
"hash": "sha256:baccd902300d354581cd5ad3cc49daa4921d55fb416a5883e218750fef166d15",
|
||||
"urls": ["https://wayland.freedesktop.org/releases/{filename}"]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -4,8 +4,6 @@ import subprocess
|
||||
|
||||
files_to_exclude = '''\
|
||||
kitty/wcwidth-std.h
|
||||
kitty/glfw.c
|
||||
kitty/keys.h
|
||||
kitty/charsets.c
|
||||
kitty/unicode-data.c
|
||||
kitty/key_encoding.py
|
||||
|
||||
4
docs/_static/custom.css
vendored
4
docs/_static/custom.css
vendored
@@ -21,6 +21,10 @@ div.body p, div.body dd, div.body li, div.body blockquote {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
div.body {
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
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,7 +44,7 @@ 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
|
||||
|
||||
|
||||
@@ -30,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
|
||||
@@ -78,6 +78,19 @@ you might have to rebuild the app.
|
||||
a self signed certificate, see for example, `here
|
||||
<https://stackoverflow.com/questions/27474751/how-can-i-codesign-an-app-without-being-in-the-mac-developer-program/27474942>`_.
|
||||
|
||||
Build and run from source with Nix
|
||||
-------------------------------------------
|
||||
|
||||
On NixOS or any other Linux or macOS system with the Nix package manager
|
||||
installed, execute `nix-shell
|
||||
<https://nixos.org/guides/nix-pills/developing-with-nix-shell.html>`_ to create
|
||||
the correct environment to build kitty or use ``nix-shell --pure`` instead to
|
||||
eliminate most of the influence of the outside system, e.g. globally installed
|
||||
packages. ``nix-shell`` will automatically fetch all required dependencies and
|
||||
make them available in the newly spawned shell.
|
||||
|
||||
Then proceed with ``make`` or ``make app`` according to the platform specific instructions above.
|
||||
|
||||
|
||||
Note for Linux/macOS packagers
|
||||
----------------------------------
|
||||
|
||||
@@ -1,9 +1,291 @@
|
||||
Changelog
|
||||
==============
|
||||
|
||||
|kitty| is a feature full, cross-platform, *fast*, GPU based terminal emulator.
|
||||
|kitty| is a feature-rich, cross-platform, *fast*, GPU based terminal.
|
||||
To update |kitty|, :doc:`follow the instructions <binary>`.
|
||||
|
||||
0.20.2 [2021-04-28]
|
||||
----------------------
|
||||
|
||||
- A new protocol extension to :ref:`unscroll <unscroll>` text from the
|
||||
scrollback buffer onto the screen. Useful, for example, to restore
|
||||
the screen after showing completions below the shell prompt.
|
||||
|
||||
- A new remote control command :ref:`at_env` to change the default
|
||||
environment passed to newly created windows (:iss:`3529`)
|
||||
|
||||
- Linux: Fix binary kitty builds not able to load fonts in WOFF2 format
|
||||
(:iss:`3506`)
|
||||
|
||||
- macOS: Prevent :kbd:`option` based shortcuts for being used for global menu
|
||||
actions (:iss:`3515`)
|
||||
|
||||
- Fix ``kitty @ close-tab`` not working with pipe based remote control
|
||||
(:iss:`3510`)
|
||||
|
||||
- Fix removal of inactive tab that is before the currently active tab causing
|
||||
the highlighted tab to be incorrect (:iss:`3516`)
|
||||
|
||||
- icat kitten: Respect EXIF orientation when displaying JPEG images
|
||||
(:iss:`3518`)
|
||||
|
||||
- GNOME: Fix maximize state not being remembered when focus changes and window
|
||||
decorations are hidden (:iss:`3507`)
|
||||
|
||||
- GNOME: Add a new :opt:`wayland_titlebar_color` option to control the color of the
|
||||
kitty window title bar
|
||||
|
||||
- Fix reading :option:`kitty --session` from ``STDIN`` not working when the
|
||||
:option:`kitty --detach` option is used (:iss:`3523`)
|
||||
|
||||
- Special case rendering of the few remaining Powerline box drawing chars
|
||||
(:iss:`3535`)
|
||||
|
||||
- Fix ``kitty @ set-colors`` not working for the :opt:`active_tab_foreground`.
|
||||
|
||||
|
||||
0.20.1 [2021-04-19]
|
||||
----------------------
|
||||
|
||||
- icat: Fix some broken GIF images with no frame delays not being animated
|
||||
(:iss:`3498`)
|
||||
|
||||
- hints kitten: Fix sending hyperlinks to their default handler not working
|
||||
(:pull:`3500`)
|
||||
|
||||
- Wayland: Fix regression in previous release causing window decorations to
|
||||
be drawn even when compositor supports server side decorations (:iss:`3501`)
|
||||
|
||||
|
||||
0.20.0 [2021-04-19]
|
||||
----------------------
|
||||
|
||||
- Support display of animated images ``kitty +kitten icat animation.gif``. See
|
||||
:ref:`animation_protocol` for details on animation support in the kitty
|
||||
graphics protocol.
|
||||
|
||||
- A new keyboard reporting protocol with various advanced features that can be
|
||||
used by full screen terminal programs and even games, see
|
||||
:doc:`keyboard-protocol` (:iss:`3248`)
|
||||
|
||||
- **Backward incompatibility**: Session files now use the full :doc:`launch <launch>`
|
||||
command with all its capabilities. However, the syntax of the command is
|
||||
slightly different from before. In particular watchers are now specified
|
||||
directly on launch and environment variables are set using ``--env``.
|
||||
|
||||
- Allow setting colors when creating windows using the :doc:`launch <launch>` command.
|
||||
|
||||
- A new option :opt:`tab_powerline_style` to control the appearance of the tab
|
||||
bar when using the powerline tab bar style.
|
||||
|
||||
- A new option :opt:`scrollback_fill_enlarged_window` to fill extra lines in
|
||||
the window when the window is expanded with lines from the scrollback
|
||||
(:pull:`3371`)
|
||||
|
||||
- diff kitten: Implement recursive diff over SSH (:iss:`3268`)
|
||||
|
||||
- ssh kitten: Allow using python instead of the shell on the server, useful if
|
||||
the shell used is a non-POSIX compliant one, such as fish (:iss:`3277`)
|
||||
|
||||
- Add support for the color settings stack that XTerm copied from us without
|
||||
acknowledgement and decided to use incompatible escape codes for.
|
||||
|
||||
- Add entries to the terminfo file for some user capabilities that are shared
|
||||
with XTerm (:pull:`3193`)
|
||||
|
||||
- The launch command now does more sophisticated resolving of executables to
|
||||
run. The system-wide PATH is used first, then system specific default paths,
|
||||
and finally the PATH inside the shell.
|
||||
|
||||
- Double clicking on empty tab bar area now opens a new tab (:iss:`3201`)
|
||||
|
||||
- kitty @ ls: Show only environment variables that are different for each
|
||||
window, by default.
|
||||
|
||||
- When passing a directory or a non-executable file as the program to run to
|
||||
kitty opens it with the shell or by parsing the shebang, instead of just failing.
|
||||
|
||||
- Linux: Fix rendering of emoji followed by the graphics variation selector not
|
||||
being colored with some fonts (:iss:`3211`)
|
||||
|
||||
- Unicode input: Fix using index in select by name mode not working for indices
|
||||
larger than 16. Also using an index does not filter the list of matches. (:pull:`3219`)
|
||||
|
||||
- Wayland: Add support for the text input protocol (:iss:`3410`)
|
||||
|
||||
- Wayland: Fix mouse handling when using client side decorations
|
||||
|
||||
- Wayland: Fix un-maximizing a window not restoring its size to what it was
|
||||
before being maximized
|
||||
|
||||
- GNOME/Wayland: Improve window decorations the titlebar now shows the window
|
||||
title. Allow running under Wayland on GNOME by default. (:iss:`3284`)
|
||||
|
||||
- Panel kitten: Allow setting WM_CLASS (:iss:`3233`)
|
||||
|
||||
- macOS: Add menu items to close the OS window and the current tab (:pull:`3240`, :iss:`3246`)
|
||||
|
||||
- macOS: Allow opening script and command files with kitty (:iss:`3366`)
|
||||
|
||||
- Also detect ``gemini://`` URLs when hovering with the mouse (:iss:`3370`)
|
||||
|
||||
- When using a non-US keyboard layout and pressing :kbd:`ctrl+key` when
|
||||
the key matches an English key, send that to the program running in the
|
||||
terminal automatically (:iss:`2000`)
|
||||
|
||||
- When matching shortcuts, also match on shifted keys, so a shortcut defined as
|
||||
:kbd:`ctrl+plus` will match a keyboard where you have to press
|
||||
:kbd:`shift+equal` to get the plus key (:iss:`2000`)
|
||||
|
||||
- Fix extra space at bottom of OS window when using the fat layout with the tab bar at the
|
||||
top (:iss:`3258`)
|
||||
|
||||
- Fix window icon not working on X11 with 64bits (:iss:`3260`)
|
||||
|
||||
- Fix OS window sizes under 100px resulting in scaled display (:iss:`3307`)
|
||||
|
||||
- Fix rendering of ligatures in the latest release of Cascadia code, which for
|
||||
some reason puts empty glyphs after the ligature glyph rather than before it
|
||||
(:iss:`3313`)
|
||||
|
||||
- Improve handling of infinite length ligatures in newer versions of FiraCode
|
||||
and CascadiaCode. Now such ligatures are detected based on glyph naming
|
||||
convention. This removes the gap in the ligatures at cell boundaries (:iss:`2695`)
|
||||
|
||||
- macOS: Disable the native operating system tabs as they are non-functional
|
||||
and can be confusing (:iss:`3325`)
|
||||
|
||||
- hints kitten: When using the linenumber action with a background action,
|
||||
preserve the working directory (:iss:`3352`)
|
||||
|
||||
- Graphics protocol: Fix suppression of responses not working for chunked
|
||||
transmission (:iss:`3375`)
|
||||
|
||||
- Fix inactive tab closing causing active tab to change (:iss:`3398`)
|
||||
|
||||
- Fix a crash on systems using musl as libc (:iss:`3395`)
|
||||
|
||||
- Improve rendering of rounded corners by using a rectircle equation rather
|
||||
than a cubic bezier (:iss:`3409`)
|
||||
|
||||
- Graphics protocol: Add a control to allow clients to specify that the cursor
|
||||
should not move when displaying an image (:iss:`3411`)
|
||||
|
||||
- Fix marking of text not working on lines that contain zero cells
|
||||
(:iss:`3403`)
|
||||
|
||||
- Fix the selection getting changed if the screen contents scroll while
|
||||
the selection is in progress (:iss:`3431`)
|
||||
|
||||
- X11: Fix :opt:`resize_in_steps` being applied even when window is maximized
|
||||
(:iss:`3473`)
|
||||
|
||||
|
||||
0.19.3 [2020-12-19]
|
||||
-------------------
|
||||
|
||||
- Happy holidays to all kitty users!
|
||||
|
||||
- A new :doc:`broadcast <kittens/broadcast>` kitten to type in all kitty windows
|
||||
simultaneously (:iss:`1569`)
|
||||
|
||||
- Add a new mappable `select_tab` action to choose a tab to switch to even
|
||||
when the tab bar is hidden (:iss:`3115`)
|
||||
|
||||
- Allow specifying text formatting in :opt:`tab_title_template` (:iss:`3146`)
|
||||
|
||||
- Linux: Read :opt:`font_features` from the FontConfig database as well, so
|
||||
that they can be configured in a single, central location (:pull:`3174`)
|
||||
|
||||
- Graphics protocol: Add support for giving individual image placements their
|
||||
own ids and for asking the terminal emulator to assign ids for images. Also
|
||||
allow suppressing responses from the terminal to commands.
|
||||
These are backwards compatible protocol extensions. (:iss:`3133`,
|
||||
:iss:`3163`)
|
||||
|
||||
- Distribute extra pixels among all eight-blocks rather than adding them
|
||||
all to the last block (:iss:`3097`)
|
||||
|
||||
- Fix drawing of a few sextant characters incorrect (:pull:`3105`)
|
||||
|
||||
- macOS: Fix minimize not working for chromeless windows (:iss:`3112`)
|
||||
|
||||
- Preserve lines in the scrollback if a scrolling region is defined that
|
||||
is contiguous with the top of the screen (:iss:`3113`)
|
||||
|
||||
- Wayland: Fix key repeat being stopped by the release of an unrelated key
|
||||
(:iss:`2191`)
|
||||
|
||||
- Add an option, :opt:`detect_urls` to control whether kitty will detect URLs
|
||||
when the mouse moves over them (:pull:`3118`)
|
||||
|
||||
- Graphics protocol: Dont return filename in the error message when opening file
|
||||
fails, since filenames can contain control characters (:iss:`3128`)
|
||||
|
||||
- macOS: Partial fix for traditional fullscreen not working on Big Sur
|
||||
(:iss:`3100`)
|
||||
|
||||
- Fix one ANSI formatting escape code not being removed from the pager history
|
||||
buffer when piping it as plain text (:iss:`3132`)
|
||||
|
||||
- Match the save/restore cursor behavior of other terminals, for the sake of
|
||||
interoperability. This means that doing a DECRC without a prior DECSC is now
|
||||
undefined (:iss:`1264`)
|
||||
|
||||
- Fix mapping ``remote_control send-text`` not working (:iss:`3147`)
|
||||
|
||||
- Add a ``right`` option for :opt:`tab_switch_strategy` (:pull:`3155`)
|
||||
|
||||
- Fix a regression in 0.19.0 that caused a rare crash when using the optional
|
||||
:opt:`scrollback_pager_history_size` (:iss:`3049`)
|
||||
|
||||
- Full screen kittens: Fix incorrect cursor position after kitten quits
|
||||
(:iss:`3176`)
|
||||
|
||||
|
||||
0.19.2 [2020-11-13]
|
||||
-------------------
|
||||
|
||||
- A new :doc:`kittens/query_terminal` kitten to easily query the running kitty
|
||||
via escape codes to detect its version, and the values of
|
||||
configuration options that enable or disable terminal features.
|
||||
|
||||
- Options to control mouse pointer shape, :opt:`default_pointer_shape`, and
|
||||
:opt:`pointer_shape_when_dragging` (:pull:`3041`)
|
||||
|
||||
- Font independent rendering for braille characters, which ensures they are properly
|
||||
aligned at all font sizes.
|
||||
|
||||
- Fix a regression in 0.19.0 that caused borders not to be drawn when setting
|
||||
:opt:`window_margin_width` and keeping :opt:`draw_minimal_borders` on
|
||||
(:iss:`3017`)
|
||||
|
||||
- Fix a regression in 0.19.0 that broke rendering of one-eight bar unicode
|
||||
characters at very small font sizes (:iss:`3025`)
|
||||
|
||||
- Wayland: Fix a crash under GNOME when using multiple OS windows
|
||||
(:pull:`3066`)
|
||||
|
||||
- Fix selections created by dragging upwards not being auto-cleared when
|
||||
screen contents change (:pull:`3028`)
|
||||
|
||||
- macOS: Fix kitty not being added to PATH automatically when using pre-built
|
||||
binaries (:iss:`3063`)
|
||||
|
||||
- Allow adding MIME definitions to kitty by placing a ``mime.types`` file in
|
||||
the kitty config directory (:iss:`3056`)
|
||||
|
||||
- Dont ignore :option:`--title` when using a session file that defines no
|
||||
windows (:iss:`3055`)
|
||||
|
||||
- Fix the send_text action not working in URL handlers (:iss:`3081`)
|
||||
|
||||
- Fix last character of URL not being detected if it is the only character on a
|
||||
new line (:iss:`3088`)
|
||||
|
||||
- Don't restrict the ICH,DCH,REP control codes to only the current scroll region (:iss:`3090`, :iss:`3096`)
|
||||
|
||||
|
||||
0.19.1 [2020-10-06]
|
||||
-------------------
|
||||
|
||||
|
||||
16
docs/conf.py
16
docs/conf.py
@@ -24,7 +24,7 @@ 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 # type: ignore
|
||||
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
|
||||
|
||||
@@ -140,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
|
||||
@@ -170,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)
|
||||
]
|
||||
|
||||
|
||||
@@ -182,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'),
|
||||
]
|
||||
# }}}
|
||||
@@ -555,7 +552,7 @@ def process_shortcut_link(env: Any, refnode: Any, has_explicit_title: bool, titl
|
||||
|
||||
|
||||
def write_conf_docs(app: Any, all_kitten_names: Iterable[str]) -> None:
|
||||
app.add_lexer('conf', ConfLexer)
|
||||
app.add_lexer('conf', ConfLexer() if version_info[0] < 3 else ConfLexer)
|
||||
app.add_object_type(
|
||||
'opt', 'opt',
|
||||
indextemplate="pair: %s; Config Setting",
|
||||
@@ -604,7 +601,8 @@ def setup(app: Any) -> None:
|
||||
write_cli_docs(kn)
|
||||
write_remote_control_protocol_docs()
|
||||
write_conf_docs(app, kn)
|
||||
app.add_lexer('session', SessionLexer)
|
||||
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.
|
||||
|
||||
100
docs/faq.rst
100
docs/faq.rst
@@ -47,8 +47,19 @@ terminfo files to the server::
|
||||
|
||||
kitty +kitten ssh myserver
|
||||
|
||||
This ssh kitten takes all the same command line arguments
|
||||
as ssh, you can alias it to ssh in your shell's rc files to avoid having to
|
||||
type it each time::
|
||||
|
||||
alias ssh="kitty +kitten ssh"
|
||||
|
||||
If for some reason that does not work (typically because the server is using a
|
||||
non POSIX compliant shell), you can use the following one-liner instead (it
|
||||
non POSIX compliant shell), you can try using it with python instead::
|
||||
|
||||
kitty +kitten ssh use-python myserver
|
||||
|
||||
If that also fails, perhaps because python is not installed on the remote
|
||||
server, use the following one-liner instead (it
|
||||
is slower as it needs to ssh into the server twice, but will work with most
|
||||
servers)::
|
||||
|
||||
@@ -165,9 +176,12 @@ 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.
|
||||
|
||||
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
|
||||
|
||||
@@ -179,7 +193,7 @@ following :file:`~/.config/fontconfig/fonts.conf`::
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
||||
<fontconfig>
|
||||
<match target="font">
|
||||
<match target="scan">
|
||||
<test name="family">
|
||||
<string>Your Font Family Name</string>
|
||||
</test>
|
||||
@@ -233,6 +247,45 @@ available. The manual way to figure it out is:
|
||||
|
||||
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
|
||||
--------------------------------------
|
||||
@@ -259,3 +312,40 @@ 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,7 +28,7 @@ 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.py <https://github.com/dsanson/termpdf.py>`_ - a terminal PDF/DJVU/CBR viewer
|
||||
* `ranger <https://github.com/ranger/ranger>`_ - a terminal file manager, with
|
||||
@@ -37,6 +37,12 @@ Some programs that use the kitty graphics protocol:
|
||||
* `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::
|
||||
@@ -142,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
|
||||
--------------------------
|
||||
|
||||
@@ -188,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
|
||||
@@ -263,7 +272,7 @@ sequence of escape codes to the terminal emulator::
|
||||
<ESC>_Gm=0;<encoded pixel data last chunk><ESC>\
|
||||
|
||||
Note that only the first escape code needs to have the full set of control
|
||||
codes such as width, height, format etc. Subsequent chunks must have
|
||||
codes such as width, height, format etc. Subsequent chunks **must** have
|
||||
only the ``m`` key. The client **must** finish sending all chunks for a single image
|
||||
before sending any other graphics related escape codes. Note that the cursor
|
||||
position used to display the image **must** be the position when the final chunk is
|
||||
@@ -313,7 +322,7 @@ 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;<NUL><NUL><NUL><ESC>\<ESC>[c
|
||||
<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
|
||||
@@ -325,11 +334,12 @@ 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>\
|
||||
|
||||
@@ -342,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
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -368,6 +396,19 @@ allows rendering of text on top of images. Negative z-index values below
|
||||
INT32_MIN/2 (-1,073,741,824) will be drawn under cells with non-default background
|
||||
colors.
|
||||
|
||||
.. note:: After placing an image on the screen the cursor must be moved to the
|
||||
right by the number of cols in the image placement rectangle and down by the
|
||||
number of rows in the image placement rectangle. If either of these cause
|
||||
the cursor to leave either the screen or the scroll area, the exact
|
||||
positioning of the cursor is undefined, and up to implementations.
|
||||
The client can ask the terminal emulator to not move the cursor at all
|
||||
by specifying ``C=1`` in the command, which sets the cursor movement policy
|
||||
to no movement for placing the current image.
|
||||
|
||||
.. versionadded:: 0.20.0
|
||||
Support for the C=1 cursor movement policy
|
||||
|
||||
|
||||
Deleting images
|
||||
---------------------
|
||||
|
||||
@@ -384,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,d=i,i=10<ESC>\ # delete the image with id=10, without freeing data
|
||||
<ESC>_Ga=d,d=Z,z=-1<ESC>\ # delete the images with z-index -1, also freeing up image data
|
||||
<ESC>_Ga=d,d=p,x=3,y=4<ESC>\ # delete all images that intersect the cell at (3, 4), without freeing data
|
||||
<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
|
||||
@@ -423,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.
|
||||
@@ -436,26 +668,63 @@ Key Value Default Description
|
||||
``O`` Positive integer. ``0`` The offset from which to read data from a file.
|
||||
``i`` Positive integer.
|
||||
``(0 - 4294967295)`` ``0`` The image id
|
||||
``I`` Positive integer.
|
||||
``(0 - 4294967295)`` ``0`` The image number
|
||||
``p`` Positive integer.
|
||||
``(0 - 4294967295)`` ``0`` The placement id
|
||||
``o`` Single character. ``null`` The type of data compression.
|
||||
``only z``
|
||||
``m`` zero or one ``0`` Whether there is more chunked data available.
|
||||
|
||||
**Keys for image display**
|
||||
-----------------------------------------------------------
|
||||
``x`` Positive integer ``0`` The left edge (in pixels) of the image area to display
|
||||
``y`` Positive integer ``0`` The top edge (in pixels) of the image area to display
|
||||
``w`` Positive integer ``0`` The width (in pixels) of the image area to display. By default, the entire width is used.
|
||||
``w`` Positive integer ``0`` The width (in pixels) of the image area to display. By default, the entire width is used
|
||||
``h`` Positive integer ``0`` The height (in pixels) of the image area to display. By default, the entire height is used
|
||||
``X`` Positive integer ``0`` The x-offset within the first cell at which to start displaying the image
|
||||
``Y`` Positive integer ``0`` The y-offset within the first cell at which to start displaying the image
|
||||
``c`` Positive integer ``0`` The number of columns to display the image over
|
||||
``r`` Positive integer ``0`` The number of rows to display the image over
|
||||
``C`` Positive integer ``0`` Cursor movement policy. ``0`` is the default, to move the cursor to after the image.
|
||||
``1`` is to not move the cursor at all when placing the image.
|
||||
``z`` 32-bit integer ``0`` The *z-index* vertical stacking order of the image
|
||||
|
||||
**Keys for animation frame loading**
|
||||
-----------------------------------------------------------
|
||||
``x`` Positive integer ``0`` The left edge (in pixels) of where the frame data should be updated
|
||||
``y`` Positive integer ``0`` The top edge (in pixels) of where the frame data should be updated
|
||||
``c`` Positive integer ``0`` The 1-based frame number of the frame whose image data serves as the base data
|
||||
when creating a new frame, by default the base data is black, fully transparent pixels
|
||||
``r`` Positive integer ``0`` The 1-based frame number of the frame that is being edited. By default, a new frame is created
|
||||
``z`` 32-bit integer ``0`` The gap (in milliseconds) of this frame from the next one. A value of
|
||||
zero is ignored. Negative values create a *gapless* frame. If not specified,
|
||||
frames have a default gap of ``40ms``. The root frame defaults to zero gap.
|
||||
``X`` Positive integer ``0`` The composition mode for blending pixels when creating a new frame or
|
||||
editing a frame's data. The default is full alpha blending. ``1`` means a
|
||||
simple overwrite.
|
||||
``Y`` Positive integer ``0`` The background color for pixels not
|
||||
specified in the frame data. Must be in 32-bit RGBA format
|
||||
|
||||
|
||||
**Keys for animation control**
|
||||
-----------------------------------------------------------
|
||||
``s`` Positive integer ``0`` ``1`` - stop animation, ``2`` - run animation, but wait for new frames, ``3`` - run animation
|
||||
``r`` Positive integer ``0`` The 1-based frame number of the frame that is being affected
|
||||
``z`` 32-bit integer ``0`` The gap (in milliseconds) of this frame from the next one. A value of
|
||||
zero is ignored. Negative values create a *gapless* frame.
|
||||
``c`` Positive integer ``0`` The 1-based frame number of the frame that should be made the current frame
|
||||
``v`` Positive integer ``0`` The number of loops to play. ``0`` is
|
||||
ignored, ``1`` is play infinite and is the default and larger number
|
||||
means play that number ``-1`` loops
|
||||
|
||||
|
||||
**Keys for deleting images**
|
||||
-----------------------------------------------------------
|
||||
``d`` Single character. ``a`` What to delete.
|
||||
``(a, A, c, C, i,
|
||||
I, p, P, q, Q, x, X,
|
||||
y, Y, z, Z)``.
|
||||
``(a, A, c, C, n, N,
|
||||
i, I, p, P, q, Q, x,
|
||||
X, y, Y, z, Z)``.
|
||||
======= ==================== ========= =================
|
||||
|
||||
|
||||
|
||||
171
docs/index.rst
171
docs/index.rst
@@ -61,7 +61,7 @@ See the :doc:`binary install instructions </binary>`. You can
|
||||
You can also use your favorite package manager to install the |kitty| package.
|
||||
|kitty| packages are available for:
|
||||
`macOS with Homebrew (Cask) <https://formulae.brew.sh/cask/kitty>`_,
|
||||
`macOS and Linux with Nix <https://nixos.org/nixos/packages.html?channel=nixpkgs-unstable&query=kitty>`_,
|
||||
`macOS and Linux with Nix <https://search.nixos.org/packages?channel=unstable&show=kitty&sort=relevance&query=kitty>`_,
|
||||
`Ubuntu <https://launchpad.net/ubuntu/+source/kitty>`_,
|
||||
`Debian <https://packages.debian.org/buster/kitty>`_,
|
||||
`openSUSE <https://build.opensuse.org/package/show/X11:terminals/kitty>`_,
|
||||
@@ -99,133 +99,7 @@ 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.
|
||||
|
||||
.. _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`
|
||||
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:
|
||||
|
||||
@@ -278,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.
|
||||
|
||||
@@ -295,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
|
||||
@@ -328,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
|
||||
@@ -355,14 +247,9 @@ For example:
|
||||
focus
|
||||
launch emacs
|
||||
|
||||
# Add a watcher that will be called with various events that occur
|
||||
# on all subsequent windows. See the documentation of the launch command
|
||||
# for details on watchers.
|
||||
watcher /some/python/file.py
|
||||
launch mpd
|
||||
launch irssi
|
||||
# Remove the watcher for further windows
|
||||
watcher clear
|
||||
.. note::
|
||||
The :doc:`launch <launch>` command when used in a session file
|
||||
cannot create new OS windows, or tabs.
|
||||
|
||||
|
||||
Mouse features
|
||||
@@ -416,6 +303,10 @@ arbitrary, command running in a new window, tab or overlay, for example::
|
||||
Would open the scrollback buffer in a new window when you press the :kbd:`F1`
|
||||
key. See :sc:`show_scrollback` for details.
|
||||
|
||||
If you want to use it with an editor such as vim to get more powerful features,
|
||||
you can see tips for doing so, in
|
||||
`this thread <https://github.com/kovidgoyal/kitty/issues/719>`_.
|
||||
|
||||
If you wish to store very large amounts of scrollback to view using the piping or
|
||||
:sc:`show_scrollback` features, you can use the :opt:`scrollback_pager_history_size`
|
||||
option.
|
||||
|
||||
@@ -28,7 +28,23 @@ images and other types of documents directly in your terminal, even over SSH.
|
||||
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
|
||||
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
|
||||
@@ -63,6 +79,18 @@ System and data visualisation tools
|
||||
|
||||
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
|
||||
-----------------------
|
||||
@@ -116,3 +144,6 @@ Miscellaneous
|
||||
`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,6 +20,9 @@ your machine).
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from typing import List
|
||||
from kitty.boss import Boss
|
||||
|
||||
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
|
||||
@@ -28,7 +31,7 @@ your machine).
|
||||
# handle_result() function
|
||||
return answer
|
||||
|
||||
def handle_result(args: List[str], answer: str, target_window_id: int, boss: kitty.boss.Boss) -> None:
|
||||
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:
|
||||
|
||||
@@ -28,11 +28,17 @@ to be used can be modified using options passed to the kitten. For example::
|
||||
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
|
||||
---------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
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:
|
||||
|
||||
|
||||
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
|
||||
@@ -21,6 +21,8 @@ to *Edit* it in which case kitty will download it and open it locally in your
|
||||
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
|
||||
|
||||
@@ -18,10 +18,10 @@ 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+]`.
|
||||
|
||||
@@ -93,6 +93,16 @@ 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
|
||||
------------------
|
||||
|
||||
|
||||
@@ -171,6 +171,8 @@ are placed into vertical or horizontal splits, by default::
|
||||
│ │ │
|
||||
└──────────────┴───────────────┘
|
||||
|
||||
.. versionadded:: 0.17.0
|
||||
The Splits layout
|
||||
|
||||
|
||||
The Horizontal Layout
|
||||
|
||||
@@ -19,12 +19,12 @@ If you want to make it case-insensitive, use::
|
||||
|
||||
To make it match only complete words, use::
|
||||
|
||||
map f1 toggle_marker regex 1 \bERROR\b
|
||||
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
|
||||
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::
|
||||
|
||||
@@ -81,7 +81,11 @@ lines. The various available criteria are:
|
||||
|
||||
``mime``
|
||||
A comma separated list of MIME types, for example: ``text/*, image/*,
|
||||
application/pdf``
|
||||
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``
|
||||
|
||||
@@ -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
|
||||
@@ -230,6 +157,44 @@ 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:
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ Where ``<ESC>`` is the byte ``0x1b``. The JSON object has the form::
|
||||
{
|
||||
"cmd": "command name",
|
||||
"version": <kitty version>,
|
||||
"no_response": Optional Boolean,
|
||||
"no_response": <Optional Boolean>,
|
||||
"payload": <Optional JSON object>,
|
||||
}
|
||||
|
||||
@@ -26,4 +26,16 @@ 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
|
||||
|
||||
@@ -115,8 +115,10 @@ shell. Run ``kitty @`` with no arguments and you will be dropped into the |kitty
|
||||
shell with completion for |kitty| command names and options.
|
||||
|
||||
You can even open the |kitty| shell inside a running |kitty| using a simple
|
||||
keyboard shortcut (:sc:`kitty_shell` by default). This has the added
|
||||
advantage that you don't need to use ``allow_remote_control`` to make it work.
|
||||
keyboard shortcut (:sc:`kitty_shell` by default).
|
||||
|
||||
.. note:: This has the added advantage that you don't need to use
|
||||
``allow_remote_control`` to make it work.
|
||||
|
||||
|
||||
Allowing only some windows to control kitty
|
||||
@@ -134,6 +136,12 @@ 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
|
||||
--------------------------------------------------
|
||||
|
||||
@@ -146,6 +154,23 @@ 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
|
||||
-----------------------------------------------
|
||||
|
||||
@@ -250,13 +250,16 @@ def write_header(text: str, path: str) -> None:
|
||||
def graphics_parser() -> None:
|
||||
flag = frozenset
|
||||
keymap: KeymapType = {
|
||||
'a': ('action', flag('tTqpd')),
|
||||
'd': ('delete_action', flag('aAiIcCpPqQxXyYzZ')),
|
||||
'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'),
|
||||
@@ -270,6 +273,7 @@ def graphics_parser() -> None:
|
||||
'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()
|
||||
@@ -114,6 +114,7 @@ def parse_ucd() -> None:
|
||||
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 parse_range_spec(spec: str) -> Set[int]:
|
||||
@@ -504,7 +505,7 @@ def gen_wcwidth() -> None:
|
||||
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) {')
|
||||
|
||||
|
||||
70
glfw/backend_utils.c
vendored
70
glfw/backend_utils.c
vendored
@@ -8,6 +8,7 @@
|
||||
#define _GNU_SOURCE
|
||||
#include "backend_utils.h"
|
||||
#include "internal.h"
|
||||
#include "memfd.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -350,3 +351,72 @@ GLFWAPI char* utf_8_strndup(const char* source, size_t max_length) {
|
||||
result[length] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new, unique, anonymous file of the given size, and
|
||||
* return the file descriptor for it. The file descriptor is set
|
||||
* CLOEXEC. The file is immediately suitable for mmap()'ing
|
||||
* the given size at offset zero.
|
||||
*
|
||||
* The file should not have a permanent backing store like a disk,
|
||||
* but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
|
||||
*
|
||||
* The file name is deleted from the file system.
|
||||
*
|
||||
* The file is suitable for buffer sharing between processes by
|
||||
* transmitting the file descriptor over Unix sockets using the
|
||||
* SCM_RIGHTS methods.
|
||||
*
|
||||
* posix_fallocate() is used to guarantee that disk space is available
|
||||
* for the file at the given size. If disk space is insufficient, errno
|
||||
* is set to ENOSPC. If posix_fallocate() is not supported, program may
|
||||
* receive SIGBUS on accessing mmap()'ed file contents instead.
|
||||
*/
|
||||
int createAnonymousFile(off_t size) {
|
||||
int ret, fd = -1, shm_anon = 0;
|
||||
#ifdef HAS_MEMFD_CREATE
|
||||
fd = memfd_create("glfw-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
||||
if (fd < 0) return -1;
|
||||
// We can add this seal before calling posix_fallocate(), as the file
|
||||
// is currently zero-sized anyway.
|
||||
//
|
||||
// There is also no need to check for the return value, we couldn’t do
|
||||
// anything with it anyway.
|
||||
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
|
||||
#elif defined(SHM_ANON)
|
||||
fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600);
|
||||
if (fd < 0) return -1;
|
||||
shm_anon = 1;
|
||||
#else
|
||||
static const char template[] = "/glfw-shared-XXXXXX";
|
||||
const char* path;
|
||||
char* name;
|
||||
|
||||
path = getenv("XDG_RUNTIME_DIR");
|
||||
if (!path)
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
name = calloc(strlen(path) + sizeof(template), 1);
|
||||
strcpy(name, path);
|
||||
strcat(name, template);
|
||||
|
||||
fd = createTmpfileCloexec(name);
|
||||
|
||||
free(name);
|
||||
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
#endif
|
||||
// posix_fallocate does not work on SHM descriptors
|
||||
ret = shm_anon ? ftruncate(fd, size) : posix_fallocate(fd, 0, size);
|
||||
if (ret != 0)
|
||||
{
|
||||
close(fd);
|
||||
errno = ret;
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
6
glfw/backend_utils.h
vendored
6
glfw/backend_utils.h
vendored
@@ -1,7 +1,7 @@
|
||||
//========================================================================
|
||||
// GLFW 3.4 Wayland - www.glfw.org
|
||||
// GLFW 3.4
|
||||
//------------------------------------------------------------------------
|
||||
// Copyright (c) 2014 Jonas Ådahl <jadahl@gmail.com>
|
||||
// Copyright (c) 2014 Kovid Goyal
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include(<sys/eventfd.h>)
|
||||
@@ -96,3 +97,4 @@ 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);
|
||||
|
||||
@@ -175,138 +175,6 @@ static void createMenuBar(void)
|
||||
[NSApp performSelector:setAppleMenuSelector withObject:appMenu];
|
||||
}
|
||||
|
||||
// Create key code translation tables
|
||||
//
|
||||
static void createKeyTables(void)
|
||||
{
|
||||
int keycode;
|
||||
|
||||
memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes));
|
||||
memset(_glfw.ns.key_to_keycode, -1, sizeof(_glfw.ns.key_to_keycode));
|
||||
|
||||
_glfw.ns.keycodes[0x1D] = GLFW_KEY_0;
|
||||
_glfw.ns.keycodes[0x12] = GLFW_KEY_1;
|
||||
_glfw.ns.keycodes[0x13] = GLFW_KEY_2;
|
||||
_glfw.ns.keycodes[0x14] = GLFW_KEY_3;
|
||||
_glfw.ns.keycodes[0x15] = GLFW_KEY_4;
|
||||
_glfw.ns.keycodes[0x17] = GLFW_KEY_5;
|
||||
_glfw.ns.keycodes[0x16] = GLFW_KEY_6;
|
||||
_glfw.ns.keycodes[0x1A] = GLFW_KEY_7;
|
||||
_glfw.ns.keycodes[0x1C] = GLFW_KEY_8;
|
||||
_glfw.ns.keycodes[0x19] = GLFW_KEY_9;
|
||||
_glfw.ns.keycodes[0x00] = GLFW_KEY_A;
|
||||
_glfw.ns.keycodes[0x0B] = GLFW_KEY_B;
|
||||
_glfw.ns.keycodes[0x08] = GLFW_KEY_C;
|
||||
_glfw.ns.keycodes[0x02] = GLFW_KEY_D;
|
||||
_glfw.ns.keycodes[0x0E] = GLFW_KEY_E;
|
||||
_glfw.ns.keycodes[0x03] = GLFW_KEY_F;
|
||||
_glfw.ns.keycodes[0x05] = GLFW_KEY_G;
|
||||
_glfw.ns.keycodes[0x04] = GLFW_KEY_H;
|
||||
_glfw.ns.keycodes[0x22] = GLFW_KEY_I;
|
||||
_glfw.ns.keycodes[0x26] = GLFW_KEY_J;
|
||||
_glfw.ns.keycodes[0x28] = GLFW_KEY_K;
|
||||
_glfw.ns.keycodes[0x25] = GLFW_KEY_L;
|
||||
_glfw.ns.keycodes[0x2E] = GLFW_KEY_M;
|
||||
_glfw.ns.keycodes[0x2D] = GLFW_KEY_N;
|
||||
_glfw.ns.keycodes[0x1F] = GLFW_KEY_O;
|
||||
_glfw.ns.keycodes[0x23] = GLFW_KEY_P;
|
||||
_glfw.ns.keycodes[0x0C] = GLFW_KEY_Q;
|
||||
_glfw.ns.keycodes[0x0F] = GLFW_KEY_R;
|
||||
_glfw.ns.keycodes[0x01] = GLFW_KEY_S;
|
||||
_glfw.ns.keycodes[0x11] = GLFW_KEY_T;
|
||||
_glfw.ns.keycodes[0x20] = GLFW_KEY_U;
|
||||
_glfw.ns.keycodes[0x09] = GLFW_KEY_V;
|
||||
_glfw.ns.keycodes[0x0D] = GLFW_KEY_W;
|
||||
_glfw.ns.keycodes[0x07] = GLFW_KEY_X;
|
||||
_glfw.ns.keycodes[0x10] = GLFW_KEY_Y;
|
||||
_glfw.ns.keycodes[0x06] = GLFW_KEY_Z;
|
||||
|
||||
_glfw.ns.keycodes[0x27] = GLFW_KEY_APOSTROPHE;
|
||||
_glfw.ns.keycodes[0x2A] = GLFW_KEY_BACKSLASH;
|
||||
_glfw.ns.keycodes[0x2B] = GLFW_KEY_COMMA;
|
||||
_glfw.ns.keycodes[0x18] = GLFW_KEY_EQUAL;
|
||||
_glfw.ns.keycodes[0x32] = GLFW_KEY_GRAVE_ACCENT;
|
||||
_glfw.ns.keycodes[0x21] = GLFW_KEY_LEFT_BRACKET;
|
||||
_glfw.ns.keycodes[0x1B] = GLFW_KEY_MINUS;
|
||||
_glfw.ns.keycodes[0x2F] = GLFW_KEY_PERIOD;
|
||||
_glfw.ns.keycodes[0x1E] = GLFW_KEY_RIGHT_BRACKET;
|
||||
_glfw.ns.keycodes[0x29] = GLFW_KEY_SEMICOLON;
|
||||
_glfw.ns.keycodes[0x2C] = GLFW_KEY_SLASH;
|
||||
_glfw.ns.keycodes[0x0A] = GLFW_KEY_WORLD_1;
|
||||
|
||||
_glfw.ns.keycodes[0x33] = GLFW_KEY_BACKSPACE;
|
||||
_glfw.ns.keycodes[0x39] = GLFW_KEY_CAPS_LOCK;
|
||||
_glfw.ns.keycodes[0x75] = GLFW_KEY_DELETE;
|
||||
_glfw.ns.keycodes[0x7D] = GLFW_KEY_DOWN;
|
||||
_glfw.ns.keycodes[0x77] = GLFW_KEY_END;
|
||||
_glfw.ns.keycodes[0x24] = GLFW_KEY_ENTER;
|
||||
_glfw.ns.keycodes[0x35] = GLFW_KEY_ESCAPE;
|
||||
_glfw.ns.keycodes[0x7A] = GLFW_KEY_F1;
|
||||
_glfw.ns.keycodes[0x78] = GLFW_KEY_F2;
|
||||
_glfw.ns.keycodes[0x63] = GLFW_KEY_F3;
|
||||
_glfw.ns.keycodes[0x76] = GLFW_KEY_F4;
|
||||
_glfw.ns.keycodes[0x60] = GLFW_KEY_F5;
|
||||
_glfw.ns.keycodes[0x61] = GLFW_KEY_F6;
|
||||
_glfw.ns.keycodes[0x62] = GLFW_KEY_F7;
|
||||
_glfw.ns.keycodes[0x64] = GLFW_KEY_F8;
|
||||
_glfw.ns.keycodes[0x65] = GLFW_KEY_F9;
|
||||
_glfw.ns.keycodes[0x6D] = GLFW_KEY_F10;
|
||||
_glfw.ns.keycodes[0x67] = GLFW_KEY_F11;
|
||||
_glfw.ns.keycodes[0x6F] = GLFW_KEY_F12;
|
||||
_glfw.ns.keycodes[0x69] = GLFW_KEY_F13;
|
||||
_glfw.ns.keycodes[0x6B] = GLFW_KEY_F14;
|
||||
_glfw.ns.keycodes[0x71] = GLFW_KEY_F15;
|
||||
_glfw.ns.keycodes[0x6A] = GLFW_KEY_F16;
|
||||
_glfw.ns.keycodes[0x40] = GLFW_KEY_F17;
|
||||
_glfw.ns.keycodes[0x4F] = GLFW_KEY_F18;
|
||||
_glfw.ns.keycodes[0x50] = GLFW_KEY_F19;
|
||||
_glfw.ns.keycodes[0x5A] = GLFW_KEY_F20;
|
||||
_glfw.ns.keycodes[0x73] = GLFW_KEY_HOME;
|
||||
_glfw.ns.keycodes[0x72] = GLFW_KEY_INSERT;
|
||||
_glfw.ns.keycodes[0x7B] = GLFW_KEY_LEFT;
|
||||
_glfw.ns.keycodes[0x3A] = GLFW_KEY_LEFT_ALT;
|
||||
_glfw.ns.keycodes[0x3B] = GLFW_KEY_LEFT_CONTROL;
|
||||
_glfw.ns.keycodes[0x38] = GLFW_KEY_LEFT_SHIFT;
|
||||
_glfw.ns.keycodes[0x37] = GLFW_KEY_LEFT_SUPER;
|
||||
_glfw.ns.keycodes[0x6E] = GLFW_KEY_MENU;
|
||||
_glfw.ns.keycodes[0x47] = GLFW_KEY_NUM_LOCK;
|
||||
_glfw.ns.keycodes[0x79] = GLFW_KEY_PAGE_DOWN;
|
||||
_glfw.ns.keycodes[0x74] = GLFW_KEY_PAGE_UP;
|
||||
_glfw.ns.keycodes[0x7C] = GLFW_KEY_RIGHT;
|
||||
_glfw.ns.keycodes[0x3D] = GLFW_KEY_RIGHT_ALT;
|
||||
_glfw.ns.keycodes[0x3E] = GLFW_KEY_RIGHT_CONTROL;
|
||||
_glfw.ns.keycodes[0x3C] = GLFW_KEY_RIGHT_SHIFT;
|
||||
_glfw.ns.keycodes[0x36] = GLFW_KEY_RIGHT_SUPER;
|
||||
_glfw.ns.keycodes[0x31] = GLFW_KEY_SPACE;
|
||||
_glfw.ns.keycodes[0x30] = GLFW_KEY_TAB;
|
||||
_glfw.ns.keycodes[0x7E] = GLFW_KEY_UP;
|
||||
|
||||
_glfw.ns.keycodes[0x52] = GLFW_KEY_KP_0;
|
||||
_glfw.ns.keycodes[0x53] = GLFW_KEY_KP_1;
|
||||
_glfw.ns.keycodes[0x54] = GLFW_KEY_KP_2;
|
||||
_glfw.ns.keycodes[0x55] = GLFW_KEY_KP_3;
|
||||
_glfw.ns.keycodes[0x56] = GLFW_KEY_KP_4;
|
||||
_glfw.ns.keycodes[0x57] = GLFW_KEY_KP_5;
|
||||
_glfw.ns.keycodes[0x58] = GLFW_KEY_KP_6;
|
||||
_glfw.ns.keycodes[0x59] = GLFW_KEY_KP_7;
|
||||
_glfw.ns.keycodes[0x5B] = GLFW_KEY_KP_8;
|
||||
_glfw.ns.keycodes[0x5C] = GLFW_KEY_KP_9;
|
||||
_glfw.ns.keycodes[0x45] = GLFW_KEY_KP_ADD;
|
||||
_glfw.ns.keycodes[0x41] = GLFW_KEY_KP_DECIMAL;
|
||||
_glfw.ns.keycodes[0x4B] = GLFW_KEY_KP_DIVIDE;
|
||||
_glfw.ns.keycodes[0x4C] = GLFW_KEY_KP_ENTER;
|
||||
_glfw.ns.keycodes[0x51] = GLFW_KEY_KP_EQUAL;
|
||||
_glfw.ns.keycodes[0x43] = GLFW_KEY_KP_MULTIPLY;
|
||||
_glfw.ns.keycodes[0x4E] = GLFW_KEY_KP_SUBTRACT;
|
||||
|
||||
for (keycode = 0; keycode < 256; keycode++)
|
||||
{
|
||||
// Store the reverse translation for faster key name lookup
|
||||
if (_glfw.ns.keycodes[keycode] >= 0)
|
||||
_glfw.ns.key_to_keycode[_glfw.ns.keycodes[keycode]] = keycode;
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve Unicode data for the current keyboard layout
|
||||
//
|
||||
static bool updateUnicodeDataNS(void)
|
||||
@@ -478,10 +346,40 @@ static GLFWapplicationwillfinishlaunchingfun finish_launching_callback = NULL;
|
||||
finish_launching_callback();
|
||||
}
|
||||
|
||||
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename {
|
||||
(void)theApplication;
|
||||
if (!filename || !_glfw.ns.file_open_callback) return NO;
|
||||
const char *path = NULL;
|
||||
@try {
|
||||
path = [[NSFileManager defaultManager] fileSystemRepresentationWithPath: filename];
|
||||
} @catch(NSException *exc) {
|
||||
NSLog(@"Converting openFile filename: %@ failed with error: %@", filename, exc.reason);
|
||||
return NO;
|
||||
}
|
||||
if (!path) return NO;
|
||||
return _glfw.ns.file_open_callback(path);
|
||||
}
|
||||
|
||||
- (void)application:(NSApplication *)sender openFiles:(NSArray *)filenames {
|
||||
(void)sender;
|
||||
if (!_glfw.ns.file_open_callback || !filenames) return;
|
||||
for (id x in filenames) {
|
||||
NSString *filename = x;
|
||||
const char *path = NULL;
|
||||
@try {
|
||||
path = [[NSFileManager defaultManager] fileSystemRepresentationWithPath: filename];
|
||||
} @catch(NSException *exc) {
|
||||
NSLog(@"Converting openFiles filename: %@ failed with error: %@", filename, exc.reason);
|
||||
}
|
||||
if (path) _glfw.ns.file_open_callback(path);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)notification
|
||||
{
|
||||
(void)notification;
|
||||
[NSApp stop:nil];
|
||||
if (_glfw.ns.file_open_callback) _glfw.ns.file_open_callback(":cocoa::application launched::");
|
||||
|
||||
CGDisplayRegisterReconfigurationCallback(display_reconfigured, NULL);
|
||||
_glfwCocoaPostEmptyEvent();
|
||||
@@ -648,8 +546,12 @@ int _glfwPlatformInit(void)
|
||||
if (_glfw.hints.init.ns.chdir)
|
||||
changeToResourcesDirectory();
|
||||
|
||||
// Press and Hold prevents some keys from emitting repeated characters
|
||||
NSDictionary* defaults = @{@"ApplePressAndHoldEnabled":@NO};
|
||||
NSDictionary* defaults = @{
|
||||
// Press and Hold prevents some keys from emitting repeated characters
|
||||
@"ApplePressAndHoldEnabled": @NO,
|
||||
// Dont generate openFile events from command line arguments
|
||||
@"NSTreatUnknownArgumentsAsOpen": @"NO",
|
||||
};
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
|
||||
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
@@ -658,8 +560,6 @@ int _glfwPlatformInit(void)
|
||||
name:NSTextInputContextKeyboardSelectionDidChangeNotification
|
||||
object:nil];
|
||||
|
||||
createKeyTables();
|
||||
|
||||
_glfw.ns.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
if (!_glfw.ns.eventSource)
|
||||
return false;
|
||||
|
||||
@@ -89,7 +89,7 @@ static char* getDisplayName(CGDirectDisplayID displayID)
|
||||
if (!service)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Cocoa: Failed to find service port for display");
|
||||
"Cocoa: Failed to find service port for display, cannot get its name, using Unknown");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -326,7 +326,7 @@ static CVReturn displayLinkCallback(
|
||||
const CVTimeStamp* now UNUSED, const CVTimeStamp* outputTime UNUSED,
|
||||
CVOptionFlags flagsIn UNUSED, CVOptionFlags* flagsOut UNUSED, void* userInfo)
|
||||
{
|
||||
CGDirectDisplayID displayID = (CGDirectDisplayID)userInfo;
|
||||
CGDirectDisplayID displayID = (uintptr_t)userInfo;
|
||||
NSNumber *arg = [NSNumber numberWithUnsignedInt:displayID];
|
||||
[NSApp performSelectorOnMainThread:@selector(render_frame_received:) withObject:arg waitUntilDone:NO];
|
||||
[arg release];
|
||||
|
||||
8
glfw/cocoa_platform.h
vendored
8
glfw/cocoa_platform.h
vendored
@@ -67,6 +67,7 @@ typedef void* CVDisplayLinkRef;
|
||||
|
||||
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int, unsigned long);
|
||||
typedef bool (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef bool (* GLFWhandlefileopen)(const char*);
|
||||
typedef void (* GLFWapplicationwillfinishlaunchingfun)(void);
|
||||
typedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
|
||||
@@ -128,6 +129,9 @@ typedef struct _GLFWwindowNS
|
||||
|
||||
bool maximized;
|
||||
bool retina;
|
||||
bool in_traditional_fullscreen;
|
||||
bool titlebar_hidden;
|
||||
unsigned long pre_full_screen_style_mask;
|
||||
|
||||
// Cached window properties to filter out duplicate events
|
||||
int width, height;
|
||||
@@ -175,8 +179,6 @@ typedef struct _GLFWlibraryNS
|
||||
|
||||
char keyName[64];
|
||||
char text[256];
|
||||
short int keycodes[256];
|
||||
short int key_to_keycode[GLFW_KEY_LAST + 1];
|
||||
char* clipboardString;
|
||||
CGPoint cascadePoint;
|
||||
// Where to place the cursor when re-enabled
|
||||
@@ -196,6 +198,8 @@ typedef struct _GLFWlibraryNS
|
||||
_GLFWDisplayLinkNS entries[256];
|
||||
size_t count;
|
||||
} displayLinks;
|
||||
// the callback to handle file open events
|
||||
GLFWhandlefileopen file_open_callback;
|
||||
|
||||
} _GLFWlibraryNS;
|
||||
|
||||
|
||||
1019
glfw/cocoa_window.m
1019
glfw/cocoa_window.m
File diff suppressed because it is too large
Load Diff
27
glfw/glfw.py
27
glfw/glfw.py
@@ -55,7 +55,7 @@ def wayland_protocol_file_name(base: str, ext: str = 'c') -> str:
|
||||
return 'wayland-{}-client-protocol.{}'.format(base, ext)
|
||||
|
||||
|
||||
def init_env(env: Env, pkg_config: Callable, at_least_version: Callable, test_compile: Callable, module: str = 'x11') -> Env:
|
||||
def init_env(env: Env, pkg_config: Callable, pkg_version: Callable, at_least_version: Callable, test_compile: Callable, module: str = 'x11') -> Env:
|
||||
ans = env.copy()
|
||||
ans.cflags.append('-fPIC')
|
||||
ans.cppflags.append('-D_GLFW_' + module.upper())
|
||||
@@ -76,7 +76,11 @@ def init_env(env: Env, pkg_config: Callable, at_least_version: Callable, test_co
|
||||
ans.ldpaths.extend('-pthread -lm'.split())
|
||||
if not is_openbsd:
|
||||
ans.ldpaths.extend('-lrt -ldl'.split())
|
||||
at_least_version('xkbcommon', 0, 5)
|
||||
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():
|
||||
@@ -85,7 +89,7 @@ def init_env(env: Env, pkg_config: Callable, at_least_version: Callable, test_co
|
||||
|
||||
elif module == 'cocoa':
|
||||
ans.cppflags.append('-DGL_SILENCE_DEPRECATION')
|
||||
for f_ in 'Cocoa IOKit CoreFoundation CoreVideo'.split():
|
||||
for f_ in 'Cocoa Carbon IOKit CoreFoundation CoreVideo'.split():
|
||||
ans.ldpaths.extend(('-framework', f_))
|
||||
|
||||
elif module == 'wayland':
|
||||
@@ -175,13 +179,12 @@ class Function:
|
||||
)
|
||||
|
||||
def load(self) -> str:
|
||||
ans = '*(void **) (&{name}_impl) = dlsym(handle, "{name}");'.format(
|
||||
name=self.name
|
||||
)
|
||||
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
|
||||
|
||||
|
||||
@@ -199,13 +202,15 @@ def generate_wrappers(glfw_header: str) -> None:
|
||||
functions.append(Function(decl))
|
||||
for line in '''\
|
||||
void* glfwGetCocoaWindow(GLFWwindow* window)
|
||||
void glfwHideCocoaTitlebar(GLFWwindow* window, bool yes)
|
||||
void* glfwGetNSGLContext(GLFWwindow *window)
|
||||
uint32_t glfwGetCocoaMonitor(GLFWmonitor* monitor)
|
||||
GLFWcocoatextinputfilterfun glfwSetCocoaTextInputFilter(GLFWwindow* window, GLFWcocoatextinputfilterfun callback)
|
||||
GLFWhandlefileopen glfwSetCocoaFileOpenCallback(GLFWhandlefileopen callback)
|
||||
GLFWcocoatogglefullscreenfun glfwSetCocoaToggleFullscreenIntercept(GLFWwindow *window, GLFWcocoatogglefullscreenfun callback)
|
||||
GLFWapplicationshouldhandlereopenfun glfwSetApplicationShouldHandleReopen(GLFWapplicationshouldhandlereopenfun callback)
|
||||
GLFWapplicationwillfinishlaunchingfun glfwSetApplicationWillFinishLaunching(GLFWapplicationwillfinishlaunchingfun callback)
|
||||
void glfwGetCocoaKeyEquivalent(int glfw_key, int glfw_mods, char* cocoa_key, size_t key_sz, int* cocoa_mods)
|
||||
uint32_t glfwGetCocoaKeyEquivalent(uint32_t glfw_key, int glfw_mods, int* cocoa_mods)
|
||||
void glfwCocoaRequestRenderFrame(GLFWwindow *w, GLFWcocoarenderframefun callback)
|
||||
void* glfwGetX11Display(void)
|
||||
int32_t glfwGetX11Window(GLFWwindow* window)
|
||||
@@ -213,6 +218,7 @@ def generate_wrappers(glfw_header: str) -> None:
|
||||
const char* glfwGetPrimarySelectionString(GLFWwindow* window, void)
|
||||
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)
|
||||
@@ -243,6 +249,7 @@ const char *action_text, int32_t timeout, GLFWDBusnotificationcreatedfun callbac
|
||||
|
||||
typedef int (* GLFWcocoatextinputfilterfun)(int,int,unsigned int,unsigned long);
|
||||
typedef bool (* GLFWapplicationshouldhandlereopenfun)(int);
|
||||
typedef bool (* GLFWhandlefileopen)(const char*);
|
||||
typedef void (* GLFWapplicationwillfinishlaunchingfun)(void);
|
||||
typedef bool (* GLFWcocoatogglefullscreenfun)(GLFWwindow*);
|
||||
typedef void (* GLFWcocoarenderframefun)(GLFWwindow*);
|
||||
|
||||
518
glfw/glfw3.h
vendored
518
glfw/glfw3.h
vendored
@@ -300,31 +300,6 @@ extern "C" {
|
||||
#define GLFW_VERSION_REVISION 0
|
||||
/*! @} */
|
||||
|
||||
/*! @name Key and button actions
|
||||
* @{ */
|
||||
/*! @brief The key or mouse button was released.
|
||||
*
|
||||
* The key or mouse button was released.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
#define GLFW_RELEASE 0
|
||||
/*! @brief The key or mouse button was pressed.
|
||||
*
|
||||
* The key or mouse button was pressed.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
#define GLFW_PRESS 1
|
||||
/*! @brief The key was held down until it repeated.
|
||||
*
|
||||
* The key was held down until it repeated.
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
#define GLFW_REPEAT 2
|
||||
/*! @} */
|
||||
|
||||
/*! @defgroup hat_state Joystick hat states
|
||||
* @brief Joystick hat states.
|
||||
*
|
||||
@@ -367,200 +342,123 @@ extern "C" {
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* The unknown key */
|
||||
#define GLFW_KEY_UNKNOWN -1
|
||||
|
||||
/* Printable keys */
|
||||
#define GLFW_KEY_SPACE 32
|
||||
#define GLFW_KEY_EXCLAM 33 /* ! */
|
||||
#define GLFW_KEY_DOUBLE_QUOTE 34 /* " */
|
||||
#define GLFW_KEY_NUMBER_SIGN 35 /* # */
|
||||
#define GLFW_KEY_DOLLAR 36 /* $ */
|
||||
#define GLFW_KEY_AMPERSAND 38 /* & */
|
||||
#define GLFW_KEY_APOSTROPHE 39 /* ' */
|
||||
#define GLFW_KEY_PARENTHESIS_LEFT 40 /* ( */
|
||||
#define GLFW_KEY_PARENTHESIS_RIGHT 41 /* ) */
|
||||
#define GLFW_KEY_PLUS 43 /* + */
|
||||
#define GLFW_KEY_COMMA 44 /* , */
|
||||
#define GLFW_KEY_MINUS 45 /* - */
|
||||
#define GLFW_KEY_PERIOD 46 /* . */
|
||||
#define GLFW_KEY_SLASH 47 /* / */
|
||||
#define GLFW_KEY_0 48
|
||||
#define GLFW_KEY_1 49
|
||||
#define GLFW_KEY_2 50
|
||||
#define GLFW_KEY_3 51
|
||||
#define GLFW_KEY_4 52
|
||||
#define GLFW_KEY_5 53
|
||||
#define GLFW_KEY_6 54
|
||||
#define GLFW_KEY_7 55
|
||||
#define GLFW_KEY_8 56
|
||||
#define GLFW_KEY_9 57
|
||||
#define GLFW_KEY_COLON 58 /* : */
|
||||
#define GLFW_KEY_SEMICOLON 59 /* ; */
|
||||
#define GLFW_KEY_LESS 60 /* < */
|
||||
#define GLFW_KEY_EQUAL 61 /* = */
|
||||
#define GLFW_KEY_GREATER 62 /* > */
|
||||
#define GLFW_KEY_AT 64 /* @ */
|
||||
#define GLFW_KEY_A 65
|
||||
#define GLFW_KEY_B 66
|
||||
#define GLFW_KEY_C 67
|
||||
#define GLFW_KEY_D 68
|
||||
#define GLFW_KEY_E 69
|
||||
#define GLFW_KEY_F 70
|
||||
#define GLFW_KEY_G 71
|
||||
#define GLFW_KEY_H 72
|
||||
#define GLFW_KEY_I 73
|
||||
#define GLFW_KEY_J 74
|
||||
#define GLFW_KEY_K 75
|
||||
#define GLFW_KEY_L 76
|
||||
#define GLFW_KEY_M 77
|
||||
#define GLFW_KEY_N 78
|
||||
#define GLFW_KEY_O 79
|
||||
#define GLFW_KEY_P 80
|
||||
#define GLFW_KEY_Q 81
|
||||
#define GLFW_KEY_R 82
|
||||
#define GLFW_KEY_S 83
|
||||
#define GLFW_KEY_T 84
|
||||
#define GLFW_KEY_U 85
|
||||
#define GLFW_KEY_V 86
|
||||
#define GLFW_KEY_W 87
|
||||
#define GLFW_KEY_X 88
|
||||
#define GLFW_KEY_Y 89
|
||||
#define GLFW_KEY_Z 90
|
||||
#define GLFW_KEY_LEFT_BRACKET 91 /* [ */
|
||||
#define GLFW_KEY_BACKSLASH 92 /* \ */
|
||||
#define GLFW_KEY_RIGHT_BRACKET 93 /* ] */
|
||||
#define GLFW_KEY_CIRCUMFLEX 94 /* ^ */
|
||||
#define GLFW_KEY_UNDERSCORE 95 /* _ */
|
||||
#define GLFW_KEY_GRAVE_ACCENT 96 /* ` */
|
||||
#define GLFW_KEY_WORLD_1 161 /* non-US #1 */
|
||||
#define GLFW_KEY_WORLD_2 162 /* non-US #2 */
|
||||
#define GLFW_KEY_PARAGRAPH 167 /* § */
|
||||
#define GLFW_KEY_MASCULINE 186 /* º */
|
||||
#define GLFW_KEY_A_GRAVE 192 /* À */
|
||||
#define GLFW_KEY_A_DIAERESIS 196 /* Ä */
|
||||
#define GLFW_KEY_A_RING 197 /* Å */
|
||||
#define GLFW_KEY_AE 198 /* Æ */
|
||||
#define GLFW_KEY_C_CEDILLA 199 /* Ç */
|
||||
#define GLFW_KEY_E_GRAVE 200 /* È */
|
||||
#define GLFW_KEY_E_ACUTE 201 /* É */
|
||||
#define GLFW_KEY_I_GRAVE 204 /* Ì */
|
||||
#define GLFW_KEY_N_TILDE 209 /* Ñ */
|
||||
#define GLFW_KEY_O_GRAVE 210 /* Ò */
|
||||
#define GLFW_KEY_O_DIAERESIS 214 /* Ö */
|
||||
#define GLFW_KEY_O_SLASH 216 /* Ø */
|
||||
#define GLFW_KEY_U_GRAVE 217 /* Ù */
|
||||
#define GLFW_KEY_U_DIAERESIS 220 /* Ü */
|
||||
#define GLFW_KEY_S_SHARP 222 /* ß */
|
||||
#define GLFW_KEY_CYRILLIC_A 223 /* А */
|
||||
#define GLFW_KEY_CYRILLIC_BE 224 /* Б */
|
||||
#define GLFW_KEY_CYRILLIC_VE 225 /* В */
|
||||
#define GLFW_KEY_CYRILLIC_GHE 226 /* Г */
|
||||
#define GLFW_KEY_CYRILLIC_DE 227 /* Д */
|
||||
#define GLFW_KEY_CYRILLIC_IE 228 /* Е */
|
||||
#define GLFW_KEY_CYRILLIC_ZHE 229 /* Ж */
|
||||
#define GLFW_KEY_CYRILLIC_ZE 230 /* З */
|
||||
#define GLFW_KEY_CYRILLIC_I 231 /* И */
|
||||
#define GLFW_KEY_CYRILLIC_SHORT_I 232 /* Й */
|
||||
#define GLFW_KEY_CYRILLIC_KA 233 /* К */
|
||||
#define GLFW_KEY_CYRILLIC_EL 234 /* Л */
|
||||
#define GLFW_KEY_CYRILLIC_EM 235 /* М */
|
||||
#define GLFW_KEY_CYRILLIC_EN 236 /* Н */
|
||||
#define GLFW_KEY_CYRILLIC_O 237 /* О */
|
||||
#define GLFW_KEY_CYRILLIC_PE 238 /* П */
|
||||
#define GLFW_KEY_CYRILLIC_ER 239 /* Р */
|
||||
#define GLFW_KEY_CYRILLIC_ES 240 /* С */
|
||||
#define GLFW_KEY_CYRILLIC_TE 241 /* Т */
|
||||
#define GLFW_KEY_CYRILLIC_U 242 /* У */
|
||||
#define GLFW_KEY_CYRILLIC_EF 243 /* Ф */
|
||||
#define GLFW_KEY_CYRILLIC_HA 244 /* Х */
|
||||
#define GLFW_KEY_CYRILLIC_TSE 245 /* Ц */
|
||||
#define GLFW_KEY_CYRILLIC_CHE 246 /* Ч */
|
||||
#define GLFW_KEY_CYRILLIC_SHA 247 /* Ш */
|
||||
#define GLFW_KEY_CYRILLIC_SHCHA 248 /* Щ */
|
||||
#define GLFW_KEY_CYRILLIC_HARD_SIGN 249 /* Ъ */
|
||||
#define GLFW_KEY_CYRILLIC_YERU 250 /* Ы */
|
||||
#define GLFW_KEY_CYRILLIC_SOFT_SIGN 251 /* Ь */
|
||||
#define GLFW_KEY_CYRILLIC_E 252 /* Э */
|
||||
#define GLFW_KEY_CYRILLIC_YU 253 /* Ю */
|
||||
#define GLFW_KEY_CYRILLIC_YA 254 /* Я */
|
||||
#define GLFW_KEY_CYRILLIC_IO 255 /* Ё */
|
||||
|
||||
#define GLFW_KEY_LAST_PRINTABLE GLFW_KEY_CYRILLIC_IO
|
||||
|
||||
/* Function keys */
|
||||
#define GLFW_KEY_ESCAPE 256
|
||||
#define GLFW_KEY_ENTER 257
|
||||
#define GLFW_KEY_TAB 258
|
||||
#define GLFW_KEY_BACKSPACE 259
|
||||
#define GLFW_KEY_INSERT 260
|
||||
#define GLFW_KEY_DELETE 261
|
||||
#define GLFW_KEY_RIGHT 262
|
||||
#define GLFW_KEY_LEFT 263
|
||||
#define GLFW_KEY_DOWN 264
|
||||
#define GLFW_KEY_UP 265
|
||||
#define GLFW_KEY_PAGE_UP 266
|
||||
#define GLFW_KEY_PAGE_DOWN 267
|
||||
#define GLFW_KEY_HOME 268
|
||||
#define GLFW_KEY_END 269
|
||||
#define GLFW_KEY_CAPS_LOCK 280
|
||||
#define GLFW_KEY_SCROLL_LOCK 281
|
||||
#define GLFW_KEY_NUM_LOCK 282
|
||||
#define GLFW_KEY_PRINT_SCREEN 283
|
||||
#define GLFW_KEY_PAUSE 284
|
||||
#define GLFW_KEY_F1 290
|
||||
#define GLFW_KEY_F2 291
|
||||
#define GLFW_KEY_F3 292
|
||||
#define GLFW_KEY_F4 293
|
||||
#define GLFW_KEY_F5 294
|
||||
#define GLFW_KEY_F6 295
|
||||
#define GLFW_KEY_F7 296
|
||||
#define GLFW_KEY_F8 297
|
||||
#define GLFW_KEY_F9 298
|
||||
#define GLFW_KEY_F10 299
|
||||
#define GLFW_KEY_F11 300
|
||||
#define GLFW_KEY_F12 301
|
||||
#define GLFW_KEY_F13 302
|
||||
#define GLFW_KEY_F14 303
|
||||
#define GLFW_KEY_F15 304
|
||||
#define GLFW_KEY_F16 305
|
||||
#define GLFW_KEY_F17 306
|
||||
#define GLFW_KEY_F18 307
|
||||
#define GLFW_KEY_F19 308
|
||||
#define GLFW_KEY_F20 309
|
||||
#define GLFW_KEY_F21 310
|
||||
#define GLFW_KEY_F22 311
|
||||
#define GLFW_KEY_F23 312
|
||||
#define GLFW_KEY_F24 313
|
||||
#define GLFW_KEY_F25 314
|
||||
#define GLFW_KEY_KP_0 320
|
||||
#define GLFW_KEY_KP_1 321
|
||||
#define GLFW_KEY_KP_2 322
|
||||
#define GLFW_KEY_KP_3 323
|
||||
#define GLFW_KEY_KP_4 324
|
||||
#define GLFW_KEY_KP_5 325
|
||||
#define GLFW_KEY_KP_6 326
|
||||
#define GLFW_KEY_KP_7 327
|
||||
#define GLFW_KEY_KP_8 328
|
||||
#define GLFW_KEY_KP_9 329
|
||||
#define GLFW_KEY_KP_DECIMAL 330
|
||||
#define GLFW_KEY_KP_DIVIDE 331
|
||||
#define GLFW_KEY_KP_MULTIPLY 332
|
||||
#define GLFW_KEY_KP_SUBTRACT 333
|
||||
#define GLFW_KEY_KP_ADD 334
|
||||
#define GLFW_KEY_KP_ENTER 335
|
||||
#define GLFW_KEY_KP_EQUAL 336
|
||||
#define GLFW_KEY_LEFT_SHIFT 340
|
||||
#define GLFW_KEY_LEFT_CONTROL 341
|
||||
#define GLFW_KEY_LEFT_ALT 342
|
||||
#define GLFW_KEY_LEFT_SUPER 343
|
||||
#define GLFW_KEY_RIGHT_SHIFT 344
|
||||
#define GLFW_KEY_RIGHT_CONTROL 345
|
||||
#define GLFW_KEY_RIGHT_ALT 346
|
||||
#define GLFW_KEY_RIGHT_SUPER 347
|
||||
#define GLFW_KEY_MENU 348
|
||||
|
||||
#define GLFW_KEY_LAST GLFW_KEY_MENU
|
||||
/* start functional key names (auto generated by gen-key-constants.py do not edit) */
|
||||
typedef enum {
|
||||
GLFW_FKEY_FIRST = 0xe000u,
|
||||
GLFW_FKEY_ESCAPE = 0xe000u,
|
||||
GLFW_FKEY_ENTER = 0xe001u,
|
||||
GLFW_FKEY_TAB = 0xe002u,
|
||||
GLFW_FKEY_BACKSPACE = 0xe003u,
|
||||
GLFW_FKEY_INSERT = 0xe004u,
|
||||
GLFW_FKEY_DELETE = 0xe005u,
|
||||
GLFW_FKEY_LEFT = 0xe006u,
|
||||
GLFW_FKEY_RIGHT = 0xe007u,
|
||||
GLFW_FKEY_UP = 0xe008u,
|
||||
GLFW_FKEY_DOWN = 0xe009u,
|
||||
GLFW_FKEY_PAGE_UP = 0xe00au,
|
||||
GLFW_FKEY_PAGE_DOWN = 0xe00bu,
|
||||
GLFW_FKEY_HOME = 0xe00cu,
|
||||
GLFW_FKEY_END = 0xe00du,
|
||||
GLFW_FKEY_CAPS_LOCK = 0xe00eu,
|
||||
GLFW_FKEY_SCROLL_LOCK = 0xe00fu,
|
||||
GLFW_FKEY_NUM_LOCK = 0xe010u,
|
||||
GLFW_FKEY_PRINT_SCREEN = 0xe011u,
|
||||
GLFW_FKEY_PAUSE = 0xe012u,
|
||||
GLFW_FKEY_MENU = 0xe013u,
|
||||
GLFW_FKEY_F1 = 0xe014u,
|
||||
GLFW_FKEY_F2 = 0xe015u,
|
||||
GLFW_FKEY_F3 = 0xe016u,
|
||||
GLFW_FKEY_F4 = 0xe017u,
|
||||
GLFW_FKEY_F5 = 0xe018u,
|
||||
GLFW_FKEY_F6 = 0xe019u,
|
||||
GLFW_FKEY_F7 = 0xe01au,
|
||||
GLFW_FKEY_F8 = 0xe01bu,
|
||||
GLFW_FKEY_F9 = 0xe01cu,
|
||||
GLFW_FKEY_F10 = 0xe01du,
|
||||
GLFW_FKEY_F11 = 0xe01eu,
|
||||
GLFW_FKEY_F12 = 0xe01fu,
|
||||
GLFW_FKEY_F13 = 0xe020u,
|
||||
GLFW_FKEY_F14 = 0xe021u,
|
||||
GLFW_FKEY_F15 = 0xe022u,
|
||||
GLFW_FKEY_F16 = 0xe023u,
|
||||
GLFW_FKEY_F17 = 0xe024u,
|
||||
GLFW_FKEY_F18 = 0xe025u,
|
||||
GLFW_FKEY_F19 = 0xe026u,
|
||||
GLFW_FKEY_F20 = 0xe027u,
|
||||
GLFW_FKEY_F21 = 0xe028u,
|
||||
GLFW_FKEY_F22 = 0xe029u,
|
||||
GLFW_FKEY_F23 = 0xe02au,
|
||||
GLFW_FKEY_F24 = 0xe02bu,
|
||||
GLFW_FKEY_F25 = 0xe02cu,
|
||||
GLFW_FKEY_F26 = 0xe02du,
|
||||
GLFW_FKEY_F27 = 0xe02eu,
|
||||
GLFW_FKEY_F28 = 0xe02fu,
|
||||
GLFW_FKEY_F29 = 0xe030u,
|
||||
GLFW_FKEY_F30 = 0xe031u,
|
||||
GLFW_FKEY_F31 = 0xe032u,
|
||||
GLFW_FKEY_F32 = 0xe033u,
|
||||
GLFW_FKEY_F33 = 0xe034u,
|
||||
GLFW_FKEY_F34 = 0xe035u,
|
||||
GLFW_FKEY_F35 = 0xe036u,
|
||||
GLFW_FKEY_KP_0 = 0xe037u,
|
||||
GLFW_FKEY_KP_1 = 0xe038u,
|
||||
GLFW_FKEY_KP_2 = 0xe039u,
|
||||
GLFW_FKEY_KP_3 = 0xe03au,
|
||||
GLFW_FKEY_KP_4 = 0xe03bu,
|
||||
GLFW_FKEY_KP_5 = 0xe03cu,
|
||||
GLFW_FKEY_KP_6 = 0xe03du,
|
||||
GLFW_FKEY_KP_7 = 0xe03eu,
|
||||
GLFW_FKEY_KP_8 = 0xe03fu,
|
||||
GLFW_FKEY_KP_9 = 0xe040u,
|
||||
GLFW_FKEY_KP_DECIMAL = 0xe041u,
|
||||
GLFW_FKEY_KP_DIVIDE = 0xe042u,
|
||||
GLFW_FKEY_KP_MULTIPLY = 0xe043u,
|
||||
GLFW_FKEY_KP_SUBTRACT = 0xe044u,
|
||||
GLFW_FKEY_KP_ADD = 0xe045u,
|
||||
GLFW_FKEY_KP_ENTER = 0xe046u,
|
||||
GLFW_FKEY_KP_EQUAL = 0xe047u,
|
||||
GLFW_FKEY_KP_SEPARATOR = 0xe048u,
|
||||
GLFW_FKEY_KP_LEFT = 0xe049u,
|
||||
GLFW_FKEY_KP_RIGHT = 0xe04au,
|
||||
GLFW_FKEY_KP_UP = 0xe04bu,
|
||||
GLFW_FKEY_KP_DOWN = 0xe04cu,
|
||||
GLFW_FKEY_KP_PAGE_UP = 0xe04du,
|
||||
GLFW_FKEY_KP_PAGE_DOWN = 0xe04eu,
|
||||
GLFW_FKEY_KP_HOME = 0xe04fu,
|
||||
GLFW_FKEY_KP_END = 0xe050u,
|
||||
GLFW_FKEY_KP_INSERT = 0xe051u,
|
||||
GLFW_FKEY_KP_DELETE = 0xe052u,
|
||||
GLFW_FKEY_KP_BEGIN = 0xe053u,
|
||||
GLFW_FKEY_MEDIA_PLAY = 0xe054u,
|
||||
GLFW_FKEY_MEDIA_PAUSE = 0xe055u,
|
||||
GLFW_FKEY_MEDIA_PLAY_PAUSE = 0xe056u,
|
||||
GLFW_FKEY_MEDIA_REVERSE = 0xe057u,
|
||||
GLFW_FKEY_MEDIA_STOP = 0xe058u,
|
||||
GLFW_FKEY_MEDIA_FAST_FORWARD = 0xe059u,
|
||||
GLFW_FKEY_MEDIA_REWIND = 0xe05au,
|
||||
GLFW_FKEY_MEDIA_TRACK_NEXT = 0xe05bu,
|
||||
GLFW_FKEY_MEDIA_TRACK_PREVIOUS = 0xe05cu,
|
||||
GLFW_FKEY_MEDIA_RECORD = 0xe05du,
|
||||
GLFW_FKEY_LOWER_VOLUME = 0xe05eu,
|
||||
GLFW_FKEY_RAISE_VOLUME = 0xe05fu,
|
||||
GLFW_FKEY_MUTE_VOLUME = 0xe060u,
|
||||
GLFW_FKEY_LEFT_SHIFT = 0xe061u,
|
||||
GLFW_FKEY_LEFT_CONTROL = 0xe062u,
|
||||
GLFW_FKEY_LEFT_ALT = 0xe063u,
|
||||
GLFW_FKEY_LEFT_SUPER = 0xe064u,
|
||||
GLFW_FKEY_LEFT_HYPER = 0xe065u,
|
||||
GLFW_FKEY_LEFT_META = 0xe066u,
|
||||
GLFW_FKEY_RIGHT_SHIFT = 0xe067u,
|
||||
GLFW_FKEY_RIGHT_CONTROL = 0xe068u,
|
||||
GLFW_FKEY_RIGHT_ALT = 0xe069u,
|
||||
GLFW_FKEY_RIGHT_SUPER = 0xe06au,
|
||||
GLFW_FKEY_RIGHT_HYPER = 0xe06bu,
|
||||
GLFW_FKEY_RIGHT_META = 0xe06cu,
|
||||
GLFW_FKEY_ISO_LEVEL3_SHIFT = 0xe06du,
|
||||
GLFW_FKEY_ISO_LEVEL5_SHIFT = 0xe06eu,
|
||||
GLFW_FKEY_LAST = 0xe06eu
|
||||
} GLFWFunctionKey;
|
||||
/* end functional key names */
|
||||
|
||||
/*! @} */
|
||||
|
||||
@@ -577,33 +475,44 @@ extern "C" {
|
||||
* If this bit is set one or more Shift keys were held down.
|
||||
*/
|
||||
#define GLFW_MOD_SHIFT 0x0001
|
||||
/*! @brief If this bit is set one or more Control keys were held down.
|
||||
*
|
||||
* If this bit is set one or more Control keys were held down.
|
||||
*/
|
||||
#define GLFW_MOD_CONTROL 0x0002
|
||||
/*! @brief If this bit is set one or more Alt keys were held down.
|
||||
*
|
||||
* If this bit is set one or more Alt keys were held down.
|
||||
*/
|
||||
#define GLFW_MOD_ALT 0x0004
|
||||
#define GLFW_MOD_ALT 0x0002
|
||||
/*! @brief If this bit is set one or more Alt keys were held down.
|
||||
*
|
||||
* If this bit is set one or more Alt keys were held down.
|
||||
*/
|
||||
#define GLFW_MOD_CONTROL 0x0004
|
||||
/*! @brief If this bit is set one or more Super keys were held down.
|
||||
*
|
||||
* If this bit is set one or more Super keys were held down.
|
||||
*/
|
||||
#define GLFW_MOD_SUPER 0x0008
|
||||
/*! @brief If this bit is set one or more Hyper keys were held down.
|
||||
*
|
||||
* If this bit is set one or more Hyper keys were held down.
|
||||
*/
|
||||
#define GLFW_MOD_HYPER 0x0010
|
||||
/*! @brief If this bit is set one or more Meta keys were held down.
|
||||
*
|
||||
* If this bit is set one or more Meta keys were held down.
|
||||
*/
|
||||
#define GLFW_MOD_META 0x0020
|
||||
/*! @brief If this bit is set the Caps Lock key is enabled.
|
||||
*
|
||||
* If this bit is set the Caps Lock key is enabled and the @ref
|
||||
* GLFW_LOCK_KEY_MODS input mode is set.
|
||||
*/
|
||||
#define GLFW_MOD_CAPS_LOCK 0x0010
|
||||
#define GLFW_MOD_CAPS_LOCK 0x0040
|
||||
/*! @brief If this bit is set the Num Lock key is enabled.
|
||||
*
|
||||
* If this bit is set the Num Lock key is enabled and the @ref
|
||||
* GLFW_LOCK_KEY_MODS input mode is set.
|
||||
*/
|
||||
#define GLFW_MOD_NUM_LOCK 0x0020
|
||||
#define GLFW_MOD_NUM_LOCK 0x0080
|
||||
#define GLFW_LOCK_MASK (GLFW_MOD_NUM_LOCK | GLFW_MOD_CAPS_LOCK)
|
||||
|
||||
/*! @} */
|
||||
|
||||
@@ -1202,6 +1111,7 @@ typedef enum {
|
||||
*/
|
||||
#define GLFW_ANGLE_PLATFORM_TYPE 0x00050002
|
||||
#define GLFW_DEBUG_KEYBOARD 0x00050003
|
||||
#define GLFW_DEBUG_RENDERING 0x00050004
|
||||
/*! @brief macOS specific init hint.
|
||||
*
|
||||
* macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint).
|
||||
@@ -1285,16 +1195,43 @@ typedef struct GLFWwindow GLFWwindow;
|
||||
*/
|
||||
typedef struct GLFWcursor GLFWcursor;
|
||||
|
||||
typedef enum {
|
||||
GLFW_RELEASE = 0,
|
||||
GLFW_PRESS = 1,
|
||||
GLFW_REPEAT = 2
|
||||
} GLFWKeyAction;
|
||||
|
||||
typedef enum {
|
||||
GLFW_IME_NONE,
|
||||
GLFW_IME_PREEDIT_CHANGED,
|
||||
GLFW_IME_COMMIT_TEXT
|
||||
} GLFWIMEState;
|
||||
|
||||
typedef enum {
|
||||
GLFW_IME_UPDATE_FOCUS = 1,
|
||||
GLFW_IME_UPDATE_CURSOR_POSITION = 2
|
||||
} GLFWIMEUpdateType;
|
||||
|
||||
typedef struct GLFWIMEUpdateEvent {
|
||||
GLFWIMEUpdateType type;
|
||||
const char *before_text, *at_text, *after_text;
|
||||
bool focused;
|
||||
struct {
|
||||
int left, top, width, height;
|
||||
} cursor;
|
||||
} GLFWIMEUpdateEvent;
|
||||
|
||||
|
||||
typedef struct GLFWkeyevent
|
||||
{
|
||||
// The [keyboard key](@ref keys) that was pressed or released.
|
||||
int key;
|
||||
uint32_t key, shifted_key, alternate_key;
|
||||
|
||||
// The platform-specific identifier of the key.
|
||||
int native_key;
|
||||
|
||||
// The event action. Either `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`.
|
||||
int action;
|
||||
GLFWKeyAction action;
|
||||
|
||||
// Bit field describing which [modifier keys](@ref mods) were held down.
|
||||
int mods;
|
||||
@@ -1303,9 +1240,9 @@ typedef struct GLFWkeyevent
|
||||
const char *text;
|
||||
|
||||
// Used for Input Method events. Zero for normal key events.
|
||||
// A value of 1 means the pre-edit text for the input event has been changed.
|
||||
// A value of 2 means the text should be committed.
|
||||
int ime_state;
|
||||
// A value of GLFW_IME_PREEDIT_CHANGED means the pre-edit text for the input event has been changed.
|
||||
// A value of GLFW_IME_COMMIT_TEXT means the text should be committed.
|
||||
GLFWIMEState ime_state;
|
||||
} GLFWkeyevent;
|
||||
|
||||
/*! @brief The function pointer type for error callbacks.
|
||||
@@ -1661,7 +1598,7 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double,int,int);
|
||||
* This is the function pointer type for key callbacks. A keyboard
|
||||
* key callback function has the following signature:
|
||||
* @code
|
||||
* void function_name(GLFWwindow* window, int key, int native_key, int action, int mods)
|
||||
* void function_name(GLFWwindow* window, uint32_t key, int native_key, int action, int mods)
|
||||
* @endcode
|
||||
* The semantics of this function are that the key that is interacted with on the
|
||||
* keyboard is reported, and the text, if any generated by the key is reported.
|
||||
@@ -1761,6 +1698,7 @@ typedef void (* GLFWjoystickfun)(int,int);
|
||||
|
||||
typedef void (* GLFWuserdatafun)(unsigned long long, void*);
|
||||
typedef void (* GLFWtickcallback)(void*);
|
||||
typedef bool (* GLFWdrawtextfun)(GLFWwindow *window, const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset, size_t right_margin);
|
||||
|
||||
/*! @brief Video mode type.
|
||||
*
|
||||
@@ -1917,6 +1855,7 @@ GLFWAPI void glfwStopMainLoop(void);
|
||||
GLFWAPI unsigned long long glfwAddTimer(monotonic_t interval, bool repeats, GLFWuserdatafun callback, void * callback_data, GLFWuserdatafun free_callback);
|
||||
GLFWAPI void glfwUpdateTimer(unsigned long long timer_id, monotonic_t interval, bool enabled);
|
||||
GLFWAPI void glfwRemoveTimer(unsigned long long);
|
||||
GLFWAPI GLFWdrawtextfun glfwSetDrawTextFunction(GLFWdrawtextfun function);
|
||||
|
||||
/*! @brief Terminates the GLFW library.
|
||||
*
|
||||
@@ -4300,108 +4239,19 @@ GLFWAPI int glfwRawMouseMotionSupported(void);
|
||||
* __Do not use this function__ for [text input](@ref input_char). You will
|
||||
* break text input for many languages even if it happens to work for yours.
|
||||
*
|
||||
* If the key is `GLFW_KEY_UNKNOWN`, the keycode is used to identify the key,
|
||||
* If the key is `0`, the keycode is used to identify the key,
|
||||
* otherwise the keycode is ignored. If you specify a non-printable key, or
|
||||
* `GLFW_KEY_UNKNOWN` and a keycode that maps to a non-printable key, this
|
||||
* `0` and a keycode that maps to a non-printable key, this
|
||||
* function returns `NULL` but does not emit an error.
|
||||
*
|
||||
* This behavior allows you to always pass in the arguments in the
|
||||
* [key callback](@ref input_key) without modification.
|
||||
*
|
||||
* The printable keys are:
|
||||
* - `GLFW_KEY_SPACE`
|
||||
* - `GLFW_KEY_EXCLAM`
|
||||
* - `GLFW_KEY_DOUBLE_QUOTE`
|
||||
* - `GLFW_KEY_NUMBER_SIGN`
|
||||
* - `GLFW_KEY_DOLLAR`
|
||||
* - `GLFW_KEY_AMPERSAND`
|
||||
* - `GLFW_KEY_APOSTROPHE`
|
||||
* - `GLFW_KEY_PARENTHESIS_LEFT`
|
||||
* - `GLFW_KEY_PARENTHESIS_RIGHT`
|
||||
* - `GLFW_KEY_PLUS`
|
||||
* - `GLFW_KEY_COMMA`
|
||||
* - `GLFW_KEY_MINUS`
|
||||
* - `GLFW_KEY_PERIOD`
|
||||
* - `GLFW_KEY_SLASH`
|
||||
* - `GLFW_KEY_0` to `GLFW_KEY_9`
|
||||
* - `GLFW_KEY_COLON`
|
||||
* - `GLFW_KEY_SEMICOLON`
|
||||
* - `GLFW_KEY_LESS`
|
||||
* - `GLFW_KEY_EQUAL`
|
||||
* - `GLFW_KEY_GREATER`
|
||||
* - `GLFW_KEY_AT`
|
||||
* - `GLFW_KEY_A` to `GLFW_KEY_Z`
|
||||
* - `GLFW_KEY_LEFT_BRACKET`
|
||||
* - `GLFW_KEY_BACKSLASH`
|
||||
* - `GLFW_KEY_RIGHT_BRACKET`
|
||||
* - `GLFW_KEY_CIRCUMFLEX`
|
||||
* - `GLFW_KEY_UNDERSCORE`
|
||||
* - `GLFW_KEY_GRAVE_ACCENT`
|
||||
* - `GLFW_KEY_WORLD_1`
|
||||
* - `GLFW_KEY_WORLD_2`
|
||||
* - `GLFW_KEY_PARAGRAPH`
|
||||
* - `GLFW_KEY_MASCULINE`
|
||||
* - `GLFW_KEY_A_GRAVE`
|
||||
* - `GLFW_KEY_A_DIAERESIS`
|
||||
* - `GLFW_KEY_A_RING`
|
||||
* - `GLFW_KEY_AE`
|
||||
* - `GLFW_KEY_C_CEDILLA`
|
||||
* - `GLFW_KEY_E_GRAVE`
|
||||
* - `GLFW_KEY_E_ACUTE`
|
||||
* - `GLFW_KEY_I_GRAVE`
|
||||
* - `GLFW_KEY_N_TILDE`
|
||||
* - `GLFW_KEY_O_GRAVE`
|
||||
* - `GLFW_KEY_O_DIAERESIS`
|
||||
* - `GLFW_KEY_O_SLASH`
|
||||
* - `GLFW_KEY_U_GRAVE`
|
||||
* - `GLFW_KEY_U_DIAERESIS`
|
||||
* - `GLFW_KEY_S_SHARP`
|
||||
* - `GLFW_KEY_CYRILLIC_A`
|
||||
* - `GLFW_KEY_CYRILLIC_BE`
|
||||
* - `GLFW_KEY_CYRILLIC_VE`
|
||||
* - `GLFW_KEY_CYRILLIC_GHE`
|
||||
* - `GLFW_KEY_CYRILLIC_DE`
|
||||
* - `GLFW_KEY_CYRILLIC_IE`
|
||||
* - `GLFW_KEY_CYRILLIC_ZHE`
|
||||
* - `GLFW_KEY_CYRILLIC_ZE`
|
||||
* - `GLFW_KEY_CYRILLIC_I`
|
||||
* - `GLFW_KEY_CYRILLIC_SHORT_I`
|
||||
* - `GLFW_KEY_CYRILLIC_KA`
|
||||
* - `GLFW_KEY_CYRILLIC_EL`
|
||||
* - `GLFW_KEY_CYRILLIC_EM`
|
||||
* - `GLFW_KEY_CYRILLIC_EN`
|
||||
* - `GLFW_KEY_CYRILLIC_O`
|
||||
* - `GLFW_KEY_CYRILLIC_PE`
|
||||
* - `GLFW_KEY_CYRILLIC_ER`
|
||||
* - `GLFW_KEY_CYRILLIC_ES`
|
||||
* - `GLFW_KEY_CYRILLIC_TE`
|
||||
* - `GLFW_KEY_CYRILLIC_U`
|
||||
* - `GLFW_KEY_CYRILLIC_EF`
|
||||
* - `GLFW_KEY_CYRILLIC_HA`
|
||||
* - `GLFW_KEY_CYRILLIC_TSE`
|
||||
* - `GLFW_KEY_CYRILLIC_CHE`
|
||||
* - `GLFW_KEY_CYRILLIC_SHA`
|
||||
* - `GLFW_KEY_CYRILLIC_SHCHA`
|
||||
* - `GLFW_KEY_CYRILLIC_HARD_SIGN`
|
||||
* - `GLFW_KEY_CYRILLIC_YERU`
|
||||
* - `GLFW_KEY_CYRILLIC_SOFT_SIGN`
|
||||
* - `GLFW_KEY_CYRILLIC_E`
|
||||
* - `GLFW_KEY_CYRILLIC_YU`
|
||||
* - `GLFW_KEY_CYRILLIC_YA`
|
||||
* - `GLFW_KEY_CYRILLIC_IO`
|
||||
* - `GLFW_KEY_KP_0` to `GLFW_KEY_KP_9`
|
||||
* - `GLFW_KEY_KP_DECIMAL`
|
||||
* - `GLFW_KEY_KP_DIVIDE`
|
||||
* - `GLFW_KEY_KP_MULTIPLY`
|
||||
* - `GLFW_KEY_KP_SUBTRACT`
|
||||
* - `GLFW_KEY_KP_ADD`
|
||||
* - `GLFW_KEY_KP_EQUAL`
|
||||
*
|
||||
* Names for printable keys depend on keyboard layout, while names for
|
||||
* non-printable keys are the same across layouts but depend on the application
|
||||
* language and should be localized along with other user interface text.
|
||||
*
|
||||
* @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`.
|
||||
* @param[in] key The key to query, or `0`.
|
||||
* @param[in] native_key The platform-specifc identifier of the key to query.
|
||||
* @return The UTF-8 encoded, layout-specific name of the key, or `NULL`.
|
||||
*
|
||||
@@ -4422,13 +4272,13 @@ GLFWAPI int glfwRawMouseMotionSupported(void);
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI const char* glfwGetKeyName(int key, int native_key);
|
||||
GLFWAPI const char* glfwGetKeyName(uint32_t key, int native_key);
|
||||
|
||||
/*! @brief Returns the platform-specific identifier of the specified key.
|
||||
*
|
||||
* This function returns the platform-specific identifier of the specified key.
|
||||
*
|
||||
* If the key is `GLFW_KEY_UNKNOWN` or does not exist on the keyboard this
|
||||
* If the key is `0` or does not exist on the keyboard this
|
||||
* method will return `-1`.
|
||||
*
|
||||
* @param[in] key Any [named key](@ref keys).
|
||||
@@ -4446,7 +4296,7 @@ GLFWAPI const char* glfwGetKeyName(int key, int native_key);
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI int glfwGetNativeKeyForKey(int key);
|
||||
GLFWAPI int glfwGetNativeKeyForKey(uint32_t key);
|
||||
|
||||
/*! @brief Returns the last reported state of a keyboard key for the specified
|
||||
* window.
|
||||
@@ -4470,7 +4320,7 @@ GLFWAPI int glfwGetNativeKeyForKey(int key);
|
||||
* __Do not use this function__ to implement [text input](@ref input_char).
|
||||
*
|
||||
* @param[in] window The desired window.
|
||||
* @param[in] key The desired [keyboard key](@ref keys). `GLFW_KEY_UNKNOWN` is
|
||||
* @param[in] key The desired [keyboard key](@ref keys). `0` is
|
||||
* not a valid key for this function.
|
||||
* @return One of `GLFW_PRESS` or `GLFW_RELEASE`.
|
||||
*
|
||||
@@ -4486,7 +4336,7 @@ GLFWAPI int glfwGetNativeKeyForKey(int key);
|
||||
*
|
||||
* @ingroup input
|
||||
*/
|
||||
GLFWAPI int glfwGetKey(GLFWwindow* window, int key);
|
||||
GLFWAPI GLFWKeyAction glfwGetKey(GLFWwindow* window, uint32_t key);
|
||||
|
||||
/*! @brief Returns the last reported state of a mouse button for the specified
|
||||
* window.
|
||||
@@ -4722,16 +4572,12 @@ GLFWAPI GLFWkeyboardfun glfwSetKeyboardCallback(GLFWwindow* window, GLFWkeyboard
|
||||
* Used to notify the IME system of changes in state such as focus gained/lost
|
||||
* and text cursor position.
|
||||
*
|
||||
* @param which: What data to notify. 1 means focus and 2 means cursor position.
|
||||
* @param a, b, c, d: Interpreted based on the value of which. When which is 1
|
||||
* a is interpreted as a boolean indicating focus gained/lost. When which is 2
|
||||
* a, b, c, d are the cursor x, y, width and height values (in the window co-ordinate
|
||||
* system).
|
||||
* @param ev: What data to notify.
|
||||
*
|
||||
* @ingroup input
|
||||
* @since Added in version 4.0
|
||||
*/
|
||||
GLFWAPI void glfwUpdateIMEState(GLFWwindow* window, int which, int a, int b, int c, int d);
|
||||
GLFWAPI void glfwUpdateIMEState(GLFWwindow* window, const GLFWIMEUpdateEvent *ev);
|
||||
|
||||
|
||||
/*! @brief Sets the mouse button callback.
|
||||
|
||||
10
glfw/ibus_glfw.c
vendored
10
glfw/ibus_glfw.c
vendored
@@ -107,11 +107,10 @@ get_ibus_text_from_message(DBusMessage *msg) {
|
||||
}
|
||||
|
||||
static inline void
|
||||
send_text(const char *text, int ime_state) {
|
||||
send_text(const char *text, GLFWIMEState ime_state) {
|
||||
_GLFWwindow *w = _glfwFocusedWindow();
|
||||
if (w && w->callbacks.keyboard) {
|
||||
GLFWkeyevent fake_ev;
|
||||
_glfwInitializeKeyEvent(&fake_ev, GLFW_KEY_UNKNOWN, 0, GLFW_PRESS, 0);
|
||||
GLFWkeyevent fake_ev = {.action = GLFW_PRESS};
|
||||
fake_ev.text = text;
|
||||
fake_ev.ime_state = ime_state;
|
||||
w->callbacks.keyboard((GLFWwindow*) w, &fake_ev);
|
||||
@@ -131,11 +130,11 @@ message_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data)
|
||||
case 0:
|
||||
text = get_ibus_text_from_message(msg);
|
||||
debug("IBUS: CommitText: '%s'\n", text ? text : "(nil)");
|
||||
send_text(text, 2);
|
||||
send_text(text, GLFW_IME_COMMIT_TEXT);
|
||||
break;
|
||||
case 1:
|
||||
text = get_ibus_text_from_message(msg);
|
||||
send_text(text, 1);
|
||||
send_text(text, GLFW_IME_PREEDIT_CHANGED);
|
||||
debug("IBUS: UpdatePreeditText: '%s'\n", text ? text : "(nil)");
|
||||
break;
|
||||
case 2:
|
||||
@@ -390,6 +389,7 @@ ibus_key_state(unsigned int glfw_modifiers, int action) {
|
||||
M(ALT, IBUS_MOD1_MASK);
|
||||
M(NUM_LOCK, IBUS_MOD2_MASK);
|
||||
M(SUPER, IBUS_MOD4_MASK);
|
||||
/* To do: figure out how to get super/hyper/meta */
|
||||
#undef M
|
||||
return ans;
|
||||
}
|
||||
|
||||
26
glfw/init.c
vendored
26
glfw/init.c
vendored
@@ -50,14 +50,14 @@ _GLFWlibrary _glfw = { false };
|
||||
//
|
||||
static _GLFWerror _glfwMainThreadError;
|
||||
static GLFWerrorfun _glfwErrorCallback;
|
||||
static _GLFWinitconfig _glfwInitHints =
|
||||
{
|
||||
true, // hat buttons
|
||||
GLFW_ANGLE_PLATFORM_TYPE_NONE, // ANGLE backend
|
||||
false, // debug keyboard
|
||||
{
|
||||
true, // macOS menu bar
|
||||
true // macOS bundle chdir
|
||||
static _GLFWinitconfig _glfwInitHints = {
|
||||
.hatButtons = true,
|
||||
.angleType = GLFW_ANGLE_PLATFORM_TYPE_NONE,
|
||||
.debugKeyboard = false,
|
||||
.debugRendering = false,
|
||||
.ns = {
|
||||
.menubar = true, // macOS menu bar
|
||||
.chdir = true // macOS bundle chdir
|
||||
}
|
||||
};
|
||||
|
||||
@@ -285,6 +285,9 @@ GLFWAPI void glfwInitHint(int hint, int value)
|
||||
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;
|
||||
@@ -375,3 +378,10 @@ GLFWAPI GLFWapplicationclosefun glfwSetApplicationCloseCallback(GLFWapplicationc
|
||||
_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;
|
||||
}
|
||||
|
||||
455
glfw/input.c
vendored
455
glfw/input.c
vendored
@@ -273,34 +273,62 @@ static bool parseMapping(_GLFWmapping* mapping, const char* string)
|
||||
////// GLFW event API //////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void _glfwInitializeKeyEvent(GLFWkeyevent *ev, int key, int native_key, int action, int mods)
|
||||
{
|
||||
ev->key = key;
|
||||
ev->native_key = native_key;
|
||||
ev->action = action;
|
||||
ev->mods = mods;
|
||||
ev->text = NULL;
|
||||
ev->ime_state = 0;
|
||||
static void
|
||||
set_key_action(_GLFWwindow *window, uint32_t key, int val, int idx) {
|
||||
const unsigned sz = arraysz(window->activated_keys);
|
||||
if (idx < 0) {
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
if (window->activated_keys[i].key == 0) {
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (idx < 0) {
|
||||
idx = sz - 1;
|
||||
memmove(window->activated_keys, window->activated_keys + 1, sizeof(window->activated_keys[0]) * (sz - 1));
|
||||
window->activated_keys[sz - 1].key = key;
|
||||
}
|
||||
}
|
||||
if (val == GLFW_RELEASE) {
|
||||
memset(window->activated_keys + idx, 0, sizeof(window->activated_keys[0]));
|
||||
if (idx < (int)sz - 1) {
|
||||
memmove(window->activated_keys + idx, window->activated_keys + idx + 1, sizeof(window->activated_keys[0]) * (sz - 1 - idx));
|
||||
memset(window->activated_keys + sz - 1, 0, sizeof(window->activated_keys[0]));
|
||||
}
|
||||
} else {
|
||||
window->activated_keys[idx].key = key;
|
||||
window->activated_keys[idx].action = val;
|
||||
}
|
||||
}
|
||||
|
||||
// Notifies shared code of a physical key event
|
||||
//
|
||||
void _glfwInputKeyboard(_GLFWwindow* window, GLFWkeyevent* ev)
|
||||
{
|
||||
if (ev->key >= 0 && ev->key <= GLFW_KEY_LAST)
|
||||
if (ev->key > 0)
|
||||
{
|
||||
bool repeated = false;
|
||||
int idx = -1;
|
||||
int current_action = GLFW_RELEASE;
|
||||
const unsigned sz = arraysz(window->activated_keys);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
if (window->activated_keys[i].key == ev->key) {
|
||||
idx = i;
|
||||
current_action = window->activated_keys[i].action;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ev->action == GLFW_RELEASE && window->keys[ev->key] == GLFW_RELEASE)
|
||||
if (ev->action == GLFW_RELEASE && current_action == GLFW_RELEASE)
|
||||
return;
|
||||
|
||||
if (ev->action == GLFW_PRESS && window->keys[ev->key] == GLFW_PRESS)
|
||||
if (ev->action == GLFW_PRESS && current_action == GLFW_PRESS)
|
||||
repeated = true;
|
||||
|
||||
if (ev->action == GLFW_RELEASE && window->stickyKeys)
|
||||
window->keys[ev->key] = _GLFW_STICK;
|
||||
set_key_action(window, ev->key, _GLFW_STICK, idx);
|
||||
else
|
||||
window->keys[ev->key] = (char) ev->action;
|
||||
set_key_action(window, ev->key, ev->action, idx);
|
||||
|
||||
if (repeated)
|
||||
ev->action = GLFW_REPEAT;
|
||||
@@ -463,200 +491,156 @@ void _glfwFreeJoystick(_GLFWjoystick* js)
|
||||
memset(js, 0, sizeof(_GLFWjoystick));
|
||||
}
|
||||
|
||||
const char* _glfwGetKeyName(int key)
|
||||
unsigned int
|
||||
encode_utf8(uint32_t ch, char* dest) {
|
||||
if (ch < 0x80) {
|
||||
dest[0] = (char)ch;
|
||||
return 1;
|
||||
}
|
||||
if (ch < 0x800) {
|
||||
dest[0] = (ch>>6) | 0xC0;
|
||||
dest[1] = (ch & 0x3F) | 0x80;
|
||||
return 2;
|
||||
}
|
||||
if (ch < 0x10000) {
|
||||
dest[0] = (ch>>12) | 0xE0;
|
||||
dest[1] = ((ch>>6) & 0x3F) | 0x80;
|
||||
dest[2] = (ch & 0x3F) | 0x80;
|
||||
return 3;
|
||||
}
|
||||
if (ch < 0x110000) {
|
||||
dest[0] = (ch>>18) | 0xF0;
|
||||
dest[1] = ((ch>>12) & 0x3F) | 0x80;
|
||||
dest[2] = ((ch>>6) & 0x3F) | 0x80;
|
||||
dest[3] = (ch & 0x3F) | 0x80;
|
||||
return 4;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char*
|
||||
_glfwGetKeyName(int key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
// Printable keys
|
||||
case GLFW_KEY_SPACE: return "SPACE";
|
||||
case GLFW_KEY_EXCLAM: return "EXCLAM";
|
||||
case GLFW_KEY_DOUBLE_QUOTE: return "DOUBLE_QUOTE";
|
||||
case GLFW_KEY_NUMBER_SIGN: return "NUMBER_SIGN";
|
||||
case GLFW_KEY_DOLLAR: return "DOLLAR";
|
||||
case GLFW_KEY_AMPERSAND: return "AMPERSAND";
|
||||
case GLFW_KEY_APOSTROPHE: return "APOSTROPHE";
|
||||
case GLFW_KEY_PARENTHESIS_LEFT: return "PARENTHESIS_LEFT";
|
||||
case GLFW_KEY_PARENTHESIS_RIGHT: return "PARENTHESIS_RIGHT";
|
||||
case GLFW_KEY_PLUS: return "PLUS";
|
||||
case GLFW_KEY_COMMA: return "COMMA";
|
||||
case GLFW_KEY_MINUS: return "MINUS";
|
||||
case GLFW_KEY_PERIOD: return "PERIOD";
|
||||
case GLFW_KEY_SLASH: return "SLASH";
|
||||
case GLFW_KEY_0: return "0";
|
||||
case GLFW_KEY_1: return "1";
|
||||
case GLFW_KEY_2: return "2";
|
||||
case GLFW_KEY_3: return "3";
|
||||
case GLFW_KEY_4: return "4";
|
||||
case GLFW_KEY_5: return "5";
|
||||
case GLFW_KEY_6: return "6";
|
||||
case GLFW_KEY_7: return "7";
|
||||
case GLFW_KEY_8: return "8";
|
||||
case GLFW_KEY_9: return "9";
|
||||
case GLFW_KEY_COLON: return "COLON";
|
||||
case GLFW_KEY_SEMICOLON: return "SEMICOLON";
|
||||
case GLFW_KEY_LESS: return "LESS";
|
||||
case GLFW_KEY_EQUAL: return "EQUAL";
|
||||
case GLFW_KEY_GREATER: return "GREATER";
|
||||
case GLFW_KEY_AT: return "AT";
|
||||
case GLFW_KEY_A: return "A";
|
||||
case GLFW_KEY_B: return "B";
|
||||
case GLFW_KEY_C: return "C";
|
||||
case GLFW_KEY_D: return "D";
|
||||
case GLFW_KEY_E: return "E";
|
||||
case GLFW_KEY_F: return "F";
|
||||
case GLFW_KEY_G: return "G";
|
||||
case GLFW_KEY_H: return "H";
|
||||
case GLFW_KEY_I: return "I";
|
||||
case GLFW_KEY_J: return "J";
|
||||
case GLFW_KEY_K: return "K";
|
||||
case GLFW_KEY_L: return "L";
|
||||
case GLFW_KEY_M: return "M";
|
||||
case GLFW_KEY_N: return "N";
|
||||
case GLFW_KEY_O: return "O";
|
||||
case GLFW_KEY_P: return "P";
|
||||
case GLFW_KEY_Q: return "Q";
|
||||
case GLFW_KEY_R: return "R";
|
||||
case GLFW_KEY_S: return "S";
|
||||
case GLFW_KEY_T: return "T";
|
||||
case GLFW_KEY_U: return "U";
|
||||
case GLFW_KEY_V: return "V";
|
||||
case GLFW_KEY_W: return "W";
|
||||
case GLFW_KEY_X: return "X";
|
||||
case GLFW_KEY_Y: return "Y";
|
||||
case GLFW_KEY_Z: return "Z";
|
||||
case GLFW_KEY_LEFT_BRACKET: return "LEFT_BRACKET";
|
||||
case GLFW_KEY_BACKSLASH: return "BACKSLASH";
|
||||
case GLFW_KEY_RIGHT_BRACKET: return "RIGHT_BRACKET";
|
||||
case GLFW_KEY_CIRCUMFLEX: return "CIRCUMFLEX";
|
||||
case GLFW_KEY_UNDERSCORE: return "UNDERSCORE";
|
||||
case GLFW_KEY_GRAVE_ACCENT: return "GRAVE_ACCENT";
|
||||
case GLFW_KEY_WORLD_1: return "WORLD_1";
|
||||
case GLFW_KEY_WORLD_2: return "WORLD_2";
|
||||
case GLFW_KEY_PARAGRAPH: return "PARAGRAPH";
|
||||
case GLFW_KEY_MASCULINE: return "MASCULINE";
|
||||
case GLFW_KEY_A_GRAVE: return "A_GRAVE";
|
||||
case GLFW_KEY_A_DIAERESIS: return "A_DIAERESIS";
|
||||
case GLFW_KEY_A_RING: return "A_RING";
|
||||
case GLFW_KEY_AE: return "AE";
|
||||
case GLFW_KEY_C_CEDILLA: return "C_CEDILLA";
|
||||
case GLFW_KEY_E_GRAVE: return "E_GRAVE";
|
||||
case GLFW_KEY_E_ACUTE: return "E_ACUTE";
|
||||
case GLFW_KEY_I_GRAVE: return "I_GRAVE";
|
||||
case GLFW_KEY_N_TILDE: return "N_TILDE";
|
||||
case GLFW_KEY_O_GRAVE: return "O_GRAVE";
|
||||
case GLFW_KEY_O_DIAERESIS: return "O_DIAERESIS";
|
||||
case GLFW_KEY_O_SLASH: return "O_SLASH";
|
||||
case GLFW_KEY_U_GRAVE: return "U_GRAVE";
|
||||
case GLFW_KEY_U_DIAERESIS: return "U_DIAERESIS";
|
||||
case GLFW_KEY_S_SHARP: return "S_SHARP";
|
||||
case GLFW_KEY_CYRILLIC_A: return "CYRILLIC_A";
|
||||
case GLFW_KEY_CYRILLIC_BE: return "CYRILLIC_BE";
|
||||
case GLFW_KEY_CYRILLIC_VE: return "CYRILLIC_VE";
|
||||
case GLFW_KEY_CYRILLIC_GHE: return "CYRILLIC_GHE";
|
||||
case GLFW_KEY_CYRILLIC_DE: return "CYRILLIC_DE";
|
||||
case GLFW_KEY_CYRILLIC_IE: return "CYRILLIC_IE";
|
||||
case GLFW_KEY_CYRILLIC_ZHE: return "CYRILLIC_ZHE";
|
||||
case GLFW_KEY_CYRILLIC_ZE: return "CYRILLIC_ZE";
|
||||
case GLFW_KEY_CYRILLIC_I: return "CYRILLIC_I";
|
||||
case GLFW_KEY_CYRILLIC_SHORT_I: return "CYRILLIC_SHORT_I";
|
||||
case GLFW_KEY_CYRILLIC_KA: return "CYRILLIC_KA";
|
||||
case GLFW_KEY_CYRILLIC_EL: return "CYRILLIC_EL";
|
||||
case GLFW_KEY_CYRILLIC_EM: return "CYRILLIC_EM";
|
||||
case GLFW_KEY_CYRILLIC_EN: return "CYRILLIC_EN";
|
||||
case GLFW_KEY_CYRILLIC_O: return "CYRILLIC_O";
|
||||
case GLFW_KEY_CYRILLIC_PE: return "CYRILLIC_PE";
|
||||
case GLFW_KEY_CYRILLIC_ER: return "CYRILLIC_ER";
|
||||
case GLFW_KEY_CYRILLIC_ES: return "CYRILLIC_ES";
|
||||
case GLFW_KEY_CYRILLIC_TE: return "CYRILLIC_TE";
|
||||
case GLFW_KEY_CYRILLIC_U: return "CYRILLIC_U";
|
||||
case GLFW_KEY_CYRILLIC_EF: return "CYRILLIC_EF";
|
||||
case GLFW_KEY_CYRILLIC_HA: return "CYRILLIC_HA";
|
||||
case GLFW_KEY_CYRILLIC_TSE: return "CYRILLIC_TSE";
|
||||
case GLFW_KEY_CYRILLIC_CHE: return "CYRILLIC_CHE";
|
||||
case GLFW_KEY_CYRILLIC_SHA: return "CYRILLIC_SHA";
|
||||
case GLFW_KEY_CYRILLIC_SHCHA: return "CYRILLIC_SHCHA";
|
||||
case GLFW_KEY_CYRILLIC_HARD_SIGN: return "CYRILLIC_HARD_SIGN";
|
||||
case GLFW_KEY_CYRILLIC_YERU: return "CYRILLIC_YERU";
|
||||
case GLFW_KEY_CYRILLIC_SOFT_SIGN: return "CYRILLIC_SOFT_SIGN";
|
||||
case GLFW_KEY_CYRILLIC_E: return "CYRILLIC_E";
|
||||
case GLFW_KEY_CYRILLIC_YU: return "CYRILLIC_YU";
|
||||
case GLFW_KEY_CYRILLIC_YA: return "CYRILLIC_YA";
|
||||
case GLFW_KEY_CYRILLIC_IO: return "CYRILLIC_IO";
|
||||
|
||||
// Function keys
|
||||
case GLFW_KEY_ESCAPE: return "ESCAPE";
|
||||
case GLFW_KEY_F1: return "F1";
|
||||
case GLFW_KEY_F2: return "F2";
|
||||
case GLFW_KEY_F3: return "F3";
|
||||
case GLFW_KEY_F4: return "F4";
|
||||
case GLFW_KEY_F5: return "F5";
|
||||
case GLFW_KEY_F6: return "F6";
|
||||
case GLFW_KEY_F7: return "F7";
|
||||
case GLFW_KEY_F8: return "F8";
|
||||
case GLFW_KEY_F9: return "F9";
|
||||
case GLFW_KEY_F10: return "F10";
|
||||
case GLFW_KEY_F11: return "F11";
|
||||
case GLFW_KEY_F12: return "F12";
|
||||
case GLFW_KEY_F13: return "F13";
|
||||
case GLFW_KEY_F14: return "F14";
|
||||
case GLFW_KEY_F15: return "F15";
|
||||
case GLFW_KEY_F16: return "F16";
|
||||
case GLFW_KEY_F17: return "F17";
|
||||
case GLFW_KEY_F18: return "F18";
|
||||
case GLFW_KEY_F19: return "F19";
|
||||
case GLFW_KEY_F20: return "F20";
|
||||
case GLFW_KEY_F21: return "F21";
|
||||
case GLFW_KEY_F22: return "F22";
|
||||
case GLFW_KEY_F23: return "F23";
|
||||
case GLFW_KEY_F24: return "F24";
|
||||
case GLFW_KEY_F25: return "F25";
|
||||
case GLFW_KEY_UP: return "UP";
|
||||
case GLFW_KEY_DOWN: return "DOWN";
|
||||
case GLFW_KEY_LEFT: return "LEFT";
|
||||
case GLFW_KEY_RIGHT: return "RIGHT";
|
||||
case GLFW_KEY_LEFT_SHIFT: return "LEFT SHIFT";
|
||||
case GLFW_KEY_RIGHT_SHIFT: return "RIGHT SHIFT";
|
||||
case GLFW_KEY_LEFT_CONTROL: return "LEFT CONTROL";
|
||||
case GLFW_KEY_RIGHT_CONTROL: return "RIGHT CONTROL";
|
||||
case GLFW_KEY_LEFT_ALT: return "LEFT ALT";
|
||||
case GLFW_KEY_RIGHT_ALT: return "RIGHT ALT";
|
||||
case GLFW_KEY_TAB: return "TAB";
|
||||
case GLFW_KEY_ENTER: return "ENTER";
|
||||
case GLFW_KEY_BACKSPACE: return "BACKSPACE";
|
||||
case GLFW_KEY_INSERT: return "INSERT";
|
||||
case GLFW_KEY_DELETE: return "DELETE";
|
||||
case GLFW_KEY_PAGE_UP: return "PAGE UP";
|
||||
case GLFW_KEY_PAGE_DOWN: return "PAGE DOWN";
|
||||
case GLFW_KEY_HOME: return "HOME";
|
||||
case GLFW_KEY_END: return "END";
|
||||
case GLFW_KEY_KP_0: return "KEYPAD 0";
|
||||
case GLFW_KEY_KP_1: return "KEYPAD 1";
|
||||
case GLFW_KEY_KP_2: return "KEYPAD 2";
|
||||
case GLFW_KEY_KP_3: return "KEYPAD 3";
|
||||
case GLFW_KEY_KP_4: return "KEYPAD 4";
|
||||
case GLFW_KEY_KP_5: return "KEYPAD 5";
|
||||
case GLFW_KEY_KP_6: return "KEYPAD 6";
|
||||
case GLFW_KEY_KP_7: return "KEYPAD 7";
|
||||
case GLFW_KEY_KP_8: return "KEYPAD 8";
|
||||
case GLFW_KEY_KP_9: return "KEYPAD 9";
|
||||
case GLFW_KEY_KP_DIVIDE: return "KEYPAD DIVIDE";
|
||||
case GLFW_KEY_KP_MULTIPLY: return "KEYPAD MULTIPLY";
|
||||
case GLFW_KEY_KP_SUBTRACT: return "KEYPAD SUBTRACT";
|
||||
case GLFW_KEY_KP_ADD: return "KEYPAD ADD";
|
||||
case GLFW_KEY_KP_DECIMAL: return "KEYPAD DECIMAL";
|
||||
case GLFW_KEY_KP_EQUAL: return "KEYPAD EQUAL";
|
||||
case GLFW_KEY_KP_ENTER: return "KEYPAD ENTER";
|
||||
case GLFW_KEY_PRINT_SCREEN: return "PRINT SCREEN";
|
||||
case GLFW_KEY_NUM_LOCK: return "NUM LOCK";
|
||||
case GLFW_KEY_CAPS_LOCK: return "CAPS LOCK";
|
||||
case GLFW_KEY_SCROLL_LOCK: return "SCROLL LOCK";
|
||||
case GLFW_KEY_PAUSE: return "PAUSE";
|
||||
case GLFW_KEY_LEFT_SUPER: return "LEFT SUPER";
|
||||
case GLFW_KEY_RIGHT_SUPER: return "RIGHT SUPER";
|
||||
case GLFW_KEY_MENU: return "MENU";
|
||||
|
||||
default: return "UNKNOWN";
|
||||
/* start functional key names (auto generated by gen-key-constants.py do not edit) */
|
||||
case GLFW_FKEY_ESCAPE: return "ESCAPE";
|
||||
case GLFW_FKEY_ENTER: return "ENTER";
|
||||
case GLFW_FKEY_TAB: return "TAB";
|
||||
case GLFW_FKEY_BACKSPACE: return "BACKSPACE";
|
||||
case GLFW_FKEY_INSERT: return "INSERT";
|
||||
case GLFW_FKEY_DELETE: return "DELETE";
|
||||
case GLFW_FKEY_LEFT: return "LEFT";
|
||||
case GLFW_FKEY_RIGHT: return "RIGHT";
|
||||
case GLFW_FKEY_UP: return "UP";
|
||||
case GLFW_FKEY_DOWN: return "DOWN";
|
||||
case GLFW_FKEY_PAGE_UP: return "PAGE_UP";
|
||||
case GLFW_FKEY_PAGE_DOWN: return "PAGE_DOWN";
|
||||
case GLFW_FKEY_HOME: return "HOME";
|
||||
case GLFW_FKEY_END: return "END";
|
||||
case GLFW_FKEY_CAPS_LOCK: return "CAPS_LOCK";
|
||||
case GLFW_FKEY_SCROLL_LOCK: return "SCROLL_LOCK";
|
||||
case GLFW_FKEY_NUM_LOCK: return "NUM_LOCK";
|
||||
case GLFW_FKEY_PRINT_SCREEN: return "PRINT_SCREEN";
|
||||
case GLFW_FKEY_PAUSE: return "PAUSE";
|
||||
case GLFW_FKEY_MENU: return "MENU";
|
||||
case GLFW_FKEY_F1: return "F1";
|
||||
case GLFW_FKEY_F2: return "F2";
|
||||
case GLFW_FKEY_F3: return "F3";
|
||||
case GLFW_FKEY_F4: return "F4";
|
||||
case GLFW_FKEY_F5: return "F5";
|
||||
case GLFW_FKEY_F6: return "F6";
|
||||
case GLFW_FKEY_F7: return "F7";
|
||||
case GLFW_FKEY_F8: return "F8";
|
||||
case GLFW_FKEY_F9: return "F9";
|
||||
case GLFW_FKEY_F10: return "F10";
|
||||
case GLFW_FKEY_F11: return "F11";
|
||||
case GLFW_FKEY_F12: return "F12";
|
||||
case GLFW_FKEY_F13: return "F13";
|
||||
case GLFW_FKEY_F14: return "F14";
|
||||
case GLFW_FKEY_F15: return "F15";
|
||||
case GLFW_FKEY_F16: return "F16";
|
||||
case GLFW_FKEY_F17: return "F17";
|
||||
case GLFW_FKEY_F18: return "F18";
|
||||
case GLFW_FKEY_F19: return "F19";
|
||||
case GLFW_FKEY_F20: return "F20";
|
||||
case GLFW_FKEY_F21: return "F21";
|
||||
case GLFW_FKEY_F22: return "F22";
|
||||
case GLFW_FKEY_F23: return "F23";
|
||||
case GLFW_FKEY_F24: return "F24";
|
||||
case GLFW_FKEY_F25: return "F25";
|
||||
case GLFW_FKEY_F26: return "F26";
|
||||
case GLFW_FKEY_F27: return "F27";
|
||||
case GLFW_FKEY_F28: return "F28";
|
||||
case GLFW_FKEY_F29: return "F29";
|
||||
case GLFW_FKEY_F30: return "F30";
|
||||
case GLFW_FKEY_F31: return "F31";
|
||||
case GLFW_FKEY_F32: return "F32";
|
||||
case GLFW_FKEY_F33: return "F33";
|
||||
case GLFW_FKEY_F34: return "F34";
|
||||
case GLFW_FKEY_F35: return "F35";
|
||||
case GLFW_FKEY_KP_0: return "KP_0";
|
||||
case GLFW_FKEY_KP_1: return "KP_1";
|
||||
case GLFW_FKEY_KP_2: return "KP_2";
|
||||
case GLFW_FKEY_KP_3: return "KP_3";
|
||||
case GLFW_FKEY_KP_4: return "KP_4";
|
||||
case GLFW_FKEY_KP_5: return "KP_5";
|
||||
case GLFW_FKEY_KP_6: return "KP_6";
|
||||
case GLFW_FKEY_KP_7: return "KP_7";
|
||||
case GLFW_FKEY_KP_8: return "KP_8";
|
||||
case GLFW_FKEY_KP_9: return "KP_9";
|
||||
case GLFW_FKEY_KP_DECIMAL: return "KP_DECIMAL";
|
||||
case GLFW_FKEY_KP_DIVIDE: return "KP_DIVIDE";
|
||||
case GLFW_FKEY_KP_MULTIPLY: return "KP_MULTIPLY";
|
||||
case GLFW_FKEY_KP_SUBTRACT: return "KP_SUBTRACT";
|
||||
case GLFW_FKEY_KP_ADD: return "KP_ADD";
|
||||
case GLFW_FKEY_KP_ENTER: return "KP_ENTER";
|
||||
case GLFW_FKEY_KP_EQUAL: return "KP_EQUAL";
|
||||
case GLFW_FKEY_KP_SEPARATOR: return "KP_SEPARATOR";
|
||||
case GLFW_FKEY_KP_LEFT: return "KP_LEFT";
|
||||
case GLFW_FKEY_KP_RIGHT: return "KP_RIGHT";
|
||||
case GLFW_FKEY_KP_UP: return "KP_UP";
|
||||
case GLFW_FKEY_KP_DOWN: return "KP_DOWN";
|
||||
case GLFW_FKEY_KP_PAGE_UP: return "KP_PAGE_UP";
|
||||
case GLFW_FKEY_KP_PAGE_DOWN: return "KP_PAGE_DOWN";
|
||||
case GLFW_FKEY_KP_HOME: return "KP_HOME";
|
||||
case GLFW_FKEY_KP_END: return "KP_END";
|
||||
case GLFW_FKEY_KP_INSERT: return "KP_INSERT";
|
||||
case GLFW_FKEY_KP_DELETE: return "KP_DELETE";
|
||||
case GLFW_FKEY_KP_BEGIN: return "KP_BEGIN";
|
||||
case GLFW_FKEY_MEDIA_PLAY: return "MEDIA_PLAY";
|
||||
case GLFW_FKEY_MEDIA_PAUSE: return "MEDIA_PAUSE";
|
||||
case GLFW_FKEY_MEDIA_PLAY_PAUSE: return "MEDIA_PLAY_PAUSE";
|
||||
case GLFW_FKEY_MEDIA_REVERSE: return "MEDIA_REVERSE";
|
||||
case GLFW_FKEY_MEDIA_STOP: return "MEDIA_STOP";
|
||||
case GLFW_FKEY_MEDIA_FAST_FORWARD: return "MEDIA_FAST_FORWARD";
|
||||
case GLFW_FKEY_MEDIA_REWIND: return "MEDIA_REWIND";
|
||||
case GLFW_FKEY_MEDIA_TRACK_NEXT: return "MEDIA_TRACK_NEXT";
|
||||
case GLFW_FKEY_MEDIA_TRACK_PREVIOUS: return "MEDIA_TRACK_PREVIOUS";
|
||||
case GLFW_FKEY_MEDIA_RECORD: return "MEDIA_RECORD";
|
||||
case GLFW_FKEY_LOWER_VOLUME: return "LOWER_VOLUME";
|
||||
case GLFW_FKEY_RAISE_VOLUME: return "RAISE_VOLUME";
|
||||
case GLFW_FKEY_MUTE_VOLUME: return "MUTE_VOLUME";
|
||||
case GLFW_FKEY_LEFT_SHIFT: return "LEFT_SHIFT";
|
||||
case GLFW_FKEY_LEFT_CONTROL: return "LEFT_CONTROL";
|
||||
case GLFW_FKEY_LEFT_ALT: return "LEFT_ALT";
|
||||
case GLFW_FKEY_LEFT_SUPER: return "LEFT_SUPER";
|
||||
case GLFW_FKEY_LEFT_HYPER: return "LEFT_HYPER";
|
||||
case GLFW_FKEY_LEFT_META: return "LEFT_META";
|
||||
case GLFW_FKEY_RIGHT_SHIFT: return "RIGHT_SHIFT";
|
||||
case GLFW_FKEY_RIGHT_CONTROL: return "RIGHT_CONTROL";
|
||||
case GLFW_FKEY_RIGHT_ALT: return "RIGHT_ALT";
|
||||
case GLFW_FKEY_RIGHT_SUPER: return "RIGHT_SUPER";
|
||||
case GLFW_FKEY_RIGHT_HYPER: return "RIGHT_HYPER";
|
||||
case GLFW_FKEY_RIGHT_META: return "RIGHT_META";
|
||||
case GLFW_FKEY_ISO_LEVEL3_SHIFT: return "ISO_LEVEL3_SHIFT";
|
||||
case GLFW_FKEY_ISO_LEVEL5_SHIFT: return "ISO_LEVEL5_SHIFT";
|
||||
/* end functional key names */
|
||||
case 0: return "UNKNOWN";
|
||||
}
|
||||
static char buf[16];
|
||||
encode_utf8(key, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
// Center the cursor in the content area of the specified window
|
||||
@@ -736,13 +720,15 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
|
||||
|
||||
if (!value)
|
||||
{
|
||||
int i;
|
||||
|
||||
// Release all sticky keys
|
||||
for (i = 0; i <= GLFW_KEY_LAST; i++)
|
||||
for (unsigned i = arraysz(window->activated_keys) - 1; i-- > 0;)
|
||||
{
|
||||
if (window->keys[i] == _GLFW_STICK)
|
||||
window->keys[i] = GLFW_RELEASE;
|
||||
if (window->activated_keys[i].action == _GLFW_STICK) {
|
||||
if (i < arraysz(window->activated_keys) - 1) {
|
||||
memmove(window->activated_keys + i, window->activated_keys + i + 1, sizeof(window->activated_keys[0]) * (arraysz(window->activated_keys) - 1 - i));
|
||||
}
|
||||
memset(window->activated_keys + arraysz(window->activated_keys) - 1, 0, sizeof(window->activated_keys[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -798,59 +784,50 @@ GLFWAPI int glfwRawMouseMotionSupported(void)
|
||||
return _glfwPlatformRawMouseMotionSupported();
|
||||
}
|
||||
|
||||
GLFWAPI const char* glfwGetKeyName(int key, int native_key)
|
||||
GLFWAPI const char* glfwGetKeyName(uint32_t key, int native_key)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(NULL);
|
||||
if (key) return _glfwGetKeyName(key);
|
||||
|
||||
if (key != GLFW_KEY_UNKNOWN)
|
||||
{
|
||||
if (key != GLFW_KEY_KP_EQUAL &&
|
||||
(key < GLFW_KEY_KP_0 || key > GLFW_KEY_KP_ADD) &&
|
||||
(key < GLFW_KEY_APOSTROPHE || key > GLFW_KEY_LAST_PRINTABLE))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
native_key = _glfwPlatformGetNativeKeyForKey(key);
|
||||
}
|
||||
|
||||
native_key = _glfwPlatformGetNativeKeyForKey(key);
|
||||
return _glfwPlatformGetNativeKeyName(native_key);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetNativeKeyForKey(int key)
|
||||
GLFWAPI int glfwGetNativeKeyForKey(uint32_t key)
|
||||
{
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(-1);
|
||||
|
||||
if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key);
|
||||
return GLFW_RELEASE;
|
||||
}
|
||||
|
||||
return _glfwPlatformGetNativeKeyForKey(key);
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetKey(GLFWwindow* handle, int key)
|
||||
GLFWAPI GLFWKeyAction glfwGetKey(GLFWwindow* handle, uint32_t key)
|
||||
{
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
assert(window != NULL);
|
||||
|
||||
_GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE);
|
||||
if (!key) return GLFW_RELEASE;
|
||||
|
||||
if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST)
|
||||
{
|
||||
_glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key);
|
||||
return GLFW_RELEASE;
|
||||
int current_action = GLFW_RELEASE;
|
||||
const unsigned sz = arraysz(window->activated_keys);
|
||||
int idx = -1;
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
if (window->activated_keys[i].key == key) {
|
||||
idx = i;
|
||||
current_action = window->activated_keys[i].action;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (window->keys[key] == _GLFW_STICK)
|
||||
|
||||
if (current_action == _GLFW_STICK)
|
||||
{
|
||||
// Sticky mode: release key now
|
||||
window->keys[key] = GLFW_RELEASE;
|
||||
return GLFW_PRESS;
|
||||
set_key_action(window, key, GLFW_RELEASE, idx);
|
||||
current_action = GLFW_PRESS;
|
||||
}
|
||||
|
||||
return (int) window->keys[key];
|
||||
return current_action;
|
||||
}
|
||||
|
||||
GLFWAPI int glfwGetMouseButton(GLFWwindow* handle, int button)
|
||||
@@ -1036,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
|
||||
|
||||
15
glfw/internal.h
vendored
15
glfw/internal.h
vendored
@@ -276,6 +276,7 @@ struct _GLFWinitconfig
|
||||
bool hatButtons;
|
||||
int angleType;
|
||||
bool debugKeyboard;
|
||||
bool debugRendering;
|
||||
struct {
|
||||
bool menubar;
|
||||
bool chdir;
|
||||
@@ -401,6 +402,11 @@ struct _GLFWcontext
|
||||
_GLFWcontextOSMesa osmesa;
|
||||
};
|
||||
|
||||
typedef struct GLFWKeyState {
|
||||
uint32_t key;
|
||||
char action;
|
||||
} GLFWKeyState;
|
||||
|
||||
// Window and context structure
|
||||
//
|
||||
struct _GLFWwindow
|
||||
@@ -431,7 +437,7 @@ struct _GLFWwindow
|
||||
bool lockKeyMods;
|
||||
int cursorMode;
|
||||
char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1];
|
||||
char keys[GLFW_KEY_LAST + 1];
|
||||
GLFWKeyState activated_keys[16];
|
||||
// Virtual cursor position when cursor is disabled
|
||||
double virtualCursorPosX, virtualCursorPosY;
|
||||
bool rawMouseMotion;
|
||||
@@ -609,6 +615,7 @@ struct _GLFWlibrary
|
||||
GLFWmonitorfun monitor;
|
||||
GLFWjoystickfun joystick;
|
||||
GLFWapplicationclosefun application_close;
|
||||
GLFWdrawtextfun draw_text;
|
||||
} callbacks;
|
||||
|
||||
|
||||
@@ -649,7 +656,7 @@ void _glfwPlatformDestroyCursor(_GLFWcursor* cursor);
|
||||
void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor);
|
||||
|
||||
const char* _glfwPlatformGetNativeKeyName(int native_key);
|
||||
int _glfwPlatformGetNativeKeyForKey(int key);
|
||||
int _glfwPlatformGetNativeKeyForKey(uint32_t key);
|
||||
|
||||
void _glfwPlatformFreeMonitor(_GLFWmonitor* monitor);
|
||||
void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos);
|
||||
@@ -723,7 +730,8 @@ 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);
|
||||
@@ -771,7 +779,6 @@ void _glfwInputWindowDamage(_GLFWwindow* window);
|
||||
void _glfwInputWindowCloseRequest(_GLFWwindow* window);
|
||||
void _glfwInputWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor);
|
||||
|
||||
void _glfwInitializeKeyEvent(GLFWkeyevent *ev, int key, int native_key, int action, int mods);
|
||||
void _glfwInputKeyboard(_GLFWwindow *window, GLFWkeyevent *ev);
|
||||
void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset, int flags, int mods);
|
||||
void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods);
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
"wl_platform.h",
|
||||
"posix_thread.h",
|
||||
"wl_cursors.h",
|
||||
"wl_text_input.h",
|
||||
"xkb_glfw.h",
|
||||
"dbus_glfw.h",
|
||||
"ibus_glfw.h",
|
||||
@@ -64,6 +65,8 @@
|
||||
"linux_joystick.h",
|
||||
"null_joystick.h",
|
||||
"linux_notify.h",
|
||||
"linux_desktop_settings.h",
|
||||
"wl_client_side_decorations.h",
|
||||
"main_loop.h"
|
||||
],
|
||||
"protocols": [
|
||||
@@ -73,13 +76,16 @@
|
||||
"unstable/pointer-constraints/pointer-constraints-unstable-v1.xml",
|
||||
"unstable/idle-inhibit/idle-inhibit-unstable-v1.xml",
|
||||
"unstable/xdg-decoration/xdg-decoration-unstable-v1.xml",
|
||||
"unstable/primary-selection/primary-selection-unstable-v1.xml"
|
||||
"unstable/primary-selection/primary-selection-unstable-v1.xml",
|
||||
"unstable/text-input/text-input-unstable-v3.xml"
|
||||
],
|
||||
"sources": [
|
||||
"wl_init.c",
|
||||
"wl_monitor.c",
|
||||
"wl_window.c",
|
||||
"wl_cursors.c",
|
||||
"wl_text_input.c",
|
||||
"wl_client_side_decorations.c",
|
||||
"posix_thread.c",
|
||||
"xkb_glfw.c",
|
||||
"dbus_glfw.c",
|
||||
@@ -88,6 +94,7 @@
|
||||
"osmesa_context.c",
|
||||
"backend_utils.c",
|
||||
"linux_joystick.c",
|
||||
"linux_desktop_settings.c",
|
||||
"null_joystick.c",
|
||||
"linux_notify.c"
|
||||
]
|
||||
|
||||
12
glfw/window.c
vendored
12
glfw/window.c
vendored
@@ -50,21 +50,19 @@ void _glfwInputWindowFocus(_GLFWwindow* window, bool focused)
|
||||
|
||||
if (!focused)
|
||||
{
|
||||
int key, button;
|
||||
_glfw.focusedWindowId = 0;
|
||||
|
||||
for (key = 0; key <= GLFW_KEY_LAST; key++)
|
||||
for (unsigned i = 0; i < arraysz(window->activated_keys); i++)
|
||||
{
|
||||
if (window->keys[key] == GLFW_PRESS)
|
||||
if (window->activated_keys[i].key > 0 && window->activated_keys[i].action == GLFW_PRESS)
|
||||
{
|
||||
const int native_key = _glfwPlatformGetNativeKeyForKey(key);
|
||||
GLFWkeyevent ev;
|
||||
_glfwInitializeKeyEvent(&ev, key, native_key, GLFW_RELEASE, 0);
|
||||
const int native_key = _glfwPlatformGetNativeKeyForKey(window->activated_keys[i].key);
|
||||
GLFWkeyevent ev = {.key = window->activated_keys[i].key, .native_key = native_key, .action = GLFW_RELEASE};
|
||||
_glfwInputKeyboard(window, &ev);
|
||||
}
|
||||
}
|
||||
|
||||
for (button = 0; button <= GLFW_MOUSE_BUTTON_LAST; button++)
|
||||
for (int button = 0; button <= GLFW_MOUSE_BUTTON_LAST; button++)
|
||||
{
|
||||
if (window->mouseButtons[button] == GLFW_PRESS)
|
||||
_glfwInputMouseClick(window, button, GLFW_RELEASE, 0);
|
||||
|
||||
438
glfw/wl_client_side_decorations.c
vendored
Normal file
438
glfw/wl_client_side_decorations.c
vendored
Normal file
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
* wl_client_side_decorations.c
|
||||
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include "wl_client_side_decorations.h"
|
||||
|
||||
#include "backend_utils.h"
|
||||
#include <sys/mman.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define debug(...) if (_glfw.hints.init.debugRendering) fprintf(stderr, __VA_ARGS__);
|
||||
#define decs window->wl.decorations
|
||||
|
||||
#define ARGB(a, r, g, b) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
|
||||
#define A(x) (((x) >> 24) & 0xff)
|
||||
#define R(x) (((x) >> 16) & 0xff)
|
||||
#define G(x) (((x) >> 8) & 0xff)
|
||||
#define B(x) ((x) & 0xff)
|
||||
#define SWAP(x, y) do { __typeof__(x) SWAP = x; x = y; y = SWAP; } while (0)
|
||||
|
||||
static const uint32_t passive_bg_color = 0xffeeeeee;
|
||||
static const uint32_t active_bg_color = 0xffdddad6;
|
||||
typedef float kernel_type;
|
||||
|
||||
static void
|
||||
build_blur_kernel(kernel_type *blur_kernel, const size_t size, kernel_type sigma) {
|
||||
// 1D Normalized Gaussian
|
||||
const kernel_type half = size / (kernel_type)2;
|
||||
kernel_type sum = 0;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
kernel_type f = (i - half);
|
||||
blur_kernel[i] = (kernel_type)exp(- f * f / sigma);
|
||||
sum += blur_kernel[i];
|
||||
}
|
||||
for (size_t i = 0; i < size; i++) blur_kernel[i] /= sum;
|
||||
}
|
||||
|
||||
static void
|
||||
blur_mask(kernel_type *image_data, ssize_t width, ssize_t height, ssize_t kernel_size, kernel_type sigma, kernel_type *scratch, kernel_type *blur_kernel, ssize_t margin) {
|
||||
(void)margin;
|
||||
build_blur_kernel(blur_kernel, kernel_size, sigma);
|
||||
const size_t half = kernel_size / 2;
|
||||
|
||||
for (ssize_t y = 0; y < height; y++) {
|
||||
kernel_type *s = image_data + y * width, *d = scratch + y * width;
|
||||
for (ssize_t x = 0; x < width; x++) {
|
||||
kernel_type a = 0;
|
||||
for (ssize_t k = 0; k < kernel_size; k++) {
|
||||
const ssize_t px = x + k - half;
|
||||
if (0 <= px && px < width) a += s[px] * blur_kernel[k];
|
||||
}
|
||||
d[x] = a;
|
||||
}
|
||||
}
|
||||
|
||||
for (ssize_t y = 0; y < height; y++) {
|
||||
kernel_type *d = image_data + y * width;
|
||||
for (ssize_t x = 0; x < width; x++) {
|
||||
kernel_type a = 0;
|
||||
for (ssize_t k = 0; k < kernel_size; k++) {
|
||||
const ssize_t py = y + k - half;
|
||||
if (0 <= py && py < height) {
|
||||
kernel_type *s = scratch + py * width;
|
||||
a += s[x] * blur_kernel[k];
|
||||
}
|
||||
}
|
||||
d[x] = a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static kernel_type*
|
||||
create_shadow_mask(size_t width, size_t height, size_t margin, size_t kernel_size, kernel_type base_alpha, kernel_type sigma) {
|
||||
kernel_type *mask = calloc(sizeof(kernel_type), 2 * width * height + kernel_size);
|
||||
if (!mask) return NULL;
|
||||
for (size_t y = margin; y < height - margin; y++) {
|
||||
kernel_type *row = mask + y * width;
|
||||
for (size_t x = margin; x < width - margin; x++) row[x] = base_alpha;
|
||||
}
|
||||
blur_mask(mask, width, height, kernel_size, sigma, mask + width * height, (kernel_type*)(mask + 2 * width * height), margin);
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void
|
||||
swap_buffers(_GLFWWaylandBufferPair *pair) {
|
||||
SWAP(pair->front, pair->back);
|
||||
SWAP(pair->data.front, pair->data.back);
|
||||
}
|
||||
|
||||
static size_t
|
||||
init_buffer_pair(_GLFWWaylandBufferPair *pair, size_t width, size_t height, unsigned scale) {
|
||||
memset(pair, 0, sizeof(_GLFWWaylandBufferPair));
|
||||
pair->width = width * scale;
|
||||
pair->height = height * scale;
|
||||
pair->stride = 4 * pair->width;
|
||||
pair->size_in_bytes = pair->stride * pair->height;
|
||||
return 2 * pair->size_in_bytes;
|
||||
}
|
||||
|
||||
static void
|
||||
alloc_buffer_pair(_GLFWWaylandBufferPair *pair, struct wl_shm_pool *pool, uint8_t *data, size_t *offset) {
|
||||
pair->data.a = data + *offset;
|
||||
pair->a = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);
|
||||
*offset += pair->size_in_bytes;
|
||||
pair->data.b = data + *offset;
|
||||
pair->b = wl_shm_pool_create_buffer(pool, *offset, pair->width, pair->height, pair->stride, WL_SHM_FORMAT_ARGB8888);
|
||||
*offset += pair->size_in_bytes;
|
||||
pair->front = pair->a; pair->back = pair->b;
|
||||
pair->data.front = pair->data.a; pair->data.back = pair->data.b;
|
||||
}
|
||||
|
||||
#define st decs.shadow_tile
|
||||
|
||||
void
|
||||
initialize_csd_metrics(_GLFWwindow *window) {
|
||||
decs.metrics.width = 12;
|
||||
decs.metrics.top = 36;
|
||||
decs.metrics.visible_titlebar_height = window->wl.decorations.metrics.top - window->wl.decorations.metrics.width;
|
||||
decs.metrics.horizontal = 2 * window->wl.decorations.metrics.width;
|
||||
decs.metrics.vertical = window->wl.decorations.metrics.width + window->wl.decorations.metrics.top;
|
||||
}
|
||||
|
||||
static size_t
|
||||
create_shadow_tile(_GLFWwindow *window) {
|
||||
const size_t margin = decs.bottom.buffer.height;
|
||||
if (st.data && st.for_decoration_size == margin) return margin;
|
||||
st.for_decoration_size = margin;
|
||||
free(st.data);
|
||||
st.segments = 7;
|
||||
st.stride = st.segments * margin;
|
||||
st.corner_size = margin * (st.segments - 1) / 2;
|
||||
kernel_type* mask = create_shadow_mask(st.stride, st.stride, margin, 2 * margin + 1, (kernel_type)0.7, 32 * margin);
|
||||
st.data = malloc(sizeof(uint32_t) * st.stride * st.stride);
|
||||
if (st.data) for (size_t i = 0; i < st.stride * st.stride; i++) st.data[i] = ((uint8_t)(mask[i] * 255)) << 24;
|
||||
free(mask);
|
||||
return margin;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
render_title_bar(_GLFWwindow *window, bool to_front_buffer) {
|
||||
const bool is_focused = window->id == _glfw.focusedWindowId;
|
||||
uint32_t bg_color = is_focused ? active_bg_color : passive_bg_color;
|
||||
uint32_t fg_color = is_focused ? 0xff444444 : 0xff888888;
|
||||
if (decs.use_custom_titlebar_color) {
|
||||
bg_color = 0xff000000 | (decs.titlebar_color & 0xffffff);
|
||||
double red = ((bg_color >> 16) & 0xFF) / 255.0;
|
||||
double green = ((bg_color >> 8) & 0xFF) / 255.0;
|
||||
double blue = (bg_color & 0xFF) / 255.0;
|
||||
double luma = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
|
||||
if (luma < 0.5) {
|
||||
fg_color = is_focused ? 0xffeeeeee : 0xff888888;
|
||||
}
|
||||
}
|
||||
uint8_t *output = to_front_buffer ? decs.top.buffer.data.front : decs.top.buffer.data.back;
|
||||
|
||||
// render shadow part
|
||||
const size_t margin = create_shadow_tile(window);
|
||||
const size_t edge_segment_size = st.corner_size - margin;
|
||||
const uint8_t divisor = is_focused ? 1 : 2;
|
||||
for (size_t y = 0; y < margin; y++) {
|
||||
// left segment
|
||||
uint32_t *s = st.data + y * st.stride + margin;
|
||||
uint32_t *d = (uint32_t*)(output + y * decs.top.buffer.stride);
|
||||
for (size_t x = 0; x < edge_segment_size; x++) d[x] = (A(s[x]) / divisor) << 24;
|
||||
// middle segment
|
||||
s += edge_segment_size;
|
||||
size_t limit = decs.top.buffer.width > edge_segment_size ? decs.top.buffer.width - edge_segment_size : 0;
|
||||
for (size_t x = edge_segment_size, sx = 0; x < limit; x++, sx = (sx + 1) % margin) d[x] = (A(s[sx]) / divisor) << 24;
|
||||
// right segment
|
||||
s += margin;
|
||||
for (size_t x = limit; x < decs.top.buffer.width; x++, s++) d[x] = (A(*s) / divisor) << 24;
|
||||
}
|
||||
|
||||
// render text part
|
||||
output += decs.top.buffer.stride * margin;
|
||||
if (window->wl.title && window->wl.title[0] && _glfw.callbacks.draw_text) {
|
||||
if (_glfw.callbacks.draw_text((GLFWwindow*)window, window->wl.title, fg_color, bg_color, output, decs.top.buffer.width, decs.top.buffer.height - margin, 0, 0, 0)) return;
|
||||
}
|
||||
for (uint32_t *px = (uint32_t*)output, *end = (uint32_t*)(output + decs.top.buffer.size_in_bytes); px < end; px++) {
|
||||
*px = bg_color;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_title_bar(_GLFWwindow *window) {
|
||||
render_title_bar(window, false);
|
||||
swap_buffers(&decs.top.buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
render_edges(_GLFWwindow *window) {
|
||||
const size_t margin = create_shadow_tile(window);
|
||||
if (!st.data) return; // out of memory
|
||||
|
||||
// bottom edge
|
||||
uint32_t *src = st.data + (st.segments - 1) * margin * st.stride;
|
||||
for (size_t y = 0; y < margin; y++) {
|
||||
uint32_t *d = (uint32_t*)(decs.bottom.buffer.data.front + y * decs.bottom.buffer.stride);
|
||||
uint32_t *s = src + st.stride * y;
|
||||
// left corner
|
||||
for (size_t x = 0; x < st.corner_size && x < decs.bottom.buffer.width; x++) d[x] = s[x];
|
||||
// middle
|
||||
size_t pos = st.corner_size, limit = decs.bottom.buffer.width > st.corner_size ? decs.bottom.buffer.width - st.corner_size : 0;
|
||||
s += st.corner_size;
|
||||
while (pos < limit) {
|
||||
uint32_t *p = d + pos;
|
||||
for (size_t x = 0; x < margin && pos + x < limit; x++) p[x] = s[x];
|
||||
pos += margin;
|
||||
}
|
||||
// right corner
|
||||
s += margin;
|
||||
for (size_t x = 0; x < st.corner_size && limit + x < decs.bottom.buffer.width; x++) d[limit + x] = s[x];
|
||||
}
|
||||
|
||||
// upper corners
|
||||
for (size_t y = 0; y < st.corner_size && y < decs.left.buffer.height; y++) {
|
||||
uint32_t *left_src = st.data + st.stride * y;
|
||||
uint32_t *left_dest = (uint32_t*)(decs.left.buffer.data.front + y * decs.left.buffer.stride);
|
||||
memcpy(left_dest, left_src, margin * sizeof(uint32_t));
|
||||
uint32_t *right_src = left_src + 2 * st.corner_size;
|
||||
uint32_t *right_dest = (uint32_t*)(decs.right.buffer.data.front + y * decs.right.buffer.stride);
|
||||
memcpy(right_dest, right_src, margin * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
// lower corners
|
||||
size_t src_height = st.corner_size - margin;
|
||||
size_t dest_top = decs.left.buffer.height > src_height ? decs.left.buffer.height - src_height : 0;
|
||||
size_t src_top = st.corner_size + margin;
|
||||
for (size_t src_y = src_top, dest_y = dest_top; src_y < src_top + src_height && dest_y < decs.left.buffer.height; src_y++, dest_y++) {
|
||||
uint32_t *s = st.data + st.stride * src_y;
|
||||
uint32_t *d = (uint32_t*)(decs.left.buffer.data.front + dest_y * decs.left.buffer.stride);
|
||||
memcpy(d, s, margin * sizeof(uint32_t));
|
||||
s += 2 * st.corner_size;
|
||||
d = (uint32_t*)(decs.right.buffer.data.front + dest_y * decs.left.buffer.stride);
|
||||
memcpy(d, s, margin * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
// sides
|
||||
size_t limit = decs.left.buffer.height > src_height ? decs.left.buffer.height - src_height : 0;
|
||||
for (size_t dest_y = st.corner_size, src_y = 0; dest_y < limit; dest_y++, src_y = (src_y + 1) % margin) {
|
||||
uint32_t *src = st.data + (st.corner_size + src_y) * st.stride;
|
||||
uint32_t *left_dest = (uint32_t*)(decs.left.buffer.data.front + dest_y * decs.left.buffer.stride);
|
||||
memcpy(left_dest, src, margin * sizeof(uint32_t));
|
||||
src += 2 * st.corner_size;
|
||||
uint32_t *right_dest = (uint32_t*)(decs.right.buffer.data.front + dest_y * decs.right.buffer.stride);
|
||||
memcpy(right_dest, src, margin * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
#define copy(which) for (uint32_t *src = (uint32_t*)decs.which.buffer.data.front, *dest = (uint32_t*)decs.which.buffer.data.back; src < (uint32_t*)(decs.which.buffer.data.front + decs.which.buffer.size_in_bytes); src++, dest++) *dest = (A(*src) / 2 ) << 24;
|
||||
copy(left); copy(bottom); copy(right);
|
||||
#undef copy
|
||||
|
||||
}
|
||||
#undef st
|
||||
|
||||
static bool
|
||||
create_shm_buffers(_GLFWwindow* window) {
|
||||
const unsigned scale = window->wl.scale >= 1 ? window->wl.scale : 1;
|
||||
|
||||
const size_t vertical_width = decs.metrics.width, vertical_height = window->wl.height + decs.metrics.top;
|
||||
const size_t horizontal_height = decs.metrics.width, horizontal_width = window->wl.width + 2 * decs.metrics.width;
|
||||
|
||||
decs.mapping.size = 0;
|
||||
decs.mapping.size += init_buffer_pair(&decs.top.buffer, window->wl.width, decs.metrics.top, scale);
|
||||
decs.mapping.size += init_buffer_pair(&decs.left.buffer, vertical_width, vertical_height, scale);
|
||||
decs.mapping.size += init_buffer_pair(&decs.bottom.buffer, horizontal_width, horizontal_height, scale);
|
||||
decs.mapping.size += init_buffer_pair(&decs.right.buffer, vertical_width, vertical_height, scale);
|
||||
|
||||
int fd = createAnonymousFile(decs.mapping.size);
|
||||
if (fd < 0) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Creating a buffer file for %zu B failed: %s",
|
||||
decs.mapping.size, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
decs.mapping.data = mmap(NULL, decs.mapping.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (decs.mapping.data == MAP_FAILED) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: mmap failed: %s", strerror(errno));
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
struct wl_shm_pool* pool = wl_shm_create_pool(_glfw.wl.shm, fd, decs.mapping.size);
|
||||
close(fd);
|
||||
size_t offset = 0;
|
||||
#define a(which) alloc_buffer_pair(&decs.which.buffer, pool, decs.mapping.data, &offset)
|
||||
a(top); a(left); a(bottom); a(right);
|
||||
#undef a
|
||||
wl_shm_pool_destroy(pool);
|
||||
create_shadow_tile(window);
|
||||
render_title_bar(window, true);
|
||||
render_edges(window);
|
||||
debug("Created decoration buffers at scale: %u vertical_height: %zu horizontal_width: %zu\n", scale, vertical_height, horizontal_width);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
free_csd_surfaces(_GLFWwindow *window) {
|
||||
#define d(which) {\
|
||||
if (decs.which.subsurface) wl_subsurface_destroy(decs.which.subsurface); \
|
||||
decs.which.subsurface = NULL; \
|
||||
if (decs.which.surface) wl_surface_destroy(decs.which.surface); \
|
||||
decs.which.surface = NULL; \
|
||||
}
|
||||
d(left); d(top); d(right); d(bottom);
|
||||
#undef d
|
||||
}
|
||||
|
||||
static void
|
||||
free_csd_buffers(_GLFWwindow *window) {
|
||||
#define d(which) { \
|
||||
if (decs.which.buffer.a) wl_buffer_destroy(decs.which.buffer.a); \
|
||||
if (decs.which.buffer.b) wl_buffer_destroy(decs.which.buffer.b); \
|
||||
memset(&decs.which.buffer, 0, sizeof(_GLFWWaylandBufferPair)); \
|
||||
}
|
||||
d(left); d(top); d(right); d(bottom);
|
||||
#undef d
|
||||
if (decs.mapping.data) munmap(decs.mapping.data, decs.mapping.size);
|
||||
decs.mapping.data = NULL; decs.mapping.size = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
position_csd_surface(_GLFWWaylandCSDEdge *s, int x, int y, int scale) {
|
||||
wl_surface_set_buffer_scale(s->surface, scale);
|
||||
s->x = x; s->y = y;
|
||||
wl_subsurface_set_position(s->subsurface, s->x, s->y);
|
||||
}
|
||||
|
||||
static void
|
||||
create_csd_surfaces(_GLFWwindow *window, _GLFWWaylandCSDEdge *s) {
|
||||
s->surface = wl_compositor_create_surface(_glfw.wl.compositor);
|
||||
s->subsurface = wl_subcompositor_get_subsurface(_glfw.wl.subcompositor, s->surface, window->wl.surface);
|
||||
}
|
||||
|
||||
#define damage_csd(which, xbuffer) \
|
||||
wl_surface_attach(decs.which.surface, xbuffer, 0, 0); \
|
||||
wl_surface_damage(decs.which.surface, 0, 0, decs.which.buffer.width, decs.which.buffer.height); \
|
||||
wl_surface_commit(decs.which.surface)
|
||||
|
||||
bool
|
||||
ensure_csd_resources(_GLFWwindow *window) {
|
||||
if (!window->decorated || window->wl.decorations.serverSide) return false;
|
||||
const bool is_focused = window->id == _glfw.focusedWindowId;
|
||||
const bool focus_changed = is_focused != decs.for_window_state.focused;
|
||||
const bool size_changed = (
|
||||
decs.for_window_state.width != window->wl.width ||
|
||||
decs.for_window_state.height != window->wl.height ||
|
||||
decs.for_window_state.scale != window->wl.scale ||
|
||||
!decs.mapping.data
|
||||
);
|
||||
const bool needs_update = focus_changed || size_changed || !decs.left.surface;
|
||||
if (!needs_update) return false;
|
||||
if (size_changed) {
|
||||
free_csd_buffers(window);
|
||||
if (!create_shm_buffers(window)) return false;
|
||||
}
|
||||
|
||||
int32_t x, y, scale = window->wl.scale < 1 ? 1 : window->wl.scale;
|
||||
x = 0; y = -decs.metrics.top;
|
||||
if (!decs.top.surface) create_csd_surfaces(window, &decs.top);
|
||||
position_csd_surface(&decs.top, x, y, scale);
|
||||
|
||||
x = -decs.metrics.width; y = -decs.metrics.top;
|
||||
if (!decs.left.surface) create_csd_surfaces(window, &decs.left);
|
||||
position_csd_surface(&decs.left, x, y, scale);
|
||||
|
||||
x = -decs.metrics.width; y = window->wl.height;
|
||||
if (!decs.bottom.surface) create_csd_surfaces(window, &decs.bottom);
|
||||
position_csd_surface(&decs.bottom, x, y, scale);
|
||||
|
||||
x = window->wl.width; y = -decs.metrics.top;
|
||||
if (!decs.right.surface) create_csd_surfaces(window, &decs.right);
|
||||
position_csd_surface(&decs.right, x, y, scale);
|
||||
|
||||
if (focus_changed) update_title_bar(window);
|
||||
damage_csd(top, decs.top.buffer.front);
|
||||
damage_csd(left, is_focused ? decs.left.buffer.front : decs.left.buffer.back);
|
||||
damage_csd(bottom, is_focused ? decs.bottom.buffer.front : decs.bottom.buffer.back);
|
||||
damage_csd(right, is_focused ? decs.right.buffer.front : decs.right.buffer.back);
|
||||
|
||||
decs.for_window_state.width = window->wl.width;
|
||||
decs.for_window_state.height = window->wl.height;
|
||||
decs.for_window_state.scale = window->wl.scale;
|
||||
decs.for_window_state.focused = is_focused;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
free_all_csd_resources(_GLFWwindow *window) {
|
||||
free_csd_surfaces(window);
|
||||
free_csd_buffers(window);
|
||||
if (decs.shadow_tile.data) free(decs.shadow_tile.data);
|
||||
decs.shadow_tile.data = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
change_csd_title(_GLFWwindow *window) {
|
||||
if (!window->decorated || window->wl.decorations.serverSide) return;
|
||||
if (ensure_csd_resources(window)) return; // CSD were re-rendered for other reasons
|
||||
if (decs.top.surface) {
|
||||
update_title_bar(window);
|
||||
damage_csd(top, decs.top.buffer.front);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_csd_window_geometry(_GLFWwindow *window, int32_t *width, int32_t *height) {
|
||||
bool has_csd = window->decorated && !window->wl.decorations.serverSide && window->wl.decorations.left.surface && !(window->wl.toplevel_states & TOPLEVEL_STATE_FULLSCREEN);
|
||||
bool size_specified_by_compositor = *width > 0 && *height > 0;
|
||||
if (!size_specified_by_compositor) {
|
||||
*width = window->wl.user_requested_content_size.width;
|
||||
*height = window->wl.user_requested_content_size.height;
|
||||
if (has_csd) *height += decs.metrics.visible_titlebar_height;
|
||||
}
|
||||
decs.geometry.x = 0; decs.geometry.y = 0;
|
||||
decs.geometry.width = *width; decs.geometry.height = *height;
|
||||
if (has_csd) {
|
||||
decs.geometry.y = -decs.metrics.visible_titlebar_height;
|
||||
*height -= decs.metrics.visible_titlebar_height;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
set_titlebar_color(_GLFWwindow *window, uint32_t color, bool use_system_color) {
|
||||
bool use_custom_color = !use_system_color;
|
||||
if (use_custom_color != decs.use_custom_titlebar_color || color != decs.titlebar_color) {
|
||||
decs.use_custom_titlebar_color = use_custom_color;
|
||||
decs.titlebar_color = color;
|
||||
}
|
||||
if (window->decorated && decs.top.surface) {
|
||||
update_title_bar(window);
|
||||
damage_csd(top, decs.top.buffer.front);
|
||||
}
|
||||
}
|
||||
17
glfw/wl_client_side_decorations.h
vendored
Normal file
17
glfw/wl_client_side_decorations.h
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
void initialize_csd_metrics(_GLFWwindow *window);
|
||||
void free_all_csd_resources(_GLFWwindow *window);
|
||||
void free_csd_surfaces(_GLFWwindow *window);
|
||||
void change_csd_title(_GLFWwindow *window);
|
||||
bool ensure_csd_resources(_GLFWwindow *window);
|
||||
void set_csd_window_geometry(_GLFWwindow *window, int32_t *width, int32_t *height);
|
||||
void set_titlebar_color(_GLFWwindow *window, uint32_t color, bool use_system_color);
|
||||
46
glfw/wl_cursors.c
vendored
46
glfw/wl_cursors.c
vendored
@@ -1,6 +1,7 @@
|
||||
// Future devs supporting whatever Wayland protocol stabilizes for cursor selection: see _themeAdd.
|
||||
|
||||
#include "internal.h"
|
||||
#include "linux_desktop_settings.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
@@ -8,38 +9,35 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static GLFWWLCursorThemes cursor_themes;
|
||||
|
||||
static int
|
||||
pixels_from_scale(int scale) {
|
||||
static bool queried_env = false;
|
||||
static int factor = 32;
|
||||
if (!queried_env) {
|
||||
const char *env = getenv("XCURSOR_SIZE");
|
||||
if (env) {
|
||||
const int retval = atoi(env);
|
||||
if (retval > 0 && retval < 2048) factor = retval;
|
||||
}
|
||||
queried_env = true;
|
||||
}
|
||||
int factor;
|
||||
const char* name;
|
||||
glfw_current_cursor_theme(&name, &factor);
|
||||
return factor * scale;
|
||||
}
|
||||
|
||||
|
||||
struct wl_cursor_theme*
|
||||
glfw_wlc_theme_for_scale(int scale) {
|
||||
GLFWWLCursorThemes *t = &_glfw.wl.cursor_themes;
|
||||
for (size_t i = 0; i < t->count; i++) {
|
||||
if (t->themes[i].scale == scale) return t->themes[i].theme;
|
||||
for (size_t i = 0; i < cursor_themes.count; i++) {
|
||||
if (cursor_themes.themes[i].scale == scale) return cursor_themes.themes[i].theme;
|
||||
}
|
||||
|
||||
if (t->count >= t->capacity) {
|
||||
t->themes = realloc(t->themes, sizeof(GLFWWLCursorTheme) * (t->count + 16));
|
||||
if (!t->themes) {
|
||||
if (cursor_themes.count >= cursor_themes.capacity) {
|
||||
cursor_themes.themes = realloc(cursor_themes.themes, sizeof(GLFWWLCursorTheme) * (cursor_themes.count + 16));
|
||||
if (!cursor_themes.themes) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: Out of memory allocating space for cursor themes");
|
||||
return NULL;
|
||||
}
|
||||
t->capacity = t->count + 16;
|
||||
cursor_themes.capacity = cursor_themes.count + 16;
|
||||
}
|
||||
struct wl_cursor_theme *ans = wl_cursor_theme_load(getenv("XCURSOR_THEME"), pixels_from_scale(scale), _glfw.wl.shm);
|
||||
int factor;
|
||||
const char* name;
|
||||
glfw_current_cursor_theme(&name, &factor);
|
||||
struct wl_cursor_theme *ans = wl_cursor_theme_load(name, pixels_from_scale(scale), _glfw.wl.shm);
|
||||
if (!ans) {
|
||||
_glfwInputError(
|
||||
GLFW_PLATFORM_ERROR, "Wayland: wl_cursor_theme_load failed at scale: %d pixels: %d",
|
||||
@@ -47,7 +45,7 @@ glfw_wlc_theme_for_scale(int scale) {
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
GLFWWLCursorTheme *theme = t->themes + t->count++;
|
||||
GLFWWLCursorTheme *theme = cursor_themes.themes + cursor_themes.count++;
|
||||
theme->scale = scale;
|
||||
theme->theme = ans;
|
||||
return ans;
|
||||
@@ -55,11 +53,9 @@ glfw_wlc_theme_for_scale(int scale) {
|
||||
|
||||
void
|
||||
glfw_wlc_destroy(void) {
|
||||
GLFWWLCursorThemes *t = &_glfw.wl.cursor_themes;
|
||||
|
||||
for (size_t i = 0; i < t->count; i++) {
|
||||
wl_cursor_theme_destroy(t->themes[i].theme);
|
||||
for (size_t i = 0; i < cursor_themes.count; i++) {
|
||||
wl_cursor_theme_destroy(cursor_themes.themes[i].theme);
|
||||
}
|
||||
free(t->themes);
|
||||
t->themes = NULL; t->capacity = 0; t->count = 0;
|
||||
free(cursor_themes.themes);
|
||||
cursor_themes.themes = NULL; cursor_themes.capacity = 0; cursor_themes.count = 0;
|
||||
}
|
||||
|
||||
168
glfw/wl_init.c
vendored
168
glfw/wl_init.c
vendored
@@ -29,6 +29,8 @@
|
||||
#define _GNU_SOURCE
|
||||
#include "internal.h"
|
||||
#include "backend_utils.h"
|
||||
#include "wl_client_side_decorations.h"
|
||||
#include "linux_desktop_settings.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#include <assert.h>
|
||||
@@ -58,37 +60,21 @@ static inline int min(int n1, int n2)
|
||||
return n1 < n2 ? n1 : n2;
|
||||
}
|
||||
|
||||
static _GLFWwindow* findWindowFromDecorationSurface(struct wl_surface* surface,
|
||||
int* which)
|
||||
static _GLFWwindow*
|
||||
findWindowFromDecorationSurface(struct wl_surface* surface, _GLFWdecorationSideWayland* which)
|
||||
{
|
||||
int focus;
|
||||
_GLFWdecorationSideWayland focus;
|
||||
if (!which) which = &focus;
|
||||
_GLFWwindow* window = _glfw.windowListHead;
|
||||
if (!which)
|
||||
which = &focus;
|
||||
while (window)
|
||||
{
|
||||
if (surface == window->wl.decorations.top.surface)
|
||||
{
|
||||
*which = topDecoration;
|
||||
break;
|
||||
}
|
||||
if (surface == window->wl.decorations.left.surface)
|
||||
{
|
||||
*which = leftDecoration;
|
||||
break;
|
||||
}
|
||||
if (surface == window->wl.decorations.right.surface)
|
||||
{
|
||||
*which = rightDecoration;
|
||||
break;
|
||||
}
|
||||
if (surface == window->wl.decorations.bottom.surface)
|
||||
{
|
||||
*which = bottomDecoration;
|
||||
break;
|
||||
}
|
||||
#define q(edge, result) if (surface == window->wl.decorations.edge.surface) { *which = result; break; }
|
||||
while (window) {
|
||||
q(top, TOP_DECORATION);
|
||||
q(left, LEFT_DECORATION);
|
||||
q(right, RIGHT_DECORATION);
|
||||
q(bottom, BOTTOM_DECORATION);
|
||||
window = window->next;
|
||||
}
|
||||
#undef q
|
||||
return window;
|
||||
}
|
||||
|
||||
@@ -103,7 +89,7 @@ static void pointerHandleEnter(void* data UNUSED,
|
||||
if (!surface)
|
||||
return;
|
||||
|
||||
int focus = 0;
|
||||
_GLFWdecorationSideWayland focus = CENTRAL_WINDOW;
|
||||
_GLFWwindow* window = wl_surface_get_user_data(surface);
|
||||
if (!window)
|
||||
{
|
||||
@@ -173,6 +159,8 @@ static void setCursor(GLFWCursorShape shape, _GLFWwindow* window)
|
||||
_glfw.wl.cursorPreviousShape = shape;
|
||||
}
|
||||
|
||||
#define x window->wl.allCursorPosX
|
||||
#define y window->wl.allCursorPosY
|
||||
static void pointerHandleMotion(void* data UNUSED,
|
||||
struct wl_pointer* pointer UNUSED,
|
||||
uint32_t time UNUSED,
|
||||
@@ -181,46 +169,45 @@ static void pointerHandleMotion(void* data UNUSED,
|
||||
{
|
||||
_GLFWwindow* window = _glfw.wl.pointerFocus;
|
||||
GLFWCursorShape cursorShape = GLFW_ARROW_CURSOR;
|
||||
double x, y;
|
||||
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||
return;
|
||||
x = wl_fixed_to_double(sx);
|
||||
y = wl_fixed_to_double(sy);
|
||||
window->wl.allCursorPosX = wl_fixed_to_double(sx);
|
||||
window->wl.allCursorPosY = wl_fixed_to_double(sy);
|
||||
|
||||
switch (window->wl.decorations.focus)
|
||||
{
|
||||
case mainWindow:
|
||||
case CENTRAL_WINDOW:
|
||||
window->wl.cursorPosX = x;
|
||||
window->wl.cursorPosY = y;
|
||||
_glfwInputCursorPos(window, x, y);
|
||||
_glfw.wl.cursorPreviousShape = GLFW_INVALID_CURSOR;
|
||||
return;
|
||||
case topDecoration:
|
||||
if (y < _GLFW_DECORATION_WIDTH)
|
||||
case TOP_DECORATION:
|
||||
if (y < window->wl.decorations.metrics.width)
|
||||
cursorShape = GLFW_VRESIZE_CURSOR;
|
||||
else
|
||||
cursorShape = GLFW_ARROW_CURSOR;
|
||||
break;
|
||||
case leftDecoration:
|
||||
if (y < _GLFW_DECORATION_WIDTH)
|
||||
case LEFT_DECORATION:
|
||||
if (y < window->wl.decorations.metrics.width)
|
||||
cursorShape = GLFW_NW_RESIZE_CURSOR;
|
||||
else
|
||||
cursorShape = GLFW_HRESIZE_CURSOR;
|
||||
break;
|
||||
case rightDecoration:
|
||||
if (y < _GLFW_DECORATION_WIDTH)
|
||||
case RIGHT_DECORATION:
|
||||
if (y < window->wl.decorations.metrics.width)
|
||||
cursorShape = GLFW_NE_RESIZE_CURSOR;
|
||||
else
|
||||
cursorShape = GLFW_HRESIZE_CURSOR;
|
||||
break;
|
||||
case bottomDecoration:
|
||||
if (x < _GLFW_DECORATION_WIDTH)
|
||||
case BOTTOM_DECORATION:
|
||||
if (x < window->wl.decorations.metrics.width)
|
||||
cursorShape = GLFW_SW_RESIZE_CURSOR;
|
||||
else if (x > window->wl.width + _GLFW_DECORATION_WIDTH)
|
||||
else if (x > window->wl.width + window->wl.decorations.metrics.width)
|
||||
cursorShape = GLFW_SE_RESIZE_CURSOR;
|
||||
else
|
||||
cursorShape = GLFW_VRESIZE_CURSOR;
|
||||
@@ -249,33 +236,46 @@ static void pointerHandleButton(void* data UNUSED,
|
||||
{
|
||||
switch (window->wl.decorations.focus)
|
||||
{
|
||||
case mainWindow:
|
||||
case CENTRAL_WINDOW:
|
||||
break;
|
||||
case topDecoration:
|
||||
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||
case TOP_DECORATION:
|
||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||
monotonic_t last_click_at = window->wl.decorations.last_click_on_top_decoration_at;
|
||||
window->wl.decorations.last_click_on_top_decoration_at = monotonic();
|
||||
if (window->wl.decorations.last_click_on_top_decoration_at - last_click_at <= _glfwPlatformGetDoubleClickInterval(window)) {
|
||||
window->wl.decorations.last_click_on_top_decoration_at = 0;
|
||||
if (window->wl.toplevel_states & TOPLEVEL_STATE_MAXIMIZED)
|
||||
xdg_toplevel_unset_maximized(window->wl.xdg.toplevel);
|
||||
else
|
||||
xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (y < window->wl.decorations.metrics.width)
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
|
||||
else
|
||||
{
|
||||
if (window->wl.xdg.toplevel)
|
||||
xdg_toplevel_move(window->wl.xdg.toplevel, _glfw.wl.seat, serial);
|
||||
}
|
||||
|
||||
break;
|
||||
case leftDecoration:
|
||||
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||
case LEFT_DECORATION:
|
||||
if (y < window->wl.decorations.metrics.width)
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
|
||||
else
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
|
||||
break;
|
||||
case rightDecoration:
|
||||
if (window->wl.cursorPosY < _GLFW_DECORATION_WIDTH)
|
||||
case RIGHT_DECORATION:
|
||||
if (y < window->wl.decorations.metrics.width)
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
|
||||
else
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
|
||||
break;
|
||||
case bottomDecoration:
|
||||
if (window->wl.cursorPosX < _GLFW_DECORATION_WIDTH)
|
||||
case BOTTOM_DECORATION:
|
||||
if (x < window->wl.decorations.metrics.width)
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
|
||||
else if (window->wl.cursorPosX > window->wl.width + _GLFW_DECORATION_WIDTH)
|
||||
else if (x > window->wl.width + window->wl.decorations.metrics.width)
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
|
||||
else
|
||||
edges = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
|
||||
@@ -291,18 +291,15 @@ static void pointerHandleButton(void* data UNUSED,
|
||||
}
|
||||
else if (button == BTN_RIGHT)
|
||||
{
|
||||
if (window->wl.decorations.focus != mainWindow && window->wl.xdg.toplevel)
|
||||
if (window->wl.decorations.focus != CENTRAL_WINDOW && window->wl.xdg.toplevel)
|
||||
{
|
||||
xdg_toplevel_show_window_menu(window->wl.xdg.toplevel,
|
||||
_glfw.wl.seat, serial,
|
||||
(int32_t)window->wl.cursorPosX,
|
||||
(int32_t)window->wl.cursorPosY);
|
||||
xdg_toplevel_show_window_menu(window->wl.xdg.toplevel, _glfw.wl.seat, serial, (int32_t)x, (int32_t)y - window->wl.decorations.metrics.top);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Don’t pass the button to the user if it was related to a decoration.
|
||||
if (window->wl.decorations.focus != mainWindow)
|
||||
if (window->wl.decorations.focus != CENTRAL_WINDOW)
|
||||
return;
|
||||
|
||||
_glfw.wl.serial = serial;
|
||||
@@ -318,6 +315,8 @@ static void pointerHandleButton(void* data UNUSED,
|
||||
: GLFW_RELEASE,
|
||||
_glfw.wl.xkb.states.modifiers);
|
||||
}
|
||||
#undef x
|
||||
#undef y
|
||||
|
||||
static void pointerHandleAxis(void* data UNUSED,
|
||||
struct wl_pointer* pointer UNUSED,
|
||||
@@ -393,7 +392,7 @@ static void keyboardHandleEnter(void* data UNUSED,
|
||||
}
|
||||
|
||||
_glfw.wl.serial = serial;
|
||||
_glfw.wl.keyboardFocus = window;
|
||||
_glfw.wl.keyboardFocusId = window->id;
|
||||
_glfwInputWindowFocus(window, true);
|
||||
uint32_t* key;
|
||||
if (keys && _glfw.wl.keyRepeatInfo.key) {
|
||||
@@ -411,21 +410,23 @@ static void keyboardHandleLeave(void* data UNUSED,
|
||||
uint32_t serial,
|
||||
struct wl_surface* surface UNUSED)
|
||||
{
|
||||
_GLFWwindow* window = _glfw.wl.keyboardFocus;
|
||||
_GLFWwindow* window = _glfwWindowForId(_glfw.wl.keyboardFocusId);
|
||||
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
_glfw.wl.serial = serial;
|
||||
_glfw.wl.keyboardFocus = NULL;
|
||||
_glfw.wl.keyboardFocusId = 0;
|
||||
_glfwInputWindowFocus(window, false);
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
dispatchPendingKeyRepeats(id_type timer_id UNUSED, void *data UNUSED) {
|
||||
if (_glfw.wl.keyRepeatInfo.keyboardFocus != _glfw.wl.keyboardFocus || _glfw.wl.keyboardRepeatRate == 0) return;
|
||||
glfw_xkb_handle_key_event(_glfw.wl.keyRepeatInfo.keyboardFocus, &_glfw.wl.xkb, _glfw.wl.keyRepeatInfo.key, GLFW_REPEAT);
|
||||
if (_glfw.wl.keyRepeatInfo.keyboardFocusId != _glfw.wl.keyboardFocusId || _glfw.wl.keyboardRepeatRate == 0) return;
|
||||
_GLFWwindow* window = _glfwWindowForId(_glfw.wl.keyboardFocusId);
|
||||
if (!window) return;
|
||||
glfw_xkb_handle_key_event(window, &_glfw.wl.xkb, _glfw.wl.keyRepeatInfo.key, GLFW_REPEAT);
|
||||
changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, (s_to_monotonic_t(1ll) / (monotonic_t)_glfw.wl.keyboardRepeatRate));
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 1);
|
||||
}
|
||||
@@ -438,26 +439,24 @@ static void keyboardHandleKey(void* data UNUSED,
|
||||
uint32_t key,
|
||||
uint32_t state)
|
||||
{
|
||||
_GLFWwindow* window = _glfw.wl.keyboardFocus;
|
||||
_GLFWwindow* window = _glfwWindowForId(_glfw.wl.keyboardFocusId);
|
||||
if (!window)
|
||||
return;
|
||||
int action = state == WL_KEYBOARD_KEY_STATE_PRESSED ? GLFW_PRESS : GLFW_RELEASE;
|
||||
|
||||
_glfw.wl.serial = serial;
|
||||
glfw_xkb_handle_key_event(window, &_glfw.wl.xkb, key, action);
|
||||
bool repeatable = false;
|
||||
_glfw.wl.keyRepeatInfo.key = 0;
|
||||
|
||||
if (action == GLFW_PRESS && _glfw.wl.keyboardRepeatRate > 0 && glfw_xkb_should_repeat(&_glfw.wl.xkb, key))
|
||||
{
|
||||
_glfw.wl.keyRepeatInfo.key = key;
|
||||
repeatable = true;
|
||||
_glfw.wl.keyRepeatInfo.keyboardFocus = window;
|
||||
}
|
||||
if (repeatable) {
|
||||
_glfw.wl.keyRepeatInfo.keyboardFocusId = window->id;
|
||||
changeTimerInterval(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, _glfw.wl.keyboardRepeatDelay);
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 1);
|
||||
} else if (action == GLFW_RELEASE && key == _glfw.wl.keyRepeatInfo.key) {
|
||||
_glfw.wl.keyRepeatInfo.key = 0;
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, 0);
|
||||
}
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.keyRepeatInfo.keyRepeatTimer, repeatable ? 1 : 0);
|
||||
}
|
||||
|
||||
static void keyboardHandleModifiers(void* data UNUSED,
|
||||
@@ -584,6 +583,7 @@ static void registryHandleGlobal(void* data UNUSED,
|
||||
if (_glfw.wl.primarySelectionDeviceManager && !_glfw.wl.primarySelectionDevice) {
|
||||
_glfwSetupWaylandPrimarySelectionDevice();
|
||||
}
|
||||
_glfwWaylandInitTextInput();
|
||||
}
|
||||
}
|
||||
else if (strcmp(interface, "xdg_wm_base") == 0)
|
||||
@@ -598,11 +598,6 @@ static void registryHandleGlobal(void* data UNUSED,
|
||||
wl_registry_bind(registry, name,
|
||||
&zxdg_decoration_manager_v1_interface, 1);
|
||||
}
|
||||
else if (strcmp(interface, "wp_viewporter") == 0)
|
||||
{
|
||||
_glfw.wl.viewporter =
|
||||
wl_registry_bind(registry, name, &wp_viewporter_interface, 1);
|
||||
}
|
||||
else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0)
|
||||
{
|
||||
_glfw.wl.relativePointerManager =
|
||||
@@ -617,6 +612,11 @@ static void registryHandleGlobal(void* data UNUSED,
|
||||
&zwp_pointer_constraints_v1_interface,
|
||||
1);
|
||||
}
|
||||
else if (strcmp(interface, GLFW_WAYLAND_TEXT_INPUT_INTERFACE_NAME) == 0)
|
||||
{
|
||||
_glfwWaylandBindTextInput(registry, name);
|
||||
_glfwWaylandInitTextInput();
|
||||
}
|
||||
else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0)
|
||||
{
|
||||
_glfw.wl.idleInhibitManager =
|
||||
@@ -717,6 +717,9 @@ glfwWaylandCheckForServerSideDecorations(void) {
|
||||
|
||||
int _glfwPlatformInit(void)
|
||||
{
|
||||
int i;
|
||||
_GLFWmonitor* monitor;
|
||||
|
||||
_glfw.wl.cursor.handle = _glfw_dlopen("libwayland-cursor.so.0");
|
||||
if (!_glfw.wl.cursor.handle)
|
||||
{
|
||||
@@ -754,6 +757,7 @@ int _glfwPlatformInit(void)
|
||||
"Wayland: Failed to initialize event loop data");
|
||||
}
|
||||
glfw_dbus_init(&_glfw.wl.dbus, &_glfw.wl.eventLoopData);
|
||||
glfw_initialize_desktop_settings();
|
||||
_glfw.wl.keyRepeatInfo.keyRepeatTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-key-repeat", ms_to_monotonic_t(500ll), 0, true, dispatchPendingKeyRepeats, NULL, NULL);
|
||||
_glfw.wl.cursorAnimationTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-cursor-animation", ms_to_monotonic_t(500ll), 0, true, animateCursorImage, NULL, NULL);
|
||||
|
||||
@@ -768,6 +772,17 @@ int _glfwPlatformInit(void)
|
||||
// Sync so we got all initial output events
|
||||
wl_display_roundtrip(_glfw.wl.display);
|
||||
|
||||
for (i = 0; i < _glfw.monitorCount; ++i)
|
||||
{
|
||||
monitor = _glfw.monitors[i];
|
||||
if (monitor->widthMM <= 0 || monitor->heightMM <= 0)
|
||||
{
|
||||
// If Wayland does not provide a physical size, assume the default 96 DPI
|
||||
monitor->widthMM = (int) (monitor->modes[monitor->wl.currentMode].width * 25.4f / 96.f);
|
||||
monitor->heightMM = (int) (monitor->modes[monitor->wl.currentMode].height * 25.4f / 96.f);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_glfw.wl.wmBase)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
@@ -817,8 +832,6 @@ void _glfwPlatformTerminate(void)
|
||||
wl_compositor_destroy(_glfw.wl.compositor);
|
||||
if (_glfw.wl.shm)
|
||||
wl_shm_destroy(_glfw.wl.shm);
|
||||
if (_glfw.wl.viewporter)
|
||||
wp_viewporter_destroy(_glfw.wl.viewporter);
|
||||
if (_glfw.wl.decorationManager)
|
||||
zxdg_decoration_manager_v1_destroy(_glfw.wl.decorationManager);
|
||||
if (_glfw.wl.wmBase)
|
||||
@@ -833,6 +846,7 @@ void _glfwPlatformTerminate(void)
|
||||
zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager);
|
||||
if (_glfw.wl.pointerConstraints)
|
||||
zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints);
|
||||
_glfwWaylandDestroyTextInput();
|
||||
if (_glfw.wl.idleInhibitManager)
|
||||
zwp_idle_inhibit_manager_v1_destroy(_glfw.wl.idleInhibitManager);
|
||||
if (_glfw.wl.dataSourceForClipboard)
|
||||
|
||||
112
glfw/wl_platform.h
vendored
112
glfw/wl_platform.h
vendored
@@ -53,12 +53,12 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
|
||||
#include "wl_cursors.h"
|
||||
|
||||
#include "wayland-xdg-shell-client-protocol.h"
|
||||
#include "wayland-viewporter-client-protocol.h"
|
||||
#include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
|
||||
#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
|
||||
#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
|
||||
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
|
||||
#include "wayland-primary-selection-unstable-v1-client-protocol.h"
|
||||
#include "wl_text_input.h"
|
||||
|
||||
#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL)
|
||||
#define _glfw_dlclose(handle) dlclose(handle)
|
||||
@@ -88,28 +88,45 @@ typedef void (* PFN_wl_egl_window_resize)(struct wl_egl_window*, int, int, int,
|
||||
#define wl_egl_window_destroy _glfw.wl.egl.window_destroy
|
||||
#define wl_egl_window_resize _glfw.wl.egl.window_resize
|
||||
|
||||
#define _GLFW_DECORATION_WIDTH 4
|
||||
#define _GLFW_DECORATION_TOP 24
|
||||
#define _GLFW_DECORATION_VERTICAL (_GLFW_DECORATION_TOP + _GLFW_DECORATION_WIDTH)
|
||||
#define _GLFW_DECORATION_HORIZONTAL (2 * _GLFW_DECORATION_WIDTH)
|
||||
|
||||
typedef enum _GLFWdecorationSideWayland
|
||||
{
|
||||
mainWindow,
|
||||
topDecoration,
|
||||
leftDecoration,
|
||||
rightDecoration,
|
||||
bottomDecoration,
|
||||
|
||||
CENTRAL_WINDOW,
|
||||
TOP_DECORATION,
|
||||
LEFT_DECORATION,
|
||||
RIGHT_DECORATION,
|
||||
BOTTOM_DECORATION,
|
||||
} _GLFWdecorationSideWayland;
|
||||
|
||||
typedef struct _GLFWdecorationWayland
|
||||
{
|
||||
struct wl_surface* surface;
|
||||
struct wl_subsurface* subsurface;
|
||||
struct wp_viewport* viewport;
|
||||
typedef struct _GLFWWaylandBufferPair {
|
||||
struct wl_buffer *a, *b, *front, *back;
|
||||
struct { uint8_t *a, *b, *front, *back; } data;
|
||||
bool has_pending_update;
|
||||
size_t size_in_bytes, width, height, stride;
|
||||
} _GLFWWaylandBufferPair;
|
||||
|
||||
typedef struct _GLFWWaylandCSDEdge {
|
||||
struct wl_surface *surface;
|
||||
struct wl_subsurface *subsurface;
|
||||
_GLFWWaylandBufferPair buffer;
|
||||
int x, y;
|
||||
} _GLFWWaylandCSDEdge;
|
||||
|
||||
typedef enum WaylandWindowState {
|
||||
|
||||
TOPLEVEL_STATE_NONE = 0,
|
||||
TOPLEVEL_STATE_MAXIMIZED = 1,
|
||||
TOPLEVEL_STATE_FULLSCREEN = 2,
|
||||
TOPLEVEL_STATE_RESIZING = 4,
|
||||
TOPLEVEL_STATE_ACTIVATED = 8,
|
||||
TOPLEVEL_STATE_TILED_LEFT = 16,
|
||||
TOPLEVEL_STATE_TILED_RIGHT = 32,
|
||||
TOPLEVEL_STATE_TILED_TOP = 64,
|
||||
TOPLEVEL_STATE_TILED_BOTTOM = 128,
|
||||
} WaylandWindowState;
|
||||
|
||||
|
||||
static const WaylandWindowState TOPLEVEL_STATE_DOCKED = TOPLEVEL_STATE_MAXIMIZED | TOPLEVEL_STATE_FULLSCREEN | TOPLEVEL_STATE_TILED_TOP | TOPLEVEL_STATE_TILED_LEFT | TOPLEVEL_STATE_TILED_RIGHT | TOPLEVEL_STATE_TILED_BOTTOM;
|
||||
|
||||
} _GLFWdecorationWayland;
|
||||
|
||||
// Wayland-specific per-window data
|
||||
//
|
||||
@@ -117,7 +134,6 @@ typedef struct _GLFWwindowWayland
|
||||
{
|
||||
int width, height;
|
||||
bool visible;
|
||||
bool maximized;
|
||||
bool hovered;
|
||||
bool transparent;
|
||||
struct wl_surface* surface;
|
||||
@@ -131,7 +147,7 @@ typedef struct _GLFWwindowWayland
|
||||
} xdg;
|
||||
|
||||
_GLFWcursor* currentCursor;
|
||||
double cursorPosX, cursorPosY;
|
||||
double cursorPosX, cursorPosY, allCursorPosX, allCursorPosY;
|
||||
|
||||
char* title;
|
||||
char appId[256];
|
||||
@@ -151,13 +167,37 @@ typedef struct _GLFWwindowWayland
|
||||
|
||||
struct zwp_idle_inhibitor_v1* idleInhibitor;
|
||||
|
||||
bool fullscreened;
|
||||
|
||||
struct {
|
||||
bool serverSide;
|
||||
struct wl_buffer* buffer;
|
||||
_GLFWdecorationWayland top, left, right, bottom;
|
||||
int focus;
|
||||
bool serverSide;
|
||||
_GLFWdecorationSideWayland focus;
|
||||
_GLFWWaylandCSDEdge top, left, right, bottom;
|
||||
|
||||
struct {
|
||||
uint8_t *data;
|
||||
size_t size;
|
||||
} mapping;
|
||||
|
||||
struct {
|
||||
int width, height, scale;
|
||||
bool focused;
|
||||
} for_window_state;
|
||||
|
||||
struct {
|
||||
unsigned int width, top, horizontal, vertical, visible_titlebar_height;
|
||||
} metrics;
|
||||
|
||||
struct {
|
||||
int32_t x, y, width, height;
|
||||
} geometry;
|
||||
|
||||
struct {
|
||||
uint32_t *data;
|
||||
size_t for_decoration_size, stride, segments, corner_size;
|
||||
} shadow_tile;
|
||||
monotonic_t last_click_on_top_decoration_at;
|
||||
|
||||
uint32_t titlebar_color;
|
||||
bool use_custom_titlebar_color;
|
||||
} decorations;
|
||||
|
||||
struct {
|
||||
@@ -166,6 +206,12 @@ typedef struct _GLFWwindowWayland
|
||||
struct wl_callback *current_wl_callback;
|
||||
} frameCallbackData;
|
||||
|
||||
struct {
|
||||
int32_t width, height;
|
||||
} user_requested_content_size;
|
||||
|
||||
uint32_t toplevel_states;
|
||||
bool maximize_on_first_show;
|
||||
|
||||
} _GLFWwindowWayland;
|
||||
|
||||
@@ -204,14 +250,13 @@ typedef struct _GLFWlibraryWayland
|
||||
struct wl_seat* seat;
|
||||
struct wl_pointer* pointer;
|
||||
struct wl_keyboard* keyboard;
|
||||
struct wl_data_device_manager* dataDeviceManager;
|
||||
struct wl_data_device* dataDevice;
|
||||
struct xdg_wm_base* wmBase;
|
||||
struct zxdg_decoration_manager_v1* decorationManager;
|
||||
struct wp_viewporter* viewporter;
|
||||
struct zwp_relative_pointer_manager_v1* relativePointerManager;
|
||||
struct zwp_pointer_constraints_v1* pointerConstraints;
|
||||
struct zwp_idle_inhibit_manager_v1* idleInhibitManager;
|
||||
struct wl_data_device_manager* dataDeviceManager;
|
||||
struct wl_data_device* dataDevice;
|
||||
struct wl_data_source* dataSourceForClipboard;
|
||||
struct zwp_primary_selection_device_manager_v1* primarySelectionDeviceManager;
|
||||
struct zwp_primary_selection_device_v1* primarySelectionDevice;
|
||||
@@ -226,17 +271,18 @@ typedef struct _GLFWlibraryWayland
|
||||
|
||||
int32_t keyboardRepeatRate;
|
||||
monotonic_t keyboardRepeatDelay;
|
||||
|
||||
struct {
|
||||
uint32_t key;
|
||||
id_type keyRepeatTimer;
|
||||
_GLFWwindow* keyboardFocus;
|
||||
GLFWid keyboardFocusId;
|
||||
} keyRepeatInfo;
|
||||
id_type cursorAnimationTimer;
|
||||
_GLFWXKBData xkb;
|
||||
_GLFWDBUSData dbus;
|
||||
|
||||
_GLFWwindow* pointerFocus;
|
||||
_GLFWwindow* keyboardFocus;
|
||||
GLFWid keyboardFocusId;
|
||||
|
||||
struct {
|
||||
void* handle;
|
||||
@@ -261,7 +307,6 @@ typedef struct _GLFWlibraryWayland
|
||||
size_t dataOffersCounter;
|
||||
_GLFWWaylandDataOffer dataOffers[8];
|
||||
char* primarySelectionString;
|
||||
GLFWWLCursorThemes cursor_themes;
|
||||
} _GLFWlibraryWayland;
|
||||
|
||||
// Wayland-specific per-monitor data
|
||||
@@ -275,7 +320,8 @@ typedef struct _GLFWmonitorWayland
|
||||
int x;
|
||||
int y;
|
||||
int scale;
|
||||
} _GLFWmonitorWayland;
|
||||
|
||||
} _GLFWmonitorWayland;
|
||||
|
||||
// Wayland-specific per-cursor data
|
||||
//
|
||||
|
||||
154
glfw/wl_text_input.c
vendored
Normal file
154
glfw/wl_text_input.c
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* wl_text_input.c
|
||||
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include "wl_text_input.h"
|
||||
#include "internal.h"
|
||||
#include "wayland-text-input-unstable-v3-client-protocol.h"
|
||||
#include <stdlib.h>
|
||||
#define debug(...) if (_glfw.hints.init.debugKeyboard) printf(__VA_ARGS__);
|
||||
|
||||
static struct zwp_text_input_v3* text_input;
|
||||
static struct zwp_text_input_manager_v3* text_input_manager;
|
||||
static char *pending_pre_edit = NULL;
|
||||
static char *pending_commit = NULL;
|
||||
uint32_t commit_serial = 0;
|
||||
|
||||
static void commit(void) {
|
||||
if (text_input) {
|
||||
zwp_text_input_v3_commit (text_input);
|
||||
commit_serial++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_enter(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, struct wl_surface *surface UNUSED) {
|
||||
debug("text-input: enter event\n");
|
||||
if (text_input) {
|
||||
zwp_text_input_v3_enable(text_input);
|
||||
zwp_text_input_v3_set_content_type(text_input, ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL);
|
||||
commit();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_leave(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, struct wl_surface *surface UNUSED) {
|
||||
debug("text-input: leave event\n");
|
||||
if (text_input) {
|
||||
zwp_text_input_v3_disable(text_input);
|
||||
commit();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
send_text(const char *text, GLFWIMEState ime_state) {
|
||||
_GLFWwindow *w = _glfwFocusedWindow();
|
||||
if (w && w->callbacks.keyboard) {
|
||||
GLFWkeyevent fake_ev = {.action = GLFW_PRESS};
|
||||
fake_ev.text = text;
|
||||
fake_ev.ime_state = ime_state;
|
||||
w->callbacks.keyboard((GLFWwindow*) w, &fake_ev);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_preedit_string(
|
||||
void *data UNUSED,
|
||||
struct zwp_text_input_v3 *text_input UNUSED,
|
||||
const char *text,
|
||||
int32_t cursor_begin,
|
||||
int32_t cursor_end
|
||||
) {
|
||||
debug("text-input: preedit_string event: text: %s cursor_begin: %d cursor_end: %d\n", text, cursor_begin, cursor_end);
|
||||
free(pending_pre_edit);
|
||||
pending_pre_edit = text ? _glfw_strdup(text) : NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_commit_string(void *data UNUSED, struct zwp_text_input_v3 *text_input UNUSED, const char *text) {
|
||||
debug("text-input: commit_string event: text: %s\n", text);
|
||||
free(pending_commit);
|
||||
pending_commit = text ? _glfw_strdup(text) : NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_delete_surrounding_text(
|
||||
void *data UNUSED,
|
||||
struct zwp_text_input_v3 *zwp_text_input_v3 UNUSED,
|
||||
uint32_t before_length,
|
||||
uint32_t after_length) {
|
||||
debug("text-input: delete_surrounding_text event: before_length: %u after_length: %u\n", before_length, after_length);
|
||||
}
|
||||
|
||||
static void
|
||||
text_input_done(void *data UNUSED, struct zwp_text_input_v3 *zwp_text_input_v3 UNUSED, uint32_t serial UNUSED) {
|
||||
debug("text-input: done event: serial: %u current_commit_serial: %u\n", serial, commit_serial);
|
||||
if (serial != commit_serial) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: text_input_done serial mismatch, expected=%u got=%u\n", commit_serial, serial);
|
||||
return;
|
||||
}
|
||||
if (pending_pre_edit) {
|
||||
send_text(pending_pre_edit, GLFW_IME_PREEDIT_CHANGED);
|
||||
free(pending_pre_edit); pending_pre_edit = NULL;
|
||||
}
|
||||
if (pending_commit) {
|
||||
send_text(pending_commit, GLFW_IME_COMMIT_TEXT);
|
||||
free(pending_commit); pending_commit = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_glfwWaylandBindTextInput(struct wl_registry* registry, uint32_t name) {
|
||||
if (!text_input_manager) text_input_manager = wl_registry_bind(registry, name, &zwp_text_input_manager_v3_interface, 1);
|
||||
}
|
||||
|
||||
void
|
||||
_glfwWaylandInitTextInput(void) {
|
||||
static const struct zwp_text_input_v3_listener text_input_listener = {
|
||||
.enter = text_input_enter,
|
||||
.leave = text_input_leave,
|
||||
.preedit_string = text_input_preedit_string,
|
||||
.commit_string = text_input_commit_string,
|
||||
.delete_surrounding_text = text_input_delete_surrounding_text,
|
||||
.done = text_input_done,
|
||||
};
|
||||
if (!text_input) {
|
||||
if (text_input_manager && _glfw.wl.seat) {
|
||||
text_input = zwp_text_input_manager_v3_get_text_input(
|
||||
text_input_manager, _glfw.wl.seat);
|
||||
if (text_input) zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_glfwWaylandDestroyTextInput(void) {
|
||||
if (text_input) zwp_text_input_v3_destroy(text_input);
|
||||
if (text_input_manager) zwp_text_input_manager_v3_destroy(text_input_manager);
|
||||
text_input = NULL; text_input_manager = NULL;
|
||||
free(pending_pre_edit); pending_pre_edit = NULL;
|
||||
free(pending_commit); pending_commit = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
_glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
|
||||
if (!text_input) return;
|
||||
switch(ev->type) {
|
||||
case GLFW_IME_UPDATE_FOCUS:
|
||||
debug("\ntext-input: updating IME focus state, focused: %d\n", ev->focused);
|
||||
if (ev->focused) zwp_text_input_v3_enable(text_input); else zwp_text_input_v3_disable(text_input);
|
||||
commit();
|
||||
break;
|
||||
case GLFW_IME_UPDATE_CURSOR_POSITION: {
|
||||
const int scale = w->wl.scale;
|
||||
const int left = ev->cursor.left / scale, top = ev->cursor.top / scale, width = ev->cursor.width / scale, height = ev->cursor.height / scale;
|
||||
debug("\ntext-input: updating cursor position: left=%d top=%d width=%d height=%d\n", left, top, width, height);
|
||||
zwp_text_input_v3_set_cursor_rectangle(text_input, left, top, width, height);
|
||||
commit();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
14
glfw/wl_text_input.h
vendored
Normal file
14
glfw/wl_text_input.h
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <wayland-client.h>
|
||||
|
||||
#define GLFW_WAYLAND_TEXT_INPUT_INTERFACE_NAME "zwp_text_input_manager_v3"
|
||||
|
||||
void _glfwWaylandBindTextInput(struct wl_registry* registry, uint32_t name);
|
||||
void _glfwWaylandInitTextInput(void);
|
||||
void _glfwWaylandDestroyTextInput(void);
|
||||
510
glfw/wl_window.c
vendored
510
glfw/wl_window.c
vendored
@@ -32,6 +32,7 @@
|
||||
#include "backend_utils.h"
|
||||
#include "memfd.h"
|
||||
#include "linux_notify.h"
|
||||
#include "wl_client_side_decorations.h"
|
||||
#include "../kitty/monotonic.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -41,10 +42,65 @@
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define debug(...) if (_glfw.hints.init.debugRendering) fprintf(stderr, __VA_ARGS__);
|
||||
|
||||
static struct wl_buffer* createShmBuffer(const GLFWimage* image, bool is_opaque, bool init_data)
|
||||
{
|
||||
struct wl_shm_pool* pool;
|
||||
struct wl_buffer* buffer;
|
||||
int stride = image->width * 4;
|
||||
int length = image->width * image->height * 4;
|
||||
void* data;
|
||||
int fd, i;
|
||||
|
||||
fd = createAnonymousFile(length);
|
||||
if (fd < 0)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Creating a buffer file for %d B failed: %s",
|
||||
length, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (data == MAP_FAILED)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: mmap failed: %s", strerror(errno));
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);
|
||||
|
||||
close(fd);
|
||||
if (init_data) {
|
||||
unsigned char* source = (unsigned char*) image->pixels;
|
||||
unsigned char* target = data;
|
||||
for (i = 0; i < image->width * image->height; i++, source += 4)
|
||||
{
|
||||
unsigned int alpha = source[3];
|
||||
|
||||
*target++ = (unsigned char) ((source[2] * alpha) / 255);
|
||||
*target++ = (unsigned char) ((source[1] * alpha) / 255);
|
||||
*target++ = (unsigned char) ((source[0] * alpha) / 255);
|
||||
*target++ = (unsigned char) alpha;
|
||||
}
|
||||
}
|
||||
|
||||
buffer =
|
||||
wl_shm_pool_create_buffer(pool, 0,
|
||||
image->width,
|
||||
image->height,
|
||||
stride, is_opaque ? WL_SHM_FORMAT_XRGB8888 : WL_SHM_FORMAT_ARGB8888);
|
||||
munmap(data, length);
|
||||
wl_shm_pool_destroy(pool);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void
|
||||
setCursorImage(_GLFWwindow* window)
|
||||
{
|
||||
setCursorImage(_GLFWwindow* window, bool on_theme_change) {
|
||||
_GLFWcursorWayland defaultCursor = {.shape = GLFW_ARROW_CURSOR};
|
||||
_GLFWcursorWayland* cursorWayland = window->cursor ? &window->cursor->wl : &defaultCursor;
|
||||
struct wl_cursor_image* image = NULL;
|
||||
@@ -55,9 +111,8 @@ setCursorImage(_GLFWwindow* window)
|
||||
if (cursorWayland->scale < 0) {
|
||||
buffer = cursorWayland->buffer;
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, 0);
|
||||
} else
|
||||
{
|
||||
if (cursorWayland->scale != scale) {
|
||||
} else {
|
||||
if (on_theme_change || cursorWayland->scale != scale) {
|
||||
struct wl_cursor *newCursor = NULL;
|
||||
struct wl_cursor_theme *theme = glfw_wlc_theme_for_scale(scale);
|
||||
if (theme) newCursor = _glfwLoadCursor(cursorWayland->shape, theme);
|
||||
@@ -132,7 +187,7 @@ static bool checkScaleChange(_GLFWwindow* window)
|
||||
{
|
||||
window->wl.scale = scale;
|
||||
wl_surface_set_buffer_scale(window->wl.surface, scale);
|
||||
setCursorImage(window);
|
||||
setCursorImage(window, false);
|
||||
return true;
|
||||
}
|
||||
if (window->wl.monitorsCount > 0 && !window->wl.initial_scale_notified) {
|
||||
@@ -158,42 +213,15 @@ static void setOpaqueRegion(_GLFWwindow* window)
|
||||
}
|
||||
|
||||
|
||||
static void resizeFramebuffer(_GLFWwindow* window)
|
||||
{
|
||||
static void
|
||||
resizeFramebuffer(_GLFWwindow* window) {
|
||||
int scale = window->wl.scale;
|
||||
int scaledWidth = window->wl.width * scale;
|
||||
int scaledHeight = window->wl.height * scale;
|
||||
debug("Resizing framebuffer to: %dx%d at scale: %d\n", window->wl.width, window->wl.height, scale);
|
||||
wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
|
||||
if (!window->wl.transparent)
|
||||
setOpaqueRegion(window);
|
||||
if (!window->wl.transparent) setOpaqueRegion(window);
|
||||
_glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
|
||||
|
||||
if (!window->wl.decorations.top.surface)
|
||||
return;
|
||||
|
||||
// Top decoration.
|
||||
wp_viewport_set_destination(window->wl.decorations.top.viewport,
|
||||
window->wl.width, _GLFW_DECORATION_TOP);
|
||||
wl_surface_commit(window->wl.decorations.top.surface);
|
||||
|
||||
// Left decoration.
|
||||
wp_viewport_set_destination(window->wl.decorations.left.viewport,
|
||||
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
|
||||
wl_surface_commit(window->wl.decorations.left.surface);
|
||||
|
||||
// Right decoration.
|
||||
wl_subsurface_set_position(window->wl.decorations.right.subsurface,
|
||||
window->wl.width, -_GLFW_DECORATION_TOP);
|
||||
wp_viewport_set_destination(window->wl.decorations.right.viewport,
|
||||
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
|
||||
wl_surface_commit(window->wl.decorations.right.surface);
|
||||
|
||||
// Bottom decoration.
|
||||
wl_subsurface_set_position(window->wl.decorations.bottom.subsurface,
|
||||
-_GLFW_DECORATION_WIDTH, window->wl.height);
|
||||
wp_viewport_set_destination(window->wl.decorations.bottom.viewport,
|
||||
window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH);
|
||||
wl_surface_commit(window->wl.decorations.bottom.surface);
|
||||
}
|
||||
|
||||
|
||||
@@ -206,15 +234,15 @@ clipboard_mime(void) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height) {
|
||||
if (width <= 0) width = window->wl.width;
|
||||
if (height <= 0) height = window->wl.height;
|
||||
static void
|
||||
dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height) {
|
||||
bool size_changed = width != window->wl.width || height != window->wl.height;
|
||||
bool scale_changed = checkScaleChange(window);
|
||||
|
||||
if (size_changed) {
|
||||
_glfwInputWindowSize(window, width, height);
|
||||
_glfwPlatformSetWindowSize(window, width, height);
|
||||
window->wl.width = width; window->wl.height = height;
|
||||
resizeFramebuffer(window);
|
||||
}
|
||||
|
||||
if (scale_changed) {
|
||||
@@ -227,221 +255,14 @@ static void dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, in
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static int
|
||||
createAnonymousFile(off_t size)
|
||||
{
|
||||
int ret, fd = -1, shm_anon = 0;
|
||||
#ifdef HAS_MEMFD_CREATE
|
||||
fd = memfd_create("glfw-shared", MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
||||
if (fd < 0) return -1;
|
||||
// We can add this seal before calling posix_fallocate(), as the file
|
||||
// is currently zero-sized anyway.
|
||||
//
|
||||
// There is also no need to check for the return value, we couldn’t do
|
||||
// anything with it anyway.
|
||||
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
|
||||
#elif defined(SHM_ANON)
|
||||
fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600);
|
||||
if (fd < 0) return -1;
|
||||
shm_anon = 1;
|
||||
#else
|
||||
static const char template[] = "/glfw-shared-XXXXXX";
|
||||
const char* path;
|
||||
char* name;
|
||||
|
||||
path = getenv("XDG_RUNTIME_DIR");
|
||||
if (!path)
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
name = calloc(strlen(path) + sizeof(template), 1);
|
||||
strcpy(name, path);
|
||||
strcat(name, template);
|
||||
|
||||
fd = createTmpfileCloexec(name);
|
||||
|
||||
free(name);
|
||||
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
#endif
|
||||
// posix_fallocate does not work on SHM descriptors
|
||||
ret = shm_anon ? ftruncate(fd, size) : posix_fallocate(fd, 0, size);
|
||||
if (ret != 0)
|
||||
{
|
||||
close(fd);
|
||||
errno = ret;
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static struct wl_buffer* createShmBuffer(const GLFWimage* image)
|
||||
{
|
||||
struct wl_shm_pool* pool;
|
||||
struct wl_buffer* buffer;
|
||||
int stride = image->width * 4;
|
||||
int length = image->width * image->height * 4;
|
||||
void* data;
|
||||
int fd, i;
|
||||
|
||||
fd = createAnonymousFile(length);
|
||||
if (fd < 0)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: Creating a buffer file for %d B failed: %s",
|
||||
length, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (data == MAP_FAILED)
|
||||
{
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR,
|
||||
"Wayland: mmap failed: %s", strerror(errno));
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pool = wl_shm_create_pool(_glfw.wl.shm, fd, length);
|
||||
|
||||
close(fd);
|
||||
unsigned char* source = (unsigned char*) image->pixels;
|
||||
unsigned char* target = data;
|
||||
for (i = 0; i < image->width * image->height; i++, source += 4)
|
||||
{
|
||||
unsigned int alpha = source[3];
|
||||
|
||||
*target++ = (unsigned char) ((source[2] * alpha) / 255);
|
||||
*target++ = (unsigned char) ((source[1] * alpha) / 255);
|
||||
*target++ = (unsigned char) ((source[0] * alpha) / 255);
|
||||
*target++ = (unsigned char) alpha;
|
||||
}
|
||||
|
||||
buffer =
|
||||
wl_shm_pool_create_buffer(pool, 0,
|
||||
image->width,
|
||||
image->height,
|
||||
stride, WL_SHM_FORMAT_ARGB8888);
|
||||
munmap(data, length);
|
||||
wl_shm_pool_destroy(pool);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void createDecoration(_GLFWdecorationWayland* decoration,
|
||||
struct wl_surface* parent,
|
||||
struct wl_buffer* buffer, bool opaque,
|
||||
int x, int y,
|
||||
int width, int height)
|
||||
{
|
||||
struct wl_region* region;
|
||||
|
||||
decoration->surface = wl_compositor_create_surface(_glfw.wl.compositor);
|
||||
decoration->subsurface =
|
||||
wl_subcompositor_get_subsurface(_glfw.wl.subcompositor,
|
||||
decoration->surface, parent);
|
||||
wl_subsurface_set_position(decoration->subsurface, x, y);
|
||||
decoration->viewport = wp_viewporter_get_viewport(_glfw.wl.viewporter,
|
||||
decoration->surface);
|
||||
wp_viewport_set_destination(decoration->viewport, width, height);
|
||||
wl_surface_attach(decoration->surface, buffer, 0, 0);
|
||||
|
||||
if (opaque)
|
||||
{
|
||||
region = wl_compositor_create_region(_glfw.wl.compositor);
|
||||
wl_region_add(region, 0, 0, width, height);
|
||||
wl_surface_set_opaque_region(decoration->surface, region);
|
||||
wl_surface_commit(decoration->surface);
|
||||
wl_region_destroy(region);
|
||||
}
|
||||
else
|
||||
wl_surface_commit(decoration->surface);
|
||||
}
|
||||
|
||||
static void createDecorations(_GLFWwindow* window)
|
||||
{
|
||||
unsigned char data[] = { 224, 224, 224, 255 };
|
||||
const GLFWimage image = { 1, 1, data };
|
||||
bool opaque = (data[3] == 255);
|
||||
|
||||
if (!_glfw.wl.viewporter || !window->decorated || window->wl.decorations.serverSide)
|
||||
return;
|
||||
|
||||
if (!window->wl.decorations.buffer)
|
||||
window->wl.decorations.buffer = createShmBuffer(&image);
|
||||
if (!window->wl.decorations.buffer)
|
||||
return;
|
||||
|
||||
createDecoration(&window->wl.decorations.top, window->wl.surface,
|
||||
window->wl.decorations.buffer, opaque,
|
||||
0, -_GLFW_DECORATION_TOP,
|
||||
window->wl.width, _GLFW_DECORATION_TOP);
|
||||
createDecoration(&window->wl.decorations.left, window->wl.surface,
|
||||
window->wl.decorations.buffer, opaque,
|
||||
-_GLFW_DECORATION_WIDTH, -_GLFW_DECORATION_TOP,
|
||||
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
|
||||
createDecoration(&window->wl.decorations.right, window->wl.surface,
|
||||
window->wl.decorations.buffer, opaque,
|
||||
window->wl.width, -_GLFW_DECORATION_TOP,
|
||||
_GLFW_DECORATION_WIDTH, window->wl.height + _GLFW_DECORATION_TOP);
|
||||
createDecoration(&window->wl.decorations.bottom, window->wl.surface,
|
||||
window->wl.decorations.buffer, opaque,
|
||||
-_GLFW_DECORATION_WIDTH, window->wl.height,
|
||||
window->wl.width + _GLFW_DECORATION_HORIZONTAL, _GLFW_DECORATION_WIDTH);
|
||||
}
|
||||
|
||||
static void destroyDecoration(_GLFWdecorationWayland* decoration)
|
||||
{
|
||||
if (decoration->surface)
|
||||
wl_surface_destroy(decoration->surface);
|
||||
if (decoration->subsurface)
|
||||
wl_subsurface_destroy(decoration->subsurface);
|
||||
if (decoration->viewport)
|
||||
wp_viewport_destroy(decoration->viewport);
|
||||
decoration->surface = NULL;
|
||||
decoration->subsurface = NULL;
|
||||
decoration->viewport = NULL;
|
||||
}
|
||||
|
||||
static void destroyDecorations(_GLFWwindow* window)
|
||||
{
|
||||
destroyDecoration(&window->wl.decorations.top);
|
||||
destroyDecoration(&window->wl.decorations.left);
|
||||
destroyDecoration(&window->wl.decorations.right);
|
||||
destroyDecoration(&window->wl.decorations.bottom);
|
||||
}
|
||||
|
||||
static void xdgDecorationHandleConfigure(void* data,
|
||||
struct zxdg_toplevel_decoration_v1* decoration UNUSED,
|
||||
uint32_t mode)
|
||||
{
|
||||
_GLFWwindow* window = data;
|
||||
|
||||
window->wl.decorations.serverSide = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
||||
if (!window->wl.decorations.serverSide)
|
||||
createDecorations(window);
|
||||
ensure_csd_resources(window);
|
||||
}
|
||||
|
||||
static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener = {
|
||||
@@ -468,6 +289,7 @@ static void surfaceHandleEnter(void *data,
|
||||
if (checkScaleChange(window)) {
|
||||
resizeFramebuffer(window);
|
||||
_glfwInputWindowContentScale(window, window->wl.scale, window->wl.scale);
|
||||
ensure_csd_resources(window);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,6 +314,7 @@ static void surfaceHandleLeave(void *data,
|
||||
if (checkScaleChange(window)) {
|
||||
resizeFramebuffer(window);
|
||||
_glfwInputWindowContentScale(window, window->wl.scale, window->wl.scale);
|
||||
ensure_csd_resources(window);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,6 +362,8 @@ static bool createSurface(_GLFWwindow* window,
|
||||
|
||||
window->wl.width = wndconfig->width;
|
||||
window->wl.height = wndconfig->height;
|
||||
window->wl.user_requested_content_size.width = wndconfig->width;
|
||||
window->wl.user_requested_content_size.height = wndconfig->height;
|
||||
window->wl.scale = 1;
|
||||
|
||||
if (!window->wl.transparent)
|
||||
@@ -547,8 +372,7 @@ static bool createSurface(_GLFWwindow* window,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, bool on)
|
||||
static void setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, bool on)
|
||||
{
|
||||
if (window->wl.xdg.toplevel)
|
||||
{
|
||||
@@ -556,12 +380,10 @@ setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, bool on)
|
||||
xdg_toplevel_set_fullscreen(
|
||||
window->wl.xdg.toplevel,
|
||||
monitor ? monitor->wl.output : NULL);
|
||||
if (!window->wl.decorations.serverSide)
|
||||
destroyDecorations(window);
|
||||
if (!window->wl.decorations.serverSide) free_csd_surfaces(window);
|
||||
} else {
|
||||
xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
|
||||
if (!_glfw.wl.decorationManager)
|
||||
createDecorations(window);
|
||||
ensure_csd_resources(window);
|
||||
}
|
||||
}
|
||||
setIdleInhibitor(window, on);
|
||||
@@ -569,7 +391,7 @@ setFullscreen(_GLFWwindow* window, _GLFWmonitor* monitor, bool on)
|
||||
|
||||
bool
|
||||
_glfwPlatformToggleFullscreen(_GLFWwindow *window, unsigned int flags UNUSED) {
|
||||
bool already_fullscreen = window->wl.fullscreened;
|
||||
bool already_fullscreen = window->wl.toplevel_states & TOPLEVEL_STATE_FULLSCREEN;
|
||||
setFullscreen(window, NULL, !already_fullscreen);
|
||||
return !already_fullscreen;
|
||||
}
|
||||
@@ -583,32 +405,33 @@ static void xdgToplevelHandleConfigure(void* data,
|
||||
_GLFWwindow* window = data;
|
||||
float aspectRatio;
|
||||
float targetRatio;
|
||||
uint32_t* state;
|
||||
bool maximized = false;
|
||||
bool fullscreen = false;
|
||||
bool activated = false;
|
||||
enum xdg_toplevel_state* state;
|
||||
uint32_t new_states = 0;
|
||||
debug("top-level configure event: size: %dx%d states: ", width, height);
|
||||
|
||||
wl_array_for_each(state, states)
|
||||
{
|
||||
switch (*state)
|
||||
{
|
||||
case XDG_TOPLEVEL_STATE_MAXIMIZED:
|
||||
maximized = true;
|
||||
break;
|
||||
case XDG_TOPLEVEL_STATE_FULLSCREEN:
|
||||
fullscreen = true;
|
||||
break;
|
||||
case XDG_TOPLEVEL_STATE_RESIZING:
|
||||
break;
|
||||
case XDG_TOPLEVEL_STATE_ACTIVATED:
|
||||
activated = true;
|
||||
break;
|
||||
wl_array_for_each(state, states) {
|
||||
switch (*state) {
|
||||
#define C(x) case XDG_##x: new_states |= x; debug("%s ", #x); break
|
||||
C(TOPLEVEL_STATE_RESIZING);
|
||||
C(TOPLEVEL_STATE_MAXIMIZED);
|
||||
C(TOPLEVEL_STATE_FULLSCREEN);
|
||||
C(TOPLEVEL_STATE_ACTIVATED);
|
||||
C(TOPLEVEL_STATE_TILED_LEFT);
|
||||
C(TOPLEVEL_STATE_TILED_RIGHT);
|
||||
C(TOPLEVEL_STATE_TILED_TOP);
|
||||
C(TOPLEVEL_STATE_TILED_BOTTOM);
|
||||
#undef C
|
||||
}
|
||||
}
|
||||
|
||||
debug("\n");
|
||||
if (new_states & TOPLEVEL_STATE_RESIZING) {
|
||||
if (width) window->wl.user_requested_content_size.width = width;
|
||||
if (height) window->wl.user_requested_content_size.height = height;
|
||||
if (!(window->wl.toplevel_states & TOPLEVEL_STATE_RESIZING)) _glfwInputLiveResize(window, true);
|
||||
}
|
||||
if (width != 0 && height != 0)
|
||||
{
|
||||
if (!maximized && !fullscreen)
|
||||
if (!(new_states & TOPLEVEL_STATE_DOCKED))
|
||||
{
|
||||
if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE)
|
||||
{
|
||||
@@ -621,15 +444,19 @@ static void xdgToplevelHandleConfigure(void* data,
|
||||
}
|
||||
}
|
||||
}
|
||||
window->wl.fullscreened = fullscreen;
|
||||
if (!fullscreen) {
|
||||
if (window->decorated && !window->wl.decorations.serverSide && window->wl.decorations.buffer) {
|
||||
width -= _GLFW_DECORATION_HORIZONTAL;
|
||||
height -= _GLFW_DECORATION_VERTICAL;
|
||||
}
|
||||
}
|
||||
bool live_resize_done = !(new_states & TOPLEVEL_STATE_RESIZING) && (window->wl.toplevel_states & TOPLEVEL_STATE_RESIZING);
|
||||
window->wl.toplevel_states = new_states;
|
||||
set_csd_window_geometry(window, &width, &height);
|
||||
dispatchChangesAfterConfigure(window, width, height);
|
||||
_glfwInputWindowFocus(window, activated);
|
||||
debug("final window content size: %dx%d\n", window->wl.width, window->wl.height);
|
||||
_glfwInputWindowFocus(window, window->wl.toplevel_states & TOPLEVEL_STATE_ACTIVATED);
|
||||
ensure_csd_resources(window);
|
||||
wl_surface_commit(window->wl.surface);
|
||||
#define geometry window->wl.decorations.geometry
|
||||
debug("Setting window geometry: x=%d y=%d %dx%d\n", geometry.x, geometry.y, geometry.width, geometry.height);
|
||||
xdg_surface_set_window_geometry(window->wl.xdg.surface, geometry.x, geometry.y, geometry.width, geometry.height);
|
||||
#undef geometry
|
||||
if (live_resize_done) _glfwInputLiveResize(window, false);
|
||||
}
|
||||
|
||||
static void xdgToplevelHandleClose(void* data,
|
||||
@@ -659,6 +486,7 @@ static void setXdgDecorations(_GLFWwindow* window)
|
||||
{
|
||||
if (_glfw.wl.decorationManager)
|
||||
{
|
||||
window->wl.decorations.serverSide = true;
|
||||
window->wl.xdg.decoration =
|
||||
zxdg_decoration_manager_v1_get_toplevel_decoration(
|
||||
_glfw.wl.decorationManager, window->wl.xdg.toplevel);
|
||||
@@ -672,7 +500,7 @@ static void setXdgDecorations(_GLFWwindow* window)
|
||||
else
|
||||
{
|
||||
window->wl.decorations.serverSide = false;
|
||||
createDecorations(window);
|
||||
ensure_csd_resources(window);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,8 +547,9 @@ static bool createXdgSurface(_GLFWwindow* window)
|
||||
window->monitor->wl.output);
|
||||
setIdleInhibitor(window, true);
|
||||
}
|
||||
else if (window->wl.maximized)
|
||||
else if (window->wl.maximize_on_first_show)
|
||||
{
|
||||
window->wl.maximize_on_first_show = false;
|
||||
xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
|
||||
setIdleInhibitor(window, false);
|
||||
setXdgDecorations(window);
|
||||
@@ -739,16 +568,15 @@ static bool createXdgSurface(_GLFWwindow* window)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
incrementCursorImage(_GLFWwindow* window)
|
||||
static void incrementCursorImage(_GLFWwindow* window)
|
||||
{
|
||||
if (window && window->wl.decorations.focus == mainWindow && window->cursorMode != GLFW_CURSOR_HIDDEN) {
|
||||
if (window && window->wl.decorations.focus == CENTRAL_WINDOW && window->cursorMode != GLFW_CURSOR_HIDDEN) {
|
||||
_GLFWcursor* cursor = window->wl.currentCursor;
|
||||
if (cursor && cursor->wl.cursor)
|
||||
{
|
||||
cursor->wl.currentImage += 1;
|
||||
cursor->wl.currentImage %= cursor->wl.cursor->image_count;
|
||||
setCursorImage(window);
|
||||
setCursorImage(window, false);
|
||||
toggleTimer(&_glfw.wl.eventLoopData, _glfw.wl.cursorAnimationTimer, cursor->wl.cursor->image_count > 1);
|
||||
return;
|
||||
}
|
||||
@@ -788,8 +616,7 @@ wayland_read_events(int poll_result, int events, void *data UNUSED) {
|
||||
else wl_display_cancel_read(_glfw.wl.display);
|
||||
}
|
||||
|
||||
static void
|
||||
handleEvents(monotonic_t timeout)
|
||||
static void handleEvents(monotonic_t timeout)
|
||||
{
|
||||
struct wl_display* display = _glfw.wl.display;
|
||||
errno = 0;
|
||||
@@ -886,6 +713,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
||||
const _GLFWctxconfig* ctxconfig,
|
||||
const _GLFWfbconfig* fbconfig)
|
||||
{
|
||||
initialize_csd_metrics(window);
|
||||
window->wl.transparent = fbconfig->transparent;
|
||||
strncpy(window->wl.appId, wndconfig->wl.appId, sizeof(window->wl.appId));
|
||||
|
||||
@@ -913,6 +741,8 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
|
||||
|
||||
if (wndconfig->title)
|
||||
window->wl.title = _glfw_strdup(wndconfig->title);
|
||||
if (wndconfig->maximized)
|
||||
window->wl.maximize_on_first_show = true;
|
||||
|
||||
if (wndconfig->visible)
|
||||
{
|
||||
@@ -945,11 +775,14 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
||||
_glfw.wl.pointerFocus = NULL;
|
||||
_glfwInputCursorEnter(window, false);
|
||||
}
|
||||
if (window == _glfw.wl.keyboardFocus)
|
||||
if (window->id == _glfw.wl.keyboardFocusId)
|
||||
{
|
||||
_glfw.wl.keyboardFocus = NULL;
|
||||
_glfw.wl.keyboardFocusId = 0;
|
||||
_glfwInputWindowFocus(window, false);
|
||||
}
|
||||
if (window->id == _glfw.wl.keyRepeatInfo.keyboardFocusId) {
|
||||
_glfw.wl.keyRepeatInfo.keyboardFocusId = 0;
|
||||
}
|
||||
|
||||
if (window->wl.idleInhibitor)
|
||||
zwp_idle_inhibitor_v1_destroy(window->wl.idleInhibitor);
|
||||
@@ -957,14 +790,10 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
||||
if (window->context.destroy)
|
||||
window->context.destroy(window);
|
||||
|
||||
destroyDecorations(window);
|
||||
|
||||
free_all_csd_resources(window);
|
||||
if (window->wl.xdg.decoration)
|
||||
zxdg_toplevel_decoration_v1_destroy(window->wl.xdg.decoration);
|
||||
|
||||
if (window->wl.decorations.buffer)
|
||||
wl_buffer_destroy(window->wl.decorations.buffer);
|
||||
|
||||
if (window->wl.native)
|
||||
wl_egl_window_destroy(window->wl.native);
|
||||
|
||||
@@ -985,14 +814,16 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
|
||||
|
||||
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
|
||||
{
|
||||
if (window->wl.title)
|
||||
if (window->wl.title) {
|
||||
if (title && strcmp(title, window->wl.title) == 0) return;
|
||||
free(window->wl.title);
|
||||
} else if (!title) return;
|
||||
// Wayland cannot handle requests larger than ~8200 bytes. Sending
|
||||
// one causes an abort(). Since titles this large are meaningless anyway
|
||||
// ensure they do not happen.
|
||||
window->wl.title = utf_8_strndup(title, 2048);
|
||||
if (window->wl.xdg.toplevel)
|
||||
xdg_toplevel_set_title(window->wl.xdg.toplevel, window->wl.title);
|
||||
if (window->wl.xdg.toplevel) xdg_toplevel_set_title(window->wl.xdg.toplevel, window->wl.title);
|
||||
change_csd_title(window);
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowIcon(_GLFWwindow* window UNUSED,
|
||||
@@ -1006,9 +837,12 @@ void _glfwPlatformGetWindowPos(_GLFWwindow* window UNUSED, int* xpos UNUSED, int
|
||||
{
|
||||
// A Wayland client is not aware of its position, so just warn and leave it
|
||||
// as (0, 0)
|
||||
|
||||
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||
"Wayland: The platform does not provide the window position");
|
||||
static bool warned_once = false;
|
||||
if (!warned_once) {
|
||||
_glfwInputError(GLFW_FEATURE_UNAVAILABLE,
|
||||
"Wayland: The platform does not provide the window position");
|
||||
warned_once = true;
|
||||
}
|
||||
}
|
||||
|
||||
void _glfwPlatformSetWindowPos(_GLFWwindow* window UNUSED, int xpos UNUSED, int ypos UNUSED)
|
||||
@@ -1030,9 +864,14 @@ void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height)
|
||||
void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height)
|
||||
{
|
||||
if (width != window->wl.width || height != window->wl.height) {
|
||||
window->wl.width = width;
|
||||
window->wl.height = height;
|
||||
window->wl.user_requested_content_size.width = width;
|
||||
window->wl.user_requested_content_size.height = height;
|
||||
int32_t width = 0, height = 0;
|
||||
set_csd_window_geometry(window, &width, &height);
|
||||
window->wl.width = width; window->wl.height = height;
|
||||
resizeFramebuffer(window);
|
||||
ensure_csd_resources(window);
|
||||
wl_surface_commit(window->wl.surface);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1085,13 +924,13 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
|
||||
if (window->decorated && !window->monitor && !window->wl.decorations.serverSide)
|
||||
{
|
||||
if (top)
|
||||
*top = _GLFW_DECORATION_TOP;
|
||||
*top = window->wl.decorations.metrics.top;
|
||||
if (left)
|
||||
*left = _GLFW_DECORATION_WIDTH;
|
||||
*left = window->wl.decorations.metrics.width;
|
||||
if (right)
|
||||
*right = _GLFW_DECORATION_WIDTH;
|
||||
*right = window->wl.decorations.metrics.width;
|
||||
if (bottom)
|
||||
*bottom = _GLFW_DECORATION_WIDTH;
|
||||
*bottom = window->wl.decorations.metrics.width;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1121,13 +960,12 @@ void _glfwPlatformRestoreWindow(_GLFWwindow* window)
|
||||
{
|
||||
if (window->monitor)
|
||||
xdg_toplevel_unset_fullscreen(window->wl.xdg.toplevel);
|
||||
if (window->wl.maximized)
|
||||
if (window->wl.toplevel_states & TOPLEVEL_STATE_MAXIMIZED)
|
||||
xdg_toplevel_unset_maximized(window->wl.xdg.toplevel);
|
||||
// There is no way to unset minimized, or even to know if we are
|
||||
// minimized, so there is nothing to do in this case.
|
||||
}
|
||||
_glfwInputWindowMonitor(window, NULL);
|
||||
window->wl.maximized = false;
|
||||
}
|
||||
|
||||
void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
|
||||
@@ -1136,7 +974,6 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
|
||||
{
|
||||
xdg_toplevel_set_maximized(window->wl.xdg.toplevel);
|
||||
}
|
||||
window->wl.maximized = true;
|
||||
}
|
||||
|
||||
void _glfwPlatformShowWindow(_GLFWwindow* window)
|
||||
@@ -1202,7 +1039,7 @@ void _glfwPlatformSetWindowMonitor(_GLFWwindow* window,
|
||||
|
||||
int _glfwPlatformWindowFocused(_GLFWwindow* window)
|
||||
{
|
||||
return _glfw.wl.keyboardFocus == window;
|
||||
return _glfw.wl.keyboardFocusId = window ? window->id : 0;
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowOccluded(_GLFWwindow* window UNUSED)
|
||||
@@ -1224,7 +1061,7 @@ int _glfwPlatformWindowVisible(_GLFWwindow* window)
|
||||
|
||||
int _glfwPlatformWindowMaximized(_GLFWwindow* window)
|
||||
{
|
||||
return window->wl.maximized;
|
||||
return window->wl.toplevel_states & TOPLEVEL_STATE_MAXIMIZED;
|
||||
}
|
||||
|
||||
int _glfwPlatformWindowHovered(_GLFWwindow* window)
|
||||
@@ -1249,9 +1086,9 @@ void _glfwPlatformSetWindowDecorated(_GLFWwindow* window, bool enabled)
|
||||
if (!window->monitor)
|
||||
{
|
||||
if (enabled)
|
||||
createDecorations(window);
|
||||
ensure_csd_resources(window);
|
||||
else
|
||||
destroyDecorations(window);
|
||||
free_csd_surfaces(window);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1350,7 +1187,7 @@ const char* _glfwPlatformGetNativeKeyName(int native_key)
|
||||
return glfw_xkb_keysym_name(native_key);
|
||||
}
|
||||
|
||||
int _glfwPlatformGetNativeKeyForKey(int key)
|
||||
int _glfwPlatformGetNativeKeyForKey(uint32_t key)
|
||||
{
|
||||
return glfw_xkb_sym_for_key(key);
|
||||
}
|
||||
@@ -1359,9 +1196,10 @@ int _glfwPlatformCreateCursor(_GLFWcursor* cursor,
|
||||
const GLFWimage* image,
|
||||
int xhot, int yhot, int count UNUSED)
|
||||
{
|
||||
cursor->wl.buffer = createShmBuffer(image);
|
||||
cursor->wl.buffer = createShmBuffer(image, false, true);
|
||||
if (!cursor->wl.buffer)
|
||||
return false;
|
||||
|
||||
cursor->wl.width = image->width;
|
||||
cursor->wl.height = image->height;
|
||||
cursor->wl.xhot = xhot;
|
||||
@@ -1509,7 +1347,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
|
||||
|
||||
// If we're not in the correct window just save the cursor
|
||||
// the next time the pointer enters the window the cursor will change
|
||||
if (window != _glfw.wl.pointerFocus || window->wl.decorations.focus != mainWindow)
|
||||
if (window != _glfw.wl.pointerFocus || window->wl.decorations.focus != CENTRAL_WINDOW)
|
||||
return;
|
||||
|
||||
// Unlock possible pointer lock if no longer disabled.
|
||||
@@ -1518,7 +1356,7 @@ void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor)
|
||||
|
||||
if (window->cursorMode == GLFW_CURSOR_NORMAL)
|
||||
{
|
||||
setCursorImage(window);
|
||||
setCursorImage(window, false);
|
||||
}
|
||||
else if (window->cursorMode == GLFW_CURSOR_DISABLED)
|
||||
{
|
||||
@@ -2101,11 +1939,6 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
return err;
|
||||
}
|
||||
|
||||
void
|
||||
_glfwPlatformUpdateIMEState(_GLFWwindow *w, int which, int a, int b, int c, int d) {
|
||||
glfw_xkb_update_ime_state(w, &_glfw.wl.xkb, which, a, b, c, d);
|
||||
}
|
||||
|
||||
static void
|
||||
frame_handle_redraw(void *data, struct wl_callback *callback, uint32_t time UNUSED) {
|
||||
_GLFWwindow* window = (_GLFWwindow*) data;
|
||||
@@ -2116,6 +1949,16 @@ frame_handle_redraw(void *data, struct wl_callback *callback, uint32_t time UNUS
|
||||
wl_callback_destroy(callback);
|
||||
}
|
||||
|
||||
void
|
||||
_glfwPlatformChangeCursorTheme(void) {
|
||||
glfw_wlc_destroy();
|
||||
_GLFWwindow *w = _glfw.windowListHead;
|
||||
while (w) {
|
||||
setCursorImage(w, true);
|
||||
w = w->next;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
////// GLFW native API //////
|
||||
@@ -2158,3 +2001,12 @@ GLFWAPI unsigned long long glfwDBusUserNotify(const char *app_name, const char*
|
||||
GLFWAPI void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler) {
|
||||
glfw_dbus_set_user_notification_activated_handler(handler);
|
||||
}
|
||||
|
||||
GLFWAPI bool glfwWaylandSetTitlebarColor(GLFWwindow *handle, uint32_t color, bool use_system_color) {
|
||||
_GLFWwindow* window = (_GLFWwindow*) handle;
|
||||
if (!window->wl.decorations.serverSide) {
|
||||
set_titlebar_color(window, color, use_system_color);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
32
glfw/x11_window.c
vendored
32
glfw/x11_window.c
vendored
@@ -185,6 +185,7 @@ static int translateState(int state)
|
||||
{
|
||||
int mods = 0;
|
||||
|
||||
/* Need some way to expose hyper and meta without xkbcommon-x11 */
|
||||
if (state & ShiftMask)
|
||||
mods |= GLFW_MOD_SHIFT;
|
||||
if (state & ControlMask)
|
||||
@@ -224,7 +225,8 @@ static void sendEventToWM(_GLFWwindow* window, Atom type,
|
||||
|
||||
// Updates the normal hints according to the window settings
|
||||
//
|
||||
static void updateNormalHints(_GLFWwindow* window, int width, int height)
|
||||
static void
|
||||
updateNormalHints(_GLFWwindow* window, int width, int height)
|
||||
{
|
||||
XSizeHints* hints = XAllocSizeHints();
|
||||
|
||||
@@ -257,7 +259,7 @@ static void updateNormalHints(_GLFWwindow* window, int width, int height)
|
||||
}
|
||||
|
||||
if (window->widthincr != GLFW_DONT_CARE &&
|
||||
window->heightincr != GLFW_DONT_CARE)
|
||||
window->heightincr != GLFW_DONT_CARE && !window->x11.maximized)
|
||||
{
|
||||
hints->flags |= PResizeInc;
|
||||
hints->width_inc = window->widthincr;
|
||||
@@ -423,14 +425,16 @@ static char* convertLatin1toUTF8(const char* source)
|
||||
const char* sp;
|
||||
|
||||
if (source) {
|
||||
for (sp = source; *sp; sp++) size += (*sp & 0x80) ? 2 : 1;
|
||||
for (sp = source; *sp; sp++)
|
||||
size += (*sp & 0x80) ? 2 : 1;
|
||||
}
|
||||
|
||||
char* target = calloc(size, 1);
|
||||
char* tp = target;
|
||||
|
||||
if (source) {
|
||||
for (sp = source; *sp; sp++) tp += encodeUTF8(tp, *sp);
|
||||
for (sp = source; *sp; sp++)
|
||||
tp += encodeUTF8(tp, *sp);
|
||||
}
|
||||
|
||||
return target;
|
||||
@@ -1741,6 +1745,9 @@ static void processEvent(XEvent *event)
|
||||
if (window->x11.maximized != maximized)
|
||||
{
|
||||
window->x11.maximized = maximized;
|
||||
int width, height;
|
||||
_glfwPlatformGetWindowSize(window, &width, &height);
|
||||
updateNormalHints(window, width, height);
|
||||
_glfwInputWindowMaximize(window, maximized);
|
||||
}
|
||||
}
|
||||
@@ -1988,8 +1995,8 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
|
||||
for (i = 0; i < count; i++)
|
||||
longCount += 2 + images[i].width * images[i].height;
|
||||
|
||||
long* icon = calloc(longCount, sizeof(long));
|
||||
long* target = icon;
|
||||
unsigned long* icon = calloc(longCount, sizeof(unsigned long));
|
||||
unsigned long* target = icon;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
@@ -1998,10 +2005,9 @@ void _glfwPlatformSetWindowIcon(_GLFWwindow* window,
|
||||
|
||||
for (j = 0; j < images[i].width * images[i].height; j++)
|
||||
{
|
||||
*target++ = (images[i].pixels[j * 4 + 0] << 16) |
|
||||
(images[i].pixels[j * 4 + 1] << 8) |
|
||||
(images[i].pixels[j * 4 + 2] << 0) |
|
||||
(images[i].pixels[j * 4 + 3] << 24);
|
||||
unsigned char *p = images->pixels + j * 4;
|
||||
const unsigned char r = *p++, g = *p++, b = *p++, a = *p++;
|
||||
*target++ = a << 24 | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2776,7 +2782,7 @@ const char* _glfwPlatformGetNativeKeyName(int native_key)
|
||||
return glfw_xkb_keysym_name(native_key);
|
||||
}
|
||||
|
||||
int _glfwPlatformGetNativeKeyForKey(int key)
|
||||
int _glfwPlatformGetNativeKeyForKey(uint32_t key)
|
||||
{
|
||||
return glfw_xkb_sym_for_key(key);
|
||||
}
|
||||
@@ -3084,8 +3090,8 @@ VkResult _glfwPlatformCreateWindowSurface(VkInstance instance,
|
||||
}
|
||||
|
||||
void
|
||||
_glfwPlatformUpdateIMEState(_GLFWwindow *w, int which, int a, int b, int c, int d) {
|
||||
glfw_xkb_update_ime_state(w, &_glfw.x11.xkb, which, a, b, c, d);
|
||||
_glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
|
||||
glfw_xkb_update_ime_state(w, &_glfw.x11.xkb, ev);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
838
glfw/xkb-compat-shim.h
vendored
Normal file
838
glfw/xkb-compat-shim.h
vendored
Normal file
@@ -0,0 +1,838 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
// needed for xkbcommon < 1.0
|
||||
|
||||
|
||||
/* We don't use the uint32_t types here, to save some space. */
|
||||
struct codepair {
|
||||
uint16_t keysym;
|
||||
uint16_t ucs;
|
||||
};
|
||||
|
||||
static const struct codepair keysymtab[] = {
|
||||
{ 0x01a1, 0x0104 }, /* Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */
|
||||
{ 0x01a2, 0x02d8 }, /* breve ˘ BREVE */
|
||||
{ 0x01a3, 0x0141 }, /* Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */
|
||||
{ 0x01a5, 0x013d }, /* Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */
|
||||
{ 0x01a6, 0x015a }, /* Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */
|
||||
{ 0x01a9, 0x0160 }, /* Scaron Š LATIN CAPITAL LETTER S WITH CARON */
|
||||
{ 0x01aa, 0x015e }, /* Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */
|
||||
{ 0x01ab, 0x0164 }, /* Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */
|
||||
{ 0x01ac, 0x0179 }, /* Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */
|
||||
{ 0x01ae, 0x017d }, /* Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */
|
||||
{ 0x01af, 0x017b }, /* Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */
|
||||
{ 0x01b1, 0x0105 }, /* aogonek ą LATIN SMALL LETTER A WITH OGONEK */
|
||||
{ 0x01b2, 0x02db }, /* ogonek ˛ OGONEK */
|
||||
{ 0x01b3, 0x0142 }, /* lstroke ł LATIN SMALL LETTER L WITH STROKE */
|
||||
{ 0x01b5, 0x013e }, /* lcaron ľ LATIN SMALL LETTER L WITH CARON */
|
||||
{ 0x01b6, 0x015b }, /* sacute ś LATIN SMALL LETTER S WITH ACUTE */
|
||||
{ 0x01b7, 0x02c7 }, /* caron ˇ CARON */
|
||||
{ 0x01b9, 0x0161 }, /* scaron š LATIN SMALL LETTER S WITH CARON */
|
||||
{ 0x01ba, 0x015f }, /* scedilla ş LATIN SMALL LETTER S WITH CEDILLA */
|
||||
{ 0x01bb, 0x0165 }, /* tcaron ť LATIN SMALL LETTER T WITH CARON */
|
||||
{ 0x01bc, 0x017a }, /* zacute ź LATIN SMALL LETTER Z WITH ACUTE */
|
||||
{ 0x01bd, 0x02dd }, /* doubleacute ˝ DOUBLE ACUTE ACCENT */
|
||||
{ 0x01be, 0x017e }, /* zcaron ž LATIN SMALL LETTER Z WITH CARON */
|
||||
{ 0x01bf, 0x017c }, /* zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */
|
||||
{ 0x01c0, 0x0154 }, /* Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */
|
||||
{ 0x01c3, 0x0102 }, /* Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */
|
||||
{ 0x01c5, 0x0139 }, /* Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */
|
||||
{ 0x01c6, 0x0106 }, /* Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */
|
||||
{ 0x01c8, 0x010c }, /* Ccaron Č LATIN CAPITAL LETTER C WITH CARON */
|
||||
{ 0x01ca, 0x0118 }, /* Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */
|
||||
{ 0x01cc, 0x011a }, /* Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */
|
||||
{ 0x01cf, 0x010e }, /* Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */
|
||||
{ 0x01d0, 0x0110 }, /* Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */
|
||||
{ 0x01d1, 0x0143 }, /* Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */
|
||||
{ 0x01d2, 0x0147 }, /* Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */
|
||||
{ 0x01d5, 0x0150 }, /* Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */
|
||||
{ 0x01d8, 0x0158 }, /* Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */
|
||||
{ 0x01d9, 0x016e }, /* Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */
|
||||
{ 0x01db, 0x0170 }, /* Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */
|
||||
{ 0x01de, 0x0162 }, /* Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */
|
||||
{ 0x01e0, 0x0155 }, /* racute ŕ LATIN SMALL LETTER R WITH ACUTE */
|
||||
{ 0x01e3, 0x0103 }, /* abreve ă LATIN SMALL LETTER A WITH BREVE */
|
||||
{ 0x01e5, 0x013a }, /* lacute ĺ LATIN SMALL LETTER L WITH ACUTE */
|
||||
{ 0x01e6, 0x0107 }, /* cacute ć LATIN SMALL LETTER C WITH ACUTE */
|
||||
{ 0x01e8, 0x010d }, /* ccaron č LATIN SMALL LETTER C WITH CARON */
|
||||
{ 0x01ea, 0x0119 }, /* eogonek ę LATIN SMALL LETTER E WITH OGONEK */
|
||||
{ 0x01ec, 0x011b }, /* ecaron ě LATIN SMALL LETTER E WITH CARON */
|
||||
{ 0x01ef, 0x010f }, /* dcaron ď LATIN SMALL LETTER D WITH CARON */
|
||||
{ 0x01f0, 0x0111 }, /* dstroke đ LATIN SMALL LETTER D WITH STROKE */
|
||||
{ 0x01f1, 0x0144 }, /* nacute ń LATIN SMALL LETTER N WITH ACUTE */
|
||||
{ 0x01f2, 0x0148 }, /* ncaron ň LATIN SMALL LETTER N WITH CARON */
|
||||
{ 0x01f5, 0x0151 }, /* odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */
|
||||
{ 0x01f8, 0x0159 }, /* rcaron ř LATIN SMALL LETTER R WITH CARON */
|
||||
{ 0x01f9, 0x016f }, /* uring ů LATIN SMALL LETTER U WITH RING ABOVE */
|
||||
{ 0x01fb, 0x0171 }, /* udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */
|
||||
{ 0x01fe, 0x0163 }, /* tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */
|
||||
{ 0x01ff, 0x02d9 }, /* abovedot ˙ DOT ABOVE */
|
||||
{ 0x02a1, 0x0126 }, /* Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */
|
||||
{ 0x02a6, 0x0124 }, /* Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */
|
||||
{ 0x02a9, 0x0130 }, /* Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */
|
||||
{ 0x02ab, 0x011e }, /* Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */
|
||||
{ 0x02ac, 0x0134 }, /* Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */
|
||||
{ 0x02b1, 0x0127 }, /* hstroke ħ LATIN SMALL LETTER H WITH STROKE */
|
||||
{ 0x02b6, 0x0125 }, /* hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */
|
||||
{ 0x02b9, 0x0131 }, /* idotless ı LATIN SMALL LETTER DOTLESS I */
|
||||
{ 0x02bb, 0x011f }, /* gbreve ğ LATIN SMALL LETTER G WITH BREVE */
|
||||
{ 0x02bc, 0x0135 }, /* jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */
|
||||
{ 0x02c5, 0x010a }, /* Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */
|
||||
{ 0x02c6, 0x0108 }, /* Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */
|
||||
{ 0x02d5, 0x0120 }, /* Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */
|
||||
{ 0x02d8, 0x011c }, /* Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */
|
||||
{ 0x02dd, 0x016c }, /* Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */
|
||||
{ 0x02de, 0x015c }, /* Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */
|
||||
{ 0x02e5, 0x010b }, /* cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */
|
||||
{ 0x02e6, 0x0109 }, /* ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */
|
||||
{ 0x02f5, 0x0121 }, /* gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */
|
||||
{ 0x02f8, 0x011d }, /* gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */
|
||||
{ 0x02fd, 0x016d }, /* ubreve ŭ LATIN SMALL LETTER U WITH BREVE */
|
||||
{ 0x02fe, 0x015d }, /* scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */
|
||||
{ 0x03a2, 0x0138 }, /* kra ĸ LATIN SMALL LETTER KRA */
|
||||
{ 0x03a3, 0x0156 }, /* Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */
|
||||
{ 0x03a5, 0x0128 }, /* Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */
|
||||
{ 0x03a6, 0x013b }, /* Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */
|
||||
{ 0x03aa, 0x0112 }, /* Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */
|
||||
{ 0x03ab, 0x0122 }, /* Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */
|
||||
{ 0x03ac, 0x0166 }, /* Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */
|
||||
{ 0x03b3, 0x0157 }, /* rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */
|
||||
{ 0x03b5, 0x0129 }, /* itilde ĩ LATIN SMALL LETTER I WITH TILDE */
|
||||
{ 0x03b6, 0x013c }, /* lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */
|
||||
{ 0x03ba, 0x0113 }, /* emacron ē LATIN SMALL LETTER E WITH MACRON */
|
||||
{ 0x03bb, 0x0123 }, /* gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */
|
||||
{ 0x03bc, 0x0167 }, /* tslash ŧ LATIN SMALL LETTER T WITH STROKE */
|
||||
{ 0x03bd, 0x014a }, /* ENG Ŋ LATIN CAPITAL LETTER ENG */
|
||||
{ 0x03bf, 0x014b }, /* eng ŋ LATIN SMALL LETTER ENG */
|
||||
{ 0x03c0, 0x0100 }, /* Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */
|
||||
{ 0x03c7, 0x012e }, /* Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */
|
||||
{ 0x03cc, 0x0116 }, /* Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */
|
||||
{ 0x03cf, 0x012a }, /* Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */
|
||||
{ 0x03d1, 0x0145 }, /* Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */
|
||||
{ 0x03d2, 0x014c }, /* Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */
|
||||
{ 0x03d3, 0x0136 }, /* Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */
|
||||
{ 0x03d9, 0x0172 }, /* Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */
|
||||
{ 0x03dd, 0x0168 }, /* Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */
|
||||
{ 0x03de, 0x016a }, /* Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */
|
||||
{ 0x03e0, 0x0101 }, /* amacron ā LATIN SMALL LETTER A WITH MACRON */
|
||||
{ 0x03e7, 0x012f }, /* iogonek į LATIN SMALL LETTER I WITH OGONEK */
|
||||
{ 0x03ec, 0x0117 }, /* eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */
|
||||
{ 0x03ef, 0x012b }, /* imacron ī LATIN SMALL LETTER I WITH MACRON */
|
||||
{ 0x03f1, 0x0146 }, /* ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */
|
||||
{ 0x03f2, 0x014d }, /* omacron ō LATIN SMALL LETTER O WITH MACRON */
|
||||
{ 0x03f3, 0x0137 }, /* kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */
|
||||
{ 0x03f9, 0x0173 }, /* uogonek ų LATIN SMALL LETTER U WITH OGONEK */
|
||||
{ 0x03fd, 0x0169 }, /* utilde ũ LATIN SMALL LETTER U WITH TILDE */
|
||||
{ 0x03fe, 0x016b }, /* umacron ū LATIN SMALL LETTER U WITH MACRON */
|
||||
{ 0x047e, 0x203e }, /* overline ‾ OVERLINE */
|
||||
{ 0x04a1, 0x3002 }, /* kana_fullstop 。 IDEOGRAPHIC FULL STOP */
|
||||
{ 0x04a2, 0x300c }, /* kana_openingbracket 「 LEFT CORNER BRACKET */
|
||||
{ 0x04a3, 0x300d }, /* kana_closingbracket 」 RIGHT CORNER BRACKET */
|
||||
{ 0x04a4, 0x3001 }, /* kana_comma 、 IDEOGRAPHIC COMMA */
|
||||
{ 0x04a5, 0x30fb }, /* kana_conjunctive ・ KATAKANA MIDDLE DOT */
|
||||
{ 0x04a6, 0x30f2 }, /* kana_WO ヲ KATAKANA LETTER WO */
|
||||
{ 0x04a7, 0x30a1 }, /* kana_a ァ KATAKANA LETTER SMALL A */
|
||||
{ 0x04a8, 0x30a3 }, /* kana_i ィ KATAKANA LETTER SMALL I */
|
||||
{ 0x04a9, 0x30a5 }, /* kana_u ゥ KATAKANA LETTER SMALL U */
|
||||
{ 0x04aa, 0x30a7 }, /* kana_e ェ KATAKANA LETTER SMALL E */
|
||||
{ 0x04ab, 0x30a9 }, /* kana_o ォ KATAKANA LETTER SMALL O */
|
||||
{ 0x04ac, 0x30e3 }, /* kana_ya ャ KATAKANA LETTER SMALL YA */
|
||||
{ 0x04ad, 0x30e5 }, /* kana_yu ュ KATAKANA LETTER SMALL YU */
|
||||
{ 0x04ae, 0x30e7 }, /* kana_yo ョ KATAKANA LETTER SMALL YO */
|
||||
{ 0x04af, 0x30c3 }, /* kana_tsu ッ KATAKANA LETTER SMALL TU */
|
||||
{ 0x04b0, 0x30fc }, /* prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */
|
||||
{ 0x04b1, 0x30a2 }, /* kana_A ア KATAKANA LETTER A */
|
||||
{ 0x04b2, 0x30a4 }, /* kana_I イ KATAKANA LETTER I */
|
||||
{ 0x04b3, 0x30a6 }, /* kana_U ウ KATAKANA LETTER U */
|
||||
{ 0x04b4, 0x30a8 }, /* kana_E エ KATAKANA LETTER E */
|
||||
{ 0x04b5, 0x30aa }, /* kana_O オ KATAKANA LETTER O */
|
||||
{ 0x04b6, 0x30ab }, /* kana_KA カ KATAKANA LETTER KA */
|
||||
{ 0x04b7, 0x30ad }, /* kana_KI キ KATAKANA LETTER KI */
|
||||
{ 0x04b8, 0x30af }, /* kana_KU ク KATAKANA LETTER KU */
|
||||
{ 0x04b9, 0x30b1 }, /* kana_KE ケ KATAKANA LETTER KE */
|
||||
{ 0x04ba, 0x30b3 }, /* kana_KO コ KATAKANA LETTER KO */
|
||||
{ 0x04bb, 0x30b5 }, /* kana_SA サ KATAKANA LETTER SA */
|
||||
{ 0x04bc, 0x30b7 }, /* kana_SHI シ KATAKANA LETTER SI */
|
||||
{ 0x04bd, 0x30b9 }, /* kana_SU ス KATAKANA LETTER SU */
|
||||
{ 0x04be, 0x30bb }, /* kana_SE セ KATAKANA LETTER SE */
|
||||
{ 0x04bf, 0x30bd }, /* kana_SO ソ KATAKANA LETTER SO */
|
||||
{ 0x04c0, 0x30bf }, /* kana_TA タ KATAKANA LETTER TA */
|
||||
{ 0x04c1, 0x30c1 }, /* kana_CHI チ KATAKANA LETTER TI */
|
||||
{ 0x04c2, 0x30c4 }, /* kana_TSU ツ KATAKANA LETTER TU */
|
||||
{ 0x04c3, 0x30c6 }, /* kana_TE テ KATAKANA LETTER TE */
|
||||
{ 0x04c4, 0x30c8 }, /* kana_TO ト KATAKANA LETTER TO */
|
||||
{ 0x04c5, 0x30ca }, /* kana_NA ナ KATAKANA LETTER NA */
|
||||
{ 0x04c6, 0x30cb }, /* kana_NI ニ KATAKANA LETTER NI */
|
||||
{ 0x04c7, 0x30cc }, /* kana_NU ヌ KATAKANA LETTER NU */
|
||||
{ 0x04c8, 0x30cd }, /* kana_NE ネ KATAKANA LETTER NE */
|
||||
{ 0x04c9, 0x30ce }, /* kana_NO ノ KATAKANA LETTER NO */
|
||||
{ 0x04ca, 0x30cf }, /* kana_HA ハ KATAKANA LETTER HA */
|
||||
{ 0x04cb, 0x30d2 }, /* kana_HI ヒ KATAKANA LETTER HI */
|
||||
{ 0x04cc, 0x30d5 }, /* kana_FU フ KATAKANA LETTER HU */
|
||||
{ 0x04cd, 0x30d8 }, /* kana_HE ヘ KATAKANA LETTER HE */
|
||||
{ 0x04ce, 0x30db }, /* kana_HO ホ KATAKANA LETTER HO */
|
||||
{ 0x04cf, 0x30de }, /* kana_MA マ KATAKANA LETTER MA */
|
||||
{ 0x04d0, 0x30df }, /* kana_MI ミ KATAKANA LETTER MI */
|
||||
{ 0x04d1, 0x30e0 }, /* kana_MU ム KATAKANA LETTER MU */
|
||||
{ 0x04d2, 0x30e1 }, /* kana_ME メ KATAKANA LETTER ME */
|
||||
{ 0x04d3, 0x30e2 }, /* kana_MO モ KATAKANA LETTER MO */
|
||||
{ 0x04d4, 0x30e4 }, /* kana_YA ヤ KATAKANA LETTER YA */
|
||||
{ 0x04d5, 0x30e6 }, /* kana_YU ユ KATAKANA LETTER YU */
|
||||
{ 0x04d6, 0x30e8 }, /* kana_YO ヨ KATAKANA LETTER YO */
|
||||
{ 0x04d7, 0x30e9 }, /* kana_RA ラ KATAKANA LETTER RA */
|
||||
{ 0x04d8, 0x30ea }, /* kana_RI リ KATAKANA LETTER RI */
|
||||
{ 0x04d9, 0x30eb }, /* kana_RU ル KATAKANA LETTER RU */
|
||||
{ 0x04da, 0x30ec }, /* kana_RE レ KATAKANA LETTER RE */
|
||||
{ 0x04db, 0x30ed }, /* kana_RO ロ KATAKANA LETTER RO */
|
||||
{ 0x04dc, 0x30ef }, /* kana_WA ワ KATAKANA LETTER WA */
|
||||
{ 0x04dd, 0x30f3 }, /* kana_N ン KATAKANA LETTER N */
|
||||
{ 0x04de, 0x309b }, /* voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */
|
||||
{ 0x04df, 0x309c }, /* semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */
|
||||
{ 0x05ac, 0x060c }, /* Arabic_comma ، ARABIC COMMA */
|
||||
{ 0x05bb, 0x061b }, /* Arabic_semicolon ؛ ARABIC SEMICOLON */
|
||||
{ 0x05bf, 0x061f }, /* Arabic_question_mark ؟ ARABIC QUESTION MARK */
|
||||
{ 0x05c1, 0x0621 }, /* Arabic_hamza ء ARABIC LETTER HAMZA */
|
||||
{ 0x05c2, 0x0622 }, /* Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */
|
||||
{ 0x05c3, 0x0623 }, /* Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */
|
||||
{ 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */
|
||||
{ 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */
|
||||
{ 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */
|
||||
{ 0x05c7, 0x0627 }, /* Arabic_alef ا ARABIC LETTER ALEF */
|
||||
{ 0x05c8, 0x0628 }, /* Arabic_beh ب ARABIC LETTER BEH */
|
||||
{ 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */
|
||||
{ 0x05ca, 0x062a }, /* Arabic_teh ت ARABIC LETTER TEH */
|
||||
{ 0x05cb, 0x062b }, /* Arabic_theh ث ARABIC LETTER THEH */
|
||||
{ 0x05cc, 0x062c }, /* Arabic_jeem ج ARABIC LETTER JEEM */
|
||||
{ 0x05cd, 0x062d }, /* Arabic_hah ح ARABIC LETTER HAH */
|
||||
{ 0x05ce, 0x062e }, /* Arabic_khah خ ARABIC LETTER KHAH */
|
||||
{ 0x05cf, 0x062f }, /* Arabic_dal د ARABIC LETTER DAL */
|
||||
{ 0x05d0, 0x0630 }, /* Arabic_thal ذ ARABIC LETTER THAL */
|
||||
{ 0x05d1, 0x0631 }, /* Arabic_ra ر ARABIC LETTER REH */
|
||||
{ 0x05d2, 0x0632 }, /* Arabic_zain ز ARABIC LETTER ZAIN */
|
||||
{ 0x05d3, 0x0633 }, /* Arabic_seen س ARABIC LETTER SEEN */
|
||||
{ 0x05d4, 0x0634 }, /* Arabic_sheen ش ARABIC LETTER SHEEN */
|
||||
{ 0x05d5, 0x0635 }, /* Arabic_sad ص ARABIC LETTER SAD */
|
||||
{ 0x05d6, 0x0636 }, /* Arabic_dad ض ARABIC LETTER DAD */
|
||||
{ 0x05d7, 0x0637 }, /* Arabic_tah ط ARABIC LETTER TAH */
|
||||
{ 0x05d8, 0x0638 }, /* Arabic_zah ظ ARABIC LETTER ZAH */
|
||||
{ 0x05d9, 0x0639 }, /* Arabic_ain ع ARABIC LETTER AIN */
|
||||
{ 0x05da, 0x063a }, /* Arabic_ghain غ ARABIC LETTER GHAIN */
|
||||
{ 0x05e0, 0x0640 }, /* Arabic_tatweel ـ ARABIC TATWEEL */
|
||||
{ 0x05e1, 0x0641 }, /* Arabic_feh ف ARABIC LETTER FEH */
|
||||
{ 0x05e2, 0x0642 }, /* Arabic_qaf ق ARABIC LETTER QAF */
|
||||
{ 0x05e3, 0x0643 }, /* Arabic_kaf ك ARABIC LETTER KAF */
|
||||
{ 0x05e4, 0x0644 }, /* Arabic_lam ل ARABIC LETTER LAM */
|
||||
{ 0x05e5, 0x0645 }, /* Arabic_meem م ARABIC LETTER MEEM */
|
||||
{ 0x05e6, 0x0646 }, /* Arabic_noon ن ARABIC LETTER NOON */
|
||||
{ 0x05e7, 0x0647 }, /* Arabic_ha ه ARABIC LETTER HEH */
|
||||
{ 0x05e8, 0x0648 }, /* Arabic_waw و ARABIC LETTER WAW */
|
||||
{ 0x05e9, 0x0649 }, /* Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */
|
||||
{ 0x05ea, 0x064a }, /* Arabic_yeh ي ARABIC LETTER YEH */
|
||||
{ 0x05eb, 0x064b }, /* Arabic_fathatan ً ARABIC FATHATAN */
|
||||
{ 0x05ec, 0x064c }, /* Arabic_dammatan ٌ ARABIC DAMMATAN */
|
||||
{ 0x05ed, 0x064d }, /* Arabic_kasratan ٍ ARABIC KASRATAN */
|
||||
{ 0x05ee, 0x064e }, /* Arabic_fatha َ ARABIC FATHA */
|
||||
{ 0x05ef, 0x064f }, /* Arabic_damma ُ ARABIC DAMMA */
|
||||
{ 0x05f0, 0x0650 }, /* Arabic_kasra ِ ARABIC KASRA */
|
||||
{ 0x05f1, 0x0651 }, /* Arabic_shadda ّ ARABIC SHADDA */
|
||||
{ 0x05f2, 0x0652 }, /* Arabic_sukun ْ ARABIC SUKUN */
|
||||
{ 0x06a1, 0x0452 }, /* Serbian_dje ђ CYRILLIC SMALL LETTER DJE */
|
||||
{ 0x06a2, 0x0453 }, /* Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */
|
||||
{ 0x06a3, 0x0451 }, /* Cyrillic_io ё CYRILLIC SMALL LETTER IO */
|
||||
{ 0x06a4, 0x0454 }, /* Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */
|
||||
{ 0x06a5, 0x0455 }, /* Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */
|
||||
{ 0x06a6, 0x0456 }, /* Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */
|
||||
{ 0x06a7, 0x0457 }, /* Ukrainian_yi ї CYRILLIC SMALL LETTER YI */
|
||||
{ 0x06a8, 0x0458 }, /* Cyrillic_je ј CYRILLIC SMALL LETTER JE */
|
||||
{ 0x06a9, 0x0459 }, /* Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */
|
||||
{ 0x06aa, 0x045a }, /* Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */
|
||||
{ 0x06ab, 0x045b }, /* Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */
|
||||
{ 0x06ac, 0x045c }, /* Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */
|
||||
{ 0x06ad, 0x0491 }, /* Ukrainian_ghe_with_upturn ґ CYRILLIC SMALL LETTER GHE WITH UPTURN */
|
||||
{ 0x06ae, 0x045e }, /* Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */
|
||||
{ 0x06af, 0x045f }, /* Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */
|
||||
{ 0x06b0, 0x2116 }, /* numerosign № NUMERO SIGN */
|
||||
{ 0x06b1, 0x0402 }, /* Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */
|
||||
{ 0x06b2, 0x0403 }, /* Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */
|
||||
{ 0x06b3, 0x0401 }, /* Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */
|
||||
{ 0x06b4, 0x0404 }, /* Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */
|
||||
{ 0x06b5, 0x0405 }, /* Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */
|
||||
{ 0x06b6, 0x0406 }, /* Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */
|
||||
{ 0x06b7, 0x0407 }, /* Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */
|
||||
{ 0x06b8, 0x0408 }, /* Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */
|
||||
{ 0x06b9, 0x0409 }, /* Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */
|
||||
{ 0x06ba, 0x040a }, /* Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */
|
||||
{ 0x06bb, 0x040b }, /* Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */
|
||||
{ 0x06bc, 0x040c }, /* Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */
|
||||
{ 0x06bd, 0x0490 }, /* Ukrainian_GHE_WITH_UPTURN Ґ CYRILLIC CAPITAL LETTER GHE WITH UPTURN */
|
||||
{ 0x06be, 0x040e }, /* Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */
|
||||
{ 0x06bf, 0x040f }, /* Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */
|
||||
{ 0x06c0, 0x044e }, /* Cyrillic_yu ю CYRILLIC SMALL LETTER YU */
|
||||
{ 0x06c1, 0x0430 }, /* Cyrillic_a а CYRILLIC SMALL LETTER A */
|
||||
{ 0x06c2, 0x0431 }, /* Cyrillic_be б CYRILLIC SMALL LETTER BE */
|
||||
{ 0x06c3, 0x0446 }, /* Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */
|
||||
{ 0x06c4, 0x0434 }, /* Cyrillic_de д CYRILLIC SMALL LETTER DE */
|
||||
{ 0x06c5, 0x0435 }, /* Cyrillic_ie е CYRILLIC SMALL LETTER IE */
|
||||
{ 0x06c6, 0x0444 }, /* Cyrillic_ef ф CYRILLIC SMALL LETTER EF */
|
||||
{ 0x06c7, 0x0433 }, /* Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */
|
||||
{ 0x06c8, 0x0445 }, /* Cyrillic_ha х CYRILLIC SMALL LETTER HA */
|
||||
{ 0x06c9, 0x0438 }, /* Cyrillic_i и CYRILLIC SMALL LETTER I */
|
||||
{ 0x06ca, 0x0439 }, /* Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */
|
||||
{ 0x06cb, 0x043a }, /* Cyrillic_ka к CYRILLIC SMALL LETTER KA */
|
||||
{ 0x06cc, 0x043b }, /* Cyrillic_el л CYRILLIC SMALL LETTER EL */
|
||||
{ 0x06cd, 0x043c }, /* Cyrillic_em м CYRILLIC SMALL LETTER EM */
|
||||
{ 0x06ce, 0x043d }, /* Cyrillic_en н CYRILLIC SMALL LETTER EN */
|
||||
{ 0x06cf, 0x043e }, /* Cyrillic_o о CYRILLIC SMALL LETTER O */
|
||||
{ 0x06d0, 0x043f }, /* Cyrillic_pe п CYRILLIC SMALL LETTER PE */
|
||||
{ 0x06d1, 0x044f }, /* Cyrillic_ya я CYRILLIC SMALL LETTER YA */
|
||||
{ 0x06d2, 0x0440 }, /* Cyrillic_er р CYRILLIC SMALL LETTER ER */
|
||||
{ 0x06d3, 0x0441 }, /* Cyrillic_es с CYRILLIC SMALL LETTER ES */
|
||||
{ 0x06d4, 0x0442 }, /* Cyrillic_te т CYRILLIC SMALL LETTER TE */
|
||||
{ 0x06d5, 0x0443 }, /* Cyrillic_u у CYRILLIC SMALL LETTER U */
|
||||
{ 0x06d6, 0x0436 }, /* Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */
|
||||
{ 0x06d7, 0x0432 }, /* Cyrillic_ve в CYRILLIC SMALL LETTER VE */
|
||||
{ 0x06d8, 0x044c }, /* Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */
|
||||
{ 0x06d9, 0x044b }, /* Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */
|
||||
{ 0x06da, 0x0437 }, /* Cyrillic_ze з CYRILLIC SMALL LETTER ZE */
|
||||
{ 0x06db, 0x0448 }, /* Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */
|
||||
{ 0x06dc, 0x044d }, /* Cyrillic_e э CYRILLIC SMALL LETTER E */
|
||||
{ 0x06dd, 0x0449 }, /* Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */
|
||||
{ 0x06de, 0x0447 }, /* Cyrillic_che ч CYRILLIC SMALL LETTER CHE */
|
||||
{ 0x06df, 0x044a }, /* Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */
|
||||
{ 0x06e0, 0x042e }, /* Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */
|
||||
{ 0x06e1, 0x0410 }, /* Cyrillic_A А CYRILLIC CAPITAL LETTER A */
|
||||
{ 0x06e2, 0x0411 }, /* Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */
|
||||
{ 0x06e3, 0x0426 }, /* Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */
|
||||
{ 0x06e4, 0x0414 }, /* Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */
|
||||
{ 0x06e5, 0x0415 }, /* Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */
|
||||
{ 0x06e6, 0x0424 }, /* Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */
|
||||
{ 0x06e7, 0x0413 }, /* Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */
|
||||
{ 0x06e8, 0x0425 }, /* Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */
|
||||
{ 0x06e9, 0x0418 }, /* Cyrillic_I И CYRILLIC CAPITAL LETTER I */
|
||||
{ 0x06ea, 0x0419 }, /* Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */
|
||||
{ 0x06eb, 0x041a }, /* Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */
|
||||
{ 0x06ec, 0x041b }, /* Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */
|
||||
{ 0x06ed, 0x041c }, /* Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */
|
||||
{ 0x06ee, 0x041d }, /* Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */
|
||||
{ 0x06ef, 0x041e }, /* Cyrillic_O О CYRILLIC CAPITAL LETTER O */
|
||||
{ 0x06f0, 0x041f }, /* Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */
|
||||
{ 0x06f1, 0x042f }, /* Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */
|
||||
{ 0x06f2, 0x0420 }, /* Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */
|
||||
{ 0x06f3, 0x0421 }, /* Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */
|
||||
{ 0x06f4, 0x0422 }, /* Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */
|
||||
{ 0x06f5, 0x0423 }, /* Cyrillic_U У CYRILLIC CAPITAL LETTER U */
|
||||
{ 0x06f6, 0x0416 }, /* Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */
|
||||
{ 0x06f7, 0x0412 }, /* Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */
|
||||
{ 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */
|
||||
{ 0x06f9, 0x042b }, /* Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */
|
||||
{ 0x06fa, 0x0417 }, /* Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */
|
||||
{ 0x06fb, 0x0428 }, /* Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */
|
||||
{ 0x06fc, 0x042d }, /* Cyrillic_E Э CYRILLIC CAPITAL LETTER E */
|
||||
{ 0x06fd, 0x0429 }, /* Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */
|
||||
{ 0x06fe, 0x0427 }, /* Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */
|
||||
{ 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */
|
||||
{ 0x07a1, 0x0386 }, /* Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */
|
||||
{ 0x07a2, 0x0388 }, /* Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */
|
||||
{ 0x07a3, 0x0389 }, /* Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */
|
||||
{ 0x07a4, 0x038a }, /* Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */
|
||||
{ 0x07a5, 0x03aa }, /* Greek_IOTAdiaeresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */
|
||||
{ 0x07a7, 0x038c }, /* Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */
|
||||
{ 0x07a8, 0x038e }, /* Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */
|
||||
{ 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */
|
||||
{ 0x07ab, 0x038f }, /* Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */
|
||||
{ 0x07ae, 0x0385 }, /* Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */
|
||||
{ 0x07af, 0x2015 }, /* Greek_horizbar ― HORIZONTAL BAR */
|
||||
{ 0x07b1, 0x03ac }, /* Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */
|
||||
{ 0x07b2, 0x03ad }, /* Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */
|
||||
{ 0x07b3, 0x03ae }, /* Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */
|
||||
{ 0x07b4, 0x03af }, /* Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */
|
||||
{ 0x07b5, 0x03ca }, /* Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */
|
||||
{ 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */
|
||||
{ 0x07b7, 0x03cc }, /* Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */
|
||||
{ 0x07b8, 0x03cd }, /* Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */
|
||||
{ 0x07b9, 0x03cb }, /* Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */
|
||||
{ 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */
|
||||
{ 0x07bb, 0x03ce }, /* Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */
|
||||
{ 0x07c1, 0x0391 }, /* Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */
|
||||
{ 0x07c2, 0x0392 }, /* Greek_BETA Β GREEK CAPITAL LETTER BETA */
|
||||
{ 0x07c3, 0x0393 }, /* Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */
|
||||
{ 0x07c4, 0x0394 }, /* Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */
|
||||
{ 0x07c5, 0x0395 }, /* Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */
|
||||
{ 0x07c6, 0x0396 }, /* Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */
|
||||
{ 0x07c7, 0x0397 }, /* Greek_ETA Η GREEK CAPITAL LETTER ETA */
|
||||
{ 0x07c8, 0x0398 }, /* Greek_THETA Θ GREEK CAPITAL LETTER THETA */
|
||||
{ 0x07c9, 0x0399 }, /* Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */
|
||||
{ 0x07ca, 0x039a }, /* Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */
|
||||
{ 0x07cb, 0x039b }, /* Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMDA */
|
||||
{ 0x07cc, 0x039c }, /* Greek_MU Μ GREEK CAPITAL LETTER MU */
|
||||
{ 0x07cd, 0x039d }, /* Greek_NU Ν GREEK CAPITAL LETTER NU */
|
||||
{ 0x07ce, 0x039e }, /* Greek_XI Ξ GREEK CAPITAL LETTER XI */
|
||||
{ 0x07cf, 0x039f }, /* Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */
|
||||
{ 0x07d0, 0x03a0 }, /* Greek_PI Π GREEK CAPITAL LETTER PI */
|
||||
{ 0x07d1, 0x03a1 }, /* Greek_RHO Ρ GREEK CAPITAL LETTER RHO */
|
||||
{ 0x07d2, 0x03a3 }, /* Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */
|
||||
{ 0x07d4, 0x03a4 }, /* Greek_TAU Τ GREEK CAPITAL LETTER TAU */
|
||||
{ 0x07d5, 0x03a5 }, /* Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */
|
||||
{ 0x07d6, 0x03a6 }, /* Greek_PHI Φ GREEK CAPITAL LETTER PHI */
|
||||
{ 0x07d7, 0x03a7 }, /* Greek_CHI Χ GREEK CAPITAL LETTER CHI */
|
||||
{ 0x07d8, 0x03a8 }, /* Greek_PSI Ψ GREEK CAPITAL LETTER PSI */
|
||||
{ 0x07d9, 0x03a9 }, /* Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */
|
||||
{ 0x07e1, 0x03b1 }, /* Greek_alpha α GREEK SMALL LETTER ALPHA */
|
||||
{ 0x07e2, 0x03b2 }, /* Greek_beta β GREEK SMALL LETTER BETA */
|
||||
{ 0x07e3, 0x03b3 }, /* Greek_gamma γ GREEK SMALL LETTER GAMMA */
|
||||
{ 0x07e4, 0x03b4 }, /* Greek_delta δ GREEK SMALL LETTER DELTA */
|
||||
{ 0x07e5, 0x03b5 }, /* Greek_epsilon ε GREEK SMALL LETTER EPSILON */
|
||||
{ 0x07e6, 0x03b6 }, /* Greek_zeta ζ GREEK SMALL LETTER ZETA */
|
||||
{ 0x07e7, 0x03b7 }, /* Greek_eta η GREEK SMALL LETTER ETA */
|
||||
{ 0x07e8, 0x03b8 }, /* Greek_theta θ GREEK SMALL LETTER THETA */
|
||||
{ 0x07e9, 0x03b9 }, /* Greek_iota ι GREEK SMALL LETTER IOTA */
|
||||
{ 0x07ea, 0x03ba }, /* Greek_kappa κ GREEK SMALL LETTER KAPPA */
|
||||
{ 0x07eb, 0x03bb }, /* Greek_lambda λ GREEK SMALL LETTER LAMDA */
|
||||
{ 0x07ec, 0x03bc }, /* Greek_mu μ GREEK SMALL LETTER MU */
|
||||
{ 0x07ed, 0x03bd }, /* Greek_nu ν GREEK SMALL LETTER NU */
|
||||
{ 0x07ee, 0x03be }, /* Greek_xi ξ GREEK SMALL LETTER XI */
|
||||
{ 0x07ef, 0x03bf }, /* Greek_omicron ο GREEK SMALL LETTER OMICRON */
|
||||
{ 0x07f0, 0x03c0 }, /* Greek_pi π GREEK SMALL LETTER PI */
|
||||
{ 0x07f1, 0x03c1 }, /* Greek_rho ρ GREEK SMALL LETTER RHO */
|
||||
{ 0x07f2, 0x03c3 }, /* Greek_sigma σ GREEK SMALL LETTER SIGMA */
|
||||
{ 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */
|
||||
{ 0x07f4, 0x03c4 }, /* Greek_tau τ GREEK SMALL LETTER TAU */
|
||||
{ 0x07f5, 0x03c5 }, /* Greek_upsilon υ GREEK SMALL LETTER UPSILON */
|
||||
{ 0x07f6, 0x03c6 }, /* Greek_phi φ GREEK SMALL LETTER PHI */
|
||||
{ 0x07f7, 0x03c7 }, /* Greek_chi χ GREEK SMALL LETTER CHI */
|
||||
{ 0x07f8, 0x03c8 }, /* Greek_psi ψ GREEK SMALL LETTER PSI */
|
||||
{ 0x07f9, 0x03c9 }, /* Greek_omega ω GREEK SMALL LETTER OMEGA */
|
||||
{ 0x08a1, 0x23b7 }, /* leftradical ⎷ ??? */
|
||||
{ 0x08a2, 0x250c }, /* topleftradical ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */
|
||||
{ 0x08a3, 0x2500 }, /* horizconnector ─ BOX DRAWINGS LIGHT HORIZONTAL */
|
||||
{ 0x08a4, 0x2320 }, /* topintegral ⌠ TOP HALF INTEGRAL */
|
||||
{ 0x08a5, 0x2321 }, /* botintegral ⌡ BOTTOM HALF INTEGRAL */
|
||||
{ 0x08a6, 0x2502 }, /* vertconnector │ BOX DRAWINGS LIGHT VERTICAL */
|
||||
{ 0x08a7, 0x23a1 }, /* topleftsqbracket ⎡ ??? */
|
||||
{ 0x08a8, 0x23a3 }, /* botleftsqbracket ⎣ ??? */
|
||||
{ 0x08a9, 0x23a4 }, /* toprightsqbracket ⎤ ??? */
|
||||
{ 0x08aa, 0x23a6 }, /* botrightsqbracket ⎦ ??? */
|
||||
{ 0x08ab, 0x239b }, /* topleftparens ⎛ ??? */
|
||||
{ 0x08ac, 0x239d }, /* botleftparens ⎝ ??? */
|
||||
{ 0x08ad, 0x239e }, /* toprightparens ⎞ ??? */
|
||||
{ 0x08ae, 0x23a0 }, /* botrightparens ⎠ ??? */
|
||||
{ 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ⎨ ??? */
|
||||
{ 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ⎬ ??? */
|
||||
/* 0x08b1 topleftsummation ? ??? */
|
||||
/* 0x08b2 botleftsummation ? ??? */
|
||||
/* 0x08b3 topvertsummationconnector ? ??? */
|
||||
/* 0x08b4 botvertsummationconnector ? ??? */
|
||||
/* 0x08b5 toprightsummation ? ??? */
|
||||
/* 0x08b6 botrightsummation ? ??? */
|
||||
/* 0x08b7 rightmiddlesummation ? ??? */
|
||||
{ 0x08bc, 0x2264 }, /* lessthanequal ≤ LESS-THAN OR EQUAL TO */
|
||||
{ 0x08bd, 0x2260 }, /* notequal ≠ NOT EQUAL TO */
|
||||
{ 0x08be, 0x2265 }, /* greaterthanequal ≥ GREATER-THAN OR EQUAL TO */
|
||||
{ 0x08bf, 0x222b }, /* integral ∫ INTEGRAL */
|
||||
{ 0x08c0, 0x2234 }, /* therefore ∴ THEREFORE */
|
||||
{ 0x08c1, 0x221d }, /* variation ∝ PROPORTIONAL TO */
|
||||
{ 0x08c2, 0x221e }, /* infinity ∞ INFINITY */
|
||||
{ 0x08c5, 0x2207 }, /* nabla ∇ NABLA */
|
||||
{ 0x08c8, 0x223c }, /* approximate ∼ TILDE OPERATOR */
|
||||
{ 0x08c9, 0x2243 }, /* similarequal ≃ ASYMPTOTICALLY EQUAL TO */
|
||||
{ 0x08cd, 0x21d4 }, /* ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */
|
||||
{ 0x08ce, 0x21d2 }, /* implies ⇒ RIGHTWARDS DOUBLE ARROW */
|
||||
{ 0x08cf, 0x2261 }, /* identical ≡ IDENTICAL TO */
|
||||
{ 0x08d6, 0x221a }, /* radical √ SQUARE ROOT */
|
||||
{ 0x08da, 0x2282 }, /* includedin ⊂ SUBSET OF */
|
||||
{ 0x08db, 0x2283 }, /* includes ⊃ SUPERSET OF */
|
||||
{ 0x08dc, 0x2229 }, /* intersection ∩ INTERSECTION */
|
||||
{ 0x08dd, 0x222a }, /* union ∪ UNION */
|
||||
{ 0x08de, 0x2227 }, /* logicaland ∧ LOGICAL AND */
|
||||
{ 0x08df, 0x2228 }, /* logicalor ∨ LOGICAL OR */
|
||||
{ 0x08ef, 0x2202 }, /* partialderivative ∂ PARTIAL DIFFERENTIAL */
|
||||
{ 0x08f6, 0x0192 }, /* function ƒ LATIN SMALL LETTER F WITH HOOK */
|
||||
{ 0x08fb, 0x2190 }, /* leftarrow ← LEFTWARDS ARROW */
|
||||
{ 0x08fc, 0x2191 }, /* uparrow ↑ UPWARDS ARROW */
|
||||
{ 0x08fd, 0x2192 }, /* rightarrow → RIGHTWARDS ARROW */
|
||||
{ 0x08fe, 0x2193 }, /* downarrow ↓ DOWNWARDS ARROW */
|
||||
/* 0x09df blank ? ??? */
|
||||
{ 0x09e0, 0x25c6 }, /* soliddiamond ◆ BLACK DIAMOND */
|
||||
{ 0x09e1, 0x2592 }, /* checkerboard ▒ MEDIUM SHADE */
|
||||
{ 0x09e2, 0x2409 }, /* ht ␉ SYMBOL FOR HORIZONTAL TABULATION */
|
||||
{ 0x09e3, 0x240c }, /* ff ␌ SYMBOL FOR FORM FEED */
|
||||
{ 0x09e4, 0x240d }, /* cr ␍ SYMBOL FOR CARRIAGE RETURN */
|
||||
{ 0x09e5, 0x240a }, /* lf ␊ SYMBOL FOR LINE FEED */
|
||||
{ 0x09e8, 0x2424 }, /* nl  SYMBOL FOR NEWLINE */
|
||||
{ 0x09e9, 0x240b }, /* vt ␋ SYMBOL FOR VERTICAL TABULATION */
|
||||
{ 0x09ea, 0x2518 }, /* lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */
|
||||
{ 0x09eb, 0x2510 }, /* uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */
|
||||
{ 0x09ec, 0x250c }, /* upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */
|
||||
{ 0x09ed, 0x2514 }, /* lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */
|
||||
{ 0x09ee, 0x253c }, /* crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
|
||||
{ 0x09ef, 0x23ba }, /* horizlinescan1 ⎺ HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */
|
||||
{ 0x09f0, 0x23bb }, /* horizlinescan3 ⎻ HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */
|
||||
{ 0x09f1, 0x2500 }, /* horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */
|
||||
{ 0x09f2, 0x23bc }, /* horizlinescan7 ⎼ HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */
|
||||
{ 0x09f3, 0x23bd }, /* horizlinescan9 ⎽ HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */
|
||||
{ 0x09f4, 0x251c }, /* leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
|
||||
{ 0x09f5, 0x2524 }, /* rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */
|
||||
{ 0x09f6, 0x2534 }, /* bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */
|
||||
{ 0x09f7, 0x252c }, /* topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
|
||||
{ 0x09f8, 0x2502 }, /* vertbar │ BOX DRAWINGS LIGHT VERTICAL */
|
||||
{ 0x0aa1, 0x2003 }, /* emspace EM SPACE */
|
||||
{ 0x0aa2, 0x2002 }, /* enspace EN SPACE */
|
||||
{ 0x0aa3, 0x2004 }, /* em3space THREE-PER-EM SPACE */
|
||||
{ 0x0aa4, 0x2005 }, /* em4space FOUR-PER-EM SPACE */
|
||||
{ 0x0aa5, 0x2007 }, /* digitspace FIGURE SPACE */
|
||||
{ 0x0aa6, 0x2008 }, /* punctspace PUNCTUATION SPACE */
|
||||
{ 0x0aa7, 0x2009 }, /* thinspace THIN SPACE */
|
||||
{ 0x0aa8, 0x200a }, /* hairspace HAIR SPACE */
|
||||
{ 0x0aa9, 0x2014 }, /* emdash — EM DASH */
|
||||
{ 0x0aaa, 0x2013 }, /* endash – EN DASH */
|
||||
{ 0x0aac, 0x2423 }, /* signifblank ␣ OPEN BOX */
|
||||
{ 0x0aae, 0x2026 }, /* ellipsis … HORIZONTAL ELLIPSIS */
|
||||
{ 0x0aaf, 0x2025 }, /* doubbaselinedot ‥ TWO DOT LEADER */
|
||||
{ 0x0ab0, 0x2153 }, /* onethird ⅓ VULGAR FRACTION ONE THIRD */
|
||||
{ 0x0ab1, 0x2154 }, /* twothirds ⅔ VULGAR FRACTION TWO THIRDS */
|
||||
{ 0x0ab2, 0x2155 }, /* onefifth ⅕ VULGAR FRACTION ONE FIFTH */
|
||||
{ 0x0ab3, 0x2156 }, /* twofifths ⅖ VULGAR FRACTION TWO FIFTHS */
|
||||
{ 0x0ab4, 0x2157 }, /* threefifths ⅗ VULGAR FRACTION THREE FIFTHS */
|
||||
{ 0x0ab5, 0x2158 }, /* fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */
|
||||
{ 0x0ab6, 0x2159 }, /* onesixth ⅙ VULGAR FRACTION ONE SIXTH */
|
||||
{ 0x0ab7, 0x215a }, /* fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */
|
||||
{ 0x0ab8, 0x2105 }, /* careof ℅ CARE OF */
|
||||
{ 0x0abb, 0x2012 }, /* figdash ‒ FIGURE DASH */
|
||||
{ 0x0abc, 0x27e8 }, /* leftanglebracket ⟨ MATHEMATICAL LEFT ANGLE BRACKET */
|
||||
{ 0x0abd, 0x002e }, /* decimalpoint . FULL STOP */
|
||||
{ 0x0abe, 0x27e9 }, /* rightanglebracket ⟩ MATHEMATICAL RIGHT ANGLE BRACKET */
|
||||
/* 0x0abf marker ? ??? */
|
||||
{ 0x0ac3, 0x215b }, /* oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */
|
||||
{ 0x0ac4, 0x215c }, /* threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */
|
||||
{ 0x0ac5, 0x215d }, /* fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */
|
||||
{ 0x0ac6, 0x215e }, /* seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */
|
||||
{ 0x0ac9, 0x2122 }, /* trademark ™ TRADE MARK SIGN */
|
||||
{ 0x0aca, 0x2613 }, /* signaturemark ☓ SALTIRE */
|
||||
/* 0x0acb trademarkincircle ? ??? */
|
||||
{ 0x0acc, 0x25c1 }, /* leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */
|
||||
{ 0x0acd, 0x25b7 }, /* rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */
|
||||
{ 0x0ace, 0x25cb }, /* emopencircle ○ WHITE CIRCLE */
|
||||
{ 0x0acf, 0x25af }, /* emopenrectangle ▯ WHITE VERTICAL RECTANGLE */
|
||||
{ 0x0ad0, 0x2018 }, /* leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */
|
||||
{ 0x0ad1, 0x2019 }, /* rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */
|
||||
{ 0x0ad2, 0x201c }, /* leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */
|
||||
{ 0x0ad3, 0x201d }, /* rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */
|
||||
{ 0x0ad4, 0x211e }, /* prescription ℞ PRESCRIPTION TAKE */
|
||||
{ 0x0ad5, 0x2030 }, /* permille ‰ PER MILLE SIGN */
|
||||
{ 0x0ad6, 0x2032 }, /* minutes ′ PRIME */
|
||||
{ 0x0ad7, 0x2033 }, /* seconds ″ DOUBLE PRIME */
|
||||
{ 0x0ad9, 0x271d }, /* latincross ✝ LATIN CROSS */
|
||||
/* 0x0ada hexagram ? ??? */
|
||||
{ 0x0adb, 0x25ac }, /* filledrectbullet ▬ BLACK RECTANGLE */
|
||||
{ 0x0adc, 0x25c0 }, /* filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */
|
||||
{ 0x0add, 0x25b6 }, /* filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */
|
||||
{ 0x0ade, 0x25cf }, /* emfilledcircle ● BLACK CIRCLE */
|
||||
{ 0x0adf, 0x25ae }, /* emfilledrect ▮ BLACK VERTICAL RECTANGLE */
|
||||
{ 0x0ae0, 0x25e6 }, /* enopencircbullet ◦ WHITE BULLET */
|
||||
{ 0x0ae1, 0x25ab }, /* enopensquarebullet ▫ WHITE SMALL SQUARE */
|
||||
{ 0x0ae2, 0x25ad }, /* openrectbullet ▭ WHITE RECTANGLE */
|
||||
{ 0x0ae3, 0x25b3 }, /* opentribulletup △ WHITE UP-POINTING TRIANGLE */
|
||||
{ 0x0ae4, 0x25bd }, /* opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */
|
||||
{ 0x0ae5, 0x2606 }, /* openstar ☆ WHITE STAR */
|
||||
{ 0x0ae6, 0x2022 }, /* enfilledcircbullet • BULLET */
|
||||
{ 0x0ae7, 0x25aa }, /* enfilledsqbullet ▪ BLACK SMALL SQUARE */
|
||||
{ 0x0ae8, 0x25b2 }, /* filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */
|
||||
{ 0x0ae9, 0x25bc }, /* filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */
|
||||
{ 0x0aea, 0x261c }, /* leftpointer ☜ WHITE LEFT POINTING INDEX */
|
||||
{ 0x0aeb, 0x261e }, /* rightpointer ☞ WHITE RIGHT POINTING INDEX */
|
||||
{ 0x0aec, 0x2663 }, /* club ♣ BLACK CLUB SUIT */
|
||||
{ 0x0aed, 0x2666 }, /* diamond ♦ BLACK DIAMOND SUIT */
|
||||
{ 0x0aee, 0x2665 }, /* heart ♥ BLACK HEART SUIT */
|
||||
{ 0x0af0, 0x2720 }, /* maltesecross ✠ MALTESE CROSS */
|
||||
{ 0x0af1, 0x2020 }, /* dagger † DAGGER */
|
||||
{ 0x0af2, 0x2021 }, /* doubledagger ‡ DOUBLE DAGGER */
|
||||
{ 0x0af3, 0x2713 }, /* checkmark ✓ CHECK MARK */
|
||||
{ 0x0af4, 0x2717 }, /* ballotcross ✗ BALLOT X */
|
||||
{ 0x0af5, 0x266f }, /* musicalsharp ♯ MUSIC SHARP SIGN */
|
||||
{ 0x0af6, 0x266d }, /* musicalflat ♭ MUSIC FLAT SIGN */
|
||||
{ 0x0af7, 0x2642 }, /* malesymbol ♂ MALE SIGN */
|
||||
{ 0x0af8, 0x2640 }, /* femalesymbol ♀ FEMALE SIGN */
|
||||
{ 0x0af9, 0x260e }, /* telephone ☎ BLACK TELEPHONE */
|
||||
{ 0x0afa, 0x2315 }, /* telephonerecorder ⌕ TELEPHONE RECORDER */
|
||||
{ 0x0afb, 0x2117 }, /* phonographcopyright ℗ SOUND RECORDING COPYRIGHT */
|
||||
{ 0x0afc, 0x2038 }, /* caret ‸ CARET */
|
||||
{ 0x0afd, 0x201a }, /* singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */
|
||||
{ 0x0afe, 0x201e }, /* doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */
|
||||
/* 0x0aff cursor ? ??? */
|
||||
{ 0x0ba3, 0x003c }, /* leftcaret < LESS-THAN SIGN */
|
||||
{ 0x0ba6, 0x003e }, /* rightcaret > GREATER-THAN SIGN */
|
||||
{ 0x0ba8, 0x2228 }, /* downcaret ∨ LOGICAL OR */
|
||||
{ 0x0ba9, 0x2227 }, /* upcaret ∧ LOGICAL AND */
|
||||
{ 0x0bc0, 0x00af }, /* overbar ¯ MACRON */
|
||||
{ 0x0bc2, 0x22a4 }, /* downtack ⊤ DOWN TACK */
|
||||
{ 0x0bc3, 0x2229 }, /* upshoe ∩ INTERSECTION */
|
||||
{ 0x0bc4, 0x230a }, /* downstile ⌊ LEFT FLOOR */
|
||||
{ 0x0bc6, 0x005f }, /* underbar _ LOW LINE */
|
||||
{ 0x0bca, 0x2218 }, /* jot ∘ RING OPERATOR */
|
||||
{ 0x0bcc, 0x2395 }, /* quad ⎕ APL FUNCTIONAL SYMBOL QUAD (Unicode 3.0) */
|
||||
{ 0x0bce, 0x22a5 }, /* uptack ⊥ UP TACK */
|
||||
{ 0x0bcf, 0x25cb }, /* circle ○ WHITE CIRCLE */
|
||||
{ 0x0bd3, 0x2308 }, /* upstile ⌈ LEFT CEILING */
|
||||
{ 0x0bd6, 0x222a }, /* downshoe ∪ UNION */
|
||||
{ 0x0bd8, 0x2283 }, /* rightshoe ⊃ SUPERSET OF */
|
||||
{ 0x0bda, 0x2282 }, /* leftshoe ⊂ SUBSET OF */
|
||||
{ 0x0bdc, 0x22a2 }, /* lefttack ⊢ RIGHT TACK */
|
||||
{ 0x0bfc, 0x22a3 }, /* righttack ⊣ LEFT TACK */
|
||||
{ 0x0cdf, 0x2017 }, /* hebrew_doublelowline ‗ DOUBLE LOW LINE */
|
||||
{ 0x0ce0, 0x05d0 }, /* hebrew_aleph א HEBREW LETTER ALEF */
|
||||
{ 0x0ce1, 0x05d1 }, /* hebrew_bet ב HEBREW LETTER BET */
|
||||
{ 0x0ce2, 0x05d2 }, /* hebrew_gimel ג HEBREW LETTER GIMEL */
|
||||
{ 0x0ce3, 0x05d3 }, /* hebrew_dalet ד HEBREW LETTER DALET */
|
||||
{ 0x0ce4, 0x05d4 }, /* hebrew_he ה HEBREW LETTER HE */
|
||||
{ 0x0ce5, 0x05d5 }, /* hebrew_waw ו HEBREW LETTER VAV */
|
||||
{ 0x0ce6, 0x05d6 }, /* hebrew_zain ז HEBREW LETTER ZAYIN */
|
||||
{ 0x0ce7, 0x05d7 }, /* hebrew_chet ח HEBREW LETTER HET */
|
||||
{ 0x0ce8, 0x05d8 }, /* hebrew_tet ט HEBREW LETTER TET */
|
||||
{ 0x0ce9, 0x05d9 }, /* hebrew_yod י HEBREW LETTER YOD */
|
||||
{ 0x0cea, 0x05da }, /* hebrew_finalkaph ך HEBREW LETTER FINAL KAF */
|
||||
{ 0x0ceb, 0x05db }, /* hebrew_kaph כ HEBREW LETTER KAF */
|
||||
{ 0x0cec, 0x05dc }, /* hebrew_lamed ל HEBREW LETTER LAMED */
|
||||
{ 0x0ced, 0x05dd }, /* hebrew_finalmem ם HEBREW LETTER FINAL MEM */
|
||||
{ 0x0cee, 0x05de }, /* hebrew_mem מ HEBREW LETTER MEM */
|
||||
{ 0x0cef, 0x05df }, /* hebrew_finalnun ן HEBREW LETTER FINAL NUN */
|
||||
{ 0x0cf0, 0x05e0 }, /* hebrew_nun נ HEBREW LETTER NUN */
|
||||
{ 0x0cf1, 0x05e1 }, /* hebrew_samech ס HEBREW LETTER SAMEKH */
|
||||
{ 0x0cf2, 0x05e2 }, /* hebrew_ayin ע HEBREW LETTER AYIN */
|
||||
{ 0x0cf3, 0x05e3 }, /* hebrew_finalpe ף HEBREW LETTER FINAL PE */
|
||||
{ 0x0cf4, 0x05e4 }, /* hebrew_pe פ HEBREW LETTER PE */
|
||||
{ 0x0cf5, 0x05e5 }, /* hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */
|
||||
{ 0x0cf6, 0x05e6 }, /* hebrew_zade צ HEBREW LETTER TSADI */
|
||||
{ 0x0cf7, 0x05e7 }, /* hebrew_qoph ק HEBREW LETTER QOF */
|
||||
{ 0x0cf8, 0x05e8 }, /* hebrew_resh ר HEBREW LETTER RESH */
|
||||
{ 0x0cf9, 0x05e9 }, /* hebrew_shin ש HEBREW LETTER SHIN */
|
||||
{ 0x0cfa, 0x05ea }, /* hebrew_taw ת HEBREW LETTER TAV */
|
||||
{ 0x0da1, 0x0e01 }, /* Thai_kokai ก THAI CHARACTER KO KAI */
|
||||
{ 0x0da2, 0x0e02 }, /* Thai_khokhai ข THAI CHARACTER KHO KHAI */
|
||||
{ 0x0da3, 0x0e03 }, /* Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */
|
||||
{ 0x0da4, 0x0e04 }, /* Thai_khokhwai ค THAI CHARACTER KHO KHWAI */
|
||||
{ 0x0da5, 0x0e05 }, /* Thai_khokhon ฅ THAI CHARACTER KHO KHON */
|
||||
{ 0x0da6, 0x0e06 }, /* Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */
|
||||
{ 0x0da7, 0x0e07 }, /* Thai_ngongu ง THAI CHARACTER NGO NGU */
|
||||
{ 0x0da8, 0x0e08 }, /* Thai_chochan จ THAI CHARACTER CHO CHAN */
|
||||
{ 0x0da9, 0x0e09 }, /* Thai_choching ฉ THAI CHARACTER CHO CHING */
|
||||
{ 0x0daa, 0x0e0a }, /* Thai_chochang ช THAI CHARACTER CHO CHANG */
|
||||
{ 0x0dab, 0x0e0b }, /* Thai_soso ซ THAI CHARACTER SO SO */
|
||||
{ 0x0dac, 0x0e0c }, /* Thai_chochoe ฌ THAI CHARACTER CHO CHOE */
|
||||
{ 0x0dad, 0x0e0d }, /* Thai_yoying ญ THAI CHARACTER YO YING */
|
||||
{ 0x0dae, 0x0e0e }, /* Thai_dochada ฎ THAI CHARACTER DO CHADA */
|
||||
{ 0x0daf, 0x0e0f }, /* Thai_topatak ฏ THAI CHARACTER TO PATAK */
|
||||
{ 0x0db0, 0x0e10 }, /* Thai_thothan ฐ THAI CHARACTER THO THAN */
|
||||
{ 0x0db1, 0x0e11 }, /* Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */
|
||||
{ 0x0db2, 0x0e12 }, /* Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */
|
||||
{ 0x0db3, 0x0e13 }, /* Thai_nonen ณ THAI CHARACTER NO NEN */
|
||||
{ 0x0db4, 0x0e14 }, /* Thai_dodek ด THAI CHARACTER DO DEK */
|
||||
{ 0x0db5, 0x0e15 }, /* Thai_totao ต THAI CHARACTER TO TAO */
|
||||
{ 0x0db6, 0x0e16 }, /* Thai_thothung ถ THAI CHARACTER THO THUNG */
|
||||
{ 0x0db7, 0x0e17 }, /* Thai_thothahan ท THAI CHARACTER THO THAHAN */
|
||||
{ 0x0db8, 0x0e18 }, /* Thai_thothong ธ THAI CHARACTER THO THONG */
|
||||
{ 0x0db9, 0x0e19 }, /* Thai_nonu น THAI CHARACTER NO NU */
|
||||
{ 0x0dba, 0x0e1a }, /* Thai_bobaimai บ THAI CHARACTER BO BAIMAI */
|
||||
{ 0x0dbb, 0x0e1b }, /* Thai_popla ป THAI CHARACTER PO PLA */
|
||||
{ 0x0dbc, 0x0e1c }, /* Thai_phophung ผ THAI CHARACTER PHO PHUNG */
|
||||
{ 0x0dbd, 0x0e1d }, /* Thai_fofa ฝ THAI CHARACTER FO FA */
|
||||
{ 0x0dbe, 0x0e1e }, /* Thai_phophan พ THAI CHARACTER PHO PHAN */
|
||||
{ 0x0dbf, 0x0e1f }, /* Thai_fofan ฟ THAI CHARACTER FO FAN */
|
||||
{ 0x0dc0, 0x0e20 }, /* Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */
|
||||
{ 0x0dc1, 0x0e21 }, /* Thai_moma ม THAI CHARACTER MO MA */
|
||||
{ 0x0dc2, 0x0e22 }, /* Thai_yoyak ย THAI CHARACTER YO YAK */
|
||||
{ 0x0dc3, 0x0e23 }, /* Thai_rorua ร THAI CHARACTER RO RUA */
|
||||
{ 0x0dc4, 0x0e24 }, /* Thai_ru ฤ THAI CHARACTER RU */
|
||||
{ 0x0dc5, 0x0e25 }, /* Thai_loling ล THAI CHARACTER LO LING */
|
||||
{ 0x0dc6, 0x0e26 }, /* Thai_lu ฦ THAI CHARACTER LU */
|
||||
{ 0x0dc7, 0x0e27 }, /* Thai_wowaen ว THAI CHARACTER WO WAEN */
|
||||
{ 0x0dc8, 0x0e28 }, /* Thai_sosala ศ THAI CHARACTER SO SALA */
|
||||
{ 0x0dc9, 0x0e29 }, /* Thai_sorusi ษ THAI CHARACTER SO RUSI */
|
||||
{ 0x0dca, 0x0e2a }, /* Thai_sosua ส THAI CHARACTER SO SUA */
|
||||
{ 0x0dcb, 0x0e2b }, /* Thai_hohip ห THAI CHARACTER HO HIP */
|
||||
{ 0x0dcc, 0x0e2c }, /* Thai_lochula ฬ THAI CHARACTER LO CHULA */
|
||||
{ 0x0dcd, 0x0e2d }, /* Thai_oang อ THAI CHARACTER O ANG */
|
||||
{ 0x0dce, 0x0e2e }, /* Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */
|
||||
{ 0x0dcf, 0x0e2f }, /* Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */
|
||||
{ 0x0dd0, 0x0e30 }, /* Thai_saraa ะ THAI CHARACTER SARA A */
|
||||
{ 0x0dd1, 0x0e31 }, /* Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */
|
||||
{ 0x0dd2, 0x0e32 }, /* Thai_saraaa า THAI CHARACTER SARA AA */
|
||||
{ 0x0dd3, 0x0e33 }, /* Thai_saraam ำ THAI CHARACTER SARA AM */
|
||||
{ 0x0dd4, 0x0e34 }, /* Thai_sarai ิ THAI CHARACTER SARA I */
|
||||
{ 0x0dd5, 0x0e35 }, /* Thai_saraii ี THAI CHARACTER SARA II */
|
||||
{ 0x0dd6, 0x0e36 }, /* Thai_saraue ึ THAI CHARACTER SARA UE */
|
||||
{ 0x0dd7, 0x0e37 }, /* Thai_sarauee ื THAI CHARACTER SARA UEE */
|
||||
{ 0x0dd8, 0x0e38 }, /* Thai_sarau ุ THAI CHARACTER SARA U */
|
||||
{ 0x0dd9, 0x0e39 }, /* Thai_sarauu ู THAI CHARACTER SARA UU */
|
||||
{ 0x0dda, 0x0e3a }, /* Thai_phinthu ฺ THAI CHARACTER PHINTHU */
|
||||
{ 0x0dde, 0x0e3e }, /* Thai_maihanakat_maitho ??? */
|
||||
{ 0x0ddf, 0x0e3f }, /* Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */
|
||||
{ 0x0de0, 0x0e40 }, /* Thai_sarae เ THAI CHARACTER SARA E */
|
||||
{ 0x0de1, 0x0e41 }, /* Thai_saraae แ THAI CHARACTER SARA AE */
|
||||
{ 0x0de2, 0x0e42 }, /* Thai_sarao โ THAI CHARACTER SARA O */
|
||||
{ 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */
|
||||
{ 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */
|
||||
{ 0x0de5, 0x0e45 }, /* Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */
|
||||
{ 0x0de6, 0x0e46 }, /* Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */
|
||||
{ 0x0de7, 0x0e47 }, /* Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */
|
||||
{ 0x0de8, 0x0e48 }, /* Thai_maiek ่ THAI CHARACTER MAI EK */
|
||||
{ 0x0de9, 0x0e49 }, /* Thai_maitho ้ THAI CHARACTER MAI THO */
|
||||
{ 0x0dea, 0x0e4a }, /* Thai_maitri ๊ THAI CHARACTER MAI TRI */
|
||||
{ 0x0deb, 0x0e4b }, /* Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */
|
||||
{ 0x0dec, 0x0e4c }, /* Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */
|
||||
{ 0x0ded, 0x0e4d }, /* Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */
|
||||
{ 0x0df0, 0x0e50 }, /* Thai_leksun ๐ THAI DIGIT ZERO */
|
||||
{ 0x0df1, 0x0e51 }, /* Thai_leknung ๑ THAI DIGIT ONE */
|
||||
{ 0x0df2, 0x0e52 }, /* Thai_leksong ๒ THAI DIGIT TWO */
|
||||
{ 0x0df3, 0x0e53 }, /* Thai_leksam ๓ THAI DIGIT THREE */
|
||||
{ 0x0df4, 0x0e54 }, /* Thai_leksi ๔ THAI DIGIT FOUR */
|
||||
{ 0x0df5, 0x0e55 }, /* Thai_lekha ๕ THAI DIGIT FIVE */
|
||||
{ 0x0df6, 0x0e56 }, /* Thai_lekhok ๖ THAI DIGIT SIX */
|
||||
{ 0x0df7, 0x0e57 }, /* Thai_lekchet ๗ THAI DIGIT SEVEN */
|
||||
{ 0x0df8, 0x0e58 }, /* Thai_lekpaet ๘ THAI DIGIT EIGHT */
|
||||
{ 0x0df9, 0x0e59 }, /* Thai_lekkao ๙ THAI DIGIT NINE */
|
||||
{ 0x0ea1, 0x3131 }, /* Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */
|
||||
{ 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */
|
||||
{ 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */
|
||||
{ 0x0ea4, 0x3134 }, /* Hangul_Nieun ㄴ HANGUL LETTER NIEUN */
|
||||
{ 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */
|
||||
{ 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */
|
||||
{ 0x0ea7, 0x3137 }, /* Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */
|
||||
{ 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */
|
||||
{ 0x0ea9, 0x3139 }, /* Hangul_Rieul ㄹ HANGUL LETTER RIEUL */
|
||||
{ 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */
|
||||
{ 0x0eab, 0x313b }, /* Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */
|
||||
{ 0x0eac, 0x313c }, /* Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */
|
||||
{ 0x0ead, 0x313d }, /* Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */
|
||||
{ 0x0eae, 0x313e }, /* Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */
|
||||
{ 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */
|
||||
{ 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */
|
||||
{ 0x0eb1, 0x3141 }, /* Hangul_Mieum ㅁ HANGUL LETTER MIEUM */
|
||||
{ 0x0eb2, 0x3142 }, /* Hangul_Pieub ㅂ HANGUL LETTER PIEUP */
|
||||
{ 0x0eb3, 0x3143 }, /* Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */
|
||||
{ 0x0eb4, 0x3144 }, /* Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */
|
||||
{ 0x0eb5, 0x3145 }, /* Hangul_Sios ㅅ HANGUL LETTER SIOS */
|
||||
{ 0x0eb6, 0x3146 }, /* Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */
|
||||
{ 0x0eb7, 0x3147 }, /* Hangul_Ieung ㅇ HANGUL LETTER IEUNG */
|
||||
{ 0x0eb8, 0x3148 }, /* Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */
|
||||
{ 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */
|
||||
{ 0x0eba, 0x314a }, /* Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */
|
||||
{ 0x0ebb, 0x314b }, /* Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */
|
||||
{ 0x0ebc, 0x314c }, /* Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */
|
||||
{ 0x0ebd, 0x314d }, /* Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */
|
||||
{ 0x0ebe, 0x314e }, /* Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */
|
||||
{ 0x0ebf, 0x314f }, /* Hangul_A ㅏ HANGUL LETTER A */
|
||||
{ 0x0ec0, 0x3150 }, /* Hangul_AE ㅐ HANGUL LETTER AE */
|
||||
{ 0x0ec1, 0x3151 }, /* Hangul_YA ㅑ HANGUL LETTER YA */
|
||||
{ 0x0ec2, 0x3152 }, /* Hangul_YAE ㅒ HANGUL LETTER YAE */
|
||||
{ 0x0ec3, 0x3153 }, /* Hangul_EO ㅓ HANGUL LETTER EO */
|
||||
{ 0x0ec4, 0x3154 }, /* Hangul_E ㅔ HANGUL LETTER E */
|
||||
{ 0x0ec5, 0x3155 }, /* Hangul_YEO ㅕ HANGUL LETTER YEO */
|
||||
{ 0x0ec6, 0x3156 }, /* Hangul_YE ㅖ HANGUL LETTER YE */
|
||||
{ 0x0ec7, 0x3157 }, /* Hangul_O ㅗ HANGUL LETTER O */
|
||||
{ 0x0ec8, 0x3158 }, /* Hangul_WA ㅘ HANGUL LETTER WA */
|
||||
{ 0x0ec9, 0x3159 }, /* Hangul_WAE ㅙ HANGUL LETTER WAE */
|
||||
{ 0x0eca, 0x315a }, /* Hangul_OE ㅚ HANGUL LETTER OE */
|
||||
{ 0x0ecb, 0x315b }, /* Hangul_YO ㅛ HANGUL LETTER YO */
|
||||
{ 0x0ecc, 0x315c }, /* Hangul_U ㅜ HANGUL LETTER U */
|
||||
{ 0x0ecd, 0x315d }, /* Hangul_WEO ㅝ HANGUL LETTER WEO */
|
||||
{ 0x0ece, 0x315e }, /* Hangul_WE ㅞ HANGUL LETTER WE */
|
||||
{ 0x0ecf, 0x315f }, /* Hangul_WI ㅟ HANGUL LETTER WI */
|
||||
{ 0x0ed0, 0x3160 }, /* Hangul_YU ㅠ HANGUL LETTER YU */
|
||||
{ 0x0ed1, 0x3161 }, /* Hangul_EU ㅡ HANGUL LETTER EU */
|
||||
{ 0x0ed2, 0x3162 }, /* Hangul_YI ㅢ HANGUL LETTER YI */
|
||||
{ 0x0ed3, 0x3163 }, /* Hangul_I ㅣ HANGUL LETTER I */
|
||||
{ 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */
|
||||
{ 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */
|
||||
{ 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */
|
||||
{ 0x0ed7, 0x11ab }, /* Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */
|
||||
{ 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */
|
||||
{ 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */
|
||||
{ 0x0eda, 0x11ae }, /* Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */
|
||||
{ 0x0edb, 0x11af }, /* Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */
|
||||
{ 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */
|
||||
{ 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */
|
||||
{ 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */
|
||||
{ 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */
|
||||
{ 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */
|
||||
{ 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */
|
||||
{ 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */
|
||||
{ 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */
|
||||
{ 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */
|
||||
{ 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */
|
||||
{ 0x0ee6, 0x11ba }, /* Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */
|
||||
{ 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */
|
||||
{ 0x0ee8, 0x11bc }, /* Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */
|
||||
{ 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */
|
||||
{ 0x0eea, 0x11be }, /* Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */
|
||||
{ 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */
|
||||
{ 0x0eec, 0x11c0 }, /* Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */
|
||||
{ 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */
|
||||
{ 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */
|
||||
{ 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */
|
||||
{ 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */
|
||||
{ 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */
|
||||
{ 0x0ef2, 0x317f }, /* Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */
|
||||
/* 0x0ef3 Hangul_KkogjiDalrinIeung ? ??? */
|
||||
{ 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */
|
||||
{ 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */
|
||||
{ 0x0ef6, 0x318d }, /* Hangul_AraeA ㆍ HANGUL LETTER ARAEA */
|
||||
{ 0x0ef7, 0x318e }, /* Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */
|
||||
{ 0x0ef8, 0x11eb }, /* Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */
|
||||
{ 0x0ef9, 0x11f0 }, /* Hangul_J_KkogjiDalrinIeung ᇰ HANGUL JONGSEONG YESIEUNG */
|
||||
{ 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */
|
||||
{ 0x0eff, 0x20a9 }, /* Korean_Won ₩ WON SIGN */
|
||||
{ 0x13a4, 0x20ac }, /* Euro € EURO SIGN */
|
||||
{ 0x13bc, 0x0152 }, /* OE Œ LATIN CAPITAL LIGATURE OE */
|
||||
{ 0x13bd, 0x0153 }, /* oe œ LATIN SMALL LIGATURE OE */
|
||||
{ 0x13be, 0x0178 }, /* Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */
|
||||
{ 0x20a0, 0x20a0 }, /* EcuSign ₠ EURO-CURRENCY SIGN */
|
||||
{ 0x20a1, 0x20a1 }, /* ColonSign ₡ COLON SIGN */
|
||||
{ 0x20a2, 0x20a2 }, /* CruzeiroSign ₢ CRUZEIRO SIGN */
|
||||
{ 0x20a3, 0x20a3 }, /* FFrancSign ₣ FRENCH FRANC SIGN */
|
||||
{ 0x20a4, 0x20a4 }, /* LiraSign ₤ LIRA SIGN */
|
||||
{ 0x20a5, 0x20a5 }, /* MillSign ₥ MILL SIGN */
|
||||
{ 0x20a6, 0x20a6 }, /* NairaSign ₦ NAIRA SIGN */
|
||||
{ 0x20a7, 0x20a7 }, /* PesetaSign ₧ PESETA SIGN */
|
||||
{ 0x20a8, 0x20a8 }, /* RupeeSign ₨ RUPEE SIGN */
|
||||
{ 0x20a9, 0x20a9 }, /* WonSign ₩ WON SIGN */
|
||||
{ 0x20aa, 0x20aa }, /* NewSheqelSign ₪ NEW SHEQEL SIGN */
|
||||
{ 0x20ab, 0x20ab }, /* DongSign ₫ DONG SIGN */
|
||||
{ 0x20ac, 0x20ac }, /* EuroSign € EURO SIGN */
|
||||
};
|
||||
|
||||
xkb_keysym_t
|
||||
utf32_to_keysym(uint32_t ucs)
|
||||
{
|
||||
/* first check for Latin-1 characters (1:1 mapping) */
|
||||
if ((ucs >= 0x0020 && ucs <= 0x007e) ||
|
||||
(ucs >= 0x00a0 && ucs <= 0x00ff))
|
||||
return ucs;
|
||||
|
||||
/* special keysyms */
|
||||
if ((ucs >= (XKB_KEY_BackSpace & 0x7f) && ucs <= (XKB_KEY_Clear & 0x7f)) ||
|
||||
ucs == (XKB_KEY_Return & 0x7f) || ucs == (XKB_KEY_Escape & 0x7f))
|
||||
return ucs | 0xff00;
|
||||
if (ucs == (XKB_KEY_Delete & 0x7f))
|
||||
return XKB_KEY_Delete;
|
||||
|
||||
/* Unicode non-symbols and code points outside Unicode planes */
|
||||
if ((ucs >= 0xfdd0 && ucs <= 0xfdef) ||
|
||||
ucs > 0x10ffff || (ucs & 0xfffe) == 0xfffe)
|
||||
return XKB_KEY_NoSymbol;
|
||||
|
||||
/* search main table */
|
||||
for (size_t i = 0; i < sizeof(keysymtab)/sizeof(keysymtab[0]); i++)
|
||||
if (keysymtab[i].ucs == ucs)
|
||||
return keysymtab[i].keysym;
|
||||
|
||||
/* Use direct encoding if everything else fails */
|
||||
return ucs | 0x01000000;
|
||||
}
|
||||
618
glfw/xkb_glfw.c
vendored
618
glfw/xkb_glfw.c
vendored
@@ -29,221 +29,255 @@
|
||||
#include <stdlib.h>
|
||||
#include "internal.h"
|
||||
#include "xkb_glfw.h"
|
||||
START_ALLOW_CASE_RANGE
|
||||
#ifdef _GLFW_X11
|
||||
#include <X11/XKBlib.h>
|
||||
#endif
|
||||
|
||||
#define debug(...) if (_glfw.hints.init.debugKeyboard) printf(__VA_ARGS__);
|
||||
|
||||
|
||||
#define map_key(key) \
|
||||
switch(key) { \
|
||||
S(space, SPACE); \
|
||||
S(exclam, EXCLAM); \
|
||||
S(quotedbl, DOUBLE_QUOTE); \
|
||||
S(numbersign, NUMBER_SIGN); \
|
||||
S(dollar, DOLLAR); \
|
||||
S(ampersand, AMPERSAND); \
|
||||
S(apostrophe, APOSTROPHE); \
|
||||
S(parenleft, PARENTHESIS_LEFT); \
|
||||
S(parenright, PARENTHESIS_RIGHT); \
|
||||
S(plus, PLUS); \
|
||||
S(comma, COMMA); \
|
||||
S(minus, MINUS); \
|
||||
S(period, PERIOD); \
|
||||
S(slash, SLASH); \
|
||||
R(0, 9, 0, 9); \
|
||||
S(colon, COLON); \
|
||||
S(semicolon, SEMICOLON); \
|
||||
S(less, LESS); \
|
||||
S(equal, EQUAL); \
|
||||
S(greater, GREATER); \
|
||||
S(at, AT); \
|
||||
D(A, Z, A, Z); \
|
||||
S(bracketleft, LEFT_BRACKET); \
|
||||
S(backslash, BACKSLASH); \
|
||||
S(bracketright, RIGHT_BRACKET); \
|
||||
S(asciicircum, CIRCUMFLEX); \
|
||||
S(underscore, UNDERSCORE); \
|
||||
S(grave, GRAVE_ACCENT); \
|
||||
R(a, z, A, Z); \
|
||||
S(paragraph, PARAGRAPH); \
|
||||
S(masculine, MASCULINE); \
|
||||
S(agrave, A_GRAVE); \
|
||||
F(Agrave, A_GRAVE); \
|
||||
S(adiaeresis, A_DIAERESIS); \
|
||||
F(Adiaeresis, A_DIAERESIS); \
|
||||
S(aring, A_RING); \
|
||||
F(Aring, A_RING); \
|
||||
S(ae, AE); \
|
||||
F(AE, AE); \
|
||||
S(ccedilla, C_CEDILLA); \
|
||||
F(Ccedilla, C_CEDILLA); \
|
||||
S(egrave, E_GRAVE); \
|
||||
F(Egrave, E_GRAVE); \
|
||||
S(aacute, E_ACUTE); \
|
||||
F(Eacute, E_ACUTE); \
|
||||
S(igrave, I_GRAVE); \
|
||||
F(Igrave, I_GRAVE); \
|
||||
S(ntilde, N_TILDE); \
|
||||
F(Ntilde, N_TILDE); \
|
||||
S(ograve, O_GRAVE); \
|
||||
F(Ograve, O_GRAVE); \
|
||||
S(odiaeresis, O_DIAERESIS); \
|
||||
F(Odiaeresis, O_DIAERESIS); \
|
||||
S(oslash, O_SLASH); \
|
||||
F(Oslash, O_SLASH); \
|
||||
S(ugrave, U_GRAVE); \
|
||||
F(Ugrave, U_GRAVE); \
|
||||
S(udiaeresis, U_DIAERESIS); \
|
||||
F(Udiaeresis, U_DIAERESIS); \
|
||||
S(ssharp, S_SHARP); \
|
||||
S(Cyrillic_a, CYRILLIC_A); \
|
||||
F(Cyrillic_A, CYRILLIC_A); \
|
||||
S(Cyrillic_be, CYRILLIC_BE); \
|
||||
F(Cyrillic_BE, CYRILLIC_BE); \
|
||||
S(Cyrillic_ve, CYRILLIC_VE); \
|
||||
F(Cyrillic_VE, CYRILLIC_VE); \
|
||||
S(Cyrillic_ghe, CYRILLIC_GHE); \
|
||||
F(Cyrillic_GHE, CYRILLIC_GHE); \
|
||||
S(Cyrillic_de, CYRILLIC_DE); \
|
||||
F(Cyrillic_DE, CYRILLIC_DE); \
|
||||
S(Cyrillic_ie, CYRILLIC_IE); \
|
||||
F(Cyrillic_IE, CYRILLIC_IE); \
|
||||
S(Cyrillic_zhe, CYRILLIC_ZHE); \
|
||||
F(Cyrillic_ZHE, CYRILLIC_ZHE); \
|
||||
S(Cyrillic_ze, CYRILLIC_ZE); \
|
||||
F(Cyrillic_ZE, CYRILLIC_ZE); \
|
||||
S(Cyrillic_i, CYRILLIC_I); \
|
||||
F(Cyrillic_I, CYRILLIC_I); \
|
||||
S(Cyrillic_shorti, CYRILLIC_SHORT_I); \
|
||||
F(Cyrillic_SHORTI, CYRILLIC_SHORT_I); \
|
||||
S(Cyrillic_ka, CYRILLIC_KA); \
|
||||
F(Cyrillic_KA, CYRILLIC_KA); \
|
||||
S(Cyrillic_el, CYRILLIC_EL); \
|
||||
F(Cyrillic_EL, CYRILLIC_EL); \
|
||||
S(Cyrillic_em, CYRILLIC_EM); \
|
||||
F(Cyrillic_EM, CYRILLIC_EM); \
|
||||
S(Cyrillic_en, CYRILLIC_EN); \
|
||||
F(Cyrillic_EN, CYRILLIC_EN); \
|
||||
S(Cyrillic_o, CYRILLIC_O); \
|
||||
F(Cyrillic_O, CYRILLIC_O); \
|
||||
S(Cyrillic_pe, CYRILLIC_PE); \
|
||||
F(Cyrillic_PE, CYRILLIC_PE); \
|
||||
S(Cyrillic_er, CYRILLIC_ER); \
|
||||
F(Cyrillic_ER, CYRILLIC_ER); \
|
||||
S(Cyrillic_es, CYRILLIC_ES); \
|
||||
F(Cyrillic_ES, CYRILLIC_ES); \
|
||||
S(Cyrillic_te, CYRILLIC_TE); \
|
||||
F(Cyrillic_TE, CYRILLIC_TE); \
|
||||
S(Cyrillic_u, CYRILLIC_U); \
|
||||
F(Cyrillic_U, CYRILLIC_U); \
|
||||
S(Cyrillic_ef, CYRILLIC_EF); \
|
||||
F(Cyrillic_EF, CYRILLIC_EF); \
|
||||
S(Cyrillic_ha, CYRILLIC_HA); \
|
||||
F(Cyrillic_HA, CYRILLIC_HA); \
|
||||
S(Cyrillic_tse, CYRILLIC_TSE); \
|
||||
F(Cyrillic_TSE, CYRILLIC_TSE); \
|
||||
S(Cyrillic_che, CYRILLIC_CHE); \
|
||||
F(Cyrillic_CHE, CYRILLIC_CHE); \
|
||||
S(Cyrillic_sha, CYRILLIC_SHA); \
|
||||
F(Cyrillic_SHA, CYRILLIC_SHA); \
|
||||
S(Cyrillic_shcha, CYRILLIC_SHCHA); \
|
||||
F(Cyrillic_SHCHA, CYRILLIC_SHCHA); \
|
||||
S(Cyrillic_hardsign, CYRILLIC_HARD_SIGN); \
|
||||
F(Cyrillic_HARDSIGN, CYRILLIC_HARD_SIGN); \
|
||||
S(Cyrillic_yeru, CYRILLIC_YERU); \
|
||||
F(Cyrillic_YERU, CYRILLIC_YERU); \
|
||||
S(Cyrillic_softsign, CYRILLIC_SOFT_SIGN); \
|
||||
F(Cyrillic_SOFTSIGN, CYRILLIC_SOFT_SIGN); \
|
||||
S(Cyrillic_e, CYRILLIC_E); \
|
||||
F(Cyrillic_E, CYRILLIC_E); \
|
||||
S(Cyrillic_yu, CYRILLIC_YU); \
|
||||
F(Cyrillic_YU, CYRILLIC_YU); \
|
||||
S(Cyrillic_ya, CYRILLIC_YA); \
|
||||
F(Cyrillic_YA, CYRILLIC_YA); \
|
||||
S(Cyrillic_io, CYRILLIC_IO); \
|
||||
F(Cyrillic_IO, CYRILLIC_IO); \
|
||||
S(Escape, ESCAPE); \
|
||||
S(Return, ENTER); \
|
||||
S(Tab, TAB); \
|
||||
S(BackSpace, BACKSPACE); \
|
||||
S(Insert, INSERT); \
|
||||
S(Delete, DELETE); \
|
||||
S(Right, RIGHT); \
|
||||
S(Left, LEFT); \
|
||||
S(Up, UP); \
|
||||
S(Down, DOWN); \
|
||||
S(Page_Up, PAGE_UP); \
|
||||
S(Page_Down, PAGE_DOWN); \
|
||||
S(Home, HOME); \
|
||||
S(End, END); \
|
||||
S(Caps_Lock, CAPS_LOCK); \
|
||||
S(Scroll_Lock, SCROLL_LOCK); \
|
||||
S(Num_Lock, NUM_LOCK); \
|
||||
S(Print, PRINT_SCREEN); \
|
||||
S(Pause, PAUSE); \
|
||||
S(KP_Decimal, KP_DECIMAL); \
|
||||
S(KP_Divide, KP_DIVIDE); \
|
||||
S(KP_Multiply, KP_MULTIPLY); \
|
||||
S(KP_Subtract, KP_SUBTRACT); \
|
||||
S(KP_Add, KP_ADD); \
|
||||
S(KP_Enter, KP_ENTER); \
|
||||
S(KP_Equal, KP_EQUAL); \
|
||||
F(KP_Home, HOME); \
|
||||
F(KP_End, END); \
|
||||
F(KP_Page_Up, PAGE_UP); \
|
||||
F(KP_Page_Down, PAGE_DOWN); \
|
||||
F(KP_Insert, INSERT); \
|
||||
F(KP_Delete, DELETE); \
|
||||
S(Shift_L, LEFT_SHIFT); \
|
||||
S(Control_L, LEFT_CONTROL); \
|
||||
S(Alt_L, LEFT_ALT); \
|
||||
S(Super_L, LEFT_SUPER); \
|
||||
S(Shift_R, RIGHT_SHIFT); \
|
||||
S(Control_R, RIGHT_CONTROL); \
|
||||
S(Alt_R, RIGHT_ALT); \
|
||||
S(Super_R, RIGHT_SUPER); \
|
||||
S(Menu, MENU); \
|
||||
R(F1, F25, F1, F25); \
|
||||
R(KP_0, KP_9, KP_0, KP_9); \
|
||||
#ifdef XKB_HAS_NO_UTF32
|
||||
#include "xkb-compat-shim.h"
|
||||
#else
|
||||
#define utf32_to_keysym xkb_utf32_to_keysym
|
||||
#endif
|
||||
|
||||
static int
|
||||
glfw_key_for_sym(xkb_keysym_t key) {
|
||||
#define S(f, t) case XKB_KEY_##f: return GLFW_KEY_##t
|
||||
#define F(f, t) S(f, t)
|
||||
#define R(s, e, gs, ...) case XKB_KEY_##s ... XKB_KEY_##e: return GLFW_KEY_##gs + key - XKB_KEY_##s
|
||||
#define D(s, e, gs, ...) R(s, e, gs, __VA_ARGS__)
|
||||
map_key(key)
|
||||
S(KP_Up, UP);
|
||||
S(KP_Down, DOWN);
|
||||
S(KP_Left, LEFT);
|
||||
S(KP_Right, RIGHT);
|
||||
switch(key) {
|
||||
/* start xkb to glfw (auto generated by gen-key-constants.py do not edit) */
|
||||
case XKB_KEY_Escape: return GLFW_FKEY_ESCAPE;
|
||||
case XKB_KEY_Return: return GLFW_FKEY_ENTER;
|
||||
case XKB_KEY_Tab: return GLFW_FKEY_TAB;
|
||||
case XKB_KEY_BackSpace: return GLFW_FKEY_BACKSPACE;
|
||||
case XKB_KEY_Insert: return GLFW_FKEY_INSERT;
|
||||
case XKB_KEY_Delete: return GLFW_FKEY_DELETE;
|
||||
case XKB_KEY_Left: return GLFW_FKEY_LEFT;
|
||||
case XKB_KEY_Right: return GLFW_FKEY_RIGHT;
|
||||
case XKB_KEY_Up: return GLFW_FKEY_UP;
|
||||
case XKB_KEY_Down: return GLFW_FKEY_DOWN;
|
||||
case XKB_KEY_Page_Up: return GLFW_FKEY_PAGE_UP;
|
||||
case XKB_KEY_Page_Down: return GLFW_FKEY_PAGE_DOWN;
|
||||
case XKB_KEY_Home: return GLFW_FKEY_HOME;
|
||||
case XKB_KEY_End: return GLFW_FKEY_END;
|
||||
case XKB_KEY_Caps_Lock: return GLFW_FKEY_CAPS_LOCK;
|
||||
case XKB_KEY_Scroll_Lock: return GLFW_FKEY_SCROLL_LOCK;
|
||||
case XKB_KEY_Num_Lock: return GLFW_FKEY_NUM_LOCK;
|
||||
case XKB_KEY_Print: return GLFW_FKEY_PRINT_SCREEN;
|
||||
case XKB_KEY_Pause: return GLFW_FKEY_PAUSE;
|
||||
case XKB_KEY_Menu: return GLFW_FKEY_MENU;
|
||||
case XKB_KEY_F1: return GLFW_FKEY_F1;
|
||||
case XKB_KEY_F2: return GLFW_FKEY_F2;
|
||||
case XKB_KEY_F3: return GLFW_FKEY_F3;
|
||||
case XKB_KEY_F4: return GLFW_FKEY_F4;
|
||||
case XKB_KEY_F5: return GLFW_FKEY_F5;
|
||||
case XKB_KEY_F6: return GLFW_FKEY_F6;
|
||||
case XKB_KEY_F7: return GLFW_FKEY_F7;
|
||||
case XKB_KEY_F8: return GLFW_FKEY_F8;
|
||||
case XKB_KEY_F9: return GLFW_FKEY_F9;
|
||||
case XKB_KEY_F10: return GLFW_FKEY_F10;
|
||||
case XKB_KEY_F11: return GLFW_FKEY_F11;
|
||||
case XKB_KEY_F12: return GLFW_FKEY_F12;
|
||||
case XKB_KEY_F13: return GLFW_FKEY_F13;
|
||||
case XKB_KEY_F14: return GLFW_FKEY_F14;
|
||||
case XKB_KEY_F15: return GLFW_FKEY_F15;
|
||||
case XKB_KEY_F16: return GLFW_FKEY_F16;
|
||||
case XKB_KEY_F17: return GLFW_FKEY_F17;
|
||||
case XKB_KEY_F18: return GLFW_FKEY_F18;
|
||||
case XKB_KEY_F19: return GLFW_FKEY_F19;
|
||||
case XKB_KEY_F20: return GLFW_FKEY_F20;
|
||||
case XKB_KEY_F21: return GLFW_FKEY_F21;
|
||||
case XKB_KEY_F22: return GLFW_FKEY_F22;
|
||||
case XKB_KEY_F23: return GLFW_FKEY_F23;
|
||||
case XKB_KEY_F24: return GLFW_FKEY_F24;
|
||||
case XKB_KEY_F25: return GLFW_FKEY_F25;
|
||||
case XKB_KEY_F26: return GLFW_FKEY_F26;
|
||||
case XKB_KEY_F27: return GLFW_FKEY_F27;
|
||||
case XKB_KEY_F28: return GLFW_FKEY_F28;
|
||||
case XKB_KEY_F29: return GLFW_FKEY_F29;
|
||||
case XKB_KEY_F30: return GLFW_FKEY_F30;
|
||||
case XKB_KEY_F31: return GLFW_FKEY_F31;
|
||||
case XKB_KEY_F32: return GLFW_FKEY_F32;
|
||||
case XKB_KEY_F33: return GLFW_FKEY_F33;
|
||||
case XKB_KEY_F34: return GLFW_FKEY_F34;
|
||||
case XKB_KEY_F35: return GLFW_FKEY_F35;
|
||||
case XKB_KEY_KP_0: return GLFW_FKEY_KP_0;
|
||||
case XKB_KEY_KP_1: return GLFW_FKEY_KP_1;
|
||||
case XKB_KEY_KP_2: return GLFW_FKEY_KP_2;
|
||||
case XKB_KEY_KP_3: return GLFW_FKEY_KP_3;
|
||||
case XKB_KEY_KP_4: return GLFW_FKEY_KP_4;
|
||||
case XKB_KEY_KP_5: return GLFW_FKEY_KP_5;
|
||||
case XKB_KEY_KP_6: return GLFW_FKEY_KP_6;
|
||||
case XKB_KEY_KP_7: return GLFW_FKEY_KP_7;
|
||||
case XKB_KEY_KP_8: return GLFW_FKEY_KP_8;
|
||||
case XKB_KEY_KP_9: return GLFW_FKEY_KP_9;
|
||||
case XKB_KEY_KP_Decimal: return GLFW_FKEY_KP_DECIMAL;
|
||||
case XKB_KEY_KP_Divide: return GLFW_FKEY_KP_DIVIDE;
|
||||
case XKB_KEY_KP_Multiply: return GLFW_FKEY_KP_MULTIPLY;
|
||||
case XKB_KEY_KP_Subtract: return GLFW_FKEY_KP_SUBTRACT;
|
||||
case XKB_KEY_KP_Add: return GLFW_FKEY_KP_ADD;
|
||||
case XKB_KEY_KP_Enter: return GLFW_FKEY_KP_ENTER;
|
||||
case XKB_KEY_KP_Equal: return GLFW_FKEY_KP_EQUAL;
|
||||
case XKB_KEY_KP_Separator: return GLFW_FKEY_KP_SEPARATOR;
|
||||
case XKB_KEY_KP_Left: return GLFW_FKEY_KP_LEFT;
|
||||
case XKB_KEY_KP_Right: return GLFW_FKEY_KP_RIGHT;
|
||||
case XKB_KEY_KP_Up: return GLFW_FKEY_KP_UP;
|
||||
case XKB_KEY_KP_Down: return GLFW_FKEY_KP_DOWN;
|
||||
case XKB_KEY_KP_Page_Up: return GLFW_FKEY_KP_PAGE_UP;
|
||||
case XKB_KEY_KP_Page_Down: return GLFW_FKEY_KP_PAGE_DOWN;
|
||||
case XKB_KEY_KP_Home: return GLFW_FKEY_KP_HOME;
|
||||
case XKB_KEY_KP_End: return GLFW_FKEY_KP_END;
|
||||
case XKB_KEY_KP_Insert: return GLFW_FKEY_KP_INSERT;
|
||||
case XKB_KEY_KP_Delete: return GLFW_FKEY_KP_DELETE;
|
||||
case XKB_KEY_KP_Begin: return GLFW_FKEY_KP_BEGIN;
|
||||
case XKB_KEY_XF86AudioPlay: return GLFW_FKEY_MEDIA_PLAY;
|
||||
case XKB_KEY_XF86AudioPause: return GLFW_FKEY_MEDIA_PAUSE;
|
||||
case XKB_KEY_XF86AudioStop: return GLFW_FKEY_MEDIA_STOP;
|
||||
case XKB_KEY_XF86AudioForward: return GLFW_FKEY_MEDIA_FAST_FORWARD;
|
||||
case XKB_KEY_XF86AudioRewind: return GLFW_FKEY_MEDIA_REWIND;
|
||||
case XKB_KEY_XF86AudioNext: return GLFW_FKEY_MEDIA_TRACK_NEXT;
|
||||
case XKB_KEY_XF86AudioPrev: return GLFW_FKEY_MEDIA_TRACK_PREVIOUS;
|
||||
case XKB_KEY_XF86AudioRecord: return GLFW_FKEY_MEDIA_RECORD;
|
||||
case XKB_KEY_XF86AudioLowerVolume: return GLFW_FKEY_LOWER_VOLUME;
|
||||
case XKB_KEY_XF86AudioRaiseVolume: return GLFW_FKEY_RAISE_VOLUME;
|
||||
case XKB_KEY_XF86AudioMute: return GLFW_FKEY_MUTE_VOLUME;
|
||||
case XKB_KEY_Shift_L: return GLFW_FKEY_LEFT_SHIFT;
|
||||
case XKB_KEY_Control_L: return GLFW_FKEY_LEFT_CONTROL;
|
||||
case XKB_KEY_Alt_L: return GLFW_FKEY_LEFT_ALT;
|
||||
case XKB_KEY_Super_L: return GLFW_FKEY_LEFT_SUPER;
|
||||
case XKB_KEY_Hyper_L: return GLFW_FKEY_LEFT_HYPER;
|
||||
case XKB_KEY_Meta_L: return GLFW_FKEY_LEFT_META;
|
||||
case XKB_KEY_Shift_R: return GLFW_FKEY_RIGHT_SHIFT;
|
||||
case XKB_KEY_Control_R: return GLFW_FKEY_RIGHT_CONTROL;
|
||||
case XKB_KEY_Alt_R: return GLFW_FKEY_RIGHT_ALT;
|
||||
case XKB_KEY_Super_R: return GLFW_FKEY_RIGHT_SUPER;
|
||||
case XKB_KEY_Hyper_R: return GLFW_FKEY_RIGHT_HYPER;
|
||||
case XKB_KEY_Meta_R: return GLFW_FKEY_RIGHT_META;
|
||||
case XKB_KEY_ISO_Level3_Shift: return GLFW_FKEY_ISO_LEVEL3_SHIFT;
|
||||
case XKB_KEY_ISO_Level5_Shift: return GLFW_FKEY_ISO_LEVEL5_SHIFT;
|
||||
/* end xkb to glfw */
|
||||
default:
|
||||
break;
|
||||
return xkb_keysym_to_utf32(key);
|
||||
}
|
||||
return GLFW_KEY_UNKNOWN;
|
||||
#undef F
|
||||
#undef D
|
||||
#undef R
|
||||
#undef S
|
||||
}
|
||||
|
||||
xkb_keysym_t
|
||||
glfw_xkb_sym_for_key(int key) {
|
||||
#define S(f, t) case GLFW_KEY_##t: return XKB_KEY_##f
|
||||
#define F(...)
|
||||
#define R(s, e, gs, ge) case GLFW_KEY_##gs ... GLFW_KEY_##ge: return XKB_KEY_##s + key - GLFW_KEY_##gs
|
||||
#define D(...)
|
||||
map_key(key)
|
||||
default:
|
||||
break;
|
||||
glfw_xkb_sym_for_key(uint32_t key) {
|
||||
switch(key) {
|
||||
/* start glfw to xkb (auto generated by gen-key-constants.py do not edit) */
|
||||
case GLFW_FKEY_ESCAPE: return XKB_KEY_Escape;
|
||||
case GLFW_FKEY_ENTER: return XKB_KEY_Return;
|
||||
case GLFW_FKEY_TAB: return XKB_KEY_Tab;
|
||||
case GLFW_FKEY_BACKSPACE: return XKB_KEY_BackSpace;
|
||||
case GLFW_FKEY_INSERT: return XKB_KEY_Insert;
|
||||
case GLFW_FKEY_DELETE: return XKB_KEY_Delete;
|
||||
case GLFW_FKEY_LEFT: return XKB_KEY_Left;
|
||||
case GLFW_FKEY_RIGHT: return XKB_KEY_Right;
|
||||
case GLFW_FKEY_UP: return XKB_KEY_Up;
|
||||
case GLFW_FKEY_DOWN: return XKB_KEY_Down;
|
||||
case GLFW_FKEY_PAGE_UP: return XKB_KEY_Page_Up;
|
||||
case GLFW_FKEY_PAGE_DOWN: return XKB_KEY_Page_Down;
|
||||
case GLFW_FKEY_HOME: return XKB_KEY_Home;
|
||||
case GLFW_FKEY_END: return XKB_KEY_End;
|
||||
case GLFW_FKEY_CAPS_LOCK: return XKB_KEY_Caps_Lock;
|
||||
case GLFW_FKEY_SCROLL_LOCK: return XKB_KEY_Scroll_Lock;
|
||||
case GLFW_FKEY_NUM_LOCK: return XKB_KEY_Num_Lock;
|
||||
case GLFW_FKEY_PRINT_SCREEN: return XKB_KEY_Print;
|
||||
case GLFW_FKEY_PAUSE: return XKB_KEY_Pause;
|
||||
case GLFW_FKEY_MENU: return XKB_KEY_Menu;
|
||||
case GLFW_FKEY_F1: return XKB_KEY_F1;
|
||||
case GLFW_FKEY_F2: return XKB_KEY_F2;
|
||||
case GLFW_FKEY_F3: return XKB_KEY_F3;
|
||||
case GLFW_FKEY_F4: return XKB_KEY_F4;
|
||||
case GLFW_FKEY_F5: return XKB_KEY_F5;
|
||||
case GLFW_FKEY_F6: return XKB_KEY_F6;
|
||||
case GLFW_FKEY_F7: return XKB_KEY_F7;
|
||||
case GLFW_FKEY_F8: return XKB_KEY_F8;
|
||||
case GLFW_FKEY_F9: return XKB_KEY_F9;
|
||||
case GLFW_FKEY_F10: return XKB_KEY_F10;
|
||||
case GLFW_FKEY_F11: return XKB_KEY_F11;
|
||||
case GLFW_FKEY_F12: return XKB_KEY_F12;
|
||||
case GLFW_FKEY_F13: return XKB_KEY_F13;
|
||||
case GLFW_FKEY_F14: return XKB_KEY_F14;
|
||||
case GLFW_FKEY_F15: return XKB_KEY_F15;
|
||||
case GLFW_FKEY_F16: return XKB_KEY_F16;
|
||||
case GLFW_FKEY_F17: return XKB_KEY_F17;
|
||||
case GLFW_FKEY_F18: return XKB_KEY_F18;
|
||||
case GLFW_FKEY_F19: return XKB_KEY_F19;
|
||||
case GLFW_FKEY_F20: return XKB_KEY_F20;
|
||||
case GLFW_FKEY_F21: return XKB_KEY_F21;
|
||||
case GLFW_FKEY_F22: return XKB_KEY_F22;
|
||||
case GLFW_FKEY_F23: return XKB_KEY_F23;
|
||||
case GLFW_FKEY_F24: return XKB_KEY_F24;
|
||||
case GLFW_FKEY_F25: return XKB_KEY_F25;
|
||||
case GLFW_FKEY_F26: return XKB_KEY_F26;
|
||||
case GLFW_FKEY_F27: return XKB_KEY_F27;
|
||||
case GLFW_FKEY_F28: return XKB_KEY_F28;
|
||||
case GLFW_FKEY_F29: return XKB_KEY_F29;
|
||||
case GLFW_FKEY_F30: return XKB_KEY_F30;
|
||||
case GLFW_FKEY_F31: return XKB_KEY_F31;
|
||||
case GLFW_FKEY_F32: return XKB_KEY_F32;
|
||||
case GLFW_FKEY_F33: return XKB_KEY_F33;
|
||||
case GLFW_FKEY_F34: return XKB_KEY_F34;
|
||||
case GLFW_FKEY_F35: return XKB_KEY_F35;
|
||||
case GLFW_FKEY_KP_0: return XKB_KEY_KP_0;
|
||||
case GLFW_FKEY_KP_1: return XKB_KEY_KP_1;
|
||||
case GLFW_FKEY_KP_2: return XKB_KEY_KP_2;
|
||||
case GLFW_FKEY_KP_3: return XKB_KEY_KP_3;
|
||||
case GLFW_FKEY_KP_4: return XKB_KEY_KP_4;
|
||||
case GLFW_FKEY_KP_5: return XKB_KEY_KP_5;
|
||||
case GLFW_FKEY_KP_6: return XKB_KEY_KP_6;
|
||||
case GLFW_FKEY_KP_7: return XKB_KEY_KP_7;
|
||||
case GLFW_FKEY_KP_8: return XKB_KEY_KP_8;
|
||||
case GLFW_FKEY_KP_9: return XKB_KEY_KP_9;
|
||||
case GLFW_FKEY_KP_DECIMAL: return XKB_KEY_KP_Decimal;
|
||||
case GLFW_FKEY_KP_DIVIDE: return XKB_KEY_KP_Divide;
|
||||
case GLFW_FKEY_KP_MULTIPLY: return XKB_KEY_KP_Multiply;
|
||||
case GLFW_FKEY_KP_SUBTRACT: return XKB_KEY_KP_Subtract;
|
||||
case GLFW_FKEY_KP_ADD: return XKB_KEY_KP_Add;
|
||||
case GLFW_FKEY_KP_ENTER: return XKB_KEY_KP_Enter;
|
||||
case GLFW_FKEY_KP_EQUAL: return XKB_KEY_KP_Equal;
|
||||
case GLFW_FKEY_KP_SEPARATOR: return XKB_KEY_KP_Separator;
|
||||
case GLFW_FKEY_KP_LEFT: return XKB_KEY_KP_Left;
|
||||
case GLFW_FKEY_KP_RIGHT: return XKB_KEY_KP_Right;
|
||||
case GLFW_FKEY_KP_UP: return XKB_KEY_KP_Up;
|
||||
case GLFW_FKEY_KP_DOWN: return XKB_KEY_KP_Down;
|
||||
case GLFW_FKEY_KP_PAGE_UP: return XKB_KEY_KP_Page_Up;
|
||||
case GLFW_FKEY_KP_PAGE_DOWN: return XKB_KEY_KP_Page_Down;
|
||||
case GLFW_FKEY_KP_HOME: return XKB_KEY_KP_Home;
|
||||
case GLFW_FKEY_KP_END: return XKB_KEY_KP_End;
|
||||
case GLFW_FKEY_KP_INSERT: return XKB_KEY_KP_Insert;
|
||||
case GLFW_FKEY_KP_DELETE: return XKB_KEY_KP_Delete;
|
||||
case GLFW_FKEY_KP_BEGIN: return XKB_KEY_KP_Begin;
|
||||
case GLFW_FKEY_MEDIA_PLAY: return XKB_KEY_XF86AudioPlay;
|
||||
case GLFW_FKEY_MEDIA_PAUSE: return XKB_KEY_XF86AudioPause;
|
||||
case GLFW_FKEY_MEDIA_STOP: return XKB_KEY_XF86AudioStop;
|
||||
case GLFW_FKEY_MEDIA_FAST_FORWARD: return XKB_KEY_XF86AudioForward;
|
||||
case GLFW_FKEY_MEDIA_REWIND: return XKB_KEY_XF86AudioRewind;
|
||||
case GLFW_FKEY_MEDIA_TRACK_NEXT: return XKB_KEY_XF86AudioNext;
|
||||
case GLFW_FKEY_MEDIA_TRACK_PREVIOUS: return XKB_KEY_XF86AudioPrev;
|
||||
case GLFW_FKEY_MEDIA_RECORD: return XKB_KEY_XF86AudioRecord;
|
||||
case GLFW_FKEY_LOWER_VOLUME: return XKB_KEY_XF86AudioLowerVolume;
|
||||
case GLFW_FKEY_RAISE_VOLUME: return XKB_KEY_XF86AudioRaiseVolume;
|
||||
case GLFW_FKEY_MUTE_VOLUME: return XKB_KEY_XF86AudioMute;
|
||||
case GLFW_FKEY_LEFT_SHIFT: return XKB_KEY_Shift_L;
|
||||
case GLFW_FKEY_LEFT_CONTROL: return XKB_KEY_Control_L;
|
||||
case GLFW_FKEY_LEFT_ALT: return XKB_KEY_Alt_L;
|
||||
case GLFW_FKEY_LEFT_SUPER: return XKB_KEY_Super_L;
|
||||
case GLFW_FKEY_LEFT_HYPER: return XKB_KEY_Hyper_L;
|
||||
case GLFW_FKEY_LEFT_META: return XKB_KEY_Meta_L;
|
||||
case GLFW_FKEY_RIGHT_SHIFT: return XKB_KEY_Shift_R;
|
||||
case GLFW_FKEY_RIGHT_CONTROL: return XKB_KEY_Control_R;
|
||||
case GLFW_FKEY_RIGHT_ALT: return XKB_KEY_Alt_R;
|
||||
case GLFW_FKEY_RIGHT_SUPER: return XKB_KEY_Super_R;
|
||||
case GLFW_FKEY_RIGHT_HYPER: return XKB_KEY_Hyper_R;
|
||||
case GLFW_FKEY_RIGHT_META: return XKB_KEY_Meta_R;
|
||||
case GLFW_FKEY_ISO_LEVEL3_SHIFT: return XKB_KEY_ISO_Level3_Shift;
|
||||
case GLFW_FKEY_ISO_LEVEL5_SHIFT: return XKB_KEY_ISO_Level5_Shift;
|
||||
/* end glfw to xkb */
|
||||
default:
|
||||
return utf32_to_keysym(key);
|
||||
}
|
||||
return GLFW_KEY_UNKNOWN;
|
||||
#undef F
|
||||
#undef D
|
||||
#undef R
|
||||
#undef S
|
||||
}
|
||||
END_ALLOW_CASE_RANGE
|
||||
|
||||
#ifdef _GLFW_X11
|
||||
|
||||
@@ -283,11 +317,90 @@ glfw_xkb_update_x11_keyboard_id(_GLFWXKBData *xkb) {
|
||||
if (conn) state = xkb_x11_state_new_from_device(keymap, conn, xkb->keyboard_device_id); \
|
||||
}
|
||||
|
||||
static void
|
||||
glfw_xkb_update_masks(_GLFWXKBData *xkb) {
|
||||
// See https://github.com/kovidgoyal/kitty/pull/3430 for discussion
|
||||
bool succeeded = false;
|
||||
unsigned used_bits = 0; /* To avoid using the same bit twice */
|
||||
XkbDescPtr xkb_ptr = XkbGetMap( _glfw.x11.display, XkbVirtualModsMask | XkbVirtualModMapMask, XkbUseCoreKbd );
|
||||
|
||||
/* shift, control, and capsLock are special; they cannot be identified reliably on X11 */
|
||||
#define S(a, n) xkb->a##Idx = xkb_keymap_mod_get_index(xkb->keymap, n); xkb->a##Mask = 1 << xkb->a##Idx; used_bits |= xkb->a##Mask;
|
||||
S(control, XKB_MOD_NAME_CTRL);
|
||||
S(shift, XKB_MOD_NAME_SHIFT);
|
||||
S(capsLock, XKB_MOD_NAME_CAPS);
|
||||
#undef S
|
||||
#define S( a ) xkb->a##Idx = XKB_MOD_INVALID; xkb->a##Mask = 0
|
||||
S(alt); S(super); S(hyper); S(meta); S(numLock);
|
||||
#undef S
|
||||
if (xkb_ptr) {
|
||||
Status status = XkbGetNames(_glfw.x11.display, XkbVirtualModNamesMask, xkb_ptr);
|
||||
if (status == Success) {
|
||||
for (int indx = 0; indx < XkbNumVirtualMods; ++indx) {
|
||||
Atom atom = xkb_ptr->names->vmods[indx];
|
||||
if (atom) {
|
||||
unsigned mask_rtn = 0;
|
||||
if (XkbVirtualModsToReal( xkb_ptr, 1<<indx, &mask_rtn) ) {
|
||||
const char *name = XGetAtomName(_glfw.x11.display, atom);
|
||||
#define S( a, s ) if (!(used_bits & mask_rtn) && strcmp(name, #s) == 0) xkb->a##Mask = mask_rtn, used_bits |= mask_rtn
|
||||
/* Note that the order matters here; earlier is higher priority. */
|
||||
S(alt, Alt);
|
||||
S(super, Super);
|
||||
S(numLock, NumLock);
|
||||
S(meta, Meta);
|
||||
S(hyper, Hyper);
|
||||
#undef S
|
||||
}
|
||||
}
|
||||
}
|
||||
succeeded = true;
|
||||
}
|
||||
XkbFreeNames(xkb_ptr, XkbVirtualModNamesMask, True);
|
||||
XkbFreeKeyboard(xkb_ptr, 0, True);
|
||||
}
|
||||
if (succeeded) {
|
||||
unsigned indx, shifted;
|
||||
for (indx = 0, shifted = 1; used_bits; ++indx, shifted <<= 1, used_bits >>= 1) {
|
||||
#define S( a ) if ( ( xkb->a##Mask & shifted ) == shifted ) xkb->a##Idx = indx
|
||||
S(alt); S(super); S(hyper); S(meta); S(numLock);
|
||||
#undef S
|
||||
}
|
||||
}
|
||||
#define S(a, n) xkb->a##Idx = xkb_keymap_mod_get_index(xkb->keymap, n); xkb->a##Mask = 1 << xkb->a##Idx;
|
||||
if (!succeeded) {
|
||||
S(numLock, XKB_MOD_NAME_NUM);
|
||||
S(alt, XKB_MOD_NAME_ALT);
|
||||
S(super, XKB_MOD_NAME_LOGO);
|
||||
}
|
||||
#undef S
|
||||
debug("Modifier indices alt: 0x%x super: 0x%x hyper: 0x%x meta: 0x%x numlock: 0x%x shift: 0x%x capslock: 0x%x\n",
|
||||
xkb->altIdx, xkb->superIdx, xkb->hyperIdx, xkb->metaIdx, xkb->numLockIdx, xkb->shiftIdx, xkb->capsLockIdx);
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
#define xkb_glfw_load_keymap(keymap, map_str) keymap = xkb_keymap_new_from_string(xkb->context, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, 0);
|
||||
#define xkb_glfw_load_state(keymap, state) state = xkb_state_new(keymap);
|
||||
|
||||
static void
|
||||
glfw_xkb_update_masks(_GLFWXKBData *xkb) {
|
||||
// Should find better solution under Wayland
|
||||
// See https://github.com/kovidgoyal/kitty/pull/3430 for discussion
|
||||
|
||||
#define S( a ) xkb->a##Idx = XKB_MOD_INVALID; xkb->a##Mask = 0
|
||||
S(hyper); S(meta);
|
||||
#undef S
|
||||
#define S(a, n) xkb->a##Idx = xkb_keymap_mod_get_index(xkb->keymap, n); xkb->a##Mask = 1 << xkb->a##Idx;
|
||||
S(control, XKB_MOD_NAME_CTRL);
|
||||
S(shift, XKB_MOD_NAME_SHIFT);
|
||||
S(capsLock, XKB_MOD_NAME_CAPS);
|
||||
S(numLock, XKB_MOD_NAME_NUM);
|
||||
S(alt, XKB_MOD_NAME_ALT);
|
||||
S(super, XKB_MOD_NAME_LOGO);
|
||||
#undef S
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void
|
||||
@@ -324,7 +437,9 @@ glfw_xkb_create_context(_GLFWXKBData *xkb) {
|
||||
"Failed to initialize XKB context");
|
||||
return false;
|
||||
}
|
||||
#ifndef _GLFW_WAYLAND
|
||||
glfw_connect_to_ibus(&xkb->ibus);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -359,6 +474,10 @@ load_compose_tables(_GLFWXKBData *xkb) {
|
||||
if (!locale) locale = getenv("LC_CTYPE");
|
||||
if (!locale) locale = getenv("LANG");
|
||||
if (!locale) locale = "C";
|
||||
|
||||
// See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=903373
|
||||
if (strcmp(locale, "en_IN") == 0) locale = "en_IN.UTF-8";
|
||||
|
||||
compose_table = xkb_compose_table_new_from_locale(xkb->context, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
|
||||
if (!compose_table) {
|
||||
_glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create XKB compose table for locale %s", locale);
|
||||
@@ -382,11 +501,26 @@ active_unknown_modifiers(_GLFWXKBData *xkb, struct xkb_state *state) {
|
||||
return ans;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
update_one_modifier(XKBStateGroup *group, xkb_mod_mask_t mask,
|
||||
xkb_mod_index_t idx, unsigned int mod) {
|
||||
if ( idx == XKB_MOD_INVALID )
|
||||
return 0;
|
||||
/* Optimization in the case of a single real modifier */
|
||||
if ( mask && ( ( mask & ( mask-1 ) ) == 0 ) )
|
||||
return (xkb_state_mod_index_is_active(group->state, idx, XKB_STATE_MODS_EFFECTIVE) == 1) ? mod : 0;
|
||||
/* Multiple real mods map to the same virtual mod */
|
||||
for ( unsigned indx = 0; indx < 32 && mask; ++indx, mask >>= 1 )
|
||||
if ( ( mask & 1 ) && xkb_state_mod_index_is_active(group->state, indx, XKB_STATE_MODS_EFFECTIVE) == 1)
|
||||
return mod;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
update_modifiers(_GLFWXKBData *xkb) {
|
||||
XKBStateGroup *group = &xkb->states;
|
||||
#define S(attr, name) if (xkb_state_mod_index_is_active(group->state, xkb->attr##Idx, XKB_STATE_MODS_EFFECTIVE)) group->modifiers |= GLFW_MOD_##name
|
||||
S(control, CONTROL); S(alt, ALT); S(shift, SHIFT); S(super, SUPER); S(capsLock, CAPS_LOCK); S(numLock, NUM_LOCK);
|
||||
#define S(attr, name) group->modifiers |= update_one_modifier( group, xkb->attr##Mask, xkb->attr##Idx, GLFW_MOD_##name )
|
||||
S(control, CONTROL); S(alt, ALT); S(shift, SHIFT); S(super, SUPER); S(hyper, HYPER); S(meta, META); S(capsLock, CAPS_LOCK); S(numLock, NUM_LOCK);
|
||||
#undef S
|
||||
xkb->states.activeUnknownModifiers = active_unknown_modifiers(xkb, xkb->states.state);
|
||||
|
||||
@@ -410,18 +544,12 @@ glfw_xkb_compile_keymap(_GLFWXKBData *xkb, const char *map_str) {
|
||||
return false;
|
||||
}
|
||||
load_compose_tables(xkb);
|
||||
#define S(a, n) xkb->a##Idx = xkb_keymap_mod_get_index(xkb->keymap, n); xkb->a##Mask = 1 << xkb->a##Idx;
|
||||
S(control, XKB_MOD_NAME_CTRL);
|
||||
S(alt, XKB_MOD_NAME_ALT);
|
||||
S(shift, XKB_MOD_NAME_SHIFT);
|
||||
S(super, XKB_MOD_NAME_LOGO);
|
||||
S(capsLock, XKB_MOD_NAME_CAPS);
|
||||
S(numLock, XKB_MOD_NAME_NUM);
|
||||
#undef S
|
||||
|
||||
glfw_xkb_update_masks(xkb);
|
||||
size_t capacity = arraysz(xkb->unknownModifiers), j = 0;
|
||||
for (xkb_mod_index_t i = 0; i < capacity; i++) xkb->unknownModifiers[i] = XKB_MOD_INVALID;
|
||||
for (xkb_mod_index_t i = 0; i < xkb_keymap_num_mods(xkb->keymap) && j < capacity - 1; i++) {
|
||||
if (i != xkb->controlIdx && i != xkb->altIdx && i != xkb->shiftIdx && i != xkb->superIdx && i != xkb->capsLockIdx && i != xkb->numLockIdx) xkb->unknownModifiers[j++] = i;
|
||||
if (i != xkb->controlIdx && i != xkb->altIdx && i != xkb->shiftIdx && i != xkb->superIdx && i != xkb->hyperIdx && i != xkb->metaIdx && i != xkb->capsLockIdx && i != xkb->numLockIdx) xkb->unknownModifiers[j++] = i;
|
||||
}
|
||||
xkb->states.modifiers = 0;
|
||||
xkb->states.activeUnknownModifiers = 0;
|
||||
@@ -493,6 +621,8 @@ format_mods(unsigned int mods) {
|
||||
if (mods & GLFW_MOD_ALT) pr("alt+");
|
||||
if (mods & GLFW_MOD_SHIFT) pr("shift+");
|
||||
if (mods & GLFW_MOD_SUPER) pr("super+");
|
||||
if (mods & GLFW_MOD_META) pr("meta+");
|
||||
if (mods & GLFW_MOD_HYPER) pr("hyper+");
|
||||
if (mods & GLFW_MOD_CAPS_LOCK) pr("capslock+");
|
||||
if (mods & GLFW_MOD_NUM_LOCK) pr("numlock+");
|
||||
if (p == s) pr("none");
|
||||
@@ -526,16 +656,16 @@ format_xkb_mods(_GLFWXKBData *xkb, const char* name, xkb_mod_mask_t mods) {
|
||||
}
|
||||
|
||||
void
|
||||
glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, int which, int a, int b, int c, int d) {
|
||||
glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, const GLFWIMEUpdateEvent *ev) {
|
||||
int x = 0, y = 0;
|
||||
switch(which) {
|
||||
case 1:
|
||||
glfw_ibus_set_focused(&xkb->ibus, a ? true : false);
|
||||
switch(ev->type) {
|
||||
case GLFW_IME_UPDATE_FOCUS:
|
||||
glfw_ibus_set_focused(&xkb->ibus, ev->focused);
|
||||
break;
|
||||
case 2:
|
||||
case GLFW_IME_UPDATE_CURSOR_POSITION:
|
||||
_glfwPlatformGetWindowPos(w, &x, &y);
|
||||
x += a; y += b;
|
||||
glfw_ibus_set_cursor_geometry(&xkb->ibus, x, y, c, d);
|
||||
x += ev->cursor.left; y += ev->cursor.top;
|
||||
glfw_ibus_set_cursor_geometry(&xkb->ibus, x, y, ev->cursor.width, ev->cursor.height);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -545,9 +675,8 @@ glfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed) {
|
||||
_GLFWwindow *window = _glfwWindowForId(ev->window_id);
|
||||
if (failed && window && window->callbacks.keyboard) {
|
||||
// notify application to remove any existing pre-edit text
|
||||
GLFWkeyevent fake_ev;
|
||||
_glfwInitializeKeyEvent(&fake_ev, GLFW_KEY_UNKNOWN, 0, GLFW_PRESS, 0);
|
||||
fake_ev.ime_state = 1;
|
||||
GLFWkeyevent fake_ev = {.action = GLFW_PRESS};
|
||||
fake_ev.ime_state = GLFW_IME_PREEDIT_CHANGED;
|
||||
window->callbacks.keyboard((GLFWwindow*) window, &fake_ev);
|
||||
}
|
||||
static xkb_keycode_t last_handled_press_keycode = 0;
|
||||
@@ -566,7 +695,7 @@ glfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed) {
|
||||
format_mods(ev->glfw_ev.mods), ev->glfw_ev.text
|
||||
);
|
||||
|
||||
ev->glfw_ev.ime_state = 0;
|
||||
ev->glfw_ev.ime_state = GLFW_IME_NONE;
|
||||
_glfwInputKeyboard(window, &ev->glfw_ev);
|
||||
} else debug("↳ discarded\n");
|
||||
if (!is_release && handled_by_ime)
|
||||
@@ -577,16 +706,15 @@ void
|
||||
glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t xkb_keycode, int action) {
|
||||
static char key_text[64] = {0};
|
||||
const xkb_keysym_t *syms, *clean_syms, *default_syms;
|
||||
xkb_keysym_t xkb_sym;
|
||||
xkb_keysym_t xkb_sym, shifted_xkb_sym = XKB_KEY_NoSymbol, alternate_xkb_sym = XKB_KEY_NoSymbol;
|
||||
xkb_keycode_t code_for_sym = xkb_keycode, ibus_keycode = xkb_keycode;
|
||||
GLFWkeyevent glfw_ev;
|
||||
_glfwInitializeKeyEvent(&glfw_ev, GLFW_KEY_UNKNOWN, 0, GLFW_PRESS, 0); // init with default values
|
||||
GLFWkeyevent glfw_ev = {.action = GLFW_PRESS};
|
||||
#ifdef _GLFW_WAYLAND
|
||||
code_for_sym += 8;
|
||||
#else
|
||||
ibus_keycode -= 8;
|
||||
#endif
|
||||
debug("%s xkb_keycode: 0x%x ", action == GLFW_RELEASE ? "Release" : "Press", xkb_keycode);
|
||||
debug("%s xkb_keycode: 0x%x ", action == GLFW_RELEASE ? "\x1b[32mRelease\x1b[m" : "\x1b[31mPress\x1b[m", xkb_keycode);
|
||||
XKBStateGroup *sg = &xkb->states;
|
||||
int num_syms = xkb_state_key_get_syms(sg->state, code_for_sym, &syms);
|
||||
int num_clean_syms = xkb_state_key_get_syms(sg->clean_state, code_for_sym, &clean_syms);
|
||||
@@ -598,6 +726,7 @@ glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t
|
||||
return;
|
||||
}
|
||||
xkb_sym = clean_syms[0];
|
||||
shifted_xkb_sym = syms[0];
|
||||
debug("clean_sym: %s ", glfw_xkb_keysym_name(clean_syms[0]));
|
||||
if (action == GLFW_PRESS || action == GLFW_REPEAT) {
|
||||
const char *text_type = "composed_text";
|
||||
@@ -618,7 +747,7 @@ glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t
|
||||
if (consumed_unknown_mods) { debug("%s", format_xkb_mods(xkb, "consumed_unknown_mods", consumed_unknown_mods)); }
|
||||
else xkb_sym = clean_syms[0];
|
||||
// xkb returns text even if alt and/or super are pressed
|
||||
if ( ((GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SUPER) & sg->modifiers) == 0) {
|
||||
if ( ((GLFW_MOD_CONTROL | GLFW_MOD_ALT | GLFW_MOD_SUPER | GLFW_MOD_HYPER | GLFW_MOD_META) & sg->modifiers) == 0) {
|
||||
xkb_state_key_get_utf8(sg->state, code_for_sym, key_text, sizeof(key_text));
|
||||
}
|
||||
text_type = "text";
|
||||
@@ -631,22 +760,30 @@ glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t
|
||||
if (xkb_sym == XKB_KEY_ISO_First_Group || xkb_sym == XKB_KEY_ISO_Last_Group || xkb_sym == XKB_KEY_ISO_Next_Group || xkb_sym == XKB_KEY_ISO_Prev_Group || xkb_sym == XKB_KEY_Mode_switch) {
|
||||
return;
|
||||
}
|
||||
int glfw_sym = glfw_key_for_sym(xkb_sym);
|
||||
bool is_fallback = false;
|
||||
if (glfw_sym == GLFW_KEY_UNKNOWN && !key_text[0]) {
|
||||
int num_default_syms = xkb_state_key_get_syms(sg->default_state, code_for_sym, &default_syms);
|
||||
if (num_default_syms > 0) {
|
||||
xkb_sym = default_syms[0];
|
||||
glfw_sym = glfw_key_for_sym(xkb_sym);
|
||||
is_fallback = true;
|
||||
}
|
||||
if (sg->modifiers & GLFW_MOD_NUM_LOCK && XKB_KEY_KP_Space <= xkb_sym && xkb_sym <= XKB_KEY_KP_9) {
|
||||
xkb_sym = xkb_state_key_get_one_sym(sg->state, code_for_sym);
|
||||
}
|
||||
int num_default_syms = xkb_state_key_get_syms(sg->default_state, code_for_sym, &default_syms);
|
||||
if (num_default_syms > 0) alternate_xkb_sym = default_syms[0];
|
||||
int glfw_sym = glfw_key_for_sym(xkb_sym);
|
||||
|
||||
debug(
|
||||
"%s%s: %d (%s) xkb_key: %d (%s)\n",
|
||||
"%s%s: %d (%s) xkb_key: %d (%s)",
|
||||
format_mods(sg->modifiers),
|
||||
is_fallback ? "glfw_fallback_key" : "glfw_key", glfw_sym, _glfwGetKeyName(glfw_sym),
|
||||
"glfw_key", glfw_sym, _glfwGetKeyName(glfw_sym),
|
||||
xkb_sym, glfw_xkb_keysym_name(xkb_sym)
|
||||
);
|
||||
bool has_shifted_key = shifted_xkb_sym != xkb_sym && shifted_xkb_sym != XKB_KEY_NoSymbol;
|
||||
bool has_alternate_key = alternate_xkb_sym != xkb_sym && alternate_xkb_sym != XKB_KEY_NoSymbol;
|
||||
if (has_shifted_key) {
|
||||
glfw_ev.shifted_key = glfw_key_for_sym(shifted_xkb_sym);
|
||||
if (glfw_ev.shifted_key) debug(" shifted_key: %d (%s)", glfw_ev.shifted_key, _glfwGetKeyName(glfw_ev.shifted_key))
|
||||
}
|
||||
if (has_alternate_key) {
|
||||
glfw_ev.alternate_key = glfw_key_for_sym(alternate_xkb_sym);
|
||||
if (glfw_ev.alternate_key) debug(" alternate_key: %d (%s)", glfw_ev.alternate_key, _glfwGetKeyName(glfw_ev.alternate_key))
|
||||
}
|
||||
debug("%s", "\n");
|
||||
|
||||
// NOTE: On linux, the reported native key identifier is the XKB keysym value.
|
||||
// Do not confuse `native_key` with `xkb_keycode` (the native keycode reported for the
|
||||
@@ -662,7 +799,6 @@ glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t
|
||||
glfw_ev.key = glfw_sym;
|
||||
glfw_ev.mods = sg->modifiers;
|
||||
glfw_ev.text = key_text;
|
||||
|
||||
_GLFWIBUSKeyEvent ibus_ev;
|
||||
ibus_ev.glfw_ev = glfw_ev;
|
||||
ibus_ev.ibus_keycode = ibus_keycode;
|
||||
|
||||
8
glfw/xkb_glfw.h
vendored
8
glfw/xkb_glfw.h
vendored
@@ -54,12 +54,16 @@ typedef struct {
|
||||
xkb_mod_index_t altIdx;
|
||||
xkb_mod_index_t shiftIdx;
|
||||
xkb_mod_index_t superIdx;
|
||||
xkb_mod_index_t hyperIdx;
|
||||
xkb_mod_index_t metaIdx;
|
||||
xkb_mod_index_t capsLockIdx;
|
||||
xkb_mod_index_t numLockIdx;
|
||||
xkb_mod_mask_t controlMask;
|
||||
xkb_mod_mask_t altMask;
|
||||
xkb_mod_mask_t shiftMask;
|
||||
xkb_mod_mask_t superMask;
|
||||
xkb_mod_mask_t hyperMask;
|
||||
xkb_mod_mask_t metaMask;
|
||||
xkb_mod_mask_t capsLockMask;
|
||||
xkb_mod_mask_t numLockMask;
|
||||
xkb_mod_index_t unknownModifiers[256];
|
||||
@@ -89,8 +93,8 @@ bool glfw_xkb_compile_keymap(_GLFWXKBData *xkb, const char *map_str);
|
||||
void glfw_xkb_update_modifiers(_GLFWXKBData *xkb, xkb_mod_mask_t depressed, xkb_mod_mask_t latched, xkb_mod_mask_t locked, xkb_layout_index_t base_group, xkb_layout_index_t latched_group, xkb_layout_index_t locked_group);
|
||||
bool glfw_xkb_should_repeat(_GLFWXKBData *xkb, xkb_keycode_t keycode);
|
||||
const char* glfw_xkb_keysym_name(xkb_keysym_t sym);
|
||||
xkb_keysym_t glfw_xkb_sym_for_key(int key);
|
||||
xkb_keysym_t glfw_xkb_sym_for_key(uint32_t key);
|
||||
void glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t keycode, int action);
|
||||
int glfw_xkb_keysym_from_name(const char *name, bool case_sensitive);
|
||||
void glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, int which, int a, int b, int c, int d);
|
||||
void glfw_xkb_update_ime_state(_GLFWwindow *w, _GLFWXKBData *xkb, const GLFWIMEUpdateEvent *ev);
|
||||
void glfw_xkb_key_from_ime(_GLFWIBUSKeyEvent *ev, bool handled_by_ime, bool failed);
|
||||
|
||||
0
kittens/broadcast/__init__.py
Normal file
0
kittens/broadcast/__init__.py
Normal file
114
kittens/broadcast/main.py
Normal file
114
kittens/broadcast/main.py
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import sys
|
||||
from base64 import standard_b64encode
|
||||
from gettext import gettext as _
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from kitty.cli import parse_args
|
||||
from kitty.cli_stub import BroadcastCLIOptions
|
||||
from kitty.key_encoding import encode_key_event
|
||||
from kitty.rc.base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION
|
||||
from kitty.remote_control import create_basic_command, encode_send
|
||||
from kitty.typing import KeyEventType, ScreenSize
|
||||
|
||||
from ..tui.handler import Handler
|
||||
from ..tui.line_edit import LineEdit
|
||||
from ..tui.loop import Loop
|
||||
from ..tui.operations import RESTORE_CURSOR, SAVE_CURSOR, styled
|
||||
|
||||
|
||||
class Broadcast(Handler):
|
||||
|
||||
def __init__(self, opts: BroadcastCLIOptions, initial_strings: List[str]) -> None:
|
||||
self.opts = opts
|
||||
self.initial_strings = initial_strings
|
||||
self.payload = {'exclude_active': True, 'data': '', 'match': opts.match_tab, 'match_tab': opts.match_tab}
|
||||
self.line_edit = LineEdit()
|
||||
if not opts.match and not opts.match_tab:
|
||||
self.payload['all'] = True
|
||||
|
||||
def initialize(self) -> None:
|
||||
self.print('Type the text to broadcast below, press', styled('Ctrl+c', fg='yellow'), 'to quit:')
|
||||
for x in self.initial_strings:
|
||||
self.write_broadcast_text(x)
|
||||
self.write(SAVE_CURSOR)
|
||||
|
||||
def commit_line(self) -> None:
|
||||
self.write(RESTORE_CURSOR + SAVE_CURSOR)
|
||||
self.cmd.clear_to_end_of_screen()
|
||||
self.line_edit.write(self.write, screen_cols=self.screen_size.cols)
|
||||
|
||||
def on_resize(self, screen_size: ScreenSize) -> None:
|
||||
super().on_resize(screen_size)
|
||||
self.commit_line()
|
||||
|
||||
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
|
||||
self.write_broadcast_text(text)
|
||||
self.line_edit.on_text(text, in_bracketed_paste)
|
||||
self.commit_line()
|
||||
|
||||
def on_interrupt(self) -> None:
|
||||
self.quit_loop(0)
|
||||
|
||||
def on_eot(self) -> None:
|
||||
self.write_broadcast_text('\x04')
|
||||
|
||||
def on_key(self, key_event: KeyEventType) -> None:
|
||||
if self.line_edit.on_key(key_event):
|
||||
self.commit_line()
|
||||
if key_event.matches('enter'):
|
||||
self.write_broadcast_text('\r')
|
||||
self.print('')
|
||||
self.line_edit.clear()
|
||||
self.write(SAVE_CURSOR)
|
||||
return
|
||||
|
||||
ek = encode_key_event(key_event)
|
||||
ek = standard_b64encode(ek.encode('utf-8')).decode('ascii')
|
||||
self.write_broadcast_data('kitty-key:' + ek)
|
||||
|
||||
def write_broadcast_text(self, text: str) -> None:
|
||||
self.write_broadcast_data('base64:' + standard_b64encode(text.encode('utf-8')).decode('ascii'))
|
||||
|
||||
def write_broadcast_data(self, data: str) -> None:
|
||||
payload = self.payload.copy()
|
||||
payload['data'] = data
|
||||
send = create_basic_command('send-text', payload, no_response=True)
|
||||
self.write(encode_send(send))
|
||||
|
||||
|
||||
OPTIONS = (MATCH_WINDOW_OPTION + '\n\n' + MATCH_TAB_OPTION.replace('--match -m', '--match-tab -t')).format
|
||||
help_text = 'Broadcast typed text to all kitty windows. By default text is sent to all windows, unless one of the matching options is specified'
|
||||
usage = '[initial text to send ...]'
|
||||
|
||||
|
||||
def parse_broadcast_args(args: List[str]) -> Tuple[BroadcastCLIOptions, List[str]]:
|
||||
return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten broadcast', result_class=BroadcastCLIOptions)
|
||||
|
||||
|
||||
def main(args: List[str]) -> Optional[Dict[str, Any]]:
|
||||
try:
|
||||
opts, items = parse_broadcast_args(args[1:])
|
||||
except SystemExit as e:
|
||||
if e.code != 0:
|
||||
print(e.args[0], file=sys.stderr)
|
||||
input(_('Press Enter to quit'))
|
||||
return None
|
||||
|
||||
print('Type text to be broadcast below, Ctrl-C to quit:', end='\r\n')
|
||||
sys.stdout.flush()
|
||||
loop = Loop()
|
||||
handler = Broadcast(opts, items)
|
||||
loop.loop(handler)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
elif __name__ == '__doc__':
|
||||
cd = sys.cli_docs # type: ignore
|
||||
cd['usage'] = usage
|
||||
cd['options'] = OPTIONS
|
||||
cd['help_text'] = help_text
|
||||
@@ -30,8 +30,6 @@ typedef uint8_t len_t;
|
||||
typedef uint32_t text_t;
|
||||
|
||||
#define LEN_MAX UINT8_MAX
|
||||
#define UTF8_ACCEPT 0
|
||||
#define UTF8_REJECT 1
|
||||
#define IS_LOWERCASE(x) (x) >= 'a' && (x) <= 'z'
|
||||
#define IS_UPPERCASE(x) (x) >= 'A' && (x) <= 'Z'
|
||||
#define LOWERCASE(x) ((IS_UPPERCASE(x)) ? (x) + 32 : (x))
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "choose-data-types.h"
|
||||
#include "../../kitty/iqsort.h"
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
@@ -45,13 +46,6 @@ output_text(GlobalData *global, const text_t *data, size_t sz) {
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
cmpscore(const void *a, const void *b) {
|
||||
double sa = FIELD(a, score), sb = FIELD(b, score);
|
||||
// Sort descending
|
||||
return (sa > sb) ? -1 : ((sa == sb) ? ((int)FIELD(a, idx) - (int)FIELD(b, idx)) : 1);
|
||||
}
|
||||
|
||||
static void
|
||||
output_with_marks(GlobalData *global, Options *opts, text_t *src, size_t src_sz, len_t *positions, len_t poslen) {
|
||||
size_t pos, i = 0;
|
||||
@@ -96,7 +90,9 @@ output_result(GlobalData *global, Candidate *c, Options *opts, len_t needle_len)
|
||||
void
|
||||
output_results(GlobalData *global, Candidate *haystack, size_t count, Options *opts, len_t needle_len) {
|
||||
Candidate *c;
|
||||
qsort(haystack, count, sizeof(*haystack), cmpscore);
|
||||
#define lt(b, a) ( (a)->score < (b)->score || ((a)->score == (b)->score && (a->idx < b->idx)) )
|
||||
QSORT(Candidate, haystack, count, lt);
|
||||
#undef lt
|
||||
size_t left = opts->limit > 0 ? opts->limit : count;
|
||||
for (size_t i = 0; i < left; i++) {
|
||||
c = haystack + i;
|
||||
|
||||
@@ -7,7 +7,7 @@ import re
|
||||
from contextlib import suppress
|
||||
from functools import lru_cache
|
||||
from hashlib import md5
|
||||
from mimetypes import guess_type
|
||||
from kitty.guess_mime_type import guess_type
|
||||
from typing import TYPE_CHECKING, Dict, List, Set, Optional, Iterator, Tuple, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -15,6 +15,11 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
path_name_map: Dict[str, str] = {}
|
||||
remote_dirs: Dict[str, str] = {}
|
||||
|
||||
|
||||
def add_remote_dir(val: str) -> None:
|
||||
remote_dirs[val] = os.path.basename(val).rpartition('-')[-1]
|
||||
|
||||
|
||||
class Segment:
|
||||
@@ -68,7 +73,10 @@ class Collection:
|
||||
self.removed_count += len(lines_for_path(left_path))
|
||||
|
||||
def finalize(self) -> None:
|
||||
self.all_paths.sort(key=path_name_map.get)
|
||||
def key(x: str) -> str:
|
||||
return path_name_map.get(x, '')
|
||||
|
||||
self.all_paths.sort(key=key)
|
||||
|
||||
def __iter__(self) -> Iterator[Tuple[str, str, Optional[str]]]:
|
||||
for path in self.all_paths:
|
||||
@@ -85,6 +93,20 @@ class Collection:
|
||||
return len(self.all_paths)
|
||||
|
||||
|
||||
def remote_hostname(path: str) -> Tuple[Optional[str], Optional[str]]:
|
||||
for q in remote_dirs:
|
||||
if path.startswith(q):
|
||||
return q, remote_dirs[q]
|
||||
return None, None
|
||||
|
||||
|
||||
def resolve_remote_name(path: str, default: str) -> str:
|
||||
remote_dir, rh = remote_hostname(path)
|
||||
if remote_dir and rh:
|
||||
return rh + ':' + os.path.relpath(path, remote_dir)
|
||||
return default
|
||||
|
||||
|
||||
def collect_files(collection: Collection, left: str, right: str) -> None:
|
||||
left_names: Set[str] = set()
|
||||
right_names: Set[str] = set()
|
||||
@@ -133,7 +155,7 @@ def sanitize(text: str) -> str:
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
def mime_type_for_path(path: str) -> str:
|
||||
return guess_type(path)[0] or 'application/octet-stream'
|
||||
return guess_type(path) or 'application/octet-stream'
|
||||
|
||||
|
||||
@lru_cache(maxsize=1024)
|
||||
@@ -181,8 +203,8 @@ def create_collection(left: str, right: str) -> Collection:
|
||||
collect_files(collection, left, right)
|
||||
else:
|
||||
pl, pr = os.path.abspath(left), os.path.abspath(right)
|
||||
path_name_map[pl] = left
|
||||
path_name_map[pr] = right
|
||||
path_name_map[pl] = resolve_remote_name(pl, left)
|
||||
path_name_map[pr] = resolve_remote_name(pr, right)
|
||||
collection.add_change(pl, pr)
|
||||
collection.finalize()
|
||||
return collection
|
||||
|
||||
@@ -9,9 +9,8 @@ from gettext import gettext as _
|
||||
from typing import Any, Dict, Sequence, Union
|
||||
|
||||
from kitty.conf.definition import Option, Shortcut, option_func
|
||||
from kitty.conf.utils import (
|
||||
positive_int, python_string, to_color, to_color_or_none
|
||||
)
|
||||
from kitty.conf.utils import python_string, to_color, to_color_or_none
|
||||
from kitty.utils import positive_int
|
||||
|
||||
# }}}
|
||||
|
||||
@@ -89,7 +88,7 @@ c('hunk_bg', '#f1f8ff')
|
||||
c('search_bg', '#444', long_text=_('Highlighting'))
|
||||
c('search_fg', 'white')
|
||||
c('select_bg', '#b4d5fe')
|
||||
c('select_fg', 'black')
|
||||
o('select_fg', 'black', option_type=to_color_or_none)
|
||||
|
||||
g('shortcuts')
|
||||
k('quit', 'q', 'quit', _('Quit'))
|
||||
|
||||
@@ -13,21 +13,28 @@ from collections import defaultdict
|
||||
from contextlib import suppress
|
||||
from functools import partial
|
||||
from gettext import gettext as _
|
||||
from typing import DefaultDict, Dict, Iterable, List, Optional, Tuple, Union
|
||||
from typing import (
|
||||
Any, DefaultDict, Dict, Iterable, List, Optional, Tuple, Union
|
||||
)
|
||||
|
||||
from kitty.cli import CONFIG_HELP, parse_args
|
||||
from kitty.cli_stub import DiffCLIOptions
|
||||
from kitty.conf.utils import KittensKeyAction
|
||||
from kitty.constants import appname
|
||||
from kitty.fast_data_types import wcswidth
|
||||
from kitty.key_encoding import RELEASE, KeyEvent, enter_key, key_defs as K
|
||||
from kitty.key_encoding import EventType, KeyEvent
|
||||
from kitty.options_stub import DiffOptions
|
||||
from kitty.utils import ScreenSize
|
||||
|
||||
from ..tui.handler import Handler
|
||||
from ..tui.images import ImageManager, Placement
|
||||
from ..tui.line_edit import LineEdit
|
||||
from ..tui.loop import Loop
|
||||
from ..tui.operations import styled
|
||||
from . import global_data
|
||||
from .collect import (
|
||||
Collection, create_collection, data_for_path, lines_for_path, sanitize,
|
||||
set_highlight_data
|
||||
set_highlight_data, add_remote_dir
|
||||
)
|
||||
from .config import init_config
|
||||
from .patch import Differ, Patch, set_diff_command, worker_processes
|
||||
@@ -35,14 +42,11 @@ from .render import (
|
||||
ImagePlacement, ImageSupportWarning, Line, LineRef, Reference, render_diff
|
||||
)
|
||||
from .search import BadRegex, Search
|
||||
from ..tui.handler import Handler
|
||||
from ..tui.images import ImageManager, Placement
|
||||
from ..tui.line_edit import LineEdit
|
||||
from ..tui.loop import Loop
|
||||
from ..tui.operations import styled
|
||||
|
||||
try:
|
||||
from .highlight import initialize_highlighter, highlight_collection, DiffHighlight
|
||||
from .highlight import (
|
||||
DiffHighlight, highlight_collection, initialize_highlighter
|
||||
)
|
||||
has_highlighter = True
|
||||
DiffHighlight
|
||||
except ImportError:
|
||||
@@ -53,7 +57,6 @@ except ImportError:
|
||||
|
||||
|
||||
INITIALIZING, COLLECTED, DIFFED, COMMAND, MESSAGE = range(5)
|
||||
ESCAPE = K['ESCAPE']
|
||||
|
||||
|
||||
def generate_diff(collection: Collection, context: int) -> Union[str, Dict[str, Patch]]:
|
||||
@@ -90,7 +93,7 @@ class DiffHandler(Handler):
|
||||
self.highlighting_done = False
|
||||
self.restore_position: Optional[Reference] = None
|
||||
for key_def, action in self.opts.key_definitions.items():
|
||||
self.add_shortcut(action, *key_def)
|
||||
self.add_shortcut(action, key_def)
|
||||
|
||||
def perform_action(self, action: KittensKeyAction) -> None:
|
||||
func, args = action
|
||||
@@ -334,8 +337,8 @@ class DiffHandler(Handler):
|
||||
if candidate.image.image_id == image_id:
|
||||
q = self.xpos_for_image(row, candidate, is_left)
|
||||
if q is not None:
|
||||
pl['x'] = q[0]
|
||||
pl['y'] = row
|
||||
pl.x = q[0]
|
||||
pl.y = row
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -479,48 +482,44 @@ class DiffHandler(Handler):
|
||||
self.message = sanitize(_('No matches found'))
|
||||
self.cmd.bell()
|
||||
|
||||
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
|
||||
if self.state is COMMAND:
|
||||
self.line_edit.on_text(text, in_bracketed_paste)
|
||||
self.draw_status_line()
|
||||
return
|
||||
if self.state is MESSAGE:
|
||||
self.state = DIFFED
|
||||
self.draw_status_line()
|
||||
return
|
||||
action = self.shortcut_action(text)
|
||||
if action is not None:
|
||||
return self.perform_action(action)
|
||||
|
||||
def on_key(self, key_event: KeyEvent) -> None:
|
||||
if self.state is MESSAGE:
|
||||
if key_event.type is not RELEASE:
|
||||
def on_key_event(self, key_event: KeyEvent, in_bracketed_paste: bool = False) -> None:
|
||||
if key_event.text:
|
||||
if self.state is COMMAND:
|
||||
self.line_edit.on_text(key_event.text, in_bracketed_paste)
|
||||
self.draw_status_line()
|
||||
return
|
||||
if self.state is MESSAGE:
|
||||
self.state = DIFFED
|
||||
self.draw_status_line()
|
||||
return
|
||||
if self.state is COMMAND:
|
||||
if self.line_edit.on_key(key_event):
|
||||
if not self.line_edit.current_input:
|
||||
return
|
||||
else:
|
||||
if self.state is MESSAGE:
|
||||
if key_event.type is not EventType.RELEASE:
|
||||
self.state = DIFFED
|
||||
self.draw_status_line()
|
||||
self.draw_status_line()
|
||||
return
|
||||
if key_event.type is RELEASE:
|
||||
return
|
||||
if self.state is COMMAND:
|
||||
if key_event.key is ESCAPE:
|
||||
self.state = DIFFED
|
||||
self.draw_status_line()
|
||||
return
|
||||
if key_event is enter_key:
|
||||
self.state = DIFFED
|
||||
self.do_search()
|
||||
self.line_edit.clear()
|
||||
if self.state is COMMAND:
|
||||
if self.line_edit.on_key(key_event):
|
||||
if not self.line_edit.current_input:
|
||||
self.state = DIFFED
|
||||
self.draw_status_line()
|
||||
return
|
||||
if key_event.matches('enter'):
|
||||
self.state = DIFFED
|
||||
self.do_search()
|
||||
self.line_edit.clear()
|
||||
self.draw_screen()
|
||||
return
|
||||
if key_event.matches('esc'):
|
||||
self.state = DIFFED
|
||||
self.draw_status_line()
|
||||
return
|
||||
if self.state >= DIFFED and self.current_search is not None and key_event.matches('esc'):
|
||||
self.current_search = None
|
||||
self.draw_screen()
|
||||
return
|
||||
if self.state >= DIFFED and self.current_search is not None and key_event.key is ESCAPE:
|
||||
self.current_search = None
|
||||
self.draw_screen()
|
||||
return
|
||||
if key_event.type is EventType.RELEASE:
|
||||
return
|
||||
action = self.shortcut_action(key_event)
|
||||
if action is not None:
|
||||
return self.perform_action(action)
|
||||
@@ -566,8 +565,8 @@ class ShowWarning:
|
||||
def __init__(self) -> None:
|
||||
self.warnings: List[str] = []
|
||||
|
||||
def __call__(self, message: str, category: object, filename: str, lineno: int, file: object = None, line: object = None) -> None:
|
||||
if category is ImageSupportWarning:
|
||||
def __call__(self, message: Any, category: Any, filename: str, lineno: int, file: object = None, line: object = None) -> None:
|
||||
if category is ImageSupportWarning and isinstance(message, str):
|
||||
showwarning.warnings.append(message)
|
||||
|
||||
|
||||
@@ -582,17 +581,39 @@ def terminate_processes(processes: Iterable[int]) -> None:
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
|
||||
|
||||
def get_ssh_file(hostname: str, rpath: str) -> str:
|
||||
import io
|
||||
import shutil
|
||||
import tarfile
|
||||
tdir = tempfile.mkdtemp(suffix=f'-{hostname}')
|
||||
add_remote_dir(tdir)
|
||||
atexit.register(shutil.rmtree, tdir)
|
||||
is_abs = rpath.startswith('/')
|
||||
rpath = rpath.lstrip('/')
|
||||
cmd = ['ssh', hostname, 'tar', '-c', '-f', '-']
|
||||
if is_abs:
|
||||
cmd.extend(('-C', '/'))
|
||||
cmd.append(rpath)
|
||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||
assert p.stdout is not None
|
||||
raw = p.stdout.read()
|
||||
if p.wait() != 0:
|
||||
raise SystemExit(p.returncode)
|
||||
with tarfile.open(fileobj=io.BytesIO(raw), mode='r:') as tf:
|
||||
members = tf.getmembers()
|
||||
tf.extractall(tdir)
|
||||
if len(members) == 1:
|
||||
for root, dirs, files in os.walk(tdir):
|
||||
if files:
|
||||
return os.path.join(root, files[0])
|
||||
return os.path.abspath(os.path.join(tdir, rpath))
|
||||
|
||||
|
||||
def get_remote_file(path: str) -> str:
|
||||
if path.startswith('ssh:'):
|
||||
parts = path.split(':', 2)
|
||||
if len(parts) == 3:
|
||||
hostname, rpath = parts[1:]
|
||||
with tempfile.NamedTemporaryFile(suffix='-' + os.path.basename(rpath), prefix='remote:', delete=False) as tf:
|
||||
atexit.register(os.remove, tf.name)
|
||||
p = subprocess.Popen(['ssh', hostname, 'cat', rpath], stdout=tf)
|
||||
if p.wait() != 0:
|
||||
raise SystemExit(p.returncode)
|
||||
return tf.name
|
||||
return get_ssh_file(parts[1], parts[2])
|
||||
return path
|
||||
|
||||
|
||||
@@ -603,12 +624,12 @@ def main(args: List[str]) -> None:
|
||||
raise SystemExit('You must specify exactly two files/directories to compare')
|
||||
left, right = items
|
||||
global_data.title = _('{} vs. {}').format(left, right)
|
||||
if os.path.isdir(left) != os.path.isdir(right):
|
||||
raise SystemExit('The items to be diffed should both be either directories or files. Comparing a directory to a file is not valid.')
|
||||
opts = init_config(cli_opts)
|
||||
set_diff_command(opts.diff_cmd)
|
||||
lines_for_path.replace_tab_by = opts.replace_tab_by
|
||||
left, right = map(get_remote_file, (left, right))
|
||||
if os.path.isdir(left) != os.path.isdir(right):
|
||||
raise SystemExit('The items to be diffed should both be either directories or files. Comparing a directory to a file is not valid.')
|
||||
for f in left, right:
|
||||
if not os.path.exists(f):
|
||||
raise SystemExit('{} does not exist'.format(f))
|
||||
|
||||
@@ -3,16 +3,17 @@
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import warnings
|
||||
from functools import lru_cache
|
||||
from gettext import gettext as _
|
||||
from itertools import repeat, zip_longest
|
||||
from math import ceil
|
||||
from typing import Callable, Dict, Generator, Iterable, List, Optional, Tuple
|
||||
|
||||
from kitty.fast_data_types import truncate_point_for_length, wcswidth
|
||||
from kitty.cli_stub import DiffCLIOptions
|
||||
from kitty.fast_data_types import truncate_point_for_length, wcswidth
|
||||
from kitty.types import run_once
|
||||
from kitty.utils import ScreenSize
|
||||
|
||||
from ..tui.images import ImageManager, can_display_images
|
||||
from .collect import (
|
||||
Collection, Segment, data_for_path, highlights_for_path, is_image,
|
||||
lines_for_path, path_name_map, sanitize
|
||||
@@ -20,14 +21,13 @@ from .collect import (
|
||||
from .config import formats
|
||||
from .diff_speedup import split_with_highlights as _split_with_highlights
|
||||
from .patch import Chunk, Hunk, Patch
|
||||
from ..tui.images import ImageManager, can_display_images
|
||||
|
||||
|
||||
class ImageSupportWarning(Warning):
|
||||
pass
|
||||
|
||||
|
||||
@lru_cache(maxsize=2)
|
||||
@run_once
|
||||
def images_supported() -> bool:
|
||||
ans = can_display_images()
|
||||
if not ans:
|
||||
|
||||
@@ -17,9 +17,7 @@ from typing import (
|
||||
from kitty.cli import parse_args
|
||||
from kitty.cli_stub import HintsCLIOptions
|
||||
from kitty.fast_data_types import set_clipboard_string
|
||||
from kitty.key_encoding import (
|
||||
KeyEvent, backspace_key, enter_key, key_defs as K
|
||||
)
|
||||
from kitty.key_encoding import KeyEvent
|
||||
from kitty.typing import BossType, KittyCommonOpts
|
||||
from kitty.utils import ScreenSize, screen_size_function, set_primary_selection
|
||||
|
||||
@@ -40,7 +38,6 @@ def kitty_common_opts() -> KittyCommonOpts:
|
||||
|
||||
DEFAULT_HINT_ALPHABET = string.digits + string.ascii_lowercase
|
||||
DEFAULT_REGEX = r'(?m)^\s*(.+)\s*$'
|
||||
ESCAPE = K['ESCAPE']
|
||||
|
||||
|
||||
class Mark:
|
||||
@@ -120,7 +117,7 @@ class Hints(Handler):
|
||||
self.all_marks = all_marks
|
||||
self.ignore_mark_indices: Set[int] = set()
|
||||
self.args = args
|
||||
self.window_title = _('Choose URL') if args.type == 'url' else _('Choose text')
|
||||
self.window_title = args.window_title or (_('Choose URL') if args.type == 'url' else _('Choose text'))
|
||||
self.multiple = args.multiple
|
||||
self.match_suffix = self.get_match_suffix(args)
|
||||
self.chosen: List[Mark] = []
|
||||
@@ -177,11 +174,11 @@ class Hints(Handler):
|
||||
self.draw_screen()
|
||||
|
||||
def on_key(self, key_event: KeyEvent) -> None:
|
||||
if key_event is backspace_key:
|
||||
if key_event.matches('backspace'):
|
||||
self.current_input = self.current_input[:-1]
|
||||
self.current_text = None
|
||||
self.draw_screen()
|
||||
elif key_event is enter_key and self.current_input:
|
||||
elif key_event.matches('enter') and self.current_input:
|
||||
try:
|
||||
idx = decode_hint(self.current_input, self.alphabet)
|
||||
self.chosen.append(self.index_map[idx])
|
||||
@@ -196,7 +193,7 @@ class Hints(Handler):
|
||||
self.draw_screen()
|
||||
else:
|
||||
self.quit_loop(0)
|
||||
elif key_event.key is ESCAPE:
|
||||
elif key_event.matches('esc'):
|
||||
self.quit_loop(0 if self.multiple else 1)
|
||||
|
||||
def on_interrupt(self) -> None:
|
||||
@@ -288,6 +285,7 @@ def quotes(text: str, s: int, e: int) -> Tuple[int, int]:
|
||||
@postprocessor
|
||||
def ip(text: str, s: int, e: int) -> Tuple[int, int]:
|
||||
from ipaddress import ip_address
|
||||
|
||||
# Check validity of IPs (or raise InvalidMatch)
|
||||
ip = text[s:e]
|
||||
|
||||
@@ -320,7 +318,9 @@ def run_loop(args: HintsCLIOptions, text: str, all_marks: Sequence[Mark], index_
|
||||
return {
|
||||
'match': handler.text_matches, 'programs': args.program,
|
||||
'multiple_joiner': args.multiple_joiner, 'customize_processing': args.customize_processing,
|
||||
'type': args.type, 'groupdicts': handler.groupdicts, 'extra_cli_args': extra_cli_args, 'linenum_action': args.linenum_action
|
||||
'type': args.type, 'groupdicts': handler.groupdicts, 'extra_cli_args': extra_cli_args,
|
||||
'linenum_action': args.linenum_action,
|
||||
'cwd': os.getcwd(),
|
||||
}
|
||||
raise SystemExit(loop.return_code)
|
||||
|
||||
@@ -519,7 +519,7 @@ The type of text to search for. A value of :code:`linenum` is special, it looks
|
||||
for error messages using the pattern specified with :option:`--regex`, which
|
||||
must have the named groups, :code:`path` and :code:`line`. If not specified,
|
||||
will look for :code:`path:line`. The :option:`--linenum-action` option
|
||||
controls what to do with the selected error message, other options are ignored.
|
||||
controls where to display the selected error message, other options are ignored.
|
||||
|
||||
|
||||
--regex
|
||||
@@ -539,13 +539,13 @@ the form key=value.
|
||||
default=self
|
||||
type=choice
|
||||
choices=self,window,tab,os_window,background
|
||||
Where to perform the action on matched errors. :code:`self` means the current
|
||||
window, :code:`window` a new kitty window, :code:`tab` a new tab,
|
||||
:code:`os_window` a new OS window and :code:`background` run in the background.
|
||||
The action to perform on the matched errors. The actual action is whatever
|
||||
arguments are provided to the kitten, for example:
|
||||
:code:`kitty + kitten hints --type=linenum vim +{line} {path}`
|
||||
will open the matched path at the matched line number in vim. This option
|
||||
controls where the action is executed: :code:`self` means the current window,
|
||||
:code:`window` a new kitty window, :code:`tab` a new tab, :code:`os_window`
|
||||
a new OS window and :code:`background` run in the background.
|
||||
arguments are provided to the kitten, for example: :code:`kitty + kitten hints
|
||||
--type=linenum --linenum-action=tab vim +{line} {path}` will open the matched
|
||||
path at the matched line number in vim in a new kitty tab.
|
||||
|
||||
|
||||
--url-prefixes
|
||||
@@ -634,6 +634,9 @@ on selected matches. See https://sw.kovidgoyal.net/kitty/kittens/hints.html
|
||||
for details. You can also specify absolute paths to load the script from elsewhere.
|
||||
|
||||
|
||||
--window-title
|
||||
The window title for the hints window, default title is selected based on
|
||||
the type of text being hinted.
|
||||
'''.format(
|
||||
default_regex=DEFAULT_REGEX,
|
||||
line='{{line}}', path='{{path}}'
|
||||
@@ -695,7 +698,7 @@ def linenum_handle_result(args: List[str], data: Dict[str, Any], target_window_i
|
||||
w.paste_bytes(text + '\r')
|
||||
elif action == 'background':
|
||||
import subprocess
|
||||
subprocess.Popen(cmd)
|
||||
subprocess.Popen(cmd, cwd=data['cwd'])
|
||||
else:
|
||||
getattr(boss, {
|
||||
'window': 'new_window_with_cwd', 'tab': 'new_tab_with_cwd', 'os_window': 'new_os_window_with_cwd'
|
||||
@@ -750,14 +753,10 @@ def handle_result(args: List[str], data: Dict[str, Any], target_window_id: int,
|
||||
elif program == '*':
|
||||
set_primary_selection(joined_text())
|
||||
else:
|
||||
cwd = None
|
||||
w = boss.window_id_map.get(target_window_id)
|
||||
if w is not None:
|
||||
cwd = w.cwd_of_child
|
||||
if w is None:
|
||||
w = boss.active_window
|
||||
cwd = data['cwd']
|
||||
program = None if program == 'default' else program
|
||||
if text_type == 'hyperlink':
|
||||
w = boss.window_id_map.get(target_window_id)
|
||||
for m in matches:
|
||||
if w is not None:
|
||||
w.open_url(m, hyperlink_id=1, cwd=cwd)
|
||||
|
||||
@@ -3,15 +3,13 @@
|
||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import contextlib
|
||||
import mimetypes
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import signal
|
||||
import socket
|
||||
import sys
|
||||
import zlib
|
||||
from base64 import standard_b64encode
|
||||
from functools import lru_cache
|
||||
from math import ceil
|
||||
from tempfile import NamedTemporaryFile
|
||||
from typing import (
|
||||
@@ -21,14 +19,17 @@ from typing import (
|
||||
from kitty.cli import parse_args
|
||||
from kitty.cli_stub import IcatCLIOptions
|
||||
from kitty.constants import appname
|
||||
from kitty.guess_mime_type import guess_type
|
||||
from kitty.types import run_once
|
||||
from kitty.typing import GRT_f, GRT_t
|
||||
from kitty.utils import (
|
||||
TTYIO, ScreenSize, ScreenSizeGetter, fit_image, screen_size_function
|
||||
)
|
||||
|
||||
from ..tui.images import (
|
||||
ConvertFailed, GraphicsCommand, NoImageMagick, OpenFailed, convert, fsenc,
|
||||
identify
|
||||
ConvertFailed, Dispose, GraphicsCommand, NoImageMagick, OpenFailed,
|
||||
OutdatedImageMagick, RenderedImage, fsenc, identify,
|
||||
render_as_single_image, render_image
|
||||
)
|
||||
from ..tui.operations import clear_images_on_screen, raw_mode
|
||||
|
||||
@@ -102,7 +103,7 @@ but you can turn it off or on explicitly, if needed.
|
||||
|
||||
--silent
|
||||
type=bool-set
|
||||
Do not print out anything to stdout during operation.
|
||||
Do not print out anything to STDOUT during operation.
|
||||
|
||||
|
||||
--z-index -z
|
||||
@@ -112,9 +113,17 @@ a double minus for values under the threshold for drawing images under cell back
|
||||
colors. For example, --1 evaluates as -1,073,741,825.
|
||||
|
||||
|
||||
--loop -l
|
||||
default=-1
|
||||
type=int
|
||||
Number of times to loop animations. Negative values loop forever. Zero means
|
||||
only the first frame of the animation is displayed. Otherwise, the animation
|
||||
is looped the specified number of times.
|
||||
|
||||
|
||||
--hold
|
||||
type=bool-set
|
||||
Wait for keypress before exiting after displaying the images.
|
||||
Wait for a key press before exiting after displaying the images.
|
||||
'''
|
||||
|
||||
|
||||
@@ -134,7 +143,7 @@ def get_screen_size() -> ScreenSize:
|
||||
return screen_size()
|
||||
|
||||
|
||||
@lru_cache(maxsize=2)
|
||||
@run_once
|
||||
def options_spec() -> str:
|
||||
return OPTIONS.format(appname='{}-icat'.format(appname))
|
||||
|
||||
@@ -190,15 +199,20 @@ def set_cursor_for_place(place: 'Place', cmd: GraphicsCommand, width: int, heigh
|
||||
|
||||
|
||||
def write_chunked(cmd: GraphicsCommand, data: bytes) -> None:
|
||||
cmd = cmd.clone()
|
||||
if cmd.f != 100:
|
||||
data = zlib.compress(data)
|
||||
cmd.o = 'z'
|
||||
data = standard_b64encode(data)
|
||||
ac = cmd.a
|
||||
quiet = cmd.q
|
||||
while data:
|
||||
chunk, data = data[:4096], data[4096:]
|
||||
cmd.m = 1 if data else 0
|
||||
write_gr_cmd(cmd, chunk)
|
||||
cmd.clear()
|
||||
cmd.a = ac
|
||||
cmd.q = quiet
|
||||
|
||||
|
||||
def show(
|
||||
@@ -207,7 +221,8 @@ def show(
|
||||
fmt: 'GRT_f',
|
||||
transmit_mode: 'GRT_t' = 't',
|
||||
align: str = 'center',
|
||||
place: Optional['Place'] = None
|
||||
place: Optional['Place'] = None,
|
||||
use_number: int = 0
|
||||
) -> None:
|
||||
cmd = GraphicsCommand()
|
||||
cmd.a = 'T'
|
||||
@@ -215,6 +230,9 @@ def show(
|
||||
cmd.s = width
|
||||
cmd.v = height
|
||||
cmd.z = zindex
|
||||
if use_number:
|
||||
cmd.I = use_number # noqa
|
||||
cmd.q = 2
|
||||
if place:
|
||||
set_cursor_for_place(place, cmd, width, height, align)
|
||||
else:
|
||||
@@ -232,6 +250,58 @@ def show(
|
||||
write_chunked(cmd, data)
|
||||
|
||||
|
||||
def show_frames(frame_data: RenderedImage, use_number: int, loops: int) -> None:
|
||||
transmit_cmd = GraphicsCommand()
|
||||
transmit_cmd.a = 'f'
|
||||
transmit_cmd.I = use_number # noqa
|
||||
transmit_cmd.q = 2
|
||||
if can_transfer_with_files:
|
||||
transmit_cmd.t = 't'
|
||||
transmit_cmd.f = 24 if frame_data.mode == 'rgb' else 32
|
||||
|
||||
def control(frame_number: int = 0, loops: Optional[int] = None, gap: Optional[int] = 0, animation_control: int = 0) -> None:
|
||||
cmd = GraphicsCommand()
|
||||
cmd.a = 'a'
|
||||
cmd.I = use_number # noqa
|
||||
cmd.r = frame_number
|
||||
if loops is not None:
|
||||
cmd.v = loops + 1
|
||||
if gap is not None:
|
||||
cmd.z = gap if gap > 0 else -1
|
||||
if animation_control:
|
||||
cmd.s = animation_control
|
||||
write_gr_cmd(cmd)
|
||||
|
||||
anchor_frame = 0
|
||||
|
||||
for frame in frame_data.frames:
|
||||
frame_number = frame.index + 1
|
||||
if frame.dispose < Dispose.previous:
|
||||
anchor_frame = frame_number
|
||||
if frame_number == 1:
|
||||
control(frame_number, gap=frame.gap, loops=None if loops < 1 else loops)
|
||||
continue
|
||||
if frame.dispose is Dispose.previous:
|
||||
if anchor_frame != frame_number:
|
||||
transmit_cmd.c = anchor_frame
|
||||
else:
|
||||
transmit_cmd.c = (frame_number - 1) if frame.needs_blend else 0
|
||||
transmit_cmd.s = frame.width
|
||||
transmit_cmd.v = frame.height
|
||||
transmit_cmd.x = frame.canvas_x
|
||||
transmit_cmd.y = frame.canvas_y
|
||||
transmit_cmd.z = frame.gap if frame.gap > 0 else -1
|
||||
if can_transfer_with_files:
|
||||
write_gr_cmd(transmit_cmd, standard_b64encode(os.path.abspath(frame.path).encode(fsenc)))
|
||||
else:
|
||||
with open(frame.path, 'rb') as f:
|
||||
data = f.read()
|
||||
write_chunked(transmit_cmd, data)
|
||||
if frame_number == 2:
|
||||
control(animation_control=2)
|
||||
control(animation_control=3)
|
||||
|
||||
|
||||
def parse_z_index(val: str) -> int:
|
||||
origin = 0
|
||||
if val.startswith('--'):
|
||||
@@ -254,6 +324,7 @@ def process(path: str, args: IcatCLIOptions, parsed_opts: ParsedOpts, is_tempfil
|
||||
needs_scaling = m.width > available_width or m.height > available_height
|
||||
needs_scaling = needs_scaling or args.scale_up
|
||||
file_removed = False
|
||||
use_number = 0
|
||||
if m.fmt == 'png' and not needs_scaling:
|
||||
outfile = path
|
||||
transmit_mode: 'GRT_t' = 't' if is_tempfile else 'f'
|
||||
@@ -263,8 +334,25 @@ def process(path: str, args: IcatCLIOptions, parsed_opts: ParsedOpts, is_tempfil
|
||||
else:
|
||||
fmt = 24 if m.mode == 'rgb' else 32
|
||||
transmit_mode = 't'
|
||||
outfile, width, height = convert(path, m, available_width, available_height, args.scale_up)
|
||||
show(outfile, width, height, parsed_opts.z_index, fmt, transmit_mode, align=args.align, place=parsed_opts.place)
|
||||
if len(m) == 1 or args.loop == 0:
|
||||
outfile, width, height = render_as_single_image(path, m, available_width, available_height, args.scale_up)
|
||||
else:
|
||||
import struct
|
||||
use_number = max(1, struct.unpack('@I', os.urandom(4))[0])
|
||||
with NamedTemporaryFile() as f:
|
||||
prefix = f.name
|
||||
frame_data = render_image(path, prefix, m, available_width, available_height, args.scale_up)
|
||||
outfile, width, height = frame_data.frames[0].path, frame_data.width, frame_data.height
|
||||
show(
|
||||
outfile, width, height, parsed_opts.z_index, fmt, transmit_mode,
|
||||
align=args.align, place=parsed_opts.place, use_number=use_number
|
||||
)
|
||||
if use_number:
|
||||
show_frames(frame_data, use_number, args.loop)
|
||||
if not can_transfer_with_files:
|
||||
for fr in frame_data.frames:
|
||||
with contextlib.suppress(FileNotFoundError):
|
||||
os.unlink(fr.path)
|
||||
if not args.place:
|
||||
print() # ensure cursor is on a new line
|
||||
return file_removed
|
||||
@@ -273,7 +361,7 @@ def process(path: str, args: IcatCLIOptions, parsed_opts: ParsedOpts, is_tempfil
|
||||
def scan(d: str) -> Generator[Tuple[str, str], None, None]:
|
||||
for dirpath, dirnames, filenames in os.walk(d):
|
||||
for f in filenames:
|
||||
mt = mimetypes.guess_type(f)[0]
|
||||
mt = guess_type(f)
|
||||
if mt and mt.startswith('image/'):
|
||||
yield os.path.join(dirpath, f), mt
|
||||
|
||||
@@ -472,6 +560,9 @@ def main(args: List[str] = sys.argv) -> None:
|
||||
process_single_item(item, cli_opts, parsed_opts, url_pat)
|
||||
except NoImageMagick as e:
|
||||
raise SystemExit(str(e))
|
||||
except OutdatedImageMagick as e:
|
||||
print(e.detailed_error, file=sys.stderr)
|
||||
raise SystemExit(str(e))
|
||||
except ConvertFailed as e:
|
||||
raise SystemExit(str(e))
|
||||
except OpenFailed as e:
|
||||
|
||||
@@ -6,8 +6,8 @@ import sys
|
||||
from typing import List
|
||||
|
||||
from kitty.key_encoding import (
|
||||
ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, SUPER, KeyEvent,
|
||||
encode_key_event
|
||||
ALT, CAPS_LOCK, CTRL, HYPER, META, NUM_LOCK, PRESS, RELEASE, REPEAT, SHIFT,
|
||||
SUPER, KeyEvent, encode_key_event
|
||||
)
|
||||
|
||||
from ..tui.handler import Handler
|
||||
@@ -21,10 +21,7 @@ class KeysHandler(Handler):
|
||||
self.cmd.set_cursor_visible(False)
|
||||
self.print('Press any keys - Ctrl+C or Ctrl+D will terminate')
|
||||
|
||||
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
|
||||
self.print('Text input: ' + text)
|
||||
|
||||
def on_key(self, key_event: KeyEvent) -> None:
|
||||
def on_key_event(self, key_event: KeyEvent, in_bracketed_paste: bool = False) -> None:
|
||||
etype = {
|
||||
PRESS: 'PRESS',
|
||||
REPEAT: 'REPEAT',
|
||||
@@ -35,13 +32,35 @@ class KeysHandler(Handler):
|
||||
SHIFT: 'Shift',
|
||||
ALT: 'Alt',
|
||||
CTRL: 'Ctrl',
|
||||
SUPER: 'Super'}.items():
|
||||
SUPER: 'Super',
|
||||
HYPER: 'Hyper',
|
||||
META: 'Meta',
|
||||
NUM_LOCK: 'NumLock',
|
||||
CAPS_LOCK: 'CapsLock',
|
||||
}.items():
|
||||
if key_event.mods & m:
|
||||
lmods.append(name)
|
||||
mods = '+'.join(lmods)
|
||||
if mods:
|
||||
mods += '+'
|
||||
self.print('Key {}: {}{} [{}]'.format(etype, mods, key_event.key, encode_key_event(key_event)))
|
||||
key = f'{mods}{key_event.key} '
|
||||
self.cmd.colored(key, 'green')
|
||||
self.cmd.colored(etype + ' ', 'yellow')
|
||||
self.cmd.styled(key_event.text, italic=True)
|
||||
self.print()
|
||||
rep = 'CSI ' + encode_key_event(key_event)[2:]
|
||||
rep = rep.replace(';', ' ; ').replace(':', ' : ')[:-1] + ' ' + rep[-1]
|
||||
self.cmd.styled(rep, fg='magenta')
|
||||
if (key_event.shifted_key or key_event.alternate_key):
|
||||
self.print()
|
||||
if key_event.shifted_key:
|
||||
self.cmd.colored('Shifted key: ', 'gray')
|
||||
self.print(key_event.shifted_key + ' ', end='')
|
||||
if key_event.alternate_key:
|
||||
self.cmd.colored('Alternate key: ', 'gray')
|
||||
self.print(key_event.alternate_key + ' ', end='')
|
||||
self.print()
|
||||
self.print()
|
||||
|
||||
def on_interrupt(self) -> None:
|
||||
self.quit_loop(0)
|
||||
|
||||
@@ -10,7 +10,7 @@ from typing import Callable, Dict, List, Tuple
|
||||
|
||||
from kitty.cli import parse_args
|
||||
from kitty.cli_stub import PanelCLIOptions
|
||||
from kitty.constants import is_macos
|
||||
from kitty.constants import is_macos, appname
|
||||
from kitty.os_window_size import WindowSizeData
|
||||
|
||||
OPTIONS = r'''
|
||||
@@ -42,7 +42,19 @@ Path to config file to use for kitty when drawing the panel.
|
||||
type=list
|
||||
Override individual kitty configuration options, can be specified multiple times.
|
||||
Syntax: :italic:`name=value`. For example: :option:`kitty +kitten panel -o` font_size=20
|
||||
'''.format
|
||||
|
||||
|
||||
--class
|
||||
dest=cls
|
||||
default={appname}-panel
|
||||
condition=not is_macos
|
||||
Set the class part of the :italic:`WM_CLASS` window property. On Wayland, it sets the app id.
|
||||
|
||||
|
||||
--name
|
||||
condition=not is_macos
|
||||
Set the name part of the :italic:`WM_CLASS` property (defaults to using the value from :option:`{appname} --class`)
|
||||
'''.format(appname=appname).format
|
||||
|
||||
|
||||
args = PanelCLIOptions()
|
||||
@@ -109,19 +121,17 @@ def setup_x11_window(win_id: int) -> None:
|
||||
|
||||
|
||||
def initial_window_size_func(opts: WindowSizeData, cached_values: Dict) -> Callable[[int, int, float, float, float, float], Tuple[int, int]]:
|
||||
from kitty.fast_data_types import glfw_primary_monitor_size, set_smallest_allowed_resize
|
||||
from kitty.fast_data_types import glfw_primary_monitor_size
|
||||
|
||||
def initial_window_size(cell_width: int, cell_height: int, dpi_x: float, dpi_y: float, xscale: float, yscale: float) -> Tuple[int, int]:
|
||||
global window_width, window_height
|
||||
monitor_width, monitor_height = glfw_primary_monitor_size()
|
||||
if args.edge in {'top', 'bottom'}:
|
||||
h = window_height = cell_height * args.lines + 1
|
||||
window_height = cell_height * args.lines + 1
|
||||
window_width = monitor_width
|
||||
set_smallest_allowed_resize(100, h)
|
||||
else:
|
||||
w = window_width = cell_width * args.columns + 1
|
||||
window_width = cell_width * args.columns + 1
|
||||
window_height = monitor_height
|
||||
set_smallest_allowed_resize(w, 100)
|
||||
return window_width, window_height
|
||||
|
||||
return initial_window_size
|
||||
@@ -138,9 +148,12 @@ def main(sys_args: List[str]) -> None:
|
||||
raise SystemExit('You must specify the program to run')
|
||||
sys.argv = ['kitty']
|
||||
for config in args.config:
|
||||
sys.argv.append('--config={}'.format(config))
|
||||
sys.argv.extend(('--config', config))
|
||||
sys.argv.extend(('--class', args.cls))
|
||||
if args.name:
|
||||
sys.argv.extend(('--name', args.name))
|
||||
for override in args.override:
|
||||
sys.argv.append('--override={}'.format(override))
|
||||
sys.argv.extend(('--override', override))
|
||||
sys.argv.extend(items)
|
||||
from kitty.main import run_app, main as real_main
|
||||
run_app.cached_values_name = 'panel'
|
||||
|
||||
0
kittens/query_terminal/__init__.py
Normal file
0
kittens/query_terminal/__init__.py
Normal file
162
kittens/query_terminal/main.py
Normal file
162
kittens/query_terminal/main.py
Normal file
@@ -0,0 +1,162 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import re
|
||||
import sys
|
||||
from binascii import unhexlify, hexlify
|
||||
from contextlib import suppress
|
||||
from typing import Dict, Iterable, List, Type
|
||||
|
||||
from kitty.cli import parse_args
|
||||
from kitty.cli_stub import QueryTerminalCLIOptions
|
||||
from kitty.constants import appname
|
||||
from kitty.utils import TTYIO
|
||||
from kitty.terminfo import names
|
||||
|
||||
|
||||
class Query:
|
||||
name: str = ''
|
||||
ans: str = ''
|
||||
query_name: str = ''
|
||||
help_text: str = ''
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.encoded_query_name = hexlify(self.query_name.encode('utf-8')).decode('ascii')
|
||||
self.pat = re.compile('\x1bP([01])\\+r{}(.*?)\x1b\\\\'.format(self.encoded_query_name).encode('ascii'))
|
||||
|
||||
def query_code(self) -> str:
|
||||
return "\x1bP+q{}\x1b\\".format(self.encoded_query_name)
|
||||
|
||||
def decode_response(self, res: bytes) -> str:
|
||||
return unhexlify(res).decode('utf-8')
|
||||
|
||||
def more_needed(self, buffer: bytes) -> bool:
|
||||
m = self.pat.search(buffer)
|
||||
if m is None:
|
||||
return True
|
||||
if m.group(1) == b'1':
|
||||
q = m.group(2)
|
||||
if q.startswith(b'='):
|
||||
with suppress(Exception):
|
||||
self.ans = self.decode_response(memoryview(q)[1:])
|
||||
return False
|
||||
|
||||
def output_line(self) -> str:
|
||||
return self.ans
|
||||
|
||||
|
||||
all_queries: Dict[str, Type[Query]] = {}
|
||||
|
||||
|
||||
def query(cls: Type[Query]) -> Type[Query]:
|
||||
all_queries[cls.name] = cls
|
||||
return cls
|
||||
|
||||
|
||||
@query
|
||||
class TerminalName(Query):
|
||||
name: str = 'name'
|
||||
query_name: str = 'TN'
|
||||
help_text: str = f'Terminal name ({names[0]})'
|
||||
|
||||
|
||||
@query
|
||||
class TerminalVersion(Query):
|
||||
name: str = 'version'
|
||||
query_name: str = 'kitty-query-version'
|
||||
help_text: str = 'Terminal version, for e.g.: 0.19.2'
|
||||
|
||||
|
||||
@query
|
||||
class AllowHyperlinks(Query):
|
||||
name: str = 'allow_hyperlinks'
|
||||
query_name: str = 'kitty-query-allow_hyperlinks'
|
||||
help_text: str = 'yes, no or ask'
|
||||
|
||||
|
||||
def do_queries(queries: Iterable, cli_opts: QueryTerminalCLIOptions) -> Dict[str, str]:
|
||||
actions = tuple(all_queries[x]() for x in queries)
|
||||
qstring = ''.join(a.query_code() for a in actions)
|
||||
received = b''
|
||||
|
||||
def more_needed(data: bytes) -> bool:
|
||||
nonlocal received
|
||||
received += data
|
||||
for a in actions:
|
||||
if a.more_needed(received):
|
||||
return True
|
||||
return False
|
||||
|
||||
with TTYIO() as ttyio:
|
||||
ttyio.send(qstring)
|
||||
ttyio.recv(more_needed, timeout=cli_opts.wait_for)
|
||||
|
||||
return {a.name: a.output_line() for a in actions}
|
||||
|
||||
|
||||
def options_spec() -> str:
|
||||
return '''\
|
||||
--wait-for
|
||||
type=float
|
||||
default=10
|
||||
The amount of time (in seconds) to wait for a response from the terminal, after
|
||||
querying it.
|
||||
'''
|
||||
|
||||
|
||||
help_text = '''\
|
||||
Query the terminal this kitten is run in for various
|
||||
capabilities. This sends escape codes to the terminal
|
||||
and based on its response prints out data about supported
|
||||
capabilities. Note that this is a blocking operation, since
|
||||
it has to wait for a response from the terminal. You can control
|
||||
the maximum wait time via the ``--wait-for`` option.
|
||||
|
||||
The output is lines of the form::
|
||||
|
||||
query: data
|
||||
|
||||
If a particular query is unsupported by the running kitty version,
|
||||
the data will be blank.
|
||||
|
||||
Note that when calling this from another program, be very
|
||||
careful not to perform any I/O on the terminal device
|
||||
until the kitten exits.
|
||||
|
||||
Available queries are::
|
||||
|
||||
{}
|
||||
'''.format(' ' + '\n '.join(
|
||||
f'{name}: {c.help_text}' for name, c in all_queries.items()))
|
||||
usage = '[query1 query2 ...]'
|
||||
|
||||
|
||||
def main(args: List[str] = sys.argv) -> None:
|
||||
cli_opts, items_ = parse_args(
|
||||
args[1:],
|
||||
options_spec,
|
||||
usage,
|
||||
help_text,
|
||||
'{} +kitten query_terminal'.format(appname),
|
||||
result_class=QueryTerminalCLIOptions
|
||||
)
|
||||
queries: List[str] = list(items_)
|
||||
if 'all' in queries or not queries:
|
||||
queries = sorted(all_queries)
|
||||
else:
|
||||
extra = frozenset(queries) - frozenset(all_queries)
|
||||
if extra:
|
||||
raise SystemExit(f'Unknown queries: {", ".join(extra)}')
|
||||
|
||||
for key, val in do_queries(queries, cli_opts).items():
|
||||
print(key + ':', val)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
elif __name__ == '__doc__':
|
||||
cd = sys.cli_docs # type: ignore
|
||||
cd['usage'] = usage
|
||||
cd['options'] = options_spec
|
||||
cd['help_text'] = help_text
|
||||
@@ -9,7 +9,7 @@ from typing import Any, Dict, List, Optional
|
||||
from kitty.cli import parse_args
|
||||
from kitty.cli_stub import RCOptions, ResizeCLIOptions
|
||||
from kitty.constants import version
|
||||
from kitty.key_encoding import CTRL, RELEASE, KeyEvent, key_defs as K
|
||||
from kitty.key_encoding import CTRL, EventType, KeyEvent
|
||||
from kitty.rc.base import command_for_name, parse_subcommand_cli
|
||||
from kitty.remote_control import encode_send, parse_rc_args
|
||||
from kitty.utils import ScreenSize
|
||||
@@ -19,11 +19,6 @@ from ..tui.loop import Loop
|
||||
from ..tui.operations import styled
|
||||
|
||||
global_opts = RCOptions()
|
||||
ESCAPE = K['ESCAPE']
|
||||
N = K['N']
|
||||
S = K['S']
|
||||
T = K['T']
|
||||
W = K['W']
|
||||
|
||||
|
||||
class Resize(Handler):
|
||||
@@ -74,12 +69,13 @@ class Resize(Handler):
|
||||
self.quit_loop(0)
|
||||
|
||||
def on_key(self, key_event: KeyEvent) -> None:
|
||||
if key_event.type is RELEASE:
|
||||
if key_event.type is EventType.RELEASE:
|
||||
return
|
||||
if key_event.key is ESCAPE:
|
||||
if key_event.matches('esc'):
|
||||
self.quit_loop(0)
|
||||
elif key_event.key in (W, N, T, S) and key_event.mods & CTRL:
|
||||
self.do_window_resize(is_decrease=key_event.key in (N, S), is_horizontal=key_event.key in (W, N), multiplier=2)
|
||||
return
|
||||
if key_event.key in ('w', 'n', 't', 's') and key_event.mods == CTRL:
|
||||
self.do_window_resize(is_decrease=key_event.key in 'ns', is_horizontal=key_event.key in 'wn', multiplier=2)
|
||||
|
||||
def on_resize(self, new_size: ScreenSize) -> None:
|
||||
self.draw_screen()
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
import importlib
|
||||
import os
|
||||
import sys
|
||||
from functools import lru_cache, partial
|
||||
from functools import partial
|
||||
from typing import Any, Dict, FrozenSet, List
|
||||
|
||||
from kitty.types import run_once
|
||||
|
||||
aliases = {'url_hints': 'hints'}
|
||||
|
||||
|
||||
@@ -57,8 +59,9 @@ def create_kitten_handler(kitten: str, orig_args: List[str]) -> Any:
|
||||
|
||||
|
||||
def set_debug(kitten: str) -> None:
|
||||
from kittens.tui.loop import debug
|
||||
import builtins
|
||||
|
||||
from kittens.tui.loop import debug
|
||||
setattr(builtins, 'debug', debug)
|
||||
|
||||
|
||||
@@ -114,20 +117,21 @@ def run_kitten(kitten: str, run_name: str = '__main__') -> None:
|
||||
for kitten in all_kitten_names():
|
||||
print(kitten, file=sys.stderr)
|
||||
raise SystemExit('No kitten named {}'.format(original_kitten_name))
|
||||
m = runpy.run_path(path, init_globals={'sys': sys, 'os': os}, run_name='__run_kitten__')
|
||||
m = runpy.run_path(path, init_globals={'sys': sys, 'os': os}, run_name='__run_kitten__') # type: ignore
|
||||
m['main'](sys.argv)
|
||||
|
||||
|
||||
@lru_cache(maxsize=2)
|
||||
@run_once
|
||||
def all_kitten_names() -> FrozenSet[str]:
|
||||
n = []
|
||||
import glob
|
||||
base = os.path.dirname(os.path.abspath(__file__))
|
||||
for x in glob.glob(os.path.join(base, '*', '__init__.py')):
|
||||
q = os.path.basename(os.path.dirname(x))
|
||||
if q != 'tui':
|
||||
n.append(q)
|
||||
return frozenset(n)
|
||||
try:
|
||||
from importlib.resources import contents
|
||||
except ImportError:
|
||||
from importlib_resources import contents # type: ignore
|
||||
ans = []
|
||||
for name in contents('kittens'):
|
||||
if '__' not in name and '.' not in name and name != 'tui':
|
||||
ans.append(name)
|
||||
return frozenset(ans)
|
||||
|
||||
|
||||
def list_kittens() -> None:
|
||||
|
||||
@@ -46,6 +46,33 @@ exec -a "-$shell_name" "$0"
|
||||
'''
|
||||
|
||||
|
||||
PYTHON_SCRIPT = '''\
|
||||
#!/usr/bin/env python
|
||||
from __future__ import print_function
|
||||
from tempfile import NamedTemporaryFile
|
||||
import subprocess, os, sys, pwd, binascii, json
|
||||
|
||||
# macOS ships with an ancient version of tic that cannot read from stdin, so we
|
||||
# create a temp file for it
|
||||
with NamedTemporaryFile() as tmp:
|
||||
tmp.write(binascii.unhexlify('{terminfo}'))
|
||||
p = subprocess.Popen(['tic', '-x', '-o', os.path.expanduser('~/.terminfo'), tmp.name], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate()
|
||||
if p.wait() != 0:
|
||||
getattr(sys.stderr, 'buffer', sys.stderr).write(stdout + stderr)
|
||||
raise SystemExit('Failed to compile terminfo using tic')
|
||||
command_to_execute = json.loads(binascii.unhexlify('{command_to_execute}'))
|
||||
if command_to_execute:
|
||||
os.execlp(command_to_execute[0], *command_to_execute)
|
||||
try:
|
||||
shell_path = pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh'
|
||||
except KeyError:
|
||||
shell_path = '/bin/sh'
|
||||
shell_name = '-' + os.path.basename(shell_path)
|
||||
os.execlp(shell_path, shell_name)
|
||||
'''
|
||||
|
||||
|
||||
def get_ssh_cli() -> Tuple[Set[str], Set[str]]:
|
||||
other_ssh_args: List[str] = []
|
||||
boolean_ssh_args: List[str] = []
|
||||
@@ -118,15 +145,14 @@ def parse_ssh_args(args: List[str]) -> Tuple[List[str], List[str], bool]:
|
||||
if arg in boolean_ssh_args:
|
||||
ssh_args.append(arg)
|
||||
continue
|
||||
if arg.startswith('-p') and arg[2:].isdigit():
|
||||
ssh_args.append(arg)
|
||||
continue
|
||||
if arg in other_ssh_args:
|
||||
if i != len(all_args) - 1:
|
||||
raise SystemExit('Option {} cannot occur in the middle'.format(arg))
|
||||
ssh_args.append(arg)
|
||||
expecting_option_val = True
|
||||
continue
|
||||
rest = all_args[i+1:]
|
||||
if rest:
|
||||
ssh_args.append(rest)
|
||||
else:
|
||||
expecting_option_val = True
|
||||
break
|
||||
raise SystemExit('Unknown option: {}'.format(arg))
|
||||
continue
|
||||
if expecting_option_val:
|
||||
@@ -151,20 +177,42 @@ def quote(x: str) -> str:
|
||||
return x
|
||||
|
||||
|
||||
def main(args: List[str]) -> NoReturn:
|
||||
ssh_args, server_args, passthrough = parse_ssh_args(args[1:])
|
||||
if passthrough:
|
||||
cmd = ['ssh'] + ssh_args + server_args
|
||||
def get_posix_cmd(terminfo: str, remote_args: List[str]) -> List[str]:
|
||||
sh_script = SHELL_SCRIPT.replace('TERMINFO', terminfo, 1)
|
||||
if remote_args:
|
||||
command_to_executeg = (quote(c) for c in remote_args)
|
||||
command_to_execute = 'exec ' + ' '.join(command_to_executeg)
|
||||
else:
|
||||
command_to_execute = ''
|
||||
sh_script = sh_script.replace('EXEC_CMD', command_to_execute)
|
||||
return [sh_script] + remote_args
|
||||
|
||||
|
||||
def get_python_cmd(terminfo: str, command_to_execute: List[str]) -> List[str]:
|
||||
import json
|
||||
script = PYTHON_SCRIPT.format(
|
||||
terminfo=terminfo.encode('utf-8').hex(),
|
||||
command_to_execute=json.dumps(command_to_execute).encode('utf-8').hex()
|
||||
)
|
||||
return [f'python -c "{script}"']
|
||||
|
||||
|
||||
def main(args: List[str]) -> NoReturn:
|
||||
args = args[1:]
|
||||
use_posix = True
|
||||
if args and args[0] == 'use-python':
|
||||
args = args[1:]
|
||||
use_posix = False
|
||||
ssh_args, server_args, passthrough = parse_ssh_args(args)
|
||||
cmd = ['ssh'] + ssh_args
|
||||
if passthrough:
|
||||
cmd += server_args
|
||||
else:
|
||||
hostname, remote_args = server_args[0], server_args[1:]
|
||||
cmd += ['-t', hostname]
|
||||
terminfo = subprocess.check_output(['infocmp']).decode('utf-8')
|
||||
sh_script = SHELL_SCRIPT.replace('TERMINFO', terminfo, 1)
|
||||
if len(server_args) > 1:
|
||||
command_to_executeg = (quote(c) for c in server_args[1:])
|
||||
command_to_execute = 'exec ' + ' '.join(command_to_executeg)
|
||||
else:
|
||||
command_to_execute = ''
|
||||
sh_script = sh_script.replace('EXEC_CMD', command_to_execute)
|
||||
cmd = ['ssh'] + ssh_args + ['-t', server_args[0], sh_script] + server_args[1:]
|
||||
f = get_posix_cmd if use_posix else get_python_cmd
|
||||
cmd += f(terminfo, remote_args)
|
||||
os.execvp('ssh', cmd)
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from typing import (
|
||||
Any, Callable, ContextManager, Dict, Optional, Sequence, Type, Union
|
||||
)
|
||||
|
||||
from kitty.types import ParsedShortcut
|
||||
from kitty.typing import (
|
||||
AbstractEventLoop, BossType, Debug, ImageManagerType, KeyEventType,
|
||||
KittensKeyActionType, LoopType, MouseEvent, ScreenSize, TermManagerType
|
||||
@@ -45,18 +46,18 @@ class Handler:
|
||||
def asyncio_loop(self) -> AbstractEventLoop:
|
||||
return self._tui_loop.asycio_loop
|
||||
|
||||
def add_shortcut(self, action: KittensKeyActionType, key: str, mods: Optional[int] = None, is_text: Optional[bool] = False) -> None:
|
||||
if not hasattr(self, '_text_shortcuts'):
|
||||
self._text_shortcuts, self._key_shortcuts = {}, {}
|
||||
if is_text:
|
||||
self._text_shortcuts[key] = action
|
||||
else:
|
||||
self._key_shortcuts[(key, mods or 0)] = action
|
||||
def add_shortcut(self, action: KittensKeyActionType, spec: Union[str, ParsedShortcut]) -> None:
|
||||
if not hasattr(self, '_key_shortcuts'):
|
||||
self._key_shortcuts: Dict[ParsedShortcut, KittensKeyActionType] = {}
|
||||
if isinstance(spec, str):
|
||||
from kitty.key_encoding import parse_shortcut
|
||||
spec = parse_shortcut(spec)
|
||||
self._key_shortcuts[spec] = action
|
||||
|
||||
def shortcut_action(self, key_event_or_text: Union[str, KeyEventType]) -> Optional[KittensKeyActionType]:
|
||||
if isinstance(key_event_or_text, str):
|
||||
return self._text_shortcuts.get(key_event_or_text)
|
||||
return self._key_shortcuts.get((key_event_or_text.key, key_event_or_text.mods))
|
||||
def shortcut_action(self, key_event: KeyEventType) -> Optional[KittensKeyActionType]:
|
||||
for sc, action in self._key_shortcuts.items():
|
||||
if key_event.matches(sc):
|
||||
return action
|
||||
|
||||
def __enter__(self) -> None:
|
||||
if self._image_manager is not None:
|
||||
@@ -85,6 +86,12 @@ class Handler:
|
||||
def on_term(self) -> None:
|
||||
self._tui_loop.quit(1)
|
||||
|
||||
def on_key_event(self, key_event: KeyEventType, in_bracketed_paste: bool = False) -> None:
|
||||
if key_event.text:
|
||||
self.on_text(key_event.text, in_bracketed_paste)
|
||||
else:
|
||||
self.on_key(key_event)
|
||||
|
||||
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
|
||||
pass
|
||||
|
||||
|
||||
@@ -8,17 +8,20 @@ import sys
|
||||
from base64 import standard_b64encode
|
||||
from collections import defaultdict, deque
|
||||
from contextlib import suppress
|
||||
from enum import IntEnum
|
||||
from itertools import count
|
||||
from typing import (
|
||||
Any, Callable, DefaultDict, Deque, Dict, List, Optional, Sequence, Tuple,
|
||||
Union
|
||||
Any, Callable, DefaultDict, Deque, Dict, Iterator, List, Optional,
|
||||
Sequence, Tuple, Union
|
||||
)
|
||||
|
||||
from kitty.fast_data_types import create_canvas
|
||||
from kitty.typing import (
|
||||
CompletedProcess, GRT_a, GRT_d, GRT_f, GRT_m, GRT_o, GRT_t, HandlerType,
|
||||
TypedDict
|
||||
CompletedProcess, GRT_a, GRT_d, GRT_f, GRT_m, GRT_o, GRT_t, HandlerType
|
||||
)
|
||||
from kitty.utils import (
|
||||
ScreenSize, find_exe, fit_image, positive_float, positive_int
|
||||
)
|
||||
from kitty.utils import ScreenSize, fit_image
|
||||
|
||||
from .operations import cursor
|
||||
|
||||
@@ -29,11 +32,68 @@ except Exception:
|
||||
fsenc = 'utf-8'
|
||||
|
||||
|
||||
class Dispose(IntEnum):
|
||||
undefined = 0
|
||||
none = 1
|
||||
background = 2
|
||||
previous = 3
|
||||
|
||||
|
||||
class Frame:
|
||||
gap: int # milliseconds
|
||||
canvas_width: int
|
||||
canvas_height: int
|
||||
width: int
|
||||
height: int
|
||||
index: int
|
||||
xdpi: float
|
||||
ydpi: float
|
||||
canvas_x: int
|
||||
canvas_y: int
|
||||
mode: str
|
||||
needs_blend: bool
|
||||
dispose: Dispose
|
||||
path: str = ''
|
||||
|
||||
def __init__(self, identify_data: Union['Frame', Dict[str, str]]):
|
||||
if isinstance(identify_data, Frame):
|
||||
for k in Frame.__annotations__:
|
||||
setattr(self, k, getattr(identify_data, k))
|
||||
else:
|
||||
self.gap = max(0, int(identify_data['gap']) * 10)
|
||||
sz, pos = identify_data['canvas'].split('+', 1)
|
||||
self.canvas_width, self.canvas_height = map(positive_int, sz.split('x', 1))
|
||||
self.canvas_x, self.canvas_y = map(int, pos.split('+', 1))
|
||||
self.width, self.height = map(positive_int, identify_data['size'].split('x', 1))
|
||||
self.xdpi, self.ydpi = map(positive_float, identify_data['dpi'].split('x', 1))
|
||||
self.index = positive_int(identify_data['index'])
|
||||
q = identify_data['transparency'].lower()
|
||||
self.mode = 'rgba' if q in ('blend', 'true') else 'rgb'
|
||||
self.needs_blend = q == 'blend'
|
||||
self.dispose = getattr(Dispose, identify_data['dispose'].lower())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
canvas = f'{self.canvas_width}x{self.canvas_height}:{self.canvas_x}+{self.canvas_y}'
|
||||
geom = f'{self.width}x{self.height}'
|
||||
return f'Frame(index={self.index}, gap={self.gap}, geom={geom}, canvas={canvas}, dispose={self.dispose.name})'
|
||||
|
||||
|
||||
class ImageData:
|
||||
|
||||
def __init__(self, fmt: str, width: int, height: int, mode: str):
|
||||
def __init__(self, fmt: str, width: int, height: int, mode: str, frames: List[Frame]):
|
||||
self.width, self.height, self.fmt, self.mode = width, height, fmt, mode
|
||||
self.transmit_fmt: GRT_f = (24 if self.mode == 'rgb' else 32)
|
||||
self.frames = frames
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.frames)
|
||||
|
||||
def __iter__(self) -> Iterator[Frame]:
|
||||
yield from self.frames
|
||||
|
||||
def __repr__(self) -> str:
|
||||
frames = '\n '.join(map(repr, self.frames))
|
||||
return f'Image(fmt={self.fmt}, mode={self.mode},\n {frames}\n)'
|
||||
|
||||
|
||||
class OpenFailed(ValueError):
|
||||
@@ -58,8 +118,20 @@ class NoImageMagick(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class OutdatedImageMagick(ValueError):
|
||||
|
||||
def __init__(self, detailed_error: str):
|
||||
super().__init__('ImageMagick on this system is too old ImageMagick 7+ required which was first released in 2016')
|
||||
self.detailed_error = detailed_error
|
||||
|
||||
|
||||
last_imagemagick_cmd: Sequence[str] = ()
|
||||
|
||||
|
||||
def run_imagemagick(path: str, cmd: Sequence[str], keep_stdout: bool = True) -> CompletedProcess:
|
||||
global last_imagemagick_cmd
|
||||
import subprocess
|
||||
last_imagemagick_cmd = cmd
|
||||
try:
|
||||
p = subprocess.run(cmd, stdout=subprocess.PIPE if keep_stdout else subprocess.DEVNULL, stderr=subprocess.PIPE)
|
||||
except FileNotFoundError:
|
||||
@@ -70,22 +142,63 @@ def run_imagemagick(path: str, cmd: Sequence[str], keep_stdout: bool = True) ->
|
||||
|
||||
|
||||
def identify(path: str) -> ImageData:
|
||||
p = run_imagemagick(path, ['identify', '-format', '%m %w %h %A', '--', path])
|
||||
parts: Tuple[str, ...] = tuple(filter(None, p.stdout.decode('utf-8').split()))
|
||||
mode = 'rgb' if parts[3].lower() == 'false' else 'rgba'
|
||||
return ImageData(parts[0].lower(), int(parts[1]), int(parts[2]), mode)
|
||||
import json
|
||||
q = '{"fmt":"%m","canvas":"%g","transparency":"%A","gap":"%T","index":"%p","size":"%wx%h","dpi":"%xx%y","dispose":"%D"},'
|
||||
exe = find_exe('magick')
|
||||
if exe:
|
||||
cmd = [exe, 'identify']
|
||||
else:
|
||||
cmd = ['identify']
|
||||
p = run_imagemagick(path, cmd + ['-format', q, '--', path])
|
||||
data = json.loads(b'[' + p.stdout.rstrip(b',') + b']')
|
||||
first = data[0]
|
||||
frames = list(map(Frame, data))
|
||||
image_fmt = first['fmt'].lower()
|
||||
if image_fmt == 'gif' and not any(f.gap > 0 for f in frames):
|
||||
# Some broken GIF images have all zero gaps, browsers with their usual
|
||||
# idiot ideas render these with a default 100ms gap https://bugzilla.mozilla.org/show_bug.cgi?id=125137
|
||||
# Browsers actually force a 100ms gap at any zero gap frame, but that
|
||||
# just means it is impossible to deliberately use zero gap frames for
|
||||
# sophisticated blending, so we dont do that.
|
||||
for f in frames:
|
||||
f.gap = 100
|
||||
mode = 'rgb'
|
||||
for f in frames:
|
||||
if f.mode == 'rgba':
|
||||
mode = 'rgba'
|
||||
break
|
||||
return ImageData(image_fmt, frames[0].canvas_width, frames[0].canvas_height, mode, frames)
|
||||
|
||||
|
||||
def convert(
|
||||
path: str, m: ImageData,
|
||||
class RenderedImage(ImageData):
|
||||
|
||||
def __init__(self, fmt: str, width: int, height: int, mode: str):
|
||||
super().__init__(fmt, width, height, mode, [])
|
||||
|
||||
|
||||
def render_image(
|
||||
path: str, output_prefix: str,
|
||||
m: ImageData,
|
||||
available_width: int, available_height: int,
|
||||
scale_up: bool,
|
||||
tdir: Optional[str] = None
|
||||
) -> Tuple[str, int, int]:
|
||||
from tempfile import NamedTemporaryFile
|
||||
width, height = m.width, m.height
|
||||
cmd = ['convert', '-background', 'none', '--', path]
|
||||
only_first_frame: bool = False
|
||||
) -> RenderedImage:
|
||||
import tempfile
|
||||
has_multiple_frames = len(m) > 1
|
||||
get_multiple_frames = has_multiple_frames and not only_first_frame
|
||||
exe = find_exe('magick')
|
||||
if exe:
|
||||
cmd = [exe, 'convert']
|
||||
else:
|
||||
exe = find_exe('convert')
|
||||
if exe is None:
|
||||
raise OSError('Failed to find the ImageMagick convert executable, make sure it is present in PATH')
|
||||
cmd = [exe]
|
||||
cmd += ['-background', 'none', '--', path]
|
||||
if only_first_frame and has_multiple_frames:
|
||||
cmd[-1] += '[0]'
|
||||
scaled = False
|
||||
width, height = m.width, m.height
|
||||
if scale_up:
|
||||
if width < available_width:
|
||||
r = available_width / width
|
||||
@@ -93,25 +206,86 @@ def convert(
|
||||
scaled = True
|
||||
if scaled or width > available_width or height > available_height:
|
||||
width, height = fit_image(width, height, available_width, available_height)
|
||||
cmd += ['-resize', '{}x{}!'.format(width, height)]
|
||||
cmd += ['-depth', '8']
|
||||
with NamedTemporaryFile(prefix='icat-', suffix='.' + m.mode, delete=False, dir=tdir) as outfile:
|
||||
run_imagemagick(path, cmd + [outfile.name])
|
||||
# ImageMagick sometimes generated rgba images smaller than the specified
|
||||
# size. See https://github.com/kovidgoyal/kitty/issues/276 for examples
|
||||
sz = os.path.getsize(outfile.name)
|
||||
resize_cmd = ['-resize', '{}x{}!'.format(width, height)]
|
||||
if get_multiple_frames:
|
||||
# we have to coalesce, resize and de-coalesce all frames
|
||||
resize_cmd = ['-coalesce'] + resize_cmd + ['-deconstruct']
|
||||
cmd += resize_cmd
|
||||
cmd += ['-depth', '8', '-auto-orient', '-set', 'filename:f', '%w-%h-%g-%p']
|
||||
ans = RenderedImage(m.fmt, width, height, m.mode)
|
||||
if only_first_frame:
|
||||
ans.frames = [Frame(m.frames[0])]
|
||||
else:
|
||||
ans.frames = list(map(Frame, m.frames))
|
||||
bytes_per_pixel = 3 if m.mode == 'rgb' else 4
|
||||
expected_size = bytes_per_pixel * width * height
|
||||
if sz < expected_size:
|
||||
missing = expected_size - sz
|
||||
if missing % (bytes_per_pixel * width) != 0:
|
||||
raise ConvertFailed(
|
||||
path, 'ImageMagick failed to convert {} correctly,'
|
||||
' it generated {} < {} of data (w={}, h={}, bpp={})'.format(
|
||||
path, sz, expected_size, width, height, bytes_per_pixel))
|
||||
height -= missing // (bytes_per_pixel * width)
|
||||
|
||||
return outfile.name, width, height
|
||||
def check_resize(frame: Frame) -> None:
|
||||
# ImageMagick sometimes generates RGBA images smaller than the specified
|
||||
# size. See https://github.com/kovidgoyal/kitty/issues/276 for examples
|
||||
sz = os.path.getsize(frame.path)
|
||||
expected_size = bytes_per_pixel * frame.width * frame.height
|
||||
if sz < expected_size:
|
||||
missing = expected_size - sz
|
||||
if missing % (bytes_per_pixel * width) != 0:
|
||||
raise ConvertFailed(
|
||||
path, 'ImageMagick failed to convert {} correctly,'
|
||||
' it generated {} < {} of data (w={}, h={}, bpp={})'.format(
|
||||
path, sz, expected_size, frame.width, frame.height, bytes_per_pixel))
|
||||
frame.height -= missing // (bytes_per_pixel * frame.width)
|
||||
if frame.index == 0:
|
||||
ans.height = frame.height
|
||||
ans.width = frame.width
|
||||
|
||||
with tempfile.TemporaryDirectory(dir=os.path.dirname(output_prefix)) as tdir:
|
||||
output_template = os.path.join(tdir, f'im-%[filename:f].{m.mode}')
|
||||
if get_multiple_frames:
|
||||
cmd.append('+adjoin')
|
||||
run_imagemagick(path, cmd + [output_template])
|
||||
unseen = {x.index for x in m}
|
||||
for x in os.listdir(tdir):
|
||||
try:
|
||||
parts = x.split('.', 1)[0].split('-')
|
||||
index = int(parts[-1])
|
||||
unseen.discard(index)
|
||||
f = ans.frames[index]
|
||||
f.width, f.height = map(positive_int, parts[1:3])
|
||||
sz, pos = parts[3].split('+', 1)
|
||||
f.canvas_width, f.canvas_height = map(positive_int, sz.split('x', 1))
|
||||
f.canvas_x, f.canvas_y = map(int, pos.split('+', 1))
|
||||
except Exception:
|
||||
raise OutdatedImageMagick(f'Unexpected output filename: {x!r} produced by ImageMagick command: {last_imagemagick_cmd}')
|
||||
f.path = output_prefix + f'-{index}.{m.mode}'
|
||||
os.rename(os.path.join(tdir, x), f.path)
|
||||
check_resize(f)
|
||||
f = ans.frames[0]
|
||||
if f.width != ans.width or f.height != ans.height:
|
||||
with open(f.path, 'r+b') as ff:
|
||||
data = ff.read()
|
||||
ff.seek(0)
|
||||
ff.truncate()
|
||||
cd = create_canvas(data, f.width, f.canvas_x, f.canvas_y, ans.width, ans.height, 3 if ans.mode == 'rgb' else 4)
|
||||
ff.write(cd)
|
||||
if get_multiple_frames:
|
||||
if unseen:
|
||||
raise ConvertFailed(path, f'Failed to render {len(unseen)} out of {len(m)} frames of animation')
|
||||
elif not ans.frames[0].path:
|
||||
raise ConvertFailed(path, 'Failed to render image')
|
||||
|
||||
return ans
|
||||
|
||||
|
||||
def render_as_single_image(
|
||||
path: str, m: ImageData,
|
||||
available_width: int, available_height: int,
|
||||
scale_up: bool,
|
||||
tdir: Optional[str] = None
|
||||
) -> Tuple[str, int, int]:
|
||||
import tempfile
|
||||
fd, output = tempfile.mkstemp(prefix='icat-', suffix=f'.{m.mode}', dir=tdir)
|
||||
os.close(fd)
|
||||
result = render_image(path, output, m, available_width, available_height, scale_up, only_first_frame=True)
|
||||
os.rename(result.frames[0].path, output)
|
||||
return output, result.width, result.height
|
||||
|
||||
|
||||
def can_display_images() -> bool:
|
||||
@@ -129,6 +303,7 @@ SentImageKey = Tuple[int, int, int]
|
||||
|
||||
class GraphicsCommand:
|
||||
a: GRT_a = 't' # action
|
||||
q: int = 0 # suppress responses
|
||||
f: GRT_f = 32 # image data format
|
||||
t: GRT_t = 'd' # transmission medium
|
||||
s: int = 0 # sent image width
|
||||
@@ -136,6 +311,8 @@ class GraphicsCommand:
|
||||
S: int = 0 # size of data to read from file
|
||||
O: int = 0 # offset of data to read from file
|
||||
i: int = 0 # image id
|
||||
I: int = 0 # image number
|
||||
p: int = 0 # placement id
|
||||
o: Optional[GRT_o] = None # type of compression
|
||||
m: GRT_m = 0 # 0 or 1 whether there is more chunked data
|
||||
x: int = 0 # left edge of image area to display
|
||||
@@ -149,7 +326,16 @@ class GraphicsCommand:
|
||||
z: int = 0 # z-index
|
||||
d: GRT_d = 'a' # what to delete
|
||||
|
||||
def serialize(self, payload: bytes = b'') -> bytes:
|
||||
def __repr__(self) -> str:
|
||||
return self.serialize().decode('ascii').replace('\033', '^]')
|
||||
|
||||
def clone(self) -> 'GraphicsCommand':
|
||||
ans = GraphicsCommand()
|
||||
for k in GraphicsCommand.__annotations__:
|
||||
setattr(ans, k, getattr(self, k))
|
||||
return ans
|
||||
|
||||
def serialize(self, payload: Union[bytes, str] = b'') -> bytes:
|
||||
items = []
|
||||
for k in GraphicsCommand.__annotations__:
|
||||
val: Union[str, None, int] = getattr(self, k)
|
||||
@@ -163,6 +349,8 @@ class GraphicsCommand:
|
||||
w(','.join(items).encode('ascii'))
|
||||
if payload:
|
||||
w(b';')
|
||||
if isinstance(payload, str):
|
||||
payload = standard_b64encode(payload.encode('utf-8'))
|
||||
w(payload)
|
||||
w(b'\033\\')
|
||||
return b''.join(ans)
|
||||
@@ -173,10 +361,15 @@ class GraphicsCommand:
|
||||
setattr(self, k, defval)
|
||||
|
||||
|
||||
class Placement(TypedDict):
|
||||
class Placement:
|
||||
cmd: GraphicsCommand
|
||||
x: int
|
||||
y: int
|
||||
x: int = 0
|
||||
y: int = 0
|
||||
|
||||
def __init__(self, cmd: GraphicsCommand, x: int = 0, y: int = 0):
|
||||
self.cmd = cmd
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
|
||||
class ImageManager:
|
||||
@@ -263,8 +456,8 @@ class ImageManager:
|
||||
skey = self.image_id_to_converted_data[image_id]
|
||||
self.transmit_image(image_data, image_id, *skey)
|
||||
with cursor(self.handler.write):
|
||||
self.handler.cmd.set_cursor_position(pl['x'], pl['y'])
|
||||
self.handler.cmd.gr_command(pl['cmd'])
|
||||
self.handler.cmd.set_cursor_position(pl.x, pl.y)
|
||||
self.handler.cmd.gr_command(pl.cmd)
|
||||
|
||||
def send_image(self, path: str, max_cols: Optional[int] = None, max_rows: Optional[int] = None, scale_up: bool = False) -> SentImageKey:
|
||||
path = os.path.abspath(path)
|
||||
@@ -316,13 +509,13 @@ class ImageManager:
|
||||
gc.i = image_id
|
||||
if src_rect is not None:
|
||||
gc.x, gc.y, gc.w, gc.h = map(int, src_rect)
|
||||
self.placements_in_flight[image_id].append({'cmd': gc, 'x': x, 'y': y})
|
||||
self.placements_in_flight[image_id].append(Placement(gc, x, y))
|
||||
with cursor(self.handler.write):
|
||||
self.handler.cmd.set_cursor_position(x, y)
|
||||
self.handler.cmd.gr_command(gc)
|
||||
|
||||
def convert_image(self, path: str, available_width: int, available_height: int, image_data: ImageData, scale_up: bool = False) -> ImageKey:
|
||||
rgba_path, width, height = convert(path, image_data, available_width, available_height, scale_up, tdir=self.tdir)
|
||||
rgba_path, width, height = render_as_single_image(path, image_data, available_width, available_height, scale_up, tdir=self.tdir)
|
||||
return rgba_path, width, height
|
||||
|
||||
def transmit_image(self, image_data: ImageData, image_id: int, rgba_path: str, width: int, height: int) -> int:
|
||||
|
||||
@@ -5,14 +5,9 @@
|
||||
from typing import Callable, Tuple
|
||||
|
||||
from kitty.fast_data_types import truncate_point_for_length, wcswidth
|
||||
from kitty.key_encoding import RELEASE, KeyEvent, key_defs as K
|
||||
from kitty.key_encoding import EventType, KeyEvent
|
||||
|
||||
HOME = K['HOME']
|
||||
END = K['END']
|
||||
BACKSPACE = K['BACKSPACE']
|
||||
DELETE = K['DELETE']
|
||||
LEFT = K['LEFT']
|
||||
RIGHT = K['RIGHT']
|
||||
from .operations import RESTORE_CURSOR, SAVE_CURSOR, move_cursor_by
|
||||
|
||||
|
||||
class LineEdit:
|
||||
@@ -31,13 +26,28 @@ class LineEdit:
|
||||
before, after = self.current_input[:x], self.current_input[x:]
|
||||
return before, after
|
||||
|
||||
def write(self, write: Callable[[str], None], prompt: str = '') -> None:
|
||||
def write(self, write: Callable[[str], None], prompt: str = '', screen_cols: int = 0) -> None:
|
||||
if self.pending_bell:
|
||||
write('\a')
|
||||
self.pending_bell = False
|
||||
write(prompt)
|
||||
write(self.current_input)
|
||||
write('\r\x1b[{}C'.format(self.cursor_pos + wcswidth(prompt)))
|
||||
text = prompt + self.current_input
|
||||
cursor_pos = self.cursor_pos + wcswidth(prompt)
|
||||
if screen_cols:
|
||||
write(SAVE_CURSOR + text + RESTORE_CURSOR)
|
||||
used_lines, last_line_cursor_pos = divmod(cursor_pos, screen_cols)
|
||||
if used_lines == 0:
|
||||
if last_line_cursor_pos:
|
||||
write(move_cursor_by(last_line_cursor_pos, 'right'))
|
||||
else:
|
||||
if used_lines:
|
||||
write(move_cursor_by(used_lines, 'down'))
|
||||
if last_line_cursor_pos:
|
||||
write(move_cursor_by(last_line_cursor_pos, 'right'))
|
||||
else:
|
||||
write(text)
|
||||
write('\r')
|
||||
if cursor_pos:
|
||||
write(move_cursor_by(cursor_pos, 'right'))
|
||||
|
||||
def add_text(self, text: str) -> None:
|
||||
if self.current_input:
|
||||
@@ -120,22 +130,22 @@ class LineEdit:
|
||||
return self.cursor_pos != orig
|
||||
|
||||
def on_key(self, key_event: KeyEvent) -> bool:
|
||||
if key_event.type is RELEASE:
|
||||
if key_event.type is EventType.RELEASE:
|
||||
return False
|
||||
elif key_event.key is HOME:
|
||||
if key_event.matches('home'):
|
||||
return self.home()
|
||||
elif key_event.key is END:
|
||||
if key_event.matches('end'):
|
||||
return self.end()
|
||||
elif key_event.key is BACKSPACE:
|
||||
if key_event.matches('backspace'):
|
||||
self.backspace()
|
||||
return True
|
||||
elif key_event.key is DELETE:
|
||||
if key_event.matches('delete'):
|
||||
self.delete()
|
||||
return True
|
||||
elif key_event.key is LEFT:
|
||||
if key_event.matches('left'):
|
||||
self.left()
|
||||
return True
|
||||
elif key_event.key is RIGHT:
|
||||
if key_event.matches('right'):
|
||||
self.right()
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -10,6 +10,7 @@ import re
|
||||
import selectors
|
||||
import signal
|
||||
import sys
|
||||
import termios
|
||||
from contextlib import contextmanager
|
||||
from functools import partial
|
||||
from typing import Any, Callable, Dict, Generator, List, NamedTuple, Optional
|
||||
@@ -20,7 +21,7 @@ from kitty.fast_data_types import (
|
||||
)
|
||||
from kitty.key_encoding import (
|
||||
ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, backspace_key, decode_key_event,
|
||||
enter_key, key_defs as K
|
||||
enter_key
|
||||
)
|
||||
from kitty.typing import ImageManagerType, KeyEventType, Protocol
|
||||
from kitty.utils import ScreenSizeGetter, screen_size_function, write_all
|
||||
@@ -28,8 +29,6 @@ from kitty.utils import ScreenSizeGetter, screen_size_function, write_all
|
||||
from .handler import Handler
|
||||
from .operations import init_state, reset_state
|
||||
|
||||
C, D = K['C'], K['D']
|
||||
|
||||
|
||||
class BinaryWrite(Protocol):
|
||||
|
||||
@@ -61,8 +60,9 @@ debug = Debug()
|
||||
|
||||
class TermManager:
|
||||
|
||||
def __init__(self) -> None:
|
||||
def __init__(self, optional_actions: int = termios.TCSANOW) -> None:
|
||||
self.extra_finalize: Optional[str] = None
|
||||
self.optional_actions = optional_actions
|
||||
|
||||
def set_state_for_loop(self, set_raw: bool = True) -> None:
|
||||
if set_raw:
|
||||
@@ -82,7 +82,7 @@ class TermManager:
|
||||
self.set_state_for_loop()
|
||||
|
||||
def __enter__(self) -> 'TermManager':
|
||||
self.tty_fd, self.original_termios = open_tty()
|
||||
self.tty_fd, self.original_termios = open_tty(False, self.optional_actions)
|
||||
self.set_state_for_loop(set_raw=False)
|
||||
return self
|
||||
|
||||
@@ -147,7 +147,7 @@ class UnhandledException(Handler):
|
||||
self.write('Press the Enter key to quit')
|
||||
|
||||
def on_key(self, key_event: KeyEventType) -> None:
|
||||
if key_event is enter_key:
|
||||
if key_event.key == 'ENTER':
|
||||
self.quit_loop(1)
|
||||
|
||||
def on_interrupt(self) -> None:
|
||||
@@ -179,11 +179,15 @@ class SignalManager:
|
||||
signal.SIGWINCH, signal.SIGINT, signal.SIGTERM)))
|
||||
|
||||
|
||||
sanitize_bracketed_paste: str = '[\x03\x04\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]'
|
||||
|
||||
|
||||
class Loop:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
sanitize_bracketed_paste: str = '[\x03\x04\x0e\x0f\r\x07\x7f\x8d\x8e\x8f\x90\x9b\x9d\x9e\x9f]'
|
||||
sanitize_bracketed_paste: str = sanitize_bracketed_paste,
|
||||
optional_actions: int = termios.TCSADRAIN
|
||||
):
|
||||
if is_macos:
|
||||
# On macOS PTY devices are not supported by the KqueueSelector and
|
||||
@@ -193,6 +197,7 @@ class Loop:
|
||||
else:
|
||||
self.asycio_loop = asyncio.get_event_loop()
|
||||
self.return_code = 0
|
||||
self.optional_actions = optional_actions
|
||||
self.read_buf = ''
|
||||
self.decoder = codecs.getincrementaldecoder('utf-8')('ignore')
|
||||
try:
|
||||
@@ -272,11 +277,25 @@ class Loop:
|
||||
pass
|
||||
else:
|
||||
self.handler.on_mouse(ev)
|
||||
elif q == '~':
|
||||
elif q in 'u~ABCDEHFPQRS':
|
||||
if csi == '200~':
|
||||
self.in_bracketed_paste = True
|
||||
return
|
||||
elif csi == '201~':
|
||||
self.in_bracketed_paste = False
|
||||
return
|
||||
try:
|
||||
k = decode_key_event(csi[:-1], q)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
if k.matches('ctrl+c'):
|
||||
self.handler.on_interrupt()
|
||||
return
|
||||
if k.matches('ctrl+d'):
|
||||
self.handler.on_eot()
|
||||
return
|
||||
self.handler.on_key_event(k)
|
||||
|
||||
def _on_pm(self, pm: str) -> None:
|
||||
pass
|
||||
@@ -293,21 +312,7 @@ class Loop:
|
||||
self.handler.on_clipboard_response(standard_b64decode(rest).decode('utf-8'), from_primary)
|
||||
|
||||
def _on_apc(self, apc: str) -> None:
|
||||
if apc.startswith('K'):
|
||||
try:
|
||||
k = decode_key_event(apc)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
if k.mods is CTRL and k.type is not RELEASE:
|
||||
if k.key is C:
|
||||
self.handler.on_interrupt()
|
||||
return
|
||||
if k.key is D:
|
||||
self.handler.on_eot()
|
||||
return
|
||||
self.handler.on_key(k)
|
||||
elif apc.startswith('G'):
|
||||
if apc.startswith('G'):
|
||||
if self.handler.image_manager is not None:
|
||||
self.handler.image_manager.handle_response(apc)
|
||||
# }}}
|
||||
@@ -317,13 +322,17 @@ class Loop:
|
||||
self.write_buf[self.iov_limit - 1] = b''.join(self.write_buf[self.iov_limit - 1:])
|
||||
del self.write_buf[self.iov_limit:]
|
||||
sizes = tuple(map(len, self.write_buf))
|
||||
try:
|
||||
written = os.writev(fd, self.write_buf)
|
||||
except BlockingIOError:
|
||||
return
|
||||
if not written:
|
||||
raise EOFError('The output stream is closed')
|
||||
if written >= sum(sizes):
|
||||
total_size = sum(sizes)
|
||||
if total_size:
|
||||
try:
|
||||
written = os.writev(fd, self.write_buf)
|
||||
except BlockingIOError:
|
||||
return
|
||||
if not written:
|
||||
raise EOFError('The output stream is closed')
|
||||
else:
|
||||
written = 0
|
||||
if written >= total_size:
|
||||
self.write_buf: List[bytes] = []
|
||||
self.asycio_loop.remove_writer(fd)
|
||||
self.waiting_for_writes = False
|
||||
@@ -388,7 +397,7 @@ class Loop:
|
||||
handler.on_resize(handler.screen_size)
|
||||
|
||||
signal_manager = SignalManager(self.asycio_loop, _on_sigwinch, handler.on_interrupt, handler.on_term)
|
||||
with TermManager() as term_manager, signal_manager:
|
||||
with TermManager(self.optional_actions) as term_manager, signal_manager:
|
||||
self._get_screen_size: ScreenSizeGetter = screen_size_function(term_manager.tty_fd)
|
||||
image_manager = None
|
||||
if handler.image_manager_class is not None:
|
||||
|
||||
@@ -20,7 +20,8 @@ SAVE_CURSOR = '\0337'
|
||||
RESTORE_CURSOR = '\0338'
|
||||
SAVE_PRIVATE_MODE_VALUES = '\033[?s'
|
||||
RESTORE_PRIVATE_MODE_VALUES = '\033[?r'
|
||||
|
||||
SAVE_COLORS = '\033[#P'
|
||||
RESTORE_COLORS = '\033[#Q'
|
||||
MODES = dict(
|
||||
LNM=(20, ''),
|
||||
IRM=(4, ''),
|
||||
@@ -39,7 +40,6 @@ MODES = dict(
|
||||
MOUSE_URXVT_MODE=(1015, '?'),
|
||||
ALTERNATE_SCREEN=(1049, '?'),
|
||||
BRACKETED_PASTE=(2004, '?'),
|
||||
EXTENDED_KEYBOARD=(2017, '?'),
|
||||
)
|
||||
|
||||
F = TypeVar('F')
|
||||
@@ -68,6 +68,11 @@ def clear_screen() -> str:
|
||||
return '\033[H\033[2J'
|
||||
|
||||
|
||||
@cmd
|
||||
def clear_to_end_of_screen() -> str:
|
||||
return '\033[J'
|
||||
|
||||
|
||||
@cmd
|
||||
def clear_to_eol() -> str:
|
||||
return '\033[K'
|
||||
@@ -108,6 +113,12 @@ def set_cursor_position(x: int, y: int) -> str: # (0, 0) is top left
|
||||
return '\033[{};{}H'.format(y + 1, x + 1)
|
||||
|
||||
|
||||
@cmd
|
||||
def move_cursor_by(amt: int, direction: str) -> str:
|
||||
suffix = {'up': 'A', 'down': 'B', 'right': 'C', 'left': 'D'}[direction]
|
||||
return f'\033[{amt}{suffix}'
|
||||
|
||||
|
||||
@cmd
|
||||
def set_cursor_shape(shape: str = 'block', blink: bool = True) -> str:
|
||||
val = {'block': 1, 'underline': 3, 'bar': 5}.get(shape, 1)
|
||||
@@ -253,28 +264,29 @@ def init_state(alternate_screen: bool = True) -> str:
|
||||
ans = (
|
||||
S7C1T + SAVE_CURSOR + SAVE_PRIVATE_MODE_VALUES + reset_mode('LNM') +
|
||||
reset_mode('IRM') + reset_mode('DECKM') + reset_mode('DECSCNM') +
|
||||
set_mode('DECARM') + reset_mode('DECOM') + set_mode('DECAWM') +
|
||||
set_mode('DECARM') + set_mode('DECAWM') +
|
||||
set_mode('DECTCEM') + reset_mode('MOUSE_BUTTON_TRACKING') +
|
||||
reset_mode('MOUSE_MOTION_TRACKING') + reset_mode('MOUSE_MOVE_TRACKING') +
|
||||
reset_mode('FOCUS_TRACKING') + reset_mode('MOUSE_UTF8_MODE') +
|
||||
reset_mode('MOUSE_SGR_MODE') + reset_mode('MOUSE_UTF8_MODE') +
|
||||
set_mode('BRACKETED_PASTE') + set_mode('EXTENDED_KEYBOARD') +
|
||||
'\033]30001\033\\' +
|
||||
set_mode('BRACKETED_PASTE') + SAVE_COLORS +
|
||||
'\033[*x' # reset DECSACE to default region select
|
||||
)
|
||||
if alternate_screen:
|
||||
ans += set_mode('ALTERNATE_SCREEN')
|
||||
ans += set_mode('ALTERNATE_SCREEN') + reset_mode('DECOM')
|
||||
ans += clear_screen()
|
||||
ans += '\033[>31u' # extended keyboard mode
|
||||
return ans
|
||||
|
||||
|
||||
def reset_state(normal_screen: bool = True) -> str:
|
||||
ans = ''
|
||||
ans += '\033[<u' # restore keyboard mode
|
||||
if normal_screen:
|
||||
ans += reset_mode('ALTERNATE_SCREEN')
|
||||
ans += RESTORE_PRIVATE_MODE_VALUES
|
||||
ans += RESTORE_CURSOR
|
||||
ans += '\033]30101\033\\'
|
||||
ans += RESTORE_COLORS
|
||||
return ans
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import os
|
||||
import string
|
||||
import subprocess
|
||||
@@ -19,9 +18,7 @@ from kitty.cli_stub import UnicodeCLIOptions
|
||||
from kitty.config import cached_values_for
|
||||
from kitty.constants import config_dir
|
||||
from kitty.fast_data_types import is_emoji_presentation_base, wcswidth
|
||||
from kitty.key_encoding import (
|
||||
CTRL, PRESS, RELEASE, SHIFT, KeyEvent, enter_key, key_defs as K
|
||||
)
|
||||
from kitty.key_encoding import EventType, KeyEvent
|
||||
from kitty.typing import BossType
|
||||
from kitty.utils import ScreenSize, get_editor
|
||||
|
||||
@@ -34,21 +31,9 @@ from ..tui.operations import (
|
||||
)
|
||||
|
||||
HEX, NAME, EMOTICONS, FAVORITES = 'HEX', 'NAME', 'EMOTICONS', 'FAVORITES'
|
||||
UP = K['UP']
|
||||
DOWN = K['DOWN']
|
||||
LEFT = K['LEFT']
|
||||
RIGHT = K['RIGHT']
|
||||
RIGHT_BRACKET = K['RIGHT_BRACKET']
|
||||
LEFT_BRACKET = K['LEFT_BRACKET']
|
||||
TAB = K['TAB']
|
||||
ESCAPE = K['ESCAPE']
|
||||
F1 = K['F1']
|
||||
F2 = K['F2']
|
||||
F3 = K['F3']
|
||||
F4 = K['F4']
|
||||
F12 = K['F12']
|
||||
favorites_path = os.path.join(config_dir, 'unicode-input-favorites.conf')
|
||||
INDEX_CHAR = '.'
|
||||
INDEX_BASE = 36
|
||||
DEFAULT_SET = tuple(map(
|
||||
ord,
|
||||
'‘’“”‹›«»‚„' '😀😛😇😈😉😍😎😮👍👎' '—–§¶†‡©®™' '→⇒•·°±−×÷¼½½¾'
|
||||
@@ -152,7 +137,7 @@ def encode_hint(num: int, digits: str = string.digits + string.ascii_lowercase)
|
||||
|
||||
|
||||
def decode_hint(x: str) -> int:
|
||||
return int(x, 36)
|
||||
return int(x, INDEX_BASE)
|
||||
|
||||
|
||||
class Table:
|
||||
@@ -172,11 +157,11 @@ class Table:
|
||||
if self.codepoints:
|
||||
return self.codepoints[self.current_idx]
|
||||
|
||||
def set_codepoints(self, codepoints: List[int], mode: str = HEX) -> None:
|
||||
def set_codepoints(self, codepoints: List[int], mode: str = HEX, current_idx: int = 0) -> None:
|
||||
self.codepoints = codepoints
|
||||
self.mode = mode
|
||||
self.layout_dirty = True
|
||||
self.current_idx = 0
|
||||
self.current_idx = current_idx if current_idx < len(codepoints) else 0
|
||||
|
||||
def codepoint_at_hint(self, hint: str) -> int:
|
||||
return self.codepoints[decode_hint(hint)]
|
||||
@@ -270,8 +255,10 @@ class Table:
|
||||
|
||||
|
||||
def is_index(w: str) -> bool:
|
||||
if w[0] != INDEX_CHAR:
|
||||
return False
|
||||
try:
|
||||
int(w.lstrip(INDEX_CHAR), 16)
|
||||
int(w.lstrip(INDEX_CHAR), INDEX_BASE)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
@@ -306,6 +293,7 @@ class UnicodeInput(Handler):
|
||||
|
||||
def update_codepoints(self) -> None:
|
||||
codepoints = None
|
||||
iindex_word = 0
|
||||
if self.mode is HEX:
|
||||
q: Tuple[str, Optional[Union[str, Sequence[int]]]] = (self.mode, None)
|
||||
codepoints = self.recent
|
||||
@@ -324,14 +312,11 @@ class UnicodeInput(Handler):
|
||||
if index_words:
|
||||
index_word = words[index_words[0]]
|
||||
words = words[:index_words[0]]
|
||||
iindex_word = int(index_word.lstrip(INDEX_CHAR), INDEX_BASE)
|
||||
codepoints = codepoints_matching_search(tuple(words))
|
||||
if index_words:
|
||||
iindex_word = int(index_word.lstrip(INDEX_CHAR), 16)
|
||||
if codepoints and iindex_word < len(codepoints):
|
||||
codepoints = [codepoints[iindex_word]]
|
||||
if q != self.last_updated_code_point_at:
|
||||
self.last_updated_code_point_at = q
|
||||
self.table.set_codepoints(codepoints or [], self.mode)
|
||||
self.table.set_codepoints(codepoints or [], self.mode, iindex_word)
|
||||
|
||||
def update_current_char(self) -> None:
|
||||
self.update_codepoints()
|
||||
@@ -431,46 +416,46 @@ class UnicodeInput(Handler):
|
||||
self.refresh()
|
||||
|
||||
def on_key(self, key_event: KeyEvent) -> None:
|
||||
if self.mode is HEX and key_event.type is not RELEASE and not key_event.mods:
|
||||
if self.mode is HEX and key_event.type is not EventType.RELEASE and not key_event.mods:
|
||||
try:
|
||||
val = int(self.line_edit.current_input, 16)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
if key_event.key is TAB:
|
||||
if key_event.matches('tab'):
|
||||
self.line_edit.current_input = hex(val + 0x10)[2:]
|
||||
self.refresh()
|
||||
return
|
||||
if key_event.key is UP:
|
||||
if key_event.matches('up'):
|
||||
self.line_edit.current_input = hex(val + 1)[2:]
|
||||
self.refresh()
|
||||
return
|
||||
if key_event.key is DOWN:
|
||||
if key_event.matches('down'):
|
||||
self.line_edit.current_input = hex(val - 1)[2:]
|
||||
self.refresh()
|
||||
return
|
||||
if self.mode is NAME and key_event.type is not RELEASE and not key_event.mods:
|
||||
if key_event.key is TAB:
|
||||
if key_event.mods == SHIFT:
|
||||
self.table.move_current(cols=-1)
|
||||
self.refresh()
|
||||
elif not key_event.mods:
|
||||
self.table.move_current(cols=1)
|
||||
self.refresh()
|
||||
return
|
||||
elif key_event.key is LEFT and not key_event.mods:
|
||||
if self.mode is NAME and key_event.type is not EventType.RELEASE and not key_event.mods:
|
||||
if key_event.matches('shift+tab'):
|
||||
self.table.move_current(cols=-1)
|
||||
self.refresh()
|
||||
return
|
||||
elif key_event.key is RIGHT and not key_event.mods:
|
||||
if key_event.matches('tab'):
|
||||
self.table.move_current(cols=1)
|
||||
self.refresh()
|
||||
return
|
||||
elif key_event.key is UP and not key_event.mods:
|
||||
if key_event.matches('left'):
|
||||
self.table.move_current(cols=-1)
|
||||
self.refresh()
|
||||
return
|
||||
if key_event.matches('right'):
|
||||
self.table.move_current(cols=1)
|
||||
self.refresh()
|
||||
return
|
||||
if key_event.matches('up'):
|
||||
self.table.move_current(rows=-1)
|
||||
self.refresh()
|
||||
return
|
||||
elif key_event.key is DOWN and not key_event.mods:
|
||||
if key_event.matches('down'):
|
||||
self.table.move_current(rows=1)
|
||||
self.refresh()
|
||||
return
|
||||
@@ -478,26 +463,34 @@ class UnicodeInput(Handler):
|
||||
if self.line_edit.on_key(key_event):
|
||||
self.refresh()
|
||||
return
|
||||
if key_event is enter_key:
|
||||
if key_event.matches('enter'):
|
||||
self.quit_loop(0)
|
||||
elif key_event.type is PRESS:
|
||||
if not key_event.mods:
|
||||
if key_event.key is ESCAPE:
|
||||
self.quit_loop(1)
|
||||
elif key_event.key is F1:
|
||||
self.switch_mode(HEX)
|
||||
elif key_event.key is F2:
|
||||
self.switch_mode(NAME)
|
||||
elif key_event.key is F3:
|
||||
self.switch_mode(EMOTICONS)
|
||||
elif key_event.key is F4:
|
||||
self.switch_mode(FAVORITES)
|
||||
elif key_event.key is F12 and self.mode is FAVORITES:
|
||||
self.edit_favorites()
|
||||
elif key_event.mods == CTRL and key_event.key in (TAB, RIGHT_BRACKET, LEFT_BRACKET):
|
||||
self.next_mode(-1 if key_event.key is LEFT_BRACKET else 1)
|
||||
elif key_event.mods == CTRL | SHIFT and key_event.key is TAB:
|
||||
self.next_mode(-1)
|
||||
return
|
||||
if key_event.matches('esc'):
|
||||
self.quit_loop(1)
|
||||
return
|
||||
if key_event.matches('f1'):
|
||||
self.switch_mode(HEX)
|
||||
return
|
||||
if key_event.matches('f2'):
|
||||
self.switch_mode(NAME)
|
||||
return
|
||||
if key_event.matches('f3'):
|
||||
self.switch_mode(EMOTICONS)
|
||||
return
|
||||
if key_event.matches('f4'):
|
||||
self.switch_mode(FAVORITES)
|
||||
return
|
||||
if key_event.matches('f12') and self.mode is FAVORITES:
|
||||
self.edit_favorites()
|
||||
return
|
||||
if key_event.matches('ctrl+shift+tab'):
|
||||
self.next_mode(-1)
|
||||
return
|
||||
for key in ('tab', '[', ']'):
|
||||
if key_event.matches(f'ctrl+{key}'):
|
||||
self.next_mode(-1 if key == '[' else 1)
|
||||
return
|
||||
|
||||
def edit_favorites(self) -> None:
|
||||
if not os.path.exists(favorites_path):
|
||||
|
||||
29168
kittens/unicode_input/names.h
generated
29168
kittens/unicode_input/names.h
generated
File diff suppressed because one or more lines are too long
42
kitty/banned.h
Normal file
42
kitty/banned.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#define BANNED(func) sorry_##func##_is_a_banned_function
|
||||
|
||||
#undef strcpy
|
||||
#define strcpy(x,y) BANNED(strcpy)
|
||||
#undef strcat
|
||||
#define strcat(x,y) BANNED(strcat)
|
||||
#undef strncpy
|
||||
#define strncpy(x,y,n) BANNED(strncpy)
|
||||
#undef strncat
|
||||
#define strncat(x,y,n) BANNED(strncat)
|
||||
|
||||
#undef sprintf
|
||||
#undef vsprintf
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
#define sprintf(...) BANNED(sprintf)
|
||||
#define vsprintf(...) BANNED(vsprintf)
|
||||
#else
|
||||
#define sprintf(buf,fmt,arg) BANNED(sprintf)
|
||||
#define vsprintf(buf,fmt,arg) BANNED(vsprintf)
|
||||
#endif
|
||||
|
||||
#undef gmtime
|
||||
#define gmtime(t) BANNED(gmtime)
|
||||
#undef localtime
|
||||
#define localtime(t) BANNED(localtime)
|
||||
#undef ctime
|
||||
#define ctime(t) BANNED(ctime)
|
||||
#undef ctime_r
|
||||
#define ctime_r(t, buf) BANNED(ctime_r)
|
||||
#undef asctime
|
||||
#define asctime(t) BANNED(asctime)
|
||||
#undef asctime_r
|
||||
#define asctime_r(t, buf) BANNED(asctime_r)
|
||||
@@ -69,7 +69,7 @@ class Borders:
|
||||
self.os_window_id = os_window_id
|
||||
self.tab_id = tab_id
|
||||
self.draw_active_borders = opts.active_border_color is not None
|
||||
self.draw_minimal_borders = opts.draw_minimal_borders
|
||||
self.draw_minimal_borders = opts.draw_minimal_borders and max(opts.window_margin_width) < 1
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
|
||||
168
kitty/boss.py
Normal file → Executable file
168
kitty/boss.py
Normal file → Executable file
@@ -10,7 +10,8 @@ from contextlib import suppress
|
||||
from functools import partial
|
||||
from gettext import gettext as _
|
||||
from typing import (
|
||||
Any, Callable, Dict, Generator, Iterable, List, Optional, Tuple, Union
|
||||
Any, Callable, Dict, Generator, Iterable, List, Optional, Tuple, Union,
|
||||
cast
|
||||
)
|
||||
from weakref import WeakValueDictionary
|
||||
|
||||
@@ -28,14 +29,14 @@ from .constants import (
|
||||
)
|
||||
from .fast_data_types import (
|
||||
CLOSE_BEING_CONFIRMED, IMPERATIVE_CLOSE_REQUESTED, NO_CLOSE_REQUESTED,
|
||||
ChildMonitor, add_timer, background_opacity_of, change_background_opacity,
|
||||
change_os_window_state, cocoa_set_menubar_title, create_os_window,
|
||||
current_application_quit_request, current_os_window, destroy_global_data,
|
||||
focus_os_window, get_clipboard_string, global_font_size,
|
||||
mark_os_window_for_close, os_window_font_size, patch_global_colors,
|
||||
safe_pipe, set_application_quit_request, set_background_image, set_boss,
|
||||
set_clipboard_string, set_in_sequence_mode, thread_write,
|
||||
toggle_fullscreen, toggle_maximized
|
||||
ChildMonitor, KeyEvent, add_timer, background_opacity_of,
|
||||
change_background_opacity, change_os_window_state, cocoa_set_menubar_title,
|
||||
create_os_window, current_application_quit_request, current_os_window,
|
||||
destroy_global_data, focus_os_window, get_clipboard_string,
|
||||
global_font_size, mark_os_window_for_close, os_window_font_size,
|
||||
patch_global_colors, safe_pipe, set_application_quit_request,
|
||||
set_background_image, set_boss, set_clipboard_string, set_in_sequence_mode,
|
||||
thread_write, toggle_fullscreen, toggle_maximized
|
||||
)
|
||||
from .keys import get_shortcut, shortcut_matches
|
||||
from .layout.base import set_layout_options
|
||||
@@ -47,6 +48,7 @@ from .session import Session, create_sessions, get_os_window_sizing_data
|
||||
from .tabs import (
|
||||
SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager
|
||||
)
|
||||
from .types import SingleKey
|
||||
from .typing import PopenType, TypedDict
|
||||
from .utils import (
|
||||
func_name, get_editor, get_primary_selection, is_path_in_temp_dir,
|
||||
@@ -62,6 +64,8 @@ class OSWindowDict(TypedDict):
|
||||
platform_window_id: Optional[int]
|
||||
is_focused: bool
|
||||
tabs: List[TabDict]
|
||||
wm_class: str
|
||||
wm_name: str
|
||||
|
||||
|
||||
def listen_on(spec: str) -> int:
|
||||
@@ -137,9 +141,10 @@ class Boss:
|
||||
opts: Options,
|
||||
args: CLIOptions,
|
||||
cached_values: Dict[str, Any],
|
||||
new_os_window_trigger: Optional[Tuple[int, bool, int]]
|
||||
global_shortcuts: Dict[str, SingleKey]
|
||||
):
|
||||
set_layout_options(opts)
|
||||
self.cocoa_application_launched = False
|
||||
self.clipboard_buffers: Dict[str, str] = {}
|
||||
self.update_check_process: Optional[PopenType] = None
|
||||
self.window_id_map: WeakValueDictionary[int, Window] = WeakValueDictionary()
|
||||
@@ -164,8 +169,9 @@ class Boss:
|
||||
set_boss(self)
|
||||
self.opts, self.args = opts, args
|
||||
self.keymap = self.opts.keymap.copy()
|
||||
if new_os_window_trigger is not None:
|
||||
self.keymap.pop(new_os_window_trigger, None)
|
||||
self.global_shortcuts_map = {v: KeyAction(k) for k, v in global_shortcuts.items()}
|
||||
for sc in global_shortcuts.values():
|
||||
self.keymap.pop(sc, None)
|
||||
if is_macos:
|
||||
from .fast_data_types import (
|
||||
cocoa_set_notification_activated_callback
|
||||
@@ -173,12 +179,8 @@ class Boss:
|
||||
cocoa_set_notification_activated_callback(notification_activated)
|
||||
|
||||
def startup_first_child(self, os_window_id: Optional[int]) -> None:
|
||||
from kitty.launch import load_watch_modules
|
||||
startup_sessions = create_sessions(self.opts, self.args, default_session=self.opts.startup_session)
|
||||
watchers = load_watch_modules(self.args.watcher)
|
||||
for startup_session in startup_sessions:
|
||||
if watchers is not None and watchers.has_watchers:
|
||||
startup_session.add_watchers_to_all_windows(watchers)
|
||||
self.add_os_window(startup_session, os_window_id=os_window_id)
|
||||
os_window_id = None
|
||||
if self.args.start_as != 'normal':
|
||||
@@ -199,16 +201,20 @@ class Boss:
|
||||
if os_window_id is None:
|
||||
size_data = get_os_window_sizing_data(opts_for_size or self.opts, startup_session)
|
||||
wclass = wclass or getattr(startup_session, 'os_window_class', None) or self.args.cls or appname
|
||||
wname = wname or self.args.name or wclass
|
||||
with startup_notification_handler(do_notify=startup_id is not None, startup_id=startup_id) as pre_show_callback:
|
||||
os_window_id = create_os_window(
|
||||
initial_window_size_func(size_data, self.cached_values),
|
||||
pre_show_callback,
|
||||
self.args.title or appname, wname or self.args.name or wclass, wclass)
|
||||
tm = TabManager(os_window_id, self.opts, self.args, startup_session)
|
||||
self.args.title or appname, wname, wclass)
|
||||
else:
|
||||
wname = self.args.name or self.args.cls or appname
|
||||
wclass = self.args.cls or appname
|
||||
tm = TabManager(os_window_id, self.opts, self.args, wclass, wname, startup_session)
|
||||
self.os_window_map[os_window_id] = tm
|
||||
return os_window_id
|
||||
|
||||
def list_os_windows(self) -> Generator[OSWindowDict, None, None]:
|
||||
def list_os_windows(self, self_window: Optional[Window] = None) -> Generator[OSWindowDict, None, None]:
|
||||
with cached_process_data():
|
||||
active_tab, active_window = self.active_tab, self.active_window
|
||||
active_tab_manager = self.active_tab_manager
|
||||
@@ -217,7 +223,9 @@ class Boss:
|
||||
'id': os_window_id,
|
||||
'platform_window_id': platform_window_id(os_window_id),
|
||||
'is_focused': tm is active_tab_manager,
|
||||
'tabs': list(tm.list_tabs(active_tab, active_window)),
|
||||
'tabs': list(tm.list_tabs(active_tab, active_window, self_window)),
|
||||
'wm_class': tm.wm_class,
|
||||
'wm_name': tm.wm_name
|
||||
}
|
||||
|
||||
@property
|
||||
@@ -279,6 +287,13 @@ class Boss:
|
||||
if tab.matches(field, pat):
|
||||
yield tab
|
||||
found = True
|
||||
elif field in ('window_id', 'window_title'):
|
||||
wf = field.split('_')[1]
|
||||
tabs = {self.tab_for_window(w) for w in self.match_windows(f'{wf}:{exp}')}
|
||||
for q in tabs:
|
||||
if q:
|
||||
found = True
|
||||
yield q
|
||||
if not found:
|
||||
tabs = {self.tab_for_window(w) for w in self.match_windows(match)}
|
||||
for q in tabs:
|
||||
@@ -363,9 +378,17 @@ class Boss:
|
||||
c = command_for_name(cmd)
|
||||
opts, items = parse_subcommand_cli(c, items)
|
||||
payload = c.message_to_kitty(global_opts, opts, items)
|
||||
c.response_from_kitty(self, self.active_window, PayloadGetter(c, payload if isinstance(payload, dict) else {}))
|
||||
except (Exception, SystemExit) as e:
|
||||
self.show_error(_('remote_control mapping failed'), str(e))
|
||||
import types
|
||||
if isinstance(cast(types.GeneratorType, payload), types.GeneratorType):
|
||||
payloads = cast(types.GeneratorType, payload)
|
||||
for x in payloads:
|
||||
c.response_from_kitty(self, self.active_window, PayloadGetter(c, x if isinstance(x, dict) else {}))
|
||||
else:
|
||||
c.response_from_kitty(self, self.active_window, PayloadGetter(c, payload if isinstance(payload, dict) else {}))
|
||||
except (Exception, SystemExit):
|
||||
import traceback
|
||||
tb = traceback.format_exc()
|
||||
self.show_error(_('remote_control mapping failed'), tb)
|
||||
|
||||
def peer_message_received(self, msg_bytes: bytes) -> Optional[bytes]:
|
||||
cmd_prefix = b'\x1bP@kitty-cmd'
|
||||
@@ -496,10 +519,10 @@ class Boss:
|
||||
run_update_check(self.opts.update_check_interval * 60 * 60)
|
||||
self.update_check_started = True
|
||||
|
||||
def activate_tab_at(self, os_window_id: int, x: int) -> int:
|
||||
def activate_tab_at(self, os_window_id: int, x: int, is_double: bool = False) -> int:
|
||||
tm = self.os_window_map.get(os_window_id)
|
||||
if tm is not None:
|
||||
tm.activate_tab_at(x)
|
||||
tm.activate_tab_at(x, is_double)
|
||||
|
||||
def on_window_resize(self, os_window_id: int, w: int, h: int, dpi_changed: bool) -> None:
|
||||
if dpi_changed:
|
||||
@@ -638,20 +661,21 @@ class Boss:
|
||||
if t is not None:
|
||||
return t.active_window
|
||||
|
||||
def dispatch_special_key(self, key: int, native_key: int, action: int, mods: int) -> bool:
|
||||
def dispatch_possible_special_key(self, ev: KeyEvent) -> bool:
|
||||
# Handles shortcuts, return True if the key was consumed
|
||||
key_action = get_shortcut(self.keymap, mods, key, native_key)
|
||||
key_action = get_shortcut(self.keymap, ev)
|
||||
if key_action is None:
|
||||
sequences = get_shortcut(self.opts.sequence_map, mods, key, native_key)
|
||||
sequences = get_shortcut(self.opts.sequence_map, ev)
|
||||
if sequences and not isinstance(sequences, KeyAction):
|
||||
self.pending_sequences = sequences
|
||||
set_in_sequence_mode(True)
|
||||
return True
|
||||
if self.global_shortcuts_map and get_shortcut(self.global_shortcuts_map, ev):
|
||||
return True
|
||||
elif isinstance(key_action, KeyAction):
|
||||
self.current_key_press_info = key, native_key, action, mods
|
||||
return self.dispatch_action(key_action)
|
||||
|
||||
def process_sequence(self, key: int, native_key: int, action: Any, mods: int) -> None:
|
||||
def process_sequence(self, ev: KeyEvent) -> None:
|
||||
if not self.pending_sequences:
|
||||
set_in_sequence_mode(False)
|
||||
return
|
||||
@@ -659,7 +683,7 @@ class Boss:
|
||||
remaining = {}
|
||||
matched_action = None
|
||||
for seq, key_action in self.pending_sequences.items():
|
||||
if shortcut_matches(seq[0], mods, key, native_key):
|
||||
if shortcut_matches(seq[0], ev):
|
||||
seq = seq[1:]
|
||||
if seq:
|
||||
remaining[seq] = key_action
|
||||
@@ -830,7 +854,7 @@ class Boss:
|
||||
s.shutdown(socket.SHUT_RDWR)
|
||||
s.close()
|
||||
|
||||
def display_scrollback(self, window: Window, data: Optional[bytes], cmd: Optional[List[str]]) -> None:
|
||||
def display_scrollback(self, window: Window, data: Optional[bytes], cmd: List[str]) -> None:
|
||||
tab = self.active_tab
|
||||
if tab is not None:
|
||||
tab.new_special_window(
|
||||
@@ -842,7 +866,7 @@ class Boss:
|
||||
confpath = prepare_config_file_for_editing()
|
||||
# On macOS vim fails to handle SIGWINCH if it occurs early, so add a
|
||||
# small delay.
|
||||
cmd = [kitty_exe(), '+runpy', 'import os, sys, time; time.sleep(0.05); os.execvp(sys.argv[1], sys.argv[1:])'] + get_editor() + [confpath]
|
||||
cmd = [kitty_exe(), '+runpy', 'import os, sys, time; time.sleep(0.05); os.execvp(sys.argv[1], sys.argv[1:])'] + get_editor(self.opts) + [confpath]
|
||||
self.new_os_window(*cmd)
|
||||
|
||||
def get_output(self, source_window: Window, num_lines: Optional[int] = 1) -> str:
|
||||
@@ -1459,19 +1483,50 @@ class Boss:
|
||||
self._cleanup_tab_after_window_removal(tab)
|
||||
target_tab.make_active()
|
||||
|
||||
def select_tab(self) -> None:
|
||||
title = 'Choose a tab to switch to'
|
||||
lines = [title, '']
|
||||
fmt = ': {1}'
|
||||
tab_id_map: Dict[int, Optional[Union[str, int]]] = {}
|
||||
current_tab = self.active_tab
|
||||
done_tab_id: Optional[Union[str, int]] = None
|
||||
|
||||
for i, tab in enumerate(self.all_tabs):
|
||||
if tab is not current_tab:
|
||||
tab_id_map[len(tab_id_map)] = tab.id
|
||||
lines.append(fmt.format(i + 1, tab.title))
|
||||
|
||||
def done(data: Dict[str, Any], target_window_id: int, self: Boss) -> None:
|
||||
nonlocal done_tab_id
|
||||
done_tab_id = tab_id_map[int(data['groupdicts'][0]['index'])]
|
||||
|
||||
def done2(target_window_id: int, self: Boss) -> None:
|
||||
tab_id = done_tab_id
|
||||
if tab_id is not None:
|
||||
for i, tab in enumerate(self.all_tabs):
|
||||
if tab.id == tab_id:
|
||||
self.set_active_tab(tab)
|
||||
break
|
||||
|
||||
self._run_kitten(
|
||||
'hints', args=(
|
||||
'--ascending', '--customize-processing=::import::kitty.choose_entry',
|
||||
r'--regex=(?m)^:\s+.+$', '--window-title', title,
|
||||
), input_data='\r\n'.join(lines).encode('utf-8'), custom_callback=done, action_on_removal=done2
|
||||
)
|
||||
|
||||
def detach_window(self, *args: str) -> None:
|
||||
if not args or args[0] == 'new':
|
||||
return self._move_window_to(target_os_window_id='new')
|
||||
if args[0] == 'new-tab':
|
||||
return self._move_window_to(target_tab_id='new')
|
||||
lines = [
|
||||
'Choose a tab to move the window to',
|
||||
''
|
||||
]
|
||||
title = 'Choose a tab to move the window to'
|
||||
lines = [title, '']
|
||||
fmt = ': {1}'
|
||||
tab_id_map: Dict[int, Optional[Union[str, int]]] = {}
|
||||
current_tab = self.active_tab
|
||||
done_tab_id: Optional[Union[str, int]] = None
|
||||
done_called = False
|
||||
|
||||
for i, tab in enumerate(self.all_tabs):
|
||||
if tab is not current_tab:
|
||||
@@ -1485,10 +1540,13 @@ class Boss:
|
||||
lines.append(fmt.format(new_idx, 'New OS Window'))
|
||||
|
||||
def done(data: Dict[str, Any], target_window_id: int, self: Boss) -> None:
|
||||
nonlocal done_tab_id
|
||||
nonlocal done_tab_id, done_called
|
||||
done_tab_id = tab_id_map[int(data['groupdicts'][0]['index'])]
|
||||
done_called = True
|
||||
|
||||
def done2(target_window_id: int, self: Boss) -> None:
|
||||
if not done_called:
|
||||
return
|
||||
tab_id = done_tab_id
|
||||
target_window = None
|
||||
for w in self.all_windows:
|
||||
@@ -1503,7 +1561,7 @@ class Boss:
|
||||
self._run_kitten(
|
||||
'hints', args=(
|
||||
'--ascending', '--customize-processing=::import::kitty.choose_entry',
|
||||
r'--regex=(?m)^:\s+.+$',
|
||||
r'--regex=(?m)^:\s+.+$', '--window-title', title,
|
||||
), input_data='\r\n'.join(lines).encode('utf-8'), custom_callback=done, action_on_removal=done2
|
||||
)
|
||||
|
||||
@@ -1511,10 +1569,8 @@ class Boss:
|
||||
if not args or args[0] == 'new':
|
||||
return self._move_tab_to()
|
||||
|
||||
lines = [
|
||||
'Choose an OS window to move the tab to',
|
||||
''
|
||||
]
|
||||
title = 'Choose an OS window to move the tab to'
|
||||
lines = [title, '']
|
||||
fmt = ': {1}'
|
||||
os_window_id_map: Dict[int, Optional[int]] = {}
|
||||
current_os_window = getattr(self.active_tab, 'os_window_id', 0)
|
||||
@@ -1551,7 +1607,7 @@ class Boss:
|
||||
self._run_kitten(
|
||||
'hints', args=(
|
||||
'--ascending', '--customize-processing=::import::kitty.choose_entry',
|
||||
r'--regex=(?m)^:\s+.+$',
|
||||
r'--regex=(?m)^:\s+.+$', '--window-title', title,
|
||||
), input_data='\r\n'.join(lines).encode('utf-8'), custom_callback=done, action_on_removal=done2
|
||||
)
|
||||
|
||||
@@ -1580,3 +1636,27 @@ class Boss:
|
||||
self.set_active_window(w, switch_os_window_if_needed=True)
|
||||
if report:
|
||||
w.report_notification_activated(identifier)
|
||||
|
||||
def show_kitty_env_vars(self) -> None:
|
||||
w = self.active_window
|
||||
if w:
|
||||
output = '\n'.join(f'{k}={v}' for k, v in os.environ.items()).encode('utf-8')
|
||||
self.display_scrollback(w, output, ['less'])
|
||||
|
||||
def open_file(self, path: str) -> None:
|
||||
if path == ":cocoa::application launched::":
|
||||
self.cocoa_application_launched = True
|
||||
return
|
||||
|
||||
def new_os_window() -> None:
|
||||
self.new_os_window(path)
|
||||
|
||||
if self.cocoa_application_launched or not self.os_window_map:
|
||||
return new_os_window()
|
||||
tab = self.active_tab
|
||||
if tab is None:
|
||||
return new_os_window()
|
||||
w = tab.active_window
|
||||
self.new_window(path)
|
||||
if w is not None:
|
||||
tab.remove_window(w)
|
||||
|
||||
11
kitty/charsets.c
generated
11
kitty/charsets.c
generated
@@ -7,10 +7,8 @@
|
||||
|
||||
// Taken from consolemap.c in the linux vt driver sourcecode
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#define UTF8_ACCEPT 0
|
||||
#define UTF8_REJECT 1
|
||||
#include "data-types.h"
|
||||
|
||||
|
||||
static uint32_t charset_translations[5][256] = {
|
||||
/* 8-bit Latin-1 mapped to Unicode -- trivial mapping */
|
||||
@@ -231,7 +229,7 @@ static const uint8_t utf8_data[] = {
|
||||
};
|
||||
|
||||
uint32_t
|
||||
decode_utf8(uint32_t* state, uint32_t* codep, uint8_t byte) {
|
||||
decode_utf8(UTF8State* state, uint32_t* codep, uint8_t byte) {
|
||||
uint32_t type = utf8_data[byte];
|
||||
|
||||
*codep = (*state != UTF8_ACCEPT) ?
|
||||
@@ -245,7 +243,8 @@ decode_utf8(uint32_t* state, uint32_t* codep, uint8_t byte) {
|
||||
size_t
|
||||
decode_utf8_string(const char *src, size_t sz, uint32_t *dest) {
|
||||
// dest must be a zeroed array of size at least sz
|
||||
uint32_t codep = 0, state = 0, prev = UTF8_ACCEPT;
|
||||
uint32_t codep = 0;
|
||||
UTF8State state = 0, prev = UTF8_ACCEPT;
|
||||
size_t i, d;
|
||||
for (i = 0, d = 0; i < sz; i++) {
|
||||
switch(decode_utf8(&state, &codep, src[i])) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user