Compare commits
720 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ee963f6296 | ||
![]() |
b44b1086d3 | ||
![]() |
9978ea3b9c | ||
![]() |
710ea1c8d9 | ||
![]() |
ed60e07257 | ||
![]() |
1fea8028a3 | ||
![]() |
c481bdf704 | ||
![]() |
5105c52ef7 | ||
![]() |
20dcae876d | ||
![]() |
3104b21758 | ||
![]() |
15e41959eb | ||
![]() |
783f4014f8 | ||
![]() |
2a29f71b2c | ||
![]() |
5919c57527 | ||
![]() |
8b2f9acfb4 | ||
![]() |
6f4716fd60 | ||
![]() |
74f222abec | ||
![]() |
9f6067d899 | ||
![]() |
f72fd32bf0 | ||
![]() |
e396c8538a | ||
![]() |
432beac315 | ||
![]() |
256281c5de | ||
![]() |
f63242f7fb | ||
![]() |
7dac9e02b3 | ||
![]() |
99cbc06292 | ||
![]() |
749bac3d72 | ||
![]() |
1bb87e14a5 | ||
![]() |
9ea29dc508 | ||
![]() |
f2f0c6b4e9 | ||
![]() |
1e3cd89516 | ||
![]() |
019f789819 | ||
![]() |
9bac6ecbff | ||
![]() |
76ab6717c9 | ||
![]() |
b1ae7eea2f | ||
![]() |
c061d06aa1 | ||
![]() |
bf5351ed41 | ||
![]() |
338a07ecad | ||
![]() |
f26c96d51b | ||
![]() |
927cb4e622 | ||
![]() |
cbf562ecb3 | ||
![]() |
69fd438370 | ||
![]() |
57e7565b7f | ||
![]() |
4ad749607a | ||
![]() |
66442cd469 | ||
![]() |
afc42fe891 | ||
![]() |
58ab66f094 | ||
![]() |
1674224c1a | ||
![]() |
4aa452a2c7 | ||
![]() |
56ea6c406c | ||
![]() |
f8b547f92e | ||
![]() |
adb187685b | ||
![]() |
f3d0c7c2ad | ||
![]() |
4b77a1c71e | ||
![]() |
5ad348b23e | ||
![]() |
c627231b0f | ||
![]() |
31273aaabc | ||
![]() |
d1614f6bc4 | ||
![]() |
95ae3642f9 | ||
![]() |
b624cfe262 | ||
![]() |
c6da845f33 | ||
![]() |
869819eb89 | ||
![]() |
f986a32185 | ||
![]() |
0532f67282 | ||
![]() |
3a8c934b19 | ||
![]() |
ab71dac282 | ||
![]() |
fa5fe5d293 | ||
![]() |
d87cf61d26 | ||
![]() |
dd8c795ec6 | ||
![]() |
a1b682d0da | ||
![]() |
e540a79032 | ||
![]() |
1c524238c8 | ||
![]() |
843c714088 | ||
![]() |
03fd9a970b | ||
![]() |
09c76d95c7 | ||
![]() |
a3e462bbba | ||
![]() |
28200fb9e5 | ||
![]() |
2c06ce761b | ||
![]() |
1947ba9c68 | ||
![]() |
f95f636f1f | ||
![]() |
ea16890fd3 | ||
![]() |
c7044b198c | ||
![]() |
5d6c021e26 | ||
![]() |
79d6d26784 | ||
![]() |
76cf58915b | ||
![]() |
cbe0478572 | ||
![]() |
ae25ad4047 | ||
![]() |
1ce23c079e | ||
![]() |
d87bfdff1a | ||
![]() |
4eb5885017 | ||
![]() |
f2db80520a | ||
![]() |
1d7f041f55 | ||
![]() |
fbdee68667 | ||
![]() |
581b87b20c | ||
![]() |
f660ec8bd2 | ||
![]() |
47e50abe24 | ||
![]() |
0c1f3d724d | ||
![]() |
4a95e8e48c | ||
![]() |
f0d276062b | ||
![]() |
7b593b9d7c | ||
![]() |
0538d6db6a | ||
![]() |
8d31f6ce2e | ||
![]() |
fc14c5b070 | ||
![]() |
fe6b46f4e7 | ||
![]() |
c07b1ac2f0 | ||
![]() |
730c6884f7 | ||
![]() |
b7fa8e5947 | ||
![]() |
3fdbd5f7ba | ||
![]() |
7f0d22a31e | ||
![]() |
8607d463ff | ||
![]() |
002cce4e81 | ||
![]() |
8660cdaad5 | ||
![]() |
d61ee46210 | ||
![]() |
4cd49632b2 | ||
![]() |
cdd2099090 | ||
![]() |
f70ba14abd | ||
![]() |
3b3e75c1dc | ||
![]() |
61fd41116a | ||
![]() |
fe085001cb | ||
![]() |
1a1d3db207 | ||
![]() |
79dd3776e2 | ||
![]() |
1bd5ea6f73 | ||
![]() |
fb2516fbf6 | ||
![]() |
bc156266c6 | ||
![]() |
5100f4ff7d | ||
![]() |
660d9dc671 | ||
![]() |
ac582ccf7c | ||
![]() |
c5508e7d19 | ||
![]() |
dda242c96e | ||
![]() |
facda0c3fb | ||
![]() |
75dec598cc | ||
![]() |
48a1a10330 | ||
![]() |
6dd9bf0038 | ||
![]() |
adf4066b69 | ||
![]() |
40973417d0 | ||
![]() |
a62b26cd2f | ||
![]() |
04d36361b1 | ||
![]() |
dbc4edc583 | ||
![]() |
692168f8dd | ||
![]() |
fefda69ac3 | ||
![]() |
c33b82c634 | ||
![]() |
ce2a3773d2 | ||
![]() |
09ccda4d28 | ||
![]() |
27b254db8a | ||
![]() |
e5364392ee | ||
![]() |
71d0481da8 | ||
![]() |
76fd4fa8df | ||
![]() |
d31e6c8b2a | ||
![]() |
a80cf3db9c | ||
![]() |
603639ad44 | ||
![]() |
016a26cf98 | ||
![]() |
5c8f984ea1 | ||
![]() |
ac568900a5 | ||
![]() |
4b8010a6f4 | ||
![]() |
645b4b0031 | ||
![]() |
71e62f96fa | ||
![]() |
a58b1998a9 | ||
![]() |
e2e6770ed1 | ||
![]() |
a88e30179a | ||
![]() |
3b108945f3 | ||
![]() |
136d6e9341 | ||
![]() |
a5a73f8352 | ||
![]() |
4f857fc4e9 | ||
![]() |
f73cae0738 | ||
![]() |
47ff4c96e0 | ||
![]() |
75d6ee539a | ||
![]() |
9ab2e563bb | ||
![]() |
5ad2ec8f71 | ||
![]() |
69dcea5148 | ||
![]() |
8475768ad1 | ||
![]() |
0c5ef5578c | ||
![]() |
593d70a42f | ||
![]() |
d1969f74ac | ||
![]() |
2f8b0dc899 | ||
![]() |
c4c2d06571 | ||
![]() |
076bcccde4 | ||
![]() |
fd8cacaa67 | ||
![]() |
0d6a1d3fdb | ||
![]() |
c0eb6985ee | ||
![]() |
4b82e0aa2b | ||
![]() |
78790f6ef7 | ||
![]() |
95327bff18 | ||
![]() |
9b30eb8eb8 | ||
![]() |
a4b0e6d937 | ||
![]() |
4c27743931 | ||
![]() |
deb9033739 | ||
![]() |
78a2a815f3 | ||
![]() |
098a46f077 | ||
![]() |
168dd31367 | ||
![]() |
148a009a95 | ||
![]() |
5327646d58 | ||
![]() |
0c24da1412 | ||
![]() |
956bb8f02b | ||
![]() |
b4a90a7a22 | ||
![]() |
078ee42be3 | ||
![]() |
1be89cb146 | ||
![]() |
80eb406b82 | ||
![]() |
e39745113a | ||
![]() |
e854b38872 | ||
![]() |
4a3b4ea2b2 | ||
![]() |
5477eb87c1 | ||
![]() |
9c8c53bffb | ||
![]() |
8f49a227b7 | ||
![]() |
cd57612059 | ||
![]() |
d1e45ef768 | ||
![]() |
a2018d7b20 | ||
![]() |
eb21a58aa4 | ||
![]() |
4408101b8d | ||
![]() |
ddf7173ae9 | ||
![]() |
a8d2185611 | ||
![]() |
6265adfcd4 | ||
![]() |
52936b9b68 | ||
![]() |
bf6cce23da | ||
![]() |
25ccdfe495 | ||
![]() |
5cb2fa6f75 | ||
![]() |
fccb172ae9 | ||
![]() |
2d3db866e6 | ||
![]() |
3b8ab10776 | ||
![]() |
3327d32d66 | ||
![]() |
8a0613bd27 | ||
![]() |
a977e688cc | ||
![]() |
8c1db16c79 | ||
![]() |
b82c70fd3c | ||
![]() |
977c9999dd | ||
![]() |
f8ece6392d | ||
![]() |
4cc1fa2111 | ||
![]() |
a253871942 | ||
![]() |
8ada93d0cb | ||
![]() |
e5a3be3c46 | ||
![]() |
c3ef4d2908 | ||
![]() |
0eb6f28375 | ||
![]() |
07d5ae749d | ||
![]() |
4b9281ee6e | ||
![]() |
b9b49602cd | ||
![]() |
f5ff9c0371 | ||
![]() |
67b828cf21 | ||
![]() |
2ef04826fd | ||
![]() |
9ca3cbd94b | ||
![]() |
afa23532b6 | ||
![]() |
bb58710fa8 | ||
![]() |
4956d36ee6 | ||
![]() |
3f367857fc | ||
![]() |
2ee6e005d0 | ||
![]() |
ecad3e75ff | ||
![]() |
0c2c847af3 | ||
![]() |
68124f60c7 | ||
![]() |
0016199ec3 | ||
![]() |
8e2c55a5da | ||
![]() |
05fe3e7ec3 | ||
![]() |
1b52365541 | ||
![]() |
ccd6fd26ec | ||
![]() |
6853a5423f | ||
![]() |
9c1edb5449 | ||
![]() |
8ee4deddb4 | ||
![]() |
c2da843dfd | ||
![]() |
018a888578 | ||
![]() |
b7d76fe26f | ||
![]() |
da5ac6aeeb | ||
![]() |
72d085525b | ||
![]() |
2a1b51ec94 | ||
![]() |
85b1cfa44f | ||
![]() |
860f1f635c | ||
![]() |
a185ce317b | ||
![]() |
b4a3ca999a | ||
![]() |
d1eb8ccc52 | ||
![]() |
b9939611d3 | ||
![]() |
a5853c4de8 | ||
![]() |
6c2c436917 | ||
![]() |
62d3786c66 | ||
![]() |
df2d0b33cc | ||
![]() |
07994d10e9 | ||
![]() |
6af1850ab4 | ||
![]() |
3159a7bec7 | ||
![]() |
b1becb12c0 | ||
![]() |
7716f98856 | ||
![]() |
423133bc3c | ||
![]() |
1eeb914a4f | ||
![]() |
1ac56a7ac2 | ||
![]() |
321119e001 | ||
![]() |
e1e2f94681 | ||
![]() |
268fc1a040 | ||
![]() |
450c019b4e | ||
![]() |
26f6b3ea82 | ||
![]() |
6be0902c09 | ||
![]() |
0b16c1eeba | ||
![]() |
dddc2a50a8 | ||
![]() |
4bfb1f616c | ||
![]() |
4ff7f6df06 | ||
![]() |
0b456d14a4 | ||
![]() |
ff9414d9ea | ||
![]() |
4f93864db7 | ||
![]() |
34abe2ceba | ||
![]() |
331b7c754c | ||
![]() |
f5670d81d4 | ||
![]() |
9a47cda892 | ||
![]() |
08a0423b78 | ||
![]() |
daeed453dc | ||
![]() |
3cdb9a7dfe | ||
![]() |
4e0d9b1b27 | ||
![]() |
95debc59d1 | ||
![]() |
d1fd22ae80 | ||
![]() |
39bb949973 | ||
![]() |
5081e65570 | ||
![]() |
c1938d12f0 | ||
![]() |
a8b9c5bea5 | ||
![]() |
f24d70b7ec | ||
![]() |
fca336c32d | ||
![]() |
5cf7575967 | ||
![]() |
844f20d01f | ||
![]() |
e3ced14393 | ||
![]() |
b7eb9bfef1 | ||
![]() |
592efb4b97 | ||
![]() |
ca5471fb03 | ||
![]() |
7aed08be40 | ||
![]() |
08314d414f | ||
![]() |
132c4f1f68 | ||
![]() |
e288096c26 | ||
![]() |
7ac017b154 | ||
![]() |
5cd990bec5 | ||
![]() |
a2445359c4 | ||
![]() |
ea0704148d | ||
![]() |
caddf59db5 | ||
![]() |
e3b6bb71a0 | ||
![]() |
faea09bbde | ||
![]() |
a3bb2df94f | ||
![]() |
d4190c9c02 | ||
![]() |
c5dfabb15b | ||
![]() |
a2d2cfea59 | ||
![]() |
a06fc4ff11 | ||
![]() |
50e33a6665 | ||
![]() |
bdbd0263a1 | ||
![]() |
f7b50ce727 | ||
![]() |
bfe56d04d5 | ||
![]() |
6c5c5b2ec0 | ||
![]() |
0811b1d5ac | ||
![]() |
fc99c99b74 | ||
![]() |
af5e423ea5 | ||
![]() |
2409d46600 | ||
![]() |
95fc1d64c8 | ||
![]() |
c9636598fc | ||
![]() |
8102fddceb | ||
![]() |
918ccdba5c | ||
![]() |
941915b862 | ||
![]() |
5a4aac7e09 | ||
![]() |
0a26321e9d | ||
![]() |
16233b16e7 | ||
![]() |
da558f2678 | ||
![]() |
00137c4189 | ||
![]() |
8c1ad59de7 | ||
![]() |
b7b5b9bbf5 | ||
![]() |
126c0505e2 | ||
![]() |
200aee9acf | ||
![]() |
61a78efa83 | ||
![]() |
3a5aecc216 | ||
![]() |
4dd9fe5cfd | ||
![]() |
d3de3448cc | ||
![]() |
96e427cd6a | ||
![]() |
8e52c6d12b | ||
![]() |
b783e12b93 | ||
![]() |
f5add68100 | ||
![]() |
4af44c5460 | ||
![]() |
6237377a59 | ||
![]() |
cd638588c4 | ||
![]() |
06a57842af | ||
![]() |
13e96c7ec8 | ||
![]() |
c4ece2a141 | ||
![]() |
96b3db0b8c | ||
![]() |
e475560af0 | ||
![]() |
61a6e670eb | ||
![]() |
2fb0d99f00 | ||
![]() |
8b8c7ecf1d | ||
![]() |
d26c114b5d | ||
![]() |
010f4d167d | ||
![]() |
799f5b8239 | ||
![]() |
6e411d60f2 | ||
![]() |
2f81c360bd | ||
![]() |
be0370cb0e | ||
![]() |
ed81b62ec2 | ||
![]() |
19cfd99439 | ||
![]() |
c03828e032 | ||
![]() |
405a897230 | ||
![]() |
03fee2ac33 | ||
![]() |
6ef757a8f8 | ||
![]() |
12ce9f2e3b | ||
![]() |
b25c593309 | ||
![]() |
ae81edfcbf | ||
![]() |
2d7706ec4f | ||
![]() |
1636ac88fc | ||
![]() |
a7c108a11c | ||
![]() |
aafe7deae7 | ||
![]() |
1057fd23be | ||
![]() |
622cc3f9c7 | ||
![]() |
19ffb0b51f | ||
![]() |
08a8bddd38 | ||
![]() |
8c457fc992 | ||
![]() |
37256ec6a2 | ||
![]() |
bec690532d | ||
![]() |
dac13701e3 | ||
![]() |
5bfb3925ba | ||
![]() |
ea7c54d79d | ||
![]() |
0651586339 | ||
![]() |
c33629aae5 | ||
![]() |
eb40cce45e | ||
![]() |
e14bc5b64e | ||
![]() |
90d1023783 | ||
![]() |
d969a6b6b7 | ||
![]() |
5213edfa6c | ||
![]() |
ef12d90b74 | ||
![]() |
6f720a0b11 | ||
![]() |
3e24e96af5 | ||
![]() |
542c655348 | ||
![]() |
140ac93884 | ||
![]() |
e4033ca4df | ||
![]() |
0d679951bc | ||
![]() |
6ebc02b68d | ||
![]() |
6e54589db4 | ||
![]() |
c85c277415 | ||
![]() |
306c7ea2be | ||
![]() |
37db4578c8 | ||
![]() |
0ddc1a21a6 | ||
![]() |
7bb3c31cee | ||
![]() |
1493c55348 | ||
![]() |
e2523c25cb | ||
![]() |
2206c509be | ||
![]() |
34a0b206f8 | ||
![]() |
051fed0c24 | ||
![]() |
ef833b3861 | ||
![]() |
40442ac02f | ||
![]() |
e86b9a112e | ||
![]() |
e7c71df0b7 | ||
![]() |
c972051611 | ||
![]() |
29c2f095a6 | ||
![]() |
3731b099bb | ||
![]() |
6e42cb152e | ||
![]() |
2f6bef7ff2 | ||
![]() |
300884d50f | ||
![]() |
c83733c533 | ||
![]() |
0bb0bf9b85 | ||
![]() |
d78eb7d28f | ||
![]() |
e00e991080 | ||
![]() |
4eaf346090 | ||
![]() |
bade7be021 | ||
![]() |
8bf5c4ed7f | ||
![]() |
1b91a667fb | ||
![]() |
093bdf640a | ||
![]() |
a115e848c6 | ||
![]() |
b2716375ac | ||
![]() |
0065aae6b6 | ||
![]() |
9b21de2fe7 | ||
![]() |
41201068ef | ||
![]() |
bf3597a519 | ||
![]() |
c8d5b2da45 | ||
![]() |
a52f469e16 | ||
![]() |
298aeb7536 | ||
![]() |
540fcc0b69 | ||
![]() |
a3837a1e4e | ||
![]() |
29dd5ae605 | ||
![]() |
0974705dd9 | ||
![]() |
1cf1b886cd | ||
![]() |
262c76eace | ||
![]() |
9732a92d7a | ||
![]() |
c71da8338b | ||
![]() |
7ae94e145d | ||
![]() |
85743aebd5 | ||
![]() |
15f82858b7 | ||
![]() |
037de64ea2 | ||
![]() |
359c390218 | ||
![]() |
0929500360 | ||
![]() |
d79a02db44 | ||
![]() |
4fb253a300 | ||
![]() |
d3973b8fad | ||
![]() |
6d90a181ce | ||
![]() |
3461204741 | ||
![]() |
4838bcbb8f | ||
![]() |
ae0c4d927a | ||
![]() |
b04cb2585d | ||
![]() |
237c941395 | ||
![]() |
f0c2d3d75a | ||
![]() |
dc4774c147 | ||
![]() |
c127daa552 | ||
![]() |
d0b06bd55f | ||
![]() |
ae0248b5bc | ||
![]() |
3698b37588 | ||
![]() |
63a47d0ba5 | ||
![]() |
781159af7d | ||
![]() |
6181487bad | ||
![]() |
53468541f7 | ||
![]() |
94bf448eda | ||
![]() |
2485482aec | ||
![]() |
f6d85baadb | ||
![]() |
c9c5ad43a5 | ||
![]() |
ed221f32fe | ||
![]() |
143c01edcb | ||
![]() |
ead241f38c | ||
![]() |
e3c94210f2 | ||
![]() |
f14c0f5a63 | ||
![]() |
62dd468500 | ||
![]() |
057b196024 | ||
![]() |
2fbe680aed | ||
![]() |
ce77b91bf6 | ||
![]() |
bb83a14d7a | ||
![]() |
68624e6c45 | ||
![]() |
0ae8d9ed42 | ||
![]() |
f2c357a209 | ||
![]() |
cd90821b93 | ||
![]() |
9184395cba | ||
![]() |
31439f311d | ||
![]() |
8d7e57f64b | ||
![]() |
f4a9b65f78 | ||
![]() |
9cf9e0639f | ||
![]() |
4d4b785a58 | ||
![]() |
7a27af8bfc | ||
![]() |
4ffd281de3 | ||
![]() |
5d523116bf | ||
![]() |
8f5cbed46f | ||
![]() |
948cbfbf0e | ||
![]() |
0063dc3925 | ||
![]() |
e05c3b6fd7 | ||
![]() |
51c42795fc | ||
![]() |
a670836d7a | ||
![]() |
da80531c22 | ||
![]() |
0df4012edc | ||
![]() |
d936e7106a | ||
![]() |
6db39e827e | ||
![]() |
5fa9e88482 | ||
![]() |
f3cb95ac1f | ||
![]() |
0306c592a7 | ||
![]() |
62fae29395 | ||
![]() |
f5f30605a8 | ||
![]() |
9f9248fd28 | ||
![]() |
23b0f7dec0 | ||
![]() |
305e5b3533 | ||
![]() |
32c4a9d65e | ||
![]() |
7e1260c9e9 | ||
![]() |
40bdea7335 | ||
![]() |
1bcfff3b79 | ||
![]() |
3e89c4c2f4 | ||
![]() |
4031815a8d | ||
![]() |
8b8c53fc4c | ||
![]() |
4d103ca16d | ||
![]() |
2dc518d8b0 | ||
![]() |
8f5f432ab6 | ||
![]() |
6b30736776 | ||
![]() |
d3334ecb06 | ||
![]() |
e8811ac6fb | ||
![]() |
0599f73fac | ||
![]() |
69e4428d80 | ||
![]() |
0e63dc18ff | ||
![]() |
f14371e909 | ||
![]() |
78cdc7d0de | ||
![]() |
1baf8928a0 | ||
![]() |
6fd901fd3d | ||
![]() |
0493b79caf | ||
![]() |
1dc9157727 | ||
![]() |
72e0fb14fe | ||
![]() |
fa30a04f2a | ||
![]() |
2f6e7d6ecd | ||
![]() |
a014eee968 | ||
![]() |
2610f5b4e2 | ||
![]() |
dc90ef776e | ||
![]() |
063868b311 | ||
![]() |
25a112469c | ||
![]() |
ab9b14215c | ||
![]() |
74386be017 | ||
![]() |
1bf8b262ea | ||
![]() |
6459e5c8ca | ||
![]() |
45d005ce65 | ||
![]() |
e03c428728 | ||
![]() |
9d286de834 | ||
![]() |
7eb35d7275 | ||
![]() |
b0404867b7 | ||
![]() |
e1bc6477b1 | ||
![]() |
6b4e6d2fa5 | ||
![]() |
8a3aae2caf | ||
![]() |
9fa404c390 | ||
![]() |
65a0fa4f35 | ||
![]() |
29f040716c | ||
![]() |
e2f9eb6a6f | ||
![]() |
f2d255d423 | ||
![]() |
bec46fc2fc | ||
![]() |
bc5cbf3e87 | ||
![]() |
ac524b6c34 | ||
![]() |
4cb2fd4f79 | ||
![]() |
a10b45fb1f | ||
![]() |
3c93f00d04 | ||
![]() |
c89df01e13 | ||
![]() |
5c39d09053 | ||
![]() |
cde8b7e810 | ||
![]() |
c9dfcd2781 | ||
![]() |
7e1b7bb8b3 | ||
![]() |
bcd4b3a680 | ||
![]() |
d42e67bdad | ||
![]() |
e6399c947a | ||
![]() |
a0de1be65f | ||
![]() |
b65c26966a | ||
![]() |
95b73f197f | ||
![]() |
89e0e7e69c | ||
![]() |
82695edaff | ||
![]() |
8d7ed3e0fc | ||
![]() |
405fe377d2 | ||
![]() |
fe9bbec92e | ||
![]() |
263146ebe2 | ||
![]() |
8827b6e738 | ||
![]() |
0ef24f3c75 | ||
![]() |
8e47d0267b | ||
![]() |
9d9fa60ece | ||
![]() |
602e4eb606 | ||
![]() |
764a2365af | ||
![]() |
8c74b80704 | ||
![]() |
509cf306f5 | ||
![]() |
fb8c83e07c | ||
![]() |
d28024bb60 | ||
![]() |
e6bb1a56eb | ||
![]() |
5293e8a819 | ||
![]() |
bed37ac844 | ||
![]() |
e7354e7308 | ||
![]() |
18001c3251 | ||
![]() |
3a01beb050 | ||
![]() |
fc545cd048 | ||
![]() |
f5f887efd9 | ||
![]() |
1a530cb96a | ||
![]() |
6a4842f110 | ||
![]() |
f60068eabd | ||
![]() |
7eb6b29d4c | ||
![]() |
0d2a6c7346 | ||
![]() |
898d988799 | ||
![]() |
7a5ef3da2b | ||
![]() |
cb8d30f938 | ||
![]() |
0778c67429 | ||
![]() |
29b96be84f | ||
![]() |
e634eb102e | ||
![]() |
f49a029c49 | ||
![]() |
f9250e28b5 | ||
![]() |
16bf3b8820 | ||
![]() |
200d0d642b | ||
![]() |
970e45559b | ||
![]() |
13d935a401 | ||
![]() |
a7e3e5915c | ||
![]() |
568f1f9d72 | ||
![]() |
7195bda7a2 | ||
![]() |
9a58de6d5a | ||
![]() |
5ced72498e | ||
![]() |
d58b618c74 | ||
![]() |
713dd24ab9 | ||
![]() |
a94cf4b3a2 | ||
![]() |
600e417154 | ||
![]() |
7bd842a530 | ||
![]() |
5ae2cc01ac | ||
![]() |
138a8f1602 | ||
![]() |
d27761a499 | ||
![]() |
7c9c982df7 | ||
![]() |
f184258f0e | ||
![]() |
88674a1957 | ||
![]() |
b68e9a10e0 | ||
![]() |
afdaaf1825 | ||
![]() |
57af5f3106 | ||
![]() |
aa34b91856 | ||
![]() |
0c10db9f14 | ||
![]() |
6fca26972b | ||
![]() |
9f44ec7c21 | ||
![]() |
67794d3f6f | ||
![]() |
1941a440d8 | ||
![]() |
80c3833031 | ||
![]() |
9503c9fe50 | ||
![]() |
2030599e1d | ||
![]() |
75571fb804 | ||
![]() |
4047c89c24 | ||
![]() |
e55444e6fe | ||
![]() |
913a1404be | ||
![]() |
355ac91c0a | ||
![]() |
cd809106c4 | ||
![]() |
80fe32be32 | ||
![]() |
15ceb18fcb | ||
![]() |
78b914b3d8 | ||
![]() |
22c2fe9610 | ||
![]() |
652079b26c | ||
![]() |
f38d1585e8 | ||
![]() |
98aa046a4d | ||
![]() |
46097617b4 | ||
![]() |
c98d207eb9 | ||
![]() |
b52e76334e | ||
![]() |
94599102e9 | ||
![]() |
89982912c1 | ||
![]() |
bec9483892 | ||
![]() |
028eeb442c | ||
![]() |
f1ab7bf0f6 | ||
![]() |
9ea0aa4934 | ||
![]() |
96fd18f994 | ||
![]() |
57ace2d0b8 | ||
![]() |
cdf51b1304 | ||
![]() |
120fffa42c | ||
![]() |
180db3c77b | ||
![]() |
b1a197ef57 | ||
![]() |
d203075a2e | ||
![]() |
9ce4e36aa2 | ||
![]() |
c16dbc01f0 | ||
![]() |
60611b8a4a | ||
![]() |
e9e82d94ac | ||
![]() |
41719aa48c | ||
![]() |
1d71196de3 | ||
![]() |
4ccd564849 | ||
![]() |
5957e1101c | ||
![]() |
973467b1ca | ||
![]() |
27c3382a6a | ||
![]() |
e20ec96fee | ||
![]() |
6a8b31571b | ||
![]() |
061a730dd3 | ||
![]() |
2d174f9bff | ||
![]() |
3971c460d1 | ||
![]() |
c87e3e98a3 | ||
![]() |
66681f94e0 | ||
![]() |
33b1131a14 | ||
![]() |
3e11476d32 | ||
![]() |
2bb3353672 | ||
![]() |
8da600a62e | ||
![]() |
8cbf8d5003 | ||
![]() |
a4daabb28a | ||
![]() |
a469191311 | ||
![]() |
9117842a45 | ||
![]() |
9252378c82 | ||
![]() |
1e5b02302b |
1
.gitattributes
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
build.zig.zon.nix linguist-generated=true
|
||||||
vendor/** linguist-vendored
|
vendor/** linguist-vendored
|
||||||
website/** linguist-documentation
|
website/** linguist-documentation
|
||||||
pkg/breakpad/vendor/** linguist-vendored
|
pkg/breakpad/vendor/** linguist-vendored
|
||||||
|
29
.github/workflows/nix.yml
vendored
@@ -1,6 +1,31 @@
|
|||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
name: Nix
|
name: Nix
|
||||||
jobs:
|
jobs:
|
||||||
|
required:
|
||||||
|
name: "Required Checks: Nix"
|
||||||
|
runs-on: namespace-profile-ghostty-sm
|
||||||
|
needs:
|
||||||
|
- check-zig-cache-hash
|
||||||
|
steps:
|
||||||
|
- id: status
|
||||||
|
name: Determine status
|
||||||
|
run: |
|
||||||
|
results=$(tr -d '\n' <<< '${{ toJSON(needs.*.result) }}')
|
||||||
|
if ! grep -q -v -E '(failure|cancelled)' <<< "$results"; then
|
||||||
|
result="failed"
|
||||||
|
else
|
||||||
|
result="success"
|
||||||
|
fi
|
||||||
|
{
|
||||||
|
echo "result=${result}"
|
||||||
|
echo "results=${results}"
|
||||||
|
} | tee -a "$GITHUB_OUTPUT"
|
||||||
|
- if: always() && steps.status.outputs.result != 'success'
|
||||||
|
name: Check for failed status
|
||||||
|
run: |
|
||||||
|
echo "One or more required build workflows failed: ${{ steps.status.outputs.results }}"
|
||||||
|
exit 1
|
||||||
|
|
||||||
check-zig-cache-hash:
|
check-zig-cache-hash:
|
||||||
if: github.repository == 'ghostty-org/ghostty'
|
if: github.repository == 'ghostty-org/ghostty'
|
||||||
runs-on: namespace-profile-ghostty-sm
|
runs-on: namespace-profile-ghostty-sm
|
||||||
@@ -25,5 +50,5 @@ jobs:
|
|||||||
name: ghostty
|
name: ghostty
|
||||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
useDaemon: false # sometimes fails on short jobs
|
useDaemon: false # sometimes fails on short jobs
|
||||||
- name: Check Zig cache hash
|
- name: Check Zig cache
|
||||||
run: nix develop -c ./nix/build-support/check-zig-cache-hash.sh
|
run: nix develop -c ./nix/build-support/check-zig-cache.sh
|
||||||
|
74
.github/workflows/publish-tag.yml
vendored
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: "Version to deploy (format: vX.Y.Z)"
|
||||||
|
required: true
|
||||||
|
|
||||||
|
name: Publish Tagged Release
|
||||||
|
|
||||||
|
# We must only run one release workflow at a time to prevent corrupting
|
||||||
|
# our release artifacts.
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
setup:
|
||||||
|
runs-on: namespace-profile-ghostty-sm
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.extract_version.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- name: Validate Version Input
|
||||||
|
run: |
|
||||||
|
if [[ ! "${{ github.event.inputs.version }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo "Error: Version must follow the format vX.Y.Z (e.g., v1.0.0)."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Version is valid: ${{ github.event.inputs.version }}"
|
||||||
|
|
||||||
|
- name: Exract the Version
|
||||||
|
id: extract_version
|
||||||
|
run: |
|
||||||
|
VERSION=${{ github.event.inputs.version }}
|
||||||
|
VERSION=${VERSION#v}
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
upload:
|
||||||
|
needs: [setup]
|
||||||
|
runs-on: namespace-profile-ghostty-sm
|
||||||
|
env:
|
||||||
|
GHOSTTY_VERSION: ${{ needs.setup.outputs.version }}
|
||||||
|
steps:
|
||||||
|
- name: Validate Release Files
|
||||||
|
run: |
|
||||||
|
BASE="https://release.files.ghostty.org/${GHOSTTY_VERSION}"
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-${GHOSTTY_VERSION}.tar.gz" | grep -q "^200$" || exit 1
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-${GHOSTTY_VERSION}.tar.gz.minisig" | grep -q "^200$" || exit 1
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-source.tar.gz" | grep -q "^200$" || exit 1
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-source.tar.gz.minisig" | grep -q "^200$" || exit 1
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-macos-universal.zip" | grep -q "^200$" || exit 1
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/ghostty-macos-universal-dsym.zip" | grep -q "^200$" || exit 1
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/Ghostty.dmg" | grep -q "^200$" || exit 1
|
||||||
|
curl -I -s -o /dev/null -w "%{http_code}" "${BASE}/appcast-staged.xml" | grep -q "^200$" || exit 1
|
||||||
|
|
||||||
|
- name: Download Staged Appcast
|
||||||
|
run: |
|
||||||
|
curl -L https://release.files.ghostty.org/${GHOSTTY_VERSION}/appcast-staged.xml > appcast-staged.xml
|
||||||
|
mv appcast-staged.xml appcast.xml
|
||||||
|
|
||||||
|
- name: Upload Appcast
|
||||||
|
run: |
|
||||||
|
rm -rf blob
|
||||||
|
mkdir blob
|
||||||
|
mv appcast.xml blob/appcast.xml
|
||||||
|
- name: Upload Appcast to R2
|
||||||
|
uses: ryand56/r2-upload-action@latest
|
||||||
|
with:
|
||||||
|
r2-account-id: ${{ secrets.CF_R2_RELEASE_ACCOUNT_ID }}
|
||||||
|
r2-access-key-id: ${{ secrets.CF_R2_RELEASE_AWS_KEY }}
|
||||||
|
r2-secret-access-key: ${{ secrets.CF_R2_RELEASE_SECRET_KEY }}
|
||||||
|
r2-bucket: ghostty-release
|
||||||
|
source-dir: blob
|
||||||
|
destination-dir: ./
|
2
.github/workflows/release-pr.yml
vendored
@@ -68,7 +68,7 @@ jobs:
|
|||||||
# Setup Sparkle
|
# Setup Sparkle
|
||||||
- name: Setup Sparkle
|
- name: Setup Sparkle
|
||||||
env:
|
env:
|
||||||
SPARKLE_VERSION: 2.6.3
|
SPARKLE_VERSION: 2.6.4
|
||||||
run: |
|
run: |
|
||||||
mkdir -p .action/sparkle
|
mkdir -p .action/sparkle
|
||||||
cd .action/sparkle
|
cd .action/sparkle
|
||||||
|
21
.github/workflows/release-tag.yml
vendored
@@ -7,6 +7,7 @@ on:
|
|||||||
upload:
|
upload:
|
||||||
description: "Upload final artifacts to R2"
|
description: "Upload final artifacts to R2"
|
||||||
default: false
|
default: false
|
||||||
|
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- "v[0-9]+.[0-9]+.[0-9]+"
|
- "v[0-9]+.[0-9]+.[0-9]+"
|
||||||
@@ -135,7 +136,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup Sparkle
|
- name: Setup Sparkle
|
||||||
env:
|
env:
|
||||||
SPARKLE_VERSION: 2.6.3
|
SPARKLE_VERSION: 2.6.4
|
||||||
run: |
|
run: |
|
||||||
mkdir -p .action/sparkle
|
mkdir -p .action/sparkle
|
||||||
cd .action/sparkle
|
cd .action/sparkle
|
||||||
@@ -297,7 +298,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Setup Sparkle
|
- name: Setup Sparkle
|
||||||
env:
|
env:
|
||||||
SPARKLE_VERSION: 2.6.3
|
SPARKLE_VERSION: 2.6.4
|
||||||
run: |
|
run: |
|
||||||
mkdir -p .action/sparkle
|
mkdir -p .action/sparkle
|
||||||
cd .action/sparkle
|
cd .action/sparkle
|
||||||
@@ -367,6 +368,7 @@ jobs:
|
|||||||
mv ghostty-macos-universal.zip blob/${GHOSTTY_VERSION}/ghostty-macos-universal.zip
|
mv ghostty-macos-universal.zip blob/${GHOSTTY_VERSION}/ghostty-macos-universal.zip
|
||||||
mv ghostty-macos-universal-dsym.zip blob/${GHOSTTY_VERSION}/ghostty-macos-universal-dsym.zip
|
mv ghostty-macos-universal-dsym.zip blob/${GHOSTTY_VERSION}/ghostty-macos-universal-dsym.zip
|
||||||
mv Ghostty.dmg blob/${GHOSTTY_VERSION}/Ghostty.dmg
|
mv Ghostty.dmg blob/${GHOSTTY_VERSION}/Ghostty.dmg
|
||||||
|
mv appcast.xml blob/${GHOSTTY_VERSION}/appcast-staged.xml
|
||||||
- name: Upload to R2
|
- name: Upload to R2
|
||||||
uses: ryand56/r2-upload-action@latest
|
uses: ryand56/r2-upload-action@latest
|
||||||
with:
|
with:
|
||||||
@@ -376,18 +378,3 @@ jobs:
|
|||||||
r2-bucket: ghostty-release
|
r2-bucket: ghostty-release
|
||||||
source-dir: blob
|
source-dir: blob
|
||||||
destination-dir: ./
|
destination-dir: ./
|
||||||
|
|
||||||
- name: Prep Appcast
|
|
||||||
run: |
|
|
||||||
rm -rf blob
|
|
||||||
mkdir blob
|
|
||||||
mv appcast.xml blob/appcast.xml
|
|
||||||
- name: Upload Appcast to R2
|
|
||||||
uses: ryand56/r2-upload-action@latest
|
|
||||||
with:
|
|
||||||
r2-account-id: ${{ secrets.CF_R2_RELEASE_ACCOUNT_ID }}
|
|
||||||
r2-access-key-id: ${{ secrets.CF_R2_RELEASE_AWS_KEY }}
|
|
||||||
r2-secret-access-key: ${{ secrets.CF_R2_RELEASE_SECRET_KEY }}
|
|
||||||
r2-bucket: ghostty-release
|
|
||||||
source-dir: blob
|
|
||||||
destination-dir: ./
|
|
||||||
|
2
.github/workflows/release-tip.yml
vendored
@@ -164,7 +164,7 @@ jobs:
|
|||||||
# Setup Sparkle
|
# Setup Sparkle
|
||||||
- name: Setup Sparkle
|
- name: Setup Sparkle
|
||||||
env:
|
env:
|
||||||
SPARKLE_VERSION: 2.6.3
|
SPARKLE_VERSION: 2.6.4
|
||||||
run: |
|
run: |
|
||||||
mkdir -p .action/sparkle
|
mkdir -p .action/sparkle
|
||||||
cd .action/sparkle
|
cd .action/sparkle
|
||||||
|
175
.github/workflows/test.yml
vendored
@@ -6,6 +6,45 @@ on:
|
|||||||
name: Test
|
name: Test
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
required:
|
||||||
|
name: "Required Checks: Test"
|
||||||
|
runs-on: namespace-profile-ghostty-sm
|
||||||
|
needs:
|
||||||
|
- build
|
||||||
|
- build-bench
|
||||||
|
- build-linux-libghostty
|
||||||
|
- build-nix
|
||||||
|
- build-macos
|
||||||
|
- build-macos-matrix
|
||||||
|
- build-windows
|
||||||
|
- test
|
||||||
|
- test-gtk
|
||||||
|
- test-sentry-linux
|
||||||
|
- test-macos
|
||||||
|
- prettier
|
||||||
|
- alejandra
|
||||||
|
- typos
|
||||||
|
- test-pkg-linux
|
||||||
|
steps:
|
||||||
|
- id: status
|
||||||
|
name: Determine status
|
||||||
|
run: |
|
||||||
|
results=$(tr -d '\n' <<< '${{ toJSON(needs.*.result) }}')
|
||||||
|
if ! grep -q -v -E '(failure|cancelled)' <<< "$results"; then
|
||||||
|
result="failed"
|
||||||
|
else
|
||||||
|
result="success"
|
||||||
|
fi
|
||||||
|
{
|
||||||
|
echo "result=${result}"
|
||||||
|
echo "results=${results}"
|
||||||
|
} | tee -a "$GITHUB_OUTPUT"
|
||||||
|
- if: always() && steps.status.outputs.result != 'success'
|
||||||
|
name: Check for failed status
|
||||||
|
run: |
|
||||||
|
echo "One or more required build workflows failed: ${{ steps.status.outputs.results }}"
|
||||||
|
exit 1
|
||||||
|
|
||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -163,10 +202,14 @@ jobs:
|
|||||||
- name: XCode Select
|
- name: XCode Select
|
||||||
run: sudo xcode-select -s /Applications/Xcode_16.0.app
|
run: sudo xcode-select -s /Applications/Xcode_16.0.app
|
||||||
|
|
||||||
|
- name: get the Zig deps
|
||||||
|
id: deps
|
||||||
|
run: nix build -L .#deps && echo "deps=$(readlink ./result)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
# GhosttyKit is the framework that is built from Zig for our native
|
# GhosttyKit is the framework that is built from Zig for our native
|
||||||
# Mac app to access.
|
# Mac app to access.
|
||||||
- name: Build GhosttyKit
|
- name: Build GhosttyKit
|
||||||
run: nix develop -c zig build
|
run: nix develop -c zig build --system ${{ steps.deps.outputs.deps }}
|
||||||
|
|
||||||
# The native app is built with native XCode tooling. This also does
|
# The native app is built with native XCode tooling. This also does
|
||||||
# codesigning. IMPORTANT: this must NOT run in a Nix environment.
|
# codesigning. IMPORTANT: this must NOT run in a Nix environment.
|
||||||
@@ -199,35 +242,39 @@ jobs:
|
|||||||
- name: XCode Select
|
- name: XCode Select
|
||||||
run: sudo xcode-select -s /Applications/Xcode_16.0.app
|
run: sudo xcode-select -s /Applications/Xcode_16.0.app
|
||||||
|
|
||||||
|
- name: get the Zig deps
|
||||||
|
id: deps
|
||||||
|
run: nix build -L .#deps && echo "deps=$(readlink ./result)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Test All
|
- name: Test All
|
||||||
run: |
|
run: |
|
||||||
# OpenGL
|
# OpenGL
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape
|
||||||
|
|
||||||
# Metal
|
# Metal
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz
|
||||||
nix develop -c zig build test -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape
|
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape
|
||||||
|
|
||||||
- name: Build All
|
- name: Build All
|
||||||
run: |
|
run: |
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=freetype
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_freetype
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_harfbuzz
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape
|
||||||
|
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=freetype
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_freetype
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_harfbuzz
|
||||||
nix develop -c zig build -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape
|
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape
|
||||||
|
|
||||||
build-windows:
|
build-windows:
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
@@ -247,10 +294,10 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
# Get the zig version from build.zig so that it only needs to be updated
|
# Get the zig version from build.zig so that it only needs to be updated
|
||||||
$fileContent = Get-Content -Path "build.zig" -Raw
|
$fileContent = Get-Content -Path "build.zig" -Raw
|
||||||
$pattern = 'const required_zig = "(.*?)";'
|
$pattern = 'buildpkg\.requireZig\("(.*?)"\);'
|
||||||
$zigVersion = [regex]::Match($fileContent, $pattern).Groups[1].Value
|
$zigVersion = [regex]::Match($fileContent, $pattern).Groups[1].Value
|
||||||
Write-Output $version
|
|
||||||
$version = "zig-windows-x86_64-$zigVersion"
|
$version = "zig-windows-x86_64-$zigVersion"
|
||||||
|
Write-Output $version
|
||||||
$uri = "https://ziglang.org/download/$zigVersion/$version.zip"
|
$uri = "https://ziglang.org/download/$zigVersion/$version.zip"
|
||||||
Invoke-WebRequest -Uri "$uri" -OutFile ".\zig-windows.zip"
|
Invoke-WebRequest -Uri "$uri" -OutFile ".\zig-windows.zip"
|
||||||
Expand-Archive -Path ".\zig-windows.zip" -DestinationPath ".\" -Force
|
Expand-Archive -Path ".\zig-windows.zip" -DestinationPath ".\" -Force
|
||||||
@@ -342,7 +389,8 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
adwaita: ["true", "false"]
|
adwaita: ["true", "false"]
|
||||||
x11: ["true", "false"]
|
x11: ["true", "false"]
|
||||||
name: GTK adwaita=${{ matrix.adwaita }} x11=${{ matrix.x11 }}
|
wayland: ["true", "false"]
|
||||||
|
name: GTK adwaita=${{ matrix.adwaita }} x11=${{ matrix.x11 }} wayland=${{ matrix.wayland }}
|
||||||
runs-on: namespace-profile-ghostty-sm
|
runs-on: namespace-profile-ghostty-sm
|
||||||
needs: test
|
needs: test
|
||||||
env:
|
env:
|
||||||
@@ -374,7 +422,43 @@ jobs:
|
|||||||
zig build \
|
zig build \
|
||||||
-Dapp-runtime=gtk \
|
-Dapp-runtime=gtk \
|
||||||
-Dgtk-adwaita=${{ matrix.adwaita }} \
|
-Dgtk-adwaita=${{ matrix.adwaita }} \
|
||||||
-Dgtk-x11=${{ matrix.x11 }}
|
-Dgtk-x11=${{ matrix.x11 }} \
|
||||||
|
-Dgtk-wayland=${{ matrix.wayland }}
|
||||||
|
|
||||||
|
test-sentry-linux:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
sentry: ["true", "false"]
|
||||||
|
name: Build -Dsentry=${{ matrix.sentry }}
|
||||||
|
runs-on: namespace-profile-ghostty-sm
|
||||||
|
needs: test
|
||||||
|
env:
|
||||||
|
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||||
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Cache
|
||||||
|
uses: namespacelabs/nscloud-cache-action@v1.2.0
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
/nix
|
||||||
|
/zig
|
||||||
|
|
||||||
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
|
- uses: cachix/install-nix-action@v30
|
||||||
|
with:
|
||||||
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
|
- uses: cachix/cachix-action@v15
|
||||||
|
with:
|
||||||
|
name: ghostty
|
||||||
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
|
|
||||||
|
- name: Test Sentry Build
|
||||||
|
run: |
|
||||||
|
nix develop -c zig build -Dsentry=${{ matrix.sentry }}
|
||||||
|
|
||||||
test-macos:
|
test-macos:
|
||||||
runs-on: namespace-profile-ghostty-macos
|
runs-on: namespace-profile-ghostty-macos
|
||||||
@@ -395,8 +479,12 @@ jobs:
|
|||||||
- name: XCode Select
|
- name: XCode Select
|
||||||
run: sudo xcode-select -s /Applications/Xcode_16.0.app
|
run: sudo xcode-select -s /Applications/Xcode_16.0.app
|
||||||
|
|
||||||
|
- name: get the Zig deps
|
||||||
|
id: deps
|
||||||
|
run: nix build -L .#deps && echo "deps=$(readlink ./result)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: test
|
- name: test
|
||||||
run: nix develop -c zig build test
|
run: nix develop -c zig build test --system ${{ steps.deps.outputs.deps }}
|
||||||
|
|
||||||
prettier:
|
prettier:
|
||||||
if: github.repository == 'ghostty-org/ghostty'
|
if: github.repository == 'ghostty-org/ghostty'
|
||||||
@@ -478,3 +566,38 @@ jobs:
|
|||||||
useDaemon: false # sometimes fails on short jobs
|
useDaemon: false # sometimes fails on short jobs
|
||||||
- name: typos check
|
- name: typos check
|
||||||
run: nix develop -c typos
|
run: nix develop -c typos
|
||||||
|
|
||||||
|
test-pkg-linux:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
pkg: ["wuffs"]
|
||||||
|
name: Test pkg/${{ matrix.pkg }}
|
||||||
|
runs-on: namespace-profile-ghostty-sm
|
||||||
|
needs: test
|
||||||
|
env:
|
||||||
|
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||||
|
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Cache
|
||||||
|
uses: namespacelabs/nscloud-cache-action@v1.2.0
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
/nix
|
||||||
|
/zig
|
||||||
|
|
||||||
|
# Install Nix and use that to run our tests so our environment matches exactly.
|
||||||
|
- uses: cachix/install-nix-action@v30
|
||||||
|
with:
|
||||||
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
|
- uses: cachix/cachix-action@v15
|
||||||
|
with:
|
||||||
|
name: ghostty
|
||||||
|
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||||
|
|
||||||
|
- name: Test ${{ matrix.pkg }} Build
|
||||||
|
run: |
|
||||||
|
nix develop -c sh -c "cd pkg/${{ matrix.pkg }} ; zig build test"
|
||||||
|
8
.github/workflows/update-colorschemes.yml
vendored
@@ -48,14 +48,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
# Only proceed if build.zig.zon has changed
|
# Only proceed if build.zig.zon has changed
|
||||||
if ! git diff --exit-code build.zig.zon; then
|
if ! git diff --exit-code build.zig.zon; then
|
||||||
nix develop -c ./nix/build-support/check-zig-cache-hash.sh --update
|
nix develop -c ./nix/build-support/check-zig-cache.sh --update
|
||||||
nix develop -c ./nix/build-support/check-zig-cache-hash.sh
|
nix develop -c ./nix/build-support/check-zig-cache.sh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Verify the build still works. We choose an arbitrary build type
|
# Verify the build still works. We choose an arbitrary build type
|
||||||
# as a canary instead of testing all build types.
|
# as a canary instead of testing all build types.
|
||||||
- name: Test Build
|
- name: Test Build
|
||||||
run: nix develop -c zig build -Dapp-runtime=gtk -Dgtk-adwaita=true
|
run: nix build .#ghostty
|
||||||
|
|
||||||
- name: Create pull request
|
- name: Create pull request
|
||||||
uses: peter-evans/create-pull-request@v7
|
uses: peter-evans/create-pull-request@v7
|
||||||
@@ -66,7 +66,7 @@ jobs:
|
|||||||
commit-message: "deps: Update iTerm2 color schemes"
|
commit-message: "deps: Update iTerm2 color schemes"
|
||||||
add-paths: |
|
add-paths: |
|
||||||
build.zig.zon
|
build.zig.zon
|
||||||
nix/zigCacheHash.nix
|
build.zig.zon.nix
|
||||||
body: |
|
body: |
|
||||||
Upstream revision: https://github.com/mbadolato/iTerm2-Color-Schemes/tree/${{ steps.zig_fetch.outputs.upstream_rev }}
|
Upstream revision: https://github.com/mbadolato/iTerm2-Color-Schemes/tree/${{ steps.zig_fetch.outputs.upstream_rev }}
|
||||||
labels: dependencies
|
labels: dependencies
|
||||||
|
2
.gitignore
vendored
@@ -17,3 +17,5 @@ test/cases/**/*.actual.png
|
|||||||
glad.zip
|
glad.zip
|
||||||
/Box_test.ppm
|
/Box_test.ppm
|
||||||
/Box_test_diff.ppm
|
/Box_test_diff.ppm
|
||||||
|
/ghostty.qcow2
|
||||||
|
/build.zig.zon2json-lock
|
||||||
|
149
CODEOWNERS
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
# This file documents the subsystem maintainers of the Ghostty project
|
||||||
|
# along with the responsibilities of a maintainer and how one can become
|
||||||
|
# a maintainer.
|
||||||
|
#
|
||||||
|
# Ghostty follows a subsystem maintainer model where distinguished
|
||||||
|
# contributors (with mutual agreement) are designated as maintainers of a
|
||||||
|
# specific subset of the project. A subsystem maintainer has more privileges
|
||||||
|
# and authority over a specific part of the project than a regular
|
||||||
|
# contributor and deference is given to them when making decisions about
|
||||||
|
# their subsystem.
|
||||||
|
#
|
||||||
|
# Ultimately Ghostty has a BDFL (Benevolent Dictator For Life) model
|
||||||
|
# currently with @mitchellh as the BDFL. The BDFL has the final say in all
|
||||||
|
# decisions and may override a maintainer's decision if necessary. I like to
|
||||||
|
# say its a BDFLFN (Benevolent Dictator For Life "For Now") model because
|
||||||
|
# long term I'd like to see the project be more community driven. But for
|
||||||
|
# now, early in its life, we're going with this model.
|
||||||
|
#
|
||||||
|
# ## Privileges
|
||||||
|
#
|
||||||
|
# - Authority to approve or reject pull requests in their subsystem.
|
||||||
|
# - Authority to moderate issues and discussions in their subsystem.
|
||||||
|
# - Authority to make roadmap and design decisions about their subsystem
|
||||||
|
# with input only from other subsystem maintainers.
|
||||||
|
#
|
||||||
|
# In all scenarios, the BDFL doesn't need to be consulted for decisions
|
||||||
|
# but may revert or override decisions if necessary. The expectation is
|
||||||
|
# that maintainers will be trusted to make the right decisions for their
|
||||||
|
# subsystem and this will be rare.
|
||||||
|
#
|
||||||
|
# ## Responsibilities
|
||||||
|
#
|
||||||
|
# Subsystem maintainership is a voluntary role and maintainers are not
|
||||||
|
# expected to dedicate any amount of time to the project. However, if a
|
||||||
|
# maintainer is inactive for a long period of time, they may be removed from
|
||||||
|
# the maintainers list to avoid bitrot or outdated information.
|
||||||
|
#
|
||||||
|
# Maintainers are expected to be exemplary members of the community and
|
||||||
|
# should be respectful, helpful, and professional in all interactions.
|
||||||
|
# This is both in regards to the community at large as well as other
|
||||||
|
# subsystem maintainers as well as @mitchellh.
|
||||||
|
#
|
||||||
|
# As technical leaders, maintainers are expected to be mindful about
|
||||||
|
# breaking changes, performance, user impact, and other technical
|
||||||
|
# considerations in their subsystem. They should be considerate of large
|
||||||
|
# changes and should be able to justify their decisions.
|
||||||
|
#
|
||||||
|
# Notably, maintainers have NO OBLIGATION to review pull requests or issues
|
||||||
|
# in their subsystem. They have full discretion to review or not review
|
||||||
|
# anything they want. This isn't a job! It is a role of trust and authority
|
||||||
|
# and the expectation is that maintainers will use their best judgement.
|
||||||
|
#
|
||||||
|
# ## Becoming a Maintainer
|
||||||
|
#
|
||||||
|
# Maintainer candidates are noticed and proposed by the community. Anyone
|
||||||
|
# may propose themselves or someone else as a maintainer. The BDFL along
|
||||||
|
# with existing maintainers will discuss and decide.
|
||||||
|
#
|
||||||
|
# Generally, we want to see consistent high quality contributions to a
|
||||||
|
# specific subsystem before considering someone as a maintainer. There isn't
|
||||||
|
# an exact number of contributions or time period required but generally
|
||||||
|
# we're looking for an order of a dozen or more contributions over a period of
|
||||||
|
# months, at least.
|
||||||
|
#
|
||||||
|
# # Subsystem List
|
||||||
|
#
|
||||||
|
# The subsystems don't fully cover the entirety of the Ghostty project but
|
||||||
|
# are created organically as experts in certain areas emerge. If you feel
|
||||||
|
# you are an expert in a certain area and would like to be a maintainer,
|
||||||
|
# please reach out to @mitchellh on Discord.
|
||||||
|
#
|
||||||
|
# (Alphabetical order)
|
||||||
|
#
|
||||||
|
# - @ghostty-org/font - All things font related including discovery,
|
||||||
|
# rasterization, shaping, coloring, etc.
|
||||||
|
#
|
||||||
|
# - @ghostty-org/gtk - Anything GTK-related in the project, primarily
|
||||||
|
# the GTK apprt. Also includes X11/Wayland integrations and general
|
||||||
|
# Linux support.
|
||||||
|
#
|
||||||
|
# - @ghostty-org/macos - The Ghostty macOS app and any macOS-specific
|
||||||
|
# features, configurations, etc.
|
||||||
|
#
|
||||||
|
# - @ghostty-org/renderer - Ghostty rendering subsystem, including the
|
||||||
|
# rendering abstractions as well as specific renderers like OpenGL
|
||||||
|
# and Metal.
|
||||||
|
#
|
||||||
|
# - @ghostty-org/shell - Ghostty shell integration, including shell
|
||||||
|
# completions, shell detection, and any other shell interactions.
|
||||||
|
#
|
||||||
|
# - @ghostty-org/terminal - The terminal emulator subsystem, including
|
||||||
|
# subprocess management and pty handling, escape sequence parsing,
|
||||||
|
# key encoding, etc.
|
||||||
|
#
|
||||||
|
# ## Outside of Ghostty
|
||||||
|
#
|
||||||
|
# Other "subsystems" exist outside of Ghostty and will not be represented
|
||||||
|
# in this CODEOWNERS file:
|
||||||
|
#
|
||||||
|
# - @ghostty-org/discord-bot - Maintainers of the Ghostty Discord bot.
|
||||||
|
#
|
||||||
|
# - @ghostty-org/website - Maintainers of the Ghostty website.
|
||||||
|
|
||||||
|
# Font
|
||||||
|
/src/font/ @ghostty-org/font
|
||||||
|
/pkg/fontconfig/ @ghostty-org/font
|
||||||
|
/pkg/freetype/ @ghostty-org/font
|
||||||
|
/pkg/harfbuzz/ @ghostty-org/font
|
||||||
|
|
||||||
|
# GTK
|
||||||
|
/src/apprt/gtk/ @ghostty-org/gtk
|
||||||
|
/src/os/cgroup.zig @ghostty-org/gtk
|
||||||
|
/src/os/flatpak.zig @ghostty-org/gtk
|
||||||
|
/dist/linux/ @ghostty-org/gtk
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
#
|
||||||
|
# This includes libghostty because the macOS apprt is built on top of
|
||||||
|
# libghostty and often requires or is impacted by changes to libghostty.
|
||||||
|
# macOS subsystem maintainers are expected to only work on libghostty
|
||||||
|
# insofar as it impacts the macOS apprt.
|
||||||
|
/include/ghostty.h @ghostty-org/macos
|
||||||
|
/src/apprt/embedded.zig @ghostty-org/macos
|
||||||
|
/src/os/cf_release_thread.zig @ghostty-org/macos
|
||||||
|
/src/os/macos.zig @ghostty-org/macos
|
||||||
|
/macos/ @ghostty-org/macos
|
||||||
|
/dist/macos/ @ghostty-org/macos
|
||||||
|
/pkg/apple-sdk/ @ghostty-org/macos
|
||||||
|
/pkg/macos/ @ghostty-org/macos
|
||||||
|
|
||||||
|
# Renderer
|
||||||
|
/src/renderer.zig @ghostty-org/renderer
|
||||||
|
/src/renderer/ @ghostty-org/renderer
|
||||||
|
/pkg/glslang/ @ghostty-org/renderer
|
||||||
|
/pkg/opengl/ @ghostty-org/renderer
|
||||||
|
/pkg/spirv-cross/ @ghostty-org/renderer
|
||||||
|
/pkg/wuffs/ @ghostty-org/renderer
|
||||||
|
|
||||||
|
# Shell
|
||||||
|
/src/shell-integration/ @ghostty-org/shell
|
||||||
|
/src/termio/shell-integration.zig @ghostty-org/shell
|
||||||
|
|
||||||
|
# Terminal
|
||||||
|
/src/simd/ @ghostty-org/terminal
|
||||||
|
/src/terminal/ @ghostty-org/terminal
|
||||||
|
/src/terminfo/ @ghostty-org/terminal
|
||||||
|
/src/unicode/ @ghostty-org/terminal
|
||||||
|
/src/Surface.zig @ghostty-org/terminal
|
||||||
|
/src/surface_mouse.zig @ghostty-org/terminal
|
180
CONTRIBUTING.md
@@ -77,3 +77,183 @@ pull request will be accepted with a high degree of certainty.
|
|||||||
> **Pull requests are NOT a place to discuss feature design.** Please do
|
> **Pull requests are NOT a place to discuss feature design.** Please do
|
||||||
> not open a WIP pull request to discuss a feature. Instead, use a discussion
|
> not open a WIP pull request to discuss a feature. Instead, use a discussion
|
||||||
> and link to your branch.
|
> and link to your branch.
|
||||||
|
|
||||||
|
# Developer Guide
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> **The remainder of this file is dedicated to developers actively
|
||||||
|
> working on Ghostty.** If you're a user reporting an issue, you can
|
||||||
|
> ignore the rest of this document.
|
||||||
|
|
||||||
|
## Input Stack Testing
|
||||||
|
|
||||||
|
The input stack is the part of the codebase that starts with a
|
||||||
|
key event and ends with text encoding being sent to the pty (it
|
||||||
|
does not include _rendering_ the text, which is part of the
|
||||||
|
font or rendering stack).
|
||||||
|
|
||||||
|
If you modify any part of the input stack, you must manually verify
|
||||||
|
all the following input cases work properly. We unfortunately do
|
||||||
|
not automate this in any way, but if we can do that one day that'd
|
||||||
|
save a LOT of grief and time.
|
||||||
|
|
||||||
|
Note: this list may not be exhaustive, I'm still working on it.
|
||||||
|
|
||||||
|
### Linux IME
|
||||||
|
|
||||||
|
IME (Input Method Editors) are a common source of bugs in the input stack,
|
||||||
|
especially on Linux since there are multiple different IME systems
|
||||||
|
interacting with different windowing systems and application frameworks
|
||||||
|
all written by different organizations.
|
||||||
|
|
||||||
|
The following matrix should be tested to ensure that all IME input works
|
||||||
|
properly:
|
||||||
|
|
||||||
|
1. Wayland, X11
|
||||||
|
2. ibus, fcitx, none
|
||||||
|
3. Dead key input (e.g. Spanish), CJK (e.g. Japanese), Emoji, Unicode Hex
|
||||||
|
4. ibus versions: 1.5.29, 1.5.30, 1.5.31 (each exhibit slightly different behaviors)
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> This is a **work in progress**. I'm still working on this list and it
|
||||||
|
> is not complete. As I find more test cases, I will add them here.
|
||||||
|
|
||||||
|
#### Dead Key Input
|
||||||
|
|
||||||
|
Set your keyboard layout to "Spanish" (or another layout that uses dead keys).
|
||||||
|
|
||||||
|
1. Launch Ghostty
|
||||||
|
2. Press `'`
|
||||||
|
3. Press `a`
|
||||||
|
4. Verify that `á` is displayed
|
||||||
|
|
||||||
|
Note that the dead key may or may not show a preedit state visually.
|
||||||
|
For ibus and fcitx it does but for the "none" case it does not. Importantly,
|
||||||
|
the text should be correct when it is sent to the pty.
|
||||||
|
|
||||||
|
We should also test canceling dead key input:
|
||||||
|
|
||||||
|
1. Launch Ghostty
|
||||||
|
2. Press `'`
|
||||||
|
3. Press escape
|
||||||
|
4. Press `a`
|
||||||
|
5. Verify that `a` is displayed (no diacritic)
|
||||||
|
|
||||||
|
#### CJK Input
|
||||||
|
|
||||||
|
Configure fcitx or ibus with a keyboard layout like Japanese or Mozc. The
|
||||||
|
exact layout doesn't matter.
|
||||||
|
|
||||||
|
1. Launch Ghostty
|
||||||
|
2. Press `Ctrl+Shift` to switch to "Hiragana"
|
||||||
|
3. On a US physical layout, type: `konn`, you should see `こん` in preedit.
|
||||||
|
4. Press `Enter`
|
||||||
|
5. Verify that `こん` is displayed in the terminal.
|
||||||
|
|
||||||
|
We should also test switching input methods while preedit is active, which
|
||||||
|
should commit the text:
|
||||||
|
|
||||||
|
1. Launch Ghostty
|
||||||
|
2. Press `Ctrl+Shift` to switch to "Hiragana"
|
||||||
|
3. On a US physical layout, type: `konn`, you should see `こん` in preedit.
|
||||||
|
4. Press `Ctrl+Shift` to switch to another layout (any)
|
||||||
|
5. Verify that `こん` is displayed in the terminal as committed text.
|
||||||
|
|
||||||
|
## Nix Virtual Machines
|
||||||
|
|
||||||
|
Several Nix virtual machine definitions are provided by the project for testing
|
||||||
|
and developing Ghostty against multiple different Linux desktop environments.
|
||||||
|
|
||||||
|
Running these requires a working Nix installation, either Nix on your
|
||||||
|
favorite Linux distribution, NixOS, or macOS with nix-darwin installed. Further
|
||||||
|
requirements for macOS are detailed below.
|
||||||
|
|
||||||
|
VMs should only be run on your local desktop and then powered off when not in
|
||||||
|
use, which will discard any changes to the VM.
|
||||||
|
|
||||||
|
The VM definitions provide minimal software "out of the box" but additional
|
||||||
|
software can be installed by using standard Nix mechanisms like `nix run nixpkgs#<package>`.
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
1. Check out the Ghostty source and change to the directory.
|
||||||
|
2. Run `nix run .#<vmtype>`. `<vmtype>` can be any of the VMs defined in the
|
||||||
|
`nix/vm` directory (without the `.nix` suffix) excluding any file prefixed
|
||||||
|
with `common` or `create`.
|
||||||
|
3. The VM will build and then launch. Depending on the speed of your system, this
|
||||||
|
can take a while, but eventually you should get a new VM window.
|
||||||
|
4. The Ghostty source directory should be mounted to `/tmp/shared` in the VM. Depending
|
||||||
|
on what UID and GID of the user that you launched the VM as, `/tmp/shared` _may_ be
|
||||||
|
writable by the VM user, so be careful!
|
||||||
|
|
||||||
|
### macOS
|
||||||
|
|
||||||
|
1. To run the VMs on macOS you will need to enable the Linux builder in your `nix-darwin`
|
||||||
|
config. This _should_ be as simple as adding `nix.linux-builder.enable=true` to your
|
||||||
|
configuration and then rebuilding. See [this](https://nixcademy.com/posts/macos-linux-builder/)
|
||||||
|
blog post for more information about the Linux builder and how to tune the performance.
|
||||||
|
2. Once the Linux builder has been enabled, you should be able to follow the Linux instructions
|
||||||
|
above to launch a VM.
|
||||||
|
|
||||||
|
### Custom VMs
|
||||||
|
|
||||||
|
To easily create a custom VM without modifying the Ghostty source, create a new
|
||||||
|
directory, then create a file called `flake.nix` with the following text in the
|
||||||
|
new directory.
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "nixpkgs/nixpkgs-unstable";
|
||||||
|
ghostty.url = "github:ghostty-org/ghostty";
|
||||||
|
};
|
||||||
|
outputs = {
|
||||||
|
nixpkgs,
|
||||||
|
ghostty,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
nixosConfigurations.custom-vm = ghostty.create-gnome-vm {
|
||||||
|
nixpkgs = nixpkgs;
|
||||||
|
system = "x86_64-linux";
|
||||||
|
overlay = ghostty.overlays.releasefast;
|
||||||
|
# module = ./configuration.nix # also works
|
||||||
|
module = {pkgs, ...}: {
|
||||||
|
environment.systemPackages = [
|
||||||
|
pkgs.btop
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The custom VM can then be run with a command like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
nix run .#nixosConfigurations.custom-vm.config.system.build.vm
|
||||||
|
```
|
||||||
|
|
||||||
|
A file named `ghostty.qcow2` will be created that is used to persist any changes
|
||||||
|
made in the VM. To "reset" the VM to default delete the file and it will be
|
||||||
|
recreated the next time you run the VM.
|
||||||
|
|
||||||
|
### Contributing new VM definitions
|
||||||
|
|
||||||
|
#### VM Acceptance Criteria
|
||||||
|
|
||||||
|
We welcome the contribution of new VM definitions, as long as they meet the following criteria:
|
||||||
|
|
||||||
|
1. The should be different enough from existing VM definitions that they represent a distinct
|
||||||
|
user (and developer) experience.
|
||||||
|
2. There's a significant Ghostty user population that uses a similar environment.
|
||||||
|
3. The VMs can be built using only packages from the current stable NixOS release.
|
||||||
|
|
||||||
|
#### VM Definition Criteria
|
||||||
|
|
||||||
|
1. VMs should be as minimal as possible so that they build and launch quickly.
|
||||||
|
Additional software can be added at runtime with a command like `nix run nixpkgs#<package name>`.
|
||||||
|
2. VMs should not expose any services to the network, or run any remote access
|
||||||
|
software like SSH daemons, VNC or RDP.
|
||||||
|
3. VMs should auto-login using the "ghostty" user.
|
||||||
|
@@ -117,3 +117,11 @@ relevant to package maintainers:
|
|||||||
often necessary for system packages to specify a specific minimum Linux
|
often necessary for system packages to specify a specific minimum Linux
|
||||||
version, glibc, etc. Run `zig targets` to a get a full list of available
|
version, glibc, etc. Run `zig targets` to a get a full list of available
|
||||||
targets.
|
targets.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
>
|
||||||
|
> **The GLFW runtime is not meant for distribution.** The GLFW runtime
|
||||||
|
> (`-Dapp-runtime=glfw`) is meant for development and testing only. It is
|
||||||
|
> missing many features, has known memory leak scenarios, known crashes,
|
||||||
|
> and more. Please do not package the GLFW-based Ghostty runtime for
|
||||||
|
> distribution.
|
||||||
|
@@ -1,18 +1,26 @@
|
|||||||
.{
|
.{
|
||||||
.name = "ghostty",
|
.name = "ghostty",
|
||||||
.version = "1.0.1",
|
.version = "1.1.2",
|
||||||
.paths = .{""},
|
.paths = .{""},
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
// Zig libs
|
// Zig libs
|
||||||
.libxev = .{
|
.libxev = .{
|
||||||
.url = "https://github.com/mitchellh/libxev/archive/db6a52bafadf00360e675fefa7926e8e6c0e9931.tar.gz",
|
.url = "https://github.com/mitchellh/libxev/archive/31eed4e337fed7b0149319e5cdbb62b848c24fbd.tar.gz",
|
||||||
.hash = "12206029de146b685739f69b10a6f08baee86b3d0a5f9a659fa2b2b66c9602078bbf",
|
.hash = "1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c",
|
||||||
},
|
},
|
||||||
.mach_glfw = .{
|
.mach_glfw = .{
|
||||||
.url = "https://github.com/mitchellh/mach-glfw/archive/37c2995f31abcf7e8378fba68ddcf4a3faa02de0.tar.gz",
|
.url = "https://github.com/mitchellh/mach-glfw/archive/37c2995f31abcf7e8378fba68ddcf4a3faa02de0.tar.gz",
|
||||||
.hash = "12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62",
|
.hash = "12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62",
|
||||||
.lazy = true,
|
.lazy = true,
|
||||||
},
|
},
|
||||||
|
.vaxis = .{
|
||||||
|
.url = "git+https://github.com/rockorager/libvaxis/?ref=main#6d729a2dc3b934818dffe06d2ba3ce02841ed74b",
|
||||||
|
.hash = "12200df4ebeaed45de26cb2c9f3b6f3746d8013b604e035dae658f86f586c8c91d2f",
|
||||||
|
},
|
||||||
|
.z2d = .{
|
||||||
|
.url = "git+https://github.com/vancluever/z2d?ref=v0.4.0#4638bb02a9dc41cc2fb811f092811f6a951c752a",
|
||||||
|
.hash = "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a",
|
||||||
|
},
|
||||||
.zig_objc = .{
|
.zig_objc = .{
|
||||||
.url = "https://github.com/mitchellh/zig-objc/archive/9b8ba849b0f58fe207ecd6ab7c147af55b17556e.tar.gz",
|
.url = "https://github.com/mitchellh/zig-objc/archive/9b8ba849b0f58fe207ecd6ab7c147af55b17556e.tar.gz",
|
||||||
.hash = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634",
|
.hash = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634",
|
||||||
@@ -25,6 +33,18 @@
|
|||||||
.url = "https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz",
|
.url = "https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz",
|
||||||
.hash = "12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25",
|
.hash = "12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25",
|
||||||
},
|
},
|
||||||
|
.zig_wayland = .{
|
||||||
|
.url = "https://deps.files.ghostty.org/zig-wayland-fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz",
|
||||||
|
.hash = "12209ca054cb1919fa276e328967f10b253f7537c4136eb48f3332b0f7cf661cad38",
|
||||||
|
},
|
||||||
|
.zf = .{
|
||||||
|
.url = "git+https://github.com/natecraddock/zf/?ref=main#ed99ca18b02dda052e20ba467e90b623c04690dd",
|
||||||
|
.hash = "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8",
|
||||||
|
},
|
||||||
|
.gobject = .{
|
||||||
|
.url = "https://github.com/ianprime0509/zig-gobject/releases/download/v0.2.2/bindings-gnome47.tar.zst",
|
||||||
|
.hash = "12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d",
|
||||||
|
},
|
||||||
|
|
||||||
// C libs
|
// C libs
|
||||||
.cimgui = .{ .path = "./pkg/cimgui" },
|
.cimgui = .{ .path = "./pkg/cimgui" },
|
||||||
@@ -46,23 +66,25 @@
|
|||||||
.glslang = .{ .path = "./pkg/glslang" },
|
.glslang = .{ .path = "./pkg/glslang" },
|
||||||
.spirv_cross = .{ .path = "./pkg/spirv-cross" },
|
.spirv_cross = .{ .path = "./pkg/spirv-cross" },
|
||||||
|
|
||||||
|
// Wayland
|
||||||
|
.wayland = .{
|
||||||
|
.url = "https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz",
|
||||||
|
.hash = "12202cdac858abc52413a6c6711d5026d2d3c8e13f95ca2c327eade0736298bb021f",
|
||||||
|
},
|
||||||
|
.wayland_protocols = .{
|
||||||
|
.url = "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz",
|
||||||
|
.hash = "12201a57c6ce0001aa034fa80fba3e1cd2253c560a45748f4f4dd21ff23b491cddef",
|
||||||
|
},
|
||||||
|
.plasma_wayland_protocols = .{
|
||||||
|
.url = "git+https://github.com/KDE/plasma-wayland-protocols?ref=main#db525e8f9da548cffa2ac77618dd0fbe7f511b86",
|
||||||
|
.hash = "12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566",
|
||||||
|
},
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
|
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
|
||||||
.iterm2_themes = .{
|
.iterm2_themes = .{
|
||||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/e030599a6a6e19fcd1ea047c7714021170129d56.tar.gz",
|
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/db227d159adc265818f2e898da0f70ef8d7b580e.tar.gz",
|
||||||
.hash = "1220cc25b537556a42b0948437c791214c229efb78b551c80b1e9b18d70bf0498620",
|
.hash = "12203d2647e5daf36a9c85b969e03f422540786ce9ea624eb4c26d204fe1f46218f3",
|
||||||
},
|
|
||||||
.vaxis = .{
|
|
||||||
.url = "git+https://github.com/rockorager/libvaxis/?ref=main#6d729a2dc3b934818dffe06d2ba3ce02841ed74b",
|
|
||||||
.hash = "12200df4ebeaed45de26cb2c9f3b6f3746d8013b604e035dae658f86f586c8c91d2f",
|
|
||||||
},
|
|
||||||
.zf = .{
|
|
||||||
.url = "git+https://github.com/natecraddock/zf/?ref=main#ed99ca18b02dda052e20ba467e90b623c04690dd",
|
|
||||||
.hash = "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8",
|
|
||||||
},
|
|
||||||
.z2d = .{
|
|
||||||
.url = "git+https://github.com/vancluever/z2d?ref=v0.4.0#4638bb02a9dc41cc2fb811f092811f6a951c752a",
|
|
||||||
.hash = "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
390
build.zig.zon.nix
generated
Normal file
@@ -0,0 +1,390 @@
|
|||||||
|
# generated by zon2nix (https://github.com/Cloudef/zig2nix)
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
linkFarm,
|
||||||
|
fetchurl,
|
||||||
|
fetchgit,
|
||||||
|
runCommandLocal,
|
||||||
|
zig,
|
||||||
|
name ? "zig-packages",
|
||||||
|
}:
|
||||||
|
with builtins;
|
||||||
|
with lib; let
|
||||||
|
unpackZigArtifact = {
|
||||||
|
name,
|
||||||
|
artifact,
|
||||||
|
}:
|
||||||
|
runCommandLocal name
|
||||||
|
{
|
||||||
|
nativeBuildInputs = [zig];
|
||||||
|
}
|
||||||
|
''
|
||||||
|
hash="$(zig fetch --global-cache-dir "$TMPDIR" ${artifact})"
|
||||||
|
mv "$TMPDIR/p/$hash" "$out"
|
||||||
|
chmod 755 "$out"
|
||||||
|
'';
|
||||||
|
|
||||||
|
fetchZig = {
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
hash,
|
||||||
|
}: let
|
||||||
|
artifact = fetchurl {inherit url hash;};
|
||||||
|
in
|
||||||
|
unpackZigArtifact {inherit name artifact;};
|
||||||
|
|
||||||
|
fetchGitZig = {
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
hash,
|
||||||
|
}: let
|
||||||
|
parts = splitString "#" url;
|
||||||
|
url_base = elemAt parts 0;
|
||||||
|
url_without_query = elemAt (splitString "?" url_base) 0;
|
||||||
|
rev_base = elemAt parts 1;
|
||||||
|
rev =
|
||||||
|
if match "^[a-fA-F0-9]{40}$" rev_base != null
|
||||||
|
then rev_base
|
||||||
|
else "refs/heads/${rev_base}";
|
||||||
|
in
|
||||||
|
fetchgit {
|
||||||
|
inherit name rev hash;
|
||||||
|
url = url_without_query;
|
||||||
|
deepClone = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchZigArtifact = {
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
hash,
|
||||||
|
}: let
|
||||||
|
parts = splitString "://" url;
|
||||||
|
proto = elemAt parts 0;
|
||||||
|
path = elemAt parts 1;
|
||||||
|
fetcher = {
|
||||||
|
"git+http" = fetchGitZig {
|
||||||
|
inherit name hash;
|
||||||
|
url = "http://${path}";
|
||||||
|
};
|
||||||
|
"git+https" = fetchGitZig {
|
||||||
|
inherit name hash;
|
||||||
|
url = "https://${path}";
|
||||||
|
};
|
||||||
|
http = fetchZig {
|
||||||
|
inherit name hash;
|
||||||
|
url = "http://${path}";
|
||||||
|
};
|
||||||
|
https = fetchZig {
|
||||||
|
inherit name hash;
|
||||||
|
url = "https://${path}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
fetcher.${proto};
|
||||||
|
in
|
||||||
|
linkFarm name [
|
||||||
|
{
|
||||||
|
name = "1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "libxev";
|
||||||
|
url = "https://github.com/mitchellh/libxev/archive/31eed4e337fed7b0149319e5cdbb62b848c24fbd.tar.gz";
|
||||||
|
hash = "sha256-VHP90NTytIZ8UZsYRKOOxN490/I6yv6ec40sP8y5MJ8=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "mach_glfw";
|
||||||
|
url = "https://github.com/mitchellh/mach-glfw/archive/37c2995f31abcf7e8378fba68ddcf4a3faa02de0.tar.gz";
|
||||||
|
hash = "sha256-HhXIvWUS8/CHWY4VXPG2ZEo+we8XOn3o5rYJCQ1n8Nk=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220736fa4ba211162c7a0e46cc8fe04d95921927688bff64ab5da7420d098a7272d";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "glfw";
|
||||||
|
url = "https://github.com/mitchellh/glfw/archive/b552c6ec47326b94015feddb36058ea567b87159.tar.gz";
|
||||||
|
hash = "sha256-IeBVAOQmtyFqVxzuXPek1onuPwIamcOyYtxqKpPEQjU=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12202adbfecdad671d585c9a5bfcbd5cdf821726779430047742ce1bf94ad67d19cb";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "xcode_frameworks";
|
||||||
|
url = "https://github.com/mitchellh/xcode-frameworks/archive/69801c154c39d7ae6129ea1ba8fe1afe00585fc8.tar.gz";
|
||||||
|
hash = "sha256-mP/I2coL57UJm/3+4Q8sPAgQwk8V4zM+S4VBBTrX2To=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "122004bfd4c519dadfb8e6281a42fc34fd1aa15aea654ea8a492839046f9894fa2cf";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "vulkan_headers";
|
||||||
|
url = "https://github.com/mitchellh/vulkan-headers/archive/04c8a0389d5a0236a96312988017cd4ce27d8041.tar.gz";
|
||||||
|
hash = "sha256-K+zrRudgHFukOM6En1StRYRMNYkeRk+qHTXvrXaG+FU=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220b3164434d2ec9db146a40bf3a30f490590d68fa8529776a3138074f0da2c11ca";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "wayland_headers";
|
||||||
|
url = "https://github.com/mitchellh/wayland-headers/archive/5f991515a29f994d87b908115a2ab0b899474bd1.tar.gz";
|
||||||
|
hash = "sha256-uFilLZinKkZt6RdVTV3lUmJpzpswDdFva22FvwU/XQI=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "122089c326186c84aa2fd034b16abc38f3ebf4862d9ae106dc1847ac44f557b36465";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "x11_headers";
|
||||||
|
url = "https://github.com/mitchellh/x11-headers/archive/2ffbd62d82ff73ec929dd8de802bc95effa0ef88.tar.gz";
|
||||||
|
hash = "sha256-EhV2bmTY/OMYN1wEul35gD0hQgS/Al262jO3pVr0O+c=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12200df4ebeaed45de26cb2c9f3b6f3746d8013b604e035dae658f86f586c8c91d2f";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "vaxis";
|
||||||
|
url = "git+https://github.com/rockorager/libvaxis/?ref=main#6d729a2dc3b934818dffe06d2ba3ce02841ed74b";
|
||||||
|
hash = "sha256-fFf79fCy4QQFVNcN722tSMjB6FyVEzCB36oH1olk9JQ=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220dd654ef941fc76fd96f9ec6adadf83f69b9887a0d3f4ee5ac0a1a3e11be35cf5";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "zigimg";
|
||||||
|
url = "git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e";
|
||||||
|
hash = "sha256-oLf3YH3yeg4ikVO/GahMCDRMTU31AHkfSnF4rt7xTKo=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "122055beff332830a391e9895c044d33b15ea21063779557024b46169fb1984c6e40";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "zg";
|
||||||
|
url = "https://codeberg.org/atman/zg/archive/v0.13.2.tar.gz";
|
||||||
|
hash = "sha256-2x9hT7bYq9KJYWLVOf21a+QvTG/F7HWT+YK15IMRzNY=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "z2d";
|
||||||
|
url = "git+https://github.com/vancluever/z2d?ref=v0.4.0#4638bb02a9dc41cc2fb811f092811f6a951c752a";
|
||||||
|
hash = "sha256-YpWXn1J3JKQSCrWB25mAfzd1/T56QstEZnhPzBwxgoM=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "zig_objc";
|
||||||
|
url = "https://github.com/mitchellh/zig-objc/archive/9b8ba849b0f58fe207ecd6ab7c147af55b17556e.tar.gz";
|
||||||
|
hash = "sha256-H+HIbh2T23uzrsg9/1/vl9Ir1HCAa2pzeTx6zktJH9Q=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "zig_js";
|
||||||
|
url = "https://github.com/mitchellh/zig-js/archive/d0b8b0a57c52fbc89f9d9fecba75ca29da7dd7d1.tar.gz";
|
||||||
|
hash = "sha256-fyNeCVbC9UAaKJY6JhAZlT0A479M/AKYMPIWEZbDWD0=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "ziglyph";
|
||||||
|
url = "https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz";
|
||||||
|
hash = "sha256-cse98+Ft8QUjX+P88yyYfaxJOJGQ9M7Ymw7jFxDz89k=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12209ca054cb1919fa276e328967f10b253f7537c4136eb48f3332b0f7cf661cad38";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "zig_wayland";
|
||||||
|
url = "https://deps.files.ghostty.org/zig-wayland-fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz";
|
||||||
|
hash = "sha256-RtAystqK/GRYIquTK1KfD7rRSCrfuzAvCD1Z9DE1ldc=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "zf";
|
||||||
|
url = "git+https://github.com/natecraddock/zf/?ref=main#ed99ca18b02dda052e20ba467e90b623c04690dd";
|
||||||
|
hash = "sha256-t6QNrEJZ4GZZsYixjYvpdrYoCmNbG8TTUmGs2MFa4sU=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220c72c1697dd9008461ead702997a15d8a1c5810247f02e7983b9f74c6c6e4c087";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "vaxis";
|
||||||
|
url = "git+https://github.com/rockorager/libvaxis/?ref=main#dc0a228a5544988d4a920cfb40be9cd28db41423";
|
||||||
|
hash = "sha256-QWN4jOrA91KlbqmeEHHJ4HTnCC9nmfxt8DHUXJpAzLI=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "gobject";
|
||||||
|
url = "https://github.com/ianprime0509/zig-gobject/releases/download/v0.2.2/bindings-gnome47.tar.zst";
|
||||||
|
hash = "sha256-UU97kNv/bZzQPKz1djhEDLapLguvfBpFfWVb6FthtcI=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12202cdac858abc52413a6c6711d5026d2d3c8e13f95ca2c327eade0736298bb021f";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "wayland";
|
||||||
|
url = "https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz";
|
||||||
|
hash = "sha256-m9G72jdG30KH2yQhNBcBJ46OnekzuX0i2uIOyczkpLk=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12201a57c6ce0001aa034fa80fba3e1cd2253c560a45748f4f4dd21ff23b491cddef";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "wayland_protocols";
|
||||||
|
url = "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz";
|
||||||
|
hash = "sha256-XO3K3egbdeYPI+XoO13SuOtO+5+Peb16NH0UiusFMPg=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "plasma_wayland_protocols";
|
||||||
|
url = "git+https://github.com/KDE/plasma-wayland-protocols?ref=main#db525e8f9da548cffa2ac77618dd0fbe7f511b86";
|
||||||
|
hash = "sha256-iWRv3+OfmHxmeCJ8m0ChjgZX6mwXlcFmK8P/Vt8gDj4=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12203d2647e5daf36a9c85b969e03f422540786ce9ea624eb4c26d204fe1f46218f3";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "iterm2_themes";
|
||||||
|
url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/db227d159adc265818f2e898da0f70ef8d7b580e.tar.gz";
|
||||||
|
hash = "sha256-Iyf7U4rpvNkPX4AOEbYSYGte5+SjRwsWD2luOn1Hz8U=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "imgui";
|
||||||
|
url = "https://github.com/ocornut/imgui/archive/e391fe2e66eb1c96b1624ae8444dc64c23146ef4.tar.gz";
|
||||||
|
hash = "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "freetype";
|
||||||
|
url = "https://github.com/freetype/freetype/archive/refs/tags/VER-2-13-2.tar.gz";
|
||||||
|
hash = "sha256-QnIB9dUVFnDQXB9bRb713aHy592XHvVPD+qqf/0quQw=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "libpng";
|
||||||
|
url = "https://github.com/glennrp/libpng/archive/refs/tags/v1.6.43.tar.gz";
|
||||||
|
hash = "sha256-/syVtGzwXo4/yKQUdQ4LparQDYnp/fF16U/wQcrxoDo=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "zlib";
|
||||||
|
url = "https://github.com/madler/zlib/archive/refs/tags/v1.3.1.tar.gz";
|
||||||
|
hash = "sha256-F+iIY/NgBnKrSRgvIXKBtvxNPHYr3jYZNeQ2qVIU0Fw=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12201149afb3326c56c05bb0a577f54f76ac20deece63aa2f5cd6ff31a4fa4fcb3b7";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "fontconfig";
|
||||||
|
url = "https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz";
|
||||||
|
hash = "sha256-O6LdkhWHGKzsXKrxpxYEO1qgVcJ7CB2RSvPMtA3OilU=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "122032442d95c3b428ae8e526017fad881e7dc78eab4d558e9a58a80bfbd65a64f7d";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "libxml2";
|
||||||
|
url = "https://github.com/GNOME/libxml2/archive/refs/tags/v2.11.5.tar.gz";
|
||||||
|
hash = "sha256-bCgFni4+60K1tLFkieORamNGwQladP7jvGXNxdiaYhU=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "harfbuzz";
|
||||||
|
url = "https://github.com/harfbuzz/harfbuzz/archive/refs/tags/8.4.0.tar.gz";
|
||||||
|
hash = "sha256-nxygiYE7BZRK0c6MfgGCEwJtNdybq0gKIeuHaDg5ZVY=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "highway";
|
||||||
|
url = "https://github.com/google/highway/archive/refs/tags/1.1.0.tar.gz";
|
||||||
|
hash = "sha256-NUqLRTm1iOcLmOxwhEJz4/J0EwLEw3e8xOgbPRhm98k=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "oniguruma";
|
||||||
|
url = "https://github.com/kkos/oniguruma/archive/refs/tags/v6.9.9.tar.gz";
|
||||||
|
hash = "sha256-ABqhIC54RI9MC/GkjHblVodrNvFtks4yB+zP1h2Z8qA=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "sentry";
|
||||||
|
url = "https://github.com/getsentry/sentry-native/archive/refs/tags/0.7.8.tar.gz";
|
||||||
|
hash = "sha256-KsZJfMjWGo0xCT5HrduMmyxFsWsHBbszSoNbZCPDGN8=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12207fd37bb8251919c112dcdd8f616a491857b34a451f7e4486490077206dc2a1ea";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "breakpad";
|
||||||
|
url = "https://github.com/getsentry/breakpad/archive/b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz";
|
||||||
|
hash = "sha256-bMqYlD0amQdmzvYQd8Ca/1k4Bj/heh7+EijlQSttatk=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "utfcpp";
|
||||||
|
url = "https://github.com/nemtrif/utfcpp/archive/refs/tags/v4.0.5.tar.gz";
|
||||||
|
hash = "sha256-/8ZooxDndgfTk/PBizJxXyI9oerExNbgV5oR345rWc8=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "wuffs";
|
||||||
|
url = "https://github.com/google/wuffs/archive/refs/tags/v0.4.0-alpha.9.tar.gz";
|
||||||
|
hash = "sha256-nkzSCr6W5sTG7enDBXEIhgEm574uLD41UVR2wlC+HBM=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "pixels";
|
||||||
|
url = "git+https://github.com/make-github-pseudonymous-again/pixels?ref=main#d843c2714d32e15b48b8d7eeb480295af537f877";
|
||||||
|
hash = "sha256-kXYGO0qn2PfyOYCrRA49BHIgTzdmKhI8SNO1ZKfUYEE=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "glslang";
|
||||||
|
url = "https://github.com/KhronosGroup/glslang/archive/refs/tags/14.2.0.tar.gz";
|
||||||
|
hash = "sha256-FKLtu1Ccs+UamlPj9eQ12/WXFgS0uDPmPmB26MCpl7U=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da";
|
||||||
|
path = fetchZigArtifact {
|
||||||
|
name = "spirv_cross";
|
||||||
|
url = "https://github.com/KhronosGroup/SPIRV-Cross/archive/476f384eb7d9e48613c45179e502a15ab95b6b49.tar.gz";
|
||||||
|
hash = "sha256-tStvz8Ref6abHwahNiwVVHNETizAmZVVaxVsU7pmV+M=";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]
|
@@ -1,12 +0,0 @@
|
|||||||
//! Reverse Index (RI) - ESC M
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
try stdout.print("A\nB\nC", .{});
|
|
||||||
try stdout.print("\x1BM", .{});
|
|
||||||
try stdout.print("D\n\n", .{});
|
|
||||||
|
|
||||||
// const stdin = std.io.getStdIn().reader();
|
|
||||||
// _ = try stdin.readByte();
|
|
||||||
}
|
|
@@ -1,23 +0,0 @@
|
|||||||
//! Reverse Index (RI) - ESC M
|
|
||||||
//! Case: test that if the cursor is at the top, it scrolls down.
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
try stdout.print("A\nB\n\n", .{});
|
|
||||||
|
|
||||||
try stdout.print("\x1B[H", .{}); // Top-left
|
|
||||||
try stdout.print("\x1BM", .{}); // Reverse-Index
|
|
||||||
try stdout.print("D", .{});
|
|
||||||
|
|
||||||
try stdout.print("\x0D", .{}); // CR
|
|
||||||
try stdout.print("\x0A", .{}); // LF
|
|
||||||
try stdout.print("\x1B[H", .{}); // Top-left
|
|
||||||
try stdout.print("\x1BM", .{}); // Reverse-Index
|
|
||||||
try stdout.print("E", .{});
|
|
||||||
|
|
||||||
try stdout.print("\n", .{});
|
|
||||||
|
|
||||||
// const stdin = std.io.getStdIn().reader();
|
|
||||||
// _ = try stdin.readByte();
|
|
||||||
}
|
|
@@ -1,99 +0,0 @@
|
|||||||
//! Outputs various box glyphs for testing.
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
|
|
||||||
// Box Drawing
|
|
||||||
{
|
|
||||||
try stdout.print("\x1b[4mBox Drawing\x1b[0m\n", .{});
|
|
||||||
var i: usize = 0x2500;
|
|
||||||
const step: usize = 32;
|
|
||||||
while (i <= 0x257F) : (i += step) {
|
|
||||||
var j: usize = 0;
|
|
||||||
while (j < step) : (j += 1) {
|
|
||||||
try stdout.print("{u} ", .{@as(u21, @intCast(i + j))});
|
|
||||||
}
|
|
||||||
|
|
||||||
try stdout.print("\n\n", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Block Elements
|
|
||||||
{
|
|
||||||
try stdout.print("\x1b[4mBlock Elements\x1b[0m\n", .{});
|
|
||||||
var i: usize = 0x2580;
|
|
||||||
const step: usize = 32;
|
|
||||||
while (i <= 0x259f) : (i += step) {
|
|
||||||
var j: usize = 0;
|
|
||||||
while (j < step) : (j += 1) {
|
|
||||||
try stdout.print("{u} ", .{@as(u21, @intCast(i + j))});
|
|
||||||
}
|
|
||||||
|
|
||||||
try stdout.print("\n\n", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Braille Elements
|
|
||||||
{
|
|
||||||
try stdout.print("\x1b[4mBraille\x1b[0m\n", .{});
|
|
||||||
var i: usize = 0x2800;
|
|
||||||
const step: usize = 32;
|
|
||||||
while (i <= 0x28FF) : (i += step) {
|
|
||||||
var j: usize = 0;
|
|
||||||
while (j < step) : (j += 1) {
|
|
||||||
try stdout.print("{u} ", .{@as(u21, @intCast(i + j))});
|
|
||||||
}
|
|
||||||
|
|
||||||
try stdout.print("\n\n", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
try stdout.print("\x1b[4mSextants\x1b[0m\n", .{});
|
|
||||||
var i: usize = 0x1FB00;
|
|
||||||
const step: usize = 32;
|
|
||||||
const end = 0x1FB3B;
|
|
||||||
while (i <= end) : (i += step) {
|
|
||||||
var j: usize = 0;
|
|
||||||
while (j < step) : (j += 1) {
|
|
||||||
const v = i + j;
|
|
||||||
if (v <= end) try stdout.print("{u} ", .{@as(u21, @intCast(v))});
|
|
||||||
}
|
|
||||||
|
|
||||||
try stdout.print("\n\n", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
try stdout.print("\x1b[4mWedge Triangles\x1b[0m\n", .{});
|
|
||||||
var i: usize = 0x1FB3C;
|
|
||||||
const step: usize = 32;
|
|
||||||
const end = 0x1FB6B;
|
|
||||||
while (i <= end) : (i += step) {
|
|
||||||
var j: usize = 0;
|
|
||||||
while (j < step) : (j += 1) {
|
|
||||||
const v = i + j;
|
|
||||||
if (v <= end) try stdout.print("{u} ", .{@as(u21, @intCast(v))});
|
|
||||||
}
|
|
||||||
|
|
||||||
try stdout.print("\n\n", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
try stdout.print("\x1b[4mOther\x1b[0m\n", .{});
|
|
||||||
var i: usize = 0x1FB70;
|
|
||||||
const step: usize = 32;
|
|
||||||
const end = 0x1FB8B;
|
|
||||||
while (i <= end) : (i += step) {
|
|
||||||
var j: usize = 0;
|
|
||||||
while (j < step) : (j += 1) {
|
|
||||||
const v = i + j;
|
|
||||||
if (v <= end) try stdout.print("{u} ", .{@as(u21, @intCast(v))});
|
|
||||||
}
|
|
||||||
|
|
||||||
try stdout.print("\n\n", .{});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,15 +0,0 @@
|
|||||||
//! Set Top and Bottom Margins (DECSTBM) - ESC [ r
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
try stdout.print("A\nB\nC\nD", .{});
|
|
||||||
try stdout.print("\x1B[1;3r", .{}); // cursor up
|
|
||||||
try stdout.print("\x1B[1;1H", .{}); // top-left
|
|
||||||
try stdout.print("\x1B[M", .{}); // delete line
|
|
||||||
try stdout.print("E\n", .{});
|
|
||||||
try stdout.print("\x1B[7;1H", .{}); // cursor up
|
|
||||||
|
|
||||||
// const stdin = std.io.getStdIn().reader();
|
|
||||||
// _ = try stdin.readByte();
|
|
||||||
}
|
|
@@ -1,14 +0,0 @@
|
|||||||
//! Delete Line (DL) - Esc [ M
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
try stdout.print("A\nB\nC\nD", .{});
|
|
||||||
try stdout.print("\x1B[2A", .{}); // cursor up
|
|
||||||
try stdout.print("\x1B[M", .{});
|
|
||||||
try stdout.print("E\n", .{});
|
|
||||||
try stdout.print("\x1B[B", .{});
|
|
||||||
|
|
||||||
// const stdin = std.io.getStdIn().reader();
|
|
||||||
// _ = try stdin.readByte();
|
|
||||||
}
|
|
@@ -1,17 +0,0 @@
|
|||||||
//! Insert Line (IL) - Esc [ L
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
try stdout.print("\x1B[2J", .{}); // clear screen
|
|
||||||
try stdout.print("\x1B[1;1H", .{}); // set cursor position
|
|
||||||
try stdout.print("A\nB\nC\nD\nE", .{});
|
|
||||||
try stdout.print("\x1B[1;2r", .{}); // set scroll region
|
|
||||||
try stdout.print("\x1B[1;1H", .{}); // set cursor position
|
|
||||||
try stdout.print("\x1B[1L", .{}); // insert lines
|
|
||||||
try stdout.print("X", .{});
|
|
||||||
try stdout.print("\x1B[7;1H", .{}); // set cursor position
|
|
||||||
|
|
||||||
// const stdin = std.io.getStdIn().reader();
|
|
||||||
// _ = try stdin.readByte();
|
|
||||||
}
|
|
@@ -1,10 +0,0 @@
|
|||||||
//! DECALN - ESC # 8
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub fn main() !void {
|
|
||||||
const stdout = std.io.getStdOut().writer();
|
|
||||||
try stdout.print("\x1B#8", .{});
|
|
||||||
|
|
||||||
// const stdin = std.io.getStdIn().reader();
|
|
||||||
// _ = try stdin.readByte();
|
|
||||||
}
|
|
13
default.nix
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
(import (
|
||||||
|
let
|
||||||
|
lock = builtins.fromJSON (builtins.readFile ./flake.lock);
|
||||||
|
nodeName = lock.nodes.root.inputs.flake-compat;
|
||||||
|
in
|
||||||
|
fetchTarball {
|
||||||
|
url =
|
||||||
|
lock.nodes.${nodeName}.locked.url
|
||||||
|
or "https://github.com/edolstra/flake-compat/archive/${lock.nodes.${nodeName}.locked.rev}.tar.gz";
|
||||||
|
sha256 = lock.nodes.${nodeName}.locked.narHash;
|
||||||
|
}
|
||||||
|
) {src = ./.;})
|
||||||
|
.defaultNix
|
1
dist/linux/app.desktop
vendored
@@ -7,6 +7,7 @@ Icon=com.mitchellh.ghostty
|
|||||||
Categories=System;TerminalEmulator;
|
Categories=System;TerminalEmulator;
|
||||||
Keywords=terminal;tty;pty;
|
Keywords=terminal;tty;pty;
|
||||||
StartupNotify=true
|
StartupNotify=true
|
||||||
|
StartupWMClass=com.mitchellh.ghostty
|
||||||
Terminal=false
|
Terminal=false
|
||||||
Actions=new-window;
|
Actions=new-window;
|
||||||
X-GNOME-UsesNotifications=true
|
X-GNOME-UsesNotifications=true
|
||||||
|
0
dist/linux/ghostty_dolphin.desktop
vendored
Normal file → Executable file
97
dist/linux/ghostty_nautilus.py
vendored
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# Adapted from wezterm: https://github.com/wez/wezterm/blob/main/assets/wezterm-nautilus.py
|
||||||
|
# original copyright notice:
|
||||||
|
#
|
||||||
|
# Copyright (C) 2022 Sebastian Wiesner <sebastian@swsnr.de>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
from os.path import isdir
|
||||||
|
from gi import require_version
|
||||||
|
from gi.repository import Nautilus, GObject, Gio, GLib
|
||||||
|
|
||||||
|
|
||||||
|
class OpenInGhosttyAction(GObject.GObject, Nautilus.MenuProvider):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
session = Gio.bus_get_sync(Gio.BusType.SESSION, None)
|
||||||
|
self._systemd = None
|
||||||
|
# Check if the this system runs under systemd, per sd_booted(3)
|
||||||
|
if isdir('/run/systemd/system/'):
|
||||||
|
self._systemd = Gio.DBusProxy.new_sync(session,
|
||||||
|
Gio.DBusProxyFlags.NONE,
|
||||||
|
None,
|
||||||
|
"org.freedesktop.systemd1",
|
||||||
|
"/org/freedesktop/systemd1",
|
||||||
|
"org.freedesktop.systemd1.Manager", None)
|
||||||
|
|
||||||
|
def _open_terminal(self, path):
|
||||||
|
cmd = ['ghostty', f'--working-directory={path}', '--gtk-single-instance=false']
|
||||||
|
child = Gio.Subprocess.new(cmd, Gio.SubprocessFlags.NONE)
|
||||||
|
if self._systemd:
|
||||||
|
# Move new terminal into a dedicated systemd scope to make systemd
|
||||||
|
# track the terminal separately; in particular this makes systemd
|
||||||
|
# keep a separate CPU and memory account for the terminal which in turn
|
||||||
|
# ensures that oomd doesn't take nautilus down if a process in
|
||||||
|
# ghostty consumes a lot of memory.
|
||||||
|
pid = int(child.get_identifier())
|
||||||
|
props = [("PIDs", GLib.Variant('au', [pid])),
|
||||||
|
('CollectMode', GLib.Variant('s', 'inactive-or-failed'))]
|
||||||
|
name = 'app-nautilus-com.mitchellh.ghostty-{}.scope'.format(pid)
|
||||||
|
args = GLib.Variant('(ssa(sv)a(sa(sv)))', (name, 'fail', props, []))
|
||||||
|
self._systemd.call_sync('StartTransientUnit', args,
|
||||||
|
Gio.DBusCallFlags.NO_AUTO_START, 500, None)
|
||||||
|
|
||||||
|
def _menu_item_activated(self, _menu, paths):
|
||||||
|
for path in paths:
|
||||||
|
self._open_terminal(path)
|
||||||
|
|
||||||
|
def _make_item(self, name, paths):
|
||||||
|
item = Nautilus.MenuItem(name=name, label='Open in Ghostty',
|
||||||
|
icon='com.mitchellh.ghostty')
|
||||||
|
item.connect('activate', self._menu_item_activated, paths)
|
||||||
|
return item
|
||||||
|
|
||||||
|
def _paths_to_open(self, files):
|
||||||
|
paths = []
|
||||||
|
for file in files:
|
||||||
|
location = file.get_location() if file.is_directory() else file.get_parent_location()
|
||||||
|
path = location.get_path()
|
||||||
|
if path and path not in paths:
|
||||||
|
paths.append(path)
|
||||||
|
if 10 < len(paths):
|
||||||
|
# Let's not open anything if the user selected a lot of directories,
|
||||||
|
# to avoid accidentally spamming their desktop with dozends of
|
||||||
|
# new windows or tabs. Ten is a totally arbitrary limit :)
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
return paths
|
||||||
|
|
||||||
|
def get_file_items(self, *args):
|
||||||
|
# Nautilus 3.0 API passes args (window, files), 4.0 API just passes files
|
||||||
|
files = args[0] if len(args) == 1 else args[1]
|
||||||
|
paths = self._paths_to_open(files)
|
||||||
|
if paths:
|
||||||
|
return [self._make_item(name='GhosttyNautilus::open_in_ghostty', paths=paths)]
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def get_background_items(self, *args):
|
||||||
|
# Nautilus 3.0 API passes args (window, file), 4.0 API just passes file
|
||||||
|
file = args[0] if len(args) == 1 else args[1]
|
||||||
|
paths = self._paths_to_open([file])
|
||||||
|
if paths:
|
||||||
|
return [self._make_item(name='GhosttyNautilus::open_folder_in_ghostty', paths=paths)]
|
||||||
|
else:
|
||||||
|
return []
|
7
dist/macos/update_appcast_tag.py
vendored
@@ -21,6 +21,7 @@ from datetime import datetime, timezone
|
|||||||
|
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(timezone.utc)
|
||||||
version = os.environ["GHOSTTY_VERSION"]
|
version = os.environ["GHOSTTY_VERSION"]
|
||||||
|
version_dash = version.replace('.', '-')
|
||||||
build = os.environ["GHOSTTY_BUILD"]
|
build = os.environ["GHOSTTY_BUILD"]
|
||||||
commit = os.environ["GHOSTTY_COMMIT"]
|
commit = os.environ["GHOSTTY_COMMIT"]
|
||||||
commit_long = os.environ["GHOSTTY_COMMIT_LONG"]
|
commit_long = os.environ["GHOSTTY_COMMIT_LONG"]
|
||||||
@@ -82,6 +83,8 @@ elem = ET.SubElement(item, "sparkle:shortVersionString")
|
|||||||
elem.text = f"{version}"
|
elem.text = f"{version}"
|
||||||
elem = ET.SubElement(item, "sparkle:minimumSystemVersion")
|
elem = ET.SubElement(item, "sparkle:minimumSystemVersion")
|
||||||
elem.text = "13.0.0"
|
elem.text = "13.0.0"
|
||||||
|
elem = ET.SubElement(item, "sparkle:fullReleaseNotesLink")
|
||||||
|
elem.text = f"https://ghostty.org/docs/install/release-notes/{version_dash}"
|
||||||
elem = ET.SubElement(item, "description")
|
elem = ET.SubElement(item, "description")
|
||||||
elem.text = f"""
|
elem.text = f"""
|
||||||
<h1>Ghostty v{version}</h1>
|
<h1>Ghostty v{version}</h1>
|
||||||
@@ -91,8 +94,8 @@ on {now.strftime('%Y-%m-%d')}.
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
We don't currently generate release notes for auto-updates.
|
We don't currently generate release notes for auto-updates.
|
||||||
You can view the complete changelog and release notes on
|
You can view the complete changelog and release notes
|
||||||
the <a href="https://ghostty.org">Ghostty website</a>.
|
at <a href="https://ghostty.org/docs/install/release-notes/{version_dash}">ghostty.org/docs/install/release-notes/{version_dash}</a>.
|
||||||
</p>
|
</p>
|
||||||
"""
|
"""
|
||||||
elem = ET.SubElement(item, "enclosure")
|
elem = ET.SubElement(item, "enclosure")
|
||||||
|
62
flake.lock
generated
@@ -3,11 +3,11 @@
|
|||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1696426674,
|
"lastModified": 1733328505,
|
||||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -21,11 +21,11 @@
|
|||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1705309234,
|
"lastModified": 1731533236,
|
||||||
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -36,11 +36,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-stable": {
|
"nixpkgs-stable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1733423277,
|
"lastModified": 1738255539,
|
||||||
"narHash": "sha256-TxabjxEgkNbCGFRHgM/b9yZWlBj60gUOUnRT/wbVQR8=",
|
"narHash": "sha256-hP2eOqhIO/OILW+3moNWO4GtdJFYCqAe9yJZgvlCoDQ=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "e36963a147267afc055f7cf65225958633e536bf",
|
"rev": "c3511a3b53b482aa7547c9d1626fd7310c1de1c5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -52,11 +52,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-unstable": {
|
"nixpkgs-unstable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1733229606,
|
"lastModified": 1738136902,
|
||||||
"narHash": "sha256-FLYY5M0rpa5C2QAE3CKLYAM6TwbKicdRK6qNrSHlNrE=",
|
"narHash": "sha256-pUvLijVGARw4u793APze3j6mU1Zwdtz7hGkGGkD87qw=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "566e53c2ad750c84f6d31f9ccb9d00f823165550",
|
"rev": "9a5db3142ce450045840cc8d832b13b8a2018e0c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -69,9 +69,11 @@
|
|||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
"nixpkgs-stable": "nixpkgs-stable",
|
"nixpkgs-stable": "nixpkgs-stable",
|
||||||
"nixpkgs-unstable": "nixpkgs-unstable",
|
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||||
"zig": "zig"
|
"zig": "zig",
|
||||||
|
"zig2nix": "zig2nix"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"systems": {
|
"systems": {
|
||||||
@@ -92,17 +94,19 @@
|
|||||||
"zig": {
|
"zig": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": [],
|
"flake-compat": [],
|
||||||
"flake-utils": "flake-utils",
|
"flake-utils": [
|
||||||
|
"flake-utils"
|
||||||
|
],
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"nixpkgs-stable"
|
"nixpkgs-stable"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1717848532,
|
"lastModified": 1738239110,
|
||||||
"narHash": "sha256-d+xIUvSTreHl8pAmU1fnmkfDTGQYCn2Rb/zOwByxS2M=",
|
"narHash": "sha256-Y5i9mQ++dyIQr+zEPNy+KIbc5wjPmfllBrag3cHZgcE=",
|
||||||
"owner": "mitchellh",
|
"owner": "mitchellh",
|
||||||
"repo": "zig-overlay",
|
"repo": "zig-overlay",
|
||||||
"rev": "02fc5cc555fc14fda40c42d7c3250efa43812b43",
|
"rev": "1a8fb6f3a04724519436355564b95fce5e272504",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -110,6 +114,30 @@
|
|||||||
"repo": "zig-overlay",
|
"repo": "zig-overlay",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"zig2nix": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": [
|
||||||
|
"flake-utils"
|
||||||
|
],
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs-stable"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1738263917,
|
||||||
|
"narHash": "sha256-j/3fwe2pEOquHabP/puljOKwAZFjIE9gXZqA91sC48M=",
|
||||||
|
"owner": "jcollie",
|
||||||
|
"repo": "zig2nix",
|
||||||
|
"rev": "c311d8e77a6ee0d995f40a6e10a89a3a4ab04f9a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "jcollie",
|
||||||
|
"ref": "c311d8e77a6ee0d995f40a6e10a89a3a4ab04f9a",
|
||||||
|
"repo": "zig2nix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
106
flake.nix
@@ -8,6 +8,7 @@
|
|||||||
# glibc versions used by our dependencies from Nix are compatible with the
|
# glibc versions used by our dependencies from Nix are compatible with the
|
||||||
# system glibc that the user is building for.
|
# system glibc that the user is building for.
|
||||||
nixpkgs-stable.url = "github:nixos/nixpkgs/release-24.11";
|
nixpkgs-stable.url = "github:nixos/nixpkgs/release-24.11";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
|
||||||
# Used for shell.nix
|
# Used for shell.nix
|
||||||
flake-compat = {
|
flake-compat = {
|
||||||
@@ -19,9 +20,18 @@
|
|||||||
url = "github:mitchellh/zig-overlay";
|
url = "github:mitchellh/zig-overlay";
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.follows = "nixpkgs-stable";
|
nixpkgs.follows = "nixpkgs-stable";
|
||||||
|
flake-utils.follows = "flake-utils";
|
||||||
flake-compat.follows = "";
|
flake-compat.follows = "";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
zig2nix = {
|
||||||
|
url = "github:jcollie/zig2nix?ref=c311d8e77a6ee0d995f40a6e10a89a3a4ab04f9a";
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.follows = "nixpkgs-stable";
|
||||||
|
flake-utils.follows = "flake-utils";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
@@ -29,40 +39,86 @@
|
|||||||
nixpkgs-unstable,
|
nixpkgs-unstable,
|
||||||
nixpkgs-stable,
|
nixpkgs-stable,
|
||||||
zig,
|
zig,
|
||||||
|
zig2nix,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
builtins.foldl' nixpkgs-stable.lib.recursiveUpdate {} (builtins.map (system: let
|
builtins.foldl' nixpkgs-stable.lib.recursiveUpdate {} (
|
||||||
pkgs-stable = nixpkgs-stable.legacyPackages.${system};
|
builtins.map (
|
||||||
pkgs-unstable = nixpkgs-unstable.legacyPackages.${system};
|
system: let
|
||||||
in {
|
pkgs-stable = nixpkgs-stable.legacyPackages.${system};
|
||||||
devShell.${system} = pkgs-stable.callPackage ./nix/devShell.nix {
|
pkgs-unstable = nixpkgs-unstable.legacyPackages.${system};
|
||||||
zig = zig.packages.${system}."0.13.0";
|
in {
|
||||||
wraptest = pkgs-stable.callPackage ./nix/wraptest.nix {};
|
devShell.${system} = pkgs-stable.callPackage ./nix/devShell.nix {
|
||||||
};
|
zig = zig.packages.${system}."0.13.0";
|
||||||
|
wraptest = pkgs-stable.callPackage ./nix/wraptest.nix {};
|
||||||
|
zig2nix = zig2nix;
|
||||||
|
};
|
||||||
|
|
||||||
packages.${system} = let
|
packages.${system} = let
|
||||||
mkArgs = optimize: {
|
mkArgs = optimize: {
|
||||||
inherit optimize;
|
inherit optimize;
|
||||||
|
|
||||||
revision = self.shortRev or self.dirtyShortRev or "dirty";
|
revision = self.shortRev or self.dirtyShortRev or "dirty";
|
||||||
};
|
};
|
||||||
in rec {
|
in rec {
|
||||||
ghostty-debug = pkgs-stable.callPackage ./nix/package.nix (mkArgs "Debug");
|
deps = pkgs-stable.callPackage ./build.zig.zon.nix {};
|
||||||
ghostty-releasesafe = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseSafe");
|
ghostty-debug = pkgs-stable.callPackage ./nix/package.nix (mkArgs "Debug");
|
||||||
ghostty-releasefast = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseFast");
|
ghostty-releasesafe = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseSafe");
|
||||||
|
ghostty-releasefast = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseFast");
|
||||||
|
|
||||||
ghostty = ghostty-releasefast;
|
ghostty = ghostty-releasefast;
|
||||||
default = ghostty;
|
default = ghostty;
|
||||||
};
|
};
|
||||||
|
|
||||||
formatter.${system} = pkgs-stable.alejandra;
|
formatter.${system} = pkgs-stable.alejandra;
|
||||||
|
|
||||||
# Our supported systems are the same supported systems as the Zig binaries.
|
apps.${system} = let
|
||||||
}) (builtins.attrNames zig.packages))
|
runVM = (
|
||||||
|
module: let
|
||||||
|
vm = import ./nix/vm/create.nix {
|
||||||
|
inherit system module;
|
||||||
|
nixpkgs = nixpkgs-stable;
|
||||||
|
overlay = self.overlays.debug;
|
||||||
|
};
|
||||||
|
program = pkgs-stable.writeShellScript "run-ghostty-vm" ''
|
||||||
|
SHARED_DIR=$(pwd)
|
||||||
|
export SHARED_DIR
|
||||||
|
|
||||||
|
${pkgs-stable.lib.getExe vm.config.system.build.vm} "$@"
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
type = "app";
|
||||||
|
program = "${program}";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
in {
|
||||||
|
wayland-cinnamon = runVM ./nix/vm/wayland-cinnamon.nix;
|
||||||
|
wayland-gnome = runVM ./nix/vm/wayland-gnome.nix;
|
||||||
|
wayland-plasma6 = runVM ./nix/vm/wayland-plasma6.nix;
|
||||||
|
x11-cinnamon = runVM ./nix/vm/x11-cinnamon.nix;
|
||||||
|
x11-gnome = runVM ./nix/vm/x11-gnome.nix;
|
||||||
|
x11-plasma6 = runVM ./nix/vm/x11-plasma6.nix;
|
||||||
|
x11-xfce = runVM ./nix/vm/x11-xfce.nix;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
# Our supported systems are the same supported systems as the Zig binaries.
|
||||||
|
) (builtins.attrNames zig.packages)
|
||||||
|
)
|
||||||
// {
|
// {
|
||||||
overlays.default = final: prev: {
|
overlays = {
|
||||||
ghostty = self.packages.${prev.system}.default;
|
default = self.overlays.releasefast;
|
||||||
|
releasefast = final: prev: {
|
||||||
|
ghostty = self.packages.${prev.system}.ghostty-releasefast;
|
||||||
|
};
|
||||||
|
debug = final: prev: {
|
||||||
|
ghostty = self.packages.${prev.system}.ghostty-debug;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
create-vm = import ./nix/vm/create.nix;
|
||||||
|
create-cinnamon-vm = import ./nix/vm/create-cinnamon.nix;
|
||||||
|
create-gnome-vm = import ./nix/vm/create-gnome.nix;
|
||||||
|
create-plasma6-vm = import ./nix/vm/create-plasma6.nix;
|
||||||
|
create-xfce-vm = import ./nix/vm/create-xfce.nix;
|
||||||
};
|
};
|
||||||
|
|
||||||
nixConfig = {
|
nixConfig = {
|
||||||
|
@@ -159,7 +159,7 @@ typedef enum {
|
|||||||
GHOSTTY_KEY_EQUAL,
|
GHOSTTY_KEY_EQUAL,
|
||||||
GHOSTTY_KEY_LEFT_BRACKET, // [
|
GHOSTTY_KEY_LEFT_BRACKET, // [
|
||||||
GHOSTTY_KEY_RIGHT_BRACKET, // ]
|
GHOSTTY_KEY_RIGHT_BRACKET, // ]
|
||||||
GHOSTTY_KEY_BACKSLASH, // /
|
GHOSTTY_KEY_BACKSLASH, // \
|
||||||
|
|
||||||
// control
|
// control
|
||||||
GHOSTTY_KEY_UP,
|
GHOSTTY_KEY_UP,
|
||||||
@@ -375,9 +375,9 @@ typedef enum {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
GHOSTTY_GOTO_SPLIT_PREVIOUS,
|
GHOSTTY_GOTO_SPLIT_PREVIOUS,
|
||||||
GHOSTTY_GOTO_SPLIT_NEXT,
|
GHOSTTY_GOTO_SPLIT_NEXT,
|
||||||
GHOSTTY_GOTO_SPLIT_TOP,
|
GHOSTTY_GOTO_SPLIT_UP,
|
||||||
GHOSTTY_GOTO_SPLIT_LEFT,
|
GHOSTTY_GOTO_SPLIT_LEFT,
|
||||||
GHOSTTY_GOTO_SPLIT_BOTTOM,
|
GHOSTTY_GOTO_SPLIT_DOWN,
|
||||||
GHOSTTY_GOTO_SPLIT_RIGHT,
|
GHOSTTY_GOTO_SPLIT_RIGHT,
|
||||||
} ghostty_action_goto_split_e;
|
} ghostty_action_goto_split_e;
|
||||||
|
|
||||||
@@ -559,10 +559,13 @@ typedef struct {
|
|||||||
|
|
||||||
// apprt.Action.Key
|
// apprt.Action.Key
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
GHOSTTY_ACTION_QUIT,
|
||||||
GHOSTTY_ACTION_NEW_WINDOW,
|
GHOSTTY_ACTION_NEW_WINDOW,
|
||||||
GHOSTTY_ACTION_NEW_TAB,
|
GHOSTTY_ACTION_NEW_TAB,
|
||||||
|
GHOSTTY_ACTION_CLOSE_TAB,
|
||||||
GHOSTTY_ACTION_NEW_SPLIT,
|
GHOSTTY_ACTION_NEW_SPLIT,
|
||||||
GHOSTTY_ACTION_CLOSE_ALL_WINDOWS,
|
GHOSTTY_ACTION_CLOSE_ALL_WINDOWS,
|
||||||
|
GHOSTTY_ACTION_TOGGLE_MAXIMIZE,
|
||||||
GHOSTTY_ACTION_TOGGLE_FULLSCREEN,
|
GHOSTTY_ACTION_TOGGLE_FULLSCREEN,
|
||||||
GHOSTTY_ACTION_TOGGLE_TAB_OVERVIEW,
|
GHOSTTY_ACTION_TOGGLE_TAB_OVERVIEW,
|
||||||
GHOSTTY_ACTION_TOGGLE_WINDOW_DECORATIONS,
|
GHOSTTY_ACTION_TOGGLE_WINDOW_DECORATIONS,
|
||||||
@@ -641,7 +644,7 @@ typedef void (*ghostty_runtime_write_clipboard_cb)(void*,
|
|||||||
ghostty_clipboard_e,
|
ghostty_clipboard_e,
|
||||||
bool);
|
bool);
|
||||||
typedef void (*ghostty_runtime_close_surface_cb)(void*, bool);
|
typedef void (*ghostty_runtime_close_surface_cb)(void*, bool);
|
||||||
typedef void (*ghostty_runtime_action_cb)(ghostty_app_t,
|
typedef bool (*ghostty_runtime_action_cb)(ghostty_app_t,
|
||||||
ghostty_target_s,
|
ghostty_target_s,
|
||||||
ghostty_action_s);
|
ghostty_action_s);
|
||||||
|
|
||||||
@@ -681,10 +684,11 @@ void ghostty_config_open();
|
|||||||
ghostty_app_t ghostty_app_new(const ghostty_runtime_config_s*,
|
ghostty_app_t ghostty_app_new(const ghostty_runtime_config_s*,
|
||||||
ghostty_config_t);
|
ghostty_config_t);
|
||||||
void ghostty_app_free(ghostty_app_t);
|
void ghostty_app_free(ghostty_app_t);
|
||||||
bool ghostty_app_tick(ghostty_app_t);
|
void ghostty_app_tick(ghostty_app_t);
|
||||||
void* ghostty_app_userdata(ghostty_app_t);
|
void* ghostty_app_userdata(ghostty_app_t);
|
||||||
void ghostty_app_set_focus(ghostty_app_t, bool);
|
void ghostty_app_set_focus(ghostty_app_t, bool);
|
||||||
bool ghostty_app_key(ghostty_app_t, ghostty_input_key_s);
|
bool ghostty_app_key(ghostty_app_t, ghostty_input_key_s);
|
||||||
|
bool ghostty_app_key_is_binding(ghostty_app_t, ghostty_input_key_s);
|
||||||
void ghostty_app_keyboard_changed(ghostty_app_t);
|
void ghostty_app_keyboard_changed(ghostty_app_t);
|
||||||
void ghostty_app_open_config(ghostty_app_t);
|
void ghostty_app_open_config(ghostty_app_t);
|
||||||
void ghostty_app_update_config(ghostty_app_t, ghostty_config_t);
|
void ghostty_app_update_config(ghostty_app_t, ghostty_config_t);
|
||||||
@@ -712,7 +716,8 @@ void ghostty_surface_set_color_scheme(ghostty_surface_t,
|
|||||||
ghostty_color_scheme_e);
|
ghostty_color_scheme_e);
|
||||||
ghostty_input_mods_e ghostty_surface_key_translation_mods(ghostty_surface_t,
|
ghostty_input_mods_e ghostty_surface_key_translation_mods(ghostty_surface_t,
|
||||||
ghostty_input_mods_e);
|
ghostty_input_mods_e);
|
||||||
void ghostty_surface_key(ghostty_surface_t, ghostty_input_key_s);
|
bool ghostty_surface_key(ghostty_surface_t, ghostty_input_key_s);
|
||||||
|
bool ghostty_surface_key_is_binding(ghostty_surface_t, ghostty_input_key_s);
|
||||||
void ghostty_surface_text(ghostty_surface_t, const char*, uintptr_t);
|
void ghostty_surface_text(ghostty_surface_t, const char*, uintptr_t);
|
||||||
bool ghostty_surface_mouse_captured(ghostty_surface_t);
|
bool ghostty_surface_mouse_captured(ghostty_surface_t);
|
||||||
bool ghostty_surface_mouse_button(ghostty_surface_t,
|
bool ghostty_surface_mouse_button(ghostty_surface_t,
|
||||||
|
12
macos/Assets.xcassets/Alternate Icons/BlueprintImage.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/BlueprintImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 434 KiB |
12
macos/Assets.xcassets/Alternate Icons/ChalkboardImage.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/ChalkboardImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 576 KiB |
6
macos/Assets.xcassets/Alternate Icons/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
12
macos/Assets.xcassets/Alternate Icons/GlassImage.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/GlassImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 515 KiB |
12
macos/Assets.xcassets/Alternate Icons/HolographicImage.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/HolographicImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 588 KiB |
12
macos/Assets.xcassets/Alternate Icons/MicrochipImage.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/MicrochipImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 630 KiB |
12
macos/Assets.xcassets/Alternate Icons/PaperImage.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/PaperImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 335 KiB |
12
macos/Assets.xcassets/Alternate Icons/RetroImage.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/RetroImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 1.0 MiB |
12
macos/Assets.xcassets/Alternate Icons/XrayImage.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "macOS-AppIcon-1024px.png",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
macos/Assets.xcassets/Alternate Icons/XrayImage.imageset/macOS-AppIcon-1024px.png
vendored
Normal file
After Width: | Height: | Size: 443 KiB |
@@ -10,8 +10,8 @@
|
|||||||
29C15B1D2CDC3B2900520DD4 /* bat in Resources */ = {isa = PBXBuildFile; fileRef = 29C15B1C2CDC3B2000520DD4 /* bat */; };
|
29C15B1D2CDC3B2900520DD4 /* bat in Resources */ = {isa = PBXBuildFile; fileRef = 29C15B1C2CDC3B2000520DD4 /* bat */; };
|
||||||
55154BE02B33911F001622DC /* ghostty in Resources */ = {isa = PBXBuildFile; fileRef = 55154BDF2B33911F001622DC /* ghostty */; };
|
55154BE02B33911F001622DC /* ghostty in Resources */ = {isa = PBXBuildFile; fileRef = 55154BDF2B33911F001622DC /* ghostty */; };
|
||||||
552964E62B34A9B400030505 /* vim in Resources */ = {isa = PBXBuildFile; fileRef = 552964E52B34A9B400030505 /* vim */; };
|
552964E62B34A9B400030505 /* vim in Resources */ = {isa = PBXBuildFile; fileRef = 552964E52B34A9B400030505 /* vim */; };
|
||||||
9351BE8E3D22937F003B3499 /* nvim in Resources */ = {isa = PBXBuildFile; fileRef = 9351BE8E2D22937F003B3499 /* nvim */; };
|
|
||||||
857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 857F63802A5E64F200CA4815 /* MainMenu.xib */; };
|
857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 857F63802A5E64F200CA4815 /* MainMenu.xib */; };
|
||||||
|
9351BE8E3D22937F003B3499 /* nvim in Resources */ = {isa = PBXBuildFile; fileRef = 9351BE8E2D22937F003B3499 /* nvim */; };
|
||||||
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = A514C8D52B54A16400493A16 /* Ghostty.Config.swift */; };
|
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = A514C8D52B54A16400493A16 /* Ghostty.Config.swift */; };
|
||||||
A514C8D72B54A16400493A16 /* Ghostty.Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = A514C8D52B54A16400493A16 /* Ghostty.Config.swift */; };
|
A514C8D72B54A16400493A16 /* Ghostty.Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = A514C8D52B54A16400493A16 /* Ghostty.Config.swift */; };
|
||||||
A514C8D82B54DC6800493A16 /* Ghostty.App.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */; };
|
A514C8D82B54DC6800493A16 /* Ghostty.App.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */; };
|
||||||
@@ -69,8 +69,13 @@
|
|||||||
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */; };
|
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */; };
|
||||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5D02AE0DEA7009128F3 /* MetalView.swift */; };
|
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5D02AE0DEA7009128F3 /* MetalView.swift */; };
|
||||||
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
|
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */ = {isa = PBXBuildFile; fileRef = A5A1F8842A489D6800D1E8BC /* terminfo */; };
|
||||||
|
A5A2A3CA2D4445E30033CF96 /* Dock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A2A3C92D4445E20033CF96 /* Dock.swift */; };
|
||||||
|
A5A2A3CC2D444ABB0033CF96 /* NSApplication+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A2A3CB2D444AB80033CF96 /* NSApplication+Extension.swift */; };
|
||||||
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A6F7292CC41B8700B232A5 /* Xcode.swift */; };
|
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5A6F7292CC41B8700B232A5 /* Xcode.swift */; };
|
||||||
|
A5AEB1652D5BE7D000513529 /* LastWindowPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */; };
|
||||||
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
||||||
|
A5CA378C2D2A4DEB00931030 /* KeyboardLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */; };
|
||||||
|
A5CA378E2D31D6C300931030 /* Weak.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CA378D2D31D6C100931030 /* Weak.swift */; };
|
||||||
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */; };
|
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */; };
|
||||||
A5CBD0582C9F30960017A1AE /* Cursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0572C9F30860017A1AE /* Cursor.swift */; };
|
A5CBD0582C9F30960017A1AE /* Cursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0572C9F30860017A1AE /* Cursor.swift */; };
|
||||||
A5CBD0592C9F37B10017A1AE /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFFE29C2410700646FDA /* Backport.swift */; };
|
A5CBD0592C9F37B10017A1AE /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFFE29C2410700646FDA /* Backport.swift */; };
|
||||||
@@ -87,6 +92,8 @@
|
|||||||
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDB29B8009000646FDA /* SplitView.swift */; };
|
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDB29B8009000646FDA /* SplitView.swift */; };
|
||||||
A5CEAFDE29B8058B00646FDA /* SplitView.Divider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDD29B8058B00646FDA /* SplitView.Divider.swift */; };
|
A5CEAFDE29B8058B00646FDA /* SplitView.Divider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDD29B8058B00646FDA /* SplitView.Divider.swift */; };
|
||||||
A5CEAFFF29C2410700646FDA /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFFE29C2410700646FDA /* Backport.swift */; };
|
A5CEAFFF29C2410700646FDA /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFFE29C2410700646FDA /* Backport.swift */; };
|
||||||
|
A5CF66D42D289CEE00139794 /* NSEvent+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CF66D32D289CEA00139794 /* NSEvent+Extension.swift */; };
|
||||||
|
A5CF66D72D29DDB500139794 /* Ghostty.Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CF66D62D29DDB100139794 /* Ghostty.Event.swift */; };
|
||||||
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D0AF3A2B36A1DE00D21823 /* TerminalRestorable.swift */; };
|
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D0AF3A2B36A1DE00D21823 /* TerminalRestorable.swift */; };
|
||||||
A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D0AF3C2B37804400D21823 /* CodableBridge.swift */; };
|
A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D0AF3C2B37804400D21823 /* CodableBridge.swift */; };
|
||||||
A5E112932AF73E6E00C6E0C2 /* ClipboardConfirmation.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5E112922AF73E6E00C6E0C2 /* ClipboardConfirmation.xib */; };
|
A5E112932AF73E6E00C6E0C2 /* ClipboardConfirmation.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5E112922AF73E6E00C6E0C2 /* ClipboardConfirmation.xib */; };
|
||||||
@@ -99,6 +106,7 @@
|
|||||||
C159E89D2B69A2EF00FDFE9C /* OSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */; };
|
C159E89D2B69A2EF00FDFE9C /* OSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */; };
|
||||||
C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F26EA62B738B9900404083 /* NSView+Extension.swift */; };
|
C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F26EA62B738B9900404083 /* NSView+Extension.swift */; };
|
||||||
C1F26EE92B76CBFC00404083 /* VibrantLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = C1F26EE82B76CBFC00404083 /* VibrantLayer.m */; };
|
C1F26EE92B76CBFC00404083 /* VibrantLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = C1F26EE82B76CBFC00404083 /* VibrantLayer.m */; };
|
||||||
|
CFBB5FEA2D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = CFBB5FE92D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift */; };
|
||||||
FC5218FA2D10FFCE004C93E0 /* zsh in Resources */ = {isa = PBXBuildFile; fileRef = FC5218F92D10FFC7004C93E0 /* zsh */; };
|
FC5218FA2D10FFCE004C93E0 /* zsh in Resources */ = {isa = PBXBuildFile; fileRef = FC5218F92D10FFC7004C93E0 /* zsh */; };
|
||||||
FC9ABA9C2D0F53F80020D4C8 /* bash-completion in Resources */ = {isa = PBXBuildFile; fileRef = FC9ABA9B2D0F538D0020D4C8 /* bash-completion */; };
|
FC9ABA9C2D0F53F80020D4C8 /* bash-completion in Resources */ = {isa = PBXBuildFile; fileRef = FC9ABA9B2D0F538D0020D4C8 /* bash-completion */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
@@ -108,8 +116,8 @@
|
|||||||
3B39CAA42B33949B00DABEB8 /* GhosttyReleaseLocal.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = GhosttyReleaseLocal.entitlements; sourceTree = "<group>"; };
|
3B39CAA42B33949B00DABEB8 /* GhosttyReleaseLocal.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = GhosttyReleaseLocal.entitlements; sourceTree = "<group>"; };
|
||||||
55154BDF2B33911F001622DC /* ghostty */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ghostty; path = "../zig-out/share/ghostty"; sourceTree = "<group>"; };
|
55154BDF2B33911F001622DC /* ghostty */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ghostty; path = "../zig-out/share/ghostty"; sourceTree = "<group>"; };
|
||||||
552964E52B34A9B400030505 /* vim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = vim; path = "../zig-out/share/vim"; sourceTree = "<group>"; };
|
552964E52B34A9B400030505 /* vim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = vim; path = "../zig-out/share/vim"; sourceTree = "<group>"; };
|
||||||
9351BE8E2D22937F003B3499 /* nvim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = vim; path = "../zig-out/share/nvim"; sourceTree = "<group>"; };
|
|
||||||
857F63802A5E64F200CA4815 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
|
857F63802A5E64F200CA4815 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = "<group>"; };
|
||||||
|
9351BE8E2D22937F003B3499 /* nvim */ = {isa = PBXFileReference; lastKnownFileType = folder; name = nvim; path = "../zig-out/share/nvim"; sourceTree = "<group>"; };
|
||||||
A514C8D52B54A16400493A16 /* Ghostty.Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Config.swift; sourceTree = "<group>"; };
|
A514C8D52B54A16400493A16 /* Ghostty.Config.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Config.swift; sourceTree = "<group>"; };
|
||||||
A51B78462AF4B58B00F3EDB9 /* TerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalWindow.swift; sourceTree = "<group>"; };
|
A51B78462AF4B58B00F3EDB9 /* TerminalWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalWindow.swift; sourceTree = "<group>"; };
|
||||||
A51BFC1D2B2FB5CE00E92F16 /* About.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = About.xib; sourceTree = "<group>"; };
|
A51BFC1D2B2FB5CE00E92F16 /* About.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = About.xib; sourceTree = "<group>"; };
|
||||||
@@ -158,10 +166,15 @@
|
|||||||
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorView.swift; sourceTree = "<group>"; };
|
A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorView.swift; sourceTree = "<group>"; };
|
||||||
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalView.swift; sourceTree = "<group>"; };
|
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalView.swift; sourceTree = "<group>"; };
|
||||||
A5A1F8842A489D6800D1E8BC /* terminfo */ = {isa = PBXFileReference; lastKnownFileType = folder; name = terminfo; path = "../zig-out/share/terminfo"; sourceTree = "<group>"; };
|
A5A1F8842A489D6800D1E8BC /* terminfo */ = {isa = PBXFileReference; lastKnownFileType = folder; name = terminfo; path = "../zig-out/share/terminfo"; sourceTree = "<group>"; };
|
||||||
|
A5A2A3C92D4445E20033CF96 /* Dock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dock.swift; sourceTree = "<group>"; };
|
||||||
|
A5A2A3CB2D444AB80033CF96 /* NSApplication+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSApplication+Extension.swift"; sourceTree = "<group>"; };
|
||||||
A5A6F7292CC41B8700B232A5 /* Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xcode.swift; sourceTree = "<group>"; };
|
A5A6F7292CC41B8700B232A5 /* Xcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xcode.swift; sourceTree = "<group>"; };
|
||||||
|
A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastWindowPosition.swift; sourceTree = "<group>"; };
|
||||||
A5B30531299BEAAA0047F10C /* Ghostty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ghostty.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
A5B30531299BEAAA0047F10C /* Ghostty.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Ghostty.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
A5B30538299BEAAB0047F10C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
A5B30538299BEAAB0047F10C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ghostty.entitlements; sourceTree = "<group>"; };
|
A5B3053D299BEAAB0047F10C /* Ghostty.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Ghostty.entitlements; sourceTree = "<group>"; };
|
||||||
|
A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardLayout.swift; sourceTree = "<group>"; };
|
||||||
|
A5CA378D2D31D6C100931030 /* Weak.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weak.swift; sourceTree = "<group>"; };
|
||||||
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggableWindowView.swift; sourceTree = "<group>"; };
|
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggableWindowView.swift; sourceTree = "<group>"; };
|
||||||
A5CBD0572C9F30860017A1AE /* Cursor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cursor.swift; sourceTree = "<group>"; };
|
A5CBD0572C9F30860017A1AE /* Cursor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cursor.swift; sourceTree = "<group>"; };
|
||||||
A5CBD05B2CA0C5C70017A1AE /* QuickTerminal.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = QuickTerminal.xib; sourceTree = "<group>"; };
|
A5CBD05B2CA0C5C70017A1AE /* QuickTerminal.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = QuickTerminal.xib; sourceTree = "<group>"; };
|
||||||
@@ -177,6 +190,8 @@
|
|||||||
A5CEAFDB29B8009000646FDA /* SplitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.swift; sourceTree = "<group>"; };
|
A5CEAFDB29B8009000646FDA /* SplitView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.swift; sourceTree = "<group>"; };
|
||||||
A5CEAFDD29B8058B00646FDA /* SplitView.Divider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.Divider.swift; sourceTree = "<group>"; };
|
A5CEAFDD29B8058B00646FDA /* SplitView.Divider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitView.Divider.swift; sourceTree = "<group>"; };
|
||||||
A5CEAFFE29C2410700646FDA /* Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backport.swift; sourceTree = "<group>"; };
|
A5CEAFFE29C2410700646FDA /* Backport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Backport.swift; sourceTree = "<group>"; };
|
||||||
|
A5CF66D32D289CEA00139794 /* NSEvent+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSEvent+Extension.swift"; sourceTree = "<group>"; };
|
||||||
|
A5CF66D62D29DDB100139794 /* Ghostty.Event.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Event.swift; sourceTree = "<group>"; };
|
||||||
A5D0AF3A2B36A1DE00D21823 /* TerminalRestorable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalRestorable.swift; sourceTree = "<group>"; };
|
A5D0AF3A2B36A1DE00D21823 /* TerminalRestorable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalRestorable.swift; sourceTree = "<group>"; };
|
||||||
A5D0AF3C2B37804400D21823 /* CodableBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableBridge.swift; sourceTree = "<group>"; };
|
A5D0AF3C2B37804400D21823 /* CodableBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableBridge.swift; sourceTree = "<group>"; };
|
||||||
A5D4499D2B53AE7B000F5B83 /* Ghostty-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Ghostty-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
A5D4499D2B53AE7B000F5B83 /* Ghostty-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Ghostty-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@@ -192,6 +207,7 @@
|
|||||||
C1F26EE72B76CBFC00404083 /* VibrantLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VibrantLayer.h; sourceTree = "<group>"; };
|
C1F26EE72B76CBFC00404083 /* VibrantLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VibrantLayer.h; sourceTree = "<group>"; };
|
||||||
C1F26EE82B76CBFC00404083 /* VibrantLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VibrantLayer.m; sourceTree = "<group>"; };
|
C1F26EE82B76CBFC00404083 /* VibrantLayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VibrantLayer.m; sourceTree = "<group>"; };
|
||||||
C1F26EEA2B76CC2400404083 /* ghostty-bridging-header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ghostty-bridging-header.h"; sourceTree = "<group>"; };
|
C1F26EEA2B76CC2400404083 /* ghostty-bridging-header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ghostty-bridging-header.h"; sourceTree = "<group>"; };
|
||||||
|
CFBB5FE92D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickTerminalSpaceBehavior.swift; sourceTree = "<group>"; };
|
||||||
FC5218F92D10FFC7004C93E0 /* zsh */ = {isa = PBXFileReference; lastKnownFileType = folder; name = zsh; path = "../zig-out/share/zsh"; sourceTree = "<group>"; };
|
FC5218F92D10FFC7004C93E0 /* zsh */ = {isa = PBXFileReference; lastKnownFileType = folder; name = zsh; path = "../zig-out/share/zsh"; sourceTree = "<group>"; };
|
||||||
FC9ABA9B2D0F538D0020D4C8 /* bash-completion */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "bash-completion"; path = "../zig-out/share/bash-completion"; sourceTree = "<group>"; };
|
FC9ABA9B2D0F538D0020D4C8 /* bash-completion */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "bash-completion"; path = "../zig-out/share/bash-completion"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
@@ -256,23 +272,28 @@
|
|||||||
A534263D2A7DCBB000EBB7A2 /* Helpers */ = {
|
A534263D2A7DCBB000EBB7A2 /* Helpers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */,
|
||||||
A5A6F7292CC41B8700B232A5 /* Xcode.swift */,
|
A5A6F7292CC41B8700B232A5 /* Xcode.swift */,
|
||||||
A5CEAFFE29C2410700646FDA /* Backport.swift */,
|
A5CEAFFE29C2410700646FDA /* Backport.swift */,
|
||||||
A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */,
|
A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */,
|
||||||
A5CBD0572C9F30860017A1AE /* Cursor.swift */,
|
A5CBD0572C9F30860017A1AE /* Cursor.swift */,
|
||||||
A5D0AF3C2B37804400D21823 /* CodableBridge.swift */,
|
A5D0AF3C2B37804400D21823 /* CodableBridge.swift */,
|
||||||
|
A5A2A3C92D4445E20033CF96 /* Dock.swift */,
|
||||||
A52FFF582CAA4FF1000C6A5B /* Fullscreen.swift */,
|
A52FFF582CAA4FF1000C6A5B /* Fullscreen.swift */,
|
||||||
A59630962AEE163600D64628 /* HostingWindow.swift */,
|
A59630962AEE163600D64628 /* HostingWindow.swift */,
|
||||||
|
A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */,
|
||||||
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */,
|
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */,
|
||||||
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */,
|
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */,
|
||||||
C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */,
|
C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */,
|
||||||
A599CDAF2CF103F20049FA26 /* NSAppearance+Extension.swift */,
|
A599CDAF2CF103F20049FA26 /* NSAppearance+Extension.swift */,
|
||||||
|
A5A2A3CB2D444AB80033CF96 /* NSApplication+Extension.swift */,
|
||||||
A54B0CEA2D0CFB4A00CBEFF8 /* NSImage+Extension.swift */,
|
A54B0CEA2D0CFB4A00CBEFF8 /* NSImage+Extension.swift */,
|
||||||
A52FFF5C2CAB4D05000C6A5B /* NSScreen+Extension.swift */,
|
A52FFF5C2CAB4D05000C6A5B /* NSScreen+Extension.swift */,
|
||||||
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
||||||
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
||||||
A5985CD62C320C4500C57AD3 /* String+Extension.swift */,
|
A5985CD62C320C4500C57AD3 /* String+Extension.swift */,
|
||||||
A5CC36142C9CDA03004D6760 /* View+Extension.swift */,
|
A5CC36142C9CDA03004D6760 /* View+Extension.swift */,
|
||||||
|
A5CA378D2D31D6C100931030 /* Weak.swift */,
|
||||||
C1F26EE72B76CBFC00404083 /* VibrantLayer.h */,
|
C1F26EE72B76CBFC00404083 /* VibrantLayer.h */,
|
||||||
C1F26EE82B76CBFC00404083 /* VibrantLayer.m */,
|
C1F26EE82B76CBFC00404083 /* VibrantLayer.m */,
|
||||||
A5CEAFDA29B8005900646FDA /* SplitView */,
|
A5CEAFDA29B8005900646FDA /* SplitView */,
|
||||||
@@ -351,12 +372,14 @@
|
|||||||
A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */,
|
A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */,
|
||||||
A514C8D52B54A16400493A16 /* Ghostty.Config.swift */,
|
A514C8D52B54A16400493A16 /* Ghostty.Config.swift */,
|
||||||
A53A6C022CCC1B7D00943E98 /* Ghostty.Action.swift */,
|
A53A6C022CCC1B7D00943E98 /* Ghostty.Action.swift */,
|
||||||
|
A5CF66D62D29DDB100139794 /* Ghostty.Event.swift */,
|
||||||
A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */,
|
A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */,
|
||||||
A56D58852ACDDB4100508D2C /* Ghostty.Shell.swift */,
|
A56D58852ACDDB4100508D2C /* Ghostty.Shell.swift */,
|
||||||
A59630A32AF059BB00D64628 /* Ghostty.SplitNode.swift */,
|
A59630A32AF059BB00D64628 /* Ghostty.SplitNode.swift */,
|
||||||
A59630A12AF0415000D64628 /* Ghostty.TerminalSplit.swift */,
|
A59630A12AF0415000D64628 /* Ghostty.TerminalSplit.swift */,
|
||||||
A55685DF29A03A9F004303CE /* AppError.swift */,
|
A55685DF29A03A9F004303CE /* AppError.swift */,
|
||||||
A52FFF5A2CAA54A8000C6A5B /* FullscreenMode+Extension.swift */,
|
A52FFF5A2CAA54A8000C6A5B /* FullscreenMode+Extension.swift */,
|
||||||
|
A5CF66D32D289CEA00139794 /* NSEvent+Extension.swift */,
|
||||||
);
|
);
|
||||||
path = Ghostty;
|
path = Ghostty;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -399,13 +422,13 @@
|
|||||||
children = (
|
children = (
|
||||||
FC9ABA9B2D0F538D0020D4C8 /* bash-completion */,
|
FC9ABA9B2D0F538D0020D4C8 /* bash-completion */,
|
||||||
29C15B1C2CDC3B2000520DD4 /* bat */,
|
29C15B1C2CDC3B2000520DD4 /* bat */,
|
||||||
55154BDF2B33911F001622DC /* ghostty */,
|
|
||||||
552964E52B34A9B400030505 /* vim */,
|
|
||||||
A586167B2B7703CC009BDB1D /* fish */,
|
A586167B2B7703CC009BDB1D /* fish */,
|
||||||
|
55154BDF2B33911F001622DC /* ghostty */,
|
||||||
A5985CE52C33060F00C57AD3 /* man */,
|
A5985CE52C33060F00C57AD3 /* man */,
|
||||||
A5A1F8842A489D6800D1E8BC /* terminfo */,
|
|
||||||
FC5218F92D10FFC7004C93E0 /* zsh */,
|
|
||||||
9351BE8E2D22937F003B3499 /* nvim */,
|
9351BE8E2D22937F003B3499 /* nvim */,
|
||||||
|
A5A1F8842A489D6800D1E8BC /* terminfo */,
|
||||||
|
552964E52B34A9B400030505 /* vim */,
|
||||||
|
FC5218F92D10FFC7004C93E0 /* zsh */,
|
||||||
);
|
);
|
||||||
name = Resources;
|
name = Resources;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -439,6 +462,7 @@
|
|||||||
children = (
|
children = (
|
||||||
A5CBD05B2CA0C5C70017A1AE /* QuickTerminal.xib */,
|
A5CBD05B2CA0C5C70017A1AE /* QuickTerminal.xib */,
|
||||||
A5CBD05D2CA0C5E70017A1AE /* QuickTerminalController.swift */,
|
A5CBD05D2CA0C5E70017A1AE /* QuickTerminalController.swift */,
|
||||||
|
CFBB5FE92D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift */,
|
||||||
A5CBD0632CA122E70017A1AE /* QuickTerminalPosition.swift */,
|
A5CBD0632CA122E70017A1AE /* QuickTerminalPosition.swift */,
|
||||||
A52FFF562CA90481000C6A5B /* QuickTerminalScreen.swift */,
|
A52FFF562CA90481000C6A5B /* QuickTerminalScreen.swift */,
|
||||||
A5CBD05F2CA0C9080017A1AE /* QuickTerminalWindow.swift */,
|
A5CBD05F2CA0C9080017A1AE /* QuickTerminalWindow.swift */,
|
||||||
@@ -602,21 +626,26 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
A5AEB1652D5BE7D000513529 /* LastWindowPosition.swift in Sources */,
|
||||||
A59630A42AF059BB00D64628 /* Ghostty.SplitNode.swift in Sources */,
|
A59630A42AF059BB00D64628 /* Ghostty.SplitNode.swift in Sources */,
|
||||||
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */,
|
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */,
|
||||||
A54B0CEB2D0CFB4C00CBEFF8 /* NSImage+Extension.swift in Sources */,
|
A54B0CEB2D0CFB4C00CBEFF8 /* NSImage+Extension.swift in Sources */,
|
||||||
A54D786C2CA7978E001B19B1 /* BaseTerminalController.swift in Sources */,
|
A54D786C2CA7978E001B19B1 /* BaseTerminalController.swift in Sources */,
|
||||||
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */,
|
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */,
|
||||||
|
CFBB5FEA2D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift in Sources */,
|
||||||
A54B0CE92D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift in Sources */,
|
A54B0CE92D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift in Sources */,
|
||||||
A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */,
|
A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */,
|
||||||
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */,
|
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */,
|
||||||
C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */,
|
C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */,
|
||||||
|
A5CF66D42D289CEE00139794 /* NSEvent+Extension.swift in Sources */,
|
||||||
A5CBD0642CA122E70017A1AE /* QuickTerminalPosition.swift in Sources */,
|
A5CBD0642CA122E70017A1AE /* QuickTerminalPosition.swift in Sources */,
|
||||||
A596309C2AEE1C9E00D64628 /* TerminalController.swift in Sources */,
|
A596309C2AEE1C9E00D64628 /* TerminalController.swift in Sources */,
|
||||||
A5CC36152C9CDA06004D6760 /* View+Extension.swift in Sources */,
|
A5CC36152C9CDA06004D6760 /* View+Extension.swift in Sources */,
|
||||||
A56D58892ACDE6CA00508D2C /* ServiceProvider.swift in Sources */,
|
A56D58892ACDE6CA00508D2C /* ServiceProvider.swift in Sources */,
|
||||||
A5CBD0602CA0C90A0017A1AE /* QuickTerminalWindow.swift in Sources */,
|
A5CBD0602CA0C90A0017A1AE /* QuickTerminalWindow.swift in Sources */,
|
||||||
A5CBD05E2CA0C5EC0017A1AE /* QuickTerminalController.swift in Sources */,
|
A5CBD05E2CA0C5EC0017A1AE /* QuickTerminalController.swift in Sources */,
|
||||||
|
A5CF66D72D29DDB500139794 /* Ghostty.Event.swift in Sources */,
|
||||||
|
A5A2A3CA2D4445E30033CF96 /* Dock.swift in Sources */,
|
||||||
A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */,
|
A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */,
|
||||||
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */,
|
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */,
|
||||||
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */,
|
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */,
|
||||||
@@ -632,12 +661,14 @@
|
|||||||
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */,
|
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */,
|
||||||
A52FFF5B2CAA54B1000C6A5B /* FullscreenMode+Extension.swift in Sources */,
|
A52FFF5B2CAA54B1000C6A5B /* FullscreenMode+Extension.swift in Sources */,
|
||||||
A5333E222B5A2128008AEFF7 /* SurfaceView_AppKit.swift in Sources */,
|
A5333E222B5A2128008AEFF7 /* SurfaceView_AppKit.swift in Sources */,
|
||||||
|
A5CA378E2D31D6C300931030 /* Weak.swift in Sources */,
|
||||||
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */,
|
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */,
|
||||||
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */,
|
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */,
|
||||||
A5333E1C2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */,
|
A5333E1C2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */,
|
||||||
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */,
|
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */,
|
||||||
A56D58862ACDDB4100508D2C /* Ghostty.Shell.swift in Sources */,
|
A56D58862ACDDB4100508D2C /* Ghostty.Shell.swift in Sources */,
|
||||||
A5985CD72C320C4500C57AD3 /* String+Extension.swift in Sources */,
|
A5985CD72C320C4500C57AD3 /* String+Extension.swift in Sources */,
|
||||||
|
A5A2A3CC2D444ABB0033CF96 /* NSApplication+Extension.swift in Sources */,
|
||||||
A59630A22AF0415000D64628 /* Ghostty.TerminalSplit.swift in Sources */,
|
A59630A22AF0415000D64628 /* Ghostty.TerminalSplit.swift in Sources */,
|
||||||
A5FEB3002ABB69450068369E /* main.swift in Sources */,
|
A5FEB3002ABB69450068369E /* main.swift in Sources */,
|
||||||
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */,
|
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */,
|
||||||
@@ -647,6 +678,7 @@
|
|||||||
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
||||||
A53A6C032CCC1B7F00943E98 /* Ghostty.Action.swift in Sources */,
|
A53A6C032CCC1B7F00943E98 /* Ghostty.Action.swift in Sources */,
|
||||||
A54B0CED2D0CFB7700CBEFF8 /* ColorizedGhosttyIcon.swift in Sources */,
|
A54B0CED2D0CFB7700CBEFF8 /* ColorizedGhosttyIcon.swift in Sources */,
|
||||||
|
A5CA378C2D2A4DEB00931030 /* KeyboardLayout.swift in Sources */,
|
||||||
A54B0CEF2D0D2E2800CBEFF8 /* ColorizedGhosttyIconImage.swift in Sources */,
|
A54B0CEF2D0D2E2800CBEFF8 /* ColorizedGhosttyIconImage.swift in Sources */,
|
||||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
||||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
||||||
@@ -765,21 +797,22 @@
|
|||||||
INFOPLIST_FILE = "Ghostty-Info.plist";
|
INFOPLIST_FILE = "Ghostty-Info.plist";
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Ghostty;
|
INFOPLIST_KEY_CFBundleDisplayName = Ghostty;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||||
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program in Ghostty wants to use AppleScript.";
|
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program running within Ghostty would like to use AppleScript.";
|
||||||
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program in Ghostty wants to use your calendar.";
|
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "A program running within Ghostty would like to use Bluetooth.";
|
||||||
INFOPLIST_KEY_NSCameraUsageDescription = "A program in Ghostty wants to use the camera.";
|
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program running within Ghostty would like to access your Calendar.";
|
||||||
INFOPLIST_KEY_NSContactsUsageDescription = "A program in Ghostty wants to use your contacts.";
|
INFOPLIST_KEY_NSCameraUsageDescription = "A program running within Ghostty would like to use the camera.";
|
||||||
|
INFOPLIST_KEY_NSContactsUsageDescription = "A program running within Ghostty would like to access your Contacts.";
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "A program in Ghostty wants to access the local network.";
|
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "A program running within Ghostty would like to access the local network.";
|
||||||
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program in Ghostty wants to use your location temporarily.";
|
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program running within Ghostty would like to use your location temporarily.";
|
||||||
INFOPLIST_KEY_NSLocationUsageDescription = "A program in Ghostty wants to use your location information.";
|
INFOPLIST_KEY_NSLocationUsageDescription = "A program running within Ghostty would like to access your location information.";
|
||||||
INFOPLIST_KEY_NSMainNibFile = MainMenu;
|
INFOPLIST_KEY_NSMainNibFile = MainMenu;
|
||||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program in Ghostty wants to use your microphone.";
|
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program running within Ghostty would like to use your microphone.";
|
||||||
INFOPLIST_KEY_NSMotionUsageDescription = "A program in Ghostty wants to access motion data.";
|
INFOPLIST_KEY_NSMotionUsageDescription = "A program running within Ghostty would like to access motion data.";
|
||||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program in Ghostty wants to use your photo library.";
|
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program running within Ghostty would like to access your Photo Library.";
|
||||||
INFOPLIST_KEY_NSRemindersUsageDescription = "A program in Ghostty wants to access your reminders.";
|
INFOPLIST_KEY_NSRemindersUsageDescription = "A program running within Ghostty would like to access your reminders.";
|
||||||
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program in Ghostty wants to use speech recognition.";
|
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program running within Ghostty would like to use speech recognition.";
|
||||||
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program in Ghostty requires elevated privileges.";
|
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program running within Ghostty requires elevated privileges.";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
@@ -934,21 +967,22 @@
|
|||||||
INFOPLIST_FILE = "Ghostty-Info.plist";
|
INFOPLIST_FILE = "Ghostty-Info.plist";
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Ghostty;
|
INFOPLIST_KEY_CFBundleDisplayName = Ghostty;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||||
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program in Ghostty wants to use AppleScript.";
|
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program running within Ghostty would like to use AppleScript.";
|
||||||
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program in Ghostty wants to use your calendar.";
|
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "A program running within Ghostty would like to use Bluetooth.";
|
||||||
INFOPLIST_KEY_NSCameraUsageDescription = "A program in Ghostty wants to use the camera.";
|
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program running within Ghostty would like to access your Calendar.";
|
||||||
INFOPLIST_KEY_NSContactsUsageDescription = "A program in Ghostty wants to use your contacts.";
|
INFOPLIST_KEY_NSCameraUsageDescription = "A program running within Ghostty would like to use the camera.";
|
||||||
|
INFOPLIST_KEY_NSContactsUsageDescription = "A program running within Ghostty would like to access your Contacts.";
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "A program in Ghostty wants to access the local network.";
|
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "A program running within Ghostty would like to access the local network.";
|
||||||
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program in Ghostty wants to use your location temporarily.";
|
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program running within Ghostty would like to use your location temporarily.";
|
||||||
INFOPLIST_KEY_NSLocationUsageDescription = "A program in Ghostty wants to use your location information.";
|
INFOPLIST_KEY_NSLocationUsageDescription = "A program running within Ghostty would like to access your location information.";
|
||||||
INFOPLIST_KEY_NSMainNibFile = MainMenu;
|
INFOPLIST_KEY_NSMainNibFile = MainMenu;
|
||||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program in Ghostty wants to use your microphone.";
|
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program running within Ghostty would like to use your microphone.";
|
||||||
INFOPLIST_KEY_NSMotionUsageDescription = "A program in Ghostty wants to access motion data.";
|
INFOPLIST_KEY_NSMotionUsageDescription = "A program running within Ghostty would like to access motion data.";
|
||||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program in Ghostty wants to use your photo library.";
|
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program running within Ghostty would like to access your Photo Library.";
|
||||||
INFOPLIST_KEY_NSRemindersUsageDescription = "A program in Ghostty wants to access your reminders.";
|
INFOPLIST_KEY_NSRemindersUsageDescription = "A program running within Ghostty would like to access your reminders.";
|
||||||
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program in Ghostty wants to use speech recognition.";
|
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program running within Ghostty would like to use speech recognition.";
|
||||||
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program in Ghostty requires elevated privileges.";
|
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program running within Ghostty requires elevated privileges.";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
@@ -987,21 +1021,22 @@
|
|||||||
INFOPLIST_FILE = "Ghostty-Info.plist";
|
INFOPLIST_FILE = "Ghostty-Info.plist";
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = Ghostty;
|
INFOPLIST_KEY_CFBundleDisplayName = Ghostty;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||||
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program in Ghostty wants to use AppleScript.";
|
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program running within Ghostty would like to use AppleScript.";
|
||||||
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program in Ghostty wants to use your calendar.";
|
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "A program running within Ghostty would like to use Bluetooth.";
|
||||||
INFOPLIST_KEY_NSCameraUsageDescription = "A program in Ghostty wants to use the camera.";
|
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program running within Ghostty would like to access your Calendar.";
|
||||||
INFOPLIST_KEY_NSContactsUsageDescription = "A program in Ghostty wants to use your contacts.";
|
INFOPLIST_KEY_NSCameraUsageDescription = "A program running within Ghostty would like to use the camera.";
|
||||||
|
INFOPLIST_KEY_NSContactsUsageDescription = "A program running within Ghostty would like to access your Contacts.";
|
||||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "A program in Ghostty wants to access the local network.";
|
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "A program running within Ghostty would like to access the local network.";
|
||||||
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program in Ghostty wants to use your location temporarily.";
|
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program running within Ghostty would like to use your location temporarily.";
|
||||||
INFOPLIST_KEY_NSLocationUsageDescription = "A program in Ghostty wants to use your location information.";
|
INFOPLIST_KEY_NSLocationUsageDescription = "A program running within Ghostty would like to access your location information.";
|
||||||
INFOPLIST_KEY_NSMainNibFile = MainMenu;
|
INFOPLIST_KEY_NSMainNibFile = MainMenu;
|
||||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program in Ghostty wants to use your microphone.";
|
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program running within Ghostty would like to use your microphone.";
|
||||||
INFOPLIST_KEY_NSMotionUsageDescription = "A program in Ghostty wants to access motion data.";
|
INFOPLIST_KEY_NSMotionUsageDescription = "A program running within Ghostty would like to access motion data.";
|
||||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program in Ghostty wants to use your photo library.";
|
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program running within Ghostty would like to access your Photo Library.";
|
||||||
INFOPLIST_KEY_NSRemindersUsageDescription = "A program in Ghostty wants to access your reminders.";
|
INFOPLIST_KEY_NSRemindersUsageDescription = "A program running within Ghostty would like to access your reminders.";
|
||||||
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program in Ghostty wants to use speech recognition.";
|
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program running within Ghostty would like to use speech recognition.";
|
||||||
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program in Ghostty requires elevated privileges.";
|
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program running within Ghostty requires elevated privileges.";
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
|
@@ -6,8 +6,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/sparkle-project/Sparkle",
|
"location" : "https://github.com/sparkle-project/Sparkle",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "b456fd404954a9e13f55aa0c88cd5a40b8399638",
|
"revision" : "0ef1ee0220239b3776f433314515fd849025673f",
|
||||||
"version" : "2.6.3"
|
"version" : "2.6.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@@ -30,11 +30,13 @@ class AppDelegate: NSObject,
|
|||||||
@IBOutlet private var menuSplitRight: NSMenuItem?
|
@IBOutlet private var menuSplitRight: NSMenuItem?
|
||||||
@IBOutlet private var menuSplitDown: NSMenuItem?
|
@IBOutlet private var menuSplitDown: NSMenuItem?
|
||||||
@IBOutlet private var menuClose: NSMenuItem?
|
@IBOutlet private var menuClose: NSMenuItem?
|
||||||
|
@IBOutlet private var menuCloseTab: NSMenuItem?
|
||||||
@IBOutlet private var menuCloseWindow: NSMenuItem?
|
@IBOutlet private var menuCloseWindow: NSMenuItem?
|
||||||
@IBOutlet private var menuCloseAllWindows: NSMenuItem?
|
@IBOutlet private var menuCloseAllWindows: NSMenuItem?
|
||||||
|
|
||||||
@IBOutlet private var menuCopy: NSMenuItem?
|
@IBOutlet private var menuCopy: NSMenuItem?
|
||||||
@IBOutlet private var menuPaste: NSMenuItem?
|
@IBOutlet private var menuPaste: NSMenuItem?
|
||||||
|
@IBOutlet private var menuPasteSelection: NSMenuItem?
|
||||||
@IBOutlet private var menuSelectAll: NSMenuItem?
|
@IBOutlet private var menuSelectAll: NSMenuItem?
|
||||||
|
|
||||||
@IBOutlet private var menuToggleVisibility: NSMenuItem?
|
@IBOutlet private var menuToggleVisibility: NSMenuItem?
|
||||||
@@ -90,10 +92,8 @@ class AppDelegate: NSObject,
|
|||||||
return ProcessInfo.processInfo.systemUptime - applicationLaunchTime
|
return ProcessInfo.processInfo.systemUptime - applicationLaunchTime
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tracks whether the application is currently visible. This can be gamed, i.e. if a user manually
|
/// Tracks the windows that we hid for toggleVisibility.
|
||||||
/// brings each window one by one to the front. But at worst its off by one set of toggles and this
|
private var hiddenState: ToggleVisibilityState? = nil
|
||||||
/// makes our logic very easy.
|
|
||||||
private var isVisible: Bool = true
|
|
||||||
|
|
||||||
/// The observer for the app appearance.
|
/// The observer for the app appearance.
|
||||||
private var appearanceObserver: NSKeyValueObservation? = nil
|
private var appearanceObserver: NSKeyValueObservation? = nil
|
||||||
@@ -217,15 +217,20 @@ class AppDelegate: NSObject,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func applicationDidBecomeActive(_ notification: Notification) {
|
func applicationDidBecomeActive(_ notification: Notification) {
|
||||||
guard !applicationHasBecomeActive else { return }
|
// If we're back manually then clear the hidden state because macOS handles it.
|
||||||
applicationHasBecomeActive = true
|
self.hiddenState = nil
|
||||||
|
|
||||||
// Let's launch our first window. We only do this if we have no other windows. It
|
// First launch stuff
|
||||||
// is possible to have other windows in a few scenarios:
|
if (!applicationHasBecomeActive) {
|
||||||
// - if we're opening a URL since `application(_:openFile:)` is called before this.
|
applicationHasBecomeActive = true
|
||||||
// - if we're restoring from persisted state
|
|
||||||
if terminalManager.windows.count == 0 && derivedConfig.initialWindow {
|
// Let's launch our first window. We only do this if we have no other windows. It
|
||||||
terminalManager.newWindow()
|
// is possible to have other windows in a few scenarios:
|
||||||
|
// - if we're opening a URL since `application(_:openFile:)` is called before this.
|
||||||
|
// - if we're restoring from persisted state
|
||||||
|
if terminalManager.windows.count == 0 && derivedConfig.initialWindow {
|
||||||
|
terminalManager.newWindow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,7 +245,13 @@ class AppDelegate: NSObject,
|
|||||||
// This probably isn't fully safe. The isEmpty check above is aspirational, it doesn't
|
// This probably isn't fully safe. The isEmpty check above is aspirational, it doesn't
|
||||||
// quite work with SwiftUI because windows are retained on close. So instead we check
|
// quite work with SwiftUI because windows are retained on close. So instead we check
|
||||||
// if there are any that are visible. I'm guessing this breaks under certain scenarios.
|
// if there are any that are visible. I'm guessing this breaks under certain scenarios.
|
||||||
if (windows.allSatisfy { !$0.isVisible }) { return .terminateNow }
|
//
|
||||||
|
// NOTE(mitchellh): I don't think we need this check at all anymore. I'm keeping it
|
||||||
|
// here because I don't want to remove it in a patch release cycle but we should
|
||||||
|
// target removing it soon.
|
||||||
|
if (self.quickController == nil && windows.allSatisfy { !$0.isVisible }) {
|
||||||
|
return .terminateNow
|
||||||
|
}
|
||||||
|
|
||||||
// If the user is shutting down, restarting, or logging out, we don't confirm quit.
|
// If the user is shutting down, restarting, or logging out, we don't confirm quit.
|
||||||
why: if let event = NSAppleEventManager.shared().currentAppleEvent {
|
why: if let event = NSAppleEventManager.shared().currentAppleEvent {
|
||||||
@@ -346,6 +357,7 @@ class AppDelegate: NSObject,
|
|||||||
syncMenuShortcut(config, action: "new_window", menuItem: self.menuNewWindow)
|
syncMenuShortcut(config, action: "new_window", menuItem: self.menuNewWindow)
|
||||||
syncMenuShortcut(config, action: "new_tab", menuItem: self.menuNewTab)
|
syncMenuShortcut(config, action: "new_tab", menuItem: self.menuNewTab)
|
||||||
syncMenuShortcut(config, action: "close_surface", menuItem: self.menuClose)
|
syncMenuShortcut(config, action: "close_surface", menuItem: self.menuClose)
|
||||||
|
syncMenuShortcut(config, action: "close_tab", menuItem: self.menuCloseTab)
|
||||||
syncMenuShortcut(config, action: "close_window", menuItem: self.menuCloseWindow)
|
syncMenuShortcut(config, action: "close_window", menuItem: self.menuCloseWindow)
|
||||||
syncMenuShortcut(config, action: "close_all_windows", menuItem: self.menuCloseAllWindows)
|
syncMenuShortcut(config, action: "close_all_windows", menuItem: self.menuCloseAllWindows)
|
||||||
syncMenuShortcut(config, action: "new_split:right", menuItem: self.menuSplitRight)
|
syncMenuShortcut(config, action: "new_split:right", menuItem: self.menuSplitRight)
|
||||||
@@ -353,13 +365,14 @@ class AppDelegate: NSObject,
|
|||||||
|
|
||||||
syncMenuShortcut(config, action: "copy_to_clipboard", menuItem: self.menuCopy)
|
syncMenuShortcut(config, action: "copy_to_clipboard", menuItem: self.menuCopy)
|
||||||
syncMenuShortcut(config, action: "paste_from_clipboard", menuItem: self.menuPaste)
|
syncMenuShortcut(config, action: "paste_from_clipboard", menuItem: self.menuPaste)
|
||||||
|
syncMenuShortcut(config, action: "paste_from_selection", menuItem: self.menuPasteSelection)
|
||||||
syncMenuShortcut(config, action: "select_all", menuItem: self.menuSelectAll)
|
syncMenuShortcut(config, action: "select_all", menuItem: self.menuSelectAll)
|
||||||
|
|
||||||
syncMenuShortcut(config, action: "toggle_split_zoom", menuItem: self.menuZoomSplit)
|
syncMenuShortcut(config, action: "toggle_split_zoom", menuItem: self.menuZoomSplit)
|
||||||
syncMenuShortcut(config, action: "goto_split:previous", menuItem: self.menuPreviousSplit)
|
syncMenuShortcut(config, action: "goto_split:previous", menuItem: self.menuPreviousSplit)
|
||||||
syncMenuShortcut(config, action: "goto_split:next", menuItem: self.menuNextSplit)
|
syncMenuShortcut(config, action: "goto_split:next", menuItem: self.menuNextSplit)
|
||||||
syncMenuShortcut(config, action: "goto_split:top", menuItem: self.menuSelectSplitAbove)
|
syncMenuShortcut(config, action: "goto_split:up", menuItem: self.menuSelectSplitAbove)
|
||||||
syncMenuShortcut(config, action: "goto_split:bottom", menuItem: self.menuSelectSplitBelow)
|
syncMenuShortcut(config, action: "goto_split:down", menuItem: self.menuSelectSplitBelow)
|
||||||
syncMenuShortcut(config, action: "goto_split:left", menuItem: self.menuSelectSplitLeft)
|
syncMenuShortcut(config, action: "goto_split:left", menuItem: self.menuSelectSplitLeft)
|
||||||
syncMenuShortcut(config, action: "goto_split:right", menuItem: self.menuSelectSplitRight)
|
syncMenuShortcut(config, action: "goto_split:right", menuItem: self.menuSelectSplitRight)
|
||||||
syncMenuShortcut(config, action: "resize_split:up,10", menuItem: self.menuMoveSplitDividerUp)
|
syncMenuShortcut(config, action: "resize_split:up,10", menuItem: self.menuMoveSplitDividerUp)
|
||||||
@@ -425,6 +438,22 @@ class AppDelegate: NSObject,
|
|||||||
// because we let it capture and propagate.
|
// because we let it capture and propagate.
|
||||||
guard NSApp.mainWindow == nil else { return event }
|
guard NSApp.mainWindow == nil else { return event }
|
||||||
|
|
||||||
|
// If this event as-is would result in a key binding then we send it.
|
||||||
|
if let app = ghostty.app,
|
||||||
|
ghostty_app_key_is_binding(
|
||||||
|
app,
|
||||||
|
event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS)) {
|
||||||
|
// If the key was handled by Ghostty we stop the event chain. If
|
||||||
|
// the key wasn't handled then we let it fall through and continue
|
||||||
|
// processing. This is important because some bindings may have no
|
||||||
|
// affect at this scope.
|
||||||
|
if (ghostty_app_key(
|
||||||
|
app,
|
||||||
|
event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS))) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If this event would be handled by our menu then we do nothing.
|
// If this event would be handled by our menu then we do nothing.
|
||||||
if let mainMenu = NSApp.mainMenu,
|
if let mainMenu = NSApp.mainMenu,
|
||||||
mainMenu.performKeyEquivalent(with: event) {
|
mainMenu.performKeyEquivalent(with: event) {
|
||||||
@@ -438,13 +467,7 @@ class AppDelegate: NSObject,
|
|||||||
guard let ghostty = self.ghostty.app else { return event }
|
guard let ghostty = self.ghostty.app else { return event }
|
||||||
|
|
||||||
// Build our event input and call ghostty
|
// Build our event input and call ghostty
|
||||||
var key_ev = ghostty_input_key_s()
|
if (ghostty_app_key(ghostty, event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS))) {
|
||||||
key_ev.action = GHOSTTY_ACTION_PRESS
|
|
||||||
key_ev.mods = Ghostty.ghosttyMods(event.modifierFlags)
|
|
||||||
key_ev.keycode = UInt32(event.keyCode)
|
|
||||||
key_ev.text = nil
|
|
||||||
key_ev.composing = false
|
|
||||||
if (ghostty_app_key(ghostty, key_ev)) {
|
|
||||||
// The key was used so we want to stop it from going to our Mac app
|
// The key was used so we want to stop it from going to our Mac app
|
||||||
Ghostty.logger.debug("local key event handled event=\(event)")
|
Ghostty.logger.debug("local key event handled event=\(event)")
|
||||||
return nil
|
return nil
|
||||||
@@ -486,15 +509,16 @@ class AppDelegate: NSObject,
|
|||||||
|
|
||||||
// Sync our auto-update settings. If SUEnableAutomaticChecks (in our Info.plist) is
|
// Sync our auto-update settings. If SUEnableAutomaticChecks (in our Info.plist) is
|
||||||
// explicitly false (NO), auto-updates are disabled. Otherwise, we use the behavior
|
// explicitly false (NO), auto-updates are disabled. Otherwise, we use the behavior
|
||||||
// defined by our "auto-update" configuration.
|
// defined by our "auto-update" configuration (if set) or fall back to Sparkle
|
||||||
if Bundle.main.infoDictionary?["SUEnableAutomaticChecks"] as? Bool != false {
|
// user-based defaults.
|
||||||
updaterController.updater.automaticallyChecksForUpdates =
|
if Bundle.main.infoDictionary?["SUEnableAutomaticChecks"] as? Bool == false {
|
||||||
config.autoUpdate == .check || config.autoUpdate == .download
|
|
||||||
updaterController.updater.automaticallyDownloadsUpdates =
|
|
||||||
config.autoUpdate == .download
|
|
||||||
} else {
|
|
||||||
updaterController.updater.automaticallyChecksForUpdates = false
|
updaterController.updater.automaticallyChecksForUpdates = false
|
||||||
updaterController.updater.automaticallyDownloadsUpdates = false
|
updaterController.updater.automaticallyDownloadsUpdates = false
|
||||||
|
} else if let autoUpdate = config.autoUpdate {
|
||||||
|
updaterController.updater.automaticallyChecksForUpdates =
|
||||||
|
autoUpdate == .check || autoUpdate == .download
|
||||||
|
updaterController.updater.automaticallyDownloadsUpdates =
|
||||||
|
autoUpdate == .download
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config could change keybindings, so update everything that depends on that
|
// Config could change keybindings, so update everything that depends on that
|
||||||
@@ -539,6 +563,30 @@ class AppDelegate: NSObject,
|
|||||||
self.appIcon = nil
|
self.appIcon = nil
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case .blueprint:
|
||||||
|
self.appIcon = NSImage(named: "BlueprintImage")!
|
||||||
|
|
||||||
|
case .chalkboard:
|
||||||
|
self.appIcon = NSImage(named: "ChalkboardImage")!
|
||||||
|
|
||||||
|
case .glass:
|
||||||
|
self.appIcon = NSImage(named: "GlassImage")!
|
||||||
|
|
||||||
|
case .holographic:
|
||||||
|
self.appIcon = NSImage(named: "HolographicImage")!
|
||||||
|
|
||||||
|
case .microchip:
|
||||||
|
self.appIcon = NSImage(named: "MicrochipImage")!
|
||||||
|
|
||||||
|
case .paper:
|
||||||
|
self.appIcon = NSImage(named: "PaperImage")!
|
||||||
|
|
||||||
|
case .retro:
|
||||||
|
self.appIcon = NSImage(named: "RetroImage")!
|
||||||
|
|
||||||
|
case .xray:
|
||||||
|
self.appIcon = NSImage(named: "XrayImage")!
|
||||||
|
|
||||||
case .customStyle:
|
case .customStyle:
|
||||||
guard let ghostColor = config.macosIconGhostColor else { break }
|
guard let ghostColor = config.macosIconGhostColor else { break }
|
||||||
guard let screenColors = config.macosIconScreenColor else { break }
|
guard let screenColors = config.macosIconScreenColor else { break }
|
||||||
@@ -691,21 +739,27 @@ class AppDelegate: NSObject,
|
|||||||
|
|
||||||
/// Toggles visibility of all Ghosty Terminal windows. When hidden, activates Ghostty as the frontmost application
|
/// Toggles visibility of all Ghosty Terminal windows. When hidden, activates Ghostty as the frontmost application
|
||||||
@IBAction func toggleVisibility(_ sender: Any) {
|
@IBAction func toggleVisibility(_ sender: Any) {
|
||||||
// We only care about terminal windows.
|
// If we have focus, then we hide all windows.
|
||||||
for window in NSApp.windows.filter({ $0.windowController is BaseTerminalController }) {
|
if NSApp.isActive {
|
||||||
if isVisible {
|
// Toggle visibility doesn't do anything if the focused window is native
|
||||||
window.orderOut(nil)
|
// fullscreen. This is only relevant if Ghostty is active.
|
||||||
} else {
|
guard let keyWindow = NSApp.keyWindow,
|
||||||
window.makeKeyAndOrderFront(nil)
|
!keyWindow.styleMask.contains(.fullScreen) else { return }
|
||||||
}
|
|
||||||
|
// Keep track of our hidden state to restore properly
|
||||||
|
self.hiddenState = .init()
|
||||||
|
NSApp.hide(nil)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// After bringing them all to front we make sure our app is active too.
|
// If we're not active, we want to become active
|
||||||
if !isVisible {
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
NSApp.activate(ignoringOtherApps: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
isVisible.toggle()
|
// Bring all windows to the front. Note: we don't use NSApp.unhide because
|
||||||
|
// that will unhide ALL hidden windows. We want to only bring forward the
|
||||||
|
// ones that we hid.
|
||||||
|
hiddenState?.restore()
|
||||||
|
hiddenState = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct DerivedConfig {
|
private struct DerivedConfig {
|
||||||
@@ -725,4 +779,33 @@ class AppDelegate: NSObject,
|
|||||||
self.quickTerminalPosition = config.quickTerminalPosition
|
self.quickTerminalPosition = config.quickTerminalPosition
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct ToggleVisibilityState {
|
||||||
|
let hiddenWindows: [Weak<NSWindow>]
|
||||||
|
let keyWindow: Weak<NSWindow>?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
// We need to know the key window so that we can bring focus back to the
|
||||||
|
// right window if it was hidden.
|
||||||
|
self.keyWindow = if let keyWindow = NSApp.keyWindow {
|
||||||
|
.init(keyWindow)
|
||||||
|
} else {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to keep track of the windows that were visible because we only
|
||||||
|
// want to bring back these windows if we remove the toggle.
|
||||||
|
//
|
||||||
|
// We also ignore fullscreen windows because they don't hide anyways.
|
||||||
|
self.hiddenWindows = NSApp.windows.filter {
|
||||||
|
$0.isVisible &&
|
||||||
|
!$0.styleMask.contains(.fullScreen)
|
||||||
|
}.map { Weak($0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
func restore() {
|
||||||
|
hiddenWindows.forEach { $0.value?.orderFrontRegardless() }
|
||||||
|
keyWindow?.value?.makeKey()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23094" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23094"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23504"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
<outlet property="menuCheckForUpdates" destination="GEA-5y-yzH" id="0nV-Tf-nJQ"/>
|
<outlet property="menuCheckForUpdates" destination="GEA-5y-yzH" id="0nV-Tf-nJQ"/>
|
||||||
<outlet property="menuClose" destination="DVo-aG-piG" id="R3t-0C-aSU"/>
|
<outlet property="menuClose" destination="DVo-aG-piG" id="R3t-0C-aSU"/>
|
||||||
<outlet property="menuCloseAllWindows" destination="yKr-Vi-Yqw" id="Zet-Ir-zbm"/>
|
<outlet property="menuCloseAllWindows" destination="yKr-Vi-Yqw" id="Zet-Ir-zbm"/>
|
||||||
|
<outlet property="menuCloseTab" destination="Obb-Mk-j8J" id="Gda-L0-gdz"/>
|
||||||
<outlet property="menuCloseWindow" destination="W5w-UZ-crk" id="6ff-BT-ENV"/>
|
<outlet property="menuCloseWindow" destination="W5w-UZ-crk" id="6ff-BT-ENV"/>
|
||||||
<outlet property="menuCopy" destination="Jqf-pv-Zcu" id="bKd-1C-oy9"/>
|
<outlet property="menuCopy" destination="Jqf-pv-Zcu" id="bKd-1C-oy9"/>
|
||||||
<outlet property="menuDecreaseFontSize" destination="kzb-SZ-dOA" id="Y1B-Vh-6Z2"/>
|
<outlet property="menuDecreaseFontSize" destination="kzb-SZ-dOA" id="Y1B-Vh-6Z2"/>
|
||||||
@@ -31,6 +32,7 @@
|
|||||||
<outlet property="menuNextSplit" destination="bD7-ei-wKU" id="LeT-xw-eh4"/>
|
<outlet property="menuNextSplit" destination="bD7-ei-wKU" id="LeT-xw-eh4"/>
|
||||||
<outlet property="menuOpenConfig" destination="BOF-NM-1cW" id="Nze-Go-glw"/>
|
<outlet property="menuOpenConfig" destination="BOF-NM-1cW" id="Nze-Go-glw"/>
|
||||||
<outlet property="menuPaste" destination="i27-pK-umN" id="ICc-X2-gV3"/>
|
<outlet property="menuPaste" destination="i27-pK-umN" id="ICc-X2-gV3"/>
|
||||||
|
<outlet property="menuPasteSelection" destination="akq-ov-Jjh" id="GS8-aQ-hVw"/>
|
||||||
<outlet property="menuPreviousSplit" destination="Lic-px-1wg" id="Rto-CG-yRe"/>
|
<outlet property="menuPreviousSplit" destination="Lic-px-1wg" id="Rto-CG-yRe"/>
|
||||||
<outlet property="menuQuickTerminal" destination="1pv-LF-NBJ" id="glN-5B-IGi"/>
|
<outlet property="menuQuickTerminal" destination="1pv-LF-NBJ" id="glN-5B-IGi"/>
|
||||||
<outlet property="menuQuit" destination="4sb-4s-VLi" id="qYN-S1-6UW"/>
|
<outlet property="menuQuit" destination="4sb-4s-VLi" id="qYN-S1-6UW"/>
|
||||||
@@ -154,6 +156,12 @@
|
|||||||
<action selector="close:" target="-1" id="tTZ-2b-Mbm"/>
|
<action selector="close:" target="-1" id="tTZ-2b-Mbm"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
|
<menuItem title="Close Tab" id="Obb-Mk-j8J">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="closeTab:" target="-1" id="UBb-Bd-nkj"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
<menuItem title="Close Window" id="W5w-UZ-crk">
|
<menuItem title="Close Window" id="W5w-UZ-crk">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
@@ -185,6 +193,12 @@
|
|||||||
<action selector="paste:" target="-1" id="ZKe-2B-mel"/>
|
<action selector="paste:" target="-1" id="ZKe-2B-mel"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
|
<menuItem title="Paste Selection" id="akq-ov-Jjh">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="pasteSelection:" target="-1" id="vo3-Rf-Udb"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
<menuItem title="Select All" id="q2h-lq-e4r">
|
<menuItem title="Select All" id="q2h-lq-e4r">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<connections>
|
<connections>
|
||||||
|
@@ -3,6 +3,12 @@ import Cocoa
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import GhosttyKit
|
import GhosttyKit
|
||||||
|
|
||||||
|
// This is a Apple's private function that we need to call to get the active space.
|
||||||
|
@_silgen_name("CGSGetActiveSpace")
|
||||||
|
func CGSGetActiveSpace(_ cid: Int) -> size_t
|
||||||
|
@_silgen_name("CGSMainConnectionID")
|
||||||
|
func CGSMainConnectionID() -> Int
|
||||||
|
|
||||||
/// Controller for the "quick" terminal.
|
/// Controller for the "quick" terminal.
|
||||||
class QuickTerminalController: BaseTerminalController {
|
class QuickTerminalController: BaseTerminalController {
|
||||||
override var windowNibName: NSNib.Name? { "QuickTerminal" }
|
override var windowNibName: NSNib.Name? { "QuickTerminal" }
|
||||||
@@ -18,6 +24,12 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
/// application to the front.
|
/// application to the front.
|
||||||
private var previousApp: NSRunningApplication? = nil
|
private var previousApp: NSRunningApplication? = nil
|
||||||
|
|
||||||
|
// The active space when the quick terminal was last shown.
|
||||||
|
private var previousActiveSpace: size_t = 0
|
||||||
|
|
||||||
|
/// Non-nil if we have hidden dock state.
|
||||||
|
private var hiddenDock: HiddenDock? = nil
|
||||||
|
|
||||||
/// The configuration derived from the Ghostty config so we don't need to rely on references.
|
/// The configuration derived from the Ghostty config so we don't need to rely on references.
|
||||||
private var derivedConfig: DerivedConfig
|
private var derivedConfig: DerivedConfig
|
||||||
|
|
||||||
@@ -32,6 +44,11 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
|
|
||||||
// Setup our notifications for behaviors
|
// Setup our notifications for behaviors
|
||||||
let center = NotificationCenter.default
|
let center = NotificationCenter.default
|
||||||
|
center.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(applicationWillTerminate(_:)),
|
||||||
|
name: NSApplication.willTerminateNotification,
|
||||||
|
object: nil)
|
||||||
center.addObserver(
|
center.addObserver(
|
||||||
self,
|
self,
|
||||||
selector: #selector(onToggleFullscreen),
|
selector: #selector(onToggleFullscreen),
|
||||||
@@ -52,6 +69,9 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
// Remove all of our notificationcenter subscriptions
|
// Remove all of our notificationcenter subscriptions
|
||||||
let center = NotificationCenter.default
|
let center = NotificationCenter.default
|
||||||
center.removeObserver(self)
|
center.removeObserver(self)
|
||||||
|
|
||||||
|
// Make sure we restore our hidden dock
|
||||||
|
hiddenDock = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: NSWindowController
|
// MARK: NSWindowController
|
||||||
@@ -87,6 +107,17 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
|
|
||||||
// MARK: NSWindowDelegate
|
// MARK: NSWindowDelegate
|
||||||
|
|
||||||
|
override func windowDidBecomeKey(_ notification: Notification) {
|
||||||
|
super.windowDidBecomeKey(notification)
|
||||||
|
|
||||||
|
// If we're not visible we don't care to run the logic below. It only
|
||||||
|
// applies if we can be seen.
|
||||||
|
guard visible else { return }
|
||||||
|
|
||||||
|
// Re-hide the dock if we were hiding it before.
|
||||||
|
hiddenDock?.hide()
|
||||||
|
}
|
||||||
|
|
||||||
override func windowDidResignKey(_ notification: Notification) {
|
override func windowDidResignKey(_ notification: Notification) {
|
||||||
super.windowDidResignKey(notification)
|
super.windowDidResignKey(notification)
|
||||||
|
|
||||||
@@ -107,8 +138,32 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
self.previousApp = nil
|
self.previousApp = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if (derivedConfig.quickTerminalAutoHide) {
|
// Regardless of autohide, we always want to bring the dock back
|
||||||
animateOut()
|
// when we lose focus.
|
||||||
|
hiddenDock?.restore()
|
||||||
|
|
||||||
|
if derivedConfig.quickTerminalAutoHide {
|
||||||
|
switch derivedConfig.quickTerminalSpaceBehavior {
|
||||||
|
case .remain:
|
||||||
|
// If we lose focus on the active space, then we can animate out
|
||||||
|
animateOut()
|
||||||
|
|
||||||
|
case .move:
|
||||||
|
let currentActiveSpace = CGSGetActiveSpace(CGSMainConnectionID())
|
||||||
|
if previousActiveSpace == currentActiveSpace {
|
||||||
|
// We haven't moved spaces. We lost focus to another app on the
|
||||||
|
// current space. Animate out.
|
||||||
|
animateOut()
|
||||||
|
} else {
|
||||||
|
// We've moved to a different space. Bring the quick terminal back
|
||||||
|
// into view.
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.window?.makeKeyAndOrderFront(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.previousActiveSpace = currentActiveSpace
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +218,9 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set previous active space
|
||||||
|
self.previousActiveSpace = CGSGetActiveSpace(CGSMainConnectionID())
|
||||||
|
|
||||||
// Animate the window in
|
// Animate the window in
|
||||||
animateWindowIn(window: window, from: position)
|
animateWindowIn(window: window, from: position)
|
||||||
|
|
||||||
@@ -198,8 +256,29 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
// Move our window off screen to the top
|
// Move our window off screen to the top
|
||||||
position.setInitial(in: window, on: screen)
|
position.setInitial(in: window, on: screen)
|
||||||
|
|
||||||
|
// We need to set our window level to a high value. In testing, only
|
||||||
|
// popUpMenu and above do what we want. This gets it above the menu bar
|
||||||
|
// and lets us render off screen.
|
||||||
|
window.level = .popUpMenu
|
||||||
|
|
||||||
// Move it to the visible position since animation requires this
|
// Move it to the visible position since animation requires this
|
||||||
window.makeKeyAndOrderFront(nil)
|
DispatchQueue.main.async {
|
||||||
|
window.makeKeyAndOrderFront(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If our dock position would conflict with our target location then
|
||||||
|
// we autohide the dock.
|
||||||
|
if position.conflictsWithDock(on: screen) {
|
||||||
|
if (hiddenDock == nil) {
|
||||||
|
hiddenDock = .init()
|
||||||
|
}
|
||||||
|
|
||||||
|
hiddenDock?.hide()
|
||||||
|
} else {
|
||||||
|
// Ensure we don't have any hidden dock if we don't conflict.
|
||||||
|
// The deinit will restore.
|
||||||
|
hiddenDock = nil
|
||||||
|
}
|
||||||
|
|
||||||
// Run the animation that moves our window into the proper place and makes
|
// Run the animation that moves our window into the proper place and makes
|
||||||
// it visible.
|
// it visible.
|
||||||
@@ -211,8 +290,16 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
// There is a very minor delay here so waiting at least an event loop tick
|
// There is a very minor delay here so waiting at least an event loop tick
|
||||||
// keeps us safe from the view not being on the window.
|
// keeps us safe from the view not being on the window.
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
// If we canceled our animation in we do nothing
|
// If we canceled our animation clean up some state.
|
||||||
guard self.visible else { return }
|
guard self.visible else {
|
||||||
|
self.hiddenDock = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// After animating in, we reset the window level to a value that
|
||||||
|
// is above other windows but not as high as popUpMenu. This allows
|
||||||
|
// things like IME dropdowns to appear properly.
|
||||||
|
window.level = .floating
|
||||||
|
|
||||||
// Now that the window is visible, sync our appearance. This function
|
// Now that the window is visible, sync our appearance. This function
|
||||||
// requires the window is visible.
|
// requires the window is visible.
|
||||||
@@ -276,6 +363,17 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func animateWindowOut(window: NSWindow, to position: QuickTerminalPosition) {
|
private func animateWindowOut(window: NSWindow, to position: QuickTerminalPosition) {
|
||||||
|
// If we hid the dock then we unhide it.
|
||||||
|
hiddenDock = nil
|
||||||
|
|
||||||
|
// If the window isn't on our active space then we don't animate, we just
|
||||||
|
// hide it.
|
||||||
|
if !window.isOnActiveSpace {
|
||||||
|
self.previousApp = nil
|
||||||
|
window.orderOut(self)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// We always animate out to whatever screen the window is actually on.
|
// We always animate out to whatever screen the window is actually on.
|
||||||
guard let screen = window.screen ?? NSScreen.main else { return }
|
guard let screen = window.screen ?? NSScreen.main else { return }
|
||||||
|
|
||||||
@@ -297,6 +395,11 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We need to set our window level to a high value. In testing, only
|
||||||
|
// popUpMenu and above do what we want. This gets it above the menu bar
|
||||||
|
// and lets us render off screen.
|
||||||
|
window.level = .popUpMenu
|
||||||
|
|
||||||
NSAnimationContext.runAnimationGroup({ context in
|
NSAnimationContext.runAnimationGroup({ context in
|
||||||
context.duration = derivedConfig.quickTerminalAnimationDuration
|
context.duration = derivedConfig.quickTerminalAnimationDuration
|
||||||
context.timingFunction = .init(name: .easeIn)
|
context.timingFunction = .init(name: .easeIn)
|
||||||
@@ -311,23 +414,13 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
private func syncAppearance() {
|
private func syncAppearance() {
|
||||||
guard let window else { return }
|
guard let window else { return }
|
||||||
|
|
||||||
|
// Change the collection behavior of the window depending on the configuration.
|
||||||
|
window.collectionBehavior = derivedConfig.quickTerminalSpaceBehavior.collectionBehavior
|
||||||
|
|
||||||
// If our window is not visible, then no need to sync the appearance yet.
|
// If our window is not visible, then no need to sync the appearance yet.
|
||||||
// Some APIs such as window blur have no effect unless the window is visible.
|
// Some APIs such as window blur have no effect unless the window is visible.
|
||||||
guard window.isVisible else { return }
|
guard window.isVisible else { return }
|
||||||
|
|
||||||
// Terminals typically operate in sRGB color space and macOS defaults
|
|
||||||
// to "native" which is typically P3. There is a lot more resources
|
|
||||||
// covered in this GitHub issue: https://github.com/mitchellh/ghostty/pull/376
|
|
||||||
// Ghostty defaults to sRGB but this can be overridden.
|
|
||||||
switch (self.derivedConfig.windowColorspace) {
|
|
||||||
case "display-p3":
|
|
||||||
window.colorSpace = .displayP3
|
|
||||||
case "srgb":
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
window.colorSpace = .sRGB
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have window transparency then set it transparent. Otherwise set it opaque.
|
// If we have window transparency then set it transparent. Otherwise set it opaque.
|
||||||
if (self.derivedConfig.backgroundOpacity < 1) {
|
if (self.derivedConfig.backgroundOpacity < 1) {
|
||||||
window.isOpaque = false
|
window.isOpaque = false
|
||||||
@@ -368,6 +461,13 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
|
|
||||||
// MARK: Notifications
|
// MARK: Notifications
|
||||||
|
|
||||||
|
@objc private func applicationWillTerminate(_ notification: Notification) {
|
||||||
|
// If the application is going to terminate we want to make sure we
|
||||||
|
// restore any global dock state. I think deinit should be called which
|
||||||
|
// would call this anyways but I can't be sure so I will do this too.
|
||||||
|
hiddenDock = nil
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func onToggleFullscreen(notification: SwiftUI.Notification) {
|
@objc private func onToggleFullscreen(notification: SwiftUI.Notification) {
|
||||||
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||||
guard target == self.focusedSurface else { return }
|
guard target == self.focusedSurface else { return }
|
||||||
@@ -396,14 +496,14 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
let quickTerminalScreen: QuickTerminalScreen
|
let quickTerminalScreen: QuickTerminalScreen
|
||||||
let quickTerminalAnimationDuration: Double
|
let quickTerminalAnimationDuration: Double
|
||||||
let quickTerminalAutoHide: Bool
|
let quickTerminalAutoHide: Bool
|
||||||
let windowColorspace: String
|
let quickTerminalSpaceBehavior: QuickTerminalSpaceBehavior
|
||||||
let backgroundOpacity: Double
|
let backgroundOpacity: Double
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.quickTerminalScreen = .main
|
self.quickTerminalScreen = .main
|
||||||
self.quickTerminalAnimationDuration = 0.2
|
self.quickTerminalAnimationDuration = 0.2
|
||||||
self.quickTerminalAutoHide = true
|
self.quickTerminalAutoHide = true
|
||||||
self.windowColorspace = ""
|
self.quickTerminalSpaceBehavior = .move
|
||||||
self.backgroundOpacity = 1.0
|
self.backgroundOpacity = 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,10 +511,39 @@ class QuickTerminalController: BaseTerminalController {
|
|||||||
self.quickTerminalScreen = config.quickTerminalScreen
|
self.quickTerminalScreen = config.quickTerminalScreen
|
||||||
self.quickTerminalAnimationDuration = config.quickTerminalAnimationDuration
|
self.quickTerminalAnimationDuration = config.quickTerminalAnimationDuration
|
||||||
self.quickTerminalAutoHide = config.quickTerminalAutoHide
|
self.quickTerminalAutoHide = config.quickTerminalAutoHide
|
||||||
self.windowColorspace = config.windowColorspace
|
self.quickTerminalSpaceBehavior = config.quickTerminalSpaceBehavior
|
||||||
self.backgroundOpacity = config.backgroundOpacity
|
self.backgroundOpacity = config.backgroundOpacity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Hides the dock globally (not just NSApp). This is only used if the quick terminal is
|
||||||
|
/// in a conflicting position with the dock.
|
||||||
|
private class HiddenDock {
|
||||||
|
let previousAutoHide: Bool
|
||||||
|
private var hidden: Bool = false
|
||||||
|
|
||||||
|
init() {
|
||||||
|
previousAutoHide = Dock.autoHideEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
func hide() {
|
||||||
|
guard !hidden else { return }
|
||||||
|
NSApp.acquirePresentationOption(.autoHideDock)
|
||||||
|
Dock.autoHideEnabled = true
|
||||||
|
hidden = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func restore() {
|
||||||
|
guard hidden else { return }
|
||||||
|
NSApp.releasePresentationOption(.autoHideDock)
|
||||||
|
Dock.autoHideEnabled = previousAutoHide
|
||||||
|
hidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Notification.Name {
|
extension Notification.Name {
|
||||||
|
@@ -69,7 +69,7 @@ enum QuickTerminalPosition : String {
|
|||||||
finalSize.width = screen.frame.width
|
finalSize.width = screen.frame.width
|
||||||
|
|
||||||
case .left, .right:
|
case .left, .right:
|
||||||
finalSize.height = screen.frame.height
|
finalSize.height = screen.visibleFrame.height
|
||||||
|
|
||||||
case .center:
|
case .center:
|
||||||
finalSize.width = screen.frame.width / 2
|
finalSize.width = screen.frame.width / 2
|
||||||
@@ -89,13 +89,13 @@ enum QuickTerminalPosition : String {
|
|||||||
return .init(x: screen.frame.minX, y: -window.frame.height)
|
return .init(x: screen.frame.minX, y: -window.frame.height)
|
||||||
|
|
||||||
case .left:
|
case .left:
|
||||||
return .init(x: -window.frame.width, y: 0)
|
return .init(x: screen.frame.minX-window.frame.width, y: 0)
|
||||||
|
|
||||||
case .right:
|
case .right:
|
||||||
return .init(x: screen.frame.maxX, y: 0)
|
return .init(x: screen.frame.maxX, y: 0)
|
||||||
|
|
||||||
case .center:
|
case .center:
|
||||||
return .init(x: (screen.visibleFrame.maxX - window.frame.width) / 2, y: screen.visibleFrame.maxY - window.frame.width)
|
return .init(x: screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2, y: screen.visibleFrame.height - window.frame.width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +115,25 @@ enum QuickTerminalPosition : String {
|
|||||||
return .init(x: screen.visibleFrame.maxX - window.frame.width, y: window.frame.origin.y)
|
return .init(x: screen.visibleFrame.maxX - window.frame.width, y: window.frame.origin.y)
|
||||||
|
|
||||||
case .center:
|
case .center:
|
||||||
return .init(x: (screen.visibleFrame.maxX - window.frame.width) / 2, y: (screen.visibleFrame.maxY - window.frame.height) / 2)
|
return .init(x: screen.visibleFrame.origin.x + (screen.visibleFrame.width - window.frame.width) / 2, y: screen.visibleFrame.origin.y + (screen.visibleFrame.height - window.frame.height) / 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func conflictsWithDock(on screen: NSScreen) -> Bool {
|
||||||
|
// Screen must have a dock for it to conflict
|
||||||
|
guard screen.hasDock else { return false }
|
||||||
|
|
||||||
|
// Get the dock orientation for this screen
|
||||||
|
guard let orientation = Dock.orientation else { return false }
|
||||||
|
|
||||||
|
// Depending on the orientation of the dock, we conflict if our quick terminal
|
||||||
|
// would potentially "hit" the dock. In the future we should probably consider
|
||||||
|
// the frame of the quick terminal.
|
||||||
|
return switch (orientation) {
|
||||||
|
case .top: self == .top || self == .left || self == .right
|
||||||
|
case .bottom: self == .bottom || self == .left || self == .right
|
||||||
|
case .left: self == .top || self == .bottom
|
||||||
|
case .right: self == .top || self == .bottom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,36 @@
|
|||||||
|
import Foundation
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
enum QuickTerminalSpaceBehavior {
|
||||||
|
case remain
|
||||||
|
case move
|
||||||
|
|
||||||
|
init?(fromGhosttyConfig string: String) {
|
||||||
|
switch (string) {
|
||||||
|
case "move":
|
||||||
|
self = .move
|
||||||
|
|
||||||
|
case "remain":
|
||||||
|
self = .remain
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var collectionBehavior: NSWindow.CollectionBehavior {
|
||||||
|
let commonBehavior: [NSWindow.CollectionBehavior] = [
|
||||||
|
.ignoresCycle,
|
||||||
|
.fullScreenAuxiliary
|
||||||
|
]
|
||||||
|
|
||||||
|
switch (self) {
|
||||||
|
case .move:
|
||||||
|
// We want this to move the window to the active space.
|
||||||
|
return NSWindow.CollectionBehavior([.canJoinAllSpaces] + commonBehavior)
|
||||||
|
case .remain:
|
||||||
|
// We want this to remain the window in the current space.
|
||||||
|
return NSWindow.CollectionBehavior([.moveToActiveSpace] + commonBehavior)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,6 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class QuickTerminalWindow: NSWindow {
|
class QuickTerminalWindow: NSPanel {
|
||||||
// Both of these must be true for windows without decorations to be able to
|
// Both of these must be true for windows without decorations to be able to
|
||||||
// still become key/main and receive events.
|
// still become key/main and receive events.
|
||||||
override var canBecomeKey: Bool { return true }
|
override var canBecomeKey: Bool { return true }
|
||||||
@@ -26,22 +26,7 @@ class QuickTerminalWindow: NSWindow {
|
|||||||
// window remains resizable.
|
// window remains resizable.
|
||||||
self.styleMask.remove(.titled)
|
self.styleMask.remove(.titled)
|
||||||
|
|
||||||
// We need to set our window level to a high value. In testing, only
|
// We don't want to activate the owning app when quick terminal is triggered.
|
||||||
// popUpMenu and above do what we want. This gets it above the menu bar
|
self.styleMask.insert(.nonactivatingPanel)
|
||||||
// and lets us render off screen.
|
|
||||||
self.level = .popUpMenu
|
|
||||||
|
|
||||||
// This plus the level above was what was needed for the animation to work,
|
|
||||||
// because it gets the window off screen properly. Plus we add some fields
|
|
||||||
// we just want the behavior of.
|
|
||||||
self.collectionBehavior = [
|
|
||||||
// We want this to be part of every space because it is a singleton.
|
|
||||||
.canJoinAllSpaces,
|
|
||||||
|
|
||||||
// We don't want to be part of command-tilde
|
|
||||||
.ignoresCycle,
|
|
||||||
|
|
||||||
// We never support fullscreen
|
|
||||||
.fullScreenNone]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -389,9 +389,9 @@ class BaseTerminalController: NSWindowController,
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (request) {
|
switch (request) {
|
||||||
case .osc_52_write:
|
case let .osc_52_write(pasteboard):
|
||||||
guard case .confirm = action else { break }
|
guard case .confirm = action else { break }
|
||||||
let pb = NSPasteboard.general
|
let pb = pasteboard ?? NSPasteboard.general
|
||||||
pb.declareTypes([.string], owner: nil)
|
pb.declareTypes([.string], owner: nil)
|
||||||
pb.setString(cc.contents, forType: .string)
|
pb.setString(cc.contents, forType: .string)
|
||||||
case .osc_52_read, .paste:
|
case .osc_52_read, .paste:
|
||||||
@@ -452,6 +452,7 @@ class BaseTerminalController: NSWindowController,
|
|||||||
self.alert = nil
|
self.alert = nil
|
||||||
switch (response) {
|
switch (response) {
|
||||||
case .alertFirstButtonReturn:
|
case .alertFirstButtonReturn:
|
||||||
|
alert.window.orderOut(nil)
|
||||||
window.close()
|
window.close()
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -540,11 +541,11 @@ class BaseTerminalController: NSWindowController,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func splitMoveFocusAbove(_ sender: Any) {
|
@IBAction func splitMoveFocusAbove(_ sender: Any) {
|
||||||
splitMoveFocus(direction: .top)
|
splitMoveFocus(direction: .up)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func splitMoveFocusBelow(_ sender: Any) {
|
@IBAction func splitMoveFocusBelow(_ sender: Any) {
|
||||||
splitMoveFocus(direction: .bottom)
|
splitMoveFocus(direction: .down)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func splitMoveFocusLeft(_ sender: Any) {
|
@IBAction func splitMoveFocusLeft(_ sender: Any) {
|
||||||
|
@@ -22,7 +22,7 @@ class TerminalController: BaseTerminalController {
|
|||||||
private var restorable: Bool = true
|
private var restorable: Bool = true
|
||||||
|
|
||||||
/// The configuration derived from the Ghostty config so we don't need to rely on references.
|
/// The configuration derived from the Ghostty config so we don't need to rely on references.
|
||||||
private var derivedConfig: DerivedConfig
|
private(set) var derivedConfig: DerivedConfig
|
||||||
|
|
||||||
/// The notification cancellable for focused surface property changes.
|
/// The notification cancellable for focused surface property changes.
|
||||||
private var surfaceAppearanceCancellables: Set<AnyCancellable> = []
|
private var surfaceAppearanceCancellables: Set<AnyCancellable> = []
|
||||||
@@ -60,6 +60,11 @@ class TerminalController: BaseTerminalController {
|
|||||||
selector: #selector(onGotoTab),
|
selector: #selector(onGotoTab),
|
||||||
name: Ghostty.Notification.ghosttyGotoTab,
|
name: Ghostty.Notification.ghosttyGotoTab,
|
||||||
object: nil)
|
object: nil)
|
||||||
|
center.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(onCloseTab),
|
||||||
|
name: .ghosttyCloseTab,
|
||||||
|
object: nil)
|
||||||
center.addObserver(
|
center.addObserver(
|
||||||
self,
|
self,
|
||||||
selector: #selector(ghosttyConfigDidChange(_:)),
|
selector: #selector(ghosttyConfigDidChange(_:)),
|
||||||
@@ -101,6 +106,12 @@ class TerminalController: BaseTerminalController {
|
|||||||
// When our fullscreen state changes, we resync our appearance because some
|
// When our fullscreen state changes, we resync our appearance because some
|
||||||
// properties change when fullscreen or not.
|
// properties change when fullscreen or not.
|
||||||
guard let focusedSurface else { return }
|
guard let focusedSurface else { return }
|
||||||
|
if (!(fullscreenStyle?.isFullscreen ?? false) &&
|
||||||
|
ghostty.config.macosTitlebarStyle == "hidden")
|
||||||
|
{
|
||||||
|
applyHiddenTitlebarStyle()
|
||||||
|
}
|
||||||
|
|
||||||
syncAppearance(focusedSurface.derivedConfig)
|
syncAppearance(focusedSurface.derivedConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,7 +255,9 @@ class TerminalController: BaseTerminalController {
|
|||||||
let backgroundColor: OSColor
|
let backgroundColor: OSColor
|
||||||
if let surfaceTree {
|
if let surfaceTree {
|
||||||
if let focusedSurface, surfaceTree.doesBorderTop(view: focusedSurface) {
|
if let focusedSurface, surfaceTree.doesBorderTop(view: focusedSurface) {
|
||||||
backgroundColor = OSColor(focusedSurface.backgroundColor ?? surfaceConfig.backgroundColor).withAlphaComponent(0.0)
|
// Similar to above, an alpha component of "0" causes compositor issues, so
|
||||||
|
// we use 0.001. See: https://github.com/ghostty-org/ghostty/pull/4308
|
||||||
|
backgroundColor = OSColor(focusedSurface.backgroundColor ?? surfaceConfig.backgroundColor).withAlphaComponent(0.001)
|
||||||
} else {
|
} else {
|
||||||
// We don't have a focused surface or our surface doesn't border the
|
// We don't have a focused surface or our surface doesn't border the
|
||||||
// top. We choose to match the color of the top-left most surface.
|
// top. We choose to match the color of the top-left most surface.
|
||||||
@@ -267,6 +280,31 @@ class TerminalController: BaseTerminalController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func setInitialWindowPosition(x: Int16?, y: Int16?, windowDecorations: Bool) {
|
||||||
|
guard let window else { return }
|
||||||
|
|
||||||
|
// If we don't have an X/Y then we try to use the previously saved window pos.
|
||||||
|
guard let x, let y else {
|
||||||
|
if (!LastWindowPosition.shared.restore(window)) {
|
||||||
|
window.center()
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer the screen our window is being placed on otherwise our primary screen.
|
||||||
|
guard let screen = window.screen ?? NSScreen.screens.first else {
|
||||||
|
window.center()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Orient based on the top left of the primary monitor
|
||||||
|
let frame = screen.visibleFrame
|
||||||
|
window.setFrameOrigin(.init(
|
||||||
|
x: frame.minX + CGFloat(x),
|
||||||
|
y: frame.maxY - (CGFloat(y) + window.frame.height)))
|
||||||
|
}
|
||||||
|
|
||||||
//MARK: - NSWindowController
|
//MARK: - NSWindowController
|
||||||
|
|
||||||
override func windowWillLoad() {
|
override func windowWillLoad() {
|
||||||
@@ -274,6 +312,43 @@ class TerminalController: BaseTerminalController {
|
|||||||
shouldCascadeWindows = false
|
shouldCascadeWindows = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fileprivate func applyHiddenTitlebarStyle() {
|
||||||
|
guard let window else { return }
|
||||||
|
|
||||||
|
window.styleMask = [
|
||||||
|
// We need `titled` in the mask to get the normal window frame
|
||||||
|
.titled,
|
||||||
|
|
||||||
|
// Full size content view so we can extend
|
||||||
|
// content in to the hidden titlebar's area
|
||||||
|
.fullSizeContentView,
|
||||||
|
|
||||||
|
.resizable,
|
||||||
|
.closable,
|
||||||
|
.miniaturizable,
|
||||||
|
]
|
||||||
|
|
||||||
|
// Hide the title
|
||||||
|
window.titleVisibility = .hidden
|
||||||
|
window.titlebarAppearsTransparent = true
|
||||||
|
|
||||||
|
// Hide the traffic lights (window control buttons)
|
||||||
|
window.standardWindowButton(.closeButton)?.isHidden = true
|
||||||
|
window.standardWindowButton(.miniaturizeButton)?.isHidden = true
|
||||||
|
window.standardWindowButton(.zoomButton)?.isHidden = true
|
||||||
|
|
||||||
|
// Disallow tabbing if the titlebar is hidden, since that will (should) also hide the tab bar.
|
||||||
|
window.tabbingMode = .disallowed
|
||||||
|
|
||||||
|
// Nuke it from orbit -- hide the titlebar container entirely, just in case. There are
|
||||||
|
// some operations that appear to bring back the titlebar visibility so this ensures
|
||||||
|
// it is gone forever.
|
||||||
|
if let themeFrame = window.contentView?.superview,
|
||||||
|
let titleBarContainer = themeFrame.firstDescendant(withClassName: "NSTitlebarContainerView") {
|
||||||
|
titleBarContainer.isHidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override func windowDidLoad() {
|
override func windowDidLoad() {
|
||||||
super.windowDidLoad()
|
super.windowDidLoad()
|
||||||
guard let window = window as? TerminalWindow else { return }
|
guard let window = window as? TerminalWindow else { return }
|
||||||
@@ -294,40 +369,41 @@ class TerminalController: BaseTerminalController {
|
|||||||
// If window decorations are disabled, remove our title
|
// If window decorations are disabled, remove our title
|
||||||
if (!config.windowDecorations) { window.styleMask.remove(.titled) }
|
if (!config.windowDecorations) { window.styleMask.remove(.titled) }
|
||||||
|
|
||||||
// Terminals typically operate in sRGB color space and macOS defaults
|
|
||||||
// to "native" which is typically P3. There is a lot more resources
|
|
||||||
// covered in this GitHub issue: https://github.com/mitchellh/ghostty/pull/376
|
|
||||||
// Ghostty defaults to sRGB but this can be overridden.
|
|
||||||
switch (config.windowColorspace) {
|
|
||||||
case "display-p3":
|
|
||||||
window.colorSpace = .displayP3
|
|
||||||
case "srgb":
|
|
||||||
fallthrough
|
|
||||||
default:
|
|
||||||
window.colorSpace = .sRGB
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have only a single surface (no splits) and that surface requested
|
// If we have only a single surface (no splits) and that surface requested
|
||||||
// an initial size then we set it here now.
|
// an initial size then we set it here now.
|
||||||
if case let .leaf(leaf) = surfaceTree {
|
if case let .leaf(leaf) = surfaceTree {
|
||||||
if let initialSize = leaf.surface.initialSize,
|
if let initialSize = leaf.surface.initialSize,
|
||||||
let screen = window.screen ?? NSScreen.main {
|
let screen = window.screen ?? NSScreen.main {
|
||||||
// Setup our frame. We need to first subtract the views frame so that we can
|
// Get the current frame of the window
|
||||||
// just get the chrome frame so that we only affect the surface view size.
|
|
||||||
var frame = window.frame
|
var frame = window.frame
|
||||||
frame.size.width -= leaf.surface.frame.size.width
|
|
||||||
frame.size.height -= leaf.surface.frame.size.height
|
|
||||||
frame.size.width += min(initialSize.width, screen.frame.width)
|
|
||||||
frame.size.height += min(initialSize.height, screen.frame.height)
|
|
||||||
|
|
||||||
// We have no tabs and we are not a split, so set the initial size of the window.
|
// Calculate the chrome size (window size minus view size)
|
||||||
|
let chromeWidth = frame.size.width - leaf.surface.frame.size.width
|
||||||
|
let chromeHeight = frame.size.height - leaf.surface.frame.size.height
|
||||||
|
|
||||||
|
// Calculate the new width and height, clamping to the screen's size
|
||||||
|
let newWidth = min(initialSize.width + chromeWidth, screen.visibleFrame.width)
|
||||||
|
let newHeight = min(initialSize.height + chromeHeight, screen.visibleFrame.height)
|
||||||
|
|
||||||
|
// Update the frame size while keeping the window's position intact
|
||||||
|
frame.size.width = newWidth
|
||||||
|
frame.size.height = newHeight
|
||||||
|
|
||||||
|
// Ensure the window doesn't go outside the screen boundaries
|
||||||
|
frame.origin.x = max(screen.frame.origin.x, min(frame.origin.x, screen.frame.maxX - newWidth))
|
||||||
|
frame.origin.y = max(screen.frame.origin.y, min(frame.origin.y, screen.frame.maxY - newHeight))
|
||||||
|
|
||||||
|
// Set the updated frame to the window
|
||||||
window.setFrame(frame, display: true)
|
window.setFrame(frame, display: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Center the window to start, we'll move the window frame automatically
|
// Set our window positioning to coordinates if config value exists, otherwise
|
||||||
// when cascading.
|
// fallback to original centering behavior
|
||||||
window.center()
|
setInitialWindowPosition(
|
||||||
|
x: config.windowPositionX,
|
||||||
|
y: config.windowPositionY,
|
||||||
|
windowDecorations: config.windowDecorations)
|
||||||
|
|
||||||
// Make sure our theme is set on the window so styling is correct.
|
// Make sure our theme is set on the window so styling is correct.
|
||||||
if let windowTheme = config.windowTheme {
|
if let windowTheme = config.windowTheme {
|
||||||
@@ -365,38 +441,7 @@ class TerminalController: BaseTerminalController {
|
|||||||
|
|
||||||
// If our titlebar style is "hidden" we adjust the style appropriately
|
// If our titlebar style is "hidden" we adjust the style appropriately
|
||||||
if (config.macosTitlebarStyle == "hidden") {
|
if (config.macosTitlebarStyle == "hidden") {
|
||||||
window.styleMask = [
|
applyHiddenTitlebarStyle()
|
||||||
// We need `titled` in the mask to get the normal window frame
|
|
||||||
.titled,
|
|
||||||
|
|
||||||
// Full size content view so we can extend
|
|
||||||
// content in to the hidden titlebar's area
|
|
||||||
.fullSizeContentView,
|
|
||||||
|
|
||||||
.resizable,
|
|
||||||
.closable,
|
|
||||||
.miniaturizable,
|
|
||||||
]
|
|
||||||
|
|
||||||
// Hide the title
|
|
||||||
window.titleVisibility = .hidden
|
|
||||||
window.titlebarAppearsTransparent = true
|
|
||||||
|
|
||||||
// Hide the traffic lights (window control buttons)
|
|
||||||
window.standardWindowButton(.closeButton)?.isHidden = true
|
|
||||||
window.standardWindowButton(.miniaturizeButton)?.isHidden = true
|
|
||||||
window.standardWindowButton(.zoomButton)?.isHidden = true
|
|
||||||
|
|
||||||
// Disallow tabbing if the titlebar is hidden, since that will (should) also hide the tab bar.
|
|
||||||
window.tabbingMode = .disallowed
|
|
||||||
|
|
||||||
// Nuke it from orbit -- hide the titlebar container entirely, just in case. There are
|
|
||||||
// some operations that appear to bring back the titlebar visibility so this ensures
|
|
||||||
// it is gone forever.
|
|
||||||
if let themeFrame = window.contentView?.superview,
|
|
||||||
let titleBarContainer = themeFrame.firstDescendant(withClassName: "NSTitlebarContainerView") {
|
|
||||||
titleBarContainer.isHidden = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// In various situations, macOS automatically tabs new windows. Ghostty handles
|
// In various situations, macOS automatically tabs new windows. Ghostty handles
|
||||||
@@ -448,6 +493,20 @@ class TerminalController: BaseTerminalController {
|
|||||||
override func windowDidMove(_ notification: Notification) {
|
override func windowDidMove(_ notification: Notification) {
|
||||||
super.windowDidMove(notification)
|
super.windowDidMove(notification)
|
||||||
self.fixTabBar()
|
self.fixTabBar()
|
||||||
|
|
||||||
|
// Whenever we move save our last position for the next start.
|
||||||
|
if let window {
|
||||||
|
LastWindowPosition.shared.save(window)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowDidBecomeMain(_ notification: Notification) {
|
||||||
|
// Whenever we get focused, use that as our last window position for
|
||||||
|
// restart. This differs from Terminal.app but matches iTerm2 behavior
|
||||||
|
// and I think its sensible.
|
||||||
|
if let window {
|
||||||
|
LastWindowPosition.shared.save(window)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when the window will be encoded. We handle the data encoding here in the
|
// Called when the window will be encoded. We handle the data encoding here in the
|
||||||
@@ -469,7 +528,50 @@ class TerminalController: BaseTerminalController {
|
|||||||
ghostty.newTab(surface: surface)
|
ghostty.newTab(surface: surface)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction override func closeWindow(_ sender: Any) {
|
private func confirmClose(
|
||||||
|
window: NSWindow,
|
||||||
|
messageText: String,
|
||||||
|
informativeText: String,
|
||||||
|
completion: @escaping () -> Void
|
||||||
|
) {
|
||||||
|
// If we need confirmation by any, show one confirmation for all windows
|
||||||
|
// in the tab group.
|
||||||
|
let alert = NSAlert()
|
||||||
|
alert.messageText = messageText
|
||||||
|
alert.informativeText = informativeText
|
||||||
|
alert.addButton(withTitle: "Close")
|
||||||
|
alert.addButton(withTitle: "Cancel")
|
||||||
|
alert.alertStyle = .warning
|
||||||
|
alert.beginSheetModal(for: window) { response in
|
||||||
|
if response == .alertFirstButtonReturn {
|
||||||
|
completion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func closeTab(_ sender: Any?) {
|
||||||
|
guard let window = window else { return }
|
||||||
|
guard window.tabGroup != nil else {
|
||||||
|
// No tabs, no tab group, just perform a normal close.
|
||||||
|
window.performClose(sender)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if surfaceTree?.needsConfirmQuit() ?? false {
|
||||||
|
confirmClose(
|
||||||
|
window: window,
|
||||||
|
messageText: "Close Tab?",
|
||||||
|
informativeText: "The terminal still has a running process. If you close the tab the process will be killed."
|
||||||
|
) {
|
||||||
|
window.close()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
window.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction override func closeWindow(_ sender: Any?) {
|
||||||
guard let window = window else { return }
|
guard let window = window else { return }
|
||||||
guard let tabGroup = window.tabGroup else {
|
guard let tabGroup = window.tabGroup else {
|
||||||
// No tabs, no tab group, just perform a normal close.
|
// No tabs, no tab group, just perform a normal close.
|
||||||
@@ -484,47 +586,34 @@ class TerminalController: BaseTerminalController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if any windows require close confirmation.
|
// Check if any windows require close confirmation.
|
||||||
var needsConfirm: Bool = false
|
let needsConfirm = tabGroup.windows.contains { tabWindow in
|
||||||
for tabWindow in tabGroup.windows {
|
guard let controller = tabWindow.windowController as? TerminalController else {
|
||||||
guard let c = tabWindow.windowController as? TerminalController else { continue }
|
return false
|
||||||
if (c.surfaceTree?.needsConfirmQuit() ?? false) {
|
|
||||||
needsConfirm = true
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
return controller.surfaceTree?.needsConfirmQuit() ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
// If none need confirmation then we can just close all the windows.
|
// If none need confirmation then we can just close all the windows.
|
||||||
if (!needsConfirm) {
|
if !needsConfirm {
|
||||||
for tabWindow in tabGroup.windows {
|
tabGroup.windows.forEach { $0.close() }
|
||||||
tabWindow.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we need confirmation by any, show one confirmation for all windows
|
confirmClose(
|
||||||
// in the tab group.
|
window: window,
|
||||||
let alert = NSAlert()
|
messageText: "Close Window?",
|
||||||
alert.messageText = "Close Window?"
|
informativeText: "All terminal sessions in this window will be terminated."
|
||||||
alert.informativeText = "All terminal sessions in this window will be terminated."
|
) {
|
||||||
alert.addButton(withTitle: "Close Window")
|
tabGroup.windows.forEach { $0.close() }
|
||||||
alert.addButton(withTitle: "Cancel")
|
}
|
||||||
alert.alertStyle = .warning
|
|
||||||
alert.beginSheetModal(for: window, completionHandler: { response in
|
|
||||||
if (response == .alertFirstButtonReturn) {
|
|
||||||
for tabWindow in tabGroup.windows {
|
|
||||||
tabWindow.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func toggleGhosttyFullScreen(_ sender: Any) {
|
@IBAction func toggleGhosttyFullScreen(_ sender: Any?) {
|
||||||
guard let surface = focusedSurface?.surface else { return }
|
guard let surface = focusedSurface?.surface else { return }
|
||||||
ghostty.toggleFullscreen(surface: surface)
|
ghostty.toggleFullscreen(surface: surface)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func toggleTerminalInspector(_ sender: Any) {
|
@IBAction func toggleTerminalInspector(_ sender: Any?) {
|
||||||
guard let surface = focusedSurface?.surface else { return }
|
guard let surface = focusedSurface?.surface else { return }
|
||||||
ghostty.toggleTerminalInspector(surface: surface)
|
ghostty.toggleTerminalInspector(surface: surface)
|
||||||
}
|
}
|
||||||
@@ -620,13 +709,21 @@ class TerminalController: BaseTerminalController {
|
|||||||
// If our index is the same we do nothing
|
// If our index is the same we do nothing
|
||||||
guard finalIndex != selectedIndex else { return }
|
guard finalIndex != selectedIndex else { return }
|
||||||
|
|
||||||
// Get our parent
|
// Get our target window
|
||||||
let parent = tabbedWindows[finalIndex]
|
let targetWindow = tabbedWindows[finalIndex]
|
||||||
|
|
||||||
// Move our current selected window to the proper index
|
// Begin a group of window operations to minimize visual updates
|
||||||
|
NSAnimationContext.beginGrouping()
|
||||||
|
NSAnimationContext.current.duration = 0
|
||||||
|
|
||||||
|
// Remove and re-add the window in the correct position
|
||||||
tabGroup.removeWindow(selectedWindow)
|
tabGroup.removeWindow(selectedWindow)
|
||||||
parent.addTabbedWindow(selectedWindow, ordered: action.amount < 0 ? .below : .above)
|
targetWindow.addTabbedWindow(selectedWindow, ordered: action.amount < 0 ? .below : .above)
|
||||||
selectedWindow.makeKeyAndOrderFront(nil)
|
|
||||||
|
// Ensure our window remains selected
|
||||||
|
selectedWindow.makeKey()
|
||||||
|
|
||||||
|
NSAnimationContext.endGrouping()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func onGotoTab(notification: SwiftUI.Notification) {
|
@objc private func onGotoTab(notification: SwiftUI.Notification) {
|
||||||
@@ -681,6 +778,12 @@ class TerminalController: BaseTerminalController {
|
|||||||
targetWindow.makeKeyAndOrderFront(nil)
|
targetWindow.makeKeyAndOrderFront(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func onCloseTab(notification: SwiftUI.Notification) {
|
||||||
|
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||||
|
guard surfaceTree?.contains(view: target) ?? false else { return }
|
||||||
|
closeTab(self)
|
||||||
|
}
|
||||||
|
|
||||||
@objc private func onToggleFullscreen(notification: SwiftUI.Notification) {
|
@objc private func onToggleFullscreen(notification: SwiftUI.Notification) {
|
||||||
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||||
guard target == self.focusedSurface else { return }
|
guard target == self.focusedSurface else { return }
|
||||||
@@ -698,7 +801,7 @@ class TerminalController: BaseTerminalController {
|
|||||||
toggleFullscreen(mode: fullscreenMode)
|
toggleFullscreen(mode: fullscreenMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct DerivedConfig {
|
struct DerivedConfig {
|
||||||
let backgroundColor: Color
|
let backgroundColor: Color
|
||||||
let macosTitlebarStyle: String
|
let macosTitlebarStyle: String
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ protocol TerminalViewDelegate: AnyObject {
|
|||||||
|
|
||||||
/// The title of the terminal should change.
|
/// The title of the terminal should change.
|
||||||
func titleDidChange(to: String)
|
func titleDidChange(to: String)
|
||||||
|
|
||||||
/// The URL of the pwd should change.
|
/// The URL of the pwd should change.
|
||||||
func pwdDidChange(to: URL?)
|
func pwdDidChange(to: URL?)
|
||||||
|
|
||||||
@@ -56,15 +56,10 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
|
|||||||
|
|
||||||
// The title for our window
|
// The title for our window
|
||||||
private var title: String {
|
private var title: String {
|
||||||
var title = "👻"
|
if let surfaceTitle, !surfaceTitle.isEmpty {
|
||||||
|
return surfaceTitle
|
||||||
if let surfaceTitle = surfaceTitle {
|
|
||||||
if (surfaceTitle.count > 0) {
|
|
||||||
title = surfaceTitle
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return "👻"
|
||||||
return title
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The pwd of the focused surface as a URL
|
// The pwd of the focused surface as a URL
|
||||||
@@ -72,7 +67,7 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
|
|||||||
guard let surfacePwd, surfacePwd != "" else { return nil }
|
guard let surfacePwd, surfacePwd != "" else { return nil }
|
||||||
return URL(fileURLWithPath: surfacePwd)
|
return URL(fileURLWithPath: surfacePwd)
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
switch ghostty.readiness {
|
switch ghostty.readiness {
|
||||||
case .loading:
|
case .loading:
|
||||||
|
@@ -115,6 +115,21 @@ class TerminalWindow: NSWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We override this so that with the hidden titlebar style the titlebar
|
||||||
|
// area is not draggable.
|
||||||
|
override var contentLayoutRect: CGRect {
|
||||||
|
var rect = super.contentLayoutRect
|
||||||
|
|
||||||
|
// If we are using a hidden titlebar style, the content layout is the
|
||||||
|
// full frame making it so that it is not draggable.
|
||||||
|
if let controller = windowController as? TerminalController,
|
||||||
|
controller.derivedConfig.macosTitlebarStyle == "hidden" {
|
||||||
|
rect.origin.y = 0
|
||||||
|
rect.size.height = self.frame.height
|
||||||
|
}
|
||||||
|
return rect
|
||||||
|
}
|
||||||
|
|
||||||
// The window theme configuration from Ghostty. This is used to control some
|
// The window theme configuration from Ghostty. This is used to control some
|
||||||
// behaviors that don't look quite right in certain situations.
|
// behaviors that don't look quite right in certain situations.
|
||||||
var windowTheme: TerminalWindowTheme?
|
var windowTheme: TerminalWindowTheme?
|
||||||
@@ -667,12 +682,16 @@ fileprivate class WindowDragView: NSView {
|
|||||||
|
|
||||||
// A view that matches the color of selected and unselected tabs in the adjacent tab bar.
|
// A view that matches the color of selected and unselected tabs in the adjacent tab bar.
|
||||||
fileprivate class WindowButtonsBackdropView: NSView {
|
fileprivate class WindowButtonsBackdropView: NSView {
|
||||||
private let terminalWindow: TerminalWindow
|
// This must be weak because the window has this view. Otherwise
|
||||||
|
// a retain cycle occurs.
|
||||||
|
private weak var terminalWindow: TerminalWindow?
|
||||||
private let isLightTheme: Bool
|
private let isLightTheme: Bool
|
||||||
private let overlayLayer = VibrantLayer()
|
private let overlayLayer = VibrantLayer()
|
||||||
|
|
||||||
var isHighlighted: Bool = true {
|
var isHighlighted: Bool = true {
|
||||||
didSet {
|
didSet {
|
||||||
|
guard let terminalWindow else { return }
|
||||||
|
|
||||||
if isLightTheme {
|
if isLightTheme {
|
||||||
overlayLayer.isHidden = isHighlighted
|
overlayLayer.isHidden = isHighlighted
|
||||||
layer?.backgroundColor = .clear
|
layer?.backgroundColor = .clear
|
||||||
|
@@ -62,7 +62,7 @@ extension Ghostty {
|
|||||||
// uses to interface with the application runtime environment.
|
// uses to interface with the application runtime environment.
|
||||||
var runtime_cfg = ghostty_runtime_config_s(
|
var runtime_cfg = ghostty_runtime_config_s(
|
||||||
userdata: Unmanaged.passUnretained(self).toOpaque(),
|
userdata: Unmanaged.passUnretained(self).toOpaque(),
|
||||||
supports_selection_clipboard: false,
|
supports_selection_clipboard: true,
|
||||||
wakeup_cb: { userdata in App.wakeup(userdata) },
|
wakeup_cb: { userdata in App.wakeup(userdata) },
|
||||||
action_cb: { app, target, action in App.action(app!, target: target, action: action) },
|
action_cb: { app, target, action in App.action(app!, target: target, action: action) },
|
||||||
read_clipboard_cb: { userdata, loc, state in App.readClipboard(userdata, location: loc, state: state) },
|
read_clipboard_cb: { userdata, loc, state in App.readClipboard(userdata, location: loc, state: state) },
|
||||||
@@ -117,23 +117,7 @@ extension Ghostty {
|
|||||||
|
|
||||||
func appTick() {
|
func appTick() {
|
||||||
guard let app = self.app else { return }
|
guard let app = self.app else { return }
|
||||||
|
ghostty_app_tick(app)
|
||||||
// Tick our app, which lets us know if we want to quit
|
|
||||||
let exit = ghostty_app_tick(app)
|
|
||||||
if (!exit) { return }
|
|
||||||
|
|
||||||
// On iOS, applications do not terminate programmatically like they do
|
|
||||||
// on macOS. On iOS, applications are only terminated when a user physically
|
|
||||||
// closes the application (i.e. going to the home screen). If we request
|
|
||||||
// exit on iOS we ignore it.
|
|
||||||
#if os(iOS)
|
|
||||||
logger.info("quit request received, ignoring on iOS")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if os(macOS)
|
|
||||||
// We want to quit, start that process
|
|
||||||
NSApplication.shared.terminate(nil)
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func openConfig() {
|
func openConfig() {
|
||||||
@@ -273,7 +257,7 @@ extension Ghostty {
|
|||||||
// MARK: Ghostty Callbacks (iOS)
|
// MARK: Ghostty Callbacks (iOS)
|
||||||
|
|
||||||
static func wakeup(_ userdata: UnsafeMutableRawPointer?) {}
|
static func wakeup(_ userdata: UnsafeMutableRawPointer?) {}
|
||||||
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) {}
|
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) -> Bool { return false }
|
||||||
static func readClipboard(
|
static func readClipboard(
|
||||||
_ userdata: UnsafeMutableRawPointer?,
|
_ userdata: UnsafeMutableRawPointer?,
|
||||||
location: ghostty_clipboard_e,
|
location: ghostty_clipboard_e,
|
||||||
@@ -336,13 +320,13 @@ extension Ghostty {
|
|||||||
let surfaceView = self.surfaceUserdata(from: userdata)
|
let surfaceView = self.surfaceUserdata(from: userdata)
|
||||||
guard let surface = surfaceView.surface else { return }
|
guard let surface = surfaceView.surface else { return }
|
||||||
|
|
||||||
// We only support the standard clipboard
|
// Get our pasteboard
|
||||||
if (location != GHOSTTY_CLIPBOARD_STANDARD) {
|
guard let pasteboard = NSPasteboard.ghostty(location) else {
|
||||||
return completeClipboardRequest(surface, data: "", state: state)
|
return completeClipboardRequest(surface, data: "", state: state)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get our string
|
// Get our string
|
||||||
let str = NSPasteboard.general.getOpinionatedStringContents() ?? ""
|
let str = pasteboard.getOpinionatedStringContents() ?? ""
|
||||||
completeClipboardRequest(surface, data: str, state: state)
|
completeClipboardRequest(surface, data: str, state: state)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,14 +364,12 @@ extension Ghostty {
|
|||||||
static func writeClipboard(_ userdata: UnsafeMutableRawPointer?, string: UnsafePointer<CChar>?, location: ghostty_clipboard_e, confirm: Bool) {
|
static func writeClipboard(_ userdata: UnsafeMutableRawPointer?, string: UnsafePointer<CChar>?, location: ghostty_clipboard_e, confirm: Bool) {
|
||||||
let surface = self.surfaceUserdata(from: userdata)
|
let surface = self.surfaceUserdata(from: userdata)
|
||||||
|
|
||||||
// We only support the standard clipboard
|
|
||||||
if (location != GHOSTTY_CLIPBOARD_STANDARD) { return }
|
|
||||||
|
|
||||||
|
guard let pasteboard = NSPasteboard.ghostty(location) else { return }
|
||||||
guard let valueStr = String(cString: string!, encoding: .utf8) else { return }
|
guard let valueStr = String(cString: string!, encoding: .utf8) else { return }
|
||||||
if !confirm {
|
if !confirm {
|
||||||
let pb = NSPasteboard.general
|
pasteboard.declareTypes([.string], owner: nil)
|
||||||
pb.declareTypes([.string], owner: nil)
|
pasteboard.setString(valueStr, forType: .string)
|
||||||
pb.setString(valueStr, forType: .string)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,7 +378,7 @@ extension Ghostty {
|
|||||||
object: surface,
|
object: surface,
|
||||||
userInfo: [
|
userInfo: [
|
||||||
Notification.ConfirmClipboardStrKey: valueStr,
|
Notification.ConfirmClipboardStrKey: valueStr,
|
||||||
Notification.ConfirmClipboardRequestKey: Ghostty.ClipboardRequest.osc_52_write,
|
Notification.ConfirmClipboardRequestKey: Ghostty.ClipboardRequest.osc_52_write(pasteboard),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -441,7 +423,7 @@ extension Ghostty {
|
|||||||
|
|
||||||
// MARK: Actions (macOS)
|
// MARK: Actions (macOS)
|
||||||
|
|
||||||
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) {
|
static func action(_ app: ghostty_app_t, target: ghostty_target_s, action: ghostty_action_s) -> Bool {
|
||||||
// Make sure it a target we understand so all our action handlers can assert
|
// Make sure it a target we understand so all our action handlers can assert
|
||||||
switch (target.tag) {
|
switch (target.tag) {
|
||||||
case GHOSTTY_TARGET_APP, GHOSTTY_TARGET_SURFACE:
|
case GHOSTTY_TARGET_APP, GHOSTTY_TARGET_SURFACE:
|
||||||
@@ -449,11 +431,14 @@ extension Ghostty {
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
Ghostty.logger.warning("unknown action target=\(target.tag.rawValue)")
|
Ghostty.logger.warning("unknown action target=\(target.tag.rawValue)")
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action dispatch
|
// Action dispatch
|
||||||
switch (action.tag) {
|
switch (action.tag) {
|
||||||
|
case GHOSTTY_ACTION_QUIT:
|
||||||
|
quit(app)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_NEW_WINDOW:
|
case GHOSTTY_ACTION_NEW_WINDOW:
|
||||||
newWindow(app, target: target)
|
newWindow(app, target: target)
|
||||||
|
|
||||||
@@ -463,17 +448,20 @@ extension Ghostty {
|
|||||||
case GHOSTTY_ACTION_NEW_SPLIT:
|
case GHOSTTY_ACTION_NEW_SPLIT:
|
||||||
newSplit(app, target: target, direction: action.action.new_split)
|
newSplit(app, target: target, direction: action.action.new_split)
|
||||||
|
|
||||||
|
case GHOSTTY_ACTION_CLOSE_TAB:
|
||||||
|
closeTab(app, target: target)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_TOGGLE_FULLSCREEN:
|
case GHOSTTY_ACTION_TOGGLE_FULLSCREEN:
|
||||||
toggleFullscreen(app, target: target, mode: action.action.toggle_fullscreen)
|
toggleFullscreen(app, target: target, mode: action.action.toggle_fullscreen)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_MOVE_TAB:
|
case GHOSTTY_ACTION_MOVE_TAB:
|
||||||
moveTab(app, target: target, move: action.action.move_tab)
|
return moveTab(app, target: target, move: action.action.move_tab)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_GOTO_TAB:
|
case GHOSTTY_ACTION_GOTO_TAB:
|
||||||
gotoTab(app, target: target, tab: action.action.goto_tab)
|
return gotoTab(app, target: target, tab: action.action.goto_tab)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_GOTO_SPLIT:
|
case GHOSTTY_ACTION_GOTO_SPLIT:
|
||||||
gotoSplit(app, target: target, direction: action.action.goto_split)
|
return gotoSplit(app, target: target, direction: action.action.goto_split)
|
||||||
|
|
||||||
case GHOSTTY_ACTION_RESIZE_SPLIT:
|
case GHOSTTY_ACTION_RESIZE_SPLIT:
|
||||||
resizeSplit(app, target: target, resize: action.action.resize_split)
|
resizeSplit(app, target: target, resize: action.action.resize_split)
|
||||||
@@ -553,10 +541,30 @@ extension Ghostty {
|
|||||||
fallthrough
|
fallthrough
|
||||||
case GHOSTTY_ACTION_QUIT_TIMER:
|
case GHOSTTY_ACTION_QUIT_TIMER:
|
||||||
Ghostty.logger.info("known but unimplemented action action=\(action.tag.rawValue)")
|
Ghostty.logger.info("known but unimplemented action action=\(action.tag.rawValue)")
|
||||||
|
return false
|
||||||
default:
|
default:
|
||||||
Ghostty.logger.warning("unknown action action=\(action.tag.rawValue)")
|
Ghostty.logger.warning("unknown action action=\(action.tag.rawValue)")
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we reached here then we assume performed since all unknown actions
|
||||||
|
// are captured in the switch and return false.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func quit(_ app: ghostty_app_t) {
|
||||||
|
// On iOS, applications do not terminate programmatically like they do
|
||||||
|
// on macOS. On iOS, applications are only terminated when a user physically
|
||||||
|
// closes the application (i.e. going to the home screen). If we request
|
||||||
|
// exit on iOS we ignore it.
|
||||||
|
#if os(iOS)
|
||||||
|
logger.info("quit request received, ignoring on iOS")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
// We want to quit, start that process
|
||||||
|
NSApplication.shared.terminate(nil)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func newWindow(_ app: ghostty_app_t, target: ghostty_target_s) {
|
private static func newWindow(_ app: ghostty_app_t, target: ghostty_target_s) {
|
||||||
@@ -651,6 +659,27 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func closeTab(_ app: ghostty_app_t, target: ghostty_target_s) {
|
||||||
|
switch (target.tag) {
|
||||||
|
case GHOSTTY_TARGET_APP:
|
||||||
|
Ghostty.logger.warning("close tab does nothing with an app target")
|
||||||
|
return
|
||||||
|
|
||||||
|
case GHOSTTY_TARGET_SURFACE:
|
||||||
|
guard let surface = target.target.surface else { return }
|
||||||
|
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
||||||
|
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: .ghosttyCloseTab,
|
||||||
|
object: surfaceView
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
default:
|
||||||
|
assertionFailure()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static func toggleFullscreen(
|
private static func toggleFullscreen(
|
||||||
_ app: ghostty_app_t,
|
_ app: ghostty_app_t,
|
||||||
target: ghostty_target_s,
|
target: ghostty_target_s,
|
||||||
@@ -692,15 +721,19 @@ extension Ghostty {
|
|||||||
private static func moveTab(
|
private static func moveTab(
|
||||||
_ app: ghostty_app_t,
|
_ app: ghostty_app_t,
|
||||||
target: ghostty_target_s,
|
target: ghostty_target_s,
|
||||||
move: ghostty_action_move_tab_s) {
|
move: ghostty_action_move_tab_s) -> Bool {
|
||||||
switch (target.tag) {
|
switch (target.tag) {
|
||||||
case GHOSTTY_TARGET_APP:
|
case GHOSTTY_TARGET_APP:
|
||||||
Ghostty.logger.warning("move tab does nothing with an app target")
|
Ghostty.logger.warning("move tab does nothing with an app target")
|
||||||
return
|
return false
|
||||||
|
|
||||||
case GHOSTTY_TARGET_SURFACE:
|
case GHOSTTY_TARGET_SURFACE:
|
||||||
guard let surface = target.target.surface else { return }
|
guard let surface = target.target.surface else { return false }
|
||||||
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
guard let surfaceView = self.surfaceView(from: surface) else { return false }
|
||||||
|
|
||||||
|
// See gotoTab for notes on this check.
|
||||||
|
guard (surfaceView.window?.tabGroup?.windows.count ?? 0) > 1 else { return false }
|
||||||
|
|
||||||
NotificationCenter.default.post(
|
NotificationCenter.default.post(
|
||||||
name: .ghosttyMoveTab,
|
name: .ghosttyMoveTab,
|
||||||
object: surfaceView,
|
object: surfaceView,
|
||||||
@@ -712,20 +745,27 @@ extension Ghostty {
|
|||||||
default:
|
default:
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func gotoTab(
|
private static func gotoTab(
|
||||||
_ app: ghostty_app_t,
|
_ app: ghostty_app_t,
|
||||||
target: ghostty_target_s,
|
target: ghostty_target_s,
|
||||||
tab: ghostty_action_goto_tab_e) {
|
tab: ghostty_action_goto_tab_e) -> Bool {
|
||||||
switch (target.tag) {
|
switch (target.tag) {
|
||||||
case GHOSTTY_TARGET_APP:
|
case GHOSTTY_TARGET_APP:
|
||||||
Ghostty.logger.warning("goto tab does nothing with an app target")
|
Ghostty.logger.warning("goto tab does nothing with an app target")
|
||||||
return
|
return false
|
||||||
|
|
||||||
case GHOSTTY_TARGET_SURFACE:
|
case GHOSTTY_TARGET_SURFACE:
|
||||||
guard let surface = target.target.surface else { return }
|
guard let surface = target.target.surface else { return false }
|
||||||
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
guard let surfaceView = self.surfaceView(from: surface) else { return false }
|
||||||
|
|
||||||
|
// Similar to goto_split (see comment there) about our performability,
|
||||||
|
// we should make this more accurate later.
|
||||||
|
guard (surfaceView.window?.tabGroup?.windows.count ?? 0) > 1 else { return false }
|
||||||
|
|
||||||
NotificationCenter.default.post(
|
NotificationCenter.default.post(
|
||||||
name: Notification.ghosttyGotoTab,
|
name: Notification.ghosttyGotoTab,
|
||||||
object: surfaceView,
|
object: surfaceView,
|
||||||
@@ -737,20 +777,31 @@ extension Ghostty {
|
|||||||
default:
|
default:
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func gotoSplit(
|
private static func gotoSplit(
|
||||||
_ app: ghostty_app_t,
|
_ app: ghostty_app_t,
|
||||||
target: ghostty_target_s,
|
target: ghostty_target_s,
|
||||||
direction: ghostty_action_goto_split_e) {
|
direction: ghostty_action_goto_split_e) -> Bool {
|
||||||
switch (target.tag) {
|
switch (target.tag) {
|
||||||
case GHOSTTY_TARGET_APP:
|
case GHOSTTY_TARGET_APP:
|
||||||
Ghostty.logger.warning("goto split does nothing with an app target")
|
Ghostty.logger.warning("goto split does nothing with an app target")
|
||||||
return
|
return false
|
||||||
|
|
||||||
case GHOSTTY_TARGET_SURFACE:
|
case GHOSTTY_TARGET_SURFACE:
|
||||||
guard let surface = target.target.surface else { return }
|
guard let surface = target.target.surface else { return false }
|
||||||
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
guard let surfaceView = self.surfaceView(from: surface) else { return false }
|
||||||
|
guard let controller = surfaceView.window?.windowController as? BaseTerminalController else { return false }
|
||||||
|
|
||||||
|
// For now, we return false if the window has no splits and we return
|
||||||
|
// true if the window has ANY splits. This isn't strictly correct because
|
||||||
|
// we should only be returning true if we actually performed the action,
|
||||||
|
// but this handles the most common case of caring about goto_split performability
|
||||||
|
// which is the no-split case.
|
||||||
|
guard controller.surfaceTree?.isSplit ?? false else { return false }
|
||||||
|
|
||||||
NotificationCenter.default.post(
|
NotificationCenter.default.post(
|
||||||
name: Notification.ghosttyFocusSplit,
|
name: Notification.ghosttyFocusSplit,
|
||||||
object: surfaceView,
|
object: surfaceView,
|
||||||
@@ -762,6 +813,8 @@ extension Ghostty {
|
|||||||
default:
|
default:
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func resizeSplit(
|
private static func resizeSplit(
|
||||||
|
@@ -132,15 +132,6 @@ extension Ghostty {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
var windowColorspace: String {
|
|
||||||
guard let config = self.config else { return "" }
|
|
||||||
var v: UnsafePointer<Int8>? = nil
|
|
||||||
let key = "window-colorspace"
|
|
||||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return "" }
|
|
||||||
guard let ptr = v else { return "" }
|
|
||||||
return String(cString: ptr)
|
|
||||||
}
|
|
||||||
|
|
||||||
var windowSaveState: String {
|
var windowSaveState: String {
|
||||||
guard let config = self.config else { return "" }
|
guard let config = self.config else { return "" }
|
||||||
var v: UnsafePointer<Int8>? = nil
|
var v: UnsafePointer<Int8>? = nil
|
||||||
@@ -149,6 +140,20 @@ extension Ghostty {
|
|||||||
guard let ptr = v else { return "" }
|
guard let ptr = v else { return "" }
|
||||||
return String(cString: ptr)
|
return String(cString: ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var windowPositionX: Int16? {
|
||||||
|
guard let config = self.config else { return nil }
|
||||||
|
var v: Int16 = 0
|
||||||
|
let key = "window-position-x"
|
||||||
|
return ghostty_config_get(config, &v, key, UInt(key.count)) ? v : nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var windowPositionY: Int16? {
|
||||||
|
guard let config = self.config else { return nil }
|
||||||
|
var v: Int16 = 0
|
||||||
|
let key = "window-position-y"
|
||||||
|
return ghostty_config_get(config, &v, key, UInt(key.count)) ? v : nil
|
||||||
|
}
|
||||||
|
|
||||||
var windowNewTabPosition: String {
|
var windowNewTabPosition: String {
|
||||||
guard let config = self.config else { return "" }
|
guard let config = self.config else { return "" }
|
||||||
@@ -160,11 +165,14 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var windowDecorations: Bool {
|
var windowDecorations: Bool {
|
||||||
guard let config = self.config else { return true }
|
let defaultValue = true
|
||||||
var v = false;
|
guard let config = self.config else { return defaultValue }
|
||||||
|
var v: UnsafePointer<Int8>? = nil
|
||||||
let key = "window-decoration"
|
let key = "window-decoration"
|
||||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return defaultValue }
|
||||||
return v;
|
guard let ptr = v else { return defaultValue }
|
||||||
|
let str = String(cString: ptr)
|
||||||
|
return WindowDecoration(rawValue: str)?.enabled() ?? defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
var windowTheme: String? {
|
var windowTheme: String? {
|
||||||
@@ -331,7 +339,7 @@ extension Ghostty {
|
|||||||
var backgroundBlurRadius: Int {
|
var backgroundBlurRadius: Int {
|
||||||
guard let config = self.config else { return 1 }
|
guard let config = self.config else { return 1 }
|
||||||
var v: Int = 0
|
var v: Int = 0
|
||||||
let key = "background-blur-radius"
|
let key = "background-blur"
|
||||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
@@ -361,13 +369,24 @@ extension Ghostty {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This isn't actually a configurable value currently but it could be done day.
|
|
||||||
// We put it here because it is a color that changes depending on the configuration.
|
|
||||||
var splitDividerColor: Color {
|
var splitDividerColor: Color {
|
||||||
let backgroundColor = OSColor(backgroundColor)
|
let backgroundColor = OSColor(backgroundColor)
|
||||||
let isLightBackground = backgroundColor.isLightColor
|
let isLightBackground = backgroundColor.isLightColor
|
||||||
let newColor = isLightBackground ? backgroundColor.darken(by: 0.08) : backgroundColor.darken(by: 0.4)
|
let newColor = isLightBackground ? backgroundColor.darken(by: 0.08) : backgroundColor.darken(by: 0.4)
|
||||||
return Color(newColor)
|
|
||||||
|
guard let config = self.config else { return Color(newColor) }
|
||||||
|
|
||||||
|
var color: ghostty_config_color_s = .init();
|
||||||
|
let key = "split-divider-color"
|
||||||
|
if (!ghostty_config_get(config, &color, key, UInt(key.count))) {
|
||||||
|
return Color(newColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
return .init(
|
||||||
|
red: Double(color.r) / 255,
|
||||||
|
green: Double(color.g) / 255,
|
||||||
|
blue: Double(color.b) / 255
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#if canImport(AppKit)
|
#if canImport(AppKit)
|
||||||
@@ -406,6 +425,16 @@ extension Ghostty {
|
|||||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var quickTerminalSpaceBehavior: QuickTerminalSpaceBehavior {
|
||||||
|
guard let config = self.config else { return .move }
|
||||||
|
var v: UnsafePointer<Int8>? = nil
|
||||||
|
let key = "quick-terminal-space-behavior"
|
||||||
|
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return .move }
|
||||||
|
guard let ptr = v else { return .move }
|
||||||
|
let str = String(cString: ptr)
|
||||||
|
return QuickTerminalSpaceBehavior(fromGhosttyConfig: str) ?? .move
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var resizeOverlay: ResizeOverlay {
|
var resizeOverlay: ResizeOverlay {
|
||||||
@@ -437,15 +466,14 @@ extension Ghostty {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
var autoUpdate: AutoUpdate {
|
var autoUpdate: AutoUpdate? {
|
||||||
let defaultValue = AutoUpdate.check
|
guard let config = self.config else { return nil }
|
||||||
guard let config = self.config else { return defaultValue }
|
|
||||||
var v: UnsafePointer<Int8>? = nil
|
var v: UnsafePointer<Int8>? = nil
|
||||||
let key = "auto-update"
|
let key = "auto-update"
|
||||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return defaultValue }
|
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return nil }
|
||||||
guard let ptr = v else { return defaultValue }
|
guard let ptr = v else { return nil }
|
||||||
let str = String(cString: ptr)
|
let str = String(cString: ptr)
|
||||||
return AutoUpdate(rawValue: str) ?? defaultValue
|
return AutoUpdate(rawValue: str)
|
||||||
}
|
}
|
||||||
|
|
||||||
var autoUpdateChannel: AutoUpdateChannel {
|
var autoUpdateChannel: AutoUpdateChannel {
|
||||||
@@ -529,4 +557,18 @@ extension Ghostty.Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum WindowDecoration: String {
|
||||||
|
case none
|
||||||
|
case client
|
||||||
|
case server
|
||||||
|
case auto
|
||||||
|
|
||||||
|
func enabled() -> Bool {
|
||||||
|
switch self {
|
||||||
|
case .client, .server, .auto: return true
|
||||||
|
case .none: return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
15
macos/Sources/Ghostty/Ghostty.Event.swift
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import Cocoa
|
||||||
|
import GhosttyKit
|
||||||
|
|
||||||
|
extension Ghostty {
|
||||||
|
/// A comparable event.
|
||||||
|
struct ComparableKeyEvent: Equatable {
|
||||||
|
let keyCode: UInt16
|
||||||
|
let flags: NSEvent.ModifierFlags
|
||||||
|
|
||||||
|
init(event: NSEvent) {
|
||||||
|
self.keyCode = event.keyCode
|
||||||
|
self.flags = event.modifierFlags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -38,6 +38,15 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the tree is split.
|
||||||
|
var isSplit: Bool {
|
||||||
|
return if case .leaf = self {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func topLeft() -> SurfaceView {
|
func topLeft() -> SurfaceView {
|
||||||
switch (self) {
|
switch (self) {
|
||||||
case .leaf(let leaf):
|
case .leaf(let leaf):
|
||||||
@@ -51,7 +60,7 @@ extension Ghostty {
|
|||||||
/// Returns the view that would prefer receiving focus in this tree. This is always the
|
/// Returns the view that would prefer receiving focus in this tree. This is always the
|
||||||
/// top-left-most view. This is used when creating a split or closing a split to find the
|
/// top-left-most view. This is used when creating a split or closing a split to find the
|
||||||
/// next view to send focus to.
|
/// next view to send focus to.
|
||||||
func preferredFocus(_ direction: SplitFocusDirection = .top) -> SurfaceView {
|
func preferredFocus(_ direction: SplitFocusDirection = .up) -> SurfaceView {
|
||||||
let container: Container
|
let container: Container
|
||||||
switch (self) {
|
switch (self) {
|
||||||
case .leaf(let leaf):
|
case .leaf(let leaf):
|
||||||
@@ -64,10 +73,10 @@ extension Ghostty {
|
|||||||
|
|
||||||
let node: SplitNode
|
let node: SplitNode
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case .previous, .top, .left:
|
case .previous, .up, .left:
|
||||||
node = container.bottomRight
|
node = container.bottomRight
|
||||||
|
|
||||||
case .next, .bottom, .right:
|
case .next, .down, .right:
|
||||||
node = container.topLeft
|
node = container.topLeft
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,14 +129,7 @@ extension Ghostty {
|
|||||||
|
|
||||||
/// Returns true if the split tree contains the given view.
|
/// Returns true if the split tree contains the given view.
|
||||||
func contains(view: SurfaceView) -> Bool {
|
func contains(view: SurfaceView) -> Bool {
|
||||||
switch (self) {
|
return leaf(for: view) != nil
|
||||||
case .leaf(let leaf):
|
|
||||||
return leaf.surface == view
|
|
||||||
|
|
||||||
case .split(let container):
|
|
||||||
return container.topLeft.contains(view: view) ||
|
|
||||||
container.bottomRight.contains(view: view)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find a surface view by UUID.
|
/// Find a surface view by UUID.
|
||||||
@@ -164,6 +166,22 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the node for the given view if its in the tree.
|
||||||
|
func leaf(for view: SurfaceView) -> Leaf? {
|
||||||
|
switch (self) {
|
||||||
|
case .leaf(let leaf):
|
||||||
|
if leaf.surface == view {
|
||||||
|
return leaf
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
case .split(let container):
|
||||||
|
return container.topLeft.leaf(for: view) ??
|
||||||
|
container.bottomRight.leaf(for: view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Sequence
|
// MARK: - Sequence
|
||||||
|
|
||||||
func makeIterator() -> IndexingIterator<[Leaf]> {
|
func makeIterator() -> IndexingIterator<[Leaf]> {
|
||||||
@@ -431,12 +449,12 @@ extension Ghostty {
|
|||||||
struct Neighbors {
|
struct Neighbors {
|
||||||
var left: SplitNode?
|
var left: SplitNode?
|
||||||
var right: SplitNode?
|
var right: SplitNode?
|
||||||
var top: SplitNode?
|
var up: SplitNode?
|
||||||
var bottom: SplitNode?
|
var down: SplitNode?
|
||||||
|
|
||||||
/// These are the previous/next nodes. It will certainly be one of the above as well
|
/// These are the previous/next nodes. It will certainly be one of the above as well
|
||||||
/// but we keep track of these separately because depending on the split direction
|
/// but we keep track of these separately because depending on the split direction
|
||||||
/// of the containing node, previous may be left OR top (same for next).
|
/// of the containing node, previous may be left OR up (same for next).
|
||||||
var previous: SplitNode?
|
var previous: SplitNode?
|
||||||
var next: SplitNode?
|
var next: SplitNode?
|
||||||
|
|
||||||
@@ -448,8 +466,8 @@ extension Ghostty {
|
|||||||
let map: [SplitFocusDirection : KeyPath<Self, SplitNode?>] = [
|
let map: [SplitFocusDirection : KeyPath<Self, SplitNode?>] = [
|
||||||
.previous: \.previous,
|
.previous: \.previous,
|
||||||
.next: \.next,
|
.next: \.next,
|
||||||
.top: \.top,
|
.up: \.up,
|
||||||
.bottom: \.bottom,
|
.down: \.down,
|
||||||
.left: \.left,
|
.left: \.left,
|
||||||
.right: \.right,
|
.right: \.right,
|
||||||
]
|
]
|
||||||
|
@@ -205,6 +205,7 @@ extension Ghostty {
|
|||||||
alert.beginSheetModal(for: window, completionHandler: { response in
|
alert.beginSheetModal(for: window, completionHandler: { response in
|
||||||
switch (response) {
|
switch (response) {
|
||||||
case .alertFirstButtonReturn:
|
case .alertFirstButtonReturn:
|
||||||
|
alert.window.orderOut(nil)
|
||||||
node = nil
|
node = nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -308,7 +309,7 @@ extension Ghostty {
|
|||||||
resizeIncrements: .init(width: 1, height: 1),
|
resizeIncrements: .init(width: 1, height: 1),
|
||||||
resizePublisher: container.resizeEvent,
|
resizePublisher: container.resizeEvent,
|
||||||
left: {
|
left: {
|
||||||
let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.right : \.bottom
|
let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.right : \.down
|
||||||
|
|
||||||
TerminalSplitNested(
|
TerminalSplitNested(
|
||||||
node: closeableTopLeft(),
|
node: closeableTopLeft(),
|
||||||
@@ -318,7 +319,7 @@ extension Ghostty {
|
|||||||
])
|
])
|
||||||
)
|
)
|
||||||
}, right: {
|
}, right: {
|
||||||
let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.left : \.top
|
let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.left : \.up
|
||||||
|
|
||||||
TerminalSplitNested(
|
TerminalSplitNested(
|
||||||
node: closeableBottomRight(),
|
node: closeableBottomRight(),
|
||||||
|
15
macos/Sources/Ghostty/NSEvent+Extension.swift
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import Cocoa
|
||||||
|
import GhosttyKit
|
||||||
|
|
||||||
|
extension NSEvent {
|
||||||
|
/// Create a Ghostty key event for a given keyboard action.
|
||||||
|
func ghosttyKeyEvent(_ action: ghostty_input_action_e) -> ghostty_input_key_s {
|
||||||
|
var key_ev = ghostty_input_key_s()
|
||||||
|
key_ev.action = action
|
||||||
|
key_ev.mods = Ghostty.ghosttyMods(modifierFlags)
|
||||||
|
key_ev.keycode = UInt32(keyCode)
|
||||||
|
key_ev.text = nil
|
||||||
|
key_ev.composing = false
|
||||||
|
return key_ev
|
||||||
|
}
|
||||||
|
}
|
@@ -66,7 +66,7 @@ extension Ghostty {
|
|||||||
|
|
||||||
/// An enum that is used for the directions that a split focus event can change.
|
/// An enum that is used for the directions that a split focus event can change.
|
||||||
enum SplitFocusDirection {
|
enum SplitFocusDirection {
|
||||||
case previous, next, top, bottom, left, right
|
case previous, next, up, down, left, right
|
||||||
|
|
||||||
/// Initialize from a Ghostty API enum.
|
/// Initialize from a Ghostty API enum.
|
||||||
static func from(direction: ghostty_action_goto_split_e) -> Self? {
|
static func from(direction: ghostty_action_goto_split_e) -> Self? {
|
||||||
@@ -77,11 +77,11 @@ extension Ghostty {
|
|||||||
case GHOSTTY_GOTO_SPLIT_NEXT:
|
case GHOSTTY_GOTO_SPLIT_NEXT:
|
||||||
return .next
|
return .next
|
||||||
|
|
||||||
case GHOSTTY_GOTO_SPLIT_TOP:
|
case GHOSTTY_GOTO_SPLIT_UP:
|
||||||
return .top
|
return .up
|
||||||
|
|
||||||
case GHOSTTY_GOTO_SPLIT_BOTTOM:
|
case GHOSTTY_GOTO_SPLIT_DOWN:
|
||||||
return .bottom
|
return .down
|
||||||
|
|
||||||
case GHOSTTY_GOTO_SPLIT_LEFT:
|
case GHOSTTY_GOTO_SPLIT_LEFT:
|
||||||
return .left
|
return .left
|
||||||
@@ -102,11 +102,11 @@ extension Ghostty {
|
|||||||
case .next:
|
case .next:
|
||||||
return GHOSTTY_GOTO_SPLIT_NEXT
|
return GHOSTTY_GOTO_SPLIT_NEXT
|
||||||
|
|
||||||
case .top:
|
case .up:
|
||||||
return GHOSTTY_GOTO_SPLIT_TOP
|
return GHOSTTY_GOTO_SPLIT_UP
|
||||||
|
|
||||||
case .bottom:
|
case .down:
|
||||||
return GHOSTTY_GOTO_SPLIT_BOTTOM
|
return GHOSTTY_GOTO_SPLIT_DOWN
|
||||||
|
|
||||||
case .left:
|
case .left:
|
||||||
return GHOSTTY_GOTO_SPLIT_LEFT
|
return GHOSTTY_GOTO_SPLIT_LEFT
|
||||||
@@ -159,7 +159,7 @@ extension Ghostty {
|
|||||||
case osc_52_read
|
case osc_52_read
|
||||||
|
|
||||||
/// An application is attempting to write to the clipboard using OSC 52
|
/// An application is attempting to write to the clipboard using OSC 52
|
||||||
case osc_52_write
|
case osc_52_write(OSPasteboard?)
|
||||||
|
|
||||||
/// The text to show in the clipboard confirmation prompt for a given request type
|
/// The text to show in the clipboard confirmation prompt for a given request type
|
||||||
func text() -> String {
|
func text() -> String {
|
||||||
@@ -188,7 +188,7 @@ extension Ghostty {
|
|||||||
case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_READ:
|
case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_READ:
|
||||||
return .osc_52_read
|
return .osc_52_read
|
||||||
case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_WRITE:
|
case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_WRITE:
|
||||||
return .osc_52_write
|
return .osc_52_write(nil)
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -198,6 +198,14 @@ extension Ghostty {
|
|||||||
/// macos-icon
|
/// macos-icon
|
||||||
enum MacOSIcon: String {
|
enum MacOSIcon: String {
|
||||||
case official
|
case official
|
||||||
|
case blueprint
|
||||||
|
case chalkboard
|
||||||
|
case glass
|
||||||
|
case holographic
|
||||||
|
case microchip
|
||||||
|
case paper
|
||||||
|
case retro
|
||||||
|
case xray
|
||||||
case customStyle = "custom-style"
|
case customStyle = "custom-style"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,6 +244,9 @@ extension Notification.Name {
|
|||||||
/// Goto tab. Has tab index in the userinfo.
|
/// Goto tab. Has tab index in the userinfo.
|
||||||
static let ghosttyMoveTab = Notification.Name("com.mitchellh.ghostty.moveTab")
|
static let ghosttyMoveTab = Notification.Name("com.mitchellh.ghostty.moveTab")
|
||||||
static let GhosttyMoveTabKey = ghosttyMoveTab.rawValue
|
static let GhosttyMoveTabKey = ghosttyMoveTab.rawValue
|
||||||
|
|
||||||
|
/// Close tab
|
||||||
|
static let ghosttyCloseTab = Notification.Name("com.mitchellh.ghostty.closeTab")
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: I am moving all of these to Notification.Name extensions over time. This
|
// NOTE: I am moving all of these to Notification.Name extensions over time. This
|
||||||
|
@@ -92,22 +92,6 @@ extension Ghostty {
|
|||||||
windowFocus = false
|
windowFocus = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDrop(of: [.fileURL], isTargeted: nil) { providers in
|
|
||||||
providers.forEach { provider in
|
|
||||||
_ = provider.loadObject(ofClass: URL.self) { url, _ in
|
|
||||||
guard let url = url else { return }
|
|
||||||
let path = Shell.escape(url.path)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
surfaceView.insertText(
|
|
||||||
path,
|
|
||||||
replacementRange: NSMakeRange(0, 0)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// If our geo size changed then we show the resize overlay as configured.
|
// If our geo size changed then we show the resize overlay as configured.
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import AppKit
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import CoreText
|
import CoreText
|
||||||
import UserNotifications
|
import UserNotifications
|
||||||
@@ -12,7 +13,14 @@ extension Ghostty {
|
|||||||
// The current title of the surface as defined by the pty. This can be
|
// The current title of the surface as defined by the pty. This can be
|
||||||
// changed with escape codes. This is public because the callbacks go
|
// changed with escape codes. This is public because the callbacks go
|
||||||
// to the app level and it is set from there.
|
// to the app level and it is set from there.
|
||||||
@Published private(set) var title: String = "👻"
|
@Published private(set) var title: String = "" {
|
||||||
|
didSet {
|
||||||
|
if !title.isEmpty {
|
||||||
|
titleFallbackTimer?.invalidate()
|
||||||
|
titleFallbackTimer = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The current pwd of the surface as defined by the pty. This can be
|
// The current pwd of the surface as defined by the pty. This can be
|
||||||
// changed with escape codes.
|
// changed with escape codes.
|
||||||
@@ -113,6 +121,12 @@ extension Ghostty {
|
|||||||
// A small delay that is introduced before a title change to avoid flickers
|
// A small delay that is introduced before a title change to avoid flickers
|
||||||
private var titleChangeTimer: Timer?
|
private var titleChangeTimer: Timer?
|
||||||
|
|
||||||
|
// A timer to fallback to ghost emoji if no title is set within the grace period
|
||||||
|
private var titleFallbackTimer: Timer?
|
||||||
|
|
||||||
|
/// Event monitor (see individual events for why)
|
||||||
|
private var eventMonitor: Any? = nil
|
||||||
|
|
||||||
// We need to support being a first responder so that we can get input events
|
// We need to support being a first responder so that we can get input events
|
||||||
override var acceptsFirstResponder: Bool { return true }
|
override var acceptsFirstResponder: Bool { return true }
|
||||||
|
|
||||||
@@ -136,6 +150,13 @@ extension Ghostty {
|
|||||||
// can do SOMETHING.
|
// can do SOMETHING.
|
||||||
super.init(frame: NSMakeRect(0, 0, 800, 600))
|
super.init(frame: NSMakeRect(0, 0, 800, 600))
|
||||||
|
|
||||||
|
// Set a timer to show the ghost emoji after 500ms if no title is set
|
||||||
|
titleFallbackTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { [weak self] _ in
|
||||||
|
if let self = self, self.title.isEmpty {
|
||||||
|
self.title = "👻"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Before we initialize the surface we want to register our notifications
|
// Before we initialize the surface we want to register our notifications
|
||||||
// so there is no window where we can't receive them.
|
// so there is no window where we can't receive them.
|
||||||
let center = NotificationCenter.default
|
let center = NotificationCenter.default
|
||||||
@@ -170,6 +191,15 @@ extension Ghostty {
|
|||||||
name: NSWindow.didChangeScreenNotification,
|
name: NSWindow.didChangeScreenNotification,
|
||||||
object: nil)
|
object: nil)
|
||||||
|
|
||||||
|
// Listen for local events that we need to know of outside of
|
||||||
|
// single surface handlers.
|
||||||
|
self.eventMonitor = NSEvent.addLocalMonitorForEvents(
|
||||||
|
matching: [
|
||||||
|
// We need keyUp because command+key events don't trigger keyUp.
|
||||||
|
.keyUp
|
||||||
|
]
|
||||||
|
) { [weak self] event in self?.localEventHandler(event) }
|
||||||
|
|
||||||
// Setup our surface. This will also initialize all the terminal IO.
|
// Setup our surface. This will also initialize all the terminal IO.
|
||||||
let surface_cfg = baseConfig ?? SurfaceConfiguration()
|
let surface_cfg = baseConfig ?? SurfaceConfiguration()
|
||||||
var surface_cfg_c = surface_cfg.ghosttyConfig(view: self)
|
var surface_cfg_c = surface_cfg.ghosttyConfig(view: self)
|
||||||
@@ -201,6 +231,9 @@ extension Ghostty {
|
|||||||
|
|
||||||
ghostty_surface_set_color_scheme(surface, scheme)
|
ghostty_surface_set_color_scheme(surface, scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The UTTypes that can be dragged onto this view.
|
||||||
|
registerForDraggedTypes(Array(Self.dropTypes))
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
@@ -212,6 +245,11 @@ extension Ghostty {
|
|||||||
let center = NotificationCenter.default
|
let center = NotificationCenter.default
|
||||||
center.removeObserver(self)
|
center.removeObserver(self)
|
||||||
|
|
||||||
|
// Remove our event monitor
|
||||||
|
if let eventMonitor {
|
||||||
|
NSEvent.removeMonitor(eventMonitor)
|
||||||
|
}
|
||||||
|
|
||||||
// Whenever the surface is removed, we need to note that our restorable
|
// Whenever the surface is removed, we need to note that our restorable
|
||||||
// state is invalid to prevent the surface from being restored.
|
// state is invalid to prevent the surface from being restored.
|
||||||
invalidateRestorableState()
|
invalidateRestorableState()
|
||||||
@@ -356,6 +394,30 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Local Events
|
||||||
|
|
||||||
|
private func localEventHandler(_ event: NSEvent) -> NSEvent? {
|
||||||
|
return switch event.type {
|
||||||
|
case .keyUp:
|
||||||
|
localEventKeyUp(event)
|
||||||
|
|
||||||
|
default:
|
||||||
|
event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func localEventKeyUp(_ event: NSEvent) -> NSEvent? {
|
||||||
|
// We only care about events with "command" because all others will
|
||||||
|
// trigger the normal responder chain.
|
||||||
|
if (!event.modifierFlags.contains(.command)) { return event }
|
||||||
|
|
||||||
|
// Command keyUp events are never sent to the normal responder chain
|
||||||
|
// so we send them here.
|
||||||
|
guard focused else { return event }
|
||||||
|
self.keyUp(with: event)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Notifications
|
// MARK: - Notifications
|
||||||
|
|
||||||
@objc private func onUpdateRendererHealth(notification: SwiftUI.Notification) {
|
@objc private func onUpdateRendererHealth(notification: SwiftUI.Notification) {
|
||||||
@@ -764,8 +826,23 @@ extension Ghostty {
|
|||||||
// know if these events cleared it.
|
// know if these events cleared it.
|
||||||
let markedTextBefore = markedText.length > 0
|
let markedTextBefore = markedText.length > 0
|
||||||
|
|
||||||
|
// We need to know the keyboard layout before below because some keyboard
|
||||||
|
// input events will change our keyboard layout and we don't want those
|
||||||
|
// going to the terminal.
|
||||||
|
let keyboardIdBefore: String? = if (!markedTextBefore) {
|
||||||
|
KeyboardLayout.id
|
||||||
|
} else {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
self.interpretKeyEvents([translationEvent])
|
self.interpretKeyEvents([translationEvent])
|
||||||
|
|
||||||
|
// If our keyboard changed from this we just assume an input method
|
||||||
|
// grabbed it and do nothing.
|
||||||
|
if (!markedTextBefore && keyboardIdBefore != KeyboardLayout.id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// If we have text, then we've composed a character, send that down. We do this
|
// If we have text, then we've composed a character, send that down. We do this
|
||||||
// first because if we completed a preedit, the text will be available here
|
// first because if we completed a preedit, the text will be available here
|
||||||
// AND we'll have a preedit.
|
// AND we'll have a preedit.
|
||||||
@@ -773,7 +850,7 @@ extension Ghostty {
|
|||||||
if let list = keyTextAccumulator, list.count > 0 {
|
if let list = keyTextAccumulator, list.count > 0 {
|
||||||
handled = true
|
handled = true
|
||||||
for text in list {
|
for text in list {
|
||||||
keyAction(action, event: event, text: text)
|
_ = keyAction(action, event: event, text: text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -783,38 +860,49 @@ extension Ghostty {
|
|||||||
// the preedit.
|
// the preedit.
|
||||||
if (markedText.length > 0 || markedTextBefore) {
|
if (markedText.length > 0 || markedTextBefore) {
|
||||||
handled = true
|
handled = true
|
||||||
keyAction(action, event: event, preedit: markedText.string)
|
_ = keyAction(action, event: event, preedit: markedText.string)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
// No text or anything, we want to handle this manually.
|
// No text or anything, we want to handle this manually.
|
||||||
keyAction(action, event: event)
|
_ = keyAction(action, event: event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func keyUp(with event: NSEvent) {
|
override func keyUp(with event: NSEvent) {
|
||||||
keyAction(GHOSTTY_ACTION_RELEASE, event: event)
|
_ = keyAction(GHOSTTY_ACTION_RELEASE, event: event)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Special case handling for some control keys
|
/// Special case handling for some control keys
|
||||||
override func performKeyEquivalent(with event: NSEvent) -> Bool {
|
override func performKeyEquivalent(with event: NSEvent) -> Bool {
|
||||||
// Only process key down events
|
switch (event.type) {
|
||||||
if (event.type != .keyDown) {
|
case .keyDown:
|
||||||
|
// Continue, we care about key down events
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Any other key event we don't care about. I don't think its even
|
||||||
|
// possible to receive any other event type.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only process events if we're focused. Some key events like C-/ macOS
|
// Only process events if we're focused. Some key events like C-/ macOS
|
||||||
// appears to send to the first view in the hierarchy rather than the
|
// appears to send to the first view in the hierarchy rather than the
|
||||||
// the first responder (I don't know why). This prevents us from handling it.
|
// the first responder (I don't know why). This prevents us from handling it.
|
||||||
|
// Besides C-/, its important we don't process key equivalents if unfocused
|
||||||
|
// because there are other event listeners for that (i.e. AppDelegate's
|
||||||
|
// local event handler).
|
||||||
if (!focused) {
|
if (!focused) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only process keys when Control is active. All known issues we're
|
// If this event as-is would result in a key binding then we send it.
|
||||||
// resolving happen only in this scenario. This probably isn't fully robust
|
if let surface,
|
||||||
// but we can broaden the scope as we find more cases.
|
ghostty_surface_key_is_binding(
|
||||||
if (!event.modifierFlags.contains(.control)) {
|
surface,
|
||||||
return false
|
event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS)) {
|
||||||
|
self.keyDown(with: event)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
let equivalent: String
|
let equivalent: String
|
||||||
@@ -832,14 +920,25 @@ extension Ghostty {
|
|||||||
case "\r":
|
case "\r":
|
||||||
// Pass C-<return> through verbatim
|
// Pass C-<return> through verbatim
|
||||||
// (prevent the default context menu equivalent)
|
// (prevent the default context menu equivalent)
|
||||||
|
if (!event.modifierFlags.contains(.control)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
equivalent = "\r"
|
equivalent = "\r"
|
||||||
|
|
||||||
|
case ".":
|
||||||
|
if (!event.modifierFlags.contains(.command)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
equivalent = "."
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Ignore other events
|
// Ignore other events
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
let newEvent = NSEvent.keyEvent(
|
let finalEvent = NSEvent.keyEvent(
|
||||||
with: .keyDown,
|
with: .keyDown,
|
||||||
location: event.locationInWindow,
|
location: event.locationInWindow,
|
||||||
modifierFlags: event.modifierFlags,
|
modifierFlags: event.modifierFlags,
|
||||||
@@ -852,7 +951,7 @@ extension Ghostty {
|
|||||||
keyCode: event.keyCode
|
keyCode: event.keyCode
|
||||||
)
|
)
|
||||||
|
|
||||||
self.keyDown(with: newEvent!)
|
self.keyDown(with: finalEvent!)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -867,6 +966,9 @@ extension Ghostty {
|
|||||||
default: return
|
default: return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we're in the middle of a preedit, don't do anything with mods.
|
||||||
|
if hasMarkedText() { return }
|
||||||
|
|
||||||
// The keyAction function will do this AGAIN below which sucks to repeat
|
// The keyAction function will do this AGAIN below which sucks to repeat
|
||||||
// but this is super cheap and flagsChanged isn't that common.
|
// but this is super cheap and flagsChanged isn't that common.
|
||||||
let mods = Ghostty.ghosttyMods(event.modifierFlags)
|
let mods = Ghostty.ghosttyMods(event.modifierFlags)
|
||||||
@@ -897,45 +999,38 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
keyAction(action, event: event)
|
_ = keyAction(action, event: event)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func keyAction(_ action: ghostty_input_action_e, event: NSEvent) {
|
private func keyAction(_ action: ghostty_input_action_e, event: NSEvent) -> Bool {
|
||||||
guard let surface = self.surface else { return }
|
guard let surface = self.surface else { return false }
|
||||||
|
return ghostty_surface_key(surface, event.ghosttyKeyEvent(action))
|
||||||
var key_ev = ghostty_input_key_s()
|
|
||||||
key_ev.action = action
|
|
||||||
key_ev.mods = Ghostty.ghosttyMods(event.modifierFlags)
|
|
||||||
key_ev.keycode = UInt32(event.keyCode)
|
|
||||||
key_ev.text = nil
|
|
||||||
key_ev.composing = false
|
|
||||||
ghostty_surface_key(surface, key_ev)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func keyAction(_ action: ghostty_input_action_e, event: NSEvent, preedit: String) {
|
private func keyAction(
|
||||||
guard let surface = self.surface else { return }
|
_ action: ghostty_input_action_e,
|
||||||
|
event: NSEvent, preedit: String
|
||||||
|
) -> Bool {
|
||||||
|
guard let surface = self.surface else { return false }
|
||||||
|
|
||||||
preedit.withCString { ptr in
|
return preedit.withCString { ptr in
|
||||||
var key_ev = ghostty_input_key_s()
|
var key_ev = event.ghosttyKeyEvent(action)
|
||||||
key_ev.action = action
|
|
||||||
key_ev.mods = Ghostty.ghosttyMods(event.modifierFlags)
|
|
||||||
key_ev.keycode = UInt32(event.keyCode)
|
|
||||||
key_ev.text = ptr
|
key_ev.text = ptr
|
||||||
key_ev.composing = true
|
key_ev.composing = true
|
||||||
ghostty_surface_key(surface, key_ev)
|
return ghostty_surface_key(surface, key_ev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func keyAction(_ action: ghostty_input_action_e, event: NSEvent, text: String) {
|
private func keyAction(
|
||||||
guard let surface = self.surface else { return }
|
_ action: ghostty_input_action_e,
|
||||||
|
event: NSEvent, text: String
|
||||||
|
) -> Bool {
|
||||||
|
guard let surface = self.surface else { return false }
|
||||||
|
|
||||||
text.withCString { ptr in
|
return text.withCString { ptr in
|
||||||
var key_ev = ghostty_input_key_s()
|
var key_ev = event.ghosttyKeyEvent(action)
|
||||||
key_ev.action = action
|
|
||||||
key_ev.mods = Ghostty.ghosttyMods(event.modifierFlags)
|
|
||||||
key_ev.keycode = UInt32(event.keyCode)
|
|
||||||
key_ev.text = ptr
|
key_ev.text = ptr
|
||||||
ghostty_surface_key(surface, key_ev)
|
return ghostty_surface_key(surface, key_ev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1053,6 +1148,14 @@ extension Ghostty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBAction func pasteSelection(_ sender: Any?) {
|
||||||
|
guard let surface = self.surface else { return }
|
||||||
|
let action = "paste_from_selection"
|
||||||
|
if (!ghostty_surface_binding_action(surface, action, UInt(action.count))) {
|
||||||
|
AppDelegate.logger.warning("action failed action=\(action)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@IBAction override func selectAll(_ sender: Any?) {
|
@IBAction override func selectAll(_ sender: Any?) {
|
||||||
guard let surface = self.surface else { return }
|
guard let surface = self.surface else { return }
|
||||||
let action = "select_all"
|
let action = "select_all"
|
||||||
@@ -1374,3 +1477,78 @@ extension Ghostty.SurfaceView: NSServicesMenuRequestor {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: NSMenuItemValidation
|
||||||
|
|
||||||
|
extension Ghostty.SurfaceView: NSMenuItemValidation {
|
||||||
|
func validateMenuItem(_ item: NSMenuItem) -> Bool {
|
||||||
|
switch item.action {
|
||||||
|
case #selector(pasteSelection):
|
||||||
|
let pb = NSPasteboard.ghosttySelection
|
||||||
|
guard let str = pb.getOpinionatedStringContents() else { return false }
|
||||||
|
return !str.isEmpty
|
||||||
|
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: NSDraggingDestination
|
||||||
|
|
||||||
|
extension Ghostty.SurfaceView {
|
||||||
|
static let dropTypes: Set<NSPasteboard.PasteboardType> = [
|
||||||
|
.string,
|
||||||
|
.fileURL,
|
||||||
|
.URL
|
||||||
|
]
|
||||||
|
|
||||||
|
override func draggingEntered(_ sender: any NSDraggingInfo) -> NSDragOperation {
|
||||||
|
guard let types = sender.draggingPasteboard.types else { return [] }
|
||||||
|
|
||||||
|
// If the dragging object contains none of our types then we return none.
|
||||||
|
// This shouldn't happen because AppKit should guarantee that we only
|
||||||
|
// receive types we registered for but its good to check.
|
||||||
|
if Set(types).isDisjoint(with: Self.dropTypes) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
// We use copy to get the proper icon
|
||||||
|
return .copy
|
||||||
|
}
|
||||||
|
|
||||||
|
override func performDragOperation(_ sender: any NSDraggingInfo) -> Bool {
|
||||||
|
let pb = sender.draggingPasteboard
|
||||||
|
|
||||||
|
let content: String?
|
||||||
|
if let url = pb.string(forType: .URL) {
|
||||||
|
// URLs first, they get escaped as-is.
|
||||||
|
content = Ghostty.Shell.escape(url)
|
||||||
|
} else if let urls = pb.readObjects(forClasses: [NSURL.self]) as? [URL],
|
||||||
|
urls.count > 0 {
|
||||||
|
// File URLs next. They get escaped individually and then joined by a
|
||||||
|
// space if there are multiple.
|
||||||
|
content = urls
|
||||||
|
.map { Ghostty.Shell.escape($0.path) }
|
||||||
|
.joined(separator: " ")
|
||||||
|
} else if let str = pb.string(forType: .string) {
|
||||||
|
// Strings are not escaped because they may be copy/pasting a
|
||||||
|
// command they want to execute.
|
||||||
|
content = str
|
||||||
|
} else {
|
||||||
|
content = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let content {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.insertText(
|
||||||
|
content,
|
||||||
|
replacementRange: NSMakeRange(0, 0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -10,6 +10,7 @@ import AppKit
|
|||||||
typealias OSView = NSView
|
typealias OSView = NSView
|
||||||
typealias OSColor = NSColor
|
typealias OSColor = NSColor
|
||||||
typealias OSSize = NSSize
|
typealias OSSize = NSSize
|
||||||
|
typealias OSPasteboard = NSPasteboard
|
||||||
|
|
||||||
protocol OSViewRepresentable: NSViewRepresentable where NSViewType == OSViewType {
|
protocol OSViewRepresentable: NSViewRepresentable where NSViewType == OSViewType {
|
||||||
associatedtype OSViewType: NSView
|
associatedtype OSViewType: NSView
|
||||||
@@ -34,6 +35,7 @@ import UIKit
|
|||||||
typealias OSView = UIView
|
typealias OSView = UIView
|
||||||
typealias OSColor = UIColor
|
typealias OSColor = UIColor
|
||||||
typealias OSSize = CGSize
|
typealias OSSize = CGSize
|
||||||
|
typealias OSPasteboard = UIPasteboard
|
||||||
|
|
||||||
protocol OSViewRepresentable: UIViewRepresentable {
|
protocol OSViewRepresentable: UIViewRepresentable {
|
||||||
associatedtype OSViewType: UIView
|
associatedtype OSViewType: UIView
|
||||||
|
38
macos/Sources/Helpers/Dock.swift
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import Cocoa
|
||||||
|
|
||||||
|
// Private API to get Dock location
|
||||||
|
@_silgen_name("CoreDockGetOrientationAndPinning")
|
||||||
|
func CoreDockGetOrientationAndPinning(
|
||||||
|
_ outOrientation: UnsafeMutablePointer<Int32>,
|
||||||
|
_ outPinning: UnsafeMutablePointer<Int32>)
|
||||||
|
|
||||||
|
// Private API to get the current Dock auto-hide state
|
||||||
|
@_silgen_name("CoreDockGetAutoHideEnabled")
|
||||||
|
func CoreDockGetAutoHideEnabled() -> Bool
|
||||||
|
|
||||||
|
// Toggles the Dock's auto-hide state
|
||||||
|
@_silgen_name("CoreDockSetAutoHideEnabled")
|
||||||
|
func CoreDockSetAutoHideEnabled(_ flag: Bool)
|
||||||
|
|
||||||
|
enum DockOrientation: Int {
|
||||||
|
case top = 1
|
||||||
|
case bottom = 2
|
||||||
|
case left = 3
|
||||||
|
case right = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
class Dock {
|
||||||
|
/// Returns the orientation of the dock or nil if it can't be determined.
|
||||||
|
static var orientation: DockOrientation? {
|
||||||
|
var orientation: Int32 = 0
|
||||||
|
var pinning: Int32 = 0
|
||||||
|
CoreDockGetOrientationAndPinning(&orientation, &pinning)
|
||||||
|
return .init(rawValue: Int(orientation)) ?? nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the dock autohide.
|
||||||
|
static var autoHideEnabled: Bool {
|
||||||
|
get { return CoreDockGetAutoHideEnabled() }
|
||||||
|
set { CoreDockSetAutoHideEnabled(newValue) }
|
||||||
|
}
|
||||||
|
}
|
@@ -307,21 +307,21 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
|||||||
// MARK: Dock
|
// MARK: Dock
|
||||||
|
|
||||||
private func hideDock() {
|
private func hideDock() {
|
||||||
NSApp.presentationOptions.insert(.autoHideDock)
|
NSApp.acquirePresentationOption(.autoHideDock)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func unhideDock() {
|
private func unhideDock() {
|
||||||
NSApp.presentationOptions.remove(.autoHideDock)
|
NSApp.releasePresentationOption(.autoHideDock)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Menu
|
// MARK: Menu
|
||||||
|
|
||||||
func hideMenu() {
|
func hideMenu() {
|
||||||
NSApp.presentationOptions.insert(.autoHideMenuBar)
|
NSApp.acquirePresentationOption(.autoHideMenuBar)
|
||||||
}
|
}
|
||||||
|
|
||||||
func unhideMenu() {
|
func unhideMenu() {
|
||||||
NSApp.presentationOptions.remove(.autoHideMenuBar)
|
NSApp.releasePresentationOption(.autoHideMenuBar)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The state that must be saved for non-native fullscreen to exit fullscreen.
|
/// The state that must be saved for non-native fullscreen to exit fullscreen.
|
||||||
|
14
macos/Sources/Helpers/KeyboardLayout.swift
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import Carbon
|
||||||
|
|
||||||
|
class KeyboardLayout {
|
||||||
|
/// Return a string ID of the current keyboard input source.
|
||||||
|
static var id: String? {
|
||||||
|
if let source = TISCopyCurrentKeyboardInputSource()?.takeRetainedValue(),
|
||||||
|
let sourceIdPointer = TISGetInputSourceProperty(source, kTISPropertyInputSourceID) {
|
||||||
|
let sourceId = unsafeBitCast(sourceIdPointer, to: CFString.self)
|
||||||
|
return sourceId as String
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
34
macos/Sources/Helpers/LastWindowPosition.swift
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import Cocoa
|
||||||
|
|
||||||
|
/// Manages the persistence and restoration of window positions across app launches.
|
||||||
|
class LastWindowPosition {
|
||||||
|
static let shared = LastWindowPosition()
|
||||||
|
|
||||||
|
private let positionKey = "NSWindowLastPosition"
|
||||||
|
|
||||||
|
func save(_ window: NSWindow) {
|
||||||
|
let origin = window.frame.origin
|
||||||
|
let point = [origin.x, origin.y]
|
||||||
|
UserDefaults.standard.set(point, forKey: positionKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func restore(_ window: NSWindow) -> Bool {
|
||||||
|
guard let points = UserDefaults.standard.array(forKey: positionKey) as? [Double],
|
||||||
|
points.count == 2 else { return false }
|
||||||
|
|
||||||
|
let lastPosition = CGPoint(x: points[0], y: points[1])
|
||||||
|
|
||||||
|
guard let screen = window.screen ?? NSScreen.main else { return false }
|
||||||
|
let visibleFrame = screen.visibleFrame
|
||||||
|
|
||||||
|
var newFrame = window.frame
|
||||||
|
newFrame.origin = lastPosition
|
||||||
|
if !visibleFrame.contains(newFrame.origin) {
|
||||||
|
newFrame.origin.x = max(visibleFrame.minX, min(visibleFrame.maxX - newFrame.width, newFrame.origin.x))
|
||||||
|
newFrame.origin.y = max(visibleFrame.minY, min(visibleFrame.maxY - newFrame.height, newFrame.origin.y))
|
||||||
|
}
|
||||||
|
|
||||||
|
window.setFrame(newFrame, display: true)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
31
macos/Sources/Helpers/NSApplication+Extension.swift
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import Cocoa
|
||||||
|
|
||||||
|
extension NSApplication {
|
||||||
|
private static var presentationOptionCounts: [NSApplication.PresentationOptions.Element: UInt] = [:]
|
||||||
|
|
||||||
|
/// Add a presentation option to the application and main a reference count so that and equal
|
||||||
|
/// number of pops is required to disable it. This is useful so that multiple classes can affect global
|
||||||
|
/// app state without overriding others.
|
||||||
|
func acquirePresentationOption(_ option: NSApplication.PresentationOptions.Element) {
|
||||||
|
Self.presentationOptionCounts[option, default: 0] += 1
|
||||||
|
presentationOptions.insert(option)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See acquirePresentationOption
|
||||||
|
func releasePresentationOption(_ option: NSApplication.PresentationOptions.Element) {
|
||||||
|
guard let value = Self.presentationOptionCounts[option] else { return }
|
||||||
|
guard value > 0 else { return }
|
||||||
|
if (value == 1) {
|
||||||
|
presentationOptions.remove(option)
|
||||||
|
Self.presentationOptionCounts.removeValue(forKey: option)
|
||||||
|
} else {
|
||||||
|
Self.presentationOptionCounts[option] = value - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension NSApplication.PresentationOptions.Element: @retroactive Hashable {
|
||||||
|
public func hash(into hasher: inout Hasher) {
|
||||||
|
hasher.combine(rawValue)
|
||||||
|
}
|
||||||
|
}
|
@@ -1,17 +1,39 @@
|
|||||||
import AppKit
|
import AppKit
|
||||||
|
import GhosttyKit
|
||||||
|
|
||||||
extension NSPasteboard {
|
extension NSPasteboard {
|
||||||
|
/// The pasteboard to used for Ghostty selection.
|
||||||
|
static var ghosttySelection: NSPasteboard = {
|
||||||
|
NSPasteboard(name: .init("com.mitchellh.ghostty.selection"))
|
||||||
|
}()
|
||||||
|
|
||||||
/// Gets the contents of the pasteboard as a string following a specific set of semantics.
|
/// Gets the contents of the pasteboard as a string following a specific set of semantics.
|
||||||
/// Does these things in order:
|
/// Does these things in order:
|
||||||
/// - Tries to get the absolute filesystem path of the file in the pasteboard if there is one.
|
/// - Tries to get the absolute filesystem path of the file in the pasteboard if there is one and ensures the file path is properly escaped.
|
||||||
/// - Tries to get any string from the pasteboard.
|
/// - Tries to get any string from the pasteboard.
|
||||||
/// If all of the above fail, returns None.
|
/// If all of the above fail, returns None.
|
||||||
func getOpinionatedStringContents() -> String? {
|
func getOpinionatedStringContents() -> String? {
|
||||||
if let file = self.string(forType: .fileURL) {
|
if let urls = readObjects(forClasses: [NSURL.self]) as? [URL],
|
||||||
if let path = NSURL(string: file)?.path {
|
urls.count > 0 {
|
||||||
return path
|
return urls
|
||||||
}
|
.map { $0.isFileURL ? Ghostty.Shell.escape($0.path) : $0.absoluteString }
|
||||||
|
.joined(separator: " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.string(forType: .string)
|
return self.string(forType: .string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The pasteboard for the Ghostty enum type.
|
||||||
|
static func ghostty(_ clipboard: ghostty_clipboard_e) -> NSPasteboard? {
|
||||||
|
switch (clipboard) {
|
||||||
|
case GHOSTTY_CLIPBOARD_STANDARD:
|
||||||
|
return Self.general
|
||||||
|
|
||||||
|
case GHOSTTY_CLIPBOARD_SELECTION:
|
||||||
|
return Self.ghosttySelection
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
9
macos/Sources/Helpers/Weak.swift
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/// A wrapper that holds a weak reference to an object. This lets us create native containers
|
||||||
|
/// of weak references.
|
||||||
|
class Weak<T: AnyObject> {
|
||||||
|
weak var value: T?
|
||||||
|
|
||||||
|
init(_ value: T) {
|
||||||
|
self.value = value
|
||||||
|
}
|
||||||
|
}
|
@@ -1,63 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Nothing in this script should fail.
|
|
||||||
set -e
|
|
||||||
|
|
||||||
CACHE_HASH_FILE="$(realpath "$(dirname "$0")/../zigCacheHash.nix")"
|
|
||||||
|
|
||||||
help() {
|
|
||||||
echo ""
|
|
||||||
echo "To fix, please (manually) re-run the script from the repository root,"
|
|
||||||
echo "commit, and push the update:"
|
|
||||||
echo ""
|
|
||||||
echo " ./nix/build-support/check-zig-cache-hash.sh --update"
|
|
||||||
echo " git add nix/zigCacheHash.nix"
|
|
||||||
echo " git commit -m \"nix: update Zig cache hash\""
|
|
||||||
echo " git push"
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ -f "${CACHE_HASH_FILE}" ]; then
|
|
||||||
OLD_CACHE_HASH="$(nix eval --raw --file "${CACHE_HASH_FILE}")"
|
|
||||||
elif [ "$1" != "--update" ]; then
|
|
||||||
echo -e "\nERROR: Zig cache hash file missing."
|
|
||||||
help
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
ZIG_GLOBAL_CACHE_DIR="$(mktemp --directory --suffix nix-zig-cache)"
|
|
||||||
export ZIG_GLOBAL_CACHE_DIR
|
|
||||||
|
|
||||||
# This is not 100% necessary in CI but is helpful when running locally to keep
|
|
||||||
# a local workstation clean.
|
|
||||||
trap 'rm -rf "${ZIG_GLOBAL_CACHE_DIR}"' EXIT
|
|
||||||
|
|
||||||
# Run Zig and download the cache to the temporary directory.
|
|
||||||
|
|
||||||
sh ./nix/build-support/fetch-zig-cache.sh
|
|
||||||
|
|
||||||
# Now, calculate the hash.
|
|
||||||
ZIG_CACHE_HASH="sha256-$(nix-hash --type sha256 --to-base64 "$(nix-hash --type sha256 "${ZIG_GLOBAL_CACHE_DIR}")")"
|
|
||||||
|
|
||||||
if [ "${OLD_CACHE_HASH}" == "${ZIG_CACHE_HASH}" ]; then
|
|
||||||
echo -e "\nOK: Zig cache store hash unchanged."
|
|
||||||
exit 0
|
|
||||||
elif [ "$1" != "--update" ]; then
|
|
||||||
echo -e "\nERROR: The Zig cache store hash has updated."
|
|
||||||
echo ""
|
|
||||||
echo " * Old hash: ${OLD_CACHE_HASH}"
|
|
||||||
echo " * New hash: ${ZIG_CACHE_HASH}"
|
|
||||||
help
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo -e "\nNew Zig cache store hash: ${ZIG_CACHE_HASH}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Write out the cache file
|
|
||||||
cat > "${CACHE_HASH_FILE}" <<EOS
|
|
||||||
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for
|
|
||||||
# more details.
|
|
||||||
"${ZIG_CACHE_HASH}"
|
|
||||||
EOS
|
|
||||||
|
|
||||||
echo -e "\nOK: Wrote new hash to file: ${CACHE_HASH_FILE}"
|
|
64
nix/build-support/check-zig-cache.sh
Executable file
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Nothing in this script should fail.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
WORK_DIR=$(mktemp -d)
|
||||||
|
|
||||||
|
if [[ ! "$WORK_DIR" || ! -d "$WORK_DIR" ]]; then
|
||||||
|
echo "could not create temp dir"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
function cleanup {
|
||||||
|
rm -rf "$WORK_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
help() {
|
||||||
|
echo ""
|
||||||
|
echo "To fix, please (manually) re-run the script from the repository root,"
|
||||||
|
echo "commit, and submit a PR with the update:"
|
||||||
|
echo ""
|
||||||
|
echo " ./nix/build-support/check-zig-cache-hash.sh --update"
|
||||||
|
echo " git add build.zig.zon.nix"
|
||||||
|
echo " git commit -m \"nix: update build.zig.zon.nix\""
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
BUILD_ZIG_ZON="$(realpath "$(dirname "$0")/../../build.zig.zon")"
|
||||||
|
BUILD_ZIG_ZON_LOCK="$(realpath "$(dirname "$0")/../../build.zig.zon2json-lock")"
|
||||||
|
BUILD_ZIG_ZON_NIX="$(realpath "$(dirname "$0")/../../build.zig.zon.nix")"
|
||||||
|
|
||||||
|
if [ -f "${BUILD_ZIG_ZON_NIX}" ]; then
|
||||||
|
OLD_HASH=$(sha512sum "${BUILD_ZIG_ZON_NIX}" | awk '{print $1}')
|
||||||
|
elif [ "$1" != "--update" ]; then
|
||||||
|
echo -e "\nERROR: build.zig.zon.nix missing."
|
||||||
|
help
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$BUILD_ZIG_ZON_LOCK"
|
||||||
|
zon2nix "$BUILD_ZIG_ZON" > "$WORK_DIR/build.zig.zon.nix"
|
||||||
|
alejandra --quiet "$WORK_DIR/build.zig.zon.nix"
|
||||||
|
rm -f "$BUILD_ZIG_ZON_LOCK"
|
||||||
|
|
||||||
|
NEW_HASH=$(sha512sum "$WORK_DIR/build.zig.zon.nix" | awk '{print $1}')
|
||||||
|
|
||||||
|
if [ "${OLD_HASH}" == "${NEW_HASH}" ]; then
|
||||||
|
echo -e "\nOK: build.zig.zon.nix unchanged."
|
||||||
|
exit 0
|
||||||
|
elif [ "$1" != "--update" ]; then
|
||||||
|
echo -e "\nERROR: build.zig.zon.nix needs to be updated."
|
||||||
|
echo ""
|
||||||
|
echo " * Old hash: ${OLD_HASH}"
|
||||||
|
echo " * New hash: ${NEW_HASH}"
|
||||||
|
help
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
mv "$WORK_DIR/build.zig.zon.nix" "$BUILD_ZIG_ZON_NIX"
|
||||||
|
echo -e "\nOK: build.zig.zon.nix updated."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
@@ -1,39 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Because Zig does not fetch recursive dependencies when you run `zig build
|
|
||||||
# --fetch` (see https://github.com/ziglang/zig/issues/20976) we need to do some
|
|
||||||
# extra work to fetch everything that we actually need to build without Internet
|
|
||||||
# access (such as when building a Nix package).
|
|
||||||
#
|
|
||||||
# An example of this happening:
|
|
||||||
#
|
|
||||||
# error: builder for '/nix/store/cx8qcwrhjmjxik2547fw99v5j6np5san-ghostty-0.1.0.drv' failed with exit code 1;
|
|
||||||
# la/build/tmp.xgHOheUF7V/p/12208cfdda4d5fdbc81b0c44b82e4d6dba2d4a86bff644a153e026fdfc80f8469133/build.zig.zon:7:20: error: unable to discover remote git server capabilities: TemporaryNameServerFailure
|
|
||||||
# > .url = "git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e",
|
|
||||||
# > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# > /build/tmp.xgHOheUF7V/p/12208cfdda4d5fdbc81b0c44b82e4d6dba2d4a86bff644a153e026fdfc80f8469133/build.zig.zon:16:20: error: unable to discover remote git server capabilities: TemporaryNameServerFailure
|
|
||||||
# > .url = "git+https://github.com/mitchellh/libxev#f6a672a78436d8efee1aa847a43a900ad773618b",
|
|
||||||
# > ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
# >
|
|
||||||
# For full logs, run 'nix log /nix/store/cx8qcwrhjmjxik2547fw99v5j6np5san-ghostty-0.1.0.drv'.
|
|
||||||
#
|
|
||||||
# To update this script, add any failing URLs with a line like this:
|
|
||||||
#
|
|
||||||
# zig fetch <url>
|
|
||||||
#
|
|
||||||
# Periodically old URLs may need to be cleaned out.
|
|
||||||
#
|
|
||||||
# Hopefully when the Zig issue is fixed this script can be eliminated in favor
|
|
||||||
# of a plain `zig build --fetch`.
|
|
||||||
|
|
||||||
if [ -z ${ZIG_GLOBAL_CACHE_DIR+x} ]
|
|
||||||
then
|
|
||||||
echo "must set ZIG_GLOBAL_CACHE_DIR!"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
zig build --fetch
|
|
||||||
zig fetch git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e
|
|
||||||
zig fetch git+https://github.com/mitchellh/libxev#f6a672a78436d8efee1aa847a43a900ad773618b
|
|
@@ -30,7 +30,9 @@
|
|||||||
glib,
|
glib,
|
||||||
glslang,
|
glslang,
|
||||||
gtk4,
|
gtk4,
|
||||||
|
gobject-introspection,
|
||||||
libadwaita,
|
libadwaita,
|
||||||
|
blueprint-compiler,
|
||||||
adwaita-icon-theme,
|
adwaita-icon-theme,
|
||||||
hicolor-icon-theme,
|
hicolor-icon-theme,
|
||||||
harfbuzz,
|
harfbuzz,
|
||||||
@@ -51,6 +53,11 @@
|
|||||||
pandoc,
|
pandoc,
|
||||||
hyperfine,
|
hyperfine,
|
||||||
typos,
|
typos,
|
||||||
|
wayland,
|
||||||
|
wayland-scanner,
|
||||||
|
wayland-protocols,
|
||||||
|
zig2nix,
|
||||||
|
system,
|
||||||
}: let
|
}: let
|
||||||
# See package.nix. Keep in sync.
|
# See package.nix. Keep in sync.
|
||||||
rpathLibs =
|
rpathLibs =
|
||||||
@@ -80,6 +87,8 @@
|
|||||||
libadwaita
|
libadwaita
|
||||||
gtk4
|
gtk4
|
||||||
glib
|
glib
|
||||||
|
gobject-introspection
|
||||||
|
wayland
|
||||||
];
|
];
|
||||||
in
|
in
|
||||||
mkShell {
|
mkShell {
|
||||||
@@ -96,6 +105,7 @@ in
|
|||||||
scdoc
|
scdoc
|
||||||
zig
|
zig
|
||||||
zip
|
zip
|
||||||
|
zig2nix.packages.${system}.zon2nix
|
||||||
|
|
||||||
# For web and wasm stuff
|
# For web and wasm stuff
|
||||||
nodejs
|
nodejs
|
||||||
@@ -150,9 +160,14 @@ in
|
|||||||
libXrandr
|
libXrandr
|
||||||
|
|
||||||
# Only needed for GTK builds
|
# Only needed for GTK builds
|
||||||
|
blueprint-compiler
|
||||||
libadwaita
|
libadwaita
|
||||||
gtk4
|
gtk4
|
||||||
glib
|
glib
|
||||||
|
gobject-introspection
|
||||||
|
wayland
|
||||||
|
wayland-scanner
|
||||||
|
wayland-protocols
|
||||||
];
|
];
|
||||||
|
|
||||||
# This should be set onto the rpath of the ghostty binary if you want
|
# This should be set onto the rpath of the ghostty binary if you want
|
||||||
|
160
nix/package.nix
@@ -2,6 +2,7 @@
|
|||||||
lib,
|
lib,
|
||||||
stdenv,
|
stdenv,
|
||||||
bzip2,
|
bzip2,
|
||||||
|
callPackage,
|
||||||
expat,
|
expat,
|
||||||
fontconfig,
|
fontconfig,
|
||||||
freetype,
|
freetype,
|
||||||
@@ -10,13 +11,11 @@
|
|||||||
oniguruma,
|
oniguruma,
|
||||||
zlib,
|
zlib,
|
||||||
libGL,
|
libGL,
|
||||||
libX11,
|
|
||||||
libXcursor,
|
|
||||||
libXi,
|
|
||||||
libXrandr,
|
|
||||||
glib,
|
glib,
|
||||||
gtk4,
|
gtk4,
|
||||||
|
gobject-introspection,
|
||||||
libadwaita,
|
libadwaita,
|
||||||
|
blueprint-compiler,
|
||||||
wrapGAppsHook4,
|
wrapGAppsHook4,
|
||||||
gsettings-desktop-schemas,
|
gsettings-desktop-schemas,
|
||||||
git,
|
git,
|
||||||
@@ -26,7 +25,15 @@
|
|||||||
pandoc,
|
pandoc,
|
||||||
revision ? "dirty",
|
revision ? "dirty",
|
||||||
optimize ? "Debug",
|
optimize ? "Debug",
|
||||||
x11 ? true,
|
enableX11 ? true,
|
||||||
|
libX11,
|
||||||
|
libXcursor,
|
||||||
|
libXi,
|
||||||
|
libXrandr,
|
||||||
|
enableWayland ? true,
|
||||||
|
wayland,
|
||||||
|
wayland-protocols,
|
||||||
|
wayland-scanner,
|
||||||
}: let
|
}: let
|
||||||
# The Zig hook has no way to select the release type without actual
|
# The Zig hook has no way to select the release type without actual
|
||||||
# overriding of the default flags.
|
# overriding of the default flags.
|
||||||
@@ -36,92 +43,52 @@
|
|||||||
# ultimately acted on and has made its way to a nixpkgs implementation, this
|
# ultimately acted on and has made its way to a nixpkgs implementation, this
|
||||||
# can probably be removed in favor of that.
|
# can probably be removed in favor of that.
|
||||||
zig_hook = zig_0_13.hook.overrideAttrs {
|
zig_hook = zig_0_13.hook.overrideAttrs {
|
||||||
zig_default_flags = "-Dcpu=baseline -Doptimize=${optimize}";
|
zig_default_flags = "-Dcpu=baseline -Doptimize=${optimize} --color off";
|
||||||
};
|
|
||||||
|
|
||||||
# We limit source like this to try and reduce the amount of rebuilds as possible
|
|
||||||
# thus we only provide the source that is needed for the build
|
|
||||||
#
|
|
||||||
# NOTE: as of the current moment only linux files are provided,
|
|
||||||
# since darwin support is not finished
|
|
||||||
src = lib.fileset.toSource {
|
|
||||||
root = ../.;
|
|
||||||
fileset = lib.fileset.intersection (lib.fileset.fromSource (lib.sources.cleanSource ../.)) (
|
|
||||||
lib.fileset.unions [
|
|
||||||
../dist/linux
|
|
||||||
../conformance
|
|
||||||
../images
|
|
||||||
../include
|
|
||||||
../pkg
|
|
||||||
../src
|
|
||||||
../vendor
|
|
||||||
../build.zig
|
|
||||||
../build.zig.zon
|
|
||||||
./build-support/fetch-zig-cache.sh
|
|
||||||
]
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
# This hash is the computation of the zigCache fixed-output derivation. This
|
|
||||||
# allows us to use remote package dependencies without breaking the sandbox.
|
|
||||||
#
|
|
||||||
# This will need updating whenever dependencies get updated (e.g. changes are
|
|
||||||
# made to zig.build.zon). If you see that the main build is trying to reach
|
|
||||||
# out to the internet and failing, this is likely the cause. Change this
|
|
||||||
# value back to lib.fakeHash, and re-run. The build failure should emit the
|
|
||||||
# updated hash, which of course, should be validated before updating here.
|
|
||||||
#
|
|
||||||
# (It's also possible that you might see a hash mismatch - without the
|
|
||||||
# network errors - if you don't have a previous instance of the cache
|
|
||||||
# derivation in your store already. If so, just update the value as above.)
|
|
||||||
zigCacheHash = import ./zigCacheHash.nix;
|
|
||||||
|
|
||||||
zigCache = stdenv.mkDerivation {
|
|
||||||
inherit src;
|
|
||||||
name = "ghostty-cache";
|
|
||||||
nativeBuildInputs = [
|
|
||||||
git
|
|
||||||
zig_hook
|
|
||||||
];
|
|
||||||
|
|
||||||
dontConfigure = true;
|
|
||||||
dontUseZigBuild = true;
|
|
||||||
dontUseZigInstall = true;
|
|
||||||
dontFixup = true;
|
|
||||||
|
|
||||||
buildPhase = ''
|
|
||||||
runHook preBuild
|
|
||||||
|
|
||||||
sh ./nix/build-support/fetch-zig-cache.sh
|
|
||||||
|
|
||||||
runHook postBuild
|
|
||||||
'';
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
runHook preInstall
|
|
||||||
|
|
||||||
cp -r --reflink=auto $ZIG_GLOBAL_CACHE_DIR $out
|
|
||||||
|
|
||||||
runHook postInstall
|
|
||||||
'';
|
|
||||||
|
|
||||||
outputHashMode = "recursive";
|
|
||||||
outputHash = zigCacheHash;
|
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
stdenv.mkDerivation (finalAttrs: {
|
stdenv.mkDerivation (finalAttrs: {
|
||||||
pname = "ghostty";
|
pname = "ghostty";
|
||||||
version = "1.0.1";
|
version = "1.1.2";
|
||||||
inherit src;
|
|
||||||
|
|
||||||
nativeBuildInputs = [
|
# We limit source like this to try and reduce the amount of rebuilds as possible
|
||||||
git
|
# thus we only provide the source that is needed for the build
|
||||||
ncurses
|
#
|
||||||
pandoc
|
# NOTE: as of the current moment only linux files are provided,
|
||||||
pkg-config
|
# since darwin support is not finished
|
||||||
zig_hook
|
src = lib.fileset.toSource {
|
||||||
wrapGAppsHook4
|
root = ../.;
|
||||||
];
|
fileset = lib.fileset.intersection (lib.fileset.fromSource (lib.sources.cleanSource ../.)) (
|
||||||
|
lib.fileset.unions [
|
||||||
|
../dist/linux
|
||||||
|
../images
|
||||||
|
../include
|
||||||
|
../pkg
|
||||||
|
../src
|
||||||
|
../vendor
|
||||||
|
../build.zig
|
||||||
|
../build.zig.zon
|
||||||
|
../build.zig.zon.nix
|
||||||
|
]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
deps = callPackage ../build.zig.zon.nix {name = "ghostty-cache-${finalAttrs.version}";};
|
||||||
|
|
||||||
|
nativeBuildInputs =
|
||||||
|
[
|
||||||
|
git
|
||||||
|
ncurses
|
||||||
|
pandoc
|
||||||
|
pkg-config
|
||||||
|
zig_hook
|
||||||
|
gobject-introspection
|
||||||
|
wrapGAppsHook4
|
||||||
|
blueprint-compiler
|
||||||
|
]
|
||||||
|
++ lib.optionals enableWayland [
|
||||||
|
wayland-scanner
|
||||||
|
wayland-protocols
|
||||||
|
];
|
||||||
|
|
||||||
buildInputs =
|
buildInputs =
|
||||||
[
|
[
|
||||||
@@ -142,22 +109,25 @@ in
|
|||||||
glib
|
glib
|
||||||
gsettings-desktop-schemas
|
gsettings-desktop-schemas
|
||||||
]
|
]
|
||||||
++ lib.optionals x11 [
|
++ lib.optionals enableX11 [
|
||||||
libX11
|
libX11
|
||||||
libXcursor
|
libXcursor
|
||||||
libXi
|
libXi
|
||||||
libXrandr
|
libXrandr
|
||||||
|
]
|
||||||
|
++ lib.optionals enableWayland [
|
||||||
|
wayland
|
||||||
];
|
];
|
||||||
|
|
||||||
dontConfigure = true;
|
dontConfigure = true;
|
||||||
|
|
||||||
zigBuildFlags = "-Dversion-string=${finalAttrs.version}-${revision}-nix -Dgtk-x11=${lib.boolToString x11}";
|
zigBuildFlags = [
|
||||||
|
"--system"
|
||||||
preBuild = ''
|
"${finalAttrs.deps}"
|
||||||
rm -rf $ZIG_GLOBAL_CACHE_DIR
|
"-Dversion-string=${finalAttrs.version}-${revision}-nix"
|
||||||
cp -r --reflink=auto ${zigCache} $ZIG_GLOBAL_CACHE_DIR
|
"-Dgtk-x11=${lib.boolToString enableX11}"
|
||||||
chmod u+rwX -R $ZIG_GLOBAL_CACHE_DIR
|
"-Dgtk-wayland=${lib.boolToString enableWayland}"
|
||||||
'';
|
];
|
||||||
|
|
||||||
outputs = [
|
outputs = [
|
||||||
"out"
|
"out"
|
||||||
@@ -191,7 +161,7 @@ in
|
|||||||
'';
|
'';
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
homepage = "https://github.com/ghostty-org/ghostty";
|
homepage = "https://ghostty.org";
|
||||||
license = lib.licenses.mit;
|
license = lib.licenses.mit;
|
||||||
platforms = [
|
platforms = [
|
||||||
"x86_64-linux"
|
"x86_64-linux"
|
||||||
|
18
nix/vm/common-cinnamon.nix
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.xserver = {
|
||||||
|
displayManager = {
|
||||||
|
lightdm = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
desktopManager = {
|
||||||
|
cinnamon = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
136
nix/vm/common-gnome.nix
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
imports = [
|
||||||
|
./common.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.xserver = {
|
||||||
|
displayManager = {
|
||||||
|
gdm = {
|
||||||
|
enable = true;
|
||||||
|
autoSuspend = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
desktopManager = {
|
||||||
|
gnome = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.systemPackages = [
|
||||||
|
pkgs.gnomeExtensions.no-overview
|
||||||
|
];
|
||||||
|
|
||||||
|
environment.gnome.excludePackages = with pkgs; [
|
||||||
|
atomix
|
||||||
|
baobab
|
||||||
|
cheese
|
||||||
|
epiphany
|
||||||
|
evince
|
||||||
|
file-roller
|
||||||
|
geary
|
||||||
|
gnome-backgrounds
|
||||||
|
gnome-calculator
|
||||||
|
gnome-calendar
|
||||||
|
gnome-clocks
|
||||||
|
gnome-connections
|
||||||
|
gnome-contacts
|
||||||
|
gnome-disk-utility
|
||||||
|
gnome-extension-manager
|
||||||
|
gnome-logs
|
||||||
|
gnome-maps
|
||||||
|
gnome-music
|
||||||
|
gnome-photos
|
||||||
|
gnome-software
|
||||||
|
gnome-system-monitor
|
||||||
|
gnome-text-editor
|
||||||
|
gnome-themes-extra
|
||||||
|
gnome-tour
|
||||||
|
gnome-user-docs
|
||||||
|
gnome-weather
|
||||||
|
hitori
|
||||||
|
iagno
|
||||||
|
loupe
|
||||||
|
nautilus
|
||||||
|
orca
|
||||||
|
seahorse
|
||||||
|
simple-scan
|
||||||
|
snapshot
|
||||||
|
sushi
|
||||||
|
tali
|
||||||
|
totem
|
||||||
|
yelp
|
||||||
|
];
|
||||||
|
|
||||||
|
programs.dconf = {
|
||||||
|
enable = true;
|
||||||
|
profiles.user.databases = [
|
||||||
|
{
|
||||||
|
settings = with lib.gvariant; {
|
||||||
|
"org/gnome/desktop/background" = {
|
||||||
|
picture-uri = "file://${pkgs.ghostty}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png";
|
||||||
|
picture-uri-dark = "file://${pkgs.ghostty}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png";
|
||||||
|
picture-options = "centered";
|
||||||
|
primary-color = "#000000000000";
|
||||||
|
secondary-color = "#000000000000";
|
||||||
|
};
|
||||||
|
"org/gnome/desktop/interface" = {
|
||||||
|
color-scheme = "prefer-dark";
|
||||||
|
};
|
||||||
|
"org/gnome/desktop/notifications" = {
|
||||||
|
show-in-lock-screen = false;
|
||||||
|
};
|
||||||
|
"org/gnome/desktop/screensaver" = {
|
||||||
|
lock-enabled = false;
|
||||||
|
picture-uri = "file://${pkgs.ghostty}/share/icons/hicolor/512x512/apps/com.mitchellh.ghostty.png";
|
||||||
|
picture-options = "centered";
|
||||||
|
primary-color = "#000000000000";
|
||||||
|
secondary-color = "#000000000000";
|
||||||
|
};
|
||||||
|
"org/gnome/desktop/session" = {
|
||||||
|
idle-delay = mkUint32 0;
|
||||||
|
};
|
||||||
|
"org/gnome/shell" = {
|
||||||
|
disable-user-extensions = false;
|
||||||
|
enabled-extensions = builtins.map (x: x.extensionUuid) (
|
||||||
|
lib.filter (p: p ? extensionUuid) config.environment.systemPackages
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
programs.geary.enable = false;
|
||||||
|
|
||||||
|
services.gnome = {
|
||||||
|
gnome-browser-connector.enable = false;
|
||||||
|
gnome-initial-setup.enable = false;
|
||||||
|
gnome-online-accounts.enable = false;
|
||||||
|
gnome-remote-desktop.enable = false;
|
||||||
|
rygel.enable = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
system.activationScripts = {
|
||||||
|
face = {
|
||||||
|
text = ''
|
||||||
|
mkdir -p /var/lib/AccountsService/{icons,users}
|
||||||
|
|
||||||
|
cp ${pkgs.ghostty}/share/icons/hicolor/1024x1024/apps/com.mitchellh.ghostty.png /var/lib/AccountsService/icons/ghostty
|
||||||
|
|
||||||
|
echo -e "[User]\nIcon=/var/lib/AccountsService/icons/ghostty\n" > /var/lib/AccountsService/users/ghostty
|
||||||
|
|
||||||
|
chown root:root /var/lib/AccountsService/users/ghostty
|
||||||
|
chmod 0600 /var/lib/AccountsService/users/ghostty
|
||||||
|
|
||||||
|
chown root:root /var/lib/AccountsService/icons/ghostty
|
||||||
|
chmod 0444 /var/lib/AccountsService/icons/ghostty
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
21
nix/vm/common-plasma6.nix
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services = {
|
||||||
|
displayManager = {
|
||||||
|
sddm = {
|
||||||
|
enable = true;
|
||||||
|
wayland = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
desktopManager = {
|
||||||
|
plasma6 = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
18
nix/vm/common-xfce.nix
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.xserver = {
|
||||||
|
displayManager = {
|
||||||
|
lightdm = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
desktopManager = {
|
||||||
|
xfce = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
83
nix/vm/common.nix
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
{pkgs, ...}: {
|
||||||
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
boot.loader.efi.canTouchEfiVariables = true;
|
||||||
|
|
||||||
|
documentation.nixos.enable = false;
|
||||||
|
|
||||||
|
networking.hostName = "ghostty";
|
||||||
|
networking.domain = "mitchellh.com";
|
||||||
|
|
||||||
|
virtualisation.vmVariant = {
|
||||||
|
virtualisation.memorySize = 2048;
|
||||||
|
};
|
||||||
|
|
||||||
|
nix = {
|
||||||
|
settings = {
|
||||||
|
trusted-users = [
|
||||||
|
"root"
|
||||||
|
"ghostty"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
extraOptions = ''
|
||||||
|
experimental-features = nix-command flakes
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
users.mutableUsers = false;
|
||||||
|
|
||||||
|
users.groups.ghostty = {};
|
||||||
|
|
||||||
|
users.users.ghostty = {
|
||||||
|
description = "Ghostty";
|
||||||
|
group = "ghostty";
|
||||||
|
extraGroups = ["wheel"];
|
||||||
|
isNormalUser = true;
|
||||||
|
initialPassword = "ghostty";
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.etc = {
|
||||||
|
"xdg/autostart/com.mitchellh.ghostty.desktop" = {
|
||||||
|
source = "${pkgs.ghostty}/share/applications/com.mitchellh.ghostty.desktop";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.systemPackages = [
|
||||||
|
pkgs.kitty
|
||||||
|
pkgs.fish
|
||||||
|
pkgs.ghostty
|
||||||
|
pkgs.helix
|
||||||
|
pkgs.neovim
|
||||||
|
pkgs.xterm
|
||||||
|
pkgs.zsh
|
||||||
|
];
|
||||||
|
|
||||||
|
security.polkit = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.dbus = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.displayManager = {
|
||||||
|
autoLogin = {
|
||||||
|
user = "ghostty";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.libinput = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.qemuGuest = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.spice-vdagentd = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.xserver = {
|
||||||
|
enable = true;
|
||||||
|
};
|
||||||
|
}
|
12
nix/vm/create-cinnamon.nix
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
system,
|
||||||
|
nixpkgs,
|
||||||
|
overlay,
|
||||||
|
module,
|
||||||
|
uid ? 1000,
|
||||||
|
gid ? 1000,
|
||||||
|
}:
|
||||||
|
import ./create.nix {
|
||||||
|
inherit system nixpkgs overlay module uid gid;
|
||||||
|
common = ./common-cinnamon.nix;
|
||||||
|
}
|
12
nix/vm/create-gnome.nix
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
system,
|
||||||
|
nixpkgs,
|
||||||
|
overlay,
|
||||||
|
module,
|
||||||
|
uid ? 1000,
|
||||||
|
gid ? 1000,
|
||||||
|
}:
|
||||||
|
import ./create.nix {
|
||||||
|
inherit system nixpkgs overlay module uid gid;
|
||||||
|
common = ./common-gnome.nix;
|
||||||
|
}
|
12
nix/vm/create-plasma6.nix
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
system,
|
||||||
|
nixpkgs,
|
||||||
|
overlay,
|
||||||
|
module,
|
||||||
|
uid ? 1000,
|
||||||
|
gid ? 1000,
|
||||||
|
}:
|
||||||
|
import ./create.nix {
|
||||||
|
inherit system nixpkgs overlay module uid gid;
|
||||||
|
common = ./common-plasma6.nix;
|
||||||
|
}
|
12
nix/vm/create-xfce.nix
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
system,
|
||||||
|
nixpkgs,
|
||||||
|
overlay,
|
||||||
|
module,
|
||||||
|
uid ? 1000,
|
||||||
|
gid ? 1000,
|
||||||
|
}:
|
||||||
|
import ./create.nix {
|
||||||
|
inherit system nixpkgs overlay module uid gid;
|
||||||
|
common = ./common-xfce.nix;
|
||||||
|
}
|
42
nix/vm/create.nix
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
system,
|
||||||
|
nixpkgs,
|
||||||
|
overlay,
|
||||||
|
module,
|
||||||
|
common ? ./common.nix,
|
||||||
|
uid ? 1000,
|
||||||
|
gid ? 1000,
|
||||||
|
}: let
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
overlays = [
|
||||||
|
overlay
|
||||||
|
];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
nixpkgs.lib.nixosSystem {
|
||||||
|
system = builtins.replaceStrings ["darwin"] ["linux"] system;
|
||||||
|
modules = [
|
||||||
|
{
|
||||||
|
virtualisation.vmVariant = {
|
||||||
|
virtualisation.host.pkgs = pkgs;
|
||||||
|
};
|
||||||
|
|
||||||
|
nixpkgs.overlays = [
|
||||||
|
overlay
|
||||||
|
];
|
||||||
|
|
||||||
|
users.groups.ghostty = {
|
||||||
|
gid = gid;
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.ghostty = {
|
||||||
|
uid = uid;
|
||||||
|
};
|
||||||
|
|
||||||
|
system.stateVersion = nixpkgs.lib.trivial.release;
|
||||||
|
}
|
||||||
|
common
|
||||||
|
module
|
||||||
|
];
|
||||||
|
}
|
7
nix/vm/wayland-cinnamon.nix
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common-cinnamon.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.displayManager.defaultSession = "cinnamon-wayland";
|
||||||
|
}
|
9
nix/vm/wayland-gnome.nix
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common-gnome.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.displayManager = {
|
||||||
|
defaultSession = "gnome";
|
||||||
|
};
|
||||||
|
}
|
6
nix/vm/wayland-plasma6.nix
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common-plasma6.nix
|
||||||
|
];
|
||||||
|
services.displayManager.defaultSession = "plasma";
|
||||||
|
}
|
7
nix/vm/x11-cinnamon.nix
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common-cinnamon.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.displayManager.defaultSession = "cinnamon";
|
||||||
|
}
|
9
nix/vm/x11-gnome.nix
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common-gnome.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.displayManager = {
|
||||||
|
defaultSession = "gnome-xorg";
|
||||||
|
};
|
||||||
|
}
|
6
nix/vm/x11-plasma6.nix
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common-plasma6.nix
|
||||||
|
];
|
||||||
|
services.displayManager.defaultSession = "plasmax11";
|
||||||
|
}
|
7
nix/vm/x11-xfce.nix
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{...}: {
|
||||||
|
imports = [
|
||||||
|
./common-xfce.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
services.displayManager.defaultSession = "xfce";
|
||||||
|
}
|
@@ -1,3 +0,0 @@
|
|||||||
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for
|
|
||||||
# more details.
|
|
||||||
"sha256-ot5onG1yq7EWQkNUgTNBuqvsnLuaoFs2UDS96IqgJmU="
|
|
@@ -1,10 +1,10 @@
|
|||||||
.{
|
.{
|
||||||
.name = "cimgui",
|
.name = "cimgui",
|
||||||
.version = "1.89.9",
|
.version = "1.90.6", // -docking branch
|
||||||
.paths = .{""},
|
.paths = .{""},
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
// This should be kept in sync with the submodule in the cimgui source
|
// This should be kept in sync with the submodule in the cimgui source
|
||||||
// code to be safe that they're compatible.
|
// code in ./vendor/ to be safe that they're compatible.
|
||||||
.imgui = .{
|
.imgui = .{
|
||||||
.url = "https://github.com/ocornut/imgui/archive/e391fe2e66eb1c96b1624ae8444dc64c23146ef4.tar.gz",
|
.url = "https://github.com/ocornut/imgui/archive/e391fe2e66eb1c96b1624ae8444dc64c23146ef4.tar.gz",
|
||||||
.hash = "1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402",
|
.hash = "1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402",
|
||||||
|