mirror of
https://codeberg.org/fairyglade/ly.git
synced 2026-08-09 15:19:13 +02:00
Compare commits
549 commits
mookums/mu
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a22805d44b |
||
|
|
b6fba46b08 |
||
|
|
7c2ae2738c |
||
|
|
84950136c9 |
||
|
|
d00780d6ce |
||
|
|
014a00d33e |
||
|
|
fb544b3357 |
||
|
|
5ccd91c132 |
||
|
|
78c7c2e2e8 |
||
|
|
a41ebe8f6f |
||
|
|
e4e399cd07 |
||
|
|
018d797ed6 |
||
|
|
54f1ff14ee |
||
|
|
7e6474924c |
||
|
|
26377fd319 |
||
|
|
5fc5fd62a0 |
||
|
|
acb532b5d4 |
||
|
|
e1696a4e73 |
||
|
|
954e90b3bd |
||
|
|
7eae544418 |
||
|
|
32f11cdbe5 |
||
|
|
9b8046e37c |
||
|
|
4513033bcb |
||
|
|
102d1c9a9b |
||
|
|
e0c287306e | ||
|
|
25fad28c34 | ||
|
|
006749b2ec |
||
|
|
cf731dbce1 | ||
|
|
1d272ff74f | ||
|
|
63f0f4931c | ||
|
|
8f4ca8d3f0 |
||
|
|
4d882f7997 |
||
|
|
eeccb7421b | ||
|
|
e833c4bc1d |
||
|
|
e138563874 |
||
|
|
8942e30a9e |
||
|
|
a801d1a569 |
||
|
|
aa00f7c8c7 |
||
|
|
ed783b5a23 | ||
|
|
f683a82f56 | ||
|
|
008d413295 | ||
|
|
a07ca88c74 |
||
|
|
d8c3aec668 |
||
|
|
5b8ebeba6e |
||
|
|
32436d438e | ||
|
|
ace79f76b5 | ||
|
|
35be66e66f |
||
|
|
0ac11065f4 |
||
|
|
9d8ccf6709 |
||
|
|
6f3fdc4708 |
||
|
|
0cee1c039a |
||
|
|
f03aca0379 | ||
|
|
0cd8b2ebfc |
||
|
|
4c066ce564 |
||
|
|
05c1d4bece |
||
|
|
78794b3e10 |
||
|
|
afee1d9194 |
||
|
|
9b1965a3d8 |
||
|
|
741e9e0345 |
||
|
|
de8579854c |
||
|
|
ee3196bab8 |
||
|
|
9ff4ddd129 |
||
|
|
b3830d5bb6 | ||
|
|
b8ae126623 | ||
|
|
4db9295102 |
||
|
|
864f5f2892 |
||
|
|
c50af66407 |
||
|
|
fdf241bed5 | ||
|
|
79eebd8ee0 |
||
|
|
3869bfd2f9 |
||
|
|
5905e054c5 |
||
|
|
807f6d249a |
||
|
|
15cd0c4779 |
||
|
|
51c5c3ee0b |
||
|
|
59c07aa3ba |
||
|
|
80d4b114f3 |
||
|
|
4f45d92ea8 | ||
|
|
5edf5251f6 | ||
|
|
eec83179b9 | ||
|
|
b8048234d9 | ||
|
|
10a873acb9 | ||
|
|
142476041d |
||
|
|
fad683e035 | ||
|
|
e882eea22a |
||
|
|
fe6942d406 | ||
|
|
5b7c7dfdf5 |
||
|
|
074bb0a68a |
||
|
|
e0a3364169 |
||
|
|
7a8d913531 | ||
|
|
984ac596af |
||
|
|
ed486c29d2 |
||
|
|
a6fc5d67e8 |
||
|
|
549576aa3e |
||
|
|
3758b5da1b |
||
|
|
5e1c681385 |
||
|
|
ac78ccc398 |
||
|
|
e548333473 | ||
|
|
60e3380375 |
||
|
|
aa392837bc |
||
|
|
abe72c74ff |
||
|
|
dda56eab37 | ||
|
|
9c50297059 |
||
|
|
acac884cfe |
||
|
|
a89c918c5d |
||
|
|
64539f4342 |
||
|
|
93696a6b30 |
||
|
|
4f26eeada0 |
||
|
|
83e98a185f |
||
|
|
128dcb16f8 |
||
|
|
3a4109eb2d | ||
|
|
7cefff4570 | ||
|
|
f31c55b562 | ||
|
|
9cde291ac7 |
||
|
|
03d976171a |
||
|
|
b01e4afc79 |
||
|
|
5a4605ffb6 |
||
|
|
01dcfa207e |
||
|
|
32d5330efb |
||
|
|
5564fed664 |
||
|
|
7c7aed9cb2 |
||
|
|
57c96a3478 |
||
|
|
6773f74788 |
||
|
|
b389e379fa |
||
|
|
4a72e41e44 | ||
|
|
d268d5bb45 |
||
|
|
f320d3f666 | ||
|
|
70e95f094a |
||
|
|
207b352888 |
||
|
|
99dba44e46 |
||
|
|
d1810d8c98 |
||
|
|
852a602032 |
||
|
|
cf5f62661c |
||
|
|
1db780c7a7 |
||
|
|
941b7e0dae |
||
|
|
e9e2d51261 |
||
|
|
769aefd6e9 |
||
|
|
f678e3bb28 |
||
|
|
f22593f828 |
||
|
|
7bbdebe58b |
||
|
|
bca38856b1 |
||
|
|
8c08359e51 | ||
|
|
fd81da7cbd |
||
|
|
950eeed3ee |
||
|
|
ce0d00771d |
||
|
|
b032c9b296 | ||
|
|
7744745f09 |
||
|
|
11735290b8 |
||
|
|
21fca058e7 |
||
|
|
2b46a81796 |
||
|
|
72f43fbc56 | ||
|
|
b00d6899e5 |
||
|
|
7ce8ff61fe |
||
|
|
1a04a608a1 |
||
|
|
b0dcc12785 |
||
|
|
b672d04dc6 |
||
|
|
2e04ea4d79 |
||
|
|
0c12008327 |
||
|
|
7934060d3b |
||
|
|
a158098df0 |
||
|
|
5bfa1670cc |
||
|
|
a4076b83da | ||
|
|
2eea683078 |
||
|
|
d7f64676ee |
||
|
|
456916f059 |
||
|
|
2b1e4dc6c9 |
||
|
|
94c306758a |
||
|
|
82d24d7725 |
||
|
|
5a51d5ced5 |
||
|
|
135d1e40f6 |
||
|
|
c0c400e0b6 |
||
|
|
9e4147bfb4 |
||
|
|
8e893932f2 |
||
|
|
26e7585b0b |
||
|
|
b1cb576f67 |
||
|
|
add7f25f0d |
||
|
|
2e7bb3eb58 | ||
|
|
e57de5172e | ||
|
|
04d4447273 | ||
|
|
ced8f9bee3 |
||
|
|
c6446db3e2 |
||
|
|
e0692885c5 |
||
|
|
b6d3aa0e1c |
||
|
|
e6966a628c |
||
|
|
cab3f1bfb5 | ||
|
|
a9ff0a6d07 |
||
|
|
92beb24c80 |
||
|
|
7e18d906c4 | ||
|
|
3bfdc75a70 |
||
|
|
6cce221cbf |
||
|
|
1c99574f73 |
||
|
|
b249dba092 |
||
|
|
3365b33d6d |
||
|
|
6cb53b6e38 | ||
|
|
d82fa82a87 |
||
|
|
c2b3d794e8 |
||
|
|
e29bda3250 |
||
|
|
4df2382698 |
||
|
|
3dfb86fe69 |
||
|
|
f9a001b160 |
||
|
|
5235ca47c5 | ||
|
|
a94abf2e69 |
||
|
|
fe354a4809 |
||
|
|
8c964d9ce5 |
||
|
|
392ea6ea63 |
||
|
|
10854e643a |
||
|
|
1980b2e479 | ||
|
|
816be7449f |
||
|
|
f0758d812e |
||
|
|
3c0c84d067 |
||
|
|
cc07c4870a | ||
|
|
68ec85f412 | ||
|
|
1537addd67 | ||
|
|
2da3648179 | ||
|
|
0cf752f3b8 | ||
|
|
15c78c838a |
||
|
|
4a486bd876 |
||
|
|
38173d8557 |
||
|
|
95d1d9378c |
||
|
|
6f62106fcc |
||
|
|
4171e29995 |
||
|
|
80c27224e9 |
||
|
|
106f157a2c | ||
|
|
ec16ad5dfc |
||
|
|
5f22173b91 |
||
|
|
4bc405f239 |
||
|
|
c4b68364ef |
||
|
|
1c05664c85 |
||
|
|
1fbcb10110 |
||
|
|
bfb3f925d9 |
||
|
|
44c8acff16 |
||
|
|
541eae5311 |
||
|
|
3faf3dec42 |
||
|
|
52d29bbd47 |
||
|
|
a3a8f11575 |
||
|
|
02f5aa702d |
||
|
|
ed88458efd |
||
|
|
8df9603188 |
||
|
|
412994775b |
||
|
|
bb669c239c |
||
|
|
657daafec8 |
||
|
|
e36872baa6 |
||
|
|
4738ca81a7 |
||
|
|
1f2453f0fb | ||
|
|
76da16904f |
||
|
|
e577c454da |
||
|
|
09c2cfb74d |
||
|
|
b3f1e91cf6 |
||
|
|
aef1dd9c1a |
||
|
|
3d977d2ff7 |
||
|
|
a34a5a97bd |
||
|
|
1839e4cb44 |
||
|
|
b2f51e5bc8 | ||
|
|
bd335c8c91 |
||
|
|
4f4855b5e9 |
||
|
|
1e2faad0f8 | ||
|
|
cb4f1952cd | ||
|
|
81a17f2904 | ||
|
|
339e39d496 | ||
|
|
44faa263b1 | ||
|
|
7a0520687d |
||
|
|
cee0e0ca4b |
||
|
|
3edd1ff1be |
||
|
|
145ad5142c |
||
|
|
d8b0ae34f3 |
||
|
|
a6535b9152 |
||
|
|
ee97f3b5e1 |
||
|
|
5924db58e1 |
||
|
|
36e220e2ff |
||
|
|
0a9ceca822 |
||
|
|
f9553655a3 |
||
|
|
230874abd1 |
||
|
|
38c3ecd089 |
||
|
|
fec0815161 |
||
|
|
a7d6b06d21 |
||
|
|
1ee8010c24 |
||
|
|
f988bd334b |
||
|
|
aa0222948a |
||
|
|
69d39dc035 |
||
|
|
6d7dbb9f27 |
||
|
|
7cfb947187 |
||
|
|
336847d418 |
||
|
|
ff9b6279d3 |
||
|
|
c7dea3c178 | ||
|
|
d7a4535007 |
||
|
|
73ecac67bf | ||
|
|
7353e75d78 |
||
|
|
cd0accfb28 |
||
|
|
b71789912d | ||
|
|
5bacc8870b |
||
|
|
a7ff18aa16 | ||
|
|
c3d180c213 |
||
|
|
3f891d7f0d |
||
|
|
e404d5bdb3 |
||
|
|
dee055748c |
||
|
|
bd2d1142b2 |
||
|
|
b382d74969 | ||
|
|
4fbbb6f0f2 |
||
|
|
ef64029795 |
||
|
|
71c694e575 |
||
|
|
c37aa6957a |
||
|
|
8377f14509 |
||
|
|
b35c055e7b |
||
|
|
c05c32c5be |
||
|
|
1f0274e797 |
||
|
|
f97d0c27d3 | ||
|
|
d36147a376 | ||
|
|
8030cf5244 | ||
|
|
933e5bdd2d | ||
|
|
a9d85a6925 | ||
|
|
3d3cf84292 | ||
|
|
19c879a201 | ||
|
|
98f59a69cc | ||
|
|
5fb40899e5 | ||
|
|
6933c4db02 | ||
|
|
c3d0864e62 | ||
|
|
e8cb8970fd | ||
|
|
c11194332c | ||
|
|
35265f5e77 | ||
|
|
5c3da10386 | ||
|
|
1d4e32ba82 |
||
|
|
97efac0cd1 |
||
|
|
04920e1b1b |
||
|
|
48e5369f56 |
||
|
|
918e9ad5ac |
||
|
|
ce17d346e8 |
||
|
|
ce0b05fd6e | ||
|
|
99f3ab96ba | ||
|
|
2a8e221e80 | ||
|
|
1c5686ea54 | ||
|
|
ab23631e66 | ||
|
|
1bcbb08202 |
||
|
|
d08b9a916e |
||
|
|
e5eb8b7f6f |
||
|
|
41f4378bfe |
||
|
|
7b81336761 | ||
|
|
3ad0c00380 | ||
|
|
7182d91b37 | ||
|
|
de11ac8972 | ||
|
|
2df2c44cdf | ||
|
|
9a3d62333a | ||
|
|
2ae86a695b |
||
|
|
ef78ac28a4 |
||
|
|
e7aad8de88 | ||
|
|
9b00df4337 | ||
|
|
a5e38e2ce5 | ||
|
|
e156414062 | ||
|
|
14aae40fda | ||
|
|
3504180e95 | ||
|
|
b4952cdc51 | ||
|
|
5e8e0af59c | ||
|
|
a8b8292318 |
||
|
|
36a27f6167 |
||
|
|
9d4c4a3a59 | ||
|
|
78ed67b35f | ||
|
|
fa46155f72 | ||
|
|
063b3145ed |
||
|
|
fa0d8c509d | ||
|
|
b42953fd7e |
||
|
|
11d9cf8b71 | ||
|
|
67a4dd8f9d |
||
|
|
cedb7a3b02 |
||
|
|
02729cce21 |
||
|
|
d9204131aa |
||
|
|
e90cf40e5b |
||
|
|
b0395ef103 | ||
|
|
692e7265e3 | ||
|
|
98af3a98c8 | ||
|
|
732888fd94 | ||
|
|
71a06e13a6 | ||
|
|
f74c4e92a9 | ||
|
|
555e72a388 | ||
|
|
a1b38188d0 | ||
|
|
751d31cae2 |
||
|
|
c6a3223a03 |
||
|
|
d2803194f3 |
||
|
|
4345a99c02 | ||
|
|
fecc688418 | ||
|
|
b7e37ce1b7 | ||
|
|
be5a68dd1d | ||
|
|
622303d150 | ||
|
|
7747b27f9d | ||
|
|
585ca5f0da | ||
|
|
c624c35ac7 | ||
|
|
beb6d04c58 | ||
|
|
32793bcfeb | ||
|
|
9ded9fd765 |
||
|
|
13ba52319c |
||
|
|
1672d4a9ec |
||
|
|
e0ed1b4eb1 |
||
|
|
fa0748ead2 |
||
|
|
86ea38f460 |
||
|
|
9efb734fd5 |
||
|
|
5f52063835 |
||
|
|
548fa210c1 |
||
|
|
75975ea301 |
||
|
|
8fa6b2cec9 |
||
|
|
4dcef65b1c |
||
|
|
1eca889e45 |
||
|
|
ac1d828a5f |
||
|
|
92845268af |
||
|
|
6504cd0209 |
||
|
|
a058a81ec9 |
||
|
|
c6db79b873 |
||
|
|
932c751ac2 |
||
|
|
4599654398 |
||
|
|
e19a23b54c |
||
|
|
f2ca72eace |
||
|
|
4e859e56cb |
||
|
|
78d64ad2a7 |
||
|
|
d80ec8fd1f |
||
|
|
d12fa27168 |
||
|
|
593a775148 |
||
|
|
6079c01a4b |
||
|
|
9c79137c9f |
||
|
|
9168266cca |
||
|
|
6cb102257c |
||
|
|
55abc4d7f1 |
||
|
|
f013af0dde |
||
|
|
973d8fe120 |
||
|
|
3e6d7a1b3b |
||
|
|
0c69e0412c |
||
|
|
d0ccaa4d69 |
||
|
|
a766dc2b9c |
||
|
|
f54657432a |
||
|
|
87503367e9 |
||
|
|
85e071a60a |
||
|
|
24599368df |
||
|
|
d5bc6aab24 | ||
|
|
c9a416b8a2 | ||
|
|
15c2b19371 | ||
|
|
142073c362 | ||
|
|
514931b486 | ||
|
|
50e9e912e0 | ||
|
|
895edaf904 |
||
|
|
bebccf4d5a |
||
|
|
346a614ba0 | ||
|
|
d171634f9b |
||
|
|
f5f7422d82 | ||
|
|
117eccd65a | ||
|
|
b6726a76c6 | ||
|
|
0dea19c8db | ||
|
|
7a82b51ac5 | ||
|
|
fe36879dfb | ||
|
|
47ebe641d9 | ||
|
|
7a1fce660c | ||
|
|
06e283961d | ||
|
|
e125d8f1aa |
||
|
|
e885a5e776 |
||
|
|
aea95b7724 |
||
|
|
215ca5edc7 |
||
|
|
022d146f76 |
||
|
|
87ceba4de8 |
||
|
|
b80c276dad |
||
|
|
b84158e1c0 |
||
|
|
c87d5b4e7a |
||
|
|
028cb9496a |
||
|
|
00c94f8ffd |
||
|
|
2bd0d0d4f3 |
||
|
|
767bdaf166 |
||
|
|
c033f5bd03 |
||
|
|
096b1a7d44 |
||
|
|
f0869f0e13 |
||
|
|
1ca53f661e |
||
|
|
8562cf4e29 |
||
|
|
b5b3317dd8 |
||
|
|
2901b408dc |
||
|
|
4e40e32f59 |
||
|
|
5e85618730 |
||
|
|
2c428f5537 |
||
|
|
071b7a2182 |
||
|
|
1075c923ef |
||
|
|
391f86f602 |
||
|
|
6fbbb4eff0 |
||
|
|
c7f70ac78f |
||
|
|
ef86ea19ac |
||
|
|
37061269a4 |
||
|
|
7b9f03176d |
||
|
|
b73c78d2fb |
||
|
|
0bbe9c78dd |
||
|
|
b18f29a81a |
||
|
|
cab3a7d214 |
||
|
|
8995c590eb |
||
|
|
57d5d7497b |
||
|
|
ce3b310e58 |
||
|
|
5d3cd62434 |
||
|
|
d40ec873a7 |
||
|
|
61f3fadfbf |
||
|
|
1314c57796 |
||
|
|
872b15c0d4 |
||
|
|
9b4d381f1e |
||
|
|
bacbacd5fb |
||
|
|
ee0c00574a |
||
|
|
6df91cac12 |
||
|
|
598fa6a505 |
||
|
|
548a411ae2 |
||
|
|
48f28e40c4 |
||
|
|
46f9ddd5fc |
||
|
|
a393525212 |
||
|
|
961018e753 |
||
|
|
8b12ade372 |
||
|
|
a64d7efc69 |
||
|
|
b592a11fb0 |
||
|
|
3fedb59fdb |
||
|
|
b1bf89a4cf |
||
|
|
2dec2e0b7f |
||
|
|
5f2f21620a |
||
|
|
48185bdfe0 |
||
|
|
f646dddd02 |
||
|
|
c1f1c8f5c1 |
||
|
|
ee488ba36e |
||
|
|
56c210372d |
||
|
|
04a0ad3b33 |
||
|
|
2dd83b41e8 |
||
|
|
19d4b195f3 |
||
|
|
93554d9ba3 |
||
|
|
075bf67cef |
||
|
|
33791a2844 |
||
|
|
8333f7ea77 |
||
|
|
2bc12549a1 |
||
|
|
1df890b238 |
||
|
|
92c6a38835 |
||
|
|
a7e8b55c6e |
||
|
|
b7e1c81ad1 |
||
|
|
dc310ca80e |
||
|
|
da2ea08078 |
||
|
|
8c69472065 |
||
|
|
0ee28927cf |
||
|
|
b7934e42d1 |
||
|
|
e775827c8b |
||
|
|
9cd58123c4 |
||
|
|
0cead672da |
||
|
|
ce90f91bbf |
||
|
|
23c7175528 |
||
|
|
5ea780f806 |
||
|
|
2eaa473144 |
||
|
|
a939b82179 |
||
|
|
2b0301c1d0 |
||
|
|
b84e6c9eed |
||
|
|
49b8697546 |
||
|
|
1d7a001a0b |
||
|
|
3dc1482603 |
||
|
|
e4abf79ad5 |
||
|
|
5f8fbe381c |
||
|
|
c6d7d177b7 |
||
|
|
dc8d143fac |
||
|
|
cbe7b37564 |
||
|
|
4eb4e15e43 |
||
|
|
08f6ce5184 |
||
|
|
291e0d836b |
134 changed files with 12753 additions and 6345 deletions
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
github: AnErrupTion
|
||||
liberapay: ShiningLea
|
||||
84
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
84
.github/ISSUE_TEMPLATE/bug.yml
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
name: Bug report
|
||||
description: File a bug report.
|
||||
title: "[Bug] "
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: prerequisites
|
||||
attributes:
|
||||
label: Pre-requisites
|
||||
description: By submitting this issue, you agree to have done the following.
|
||||
options:
|
||||
- label: I have looked for any other duplicate issues
|
||||
required: true
|
||||
- label: I have reproduced the issue with the default config of Ly
|
||||
required: true
|
||||
- label: I have reproduced the issue on a fresh install of Ly **as well as** my OS and my desktop environment/window manager, all with **default settings**, except ones I will mention
|
||||
required: false
|
||||
- label: I have confirmed this issue also occurs on the latest development version (found in the `master` branch)
|
||||
required: true
|
||||
- label: I understand that this issue **will** be closed if requirements are not or not properly met
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Ly version
|
||||
description: The output of `ly --version`. Please note that only the latest release and the current version in development are supported.
|
||||
placeholder: 1.1.0-dev.12+2b0301c
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: observed
|
||||
attributes:
|
||||
label: Observed behavior
|
||||
description: What happened?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: What did you expect to happen instead?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: desktop
|
||||
attributes:
|
||||
label: OS + Desktop environment/Window manager
|
||||
description: Which operating system and DE (or WM) did you use when observing the problem?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: osimage
|
||||
attributes:
|
||||
label: OS version/snapshot used to reproduce
|
||||
description: Which version (or snapshot if rolling release) of the OS did you use to reproduce the issue? This is only required if you have reproduced the issue on a fresh copy of the OS.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: What **exactly** can someone else do in order to observe the problem you observed?
|
||||
placeholder: |
|
||||
1. Authenticate with ...
|
||||
2. Go to ...
|
||||
3. Create file ...
|
||||
4. Log out and log back in
|
||||
5. Observe error
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant logs
|
||||
description: |
|
||||
Please copy and paste (or attach) any relevant logs, error messages or any other output. This will be automatically formatted into code, so no need for backticks. Screenshots are accepted if they make life easier for you. The log files (located as specified by `/etc/ly/config.ini`) usually contain relevant information about the problem:
|
||||
- The session log is located at `~/.local/state/ly-session.log` by default.
|
||||
- The system log is located at `/var/log/ly.log` by default.
|
||||
render: shell
|
||||
- type: textarea
|
||||
id: moreinfo
|
||||
attributes:
|
||||
label: Additional information
|
||||
description: If you have any additional information that might be helpful in reproducing the problem, please provide it here.
|
||||
29
.github/ISSUE_TEMPLATE/feature.yml
vendored
Normal file
29
.github/ISSUE_TEMPLATE/feature.yml
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
name: Feature request
|
||||
description: Request a new feature or enhancement.
|
||||
title: "[Feature] "
|
||||
labels: ["feature"]
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: prerequisites
|
||||
attributes:
|
||||
label: Pre-requisites
|
||||
description: By submitting this issue, you agree to have done the following.
|
||||
options:
|
||||
- label: I have looked for any other duplicate issues
|
||||
required: true
|
||||
- label: I have confirmed the requested feature doesn't exist in the latest version in development
|
||||
required: true
|
||||
- type: textarea
|
||||
id: wanted
|
||||
attributes:
|
||||
label: Wanted behavior
|
||||
description: What do you want to be added? Describe the behavior clearly.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives
|
||||
description: Are there any alternatives to the new behavior you wish to see being added?
|
||||
validations:
|
||||
required: true
|
||||
12
.github/pull_request_template.md
vendored
Normal file
12
.github/pull_request_template.md
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
## What are the changes about?
|
||||
|
||||
_Replace this with a brief description of your changes_
|
||||
|
||||
## What existing issue(s) does this resolve?
|
||||
|
||||
_Replace this with a reference to (an) existing issue(s), or N/A if there is none_
|
||||
|
||||
## Pre-requisites
|
||||
|
||||
- [ ] I have tested & confirmed the changes work locally
|
||||
- [ ] I have read and fully adhere to the rules set in the contributing guidelines found in `CONTRIBUTING.md`
|
||||
BIN
.github/screenshot.png
vendored
Normal file
BIN
.github/screenshot.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,4 +1,6 @@
|
|||
.idea/
|
||||
zig-cache/
|
||||
zig-out/
|
||||
valgrind.log
|
||||
valgrind.log
|
||||
.zig-cache
|
||||
zig-pkg
|
||||
|
|
|
|||
377
build.zig
377
build.zig
|
|
@ -1,221 +1,244 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const ly_version = std.SemanticVersion{ .major = 1, .minor = 0, .patch = 0, .build = "dev" };
|
||||
const InitSystem = enum {
|
||||
systemd,
|
||||
openrc,
|
||||
runit,
|
||||
s6,
|
||||
dinit,
|
||||
sysvinit,
|
||||
freebsd,
|
||||
};
|
||||
const InstallType = enum {
|
||||
installexe,
|
||||
installnoconf,
|
||||
uninstallexe,
|
||||
uninstallnoconf,
|
||||
};
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const data_directory = b.option([]const u8, "data_directory", "Specify a default data directory (default is /etc/ly)");
|
||||
const min_zig_string = "0.16.0";
|
||||
const current_zig = builtin.zig_version;
|
||||
|
||||
// Implementing zig version detection through compile time
|
||||
comptime {
|
||||
const min_zig = std.SemanticVersion.parse(min_zig_string) catch unreachable;
|
||||
if (current_zig.order(min_zig) == .lt) {
|
||||
@compileError(std.fmt.comptimePrint("Your Zig version v{} does not meet the minimum build requirement of v{}", .{ current_zig, min_zig }));
|
||||
}
|
||||
}
|
||||
|
||||
const ly_version = std.SemanticVersion{ .major = 1, .minor = 5, .patch = 0 };
|
||||
|
||||
fn InstallStep(
|
||||
b: *std.Build,
|
||||
target: std.Build.ResolvedTarget,
|
||||
comptime install_type: InstallType,
|
||||
dest_directory: []const u8,
|
||||
config_directory: []const u8,
|
||||
prefix_directory: []const u8,
|
||||
executable_name: []const u8,
|
||||
init_system: InitSystem,
|
||||
default_tty_str: []const u8,
|
||||
) *std.Build.Step.Run {
|
||||
const step = b.addRunArtifact(b.addExecutable(.{
|
||||
.name = "install",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("install.zig"),
|
||||
.target = target,
|
||||
}),
|
||||
}));
|
||||
step.step.dependOn(b.getInstallStep());
|
||||
|
||||
step.addArgs(&.{
|
||||
std.enums.tagName(InstallType, install_type).?,
|
||||
dest_directory,
|
||||
config_directory,
|
||||
prefix_directory,
|
||||
executable_name,
|
||||
std.enums.tagName(InitSystem, init_system).?,
|
||||
default_tty_str,
|
||||
});
|
||||
|
||||
return step;
|
||||
}
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const dest_directory = b.option([]const u8, "dest_directory", "Specify a destination directory for installation") orelse "";
|
||||
const config_directory = b.option([]const u8, "config_directory", "Specify a default config directory (default is /etc). This path gets embedded into the binary") orelse "/etc";
|
||||
const prefix_directory = b.option([]const u8, "prefix_directory", "Specify a default prefix directory (default is /usr)") orelse "/usr";
|
||||
const executable_name = b.option([]const u8, "name", "Specify installed executable file name (default is ly)") orelse "ly";
|
||||
const init_system = b.option(InitSystem, "init_system", "Specify the target init system (default is systemd)") orelse .systemd;
|
||||
|
||||
const build_options = b.addOptions();
|
||||
build_options.addOption([]const u8, "data_directory", data_directory orelse "/etc/ly");
|
||||
const version_str = b.fmt("{d}.{d}.{d}-{s}", .{ ly_version.major, ly_version.minor, ly_version.patch, ly_version.build.? });
|
||||
const version_str = try getVersionStr(b, "ly", ly_version);
|
||||
const enable_x11_support = b.option(bool, "enable_x11_support", "Enable X11 support (default is on)") orelse true;
|
||||
const default_tty = b.option(u8, "default_tty", "Set the TTY (default is 2)") orelse 2;
|
||||
const fallback_tty = b.option(u8, "fallback_tty", "Set the fallback TTY (default is 2). This value gets embedded into the binary") orelse 2;
|
||||
const fallback_uid_min = b.option(std.posix.uid_t, "fallback_uid_min", "Set the fallback minimum UID (default is 1000). This value gets embedded into the binary") orelse 1000;
|
||||
const fallback_uid_max = b.option(std.posix.uid_t, "fallback_uid_max", "Set the fallback maximum UID (default is 60000). This value gets embedded into the binary") orelse 60000;
|
||||
|
||||
const default_tty_str = try std.fmt.allocPrint(b.allocator, "{d}", .{default_tty});
|
||||
|
||||
build_options.addOption([]const u8, "config_directory", config_directory);
|
||||
build_options.addOption([]const u8, "prefix_directory", prefix_directory);
|
||||
build_options.addOption([]const u8, "version", version_str);
|
||||
build_options.addOption(u8, "tty", default_tty);
|
||||
build_options.addOption(u8, "fallback_tty", fallback_tty);
|
||||
build_options.addOption(std.posix.uid_t, "fallback_uid_min", fallback_uid_min);
|
||||
build_options.addOption(std.posix.uid_t, "fallback_uid_max", fallback_uid_max);
|
||||
build_options.addOption(bool, "enable_x11_support", enable_x11_support);
|
||||
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const c_args = [_][]const u8{
|
||||
"-std=c99",
|
||||
"-pedantic",
|
||||
"-g",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror=vla",
|
||||
"-Wno-unused-parameter",
|
||||
"-D_DEFAULT_SOURCE",
|
||||
"-D_POSIX_C_SOURCE=200809L",
|
||||
"-D_XOPEN_SOURCE",
|
||||
};
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "ly",
|
||||
.root_source_file = .{ .path = "src/main.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.link_libc = true,
|
||||
}),
|
||||
});
|
||||
|
||||
const zigini = b.dependency("zigini", .{ .target = target, .optimize = optimize });
|
||||
exe.root_module.addImport("zigini", zigini.module("zigini"));
|
||||
const zlua = b.dependency("zlua", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.lang = .luajit,
|
||||
});
|
||||
exe.root_module.addImport("zlua", zlua.module("zlua"));
|
||||
|
||||
const ly_ui = b.dependency("ly_ui", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.enable_x11_support = enable_x11_support,
|
||||
.fallback_uid_min = fallback_uid_min,
|
||||
.fallback_uid_max = fallback_uid_max,
|
||||
});
|
||||
exe.root_module.addImport("ly-ui", ly_ui.module("ly-ui"));
|
||||
|
||||
exe.root_module.addOptions("build_options", build_options);
|
||||
|
||||
const clap = b.dependency("clap", .{ .target = target, .optimize = optimize });
|
||||
exe.root_module.addImport("clap", clap.module("clap"));
|
||||
|
||||
exe.linkSystemLibrary("pam");
|
||||
exe.linkSystemLibrary("xcb");
|
||||
exe.linkLibC();
|
||||
|
||||
exe.addIncludePath(.{ .path = "dep/termbox_next/src" });
|
||||
|
||||
exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/input.c" }, .flags = &c_args });
|
||||
exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/memstream.c" }, .flags = &c_args });
|
||||
exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/ringbuffer.c" }, .flags = &c_args });
|
||||
exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/term.c" }, .flags = &c_args });
|
||||
exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/termbox.c" }, .flags = &c_args });
|
||||
exe.addCSourceFile(.{ .file = .{ .path = "dep/termbox_next/src/utf8.c" }, .flags = &c_args });
|
||||
exe.root_module.linkSystemLibrary("pam", .{});
|
||||
if (enable_x11_support) exe.root_module.linkSystemLibrary("xcb", .{});
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
if (b.args) |args| run_cmd.addArgs(args);
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
const installexe_step = b.step("installexe", "Install Ly");
|
||||
installexe_step.makeFn = installexe;
|
||||
installexe_step.dependOn(b.getInstallStep());
|
||||
const installexe_step = b.step("installexe", "Install Ly and the selected init system service");
|
||||
installexe_step.dependOn(&InstallStep(
|
||||
b,
|
||||
target,
|
||||
.installexe,
|
||||
dest_directory,
|
||||
config_directory,
|
||||
prefix_directory,
|
||||
executable_name,
|
||||
init_system,
|
||||
default_tty_str,
|
||||
).step);
|
||||
|
||||
const installnoconf_step = b.step("installnoconf", "Install Ly without its configuration file");
|
||||
installnoconf_step.makeFn = installnoconf;
|
||||
installnoconf_step.dependOn(b.getInstallStep());
|
||||
const installnoconf_step = b.step("installnoconf", "Install Ly and the selected init system service, but not the configuration file");
|
||||
installnoconf_step.dependOn(&InstallStep(
|
||||
b,
|
||||
target,
|
||||
.installnoconf,
|
||||
dest_directory,
|
||||
config_directory,
|
||||
prefix_directory,
|
||||
executable_name,
|
||||
init_system,
|
||||
default_tty_str,
|
||||
).step);
|
||||
|
||||
const installsystemd_step = b.step("installsystemd", "Install the Ly systemd service");
|
||||
installsystemd_step.makeFn = installsystemd;
|
||||
installsystemd_step.dependOn(installexe_step);
|
||||
const uninstallexe_step = b.step("uninstallexe", "Uninstall Ly and remove the selected init system service");
|
||||
uninstallexe_step.dependOn(&InstallStep(
|
||||
b,
|
||||
target,
|
||||
.uninstallexe,
|
||||
dest_directory,
|
||||
config_directory,
|
||||
prefix_directory,
|
||||
executable_name,
|
||||
init_system,
|
||||
default_tty_str,
|
||||
).step);
|
||||
|
||||
const installopenrc_step = b.step("installopenrc", "Install the Ly openrc service");
|
||||
installopenrc_step.makeFn = installopenrc;
|
||||
installopenrc_step.dependOn(installexe_step);
|
||||
|
||||
const installrunit_step = b.step("installrunit", "Install the Ly runit service");
|
||||
installrunit_step.makeFn = installrunit;
|
||||
installrunit_step.dependOn(installexe_step);
|
||||
|
||||
const uninstallall_step = b.step("uninstallall", "Uninstall Ly and all services");
|
||||
uninstallall_step.makeFn = uninstallall;
|
||||
const uninstallnoconf_step = b.step("uninstallnoconf", "Uninstall Ly and remove the selected init system service, but keep the configuration directory");
|
||||
uninstallnoconf_step.dependOn(&InstallStep(
|
||||
b,
|
||||
target,
|
||||
.uninstallnoconf,
|
||||
dest_directory,
|
||||
config_directory,
|
||||
prefix_directory,
|
||||
executable_name,
|
||||
init_system,
|
||||
default_tty_str,
|
||||
).step);
|
||||
}
|
||||
|
||||
fn installexe(self: *std.Build.Step, progress: *std.Progress.Node) !void {
|
||||
_ = progress;
|
||||
_ = self;
|
||||
fn getVersionStr(b: *std.Build, name: []const u8, version: std.SemanticVersion) ![]const u8 {
|
||||
const version_str = b.fmt("{d}.{d}.{d}", .{ version.major, version.minor, version.patch });
|
||||
|
||||
try install_ly(true);
|
||||
}
|
||||
|
||||
fn installnoconf(self: *std.Build.Step, progress: *std.Progress.Node) !void {
|
||||
_ = progress;
|
||||
_ = self;
|
||||
|
||||
try install_ly(false);
|
||||
}
|
||||
|
||||
fn installsystemd(self: *std.Build.Step, progress: *std.Progress.Node) !void {
|
||||
_ = progress;
|
||||
_ = self;
|
||||
|
||||
var service_dir = std.fs.openDirAbsolute("/usr/lib/systemd/system", .{}) catch unreachable;
|
||||
defer service_dir.close();
|
||||
|
||||
try std.fs.cwd().copyFile("res/ly.service", service_dir, "ly.service", .{ .override_mode = 644 });
|
||||
}
|
||||
|
||||
fn installopenrc(self: *std.Build.Step, progress: *std.Progress.Node) !void {
|
||||
_ = progress;
|
||||
_ = self;
|
||||
|
||||
var service_dir = std.fs.openDirAbsolute("/etc/init.d", .{}) catch unreachable;
|
||||
defer service_dir.close();
|
||||
|
||||
try std.fs.cwd().copyFile("res/ly-openrc", service_dir, "ly", .{ .override_mode = 755 });
|
||||
}
|
||||
|
||||
fn installrunit(self: *std.Build.Step, progress: *std.Progress.Node) !void {
|
||||
_ = progress;
|
||||
_ = self;
|
||||
|
||||
var service_dir = std.fs.openDirAbsolute("/etc/sv", .{}) catch unreachable;
|
||||
defer service_dir.close();
|
||||
|
||||
std.fs.makeDirAbsolute("/etc/sv/ly") catch {
|
||||
std.debug.print("warn: /etc/sv/ly already exists as a directory.\n", .{});
|
||||
var status: u8 = undefined;
|
||||
const git_describe_raw = b.runAllowFail(&[_][]const u8{
|
||||
"git",
|
||||
"-C",
|
||||
b.build_root.path orelse ".",
|
||||
"describe",
|
||||
"--match",
|
||||
"*.*.*",
|
||||
"--tags",
|
||||
}, &status, .ignore) catch {
|
||||
return version_str;
|
||||
};
|
||||
var git_describe = std.mem.trim(u8, git_describe_raw, " \n\r");
|
||||
git_describe = std.mem.trimStart(u8, git_describe, "v");
|
||||
|
||||
var ly_service_dir = std.fs.openDirAbsolute("/etc/sv/ly", .{}) catch unreachable;
|
||||
defer ly_service_dir.close();
|
||||
switch (std.mem.count(u8, git_describe, "-")) {
|
||||
0 => {
|
||||
if (!std.mem.eql(u8, version_str, git_describe)) {
|
||||
std.debug.print("{s} version '{s}' does not match git tag: '{s}'\n", .{ name, version_str, git_describe });
|
||||
std.process.exit(1);
|
||||
}
|
||||
return version_str;
|
||||
},
|
||||
2 => {
|
||||
// Untagged development build (e.g. 0.10.0-dev.2025+ecf0050a9).
|
||||
var it = std.mem.splitScalar(u8, git_describe, '-');
|
||||
const tagged_ancestor = std.mem.trimStart(u8, it.first(), "v");
|
||||
const commit_height = it.next().?;
|
||||
const commit_id = it.next().?;
|
||||
|
||||
try std.fs.cwd().copyFile("res/ly-runit-service/conf", ly_service_dir, "conf", .{});
|
||||
try std.fs.cwd().copyFile("res/ly-runit-service/finish", ly_service_dir, "finish", .{});
|
||||
try std.fs.cwd().copyFile("res/ly-runit-service/run", ly_service_dir, "run", .{});
|
||||
}
|
||||
const ancestor_ver = try std.SemanticVersion.parse(tagged_ancestor);
|
||||
if (version.order(ancestor_ver) != .gt) {
|
||||
std.debug.print("{s} version '{f}' must be greater than tagged ancestor '{f}'\n", .{ name, version, ancestor_ver });
|
||||
std.process.exit(1);
|
||||
}
|
||||
|
||||
fn uninstallall(self: *std.Build.Step, progress: *std.Progress.Node) !void {
|
||||
_ = progress;
|
||||
_ = self;
|
||||
// Check that the commit hash is prefixed with a 'g' (a Git convention).
|
||||
if (commit_id.len < 1 or commit_id[0] != 'g') {
|
||||
std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
|
||||
return version_str;
|
||||
}
|
||||
|
||||
try std.fs.deleteTreeAbsolute("/etc/ly");
|
||||
try std.fs.deleteFileAbsolute("/usr/bin/ly");
|
||||
try std.fs.deleteFileAbsolute("/etc/pam.d/ly");
|
||||
std.fs.deleteFileAbsolute("/usr/lib/systemd/system/ly.service") catch {
|
||||
std.debug.print("warn: systemd service not found.\n", .{});
|
||||
};
|
||||
std.fs.deleteFileAbsolute("/etc/init.d/ly") catch {
|
||||
std.debug.print("warn: openrc service not found.\n", .{});
|
||||
};
|
||||
std.fs.deleteTreeAbsolute("/etc/sv/ly") catch {
|
||||
std.debug.print("warn: runit service not found.\n", .{});
|
||||
};
|
||||
}
|
||||
|
||||
fn install_ly(install_config: bool) !void {
|
||||
std.fs.makeDirAbsolute("/etc/ly") catch {
|
||||
std.debug.print("warn: /etc/ly already exists as a directory.\n", .{});
|
||||
};
|
||||
|
||||
std.fs.makeDirAbsolute("/etc/ly/lang") catch {
|
||||
std.debug.print("warn: /etc/ly/lang already exists as a directory.\n", .{});
|
||||
};
|
||||
|
||||
var current_dir = std.fs.cwd();
|
||||
|
||||
{
|
||||
var executable_dir = std.fs.openDirAbsolute("/usr/bin", .{}) catch unreachable;
|
||||
defer executable_dir.close();
|
||||
|
||||
try current_dir.copyFile("zig-out/bin/ly", executable_dir, "ly", .{});
|
||||
}
|
||||
|
||||
{
|
||||
var config_dir = std.fs.openDirAbsolute("/etc/ly", .{}) catch unreachable;
|
||||
defer config_dir.close();
|
||||
|
||||
if (install_config) {
|
||||
try current_dir.copyFile("res/config.ini", config_dir, "config.ini", .{});
|
||||
}
|
||||
try current_dir.copyFile("res/xsetup.sh", config_dir, "xsetup.sh", .{});
|
||||
try current_dir.copyFile("res/wsetup.sh", config_dir, "wsetup.sh", .{});
|
||||
}
|
||||
|
||||
{
|
||||
var lang_dir = std.fs.openDirAbsolute("/etc/ly/lang", .{}) catch unreachable;
|
||||
defer lang_dir.close();
|
||||
|
||||
try current_dir.copyFile("res/lang/cat.ini", lang_dir, "cat.ini", .{});
|
||||
try current_dir.copyFile("res/lang/cs.ini", lang_dir, "cs.ini", .{});
|
||||
try current_dir.copyFile("res/lang/de.ini", lang_dir, "de.ini", .{});
|
||||
try current_dir.copyFile("res/lang/en.ini", lang_dir, "en.ini", .{});
|
||||
try current_dir.copyFile("res/lang/es.ini", lang_dir, "es.ini", .{});
|
||||
try current_dir.copyFile("res/lang/fr.ini", lang_dir, "fr.ini", .{});
|
||||
try current_dir.copyFile("res/lang/it.ini", lang_dir, "it.ini", .{});
|
||||
try current_dir.copyFile("res/lang/pl.ini", lang_dir, "pl.ini", .{});
|
||||
try current_dir.copyFile("res/lang/pt.ini", lang_dir, "pt.ini", .{});
|
||||
try current_dir.copyFile("res/lang/pt_BR.ini", lang_dir, "pt_BR.ini", .{});
|
||||
try current_dir.copyFile("res/lang/ro.ini", lang_dir, "ro.ini", .{});
|
||||
try current_dir.copyFile("res/lang/ru.ini", lang_dir, "ru.ini", .{});
|
||||
try current_dir.copyFile("res/lang/sr.ini", lang_dir, "sr.ini", .{});
|
||||
try current_dir.copyFile("res/lang/sv.ini", lang_dir, "sv.ini", .{});
|
||||
try current_dir.copyFile("res/lang/tr.ini", lang_dir, "tr.ini", .{});
|
||||
try current_dir.copyFile("res/lang/uk.ini", lang_dir, "uk.ini", .{});
|
||||
}
|
||||
|
||||
{
|
||||
var pam_dir = std.fs.openDirAbsolute("/etc/pam.d", .{}) catch unreachable;
|
||||
defer pam_dir.close();
|
||||
|
||||
try current_dir.copyFile("res/pam.d/ly", pam_dir, "ly", .{ .override_mode = 644 });
|
||||
// The version is reformatted in accordance with the https://semver.org specification.
|
||||
return b.fmt("{s}-dev.{s}+{s}", .{ version_str, commit_height, commit_id[1..] });
|
||||
},
|
||||
else => {
|
||||
std.debug.print("Unexpected `git describe` output: {s}\n", .{git_describe});
|
||||
return version_str;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,24 @@
|
|||
.{
|
||||
.name = "ly",
|
||||
.version = "1.0.0",
|
||||
.name = .ly,
|
||||
.version = "1.5.0",
|
||||
.fingerprint = 0xa148ffcc5dc2cb59,
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
.clap = .{
|
||||
.url = "https://github.com/Hejsil/zig-clap/archive/8c98e6404b22aafc0184e999d8f068b81cc22fa1.tar.gz",
|
||||
.hash = "122014e73fd712190e109950837b97f6143f02d7e2b6986e1db70b6f4aadb5ba6a0d",
|
||||
.ly_ui = .{
|
||||
.path = "ly-ui",
|
||||
},
|
||||
.zigini = .{
|
||||
.url = "https://github.com/Kawaii-Ash/zigini/archive/ce1f322482099db058f5d9fdd05fbfa255d79723.tar.gz",
|
||||
.hash = "1220e7a99793a0430e0a7c0b938cb3c98321035bc297e21cd0e2413cf740b4923b9f",
|
||||
.clap = .{
|
||||
.url = "git+https://github.com/Hejsil/zig-clap#fc1e5cc3f6d9d3001112385ee6256d694e959d2f",
|
||||
.hash = "clap-0.11.0-oBajB7foAQC3Iyn4IVCkUdYaOVVng5IZkSncySTjNig1",
|
||||
},
|
||||
.zlua = .{
|
||||
.url = "git+https://github.com/natecraddock/ziglua?ref=zig-0.16#8f271c82baa5fc43aa02a72f6da020c2025d9436",
|
||||
.hash = "zlua-0.1.0-hGRpC2aABQD4D9PBVH3wAW8k32-I4969MRQ0CpOwoley",
|
||||
},
|
||||
},
|
||||
.paths = .{""},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
},
|
||||
}
|
||||
|
|
|
|||
47
changelog.md
47
changelog.md
|
|
@ -1,47 +0,0 @@
|
|||
# Zig Rewrite (Version 1.0.0)
|
||||
|
||||
## Config Options
|
||||
|
||||
res/config.ini contains all of the available config options and their default values.
|
||||
|
||||
### Additions
|
||||
|
||||
+ `border_fg` has been introduced to change the color of the borders.
|
||||
+ `term_restore_cursor_cmd` should restore the cursor to it's usual state.
|
||||
+ `vi_mode` to enable vi keybindings.
|
||||
+ `sleep_key` and `sleep_cmd`.
|
||||
|
||||
Note: `sleep_cmd` is unset by default, meaning it's hidden and has no effect.
|
||||
|
||||
### Changes
|
||||
|
||||
+ xinitrc can be set to null to hide it.
|
||||
+ `blank_password` has been renamed to `clear_password`.
|
||||
+ `save_file` has been deprecated and will be removed in a future version.
|
||||
|
||||
### Removals
|
||||
|
||||
+ `wayland_specifier` has been removed.
|
||||
|
||||
## Save File
|
||||
|
||||
The save file is now in .ini format and stored in the same directory as the config.
|
||||
Older save files will be migrated to the new format.
|
||||
|
||||
Example:
|
||||
|
||||
```ini
|
||||
user = ash
|
||||
session_index = 0
|
||||
```
|
||||
|
||||
## Misc
|
||||
|
||||
+ Display server name added next to selected session.
|
||||
+ getty@tty2 has been added as a conflict in res/ly.service, so if it is running, ly should still be able to start.
|
||||
+ `XDG_CURRENT_DESKTOP` is now set by ly.
|
||||
+ LANG is no longer set by ly.
|
||||
+ X Server PID is fetched from /tmp/X{d}.lock to be able to kill the process since it detaches.
|
||||
+ Non .desktop files are now ignored in sessions directory.
|
||||
+ PAM auth is now done in a child process. (Fixes some issues with logging out and back in).
|
||||
+ When ly receives SIGTERM, the terminal is now cleared and existing child processes are cleaned up.
|
||||
29
contributing.md
Normal file
29
contributing.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Contributing to Ly
|
||||
|
||||
To contribute to Ly, you have to open a pull request on [Codeberg](https://codeberg.org/fairyglade/ly), as [GitHub](https://github.com/fairyglade/ly) is just a mirror. However, you also have to respect the following rules, otherwise your PR may end up being rejected:
|
||||
|
||||
## AI usage
|
||||
|
||||
While we cannot control your usage of any LLM whatsoever, we do heavily discourage their use for environmental, ethical & moral reasons that you can learn more about on the Internet. However, if you do end up still using them, here are a couple rules you **must absolutely** respect for your PR not to get instantly shot down. Of course, it goes without saying that **all** the other rules in the other sections below **must** be adhered to, even more so when AI is used.
|
||||
|
||||
1. **Communicate through you**, not the AI; as in, responses to reviews or other comments in the PR must be written by you and not an LLM. If English isn't your native language and you have a lot of trouble speaking it, you _may_ use AI, but responses must not be your typical, alienating AI-generated text: https://github.com/realrossmanngroup/no_ai_slop_writing_rules
|
||||
2. Control the code, as in, **don't vibecode**, especially if you don't know Zig. Don't waste the maintainers's time, and **heavily test your code**, as AI-generated code is typically more error-prone in subtle ways (even if they can be better than humans in some other aspects). Finally, don't generate any "summary" or whatever the AI may come up with during the process. **If you cannot understand the generated code, do not contribute it**. Maintainers are busy and are often simple volunteers, so the less work they have to do, the better.
|
||||
3. **Be transparent about AI usage**. Precise what AI model was used in the process, and preferably, how you went about creating your changes (i.e. how did you use AI to make the pull request). The more honest you are about it, the more likely your PR will be accepted and merged into the code base.
|
||||
|
||||
If all the above rules are respected, your changes have much more likely to be accepted into the code base.
|
||||
|
||||
## Code style
|
||||
|
||||
You must follow Zig's [style guide](https://ziglang.org/documentation/master/#Style-Guide). In most cases, all you'll have to do is run `zig fmt` after you have completed your changes, though it does not fix everything, notably variable, function & field naming, as well as the maximum length of aline.
|
||||
|
||||
For the former, please refer to the aforementioned style guide in that case. For the latter, you must respect a maximum line length of **80 characters**. For function calls with many parameters or anything similar that may overflow this limit, consider adding a trailing comma to the list so that `zig fmt` can split it into multiple lines. Ideally, few or no lines end up having to get soft wrapped by an editor having this limit set.
|
||||
|
||||
## Commit names & descriptions
|
||||
|
||||
1. **Commit names must be descriptive**, resorting to the commit description if they are too long. In such cases, the commit name **must** be shortened while still making sense. With that said, no particular convention (emojis, prefixes & suffixes, etc.) is mandated for them, and you are free to adopt any one you think is best or that you are used to.
|
||||
2. **Do not force push**, as PRs will get squashed into a single commit anyway. This erases history and makes it impossible to see the changes done over time and by a particular commit.
|
||||
3. While not a requirement per se, **consider signing your commits** with an SSH or GPG key for security purposes. This ensures to a lesser extent that you are the person who you claim to be, and reduces the potential amount of malicious contributions that could infiltrate the code base.
|
||||
|
||||
## Additional requirements
|
||||
|
||||
Other requirements (such as testing your code) for merging the changes will be available when you open your pull request via the template (which you **must** use), however, these are not stated here because they may not be mandatory in some cases (e.g. if the changes are still work-in-progress), so they may temporarily be ommitted for a certain period of time until the changes are fully ready to be reviewed, for instance.
|
||||
2
create_vendor_tarball.sh
Executable file
2
create_vendor_tarball.sh
Executable file
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
tar --zstd -cvf vendor.tar.zst zig-pkg ly-ui/zig-pkg ly-core/zig-pkg
|
||||
7
dep/termbox_next/.gitignore
vendored
7
dep/termbox_next/.gitignore
vendored
|
|
@ -1,7 +0,0 @@
|
|||
bin
|
||||
obj
|
||||
src/demo/*.o
|
||||
src/demo/keyboard
|
||||
src/demo/output
|
||||
src/demo/paint
|
||||
src/demo/truecolor
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
Copyright (C) 2010-2013 nsf <no.smile.face@gmail.com>
|
||||
|
||||
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.
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
# Termbox
|
||||
[Termbox](https://github.com/nsf/termbox)
|
||||
was a promising Text User Interface (TUI) library.
|
||||
Unfortunately, its original author
|
||||
[changed his mind](https://github.com/nsf/termbox/issues/37#issuecomment-261075481)
|
||||
about consoles and despite the
|
||||
[community's efforts](https://github.com/nsf/termbox/pull/104#issuecomment-300308156)
|
||||
to keep the library's development going, preferred to let it die. Before it happened,
|
||||
[some people](https://wiki.musl-libc.org/alternatives.html)
|
||||
already noticed the robustness of the initial architecture
|
||||
[became compromised](https://github.com/nsf/termbox/commit/66c3f91b14e24510319bce6b5cc2fecf8cf5abff#commitcomment-3790714)
|
||||
in a nonsensical refactoring frenzy. Now, the author refuses to merge features
|
||||
like true-color support, invoking some
|
||||
[spurious correlations](https://github.com/nsf/termbox/pull/104#issuecomment-300292223)
|
||||
we will discuss no further.
|
||||
|
||||
## The new Termbox-next
|
||||
This fork was made to restore the codebase to its original quality (before
|
||||
[66c3f91](https://github.com/nsf/termbox/commit/66c3f91b14e24510319bce6b5cc2fecf8cf5abff))
|
||||
while providing all the functionnalities of the current implementation.
|
||||
This was achieved by branching at
|
||||
[a2e217f](https://github.com/nsf/termbox/commit/a2e217f0fb97e6bbd589136ea3945f9d5a123d81)
|
||||
and cherry-picking all the commits up to
|
||||
[d63b83a](https://github.com/nsf/termbox/commit/d63b83af04e0fd6da836bb8f37e5cec72a1dc95a)
|
||||
if they weren't harmful.
|
||||
|
||||
## Changes
|
||||
A lot of things changed during the process:
|
||||
- *waf*, the original build system, was completely removed from the
|
||||
project and replaced by make.
|
||||
- anything related to python was removed as well
|
||||
|
||||
## Getting started
|
||||
Termbox's interface only consists of 12 functions:
|
||||
```
|
||||
tb_init() // initialization
|
||||
tb_shutdown() // shutdown
|
||||
|
||||
tb_width() // width of the terminal screen
|
||||
tb_height() // height of the terminal screen
|
||||
|
||||
tb_clear() // clear buffer
|
||||
tb_present() // sync internal buffer with terminal
|
||||
|
||||
tb_put_cell()
|
||||
tb_change_cell()
|
||||
tb_blit() // drawing functions
|
||||
|
||||
tb_select_input_mode() // change input mode
|
||||
tb_peek_event() // peek a keyboard event
|
||||
tb_poll_event() // wait for a keyboard event
|
||||
```
|
||||
See src/termbox.h header file for full detail.
|
||||
|
||||
## TL;DR
|
||||
`make` to build a static version of the lib under bin/termbox.a
|
||||
`cd src/demo && make` to build the example programs in src/demo/
|
||||
|
|
@ -1,827 +0,0 @@
|
|||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include "termbox.h"
|
||||
|
||||
struct key
|
||||
{
|
||||
unsigned char x;
|
||||
unsigned char y;
|
||||
uint32_t ch;
|
||||
};
|
||||
|
||||
#define STOP {0,0,0}
|
||||
struct key K_ESC[] = {{1, 1, 'E'}, {2, 1, 'S'}, {3, 1, 'C'}, STOP};
|
||||
struct key K_F1[] = {{6, 1, 'F'}, {7, 1, '1'}, STOP};
|
||||
struct key K_F2[] = {{9, 1, 'F'}, {10, 1, '2'}, STOP};
|
||||
struct key K_F3[] = {{12, 1, 'F'}, {13, 1, '3'}, STOP};
|
||||
struct key K_F4[] = {{15, 1, 'F'}, {16, 1, '4'}, STOP};
|
||||
struct key K_F5[] = {{19, 1, 'F'}, {20, 1, '5'}, STOP};
|
||||
struct key K_F6[] = {{22, 1, 'F'}, {23, 1, '6'}, STOP};
|
||||
struct key K_F7[] = {{25, 1, 'F'}, {26, 1, '7'}, STOP};
|
||||
struct key K_F8[] = {{28, 1, 'F'}, {29, 1, '8'}, STOP};
|
||||
struct key K_F9[] = {{33, 1, 'F'}, {34, 1, '9'}, STOP};
|
||||
struct key K_F10[] = {{36, 1, 'F'}, {37, 1, '1'}, {38, 1, '0'}, STOP};
|
||||
struct key K_F11[] = {{40, 1, 'F'}, {41, 1, '1'}, {42, 1, '1'}, STOP};
|
||||
struct key K_F12[] = {{44, 1, 'F'}, {45, 1, '1'}, {46, 1, '2'}, STOP};
|
||||
struct key K_PRN[] = {{50, 1, 'P'}, {51, 1, 'R'}, {52, 1, 'N'}, STOP};
|
||||
struct key K_SCR[] = {{54, 1, 'S'}, {55, 1, 'C'}, {56, 1, 'R'}, STOP};
|
||||
struct key K_BRK[] = {{58, 1, 'B'}, {59, 1, 'R'}, {60, 1, 'K'}, STOP};
|
||||
struct key K_LED1[] = {{66, 1, '-'}, STOP};
|
||||
struct key K_LED2[] = {{70, 1, '-'}, STOP};
|
||||
struct key K_LED3[] = {{74, 1, '-'}, STOP};
|
||||
|
||||
struct key K_TILDE[] = {{1, 4, '`'}, STOP};
|
||||
struct key K_TILDE_SHIFT[] = {{1, 4, '~'}, STOP};
|
||||
struct key K_1[] = {{4, 4, '1'}, STOP};
|
||||
struct key K_1_SHIFT[] = {{4, 4, '!'}, STOP};
|
||||
struct key K_2[] = {{7, 4, '2'}, STOP};
|
||||
struct key K_2_SHIFT[] = {{7, 4, '@'}, STOP};
|
||||
struct key K_3[] = {{10, 4, '3'}, STOP};
|
||||
struct key K_3_SHIFT[] = {{10, 4, '#'}, STOP};
|
||||
struct key K_4[] = {{13, 4, '4'}, STOP};
|
||||
struct key K_4_SHIFT[] = {{13, 4, '$'}, STOP};
|
||||
struct key K_5[] = {{16, 4, '5'}, STOP};
|
||||
struct key K_5_SHIFT[] = {{16, 4, '%'}, STOP};
|
||||
struct key K_6[] = {{19, 4, '6'}, STOP};
|
||||
struct key K_6_SHIFT[] = {{19, 4, '^'}, STOP};
|
||||
struct key K_7[] = {{22, 4, '7'}, STOP};
|
||||
struct key K_7_SHIFT[] = {{22, 4, '&'}, STOP};
|
||||
struct key K_8[] = {{25, 4, '8'}, STOP};
|
||||
struct key K_8_SHIFT[] = {{25, 4, '*'}, STOP};
|
||||
struct key K_9[] = {{28, 4, '9'}, STOP};
|
||||
struct key K_9_SHIFT[] = {{28, 4, '('}, STOP};
|
||||
struct key K_0[] = {{31, 4, '0'}, STOP};
|
||||
struct key K_0_SHIFT[] = {{31, 4, ')'}, STOP};
|
||||
struct key K_MINUS[] = {{34, 4, '-'}, STOP};
|
||||
struct key K_MINUS_SHIFT[] = {{34, 4, '_'}, STOP};
|
||||
struct key K_EQUALS[] = {{37, 4, '='}, STOP};
|
||||
struct key K_EQUALS_SHIFT[] = {{37, 4, '+'}, STOP};
|
||||
struct key K_BACKSLASH[] = {{40, 4, '\\'}, STOP};
|
||||
struct key K_BACKSLASH_SHIFT[] = {{40, 4, '|'}, STOP};
|
||||
struct key K_BACKSPACE[] = {{44, 4, 0x2190}, {45, 4, 0x2500}, {46, 4, 0x2500}, STOP};
|
||||
struct key K_INS[] = {{50, 4, 'I'}, {51, 4, 'N'}, {52, 4, 'S'}, STOP};
|
||||
struct key K_HOM[] = {{54, 4, 'H'}, {55, 4, 'O'}, {56, 4, 'M'}, STOP};
|
||||
struct key K_PGU[] = {{58, 4, 'P'}, {59, 4, 'G'}, {60, 4, 'U'}, STOP};
|
||||
struct key K_K_NUMLOCK[] = {{65, 4, 'N'}, STOP};
|
||||
struct key K_K_SLASH[] = {{68, 4, '/'}, STOP};
|
||||
struct key K_K_STAR[] = {{71, 4, '*'}, STOP};
|
||||
struct key K_K_MINUS[] = {{74, 4, '-'}, STOP};
|
||||
|
||||
struct key K_TAB[] = {{1, 6, 'T'}, {2, 6, 'A'}, {3, 6, 'B'}, STOP};
|
||||
struct key K_q[] = {{6, 6, 'q'}, STOP};
|
||||
struct key K_Q[] = {{6, 6, 'Q'}, STOP};
|
||||
struct key K_w[] = {{9, 6, 'w'}, STOP};
|
||||
struct key K_W[] = {{9, 6, 'W'}, STOP};
|
||||
struct key K_e[] = {{12, 6, 'e'}, STOP};
|
||||
struct key K_E[] = {{12, 6, 'E'}, STOP};
|
||||
struct key K_r[] = {{15, 6, 'r'}, STOP};
|
||||
struct key K_R[] = {{15, 6, 'R'}, STOP};
|
||||
struct key K_t[] = {{18, 6, 't'}, STOP};
|
||||
struct key K_T[] = {{18, 6, 'T'}, STOP};
|
||||
struct key K_y[] = {{21, 6, 'y'}, STOP};
|
||||
struct key K_Y[] = {{21, 6, 'Y'}, STOP};
|
||||
struct key K_u[] = {{24, 6, 'u'}, STOP};
|
||||
struct key K_U[] = {{24, 6, 'U'}, STOP};
|
||||
struct key K_i[] = {{27, 6, 'i'}, STOP};
|
||||
struct key K_I[] = {{27, 6, 'I'}, STOP};
|
||||
struct key K_o[] = {{30, 6, 'o'}, STOP};
|
||||
struct key K_O[] = {{30, 6, 'O'}, STOP};
|
||||
struct key K_p[] = {{33, 6, 'p'}, STOP};
|
||||
struct key K_P[] = {{33, 6, 'P'}, STOP};
|
||||
struct key K_LSQB[] = {{36, 6, '['}, STOP};
|
||||
struct key K_LCUB[] = {{36, 6, '{'}, STOP};
|
||||
struct key K_RSQB[] = {{39, 6, ']'}, STOP};
|
||||
struct key K_RCUB[] = {{39, 6, '}'}, STOP};
|
||||
struct key K_ENTER[] =
|
||||
{
|
||||
{43, 6, 0x2591}, {44, 6, 0x2591}, {45, 6, 0x2591}, {46, 6, 0x2591},
|
||||
{43, 7, 0x2591}, {44, 7, 0x2591}, {45, 7, 0x21B5}, {46, 7, 0x2591},
|
||||
{41, 8, 0x2591}, {42, 8, 0x2591}, {43, 8, 0x2591}, {44, 8, 0x2591},
|
||||
{45, 8, 0x2591}, {46, 8, 0x2591}, STOP
|
||||
};
|
||||
struct key K_DEL[] = {{50, 6, 'D'}, {51, 6, 'E'}, {52, 6, 'L'}, STOP};
|
||||
struct key K_END[] = {{54, 6, 'E'}, {55, 6, 'N'}, {56, 6, 'D'}, STOP};
|
||||
struct key K_PGD[] = {{58, 6, 'P'}, {59, 6, 'G'}, {60, 6, 'D'}, STOP};
|
||||
struct key K_K_7[] = {{65, 6, '7'}, STOP};
|
||||
struct key K_K_8[] = {{68, 6, '8'}, STOP};
|
||||
struct key K_K_9[] = {{71, 6, '9'}, STOP};
|
||||
struct key K_K_PLUS[] = {{74, 6, ' '}, {74, 7, '+'}, {74, 8, ' '}, STOP};
|
||||
|
||||
struct key K_CAPS[] = {{1, 8, 'C'}, {2, 8, 'A'}, {3, 8, 'P'}, {4, 8, 'S'}, STOP};
|
||||
struct key K_a[] = {{7, 8, 'a'}, STOP};
|
||||
struct key K_A[] = {{7, 8, 'A'}, STOP};
|
||||
struct key K_s[] = {{10, 8, 's'}, STOP};
|
||||
struct key K_S[] = {{10, 8, 'S'}, STOP};
|
||||
struct key K_d[] = {{13, 8, 'd'}, STOP};
|
||||
struct key K_D[] = {{13, 8, 'D'}, STOP};
|
||||
struct key K_f[] = {{16, 8, 'f'}, STOP};
|
||||
struct key K_F[] = {{16, 8, 'F'}, STOP};
|
||||
struct key K_g[] = {{19, 8, 'g'}, STOP};
|
||||
struct key K_G[] = {{19, 8, 'G'}, STOP};
|
||||
struct key K_h[] = {{22, 8, 'h'}, STOP};
|
||||
struct key K_H[] = {{22, 8, 'H'}, STOP};
|
||||
struct key K_j[] = {{25, 8, 'j'}, STOP};
|
||||
struct key K_J[] = {{25, 8, 'J'}, STOP};
|
||||
struct key K_k[] = {{28, 8, 'k'}, STOP};
|
||||
struct key K_K[] = {{28, 8, 'K'}, STOP};
|
||||
struct key K_l[] = {{31, 8, 'l'}, STOP};
|
||||
struct key K_L[] = {{31, 8, 'L'}, STOP};
|
||||
struct key K_SEMICOLON[] = {{34, 8, ';'}, STOP};
|
||||
struct key K_PARENTHESIS[] = {{34, 8, ':'}, STOP};
|
||||
struct key K_QUOTE[] = {{37, 8, '\''}, STOP};
|
||||
struct key K_DOUBLEQUOTE[] = {{37, 8, '"'}, STOP};
|
||||
struct key K_K_4[] = {{65, 8, '4'}, STOP};
|
||||
struct key K_K_5[] = {{68, 8, '5'}, STOP};
|
||||
struct key K_K_6[] = {{71, 8, '6'}, STOP};
|
||||
|
||||
struct key K_LSHIFT[] = {{1, 10, 'S'}, {2, 10, 'H'}, {3, 10, 'I'}, {4, 10, 'F'}, {5, 10, 'T'}, STOP};
|
||||
struct key K_z[] = {{9, 10, 'z'}, STOP};
|
||||
struct key K_Z[] = {{9, 10, 'Z'}, STOP};
|
||||
struct key K_x[] = {{12, 10, 'x'}, STOP};
|
||||
struct key K_X[] = {{12, 10, 'X'}, STOP};
|
||||
struct key K_c[] = {{15, 10, 'c'}, STOP};
|
||||
struct key K_C[] = {{15, 10, 'C'}, STOP};
|
||||
struct key K_v[] = {{18, 10, 'v'}, STOP};
|
||||
struct key K_V[] = {{18, 10, 'V'}, STOP};
|
||||
struct key K_b[] = {{21, 10, 'b'}, STOP};
|
||||
struct key K_B[] = {{21, 10, 'B'}, STOP};
|
||||
struct key K_n[] = {{24, 10, 'n'}, STOP};
|
||||
struct key K_N[] = {{24, 10, 'N'}, STOP};
|
||||
struct key K_m[] = {{27, 10, 'm'}, STOP};
|
||||
struct key K_M[] = {{27, 10, 'M'}, STOP};
|
||||
struct key K_COMMA[] = {{30, 10, ','}, STOP};
|
||||
struct key K_LANB[] = {{30, 10, '<'}, STOP};
|
||||
struct key K_PERIOD[] = {{33, 10, '.'}, STOP};
|
||||
struct key K_RANB[] = {{33, 10, '>'}, STOP};
|
||||
struct key K_SLASH[] = {{36, 10, '/'}, STOP};
|
||||
struct key K_QUESTION[] = {{36, 10, '?'}, STOP};
|
||||
struct key K_RSHIFT[] = {{42, 10, 'S'}, {43, 10, 'H'}, {44, 10, 'I'}, {45, 10, 'F'}, {46, 10, 'T'}, STOP};
|
||||
struct key K_ARROW_UP[] = {{54, 10, '('}, {55, 10, 0x2191}, {56, 10, ')'}, STOP};
|
||||
struct key K_K_1[] = {{65, 10, '1'}, STOP};
|
||||
struct key K_K_2[] = {{68, 10, '2'}, STOP};
|
||||
struct key K_K_3[] = {{71, 10, '3'}, STOP};
|
||||
struct key K_K_ENTER[] = {{74, 10, 0x2591}, {74, 11, 0x2591}, {74, 12, 0x2591}, STOP};
|
||||
|
||||
struct key K_LCTRL[] = {{1, 12, 'C'}, {2, 12, 'T'}, {3, 12, 'R'}, {4, 12, 'L'}, STOP};
|
||||
struct key K_LWIN[] = {{6, 12, 'W'}, {7, 12, 'I'}, {8, 12, 'N'}, STOP};
|
||||
struct key K_LALT[] = {{10, 12, 'A'}, {11, 12, 'L'}, {12, 12, 'T'}, STOP};
|
||||
struct key K_SPACE[] =
|
||||
{
|
||||
{14, 12, ' '}, {15, 12, ' '}, {16, 12, ' '}, {17, 12, ' '}, {18, 12, ' '},
|
||||
{19, 12, 'S'}, {20, 12, 'P'}, {21, 12, 'A'}, {22, 12, 'C'}, {23, 12, 'E'},
|
||||
{24, 12, ' '}, {25, 12, ' '}, {26, 12, ' '}, {27, 12, ' '}, {28, 12, ' '},
|
||||
STOP
|
||||
};
|
||||
struct key K_RALT[] = {{30, 12, 'A'}, {31, 12, 'L'}, {32, 12, 'T'}, STOP};
|
||||
struct key K_RWIN[] = {{34, 12, 'W'}, {35, 12, 'I'}, {36, 12, 'N'}, STOP};
|
||||
struct key K_RPROP[] = {{38, 12, 'P'}, {39, 12, 'R'}, {40, 12, 'O'}, {41, 12, 'P'}, STOP};
|
||||
struct key K_RCTRL[] = {{43, 12, 'C'}, {44, 12, 'T'}, {45, 12, 'R'}, {46, 12, 'L'}, STOP};
|
||||
struct key K_ARROW_LEFT[] = {{50, 12, '('}, {51, 12, 0x2190}, {52, 12, ')'}, STOP};
|
||||
struct key K_ARROW_DOWN[] = {{54, 12, '('}, {55, 12, 0x2193}, {56, 12, ')'}, STOP};
|
||||
struct key K_ARROW_RIGHT[] = {{58, 12, '('}, {59, 12, 0x2192}, {60, 12, ')'}, STOP};
|
||||
struct key K_K_0[] = {{65, 12, ' '}, {66, 12, '0'}, {67, 12, ' '}, {68, 12, ' '}, STOP};
|
||||
struct key K_K_PERIOD[] = {{71, 12, '.'}, STOP};
|
||||
|
||||
struct combo
|
||||
{
|
||||
struct key* keys[6];
|
||||
};
|
||||
|
||||
struct combo combos[] =
|
||||
{
|
||||
{{K_TILDE, K_2, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_A, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_B, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_C, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_D, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_E, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_F, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_G, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_H, K_BACKSPACE, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_I, K_TAB, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_J, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_K, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_L, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_M, K_ENTER, K_K_ENTER, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_N, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_O, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_P, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_Q, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_R, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_S, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_T, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_U, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_V, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_W, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_X, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_Y, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_Z, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_LSQB, K_ESC, K_3, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_4, K_BACKSLASH, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_RSQB, K_5, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_6, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_7, K_SLASH, K_MINUS_SHIFT, K_LCTRL, K_RCTRL, 0}},
|
||||
{{K_SPACE, 0}},
|
||||
{{K_1_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_DOUBLEQUOTE, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_3_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_4_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_5_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_7_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_QUOTE, 0}},
|
||||
{{K_9_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_0_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_8_SHIFT, K_K_STAR, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_EQUALS_SHIFT, K_K_PLUS, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_COMMA, 0}},
|
||||
{{K_MINUS, K_K_MINUS, 0}},
|
||||
{{K_PERIOD, K_K_PERIOD, 0}},
|
||||
{{K_SLASH, K_K_SLASH, 0}},
|
||||
{{K_0, K_K_0, 0}},
|
||||
{{K_1, K_K_1, 0}},
|
||||
{{K_2, K_K_2, 0}},
|
||||
{{K_3, K_K_3, 0}},
|
||||
{{K_4, K_K_4, 0}},
|
||||
{{K_5, K_K_5, 0}},
|
||||
{{K_6, K_K_6, 0}},
|
||||
{{K_7, K_K_7, 0}},
|
||||
{{K_8, K_K_8, 0}},
|
||||
{{K_9, K_K_9, 0}},
|
||||
{{K_PARENTHESIS, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_SEMICOLON, 0}},
|
||||
{{K_LANB, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_EQUALS, 0}},
|
||||
{{K_RANB, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_QUESTION, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_2_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_A, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_B, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_C, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_D, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_E, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_F, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_G, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_H, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_I, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_J, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_K, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_L, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_M, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_N, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_O, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_P, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_Q, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_R, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_S, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_T, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_U, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_V, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_W, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_X, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_Y, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_Z, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_LSQB, 0}},
|
||||
{{K_BACKSLASH, 0}},
|
||||
{{K_RSQB, 0}},
|
||||
{{K_6_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_MINUS_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_TILDE, 0}},
|
||||
{{K_a, 0}},
|
||||
{{K_b, 0}},
|
||||
{{K_c, 0}},
|
||||
{{K_d, 0}},
|
||||
{{K_e, 0}},
|
||||
{{K_f, 0}},
|
||||
{{K_g, 0}},
|
||||
{{K_h, 0}},
|
||||
{{K_i, 0}},
|
||||
{{K_j, 0}},
|
||||
{{K_k, 0}},
|
||||
{{K_l, 0}},
|
||||
{{K_m, 0}},
|
||||
{{K_n, 0}},
|
||||
{{K_o, 0}},
|
||||
{{K_p, 0}},
|
||||
{{K_q, 0}},
|
||||
{{K_r, 0}},
|
||||
{{K_s, 0}},
|
||||
{{K_t, 0}},
|
||||
{{K_u, 0}},
|
||||
{{K_v, 0}},
|
||||
{{K_w, 0}},
|
||||
{{K_x, 0}},
|
||||
{{K_y, 0}},
|
||||
{{K_z, 0}},
|
||||
{{K_LCUB, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_BACKSLASH_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_RCUB, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_TILDE_SHIFT, K_LSHIFT, K_RSHIFT, 0}},
|
||||
{{K_8, K_BACKSPACE, K_LCTRL, K_RCTRL, 0}}
|
||||
};
|
||||
|
||||
struct combo func_combos[] =
|
||||
{
|
||||
{{K_F1, 0}},
|
||||
{{K_F2, 0}},
|
||||
{{K_F3, 0}},
|
||||
{{K_F4, 0}},
|
||||
{{K_F5, 0}},
|
||||
{{K_F6, 0}},
|
||||
{{K_F7, 0}},
|
||||
{{K_F8, 0}},
|
||||
{{K_F9, 0}},
|
||||
{{K_F10, 0}},
|
||||
{{K_F11, 0}},
|
||||
{{K_F12, 0}},
|
||||
{{K_INS, 0}},
|
||||
{{K_DEL, 0}},
|
||||
{{K_HOM, 0}},
|
||||
{{K_END, 0}},
|
||||
{{K_PGU, 0}},
|
||||
{{K_PGD, 0}},
|
||||
{{K_ARROW_UP, 0}},
|
||||
{{K_ARROW_DOWN, 0}},
|
||||
{{K_ARROW_LEFT, 0}},
|
||||
{{K_ARROW_RIGHT, 0}}
|
||||
};
|
||||
|
||||
void print_tb(const char* str, int x, int y, uint32_t fg, uint32_t bg)
|
||||
{
|
||||
while (*str)
|
||||
{
|
||||
uint32_t uni;
|
||||
str += utf8_char_to_unicode(&uni, str);
|
||||
tb_change_cell(x, y, uni, fg, bg);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
|
||||
void printf_tb(int x, int y, uint32_t fg, uint32_t bg, const char* fmt, ...)
|
||||
{
|
||||
char buf[4096];
|
||||
va_list vl;
|
||||
va_start(vl, fmt);
|
||||
vsnprintf(buf, sizeof(buf), fmt, vl);
|
||||
va_end(vl);
|
||||
print_tb(buf, x, y, fg, bg);
|
||||
}
|
||||
|
||||
void draw_key(struct key* k, uint32_t fg, uint32_t bg)
|
||||
{
|
||||
while (k->x)
|
||||
{
|
||||
tb_change_cell(k->x + 2, k->y + 4, k->ch, fg, bg);
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
void draw_keyboard()
|
||||
{
|
||||
int i;
|
||||
tb_change_cell(0, 0, 0x250C, TB_WHITE, TB_DEFAULT);
|
||||
tb_change_cell(79, 0, 0x2510, TB_WHITE, TB_DEFAULT);
|
||||
tb_change_cell(0, 23, 0x2514, TB_WHITE, TB_DEFAULT);
|
||||
tb_change_cell(79, 23, 0x2518, TB_WHITE, TB_DEFAULT);
|
||||
|
||||
for (i = 1; i < 79; ++i)
|
||||
{
|
||||
tb_change_cell(i, 0, 0x2500, TB_WHITE, TB_DEFAULT);
|
||||
tb_change_cell(i, 23, 0x2500, TB_WHITE, TB_DEFAULT);
|
||||
tb_change_cell(i, 17, 0x2500, TB_WHITE, TB_DEFAULT);
|
||||
tb_change_cell(i, 4, 0x2500, TB_WHITE, TB_DEFAULT);
|
||||
}
|
||||
|
||||
for (i = 1; i < 23; ++i)
|
||||
{
|
||||
tb_change_cell(0, i, 0x2502, TB_WHITE, TB_DEFAULT);
|
||||
tb_change_cell(79, i, 0x2502, TB_WHITE, TB_DEFAULT);
|
||||
}
|
||||
|
||||
tb_change_cell(0, 17, 0x251C, TB_WHITE, TB_DEFAULT);
|
||||
tb_change_cell(79, 17, 0x2524, TB_WHITE, TB_DEFAULT);
|
||||
tb_change_cell(0, 4, 0x251C, TB_WHITE, TB_DEFAULT);
|
||||
tb_change_cell(79, 4, 0x2524, TB_WHITE, TB_DEFAULT);
|
||||
|
||||
for (i = 5; i < 17; ++i)
|
||||
{
|
||||
tb_change_cell(1, i, 0x2588, TB_YELLOW, TB_YELLOW);
|
||||
tb_change_cell(78, i, 0x2588, TB_YELLOW, TB_YELLOW);
|
||||
}
|
||||
|
||||
draw_key(K_ESC, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_F1, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_F2, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_F3, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_F4, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_F5, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_F6, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_F7, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_F8, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_F9, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_F10, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_F11, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_F12, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_PRN, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_SCR, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_BRK, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_LED1, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_LED2, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_LED3, TB_WHITE, TB_BLUE);
|
||||
|
||||
draw_key(K_TILDE, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_1, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_2, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_3, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_4, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_5, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_6, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_7, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_8, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_9, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_0, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_MINUS, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_EQUALS, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_BACKSLASH, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_BACKSPACE, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_INS, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_HOM, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_PGU, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_NUMLOCK, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_SLASH, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_STAR, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_MINUS, TB_WHITE, TB_BLUE);
|
||||
|
||||
draw_key(K_TAB, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_q, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_w, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_e, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_r, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_t, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_y, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_u, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_i, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_o, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_p, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_LSQB, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_RSQB, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_ENTER, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_DEL, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_END, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_PGD, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_7, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_8, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_9, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_PLUS, TB_WHITE, TB_BLUE);
|
||||
|
||||
draw_key(K_CAPS, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_a, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_s, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_d, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_f, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_g, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_h, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_j, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_k, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_l, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_SEMICOLON, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_QUOTE, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_4, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_5, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_6, TB_WHITE, TB_BLUE);
|
||||
|
||||
draw_key(K_LSHIFT, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_z, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_x, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_c, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_v, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_b, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_n, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_m, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_COMMA, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_PERIOD, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_SLASH, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_RSHIFT, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_ARROW_UP, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_1, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_2, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_3, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_ENTER, TB_WHITE, TB_BLUE);
|
||||
|
||||
draw_key(K_LCTRL, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_LWIN, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_LALT, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_SPACE, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_RCTRL, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_RPROP, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_RWIN, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_RALT, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_ARROW_LEFT, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_ARROW_DOWN, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_ARROW_RIGHT, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_0, TB_WHITE, TB_BLUE);
|
||||
draw_key(K_K_PERIOD, TB_WHITE, TB_BLUE);
|
||||
|
||||
printf_tb(33, 1, TB_MAGENTA | TB_BOLD, TB_DEFAULT, "Keyboard demo!");
|
||||
printf_tb(21, 2, TB_MAGENTA, TB_DEFAULT,
|
||||
"(press CTRL+X and then CTRL+Q to exit)");
|
||||
printf_tb(15, 3, TB_MAGENTA, TB_DEFAULT,
|
||||
"(press CTRL+X and then CTRL+C to change input mode)");
|
||||
|
||||
int inputmode = tb_select_input_mode(0);
|
||||
char inputmode_str[64];
|
||||
|
||||
if (inputmode & TB_INPUT_ESC)
|
||||
{
|
||||
sprintf(inputmode_str, "TB_INPUT_ESC");
|
||||
}
|
||||
|
||||
if (inputmode & TB_INPUT_ALT)
|
||||
{
|
||||
sprintf(inputmode_str, "TB_INPUT_ALT");
|
||||
}
|
||||
|
||||
if (inputmode & TB_INPUT_MOUSE)
|
||||
{
|
||||
sprintf(inputmode_str + 12, " | TB_INPUT_MOUSE");
|
||||
}
|
||||
|
||||
printf_tb(3, 18, TB_WHITE, TB_DEFAULT, "Input mode: %s", inputmode_str);
|
||||
}
|
||||
|
||||
const char* funckeymap(int k)
|
||||
{
|
||||
static const char* fcmap[] =
|
||||
{
|
||||
"CTRL+2, CTRL+~",
|
||||
"CTRL+A",
|
||||
"CTRL+B",
|
||||
"CTRL+C",
|
||||
"CTRL+D",
|
||||
"CTRL+E",
|
||||
"CTRL+F",
|
||||
"CTRL+G",
|
||||
"CTRL+H, BACKSPACE",
|
||||
"CTRL+I, TAB",
|
||||
"CTRL+J",
|
||||
"CTRL+K",
|
||||
"CTRL+L",
|
||||
"CTRL+M, ENTER",
|
||||
"CTRL+N",
|
||||
"CTRL+O",
|
||||
"CTRL+P",
|
||||
"CTRL+Q",
|
||||
"CTRL+R",
|
||||
"CTRL+S",
|
||||
"CTRL+T",
|
||||
"CTRL+U",
|
||||
"CTRL+V",
|
||||
"CTRL+W",
|
||||
"CTRL+X",
|
||||
"CTRL+Y",
|
||||
"CTRL+Z",
|
||||
"CTRL+3, ESC, CTRL+[",
|
||||
"CTRL+4, CTRL+\\",
|
||||
"CTRL+5, CTRL+]",
|
||||
"CTRL+6",
|
||||
"CTRL+7, CTRL+/, CTRL+_",
|
||||
"SPACE"
|
||||
};
|
||||
static const char* fkmap[] =
|
||||
{
|
||||
"F1",
|
||||
"F2",
|
||||
"F3",
|
||||
"F4",
|
||||
"F5",
|
||||
"F6",
|
||||
"F7",
|
||||
"F8",
|
||||
"F9",
|
||||
"F10",
|
||||
"F11",
|
||||
"F12",
|
||||
"INSERT",
|
||||
"DELETE",
|
||||
"HOME",
|
||||
"END",
|
||||
"PGUP",
|
||||
"PGDN",
|
||||
"ARROW UP",
|
||||
"ARROW DOWN",
|
||||
"ARROW LEFT",
|
||||
"ARROW RIGHT"
|
||||
};
|
||||
|
||||
if (k == TB_KEY_CTRL_8)
|
||||
{
|
||||
return "CTRL+8, BACKSPACE 2"; // 0x7F
|
||||
}
|
||||
else if (k >= TB_KEY_ARROW_RIGHT && k <= 0xFFFF)
|
||||
{
|
||||
return fkmap[0xFFFF - k];
|
||||
}
|
||||
else if (k <= TB_KEY_SPACE)
|
||||
{
|
||||
return fcmap[k];
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
void pretty_print_press(struct tb_event* ev)
|
||||
{
|
||||
char buf[7];
|
||||
buf[utf8_unicode_to_char(buf, ev->ch)] = '\0';
|
||||
printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Key: ");
|
||||
printf_tb(8, 19, TB_YELLOW, TB_DEFAULT, "decimal: %d", ev->key);
|
||||
printf_tb(8, 20, TB_GREEN, TB_DEFAULT, "hex: 0x%X", ev->key);
|
||||
printf_tb(8, 21, TB_CYAN, TB_DEFAULT, "octal: 0%o", ev->key);
|
||||
printf_tb(8, 22, TB_RED, TB_DEFAULT, "string: %s", funckeymap(ev->key));
|
||||
|
||||
printf_tb(54, 19, TB_WHITE, TB_DEFAULT, "Char: ");
|
||||
printf_tb(60, 19, TB_YELLOW, TB_DEFAULT, "decimal: %d", ev->ch);
|
||||
printf_tb(60, 20, TB_GREEN, TB_DEFAULT, "hex: 0x%X", ev->ch);
|
||||
printf_tb(60, 21, TB_CYAN, TB_DEFAULT, "octal: 0%o", ev->ch);
|
||||
printf_tb(60, 22, TB_RED, TB_DEFAULT, "string: %s", buf);
|
||||
|
||||
printf_tb(54, 18, TB_WHITE, TB_DEFAULT, "Modifier: %s",
|
||||
(ev->mod) ? "TB_MOD_ALT" : "none");
|
||||
|
||||
}
|
||||
|
||||
void pretty_print_resize(struct tb_event* ev)
|
||||
{
|
||||
printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Resize event: %d x %d", ev->w, ev->h);
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
|
||||
void pretty_print_mouse(struct tb_event* ev)
|
||||
{
|
||||
printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Mouse event: %d x %d", ev->x, ev->y);
|
||||
char* btn = "";
|
||||
|
||||
switch (ev->key)
|
||||
{
|
||||
case TB_KEY_MOUSE_LEFT:
|
||||
btn = "MouseLeft: %d";
|
||||
break;
|
||||
|
||||
case TB_KEY_MOUSE_MIDDLE:
|
||||
btn = "MouseMiddle: %d";
|
||||
break;
|
||||
|
||||
case TB_KEY_MOUSE_RIGHT:
|
||||
btn = "MouseRight: %d";
|
||||
break;
|
||||
|
||||
case TB_KEY_MOUSE_WHEEL_UP:
|
||||
btn = "MouseWheelUp: %d";
|
||||
break;
|
||||
|
||||
case TB_KEY_MOUSE_WHEEL_DOWN:
|
||||
btn = "MouseWheelDown: %d";
|
||||
break;
|
||||
|
||||
case TB_KEY_MOUSE_RELEASE:
|
||||
btn = "MouseRelease: %d";
|
||||
}
|
||||
|
||||
counter++;
|
||||
printf_tb(43, 19, TB_WHITE, TB_DEFAULT, "Key: ");
|
||||
printf_tb(48, 19, TB_YELLOW, TB_DEFAULT, btn, counter);
|
||||
}
|
||||
|
||||
void dispatch_press(struct tb_event* ev)
|
||||
{
|
||||
if (ev->mod & TB_MOD_ALT)
|
||||
{
|
||||
draw_key(K_LALT, TB_WHITE, TB_RED);
|
||||
draw_key(K_RALT, TB_WHITE, TB_RED);
|
||||
}
|
||||
|
||||
struct combo* k = 0;
|
||||
|
||||
if (ev->key >= TB_KEY_ARROW_RIGHT)
|
||||
{
|
||||
k = &func_combos[0xFFFF - ev->key];
|
||||
}
|
||||
else if (ev->ch < 128)
|
||||
{
|
||||
if (ev->ch == 0 && ev->key < 128)
|
||||
{
|
||||
k = &combos[ev->key];
|
||||
}
|
||||
else
|
||||
{
|
||||
k = &combos[ev->ch];
|
||||
}
|
||||
}
|
||||
|
||||
if (!k)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
struct key** keys = k->keys;
|
||||
|
||||
while (*keys)
|
||||
{
|
||||
draw_key(*keys, TB_WHITE, TB_RED);
|
||||
keys++;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
int ret;
|
||||
|
||||
ret = tb_init();
|
||||
|
||||
if (ret)
|
||||
{
|
||||
fprintf(stderr, "tb_init() failed with error code %d\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
tb_select_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE);
|
||||
struct tb_event ev;
|
||||
|
||||
tb_clear();
|
||||
draw_keyboard();
|
||||
tb_present();
|
||||
int inputmode = 0;
|
||||
int ctrlxpressed = 0;
|
||||
|
||||
while (tb_poll_event(&ev))
|
||||
{
|
||||
switch (ev.type)
|
||||
{
|
||||
case TB_EVENT_KEY:
|
||||
if (ev.key == TB_KEY_CTRL_Q && ctrlxpressed)
|
||||
{
|
||||
tb_shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ev.key == TB_KEY_CTRL_C && ctrlxpressed)
|
||||
{
|
||||
static int chmap[] =
|
||||
{
|
||||
TB_INPUT_ESC | TB_INPUT_MOUSE, // 101
|
||||
TB_INPUT_ALT | TB_INPUT_MOUSE, // 110
|
||||
TB_INPUT_ESC, // 001
|
||||
TB_INPUT_ALT, // 010
|
||||
};
|
||||
inputmode++;
|
||||
|
||||
if (inputmode >= 4)
|
||||
{
|
||||
inputmode = 0;
|
||||
}
|
||||
|
||||
tb_select_input_mode(chmap[inputmode]);
|
||||
}
|
||||
|
||||
if (ev.key == TB_KEY_CTRL_X)
|
||||
{
|
||||
ctrlxpressed = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctrlxpressed = 0;
|
||||
}
|
||||
|
||||
tb_clear();
|
||||
draw_keyboard();
|
||||
dispatch_press(&ev);
|
||||
pretty_print_press(&ev);
|
||||
tb_present();
|
||||
break;
|
||||
|
||||
case TB_EVENT_RESIZE:
|
||||
tb_clear();
|
||||
draw_keyboard();
|
||||
pretty_print_resize(&ev);
|
||||
tb_present();
|
||||
break;
|
||||
|
||||
case TB_EVENT_MOUSE:
|
||||
tb_clear();
|
||||
draw_keyboard();
|
||||
pretty_print_mouse(&ev);
|
||||
tb_present();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tb_shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
CC=gcc
|
||||
FLAGS=-std=c99 -pedantic -Wall -Werror -g -static
|
||||
INCL=-I../
|
||||
BIND=../../bin
|
||||
|
||||
%.o:%.c
|
||||
@echo "building source object $@"
|
||||
@$(CC) $(INCL) $(FLAGS) -c -o $@ $<
|
||||
|
||||
all:keyboard output paint truecolor
|
||||
|
||||
keyboard:keyboard.o
|
||||
@echo "compiling $@"
|
||||
@$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a
|
||||
|
||||
output:output.o
|
||||
@echo "compiling $@"
|
||||
@$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a
|
||||
|
||||
paint:paint.o
|
||||
@echo "compiling $@"
|
||||
@$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a
|
||||
|
||||
truecolor:truecolor.o
|
||||
@echo "compiling $@"
|
||||
@$(CC) $(INCL) $(FLAGS) -o $@ $@.o $(BIND)/termbox.a
|
||||
|
||||
clean:
|
||||
@echo "cleaning workspace"
|
||||
@rm -rf *.o keyboard output paint truecolor
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../termbox.h"
|
||||
|
||||
static const char chars[] = "nnnnnnnnnbbbbbbbbbuuuuuuuuuBBBBBBBBB";
|
||||
|
||||
static const uint32_t all_attrs[] =
|
||||
{
|
||||
0,
|
||||
TB_BOLD,
|
||||
TB_UNDERLINE,
|
||||
TB_BOLD | TB_UNDERLINE,
|
||||
};
|
||||
|
||||
static int next_char(int current)
|
||||
{
|
||||
current++;
|
||||
|
||||
if (!chars[current])
|
||||
{
|
||||
current = 0;
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
static void draw_line(int x, int y, uint32_t bg)
|
||||
{
|
||||
int a, c;
|
||||
int current_char = 0;
|
||||
|
||||
for (a = 0; a < 4; a++)
|
||||
{
|
||||
for (c = TB_DEFAULT; c <= TB_WHITE; c++)
|
||||
{
|
||||
uint32_t fg = all_attrs[a] | c;
|
||||
tb_change_cell(x, y, chars[current_char], fg, bg);
|
||||
current_char = next_char(current_char);
|
||||
x++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_combinations_table(int sx, int sy, const uint32_t* attrs,
|
||||
int attrs_n)
|
||||
{
|
||||
int i, c;
|
||||
|
||||
for (i = 0; i < attrs_n; i++)
|
||||
{
|
||||
for (c = TB_DEFAULT; c <= TB_WHITE; c++)
|
||||
{
|
||||
uint32_t bg = attrs[i] | c;
|
||||
draw_line(sx, sy, bg);
|
||||
sy++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_all()
|
||||
{
|
||||
tb_clear();
|
||||
|
||||
tb_select_output_mode(TB_OUTPUT_NORMAL);
|
||||
static const uint32_t col1[] = {0, TB_BOLD};
|
||||
static const uint32_t col2[] = {TB_REVERSE};
|
||||
print_combinations_table(1, 1, col1, 2);
|
||||
print_combinations_table(2 + strlen(chars), 1, col2, 1);
|
||||
tb_present();
|
||||
|
||||
tb_select_output_mode(TB_OUTPUT_GRAYSCALE);
|
||||
int c, x, y;
|
||||
|
||||
for (x = 0, y = 23; x < 24; ++x)
|
||||
{
|
||||
tb_change_cell(x, y, '@', x, 0);
|
||||
tb_change_cell(x + 25, y, ' ', 0, x);
|
||||
}
|
||||
|
||||
tb_present();
|
||||
|
||||
tb_select_output_mode(TB_OUTPUT_216);
|
||||
y++;
|
||||
|
||||
for (c = 0, x = 0; c < 216; ++c, ++x)
|
||||
{
|
||||
if (!(x % 24))
|
||||
{
|
||||
x = 0;
|
||||
++y;
|
||||
}
|
||||
|
||||
tb_change_cell(x, y, '@', c, 0);
|
||||
tb_change_cell(x + 25, y, ' ', 0, c);
|
||||
}
|
||||
|
||||
tb_present();
|
||||
|
||||
tb_select_output_mode(TB_OUTPUT_256);
|
||||
y++;
|
||||
|
||||
for (c = 0, x = 0; c < 256; ++c, ++x)
|
||||
{
|
||||
if (!(x % 24))
|
||||
{
|
||||
x = 0;
|
||||
++y;
|
||||
}
|
||||
|
||||
tb_change_cell(x, y, '+', c | ((y & 1) ? TB_UNDERLINE : 0), 0);
|
||||
tb_change_cell(x + 25, y, ' ', 0, c);
|
||||
}
|
||||
|
||||
tb_present();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
int ret = tb_init();
|
||||
|
||||
if (ret)
|
||||
{
|
||||
fprintf(stderr, "tb_init() failed with error code %d\n", ret);
|
||||
return 1;
|
||||
}
|
||||
|
||||
draw_all();
|
||||
|
||||
struct tb_event ev;
|
||||
|
||||
while (tb_poll_event(&ev))
|
||||
{
|
||||
switch (ev.type)
|
||||
{
|
||||
case TB_EVENT_KEY:
|
||||
switch (ev.key)
|
||||
{
|
||||
case TB_KEY_ESC:
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TB_EVENT_RESIZE:
|
||||
draw_all();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
tb_shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
#include "../termbox.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static int curCol = 0;
|
||||
static int curRune = 0;
|
||||
static struct tb_cell* backbuf;
|
||||
static int bbw = 0, bbh = 0;
|
||||
|
||||
static const uint32_t runes[] =
|
||||
{
|
||||
0x20, // ' '
|
||||
0x2591, // '░'
|
||||
0x2592, // '▒'
|
||||
0x2593, // '▓'
|
||||
0x2588, // '█'
|
||||
};
|
||||
|
||||
#define len(a) (sizeof(a)/sizeof(a[0]))
|
||||
|
||||
static const uint32_t colors[] =
|
||||
{
|
||||
TB_BLACK,
|
||||
TB_RED,
|
||||
TB_GREEN,
|
||||
TB_YELLOW,
|
||||
TB_BLUE,
|
||||
TB_MAGENTA,
|
||||
TB_CYAN,
|
||||
TB_WHITE,
|
||||
};
|
||||
|
||||
void updateAndDrawButtons(int* current, int x, int y, int mx, int my, int n,
|
||||
void (*attrFunc)(int, uint32_t*, uint32_t*, uint32_t*))
|
||||
{
|
||||
int lx = x;
|
||||
int ly = y;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if (lx <= mx && mx <= lx + 3 && ly <= my && my <= ly + 1)
|
||||
{
|
||||
*current = i;
|
||||
}
|
||||
|
||||
uint32_t r;
|
||||
uint32_t fg, bg;
|
||||
(*attrFunc)(i, &r, &fg, &bg);
|
||||
tb_change_cell(lx + 0, ly + 0, r, fg, bg);
|
||||
tb_change_cell(lx + 1, ly + 0, r, fg, bg);
|
||||
tb_change_cell(lx + 2, ly + 0, r, fg, bg);
|
||||
tb_change_cell(lx + 3, ly + 0, r, fg, bg);
|
||||
tb_change_cell(lx + 0, ly + 1, r, fg, bg);
|
||||
tb_change_cell(lx + 1, ly + 1, r, fg, bg);
|
||||
tb_change_cell(lx + 2, ly + 1, r, fg, bg);
|
||||
tb_change_cell(lx + 3, ly + 1, r, fg, bg);
|
||||
lx += 4;
|
||||
}
|
||||
|
||||
lx = x;
|
||||
ly = y;
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if (*current == i)
|
||||
{
|
||||
uint32_t fg = TB_RED | TB_BOLD;
|
||||
uint32_t bg = TB_DEFAULT;
|
||||
tb_change_cell(lx + 0, ly + 2, '^', fg, bg);
|
||||
tb_change_cell(lx + 1, ly + 2, '^', fg, bg);
|
||||
tb_change_cell(lx + 2, ly + 2, '^', fg, bg);
|
||||
tb_change_cell(lx + 3, ly + 2, '^', fg, bg);
|
||||
}
|
||||
|
||||
lx += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void runeAttrFunc(int i, uint32_t* r, uint32_t* fg, uint32_t* bg)
|
||||
{
|
||||
*r = runes[i];
|
||||
*fg = TB_DEFAULT;
|
||||
*bg = TB_DEFAULT;
|
||||
}
|
||||
|
||||
void colorAttrFunc(int i, uint32_t* r, uint32_t* fg, uint32_t* bg)
|
||||
{
|
||||
*r = ' ';
|
||||
*fg = TB_DEFAULT;
|
||||
*bg = colors[i];
|
||||
}
|
||||
|
||||
void updateAndRedrawAll(int mx, int my)
|
||||
{
|
||||
tb_clear();
|
||||
|
||||
if (mx != -1 && my != -1)
|
||||
{
|
||||
backbuf[bbw * my + mx].ch = runes[curRune];
|
||||
backbuf[bbw * my + mx].fg = colors[curCol];
|
||||
}
|
||||
|
||||
memcpy(tb_cell_buffer(), backbuf, sizeof(struct tb_cell)*bbw * bbh);
|
||||
int h = tb_height();
|
||||
updateAndDrawButtons(&curRune, 0, 0, mx, my, len(runes), runeAttrFunc);
|
||||
updateAndDrawButtons(&curCol, 0, h - 3, mx, my, len(colors), colorAttrFunc);
|
||||
tb_present();
|
||||
}
|
||||
|
||||
void reallocBackBuffer(int w, int h)
|
||||
{
|
||||
bbw = w;
|
||||
bbh = h;
|
||||
|
||||
if (backbuf)
|
||||
{
|
||||
free(backbuf);
|
||||
}
|
||||
|
||||
backbuf = calloc(sizeof(struct tb_cell), w * h);
|
||||
}
|
||||
|
||||
int main(int argv, char** argc)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
int code = tb_init();
|
||||
|
||||
if (code < 0)
|
||||
{
|
||||
fprintf(stderr, "termbox init failed, code: %d\n", code);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tb_select_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE);
|
||||
int w = tb_width();
|
||||
int h = tb_height();
|
||||
reallocBackBuffer(w, h);
|
||||
updateAndRedrawAll(-1, -1);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
struct tb_event ev;
|
||||
int mx = -1;
|
||||
int my = -1;
|
||||
int t = tb_poll_event(&ev);
|
||||
|
||||
if (t == -1)
|
||||
{
|
||||
tb_shutdown();
|
||||
fprintf(stderr, "termbox poll event error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (t)
|
||||
{
|
||||
case TB_EVENT_KEY:
|
||||
if (ev.key == TB_KEY_ESC)
|
||||
{
|
||||
tb_shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TB_EVENT_MOUSE:
|
||||
if (ev.key == TB_KEY_MOUSE_LEFT)
|
||||
{
|
||||
mx = ev.x;
|
||||
my = ev.y;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TB_EVENT_RESIZE:
|
||||
reallocBackBuffer(ev.w, ev.h);
|
||||
break;
|
||||
}
|
||||
|
||||
updateAndRedrawAll(mx, my);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
#include "termbox.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
tb_init();
|
||||
tb_select_output_mode(TB_OUTPUT_TRUECOLOR);
|
||||
int w = tb_width();
|
||||
int h = tb_height();
|
||||
uint32_t bg = 0x000000, fg = 0x000000;
|
||||
tb_clear();
|
||||
int z = 0;
|
||||
|
||||
for (int y = 1; y < h; y++)
|
||||
{
|
||||
for (int x = 1; x < w; x++)
|
||||
{
|
||||
uint32_t ch;
|
||||
utf8_char_to_unicode(&ch, "x");
|
||||
fg = 0;
|
||||
|
||||
if (z % 2 == 0)
|
||||
{
|
||||
fg |= TB_BOLD;
|
||||
}
|
||||
|
||||
if (z % 3 == 0)
|
||||
{
|
||||
fg |= TB_UNDERLINE;
|
||||
}
|
||||
|
||||
if (z % 5 == 0)
|
||||
{
|
||||
fg |= TB_REVERSE;
|
||||
}
|
||||
|
||||
tb_change_cell(x, y, ch, fg, bg);
|
||||
bg += 0x000101;
|
||||
z++;
|
||||
}
|
||||
|
||||
bg += 0x080000;
|
||||
|
||||
if (bg > 0xFFFFFF)
|
||||
{
|
||||
bg = 0;
|
||||
}
|
||||
}
|
||||
|
||||
tb_present();
|
||||
|
||||
while (1)
|
||||
{
|
||||
struct tb_event ev;
|
||||
int t = tb_poll_event(&ev);
|
||||
|
||||
if (t == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (t == TB_EVENT_KEY)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tb_shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,319 +0,0 @@
|
|||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "term.h"
|
||||
|
||||
#define BUFFER_SIZE_MAX 16
|
||||
|
||||
// if s1 starts with s2 returns 1, else 0
|
||||
static int starts_with(const char* s1, const char* s2)
|
||||
{
|
||||
// nice huh?
|
||||
while (*s2)
|
||||
{
|
||||
if (*s1++ != *s2++)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int parse_mouse_event(struct tb_event* event, const char* buf, int len)
|
||||
{
|
||||
if ((len >= 6) && starts_with(buf, "\033[M"))
|
||||
{
|
||||
// X10 mouse encoding, the simplest one
|
||||
// \033 [ M Cb Cx Cy
|
||||
int b = buf[3] - 32;
|
||||
|
||||
switch (b & 3)
|
||||
{
|
||||
case 0:
|
||||
if ((b & 64) != 0)
|
||||
{
|
||||
event->key = TB_KEY_MOUSE_WHEEL_UP;
|
||||
}
|
||||
else
|
||||
{
|
||||
event->key = TB_KEY_MOUSE_LEFT;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if ((b & 64) != 0)
|
||||
{
|
||||
event->key = TB_KEY_MOUSE_WHEEL_DOWN;
|
||||
}
|
||||
else
|
||||
{
|
||||
event->key = TB_KEY_MOUSE_MIDDLE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
event->key = TB_KEY_MOUSE_RIGHT;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
event->key = TB_KEY_MOUSE_RELEASE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -6;
|
||||
}
|
||||
|
||||
event->type = TB_EVENT_MOUSE; // TB_EVENT_KEY by default
|
||||
|
||||
if ((b & 32) != 0)
|
||||
{
|
||||
event->mod |= TB_MOD_MOTION;
|
||||
}
|
||||
|
||||
// the coord is 1,1 for upper left
|
||||
event->x = (uint8_t)buf[4] - 1 - 32;
|
||||
event->y = (uint8_t)buf[5] - 1 - 32;
|
||||
return 6;
|
||||
}
|
||||
else if (starts_with(buf, "\033[<") || starts_with(buf, "\033["))
|
||||
{
|
||||
// xterm 1006 extended mode or urxvt 1015 extended mode
|
||||
// xterm: \033 [ < Cb ; Cx ; Cy (M or m)
|
||||
// urxvt: \033 [ Cb ; Cx ; Cy M
|
||||
int i, mi = -1, starti = -1;
|
||||
int isM, isU, s1 = -1, s2 = -1;
|
||||
int n1 = 0, n2 = 0, n3 = 0;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
// We search the first (s1) and the last (s2) ';'
|
||||
if (buf[i] == ';')
|
||||
{
|
||||
if (s1 == -1)
|
||||
{
|
||||
s1 = i;
|
||||
}
|
||||
|
||||
s2 = i;
|
||||
}
|
||||
|
||||
// We search for the first 'm' or 'M'
|
||||
if ((buf[i] == 'm' || buf[i] == 'M') && mi == -1)
|
||||
{
|
||||
mi = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mi == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// whether it's a capital M or not
|
||||
isM = (buf[mi] == 'M');
|
||||
|
||||
if (buf[2] == '<')
|
||||
{
|
||||
isU = 0;
|
||||
starti = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
isU = 1;
|
||||
starti = 2;
|
||||
}
|
||||
|
||||
if (s1 == -1 || s2 == -1 || s1 == s2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
n1 = strtoul(&buf[starti], NULL, 10);
|
||||
n2 = strtoul(&buf[s1 + 1], NULL, 10);
|
||||
n3 = strtoul(&buf[s2 + 1], NULL, 10);
|
||||
|
||||
if (isU)
|
||||
{
|
||||
n1 -= 32;
|
||||
}
|
||||
|
||||
switch (n1 & 3)
|
||||
{
|
||||
case 0:
|
||||
if ((n1 & 64) != 0)
|
||||
{
|
||||
event->key = TB_KEY_MOUSE_WHEEL_UP;
|
||||
}
|
||||
else
|
||||
{
|
||||
event->key = TB_KEY_MOUSE_LEFT;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if ((n1 & 64) != 0)
|
||||
{
|
||||
event->key = TB_KEY_MOUSE_WHEEL_DOWN;
|
||||
}
|
||||
else
|
||||
{
|
||||
event->key = TB_KEY_MOUSE_MIDDLE;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 2:
|
||||
event->key = TB_KEY_MOUSE_RIGHT;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
event->key = TB_KEY_MOUSE_RELEASE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return mi + 1;
|
||||
}
|
||||
|
||||
if (!isM)
|
||||
{
|
||||
// on xterm mouse release is signaled by lowercase m
|
||||
event->key = TB_KEY_MOUSE_RELEASE;
|
||||
}
|
||||
|
||||
event->type = TB_EVENT_MOUSE; // TB_EVENT_KEY by default
|
||||
|
||||
if ((n1 & 32) != 0)
|
||||
{
|
||||
event->mod |= TB_MOD_MOTION;
|
||||
}
|
||||
|
||||
event->x = (uint8_t)n2 - 1;
|
||||
event->y = (uint8_t)n3 - 1;
|
||||
return mi + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// convert escape sequence to event, and return consumed bytes on success (failure == 0)
|
||||
static int parse_escape_seq(struct tb_event* event, const char* buf, int len)
|
||||
{
|
||||
int mouse_parsed = parse_mouse_event(event, buf, len);
|
||||
|
||||
if (mouse_parsed != 0)
|
||||
{
|
||||
return mouse_parsed;
|
||||
}
|
||||
|
||||
// it's pretty simple here, find 'starts_with' match and return success, else return failure
|
||||
int i;
|
||||
|
||||
for (i = 0; keys[i]; i++)
|
||||
{
|
||||
if (starts_with(buf, keys[i]))
|
||||
{
|
||||
event->ch = 0;
|
||||
event->key = 0xFFFF - i;
|
||||
return strlen(keys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool extract_event(struct tb_event* event, struct ringbuffer* inbuf,
|
||||
int inputmode)
|
||||
{
|
||||
char buf[BUFFER_SIZE_MAX + 1];
|
||||
int nbytes = ringbuffer_data_size(inbuf);
|
||||
|
||||
if (nbytes > BUFFER_SIZE_MAX)
|
||||
{
|
||||
nbytes = BUFFER_SIZE_MAX;
|
||||
}
|
||||
|
||||
if (nbytes == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ringbuffer_read(inbuf, buf, nbytes);
|
||||
buf[nbytes] = '\0';
|
||||
|
||||
if (buf[0] == '\033')
|
||||
{
|
||||
int n = parse_escape_seq(event, buf, nbytes);
|
||||
|
||||
if (n != 0)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
if (n < 0)
|
||||
{
|
||||
success = false;
|
||||
n = -n;
|
||||
}
|
||||
|
||||
ringbuffer_pop(inbuf, 0, n);
|
||||
return success;
|
||||
}
|
||||
else
|
||||
{
|
||||
// it's not escape sequence, then it's ALT or ESC, check inputmode
|
||||
if (inputmode & TB_INPUT_ESC)
|
||||
{
|
||||
// if we're in escape mode, fill ESC event, pop buffer, return success
|
||||
event->ch = 0;
|
||||
event->key = TB_KEY_ESC;
|
||||
event->mod = 0;
|
||||
ringbuffer_pop(inbuf, 0, 1);
|
||||
return true;
|
||||
}
|
||||
else if (inputmode & TB_INPUT_ALT)
|
||||
{
|
||||
// if we're in alt mode, set ALT modifier to event and redo parsing
|
||||
event->mod = TB_MOD_ALT;
|
||||
ringbuffer_pop(inbuf, 0, 1);
|
||||
return extract_event(event, inbuf, inputmode);
|
||||
}
|
||||
|
||||
assert(!"never got here");
|
||||
}
|
||||
}
|
||||
|
||||
// if we're here, this is not an escape sequence and not an alt sequence
|
||||
// so, it's a FUNCTIONAL KEY or a UNICODE character
|
||||
|
||||
// first of all check if it's a functional key*/
|
||||
if ((unsigned char)buf[0] <= TB_KEY_SPACE ||
|
||||
(unsigned char)buf[0] == TB_KEY_BACKSPACE2)
|
||||
{
|
||||
// fill event, pop buffer, return success
|
||||
event->ch = 0;
|
||||
event->key = (uint16_t)buf[0];
|
||||
ringbuffer_pop(inbuf, 0, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// feh... we got utf8 here
|
||||
|
||||
// check if there is all bytes
|
||||
if (nbytes >= utf8_char_length(buf[0]))
|
||||
{
|
||||
// everything ok, fill event, pop buffer, return success
|
||||
utf8_char_to_unicode(&event->ch, buf);
|
||||
event->key = 0;
|
||||
ringbuffer_pop(inbuf, 0, utf8_char_length(buf[0]));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "memstream.h"
|
||||
|
||||
void memstream_init(struct memstream* s, int fd, void* buffer, size_t len)
|
||||
{
|
||||
s->file = fd;
|
||||
s->data = buffer;
|
||||
s->pos = 0;
|
||||
s->capa = len;
|
||||
}
|
||||
|
||||
void memstream_flush(struct memstream* s)
|
||||
{
|
||||
write(s->file, s->data, s->pos);
|
||||
s->pos = 0;
|
||||
}
|
||||
|
||||
void memstream_write(struct memstream* s, void* source, size_t len)
|
||||
{
|
||||
unsigned char* data = source;
|
||||
|
||||
if (s->pos + len > s->capa)
|
||||
{
|
||||
memstream_flush(s);
|
||||
}
|
||||
|
||||
memcpy(s->data + s->pos, data, len);
|
||||
s->pos += len;
|
||||
}
|
||||
|
||||
void memstream_puts(struct memstream* s, const char* str)
|
||||
{
|
||||
memstream_write(s, (void*) str, strlen(str));
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
#ifndef H_MEMSTREAM
|
||||
#define H_MEMSTREAM
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct memstream
|
||||
{
|
||||
size_t pos;
|
||||
size_t capa;
|
||||
int file;
|
||||
unsigned char* data;
|
||||
};
|
||||
|
||||
void memstream_init(struct memstream* s, int fd, void* buffer, size_t len);
|
||||
void memstream_flush(struct memstream* s);
|
||||
void memstream_write(struct memstream* s, void* source, size_t len);
|
||||
void memstream_puts(struct memstream* s, const char* str);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,195 +0,0 @@
|
|||
#include "ringbuffer.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h> // for ptrdiff_t
|
||||
|
||||
int init_ringbuffer(struct ringbuffer* r, size_t size)
|
||||
{
|
||||
r->buf = (char*)malloc(size);
|
||||
|
||||
if (!r->buf)
|
||||
{
|
||||
return ERINGBUFFER_ALLOC_FAIL;
|
||||
}
|
||||
|
||||
r->size = size;
|
||||
clear_ringbuffer(r);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_ringbuffer(struct ringbuffer* r)
|
||||
{
|
||||
free(r->buf);
|
||||
}
|
||||
|
||||
void clear_ringbuffer(struct ringbuffer* r)
|
||||
{
|
||||
r->begin = 0;
|
||||
r->end = 0;
|
||||
}
|
||||
|
||||
size_t ringbuffer_free_space(struct ringbuffer* r)
|
||||
{
|
||||
if (r->begin == 0 && r->end == 0)
|
||||
{
|
||||
return r->size;
|
||||
}
|
||||
|
||||
if (r->begin < r->end)
|
||||
{
|
||||
return r->size - (r->end - r->begin) - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return r->begin - r->end - 1;
|
||||
}
|
||||
}
|
||||
|
||||
size_t ringbuffer_data_size(struct ringbuffer* r)
|
||||
{
|
||||
if (r->begin == 0 && r->end == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (r->begin <= r->end)
|
||||
{
|
||||
return r->end - r->begin + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return r->size - (r->begin - r->end) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ringbuffer_push(struct ringbuffer* r, const void* data, size_t size)
|
||||
{
|
||||
if (ringbuffer_free_space(r) < size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (r->begin == 0 && r->end == 0)
|
||||
{
|
||||
memcpy(r->buf, data, size);
|
||||
r->begin = r->buf;
|
||||
r->end = r->buf + size - 1;
|
||||
return;
|
||||
}
|
||||
|
||||
r->end++;
|
||||
|
||||
if (r->begin < r->end)
|
||||
{
|
||||
if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size)
|
||||
{
|
||||
// we can fit without cut
|
||||
memcpy(r->end, data, size);
|
||||
r->end += size - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// make a cut
|
||||
size_t s = r->buf + r->size - r->end;
|
||||
memcpy(r->end, data, s);
|
||||
size -= s;
|
||||
memcpy(r->buf, (char*)data + s, size);
|
||||
r->end = r->buf + size - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(r->end, data, size);
|
||||
r->end += size - 1;
|
||||
}
|
||||
}
|
||||
|
||||
void ringbuffer_pop(struct ringbuffer* r, void* data, size_t size)
|
||||
{
|
||||
if (ringbuffer_data_size(r) < size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int need_clear = 0;
|
||||
|
||||
if (ringbuffer_data_size(r) == size)
|
||||
{
|
||||
need_clear = 1;
|
||||
}
|
||||
|
||||
if (r->begin < r->end)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
memcpy(data, r->begin, size);
|
||||
}
|
||||
|
||||
r->begin += size;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
memcpy(data, r->begin, size);
|
||||
}
|
||||
|
||||
r->begin += size;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t s = r->buf + r->size - r->begin;
|
||||
|
||||
if (data)
|
||||
{
|
||||
memcpy(data, r->begin, s);
|
||||
}
|
||||
|
||||
size -= s;
|
||||
|
||||
if (data)
|
||||
{
|
||||
memcpy((char*)data + s, r->buf, size);
|
||||
}
|
||||
|
||||
r->begin = r->buf + size;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_clear)
|
||||
{
|
||||
clear_ringbuffer(r);
|
||||
}
|
||||
}
|
||||
|
||||
void ringbuffer_read(struct ringbuffer* r, void* data, size_t size)
|
||||
{
|
||||
if (ringbuffer_data_size(r) < size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (r->begin < r->end)
|
||||
{
|
||||
memcpy(data, r->begin, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((size_t)(r->buf + (ptrdiff_t)r->size - r->begin) >= size)
|
||||
{
|
||||
memcpy(data, r->begin, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t s = r->buf + r->size - r->begin;
|
||||
memcpy(data, r->begin, s);
|
||||
size -= s;
|
||||
memcpy((char*)data + s, r->buf, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
#ifndef H_RINGBUFFER
|
||||
#define H_RINGBUFFER
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define ERINGBUFFER_ALLOC_FAIL -1
|
||||
|
||||
struct ringbuffer
|
||||
{
|
||||
char* buf;
|
||||
size_t size;
|
||||
|
||||
char* begin;
|
||||
char* end;
|
||||
};
|
||||
|
||||
int init_ringbuffer(struct ringbuffer* r, size_t size);
|
||||
void free_ringbuffer(struct ringbuffer* r);
|
||||
void clear_ringbuffer(struct ringbuffer* r);
|
||||
size_t ringbuffer_free_space(struct ringbuffer* r);
|
||||
size_t ringbuffer_data_size(struct ringbuffer* r);
|
||||
void ringbuffer_push(struct ringbuffer* r, const void* data, size_t size);
|
||||
void ringbuffer_pop(struct ringbuffer* r, void* data, size_t size);
|
||||
void ringbuffer_read(struct ringbuffer* r, void* data, size_t size);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,412 +0,0 @@
|
|||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "term.h"
|
||||
#define ENTER_MOUSE_SEQ "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h"
|
||||
#define EXIT_MOUSE_SEQ "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l"
|
||||
|
||||
#define EUNSUPPORTED_TERM -1
|
||||
|
||||
// rxvt-256color
|
||||
static const char* rxvt_256color_keys[] =
|
||||
{
|
||||
"\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~",
|
||||
"\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~",
|
||||
"\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~",
|
||||
"\033[A", "\033[B", "\033[D", "\033[C", NULL
|
||||
};
|
||||
static const char* rxvt_256color_funcs[] =
|
||||
{
|
||||
"\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l",
|
||||
"\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m",
|
||||
"\033=", "\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
|
||||
};
|
||||
|
||||
// Eterm
|
||||
static const char* eterm_keys[] =
|
||||
{
|
||||
"\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~",
|
||||
"\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~",
|
||||
"\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~",
|
||||
"\033[A", "\033[B", "\033[D", "\033[C", NULL
|
||||
};
|
||||
static const char* eterm_funcs[] =
|
||||
{
|
||||
"\0337\033[?47h", "\033[2J\033[?47l\0338", "\033[?25h", "\033[?25l",
|
||||
"\033[H\033[2J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m",
|
||||
"", "", "", "",
|
||||
};
|
||||
|
||||
// screen
|
||||
static const char* screen_keys[] =
|
||||
{
|
||||
"\033OP", "\033OQ", "\033OR", "\033OS", "\033[15~", "\033[17~",
|
||||
"\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~",
|
||||
"\033[2~", "\033[3~", "\033[1~", "\033[4~", "\033[5~", "\033[6~",
|
||||
"\033OA", "\033OB", "\033OD", "\033OC", NULL
|
||||
};
|
||||
static const char* screen_funcs[] =
|
||||
{
|
||||
"\033[?1049h", "\033[?1049l", "\033[34h\033[?25h", "\033[?25l",
|
||||
"\033[H\033[J", "\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m",
|
||||
"\033[?1h\033=", "\033[?1l\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
|
||||
};
|
||||
|
||||
// rxvt-unicode
|
||||
static const char* rxvt_unicode_keys[] =
|
||||
{
|
||||
"\033[11~", "\033[12~", "\033[13~", "\033[14~", "\033[15~", "\033[17~",
|
||||
"\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~",
|
||||
"\033[2~", "\033[3~", "\033[7~", "\033[8~", "\033[5~", "\033[6~",
|
||||
"\033[A", "\033[B", "\033[D", "\033[C", NULL
|
||||
};
|
||||
static const char* rxvt_unicode_funcs[] =
|
||||
{
|
||||
"\033[?1049h", "\033[r\033[?1049l", "\033[?25h", "\033[?25l",
|
||||
"\033[H\033[2J", "\033[m\033(B", "\033[4m", "\033[1m", "\033[5m",
|
||||
"\033[7m", "\033=", "\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
|
||||
};
|
||||
|
||||
// linux
|
||||
static const char* linux_keys[] =
|
||||
{
|
||||
"\033[[A", "\033[[B", "\033[[C", "\033[[D", "\033[[E", "\033[17~",
|
||||
"\033[18~", "\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~",
|
||||
"\033[2~", "\033[3~", "\033[1~", "\033[4~", "\033[5~", "\033[6~",
|
||||
"\033[A", "\033[B", "\033[D", "\033[C", NULL
|
||||
};
|
||||
static const char* linux_funcs[] =
|
||||
{
|
||||
"", "", "\033[?25h\033[?0c", "\033[?25l\033[?1c", "\033[H\033[J",
|
||||
"\033[0;10m", "\033[4m", "\033[1m", "\033[5m", "\033[7m", "", "", "", "",
|
||||
};
|
||||
|
||||
// xterm
|
||||
static const char* xterm_keys[] =
|
||||
{
|
||||
"\033OP", "\033OQ", "\033OR", "\033OS", "\033[15~", "\033[17~", "\033[18~",
|
||||
"\033[19~", "\033[20~", "\033[21~", "\033[23~", "\033[24~", "\033[2~",
|
||||
"\033[3~", "\033OH", "\033OF", "\033[5~", "\033[6~", "\033OA", "\033OB",
|
||||
"\033OD", "\033OC", NULL
|
||||
};
|
||||
static const char* xterm_funcs[] =
|
||||
{
|
||||
"\033[?1049h", "\033[?1049l", "\033[?12l\033[?25h", "\033[?25l",
|
||||
"\033[H\033[2J", "\033(B\033[m", "\033[4m", "\033[1m", "\033[5m", "\033[7m",
|
||||
"\033[?1h\033=", "\033[?1l\033>", ENTER_MOUSE_SEQ, EXIT_MOUSE_SEQ,
|
||||
};
|
||||
|
||||
struct term
|
||||
{
|
||||
const char* name;
|
||||
const char** keys;
|
||||
const char** funcs;
|
||||
};
|
||||
|
||||
static struct term terms[] =
|
||||
{
|
||||
{"rxvt-256color", rxvt_256color_keys, rxvt_256color_funcs},
|
||||
{"Eterm", eterm_keys, eterm_funcs},
|
||||
{"screen", screen_keys, screen_funcs},
|
||||
{"rxvt-unicode", rxvt_unicode_keys, rxvt_unicode_funcs},
|
||||
{"linux", linux_keys, linux_funcs},
|
||||
{"xterm", xterm_keys, xterm_funcs},
|
||||
{0, 0, 0},
|
||||
};
|
||||
|
||||
static int init_from_terminfo = 0;
|
||||
const char** keys;
|
||||
const char** funcs;
|
||||
|
||||
static int try_compatible(const char* term, const char* name,
|
||||
const char** tkeys, const char** tfuncs)
|
||||
{
|
||||
if (strstr(term, name))
|
||||
{
|
||||
keys = tkeys;
|
||||
funcs = tfuncs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return EUNSUPPORTED_TERM;
|
||||
}
|
||||
|
||||
static int init_term_builtin(void)
|
||||
{
|
||||
int i;
|
||||
const char* term = getenv("TERM");
|
||||
|
||||
if (term)
|
||||
{
|
||||
for (i = 0; terms[i].name; i++)
|
||||
{
|
||||
if (!strcmp(terms[i].name, term))
|
||||
{
|
||||
keys = terms[i].keys;
|
||||
funcs = terms[i].funcs;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// let's do some heuristic, maybe it's a compatible terminal
|
||||
if (try_compatible(term, "xterm", xterm_keys, xterm_funcs) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (try_compatible(term, "rxvt", rxvt_unicode_keys, rxvt_unicode_funcs) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (try_compatible(term, "linux", linux_keys, linux_funcs) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (try_compatible(term, "Eterm", eterm_keys, eterm_funcs) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (try_compatible(term, "screen", screen_keys, screen_funcs) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// let's assume that 'cygwin' is xterm compatible
|
||||
if (try_compatible(term, "cygwin", xterm_keys, xterm_funcs) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return EUNSUPPORTED_TERM;
|
||||
}
|
||||
|
||||
// terminfo
|
||||
static char* read_file(const char* file)
|
||||
{
|
||||
FILE* f = fopen(file, "rb");
|
||||
|
||||
if (!f)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
|
||||
if (fstat(fileno(f), &st) != 0)
|
||||
{
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* data = malloc(st.st_size);
|
||||
|
||||
if (!data)
|
||||
{
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fread(data, 1, st.st_size, f) != (size_t)st.st_size)
|
||||
{
|
||||
fclose(f);
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return data;
|
||||
}
|
||||
|
||||
static char* terminfo_try_path(const char* path, const char* term)
|
||||
{
|
||||
char tmp[4096];
|
||||
snprintf(tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term);
|
||||
tmp[sizeof(tmp) - 1] = '\0';
|
||||
char* data = read_file(tmp);
|
||||
|
||||
if (data)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
// fallback to darwin specific dirs structure
|
||||
snprintf(tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term);
|
||||
tmp[sizeof(tmp) - 1] = '\0';
|
||||
return read_file(tmp);
|
||||
}
|
||||
|
||||
static char* load_terminfo(void)
|
||||
{
|
||||
char tmp[4096];
|
||||
const char* term = getenv("TERM");
|
||||
|
||||
if (!term)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// if TERMINFO is set, no other directory should be searched
|
||||
const char* terminfo = getenv("TERMINFO");
|
||||
|
||||
if (terminfo)
|
||||
{
|
||||
return terminfo_try_path(terminfo, term);
|
||||
}
|
||||
|
||||
// next, consider ~/.terminfo
|
||||
const char* home = getenv("HOME");
|
||||
|
||||
if (home)
|
||||
{
|
||||
snprintf(tmp, sizeof(tmp), "%s/.terminfo", home);
|
||||
tmp[sizeof(tmp) - 1] = '\0';
|
||||
char* data = terminfo_try_path(tmp, term);
|
||||
|
||||
if (data)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
// next, TERMINFO_DIRS
|
||||
const char* dirs = getenv("TERMINFO_DIRS");
|
||||
|
||||
if (dirs)
|
||||
{
|
||||
snprintf(tmp, sizeof(tmp), "%s", dirs);
|
||||
tmp[sizeof(tmp) - 1] = '\0';
|
||||
char* dir = strtok(tmp, ":");
|
||||
|
||||
while (dir)
|
||||
{
|
||||
const char* cdir = dir;
|
||||
|
||||
if (strcmp(cdir, "") == 0)
|
||||
{
|
||||
cdir = "/usr/share/terminfo";
|
||||
}
|
||||
|
||||
char* data = terminfo_try_path(cdir, term);
|
||||
|
||||
if (data)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
dir = strtok(0, ":");
|
||||
}
|
||||
}
|
||||
|
||||
// fallback to /usr/share/terminfo
|
||||
return terminfo_try_path("/usr/share/terminfo", term);
|
||||
}
|
||||
|
||||
#define TI_MAGIC 0432
|
||||
#define TI_ALT_MAGIC 542
|
||||
#define TI_HEADER_LENGTH 12
|
||||
#define TB_KEYS_NUM 22
|
||||
|
||||
static const char* terminfo_copy_string(char* data, int str, int table)
|
||||
{
|
||||
const int16_t off = *(int16_t*)(data + str);
|
||||
const char* src = data + table + off;
|
||||
int len = strlen(src);
|
||||
char* dst = malloc(len + 1);
|
||||
strcpy(dst, src);
|
||||
return dst;
|
||||
}
|
||||
|
||||
const int16_t ti_funcs[] =
|
||||
{
|
||||
28, 40, 16, 13, 5, 39, 36, 27, 26, 34, 89, 88,
|
||||
};
|
||||
|
||||
const int16_t ti_keys[] =
|
||||
{
|
||||
// apparently not a typo; 67 is F10 for whatever reason
|
||||
66, 68, 69, 70, 71, 72, 73, 74, 75, 67, 216, 217, 77, 59, 76, 164, 82,
|
||||
81, 87, 61, 79, 83,
|
||||
};
|
||||
|
||||
int init_term(void)
|
||||
{
|
||||
int i;
|
||||
char* data = load_terminfo();
|
||||
|
||||
if (!data)
|
||||
{
|
||||
init_from_terminfo = 0;
|
||||
return init_term_builtin();
|
||||
}
|
||||
|
||||
int16_t* header = (int16_t*)data;
|
||||
|
||||
const int number_sec_len = header[0] == TI_ALT_MAGIC ? 4 : 2;
|
||||
|
||||
if ((header[1] + header[2]) % 2)
|
||||
{
|
||||
// old quirk to align everything on word boundaries
|
||||
header[2] += 1;
|
||||
}
|
||||
|
||||
const int str_offset = TI_HEADER_LENGTH +
|
||||
header[1] + header[2] + number_sec_len * header[3];
|
||||
const int table_offset = str_offset + 2 * header[4];
|
||||
|
||||
keys = malloc(sizeof(const char*) * (TB_KEYS_NUM + 1));
|
||||
|
||||
for (i = 0; i < TB_KEYS_NUM; i++)
|
||||
{
|
||||
keys[i] = terminfo_copy_string(data,
|
||||
str_offset + 2 * ti_keys[i], table_offset);
|
||||
}
|
||||
|
||||
keys[i] = NULL;
|
||||
|
||||
funcs = malloc(sizeof(const char*) * T_FUNCS_NUM);
|
||||
|
||||
// the last two entries are reserved for mouse. because the table offset is
|
||||
// not there, the two entries have to fill in manually
|
||||
for (i = 0; i < T_FUNCS_NUM - 2; i++)
|
||||
{
|
||||
funcs[i] = terminfo_copy_string(data,
|
||||
str_offset + 2 * ti_funcs[i], table_offset);
|
||||
}
|
||||
|
||||
funcs[T_FUNCS_NUM - 2] = ENTER_MOUSE_SEQ;
|
||||
funcs[T_FUNCS_NUM - 1] = EXIT_MOUSE_SEQ;
|
||||
init_from_terminfo = 1;
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void shutdown_term(void)
|
||||
{
|
||||
if (init_from_terminfo)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TB_KEYS_NUM; i++)
|
||||
{
|
||||
free((void*)keys[i]);
|
||||
}
|
||||
|
||||
// the last two entries are reserved for mouse. because the table offset
|
||||
// is not there, the two entries have to fill in manually and do not
|
||||
// need to be freed.
|
||||
for (i = 0; i < T_FUNCS_NUM - 2; i++)
|
||||
{
|
||||
free((void*)funcs[i]);
|
||||
}
|
||||
|
||||
free(keys);
|
||||
free(funcs);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
#ifndef H_TERM
|
||||
#define H_TERM
|
||||
|
||||
#include "termbox.h"
|
||||
#include "ringbuffer.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#define EUNSUPPORTED_TERM -1
|
||||
|
||||
enum
|
||||
{
|
||||
T_ENTER_CA,
|
||||
T_EXIT_CA,
|
||||
T_SHOW_CURSOR,
|
||||
T_HIDE_CURSOR,
|
||||
T_CLEAR_SCREEN,
|
||||
T_SGR0,
|
||||
T_UNDERLINE,
|
||||
T_BOLD,
|
||||
T_BLINK,
|
||||
T_REVERSE,
|
||||
T_ENTER_KEYPAD,
|
||||
T_EXIT_KEYPAD,
|
||||
T_ENTER_MOUSE,
|
||||
T_EXIT_MOUSE,
|
||||
T_FUNCS_NUM,
|
||||
};
|
||||
|
||||
extern const char** keys;
|
||||
extern const char** funcs;
|
||||
|
||||
// true on success, false on failure
|
||||
bool extract_event(struct tb_event* event, struct ringbuffer* inbuf,
|
||||
int inputmode);
|
||||
int init_term(void);
|
||||
void shutdown_term(void);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,885 +0,0 @@
|
|||
#include "term.h"
|
||||
#include "termbox.h"
|
||||
#include "memstream.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <wchar.h>
|
||||
|
||||
struct cellbuf
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
struct tb_cell* cells;
|
||||
};
|
||||
|
||||
#define CELL(buf, x, y) (buf)->cells[(y) * (buf)->width + (x)]
|
||||
#define IS_CURSOR_HIDDEN(cx, cy) (cx == -1 || cy == -1)
|
||||
#define LAST_COORD_INIT -1
|
||||
|
||||
static struct termios orig_tios;
|
||||
|
||||
static struct cellbuf back_buffer;
|
||||
static struct cellbuf front_buffer;
|
||||
static unsigned char write_buffer_data[32 * 1024];
|
||||
static struct memstream write_buffer;
|
||||
|
||||
static int termw = -1;
|
||||
static int termh = -1;
|
||||
|
||||
static int inputmode = TB_INPUT_ESC;
|
||||
static int outputmode = TB_OUTPUT_NORMAL;
|
||||
|
||||
static struct ringbuffer inbuf;
|
||||
|
||||
static int out;
|
||||
static FILE* in;
|
||||
|
||||
static int out_fileno;
|
||||
static int in_fileno;
|
||||
|
||||
static int winch_fds[2];
|
||||
|
||||
static int lastx = LAST_COORD_INIT;
|
||||
static int lasty = LAST_COORD_INIT;
|
||||
static int cursor_x = -1;
|
||||
static int cursor_y = -1;
|
||||
|
||||
static uint32_t background = TB_DEFAULT;
|
||||
static uint32_t foreground = TB_DEFAULT;
|
||||
|
||||
static void write_cursor(int x, int y);
|
||||
static void write_sgr(uint32_t fg, uint32_t bg);
|
||||
|
||||
static void cellbuf_init(struct cellbuf* buf, int width, int height);
|
||||
static void cellbuf_resize(struct cellbuf* buf, int width, int height);
|
||||
static void cellbuf_clear(struct cellbuf* buf);
|
||||
static void cellbuf_free(struct cellbuf* buf);
|
||||
|
||||
static void update_size(void);
|
||||
static void update_term_size(void);
|
||||
static void send_attr(uint32_t fg, uint32_t bg);
|
||||
static void send_char(int x, int y, uint32_t c);
|
||||
static void send_clear(void);
|
||||
static void sigwinch_handler(int xxx);
|
||||
static int wait_fill_event(struct tb_event* event, struct timeval* timeout);
|
||||
|
||||
// may happen in a different thread
|
||||
static volatile int buffer_size_change_request;
|
||||
|
||||
int tb_init_file(const char* name)
|
||||
{
|
||||
out = open(name, O_WRONLY);
|
||||
in = fopen(name, "r");
|
||||
|
||||
if (out == -1 || !in)
|
||||
{
|
||||
if (out != -1)
|
||||
{
|
||||
close(out);
|
||||
}
|
||||
|
||||
if (in)
|
||||
{
|
||||
fclose(in);
|
||||
}
|
||||
|
||||
return TB_EFAILED_TO_OPEN_TTY;
|
||||
}
|
||||
|
||||
out_fileno = out;
|
||||
in_fileno = fileno(in);
|
||||
|
||||
if (init_term() < 0)
|
||||
{
|
||||
close(out);
|
||||
fclose(in);
|
||||
|
||||
return TB_EUNSUPPORTED_TERMINAL;
|
||||
}
|
||||
|
||||
if (pipe(winch_fds) < 0)
|
||||
{
|
||||
close(out);
|
||||
fclose(in);
|
||||
|
||||
return TB_EPIPE_TRAP_ERROR;
|
||||
}
|
||||
|
||||
struct sigaction sa;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = sigwinch_handler;
|
||||
sa.sa_flags = 0;
|
||||
sigaction(SIGWINCH, &sa, 0);
|
||||
tcgetattr(out_fileno, &orig_tios);
|
||||
|
||||
struct termios tios;
|
||||
|
||||
memcpy(&tios, &orig_tios, sizeof(tios));
|
||||
tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
|
||||
| INLCR | IGNCR | ICRNL | IXON);
|
||||
tios.c_oflag &= ~OPOST;
|
||||
tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
tios.c_cflag &= ~(CSIZE | PARENB);
|
||||
tios.c_cflag |= CS8;
|
||||
tios.c_cc[VMIN] = 0;
|
||||
tios.c_cc[VTIME] = 0;
|
||||
tcsetattr(out_fileno, TCSAFLUSH, &tios);
|
||||
|
||||
memstream_init(&write_buffer, out_fileno, write_buffer_data,
|
||||
sizeof(write_buffer_data));
|
||||
memstream_puts(&write_buffer, funcs[T_ENTER_CA]);
|
||||
memstream_puts(&write_buffer, funcs[T_ENTER_KEYPAD]);
|
||||
memstream_puts(&write_buffer, funcs[T_HIDE_CURSOR]);
|
||||
send_clear();
|
||||
|
||||
update_term_size();
|
||||
cellbuf_init(&back_buffer, termw, termh);
|
||||
cellbuf_init(&front_buffer, termw, termh);
|
||||
cellbuf_clear(&back_buffer);
|
||||
cellbuf_clear(&front_buffer);
|
||||
init_ringbuffer(&inbuf, 4096);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tb_init(void)
|
||||
{
|
||||
return tb_init_file("/dev/tty");
|
||||
}
|
||||
|
||||
void tb_shutdown(void)
|
||||
{
|
||||
if (termw == -1)
|
||||
{
|
||||
fputs("tb_shutdown() should not be called twice.", stderr);
|
||||
abort();
|
||||
}
|
||||
|
||||
memstream_puts(&write_buffer, funcs[T_SHOW_CURSOR]);
|
||||
memstream_puts(&write_buffer, funcs[T_SGR0]);
|
||||
memstream_puts(&write_buffer, funcs[T_CLEAR_SCREEN]);
|
||||
memstream_puts(&write_buffer, funcs[T_EXIT_CA]);
|
||||
memstream_puts(&write_buffer, funcs[T_EXIT_KEYPAD]);
|
||||
memstream_puts(&write_buffer, funcs[T_EXIT_MOUSE]);
|
||||
memstream_flush(&write_buffer);
|
||||
tcsetattr(out_fileno, TCSAFLUSH, &orig_tios);
|
||||
|
||||
shutdown_term();
|
||||
close(out);
|
||||
fclose(in);
|
||||
close(winch_fds[0]);
|
||||
close(winch_fds[1]);
|
||||
|
||||
cellbuf_free(&back_buffer);
|
||||
cellbuf_free(&front_buffer);
|
||||
free_ringbuffer(&inbuf);
|
||||
termw = termh = -1;
|
||||
}
|
||||
|
||||
void tb_present(void)
|
||||
{
|
||||
int x, y, w, i;
|
||||
struct tb_cell* back, *front;
|
||||
|
||||
// invalidate cursor position
|
||||
lastx = LAST_COORD_INIT;
|
||||
lasty = LAST_COORD_INIT;
|
||||
|
||||
if (buffer_size_change_request)
|
||||
{
|
||||
update_size();
|
||||
buffer_size_change_request = 0;
|
||||
}
|
||||
|
||||
for (y = 0; y < front_buffer.height; ++y)
|
||||
{
|
||||
for (x = 0; x < front_buffer.width;)
|
||||
{
|
||||
back = &CELL(&back_buffer, x, y);
|
||||
front = &CELL(&front_buffer, x, y);
|
||||
w = wcwidth(back->ch);
|
||||
|
||||
if (w < 1)
|
||||
{
|
||||
w = 1;
|
||||
}
|
||||
|
||||
if (memcmp(back, front, sizeof(struct tb_cell)) == 0)
|
||||
{
|
||||
x += w;
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(front, back, sizeof(struct tb_cell));
|
||||
send_attr(back->fg, back->bg);
|
||||
|
||||
if (w > 1 && x >= front_buffer.width - (w - 1))
|
||||
{
|
||||
// Not enough room for wide ch, so send spaces
|
||||
for (i = x; i < front_buffer.width; ++i)
|
||||
{
|
||||
send_char(i, y, ' ');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
send_char(x, y, back->ch);
|
||||
|
||||
for (i = 1; i < w; ++i)
|
||||
{
|
||||
front = &CELL(&front_buffer, x + i, y);
|
||||
front->ch = 0;
|
||||
front->fg = back->fg;
|
||||
front->bg = back->bg;
|
||||
}
|
||||
}
|
||||
|
||||
x += w;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
|
||||
{
|
||||
write_cursor(cursor_x, cursor_y);
|
||||
}
|
||||
|
||||
memstream_flush(&write_buffer);
|
||||
}
|
||||
|
||||
void tb_set_cursor(int cx, int cy)
|
||||
{
|
||||
if (IS_CURSOR_HIDDEN(cursor_x, cursor_y) && !IS_CURSOR_HIDDEN(cx, cy))
|
||||
{
|
||||
memstream_puts(&write_buffer, funcs[T_SHOW_CURSOR]);
|
||||
}
|
||||
|
||||
if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y) && IS_CURSOR_HIDDEN(cx, cy))
|
||||
{
|
||||
memstream_puts(&write_buffer, funcs[T_HIDE_CURSOR]);
|
||||
}
|
||||
|
||||
cursor_x = cx;
|
||||
cursor_y = cy;
|
||||
|
||||
if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
|
||||
{
|
||||
write_cursor(cursor_x, cursor_y);
|
||||
}
|
||||
}
|
||||
|
||||
void tb_put_cell(int x, int y, const struct tb_cell* cell)
|
||||
{
|
||||
if ((unsigned)x >= (unsigned)back_buffer.width)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((unsigned)y >= (unsigned)back_buffer.height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CELL(&back_buffer, x, y) = *cell;
|
||||
}
|
||||
|
||||
void tb_change_cell(int x, int y, uint32_t ch, uint32_t fg, uint32_t bg)
|
||||
{
|
||||
struct tb_cell c = {ch, fg, bg};
|
||||
tb_put_cell(x, y, &c);
|
||||
}
|
||||
|
||||
void tb_blit(int x, int y, int w, int h, const struct tb_cell* cells)
|
||||
{
|
||||
if (x + w < 0 || x >= back_buffer.width)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (y + h < 0 || y >= back_buffer.height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int xo = 0, yo = 0, ww = w, hh = h;
|
||||
|
||||
if (x < 0)
|
||||
{
|
||||
xo = -x;
|
||||
ww -= xo;
|
||||
x = 0;
|
||||
}
|
||||
|
||||
if (y < 0)
|
||||
{
|
||||
yo = -y;
|
||||
hh -= yo;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
if (ww > back_buffer.width - x)
|
||||
{
|
||||
ww = back_buffer.width - x;
|
||||
}
|
||||
|
||||
if (hh > back_buffer.height - y)
|
||||
{
|
||||
hh = back_buffer.height - y;
|
||||
}
|
||||
|
||||
int sy;
|
||||
struct tb_cell* dst = &CELL(&back_buffer, x, y);
|
||||
const struct tb_cell* src = cells + yo * w + xo;
|
||||
size_t size = sizeof(struct tb_cell) * ww;
|
||||
|
||||
for (sy = 0; sy < hh; ++sy)
|
||||
{
|
||||
memcpy(dst, src, size);
|
||||
dst += back_buffer.width;
|
||||
src += w;
|
||||
}
|
||||
}
|
||||
|
||||
struct tb_cell* tb_cell_buffer(void)
|
||||
{
|
||||
return back_buffer.cells;
|
||||
}
|
||||
|
||||
int tb_poll_event(struct tb_event* event)
|
||||
{
|
||||
return wait_fill_event(event, 0);
|
||||
}
|
||||
|
||||
int tb_peek_event(struct tb_event* event, int timeout)
|
||||
{
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeout / 1000;
|
||||
tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
|
||||
return wait_fill_event(event, &tv);
|
||||
}
|
||||
|
||||
int tb_width(void)
|
||||
{
|
||||
return termw;
|
||||
}
|
||||
|
||||
int tb_height(void)
|
||||
{
|
||||
return termh;
|
||||
}
|
||||
|
||||
void tb_clear(void)
|
||||
{
|
||||
if (buffer_size_change_request)
|
||||
{
|
||||
update_size();
|
||||
buffer_size_change_request = 0;
|
||||
}
|
||||
|
||||
cellbuf_clear(&back_buffer);
|
||||
}
|
||||
|
||||
int tb_select_input_mode(int mode)
|
||||
{
|
||||
if (mode)
|
||||
{
|
||||
if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == 0)
|
||||
{
|
||||
mode |= TB_INPUT_ESC;
|
||||
}
|
||||
|
||||
// technically termbox can handle that, but let's be nice
|
||||
// and show here what mode is actually used
|
||||
if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == (TB_INPUT_ESC | TB_INPUT_ALT))
|
||||
{
|
||||
mode &= ~TB_INPUT_ALT;
|
||||
}
|
||||
|
||||
inputmode = mode;
|
||||
|
||||
if (mode & TB_INPUT_MOUSE)
|
||||
{
|
||||
memstream_puts(&write_buffer, funcs[T_ENTER_MOUSE]);
|
||||
memstream_flush(&write_buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
memstream_puts(&write_buffer, funcs[T_EXIT_MOUSE]);
|
||||
memstream_flush(&write_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return inputmode;
|
||||
}
|
||||
|
||||
int tb_select_output_mode(int mode)
|
||||
{
|
||||
if (mode)
|
||||
{
|
||||
outputmode = mode;
|
||||
}
|
||||
|
||||
return outputmode;
|
||||
}
|
||||
|
||||
void tb_set_clear_attributes(uint32_t fg, uint32_t bg)
|
||||
{
|
||||
foreground = fg;
|
||||
background = bg;
|
||||
}
|
||||
|
||||
static unsigned convertnum(uint32_t num, char* buf)
|
||||
{
|
||||
unsigned i, l = 0;
|
||||
int ch;
|
||||
|
||||
do
|
||||
{
|
||||
buf[l++] = '0' + (num % 10);
|
||||
num /= 10;
|
||||
}
|
||||
while (num);
|
||||
|
||||
for (i = 0; i < l / 2; i++)
|
||||
{
|
||||
ch = buf[i];
|
||||
buf[i] = buf[l - 1 - i];
|
||||
buf[l - 1 - i] = ch;
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
#define WRITE_LITERAL(X) memstream_write(&write_buffer, (X), sizeof(X) -1)
|
||||
#define WRITE_INT(X) memstream_write(&write_buffer, buf, convertnum((X), buf))
|
||||
|
||||
static void write_cursor(int x, int y)
|
||||
{
|
||||
char buf[32];
|
||||
WRITE_LITERAL("\033[");
|
||||
WRITE_INT(y + 1);
|
||||
WRITE_LITERAL(";");
|
||||
WRITE_INT(x + 1);
|
||||
WRITE_LITERAL("H");
|
||||
}
|
||||
|
||||
static void write_sgr(uint32_t fg, uint32_t bg)
|
||||
{
|
||||
char buf[32];
|
||||
|
||||
if (outputmode != TB_OUTPUT_TRUECOLOR && fg == TB_DEFAULT && bg == TB_DEFAULT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (outputmode)
|
||||
{
|
||||
case TB_OUTPUT_TRUECOLOR:
|
||||
WRITE_LITERAL("\033[38;2;");
|
||||
WRITE_INT(fg >> 16 & 0xFF);
|
||||
WRITE_LITERAL(";");
|
||||
WRITE_INT(fg >> 8 & 0xFF);
|
||||
WRITE_LITERAL(";");
|
||||
WRITE_INT(fg & 0xFF);
|
||||
WRITE_LITERAL(";48;2;");
|
||||
WRITE_INT(bg >> 16 & 0xFF);
|
||||
WRITE_LITERAL(";");
|
||||
WRITE_INT(bg >> 8 & 0xFF);
|
||||
WRITE_LITERAL(";");
|
||||
WRITE_INT(bg & 0xFF);
|
||||
WRITE_LITERAL("m");
|
||||
break;
|
||||
|
||||
case TB_OUTPUT_256:
|
||||
case TB_OUTPUT_216:
|
||||
case TB_OUTPUT_GRAYSCALE:
|
||||
WRITE_LITERAL("\033[");
|
||||
|
||||
if (fg != TB_DEFAULT)
|
||||
{
|
||||
WRITE_LITERAL("38;5;");
|
||||
WRITE_INT(fg);
|
||||
|
||||
if (bg != TB_DEFAULT)
|
||||
{
|
||||
WRITE_LITERAL(";");
|
||||
}
|
||||
}
|
||||
|
||||
if (bg != TB_DEFAULT)
|
||||
{
|
||||
WRITE_LITERAL("48;5;");
|
||||
WRITE_INT(bg);
|
||||
}
|
||||
|
||||
WRITE_LITERAL("m");
|
||||
break;
|
||||
|
||||
case TB_OUTPUT_NORMAL:
|
||||
default:
|
||||
WRITE_LITERAL("\033[");
|
||||
|
||||
if (fg != TB_DEFAULT)
|
||||
{
|
||||
WRITE_LITERAL("3");
|
||||
WRITE_INT(fg - 1);
|
||||
|
||||
if (bg != TB_DEFAULT)
|
||||
{
|
||||
WRITE_LITERAL(";");
|
||||
}
|
||||
}
|
||||
|
||||
if (bg != TB_DEFAULT)
|
||||
{
|
||||
WRITE_LITERAL("4");
|
||||
WRITE_INT(bg - 1);
|
||||
}
|
||||
|
||||
WRITE_LITERAL("m");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void cellbuf_init(struct cellbuf* buf, int width, int height)
|
||||
{
|
||||
buf->cells = (struct tb_cell*)malloc(sizeof(struct tb_cell) * width * height);
|
||||
assert(buf->cells);
|
||||
buf->width = width;
|
||||
buf->height = height;
|
||||
}
|
||||
|
||||
static void cellbuf_resize(struct cellbuf* buf, int width, int height)
|
||||
{
|
||||
if (buf->width == width && buf->height == height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int oldw = buf->width;
|
||||
int oldh = buf->height;
|
||||
struct tb_cell* oldcells = buf->cells;
|
||||
|
||||
cellbuf_init(buf, width, height);
|
||||
cellbuf_clear(buf);
|
||||
|
||||
int minw = (width < oldw) ? width : oldw;
|
||||
int minh = (height < oldh) ? height : oldh;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < minh; ++i)
|
||||
{
|
||||
struct tb_cell* csrc = oldcells + (i * oldw);
|
||||
struct tb_cell* cdst = buf->cells + (i * width);
|
||||
memcpy(cdst, csrc, sizeof(struct tb_cell) * minw);
|
||||
}
|
||||
|
||||
free(oldcells);
|
||||
}
|
||||
|
||||
static void cellbuf_clear(struct cellbuf* buf)
|
||||
{
|
||||
int i;
|
||||
int ncells = buf->width * buf->height;
|
||||
|
||||
for (i = 0; i < ncells; ++i)
|
||||
{
|
||||
buf->cells[i].ch = ' ';
|
||||
buf->cells[i].fg = foreground;
|
||||
buf->cells[i].bg = background;
|
||||
}
|
||||
}
|
||||
|
||||
static void cellbuf_free(struct cellbuf* buf)
|
||||
{
|
||||
free(buf->cells);
|
||||
}
|
||||
|
||||
static void get_term_size(int* w, int* h)
|
||||
{
|
||||
struct winsize sz;
|
||||
memset(&sz, 0, sizeof(sz));
|
||||
|
||||
ioctl(out_fileno, TIOCGWINSZ, &sz);
|
||||
|
||||
if (w)
|
||||
{
|
||||
*w = sz.ws_col;
|
||||
}
|
||||
|
||||
if (h)
|
||||
{
|
||||
*h = sz.ws_row;
|
||||
}
|
||||
}
|
||||
|
||||
static void send_attr(uint32_t fg, uint32_t bg)
|
||||
{
|
||||
#define LAST_ATTR_INIT 0xFFFFFFFF
|
||||
static uint32_t lastfg = LAST_ATTR_INIT, lastbg = LAST_ATTR_INIT;
|
||||
|
||||
if (fg != lastfg || bg != lastbg)
|
||||
{
|
||||
memstream_puts(&write_buffer, funcs[T_SGR0]);
|
||||
uint32_t fgcol;
|
||||
uint32_t bgcol;
|
||||
|
||||
switch (outputmode)
|
||||
{
|
||||
case TB_OUTPUT_TRUECOLOR:
|
||||
fgcol = fg;
|
||||
bgcol = bg;
|
||||
break;
|
||||
|
||||
case TB_OUTPUT_256:
|
||||
fgcol = fg & 0xFF;
|
||||
bgcol = bg & 0xFF;
|
||||
break;
|
||||
|
||||
case TB_OUTPUT_216:
|
||||
fgcol = fg & 0xFF;
|
||||
|
||||
if (fgcol > 215)
|
||||
{
|
||||
fgcol = 7;
|
||||
}
|
||||
|
||||
bgcol = bg & 0xFF;
|
||||
|
||||
if (bgcol > 215)
|
||||
{
|
||||
bgcol = 0;
|
||||
}
|
||||
|
||||
fgcol += 0x10;
|
||||
bgcol += 0x10;
|
||||
break;
|
||||
|
||||
case TB_OUTPUT_GRAYSCALE:
|
||||
fgcol = fg & 0xFF;
|
||||
|
||||
if (fgcol > 23)
|
||||
{
|
||||
fgcol = 23;
|
||||
}
|
||||
|
||||
bgcol = bg & 0xFF;
|
||||
|
||||
if (bgcol > 23)
|
||||
{
|
||||
bgcol = 0;
|
||||
}
|
||||
|
||||
fgcol += 0xe8;
|
||||
bgcol += 0xe8;
|
||||
break;
|
||||
|
||||
case TB_OUTPUT_NORMAL:
|
||||
default:
|
||||
fgcol = fg & 0x0F;
|
||||
bgcol = bg & 0x0F;
|
||||
}
|
||||
|
||||
if (fg & TB_BOLD)
|
||||
{
|
||||
memstream_puts(&write_buffer, funcs[T_BOLD]);
|
||||
}
|
||||
|
||||
if (bg & TB_BOLD)
|
||||
{
|
||||
memstream_puts(&write_buffer, funcs[T_BLINK]);
|
||||
}
|
||||
|
||||
if (fg & TB_UNDERLINE)
|
||||
{
|
||||
memstream_puts(&write_buffer, funcs[T_UNDERLINE]);
|
||||
}
|
||||
|
||||
if ((fg & TB_REVERSE) || (bg & TB_REVERSE))
|
||||
{
|
||||
memstream_puts(&write_buffer, funcs[T_REVERSE]);
|
||||
}
|
||||
|
||||
write_sgr(fgcol, bgcol);
|
||||
|
||||
lastfg = fg;
|
||||
lastbg = bg;
|
||||
}
|
||||
}
|
||||
|
||||
static void send_char(int x, int y, uint32_t c)
|
||||
{
|
||||
char buf[7];
|
||||
int bw = utf8_unicode_to_char(buf, c);
|
||||
buf[bw] = '\0';
|
||||
|
||||
if (x - 1 != lastx || y != lasty)
|
||||
{
|
||||
write_cursor(x, y);
|
||||
}
|
||||
|
||||
lastx = x;
|
||||
lasty = y;
|
||||
|
||||
if (!c)
|
||||
{
|
||||
buf[0] = ' '; // replace 0 with whitespace
|
||||
}
|
||||
|
||||
memstream_puts(&write_buffer, buf);
|
||||
}
|
||||
|
||||
static void send_clear(void)
|
||||
{
|
||||
send_attr(foreground, background);
|
||||
memstream_puts(&write_buffer, funcs[T_CLEAR_SCREEN]);
|
||||
|
||||
if (!IS_CURSOR_HIDDEN(cursor_x, cursor_y))
|
||||
{
|
||||
write_cursor(cursor_x, cursor_y);
|
||||
}
|
||||
|
||||
memstream_flush(&write_buffer);
|
||||
|
||||
// we need to invalidate cursor position too and these two vars are
|
||||
// used only for simple cursor positioning optimization, cursor
|
||||
// actually may be in the correct place, but we simply discard
|
||||
// optimization once and it gives us simple solution for the case when
|
||||
// cursor moved
|
||||
lastx = LAST_COORD_INIT;
|
||||
lasty = LAST_COORD_INIT;
|
||||
}
|
||||
|
||||
static void sigwinch_handler(int xxx)
|
||||
{
|
||||
(void) xxx;
|
||||
const int zzz = 1;
|
||||
write(winch_fds[1], &zzz, sizeof(int));
|
||||
}
|
||||
|
||||
static void update_size(void)
|
||||
{
|
||||
update_term_size();
|
||||
cellbuf_resize(&back_buffer, termw, termh);
|
||||
cellbuf_resize(&front_buffer, termw, termh);
|
||||
cellbuf_clear(&front_buffer);
|
||||
send_clear();
|
||||
}
|
||||
|
||||
static void update_term_size(void)
|
||||
{
|
||||
struct winsize sz;
|
||||
memset(&sz, 0, sizeof(sz));
|
||||
|
||||
ioctl(out_fileno, TIOCGWINSZ, &sz);
|
||||
|
||||
termw = sz.ws_col;
|
||||
termh = sz.ws_row;
|
||||
}
|
||||
|
||||
static int wait_fill_event(struct tb_event* event, struct timeval* timeout)
|
||||
{
|
||||
#define ENOUGH_DATA_FOR_INPUT_PARSING 128
|
||||
int result;
|
||||
char buf[ENOUGH_DATA_FOR_INPUT_PARSING];
|
||||
fd_set events;
|
||||
memset(event, 0, sizeof(struct tb_event));
|
||||
|
||||
// try to extract event from input buffer, return on success
|
||||
event->type = TB_EVENT_KEY;
|
||||
|
||||
if (extract_event(event, &inbuf, inputmode))
|
||||
{
|
||||
return event->type;
|
||||
}
|
||||
|
||||
// it looks like input buffer is incomplete, let's try the short path
|
||||
size_t r = fread(buf, 1, ENOUGH_DATA_FOR_INPUT_PARSING, in);
|
||||
|
||||
if (r < ENOUGH_DATA_FOR_INPUT_PARSING && feof(in))
|
||||
{
|
||||
clearerr(in);
|
||||
}
|
||||
|
||||
if (r > 0)
|
||||
{
|
||||
if (ringbuffer_free_space(&inbuf) < r)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ringbuffer_push(&inbuf, buf, r);
|
||||
|
||||
if (extract_event(event, &inbuf, inputmode))
|
||||
{
|
||||
return event->type;
|
||||
}
|
||||
}
|
||||
|
||||
// no stuff in FILE's internal buffer, block in select
|
||||
while (1)
|
||||
{
|
||||
FD_ZERO(&events);
|
||||
FD_SET(in_fileno, &events);
|
||||
FD_SET(winch_fds[0], &events);
|
||||
int maxfd = (winch_fds[0] > in_fileno) ? winch_fds[0] : in_fileno;
|
||||
result = select(maxfd + 1, &events, 0, 0, timeout);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (FD_ISSET(in_fileno, &events))
|
||||
{
|
||||
event->type = TB_EVENT_KEY;
|
||||
size_t r = fread(buf, 1, ENOUGH_DATA_FOR_INPUT_PARSING, in);
|
||||
|
||||
if (r < ENOUGH_DATA_FOR_INPUT_PARSING && feof(in))
|
||||
{
|
||||
clearerr(in);
|
||||
}
|
||||
|
||||
if (r == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// if there is no free space in input buffer, return error
|
||||
if (ringbuffer_free_space(&inbuf) < r)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// fill buffer
|
||||
ringbuffer_push(&inbuf, buf, r);
|
||||
|
||||
if (extract_event(event, &inbuf, inputmode))
|
||||
{
|
||||
return event->type;
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(winch_fds[0], &events))
|
||||
{
|
||||
event->type = TB_EVENT_RESIZE;
|
||||
int zzz = 0;
|
||||
read(winch_fds[0], &zzz, sizeof(int));
|
||||
buffer_size_change_request = 1;
|
||||
get_term_size(&event->w, &event->h);
|
||||
return TB_EVENT_RESIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,307 +0,0 @@
|
|||
#ifndef H_TERMBOX
|
||||
#define H_TERMBOX
|
||||
#include <stdint.h>
|
||||
|
||||
// shared objects
|
||||
#if __GNUC__ >= 4
|
||||
#define SO_IMPORT __attribute__((visibility("default")))
|
||||
#else
|
||||
#define SO_IMPORT
|
||||
#endif
|
||||
|
||||
// c++
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Key constants. See also struct tb_event's key field.
|
||||
// These are a safe subset of terminfo keys, which exist on all popular
|
||||
// terminals. Termbox uses only them to stay truly portable.
|
||||
#define TB_KEY_F1 (0xFFFF-0)
|
||||
#define TB_KEY_F2 (0xFFFF-1)
|
||||
#define TB_KEY_F3 (0xFFFF-2)
|
||||
#define TB_KEY_F4 (0xFFFF-3)
|
||||
#define TB_KEY_F5 (0xFFFF-4)
|
||||
#define TB_KEY_F6 (0xFFFF-5)
|
||||
#define TB_KEY_F7 (0xFFFF-6)
|
||||
#define TB_KEY_F8 (0xFFFF-7)
|
||||
#define TB_KEY_F9 (0xFFFF-8)
|
||||
#define TB_KEY_F10 (0xFFFF-9)
|
||||
#define TB_KEY_F11 (0xFFFF-10)
|
||||
#define TB_KEY_F12 (0xFFFF-11)
|
||||
#define TB_KEY_INSERT (0xFFFF-12)
|
||||
#define TB_KEY_DELETE (0xFFFF-13)
|
||||
#define TB_KEY_HOME (0xFFFF-14)
|
||||
#define TB_KEY_END (0xFFFF-15)
|
||||
#define TB_KEY_PGUP (0xFFFF-16)
|
||||
#define TB_KEY_PGDN (0xFFFF-17)
|
||||
#define TB_KEY_ARROW_UP (0xFFFF-18)
|
||||
#define TB_KEY_ARROW_DOWN (0xFFFF-19)
|
||||
#define TB_KEY_ARROW_LEFT (0xFFFF-20)
|
||||
#define TB_KEY_ARROW_RIGHT (0xFFFF-21)
|
||||
#define TB_KEY_MOUSE_LEFT (0xFFFF-22)
|
||||
#define TB_KEY_MOUSE_RIGHT (0xFFFF-23)
|
||||
#define TB_KEY_MOUSE_MIDDLE (0xFFFF-24)
|
||||
#define TB_KEY_MOUSE_RELEASE (0xFFFF-25)
|
||||
#define TB_KEY_MOUSE_WHEEL_UP (0xFFFF-26)
|
||||
#define TB_KEY_MOUSE_WHEEL_DOWN (0xFFFF-27)
|
||||
|
||||
// These are all ASCII code points below SPACE character and a BACKSPACE key.
|
||||
#define TB_KEY_CTRL_TILDE 0x00
|
||||
#define TB_KEY_CTRL_2 0x00 // clash with 'CTRL_TILDE'
|
||||
#define TB_KEY_CTRL_A 0x01
|
||||
#define TB_KEY_CTRL_B 0x02
|
||||
#define TB_KEY_CTRL_C 0x03
|
||||
#define TB_KEY_CTRL_D 0x04
|
||||
#define TB_KEY_CTRL_E 0x05
|
||||
#define TB_KEY_CTRL_F 0x06
|
||||
#define TB_KEY_CTRL_G 0x07
|
||||
#define TB_KEY_BACKSPACE 0x08
|
||||
#define TB_KEY_CTRL_H 0x08 // clash with 'CTRL_BACKSPACE'
|
||||
#define TB_KEY_TAB 0x09
|
||||
#define TB_KEY_CTRL_I 0x09 // clash with 'TAB'
|
||||
#define TB_KEY_CTRL_J 0x0A
|
||||
#define TB_KEY_CTRL_K 0x0B
|
||||
#define TB_KEY_CTRL_L 0x0C
|
||||
#define TB_KEY_ENTER 0x0D
|
||||
#define TB_KEY_CTRL_M 0x0D // clash with 'ENTER'
|
||||
#define TB_KEY_CTRL_N 0x0E
|
||||
#define TB_KEY_CTRL_O 0x0F
|
||||
#define TB_KEY_CTRL_P 0x10
|
||||
#define TB_KEY_CTRL_Q 0x11
|
||||
#define TB_KEY_CTRL_R 0x12
|
||||
#define TB_KEY_CTRL_S 0x13
|
||||
#define TB_KEY_CTRL_T 0x14
|
||||
#define TB_KEY_CTRL_U 0x15
|
||||
#define TB_KEY_CTRL_V 0x16
|
||||
#define TB_KEY_CTRL_W 0x17
|
||||
#define TB_KEY_CTRL_X 0x18
|
||||
#define TB_KEY_CTRL_Y 0x19
|
||||
#define TB_KEY_CTRL_Z 0x1A
|
||||
#define TB_KEY_ESC 0x1B
|
||||
#define TB_KEY_CTRL_LSQ_BRACKET 0x1B // clash with 'ESC'
|
||||
#define TB_KEY_CTRL_3 0x1B // clash with 'ESC'
|
||||
#define TB_KEY_CTRL_4 0x1C
|
||||
#define TB_KEY_CTRL_BACKSLASH 0x1C // clash with 'CTRL_4'
|
||||
#define TB_KEY_CTRL_5 0x1D
|
||||
#define TB_KEY_CTRL_RSQ_BRACKET 0x1D // clash with 'CTRL_5'
|
||||
#define TB_KEY_CTRL_6 0x1E
|
||||
#define TB_KEY_CTRL_7 0x1F
|
||||
#define TB_KEY_CTRL_SLASH 0x1F // clash with 'CTRL_7'
|
||||
#define TB_KEY_CTRL_UNDERSCORE 0x1F // clash with 'CTRL_7'
|
||||
#define TB_KEY_SPACE 0x20
|
||||
#define TB_KEY_BACKSPACE2 0x7F
|
||||
#define TB_KEY_CTRL_8 0x7F // clash with 'BACKSPACE2'
|
||||
|
||||
// These are non-existing ones.
|
||||
// #define TB_KEY_CTRL_1 clash with '1'
|
||||
// #define TB_KEY_CTRL_9 clash with '9'
|
||||
// #define TB_KEY_CTRL_0 clash with '0'
|
||||
|
||||
// Alt modifier constant, see tb_event.mod field and tb_select_input_mode function.
|
||||
// Mouse-motion modifier
|
||||
#define TB_MOD_ALT 0x01
|
||||
#define TB_MOD_MOTION 0x02
|
||||
|
||||
// Colors (see struct tb_cell's fg and bg fields).
|
||||
#define TB_DEFAULT 0x00
|
||||
#define TB_BLACK 0x01
|
||||
#define TB_RED 0x02
|
||||
#define TB_GREEN 0x03
|
||||
#define TB_YELLOW 0x04
|
||||
#define TB_BLUE 0x05
|
||||
#define TB_MAGENTA 0x06
|
||||
#define TB_CYAN 0x07
|
||||
#define TB_WHITE 0x08
|
||||
|
||||
// Attributes, it is possible to use multiple attributes by combining them
|
||||
// using bitwise OR ('|'). Although, colors cannot be combined. But you can
|
||||
// combine attributes and a single color. See also struct tb_cell's fg and bg
|
||||
// fields.
|
||||
#define TB_BOLD 0x01000000
|
||||
#define TB_UNDERLINE 0x02000000
|
||||
#define TB_REVERSE 0x04000000
|
||||
|
||||
// A cell, single conceptual entity on the terminal screen. The terminal screen
|
||||
// is basically a 2d array of cells. It has the following fields:
|
||||
// - 'ch' is a unicode character
|
||||
// - 'fg' foreground color and attributes
|
||||
// - 'bg' background color and attributes
|
||||
struct tb_cell
|
||||
{
|
||||
uint32_t ch;
|
||||
uint32_t fg;
|
||||
uint32_t bg;
|
||||
};
|
||||
|
||||
#define TB_EVENT_KEY 1
|
||||
#define TB_EVENT_RESIZE 2
|
||||
#define TB_EVENT_MOUSE 3
|
||||
|
||||
// An event, single interaction from the user. The 'mod' and 'ch' fields are
|
||||
// valid if 'type' is TB_EVENT_KEY. The 'w' and 'h' fields are valid if 'type'
|
||||
// is TB_EVENT_RESIZE. The 'x' and 'y' fields are valid if 'type' is
|
||||
// TB_EVENT_MOUSE. The 'key' field is valid if 'type' is either TB_EVENT_KEY
|
||||
// or TB_EVENT_MOUSE. The fields 'key' and 'ch' are mutually exclusive; only
|
||||
// one of them can be non-zero at a time.
|
||||
struct tb_event
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t mod; // modifiers to either 'key' or 'ch' below
|
||||
uint16_t key; // one of the TB_KEY_* constants
|
||||
uint32_t ch; // unicode character
|
||||
int32_t w;
|
||||
int32_t h;
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
};
|
||||
|
||||
// Error codes returned by tb_init(). All of them are self-explanatory, except
|
||||
// the pipe trap error. Termbox uses unix pipes in order to deliver a message
|
||||
// from a signal handler (SIGWINCH) to the main event reading loop. Honestly in
|
||||
// most cases you should just check the returned code as < 0.
|
||||
#define TB_EUNSUPPORTED_TERMINAL -1
|
||||
#define TB_EFAILED_TO_OPEN_TTY -2
|
||||
#define TB_EPIPE_TRAP_ERROR -3
|
||||
|
||||
// Initializes the termbox library. This function should be called before any
|
||||
// other functions. Function tb_init is same as tb_init_file("/dev/tty"). After successful initialization, the library must be
|
||||
// finalized using the tb_shutdown() function.
|
||||
SO_IMPORT int tb_init(void);
|
||||
SO_IMPORT int tb_init_file(const char* name);
|
||||
SO_IMPORT void tb_shutdown(void);
|
||||
|
||||
// Returns the size of the internal back buffer (which is the same as
|
||||
// terminal's window size in characters). The internal buffer can be resized
|
||||
// after tb_clear() or tb_present() function calls. Both dimensions have an
|
||||
// unspecified negative value when called before tb_init() or after
|
||||
// tb_shutdown().
|
||||
SO_IMPORT int tb_width(void);
|
||||
SO_IMPORT int tb_height(void);
|
||||
|
||||
// Clears the internal back buffer using TB_DEFAULT color or the
|
||||
// color/attributes set by tb_set_clear_attributes() function.
|
||||
SO_IMPORT void tb_clear(void);
|
||||
SO_IMPORT void tb_set_clear_attributes(uint32_t fg, uint32_t bg);
|
||||
|
||||
// Synchronizes the internal back buffer with the terminal.
|
||||
SO_IMPORT void tb_present(void);
|
||||
|
||||
#define TB_HIDE_CURSOR -1
|
||||
|
||||
// Sets the position of the cursor. Upper-left character is (0, 0). If you pass
|
||||
// TB_HIDE_CURSOR as both coordinates, then the cursor will be hidden. Cursor
|
||||
// is hidden by default.
|
||||
SO_IMPORT void tb_set_cursor(int cx, int cy);
|
||||
|
||||
// Changes cell's parameters in the internal back buffer at the specified
|
||||
// position.
|
||||
SO_IMPORT void tb_put_cell(int x, int y, const struct tb_cell* cell);
|
||||
SO_IMPORT void tb_change_cell(int x, int y, uint32_t ch, uint32_t fg,
|
||||
uint32_t bg);
|
||||
|
||||
// Copies the buffer from 'cells' at the specified position, assuming the
|
||||
// buffer is a two-dimensional array of size ('w' x 'h'), represented as a
|
||||
// one-dimensional buffer containing lines of cells starting from the top.
|
||||
// (DEPRECATED: use tb_cell_buffer() instead and copy memory on your own)
|
||||
SO_IMPORT void tb_blit(int x, int y, int w, int h, const struct tb_cell* cells);
|
||||
|
||||
// Returns a pointer to internal cell back buffer. You can get its dimensions
|
||||
// using tb_width() and tb_height() functions. The pointer stays valid as long
|
||||
// as no tb_clear() and tb_present() calls are made. The buffer is
|
||||
// one-dimensional buffer containing lines of cells starting from the top.
|
||||
SO_IMPORT struct tb_cell* tb_cell_buffer(void);
|
||||
|
||||
#define TB_INPUT_CURRENT 0 // 000
|
||||
#define TB_INPUT_ESC 1 // 001
|
||||
#define TB_INPUT_ALT 2 // 010
|
||||
#define TB_INPUT_MOUSE 4 // 100
|
||||
|
||||
// Sets the termbox input mode. Termbox has two input modes:
|
||||
// 1. Esc input mode.
|
||||
// When ESC sequence is in the buffer and it doesn't match any known
|
||||
// ESC sequence => ESC means TB_KEY_ESC.
|
||||
// 2. Alt input mode.
|
||||
// When ESC sequence is in the buffer and it doesn't match any known
|
||||
// sequence => ESC enables TB_MOD_ALT modifier for the next keyboard event.
|
||||
//
|
||||
// You can also apply TB_INPUT_MOUSE via bitwise OR operation to either of the
|
||||
// modes (e.g. TB_INPUT_ESC | TB_INPUT_MOUSE). If none of the main two modes
|
||||
// were set, but the mouse mode was, TB_INPUT_ESC mode is used. If for some
|
||||
// reason you've decided to use (TB_INPUT_ESC | TB_INPUT_ALT) combination, it
|
||||
// will behave as if only TB_INPUT_ESC was selected.
|
||||
//
|
||||
// If 'mode' is TB_INPUT_CURRENT, it returns the current input mode.
|
||||
//
|
||||
// Default termbox input mode is TB_INPUT_ESC.
|
||||
SO_IMPORT int tb_select_input_mode(int mode);
|
||||
|
||||
#define TB_OUTPUT_CURRENT 0
|
||||
#define TB_OUTPUT_NORMAL 1
|
||||
#define TB_OUTPUT_256 2
|
||||
#define TB_OUTPUT_216 3
|
||||
#define TB_OUTPUT_GRAYSCALE 4
|
||||
#define TB_OUTPUT_TRUECOLOR 5
|
||||
|
||||
// Sets the termbox output mode. Termbox has three output options:
|
||||
// 1. TB_OUTPUT_NORMAL => [1..8]
|
||||
// This mode provides 8 different colors:
|
||||
// black, red, green, yellow, blue, magenta, cyan, white
|
||||
// Shortcut: TB_BLACK, TB_RED, ...
|
||||
// Attributes: TB_BOLD, TB_UNDERLINE, TB_REVERSE
|
||||
//
|
||||
// Example usage:
|
||||
// tb_change_cell(x, y, '@', TB_BLACK | TB_BOLD, TB_RED);
|
||||
//
|
||||
// 2. TB_OUTPUT_256 => [0..256]
|
||||
// In this mode you can leverage the 256 terminal mode:
|
||||
// 0x00 - 0x07: the 8 colors as in TB_OUTPUT_NORMAL
|
||||
// 0x08 - 0x0f: TB_* | TB_BOLD
|
||||
// 0x10 - 0xe7: 216 different colors
|
||||
// 0xe8 - 0xff: 24 different shades of grey
|
||||
//
|
||||
// Example usage:
|
||||
// tb_change_cell(x, y, '@', 184, 240);
|
||||
// tb_change_cell(x, y, '@', 0xb8, 0xf0);
|
||||
//
|
||||
// 3. TB_OUTPUT_216 => [0..216]
|
||||
// This mode supports the 3rd range of the 256 mode only.
|
||||
// But you don't need to provide an offset.
|
||||
//
|
||||
// 4. TB_OUTPUT_GRAYSCALE => [0..23]
|
||||
// This mode supports the 4th range of the 256 mode only.
|
||||
// But you dont need to provide an offset.
|
||||
//
|
||||
// 5. TB_OUTPUT_TRUECOLOR => [0x000000..0xFFFFFF]
|
||||
// This mode supports 24-bit true color. Format is 0xRRGGBB.
|
||||
//
|
||||
// Execute build/src/demo/output to see its impact on your terminal.
|
||||
//
|
||||
// If 'mode' is TB_OUTPUT_CURRENT, it returns the current output mode.
|
||||
//
|
||||
// Default termbox output mode is TB_OUTPUT_NORMAL.
|
||||
SO_IMPORT int tb_select_output_mode(int mode);
|
||||
|
||||
// Wait for an event up to 'timeout' milliseconds and fill the 'event'
|
||||
// structure with it, when the event is available. Returns the type of the
|
||||
// event (one of TB_EVENT_* constants) or -1 if there was an error or 0 in case
|
||||
// there were no event during 'timeout' period.
|
||||
SO_IMPORT int tb_peek_event(struct tb_event* event, int timeout);
|
||||
|
||||
// Wait for an event forever and fill the 'event' structure with it, when the
|
||||
// event is available. Returns the type of the event (one of TB_EVENT_
|
||||
// constants) or -1 if there was an error.
|
||||
SO_IMPORT int tb_poll_event(struct tb_event* event);
|
||||
|
||||
// Utility utf8 functions.
|
||||
#define TB_EOF -1
|
||||
SO_IMPORT int utf8_char_length(char c);
|
||||
SO_IMPORT int utf8_char_to_unicode(uint32_t* out, const char* c);
|
||||
SO_IMPORT int utf8_unicode_to_char(char* out, uint32_t c);
|
||||
|
||||
// c++
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
#include "termbox.h"
|
||||
|
||||
static const unsigned char utf8_length[256] =
|
||||
{
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
|
||||
};
|
||||
|
||||
static const unsigned char utf8_mask[6] =
|
||||
{
|
||||
0x7F,
|
||||
0x1F,
|
||||
0x0F,
|
||||
0x07,
|
||||
0x03,
|
||||
0x01
|
||||
};
|
||||
|
||||
int utf8_char_length(char c)
|
||||
{
|
||||
return utf8_length[(unsigned char)c];
|
||||
}
|
||||
|
||||
int utf8_char_to_unicode(uint32_t* out, const char* c)
|
||||
{
|
||||
if (*c == 0)
|
||||
{
|
||||
return TB_EOF;
|
||||
}
|
||||
|
||||
int i;
|
||||
unsigned char len = utf8_char_length(*c);
|
||||
unsigned char mask = utf8_mask[len - 1];
|
||||
uint32_t result = c[0] & mask;
|
||||
|
||||
for (i = 1; i < len; ++i)
|
||||
{
|
||||
result <<= 6;
|
||||
result |= c[i] & 0x3f;
|
||||
}
|
||||
|
||||
*out = result;
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
int utf8_unicode_to_char(char* out, uint32_t c)
|
||||
{
|
||||
int len = 0;
|
||||
int first;
|
||||
int i;
|
||||
|
||||
if (c < 0x80)
|
||||
{
|
||||
first = 0;
|
||||
len = 1;
|
||||
}
|
||||
else if (c < 0x800)
|
||||
{
|
||||
first = 0xc0;
|
||||
len = 2;
|
||||
}
|
||||
else if (c < 0x10000)
|
||||
{
|
||||
first = 0xe0;
|
||||
len = 3;
|
||||
}
|
||||
else if (c < 0x200000)
|
||||
{
|
||||
first = 0xf0;
|
||||
len = 4;
|
||||
}
|
||||
else if (c < 0x4000000)
|
||||
{
|
||||
first = 0xf8;
|
||||
len = 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
first = 0xfc;
|
||||
len = 6;
|
||||
}
|
||||
|
||||
for (i = len - 1; i > 0; --i)
|
||||
{
|
||||
out[i] = (c & 0x3f) | 0x80;
|
||||
c >>= 6;
|
||||
}
|
||||
|
||||
out[0] = c | first;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
--style=break
|
||||
--indent=force-tab=4
|
||||
--indent-classes
|
||||
--indent-switches
|
||||
--indent-namespaces
|
||||
--indent-after-parens
|
||||
--indent-continuation=1
|
||||
--indent-preproc-block
|
||||
--indent-preproc-define
|
||||
--indent-preproc-cond
|
||||
--indent-col1-comments
|
||||
--min-conditional-indent=0
|
||||
--max-continuation-indent=40
|
||||
--break-blocks
|
||||
--pad-oper
|
||||
--pad-comma
|
||||
--pad-header
|
||||
--unpad-paren
|
||||
--align-pointer=type
|
||||
--align-reference=type
|
||||
--break-one-line-headers
|
||||
--add-braces
|
||||
--attach-return-type
|
||||
--attach-return-type-decl
|
||||
--remove-comment-prefix
|
||||
--max-code-length=80
|
||||
--mode=c
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import sys, os, subprocess
|
||||
|
||||
def escaped(s):
|
||||
return s.replace("\033", "\\033")
|
||||
|
||||
def tput(term, name):
|
||||
try:
|
||||
return subprocess.check_output(['tput', '-T%s' % term, name]).decode()
|
||||
except subprocess.CalledProcessError as e:
|
||||
return e.output.decode()
|
||||
|
||||
|
||||
def w(s):
|
||||
if s == None:
|
||||
return
|
||||
sys.stdout.write(s)
|
||||
|
||||
terminals = {
|
||||
'xterm' : 'xterm',
|
||||
'rxvt-256color' : 'rxvt_256color',
|
||||
'rxvt-unicode' : 'rxvt_unicode',
|
||||
'linux' : 'linux',
|
||||
'Eterm' : 'eterm',
|
||||
'screen' : 'screen'
|
||||
}
|
||||
|
||||
keys = [
|
||||
"F1", "kf1",
|
||||
"F2", "kf2",
|
||||
"F3", "kf3",
|
||||
"F4", "kf4",
|
||||
"F5", "kf5",
|
||||
"F6", "kf6",
|
||||
"F7", "kf7",
|
||||
"F8", "kf8",
|
||||
"F9", "kf9",
|
||||
"F10", "kf10",
|
||||
"F11", "kf11",
|
||||
"F12", "kf12",
|
||||
"INSERT", "kich1",
|
||||
"DELETE", "kdch1",
|
||||
"HOME", "khome",
|
||||
"END", "kend",
|
||||
"PGUP", "kpp",
|
||||
"PGDN", "knp",
|
||||
"KEY_UP", "kcuu1",
|
||||
"KEY_DOWN", "kcud1",
|
||||
"KEY_LEFT", "kcub1",
|
||||
"KEY_RIGHT", "kcuf1"
|
||||
]
|
||||
|
||||
funcs = [
|
||||
"T_ENTER_CA", "smcup",
|
||||
"T_EXIT_CA", "rmcup",
|
||||
"T_SHOW_CURSOR", "cnorm",
|
||||
"T_HIDE_CURSOR", "civis",
|
||||
"T_CLEAR_SCREEN", "clear",
|
||||
"T_SGR0", "sgr0",
|
||||
"T_UNDERLINE", "smul",
|
||||
"T_BOLD", "bold",
|
||||
"T_BLINK", "blink",
|
||||
"T_REVERSE", "rev",
|
||||
"T_ENTER_KEYPAD", "smkx",
|
||||
"T_EXIT_KEYPAD", "rmkx"
|
||||
]
|
||||
|
||||
def iter_pairs(iterable):
|
||||
iterable = iter(iterable)
|
||||
while True:
|
||||
yield (next(iterable), next(iterable))
|
||||
|
||||
def do_term(term, nick):
|
||||
w("// %s\n" % term)
|
||||
w("static const char *%s_keys[] = {\n\t" % nick)
|
||||
for k, v in iter_pairs(keys):
|
||||
w('"')
|
||||
w(escaped(tput(term, v)))
|
||||
w('",')
|
||||
w(" 0\n};\n")
|
||||
w("static const char *%s_funcs[] = {\n\t" % nick)
|
||||
for k,v in iter_pairs(funcs):
|
||||
w('"')
|
||||
if v == "sgr":
|
||||
w("\\033[3%d;4%dm")
|
||||
elif v == "cup":
|
||||
w("\\033[%d;%dH")
|
||||
else:
|
||||
w(escaped(tput(term, v)))
|
||||
w('", ')
|
||||
w("\n};\n\n")
|
||||
|
||||
def do_terms(d):
|
||||
w("static struct term {\n")
|
||||
w("\tconst char *name;\n")
|
||||
w("\tconst char **keys;\n")
|
||||
w("\tconst char **funcs;\n")
|
||||
w("} terms[] = {\n")
|
||||
for k, v in d.items():
|
||||
w('\t{"%s", %s_keys, %s_funcs},\n' % (k, v, v))
|
||||
w("\t{0, 0, 0},\n")
|
||||
w("};\n")
|
||||
|
||||
for k,v in terminals.items():
|
||||
do_term(k, v)
|
||||
|
||||
do_terms(terminals)
|
||||
445
install.zig
Normal file
445
install.zig
Normal file
|
|
@ -0,0 +1,445 @@
|
|||
const std = @import("std");
|
||||
|
||||
const PatchMap = std.StringHashMap([]const u8);
|
||||
const InitSystem = enum {
|
||||
systemd,
|
||||
openrc,
|
||||
runit,
|
||||
s6,
|
||||
dinit,
|
||||
sysvinit,
|
||||
freebsd,
|
||||
};
|
||||
const InstallType = enum {
|
||||
installexe,
|
||||
installnoconf,
|
||||
uninstallexe,
|
||||
uninstallnoconf,
|
||||
};
|
||||
|
||||
var dest_directory: []const u8 = undefined;
|
||||
var config_directory: []const u8 = undefined;
|
||||
var prefix_directory: []const u8 = undefined;
|
||||
var executable_name: []const u8 = undefined;
|
||||
var init_system: InitSystem = undefined;
|
||||
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
const io = init.io;
|
||||
const allocator = init.gpa;
|
||||
|
||||
var args = init.minimal.args.iterate();
|
||||
if (!args.skip()) return error.NoProgramName;
|
||||
|
||||
const install_type = std.meta.stringToEnum(InstallType, args.next().?).?;
|
||||
dest_directory = args.next().?;
|
||||
config_directory = args.next().?;
|
||||
prefix_directory = args.next().?;
|
||||
executable_name = args.next().?;
|
||||
init_system = std.meta.stringToEnum(InitSystem, args.next().?).?;
|
||||
const default_tty_str = args.next().?;
|
||||
|
||||
switch (install_type) {
|
||||
.installexe, .installnoconf => {
|
||||
var patch_map = PatchMap.init(allocator);
|
||||
defer patch_map.deinit();
|
||||
|
||||
try patch_map.put("$DEFAULT_TTY", default_tty_str);
|
||||
try patch_map.put("$CONFIG_DIRECTORY", config_directory);
|
||||
try patch_map.put("$PREFIX_DIRECTORY", prefix_directory);
|
||||
try patch_map.put("$EXECUTABLE_NAME", executable_name);
|
||||
|
||||
try installLy(allocator, io, patch_map, install_type == .installexe);
|
||||
try installService(allocator, io, patch_map);
|
||||
},
|
||||
.uninstallexe, .uninstallnoconf => {
|
||||
if (install_type == .uninstallexe) {
|
||||
try deleteTree(allocator, io, config_directory, "/ly", "ly config directory not found");
|
||||
}
|
||||
|
||||
const exe_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ prefix_directory, "/bin/", executable_name });
|
||||
defer allocator.free(exe_path);
|
||||
|
||||
var success = true;
|
||||
std.Io.Dir.cwd().deleteFile(io, exe_path) catch {
|
||||
std.debug.print("warn: ly executable not found\n", .{});
|
||||
success = false;
|
||||
};
|
||||
if (success) std.debug.print("info: deleted {s}\n", .{exe_path});
|
||||
|
||||
try deleteFile(allocator, io, config_directory, "/pam.d/ly", "ly pam file not found");
|
||||
|
||||
switch (init_system) {
|
||||
.systemd => try deleteFile(allocator, io, prefix_directory, "/lib/systemd/system/ly@.service", "systemd service not found"),
|
||||
.openrc => try deleteFile(allocator, io, config_directory, "/init.d/ly", "openrc service not found"),
|
||||
.runit => try deleteTree(allocator, io, config_directory, "/sv/ly", "runit service not found"),
|
||||
.s6 => {
|
||||
try deleteTree(allocator, io, config_directory, "/s6/sv/ly-srv", "s6 service not found");
|
||||
try deleteFile(allocator, io, config_directory, "/s6/adminsv/default/contents.d/ly-srv", "s6 admin service not found");
|
||||
},
|
||||
.dinit => try deleteFile(allocator, io, config_directory, "/dinit.d/ly", "dinit service not found"),
|
||||
.sysvinit => try deleteFile(allocator, io, config_directory, "/init.d/ly", "sysvinit service not found"),
|
||||
.freebsd => try deleteFile(allocator, io, prefix_directory, "/bin/ly_wrapper", "freebsd wrapper not found"),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn installLy(allocator: std.mem.Allocator, io: std.Io, patch_map: PatchMap, install_config: bool) !void {
|
||||
const ly_config_directory = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly" });
|
||||
defer allocator.free(ly_config_directory);
|
||||
|
||||
std.Io.Dir.cwd().createDirPath(io, ly_config_directory) catch {
|
||||
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_config_directory});
|
||||
};
|
||||
|
||||
const ly_custom_sessions_directory = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/custom-sessions" });
|
||||
defer allocator.free(ly_custom_sessions_directory);
|
||||
|
||||
std.Io.Dir.cwd().createDirPath(io, ly_custom_sessions_directory) catch {
|
||||
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_custom_sessions_directory});
|
||||
};
|
||||
|
||||
const ly_lang_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/lang" });
|
||||
defer allocator.free(ly_lang_path);
|
||||
|
||||
std.Io.Dir.cwd().createDirPath(io, ly_lang_path) catch {
|
||||
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_lang_path});
|
||||
};
|
||||
|
||||
{
|
||||
const exe_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin" });
|
||||
defer allocator.free(exe_path);
|
||||
|
||||
std.Io.Dir.cwd().createDirPath(io, exe_path) catch {
|
||||
if (!std.mem.eql(u8, dest_directory, "")) {
|
||||
std.debug.print("warn: {s} already exists as a directory.\n", .{exe_path});
|
||||
}
|
||||
};
|
||||
|
||||
var executable_dir = std.Io.Dir.cwd().openDir(io, exe_path, .{}) catch unreachable;
|
||||
defer executable_dir.close(io);
|
||||
|
||||
try installFile(io, "zig-out/bin/ly", executable_dir, exe_path, executable_name, .{});
|
||||
}
|
||||
|
||||
{
|
||||
var config_dir = std.Io.Dir.cwd().openDir(io, ly_config_directory, .{}) catch unreachable;
|
||||
defer config_dir.close(io);
|
||||
|
||||
if (install_config) {
|
||||
const patched_config = try patchFile(allocator, io, "res/config.ini", patch_map);
|
||||
defer allocator.free(patched_config);
|
||||
|
||||
try installText(io, patched_config, config_dir, ly_config_directory, "config.ini", .{});
|
||||
|
||||
try installFile(io, "res/startup.sh", config_dir, ly_config_directory, "startup.sh", .{ .permissions = .fromMode(0o755) });
|
||||
}
|
||||
|
||||
const patched_example_config = try patchFile(allocator, io, "res/config.ini", patch_map);
|
||||
defer allocator.free(patched_example_config);
|
||||
|
||||
try installText(io, patched_example_config, config_dir, ly_config_directory, "config.ini.example", .{});
|
||||
|
||||
const patched_setup = try patchFile(allocator, io, "res/setup.sh", patch_map);
|
||||
defer allocator.free(patched_setup);
|
||||
|
||||
try installText(io, patched_setup, config_dir, ly_config_directory, "setup.sh", .{ .permissions = .fromMode(0o755) });
|
||||
|
||||
try installFile(io, "res/example.dur", config_dir, ly_config_directory, "example.dur", .{ .permissions = .fromMode(0o755) });
|
||||
|
||||
try installFile(io, "res/example.lua", config_dir, ly_config_directory, "example.lua", .{ .permissions = .fromMode(0o755) });
|
||||
}
|
||||
|
||||
{
|
||||
var custom_sessions_dir = std.Io.Dir.cwd().openDir(io, ly_custom_sessions_directory, .{}) catch unreachable;
|
||||
defer custom_sessions_dir.close(io);
|
||||
|
||||
const patched_readme = try patchFile(allocator, io, "res/custom-sessions/README", patch_map);
|
||||
defer allocator.free(patched_readme);
|
||||
|
||||
try installText(io, patched_readme, custom_sessions_dir, ly_custom_sessions_directory, "README", .{});
|
||||
}
|
||||
|
||||
{
|
||||
var lang_dir = std.Io.Dir.cwd().openDir(io, ly_lang_path, .{}) catch unreachable;
|
||||
defer lang_dir.close(io);
|
||||
|
||||
const languages = [_][]const u8{
|
||||
"ar.ini",
|
||||
"bg.ini",
|
||||
"cat.ini",
|
||||
"cs.ini",
|
||||
"de.ini",
|
||||
"en.ini",
|
||||
"eo.ini",
|
||||
"es.ini",
|
||||
"fr.ini",
|
||||
"it.ini",
|
||||
"ja_JP.ini",
|
||||
"ku.ini",
|
||||
"lv.ini",
|
||||
"pl.ini",
|
||||
"pt.ini",
|
||||
"pt_BR.ini",
|
||||
"ro.ini",
|
||||
"ru.ini",
|
||||
"sr.ini",
|
||||
"sr_Cyrl.ini",
|
||||
"sv.ini",
|
||||
"tr.ini",
|
||||
"uk.ini",
|
||||
"zh_CN.ini",
|
||||
"zh_TW.ini",
|
||||
};
|
||||
|
||||
inline for (languages) |language| {
|
||||
try installFile(io, "res/lang/" ++ language, lang_dir, ly_lang_path, language, .{});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const pam_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/pam.d" });
|
||||
defer allocator.free(pam_path);
|
||||
|
||||
std.Io.Dir.cwd().createDirPath(io, pam_path) catch {
|
||||
if (!std.mem.eql(u8, dest_directory, "")) {
|
||||
std.debug.print("warn: {s} already exists as a directory.\n", .{pam_path});
|
||||
}
|
||||
};
|
||||
|
||||
var pam_dir = std.Io.Dir.cwd().openDir(io, pam_path, .{}) catch unreachable;
|
||||
defer pam_dir.close(io);
|
||||
|
||||
try installFile(io, if (init_system == .freebsd) "res/pam.d/ly-freebsd" else "res/pam.d/ly-linux", pam_dir, pam_path, "ly", .{ .permissions = .fromMode(0o644) });
|
||||
try installFile(io, if (init_system == .freebsd) "res/pam.d/ly-freebsd-autologin" else "res/pam.d/ly-linux-autologin", pam_dir, pam_path, "ly-autologin", .{ .permissions = .fromMode(0o644) });
|
||||
}
|
||||
}
|
||||
|
||||
fn installService(allocator: std.mem.Allocator, io: std.Io, patch_map: PatchMap) !void {
|
||||
switch (init_system) {
|
||||
.systemd => {
|
||||
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/lib/systemd/system" });
|
||||
defer allocator.free(service_path);
|
||||
|
||||
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
|
||||
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
|
||||
defer service_dir.close(io);
|
||||
|
||||
const patched_service = try patchFile(allocator, io, "res/ly@.service", patch_map);
|
||||
defer allocator.free(patched_service);
|
||||
|
||||
try installText(io, patched_service, service_dir, service_path, "ly@.service", .{ .permissions = .fromMode(0o644) });
|
||||
|
||||
const patched_kmsconvt_service = try patchFile(allocator, io, "res/ly-kmsconvt@.service", patch_map);
|
||||
defer allocator.free(patched_kmsconvt_service);
|
||||
|
||||
try installText(io, patched_kmsconvt_service, service_dir, service_path, "ly-kmsconvt@.service", .{ .permissions = .fromMode(0o644) });
|
||||
},
|
||||
.openrc => {
|
||||
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
|
||||
defer allocator.free(service_path);
|
||||
|
||||
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
|
||||
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
|
||||
defer service_dir.close(io);
|
||||
|
||||
const patched_service = try patchFile(allocator, io, "res/ly-openrc", patch_map);
|
||||
defer allocator.free(patched_service);
|
||||
|
||||
try installText(io, patched_service, service_dir, service_path, executable_name, .{ .permissions = .fromMode(0o755) });
|
||||
},
|
||||
.runit => {
|
||||
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/sv/ly" });
|
||||
defer allocator.free(service_path);
|
||||
|
||||
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
|
||||
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
|
||||
defer service_dir.close(io);
|
||||
|
||||
const supervise_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ service_path, "supervise" });
|
||||
defer allocator.free(supervise_path);
|
||||
|
||||
const patched_conf = try patchFile(allocator, io, "res/ly-runit-service/conf", patch_map);
|
||||
defer allocator.free(patched_conf);
|
||||
|
||||
try installText(io, patched_conf, service_dir, service_path, "conf", .{});
|
||||
|
||||
try installFile(io, "res/ly-runit-service/finish", service_dir, service_path, "finish", .{ .permissions = .fromMode(0o755) });
|
||||
|
||||
const patched_run = try patchFile(allocator, io, "res/ly-runit-service/run", patch_map);
|
||||
defer allocator.free(patched_run);
|
||||
|
||||
try installText(io, patched_run, service_dir, service_path, "run", .{ .permissions = .fromMode(0o755) });
|
||||
|
||||
std.Io.Dir.cwd().symLink(io, "/run/runit/supervise.ly", supervise_path, .{}) catch |err| {
|
||||
if (err == error.PathAlreadyExists) {
|
||||
std.debug.print("warn: /run/runit/supervise.ly already exists as a symbolic link.\n", .{});
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
};
|
||||
std.debug.print("info: installed symlink /run/runit/supervise.ly\n", .{});
|
||||
},
|
||||
.s6 => {
|
||||
const admin_service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/adminsv/default/contents.d" });
|
||||
std.Io.Dir.cwd().createDirPath(io, admin_service_path) catch {};
|
||||
defer allocator.free(admin_service_path);
|
||||
|
||||
var admin_service_dir = std.Io.Dir.cwd().openDir(io, admin_service_path, .{}) catch unreachable;
|
||||
defer admin_service_dir.close(io);
|
||||
|
||||
const file = try admin_service_dir.createFile(io, "ly-srv", .{});
|
||||
file.close(io);
|
||||
|
||||
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/sv/ly-srv" });
|
||||
defer allocator.free(service_path);
|
||||
|
||||
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
|
||||
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
|
||||
defer service_dir.close(io);
|
||||
|
||||
const patched_run = try patchFile(allocator, io, "res/ly-s6/run", patch_map);
|
||||
defer allocator.free(patched_run);
|
||||
|
||||
try installText(io, patched_run, service_dir, service_path, "run", .{ .permissions = .fromMode(0o755) });
|
||||
|
||||
try installFile(io, "res/ly-s6/type", service_dir, service_path, "type", .{});
|
||||
},
|
||||
.dinit => {
|
||||
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/dinit.d" });
|
||||
defer allocator.free(service_path);
|
||||
|
||||
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
|
||||
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
|
||||
defer service_dir.close(io);
|
||||
|
||||
const patched_service = try patchFile(allocator, io, "res/ly-dinit", patch_map);
|
||||
defer allocator.free(patched_service);
|
||||
|
||||
try installText(io, patched_service, service_dir, service_path, "ly", .{});
|
||||
},
|
||||
.sysvinit => {
|
||||
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
|
||||
defer allocator.free(service_path);
|
||||
|
||||
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
|
||||
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
|
||||
defer service_dir.close(io);
|
||||
|
||||
const patched_service = try patchFile(allocator, io, "res/ly-sysvinit", patch_map);
|
||||
defer allocator.free(patched_service);
|
||||
|
||||
try installText(io, patched_service, service_dir, service_path, "ly", .{ .permissions = .fromMode(0o755) });
|
||||
},
|
||||
.freebsd => {
|
||||
const exe_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin" });
|
||||
defer allocator.free(exe_path);
|
||||
|
||||
var executable_dir = std.Io.Dir.cwd().openDir(io, exe_path, .{}) catch unreachable;
|
||||
defer executable_dir.close(io);
|
||||
|
||||
const patched_wrapper = try patchFile(allocator, io, "res/ly-freebsd-wrapper", patch_map);
|
||||
defer allocator.free(patched_wrapper);
|
||||
|
||||
try installText(io, patched_wrapper, executable_dir, exe_path, "ly_wrapper", .{ .permissions = .fromMode(0o755) });
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn installFile(
|
||||
io: std.Io,
|
||||
source_file: []const u8,
|
||||
destination_directory: std.Io.Dir,
|
||||
destination_directory_path: []const u8,
|
||||
destination_file: []const u8,
|
||||
options: std.Io.Dir.CopyFileOptions,
|
||||
) !void {
|
||||
try std.Io.Dir.cwd().copyFile(source_file, destination_directory, destination_file, io, options);
|
||||
std.debug.print("info: installed {s}/{s}\n", .{ destination_directory_path, destination_file });
|
||||
}
|
||||
|
||||
fn patchFile(allocator: std.mem.Allocator, io: std.Io, source_file: []const u8, patch_map: PatchMap) ![]const u8 {
|
||||
var file = try std.Io.Dir.cwd().openFile(io, source_file, .{});
|
||||
defer file.close(io);
|
||||
|
||||
const stat = try file.stat(io);
|
||||
|
||||
var buffer: [4096]u8 = undefined;
|
||||
var reader = file.reader(io, &buffer);
|
||||
var text = try reader.interface.readAlloc(allocator, @intCast(stat.size));
|
||||
|
||||
var iterator = patch_map.iterator();
|
||||
while (iterator.next()) |kv| {
|
||||
const new_text = try std.mem.replaceOwned(u8, allocator, text, kv.key_ptr.*, kv.value_ptr.*);
|
||||
allocator.free(text);
|
||||
text = new_text;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
fn installText(
|
||||
io: std.Io,
|
||||
text: []const u8,
|
||||
destination_directory: std.Io.Dir,
|
||||
destination_directory_path: []const u8,
|
||||
destination_file: []const u8,
|
||||
options: std.Io.File.CreateFlags,
|
||||
) !void {
|
||||
var file = try destination_directory.createFile(io, destination_file, options);
|
||||
defer file.close(io);
|
||||
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var writer = file.writer(io, &buffer);
|
||||
try writer.interface.writeAll(text);
|
||||
try writer.interface.flush();
|
||||
|
||||
std.debug.print("info: installed {s}/{s}\n", .{ destination_directory_path, destination_file });
|
||||
}
|
||||
|
||||
fn deleteFile(
|
||||
allocator: std.mem.Allocator,
|
||||
io: std.Io,
|
||||
prefix: []const u8,
|
||||
file: []const u8,
|
||||
warning: []const u8,
|
||||
) !void {
|
||||
const path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix, file });
|
||||
defer allocator.free(path);
|
||||
|
||||
std.Io.Dir.cwd().deleteFile(io, path) catch |err| {
|
||||
if (err == error.FileNotFound) {
|
||||
std.debug.print("warn: {s}\n", .{warning});
|
||||
return;
|
||||
}
|
||||
|
||||
return err;
|
||||
};
|
||||
|
||||
std.debug.print("info: deleted {s}\n", .{path});
|
||||
}
|
||||
|
||||
fn deleteTree(
|
||||
allocator: std.mem.Allocator,
|
||||
io: std.Io,
|
||||
prefix: []const u8,
|
||||
directory: []const u8,
|
||||
warning: []const u8,
|
||||
) !void {
|
||||
const path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix, directory });
|
||||
defer allocator.free(path);
|
||||
|
||||
var dir = std.Io.Dir.cwd().openDir(io, path, .{}) catch |err| {
|
||||
if (err == error.FileNotFound) {
|
||||
std.debug.print("warn: {s}\n", .{warning});
|
||||
return;
|
||||
}
|
||||
|
||||
return err;
|
||||
};
|
||||
dir.close(io);
|
||||
|
||||
try std.Io.Dir.cwd().deleteTree(io, path);
|
||||
|
||||
std.debug.print("info: deleted {s}\n", .{path});
|
||||
}
|
||||
82
ly-core/build.zig
Normal file
82
ly-core/build.zig
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
const std = @import("std");
|
||||
const Translator = @import("translate_c").Translator;
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const enable_x11_support = b.option(bool, "enable_x11_support", "Enable X11 support") orelse true;
|
||||
const mod = b.addModule("ly-core", .{
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const fallback_uid_min = b.option(std.posix.uid_t, "fallback_uid_min", "Set the fallback minimum UID (default is 1000). This value gets embedded into the binary").?;
|
||||
const fallback_uid_max = b.option(std.posix.uid_t, "fallback_uid_max", "Set the fallback maximum UID (default is 60000). This value gets embedded into the binary").?;
|
||||
const build_options = b.addOptions();
|
||||
build_options.addOption(std.posix.uid_t, "fallback_uid_min", fallback_uid_min);
|
||||
build_options.addOption(std.posix.uid_t, "fallback_uid_max", fallback_uid_max);
|
||||
mod.addOptions("build_options", build_options);
|
||||
|
||||
const zigini = b.dependency("zigini", .{ .target = target, .optimize = optimize });
|
||||
mod.addImport("zigini", zigini.module("zigini"));
|
||||
|
||||
const translate_c = b.dependency("translate_c", .{
|
||||
.target = target,
|
||||
});
|
||||
|
||||
addCImport(b, mod, translate_c, target, optimize, "pam", "#include <security/pam_appl.h>");
|
||||
addCImport(b, mod, translate_c, target, optimize, "utmp", "#include <utmpx.h>");
|
||||
if (enable_x11_support) {
|
||||
addCImport(b, mod, translate_c, target, optimize, "xcb", "#include <xcb/xcb.h>");
|
||||
}
|
||||
if (target.result.os.tag == .freebsd) {
|
||||
addCImport(b, mod, translate_c, target, optimize, "pwd",
|
||||
\\#include <pwd.h>
|
||||
\\#include <sys/types.h>
|
||||
\\#include <login_cap.h>
|
||||
);
|
||||
} else {
|
||||
addCImport(b, mod, translate_c, target, optimize, "pwd", "#include <pwd.h>");
|
||||
}
|
||||
addCImport(b, mod, translate_c, target, optimize, "stdlib", "#include <stdlib.h>");
|
||||
addCImport(b, mod, translate_c, target, optimize, "unistd", "#include <unistd.h>");
|
||||
addCImport(b, mod, translate_c, target, optimize, "grp", "#include <grp.h>");
|
||||
addCImport(b, mod, translate_c, target, optimize, "system_time", "#include <sys/time.h>");
|
||||
addCImport(b, mod, translate_c, target, optimize, "time", "#include <time.h>");
|
||||
|
||||
if (target.result.os.tag == .linux) {
|
||||
addCImport(b, mod, translate_c, target, optimize, "kd", "#include <sys/kd.h>");
|
||||
addCImport(b, mod, translate_c, target, optimize, "vt", "#include <sys/vt.h>");
|
||||
} else if (target.result.os.tag == .freebsd) {
|
||||
addCImport(b, mod, translate_c, target, optimize, "kbio", "#include <sys/kbio.h>");
|
||||
addCImport(b, mod, translate_c, target, optimize, "consio", "#include <sys/consio.h>");
|
||||
addCImport(b, mod, translate_c, target, optimize, "sysctl", "#include <sys/sysctl.h>");
|
||||
addCImport(b, mod, translate_c, target, optimize, "reboot", "#include <sys/reboot.h>");
|
||||
}
|
||||
|
||||
const mod_tests = b.addTest(.{
|
||||
.root_module = mod,
|
||||
});
|
||||
const run_mod_tests = b.addRunArtifact(mod_tests);
|
||||
|
||||
const test_step = b.step("test", "Run tests");
|
||||
test_step.dependOn(&run_mod_tests.step);
|
||||
}
|
||||
|
||||
fn addCImport(
|
||||
b: *std.Build,
|
||||
mod: *std.Build.Module,
|
||||
translate_c: *std.Build.Dependency,
|
||||
target: std.Build.ResolvedTarget,
|
||||
optimize: std.builtin.OptimizeMode,
|
||||
comptime name: []const u8,
|
||||
comptime bytes: []const u8,
|
||||
) void {
|
||||
const pam: Translator = .init(translate_c, .{
|
||||
.c_source_file = b.addWriteFiles().add(name ++ ".h", bytes),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
mod.addImport(name, pam.mod);
|
||||
}
|
||||
21
ly-core/build.zig.zon
Normal file
21
ly-core/build.zig.zon
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
.{
|
||||
.name = .ly_core,
|
||||
.version = "1.1.0",
|
||||
.fingerprint = 0xddda7afda795472,
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
.zigini = .{
|
||||
.url = "git+https://github.com/AshAmetrine/zigini?ref=master#a665d081dda42664a96da2840ea09c5ccf9d0692",
|
||||
.hash = "zigini-0.5.0-BSkB7e9WAACfyCBABNZiWL3gFMw18GKn3qBcPs8L1Ec1",
|
||||
},
|
||||
.translate_c = .{
|
||||
.url = "git+https://codeberg.org/ziglang/translate-c?ref=zig-0.16.x#6fe0ffc4549f15c5f2d9432c2b4460ba90ff85ac",
|
||||
.hash = "translate_c-1.0.0-Q_BUWo_5BgD4flHdUhA31zOz0XvZk9k7lQv1ouzyNXj2",
|
||||
},
|
||||
},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
},
|
||||
}
|
||||
106
ly-core/src/LogFile.zig
Normal file
106
ly-core/src/LogFile.zig
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
const std = @import("std");
|
||||
const interop = @import("interop.zig");
|
||||
|
||||
const LogFile = @This();
|
||||
|
||||
maybe_path: ?[]const u8,
|
||||
could_open_log_file: bool = undefined,
|
||||
maybe_file: ?std.Io.File = null,
|
||||
buffer: []u8,
|
||||
maybe_file_writer: ?std.Io.File.Writer = null,
|
||||
|
||||
pub fn init(io: std.Io, path: ?[]const u8, buffer: []u8) !LogFile {
|
||||
var log_file = LogFile{
|
||||
.maybe_path = path,
|
||||
.buffer = buffer,
|
||||
};
|
||||
|
||||
if (path) |p| {
|
||||
log_file.could_open_log_file = try openLogFile(io, p, &log_file);
|
||||
} else {
|
||||
std.posix.system.openlog("ly", 0, 0);
|
||||
log_file.could_open_log_file = true;
|
||||
}
|
||||
|
||||
return log_file;
|
||||
}
|
||||
|
||||
pub fn reinit(self: *LogFile, io: std.Io) !void {
|
||||
if (self.maybe_path) |path| {
|
||||
self.could_open_log_file = try openLogFile(io, path, self);
|
||||
} else {
|
||||
std.posix.system.openlog("ly", 0, 0);
|
||||
self.could_open_log_file = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *LogFile, io: std.Io) void {
|
||||
if (self.maybe_file) |file| {
|
||||
file.close(io);
|
||||
} else {
|
||||
std.posix.system.closelog();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn info(self: *LogFile, io: std.Io, category: []const u8, comptime message: []const u8, args: anytype) !void {
|
||||
if (self.maybe_file_writer) |*writer| {
|
||||
var buffer: [128:0]u8 = undefined;
|
||||
const time = interop.timeAsString(io, &buffer, "%Y-%m-%d %H:%M:%S");
|
||||
|
||||
try writer.interface.print("{s} [info/{s}] ", .{ time, category });
|
||||
try writer.interface.print(message, args);
|
||||
try writer.interface.writeByte('\n');
|
||||
try writer.interface.flush();
|
||||
} else {
|
||||
var buffer: [1024]u8 = undefined;
|
||||
const slice = try std.fmt.bufPrint(&buffer, message, args);
|
||||
const msg = try std.fmt.bufPrintZ(buffer[slice.len..], "[info/{s}] {s}", .{ category, slice });
|
||||
|
||||
std.posix.system.syslog(std.posix.LOG.INFO, msg.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn err(self: *LogFile, io: std.Io, category: []const u8, comptime message: []const u8, args: anytype) !void {
|
||||
if (self.maybe_file_writer) |*writer| {
|
||||
var buffer: [128:0]u8 = undefined;
|
||||
const time = interop.timeAsString(io, &buffer, "%Y-%m-%d %H:%M:%S");
|
||||
|
||||
try writer.interface.print("{s} [err/{s}] ", .{ time, category });
|
||||
try writer.interface.print(message, args);
|
||||
try writer.interface.writeByte('\n');
|
||||
try writer.interface.flush();
|
||||
} else {
|
||||
var buffer: [1024]u8 = undefined;
|
||||
const slice = try std.fmt.bufPrint(&buffer, message, args);
|
||||
const msg = try std.fmt.bufPrintZ(buffer[slice.len..], "[info/{s}] {s}", .{ category, slice });
|
||||
|
||||
std.posix.system.syslog(std.posix.LOG.ERR, msg.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
fn openLogFile(io: std.Io, path: []const u8, log_file: *LogFile) !bool {
|
||||
var could_open_log_file = true;
|
||||
open_log_file: {
|
||||
log_file.maybe_file = std.Io.Dir.cwd().openFile(io, path, .{ .mode = .write_only }) catch std.Io.Dir.cwd().createFile(io, path, .{ .permissions = .fromMode(0o666) }) catch {
|
||||
// If we could neither open an existing log file nor create a new
|
||||
// one, abort.
|
||||
could_open_log_file = false;
|
||||
break :open_log_file;
|
||||
};
|
||||
}
|
||||
|
||||
if (!could_open_log_file) {
|
||||
log_file.maybe_file = try std.Io.Dir.openFileAbsolute(io, "/dev/null", .{ .mode = .write_only });
|
||||
}
|
||||
|
||||
var log_file_writer = log_file.maybe_file.?.writer(io, log_file.buffer);
|
||||
|
||||
// Seek to the end of the log file
|
||||
if (could_open_log_file) {
|
||||
const stat = try log_file.maybe_file.?.stat(io);
|
||||
try log_file_writer.seekTo(stat.size);
|
||||
}
|
||||
|
||||
log_file.maybe_file_writer = log_file_writer;
|
||||
return could_open_log_file;
|
||||
}
|
||||
52
ly-core/src/SharedError.zig
Normal file
52
ly-core/src/SharedError.zig
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
const std = @import("std");
|
||||
|
||||
const ErrInt = std.meta.Int(.unsigned, @bitSizeOf(anyerror));
|
||||
const PaddingInt = std.meta.Int(.unsigned, 8 - (@bitSizeOf(ErrInt) + @bitSizeOf(bool)) % 8);
|
||||
|
||||
const ErrorHandler = packed struct {
|
||||
has_error: bool = false,
|
||||
err_int: ErrInt = 0,
|
||||
padding: PaddingInt = 0,
|
||||
};
|
||||
|
||||
const SharedError = @This();
|
||||
|
||||
data: []align(std.heap.page_size_min) u8,
|
||||
write_error_event_fn: ?*const fn (anyerror, *anyopaque) anyerror!void,
|
||||
ctx: ?*anyopaque,
|
||||
|
||||
pub fn init(
|
||||
write_error_event_fn: ?*const fn (anyerror, *anyopaque) anyerror!void,
|
||||
ctx: ?*anyopaque,
|
||||
) !SharedError {
|
||||
const data = try std.posix.mmap(null, @sizeOf(ErrorHandler), .{ .READ = true, .WRITE = true }, .{ .TYPE = .SHARED, .ANONYMOUS = true }, -1, 0);
|
||||
|
||||
return .{
|
||||
.data = data,
|
||||
.write_error_event_fn = write_error_event_fn,
|
||||
.ctx = ctx,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *SharedError) void {
|
||||
std.posix.munmap(self.data);
|
||||
}
|
||||
|
||||
pub fn writeError(self: SharedError, err: anyerror) void {
|
||||
var writer: std.Io.Writer = .fixed(self.data);
|
||||
writer.writeStruct(ErrorHandler{ .has_error = true, .err_int = @intFromError(err) }, .native) catch {};
|
||||
|
||||
if (self.write_error_event_fn) |write_error_event_fn| {
|
||||
@call(.auto, write_error_event_fn, .{ err, self.ctx.? }) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn readError(self: SharedError) ?anyerror {
|
||||
var reader: std.Io.Reader = .fixed(self.data);
|
||||
const err_handler = try reader.takeStruct(ErrorHandler, .native);
|
||||
|
||||
if (err_handler.has_error)
|
||||
return @errorFromInt(err_handler.err_int);
|
||||
|
||||
return null;
|
||||
}
|
||||
6
ly-core/src/UidRange.zig
Normal file
6
ly-core/src/UidRange.zig
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
const std = @import("std");
|
||||
|
||||
// We set both values to 0 by default so that, in case they aren't present in
|
||||
// the login.defs for some reason, then only the root username will be shown
|
||||
uid_min: std.posix.uid_t = 0,
|
||||
uid_max: std.posix.uid_t = 0,
|
||||
433
ly-core/src/interop.zig
Normal file
433
ly-core/src/interop.zig
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const build_options = @import("build_options");
|
||||
const UidRange = @import("UidRange.zig");
|
||||
const pwd = @import("pwd");
|
||||
const stdlib = @import("stdlib");
|
||||
const unistd = @import("unistd");
|
||||
const grp = @import("grp");
|
||||
const system_time = @import("system_time");
|
||||
const time = @import("time");
|
||||
|
||||
pub const pam = @import("pam");
|
||||
pub const utmp = @import("utmp");
|
||||
// Exists for X11 support only
|
||||
pub const xcb = @import("xcb");
|
||||
|
||||
// FreeBSD only
|
||||
pub const sysctl = @import("sysctl");
|
||||
|
||||
pub const TimeOfDay = struct {
|
||||
seconds: i64,
|
||||
microseconds: i64,
|
||||
};
|
||||
|
||||
pub const UsernameEntry = struct {
|
||||
username: ?[]const u8,
|
||||
uid: std.posix.uid_t,
|
||||
gid: std.posix.gid_t,
|
||||
home: ?[]const u8,
|
||||
shell: ?[]const u8,
|
||||
passwd_struct: [*]pwd.passwd,
|
||||
};
|
||||
|
||||
// Contains the platform-specific code
|
||||
fn PlatformStruct() type {
|
||||
return switch (builtin.os.tag) {
|
||||
.linux => struct {
|
||||
pub const kd = @import("kd");
|
||||
pub const vt = @import("vt");
|
||||
|
||||
pub const LedState = c_char;
|
||||
pub const get_led_state = kd.KDGKBLED;
|
||||
pub const set_led_state = kd.KDSKBLED;
|
||||
pub const numlock_led = kd.K_NUMLOCK;
|
||||
pub const capslock_led = kd.K_CAPSLOCK;
|
||||
pub const vt_activate = vt.VT_ACTIVATE;
|
||||
pub const vt_waitactive = vt.VT_WAITACTIVE;
|
||||
|
||||
pub fn setUserContextImpl(username: [*:0]const u8, entry: UsernameEntry) !void {
|
||||
const status = grp.initgroups(username, @intCast(entry.gid));
|
||||
if (status != 0) return error.GroupInitializationFailed;
|
||||
|
||||
if (isError(std.posix.system.setgid(@intCast(entry.gid)))) return error.SetUserGidFailed;
|
||||
if (isError(std.posix.system.setuid(@intCast(entry.uid)))) return error.SetUserUidFailed;
|
||||
}
|
||||
|
||||
// Procedure:
|
||||
// 1. Open /proc/self/stat to retrieve the tty_nr field
|
||||
// 2. Parse the tty_nr field to extract the major and minor device
|
||||
// numbers
|
||||
// 3. Then, read every /sys/class/tty/[dir]/dev, where [dir] is
|
||||
// every sub-directory
|
||||
// 4. Finally, compare the major and minor device numbers with the
|
||||
// extracted values. If they correspond, parse [dir] to get the
|
||||
// TTY ID
|
||||
pub fn getActiveTtyImpl(allocator: std.mem.Allocator, io: std.Io, use_kmscon_vt: bool) !u8 {
|
||||
var file_buffer: [256]u8 = undefined;
|
||||
|
||||
if (use_kmscon_vt) {
|
||||
var file = try std.Io.Dir.openFileAbsolute(io, "/sys/class/tty/tty0/active", .{});
|
||||
defer file.close(io);
|
||||
|
||||
var reader = file.reader(io, &file_buffer);
|
||||
var buffer: [16]u8 = undefined;
|
||||
const read = try readBuffer(&reader.interface, &buffer);
|
||||
|
||||
const tty = buffer[0..(read - 1)];
|
||||
return std.fmt.parseInt(u8, tty["tty".len..], 10);
|
||||
}
|
||||
|
||||
var tty_major: u16 = undefined;
|
||||
var tty_minor: u16 = undefined;
|
||||
|
||||
{
|
||||
var file = try std.Io.Dir.openFileAbsolute(io, "/proc/self/stat", .{});
|
||||
defer file.close(io);
|
||||
|
||||
var reader = file.reader(io, &file_buffer);
|
||||
var buffer: [1024]u8 = undefined;
|
||||
const read = try readBuffer(&reader.interface, &buffer);
|
||||
|
||||
var iterator = std.mem.splitScalar(u8, buffer[0..read], ' ');
|
||||
var fields: [52][]const u8 = undefined;
|
||||
var index: usize = 0;
|
||||
|
||||
while (iterator.next()) |field| {
|
||||
fields[index] = field;
|
||||
index += 1;
|
||||
}
|
||||
|
||||
const tty_nr = try std.fmt.parseInt(u16, fields[6], 10);
|
||||
tty_major = tty_nr / 256;
|
||||
tty_minor = tty_nr % 256;
|
||||
}
|
||||
|
||||
var directory = try std.Io.Dir.openDirAbsolute(io, "/sys/class/tty", .{ .iterate = true });
|
||||
defer directory.close(io);
|
||||
|
||||
var iterator = directory.iterate();
|
||||
while (try iterator.next(io)) |entry| {
|
||||
const path = try std.fmt.allocPrint(allocator, "/sys/class/tty/{s}/dev", .{entry.name});
|
||||
defer allocator.free(path);
|
||||
|
||||
var file = try std.Io.Dir.openFileAbsolute(io, path, .{});
|
||||
defer file.close(io);
|
||||
|
||||
var reader = file.reader(io, &file_buffer);
|
||||
var buffer: [16]u8 = undefined;
|
||||
const read = try readBuffer(&reader.interface, &buffer);
|
||||
|
||||
var device_iterator = std.mem.splitScalar(u8, buffer[0..(read - 1)], ':');
|
||||
const device_major_str = device_iterator.next() orelse continue;
|
||||
const device_minor_str = device_iterator.next() orelse continue;
|
||||
|
||||
const device_major = try std.fmt.parseInt(u8, device_major_str, 10);
|
||||
const device_minor = try std.fmt.parseInt(u8, device_minor_str, 10);
|
||||
|
||||
if (device_major == tty_major and device_minor == tty_minor) {
|
||||
const tty_id_str = entry.name["tty".len..];
|
||||
return try std.fmt.parseInt(u8, tty_id_str, 10);
|
||||
}
|
||||
}
|
||||
|
||||
return error.NoTtyFound;
|
||||
}
|
||||
|
||||
// This is very bad parsing, but we only need to get 2 values..
|
||||
// and the format of the file seems to be standard? So this should
|
||||
// be fine...
|
||||
pub fn getUserIdRange(allocator: std.mem.Allocator, io: std.Io, file_path: []const u8) !UidRange {
|
||||
const login_defs_file = try std.Io.Dir.cwd().openFile(io, file_path, .{});
|
||||
defer login_defs_file.close(io);
|
||||
|
||||
var buffer: [4096]u8 = undefined;
|
||||
var reader = login_defs_file.reader(io, &buffer);
|
||||
|
||||
const login_defs_buffer = try reader.interface.allocRemaining(allocator, .unlimited);
|
||||
defer allocator.free(login_defs_buffer);
|
||||
|
||||
var iterator = std.mem.splitScalar(u8, login_defs_buffer, '\n');
|
||||
var uid_range = UidRange{};
|
||||
var uid_min_Found = false;
|
||||
var uid_max_Found = false;
|
||||
|
||||
while (iterator.next()) |line| {
|
||||
const trimmed_line = std.mem.trim(u8, line, " \n\r\t");
|
||||
|
||||
if (std.mem.startsWith(u8, trimmed_line, "UID_MIN")) {
|
||||
uid_range.uid_min = try parseValue(std.posix.uid_t, "UID_MIN", trimmed_line);
|
||||
uid_min_Found = true;
|
||||
} else if (std.mem.startsWith(u8, trimmed_line, "UID_MAX")) {
|
||||
uid_range.uid_max = try parseValue(std.posix.uid_t, "UID_MAX", trimmed_line);
|
||||
uid_max_Found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(uid_min_Found or uid_max_Found)) {
|
||||
return error.UidNameNotFound;
|
||||
}
|
||||
|
||||
if (!uid_min_Found) {
|
||||
uid_range.uid_min = build_options.fallback_uid_min;
|
||||
}
|
||||
|
||||
if (!uid_max_Found) {
|
||||
uid_range.uid_max = build_options.fallback_uid_max;
|
||||
}
|
||||
|
||||
return uid_range;
|
||||
}
|
||||
|
||||
pub fn shutdownSystemImpl() !void {
|
||||
std.posix.system.sync();
|
||||
if (isError(std.os.linux.reboot(.MAGIC1, .MAGIC2, .POWER_OFF, null))) {
|
||||
return error.CouldntShutdown;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rebootSystemImpl() !void {
|
||||
std.posix.system.sync();
|
||||
if (isError(std.os.linux.reboot(.MAGIC1, .MAGIC2, .RESTART, null))) {
|
||||
return error.CouldntShutdown;
|
||||
}
|
||||
}
|
||||
|
||||
fn parseValue(comptime T: type, name: []const u8, buffer: []const u8) !T {
|
||||
var iterator = std.mem.splitAny(u8, buffer, " \t");
|
||||
var maybe_value: ?T = null;
|
||||
|
||||
while (iterator.next()) |slice| {
|
||||
// Skip the slice if it's empty (whitespace) or is the name of the
|
||||
// property (e.g. UID_MIN or UID_MAX)
|
||||
if (slice.len == 0 or std.mem.eql(u8, slice, name)) continue;
|
||||
maybe_value = std.fmt.parseInt(T, slice, 10) catch continue;
|
||||
}
|
||||
|
||||
return maybe_value orelse error.ValueNotFound;
|
||||
}
|
||||
|
||||
fn readBuffer(reader: *std.Io.Reader, buffer: []u8) !usize {
|
||||
var bytes_read: usize = 0;
|
||||
var byte: u8 = try reader.takeByte();
|
||||
|
||||
while (byte != 0 and bytes_read < buffer.len) {
|
||||
buffer[bytes_read] = byte;
|
||||
bytes_read += 1;
|
||||
byte = reader.takeByte() catch break;
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
},
|
||||
.freebsd => struct {
|
||||
pub const kbio = @import("kbio");
|
||||
pub const consio = @import("consio");
|
||||
pub const reboot = @import("reboot");
|
||||
|
||||
pub const LedState = c_int;
|
||||
pub const get_led_state = kbio.KDGETLED;
|
||||
pub const set_led_state = kbio.KDSETLED;
|
||||
pub const numlock_led = kbio.LED_NUM;
|
||||
pub const capslock_led = kbio.LED_CAP;
|
||||
pub const vt_activate = consio.VT_ACTIVATE;
|
||||
pub const vt_waitactive = consio.VT_WAITACTIVE;
|
||||
|
||||
const FREEBSD_UID_MIN = 1000;
|
||||
const FREEBSD_UID_MAX = 32000;
|
||||
|
||||
pub fn setUserContextImpl(username: [*:0]const u8, entry: UsernameEntry) !void {
|
||||
// FreeBSD has initgroups() in unistd
|
||||
const status = unistd.initgroups(username, @intCast(entry.gid));
|
||||
if (status != 0) return error.GroupInitializationFailed;
|
||||
|
||||
// FreeBSD sets the GID and UID with setusercontext()
|
||||
const result = pwd.setusercontext(null, entry.passwd_struct, @intCast(entry.uid), pwd.LOGIN_SETALL);
|
||||
if (result != 0) return error.SetUserUidFailed;
|
||||
}
|
||||
|
||||
pub fn getActiveTtyImpl(_: std.mem.Allocator, _: std.Io, _: bool) !u8 {
|
||||
return error.FeatureUnimplemented;
|
||||
}
|
||||
|
||||
pub fn getUserIdRange(_: std.mem.Allocator, _: std.Io, _: []const u8) !UidRange {
|
||||
return .{
|
||||
// Hardcoded default values chosen from
|
||||
// /usr/src/usr.sbin/pw/pw_conf.c
|
||||
.uid_min = FREEBSD_UID_MIN,
|
||||
.uid_max = FREEBSD_UID_MAX,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn shutdownSystemImpl() !void {
|
||||
if (isError(unistd.reboot(reboot.RB_POWEROFF))) {
|
||||
return error.CouldntShutdown;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rebootSystemImpl() !void {
|
||||
if (isError(unistd.reboot(reboot.RB_AUTOBOOT))) {
|
||||
return error.CouldntReboot;
|
||||
}
|
||||
}
|
||||
},
|
||||
else => @compileError("Unsupported target: " ++ builtin.os.tag),
|
||||
};
|
||||
}
|
||||
|
||||
const platform_struct = PlatformStruct();
|
||||
|
||||
pub fn isError(result: anytype) bool {
|
||||
if (@typeInfo(@TypeOf(result)).int.signedness == .signed) {
|
||||
return result < 0;
|
||||
}
|
||||
|
||||
if (@typeInfo(@TypeOf(result)).int.signedness == .unsigned) {
|
||||
return switch (builtin.os.tag) {
|
||||
.linux => std.os.linux.errno(result) != .SUCCESS,
|
||||
else => @compileError("interop.isError() not implemented for current target!"),
|
||||
};
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub fn supportsUnicode() bool {
|
||||
return builtin.os.tag == .linux or builtin.os.tag == .freebsd;
|
||||
}
|
||||
|
||||
pub fn timeAsString(io: std.Io, buf: [:0]u8, format: [:0]const u8) []u8 {
|
||||
const timer: isize = @intCast(std.Io.Timestamp.now(io, .real).toSeconds());
|
||||
const tm_info = time.localtime(&timer);
|
||||
const len = time.strftime(buf, buf.len, format, tm_info);
|
||||
|
||||
return buf[0..len];
|
||||
}
|
||||
|
||||
pub fn getTimeOfDay() !TimeOfDay {
|
||||
var tv: system_time.timeval = undefined;
|
||||
const status = system_time.gettimeofday(&tv, null);
|
||||
|
||||
if (status != 0) return error.FailedToGetTimeOfDay;
|
||||
|
||||
return .{
|
||||
.seconds = @intCast(tv.tv_sec),
|
||||
.microseconds = @intCast(tv.tv_usec),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getActiveTty(allocator: std.mem.Allocator, io: std.Io, use_kmscon_vt: bool) !u8 {
|
||||
return platform_struct.getActiveTtyImpl(allocator, io, use_kmscon_vt);
|
||||
}
|
||||
|
||||
pub fn switchTty(tty: u8) !void {
|
||||
var status = std.c.ioctl(std.posix.STDIN_FILENO, platform_struct.vt_activate, tty);
|
||||
if (status != 0) return error.FailedToActivateTty;
|
||||
|
||||
status = std.c.ioctl(std.posix.STDIN_FILENO, platform_struct.vt_waitactive, tty);
|
||||
if (status != 0) return error.FailedToWaitForActiveTty;
|
||||
}
|
||||
|
||||
pub fn getLockState() !struct {
|
||||
numlock: bool,
|
||||
capslock: bool,
|
||||
} {
|
||||
var led: platform_struct.LedState = undefined;
|
||||
const status = std.c.ioctl(std.posix.STDIN_FILENO, platform_struct.get_led_state, &led);
|
||||
if (status != 0) return error.FailedToGetLockState;
|
||||
|
||||
return .{
|
||||
.numlock = (led & platform_struct.numlock_led) != 0,
|
||||
.capslock = (led & platform_struct.capslock_led) != 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setNumlock(val: bool) !void {
|
||||
var led: platform_struct.LedState = undefined;
|
||||
var status = std.c.ioctl(std.posix.STDIN_FILENO, platform_struct.get_led_state, &led);
|
||||
if (status != 0) return error.FailedToGetNumlock;
|
||||
|
||||
const numlock = (led & platform_struct.numlock_led) != 0;
|
||||
if (numlock != val) {
|
||||
status = std.c.ioctl(std.posix.STDIN_FILENO, platform_struct.set_led_state, led ^ platform_struct.numlock_led);
|
||||
if (status != 0) return error.FailedToSetNumlock;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setUserContext(allocator: std.mem.Allocator, entry: UsernameEntry) !void {
|
||||
const username_z = try allocator.dupeZ(u8, entry.username.?);
|
||||
defer allocator.free(username_z);
|
||||
|
||||
return platform_struct.setUserContextImpl(username_z.ptr, entry);
|
||||
}
|
||||
|
||||
pub fn setUserShell(entry: *UsernameEntry) void {
|
||||
unistd.setusershell();
|
||||
|
||||
const shell = unistd.getusershell();
|
||||
entry.shell = std.mem.span(shell);
|
||||
|
||||
unistd.endusershell();
|
||||
}
|
||||
|
||||
pub fn setEnvironmentVariable(allocator: std.mem.Allocator, name: []const u8, value: []const u8, replace: bool) !void {
|
||||
const name_z = try allocator.dupeZ(u8, name);
|
||||
defer allocator.free(name_z);
|
||||
|
||||
const value_z = try allocator.dupeZ(u8, value);
|
||||
defer allocator.free(value_z);
|
||||
|
||||
const status = stdlib.setenv(name_z.ptr, value_z.ptr, @intFromBool(replace));
|
||||
if (status != 0) return error.SetEnvironmentVariableFailed;
|
||||
}
|
||||
|
||||
pub fn putEnvironmentVariable(name_and_value: []u8) !void {
|
||||
const status = stdlib.putenv(name_and_value.ptr);
|
||||
if (status != 0) return error.PutEnvironmentVariableFailed;
|
||||
}
|
||||
|
||||
pub fn getNextUsernameEntry() ?UsernameEntry {
|
||||
const entry = pwd.getpwent();
|
||||
if (entry == null) return null;
|
||||
|
||||
return .{
|
||||
.username = if (entry.*.pw_name) |name| std.mem.span(name) else null,
|
||||
.uid = @intCast(entry.*.pw_uid),
|
||||
.gid = @intCast(entry.*.pw_gid),
|
||||
.home = if (entry.*.pw_dir) |dir| std.mem.span(dir) else null,
|
||||
.shell = if (entry.*.pw_shell) |shell| std.mem.span(shell) else null,
|
||||
.passwd_struct = entry,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getUsernameEntry(username: [:0]const u8) ?UsernameEntry {
|
||||
const entry = pwd.getpwnam(username);
|
||||
if (entry == null) return null;
|
||||
|
||||
return .{
|
||||
.username = if (entry.*.pw_name) |name| std.mem.span(name) else null,
|
||||
.uid = @intCast(entry.*.pw_uid),
|
||||
.gid = @intCast(entry.*.pw_gid),
|
||||
.home = if (entry.*.pw_dir) |dir| std.mem.span(dir) else null,
|
||||
.shell = if (entry.*.pw_shell) |shell| std.mem.span(shell) else null,
|
||||
.passwd_struct = entry,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn closePasswordDatabase() void {
|
||||
pwd.endpwent();
|
||||
}
|
||||
|
||||
// This is very bad parsing, but we only need to get 2 values... and the format
|
||||
// of the file doesn't seem to be standard? So this should be fine...
|
||||
pub fn getUserIdRange(allocator: std.mem.Allocator, io: std.Io, file_path: []const u8) !UidRange {
|
||||
return platform_struct.getUserIdRange(allocator, io, file_path);
|
||||
}
|
||||
|
||||
pub fn shutdownSystem() !void {
|
||||
try platform_struct.shutdownSystemImpl();
|
||||
}
|
||||
|
||||
pub fn rebootSystem() !void {
|
||||
try platform_struct.rebootSystemImpl();
|
||||
}
|
||||
78
ly-core/src/root.zig
Normal file
78
ly-core/src/root.zig
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub const ini = @import("zigini");
|
||||
|
||||
pub const interop = @import("interop.zig");
|
||||
pub const UidRange = @import("UidRange.zig");
|
||||
pub const LogFile = @import("LogFile.zig");
|
||||
pub const SharedError = @import("SharedError.zig");
|
||||
|
||||
pub fn IniParser(comptime Struct: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
const temporary_allocator = std.heap.page_allocator;
|
||||
|
||||
pub const Error = struct {
|
||||
type_name: []const u8,
|
||||
key: []const u8,
|
||||
value: []const u8,
|
||||
error_name: []const u8,
|
||||
};
|
||||
pub var global_errors: std.ArrayList(Error) = .empty;
|
||||
|
||||
ini_struct: ini.Ini(Struct),
|
||||
structure: Struct,
|
||||
maybe_load_error: ?anyerror,
|
||||
errors: std.ArrayList(Error),
|
||||
|
||||
pub fn init(
|
||||
allocator: std.mem.Allocator,
|
||||
io: std.Io,
|
||||
path: []const u8,
|
||||
field_handler: ?fn (allocator: std.mem.Allocator, field: ini.IniField) ?ini.IniField,
|
||||
) !Self {
|
||||
var ini_struct = ini.Ini(Struct).init(allocator);
|
||||
errdefer ini_struct.deinit();
|
||||
|
||||
var maybe_load_error: ?anyerror = null;
|
||||
|
||||
const structure = ini_struct.readFileToStruct(io, path, .{
|
||||
.fieldHandler = field_handler,
|
||||
.errorHandler = errorHandler,
|
||||
.comment_characters = "#",
|
||||
}) catch |err| load_error: {
|
||||
maybe_load_error = err;
|
||||
break :load_error Struct{};
|
||||
};
|
||||
|
||||
return .{
|
||||
.ini_struct = ini_struct,
|
||||
.structure = structure,
|
||||
.maybe_load_error = maybe_load_error,
|
||||
.errors = global_errors,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.ini_struct.deinit();
|
||||
|
||||
for (0..global_errors.items.len) |i| {
|
||||
const err = global_errors.items[i];
|
||||
temporary_allocator.free(err.type_name);
|
||||
temporary_allocator.free(err.key);
|
||||
temporary_allocator.free(err.value);
|
||||
}
|
||||
|
||||
global_errors.deinit(temporary_allocator);
|
||||
}
|
||||
|
||||
fn errorHandler(type_name: []const u8, key: []const u8, value: []const u8, err: anyerror) void {
|
||||
global_errors.append(temporary_allocator, .{
|
||||
.type_name = temporary_allocator.dupe(u8, type_name) catch return,
|
||||
.key = temporary_allocator.dupe(u8, key) catch return,
|
||||
.value = temporary_allocator.dupe(u8, value) catch return,
|
||||
.error_name = @errorName(err),
|
||||
}) catch return;
|
||||
}
|
||||
};
|
||||
}
|
||||
62
ly-ui/build.zig
Normal file
62
ly-ui/build.zig
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
const std = @import("std");
|
||||
const Translator = @import("translate_c").Translator;
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
const enable_x11_support = b.option(bool, "enable_x11_support", "Enable X11 support") orelse true;
|
||||
const mod = b.addModule("ly-ui", .{
|
||||
.root_source_file = b.path("src/root.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const fallback_uid_min = b.option(std.posix.uid_t, "fallback_uid_min", "Set the fallback minimum UID (default is 1000). This value gets embedded into the binary");
|
||||
const fallback_uid_max = b.option(std.posix.uid_t, "fallback_uid_max", "Set the fallback maximum UID (default is 60000). This value gets embedded into the binary");
|
||||
|
||||
const ly_core = b.dependency("ly_core", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.enable_x11_support = enable_x11_support,
|
||||
.fallback_uid_min = fallback_uid_min,
|
||||
.fallback_uid_max = fallback_uid_max,
|
||||
});
|
||||
mod.addImport("ly-core", ly_core.module("ly-core"));
|
||||
|
||||
const termbox_dep = b.dependency("termbox2", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const translate_c_dep = b.dependency("translate_c", .{
|
||||
.target = target,
|
||||
});
|
||||
|
||||
const termbox2: Translator = .init(translate_c_dep, .{
|
||||
.c_source_file = termbox_dep.path("termbox2.h"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
termbox2.defineCMacro("TB_IMPL", null);
|
||||
// TODO 0.16.0: Workaround until Aro gets better...
|
||||
// https://codeberg.org/ziglang/translate-c/issues/319
|
||||
termbox2.defineCMacro("_XOPEN_SOURCE", "700");
|
||||
termbox2.defineCMacro("TB_OPT_ATTR_W", "32"); // Enable 24-bit color support + styling (32-bit)
|
||||
// TODO 0.16.0: Including <fcntl.h> with -OReleaseSafe causes
|
||||
// __attribute__(__error__()) to be called. Below
|
||||
// is the workaround.
|
||||
termbox2.defineCMacro("_FORTIFY_SOURCE", "0");
|
||||
// TODO 0.16.0: Needed for now
|
||||
if (target.result.os.tag == .freebsd) {
|
||||
termbox2.defineCMacro("__BSD_VISIBLE", "1");
|
||||
}
|
||||
mod.addImport("termbox2", termbox2.mod);
|
||||
|
||||
const mod_tests = b.addTest(.{
|
||||
.root_module = mod,
|
||||
});
|
||||
const run_mod_tests = b.addRunArtifact(mod_tests);
|
||||
|
||||
const test_step = b.step("test", "Run tests");
|
||||
test_step.dependOn(&run_mod_tests.step);
|
||||
}
|
||||
24
ly-ui/build.zig.zon
Normal file
24
ly-ui/build.zig.zon
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
.{
|
||||
.name = .ly_ui,
|
||||
.version = "1.1.0",
|
||||
.fingerprint = 0x8d11bf85a74ec803,
|
||||
.minimum_zig_version = "0.16.0",
|
||||
.dependencies = .{
|
||||
.ly_core = .{
|
||||
.path = "../ly-core",
|
||||
},
|
||||
.termbox2 = .{
|
||||
.url = "git+https://github.com/AnErrupTion/termbox2?ref=master#c7f241e8888ce243e1748b05c26a42fcfaaad936",
|
||||
.hash = "N-V-__8AAAUXBQD6Fwpi9m0MBqWXFFaqW5l1lVrJC2Ynj7a-",
|
||||
},
|
||||
.translate_c = .{
|
||||
.url = "git+https://codeberg.org/ziglang/translate-c?ref=zig-0.16.x#6fe0ffc4549f15c5f2d9432c2b4460ba90ff85ac",
|
||||
.hash = "translate_c-1.0.0-Q_BUWo_5BgD4flHdUhA31zOz0XvZk9k7lQv1ouzyNXj2",
|
||||
},
|
||||
},
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
},
|
||||
}
|
||||
21
ly-ui/src/Cell.zig
Normal file
21
ly-ui/src/Cell.zig
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
const TerminalBuffer = @import("TerminalBuffer.zig");
|
||||
|
||||
const Cell = @This();
|
||||
|
||||
ch: u32,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
|
||||
pub fn init(ch: u32, fg: u32, bg: u32) Cell {
|
||||
return .{
|
||||
.ch = ch,
|
||||
.fg = fg,
|
||||
.bg = bg,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn put(self: Cell, x: usize, y: usize) !void {
|
||||
if (self.ch == 0) return;
|
||||
|
||||
try TerminalBuffer.setCell(x, y, self);
|
||||
}
|
||||
221
ly-ui/src/Position.zig
Normal file
221
ly-ui/src/Position.zig
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
const Position = @This();
|
||||
|
||||
x: usize,
|
||||
y: usize,
|
||||
|
||||
pub fn init(x: usize, y: usize) Position {
|
||||
return .{
|
||||
.x = x,
|
||||
.y = y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn add(self: Position, other: Position) Position {
|
||||
return .{
|
||||
.x = self.x + other.x,
|
||||
.y = self.y + other.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addIf(self: Position, other: Position, condition: bool) Position {
|
||||
return .{
|
||||
.x = self.x + if (condition) other.x else 0,
|
||||
.y = self.y + if (condition) other.y else 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addX(self: Position, x: usize) Position {
|
||||
return .{
|
||||
.x = self.x + x,
|
||||
.y = self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addY(self: Position, y: usize) Position {
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = self.y + y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addXIf(self: Position, x: usize, condition: bool) Position {
|
||||
return .{
|
||||
.x = self.x + if (condition) x else 0,
|
||||
.y = self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addYIf(self: Position, y: usize, condition: bool) Position {
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = self.y + if (condition) y else 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addXFrom(self: Position, other: Position) Position {
|
||||
return .{
|
||||
.x = self.x + other.x,
|
||||
.y = self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addYFrom(self: Position, other: Position) Position {
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = self.y + other.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addXFromIf(self: Position, other: Position, condition: bool) Position {
|
||||
return .{
|
||||
.x = self.x + if (condition) other.x else 0,
|
||||
.y = self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addYFromIf(self: Position, other: Position, condition: bool) Position {
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = self.y + if (condition) other.y else 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn remove(self: Position, other: Position) Position {
|
||||
return .{
|
||||
.x = self.x - other.x,
|
||||
.y = self.y - other.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn removeIf(self: Position, other: Position, condition: bool) Position {
|
||||
return .{
|
||||
.x = self.x - if (condition) other.x else 0,
|
||||
.y = self.y - if (condition) other.y else 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn removeX(self: Position, x: usize) Position {
|
||||
return .{
|
||||
.x = self.x - x,
|
||||
.y = self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn removeY(self: Position, y: usize) Position {
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = self.y - y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn removeXIf(self: Position, x: usize, condition: bool) Position {
|
||||
return .{
|
||||
.x = self.x - if (condition) x else 0,
|
||||
.y = self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn removeYIf(self: Position, y: usize, condition: bool) Position {
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = self.y - if (condition) y else 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn removeXFrom(self: Position, other: Position) Position {
|
||||
return .{
|
||||
.x = self.x - other.x,
|
||||
.y = self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn removeYFrom(self: Position, other: Position) Position {
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = self.y - other.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn removeXFromIf(self: Position, other: Position, condition: bool) Position {
|
||||
return .{
|
||||
.x = self.x - if (condition) other.x else 0,
|
||||
.y = self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn removeYFromIf(self: Position, other: Position, condition: bool) Position {
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = self.y - if (condition) other.y else 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn invert(self: Position, other: Position) Position {
|
||||
return .{
|
||||
.x = other.x - self.x,
|
||||
.y = other.y - self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn invertIf(self: Position, other: Position, condition: bool) Position {
|
||||
return .{
|
||||
.x = if (condition) other.x - self.x else self.x,
|
||||
.y = if (condition) other.y - self.y else self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn invertX(self: Position, width: usize) Position {
|
||||
return .{
|
||||
.x = width - self.x,
|
||||
.y = self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn invertY(self: Position, height: usize) Position {
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = height - self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn invertXIf(self: Position, width: usize, condition: bool) Position {
|
||||
return .{
|
||||
.x = if (condition) width - self.x else self.x,
|
||||
.y = self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn invertYIf(self: Position, height: usize, condition: bool) Position {
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = if (condition) height - self.y else self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn resetXFrom(self: Position, other: Position) Position {
|
||||
return .{
|
||||
.x = other.x,
|
||||
.y = self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn resetYFrom(self: Position, other: Position) Position {
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = other.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn resetXFromIf(self: Position, other: Position, condition: bool) Position {
|
||||
return .{
|
||||
.x = if (condition) other.x else self.x,
|
||||
.y = self.y,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn resetYFromIf(self: Position, other: Position, condition: bool) Position {
|
||||
return .{
|
||||
.x = self.x,
|
||||
.y = if (condition) other.y else self.y,
|
||||
};
|
||||
}
|
||||
680
ly-ui/src/TerminalBuffer.zig
Normal file
680
ly-ui/src/TerminalBuffer.zig
Normal file
|
|
@ -0,0 +1,680 @@
|
|||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Random = std.Random;
|
||||
|
||||
const ly_core = @import("ly-core");
|
||||
const interop = ly_core.interop;
|
||||
const LogFile = ly_core.LogFile;
|
||||
const SharedError = ly_core.SharedError;
|
||||
pub const termbox = @import("termbox2");
|
||||
|
||||
const Cell = @import("Cell.zig");
|
||||
const keyboard = @import("keyboard.zig");
|
||||
const Position = @import("Position.zig");
|
||||
const Widget = @import("Widget.zig");
|
||||
|
||||
const TerminalBuffer = @This();
|
||||
|
||||
pub const KeybindCallbackFn = *const fn (*anyopaque) anyerror!bool;
|
||||
pub const KeybindMap = std.AutoHashMap(keyboard.Key, struct {
|
||||
callback: KeybindCallbackFn,
|
||||
context: *anyopaque,
|
||||
});
|
||||
|
||||
pub const InitOptions = struct {
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
border_fg: u32,
|
||||
full_color: bool,
|
||||
is_tty: bool,
|
||||
};
|
||||
|
||||
pub const Styling = struct {
|
||||
pub const BOLD = termbox.TB_BOLD;
|
||||
pub const UNDERLINE = termbox.TB_UNDERLINE;
|
||||
pub const REVERSE = termbox.TB_REVERSE;
|
||||
pub const ITALIC = termbox.TB_ITALIC;
|
||||
pub const BLINK = termbox.TB_BLINK;
|
||||
pub const HI_BLACK = termbox.TB_HI_BLACK;
|
||||
pub const BRIGHT = termbox.TB_BRIGHT;
|
||||
pub const DIM = termbox.TB_DIM;
|
||||
};
|
||||
|
||||
pub const Color = struct {
|
||||
pub const DEFAULT = 0x00000000;
|
||||
pub const TRUE_BLACK = Styling.HI_BLACK;
|
||||
pub const TRUE_RED = 0x00FF0000;
|
||||
pub const TRUE_GREEN = 0x0000FF00;
|
||||
pub const TRUE_YELLOW = 0x00FFFF00;
|
||||
pub const TRUE_BLUE = 0x000000FF;
|
||||
pub const TRUE_MAGENTA = 0x00FF00FF;
|
||||
pub const TRUE_CYAN = 0x0000FFFF;
|
||||
pub const TRUE_WHITE = 0x00FFFFFF;
|
||||
pub const TRUE_DIM_RED = 0x00800000;
|
||||
pub const TRUE_DIM_GREEN = 0x00008000;
|
||||
pub const TRUE_DIM_YELLOW = 0x00808000;
|
||||
pub const TRUE_DIM_BLUE = 0x00000080;
|
||||
pub const TRUE_DIM_MAGENTA = 0x00800080;
|
||||
pub const TRUE_DIM_CYAN = 0x00008080;
|
||||
pub const TRUE_DIM_WHITE = 0x00C0C0C0;
|
||||
pub const ECOL_BLACK = 1;
|
||||
pub const ECOL_RED = 2;
|
||||
pub const ECOL_GREEN = 3;
|
||||
pub const ECOL_YELLOW = 4;
|
||||
pub const ECOL_BLUE = 5;
|
||||
pub const ECOL_MAGENTA = 6;
|
||||
pub const ECOL_CYAN = 7;
|
||||
pub const ECOL_WHITE = 8;
|
||||
};
|
||||
|
||||
pub const START_POSITION = Position.init(0, 0);
|
||||
|
||||
log_file: *LogFile,
|
||||
random: Random,
|
||||
width: usize,
|
||||
height: usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
border_fg: u32,
|
||||
box_chars: struct {
|
||||
left_up: u32,
|
||||
left_down: u32,
|
||||
right_up: u32,
|
||||
right_down: u32,
|
||||
top: u32,
|
||||
bottom: u32,
|
||||
left: u32,
|
||||
right: u32,
|
||||
},
|
||||
blank_cell: Cell,
|
||||
full_color: bool,
|
||||
termios: ?std.posix.termios,
|
||||
keybinds: KeybindMap,
|
||||
handlable_widgets: std.ArrayList(*Widget),
|
||||
run: bool,
|
||||
update: bool,
|
||||
active_widget_index: usize,
|
||||
|
||||
pub fn init(
|
||||
allocator: Allocator,
|
||||
io: std.Io,
|
||||
options: InitOptions,
|
||||
log_file: *LogFile,
|
||||
random: Random,
|
||||
) !TerminalBuffer {
|
||||
// Initialize termbox
|
||||
var err = termbox.tb_init();
|
||||
if (err != 0) {
|
||||
try log_file.err(
|
||||
io,
|
||||
"tui",
|
||||
"failed to initialise termbox2: {s}, term: {s}",
|
||||
.{ termbox.tb_strerror(err), std.c.getenv("TERM").? },
|
||||
);
|
||||
return error.TermboxInitFailed;
|
||||
}
|
||||
|
||||
if (options.full_color) {
|
||||
err = termbox.tb_set_output_mode(termbox.TB_OUTPUT_TRUECOLOR);
|
||||
if (err != 0) {
|
||||
try log_file.err(
|
||||
io,
|
||||
"tui",
|
||||
"failed to set termbox2 output mode to 24-bit color: {s}",
|
||||
.{termbox.tb_strerror(err)},
|
||||
);
|
||||
return error.TermboxSetOutputModeFailed;
|
||||
}
|
||||
try log_file.info(
|
||||
io,
|
||||
"tui",
|
||||
"termbox2 set to 24-bit color output mode",
|
||||
.{},
|
||||
);
|
||||
} else {
|
||||
try log_file.info(
|
||||
io,
|
||||
"tui",
|
||||
"termbox2 set to eight-color output mode",
|
||||
.{},
|
||||
);
|
||||
}
|
||||
|
||||
// Let's take some precautions here and clear the back buffer as well
|
||||
try clearScreen(true);
|
||||
|
||||
const width = getWidth();
|
||||
const height = getHeight();
|
||||
|
||||
try log_file.info(
|
||||
io,
|
||||
"tui",
|
||||
"screen resolution is {d}x{d}",
|
||||
.{ width, height },
|
||||
);
|
||||
|
||||
return .{
|
||||
.log_file = log_file,
|
||||
.random = random,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.fg = options.fg,
|
||||
.bg = options.bg,
|
||||
.border_fg = options.border_fg,
|
||||
.box_chars = if (interop.supportsUnicode()) .{
|
||||
.left_up = 0x250C,
|
||||
.left_down = 0x2514,
|
||||
.right_up = 0x2510,
|
||||
.right_down = 0x2518,
|
||||
.top = 0x2500,
|
||||
.bottom = 0x2500,
|
||||
.left = 0x2502,
|
||||
.right = 0x2502,
|
||||
} else .{
|
||||
.left_up = '+',
|
||||
.left_down = '+',
|
||||
.right_up = '+',
|
||||
.right_down = '+',
|
||||
.top = '-',
|
||||
.bottom = '-',
|
||||
.left = '|',
|
||||
.right = '|',
|
||||
},
|
||||
.blank_cell = Cell.init(' ', options.fg, options.bg),
|
||||
.full_color = options.full_color,
|
||||
// Needed to reclaim the TTY after giving up its control
|
||||
.termios = try std.posix.tcgetattr(std.posix.STDIN_FILENO),
|
||||
.keybinds = KeybindMap.init(allocator),
|
||||
.handlable_widgets = .empty,
|
||||
.run = true,
|
||||
.update = true,
|
||||
.active_widget_index = 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *TerminalBuffer) void {
|
||||
self.keybinds.deinit();
|
||||
TerminalBuffer.shutdown() catch {};
|
||||
}
|
||||
|
||||
pub fn runEventLoop(
|
||||
self: *TerminalBuffer,
|
||||
allocator: Allocator,
|
||||
io: std.Io,
|
||||
shared_error: SharedError,
|
||||
layers: [][]*Widget,
|
||||
active_widget: *Widget,
|
||||
inactivity_delay: u16,
|
||||
position_widgets_fn: *const fn (*anyopaque) anyerror!void,
|
||||
inactivity_event_fn: ?*const fn (*anyopaque) anyerror!void,
|
||||
context: *anyopaque,
|
||||
) !void {
|
||||
try self.registerGlobalKeybind(io, "Ctrl+K", &moveCursorUp, self);
|
||||
try self.registerGlobalKeybind(io, "Up", &moveCursorUp, self);
|
||||
|
||||
try self.registerGlobalKeybind(io, "Ctrl+J", &moveCursorDown, self);
|
||||
try self.registerGlobalKeybind(io, "Down", &moveCursorDown, self);
|
||||
|
||||
try self.registerGlobalKeybind(io, "Tab", &wrapCursor, self);
|
||||
try self.registerGlobalKeybind(io, "Shift+Tab", &wrapCursorReverse, self);
|
||||
|
||||
defer self.handlable_widgets.deinit(allocator);
|
||||
|
||||
var i: usize = 0;
|
||||
for (layers) |layer| {
|
||||
for (layer) |widget| {
|
||||
try widget.update(context);
|
||||
|
||||
if (widget.vtable.handle_fn != null) {
|
||||
try self.handlable_widgets.append(allocator, widget);
|
||||
|
||||
if (widget.id == active_widget.id) self.active_widget_index = i;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try @call(.auto, position_widgets_fn, .{context});
|
||||
|
||||
var event: termbox.tb_event = undefined;
|
||||
var inactivity_cmd_ran = false;
|
||||
var inactivity_time_start = try interop.getTimeOfDay();
|
||||
|
||||
while (self.run) {
|
||||
var maybe_timeout: ?usize = null;
|
||||
|
||||
if (self.update) {
|
||||
try TerminalBuffer.clearScreen(false);
|
||||
|
||||
// Reset cursor
|
||||
const current_widget = self.getActiveWidget();
|
||||
current_widget.handle(null) catch |err| {
|
||||
shared_error.writeError(error.SetCursorFailed);
|
||||
try self.log_file.err(
|
||||
io,
|
||||
"tui",
|
||||
"failed to set cursor in active widget '{s}': {s}",
|
||||
.{ current_widget.display_name, @errorName(err) },
|
||||
);
|
||||
};
|
||||
|
||||
for (layers) |layer| {
|
||||
for (layer) |widget| {
|
||||
try widget.update(context);
|
||||
widget.draw();
|
||||
|
||||
if (try widget.calculateTimeout(context)) |widget_timeout| {
|
||||
if (maybe_timeout == null or widget_timeout < maybe_timeout.?) maybe_timeout = widget_timeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We don't care about present errors here
|
||||
TerminalBuffer.presentBuffer() catch {};
|
||||
}
|
||||
|
||||
if (inactivity_event_fn) |inactivity_fn| {
|
||||
const time = try interop.getTimeOfDay();
|
||||
|
||||
if (!inactivity_cmd_ran and time.seconds - inactivity_time_start.seconds > inactivity_delay) {
|
||||
try @call(.auto, inactivity_fn, .{context});
|
||||
inactivity_cmd_ran = true;
|
||||
}
|
||||
}
|
||||
|
||||
const event_error = if (maybe_timeout) |timeout| termbox.tb_peek_event(&event, @intCast(timeout)) else termbox.tb_poll_event(&event);
|
||||
|
||||
self.update = maybe_timeout != null or event_error >= 0;
|
||||
|
||||
if (event_error < 0) continue;
|
||||
|
||||
// Input of some kind was detected, so reset the inactivity timer
|
||||
inactivity_time_start = try interop.getTimeOfDay();
|
||||
|
||||
if (event.type == termbox.TB_EVENT_RESIZE) {
|
||||
self.width = TerminalBuffer.getWidth();
|
||||
self.height = TerminalBuffer.getHeight();
|
||||
|
||||
try self.log_file.info(
|
||||
io,
|
||||
"tui",
|
||||
"screen resolution updated to {d}x{d}",
|
||||
.{ self.width, self.height },
|
||||
);
|
||||
|
||||
for (layers) |layer| {
|
||||
for (layer) |widget| {
|
||||
widget.realloc() catch |err| {
|
||||
shared_error.writeError(error.WidgetReallocationFailed);
|
||||
try self.log_file.err(
|
||||
io,
|
||||
"tui",
|
||||
"failed to reallocate widget '{s}': {s}",
|
||||
.{ widget.display_name, @errorName(err) },
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
try @call(.auto, position_widgets_fn, .{context});
|
||||
|
||||
self.update = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
var maybe_keys = try self.handleKeybind(allocator, event);
|
||||
if (maybe_keys) |*keys| {
|
||||
defer keys.deinit(allocator);
|
||||
|
||||
const current_widget = self.getActiveWidget();
|
||||
for (keys.items) |key| {
|
||||
current_widget.handle(key) catch |err| {
|
||||
shared_error.writeError(error.CurrentWidgetHandlingFailed);
|
||||
try self.log_file.err(
|
||||
io,
|
||||
"tui",
|
||||
"failed to handle active widget '{s}': {s}",
|
||||
.{ current_widget.display_name, @errorName(err) },
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
self.update = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stopEventLoop(self: *TerminalBuffer) void {
|
||||
self.run = false;
|
||||
}
|
||||
|
||||
pub fn drawNextFrame(self: *TerminalBuffer, value: bool) void {
|
||||
self.update = value;
|
||||
}
|
||||
|
||||
pub fn getActiveWidget(self: *TerminalBuffer) *Widget {
|
||||
return self.handlable_widgets.items[self.active_widget_index];
|
||||
}
|
||||
|
||||
pub fn setActiveWidget(self: *TerminalBuffer, widget: *Widget) void {
|
||||
for (self.handlable_widgets.items, 0..) |widg, i| {
|
||||
if (widg.id == widget.id) self.active_widget_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getWidth() usize {
|
||||
return @intCast(termbox.tb_width());
|
||||
}
|
||||
|
||||
pub fn getHeight() usize {
|
||||
return @intCast(termbox.tb_height());
|
||||
}
|
||||
|
||||
pub fn setCursor(x: usize, y: usize) !void {
|
||||
if (termbox.tb_set_cursor(@intCast(x), @intCast(y)) != 0) {
|
||||
return error.TermboxSetCursorFailed;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clearScreen(clear_back_buffer: bool) !void {
|
||||
if (termbox.tb_clear() != 0) return error.TermboxClearFailed;
|
||||
if (clear_back_buffer) try clearBackBuffer();
|
||||
}
|
||||
|
||||
pub fn shutdown() !void {
|
||||
if (termbox.tb_shutdown() != 0) return error.TermboxShutdownFailed;
|
||||
}
|
||||
|
||||
pub fn presentBuffer() !void {
|
||||
if (termbox.tb_present() != 0) return error.TermboxPresentFailed;
|
||||
}
|
||||
|
||||
pub fn getCell(x: usize, y: usize) ?Cell {
|
||||
var maybe_cell: ?*termbox.tb_cell = undefined;
|
||||
if (termbox.tb_get_cell(
|
||||
@intCast(x),
|
||||
@intCast(y),
|
||||
1,
|
||||
&maybe_cell,
|
||||
) != 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (maybe_cell) |cell| {
|
||||
return Cell.init(cell.ch, cell.fg, cell.bg);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn setCell(x: usize, y: usize, cell: Cell) !void {
|
||||
if (termbox.tb_set_cell(
|
||||
@intCast(x),
|
||||
@intCast(y),
|
||||
cell.ch,
|
||||
cell.fg,
|
||||
cell.bg,
|
||||
) != 0) {
|
||||
return error.TermboxSetCellFailed;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setCellBoundsChecked(self: *TerminalBuffer, x: isize, y: isize, cell: Cell) !void {
|
||||
if (0 <= x and x < self.width and 0 <= y and y < self.height) {
|
||||
try cell.put(@intCast(x), @intCast(y));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reclaim(self: TerminalBuffer) !void {
|
||||
if (self.termios) |termios| {
|
||||
// Take back control of the TTY
|
||||
const err = termbox.tb_init();
|
||||
if (err != 0 and err != termbox.TB_ERR_INIT_ALREADY) return error.TermboxReinitFailed;
|
||||
|
||||
if (self.full_color and termbox.tb_set_output_mode(termbox.TB_OUTPUT_TRUECOLOR) != 0) {
|
||||
return error.TermboxSetOutputModeFailed;
|
||||
}
|
||||
|
||||
try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, termios);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn registerKeybind(
|
||||
self: *TerminalBuffer,
|
||||
io: std.Io,
|
||||
keybinds: *KeybindMap,
|
||||
keybind: []const u8,
|
||||
callback: KeybindCallbackFn,
|
||||
context: *anyopaque,
|
||||
) !void {
|
||||
const key = try self.parseKeybind(io, keybind);
|
||||
|
||||
keybinds.put(key, .{
|
||||
.callback = callback,
|
||||
.context = context,
|
||||
}) catch |err| {
|
||||
try self.log_file.err(
|
||||
io,
|
||||
"tui",
|
||||
"failed to register keybind {s}: {s}",
|
||||
.{ keybind, @errorName(err) },
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
pub fn registerGlobalKeybind(
|
||||
self: *TerminalBuffer,
|
||||
io: std.Io,
|
||||
keybind: []const u8,
|
||||
callback: KeybindCallbackFn,
|
||||
context: *anyopaque,
|
||||
) !void {
|
||||
try self.registerKeybind(io, &self.keybinds, keybind, callback, context);
|
||||
}
|
||||
|
||||
pub fn simulateKeybind(self: *TerminalBuffer, io: std.Io, keybind: []const u8) !bool {
|
||||
const key = try self.parseKeybind(io, keybind);
|
||||
|
||||
if (self.keybinds.get(key)) |binding| {
|
||||
return try @call(
|
||||
.auto,
|
||||
binding.callback,
|
||||
.{binding.context},
|
||||
);
|
||||
}
|
||||
|
||||
const current_widget = self.getActiveWidget();
|
||||
if (current_widget.keybinds) |keybinds| {
|
||||
if (keybinds.get(key)) |binding| {
|
||||
return try @call(
|
||||
.auto,
|
||||
binding.callback,
|
||||
.{binding.context},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn drawText(
|
||||
text: []const u8,
|
||||
x: usize,
|
||||
y: usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
) !void {
|
||||
const utf8view = std.unicode.Utf8View.init(text) catch return;
|
||||
var utf8 = utf8view.iterator();
|
||||
|
||||
var i = x;
|
||||
while (utf8.nextCodepoint()) |codepoint| : (i += @intCast(termbox.tb_wcwidth(codepoint))) {
|
||||
const cell = Cell.init(codepoint, fg, bg);
|
||||
try cell.put(i, y);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drawConfinedText(
|
||||
text: []const u8,
|
||||
x: usize,
|
||||
y: usize,
|
||||
max_length: usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
) !void {
|
||||
const utf8view = std.unicode.Utf8View.init(text) catch return;
|
||||
var utf8 = utf8view.iterator();
|
||||
|
||||
var i = x;
|
||||
while (utf8.nextCodepoint()) |codepoint| : (i += @intCast(termbox.tb_wcwidth(codepoint))) {
|
||||
if (i - x >= max_length) break;
|
||||
|
||||
const cell = Cell.init(codepoint, fg, bg);
|
||||
try cell.put(i, y);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drawCharMultiple(
|
||||
char: u32,
|
||||
x: usize,
|
||||
y: usize,
|
||||
length: usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
) !void {
|
||||
const cell = Cell.init(char, fg, bg);
|
||||
for (0..length) |xx| try cell.put(x + xx, y);
|
||||
}
|
||||
|
||||
// Every codepoint is assumed to have a width of 1.
|
||||
// Since Ly is normally running in a TTY, this should be fine.
|
||||
pub fn strWidth(str: []const u8) usize {
|
||||
const utf8view = std.unicode.Utf8View.init(str) catch return str.len;
|
||||
var utf8 = utf8view.iterator();
|
||||
var length: c_int = 0;
|
||||
|
||||
while (utf8.nextCodepoint()) |codepoint| {
|
||||
length += termbox.tb_wcwidth(codepoint);
|
||||
}
|
||||
|
||||
return @intCast(length);
|
||||
}
|
||||
|
||||
fn clearBackBuffer() !void {
|
||||
if (termbox.global.initialized == 0) return;
|
||||
|
||||
// Clear the TTY because termbox2 doesn't seem to do it properly
|
||||
const capability = termbox.global.caps[termbox.TB_CAP_CLEAR_SCREEN];
|
||||
const capability_slice = std.mem.span(capability);
|
||||
const result = std.posix.system.write(termbox.global.ttyfd, capability_slice.ptr, capability_slice.len);
|
||||
|
||||
if (result != capability_slice.len) return error.PartialClearBackBuffer;
|
||||
if (result < 0) return error.ClearBackBufferFailed;
|
||||
}
|
||||
|
||||
fn parseKeybind(self: *TerminalBuffer, io: std.Io, keybind: []const u8) !keyboard.Key {
|
||||
var key = std.mem.zeroes(keyboard.Key);
|
||||
var iterator = std.mem.splitScalar(u8, keybind, '+');
|
||||
|
||||
while (iterator.next()) |item| {
|
||||
var found = false;
|
||||
|
||||
inline for (comptime std.meta.fieldNames(keyboard.Key)) |name| {
|
||||
if (std.ascii.eqlIgnoreCase(name, item)) {
|
||||
@field(key, name) = true;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
try self.log_file.err(
|
||||
io,
|
||||
"tui",
|
||||
"failed to parse key {s} of keybind {s}",
|
||||
.{ item, keybind },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
fn handleKeybind(
|
||||
self: *TerminalBuffer,
|
||||
allocator: Allocator,
|
||||
tb_event: termbox.tb_event,
|
||||
) !?std.ArrayList(keyboard.Key) {
|
||||
var keys = try keyboard.getKeyList(allocator, tb_event);
|
||||
|
||||
for (keys.items) |key| {
|
||||
if (self.keybinds.get(key)) |binding| {
|
||||
const passthrough_event = try @call(
|
||||
.auto,
|
||||
binding.callback,
|
||||
.{binding.context},
|
||||
);
|
||||
|
||||
if (!passthrough_event) {
|
||||
keys.deinit(allocator);
|
||||
return null;
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
const current_widget = self.getActiveWidget();
|
||||
if (current_widget.keybinds) |keybinds| {
|
||||
if (keybinds.get(key)) |binding| {
|
||||
const passthrough_event = try @call(
|
||||
.auto,
|
||||
binding.callback,
|
||||
.{binding.context},
|
||||
);
|
||||
|
||||
if (!passthrough_event) {
|
||||
keys.deinit(allocator);
|
||||
return null;
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
fn moveCursorUp(ptr: *anyopaque) !bool {
|
||||
var state: *TerminalBuffer = @ptrCast(@alignCast(ptr));
|
||||
if (state.active_widget_index == 0) return false;
|
||||
|
||||
state.active_widget_index -= 1;
|
||||
state.update = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn moveCursorDown(ptr: *anyopaque) !bool {
|
||||
var state: *TerminalBuffer = @ptrCast(@alignCast(ptr));
|
||||
if (state.active_widget_index == state.handlable_widgets.items.len - 1) return false;
|
||||
|
||||
state.active_widget_index += 1;
|
||||
state.update = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn wrapCursor(ptr: *anyopaque) !bool {
|
||||
var state: *TerminalBuffer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
state.active_widget_index = (state.active_widget_index + 1) % state.handlable_widgets.items.len;
|
||||
state.update = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn wrapCursorReverse(ptr: *anyopaque) !bool {
|
||||
var state: *TerminalBuffer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
state.active_widget_index = if (state.active_widget_index == 0) state.handlable_widgets.items.len - 1 else state.active_widget_index - 1;
|
||||
state.update = true;
|
||||
return false;
|
||||
}
|
||||
186
ly-ui/src/Widget.zig
Normal file
186
ly-ui/src/Widget.zig
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
const Widget = @This();
|
||||
|
||||
const keyboard = @import("keyboard.zig");
|
||||
const TerminalBuffer = @import("TerminalBuffer.zig");
|
||||
|
||||
const VTable = struct {
|
||||
deinit_fn: ?*const fn (ptr: *anyopaque) void,
|
||||
realloc_fn: ?*const fn (ptr: *anyopaque) anyerror!void,
|
||||
draw_fn: *const fn (ptr: *anyopaque) void,
|
||||
update_fn: ?*const fn (ptr: *anyopaque, ctx: *anyopaque) anyerror!void,
|
||||
handle_fn: ?*const fn (ptr: *anyopaque, maybe_key: ?keyboard.Key) anyerror!void,
|
||||
calculate_timeout_fn: ?*const fn (ptr: *anyopaque, ctx: *anyopaque) anyerror!?usize,
|
||||
};
|
||||
|
||||
pub var idCounter: u64 = 0;
|
||||
|
||||
id: u64,
|
||||
display_name: []const u8,
|
||||
keybinds: ?TerminalBuffer.KeybindMap,
|
||||
pointer: *anyopaque,
|
||||
vtable: VTable,
|
||||
|
||||
pub fn init(
|
||||
display_name: []const u8,
|
||||
keybinds: ?TerminalBuffer.KeybindMap,
|
||||
pointer: anytype,
|
||||
comptime deinit_fn: ?fn (ptr: @TypeOf(pointer)) void,
|
||||
comptime realloc_fn: ?fn (ptr: @TypeOf(pointer)) anyerror!void,
|
||||
comptime draw_fn: fn (ptr: @TypeOf(pointer)) void,
|
||||
comptime update_fn: ?fn (ptr: @TypeOf(pointer), ctx: *anyopaque) anyerror!void,
|
||||
comptime handle_fn: ?fn (ptr: @TypeOf(pointer), maybe_key: ?keyboard.Key) anyerror!void,
|
||||
comptime calculate_timeout_fn: ?fn (ptr: @TypeOf(pointer), ctx: *anyopaque) anyerror!?usize,
|
||||
) Widget {
|
||||
const Pointer = @TypeOf(pointer);
|
||||
const Impl = struct {
|
||||
pub fn deinitImpl(ptr: *anyopaque) void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
return @call(
|
||||
.always_inline,
|
||||
deinit_fn.?,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn reallocImpl(ptr: *anyopaque) !void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
return @call(
|
||||
.always_inline,
|
||||
realloc_fn.?,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn drawImpl(ptr: *anyopaque) void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
return @call(
|
||||
.always_inline,
|
||||
draw_fn,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn updateImpl(ptr: *anyopaque, ctx: *anyopaque) !void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
return @call(
|
||||
.always_inline,
|
||||
update_fn.?,
|
||||
.{ impl, ctx },
|
||||
);
|
||||
}
|
||||
|
||||
pub fn handleImpl(ptr: *anyopaque, maybe_key: ?keyboard.Key) !void {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
return @call(
|
||||
.always_inline,
|
||||
handle_fn.?,
|
||||
.{ impl, maybe_key },
|
||||
);
|
||||
}
|
||||
|
||||
pub fn calculateTimeoutImpl(ptr: *anyopaque, ctx: *anyopaque) !?usize {
|
||||
const impl: Pointer = @ptrCast(@alignCast(ptr));
|
||||
|
||||
return @call(
|
||||
.always_inline,
|
||||
calculate_timeout_fn.?,
|
||||
.{ impl, ctx },
|
||||
);
|
||||
}
|
||||
|
||||
const vtable = VTable{
|
||||
.deinit_fn = if (deinit_fn != null) deinitImpl else null,
|
||||
.realloc_fn = if (realloc_fn != null) reallocImpl else null,
|
||||
.draw_fn = drawImpl,
|
||||
.update_fn = if (update_fn != null) updateImpl else null,
|
||||
.handle_fn = if (handle_fn != null) handleImpl else null,
|
||||
.calculate_timeout_fn = if (calculate_timeout_fn != null) calculateTimeoutImpl else null,
|
||||
};
|
||||
};
|
||||
|
||||
idCounter += 1;
|
||||
return .{
|
||||
.id = idCounter,
|
||||
.display_name = display_name,
|
||||
.keybinds = keybinds,
|
||||
.pointer = pointer,
|
||||
.vtable = Impl.vtable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Widget) void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
if (self.vtable.deinit_fn) |deinit_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
deinit_fn,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn realloc(self: *Widget) !void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
if (self.vtable.realloc_fn) |realloc_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
realloc_fn,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(self: *Widget) void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
@call(
|
||||
.auto,
|
||||
self.vtable.draw_fn,
|
||||
.{impl},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn update(self: *Widget, ctx: *anyopaque) !void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
if (self.vtable.update_fn) |update_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
update_fn,
|
||||
.{ impl, ctx },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle(self: *Widget, maybe_key: ?keyboard.Key) !void {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
if (self.vtable.handle_fn) |handle_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
handle_fn,
|
||||
.{ impl, maybe_key },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calculateTimeout(self: *Widget, ctx: *anyopaque) !?usize {
|
||||
const impl: @TypeOf(self.pointer) = @ptrCast(@alignCast(self.pointer));
|
||||
|
||||
if (self.vtable.calculate_timeout_fn) |calculate_timeout_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
calculate_timeout_fn,
|
||||
.{ impl, ctx },
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
237
ly-ui/src/components/BigLabel.zig
Normal file
237
ly-ui/src/components/BigLabel.zig
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
const BigLabel = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const ly_core = @import("ly-core");
|
||||
const interop = ly_core.interop;
|
||||
|
||||
const en = @import("bigLabelLocales/en.zig");
|
||||
const fa = @import("bigLabelLocales/fa.zig");
|
||||
const Cell = @import("../Cell.zig");
|
||||
const Position = @import("../Position.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const Widget = @import("../Widget.zig");
|
||||
|
||||
pub const CHAR_WIDTH = 5;
|
||||
pub const CHAR_HEIGHT = 5;
|
||||
pub const CHAR_SIZE = CHAR_WIDTH * CHAR_HEIGHT;
|
||||
pub const X: u32 = if (ly_core.interop.supportsUnicode()) 0x2593 else '#';
|
||||
pub const O: u32 = 0;
|
||||
|
||||
// zig fmt: off
|
||||
pub const LocaleChars = struct {
|
||||
ZERO: [CHAR_SIZE]u21,
|
||||
ONE: [CHAR_SIZE]u21,
|
||||
TWO: [CHAR_SIZE]u21,
|
||||
THREE: [CHAR_SIZE]u21,
|
||||
FOUR: [CHAR_SIZE]u21,
|
||||
FIVE: [CHAR_SIZE]u21,
|
||||
SIX: [CHAR_SIZE]u21,
|
||||
SEVEN: [CHAR_SIZE]u21,
|
||||
EIGHT: [CHAR_SIZE]u21,
|
||||
NINE: [CHAR_SIZE]u21,
|
||||
S: [CHAR_SIZE]u21,
|
||||
E: [CHAR_SIZE]u21,
|
||||
P: [CHAR_SIZE]u21,
|
||||
A: [CHAR_SIZE]u21,
|
||||
M: [CHAR_SIZE]u21,
|
||||
};
|
||||
// zig fmt: on
|
||||
|
||||
pub const BigLabelLocale = enum {
|
||||
en,
|
||||
fa,
|
||||
};
|
||||
|
||||
instance: ?Widget = null,
|
||||
allocator: ?Allocator = null,
|
||||
buffer: *TerminalBuffer,
|
||||
text: []const u8,
|
||||
max_width: ?usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
locale: BigLabelLocale,
|
||||
update_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!void,
|
||||
calculate_timeout_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!?usize,
|
||||
component_pos: Position,
|
||||
children_pos: Position,
|
||||
|
||||
pub fn init(
|
||||
buffer: *TerminalBuffer,
|
||||
text: []const u8,
|
||||
max_width: ?usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
locale: BigLabelLocale,
|
||||
update_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!void,
|
||||
calculate_timeout_fn: ?*const fn (*BigLabel, *anyopaque) anyerror!?usize,
|
||||
) BigLabel {
|
||||
return .{
|
||||
.instance = null,
|
||||
.allocator = null,
|
||||
.buffer = buffer,
|
||||
.text = text,
|
||||
.max_width = max_width,
|
||||
.fg = fg,
|
||||
.bg = bg,
|
||||
.locale = locale,
|
||||
.update_fn = update_fn,
|
||||
.calculate_timeout_fn = calculate_timeout_fn,
|
||||
.component_pos = TerminalBuffer.START_POSITION,
|
||||
.children_pos = TerminalBuffer.START_POSITION,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *BigLabel) void {
|
||||
if (self.allocator) |allocator| allocator.free(self.text);
|
||||
}
|
||||
|
||||
pub fn widget(self: *BigLabel) *Widget {
|
||||
if (self.instance) |*instance| return instance;
|
||||
self.instance = Widget.init(
|
||||
"BigLabel",
|
||||
null,
|
||||
self,
|
||||
deinit,
|
||||
null,
|
||||
draw,
|
||||
update,
|
||||
null,
|
||||
calculateTimeout,
|
||||
);
|
||||
return &self.instance.?;
|
||||
}
|
||||
|
||||
pub fn setTextAlloc(
|
||||
self: *BigLabel,
|
||||
allocator: Allocator,
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) !void {
|
||||
self.text = try std.fmt.allocPrint(allocator, fmt, args);
|
||||
self.allocator = allocator;
|
||||
}
|
||||
|
||||
pub fn setTextBuf(
|
||||
self: *BigLabel,
|
||||
buffer: []u8,
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) !void {
|
||||
self.text = try std.fmt.bufPrint(buffer, fmt, args);
|
||||
self.allocator = null;
|
||||
}
|
||||
|
||||
pub fn setText(self: *BigLabel, text: []const u8) void {
|
||||
self.text = text;
|
||||
self.allocator = null;
|
||||
}
|
||||
|
||||
pub fn positionX(self: *BigLabel, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addX(TerminalBuffer.strWidth(self.text) * CHAR_WIDTH);
|
||||
}
|
||||
|
||||
pub fn positionY(self: *BigLabel, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addY(CHAR_HEIGHT);
|
||||
}
|
||||
|
||||
pub fn positionXY(self: *BigLabel, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = Position.init(
|
||||
TerminalBuffer.strWidth(self.text) * CHAR_WIDTH,
|
||||
CHAR_HEIGHT,
|
||||
).add(original_pos);
|
||||
}
|
||||
|
||||
pub fn childrenPosition(self: BigLabel) Position {
|
||||
return self.children_pos;
|
||||
}
|
||||
|
||||
fn draw(self: *BigLabel) void {
|
||||
for (self.text, 0..) |c, i| {
|
||||
const clock_cell = clockCell(
|
||||
c,
|
||||
self.fg,
|
||||
self.bg,
|
||||
self.locale,
|
||||
);
|
||||
|
||||
alphaBlit(
|
||||
self.component_pos.x + i * (CHAR_WIDTH + 1),
|
||||
self.component_pos.y,
|
||||
self.buffer.width,
|
||||
self.buffer.height,
|
||||
clock_cell,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn update(self: *BigLabel, context: *anyopaque) !void {
|
||||
if (self.update_fn) |update_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
update_fn,
|
||||
.{ self, context },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn calculateTimeout(self: *BigLabel, ctx: *anyopaque) !?usize {
|
||||
if (self.calculate_timeout_fn) |calculate_timeout_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
calculate_timeout_fn,
|
||||
.{ self, ctx },
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn clockCell(char: u8, fg: u32, bg: u32, locale: BigLabelLocale) [CHAR_SIZE]Cell {
|
||||
var cells: [CHAR_SIZE]Cell = undefined;
|
||||
|
||||
//@divTrunc(time.microseconds, 500000) != 0)
|
||||
const clock_chars = toBigNumber(char, locale);
|
||||
for (0..cells.len) |i| cells[i] = Cell.init(clock_chars[i], fg, bg);
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
fn alphaBlit(x: usize, y: usize, tb_width: usize, tb_height: usize, cells: [CHAR_SIZE]Cell) void {
|
||||
if (x + CHAR_WIDTH >= tb_width or y + CHAR_HEIGHT >= tb_height) return;
|
||||
|
||||
for (0..CHAR_HEIGHT) |yy| {
|
||||
for (0..CHAR_WIDTH) |xx| {
|
||||
const cell = cells[yy * CHAR_WIDTH + xx];
|
||||
cell.put(x + xx, y + yy) catch {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn toBigNumber(char: u8, locale: BigLabelLocale) [CHAR_SIZE]u21 {
|
||||
const locale_chars = switch (locale) {
|
||||
.fa => fa.locale_chars,
|
||||
.en => en.locale_chars,
|
||||
};
|
||||
return switch (char) {
|
||||
'0' => locale_chars.ZERO,
|
||||
'1' => locale_chars.ONE,
|
||||
'2' => locale_chars.TWO,
|
||||
'3' => locale_chars.THREE,
|
||||
'4' => locale_chars.FOUR,
|
||||
'5' => locale_chars.FIVE,
|
||||
'6' => locale_chars.SIX,
|
||||
'7' => locale_chars.SEVEN,
|
||||
'8' => locale_chars.EIGHT,
|
||||
'9' => locale_chars.NINE,
|
||||
'p', 'P' => locale_chars.P,
|
||||
'a', 'A' => locale_chars.A,
|
||||
'm', 'M' => locale_chars.M,
|
||||
':' => locale_chars.S,
|
||||
else => locale_chars.E,
|
||||
};
|
||||
}
|
||||
190
ly-ui/src/components/Box.zig
Normal file
190
ly-ui/src/components/Box.zig
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
const std = @import("std");
|
||||
|
||||
const Cell = @import("../Cell.zig");
|
||||
const Position = @import("../Position.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const Widget = @import("../Widget.zig");
|
||||
|
||||
const Box = @This();
|
||||
|
||||
instance: ?Widget = null,
|
||||
buffer: *TerminalBuffer,
|
||||
horizontal_margin: usize,
|
||||
vertical_margin: usize,
|
||||
width: usize,
|
||||
height: usize,
|
||||
show_borders: bool,
|
||||
blank_box: bool,
|
||||
top_title: ?[]const u8,
|
||||
bottom_title: ?[]const u8,
|
||||
border_fg: u32,
|
||||
title_fg: u32,
|
||||
bg: u32,
|
||||
update_fn: ?*const fn (*Box, *anyopaque) anyerror!void,
|
||||
left_pos: Position,
|
||||
right_pos: Position,
|
||||
children_pos: Position,
|
||||
|
||||
pub fn init(
|
||||
buffer: *TerminalBuffer,
|
||||
horizontal_margin: usize,
|
||||
vertical_margin: usize,
|
||||
width: usize,
|
||||
height: usize,
|
||||
show_borders: bool,
|
||||
blank_box: bool,
|
||||
top_title: ?[]const u8,
|
||||
bottom_title: ?[]const u8,
|
||||
border_fg: u32,
|
||||
title_fg: u32,
|
||||
bg: u32,
|
||||
update_fn: ?*const fn (*Box, *anyopaque) anyerror!void,
|
||||
) Box {
|
||||
return .{
|
||||
.instance = null,
|
||||
.buffer = buffer,
|
||||
.horizontal_margin = horizontal_margin,
|
||||
.vertical_margin = vertical_margin,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.show_borders = show_borders,
|
||||
.blank_box = blank_box,
|
||||
.top_title = top_title,
|
||||
.bottom_title = bottom_title,
|
||||
.border_fg = border_fg,
|
||||
.title_fg = title_fg,
|
||||
.bg = bg,
|
||||
.update_fn = update_fn,
|
||||
.left_pos = TerminalBuffer.START_POSITION,
|
||||
.right_pos = TerminalBuffer.START_POSITION,
|
||||
.children_pos = TerminalBuffer.START_POSITION,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn widget(self: *Box) *Widget {
|
||||
if (self.instance) |*instance| return instance;
|
||||
self.instance = Widget.init(
|
||||
"Box",
|
||||
null,
|
||||
self,
|
||||
null,
|
||||
null,
|
||||
draw,
|
||||
update,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
return &self.instance.?;
|
||||
}
|
||||
|
||||
pub fn positionXY(self: *Box, original_pos: Position) void {
|
||||
if (self.buffer.width < 2 or self.buffer.height < 2) return;
|
||||
|
||||
self.left_pos = original_pos;
|
||||
self.right_pos = Position.init(
|
||||
@min(self.buffer.width, self.width),
|
||||
@min(self.buffer.height, self.height),
|
||||
).add(self.left_pos);
|
||||
|
||||
self.children_pos = Position.init(
|
||||
self.horizontal_margin,
|
||||
self.vertical_margin,
|
||||
).add(self.left_pos);
|
||||
}
|
||||
|
||||
pub fn childrenPosition(self: Box) Position {
|
||||
return self.children_pos;
|
||||
}
|
||||
|
||||
fn draw(self: *Box) void {
|
||||
if (self.show_borders) {
|
||||
var left_up = Cell.init(
|
||||
self.buffer.box_chars.left_up,
|
||||
self.border_fg,
|
||||
self.bg,
|
||||
);
|
||||
var right_up = Cell.init(
|
||||
self.buffer.box_chars.right_up,
|
||||
self.border_fg,
|
||||
self.bg,
|
||||
);
|
||||
var left_down = Cell.init(
|
||||
self.buffer.box_chars.left_down,
|
||||
self.border_fg,
|
||||
self.bg,
|
||||
);
|
||||
var right_down = Cell.init(
|
||||
self.buffer.box_chars.right_down,
|
||||
self.border_fg,
|
||||
self.bg,
|
||||
);
|
||||
var top = Cell.init(
|
||||
self.buffer.box_chars.top,
|
||||
self.border_fg,
|
||||
self.bg,
|
||||
);
|
||||
var bottom = Cell.init(
|
||||
self.buffer.box_chars.bottom,
|
||||
self.border_fg,
|
||||
self.bg,
|
||||
);
|
||||
|
||||
left_up.put(self.left_pos.x - 1, self.left_pos.y - 1) catch {};
|
||||
right_up.put(self.right_pos.x, self.left_pos.y - 1) catch {};
|
||||
left_down.put(self.left_pos.x - 1, self.right_pos.y) catch {};
|
||||
right_down.put(self.right_pos.x, self.right_pos.y) catch {};
|
||||
|
||||
for (0..self.width) |i| {
|
||||
top.put(self.left_pos.x + i, self.left_pos.y - 1) catch {};
|
||||
bottom.put(self.left_pos.x + i, self.right_pos.y) catch {};
|
||||
}
|
||||
|
||||
top.ch = self.buffer.box_chars.left;
|
||||
bottom.ch = self.buffer.box_chars.right;
|
||||
|
||||
for (0..self.height) |i| {
|
||||
top.put(self.left_pos.x - 1, self.left_pos.y + i) catch {};
|
||||
bottom.put(self.right_pos.x, self.left_pos.y + i) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
if (self.blank_box) {
|
||||
for (0..self.height) |y| {
|
||||
for (0..self.width) |x| {
|
||||
self.buffer.blank_cell.put(self.left_pos.x + x, self.left_pos.y + y) catch {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self.top_title) |title| {
|
||||
TerminalBuffer.drawConfinedText(
|
||||
title,
|
||||
self.left_pos.x,
|
||||
self.left_pos.y - 1,
|
||||
self.width,
|
||||
self.title_fg,
|
||||
self.bg,
|
||||
) catch {};
|
||||
}
|
||||
|
||||
if (self.bottom_title) |title| {
|
||||
TerminalBuffer.drawConfinedText(
|
||||
title,
|
||||
self.left_pos.x,
|
||||
self.left_pos.y + self.height,
|
||||
self.width,
|
||||
self.title_fg,
|
||||
self.bg,
|
||||
) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
fn update(self: *Box, ctx: *anyopaque) !void {
|
||||
if (self.update_fn) |update_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
update_fn,
|
||||
.{ self, ctx },
|
||||
);
|
||||
}
|
||||
}
|
||||
153
ly-ui/src/components/Label.zig
Normal file
153
ly-ui/src/components/Label.zig
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
const Label = @This();
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Cell = @import("../Cell.zig");
|
||||
const Position = @import("../Position.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const Widget = @import("../Widget.zig");
|
||||
|
||||
instance: ?Widget,
|
||||
allocator: ?Allocator,
|
||||
text: []const u8,
|
||||
max_width: ?usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
update_fn: ?*const fn (*Label, *anyopaque) anyerror!void,
|
||||
calculate_timeout_fn: ?*const fn (*Label, *anyopaque) anyerror!?usize,
|
||||
component_pos: Position,
|
||||
children_pos: Position,
|
||||
|
||||
pub fn init(
|
||||
text: []const u8,
|
||||
max_width: ?usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
update_fn: ?*const fn (*Label, *anyopaque) anyerror!void,
|
||||
calculate_timeout_fn: ?*const fn (*Label, *anyopaque) anyerror!?usize,
|
||||
) Label {
|
||||
return .{
|
||||
.instance = null,
|
||||
.allocator = null,
|
||||
.text = text,
|
||||
.max_width = max_width,
|
||||
.fg = fg,
|
||||
.bg = bg,
|
||||
.update_fn = update_fn,
|
||||
.calculate_timeout_fn = calculate_timeout_fn,
|
||||
.component_pos = TerminalBuffer.START_POSITION,
|
||||
.children_pos = TerminalBuffer.START_POSITION,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Label) void {
|
||||
if (self.allocator) |allocator| allocator.free(self.text);
|
||||
}
|
||||
|
||||
pub fn widget(self: *Label) *Widget {
|
||||
if (self.instance) |*instance| return instance;
|
||||
self.instance = Widget.init(
|
||||
"Label",
|
||||
null,
|
||||
self,
|
||||
deinit,
|
||||
null,
|
||||
draw,
|
||||
update,
|
||||
null,
|
||||
calculateTimeout,
|
||||
);
|
||||
return &self.instance.?;
|
||||
}
|
||||
|
||||
pub fn setTextAlloc(
|
||||
self: *Label,
|
||||
allocator: Allocator,
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) !void {
|
||||
self.text = try std.fmt.allocPrint(allocator, fmt, args);
|
||||
self.allocator = allocator;
|
||||
}
|
||||
|
||||
pub fn setTextBuf(
|
||||
self: *Label,
|
||||
buffer: []u8,
|
||||
comptime fmt: []const u8,
|
||||
args: anytype,
|
||||
) !void {
|
||||
self.text = try std.fmt.bufPrint(buffer, fmt, args);
|
||||
self.allocator = null;
|
||||
}
|
||||
|
||||
pub fn setText(self: *Label, text: []const u8) void {
|
||||
self.text = text;
|
||||
self.allocator = null;
|
||||
}
|
||||
|
||||
pub fn positionX(self: *Label, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addX(TerminalBuffer.strWidth(self.text));
|
||||
}
|
||||
|
||||
pub fn positionY(self: *Label, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addY(1);
|
||||
}
|
||||
|
||||
pub fn positionXY(self: *Label, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = Position.init(
|
||||
TerminalBuffer.strWidth(self.text),
|
||||
1,
|
||||
).add(original_pos);
|
||||
}
|
||||
|
||||
pub fn childrenPosition(self: Label) Position {
|
||||
return self.children_pos;
|
||||
}
|
||||
|
||||
fn draw(self: *Label) void {
|
||||
if (self.max_width) |width| {
|
||||
TerminalBuffer.drawConfinedText(
|
||||
self.text,
|
||||
self.component_pos.x,
|
||||
self.component_pos.y,
|
||||
width,
|
||||
self.fg,
|
||||
self.bg,
|
||||
) catch {};
|
||||
return;
|
||||
}
|
||||
|
||||
TerminalBuffer.drawText(
|
||||
self.text,
|
||||
self.component_pos.x,
|
||||
self.component_pos.y,
|
||||
self.fg,
|
||||
self.bg,
|
||||
) catch {};
|
||||
}
|
||||
|
||||
fn update(self: *Label, ctx: *anyopaque) !void {
|
||||
if (self.update_fn) |update_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
update_fn,
|
||||
.{ self, ctx },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn calculateTimeout(self: *Label, ctx: *anyopaque) !?usize {
|
||||
if (self.calculate_timeout_fn) |calculate_timeout_fn| {
|
||||
return @call(
|
||||
.auto,
|
||||
calculate_timeout_fn,
|
||||
.{ self, ctx },
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
250
ly-ui/src/components/Text.zig
Normal file
250
ly-ui/src/components/Text.zig
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const keyboard = @import("../keyboard.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const Position = @import("../Position.zig");
|
||||
const Widget = @import("../Widget.zig");
|
||||
|
||||
const DynamicString = std.ArrayList(u8);
|
||||
|
||||
const Text = @This();
|
||||
|
||||
instance: ?Widget,
|
||||
allocator: Allocator,
|
||||
buffer: *TerminalBuffer,
|
||||
text: DynamicString,
|
||||
end: usize,
|
||||
cursor: usize,
|
||||
visible_start: usize,
|
||||
width: usize,
|
||||
component_pos: Position,
|
||||
children_pos: Position,
|
||||
should_insert: bool,
|
||||
masked: bool,
|
||||
maybe_mask: ?u32,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
keybinds: TerminalBuffer.KeybindMap,
|
||||
|
||||
pub fn init(
|
||||
allocator: Allocator,
|
||||
io: std.Io,
|
||||
buffer: *TerminalBuffer,
|
||||
should_insert: bool,
|
||||
masked: bool,
|
||||
maybe_mask: ?u32,
|
||||
width: usize,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
) !*Text {
|
||||
var self = try allocator.create(Text);
|
||||
self.* = Text{
|
||||
.instance = null,
|
||||
.allocator = allocator,
|
||||
.buffer = buffer,
|
||||
.text = .empty,
|
||||
.end = 0,
|
||||
.cursor = 0,
|
||||
.visible_start = 0,
|
||||
.width = width,
|
||||
.component_pos = TerminalBuffer.START_POSITION,
|
||||
.children_pos = TerminalBuffer.START_POSITION,
|
||||
.should_insert = should_insert,
|
||||
.masked = masked,
|
||||
.maybe_mask = maybe_mask,
|
||||
.fg = fg,
|
||||
.bg = bg,
|
||||
.keybinds = .init(allocator),
|
||||
};
|
||||
|
||||
try buffer.registerKeybind(io, &self.keybinds, "Left", &goLeft, self);
|
||||
try buffer.registerKeybind(io, &self.keybinds, "Right", &goRight, self);
|
||||
try buffer.registerKeybind(io, &self.keybinds, "Delete", &delete, self);
|
||||
try buffer.registerKeybind(io, &self.keybinds, "Backspace", &backspace, self);
|
||||
try buffer.registerKeybind(io, &self.keybinds, "Ctrl+U", &clearTextEntry, self);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Text) void {
|
||||
self.text.deinit(self.allocator);
|
||||
self.keybinds.deinit();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn widget(self: *Text) *Widget {
|
||||
if (self.instance) |*instance| return instance;
|
||||
self.instance = Widget.init(
|
||||
"Text",
|
||||
self.keybinds,
|
||||
self,
|
||||
deinit,
|
||||
null,
|
||||
draw,
|
||||
null,
|
||||
handle,
|
||||
null,
|
||||
);
|
||||
return &self.instance.?;
|
||||
}
|
||||
|
||||
pub fn positionX(self: *Text, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addX(self.width);
|
||||
}
|
||||
|
||||
pub fn positionY(self: *Text, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = original_pos.addY(1);
|
||||
}
|
||||
|
||||
pub fn positionXY(self: *Text, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.children_pos = Position.init(
|
||||
self.width,
|
||||
1,
|
||||
).add(original_pos);
|
||||
}
|
||||
|
||||
pub fn childrenPosition(self: Text) Position {
|
||||
return self.children_pos;
|
||||
}
|
||||
|
||||
pub fn clear(self: *Text) void {
|
||||
self.text.clearRetainingCapacity();
|
||||
self.end = 0;
|
||||
self.cursor = 0;
|
||||
self.visible_start = 0;
|
||||
}
|
||||
|
||||
pub fn toggleMask(self: *Text) void {
|
||||
self.masked = !self.masked;
|
||||
}
|
||||
|
||||
pub fn handle(self: *Text, maybe_key: ?keyboard.Key) !void {
|
||||
if (maybe_key) |key| {
|
||||
if (self.should_insert) {
|
||||
const maybe_character = key.getEnabledPrintableAscii();
|
||||
if (maybe_character) |character| try self.write(character);
|
||||
}
|
||||
}
|
||||
|
||||
if (self.masked and self.maybe_mask == null) {
|
||||
try TerminalBuffer.setCursor(
|
||||
self.component_pos.x,
|
||||
self.component_pos.y,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try TerminalBuffer.setCursor(
|
||||
self.component_pos.x + (self.cursor - self.visible_start),
|
||||
self.component_pos.y,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn writeText(self: *Text, str: []const u8) !void {
|
||||
for (str) |c| try self.write(c);
|
||||
}
|
||||
|
||||
fn draw(self: *Text) void {
|
||||
if (self.masked) {
|
||||
if (self.maybe_mask) |mask| {
|
||||
if (self.width < 1) return;
|
||||
|
||||
const length = @min(TerminalBuffer.strWidth(self.text.items), self.width - 1);
|
||||
if (length == 0) return;
|
||||
|
||||
TerminalBuffer.drawCharMultiple(
|
||||
mask,
|
||||
self.component_pos.x,
|
||||
self.component_pos.y,
|
||||
length,
|
||||
self.fg,
|
||||
self.bg,
|
||||
) catch {};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const str_length = TerminalBuffer.strWidth(self.text.items);
|
||||
const length = @min(str_length, self.width);
|
||||
if (length == 0) return;
|
||||
|
||||
const visible_slice = vs: {
|
||||
if (str_length > self.width and self.cursor < str_length) {
|
||||
break :vs self.text.items[self.visible_start..(self.width + self.visible_start)];
|
||||
} else {
|
||||
break :vs self.text.items[self.visible_start..];
|
||||
}
|
||||
};
|
||||
|
||||
TerminalBuffer.drawText(
|
||||
visible_slice,
|
||||
self.component_pos.x,
|
||||
self.component_pos.y,
|
||||
self.fg,
|
||||
self.bg,
|
||||
) catch {};
|
||||
}
|
||||
|
||||
fn goLeft(ptr: *anyopaque) !bool {
|
||||
var self: *Text = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (self.cursor == 0) return false;
|
||||
if (self.visible_start > 0) self.visible_start -= 1;
|
||||
|
||||
self.cursor -= 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn goRight(ptr: *anyopaque) !bool {
|
||||
var self: *Text = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (self.cursor >= self.end) return false;
|
||||
if (self.cursor - self.visible_start == self.width - 1) self.visible_start += 1;
|
||||
|
||||
self.cursor += 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn delete(ptr: *anyopaque) !bool {
|
||||
var self: *Text = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (self.cursor >= self.end or !self.should_insert) return false;
|
||||
|
||||
_ = self.text.orderedRemove(self.cursor);
|
||||
|
||||
self.end -= 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn backspace(ptr: *anyopaque) !bool {
|
||||
const self: *Text = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (self.cursor == 0 or !self.should_insert) return false;
|
||||
|
||||
_ = try goLeft(ptr);
|
||||
_ = try delete(ptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
fn write(self: *Text, char: u8) !void {
|
||||
if (char == 0) return;
|
||||
|
||||
try self.text.insert(self.allocator, self.cursor, char);
|
||||
|
||||
self.end += 1;
|
||||
_ = try goRight(self);
|
||||
}
|
||||
|
||||
fn clearTextEntry(ptr: *anyopaque) !bool {
|
||||
var self: *Text = @ptrCast(@alignCast(ptr));
|
||||
|
||||
if (!self.should_insert) return false;
|
||||
|
||||
self.clear();
|
||||
self.buffer.drawNextFrame(true);
|
||||
return false;
|
||||
}
|
||||
114
ly-ui/src/components/bigLabelLocales/en.zig
Normal file
114
ly-ui/src/components/bigLabelLocales/en.zig
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
const BigLabel = @import("../BigLabel.zig");
|
||||
const LocaleChars = BigLabel.LocaleChars;
|
||||
const X = BigLabel.X;
|
||||
const O = BigLabel.O;
|
||||
|
||||
// zig fmt: off
|
||||
pub const locale_chars = LocaleChars{
|
||||
.ZERO = [_]u21{
|
||||
X,X,X,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,X,X,X,
|
||||
},
|
||||
.ONE = [_]u21{
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
},
|
||||
.TWO = [_]u21{
|
||||
X,X,X,X,X,
|
||||
O,O,O,X,X,
|
||||
X,X,X,X,X,
|
||||
X,X,O,O,O,
|
||||
X,X,X,X,X,
|
||||
},
|
||||
.THREE = [_]u21{
|
||||
X,X,X,X,X,
|
||||
O,O,O,X,X,
|
||||
X,X,X,X,X,
|
||||
O,O,O,X,X,
|
||||
X,X,X,X,X,
|
||||
},
|
||||
.FOUR = [_]u21{
|
||||
X,X,O,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,X,X,X,
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
},
|
||||
.FIVE = [_]u21{
|
||||
X,X,X,X,X,
|
||||
X,X,O,O,O,
|
||||
X,X,X,X,X,
|
||||
O,O,O,X,X,
|
||||
X,X,X,X,X,
|
||||
},
|
||||
.SIX = [_]u21{
|
||||
X,X,X,X,X,
|
||||
X,X,O,O,O,
|
||||
X,X,X,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,X,X,X,
|
||||
},
|
||||
.SEVEN = [_]u21{
|
||||
X,X,X,X,X,
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
O,O,O,X,X,
|
||||
},
|
||||
.EIGHT = [_]u21{
|
||||
X,X,X,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,X,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,X,X,X,
|
||||
},
|
||||
.NINE = [_]u21{
|
||||
X,X,X,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,X,X,X,
|
||||
O,O,O,X,X,
|
||||
X,X,X,X,X,
|
||||
},
|
||||
.S = [_]u21{
|
||||
O,O,O,O,O,
|
||||
O,O,X,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,X,O,O,
|
||||
O,O,O,O,O,
|
||||
},
|
||||
.E = [_]u21{
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
},
|
||||
.P = [_]u21{
|
||||
X,X,X,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,X,X,X,
|
||||
X,X,O,O,O,
|
||||
X,X,O,O,O,
|
||||
},
|
||||
.A = [_]u21{
|
||||
X,X,X,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,X,X,X,
|
||||
X,X,O,X,X,
|
||||
X,X,O,X,X,
|
||||
},
|
||||
.M = [_]u21{
|
||||
X,X,X,X,X,
|
||||
X,O,X,O,X,
|
||||
X,O,X,O,X,
|
||||
X,O,O,O,X,
|
||||
X,O,O,O,X,
|
||||
},
|
||||
};
|
||||
// zig fmt: on
|
||||
114
ly-ui/src/components/bigLabelLocales/fa.zig
Normal file
114
ly-ui/src/components/bigLabelLocales/fa.zig
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
const BigLabel = @import("../BigLabel.zig");
|
||||
const LocaleChars = BigLabel.LocaleChars;
|
||||
const X = BigLabel.X;
|
||||
const O = BigLabel.O;
|
||||
|
||||
// zig fmt: off
|
||||
pub const locale_chars = LocaleChars{
|
||||
.ZERO = [_]u21{
|
||||
O,O,O,O,O,
|
||||
O,O,X,O,O,
|
||||
O,X,O,X,O,
|
||||
O,O,X,O,O,
|
||||
O,O,O,O,O,
|
||||
},
|
||||
.ONE = [_]u21{
|
||||
O,O,X,O,O,
|
||||
O,X,X,O,O,
|
||||
O,O,X,O,O,
|
||||
O,O,X,O,O,
|
||||
O,O,X,O,O,
|
||||
},
|
||||
.TWO = [_]u21{
|
||||
O,X,O,X,O,
|
||||
O,X,X,X,O,
|
||||
O,X,O,O,O,
|
||||
O,X,O,O,O,
|
||||
O,X,O,O,O,
|
||||
},
|
||||
.THREE = [_]u21{
|
||||
X,O,X,O,X,
|
||||
X,X,X,X,X,
|
||||
X,O,O,O,O,
|
||||
X,O,O,O,O,
|
||||
X,O,O,O,O,
|
||||
},
|
||||
.FOUR = [_]u21{
|
||||
O,X,O,X,X,
|
||||
O,X,X,O,O,
|
||||
O,X,X,X,X,
|
||||
O,X,O,O,O,
|
||||
O,X,O,O,O,
|
||||
},
|
||||
.FIVE = [_]u21{
|
||||
O,O,X,X,O,
|
||||
O,X,O,O,X,
|
||||
X,O,O,O,X,
|
||||
X,O,X,O,X,
|
||||
O,X,O,X,O,
|
||||
},
|
||||
.SIX = [_]u21{
|
||||
O,X,X,O,O,
|
||||
O,X,O,O,X,
|
||||
O,O,X,O,O,
|
||||
O,X,O,O,O,
|
||||
X,O,O,O,O,
|
||||
},
|
||||
.SEVEN = [_]u21{
|
||||
X,O,O,O,X,
|
||||
X,O,O,O,X,
|
||||
O,X,O,X,O,
|
||||
O,X,O,X,O,
|
||||
O,O,X,O,O,
|
||||
},
|
||||
.EIGHT = [_]u21{
|
||||
O,O,O,X,O,
|
||||
O,O,X,O,X,
|
||||
O,O,X,O,X,
|
||||
O,X,O,O,X,
|
||||
O,X,O,O,X,
|
||||
},
|
||||
.NINE = [_]u21{
|
||||
O,X,X,X,O,
|
||||
O,X,O,X,O,
|
||||
O,X,X,X,O,
|
||||
O,O,O,X,O,
|
||||
O,O,O,X,O,
|
||||
},
|
||||
.P = [_]u21{
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
},
|
||||
.A = [_]u21{
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
},
|
||||
.M = [_]u21{
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
},
|
||||
.S = [_]u21{
|
||||
O,O,O,O,O,
|
||||
O,O,X,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,X,O,O,
|
||||
O,O,O,O,O,
|
||||
},
|
||||
.E = [_]u21{
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
O,O,O,O,O,
|
||||
},
|
||||
};
|
||||
// zig fmt: on
|
||||
172
ly-ui/src/components/generic.zig
Normal file
172
ly-ui/src/components/generic.zig
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
const std = @import("std");
|
||||
|
||||
const Cell = @import("../Cell.zig");
|
||||
const keyboard = @import("../keyboard.zig");
|
||||
const TerminalBuffer = @import("../TerminalBuffer.zig");
|
||||
const Position = @import("../Position.zig");
|
||||
|
||||
pub fn CyclableLabel(comptime ItemType: type, comptime ChangeItemType: type) type {
|
||||
return struct {
|
||||
const Allocator = std.mem.Allocator;
|
||||
const ItemList = std.ArrayList(ItemType);
|
||||
const DrawItemFn = *const fn (*Self, ItemType, usize, usize, usize) void;
|
||||
const ChangeItemFn = *const fn (ItemType, ?ChangeItemType) void;
|
||||
|
||||
const Self = @This();
|
||||
|
||||
allocator: Allocator,
|
||||
buffer: *TerminalBuffer,
|
||||
list: ItemList,
|
||||
current: usize,
|
||||
width: usize,
|
||||
component_pos: Position,
|
||||
children_pos: Position,
|
||||
text_in_center: bool,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
cursor: usize,
|
||||
draw_item_fn: DrawItemFn,
|
||||
change_item_fn: ?ChangeItemFn,
|
||||
change_item_arg: ?ChangeItemType,
|
||||
keybinds: TerminalBuffer.KeybindMap,
|
||||
|
||||
pub fn init(
|
||||
allocator: Allocator,
|
||||
io: std.Io,
|
||||
buffer: *TerminalBuffer,
|
||||
draw_item_fn: DrawItemFn,
|
||||
change_item_fn: ?ChangeItemFn,
|
||||
change_item_arg: ?ChangeItemType,
|
||||
width: usize,
|
||||
text_in_center: bool,
|
||||
fg: u32,
|
||||
bg: u32,
|
||||
) !*Self {
|
||||
var self = try allocator.create(Self);
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.buffer = buffer,
|
||||
.list = .empty,
|
||||
.current = 0,
|
||||
.width = width,
|
||||
.component_pos = TerminalBuffer.START_POSITION,
|
||||
.children_pos = TerminalBuffer.START_POSITION,
|
||||
.text_in_center = text_in_center,
|
||||
.fg = fg,
|
||||
.bg = bg,
|
||||
.cursor = 0,
|
||||
.draw_item_fn = draw_item_fn,
|
||||
.change_item_fn = change_item_fn,
|
||||
.change_item_arg = change_item_arg,
|
||||
.keybinds = .init(allocator),
|
||||
};
|
||||
|
||||
try buffer.registerKeybind(io, &self.keybinds, "Left", &goLeft, self);
|
||||
try buffer.registerKeybind(io, &self.keybinds, "Ctrl+H", &goLeft, self);
|
||||
try buffer.registerKeybind(io, &self.keybinds, "Right", &goRight, self);
|
||||
try buffer.registerKeybind(io, &self.keybinds, "Ctrl+L", &goRight, self);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.list.deinit(self.allocator);
|
||||
self.keybinds.deinit();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn positionX(self: *Self, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.cursor = self.component_pos.x + 2;
|
||||
self.children_pos = original_pos.addX(self.width);
|
||||
}
|
||||
|
||||
pub fn positionY(self: *Self, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.cursor = self.component_pos.x + 2;
|
||||
self.children_pos = original_pos.addY(1);
|
||||
}
|
||||
|
||||
pub fn positionXY(self: *Self, original_pos: Position) void {
|
||||
self.component_pos = original_pos;
|
||||
self.cursor = self.component_pos.x + 2;
|
||||
self.children_pos = Position.init(
|
||||
self.width,
|
||||
1,
|
||||
).add(original_pos);
|
||||
}
|
||||
|
||||
pub fn childrenPosition(self: Self) Position {
|
||||
return self.children_pos;
|
||||
}
|
||||
|
||||
pub fn addItem(self: *Self, item: ItemType) !void {
|
||||
try self.list.append(self.allocator, item);
|
||||
self.current = self.list.items.len - 1;
|
||||
}
|
||||
|
||||
pub fn handle(self: *Self, _: ?keyboard.Key) !void {
|
||||
try TerminalBuffer.setCursor(
|
||||
self.component_pos.x + self.cursor + 2,
|
||||
self.component_pos.y,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn draw(self: *Self) void {
|
||||
if (self.list.items.len == 0) return;
|
||||
if (self.width < 2) return;
|
||||
|
||||
var left_arrow = Cell.init('<', self.fg, self.bg);
|
||||
var right_arrow = Cell.init('>', self.fg, self.bg);
|
||||
|
||||
left_arrow.put(self.component_pos.x, self.component_pos.y) catch {};
|
||||
right_arrow.put(
|
||||
self.component_pos.x + self.width - 1,
|
||||
self.component_pos.y,
|
||||
) catch {};
|
||||
|
||||
const current_item = self.list.items[self.current];
|
||||
const x = self.component_pos.x + 2;
|
||||
const y = self.component_pos.y;
|
||||
const width = self.width - 2;
|
||||
|
||||
@call(
|
||||
.auto,
|
||||
self.draw_item_fn,
|
||||
.{ self, current_item, x, y, width },
|
||||
);
|
||||
}
|
||||
|
||||
fn goLeft(ptr: *anyopaque) !bool {
|
||||
var self: *Self = @ptrCast(@alignCast(ptr));
|
||||
|
||||
self.current = if (self.current == 0) self.list.items.len - 1 else self.current - 1;
|
||||
|
||||
if (self.change_item_fn) |change_item_fn| {
|
||||
@call(
|
||||
.auto,
|
||||
change_item_fn,
|
||||
.{ self.list.items[self.current], self.change_item_arg },
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fn goRight(ptr: *anyopaque) !bool {
|
||||
var self: *Self = @ptrCast(@alignCast(ptr));
|
||||
|
||||
self.current = if (self.current == self.list.items.len - 1) 0 else self.current + 1;
|
||||
|
||||
if (self.change_item_fn) |change_item_fn| {
|
||||
@call(
|
||||
.auto,
|
||||
change_item_fn,
|
||||
.{ self.list.items[self.current], self.change_item_arg },
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
717
ly-ui/src/keyboard.zig
Normal file
717
ly-ui/src/keyboard.zig
Normal file
|
|
@ -0,0 +1,717 @@
|
|||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const KeyList = std.ArrayList(Key);
|
||||
|
||||
const TerminalBuffer = @import("TerminalBuffer.zig");
|
||||
const termbox = TerminalBuffer.termbox;
|
||||
|
||||
pub const Key = packed struct {
|
||||
ctrl: bool,
|
||||
shift: bool,
|
||||
alt: bool,
|
||||
|
||||
f1: bool,
|
||||
f2: bool,
|
||||
f3: bool,
|
||||
f4: bool,
|
||||
f5: bool,
|
||||
f6: bool,
|
||||
f7: bool,
|
||||
f8: bool,
|
||||
f9: bool,
|
||||
f10: bool,
|
||||
f11: bool,
|
||||
f12: bool,
|
||||
|
||||
insert: bool,
|
||||
delete: bool,
|
||||
home: bool,
|
||||
end: bool,
|
||||
pageup: bool,
|
||||
pagedown: bool,
|
||||
up: bool,
|
||||
down: bool,
|
||||
left: bool,
|
||||
right: bool,
|
||||
tab: bool,
|
||||
backspace: bool,
|
||||
enter: bool,
|
||||
|
||||
@" ": bool,
|
||||
@"!": bool,
|
||||
@"`": bool,
|
||||
esc: bool,
|
||||
@"[": bool,
|
||||
@"\\": bool,
|
||||
@"]": bool,
|
||||
@"/": bool,
|
||||
_: bool,
|
||||
@"'": bool,
|
||||
@"\"": bool,
|
||||
@",": bool,
|
||||
@"-": bool,
|
||||
@".": bool,
|
||||
@"#": bool,
|
||||
@"$": bool,
|
||||
@"%": bool,
|
||||
@"&": bool,
|
||||
@"*": bool,
|
||||
@"(": bool,
|
||||
@")": bool,
|
||||
@"+": bool,
|
||||
@"=": bool,
|
||||
@":": bool,
|
||||
@";": bool,
|
||||
@"<": bool,
|
||||
@">": bool,
|
||||
@"?": bool,
|
||||
@"@": bool,
|
||||
@"^": bool,
|
||||
@"~": bool,
|
||||
@"{": bool,
|
||||
@"}": bool,
|
||||
@"|": bool,
|
||||
|
||||
@"0": bool,
|
||||
@"1": bool,
|
||||
@"2": bool,
|
||||
@"3": bool,
|
||||
@"4": bool,
|
||||
@"5": bool,
|
||||
@"6": bool,
|
||||
@"7": bool,
|
||||
@"8": bool,
|
||||
@"9": bool,
|
||||
|
||||
a: bool,
|
||||
b: bool,
|
||||
c: bool,
|
||||
d: bool,
|
||||
e: bool,
|
||||
f: bool,
|
||||
g: bool,
|
||||
h: bool,
|
||||
i: bool,
|
||||
j: bool,
|
||||
k: bool,
|
||||
l: bool,
|
||||
m: bool,
|
||||
n: bool,
|
||||
o: bool,
|
||||
p: bool,
|
||||
q: bool,
|
||||
r: bool,
|
||||
s: bool,
|
||||
t: bool,
|
||||
u: bool,
|
||||
v: bool,
|
||||
w: bool,
|
||||
x: bool,
|
||||
y: bool,
|
||||
z: bool,
|
||||
|
||||
pub fn getEnabledPrintableAscii(self: Key) ?u8 {
|
||||
if (self.ctrl or self.alt) return null;
|
||||
|
||||
inline for (comptime std.meta.fieldNames(Key)) |name| {
|
||||
if (name.len == 1 and std.ascii.isPrint(name[0]) and @field(self, name)) {
|
||||
if (self.shift) {
|
||||
if (!std.ascii.isAlphanumeric(name[0])) return null;
|
||||
return std.ascii.toUpper(name[0]);
|
||||
}
|
||||
|
||||
return name[0];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn getKeyList(allocator: Allocator, tb_event: termbox.tb_event) !KeyList {
|
||||
var keys: KeyList = .empty;
|
||||
var key = std.mem.zeroes(Key);
|
||||
|
||||
if (tb_event.mod & termbox.TB_MOD_CTRL != 0) key.ctrl = true;
|
||||
if (tb_event.mod & termbox.TB_MOD_SHIFT != 0) key.shift = true;
|
||||
if (tb_event.mod & termbox.TB_MOD_ALT != 0) key.alt = true;
|
||||
|
||||
if (tb_event.key == termbox.TB_KEY_BACK_TAB) {
|
||||
key.shift = true;
|
||||
key.tab = true;
|
||||
} else if (tb_event.key > termbox.TB_KEY_BACK_TAB) {
|
||||
const code = 0xFFFF - tb_event.key;
|
||||
|
||||
switch (code) {
|
||||
0 => key.f1 = true,
|
||||
1 => key.f2 = true,
|
||||
2 => key.f3 = true,
|
||||
3 => key.f4 = true,
|
||||
4 => key.f5 = true,
|
||||
5 => key.f6 = true,
|
||||
6 => key.f7 = true,
|
||||
7 => key.f8 = true,
|
||||
8 => key.f9 = true,
|
||||
9 => key.f10 = true,
|
||||
10 => key.f11 = true,
|
||||
11 => key.f12 = true,
|
||||
12 => key.insert = true,
|
||||
13 => key.delete = true,
|
||||
14 => key.home = true,
|
||||
15 => key.end = true,
|
||||
16 => key.pageup = true,
|
||||
17 => key.pagedown = true,
|
||||
18 => key.up = true,
|
||||
19 => key.down = true,
|
||||
20 => key.left = true,
|
||||
21 => key.right = true,
|
||||
else => {},
|
||||
}
|
||||
|
||||
if (code >= 18 and code <= 21) {
|
||||
// https://github.com/termbox/termbox2/blob/605398fa79108412976191e062ea14bd4bd30213/termbox2.h#L446
|
||||
key.ctrl = false;
|
||||
key.shift = false;
|
||||
}
|
||||
} else if (tb_event.ch < 128) {
|
||||
const code = if (tb_event.ch == 0 and tb_event.key < 128) tb_event.key else tb_event.ch;
|
||||
|
||||
switch (code) {
|
||||
// Non-standard control codes
|
||||
0 => {
|
||||
key.ctrl = true;
|
||||
key.@"2" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"`" = true;
|
||||
},
|
||||
1 => {
|
||||
key.ctrl = true;
|
||||
key.a = true;
|
||||
},
|
||||
2 => {
|
||||
key.ctrl = true;
|
||||
key.b = true;
|
||||
},
|
||||
3 => {
|
||||
key.ctrl = true;
|
||||
key.c = true;
|
||||
},
|
||||
4 => {
|
||||
key.ctrl = true;
|
||||
key.d = true;
|
||||
},
|
||||
5 => {
|
||||
key.ctrl = true;
|
||||
key.e = true;
|
||||
},
|
||||
6 => {
|
||||
key.ctrl = true;
|
||||
key.f = true;
|
||||
},
|
||||
7 => {
|
||||
key.ctrl = true;
|
||||
key.g = true;
|
||||
},
|
||||
8 => {
|
||||
key.ctrl = true;
|
||||
key.h = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.backspace = true;
|
||||
},
|
||||
9 => {
|
||||
key.ctrl = true;
|
||||
key.i = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.tab = true;
|
||||
},
|
||||
10 => {
|
||||
key.ctrl = true;
|
||||
key.j = true;
|
||||
},
|
||||
11 => {
|
||||
key.ctrl = true;
|
||||
key.k = true;
|
||||
},
|
||||
12 => {
|
||||
key.ctrl = true;
|
||||
key.l = true;
|
||||
},
|
||||
13 => {
|
||||
key.ctrl = true;
|
||||
key.m = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.enter = true;
|
||||
},
|
||||
14 => {
|
||||
key.ctrl = true;
|
||||
key.n = true;
|
||||
},
|
||||
15 => {
|
||||
key.ctrl = true;
|
||||
key.o = true;
|
||||
},
|
||||
16 => {
|
||||
key.ctrl = true;
|
||||
key.p = true;
|
||||
},
|
||||
17 => {
|
||||
key.ctrl = true;
|
||||
key.q = true;
|
||||
},
|
||||
18 => {
|
||||
key.ctrl = true;
|
||||
key.r = true;
|
||||
},
|
||||
19 => {
|
||||
key.ctrl = true;
|
||||
key.s = true;
|
||||
},
|
||||
20 => {
|
||||
key.ctrl = true;
|
||||
key.t = true;
|
||||
},
|
||||
21 => {
|
||||
key.ctrl = true;
|
||||
key.u = true;
|
||||
},
|
||||
22 => {
|
||||
key.ctrl = true;
|
||||
key.v = true;
|
||||
},
|
||||
23 => {
|
||||
key.ctrl = true;
|
||||
key.w = true;
|
||||
},
|
||||
24 => {
|
||||
key.ctrl = true;
|
||||
key.x = true;
|
||||
},
|
||||
25 => {
|
||||
key.ctrl = true;
|
||||
key.y = true;
|
||||
},
|
||||
26 => {
|
||||
key.ctrl = true;
|
||||
key.z = true;
|
||||
},
|
||||
27 => {
|
||||
key.ctrl = true;
|
||||
key.@"3" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.esc = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"[" = true;
|
||||
},
|
||||
28 => {
|
||||
key.ctrl = true;
|
||||
key.@"4" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"\\" = true;
|
||||
},
|
||||
29 => {
|
||||
key.ctrl = true;
|
||||
key.@"5" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"]" = true;
|
||||
},
|
||||
30 => {
|
||||
key.ctrl = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"6" = true;
|
||||
},
|
||||
31 => {
|
||||
key.ctrl = true;
|
||||
key.@"7" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"/" = true;
|
||||
try keys.append(allocator, key);
|
||||
|
||||
key = std.mem.zeroes(Key);
|
||||
key._ = true;
|
||||
},
|
||||
// Standard ASCII characters
|
||||
32 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@" " = true;
|
||||
},
|
||||
33 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"!" = true;
|
||||
},
|
||||
34 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"\"" = true;
|
||||
},
|
||||
35 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"#" = true;
|
||||
},
|
||||
36 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"$" = true;
|
||||
},
|
||||
37 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"%" = true;
|
||||
},
|
||||
38 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"&" = true;
|
||||
},
|
||||
39 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"'" = true;
|
||||
},
|
||||
40 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"(" = true;
|
||||
},
|
||||
41 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@")" = true;
|
||||
},
|
||||
42 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"*" = true;
|
||||
},
|
||||
43 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"+" = true;
|
||||
},
|
||||
44 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"," = true;
|
||||
},
|
||||
45 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"-" = true;
|
||||
},
|
||||
46 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"." = true;
|
||||
},
|
||||
47 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"/" = true;
|
||||
},
|
||||
48 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"0" = true;
|
||||
},
|
||||
49 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"1" = true;
|
||||
},
|
||||
50 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"2" = true;
|
||||
},
|
||||
51 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"3" = true;
|
||||
},
|
||||
52 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"4" = true;
|
||||
},
|
||||
53 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"5" = true;
|
||||
},
|
||||
54 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"6" = true;
|
||||
},
|
||||
55 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"7" = true;
|
||||
},
|
||||
56 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"8" = true;
|
||||
},
|
||||
57 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"9" = true;
|
||||
},
|
||||
58 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@":" = true;
|
||||
},
|
||||
59 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@";" = true;
|
||||
},
|
||||
60 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"<" = true;
|
||||
},
|
||||
61 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"=" = true;
|
||||
},
|
||||
62 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@">" = true;
|
||||
},
|
||||
63 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"?" = true;
|
||||
},
|
||||
64 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"@" = true;
|
||||
},
|
||||
65 => {
|
||||
key.shift = true;
|
||||
key.a = true;
|
||||
},
|
||||
66 => {
|
||||
key.shift = true;
|
||||
key.b = true;
|
||||
},
|
||||
67 => {
|
||||
key.shift = true;
|
||||
key.c = true;
|
||||
},
|
||||
68 => {
|
||||
key.shift = true;
|
||||
key.d = true;
|
||||
},
|
||||
69 => {
|
||||
key.shift = true;
|
||||
key.e = true;
|
||||
},
|
||||
70 => {
|
||||
key.shift = true;
|
||||
key.f = true;
|
||||
},
|
||||
71 => {
|
||||
key.shift = true;
|
||||
key.g = true;
|
||||
},
|
||||
72 => {
|
||||
key.shift = true;
|
||||
key.h = true;
|
||||
},
|
||||
73 => {
|
||||
key.shift = true;
|
||||
key.i = true;
|
||||
},
|
||||
74 => {
|
||||
key.shift = true;
|
||||
key.j = true;
|
||||
},
|
||||
75 => {
|
||||
key.shift = true;
|
||||
key.k = true;
|
||||
},
|
||||
76 => {
|
||||
key.shift = true;
|
||||
key.l = true;
|
||||
},
|
||||
77 => {
|
||||
key.shift = true;
|
||||
key.m = true;
|
||||
},
|
||||
78 => {
|
||||
key.shift = true;
|
||||
key.n = true;
|
||||
},
|
||||
79 => {
|
||||
key.shift = true;
|
||||
key.o = true;
|
||||
},
|
||||
80 => {
|
||||
key.shift = true;
|
||||
key.p = true;
|
||||
},
|
||||
81 => {
|
||||
key.shift = true;
|
||||
key.q = true;
|
||||
},
|
||||
82 => {
|
||||
key.shift = true;
|
||||
key.r = true;
|
||||
},
|
||||
83 => {
|
||||
key.shift = true;
|
||||
key.s = true;
|
||||
},
|
||||
84 => {
|
||||
key.shift = true;
|
||||
key.t = true;
|
||||
},
|
||||
85 => {
|
||||
key.shift = true;
|
||||
key.u = true;
|
||||
},
|
||||
86 => {
|
||||
key.shift = true;
|
||||
key.v = true;
|
||||
},
|
||||
87 => {
|
||||
key.shift = true;
|
||||
key.w = true;
|
||||
},
|
||||
88 => {
|
||||
key.shift = true;
|
||||
key.x = true;
|
||||
},
|
||||
89 => {
|
||||
key.shift = true;
|
||||
key.y = true;
|
||||
},
|
||||
90 => {
|
||||
key.shift = true;
|
||||
key.z = true;
|
||||
},
|
||||
91 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"[" = true;
|
||||
},
|
||||
92 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"\\" = true;
|
||||
},
|
||||
93 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"]" = true;
|
||||
},
|
||||
94 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"^" = true;
|
||||
},
|
||||
95 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key._ = true;
|
||||
},
|
||||
96 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"`" = true;
|
||||
},
|
||||
97 => {
|
||||
key.a = true;
|
||||
},
|
||||
98 => {
|
||||
key.b = true;
|
||||
},
|
||||
99 => {
|
||||
key.c = true;
|
||||
},
|
||||
100 => {
|
||||
key.d = true;
|
||||
},
|
||||
101 => {
|
||||
key.e = true;
|
||||
},
|
||||
102 => {
|
||||
key.f = true;
|
||||
},
|
||||
103 => {
|
||||
key.g = true;
|
||||
},
|
||||
104 => {
|
||||
key.h = true;
|
||||
},
|
||||
105 => {
|
||||
key.i = true;
|
||||
},
|
||||
106 => {
|
||||
key.j = true;
|
||||
},
|
||||
107 => {
|
||||
key.k = true;
|
||||
},
|
||||
108 => {
|
||||
key.l = true;
|
||||
},
|
||||
109 => {
|
||||
key.m = true;
|
||||
},
|
||||
110 => {
|
||||
key.n = true;
|
||||
},
|
||||
111 => {
|
||||
key.o = true;
|
||||
},
|
||||
112 => {
|
||||
key.p = true;
|
||||
},
|
||||
113 => {
|
||||
key.q = true;
|
||||
},
|
||||
114 => {
|
||||
key.r = true;
|
||||
},
|
||||
115 => {
|
||||
key.s = true;
|
||||
},
|
||||
116 => {
|
||||
key.t = true;
|
||||
},
|
||||
117 => {
|
||||
key.u = true;
|
||||
},
|
||||
118 => {
|
||||
key.v = true;
|
||||
},
|
||||
119 => {
|
||||
key.w = true;
|
||||
},
|
||||
120 => {
|
||||
key.x = true;
|
||||
},
|
||||
121 => {
|
||||
key.y = true;
|
||||
},
|
||||
122 => {
|
||||
key.z = true;
|
||||
},
|
||||
123 => {
|
||||
key.@"{" = true;
|
||||
},
|
||||
124 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"|" = true;
|
||||
},
|
||||
125 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"}" = true;
|
||||
},
|
||||
126 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.@"~" = true;
|
||||
},
|
||||
127 => {
|
||||
key = std.mem.zeroes(Key);
|
||||
key.backspace = true;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
try keys.append(allocator, key);
|
||||
|
||||
return keys;
|
||||
}
|
||||
13
ly-ui/src/root.zig
Normal file
13
ly-ui/src/root.zig
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
pub const ly_core = @import("ly-core");
|
||||
|
||||
pub const Cell = @import("Cell.zig");
|
||||
pub const keyboard = @import("keyboard.zig");
|
||||
pub const Position = @import("Position.zig");
|
||||
pub const TerminalBuffer = @import("TerminalBuffer.zig");
|
||||
pub const Widget = @import("Widget.zig");
|
||||
|
||||
pub const BigLabel = @import("components/BigLabel.zig");
|
||||
pub const Box = @import("components/Box.zig");
|
||||
pub const CyclableLabel = @import("components/generic.zig").CyclableLabel;
|
||||
pub const Label = @import("components/Label.zig");
|
||||
pub const Text = @import("components/Text.zig");
|
||||
347
readme.md
347
readme.md
|
|
@ -1,207 +1,294 @@
|
|||
# The Ly display manager
|
||||
|
||||
# Ly - a TUI display manager
|
||||

|
||||

|
||||
|
||||
Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD.
|
||||
_Note: the above animation can be found [here](https://codeberg.org/fairyglade/ly-community/src/branch/main/animations/dur/blackhole-smooth-240x67.dur)!_
|
||||
|
||||
Ly is a lightweight TUI (ncurses-like) display manager for Linux and BSD, designed with portability in mind and doesn't require systemd to run.
|
||||
|
||||
Join us on Matrix over at [#ly-dm:matrix.org](https://matrix.to/#/#ly-dm:matrix.org)!
|
||||
|
||||
> [!NOTE]
|
||||
> Development happens on [Codeberg](https://codeberg.org/fairyglade/ly) with a mirror on [GitHub](https://github.com/fairyglade/ly).
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Compile-time:
|
||||
- zig 0.12.0
|
||||
- a C standard library
|
||||
- zig 0.16.x (you must use a **release version** of zig; check that `zig version` does not have a `-dev*` suffix)
|
||||
|
||||
- libc
|
||||
|
||||
- pam
|
||||
- xcb
|
||||
|
||||
- xcb (optional, required by default; needed for X11 support)
|
||||
|
||||
- Runtime (with default config):
|
||||
- xorg
|
||||
|
||||
- xorg-xauth
|
||||
- mcookie
|
||||
- tput
|
||||
|
||||
- shutdown
|
||||
|
||||
- brightnessctl
|
||||
|
||||
### Debian
|
||||
|
||||
```
|
||||
# apt install build-essential libpam0g-dev libxcb-xkb-dev
|
||||
# apt install build-essential libpam0g-dev libxcb-xkb-dev xauth xserver-xorg brightnessctl
|
||||
```
|
||||
|
||||
### Fedora
|
||||
**Warning**: You may encounter issues with SELinux on Fedora.
|
||||
It is recommended to add a rule for Ly as it currently does not ship one.
|
||||
|
||||
```
|
||||
# dnf install kernel-devel pam-devel libxcb-devel
|
||||
# dnf install kernel-devel pam-devel libxcb-devel zig xorg-x11-xauth xorg-x11-server brightnessctl
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> Distributions using SELinux such as Fedora and openSUSE Tumbleweed may encounter issues. If you encounter such issues, such as session launch failures, proceed with the following steps:
|
||||
>
|
||||
> ```
|
||||
> # ausearch -m avc -ts recent
|
||||
> ```
|
||||
>
|
||||
> If SELinux is denying process context transition, you will see this output:
|
||||
>
|
||||
> ```
|
||||
> denied { transition } for pid=XXXX comm="ly" path="/usr/bin/bash"
|
||||
> scontext=system_u:system_r:unconfined_service_t:s0
|
||||
> tcontext=unconfined_u:unconfined_r:unconfined_t:s0
|
||||
> tclass=process permissive=0
|
||||
> ```
|
||||
>
|
||||
> Pipe this output into `audit2allow` to generate a security module package. This will persist regardless of changes to filesystem permissions:
|
||||
>
|
||||
> ```
|
||||
> # ausearch -m avc -ts recent | audit2allow -M ly-local
|
||||
> ```
|
||||
>
|
||||
> ```
|
||||
> # semodule -i ly-local.pp
|
||||
> ```
|
||||
>
|
||||
> _This fix has been confirmed on Fedora 39 and 44 and openSUSE Tumbleweed. (#494)_
|
||||
|
||||
### FreeBSD
|
||||
|
||||
```
|
||||
# pkg install ca_root_nss libxcb git xorg xauth
|
||||
```
|
||||
|
||||
## Availability
|
||||
|
||||
[](https://repology.org/project/ly-display-manager/versions)
|
||||
|
||||
## Custom animations & user scripts
|
||||
|
||||
If you want to pimp your Ly installation even further than what the traditional configuration file allows you to, a [community repository](https://codeberg.org/fairyglade/ly-community) exists containing custom animations & scripts, so go check it out!
|
||||
|
||||
## Support
|
||||
The following desktop environments were tested with success:
|
||||
- awesome
|
||||
- bspwm
|
||||
- budgie
|
||||
- cinnamon
|
||||
- deepin
|
||||
- dwl
|
||||
- dwm
|
||||
- enlightenment
|
||||
- gnome
|
||||
- i3
|
||||
- kde
|
||||
- labwc
|
||||
- lxde
|
||||
- lxqt
|
||||
- mate
|
||||
- maxx
|
||||
- pantheon
|
||||
- qtile
|
||||
- spectrwm
|
||||
- sway
|
||||
- windowmaker
|
||||
- xfce
|
||||
- xmonad
|
||||
|
||||
Ly should work with any X desktop environment, and provides
|
||||
basic wayland support (sway works very well, for example).
|
||||
Every environment that works on other login managers also should work on Ly.
|
||||
|
||||
## systemd?
|
||||
Unlike what you may have heard, Ly does not require `systemd`,
|
||||
and was even specifically designed not to depend on `logind`.
|
||||
You should be able to make it work easily with a better init,
|
||||
changing the source code won't be necessary :)
|
||||
- Unlike most login managers Ly has an xinitrc and shell entry.
|
||||
|
||||
- If you installed your favorite environment and you don't see it, that's because Ly doesn't automatically refresh itself. To fix this you should restart Ly service (depends on your init system) or the easy way is to reboot your system.
|
||||
|
||||
- If your environment is still missing then check at `/usr/share/xsessions` or `/usr/share/wayland-sessions` to see if a .desktop file is present.
|
||||
|
||||
- If there isn't a .desktop file then create a new one at `/etc/ly/custom-sessions` that launches your favorite environment. These .desktop files can be only seen by Ly and if you want them system-wide you also can create at those directories instead.
|
||||
|
||||
- If Xorg sessions don't work then check if your distro compiles Ly with Xorg.
|
||||
|
||||
Logs are defined by `/etc/ly/config.ini`:
|
||||
|
||||
- The session log is located at `~/.local/state/ly-session.log` by default.
|
||||
|
||||
- The system log is located at `/var/log/ly.log` by default.
|
||||
|
||||
## Manually building
|
||||
|
||||
The procedure for manually building Ly is pretty standard:
|
||||
|
||||
## Cloning and Compiling
|
||||
Clone the repository
|
||||
```
|
||||
$ git clone https://github.com/fairyglade/ly
|
||||
```
|
||||
|
||||
Change the directory to ly
|
||||
```
|
||||
$ git clone https://codeberg.org/fairyglade/ly.git
|
||||
$ cd ly
|
||||
```
|
||||
|
||||
Compile
|
||||
```
|
||||
$ zig build
|
||||
```
|
||||
|
||||
Test in the configured tty (tty2 by default)
|
||||
or a terminal emulator (but desktop environments won't start)
|
||||
After building, you can (optionally) test Ly in a terminal emulator, although authentication will **not** work:
|
||||
|
||||
```
|
||||
# zig build run
|
||||
$ zig build run
|
||||
```
|
||||
|
||||
Install Ly and the provided systemd service file
|
||||
> [!IMPORTANT]
|
||||
> While you can run Ly in a terminal emulator as root, it is **not** recommended. If you want to test Ly, please enable its service (as described below) and reboot your machine.
|
||||
|
||||
> [!NOTE]
|
||||
> You can, however, test your configuration file changes like that. Note that you must do Ctrl+C in order to exit Ly.
|
||||
|
||||
The next sections will explain how to use Ly with a variety of init systems. Detailed explanation is only given for systemd, but should be applicable for all.
|
||||
|
||||
> [!NOTE]
|
||||
> All following sections will assume you are using LightDM for convenience sake.
|
||||
|
||||
### systemd
|
||||
|
||||
Now, you can install Ly on your system:
|
||||
|
||||
```
|
||||
# zig build installsystemd
|
||||
# zig build installexe -Dinit_system=systemd
|
||||
```
|
||||
|
||||
Enable the service
|
||||
> [!NOTE]
|
||||
> The `init_system` parameter is optional and defaults to `systemd`.
|
||||
|
||||
Note that you also need to disable your current display manager. For example, if you are using LightDM, you can execute the following command:
|
||||
|
||||
```
|
||||
# systemctl enable ly.service
|
||||
# systemctl disable lightdm.service
|
||||
```
|
||||
|
||||
If you need to switch between ttys after Ly's start you also have to
|
||||
disable getty on Ly's tty to prevent "login" from spawning on top of it
|
||||
Then, similarly to the previous command, you need to enable the Ly service:
|
||||
|
||||
```
|
||||
# systemctl enable ly@tty2.service
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Because Ly runs in a TTY, you **must** disable the TTY service that Ly will run on, otherwise bad things will happen. For example, to disable `getty` spawning on TTY 2, you need to execute the following command:
|
||||
|
||||
```
|
||||
# systemctl disable getty@tty2.service
|
||||
```
|
||||
|
||||
On non-systemd systems, you can change the TTY Ly will run on by editing the corresponding service file for your platform.
|
||||
|
||||
### OpenRC
|
||||
**NOTE**: On Gentoo, Ly will disable the `display-manager-init` service in order to run.
|
||||
|
||||
Clone, compile and test.
|
||||
|
||||
Install Ly and the provided OpenRC service
|
||||
```
|
||||
# zig build installopenrc
|
||||
```
|
||||
|
||||
Enable the service
|
||||
```
|
||||
# zig build installexe -Dinit_system=openrc
|
||||
# rc-update del lightdm
|
||||
# rc-update add ly
|
||||
```
|
||||
|
||||
You can edit which tty Ly will start on by editing the `tty` option in the configuration file.
|
||||
|
||||
If you choose a tty that already has a login/getty running (has a basic login prompt),
|
||||
then you have to disable getty, so it doesn't respawn on top of ly
|
||||
```
|
||||
# rc-update del agetty.tty2
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> On Gentoo, Alpine and potentially others, you also **must** comment out the appropriate line for the TTY in /etc/inittab.
|
||||
|
||||
### runit
|
||||
|
||||
```
|
||||
# zig build installrunit
|
||||
# zig build installexe -Dinit_system=runit
|
||||
# rm /var/service/lightdm
|
||||
# ln -s /etc/sv/ly /var/service/
|
||||
```
|
||||
|
||||
By default, ly will run on tty2. To change the tty it must be set in `/etc/ly/config.ini`
|
||||
|
||||
You should as well disable your existing display manager service if needed, e.g.:
|
||||
|
||||
```
|
||||
# rm /var/service/lxdm
|
||||
```
|
||||
|
||||
The agetty service for the tty console where you are running ly should be disabled.
|
||||
For instance, if you are running ly on tty2 (that's the default, check your `/etc/ly/config.ini`)
|
||||
you should disable the agetty-tty2 service like this:
|
||||
|
||||
```
|
||||
# rm /var/service/agetty-tty2
|
||||
```
|
||||
|
||||
### s6
|
||||
|
||||
```
|
||||
# zig build installexe -Dinit_system=s6
|
||||
# s6-rc -d change lightdm
|
||||
# s6-service add default ly-srv
|
||||
# s6-db-reload
|
||||
# s6-rc -u change ly-srv
|
||||
```
|
||||
|
||||
To disable TTY 2, edit `/etc/s6/config/tty2.conf` and set `SPAWN="no"`.
|
||||
|
||||
### dinit
|
||||
|
||||
```
|
||||
# zig build installexe -Dinit_system=dinit
|
||||
# dinitctl disable lightdm
|
||||
# dinitctl enable ly
|
||||
```
|
||||
|
||||
To disable TTY 2, go to `/etc/dinit.d/config/console.conf` and modify `ACTIVE_CONSOLES`.
|
||||
|
||||
### sysvinit
|
||||
|
||||
```
|
||||
# zig build installexe -Dinit_system=sysvinit
|
||||
# update-rc.d lightdm disable
|
||||
# update-rc.d ly defaults
|
||||
```
|
||||
|
||||
To disable TTY 2, go to `/etc/inittab` and comment out the line containing `tty2`.
|
||||
|
||||
### FreeBSD
|
||||
|
||||
```
|
||||
# zig build installexe -Dprefix_directory=/usr/local -Dconfig_directory=/usr/local/etc -Dinit_system=freebsd
|
||||
# sysrc lightdm_enable="NO"
|
||||
```
|
||||
|
||||
To enable Ly, add the following entry to `/etc/gettytab`:
|
||||
|
||||
```
|
||||
Ly:\
|
||||
:lo=/usr/local/bin/ly_wrapper:\
|
||||
:al=root:
|
||||
```
|
||||
|
||||
Then, modify the command field of the `ttyv1` terminal entry in `/etc/ttys` (TTYs in FreeBSD start at 0):
|
||||
|
||||
```
|
||||
ttyv1 "/usr/libexec/getty Ly" xterm on secure
|
||||
```
|
||||
|
||||
### Updating
|
||||
You can also install Ly without copying the system service and the configuration file. That's
|
||||
called *updating*. To update, simply run:
|
||||
|
||||
You can also install Ly without overriding the current configuration file. This is called **updating**. To update, simply run:
|
||||
|
||||
```
|
||||
# zig build installnoconf
|
||||
```
|
||||
|
||||
If you want to also copy the default config file (but still not the system service), run:
|
||||
|
||||
```
|
||||
# zig build installexe
|
||||
```
|
||||
|
||||
## Arch Linux Installation
|
||||
You can install ly from the [`[extra]` repos](https://archlinux.org/packages/extra/x86_64/ly/):
|
||||
```
|
||||
$ sudo pacman -S ly
|
||||
```
|
||||
You can, of course, still select the init system of your choice when using this command.
|
||||
|
||||
## Configuration
|
||||
You can find all the configuration in `/etc/ly/config.ini`.
|
||||
The file is commented, and includes the default values.
|
||||
|
||||
You can find all the configuration in `/etc/ly/config.ini`. The file is fully commented, and includes the default values.
|
||||
|
||||
You may also check the validity of your configuration file (i.e. if there are any errors in it) with the following command:
|
||||
|
||||
```
|
||||
$ ly --validate-config /etc/ly/config.ini
|
||||
```
|
||||
|
||||
## Controls
|
||||
Use the up and down arrow keys to change the current field, and the
|
||||
left and right arrow keys to change the target desktop environment
|
||||
while on the desktop field (above the login field).
|
||||
|
||||
## .xinitrc
|
||||
If your .xinitrc doesn't work make sure it is executable and includes a shebang.
|
||||
This file is supposed to be a shell script! Quoting from xinit's man page:
|
||||
Use the Up/Down arrow keys to change the current field, and the Left/Right arrow keys to scroll through the different fields (whether it be the info line, the desktop environment, or the username). The info line is where messages and errors are displayed.
|
||||
|
||||
## A note on .xinitrc
|
||||
|
||||
If your `.xinitrc` file doesn't work, make sure it is executable and includes a shebang. This file is supposed to be a shell script! Quoting from `xinit`'s man page:
|
||||
|
||||
> If no specific client program is given on the command line, xinit will look for a file in the user's home directory called .xinitrc to run as a shell script to start up client programs.
|
||||
|
||||
On Arch Linux, the example .xinitrc (/etc/X11/xinit/xinitrc) starts like this:
|
||||
A typical shebang for a shell script looks like this:
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
```
|
||||
|
||||
## Tips
|
||||
The numlock and capslock state is printed in the top-right corner.
|
||||
Use the F1 and F2 keys to respectively shutdown and reboot.
|
||||
Take a look at your .xsession if X doesn't start, as it can interfere
|
||||
(this file is launched with X to configure the display properly).
|
||||
|
||||
## PSX DOOM fire animation
|
||||
To enable the famous PSX DOOM fire described by [Fabien Sanglard](http://fabiensanglard.net/doom_fire_psx/index.html),
|
||||
just uncomment `animate = true` in `/etc/ly/config.ini`. You may also
|
||||
disable the main box borders with `hide_borders = true`.
|
||||
- The numlock and capslock states are printed in the top-right corner.
|
||||
|
||||
## Additional Information
|
||||
The name "Ly" is a tribute to the fairy from the game Rayman.
|
||||
Ly was tested by oxodao, who is some seriously awesome dude.
|
||||
- Use the F1 and F2 keys to respectively shutdown and reboot.
|
||||
|
||||
## Gentoo (OpenRC) installation tip
|
||||
To avoid a console spawning on top on Ly, comment out the appropriate line from /etc/inittab (default is 2).
|
||||
- Take a look at your `.xsession` file if X doesn't start, as it can interfere (this file is launched with X to configure the display properly).
|
||||
|
||||
## A final note
|
||||
|
||||
The name "Ly" is a tribute to the fairy from the game Rayman. Ly was tested by oxodao, who is some seriously awesome dude.
|
||||
|
||||
Also, Ly wouldn't be there today without [ashametrine](https://github.com/ashametrine), who has done significant contributions to the project for the Zig rewrite, which lead to the release of Ly v1.0.0. Massive thanks, and sorry for not crediting you enough beforehand!
|
||||
|
||||
### Donate
|
||||
|
||||
If you like Ly and wish to support my work further, feel free to donate via my
|
||||
[Liberapay link](https://liberapay.com/ShiningLea)!
|
||||
|
|
|
|||
508
res/config.ini
508
res/config.ini
|
|
@ -1,153 +1,451 @@
|
|||
# Ly supports 24-bit true color with styling, which means each color is a 32-bit value.
|
||||
# The format is 0xSSRRGGBB, where SS is the styling, RR is red, GG is green, and BB is blue.
|
||||
# Here are the possible styling options:
|
||||
# TB_BOLD 0x01000000
|
||||
# TB_UNDERLINE 0x02000000
|
||||
# TB_REVERSE 0x04000000
|
||||
# TB_ITALIC 0x08000000
|
||||
# TB_BLINK 0x10000000
|
||||
# TB_HI_BLACK 0x20000000
|
||||
# TB_BRIGHT 0x40000000
|
||||
# TB_DIM 0x80000000
|
||||
# Programmatically, you'd apply them using the bitwise OR operator (|), but because Ly's
|
||||
# configuration doesn't support using it, you have to manually compute the color value.
|
||||
# Note that, if you want to use the default color value of the terminal, you can use the
|
||||
# special value 0x00000000. This means that, if you want to use black, you *must* use
|
||||
# the styling option TB_HI_BLACK (the RGB values are ignored when using this option).
|
||||
|
||||
# Allow empty password or not when authenticating
|
||||
allow_empty_password = true
|
||||
|
||||
# The active animation
|
||||
# none -> Nothing (default)
|
||||
# doom -> PSX DOOM fire
|
||||
# matrix -> CMatrix
|
||||
# none -> Nothing
|
||||
# doom -> PSX DOOM fire
|
||||
# matrix -> CMatrix
|
||||
# colormix -> Color mixing shader
|
||||
# gameoflife -> John Conway's Game of Life
|
||||
# dur_file -> .dur file format (https://github.com/cmang/durdraw/tree/master)
|
||||
# lua -> user-made animation written in LuaJIT
|
||||
animation = none
|
||||
|
||||
# Format string for clock in top right corner (see strftime specification). Example: %c
|
||||
clock = null
|
||||
# Delay between each animation frame in milliseconds
|
||||
animation_frame_delay = 5
|
||||
|
||||
# Enable/disable big clock
|
||||
bigclock = false
|
||||
# Stop the animation after some time
|
||||
# 0 -> Run forever
|
||||
# 1..2e12 -> Stop the animation after this many seconds
|
||||
animation_timeout_sec = 0
|
||||
|
||||
# The character used to mask the password
|
||||
# You can either type it directly as a UTF-8 character (like *), or use a UTF-32
|
||||
# codepoint (for example 0x2022 for a bullet point)
|
||||
# If null, the password will be hidden
|
||||
# Note: you can use a # by escaping it like so: \#
|
||||
asterisk = *
|
||||
|
||||
# Erase password input on failure
|
||||
clear_password = false
|
||||
# The number of failed authentications before a special animation is played... ;)
|
||||
# If set to 0, the animation will never be played
|
||||
auth_fails = 10
|
||||
|
||||
# Enable vi keybindings
|
||||
vi_mode = false
|
||||
# Automatic login configuration
|
||||
# This feature allows Ly to automatically log in a user without password prompt.
|
||||
# IMPORTANT: Both auto_login_user and auto_login_session must be set for this to work.
|
||||
# Autologin only happens once at startup - it won't re-trigger after logout.
|
||||
|
||||
# The `fg` and `bg` color settings take a digit 0-8 corresponding to:
|
||||
#define TB_DEFAULT 0x00
|
||||
#define TB_BLACK 0x01
|
||||
#define TB_RED 0x02
|
||||
#define TB_GREEN 0x03
|
||||
#define TB_YELLOW 0x04
|
||||
#define TB_BLUE 0x05
|
||||
#define TB_MAGENTA 0x06
|
||||
#define TB_CYAN 0x07
|
||||
#define TB_WHITE 0x08
|
||||
#
|
||||
# Setting both to zero makes `bg` black and `fg` white. To set the actual color palette you are encouraged to use another tool
|
||||
# such as [mkinitcpio-colors](https://github.com/evanpurkhiser/mkinitcpio-colors). Note that the color palette defined with
|
||||
# `mkinitcpio-colors` takes 16 colors (0-15), only values 0-8 are valid for `ly` config and these values do not correspond
|
||||
# exactly. For instance, in defining palettes with `mkinitcpio-colors` the order is black, dark red, dark green, brown, dark
|
||||
# blue, dark purple, dark cyan, light gray, dark gray, bright red, bright green, yellow, bright blue, bright purple, bright
|
||||
# cyan, and white, indexed in that order 0 through 15. For example, the color defined for white (indexed at 15 in the mkinitcpio
|
||||
# config) will be used by `ly` for `fg = 8`.
|
||||
# PAM service name to use for automatic login
|
||||
# The default service (ly-autologin) uses pam_permit to allow login without password
|
||||
# The appropriate platform-specific PAM configuration (ly-autologin) will be used automatically
|
||||
auto_login_service = ly-autologin
|
||||
|
||||
# Session name to launch automatically
|
||||
# To find available session names, check the .desktop files in:
|
||||
# - /usr/share/xsessions/ (for X11 sessions)
|
||||
# - /usr/share/wayland-sessions/ (for Wayland sessions)
|
||||
# Use the filename without .desktop extension, the Name field inside the file or the value of the DesktopNames field
|
||||
# Examples: "i3", "sway", "gnome", "plasma", "xfce"
|
||||
# If null, automatic login is disabled
|
||||
auto_login_session = null
|
||||
|
||||
# Username to automatically log in
|
||||
# Must be a valid user on the system
|
||||
# If null, automatic login is disabled
|
||||
auto_login_user = null
|
||||
|
||||
# Identifier for battery whose charge to display at top left
|
||||
# Primary battery is usually BAT0 or BAT1
|
||||
# If set to null, battery status won't be shown
|
||||
# Unused on FreeBSD (a sysctl is used there)
|
||||
battery_id = null
|
||||
|
||||
# Background color id
|
||||
bg = 0
|
||||
bg = 0x00000000
|
||||
|
||||
# Foreground color id
|
||||
fg = 8
|
||||
# Change the state and language of the big clock
|
||||
# none -> Disabled (default)
|
||||
# en -> English
|
||||
# fa -> Farsi
|
||||
bigclock = none
|
||||
|
||||
# Border color
|
||||
border_fg = 8
|
||||
# Set bigclock to 12-hour notation.
|
||||
bigclock_12hr = false
|
||||
|
||||
# Set bigclock to show the seconds.
|
||||
bigclock_seconds = false
|
||||
|
||||
# Blank main box background
|
||||
# Setting to false will make it transparent
|
||||
blank_box = true
|
||||
|
||||
# Border foreground color id
|
||||
border_fg = 0x00FFFFFF
|
||||
|
||||
# Relative horizontal position from the end of the screen
|
||||
# default: 0.5
|
||||
box_position_h = 0.5
|
||||
|
||||
# Relative vertical position from the bottom of the screen
|
||||
# default: 0.5
|
||||
box_position_v = 0.5
|
||||
|
||||
# Title to show at the top of the main box
|
||||
# If set to null, none will be shown
|
||||
box_title = null
|
||||
|
||||
# Brightness decrease command
|
||||
brightness_down_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q -n s 10%-
|
||||
|
||||
# Brightness decrease key combination
|
||||
# If null, the keybind is disabled and isn't shown
|
||||
brightness_down_key = F5
|
||||
|
||||
# Brightness increase command
|
||||
brightness_up_cmd = $PREFIX_DIRECTORY/bin/brightnessctl -q -n s +10%
|
||||
|
||||
# Brightness increase key combination
|
||||
# If null, the keybind is disabled and isn't shown
|
||||
brightness_up_key = F6
|
||||
|
||||
# Erase password input on failure
|
||||
clear_password = false
|
||||
|
||||
# Format string for clock in top right corner (see strftime specification). Example: %c
|
||||
# If null, the clock won't be shown
|
||||
clock = null
|
||||
|
||||
# CMatrix animation foreground color id
|
||||
cmatrix_fg = 0x0000FF00
|
||||
|
||||
# CMatrix animation character string head color id
|
||||
cmatrix_head_col = 0x01FFFFFF
|
||||
|
||||
# CMatrix animation minimum codepoint. It uses a 16-bit integer
|
||||
# For Japanese characters for example, you can use 0x3000 here
|
||||
cmatrix_min_codepoint = 0x21
|
||||
|
||||
# CMatrix animation maximum codepoint. It uses a 16-bit integer
|
||||
# For Japanese characters for example, you can use 0x30FF here
|
||||
cmatrix_max_codepoint = 0x7B
|
||||
|
||||
# Color mixing animation first color id
|
||||
colormix_col1 = 0x00FF0000
|
||||
|
||||
# Color mixing animation second color id
|
||||
colormix_col2 = 0x000000FF
|
||||
|
||||
# Color mixing animation third color id
|
||||
colormix_col3 = 0x20000000
|
||||
|
||||
# Screen corners customization
|
||||
# Keywords:
|
||||
# shutdown -> Shutdown key
|
||||
# restart -> Restart key
|
||||
# britup -> Brightness up key
|
||||
# britdown -> Brightness down key
|
||||
# password -> Toggle password key
|
||||
# clock -> Clock (format defined by 'clock' option)
|
||||
# tty -> Active TTY number
|
||||
# battery -> Battery percentage
|
||||
# version -> Ly version string
|
||||
# numlock -> Numlock state
|
||||
# capslock -> Capslock state
|
||||
# labels -> All custom info labels (lbl:)
|
||||
# binds -> All custom keybind hints (cmd:)
|
||||
# lbl:name -> Specific custom info label
|
||||
# cmd:key -> Specific custom keybind hint
|
||||
#
|
||||
# If using a keyword that groups multiple labels into one (e.g. labels, binds),
|
||||
# they'll be placed horizontally
|
||||
#
|
||||
# Also, the order defines the vertical stack (first item is at the edge)
|
||||
# If items are separted by commas, they'll be placed horizontally
|
||||
# It is possible to have both horizontal and vertical items on the same corner
|
||||
|
||||
# Bottom left
|
||||
corner_bottom_left = version
|
||||
|
||||
# Bottom right
|
||||
corner_bottom_right = labels
|
||||
|
||||
# Top left
|
||||
corner_top_left = shutdown,restart,britup,britdown,password battery
|
||||
|
||||
# Top right
|
||||
corner_top_right = clock numlock,capslock
|
||||
|
||||
# For custom binds: the horizontal limit in characters for each
|
||||
# line of custom binds before moving on to the next.
|
||||
# If null, defaults to the width of the terminal instead.
|
||||
custom_bind_width = null
|
||||
|
||||
# Custom sessions directory
|
||||
# You can specify multiple directories,
|
||||
# e.g. $CONFIG_DIRECTORY/ly/custom-sessions:$PREFIX_DIRECTORY/share/custom-sessions
|
||||
custom_sessions = $CONFIG_DIRECTORY/ly/custom-sessions
|
||||
|
||||
# Input box active by default on startup
|
||||
# Available inputs: info_line, session, login, password
|
||||
default_input = login
|
||||
|
||||
# DOOM animation fire height (1 thru 9)
|
||||
doom_fire_height = 6
|
||||
|
||||
# DOOM animation fire spread (0 thru 4)
|
||||
doom_fire_spread = 2
|
||||
|
||||
# DOOM animation custom top color (low intensity flames)
|
||||
doom_top_color = 0x009F2707
|
||||
|
||||
# DOOM animation custom middle color (medium intensity flames)
|
||||
doom_middle_color = 0x00C78F17
|
||||
|
||||
# DOOM animation custom bottom color (high intensity flames)
|
||||
doom_bottom_color = 0x00FFFFFF
|
||||
|
||||
# Dur file path
|
||||
dur_file_path = $CONFIG_DIRECTORY/ly/example.dur
|
||||
|
||||
# Dur file alignment
|
||||
# The dur file can be aligned with a direction and centered easily with the flags below
|
||||
# Available inputs: topleft, topcenter, topright, centerleft, center, centerright, bottomleft, bottomcenter, bottomright
|
||||
dur_offset_alignment = center
|
||||
|
||||
# Dur offset x direction (value is added to the current position determined by alignment, negatives are supported)
|
||||
dur_x_offset = 0
|
||||
|
||||
# Dur offset y direction (value is added to the current position determined by alignment, negatives are supported)
|
||||
dur_y_offset = 0
|
||||
|
||||
# Set margin to the edges of the DM (useful for curved monitors)
|
||||
edge_margin = 0
|
||||
|
||||
# Error background color id
|
||||
error_bg = 0x00000000
|
||||
|
||||
# Error foreground color id
|
||||
# Default is red and bold
|
||||
error_fg = 0x01FF0000
|
||||
|
||||
# Foreground color id
|
||||
fg = 0x00FFFFFF
|
||||
|
||||
# Render true colors (if supported)
|
||||
# If false, output will be in eight-color mode
|
||||
# All eight-color mode color codes:
|
||||
# TB_DEFAULT 0x0000
|
||||
# TB_BLACK 0x0001
|
||||
# TB_RED 0x0002
|
||||
# TB_GREEN 0x0003
|
||||
# TB_YELLOW 0x0004
|
||||
# TB_BLUE 0x0005
|
||||
# TB_MAGENTA 0x0006
|
||||
# TB_CYAN 0x0007
|
||||
# TB_WHITE 0x0008
|
||||
# If full color is off, the styling options still work. The colors are
|
||||
# always 32-bit values with the styling in the most significant byte.
|
||||
# Note: If using the dur_file animation option and the dur file's color range
|
||||
# is saved as 256 with this option disabled, the file will not be drawn.
|
||||
full_color = true
|
||||
|
||||
# Game of Life entropy interval (0 = disabled, >0 = add entropy every N generations)
|
||||
# 0 -> Pure Conway's Game of Life (will eventually stabilize)
|
||||
# 10 -> Add entropy every 10 generations (recommended for continuous activity)
|
||||
# 50+ -> Less frequent entropy for more natural evolution
|
||||
gameoflife_entropy_interval = 10
|
||||
|
||||
# Game of Life animation foreground color id
|
||||
gameoflife_fg = 0x0000FF00
|
||||
|
||||
# Game of Life frame delay (lower = faster animation, higher = slower)
|
||||
# 1-3 -> Very fast animation
|
||||
# 6 -> Default smooth animation speed
|
||||
# 10+ -> Slower, more contemplative speed
|
||||
gameoflife_frame_delay = 6
|
||||
|
||||
# Game of Life initial cell density (0.0 to 1.0)
|
||||
# 0.1 -> Sparse, minimal activity
|
||||
# 0.4 -> Balanced activity (recommended)
|
||||
# 0.7+ -> Dense, chaotic patterns
|
||||
gameoflife_initial_density = 0.4
|
||||
|
||||
# Remove main box borders
|
||||
hide_borders = false
|
||||
|
||||
# Main box margins
|
||||
margin_box_h = 2
|
||||
margin_box_v = 1
|
||||
# Command executed when no input is detected for a certain time
|
||||
# If null, no command will be executed
|
||||
inactivity_cmd = null
|
||||
|
||||
# Executes a command after a certain amount of seconds
|
||||
inactivity_delay = 0
|
||||
|
||||
# Initial text to show on the info line
|
||||
# If set to null, the info line defaults to the hostname
|
||||
initial_info_text = null
|
||||
|
||||
# Input boxes length
|
||||
input_len = 34
|
||||
|
||||
# Max input sizes
|
||||
max_desktop_len = 100
|
||||
max_login_len = 255
|
||||
max_password_len = 255
|
||||
|
||||
# Input box active by default on startup
|
||||
# Available inputs: session, login, password
|
||||
default_input = login
|
||||
|
||||
# Load the saved desktop and username
|
||||
load = true
|
||||
|
||||
# Save the current desktop and login as defaults
|
||||
save = true
|
||||
|
||||
# Deprecated - Will be removed in a future version
|
||||
# New save files are now loaded from the same directory as the config
|
||||
# Currently used to migrate old save files to the new version
|
||||
# File in which to save and load the default desktop and login
|
||||
save_file = /etc/ly/save
|
||||
|
||||
# Remove power management command hints
|
||||
hide_key_hints = false
|
||||
|
||||
# Specifies the key used for shutdown (F1-F12)
|
||||
shutdown_key = F1
|
||||
|
||||
# Specifies the key used for restart (F1-F12)
|
||||
restart_key = F2
|
||||
|
||||
# Specifies the key used for sleep (F1-F12)
|
||||
sleep_key = F3
|
||||
|
||||
# Command executed when pressing shutdown_key
|
||||
shutdown_cmd = /sbin/shutdown -a now
|
||||
|
||||
# Command executed when pressing restart_key
|
||||
restart_cmd = /sbin/shutdown -r now
|
||||
|
||||
# Command executed when pressing sleep key (can be null)
|
||||
sleep_cmd = null
|
||||
|
||||
# Active language
|
||||
# Available languages are found in /etc/ly/lang/
|
||||
# Available languages are found in $CONFIG_DIRECTORY/ly/lang/
|
||||
lang = en
|
||||
|
||||
# TTY in use
|
||||
tty = 2
|
||||
# Command executed when logging in
|
||||
# If null, no command will be executed
|
||||
# Important: the code itself must end with `exec "$@"` in order to launch the session!
|
||||
# You can also set environment variables in there, they'll persist until logout
|
||||
login_cmd = null
|
||||
|
||||
# Console path
|
||||
console_dev = /dev/console
|
||||
# Path for login.defs file (used for listing all local users on the system on
|
||||
# Linux)
|
||||
login_defs_path = /etc/login.defs
|
||||
|
||||
# Default path. If null, ly doesn't set a path.
|
||||
path = /sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin
|
||||
# Command executed when logging out
|
||||
# If null, no command will be executed
|
||||
# Important: the session will already be terminated when this command is executed, so
|
||||
# no need to add `exec "$@"` at the end
|
||||
logout_cmd = null
|
||||
|
||||
# Event timeout in milliseconds
|
||||
min_refresh_delta = 5
|
||||
# The file pointing to the Lua file to be used when using the Lua animation option
|
||||
lua_animation_file = $CONFIG_DIRECTORY/ly/example.lua
|
||||
|
||||
# General log file path
|
||||
# If null, syslog will be used instead
|
||||
ly_log = /var/log/ly.log
|
||||
|
||||
# Main box horizontal margin
|
||||
margin_box_h = 2
|
||||
|
||||
# Main box vertical margin
|
||||
margin_box_v = 1
|
||||
|
||||
# Set numlock on/off at startup
|
||||
numlock = false
|
||||
|
||||
# Default path
|
||||
# If null, ly doesn't set a path
|
||||
path = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
|
||||
# Specifies the key combination used for restart
|
||||
# If null, the keybind is disabled and isn't shown
|
||||
restart_key = F2
|
||||
|
||||
# Absolute directory for the save file
|
||||
# If null, current desktop & login won't be saved nor loaded
|
||||
save_file_dir = $CONFIG_DIRECTORY/ly
|
||||
|
||||
# Service name (set to ly to use the provided pam config file)
|
||||
service_name = ly
|
||||
|
||||
# Terminal reset command (tput is faster)
|
||||
term_reset_cmd = /usr/bin/tput reset
|
||||
# Session log file path
|
||||
# This will contain stdout and stderr of Wayland sessions
|
||||
# By default it's saved in the user's home directory
|
||||
# Important: due to technical limitations, X11, shell sessions as well as
|
||||
# launching session via KMSCON aren't supported, which means you won't get any
|
||||
# logs from those sessions.
|
||||
# If null, no session log will be created
|
||||
session_log = .local/state/ly-session.log
|
||||
|
||||
# Terminal restore cursor command
|
||||
term_restore_cursor_cmd = /usr/bin/tput cnorm
|
||||
# Setup command
|
||||
setup_cmd = $CONFIG_DIRECTORY/ly/setup.sh
|
||||
|
||||
# Cookie generator
|
||||
mcookie_cmd = /usr/bin/mcookie
|
||||
# Show the shell session in the session list
|
||||
# If false, the shell session will be hidden
|
||||
shell = true
|
||||
|
||||
# Wayland setup command
|
||||
wayland_cmd = /etc/ly/wsetup.sh
|
||||
# Specifies the key combination used for showing the password
|
||||
# If null, the keybind is disabled and isn't shown
|
||||
show_password_key = F7
|
||||
|
||||
# Specifies the key combination used for shutdown
|
||||
# If null, the keybind is disabled and isn't shown
|
||||
shutdown_key = F1
|
||||
|
||||
# Command executed when starting Ly (before the TTY is taken control of)
|
||||
# See file at path below for an example of changing the default TTY colors
|
||||
start_cmd = $CONFIG_DIRECTORY/ly/startup.sh
|
||||
|
||||
# Center the session name.
|
||||
text_in_center = false
|
||||
|
||||
# If true, user will need to manually type username instead of selecting from the list
|
||||
# of discovered users
|
||||
type_username = false
|
||||
|
||||
# Default vi mode
|
||||
# normal -> normal mode
|
||||
# insert -> insert mode
|
||||
vi_default_mode = normal
|
||||
|
||||
# Enable vi keybindings
|
||||
vi_mode = false
|
||||
|
||||
# Wayland desktop environments
|
||||
waylandsessions = /usr/share/wayland-sessions
|
||||
|
||||
# xinitrc (hidden if null)
|
||||
xinitrc = ~/.xinitrc
|
||||
# You can specify multiple directories,
|
||||
# e.g. $PREFIX_DIRECTORY/share/wayland-sessions:$PREFIX_DIRECTORY/local/share/wayland-sessions
|
||||
# If null, Wayland sessions will not be shown
|
||||
waylandsessions = $PREFIX_DIRECTORY/share/wayland-sessions
|
||||
|
||||
# Xorg server command
|
||||
x_cmd = /usr/bin/X
|
||||
# Add the -quiet argument to hide startup logs from the server
|
||||
x_cmd = $PREFIX_DIRECTORY/bin/X
|
||||
|
||||
# Xorg setup command
|
||||
x_cmd_setup = /etc/ly/xsetup.sh
|
||||
# Xorg virtual terminal number
|
||||
# Mostly useful for FreeBSD where choosing the current TTY causes issues
|
||||
# If null, the current TTY will be chosen
|
||||
x_vt = null
|
||||
|
||||
# Xorg xauthority edition tool
|
||||
xauth_cmd = /usr/bin/xauth
|
||||
xauth_cmd = $PREFIX_DIRECTORY/bin/xauth
|
||||
|
||||
# xinitrc
|
||||
# If null, the xinitrc session will be hidden
|
||||
xinitrc = ~/.xinitrc
|
||||
|
||||
# Xorg desktop environments
|
||||
xsessions = /usr/share/xsessions
|
||||
# You can specify multiple directories,
|
||||
# e.g. $PREFIX_DIRECTORY/share/xsessions:$PREFIX_DIRECTORY/local/share/xsessions
|
||||
# If null, X11 sessions will not be shown
|
||||
xsessions = $PREFIX_DIRECTORY/share/xsessions
|
||||
|
||||
# Custom Commands and Labels:
|
||||
# The following examples below give an outline for setting up custom commands and labels.
|
||||
# Unless specified as optional, an option is mandatory.
|
||||
|
||||
# Comments preceding with '##' are for documentation.
|
||||
# Comments preceding with '#' comment out the example INI.
|
||||
|
||||
## Declare a command with the F8 binding.
|
||||
#[cmd:F8]
|
||||
## The name of the command to show up in Ly.
|
||||
## Note: "$" in "$brightness_up" fetches the appropriate string from the specified locale file
|
||||
## and is replaced with the value representing "brightness_up".
|
||||
## You can see the list of keys in any locale file in $CONFIG_DIRECTORY/ly/lang.
|
||||
#cmd = touch /tmp/ly.gaming
|
||||
#name = custom command $brightness_up
|
||||
|
||||
## Declare a label with an ID. This ID should be unique across all labels.
|
||||
#[lbl:kernel]
|
||||
#cmd = uname -srn
|
||||
## Optional, defaulting to 0.
|
||||
## In frames, the time to re-run the command and update the label.
|
||||
## If 0, only run once and do not refresh afterwards
|
||||
#refresh = 0
|
||||
|
|
|
|||
23
res/custom-sessions/README
Normal file
23
res/custom-sessions/README
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
A custom session is just a desktop entry file, like for X11 and Wayland
|
||||
sessions. For example:
|
||||
|
||||
[Desktop Entry]
|
||||
Name=Fish shell
|
||||
Exec=$PREFIX_DIRECTORY/bin/fish
|
||||
DesktopNames=null
|
||||
Terminal=true
|
||||
|
||||
The DesktopNames value is optional and sets the XDG_SESSION_DESKTOP and
|
||||
XDG_CURRENT_DESKTOP environment variables. If equal to null or if not present,
|
||||
XDG_SESSION_DESKTOP and XDG_CURRENT_DESKTOP will not be set. Otherwise, the
|
||||
syntax is the same as described in the Freedesktop Desktop Entry Specification.
|
||||
|
||||
The Terminal value specifies if standard output and standard error should be
|
||||
redirected to the session log file found in Ly's configuration file. If set to
|
||||
true, Ly will consider the program is going to run in a TTY, and thus will not
|
||||
redirect standard output & error. It is optional and defaults to false.
|
||||
|
||||
Finally, do note that if the Terminal value is set to true, the
|
||||
XDG_SESSION_TYPE environment variable will be set to "tty". Otherwise, it will
|
||||
be set to "unspecified" (without quotes), which is behavior that at least
|
||||
systemd recognizes (see pam_systemd's man page).
|
||||
BIN
res/example.dur
Normal file
BIN
res/example.dur
Normal file
Binary file not shown.
119
res/example.lua
Normal file
119
res/example.lua
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
-- [[
|
||||
-- This is an example of using LuaJIT to create a custom animation in Ly, in this case
|
||||
-- bouncing squares that change colors.
|
||||
--
|
||||
-- You are given the following `ly` table:
|
||||
-- {
|
||||
-- height: number -- The height of the terminal
|
||||
-- width: number -- The width of the terminal
|
||||
-- putCell(byte, fg, bg, x, y) -- Draw a cell.
|
||||
-- All arguments to this function are integers, and
|
||||
-- must be in the unsigned 32-bit integer range: 0 to 2^32-1.
|
||||
-- If an argument cannot be converted to this range, it will throw
|
||||
-- an error.
|
||||
--
|
||||
-- For reference, the XY coordinates (0,0) draw a cell on the top-left
|
||||
-- of the terminal, where the positive-X axis moves right and the
|
||||
-- positive-Y axis moves down.
|
||||
--
|
||||
-- For arguments fg and bg: they are colors in the format
|
||||
-- 0xSSRRGGBB, where SS is for styling. See your
|
||||
-- config.ini for more details.
|
||||
--
|
||||
-- For the byte argument, you may use string.byte to fill this argument.
|
||||
--
|
||||
-- putRect(byte, fg, bg, x, y, w, h) -- Draw a rectangle.
|
||||
-- Arguments are the same as putCell except for w and h, which are also
|
||||
-- unsigned integers. The rectangle will be drawn from the top-left, with
|
||||
-- argument w extending it to the right and argument h extending downwards.
|
||||
--
|
||||
--
|
||||
-- putLabel(str, fg, bg, x, y) -- Draw text in argument str. See putCell()
|
||||
-- for info on the rest of the arguments.
|
||||
--
|
||||
-- clock() -- The time, in microseconds.
|
||||
-- }
|
||||
--
|
||||
-- A function named `draw()` must be declared in the script. This is ran every
|
||||
-- frame.
|
||||
--
|
||||
-- In addition to the base library, you are also given the following standard
|
||||
-- libraries:
|
||||
-- bit (A library exclusive to LuaJIT, see https://bitop.luajit.org/api.html)
|
||||
-- math
|
||||
-- string
|
||||
-- table
|
||||
--
|
||||
-- The std libraries io and debug are NOT included.
|
||||
--
|
||||
-- ]]
|
||||
|
||||
-- You should probably copy FPS and FPS_COUNT into any future LuaJIT animations
|
||||
-- you create.
|
||||
local FPS_COUNT = 40
|
||||
local function FPS()
|
||||
return (1 / FPS_COUNT) * 1000000
|
||||
end
|
||||
|
||||
|
||||
local SQUARE_WIDTH = 10
|
||||
local SQUARE_HEIGHT = 5
|
||||
|
||||
local SQUARE_COUNT = 25
|
||||
|
||||
local squares = {}
|
||||
|
||||
for i = 1, SQUARE_COUNT do
|
||||
local vx = 1
|
||||
local vy = 1
|
||||
if math.random(1, 2) == 2 then vx = -vx end
|
||||
if math.random(1, 2) == 2 then vy = -vy end
|
||||
squares[#squares + 1] = {
|
||||
x = math.random(1, ly.width - SQUARE_WIDTH),
|
||||
y = math.random(1, ly.height - SQUARE_HEIGHT),
|
||||
vx = vx,
|
||||
vy = vy,
|
||||
color = math.random(0xFFFFFF)
|
||||
}
|
||||
end
|
||||
|
||||
local timer = ly.clock()
|
||||
local perf = ly.clock()
|
||||
|
||||
function draw()
|
||||
-- Rather than progressing the animation by frame, do it based on
|
||||
-- seconds, via ly.clock(). In this timeframe, you can update the animation
|
||||
-- state.
|
||||
-- DO NOT DRAW CELLS IN THIS TIMEFRAME. You will get flickering.
|
||||
|
||||
-- if this check passes, we can update the animation
|
||||
if timer + FPS() < ly.clock() then
|
||||
for i, v in ipairs(squares) do
|
||||
v.x = v.x + v.vx
|
||||
v.y = v.y + v.vy
|
||||
if v.x == 0 then
|
||||
v.vx = 1; v.color = math.random(0xFFFFFF)
|
||||
end
|
||||
if v.x + SQUARE_WIDTH >= ly.width - 1 then
|
||||
v.vx = -1; v.color = math.random(0xFFFFFF)
|
||||
end
|
||||
if v.y == 0 then
|
||||
v.vy = 1; v.color = math.random(0xFFFFFF)
|
||||
end
|
||||
if v.y + SQUARE_HEIGHT >= ly.height - 1 then
|
||||
v.vy = -1; v.color = math.random(0xFFFFFF)
|
||||
end
|
||||
end
|
||||
timer = ly.clock()
|
||||
end
|
||||
|
||||
|
||||
for i, v in ipairs(squares) do
|
||||
ly.putRect(string.byte(' '), 0, v.color, v.x, v.y, SQUARE_WIDTH, SQUARE_HEIGHT)
|
||||
end
|
||||
|
||||
local new_perf = ly.clock()
|
||||
local str = "FT: " .. ((new_perf - perf) / 1000) .. "ms"
|
||||
ly.putLabel(str, 0x00FFFFFF, 0, (ly.width / 2) - (string.len(str) / 2), ly.height - 1)
|
||||
perf = new_perf
|
||||
end
|
||||
82
res/lang/ar.ini
Normal file
82
res/lang/ar.ini
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
authenticating = جاري المصادقة...
|
||||
brightness_down = خفض السطوع
|
||||
brightness_up = رفع السطوع
|
||||
capslock = capslock
|
||||
custom = مخصص
|
||||
custom_info_err_output_long = الإخراج طويل جداً
|
||||
custom_info_err_no_output = لا يوجد إخراج
|
||||
custom_info_err_no_output_error = ، خطأ محتمل
|
||||
err_alloc = فشل في تخصيص الذاكرة
|
||||
err_args = تعذر تحليل وسيطات سطر الأوامر
|
||||
err_autologin_session = لم يتم العثور على جلسة تسجيل الدخول التلقائي
|
||||
err_bounds = out-of-bounds index
|
||||
err_brightness_change = فشل في تغيير سطوع الشاشة
|
||||
err_chdir = فشل في فتح مجلد المنزل
|
||||
err_clock_too_long = نص الساعة طويل جداً
|
||||
err_config = فشل في تفسير ملف الإعدادات
|
||||
err_crawl = فشل الزحف في أدلة الجلسة
|
||||
err_dgn_oob = رسالة سجل (Log)
|
||||
err_domain = اسم نطاق غير صالح
|
||||
err_empty_password = لا يُسمح بكلمة مرور فارغة
|
||||
err_envlist = فشل في جلب قائمة المتغيرات البيئية
|
||||
err_get_active_tty = فشل الحصول على tty النشط
|
||||
err_hibernate = فشل تنفيذ أمر الإسبات
|
||||
err_hostname = فشل في جلب اسم المضيف (Hostname)
|
||||
err_inactivity = فشل تنفيذ أمر عدم النشاط
|
||||
err_lock_state = فشل الحصول على حالة القفل
|
||||
err_log = فشل فتح ملف السجل
|
||||
err_mlock = فشل في تأمين ذاكرة كلمة المرور (mlock)
|
||||
err_null = مؤشر فارغ (Null pointer)
|
||||
err_numlock = فشل في ضبط Num Lock
|
||||
err_pam = فشل في معاملة PAM
|
||||
err_pam_abort = تم إلغاء معاملة PAM
|
||||
err_pam_acct_expired = الحساب منتهي الصلاحية
|
||||
err_pam_auth = خطأ في المصادقة (Authentication error)
|
||||
err_pam_authinfo_unavail = فشل في الحصول على معلومات المستخدم
|
||||
err_pam_authok_reqd = انتهت صلاحية رمز المصادقة (Token)
|
||||
err_pam_buf = خطأ في ذاكرة التخزين المؤقت (Buffer)
|
||||
err_pam_cred_err = فشل في تعيين بيانات الاعتماد (Credentials)
|
||||
err_pam_cred_expired = بيانات الاعتماد منتهية الصلاحية
|
||||
err_pam_cred_insufficient = بيانات الاعتماد غير كافية
|
||||
err_pam_cred_unavail = فشل في الحصول على بيانات الاعتماد
|
||||
err_pam_maxtries = تم بلوغ الحد الأقصى لمحاولات المصادقة
|
||||
err_pam_perm_denied = تم رفض الوصول (Permission denied)
|
||||
err_pam_session = خطأ في جلسة المستخدم (Session error)
|
||||
err_pam_sys = خطأ في النظام (System error)
|
||||
err_pam_user_unknown = المستخدم غير موجود
|
||||
err_path = فشل في تعيين متغير PATH
|
||||
err_perm_dir = فشل في تغيير المجلد الحالي
|
||||
err_perm_group = فشل في تخفيض صلاحيات المجموعة (Group permissions)
|
||||
err_perm_user = فشل في تخفيض صلاحيات المستخدم (User permissions)
|
||||
err_pwnam = فشل في جلب معلومات المستخدم
|
||||
err_sleep = فشل في تنفيذ أمر sleep
|
||||
err_start = فشل تنفيذ أمر البدء
|
||||
err_battery = فشل تحميل حالة البطارية
|
||||
err_switch_tty = فشل تبديل tty
|
||||
err_tty_ctrl = فشل في نقل تحكم الطرفية (TTY)
|
||||
err_no_users = لم يتم العثور على مستخدمين
|
||||
err_uid_range = فشل الحصول الديناميكي على نطاق uid
|
||||
err_user_gid = فشل في تعيين معرّف المجموعة (GID) للمستخدم
|
||||
err_user_init = فشل في تهيئة بيانات المستخدم
|
||||
err_user_uid = فشل في تعيين معرّف المستخدم (UID)
|
||||
err_xauth = فشل في تنفيذ أمر xauth
|
||||
err_xcb_conn = فشل في الاتصال بمكتبة XCB
|
||||
err_xsessions_dir = فشل في العثور على مجلد Xsessions
|
||||
err_xsessions_open = فشل في فتح مجلد Xsessions
|
||||
hibernate = إسبات
|
||||
insert = ادخال
|
||||
login = تسجيل الدخول
|
||||
logout = تم تسجيل خروجك
|
||||
no_x11_support = دعم x11 معطّل في وقت الترجمة
|
||||
normal = عادي
|
||||
numlock = numlock
|
||||
other = اخر
|
||||
password = كلمة السر
|
||||
restart = اعادة التشغيل
|
||||
shell = shell
|
||||
shutdown = ايقاف التشغيل
|
||||
sleep = وضع السكون
|
||||
toggle_password = إظهار/إخفاء كلمة المرور
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
82
res/lang/bg.ini
Normal file
82
res/lang/bg.ini
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
authenticating = удостоверяване...
|
||||
brightness_down = намаляване на яркостта
|
||||
brightness_up = увеличаване на яркостта
|
||||
capslock = caps lock
|
||||
custom = персонализирано
|
||||
custom_info_err_output_long = резултатът е твърде дълъг
|
||||
custom_info_err_no_output = няма резултат
|
||||
custom_info_err_no_output_error = , възможна грешка
|
||||
err_alloc = неуспешно заделяне на памет
|
||||
err_args = неуспешен анализ на аргументите от командния ред
|
||||
err_autologin_session = сесията за автоматично влизане не е намерена
|
||||
err_bounds = индексът е извън границите
|
||||
err_brightness_change = неуспешна промяна на яркостта
|
||||
err_chdir = неуспешно отваряне на домашната папка
|
||||
err_clock_too_long = низът на часовника е твърде дълъг
|
||||
err_config = неуспешен анализ на конфигурационния файл
|
||||
err_crawl = неуспешно обхождане на папките със сесии
|
||||
err_dgn_oob = съобщение в дневника
|
||||
err_domain = невалиден домейн
|
||||
err_empty_password = не е позволена празна парола
|
||||
err_envlist = неуспешно получаване на списъка с променливи на средата
|
||||
err_get_active_tty = неуспешно откриване на активния TTY
|
||||
err_hibernate = неуспешно изпълнение на командата за хибернация
|
||||
err_hostname = неуспешно получаване на името на хоста
|
||||
err_inactivity = неуспешно изпълнение на командата за неактивност
|
||||
err_lock_state = неуспешно получаване на състоянието на заключване
|
||||
err_log = неуспешно отваряне на файла с дневника
|
||||
err_mlock = неуспешно заключване на паметта за паролата
|
||||
err_null = нулев указател
|
||||
err_numlock = неуспешно задаване на num lock
|
||||
err_pam = неуспешна транзакция
|
||||
err_pam_abort = прекратена транзакция
|
||||
err_pam_acct_expired = изтекъл профил
|
||||
err_pam_auth = грешка при удостоверяването
|
||||
err_pam_authinfo_unavail = неуспешно получаване на информация за потребителя
|
||||
err_pam_authok_reqd = изтекъл жетон
|
||||
err_pam_buf = грешка в буфера на паметта
|
||||
err_pam_cred_err = неуспешно задаване на удостоверения
|
||||
err_pam_cred_expired = изтекли удостоверения
|
||||
err_pam_cred_insufficient = недостатъчни удостоверения
|
||||
err_pam_cred_unavail = неуспешно получаване на удостоверения
|
||||
err_pam_maxtries = достигнат е максималният лимит на опитите
|
||||
err_pam_perm_denied = достъпът е отказан
|
||||
err_pam_session = грешка в сесията
|
||||
err_pam_sys = системна грешка
|
||||
err_pam_user_unknown = непознат потребител
|
||||
err_path = неуспешно задаване на пътя
|
||||
err_perm_dir = неуспешна смяна на текущата папка
|
||||
err_perm_group = неуспешно понижаване на правата на групата
|
||||
err_perm_user = неуспешно понижаване на правата на потребителя
|
||||
err_pwnam = неуспешно получаване на информация за потребителя
|
||||
err_sleep = неуспешно изпълнение на командата за заспиване
|
||||
err_start = неуспешно изпълнение на командата за стартиране
|
||||
err_battery = неуспешно зареждане на състоянието на батерията
|
||||
err_switch_tty = неуспешна смяна на TTY
|
||||
err_tty_ctrl = неуспешно прехвърляне на контрола над TTY
|
||||
err_no_users = не са намерени потребители
|
||||
err_uid_range = неуспешно динамично получаване на UID обхват
|
||||
err_user_gid = неуспешно задаване на потребителския GID
|
||||
err_user_init = неуспешна стартиране на потребителя
|
||||
err_user_uid = неуспешно задаване на потребителския UID
|
||||
err_xauth = неуспешна команда xauth
|
||||
err_xcb_conn = неуспешна xcb връзка
|
||||
err_xsessions_dir = папката със сесии не е намерена
|
||||
err_xsessions_open = неуспешно отваряне на папката със сесии
|
||||
hibernate = хибернация
|
||||
insert = вмъкване
|
||||
login = вход
|
||||
logout = излизане
|
||||
no_x11_support = поддръжката на x11 е изключена при компилирането
|
||||
normal = нормално
|
||||
numlock = num lock
|
||||
other = друго
|
||||
password = парола
|
||||
restart = рестартиране
|
||||
shell = обвивка
|
||||
shutdown = изключване
|
||||
sleep = заспиване
|
||||
toggle_password = превключване на паролата
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
@ -1,45 +1,82 @@
|
|||
authenticating = autenticant...
|
||||
brightness_down = abaixar brillantor
|
||||
brightness_up = apujar brillantor
|
||||
capslock = Bloq Majús
|
||||
err_alloc = falla d'assignació de memòria
|
||||
err_bounds = índex fora de límit
|
||||
err_chdir = error al obrir carpeta home
|
||||
err_console_dev = error al accedir a la consola
|
||||
custom = personalitzat
|
||||
custom_info_err_output_long = sortida massa llarga
|
||||
custom_info_err_no_output = sense sortida
|
||||
custom_info_err_no_output_error = , possible error
|
||||
err_alloc = assignació de memòria fallida
|
||||
err_args = no s'han pogut analitzar els arguments de la línia d'ordres
|
||||
err_autologin_session = no s'ha trobat la sessió d'inici de sessió automàtic
|
||||
err_bounds = índex fora de límits
|
||||
err_brightness_change = error en canviar la brillantor
|
||||
err_chdir = error en obrir la carpeta home
|
||||
err_clock_too_long = la cadena del rellotge és massa llarga
|
||||
err_config = no s'ha pogut analitzar el fitxer de configuració
|
||||
err_crawl = no s'han pogut explorar els directoris de sessió
|
||||
err_dgn_oob = missatge de registre
|
||||
err_domain = domini invàlid
|
||||
err_hostname = error al obtenir el nom del host
|
||||
err_mlock = error al bloquejar la clau de memòria
|
||||
err_empty_password = no es permet la contrasenya buida
|
||||
err_envlist = error en obtenir l'envlist
|
||||
err_get_active_tty = no s'ha pogut obtenir el tty actiu
|
||||
err_hibernate = no s'ha pogut executar l'ordre d'hibernació
|
||||
err_hostname = error en obtenir el nom de l'amfitrió
|
||||
err_inactivity = no s'ha pogut executar l'ordre d'inactivitat
|
||||
err_lock_state = no s'ha pogut obtenir l'estat de bloqueig
|
||||
err_log = no s'ha pogut obrir el fitxer de registre
|
||||
err_mlock = error en bloquejar la memòria de clau
|
||||
err_null = punter nul
|
||||
err_numlock = error en establir el Bloq num
|
||||
err_pam = error en la transacció pam
|
||||
err_pam_abort = transacció pam avortada
|
||||
err_pam_acct_expired = compte expirat
|
||||
err_pam_auth = error d'autenticació
|
||||
err_pam_authinfo_unavail = error al obtenir informació de l'usuari
|
||||
err_pam_authinfo_unavail = error en obtenir la informació de l'usuari
|
||||
err_pam_authok_reqd = token expirat
|
||||
err_pam_buf = error de la memòria intermitja
|
||||
err_pam_cred_err = error al establir les credencials
|
||||
err_pam_buf = error en la memòria intermèdia
|
||||
err_pam_cred_err = error en establir les credencials
|
||||
err_pam_cred_expired = credencials expirades
|
||||
err_pam_cred_insufficient = credencials insuficients
|
||||
err_pam_cred_unavail = error al obtenir credencials
|
||||
err_pam_maxtries = s'ha assolit al màxim nombre d'intents
|
||||
err_pam_cred_unavail = error en obtenir credencials
|
||||
err_pam_maxtries = s'ha assolit al nombre màxim d'intents
|
||||
err_pam_perm_denied = permís denegat
|
||||
err_pam_session = error de sessió
|
||||
err_pam_sys = error de sistema
|
||||
err_pam_user_unknown = usuari desconegut
|
||||
err_path = error al establir la ruta
|
||||
err_perm_dir = error al canviar de directori actual
|
||||
err_perm_group = error al degradar els permisos de grup
|
||||
err_perm_user = error al degradar els permisos de l'usuari
|
||||
err_pwnam = error al obtenir la informació de l'usuari
|
||||
err_user_gid = error al establir el GID de l'usuari
|
||||
err_user_init = error al inicialitzar usuari
|
||||
err_user_uid = error al establir el UID de l'usuari
|
||||
err_xsessions_dir = error al cercar la carpeta de sessions
|
||||
err_xsessions_open = error al obrir la carpeta de sessions
|
||||
err_path = error en establir la ruta
|
||||
err_perm_dir = error en canviar el directori actual
|
||||
err_perm_group = error en degradar els permisos de grup
|
||||
err_perm_user = error en degradar els permisos de l'usuari
|
||||
err_pwnam = error en obtenir la informació de l'usuari
|
||||
err_sleep = no s'ha pogut executar l'ordre de suspensió
|
||||
err_start = no s'ha pogut executar l'ordre d'inici
|
||||
err_battery = no s'ha pogut carregar l'estat de la bateria
|
||||
err_switch_tty = no s'ha pogut canviar de tty
|
||||
err_tty_ctrl = ha fallat la transferència del control tty
|
||||
err_no_users = no s'han trobat usuaris
|
||||
err_uid_range = no s'ha pogut obtenir dinàmicament el rang d'uid
|
||||
err_user_gid = error en establir el GID de l'usuari
|
||||
err_user_init = error en inicialitzar usuari
|
||||
err_user_uid = error en establir l'UID de l'usuari
|
||||
err_xauth = error en la comanda xauth
|
||||
err_xcb_conn = error en la connexió xcb
|
||||
err_xsessions_dir = error en trobar la carpeta de sessions
|
||||
err_xsessions_open = error en obrir la carpeta de sessions
|
||||
hibernate = hibernar
|
||||
insert = inserir
|
||||
login = iniciar sessió
|
||||
logout = tancar sessió
|
||||
logout = sessió tancada
|
||||
no_x11_support = suport x11 desactivat en temps de compilació
|
||||
normal = normal
|
||||
numlock = Bloq Num
|
||||
other = altres
|
||||
password = Clau
|
||||
restart = reiniciar
|
||||
shell = shell
|
||||
shutdown = aturar
|
||||
sleep = suspendre
|
||||
toggle_password = mostrar/amagar contrasenya
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
|
|
@ -1,13 +1,33 @@
|
|||
authenticating = ověřování...
|
||||
brightness_down = snížit jas
|
||||
brightness_up = zvýšit jas
|
||||
capslock = capslock
|
||||
custom = vlastní
|
||||
custom_info_err_output_long = výstup je příliš dlouhý
|
||||
custom_info_err_no_output = žádný výstup
|
||||
custom_info_err_no_output_error = , možná chyba
|
||||
err_alloc = alokace paměti selhala
|
||||
err_args = nelze analyzovat argumenty příkazového řádku
|
||||
err_autologin_session = relace automatického přihlášení nebyla nalezena
|
||||
err_bounds = index je mimo hranice pole
|
||||
err_brightness_change = nepodařilo se změnit jas
|
||||
err_chdir = nelze otevřít domovský adresář
|
||||
err_console_dev = chyba při přístupu do konzole
|
||||
err_clock_too_long = řetězec hodin je příliš dlouhý
|
||||
err_config = nelze analyzovat konfigurační soubor
|
||||
err_crawl = nepodařilo se prohledat adresáře relací
|
||||
err_dgn_oob = zpráva protokolu
|
||||
err_domain = neplatná doména
|
||||
err_empty_password = prázdné heslo není povoleno
|
||||
err_envlist = nepodařilo se získat seznam proměnných prostředí
|
||||
err_get_active_tty = nepodařilo se získat aktivní tty
|
||||
err_hibernate = nepodařilo se spustit příkaz hibernace
|
||||
err_hostname = nelze získat název hostitele
|
||||
err_inactivity = nepodařilo se spustit příkaz nečinnosti
|
||||
err_lock_state = nepodařilo se získat stav zámku
|
||||
err_log = nepodařilo se otevřít soubor protokolu
|
||||
err_mlock = uzamčení paměti hesel selhalo
|
||||
err_null = nulový ukazatel
|
||||
err_numlock = nepodařilo se nastavit numlock
|
||||
err_pam = pam transakce selhala
|
||||
err_pam_abort = pam transakce přerušena
|
||||
err_pam_acct_expired = platnost účtu vypršela
|
||||
|
|
@ -29,17 +49,34 @@ err_perm_dir = nepodařilo se změnit adresář
|
|||
err_perm_group = nepodařilo se snížit skupinová oprávnění
|
||||
err_perm_user = nepodařilo se snížit uživatelská oprávnění
|
||||
err_pwnam = nelze získat informace o uživateli
|
||||
err_sleep = nepodařilo se spustit příkaz spánku
|
||||
err_start = nepodařilo se spustit příkaz spuštění
|
||||
err_battery = nepodařilo se načíst stav baterie
|
||||
err_switch_tty = nepodařilo se přepnout tty
|
||||
err_tty_ctrl = přenos řízení tty selhal
|
||||
err_no_users = nebyli nalezeni žádní uživatelé
|
||||
err_uid_range = nepodařilo se dynamicky získat rozsah uid
|
||||
err_user_gid = nastavení GID uživatele selhalo
|
||||
err_user_init = inicializace uživatele selhala
|
||||
err_user_uid = nastavení UID uživateli selhalo
|
||||
err_xauth = příkaz xauth selhal
|
||||
err_xcb_conn = připojení xcb selhalo
|
||||
err_xsessions_dir = nepodařilo se najít složku relací
|
||||
err_xsessions_open = nepodařilo se otevřít složku relací
|
||||
hibernate = hibernace
|
||||
insert = vložit
|
||||
login = uživatel
|
||||
logout = odhlášen
|
||||
no_x11_support = podpora x11 zakázána při kompilaci
|
||||
normal = normální
|
||||
numlock = numlock
|
||||
other = jiné
|
||||
password = heslo
|
||||
restart = restartovat
|
||||
shell = příkazový řádek
|
||||
shutdown = vypnout
|
||||
sleep = uspat
|
||||
toggle_password = zobrazit/skrýt heslo
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
|
|
@ -1,45 +1,82 @@
|
|||
authenticating = authentifizieren...
|
||||
brightness_down = Helligkeit-
|
||||
brightness_up = Helligkeit+
|
||||
capslock = Feststelltaste
|
||||
custom = benutzerdefiniert
|
||||
custom_info_err_output_long = Ausgabe zu lang
|
||||
custom_info_err_no_output = keine Ausgabe
|
||||
custom_info_err_no_output_error = , möglicher Fehler
|
||||
err_alloc = Speicherzuweisung fehlgeschlagen
|
||||
err_bounds = Listenindex ist außerhalb des Bereichs
|
||||
err_chdir = Fehler beim oeffnen des home-ordners
|
||||
err_console_dev = Zugriff auf die Konsole fehlgeschlagen
|
||||
err_dgn_oob = Protokoll Nachricht
|
||||
err_domain = Unzulaessige domain
|
||||
err_hostname = Holen des Hostnames fehlgeschlagen
|
||||
err_mlock = Abschließen des Passwortspeichers fehlgeschlagen
|
||||
err_null = Null Zeiger
|
||||
err_pam = pam Transaktion fehlgeschlagen
|
||||
err_pam_abort = pam Transaktion abgebrochen
|
||||
err_args = Kommandozeilenargumente konnten nicht verarbeitet werden
|
||||
err_autologin_session = Autologin-Sitzung nicht gefunden
|
||||
err_bounds = Index ausserhalb des Bereichs
|
||||
err_brightness_change = Helligkeitsänderung fehlgeschlagen
|
||||
err_chdir = Fehler beim Oeffnen des Home-Ordners
|
||||
err_clock_too_long = Uhrzeitzeichenkette zu lang
|
||||
err_config = Fehler beim Verarbeiten der Konfigurationsdatei
|
||||
err_crawl = Sitzungsverzeichnisse konnten nicht durchsucht werden
|
||||
err_dgn_oob = Diagnose-Nachricht
|
||||
err_domain = Ungueltige Domain
|
||||
err_empty_password = Leeres Passwort nicht zugelassen
|
||||
err_envlist = Fehler beim Abrufen der Umgebungs-Variablen
|
||||
err_get_active_tty = Aktives tty konnte nicht ermittelt werden
|
||||
err_hibernate = Ruhezustand-Befehl konnte nicht ausgeführt werden
|
||||
err_hostname = Abrufen des Hostnames fehlgeschlagen
|
||||
err_inactivity = Inaktivitätsbefehl konnte nicht ausgeführt werden
|
||||
err_lock_state = Sperrstatus konnte nicht ermittelt werden
|
||||
err_log = Protokolldatei konnte nicht geöffnet werden
|
||||
err_mlock = Sperren des Passwortspeichers fehlgeschlagen
|
||||
err_null = Null Pointer
|
||||
err_numlock = Numlock konnte nicht aktiviert werden
|
||||
err_pam = PAM-Transaktion fehlgeschlagen
|
||||
err_pam_abort = PAM-Transaktion abgebrochen
|
||||
err_pam_acct_expired = Benutzerkonto abgelaufen
|
||||
err_pam_auth = Authentifizierungs Fehler
|
||||
err_pam_authinfo_unavail = holen der Benutzerinformationen fehlgeschlagen
|
||||
err_pam_authok_reqd = Schluessel abgelaufen
|
||||
err_pam_auth = Authentifizierungsfehler
|
||||
err_pam_authinfo_unavail = Abrufen der Benutzerinformationen fehlgeschlagen
|
||||
err_pam_authok_reqd = Passwort abgelaufen
|
||||
err_pam_buf = Speicherpufferfehler
|
||||
err_pam_cred_err = Fehler beim setzen der Anmeldedaten
|
||||
err_pam_cred_err = Fehler beim Setzen der Anmeldedaten
|
||||
err_pam_cred_expired = Anmeldedaten abgelaufen
|
||||
err_pam_cred_insufficient = Anmeldedaten unzureichend
|
||||
err_pam_cred_unavail = Fehler beim holen der Anmeldedaten
|
||||
err_pam_maxtries = Maximale Versuche erreicht
|
||||
err_pam_perm_denied = Zugriff Verweigert
|
||||
err_pam_cred_unavail = Fehler beim Abrufen der Anmeldedaten
|
||||
err_pam_maxtries = Maximale Versuchsanzahl erreicht
|
||||
err_pam_perm_denied = Zugriff verweigert
|
||||
err_pam_session = Sitzungsfehler
|
||||
err_pam_sys = Systemfehler
|
||||
err_pam_user_unknown = Unbekannter Nutzer
|
||||
err_path = Fehler beim setzen des Pfades
|
||||
err_perm_dir = Fehler beim wechseln des Ordners
|
||||
err_perm_group = Fehler beim heruntersetzen der Gruppen Berechtigungen
|
||||
err_perm_user = Fehler beim heruntersetzen der Nutzer Berechtigungen
|
||||
err_pwnam = Holen der Benutzerinformationen fehlgeschlagen
|
||||
err_user_gid = Fehler beim setzen der Gruppen Id des Nutzers
|
||||
err_user_init = Initialisierung des Nutzers fehlgeschlagen
|
||||
err_user_uid = Setzen der Benutzer Id fehlgeschlagen
|
||||
err_xsessions_dir = Fehler beim finden des Sitzungsordners
|
||||
err_xsessions_open = Fehler beim öffnen des Sitzungsordners
|
||||
login = Anmelden
|
||||
logout = Abgemeldet
|
||||
numlock = Numtaste
|
||||
err_path = Fehler beim Setzen des Pfades
|
||||
err_perm_dir = Ordnerwechsel fehlgeschlagen
|
||||
err_perm_group = Fehler beim Heruntersetzen der Gruppenberechtigungen
|
||||
err_perm_user = Fehler beim Heruntersetzen der Nutzerberechtigungen
|
||||
err_pwnam = Abrufen der Benutzerinformationen fehlgeschlagen
|
||||
err_sleep = Sleep-Befehl fehlgeschlagen
|
||||
err_start = Startbefehl konnte nicht ausgeführt werden
|
||||
err_battery = Akkustand konnte nicht geladen werden
|
||||
err_switch_tty = tty konnte nicht gewechselt werden
|
||||
err_tty_ctrl = Fehler bei der TTY-Uebergabe
|
||||
err_no_users = Keine Benutzer gefunden
|
||||
err_uid_range = uid-Bereich konnte nicht dynamisch ermittelt werden
|
||||
err_user_gid = Fehler beim Setzen der Gruppen-ID
|
||||
err_user_init = Nutzer-Initialisierung fehlgeschlagen
|
||||
err_user_uid = Setzen der Benutzer-ID fehlgeschlagen
|
||||
err_xauth = Xauth-Befehl fehlgeschlagen
|
||||
err_xcb_conn = xcb-Verbindung fehlgeschlagen
|
||||
err_xsessions_dir = Fehler beim Finden des Sitzungsordners
|
||||
err_xsessions_open = Fehler beim Oeffnen des Sitzungsordners
|
||||
hibernate = Ruhezustand
|
||||
insert = Einfügen
|
||||
login = Nutzer
|
||||
logout = Abmelden
|
||||
no_x11_support = x11-Unterstützung zur Kompilierzeit deaktiviert
|
||||
normal = Normal
|
||||
numlock = Numlock
|
||||
other = Andere
|
||||
password = Passwort
|
||||
restart = Neustarten
|
||||
shell = shell
|
||||
shell = Shell
|
||||
shutdown = Herunterfahren
|
||||
sleep = Sleep
|
||||
toggle_password = Passwort anzeigen/verbergen
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
|
|
@ -1,13 +1,33 @@
|
|||
authenticating = authenticating...
|
||||
brightness_down = decrease brightness
|
||||
brightness_up = increase brightness
|
||||
capslock = capslock
|
||||
custom = custom
|
||||
custom_info_err_output_long = output too long
|
||||
custom_info_err_no_output = no output
|
||||
custom_info_err_no_output_error = , possible error
|
||||
err_alloc = failed memory allocation
|
||||
err_args = unable to parse command line arguments
|
||||
err_autologin_session = autologin session not found
|
||||
err_bounds = out-of-bounds index
|
||||
err_brightness_change = failed to change brightness
|
||||
err_chdir = failed to open home folder
|
||||
err_console_dev = failed to access console
|
||||
err_clock_too_long = clock string too long
|
||||
err_config = unable to parse config file
|
||||
err_crawl = failed to crawl session directories
|
||||
err_dgn_oob = log message
|
||||
err_domain = invalid domain
|
||||
err_empty_password = empty password not allowed
|
||||
err_envlist = failed to get envlist
|
||||
err_get_active_tty = failed to get active tty
|
||||
err_hibernate = failed to execute hibernate command
|
||||
err_hostname = failed to get hostname
|
||||
err_inactivity = failed to execute inactivity command
|
||||
err_lock_state = failed to get lock state
|
||||
err_log = failed to open log file
|
||||
err_mlock = failed to lock password memory
|
||||
err_null = null pointer
|
||||
err_numlock = failed to set numlock
|
||||
err_pam = pam transaction failed
|
||||
err_pam_abort = pam transaction aborted
|
||||
err_pam_acct_expired = account expired
|
||||
|
|
@ -29,22 +49,34 @@ err_perm_dir = failed to change current directory
|
|||
err_perm_group = failed to downgrade group permissions
|
||||
err_perm_user = failed to downgrade user permissions
|
||||
err_pwnam = failed to get user info
|
||||
err_unknown = an unknown error occurred
|
||||
err_sleep = failed to execute sleep command
|
||||
err_start = failed to execute start command
|
||||
err_battery = failed to load battery status
|
||||
err_switch_tty = failed to switch tty
|
||||
err_tty_ctrl = tty control transfer failed
|
||||
err_no_users = no users found
|
||||
err_uid_range = failed to dynamically get uid range
|
||||
err_user_gid = failed to set user GID
|
||||
err_user_init = failed to initialize user
|
||||
err_user_uid = failed to set user UID
|
||||
err_xauth = xauth command failed
|
||||
err_xcb_conn = xcb connection failed
|
||||
err_xsessions_dir = failed to find sessions folder
|
||||
err_xsessions_open = failed to open sessions folder
|
||||
hibernate = hibernate
|
||||
insert = insert
|
||||
login = login
|
||||
logout = logged out
|
||||
no_x11_support = x11 support disabled at compile-time
|
||||
normal = normal
|
||||
numlock = numlock
|
||||
other = other
|
||||
password = password
|
||||
restart = reboot
|
||||
shell = shell
|
||||
shutdown = shutdown
|
||||
sleep = sleep
|
||||
toggle_password = toggle password
|
||||
wayland = wayland
|
||||
xinitrc = xinitrc
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
82
res/lang/eo.ini
Normal file
82
res/lang/eo.ini
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
authenticating = aŭtentigado...
|
||||
brightness_down = malpliigi helecon
|
||||
brightness_up = pliigi helecon
|
||||
capslock = majuskla baskulo
|
||||
custom = propra
|
||||
custom_info_err_output_long = eligo tro longa
|
||||
custom_info_err_no_output = neniu eligo
|
||||
custom_info_err_no_output_error = , ebla eraro
|
||||
err_alloc = malsukcesis memorasignon
|
||||
err_args = ne povas analizi argumentojn de komanda linio
|
||||
err_autologin_session = aŭtomatan ensalutan seancon ne trovis
|
||||
err_bounds = indico estas ekster-intervala
|
||||
err_brightness_change = malsukcesis ŝanĝi la helecon
|
||||
err_chdir = malsukcesis malfermi hejman dosierujon
|
||||
err_clock_too_long = horloĝa ĉeno estas tro longa
|
||||
err_config = ne povas analizi agordan dosieron
|
||||
err_crawl = malsukcesis dum serĉado de seancaj dosierujoj
|
||||
err_dgn_oob = protokola mesaĝo
|
||||
err_domain = malvalida domajno
|
||||
err_empty_password = ne akceptas malplenan pasvorton
|
||||
err_envlist = malsukcesis preni la medivariablojn
|
||||
err_get_active_tty = malsukcesis preni la aktivan TTY-on
|
||||
err_hibernate = malsukcesis ruli la komandon por diskodormo
|
||||
err_hostname = malsukcesis preni la sistemnomon
|
||||
err_inactivity = malsukcesis ruli la agorditan komandon por malaktiveco
|
||||
err_lock_state = malsukcesis preni la ŝlosan staton
|
||||
err_log = malsukcesis malfermi la protokolan dosieron
|
||||
err_mlock = malsukcesis ŝlosi pasvortan memoron
|
||||
err_null = nula memorloko
|
||||
err_numlock = malsukcesis agordi numeran baskulon
|
||||
err_pam = PAM-a transakcio malsukcesis
|
||||
err_pam_abort = PAM-a transakcio malsukcesis
|
||||
err_pam_acct_expired = konto eksvalidiĝis
|
||||
err_pam_auth = aŭtentiga eraro
|
||||
err_pam_authinfo_unavail = malsukcesis preni uzantajn informojn
|
||||
err_pam_authok_reqd = memorsigno eksvalidiĝis
|
||||
err_pam_buf = bufra eraro
|
||||
err_pam_cred_err = malsukcesis agordi akreditaĵon
|
||||
err_pam_cred_expired = akreditaĵo eksvalidiĝis
|
||||
err_pam_cred_insufficient = nesufiĉa akreditaĵo
|
||||
err_pam_cred_unavail = malsukcesis preni akreditaĵon
|
||||
err_pam_maxtries = atingis maksimuman kvanton da provoj
|
||||
err_pam_perm_denied = permeso negis
|
||||
err_pam_session = seancan eraron
|
||||
err_pam_sys = sisteman eraron
|
||||
err_pam_user_unknown = ne konas uzanton
|
||||
err_path = malsukcesis agordi la median dosierindikon
|
||||
err_perm_dir = malsukcesis ŝanĝi la nunan dosierujon
|
||||
err_perm_group = malsukcesis redukti grupajn permesojn
|
||||
err_perm_user = malsukcesis redukti uzantajn permesojn
|
||||
err_pwnam = malsukcesis preni uzantajn informojn
|
||||
err_sleep = malsukcesis ruli memordorman komandon
|
||||
err_start = malsukcesis ruli startan komandon
|
||||
err_battery = malsukcesis ŝargi baterian staton
|
||||
err_switch_tty = malsukcesis ŝanĝi TTY-on
|
||||
err_tty_ctrl = TTY-an stiran transigon malsukcesis
|
||||
err_no_users = nul uzantojn trovas
|
||||
err_uid_range = malsukcesis dinamike preni UID-an intervalon
|
||||
err_user_gid = malsukcesis agordi uzantan GID-on
|
||||
err_user_init = malsukcesis iniciĝi uzanto
|
||||
err_user_uid = malsukcesis agordi uzantan UID-on
|
||||
err_xauth = malsukcesis plenumi je xauth
|
||||
err_xcb_conn = malsukcesis dum konectado al xcb
|
||||
err_xsessions_dir = malsukcesis trovi seancan dosierujon
|
||||
err_xsessions_open = malsukcesis malfermi seancan dosierujon
|
||||
hibernate = diskodormi
|
||||
insert = enmeti
|
||||
login = uzanto
|
||||
logout = elsalutis
|
||||
no_x11_support = x11 estas foriĝita de kompil-tempo
|
||||
normal = normala
|
||||
numlock = numera baskulo
|
||||
other = alia
|
||||
password = pasvorto
|
||||
restart = restartigi
|
||||
shell = ŝelo
|
||||
shutdown = malŝalti
|
||||
sleep = memordormi
|
||||
toggle_password = montri/kaŝi pasvorton
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
@ -1,13 +1,33 @@
|
|||
authenticating = autenticando...
|
||||
brightness_down = bajar brillo
|
||||
brightness_up = subir brillo
|
||||
capslock = Bloq Mayús
|
||||
custom = personalizado
|
||||
custom_info_err_output_long = salida demasiado larga
|
||||
custom_info_err_no_output = sin salida
|
||||
custom_info_err_no_output_error = , posible error
|
||||
err_alloc = asignación de memoria fallida
|
||||
err_args = no se pudieron analizar los argumentos de la línea de comandos
|
||||
err_autologin_session = no se encontró la sesión de inicio de sesión automático
|
||||
err_bounds = índice fuera de límites
|
||||
err_brightness_change = no se pudo cambiar el brillo
|
||||
err_chdir = error al abrir la carpeta home
|
||||
err_console_dev = error al acceder a la consola
|
||||
err_clock_too_long = la cadena del reloj es demasiado larga
|
||||
err_config = no se pudo analizar el archivo de configuración
|
||||
err_crawl = no se pudieron explorar los directorios de sesión
|
||||
err_dgn_oob = mensaje de registro
|
||||
err_domain = dominio inválido
|
||||
err_empty_password = no se permite contraseña vacía
|
||||
err_envlist = no se pudo obtener la lista de variables de entorno
|
||||
err_get_active_tty = no se pudo obtener el tty activo
|
||||
err_hibernate = no se pudo ejecutar el comando de hibernación
|
||||
err_hostname = error al obtener el nombre de host
|
||||
err_inactivity = no se pudo ejecutar el comando de inactividad
|
||||
err_lock_state = no se pudo obtener el estado de bloqueo
|
||||
err_log = no se pudo abrir el archivo de registro
|
||||
err_mlock = error al bloquear la contraseña de memoria
|
||||
err_null = puntero nulo
|
||||
err_numlock = no se pudo configurar numlock
|
||||
err_pam = error en la transacción pam
|
||||
err_pam_abort = transacción pam abortada
|
||||
err_pam_acct_expired = cuenta expirada
|
||||
|
|
@ -29,17 +49,34 @@ err_perm_dir = error al cambiar el directorio actual
|
|||
err_perm_group = error al degradar los permisos del grupo
|
||||
err_perm_user = error al degradar los permisos del usuario
|
||||
err_pwnam = error al obtener la información del usuario
|
||||
err_sleep = no se pudo ejecutar el comando de suspensión
|
||||
err_start = no se pudo ejecutar el comando de inicio
|
||||
err_battery = no se pudo cargar el estado de la batería
|
||||
err_switch_tty = no se pudo cambiar de tty
|
||||
err_tty_ctrl = falló la transferencia de control tty
|
||||
err_no_users = no se encontraron usuarios
|
||||
err_uid_range = no se pudo obtener dinámicamente el rango de uid
|
||||
err_user_gid = error al establecer el GID del usuario
|
||||
err_user_init = error al inicializar usuario
|
||||
err_user_uid = error al establecer el UID del usuario
|
||||
err_xauth = falló el comando xauth
|
||||
err_xcb_conn = falló la conexión xcb
|
||||
err_xsessions_dir = error al buscar la carpeta de sesiones
|
||||
err_xsessions_open = error al abrir la carpeta de sesiones
|
||||
login = iniciar sesión
|
||||
hibernate = hibernar
|
||||
insert = insertar
|
||||
login = usuario
|
||||
logout = cerrar sesión
|
||||
no_x11_support = soporte x11 desactivado en tiempo de compilación
|
||||
normal = normal
|
||||
numlock = Bloq Num
|
||||
other = otro
|
||||
password = contraseña
|
||||
restart = reiniciar
|
||||
shell = shell
|
||||
shutdown = apagar
|
||||
sleep = suspender
|
||||
toggle_password = mostrar/ocultar contraseña
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
|
|
@ -1,19 +1,39 @@
|
|||
capslock = verr.maj
|
||||
authenticating = authentification...
|
||||
brightness_down = diminuer la luminosité
|
||||
brightness_up = augmenter la luminosité
|
||||
capslock = verr.maj
|
||||
custom = customisé
|
||||
custom_info_err_output_long = sortie trop longue
|
||||
custom_info_err_no_output = pas de sortie
|
||||
custom_info_err_no_output_error = , erreur possible
|
||||
err_alloc = échec d'allocation mémoire
|
||||
err_args = échec de l'analyse des arguments en lignes de commande
|
||||
err_autologin_session = session de connexion automatique introuvable
|
||||
err_bounds = indice hors-limite
|
||||
err_brightness_change = échec du changement de luminosité
|
||||
err_chdir = échec de l'ouverture du répertoire home
|
||||
err_console_dev = échec d'accès à la console
|
||||
err_clock_too_long = chaîne de formattage de l'horloge trop longue
|
||||
err_config = échec de lecture du fichier de configuration
|
||||
err_crawl = échec de la navigation des répertoires de session
|
||||
err_dgn_oob = message
|
||||
err_domain = domaine invalide
|
||||
err_empty_password = mot de passe vide non autorisé
|
||||
err_envlist = échec de lecture de la liste d'environnement
|
||||
err_get_active_tty = échec de lecture du terminal actif
|
||||
err_hibernate = échec de l'exécution de la commande de veille prolongée
|
||||
err_hostname = échec de lecture du nom d'hôte
|
||||
err_inactivity = échec de l'exécution de la commande d'inactivité
|
||||
err_lock_state = échec de lecture de l'état de verrouillage
|
||||
err_log = échec de l'ouverture du fichier de journal
|
||||
err_mlock = échec du verrouillage mémoire
|
||||
err_null = pointeur null
|
||||
err_numlock = échec de modification du verr.num
|
||||
err_pam = échec de la transaction pam
|
||||
err_pam_abort = transaction pam avortée
|
||||
err_pam_acct_expired = compte expiré
|
||||
err_pam_auth = erreur d'authentification
|
||||
err_pam_authok_reqd = tiquet expiré
|
||||
err_pam_authinfo_unavail = échec de l'obtention des infos utilisateur
|
||||
err_pam_authok_reqd = tiquet expiré
|
||||
err_pam_buf = erreur de mémoire tampon
|
||||
err_pam_cred_err = échec de la modification des identifiants
|
||||
err_pam_cred_expired = identifiants expirés
|
||||
|
|
@ -29,17 +49,34 @@ err_perm_dir = échec de changement de répertoire
|
|||
err_perm_group = échec du déclassement des permissions de groupe
|
||||
err_perm_user = échec du déclassement des permissions utilisateur
|
||||
err_pwnam = échec de lecture des infos utilisateur
|
||||
err_sleep = échec de l'exécution de la commande de veille
|
||||
err_start = échec de l'exécution de la commande de démarrage
|
||||
err_battery = échec de lecture de l'état de la batterie
|
||||
err_switch_tty = échec du changement de terminal
|
||||
err_tty_ctrl = échec du transfert de contrôle du terminal
|
||||
err_no_users = aucun utilisateur trouvé
|
||||
err_uid_range = échec de récupération dynamique de la plage d'UID
|
||||
err_user_gid = échec de modification du GID
|
||||
err_user_init = échec d'initialisation de l'utilisateur
|
||||
err_user_uid = échec de modification du UID
|
||||
err_xauth = échec de la commande xauth
|
||||
err_xcb_conn = échec de la connexion xcb
|
||||
err_xsessions_dir = échec de la recherche du dossier de sessions
|
||||
err_xsessions_open = échec de l'ouverture du dossier de sessions
|
||||
hibernate = veille prolongée
|
||||
insert = insertion
|
||||
login = identifiant
|
||||
logout = déconnection
|
||||
logout = déconnecté
|
||||
no_x11_support = support pour x11 désactivé lors de la compilation
|
||||
normal = normal
|
||||
numlock = verr.num
|
||||
other = autre
|
||||
password = mot de passe
|
||||
restart = redémarrer
|
||||
shell = shell
|
||||
shutdown = éteindre
|
||||
sleep = veille
|
||||
toggle_password = afficher le mot de passe
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
|
|
@ -1,13 +1,33 @@
|
|||
authenticating = autenticazione in corso...
|
||||
brightness_down = diminuisci luminosità
|
||||
brightness_up = aumenta luminosità
|
||||
capslock = capslock
|
||||
custom = personalizzato
|
||||
custom_info_err_output_long = output troppo lungo
|
||||
custom_info_err_no_output = nessun output
|
||||
custom_info_err_no_output_error = , possibile errore
|
||||
err_alloc = impossibile allocare memoria
|
||||
err_args = impossibile analizzare gli argomenti della riga di comando
|
||||
err_autologin_session = sessione di accesso automatico non trovata
|
||||
err_bounds = indice fuori limite
|
||||
err_brightness_change = impossibile modificare la luminosità
|
||||
err_chdir = impossibile aprire home directory
|
||||
err_console_dev = impossibile aprire console
|
||||
err_clock_too_long = stringa dell'orologio troppo lunga
|
||||
err_config = impossibile analizzare il file di configurazione
|
||||
err_crawl = impossibile esplorare le directory delle sessioni
|
||||
err_dgn_oob = messaggio log
|
||||
err_domain = dominio non valido
|
||||
err_empty_password = password vuota non consentita
|
||||
err_envlist = impossibile ottenere la lista delle variabili d'ambiente
|
||||
err_get_active_tty = impossibile ottenere il tty attivo
|
||||
err_hibernate = impossibile eseguire il comando di ibernazione
|
||||
err_hostname = impossibile ottenere hostname
|
||||
err_inactivity = impossibile eseguire il comando di inattività
|
||||
err_lock_state = impossibile ottenere lo stato di blocco
|
||||
err_log = impossibile aprire il file di log
|
||||
err_mlock = impossibile ottenere lock per la password in memoria
|
||||
err_null = puntatore nullo
|
||||
err_numlock = impossibile impostare il numlock
|
||||
err_pam = transazione PAM fallita
|
||||
err_pam_abort = transazione PAM interrotta
|
||||
err_pam_acct_expired = account scaduto
|
||||
|
|
@ -29,17 +49,34 @@ err_perm_dir = impossibile cambiare directory corrente
|
|||
err_perm_group = impossibile ridurre permessi gruppo
|
||||
err_perm_user = impossibile ridurre permessi utente
|
||||
err_pwnam = impossibile ottenere dati utente
|
||||
err_sleep = impossibile eseguire il comando di sospensione
|
||||
err_start = impossibile eseguire il comando di avvio
|
||||
err_battery = impossibile caricare lo stato della batteria
|
||||
err_switch_tty = impossibile cambiare tty
|
||||
err_tty_ctrl = trasferimento del controllo tty fallito
|
||||
err_no_users = nessun utente trovato
|
||||
err_uid_range = impossibile ottenere dinamicamente l'intervallo uid
|
||||
err_user_gid = impossibile impostare GID utente
|
||||
err_user_init = impossibile inizializzare utente
|
||||
err_user_uid = impossible impostare UID utente
|
||||
err_xauth = comando xauth fallito
|
||||
err_xcb_conn = connessione xcb fallita
|
||||
err_xsessions_dir = impossibile localizzare cartella sessioni
|
||||
err_xsessions_open = impossibile aprire cartella sessioni
|
||||
hibernate = ibernazione
|
||||
insert = inserisci
|
||||
login = username
|
||||
logout = scollegato
|
||||
no_x11_support = supporto x11 disabilitato in fase di compilazione
|
||||
normal = normale
|
||||
numlock = numlock
|
||||
other = altro
|
||||
password = password
|
||||
restart = riavvio
|
||||
shell = shell
|
||||
shutdown = arresto
|
||||
sleep = sospendi
|
||||
toggle_password = mostra/nascondi password
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
82
res/lang/ja_JP.ini
Normal file
82
res/lang/ja_JP.ini
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
authenticating = 認証中...
|
||||
brightness_down = 明るさを下げる
|
||||
brightness_up = 明るさを上げる
|
||||
capslock = CapsLock
|
||||
custom = カスタム
|
||||
custom_info_err_output_long = 出力が長すぎます
|
||||
custom_info_err_no_output = 出力なし
|
||||
custom_info_err_no_output_error = 、エラーの可能性あり
|
||||
err_alloc = メモリ割り当て失敗
|
||||
err_args = コマンドライン引数を解析できません
|
||||
err_autologin_session = 自動ログインセッションが見つかりません
|
||||
err_bounds = 境界外インデックス
|
||||
err_brightness_change = 明るさの変更に失敗しました
|
||||
err_chdir = ホームフォルダを開けませんでした
|
||||
err_clock_too_long = 時計の文字列が長すぎます
|
||||
err_config = 設定ファイルを解析できません
|
||||
err_crawl = セッションディレクトリのクロールに失敗しました
|
||||
err_dgn_oob = ログメッセージ
|
||||
err_domain = 無効なドメイン
|
||||
err_empty_password = 空のパスワードは許可されていません
|
||||
err_envlist = 環境変数リストの取得に失敗しました
|
||||
err_get_active_tty = アクティブなttyの取得に失敗しました
|
||||
err_hibernate = 休止状態コマンドの実行に失敗しました
|
||||
err_hostname = ホスト名の取得に失敗しました
|
||||
err_inactivity = 無操作コマンドの実行に失敗しました
|
||||
err_lock_state = ロック状態の取得に失敗しました
|
||||
err_log = ログファイルを開けませんでした
|
||||
err_mlock = パスワードメモリのロックに失敗しました
|
||||
err_null = ヌルポインタ
|
||||
err_numlock = NumLockの設定に失敗しました
|
||||
err_pam = PAMトランザクション失敗
|
||||
err_pam_abort = PAMトランザクションが中断されました
|
||||
err_pam_acct_expired = アカウントの有効期限が切れています
|
||||
err_pam_auth = 認証エラー
|
||||
err_pam_authinfo_unavail = ユーザー情報の取得に失敗しました
|
||||
err_pam_authok_reqd = トークンの有効期限が切れています
|
||||
err_pam_buf = メモリバッファエラー
|
||||
err_pam_cred_err = 認証情報の設定に失敗しました
|
||||
err_pam_cred_expired = 認証情報の有効期限が切れています
|
||||
err_pam_cred_insufficient = 認証情報が不十分です
|
||||
err_pam_cred_unavail = 認証情報の取得に失敗しました
|
||||
err_pam_maxtries = 最大試行回数に到達しました
|
||||
err_pam_perm_denied = アクセスが拒否されました
|
||||
err_pam_session = セッションエラー
|
||||
err_pam_sys = システムエラー
|
||||
err_pam_user_unknown = 不明なユーザー
|
||||
err_path = パスの設定に失敗しました
|
||||
err_perm_dir = カレントディレクトリの変更に失敗しました
|
||||
err_perm_group = グループ権限のダウングレードに失敗しました
|
||||
err_perm_user = ユーザー権限のダウングレードに失敗しました
|
||||
err_pwnam = ユーザー情報の取得に失敗しました
|
||||
err_sleep = スリープコマンドの実行に失敗しました
|
||||
err_start = 起動コマンドの実行に失敗しました
|
||||
err_battery = バッテリー状態の読み込みに失敗しました
|
||||
err_switch_tty = ttyの切り替えに失敗しました
|
||||
err_tty_ctrl = TTY制御の転送に失敗しました
|
||||
err_no_users = ユーザーが見つかりません
|
||||
err_uid_range = uidの範囲を動的に取得できませんでした
|
||||
err_user_gid = ユーザーGIDの設定に失敗しました
|
||||
err_user_init = ユーザーの初期化に失敗しました
|
||||
err_user_uid = ユーザーUIDの設定に失敗しました
|
||||
err_xauth = xauthコマンドの実行に失敗しました
|
||||
err_xcb_conn = XCB接続に失敗しました
|
||||
err_xsessions_dir = セッションフォルダが見つかりませんでした
|
||||
err_xsessions_open = セッションフォルダを開けませんでした
|
||||
hibernate = 休止状態
|
||||
insert = 挿入
|
||||
login = ログイン
|
||||
logout = ログアウト済み
|
||||
no_x11_support = x11サポートはコンパイル時に無効化されています
|
||||
normal = 通常
|
||||
numlock = NumLock
|
||||
other = その他
|
||||
password = パスワード
|
||||
restart = 再起動
|
||||
shell = シェル
|
||||
shutdown = シャットダウン
|
||||
sleep = スリープ
|
||||
toggle_password = パスワードの表示/非表示
|
||||
wayland = Wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
82
res/lang/ku.ini
Normal file
82
res/lang/ku.ini
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
authenticating = tê piştrastkirin...
|
||||
brightness_down = ronahiyê kêm bike
|
||||
brightness_up = ronahiyê bilind bike
|
||||
capslock = tîpên girdek (capslock)
|
||||
custom = kesane
|
||||
custom_info_err_output_long = encam pir dirêj e
|
||||
custom_info_err_no_output = encam tune
|
||||
custom_info_err_no_output_error = , xeletiya mimkun
|
||||
err_alloc = veqetandina bîrê têk çû
|
||||
err_args = argumanên rêzika fermanê nehatin analîzkirin
|
||||
err_autologin_session = danişîna têketina xweber nehate dîtin
|
||||
err_bounds = îndeksa derveyî sînor
|
||||
err_brightness_change = guherandina ronahiyê têk çû
|
||||
err_chdir = vekirina peldanka malê têk çû
|
||||
err_clock_too_long = rêzika demjimêrê pir dirêj e
|
||||
err_config = pela rêkxistinê nehat analîzkirin
|
||||
err_crawl = gerandina pelrêçên danişînê têk çû
|
||||
err_dgn_oob = peyama têketinê
|
||||
err_domain = navpara nederbasdar
|
||||
err_empty_password = borînpeyv nabe ku vala be
|
||||
err_envlist = girtina lîsteya jîngehê (envlist) têk çû
|
||||
err_get_active_tty = girtina tty ya çalak têk çû
|
||||
err_hibernate = fermana cemidaninê nehat xebitandin
|
||||
err_hostname = girtina navê mêvandar têk çû
|
||||
err_inactivity = fermana neçalaktiyê nehat xebitandin
|
||||
err_lock_state = girtina rewşa kilîtkirinê têk çû
|
||||
err_log = vekirina pelê têkeinê têk çû
|
||||
err_mlock = kilîtkirina bîra borînpeyvê têk çû
|
||||
err_null = nîşandera null
|
||||
err_numlock = sazkirina numlock têk çû
|
||||
err_pam = danûstendina pam têk çû
|
||||
err_pam_abort = danûstendina pam hate têkbirin
|
||||
err_pam_acct_expired = dema jimarê derbas bûye
|
||||
err_pam_auth = şaşetiya piştrastkirinê
|
||||
err_pam_authinfo_unavail = zanyariyên bikarhêner nehatin girtin
|
||||
err_pam_authok_reqd = dema nîşandanê derbas bûye
|
||||
err_pam_buf = şaşetiya bîra demkî
|
||||
err_pam_cred_err = sazkirina rastkitinê têk çû
|
||||
err_pam_cred_expired = dema rastkitinê derbas bûye
|
||||
err_pam_cred_insufficient = rastkitinê kêm
|
||||
err_pam_cred_unavail = girtina rastkitinê têk çû
|
||||
err_pam_maxtries = sînorê hewldanên herî bilind hat gihîştin
|
||||
err_pam_perm_denied = mafdayîn hat paşguhkirin
|
||||
err_pam_session = şaşetiya danişînê
|
||||
err_pam_sys = şaşetiya pergalê
|
||||
err_pam_user_unknown = bikarhênerê nenas
|
||||
err_path = sazkirina rêgehê têk çû
|
||||
err_perm_dir = guhertina pelrêçê heyî têk çû
|
||||
err_perm_group = kêmkirina mafdayînên komê têk çû
|
||||
err_perm_user = kêmkirina mafdayînên bikarhêner têk çû
|
||||
err_pwnam = girtina zanyariyên bikarhêner têk çû
|
||||
err_sleep = fermana cemidaninê nehat xebitandin
|
||||
err_start = fermana destpêkirinê nehat xebitandin
|
||||
err_battery = barkirina rewşa betariyê têk çû
|
||||
err_switch_tty = guhertina tty têk çû
|
||||
err_tty_ctrl = guhertina kontrola tty têk çû
|
||||
err_no_users = tu bikarhêner nehatin dîtin
|
||||
err_uid_range = girtina rêjeya dînamîk a sînorê uid têk çû
|
||||
err_user_gid = sazkirina GID a bikarhêner têk çû
|
||||
err_user_init = destpêkirina bikarhêner têk çû
|
||||
err_user_uid = sazkirina UID a bikarhêner têk çû
|
||||
err_xauth = fermana xauth têk çû
|
||||
err_xcb_conn = girêdana xcb têk çû
|
||||
err_xsessions_dir = dîtina peldanka danişînan têk çû
|
||||
err_xsessions_open = vekirina peldanka danişînan têk çû
|
||||
hibernate = bicemidîne
|
||||
insert = têxîne
|
||||
login = têketin
|
||||
logout = derkeve
|
||||
no_x11_support = piştgiriya x11 di dema berhevkirinê de hatiye girtin
|
||||
normal = normal
|
||||
numlock = numlock
|
||||
other = ên din
|
||||
password = borînpeyv
|
||||
restart = ji nû ve bide destpêkirin
|
||||
shell = shell
|
||||
shutdown = vemirîne
|
||||
sleep = têxîne xewê
|
||||
toggle_password = şîfre nîşan bide/veşêre
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
82
res/lang/lv.ini
Normal file
82
res/lang/lv.ini
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
authenticating = autentificējas...
|
||||
brightness_down = samazināt spilgtumu
|
||||
brightness_up = palielināt spilgtumu
|
||||
capslock = caps lock
|
||||
custom = pielāgots
|
||||
custom_info_err_output_long = izvade pārāk gara
|
||||
custom_info_err_no_output = nav izvades
|
||||
custom_info_err_no_output_error = , iespējama kļūda
|
||||
err_alloc = neizdevās atmiņas piešķiršana
|
||||
err_args = nevar parsēt komandrindas argumentus
|
||||
err_autologin_session = automātiskās pieteikšanās sesija nav atrasta
|
||||
err_bounds = indekss ārpus robežām
|
||||
err_brightness_change = neizdevās mainīt spilgtumu
|
||||
err_chdir = neizdevās atvērt mājas mapi
|
||||
err_clock_too_long = pulksteņa virkne pārāk gara
|
||||
err_config = neizdevās parsēt konfigurācijas failu
|
||||
err_crawl = neizdevās pārlūkot sesiju direktorijus
|
||||
err_dgn_oob = žurnāla ziņojums
|
||||
err_domain = nederīgs domēns
|
||||
err_empty_password = tukša parole nav atļauta
|
||||
err_envlist = neizdevās iegūt vides mainīgo sarakstu
|
||||
err_get_active_tty = neizdevās iegūt aktīvo tty
|
||||
err_hibernate = neizdevās izpildīt hibernācijas komandu
|
||||
err_hostname = neizdevās iegūt hostname
|
||||
err_inactivity = neizdevās izpildīt neaktivitātes komandu
|
||||
err_lock_state = neizdevās iegūt bloķēšanas stāvokli
|
||||
err_log = neizdevās atvērt žurnāla failu
|
||||
err_mlock = neizdevās bloķēt paroles atmiņu
|
||||
err_null = null rādītājs
|
||||
err_numlock = neizdevās iestatīt numlock
|
||||
err_pam = pam transakcija neizdevās
|
||||
err_pam_abort = pam transakcija pārtraukta
|
||||
err_pam_acct_expired = konts novecojis
|
||||
err_pam_auth = autentifikācijas kļūda
|
||||
err_pam_authinfo_unavail = neizdevās iegūt lietotāja informāciju
|
||||
err_pam_authok_reqd = žetons beidzies
|
||||
err_pam_buf = atmiņas bufera kļūda
|
||||
err_pam_cred_err = neizdevās iestatīt akreditācijas datus
|
||||
err_pam_cred_expired = akreditācijas dati novecojuši
|
||||
err_pam_cred_insufficient = nepietiekami akreditācijas dati
|
||||
err_pam_cred_unavail = neizdevās iegūt akreditācijas datus
|
||||
err_pam_maxtries = sasniegts maksimālais mēģinājumu skaits
|
||||
err_pam_perm_denied = piekļuve liegta
|
||||
err_pam_session = sesijas kļūda
|
||||
err_pam_sys = sistēmas kļūda
|
||||
err_pam_user_unknown = nezināms lietotājs
|
||||
err_path = neizdevās iestatīt ceļu
|
||||
err_perm_dir = neizdevās mainīt pašreizējo mapi
|
||||
err_perm_group = neizdevās pazemināt grupas atļaujas
|
||||
err_perm_user = neizdevās pazemināt lietotāja atļaujas
|
||||
err_pwnam = neizdevās iegūt lietotāja informāciju
|
||||
err_sleep = neizdevās izpildīt miega komandu
|
||||
err_start = neizdevās izpildīt startēšanas komandu
|
||||
err_battery = neizdevās ielādēt akumulatora stāvokli
|
||||
err_switch_tty = neizdevās pārslēgt tty
|
||||
err_tty_ctrl = tty vadības nodošana neizdevās
|
||||
err_no_users = lietotāji nav atrasti
|
||||
err_uid_range = neizdevās dinamiski iegūt uid diapazonu
|
||||
err_user_gid = neizdevās iestatīt lietotāja GID
|
||||
err_user_init = neizdevās inicializēt lietotāju
|
||||
err_user_uid = neizdevās iestatīt lietotāja UID
|
||||
err_xauth = xauth komanda neizdevās
|
||||
err_xcb_conn = xcb savienojums neizdevās
|
||||
err_xsessions_dir = neizdevās atrast sesiju mapi
|
||||
err_xsessions_open = neizdevās atvērt sesiju mapi
|
||||
hibernate = hibernācija
|
||||
insert = ievietot
|
||||
login = lietotājs
|
||||
logout = iziet
|
||||
no_x11_support = x11 atbalsts atspējots kompilācijas laikā
|
||||
normal = parastais
|
||||
numlock = numlock
|
||||
other = cits
|
||||
password = parole
|
||||
restart = restartēt
|
||||
shell = terminālis
|
||||
shutdown = izslēgt
|
||||
sleep = snauda
|
||||
toggle_password = rādīt/slēpt paroli
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
49
res/lang/normalize_lang_files.py
Executable file
49
res/lang/normalize_lang_files.py
Executable file
|
|
@ -0,0 +1,49 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from pathlib import Path
|
||||
from sys import stderr
|
||||
|
||||
|
||||
def process_lang_file(path: Path, lang_keys: list[str]) -> None:
|
||||
# read key-value-pairs from lang file into dict
|
||||
existing_entries = {}
|
||||
with open(path, "r", encoding="UTF-8") as fh:
|
||||
while line := fh.readline():
|
||||
try:
|
||||
key, value = line.split("=", 1)
|
||||
existing_entries[key.strip()] = value.strip()
|
||||
except ValueError: # line does not contain '='
|
||||
continue
|
||||
|
||||
# re-write current lang file with entries in order of occurence in `lang_keys`
|
||||
# and with empty lines for missing translations
|
||||
with open(path, "w", encoding="UTF-8") as fh:
|
||||
for item in lang_keys:
|
||||
try:
|
||||
fh.write(f"{item} = {existing_entries[item]}\n")
|
||||
except KeyError: # no translation for `item` yet
|
||||
fh.write("\n")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
zig_lang_file = Path(__file__).parent.joinpath("../../src/config/Lang.zig").resolve()
|
||||
if not zig_lang_file.exists():
|
||||
print(f"ERROR: File '{zig_lang_file.as_posix()}' does not exist. Exiting.", file=stderr)
|
||||
exit(1)
|
||||
|
||||
# read "language keys" from `zig_lang_file` into list
|
||||
lang_keys = []
|
||||
with open(zig_lang_file, "r", encoding="UTF-8") as fh:
|
||||
while line := fh.readline():
|
||||
# only process lines that are not empty or no comments
|
||||
if not (line.strip() == "" or line.startswith("//")):
|
||||
lang_keys.append(line.split(":")[0].strip())
|
||||
|
||||
lang_files = [f for f in Path.iterdir(Path(__file__).parent) if f.name.endswith(".ini") and f.is_file()]
|
||||
|
||||
for file in lang_files:
|
||||
process_lang_file(file, lang_keys)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1,45 +1,82 @@
|
|||
authenticating = uwierzytelnianie...
|
||||
brightness_down = zmniejsz jasność
|
||||
brightness_up = zwiększ jasność
|
||||
capslock = capslock
|
||||
custom = własny
|
||||
custom_info_err_output_long = wyjście zbyt długie
|
||||
custom_info_err_no_output = brak wyjścia
|
||||
custom_info_err_no_output_error = , możliwy błąd
|
||||
err_alloc = nieudana alokacja pamięci
|
||||
err_bounds = indeks poza granicami
|
||||
err_args = nie można przetworzyć argumentów wiersza poleceń
|
||||
err_autologin_session = nie znaleziono sesji autologowania
|
||||
err_bounds = indeks poza zakresem
|
||||
err_brightness_change = nie udało się zmienić jasności
|
||||
err_chdir = nie udało się otworzyć folderu domowego
|
||||
err_console_dev = nie udało się uzyskać dostępu do konsoli
|
||||
err_clock_too_long = ciąg znaków zegara jest za długi
|
||||
err_config = nie można przetworzyć pliku konfiguracyjnego
|
||||
err_crawl = nie udało się przeszukać katalogów sesji
|
||||
err_dgn_oob = wiadomość loga
|
||||
err_domain = niepoprawna domena
|
||||
err_empty_password = puste hasło jest niedozwolone
|
||||
err_envlist = nie udało się pobrać listy zmiennych środowiskowych
|
||||
err_get_active_tty = nie udało się uzyskać aktywnego tty
|
||||
err_hibernate = nie udało się wykonać polecenia hibernacji
|
||||
err_hostname = nie udało się uzyskać nazwy hosta
|
||||
err_inactivity = nie udało się wykonać polecenia nieaktywności
|
||||
err_lock_state = nie udało się uzyskać stanu blokady
|
||||
err_log = nie udało się otworzyć pliku logu
|
||||
err_mlock = nie udało się zablokować pamięci haseł
|
||||
err_null = wskaźnik zerowy
|
||||
err_null = pusty wskaźnik
|
||||
err_numlock = nie udało się ustawić numlock
|
||||
err_pam = transakcja pam nieudana
|
||||
err_pam_abort = transakcja pam przerwana
|
||||
err_pam_acct_expired = konto wygasło
|
||||
err_pam_auth = błąd autentyfikacji
|
||||
err_pam_auth = błąd uwierzytelniania
|
||||
err_pam_authinfo_unavail = nie udało się zdobyć informacji o użytkowniku
|
||||
err_pam_authok_reqd = token wygasł
|
||||
err_pam_buf = błąd bufora pamięci
|
||||
err_pam_cred_err = nie udało się ustawić uwierzytelnienia
|
||||
err_pam_cred_err = nie udało się ustawić uwierzytelnienia
|
||||
err_pam_cred_expired = uwierzytelnienie wygasło
|
||||
err_pam_cred_insufficient = niewystarczające uwierzytelnienie
|
||||
err_pam_cred_unavail = nie udało się uzyskać uwierzytelnienia
|
||||
err_pam_cred_unavail = nie udało się uzyskać uwierzytelnienia
|
||||
err_pam_maxtries = osiągnięto limit prób
|
||||
err_pam_perm_denied = brak uprawnień
|
||||
err_pam_perm_denied = odmowa dostępu
|
||||
err_pam_session = błąd sesji
|
||||
err_pam_sys = błąd systemu
|
||||
err_pam_user_unknown = nieznany użytkownik
|
||||
err_path = nie udało się ustawić ścieżki
|
||||
err_perm_dir = nie udało się zmienić obecnego katalogu
|
||||
err_perm_group = nie udało się obniżyć uprawnień grupy
|
||||
err_perm_user = nie udało się obniżyć uprawnień użytkownika
|
||||
err_pwnam = nie udało się uzyskać informacji o użytkowniku
|
||||
err_path = nie udało się ustawić ścieżki
|
||||
err_perm_dir = nie udało się zmienić obecnego katalogu
|
||||
err_perm_group = nie udało się obniżyć uprawnień grupy
|
||||
err_perm_user = nie udało się obniżyć uprawnień użytkownika
|
||||
err_pwnam = nie udało się uzyskać informacji o użytkowniku
|
||||
err_sleep = nie udało się wykonać polecenia sleep
|
||||
err_start = nie udało się wykonać polecenia startowego
|
||||
err_battery = nie udało się sprawdzić statusu baterii
|
||||
err_switch_tty = nie można przełączyć tty
|
||||
err_tty_ctrl = nie udało się przekazać kontroli tty
|
||||
err_no_users = nie znaleziono żadnego użytkownika
|
||||
err_uid_range = nie udało się dynamicznie pobrać zakresu uid
|
||||
err_user_gid = nie udało się ustawić GID użytkownika
|
||||
err_user_init = nie udało się zainicjalizować użytkownika
|
||||
err_user_init = nie udało się zainicjalizować użytkownika
|
||||
err_user_uid = nie udało się ustawić UID użytkownika
|
||||
err_xsessions_dir = nie udało się znaleźć folderu sesji
|
||||
err_xsessions_open = nie udało się otworzyć folderu sesji
|
||||
err_xauth = polecenie xauth nie powiodło się
|
||||
err_xcb_conn = połączenie xcb nie powiodło się
|
||||
err_xsessions_dir = nie udało się znaleźć folderu sesji
|
||||
err_xsessions_open = nie udało się otworzyć folderu sesji
|
||||
hibernate = hibernuj
|
||||
insert = wstaw
|
||||
login = login
|
||||
logout = wylogowano
|
||||
no_x11_support = obsługa x11 wyłączona podczas kompilacji
|
||||
normal = normalny
|
||||
numlock = numlock
|
||||
other = inny
|
||||
password = hasło
|
||||
restart = uruchom ponownie
|
||||
shell = powłoka
|
||||
shutdown = wyłącz
|
||||
sleep = uśpij
|
||||
toggle_password = Pokaż/ukryj hasło
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
|
|
@ -1,13 +1,33 @@
|
|||
authenticating = a autenticar...
|
||||
brightness_down = diminuir brilho
|
||||
brightness_up = aumentar brilho
|
||||
capslock = capslock
|
||||
custom = personalizado
|
||||
custom_info_err_output_long = saída demasiado longa
|
||||
custom_info_err_no_output = sem saída
|
||||
custom_info_err_no_output_error = , possível erro
|
||||
err_alloc = erro na atribuição de memória
|
||||
err_args = não foi possível analisar os argumentos da linha de comandos
|
||||
err_autologin_session = sessão de início de sessão automático não encontrada
|
||||
err_bounds = índice fora de limites
|
||||
err_brightness_change = não foi possível alterar o brilho
|
||||
err_chdir = erro ao abrir a pasta home
|
||||
err_console_dev = erro ao aceder à consola
|
||||
err_clock_too_long = a cadeia de caracteres do relógio é demasiado longa
|
||||
err_config = não foi possível analisar o ficheiro de configuração
|
||||
err_crawl = não foi possível explorar os diretórios de sessão
|
||||
err_dgn_oob = mensagem de registo
|
||||
err_domain = domínio inválido
|
||||
err_empty_password = palavra-passe vazia não é permitida
|
||||
err_envlist = não foi possível obter a lista de variáveis de ambiente
|
||||
err_get_active_tty = não foi possível obter o tty ativo
|
||||
err_hibernate = não foi possível executar o comando de hibernação
|
||||
err_hostname = erro ao obter o nome do host
|
||||
err_inactivity = não foi possível executar o comando de inatividade
|
||||
err_lock_state = não foi possível obter o estado de bloqueio
|
||||
err_log = não foi possível abrir o ficheiro de registo
|
||||
err_mlock = erro de bloqueio de memória
|
||||
err_null = ponteiro nulo
|
||||
err_numlock = não foi possível definir o numlock
|
||||
err_pam = erro na transação pam
|
||||
err_pam_abort = transação pam abortada
|
||||
err_pam_acct_expired = conta expirada
|
||||
|
|
@ -29,17 +49,34 @@ err_perm_dir = erro ao alterar o diretório atual
|
|||
err_perm_group = erro ao reduzir as permissões do grupo
|
||||
err_perm_user = erro ao reduzir as permissões do utilizador
|
||||
err_pwnam = erro ao obter informação do utilizador
|
||||
err_sleep = não foi possível executar o comando de suspensão
|
||||
err_start = não foi possível executar o comando de início
|
||||
err_battery = não foi possível carregar o estado da bateria
|
||||
err_switch_tty = não foi possível mudar de tty
|
||||
err_tty_ctrl = falhou a transferência de controlo do tty
|
||||
err_no_users = nenhum utilizador encontrado
|
||||
err_uid_range = não foi possível obter dinamicamente o intervalo de uid
|
||||
err_user_gid = erro ao definir o GID do utilizador
|
||||
err_user_init = erro ao iniciar o utilizador
|
||||
err_user_uid = erro ao definir o UID do utilizador
|
||||
err_xauth = o comando xauth falhou
|
||||
err_xcb_conn = a ligação xcb falhou
|
||||
err_xsessions_dir = erro ao localizar a pasta das sessões
|
||||
err_xsessions_open = erro ao abrir a pasta das sessões
|
||||
hibernate = hibernar
|
||||
insert = inserir
|
||||
login = iniciar sessão
|
||||
logout = terminar sessão
|
||||
no_x11_support = suporte a x11 desativado em tempo de compilação
|
||||
normal = normal
|
||||
numlock = numlock
|
||||
other = outro
|
||||
password = palavra-passe
|
||||
restart = reiniciar
|
||||
shell = shell
|
||||
shutdown = encerrar
|
||||
sleep = suspender
|
||||
toggle_password = mostrar/ocultar palavra-passe
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
|
|
@ -1,19 +1,39 @@
|
|||
authenticating = autenticando...
|
||||
brightness_down = diminuir brilho
|
||||
brightness_up = aumentar brilho
|
||||
capslock = caixa alta
|
||||
custom = personalizado
|
||||
custom_info_err_output_long = saída muito longa
|
||||
custom_info_err_no_output = sem saída
|
||||
custom_info_err_no_output_error = , possível erro
|
||||
err_alloc = alocação de memória malsucedida
|
||||
err_args = não foi possível analisar os argumentos da linha de comando
|
||||
err_autologin_session = sessão de login automático não encontrada
|
||||
err_bounds = índice fora de limites
|
||||
err_brightness_change = não foi possível alterar o brilho
|
||||
err_chdir = não foi possível abrir o diretório home
|
||||
err_console_dev = não foi possível acessar o console
|
||||
err_clock_too_long = a string do relógio é muito longa
|
||||
err_config = não foi possível analisar o arquivo de configuração
|
||||
err_crawl = não foi possível explorar os diretórios de sessão
|
||||
err_dgn_oob = mensagem de log
|
||||
err_domain = domínio inválido
|
||||
err_empty_password = senha vazia não é permitida
|
||||
err_envlist = não foi possível obter a lista de variáveis de ambiente
|
||||
err_get_active_tty = não foi possível obter o tty ativo
|
||||
err_hibernate = não foi possível executar o comando de hibernação
|
||||
err_hostname = não foi possível obter o nome do host
|
||||
err_inactivity = não foi possível executar o comando de inatividade
|
||||
err_lock_state = não foi possível obter o estado de bloqueio
|
||||
err_log = não foi possível abrir o arquivo de log
|
||||
err_mlock = bloqueio da memória de senha malsucedido
|
||||
err_null = ponteiro nulo
|
||||
err_numlock = não foi possível definir o numlock
|
||||
err_pam = transação pam malsucedida
|
||||
err_pam_abort = transação pam abortada
|
||||
err_pam_acct_expired = conta expirada
|
||||
err_pam_auth = erro de autenticação
|
||||
err_pam_authinfo_unavail = não foi possível obter informações do usuário
|
||||
err_pam_authok_reqd = token expirada
|
||||
err_pam_authok_reqd = token expirado
|
||||
err_pam_buf = erro de buffer de memória
|
||||
err_pam_cred_err = erro para definir credenciais
|
||||
err_pam_cred_expired = credenciais expiradas
|
||||
|
|
@ -29,17 +49,34 @@ err_perm_dir = não foi possível alterar o diretório atual
|
|||
err_perm_group = não foi possível reduzir as permissões de grupo
|
||||
err_perm_user = não foi possível reduzir as permissões de usuário
|
||||
err_pwnam = não foi possível obter informações do usuário
|
||||
err_sleep = não foi possível executar o comando de suspensão
|
||||
err_start = não foi possível executar o comando de início
|
||||
err_battery = não foi possível carregar o status da bateria
|
||||
err_switch_tty = não foi possível mudar de tty
|
||||
err_tty_ctrl = a transferência de controle do tty falhou
|
||||
err_no_users = nenhum usuário encontrado
|
||||
err_uid_range = não foi possível obter dinamicamente o intervalo de uid
|
||||
err_user_gid = não foi possível definir o GID do usuário
|
||||
err_user_init = não foi possível iniciar o usuário
|
||||
err_user_uid = não foi possível definir o UID do usuário
|
||||
err_xauth = o comando xauth falhou
|
||||
err_xcb_conn = a conexão xcb falhou
|
||||
err_xsessions_dir = não foi possível encontrar a pasta das sessões
|
||||
err_xsessions_open = não foi possível abrir a pasta das sessões
|
||||
hibernate = hibernar
|
||||
insert = inserir
|
||||
login = conectar
|
||||
logout = desconectado
|
||||
no_x11_support = suporte a x11 desativado em tempo de compilação
|
||||
normal = normal
|
||||
numlock = numlock
|
||||
other = outro
|
||||
password = senha
|
||||
restart = reiniciar
|
||||
shell = shell
|
||||
shutdown = desligar
|
||||
sleep = suspender
|
||||
toggle_password = mostrar/ocultar senha
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
|
|
@ -1,14 +1,34 @@
|
|||
authenticating = autentificare...
|
||||
brightness_down = scade luminozitatea
|
||||
brightness_up = crește luminozitatea
|
||||
capslock = capslock
|
||||
|
||||
|
||||
|
||||
err_console_dev = nu s-a putut accesa consola
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
custom = personalizat
|
||||
custom_info_err_output_long = ieșire prea lungă
|
||||
custom_info_err_no_output = fără ieșire
|
||||
custom_info_err_no_output_error = , posibil eroare
|
||||
err_alloc = alocare de memorie eșuată
|
||||
err_args = imposibil de analizat argumentele liniei de comandă
|
||||
err_autologin_session = sesiunea de autentificare automată nu a fost găsită
|
||||
err_bounds = index în afara limitelor
|
||||
err_brightness_change = imposibil de schimbat luminozitatea
|
||||
err_chdir = imposibil de deschis folderul de acasă
|
||||
err_clock_too_long = șirul de ceas este prea lung
|
||||
err_config = imposibil de analizat fișierul de configurare
|
||||
err_crawl = imposibil de explorat directoarele de sesiune
|
||||
err_dgn_oob = mesaj jurnal
|
||||
err_domain = domeniu invalid
|
||||
err_empty_password = parola goală nu este permisă
|
||||
err_envlist = imposibil de obținut lista de variabile de mediu
|
||||
err_get_active_tty = imposibil de obținut tty-ul activ
|
||||
err_hibernate = imposibil de executat comanda de hibernare
|
||||
err_hostname = imposibil de obținut numele gazdei
|
||||
err_inactivity = imposibil de executat comanda de inactivitate
|
||||
err_lock_state = imposibil de obținut starea de blocare
|
||||
err_log = imposibil de deschis fișierul jurnal
|
||||
err_mlock = imposibil de blocat memoria parolei
|
||||
err_null = pointer nul
|
||||
err_numlock = imposibil de setat numlock
|
||||
err_pam = tranzacție pam eșuată
|
||||
err_pam_abort = tranzacţie pam anulată
|
||||
err_pam_acct_expired = cont expirat
|
||||
err_pam_auth = eroare de autentificare
|
||||
|
|
@ -24,22 +44,39 @@ err_pam_perm_denied = acces interzis
|
|||
err_pam_session = eroare de sesiune
|
||||
err_pam_sys = eroare de sistem
|
||||
err_pam_user_unknown = utilizator necunoscut
|
||||
|
||||
err_path = imposibil de setat calea
|
||||
err_perm_dir = nu s-a putut schimba dosarul (folder-ul) curent
|
||||
err_perm_group = nu s-a putut face downgrade permisiunilor de grup
|
||||
err_perm_user = nu s-a putut face downgrade permisiunilor de utilizator
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
err_pwnam = imposibil de obținut informații despre utilizator
|
||||
err_sleep = imposibil de executat comanda de repaus
|
||||
err_start = imposibil de executat comanda de pornire
|
||||
err_battery = imposibil de încărcat starea bateriei
|
||||
err_switch_tty = imposibil de comutat tty
|
||||
err_tty_ctrl = transferul controlului tty a eșuat
|
||||
err_no_users = niciun utilizator găsit
|
||||
err_uid_range = imposibil de obținut dinamic intervalul uid
|
||||
err_user_gid = imposibil de setat GID-ul utilizatorului
|
||||
err_user_init = imposibil de inițializa utilizatorul
|
||||
err_user_uid = imposibil de setat UID-ul utilizatorului
|
||||
err_xauth = comanda xauth a eșuat
|
||||
err_xcb_conn = conexiunea xcb a eșuat
|
||||
err_xsessions_dir = imposibil de găsit folderul de sesiuni
|
||||
err_xsessions_open = imposibil de deschis folderul de sesiuni
|
||||
hibernate = hibernare
|
||||
insert = inserare
|
||||
login = utilizator
|
||||
logout = opreşte sesiunea
|
||||
no_x11_support = suportul x11 dezactivat la compilare
|
||||
normal = normal
|
||||
numlock = numlock
|
||||
other = altul
|
||||
password = parolă
|
||||
restart = resetează
|
||||
shell = shell
|
||||
shutdown = opreşte sistemul
|
||||
sleep = repaus
|
||||
toggle_password = afișare/ascundere parolă
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
|
|
@ -1,13 +1,33 @@
|
|||
authenticating = аутентификация...
|
||||
brightness_down = уменьшить яркость
|
||||
brightness_up = увеличить яркость
|
||||
capslock = capslock
|
||||
custom = пользовательский
|
||||
custom_info_err_output_long = вывод слишком длинный
|
||||
custom_info_err_no_output = нет вывода
|
||||
custom_info_err_no_output_error = , возможная ошибка
|
||||
err_alloc = не удалось выделить память
|
||||
err_args = не удалось разобрать аргументы командной строки
|
||||
err_autologin_session = не найдена сессия с автологином
|
||||
err_bounds = за пределами индекса
|
||||
err_brightness_change = не удалось изменить яркость
|
||||
err_chdir = не удалось открыть домашнюю папку
|
||||
err_console_dev = не удалось получить доступ к консоли
|
||||
err_clock_too_long = строка часов слишком длинная
|
||||
err_config = не удалось разобрать файл конфигурации
|
||||
err_crawl = не удалось просканировать каталоги сессии
|
||||
err_dgn_oob = отладочное сообщение (log)
|
||||
err_domain = неверный домен
|
||||
err_empty_password = пустой пароль не допустим
|
||||
err_envlist = не удалось получить список переменных среды
|
||||
err_get_active_tty = не удалось получить активный tty
|
||||
err_hibernate = не удалось выполнить команду гибернации
|
||||
err_hostname = не удалось получить имя хоста
|
||||
err_inactivity = не удалось выполнить команду бездействия
|
||||
err_lock_state = не удалось получить состояние lock
|
||||
err_log = не удалось открыть файл log
|
||||
err_mlock = сбой блокировки памяти
|
||||
err_null = нулевой указатель
|
||||
err_numlock = не удалось установить numlock
|
||||
err_pam = pam транзакция не удалась
|
||||
err_pam_abort = pam транзакция прервана
|
||||
err_pam_acct_expired = срок действия аккаунта истёк
|
||||
|
|
@ -29,17 +49,34 @@ err_perm_dir = не удалось изменить текущий катало
|
|||
err_perm_group = не удалось понизить права доступа группы
|
||||
err_perm_user = не удалось понизить права доступа пользователя
|
||||
err_pwnam = не удалось получить информацию о пользователе
|
||||
err_sleep = не удалось выполнить команду sleep
|
||||
err_start = не удалось выполнить команду запуска
|
||||
err_battery = не удалось получить статус батареи
|
||||
err_switch_tty = не удалось переключить tty
|
||||
err_tty_ctrl = передача управления tty не удалась
|
||||
err_no_users = пользователи не найдены
|
||||
err_uid_range = не удалось динамически получить диапазон uid
|
||||
err_user_gid = не удалось установить GID пользователя
|
||||
err_user_init = не удалось инициализировать пользователя
|
||||
err_user_uid = не удалось установить UID пользователя
|
||||
err_xauth = команда xauth не выполнена
|
||||
err_xcb_conn = ошибка подключения xcb
|
||||
err_xsessions_dir = не удалось найти сессионную папку
|
||||
err_xsessions_open = не удалось открыть сессионную папку
|
||||
hibernate = гибернация
|
||||
insert = вставка
|
||||
login = логин
|
||||
logout = logged out
|
||||
logout = вышел из системы
|
||||
no_x11_support = поддержка x11 отключена при компиляции
|
||||
normal = обычный
|
||||
numlock = numlock
|
||||
other = прочие
|
||||
password = пароль
|
||||
restart = перезагрузить
|
||||
shell = shell
|
||||
shell = оболочка
|
||||
shutdown = выключить
|
||||
sleep = сон
|
||||
toggle_password = показать/скрыть пароль
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
|
|
@ -1,45 +1,82 @@
|
|||
authenticating = autentifikacija...
|
||||
brightness_down = smanji osvetljenost
|
||||
brightness_up = povećaj osvetljenost
|
||||
capslock = capslock
|
||||
err_alloc = neuspijesna alokacija memorije
|
||||
custom = prilagođeno
|
||||
custom_info_err_output_long = izlaz predugačak
|
||||
custom_info_err_no_output = nema izlaza
|
||||
custom_info_err_no_output_error = , moguća greška
|
||||
err_alloc = neuspješna alokacija memorije
|
||||
err_args = nije moguće raščlaniti argumente komandne linije
|
||||
err_autologin_session = sesija automatske prijave nije pronađena
|
||||
err_bounds = izvan granica indeksa
|
||||
err_chdir = neuspijesno otvaranje home foldera
|
||||
err_console_dev = neuspijesno pristupanje konzoli
|
||||
err_brightness_change = nije uspela promena osvetljenosti
|
||||
err_chdir = neuspješno otvaranje home foldera
|
||||
err_clock_too_long = niska sata je preduga
|
||||
err_config = nije moguće raščlaniti konfiguracioni fajl
|
||||
err_crawl = nije uspelo skeniranje direktorijuma sesija
|
||||
err_dgn_oob = log poruka
|
||||
err_domain = nevazeci domen
|
||||
err_hostname = neuspijesno trazenje hostname-a
|
||||
err_mlock = neuspijesno zakljucavanje memorije lozinke
|
||||
err_null = null pokazivac
|
||||
err_pam = pam transakcija neuspijesna
|
||||
err_domain = nevažeći domen
|
||||
err_empty_password = prazna lozinka nije dozvoljena
|
||||
err_envlist = nije uspelo dobijanje liste promenljivih okruženja
|
||||
err_get_active_tty = nije uspelo dobijanje aktivnog tty
|
||||
err_hibernate = nije uspelo izvršavanje komande hibernacije
|
||||
err_hostname = neuspješno traženje hostname-a
|
||||
err_inactivity = nije uspelo izvršavanje komande neaktivnosti
|
||||
err_lock_state = nije uspelo dobijanje stanja zaključavanja
|
||||
err_log = nije uspelo otvaranje fajla dnevnika
|
||||
err_mlock = neuspješno zaključavanje memorije lozinke
|
||||
err_null = null pokazivač
|
||||
err_numlock = nije uspelo postavljanje numlock
|
||||
err_pam = pam transakcija neuspješna
|
||||
err_pam_abort = pam transakcija prekinuta
|
||||
err_pam_acct_expired = nalog istekao
|
||||
err_pam_auth = greska pri autentikaciji
|
||||
err_pam_authinfo_unavail = neuspjelo uzimanje informacija o korisniku
|
||||
err_pam_auth = greška pri autentikaciji
|
||||
err_pam_authinfo_unavail = neuspješno uzimanje informacija o korisniku
|
||||
err_pam_authok_reqd = token istekao
|
||||
err_pam_buf = greska bafera memorije
|
||||
err_pam_cred_err = neuspjelo postavljanje kredencijala
|
||||
err_pam_buf = greška bafera memorije
|
||||
err_pam_cred_err = neuspješno postavljanje kredencijala
|
||||
err_pam_cred_expired = kredencijali istekli
|
||||
err_pam_cred_insufficient = nedovoljni kredencijali
|
||||
err_pam_cred_unavail = neuspjelo uzimanje kredencijala
|
||||
err_pam_maxtries = dostignut maksimalan broj pokusaja
|
||||
err_pam_perm_denied = nedozovoljeno
|
||||
err_pam_session = greska sesije
|
||||
err_pam_sys = greska sistema
|
||||
err_pam_cred_unavail = neuspješno uzimanje kredencijala
|
||||
err_pam_maxtries = dostignut maksimalan broj pokušaja
|
||||
err_pam_perm_denied = nedozvoljeno
|
||||
err_pam_session = greška sesije
|
||||
err_pam_sys = greška sistema
|
||||
err_pam_user_unknown = nepoznat korisnik
|
||||
err_path = neuspjelo postavljanje path-a
|
||||
err_perm_dir = neuspjelo mijenjanje foldera
|
||||
err_perm_group = neuspjesno snizavanje dozvola grupe
|
||||
err_perm_user = neuspijesno snizavanje dozvola korisnika
|
||||
err_pwnam = neuspijesno skupljanje informacija o korisniku
|
||||
err_user_gid = neuspijesno postavljanje korisničkog GID-a
|
||||
err_user_init = neuspijensa inicijalizacija korisnika
|
||||
err_user_uid = neuspijesno postavljanje UID-a korisnika
|
||||
err_xsessions_dir = neuspijesno pronalazenje foldera sesija
|
||||
err_xsessions_open = neuspijesno otvaranje foldera sesija
|
||||
err_path = neuspješno postavljanje path-a
|
||||
err_perm_dir = neuspješno mijenjanje foldera
|
||||
err_perm_group = neuspješno snižavanje dozvola grupe
|
||||
err_perm_user = neuspješno snižavanje dozvola korisnika
|
||||
err_pwnam = neuspješno skupljanje informacija o korisniku
|
||||
err_sleep = nije uspelo izvršavanje komande spavanja
|
||||
err_start = nije uspelo izvršavanje komande pokretanja
|
||||
err_battery = nije uspelo učitavanje statusa baterije
|
||||
err_switch_tty = nije uspelo prebacivanje tty
|
||||
err_tty_ctrl = prenos kontrole tty nije uspeo
|
||||
err_no_users = nisu pronađeni korisnici
|
||||
err_uid_range = nije uspelo dinamičko dobijanje opsega uid
|
||||
err_user_gid = neuspješno postavljanje korisničkog GID-a
|
||||
err_user_init = neuspješna inicijalizacija korisnika
|
||||
err_user_uid = neuspješno postavljanje UID-a korisnika
|
||||
err_xauth = komanda xauth nije uspela
|
||||
err_xcb_conn = xcb veza nije uspela
|
||||
err_xsessions_dir = neuspješno pronalaženje foldera sesija
|
||||
err_xsessions_open = neuspješno otvaranje foldera sesija
|
||||
hibernate = hibernacija
|
||||
insert = umetni
|
||||
login = korisnik
|
||||
logout = izlogovan
|
||||
no_x11_support = x11 podrška onemogućena tokom prevođenja
|
||||
normal = normalno
|
||||
numlock = numlock
|
||||
other = ostalo
|
||||
password = lozinka
|
||||
restart = ponovo pokreni
|
||||
shell = shell
|
||||
shutdown = ugasi
|
||||
sleep = uspavaj
|
||||
toggle_password = prikaži/sakrij lozinku
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
82
res/lang/sr_Cyrl.ini
Normal file
82
res/lang/sr_Cyrl.ini
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
authenticating = аутентификација...
|
||||
brightness_down = смањи осветљеност
|
||||
brightness_up = повећај осветљеност
|
||||
capslock = capslock
|
||||
custom = прилагођено
|
||||
custom_info_err_output_long = излаз предугачак
|
||||
custom_info_err_no_output = нема излаза
|
||||
custom_info_err_no_output_error = , могућа грешка
|
||||
err_alloc = неуспешна алокација меморије
|
||||
err_args = није могуће рашчланити аргументе командне линије
|
||||
err_autologin_session = сесија аутоматске пријаве није пронађена
|
||||
err_bounds = изван граница индекса
|
||||
err_brightness_change = није успела промена осветљености
|
||||
err_chdir = неуспешно отварање home фолдера
|
||||
err_clock_too_long = ниска сата је предуга
|
||||
err_config = није могуће рашчланити конфигурациони фајл
|
||||
err_crawl = није успело скенирање директоријума сесија
|
||||
err_dgn_oob = лог порука
|
||||
err_domain = неважећи домен
|
||||
err_empty_password = празна лозинка није дозвољена
|
||||
err_envlist = није успело добијање листе променљивих окружења
|
||||
err_get_active_tty = није успело добијање активног tty
|
||||
err_hibernate = није успело извршавање команде хибернације
|
||||
err_hostname = неуспешно тражење hostname-а
|
||||
err_inactivity = није успело извршавање команде неактивности
|
||||
err_lock_state = није успело добијање стања закључавања
|
||||
err_log = није успело отварање фајла дневника
|
||||
err_mlock = неуспешно закључавање меморије лозинке
|
||||
err_null = null показивач
|
||||
err_numlock = није успело постављање numlock
|
||||
err_pam = pam трансакција неуспешна
|
||||
err_pam_abort = pam трансакција прекинута
|
||||
err_pam_acct_expired = налог истекао
|
||||
err_pam_auth = грешка при аутентикацији
|
||||
err_pam_authinfo_unavail = неуспешно узимање информација о кориснику
|
||||
err_pam_authok_reqd = токен истекао
|
||||
err_pam_buf = грешка бафера меморије
|
||||
err_pam_cred_err = неуспешно постављање акредитива
|
||||
err_pam_cred_expired = акредитиви истекли
|
||||
err_pam_cred_insufficient = недовољни акредитиви
|
||||
err_pam_cred_unavail = неуспешно узимање акредитива
|
||||
err_pam_maxtries = достигнут максималан број покушаја
|
||||
err_pam_perm_denied = недозвољено
|
||||
err_pam_session = грешка сесије
|
||||
err_pam_sys = грешка система
|
||||
err_pam_user_unknown = непознат корисник
|
||||
err_path = неуспешно постављање путање
|
||||
err_perm_dir = неуспешно мењање фолдера
|
||||
err_perm_group = неуспешно снижавање дозвола групе
|
||||
err_perm_user = неуспешно снижавање дозвола корисника
|
||||
err_pwnam = неуспешно прикупљање информација о кориснику
|
||||
err_sleep = није успело извршавање команде спавања
|
||||
err_start = није успело извршавање команде покретања
|
||||
err_battery = није успело учитавање статуса батерије
|
||||
err_switch_tty = није успело пребацивање tty
|
||||
err_tty_ctrl = пренос контроле tty није успео
|
||||
err_no_users = нису пронађени корисници
|
||||
err_uid_range = није успело динамичко добијање опсега uid
|
||||
err_user_gid = неуспешно постављање корисничког GID-а
|
||||
err_user_init = неуспешна иницијализација корисника
|
||||
err_user_uid = неуспешно постављање UID-а корисника
|
||||
err_xauth = команда xauth није успела
|
||||
err_xcb_conn = xcb веза није успела
|
||||
err_xsessions_dir = неуспешно проналажење фолдера сесија
|
||||
err_xsessions_open = неуспешно отварање фолдера сесија
|
||||
hibernate = хибернација
|
||||
insert = уметни
|
||||
login = корисник
|
||||
logout = одјављен
|
||||
no_x11_support = x11 подршка онемогућена током превођења
|
||||
normal = нормално
|
||||
numlock = numlock
|
||||
other = остало
|
||||
password = лозинка
|
||||
restart = поново покрени
|
||||
shell = shell
|
||||
shutdown = угаси
|
||||
sleep = успавај
|
||||
toggle_password = прикажи/сакриј лозинку
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
@ -1,45 +1,82 @@
|
|||
authenticating = autentiserar...
|
||||
brightness_down = minska ljusstyrka
|
||||
brightness_up = öka ljusstyrka
|
||||
capslock = capslock
|
||||
err_alloc = misslyckad minnesallokering
|
||||
err_bounds = utanför banan index
|
||||
custom = anpassad
|
||||
custom_info_err_output_long = utdata för lång
|
||||
custom_info_err_no_output = ingen utdata
|
||||
custom_info_err_no_output_error = , möjligt fel
|
||||
err_alloc = minnesallokering misslyckades
|
||||
err_args = tolkning av kommandoargument misslyckades
|
||||
err_autologin_session = autologin-session hittades inte
|
||||
err_bounds = index-värde utanför intervallet
|
||||
err_brightness_change = ändring av ljusstyrka misslyckades
|
||||
err_chdir = misslyckades att öppna hemkatalog
|
||||
err_console_dev = misslyckades att komma åt konsol
|
||||
err_clock_too_long = klocksträng för lång
|
||||
err_config = tolkning av konfigfil misslyckades
|
||||
err_crawl = genomsökning av sessionskataloger misslyckades
|
||||
err_dgn_oob = loggmeddelande
|
||||
err_domain = okänd domän
|
||||
err_hostname = misslyckades att hämta värdnamn
|
||||
err_mlock = misslyckades att låsa lösenordsminne
|
||||
err_domain = ogitlig domän
|
||||
err_empty_password = tomt lösenord godtas ej
|
||||
err_envlist = hämtning av env-lista misslyckades
|
||||
err_get_active_tty = hämtning av aktiv tty misslyckades
|
||||
err_hibernate = vilolägets kommando misslyckades
|
||||
err_hostname = hämtning av hostname misslyckades
|
||||
err_inactivity = inaktivitetslägets kommando misslyckades
|
||||
err_lock_state = hämtning av låsningsstatus misslyckades
|
||||
err_log = öppning av loggfil misslyckades
|
||||
err_mlock = låsning av lösenordsminne misslyckades
|
||||
err_null = nullpekare
|
||||
err_numlock = inställning av numlock misslyckades
|
||||
err_pam = pam-transaktion misslyckades
|
||||
err_pam_abort = pam-transaktion avbröts
|
||||
err_pam_acct_expired = konto upphört
|
||||
err_pam_auth = autentiseringsfel
|
||||
err_pam_authinfo_unavail = misslyckades att hämta användarinfo
|
||||
err_pam_authok_reqd = token utgången
|
||||
err_pam_buf = minnesbuffer fel
|
||||
err_pam_cred_err = misslyckades att ställa in inloggningsuppgifter
|
||||
err_pam_cred_expired = inloggningsuppgifter upphörda
|
||||
err_pam_acct_expired = kontot har löpt ut
|
||||
err_pam_auth = autentisering misslyckades
|
||||
err_pam_authinfo_unavail = hämtning av användarinformation misslyckades
|
||||
err_pam_authok_reqd = token har löpt ut
|
||||
err_pam_buf = minnesbufferfel
|
||||
err_pam_cred_err = inställning av inloggningsuppgifter misslyckades
|
||||
err_pam_cred_expired = inloggningsuppgifterna har löpt ut
|
||||
err_pam_cred_insufficient = otillräckliga inloggningsuppgifter
|
||||
err_pam_cred_unavail = misslyckades att hämta inloggningsuppgifter
|
||||
err_pam_maxtries = nådde maximal försöksgräns
|
||||
err_pam_perm_denied = åtkomst nekad
|
||||
err_pam_cred_unavail = hämtning av inloggningsuppgifter misslyckades
|
||||
err_pam_maxtries = gränsen för antal försök nådd
|
||||
err_pam_perm_denied = tillstånd nekas
|
||||
err_pam_session = sessionsfel
|
||||
err_pam_sys = systemfel
|
||||
err_pam_user_unknown = okänd användare
|
||||
err_path = misslyckades att ställa in sökväg
|
||||
err_perm_dir = misslyckades att ändra aktuell katalog
|
||||
err_perm_group = misslyckades att nergradera gruppbehörigheter
|
||||
err_perm_user = misslyckades att nergradera användarbehörigheter
|
||||
err_pwnam = misslyckades att hämta användarinfo
|
||||
err_user_gid = misslyckades att ställa in användar-GID
|
||||
err_user_init = misslyckades att initialisera användaren
|
||||
err_user_uid = misslyckades att ställa in användar-UID
|
||||
err_xsessions_dir = misslyckades att hitta sessionskatalog
|
||||
err_xsessions_open = misslyckades att öppna sessionskatalog
|
||||
err_path = inställning av sökväg misslyckades
|
||||
err_perm_dir = byte av nuvarande katalog misslyckades
|
||||
err_perm_group = nedgradering av grupptillstånd misslyckades
|
||||
err_perm_user = nedgradering av användartillstånd misslyckades
|
||||
err_pwnam = hämtning av användarinformation misslyckades
|
||||
err_sleep = strömsparlägets kommando misslyckades
|
||||
err_start = startkommando misslyckades
|
||||
err_battery = hämtning av batteristatus misslyckades
|
||||
err_switch_tty = byte av tty misslyckades
|
||||
err_tty_ctrl = överföring av tty-kontroll misslyckades
|
||||
err_no_users = inga användare hittades
|
||||
err_uid_range = dynamisk hämtning av uid-intervall misslyckades
|
||||
err_user_gid = inställning av användarens GID misslyckades
|
||||
err_user_init = initiering av användare misslyckades
|
||||
err_user_uid = inställning av användarens UID misslyckades
|
||||
err_xauth = xauth-kommando misslyckades
|
||||
err_xcb_conn = xcb-anslutning misslyckades
|
||||
err_xsessions_dir = sessionskatalog hittades inte
|
||||
err_xsessions_open = öppning av sessionskatalog misslyckades
|
||||
hibernate = viloläge
|
||||
insert = infoga
|
||||
login = inloggning
|
||||
logout = utloggad
|
||||
no_x11_support = x11-stöd inaktiverat vid kompilering
|
||||
normal = normal
|
||||
numlock = numlock
|
||||
other = övrig
|
||||
password = lösenord
|
||||
restart = starta om
|
||||
shell = skal
|
||||
shell = shell
|
||||
shutdown = stäng av
|
||||
sleep = viloläge
|
||||
toggle_password = visa/dölj lösenord
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
|
|
@ -1,13 +1,33 @@
|
|||
authenticating = kimlik doğrulanıyor...
|
||||
brightness_down = parlakligi azalt
|
||||
brightness_up = parlakligi arttir
|
||||
capslock = capslock
|
||||
custom = özel
|
||||
custom_info_err_output_long = çıktı çok uzun
|
||||
custom_info_err_no_output = çıktı yok
|
||||
custom_info_err_no_output_error = , olası hata
|
||||
err_alloc = basarisiz bellek ayirma
|
||||
err_args = komut satırı argümanları ayrıştırılamıyor
|
||||
err_autologin_session = otomatik oturum açma oturumu bulunamadı
|
||||
err_bounds = sinirlarin disinda dizin
|
||||
err_brightness_change = parlaklık değiştirilemedi
|
||||
err_chdir = ev klasoru acilamadi
|
||||
err_console_dev = konsola erisilemedi
|
||||
err_clock_too_long = saat dizesi çok uzun
|
||||
err_config = yapılandırma dosyası ayrıştırılamıyor
|
||||
err_crawl = oturum dizinleri taranamadı
|
||||
err_dgn_oob = log mesaji
|
||||
err_domain = gecersiz etki alani
|
||||
err_empty_password = boş parola kullanılamaz
|
||||
err_envlist = ortam değişkenleri listesi alınamadı
|
||||
err_get_active_tty = aktif tty alınamadı
|
||||
err_hibernate = hazırda bekletme komutu çalıştırılamadı
|
||||
err_hostname = ana bilgisayar adi alinamadi
|
||||
err_inactivity = hareketsizlik komutu çalıştırılamadı
|
||||
err_lock_state = kilit durumu alınamadı
|
||||
err_log = günlük dosyası açılamadı
|
||||
err_mlock = parola bellegi kilitlenemedi
|
||||
err_null = bos isaretci hatasi
|
||||
err_numlock = numlock ayarlanamadı
|
||||
err_pam = pam islemi basarisiz oldu
|
||||
err_pam_abort = pam islemi durduruldu
|
||||
err_pam_acct_expired = hesabin suresi dolmus
|
||||
|
|
@ -29,17 +49,34 @@ err_perm_dir = gecerli dizin degistirilemedi
|
|||
err_perm_group = grup izinleri dusurulemedi
|
||||
err_perm_user = kullanici izinleri dusurulemedi
|
||||
err_pwnam = kullanici bilgileri alinamadi
|
||||
err_sleep = uyku komutu çalıştırılamadı
|
||||
err_start = başlatma komutu çalıştırılamadı
|
||||
err_battery = pil durumu yüklenemedi
|
||||
err_switch_tty = tty değiştirilemedi
|
||||
err_tty_ctrl = tty kontrol aktarımı başarısız oldu
|
||||
err_no_users = kullanıcı bulunamadı
|
||||
err_uid_range = uid aralığı dinamik olarak alınamadı
|
||||
err_user_gid = kullanici icin GID ayarlanamadi
|
||||
err_user_init = kullanici oturumu baslatilamadi
|
||||
err_user_uid = kullanici icin UID ayarlanamadi
|
||||
err_xauth = xauth komutu başarısız oldu
|
||||
err_xcb_conn = xcb bağlantısı başarısız oldu
|
||||
err_xsessions_dir = oturumlar klasoru bulunamadi
|
||||
err_xsessions_open = oturumlar klasoru acilamadi
|
||||
hibernate = askiya al
|
||||
insert = ekle
|
||||
login = kullanici
|
||||
logout = oturumdan cikis yapildi
|
||||
no_x11_support = x11 desteği derleme zamanında devre dışı bırakıldı
|
||||
normal = normal
|
||||
numlock = numlock
|
||||
other = baska
|
||||
password = sifre
|
||||
restart = yeniden baslat
|
||||
shell = shell
|
||||
shutdown = makineyi kapat
|
||||
sleep = uykuya al
|
||||
toggle_password = parolayı göster/gizle
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
|
|
@ -1,13 +1,33 @@
|
|||
authenticating = автентифікація...
|
||||
brightness_down = зменшити яскравість
|
||||
brightness_up = збільшити яскравість
|
||||
capslock = capslock
|
||||
custom = власний
|
||||
custom_info_err_output_long = вивід занадто довгий
|
||||
custom_info_err_no_output = немає виводу
|
||||
custom_info_err_no_output_error = , можлива помилка
|
||||
err_alloc = невдале виділення пам'яті
|
||||
err_args = не вдалося розібрати аргументи командного рядка
|
||||
err_autologin_session = сеанс автоматичного входу не знайдено
|
||||
err_bounds = поза межами індексу
|
||||
err_brightness_change = не вдалося змінити яскравість
|
||||
err_chdir = не вдалося відкрити домашній каталог
|
||||
err_console_dev = невдалий доступ до консолі
|
||||
err_clock_too_long = рядок годинника занадто довгий
|
||||
err_config = не вдалося розібрати файл конфігурації
|
||||
err_crawl = не вдалося сканувати каталоги сесій
|
||||
err_dgn_oob = повідомлення журналу (log)
|
||||
err_domain = недійсний домен
|
||||
err_empty_password = порожній пароль не дозволено
|
||||
err_envlist = не вдалося отримати список змінних середовища
|
||||
err_get_active_tty = не вдалося отримати активний tty
|
||||
err_hibernate = не вдалося виконати команду гібернації
|
||||
err_hostname = не вдалося отримати ім'я хосту
|
||||
err_inactivity = не вдалося виконати команду неактивності
|
||||
err_lock_state = не вдалося отримати стан блокування
|
||||
err_log = не вдалося відкрити файл журналу
|
||||
err_mlock = збій блокування пам'яті
|
||||
err_null = нульовий вказівник
|
||||
err_numlock = не вдалося встановити numlock
|
||||
err_pam = невдала pam транзакція
|
||||
err_pam_abort = pam транзакція перервана
|
||||
err_pam_acct_expired = термін дії акаунту вичерпано
|
||||
|
|
@ -29,17 +49,34 @@ err_perm_dir = не вдалося змінити поточний катало
|
|||
err_perm_group = не вдалося понизити права доступу групи
|
||||
err_perm_user = не вдалося понизити права доступу користувача
|
||||
err_pwnam = не вдалося отримати дані користувача
|
||||
err_sleep = не вдалося виконати команду сну
|
||||
err_start = не вдалося виконати команду запуску
|
||||
err_battery = не вдалося завантажити стан акумулятора
|
||||
err_switch_tty = не вдалося переключити tty
|
||||
err_tty_ctrl = передача керування tty не вдалася
|
||||
err_no_users = користувачів не знайдено
|
||||
err_uid_range = не вдалося динамічно отримати діапазон uid
|
||||
err_user_gid = не вдалося змінити GID користувача
|
||||
err_user_init = не вдалося ініціалізувати користувача
|
||||
err_user_uid = не вдалося змінити UID користувача
|
||||
err_xauth = команда xauth не вдалася
|
||||
err_xcb_conn = з'єднання xcb не вдалося
|
||||
err_xsessions_dir = не вдалося знайти каталог сесій
|
||||
err_xsessions_open = не вдалося відкрити каталог сесій
|
||||
hibernate = гібернація
|
||||
insert = вставити
|
||||
login = логін
|
||||
logout = вийти
|
||||
no_x11_support = підтримку x11 вимкнено під час компіляції
|
||||
normal = нормальний
|
||||
numlock = numlock
|
||||
other = інший
|
||||
password = пароль
|
||||
restart = перезавантажити
|
||||
shell = оболонка
|
||||
shutdown = вимкнути
|
||||
sleep = сплячий режим
|
||||
toggle_password = показати/приховати пароль
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
|
|
|
|||
82
res/lang/zh_CN.ini
Normal file
82
res/lang/zh_CN.ini
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
authenticating = 正在认证...
|
||||
brightness_down = 降低亮度
|
||||
brightness_up = 提高亮度
|
||||
capslock = 大写锁定
|
||||
custom = 自定义
|
||||
custom_info_err_output_long = 输出过长
|
||||
custom_info_err_no_output = 无输出
|
||||
custom_info_err_no_output_error = ,可能有错误
|
||||
err_alloc = 内存分配失败
|
||||
err_args = 无法解析命令行参数
|
||||
err_autologin_session = 未找到自动登录会话
|
||||
err_bounds = 索引越界
|
||||
err_brightness_change = 无法更改亮度
|
||||
err_chdir = 无法打开home文件夹
|
||||
err_clock_too_long = 时钟字符串过长
|
||||
err_config = 无法解析配置文件
|
||||
err_crawl = 无法扫描会话目录
|
||||
err_dgn_oob = 日志消息
|
||||
err_domain = 无效的域
|
||||
err_empty_password = 不允许空密码
|
||||
err_envlist = 无法获取环境变量列表
|
||||
err_get_active_tty = 无法获取当前活动的tty
|
||||
err_hibernate = 无法执行休眠命令
|
||||
err_hostname = 获取主机名失败
|
||||
err_inactivity = 无法执行非活动命令
|
||||
err_lock_state = 无法获取锁定状态
|
||||
err_log = 无法打开日志文件
|
||||
err_mlock = 锁定密码存储器失败
|
||||
err_null = 空指针
|
||||
err_numlock = 无法设置numlock
|
||||
err_pam = PAM事件失败
|
||||
err_pam_abort = PAM事务已中止
|
||||
err_pam_acct_expired = 帐户已过期
|
||||
err_pam_auth = 身份验证错误
|
||||
err_pam_authinfo_unavail = 获取用户信息失败
|
||||
err_pam_authok_reqd = 口令已过期
|
||||
err_pam_buf = 内存缓冲区错误
|
||||
err_pam_cred_err = 设置凭据失败
|
||||
err_pam_cred_expired = 凭据已过期
|
||||
err_pam_cred_insufficient = 凭据不足
|
||||
err_pam_cred_unavail = 无法获取凭据
|
||||
err_pam_maxtries = 已达到最大尝试次数限制
|
||||
err_pam_perm_denied = 拒绝访问
|
||||
err_pam_session = 会话错误
|
||||
err_pam_sys = 系统错误
|
||||
err_pam_user_unknown = 未知用户
|
||||
err_path = 无法设置路径
|
||||
err_perm_dir = 更改当前目录失败
|
||||
err_perm_group = 组权限降级失败
|
||||
err_perm_user = 用户权限降级失败
|
||||
err_pwnam = 获取用户信息失败
|
||||
err_sleep = 无法执行睡眠命令
|
||||
err_start = 无法执行启动命令
|
||||
err_battery = 无法加载电池状态
|
||||
err_switch_tty = 无法切换tty
|
||||
err_tty_ctrl = tty控制转移失败
|
||||
err_no_users = 未找到用户
|
||||
err_uid_range = 无法动态获取uid范围
|
||||
err_user_gid = 设置用户GID失败
|
||||
err_user_init = 初始化用户失败
|
||||
err_user_uid = 设置用户UID失败
|
||||
err_xauth = xauth命令失败
|
||||
err_xcb_conn = xcb连接失败
|
||||
err_xsessions_dir = 找不到会话文件夹
|
||||
err_xsessions_open = 无法打开会话文件夹
|
||||
hibernate = 休眠
|
||||
insert = 插入
|
||||
login = 登录
|
||||
logout = 注销
|
||||
no_x11_support = x11支持在编译时被禁用
|
||||
normal = 正常
|
||||
numlock = 数字锁定
|
||||
other = 其他
|
||||
password = 密码
|
||||
restart = 重启
|
||||
shell = shell
|
||||
shutdown = 关机
|
||||
sleep = 睡眠
|
||||
toggle_password = 显示/隐藏密码
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
82
res/lang/zh_TW.ini
Normal file
82
res/lang/zh_TW.ini
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
authenticating = 正在驗證...
|
||||
brightness_down = 降低亮度
|
||||
brightness_up = 提高亮度
|
||||
capslock = 大寫鎖定
|
||||
custom = 自訂
|
||||
custom_info_err_output_long = 輸出過長
|
||||
custom_info_err_no_output = 無輸出
|
||||
custom_info_err_no_output_error = ,可能有錯誤
|
||||
err_alloc = 記憶體配置失敗
|
||||
err_args = 無法解析命令列參數
|
||||
err_autologin_session = 找不到自動登入工作階段
|
||||
err_bounds = 索引超出範圍
|
||||
err_brightness_change = 無法變更亮度
|
||||
err_chdir = 無法開啟家目錄
|
||||
err_clock_too_long = 時鐘字串過長
|
||||
err_config = 無法解析設定檔
|
||||
err_crawl = 無法掃描工作階段目錄
|
||||
err_dgn_oob = 日誌訊息
|
||||
err_domain = 無效的網域
|
||||
err_empty_password = 不允許空密碼
|
||||
err_envlist = 無法取得環境變數清單
|
||||
err_get_active_tty = 無法取得目前使用中的 tty
|
||||
err_hibernate = 無法執行休眠命令
|
||||
err_hostname = 取得主機名稱失敗
|
||||
err_inactivity = 無法執行閒置命令
|
||||
err_lock_state = 無法取得鎖定狀態
|
||||
err_log = 無法開啟日誌檔案
|
||||
err_mlock = 鎖定密碼記憶體失敗
|
||||
err_null = 空指標
|
||||
err_numlock = 無法設定 numlock
|
||||
err_pam = PAM 交易失敗
|
||||
err_pam_abort = PAM 交易已中止
|
||||
err_pam_acct_expired = 帳號已過期
|
||||
err_pam_auth = 驗證錯誤
|
||||
err_pam_authinfo_unavail = 取得使用者資訊失敗
|
||||
err_pam_authok_reqd = 金鑰已過期
|
||||
err_pam_buf = 記憶體緩衝區錯誤
|
||||
err_pam_cred_err = 設定憑證失敗
|
||||
err_pam_cred_expired = 憑證已過期
|
||||
err_pam_cred_insufficient = 憑證不足
|
||||
err_pam_cred_unavail = 無法取得憑證
|
||||
err_pam_maxtries = 已達到最大嘗試次數限制
|
||||
err_pam_perm_denied = 拒絕存取
|
||||
err_pam_session = 工作階段錯誤
|
||||
err_pam_sys = 系統錯誤
|
||||
err_pam_user_unknown = 未知的使用者
|
||||
err_path = 無法設定路徑
|
||||
err_perm_dir = 變更目前目錄失敗
|
||||
err_perm_group = 群組權限降級失敗
|
||||
err_perm_user = 使用者權限降級失敗
|
||||
err_pwnam = 取得使用者資訊失敗
|
||||
err_sleep = 無法執行睡眠命令
|
||||
err_start = 無法執行啟動命令
|
||||
err_battery = 無法載入電池狀態
|
||||
err_switch_tty = 無法切換 tty
|
||||
err_tty_ctrl = tty 控制權移轉失敗
|
||||
err_no_users = 找不到使用者
|
||||
err_uid_range = 無法動態取得 uid 範圍
|
||||
err_user_gid = 設定使用者 GID 失敗
|
||||
err_user_init = 初始化使用者失敗
|
||||
err_user_uid = 設定使用者 UID 失敗
|
||||
err_xauth = xauth 命令失敗
|
||||
err_xcb_conn = xcb 連線失敗
|
||||
err_xsessions_dir = 找不到工作階段資料夾
|
||||
err_xsessions_open = 無法開啟工作階段資料夾
|
||||
hibernate = 休眠
|
||||
insert = 插入
|
||||
login = 登入
|
||||
logout = 登出
|
||||
no_x11_support = x11 支援已在編譯時停用
|
||||
normal = 正常
|
||||
numlock = 數字鎖定
|
||||
other = 其他
|
||||
password = 密碼
|
||||
restart = 重新啟動
|
||||
shell = shell
|
||||
shutdown = 關機
|
||||
sleep = 睡眠
|
||||
toggle_password = 顯示/隱藏密碼
|
||||
wayland = wayland
|
||||
x11 = x11
|
||||
xinitrc = xinitrc
|
||||
8
res/ly-dinit
Normal file
8
res/ly-dinit
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
type = process
|
||||
restart = true
|
||||
smooth-recovery = true
|
||||
command = $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME
|
||||
depends-on = login.target
|
||||
termsignal = HUP
|
||||
# ly needs access to the console while login.target already occupies it
|
||||
options = shares-console
|
||||
7
res/ly-freebsd-wrapper
Normal file
7
res/ly-freebsd-wrapper
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
# On FreeBSD, even if we override the default login program, getty will still
|
||||
# try to append "login -fp root" as arguments to Ly, which is not supported.
|
||||
# To avoid this, we use a wrapper script that ignores these arguments before
|
||||
# actually executing Ly.
|
||||
exec $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME
|
||||
28
res/ly-kmsconvt@.service
Normal file
28
res/ly-kmsconvt@.service
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Default kmsconvt@.service
|
||||
# Modified for Ly by AnErrupTion <anerruption@disroot.org>
|
||||
|
||||
[Unit]
|
||||
Description=TUI display manager on %I using KMSCON
|
||||
After=systemd-user-sessions.service plymouth-quit-wait.service rc-local.service kmsconvt@%i.service
|
||||
Conflicts=getty@%i.service kmsconvt@%i.service ly@%i.service
|
||||
OnFailure=ly@%i.service
|
||||
|
||||
[Service]
|
||||
ExecStart=$PREFIX_DIRECTORY/bin/kmscon --term=linux --font-engine unifont --vt=%I --login -- $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME --use-kmscon-vt
|
||||
Type=idle
|
||||
Restart=always
|
||||
RestartSec=0
|
||||
UtmpIdentifier=%I
|
||||
StandardInput=tty
|
||||
StandardOutput=tty
|
||||
TTYPath=/dev/%I
|
||||
TTYReset=yes
|
||||
TTYVHangup=yes
|
||||
TTYVTDisallocate=yes
|
||||
IgnoreSIGPIPE=no
|
||||
SendSIGHUP=yes
|
||||
|
||||
[Install]
|
||||
Alias=autovt@.service
|
||||
WantedBy=multi-user.target
|
||||
DefaultInstance=tty2
|
||||
|
|
@ -19,17 +19,13 @@ then
|
|||
commandUL="/sbin/agetty"
|
||||
fi
|
||||
|
||||
## Get the tty from the conf file
|
||||
CONFTTY=$(cat /etc/ly/config.ini | sed -n 's/^tty.*=[^1-9]*// p')
|
||||
|
||||
## The execution vars
|
||||
# If CONFTTY is empty then default to 2
|
||||
TTY="tty${CONFTTY:-2}"
|
||||
TTY="tty$DEFAULT_TTY"
|
||||
TERM=linux
|
||||
BAUD=38400
|
||||
# If we don't have getty then we should have agetty
|
||||
command=${commandB:-$commandUL}
|
||||
command_args_foreground="-nl /usr/bin/ly $TTY $BAUD $TERM"
|
||||
command_args_foreground="-nl $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME $TTY $BAUD $TERM"
|
||||
|
||||
depend() {
|
||||
after agetty
|
||||
|
|
|
|||
|
|
@ -7,6 +7,4 @@ fi
|
|||
|
||||
BAUD_RATE=38400
|
||||
TERM_NAME=linux
|
||||
|
||||
auxtty=$(/bin/cat /etc/ly/config.ini 2>/dev/null 1| /bin/sed -n 's/\(^[[:space:]]*tty[[:space:]]*=[[:space:]]*\)\([[:digit:]][[:digit:]]*\)\(.*\)/\2/p')
|
||||
TTY=tty${auxtty:-2}
|
||||
TTY=tty$DEFAULT_TTY
|
||||
|
|
|
|||
|
|
@ -10,4 +10,4 @@ elif [ -x /sbin/agetty -o -x /bin/agetty ]; then
|
|||
GETTY=agetty
|
||||
fi
|
||||
|
||||
exec setsid ${GETTY} ${GETTY_ARGS} -nl /usr/bin/ly "${TTY}" "${BAUD_RATE}" "${TERM_NAME}"
|
||||
exec setsid ${GETTY} ${GETTY_ARGS} -nl $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME "${TTY}" "${BAUD_RATE}" "${TERM_NAME}"
|
||||
|
|
|
|||
2
res/ly-s6/run
Normal file
2
res/ly-s6/run
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/execlineb -P
|
||||
exec agetty -L -8 -n -l $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME tty$DEFAULT_TTY 115200
|
||||
1
res/ly-s6/type
Normal file
1
res/ly-s6/type
Normal file
|
|
@ -0,0 +1 @@
|
|||
longrun
|
||||
65
res/ly-sysvinit
Executable file
65
res/ly-sysvinit
Executable file
|
|
@ -0,0 +1,65 @@
|
|||
#!/bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: ly
|
||||
# Required-Start: $remote_fs $syslog
|
||||
# Required-Stop: $remote_fs $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Ly display manager
|
||||
# Description: Starts and stops the Ly display manager
|
||||
### END INIT INFO
|
||||
#
|
||||
# Author: AnErrupTion <anerruption@disroot.org>
|
||||
#
|
||||
|
||||
PATH=/sbin:/usr/sbin:/bin:/usr/bin
|
||||
DAEMON=/usr/bin/ly
|
||||
TTY=/dev/tty$DEFAULT_TTY
|
||||
PIDFILE=/var/run/ly.pid
|
||||
NAME=ly
|
||||
DESC="Ly display manager"
|
||||
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
log_daemon_msg "Starting $DESC on $TTY..."
|
||||
if [ -f "$PIDFILE" ]; then
|
||||
log_progress_msg "$DESC is already running"
|
||||
log_end_msg 0
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Ensure TTY exists
|
||||
[ -c "$TTY" ] || {
|
||||
log_failure_msg "$TTY does not exist"
|
||||
return 1
|
||||
}
|
||||
|
||||
start-stop-daemon --start --background --make-pidfile --pidfile $PIDFILE \
|
||||
--chdir / --exec /bin/sh -- -c "exec setsid sh -c 'exec <$TTY >$TTY 2>&1 $DAEMON'"
|
||||
log_end_msg $?
|
||||
;;
|
||||
stop)
|
||||
log_daemon_msg "Stopping $DESC..."
|
||||
start-stop-daemon --stop --pidfile $PIDFILE --retry 5
|
||||
RETVAL=$?
|
||||
[ $RETVAL -eq 0 ] && rm -f "$PIDFILE"
|
||||
log_end_msg $RETVAL
|
||||
;;
|
||||
restart)
|
||||
echo "Restarting $DESC..."
|
||||
$0 stop
|
||||
sleep 1
|
||||
$0 start
|
||||
;;
|
||||
status)
|
||||
status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $?
|
||||
;;
|
||||
*)
|
||||
echo "Usage: /etc/init.d/$NAME {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
[Unit]
|
||||
Description=TUI display manager
|
||||
After=systemd-user-sessions.service plymouth-quit-wait.service
|
||||
After=getty@tty2.service
|
||||
Conflicts=getty@tty2.service
|
||||
|
||||
[Service]
|
||||
Type=idle
|
||||
ExecStart=/usr/bin/ly
|
||||
StandardError=journal
|
||||
StandardInput=tty
|
||||
TTYPath=/dev/tty2
|
||||
TTYReset=yes
|
||||
TTYVHangup=yes
|
||||
|
||||
[Install]
|
||||
Alias=display-manager.service
|
||||
46
res/ly@.service
Normal file
46
res/ly@.service
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# systemd is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation; either version 2.1 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Modified for Ly by AnErrupTion <anerruption@disroot.org>
|
||||
|
||||
[Unit]
|
||||
Description=TUI display manager on %I
|
||||
After=systemd-user-sessions.service plymouth-quit-wait.service getty@%i.service
|
||||
Conflicts=getty@%i.service kmsconvt@%i.service ly-kmsconvt@%i.service
|
||||
|
||||
[Service]
|
||||
ExecStart=$PREFIX_DIRECTORY/bin/agetty -nl $PREFIX_DIRECTORY/bin/$EXECUTABLE_NAME %I ${TERM}
|
||||
Type=idle
|
||||
Restart=always
|
||||
RestartSec=0
|
||||
UtmpIdentifier=%I
|
||||
StandardInput=tty
|
||||
StandardOutput=tty
|
||||
TTYPath=/dev/%I
|
||||
TTYReset=yes
|
||||
TTYVHangup=yes
|
||||
TTYVTDisallocate=yes
|
||||
IgnoreSIGPIPE=no
|
||||
SendSIGHUP=yes
|
||||
|
||||
ImportCredential=tty.virtual.%I.agetty.*:agetty.
|
||||
ImportCredential=tty.virtual.%I.login.*:login.
|
||||
ImportCredential=agetty.*
|
||||
ImportCredential=login.*
|
||||
ImportCredential=shell.*
|
||||
|
||||
# Unset locale for the console getty since the console has problems
|
||||
# displaying some internationalized messages.
|
||||
UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION
|
||||
|
||||
[Install]
|
||||
Alias=autovt@.service
|
||||
|
||||
WantedBy=multi-user.target
|
||||
DefaultInstance=tty2
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
#%PAM-1.0
|
||||
|
||||
# OpenPAM (used in FreeBSD) doesn't support prepending "-" for ignoring missing
|
||||
# modules.
|
||||
auth include login
|
||||
account include login
|
||||
password include login
|
||||
8
res/pam.d/ly-freebsd-autologin
Normal file
8
res/pam.d/ly-freebsd-autologin
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#%PAM-1.0
|
||||
|
||||
# OpenPAM (used in FreeBSD) doesn't support prepending "-" for ignoring missing
|
||||
# modules.
|
||||
auth required pam_permit.so
|
||||
account include login
|
||||
password include login
|
||||
session include login
|
||||
16
res/pam.d/ly-linux
Normal file
16
res/pam.d/ly-linux
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#%PAM-1.0
|
||||
|
||||
auth include login
|
||||
-auth optional pam_gnome_keyring.so
|
||||
-auth optional pam_kwallet5.so
|
||||
|
||||
account include login
|
||||
|
||||
password include login
|
||||
-password optional pam_gnome_keyring.so use_authtok
|
||||
|
||||
-session optional pam_systemd.so class=greeter
|
||||
-session optional pam_elogind.so
|
||||
session include login
|
||||
-session optional pam_gnome_keyring.so auto_start
|
||||
-session optional pam_kwallet5.so auto_start
|
||||
16
res/pam.d/ly-linux-autologin
Normal file
16
res/pam.d/ly-linux-autologin
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#%PAM-1.0
|
||||
|
||||
auth required pam_permit.so
|
||||
-auth optional pam_gnome_keyring.so
|
||||
-auth optional pam_kwallet5.so
|
||||
|
||||
account include login
|
||||
|
||||
password include login
|
||||
-password optional pam_gnome_keyring.so use_authtok
|
||||
|
||||
-session optional pam_systemd.so class=greeter
|
||||
-session optional pam_elogind.so
|
||||
session include login
|
||||
-session optional pam_gnome_keyring.so auto_start
|
||||
-session optional pam_kwallet5.so auto_start
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue