Compare commits
645 Commits
v1.1.0
...
opengl-tes
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4bcaf1f3b4 | ||
![]() |
bcff4e18f4 | ||
![]() |
67f47a6e22 | ||
![]() |
416b617de9 | ||
![]() |
5d9bee98e8 | ||
![]() |
bf179207a1 | ||
![]() |
150b7bdad1 | ||
![]() |
20111048a4 | ||
![]() |
36c7fda40b | ||
![]() |
99da99cb9c | ||
![]() |
b8b5882c75 | ||
![]() |
e6b77cf008 | ||
![]() |
90a3719275 | ||
![]() |
c1bf301bc7 | ||
![]() |
7200c87326 | ||
![]() |
89728070e9 | ||
![]() |
c1ef0c0369 | ||
![]() |
8cc5ee39d0 | ||
![]() |
c58fe676ad | ||
![]() |
68b8ba12c7 | ||
![]() |
8aab16b6e6 | ||
![]() |
b722f537e7 | ||
![]() |
9d5ce6e47d | ||
![]() |
36f841ee80 | ||
![]() |
1317e62722 | ||
![]() |
747c43ffa0 | ||
![]() |
f659e70938 | ||
![]() |
4a51643043 | ||
![]() |
9a9bc43a9b | ||
![]() |
1980f9aed4 | ||
![]() |
141b697f9d | ||
![]() |
f31f8bb782 | ||
![]() |
f0080529c4 | ||
![]() |
88ff566e06 | ||
![]() |
75045d92b4 | ||
![]() |
41130ce25f | ||
![]() |
9b3bd146c6 | ||
![]() |
a7a57011f0 | ||
![]() |
d61e53d6d6 | ||
![]() |
9c064216a2 | ||
![]() |
7b8c2232d3 | ||
![]() |
6f84a5d682 | ||
![]() |
bd315c8394 | ||
![]() |
403b3617f7 | ||
![]() |
39d4cc3702 | ||
![]() |
55c221c572 | ||
![]() |
15efb913bf | ||
![]() |
f412237106 | ||
![]() |
cb7180ef77 | ||
![]() |
e93be23f68 | ||
![]() |
bfec219510 | ||
![]() |
e56002e149 | ||
![]() |
907e62aa29 | ||
![]() |
dc21ea9998 | ||
![]() |
c5b1961c6b | ||
![]() |
bd7c5cc95f | ||
![]() |
02bfa946d5 | ||
![]() |
f7999444eb | ||
![]() |
07ec421cd3 | ||
![]() |
648e0a06ab | ||
![]() |
4d0bf303c6 | ||
![]() |
c0f5f913c9 | ||
![]() |
bab8c28c8b | ||
![]() |
69590c80a1 | ||
![]() |
5bf10dce12 | ||
![]() |
0f2f0ab69f | ||
![]() |
946c0c370f | ||
![]() |
ee78a3d345 | ||
![]() |
72017ea4d8 | ||
![]() |
8c0ccfc5b3 | ||
![]() |
a773588c99 | ||
![]() |
73341b052b | ||
![]() |
1ee9c85954 | ||
![]() |
e3fbbe8fe3 | ||
![]() |
d75c5ec038 | ||
![]() |
ee95a5f3e0 | ||
![]() |
742bca713d | ||
![]() |
899ab302e1 | ||
![]() |
590eb60759 | ||
![]() |
daa79c3598 | ||
![]() |
29322535a5 | ||
![]() |
e0fe12cc05 | ||
![]() |
1d040dd17d | ||
![]() |
7f7191dfec | ||
![]() |
a2df8e4b86 | ||
![]() |
c344c320eb | ||
![]() |
291c2f541c | ||
![]() |
f8590ce44f | ||
![]() |
d3424a922a | ||
![]() |
5cd8ebdafd | ||
![]() |
3bc2b02303 | ||
![]() |
ec4d110251 | ||
![]() |
2018a8fd3c | ||
![]() |
4b1d1e0ed4 | ||
![]() |
644acdacdc | ||
![]() |
791d332a25 | ||
![]() |
009b53c45e | ||
![]() |
5ad8ea6b22 | ||
![]() |
f8f9f7041a | ||
![]() |
2e6a2a148f | ||
![]() |
550edd4262 | ||
![]() |
cfea2ea12c | ||
![]() |
234b804872 | ||
![]() |
f37d1fd7ed | ||
![]() |
c2aac45848 | ||
![]() |
b497400be6 | ||
![]() |
ec5066988e | ||
![]() |
4ab3754a59 | ||
![]() |
b9ea32b8ce | ||
![]() |
69dfc8870d | ||
![]() |
09d538b620 | ||
![]() |
07b653bd71 | ||
![]() |
e8101c1136 | ||
![]() |
8eacde92e6 | ||
![]() |
221f905a1c | ||
![]() |
73c7943fff | ||
![]() |
d8497d9b16 | ||
![]() |
ef0ff94c75 | ||
![]() |
572fc8b5d7 | ||
![]() |
1f6b1d75eb | ||
![]() |
aeada3f1a8 | ||
![]() |
f1f9db8b96 | ||
![]() |
a542e63582 | ||
![]() |
bdb66984b6 | ||
![]() |
6613a695f0 | ||
![]() |
b96a5d702b | ||
![]() |
66c83648c8 | ||
![]() |
1dbeba7065 | ||
![]() |
fc21444f2d | ||
![]() |
907ed239a1 | ||
![]() |
b123b14686 | ||
![]() |
99bde549af | ||
![]() |
1f6aa0e90d | ||
![]() |
18084a3e61 | ||
![]() |
816ff8cef0 | ||
![]() |
2e45a4c7a3 | ||
![]() |
3116a1b92c | ||
![]() |
601acf4059 | ||
![]() |
7e9be00924 | ||
![]() |
43467690f3 | ||
![]() |
2408d4c6a9 | ||
![]() |
0f4d2bb237 | ||
![]() |
86d3f18707 | ||
![]() |
2f814b37e8 | ||
![]() |
bd848a27d2 | ||
![]() |
f091a69790 | ||
![]() |
2466de4556 | ||
![]() |
251caeb22a | ||
![]() |
3abbe6d3ba | ||
![]() |
7e2286eb8c | ||
![]() |
95daca616d | ||
![]() |
14b66e93d1 | ||
![]() |
0ecee3ee92 | ||
![]() |
57d0a4d2e7 | ||
![]() |
480b1a9805 | ||
![]() |
ebffe299ce | ||
![]() |
300f4544ef | ||
![]() |
843cc83f42 | ||
![]() |
78f16d040d | ||
![]() |
6767493428 | ||
![]() |
bb3dad1309 | ||
![]() |
d3fd2b02e7 | ||
![]() |
5efa2a6ca1 | ||
![]() |
a0080ddad7 | ||
![]() |
b0b2de01f5 | ||
![]() |
b48fcf33f7 | ||
![]() |
35aab1a302 | ||
![]() |
c67c7da582 | ||
![]() |
9d86bdfe72 | ||
![]() |
abb97fa574 | ||
![]() |
7e268b9a43 | ||
![]() |
d2931b5d8f | ||
![]() |
2ef11fb65f | ||
![]() |
d511b3601d | ||
![]() |
e03e98e106 | ||
![]() |
dcb8440b52 | ||
![]() |
7eddf98269 | ||
![]() |
da731e6caa | ||
![]() |
79a9ddf66f | ||
![]() |
e8a988f6d3 | ||
![]() |
c7681e8fd7 | ||
![]() |
cff092f4c6 | ||
![]() |
3ebd5b839f | ||
![]() |
be839cb681 | ||
![]() |
3c49bc5086 | ||
![]() |
edf619205c | ||
![]() |
238573d42e | ||
![]() |
4cf127a064 | ||
![]() |
e8c20b5501 | ||
![]() |
cb8085ab72 | ||
![]() |
dd95f727ec | ||
![]() |
67488754d5 | ||
![]() |
4a215a9518 | ||
![]() |
b25da6b9c4 | ||
![]() |
3ba4864f6c | ||
![]() |
77e16770cc | ||
![]() |
cd442eb9e2 | ||
![]() |
6c00c36d62 | ||
![]() |
9ed76729ab | ||
![]() |
b4bfdb2c44 | ||
![]() |
23d2d4ec70 | ||
![]() |
40c1140f7d | ||
![]() |
27c4fd76f3 | ||
![]() |
22ed08cfd8 | ||
![]() |
260a90cbf0 | ||
![]() |
e07b6fdf6b | ||
![]() |
44d4990eb2 | ||
![]() |
58b0434092 | ||
![]() |
d6bd7b56b3 | ||
![]() |
8f7425f78c | ||
![]() |
bd617c52e9 | ||
![]() |
f71b294697 | ||
![]() |
7123d4e055 | ||
![]() |
1f695c2646 | ||
![]() |
58adaffcb9 | ||
![]() |
8f62901218 | ||
![]() |
2f65f01fc8 | ||
![]() |
66e8d91957 | ||
![]() |
fd6e4fd615 | ||
![]() |
423bc1971b | ||
![]() |
2c6e6ad680 | ||
![]() |
6373399e59 | ||
![]() |
6b1a017a86 | ||
![]() |
e252932bde | ||
![]() |
9c97084ad0 | ||
![]() |
5851bad4a0 | ||
![]() |
9360afd29f | ||
![]() |
ee8ae196ee | ||
![]() |
a646aee6bd | ||
![]() |
28e20f3015 | ||
![]() |
f93eb0b27f | ||
![]() |
82326508b1 | ||
![]() |
8d395c094b | ||
![]() |
fc893ae7e3 | ||
![]() |
30a49d0458 | ||
![]() |
68a2478317 | ||
![]() |
c1e87e7122 | ||
![]() |
6e751d2d7e | ||
![]() |
dbba3f1a60 | ||
![]() |
34388ab5df | ||
![]() |
8721f2ae51 | ||
![]() |
29447b60b3 | ||
![]() |
e2b5584a8d | ||
![]() |
e7a9d6a81d | ||
![]() |
b342909e10 | ||
![]() |
df62d45b36 | ||
![]() |
5d5a632a89 | ||
![]() |
ed647caa2e | ||
![]() |
a3cd7c6f02 | ||
![]() |
0e6c26bbfe | ||
![]() |
efc1b10bfd | ||
![]() |
17cae57f51 | ||
![]() |
c6485b9fd5 | ||
![]() |
afb154ee5d | ||
![]() |
5accc069fb | ||
![]() |
8838ebf02a | ||
![]() |
f73c1a2c59 | ||
![]() |
9681009650 | ||
![]() |
b0f1f19da0 | ||
![]() |
a85651fe4f | ||
![]() |
1cfe7027e5 | ||
![]() |
744240c009 | ||
![]() |
ef88d1cba9 | ||
![]() |
5a5478abe1 | ||
![]() |
a1437e5579 | ||
![]() |
31df9d5576 | ||
![]() |
ef7f8cb3da | ||
![]() |
63ea1ab32e | ||
![]() |
cdfa028521 | ||
![]() |
c7938af7be | ||
![]() |
78a98e01fc | ||
![]() |
d85ed8275e | ||
![]() |
6581b9cf41 | ||
![]() |
12ba5d89f0 | ||
![]() |
16a61c43dd | ||
![]() |
acbb1d3bd4 | ||
![]() |
dec14f3096 | ||
![]() |
5fdb732798 | ||
![]() |
4e5e4a7c2f | ||
![]() |
c068390634 | ||
![]() |
3cac06a70a | ||
![]() |
62dcddb315 | ||
![]() |
0638eca633 | ||
![]() |
c6b049b12b | ||
![]() |
3f847de964 | ||
![]() |
9972eeb673 | ||
![]() |
166362d349 | ||
![]() |
0b7df7511a | ||
![]() |
df9de1523c | ||
![]() |
22f3e60dcf | ||
![]() |
af2d710000 | ||
![]() |
3d08b1c4aa | ||
![]() |
22d99f2533 | ||
![]() |
8fadb54e65 | ||
![]() |
0321aec68f | ||
![]() |
d284146621 | ||
![]() |
92340f8fb0 | ||
![]() |
b4349d3226 | ||
![]() |
1254c6b981 | ||
![]() |
ce8bfe45ed | ||
![]() |
e1e1539e4f | ||
![]() |
9bcf554139 | ||
![]() |
1dc375dd0e | ||
![]() |
f4f36a9a98 | ||
![]() |
71ae51b4b3 | ||
![]() |
61f41e5c01 | ||
![]() |
2ef042978d | ||
![]() |
a52c603f16 | ||
![]() |
da10269d3f | ||
![]() |
ac7029256a | ||
![]() |
995959dce4 | ||
![]() |
aa4aaa200f | ||
![]() |
bc2acdd060 | ||
![]() |
2f63f840de | ||
![]() |
eaeb6a620f | ||
![]() |
65c65b9c97 | ||
![]() |
dad2cf887b | ||
![]() |
22c506b03e | ||
![]() |
427da79a02 | ||
![]() |
a8b6b96fbd | ||
![]() |
e7cbb7fd16 | ||
![]() |
bdf0f27d1a | ||
![]() |
0af256b57a | ||
![]() |
398add17f1 | ||
![]() |
eec150d4cd | ||
![]() |
573fe7348b | ||
![]() |
f1134640c5 | ||
![]() |
5131f8a71c | ||
![]() |
867982a2ff | ||
![]() |
bde5b963d0 | ||
![]() |
5d80db2ef8 | ||
![]() |
32a62ff862 | ||
![]() |
d4bcac0150 | ||
![]() |
51dc1e2e8c | ||
![]() |
4f3c4037aa | ||
![]() |
7c19dd5a33 | ||
![]() |
8758295647 | ||
![]() |
1ee8dfc99c | ||
![]() |
cd287b4161 | ||
![]() |
454a53b3f1 | ||
![]() |
6189f5d09e | ||
![]() |
dcd17c6ac4 | ||
![]() |
3542778d84 | ||
![]() |
95fc5ad1e9 | ||
![]() |
cc9c45de2a | ||
![]() |
5e9908af27 | ||
![]() |
6b75ca40ca | ||
![]() |
3f715c296a | ||
![]() |
e307f1a373 | ||
![]() |
726ac36612 | ||
![]() |
ce62b5cc5e | ||
![]() |
1b6b029e0d | ||
![]() |
2383e4d90d | ||
![]() |
7c6375f744 | ||
![]() |
6770ad3736 | ||
![]() |
a262da92bf | ||
![]() |
870b74f4da | ||
![]() |
c1fb9a33f7 | ||
![]() |
4291e1c5d7 | ||
![]() |
b5ecd7b6be | ||
![]() |
d532a6e260 | ||
![]() |
2697061e5b | ||
![]() |
d1fa933006 | ||
![]() |
2d5a07c795 | ||
![]() |
b3f994a9d2 | ||
![]() |
38908e0126 | ||
![]() |
8eaa901aec | ||
![]() |
aa2dbe2919 | ||
![]() |
c9f8732e5c | ||
![]() |
da32534e8a | ||
![]() |
3dbbbdee0b | ||
![]() |
16c6903706 | ||
![]() |
3253df3d54 | ||
![]() |
830a117555 | ||
![]() |
768b0a79cb | ||
![]() |
429c8ab277 | ||
![]() |
76bd002aa4 | ||
![]() |
246f4baf7c | ||
![]() |
1013ba63ee | ||
![]() |
b1df97b33f | ||
![]() |
25b93ceb55 | ||
![]() |
497a1b6f8f | ||
![]() |
bf7e9603d2 | ||
![]() |
2e7ed98dfd | ||
![]() |
36e6ed3339 | ||
![]() |
9a5bc65034 | ||
![]() |
8ad2ae6ab4 | ||
![]() |
fe11efff63 | ||
![]() |
b0d68324a6 | ||
![]() |
fb35d10981 | ||
![]() |
191b19f9a5 | ||
![]() |
c7b3cbd397 | ||
![]() |
0ce1342263 | ||
![]() |
6d8db4b380 | ||
![]() |
d7a82f212a | ||
![]() |
aeccbd266a | ||
![]() |
ac9f8ba9b1 | ||
![]() |
baa47ff24e | ||
![]() |
818bc779b3 | ||
![]() |
88a6b542b3 | ||
![]() |
494273cf08 | ||
![]() |
03d1240999 | ||
![]() |
b551e106a8 | ||
![]() |
0c3b873dde | ||
![]() |
d3623393a6 | ||
![]() |
2adee4290a | ||
![]() |
94e2982d4b | ||
![]() |
238b0faf5c | ||
![]() |
927f626d9a | ||
![]() |
bd6a133e95 | ||
![]() |
1c41cf236f | ||
![]() |
a831df903d | ||
![]() |
ff5c1001c6 | ||
![]() |
e4cf81c2ba | ||
![]() |
cb5379ab1d | ||
![]() |
7e5c57a848 | ||
![]() |
5841a4f958 | ||
![]() |
725488e1a2 | ||
![]() |
0acf82bb9c | ||
![]() |
9944fd5958 | ||
![]() |
e7d4daa7c1 | ||
![]() |
5de0e775cb | ||
![]() |
301fdff58f | ||
![]() |
99c7abb43a | ||
![]() |
a85de40710 | ||
![]() |
e174fb2748 | ||
![]() |
403eab2cf0 | ||
![]() |
bdafc2227c | ||
![]() |
c9cafd3051 | ||
![]() |
48f94e6fcc | ||
![]() |
43b2e43a11 | ||
![]() |
8dffe3450c | ||
![]() |
ae953b5f10 | ||
![]() |
c7635201ab | ||
![]() |
c35ca1e87f | ||
![]() |
d06d6796c5 | ||
![]() |
e6c9dc7040 | ||
![]() |
5d0dde57f9 | ||
![]() |
d0108416d0 | ||
![]() |
71297870cf | ||
![]() |
fcde494440 | ||
![]() |
b7bd8444c7 | ||
![]() |
2b2b3c5b3b | ||
![]() |
818c81282b | ||
![]() |
f0842c5599 | ||
![]() |
2d0940f6ae | ||
![]() |
f1f1120749 | ||
![]() |
c1ff382e97 | ||
![]() |
29f25ae474 | ||
![]() |
c7971b562e | ||
![]() |
b7009202ce | ||
![]() |
25c5ecf553 | ||
![]() |
09fbf096d3 | ||
![]() |
b975f1e860 | ||
![]() |
8c4b0f815d | ||
![]() |
4e8e2d9796 | ||
![]() |
90ce5b75f1 | ||
![]() |
fd9cbde3d8 | ||
![]() |
b0edda4b69 | ||
![]() |
bbd279bb4f | ||
![]() |
bdbf30dc96 | ||
![]() |
a111b3f96f | ||
![]() |
f239df59ca | ||
![]() |
c036eb2444 | ||
![]() |
cee189de11 | ||
![]() |
d2f82b2e40 | ||
![]() |
3e669fc4bb | ||
![]() |
1a5b69181f | ||
![]() |
55c5b8b72f | ||
![]() |
5e77a973b2 | ||
![]() |
9c81cd323d | ||
![]() |
78446008c4 | ||
![]() |
e09d8455a1 | ||
![]() |
1dcea3b11f | ||
![]() |
9d62c31f44 | ||
![]() |
0272ad9edb | ||
![]() |
f3829072f3 | ||
![]() |
2b6b7c19d2 | ||
![]() |
3a9d61d6e4 | ||
![]() |
6d8b3973e4 | ||
![]() |
aa4d9809c3 | ||
![]() |
ec8e7d9d86 | ||
![]() |
f1f23e1c7d | ||
![]() |
2e0e8af1ad | ||
![]() |
53f1b4bc15 | ||
![]() |
eae420a241 | ||
![]() |
bf49784b7d | ||
![]() |
b6a3b98828 | ||
![]() |
eb0816c2c4 | ||
![]() |
30fa18390f | ||
![]() |
f51789b17a | ||
![]() |
97b104cf9d | ||
![]() |
a7d1029e5c | ||
![]() |
aed30502bd | ||
![]() |
56efaf0c82 | ||
![]() |
686b6a2971 | ||
![]() |
9cb297202b | ||
![]() |
a0f243691a | ||
![]() |
cdfea4bc7e | ||
![]() |
5c1f85e861 | ||
![]() |
2da7d77feb | ||
![]() |
1121ea9d6c | ||
![]() |
a581955b9b | ||
![]() |
228b4dbd60 | ||
![]() |
8231ebb770 | ||
![]() |
95e6a27393 | ||
![]() |
f32ad5216b | ||
![]() |
56b244973f | ||
![]() |
cdd287c88b | ||
![]() |
315df0ab3f | ||
![]() |
ac7aa757bd | ||
![]() |
52a5069dec | ||
![]() |
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 | ||
![]() |
0016199ec3 | ||
![]() |
8e2c55a5da | ||
![]() |
05fe3e7ec3 | ||
![]() |
1b52365541 | ||
![]() |
8da600a62e |
2
.envrc
@@ -4,3 +4,5 @@ if has nix; then
|
||||
watch_file nix/{devShell,package,wraptest}.nix
|
||||
use flake
|
||||
fi
|
||||
|
||||
source_env_if_exists .envrc.local
|
||||
|
6
.gitattributes
vendored
@@ -1,6 +1,12 @@
|
||||
build.zig.zon.nix linguist-generated=true
|
||||
build.zig.zon.txt linguist-generated=true
|
||||
build.zig.zon.json linguist-generated=true
|
||||
vendor/** linguist-vendored
|
||||
website/** linguist-documentation
|
||||
pkg/breakpad/vendor/** linguist-vendored
|
||||
pkg/cimgui/vendor/** linguist-vendored
|
||||
pkg/glfw/wayland-headers/** linguist-vendored
|
||||
pkg/libintl/config.h linguist-generated=true
|
||||
pkg/libintl/libintl.h linguist-generated=true
|
||||
pkg/simdutf/vendor/** linguist-vendored
|
||||
src/terminal/res/** linguist-vendored
|
||||
|
4
.github/workflows/nix.yml
vendored
@@ -50,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
|
||||
|
2
.github/workflows/publish-tag.yml
vendored
@@ -55,7 +55,7 @@ jobs:
|
||||
|
||||
- name: Download Staged Appcast
|
||||
run: |
|
||||
curl -L https://release.files.ghostty.org/${GHOSTTY_VERSION}/appcast-staged.xml
|
||||
curl -L https://release.files.ghostty.org/${GHOSTTY_VERSION}/appcast-staged.xml > appcast-staged.xml
|
||||
mv appcast-staged.xml appcast.xml
|
||||
|
||||
- name: Upload Appcast
|
||||
|
2
.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
|
||||
|
19
.github/workflows/release-tag.yml
vendored
@@ -77,9 +77,18 @@ jobs:
|
||||
needs: [setup]
|
||||
env:
|
||||
GHOSTTY_VERSION: ${{ needs.setup.outputs.version }}
|
||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@v1.2.0
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
@@ -91,8 +100,8 @@ jobs:
|
||||
|
||||
- name: Create Tarball
|
||||
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
|
||||
nix develop -c zig build distcheck
|
||||
cp zig-out/dist/ghostty-${GHOSTTY_VERSION}.tar.gz .
|
||||
|
||||
- name: Sign Tarball
|
||||
run: |
|
||||
@@ -108,8 +117,6 @@ jobs:
|
||||
path: |-
|
||||
ghostty-${{ env.GHOSTTY_VERSION }}.tar.gz
|
||||
ghostty-${{ env.GHOSTTY_VERSION }}.tar.gz.minisig
|
||||
ghostty-source.tar.gz
|
||||
ghostty-source.tar.gz.minisig
|
||||
|
||||
build-macos:
|
||||
needs: [setup]
|
||||
@@ -136,7 +143,7 @@ jobs:
|
||||
|
||||
- name: Setup Sparkle
|
||||
env:
|
||||
SPARKLE_VERSION: 2.6.3
|
||||
SPARKLE_VERSION: 2.6.4
|
||||
run: |
|
||||
mkdir -p .action/sparkle
|
||||
cd .action/sparkle
|
||||
@@ -298,7 +305,7 @@ jobs:
|
||||
|
||||
- name: Setup Sparkle
|
||||
env:
|
||||
SPARKLE_VERSION: 2.6.3
|
||||
SPARKLE_VERSION: 2.6.4
|
||||
run: |
|
||||
mkdir -p .action/sparkle
|
||||
cd .action/sparkle
|
||||
|
18
.github/workflows/release-tip.yml
vendored
@@ -101,8 +101,17 @@ jobs:
|
||||
)
|
||||
}}
|
||||
runs-on: namespace-profile-ghostty-md
|
||||
env:
|
||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@v1.2.0
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=channel:nixos-unstable
|
||||
@@ -111,12 +120,17 @@ jobs:
|
||||
name: ghostty
|
||||
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"
|
||||
- name: Create Tarball
|
||||
run: git archive --format=tgz --prefix=ghostty-source/ -o ghostty-source.tar.gz HEAD
|
||||
run: |
|
||||
rm -rf zig-out/dist
|
||||
nix develop -c zig build distcheck
|
||||
cp zig-out/dist/*.tar.gz ghostty-source.tar.gz
|
||||
|
||||
- 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
|
||||
|
||||
- name: Update Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
@@ -164,7 +178,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
|
||||
|
428
.github/workflows/test.yml
vendored
@@ -8,15 +8,19 @@ name: Test
|
||||
jobs:
|
||||
required:
|
||||
name: "Required Checks: Test"
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
runs-on: namespace-profile-ghostty-xsm
|
||||
needs:
|
||||
- build
|
||||
- build-bench
|
||||
- build-dist
|
||||
- build-flatpak
|
||||
- build-linux
|
||||
- build-linux-libghostty
|
||||
- build-nix
|
||||
- build-snap
|
||||
- build-macos
|
||||
- build-macos-matrix
|
||||
- build-windows
|
||||
- build-windows-cross
|
||||
- test
|
||||
- test-gtk
|
||||
- test-sentry-linux
|
||||
@@ -24,7 +28,11 @@ jobs:
|
||||
- prettier
|
||||
- alejandra
|
||||
- typos
|
||||
- translations
|
||||
- blueprint-compiler
|
||||
- test-pkg-linux
|
||||
- test-debian-12
|
||||
- zig-fmt
|
||||
steps:
|
||||
- id: status
|
||||
name: Determine status
|
||||
@@ -45,52 +53,6 @@ jobs:
|
||||
echo "One or more required build workflows failed: ${{ steps.status.outputs.results }}"
|
||||
exit 1
|
||||
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ["namespace-profile-ghostty-md"]
|
||||
|
||||
target: [
|
||||
aarch64-linux,
|
||||
x86_64-linux,
|
||||
x86-windows-gnu,
|
||||
x86_64-windows-gnu,
|
||||
# We don't support cross-compiling to macOS because the macOS build
|
||||
# requires xcode due to the swift harness.
|
||||
#aarch64-macos,
|
||||
#x86_64-macos,
|
||||
]
|
||||
runs-on: ${{ matrix.os }}
|
||||
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 }}"
|
||||
|
||||
# Cross-compile the binary. We always use static building for this
|
||||
# because its the only way to access the headers.
|
||||
- name: Test Build
|
||||
run: nix develop -c zig build -Dapp-runtime=glfw -Dtarget=${{ matrix.target }}
|
||||
|
||||
build-bench:
|
||||
# We build benchmarks on large because it uses ReleaseFast
|
||||
runs-on: namespace-profile-ghostty-lg
|
||||
@@ -121,6 +83,73 @@ jobs:
|
||||
- name: Build Benchmarks
|
||||
run: nix develop -c zig build -Dapp-runtime=glfw -Demit-bench
|
||||
|
||||
build-flatpak:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
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: Build with Flatpak
|
||||
run: |
|
||||
nix develop -c \
|
||||
zig build \
|
||||
-Dflatpak=true
|
||||
|
||||
build-linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [namespace-profile-ghostty-md, namespace-profile-ghostty-md-arm64]
|
||||
runs-on: ${{ matrix.os }}
|
||||
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 Build
|
||||
run: nix develop -c zig build -Dapp-runtime=glfw
|
||||
|
||||
build-linux-libghostty:
|
||||
runs-on: namespace-profile-ghostty-md
|
||||
needs: test
|
||||
@@ -183,6 +212,45 @@ jobs:
|
||||
- name: Test NixOS package build
|
||||
run: nix build .#ghostty
|
||||
|
||||
build-dist:
|
||||
runs-on: namespace-profile-ghostty-md
|
||||
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: Build and Check Source Tarball
|
||||
run: |
|
||||
rm -rf zig-out/dist
|
||||
nix develop -c zig build distcheck
|
||||
cp zig-out/dist/*.tar.gz ghostty-source.tar.gz
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: source-tarball
|
||||
path: |-
|
||||
ghostty-source.tar.gz
|
||||
|
||||
build-macos:
|
||||
runs-on: namespace-profile-ghostty-macos
|
||||
needs: test
|
||||
@@ -202,10 +270,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.
|
||||
@@ -238,35 +310,65 @@ 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-snap:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
[namespace-profile-ghostty-snap, namespace-profile-ghostty-snap-arm64]
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: test
|
||||
env:
|
||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@v1.2.0
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- run: sudo apt install -y udev
|
||||
- run: sudo systemctl start systemd-udevd
|
||||
- uses: snapcore/action-build@v1
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-2022
|
||||
@@ -336,6 +438,52 @@ jobs:
|
||||
shell: pwsh
|
||||
run: Get-Content -Path ".\build.log"
|
||||
|
||||
build-windows-cross:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ["namespace-profile-ghostty-md"]
|
||||
|
||||
target: [
|
||||
x86-windows-gnu,
|
||||
x86_64-windows-gnu,
|
||||
# We don't support cross-compiling to macOS or Linux because
|
||||
# we require system libraries.
|
||||
#aarch64-linux,
|
||||
#x86_64-linux,
|
||||
#aarch64-macos,
|
||||
#x86_64-macos,
|
||||
]
|
||||
runs-on: ${{ matrix.os }}
|
||||
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 }}"
|
||||
|
||||
# Cross-compile the binary. We always use static building for this
|
||||
# because its the only way to access the headers.
|
||||
- name: Test Build
|
||||
run: nix develop -c zig build -Dapp-runtime=glfw -Dtarget=${{ matrix.target }}
|
||||
|
||||
test:
|
||||
if: github.repository == 'ghostty-org/ghostty'
|
||||
runs-on: namespace-profile-ghostty-md
|
||||
@@ -366,7 +514,7 @@ jobs:
|
||||
run: nix develop -c zig build -Dapp-runtime=none test
|
||||
|
||||
- name: Test GTK Build
|
||||
run: nix develop -c zig build -Dapp-runtime=gtk -Dgtk-adwaita=true -Demit-docs
|
||||
run: nix develop -c zig build -Dapp-runtime=gtk -Demit-docs
|
||||
|
||||
- name: Test GLFW Build
|
||||
run: nix develop -c zig build -Dapp-runtime=glfw
|
||||
@@ -379,10 +527,9 @@ jobs:
|
||||
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 }}
|
||||
name: GTK x11=${{ matrix.x11 }} wayland=${{ matrix.wayland }}
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
needs: test
|
||||
env:
|
||||
@@ -413,7 +560,6 @@ jobs:
|
||||
nix develop -c \
|
||||
zig build \
|
||||
-Dapp-runtime=gtk \
|
||||
-Dgtk-adwaita=${{ matrix.adwaita }} \
|
||||
-Dgtk-x11=${{ matrix.x11 }} \
|
||||
-Dgtk-wayland=${{ matrix.wayland }}
|
||||
|
||||
@@ -471,12 +617,43 @@ 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 }}
|
||||
|
||||
zig-fmt:
|
||||
if: github.repository == 'ghostty-org/ghostty'
|
||||
runs-on: namespace-profile-ghostty-xsm
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||
steps:
|
||||
- uses: actions/checkout@v4 # Check out repo so we can lint it
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@v1.2.0
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- 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 }}"
|
||||
skipPush: true
|
||||
useDaemon: false # sometimes fails on short jobs
|
||||
- name: zig fmt
|
||||
run: nix develop -c zig fmt --check .
|
||||
|
||||
prettier:
|
||||
if: github.repository == 'ghostty-org/ghostty'
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
runs-on: namespace-profile-ghostty-xsm
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||
@@ -503,7 +680,7 @@ jobs:
|
||||
|
||||
alejandra:
|
||||
if: github.repository == 'ghostty-org/ghostty'
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
runs-on: namespace-profile-ghostty-xsm
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||
@@ -530,7 +707,7 @@ jobs:
|
||||
|
||||
typos:
|
||||
if: github.repository == 'ghostty-org/ghostty'
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
runs-on: namespace-profile-ghostty-xsm
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||
@@ -555,6 +732,74 @@ jobs:
|
||||
- name: typos check
|
||||
run: nix develop -c typos
|
||||
|
||||
translations:
|
||||
if: github.repository == 'ghostty-org/ghostty'
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||
steps:
|
||||
- uses: actions/checkout@v4 # Check out repo so we can lint it
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@v1.2.0
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- 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 }}"
|
||||
skipPush: true
|
||||
useDaemon: false # sometimes fails on short jobs
|
||||
- name: check translations
|
||||
run: |
|
||||
old_pot=$(mktemp)
|
||||
cp po/com.mitchellh.ghostty.pot "$old_pot"
|
||||
nix develop -c zig build update-translations
|
||||
|
||||
# Compare previous POT to current POT
|
||||
msgcmp "$old_pot" po/com.mitchellh.ghostty.pot --use-untranslated
|
||||
|
||||
# Compare all other POs to current POT
|
||||
for f in po/*.po; do
|
||||
# Ignore untranslated entries
|
||||
msgcmp --use-untranslated "$f" po/com.mitchellh.ghostty.pot;
|
||||
done
|
||||
|
||||
blueprint-compiler:
|
||||
if: github.repository == 'ghostty-org/ghostty'
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
ZIG_LOCAL_CACHE_DIR: /zig/local-cache
|
||||
ZIG_GLOBAL_CACHE_DIR: /zig/global-cache
|
||||
steps:
|
||||
- uses: actions/checkout@v4 # Check out repo so we can lint it
|
||||
- name: Setup Cache
|
||||
uses: namespacelabs/nscloud-cache-action@v1.2.0
|
||||
with:
|
||||
path: |
|
||||
/nix
|
||||
/zig
|
||||
- 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 }}"
|
||||
skipPush: true
|
||||
useDaemon: false # sometimes fails on short jobs
|
||||
- name: check blueprints
|
||||
run: nix develop -c ./nix/build-support/check-blueprints.sh
|
||||
- name: check unchanged
|
||||
run: git diff --exit-code
|
||||
|
||||
test-pkg-linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -589,3 +834,32 @@ jobs:
|
||||
- name: Test ${{ matrix.pkg }} Build
|
||||
run: |
|
||||
nix develop -c sh -c "cd pkg/${{ matrix.pkg }} ; zig build test"
|
||||
|
||||
test-debian-12:
|
||||
name: Test build on Debian 12
|
||||
runs-on: namespace-profile-ghostty-sm
|
||||
needs: [test, build-dist]
|
||||
steps:
|
||||
- name: Install and configure Namespace CLI
|
||||
uses: namespacelabs/nscloud-setup@v0
|
||||
|
||||
- name: Configure Namespace powered Buildx
|
||||
uses: namespacelabs/nscloud-setup-buildx-action@v0
|
||||
|
||||
- name: Download Source Tarball Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: source-tarball
|
||||
|
||||
- name: Extract tarball
|
||||
run: |
|
||||
mkdir dist
|
||||
tar --verbose --extract --strip-components 1 --directory dist --file ghostty-source.tar.gz
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: dist
|
||||
file: dist/src/build/docker/debian/Dockerfile
|
||||
build-args: |
|
||||
DISTRO_VERSION=12
|
||||
|
10
.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,9 @@ jobs:
|
||||
commit-message: "deps: Update iTerm2 color schemes"
|
||||
add-paths: |
|
||||
build.zig.zon
|
||||
nix/zigCacheHash.nix
|
||||
build.zig.zon.nix
|
||||
build.zig.zon.txt
|
||||
build.zig.zon.json
|
||||
body: |
|
||||
Upstream revision: https://github.com/mbadolato/iTerm2-Color-Schemes/tree/${{ steps.zig_fetch.outputs.upstream_rev }}
|
||||
labels: dependencies
|
||||
|
1
.gitignore
vendored
@@ -5,6 +5,7 @@
|
||||
.DS_Store
|
||||
.vscode/
|
||||
.direnv/
|
||||
.envrc.local
|
||||
.flatpak-builder/
|
||||
zig-cache/
|
||||
.zig-cache/
|
||||
|
168
CODEOWNERS
Normal file
@@ -0,0 +1,168 @@
|
||||
# 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/localization/* - Anything related to localization
|
||||
# for a specific locale.
|
||||
#
|
||||
# - @ghostty-org/macos - The Ghostty macOS app and any macOS-specific
|
||||
# features, configurations, etc.
|
||||
#
|
||||
# - @ghostty-org/packaging/snap - Ghostty snap packaging
|
||||
# (https://snapcraft.io/ghostty)
|
||||
#
|
||||
# - @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/input/KeyEncoder.zig @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
|
||||
|
||||
# Localization
|
||||
/po/README_TRANSLATORS.md @ghostty-org/localization
|
||||
/po/com.mitchellh.ghostty.pot @ghostty-org/localization
|
||||
/po/de_DE.UTF-8.po @ghostty-org/de_DE
|
||||
/po/nb_NO.UTF-8.po @ghostty-org/nb_NO
|
||||
/po/pl_PL.UTF-8.po @ghostty-org/pl_PL
|
||||
/po/uk_UA.UTF-8.po @ghostty-org/uk_UA
|
||||
/po/zh_CN.UTF-8.po @ghostty-org/zh_CN
|
||||
|
||||
# Packaging - Snap
|
||||
/snap/ @ghostty-org/snap
|
@@ -21,6 +21,15 @@ All issues are actionable. Pick one and start working on it. Thank you.
|
||||
If you need help or guidance, comment on the issue. Issues that are extra
|
||||
friendly to new contributors are tagged with "contributor friendly".
|
||||
|
||||
**I'd like to translate Ghostty to my language!**
|
||||
|
||||
We have written a [Translator's Guide](po/README_TRANSLATORS.md) for
|
||||
everyone interested in contributing translations to Ghostty.
|
||||
Translations usually do not need to go through the process of issue triage
|
||||
and you can submit pull requests directly, although please make sure that
|
||||
our [Style Guide](po/README_TRANSLATORS.md#style-guide) is followed before
|
||||
submission.
|
||||
|
||||
**I have a bug!**
|
||||
|
||||
1. Search the issue tracker and discussions for similar issues.
|
||||
@@ -78,6 +87,93 @@ pull request will be accepted with a high degree of certainty.
|
||||
> 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.
|
||||
|
||||
## Including and Updating Translations
|
||||
|
||||
See the [Contributor's Guide](po/README_CONTRIBUTORS.md) for more details.
|
||||
|
||||
## 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
|
||||
|
16
PACKAGING.md
@@ -23,13 +23,6 @@ https://release.files.ghostty.org/VERSION/ghostty-VERSION.tar.gz
|
||||
https://release.files.ghostty.org/VERSION/ghostty-VERSION.tar.gz.minisig
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> **Version 1.0.0 the filename is `ghostty-source.tar.gz`.** Future
|
||||
> versions will use the `ghostty-VERSION.tar.gz` format since it is more
|
||||
> typical for source tarballs. But for version 1.0.0, the filename is
|
||||
> `ghostty-source.tar.gz`.
|
||||
|
||||
Signature files are signed with
|
||||
[minisign](https://jedisct1.github.io/minisign/)
|
||||
using the following public key:
|
||||
@@ -55,7 +48,7 @@ To find the version of Zig required to build Ghostty, check the `required_zig`
|
||||
constant in `build.zig`. You don't need to know Zig to extract this information.
|
||||
This version will always be an official released version of Zig.
|
||||
|
||||
For example, at the time of writing this document, Ghostty requires Zig 0.13.0.
|
||||
For example, at the time of writing this document, Ghostty requires Zig 0.14.0.
|
||||
|
||||
## Building Ghostty
|
||||
|
||||
@@ -88,6 +81,13 @@ for system packages which separate a build and install step, since the
|
||||
install step can then be done with a `mv` or `cp` command (from `/tmp/ghostty`
|
||||
to wherever the package manager expects it).
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> **Version 1.1.1 and 1.1.2 are missing `fetch-zig-cache.sh`.** This was
|
||||
> an oversight on the release process. You can use the script from version
|
||||
> 1.1.0 to fetch the Zig cache for these versions. Future versions will
|
||||
> restore the script.
|
||||
|
||||
### Build Options
|
||||
|
||||
Ghostty uses the Zig build system. You can see all available build options by
|
||||
|
24
README.md
@@ -188,8 +188,11 @@ SENTRY_DSN=https://e914ee84fd895c4fe324afa3e53dac76@o4507352570920960.ingest.us.
|
||||
## Developing Ghostty
|
||||
|
||||
See the documentation on the Ghostty website for
|
||||
[building Ghostty from source](http://ghostty.org/docs/install/build).
|
||||
For development, omit the `-Doptimize` flag to build a debug build.
|
||||
[building Ghostty from a source tarball](http://ghostty.org/docs/install/build).
|
||||
Building Ghostty from a Git checkout is very similar, except you want to
|
||||
omit the `-Doptimize` flag to build a debug build, and you may require
|
||||
additional dependencies since the source tarball includes some processed
|
||||
files that are not in the Git repository.
|
||||
|
||||
On Linux or macOS, you can use `zig build -Dapp-runtime=glfw run` for a quick
|
||||
GLFW-based app for a faster development cycle while developing core
|
||||
@@ -206,6 +209,21 @@ Other useful commands:
|
||||
in the current running terminal emulator so if you want to check the
|
||||
behavior of this project, you must run this command in Ghostty.
|
||||
|
||||
### Extra Dependencies
|
||||
|
||||
Building Ghostty from a Git checkout on Linux requires some additional
|
||||
dependencies:
|
||||
|
||||
- `blueprint-compiler`
|
||||
|
||||
macOS users don't require any additional dependencies.
|
||||
|
||||
> [!NOTE]
|
||||
> This only applies to building from a _Git checkout_. This section does
|
||||
> not apply if you're building from a released _source tarball_. For
|
||||
> source tarballs, see the
|
||||
> [website](http://ghostty.org/docs/install/build).
|
||||
|
||||
### Linting
|
||||
|
||||
#### Prettier
|
||||
@@ -233,7 +251,7 @@ nix develop -c prettier --write .
|
||||
Nix modules are formatted with [Alejandra](https://github.com/kamadorueda/alejandra/). An Alejandra CI check
|
||||
will fail builds with improper formatting.
|
||||
|
||||
Nix users can use the following command to format with Alejanda:
|
||||
Nix users can use the following command to format with Alejandra:
|
||||
|
||||
```
|
||||
nix develop -c alejandra .
|
||||
|
54
build.zig
@@ -3,7 +3,7 @@ const builtin = @import("builtin");
|
||||
const buildpkg = @import("src/build/main.zig");
|
||||
|
||||
comptime {
|
||||
buildpkg.requireZig("0.13.0");
|
||||
buildpkg.requireZig("0.14.0");
|
||||
}
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
@@ -11,34 +11,42 @@ pub fn build(b: *std.Build) !void {
|
||||
|
||||
// Ghostty resources like terminfo, shell integration, themes, etc.
|
||||
const resources = try buildpkg.GhosttyResources.init(b, &config);
|
||||
const i18n = try buildpkg.GhosttyI18n.init(b, &config);
|
||||
|
||||
// Ghostty dependencies used by many artifacts.
|
||||
const deps = try buildpkg.SharedDeps.init(b, &config);
|
||||
const exe = try buildpkg.GhosttyExe.init(b, &config, &deps);
|
||||
if (config.emit_helpgen) deps.help_strings.install();
|
||||
|
||||
// Ghostty executable, the actual runnable Ghostty program.
|
||||
const exe = try buildpkg.GhosttyExe.init(b, &config, &deps);
|
||||
|
||||
// Ghostty docs
|
||||
if (config.emit_docs) {
|
||||
const docs = try buildpkg.GhosttyDocs.init(b, &deps);
|
||||
docs.install();
|
||||
}
|
||||
const docs = try buildpkg.GhosttyDocs.init(b, &deps);
|
||||
if (config.emit_docs) docs.install();
|
||||
|
||||
// Ghostty webdata
|
||||
if (config.emit_webdata) {
|
||||
const webdata = try buildpkg.GhosttyWebdata.init(b, &deps);
|
||||
webdata.install();
|
||||
}
|
||||
const webdata = try buildpkg.GhosttyWebdata.init(b, &deps);
|
||||
if (config.emit_webdata) webdata.install();
|
||||
|
||||
// Ghostty bench tools
|
||||
if (config.emit_bench) {
|
||||
const bench = try buildpkg.GhosttyBench.init(b, &deps);
|
||||
bench.install();
|
||||
const bench = try buildpkg.GhosttyBench.init(b, &deps);
|
||||
if (config.emit_bench) bench.install();
|
||||
|
||||
// Ghostty dist tarball
|
||||
const dist = try buildpkg.GhosttyDist.init(b, &config);
|
||||
{
|
||||
const step = b.step("dist", "Build the dist tarball");
|
||||
step.dependOn(dist.install_step);
|
||||
const check_step = b.step("distcheck", "Install and validate the dist tarball");
|
||||
check_step.dependOn(dist.check_step);
|
||||
check_step.dependOn(dist.install_step);
|
||||
}
|
||||
|
||||
// If we're not building libghostty, then install the exe and resources.
|
||||
if (config.app_runtime != .none) {
|
||||
exe.install();
|
||||
resources.install();
|
||||
i18n.install();
|
||||
}
|
||||
|
||||
// Libghostty
|
||||
@@ -48,7 +56,7 @@ pub fn build(b: *std.Build) !void {
|
||||
// As such, these build steps are lacking. For example, the Darwin
|
||||
// build only produces an xcframework.
|
||||
if (config.app_runtime == .none) {
|
||||
if (config.target.result.isDarwin()) darwin: {
|
||||
if (config.target.result.os.tag.isDarwin()) darwin: {
|
||||
if (!config.emit_xcframework) break :darwin;
|
||||
|
||||
// Build the xcframework
|
||||
@@ -58,6 +66,7 @@ pub fn build(b: *std.Build) !void {
|
||||
// The xcframework build always installs resources because our
|
||||
// macOS xcode project contains references to them.
|
||||
resources.install();
|
||||
i18n.install();
|
||||
|
||||
// If we aren't emitting docs we need to emit a placeholder so
|
||||
// our macOS xcodeproject builds.
|
||||
@@ -80,6 +89,16 @@ pub fn build(b: *std.Build) !void {
|
||||
{
|
||||
const run_cmd = b.addRunArtifact(exe.exe);
|
||||
if (b.args) |args| run_cmd.addArgs(args);
|
||||
|
||||
// Set the proper resources dir so things like shell integration
|
||||
// work correctly. If we're running `zig build run` in Ghostty,
|
||||
// this also ensures it overwrites the release one with our debug
|
||||
// build.
|
||||
run_cmd.setEnvironmentVariable(
|
||||
"GHOSTTY_RESOURCES_DIR",
|
||||
b.getInstallPath(.prefix, "share/ghostty"),
|
||||
);
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
}
|
||||
@@ -103,4 +122,11 @@ pub fn build(b: *std.Build) !void {
|
||||
test_step.dependOn(&test_run.step);
|
||||
}
|
||||
}
|
||||
|
||||
// update-translations does what it sounds like and updates the "pot"
|
||||
// files. These should be committed to the repo.
|
||||
{
|
||||
const step = b.step("update-translations", "Update translation files");
|
||||
step.dependOn(i18n.update_step);
|
||||
}
|
||||
}
|
||||
|
111
build.zig.zon
@@ -1,86 +1,111 @@
|
||||
.{
|
||||
.name = "ghostty",
|
||||
.version = "1.1.0",
|
||||
.name = .ghostty,
|
||||
.version = "1.1.3",
|
||||
.paths = .{""},
|
||||
.fingerprint = 0x64407a2a0b4147e5,
|
||||
.dependencies = .{
|
||||
// Zig libs
|
||||
|
||||
.libxev = .{
|
||||
.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",
|
||||
// mitchellh/libxev
|
||||
.url = "https://github.com/mitchellh/libxev/archive/3df9337a9e84450a58a2c4af434ec1a036f7b494.tar.gz",
|
||||
.hash = "libxev-0.0.0-86vtc-ziEgDbLP0vihUn1MhsxNKY4GJEga6BEr7oyHpz",
|
||||
.lazy = true,
|
||||
},
|
||||
.vaxis = .{
|
||||
.url = "git+https://github.com/rockorager/libvaxis/?ref=main#6d729a2dc3b934818dffe06d2ba3ce02841ed74b",
|
||||
.hash = "12200df4ebeaed45de26cb2c9f3b6f3746d8013b604e035dae658f86f586c8c91d2f",
|
||||
// rockorager/libvaxis
|
||||
.url = "git+https://github.com/rockorager/libvaxis#4182b7fa42f27cf14a71dbdb54cfd82c5c6e3447",
|
||||
.hash = "vaxis-0.1.0-BWNV_MHyCAA0rNbPTr50Z44PyEdNP9zQSnHcXBXoo3Ti",
|
||||
.lazy = true,
|
||||
},
|
||||
.z2d = .{
|
||||
.url = "git+https://github.com/vancluever/z2d?ref=v0.4.0#4638bb02a9dc41cc2fb811f092811f6a951c752a",
|
||||
.hash = "12201f0d542e7541cf492a001d4d0d0155c92f58212fbcb0d224e95edeba06b5416a",
|
||||
// vancluever/z2d
|
||||
.url = "https://github.com/vancluever/z2d/archive/1e89605a624940c310c7a1d81b46a7c5c05919e3.tar.gz",
|
||||
.hash = "z2d-0.6.0-j5P_HvLdCABu-dXpCeRM7Uk4m16vULg1980lMNCQj4_C",
|
||||
.lazy = true,
|
||||
},
|
||||
.zig_objc = .{
|
||||
.url = "https://github.com/mitchellh/zig-objc/archive/9b8ba849b0f58fe207ecd6ab7c147af55b17556e.tar.gz",
|
||||
.hash = "1220e17e64ef0ef561b3e4b9f3a96a2494285f2ec31c097721bf8c8677ec4415c634",
|
||||
// mitchellh/zig-objc
|
||||
.url = "https://github.com/mitchellh/zig-objc/archive/3ab0d37c7d6b933d6ded1b3a35b6b60f05590a98.tar.gz",
|
||||
.hash = "zig_objc-0.0.0-Ir_Sp3TyAADEVRTxXlScq3t_uKAM91MYNerZkHfbD0yt",
|
||||
.lazy = true,
|
||||
},
|
||||
.zig_js = .{
|
||||
.url = "https://github.com/mitchellh/zig-js/archive/d0b8b0a57c52fbc89f9d9fecba75ca29da7dd7d1.tar.gz",
|
||||
.hash = "12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc",
|
||||
// mitchellh/zig-js
|
||||
.url = "https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz",
|
||||
.hash = "N-V-__8AAB9YCQBaZtQjJZVndk-g_GDIK-NTZcIa63bFp9yZ",
|
||||
.lazy = true,
|
||||
},
|
||||
.ziglyph = .{
|
||||
.url = "https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz",
|
||||
.hash = "12207831bce7d4abce57b5a98e8f3635811cfefd160bca022eb91fe905d36a02cf25",
|
||||
.hash = "ziglyph-0.11.2-AAAAAHPtHwB4Mbzn1KvOV7Wpjo82NYEc_v0WC8oCLrkf",
|
||||
.lazy = true,
|
||||
},
|
||||
.zig_wayland = .{
|
||||
.url = "https://codeberg.org/ifreund/zig-wayland/archive/fbfe3b4ac0b472a27b1f1a67405436c58cbee12d.tar.gz",
|
||||
.hash = "12209ca054cb1919fa276e328967f10b253f7537c4136eb48f3332b0f7cf661cad38",
|
||||
// codeberg ifreund/zig-wayland
|
||||
.url = "https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz",
|
||||
.hash = "wayland-0.4.0-dev-lQa1kjfIAQCmhhQu3xF0KH-94-TzeMXOqfnP0-Dg6Wyy",
|
||||
},
|
||||
.zf = .{
|
||||
.url = "git+https://github.com/natecraddock/zf/?ref=main#ed99ca18b02dda052e20ba467e90b623c04690dd",
|
||||
.hash = "1220edc3b8d8bedbb50555947987e5e8e2f93871ca3c8e8d4cc8f1377c15b5dd35e8",
|
||||
// natecraddock/zf
|
||||
.url = "https://github.com/natecraddock/zf/archive/1039cf75447a8d5b8d481fedb914fe848d246276.tar.gz",
|
||||
.hash = "zf-0.10.3-OIRy8bKIAADhjqtdjVaDfONRuI7RVl5gMbhCoOwiBWV5",
|
||||
.lazy = true,
|
||||
},
|
||||
.gobject = .{
|
||||
// https://github.com/jcollie/ghostty-gobject based on zig_gobject
|
||||
// Temporary until we generate them at build time automatically.
|
||||
.url = "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.0-2025-03-18-21-1/ghostty-gobject-0.14.0-2025-03-18-21-1.tar.zst",
|
||||
.hash = "gobject-0.2.0-Skun7IWDlQAOKu4BV7LapIxL9Imbq1JRmzvcIkazvAxR",
|
||||
.lazy = true,
|
||||
},
|
||||
|
||||
// C libs
|
||||
.cimgui = .{ .path = "./pkg/cimgui" },
|
||||
.fontconfig = .{ .path = "./pkg/fontconfig" },
|
||||
.freetype = .{ .path = "./pkg/freetype" },
|
||||
.harfbuzz = .{ .path = "./pkg/harfbuzz" },
|
||||
.highway = .{ .path = "./pkg/highway" },
|
||||
.libpng = .{ .path = "./pkg/libpng" },
|
||||
.macos = .{ .path = "./pkg/macos" },
|
||||
.oniguruma = .{ .path = "./pkg/oniguruma" },
|
||||
.opengl = .{ .path = "./pkg/opengl" },
|
||||
.sentry = .{ .path = "./pkg/sentry" },
|
||||
.simdutf = .{ .path = "./pkg/simdutf" },
|
||||
.utfcpp = .{ .path = "./pkg/utfcpp" },
|
||||
.wuffs = .{ .path = "./pkg/wuffs" },
|
||||
.zlib = .{ .path = "./pkg/zlib" },
|
||||
.cimgui = .{ .path = "./pkg/cimgui", .lazy = true },
|
||||
.fontconfig = .{ .path = "./pkg/fontconfig", .lazy = true },
|
||||
.freetype = .{ .path = "./pkg/freetype", .lazy = true },
|
||||
.glfw = .{ .path = "./pkg/glfw", .lazy = true },
|
||||
.gtk4_layer_shell = .{ .path = "./pkg/gtk4-layer-shell", .lazy = true },
|
||||
.harfbuzz = .{ .path = "./pkg/harfbuzz", .lazy = true },
|
||||
.highway = .{ .path = "./pkg/highway", .lazy = true },
|
||||
.libintl = .{ .path = "./pkg/libintl", .lazy = true },
|
||||
.libpng = .{ .path = "./pkg/libpng", .lazy = true },
|
||||
.macos = .{ .path = "./pkg/macos", .lazy = true },
|
||||
.oniguruma = .{ .path = "./pkg/oniguruma", .lazy = true },
|
||||
.opengl = .{ .path = "./pkg/opengl", .lazy = true },
|
||||
.sentry = .{ .path = "./pkg/sentry", .lazy = true },
|
||||
.simdutf = .{ .path = "./pkg/simdutf", .lazy = true },
|
||||
.utfcpp = .{ .path = "./pkg/utfcpp", .lazy = true },
|
||||
.wuffs = .{ .path = "./pkg/wuffs", .lazy = true },
|
||||
.zlib = .{ .path = "./pkg/zlib", .lazy = true },
|
||||
|
||||
// Shader translation
|
||||
.glslang = .{ .path = "./pkg/glslang" },
|
||||
.spirv_cross = .{ .path = "./pkg/spirv-cross" },
|
||||
.glslang = .{ .path = "./pkg/glslang", .lazy = true },
|
||||
.spirv_cross = .{ .path = "./pkg/spirv-cross", .lazy = true },
|
||||
|
||||
// Wayland
|
||||
.wayland = .{
|
||||
.url = "https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz",
|
||||
.hash = "12202cdac858abc52413a6c6711d5026d2d3c8e13f95ca2c327eade0736298bb021f",
|
||||
.hash = "N-V-__8AAKrHGAAs2shYq8UkE6bGcR1QJtLTyOE_lcosMn6t",
|
||||
.lazy = true,
|
||||
},
|
||||
.wayland_protocols = .{
|
||||
.url = "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz",
|
||||
.hash = "12201a57c6ce0001aa034fa80fba3e1cd2253c560a45748f4f4dd21ff23b491cddef",
|
||||
.hash = "N-V-__8AAKw-DAAaV8bOAAGqA0-oD7o-HNIlPFYKRXSPT03S",
|
||||
.lazy = true,
|
||||
},
|
||||
.plasma_wayland_protocols = .{
|
||||
.url = "git+https://github.com/KDE/plasma-wayland-protocols?ref=main#db525e8f9da548cffa2ac77618dd0fbe7f511b86",
|
||||
.hash = "12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566",
|
||||
.url = "https://deps.files.ghostty.org/plasma_wayland_protocols-12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566.tar.gz",
|
||||
.hash = "N-V-__8AAKYZBAB-CFHBKs3u4JkeiT4BMvyHu3Y5aaWF3Bbs",
|
||||
.lazy = true,
|
||||
},
|
||||
|
||||
// Other
|
||||
.apple_sdk = .{ .path = "./pkg/apple-sdk" },
|
||||
.iterm2_themes = .{
|
||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/0e23daf59234fc892cba949562d7bf69204594bb.tar.gz",
|
||||
.hash = "12204fc99743d8232e691ac22e058519bfc6ea92de4a11c6dba59b117531c847cd6a",
|
||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8650079de477e80a5983646e3e4d24cda1dbaefa.tar.gz",
|
||||
.hash = "N-V-__8AADk6LwSAbK3OMyGiadf6aeyztHNV4-zKaLy6IZa6",
|
||||
.lazy = true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
172
build.zig.zon.json
generated
Normal file
@@ -0,0 +1,172 @@
|
||||
{
|
||||
"N-V-__8AALw2uwF_03u4JRkZwRLc3Y9hakkYV7NKRR9-RIZJ": {
|
||||
"name": "breakpad",
|
||||
"url": "https://deps.files.ghostty.org/breakpad-b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz",
|
||||
"hash": "sha256-bMqYlD0amQdmzvYQd8Ca/1k4Bj/heh7+EijlQSttatk="
|
||||
},
|
||||
"N-V-__8AAIrfdwARSa-zMmxWwFuwpXf1T3asIN7s5jqi9c1v": {
|
||||
"name": "fontconfig",
|
||||
"url": "https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz",
|
||||
"hash": "sha256-O6LdkhWHGKzsXKrxpxYEO1qgVcJ7CB2RSvPMtA3OilU="
|
||||
},
|
||||
"N-V-__8AAKLKpwC4H27Ps_0iL3bPkQb-z6ZVSrB-x_3EEkub": {
|
||||
"name": "freetype",
|
||||
"url": "https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz",
|
||||
"hash": "sha256-QnIB9dUVFnDQXB9bRb713aHy592XHvVPD+qqf/0quQw="
|
||||
},
|
||||
"N-V-__8AADcZkgn4cMhTUpIz6mShCKyqqB-NBtf_S2bHaTC-": {
|
||||
"name": "gettext",
|
||||
"url": "https://deps.files.ghostty.org/gettext-0.24.tar.gz",
|
||||
"hash": "sha256-yRhQPVk9cNr0hE0XWhPYFq+stmfAb7oeydzVACwVGLc="
|
||||
},
|
||||
"N-V-__8AAMrJSwAUGb9-vTzkNR-5LXS81MR__ZRVfF3tWgG6": {
|
||||
"name": "glfw",
|
||||
"url": "https://github.com/glfw/glfw/archive/e7ea71be039836da3a98cea55ae5569cb5eb885c.tar.gz",
|
||||
"hash": "sha256-M3N1XUAlMebBo5X1Py+9YxjKXgZ6eacqWRCbUmwLKQo="
|
||||
},
|
||||
"N-V-__8AABzkUgISeKGgXAzgtutgJsZc0-kkeqBBscJgMkvy": {
|
||||
"name": "glslang",
|
||||
"url": "https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz",
|
||||
"hash": "sha256-FKLtu1Ccs+UamlPj9eQ12/WXFgS0uDPmPmB26MCpl7U="
|
||||
},
|
||||
"gobject-0.2.0-Skun7IWDlQAOKu4BV7LapIxL9Imbq1JRmzvcIkazvAxR": {
|
||||
"name": "gobject",
|
||||
"url": "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.0-2025-03-18-21-1/ghostty-gobject-0.14.0-2025-03-18-21-1.tar.zst",
|
||||
"hash": "sha256-hWcpl0Wd3XydT+RY7+VIoxXPhCzele1Ip76YSh+KmLI="
|
||||
},
|
||||
"N-V-__8AALiNBAA-_0gprYr92CjrMj1I5bqNu0TSJOnjFNSr": {
|
||||
"name": "gtk4_layer_shell",
|
||||
"url": "https://deps.files.ghostty.org/gtk4-layer-shell-1.1.0.tar.gz",
|
||||
"hash": "sha256-mChCgSYKXu9bT2OlXxbEv2p4ihAgptsDfssPcfozaYg="
|
||||
},
|
||||
"N-V-__8AAG02ugUcWec-Ndp-i7JTsJ0dgF8nnJRUInkGLG7G": {
|
||||
"name": "harfbuzz",
|
||||
"url": "https://deps.files.ghostty.org/harfbuzz-11.0.0.tar.xz",
|
||||
"hash": "sha256-8WNRuv4hRyX+LB1bWfDZPkmQWkskeJn7kNcM/5U6K5s="
|
||||
},
|
||||
"N-V-__8AAGmZhABbsPJLfbqrh6JTHsXhY6qCaLAQyx25e0XE": {
|
||||
"name": "highway",
|
||||
"url": "https://deps.files.ghostty.org/highway-66486a10623fa0d72fe91260f96c892e41aceb06.tar.gz",
|
||||
"hash": "sha256-h9T4iT704I8iSXNgj/6/lCaKgTgLp5wS6IQZaMgKohI="
|
||||
},
|
||||
"N-V-__8AAH0GaQC8a52s6vfIxg88OZgFgEW6DFxfSK4lX_l3": {
|
||||
"name": "imgui",
|
||||
"url": "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz",
|
||||
"hash": "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA="
|
||||
},
|
||||
"N-V-__8AADk6LwSAbK3OMyGiadf6aeyztHNV4-zKaLy6IZa6": {
|
||||
"name": "iterm2_themes",
|
||||
"url": "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8650079de477e80a5983646e3e4d24cda1dbaefa.tar.gz",
|
||||
"hash": "sha256-nOkH31MQQd2PPdjVpRxBxNQWfR9Exg6nRF/KHgSz3cM="
|
||||
},
|
||||
"N-V-__8AAJrvXQCqAT8Mg9o_tk6m0yf5Fz-gCNEOKLyTSerD": {
|
||||
"name": "libpng",
|
||||
"url": "https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz",
|
||||
"hash": "sha256-/syVtGzwXo4/yKQUdQ4LparQDYnp/fF16U/wQcrxoDo="
|
||||
},
|
||||
"libxev-0.0.0-86vtc-ziEgDbLP0vihUn1MhsxNKY4GJEga6BEr7oyHpz": {
|
||||
"name": "libxev",
|
||||
"url": "https://github.com/mitchellh/libxev/archive/3df9337a9e84450a58a2c4af434ec1a036f7b494.tar.gz",
|
||||
"hash": "sha256-oKZqA9d79jHnp/HsqJWQE33Ffn5Ee5G4VnlQepQuY4o="
|
||||
},
|
||||
"N-V-__8AAG3RoQEyRC2Vw7Qoro5SYBf62IHn3HjqtNVY6aWK": {
|
||||
"name": "libxml2",
|
||||
"url": "https://deps.files.ghostty.org/libxml2-2.11.5.tar.gz",
|
||||
"hash": "sha256-bCgFni4+60K1tLFkieORamNGwQladP7jvGXNxdiaYhU="
|
||||
},
|
||||
"N-V-__8AAHjwMQDBXnLq3Q2QhaivE0kE2aD138vtX2Bq1g7c": {
|
||||
"name": "oniguruma",
|
||||
"url": "https://deps.files.ghostty.org/oniguruma-1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb.tar.gz",
|
||||
"hash": "sha256-ABqhIC54RI9MC/GkjHblVodrNvFtks4yB+zP1h2Z8qA="
|
||||
},
|
||||
"N-V-__8AADYiAAB_80AWnH1AxXC0tql9thT-R-DYO1gBqTLc": {
|
||||
"name": "pixels",
|
||||
"url": "https://deps.files.ghostty.org/pixels-12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806.tar.gz",
|
||||
"hash": "sha256-Veg7FtCRCCUCvxSb9FfzH0IJLFmCZQ4/+657SIcb8Ro="
|
||||
},
|
||||
"N-V-__8AAKYZBAB-CFHBKs3u4JkeiT4BMvyHu3Y5aaWF3Bbs": {
|
||||
"name": "plasma_wayland_protocols",
|
||||
"url": "https://deps.files.ghostty.org/plasma_wayland_protocols-12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566.tar.gz",
|
||||
"hash": "sha256-XFi6IUrNjmvKNCbcCLAixGqN2Zeymhs+KLrfccIN9EE="
|
||||
},
|
||||
"N-V-__8AAPlZGwBEa-gxrcypGBZ2R8Bse4JYSfo_ul8i2jlG": {
|
||||
"name": "sentry",
|
||||
"url": "https://deps.files.ghostty.org/sentry-1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e.tar.gz",
|
||||
"hash": "sha256-KsZJfMjWGo0xCT5HrduMmyxFsWsHBbszSoNbZCPDGN8="
|
||||
},
|
||||
"N-V-__8AANb6pwD7O1WG6L5nvD_rNMvnSc9Cpg1ijSlTYywv": {
|
||||
"name": "spirv_cross",
|
||||
"url": "https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz",
|
||||
"hash": "sha256-tStvz8Ref6abHwahNiwVVHNETizAmZVVaxVsU7pmV+M="
|
||||
},
|
||||
"N-V-__8AAHffAgDU0YQmynL8K35WzkcnMUmBVQHQ0jlcKpjH": {
|
||||
"name": "utfcpp",
|
||||
"url": "https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.tar.gz",
|
||||
"hash": "sha256-/8ZooxDndgfTk/PBizJxXyI9oerExNbgV5oR345rWc8="
|
||||
},
|
||||
"vaxis-0.1.0-BWNV_MHyCAA0rNbPTr50Z44PyEdNP9zQSnHcXBXoo3Ti": {
|
||||
"name": "vaxis",
|
||||
"url": "git+https://github.com/rockorager/libvaxis#4182b7fa42f27cf14a71dbdb54cfd82c5c6e3447",
|
||||
"hash": "sha256-iONEySjPeD0WYJ93fw5mxT+0pVfUO/m6008J/LXjQkA="
|
||||
},
|
||||
"N-V-__8AAKrHGAAs2shYq8UkE6bGcR1QJtLTyOE_lcosMn6t": {
|
||||
"name": "wayland",
|
||||
"url": "https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz",
|
||||
"hash": "sha256-6kGR1o5DdnflHzqs3ieCmBAUTpMdOXoyfcYDXiw5xQ0="
|
||||
},
|
||||
"N-V-__8AAKw-DAAaV8bOAAGqA0-oD7o-HNIlPFYKRXSPT03S": {
|
||||
"name": "wayland_protocols",
|
||||
"url": "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz",
|
||||
"hash": "sha256-XO3K3egbdeYPI+XoO13SuOtO+5+Peb16NH0UiusFMPg="
|
||||
},
|
||||
"N-V-__8AAAzZywE3s51XfsLbP9eyEw57ae9swYB9aGB6fCMs": {
|
||||
"name": "wuffs",
|
||||
"url": "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz",
|
||||
"hash": "sha256-nkzSCr6W5sTG7enDBXEIhgEm574uLD41UVR2wlC+HBM="
|
||||
},
|
||||
"z2d-0.6.0-j5P_HvLdCABu-dXpCeRM7Uk4m16vULg1980lMNCQj4_C": {
|
||||
"name": "z2d",
|
||||
"url": "https://github.com/vancluever/z2d/archive/1e89605a624940c310c7a1d81b46a7c5c05919e3.tar.gz",
|
||||
"hash": "sha256-PEKVSUZ6teRbDyhFPWSiuBSe40pgr0kVRivIY8Cn8HQ="
|
||||
},
|
||||
"zf-0.10.3-OIRy8bKIAADhjqtdjVaDfONRuI7RVl5gMbhCoOwiBWV5": {
|
||||
"name": "zf",
|
||||
"url": "https://github.com/natecraddock/zf/archive/1039cf75447a8d5b8d481fedb914fe848d246276.tar.gz",
|
||||
"hash": "sha256-xVva07TAYlVv4E4PKe2wUj86a6Ky2YC30YBgtbvNKvw="
|
||||
},
|
||||
"zg-0.13.4-AAAAAGiZ7QLz4pvECFa_wG4O4TP4FLABHHbemH2KakWM": {
|
||||
"name": "zg",
|
||||
"url": "git+https://codeberg.org/atman/zg#4a002763419a34d61dcbb1f415821b83b9bf8ddc",
|
||||
"hash": "sha256-fo3l6cjkrr/godElTGnQzalBsasN7J73IDIRmw7v1gA="
|
||||
},
|
||||
"N-V-__8AAB9YCQBaZtQjJZVndk-g_GDIK-NTZcIa63bFp9yZ": {
|
||||
"name": "zig_js",
|
||||
"url": "https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz",
|
||||
"hash": "sha256-fyNeCVbC9UAaKJY6JhAZlT0A479M/AKYMPIWEZbDWD0="
|
||||
},
|
||||
"zig_objc-0.0.0-Ir_Sp3TyAADEVRTxXlScq3t_uKAM91MYNerZkHfbD0yt": {
|
||||
"name": "zig_objc",
|
||||
"url": "https://github.com/mitchellh/zig-objc/archive/3ab0d37c7d6b933d6ded1b3a35b6b60f05590a98.tar.gz",
|
||||
"hash": "sha256-zn1tR6xhSmDla4UJ3t+Gni4Ni3R8deSK3tEe7DGzNXw="
|
||||
},
|
||||
"wayland-0.4.0-dev-lQa1kjfIAQCmhhQu3xF0KH-94-TzeMXOqfnP0-Dg6Wyy": {
|
||||
"name": "zig_wayland",
|
||||
"url": "https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz",
|
||||
"hash": "sha256-E77GZ15APYbbO1WzmuJi8eG9/iQFbc2CgkNBxjCLUhk="
|
||||
},
|
||||
"zigimg-0.1.0-lly-O4heEADSRxoTwJwrD3TBfUob9052sIgb9SL8Iz-A": {
|
||||
"name": "zigimg",
|
||||
"url": "git+https://github.com/TUSF/zigimg#0ce4eca3560d5553b13263d6b6bb72e146dd43d0",
|
||||
"hash": "sha256-Rr+mAfbLOoaxHOwCug+0cWCmW9gDhjhnaO2J/Oik9HI="
|
||||
},
|
||||
"ziglyph-0.11.2-AAAAAHPtHwB4Mbzn1KvOV7Wpjo82NYEc_v0WC8oCLrkf": {
|
||||
"name": "ziglyph",
|
||||
"url": "https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz",
|
||||
"hash": "sha256-cse98+Ft8QUjX+P88yyYfaxJOJGQ9M7Ymw7jFxDz89k="
|
||||
},
|
||||
"N-V-__8AAB0eQwD-0MdOEBmz7intriBReIsIDNlukNVoNu6o": {
|
||||
"name": "zlib",
|
||||
"url": "https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz",
|
||||
"hash": "sha256-F+iIY/NgBnKrSRgvIXKBtvxNPHYr3jYZNeQ2qVIU0Fw="
|
||||
}
|
||||
}
|
356
build.zig.zon.nix
generated
Normal file
@@ -0,0 +1,356 @@
|
||||
# generated by zon2nix (https://github.com/jcollie/zon2nix)
|
||||
{
|
||||
lib,
|
||||
linkFarm,
|
||||
fetchurl,
|
||||
fetchgit,
|
||||
runCommandLocal,
|
||||
zig_0_14,
|
||||
name ? "zig-packages",
|
||||
}: let
|
||||
unpackZigArtifact = {
|
||||
name,
|
||||
artifact,
|
||||
}:
|
||||
runCommandLocal name
|
||||
{
|
||||
nativeBuildInputs = [zig_0_14];
|
||||
}
|
||||
''
|
||||
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 = lib.splitString "#" url;
|
||||
url_base = builtins.elemAt parts 0;
|
||||
url_without_query = builtins.elemAt (lib.splitString "?" url_base) 0;
|
||||
rev_base = builtins.elemAt parts 1;
|
||||
rev =
|
||||
if builtins.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 = lib.splitString "://" url;
|
||||
proto = builtins.elemAt parts 0;
|
||||
path = builtins.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 = "N-V-__8AALw2uwF_03u4JRkZwRLc3Y9hakkYV7NKRR9-RIZJ";
|
||||
path = fetchZigArtifact {
|
||||
name = "breakpad";
|
||||
url = "https://deps.files.ghostty.org/breakpad-b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz";
|
||||
hash = "sha256-bMqYlD0amQdmzvYQd8Ca/1k4Bj/heh7+EijlQSttatk=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAIrfdwARSa-zMmxWwFuwpXf1T3asIN7s5jqi9c1v";
|
||||
path = fetchZigArtifact {
|
||||
name = "fontconfig";
|
||||
url = "https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz";
|
||||
hash = "sha256-O6LdkhWHGKzsXKrxpxYEO1qgVcJ7CB2RSvPMtA3OilU=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAKLKpwC4H27Ps_0iL3bPkQb-z6ZVSrB-x_3EEkub";
|
||||
path = fetchZigArtifact {
|
||||
name = "freetype";
|
||||
url = "https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz";
|
||||
hash = "sha256-QnIB9dUVFnDQXB9bRb713aHy592XHvVPD+qqf/0quQw=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AADcZkgn4cMhTUpIz6mShCKyqqB-NBtf_S2bHaTC-";
|
||||
path = fetchZigArtifact {
|
||||
name = "gettext";
|
||||
url = "https://deps.files.ghostty.org/gettext-0.24.tar.gz";
|
||||
hash = "sha256-yRhQPVk9cNr0hE0XWhPYFq+stmfAb7oeydzVACwVGLc=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAMrJSwAUGb9-vTzkNR-5LXS81MR__ZRVfF3tWgG6";
|
||||
path = fetchZigArtifact {
|
||||
name = "glfw";
|
||||
url = "https://github.com/glfw/glfw/archive/e7ea71be039836da3a98cea55ae5569cb5eb885c.tar.gz";
|
||||
hash = "sha256-M3N1XUAlMebBo5X1Py+9YxjKXgZ6eacqWRCbUmwLKQo=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AABzkUgISeKGgXAzgtutgJsZc0-kkeqBBscJgMkvy";
|
||||
path = fetchZigArtifact {
|
||||
name = "glslang";
|
||||
url = "https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz";
|
||||
hash = "sha256-FKLtu1Ccs+UamlPj9eQ12/WXFgS0uDPmPmB26MCpl7U=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "gobject-0.2.0-Skun7IWDlQAOKu4BV7LapIxL9Imbq1JRmzvcIkazvAxR";
|
||||
path = fetchZigArtifact {
|
||||
name = "gobject";
|
||||
url = "https://github.com/jcollie/ghostty-gobject/releases/download/0.14.0-2025-03-18-21-1/ghostty-gobject-0.14.0-2025-03-18-21-1.tar.zst";
|
||||
hash = "sha256-hWcpl0Wd3XydT+RY7+VIoxXPhCzele1Ip76YSh+KmLI=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AALiNBAA-_0gprYr92CjrMj1I5bqNu0TSJOnjFNSr";
|
||||
path = fetchZigArtifact {
|
||||
name = "gtk4_layer_shell";
|
||||
url = "https://deps.files.ghostty.org/gtk4-layer-shell-1.1.0.tar.gz";
|
||||
hash = "sha256-mChCgSYKXu9bT2OlXxbEv2p4ihAgptsDfssPcfozaYg=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAG02ugUcWec-Ndp-i7JTsJ0dgF8nnJRUInkGLG7G";
|
||||
path = fetchZigArtifact {
|
||||
name = "harfbuzz";
|
||||
url = "https://deps.files.ghostty.org/harfbuzz-11.0.0.tar.xz";
|
||||
hash = "sha256-8WNRuv4hRyX+LB1bWfDZPkmQWkskeJn7kNcM/5U6K5s=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAGmZhABbsPJLfbqrh6JTHsXhY6qCaLAQyx25e0XE";
|
||||
path = fetchZigArtifact {
|
||||
name = "highway";
|
||||
url = "https://deps.files.ghostty.org/highway-66486a10623fa0d72fe91260f96c892e41aceb06.tar.gz";
|
||||
hash = "sha256-h9T4iT704I8iSXNgj/6/lCaKgTgLp5wS6IQZaMgKohI=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAH0GaQC8a52s6vfIxg88OZgFgEW6DFxfSK4lX_l3";
|
||||
path = fetchZigArtifact {
|
||||
name = "imgui";
|
||||
url = "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz";
|
||||
hash = "sha256-oF/QHgTPEat4Hig4fGIdLkIPHmBEyOJ6JeYD6pnveGA=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AADk6LwSAbK3OMyGiadf6aeyztHNV4-zKaLy6IZa6";
|
||||
path = fetchZigArtifact {
|
||||
name = "iterm2_themes";
|
||||
url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8650079de477e80a5983646e3e4d24cda1dbaefa.tar.gz";
|
||||
hash = "sha256-nOkH31MQQd2PPdjVpRxBxNQWfR9Exg6nRF/KHgSz3cM=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAJrvXQCqAT8Mg9o_tk6m0yf5Fz-gCNEOKLyTSerD";
|
||||
path = fetchZigArtifact {
|
||||
name = "libpng";
|
||||
url = "https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz";
|
||||
hash = "sha256-/syVtGzwXo4/yKQUdQ4LparQDYnp/fF16U/wQcrxoDo=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "libxev-0.0.0-86vtc-ziEgDbLP0vihUn1MhsxNKY4GJEga6BEr7oyHpz";
|
||||
path = fetchZigArtifact {
|
||||
name = "libxev";
|
||||
url = "https://github.com/mitchellh/libxev/archive/3df9337a9e84450a58a2c4af434ec1a036f7b494.tar.gz";
|
||||
hash = "sha256-oKZqA9d79jHnp/HsqJWQE33Ffn5Ee5G4VnlQepQuY4o=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAG3RoQEyRC2Vw7Qoro5SYBf62IHn3HjqtNVY6aWK";
|
||||
path = fetchZigArtifact {
|
||||
name = "libxml2";
|
||||
url = "https://deps.files.ghostty.org/libxml2-2.11.5.tar.gz";
|
||||
hash = "sha256-bCgFni4+60K1tLFkieORamNGwQladP7jvGXNxdiaYhU=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAHjwMQDBXnLq3Q2QhaivE0kE2aD138vtX2Bq1g7c";
|
||||
path = fetchZigArtifact {
|
||||
name = "oniguruma";
|
||||
url = "https://deps.files.ghostty.org/oniguruma-1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb.tar.gz";
|
||||
hash = "sha256-ABqhIC54RI9MC/GkjHblVodrNvFtks4yB+zP1h2Z8qA=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AADYiAAB_80AWnH1AxXC0tql9thT-R-DYO1gBqTLc";
|
||||
path = fetchZigArtifact {
|
||||
name = "pixels";
|
||||
url = "https://deps.files.ghostty.org/pixels-12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806.tar.gz";
|
||||
hash = "sha256-Veg7FtCRCCUCvxSb9FfzH0IJLFmCZQ4/+657SIcb8Ro=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAKYZBAB-CFHBKs3u4JkeiT4BMvyHu3Y5aaWF3Bbs";
|
||||
path = fetchZigArtifact {
|
||||
name = "plasma_wayland_protocols";
|
||||
url = "https://deps.files.ghostty.org/plasma_wayland_protocols-12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566.tar.gz";
|
||||
hash = "sha256-XFi6IUrNjmvKNCbcCLAixGqN2Zeymhs+KLrfccIN9EE=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAPlZGwBEa-gxrcypGBZ2R8Bse4JYSfo_ul8i2jlG";
|
||||
path = fetchZigArtifact {
|
||||
name = "sentry";
|
||||
url = "https://deps.files.ghostty.org/sentry-1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e.tar.gz";
|
||||
hash = "sha256-KsZJfMjWGo0xCT5HrduMmyxFsWsHBbszSoNbZCPDGN8=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AANb6pwD7O1WG6L5nvD_rNMvnSc9Cpg1ijSlTYywv";
|
||||
path = fetchZigArtifact {
|
||||
name = "spirv_cross";
|
||||
url = "https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz";
|
||||
hash = "sha256-tStvz8Ref6abHwahNiwVVHNETizAmZVVaxVsU7pmV+M=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAHffAgDU0YQmynL8K35WzkcnMUmBVQHQ0jlcKpjH";
|
||||
path = fetchZigArtifact {
|
||||
name = "utfcpp";
|
||||
url = "https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.tar.gz";
|
||||
hash = "sha256-/8ZooxDndgfTk/PBizJxXyI9oerExNbgV5oR345rWc8=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "vaxis-0.1.0-BWNV_MHyCAA0rNbPTr50Z44PyEdNP9zQSnHcXBXoo3Ti";
|
||||
path = fetchZigArtifact {
|
||||
name = "vaxis";
|
||||
url = "git+https://github.com/rockorager/libvaxis#4182b7fa42f27cf14a71dbdb54cfd82c5c6e3447";
|
||||
hash = "sha256-iONEySjPeD0WYJ93fw5mxT+0pVfUO/m6008J/LXjQkA=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAKrHGAAs2shYq8UkE6bGcR1QJtLTyOE_lcosMn6t";
|
||||
path = fetchZigArtifact {
|
||||
name = "wayland";
|
||||
url = "https://deps.files.ghostty.org/wayland-9cb3d7aa9dc995ffafdbdef7ab86a949d0fb0e7d.tar.gz";
|
||||
hash = "sha256-6kGR1o5DdnflHzqs3ieCmBAUTpMdOXoyfcYDXiw5xQ0=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAKw-DAAaV8bOAAGqA0-oD7o-HNIlPFYKRXSPT03S";
|
||||
path = fetchZigArtifact {
|
||||
name = "wayland_protocols";
|
||||
url = "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz";
|
||||
hash = "sha256-XO3K3egbdeYPI+XoO13SuOtO+5+Peb16NH0UiusFMPg=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAAzZywE3s51XfsLbP9eyEw57ae9swYB9aGB6fCMs";
|
||||
path = fetchZigArtifact {
|
||||
name = "wuffs";
|
||||
url = "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz";
|
||||
hash = "sha256-nkzSCr6W5sTG7enDBXEIhgEm574uLD41UVR2wlC+HBM=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "z2d-0.6.0-j5P_HvLdCABu-dXpCeRM7Uk4m16vULg1980lMNCQj4_C";
|
||||
path = fetchZigArtifact {
|
||||
name = "z2d";
|
||||
url = "https://github.com/vancluever/z2d/archive/1e89605a624940c310c7a1d81b46a7c5c05919e3.tar.gz";
|
||||
hash = "sha256-PEKVSUZ6teRbDyhFPWSiuBSe40pgr0kVRivIY8Cn8HQ=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "zf-0.10.3-OIRy8bKIAADhjqtdjVaDfONRuI7RVl5gMbhCoOwiBWV5";
|
||||
path = fetchZigArtifact {
|
||||
name = "zf";
|
||||
url = "https://github.com/natecraddock/zf/archive/1039cf75447a8d5b8d481fedb914fe848d246276.tar.gz";
|
||||
hash = "sha256-xVva07TAYlVv4E4PKe2wUj86a6Ky2YC30YBgtbvNKvw=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "zg-0.13.4-AAAAAGiZ7QLz4pvECFa_wG4O4TP4FLABHHbemH2KakWM";
|
||||
path = fetchZigArtifact {
|
||||
name = "zg";
|
||||
url = "git+https://codeberg.org/atman/zg#4a002763419a34d61dcbb1f415821b83b9bf8ddc";
|
||||
hash = "sha256-fo3l6cjkrr/godElTGnQzalBsasN7J73IDIRmw7v1gA=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAB9YCQBaZtQjJZVndk-g_GDIK-NTZcIa63bFp9yZ";
|
||||
path = fetchZigArtifact {
|
||||
name = "zig_js";
|
||||
url = "https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz";
|
||||
hash = "sha256-fyNeCVbC9UAaKJY6JhAZlT0A479M/AKYMPIWEZbDWD0=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "zig_objc-0.0.0-Ir_Sp3TyAADEVRTxXlScq3t_uKAM91MYNerZkHfbD0yt";
|
||||
path = fetchZigArtifact {
|
||||
name = "zig_objc";
|
||||
url = "https://github.com/mitchellh/zig-objc/archive/3ab0d37c7d6b933d6ded1b3a35b6b60f05590a98.tar.gz";
|
||||
hash = "sha256-zn1tR6xhSmDla4UJ3t+Gni4Ni3R8deSK3tEe7DGzNXw=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "wayland-0.4.0-dev-lQa1kjfIAQCmhhQu3xF0KH-94-TzeMXOqfnP0-Dg6Wyy";
|
||||
path = fetchZigArtifact {
|
||||
name = "zig_wayland";
|
||||
url = "https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz";
|
||||
hash = "sha256-E77GZ15APYbbO1WzmuJi8eG9/iQFbc2CgkNBxjCLUhk=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "zigimg-0.1.0-lly-O4heEADSRxoTwJwrD3TBfUob9052sIgb9SL8Iz-A";
|
||||
path = fetchZigArtifact {
|
||||
name = "zigimg";
|
||||
url = "git+https://github.com/TUSF/zigimg#0ce4eca3560d5553b13263d6b6bb72e146dd43d0";
|
||||
hash = "sha256-Rr+mAfbLOoaxHOwCug+0cWCmW9gDhjhnaO2J/Oik9HI=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "ziglyph-0.11.2-AAAAAHPtHwB4Mbzn1KvOV7Wpjo82NYEc_v0WC8oCLrkf";
|
||||
path = fetchZigArtifact {
|
||||
name = "ziglyph";
|
||||
url = "https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz";
|
||||
hash = "sha256-cse98+Ft8QUjX+P88yyYfaxJOJGQ9M7Ymw7jFxDz89k=";
|
||||
};
|
||||
}
|
||||
{
|
||||
name = "N-V-__8AAB0eQwD-0MdOEBmz7intriBReIsIDNlukNVoNu6o";
|
||||
path = fetchZigArtifact {
|
||||
name = "zlib";
|
||||
url = "https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz";
|
||||
hash = "sha256-F+iIY/NgBnKrSRgvIXKBtvxNPHYr3jYZNeQ2qVIU0Fw=";
|
||||
};
|
||||
}
|
||||
]
|
34
build.zig.zon.txt
generated
Normal file
@@ -0,0 +1,34 @@
|
||||
git+https://codeberg.org/atman/zg#4a002763419a34d61dcbb1f415821b83b9bf8ddc
|
||||
git+https://github.com/TUSF/zigimg#0ce4eca3560d5553b13263d6b6bb72e146dd43d0
|
||||
git+https://github.com/rockorager/libvaxis#4182b7fa42f27cf14a71dbdb54cfd82c5c6e3447
|
||||
https://codeberg.org/ifreund/zig-wayland/archive/f3c5d503e540ada8cbcb056420de240af0c094f7.tar.gz
|
||||
https://deps.files.ghostty.org/breakpad-b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz
|
||||
https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz
|
||||
https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz
|
||||
https://deps.files.ghostty.org/gettext-0.24.tar.gz
|
||||
https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz
|
||||
https://deps.files.ghostty.org/gtk4-layer-shell-1.1.0.tar.gz
|
||||
https://deps.files.ghostty.org/harfbuzz-11.0.0.tar.xz
|
||||
https://deps.files.ghostty.org/highway-66486a10623fa0d72fe91260f96c892e41aceb06.tar.gz
|
||||
https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz
|
||||
https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz
|
||||
https://deps.files.ghostty.org/libxml2-2.11.5.tar.gz
|
||||
https://deps.files.ghostty.org/oniguruma-1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb.tar.gz
|
||||
https://deps.files.ghostty.org/pixels-12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806.tar.gz
|
||||
https://deps.files.ghostty.org/plasma_wayland_protocols-12207e0851c12acdeee0991e893e0132fc87bb763969a585dc16ecca33e88334c566.tar.gz
|
||||
https://deps.files.ghostty.org/sentry-1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e.tar.gz
|
||||
https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz
|
||||
https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.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/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz
|
||||
https://deps.files.ghostty.org/zig_js-12205a66d423259567764fa0fc60c82be35365c21aeb76c5a7dc99698401f4f6fefc.tar.gz
|
||||
https://deps.files.ghostty.org/ziglyph-b89d43d1e3fb01b6074bc1f7fc980324b04d26a5.tar.gz
|
||||
https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz
|
||||
https://github.com/glfw/glfw/archive/e7ea71be039836da3a98cea55ae5569cb5eb885c.tar.gz
|
||||
https://github.com/jcollie/ghostty-gobject/releases/download/0.14.0-2025-03-18-21-1/ghostty-gobject-0.14.0-2025-03-18-21-1.tar.zst
|
||||
https://github.com/mbadolato/iTerm2-Color-Schemes/archive/8650079de477e80a5983646e3e4d24cda1dbaefa.tar.gz
|
||||
https://github.com/mitchellh/libxev/archive/3df9337a9e84450a58a2c4af434ec1a036f7b494.tar.gz
|
||||
https://github.com/mitchellh/zig-objc/archive/3ab0d37c7d6b933d6ded1b3a35b6b60f05590a98.tar.gz
|
||||
https://github.com/natecraddock/zf/archive/1039cf75447a8d5b8d481fedb914fe848d246276.tar.gz
|
||||
https://github.com/vancluever/z2d/archive/1e89605a624940c310c7a1d81b46a7c5c05919e3.tar.gz
|
62
flake.lock
generated
@@ -3,11 +3,11 @@
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1696426674,
|
||||
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
|
||||
"lastModified": 1733328505,
|
||||
"narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
|
||||
"rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -21,11 +21,11 @@
|
||||
"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": {
|
||||
@@ -36,11 +36,11 @@
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1733423277,
|
||||
"narHash": "sha256-TxabjxEgkNbCGFRHgM/b9yZWlBj60gUOUnRT/wbVQR8=",
|
||||
"lastModified": 1741992157,
|
||||
"narHash": "sha256-nlIfTsTrMSksEJc1f7YexXiPVuzD1gOfeN1ggwZyUoc=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e36963a147267afc055f7cf65225958633e536bf",
|
||||
"rev": "da4b122f63095ca1199bd4d526f9e26426697689",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -52,11 +52,11 @@
|
||||
},
|
||||
"nixpkgs-unstable": {
|
||||
"locked": {
|
||||
"lastModified": 1733229606,
|
||||
"narHash": "sha256-FLYY5M0rpa5C2QAE3CKLYAM6TwbKicdRK6qNrSHlNrE=",
|
||||
"lastModified": 1741865919,
|
||||
"narHash": "sha256-4thdbnP6dlbdq+qZWTsm4ffAwoS8Tiq1YResB+RP6WE=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "566e53c2ad750c84f6d31f9ccb9d00f823165550",
|
||||
"rev": "573c650e8a14b2faa0041645ab18aed7e60f0c9a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -69,9 +69,11 @@
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs-stable": "nixpkgs-stable",
|
||||
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||
"zig": "zig"
|
||||
"zig": "zig",
|
||||
"zon2nix": "zon2nix"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
@@ -92,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": 1741825901,
|
||||
"narHash": "sha256-aeopo+aXg5I2IksOPFN79usw7AeimH1+tjfuMzJHFdk=",
|
||||
"owner": "mitchellh",
|
||||
"repo": "zig-overlay",
|
||||
"rev": "02fc5cc555fc14fda40c42d7c3250efa43812b43",
|
||||
"rev": "0b14285e283f5a747f372fb2931835dd937c4383",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -110,6 +114,30 @@
|
||||
"repo": "zig-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"zon2nix": {
|
||||
"inputs": {
|
||||
"flake-utils": [
|
||||
"flake-utils"
|
||||
],
|
||||
"nixpkgs": [
|
||||
"nixpkgs-unstable"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1742104771,
|
||||
"narHash": "sha256-LhidlyEA9MP8jGe1rEnyjGFCzLLgCdDpYeWggibayr0=",
|
||||
"owner": "jcollie",
|
||||
"repo": "zon2nix",
|
||||
"rev": "56c159be489cc6c0e73c3930bd908ddc6fe89613",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "jcollie",
|
||||
"ref": "56c159be489cc6c0e73c3930bd908ddc6fe89613",
|
||||
"repo": "zon2nix",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
29
flake.nix
@@ -8,6 +8,7 @@
|
||||
# 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 = {
|
||||
@@ -19,9 +20,18 @@
|
||||
url = "github:mitchellh/zig-overlay";
|
||||
inputs = {
|
||||
nixpkgs.follows = "nixpkgs-stable";
|
||||
flake-utils.follows = "flake-utils";
|
||||
flake-compat.follows = "";
|
||||
};
|
||||
};
|
||||
|
||||
zon2nix = {
|
||||
url = "github:jcollie/zon2nix?ref=56c159be489cc6c0e73c3930bd908ddc6fe89613";
|
||||
inputs = {
|
||||
nixpkgs.follows = "nixpkgs-unstable";
|
||||
flake-utils.follows = "flake-utils";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
outputs = {
|
||||
@@ -29,6 +39,7 @@
|
||||
nixpkgs-unstable,
|
||||
nixpkgs-stable,
|
||||
zig,
|
||||
zon2nix,
|
||||
...
|
||||
}:
|
||||
builtins.foldl' nixpkgs-stable.lib.recursiveUpdate {} (
|
||||
@@ -38,8 +49,11 @@
|
||||
pkgs-unstable = nixpkgs-unstable.legacyPackages.${system};
|
||||
in {
|
||||
devShell.${system} = pkgs-stable.callPackage ./nix/devShell.nix {
|
||||
zig = zig.packages.${system}."0.13.0";
|
||||
zig = zig.packages.${system}."0.14.0";
|
||||
wraptest = pkgs-stable.callPackage ./nix/wraptest.nix {};
|
||||
# remove once blueprint-compiler 0.16.0 is in the stable nixpkgs
|
||||
blueprint-compiler = pkgs-unstable.blueprint-compiler;
|
||||
zon2nix = zon2nix;
|
||||
};
|
||||
|
||||
packages.${system} = let
|
||||
@@ -49,9 +63,10 @@
|
||||
revision = self.shortRev or self.dirtyShortRev or "dirty";
|
||||
};
|
||||
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");
|
||||
deps = pkgs-unstable.callPackage ./build.zig.zon.nix {};
|
||||
ghostty-debug = pkgs-unstable.callPackage ./nix/package.nix (mkArgs "Debug");
|
||||
ghostty-releasesafe = pkgs-unstable.callPackage ./nix/package.nix (mkArgs "ReleaseSafe");
|
||||
ghostty-releasefast = pkgs-unstable.callPackage ./nix/package.nix (mkArgs "ReleaseFast");
|
||||
|
||||
ghostty = ghostty-releasefast;
|
||||
default = ghostty;
|
||||
@@ -64,14 +79,14 @@
|
||||
module: let
|
||||
vm = import ./nix/vm/create.nix {
|
||||
inherit system module;
|
||||
nixpkgs = nixpkgs-stable;
|
||||
nixpkgs = nixpkgs-unstable;
|
||||
overlay = self.overlays.debug;
|
||||
};
|
||||
program = pkgs-stable.writeShellScript "run-ghostty-vm" ''
|
||||
program = pkgs-unstable.writeShellScript "run-ghostty-vm" ''
|
||||
SHARED_DIR=$(pwd)
|
||||
export SHARED_DIR
|
||||
|
||||
${pkgs-stable.lib.getExe vm.config.system.build.vm} "$@"
|
||||
${pkgs-unstable.lib.getExe vm.config.system.build.vm} "$@"
|
||||
'';
|
||||
in {
|
||||
type = "app";
|
||||
|
@@ -412,6 +412,7 @@ typedef enum {
|
||||
GHOSTTY_FULLSCREEN_NATIVE,
|
||||
GHOSTTY_FULLSCREEN_NON_NATIVE,
|
||||
GHOSTTY_FULLSCREEN_NON_NATIVE_VISIBLE_MENU,
|
||||
GHOSTTY_FULLSCREEN_NON_NATIVE_PADDED_NOTCH,
|
||||
} ghostty_action_fullscreen_e;
|
||||
|
||||
// apprt.action.SecureInput
|
||||
@@ -579,12 +580,14 @@ typedef enum {
|
||||
GHOSTTY_ACTION_TOGGLE_SPLIT_ZOOM,
|
||||
GHOSTTY_ACTION_PRESENT_TERMINAL,
|
||||
GHOSTTY_ACTION_SIZE_LIMIT,
|
||||
GHOSTTY_ACTION_RESET_WINDOW_SIZE,
|
||||
GHOSTTY_ACTION_INITIAL_SIZE,
|
||||
GHOSTTY_ACTION_CELL_SIZE,
|
||||
GHOSTTY_ACTION_INSPECTOR,
|
||||
GHOSTTY_ACTION_RENDER_INSPECTOR,
|
||||
GHOSTTY_ACTION_DESKTOP_NOTIFICATION,
|
||||
GHOSTTY_ACTION_SET_TITLE,
|
||||
GHOSTTY_ACTION_PROMPT_TITLE,
|
||||
GHOSTTY_ACTION_PWD,
|
||||
GHOSTTY_ACTION_MOUSE_SHAPE,
|
||||
GHOSTTY_ACTION_MOUSE_VISIBILITY,
|
||||
@@ -597,6 +600,7 @@ typedef enum {
|
||||
GHOSTTY_ACTION_COLOR_CHANGE,
|
||||
GHOSTTY_ACTION_RELOAD_CONFIG,
|
||||
GHOSTTY_ACTION_CONFIG_CHANGE,
|
||||
GHOSTTY_ACTION_CLOSE_WINDOW,
|
||||
} ghostty_action_tag_e;
|
||||
|
||||
typedef union {
|
||||
@@ -644,7 +648,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);
|
||||
|
||||
@@ -665,6 +669,7 @@ typedef struct {
|
||||
int ghostty_init(void);
|
||||
void ghostty_cli_main(uintptr_t, char**);
|
||||
ghostty_info_s ghostty_info(void);
|
||||
const char* ghostty_translate(const char*);
|
||||
|
||||
ghostty_config_t ghostty_config_new();
|
||||
void ghostty_config_free(ghostty_config_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 |
@@ -40,6 +40,7 @@
|
||||
A53D0C952B53B4D800305CE6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A5B30538299BEAAB0047F10C /* Assets.xcassets */; };
|
||||
A53D0C9B2B543F3B00305CE6 /* Ghostty.App.swift in Sources */ = {isa = PBXBuildFile; fileRef = A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */; };
|
||||
A53D0C9C2B543F7B00305CE6 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = A55B7BB729B6F53A0055DE60 /* Package.swift */; };
|
||||
A546F1142D7B68D7003B11A0 /* locale in Resources */ = {isa = PBXBuildFile; fileRef = A546F1132D7B68D7003B11A0 /* locale */; };
|
||||
A54B0CE92D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A54B0CE82D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift */; };
|
||||
A54B0CEB2D0CFB4C00CBEFF8 /* NSImage+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A54B0CEA2D0CFB4A00CBEFF8 /* NSImage+Extension.swift */; };
|
||||
A54B0CED2D0CFB7700CBEFF8 /* ColorizedGhosttyIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = A54B0CEC2D0CFB7300CBEFF8 /* ColorizedGhosttyIcon.swift */; };
|
||||
@@ -72,6 +73,7 @@
|
||||
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 */; };
|
||||
@@ -137,6 +139,7 @@
|
||||
A53A6C022CCC1B7D00943E98 /* Ghostty.Action.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.Action.swift; sourceTree = "<group>"; };
|
||||
A53D0C932B53B43700305CE6 /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = "<group>"; };
|
||||
A53D0C992B543F3B00305CE6 /* Ghostty.App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Ghostty.App.swift; sourceTree = "<group>"; };
|
||||
A546F1132D7B68D7003B11A0 /* locale */ = {isa = PBXFileReference; lastKnownFileType = folder; name = locale; path = "../zig-out/share/locale"; sourceTree = "<group>"; };
|
||||
A54B0CE82D0CECD100CBEFF8 /* ColorizedGhosttyIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorizedGhosttyIconView.swift; sourceTree = "<group>"; };
|
||||
A54B0CEA2D0CFB4A00CBEFF8 /* NSImage+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSImage+Extension.swift"; sourceTree = "<group>"; };
|
||||
A54B0CEC2D0CFB7300CBEFF8 /* ColorizedGhosttyIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorizedGhosttyIcon.swift; sourceTree = "<group>"; };
|
||||
@@ -168,6 +171,7 @@
|
||||
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>"; };
|
||||
@@ -270,6 +274,7 @@
|
||||
A534263D2A7DCBB000EBB7A2 /* Helpers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A5AEB1642D5BE7BF00513529 /* LastWindowPosition.swift */,
|
||||
A5A6F7292CC41B8700B232A5 /* Xcode.swift */,
|
||||
A5CEAFFE29C2410700646FDA /* Backport.swift */,
|
||||
A5333E1B2B5A1CE3008AEFF7 /* CrossKit.swift */,
|
||||
@@ -421,6 +426,7 @@
|
||||
29C15B1C2CDC3B2000520DD4 /* bat */,
|
||||
A586167B2B7703CC009BDB1D /* fish */,
|
||||
55154BDF2B33911F001622DC /* ghostty */,
|
||||
A546F1132D7B68D7003B11A0 /* locale */,
|
||||
A5985CE52C33060F00C57AD3 /* man */,
|
||||
9351BE8E2D22937F003B3499 /* nvim */,
|
||||
A5A1F8842A489D6800D1E8BC /* terminfo */,
|
||||
@@ -590,20 +596,21 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
FC9ABA9C2D0F53F80020D4C8 /* bash-completion in Resources */,
|
||||
29C15B1D2CDC3B2900520DD4 /* bat in Resources */,
|
||||
A586167C2B7703CC009BDB1D /* fish in Resources */,
|
||||
55154BE02B33911F001622DC /* ghostty in Resources */,
|
||||
A546F1142D7B68D7003B11A0 /* locale in Resources */,
|
||||
A5985CE62C33060F00C57AD3 /* man in Resources */,
|
||||
9351BE8E3D22937F003B3499 /* nvim in Resources */,
|
||||
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */,
|
||||
552964E62B34A9B400030505 /* vim in Resources */,
|
||||
FC5218FA2D10FFCE004C93E0 /* zsh in Resources */,
|
||||
A5B30539299BEAAB0047F10C /* Assets.xcassets in Resources */,
|
||||
A51BFC1E2B2FB5CE00E92F16 /* About.xib in Resources */,
|
||||
A5E112932AF73E6E00C6E0C2 /* ClipboardConfirmation.xib in Resources */,
|
||||
A5CDF1912AAF9A5800513312 /* ConfigurationErrors.xib in Resources */,
|
||||
857F63812A5E64F200CA4815 /* MainMenu.xib in Resources */,
|
||||
29C15B1D2CDC3B2900520DD4 /* bat in Resources */,
|
||||
A596309A2AEE1C6400D64628 /* Terminal.xib in Resources */,
|
||||
A586167C2B7703CC009BDB1D /* fish in Resources */,
|
||||
FC5218FA2D10FFCE004C93E0 /* zsh in Resources */,
|
||||
55154BE02B33911F001622DC /* ghostty in Resources */,
|
||||
A5985CE62C33060F00C57AD3 /* man in Resources */,
|
||||
A5A1F8852A489D6800D1E8BC /* terminfo in Resources */,
|
||||
552964E62B34A9B400030505 /* vim in Resources */,
|
||||
9351BE8E3D22937F003B3499 /* nvim in Resources */,
|
||||
A5CBD05C2CA0C5C70017A1AE /* QuickTerminal.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -623,6 +630,7 @@
|
||||
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 */,
|
||||
|
@@ -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"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@@ -28,7 +28,9 @@ class AppDelegate: NSObject,
|
||||
@IBOutlet private var menuNewWindow: NSMenuItem?
|
||||
@IBOutlet private var menuNewTab: NSMenuItem?
|
||||
@IBOutlet private var menuSplitRight: NSMenuItem?
|
||||
@IBOutlet private var menuSplitLeft: NSMenuItem?
|
||||
@IBOutlet private var menuSplitDown: NSMenuItem?
|
||||
@IBOutlet private var menuSplitUp: NSMenuItem?
|
||||
@IBOutlet private var menuClose: NSMenuItem?
|
||||
@IBOutlet private var menuCloseTab: NSMenuItem?
|
||||
@IBOutlet private var menuCloseWindow: NSMenuItem?
|
||||
@@ -41,6 +43,7 @@ class AppDelegate: NSObject,
|
||||
|
||||
@IBOutlet private var menuToggleVisibility: NSMenuItem?
|
||||
@IBOutlet private var menuToggleFullScreen: NSMenuItem?
|
||||
@IBOutlet private var menuBringAllToFront: NSMenuItem?
|
||||
@IBOutlet private var menuZoomSplit: NSMenuItem?
|
||||
@IBOutlet private var menuPreviousSplit: NSMenuItem?
|
||||
@IBOutlet private var menuNextSplit: NSMenuItem?
|
||||
@@ -48,10 +51,12 @@ class AppDelegate: NSObject,
|
||||
@IBOutlet private var menuSelectSplitBelow: NSMenuItem?
|
||||
@IBOutlet private var menuSelectSplitLeft: NSMenuItem?
|
||||
@IBOutlet private var menuSelectSplitRight: NSMenuItem?
|
||||
@IBOutlet private var menuReturnToDefaultSize: NSMenuItem?
|
||||
|
||||
@IBOutlet private var menuIncreaseFontSize: NSMenuItem?
|
||||
@IBOutlet private var menuDecreaseFontSize: NSMenuItem?
|
||||
@IBOutlet private var menuResetFontSize: NSMenuItem?
|
||||
@IBOutlet private var menuChangeTitle: NSMenuItem?
|
||||
@IBOutlet private var menuQuickTerminal: NSMenuItem?
|
||||
@IBOutlet private var menuTerminalInspector: NSMenuItem?
|
||||
|
||||
@@ -93,7 +98,7 @@ class AppDelegate: NSObject,
|
||||
}
|
||||
|
||||
/// Tracks the windows that we hid for toggleVisibility.
|
||||
private var hiddenWindows: [Weak<NSWindow>] = []
|
||||
private var hiddenState: ToggleVisibilityState? = nil
|
||||
|
||||
/// The observer for the app appearance.
|
||||
private var appearanceObserver: NSKeyValueObservation? = nil
|
||||
@@ -217,8 +222,8 @@ class AppDelegate: NSObject,
|
||||
}
|
||||
|
||||
func applicationDidBecomeActive(_ notification: Notification) {
|
||||
// If we're back then clear the hidden windows
|
||||
self.hiddenWindows = []
|
||||
// If we're back manually then clear the hidden state because macOS handles it.
|
||||
self.hiddenState = nil
|
||||
|
||||
// First launch stuff
|
||||
if (!applicationHasBecomeActive) {
|
||||
@@ -245,7 +250,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 {
|
||||
@@ -355,7 +366,9 @@ class AppDelegate: NSObject,
|
||||
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)
|
||||
syncMenuShortcut(config, action: "new_split:left", menuItem: self.menuSplitLeft)
|
||||
syncMenuShortcut(config, action: "new_split:down", menuItem: self.menuSplitDown)
|
||||
syncMenuShortcut(config, action: "new_split:up", menuItem: self.menuSplitUp)
|
||||
|
||||
syncMenuShortcut(config, action: "copy_to_clipboard", menuItem: self.menuCopy)
|
||||
syncMenuShortcut(config, action: "paste_from_clipboard", menuItem: self.menuPaste)
|
||||
@@ -374,10 +387,12 @@ class AppDelegate: NSObject,
|
||||
syncMenuShortcut(config, action: "resize_split:right,10", menuItem: self.menuMoveSplitDividerRight)
|
||||
syncMenuShortcut(config, action: "resize_split:left,10", menuItem: self.menuMoveSplitDividerLeft)
|
||||
syncMenuShortcut(config, action: "equalize_splits", menuItem: self.menuEqualizeSplits)
|
||||
syncMenuShortcut(config, action: "reset_window_size", menuItem: self.menuReturnToDefaultSize)
|
||||
|
||||
syncMenuShortcut(config, action: "increase_font_size:1", menuItem: self.menuIncreaseFontSize)
|
||||
syncMenuShortcut(config, action: "decrease_font_size:1", menuItem: self.menuDecreaseFontSize)
|
||||
syncMenuShortcut(config, action: "reset_font_size", menuItem: self.menuResetFontSize)
|
||||
syncMenuShortcut(config, action: "change_title_prompt", menuItem: self.menuChangeTitle)
|
||||
syncMenuShortcut(config, action: "toggle_quick_terminal", menuItem: self.menuQuickTerminal)
|
||||
syncMenuShortcut(config, action: "toggle_visibility", menuItem: self.menuToggleVisibility)
|
||||
syncMenuShortcut(config, action: "inspector:toggle", menuItem: self.menuTerminalInspector)
|
||||
@@ -431,7 +446,7 @@ class AppDelegate: NSObject,
|
||||
// If we have a main window then we don't process any of the keys
|
||||
// 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(
|
||||
@@ -447,26 +462,26 @@ class AppDelegate: NSObject,
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If this event would be handled by our menu then we do nothing.
|
||||
if let mainMenu = NSApp.mainMenu,
|
||||
mainMenu.performKeyEquivalent(with: event) {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// If we reach this point then we try to process the key event
|
||||
// through the Ghostty key mechanism.
|
||||
|
||||
|
||||
// Ghostty must be loaded
|
||||
guard let ghostty = self.ghostty.app else { return event }
|
||||
|
||||
|
||||
// Build our event input and call ghostty
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
@@ -524,6 +539,15 @@ class AppDelegate: NSObject,
|
||||
// AppKit mutex on the appearance.
|
||||
DispatchQueue.main.async { self.syncAppearance(config: config) }
|
||||
|
||||
// Decide whether to hide/unhide app from dock and app switcher
|
||||
switch (config.macosHidden) {
|
||||
case .never:
|
||||
NSApp.setActivationPolicy(.regular)
|
||||
|
||||
case .always:
|
||||
NSApp.setActivationPolicy(.accessory)
|
||||
}
|
||||
|
||||
// If we have configuration errors, we need to show them.
|
||||
let c = ConfigurationErrorsController.sharedInstance
|
||||
c.errors = config.errors
|
||||
@@ -557,6 +581,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 }
|
||||
@@ -711,9 +759,13 @@ class AppDelegate: NSObject,
|
||||
@IBAction func toggleVisibility(_ sender: Any) {
|
||||
// If we have focus, then we hide all windows.
|
||||
if NSApp.isActive {
|
||||
// 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.
|
||||
self.hiddenWindows = NSApp.windows.filter { $0.isVisible }.map { Weak($0) }
|
||||
// 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
|
||||
}
|
||||
@@ -724,8 +776,16 @@ class AppDelegate: NSObject,
|
||||
// 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.
|
||||
self.hiddenWindows.forEach { $0.value?.orderFrontRegardless() }
|
||||
self.hiddenWindows = []
|
||||
hiddenState?.restore()
|
||||
hiddenState = nil
|
||||
}
|
||||
|
||||
@IBAction func bringAllToFront(_ sender: Any) {
|
||||
if !NSApp.isActive {
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
|
||||
NSApplication.shared.arrangeInFront(sender)
|
||||
}
|
||||
|
||||
private struct DerivedConfig {
|
||||
@@ -745,4 +805,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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -14,6 +14,8 @@
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customObject id="bbz-4X-AYv" userLabel="AppDelegate" customClass="AppDelegate" customModule="Ghostty" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="menuBringAllToFront" destination="LE2-aR-0XJ" id="AP9-oK-60V"/>
|
||||
<outlet property="menuChangeTitle" destination="24I-xg-qIq" id="kg6-kT-jNL"/>
|
||||
<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"/>
|
||||
@@ -38,6 +40,7 @@
|
||||
<outlet property="menuQuit" destination="4sb-4s-VLi" id="qYN-S1-6UW"/>
|
||||
<outlet property="menuReloadConfig" destination="KKH-XX-5py" id="Wvp-7J-wqX"/>
|
||||
<outlet property="menuResetFontSize" destination="Jah-MY-aLX" id="ger-qM-wrm"/>
|
||||
<outlet property="menuReturnToDefaultSize" destination="Gbx-Vi-OGC" id="po9-qC-Iz6"/>
|
||||
<outlet property="menuSecureInput" destination="oC6-w4-qI7" id="PCc-pe-Mda"/>
|
||||
<outlet property="menuSelectAll" destination="q2h-lq-e4r" id="s98-r1-Jcv"/>
|
||||
<outlet property="menuSelectSplitAbove" destination="0yU-hC-8xF" id="aPc-lS-own"/>
|
||||
@@ -45,8 +48,10 @@
|
||||
<outlet property="menuSelectSplitLeft" destination="cTK-oy-KuV" id="Jpr-5q-dqz"/>
|
||||
<outlet property="menuSelectSplitRight" destination="upj-mc-L7X" id="nLY-o1-lky"/>
|
||||
<outlet property="menuServices" destination="aQe-vS-j8Q" id="uWQ-Wo-T1L"/>
|
||||
<outlet property="menuSplitDown" destination="UDZ-4y-6xL" id="fgZ-Wb-8OR"/>
|
||||
<outlet property="menuSplitDown" destination="UDZ-4y-6xL" id="ptr-mj-Azh"/>
|
||||
<outlet property="menuSplitLeft" destination="Ppv-GP-lQU" id="Xd5-Cd-Jut"/>
|
||||
<outlet property="menuSplitRight" destination="VUR-Ld-nLx" id="RxO-Zw-ovb"/>
|
||||
<outlet property="menuSplitUp" destination="Ggp-7N-GbX" id="YJF-uq-S4Y"/>
|
||||
<outlet property="menuTerminalInspector" destination="QwP-M5-fvh" id="wJi-Dh-S9f"/>
|
||||
<outlet property="menuToggleFullScreen" destination="8kY-Pi-KaY" id="yQg-6V-OO6"/>
|
||||
<outlet property="menuToggleVisibility" destination="DOX-wA-ilh" id="iBj-Bc-2bq"/>
|
||||
@@ -143,10 +148,22 @@
|
||||
<action selector="splitRight:" target="-1" id="cv2-Xg-FR4"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Split Left" id="Ppv-GP-lQU">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="splitLeft:" target="-1" id="Cey-Mf-bD2"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Split Down" id="UDZ-4y-6xL">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="splitDown:" target="-1" id="c6x-CF-u52"/>
|
||||
<action selector="splitDown:" target="-1" id="Zej-CF-6nO"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Split Up" id="Ggp-7N-GbX">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="splitUp:" target="-1" id="bbi-dK-pOS"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="sjq-M1-UGS"/>
|
||||
@@ -232,6 +249,13 @@
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="L3L-I8-sqk"/>
|
||||
<menuItem title="Change Title..." id="24I-xg-qIq">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="changeTitle:" target="-1" id="XuL-QB-Q9l"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="Vkj-tP-dMZ"/>
|
||||
<menuItem title="Quick Terminal" id="1pv-LF-NBJ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
@@ -270,12 +294,6 @@
|
||||
<action selector="toggleGhosttyFullScreen:" target="-1" id="QB9-7R-xyc"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Show/Hide All Terminals" id="DOX-wA-ilh">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
@@ -370,6 +388,20 @@
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="dgt-Tx-d4e"/>
|
||||
<menuItem title="Return To Default Size" id="Gbx-Vi-OGC" userLabel="Return To Default Size">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="returnToDefaultSize:" target="-1" id="Bpt-GO-UU1"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="CpM-rI-Sc1"/>
|
||||
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
|
@@ -59,6 +59,11 @@ class QuickTerminalController: BaseTerminalController {
|
||||
selector: #selector(ghosttyConfigDidChange(_:)),
|
||||
name: .ghosttyConfigDidChange,
|
||||
object: nil)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(onNewTab),
|
||||
name: Ghostty.Notification.ghosttyNewTab,
|
||||
object: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@@ -437,14 +442,7 @@ class QuickTerminalController: BaseTerminalController {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: First Responder
|
||||
|
||||
@IBAction override func closeWindow(_ sender: Any) {
|
||||
// Instead of closing the window, we animate it out.
|
||||
animateOut()
|
||||
}
|
||||
|
||||
@IBAction func newTab(_ sender: Any?) {
|
||||
private func showNoNewTabAlert() {
|
||||
guard let window else { return }
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Cannot Create New Tab"
|
||||
@@ -454,11 +452,27 @@ class QuickTerminalController: BaseTerminalController {
|
||||
alert.beginSheetModal(for: window)
|
||||
}
|
||||
|
||||
// MARK: First Responder
|
||||
|
||||
@IBAction override func closeWindow(_ sender: Any) {
|
||||
// Instead of closing the window, we animate it out.
|
||||
animateOut()
|
||||
}
|
||||
|
||||
@IBAction func newTab(_ sender: Any?) {
|
||||
showNoNewTabAlert()
|
||||
}
|
||||
|
||||
@IBAction func toggleGhosttyFullScreen(_ sender: Any) {
|
||||
guard let surface = focusedSurface?.surface else { return }
|
||||
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) {
|
||||
@@ -492,6 +506,14 @@ class QuickTerminalController: BaseTerminalController {
|
||||
syncAppearance()
|
||||
}
|
||||
|
||||
@objc private func onNewTab(notification: SwiftUI.Notification) {
|
||||
guard let surfaceView = notification.object as? Ghostty.SurfaceView else { return }
|
||||
guard let window = surfaceView.window else { return }
|
||||
guard window.windowController is QuickTerminalController else { return }
|
||||
// Tabs aren't supported with Quick Terminals or derivatives
|
||||
showNoNewTabAlert()
|
||||
}
|
||||
|
||||
private struct DerivedConfig {
|
||||
let quickTerminalScreen: QuickTerminalScreen
|
||||
let quickTerminalAnimationDuration: Double
|
||||
|
@@ -413,6 +413,14 @@ class BaseTerminalController: NSWindowController,
|
||||
override func windowDidLoad() {
|
||||
guard let window else { return }
|
||||
|
||||
// If there is a hardcoded title in the configuration, we set that
|
||||
// immediately. Future `set_title` apprt actions will override this
|
||||
// if necessary but this ensures our window loads with the proper
|
||||
// title immediately rather than on another event loop tick (see #5934)
|
||||
if let title = derivedConfig.title {
|
||||
window.title = title
|
||||
}
|
||||
|
||||
// We always initialize our fullscreen style to native if we can because
|
||||
// initialization sets up some state (i.e. observers). If its set already
|
||||
// somehow we don't do this.
|
||||
@@ -521,11 +529,21 @@ class BaseTerminalController: NSWindowController,
|
||||
ghostty.split(surface: surface, direction: GHOSTTY_SPLIT_DIRECTION_RIGHT)
|
||||
}
|
||||
|
||||
@IBAction func splitLeft(_ sender: Any) {
|
||||
guard let surface = focusedSurface?.surface else { return }
|
||||
ghostty.split(surface: surface, direction: GHOSTTY_SPLIT_DIRECTION_LEFT)
|
||||
}
|
||||
|
||||
@IBAction func splitDown(_ sender: Any) {
|
||||
guard let surface = focusedSurface?.surface else { return }
|
||||
ghostty.split(surface: surface, direction: GHOSTTY_SPLIT_DIRECTION_DOWN)
|
||||
}
|
||||
|
||||
@IBAction func splitUp(_ sender: Any) {
|
||||
guard let surface = focusedSurface?.surface else { return }
|
||||
ghostty.split(surface: surface, direction: GHOSTTY_SPLIT_DIRECTION_UP)
|
||||
}
|
||||
|
||||
@IBAction func splitZoom(_ sender: Any) {
|
||||
guard let surface = focusedSurface?.surface else { return }
|
||||
ghostty.splitToggleZoom(surface: surface)
|
||||
@@ -607,17 +625,20 @@ class BaseTerminalController: NSWindowController,
|
||||
}
|
||||
|
||||
private struct DerivedConfig {
|
||||
let title: String?
|
||||
let macosTitlebarProxyIcon: Ghostty.MacOSTitlebarProxyIcon
|
||||
let windowStepResize: Bool
|
||||
let focusFollowsMouse: Bool
|
||||
|
||||
init() {
|
||||
self.title = nil
|
||||
self.macosTitlebarProxyIcon = .visible
|
||||
self.windowStepResize = false
|
||||
self.focusFollowsMouse = false
|
||||
}
|
||||
|
||||
init(_ config: Ghostty.Config) {
|
||||
self.title = config.title
|
||||
self.macosTitlebarProxyIcon = config.macosTitlebarProxyIcon
|
||||
self.windowStepResize = config.windowStepResize
|
||||
self.focusFollowsMouse = config.focusFollowsMouse
|
||||
|
@@ -27,6 +27,9 @@ class TerminalController: BaseTerminalController {
|
||||
/// The notification cancellable for focused surface property changes.
|
||||
private var surfaceAppearanceCancellables: Set<AnyCancellable> = []
|
||||
|
||||
/// This will be set to the initial frame of the window from the xib on load.
|
||||
private var initialFrame: NSRect? = nil
|
||||
|
||||
init(_ ghostty: Ghostty.App,
|
||||
withBaseConfig base: Ghostty.SurfaceConfiguration? = nil,
|
||||
withSurfaceTree tree: Ghostty.SplitNode? = nil
|
||||
@@ -65,6 +68,12 @@ class TerminalController: BaseTerminalController {
|
||||
selector: #selector(onCloseTab),
|
||||
name: .ghosttyCloseTab,
|
||||
object: nil)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(onResetWindowSize),
|
||||
name: .ghosttyResetWindowSize,
|
||||
object: nil
|
||||
)
|
||||
center.addObserver(
|
||||
self,
|
||||
selector: #selector(ghosttyConfigDidChange(_:)),
|
||||
@@ -76,6 +85,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) {
|
||||
@@ -212,6 +227,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.
|
||||
@@ -283,9 +301,12 @@ class TerminalController: BaseTerminalController {
|
||||
private func setInitialWindowPosition(x: Int16?, y: Int16?, windowDecorations: Bool) {
|
||||
guard let window else { return }
|
||||
|
||||
// If we don't have both an X and Y we center.
|
||||
// If we don't have an X/Y then we try to use the previously saved window pos.
|
||||
guard let x, let y else {
|
||||
window.center()
|
||||
if (!LastWindowPosition.shared.restore(window)) {
|
||||
window.center()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -302,6 +323,55 @@ class TerminalController: BaseTerminalController {
|
||||
y: frame.maxY - (CGFloat(y) + window.frame.height)))
|
||||
}
|
||||
|
||||
/// Returns the default size of the window. This is contextual based on the focused surface because
|
||||
/// the focused surface may specify a different default size than others.
|
||||
private var defaultSize: NSRect? {
|
||||
guard let screen = window?.screen ?? NSScreen.main else { return nil }
|
||||
|
||||
if derivedConfig.maximize {
|
||||
return screen.visibleFrame
|
||||
} else if let focusedSurface,
|
||||
let initialSize = focusedSurface.initialSize {
|
||||
// Get the current frame of the window
|
||||
guard var frame = window?.frame else { return nil }
|
||||
|
||||
// Calculate the chrome size (window size minus view size)
|
||||
let chromeWidth = frame.size.width - focusedSurface.frame.size.width
|
||||
let chromeHeight = frame.size.height - focusedSurface.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))
|
||||
|
||||
return frame
|
||||
}
|
||||
|
||||
guard let initialFrame else { return nil }
|
||||
guard var frame = window?.frame else { return nil }
|
||||
|
||||
// Calculate the new width and height, clamping to the screen's size
|
||||
let newWidth = min(initialFrame.size.width, screen.visibleFrame.width)
|
||||
let newHeight = min(initialFrame.size.height, 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))
|
||||
|
||||
return frame
|
||||
}
|
||||
|
||||
//MARK: - NSWindowController
|
||||
|
||||
override func windowWillLoad() {
|
||||
@@ -350,6 +420,9 @@ class TerminalController: BaseTerminalController {
|
||||
super.windowDidLoad()
|
||||
guard let window = window as? TerminalWindow else { return }
|
||||
|
||||
// Store our initial frame so we can know our default later.
|
||||
initialFrame = window.frame
|
||||
|
||||
// I copy this because we may change the source in the future but also because
|
||||
// I regularly audit our codebase for "ghostty.config" access because generally
|
||||
// you shouldn't use it. Its safe in this case because for a new window we should
|
||||
@@ -366,32 +439,15 @@ class TerminalController: BaseTerminalController {
|
||||
// If window decorations are disabled, remove our title
|
||||
if (!config.windowDecorations) { window.styleMask.remove(.titled) }
|
||||
|
||||
// If we have only a single surface (no splits) and that surface requested
|
||||
// an initial size then we set it here now.
|
||||
// If we have only a single surface (no splits) and there is a default size then
|
||||
// we should resize to that default size.
|
||||
if case let .leaf(leaf) = surfaceTree {
|
||||
if let initialSize = leaf.surface.initialSize,
|
||||
let screen = window.screen ?? NSScreen.main {
|
||||
// Get the current frame of the window
|
||||
var frame = window.frame
|
||||
// If this is our first surface then our focused surface will be nil
|
||||
// so we force the focused surface to the leaf.
|
||||
focusedSurface = leaf.surface
|
||||
|
||||
// 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)
|
||||
if let defaultSize {
|
||||
window.setFrame(defaultSize, display: true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,6 +546,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
|
||||
@@ -554,6 +624,11 @@ class TerminalController: BaseTerminalController {
|
||||
window.close()
|
||||
}
|
||||
|
||||
@IBAction func returnToDefaultSize(_ sender: Any?) {
|
||||
guard let defaultSize else { return }
|
||||
window?.setFrame(defaultSize, display: true)
|
||||
}
|
||||
|
||||
@IBAction override func closeWindow(_ sender: Any?) {
|
||||
guard let window = window else { return }
|
||||
guard let tabGroup = window.tabGroup else {
|
||||
@@ -692,13 +767,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) {
|
||||
@@ -759,6 +842,12 @@ class TerminalController: BaseTerminalController {
|
||||
closeTab(self)
|
||||
}
|
||||
|
||||
@objc private func onResetWindowSize(notification: SwiftUI.Notification) {
|
||||
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||
guard surfaceTree?.contains(view: target) ?? false else { return }
|
||||
returnToDefaultSize(nil)
|
||||
}
|
||||
|
||||
@objc private func onToggleFullscreen(notification: SwiftUI.Notification) {
|
||||
guard let target = notification.object as? Ghostty.SurfaceView else { return }
|
||||
guard target == self.focusedSurface else { return }
|
||||
@@ -776,18 +865,69 @@ class TerminalController: BaseTerminalController {
|
||||
toggleFullscreen(mode: fullscreenMode)
|
||||
}
|
||||
|
||||
@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
|
||||
let maximize: Bool
|
||||
|
||||
init() {
|
||||
self.backgroundColor = Color(NSColor.windowBackgroundColor)
|
||||
self.macosTitlebarStyle = "system"
|
||||
self.maximize = false
|
||||
}
|
||||
|
||||
init(_ config: Ghostty.Config) {
|
||||
self.backgroundColor = config.backgroundColor
|
||||
self.macosTitlebarStyle = config.macosTitlebarStyle
|
||||
self.maximize = config.maximize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension TerminalController: NSMenuItemValidation {
|
||||
func validateMenuItem(_ item: NSMenuItem) -> Bool {
|
||||
switch item.action {
|
||||
case #selector(returnToDefaultSize):
|
||||
guard let window else { return false }
|
||||
|
||||
// Native fullscreen windows can't revert to default size.
|
||||
if window.styleMask.contains(.fullScreen) {
|
||||
return false
|
||||
}
|
||||
|
||||
// If we're fullscreen at all then we can't change size
|
||||
if fullscreenStyle?.isFullscreen ?? false {
|
||||
return false
|
||||
}
|
||||
|
||||
// If our window is already the default size or we don't have a
|
||||
// default size, then disable.
|
||||
guard let defaultSize,
|
||||
window.frame.size != .init(
|
||||
width: defaultSize.size.width,
|
||||
height: defaultSize.size.height
|
||||
)
|
||||
else {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -86,7 +86,7 @@ class TerminalManager {
|
||||
// fullscreen for the logic later in this method.
|
||||
c.toggleFullscreen(mode: .native)
|
||||
|
||||
case .nonNative, .nonNativeVisibleMenu:
|
||||
case .nonNative, .nonNativeVisibleMenu, .nonNativePaddedNotch:
|
||||
// If we're non-native then we have to do it on a later loop
|
||||
// so that the content view is setup.
|
||||
DispatchQueue.main.async {
|
||||
@@ -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
|
||||
@@ -128,6 +125,9 @@ class TerminalManager {
|
||||
}
|
||||
|
||||
private func newTab(to parent: NSWindow, withBaseConfig base: Ghostty.SurfaceConfiguration?) {
|
||||
// Making sure that we're dealing with a TerminalController
|
||||
guard parent.windowController is TerminalController else { return }
|
||||
|
||||
// If our parent is in non-native fullscreen, then new tabs do not work.
|
||||
// See: https://github.com/mitchellh/ghostty/issues/392
|
||||
if let controller = parent.windowController as? TerminalController,
|
||||
|
@@ -95,6 +95,23 @@ fileprivate class CenteredDynamicLabel: NSTextField {
|
||||
setContentHuggingPriority(.defaultLow, for: .horizontal)
|
||||
setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
||||
}
|
||||
|
||||
// Vertically center the text
|
||||
override func draw(_ dirtyRect: NSRect) {
|
||||
guard let attributedString = self.attributedStringValue.mutableCopy() as? NSMutableAttributedString else {
|
||||
super.draw(dirtyRect)
|
||||
return
|
||||
}
|
||||
|
||||
let textSize = attributedString.size()
|
||||
|
||||
let yOffset = (self.bounds.height - textSize.height) / 2 - 1 // -1 to center it better
|
||||
|
||||
let centeredRect = NSRect(x: self.bounds.origin.x, y: self.bounds.origin.y + yOffset,
|
||||
width: self.bounds.width, height: textSize.height)
|
||||
|
||||
attributedString.draw(in: centeredRect)
|
||||
}
|
||||
}
|
||||
|
||||
extension NSToolbarItem.Identifier {
|
||||
|
@@ -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 }
|
||||
@@ -295,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)
|
||||
@@ -714,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)
|
||||
|
||||
|
@@ -13,6 +13,9 @@ extension FullscreenMode {
|
||||
case GHOSTTY_FULLSCREEN_NON_NATIVE_VISIBLE_MENU:
|
||||
.nonNativeVisibleMenu
|
||||
|
||||
case GHOSTTY_FULLSCREEN_NON_NATIVE_PADDED_NOTCH:
|
||||
.nonNativePaddedNotch
|
||||
|
||||
default:
|
||||
nil
|
||||
}
|
||||
|
@@ -257,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,
|
||||
@@ -423,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:
|
||||
@@ -431,7 +431,7 @@ extension Ghostty {
|
||||
|
||||
default:
|
||||
Ghostty.logger.warning("unknown action target=\(target.tag.rawValue)")
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
// Action dispatch
|
||||
@@ -455,13 +455,13 @@ extension Ghostty {
|
||||
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)
|
||||
@@ -484,6 +484,9 @@ extension Ghostty {
|
||||
case GHOSTTY_ACTION_SET_TITLE:
|
||||
setTitle(app, target: target, v: action.action.set_title)
|
||||
|
||||
case GHOSTTY_ACTION_PROMPT_TITLE:
|
||||
return promptTitle(app, target: target)
|
||||
|
||||
case GHOSTTY_ACTION_PWD:
|
||||
pwdChanged(app, target: target, v: action.action.pwd)
|
||||
|
||||
@@ -505,6 +508,9 @@ extension Ghostty {
|
||||
case GHOSTTY_ACTION_INITIAL_SIZE:
|
||||
setInitialSize(app, target: target, v: action.action.initial_size)
|
||||
|
||||
case GHOSTTY_ACTION_RESET_WINDOW_SIZE:
|
||||
resetWindowSize(app, target: target)
|
||||
|
||||
case GHOSTTY_ACTION_CELL_SIZE:
|
||||
setCellSize(app, target: target, v: action.action.cell_size)
|
||||
|
||||
@@ -541,10 +547,15 @@ 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) {
|
||||
@@ -716,15 +727,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,
|
||||
@@ -736,20 +751,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,
|
||||
@@ -761,20 +783,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,
|
||||
@@ -786,6 +819,8 @@ extension Ghostty {
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private static func resizeSplit(
|
||||
@@ -978,6 +1013,26 @@ extension Ghostty {
|
||||
}
|
||||
}
|
||||
|
||||
private static func promptTitle(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s) -> Bool {
|
||||
switch (target.tag) {
|
||||
case GHOSTTY_TARGET_APP:
|
||||
Ghostty.logger.warning("set title prompt does nothing with an app target")
|
||||
return false
|
||||
|
||||
case GHOSTTY_TARGET_SURFACE:
|
||||
guard let surface = target.target.surface else { return false }
|
||||
guard let surfaceView = self.surfaceView(from: surface) else { return false }
|
||||
surfaceView.promptTitle()
|
||||
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private static func pwdChanged(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s,
|
||||
@@ -1079,7 +1134,7 @@ extension Ghostty {
|
||||
v: ghostty_action_initial_size_s) {
|
||||
switch (target.tag) {
|
||||
case GHOSTTY_TARGET_APP:
|
||||
Ghostty.logger.warning("mouse over link does nothing with an app target")
|
||||
Ghostty.logger.warning("initial size does nothing with an app target")
|
||||
return
|
||||
|
||||
case GHOSTTY_TARGET_SURFACE:
|
||||
@@ -1093,6 +1148,28 @@ extension Ghostty {
|
||||
}
|
||||
}
|
||||
|
||||
private static func resetWindowSize(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s) {
|
||||
switch (target.tag) {
|
||||
case GHOSTTY_TARGET_APP:
|
||||
Ghostty.logger.warning("reset window size 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: .ghosttyResetWindowSize,
|
||||
object: surfaceView
|
||||
)
|
||||
|
||||
|
||||
default:
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
private static func setCellSize(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s,
|
||||
|
@@ -132,6 +132,15 @@ extension Ghostty {
|
||||
return v
|
||||
}
|
||||
|
||||
var title: String? {
|
||||
guard let config = self.config else { return nil }
|
||||
var v: UnsafePointer<Int8>? = nil
|
||||
let key = "title"
|
||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return nil }
|
||||
guard let ptr = v else { return nil }
|
||||
return String(cString: ptr)
|
||||
}
|
||||
|
||||
var windowSaveState: String {
|
||||
guard let config = self.config else { return "" }
|
||||
var v: UnsafePointer<Int8>? = nil
|
||||
@@ -140,7 +149,7 @@ 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
|
||||
@@ -216,6 +225,8 @@ extension Ghostty {
|
||||
.nonNative
|
||||
case "visible-menu":
|
||||
.nonNativeVisibleMenu
|
||||
case "padded-notch":
|
||||
.nonNativePaddedNotch
|
||||
default:
|
||||
defaultValue
|
||||
}
|
||||
@@ -300,6 +311,16 @@ extension Ghostty {
|
||||
return buffer.map { .init(ghostty: $0) }
|
||||
}
|
||||
|
||||
var macosHidden: MacHidden {
|
||||
guard let config = self.config else { return .never }
|
||||
var v: UnsafePointer<Int8>? = nil
|
||||
let key = "macos-hidden"
|
||||
guard ghostty_config_get(config, &v, key, UInt(key.count)) else { return .never }
|
||||
guard let ptr = v else { return .never }
|
||||
let str = String(cString: ptr)
|
||||
return MacHidden(rawValue: str) ?? .never
|
||||
}
|
||||
|
||||
var focusFollowsMouse : Bool {
|
||||
guard let config = self.config else { return false }
|
||||
var v = false;
|
||||
@@ -502,6 +523,14 @@ extension Ghostty {
|
||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
||||
return v
|
||||
}
|
||||
|
||||
var maximize: Bool {
|
||||
guard let config = self.config else { return true }
|
||||
var v = false;
|
||||
let key = "maximize"
|
||||
_ = ghostty_config_get(config, &v, key, UInt(key.count))
|
||||
return v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,6 +543,11 @@ extension Ghostty.Config {
|
||||
case download
|
||||
}
|
||||
|
||||
enum MacHidden : String {
|
||||
case never
|
||||
case always
|
||||
}
|
||||
|
||||
enum ResizeOverlay : String {
|
||||
case always
|
||||
case never
|
||||
|
@@ -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):
|
||||
@@ -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]> {
|
||||
|
@@ -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.
|
||||
|
@@ -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"
|
||||
}
|
||||
|
||||
@@ -239,6 +247,9 @@ extension Notification.Name {
|
||||
|
||||
/// Close tab
|
||||
static let ghosttyCloseTab = Notification.Name("com.mitchellh.ghostty.closeTab")
|
||||
|
||||
/// Resize the window to a default size.
|
||||
static let ghosttyResetWindowSize = Notification.Name("com.mitchellh.ghostty.resetWindowSize")
|
||||
}
|
||||
|
||||
// NOTE: I am moving all of these to Notification.Name extensions over time. This
|
||||
|
@@ -124,6 +124,11 @@ extension Ghostty {
|
||||
// A timer to fallback to ghost emoji if no title is set within the grace period
|
||||
private var titleFallbackTimer: Timer?
|
||||
|
||||
// This is the title from the terminal. This is nil if we're currently using
|
||||
// the terminal title as the main title property. If the title is set manually
|
||||
// by the user, this is set to the prior value (which may be empty, but non-nil).
|
||||
private var titleFromTerminal: String?
|
||||
|
||||
/// Event monitor (see individual events for why)
|
||||
private var eventMonitor: Any? = nil
|
||||
|
||||
@@ -380,6 +385,45 @@ extension Ghostty {
|
||||
NSCursor.setHiddenUntilMouseMoves(!visible)
|
||||
}
|
||||
|
||||
/// Set the title by prompting the user.
|
||||
func promptTitle() {
|
||||
// Create an alert dialog
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Change Terminal Title"
|
||||
alert.informativeText = "Leave blank to restore the default."
|
||||
alert.alertStyle = .informational
|
||||
|
||||
// Add a text field to the alert
|
||||
let textField = NSTextField(frame: NSRect(x: 0, y: 0, width: 250, height: 24))
|
||||
textField.stringValue = title
|
||||
alert.accessoryView = textField
|
||||
|
||||
// Add buttons
|
||||
alert.addButton(withTitle: "OK")
|
||||
alert.addButton(withTitle: "Cancel")
|
||||
|
||||
let response = alert.runModal()
|
||||
|
||||
// Check if the user clicked "OK"
|
||||
if response == .alertFirstButtonReturn {
|
||||
// Get the input text
|
||||
let newTitle = textField.stringValue
|
||||
|
||||
if newTitle.isEmpty {
|
||||
// Empty means that user wants the title to be set automatically
|
||||
// We also need to reload the config for the "title" property to be
|
||||
// used again by this tab.
|
||||
let prevTitle = titleFromTerminal ?? "👻"
|
||||
titleFromTerminal = nil
|
||||
setTitle(prevTitle)
|
||||
} else {
|
||||
// Set the title and prevent it from being changed automatically
|
||||
titleFromTerminal = title
|
||||
title = newTitle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setTitle(_ title: String) {
|
||||
// This fixes an issue where very quick changes to the title could
|
||||
// cause an unpleasant flickering. We set a timer so that we can
|
||||
@@ -390,6 +434,11 @@ extension Ghostty {
|
||||
withTimeInterval: 0.075,
|
||||
repeats: false
|
||||
) { [weak self] _ in
|
||||
// Set the title if it wasn't manually set.
|
||||
guard self?.titleFromTerminal == nil else {
|
||||
self?.titleFromTerminal = title
|
||||
return
|
||||
}
|
||||
self?.title = title
|
||||
}
|
||||
}
|
||||
@@ -849,28 +898,8 @@ extension Ghostty {
|
||||
var handled: Bool = false
|
||||
if let list = keyTextAccumulator, list.count > 0 {
|
||||
handled = true
|
||||
|
||||
// This is a hack. libghostty on macOS treats ctrl input as not having
|
||||
// text because some keyboard layouts generate bogus characters for
|
||||
// ctrl+key. libghostty can't tell this is from an IM keyboard giving
|
||||
// us direct values. So, we just remove control.
|
||||
var modifierFlags = event.modifierFlags
|
||||
modifierFlags.remove(.control)
|
||||
if let keyTextEvent = NSEvent.keyEvent(
|
||||
with: .keyDown,
|
||||
location: event.locationInWindow,
|
||||
modifierFlags: modifierFlags,
|
||||
timestamp: event.timestamp,
|
||||
windowNumber: event.windowNumber,
|
||||
context: nil,
|
||||
characters: event.characters ?? "",
|
||||
charactersIgnoringModifiers: event.charactersIgnoringModifiers ?? "",
|
||||
isARepeat: event.isARepeat,
|
||||
keyCode: event.keyCode
|
||||
) {
|
||||
for text in list {
|
||||
_ = keyAction(action, event: keyTextEvent, text: text)
|
||||
}
|
||||
for text in list {
|
||||
_ = keyAction(action, event: event, text: text)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -893,6 +922,33 @@ extension Ghostty {
|
||||
_ = keyAction(GHOSTTY_ACTION_RELEASE, event: event)
|
||||
}
|
||||
|
||||
/// Records the timestamp of the last event to performKeyEquivalent that had a command key active.
|
||||
///
|
||||
/// For command+key inputs, the AppKit input stack calls performKeyEquivalent to give us a chance
|
||||
/// to handle them first. If we return "false" then it goes through the standard AppKit responder chain.
|
||||
/// For an NSTextInputClient, that may redirect some commands _before_ our keyDown gets called.
|
||||
/// Concretely: Command+Period will do: performKeyEquivalent, doCommand ("cancel:"). In doCommand,
|
||||
/// we need to know that we actually want to handle that in keyDown, so we send it back through the
|
||||
/// event dispatch system and use this timestamp as an identity to know to actually send it to keyDown.
|
||||
///
|
||||
/// Why not send it to keyDown always? Because if the user rebinds a command to something we
|
||||
/// actually handle then we do want the standard response chain to handle the key input. Unfortunately,
|
||||
/// we can't know what a command is bound to at a system level until we let it flow through the system.
|
||||
/// That's the crux of the problem.
|
||||
///
|
||||
/// So, we have to send it back through if we didn't handle it.
|
||||
///
|
||||
/// The next part of the problem is comparing NSEvent identity seems pretty nasty. I couldn't
|
||||
/// find a good way to do it. I originally stored a weak ref and did identity comparison but that
|
||||
/// doesn't work and for reasons I couldn't figure out the value gets mangled (fields don't match
|
||||
/// before/after the assignment). I suspect it has something to do with the fact an NSEvent is wrapping
|
||||
/// a lower level event pointer and its just not surviving the Swift runtime somehow. I don't know.
|
||||
///
|
||||
/// The best thing I could find was to store the event timestamp which has decent granularity
|
||||
/// and compare that. To further complicate things, some events are synthetic and have a zero
|
||||
/// timestamp so we have to protect against that. Fun!
|
||||
var lastCommandEvent: TimeInterval?
|
||||
|
||||
/// Special case handling for some control keys
|
||||
override func performKeyEquivalent(with event: NSEvent) -> Bool {
|
||||
switch (event.type) {
|
||||
@@ -946,15 +1002,41 @@ extension Ghostty {
|
||||
|
||||
equivalent = "\r"
|
||||
|
||||
case ".":
|
||||
if (!event.modifierFlags.contains(.command)) {
|
||||
default:
|
||||
// It looks like some part of AppKit sometimes generates synthetic NSEvents
|
||||
// with a zero timestamp. We never process these at this point. Concretely,
|
||||
// this happens for me when pressing Cmd+period with default bindings. This
|
||||
// binds to "cancel" which goes through AppKit to produce a synthetic "escape".
|
||||
//
|
||||
// Question: should we be ignoring all synthetic events? Should we be finding
|
||||
// synthetic escape and ignoring it? I feel like Cmd+period could map to a
|
||||
// escape binding by accident, but it hasn't happened yet...
|
||||
if event.timestamp == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
equivalent = "."
|
||||
// All of this logic here re: lastCommandEvent is to workaround some
|
||||
// nasty behavior. See the docs for lastCommandEvent for more info.
|
||||
|
||||
default:
|
||||
// Ignore other events
|
||||
// Ignore all other non-command events. This lets the event continue
|
||||
// through the AppKit event systems.
|
||||
if (!event.modifierFlags.contains(.command)) {
|
||||
// Reset since we got a non-command event.
|
||||
lastCommandEvent = nil
|
||||
return false
|
||||
}
|
||||
|
||||
// If we have a prior command binding and the timestamp matches exactly
|
||||
// then we pass it through to keyDown for encoding.
|
||||
if let lastCommandEvent {
|
||||
self.lastCommandEvent = nil
|
||||
if lastCommandEvent == event.timestamp {
|
||||
equivalent = event.characters ?? ""
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
lastCommandEvent = event.timestamp
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1132,11 +1214,15 @@ extension Ghostty {
|
||||
|
||||
menu.addItem(.separator())
|
||||
menu.addItem(withTitle: "Split Right", action: #selector(splitRight(_:)), keyEquivalent: "")
|
||||
menu.addItem(withTitle: "Split Left", action: #selector(splitLeft(_:)), keyEquivalent: "")
|
||||
menu.addItem(withTitle: "Split Down", action: #selector(splitDown(_:)), keyEquivalent: "")
|
||||
menu.addItem(withTitle: "Split Up", action: #selector(splitUp(_:)), keyEquivalent: "")
|
||||
|
||||
menu.addItem(.separator())
|
||||
menu.addItem(withTitle: "Reset Terminal", action: #selector(resetTerminal(_:)), keyEquivalent: "")
|
||||
menu.addItem(withTitle: "Toggle Terminal Inspector", action: #selector(toggleTerminalInspector(_:)), keyEquivalent: "")
|
||||
menu.addItem(.separator())
|
||||
menu.addItem(withTitle: "Change Title...", action: #selector(changeTitle(_:)), keyEquivalent: "")
|
||||
|
||||
return menu
|
||||
}
|
||||
@@ -1189,11 +1275,21 @@ extension Ghostty {
|
||||
ghostty_surface_split(surface, GHOSTTY_SPLIT_DIRECTION_RIGHT)
|
||||
}
|
||||
|
||||
@IBAction func splitLeft(_ sender: Any) {
|
||||
guard let surface = self.surface else { return }
|
||||
ghostty_surface_split(surface, GHOSTTY_SPLIT_DIRECTION_LEFT)
|
||||
}
|
||||
|
||||
@IBAction func splitDown(_ sender: Any) {
|
||||
guard let surface = self.surface else { return }
|
||||
ghostty_surface_split(surface, GHOSTTY_SPLIT_DIRECTION_DOWN)
|
||||
}
|
||||
|
||||
@IBAction func splitUp(_ sender: Any) {
|
||||
guard let surface = self.surface else { return }
|
||||
ghostty_surface_split(surface, GHOSTTY_SPLIT_DIRECTION_UP)
|
||||
}
|
||||
|
||||
@objc func resetTerminal(_ sender: Any) {
|
||||
guard let surface = self.surface else { return }
|
||||
let action = "reset"
|
||||
@@ -1209,6 +1305,10 @@ extension Ghostty {
|
||||
AppDelegate.logger.warning("action failed action=\(action)")
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func changeTitle(_ sender: Any) {
|
||||
promptTitle()
|
||||
}
|
||||
|
||||
/// Show a user notification and associate it with this surface
|
||||
func showUserNotification(title: String, body: String) {
|
||||
@@ -1433,9 +1533,19 @@ extension Ghostty.SurfaceView: NSTextInputClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// This function needs to exist for two reasons:
|
||||
/// 1. Prevents an audible NSBeep for unimplemented actions.
|
||||
/// 2. Allows us to properly encode super+key input events that we don't handle
|
||||
override func doCommand(by selector: Selector) {
|
||||
// This currently just prevents NSBeep from interpretKeyEvents but in the future
|
||||
// we may want to make some of this work.
|
||||
// If we are being processed by performKeyEquivalent with a command binding,
|
||||
// we send it back through the event system so it can be encoded.
|
||||
if let lastCommandEvent,
|
||||
let current = NSApp.currentEvent,
|
||||
lastCommandEvent == current.timestamp
|
||||
{
|
||||
NSApp.sendEvent(current)
|
||||
return
|
||||
}
|
||||
|
||||
print("SEL: \(selector)")
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ enum FullscreenMode {
|
||||
case native
|
||||
case nonNative
|
||||
case nonNativeVisibleMenu
|
||||
case nonNativePaddedNotch
|
||||
|
||||
/// Initializes the fullscreen style implementation for the mode. This will not toggle any
|
||||
/// fullscreen properties. This may fail if the window isn't configured properly for a given
|
||||
@@ -20,6 +21,9 @@ enum FullscreenMode {
|
||||
|
||||
case .nonNativeVisibleMenu:
|
||||
return NonNativeFullscreenVisibleMenu(window)
|
||||
|
||||
case .nonNativePaddedNotch:
|
||||
return NonNativeFullscreenPaddedNotch(window)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,6 +145,7 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
||||
|
||||
struct Properties {
|
||||
var hideMenu: Bool = true
|
||||
var paddedNotch: Bool = false
|
||||
}
|
||||
|
||||
private var savedState: SavedState?
|
||||
@@ -278,6 +283,9 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
||||
// put an #available check, but it was in a bug fix release so I think
|
||||
// if a bug is reported to Ghostty we can just advise the user to
|
||||
// update.
|
||||
} else if (properties.paddedNotch) {
|
||||
// We are hiding the menu, we may need to avoid the notch.
|
||||
frame.size.height -= screen.safeAreaInsets.top
|
||||
}
|
||||
|
||||
return frame
|
||||
@@ -349,3 +357,7 @@ class NonNativeFullscreen: FullscreenBase, FullscreenStyle {
|
||||
class NonNativeFullscreenVisibleMenu: NonNativeFullscreen {
|
||||
override var properties: Properties { Properties(hideMenu: false) }
|
||||
}
|
||||
|
||||
class NonNativeFullscreenPaddedNotch: NonNativeFullscreen {
|
||||
override var properties: Properties { Properties(paddedNotch: true) }
|
||||
}
|
||||
|
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
|
||||
}
|
||||
}
|
43
nix/build-support/build-inputs.nix
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
stdenv,
|
||||
enableX11 ? true,
|
||||
enableWayland ? true,
|
||||
}:
|
||||
[
|
||||
pkgs.libGL
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [
|
||||
pkgs.bzip2
|
||||
pkgs.expat
|
||||
pkgs.fontconfig
|
||||
pkgs.freetype
|
||||
pkgs.harfbuzz
|
||||
pkgs.libpng
|
||||
pkgs.libxml2
|
||||
pkgs.oniguruma
|
||||
pkgs.simdutf
|
||||
pkgs.zlib
|
||||
|
||||
pkgs.glslang
|
||||
pkgs.spirv-cross
|
||||
|
||||
pkgs.libxkbcommon
|
||||
|
||||
pkgs.glib
|
||||
pkgs.gobject-introspection
|
||||
pkgs.gsettings-desktop-schemas
|
||||
pkgs.gtk4
|
||||
pkgs.libadwaita
|
||||
]
|
||||
++ lib.optionals (stdenv.hostPlatform.isLinux && enableX11) [
|
||||
pkgs.xorg.libX11
|
||||
pkgs.xorg.libXcursor
|
||||
pkgs.xorg.libXi
|
||||
pkgs.xorg.libXrandr
|
||||
]
|
||||
++ lib.optionals (stdenv.hostPlatform.isLinux && enableWayland) [
|
||||
pkgs.gtk4-layer-shell
|
||||
pkgs.wayland
|
||||
]
|
6
nix/build-support/check-blueprints.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o nounset -o pipefail -o errexit
|
||||
|
||||
find src -name \*.blp -exec blueprint-compiler format {} \+
|
||||
find src -name \*.blp -exec blueprint-compiler compile --output=/dev/null {} \;
|
@@ -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}"
|
104
nix/build-support/check-zig-cache.sh
Executable file
@@ -0,0 +1,104 @@
|
||||
#!/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.zon.json
|
||||
#
|
||||
# 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 build.zig.zon.txt build.zig.zon.json"
|
||||
echo " git commit -m \"nix: update build.zig.zon.nix build.zig.zon.txt build.zig.zon.json\""
|
||||
echo ""
|
||||
}
|
||||
|
||||
ROOT="$(realpath "$(dirname "$0")/../../")"
|
||||
BUILD_ZIG_ZON="$ROOT/build.zig.zon"
|
||||
BUILD_ZIG_ZON_NIX="$ROOT/build.zig.zon.nix"
|
||||
BUILD_ZIG_ZON_TXT="$ROOT/build.zig.zon.txt"
|
||||
BUILD_ZIG_ZON_JSON="$ROOT/build.zig.zon.json"
|
||||
|
||||
if [ -f "${BUILD_ZIG_ZON_NIX}" ]; then
|
||||
OLD_HASH_NIX=$(sha512sum "${BUILD_ZIG_ZON_NIX}" | awk '{print $1}')
|
||||
elif [ "$1" != "--update" ]; then
|
||||
echo -e "\nERROR: build.zig.zon.nix missing."
|
||||
help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f "${BUILD_ZIG_ZON_TXT}" ]; then
|
||||
OLD_HASH_TXT=$(sha512sum "${BUILD_ZIG_ZON_TXT}" | awk '{print $1}')
|
||||
elif [ "$1" != "--update" ]; then
|
||||
echo -e "\nERROR: build.zig.zon.txt missing."
|
||||
help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -f "${BUILD_ZIG_ZON_JSON}" ]; then
|
||||
OLD_HASH_JSON=$(sha512sum "${BUILD_ZIG_ZON_JSON}" | awk '{print $1}')
|
||||
elif [ "$1" != "--update" ]; then
|
||||
echo -e "\nERROR: build.zig.zon.json missing."
|
||||
help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
zon2nix "$BUILD_ZIG_ZON" --nix "$WORK_DIR/build.zig.zon.nix" --txt "$WORK_DIR/build.zig.zon.txt" --json "$WORK_DIR/build.zig.zon.json"
|
||||
alejandra --quiet "$WORK_DIR/build.zig.zon.nix"
|
||||
prettier --write "$WORK_DIR/build.zig.zon.json"
|
||||
|
||||
NEW_HASH_NIX=$(sha512sum "$WORK_DIR/build.zig.zon.nix" | awk '{print $1}')
|
||||
NEW_HASH_TXT=$(sha512sum "$WORK_DIR/build.zig.zon.txt" | awk '{print $1}')
|
||||
NEW_HASH_JSON=$(sha512sum "$WORK_DIR/build.zig.zon.json" | awk '{print $1}')
|
||||
|
||||
if [ "${OLD_HASH_NIX}" == "${NEW_HASH_NIX}" ] && [ "${OLD_HASH_TXT}" == "${NEW_HASH_TXT}" ] && [ "${OLD_HASH_JSON}" == "${NEW_HASH_JSON}" ]; then
|
||||
echo -e "\nOK: build.zig.zon.nix unchanged."
|
||||
echo -e "OK: build.zig.zon.txt unchanged."
|
||||
echo -e "OK: build.zig.zon.json unchanged."
|
||||
exit 0
|
||||
elif [ "$1" != "--update" ]; then
|
||||
echo -e "\nERROR: build.zig.zon.nix, build.zig.zon.txt, or build.zig.zon.json needs to be updated.\n"
|
||||
echo " * Old build.zig.zon.nix hash: ${OLD_HASH_NIX}"
|
||||
echo " * New build.zig.zon.nix hash: ${NEW_HASH_NIX}"
|
||||
echo " * Old build.zig.zon.txt hash: ${OLD_HASH_TXT}"
|
||||
echo " * New build.zig.zon.txt hash: ${NEW_HASH_TXT}"
|
||||
echo " * Old build.zig.zon.json hash: ${OLD_HASH_JSON}"
|
||||
echo " * New build.zig.zon.json hash: ${NEW_HASH_JSON}"
|
||||
help
|
||||
exit 1
|
||||
else
|
||||
mv "$WORK_DIR/build.zig.zon.nix" "$BUILD_ZIG_ZON_NIX"
|
||||
echo -e "\nOK: build.zig.zon.nix updated."
|
||||
mv "$WORK_DIR/build.zig.zon.txt" "$BUILD_ZIG_ZON_TXT"
|
||||
echo -e "OK: build.zig.zon.txt updated."
|
||||
mv "$WORK_DIR/build.zig.zon.json" "$BUILD_ZIG_ZON_JSON"
|
||||
echo -e "OK: build.zig.zon.json 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"
|
||||
|
17
nix/build-support/gi-typelib-path.nix
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
stdenv,
|
||||
}:
|
||||
lib.makeSearchPath "lib/girepository-1.0" (map (lib.getOutput "lib") (lib.optionals stdenv.hostPlatform.isLinux [
|
||||
pkgs.cairo
|
||||
pkgs.gdk-pixbuf
|
||||
pkgs.glib
|
||||
pkgs.gobject-introspection
|
||||
pkgs.graphene
|
||||
pkgs.gtk4
|
||||
pkgs.gtk4-layer-shell
|
||||
pkgs.harfbuzz
|
||||
pkgs.libadwaita
|
||||
pkgs.pango
|
||||
]))
|
10
nix/build-support/ld-library-path.nix
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
stdenv,
|
||||
enableX11 ? true,
|
||||
enableWayland ? true,
|
||||
}:
|
||||
lib.makeLibraryPath (import ./build-inputs.nix {
|
||||
inherit pkgs lib stdenv enableX11 enableWayland;
|
||||
})
|
30
nix/build-support/update-mirror.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# This script generates a directory that can be uploaded to blob
|
||||
# storage to mirror our dependencies. The dependencies are unmodified
|
||||
# so their checksum and content hashes will match.
|
||||
|
||||
set -e # Exit immediately if a command exits with a non-zero status
|
||||
|
||||
SCRIPT_PATH="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
|
||||
INPUT_FILE="$SCRIPT_PATH/../../build.zig.zon2json-lock"
|
||||
OUTPUT_DIR="blob"
|
||||
|
||||
# Ensure the output directory exists
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# Use jq to iterate over the JSON and download files
|
||||
jq -r 'to_entries[] | "\(.key) \(.value.name) \(.value.url)"' "$INPUT_FILE" | while read -r key name url; do
|
||||
# Skip URLs that don't start with http(s). They aren't necessary for
|
||||
# our mirror.
|
||||
if ! echo "$url" | grep -Eq "^https?://"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Extract the file extension from the URL
|
||||
extension=$(echo "$url" | grep -oE '\.[a-z0-9]+(\.[a-z0-9]+)?$')
|
||||
|
||||
filename="${name}-${key}${extension}"
|
||||
echo "$url -> $filename"
|
||||
curl -L -o "$OUTPUT_DIR/$filename" "$url"
|
||||
done
|
@@ -14,6 +14,7 @@
|
||||
python3,
|
||||
qemu,
|
||||
scdoc,
|
||||
snapcraft,
|
||||
valgrind,
|
||||
#, vulkan-loader # unused
|
||||
vttest,
|
||||
@@ -30,12 +31,16 @@
|
||||
glib,
|
||||
glslang,
|
||||
gtk4,
|
||||
gtk4-layer-shell,
|
||||
gobject-introspection,
|
||||
libadwaita,
|
||||
blueprint-compiler,
|
||||
gettext,
|
||||
adwaita-icon-theme,
|
||||
hicolor-icon-theme,
|
||||
harfbuzz,
|
||||
libpng,
|
||||
libGL,
|
||||
libxkbcommon,
|
||||
libX11,
|
||||
libXcursor,
|
||||
libXext,
|
||||
@@ -47,6 +52,7 @@
|
||||
simdutf,
|
||||
zlib,
|
||||
alejandra,
|
||||
jq,
|
||||
minisign,
|
||||
pandoc,
|
||||
hyperfine,
|
||||
@@ -54,44 +60,24 @@
|
||||
wayland,
|
||||
wayland-scanner,
|
||||
wayland-protocols,
|
||||
zon2nix,
|
||||
system,
|
||||
pkgs,
|
||||
}: let
|
||||
# See package.nix. Keep in sync.
|
||||
rpathLibs =
|
||||
[
|
||||
libGL
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [
|
||||
bzip2
|
||||
expat
|
||||
fontconfig
|
||||
freetype
|
||||
harfbuzz
|
||||
libpng
|
||||
libxml2
|
||||
oniguruma
|
||||
simdutf
|
||||
zlib
|
||||
|
||||
glslang
|
||||
spirv-cross
|
||||
|
||||
libX11
|
||||
libXcursor
|
||||
libXi
|
||||
libXrandr
|
||||
|
||||
libadwaita
|
||||
gtk4
|
||||
glib
|
||||
wayland
|
||||
];
|
||||
ld_library_path = import ./build-support/ld-library-path.nix {
|
||||
inherit pkgs lib stdenv;
|
||||
};
|
||||
gi_typelib_path = import ./build-support/gi-typelib-path.nix {
|
||||
inherit pkgs lib stdenv;
|
||||
};
|
||||
in
|
||||
mkShell {
|
||||
name = "ghostty";
|
||||
|
||||
packages =
|
||||
[
|
||||
# For builds
|
||||
jq
|
||||
llvmPackages_latest.llvm
|
||||
minisign
|
||||
ncurses
|
||||
@@ -100,6 +86,7 @@ in
|
||||
scdoc
|
||||
zig
|
||||
zip
|
||||
zon2nix.packages.${system}.zon2nix
|
||||
|
||||
# For web and wasm stuff
|
||||
nodejs
|
||||
@@ -118,6 +105,15 @@ in
|
||||
# wasm
|
||||
wabt
|
||||
wasmtime
|
||||
|
||||
# Localization
|
||||
gettext
|
||||
|
||||
# We need these GTK-related deps on all platform so we can build
|
||||
# dist tarballs.
|
||||
blueprint-compiler
|
||||
libadwaita
|
||||
gtk4
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [
|
||||
# My nix shell environment installs the non-interactive version
|
||||
@@ -129,6 +125,7 @@ in
|
||||
qemu
|
||||
|
||||
gdb
|
||||
snapcraft
|
||||
valgrind
|
||||
wraptest
|
||||
|
||||
@@ -146,6 +143,7 @@ in
|
||||
glslang
|
||||
spirv-cross
|
||||
|
||||
libxkbcommon
|
||||
libX11
|
||||
libXcursor
|
||||
libXext
|
||||
@@ -154,9 +152,9 @@ in
|
||||
libXrandr
|
||||
|
||||
# Only needed for GTK builds
|
||||
libadwaita
|
||||
gtk4
|
||||
gtk4-layer-shell
|
||||
glib
|
||||
gobject-introspection
|
||||
wayland
|
||||
wayland-scanner
|
||||
wayland-protocols
|
||||
@@ -164,7 +162,8 @@ in
|
||||
|
||||
# This should be set onto the rpath of the ghostty binary if you want
|
||||
# it to be "portable" across the system.
|
||||
LD_LIBRARY_PATH = lib.makeLibraryPath rpathLibs;
|
||||
LD_LIBRARY_PATH = ld_library_path;
|
||||
GI_TYPELIB_PATH = gi_typelib_path;
|
||||
|
||||
shellHook =
|
||||
(lib.optionalString stdenv.hostPlatform.isLinux ''
|
||||
@@ -181,5 +180,9 @@ in
|
||||
# and we need iOS too.
|
||||
unset SDKROOT
|
||||
unset DEVELOPER_DIR
|
||||
|
||||
# We need to remove "xcrun" from the PATH. It is injected by
|
||||
# some dependency but we need to rely on system Xcode tools
|
||||
export PATH=$(echo "$PATH" | awk -v RS=: -v ORS=: '$0 !~ /xcrun/ || $0 == "/usr/bin" {print}' | sed 's/:$//')
|
||||
'');
|
||||
}
|
||||
|
164
nix/package.nix
@@ -1,36 +1,24 @@
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
bzip2,
|
||||
expat,
|
||||
fontconfig,
|
||||
freetype,
|
||||
harfbuzz,
|
||||
libpng,
|
||||
oniguruma,
|
||||
zlib,
|
||||
libGL,
|
||||
glib,
|
||||
gtk4,
|
||||
libadwaita,
|
||||
callPackage,
|
||||
gobject-introspection,
|
||||
blueprint-compiler,
|
||||
libxml2,
|
||||
gettext,
|
||||
wrapGAppsHook4,
|
||||
gsettings-desktop-schemas,
|
||||
git,
|
||||
ncurses,
|
||||
pkg-config,
|
||||
zig_0_13,
|
||||
zig_0_14,
|
||||
pandoc,
|
||||
revision ? "dirty",
|
||||
optimize ? "Debug",
|
||||
enableX11 ? true,
|
||||
libX11,
|
||||
libXcursor,
|
||||
libXi,
|
||||
libXrandr,
|
||||
enableWayland ? true,
|
||||
wayland,
|
||||
wayland-protocols,
|
||||
wayland-scanner,
|
||||
pkgs,
|
||||
}: let
|
||||
# The Zig hook has no way to select the release type without actual
|
||||
# overriding of the default flags.
|
||||
@@ -39,83 +27,44 @@
|
||||
# https://github.com/ziglang/zig/issues/14281#issuecomment-1624220653 is
|
||||
# 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_hook = zig_0_14.hook.overrideAttrs {
|
||||
zig_default_flags = "-Dcpu=baseline -Doptimize=${optimize} --color off";
|
||||
};
|
||||
|
||||
# We limit source like this to try and reduce the amount of rebuilds as possible
|
||||
# thus we only provide the source that is needed for the build
|
||||
#
|
||||
# NOTE: as of the current moment only linux files are provided,
|
||||
# since darwin support is not finished
|
||||
src = lib.fileset.toSource {
|
||||
root = ../.;
|
||||
fileset = lib.fileset.intersection (lib.fileset.fromSource (lib.sources.cleanSource ../.)) (
|
||||
lib.fileset.unions [
|
||||
../dist/linux
|
||||
../images
|
||||
../include
|
||||
../pkg
|
||||
../src
|
||||
../vendor
|
||||
../build.zig
|
||||
../build.zig.zon
|
||||
./build-support/fetch-zig-cache.sh
|
||||
]
|
||||
);
|
||||
gi_typelib_path = import ./build-support/gi-typelib-path.nix {
|
||||
inherit pkgs lib stdenv;
|
||||
};
|
||||
|
||||
# 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;
|
||||
buildInputs = import ./build-support/build-inputs.nix {
|
||||
inherit pkgs lib stdenv enableX11 enableWayland;
|
||||
};
|
||||
in
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "ghostty";
|
||||
version = "1.1.0";
|
||||
inherit src;
|
||||
version = "1.1.3";
|
||||
|
||||
# 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
|
||||
../po
|
||||
../pkg
|
||||
../src
|
||||
../vendor
|
||||
../build.zig
|
||||
../build.zig.zon
|
||||
../build.zig.zon.nix
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
deps = callPackage ../build.zig.zon.nix {name = "ghostty-cache-${finalAttrs.version}";};
|
||||
|
||||
nativeBuildInputs =
|
||||
[
|
||||
@@ -124,47 +73,26 @@ in
|
||||
pandoc
|
||||
pkg-config
|
||||
zig_hook
|
||||
gobject-introspection
|
||||
wrapGAppsHook4
|
||||
blueprint-compiler
|
||||
libxml2 # for xmllint
|
||||
gettext
|
||||
]
|
||||
++ lib.optionals enableWayland [
|
||||
wayland-scanner
|
||||
wayland-protocols
|
||||
];
|
||||
|
||||
buildInputs =
|
||||
[
|
||||
libGL
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [
|
||||
bzip2
|
||||
expat
|
||||
fontconfig
|
||||
freetype
|
||||
harfbuzz
|
||||
libpng
|
||||
oniguruma
|
||||
zlib
|
||||
|
||||
libadwaita
|
||||
gtk4
|
||||
glib
|
||||
gsettings-desktop-schemas
|
||||
]
|
||||
++ lib.optionals enableX11 [
|
||||
libX11
|
||||
libXcursor
|
||||
libXi
|
||||
libXrandr
|
||||
]
|
||||
++ lib.optionals enableWayland [
|
||||
wayland
|
||||
];
|
||||
buildInputs = buildInputs;
|
||||
|
||||
dontConfigure = true;
|
||||
|
||||
GI_TYPELIB_PATH = gi_typelib_path;
|
||||
|
||||
zigBuildFlags = [
|
||||
"--system"
|
||||
"${zigCache}/p"
|
||||
"${finalAttrs.deps}"
|
||||
"-Dversion-string=${finalAttrs.version}-${revision}-nix"
|
||||
"-Dgtk-x11=${lib.boolToString enableX11}"
|
||||
"-Dgtk-wayland=${lib.boolToString enableWayland}"
|
||||
|
@@ -1,3 +1,3 @@
|
||||
# This file is auto-generated! check build-support/check-zig-cache-hash.sh for
|
||||
# more details.
|
||||
"sha256-Bjy31evaKgpRX1mGwAFkai44eiiorTV1gW3VdP9Ins8="
|
||||
"sha256-S8kS+gO17dl9LJGKL1+kgDUre+vPTmdTvXzgc585Fl8="
|
||||
|
@@ -1,5 +1,7 @@
|
||||
.{
|
||||
.name = "apple-sdk",
|
||||
.name = .apple_sdk,
|
||||
.version = "0.1.0",
|
||||
.dependencies = .{},
|
||||
.fingerprint = 0xdde52860f7c464d2,
|
||||
.paths = .{""},
|
||||
}
|
||||
|
@@ -4,85 +4,85 @@ pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const upstream = b.dependency("breakpad", .{});
|
||||
|
||||
const lib = b.addStaticLibrary(.{
|
||||
.name = "breakpad",
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
lib.linkLibCpp();
|
||||
lib.addIncludePath(upstream.path("src"));
|
||||
lib.addIncludePath(b.path("vendor"));
|
||||
if (target.result.isDarwin()) {
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
const apple_sdk = @import("apple_sdk");
|
||||
try apple_sdk.addPaths(b, &lib.root_module);
|
||||
try apple_sdk.addPaths(b, lib.root_module);
|
||||
}
|
||||
|
||||
var flags = std.ArrayList([]const u8).init(b.allocator);
|
||||
defer flags.deinit();
|
||||
try flags.appendSlice(&.{});
|
||||
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = common,
|
||||
.flags = flags.items,
|
||||
});
|
||||
|
||||
if (target.result.isDarwin()) {
|
||||
if (b.lazyDependency("breakpad", .{})) |upstream| {
|
||||
lib.addIncludePath(upstream.path("src"));
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = common_apple,
|
||||
.flags = flags.items,
|
||||
});
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = client_apple,
|
||||
.files = common,
|
||||
.flags = flags.items,
|
||||
});
|
||||
|
||||
switch (target.result.os.tag) {
|
||||
.macos => {
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = common_mac,
|
||||
.flags = flags.items,
|
||||
});
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = client_mac,
|
||||
.flags = flags.items,
|
||||
});
|
||||
},
|
||||
|
||||
.ios => lib.addCSourceFiles(.{
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = client_ios,
|
||||
.files = common_apple,
|
||||
.flags = flags.items,
|
||||
}),
|
||||
});
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = client_apple,
|
||||
.flags = flags.items,
|
||||
});
|
||||
|
||||
else => {},
|
||||
switch (target.result.os.tag) {
|
||||
.macos => {
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = common_mac,
|
||||
.flags = flags.items,
|
||||
});
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = client_mac,
|
||||
.flags = flags.items,
|
||||
});
|
||||
},
|
||||
|
||||
.ios => lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = client_ios,
|
||||
.flags = flags.items,
|
||||
}),
|
||||
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (target.result.os.tag == .linux) {
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = common_linux,
|
||||
.flags = flags.items,
|
||||
});
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = client_linux,
|
||||
.flags = flags.items,
|
||||
});
|
||||
}
|
||||
if (target.result.os.tag == .linux) {
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = common_linux,
|
||||
.flags = flags.items,
|
||||
});
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = client_linux,
|
||||
.flags = flags.items,
|
||||
});
|
||||
}
|
||||
|
||||
lib.installHeadersDirectory(
|
||||
upstream.path("src"),
|
||||
"",
|
||||
.{ .include_extensions = &.{".h"} },
|
||||
);
|
||||
lib.installHeadersDirectory(
|
||||
upstream.path("src"),
|
||||
"",
|
||||
.{ .include_extensions = &.{".h"} },
|
||||
);
|
||||
}
|
||||
|
||||
b.installArtifact(lib);
|
||||
}
|
||||
|
@@ -1,11 +1,13 @@
|
||||
.{
|
||||
.name = "breakpad",
|
||||
.name = .breakpad,
|
||||
.version = "0.1.0",
|
||||
.fingerprint = 0xfe9f9e4c76d5f962,
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.breakpad = .{
|
||||
.url = "https://github.com/getsentry/breakpad/archive/b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz",
|
||||
.hash = "12207fd37bb8251919c112dcdd8f616a491857b34a451f7e4486490077206dc2a1ea",
|
||||
.url = "https://deps.files.ghostty.org/breakpad-b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz",
|
||||
.hash = "N-V-__8AALw2uwF_03u4JRkZwRLc3Y9hakkYV7NKRR9-RIZJ",
|
||||
.lazy = true,
|
||||
},
|
||||
|
||||
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||
|
@@ -40,7 +40,13 @@ pub fn build(b: *std.Build) !void {
|
||||
.@"enable-libpng" = true,
|
||||
});
|
||||
lib.linkLibrary(freetype.artifact("freetype"));
|
||||
module.addIncludePath(freetype.builder.dependency("freetype", .{}).path("include"));
|
||||
|
||||
if (freetype.builder.lazyDependency(
|
||||
"freetype",
|
||||
.{},
|
||||
)) |freetype_dep| {
|
||||
module.addIncludePath(freetype_dep.path("include"));
|
||||
}
|
||||
}
|
||||
|
||||
lib.addIncludePath(imgui.path(""));
|
||||
@@ -76,9 +82,9 @@ pub fn build(b: *std.Build) !void {
|
||||
.flags = flags.items,
|
||||
});
|
||||
|
||||
if (target.result.isDarwin()) {
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
if (!target.query.isNative()) {
|
||||
try @import("apple_sdk").addPaths(b, &lib.root_module);
|
||||
try @import("apple_sdk").addPaths(b, lib.root_module);
|
||||
try @import("apple_sdk").addPaths(b, module);
|
||||
}
|
||||
lib.addCSourceFile(.{
|
||||
|
@@ -1,13 +1,15 @@
|
||||
.{
|
||||
.name = "cimgui",
|
||||
.name = .cimgui,
|
||||
.version = "1.90.6", // -docking branch
|
||||
.fingerprint = 0x49726f5f8acbc90d,
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
// This should be kept in sync with the submodule in the cimgui source
|
||||
// code in ./vendor/ to be safe that they're compatible.
|
||||
.imgui = .{
|
||||
.url = "https://github.com/ocornut/imgui/archive/e391fe2e66eb1c96b1624ae8444dc64c23146ef4.tar.gz",
|
||||
.hash = "1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402",
|
||||
// ocornut/imgui
|
||||
.url = "https://deps.files.ghostty.org/imgui-1220bc6b9daceaf7c8c60f3c3998058045ba0c5c5f48ae255ff97776d9cd8bfc6402.tar.gz",
|
||||
.hash = "N-V-__8AAH0GaQC8a52s6vfIxg88OZgFgEW6DFxfSK4lX_l3",
|
||||
},
|
||||
|
||||
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||
|
@@ -64,7 +64,6 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
|
||||
const libxml2_iconv_enabled = options.libxml2_iconv_enabled;
|
||||
const freetype_enabled = options.freetype_enabled;
|
||||
|
||||
const upstream = b.dependency("fontconfig", .{});
|
||||
const lib = b.addStaticLibrary(.{
|
||||
.name = "fontconfig",
|
||||
.target = target,
|
||||
@@ -75,9 +74,7 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
|
||||
lib.linkSystemLibrary("pthread");
|
||||
}
|
||||
|
||||
lib.addIncludePath(upstream.path(""));
|
||||
lib.addIncludePath(b.path("override/include"));
|
||||
module.addIncludePath(upstream.path(""));
|
||||
module.addIncludePath(b.path("override/include"));
|
||||
|
||||
var flags = std.ArrayList([]const u8).init(b.allocator);
|
||||
@@ -188,11 +185,12 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
|
||||
if (b.systemIntegrationOption("freetype", .{})) {
|
||||
lib.linkSystemLibrary2("freetype2", dynamic_link_opts);
|
||||
} else {
|
||||
const freetype_dep = b.dependency(
|
||||
if (b.lazyDependency(
|
||||
"freetype",
|
||||
.{ .target = target, .optimize = optimize },
|
||||
);
|
||||
lib.linkLibrary(freetype_dep.artifact("freetype"));
|
||||
)) |freetype_dep| {
|
||||
lib.linkLibrary(freetype_dep.artifact("freetype"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,26 +212,31 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
|
||||
if (b.systemIntegrationOption("libxml2", .{})) {
|
||||
lib.linkSystemLibrary2("libxml-2.0", dynamic_link_opts);
|
||||
} else {
|
||||
const libxml2_dep = b.dependency("libxml2", .{
|
||||
if (b.lazyDependency("libxml2", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.iconv = libxml2_iconv_enabled,
|
||||
});
|
||||
lib.linkLibrary(libxml2_dep.artifact("xml2"));
|
||||
})) |libxml2_dep| {
|
||||
lib.linkLibrary(libxml2_dep.artifact("xml2"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = srcs,
|
||||
.flags = flags.items,
|
||||
});
|
||||
if (b.lazyDependency("fontconfig", .{})) |upstream| {
|
||||
lib.addIncludePath(upstream.path(""));
|
||||
module.addIncludePath(upstream.path(""));
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = srcs,
|
||||
.flags = flags.items,
|
||||
});
|
||||
|
||||
lib.installHeadersDirectory(
|
||||
upstream.path("fontconfig"),
|
||||
"fontconfig",
|
||||
.{ .include_extensions = &.{".h"} },
|
||||
);
|
||||
lib.installHeadersDirectory(
|
||||
upstream.path("fontconfig"),
|
||||
"fontconfig",
|
||||
.{ .include_extensions = &.{".h"} },
|
||||
);
|
||||
}
|
||||
|
||||
b.installArtifact(lib);
|
||||
|
||||
|
@@ -1,13 +1,16 @@
|
||||
.{
|
||||
.name = "fontconfig",
|
||||
.name = .fontconfig,
|
||||
.version = "2.14.2",
|
||||
.fingerprint = 0x4a79a5a40c6d6d8,
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.fontconfig = .{
|
||||
.url = "https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz",
|
||||
.hash = "12201149afb3326c56c05bb0a577f54f76ac20deece63aa2f5cd6ff31a4fa4fcb3b7",
|
||||
.hash = "N-V-__8AAIrfdwARSa-zMmxWwFuwpXf1T3asIN7s5jqi9c1v",
|
||||
.lazy = true,
|
||||
},
|
||||
|
||||
.freetype = .{ .path = "../freetype" },
|
||||
.libxml2 = .{ .path = "../libxml2" },
|
||||
.freetype = .{ .path = "../freetype", .lazy = true },
|
||||
.libxml2 = .{ .path = "../libxml2", .lazy = true },
|
||||
},
|
||||
}
|
||||
|
@@ -84,7 +84,7 @@ pub const Property = enum {
|
||||
|
||||
pub fn cval(self: Property) [:0]const u8 {
|
||||
@setEvalBranchQuota(10_000);
|
||||
inline for (@typeInfo(Property).Enum.fields) |field| {
|
||||
inline for (@typeInfo(Property).@"enum".fields) |field| {
|
||||
if (self == @field(Property, field.name)) {
|
||||
// Build our string in a comptime context so it is a binary
|
||||
// constant and not stack allocated.
|
||||
|
@@ -61,20 +61,17 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
|
||||
|
||||
const libpng_enabled = options.libpng_enabled;
|
||||
|
||||
const upstream = b.dependency("freetype", .{});
|
||||
const lib = b.addStaticLibrary(.{
|
||||
.name = "freetype",
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
lib.linkLibC();
|
||||
lib.addIncludePath(upstream.path("include"));
|
||||
if (target.result.isDarwin()) {
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
const apple_sdk = @import("apple_sdk");
|
||||
try apple_sdk.addPaths(b, &lib.root_module);
|
||||
try apple_sdk.addPaths(b, lib.root_module);
|
||||
}
|
||||
|
||||
module.addIncludePath(upstream.path("include"));
|
||||
var flags = std.ArrayList([]const u8).init(b.allocator);
|
||||
defer flags.deinit();
|
||||
try flags.appendSlice(&.{
|
||||
@@ -114,48 +111,52 @@ fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Bu
|
||||
}
|
||||
}
|
||||
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = srcs,
|
||||
.flags = flags.items,
|
||||
});
|
||||
if (b.lazyDependency("freetype", .{})) |upstream| {
|
||||
lib.addIncludePath(upstream.path("include"));
|
||||
module.addIncludePath(upstream.path("include"));
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = srcs,
|
||||
.flags = flags.items,
|
||||
});
|
||||
|
||||
switch (target.result.os.tag) {
|
||||
.linux => lib.addCSourceFile(.{
|
||||
.file = upstream.path("builds/unix/ftsystem.c"),
|
||||
.flags = flags.items,
|
||||
}),
|
||||
.windows => lib.addCSourceFile(.{
|
||||
.file = upstream.path("builds/windows/ftsystem.c"),
|
||||
.flags = flags.items,
|
||||
}),
|
||||
else => lib.addCSourceFile(.{
|
||||
.file = upstream.path("src/base/ftsystem.c"),
|
||||
.flags = flags.items,
|
||||
}),
|
||||
}
|
||||
switch (target.result.os.tag) {
|
||||
.windows => {
|
||||
lib.addCSourceFile(.{
|
||||
.file = upstream.path("builds/windows/ftdebug.c"),
|
||||
switch (target.result.os.tag) {
|
||||
.linux => lib.addCSourceFile(.{
|
||||
.file = upstream.path("builds/unix/ftsystem.c"),
|
||||
.flags = flags.items,
|
||||
});
|
||||
lib.addWin32ResourceFile(.{
|
||||
.file = upstream.path("src/base/ftver.rc"),
|
||||
});
|
||||
},
|
||||
else => lib.addCSourceFile(.{
|
||||
.file = upstream.path("src/base/ftdebug.c"),
|
||||
.flags = flags.items,
|
||||
}),
|
||||
}
|
||||
}),
|
||||
.windows => lib.addCSourceFile(.{
|
||||
.file = upstream.path("builds/windows/ftsystem.c"),
|
||||
.flags = flags.items,
|
||||
}),
|
||||
else => lib.addCSourceFile(.{
|
||||
.file = upstream.path("src/base/ftsystem.c"),
|
||||
.flags = flags.items,
|
||||
}),
|
||||
}
|
||||
switch (target.result.os.tag) {
|
||||
.windows => {
|
||||
lib.addCSourceFile(.{
|
||||
.file = upstream.path("builds/windows/ftdebug.c"),
|
||||
.flags = flags.items,
|
||||
});
|
||||
lib.addWin32ResourceFile(.{
|
||||
.file = upstream.path("src/base/ftver.rc"),
|
||||
});
|
||||
},
|
||||
else => lib.addCSourceFile(.{
|
||||
.file = upstream.path("src/base/ftdebug.c"),
|
||||
.flags = flags.items,
|
||||
}),
|
||||
}
|
||||
|
||||
lib.installHeader(b.path("freetype-zig.h"), "freetype-zig.h");
|
||||
lib.installHeadersDirectory(
|
||||
upstream.path("include"),
|
||||
"",
|
||||
.{ .include_extensions = &.{".h"} },
|
||||
);
|
||||
lib.installHeader(b.path("freetype-zig.h"), "freetype-zig.h");
|
||||
lib.installHeadersDirectory(
|
||||
upstream.path("include"),
|
||||
"",
|
||||
.{ .include_extensions = &.{".h"} },
|
||||
);
|
||||
}
|
||||
|
||||
b.installArtifact(lib);
|
||||
|
||||
|
@@ -1,11 +1,14 @@
|
||||
.{
|
||||
.name = "freetype",
|
||||
.name = .freetype,
|
||||
.version = "2.13.2",
|
||||
.fingerprint = 0xac2059b6f7bbfe0a,
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
// freetype/freetype
|
||||
.freetype = .{
|
||||
.url = "https://github.com/freetype/freetype/archive/refs/tags/VER-2-13-2.tar.gz",
|
||||
.hash = "1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d",
|
||||
.url = "https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz",
|
||||
.hash = "N-V-__8AAKLKpwC4H27Ps_0iL3bPkQb-z6ZVSrB-x_3EEkub",
|
||||
.lazy = true,
|
||||
},
|
||||
|
||||
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||
|
@@ -278,7 +278,9 @@ pub const LoadFlags = packed struct {
|
||||
color: bool = false,
|
||||
compute_metrics: bool = false,
|
||||
bitmap_metrics_only: bool = false,
|
||||
_padding2: u9 = 0,
|
||||
_padding2: u1 = 0,
|
||||
no_svg: bool = false,
|
||||
_padding3: u7 = 0,
|
||||
|
||||
test {
|
||||
// This must always be an i32 size so we can bitcast directly.
|
||||
|
209
pkg/glfw/Cursor.zig
Normal file
@@ -0,0 +1,209 @@
|
||||
//! Represents a cursor and provides facilities for setting cursor images.
|
||||
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
|
||||
const c = @import("c.zig").c;
|
||||
const Image = @import("Image.zig");
|
||||
|
||||
const internal_debug = @import("internal_debug.zig");
|
||||
|
||||
const Cursor = @This();
|
||||
|
||||
ptr: *c.GLFWcursor,
|
||||
|
||||
/// Standard system cursor shapes.
|
||||
///
|
||||
/// These are the standard cursor shapes that can be requested from the platform (window system).
|
||||
pub const Shape = enum(i32) {
|
||||
/// The regular arrow cursor shape.
|
||||
arrow = c.GLFW_ARROW_CURSOR,
|
||||
|
||||
/// The text input I-beam cursor shape.
|
||||
ibeam = c.GLFW_IBEAM_CURSOR,
|
||||
|
||||
/// The crosshair cursor shape.
|
||||
crosshair = c.GLFW_CROSSHAIR_CURSOR,
|
||||
|
||||
/// The pointing hand cursor shape.
|
||||
///
|
||||
/// NOTE: This supersedes the old `hand` enum.
|
||||
pointing_hand = c.GLFW_POINTING_HAND_CURSOR,
|
||||
|
||||
/// The horizontal resize/move arrow shape.
|
||||
///
|
||||
/// The horizontal resize/move arrow shape. This is usually a horizontal double-headed arrow.
|
||||
//
|
||||
// NOTE: This supersedes the old `hresize` enum.
|
||||
resize_ew = c.GLFW_RESIZE_EW_CURSOR,
|
||||
|
||||
/// The vertical resize/move arrow shape.
|
||||
///
|
||||
/// The vertical resize/move shape. This is usually a vertical double-headed arrow.
|
||||
///
|
||||
/// NOTE: This supersedes the old `vresize` enum.
|
||||
resize_ns = c.GLFW_RESIZE_NS_CURSOR,
|
||||
|
||||
/// The top-left to bottom-right diagonal resize/move arrow shape.
|
||||
///
|
||||
/// The top-left to bottom-right diagonal resize/move shape. This is usually a diagonal
|
||||
/// double-headed arrow.
|
||||
///
|
||||
/// macos: This shape is provided by a private system API and may fail CursorUnavailable in the
|
||||
/// future.
|
||||
///
|
||||
/// x11: This shape is provided by a newer standard not supported by all cursor themes.
|
||||
///
|
||||
/// wayland: This shape is provided by a newer standard not supported by all cursor themes.
|
||||
resize_nwse = c.GLFW_RESIZE_NWSE_CURSOR,
|
||||
|
||||
/// The top-right to bottom-left diagonal resize/move arrow shape.
|
||||
///
|
||||
/// The top-right to bottom-left diagonal resize/move shape. This is usually a diagonal
|
||||
/// double-headed arrow.
|
||||
///
|
||||
/// macos: This shape is provided by a private system API and may fail with CursorUnavailable
|
||||
/// in the future.
|
||||
///
|
||||
/// x11: This shape is provided by a newer standard not supported by all cursor themes.
|
||||
///
|
||||
/// wayland: This shape is provided by a newer standard not supported by all cursor themes.
|
||||
resize_nesw = c.GLFW_RESIZE_NESW_CURSOR,
|
||||
|
||||
/// The omni-directional resize/move cursor shape.
|
||||
///
|
||||
/// The omni-directional resize cursor/move shape. This is usually either a combined horizontal
|
||||
/// and vertical double-headed arrow or a grabbing hand.
|
||||
resize_all = c.GLFW_RESIZE_ALL_CURSOR,
|
||||
|
||||
/// The operation-not-allowed shape.
|
||||
///
|
||||
/// The operation-not-allowed shape. This is usually a circle with a diagonal line through it.
|
||||
///
|
||||
/// x11: This shape is provided by a newer standard not supported by all cursor themes.
|
||||
///
|
||||
/// wayland: This shape is provided by a newer standard not supported by all cursor themes.
|
||||
not_allowed = c.GLFW_NOT_ALLOWED_CURSOR,
|
||||
};
|
||||
|
||||
/// Creates a custom cursor.
|
||||
///
|
||||
/// Creates a new custom cursor image that can be set for a window with glfw.Cursor.set. The cursor
|
||||
/// can be destroyed with glfwCursor.destroy. Any remaining cursors are destroyed by glfw.terminate.
|
||||
///
|
||||
/// The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight bits per channel with
|
||||
/// the red channel first. They are arranged canonically as packed sequential rows, starting from
|
||||
/// the top-left corner.
|
||||
///
|
||||
/// The cursor hotspot is specified in pixels, relative to the upper-left corner of the cursor
|
||||
/// image. Like all other coordinate systems in GLFW, the X-axis points to the right and the Y-axis
|
||||
/// points down.
|
||||
///
|
||||
/// @param[in] image The desired cursor image.
|
||||
/// @param[in] xhot The desired x-coordinate, in pixels, of the cursor hotspot.
|
||||
/// @param[in] yhot The desired y-coordinate, in pixels, of the cursor hotspot.
|
||||
/// @return The handle of the created cursor.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.PlatformError and glfw.ErrorCode.InvalidValue
|
||||
/// null is returned in the event of an error.
|
||||
///
|
||||
/// @pointer_lifetime The specified image data is copied before this function returns.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: cursor_object, glfw.Cursor.destroy, glfw.Cursor.createStandard
|
||||
pub inline fn create(image: Image, xhot: i32, yhot: i32) ?Cursor {
|
||||
internal_debug.assertInitialized();
|
||||
const img = image.toC();
|
||||
if (c.glfwCreateCursor(&img, @as(c_int, @intCast(xhot)), @as(c_int, @intCast(yhot)))) |cursor| return Cursor{ .ptr = cursor };
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Creates a cursor with a standard shape.
|
||||
///
|
||||
/// Returns a cursor with a standard shape, that can be set for a window with glfw.Window.setCursor.
|
||||
/// The images for these cursors come from the system cursor theme and their exact appearance will
|
||||
/// vary between platforms.
|
||||
///
|
||||
/// Most of these shapes are guaranteed to exist on every supported platform but a few may not be
|
||||
/// present. See the table below for details.
|
||||
///
|
||||
/// | Cursor shape | Windows | macOS | X11 | Wayland |
|
||||
/// |------------------|---------|-----------------|-------------------|-------------------|
|
||||
/// | `.arrow` | Yes | Yes | Yes | Yes |
|
||||
/// | `.ibeam` | Yes | Yes | Yes | Yes |
|
||||
/// | `.crosshair` | Yes | Yes | Yes | Yes |
|
||||
/// | `.pointing_hand` | Yes | Yes | Yes | Yes |
|
||||
/// | `.resize_ew` | Yes | Yes | Yes | Yes |
|
||||
/// | `.resize_ns` | Yes | Yes | Yes | Yes |
|
||||
/// | `.resize_nwse` | Yes | Yes<sup>1</sup> | Maybe<sup>2</sup> | Maybe<sup>2</sup> |
|
||||
/// | `.resize_nesw` | Yes | Yes<sup>1</sup> | Maybe<sup>2</sup> | Maybe<sup>2</sup> |
|
||||
/// | `.resize_all` | Yes | Yes | Yes | Yes |
|
||||
/// | `.not_allowed` | Yes | Yes | Maybe<sup>2</sup> | Maybe<sup>2</sup> |
|
||||
///
|
||||
/// 1. This uses a private system API and may fail in the future.
|
||||
/// 2. This uses a newer standard that not all cursor themes support.
|
||||
///
|
||||
/// If the requested shape is not available, this function emits a CursorUnavailable error
|
||||
/// Possible errors include glfw.ErrorCode.PlatformError and glfw.ErrorCode.CursorUnavailable.
|
||||
/// null is returned in the event of an error.
|
||||
///
|
||||
/// thread_safety: This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: cursor_object, glfwCreateCursor
|
||||
pub inline fn createStandard(shape: Shape) ?Cursor {
|
||||
internal_debug.assertInitialized();
|
||||
if (c.glfwCreateStandardCursor(@as(c_int, @intCast(@intFromEnum(shape))))) |cursor| return Cursor{ .ptr = cursor };
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Destroys a cursor.
|
||||
///
|
||||
/// This function destroys a cursor previously created with glfw.Cursor.create. Any remaining
|
||||
/// cursors will be destroyed by glfw.terminate.
|
||||
///
|
||||
/// If the specified cursor is current for any window, that window will be reverted to the default
|
||||
/// cursor. This does not affect the cursor mode.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.PlatformError.
|
||||
///
|
||||
/// @reentrancy This function must not be called from a callback.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: cursor_object, glfw.createCursor
|
||||
pub inline fn destroy(self: Cursor) void {
|
||||
internal_debug.assertInitialized();
|
||||
c.glfwDestroyCursor(self.ptr);
|
||||
}
|
||||
|
||||
test "create" {
|
||||
const allocator = testing.allocator;
|
||||
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const image = try Image.init(allocator, 32, 32, 32 * 32 * 4);
|
||||
defer image.deinit(allocator);
|
||||
|
||||
const cursor = glfw.Cursor.create(image, 0, 0);
|
||||
if (cursor) |cur| cur.destroy();
|
||||
}
|
||||
|
||||
test "createStandard" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const cursor = glfw.Cursor.createStandard(.ibeam);
|
||||
if (cursor) |cur| cur.destroy();
|
||||
}
|
74
pkg/glfw/GammaRamp.zig
Normal file
@@ -0,0 +1,74 @@
|
||||
//! Gamma ramp for monitors and related functions.
|
||||
//!
|
||||
//! It may be .owned (e.g. in the case of a gamma ramp initialized by you for passing into
|
||||
//! glfw.Monitor.setGammaRamp) or not .owned (e.g. in the case of one gotten via
|
||||
//! glfw.Monitor.getGammaRamp.) If it is .owned, deinit should be called to free the memory. It is
|
||||
//! safe to call deinit even if not .owned.
|
||||
//!
|
||||
//! see also: monitor_gamma, glfw.Monitor.getGammaRamp
|
||||
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const mem = std.mem;
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
const GammaRamp = @This();
|
||||
|
||||
red: []u16,
|
||||
green: []u16,
|
||||
blue: []u16,
|
||||
owned: ?[]u16,
|
||||
|
||||
/// Initializes a new owned gamma ramp with the given array size and undefined values.
|
||||
///
|
||||
/// see also: glfw.Monitor.getGammaRamp
|
||||
pub inline fn init(allocator: mem.Allocator, size: usize) !GammaRamp {
|
||||
const buf = try allocator.alloc(u16, size * 3);
|
||||
return GammaRamp{
|
||||
.red = buf[size * 0 .. (size * 0) + size],
|
||||
.green = buf[size * 1 .. (size * 1) + size],
|
||||
.blue = buf[size * 2 .. (size * 2) + size],
|
||||
.owned = buf,
|
||||
};
|
||||
}
|
||||
|
||||
/// Turns a GLFW / C gamma ramp into the nicer Zig type, and sets `.owned = false`.
|
||||
///
|
||||
/// The returned memory is valid for as long as the GLFW C memory is valid.
|
||||
pub inline fn fromC(native: c.GLFWgammaramp) GammaRamp {
|
||||
return GammaRamp{
|
||||
.red = native.red[0..native.size],
|
||||
.green = native.green[0..native.size],
|
||||
.blue = native.blue[0..native.size],
|
||||
.owned = null,
|
||||
};
|
||||
}
|
||||
|
||||
/// Turns the nicer Zig type into a GLFW / C gamma ramp, for passing into GLFW C functions.
|
||||
///
|
||||
/// The returned memory is valid for as long as the Zig memory is valid.
|
||||
pub inline fn toC(self: GammaRamp) c.GLFWgammaramp {
|
||||
std.debug.assert(self.red.len == self.green.len);
|
||||
std.debug.assert(self.red.len == self.blue.len);
|
||||
return c.GLFWgammaramp{
|
||||
.red = &self.red[0],
|
||||
.green = &self.green[0],
|
||||
.blue = &self.blue[0],
|
||||
.size = @as(c_uint, @intCast(self.red.len)),
|
||||
};
|
||||
}
|
||||
|
||||
/// Deinitializes the memory using the allocator iff `.owned = true`.
|
||||
pub inline fn deinit(self: GammaRamp, allocator: mem.Allocator) void {
|
||||
if (self.owned) |buf| allocator.free(buf);
|
||||
}
|
||||
|
||||
test "conversion" {
|
||||
const allocator = testing.allocator;
|
||||
|
||||
const ramp = try GammaRamp.init(allocator, 256);
|
||||
defer ramp.deinit(allocator);
|
||||
|
||||
const glfw = ramp.toC();
|
||||
_ = GammaRamp.fromC(glfw);
|
||||
}
|
82
pkg/glfw/Image.zig
Normal file
@@ -0,0 +1,82 @@
|
||||
//! Image data
|
||||
//!
|
||||
//!
|
||||
//! This describes a single 2D image. See the documentation for each related function what the
|
||||
//! expected pixel format is.
|
||||
//!
|
||||
//! see also: cursor_custom, window_icon
|
||||
//!
|
||||
//! It may be .owned (e.g. in the case of an image initialized by you for passing into glfw) or not
|
||||
//! .owned (e.g. in the case of one gotten via glfw) If it is .owned, deinit should be called to
|
||||
//! free the memory. It is safe to call deinit even if not .owned.
|
||||
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const mem = std.mem;
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
const Image = @This();
|
||||
|
||||
/// The width of this image, in pixels.
|
||||
width: u32,
|
||||
|
||||
/// The height of this image, in pixels.
|
||||
height: u32,
|
||||
|
||||
/// The pixel data of this image, arranged left-to-right, top-to-bottom.
|
||||
pixels: []u8,
|
||||
|
||||
/// Whether or not the pixels data is owned by you (true) or GLFW (false).
|
||||
owned: bool,
|
||||
|
||||
/// Initializes a new owned image with the given size and pixel_data_len of undefined .pixel values.
|
||||
pub inline fn init(allocator: mem.Allocator, width: u32, height: u32, pixel_data_len: usize) !Image {
|
||||
const buf = try allocator.alloc(u8, pixel_data_len);
|
||||
return Image{
|
||||
.width = width,
|
||||
.height = height,
|
||||
.pixels = buf,
|
||||
.owned = true,
|
||||
};
|
||||
}
|
||||
|
||||
/// Turns a GLFW / C image into the nicer Zig type, and sets `.owned = false`.
|
||||
///
|
||||
/// The length of pixel data must be supplied, as GLFW's image type does not itself describe the
|
||||
/// number of bytes required per pixel / the length of the pixel data array.
|
||||
///
|
||||
/// The returned memory is valid for as long as the GLFW C memory is valid.
|
||||
pub inline fn fromC(native: c.GLFWimage, pixel_data_len: usize) Image {
|
||||
return Image{
|
||||
.width = @as(u32, @intCast(native.width)),
|
||||
.height = @as(u32, @intCast(native.height)),
|
||||
.pixels = native.pixels[0..pixel_data_len],
|
||||
.owned = false,
|
||||
};
|
||||
}
|
||||
|
||||
/// Turns the nicer Zig type into a GLFW / C image, for passing into GLFW C functions.
|
||||
///
|
||||
/// The returned memory is valid for as long as the Zig memory is valid.
|
||||
pub inline fn toC(self: Image) c.GLFWimage {
|
||||
return c.GLFWimage{
|
||||
.width = @as(c_int, @intCast(self.width)),
|
||||
.height = @as(c_int, @intCast(self.height)),
|
||||
.pixels = &self.pixels[0],
|
||||
};
|
||||
}
|
||||
|
||||
/// Deinitializes the memory using the allocator iff `.owned = true`.
|
||||
pub inline fn deinit(self: Image, allocator: mem.Allocator) void {
|
||||
if (self.owned) allocator.free(self.pixels);
|
||||
}
|
||||
|
||||
test "conversion" {
|
||||
const allocator = testing.allocator;
|
||||
|
||||
const image = try Image.init(allocator, 256, 256, 256 * 256 * 4);
|
||||
defer image.deinit(allocator);
|
||||
|
||||
const glfw = image.toC();
|
||||
_ = Image.fromC(glfw, image.width * image.height * 4);
|
||||
}
|
642
pkg/glfw/Joystick.zig
Normal file
@@ -0,0 +1,642 @@
|
||||
//! Represents a Joystick or gamepad
|
||||
//!
|
||||
//! It can be manually crafted via e.g. `glfw.Joystick{.jid = .one}`, but more
|
||||
//! typically you'll want to discover the joystick using `glfw.Joystick.setCallback`.
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
const c = @import("c.zig").c;
|
||||
const Window = @import("Window.zig");
|
||||
const Action = @import("action.zig").Action;
|
||||
const GamepadAxis = @import("gamepad_axis.zig").GamepadAxis;
|
||||
const GamepadButton = @import("gamepad_button.zig").GamepadButton;
|
||||
const Hat = @import("hat.zig").Hat;
|
||||
|
||||
const internal_debug = @import("internal_debug.zig");
|
||||
|
||||
const Joystick = @This();
|
||||
|
||||
/// The GLFW joystick ID.
|
||||
jid: Id,
|
||||
|
||||
/// Joystick IDs.
|
||||
///
|
||||
/// See glfw.Joystick.setCallback for how these are used.
|
||||
pub const Id = enum(c_int) {
|
||||
one = c.GLFW_JOYSTICK_1,
|
||||
two = c.GLFW_JOYSTICK_2,
|
||||
three = c.GLFW_JOYSTICK_3,
|
||||
four = c.GLFW_JOYSTICK_4,
|
||||
five = c.GLFW_JOYSTICK_5,
|
||||
six = c.GLFW_JOYSTICK_6,
|
||||
seven = c.GLFW_JOYSTICK_7,
|
||||
eight = c.GLFW_JOYSTICK_8,
|
||||
nine = c.GLFW_JOYSTICK_9,
|
||||
ten = c.GLFW_JOYSTICK_10,
|
||||
eleven = c.GLFW_JOYSTICK_11,
|
||||
twelve = c.GLFW_JOYSTICK_12,
|
||||
thirteen = c.GLFW_JOYSTICK_13,
|
||||
fourteen = c.GLFW_JOYSTICK_14,
|
||||
fifteen = c.GLFW_JOYSTICK_15,
|
||||
sixteen = c.GLFW_JOYSTICK_16,
|
||||
pub const last = @as(@This(), @enumFromInt(c.GLFW_JOYSTICK_LAST));
|
||||
};
|
||||
|
||||
/// Gamepad input state
|
||||
///
|
||||
/// This describes the input state of a gamepad.
|
||||
///
|
||||
/// see also: gamepad, glfwGetGamepadState
|
||||
const GamepadState = extern struct {
|
||||
/// The states of each gamepad button (see gamepad_buttons), `glfw.Action.press` or `glfw.Action.release`.
|
||||
///
|
||||
/// Use the enumeration helper e.g. `.getButton(.dpad_up)` to access these indices.
|
||||
buttons: [15]u8,
|
||||
|
||||
/// The states of each gamepad axis (see gamepad_axes), in the range -1.0 to 1.0 inclusive.
|
||||
///
|
||||
/// Use the enumeration helper e.g. `.getAxis(.left_x)` to access these indices.
|
||||
axes: [6]f32,
|
||||
|
||||
/// Returns the state of the specified gamepad button.
|
||||
pub fn getButton(self: @This(), which: GamepadButton) Action {
|
||||
return @as(Action, @enumFromInt(self.buttons[@as(u32, @intCast(@intFromEnum(which)))]));
|
||||
}
|
||||
|
||||
/// Returns the status of the specified gamepad axis, in the range -1.0 to 1.0 inclusive.
|
||||
pub fn getAxis(self: @This(), which: GamepadAxis) f32 {
|
||||
return self.axes[@as(u32, @intCast(@intFromEnum(which)))];
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns whether the specified joystick is present.
|
||||
///
|
||||
/// This function returns whether the specified joystick is present.
|
||||
///
|
||||
/// There is no need to call this function before other functions that accept a joystick ID, as
|
||||
/// they all check for presence before performing any other work.
|
||||
///
|
||||
/// @return `true` if the joystick is present, or `false` otherwise.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.PlatformError.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: joystick
|
||||
pub inline fn present(self: Joystick) bool {
|
||||
internal_debug.assertInitialized();
|
||||
const is_present = c.glfwJoystickPresent(@intFromEnum(self.jid));
|
||||
return is_present == c.GLFW_TRUE;
|
||||
}
|
||||
|
||||
/// Returns the values of all axes of the specified joystick.
|
||||
///
|
||||
/// This function returns the values of all axes of the specified joystick. Each element in the
|
||||
/// array is a value between -1.0 and 1.0.
|
||||
///
|
||||
/// If the specified joystick is not present this function will return null but will not generate
|
||||
/// an error. This can be used instead of first calling glfw.Joystick.present.
|
||||
///
|
||||
/// @return An array of axis values, or null if the joystick is not present.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.PlatformError.
|
||||
/// null is additionally returned in the event of an error.
|
||||
///
|
||||
/// @pointer_lifetime The returned array is allocated and freed by GLFW. You should not free it
|
||||
/// yourself. It is valid until the specified joystick is disconnected or the library is
|
||||
/// terminated.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: joystick_axis
|
||||
/// Replaces `glfwGetJoystickPos`.
|
||||
pub inline fn getAxes(self: Joystick) ?[]const f32 {
|
||||
internal_debug.assertInitialized();
|
||||
var count: c_int = undefined;
|
||||
const axes = c.glfwGetJoystickAxes(@intFromEnum(self.jid), &count);
|
||||
if (axes == null) return null;
|
||||
return axes[0..@as(u32, @intCast(count))];
|
||||
}
|
||||
|
||||
/// Returns the state of all buttons of the specified joystick.
|
||||
///
|
||||
/// This function returns the state of all buttons of the specified joystick. Each element in the
|
||||
/// array is either `glfw.Action.press` or `glfw.Action.release`.
|
||||
///
|
||||
/// For backward compatibility with earlier versions that did not have glfw.Joystick.getHats, the
|
||||
/// button array also includes all hats, each represented as four buttons. The hats are in the same
|
||||
/// order as returned by glfw.Joystick.getHats and are in the order _up_, _right_, _down_ and
|
||||
/// _left_. To disable these extra buttons, set the glfw.joystick_hat_buttons init hint before
|
||||
/// initialization.
|
||||
///
|
||||
/// If the specified joystick is not present this function will return null but will not generate an
|
||||
/// error. This can be used instead of first calling glfw.Joystick.present.
|
||||
///
|
||||
/// @return An array of button states, or null if the joystick is not present.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.PlatformError.
|
||||
/// null is additionally returned in the event of an error.
|
||||
///
|
||||
/// @pointer_lifetime The returned array is allocated and freed by GLFW. You should not free it
|
||||
/// yourself. It is valid until the specified joystick is disconnected or the library is terminated.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: joystick_button
|
||||
pub inline fn getButtons(self: Joystick) ?[]const u8 {
|
||||
internal_debug.assertInitialized();
|
||||
var count: c_int = undefined;
|
||||
const buttons = c.glfwGetJoystickButtons(@intFromEnum(self.jid), &count);
|
||||
if (buttons == null) return null;
|
||||
return buttons[0..@as(u32, @intCast(count))];
|
||||
}
|
||||
|
||||
/// Returns the state of all hats of the specified joystick.
|
||||
///
|
||||
/// This function returns the state of all hats of the specified joystick. Each element in the array
|
||||
/// is one of the following values:
|
||||
///
|
||||
/// | Name | Value |
|
||||
/// |---------------------------|---------------------------------------------|
|
||||
/// | `glfw.RawHats.centered` | 0 |
|
||||
/// | `glfw.RawHats.up` | 1 |
|
||||
/// | `glfw.RawHats.right` | 2 |
|
||||
/// | `glfw.RawHats.down` | 4 |
|
||||
/// | `glfw.RawHats.left` | 8 |
|
||||
/// | `glfw.RawHats.right_up` | `glfw.RawHats.right` \| `glfw.RawHats.up` |
|
||||
/// | `glfw.RawHats.right_down` | `glfw.RawHats.right` \| `glfw.RawHats.down` |
|
||||
/// | `glfw.RawHats.left_up` | `glfw.RawHats.left` \| `glfw.RawHats.up` |
|
||||
/// | `glfw.RawHats.left_down` | `glfw.RawHats.left` \| `glfw.RawHats.down` |
|
||||
///
|
||||
/// The diagonal directions are bitwise combinations of the primary (up, right, down and left)
|
||||
/// directions, since the Zig GLFW wrapper returns a packed struct it is trivial to test for these:
|
||||
///
|
||||
/// ```
|
||||
/// if (hats.up and hats.right) {
|
||||
/// // up-right!
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If the specified joystick is not present this function will return null but will not generate an
|
||||
/// error. This can be used instead of first calling glfw.Joystick.present.
|
||||
///
|
||||
/// @return An array of hat states, or null if the joystick is not present.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.PlatformError.
|
||||
/// null is additionally returned in the event of an error.
|
||||
///
|
||||
/// @pointer_lifetime The returned array is allocated and freed by GLFW. You should not free it
|
||||
/// yourself. It is valid until the specified joystick is disconnected, this function is called
|
||||
/// again for that joystick or the library is terminated.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: joystick_hat
|
||||
pub inline fn getHats(self: Joystick) ?[]const Hat {
|
||||
internal_debug.assertInitialized();
|
||||
var count: c_int = undefined;
|
||||
const hats = c.glfwGetJoystickHats(@intFromEnum(self.jid), &count);
|
||||
if (hats == null) return null;
|
||||
const slice = hats[0..@as(u32, @intCast(count))];
|
||||
return @as(*const []const Hat, @ptrCast(&slice)).*;
|
||||
}
|
||||
|
||||
/// Returns the name of the specified joystick.
|
||||
///
|
||||
/// This function returns the name, encoded as UTF-8, of the specified joystick. The returned string
|
||||
/// is allocated and freed by GLFW. You should not free it yourself.
|
||||
///
|
||||
/// If the specified joystick is not present this function will return null but will not generate an
|
||||
/// error. This can be used instead of first calling glfw.Joystick.present.
|
||||
///
|
||||
/// @return The UTF-8 encoded name of the joystick, or null if the joystick is not present or an
|
||||
/// error occurred.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.PlatformError.
|
||||
/// null is additionally returned in the event of an error.
|
||||
///
|
||||
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
|
||||
/// yourself. It is valid until the specified joystick is disconnected or the library is terminated.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: joystick_name
|
||||
pub inline fn getName(self: Joystick) ?[:0]const u8 {
|
||||
internal_debug.assertInitialized();
|
||||
const name_opt = c.glfwGetJoystickName(@intFromEnum(self.jid));
|
||||
return if (name_opt) |name|
|
||||
std.mem.span(@as([*:0]const u8, @ptrCast(name)))
|
||||
else
|
||||
null;
|
||||
}
|
||||
|
||||
/// Returns the SDL compatible GUID of the specified joystick.
|
||||
///
|
||||
/// This function returns the SDL compatible GUID, as a UTF-8 encoded hexadecimal string, of the
|
||||
/// specified joystick. The returned string is allocated and freed by GLFW. You should not free it
|
||||
/// yourself.
|
||||
///
|
||||
/// The GUID is what connects a joystick to a gamepad mapping. A connected joystick will always have
|
||||
/// a GUID even if there is no gamepad mapping assigned to it.
|
||||
///
|
||||
/// If the specified joystick is not present this function will return null but will not generate an
|
||||
/// error. This can be used instead of first calling glfw.Joystick.present.
|
||||
///
|
||||
/// The GUID uses the format introduced in SDL 2.0.5. This GUID tries to uniquely identify the make
|
||||
/// and model of a joystick but does not identify a specific unit, e.g. all wired Xbox 360
|
||||
/// controllers will have the same GUID on that platform. The GUID for a unit may vary between
|
||||
/// platforms depending on what hardware information the platform specific APIs provide.
|
||||
///
|
||||
/// @return The UTF-8 encoded GUID of the joystick, or null if the joystick is not present.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.PlatformError.
|
||||
/// null is additionally returned in the event of an error.
|
||||
///
|
||||
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
|
||||
/// yourself. It is valid until the specified joystick is disconnected or the library is terminated.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: gamepad
|
||||
pub inline fn getGUID(self: Joystick) ?[:0]const u8 {
|
||||
internal_debug.assertInitialized();
|
||||
const guid_opt = c.glfwGetJoystickGUID(@intFromEnum(self.jid));
|
||||
return if (guid_opt) |guid|
|
||||
std.mem.span(@as([*:0]const u8, @ptrCast(guid)))
|
||||
else
|
||||
null;
|
||||
}
|
||||
|
||||
/// Sets the user pointer of the specified joystick.
|
||||
///
|
||||
/// This function sets the user-defined pointer of the specified joystick. The current value is
|
||||
/// retained until the joystick is disconnected. The initial value is null.
|
||||
///
|
||||
/// This function may be called from the joystick callback, even for a joystick that is being disconnected.
|
||||
///
|
||||
/// @thread_safety This function may be called from any thread. Access is not synchronized.
|
||||
///
|
||||
/// see also: joystick_userptr, glfw.Joystick.getUserPointer
|
||||
pub inline fn setUserPointer(self: Joystick, comptime T: type, pointer: *T) void {
|
||||
internal_debug.assertInitialized();
|
||||
c.glfwSetJoystickUserPointer(@intFromEnum(self.jid), @as(*anyopaque, @ptrCast(pointer)));
|
||||
}
|
||||
|
||||
/// Returns the user pointer of the specified joystick.
|
||||
///
|
||||
/// This function returns the current value of the user-defined pointer of the specified joystick.
|
||||
/// The initial value is null.
|
||||
///
|
||||
/// This function may be called from the joystick callback, even for a joystick that is being
|
||||
/// disconnected.
|
||||
///
|
||||
/// @thread_safety This function may be called from any thread. Access is not synchronized.
|
||||
///
|
||||
/// see also: joystick_userptr, glfw.Joystick.setUserPointer
|
||||
pub inline fn getUserPointer(self: Joystick, comptime PointerType: type) ?PointerType {
|
||||
internal_debug.assertInitialized();
|
||||
const ptr = c.glfwGetJoystickUserPointer(@intFromEnum(self.jid));
|
||||
if (ptr) |p| return @as(PointerType, @ptrCast(@alignCast(p)));
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Describes an event relating to a joystick.
|
||||
pub const Event = enum(c_int) {
|
||||
/// The device was connected.
|
||||
connected = c.GLFW_CONNECTED,
|
||||
|
||||
/// The device was disconnected.
|
||||
disconnected = c.GLFW_DISCONNECTED,
|
||||
};
|
||||
|
||||
/// Sets the joystick configuration callback.
|
||||
///
|
||||
/// This function sets the joystick configuration callback, or removes the currently set callback.
|
||||
/// This is called when a joystick is connected to or disconnected from the system.
|
||||
///
|
||||
/// For joystick connection and disconnection events to be delivered on all platforms, you need to
|
||||
/// call one of the event processing (see events) functions. Joystick disconnection may also be
|
||||
/// detected and the callback called by joystick functions. The function will then return whatever
|
||||
/// it returns if the joystick is not present.
|
||||
///
|
||||
/// @param[in] callback The new callback, or null to remove the currently set callback.
|
||||
///
|
||||
/// @callback_param `jid` The joystick that was connected or disconnected.
|
||||
/// @callback_param `event` One of `.connected` or `.disconnected`. Future releases may add
|
||||
/// more events.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: joystick_event
|
||||
pub inline fn setCallback(comptime callback: ?fn (joystick: Joystick, event: Event) void) void {
|
||||
internal_debug.assertInitialized();
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn joystickCallbackWrapper(jid: c_int, event: c_int) callconv(.C) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
Joystick{ .jid = @as(Joystick.Id, @enumFromInt(jid)) },
|
||||
@as(Event, @enumFromInt(event)),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (c.glfwSetJoystickCallback(CWrapper.joystickCallbackWrapper) != null) return;
|
||||
} else {
|
||||
if (c.glfwSetJoystickCallback(null) != null) return;
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the specified SDL_GameControllerDB gamepad mappings.
|
||||
///
|
||||
/// This function parses the specified ASCII encoded string and updates the internal list with any
|
||||
/// gamepad mappings it finds. This string may contain either a single gamepad mapping or many
|
||||
/// mappings separated by newlines. The parser supports the full format of the `gamecontrollerdb.txt`
|
||||
/// source file including empty lines and comments.
|
||||
///
|
||||
/// See gamepad_mapping for a description of the format.
|
||||
///
|
||||
/// If there is already a gamepad mapping for a given GUID in the internal list, it will be
|
||||
/// replaced by the one passed to this function. If the library is terminated and re-initialized
|
||||
/// the internal list will revert to the built-in default.
|
||||
///
|
||||
/// @param[in] string The string containing the gamepad mappings.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.InvalidValue.
|
||||
/// Returns a boolean indicating success.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: gamepad, glfw.Joystick.isGamepad, glfwGetGamepadName
|
||||
///
|
||||
///
|
||||
/// @ingroup input
|
||||
pub inline fn updateGamepadMappings(gamepad_mappings: [*:0]const u8) bool {
|
||||
internal_debug.assertInitialized();
|
||||
return c.glfwUpdateGamepadMappings(gamepad_mappings) == c.GLFW_TRUE;
|
||||
}
|
||||
|
||||
/// Returns whether the specified joystick has a gamepad mapping.
|
||||
///
|
||||
/// This function returns whether the specified joystick is both present and has a gamepad mapping.
|
||||
///
|
||||
/// If the specified joystick is present but does not have a gamepad mapping this function will
|
||||
/// return `false` but will not generate an error. Call glfw.Joystick.present to check if a
|
||||
/// joystick is present regardless of whether it has a mapping.
|
||||
///
|
||||
/// @return `true` if a joystick is both present and has a gamepad mapping, or `false` otherwise.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.InvalidEnum.
|
||||
/// Additionally returns false in the event of an error.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: gamepad, glfw.Joystick.getGamepadState
|
||||
pub inline fn isGamepad(self: Joystick) bool {
|
||||
internal_debug.assertInitialized();
|
||||
const is_gamepad = c.glfwJoystickIsGamepad(@intFromEnum(self.jid));
|
||||
return is_gamepad == c.GLFW_TRUE;
|
||||
}
|
||||
|
||||
/// Returns the human-readable gamepad name for the specified joystick.
|
||||
///
|
||||
/// This function returns the human-readable name of the gamepad from the gamepad mapping assigned
|
||||
/// to the specified joystick.
|
||||
///
|
||||
/// If the specified joystick is not present or does not have a gamepad mapping this function will
|
||||
/// return null, not an error. Call glfw.Joystick.present to check whether it is
|
||||
/// present regardless of whether it has a mapping.
|
||||
///
|
||||
/// @return The UTF-8 encoded name of the gamepad, or null if the joystick is not present or does
|
||||
/// not have a mapping.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.InvalidEnum.
|
||||
/// Additionally returns null in the event of an error.
|
||||
///
|
||||
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
|
||||
/// yourself. It is valid until the specified joystick is disconnected, the gamepad mappings are
|
||||
/// updated or the library is terminated.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: gamepad, glfw.Joystick.isGamepad
|
||||
pub inline fn getGamepadName(self: Joystick) ?[:0]const u8 {
|
||||
internal_debug.assertInitialized();
|
||||
const name_opt = c.glfwGetGamepadName(@intFromEnum(self.jid));
|
||||
return if (name_opt) |name|
|
||||
std.mem.span(@as([*:0]const u8, @ptrCast(name)))
|
||||
else
|
||||
null;
|
||||
}
|
||||
|
||||
/// Retrieves the state of the joystick remapped as a gamepad.
|
||||
///
|
||||
/// This function retrieves the state of the joystick remapped to an Xbox-like gamepad.
|
||||
///
|
||||
/// If the specified joystick is not present or does not have a gamepad mapping this function will
|
||||
/// return `false`. Call glfw.joystickPresent to check whether it is present regardless of whether
|
||||
/// it has a mapping.
|
||||
///
|
||||
/// The Guide button may not be available for input as it is often hooked by the system or the
|
||||
/// Steam client.
|
||||
///
|
||||
/// Not all devices have all the buttons or axes provided by GamepadState. Unavailable buttons
|
||||
/// and axes will always report `glfw.Action.release` and 0.0 respectively.
|
||||
///
|
||||
/// @param[in] jid The joystick (see joysticks) to query.
|
||||
/// @param[out] state The gamepad input state of the joystick.
|
||||
/// @return the gamepad input state if successful, or null if no joystick is connected or it has no
|
||||
/// gamepad mapping.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.InvalidEnum.
|
||||
/// Returns null in the event of an error.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: gamepad, glfw.UpdateGamepadMappings, glfw.Joystick.isGamepad
|
||||
pub inline fn getGamepadState(self: Joystick) ?GamepadState {
|
||||
internal_debug.assertInitialized();
|
||||
var state: GamepadState = undefined;
|
||||
const success = c.glfwGetGamepadState(@intFromEnum(self.jid), @as(*c.GLFWgamepadstate, @ptrCast(&state)));
|
||||
return if (success == c.GLFW_TRUE) state else null;
|
||||
}
|
||||
|
||||
test "present" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const joystick = glfw.Joystick{ .jid = .one };
|
||||
_ = joystick.present();
|
||||
}
|
||||
|
||||
test "getAxes" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const joystick = glfw.Joystick{ .jid = .one };
|
||||
_ = joystick.getAxes();
|
||||
}
|
||||
|
||||
test "getButtons" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const joystick = glfw.Joystick{ .jid = .one };
|
||||
_ = joystick.getButtons();
|
||||
}
|
||||
|
||||
test "getHats" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const joystick = glfw.Joystick{ .jid = .one };
|
||||
|
||||
if (joystick.getHats()) |hats| {
|
||||
for (hats) |hat| {
|
||||
if (hat.down and hat.up) {
|
||||
// down-up!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "getName" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const joystick = glfw.Joystick{ .jid = .one };
|
||||
_ = joystick.getName();
|
||||
}
|
||||
|
||||
test "getGUID" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const joystick = glfw.Joystick{ .jid = .one };
|
||||
_ = joystick.getGUID();
|
||||
}
|
||||
|
||||
test "setUserPointer_syntax" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const joystick = glfw.Joystick{ .jid = .one };
|
||||
|
||||
// Must be called from joystick callback, we cannot test it.
|
||||
_ = joystick;
|
||||
_ = setUserPointer;
|
||||
}
|
||||
|
||||
test "getUserPointer_syntax" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const joystick = glfw.Joystick{ .jid = .one };
|
||||
|
||||
// Must be called from joystick callback, we cannot test it.
|
||||
_ = joystick;
|
||||
_ = getUserPointer;
|
||||
}
|
||||
|
||||
test "setCallback" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
glfw.Joystick.setCallback((struct {
|
||||
pub fn callback(joystick: Joystick, event: Event) void {
|
||||
_ = joystick;
|
||||
_ = event;
|
||||
}
|
||||
}).callback);
|
||||
}
|
||||
|
||||
test "updateGamepadMappings_syntax" {
|
||||
// We don't have a gamepad mapping to test with, just confirm the syntax is good.
|
||||
_ = updateGamepadMappings;
|
||||
}
|
||||
|
||||
test "isGamepad" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const joystick = glfw.Joystick{ .jid = .one };
|
||||
_ = joystick.isGamepad();
|
||||
}
|
||||
|
||||
test "getGamepadName" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const joystick = glfw.Joystick{ .jid = .one };
|
||||
_ = joystick.getGamepadName();
|
||||
}
|
||||
|
||||
test "getGamepadState" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const joystick = glfw.Joystick{ .jid = .one };
|
||||
_ = joystick.getGamepadState();
|
||||
_ = (std.mem.zeroes(GamepadState)).getAxis(.left_x);
|
||||
_ = (std.mem.zeroes(GamepadState)).getButton(.dpad_up);
|
||||
}
|
26
pkg/glfw/LICENSE
Normal file
@@ -0,0 +1,26 @@
|
||||
Copyright (c) 2021 Hexops Contributors (given via the Git commit history).
|
||||
Copyright (c) 2025 Mitchell Hashimoto
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
599
pkg/glfw/Monitor.zig
Normal file
@@ -0,0 +1,599 @@
|
||||
//! Monitor type and related functions
|
||||
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const testing = std.testing;
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
const GammaRamp = @import("GammaRamp.zig");
|
||||
const VideoMode = @import("VideoMode.zig");
|
||||
|
||||
const internal_debug = @import("internal_debug.zig");
|
||||
|
||||
const Monitor = @This();
|
||||
|
||||
handle: *c.GLFWmonitor,
|
||||
|
||||
/// A monitor position, in screen coordinates, of the upper left corner of the monitor on the
|
||||
/// virtual screen.
|
||||
const Pos = struct {
|
||||
/// The x coordinate.
|
||||
x: u32,
|
||||
/// The y coordinate.
|
||||
y: u32,
|
||||
};
|
||||
|
||||
/// Returns the position of the monitor's viewport on the virtual screen.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.PlatformError.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: monitor_properties
|
||||
pub inline fn getPos(self: Monitor) Pos {
|
||||
internal_debug.assertInitialized();
|
||||
var xpos: c_int = 0;
|
||||
var ypos: c_int = 0;
|
||||
c.glfwGetMonitorPos(self.handle, &xpos, &ypos);
|
||||
return Pos{ .x = @as(u32, @intCast(xpos)), .y = @as(u32, @intCast(ypos)) };
|
||||
}
|
||||
|
||||
/// The monitor workarea, in screen coordinates.
|
||||
///
|
||||
/// This is the position of the upper-left corner of the work area of the monitor, along with the
|
||||
/// work area size. The work area is defined as the area of the monitor not occluded by the
|
||||
/// window system task bar where present. If no task bar exists then the work area is the
|
||||
/// monitor resolution in screen coordinates.
|
||||
const Workarea = struct {
|
||||
x: u32,
|
||||
y: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
};
|
||||
|
||||
/// Retrieves the work area of the monitor.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.PlatformError.
|
||||
/// A zero value is returned in the event of an error.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: monitor_workarea
|
||||
pub inline fn getWorkarea(self: Monitor) Workarea {
|
||||
internal_debug.assertInitialized();
|
||||
var xpos: c_int = 0;
|
||||
var ypos: c_int = 0;
|
||||
var width: c_int = 0;
|
||||
var height: c_int = 0;
|
||||
c.glfwGetMonitorWorkarea(self.handle, &xpos, &ypos, &width, &height);
|
||||
return Workarea{ .x = @as(u32, @intCast(xpos)), .y = @as(u32, @intCast(ypos)), .width = @as(u32, @intCast(width)), .height = @as(u32, @intCast(height)) };
|
||||
}
|
||||
|
||||
/// The physical size, in millimetres, of the display area of a monitor.
|
||||
const PhysicalSize = struct {
|
||||
width_mm: u32,
|
||||
height_mm: u32,
|
||||
};
|
||||
|
||||
/// Returns the physical size of the monitor.
|
||||
///
|
||||
/// Some platforms do not provide accurate monitor size information, either because the monitor
|
||||
/// [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data)
|
||||
/// data is incorrect or because the driver does not report it accurately.
|
||||
///
|
||||
/// win32: On Windows 8 and earlier the physical size is calculated from
|
||||
/// the current resolution and system DPI instead of querying the monitor EDID data
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: monitor_properties
|
||||
pub inline fn getPhysicalSize(self: Monitor) PhysicalSize {
|
||||
internal_debug.assertInitialized();
|
||||
var width_mm: c_int = 0;
|
||||
var height_mm: c_int = 0;
|
||||
c.glfwGetMonitorPhysicalSize(self.handle, &width_mm, &height_mm);
|
||||
return PhysicalSize{ .width_mm = @as(u32, @intCast(width_mm)), .height_mm = @as(u32, @intCast(height_mm)) };
|
||||
}
|
||||
|
||||
/// The content scale for a monitor.
|
||||
///
|
||||
/// This is the ratio between the current DPI and the platform's default DPI. This is especially
|
||||
/// important for text and any UI elements. If the pixel dimensions of your UI scaled by this look
|
||||
/// appropriate on your machine then it should appear at a reasonable size on other machines
|
||||
/// regardless of their DPI and scaling settings. This relies on the system DPI and scaling
|
||||
/// settings being somewhat correct.
|
||||
///
|
||||
/// The content scale may depend on both the monitor resolution and pixel density and on users
|
||||
/// settings. It may be very different from the raw DPI calculated from the physical size and
|
||||
/// current resolution.
|
||||
const ContentScale = struct {
|
||||
x_scale: f32,
|
||||
y_scale: f32,
|
||||
};
|
||||
|
||||
/// Returns the content scale for the monitor.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.PlatformError.
|
||||
/// A zero value is returned in the event of an error.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: monitor_scale, glfw.Window.getContentScale
|
||||
pub inline fn getContentScale(self: Monitor) ContentScale {
|
||||
internal_debug.assertInitialized();
|
||||
var x_scale: f32 = 0;
|
||||
var y_scale: f32 = 0;
|
||||
c.glfwGetMonitorContentScale(self.handle, &x_scale, &y_scale);
|
||||
return ContentScale{ .x_scale = @as(f32, @floatCast(x_scale)), .y_scale = @as(f32, @floatCast(y_scale)) };
|
||||
}
|
||||
|
||||
/// Returns the name of the specified monitor.
|
||||
///
|
||||
/// This function returns a human-readable name, encoded as UTF-8, of the specified monitor. The
|
||||
/// name typically reflects the make and model of the monitor and is not guaranteed to be unique
|
||||
/// among the connected monitors.
|
||||
///
|
||||
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
|
||||
/// yourself. It is valid until the specified monitor is disconnected or the library is terminated.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: monitor_properties
|
||||
pub inline fn getName(self: Monitor) [*:0]const u8 {
|
||||
internal_debug.assertInitialized();
|
||||
if (c.glfwGetMonitorName(self.handle)) |name| return @as([*:0]const u8, @ptrCast(name));
|
||||
// `glfwGetMonitorName` returns `null` only for errors, but the only error is unreachable
|
||||
// (NotInitialized)
|
||||
unreachable;
|
||||
}
|
||||
|
||||
/// Sets the user pointer of the specified monitor.
|
||||
///
|
||||
/// This function sets the user-defined pointer of the specified monitor. The current value is
|
||||
/// retained until the monitor is disconnected.
|
||||
///
|
||||
/// This function may be called from the monitor callback, even for a monitor that is being
|
||||
/// disconnected.
|
||||
///
|
||||
/// @thread_safety This function may be called from any thread. Access is not synchronized.
|
||||
///
|
||||
/// see also: monitor_userptr, glfw.Monitor.getUserPointer
|
||||
pub inline fn setUserPointer(self: Monitor, comptime T: type, ptr: *T) void {
|
||||
internal_debug.assertInitialized();
|
||||
c.glfwSetMonitorUserPointer(self.handle, ptr);
|
||||
}
|
||||
|
||||
/// Returns the user pointer of the specified monitor.
|
||||
///
|
||||
/// This function returns the current value of the user-defined pointer of the specified monitor.
|
||||
///
|
||||
/// This function may be called from the monitor callback, even for a monitor that is being
|
||||
/// disconnected.
|
||||
///
|
||||
/// @thread_safety This function may be called from any thread. Access is not synchronized.
|
||||
///
|
||||
/// see also: monitor_userptr, glfw.Monitor.setUserPointer
|
||||
pub inline fn getUserPointer(self: Monitor, comptime T: type) ?*T {
|
||||
internal_debug.assertInitialized();
|
||||
const ptr = c.glfwGetMonitorUserPointer(self.handle);
|
||||
if (ptr == null) return null;
|
||||
return @as(*T, @ptrCast(@alignCast(ptr.?)));
|
||||
}
|
||||
|
||||
/// Returns the available video modes for the specified monitor.
|
||||
///
|
||||
/// This function returns an array of all video modes supported by the monitor. The returned slice
|
||||
/// is sorted in ascending order, first by color bit depth (the sum of all channel depths) and
|
||||
/// then by resolution area (the product of width and height), then resolution width and finally
|
||||
/// by refresh rate.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.PlatformError, glfw.ErrorCode.FeatureUnavailable.
|
||||
/// Returns null in the event of an error.
|
||||
///
|
||||
/// The returned slice memory is owned by the caller.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: monitor_modes, glfw.Monitor.getVideoMode
|
||||
///
|
||||
/// wayland: Gamma handling is privileged protocol, this function will thus never be implemented and
|
||||
/// emits glfw.ErrorCode.FeatureUnavailable
|
||||
///
|
||||
/// TODO(glfw): rewrite this to not require any allocation.
|
||||
pub inline fn getVideoModes(self: Monitor, allocator: mem.Allocator) mem.Allocator.Error!?[]VideoMode {
|
||||
internal_debug.assertInitialized();
|
||||
var count: c_int = 0;
|
||||
if (c.glfwGetVideoModes(self.handle, &count)) |modes| {
|
||||
const slice = try allocator.alloc(VideoMode, @as(u32, @intCast(count)));
|
||||
var i: u32 = 0;
|
||||
while (i < count) : (i += 1) {
|
||||
slice[i] = VideoMode{ .handle = @as([*c]const c.GLFWvidmode, @ptrCast(modes))[i] };
|
||||
}
|
||||
return slice;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Returns the current mode of the specified monitor.
|
||||
///
|
||||
/// This function returns the current video mode of the specified monitor. If you have created a
|
||||
/// full screen window for that monitor, the return value will depend on whether that window is
|
||||
/// iconified.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.PlatformError, glfw.ErrorCode.FeatureUnavailable.
|
||||
/// Additionally returns null in the event of an error.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// wayland: Gamma handling is a privileged protocol, this function will thus never be implemented
|
||||
/// and will thus never be implemented and emits glfw.ErrorCode.FeatureUnavailable
|
||||
///
|
||||
/// see also: monitor_modes, glfw.Monitor.getVideoModes
|
||||
pub inline fn getVideoMode(self: Monitor) ?VideoMode {
|
||||
internal_debug.assertInitialized();
|
||||
if (c.glfwGetVideoMode(self.handle)) |mode| return VideoMode{ .handle = mode.* };
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Generates a gamma ramp and sets it for the specified monitor.
|
||||
///
|
||||
/// This function generates an appropriately sized gamma ramp from the specified exponent and then
|
||||
/// calls glfw.Monitor.setGammaRamp with it. The value must be a finite number greater than zero.
|
||||
///
|
||||
/// The software controlled gamma ramp is applied _in addition_ to the hardware gamma correction,
|
||||
/// which today is usually an approximation of sRGB gamma. This means that setting a perfectly
|
||||
/// linear ramp, or gamma 1.0, will produce the default (usually sRGB-like) behavior.
|
||||
///
|
||||
/// For gamma correct rendering with OpenGL or OpenGL ES, see the glfw.srgb_capable hint.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.PlatformError, glfw.ErrorCode.FeatureUnavailable.
|
||||
///
|
||||
/// wayland: Gamma handling is privileged protocol, this function will thus never be implemented and
|
||||
/// emits glfw.ErrorCode.FeatureUnavailable
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: monitor_gamma
|
||||
pub inline fn setGamma(self: Monitor, gamma: f32) void {
|
||||
internal_debug.assertInitialized();
|
||||
|
||||
std.debug.assert(!std.math.isNan(gamma));
|
||||
std.debug.assert(gamma >= 0);
|
||||
std.debug.assert(gamma <= std.math.f32_max);
|
||||
|
||||
c.glfwSetGamma(self.handle, gamma);
|
||||
}
|
||||
|
||||
/// Returns the current gamma ramp for the specified monitor.
|
||||
///
|
||||
/// This function returns the current gamma ramp of the specified monitor.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.PlatformError.
|
||||
/// Additionally returns null in the event of an error.
|
||||
///
|
||||
/// wayland: Gamma handling is a privileged protocol, this function will thus never be implemented
|
||||
/// and returns glfw.ErrorCode.FeatureUnavailable.
|
||||
///
|
||||
/// The returned gamma ramp is `.owned = true` by GLFW, and is valid until the monitor is
|
||||
/// disconnected, this function is called again, or `glfw.terminate()` is called.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: monitor_gamma
|
||||
pub inline fn getGammaRamp(self: Monitor) ?GammaRamp {
|
||||
internal_debug.assertInitialized();
|
||||
if (c.glfwGetGammaRamp(self.handle)) |ramp| return GammaRamp.fromC(ramp.*);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Sets the current gamma ramp for the specified monitor.
|
||||
///
|
||||
/// This function sets the current gamma ramp for the specified monitor. The original gamma ramp
|
||||
/// for that monitor is saved by GLFW the first time this function is called and is restored by
|
||||
/// `glfw.terminate()`.
|
||||
///
|
||||
/// The software controlled gamma ramp is applied _in addition_ to the hardware gamma correction,
|
||||
/// which today is usually an approximation of sRGB gamma. This means that setting a perfectly
|
||||
/// linear ramp, or gamma 1.0, will produce the default (usually sRGB-like) behavior.
|
||||
///
|
||||
/// For gamma correct rendering with OpenGL or OpenGL ES, see the glfw.srgb_capable hint.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.PlatformError, glfw.ErrorCode.FeatureUnavailable.
|
||||
///
|
||||
/// The size of the specified gamma ramp should match the size of the current ramp for that
|
||||
/// monitor. On win32, the gamma ramp size must be 256.
|
||||
///
|
||||
/// wayland: Gamma handling is a privileged protocol, this function will thus never be implemented
|
||||
/// and returns glfw.ErrorCode.FeatureUnavailable.
|
||||
///
|
||||
/// @pointer_lifetime The specified gamma ramp is copied before this function returns.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: monitor_gamma
|
||||
pub inline fn setGammaRamp(self: Monitor, ramp: GammaRamp) void {
|
||||
internal_debug.assertInitialized();
|
||||
c.glfwSetGammaRamp(self.handle, &ramp.toC());
|
||||
}
|
||||
|
||||
/// Returns the currently connected monitors.
|
||||
///
|
||||
/// This function returns a slice of all currently connected monitors. The primary monitor is
|
||||
/// always first. If no monitors were found, this function returns an empty slice.
|
||||
///
|
||||
/// The returned slice memory is owned by the caller. The underlying handles are owned by GLFW, and
|
||||
/// are valid until the monitor configuration changes or `glfw.terminate` is called.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: monitor_monitors, monitor_event, glfw.monitor.getPrimary
|
||||
pub inline fn getAll(allocator: mem.Allocator) mem.Allocator.Error![]Monitor {
|
||||
internal_debug.assertInitialized();
|
||||
var count: c_int = 0;
|
||||
if (c.glfwGetMonitors(&count)) |monitors| {
|
||||
const slice = try allocator.alloc(Monitor, @as(u32, @intCast(count)));
|
||||
var i: u32 = 0;
|
||||
while (i < count) : (i += 1) {
|
||||
slice[i] = Monitor{ .handle = @as([*c]const ?*c.GLFWmonitor, @ptrCast(monitors))[i].? };
|
||||
}
|
||||
return slice;
|
||||
}
|
||||
// `glfwGetMonitors` returning null can be either an error or no monitors, but the only error is
|
||||
// unreachable (NotInitialized)
|
||||
return &[_]Monitor{};
|
||||
}
|
||||
|
||||
/// Returns the primary monitor.
|
||||
///
|
||||
/// This function returns the primary monitor. This is usually the monitor where elements like
|
||||
/// the task bar or global menu bar are located.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: monitor_monitors, glfw.monitors.getAll
|
||||
pub inline fn getPrimary() ?Monitor {
|
||||
internal_debug.assertInitialized();
|
||||
if (c.glfwGetPrimaryMonitor()) |handle| return Monitor{ .handle = handle };
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Describes an event relating to a monitor.
|
||||
pub const Event = enum(c_int) {
|
||||
/// The device was connected.
|
||||
connected = c.GLFW_CONNECTED,
|
||||
|
||||
/// The device was disconnected.
|
||||
disconnected = c.GLFW_DISCONNECTED,
|
||||
};
|
||||
|
||||
/// Sets the monitor configuration callback.
|
||||
///
|
||||
/// This function sets the monitor configuration callback, or removes the currently set callback.
|
||||
/// This is called when a monitor is connected to or disconnected from the system. Example:
|
||||
///
|
||||
/// ```
|
||||
/// fn monitorCallback(monitor: glfw.Monitor, event: glfw.Monitor.Event, data: *MyData) void {
|
||||
/// // data is the pointer you passed into setCallback.
|
||||
/// // event is one of .connected or .disconnected
|
||||
/// }
|
||||
/// ...
|
||||
/// glfw.Monitor.setCallback(MyData, &myData, monitorCallback)
|
||||
/// ```
|
||||
///
|
||||
/// `event` may be one of .connected or .disconnected. More events may be added in the future.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: monitor_event
|
||||
pub inline fn setCallback(comptime callback: ?fn (monitor: Monitor, event: Event) void) void {
|
||||
internal_debug.assertInitialized();
|
||||
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn monitorCallbackWrapper(monitor: ?*c.GLFWmonitor, event: c_int) callconv(.C) void {
|
||||
@call(.always_inline, user_callback, .{
|
||||
Monitor{ .handle = monitor.? },
|
||||
@as(Event, @enumFromInt(event)),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (c.glfwSetMonitorCallback(CWrapper.monitorCallbackWrapper) != null) return;
|
||||
} else {
|
||||
if (c.glfwSetMonitorCallback(null) != null) return;
|
||||
}
|
||||
}
|
||||
|
||||
test "getAll" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const allocator = testing.allocator;
|
||||
const monitors = try getAll(allocator);
|
||||
defer allocator.free(monitors);
|
||||
}
|
||||
|
||||
test "getPrimary" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
_ = getPrimary();
|
||||
}
|
||||
|
||||
test "getPos" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const monitor = getPrimary();
|
||||
if (monitor) |m| {
|
||||
_ = m.getPos();
|
||||
}
|
||||
}
|
||||
|
||||
test "getWorkarea" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const monitor = getPrimary();
|
||||
if (monitor) |m| {
|
||||
_ = m.getWorkarea();
|
||||
}
|
||||
}
|
||||
|
||||
test "getPhysicalSize" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const monitor = getPrimary();
|
||||
if (monitor) |m| {
|
||||
_ = m.getPhysicalSize();
|
||||
}
|
||||
}
|
||||
|
||||
test "getContentScale" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const monitor = getPrimary();
|
||||
if (monitor) |m| {
|
||||
_ = m.getContentScale();
|
||||
}
|
||||
}
|
||||
|
||||
test "getName" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const monitor = getPrimary();
|
||||
if (monitor) |m| {
|
||||
_ = m.getName();
|
||||
}
|
||||
}
|
||||
|
||||
test "userPointer" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const monitor = getPrimary();
|
||||
if (monitor) |m| {
|
||||
var p = m.getUserPointer(u32);
|
||||
try testing.expect(p == null);
|
||||
var x: u32 = 5;
|
||||
m.setUserPointer(u32, &x);
|
||||
p = m.getUserPointer(u32);
|
||||
try testing.expectEqual(p.?.*, 5);
|
||||
}
|
||||
}
|
||||
|
||||
test "setCallback" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
setCallback(struct {
|
||||
fn callback(monitor: Monitor, event: Event) void {
|
||||
_ = monitor;
|
||||
_ = event;
|
||||
}
|
||||
}.callback);
|
||||
}
|
||||
|
||||
test "getVideoModes" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const monitor = getPrimary();
|
||||
if (monitor) |m| {
|
||||
const allocator = testing.allocator;
|
||||
const modes_maybe = try m.getVideoModes(allocator);
|
||||
if (modes_maybe) |modes| {
|
||||
defer allocator.free(modes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test "getVideoMode" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const monitor = getPrimary();
|
||||
if (monitor) |m| {
|
||||
_ = m.getVideoMode();
|
||||
}
|
||||
}
|
||||
|
||||
test "set_getGammaRamp" {
|
||||
const allocator = testing.allocator;
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
const monitor = getPrimary();
|
||||
if (monitor) |m| {
|
||||
if (m.getGammaRamp()) |ramp| {
|
||||
// Set it to the exact same value; if we do otherwise an our tests fail it wouldn't call
|
||||
// terminate and our made-up gamma ramp would get stuck.
|
||||
m.setGammaRamp(ramp);
|
||||
|
||||
// technically not needed here / noop because GLFW owns this gamma ramp.
|
||||
defer ramp.deinit(allocator);
|
||||
}
|
||||
}
|
||||
}
|
50
pkg/glfw/VideoMode.zig
Normal file
@@ -0,0 +1,50 @@
|
||||
//! Monitor video modes and related functions
|
||||
//!
|
||||
//! see also: glfw.Monitor.getVideoMode
|
||||
|
||||
const std = @import("std");
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
const VideoMode = @This();
|
||||
|
||||
handle: c.GLFWvidmode,
|
||||
|
||||
/// Returns the width of the video mode, in screen coordinates.
|
||||
pub inline fn getWidth(self: VideoMode) u32 {
|
||||
return @as(u32, @intCast(self.handle.width));
|
||||
}
|
||||
|
||||
/// Returns the height of the video mode, in screen coordinates.
|
||||
pub inline fn getHeight(self: VideoMode) u32 {
|
||||
return @as(u32, @intCast(self.handle.height));
|
||||
}
|
||||
|
||||
/// Returns the bit depth of the red channel of the video mode.
|
||||
pub inline fn getRedBits(self: VideoMode) u32 {
|
||||
return @as(u32, @intCast(self.handle.redBits));
|
||||
}
|
||||
|
||||
/// Returns the bit depth of the green channel of the video mode.
|
||||
pub inline fn getGreenBits(self: VideoMode) u32 {
|
||||
return @as(u32, @intCast(self.handle.greenBits));
|
||||
}
|
||||
|
||||
/// Returns the bit depth of the blue channel of the video mode.
|
||||
pub inline fn getBlueBits(self: VideoMode) u32 {
|
||||
return @as(u32, @intCast(self.handle.blueBits));
|
||||
}
|
||||
|
||||
/// Returns the refresh rate of the video mode, in Hz.
|
||||
pub inline fn getRefreshRate(self: VideoMode) u32 {
|
||||
return @as(u32, @intCast(self.handle.refreshRate));
|
||||
}
|
||||
|
||||
test "getters" {
|
||||
const x = std.mem.zeroes(VideoMode);
|
||||
_ = x.getWidth();
|
||||
_ = x.getHeight();
|
||||
_ = x.getRedBits();
|
||||
_ = x.getGreenBits();
|
||||
_ = x.getBlueBits();
|
||||
_ = x.getRefreshRate();
|
||||
}
|
3551
pkg/glfw/Window.zig
Normal file
13
pkg/glfw/action.zig
Normal file
@@ -0,0 +1,13 @@
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
/// Key and button actions
|
||||
pub const Action = enum(c_int) {
|
||||
/// The key or mouse button was released.
|
||||
release = c.GLFW_RELEASE,
|
||||
|
||||
/// The key or mouse button was pressed.
|
||||
press = c.GLFW_PRESS,
|
||||
|
||||
/// The key was held down until it repeated.
|
||||
repeat = c.GLFW_REPEAT,
|
||||
};
|
143
pkg/glfw/allocator.zig
Normal file
@@ -0,0 +1,143 @@
|
||||
// TODO: implement custom allocator support
|
||||
|
||||
// /*! @brief
|
||||
// *
|
||||
// * @sa @ref init_allocator
|
||||
// * @sa @ref glfwInitAllocator
|
||||
// *
|
||||
// * @since Added in version 3.4.
|
||||
// *
|
||||
// * @ingroup init
|
||||
// */
|
||||
// typedef struct GLFWallocator
|
||||
// {
|
||||
// GLFWallocatefun allocate;
|
||||
// GLFWreallocatefun reallocate;
|
||||
// GLFWdeallocatefun deallocate;
|
||||
// void* user;
|
||||
// } GLFWallocator;
|
||||
|
||||
// /*! @brief The function pointer type for memory allocation callbacks.
|
||||
// *
|
||||
// * This is the function pointer type for memory allocation callbacks. A memory
|
||||
// * allocation callback function has the following signature:
|
||||
// * @code
|
||||
// * void* function_name(size_t size, void* user)
|
||||
// * @endcode
|
||||
// *
|
||||
// * This function must return either a memory block at least `size` bytes long,
|
||||
// * or `NULL` if allocation failed. Note that not all parts of GLFW handle allocation
|
||||
// * failures gracefully yet.
|
||||
// *
|
||||
// * This function may be called during @ref glfwInit but before the library is
|
||||
// * flagged as initialized, as well as during @ref glfwTerminate after the
|
||||
// * library is no longer flagged as initialized.
|
||||
// *
|
||||
// * Any memory allocated by this function will be deallocated during library
|
||||
// * termination or earlier.
|
||||
// *
|
||||
// * The size will always be greater than zero. Allocations of size zero are filtered out
|
||||
// * before reaching the custom allocator.
|
||||
// *
|
||||
// * @param[in] size The minimum size, in bytes, of the memory block.
|
||||
// * @param[in] user The user-defined pointer from the allocator.
|
||||
// * @return The address of the newly allocated memory block, or `NULL` if an
|
||||
// * error occurred.
|
||||
// *
|
||||
// * @pointer_lifetime The returned memory block must be valid at least until it
|
||||
// * is deallocated.
|
||||
// *
|
||||
// * @reentrancy This function should not call any GLFW function.
|
||||
// *
|
||||
// * @thread_safety This function may be called from any thread that calls GLFW functions.
|
||||
// *
|
||||
// * @sa @ref init_allocator
|
||||
// * @sa @ref GLFWallocator
|
||||
// *
|
||||
// * @since Added in version 3.4.
|
||||
// *
|
||||
// * @ingroup init
|
||||
// */
|
||||
// typedef void* (* GLFWallocatefun)(size_t size, void* user);
|
||||
|
||||
// /*! @brief The function pointer type for memory reallocation callbacks.
|
||||
// *
|
||||
// * This is the function pointer type for memory reallocation callbacks.
|
||||
// * A memory reallocation callback function has the following signature:
|
||||
// * @code
|
||||
// * void* function_name(void* block, size_t size, void* user)
|
||||
// * @endcode
|
||||
// *
|
||||
// * This function must return a memory block at least `size` bytes long, or
|
||||
// * `NULL` if allocation failed. Note that not all parts of GLFW handle allocation
|
||||
// * failures gracefully yet.
|
||||
// *
|
||||
// * This function may be called during @ref glfwInit but before the library is
|
||||
// * flagged as initialized, as well as during @ref glfwTerminate after the
|
||||
// * library is no longer flagged as initialized.
|
||||
// *
|
||||
// * Any memory allocated by this function will be deallocated during library
|
||||
// * termination or earlier.
|
||||
// *
|
||||
// * The block address will never be `NULL` and the size will always be greater than zero.
|
||||
// * Reallocations of a block to size zero are converted into deallocations. Reallocations
|
||||
// * of `NULL` to a non-zero size are converted into regular allocations.
|
||||
// *
|
||||
// * @param[in] block The address of the memory block to reallocate.
|
||||
// * @param[in] size The new minimum size, in bytes, of the memory block.
|
||||
// * @param[in] user The user-defined pointer from the allocator.
|
||||
// * @return The address of the newly allocated or resized memory block, or
|
||||
// * `NULL` if an error occurred.
|
||||
// *
|
||||
// * @pointer_lifetime The returned memory block must be valid at least until it
|
||||
// * is deallocated.
|
||||
// *
|
||||
// * @reentrancy This function should not call any GLFW function.
|
||||
// *
|
||||
// * @thread_safety This function may be called from any thread that calls GLFW functions.
|
||||
// *
|
||||
// * @sa @ref init_allocator
|
||||
// * @sa @ref GLFWallocator
|
||||
// *
|
||||
// * @since Added in version 3.4.
|
||||
// *
|
||||
// * @ingroup init
|
||||
// */
|
||||
// typedef void* (* GLFWreallocatefun)(void* block, size_t size, void* user);
|
||||
|
||||
// /*! @brief The function pointer type for memory deallocation callbacks.
|
||||
// *
|
||||
// * This is the function pointer type for memory deallocation callbacks.
|
||||
// * A memory deallocation callback function has the following signature:
|
||||
// * @code
|
||||
// * void function_name(void* block, void* user)
|
||||
// * @endcode
|
||||
// *
|
||||
// * This function may deallocate the specified memory block. This memory block
|
||||
// * will have been allocated with the same allocator.
|
||||
// *
|
||||
// * This function may be called during @ref glfwInit but before the library is
|
||||
// * flagged as initialized, as well as during @ref glfwTerminate after the
|
||||
// * library is no longer flagged as initialized.
|
||||
// *
|
||||
// * The block address will never be `NULL`. Deallocations of `NULL` are filtered out
|
||||
// * before reaching the custom allocator.
|
||||
// *
|
||||
// * @param[in] block The address of the memory block to deallocate.
|
||||
// * @param[in] user The user-defined pointer from the allocator.
|
||||
// *
|
||||
// * @pointer_lifetime The specified memory block will not be accessed by GLFW
|
||||
// * after this function is called.
|
||||
// *
|
||||
// * @reentrancy This function should not call any GLFW function.
|
||||
// *
|
||||
// * @thread_safety This function may be called from any thread that calls GLFW functions.
|
||||
// *
|
||||
// * @sa @ref init_allocator
|
||||
// * @sa @ref GLFWallocator
|
||||
// *
|
||||
// * @since Added in version 3.4.
|
||||
// *
|
||||
// * @ingroup init
|
||||
// */
|
||||
// typedef void (* GLFWdeallocatefun)(void* block, void* user);
|
272
pkg/glfw/build.zig
Normal file
@@ -0,0 +1,272 @@
|
||||
const std = @import("std");
|
||||
const apple_sdk = @import("apple_sdk");
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const module = b.addModule("glfw", .{
|
||||
.root_source_file = b.path("main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const lib = try buildLib(b, module, .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const test_exe: ?*std.Build.Step.Compile = if (target.query.isNative()) exe: {
|
||||
const exe = b.addTest(.{
|
||||
.name = "test",
|
||||
.root_source_file = b.path("main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
if (target.result.os.tag.isDarwin()) {
|
||||
try apple_sdk.addPaths(b, exe.root_module);
|
||||
}
|
||||
|
||||
const tests_run = b.addRunArtifact(exe);
|
||||
const test_step = b.step("test", "Run tests");
|
||||
test_step.dependOn(&tests_run.step);
|
||||
|
||||
// Uncomment this if we're debugging tests
|
||||
b.installArtifact(exe);
|
||||
|
||||
break :exe exe;
|
||||
} else null;
|
||||
|
||||
if (b.systemIntegrationOption("glfw3", .{})) {
|
||||
module.linkSystemLibrary("glfw3", dynamic_link_opts);
|
||||
if (test_exe) |exe| exe.linkSystemLibrary2("glfw3", dynamic_link_opts);
|
||||
} else {
|
||||
module.linkLibrary(lib);
|
||||
b.installArtifact(lib);
|
||||
if (test_exe) |exe| exe.linkLibrary(lib);
|
||||
}
|
||||
}
|
||||
|
||||
fn buildLib(
|
||||
b: *std.Build,
|
||||
module: *std.Build.Module,
|
||||
options: anytype,
|
||||
) !*std.Build.Step.Compile {
|
||||
const target = options.target;
|
||||
const optimize = options.optimize;
|
||||
|
||||
const use_x11 = b.option(
|
||||
bool,
|
||||
"x11",
|
||||
"Build with X11. Only useful on Linux",
|
||||
) orelse true;
|
||||
const use_wl = b.option(
|
||||
bool,
|
||||
"wayland",
|
||||
"Build with Wayland. Only useful on Linux",
|
||||
) orelse true;
|
||||
|
||||
const use_opengl = b.option(
|
||||
bool,
|
||||
"opengl",
|
||||
"Build with OpenGL; deprecated on MacOS",
|
||||
) orelse false;
|
||||
const use_gles = b.option(
|
||||
bool,
|
||||
"gles",
|
||||
"Build with GLES; not supported on MacOS",
|
||||
) orelse false;
|
||||
const use_metal = b.option(
|
||||
bool,
|
||||
"metal",
|
||||
"Build with Metal; only supported on MacOS",
|
||||
) orelse true;
|
||||
|
||||
const lib = b.addStaticLibrary(.{
|
||||
.name = "glfw",
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
lib.linkLibC();
|
||||
|
||||
const upstream = b.lazyDependency("glfw", .{}) orelse return lib;
|
||||
lib.addIncludePath(upstream.path("include"));
|
||||
module.addIncludePath(upstream.path("include"));
|
||||
lib.installHeadersDirectory(upstream.path("include/GLFW"), "GLFW", .{});
|
||||
|
||||
switch (target.result.os.tag) {
|
||||
.windows => {
|
||||
lib.linkSystemLibrary("gdi32");
|
||||
lib.linkSystemLibrary("user32");
|
||||
lib.linkSystemLibrary("shell32");
|
||||
|
||||
if (use_opengl) {
|
||||
lib.linkSystemLibrary("opengl32");
|
||||
}
|
||||
|
||||
if (use_gles) {
|
||||
lib.linkSystemLibrary("GLESv3");
|
||||
}
|
||||
|
||||
const flags = [_][]const u8{"-D_GLFW_WIN32"};
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = &base_sources,
|
||||
.flags = &flags,
|
||||
});
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = &windows_sources,
|
||||
.flags = &flags,
|
||||
});
|
||||
},
|
||||
|
||||
.macos => {
|
||||
try apple_sdk.addPaths(b, lib.root_module);
|
||||
try apple_sdk.addPaths(b, module);
|
||||
|
||||
// Transitive dependencies, explicit linkage of these works around
|
||||
// ziglang/zig#17130
|
||||
lib.linkFramework("CFNetwork");
|
||||
lib.linkFramework("ApplicationServices");
|
||||
lib.linkFramework("ColorSync");
|
||||
lib.linkFramework("CoreText");
|
||||
lib.linkFramework("ImageIO");
|
||||
|
||||
// Direct dependencies
|
||||
lib.linkSystemLibrary("objc");
|
||||
lib.linkFramework("IOKit");
|
||||
lib.linkFramework("CoreFoundation");
|
||||
lib.linkFramework("AppKit");
|
||||
lib.linkFramework("CoreServices");
|
||||
lib.linkFramework("CoreGraphics");
|
||||
lib.linkFramework("Foundation");
|
||||
|
||||
if (use_metal) {
|
||||
lib.linkFramework("Metal");
|
||||
}
|
||||
|
||||
if (use_opengl) {
|
||||
lib.linkFramework("OpenGL");
|
||||
}
|
||||
|
||||
const flags = [_][]const u8{"-D_GLFW_COCOA"};
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = &base_sources,
|
||||
.flags = &flags,
|
||||
});
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = &macos_sources,
|
||||
.flags = &flags,
|
||||
});
|
||||
},
|
||||
|
||||
// everything that isn't windows or mac is linux :P
|
||||
else => {
|
||||
var sources = std.BoundedArray([]const u8, 64).init(0) catch unreachable;
|
||||
var flags = std.BoundedArray([]const u8, 16).init(0) catch unreachable;
|
||||
|
||||
sources.appendSlice(&base_sources) catch unreachable;
|
||||
sources.appendSlice(&linux_sources) catch unreachable;
|
||||
|
||||
if (use_x11) {
|
||||
lib.linkSystemLibrary2("X11", dynamic_link_opts);
|
||||
lib.linkSystemLibrary2("xkbcommon", dynamic_link_opts);
|
||||
sources.appendSlice(&linux_x11_sources) catch unreachable;
|
||||
flags.append("-D_GLFW_X11") catch unreachable;
|
||||
}
|
||||
|
||||
if (use_wl) {
|
||||
lib.linkSystemLibrary2("wayland-client", dynamic_link_opts);
|
||||
|
||||
lib.root_module.addCMacro("WL_MARSHAL_FLAG_DESTROY", "1");
|
||||
lib.addIncludePath(b.path("wayland-headers"));
|
||||
|
||||
sources.appendSlice(&linux_wl_sources) catch unreachable;
|
||||
flags.append("-D_GLFW_WAYLAND") catch unreachable;
|
||||
flags.append("-Wno-implicit-function-declaration") catch unreachable;
|
||||
}
|
||||
|
||||
lib.addCSourceFiles(.{
|
||||
.root = upstream.path(""),
|
||||
.files = sources.slice(),
|
||||
.flags = flags.slice(),
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
return lib;
|
||||
}
|
||||
|
||||
// For dynamic linking, we prefer dynamic linking and to search by
|
||||
// mode first. Mode first will search all paths for a dynamic library
|
||||
// before falling back to static.
|
||||
const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
|
||||
.preferred_link_mode = .dynamic,
|
||||
.search_strategy = .mode_first,
|
||||
};
|
||||
|
||||
const base_sources = [_][]const u8{
|
||||
"src/context.c",
|
||||
"src/egl_context.c",
|
||||
"src/init.c",
|
||||
"src/input.c",
|
||||
"src/monitor.c",
|
||||
"src/null_init.c",
|
||||
"src/null_joystick.c",
|
||||
"src/null_monitor.c",
|
||||
"src/null_window.c",
|
||||
"src/osmesa_context.c",
|
||||
"src/platform.c",
|
||||
"src/vulkan.c",
|
||||
"src/window.c",
|
||||
};
|
||||
|
||||
const linux_sources = [_][]const u8{
|
||||
"src/linux_joystick.c",
|
||||
"src/posix_module.c",
|
||||
"src/posix_poll.c",
|
||||
"src/posix_thread.c",
|
||||
"src/posix_time.c",
|
||||
"src/xkb_unicode.c",
|
||||
};
|
||||
|
||||
const linux_wl_sources = [_][]const u8{
|
||||
"src/wl_init.c",
|
||||
"src/wl_monitor.c",
|
||||
"src/wl_window.c",
|
||||
};
|
||||
|
||||
const linux_x11_sources = [_][]const u8{
|
||||
"src/glx_context.c",
|
||||
"src/x11_init.c",
|
||||
"src/x11_monitor.c",
|
||||
"src/x11_window.c",
|
||||
};
|
||||
|
||||
const windows_sources = [_][]const u8{
|
||||
"src/wgl_context.c",
|
||||
"src/win32_init.c",
|
||||
"src/win32_joystick.c",
|
||||
"src/win32_module.c",
|
||||
"src/win32_monitor.c",
|
||||
"src/win32_thread.c",
|
||||
"src/win32_time.c",
|
||||
"src/win32_window.c",
|
||||
};
|
||||
|
||||
const macos_sources = [_][]const u8{
|
||||
// C sources
|
||||
"src/cocoa_time.c",
|
||||
"src/posix_module.c",
|
||||
"src/posix_thread.c",
|
||||
|
||||
// ObjC sources
|
||||
"src/cocoa_init.m",
|
||||
"src/cocoa_joystick.m",
|
||||
"src/cocoa_monitor.m",
|
||||
"src/cocoa_window.m",
|
||||
"src/nsgl_context.m",
|
||||
};
|
15
pkg/glfw/build.zig.zon
Normal file
@@ -0,0 +1,15 @@
|
||||
.{
|
||||
.name = .glfw,
|
||||
.version = "3.4.0",
|
||||
.fingerprint = 0x3bbe0a5c667e2c62,
|
||||
.paths = .{""},
|
||||
.dependencies = .{
|
||||
.glfw = .{
|
||||
.url = "https://github.com/glfw/glfw/archive/e7ea71be039836da3a98cea55ae5569cb5eb885c.tar.gz",
|
||||
.hash = "N-V-__8AAMrJSwAUGb9-vTzkNR-5LXS81MR__ZRVfF3tWgG6",
|
||||
.lazy = true,
|
||||
},
|
||||
|
||||
.apple_sdk = .{ .path = "../apple-sdk" },
|
||||
},
|
||||
}
|
6
pkg/glfw/c.zig
Normal file
@@ -0,0 +1,6 @@
|
||||
pub const c = @cImport({
|
||||
// Must be uncommented for vulkan.zig to work
|
||||
// @cDefine("GLFW_INCLUDE_VULKAN", "1");
|
||||
@cDefine("GLFW_INCLUDE_NONE", "1");
|
||||
@cInclude("GLFW/glfw3.h");
|
||||
});
|
71
pkg/glfw/clipboard.zig
Normal file
@@ -0,0 +1,71 @@
|
||||
const std = @import("std");
|
||||
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
const internal_debug = @import("internal_debug.zig");
|
||||
|
||||
/// Sets the clipboard to the specified string.
|
||||
///
|
||||
/// This function sets the system clipboard to the specified, UTF-8 encoded string.
|
||||
///
|
||||
/// @param[in] string A UTF-8 encoded string.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.PlatformError.
|
||||
///
|
||||
/// @pointer_lifetime The specified string is copied before this function returns.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: clipboard, glfwGetClipboardString
|
||||
pub inline fn setClipboardString(value: [*:0]const u8) void {
|
||||
internal_debug.assertInitialized();
|
||||
c.glfwSetClipboardString(null, value);
|
||||
}
|
||||
|
||||
/// Returns the contents of the clipboard as a string.
|
||||
///
|
||||
/// This function returns the contents of the system clipboard, if it contains or is convertible to
|
||||
/// a UTF-8 encoded string. If the clipboard is empty or if its contents cannot be converted,
|
||||
/// glfw.ErrorCode.FormatUnavailable is returned.
|
||||
///
|
||||
/// @return The contents of the clipboard as a UTF-8 encoded string.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.FormatUnavailable and glfw.ErrorCode.PlatformError.
|
||||
/// null is returned in the event of an error.
|
||||
///
|
||||
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
|
||||
/// yourself. It is valid until the next call to glfw.getClipboardString or glfw.setClipboardString
|
||||
/// or until the library is terminated.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: clipboard, glfwSetClipboardString
|
||||
pub inline fn getClipboardString() ?[:0]const u8 {
|
||||
internal_debug.assertInitialized();
|
||||
if (c.glfwGetClipboardString(null)) |c_str| return std.mem.span(@as([*:0]const u8, @ptrCast(c_str)));
|
||||
return null;
|
||||
}
|
||||
|
||||
test "setClipboardString" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
glfw.setClipboardString("hello mach");
|
||||
}
|
||||
|
||||
test "getClipboardString" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
_ = glfw.getClipboardString();
|
||||
}
|
338
pkg/glfw/errors.zig
Normal file
@@ -0,0 +1,338 @@
|
||||
//! Errors
|
||||
|
||||
const testing = @import("std").testing;
|
||||
const mem = @import("std").mem;
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
/// Errors that GLFW can produce.
|
||||
pub const ErrorCode = error{
|
||||
/// GLFW has not been initialized.
|
||||
///
|
||||
/// This occurs if a GLFW function was called that must not be called unless the library is
|
||||
/// initialized.
|
||||
NotInitialized,
|
||||
|
||||
/// No context is current for this thread.
|
||||
///
|
||||
/// This occurs if a GLFW function was called that needs and operates on the current OpenGL or
|
||||
/// OpenGL ES context but no context is current on the calling thread. One such function is
|
||||
/// glfw.SwapInterval.
|
||||
NoCurrentContext,
|
||||
|
||||
/// One of the arguments to the function was an invalid enum value.
|
||||
///
|
||||
/// One of the arguments to the function was an invalid enum value, for example requesting
|
||||
/// glfw.red_bits with glfw.getWindowAttrib.
|
||||
InvalidEnum,
|
||||
|
||||
/// One of the arguments to the function was an invalid value.
|
||||
///
|
||||
/// One of the arguments to the function was an invalid value, for example requesting a
|
||||
/// non-existent OpenGL or OpenGL ES version like 2.7.
|
||||
///
|
||||
/// Requesting a valid but unavailable OpenGL or OpenGL ES version will instead result in a
|
||||
/// glfw.ErrorCode.VersionUnavailable error.
|
||||
InvalidValue,
|
||||
|
||||
/// A memory allocation failed.
|
||||
OutOfMemory,
|
||||
|
||||
/// GLFW could not find support for the requested API on the system.
|
||||
///
|
||||
/// The installed graphics driver does not support the requested API, or does not support it
|
||||
/// via the chosen context creation API. Below are a few examples.
|
||||
///
|
||||
/// Some pre-installed Windows graphics drivers do not support OpenGL. AMD only supports
|
||||
/// OpenGL ES via EGL, while Nvidia and Intel only support it via a WGL or GLX extension. macOS
|
||||
/// does not provide OpenGL ES at all. The Mesa EGL, OpenGL and OpenGL ES libraries do not
|
||||
/// interface with the Nvidia binary driver. Older graphics drivers do not support Vulkan.
|
||||
APIUnavailable,
|
||||
|
||||
/// The requested OpenGL or OpenGL ES version (including any requested context or framebuffer
|
||||
/// hints) is not available on this machine.
|
||||
///
|
||||
/// The machine does not support your requirements. If your application is sufficiently
|
||||
/// flexible, downgrade your requirements and try again. Otherwise, inform the user that their
|
||||
/// machine does not match your requirements.
|
||||
///
|
||||
/// Future invalid OpenGL and OpenGL ES versions, for example OpenGL 4.8 if 5.0 comes out
|
||||
/// before the 4.x series gets that far, also fail with this error and not glfw.ErrorCode.InvalidValue,
|
||||
/// because GLFW cannot know what future versions will exist.
|
||||
VersionUnavailable,
|
||||
|
||||
/// A platform-specific error occurred that does not match any of the more specific categories.
|
||||
///
|
||||
/// A bug or configuration error in GLFW, the underlying operating system or its drivers, or a
|
||||
/// lack of required resources. Report the issue to our [issue tracker](https://github.com/glfw/glfw/issues).
|
||||
PlatformError,
|
||||
|
||||
/// The requested format is not supported or available.
|
||||
///
|
||||
/// If emitted during window creation, the requested pixel format is not supported.
|
||||
///
|
||||
/// If emitted when querying the clipboard, the contents of the clipboard could not be
|
||||
/// converted to the requested format.
|
||||
///
|
||||
/// If emitted during window creation, one or more hard constraints did not match any of the
|
||||
/// available pixel formats. If your application is sufficiently flexible, downgrade your
|
||||
/// requirements and try again. Otherwise, inform the user that their machine does not match
|
||||
/// your requirements.
|
||||
///
|
||||
/// If emitted when querying the clipboard, ignore the error or report it to the user, as
|
||||
/// appropriate.
|
||||
FormatUnavailable,
|
||||
|
||||
/// The specified window does not have an OpenGL or OpenGL ES context.
|
||||
///
|
||||
/// A window that does not have an OpenGL or OpenGL ES context was passed to a function that
|
||||
/// requires it to have one.
|
||||
NoWindowContext,
|
||||
|
||||
/// The specified cursor shape is not available.
|
||||
///
|
||||
/// The specified standard cursor shape is not available, either because the
|
||||
/// current platform cursor theme does not provide it or because it is not
|
||||
/// available on the platform.
|
||||
///
|
||||
/// analysis: Platform or system settings limitation. Pick another standard cursor shape or
|
||||
/// create a custom cursor.
|
||||
CursorUnavailable,
|
||||
|
||||
/// The requested feature is not provided by the platform.
|
||||
///
|
||||
/// The requested feature is not provided by the platform, so GLFW is unable to
|
||||
/// implement it. The documentation for each function notes if it could emit
|
||||
/// this error.
|
||||
///
|
||||
/// analysis: Platform or platform version limitation. The error can be ignored
|
||||
/// unless the feature is critical to the application.
|
||||
///
|
||||
/// A function call that emits this error has no effect other than the error and
|
||||
/// updating any existing out parameters.
|
||||
///
|
||||
FeatureUnavailable,
|
||||
|
||||
/// The requested feature is not implemented for the platform.
|
||||
///
|
||||
/// The requested feature has not yet been implemented in GLFW for this platform.
|
||||
///
|
||||
/// analysis: An incomplete implementation of GLFW for this platform, hopefully
|
||||
/// fixed in a future release. The error can be ignored unless the feature is
|
||||
/// critical to the application.
|
||||
///
|
||||
/// A function call that emits this error has no effect other than the error and
|
||||
/// updating any existing out parameters.
|
||||
///
|
||||
FeatureUnimplemented,
|
||||
|
||||
/// Platform unavailable or no matching platform was found.
|
||||
///
|
||||
/// If emitted during initialization, no matching platform was found. If glfw.InitHint.platform
|
||||
/// is set to `.any_platform`, GLFW could not detect any of the platforms supported by this
|
||||
/// library binary, except for the Null platform. If set to a specific platform, it is either
|
||||
/// not supported by this library binary or GLFW was not able to detect it.
|
||||
///
|
||||
/// If emitted by a native access function, GLFW was initialized for a different platform
|
||||
/// than the function is for.
|
||||
///
|
||||
/// analysis: Failure to detect any platform usually only happens on non-macOS Unix
|
||||
/// systems, either when no window system is running or the program was run from
|
||||
/// a terminal that does not have the necessary environment variables. Fall back to
|
||||
/// a different platform if possible or notify the user that no usable platform was
|
||||
/// detected.
|
||||
///
|
||||
/// Failure to detect a specific platform may have the same cause as above or be because
|
||||
/// support for that platform was not compiled in. Call glfw.platformSupported to
|
||||
/// check whether a specific platform is supported by a library binary.
|
||||
///
|
||||
PlatformUnavailable,
|
||||
};
|
||||
|
||||
/// An error produced by GLFW and the description associated with it.
|
||||
pub const Error = struct {
|
||||
error_code: ErrorCode,
|
||||
description: [:0]const u8,
|
||||
};
|
||||
|
||||
fn convertError(e: c_int) ErrorCode!void {
|
||||
return switch (e) {
|
||||
c.GLFW_NO_ERROR => {},
|
||||
c.GLFW_NOT_INITIALIZED => ErrorCode.NotInitialized,
|
||||
c.GLFW_NO_CURRENT_CONTEXT => ErrorCode.NoCurrentContext,
|
||||
c.GLFW_INVALID_ENUM => ErrorCode.InvalidEnum,
|
||||
c.GLFW_INVALID_VALUE => ErrorCode.InvalidValue,
|
||||
c.GLFW_OUT_OF_MEMORY => ErrorCode.OutOfMemory,
|
||||
c.GLFW_API_UNAVAILABLE => ErrorCode.APIUnavailable,
|
||||
c.GLFW_VERSION_UNAVAILABLE => ErrorCode.VersionUnavailable,
|
||||
c.GLFW_PLATFORM_ERROR => ErrorCode.PlatformError,
|
||||
c.GLFW_FORMAT_UNAVAILABLE => ErrorCode.FormatUnavailable,
|
||||
c.GLFW_NO_WINDOW_CONTEXT => ErrorCode.NoWindowContext,
|
||||
c.GLFW_CURSOR_UNAVAILABLE => ErrorCode.CursorUnavailable,
|
||||
c.GLFW_FEATURE_UNAVAILABLE => ErrorCode.FeatureUnavailable,
|
||||
c.GLFW_FEATURE_UNIMPLEMENTED => ErrorCode.FeatureUnimplemented,
|
||||
c.GLFW_PLATFORM_UNAVAILABLE => ErrorCode.PlatformUnavailable,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Clears the last error and the error description pointer for the calling thread. Does nothing if
|
||||
/// no error has occurred since the last call.
|
||||
///
|
||||
/// @remark This function may be called before @ref glfwInit.
|
||||
///
|
||||
/// @thread_safety This function may be called from any thread.
|
||||
pub inline fn clearError() void {
|
||||
_ = c.glfwGetError(null);
|
||||
}
|
||||
|
||||
/// Returns and clears the last error for the calling thread.
|
||||
///
|
||||
/// This function returns and clears the error code of the last error that occurred on the calling
|
||||
/// thread, along with a UTF-8 encoded human-readable description of it. If no error has occurred
|
||||
/// since the last call, it returns GLFW_NO_ERROR (zero) and the description pointer is set to
|
||||
/// `NULL`.
|
||||
///
|
||||
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
|
||||
/// yourself. It is guaranteed to be valid only until the next error occurs or the library is
|
||||
/// terminated.
|
||||
///
|
||||
/// @remark This function may be called before @ref glfwInit.
|
||||
///
|
||||
/// @thread_safety This function may be called from any thread.
|
||||
pub inline fn getError() ?Error {
|
||||
var desc: [*c]const u8 = null;
|
||||
convertError(c.glfwGetError(&desc)) catch |error_code| {
|
||||
return .{
|
||||
.error_code = error_code,
|
||||
.description = mem.sliceTo(desc, 0),
|
||||
};
|
||||
};
|
||||
return null;
|
||||
}
|
||||
|
||||
pub inline fn mustGetError() Error {
|
||||
return getError() orelse {
|
||||
@panic("glfw: mustGetError called but no error is present");
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns and clears the last error for the calling thread.
|
||||
///
|
||||
/// This function returns and clears the error code of the last error that occurred on the calling
|
||||
/// thread. If no error has occurred since the last call, it returns GLFW_NO_ERROR (zero).
|
||||
///
|
||||
/// @return The last error code for the calling thread, or @ref GLFW_NO_ERROR (zero).
|
||||
///
|
||||
/// @remark This function may be called before @ref glfwInit.
|
||||
///
|
||||
/// @thread_safety This function may be called from any thread.
|
||||
pub inline fn getErrorCode() ErrorCode!void {
|
||||
return convertError(c.glfwGetError(null));
|
||||
}
|
||||
|
||||
/// Returns and clears the last error code for the calling thread. If no error is present, this
|
||||
/// function panics.
|
||||
pub inline fn mustGetErrorCode() ErrorCode {
|
||||
try getErrorCode();
|
||||
@panic("glfw: mustGetErrorCode called but no error is present");
|
||||
}
|
||||
|
||||
/// Returns and clears the last error description for the calling thread.
|
||||
///
|
||||
/// This function returns a UTF-8 encoded human-readable description of the last error that occured
|
||||
/// on the calling thread. If no error has occurred since the last call, it returns null.
|
||||
///
|
||||
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
|
||||
/// yourself. It is guaranteed to be valid only until the next error occurs or the library is
|
||||
/// terminated.
|
||||
///
|
||||
/// @remark This function may be called before @ref glfwInit.
|
||||
///
|
||||
/// @thread_safety This function may be called from any thread.
|
||||
pub inline fn getErrorString() ?[:0]const u8 {
|
||||
var desc: [*c]const u8 = null;
|
||||
const error_code = c.glfwGetError(&desc);
|
||||
if (error_code != c.GLFW_NO_ERROR) {
|
||||
return mem.sliceTo(desc, 0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Returns and clears the last error description for the calling thread. If no error is present,
|
||||
/// this function panics.
|
||||
pub inline fn mustGetErrorString() [:0]const u8 {
|
||||
return getErrorString() orelse {
|
||||
@panic("glfw: mustGetErrorString called but no error is present");
|
||||
};
|
||||
}
|
||||
|
||||
/// Sets the error callback.
|
||||
///
|
||||
/// This function sets the error callback, which is called with an error code
|
||||
/// and a human-readable description each time a GLFW error occurs.
|
||||
///
|
||||
/// The error code is set before the callback is called. Calling @ref
|
||||
/// glfwGetError from the error callback will return the same value as the error
|
||||
/// code argument.
|
||||
///
|
||||
/// The error callback is called on the thread where the error occurred. If you
|
||||
/// are using GLFW from multiple threads, your error callback needs to be
|
||||
/// written accordingly.
|
||||
///
|
||||
/// Because the description string may have been generated specifically for that
|
||||
/// error, it is not guaranteed to be valid after the callback has returned. If
|
||||
/// you wish to use it after the callback returns, you need to make a copy.
|
||||
///
|
||||
/// Once set, the error callback remains set even after the library has been
|
||||
/// terminated.
|
||||
///
|
||||
/// @param[in] callback The new callback, or `NULL` to remove the currently set
|
||||
/// callback.
|
||||
///
|
||||
/// @callback_param `error_code` An error code. Future releases may add more error codes.
|
||||
/// @callback_param `description` A UTF-8 encoded string describing the error.
|
||||
///
|
||||
/// @errors None.
|
||||
///
|
||||
/// @remark This function may be called before @ref glfwInit.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
pub fn setErrorCallback(comptime callback: ?fn (error_code: ErrorCode, description: [:0]const u8) void) void {
|
||||
if (callback) |user_callback| {
|
||||
const CWrapper = struct {
|
||||
pub fn errorCallbackWrapper(err_int: c_int, c_description: [*c]const u8) callconv(.C) void {
|
||||
convertError(err_int) catch |error_code| {
|
||||
user_callback(error_code, mem.sliceTo(c_description, 0));
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
_ = c.glfwSetErrorCallback(CWrapper.errorCallbackWrapper);
|
||||
return;
|
||||
}
|
||||
|
||||
_ = c.glfwSetErrorCallback(null);
|
||||
}
|
||||
|
||||
test "set error callback" {
|
||||
const TestStruct = struct {
|
||||
pub fn callback(_: ErrorCode, _: [:0]const u8) void {}
|
||||
};
|
||||
setErrorCallback(TestStruct.callback);
|
||||
}
|
||||
|
||||
test "error string" {
|
||||
try testing.expect(getErrorString() == null);
|
||||
}
|
||||
|
||||
test "error code" {
|
||||
try getErrorCode();
|
||||
}
|
||||
|
||||
test "error code and string" {
|
||||
try testing.expect(getError() == null);
|
||||
}
|
||||
|
||||
test "clear error" {
|
||||
clearError();
|
||||
}
|
16
pkg/glfw/gamepad_axis.zig
Normal file
@@ -0,0 +1,16 @@
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
/// Gamepad axes.
|
||||
///
|
||||
/// See glfw.getGamepadState for how these are used.
|
||||
pub const GamepadAxis = enum(c_int) {
|
||||
left_x = c.GLFW_GAMEPAD_AXIS_LEFT_X,
|
||||
left_y = c.GLFW_GAMEPAD_AXIS_LEFT_Y,
|
||||
right_x = c.GLFW_GAMEPAD_AXIS_RIGHT_X,
|
||||
right_y = c.GLFW_GAMEPAD_AXIS_RIGHT_Y,
|
||||
left_trigger = c.GLFW_GAMEPAD_AXIS_LEFT_TRIGGER,
|
||||
right_trigger = c.GLFW_GAMEPAD_AXIS_RIGHT_TRIGGER,
|
||||
};
|
||||
|
||||
/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden.
|
||||
pub const last = GamepadAxis.right_trigger;
|
37
pkg/glfw/gamepad_button.zig
Normal file
@@ -0,0 +1,37 @@
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
/// Gamepad buttons.
|
||||
///
|
||||
/// See glfw.getGamepadState for how these are used.
|
||||
pub const GamepadButton = enum(c_int) {
|
||||
a = c.GLFW_GAMEPAD_BUTTON_A,
|
||||
b = c.GLFW_GAMEPAD_BUTTON_B,
|
||||
x = c.GLFW_GAMEPAD_BUTTON_X,
|
||||
y = c.GLFW_GAMEPAD_BUTTON_Y,
|
||||
left_bumper = c.GLFW_GAMEPAD_BUTTON_LEFT_BUMPER,
|
||||
right_bumper = c.GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER,
|
||||
back = c.GLFW_GAMEPAD_BUTTON_BACK,
|
||||
start = c.GLFW_GAMEPAD_BUTTON_START,
|
||||
guide = c.GLFW_GAMEPAD_BUTTON_GUIDE,
|
||||
left_thumb = c.GLFW_GAMEPAD_BUTTON_LEFT_THUMB,
|
||||
right_thumb = c.GLFW_GAMEPAD_BUTTON_RIGHT_THUMB,
|
||||
dpad_up = c.GLFW_GAMEPAD_BUTTON_DPAD_UP,
|
||||
dpad_right = c.GLFW_GAMEPAD_BUTTON_DPAD_RIGHT,
|
||||
dpad_down = c.GLFW_GAMEPAD_BUTTON_DPAD_DOWN,
|
||||
dpad_left = c.GLFW_GAMEPAD_BUTTON_DPAD_LEFT,
|
||||
};
|
||||
|
||||
/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden.
|
||||
pub const last = GamepadButton.dpad_left;
|
||||
|
||||
/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden.
|
||||
pub const cross = GamepadButton.a;
|
||||
|
||||
/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden.
|
||||
pub const circle = GamepadButton.b;
|
||||
|
||||
/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden.
|
||||
pub const square = GamepadButton.x;
|
||||
|
||||
/// Not in the GamepadAxis enumeration as it is a duplicate value which is forbidden.
|
||||
pub const triangle = GamepadButton.y;
|
100
pkg/glfw/hat.zig
Normal file
@@ -0,0 +1,100 @@
|
||||
const c = @import("c.zig").c;
|
||||
|
||||
// must be in sync with GLFW C constants in hat state group, search for "@defgroup hat_state Joystick hat states"
|
||||
/// A bitmask of all Joystick hat states
|
||||
///
|
||||
/// See glfw.Joystick.getHats for how these are used.
|
||||
pub const Hat = packed struct(u8) {
|
||||
up: bool = false,
|
||||
right: bool = false,
|
||||
down: bool = false,
|
||||
left: bool = false,
|
||||
_padding: u4 = 0,
|
||||
|
||||
pub inline fn centered(self: Hat) bool {
|
||||
return self.up == false and self.right == false and self.down == false and self.left == false;
|
||||
}
|
||||
|
||||
inline fn verifyIntType(comptime IntType: type) void {
|
||||
comptime {
|
||||
switch (@import("shims.zig").typeInfo(IntType)) {
|
||||
.int => {},
|
||||
else => @compileError("Int was not of int type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn toInt(self: Hat, comptime IntType: type) IntType {
|
||||
verifyIntType(IntType);
|
||||
return @as(IntType, @intCast(@as(u8, @bitCast(self))));
|
||||
}
|
||||
|
||||
pub inline fn fromInt(flags: anytype) Hat {
|
||||
verifyIntType(@TypeOf(flags));
|
||||
return @as(Hat, @bitCast(@as(u8, @intCast(flags))));
|
||||
}
|
||||
};
|
||||
|
||||
/// Holds all GLFW hat values in their raw form.
|
||||
pub const RawHat = struct {
|
||||
pub const centered = c.GLFW_HAT_CENTERED;
|
||||
pub const up = c.GLFW_HAT_UP;
|
||||
pub const right = c.GLFW_HAT_RIGHT;
|
||||
pub const down = c.GLFW_HAT_DOWN;
|
||||
pub const left = c.GLFW_HAT_LEFT;
|
||||
|
||||
pub const right_up = right | up;
|
||||
pub const right_down = right | down;
|
||||
pub const left_up = left | up;
|
||||
pub const left_down = left | down;
|
||||
};
|
||||
|
||||
test "from int, single" {
|
||||
const std = @import("std");
|
||||
|
||||
try std.testing.expectEqual(Hat{
|
||||
.up = true,
|
||||
.right = false,
|
||||
.down = false,
|
||||
.left = false,
|
||||
._padding = 0,
|
||||
}, Hat.fromInt(RawHat.up));
|
||||
}
|
||||
|
||||
test "from int, multi" {
|
||||
const std = @import("std");
|
||||
|
||||
try std.testing.expectEqual(Hat{
|
||||
.up = true,
|
||||
.right = false,
|
||||
.down = true,
|
||||
.left = true,
|
||||
._padding = 0,
|
||||
}, Hat.fromInt(RawHat.up | RawHat.down | RawHat.left));
|
||||
}
|
||||
|
||||
test "to int, single" {
|
||||
const std = @import("std");
|
||||
|
||||
var v = Hat{
|
||||
.up = true,
|
||||
.right = false,
|
||||
.down = false,
|
||||
.left = false,
|
||||
._padding = 0,
|
||||
};
|
||||
try std.testing.expectEqual(v.toInt(c_int), RawHat.up);
|
||||
}
|
||||
|
||||
test "to int, multi" {
|
||||
const std = @import("std");
|
||||
|
||||
var v = Hat{
|
||||
.up = true,
|
||||
.right = false,
|
||||
.down = true,
|
||||
.left = true,
|
||||
._padding = 0,
|
||||
};
|
||||
try std.testing.expectEqual(v.toInt(c_int), RawHat.up | RawHat.down | RawHat.left);
|
||||
}
|
14
pkg/glfw/internal_debug.zig
Normal file
@@ -0,0 +1,14 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const is_debug = builtin.mode == .Debug;
|
||||
var glfw_initialized = if (is_debug) false else @as(void, {});
|
||||
pub inline fn toggleInitialized() void {
|
||||
if (is_debug) glfw_initialized = !glfw_initialized;
|
||||
}
|
||||
pub inline fn assertInitialized() void {
|
||||
if (is_debug) std.debug.assert(glfw_initialized);
|
||||
}
|
||||
pub inline fn assumeInitialized() void {
|
||||
if (is_debug) glfw_initialized = true;
|
||||
}
|
266
pkg/glfw/key.zig
Normal file
@@ -0,0 +1,266 @@
|
||||
//! Keyboard key IDs.
|
||||
//!
|
||||
//! See glfw.setKeyCallback for how these are used.
|
||||
//!
|
||||
//! These key codes are inspired by the _USB HID Usage Tables v1.12_ (p. 53-60), but re-arranged to
|
||||
//! map to 7-bit ASCII for printable keys (function keys are put in the 256+ range).
|
||||
//!
|
||||
//! The naming of the key codes follow these rules:
|
||||
//!
|
||||
//! - The US keyboard layout is used
|
||||
//! - Names of printable alphanumeric characters are used (e.g. "a", "r", "three", etc.)
|
||||
//! - For non-alphanumeric characters, Unicode:ish names are used (e.g. "comma", "left_bracket",
|
||||
//! etc.). Note that some names do not correspond to the Unicode standard (usually for brevity)
|
||||
//! - Keys that lack a clear US mapping are named "world_x"
|
||||
//! - For non-printable keys, custom names are used (e.g. "F4", "backspace", etc.)
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
const cc = @import("c.zig").c;
|
||||
|
||||
const internal_debug = @import("internal_debug.zig");
|
||||
|
||||
/// enum containing all glfw keys
|
||||
pub const Key = enum(c_int) {
|
||||
/// The unknown key
|
||||
unknown = cc.GLFW_KEY_UNKNOWN,
|
||||
|
||||
/// Printable keys
|
||||
space = cc.GLFW_KEY_SPACE,
|
||||
apostrophe = cc.GLFW_KEY_APOSTROPHE,
|
||||
comma = cc.GLFW_KEY_COMMA,
|
||||
minus = cc.GLFW_KEY_MINUS,
|
||||
period = cc.GLFW_KEY_PERIOD,
|
||||
slash = cc.GLFW_KEY_SLASH,
|
||||
zero = cc.GLFW_KEY_0,
|
||||
one = cc.GLFW_KEY_1,
|
||||
two = cc.GLFW_KEY_2,
|
||||
three = cc.GLFW_KEY_3,
|
||||
four = cc.GLFW_KEY_4,
|
||||
five = cc.GLFW_KEY_5,
|
||||
six = cc.GLFW_KEY_6,
|
||||
seven = cc.GLFW_KEY_7,
|
||||
eight = cc.GLFW_KEY_8,
|
||||
nine = cc.GLFW_KEY_9,
|
||||
semicolon = cc.GLFW_KEY_SEMICOLON,
|
||||
equal = cc.GLFW_KEY_EQUAL,
|
||||
a = cc.GLFW_KEY_A,
|
||||
b = cc.GLFW_KEY_B,
|
||||
c = cc.GLFW_KEY_C,
|
||||
d = cc.GLFW_KEY_D,
|
||||
e = cc.GLFW_KEY_E,
|
||||
f = cc.GLFW_KEY_F,
|
||||
g = cc.GLFW_KEY_G,
|
||||
h = cc.GLFW_KEY_H,
|
||||
i = cc.GLFW_KEY_I,
|
||||
j = cc.GLFW_KEY_J,
|
||||
k = cc.GLFW_KEY_K,
|
||||
l = cc.GLFW_KEY_L,
|
||||
m = cc.GLFW_KEY_M,
|
||||
n = cc.GLFW_KEY_N,
|
||||
o = cc.GLFW_KEY_O,
|
||||
p = cc.GLFW_KEY_P,
|
||||
q = cc.GLFW_KEY_Q,
|
||||
r = cc.GLFW_KEY_R,
|
||||
s = cc.GLFW_KEY_S,
|
||||
t = cc.GLFW_KEY_T,
|
||||
u = cc.GLFW_KEY_U,
|
||||
v = cc.GLFW_KEY_V,
|
||||
w = cc.GLFW_KEY_W,
|
||||
x = cc.GLFW_KEY_X,
|
||||
y = cc.GLFW_KEY_Y,
|
||||
z = cc.GLFW_KEY_Z,
|
||||
left_bracket = cc.GLFW_KEY_LEFT_BRACKET,
|
||||
backslash = cc.GLFW_KEY_BACKSLASH,
|
||||
right_bracket = cc.GLFW_KEY_RIGHT_BRACKET,
|
||||
grave_accent = cc.GLFW_KEY_GRAVE_ACCENT,
|
||||
world_1 = cc.GLFW_KEY_WORLD_1, // non-US #1
|
||||
world_2 = cc.GLFW_KEY_WORLD_2, // non-US #2
|
||||
|
||||
// Function keys
|
||||
escape = cc.GLFW_KEY_ESCAPE,
|
||||
enter = cc.GLFW_KEY_ENTER,
|
||||
tab = cc.GLFW_KEY_TAB,
|
||||
backspace = cc.GLFW_KEY_BACKSPACE,
|
||||
insert = cc.GLFW_KEY_INSERT,
|
||||
delete = cc.GLFW_KEY_DELETE,
|
||||
right = cc.GLFW_KEY_RIGHT,
|
||||
left = cc.GLFW_KEY_LEFT,
|
||||
down = cc.GLFW_KEY_DOWN,
|
||||
up = cc.GLFW_KEY_UP,
|
||||
page_up = cc.GLFW_KEY_PAGE_UP,
|
||||
page_down = cc.GLFW_KEY_PAGE_DOWN,
|
||||
home = cc.GLFW_KEY_HOME,
|
||||
end = cc.GLFW_KEY_END,
|
||||
caps_lock = cc.GLFW_KEY_CAPS_LOCK,
|
||||
scroll_lock = cc.GLFW_KEY_SCROLL_LOCK,
|
||||
num_lock = cc.GLFW_KEY_NUM_LOCK,
|
||||
print_screen = cc.GLFW_KEY_PRINT_SCREEN,
|
||||
pause = cc.GLFW_KEY_PAUSE,
|
||||
F1 = cc.GLFW_KEY_F1,
|
||||
F2 = cc.GLFW_KEY_F2,
|
||||
F3 = cc.GLFW_KEY_F3,
|
||||
F4 = cc.GLFW_KEY_F4,
|
||||
F5 = cc.GLFW_KEY_F5,
|
||||
F6 = cc.GLFW_KEY_F6,
|
||||
F7 = cc.GLFW_KEY_F7,
|
||||
F8 = cc.GLFW_KEY_F8,
|
||||
F9 = cc.GLFW_KEY_F9,
|
||||
F10 = cc.GLFW_KEY_F10,
|
||||
F11 = cc.GLFW_KEY_F11,
|
||||
F12 = cc.GLFW_KEY_F12,
|
||||
F13 = cc.GLFW_KEY_F13,
|
||||
F14 = cc.GLFW_KEY_F14,
|
||||
F15 = cc.GLFW_KEY_F15,
|
||||
F16 = cc.GLFW_KEY_F16,
|
||||
F17 = cc.GLFW_KEY_F17,
|
||||
F18 = cc.GLFW_KEY_F18,
|
||||
F19 = cc.GLFW_KEY_F19,
|
||||
F20 = cc.GLFW_KEY_F20,
|
||||
F21 = cc.GLFW_KEY_F21,
|
||||
F22 = cc.GLFW_KEY_F22,
|
||||
F23 = cc.GLFW_KEY_F23,
|
||||
F24 = cc.GLFW_KEY_F24,
|
||||
F25 = cc.GLFW_KEY_F25,
|
||||
kp_0 = cc.GLFW_KEY_KP_0,
|
||||
kp_1 = cc.GLFW_KEY_KP_1,
|
||||
kp_2 = cc.GLFW_KEY_KP_2,
|
||||
kp_3 = cc.GLFW_KEY_KP_3,
|
||||
kp_4 = cc.GLFW_KEY_KP_4,
|
||||
kp_5 = cc.GLFW_KEY_KP_5,
|
||||
kp_6 = cc.GLFW_KEY_KP_6,
|
||||
kp_7 = cc.GLFW_KEY_KP_7,
|
||||
kp_8 = cc.GLFW_KEY_KP_8,
|
||||
kp_9 = cc.GLFW_KEY_KP_9,
|
||||
kp_decimal = cc.GLFW_KEY_KP_DECIMAL,
|
||||
kp_divide = cc.GLFW_KEY_KP_DIVIDE,
|
||||
kp_multiply = cc.GLFW_KEY_KP_MULTIPLY,
|
||||
kp_subtract = cc.GLFW_KEY_KP_SUBTRACT,
|
||||
kp_add = cc.GLFW_KEY_KP_ADD,
|
||||
kp_enter = cc.GLFW_KEY_KP_ENTER,
|
||||
kp_equal = cc.GLFW_KEY_KP_EQUAL,
|
||||
left_shift = cc.GLFW_KEY_LEFT_SHIFT,
|
||||
left_control = cc.GLFW_KEY_LEFT_CONTROL,
|
||||
left_alt = cc.GLFW_KEY_LEFT_ALT,
|
||||
left_super = cc.GLFW_KEY_LEFT_SUPER,
|
||||
right_shift = cc.GLFW_KEY_RIGHT_SHIFT,
|
||||
right_control = cc.GLFW_KEY_RIGHT_CONTROL,
|
||||
right_alt = cc.GLFW_KEY_RIGHT_ALT,
|
||||
right_super = cc.GLFW_KEY_RIGHT_SUPER,
|
||||
menu = cc.GLFW_KEY_MENU,
|
||||
|
||||
pub inline fn last() Key {
|
||||
return @as(Key, @enumFromInt(cc.GLFW_KEY_LAST));
|
||||
}
|
||||
|
||||
/// Returns the layout-specific name of the specified printable key.
|
||||
///
|
||||
/// This function returns the name of the specified printable key, encoded as UTF-8. This is
|
||||
/// typically the character that key would produce without any modifier keys, intended for
|
||||
/// displaying key bindings to the user. For dead keys, it is typically the diacritic it would add
|
||||
/// to a character.
|
||||
///
|
||||
/// __Do not use this function__ for text input (see input_char). You will break text input for many
|
||||
/// languages even if it happens to work for yours.
|
||||
///
|
||||
/// If the key is `glfw.key.unknown`, the scancode is used to identify the key, otherwise the
|
||||
/// scancode is ignored. If you specify a non-printable key, or `glfw.key.unknown` and a scancode
|
||||
/// that maps to a non-printable key, this function returns null but does not emit an error.
|
||||
///
|
||||
/// This behavior allows you to always pass in the arguments in the key callback (see input_key)
|
||||
/// without modification.
|
||||
///
|
||||
/// The printable keys are:
|
||||
///
|
||||
/// - `glfw.Key.apostrophe`
|
||||
/// - `glfw.Key.comma`
|
||||
/// - `glfw.Key.minus`
|
||||
/// - `glfw.Key.period`
|
||||
/// - `glfw.Key.slash`
|
||||
/// - `glfw.Key.semicolon`
|
||||
/// - `glfw.Key.equal`
|
||||
/// - `glfw.Key.left_bracket`
|
||||
/// - `glfw.Key.right_bracket`
|
||||
/// - `glfw.Key.backslash`
|
||||
/// - `glfw.Key.world_1`
|
||||
/// - `glfw.Key.world_2`
|
||||
/// - `glfw.Key.0` to `glfw.key.9`
|
||||
/// - `glfw.Key.a` to `glfw.key.z`
|
||||
/// - `glfw.Key.kp_0` to `glfw.key.kp_9`
|
||||
/// - `glfw.Key.kp_decimal`
|
||||
/// - `glfw.Key.kp_divide`
|
||||
/// - `glfw.Key.kp_multiply`
|
||||
/// - `glfw.Key.kp_subtract`
|
||||
/// - `glfw.Key.kp_add`
|
||||
/// - `glfw.Key.kp_equal`
|
||||
///
|
||||
/// Names for printable keys depend on keyboard layout, while names for non-printable keys are the
|
||||
/// same across layouts but depend on the application language and should be localized along with
|
||||
/// other user interface text.
|
||||
///
|
||||
/// @param[in] key The key to query, or `glfw.key.unknown`.
|
||||
/// @param[in] scancode The scancode of the key to query.
|
||||
/// @return The UTF-8 encoded, layout-specific name of the key, or null.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.PlatformError.
|
||||
/// Also returns null in the event of an error.
|
||||
///
|
||||
/// The contents of the returned string may change when a keyboard layout change event is received.
|
||||
///
|
||||
/// @pointer_lifetime The returned string is allocated and freed by GLFW. You should not free it
|
||||
/// yourself. It is valid until the library is terminated.
|
||||
///
|
||||
/// @thread_safety This function must only be called from the main thread.
|
||||
///
|
||||
/// see also: input_key_name
|
||||
pub inline fn getName(self: Key, scancode: i32) ?[:0]const u8 {
|
||||
internal_debug.assertInitialized();
|
||||
const name_opt = cc.glfwGetKeyName(@intFromEnum(self), @as(c_int, @intCast(scancode)));
|
||||
return if (name_opt) |name|
|
||||
std.mem.span(@as([*:0]const u8, @ptrCast(name)))
|
||||
else
|
||||
null;
|
||||
}
|
||||
|
||||
/// Returns the platform-specific scancode of the specified key.
|
||||
///
|
||||
/// This function returns the platform-specific scancode of the specified key.
|
||||
///
|
||||
/// If the key is `glfw.key.UNKNOWN` or does not exist on the keyboard this method will return `-1`.
|
||||
///
|
||||
/// @param[in] key Any named key (see keys).
|
||||
/// @return The platform-specific scancode for the key.
|
||||
///
|
||||
/// Possible errors include glfw.ErrorCode.InvalidEnum and glfw.ErrorCode.PlatformError.
|
||||
/// Additionally returns -1 in the event of an error.
|
||||
///
|
||||
/// @thread_safety This function may be called from any thread.
|
||||
pub inline fn getScancode(self: Key) i32 {
|
||||
internal_debug.assertInitialized();
|
||||
return cc.glfwGetKeyScancode(@intFromEnum(self));
|
||||
}
|
||||
};
|
||||
|
||||
test "getName" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
_ = glfw.Key.a.getName(0);
|
||||
}
|
||||
|
||||
test "getScancode" {
|
||||
const glfw = @import("main.zig");
|
||||
defer glfw.clearError(); // clear any error we generate
|
||||
if (!glfw.init(.{})) {
|
||||
std.log.err("failed to initialize GLFW: {?s}", .{glfw.getErrorString()});
|
||||
std.process.exit(1);
|
||||
}
|
||||
defer glfw.terminate();
|
||||
|
||||
_ = glfw.Key.a.getScancode();
|
||||
}
|