Compare commits
969 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
889478f310 | ||
![]() |
8bd3073cb7 | ||
![]() |
acafe881a0 | ||
![]() |
80030f20f0 | ||
![]() |
602b47ff4e | ||
![]() |
3a7fb03ef9 | ||
![]() |
3423cfdc71 | ||
![]() |
b2a8211f9e | ||
![]() |
8fdb3e6e99 | ||
![]() |
f1c510f9fe | ||
![]() |
3264051f8d | ||
![]() |
b2ad90fe18 | ||
![]() |
9d5e64a76f | ||
![]() |
691fbccbfc | ||
![]() |
92bd6b6244 | ||
![]() |
ccf72dfbf7 | ||
![]() |
196bc55757 | ||
![]() |
f458d48f9b | ||
![]() |
adf7c87cb2 | ||
![]() |
c9ca09af90 | ||
![]() |
e30b5efd5d | ||
![]() |
67650c273a | ||
![]() |
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 | ||
![]() |
3f7c3afaf9 | ||
![]() |
a857d56fb6 | ||
![]() |
4ccd564849 | ||
![]() |
df0620afe9 | ||
![]() |
5957e1101c | ||
![]() |
4543cdeac8 | ||
![]() |
5ba8fee38a | ||
![]() |
47cf5cbb40 | ||
![]() |
973467b1ca | ||
![]() |
cf34ffa28e | ||
![]() |
eaa872216b | ||
![]() |
d59a57e133 | ||
![]() |
27c3382a6a | ||
![]() |
a30b2eda39 | ||
![]() |
e20ec96fee | ||
![]() |
6a8b31571b | ||
![]() |
061a730dd3 | ||
![]() |
85ed9b626e | ||
![]() |
ecfca17ad6 | ||
![]() |
12a333dfb4 | ||
![]() |
783a06689e | ||
![]() |
789e2024a5 | ||
![]() |
d7c5017cd2 | ||
![]() |
413964774c | ||
![]() |
aa81c16ba1 | ||
![]() |
fa4d4a38c1 | ||
![]() |
2d174f9bff | ||
![]() |
3971c460d1 | ||
![]() |
f97f7e8a70 | ||
![]() |
478fe3917c | ||
![]() |
98d77788f4 | ||
![]() |
220d40e99a | ||
![]() |
d512f56005 | ||
![]() |
dd41a9447d | ||
![]() |
d54817607c | ||
![]() |
ffe1b7a872 | ||
![]() |
0da8801dc9 | ||
![]() |
9204bb888f | ||
![]() |
bdeb93fe87 | ||
![]() |
e9edd21bed | ||
![]() |
ef542c6e63 | ||
![]() |
41df2d9805 | ||
![]() |
d20446e4de | ||
![]() |
a6eec4cbe2 | ||
![]() |
7a4215abd7 | ||
![]() |
31f101c970 | ||
![]() |
2f6860fbc5 | ||
![]() |
f8c3dc1bbf | ||
![]() |
ade07c4c3c | ||
![]() |
68318e2830 | ||
![]() |
a15473d9bd | ||
![]() |
397bf41ec3 | ||
![]() |
c87e3e98a3 | ||
![]() |
a1f7a95763 | ||
![]() |
c62f64866c | ||
![]() |
716848cb58 | ||
![]() |
a7c93cdfb1 | ||
![]() |
e17bb69645 | ||
![]() |
318641f5a1 | ||
![]() |
26b1888494 | ||
![]() |
1a27ce0797 | ||
![]() |
adcaff7137 | ||
![]() |
c2c578789b | ||
![]() |
1c2532c184 | ||
![]() |
ff50b5539e | ||
![]() |
f2ac9b85e3 | ||
![]() |
66681f94e0 | ||
![]() |
011c17da41 | ||
![]() |
da186fb9df | ||
![]() |
0dc3ea35c0 | ||
![]() |
4e7982fc2b | ||
![]() |
4f2110bce0 | ||
![]() |
89e5ca2fb4 | ||
![]() |
33b1131a14 | ||
![]() |
d01b2397f1 | ||
![]() |
aed61b62ae | ||
![]() |
e9bc033b88 | ||
![]() |
c011c4622d | ||
![]() |
057dd3e209 | ||
![]() |
87bd0bb744 | ||
![]() |
cfeed2b7a2 | ||
![]() |
d2d6f8b9f4 | ||
![]() |
31c9a2fe59 | ||
![]() |
4d983a2083 | ||
![]() |
0fd65035c5 | ||
![]() |
3059d9036b | ||
![]() |
37c5f5a123 | ||
![]() |
35eefd4240 | ||
![]() |
8607b1f844 | ||
![]() |
a9c1a5c73c | ||
![]() |
06389b280a | ||
![]() |
cb96ce5dcc | ||
![]() |
936d0c0d58 | ||
![]() |
7256ebe13d | ||
![]() |
479e735e7f | ||
![]() |
b3290f6887 | ||
![]() |
3e11476d32 | ||
![]() |
d5703a57e7 | ||
![]() |
84a03aa202 | ||
![]() |
b3925b83ae | ||
![]() |
5698358c2f | ||
![]() |
ff6e60be56 | ||
![]() |
db0da9a273 | ||
![]() |
31ee48a355 | ||
![]() |
b7dba0c5f5 | ||
![]() |
3b8a0ed2b8 | ||
![]() |
9c15d8de35 | ||
![]() |
2bb3353672 | ||
![]() |
074edd3065 | ||
![]() |
27ddc2a9b2 | ||
![]() |
7881bb2bf0 | ||
![]() |
c20fe23946 | ||
![]() |
2e048c870c | ||
![]() |
329c87a2b4 | ||
![]() |
c8c3fb2900 | ||
![]() |
6508fec945 | ||
![]() |
b39850fd8b | ||
![]() |
64ea3a1a29 | ||
![]() |
65a1c0df80 | ||
![]() |
8da600a62e | ||
![]() |
25a4a89ee3 | ||
![]() |
5be77ded3a | ||
![]() |
02538bed3c | ||
![]() |
a787e4b8fc | ||
![]() |
e24f33ae6b | ||
![]() |
64c393716a | ||
![]() |
2ab0376d80 | ||
![]() |
d359231e5c | ||
![]() |
16f81353cc | ||
![]() |
9bef43fd99 | ||
![]() |
56681741cb | ||
![]() |
c84fefc4ea | ||
![]() |
574407aacd | ||
![]() |
579de8e491 | ||
![]() |
6ca64bc410 | ||
![]() |
2b245c965c | ||
![]() |
a92ed15baa | ||
![]() |
b6e45d49a3 | ||
![]() |
5c2fb580d0 | ||
![]() |
5e14b8e501 | ||
![]() |
1a6d9590a2 | ||
![]() |
1ca25d3b5e | ||
![]() |
c8950d376a | ||
![]() |
28bbd526f2 | ||
![]() |
2993d12de7 | ||
![]() |
02ca5bedac | ||
![]() |
b50e087581 | ||
![]() |
85fc49b22c | ||
![]() |
9fc1e4e91a | ||
![]() |
a2c446cb73 | ||
![]() |
1783ec922d | ||
![]() |
b8a75c24a6 | ||
![]() |
ea8fe9a4b0 | ||
![]() |
bfde326bcb | ||
![]() |
fa83140585 | ||
![]() |
a671ca43f8 | ||
![]() |
5293fc9c2f | ||
![]() |
531a225b1e | ||
![]() |
a55bea3491 | ||
![]() |
351a7c03a5 | ||
![]() |
f95aa32965 | ||
![]() |
63dad2fb10 | ||
![]() |
c4ff873726 | ||
![]() |
ca7c954712 | ||
![]() |
b22417630b | ||
![]() |
54679ff30e | ||
![]() |
38643ec4fe | ||
![]() |
040ce0e7df | ||
![]() |
b00a4275f0 | ||
![]() |
3bccb4f05e | ||
![]() |
2a4e5e1c8e | ||
![]() |
a8261ec9f6 | ||
![]() |
1e937cf2f1 | ||
![]() |
5340aa6c96 | ||
![]() |
95ee6c1633 | ||
![]() |
e8a2950324 | ||
![]() |
2362a67f68 | ||
![]() |
e00233bebf | ||
![]() |
00d4610fa5 | ||
![]() |
8ecb11a602 | ||
![]() |
8cb13bcfad | ||
![]() |
4ed8306b02 | ||
![]() |
9632a2b956 | ||
![]() |
02b34f44f6 | ||
![]() |
034f79cfa2 | ||
![]() |
e8054c41f5 | ||
![]() |
0160f8a0d9 | ||
![]() |
6cbd69da78 | ||
![]() |
1fa2e699d1 | ||
![]() |
16e4529f69 | ||
![]() |
2555f09d88 | ||
![]() |
bee2188014 | ||
![]() |
bf46ab8d2d | ||
![]() |
8111f5b995 | ||
![]() |
590c2929e7 | ||
![]() |
27a5a50aa0 | ||
![]() |
c8dca6c9a8 | ||
![]() |
e2aee7f75b | ||
![]() |
f0b77e8972 | ||
![]() |
571d9b015d | ||
![]() |
66dbc48e6b | ||
![]() |
1622263519 | ||
![]() |
93b6bfdefe | ||
![]() |
96753aa7e0 | ||
![]() |
09470ede55 | ||
![]() |
6139cb00cf | ||
![]() |
7e4a69e7df | ||
![]() |
66ed72f486 | ||
![]() |
6f2a7d3c36 | ||
![]() |
4b3c1b031e | ||
![]() |
83987968ac | ||
![]() |
2998775152 | ||
![]() |
322f166ca5 | ||
![]() |
8335a31e45 | ||
![]() |
e07f813a50 | ||
![]() |
b9128aded5 | ||
![]() |
8cbf8d5003 | ||
![]() |
50f7632d81 | ||
![]() |
2114e0a613 | ||
![]() |
c2792a1811 | ||
![]() |
a4daabb28a | ||
![]() |
2c3847c9af | ||
![]() |
9db02fd152 | ||
![]() |
f7a461a85f | ||
![]() |
c320635c29 | ||
![]() |
a295c5e884 | ||
![]() |
600eea08cd | ||
![]() |
a8e5eef11c | ||
![]() |
73367a55f6 | ||
![]() |
b5f70b834b | ||
![]() |
869987277c | ||
![]() |
aef90ffff1 | ||
![]() |
729b7ba7ed | ||
![]() |
5411c001c8 | ||
![]() |
ebfa606c67 | ||
![]() |
7aced21a8e | ||
![]() |
eef9664ef8 | ||
![]() |
cb5fbc1041 | ||
![]() |
b2cb80dfbb | ||
![]() |
184db2654c | ||
![]() |
35b9ceee21 | ||
![]() |
6217dbebcf | ||
![]() |
c1f61b4344 | ||
![]() |
5867fe425f | ||
![]() |
a469191311 | ||
![]() |
9117842a45 | ||
![]() |
9252378c82 | ||
![]() |
1e5b02302b |
3
.gitattributes
vendored
@@ -1,3 +1,6 @@
|
||||
build.zig.zon.nix linguist-generated=true
|
||||
build.zig.zon.txt linguist-generated=true
|
||||
build.zig.zon2json-lock linguist-generated=true
|
||||
vendor/** linguist-vendored
|
||||
website/** linguist-documentation
|
||||
pkg/breakpad/vendor/** linguist-vendored
|
||||
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Features, Bug Reports, Questions
|
||||
url: https://github.com/ghostty-org/ghostty/discussions/new/choose
|
||||
about: Our preferred starting point if you have any questions or suggestions about configuration, features or behavior.
|
9
.github/ISSUE_TEMPLATE/preapproved.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
name: Pre-Discussed and Approved Topics
|
||||
about: |-
|
||||
Only for topics already discussed and approved in the GitHub Discussions section.
|
||||
---
|
||||
|
||||
**DO NOT OPEN A NEW ISSUE. PLEASE USE THE DISCUSSIONS SECTION.**
|
||||
|
||||
**I DIDN'T READ THE ABOVE LINE. PLEASE CLOSE THIS ISSUE.**
|
32
.github/workflows/milestone.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# Description:
|
||||
# - Add milestone to a merged PR automatically
|
||||
# - Add milestone to a closed issue that has a merged PR fix (if any)
|
||||
|
||||
name: Milestone Action
|
||||
on:
|
||||
issues:
|
||||
types: [closed]
|
||||
pull_request_target:
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
update-milestone:
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
name: Milestone Update
|
||||
steps:
|
||||
- name: Set Milestone for PR
|
||||
uses: hustcer/milestone-action@v2
|
||||
if: github.event.pull_request.merged == true
|
||||
with:
|
||||
action: bind-pr # `bind-pr` is the default action
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Bind milestone to closed issue that has a merged PR fix
|
||||
- name: Set Milestone for Issue
|
||||
uses: hustcer/milestone-action@v2
|
||||
if: github.event.issue.state == 'closed'
|
||||
with:
|
||||
action: bind-issue
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
29
.github/workflows/nix.yml
vendored
@@ -1,6 +1,31 @@
|
||||
on: [push, pull_request]
|
||||
name: Nix
|
||||
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:
|
||||
if: github.repository == 'ghostty-org/ghostty'
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
@@ -25,5 +50,5 @@ jobs:
|
||||
name: ghostty
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
useDaemon: false # sometimes fails on short jobs
|
||||
- name: Check Zig cache hash
|
||||
run: nix develop -c ./nix/build-support/check-zig-cache-hash.sh
|
||||
- name: Check Zig cache
|
||||
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: ./
|
4
.github/workflows/release-pr.yml
vendored
@@ -68,7 +68,7 @@ jobs:
|
||||
# Setup Sparkle
|
||||
- name: Setup Sparkle
|
||||
env:
|
||||
SPARKLE_VERSION: 2.6.3
|
||||
SPARKLE_VERSION: 2.6.4
|
||||
run: |
|
||||
mkdir -p .action/sparkle
|
||||
cd .action/sparkle
|
||||
@@ -110,6 +110,7 @@ jobs:
|
||||
|
||||
# Updater
|
||||
/usr/libexec/PlistBuddy -c "Set :SUPublicEDKey $SPARKLE_KEY_PUB" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||
/usr/libexec/PlistBuddy -c "Delete :SUEnableAutomaticChecks" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||
|
||||
- name: Codesign app bundle
|
||||
env:
|
||||
@@ -261,6 +262,7 @@ jobs:
|
||||
|
||||
# Updater
|
||||
/usr/libexec/PlistBuddy -c "Set :SUPublicEDKey $SPARKLE_KEY_PUB" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||
/usr/libexec/PlistBuddy -c "Delete :SUEnableAutomaticChecks" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||
|
||||
- name: Codesign app bundle
|
||||
env:
|
||||
|
35
.github/workflows/release-tag.yml
vendored
@@ -7,6 +7,7 @@ on:
|
||||
upload:
|
||||
description: "Upload final artifacts to R2"
|
||||
default: false
|
||||
|
||||
push:
|
||||
tags:
|
||||
- "v[0-9]+.[0-9]+.[0-9]+"
|
||||
@@ -74,6 +75,8 @@ jobs:
|
||||
source-tarball:
|
||||
runs-on: namespace-profile-ghostty-md
|
||||
needs: [setup]
|
||||
env:
|
||||
GHOSTTY_VERSION: ${{ needs.setup.outputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@@ -87,19 +90,24 @@ jobs:
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
|
||||
- name: Create Tarball
|
||||
run: git archive --format=tgz --prefix=ghostty-source/ -o ghostty-source.tar.gz HEAD
|
||||
run: |
|
||||
git archive --format=tgz --prefix="ghostty-${GHOSTTY_VERSION}/" -o "ghostty-${GHOSTTY_VERSION}.tar.gz" HEAD
|
||||
git archive --format=tgz --prefix=ghostty-source/ -o ghostty-source.tar.gz HEAD
|
||||
|
||||
- name: Sign Tarball
|
||||
run: |
|
||||
echo -n "${{ secrets.MINISIGN_KEY }}" > minisign.key
|
||||
echo -n "${{ secrets.MINISIGN_PASSWORD }}" > minisign.password
|
||||
nix develop -c minisign -S -m ghostty-source.tar.gz -s minisign.key < minisign.password
|
||||
nix develop -c minisign -S -m "ghostty-${GHOSTTY_VERSION}.tar.gz" -s minisign.key < minisign.password
|
||||
nix develop -c minisign -S -m "ghostty-source.tar.gz" -s minisign.key < minisign.password
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: source-tarball
|
||||
path: |-
|
||||
ghostty-${{ env.GHOSTTY_VERSION }}.tar.gz
|
||||
ghostty-${{ env.GHOSTTY_VERSION }}.tar.gz.minisig
|
||||
ghostty-source.tar.gz
|
||||
ghostty-source.tar.gz.minisig
|
||||
|
||||
@@ -128,7 +136,7 @@ jobs:
|
||||
|
||||
- name: Setup Sparkle
|
||||
env:
|
||||
SPARKLE_VERSION: 2.6.3
|
||||
SPARKLE_VERSION: 2.6.4
|
||||
run: |
|
||||
mkdir -p .action/sparkle
|
||||
cd .action/sparkle
|
||||
@@ -165,6 +173,7 @@ jobs:
|
||||
|
||||
# Updater
|
||||
/usr/libexec/PlistBuddy -c "Set :SUPublicEDKey $SPARKLE_KEY_PUB" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||
/usr/libexec/PlistBuddy -c "Delete :SUEnableAutomaticChecks" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||
|
||||
- name: Codesign app bundle
|
||||
env:
|
||||
@@ -289,7 +298,7 @@ jobs:
|
||||
|
||||
- name: Setup Sparkle
|
||||
env:
|
||||
SPARKLE_VERSION: 2.6.3
|
||||
SPARKLE_VERSION: 2.6.4
|
||||
run: |
|
||||
mkdir -p .action/sparkle
|
||||
cd .action/sparkle
|
||||
@@ -352,11 +361,14 @@ jobs:
|
||||
run: |
|
||||
mkdir blob
|
||||
mkdir -p blob/${GHOSTTY_VERSION}
|
||||
mv "ghostty-${GHOSTTY_VERSION}.tar.gz" blob/${GHOSTTY_VERSION}/ghostty-${GHOSTTY_VERSION}.tar.gz
|
||||
mv "ghostty-${GHOSTTY_VERSION}.tar.gz.minisig" blob/${GHOSTTY_VERSION}/ghostty-${GHOSTTY_VERSION}.tar.gz.minisig
|
||||
mv ghostty-source.tar.gz blob/${GHOSTTY_VERSION}/ghostty-source.tar.gz
|
||||
mv ghostty-source.tar.gz.minisig blob/${GHOSTTY_VERSION}/ghostty-source.tar.gz.minisig
|
||||
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.dmg blob/${GHOSTTY_VERSION}/Ghostty.dmg
|
||||
mv appcast.xml blob/${GHOSTTY_VERSION}/appcast-staged.xml
|
||||
- name: Upload to R2
|
||||
uses: ryand56/r2-upload-action@latest
|
||||
with:
|
||||
@@ -366,18 +378,3 @@ jobs:
|
||||
r2-bucket: ghostty-release
|
||||
source-dir: blob
|
||||
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: ./
|
||||
|
5
.github/workflows/release-tip.yml
vendored
@@ -164,7 +164,7 @@ jobs:
|
||||
# Setup Sparkle
|
||||
- name: Setup Sparkle
|
||||
env:
|
||||
SPARKLE_VERSION: 2.6.3
|
||||
SPARKLE_VERSION: 2.6.4
|
||||
run: |
|
||||
mkdir -p .action/sparkle
|
||||
cd .action/sparkle
|
||||
@@ -205,6 +205,7 @@ jobs:
|
||||
|
||||
# Updater
|
||||
/usr/libexec/PlistBuddy -c "Set :SUPublicEDKey $SPARKLE_KEY_PUB" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||
/usr/libexec/PlistBuddy -c "Delete :SUEnableAutomaticChecks" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||
|
||||
- name: Codesign app bundle
|
||||
env:
|
||||
@@ -419,6 +420,7 @@ jobs:
|
||||
|
||||
# Updater
|
||||
/usr/libexec/PlistBuddy -c "Set :SUPublicEDKey $SPARKLE_KEY_PUB" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||
/usr/libexec/PlistBuddy -c "Delete :SUEnableAutomaticChecks" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||
|
||||
- name: Codesign app bundle
|
||||
env:
|
||||
@@ -593,6 +595,7 @@ jobs:
|
||||
|
||||
# Updater
|
||||
/usr/libexec/PlistBuddy -c "Set :SUPublicEDKey $SPARKLE_KEY_PUB" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||
/usr/libexec/PlistBuddy -c "Delete :SUEnableAutomaticChecks" "macos/build/Release/Ghostty.app/Contents/Info.plist"
|
||||
|
||||
- name: Codesign app bundle
|
||||
env:
|
||||
|
214
.github/workflows/test.yml
vendored
@@ -6,6 +6,45 @@ on:
|
||||
name: Test
|
||||
|
||||
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:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -163,10 +202,14 @@ jobs:
|
||||
- name: XCode Select
|
||||
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
|
||||
# Mac app to access.
|
||||
- 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
|
||||
# codesigning. IMPORTANT: this must NOT run in a Nix environment.
|
||||
@@ -199,35 +242,39 @@ jobs:
|
||||
- name: XCode Select
|
||||
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
|
||||
run: |
|
||||
# OpenGL
|
||||
nix develop -c zig build test -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 -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 -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=freetype
|
||||
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 --system ${{ steps.deps.outputs.deps }} -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_harfbuzz
|
||||
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=opengl -Dfont-backend=coretext_noshape
|
||||
|
||||
# Metal
|
||||
nix develop -c zig build test -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 -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 -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=freetype
|
||||
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 --system ${{ steps.deps.outputs.deps }} -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_harfbuzz
|
||||
nix develop -c zig build test --system ${{ steps.deps.outputs.deps }} -Dapp-runtime=glfw -Drenderer=metal -Dfont-backend=coretext_noshape
|
||||
|
||||
- name: Build All
|
||||
run: |
|
||||
nix develop -c zig build -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 -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 -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=freetype
|
||||
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -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_freetype
|
||||
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -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_noshape
|
||||
|
||||
nix develop -c zig build -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 -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 -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=freetype
|
||||
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -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_freetype
|
||||
nix develop -c zig build --system ${{ steps.deps.outputs.deps }} -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_noshape
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-2022
|
||||
@@ -247,10 +294,10 @@ jobs:
|
||||
run: |
|
||||
# Get the zig version from build.zig so that it only needs to be updated
|
||||
$fileContent = Get-Content -Path "build.zig" -Raw
|
||||
$pattern = 'const required_zig = "(.*?)";'
|
||||
$pattern = 'buildpkg\.requireZig\("(.*?)"\);'
|
||||
$zigVersion = [regex]::Match($fileContent, $pattern).Groups[1].Value
|
||||
Write-Output $version
|
||||
$version = "zig-windows-x86_64-$zigVersion"
|
||||
Write-Output $version
|
||||
$uri = "https://ziglang.org/download/$zigVersion/$version.zip"
|
||||
Invoke-WebRequest -Uri "$uri" -OutFile ".\zig-windows.zip"
|
||||
Expand-Archive -Path ".\zig-windows.zip" -DestinationPath ".\" -Force
|
||||
@@ -329,9 +376,6 @@ jobs:
|
||||
- name: Test GTK Build
|
||||
run: nix develop -c zig build -Dapp-runtime=gtk -Dgtk-adwaita=true -Demit-docs
|
||||
|
||||
- name: Test GTK Build (No Libadwaita)
|
||||
run: nix develop -c zig build -Dapp-runtime=gtk -Dgtk-adwaita=false -Demit-docs
|
||||
|
||||
- name: Test GLFW Build
|
||||
run: nix develop -c zig build -Dapp-runtime=glfw
|
||||
|
||||
@@ -339,6 +383,83 @@ jobs:
|
||||
- name: Test System Build
|
||||
run: nix develop -c zig build --system ${ZIG_GLOBAL_CACHE_DIR}/p
|
||||
|
||||
test-gtk:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
adwaita: ["true", "false"]
|
||||
x11: ["true", "false"]
|
||||
wayland: ["true", "false"]
|
||||
name: GTK adwaita=${{ matrix.adwaita }} x11=${{ matrix.x11 }} wayland=${{ matrix.wayland }}
|
||||
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 GTK Build
|
||||
run: |
|
||||
nix develop -c \
|
||||
zig build \
|
||||
-Dapp-runtime=gtk \
|
||||
-Dgtk-adwaita=${{ matrix.adwaita }} \
|
||||
-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:
|
||||
runs-on: namespace-profile-ghostty-macos
|
||||
needs: test
|
||||
@@ -358,8 +479,12 @@ jobs:
|
||||
- name: XCode Select
|
||||
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
|
||||
run: nix develop -c zig build test
|
||||
run: nix develop -c zig build test --system ${{ steps.deps.outputs.deps }}
|
||||
|
||||
prettier:
|
||||
if: github.repository == 'ghostty-org/ghostty'
|
||||
@@ -441,3 +566,38 @@ jobs:
|
||||
useDaemon: false # sometimes fails on short jobs
|
||||
- name: typos check
|
||||
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: |
|
||||
# Only proceed if build.zig.zon has changed
|
||||
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-hash.sh
|
||||
nix develop -c ./nix/build-support/check-zig-cache.sh --update
|
||||
nix develop -c ./nix/build-support/check-zig-cache.sh
|
||||
fi
|
||||
|
||||
# Verify the build still works. We choose an arbitrary build type
|
||||
# as a canary instead of testing all build types.
|
||||
- name: Test Build
|
||||
run: nix develop -c zig build -Dapp-runtime=gtk -Dgtk-adwaita=true
|
||||
run: nix build .#ghostty
|
||||
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
commit-message: "deps: Update iTerm2 color schemes"
|
||||
add-paths: |
|
||||
build.zig.zon
|
||||
nix/zigCacheHash.nix
|
||||
build.zig.zon.nix
|
||||
body: |
|
||||
Upstream revision: https://github.com/mbadolato/iTerm2-Color-Schemes/tree/${{ steps.zig_fetch.outputs.upstream_rev }}
|
||||
labels: dependencies
|
||||
|
1
.gitignore
vendored
@@ -17,3 +17,4 @@ test/cases/**/*.actual.png
|
||||
glad.zip
|
||||
/Box_test.ppm
|
||||
/Box_test_diff.ppm
|
||||
/ghostty.qcow2
|
||||
|
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
|
||||
> not open a WIP pull request to discuss a feature. Instead, use a discussion
|
||||
> 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.
|
||||
|
12
PACKAGING.md
@@ -19,8 +19,8 @@ at `release.files.ghostty.org` in the following URL format where
|
||||
`VERSION` is the version number with no prefix such as `1.0.0`:
|
||||
|
||||
```
|
||||
https://release.files.ghostty.org/VERSION/ghostty-source.tar.gz
|
||||
https://release.files.ghostty.org/VERSION/ghostty-source.tar.gz.minisig
|
||||
https://release.files.ghostty.org/VERSION/ghostty-VERSION.tar.gz
|
||||
https://release.files.ghostty.org/VERSION/ghostty-VERSION.tar.gz.minisig
|
||||
```
|
||||
|
||||
Signature files are signed with
|
||||
@@ -110,3 +110,11 @@ relevant to package maintainers:
|
||||
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
|
||||
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",
|
||||
.version = "1.0.0",
|
||||
.version = "1.1.3",
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
// Zig libs
|
||||
.libxev = .{
|
||||
.url = "https://github.com/mitchellh/libxev/archive/db6a52bafadf00360e675fefa7926e8e6c0e9931.tar.gz",
|
||||
.hash = "12206029de146b685739f69b10a6f08baee86b3d0a5f9a659fa2b2b66c9602078bbf",
|
||||
.url = "https://github.com/mitchellh/libxev/archive/31eed4e337fed7b0149319e5cdbb62b848c24fbd.tar.gz",
|
||||
.hash = "1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c",
|
||||
},
|
||||
.mach_glfw = .{
|
||||
.url = "https://github.com/mitchellh/mach-glfw/archive/37c2995f31abcf7e8378fba68ddcf4a3faa02de0.tar.gz",
|
||||
.hash = "12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62",
|
||||
.lazy = true,
|
||||
},
|
||||
.vaxis = .{
|
||||
.url = "https://github.com/rockorager/libvaxis/archive/6d729a2dc3b934818dffe06d2ba3ce02841ed74b.tar.gz",
|
||||
.hash = "12200df4ebeaed45de26cb2c9f3b6f3746d8013b604e035dae658f86f586c8c91d2f",
|
||||
},
|
||||
.z2d = .{
|
||||
.url = "https://github.com/vancluever/z2d/archive/4638bb02a9dc41cc2fb811f092811f6a951c752a.tar.gz",
|
||||
.hash = "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a",
|
||||
},
|
||||
.zig_objc = .{
|
||||
.url = "https://github.com/mitchellh/zig-objc/archive/9b8ba849b0f58fe207ecd6ab7c147af55b17556e.tar.gz",
|
||||
.hash = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634",
|
||||
@@ -25,6 +33,18 @@
|
||||
.url = "https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz",
|
||||
.hash = "12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25",
|
||||
},
|
||||
.zig_wayland = .{
|
||||
.url = "https://deps.files.ghostty.org/zig-wayland-fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz",
|
||||
.hash = "12209ca054cb1919fa276e328967f10b253f7537c4136eb48f3332b0f7cf661cad38",
|
||||
},
|
||||
.zf = .{
|
||||
.url = "https://github.com/natecraddock/zf/archive/ed99ca18b02dda052e20ba467e90b623c04690dd.tar.gz",
|
||||
.hash = "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8",
|
||||
},
|
||||
.gobject = .{
|
||||
.url = "https://github.com/ianprime0509/zig-gobject/releases/download/v0.2.2/bindings-gnome47.tar.zst",
|
||||
.hash = "12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d",
|
||||
},
|
||||
|
||||
// C libs
|
||||
.cimgui = .{ .path = "./pkg/cimgui" },
|
||||
@@ -46,23 +66,25 @@
|
||||
.glslang = .{ .path = "./pkg/glslang" },
|
||||
.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 = "https://github.com/KDE/plasma-wayland-protocols/archive/db525e8f9da548cffa2ac77618dd0fbe7f511b86.tar.gz",
|
||||
.hash = "12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566",
|
||||
},
|
||||
|
||||
// Other
|
||||
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
|
||||
.iterm2_themes = .{
|
||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/d6c42066b3045292e0b1154ad84ff22d6863ebf7.tar.gz",
|
||||
.hash = "12204358b2848ffd993d3425055bff0a5ba9b1b60bead763a6dea0517965d7290a6c",
|
||||
},
|
||||
.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",
|
||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/db227d159adc265818f2e898da0f70ef8d7b580e.tar.gz",
|
||||
.hash = "12203d2647e5daf36a9c85b969e03f422540786ce9ea624eb4c26d204fe1f46218f3",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
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 = "https://github.com/rockorager/libvaxis/archive/6d729a2dc3b934818dffe06d2ba3ce02841ed74b.tar.gz";
|
||||
hash = "sha256-OCNs6Gl2ruq5dBm4uIxs93hoXw/+n+x1+bIDfQGDx3s=";
|
||||
};
|
||||
}
|
||||
{
|
||||
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 = "https://github.com/vancluever/z2d/archive/4638bb02a9dc41cc2fb811f092811f6a951c752a.tar.gz";
|
||||
hash = "sha256-P0UJ54RO/vVyDa+UkBl+QEOjzoMMEFSOTexQP/uBXfc=";
|
||||
};
|
||||
}
|
||||
{
|
||||
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 = "https://github.com/natecraddock/zf/archive/ed99ca18b02dda052e20ba467e90b623c04690dd.tar.gz";
|
||||
hash = "sha256-/oLryY3VQfjbtQi+UP+n6FJTVA/YxIetjO+6Ovrh6/E=";
|
||||
};
|
||||
}
|
||||
{
|
||||
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-6kGR1o5DdnflHzqs3ieCmBAUTpMdOXoyfcYDXiw5xQ0=";
|
||||
};
|
||||
}
|
||||
{
|
||||
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 = "https://github.com/KDE/plasma-wayland-protocols/archive/db525e8f9da548cffa2ac77618dd0fbe7f511b86.tar.gz";
|
||||
hash = "sha256-XFi6IUrNjmvKNCbcCLAixGqN2Zeymhs+KLrfccIN9EE=";
|
||||
};
|
||||
}
|
||||
{
|
||||
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 = "https://github.com/make-github-pseudonymous-again/pixels/archive/d843c2714d32e15b48b8d7eeb480295af537f877.tar.gz";
|
||||
hash = "sha256-Veg7FtCRCCUCvxSb9FfzH0IJLFmCZQ4/+657SIcb8Ro=";
|
||||
};
|
||||
}
|
||||
{
|
||||
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=";
|
||||
};
|
||||
}
|
||||
]
|
38
build.zig.zon.txt
generated
Normal file
@@ -0,0 +1,38 @@
|
||||
git+https://github.com/rockorager/libvaxis/?ref=main#dc0a228a5544988d4a920cfb40be9cd28db41423
|
||||
git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e
|
||||
https://codeberg.org/atman/zg/archive/v0.13.2.tar.gz
|
||||
https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz
|
||||
https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz
|
||||
https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz
|
||||
https://deps.files.ghostty.org/zig-wayland-fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz
|
||||
https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz
|
||||
https://github.com/GNOME/libxml2/archive/refs/tags/v2.11.5.tar.gz
|
||||
https://github.com/KDE/plasma-wayland-protocols/archive/db525e8f9da548cffa2ac77618dd0fbe7f511b86.tar.gz
|
||||
https://github.com/KhronosGroup/SPIRV-Cross/archive/476f384eb7d9e48613c45179e502a15ab95b6b49.tar.gz
|
||||
https://github.com/KhronosGroup/glslang/archive/refs/tags/14.2.0.tar.gz
|
||||
https://github.com/freetype/freetype/archive/refs/tags/VER-2-13-2.tar.gz
|
||||
https://github.com/getsentry/breakpad/archive/b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz
|
||||
https://github.com/getsentry/sentry-native/archive/refs/tags/0.7.8.tar.gz
|
||||
https://github.com/glennrp/libpng/archive/refs/tags/v1.6.43.tar.gz
|
||||
https://github.com/google/highway/archive/refs/tags/1.1.0.tar.gz
|
||||
https://github.com/google/wuffs/archive/refs/tags/v0.4.0-alpha.9.tar.gz
|
||||
https://github.com/harfbuzz/harfbuzz/archive/refs/tags/8.4.0.tar.gz
|
||||
https://github.com/ianprime0509/zig-gobject/releases/download/v0.2.2/bindings-gnome47.tar.zst
|
||||
https://github.com/kkos/oniguruma/archive/refs/tags/v6.9.9.tar.gz
|
||||
https://github.com/madler/zlib/archive/refs/tags/v1.3.1.tar.gz
|
||||
https://github.com/make-github-pseudonymous-again/pixels/archive/d843c2714d32e15b48b8d7eeb480295af537f877.tar.gz
|
||||
https://github.com/mbadolato/iTerm2-Color-Schemes/archive/db227d159adc265818f2e898da0f70ef8d7b580e.tar.gz
|
||||
https://github.com/mitchellh/glfw/archive/b552c6ec47326b94015feddb36058ea567b87159.tar.gz
|
||||
https://github.com/mitchellh/libxev/archive/31eed4e337fed7b0149319e5cdbb62b848c24fbd.tar.gz
|
||||
https://github.com/mitchellh/mach-glfw/archive/37c2995f31abcf7e8378fba68ddcf4a3faa02de0.tar.gz
|
||||
https://github.com/mitchellh/vulkan-headers/archive/04c8a0389d5a0236a96312988017cd4ce27d8041.tar.gz
|
||||
https://github.com/mitchellh/wayland-headers/archive/5f991515a29f994d87b908115a2ab0b899474bd1.tar.gz
|
||||
https://github.com/mitchellh/x11-headers/archive/2ffbd62d82ff73ec929dd8de802bc95effa0ef88.tar.gz
|
||||
https://github.com/mitchellh/xcode-frameworks/archive/69801c154c39d7ae6129ea1ba8fe1afe00585fc8.tar.gz
|
||||
https://github.com/mitchellh/zig-js/archive/d0b8b0a57c52fbc89f9d9fecba75ca29da7dd7d1.tar.gz
|
||||
https://github.com/mitchellh/zig-objc/archive/9b8ba849b0f58fe207ecd6ab7c147af55b17556e.tar.gz
|
||||
https://github.com/natecraddock/zf/archive/ed99ca18b02dda052e20ba467e90b623c04690dd.tar.gz
|
||||
https://github.com/nemtrif/utfcpp/archive/refs/tags/v4.0.5.tar.gz
|
||||
https://github.com/ocornut/imgui/archive/e391fe2e66eb1c96b1624ae8444dc64c23146ef4.tar.gz
|
||||
https://github.com/rockorager/libvaxis/archive/6d729a2dc3b934818dffe06d2ba3ce02841ed74b.tar.gz
|
||||
https://github.com/vancluever/z2d/archive/4638bb02a9dc41cc2fb811f092811f6a951c752a.tar.gz
|
192
build.zig.zon2json-lock
generated
Normal file
@@ -0,0 +1,192 @@
|
||||
{
|
||||
"1220ebf88622c4d502dc59e71347e4d28c47e033f11b59aff774ae5787565c40999c": {
|
||||
"name": "libxev",
|
||||
"url": "https://github.com/mitchellh/libxev/archive/31eed4e337fed7b0149319e5cdbb62b848c24fbd.tar.gz",
|
||||
"hash": "sha256-VHP90NTytIZ8UZsYRKOOxN490/I6yv6ec40sP8y5MJ8="
|
||||
},
|
||||
"12206ed982e709e565d536ce930701a8c07edfd2cfdce428683f3f2a601d37696a62": {
|
||||
"name": "mach_glfw",
|
||||
"url": "https://github.com/mitchellh/mach-glfw/archive/37c2995f31abcf7e8378fba68ddcf4a3faa02de0.tar.gz",
|
||||
"hash": "sha256-HhXIvWUS8/CHWY4VXPG2ZEo+we8XOn3o5rYJCQ1n8Nk="
|
||||
},
|
||||
"1220736fa4ba211162c7a0e46cc8fe04d95921927688bff64ab5da7420d098a7272d": {
|
||||
"name": "glfw",
|
||||
"url": "https://github.com/mitchellh/glfw/archive/b552c6ec47326b94015feddb36058ea567b87159.tar.gz",
|
||||
"hash": "sha256-IeBVAOQmtyFqVxzuXPek1onuPwIamcOyYtxqKpPEQjU="
|
||||
},
|
||||
"12202adbfecdad671d585c9a5bfcbd5cdf821726779430047742ce1bf94ad67d19cb": {
|
||||
"name": "xcode_frameworks",
|
||||
"url": "https://github.com/mitchellh/xcode-frameworks/archive/69801c154c39d7ae6129ea1ba8fe1afe00585fc8.tar.gz",
|
||||
"hash": "sha256-mP/I2coL57UJm/3+4Q8sPAgQwk8V4zM+S4VBBTrX2To="
|
||||
},
|
||||
"122004bfd4c519dadfb8e6281a42fc34fd1aa15aea654ea8a492839046f9894fa2cf": {
|
||||
"name": "vulkan_headers",
|
||||
"url": "https://github.com/mitchellh/vulkan-headers/archive/04c8a0389d5a0236a96312988017cd4ce27d8041.tar.gz",
|
||||
"hash": "sha256-K+zrRudgHFukOM6En1StRYRMNYkeRk+qHTXvrXaG+FU="
|
||||
},
|
||||
"1220b3164434d2ec9db146a40bf3a30f490590d68fa8529776a3138074f0da2c11ca": {
|
||||
"name": "wayland_headers",
|
||||
"url": "https://github.com/mitchellh/wayland-headers/archive/5f991515a29f994d87b908115a2ab0b899474bd1.tar.gz",
|
||||
"hash": "sha256-uFilLZinKkZt6RdVTV3lUmJpzpswDdFva22FvwU/XQI="
|
||||
},
|
||||
"122089c326186c84aa2fd034b16abc38f3ebf4862d9ae106dc1847ac44f557b36465": {
|
||||
"name": "x11_headers",
|
||||
"url": "https://github.com/mitchellh/x11-headers/archive/2ffbd62d82ff73ec929dd8de802bc95effa0ef88.tar.gz",
|
||||
"hash": "sha256-EhV2bmTY/OMYN1wEul35gD0hQgS/Al262jO3pVr0O+c="
|
||||
},
|
||||
"12200df4ebeaed45de26cb2c9f3b6f3746d8013b604e035dae658f86f586c8c91d2f": {
|
||||
"name": "vaxis",
|
||||
"url": "https://github.com/rockorager/libvaxis/archive/6d729a2dc3b934818dffe06d2ba3ce02841ed74b.tar.gz",
|
||||
"hash": "sha256-OCNs6Gl2ruq5dBm4uIxs93hoXw/+n+x1+bIDfQGDx3s="
|
||||
},
|
||||
"1220dd654ef941fc76fd96f9ec6adadf83f69b9887a0d3f4ee5ac0a1a3e11be35cf5": {
|
||||
"name": "zigimg",
|
||||
"url": "git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e",
|
||||
"hash": "sha256-oLf3YH3yeg4ikVO/GahMCDRMTU31AHkfSnF4rt7xTKo="
|
||||
},
|
||||
"122055beff332830a391e9895c044d33b15ea21063779557024b46169fb1984c6e40": {
|
||||
"name": "zg",
|
||||
"url": "https://codeberg.org/atman/zg/archive/v0.13.2.tar.gz",
|
||||
"hash": "sha256-2x9hT7bYq9KJYWLVOf21a+QvTG/F7HWT+YK15IMRzNY="
|
||||
},
|
||||
"12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a": {
|
||||
"name": "z2d",
|
||||
"url": "https://github.com/vancluever/z2d/archive/4638bb02a9dc41cc2fb811f092811f6a951c752a.tar.gz",
|
||||
"hash": "sha256-P0UJ54RO/vVyDa+UkBl+QEOjzoMMEFSOTexQP/uBXfc="
|
||||
},
|
||||
"1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634": {
|
||||
"name": "zig_objc",
|
||||
"url": "https://github.com/mitchellh/zig-objc/archive/9b8ba849b0f58fe207ecd6ab7c147af55b17556e.tar.gz",
|
||||
"hash": "sha256-H+HIbh2T23uzrsg9/1/vl9Ir1HCAa2pzeTx6zktJH9Q="
|
||||
},
|
||||
"12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc": {
|
||||
"name": "zig_js",
|
||||
"url": "https://github.com/mitchellh/zig-js/archive/d0b8b0a57c52fbc89f9d9fecba75ca29da7dd7d1.tar.gz",
|
||||
"hash": "sha256-fyNeCVbC9UAaKJY6JhAZlT0A479M/AKYMPIWEZbDWD0="
|
||||
},
|
||||
"12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25": {
|
||||
"name": "ziglyph",
|
||||
"url": "https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz",
|
||||
"hash": "sha256-cse98+Ft8QUjX+P88yyYfaxJOJGQ9M7Ymw7jFxDz89k="
|
||||
},
|
||||
"12209ca054cb1919fa276e328967f10b253f7537c4136eb48f3332b0f7cf661cad38": {
|
||||
"name": "zig_wayland",
|
||||
"url": "https://deps.files.ghostty.org/zig-wayland-fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz",
|
||||
"hash": "sha256-RtAystqK/GRYIquTK1KfD7rRSCrfuzAvCD1Z9DE1ldc="
|
||||
},
|
||||
"1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8": {
|
||||
"name": "zf",
|
||||
"url": "https://github.com/natecraddock/zf/archive/ed99ca18b02dda052e20ba467e90b623c04690dd.tar.gz",
|
||||
"hash": "sha256-/oLryY3VQfjbtQi+UP+n6FJTVA/YxIetjO+6Ovrh6/E="
|
||||
},
|
||||
"1220c72c1697dd9008461ead702997a15d8a1c5810247f02e7983b9f74c6c6e4c087": {
|
||||
"name": "vaxis",
|
||||
"url": "git+https://github.com/rockorager/libvaxis/?ref=main#dc0a228a5544988d4a920cfb40be9cd28db41423",
|
||||
"hash": "sha256-QWN4jOrA91KlbqmeEHHJ4HTnCC9nmfxt8DHUXJpAzLI="
|
||||
},
|
||||
"12208d70ee791d7ef7e16e1c3c9c1127b57f1ed066a24f87d57fc9f730c5dc394b9d": {
|
||||
"name": "gobject",
|
||||
"url": "https://github.com/ianprime0509/zig-gobject/releases/download/v0.2.2/bindings-gnome47.tar.zst",
|
||||
"hash": "sha256-UU97kNv/bZzQPKz1djhEDLapLguvfBpFfWVb6FthtcI="
|
||||
},
|
||||
"12202cdac858abc52413a6c6711d5026d2d3c8e13f95ca2c327eade0736298bb021f": {
|
||||
"name": "wayland",
|
||||
"url": "https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz",
|
||||
"hash": "sha256-6kGR1o5DdnflHzqs3ieCmBAUTpMdOXoyfcYDXiw5xQ0="
|
||||
},
|
||||
"12201a57c6ce0001aa034fa80fba3e1cd2253c560a45748f4f4dd21ff23b491cddef": {
|
||||
"name": "wayland_protocols",
|
||||
"url": "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz",
|
||||
"hash": "sha256-XO3K3egbdeYPI+XoO13SuOtO+5+Peb16NH0UiusFMPg="
|
||||
},
|
||||
"12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566": {
|
||||
"name": "plasma_wayland_protocols",
|
||||
"url": "https://github.com/KDE/plasma-wayland-protocols/archive/db525e8f9da548cffa2ac77618dd0fbe7f511b86.tar.gz",
|
||||
"hash": "sha256-XFi6IUrNjmvKNCbcCLAixGqN2Zeymhs+KLrfccIN9EE="
|
||||
},
|
||||
"12203d2647e5daf36a9c85b969e03f422540786ce9ea624eb4c26d204fe1f46218f3": {
|
||||
"name": "iterm2_themes",
|
||||
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/db227d159adc265818f2e898da0f70ef8d7b580e.tar.gz",
|
||||
"hash": "sha256-Iyf7U4rpvNkPX4AOEbYSYGte5+SjRwsWD2luOn1Hz8U="
|
||||
},
|
||||
"1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402": {
|
||||
"name": "imgui",
|
||||
"url": "https://github.com/ocornut/imgui/archive/e391fe2e66eb1c96b1624ae8444dc64c23146ef4.tar.gz",
|
||||
"hash": "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA="
|
||||
},
|
||||
"1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d": {
|
||||
"name": "freetype",
|
||||
"url": "https://github.com/freetype/freetype/archive/refs/tags/VER-2-13-2.tar.gz",
|
||||
"hash": "sha256-QnIB9dUVFnDQXB9bRb713aHy592XHvVPD+qqf/0quQw="
|
||||
},
|
||||
"1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66": {
|
||||
"name": "libpng",
|
||||
"url": "https://github.com/glennrp/libpng/archive/refs/tags/v1.6.43.tar.gz",
|
||||
"hash": "sha256-/syVtGzwXo4/yKQUdQ4LparQDYnp/fF16U/wQcrxoDo="
|
||||
},
|
||||
"1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb": {
|
||||
"name": "zlib",
|
||||
"url": "https://github.com/madler/zlib/archive/refs/tags/v1.3.1.tar.gz",
|
||||
"hash": "sha256-F+iIY/NgBnKrSRgvIXKBtvxNPHYr3jYZNeQ2qVIU0Fw="
|
||||
},
|
||||
"12201149afb3326c56c05bb0a577f54f76ac20deece63aa2f5cd6ff31a4fa4fcb3b7": {
|
||||
"name": "fontconfig",
|
||||
"url": "https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz",
|
||||
"hash": "sha256-O6LdkhWHGKzsXKrxpxYEO1qgVcJ7CB2RSvPMtA3OilU="
|
||||
},
|
||||
"122032442d95c3b428ae8e526017fad881e7dc78eab4d558e9a58a80bfbd65a64f7d": {
|
||||
"name": "libxml2",
|
||||
"url": "https://github.com/GNOME/libxml2/archive/refs/tags/v2.11.5.tar.gz",
|
||||
"hash": "sha256-bCgFni4+60K1tLFkieORamNGwQladP7jvGXNxdiaYhU="
|
||||
},
|
||||
"1220b8588f106c996af10249bfa092c6fb2f35fbacb1505ef477a0b04a7dd1063122": {
|
||||
"name": "harfbuzz",
|
||||
"url": "https://github.com/harfbuzz/harfbuzz/archive/refs/tags/8.4.0.tar.gz",
|
||||
"hash": "sha256-nxygiYE7BZRK0c6MfgGCEwJtNdybq0gKIeuHaDg5ZVY="
|
||||
},
|
||||
"12205c83b8311a24b1d5ae6d21640df04f4b0726e314337c043cde1432758cbe165b": {
|
||||
"name": "highway",
|
||||
"url": "https://github.com/google/highway/archive/refs/tags/1.1.0.tar.gz",
|
||||
"hash": "sha256-NUqLRTm1iOcLmOxwhEJz4/J0EwLEw3e8xOgbPRhm98k="
|
||||
},
|
||||
"1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb": {
|
||||
"name": "oniguruma",
|
||||
"url": "https://github.com/kkos/oniguruma/archive/refs/tags/v6.9.9.tar.gz",
|
||||
"hash": "sha256-ABqhIC54RI9MC/GkjHblVodrNvFtks4yB+zP1h2Z8qA="
|
||||
},
|
||||
"1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e": {
|
||||
"name": "sentry",
|
||||
"url": "https://github.com/getsentry/sentry-native/archive/refs/tags/0.7.8.tar.gz",
|
||||
"hash": "sha256-KsZJfMjWGo0xCT5HrduMmyxFsWsHBbszSoNbZCPDGN8="
|
||||
},
|
||||
"12207fd37bb8251919c112dcdd8f616a491857b34a451f7e4486490077206dc2a1ea": {
|
||||
"name": "breakpad",
|
||||
"url": "https://github.com/getsentry/breakpad/archive/b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz",
|
||||
"hash": "sha256-bMqYlD0amQdmzvYQd8Ca/1k4Bj/heh7+EijlQSttatk="
|
||||
},
|
||||
"1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641": {
|
||||
"name": "utfcpp",
|
||||
"url": "https://github.com/nemtrif/utfcpp/archive/refs/tags/v4.0.5.tar.gz",
|
||||
"hash": "sha256-/8ZooxDndgfTk/PBizJxXyI9oerExNbgV5oR345rWc8="
|
||||
},
|
||||
"122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd": {
|
||||
"name": "wuffs",
|
||||
"url": "https://github.com/google/wuffs/archive/refs/tags/v0.4.0-alpha.9.tar.gz",
|
||||
"hash": "sha256-nkzSCr6W5sTG7enDBXEIhgEm574uLD41UVR2wlC+HBM="
|
||||
},
|
||||
"12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806": {
|
||||
"name": "pixels",
|
||||
"url": "https://github.com/make-github-pseudonymous-again/pixels/archive/d843c2714d32e15b48b8d7eeb480295af537f877.tar.gz",
|
||||
"hash": "sha256-Veg7FtCRCCUCvxSb9FfzH0IJLFmCZQ4/+657SIcb8Ro="
|
||||
},
|
||||
"12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1": {
|
||||
"name": "glslang",
|
||||
"url": "https://github.com/KhronosGroup/glslang/archive/refs/tags/14.2.0.tar.gz",
|
||||
"hash": "sha256-FKLtu1Ccs+UamlPj9eQ12/WXFgS0uDPmPmB26MCpl7U="
|
||||
},
|
||||
"1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da": {
|
||||
"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
|
6
dist/linux/app.desktop
vendored
@@ -7,9 +7,15 @@ Icon=com.mitchellh.ghostty
|
||||
Categories=System;TerminalEmulator;
|
||||
Keywords=terminal;tty;pty;
|
||||
StartupNotify=true
|
||||
StartupWMClass=com.mitchellh.ghostty
|
||||
Terminal=false
|
||||
Actions=new-window;
|
||||
X-GNOME-UsesNotifications=true
|
||||
X-TerminalArgExec=-e
|
||||
X-TerminalArgTitle=--title=
|
||||
X-TerminalArgAppId=--class=
|
||||
X-TerminalArgDir=--working-directory=
|
||||
X-TerminalArgHold=--wait-after-command
|
||||
|
||||
[Desktop Action new-window]
|
||||
Name=New Window
|
||||
|
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)
|
||||
version = os.environ["GHOSTTY_VERSION"]
|
||||
version_dash = version.replace('.', '-')
|
||||
build = os.environ["GHOSTTY_BUILD"]
|
||||
commit = os.environ["GHOSTTY_COMMIT"]
|
||||
commit_long = os.environ["GHOSTTY_COMMIT_LONG"]
|
||||
@@ -82,6 +83,8 @@ elem = ET.SubElement(item, "sparkle:shortVersionString")
|
||||
elem.text = f"{version}"
|
||||
elem = ET.SubElement(item, "sparkle:minimumSystemVersion")
|
||||
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.text = f"""
|
||||
<h1>Ghostty v{version}</h1>
|
||||
@@ -91,8 +94,8 @@ on {now.strftime('%Y-%m-%d')}.
|
||||
</p>
|
||||
<p>
|
||||
We don't currently generate release notes for auto-updates.
|
||||
You can view the complete changelog and release notes on
|
||||
the <a href="https://ghostty.org">Ghostty website</a>.
|
||||
You can view the complete changelog and release notes
|
||||
at <a href="https://ghostty.org/docs/install/release-notes/{version_dash}">ghostty.org/docs/install/release-notes/{version_dash}</a>.
|
||||
</p>
|
||||
"""
|
||||
elem = ET.SubElement(item, "enclosure")
|
||||
|
73
flake.lock
generated
@@ -1,15 +1,31 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1733328505,
|
||||
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1705309234,
|
||||
"narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=",
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -20,11 +36,11 @@
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1733423277,
|
||||
"narHash": "sha256-TxabjxEgkNbCGFRHgM/b9yZWlBj60gUOUnRT/wbVQR8=",
|
||||
"lastModified": 1738255539,
|
||||
"narHash": "sha256-hP2eOqhIO/OILW+3moNWO4GtdJFYCqAe9yJZgvlCoDQ=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e36963a147267afc055f7cf65225958633e536bf",
|
||||
"rev": "c3511a3b53b482aa7547c9d1626fd7310c1de1c5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -36,11 +52,11 @@
|
||||
},
|
||||
"nixpkgs-unstable": {
|
||||
"locked": {
|
||||
"lastModified": 1733229606,
|
||||
"narHash": "sha256-FLYY5M0rpa5C2QAE3CKLYAM6TwbKicdRK6qNrSHlNrE=",
|
||||
"lastModified": 1738136902,
|
||||
"narHash": "sha256-pUvLijVGARw4u793APze3j6mU1Zwdtz7hGkGGkD87qw=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "566e53c2ad750c84f6d31f9ccb9d00f823165550",
|
||||
"rev": "9a5db3142ce450045840cc8d832b13b8a2018e0c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -52,9 +68,12 @@
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs-stable": "nixpkgs-stable",
|
||||
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||
"zig": "zig"
|
||||
"zig": "zig",
|
||||
"zig2nix": "zig2nix"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
@@ -75,17 +94,19 @@
|
||||
"zig": {
|
||||
"inputs": {
|
||||
"flake-compat": [],
|
||||
"flake-utils": "flake-utils",
|
||||
"flake-utils": [
|
||||
"flake-utils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs-stable"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1717848532,
|
||||
"narHash": "sha256-d+xIUvSTreHl8pAmU1fnmkfDTGQYCn2Rb/zOwByxS2M=",
|
||||
"lastModified": 1738239110,
|
||||
"narHash": "sha256-Y5i9mQ++dyIQr+zEPNy+KIbc5wjPmfllBrag3cHZgcE=",
|
||||
"owner": "mitchellh",
|
||||
"repo": "zig-overlay",
|
||||
"rev": "02fc5cc555fc14fda40c42d7c3250efa43812b43",
|
||||
"rev": "1a8fb6f3a04724519436355564b95fce5e272504",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -93,6 +114,30 @@
|
||||
"repo": "zig-overlay",
|
||||
"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",
|
||||
|
115
flake.nix
@@ -8,14 +8,30 @@
|
||||
# glibc versions used by our dependencies from Nix are compatible with the
|
||||
# system glibc that the user is building for.
|
||||
nixpkgs-stable.url = "github:nixos/nixpkgs/release-24.11";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
# Used for shell.nix
|
||||
flake-compat = {
|
||||
url = "github:edolstra/flake-compat";
|
||||
flake = false;
|
||||
};
|
||||
|
||||
zig = {
|
||||
url = "github:mitchellh/zig-overlay";
|
||||
inputs = {
|
||||
nixpkgs.follows = "nixpkgs-stable";
|
||||
flake-utils.follows = "flake-utils";
|
||||
flake-compat.follows = "";
|
||||
};
|
||||
};
|
||||
|
||||
zig2nix = {
|
||||
url = "github:jcollie/zig2nix?ref=c311d8e77a6ee0d995f40a6e10a89a3a4ab04f9a";
|
||||
inputs = {
|
||||
nixpkgs.follows = "nixpkgs-stable";
|
||||
flake-utils.follows = "flake-utils";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
outputs = {
|
||||
@@ -23,36 +39,87 @@
|
||||
nixpkgs-unstable,
|
||||
nixpkgs-stable,
|
||||
zig,
|
||||
zig2nix,
|
||||
...
|
||||
}:
|
||||
builtins.foldl' nixpkgs-stable.lib.recursiveUpdate {} (builtins.map (system: let
|
||||
pkgs-stable = nixpkgs-stable.legacyPackages.${system};
|
||||
pkgs-unstable = nixpkgs-unstable.legacyPackages.${system};
|
||||
in {
|
||||
devShell.${system} = pkgs-stable.callPackage ./nix/devShell.nix {
|
||||
zig = zig.packages.${system}."0.13.0";
|
||||
wraptest = pkgs-stable.callPackage ./nix/wraptest.nix {};
|
||||
};
|
||||
builtins.foldl' nixpkgs-stable.lib.recursiveUpdate {} (
|
||||
builtins.map (
|
||||
system: let
|
||||
pkgs-stable = nixpkgs-stable.legacyPackages.${system};
|
||||
pkgs-unstable = nixpkgs-unstable.legacyPackages.${system};
|
||||
in {
|
||||
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
|
||||
mkArgs = optimize: {
|
||||
inherit optimize;
|
||||
packages.${system} = let
|
||||
mkArgs = optimize: {
|
||||
inherit optimize;
|
||||
|
||||
revision = self.shortRev or self.dirtyShortRev or "dirty";
|
||||
revision = self.shortRev or self.dirtyShortRev or "dirty";
|
||||
};
|
||||
in rec {
|
||||
deps = pkgs-stable.callPackage ./build.zig.zon.nix {};
|
||||
ghostty-debug = pkgs-stable.callPackage ./nix/package.nix (mkArgs "Debug");
|
||||
ghostty-releasesafe = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseSafe");
|
||||
ghostty-releasefast = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseFast");
|
||||
|
||||
ghostty = ghostty-releasefast;
|
||||
default = ghostty;
|
||||
};
|
||||
|
||||
formatter.${system} = pkgs-stable.alejandra;
|
||||
|
||||
apps.${system} = let
|
||||
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 = self.overlays.releasefast;
|
||||
releasefast = final: prev: {
|
||||
ghostty = self.packages.${prev.system}.ghostty-releasefast;
|
||||
};
|
||||
debug = final: prev: {
|
||||
ghostty = self.packages.${prev.system}.ghostty-debug;
|
||||
};
|
||||
in rec {
|
||||
ghostty-debug = pkgs-stable.callPackage ./nix/package.nix (mkArgs "Debug");
|
||||
ghostty-releasesafe = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseSafe");
|
||||
ghostty-releasefast = pkgs-stable.callPackage ./nix/package.nix (mkArgs "ReleaseFast");
|
||||
|
||||
ghostty = ghostty-releasefast;
|
||||
default = ghostty;
|
||||
};
|
||||
|
||||
formatter.${system} = pkgs-stable.alejandra;
|
||||
|
||||
# Our supported systems are the same supported systems as the Zig binaries.
|
||||
}) (builtins.attrNames zig.packages));
|
||||
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 = {
|
||||
extra-substituters = ["https://ghostty.cachix.org"];
|
||||
|
@@ -159,7 +159,7 @@ typedef enum {
|
||||
GHOSTTY_KEY_EQUAL,
|
||||
GHOSTTY_KEY_LEFT_BRACKET, // [
|
||||
GHOSTTY_KEY_RIGHT_BRACKET, // ]
|
||||
GHOSTTY_KEY_BACKSLASH, // /
|
||||
GHOSTTY_KEY_BACKSLASH, // \
|
||||
|
||||
// control
|
||||
GHOSTTY_KEY_UP,
|
||||
@@ -375,9 +375,9 @@ typedef enum {
|
||||
typedef enum {
|
||||
GHOSTTY_GOTO_SPLIT_PREVIOUS,
|
||||
GHOSTTY_GOTO_SPLIT_NEXT,
|
||||
GHOSTTY_GOTO_SPLIT_TOP,
|
||||
GHOSTTY_GOTO_SPLIT_UP,
|
||||
GHOSTTY_GOTO_SPLIT_LEFT,
|
||||
GHOSTTY_GOTO_SPLIT_BOTTOM,
|
||||
GHOSTTY_GOTO_SPLIT_DOWN,
|
||||
GHOSTTY_GOTO_SPLIT_RIGHT,
|
||||
} ghostty_action_goto_split_e;
|
||||
|
||||
@@ -559,10 +559,13 @@ typedef struct {
|
||||
|
||||
// apprt.Action.Key
|
||||
typedef enum {
|
||||
GHOSTTY_ACTION_QUIT,
|
||||
GHOSTTY_ACTION_NEW_WINDOW,
|
||||
GHOSTTY_ACTION_NEW_TAB,
|
||||
GHOSTTY_ACTION_CLOSE_TAB,
|
||||
GHOSTTY_ACTION_NEW_SPLIT,
|
||||
GHOSTTY_ACTION_CLOSE_ALL_WINDOWS,
|
||||
GHOSTTY_ACTION_TOGGLE_MAXIMIZE,
|
||||
GHOSTTY_ACTION_TOGGLE_FULLSCREEN,
|
||||
GHOSTTY_ACTION_TOGGLE_TAB_OVERVIEW,
|
||||
GHOSTTY_ACTION_TOGGLE_WINDOW_DECORATIONS,
|
||||
@@ -641,7 +644,7 @@ typedef void (*ghostty_runtime_write_clipboard_cb)(void*,
|
||||
ghostty_clipboard_e,
|
||||
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_action_s);
|
||||
|
||||
@@ -681,10 +684,11 @@ void ghostty_config_open();
|
||||
ghostty_app_t ghostty_app_new(const ghostty_runtime_config_s*,
|
||||
ghostty_config_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_set_focus(ghostty_app_t, bool);
|
||||
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_open_config(ghostty_app_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_input_mods_e ghostty_surface_key_translation_mods(ghostty_surface_t,
|
||||
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);
|
||||
bool ghostty_surface_mouse_captured(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 |
@@ -51,6 +51,8 @@
|
||||
<key>GHOSTTY_MAC_APP</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
<key>MDItemKeywords</key>
|
||||
<string>Terminal</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>NSServices</key>
|
||||
@@ -94,6 +96,8 @@
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>SUEnableAutomaticChecks</key>
|
||||
<false/>
|
||||
<key>SUPublicEDKey</key>
|
||||
<string>wsNcGf5hirwtdXMVnYoxRIX/SqZQLMOsYlD3q3imeok=</string>
|
||||
</dict>
|
||||
|
@@ -11,6 +11,7 @@
|
||||
55154BE02B33911F001622DC /* ghostty in Resources */ = {isa = PBXBuildFile; fileRef = 55154BDF2B33911F001622DC /* ghostty */; };
|
||||
552964E62B34A9B400030505 /* vim in Resources */ = {isa = PBXBuildFile; fileRef = 552964E52B34A9B400030505 /* vim */; };
|
||||
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 */; };
|
||||
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 */; };
|
||||
@@ -68,8 +69,13 @@
|
||||
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5CE2AE0DB50009128F3 /* InspectorView.swift */; };
|
||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A59FB5D02AE0DEA7009128F3 /* MetalView.swift */; };
|
||||
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 */; };
|
||||
A5AEB1652D5BE7D000513529 /* LastWindowPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */; };
|
||||
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 */; };
|
||||
A5CBD0582C9F30960017A1AE /* Cursor.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CBD0572C9F30860017A1AE /* Cursor.swift */; };
|
||||
A5CBD0592C9F37B10017A1AE /* Backport.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFFE29C2410700646FDA /* Backport.swift */; };
|
||||
@@ -86,6 +92,8 @@
|
||||
A5CEAFDC29B8009000646FDA /* SplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5CEAFDB29B8009000646FDA /* SplitView.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 */; };
|
||||
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 */; };
|
||||
A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D0AF3C2B37804400D21823 /* CodableBridge.swift */; };
|
||||
A5E112932AF73E6E00C6E0C2 /* ClipboardConfirmation.xib in Resources */ = {isa = PBXBuildFile; fileRef = A5E112922AF73E6E00C6E0C2 /* ClipboardConfirmation.xib */; };
|
||||
@@ -98,6 +106,7 @@
|
||||
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 */; };
|
||||
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 */; };
|
||||
FC9ABA9C2D0F53F80020D4C8 /* bash-completion in Resources */ = {isa = PBXBuildFile; fileRef = FC9ABA9B2D0F538D0020D4C8 /* bash-completion */; };
|
||||
/* End PBXBuildFile section */
|
||||
@@ -108,6 +117,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -156,10 +166,15 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -175,6 +190,8 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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; };
|
||||
@@ -190,6 +207,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
FC9ABA9B2D0F538D0020D4C8 /* bash-completion */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "bash-completion"; path = "../zig-out/share/bash-completion"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
@@ -254,23 +272,28 @@
|
||||
A534263D2A7DCBB000EBB7A2 /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */,
|
||||
A5A6F7292CC41B8700B232A5 /* Xcode.swift */,
|
||||
A5CEAFFE29C2410700646FDA /* Backport.swift */,
|
||||
A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */,
|
||||
A5CBD0572C9F30860017A1AE /* Cursor.swift */,
|
||||
A5D0AF3C2B37804400D21823 /* CodableBridge.swift */,
|
||||
A5A2A3C92D4445E20033CF96 /* Dock.swift */,
|
||||
A52FFF582CAA4FF1000C6A5B /* Fullscreen.swift */,
|
||||
A59630962AEE163600D64628 /* HostingWindow.swift */,
|
||||
A5CA378B2D2A4DE800931030 /* KeyboardLayout.swift */,
|
||||
A59FB5D02AE0DEA7009128F3 /* MetalView.swift */,
|
||||
A5CBD0552C9E65A50017A1AE /* DraggableWindowView.swift */,
|
||||
C159E81C2B66A06B00FDFE9C /* OSColor+Extension.swift */,
|
||||
A599CDAF2CF103F20049FA26 /* NSAppearance+Extension.swift */,
|
||||
A5A2A3CB2D444AB80033CF96 /* NSApplication+Extension.swift */,
|
||||
A54B0CEA2D0CFB4A00CBEFF8 /* NSImage+Extension.swift */,
|
||||
A52FFF5C2CAB4D05000C6A5B /* NSScreen+Extension.swift */,
|
||||
C1F26EA62B738B9900404083 /* NSView+Extension.swift */,
|
||||
AEE8B3442B9AA39600260C5E /* NSPasteboard+Extension.swift */,
|
||||
A5985CD62C320C4500C57AD3 /* String+Extension.swift */,
|
||||
A5CC36142C9CDA03004D6760 /* View+Extension.swift */,
|
||||
A5CA378D2D31D6C100931030 /* Weak.swift */,
|
||||
C1F26EE72B76CBFC00404083 /* VibrantLayer.h */,
|
||||
C1F26EE82B76CBFC00404083 /* VibrantLayer.m */,
|
||||
A5CEAFDA29B8005900646FDA /* SplitView */,
|
||||
@@ -349,12 +372,14 @@
|
||||
A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */,
|
||||
A514C8D52B54A16400493A16 /* Ghostty.Config.swift */,
|
||||
A53A6C022CCC1B7D00943E98 /* Ghostty.Action.swift */,
|
||||
A5CF66D62D29DDB100139794 /* Ghostty.Event.swift */,
|
||||
A5278A9A2AA05B2600CD3039 /* Ghostty.Input.swift */,
|
||||
A56D58852ACDDB4100508D2C /* Ghostty.Shell.swift */,
|
||||
A59630A32AF059BB00D64628 /* Ghostty.SplitNode.swift */,
|
||||
A59630A12AF0415000D64628 /* Ghostty.TerminalSplit.swift */,
|
||||
A55685DF29A03A9F004303CE /* AppError.swift */,
|
||||
A52FFF5A2CAA54A8000C6A5B /* FullscreenMode+Extension.swift */,
|
||||
A5CF66D32D289CEA00139794 /* NSEvent+Extension.swift */,
|
||||
);
|
||||
path = Ghostty;
|
||||
sourceTree = "<group>";
|
||||
@@ -397,11 +422,12 @@
|
||||
children = (
|
||||
FC9ABA9B2D0F538D0020D4C8 /* bash-completion */,
|
||||
29C15B1C2CDC3B2000520DD4 /* bat */,
|
||||
55154BDF2B33911F001622DC /* ghostty */,
|
||||
552964E52B34A9B400030505 /* vim */,
|
||||
A586167B2B7703CC009BDB1D /* fish */,
|
||||
55154BDF2B33911F001622DC /* ghostty */,
|
||||
A5985CE52C33060F00C57AD3 /* man */,
|
||||
9351BE8E2D22937F003B3499 /* nvim */,
|
||||
A5A1F8842A489D6800D1E8BC /* terminfo */,
|
||||
552964E52B34A9B400030505 /* vim */,
|
||||
FC5218F92D10FFC7004C93E0 /* zsh */,
|
||||
);
|
||||
name = Resources;
|
||||
@@ -436,6 +462,7 @@
|
||||
children = (
|
||||
A5CBD05B2CA0C5C70017A1AE /* QuickTerminal.xib */,
|
||||
A5CBD05D2CA0C5E70017A1AE /* QuickTerminalController.swift */,
|
||||
CFBB5FE92D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift */,
|
||||
A5CBD0632CA122E70017A1AE /* QuickTerminalPosition.swift */,
|
||||
A52FFF562CA90481000C6A5B /* QuickTerminalScreen.swift */,
|
||||
A5CBD05F2CA0C9080017A1AE /* QuickTerminalWindow.swift */,
|
||||
@@ -579,6 +606,7 @@
|
||||
A5985CE62C33060F00C57AD3 /* man in Resources */,
|
||||
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */,
|
||||
552964E62B34A9B400030505 /* vim in Resources */,
|
||||
9351BE8E3D22937F003B3499 /* nvim in Resources */,
|
||||
A5CBD05C2CA0C5C70017A1AE /* QuickTerminal.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -598,21 +626,26 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A5AEB1652D5BE7D000513529 /* LastWindowPosition.swift in Sources */,
|
||||
A59630A42AF059BB00D64628 /* Ghostty.SplitNode.swift in Sources */,
|
||||
A514C8D62B54A16400493A16 /* Ghostty.Config.swift in Sources */,
|
||||
A54B0CEB2D0CFB4C00CBEFF8 /* NSImage+Extension.swift in Sources */,
|
||||
A54D786C2CA7978E001B19B1 /* BaseTerminalController.swift in Sources */,
|
||||
A59FB5CF2AE0DB50009128F3 /* InspectorView.swift in Sources */,
|
||||
CFBB5FEA2D231E5000FD62EE /* QuickTerminalSpaceBehavior.swift in Sources */,
|
||||
A54B0CE92D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift in Sources */,
|
||||
A5D0AF3D2B37804400D21823 /* CodableBridge.swift in Sources */,
|
||||
A5D0AF3B2B36A1DE00D21823 /* TerminalRestorable.swift in Sources */,
|
||||
C1F26EA72B738B9900404083 /* NSView+Extension.swift in Sources */,
|
||||
A5CF66D42D289CEE00139794 /* NSEvent+Extension.swift in Sources */,
|
||||
A5CBD0642CA122E70017A1AE /* QuickTerminalPosition.swift in Sources */,
|
||||
A596309C2AEE1C9E00D64628 /* TerminalController.swift in Sources */,
|
||||
A5CC36152C9CDA06004D6760 /* View+Extension.swift in Sources */,
|
||||
A56D58892ACDE6CA00508D2C /* ServiceProvider.swift in Sources */,
|
||||
A5CBD0602CA0C90A0017A1AE /* QuickTerminalWindow.swift in Sources */,
|
||||
A5CBD05E2CA0C5EC0017A1AE /* QuickTerminalController.swift in Sources */,
|
||||
A5CF66D72D29DDB500139794 /* Ghostty.Event.swift in Sources */,
|
||||
A5A2A3CA2D4445E30033CF96 /* Dock.swift in Sources */,
|
||||
A51BFC222B2FB6B400E92F16 /* AboutView.swift in Sources */,
|
||||
A5278A9B2AA05B2600CD3039 /* Ghostty.Input.swift in Sources */,
|
||||
A5CBD0562C9E65B80017A1AE /* DraggableWindowView.swift in Sources */,
|
||||
@@ -628,12 +661,14 @@
|
||||
A5A6F72A2CC41B8900B232A5 /* Xcode.swift in Sources */,
|
||||
A52FFF5B2CAA54B1000C6A5B /* FullscreenMode+Extension.swift in Sources */,
|
||||
A5333E222B5A2128008AEFF7 /* SurfaceView_AppKit.swift in Sources */,
|
||||
A5CA378E2D31D6C300931030 /* Weak.swift in Sources */,
|
||||
A5CDF1952AAFA19600513312 /* ConfigurationErrorsView.swift in Sources */,
|
||||
A55B7BBC29B6FC330055DE60 /* SurfaceView.swift in Sources */,
|
||||
A5333E1C2B5A1CE3008AEFF7 /* CrossKit.swift in Sources */,
|
||||
A59444F729A2ED5200725BBA /* SettingsView.swift in Sources */,
|
||||
A56D58862ACDDB4100508D2C /* Ghostty.Shell.swift in Sources */,
|
||||
A5985CD72C320C4500C57AD3 /* String+Extension.swift in Sources */,
|
||||
A5A2A3CC2D444ABB0033CF96 /* NSApplication+Extension.swift in Sources */,
|
||||
A59630A22AF0415000D64628 /* Ghostty.TerminalSplit.swift in Sources */,
|
||||
A5FEB3002ABB69450068369E /* main.swift in Sources */,
|
||||
A55B7BB829B6F53A0055DE60 /* Package.swift in Sources */,
|
||||
@@ -643,6 +678,7 @@
|
||||
A5CDF1932AAF9E0800513312 /* ConfigurationErrorsController.swift in Sources */,
|
||||
A53A6C032CCC1B7F00943E98 /* Ghostty.Action.swift in Sources */,
|
||||
A54B0CED2D0CFB7700CBEFF8 /* ColorizedGhosttyIcon.swift in Sources */,
|
||||
A5CA378C2D2A4DEB00931030 /* KeyboardLayout.swift in Sources */,
|
||||
A54B0CEF2D0D2E2800CBEFF8 /* ColorizedGhosttyIconImage.swift in Sources */,
|
||||
A59FB5D12AE0DEA7009128F3 /* MetalView.swift in Sources */,
|
||||
A55685E029A03A9F004303CE /* AppError.swift in Sources */,
|
||||
@@ -761,21 +797,22 @@
|
||||
INFOPLIST_FILE = "Ghostty-Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Ghostty;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program in Ghostty wants to use AppleScript.";
|
||||
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program in Ghostty wants to use your calendar.";
|
||||
INFOPLIST_KEY_NSCameraUsageDescription = "A program in Ghostty wants to use the camera.";
|
||||
INFOPLIST_KEY_NSContactsUsageDescription = "A program in Ghostty wants to use your contacts.";
|
||||
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program running within Ghostty would like to use AppleScript.";
|
||||
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "A program running within Ghostty would like to use Bluetooth.";
|
||||
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program running within Ghostty would like to access your Calendar.";
|
||||
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_NSLocalNetworkUsageDescription = "A program in Ghostty wants to access the local network.";
|
||||
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program in Ghostty wants to use your location temporarily.";
|
||||
INFOPLIST_KEY_NSLocationUsageDescription = "A program in Ghostty wants to use your location information.";
|
||||
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "A program running within Ghostty would like to access the local network.";
|
||||
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program running within Ghostty would like to use your location temporarily.";
|
||||
INFOPLIST_KEY_NSLocationUsageDescription = "A program running within Ghostty would like to access your location information.";
|
||||
INFOPLIST_KEY_NSMainNibFile = MainMenu;
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program in Ghostty wants to use your microphone.";
|
||||
INFOPLIST_KEY_NSMotionUsageDescription = "A program in Ghostty wants to access motion data.";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program in Ghostty wants to use your photo library.";
|
||||
INFOPLIST_KEY_NSRemindersUsageDescription = "A program in Ghostty wants to access your reminders.";
|
||||
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program in Ghostty wants to use speech recognition.";
|
||||
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program in Ghostty requires elevated privileges.";
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program running within Ghostty would like to use your microphone.";
|
||||
INFOPLIST_KEY_NSMotionUsageDescription = "A program running within Ghostty would like to access motion data.";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program running within Ghostty would like to access your Photo Library.";
|
||||
INFOPLIST_KEY_NSRemindersUsageDescription = "A program running within Ghostty would like to access your reminders.";
|
||||
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program running within Ghostty would like to use speech recognition.";
|
||||
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program running within Ghostty requires elevated privileges.";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
@@ -930,21 +967,22 @@
|
||||
INFOPLIST_FILE = "Ghostty-Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Ghostty;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program in Ghostty wants to use AppleScript.";
|
||||
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program in Ghostty wants to use your calendar.";
|
||||
INFOPLIST_KEY_NSCameraUsageDescription = "A program in Ghostty wants to use the camera.";
|
||||
INFOPLIST_KEY_NSContactsUsageDescription = "A program in Ghostty wants to use your contacts.";
|
||||
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program running within Ghostty would like to use AppleScript.";
|
||||
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "A program running within Ghostty would like to use Bluetooth.";
|
||||
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program running within Ghostty would like to access your Calendar.";
|
||||
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_NSLocalNetworkUsageDescription = "A program in Ghostty wants to access the local network.";
|
||||
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program in Ghostty wants to use your location temporarily.";
|
||||
INFOPLIST_KEY_NSLocationUsageDescription = "A program in Ghostty wants to use your location information.";
|
||||
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "A program running within Ghostty would like to access the local network.";
|
||||
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program running within Ghostty would like to use your location temporarily.";
|
||||
INFOPLIST_KEY_NSLocationUsageDescription = "A program running within Ghostty would like to access your location information.";
|
||||
INFOPLIST_KEY_NSMainNibFile = MainMenu;
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program in Ghostty wants to use your microphone.";
|
||||
INFOPLIST_KEY_NSMotionUsageDescription = "A program in Ghostty wants to access motion data.";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program in Ghostty wants to use your photo library.";
|
||||
INFOPLIST_KEY_NSRemindersUsageDescription = "A program in Ghostty wants to access your reminders.";
|
||||
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program in Ghostty wants to use speech recognition.";
|
||||
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program in Ghostty requires elevated privileges.";
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program running within Ghostty would like to use your microphone.";
|
||||
INFOPLIST_KEY_NSMotionUsageDescription = "A program running within Ghostty would like to access motion data.";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program running within Ghostty would like to access your Photo Library.";
|
||||
INFOPLIST_KEY_NSRemindersUsageDescription = "A program running within Ghostty would like to access your reminders.";
|
||||
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program running within Ghostty would like to use speech recognition.";
|
||||
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program running within Ghostty requires elevated privileges.";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
@@ -983,21 +1021,22 @@
|
||||
INFOPLIST_FILE = "Ghostty-Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = Ghostty;
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
|
||||
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program in Ghostty wants to use AppleScript.";
|
||||
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program in Ghostty wants to use your calendar.";
|
||||
INFOPLIST_KEY_NSCameraUsageDescription = "A program in Ghostty wants to use the camera.";
|
||||
INFOPLIST_KEY_NSContactsUsageDescription = "A program in Ghostty wants to use your contacts.";
|
||||
INFOPLIST_KEY_NSAppleEventsUsageDescription = "A program running within Ghostty would like to use AppleScript.";
|
||||
INFOPLIST_KEY_NSBluetoothAlwaysUsageDescription = "A program running within Ghostty would like to use Bluetooth.";
|
||||
INFOPLIST_KEY_NSCalendarsUsageDescription = "A program running within Ghostty would like to access your Calendar.";
|
||||
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_NSLocalNetworkUsageDescription = "A program in Ghostty wants to access the local network.";
|
||||
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program in Ghostty wants to use your location temporarily.";
|
||||
INFOPLIST_KEY_NSLocationUsageDescription = "A program in Ghostty wants to use your location information.";
|
||||
INFOPLIST_KEY_NSLocalNetworkUsageDescription = "A program running within Ghostty would like to access the local network.";
|
||||
INFOPLIST_KEY_NSLocationTemporaryUsageDescriptionDictionary = "A program running within Ghostty would like to use your location temporarily.";
|
||||
INFOPLIST_KEY_NSLocationUsageDescription = "A program running within Ghostty would like to access your location information.";
|
||||
INFOPLIST_KEY_NSMainNibFile = MainMenu;
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program in Ghostty wants to use your microphone.";
|
||||
INFOPLIST_KEY_NSMotionUsageDescription = "A program in Ghostty wants to access motion data.";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program in Ghostty wants to use your photo library.";
|
||||
INFOPLIST_KEY_NSRemindersUsageDescription = "A program in Ghostty wants to access your reminders.";
|
||||
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program in Ghostty wants to use speech recognition.";
|
||||
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program in Ghostty requires elevated privileges.";
|
||||
INFOPLIST_KEY_NSMicrophoneUsageDescription = "A program running within Ghostty would like to use your microphone.";
|
||||
INFOPLIST_KEY_NSMotionUsageDescription = "A program running within Ghostty would like to access motion data.";
|
||||
INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "A program running within Ghostty would like to access your Photo Library.";
|
||||
INFOPLIST_KEY_NSRemindersUsageDescription = "A program running within Ghostty would like to access your reminders.";
|
||||
INFOPLIST_KEY_NSSpeechRecognitionUsageDescription = "A program running within Ghostty would like to use speech recognition.";
|
||||
INFOPLIST_KEY_NSSystemAdministrationUsageDescription = "A program running within Ghostty requires elevated privileges.";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
|
@@ -6,8 +6,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/sparkle-project/Sparkle",
|
||||
"state" : {
|
||||
"revision" : "b456fd404954a9e13f55aa0c88cd5a40b8399638",
|
||||
"version" : "2.6.3"
|
||||
"revision" : "0ef1ee0220239b3776f433314515fd849025673f",
|
||||
"version" : "2.6.4"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@@ -30,11 +30,13 @@ class AppDelegate: NSObject,
|
||||
@IBOutlet private var menuSplitRight: NSMenuItem?
|
||||
@IBOutlet private var menuSplitDown: NSMenuItem?
|
||||
@IBOutlet private var menuClose: NSMenuItem?
|
||||
@IBOutlet private var menuCloseTab: NSMenuItem?
|
||||
@IBOutlet private var menuCloseWindow: NSMenuItem?
|
||||
@IBOutlet private var menuCloseAllWindows: NSMenuItem?
|
||||
|
||||
@IBOutlet private var menuCopy: NSMenuItem?
|
||||
@IBOutlet private var menuPaste: NSMenuItem?
|
||||
@IBOutlet private var menuPasteSelection: NSMenuItem?
|
||||
@IBOutlet private var menuSelectAll: NSMenuItem?
|
||||
|
||||
@IBOutlet private var menuToggleVisibility: NSMenuItem?
|
||||
@@ -90,10 +92,8 @@ class AppDelegate: NSObject,
|
||||
return ProcessInfo.processInfo.systemUptime - applicationLaunchTime
|
||||
}
|
||||
|
||||
/// Tracks whether the application is currently visible. This can be gamed, i.e. if a user manually
|
||||
/// brings each window one by one to the front. But at worst its off by one set of toggles and this
|
||||
/// makes our logic very easy.
|
||||
private var isVisible: Bool = true
|
||||
/// Tracks the windows that we hid for toggleVisibility.
|
||||
private var hiddenState: ToggleVisibilityState? = nil
|
||||
|
||||
/// The observer for the app appearance.
|
||||
private var appearanceObserver: NSKeyValueObservation? = nil
|
||||
@@ -217,15 +217,20 @@ class AppDelegate: NSObject,
|
||||
}
|
||||
|
||||
func applicationDidBecomeActive(_ notification: Notification) {
|
||||
guard !applicationHasBecomeActive else { return }
|
||||
applicationHasBecomeActive = true
|
||||
// If we're back manually then clear the hidden state because macOS handles it.
|
||||
self.hiddenState = nil
|
||||
|
||||
// Let's launch our first window. We only do this if we have no other windows. It
|
||||
// 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()
|
||||
// First launch stuff
|
||||
if (!applicationHasBecomeActive) {
|
||||
applicationHasBecomeActive = true
|
||||
|
||||
// Let's launch our first window. We only do this if we have no other windows. It
|
||||
// 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
|
||||
// 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 (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.
|
||||
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_tab", menuItem: self.menuNewTab)
|
||||
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_all_windows", menuItem: self.menuCloseAllWindows)
|
||||
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: "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: "toggle_split_zoom", menuItem: self.menuZoomSplit)
|
||||
syncMenuShortcut(config, action: "goto_split:previous", menuItem: self.menuPreviousSplit)
|
||||
syncMenuShortcut(config, action: "goto_split:next", menuItem: self.menuNextSplit)
|
||||
syncMenuShortcut(config, action: "goto_split:top", menuItem: self.menuSelectSplitAbove)
|
||||
syncMenuShortcut(config, action: "goto_split:bottom", menuItem: self.menuSelectSplitBelow)
|
||||
syncMenuShortcut(config, action: "goto_split:up", menuItem: self.menuSelectSplitAbove)
|
||||
syncMenuShortcut(config, action: "goto_split:down", menuItem: self.menuSelectSplitBelow)
|
||||
syncMenuShortcut(config, action: "goto_split:left", menuItem: self.menuSelectSplitLeft)
|
||||
syncMenuShortcut(config, action: "goto_split:right", menuItem: self.menuSelectSplitRight)
|
||||
syncMenuShortcut(config, action: "resize_split:up,10", menuItem: self.menuMoveSplitDividerUp)
|
||||
@@ -425,6 +438,22 @@ class AppDelegate: NSObject,
|
||||
// because we let it capture and propagate.
|
||||
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 let mainMenu = NSApp.mainMenu,
|
||||
mainMenu.performKeyEquivalent(with: event) {
|
||||
@@ -438,13 +467,7 @@ class AppDelegate: NSObject,
|
||||
guard let ghostty = self.ghostty.app else { return event }
|
||||
|
||||
// Build our event input and call ghostty
|
||||
var key_ev = ghostty_input_key_s()
|
||||
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)) {
|
||||
if (ghostty_app_key(ghostty, event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS))) {
|
||||
// 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)")
|
||||
return nil
|
||||
@@ -484,11 +507,19 @@ class AppDelegate: NSObject,
|
||||
default: UserDefaults.standard.removeObject(forKey: "NSQuitAlwaysKeepsWindows")
|
||||
}
|
||||
|
||||
// Sync our auto-update settings
|
||||
updaterController.updater.automaticallyChecksForUpdates =
|
||||
config.autoUpdate == .check || config.autoUpdate == .download
|
||||
updaterController.updater.automaticallyDownloadsUpdates =
|
||||
config.autoUpdate == .download
|
||||
// Sync our auto-update settings. If SUEnableAutomaticChecks (in our Info.plist) is
|
||||
// explicitly false (NO), auto-updates are disabled. Otherwise, we use the behavior
|
||||
// defined by our "auto-update" configuration (if set) or fall back to Sparkle
|
||||
// user-based defaults.
|
||||
if Bundle.main.infoDictionary?["SUEnableAutomaticChecks"] as? Bool == false {
|
||||
updaterController.updater.automaticallyChecksForUpdates = 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
|
||||
syncMenuShortcuts(config)
|
||||
@@ -532,6 +563,30 @@ class AppDelegate: NSObject,
|
||||
self.appIcon = nil
|
||||
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:
|
||||
guard let ghostColor = config.macosIconGhostColor else { break }
|
||||
guard let screenColors = config.macosIconScreenColor else { break }
|
||||
@@ -662,7 +717,7 @@ class AppDelegate: NSObject,
|
||||
}
|
||||
|
||||
@IBAction func showHelp(_ sender: Any) {
|
||||
guard let url = URL(string: "https://github.com/ghostty-org/ghostty") else { return }
|
||||
guard let url = URL(string: "https://ghostty.org/docs") else { return }
|
||||
NSWorkspace.shared.open(url)
|
||||
}
|
||||
|
||||
@@ -684,21 +739,27 @@ class AppDelegate: NSObject,
|
||||
|
||||
/// Toggles visibility of all Ghosty Terminal windows. When hidden, activates Ghostty as the frontmost application
|
||||
@IBAction func toggleVisibility(_ sender: Any) {
|
||||
// We only care about terminal windows.
|
||||
for window in NSApp.windows.filter({ $0.windowController is BaseTerminalController }) {
|
||||
if isVisible {
|
||||
window.orderOut(nil)
|
||||
} else {
|
||||
window.makeKeyAndOrderFront(nil)
|
||||
}
|
||||
// If we have focus, then we hide all windows.
|
||||
if NSApp.isActive {
|
||||
// Toggle visibility doesn't do anything if the focused window is native
|
||||
// fullscreen. This is only relevant if Ghostty is active.
|
||||
guard let keyWindow = NSApp.keyWindow,
|
||||
!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 !isVisible {
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
// If we're not active, we want to become active
|
||||
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 {
|
||||
@@ -718,4 +779,33 @@ class AppDelegate: NSObject,
|
||||
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"?>
|
||||
<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>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23094"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23504"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<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="menuClose" destination="DVo-aG-piG" id="R3t-0C-aSU"/>
|
||||
<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="menuCopy" destination="Jqf-pv-Zcu" id="bKd-1C-oy9"/>
|
||||
<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="menuOpenConfig" destination="BOF-NM-1cW" id="Nze-Go-glw"/>
|
||||
<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="menuQuickTerminal" destination="1pv-LF-NBJ" id="glN-5B-IGi"/>
|
||||
<outlet property="menuQuit" destination="4sb-4s-VLi" id="qYN-S1-6UW"/>
|
||||
@@ -154,6 +156,12 @@
|
||||
<action selector="close:" target="-1" id="tTZ-2b-Mbm"/>
|
||||
</connections>
|
||||
</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">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
@@ -185,6 +193,12 @@
|
||||
<action selector="paste:" target="-1" id="ZKe-2B-mel"/>
|
||||
</connections>
|
||||
</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">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
|
@@ -3,6 +3,12 @@ import Cocoa
|
||||
import SwiftUI
|
||||
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.
|
||||
class QuickTerminalController: BaseTerminalController {
|
||||
override var windowNibName: NSNib.Name? { "QuickTerminal" }
|
||||
@@ -18,6 +24,12 @@ class QuickTerminalController: BaseTerminalController {
|
||||
/// application to the front.
|
||||
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.
|
||||
private var derivedConfig: DerivedConfig
|
||||
|
||||
@@ -32,6 +44,11 @@ class QuickTerminalController: BaseTerminalController {
|
||||
|
||||
// Setup our notifications for behaviors
|
||||
let center = NotificationCenter.default
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(applicationWillTerminate(_:)),
|
||||
name: NSApplication.willTerminateNotification,
|
||||
object: nil)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(onToggleFullscreen),
|
||||
@@ -52,6 +69,9 @@ class QuickTerminalController: BaseTerminalController {
|
||||
// Remove all of our notificationcenter subscriptions
|
||||
let center = NotificationCenter.default
|
||||
center.removeObserver(self)
|
||||
|
||||
// Make sure we restore our hidden dock
|
||||
hiddenDock = nil
|
||||
}
|
||||
|
||||
// MARK: NSWindowController
|
||||
@@ -69,7 +89,7 @@ class QuickTerminalController: BaseTerminalController {
|
||||
window.isRestorable = false
|
||||
|
||||
// Setup our configured appearance that we support.
|
||||
syncAppearance(ghostty.config)
|
||||
syncAppearance()
|
||||
|
||||
// Setup our initial size based on our configured position
|
||||
position.setLoaded(window)
|
||||
@@ -87,6 +107,17 @@ class QuickTerminalController: BaseTerminalController {
|
||||
|
||||
// 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) {
|
||||
super.windowDidResignKey(notification)
|
||||
|
||||
@@ -107,8 +138,32 @@ class QuickTerminalController: BaseTerminalController {
|
||||
self.previousApp = nil
|
||||
}
|
||||
|
||||
if (derivedConfig.quickTerminalAutoHide) {
|
||||
animateOut()
|
||||
// Regardless of autohide, we always want to bring the dock back
|
||||
// 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
|
||||
animateWindowIn(window: window, from: position)
|
||||
|
||||
@@ -198,8 +256,29 @@ class QuickTerminalController: BaseTerminalController {
|
||||
// Move our window off screen to the top
|
||||
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
|
||||
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
|
||||
// it visible.
|
||||
@@ -211,8 +290,20 @@ class QuickTerminalController: BaseTerminalController {
|
||||
// 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.
|
||||
DispatchQueue.main.async {
|
||||
// If we canceled our animation in we do nothing
|
||||
guard self.visible else { return }
|
||||
// If we canceled our animation clean up some state.
|
||||
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
|
||||
// requires the window is visible.
|
||||
self.syncAppearance()
|
||||
|
||||
// Once our animation is done, we must grab focus since we can't grab
|
||||
// focus of a non-visible window.
|
||||
@@ -272,6 +363,17 @@ class QuickTerminalController: BaseTerminalController {
|
||||
}
|
||||
|
||||
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.
|
||||
guard let screen = window.screen ?? NSScreen.main else { return }
|
||||
|
||||
@@ -293,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
|
||||
context.duration = derivedConfig.quickTerminalAnimationDuration
|
||||
context.timingFunction = .init(name: .easeIn)
|
||||
@@ -304,34 +411,18 @@ class QuickTerminalController: BaseTerminalController {
|
||||
})
|
||||
}
|
||||
|
||||
private func syncAppearance(_ config: Ghostty.Config) {
|
||||
private func syncAppearance() {
|
||||
guard let window else { return }
|
||||
|
||||
// If our window is not visible, then delay this. This is possible specifically
|
||||
// during state restoration but probably in other scenarios as well. To delay,
|
||||
// we just loop directly on the dispatch queue. We have to delay because some
|
||||
// APIs such as window blur have no effect unless the window is visible.
|
||||
guard window.isVisible else {
|
||||
// Weak window so that if the window changes or is destroyed we aren't holding a ref
|
||||
DispatchQueue.main.async { [weak self] in self?.syncAppearance(config) }
|
||||
return
|
||||
}
|
||||
// Change the collection behavior of the window depending on the configuration.
|
||||
window.collectionBehavior = derivedConfig.quickTerminalSpaceBehavior.collectionBehavior
|
||||
|
||||
// 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 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.
|
||||
guard window.isVisible else { return }
|
||||
|
||||
// If we have window transparency then set it transparent. Otherwise set it opaque.
|
||||
if (config.backgroundOpacity < 1) {
|
||||
if (self.derivedConfig.backgroundOpacity < 1) {
|
||||
window.isOpaque = false
|
||||
|
||||
// This is weird, but we don't use ".clear" because this creates a look that
|
||||
@@ -368,8 +459,20 @@ class QuickTerminalController: BaseTerminalController {
|
||||
ghostty.toggleFullscreen(surface: surface)
|
||||
}
|
||||
|
||||
@IBAction func toggleTerminalInspector(_ sender: Any?) {
|
||||
guard let surface = focusedSurface?.surface else { return }
|
||||
ghostty.toggleTerminalInspector(surface: surface)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||
guard target == self.focusedSurface else { return }
|
||||
@@ -391,24 +494,59 @@ class QuickTerminalController: BaseTerminalController {
|
||||
// Update our derived config
|
||||
self.derivedConfig = DerivedConfig(config)
|
||||
|
||||
syncAppearance(config)
|
||||
syncAppearance()
|
||||
}
|
||||
|
||||
private struct DerivedConfig {
|
||||
let quickTerminalScreen: QuickTerminalScreen
|
||||
let quickTerminalAnimationDuration: Double
|
||||
let quickTerminalAutoHide: Bool
|
||||
let quickTerminalSpaceBehavior: QuickTerminalSpaceBehavior
|
||||
let backgroundOpacity: Double
|
||||
|
||||
init() {
|
||||
self.quickTerminalScreen = .main
|
||||
self.quickTerminalAnimationDuration = 0.2
|
||||
self.quickTerminalAutoHide = true
|
||||
self.quickTerminalSpaceBehavior = .move
|
||||
self.backgroundOpacity = 1.0
|
||||
}
|
||||
|
||||
init(_ config: Ghostty.Config) {
|
||||
self.quickTerminalScreen = config.quickTerminalScreen
|
||||
self.quickTerminalAnimationDuration = config.quickTerminalAnimationDuration
|
||||
self.quickTerminalAutoHide = config.quickTerminalAutoHide
|
||||
self.quickTerminalSpaceBehavior = config.quickTerminalSpaceBehavior
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -69,7 +69,7 @@ enum QuickTerminalPosition : String {
|
||||
finalSize.width = screen.frame.width
|
||||
|
||||
case .left, .right:
|
||||
finalSize.height = screen.frame.height
|
||||
finalSize.height = screen.visibleFrame.height
|
||||
|
||||
case .center:
|
||||
finalSize.width = screen.frame.width / 2
|
||||
@@ -89,13 +89,13 @@ enum QuickTerminalPosition : String {
|
||||
return .init(x: screen.frame.minX, y: -window.frame.height)
|
||||
|
||||
case .left:
|
||||
return .init(x: -window.frame.width, y: 0)
|
||||
return .init(x: screen.frame.minX-window.frame.width, y: 0)
|
||||
|
||||
case .right:
|
||||
return .init(x: screen.frame.maxX, y: 0)
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
class QuickTerminalWindow: NSWindow {
|
||||
class QuickTerminalWindow: NSPanel {
|
||||
// Both of these must be true for windows without decorations to be able to
|
||||
// still become key/main and receive events.
|
||||
override var canBecomeKey: Bool { return true }
|
||||
@@ -26,22 +26,7 @@ class QuickTerminalWindow: NSWindow {
|
||||
// window remains resizable.
|
||||
self.styleMask.remove(.titled)
|
||||
|
||||
// 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.
|
||||
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]
|
||||
// We don't want to activate the owning app when quick terminal is triggered.
|
||||
self.styleMask.insert(.nonactivatingPanel)
|
||||
}
|
||||
}
|
||||
|
@@ -45,6 +45,11 @@ class BaseTerminalController: NSWindowController,
|
||||
didSet { surfaceTreeDidChange(from: oldValue, to: surfaceTree) }
|
||||
}
|
||||
|
||||
/// Whether the terminal surface should focus when the mouse is over it.
|
||||
var focusFollowsMouse: Bool {
|
||||
self.derivedConfig.focusFollowsMouse
|
||||
}
|
||||
|
||||
/// Non-nil when an alert is active so we don't overlap multiple.
|
||||
private var alert: NSAlert? = nil
|
||||
|
||||
@@ -106,8 +111,8 @@ class BaseTerminalController: NSWindowController,
|
||||
// Listen for local events that we need to know of outside of
|
||||
// single surface handlers.
|
||||
self.eventMonitor = NSEvent.addLocalMonitorForEvents(
|
||||
matching: [.flagsChanged],
|
||||
handler: localEventHandler)
|
||||
matching: [.flagsChanged]
|
||||
) { [weak self] event in self?.localEventHandler(event) }
|
||||
}
|
||||
|
||||
deinit {
|
||||
@@ -155,7 +160,7 @@ class BaseTerminalController: NSWindowController,
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
|
||||
|
||||
@objc private func didChangeScreenParametersNotification(_ notification: Notification) {
|
||||
// If we have a window that is visible and it is outside the bounds of the
|
||||
// screen then we clamp it back to within the screen.
|
||||
@@ -262,7 +267,6 @@ class BaseTerminalController: NSWindowController,
|
||||
|
||||
// Set the main window title
|
||||
window.title = to
|
||||
|
||||
}
|
||||
|
||||
func pwdDidChange(to: URL?) {
|
||||
@@ -309,11 +313,11 @@ class BaseTerminalController: NSWindowController,
|
||||
// We consider our mode changed if the types change (obvious) but
|
||||
// also if its nil (not obvious) because nil means that the style has
|
||||
// likely changed but we don't support it.
|
||||
if newStyle == nil || type(of: newStyle) != type(of: oldStyle) {
|
||||
if newStyle == nil || type(of: newStyle!) != type(of: oldStyle) {
|
||||
// Our mode changed. Exit fullscreen (since we're toggling anyways)
|
||||
// and then unset the style so that we replace it next time.
|
||||
// and then set the new style for future use
|
||||
oldStyle.exit()
|
||||
self.fullscreenStyle = nil
|
||||
self.fullscreenStyle = newStyle
|
||||
|
||||
// We're done
|
||||
return
|
||||
@@ -385,9 +389,9 @@ class BaseTerminalController: NSWindowController,
|
||||
}
|
||||
|
||||
switch (request) {
|
||||
case .osc_52_write:
|
||||
case let .osc_52_write(pasteboard):
|
||||
guard case .confirm = action else { break }
|
||||
let pb = NSPasteboard.general
|
||||
let pb = pasteboard ?? NSPasteboard.general
|
||||
pb.declareTypes([.string], owner: nil)
|
||||
pb.setString(cc.contents, forType: .string)
|
||||
case .osc_52_read, .paste:
|
||||
@@ -448,6 +452,7 @@ class BaseTerminalController: NSWindowController,
|
||||
self.alert = nil
|
||||
switch (response) {
|
||||
case .alertFirstButtonReturn:
|
||||
alert.window.orderOut(nil)
|
||||
window.close()
|
||||
|
||||
default:
|
||||
@@ -536,11 +541,11 @@ class BaseTerminalController: NSWindowController,
|
||||
}
|
||||
|
||||
@IBAction func splitMoveFocusAbove(_ sender: Any) {
|
||||
splitMoveFocus(direction: .top)
|
||||
splitMoveFocus(direction: .up)
|
||||
}
|
||||
|
||||
@IBAction func splitMoveFocusBelow(_ sender: Any) {
|
||||
splitMoveFocus(direction: .bottom)
|
||||
splitMoveFocus(direction: .down)
|
||||
}
|
||||
|
||||
@IBAction func splitMoveFocusLeft(_ sender: Any) {
|
||||
@@ -604,15 +609,18 @@ class BaseTerminalController: NSWindowController,
|
||||
private struct DerivedConfig {
|
||||
let macosTitlebarProxyIcon: Ghostty.MacOSTitlebarProxyIcon
|
||||
let windowStepResize: Bool
|
||||
let focusFollowsMouse: Bool
|
||||
|
||||
init() {
|
||||
self.macosTitlebarProxyIcon = .visible
|
||||
self.windowStepResize = false
|
||||
self.focusFollowsMouse = false
|
||||
}
|
||||
|
||||
init(_ config: Ghostty.Config) {
|
||||
self.macosTitlebarProxyIcon = config.macosTitlebarProxyIcon
|
||||
self.windowStepResize = config.windowStepResize
|
||||
self.focusFollowsMouse = config.focusFollowsMouse
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -22,7 +22,7 @@ class TerminalController: BaseTerminalController {
|
||||
private var restorable: Bool = true
|
||||
|
||||
/// 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.
|
||||
private var surfaceAppearanceCancellables: Set<AnyCancellable> = []
|
||||
@@ -60,6 +60,11 @@ class TerminalController: BaseTerminalController {
|
||||
selector: #selector(onGotoTab),
|
||||
name: Ghostty.Notification.ghosttyGotoTab,
|
||||
object: nil)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(onCloseTab),
|
||||
name: .ghosttyCloseTab,
|
||||
object: nil)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(ghosttyConfigDidChange(_:)),
|
||||
@@ -71,6 +76,12 @@ class TerminalController: BaseTerminalController {
|
||||
selector: #selector(onFrameDidChange),
|
||||
name: NSView.frameDidChangeNotification,
|
||||
object: nil)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(onEqualizeSplits),
|
||||
name: Ghostty.Notification.didEqualizeSplits,
|
||||
object: nil
|
||||
)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@@ -101,6 +112,12 @@ class TerminalController: BaseTerminalController {
|
||||
// When our fullscreen state changes, we resync our appearance because some
|
||||
// properties change when fullscreen or not.
|
||||
guard let focusedSurface else { return }
|
||||
if (!(fullscreenStyle?.isFullscreen ?? false) &&
|
||||
ghostty.config.macosTitlebarStyle == "hidden")
|
||||
{
|
||||
applyHiddenTitlebarStyle()
|
||||
}
|
||||
|
||||
syncAppearance(focusedSurface.derivedConfig)
|
||||
}
|
||||
|
||||
@@ -117,9 +134,6 @@ class TerminalController: BaseTerminalController {
|
||||
// Update our derived config
|
||||
self.derivedConfig = DerivedConfig(config)
|
||||
|
||||
guard let window = window as? TerminalWindow else { return }
|
||||
window.focusFollowsMouse = config.focusFollowsMouse
|
||||
|
||||
// If we have no surfaces in our window (is that possible?) then we update
|
||||
// our window appearance based on the root config. If we have surfaces, we
|
||||
// don't call this because the TODO
|
||||
@@ -204,6 +218,9 @@ class TerminalController: BaseTerminalController {
|
||||
// Set our explicit appearance if we need to based on the configuration.
|
||||
window.appearance = surfaceConfig.windowAppearance
|
||||
|
||||
// Update our window light/darkness based on our updated background color
|
||||
window.isLightTheme = OSColor(surfaceConfig.backgroundColor).isLightColor
|
||||
|
||||
// If our window is not visible, then we do nothing. Some things such as blurring
|
||||
// have no effect if the window is not visible. Ultimately, we'll have this called
|
||||
// at some point when a surface becomes focused.
|
||||
@@ -247,7 +264,9 @@ class TerminalController: BaseTerminalController {
|
||||
let backgroundColor: OSColor
|
||||
if let surfaceTree {
|
||||
if let focusedSurface, surfaceTree.doesBorderTop(view: focusedSurface) {
|
||||
backgroundColor = OSColor(focusedSurface.backgroundColor ?? surfaceConfig.backgroundColor)
|
||||
// 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 {
|
||||
// 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.
|
||||
@@ -270,6 +289,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
|
||||
|
||||
override func windowWillLoad() {
|
||||
@@ -277,6 +321,43 @@ class TerminalController: BaseTerminalController {
|
||||
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() {
|
||||
super.windowDidLoad()
|
||||
guard let window = window as? TerminalWindow else { return }
|
||||
@@ -297,40 +378,41 @@ class TerminalController: BaseTerminalController {
|
||||
// If window decorations are disabled, remove our title
|
||||
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
|
||||
// an initial size then we set it here now.
|
||||
if case let .leaf(leaf) = surfaceTree {
|
||||
if let initialSize = leaf.surface.initialSize,
|
||||
let screen = window.screen ?? NSScreen.main {
|
||||
// Setup our frame. We need to first subtract the views frame so that we can
|
||||
// just get the chrome frame so that we only affect the surface view size.
|
||||
// Get the current frame of the window
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Center the window to start, we'll move the window frame automatically
|
||||
// when cascading.
|
||||
window.center()
|
||||
// Set our window positioning to coordinates if config value exists, otherwise
|
||||
// fallback to original centering behavior
|
||||
setInitialWindowPosition(
|
||||
x: config.windowPositionX,
|
||||
y: config.windowPositionY,
|
||||
windowDecorations: config.windowDecorations)
|
||||
|
||||
// Make sure our theme is set on the window so styling is correct.
|
||||
if let windowTheme = config.windowTheme {
|
||||
@@ -368,38 +450,7 @@ class TerminalController: BaseTerminalController {
|
||||
|
||||
// If our titlebar style is "hidden" we adjust the style appropriately
|
||||
if (config.macosTitlebarStyle == "hidden") {
|
||||
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
|
||||
}
|
||||
applyHiddenTitlebarStyle()
|
||||
}
|
||||
|
||||
// In various situations, macOS automatically tabs new windows. Ghostty handles
|
||||
@@ -422,8 +473,6 @@ class TerminalController: BaseTerminalController {
|
||||
}
|
||||
}
|
||||
|
||||
window.focusFollowsMouse = config.focusFollowsMouse
|
||||
|
||||
// Apply any additional appearance-related properties to the new window. We
|
||||
// apply this based on the root config but change it later based on surface
|
||||
// config (see focused surface change callback).
|
||||
@@ -453,6 +502,20 @@ class TerminalController: BaseTerminalController {
|
||||
override func windowDidMove(_ notification: Notification) {
|
||||
super.windowDidMove(notification)
|
||||
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
|
||||
@@ -474,7 +537,50 @@ class TerminalController: BaseTerminalController {
|
||||
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 tabGroup = window.tabGroup else {
|
||||
// No tabs, no tab group, just perform a normal close.
|
||||
@@ -489,47 +595,34 @@ class TerminalController: BaseTerminalController {
|
||||
}
|
||||
|
||||
// Check if any windows require close confirmation.
|
||||
var needsConfirm: Bool = false
|
||||
for tabWindow in tabGroup.windows {
|
||||
guard let c = tabWindow.windowController as? TerminalController else { continue }
|
||||
if (c.surfaceTree?.needsConfirmQuit() ?? false) {
|
||||
needsConfirm = true
|
||||
break
|
||||
let needsConfirm = tabGroup.windows.contains { tabWindow in
|
||||
guard let controller = tabWindow.windowController as? TerminalController else {
|
||||
return false
|
||||
}
|
||||
return controller.surfaceTree?.needsConfirmQuit() ?? false
|
||||
}
|
||||
|
||||
// If none need confirmation then we can just close all the windows.
|
||||
if (!needsConfirm) {
|
||||
for tabWindow in tabGroup.windows {
|
||||
tabWindow.close()
|
||||
}
|
||||
|
||||
if !needsConfirm {
|
||||
tabGroup.windows.forEach { $0.close() }
|
||||
return
|
||||
}
|
||||
|
||||
// If we need confirmation by any, show one confirmation for all windows
|
||||
// in the tab group.
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Close Window?"
|
||||
alert.informativeText = "All terminal sessions in this window will be terminated."
|
||||
alert.addButton(withTitle: "Close Window")
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
alert.alertStyle = .warning
|
||||
alert.beginSheetModal(for: window, completionHandler: { response in
|
||||
if (response == .alertFirstButtonReturn) {
|
||||
for tabWindow in tabGroup.windows {
|
||||
tabWindow.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
confirmClose(
|
||||
window: window,
|
||||
messageText: "Close Window?",
|
||||
informativeText: "All terminal sessions in this window will be terminated."
|
||||
) {
|
||||
tabGroup.windows.forEach { $0.close() }
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func toggleGhosttyFullScreen(_ sender: Any) {
|
||||
@IBAction func toggleGhosttyFullScreen(_ sender: Any?) {
|
||||
guard let surface = focusedSurface?.surface else { return }
|
||||
ghostty.toggleFullscreen(surface: surface)
|
||||
}
|
||||
|
||||
@IBAction func toggleTerminalInspector(_ sender: Any) {
|
||||
@IBAction func toggleTerminalInspector(_ sender: Any?) {
|
||||
guard let surface = focusedSurface?.surface else { return }
|
||||
ghostty.toggleTerminalInspector(surface: surface)
|
||||
}
|
||||
@@ -625,13 +718,21 @@ class TerminalController: BaseTerminalController {
|
||||
// If our index is the same we do nothing
|
||||
guard finalIndex != selectedIndex else { return }
|
||||
|
||||
// Get our parent
|
||||
let parent = tabbedWindows[finalIndex]
|
||||
// Get our target window
|
||||
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)
|
||||
parent.addTabbedWindow(selectedWindow, ordered: action.amount < 0 ? .below : .above)
|
||||
selectedWindow.makeKeyAndOrderFront(nil)
|
||||
targetWindow.addTabbedWindow(selectedWindow, ordered: action.amount < 0 ? .below : .above)
|
||||
|
||||
// Ensure our window remains selected
|
||||
selectedWindow.makeKey()
|
||||
|
||||
NSAnimationContext.endGrouping()
|
||||
}
|
||||
|
||||
@objc private func onGotoTab(notification: SwiftUI.Notification) {
|
||||
@@ -686,6 +787,12 @@ class TerminalController: BaseTerminalController {
|
||||
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) {
|
||||
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||
guard target == self.focusedSurface else { return }
|
||||
@@ -703,7 +810,18 @@ class TerminalController: BaseTerminalController {
|
||||
toggleFullscreen(mode: fullscreenMode)
|
||||
}
|
||||
|
||||
private struct DerivedConfig {
|
||||
@objc private func onEqualizeSplits(_ notification: Notification) {
|
||||
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||
|
||||
// Check if target surface is in current controller's tree
|
||||
guard surfaceTree?.contains(view: target) ?? false else { return }
|
||||
|
||||
if case .split(let container) = surfaceTree {
|
||||
_ = container.equalize()
|
||||
}
|
||||
}
|
||||
|
||||
struct DerivedConfig {
|
||||
let backgroundColor: Color
|
||||
let macosTitlebarStyle: String
|
||||
|
||||
|
@@ -95,11 +95,8 @@ class TerminalManager {
|
||||
}
|
||||
}
|
||||
|
||||
// If our app isn't active, we make it active. All new_window actions
|
||||
// force our app to be active.
|
||||
if !NSApp.isActive {
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
// All new_window actions force our app to be active.
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
|
||||
// We're dispatching this async because otherwise the lastCascadePoint doesn't
|
||||
// take effect. Our best theory is there is some next-event-loop-tick logic
|
||||
|
@@ -10,7 +10,7 @@ protocol TerminalViewDelegate: AnyObject {
|
||||
|
||||
/// The title of the terminal should change.
|
||||
func titleDidChange(to: String)
|
||||
|
||||
|
||||
/// The URL of the pwd should change.
|
||||
func pwdDidChange(to: URL?)
|
||||
|
||||
@@ -56,23 +56,18 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
|
||||
|
||||
// The title for our window
|
||||
private var title: String {
|
||||
var title = "👻"
|
||||
|
||||
if let surfaceTitle = surfaceTitle {
|
||||
if (surfaceTitle.count > 0) {
|
||||
title = surfaceTitle
|
||||
}
|
||||
if let surfaceTitle, !surfaceTitle.isEmpty {
|
||||
return surfaceTitle
|
||||
}
|
||||
|
||||
return title
|
||||
return "👻"
|
||||
}
|
||||
|
||||
// The pwd of the focused surface as a URL
|
||||
private var pwdURL: URL? {
|
||||
guard let surfacePwd else { return nil }
|
||||
guard let surfacePwd, surfacePwd != "" else { return nil }
|
||||
return URL(fileURLWithPath: surfacePwd)
|
||||
}
|
||||
|
||||
|
||||
var body: some View {
|
||||
switch ghostty.readiness {
|
||||
case .loading:
|
||||
|
@@ -3,6 +3,10 @@ import Cocoa
|
||||
class TerminalWindow: NSWindow {
|
||||
@objc dynamic var keyEquivalent: String = ""
|
||||
|
||||
/// This is used to determine if certain elements should be drawn light or dark and should
|
||||
/// be updated whenever the window background color or surrounding elements changes.
|
||||
var isLightTheme: Bool = false
|
||||
|
||||
lazy var titlebarColor: NSColor = backgroundColor {
|
||||
didSet {
|
||||
guard let titlebarContainer else { return }
|
||||
@@ -115,6 +119,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
|
||||
// behaviors that don't look quite right in certain situations.
|
||||
var windowTheme: TerminalWindowTheme?
|
||||
@@ -280,7 +299,6 @@ class TerminalWindow: NSWindow {
|
||||
|
||||
|
||||
if newTabButtonImageLayer == nil {
|
||||
let isLightTheme = backgroundColor.isLightColor
|
||||
let fillColor: NSColor = isLightTheme ? .black.withAlphaComponent(0.85) : .white.withAlphaComponent(0.85)
|
||||
let newImage = NSImage(size: newTabButtonImage.size, flipped: false) { rect in
|
||||
newTabButtonImage.draw(in: rect)
|
||||
@@ -414,8 +432,6 @@ class TerminalWindow: NSWindow {
|
||||
}
|
||||
}
|
||||
|
||||
var focusFollowsMouse: Bool = false
|
||||
|
||||
// Find the NSTextField responsible for displaying the titlebar's title.
|
||||
private var titlebarTextField: NSTextField? {
|
||||
guard let titlebarView = titlebarContainer?.subviews
|
||||
@@ -669,12 +685,16 @@ fileprivate class WindowDragView: NSView {
|
||||
|
||||
// A view that matches the color of selected and unselected tabs in the adjacent tab bar.
|
||||
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 overlayLayer = VibrantLayer()
|
||||
|
||||
var isHighlighted: Bool = true {
|
||||
didSet {
|
||||
guard let terminalWindow else { return }
|
||||
|
||||
if isLightTheme {
|
||||
overlayLayer.isHidden = isHighlighted
|
||||
layer?.backgroundColor = .clear
|
||||
@@ -697,7 +717,7 @@ fileprivate class WindowButtonsBackdropView: NSView {
|
||||
|
||||
init(window: TerminalWindow) {
|
||||
self.terminalWindow = window
|
||||
self.isLightTheme = window.backgroundColor.isLightColor
|
||||
self.isLightTheme = window.isLightTheme
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
|
@@ -62,7 +62,7 @@ extension Ghostty {
|
||||
// uses to interface with the application runtime environment.
|
||||
var runtime_cfg = ghostty_runtime_config_s(
|
||||
userdata: Unmanaged.passUnretained(self).toOpaque(),
|
||||
supports_selection_clipboard: false,
|
||||
supports_selection_clipboard: true,
|
||||
wakeup_cb: { userdata in App.wakeup(userdata) },
|
||||
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) },
|
||||
@@ -117,23 +117,7 @@ extension Ghostty {
|
||||
|
||||
func appTick() {
|
||||
guard let app = self.app else { return }
|
||||
|
||||
// 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
|
||||
ghostty_app_tick(app)
|
||||
}
|
||||
|
||||
func openConfig() {
|
||||
@@ -273,7 +257,7 @@ extension Ghostty {
|
||||
// MARK: Ghostty Callbacks (iOS)
|
||||
|
||||
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(
|
||||
_ userdata: UnsafeMutableRawPointer?,
|
||||
location: ghostty_clipboard_e,
|
||||
@@ -336,13 +320,13 @@ extension Ghostty {
|
||||
let surfaceView = self.surfaceUserdata(from: userdata)
|
||||
guard let surface = surfaceView.surface else { return }
|
||||
|
||||
// We only support the standard clipboard
|
||||
if (location != GHOSTTY_CLIPBOARD_STANDARD) {
|
||||
// Get our pasteboard
|
||||
guard let pasteboard = NSPasteboard.ghostty(location) else {
|
||||
return completeClipboardRequest(surface, data: "", state: state)
|
||||
}
|
||||
|
||||
// Get our string
|
||||
let str = NSPasteboard.general.getOpinionatedStringContents() ?? ""
|
||||
let str = pasteboard.getOpinionatedStringContents() ?? ""
|
||||
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) {
|
||||
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 }
|
||||
if !confirm {
|
||||
let pb = NSPasteboard.general
|
||||
pb.declareTypes([.string], owner: nil)
|
||||
pb.setString(valueStr, forType: .string)
|
||||
pasteboard.declareTypes([.string], owner: nil)
|
||||
pasteboard.setString(valueStr, forType: .string)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -396,7 +378,7 @@ extension Ghostty {
|
||||
object: surface,
|
||||
userInfo: [
|
||||
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)
|
||||
|
||||
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
|
||||
switch (target.tag) {
|
||||
case GHOSTTY_TARGET_APP, GHOSTTY_TARGET_SURFACE:
|
||||
@@ -449,11 +431,14 @@ extension Ghostty {
|
||||
|
||||
default:
|
||||
Ghostty.logger.warning("unknown action target=\(target.tag.rawValue)")
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
// Action dispatch
|
||||
switch (action.tag) {
|
||||
case GHOSTTY_ACTION_QUIT:
|
||||
quit(app)
|
||||
|
||||
case GHOSTTY_ACTION_NEW_WINDOW:
|
||||
newWindow(app, target: target)
|
||||
|
||||
@@ -463,17 +448,20 @@ extension Ghostty {
|
||||
case GHOSTTY_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:
|
||||
toggleFullscreen(app, target: target, mode: action.action.toggle_fullscreen)
|
||||
|
||||
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:
|
||||
gotoTab(app, target: target, tab: action.action.goto_tab)
|
||||
return gotoTab(app, target: target, tab: action.action.goto_tab)
|
||||
|
||||
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:
|
||||
resizeSplit(app, target: target, resize: action.action.resize_split)
|
||||
@@ -553,10 +541,30 @@ extension Ghostty {
|
||||
fallthrough
|
||||
case GHOSTTY_ACTION_QUIT_TIMER:
|
||||
Ghostty.logger.info("known but unimplemented action action=\(action.tag.rawValue)")
|
||||
|
||||
return false
|
||||
default:
|
||||
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) {
|
||||
@@ -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(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s,
|
||||
@@ -692,15 +721,19 @@ extension Ghostty {
|
||||
private static func moveTab(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s,
|
||||
move: ghostty_action_move_tab_s) {
|
||||
move: ghostty_action_move_tab_s) -> Bool {
|
||||
switch (target.tag) {
|
||||
case GHOSTTY_TARGET_APP:
|
||||
Ghostty.logger.warning("move tab does nothing with an app target")
|
||||
return
|
||||
return false
|
||||
|
||||
case GHOSTTY_TARGET_SURFACE:
|
||||
guard let surface = target.target.surface else { return }
|
||||
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
||||
guard let surface = target.target.surface else { return false }
|
||||
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(
|
||||
name: .ghosttyMoveTab,
|
||||
object: surfaceView,
|
||||
@@ -712,20 +745,27 @@ extension Ghostty {
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private static func gotoTab(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s,
|
||||
tab: ghostty_action_goto_tab_e) {
|
||||
tab: ghostty_action_goto_tab_e) -> Bool {
|
||||
switch (target.tag) {
|
||||
case GHOSTTY_TARGET_APP:
|
||||
Ghostty.logger.warning("goto tab does nothing with an app target")
|
||||
return
|
||||
return false
|
||||
|
||||
case GHOSTTY_TARGET_SURFACE:
|
||||
guard let surface = target.target.surface else { return }
|
||||
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
||||
guard let surface = target.target.surface else { return false }
|
||||
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(
|
||||
name: Notification.ghosttyGotoTab,
|
||||
object: surfaceView,
|
||||
@@ -737,20 +777,31 @@ extension Ghostty {
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private static func gotoSplit(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s,
|
||||
direction: ghostty_action_goto_split_e) {
|
||||
direction: ghostty_action_goto_split_e) -> Bool {
|
||||
switch (target.tag) {
|
||||
case GHOSTTY_TARGET_APP:
|
||||
Ghostty.logger.warning("goto split does nothing with an app target")
|
||||
return
|
||||
return false
|
||||
|
||||
case GHOSTTY_TARGET_SURFACE:
|
||||
guard let surface = target.target.surface else { return }
|
||||
guard let surfaceView = self.surfaceView(from: surface) else { return }
|
||||
guard let surface = target.target.surface else { return false }
|
||||
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(
|
||||
name: Notification.ghosttyFocusSplit,
|
||||
object: surfaceView,
|
||||
@@ -762,6 +813,8 @@ extension Ghostty {
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private static func resizeSplit(
|
||||
|
@@ -132,15 +132,6 @@ extension Ghostty {
|
||||
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 {
|
||||
guard let config = self.config else { return "" }
|
||||
var v: UnsafePointer<Int8>? = nil
|
||||
@@ -149,6 +140,20 @@ extension Ghostty {
|
||||
guard let ptr = v else { return "" }
|
||||
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 {
|
||||
guard let config = self.config else { return "" }
|
||||
@@ -160,11 +165,14 @@ extension Ghostty {
|
||||
}
|
||||
|
||||
var windowDecorations: Bool {
|
||||
guard let config = self.config else { return true }
|
||||
var v = false;
|
||||
let defaultValue = true
|
||||
guard let config = self.config else { return defaultValue }
|
||||
var v: UnsafePointer<Int8>? = nil
|
||||
let key = "window-decoration"
|
||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
||||
return v;
|
||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return defaultValue }
|
||||
guard let ptr = v else { return defaultValue }
|
||||
let str = String(cString: ptr)
|
||||
return WindowDecoration(rawValue: str)?.enabled() ?? defaultValue
|
||||
}
|
||||
|
||||
var windowTheme: String? {
|
||||
@@ -331,7 +339,7 @@ extension Ghostty {
|
||||
var backgroundBlurRadius: Int {
|
||||
guard let config = self.config else { return 1 }
|
||||
var v: Int = 0
|
||||
let key = "background-blur-radius"
|
||||
let key = "background-blur"
|
||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
||||
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 {
|
||||
let backgroundColor = OSColor(backgroundColor)
|
||||
let isLightBackground = backgroundColor.isLightColor
|
||||
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)
|
||||
@@ -406,6 +425,16 @@ extension Ghostty {
|
||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
||||
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
|
||||
|
||||
var resizeOverlay: ResizeOverlay {
|
||||
@@ -437,15 +466,14 @@ extension Ghostty {
|
||||
return v;
|
||||
}
|
||||
|
||||
var autoUpdate: AutoUpdate {
|
||||
let defaultValue = AutoUpdate.check
|
||||
guard let config = self.config else { return defaultValue }
|
||||
var autoUpdate: AutoUpdate? {
|
||||
guard let config = self.config else { return nil }
|
||||
var v: UnsafePointer<Int8>? = nil
|
||||
let key = "auto-update"
|
||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return defaultValue }
|
||||
guard let ptr = v else { return defaultValue }
|
||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return nil }
|
||||
guard let ptr = v else { return nil }
|
||||
let str = String(cString: ptr)
|
||||
return AutoUpdate(rawValue: str) ?? defaultValue
|
||||
return AutoUpdate(rawValue: str)
|
||||
}
|
||||
|
||||
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 {
|
||||
switch (self) {
|
||||
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
|
||||
/// top-left-most view. This is used when creating a split or closing a split to find the
|
||||
/// next view to send focus to.
|
||||
func preferredFocus(_ direction: SplitFocusDirection = .top) -> SurfaceView {
|
||||
func preferredFocus(_ direction: SplitFocusDirection = .up) -> SurfaceView {
|
||||
let container: Container
|
||||
switch (self) {
|
||||
case .leaf(let leaf):
|
||||
@@ -64,10 +73,10 @@ extension Ghostty {
|
||||
|
||||
let node: SplitNode
|
||||
switch (direction) {
|
||||
case .previous, .top, .left:
|
||||
case .previous, .up, .left:
|
||||
node = container.bottomRight
|
||||
|
||||
case .next, .bottom, .right:
|
||||
case .next, .down, .right:
|
||||
node = container.topLeft
|
||||
}
|
||||
|
||||
@@ -120,14 +129,7 @@ extension Ghostty {
|
||||
|
||||
/// Returns true if the split tree contains the given view.
|
||||
func contains(view: SurfaceView) -> Bool {
|
||||
switch (self) {
|
||||
case .leaf(let leaf):
|
||||
return leaf.surface == view
|
||||
|
||||
case .split(let container):
|
||||
return container.topLeft.contains(view: view) ||
|
||||
container.bottomRight.contains(view: view)
|
||||
}
|
||||
return leaf(for: view) != nil
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
||||
func makeIterator() -> IndexingIterator<[Leaf]> {
|
||||
@@ -431,12 +449,12 @@ extension Ghostty {
|
||||
struct Neighbors {
|
||||
var left: SplitNode?
|
||||
var right: SplitNode?
|
||||
var top: SplitNode?
|
||||
var bottom: SplitNode?
|
||||
var up: SplitNode?
|
||||
var down: SplitNode?
|
||||
|
||||
/// 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
|
||||
/// 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 next: SplitNode?
|
||||
|
||||
@@ -448,8 +466,8 @@ extension Ghostty {
|
||||
let map: [SplitFocusDirection : KeyPath<Self, SplitNode?>] = [
|
||||
.previous: \.previous,
|
||||
.next: \.next,
|
||||
.top: \.top,
|
||||
.bottom: \.bottom,
|
||||
.up: \.up,
|
||||
.down: \.down,
|
||||
.left: \.left,
|
||||
.right: \.right,
|
||||
]
|
||||
|
@@ -50,7 +50,6 @@ extension Ghostty {
|
||||
var body: some View {
|
||||
let center = NotificationCenter.default
|
||||
let pubZoom = center.publisher(for: Notification.didToggleSplitZoom)
|
||||
let pubEqualize = center.publisher(for: Notification.didEqualizeSplits)
|
||||
|
||||
// If we're zoomed, we don't render anything, we are transparent. This
|
||||
// ensures that the View stays around so we don't lose our state, but
|
||||
@@ -76,7 +75,6 @@ extension Ghostty {
|
||||
container: container
|
||||
)
|
||||
.onReceive(pubZoom) { onZoom(notification: $0) }
|
||||
.onReceive(pubEqualize) { onEqualize(notification: $0) }
|
||||
}
|
||||
}
|
||||
.navigationTitle(surfaceTitle ?? "Ghostty")
|
||||
@@ -137,11 +135,6 @@ extension Ghostty {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func onEqualize(notification: SwiftUI.Notification) {
|
||||
guard case .split(let c) = node else { return }
|
||||
_ = c.equalize()
|
||||
}
|
||||
}
|
||||
|
||||
/// A noSplit leaf node of a split tree.
|
||||
@@ -205,6 +198,7 @@ extension Ghostty {
|
||||
alert.beginSheetModal(for: window, completionHandler: { response in
|
||||
switch (response) {
|
||||
case .alertFirstButtonReturn:
|
||||
alert.window.orderOut(nil)
|
||||
node = nil
|
||||
|
||||
default:
|
||||
@@ -308,7 +302,7 @@ extension Ghostty {
|
||||
resizeIncrements: .init(width: 1, height: 1),
|
||||
resizePublisher: container.resizeEvent,
|
||||
left: {
|
||||
let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.right : \.bottom
|
||||
let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.right : \.down
|
||||
|
||||
TerminalSplitNested(
|
||||
node: closeableTopLeft(),
|
||||
@@ -318,7 +312,7 @@ extension Ghostty {
|
||||
])
|
||||
)
|
||||
}, right: {
|
||||
let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.left : \.top
|
||||
let neighborKey: WritableKeyPath<SplitNode.Neighbors, SplitNode?> = container.direction == .horizontal ? \.left : \.up
|
||||
|
||||
TerminalSplitNested(
|
||||
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.
|
||||
enum SplitFocusDirection {
|
||||
case previous, next, top, bottom, left, right
|
||||
case previous, next, up, down, left, right
|
||||
|
||||
/// Initialize from a Ghostty API enum.
|
||||
static func from(direction: ghostty_action_goto_split_e) -> Self? {
|
||||
@@ -77,11 +77,11 @@ extension Ghostty {
|
||||
case GHOSTTY_GOTO_SPLIT_NEXT:
|
||||
return .next
|
||||
|
||||
case GHOSTTY_GOTO_SPLIT_TOP:
|
||||
return .top
|
||||
case GHOSTTY_GOTO_SPLIT_UP:
|
||||
return .up
|
||||
|
||||
case GHOSTTY_GOTO_SPLIT_BOTTOM:
|
||||
return .bottom
|
||||
case GHOSTTY_GOTO_SPLIT_DOWN:
|
||||
return .down
|
||||
|
||||
case GHOSTTY_GOTO_SPLIT_LEFT:
|
||||
return .left
|
||||
@@ -102,11 +102,11 @@ extension Ghostty {
|
||||
case .next:
|
||||
return GHOSTTY_GOTO_SPLIT_NEXT
|
||||
|
||||
case .top:
|
||||
return GHOSTTY_GOTO_SPLIT_TOP
|
||||
case .up:
|
||||
return GHOSTTY_GOTO_SPLIT_UP
|
||||
|
||||
case .bottom:
|
||||
return GHOSTTY_GOTO_SPLIT_BOTTOM
|
||||
case .down:
|
||||
return GHOSTTY_GOTO_SPLIT_DOWN
|
||||
|
||||
case .left:
|
||||
return GHOSTTY_GOTO_SPLIT_LEFT
|
||||
@@ -159,7 +159,7 @@ extension Ghostty {
|
||||
case osc_52_read
|
||||
|
||||
/// 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
|
||||
func text() -> String {
|
||||
@@ -188,7 +188,7 @@ extension Ghostty {
|
||||
case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_READ:
|
||||
return .osc_52_read
|
||||
case GHOSTTY_CLIPBOARD_REQUEST_OSC_52_WRITE:
|
||||
return .osc_52_write
|
||||
return .osc_52_write(nil)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@@ -198,6 +198,14 @@ extension Ghostty {
|
||||
/// macos-icon
|
||||
enum MacOSIcon: String {
|
||||
case official
|
||||
case blueprint
|
||||
case chalkboard
|
||||
case glass
|
||||
case holographic
|
||||
case microchip
|
||||
case paper
|
||||
case retro
|
||||
case xray
|
||||
case customStyle = "custom-style"
|
||||
}
|
||||
|
||||
@@ -236,6 +244,9 @@ extension Notification.Name {
|
||||
/// Goto tab. Has tab index in the userinfo.
|
||||
static let ghosttyMoveTab = Notification.Name("com.mitchellh.ghostty.moveTab")
|
||||
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
|
||||
|
@@ -92,22 +92,6 @@ extension Ghostty {
|
||||
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
|
||||
|
||||
// If our geo size changed then we show the resize overlay as configured.
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import AppKit
|
||||
import SwiftUI
|
||||
import CoreText
|
||||
import UserNotifications
|
||||
@@ -12,7 +13,14 @@ extension Ghostty {
|
||||
// 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
|
||||
// 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
|
||||
// changed with escape codes.
|
||||
@@ -113,6 +121,12 @@ extension Ghostty {
|
||||
// A small delay that is introduced before a title change to avoid flickers
|
||||
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
|
||||
override var acceptsFirstResponder: Bool { return true }
|
||||
|
||||
@@ -136,6 +150,13 @@ extension Ghostty {
|
||||
// can do SOMETHING.
|
||||
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
|
||||
// so there is no window where we can't receive them.
|
||||
let center = NotificationCenter.default
|
||||
@@ -170,6 +191,15 @@ extension Ghostty {
|
||||
name: NSWindow.didChangeScreenNotification,
|
||||
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.
|
||||
let surface_cfg = baseConfig ?? SurfaceConfiguration()
|
||||
var surface_cfg_c = surface_cfg.ghosttyConfig(view: self)
|
||||
@@ -201,6 +231,9 @@ extension Ghostty {
|
||||
|
||||
ghostty_surface_set_color_scheme(surface, scheme)
|
||||
}
|
||||
|
||||
// The UTTypes that can be dragged onto this view.
|
||||
registerForDraggedTypes(Array(Self.dropTypes))
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@@ -212,6 +245,11 @@ extension Ghostty {
|
||||
let center = NotificationCenter.default
|
||||
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
|
||||
// state is invalid to prevent the surface from being restored.
|
||||
invalidateRestorableState()
|
||||
@@ -356,22 +394,52 @@ 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
|
||||
|
||||
@objc private func onUpdateRendererHealth(notification: SwiftUI.Notification) {
|
||||
guard let healthAny = notification.userInfo?["health"] else { return }
|
||||
guard let health = healthAny as? ghostty_action_renderer_health_e else { return }
|
||||
healthy = health == GHOSTTY_RENDERER_HEALTH_OK
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.healthy = health == GHOSTTY_RENDERER_HEALTH_OK
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func ghosttyDidContinueKeySequence(notification: SwiftUI.Notification) {
|
||||
guard let keyAny = notification.userInfo?[Ghostty.Notification.KeySequenceKey] else { return }
|
||||
guard let key = keyAny as? Ghostty.KeyEquivalent else { return }
|
||||
keySequence.append(key)
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.keySequence.append(key)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func ghosttyDidEndKeySequence(notification: SwiftUI.Notification) {
|
||||
keySequence = []
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.keySequence = []
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func ghosttyConfigDidChange(_ notification: SwiftUI.Notification) {
|
||||
@@ -381,7 +449,9 @@ extension Ghostty {
|
||||
] as? Ghostty.Config else { return }
|
||||
|
||||
// Update our derived config
|
||||
self.derivedConfig = DerivedConfig(config)
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.derivedConfig = DerivedConfig(config)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func ghosttyColorDidChange(_ notification: SwiftUI.Notification) {
|
||||
@@ -391,7 +461,9 @@ extension Ghostty {
|
||||
|
||||
switch (change.kind) {
|
||||
case .background:
|
||||
self.backgroundColor = change.color
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.backgroundColor = change.color
|
||||
}
|
||||
|
||||
default:
|
||||
// We don't do anything for the other colors yet.
|
||||
@@ -413,7 +485,9 @@ extension Ghostty {
|
||||
// We also just trigger a backing property change. Just in case the screen has
|
||||
// a different scaling factor, this ensures that we update our content scale.
|
||||
// Issue: https://github.com/ghostty-org/ghostty/issues/2731
|
||||
viewDidChangeBackingProperties()
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
self?.viewDidChangeBackingProperties()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSView
|
||||
@@ -605,11 +679,12 @@ extension Ghostty {
|
||||
let mods = Ghostty.ghosttyMods(event.modifierFlags)
|
||||
ghostty_surface_mouse_pos(surface, pos.x, frame.height - pos.y, mods)
|
||||
|
||||
// If focus follows mouse is enabled then move focus to this surface.
|
||||
if let window = self.window as? TerminalWindow,
|
||||
window.isKeyWindow &&
|
||||
window.focusFollowsMouse &&
|
||||
!self.focused
|
||||
// Handle focus-follows-mouse
|
||||
if let window,
|
||||
let controller = window.windowController as? BaseTerminalController,
|
||||
(window.isKeyWindow &&
|
||||
!self.focused &&
|
||||
controller.focusFollowsMouse)
|
||||
{
|
||||
Ghostty.moveFocus(to: self)
|
||||
}
|
||||
@@ -751,8 +826,23 @@ extension Ghostty {
|
||||
// know if these events cleared it.
|
||||
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])
|
||||
|
||||
// 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
|
||||
// first because if we completed a preedit, the text will be available here
|
||||
// AND we'll have a preedit.
|
||||
@@ -760,7 +850,7 @@ extension Ghostty {
|
||||
if let list = keyTextAccumulator, list.count > 0 {
|
||||
handled = true
|
||||
for text in list {
|
||||
keyAction(action, event: event, text: text)
|
||||
_ = keyAction(action, event: event, text: text)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -770,38 +860,49 @@ extension Ghostty {
|
||||
// the preedit.
|
||||
if (markedText.length > 0 || markedTextBefore) {
|
||||
handled = true
|
||||
keyAction(action, event: event, preedit: markedText.string)
|
||||
_ = keyAction(action, event: event, preedit: markedText.string)
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
// No text or anything, we want to handle this manually.
|
||||
keyAction(action, event: event)
|
||||
_ = keyAction(action, event: event)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
override func performKeyEquivalent(with event: NSEvent) -> Bool {
|
||||
// Only process key down events
|
||||
if (event.type != .keyDown) {
|
||||
switch (event.type) {
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Only process keys when Control is active. All known issues we're
|
||||
// resolving happen only in this scenario. This probably isn't fully robust
|
||||
// but we can broaden the scope as we find more cases.
|
||||
if (!event.modifierFlags.contains(.control)) {
|
||||
return false
|
||||
// If this event as-is would result in a key binding then we send it.
|
||||
if let surface,
|
||||
ghostty_surface_key_is_binding(
|
||||
surface,
|
||||
event.ghosttyKeyEvent(GHOSTTY_ACTION_PRESS)) {
|
||||
self.keyDown(with: event)
|
||||
return true
|
||||
}
|
||||
|
||||
let equivalent: String
|
||||
@@ -819,14 +920,25 @@ extension Ghostty {
|
||||
case "\r":
|
||||
// Pass C-<return> through verbatim
|
||||
// (prevent the default context menu equivalent)
|
||||
if (!event.modifierFlags.contains(.control)) {
|
||||
return false
|
||||
}
|
||||
|
||||
equivalent = "\r"
|
||||
|
||||
case ".":
|
||||
if (!event.modifierFlags.contains(.command)) {
|
||||
return false
|
||||
}
|
||||
|
||||
equivalent = "."
|
||||
|
||||
default:
|
||||
// Ignore other events
|
||||
return false
|
||||
}
|
||||
|
||||
let newEvent = NSEvent.keyEvent(
|
||||
let finalEvent = NSEvent.keyEvent(
|
||||
with: .keyDown,
|
||||
location: event.locationInWindow,
|
||||
modifierFlags: event.modifierFlags,
|
||||
@@ -839,7 +951,7 @@ extension Ghostty {
|
||||
keyCode: event.keyCode
|
||||
)
|
||||
|
||||
self.keyDown(with: newEvent!)
|
||||
self.keyDown(with: finalEvent!)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -854,6 +966,9 @@ extension Ghostty {
|
||||
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
|
||||
// but this is super cheap and flagsChanged isn't that common.
|
||||
let mods = Ghostty.ghosttyMods(event.modifierFlags)
|
||||
@@ -884,45 +999,38 @@ extension Ghostty {
|
||||
}
|
||||
}
|
||||
|
||||
keyAction(action, event: event)
|
||||
_ = keyAction(action, event: event)
|
||||
}
|
||||
|
||||
private func keyAction(_ action: ghostty_input_action_e, event: NSEvent) {
|
||||
guard let surface = self.surface else { return }
|
||||
|
||||
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) -> Bool {
|
||||
guard let surface = self.surface else { return false }
|
||||
return ghostty_surface_key(surface, event.ghosttyKeyEvent(action))
|
||||
}
|
||||
|
||||
private func keyAction(_ action: ghostty_input_action_e, event: NSEvent, preedit: String) {
|
||||
guard let surface = self.surface else { return }
|
||||
private func keyAction(
|
||||
_ action: ghostty_input_action_e,
|
||||
event: NSEvent, preedit: String
|
||||
) -> Bool {
|
||||
guard let surface = self.surface else { return false }
|
||||
|
||||
preedit.withCString { ptr in
|
||||
var key_ev = ghostty_input_key_s()
|
||||
key_ev.action = action
|
||||
key_ev.mods = Ghostty.ghosttyMods(event.modifierFlags)
|
||||
key_ev.keycode = UInt32(event.keyCode)
|
||||
return preedit.withCString { ptr in
|
||||
var key_ev = event.ghosttyKeyEvent(action)
|
||||
key_ev.text = ptr
|
||||
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) {
|
||||
guard let surface = self.surface else { return }
|
||||
private func keyAction(
|
||||
_ action: ghostty_input_action_e,
|
||||
event: NSEvent, text: String
|
||||
) -> Bool {
|
||||
guard let surface = self.surface else { return false }
|
||||
|
||||
text.withCString { ptr in
|
||||
var key_ev = ghostty_input_key_s()
|
||||
key_ev.action = action
|
||||
key_ev.mods = Ghostty.ghosttyMods(event.modifierFlags)
|
||||
key_ev.keycode = UInt32(event.keyCode)
|
||||
return text.withCString { ptr in
|
||||
var key_ev = event.ghosttyKeyEvent(action)
|
||||
key_ev.text = ptr
|
||||
ghostty_surface_key(surface, key_ev)
|
||||
return ghostty_surface_key(surface, key_ev)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1040,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?) {
|
||||
guard let surface = self.surface else { return }
|
||||
let action = "select_all"
|
||||
@@ -1361,3 +1477,78 @@ extension Ghostty.SurfaceView: NSServicesMenuRequestor {
|
||||
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 OSColor = NSColor
|
||||
typealias OSSize = NSSize
|
||||
typealias OSPasteboard = NSPasteboard
|
||||
|
||||
protocol OSViewRepresentable: NSViewRepresentable where NSViewType == OSViewType {
|
||||
associatedtype OSViewType: NSView
|
||||
@@ -34,6 +35,7 @@ import UIKit
|
||||
typealias OSView = UIView
|
||||
typealias OSColor = UIColor
|
||||
typealias OSSize = CGSize
|
||||
typealias OSPasteboard = UIPasteboard
|
||||
|
||||
protocol OSViewRepresentable: UIViewRepresentable {
|
||||
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
|
||||
|
||||
private func hideDock() {
|
||||
NSApp.presentationOptions.insert(.autoHideDock)
|
||||
NSApp.acquirePresentationOption(.autoHideDock)
|
||||
}
|
||||
|
||||
private func unhideDock() {
|
||||
NSApp.presentationOptions.remove(.autoHideDock)
|
||||
NSApp.releasePresentationOption(.autoHideDock)
|
||||
}
|
||||
|
||||
// MARK: Menu
|
||||
|
||||
func hideMenu() {
|
||||
NSApp.presentationOptions.insert(.autoHideMenuBar)
|
||||
NSApp.acquirePresentationOption(.autoHideMenuBar)
|
||||
}
|
||||
|
||||
func unhideMenu() {
|
||||
NSApp.presentationOptions.remove(.autoHideMenuBar)
|
||||
NSApp.releasePresentationOption(.autoHideMenuBar)
|
||||
}
|
||||
|
||||
/// 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 GhosttyKit
|
||||
|
||||
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.
|
||||
/// 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.
|
||||
/// If all of the above fail, returns None.
|
||||
func getOpinionatedStringContents() -> String? {
|
||||
if let file = self.string(forType: .fileURL) {
|
||||
if let path = NSURL(string: file)?.path {
|
||||
return path
|
||||
}
|
||||
if let urls = readObjects(forClasses: [NSURL.self]) as? [URL],
|
||||
urls.count > 0 {
|
||||
return urls
|
||||
.map { $0.isFileURL ? Ghostty.Shell.escape($0.path) : $0.absoluteString }
|
||||
.joined(separator: " ")
|
||||
}
|
||||
|
||||
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}"
|
78
nix/build-support/check-zig-cache.sh
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# This script checks if the build.zig.zon.nix file is up-to-date.
|
||||
# If the `--update` flag is passed, it will update all necessary
|
||||
# files to be up to date.
|
||||
#
|
||||
# The files owned by this are:
|
||||
#
|
||||
# - build.zig.zon.nix
|
||||
# - build.zig.zon.txt
|
||||
# - build.zig.zon2json-lock
|
||||
#
|
||||
# All of these are auto-generated and should not be edited manually.
|
||||
|
||||
# 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 ""
|
||||
}
|
||||
|
||||
ROOT="$(realpath "$(dirname "$0")/../../")"
|
||||
BUILD_ZIG_ZON="$ROOT/build.zig.zon"
|
||||
BUILD_ZIG_ZON_LOCK="$ROOT/build.zig.zon2json-lock"
|
||||
BUILD_ZIG_ZON_NIX="$ROOT/build.zig.zon.nix"
|
||||
BUILD_ZIG_ZON_TXT="$ROOT/build.zig.zon.txt"
|
||||
|
||||
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"
|
||||
|
||||
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
|
||||
jq -r '.[] .url' "$BUILD_ZIG_ZON_LOCK" | sort > "$BUILD_ZIG_ZON_TXT"
|
||||
mv "$WORK_DIR/build.zig.zon.nix" "$BUILD_ZIG_ZON_NIX"
|
||||
echo -e "\nOK: build.zig.zon.nix updated."
|
||||
exit 0
|
||||
fi
|
||||
|
@@ -1,32 +1,13 @@
|
||||
#!/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).
|
||||
# NOTE THIS IS A TEMPORARY SCRIPT TO SUPPORT PACKAGE MAINTAINERS.
|
||||
#
|
||||
# An example of this happening:
|
||||
# A future Zig version will hopefully fix the issue where
|
||||
# `zig build --fetch` doesn't fetch transitive dependencies[1]. When that
|
||||
# is resolved, we won't need any special machinery for the general use case
|
||||
# at all and packagers can just use `zig build --fetch`.
|
||||
#
|
||||
# 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`.
|
||||
# [1]: https://github.com/ziglang/zig/issues/20976
|
||||
|
||||
if [ -z ${ZIG_GLOBAL_CACHE_DIR+x} ]
|
||||
then
|
||||
@@ -34,6 +15,13 @@ then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
zig build --fetch
|
||||
zig fetch git+https://github.com/zigimg/zigimg#3a667bdb3d7f0955a5a51c8468eac83210c1439e
|
||||
zig fetch git+https://github.com/mitchellh/libxev#f6a672a78436d8efee1aa847a43a900ad773618b
|
||||
# Go through each line of our build.zig.zon.txt and fetch it.
|
||||
SCRIPT_PATH="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
||||
ZON_TXT_FILE="$SCRIPT_PATH/../../build.zig.zon.txt"
|
||||
while IFS= read -r url; do
|
||||
echo "Fetching: $url"
|
||||
zig fetch "$url" >/dev/null 2>&1 || {
|
||||
echo "Failed to fetch: $url" >&2
|
||||
exit 1
|
||||
}
|
||||
done < "$ZON_TXT_FILE"
|
||||
|
@@ -30,7 +30,9 @@
|
||||
glib,
|
||||
glslang,
|
||||
gtk4,
|
||||
gobject-introspection,
|
||||
libadwaita,
|
||||
blueprint-compiler,
|
||||
adwaita-icon-theme,
|
||||
hicolor-icon-theme,
|
||||
harfbuzz,
|
||||
@@ -47,10 +49,16 @@
|
||||
simdutf,
|
||||
zlib,
|
||||
alejandra,
|
||||
jq,
|
||||
minisign,
|
||||
pandoc,
|
||||
hyperfine,
|
||||
typos,
|
||||
wayland,
|
||||
wayland-scanner,
|
||||
wayland-protocols,
|
||||
zig2nix,
|
||||
system,
|
||||
}: let
|
||||
# See package.nix. Keep in sync.
|
||||
rpathLibs =
|
||||
@@ -80,6 +88,8 @@
|
||||
libadwaita
|
||||
gtk4
|
||||
glib
|
||||
gobject-introspection
|
||||
wayland
|
||||
];
|
||||
in
|
||||
mkShell {
|
||||
@@ -88,6 +98,7 @@ in
|
||||
packages =
|
||||
[
|
||||
# For builds
|
||||
jq
|
||||
llvmPackages_latest.llvm
|
||||
minisign
|
||||
ncurses
|
||||
@@ -96,6 +107,7 @@ in
|
||||
scdoc
|
||||
zig
|
||||
zip
|
||||
zig2nix.packages.${system}.zon2nix
|
||||
|
||||
# For web and wasm stuff
|
||||
nodejs
|
||||
@@ -150,9 +162,14 @@ in
|
||||
libXrandr
|
||||
|
||||
# Only needed for GTK builds
|
||||
blueprint-compiler
|
||||
libadwaita
|
||||
gtk4
|
||||
glib
|
||||
gobject-introspection
|
||||
wayland
|
||||
wayland-scanner
|
||||
wayland-protocols
|
||||
];
|
||||
|
||||
# This should be set onto the rpath of the ghostty binary if you want
|
||||
|
184
nix/package.nix
@@ -2,6 +2,7 @@
|
||||
lib,
|
||||
stdenv,
|
||||
bzip2,
|
||||
callPackage,
|
||||
expat,
|
||||
fontconfig,
|
||||
freetype,
|
||||
@@ -10,13 +11,11 @@
|
||||
oniguruma,
|
||||
zlib,
|
||||
libGL,
|
||||
libX11,
|
||||
libXcursor,
|
||||
libXi,
|
||||
libXrandr,
|
||||
glib,
|
||||
gtk4,
|
||||
gobject-introspection,
|
||||
libadwaita,
|
||||
blueprint-compiler,
|
||||
wrapGAppsHook4,
|
||||
gsettings-desktop-schemas,
|
||||
git,
|
||||
@@ -26,6 +25,15 @@
|
||||
pandoc,
|
||||
revision ? "dirty",
|
||||
optimize ? "Debug",
|
||||
enableX11 ? true,
|
||||
libX11,
|
||||
libXcursor,
|
||||
libXi,
|
||||
libXrandr,
|
||||
enableWayland ? true,
|
||||
wayland,
|
||||
wayland-protocols,
|
||||
wayland-scanner,
|
||||
}: let
|
||||
# The Zig hook has no way to select the release type without actual
|
||||
# overriding of the default flags.
|
||||
@@ -35,92 +43,52 @@
|
||||
# ultimately acted on and has made its way to a nixpkgs implementation, this
|
||||
# can probably be removed in favor of that.
|
||||
zig_hook = zig_0_13.hook.overrideAttrs {
|
||||
zig_default_flags = "-Dcpu=baseline -Doptimize=${optimize}";
|
||||
};
|
||||
|
||||
# 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;
|
||||
zig_default_flags = "-Dcpu=baseline -Doptimize=${optimize} --color off";
|
||||
};
|
||||
in
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "ghostty";
|
||||
version = "1.0.0";
|
||||
inherit src;
|
||||
version = "1.1.3";
|
||||
|
||||
nativeBuildInputs = [
|
||||
git
|
||||
ncurses
|
||||
pandoc
|
||||
pkg-config
|
||||
zig_hook
|
||||
wrapGAppsHook4
|
||||
];
|
||||
# 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
|
||||
../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 =
|
||||
[
|
||||
@@ -136,28 +104,37 @@ in
|
||||
oniguruma
|
||||
zlib
|
||||
|
||||
libX11
|
||||
libXcursor
|
||||
libXi
|
||||
libXrandr
|
||||
|
||||
libadwaita
|
||||
gtk4
|
||||
glib
|
||||
gsettings-desktop-schemas
|
||||
]
|
||||
++ lib.optionals enableX11 [
|
||||
libX11
|
||||
libXcursor
|
||||
libXi
|
||||
libXrandr
|
||||
]
|
||||
++ lib.optionals enableWayland [
|
||||
wayland
|
||||
];
|
||||
|
||||
dontConfigure = true;
|
||||
|
||||
zigBuildFlags = "-Dversion-string=${finalAttrs.version}-${revision}-nix";
|
||||
zigBuildFlags = [
|
||||
"--system"
|
||||
"${finalAttrs.deps}"
|
||||
"-Dversion-string=${finalAttrs.version}-${revision}-nix"
|
||||
"-Dgtk-x11=${lib.boolToString enableX11}"
|
||||
"-Dgtk-wayland=${lib.boolToString enableWayland}"
|
||||
];
|
||||
|
||||
preBuild = ''
|
||||
rm -rf $ZIG_GLOBAL_CACHE_DIR
|
||||
cp -r --reflink=auto ${zigCache} $ZIG_GLOBAL_CACHE_DIR
|
||||
chmod u+rwX -R $ZIG_GLOBAL_CACHE_DIR
|
||||
'';
|
||||
|
||||
outputs = ["out" "terminfo" "shell_integration" "vim"];
|
||||
outputs = [
|
||||
"out"
|
||||
"terminfo"
|
||||
"shell_integration"
|
||||
"vim"
|
||||
];
|
||||
|
||||
postInstall = ''
|
||||
terminfo_src=${
|
||||
@@ -183,14 +160,13 @@ in
|
||||
echo "$vim" >> "$out/nix-support/propagated-user-env-packages"
|
||||
'';
|
||||
|
||||
postFixup = ''
|
||||
patchelf --add-rpath "${lib.makeLibraryPath [libX11]}" "$out/bin/.ghostty-wrapped"
|
||||
'';
|
||||
|
||||
meta = {
|
||||
homepage = "https://github.com/ghostty-org/ghostty";
|
||||
homepage = "https://ghostty.org";
|
||||
license = lib.licenses.mit;
|
||||
platforms = ["x86_64-linux" "aarch64-linux"];
|
||||
platforms = [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
];
|
||||
mainProgram = "ghostty";
|
||||
};
|
||||
})
|
||||
|
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";
|
||||
};
|
||||
}
|