mirror of
https://github.com/tmux/tmux.git
synced 2025-06-23 00:00:37 -04:00
Compare commits
715 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
96244dd727 | ||
|
b38b5d3df2 | ||
|
ad3e6ff054 | ||
|
522652913f | ||
|
43e88c892d | ||
|
d82d9468b4 | ||
|
d7f75ac985 | ||
|
1dbceaa790 | ||
|
77ca59acac | ||
|
7be0613657 | ||
|
b9ad9186fe | ||
|
3f4b154b70 | ||
|
ff73c95547 | ||
|
aa0d05f676 | ||
|
545832e9fa | ||
|
a0ac2a5d63 | ||
|
833c7fbf6d | ||
|
faf2a44890 | ||
|
d194ceb604 | ||
|
37a2c98d3d | ||
|
79b02998a6 | ||
|
1ce1e7ef27 | ||
|
7499d925da | ||
|
367f17a4ff | ||
|
3d2b26dcfe | ||
|
6106a0f2c4 | ||
|
1926c1b683 | ||
|
6cc1a607fc | ||
|
1905afb3d8 | ||
|
345a45083d | ||
|
3a3babf597 | ||
|
f367e3a6c8 | ||
|
f2283b0cb5 | ||
|
26081ac125 | ||
|
36fba4cc54 | ||
|
58b4286edb | ||
|
b0b6a6f665 | ||
|
63e5989f53 | ||
|
9da5707d3e | ||
|
55292b72d1 | ||
|
06b1ad6241 | ||
|
1efe41b9b9 | ||
|
1198eed632 | ||
|
0772fe822a | ||
|
f53fac1b56 | ||
|
68f2ac9296 | ||
|
b4f8340f16 | ||
|
d2f73c17d5 | ||
|
5db914b745 | ||
|
cd3d2910ee | ||
|
de7653f5a4 | ||
|
b905039ed2 | ||
|
f0a85d0469 | ||
|
ac2779f4d8 | ||
|
68ffe65499 | ||
|
3e14e7c48b | ||
|
b526e678b5 | ||
|
d3c39375d5 | ||
|
a7991dcbad | ||
|
47dff97834 | ||
|
2905395695 | ||
|
4bf38da4d4 | ||
|
9b37b9285e | ||
|
dfdced2ab1 | ||
|
c2a95a372f | ||
|
ef923d28ff | ||
|
53b0e0bc02 | ||
|
096c4b3e63 | ||
|
b8189d6ee3 | ||
|
f41dc91357 | ||
|
ef0c12ad9e | ||
|
9e1f110db0 | ||
|
483b2b3edb | ||
|
34a35b0f09 | ||
|
aca3ffb30a | ||
|
c3c4524def | ||
|
f101762d1b | ||
|
a541be3951 | ||
|
b7d640e764 | ||
|
969f6a60c3 | ||
|
6703cb85d9 | ||
|
111e16e772 | ||
|
3eb93383a3 | ||
|
817b621d20 | ||
|
5eb30c1543 | ||
|
d4b8635f50 | ||
|
4e4fe3eb39 | ||
|
882fb4d295 | ||
|
eaf70c955b | ||
|
3543d79048 | ||
|
94783addfc | ||
|
91c0de60b4 | ||
|
21f7db4c4d | ||
|
f224d61f37 | ||
|
d938ab5dd7 | ||
|
9a8f46e554 | ||
|
27ee0c9c3b | ||
|
251a87e2d2 | ||
|
084e6ee9ec | ||
|
18331e39bf | ||
|
250c88efdc | ||
|
9a377485be | ||
|
47a56c11f2 | ||
|
c4b9716873 | ||
|
ec119b2f9e | ||
|
5d1a6acc84 | ||
|
80eb460fc9 | ||
|
ef68debc8d | ||
|
4c12ac9fb8 | ||
|
244bb726e2 | ||
|
6ab268c7bb | ||
|
0c9f8ff189 | ||
|
dd7d04be95 | ||
|
58392d29da | ||
|
9260f5dc96 | ||
|
62a6c16b43 | ||
|
31e8d4676a | ||
|
d3cbe00f78 | ||
|
97fe3563fa | ||
|
37ad1e2f6d | ||
|
00894d188d | ||
|
2a5eba7899 | ||
|
bf30492d57 | ||
|
e00853ee82 | ||
|
e75f3a0060 | ||
|
eece41547e | ||
|
c311202b73 | ||
|
e149d29803 | ||
|
190ddda572 | ||
|
6b470c5222 | ||
|
6b32d195e8 | ||
|
bec6ce54c1 | ||
|
106d1c3538 | ||
|
ae8f2208c9 | ||
|
5c3cf2f08b | ||
|
102f34090d | ||
|
99790c90aa | ||
|
6d792e4123 | ||
|
98e322d5a5 | ||
|
01edce40f3 | ||
|
0549f87ccc | ||
|
72cd0eac29 | ||
|
f0c68533c4 | ||
|
9e4a9c51cc | ||
|
6874ec0dcc | ||
|
252f41818e | ||
|
d361f21065 | ||
|
feb090abb3 | ||
|
db771ec6e3 | ||
|
6f7db82b18 | ||
|
67cc7f6dc6 | ||
|
db978db271 | ||
|
363f35f076 | ||
|
49b7276f2a | ||
|
f57131e11b | ||
|
420af9e108 | ||
|
ceaf9b7452 | ||
|
5fd45b3892 | ||
|
64d82d5988 | ||
|
0f308bd18f | ||
|
1365f1ce52 | ||
|
7fc4e54efe | ||
|
c98c08c232 | ||
|
990c724bd8 | ||
|
b82c7b40b0 | ||
|
70299c6646 | ||
|
ea6c5053c8 | ||
|
3f4fd45d5c | ||
|
7c7e88253e | ||
|
bb94a5437d | ||
|
273f9b2027 | ||
|
b6d0b4be30 | ||
|
2dce45f427 | ||
|
a3ede3106a | ||
|
dd9722184b | ||
|
a854e36f2b | ||
|
be594ff8a4 | ||
|
c66628e52b | ||
|
350a151ee4 | ||
|
d35458e3fe | ||
|
d6883c0266 | ||
|
563ed05353 | ||
|
f527412d9b | ||
|
713cacab1e | ||
|
846f813565 | ||
|
c26d71d3e9 | ||
|
c8677d3272 | ||
|
596ea62dc3 | ||
|
1fe30bb2e8 | ||
|
809d659e64 | ||
|
09f4e43189 | ||
|
679bbdcf8f | ||
|
a0c79aa87b | ||
|
46f3846659 | ||
|
bbc3cc558c | ||
|
c8bd42de16 | ||
|
62e15e905b | ||
|
125a7b9177 | ||
|
895044c52b | ||
|
40c01c2d37 | ||
|
911d768b71 | ||
|
71a503e40c | ||
|
487b0ee124 | ||
|
eaec0a48f4 | ||
|
fdbc6cdea5 | ||
|
9623ec3ee4 | ||
|
63582c154c | ||
|
914815e70f | ||
|
354926a956 | ||
|
df303770ea | ||
|
a269c33a54 | ||
|
eb04f9314b | ||
|
a9d0461839 | ||
|
71aa4fe767 | ||
|
cfd3c02306 | ||
|
35104059ed | ||
|
934035db71 | ||
|
8ff65230e1 | ||
|
cb00e869ea | ||
|
f8b56fdc3f | ||
|
2d8b6fcf23 | ||
|
6ddee22f81 | ||
|
66c738bc10 | ||
|
bcc47add38 | ||
|
d326c51d79 | ||
|
940fdf7ed0 | ||
|
4f5d6d97d3 | ||
|
00412b570b | ||
|
6a35b8ad07 | ||
|
735082b7c2 | ||
|
5b7bdc82c8 | ||
|
a3dea81b49 | ||
|
486221f206 | ||
|
9528d7470b | ||
|
933d6b90ed | ||
|
a869693405 | ||
|
114977dd25 | ||
|
aba8ffbe7b | ||
|
455a2b3705 | ||
|
e0638c48cd | ||
|
41f6b691e3 | ||
|
0f30fa34e3 | ||
|
e486f5ffdc | ||
|
5a68730e2f | ||
|
16b44d2a5b | ||
|
34775fc235 | ||
|
f2fd8c854a | ||
|
9f2a853d87 | ||
|
7a78cdf78c | ||
|
2df15ad08c | ||
|
780a87be9a | ||
|
1e303b6a9d | ||
|
8d2aee34ab | ||
|
05116cefe6 | ||
|
ce4be58eef | ||
|
8d6eb4be40 | ||
|
38c38f1c64 | ||
|
ddf6af79e3 | ||
|
826ba515be | ||
|
729bded8bf | ||
|
b95a06d2d4 | ||
|
157d748949 | ||
|
1c1f4c1219 | ||
|
17bab32794 | ||
|
25b1cc1e8e | ||
|
89adec0ca5 | ||
|
f95d055e04 | ||
|
7b148f7b5b | ||
|
9bd039d1bc | ||
|
7c30056d96 | ||
|
ac44566c9c | ||
|
64f1076d97 | ||
|
489c69f5ed | ||
|
f897049935 | ||
|
d8b66110f7 | ||
|
8b1a3bb2e5 | ||
|
37771a5a8d | ||
|
3a8a31d8d2 | ||
|
c36ffcbe56 | ||
|
3d8ead8a97 | ||
|
141cd78407 | ||
|
d0c8124661 | ||
|
2917bc5274 | ||
|
2d1e93447e | ||
|
31b6c9356c | ||
|
9e2a7c28f5 | ||
|
73b2277af8 | ||
|
a84c109604 | ||
|
4823acca8f | ||
|
34807388b0 | ||
|
08be883297 | ||
|
13bd0e46db | ||
|
6e9a914014 | ||
|
7990e5fa8f | ||
|
4860a58d07 | ||
|
99af9f23bd | ||
|
9ebbe2cca7 | ||
|
692bae9ea6 | ||
|
a6645c4de4 | ||
|
06292baadc | ||
|
ceda0a68ae | ||
|
4fa90c9acf | ||
|
c7e61a01e5 | ||
|
963e824f5f | ||
|
de6bce057a | ||
|
937ba1d8dd | ||
|
651891c3bd | ||
|
4008e2ff6d | ||
|
b88130d24b | ||
|
fc7ee7efc7 | ||
|
d0eb3fe543 | ||
|
2ac0faf119 | ||
|
775789fbd5 | ||
|
7b6fbe7262 | ||
|
109d2bda1a | ||
|
ddd4e57c65 | ||
|
3c2621b41b | ||
|
5039be657c | ||
|
d02254f754 | ||
|
aa1353947e | ||
|
171004dfd0 | ||
|
c773fe89e7 | ||
|
093b5a5518 | ||
|
db1665868f | ||
|
c07e856d24 | ||
|
692ce59bce | ||
|
9e7c1aee48 | ||
|
4c2eedca5a | ||
|
ac6c1e9589 | ||
|
0903790b00 | ||
|
03de52653e | ||
|
da06719309 | ||
|
fc84097379 | ||
|
4fd725c6e1 | ||
|
d39dcea30a | ||
|
bfd65398a9 | ||
|
452d987e0e | ||
|
8ef899f315 | ||
|
a18d1146aa | ||
|
5b5004e5ac | ||
|
6ff8f8fbf9 | ||
|
c9616700ca | ||
|
4c928dce74 | ||
|
fb37d52dde | ||
|
363d9c401e | ||
|
8643ece345 | ||
|
9ba433e521 | ||
|
3823fa2c57 | ||
|
0a8571b6fe | ||
|
ea9f416c99 | ||
|
036d8993e6 | ||
|
e8530c9fee | ||
|
dd4c0109a9 | ||
|
43530d4397 | ||
|
553d4cba79 | ||
|
c62a9ca16b | ||
|
424f13fe13 | ||
|
4bb6da75ba | ||
|
a28175dbfd | ||
|
fc204bb5e5 | ||
|
6207a45139 | ||
|
3c3643f580 | ||
|
89c1c43ef9 | ||
|
2e9d7ebf15 | ||
|
d8ddeec7db | ||
|
6f0254e6a8 | ||
|
aa17f0e0c1 | ||
|
0ae8b681b2 | ||
|
6c0067c103 | ||
|
5458cb2850 | ||
|
0c374868ca | ||
|
bf5d3f2e26 | ||
|
d5ef837f63 | ||
|
b79e28b2c3 | ||
|
8ffd5458ff | ||
|
b54e1fc4f7 | ||
|
bdb6321229 | ||
|
bd29a48b56 | ||
|
f3f1c3db58 | ||
|
608d113486 | ||
|
9ae69c3795 | ||
|
0960862950 | ||
|
44ad25b367 | ||
|
40b97b1715 | ||
|
4bdb855020 | ||
|
ea7136fb83 | ||
|
428f8a9b28 | ||
|
84faada25b | ||
|
55d0abad89 | ||
|
7d91b4b90b | ||
|
66369416fc | ||
|
001e26d0bb | ||
|
2e39d698d2 | ||
|
e809c2ec35 | ||
|
4266d3efc8 | ||
|
40a20bc8ae | ||
|
f7bf7e9671 | ||
|
008ecd4592 | ||
|
73a2b12088 | ||
|
605bf21ff2 | ||
|
f028445407 | ||
|
bdf8e614af | ||
|
4dea352dee | ||
|
151875d144 | ||
|
88fd1f00b8 | ||
|
1a14d6d2e1 | ||
|
381c00a74e | ||
|
5aadee6df4 | ||
|
a5545dbc9f | ||
|
fdf465925e | ||
|
36e1ac6556 | ||
|
ffa376edf7 | ||
|
b777780720 | ||
|
347cd0b5f8 | ||
|
0ca28b362e | ||
|
51b80b985e | ||
|
789a98982e | ||
|
b202a2f1b5 | ||
|
7e79108f8a | ||
|
f09cde2542 | ||
|
9f9156c030 | ||
|
d394293ba5 | ||
|
c57a09269b | ||
|
8191c58737 | ||
|
f68d35c529 | ||
|
c02bc4dbe9 | ||
|
4872811ba7 | ||
|
1a1290f30b | ||
|
7be7ca7195 | ||
|
32197fa52d | ||
|
9653a52a6a | ||
|
d60c8942ce | ||
|
c99f9e92e0 | ||
|
e26356607e | ||
|
43e5e80343 | ||
|
1742138f05 | ||
|
c767d62329 | ||
|
7ad29b9831 | ||
|
d209fe9b1e | ||
|
c5542637d7 | ||
|
e7c829fc67 | ||
|
579829eef2 | ||
|
3aa20f6e75 | ||
|
f78279bb2e | ||
|
a99d7c6314 | ||
|
cf1ed67fcc | ||
|
1aec420465 | ||
|
16e4b39359 | ||
|
9456258ccc | ||
|
c41d59f232 | ||
|
d682ef88e6 | ||
|
c1e6e54e6e | ||
|
62f657845e | ||
|
a9841a6d1e | ||
|
70ecf17f85 | ||
|
71d453f169 | ||
|
071849f82f | ||
|
52084b2948 | ||
|
dfbc6b1888 | ||
|
e3a8b8434c | ||
|
8636848e63 | ||
|
3d93b0c52e | ||
|
d9942c769e | ||
|
6a45e6c256 | ||
|
b770a429c6 | ||
|
11e69f6025 | ||
|
57837bbf67 | ||
|
4c60afde78 | ||
|
ed689cd54e | ||
|
7a44984069 | ||
|
dee72ed41f | ||
|
1071ef8fc5 | ||
|
2b535bc173 | ||
|
b791f00bf1 | ||
|
3e82ad5340 | ||
|
7b1030293c | ||
|
a5fd80bbc3 | ||
|
fda3937734 | ||
|
b13c230749 | ||
|
715f39a53a | ||
|
2f74e811f1 | ||
|
828efc7bcf | ||
|
84936b832f | ||
|
c8494dff7b | ||
|
8fcc212e7a | ||
|
0e281530cb | ||
|
efded95ed7 | ||
|
18870913c5 | ||
|
63b7282377 | ||
|
269dab4b3e | ||
|
4ece43a029 | ||
|
8b3e2eab5a | ||
|
e4c4ceb286 | ||
|
b7e22d00b4 | ||
|
1a11c972ae | ||
|
43b841f188 | ||
|
f9b0460840 | ||
|
659d876cd5 | ||
|
ac43186dff | ||
|
e79fb214f8 | ||
|
9cf58d1a52 | ||
|
a2a02fd7d7 | ||
|
237ee6f231 | ||
|
4e57894e85 | ||
|
8c9fbbf4f3 | ||
|
3f3d61bd58 | ||
|
80d4f4afc6 | ||
|
2546216019 | ||
|
ff8882a24f | ||
|
ffe2410639 | ||
|
9e14c1f88d | ||
|
29a5dfc7c0 | ||
|
645bf8b3ab | ||
|
f41c536ff3 | ||
|
bdd05bdbd3 | ||
|
fe385b180f | ||
|
0eb5d25453 | ||
|
1d98394b41 | ||
|
149d9cc851 | ||
|
204d8f31d7 | ||
|
fbe6fe7f55 | ||
|
168eab11a7 | ||
|
41b318ac7c | ||
|
bf636d9575 | ||
|
39d41d0810 | ||
|
9d8131c190 | ||
|
0ff991b25f | ||
|
15c70e562c | ||
|
8f34504736 | ||
|
551e0c36d9 | ||
|
48eba4c195 | ||
|
9f605178c3 | ||
|
bcafe51378 | ||
|
280fe77edd | ||
|
b9524f5b72 | ||
|
a2018b2c3f | ||
|
c21af7e446 | ||
|
d73078838d | ||
|
22eb0334c3 | ||
|
a9ac614691 | ||
|
1d0f68dee9 | ||
|
ac4bb89d43 | ||
|
b55f34029a | ||
|
907f58cc3c | ||
|
023b0f76c3 | ||
|
ddaeebc213 | ||
|
1262e685b8 | ||
|
0bd78b42c0 | ||
|
4a0126f7fb | ||
|
7acc8d703d | ||
|
77118f3a9f | ||
|
e25926d3c4 | ||
|
0cb75f1332 | ||
|
93b1b78150 | ||
|
493922dc4b | ||
|
f10854cfc5 | ||
|
993e7a937f | ||
|
f5af3cfb21 | ||
|
e7e112fbd0 | ||
|
c42087c789 | ||
|
3aa458ea63 | ||
|
c4a6f403bb | ||
|
9789ea3fb4 | ||
|
f416ae1c12 | ||
|
d578cf8d3f | ||
|
789cb91f31 | ||
|
eb1f8d70a7 | ||
|
42895efac3 | ||
|
483cc77c1c | ||
|
9b1ea8b16d | ||
|
b41892622d | ||
|
9051220243 | ||
|
c0031f8b85 | ||
|
c1a30ed995 | ||
|
565de3f54b | ||
|
153ae758c9 | ||
|
5086377f30 | ||
|
7c0789d2d2 | ||
|
2a32565e0c | ||
|
7ced0a03d2 | ||
|
cb51942669 | ||
|
093fb53773 | ||
|
21e00e4635 | ||
|
09afc6c8ee | ||
|
a41a927441 | ||
|
3fe01ff09c | ||
|
b5ab4d2c13 | ||
|
4d79d463ef | ||
|
7cb48fc40b | ||
|
8bd17bff49 | ||
|
3b3f42053a | ||
|
70ff8cfe1e | ||
|
6249a4b866 | ||
|
7e497c7f23 | ||
|
1536b7e206 | ||
|
e46d0632a5 | ||
|
20da167377 | ||
|
fe475bd856 | ||
|
079f48e8a6 | ||
|
aaa043a20f | ||
|
48f41e4a41 | ||
|
6fb80527f3 | ||
|
f86eba2129 | ||
|
50f4e0fac9 | ||
|
77c135349a | ||
|
c449512be4 | ||
|
17290b9121 | ||
|
3be369522b | ||
|
dbfbd8a195 | ||
|
9614f51560 | ||
|
36896f6dd0 | ||
|
2d08235987 | ||
|
2291045116 | ||
|
9dd1f442c5 | ||
|
8edece2cdb | ||
|
d001a94d7b | ||
|
c2580cfe24 | ||
|
dafd6f462f | ||
|
2111142cf1 | ||
|
0fc961b22e | ||
|
5ce34add77 | ||
|
934f357149 | ||
|
ff2766b024 | ||
|
9ef854f5a9 | ||
|
a10452be2d | ||
|
9cc8e40aa0 | ||
|
f49f92737f | ||
|
19344ec890 | ||
|
a2cc601c3d | ||
|
9ab1ba36cd | ||
|
f03c3ca6c3 | ||
|
0a0ded3268 | ||
|
6da520c5a1 | ||
|
68dc9af9ac | ||
|
f7b30ed3d4 | ||
|
e867528209 | ||
|
038dfb27a8 | ||
|
416c27c995 | ||
|
19344efa78 | ||
|
9c34aad21c | ||
|
7c2dcd7238 | ||
|
03149bf7f6 | ||
|
497021d0db | ||
|
cfdc5b62ad | ||
|
9b08e5139b | ||
|
e139f977b1 | ||
|
9abf5d9fe5 | ||
|
273577ba0a | ||
|
e15058e60f | ||
|
de5cd54124 | ||
|
c6cf09450a | ||
|
c6e7568471 | ||
|
7b8ececd8d | ||
|
42ba6c1b22 | ||
|
9a2fdf8fd4 | ||
|
00812c9053 | ||
|
33c59100ae | ||
|
36d904011a | ||
|
9d9445a48e | ||
|
a8da24771c | ||
|
ab1d18d00f | ||
|
ee431d482a | ||
|
e06c09889c | ||
|
86dfbda0e4 | ||
|
697cebb4c1 | ||
|
3c65475561 | ||
|
dc6bc0e95a | ||
|
b130e951cc | ||
|
9e19f132f2 | ||
|
1afe22086f | ||
|
8e8b9865d1 | ||
|
a39827a85c | ||
|
9e03df5500 | ||
|
dd602eaa61 | ||
|
d0d2c39dec | ||
|
57fec74966 | ||
|
9360e0ef32 | ||
|
f08c019d41 | ||
|
c3af8f6b16 | ||
|
01c4919f5f | ||
|
cdacc12ce3 | ||
|
d8c527a5f9 | ||
|
b22edcf3a5 | ||
|
786cff8db9 | ||
|
b63afaea61 | ||
|
9c89f7c2af | ||
|
d46870ede5 | ||
|
a888ce9963 | ||
|
8ff3091d16 | ||
|
d9f84854ac | ||
|
89fe2680a9 | ||
|
7cee982f90 | ||
|
42358cc521 | ||
|
06869ff22f | ||
|
616bde08ac | ||
|
6d0828b81c | ||
|
42ddf02ffc | ||
|
56390e0a39 | ||
|
18a5835aff | ||
|
67960dcc9a | ||
|
810daefdd1 | ||
|
ccc9dc3bb4 | ||
|
be2eb57d62 | ||
|
c07d582e24 | ||
|
afb3a5fe71 | ||
|
020c403dff | ||
|
e77e11ec6b | ||
|
3edda3c5e7 | ||
|
1184dc08d4 | ||
|
18838fbc87 | ||
|
be2617036f | ||
|
0f6227f46b | ||
|
c1ac007576 | ||
|
201a8d8e7e | ||
|
b566cd57bf |
5
.github/CONTRIBUTING.md
vendored
5
.github/CONTRIBUTING.md
vendored
@ -77,3 +77,8 @@ The log files are:
|
||||
- `tmux-out*.log`: output log file.
|
||||
|
||||
Please attach the log files to your issue.
|
||||
|
||||
## What does it mean if an issue is closed?
|
||||
|
||||
All it means is that work on the issue is not planned for the near future. See
|
||||
the issue's comments to find out if contributions would be welcome.
|
||||
|
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
@ -1,3 +1,12 @@
|
||||
---
|
||||
name: Use this issue template
|
||||
about: Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
### Issue description
|
||||
|
||||
Please read https://github.com/tmux/tmux/blob/master/.github/CONTRIBUTING.md
|
2
.github/README.md
vendored
2
.github/README.md
vendored
@ -79,7 +79,7 @@ A small example configuration is in `example_tmux.conf`.
|
||||
|
||||
And a bash(1) completion file at:
|
||||
|
||||
https://github.com/imomaliev/tmux-bash-completion
|
||||
https://github.com/scop/bash-completion/blob/main/completions/tmux
|
||||
|
||||
For debugging, run tmux with `-v` or `-vv` to generate server and client log
|
||||
files in the current directory.
|
||||
|
27
.github/workflows/lock.yml
vendored
27
.github/workflows/lock.yml
vendored
@ -3,21 +3,32 @@ name: 'Lock Threads'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
|
||||
concurrency:
|
||||
group: lock-threads
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
action:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v2
|
||||
- uses: dessant/lock-threads@v5
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-lock-inactive-days: '30'
|
||||
pr-lock-inactive-days: '60'
|
||||
issue-lock-comment: >
|
||||
issue-inactive-days: '30'
|
||||
issue-comment: >
|
||||
This issue has been automatically locked since there
|
||||
has not been any recent activity after it was closed.
|
||||
Please open a new issue for related bugs.
|
||||
pr-lock-comment: >
|
||||
pr-inactive-days: '60'
|
||||
pr-comment: >
|
||||
This pull request has been automatically locked since there
|
||||
has not been any recent activity after it was closed.
|
||||
Please open a new issue for related bugs.
|
||||
discussion-inactive-days: '60'
|
||||
discussion-comment: >
|
||||
This discussion has been automatically locked since there
|
||||
has not been any recent activity after it was closed.
|
||||
|
35
.gitignore
vendored
35
.gitignore
vendored
@ -1,23 +1,24 @@
|
||||
*.o
|
||||
*~
|
||||
*.diff
|
||||
*.patch
|
||||
*.core
|
||||
core
|
||||
tags
|
||||
*.dSYM
|
||||
*.diff
|
||||
*.o
|
||||
*.patch
|
||||
*.swp
|
||||
*~
|
||||
.deps/
|
||||
compat/.dirstamp
|
||||
aclocal.m4
|
||||
autom4te.cache/
|
||||
config.log
|
||||
config.status
|
||||
etc/
|
||||
tmux
|
||||
.dirstamp
|
||||
Makefile
|
||||
Makefile.in
|
||||
configure
|
||||
tmux.1.*
|
||||
*.dSYM
|
||||
aclocal.m4
|
||||
autom4te.cache/
|
||||
cmd-parse.c
|
||||
compat/.dirstamp
|
||||
config.log
|
||||
config.status
|
||||
configure
|
||||
core
|
||||
etc/
|
||||
fuzz/*-fuzzer
|
||||
.dirstamp
|
||||
tags
|
||||
tmux
|
||||
tmux.1.*
|
||||
|
206
CHANGES
206
CHANGES
@ -1,3 +1,205 @@
|
||||
CHANGES FROM 3.4 TO 3.5
|
||||
|
||||
* Revamp extended keys support to more closely match xterm and support mode 2
|
||||
as well as mode 1. This is a substantial change to key handling which changes
|
||||
tmux to always request mode 2 from parent terminal, changes to an unambiguous
|
||||
internal representation of keys, and adds an option (extended-keys-format) to
|
||||
control the format similar to the xterm(1) formatOtherKeys resource.
|
||||
|
||||
* Clear an overlay (popup or menu) when command prompt is entered.
|
||||
|
||||
* Add copy-mode -d flag to scroll a page down if in copy mode already (matching
|
||||
-e).
|
||||
|
||||
* Display hyperlinks in copy mode and add copy_cursor_hyperlink format to get
|
||||
the hyperlink under the cursor.
|
||||
|
||||
* Add a prefix timeout option.
|
||||
|
||||
* Mouse move keys are not useful as key bindings because we do not turn them on
|
||||
unless the application requests them. Ignore them so they do not cause the
|
||||
prefix to be canceled
|
||||
|
||||
* Add search_count and search_count_partial formats in copy mode.
|
||||
|
||||
* Do not reset mouse pane if clicked on status line,
|
||||
|
||||
* Add mirrored versions of the main-horizontal and main-vertical layouts where
|
||||
the main pane is bottom or right instead of top or left.
|
||||
|
||||
* Allow REP to work with Unicode characters.
|
||||
|
||||
* Fix size calculation of terminators for clipboard escape sequences.
|
||||
|
||||
* Treat CRLF as LF in config files where it is easy to do so.
|
||||
|
||||
* The Linux console has some bugs with bright colours, so add some workarounds
|
||||
for it.
|
||||
|
||||
* If built with systemd, remove some environment variables it uses.
|
||||
|
||||
* Adjust the logic when deleting last buffer to better preserve the selection:
|
||||
if selecting the element below the deleted one fails (because as the last
|
||||
one), select the one above it instead.
|
||||
|
||||
* Add --enable-jemalloc to build with jemalloc memory allocator (since glibc
|
||||
malloc is so poor).
|
||||
|
||||
* Add a way (refresh-client -r) for control mode clients to provide OSC 10 and
|
||||
11 responses to tmux so they can set the default foreground and background
|
||||
colours.
|
||||
|
||||
* Add N to search backwards in tree modes.
|
||||
|
||||
* Use default-shell for command prompt, #() and popups.
|
||||
|
||||
* Revert part of a change intended to improve search performance by skipping
|
||||
parts of lines already searched, but which in fact skipped the ends of lines
|
||||
altogether.
|
||||
|
||||
* Add a command-error hook when a command fails.
|
||||
|
||||
* Add an option allow-set-title to forbid applications from changing the pane
|
||||
title.
|
||||
|
||||
* Correct handling of mouse up events (don't ignore all but the last released
|
||||
button), and always process down event for double click.
|
||||
|
||||
* Fix a crash if focusing a pane that is exiting.
|
||||
|
||||
* Pick newest session (as documented) when looking for next session for
|
||||
detach-on-destroy.
|
||||
|
||||
* Reduce default escape-time to 10 milliseconds.
|
||||
|
||||
* Add display-menu -M to always turn mouse on in a menu.
|
||||
|
||||
* Look for feature code 21 for DECSLRM and 28 for DECFRA in the device
|
||||
attributes and also accept level 1.
|
||||
|
||||
* Fix crash if built with SIXEL and the SIXEL colour register is invalid; also
|
||||
remove SIXEL images before reflow.
|
||||
|
||||
* Do not notify window-layout-changed if the window is about to be destroyed.
|
||||
|
||||
* Do not consider a selection present if it is empty for the selection_active
|
||||
and selection_present format variables.
|
||||
|
||||
* Fix split-window -p.
|
||||
|
||||
CHANGES FROM 3.3a TO 3.4
|
||||
|
||||
* Add options keep-last and keep-group to destroy-unattached to keep the last
|
||||
session whether in a group.
|
||||
|
||||
* Don't allow paste-buffer into dead panes.
|
||||
|
||||
* Add -t to source-file.
|
||||
|
||||
* Rewrite combined character handling to be more consistent and to support
|
||||
newer Unicode combined characters.
|
||||
|
||||
* Add basic support for SIXEL if built with --enable-sixel.
|
||||
|
||||
* Add a session, pane and user mouse range types for the status line and add
|
||||
format variables for mouse_status_line and mouse_status_range so they can be
|
||||
associated with different commands in the key bindings.
|
||||
|
||||
* Add flag (-o) to next-prompt/previous-prompt to go to OSC 133 command output.
|
||||
|
||||
* Add options and flags for menu styles (menu-style, menu-border-style) similar
|
||||
to those existing for popups.
|
||||
|
||||
* Add support for marking lines with a shell prompt based on the OSC 133 extension.
|
||||
|
||||
* Check for libterminfo for NetBSD.
|
||||
|
||||
* Add "us" to styles for underscore colour.
|
||||
|
||||
* Add flags (-c and -y) to change the confirm key and default behaviour of
|
||||
confirm-before.
|
||||
|
||||
* Use ncurses' new tparm_s function (added in 6.4-20230424) instead of tparm so
|
||||
it does not object to string arguments in c apabilities it doesn't already
|
||||
know. Also ignore errors from tparm if using previous ncurses versions.
|
||||
|
||||
* Set default lock command to vlock on Linux if present at build time.
|
||||
|
||||
* Discard mouse sequences that have the right form but actually are invalid.
|
||||
|
||||
* Add support for spawning panes in separate cgroups with systemd and a
|
||||
configure flag (--disable-cgroups) to turn off.
|
||||
|
||||
* Add a format (pane_unseen_changes) to show if there are unseen changes while
|
||||
in a mode.
|
||||
|
||||
* Remove old buffer when renaming rather than complaining.
|
||||
|
||||
* Add an L modifier like P, W, S to loop over clients.
|
||||
|
||||
* Add -f to list-clients like the other list commands.
|
||||
|
||||
* Extend display-message to work for control clients.
|
||||
|
||||
* Add a flag to display-menu to select the manu item selected when the menu is
|
||||
open.
|
||||
|
||||
* Have tmux recognise pasted text wrapped in bracket paste sequences, rather
|
||||
than only forwarding them to the program inside.
|
||||
|
||||
* Have client return 1 if process is interrupted to an input pane.
|
||||
|
||||
* Query the client terminal for foreground and background colours and if OSC 10
|
||||
or 11 is received but no colour has been set inside tmux, return the colour
|
||||
from the first attached client.
|
||||
|
||||
* Add send-keys -K to handle keys directly as if typed (so look up in key
|
||||
table).
|
||||
|
||||
* Process escape sequences in show-buffer.
|
||||
|
||||
* Add a -l flag to display-message to disable format expansion.
|
||||
|
||||
* Add paste-buffer-deleted notification and fix name of paste-buffer-changed.
|
||||
|
||||
* Do not attempt to connect to the socket as a client if systemd is active.
|
||||
|
||||
* Add scroll-top and scroll-bottom commands to scroll so cursor is at top or
|
||||
bottom.
|
||||
|
||||
* Add a -T flag to capture-pane to stop at the last used cell instead of the
|
||||
full width. Restore the previous behaviour by making it default to off unless
|
||||
-J is used.
|
||||
|
||||
* Add message-line option to control where message and prompt go.
|
||||
|
||||
* Notification when a paste buffer is deleted.
|
||||
|
||||
* Add a Nobr terminfo(5) capability to tell tmux the terminal does not use bright
|
||||
colours for bold.
|
||||
|
||||
* Change g and G to go to top and bottom in menus.
|
||||
|
||||
* Add a third state "all" to allow-passthrough to work even in invisible panes.
|
||||
|
||||
* Add support for OSC 8 hyperlinks.
|
||||
|
||||
* Store the time lines are scrolled into history and display in copy mode.
|
||||
|
||||
* Add a %config-error reply to control mode for configuration file errors since
|
||||
reporting them in view mode is useless.
|
||||
|
||||
* A new feature flag (ignorefkeys) to ignore terminfo(5) function key
|
||||
definitions for rxvt.
|
||||
|
||||
* Pass through first argument to OSC 52 (which clipboards to set) if the
|
||||
application provides it.
|
||||
|
||||
* Expand arguments to send-keys, capture-pane, split-window, join-pane where it
|
||||
makes sense to do so.
|
||||
|
||||
* Ignore named buffers when choosing a buffer if one is not specified by the user.
|
||||
|
||||
CHANGES FROM 3.3 TO 3.3a
|
||||
|
||||
* Do not crash when run-shell produces output from a config file.
|
||||
@ -187,7 +389,7 @@ CHANGES FROM 3.2 TO 3.2a
|
||||
* Do not expand the filename given to -f so it can contain colons.
|
||||
|
||||
* Fixes for problems with extended keys and modifiers, scroll region,
|
||||
source-file, crosscompiling, format modifiers and other minor issues.
|
||||
source-file, cross compiling, format modifiers and other minor issues.
|
||||
|
||||
CHANGES FROM 3.1c TO 3.2
|
||||
|
||||
@ -1302,7 +1504,7 @@ Incompatible Changes
|
||||
|
||||
bind -Tcopy-mode C-r command-prompt -i -p'search up' "send -X search-backward-incremental '%%'"
|
||||
|
||||
There are also some new commmands available with send -X, such as
|
||||
There are also some new commands available with send -X, such as
|
||||
copy-pipe-and-cancel.
|
||||
* set-remain-on-exit has gone -- can be achieved with hooks instead.
|
||||
* Hooks: before hooks have been removed and only a selection of commands now
|
||||
|
17
Makefile.am
17
Makefile.am
@ -1,5 +1,3 @@
|
||||
# Makefile.am
|
||||
|
||||
# Obvious program stuff.
|
||||
bin_PROGRAMS = tmux
|
||||
CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c
|
||||
@ -14,6 +12,7 @@ dist_EXTRA_tmux_SOURCES = compat/*.[ch]
|
||||
AM_CPPFLAGS += @XOPEN_DEFINES@ \
|
||||
-DTMUX_VERSION='"@VERSION@"' \
|
||||
-DTMUX_CONF='"$(sysconfdir)/tmux.conf:~/.tmux.conf:$$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"' \
|
||||
-DTMUX_LOCK_CMD='"@DEFAULT_LOCK_CMD@"' \
|
||||
-DTMUX_TERM='"@DEFAULT_TERM@"'
|
||||
|
||||
# Additional object files.
|
||||
@ -31,7 +30,7 @@ AM_CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align
|
||||
AM_CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes
|
||||
AM_CFLAGS += -Wno-unused-result -Wno-format-y2k
|
||||
if IS_DARWIN
|
||||
AM_CFLAGS += -Wno-deprecated-declarations -Wno-cast-align
|
||||
AM_CFLAGS += -Wno-deprecated-declarations -Wno-cast-align -Wno-macro-redefined
|
||||
endif
|
||||
AM_CPPFLAGS += -DDEBUG
|
||||
endif
|
||||
@ -67,6 +66,11 @@ if IS_HAIKU
|
||||
AM_CPPFLAGS += -D_BSD_SOURCE
|
||||
endif
|
||||
|
||||
# Set flags for Cygwin.
|
||||
if IS_CYGWIN
|
||||
AM_CPPFLAGS += -DTMUX_SOCK_PERM=0
|
||||
endif
|
||||
|
||||
# List of sources.
|
||||
dist_tmux_SOURCES = \
|
||||
alerts.c \
|
||||
@ -150,6 +154,7 @@ dist_tmux_SOURCES = \
|
||||
grid-reader.c \
|
||||
grid-view.c \
|
||||
grid.c \
|
||||
hyperlinks.c \
|
||||
input-keys.c \
|
||||
input.c \
|
||||
job.c \
|
||||
@ -189,6 +194,7 @@ dist_tmux_SOURCES = \
|
||||
tty-keys.c \
|
||||
tty-term.c \
|
||||
tty.c \
|
||||
utf8-combined.c \
|
||||
utf8.c \
|
||||
window-buffer.c \
|
||||
window-client.c \
|
||||
@ -216,6 +222,11 @@ if HAVE_UTF8PROC
|
||||
nodist_tmux_SOURCES += compat/utf8proc.c
|
||||
endif
|
||||
|
||||
# Enable sixel support.
|
||||
if ENABLE_SIXEL
|
||||
dist_tmux_SOURCES += image.c image-sixel.c
|
||||
endif
|
||||
|
||||
if NEED_FUZZING
|
||||
check_PROGRAMS = fuzz/input-fuzzer
|
||||
fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
||||
|
3
README
3
README
@ -36,6 +36,7 @@ autoconf, automake and pkg-config:
|
||||
$ cd tmux
|
||||
$ sh autogen.sh
|
||||
$ ./configure && make
|
||||
$ sudo make install
|
||||
|
||||
* Contributing
|
||||
|
||||
@ -65,7 +66,7 @@ Also see the tmux FAQ at:
|
||||
|
||||
A bash(1) completion file is at:
|
||||
|
||||
https://github.com/imomaliev/tmux-bash-completion
|
||||
https://github.com/scop/bash-completion/blob/main/completions/tmux
|
||||
|
||||
For debugging, run tmux with -v and -vv to generate server and client log files
|
||||
in the current directory.
|
||||
|
@ -38,7 +38,7 @@ tmuxのドキュメントについてはtmux.1マニュアルをご覧くださ
|
||||
サンプル設定は本リポジトリのexample_tmux.confに
|
||||
また、bash-completionファイルは下記にあります。
|
||||
|
||||
https://github.com/imomaliev/tmux-bash-completion
|
||||
https://github.com/scop/bash-completion/blob/main/completions/tmux
|
||||
|
||||
「-v」や「-vv」を指定することでデバッグモードでの起動が可能です。カレントディレクトリにサーバーやクライアントのログファイルが生成されます。
|
||||
|
||||
|
32
SYNCING
32
SYNCING
@ -1,17 +1,17 @@
|
||||
Preamble
|
||||
========
|
||||
|
||||
Tmux portable relies on repositories "tmux" and "tmux-openbsd".
|
||||
Tmux portable relies on repositories "tmux" and "tmux-obsd".
|
||||
Here's a description of them:
|
||||
|
||||
* "tmux" is the portable version, the one which contains code for other
|
||||
operating systems, and autotools, etc., which isn't found or needed in the
|
||||
OpenBSD base system.
|
||||
|
||||
* "tmux-openbsd" is the version of tmux in OpenBSD base system which provides
|
||||
* "tmux-obsd" is the version of tmux in OpenBSD base system which provides
|
||||
the basis of the portable tmux version.
|
||||
|
||||
Note: The "tmux-openbsd" repository is actually handled by "git cvsimport"
|
||||
Note: The "tmux-obsd" repository is actually handled by "git cvsimport"
|
||||
running at 15 minute intervals, so a commit made to OpenBSD's tmux CVS
|
||||
repository will take at least that long to appear in this git repository.
|
||||
(It might take longer, depending on the CVS mirror used to import the
|
||||
@ -34,11 +34,11 @@ this information has ever been set before.
|
||||
Cloning repositories
|
||||
====================
|
||||
|
||||
This involves having both tmux and tmux-openbsd cloned, as in:
|
||||
This involves having both tmux and tmux-obsd cloned, as in:
|
||||
|
||||
% cd /some/where/useful
|
||||
% git clone https://github.com/tmux/tmux.git
|
||||
% git clone https://github.com/ThomasAdam/tmux-openbsd.git
|
||||
% git clone https://github.com/ThomasAdam/tmux-obsd.git
|
||||
|
||||
Note that you do not need additional checkouts to manage the sync -- an
|
||||
existing clone of either repositories will suffice. So if you already have
|
||||
@ -47,30 +47,30 @@ these checkouts existing, skip that.
|
||||
Adding in git-remotes
|
||||
=====================
|
||||
|
||||
Because the portable "tmux" git repository and the "tmux-openbsd"
|
||||
Because the portable "tmux" git repository and the "tmux-obsd"
|
||||
repository do not inherently share any history between each other, the
|
||||
history has been faked between them. This "faking of history" is something
|
||||
which has to be told to git for the purposes of comparing the "tmux" and
|
||||
"tmux-openbsd" repositories for syncing. To do this, we must reference the
|
||||
clone of the "tmux-openbsd" repository from the "tmux" repository, as
|
||||
"tmux-obsd" repositories for syncing. To do this, we must reference the
|
||||
clone of the "tmux-obsd" repository from the "tmux" repository, as
|
||||
shown by the following command:
|
||||
|
||||
% cd /path/to/tmux
|
||||
% git remote add obsd-tmux file:///path/to/tmux-openbsd
|
||||
% git remote add obsd-tmux file:///path/to/tmux-obsd
|
||||
|
||||
So that now, the remote "obsd-tmux" can be used to reference branches and
|
||||
commits from the "tmux-openbsd" repository, but from the context of the
|
||||
commits from the "tmux-obsd" repository, but from the context of the
|
||||
portable "tmux" repository, which makes sense because it's the "tmux"
|
||||
repository which will have the updates applied to them.
|
||||
|
||||
Fetching updates
|
||||
================
|
||||
|
||||
To ensure the latest commits from "tmux-openbsd" can be found from within
|
||||
"tmux", we have to ensure the "master" branch from "tmux-openbsd" is
|
||||
To ensure the latest commits from "tmux-obsd" can be found from within
|
||||
"tmux", we have to ensure the "master" branch from "tmux-obsd" is
|
||||
up-to-date first, and then reference that update in "tmux", as in:
|
||||
|
||||
% cd /path/to/tmux-openbsd
|
||||
% cd /path/to/tmux-obsd
|
||||
% git checkout master
|
||||
% git pull
|
||||
|
||||
@ -82,9 +82,9 @@ Then back in "tmux":
|
||||
Creating the necessary branches
|
||||
===============================
|
||||
|
||||
Now that "tmux" can see commits and branches from "tmux-openbsd" by way
|
||||
Now that "tmux" can see commits and branches from "tmux-obsd" by way
|
||||
of the remote name "obsd-tmux", we can now create the master branch from
|
||||
"tmux-openbsd" in the "tmux" repository:
|
||||
"tmux-obsd" in the "tmux" repository:
|
||||
|
||||
% git checkout -b obsd-master obsd-tmux/master
|
||||
|
||||
@ -92,7 +92,7 @@ Adding in the fake history points
|
||||
=================================
|
||||
|
||||
To tie both the "master" branch from "tmux" and the "obsd-master"
|
||||
branch from "tmux-openbsd" together, the fake history points added to the
|
||||
branch from "tmux-obsd" together, the fake history points added to the
|
||||
"tmux" repository need to be added. To do this, we must add an
|
||||
additional refspec line, as in:
|
||||
|
||||
|
8
alerts.c
8
alerts.c
@ -315,11 +315,11 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option)
|
||||
if (visual == VISUAL_OFF)
|
||||
continue;
|
||||
if (c->session->curw == wl) {
|
||||
status_message_set(c, -1, 1, 0, "%s in current window",
|
||||
type);
|
||||
status_message_set(c, -1, 1, 0, 0,
|
||||
"%s in current window", type);
|
||||
} else {
|
||||
status_message_set(c, -1, 1, 0, "%s in window %d", type,
|
||||
wl->idx);
|
||||
status_message_set(c, -1, 1, 0, 0,
|
||||
"%s in window %d", type, wl->idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
349
arguments.c
349
arguments.c
@ -37,6 +37,10 @@ struct args_entry {
|
||||
u_char flag;
|
||||
struct args_values values;
|
||||
u_int count;
|
||||
|
||||
int flags;
|
||||
#define ARGS_ENTRY_OPTIONAL_VALUE 0x1
|
||||
|
||||
RB_ENTRY(args_entry) entry;
|
||||
};
|
||||
|
||||
@ -94,6 +98,22 @@ args_copy_value(struct args_value *to, struct args_value *from)
|
||||
}
|
||||
}
|
||||
|
||||
/* Type to string. */
|
||||
static const char *
|
||||
args_type_to_string (enum args_type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ARGS_NONE:
|
||||
return "NONE";
|
||||
case ARGS_STRING:
|
||||
return "STRING";
|
||||
case ARGS_COMMANDS:
|
||||
return "COMMANDS";
|
||||
}
|
||||
return "INVALID";
|
||||
}
|
||||
|
||||
/* Get value as string. */
|
||||
static const char *
|
||||
args_value_as_string(struct args_value *value)
|
||||
@ -122,6 +142,103 @@ args_create(void)
|
||||
return (args);
|
||||
}
|
||||
|
||||
/* Parse a single flag. */
|
||||
static int
|
||||
args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
|
||||
struct args *args, u_int *i, const char *string, int flag,
|
||||
int optional_argument)
|
||||
{
|
||||
struct args_value *argument, *new;
|
||||
const char *s;
|
||||
|
||||
new = xcalloc(1, sizeof *new);
|
||||
if (*string != '\0') {
|
||||
new->type = ARGS_STRING;
|
||||
new->string = xstrdup(string);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (*i == count)
|
||||
argument = NULL;
|
||||
else {
|
||||
argument = &values[*i];
|
||||
if (argument->type != ARGS_STRING) {
|
||||
xasprintf(cause, "-%c argument must be a string", flag);
|
||||
args_free_value(new);
|
||||
free(new);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
if (argument == NULL) {
|
||||
args_free_value(new);
|
||||
free(new);
|
||||
if (optional_argument) {
|
||||
log_debug("%s: -%c (optional)", __func__, flag);
|
||||
args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
|
||||
return (0); /* either - or end */
|
||||
}
|
||||
xasprintf(cause, "-%c expects an argument", flag);
|
||||
return (-1);
|
||||
}
|
||||
args_copy_value(new, argument);
|
||||
(*i)++;
|
||||
|
||||
out:
|
||||
s = args_value_as_string(new);
|
||||
log_debug("%s: -%c = %s", __func__, flag, s);
|
||||
args_set(args, flag, new, 0);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Parse flags argument. */
|
||||
static int
|
||||
args_parse_flags(const struct args_parse *parse, struct args_value *values,
|
||||
u_int count, char **cause, struct args *args, u_int *i)
|
||||
{
|
||||
struct args_value *value;
|
||||
u_char flag;
|
||||
const char *found, *string;
|
||||
int optional_argument;
|
||||
|
||||
value = &values[*i];
|
||||
if (value->type != ARGS_STRING)
|
||||
return (1);
|
||||
|
||||
string = value->string;
|
||||
log_debug("%s: next %s", __func__, string);
|
||||
if (*string++ != '-' || *string == '\0')
|
||||
return (1);
|
||||
(*i)++;
|
||||
if (string[0] == '-' && string[1] == '\0')
|
||||
return (1);
|
||||
|
||||
for (;;) {
|
||||
flag = *string++;
|
||||
if (flag == '\0')
|
||||
return (0);
|
||||
if (flag == '?')
|
||||
return (-1);
|
||||
if (!isalnum(flag)) {
|
||||
xasprintf(cause, "invalid flag -%c", flag);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
found = strchr(parse->template, flag);
|
||||
if (found == NULL) {
|
||||
xasprintf(cause, "unknown flag -%c", flag);
|
||||
return (-1);
|
||||
}
|
||||
if (found[1] != ':') {
|
||||
log_debug("%s: -%c", __func__, flag);
|
||||
args_set(args, flag, NULL, 0);
|
||||
continue;
|
||||
}
|
||||
optional_argument = (found[2] == ':');
|
||||
return (args_parse_flag_argument(values, count, cause, args, i,
|
||||
string, flag, optional_argument));
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse arguments into a new argument set. */
|
||||
struct args *
|
||||
args_parse(const struct args_parse *parse, struct args_value *values,
|
||||
@ -131,86 +248,21 @@ args_parse(const struct args_parse *parse, struct args_value *values,
|
||||
u_int i;
|
||||
enum args_parse_type type;
|
||||
struct args_value *value, *new;
|
||||
u_char flag;
|
||||
const char *found, *string, *s;
|
||||
int optional_argument;
|
||||
const char *s;
|
||||
int stop;
|
||||
|
||||
if (count == 0)
|
||||
return (args_create());
|
||||
|
||||
args = args_create();
|
||||
for (i = 1; i < count; /* nothing */) {
|
||||
value = &values[i];
|
||||
if (value->type != ARGS_STRING)
|
||||
break;
|
||||
|
||||
string = value->string;
|
||||
if (*string++ != '-' || *string == '\0')
|
||||
break;
|
||||
i++;
|
||||
if (string[0] == '-' && string[1] == '\0')
|
||||
break;
|
||||
|
||||
for (;;) {
|
||||
flag = *string++;
|
||||
if (flag == '\0')
|
||||
break;
|
||||
if (flag == '?') {
|
||||
args_free(args);
|
||||
return (NULL);
|
||||
}
|
||||
if (!isalnum(flag)) {
|
||||
xasprintf(cause, "invalid flag -%c", flag);
|
||||
args_free(args);
|
||||
return (NULL);
|
||||
}
|
||||
found = strchr(parse->template, flag);
|
||||
if (found == NULL) {
|
||||
xasprintf(cause, "unknown flag -%c", flag);
|
||||
args_free(args);
|
||||
return (NULL);
|
||||
}
|
||||
if (*++found != ':') {
|
||||
log_debug("%s: -%c", __func__, flag);
|
||||
args_set(args, flag, NULL);
|
||||
continue;
|
||||
}
|
||||
if (*found == ':') {
|
||||
optional_argument = 1;
|
||||
found++;
|
||||
}
|
||||
new = xcalloc(1, sizeof *new);
|
||||
if (*string != '\0') {
|
||||
new->type = ARGS_STRING;
|
||||
new->string = xstrdup(string);
|
||||
} else {
|
||||
if (i == count) {
|
||||
if (optional_argument) {
|
||||
log_debug("%s: -%c", __func__,
|
||||
flag);
|
||||
args_set(args, flag, NULL);
|
||||
continue;
|
||||
}
|
||||
xasprintf(cause,
|
||||
"-%c expects an argument",
|
||||
flag);
|
||||
args_free(args);
|
||||
return (NULL);
|
||||
}
|
||||
if (values[i].type != ARGS_STRING) {
|
||||
xasprintf(cause,
|
||||
"-%c argument must be a string",
|
||||
flag);
|
||||
args_free(args);
|
||||
return (NULL);
|
||||
}
|
||||
args_copy_value(new, &values[i++]);
|
||||
}
|
||||
s = args_value_as_string(new);
|
||||
log_debug("%s: -%c = %s", __func__, flag, s);
|
||||
args_set(args, flag, new);
|
||||
break;
|
||||
stop = args_parse_flags(parse, values, count, cause, args, &i);
|
||||
if (stop == -1) {
|
||||
args_free(args);
|
||||
return (NULL);
|
||||
}
|
||||
if (stop == 1)
|
||||
break;
|
||||
}
|
||||
log_debug("%s: flags end at %u of %u", __func__, i, count);
|
||||
if (i != count) {
|
||||
@ -218,8 +270,8 @@ args_parse(const struct args_parse *parse, struct args_value *values,
|
||||
value = &values[i];
|
||||
|
||||
s = args_value_as_string(value);
|
||||
log_debug("%s: %u = %s (type %d)", __func__, i, s,
|
||||
value->type);
|
||||
log_debug("%s: %u = %s (type %s)", __func__, i, s,
|
||||
args_type_to_string (value->type));
|
||||
|
||||
if (parse->cb != NULL) {
|
||||
type = parse->cb(args, args->count, cause);
|
||||
@ -323,13 +375,13 @@ args_copy(struct args *args, int argc, char **argv)
|
||||
RB_FOREACH(entry, args_tree, &args->tree) {
|
||||
if (TAILQ_EMPTY(&entry->values)) {
|
||||
for (i = 0; i < entry->count; i++)
|
||||
args_set(new_args, entry->flag, NULL);
|
||||
args_set(new_args, entry->flag, NULL, 0);
|
||||
continue;
|
||||
}
|
||||
TAILQ_FOREACH(value, &entry->values, entry) {
|
||||
new_value = xcalloc(1, sizeof *new_value);
|
||||
args_copy_copy_value(new_value, value, argc, argv);
|
||||
args_set(new_args, entry->flag, new_value);
|
||||
args_set(new_args, entry->flag, new_value, 0);
|
||||
}
|
||||
}
|
||||
if (args->count == 0)
|
||||
@ -487,6 +539,7 @@ args_print(struct args *args)
|
||||
char *buf;
|
||||
u_int i, j;
|
||||
struct args_entry *entry;
|
||||
struct args_entry *last = NULL;
|
||||
struct args_value *value;
|
||||
|
||||
len = 1;
|
||||
@ -494,6 +547,8 @@ args_print(struct args *args)
|
||||
|
||||
/* Process the flags first. */
|
||||
RB_FOREACH(entry, args_tree, &args->tree) {
|
||||
if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE)
|
||||
continue;
|
||||
if (!TAILQ_EMPTY(&entry->values))
|
||||
continue;
|
||||
|
||||
@ -505,6 +560,16 @@ args_print(struct args *args)
|
||||
|
||||
/* Then the flags with arguments. */
|
||||
RB_FOREACH(entry, args_tree, &args->tree) {
|
||||
if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) {
|
||||
if (*buf != '\0')
|
||||
args_print_add(&buf, &len, " -%c", entry->flag);
|
||||
else
|
||||
args_print_add(&buf, &len, "-%c", entry->flag);
|
||||
last = entry;
|
||||
continue;
|
||||
}
|
||||
if (TAILQ_EMPTY(&entry->values))
|
||||
continue;
|
||||
TAILQ_FOREACH(value, &entry->values, entry) {
|
||||
if (*buf != '\0')
|
||||
args_print_add(&buf, &len, " -%c", entry->flag);
|
||||
@ -512,7 +577,10 @@ args_print(struct args *args)
|
||||
args_print_add(&buf, &len, "-%c", entry->flag);
|
||||
args_print_add_value(&buf, &len, value);
|
||||
}
|
||||
last = entry;
|
||||
}
|
||||
if (last && (last->flags & ARGS_ENTRY_OPTIONAL_VALUE))
|
||||
args_print_add(&buf, &len, " --");
|
||||
|
||||
/* And finally the argument vector. */
|
||||
for (i = 0; i < args->count; i++)
|
||||
@ -582,7 +650,7 @@ args_has(struct args *args, u_char flag)
|
||||
|
||||
/* Set argument value in the arguments tree. */
|
||||
void
|
||||
args_set(struct args *args, u_char flag, struct args_value *value)
|
||||
args_set(struct args *args, u_char flag, struct args_value *value, int flags)
|
||||
{
|
||||
struct args_entry *entry;
|
||||
|
||||
@ -591,12 +659,15 @@ args_set(struct args *args, u_char flag, struct args_value *value)
|
||||
entry = xcalloc(1, sizeof *entry);
|
||||
entry->flag = flag;
|
||||
entry->count = 1;
|
||||
entry->flags = flags;
|
||||
TAILQ_INIT(&entry->values);
|
||||
RB_INSERT(args_tree, &args->tree, entry);
|
||||
} else
|
||||
entry->count++;
|
||||
if (value != NULL && value->type != ARGS_NONE)
|
||||
TAILQ_INSERT_TAIL(&entry->values, value, entry);
|
||||
else
|
||||
free(value);
|
||||
}
|
||||
|
||||
/* Get argument value. Will be NULL if it isn't present. */
|
||||
@ -696,6 +767,7 @@ args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
|
||||
struct args_value *value;
|
||||
struct args_command_state *state;
|
||||
const char *cmd;
|
||||
const char *file;
|
||||
|
||||
state = xcalloc(1, sizeof *state);
|
||||
|
||||
@ -722,7 +794,9 @@ args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
|
||||
|
||||
if (wait)
|
||||
state->pi.item = item;
|
||||
cmd_get_source(self, &state->pi.file, &state->pi.line);
|
||||
cmd_get_source(self, &file, &state->pi.line);
|
||||
if (file != NULL)
|
||||
state->pi.file = xstrdup(file);
|
||||
state->pi.c = tc;
|
||||
if (state->pi.c != NULL)
|
||||
state->pi.c->references++;
|
||||
@ -747,6 +821,8 @@ args_make_commands(struct args_command_state *state, int argc, char **argv,
|
||||
}
|
||||
|
||||
cmd = xstrdup(state->cmd);
|
||||
log_debug("%s: %s", __func__, cmd);
|
||||
cmd_log_argv(argc, argv, __func__);
|
||||
for (i = 0; i < argc; i++) {
|
||||
new_cmd = cmd_template_replace(cmd, argv[i], i + 1);
|
||||
log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd);
|
||||
@ -775,6 +851,7 @@ args_make_commands_free(struct args_command_state *state)
|
||||
cmd_list_free(state->cmdlist);
|
||||
if (state->pi.c != NULL)
|
||||
server_client_unref(state->pi.c);
|
||||
free((void *)state->pi.file);
|
||||
free(state->cmd);
|
||||
free(state);
|
||||
}
|
||||
@ -848,6 +925,41 @@ args_strtonum(struct args *args, u_char flag, long long minval,
|
||||
return (ll);
|
||||
}
|
||||
|
||||
/* Convert an argument value to a number, and expand formats. */
|
||||
long long
|
||||
args_strtonum_and_expand(struct args *args, u_char flag, long long minval,
|
||||
long long maxval, struct cmdq_item *item, char **cause)
|
||||
{
|
||||
const char *errstr;
|
||||
char *formatted;
|
||||
long long ll;
|
||||
struct args_entry *entry;
|
||||
struct args_value *value;
|
||||
|
||||
if ((entry = args_find(args, flag)) == NULL) {
|
||||
*cause = xstrdup("missing");
|
||||
return (0);
|
||||
}
|
||||
value = TAILQ_LAST(&entry->values, args_values);
|
||||
if (value == NULL ||
|
||||
value->type != ARGS_STRING ||
|
||||
value->string == NULL) {
|
||||
*cause = xstrdup("missing");
|
||||
return (0);
|
||||
}
|
||||
|
||||
formatted = format_single_from_target(item, value->string);
|
||||
ll = strtonum(formatted, minval, maxval, &errstr);
|
||||
free(formatted);
|
||||
if (errstr != NULL) {
|
||||
*cause = xstrdup(errstr);
|
||||
return (0);
|
||||
}
|
||||
|
||||
*cause = NULL;
|
||||
return (ll);
|
||||
}
|
||||
|
||||
/* Convert an argument to a number which may be a percentage. */
|
||||
long long
|
||||
args_percentage(struct args *args, u_char flag, long long minval,
|
||||
@ -860,6 +972,10 @@ args_percentage(struct args *args, u_char flag, long long minval,
|
||||
*cause = xstrdup("missing");
|
||||
return (0);
|
||||
}
|
||||
if (TAILQ_EMPTY(&entry->values)) {
|
||||
*cause = xstrdup("empty");
|
||||
return (0);
|
||||
}
|
||||
value = TAILQ_LAST(&entry->values, args_values)->string;
|
||||
return (args_string_percentage(value, minval, maxval, curval, cause));
|
||||
}
|
||||
@ -874,6 +990,10 @@ args_string_percentage(const char *value, long long minval, long long maxval,
|
||||
size_t valuelen = strlen(value);
|
||||
char *copy;
|
||||
|
||||
if (valuelen == 0) {
|
||||
*cause = xstrdup("empty");
|
||||
return (0);
|
||||
}
|
||||
if (value[valuelen - 1] == '%') {
|
||||
copy = xstrdup(value);
|
||||
copy[valuelen - 1] = '\0';
|
||||
@ -904,3 +1024,74 @@ args_string_percentage(const char *value, long long minval, long long maxval,
|
||||
*cause = NULL;
|
||||
return (ll);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an argument to a number which may be a percentage, and expand
|
||||
* formats.
|
||||
*/
|
||||
long long
|
||||
args_percentage_and_expand(struct args *args, u_char flag, long long minval,
|
||||
long long maxval, long long curval, struct cmdq_item *item, char **cause)
|
||||
{
|
||||
const char *value;
|
||||
struct args_entry *entry;
|
||||
|
||||
if ((entry = args_find(args, flag)) == NULL) {
|
||||
*cause = xstrdup("missing");
|
||||
return (0);
|
||||
}
|
||||
if (TAILQ_EMPTY(&entry->values)) {
|
||||
*cause = xstrdup("empty");
|
||||
return (0);
|
||||
}
|
||||
value = TAILQ_LAST(&entry->values, args_values)->string;
|
||||
return (args_string_percentage_and_expand(value, minval, maxval, curval,
|
||||
item, cause));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a string to a number which may be a percentage, and expand formats.
|
||||
*/
|
||||
long long
|
||||
args_string_percentage_and_expand(const char *value, long long minval,
|
||||
long long maxval, long long curval, struct cmdq_item *item, char **cause)
|
||||
{
|
||||
const char *errstr;
|
||||
long long ll;
|
||||
size_t valuelen = strlen(value);
|
||||
char *copy, *f;
|
||||
|
||||
if (value[valuelen - 1] == '%') {
|
||||
copy = xstrdup(value);
|
||||
copy[valuelen - 1] = '\0';
|
||||
|
||||
f = format_single_from_target(item, copy);
|
||||
ll = strtonum(f, 0, 100, &errstr);
|
||||
free(f);
|
||||
free(copy);
|
||||
if (errstr != NULL) {
|
||||
*cause = xstrdup(errstr);
|
||||
return (0);
|
||||
}
|
||||
ll = (curval * ll) / 100;
|
||||
if (ll < minval) {
|
||||
*cause = xstrdup("too small");
|
||||
return (0);
|
||||
}
|
||||
if (ll > maxval) {
|
||||
*cause = xstrdup("too large");
|
||||
return (0);
|
||||
}
|
||||
} else {
|
||||
f = format_single_from_target(item, value);
|
||||
ll = strtonum(f, minval, maxval, &errstr);
|
||||
free(f);
|
||||
if (errstr != NULL) {
|
||||
*cause = xstrdup(errstr);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
*cause = NULL;
|
||||
return (ll);
|
||||
}
|
||||
|
47
cfg.c
47
cfg.c
@ -51,8 +51,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
cfg_finished = 1;
|
||||
|
||||
if (!RB_EMPTY(&sessions))
|
||||
cfg_show_causes(RB_MIN(sessions, &sessions));
|
||||
cfg_show_causes(NULL);
|
||||
|
||||
if (cfg_item != NULL)
|
||||
cmdq_continue(cfg_item);
|
||||
@ -67,6 +66,7 @@ start_cfg(void)
|
||||
{
|
||||
struct client *c;
|
||||
u_int i;
|
||||
int flags = 0;
|
||||
|
||||
/*
|
||||
* Configuration files are loaded without a client, so commands are run
|
||||
@ -84,19 +84,17 @@ start_cfg(void)
|
||||
cmdq_append(c, cfg_item);
|
||||
}
|
||||
|
||||
for (i = 0; i < cfg_nfiles; i++) {
|
||||
if (cfg_quiet)
|
||||
load_cfg(cfg_files[i], c, NULL, CMD_PARSE_QUIET, NULL);
|
||||
else
|
||||
load_cfg(cfg_files[i], c, NULL, 0, NULL);
|
||||
}
|
||||
if (cfg_quiet)
|
||||
flags = CMD_PARSE_QUIET;
|
||||
for (i = 0; i < cfg_nfiles; i++)
|
||||
load_cfg(cfg_files[i], c, NULL, NULL, flags, NULL);
|
||||
|
||||
cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
|
||||
}
|
||||
|
||||
int
|
||||
load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
|
||||
struct cmdq_item **new_item)
|
||||
load_cfg(const char *path, struct client *c, struct cmdq_item *item,
|
||||
struct cmd_find_state *current, int flags, struct cmdq_item **new_item)
|
||||
{
|
||||
FILE *f;
|
||||
struct cmd_parse_input pi;
|
||||
@ -135,7 +133,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
|
||||
}
|
||||
|
||||
if (item != NULL)
|
||||
state = cmdq_copy_state(cmdq_get_state(item));
|
||||
state = cmdq_copy_state(cmdq_get_state(item), current);
|
||||
else
|
||||
state = cmdq_new_state(NULL, NULL, 0);
|
||||
cmdq_add_format(state, "current_file", "%s", pi.file);
|
||||
@ -155,8 +153,8 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
|
||||
|
||||
int
|
||||
load_cfg_from_buffer(const void *buf, size_t len, const char *path,
|
||||
struct client *c, struct cmdq_item *item, int flags,
|
||||
struct cmdq_item **new_item)
|
||||
struct client *c, struct cmdq_item *item, struct cmd_find_state *current,
|
||||
int flags, struct cmdq_item **new_item)
|
||||
{
|
||||
struct cmd_parse_input pi;
|
||||
struct cmd_parse_result *pr;
|
||||
@ -187,7 +185,7 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path,
|
||||
}
|
||||
|
||||
if (item != NULL)
|
||||
state = cmdq_copy_state(cmdq_get_state(item));
|
||||
state = cmdq_copy_state(cmdq_get_state(item), current);
|
||||
else
|
||||
state = cmdq_new_state(NULL, NULL, 0);
|
||||
cmdq_add_format(state, "current_file", "%s", pi.file);
|
||||
@ -238,11 +236,29 @@ cfg_print_causes(struct cmdq_item *item)
|
||||
void
|
||||
cfg_show_causes(struct session *s)
|
||||
{
|
||||
struct client *c = TAILQ_FIRST(&clients);
|
||||
struct window_pane *wp;
|
||||
struct window_mode_entry *wme;
|
||||
u_int i;
|
||||
|
||||
if (s == NULL || cfg_ncauses == 0)
|
||||
if (cfg_ncauses == 0)
|
||||
return;
|
||||
|
||||
if (c != NULL && (c->flags & CLIENT_CONTROL)) {
|
||||
for (i = 0; i < cfg_ncauses; i++) {
|
||||
control_write(c, "%%config-error %s", cfg_causes[i]);
|
||||
free(cfg_causes[i]);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (s == NULL) {
|
||||
if (c != NULL && c->session != NULL)
|
||||
s = c->session;
|
||||
else
|
||||
s = RB_MIN(sessions, &sessions);
|
||||
}
|
||||
if (s == NULL || s->attached == 0) /* wait for an attached session */
|
||||
return;
|
||||
wp = s->curw->window->active;
|
||||
|
||||
@ -254,6 +270,7 @@ cfg_show_causes(struct session *s)
|
||||
free(cfg_causes[i]);
|
||||
}
|
||||
|
||||
out:
|
||||
free(cfg_causes);
|
||||
cfg_causes = NULL;
|
||||
cfg_ncauses = 0;
|
||||
|
48
client.c
48
client.c
@ -245,9 +245,6 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
|
||||
u_int ncaps = 0;
|
||||
struct args_value *values;
|
||||
|
||||
/* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
|
||||
/* Set up the initial command. */
|
||||
if (shell_command != NULL) {
|
||||
msg = MSG_SHELL;
|
||||
@ -284,6 +281,12 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
|
||||
log_debug("flags are %#llx", (unsigned long long)client_flags);
|
||||
|
||||
/* Initialize the client socket and start the server. */
|
||||
#ifdef HAVE_SYSTEMD
|
||||
if (systemd_activated()) {
|
||||
/* socket-based activation, do not even try to be a client. */
|
||||
fd = server_start(client_proc, flags, base, 0, NULL);
|
||||
} else
|
||||
#endif
|
||||
fd = client_connect(base, socket_path, client_flags);
|
||||
if (fd == -1) {
|
||||
if (errno == ECONNREFUSED) {
|
||||
@ -449,11 +452,12 @@ client_send_identify(const char *ttynam, const char *termname, char **caps,
|
||||
{
|
||||
char **ss;
|
||||
size_t sslen;
|
||||
int fd, flags = client_flags;
|
||||
int fd;
|
||||
uint64_t flags = client_flags;
|
||||
pid_t pid;
|
||||
u_int i;
|
||||
|
||||
proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags);
|
||||
proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &flags, sizeof flags);
|
||||
proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &client_flags,
|
||||
sizeof client_flags);
|
||||
|
||||
@ -494,20 +498,10 @@ client_send_identify(const char *ttynam, const char *termname, char **caps,
|
||||
static __dead void
|
||||
client_exec(const char *shell, const char *shellcmd)
|
||||
{
|
||||
const char *name, *ptr;
|
||||
char *argv0;
|
||||
char *argv0;
|
||||
|
||||
log_debug("shell %s, command %s", shell, shellcmd);
|
||||
|
||||
ptr = strrchr(shell, '/');
|
||||
if (ptr != NULL && *(ptr + 1) != '\0')
|
||||
name = ptr + 1;
|
||||
else
|
||||
name = shell;
|
||||
if (client_flags & CLIENT_LOGIN)
|
||||
xasprintf(&argv0, "-%s", name);
|
||||
else
|
||||
xasprintf(&argv0, "%s", name);
|
||||
argv0 = shell_argv0(shell, !!(client_flags & CLIENT_LOGIN));
|
||||
setenv("SHELL", shell, 1);
|
||||
|
||||
proc_clear_signals(client_proc, 1);
|
||||
@ -527,11 +521,22 @@ client_signal(int sig)
|
||||
{
|
||||
struct sigaction sigact;
|
||||
int status;
|
||||
pid_t pid;
|
||||
|
||||
log_debug("%s: %s", __func__, strsignal(sig));
|
||||
if (sig == SIGCHLD)
|
||||
waitpid(WAIT_ANY, &status, WNOHANG);
|
||||
else if (!client_attached) {
|
||||
if (sig == SIGCHLD) {
|
||||
for (;;) {
|
||||
pid = waitpid(WAIT_ANY, &status, WNOHANG);
|
||||
if (pid == 0)
|
||||
break;
|
||||
if (pid == -1) {
|
||||
if (errno == ECHILD)
|
||||
break;
|
||||
log_debug("waitpid failed: %s",
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
} else if (!client_attached) {
|
||||
if (sig == SIGTERM || sig == SIGHUP)
|
||||
proc_exit(client_proc);
|
||||
} else {
|
||||
@ -694,6 +699,9 @@ client_dispatch_wait(struct imsg *imsg)
|
||||
!(client_flags & CLIENT_CONTROL), client_file_check_cb,
|
||||
NULL);
|
||||
break;
|
||||
case MSG_READ_CANCEL:
|
||||
file_read_cancel(&client_files, imsg);
|
||||
break;
|
||||
case MSG_WRITE_OPEN:
|
||||
file_write_open(&client_files, client_peer, imsg, 1,
|
||||
!(client_flags & CLIENT_CONTROL), client_file_check_cb,
|
||||
|
@ -158,6 +158,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
|
||||
c->flags |= CLIENT_ATTACHED;
|
||||
}
|
||||
|
||||
if (cfg_finished)
|
||||
cfg_show_causes(s);
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ const struct cmd_entry cmd_bind_key_entry = {
|
||||
|
||||
.args = { "nrN:T:", 1, -1, cmd_bind_key_args_parse },
|
||||
.usage = "[-nr] [-T key-table] [-N note] key "
|
||||
"[command [arguments]]",
|
||||
"[command [argument ...]]",
|
||||
|
||||
.flags = CMD_AFTERHOOK,
|
||||
.exec = cmd_bind_key_exec
|
||||
|
@ -99,7 +99,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
|
||||
w = wp->window = window_create(w->sx, w->sy, w->xpixel, w->ypixel);
|
||||
options_set_parent(wp->options, w->options);
|
||||
wp->flags |= PANE_STYLECHANGED;
|
||||
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
|
||||
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
|
||||
w->active = wp;
|
||||
w->latest = tc;
|
||||
@ -115,6 +115,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
|
||||
layout_init(w, wp);
|
||||
wp->flags |= PANE_CHANGED;
|
||||
colour_palette_from_option(&wp->palette, wp->options);
|
||||
|
||||
if (idx == -1)
|
||||
idx = -1 - options_get_number(dst_s->options, "base-index");
|
||||
|
@ -39,8 +39,8 @@ const struct cmd_entry cmd_capture_pane_entry = {
|
||||
.name = "capture-pane",
|
||||
.alias = "capturep",
|
||||
|
||||
.args = { "ab:CeE:JNpPqS:t:", 0, 0, NULL },
|
||||
.usage = "[-aCeJNpPq] " CMD_BUFFER_USAGE " [-E end-line] "
|
||||
.args = { "ab:CeE:JMNpPqS:Tt:", 0, 0, NULL },
|
||||
.usage = "[-aCeJMNpPqT] " CMD_BUFFER_USAGE " [-E end-line] "
|
||||
"[-S start-line] " CMD_TARGET_PANE_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
@ -53,8 +53,8 @@ const struct cmd_entry cmd_clear_history_entry = {
|
||||
.name = "clear-history",
|
||||
.alias = "clearhist",
|
||||
|
||||
.args = { "t:", 0, 0, NULL },
|
||||
.usage = CMD_TARGET_PANE_USAGE,
|
||||
.args = { "Ht:", 0, 0, NULL },
|
||||
.usage = "[-H] " CMD_TARGET_PANE_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
@ -107,14 +107,16 @@ static char *
|
||||
cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
|
||||
struct window_pane *wp, size_t *len)
|
||||
{
|
||||
struct grid *gd;
|
||||
const struct grid_line *gl;
|
||||
struct grid_cell *gc = NULL;
|
||||
int n, with_codes, escape_c0, join_lines, no_trim;
|
||||
u_int i, sx, top, bottom, tmp;
|
||||
char *cause, *buf, *line;
|
||||
const char *Sflag, *Eflag;
|
||||
size_t linelen;
|
||||
struct grid *gd;
|
||||
const struct grid_line *gl;
|
||||
struct screen *s;
|
||||
struct grid_cell *gc = NULL;
|
||||
struct window_mode_entry *wme;
|
||||
int n, join_lines, flags = 0;
|
||||
u_int i, sx, top, bottom, tmp;
|
||||
char *cause, *buf, *line;
|
||||
const char *Sflag, *Eflag;
|
||||
size_t linelen;
|
||||
|
||||
sx = screen_size_x(&wp->base);
|
||||
if (args_has(args, 'a')) {
|
||||
@ -126,14 +128,27 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
|
||||
}
|
||||
return (xstrdup(""));
|
||||
}
|
||||
} else
|
||||
s = &wp->base;
|
||||
} else if (args_has(args, 'M')) {
|
||||
wme = TAILQ_FIRST(&wp->modes);
|
||||
if (wme != NULL && wme->mode->get_screen != NULL) {
|
||||
s = wme->mode->get_screen (wme);
|
||||
gd = s->grid;
|
||||
} else {
|
||||
s = &wp->base;
|
||||
gd = wp->base.grid;
|
||||
}
|
||||
} else {
|
||||
s = &wp->base;
|
||||
gd = wp->base.grid;
|
||||
}
|
||||
|
||||
Sflag = args_get(args, 'S');
|
||||
if (Sflag != NULL && strcmp(Sflag, "-") == 0)
|
||||
top = 0;
|
||||
else {
|
||||
n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause);
|
||||
n = args_strtonum_and_expand(args, 'S', INT_MIN, SHRT_MAX,
|
||||
item, &cause);
|
||||
if (cause != NULL) {
|
||||
top = gd->hsize;
|
||||
free(cause);
|
||||
@ -149,7 +164,8 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
|
||||
if (Eflag != NULL && strcmp(Eflag, "-") == 0)
|
||||
bottom = gd->hsize + gd->sy - 1;
|
||||
else {
|
||||
n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause);
|
||||
n = args_strtonum_and_expand(args, 'E', INT_MIN, SHRT_MAX,
|
||||
item, &cause);
|
||||
if (cause != NULL) {
|
||||
bottom = gd->hsize + gd->sy - 1;
|
||||
free(cause);
|
||||
@ -167,15 +183,19 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
|
||||
top = tmp;
|
||||
}
|
||||
|
||||
with_codes = args_has(args, 'e');
|
||||
escape_c0 = args_has(args, 'C');
|
||||
join_lines = args_has(args, 'J');
|
||||
no_trim = args_has(args, 'N');
|
||||
if (args_has(args, 'e'))
|
||||
flags |= GRID_STRING_WITH_SEQUENCES;
|
||||
if (args_has(args, 'C'))
|
||||
flags |= GRID_STRING_ESCAPE_SEQUENCES;
|
||||
if (!join_lines && !args_has(args, 'T'))
|
||||
flags |= GRID_STRING_EMPTY_CELLS;
|
||||
if (!join_lines && !args_has(args, 'N'))
|
||||
flags |= GRID_STRING_TRIM_SPACES;
|
||||
|
||||
buf = NULL;
|
||||
for (i = top; i <= bottom; i++) {
|
||||
line = grid_string_cells(gd, 0, i, sx, &gc, with_codes,
|
||||
escape_c0, !join_lines && !no_trim);
|
||||
line = grid_string_cells(gd, 0, i, sx, &gc, flags, s);
|
||||
linelen = strlen(line);
|
||||
|
||||
buf = cmd_capture_pane_append(buf, len, line, linelen);
|
||||
@ -202,6 +222,8 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if (cmd_get_entry(self) == &cmd_clear_history_entry) {
|
||||
window_pane_reset_mode_all(wp);
|
||||
grid_clear_history(wp->base.grid);
|
||||
if (args_has(args, 'H'))
|
||||
screen_reset_hyperlinks(wp->screen);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ const struct cmd_entry cmd_choose_tree_entry = {
|
||||
.name = "choose-tree",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "F:f:GK:NO:rst:wZ", 0, 1, cmd_choose_tree_args_parse },
|
||||
.args = { "F:f:GK:NO:rst:wyZ", 0, 1, cmd_choose_tree_args_parse },
|
||||
.usage = "[-GNrswZ] [-F format] [-f filter] [-K key-format] "
|
||||
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
|
||||
|
||||
@ -47,7 +47,7 @@ const struct cmd_entry cmd_choose_client_entry = {
|
||||
.name = "choose-client",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "F:f:K:NO:rt:Z", 0, 1, cmd_choose_tree_args_parse },
|
||||
.args = { "F:f:K:NO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
|
||||
.usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
|
||||
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
|
||||
|
||||
@ -61,7 +61,7 @@ const struct cmd_entry cmd_choose_buffer_entry = {
|
||||
.name = "choose-buffer",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "F:f:K:NO:rt:Z", 0, 1, cmd_choose_tree_args_parse },
|
||||
.args = { "F:f:K:NO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
|
||||
.usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
|
||||
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
|
||||
|
||||
@ -75,7 +75,7 @@ const struct cmd_entry cmd_customize_mode_entry = {
|
||||
.name = "customize-mode",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "F:f:Nt:Z", 0, 0, NULL },
|
||||
.args = { "F:f:Nt:yZ", 0, 0, NULL },
|
||||
.usage = "[-NZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
@ -100,7 +100,7 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
|
||||
const struct window_mode *mode;
|
||||
|
||||
if (cmd_get_entry(self) == &cmd_choose_buffer_entry) {
|
||||
if (paste_get_top(NULL) == NULL)
|
||||
if (paste_is_empty())
|
||||
return (CMD_RETURN_NORMAL);
|
||||
mode = &window_buffer_mode;
|
||||
} else if (cmd_get_entry(self) == &cmd_choose_client_entry) {
|
||||
|
@ -44,7 +44,7 @@ const struct cmd_entry cmd_command_prompt_entry = {
|
||||
|
||||
.args = { "1bFkiI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse },
|
||||
.usage = "[-1bFkiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
|
||||
" [-T type] [template]",
|
||||
" [-T prompt-type] [template]",
|
||||
|
||||
.flags = CMD_CLIENT_TFLAG,
|
||||
.exec = cmd_command_prompt_exec
|
||||
@ -143,6 +143,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
|
||||
cdata->prompt_type = status_prompt_type(type);
|
||||
if (cdata->prompt_type == PROMPT_TYPE_INVALID) {
|
||||
cmdq_error(item, "unknown type: %s", type);
|
||||
cmd_command_prompt_free(cdata);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
} else
|
||||
@ -179,10 +180,10 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
|
||||
|
||||
if (s == NULL)
|
||||
goto out;
|
||||
|
||||
if (done) {
|
||||
if (cdata->flags & PROMPT_INCREMENTAL)
|
||||
goto out;
|
||||
|
||||
cmd_append_argv(&cdata->argc, &cdata->argv, s);
|
||||
if (++cdata->current != cdata->count) {
|
||||
prompt = &cdata->prompts[cdata->current];
|
||||
@ -193,8 +194,11 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
|
||||
|
||||
argc = cdata->argc;
|
||||
argv = cmd_copy_argv(cdata->argc, cdata->argv);
|
||||
cmd_append_argv(&argc, &argv, s);
|
||||
if (!done)
|
||||
cmd_append_argv(&argc, &argv, s);
|
||||
|
||||
if (done) {
|
||||
cmd_free_argv(cdata->argc, cdata->argv);
|
||||
cdata->argc = argc;
|
||||
cdata->argv = cmd_copy_argv(argc, argv);
|
||||
}
|
||||
|
@ -41,8 +41,9 @@ const struct cmd_entry cmd_confirm_before_entry = {
|
||||
.name = "confirm-before",
|
||||
.alias = "confirm",
|
||||
|
||||
.args = { "bp:t:", 1, 1, cmd_confirm_before_args_parse },
|
||||
.usage = "[-b] [-p prompt] " CMD_TARGET_CLIENT_USAGE " command",
|
||||
.args = { "bc:p:t:y", 1, 1, cmd_confirm_before_args_parse },
|
||||
.usage = "[-by] [-c confirm-key] [-p prompt] " CMD_TARGET_CLIENT_USAGE
|
||||
" command",
|
||||
|
||||
.flags = CMD_CLIENT_TFLAG,
|
||||
.exec = cmd_confirm_before_exec
|
||||
@ -51,6 +52,8 @@ const struct cmd_entry cmd_confirm_before_entry = {
|
||||
struct cmd_confirm_before_data {
|
||||
struct cmdq_item *item;
|
||||
struct cmd_list *cmdlist;
|
||||
u_char confirm_key;
|
||||
int default_yes;
|
||||
};
|
||||
|
||||
static enum args_parse_type
|
||||
@ -68,22 +71,40 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct client *tc = cmdq_get_target_client(item);
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
char *new_prompt;
|
||||
const char *prompt, *cmd;
|
||||
const char *confirm_key, *prompt, *cmd;
|
||||
int wait = !args_has(args, 'b');
|
||||
|
||||
cdata = xcalloc(1, sizeof *cdata);
|
||||
cdata->cmdlist = args_make_commands_now(self, item, 0, 1);
|
||||
if (cdata->cmdlist == NULL)
|
||||
if (cdata->cmdlist == NULL) {
|
||||
free(cdata);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (wait)
|
||||
cdata->item = item;
|
||||
|
||||
cdata->default_yes = args_has(args, 'y');
|
||||
if ((confirm_key = args_get(args, 'c')) != NULL) {
|
||||
if (confirm_key[1] == '\0' &&
|
||||
confirm_key[0] > 31 &&
|
||||
confirm_key[0] < 127)
|
||||
cdata->confirm_key = confirm_key[0];
|
||||
else {
|
||||
cmdq_error(item, "invalid confirm key");
|
||||
free(cdata);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
else
|
||||
cdata->confirm_key = 'y';
|
||||
|
||||
if ((prompt = args_get(args, 'p')) != NULL)
|
||||
xasprintf(&new_prompt, "%s ", prompt);
|
||||
else {
|
||||
cmd = cmd_get_entry(cmd_list_first(cdata->cmdlist))->name;
|
||||
xasprintf(&new_prompt, "Confirm '%s'? (y/n) ", cmd);
|
||||
xasprintf(&new_prompt, "Confirm '%s'? (%c/n) ", cmd,
|
||||
cdata->confirm_key);
|
||||
}
|
||||
|
||||
status_prompt_set(tc, target, new_prompt, NULL,
|
||||
@ -107,9 +128,9 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s,
|
||||
if (c->flags & CLIENT_DEAD)
|
||||
goto out;
|
||||
|
||||
if (s == NULL || *s == '\0')
|
||||
if (s == NULL)
|
||||
goto out;
|
||||
if (tolower((u_char)s[0]) != 'y' || s[1] != '\0')
|
||||
if (s[0] != cdata->confirm_key && (s[0] != '\r' || !cdata->default_yes))
|
||||
goto out;
|
||||
retcode = 0;
|
||||
|
||||
@ -123,12 +144,12 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s,
|
||||
}
|
||||
|
||||
out:
|
||||
if (item != NULL) {
|
||||
if (cmdq_get_client(item) != NULL &&
|
||||
cmdq_get_client(item)->session == NULL)
|
||||
cmdq_get_client(item)->retval = retcode;
|
||||
cmdq_continue(item);
|
||||
}
|
||||
if (item != NULL) {
|
||||
if (cmdq_get_client(item) != NULL &&
|
||||
cmdq_get_client(item)->session == NULL)
|
||||
cmdq_get_client(item)->retval = retcode;
|
||||
cmdq_continue(item);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
@ -30,8 +30,8 @@ const struct cmd_entry cmd_copy_mode_entry = {
|
||||
.name = "copy-mode",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "eHMs:t:uq", 0, 0, NULL },
|
||||
.usage = "[-eHMuq] [-s src-pane] " CMD_TARGET_PANE_USAGE,
|
||||
.args = { "deHMqSs:t:u", 0, 0, NULL },
|
||||
.usage = "[-deHMqSu] [-s src-pane] " CMD_TARGET_PANE_USAGE,
|
||||
|
||||
.source = { 's', CMD_FIND_PANE, 0 },
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
@ -91,6 +91,13 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
if (args_has(args, 'u'))
|
||||
window_copy_pageup(wp, 0);
|
||||
if (args_has(args, 'd'))
|
||||
window_copy_pagedown(wp, 0, args_has(args, 'e'));
|
||||
if (args_has(args, 'S')) {
|
||||
window_copy_scroll(wp, c->tty.mouse_slider_mpos, event->m.y,
|
||||
args_has(args, 'e'));
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
@ -38,9 +38,11 @@ const struct cmd_entry cmd_display_menu_entry = {
|
||||
.name = "display-menu",
|
||||
.alias = "menu",
|
||||
|
||||
.args = { "c:t:OT:x:y:", 1, -1, cmd_display_menu_args_parse },
|
||||
.usage = "[-O] [-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] "
|
||||
"[-x position] [-y position] name key command ...",
|
||||
.args = { "b:c:C:H:s:S:MOt:T:x:y:", 1, -1, cmd_display_menu_args_parse },
|
||||
.usage = "[-MO] [-b border-lines] [-c target-client] "
|
||||
"[-C starting-choice] [-H selected-style] [-s style] "
|
||||
"[-S border-style] " CMD_TARGET_PANE_USAGE " [-T title] "
|
||||
"[-x position] [-y position] name [key] [command] ...",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
@ -56,8 +58,8 @@ const struct cmd_entry cmd_display_popup_entry = {
|
||||
.usage = "[-BCE] [-b border-lines] [-c target-client] "
|
||||
"[-d start-directory] [-e environment] [-h height] "
|
||||
"[-s style] [-S border-style] " CMD_TARGET_PANE_USAGE
|
||||
"[-T title] [-w width] [-x position] [-y position] "
|
||||
"[shell-command]",
|
||||
" [-T title] [-w width] [-x position] [-y position] "
|
||||
"[shell-command [argument ...]]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
@ -274,6 +276,7 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item,
|
||||
log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h);
|
||||
free(p);
|
||||
|
||||
format_free(ft);
|
||||
return (1);
|
||||
}
|
||||
|
||||
@ -286,19 +289,41 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct client *tc = cmdq_get_target_client(item);
|
||||
struct menu *menu = NULL;
|
||||
struct menu_item menu_item;
|
||||
const char *key, *name;
|
||||
char *title;
|
||||
int flags = 0;
|
||||
const char *key, *name, *value;
|
||||
const char *style = args_get(args, 's');
|
||||
const char *border_style = args_get(args, 'S');
|
||||
const char *selected_style = args_get(args, 'H');
|
||||
enum box_lines lines = BOX_LINES_DEFAULT;
|
||||
char *title, *cause;
|
||||
int flags = 0, starting_choice = 0;
|
||||
u_int px, py, i, count = args_count(args);
|
||||
struct options *o = target->s->curw->window->options;
|
||||
struct options_entry *oe;
|
||||
|
||||
|
||||
if (tc->overlay_draw != NULL)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
if (args_has(args, 'C')) {
|
||||
if (strcmp(args_get(args, 'C'), "-") == 0)
|
||||
starting_choice = -1;
|
||||
else {
|
||||
starting_choice = args_strtonum(args, 'C', 0, UINT_MAX,
|
||||
&cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(item, "starting choice %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args_has(args, 'T'))
|
||||
title = format_single_from_target(item, args_get(args, 'T'));
|
||||
else
|
||||
title = xstrdup("");
|
||||
menu = menu_create(title);
|
||||
free(title);
|
||||
|
||||
for (i = 0; i != count; /* nothing */) {
|
||||
name = args_string(args, i++);
|
||||
@ -309,7 +334,6 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
|
||||
|
||||
if (count - i < 2) {
|
||||
cmdq_error(item, "not enough arguments");
|
||||
free(title);
|
||||
menu_free(menu);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
@ -321,7 +345,6 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
|
||||
|
||||
menu_add_item(menu, &menu_item, item, tc, target);
|
||||
}
|
||||
free(title);
|
||||
if (menu == NULL) {
|
||||
cmdq_error(item, "invalid menu arguments");
|
||||
return (CMD_RETURN_ERROR);
|
||||
@ -336,12 +359,24 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
value = args_get(args, 'b');
|
||||
if (value != NULL) {
|
||||
oe = options_get(o, "menu-border-lines");
|
||||
lines = options_find_choice(options_table_entry(oe), value,
|
||||
&cause);
|
||||
if (lines == -1) {
|
||||
cmdq_error(item, "menu-border-lines %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
if (args_has(args, 'O'))
|
||||
flags |= MENU_STAYOPEN;
|
||||
if (!event->m.valid)
|
||||
if (!event->m.valid && !args_has(args, 'M'))
|
||||
flags |= MENU_NOMOUSE;
|
||||
if (menu_display(menu, flags, item, px, py, tc, target, NULL,
|
||||
NULL) != 0)
|
||||
if (menu_display(menu, flags, starting_choice, item, px, py, tc, lines,
|
||||
style, selected_style, border_style, target, NULL, NULL) != 0)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
return (CMD_RETURN_WAIT);
|
||||
}
|
||||
@ -454,11 +489,13 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
|
||||
cmd_free_argv(argc, argv);
|
||||
if (env != NULL)
|
||||
environ_free(env);
|
||||
free(cwd);
|
||||
free(title);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
if (env != NULL)
|
||||
environ_free(env);
|
||||
free(cwd);
|
||||
free(title);
|
||||
cmd_free_argv(argc, argv);
|
||||
return (CMD_RETURN_WAIT);
|
||||
|
@ -39,8 +39,8 @@ const struct cmd_entry cmd_display_message_entry = {
|
||||
.name = "display-message",
|
||||
.alias = "display",
|
||||
|
||||
.args = { "ac:d:INpt:F:v", 0, 1, NULL },
|
||||
.usage = "[-aINpv] [-c target-client] [-d delay] [-F format] "
|
||||
.args = { "aCc:d:lINpt:F:v", 0, 1, NULL },
|
||||
.usage = "[-aCIlNpv] [-c target-client] [-d delay] [-F format] "
|
||||
CMD_TARGET_PANE_USAGE " [message]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
|
||||
@ -68,9 +68,11 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct window_pane *wp = target->wp;
|
||||
const char *template;
|
||||
char *msg, *cause;
|
||||
int delay = -1, flags;
|
||||
int delay = -1, flags, Nflag = args_has(args, 'N');
|
||||
int Cflag = args_has(args, 'C');
|
||||
struct format_tree *ft;
|
||||
u_int count = args_count(args);
|
||||
struct evbuffer *evb;
|
||||
|
||||
if (args_has(args, 'I')) {
|
||||
if (wp == NULL)
|
||||
@ -132,15 +134,24 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
msg = format_expand_time(ft, template);
|
||||
if (args_has(args, 'l'))
|
||||
msg = xstrdup(template);
|
||||
else
|
||||
msg = format_expand_time(ft, template);
|
||||
|
||||
if (cmdq_get_client(item) == NULL)
|
||||
cmdq_error(item, "%s", msg);
|
||||
else if (args_has(args, 'p'))
|
||||
cmdq_print(item, "%s", msg);
|
||||
else if (tc != NULL) {
|
||||
status_message_set(tc, delay, 0, args_has(args, 'N'), "%s",
|
||||
msg);
|
||||
}
|
||||
else if (tc != NULL && (tc->flags & CLIENT_CONTROL)) {
|
||||
evb = evbuffer_new();
|
||||
if (evb == NULL)
|
||||
fatalx("out of memory");
|
||||
evbuffer_add_printf(evb, "%%message %s", msg);
|
||||
server_client_print(tc, 0, evb);
|
||||
evbuffer_free(evb);
|
||||
} else if (tc != NULL)
|
||||
status_message_set(tc, delay, 0, Nflag, Cflag, "%s", msg);
|
||||
free(msg);
|
||||
|
||||
format_free(ft);
|
||||
|
@ -144,7 +144,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
|
||||
llen = 0;
|
||||
|
||||
if (sx < len * 6 || sy < 5) {
|
||||
tty_attributes(tty, &fgc, &grid_default_cell, NULL);
|
||||
tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
|
||||
if (sx >= len + llen + 1) {
|
||||
len += llen + 1;
|
||||
tty_cursor(tty, xoff + px - len / 2, yoff + py);
|
||||
@ -161,7 +161,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
|
||||
px -= len * 3;
|
||||
py -= 2;
|
||||
|
||||
tty_attributes(tty, &bgc, &grid_default_cell, NULL);
|
||||
tty_attributes(tty, &bgc, &grid_default_cell, NULL, NULL);
|
||||
for (ptr = buf; *ptr != '\0'; ptr++) {
|
||||
if (*ptr < '0' || *ptr > '9')
|
||||
continue;
|
||||
@ -179,7 +179,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
|
||||
|
||||
if (sy <= 6)
|
||||
goto out;
|
||||
tty_attributes(tty, &fgc, &grid_default_cell, NULL);
|
||||
tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
|
||||
if (rlen != 0 && sx >= rlen) {
|
||||
tty_cursor(tty, xoff + sx - rlen, yoff);
|
||||
tty_putn(tty, rbuf, rlen, rlen);
|
||||
@ -246,7 +246,7 @@ cmd_display_panes_key(struct client *c, void *data, struct key_event *event)
|
||||
wp = window_pane_at_index(w, index);
|
||||
if (wp == NULL)
|
||||
return (1);
|
||||
window_unzoom(w);
|
||||
window_unzoom(w, 1);
|
||||
|
||||
xasprintf(&expanded, "%%%u", wp->id);
|
||||
|
||||
|
@ -48,6 +48,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct window_pane *wp = target->wp;
|
||||
const char *s = args_string(args, 0), *suffix = "";
|
||||
const char *star = "*";
|
||||
struct args_value *filter;
|
||||
int C, N, T;
|
||||
|
||||
@ -55,6 +56,8 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
N = args_has(args, 'N');
|
||||
T = args_has(args, 'T');
|
||||
|
||||
if (args_has(args, 'r'))
|
||||
star = "";
|
||||
if (args_has(args, 'r') && args_has(args, 'i'))
|
||||
suffix = "/ri";
|
||||
else if (args_has(args, 'r'))
|
||||
@ -71,40 +74,40 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if (C && N && T) {
|
||||
xasprintf(&filter->string,
|
||||
"#{||:"
|
||||
"#{C%s:%s},#{||:#{m%s:*%s*,#{window_name}},"
|
||||
"#{m%s:*%s*,#{pane_title}}}}",
|
||||
suffix, s, suffix, s, suffix, s);
|
||||
"#{C%s:%s},#{||:#{m%s:%s%s%s,#{window_name}},"
|
||||
"#{m%s:%s%s%s,#{pane_title}}}}",
|
||||
suffix, s, suffix, star, s, star, suffix, star, s, star);
|
||||
} else if (C && N) {
|
||||
xasprintf(&filter->string,
|
||||
"#{||:#{C%s:%s},#{m%s:*%s*,#{window_name}}}",
|
||||
suffix, s, suffix, s);
|
||||
"#{||:#{C%s:%s},#{m%s:%s%s%s,#{window_name}}}",
|
||||
suffix, s, suffix, star, s, star);
|
||||
} else if (C && T) {
|
||||
xasprintf(&filter->string,
|
||||
"#{||:#{C%s:%s},#{m%s:*%s*,#{pane_title}}}",
|
||||
suffix, s, suffix, s);
|
||||
"#{||:#{C%s:%s},#{m%s:%s%s%s,#{pane_title}}}",
|
||||
suffix, s, suffix, star, s, star);
|
||||
} else if (N && T) {
|
||||
xasprintf(&filter->string,
|
||||
"#{||:#{m%s:*%s*,#{window_name}},"
|
||||
"#{m%s:*%s*,#{pane_title}}}",
|
||||
suffix, s, suffix, s);
|
||||
"#{||:#{m%s:%s%s%s,#{window_name}},"
|
||||
"#{m%s:%s%s%s,#{pane_title}}}",
|
||||
suffix, star, s, star, suffix, star, s, star);
|
||||
} else if (C) {
|
||||
xasprintf(&filter->string,
|
||||
"#{C%s:%s}",
|
||||
suffix, s);
|
||||
} else if (N) {
|
||||
xasprintf(&filter->string,
|
||||
"#{m%s:*%s*,#{window_name}}",
|
||||
suffix, s);
|
||||
"#{m%s:%s%s%s,#{window_name}}",
|
||||
suffix, star, s, star);
|
||||
} else {
|
||||
xasprintf(&filter->string,
|
||||
"#{m%s:*%s*,#{pane_title}}",
|
||||
suffix, s);
|
||||
"#{m%s:%s%s%s,#{pane_title}}",
|
||||
suffix, star, s, star);
|
||||
}
|
||||
|
||||
new_args = args_create();
|
||||
if (args_has(args, 'Z'))
|
||||
args_set(new_args, 'Z', NULL);
|
||||
args_set(new_args, 'f', filter);
|
||||
args_set(new_args, 'Z', NULL, 0);
|
||||
args_set(new_args, 'f', filter, 0);
|
||||
|
||||
window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args);
|
||||
args_free(new_args);
|
||||
|
12
cmd-find.c
12
cmd-find.c
@ -582,27 +582,27 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
|
||||
|
||||
/* Try special characters. */
|
||||
if (strcmp(pane, "!") == 0) {
|
||||
fs->wp = fs->w->last;
|
||||
fs->wp = TAILQ_FIRST(&fs->w->last_panes);
|
||||
if (fs->wp == NULL)
|
||||
return (-1);
|
||||
return (0);
|
||||
} else if (strcmp(pane, "{up-of}") == 0) {
|
||||
fs->wp = window_pane_find_up(fs->current->wp);
|
||||
fs->wp = window_pane_find_up(fs->w->active);
|
||||
if (fs->wp == NULL)
|
||||
return (-1);
|
||||
return (0);
|
||||
} else if (strcmp(pane, "{down-of}") == 0) {
|
||||
fs->wp = window_pane_find_down(fs->current->wp);
|
||||
fs->wp = window_pane_find_down(fs->w->active);
|
||||
if (fs->wp == NULL)
|
||||
return (-1);
|
||||
return (0);
|
||||
} else if (strcmp(pane, "{left-of}") == 0) {
|
||||
fs->wp = window_pane_find_left(fs->current->wp);
|
||||
fs->wp = window_pane_find_left(fs->w->active);
|
||||
if (fs->wp == NULL)
|
||||
return (-1);
|
||||
return (0);
|
||||
} else if (strcmp(pane, "{right-of}") == 0) {
|
||||
fs->wp = window_pane_find_right(fs->current->wp);
|
||||
fs->wp = window_pane_find_right(fs->w->active);
|
||||
if (fs->wp == NULL)
|
||||
return (-1);
|
||||
return (0);
|
||||
@ -614,7 +614,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
|
||||
n = strtonum(pane + 1, 1, INT_MAX, NULL);
|
||||
else
|
||||
n = 1;
|
||||
wp = fs->current->wp;
|
||||
wp = fs->w->active;
|
||||
if (pane[0] == '+')
|
||||
fs->wp = window_pane_next_by_number(fs->w, wp, n);
|
||||
else
|
||||
|
@ -157,7 +157,7 @@ cmd_if_shell_callback(struct job *job)
|
||||
if (cmdlist == NULL) {
|
||||
if (cdata->item == NULL) {
|
||||
*error = toupper((u_char)*error);
|
||||
status_message_set(c, -1, 1, 0, "%s", error);
|
||||
status_message_set(c, -1, 1, 0, 0, "%s", error);
|
||||
} else
|
||||
cmdq_error(cdata->item, "%s", error);
|
||||
free(error);
|
||||
|
@ -71,10 +71,11 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct window *src_w, *dst_w;
|
||||
struct window_pane *src_wp, *dst_wp;
|
||||
char *cause = NULL;
|
||||
int size, percentage, dst_idx;
|
||||
int size, dst_idx;
|
||||
int flags;
|
||||
enum layout_type type;
|
||||
struct layout_cell *lc;
|
||||
u_int curval = 0;
|
||||
|
||||
dst_s = target->s;
|
||||
dst_wl = target->wl;
|
||||
@ -97,23 +98,30 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if (args_has(args, 'h'))
|
||||
type = LAYOUT_LEFTRIGHT;
|
||||
|
||||
/* If the 'p' flag is dropped then this bit can be moved into 'l'. */
|
||||
if (args_has(args, 'l') || args_has(args, 'p')) {
|
||||
if (args_has(args, 'f')) {
|
||||
if (type == LAYOUT_TOPBOTTOM)
|
||||
curval = dst_w->sy;
|
||||
else
|
||||
curval = dst_w->sx;
|
||||
} else {
|
||||
if (type == LAYOUT_TOPBOTTOM)
|
||||
curval = dst_wp->sy;
|
||||
else
|
||||
curval = dst_wp->sx;
|
||||
}
|
||||
}
|
||||
|
||||
size = -1;
|
||||
if (args_has(args, 'l')) {
|
||||
if (type == LAYOUT_TOPBOTTOM) {
|
||||
size = args_percentage(args, 'l', 0, INT_MAX,
|
||||
dst_wp->sy, &cause);
|
||||
} else {
|
||||
size = args_percentage(args, 'l', 0, INT_MAX,
|
||||
dst_wp->sx, &cause);
|
||||
}
|
||||
size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
|
||||
item, &cause);
|
||||
} else if (args_has(args, 'p')) {
|
||||
percentage = args_strtonum(args, 'p', 0, 100, &cause);
|
||||
if (cause == NULL) {
|
||||
if (type == LAYOUT_TOPBOTTOM)
|
||||
size = (dst_wp->sy * percentage) / 100;
|
||||
else
|
||||
size = (dst_wp->sx * percentage) / 100;
|
||||
}
|
||||
size = args_strtonum_and_expand(args, 'l', 0, 100, item,
|
||||
&cause);
|
||||
if (cause == NULL)
|
||||
size = curval * size / 100;
|
||||
}
|
||||
if (cause != NULL) {
|
||||
cmdq_error(item, "size %s", cause);
|
||||
@ -141,12 +149,13 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
|
||||
src_wp->window = dst_w;
|
||||
options_set_parent(src_wp->options, dst_w->options);
|
||||
src_wp->flags |= PANE_STYLECHANGED;
|
||||
src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
|
||||
if (flags & SPAWN_BEFORE)
|
||||
TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry);
|
||||
else
|
||||
TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
|
||||
layout_assign_pane(lc, src_wp, 0);
|
||||
colour_palette_from_option(&src_wp->palette, src_wp->options);
|
||||
|
||||
recalculate_sizes();
|
||||
|
||||
|
@ -41,8 +41,8 @@ const struct cmd_entry cmd_list_clients_entry = {
|
||||
.name = "list-clients",
|
||||
.alias = "lsc",
|
||||
|
||||
.args = { "F:t:", 0, 0, NULL },
|
||||
.usage = "[-F format] " CMD_TARGET_SESSION_USAGE,
|
||||
.args = { "F:f:t:", 0, 0, NULL },
|
||||
.usage = "[-F format] [-f filter] " CMD_TARGET_SESSION_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_SESSION, 0 },
|
||||
|
||||
@ -58,9 +58,10 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct client *c;
|
||||
struct session *s;
|
||||
struct format_tree *ft;
|
||||
const char *template;
|
||||
const char *template, *filter;
|
||||
u_int idx;
|
||||
char *line;
|
||||
char *line, *expanded;
|
||||
int flag;
|
||||
|
||||
if (args_has(args, 't'))
|
||||
s = target->s;
|
||||
@ -69,6 +70,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
|
||||
|
||||
if ((template = args_get(args, 'F')) == NULL)
|
||||
template = LIST_CLIENTS_TEMPLATE;
|
||||
filter = args_get(args, 'f');
|
||||
|
||||
idx = 0;
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
@ -79,9 +81,17 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
|
||||
format_add(ft, "line", "%u", idx);
|
||||
format_defaults(ft, c, NULL, NULL, NULL);
|
||||
|
||||
line = format_expand(ft, template);
|
||||
cmdq_print(item, "%s", line);
|
||||
free(line);
|
||||
if (filter != NULL) {
|
||||
expanded = format_expand(ft, filter);
|
||||
flag = format_true(expanded);
|
||||
free(expanded);
|
||||
} else
|
||||
flag = 1;
|
||||
if (flag) {
|
||||
line = format_expand(ft, template);
|
||||
cmdq_print(item, "%s", line);
|
||||
free(line);
|
||||
}
|
||||
|
||||
format_free(ft);
|
||||
|
||||
|
@ -91,7 +91,7 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
|
||||
struct key_binding *bd;
|
||||
const char *key;
|
||||
char *tmp, *note;
|
||||
int found = 0;
|
||||
int found = 0;
|
||||
|
||||
table = key_bindings_get_table(tablename, 0);
|
||||
if (table == NULL)
|
||||
@ -114,8 +114,8 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
|
||||
note = xstrdup(bd->note);
|
||||
tmp = utf8_padcstr(key, keywidth + 1);
|
||||
if (args_has(args, '1') && tc != NULL) {
|
||||
status_message_set(tc, -1, 1, 0, "%s%s%s", prefix, tmp,
|
||||
note);
|
||||
status_message_set(tc, -1, 1, 0, 0, "%s%s%s", prefix,
|
||||
tmp, note);
|
||||
} else
|
||||
cmdq_print(item, "%s%s%s", prefix, tmp, note);
|
||||
free(tmp);
|
||||
@ -148,6 +148,7 @@ static enum cmd_retval
|
||||
cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
struct args *args = cmd_get_args(self);
|
||||
struct client *tc = cmdq_get_target_client(item);
|
||||
struct key_table *table;
|
||||
struct key_binding *bd;
|
||||
const char *tablename, *r, *keystr;
|
||||
@ -296,9 +297,15 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
|
||||
strlcat(tmp, cp, tmpsize);
|
||||
free(cp);
|
||||
|
||||
cmdq_print(item, "bind-key %s", tmp);
|
||||
|
||||
if (args_has(args, '1') && tc != NULL) {
|
||||
status_message_set(tc, -1, 1, 0, 0,
|
||||
"bind-key %s", tmp);
|
||||
} else
|
||||
cmdq_print(item, "bind-key %s", tmp);
|
||||
free(key);
|
||||
|
||||
if (args_has(args, '1'))
|
||||
break;
|
||||
bd = key_bindings_next(table, bd);
|
||||
}
|
||||
table = key_bindings_next_table(table);
|
||||
@ -314,6 +321,31 @@ out:
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
|
||||
const char *template, struct cmdq_item *item)
|
||||
{
|
||||
const char *s;
|
||||
char *line;
|
||||
|
||||
format_add(ft, "command_list_name", "%s", entry->name);
|
||||
if (entry->alias != NULL)
|
||||
s = entry->alias;
|
||||
else
|
||||
s = "";
|
||||
format_add(ft, "command_list_alias", "%s", s);
|
||||
if (entry->usage != NULL)
|
||||
s = entry->usage;
|
||||
else
|
||||
s = "";
|
||||
format_add(ft, "command_list_usage", "%s", s);
|
||||
|
||||
line = format_expand(ft, template);
|
||||
if (*line != '\0')
|
||||
cmdq_print(item, "%s", line);
|
||||
free(line);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
@ -321,8 +353,8 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
|
||||
const struct cmd_entry **entryp;
|
||||
const struct cmd_entry *entry;
|
||||
struct format_tree *ft;
|
||||
const char *template, *s, *command;
|
||||
char *line;
|
||||
const char *template, *command;
|
||||
char *cause;
|
||||
|
||||
if ((template = args_get(args, 'F')) == NULL) {
|
||||
template = "#{command_list_name}"
|
||||
@ -334,30 +366,19 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
|
||||
format_defaults(ft, NULL, NULL, NULL, NULL);
|
||||
|
||||
command = args_string(args, 0);
|
||||
for (entryp = cmd_table; *entryp != NULL; entryp++) {
|
||||
entry = *entryp;
|
||||
if (command != NULL &&
|
||||
(strcmp(entry->name, command) != 0 &&
|
||||
(entry->alias == NULL ||
|
||||
strcmp(entry->alias, command) != 0)))
|
||||
continue;
|
||||
|
||||
format_add(ft, "command_list_name", "%s", entry->name);
|
||||
if (entry->alias != NULL)
|
||||
s = entry->alias;
|
||||
else
|
||||
s = "";
|
||||
format_add(ft, "command_list_alias", "%s", s);
|
||||
if (entry->usage != NULL)
|
||||
s = entry->usage;
|
||||
else
|
||||
s = "";
|
||||
format_add(ft, "command_list_usage", "%s", s);
|
||||
|
||||
line = format_expand(ft, template);
|
||||
if (*line != '\0')
|
||||
cmdq_print(item, "%s", line);
|
||||
free(line);
|
||||
if (command == NULL) {
|
||||
for (entryp = cmd_table; *entryp != NULL; entryp++)
|
||||
cmd_list_single_command(*entryp, ft, template, item);
|
||||
} else {
|
||||
entry = cmd_find(command, &cause);
|
||||
if (entry != NULL)
|
||||
cmd_list_single_command(entry, ft, template, item);
|
||||
else {
|
||||
cmdq_error(item, "%s", cause);
|
||||
free(cause);
|
||||
format_free(ft);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
format_free(ft);
|
||||
|
@ -77,7 +77,7 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
|
||||
} else if (tc != NULL &&
|
||||
tc->session != NULL &&
|
||||
(~tc->flags & CLIENT_DEAD))
|
||||
tty_set_selection(&tc->tty, copy, bsize);
|
||||
tty_set_selection(&tc->tty, "", copy, bsize);
|
||||
if (tc != NULL)
|
||||
server_client_unref(tc);
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ const struct cmd_entry cmd_new_session_entry = {
|
||||
.usage = "[-AdDEPX] [-c start-directory] [-e environment] [-F format] "
|
||||
"[-f flags] [-n window-name] [-s session-name] "
|
||||
CMD_TARGET_SESSION_USAGE " [-x width] [-y height] "
|
||||
"[shell-command]",
|
||||
"[shell-command [argument ...]]",
|
||||
|
||||
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
|
||||
|
||||
@ -333,13 +333,6 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
|
||||
server_client_set_key_table(c, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* If there are still configuration file errors to display, put the new
|
||||
* session's current window into more mode and display them now.
|
||||
*/
|
||||
if (cfg_finished)
|
||||
cfg_show_causes(s);
|
||||
|
||||
/* Print if requested. */
|
||||
if (args_has(args, 'P')) {
|
||||
if ((template = args_get(args, 'F')) == NULL)
|
||||
@ -357,6 +350,9 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
|
||||
cmd_find_from_session(&fs, s, 0);
|
||||
cmdq_insert_hook(s, item, &fs, "after-new-session");
|
||||
|
||||
if (cfg_finished)
|
||||
cfg_show_causes(s);
|
||||
|
||||
if (sc.argv != NULL)
|
||||
cmd_free_argv(sc.argc, sc.argv);
|
||||
free(cwd);
|
||||
|
@ -40,7 +40,8 @@ const struct cmd_entry cmd_new_window_entry = {
|
||||
|
||||
.args = { "abc:de:F:kn:PSt:", 0, -1, NULL },
|
||||
.usage = "[-abdkPS] [-c start-directory] [-e environment] [-F format] "
|
||||
"[-n window-name] " CMD_TARGET_WINDOW_USAGE " [shell-command]",
|
||||
"[-n window-name] " CMD_TARGET_WINDOW_USAGE
|
||||
" [shell-command [argument ...]]",
|
||||
|
||||
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },
|
||||
|
||||
@ -60,7 +61,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct session *s = target->s;
|
||||
struct winlink *wl = target->wl, *new_wl = NULL;
|
||||
int idx = target->idx, before;
|
||||
char *cause = NULL, *cp;
|
||||
char *cause = NULL, *cp, *expanded;
|
||||
const char *template, *name;
|
||||
struct cmd_find_state fs;
|
||||
struct args_value *av;
|
||||
@ -71,16 +72,19 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
*/
|
||||
name = args_get(args, 'n');
|
||||
if (args_has(args, 'S') && name != NULL && target->idx == -1) {
|
||||
expanded = format_single(item, name, c, s, NULL, NULL);
|
||||
RB_FOREACH(wl, winlinks, &s->windows) {
|
||||
if (strcmp(wl->window->name, name) != 0)
|
||||
if (strcmp(wl->window->name, expanded) != 0)
|
||||
continue;
|
||||
if (new_wl == NULL) {
|
||||
new_wl = wl;
|
||||
continue;
|
||||
}
|
||||
cmdq_error(item, "multiple windows named %s", name);
|
||||
free(expanded);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
free(expanded);
|
||||
if (new_wl != NULL) {
|
||||
if (args_has(args, 'd'))
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
63
cmd-parse.y
63
cmd-parse.y
@ -223,9 +223,16 @@ assignment : EQUALS
|
||||
{
|
||||
struct cmd_parse_state *ps = &parse_state;
|
||||
int flags = ps->input->flags;
|
||||
int flag = 1;
|
||||
struct cmd_parse_scope *scope;
|
||||
|
||||
if ((~flags & CMD_PARSE_PARSEONLY) &&
|
||||
(ps->scope == NULL || ps->scope->flag))
|
||||
if (ps->scope != NULL) {
|
||||
flag = ps->scope->flag;
|
||||
TAILQ_FOREACH(scope, &ps->stack, entry)
|
||||
flag = flag && scope->flag;
|
||||
}
|
||||
|
||||
if ((~flags & CMD_PARSE_PARSEONLY) && flag)
|
||||
environ_put(global_environ, $1, 0);
|
||||
free($1);
|
||||
}
|
||||
@ -234,9 +241,16 @@ hidden_assignment : HIDDEN EQUALS
|
||||
{
|
||||
struct cmd_parse_state *ps = &parse_state;
|
||||
int flags = ps->input->flags;
|
||||
int flag = 1;
|
||||
struct cmd_parse_scope *scope;
|
||||
|
||||
if ((~flags & CMD_PARSE_PARSEONLY) &&
|
||||
(ps->scope == NULL || ps->scope->flag))
|
||||
if (ps->scope != NULL) {
|
||||
flag = ps->scope->flag;
|
||||
TAILQ_FOREACH(scope, &ps->stack, entry)
|
||||
flag = flag && scope->flag;
|
||||
}
|
||||
|
||||
if ((~flags & CMD_PARSE_PARSEONLY) && flag)
|
||||
environ_put(global_environ, $2, ENVIRON_HIDDEN);
|
||||
free($2);
|
||||
}
|
||||
@ -1086,7 +1100,8 @@ cmd_parse_from_arguments(struct args_value *values, u_int count,
|
||||
arg->type = CMD_PARSE_STRING;
|
||||
arg->string = copy;
|
||||
TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry);
|
||||
}
|
||||
} else
|
||||
free(copy);
|
||||
} else if (values[i].type == ARGS_COMMANDS) {
|
||||
arg = xcalloc(1, sizeof *arg);
|
||||
arg->type = CMD_PARSE_PARSED_COMMANDS;
|
||||
@ -1272,6 +1287,16 @@ yylex(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\r') {
|
||||
/*
|
||||
* Treat \r\n as \n.
|
||||
*/
|
||||
ch = yylex_getc();
|
||||
if (ch != '\n') {
|
||||
yylex_ungetc(ch);
|
||||
ch = '\r';
|
||||
}
|
||||
}
|
||||
if (ch == '\n') {
|
||||
/*
|
||||
* End of line. Update the line number.
|
||||
@ -1602,6 +1627,7 @@ yylex_token_tilde(char **buf, size_t *len)
|
||||
static char *
|
||||
yylex_token(int ch)
|
||||
{
|
||||
struct cmd_parse_state *ps = &parse_state;
|
||||
char *buf;
|
||||
size_t len;
|
||||
enum { START,
|
||||
@ -1614,13 +1640,34 @@ yylex_token(int ch)
|
||||
|
||||
for (;;) {
|
||||
/* EOF or \n are always the end of the token. */
|
||||
if (ch == EOF || (state == NONE && ch == '\n'))
|
||||
if (ch == EOF) {
|
||||
log_debug("%s: end at EOF", __func__);
|
||||
break;
|
||||
}
|
||||
if (state == NONE && ch == '\r') {
|
||||
ch = yylex_getc();
|
||||
if (ch != '\n') {
|
||||
yylex_ungetc(ch);
|
||||
ch = '\r';
|
||||
}
|
||||
}
|
||||
if (ch == '\n') {
|
||||
if (state == NONE) {
|
||||
log_debug("%s: end at EOL", __func__);
|
||||
break;
|
||||
}
|
||||
ps->input->line++;
|
||||
}
|
||||
|
||||
/* Whitespace or ; or } ends a token unless inside quotes. */
|
||||
if ((ch == ' ' || ch == '\t' || ch == ';' || ch == '}') &&
|
||||
state == NONE)
|
||||
if (state == NONE && (ch == ' ' || ch == '\t')) {
|
||||
log_debug("%s: end at WS", __func__);
|
||||
break;
|
||||
}
|
||||
if (state == NONE && (ch == ';' || ch == '}')) {
|
||||
log_debug("%s: end at %c", __func__, ch);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Spaces and comments inside quotes after \n are removed but
|
||||
|
@ -54,6 +54,11 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
||||
size_t seplen, bufsize;
|
||||
int bracket = args_has(args, 'p');
|
||||
|
||||
if (window_pane_exited(wp)) {
|
||||
cmdq_error(item, "target pane has exited");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
bufname = NULL;
|
||||
if (args_has(args, 'b'))
|
||||
bufname = args_get(args, 'b');
|
||||
|
@ -68,7 +68,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
sigset_t set, oldset;
|
||||
|
||||
/* Do nothing if pane is dead. */
|
||||
if (wp->fd == -1 || (wp->flags & PANE_EXITED)) {
|
||||
if (window_pane_exited(wp)) {
|
||||
cmdq_error(item, "target pane has exited");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
74
cmd-queue.c
74
cmd-queue.c
@ -236,8 +236,10 @@ cmdq_link_state(struct cmdq_state *state)
|
||||
|
||||
/* Make a copy of a state. */
|
||||
struct cmdq_state *
|
||||
cmdq_copy_state(struct cmdq_state *state)
|
||||
cmdq_copy_state(struct cmdq_state *state, struct cmd_find_state *current)
|
||||
{
|
||||
if (current != NULL)
|
||||
return (cmdq_new_state(current, &state->event, state->flags));
|
||||
return (cmdq_new_state(&state->current, &state->event, state->flags));
|
||||
}
|
||||
|
||||
@ -662,9 +664,18 @@ cmdq_fire_command(struct cmdq_item *item)
|
||||
|
||||
out:
|
||||
item->client = saved;
|
||||
if (retval == CMD_RETURN_ERROR)
|
||||
if (retval == CMD_RETURN_ERROR) {
|
||||
fsp = NULL;
|
||||
if (cmd_find_valid_state(&item->target))
|
||||
fsp = &item->target;
|
||||
else if (cmd_find_valid_state(&item->state->current))
|
||||
fsp = &item->state->current;
|
||||
else if (cmd_find_from_client(&fs, item->client, 0) == 0)
|
||||
fsp = &fs;
|
||||
cmdq_insert_hook(fsp != NULL ? fsp->s : NULL, item, fsp,
|
||||
"command-error");
|
||||
cmdq_guard(item, "error", flags);
|
||||
else
|
||||
} else
|
||||
cmdq_guard(item, "end", flags);
|
||||
return (retval);
|
||||
}
|
||||
@ -803,10 +814,10 @@ cmdq_running(struct client *c)
|
||||
struct cmdq_list *queue = cmdq_get(c);
|
||||
|
||||
if (queue->item == NULL)
|
||||
return (NULL);
|
||||
if (queue->item->flags & CMDQ_WAITING)
|
||||
return (NULL);
|
||||
return (queue->item);
|
||||
return (NULL);
|
||||
if (queue->item->flags & CMDQ_WAITING)
|
||||
return (NULL);
|
||||
return (queue->item);
|
||||
}
|
||||
|
||||
/* Print a guard line. */
|
||||
@ -821,45 +832,30 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
|
||||
control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
|
||||
}
|
||||
|
||||
/* Show message from command. */
|
||||
void
|
||||
cmdq_print_data(struct cmdq_item *item, struct evbuffer *evb)
|
||||
{
|
||||
server_client_print(item->client, 1, evb);
|
||||
}
|
||||
|
||||
/* Show message from command. */
|
||||
void
|
||||
cmdq_print(struct cmdq_item *item, const char *fmt, ...)
|
||||
{
|
||||
struct client *c = item->client;
|
||||
struct window_pane *wp;
|
||||
struct window_mode_entry *wme;
|
||||
va_list ap;
|
||||
char *tmp, *msg;
|
||||
va_list ap;
|
||||
struct evbuffer *evb;
|
||||
|
||||
evb = evbuffer_new();
|
||||
if (evb == NULL)
|
||||
fatalx("out of memory");
|
||||
|
||||
va_start(ap, fmt);
|
||||
xvasprintf(&msg, fmt, ap);
|
||||
evbuffer_add_vprintf(evb, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
log_debug("%s: %s", __func__, msg);
|
||||
|
||||
if (c == NULL)
|
||||
/* nothing */;
|
||||
else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
|
||||
if (~c->flags & CLIENT_UTF8) {
|
||||
tmp = msg;
|
||||
msg = utf8_sanitize(tmp);
|
||||
free(tmp);
|
||||
}
|
||||
if (c->flags & CLIENT_CONTROL)
|
||||
control_write(c, "%s", msg);
|
||||
else
|
||||
file_print(c, "%s\n", msg);
|
||||
} else {
|
||||
wp = server_client_get_pane(c);
|
||||
wme = TAILQ_FIRST(&wp->modes);
|
||||
if (wme == NULL || wme->mode != &window_view_mode) {
|
||||
window_pane_set_mode(wp, NULL, &window_view_mode, NULL,
|
||||
NULL);
|
||||
}
|
||||
window_copy_add(wp, 0, "%s", msg);
|
||||
}
|
||||
|
||||
free(msg);
|
||||
cmdq_print_data(item, evb);
|
||||
evbuffer_free(evb);
|
||||
}
|
||||
|
||||
/* Show error from command. */
|
||||
@ -896,7 +892,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...)
|
||||
c->retval = 1;
|
||||
} else {
|
||||
*msg = toupper((u_char) *msg);
|
||||
status_message_set(c, -1, 1, 0, "%s", msg);
|
||||
status_message_set(c, -1, 1, 0, 0, "%s", msg);
|
||||
}
|
||||
|
||||
free(msg);
|
||||
|
@ -34,9 +34,10 @@ const struct cmd_entry cmd_refresh_client_entry = {
|
||||
.name = "refresh-client",
|
||||
.alias = "refresh",
|
||||
|
||||
.args = { "A:B:cC:Df:F:l::LRSt:U", 0, 1, NULL },
|
||||
.args = { "A:B:cC:Df:r:F:l::LRSt:U", 0, 1, NULL },
|
||||
.usage = "[-cDlLRSU] [-A pane:state] [-B name:what:format] "
|
||||
"[-C XxY] [-f flags] " CMD_TARGET_CLIENT_USAGE " [adjustment]",
|
||||
"[-C XxY] [-f flags] [-r pane:report] " CMD_TARGET_CLIENT_USAGE
|
||||
" [adjustment]",
|
||||
|
||||
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG,
|
||||
.exec = cmd_refresh_client_exec
|
||||
@ -193,6 +194,34 @@ cmd_refresh_client_clipboard(struct cmd *self, struct cmdq_item *item)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_refresh_report(struct tty *tty, const char *value)
|
||||
{
|
||||
struct window_pane *wp;
|
||||
u_int pane;
|
||||
size_t size = 0;
|
||||
char *copy, *split;
|
||||
|
||||
if (*value != '%')
|
||||
return;
|
||||
copy = xstrdup(value);
|
||||
if ((split = strchr(copy, ':')) == NULL)
|
||||
goto out;
|
||||
*split++ = '\0';
|
||||
|
||||
if (sscanf(copy, "%%%u", &pane) != 1)
|
||||
goto out;
|
||||
wp = window_pane_find_by_id(pane);
|
||||
if (wp == NULL)
|
||||
goto out;
|
||||
|
||||
tty_keys_colours(tty, split, strlen(split), &size, &wp->control_fg,
|
||||
&wp->control_bg);
|
||||
|
||||
out:
|
||||
free(copy);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
@ -262,6 +291,8 @@ cmd_refresh_client_exec(struct cmd *self, struct cmdq_item *item)
|
||||
server_client_set_flags(tc, args_get(args, 'F'));
|
||||
if (args_has(args, 'f'))
|
||||
server_client_set_flags(tc, args_get(args, 'f'));
|
||||
if (args_has(args, 'r'))
|
||||
cmd_refresh_report(tty, args_get(args, 'r'));
|
||||
|
||||
if (args_has(args, 'A')) {
|
||||
if (~tc->flags & CLIENT_CONTROL)
|
||||
|
@ -87,7 +87,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
|
||||
if (args_has(args, 'Z')) {
|
||||
if (w->flags & WINDOW_ZOOMED)
|
||||
window_unzoom(w);
|
||||
window_unzoom(w, 1);
|
||||
else
|
||||
window_zoom(wp);
|
||||
server_redraw_window(w);
|
||||
|
@ -53,8 +53,7 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct session *s = target->s;
|
||||
const char *errstr;
|
||||
char *cause;
|
||||
u_int adjust, sx, sy;
|
||||
int xpixel = -1, ypixel = -1;
|
||||
u_int adjust, sx, sy, xpixel = 0, ypixel = 0;
|
||||
|
||||
if (args_count(args) == 0)
|
||||
adjust = 1;
|
||||
|
@ -36,7 +36,7 @@ const struct cmd_entry cmd_respawn_pane_entry = {
|
||||
|
||||
.args = { "c:e:kt:", 0, -1, NULL },
|
||||
.usage = "[-k] [-c start-directory] [-e environment] "
|
||||
CMD_TARGET_PANE_USAGE " [shell-command]",
|
||||
CMD_TARGET_PANE_USAGE " [shell-command [argument ...]]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
|
@ -36,7 +36,7 @@ const struct cmd_entry cmd_respawn_window_entry = {
|
||||
|
||||
.args = { "c:e:kt:", 0, -1, NULL },
|
||||
.usage = "[-k] [-c start-directory] [-e environment] "
|
||||
CMD_TARGET_WINDOW_USAGE " [shell-command]",
|
||||
CMD_TARGET_WINDOW_USAGE " [shell-command [argument ...]]",
|
||||
|
||||
.target = { 't', CMD_FIND_WINDOW, 0 },
|
||||
|
||||
|
@ -44,8 +44,9 @@ const struct cmd_entry cmd_run_shell_entry = {
|
||||
.name = "run-shell",
|
||||
.alias = "run",
|
||||
|
||||
.args = { "bd:Ct:", 0, 1, cmd_run_shell_args_parse },
|
||||
.usage = "[-bC] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]",
|
||||
.args = { "bd:Ct:Es:c:", 0, 1, cmd_run_shell_args_parse },
|
||||
.usage = "[-bCE] [-c start-directory] [-d delay] " CMD_TARGET_PANE_USAGE
|
||||
" [shell-command]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
|
||||
|
||||
@ -84,12 +85,18 @@ cmd_run_shell_print(struct job *job, const char *msg)
|
||||
|
||||
if (cdata->wp_id != -1)
|
||||
wp = window_pane_find_by_id(cdata->wp_id);
|
||||
if (wp == NULL && cdata->item != NULL && cdata->client != NULL)
|
||||
wp = server_client_get_pane(cdata->client);
|
||||
if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0)
|
||||
wp = fs.wp;
|
||||
if (wp == NULL)
|
||||
return;
|
||||
if (wp == NULL) {
|
||||
if (cdata->item != NULL) {
|
||||
cmdq_print(cdata->item, "%s", msg);
|
||||
return;
|
||||
}
|
||||
if (cdata->item != NULL && cdata->client != NULL)
|
||||
wp = server_client_get_pane(cdata->client);
|
||||
if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0)
|
||||
wp = fs.wp;
|
||||
if (wp == NULL)
|
||||
return;
|
||||
}
|
||||
|
||||
wme = TAILQ_FIRST(&wp->modes);
|
||||
if (wme == NULL || wme->mode != &window_view_mode)
|
||||
@ -103,6 +110,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct args *args = cmd_get_args(self);
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct cmd_run_shell_data *cdata;
|
||||
struct client *c = cmdq_get_client(item);
|
||||
struct client *tc = cmdq_get_target_client(item);
|
||||
struct session *s = target->s;
|
||||
struct window_pane *wp = target->wp;
|
||||
@ -137,7 +145,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
|
||||
cdata->wp_id = -1;
|
||||
|
||||
if (wait) {
|
||||
cdata->client = cmdq_get_client(item);
|
||||
cdata->client = c;
|
||||
cdata->item = item;
|
||||
} else {
|
||||
cdata->client = tc;
|
||||
@ -145,8 +153,13 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
if (cdata->client != NULL)
|
||||
cdata->client->references++;
|
||||
if (args_has(args, 'c'))
|
||||
cdata->cwd = xstrdup(args_get(args, 'c'));
|
||||
else
|
||||
cdata->cwd = xstrdup(server_client_get_cwd(c, s));
|
||||
|
||||
cdata->cwd = xstrdup(server_client_get_cwd(cmdq_get_client(item), s));
|
||||
if (args_has(args, 'E'))
|
||||
cdata->flags |= JOB_SHOWSTDERR;
|
||||
|
||||
cdata->s = s;
|
||||
if (s != NULL)
|
||||
@ -194,7 +207,7 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
|
||||
if (cmdlist == NULL) {
|
||||
if (cdata->item == NULL) {
|
||||
*error = toupper((u_char)*error);
|
||||
status_message_set(c, -1, 1, 0, "%s", error);
|
||||
status_message_set(c, -1, 1, 0, 0, "%s", error);
|
||||
} else
|
||||
cmdq_error(cdata->item, "%s", error);
|
||||
free(error);
|
||||
|
@ -78,7 +78,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
||||
int flags;
|
||||
const char *bufname = args_get(args, 'b'), *bufdata;
|
||||
size_t bufsize;
|
||||
char *path, *tmp;
|
||||
char *path;
|
||||
struct evbuffer *evb;
|
||||
|
||||
if (bufname == NULL) {
|
||||
if ((pb = paste_get_top(NULL)) == NULL) {
|
||||
@ -96,10 +97,12 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
||||
|
||||
if (cmd_get_entry(self) == &cmd_show_buffer_entry) {
|
||||
if (c->session != NULL || (c->flags & CLIENT_CONTROL)) {
|
||||
utf8_stravisx(&tmp, bufdata, bufsize,
|
||||
VIS_OCTAL|VIS_CSTYLE|VIS_TAB);
|
||||
cmdq_print(item, "%s", tmp);
|
||||
free(tmp);
|
||||
evb = evbuffer_new();
|
||||
if (evb == NULL)
|
||||
fatalx("out of memory");
|
||||
evbuffer_add(evb, bufdata, bufsize);
|
||||
cmdq_print_data(item, evb);
|
||||
evbuffer_free(evb);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
path = xstrdup("-");
|
||||
|
@ -98,7 +98,11 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct options_entry *o;
|
||||
|
||||
if (entry == &cmd_last_pane_entry || args_has(args, 'l')) {
|
||||
lastwp = w->last;
|
||||
/*
|
||||
* Check for no last pane found in case the other pane was
|
||||
* spawned without being visited (for example split-window -d).
|
||||
*/
|
||||
lastwp = TAILQ_FIRST(&w->last_panes);
|
||||
if (lastwp == NULL && window_count_panes(w) == 2) {
|
||||
lastwp = TAILQ_PREV(w->active, window_panes, entry);
|
||||
if (lastwp == NULL)
|
||||
@ -145,12 +149,14 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
markedwp = marked_pane.wp;
|
||||
|
||||
if (lastwp != NULL) {
|
||||
lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
|
||||
lastwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
|
||||
PANE_THEMECHANGED);
|
||||
server_redraw_window_borders(lastwp->window);
|
||||
server_status_window(lastwp->window);
|
||||
}
|
||||
if (markedwp != NULL) {
|
||||
markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
|
||||
markedwp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
|
||||
PANE_THEMECHANGED);
|
||||
server_redraw_window_borders(markedwp->window);
|
||||
server_status_window(markedwp->window);
|
||||
}
|
||||
@ -165,7 +171,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
options_set_string(oo, "window-active-style", 0, "%s", style);
|
||||
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED);
|
||||
wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|PANE_THEMECHANGED);
|
||||
}
|
||||
if (args_has(args, 'g')) {
|
||||
cmdq_print(item, "%s", options_get_string(oo, "window-style"));
|
||||
|
@ -33,13 +33,13 @@ const struct cmd_entry cmd_send_keys_entry = {
|
||||
.name = "send-keys",
|
||||
.alias = "send",
|
||||
|
||||
.args = { "FHlMN:Rt:X", 0, -1, NULL },
|
||||
.usage = "[-FHlMRX] [-N repeat-count] " CMD_TARGET_PANE_USAGE
|
||||
" key ...",
|
||||
.args = { "c:FHKlMN:Rt:X", 0, -1, NULL },
|
||||
.usage = "[-FHKlMRX] [-c target-client] [-N repeat-count] "
|
||||
CMD_TARGET_PANE_USAGE " [key ...]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
.flags = CMD_AFTERHOOK,
|
||||
.flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL,
|
||||
.exec = cmd_send_keys_exec
|
||||
};
|
||||
|
||||
@ -58,7 +58,7 @@ const struct cmd_entry cmd_send_prefix_entry = {
|
||||
|
||||
static struct cmdq_item *
|
||||
cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
|
||||
key_code key)
|
||||
struct args *args, key_code key)
|
||||
{
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct client *tc = cmdq_get_target_client(item);
|
||||
@ -66,8 +66,22 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
|
||||
struct winlink *wl = target->wl;
|
||||
struct window_pane *wp = target->wp;
|
||||
struct window_mode_entry *wme;
|
||||
struct key_table *table;
|
||||
struct key_table *table = NULL;
|
||||
struct key_binding *bd;
|
||||
struct key_event *event;
|
||||
|
||||
if (args_has(args, 'K')) {
|
||||
if (tc == NULL)
|
||||
return (item);
|
||||
event = xcalloc(1, sizeof *event);
|
||||
event->key = key|KEYC_SENT;
|
||||
memset(&event->m, 0, sizeof event->m);
|
||||
if (server_client_handle_key(tc, event) == 0) {
|
||||
free(event->buf);
|
||||
free(event);
|
||||
}
|
||||
return (item);
|
||||
}
|
||||
|
||||
wme = TAILQ_FIRST(&wp->modes);
|
||||
if (wme == NULL || wme->mode->key_table == NULL) {
|
||||
@ -102,14 +116,16 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after,
|
||||
n = strtol(s, &endptr, 16);
|
||||
if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0')
|
||||
return (item);
|
||||
return (cmd_send_keys_inject_key(item, after, KEYC_LITERAL|n));
|
||||
return (cmd_send_keys_inject_key(item, after, args,
|
||||
KEYC_LITERAL|n));
|
||||
}
|
||||
|
||||
literal = args_has(args, 'l');
|
||||
if (!literal) {
|
||||
key = key_string_lookup_string(s);
|
||||
if (key != KEYC_NONE && key != KEYC_UNKNOWN) {
|
||||
after = cmd_send_keys_inject_key(item, after, key);
|
||||
after = cmd_send_keys_inject_key(item, after, args,
|
||||
key);
|
||||
if (after != NULL)
|
||||
return (after);
|
||||
}
|
||||
@ -125,7 +141,8 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after,
|
||||
continue;
|
||||
key = uc;
|
||||
}
|
||||
after = cmd_send_keys_inject_key(item, after, key);
|
||||
after = cmd_send_keys_inject_key(item, after, args,
|
||||
key);
|
||||
}
|
||||
free(ud);
|
||||
}
|
||||
@ -151,7 +168,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
|
||||
char *cause = NULL;
|
||||
|
||||
if (args_has(args, 'N')) {
|
||||
np = args_strtonum(args, 'N', 1, UINT_MAX, &cause);
|
||||
np = args_strtonum_and_expand(args, 'N', 1, UINT_MAX, item,
|
||||
&cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(item, "repeat count %s", cause);
|
||||
free(cause);
|
||||
@ -192,21 +210,21 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
|
||||
key = options_get_number(s->options, "prefix2");
|
||||
else
|
||||
key = options_get_number(s->options, "prefix");
|
||||
cmd_send_keys_inject_key(item, item, key);
|
||||
cmd_send_keys_inject_key(item, item, args, key);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
if (args_has(args, 'R')) {
|
||||
colour_palette_clear(&wp->palette);
|
||||
input_reset(wp->ictx, 1);
|
||||
wp->flags |= (PANE_STYLECHANGED|PANE_REDRAW);
|
||||
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED|PANE_REDRAW);
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
if (args_has(args, 'N') || args_has(args, 'R'))
|
||||
return (CMD_RETURN_NORMAL);
|
||||
for (; np != 0; np--)
|
||||
cmd_send_keys_inject_key(item, NULL, event->key);
|
||||
cmd_send_keys_inject_key(item, NULL, args, event->key);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ const struct cmd_entry cmd_set_buffer_entry = {
|
||||
|
||||
.args = { "ab:t:n:w", 0, 1, NULL },
|
||||
.usage = "[-aw] " CMD_BUFFER_USAGE " [-n new-buffer-name] "
|
||||
CMD_TARGET_CLIENT_USAGE " data",
|
||||
CMD_TARGET_CLIENT_USAGE " [data]",
|
||||
|
||||
.flags = CMD_AFTERHOOK|CMD_CLIENT_TFLAG|CMD_CLIENT_CANFAIL,
|
||||
.exec = cmd_set_buffer_exec
|
||||
@ -69,8 +69,13 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
||||
pb = paste_get_name(bufname);
|
||||
|
||||
if (cmd_get_entry(self) == &cmd_delete_buffer_entry) {
|
||||
if (pb == NULL)
|
||||
if (pb == NULL) {
|
||||
if (bufname != NULL) {
|
||||
cmdq_error(item, "unknown buffer: %s", bufname);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
pb = paste_get_top(&bufname);
|
||||
}
|
||||
if (pb == NULL) {
|
||||
cmdq_error(item, "no buffer");
|
||||
return (CMD_RETURN_ERROR);
|
||||
@ -80,8 +85,13 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
|
||||
if (args_has(args, 'n')) {
|
||||
if (pb == NULL)
|
||||
if (pb == NULL) {
|
||||
if (bufname != NULL) {
|
||||
cmdq_error(item, "unknown buffer: %s", bufname);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
pb = paste_get_top(&bufname);
|
||||
}
|
||||
if (pb == NULL) {
|
||||
cmdq_error(item, "no buffer");
|
||||
return (CMD_RETURN_ERROR);
|
||||
@ -121,7 +131,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (args_has(args, 'w') && tc != NULL)
|
||||
tty_set_selection(&tc->tty, bufdata, bufsize);
|
||||
tty_set_selection(&tc->tty, "", bufdata, bufsize);
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ const struct cmd_entry cmd_set_environment_entry = {
|
||||
.alias = "setenv",
|
||||
|
||||
.args = { "Fhgrt:u", 1, 2, NULL },
|
||||
.usage = "[-Fhgru] " CMD_TARGET_SESSION_USAGE " name [value]",
|
||||
.usage = "[-Fhgru] " CMD_TARGET_SESSION_USAGE " variable [value]",
|
||||
|
||||
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
|
||||
|
||||
|
@ -39,7 +39,7 @@ const struct cmd_entry cmd_show_environment_entry = {
|
||||
.alias = "showenv",
|
||||
|
||||
.args = { "hgst:", 0, 1, NULL },
|
||||
.usage = "[-hgs] " CMD_TARGET_SESSION_USAGE " [name]",
|
||||
.usage = "[-hgs] " CMD_TARGET_SESSION_USAGE " [variable]",
|
||||
|
||||
.target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL },
|
||||
|
||||
|
@ -65,7 +65,7 @@ const struct cmd_entry cmd_show_hooks_entry = {
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "gpt:w", 0, 1, NULL },
|
||||
.usage = "[-gpw] " CMD_TARGET_PANE_USAGE,
|
||||
.usage = "[-gpw] " CMD_TARGET_PANE_USAGE " [hook]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
|
||||
|
||||
|
@ -32,7 +32,7 @@ const struct cmd_entry cmd_show_prompt_history_entry = {
|
||||
.alias = "showphist",
|
||||
|
||||
.args = { "T:", 0, 0, NULL },
|
||||
.usage = "[-T type]",
|
||||
.usage = "[-T prompt-type]",
|
||||
|
||||
.flags = CMD_AFTERHOOK,
|
||||
.exec = cmd_show_prompt_history_exec
|
||||
@ -43,7 +43,7 @@ const struct cmd_entry cmd_clear_prompt_history_entry = {
|
||||
.alias = "clearphist",
|
||||
|
||||
.args = { "T:", 0, 0, NULL },
|
||||
.usage = "[-T type]",
|
||||
.usage = "[-T prompt-type]",
|
||||
|
||||
.flags = CMD_AFTERHOOK,
|
||||
.exec = cmd_show_prompt_history_exec
|
||||
|
@ -29,14 +29,19 @@
|
||||
* Sources a configuration file.
|
||||
*/
|
||||
|
||||
#define CMD_SOURCE_FILE_DEPTH_LIMIT 50
|
||||
static u_int cmd_source_file_depth;
|
||||
|
||||
static enum cmd_retval cmd_source_file_exec(struct cmd *, struct cmdq_item *);
|
||||
|
||||
const struct cmd_entry cmd_source_file_entry = {
|
||||
.name = "source-file",
|
||||
.alias = "source",
|
||||
|
||||
.args = { "Fnqv", 1, -1, NULL },
|
||||
.usage = "[-Fnqv] path ...",
|
||||
.args = { "t:Fnqv", 1, -1, NULL },
|
||||
.usage = "[-Fnqv] " CMD_TARGET_PANE_USAGE " path ...",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
|
||||
|
||||
.flags = 0,
|
||||
.exec = cmd_source_file_exec
|
||||
@ -57,6 +62,16 @@ struct cmd_source_file_data {
|
||||
static enum cmd_retval
|
||||
cmd_source_file_complete_cb(struct cmdq_item *item, __unused void *data)
|
||||
{
|
||||
struct client *c = cmdq_get_client(item);
|
||||
|
||||
if (c == NULL) {
|
||||
cmd_source_file_depth--;
|
||||
log_debug("%s: depth now %u", __func__, cmd_source_file_depth);
|
||||
} else {
|
||||
c->source_file_depth--;
|
||||
log_debug("%s: depth now %u", __func__, c->source_file_depth);
|
||||
}
|
||||
|
||||
cfg_print_causes(item);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
@ -92,6 +107,7 @@ cmd_source_file_done(struct client *c, const char *path, int error,
|
||||
size_t bsize = EVBUFFER_LENGTH(buffer);
|
||||
u_int n;
|
||||
struct cmdq_item *new_item;
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
|
||||
if (!closed)
|
||||
return;
|
||||
@ -100,7 +116,7 @@ cmd_source_file_done(struct client *c, const char *path, int error,
|
||||
cmdq_error(item, "%s: %s", path, strerror(error));
|
||||
else if (bsize != 0) {
|
||||
if (load_cfg_from_buffer(bdata, bsize, path, c, cdata->after,
|
||||
cdata->flags, &new_item) < 0)
|
||||
target, cdata->flags, &new_item) < 0)
|
||||
cdata->retval = CMD_RETURN_ERROR;
|
||||
else if (new_item != NULL)
|
||||
cdata->after = new_item;
|
||||
@ -118,7 +134,16 @@ cmd_source_file_done(struct client *c, const char *path, int error,
|
||||
static void
|
||||
cmd_source_file_add(struct cmd_source_file_data *cdata, const char *path)
|
||||
{
|
||||
char resolved[PATH_MAX];
|
||||
|
||||
if (realpath(path, resolved) == NULL) {
|
||||
log_debug("%s: realpath(\"%s\") failed: %s", __func__,
|
||||
path, strerror(errno));
|
||||
} else
|
||||
path = resolved;
|
||||
|
||||
log_debug("%s: %s", __func__, path);
|
||||
|
||||
cdata->files = xreallocarray(cdata->files, cdata->nfiles + 1,
|
||||
sizeof *cdata->files);
|
||||
cdata->files[cdata->nfiles++] = xstrdup(path);
|
||||
@ -137,6 +162,22 @@ cmd_source_file_exec(struct cmd *self, struct cmdq_item *item)
|
||||
int result;
|
||||
u_int i, j;
|
||||
|
||||
if (c == NULL) {
|
||||
if (cmd_source_file_depth >= CMD_SOURCE_FILE_DEPTH_LIMIT) {
|
||||
cmdq_error(item, "too many nested files");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
cmd_source_file_depth++;
|
||||
log_debug("%s: depth now %u", __func__, cmd_source_file_depth);
|
||||
} else {
|
||||
if (c->source_file_depth >= CMD_SOURCE_FILE_DEPTH_LIMIT) {
|
||||
cmdq_error(item, "too many nested files");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
c->source_file_depth++;
|
||||
log_debug("%s: depth now %u", __func__, c->source_file_depth);
|
||||
}
|
||||
|
||||
cdata = xcalloc(1, sizeof *cdata);
|
||||
cdata->item = item;
|
||||
|
||||
|
@ -42,7 +42,7 @@ const struct cmd_entry cmd_split_window_entry = {
|
||||
.args = { "bc:de:fF:hIl:p:Pt:vZ", 0, -1, NULL },
|
||||
.usage = "[-bdefhIPvZ] [-c start-directory] [-e environment] "
|
||||
"[-F format] [-l size] " CMD_TARGET_PANE_USAGE
|
||||
"[shell-command]",
|
||||
" [shell-command [argument ...]]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
@ -65,67 +65,46 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
enum layout_type type;
|
||||
struct layout_cell *lc;
|
||||
struct cmd_find_state fs;
|
||||
int size, percentage, flags, input;
|
||||
const char *template, *errstr, *p;
|
||||
char *cause, *cp, *copy;
|
||||
size_t plen;
|
||||
int size, flags, input;
|
||||
const char *template;
|
||||
char *cause = NULL, *cp;
|
||||
struct args_value *av;
|
||||
u_int count = args_count(args);
|
||||
u_int count = args_count(args), curval = 0;
|
||||
|
||||
type = LAYOUT_TOPBOTTOM;
|
||||
if (args_has(args, 'h'))
|
||||
type = LAYOUT_LEFTRIGHT;
|
||||
else
|
||||
type = LAYOUT_TOPBOTTOM;
|
||||
if ((p = args_get(args, 'l')) != NULL) {
|
||||
plen = strlen(p);
|
||||
if (p[plen - 1] == '%') {
|
||||
copy = xstrdup(p);
|
||||
copy[plen - 1] = '\0';
|
||||
percentage = strtonum(copy, 0, INT_MAX, &errstr);
|
||||
free(copy);
|
||||
if (errstr != NULL) {
|
||||
cmdq_error(item, "percentage %s", errstr);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (args_has(args, 'f')) {
|
||||
if (type == LAYOUT_TOPBOTTOM)
|
||||
size = (w->sy * percentage) / 100;
|
||||
else
|
||||
size = (w->sx * percentage) / 100;
|
||||
} else {
|
||||
if (type == LAYOUT_TOPBOTTOM)
|
||||
size = (wp->sy * percentage) / 100;
|
||||
else
|
||||
size = (wp->sx * percentage) / 100;
|
||||
}
|
||||
} else {
|
||||
size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(item, "lines %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
} else if (args_has(args, 'p')) {
|
||||
percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(item, "create pane failed: -p %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
/* If the 'p' flag is dropped then this bit can be moved into 'l'. */
|
||||
if (args_has(args, 'l') || args_has(args, 'p')) {
|
||||
if (args_has(args, 'f')) {
|
||||
if (type == LAYOUT_TOPBOTTOM)
|
||||
size = (w->sy * percentage) / 100;
|
||||
curval = w->sy;
|
||||
else
|
||||
size = (w->sx * percentage) / 100;
|
||||
curval = w->sx;
|
||||
} else {
|
||||
if (type == LAYOUT_TOPBOTTOM)
|
||||
size = (wp->sy * percentage) / 100;
|
||||
curval = wp->sy;
|
||||
else
|
||||
size = (wp->sx * percentage) / 100;
|
||||
curval = wp->sx;
|
||||
}
|
||||
} else
|
||||
size = -1;
|
||||
}
|
||||
|
||||
size = -1;
|
||||
if (args_has(args, 'l')) {
|
||||
size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
|
||||
item, &cause);
|
||||
} else if (args_has(args, 'p')) {
|
||||
size = args_strtonum_and_expand(args, 'p', 0, 100, item,
|
||||
&cause);
|
||||
if (cause == NULL)
|
||||
size = curval * size / 100;
|
||||
}
|
||||
if (cause != NULL) {
|
||||
cmdq_error(item, "size %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
window_push_zoom(wp->window, 1, args_has(args, 'Z'));
|
||||
input = (args_has(args, 'I') && count == 0);
|
||||
|
@ -101,10 +101,10 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
|
||||
src_wp->window = dst_w;
|
||||
options_set_parent(src_wp->options, dst_w->options);
|
||||
src_wp->flags |= PANE_STYLECHANGED;
|
||||
src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
|
||||
dst_wp->window = src_w;
|
||||
options_set_parent(dst_wp->options, src_w->options);
|
||||
dst_wp->flags |= PANE_STYLECHANGED;
|
||||
dst_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
|
||||
|
||||
sx = src_wp->sx; sy = src_wp->sy;
|
||||
xoff = src_wp->xoff; yoff = src_wp->yoff;
|
||||
@ -128,13 +128,16 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
window_set_active_pane(dst_w, src_wp, 1);
|
||||
}
|
||||
if (src_w != dst_w) {
|
||||
if (src_w->last == src_wp)
|
||||
src_w->last = NULL;
|
||||
if (dst_w->last == dst_wp)
|
||||
dst_w->last = NULL;
|
||||
window_pane_stack_remove(&src_w->last_panes, src_wp);
|
||||
window_pane_stack_remove(&dst_w->last_panes, dst_wp);
|
||||
colour_palette_from_option(&src_wp->palette, src_wp->options);
|
||||
colour_palette_from_option(&dst_wp->palette, dst_wp->options);
|
||||
layout_fix_panes(src_w, NULL);
|
||||
server_redraw_window(src_w);
|
||||
}
|
||||
server_redraw_window(src_w);
|
||||
layout_fix_panes(dst_w, NULL);
|
||||
server_redraw_window(dst_w);
|
||||
|
||||
notify_window("window-layout-changed", src_w);
|
||||
if (src_w != dst_w)
|
||||
notify_window("window-layout-changed", dst_w);
|
||||
|
20
cmd.c
20
cmd.c
@ -47,7 +47,6 @@ extern const struct cmd_entry cmd_display_menu_entry;
|
||||
extern const struct cmd_entry cmd_display_message_entry;
|
||||
extern const struct cmd_entry cmd_display_popup_entry;
|
||||
extern const struct cmd_entry cmd_display_panes_entry;
|
||||
extern const struct cmd_entry cmd_down_pane_entry;
|
||||
extern const struct cmd_entry cmd_find_window_entry;
|
||||
extern const struct cmd_entry cmd_has_session_entry;
|
||||
extern const struct cmd_entry cmd_if_shell_entry;
|
||||
@ -117,7 +116,6 @@ extern const struct cmd_entry cmd_swap_window_entry;
|
||||
extern const struct cmd_entry cmd_switch_client_entry;
|
||||
extern const struct cmd_entry cmd_unbind_key_entry;
|
||||
extern const struct cmd_entry cmd_unlink_window_entry;
|
||||
extern const struct cmd_entry cmd_up_pane_entry;
|
||||
extern const struct cmd_entry cmd_wait_for_entry;
|
||||
|
||||
const struct cmd_entry *cmd_table[] = {
|
||||
@ -446,7 +444,7 @@ cmd_get_alias(const char *name)
|
||||
}
|
||||
|
||||
/* Look up a command entry by name. */
|
||||
static const struct cmd_entry *
|
||||
const struct cmd_entry *
|
||||
cmd_find(const char *name, char **cause)
|
||||
{
|
||||
const struct cmd_entry **loop, *entry, *found = NULL;
|
||||
@ -638,7 +636,7 @@ cmd_list_free(struct cmd_list *cmdlist)
|
||||
|
||||
/* Copy a command list, expanding %s in arguments. */
|
||||
struct cmd_list *
|
||||
cmd_list_copy(struct cmd_list *cmdlist, int argc, char **argv)
|
||||
cmd_list_copy(const struct cmd_list *cmdlist, int argc, char **argv)
|
||||
{
|
||||
struct cmd *cmd;
|
||||
struct cmd_list *new_cmdlist;
|
||||
@ -669,7 +667,7 @@ cmd_list_copy(struct cmd_list *cmdlist, int argc, char **argv)
|
||||
|
||||
/* Get a command list as a string. */
|
||||
char *
|
||||
cmd_list_print(struct cmd_list *cmdlist, int escaped)
|
||||
cmd_list_print(const struct cmd_list *cmdlist, int escaped)
|
||||
{
|
||||
struct cmd *cmd, *next;
|
||||
char *buf, *this;
|
||||
@ -812,10 +810,14 @@ cmd_mouse_pane(struct mouse_event *m, struct session **sp,
|
||||
|
||||
if ((wl = cmd_mouse_window(m, sp)) == NULL)
|
||||
return (NULL);
|
||||
if ((wp = window_pane_find_by_id(m->wp)) == NULL)
|
||||
return (NULL);
|
||||
if (!window_has_pane(wl->window, wp))
|
||||
return (NULL);
|
||||
if (m->wp == -1)
|
||||
wp = wl->window->active;
|
||||
else {
|
||||
if ((wp = window_pane_find_by_id(m->wp)) == NULL)
|
||||
return (NULL);
|
||||
if (!window_has_pane(wl->window, wp))
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (wlp != NULL)
|
||||
*wlp = wl;
|
||||
|
94
colour.c
94
colour.c
@ -182,6 +182,46 @@ colour_tostring(int c)
|
||||
return ("invalid");
|
||||
}
|
||||
|
||||
/* Convert background colour to theme. */
|
||||
enum client_theme
|
||||
colour_totheme(int c)
|
||||
{
|
||||
int r, g, b, brightness;
|
||||
|
||||
if (c == -1)
|
||||
return (THEME_UNKNOWN);
|
||||
|
||||
if (c & COLOUR_FLAG_RGB) {
|
||||
r = (c >> 16) & 0xff;
|
||||
g = (c >> 8) & 0xff;
|
||||
b = (c >> 0) & 0xff;
|
||||
|
||||
brightness = r + g + b;
|
||||
if (brightness > 382)
|
||||
return (THEME_LIGHT);
|
||||
return (THEME_DARK);
|
||||
}
|
||||
|
||||
if (c & COLOUR_FLAG_256)
|
||||
return (colour_totheme(colour_256toRGB(c)));
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
case 90:
|
||||
return (THEME_DARK);
|
||||
case 7:
|
||||
case 97:
|
||||
return (THEME_LIGHT);
|
||||
default:
|
||||
if (c >= 0 && c <= 7)
|
||||
return (colour_totheme(colour_256toRGB(c)));
|
||||
if (c >= 90 && c <= 97)
|
||||
return (colour_totheme(colour_256toRGB(8 + c - 90)));
|
||||
break;
|
||||
}
|
||||
return (THEME_UNKNOWN);
|
||||
}
|
||||
|
||||
/* Convert colour from string. */
|
||||
int
|
||||
colour_fromstring(const char *s)
|
||||
@ -942,13 +982,17 @@ colour_byname(const char *name)
|
||||
{ "yellow3", 0xcdcd00 },
|
||||
{ "yellow4", 0x8b8b00 }
|
||||
};
|
||||
u_int i;
|
||||
int c;
|
||||
u_int i;
|
||||
int c;
|
||||
const char *errstr;
|
||||
|
||||
if (strncmp(name, "grey", 4) == 0 || strncmp(name, "gray", 4) == 0) {
|
||||
if (!isdigit((u_char)name[4]))
|
||||
if (name[4] == '\0')
|
||||
return (0xbebebe|COLOUR_FLAG_RGB);
|
||||
c = round(2.55 * atoi(name + 4));
|
||||
c = strtonum(name + 4, 0, 100, &errstr);
|
||||
if (errstr != NULL)
|
||||
return (-1);
|
||||
c = round(2.55 * c);
|
||||
if (c < 0 || c > 255)
|
||||
return (-1);
|
||||
return (colour_join_rgb(c, c, c));
|
||||
@ -960,6 +1004,47 @@ colour_byname(const char *name)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Parse colour from an X11 string. */
|
||||
int
|
||||
colour_parseX11(const char *p)
|
||||
{
|
||||
double c, m, y, k = 0;
|
||||
u_int r, g, b;
|
||||
size_t len = strlen(p);
|
||||
int colour = -1;
|
||||
char *copy;
|
||||
|
||||
if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) ||
|
||||
(len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) ||
|
||||
sscanf(p, "%d,%d,%d", &r, &g, &b) == 3)
|
||||
colour = colour_join_rgb(r, g, b);
|
||||
else if ((len == 18 &&
|
||||
sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) ||
|
||||
(len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3))
|
||||
colour = colour_join_rgb(r >> 8, g >> 8, b >> 8);
|
||||
else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 ||
|
||||
sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) &&
|
||||
c >= 0 && c <= 1 && m >= 0 && m <= 1 &&
|
||||
y >= 0 && y <= 1 && k >= 0 && k <= 1) {
|
||||
colour = colour_join_rgb(
|
||||
(1 - c) * (1 - k) * 255,
|
||||
(1 - m) * (1 - k) * 255,
|
||||
(1 - y) * (1 - k) * 255);
|
||||
} else {
|
||||
while (len != 0 && *p == ' ') {
|
||||
p++;
|
||||
len--;
|
||||
}
|
||||
while (len != 0 && p[len - 1] == ' ')
|
||||
len--;
|
||||
copy = xstrndup(p, len);
|
||||
colour = colour_byname(copy);
|
||||
free(copy);
|
||||
}
|
||||
log_debug("%s: %s = %s", __func__, p, colour_tostring(colour));
|
||||
return (colour);
|
||||
}
|
||||
|
||||
/* Initialize palette. */
|
||||
void
|
||||
colour_palette_init(struct colour_palette *p)
|
||||
@ -1069,5 +1154,4 @@ colour_palette_from_option(struct colour_palette *p, struct options *oo)
|
||||
}
|
||||
a = options_array_next(a);
|
||||
}
|
||||
|
||||
}
|
||||
|
37
compat.h
37
compat.h
@ -38,6 +38,14 @@
|
||||
#include <event2/bufferevent_compat.h>
|
||||
#else
|
||||
#include <event.h>
|
||||
#ifndef EVBUFFER_EOL_LF
|
||||
/*
|
||||
* This doesn't really work because evbuffer_readline is broken, but gets us to
|
||||
* build with very old (older than 1.4.14) libevent.
|
||||
*/
|
||||
#define EVBUFFER_EOL_LF
|
||||
#define evbuffer_readln(a, b, c) evbuffer_readline(a)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MALLOC_TRIM
|
||||
@ -289,6 +297,11 @@ void explicit_bzero(void *, size_t);
|
||||
int getdtablecount(void);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_GETDTABLESIZE
|
||||
/* getdtablesize.c */
|
||||
int getdtablesize(void);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_CLOSEFROM
|
||||
/* closefrom.c */
|
||||
void closefrom(int);
|
||||
@ -334,6 +347,18 @@ char *strndup(const char *, size_t);
|
||||
void *memmem(const void *, size_t, const void *, size_t);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_HTONLL
|
||||
/* htonll.c */
|
||||
#undef htonll
|
||||
uint64_t htonll(uint64_t);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_NTOHLL
|
||||
/* ntohll.c */
|
||||
#undef ntohll
|
||||
uint64_t ntohll(uint64_t);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_GETPEEREID
|
||||
/* getpeereid.c */
|
||||
int getpeereid(int, uid_t *, gid_t *);
|
||||
@ -423,7 +448,9 @@ void *recallocarray(void *, size_t, size_t, size_t);
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
/* systemd.c */
|
||||
int systemd_activated(void);
|
||||
int systemd_create_socket(int, char **);
|
||||
int systemd_move_to_new_cgroup(char **);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UTF8PROC
|
||||
@ -439,11 +466,11 @@ int utf8proc_wctomb(char *, wchar_t);
|
||||
#endif
|
||||
|
||||
/* getopt.c */
|
||||
extern int BSDopterr;
|
||||
extern int BSDoptind;
|
||||
extern int BSDoptopt;
|
||||
extern int BSDoptreset;
|
||||
extern char *BSDoptarg;
|
||||
extern int BSDopterr;
|
||||
extern int BSDoptind;
|
||||
extern int BSDoptopt;
|
||||
extern int BSDoptreset;
|
||||
extern char *BSDoptarg;
|
||||
int BSDgetopt(int, char *const *, const char *);
|
||||
#define getopt(ac, av, o) BSDgetopt(ac, av, o)
|
||||
#define opterr BSDopterr
|
||||
|
@ -27,7 +27,7 @@
|
||||
#endif
|
||||
|
||||
int
|
||||
clock_gettime(int clock, struct timespec *ts)
|
||||
clock_gettime(__unused int clock, struct timespec *ts)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
|
115
compat/getopt.c
115
compat/getopt.c
@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1987, 1993, 1994
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* OPENBSD ORIGINAL: lib/libc/stdlib/getopt.c */
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int BSDopterr = 1, /* if error message should be printed */
|
||||
BSDoptind = 1, /* index into parent argv vector */
|
||||
BSDoptopt, /* character checked for validity */
|
||||
BSDoptreset; /* reset getopt */
|
||||
char *BSDoptarg; /* argument associated with option */
|
||||
|
||||
#define BADCH (int)'?'
|
||||
#define BADARG (int)':'
|
||||
#define EMSG ""
|
||||
|
||||
/*
|
||||
* getopt --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
BSDgetopt(int nargc, char *const *nargv, const char *ostr)
|
||||
{
|
||||
static const char *place = EMSG; /* option letter processing */
|
||||
char *oli; /* option letter list index */
|
||||
|
||||
if (ostr == NULL)
|
||||
return (-1);
|
||||
|
||||
if (BSDoptreset || !*place) { /* update scanning pointer */
|
||||
BSDoptreset = 0;
|
||||
if (BSDoptind >= nargc || *(place = nargv[BSDoptind]) != '-') {
|
||||
place = EMSG;
|
||||
return (-1);
|
||||
}
|
||||
if (place[1] && *++place == '-') { /* found "--" */
|
||||
if (place[1])
|
||||
return (BADCH);
|
||||
++BSDoptind;
|
||||
place = EMSG;
|
||||
return (-1);
|
||||
}
|
||||
} /* option letter okay? */
|
||||
if ((BSDoptopt = (int)*place++) == (int)':' ||
|
||||
!(oli = strchr(ostr, BSDoptopt))) {
|
||||
/*
|
||||
* if the user didn't specify '-' as an option,
|
||||
* assume it means -1.
|
||||
*/
|
||||
if (BSDoptopt == (int)'-')
|
||||
return (-1);
|
||||
if (!*place)
|
||||
++BSDoptind;
|
||||
if (BSDopterr && *ostr != ':')
|
||||
(void)fprintf(stderr,
|
||||
"%s: unknown option -- %c\n", getprogname(),
|
||||
BSDoptopt);
|
||||
return (BADCH);
|
||||
}
|
||||
if (*++oli != ':') { /* don't need argument */
|
||||
BSDoptarg = NULL;
|
||||
if (!*place)
|
||||
++BSDoptind;
|
||||
}
|
||||
else { /* need an argument */
|
||||
if (*place) /* no white space */
|
||||
BSDoptarg = (char *)place;
|
||||
else if (nargc <= ++BSDoptind) { /* no arg */
|
||||
place = EMSG;
|
||||
if (*ostr == ':')
|
||||
return (BADARG);
|
||||
if (BSDopterr)
|
||||
(void)fprintf(stderr,
|
||||
"%s: option requires an argument -- %c\n",
|
||||
getprogname(), BSDoptopt);
|
||||
return (BADCH);
|
||||
}
|
||||
else /* white space */
|
||||
BSDoptarg = nargv[BSDoptind];
|
||||
place = EMSG;
|
||||
++BSDoptind;
|
||||
}
|
||||
return (BSDoptopt); /* dump back option letter */
|
||||
}
|
577
compat/getopt_long.c
Normal file
577
compat/getopt_long.c
Normal file
@ -0,0 +1,577 @@
|
||||
/* This file is obtained from OpenSSH:
|
||||
* Repository: https://github.com/openssh/openssh-portable
|
||||
* Commit: b5b405fee7f3e79d44e2d2971a4b6b4cc53f112e
|
||||
* File: /openbsd-compat/getopt_long.c */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* Sponsored in part by the Defense Advanced Research Projects
|
||||
* Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||
*/
|
||||
/*-
|
||||
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Dieter Baron and Thomas Klausner.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* OPENBSD ORIGINAL: lib/libc/stdlib/getopt_long.c */
|
||||
#include "compat.h"
|
||||
|
||||
/* The following macro constants are taken from getopt.h of OpenSSH:
|
||||
* Repository: https://github.com/openssh/openssh-portable
|
||||
* Commit: b5b405fee7f3e79d44e2d2971a4b6b4cc53f112e
|
||||
* File: /openbsd-compat/getopt.h
|
||||
*
|
||||
* ---- BEGIN - Copyright notice and license of getopt.h ----
|
||||
* Copyright (c) 2000 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Dieter Baron and Thomas Klausner.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
* ---- END ----
|
||||
*/
|
||||
#define no_argument 0
|
||||
#define required_argument 1
|
||||
#define optional_argument 2
|
||||
|
||||
#if !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_OPTRESET)
|
||||
|
||||
#if 0
|
||||
#include <err.h>
|
||||
#include <getopt.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
struct option {
|
||||
/* name of long option */
|
||||
const char *name;
|
||||
/*
|
||||
* one of no_argument, required_argument, and optional_argument:
|
||||
* whether option takes an argument
|
||||
*/
|
||||
int has_arg;
|
||||
/* if not NULL, set *flag to val when option found */
|
||||
int *flag;
|
||||
/* if flag not NULL, value to set *flag to; else return value */
|
||||
int val;
|
||||
};
|
||||
|
||||
int opterr = 1; /* if error message should be printed */
|
||||
int optind = 1; /* index into parent argv vector */
|
||||
int optopt = '?'; /* character checked for validity */
|
||||
int optreset; /* reset getopt */
|
||||
char *optarg; /* argument associated with option */
|
||||
|
||||
#define PRINT_ERROR ((opterr) && (*options != ':'))
|
||||
|
||||
#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */
|
||||
#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */
|
||||
#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */
|
||||
|
||||
/* return values */
|
||||
#define BADCH (int)'?'
|
||||
#define BADARG ((*options == ':') ? (int)':' : (int)'?')
|
||||
#define INORDER (int)1
|
||||
|
||||
#define EMSG (char *)""
|
||||
|
||||
static int getopt_internal(int, char * const *, const char *,
|
||||
const struct option *, int *, int);
|
||||
static int parse_long_options(char * const *, const char *,
|
||||
const struct option *, int *, int);
|
||||
static int gcd(int, int);
|
||||
static void permute_args(int, int, int, char * const *);
|
||||
|
||||
static char *place = EMSG; /* option letter processing */
|
||||
|
||||
/* XXX: set optreset to 1 rather than these two */
|
||||
static int nonopt_start = -1; /* first non option argument (for permute) */
|
||||
static int nonopt_end = -1; /* first option after non options (for permute) */
|
||||
|
||||
/* Error messages */
|
||||
static const char recargchar[] = "option requires an argument -- %c";
|
||||
static const char recargstring[] = "option requires an argument -- %s";
|
||||
static const char ambig[] = "ambiguous option -- %.*s";
|
||||
static const char noarg[] = "option doesn't take an argument -- %.*s";
|
||||
static const char illoptchar[] = "unknown option -- %c";
|
||||
static const char illoptstring[] = "unknown option -- %s";
|
||||
|
||||
/*
|
||||
* Compute the greatest common divisor of a and b.
|
||||
*/
|
||||
static int
|
||||
gcd(int a, int b)
|
||||
{
|
||||
int c;
|
||||
|
||||
c = a % b;
|
||||
while (c != 0) {
|
||||
a = b;
|
||||
b = c;
|
||||
c = a % b;
|
||||
}
|
||||
|
||||
return (b);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exchange the block from nonopt_start to nonopt_end with the block
|
||||
* from nonopt_end to opt_end (keeping the same order of arguments
|
||||
* in each block).
|
||||
*/
|
||||
static void
|
||||
permute_args(int panonopt_start, int panonopt_end, int opt_end,
|
||||
char * const *nargv)
|
||||
{
|
||||
int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
|
||||
char *swap;
|
||||
|
||||
/*
|
||||
* compute lengths of blocks and number and size of cycles
|
||||
*/
|
||||
nnonopts = panonopt_end - panonopt_start;
|
||||
nopts = opt_end - panonopt_end;
|
||||
ncycle = gcd(nnonopts, nopts);
|
||||
cyclelen = (opt_end - panonopt_start) / ncycle;
|
||||
|
||||
for (i = 0; i < ncycle; i++) {
|
||||
cstart = panonopt_end+i;
|
||||
pos = cstart;
|
||||
for (j = 0; j < cyclelen; j++) {
|
||||
if (pos >= panonopt_end)
|
||||
pos -= nnonopts;
|
||||
else
|
||||
pos += nopts;
|
||||
swap = nargv[pos];
|
||||
/* LINTED const cast */
|
||||
((char **) nargv)[pos] = nargv[cstart];
|
||||
/* LINTED const cast */
|
||||
((char **)nargv)[cstart] = swap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* parse_long_options --
|
||||
* Parse long options in argc/argv argument vector.
|
||||
* Returns -1 if short_too is set and the option does not match long_options.
|
||||
*/
|
||||
static int
|
||||
parse_long_options(char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx, int short_too)
|
||||
{
|
||||
char *current_argv, *has_equal;
|
||||
size_t current_argv_len;
|
||||
int i, match;
|
||||
|
||||
current_argv = place;
|
||||
match = -1;
|
||||
|
||||
optind++;
|
||||
|
||||
if ((has_equal = strchr(current_argv, '=')) != NULL) {
|
||||
/* argument found (--option=arg) */
|
||||
current_argv_len = has_equal - current_argv;
|
||||
has_equal++;
|
||||
} else
|
||||
current_argv_len = strlen(current_argv);
|
||||
|
||||
for (i = 0; long_options[i].name; i++) {
|
||||
/* find matching long option */
|
||||
if (strncmp(current_argv, long_options[i].name,
|
||||
current_argv_len))
|
||||
continue;
|
||||
|
||||
if (strlen(long_options[i].name) == current_argv_len) {
|
||||
/* exact match */
|
||||
match = i;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* If this is a known short option, don't allow
|
||||
* a partial match of a single character.
|
||||
*/
|
||||
if (short_too && current_argv_len == 1)
|
||||
continue;
|
||||
|
||||
if (match == -1) /* partial match */
|
||||
match = i;
|
||||
else {
|
||||
/* ambiguous abbreviation */
|
||||
if (PRINT_ERROR)
|
||||
warnx(ambig, (int)current_argv_len,
|
||||
current_argv);
|
||||
optopt = 0;
|
||||
return (BADCH);
|
||||
}
|
||||
}
|
||||
if (match != -1) { /* option found */
|
||||
if (long_options[match].has_arg == no_argument
|
||||
&& has_equal) {
|
||||
if (PRINT_ERROR)
|
||||
warnx(noarg, (int)current_argv_len,
|
||||
current_argv);
|
||||
/*
|
||||
* XXX: GNU sets optopt to val regardless of flag
|
||||
*/
|
||||
if (long_options[match].flag == NULL)
|
||||
optopt = long_options[match].val;
|
||||
else
|
||||
optopt = 0;
|
||||
return (BADARG);
|
||||
}
|
||||
if (long_options[match].has_arg == required_argument ||
|
||||
long_options[match].has_arg == optional_argument) {
|
||||
if (has_equal)
|
||||
optarg = has_equal;
|
||||
else if (long_options[match].has_arg ==
|
||||
required_argument) {
|
||||
/*
|
||||
* optional argument doesn't use next nargv
|
||||
*/
|
||||
optarg = nargv[optind++];
|
||||
}
|
||||
}
|
||||
if ((long_options[match].has_arg == required_argument)
|
||||
&& (optarg == NULL)) {
|
||||
/*
|
||||
* Missing argument; leading ':' indicates no error
|
||||
* should be generated.
|
||||
*/
|
||||
if (PRINT_ERROR)
|
||||
warnx(recargstring,
|
||||
current_argv);
|
||||
/*
|
||||
* XXX: GNU sets optopt to val regardless of flag
|
||||
*/
|
||||
if (long_options[match].flag == NULL)
|
||||
optopt = long_options[match].val;
|
||||
else
|
||||
optopt = 0;
|
||||
--optind;
|
||||
return (BADARG);
|
||||
}
|
||||
} else { /* unknown option */
|
||||
if (short_too) {
|
||||
--optind;
|
||||
return (-1);
|
||||
}
|
||||
if (PRINT_ERROR)
|
||||
warnx(illoptstring, current_argv);
|
||||
optopt = 0;
|
||||
return (BADCH);
|
||||
}
|
||||
if (idx)
|
||||
*idx = match;
|
||||
if (long_options[match].flag) {
|
||||
*long_options[match].flag = long_options[match].val;
|
||||
return (0);
|
||||
} else
|
||||
return (long_options[match].val);
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_internal --
|
||||
* Parse argc/argv argument vector. Called by user level routines.
|
||||
*/
|
||||
static int
|
||||
getopt_internal(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx, int flags)
|
||||
{
|
||||
char *oli; /* option letter list index */
|
||||
int optchar, short_too;
|
||||
static int posixly_correct = -1;
|
||||
|
||||
if (options == NULL)
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
* XXX Some GNU programs (like cvs) set optind to 0 instead of
|
||||
* XXX using optreset. Work around this braindamage.
|
||||
*/
|
||||
if (optind == 0)
|
||||
optind = optreset = 1;
|
||||
|
||||
/*
|
||||
* Disable GNU extensions if POSIXLY_CORRECT is set or options
|
||||
* string begins with a '+'.
|
||||
*/
|
||||
if (posixly_correct == -1 || optreset)
|
||||
posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
|
||||
if (*options == '-')
|
||||
flags |= FLAG_ALLARGS;
|
||||
else if (posixly_correct || *options == '+')
|
||||
flags &= ~FLAG_PERMUTE;
|
||||
if (*options == '+' || *options == '-')
|
||||
options++;
|
||||
|
||||
optarg = NULL;
|
||||
if (optreset)
|
||||
nonopt_start = nonopt_end = -1;
|
||||
start:
|
||||
if (optreset || !*place) { /* update scanning pointer */
|
||||
optreset = 0;
|
||||
if (optind >= nargc) { /* end of argument vector */
|
||||
place = EMSG;
|
||||
if (nonopt_end != -1) {
|
||||
/* do permutation, if we have to */
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
optind -= nonopt_end - nonopt_start;
|
||||
}
|
||||
else if (nonopt_start != -1) {
|
||||
/*
|
||||
* If we skipped non-options, set optind
|
||||
* to the first of them.
|
||||
*/
|
||||
optind = nonopt_start;
|
||||
}
|
||||
nonopt_start = nonopt_end = -1;
|
||||
return (-1);
|
||||
}
|
||||
if (*(place = nargv[optind]) != '-' ||
|
||||
(place[1] == '\0' && strchr(options, '-') == NULL)) {
|
||||
place = EMSG; /* found non-option */
|
||||
if (flags & FLAG_ALLARGS) {
|
||||
/*
|
||||
* GNU extension:
|
||||
* return non-option as argument to option 1
|
||||
*/
|
||||
optarg = nargv[optind++];
|
||||
return (INORDER);
|
||||
}
|
||||
if (!(flags & FLAG_PERMUTE)) {
|
||||
/*
|
||||
* If no permutation wanted, stop parsing
|
||||
* at first non-option.
|
||||
*/
|
||||
return (-1);
|
||||
}
|
||||
/* do permutation */
|
||||
if (nonopt_start == -1)
|
||||
nonopt_start = optind;
|
||||
else if (nonopt_end != -1) {
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
nonopt_start = optind -
|
||||
(nonopt_end - nonopt_start);
|
||||
nonopt_end = -1;
|
||||
}
|
||||
optind++;
|
||||
/* process next argument */
|
||||
goto start;
|
||||
}
|
||||
if (nonopt_start != -1 && nonopt_end == -1)
|
||||
nonopt_end = optind;
|
||||
|
||||
/*
|
||||
* If we have "-" do nothing, if "--" we are done.
|
||||
*/
|
||||
if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
|
||||
optind++;
|
||||
place = EMSG;
|
||||
/*
|
||||
* We found an option (--), so if we skipped
|
||||
* non-options, we have to permute.
|
||||
*/
|
||||
if (nonopt_end != -1) {
|
||||
permute_args(nonopt_start, nonopt_end,
|
||||
optind, nargv);
|
||||
optind -= nonopt_end - nonopt_start;
|
||||
}
|
||||
nonopt_start = nonopt_end = -1;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check long options if:
|
||||
* 1) we were passed some
|
||||
* 2) the arg is not just "-"
|
||||
* 3) either the arg starts with -- we are getopt_long_only()
|
||||
*/
|
||||
if (long_options != NULL && place != nargv[optind] &&
|
||||
(*place == '-' || (flags & FLAG_LONGONLY))) {
|
||||
short_too = 0;
|
||||
if (*place == '-')
|
||||
place++; /* --foo long option */
|
||||
else if (*place != ':' && strchr(options, *place) != NULL)
|
||||
short_too = 1; /* could be short option too */
|
||||
|
||||
optchar = parse_long_options(nargv, options, long_options,
|
||||
idx, short_too);
|
||||
if (optchar != -1) {
|
||||
place = EMSG;
|
||||
return (optchar);
|
||||
}
|
||||
}
|
||||
|
||||
if ((optchar = (int)*place++) == (int)':' ||
|
||||
(optchar == (int)'-' && *place != '\0') ||
|
||||
(oli = strchr(options, optchar)) == NULL) {
|
||||
/*
|
||||
* If the user specified "-" and '-' isn't listed in
|
||||
* options, return -1 (non-option) as per POSIX.
|
||||
* Otherwise, it is an unknown option character (or ':').
|
||||
*/
|
||||
if (optchar == (int)'-' && *place == '\0')
|
||||
return (-1);
|
||||
if (!*place)
|
||||
++optind;
|
||||
if (PRINT_ERROR)
|
||||
warnx(illoptchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADCH);
|
||||
}
|
||||
if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
|
||||
/* -W long-option */
|
||||
if (*place) /* no space */
|
||||
/* NOTHING */;
|
||||
else if (++optind >= nargc) { /* no arg */
|
||||
place = EMSG;
|
||||
if (PRINT_ERROR)
|
||||
warnx(recargchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADARG);
|
||||
} else /* white space */
|
||||
place = nargv[optind];
|
||||
optchar = parse_long_options(nargv, options, long_options,
|
||||
idx, 0);
|
||||
place = EMSG;
|
||||
return (optchar);
|
||||
}
|
||||
if (*++oli != ':') { /* doesn't take argument */
|
||||
if (!*place)
|
||||
++optind;
|
||||
} else { /* takes (optional) argument */
|
||||
optarg = NULL;
|
||||
if (*place) /* no white space */
|
||||
optarg = place;
|
||||
else if (oli[1] != ':') { /* arg not optional */
|
||||
if (++optind >= nargc) { /* no arg */
|
||||
place = EMSG;
|
||||
if (PRINT_ERROR)
|
||||
warnx(recargchar, optchar);
|
||||
optopt = optchar;
|
||||
return (BADARG);
|
||||
} else
|
||||
optarg = nargv[optind];
|
||||
}
|
||||
place = EMSG;
|
||||
++optind;
|
||||
}
|
||||
/* dump back option letter */
|
||||
return (optchar);
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt --
|
||||
* Parse argc/argv argument vector.
|
||||
*
|
||||
* [eventually this will replace the BSD getopt]
|
||||
*/
|
||||
int
|
||||
getopt(int nargc, char * const *nargv, const char *options)
|
||||
{
|
||||
|
||||
/*
|
||||
* We don't pass FLAG_PERMUTE to getopt_internal() since
|
||||
* the BSD getopt(3) (unlike GNU) has never done this.
|
||||
*
|
||||
* Furthermore, since many privileged programs call getopt()
|
||||
* before dropping privileges it makes sense to keep things
|
||||
* as simple (and bug-free) as possible.
|
||||
*/
|
||||
return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* getopt_long --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt_long(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx)
|
||||
{
|
||||
|
||||
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||
FLAG_PERMUTE));
|
||||
}
|
||||
|
||||
/*
|
||||
* getopt_long_only --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt_long_only(int nargc, char * const *nargv, const char *options,
|
||||
const struct option *long_options, int *idx)
|
||||
{
|
||||
|
||||
return (getopt_internal(nargc, nargv, options, long_options, idx,
|
||||
FLAG_PERMUTE|FLAG_LONGONLY));
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !defined(HAVE_GETOPT) || !defined(HAVE_OPTRESET) */
|
@ -18,6 +18,7 @@
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef HAVE_UCRED_H
|
||||
#include <ucred.h>
|
||||
@ -49,6 +50,8 @@ getpeereid(int s, uid_t *uid, gid_t *gid)
|
||||
ucred_free(ucred);
|
||||
return (0);
|
||||
#else
|
||||
return (getuid());
|
||||
*uid = geteuid();
|
||||
*gid = getegid();
|
||||
return (0);
|
||||
#endif
|
||||
}
|
||||
|
31
compat/htonll.c
Normal file
31
compat/htonll.c
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
uint64_t
|
||||
htonll(uint64_t v)
|
||||
{
|
||||
uint32_t b;
|
||||
uint32_t t;
|
||||
|
||||
b = htonl (v & 0xffffffff);
|
||||
t = htonl (v >> 32);
|
||||
return ((uint64_t)b << 32 | t);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
456
compat/imsg.c
456
compat/imsg.c
@ -1,6 +1,7 @@
|
||||
/* $OpenBSD: imsg.c,v 1.16 2017/12/14 09:27:44 kettenis Exp $ */
|
||||
/* $OpenBSD: imsg.c,v 1.37 2024/11/26 13:57:31 claudio Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
@ -21,6 +22,7 @@
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
@ -28,203 +30,288 @@
|
||||
#include "compat.h"
|
||||
#include "imsg.h"
|
||||
|
||||
int imsg_fd_overhead = 0;
|
||||
#define IMSG_ALLOW_FDPASS 0x01
|
||||
#define IMSG_FD_MARK 0x80000000U
|
||||
|
||||
static int imsg_get_fd(struct imsgbuf *);
|
||||
static struct ibuf *imsg_parse_hdr(struct ibuf *, void *, int *);
|
||||
|
||||
int
|
||||
imsgbuf_init(struct imsgbuf *imsgbuf, int fd)
|
||||
{
|
||||
imsgbuf->w = msgbuf_new_reader(IMSG_HEADER_SIZE, imsg_parse_hdr,
|
||||
imsgbuf);
|
||||
if (imsgbuf->w == NULL)
|
||||
return (-1);
|
||||
imsgbuf->pid = getpid();
|
||||
imsgbuf->maxsize = MAX_IMSGSIZE;
|
||||
imsgbuf->fd = fd;
|
||||
imsgbuf->flags = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
imsg_init(struct imsgbuf *ibuf, int fd)
|
||||
imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf)
|
||||
{
|
||||
msgbuf_init(&ibuf->w);
|
||||
memset(&ibuf->r, 0, sizeof(ibuf->r));
|
||||
ibuf->fd = fd;
|
||||
ibuf->w.fd = fd;
|
||||
ibuf->pid = getpid();
|
||||
TAILQ_INIT(&ibuf->fds);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
imsg_read(struct imsgbuf *ibuf)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct cmsghdr *cmsg;
|
||||
union {
|
||||
struct cmsghdr hdr;
|
||||
char buf[CMSG_SPACE(sizeof(int) * 1)];
|
||||
} cmsgbuf;
|
||||
struct iovec iov;
|
||||
ssize_t n = -1;
|
||||
int fd;
|
||||
struct imsg_fd *ifd;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
|
||||
|
||||
iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
|
||||
iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = &cmsgbuf.buf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf.buf);
|
||||
|
||||
if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
|
||||
return (-1);
|
||||
|
||||
again:
|
||||
if (getdtablecount() + imsg_fd_overhead +
|
||||
(int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))
|
||||
>= getdtablesize()) {
|
||||
errno = EAGAIN;
|
||||
free(ifd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
|
||||
if (errno == EINTR)
|
||||
goto again;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ibuf->r.wpos += n;
|
||||
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
|
||||
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_RIGHTS) {
|
||||
int i;
|
||||
int j;
|
||||
|
||||
/*
|
||||
* We only accept one file descriptor. Due to C
|
||||
* padding rules, our control buffer might contain
|
||||
* more than one fd, and we must close them.
|
||||
*/
|
||||
j = ((char *)cmsg + cmsg->cmsg_len -
|
||||
(char *)CMSG_DATA(cmsg)) / sizeof(int);
|
||||
for (i = 0; i < j; i++) {
|
||||
fd = ((int *)CMSG_DATA(cmsg))[i];
|
||||
if (ifd != NULL) {
|
||||
ifd->fd = fd;
|
||||
TAILQ_INSERT_TAIL(&ibuf->fds, ifd,
|
||||
entry);
|
||||
ifd = NULL;
|
||||
} else
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
/* we do not handle other ctl data level */
|
||||
}
|
||||
|
||||
fail:
|
||||
free(ifd);
|
||||
return (n);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
|
||||
{
|
||||
size_t av, left, datalen;
|
||||
|
||||
av = ibuf->r.wpos;
|
||||
|
||||
if (IMSG_HEADER_SIZE > av)
|
||||
return (0);
|
||||
|
||||
memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
|
||||
if (imsg->hdr.len < IMSG_HEADER_SIZE ||
|
||||
imsg->hdr.len > MAX_IMSGSIZE) {
|
||||
errno = ERANGE;
|
||||
return (-1);
|
||||
}
|
||||
if (imsg->hdr.len > av)
|
||||
return (0);
|
||||
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
|
||||
if (datalen == 0)
|
||||
imsg->data = NULL;
|
||||
else if ((imsg->data = malloc(datalen)) == NULL)
|
||||
return (-1);
|
||||
|
||||
if (imsg->hdr.flags & IMSGF_HASFD)
|
||||
imsg->fd = imsg_get_fd(ibuf);
|
||||
else
|
||||
imsg->fd = -1;
|
||||
|
||||
memcpy(imsg->data, ibuf->r.rptr, datalen);
|
||||
|
||||
if (imsg->hdr.len < av) {
|
||||
left = av - imsg->hdr.len;
|
||||
memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
|
||||
ibuf->r.wpos = left;
|
||||
} else
|
||||
ibuf->r.wpos = 0;
|
||||
|
||||
return (datalen + IMSG_HEADER_SIZE);
|
||||
imsgbuf->flags |= IMSG_ALLOW_FDPASS;
|
||||
}
|
||||
|
||||
int
|
||||
imsg_compose(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
|
||||
int fd, const void *data, uint16_t datalen)
|
||||
imsgbuf_set_maxsize(struct imsgbuf *imsgbuf, uint32_t maxsize)
|
||||
{
|
||||
if (maxsize < IMSG_HEADER_SIZE || maxsize & IMSG_FD_MARK) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
imsgbuf->maxsize = maxsize;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
imsgbuf_read(struct imsgbuf *imsgbuf)
|
||||
{
|
||||
if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
|
||||
return msgbuf_read(imsgbuf->fd, imsgbuf->w);
|
||||
else
|
||||
return ibuf_read(imsgbuf->fd, imsgbuf->w);
|
||||
}
|
||||
|
||||
int
|
||||
imsgbuf_write(struct imsgbuf *imsgbuf)
|
||||
{
|
||||
if (imsgbuf->flags & IMSG_ALLOW_FDPASS)
|
||||
return msgbuf_write(imsgbuf->fd, imsgbuf->w);
|
||||
else
|
||||
return ibuf_write(imsgbuf->fd, imsgbuf->w);
|
||||
}
|
||||
|
||||
int
|
||||
imsgbuf_flush(struct imsgbuf *imsgbuf)
|
||||
{
|
||||
while (imsgbuf_queuelen(imsgbuf) > 0) {
|
||||
if (imsgbuf_write(imsgbuf) == -1)
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
imsgbuf_clear(struct imsgbuf *imsgbuf)
|
||||
{
|
||||
msgbuf_free(imsgbuf->w);
|
||||
imsgbuf->w = NULL;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
imsgbuf_queuelen(struct imsgbuf *imsgbuf)
|
||||
{
|
||||
return msgbuf_queuelen(imsgbuf->w);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
|
||||
{
|
||||
struct imsg m;
|
||||
struct ibuf *buf;
|
||||
|
||||
if ((buf = msgbuf_get(imsgbuf->w)) == NULL)
|
||||
return (0);
|
||||
|
||||
if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1)
|
||||
return (-1);
|
||||
|
||||
if (ibuf_size(buf))
|
||||
m.data = ibuf_data(buf);
|
||||
else
|
||||
m.data = NULL;
|
||||
m.buf = buf;
|
||||
m.hdr.len &= ~IMSG_FD_MARK;
|
||||
|
||||
*imsg = m;
|
||||
return (ibuf_size(buf) + IMSG_HEADER_SIZE);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf)
|
||||
{
|
||||
if (ibuf_size(imsg->buf) == 0) {
|
||||
errno = EBADMSG;
|
||||
return (-1);
|
||||
}
|
||||
return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_get_data(struct imsg *imsg, void *data, size_t len)
|
||||
{
|
||||
if (len == 0) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
if (ibuf_size(imsg->buf) != len) {
|
||||
errno = EBADMSG;
|
||||
return (-1);
|
||||
}
|
||||
return ibuf_get(imsg->buf, data, len);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_get_fd(struct imsg *imsg)
|
||||
{
|
||||
return ibuf_fd_get(imsg->buf);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
imsg_get_id(struct imsg *imsg)
|
||||
{
|
||||
return (imsg->hdr.peerid);
|
||||
}
|
||||
|
||||
size_t
|
||||
imsg_get_len(struct imsg *imsg)
|
||||
{
|
||||
return ibuf_size(imsg->buf);
|
||||
}
|
||||
|
||||
pid_t
|
||||
imsg_get_pid(struct imsg *imsg)
|
||||
{
|
||||
return (imsg->hdr.pid);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
imsg_get_type(struct imsg *imsg)
|
||||
{
|
||||
return (imsg->hdr.type);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
|
||||
int fd, const void *data, size_t datalen)
|
||||
{
|
||||
struct ibuf *wbuf;
|
||||
|
||||
if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
|
||||
if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
|
||||
return (-1);
|
||||
|
||||
if (imsg_add(wbuf, data, datalen) == -1)
|
||||
return (-1);
|
||||
|
||||
wbuf->fd = fd;
|
||||
|
||||
imsg_close(ibuf, wbuf);
|
||||
ibuf_fd_set(wbuf, fd);
|
||||
imsg_close(imsgbuf, wbuf);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
|
||||
imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
|
||||
int fd, const struct iovec *iov, int iovcnt)
|
||||
{
|
||||
struct ibuf *wbuf;
|
||||
int i, datalen = 0;
|
||||
int i;
|
||||
size_t datalen = 0;
|
||||
|
||||
for (i = 0; i < iovcnt; i++)
|
||||
datalen += iov[i].iov_len;
|
||||
|
||||
if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
|
||||
if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
|
||||
return (-1);
|
||||
|
||||
for (i = 0; i < iovcnt; i++)
|
||||
if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
|
||||
return (-1);
|
||||
|
||||
wbuf->fd = fd;
|
||||
|
||||
imsg_close(ibuf, wbuf);
|
||||
ibuf_fd_set(wbuf, fd);
|
||||
imsg_close(imsgbuf, wbuf);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
/*
|
||||
* Enqueue imsg with payload from ibuf buf. fd passing is not possible
|
||||
* with this function.
|
||||
*/
|
||||
int
|
||||
imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
|
||||
pid_t pid, struct ibuf *buf)
|
||||
{
|
||||
struct ibuf *hdrbuf = NULL;
|
||||
struct imsg_hdr hdr;
|
||||
int save_errno;
|
||||
|
||||
if (ibuf_size(buf) + IMSG_HEADER_SIZE > imsgbuf->maxsize) {
|
||||
errno = ERANGE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdr.type = type;
|
||||
hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE;
|
||||
hdr.peerid = id;
|
||||
if ((hdr.pid = pid) == 0)
|
||||
hdr.pid = imsgbuf->pid;
|
||||
|
||||
if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL)
|
||||
goto fail;
|
||||
if (imsg_add(hdrbuf, &hdr, sizeof(hdr)) == -1)
|
||||
goto fail;
|
||||
|
||||
ibuf_close(imsgbuf->w, hdrbuf);
|
||||
ibuf_close(imsgbuf->w, buf);
|
||||
return (1);
|
||||
|
||||
fail:
|
||||
save_errno = errno;
|
||||
ibuf_free(buf);
|
||||
ibuf_free(hdrbuf);
|
||||
errno = save_errno;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Forward imsg to another channel. Any attached fd is closed.
|
||||
*/
|
||||
int
|
||||
imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg)
|
||||
{
|
||||
struct ibuf *wbuf;
|
||||
size_t len;
|
||||
|
||||
ibuf_rewind(msg->buf);
|
||||
ibuf_skip(msg->buf, sizeof(msg->hdr));
|
||||
len = ibuf_size(msg->buf);
|
||||
|
||||
if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid,
|
||||
msg->hdr.pid, len)) == NULL)
|
||||
return (-1);
|
||||
|
||||
if (len != 0) {
|
||||
if (ibuf_add_ibuf(wbuf, msg->buf) == -1) {
|
||||
ibuf_free(wbuf);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
imsg_close(imsgbuf, wbuf);
|
||||
return (1);
|
||||
}
|
||||
|
||||
struct ibuf *
|
||||
imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
|
||||
uint16_t datalen)
|
||||
imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
|
||||
size_t datalen)
|
||||
{
|
||||
struct ibuf *wbuf;
|
||||
struct imsg_hdr hdr;
|
||||
|
||||
datalen += IMSG_HEADER_SIZE;
|
||||
if (datalen > MAX_IMSGSIZE) {
|
||||
if (datalen > imsgbuf->maxsize) {
|
||||
errno = ERANGE;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
hdr.type = type;
|
||||
hdr.flags = 0;
|
||||
hdr.peerid = peerid;
|
||||
hdr.peerid = id;
|
||||
if ((hdr.pid = pid) == 0)
|
||||
hdr.pid = ibuf->pid;
|
||||
if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
|
||||
hdr.pid = imsgbuf->pid;
|
||||
if ((wbuf = ibuf_dynamic(datalen, imsgbuf->maxsize)) == NULL) {
|
||||
return (NULL);
|
||||
}
|
||||
if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
|
||||
@ -234,7 +321,7 @@ imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
|
||||
}
|
||||
|
||||
int
|
||||
imsg_add(struct ibuf *msg, const void *data, uint16_t datalen)
|
||||
imsg_add(struct ibuf *msg, const void *data, size_t datalen)
|
||||
{
|
||||
if (datalen)
|
||||
if (ibuf_add(msg, data, datalen) == -1) {
|
||||
@ -245,58 +332,47 @@ imsg_add(struct ibuf *msg, const void *data, uint16_t datalen)
|
||||
}
|
||||
|
||||
void
|
||||
imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
|
||||
imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg)
|
||||
{
|
||||
struct imsg_hdr *hdr;
|
||||
uint32_t len;
|
||||
|
||||
hdr = (struct imsg_hdr *)msg->buf;
|
||||
|
||||
hdr->flags &= ~IMSGF_HASFD;
|
||||
if (msg->fd != -1)
|
||||
hdr->flags |= IMSGF_HASFD;
|
||||
|
||||
hdr->len = (uint16_t)msg->wpos;
|
||||
|
||||
ibuf_close(&ibuf->w, msg);
|
||||
len = ibuf_size(msg);
|
||||
if (ibuf_fd_avail(msg))
|
||||
len |= IMSG_FD_MARK;
|
||||
(void)ibuf_set_h32(msg, offsetof(struct imsg_hdr, len), len);
|
||||
ibuf_close(imsgbuf->w, msg);
|
||||
}
|
||||
|
||||
void
|
||||
imsg_free(struct imsg *imsg)
|
||||
{
|
||||
freezero(imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE);
|
||||
ibuf_free(imsg->buf);
|
||||
}
|
||||
|
||||
static int
|
||||
imsg_get_fd(struct imsgbuf *ibuf)
|
||||
static struct ibuf *
|
||||
imsg_parse_hdr(struct ibuf *buf, void *arg, int *fd)
|
||||
{
|
||||
int fd;
|
||||
struct imsg_fd *ifd;
|
||||
struct imsgbuf *imsgbuf = arg;
|
||||
struct imsg_hdr hdr;
|
||||
struct ibuf *b;
|
||||
uint32_t len;
|
||||
|
||||
if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
|
||||
return (-1);
|
||||
if (ibuf_get(buf, &hdr, sizeof(hdr)) == -1)
|
||||
return (NULL);
|
||||
|
||||
fd = ifd->fd;
|
||||
TAILQ_REMOVE(&ibuf->fds, ifd, entry);
|
||||
free(ifd);
|
||||
len = hdr.len & ~IMSG_FD_MARK;
|
||||
|
||||
return (fd);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_flush(struct imsgbuf *ibuf)
|
||||
{
|
||||
while (ibuf->w.queued)
|
||||
if (msgbuf_write(&ibuf->w) <= 0)
|
||||
return (-1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
imsg_clear(struct imsgbuf *ibuf)
|
||||
{
|
||||
int fd;
|
||||
|
||||
msgbuf_clear(&ibuf->w);
|
||||
while ((fd = imsg_get_fd(ibuf)) != -1)
|
||||
close(fd);
|
||||
if (len < IMSG_HEADER_SIZE || len > imsgbuf->maxsize) {
|
||||
errno = ERANGE;
|
||||
return (NULL);
|
||||
}
|
||||
if ((b = ibuf_open(len)) == NULL)
|
||||
return (NULL);
|
||||
if (hdr.len & IMSG_FD_MARK) {
|
||||
ibuf_fd_set(b, *fd);
|
||||
*fd = -1;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
122
compat/imsg.h
122
compat/imsg.h
@ -1,6 +1,7 @@
|
||||
/* $OpenBSD: imsg.h,v 1.5 2019/01/20 02:50:03 bcook Exp $ */
|
||||
/* $OpenBSD: imsg.h,v 1.19 2024/11/26 13:57:31 claudio Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
|
||||
* Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
|
||||
* Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
@ -21,7 +22,7 @@
|
||||
#ifndef _IMSG_H_
|
||||
#define _IMSG_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#define IBUF_READ_SIZE 65535
|
||||
#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
|
||||
@ -37,77 +38,116 @@ struct ibuf {
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct msgbuf {
|
||||
TAILQ_HEAD(, ibuf) bufs;
|
||||
uint32_t queued;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct ibuf_read {
|
||||
unsigned char buf[IBUF_READ_SIZE];
|
||||
unsigned char *rptr;
|
||||
size_t wpos;
|
||||
};
|
||||
|
||||
struct imsg_fd {
|
||||
TAILQ_ENTRY(imsg_fd) entry;
|
||||
int fd;
|
||||
};
|
||||
struct msgbuf;
|
||||
|
||||
struct imsgbuf {
|
||||
TAILQ_HEAD(, imsg_fd) fds;
|
||||
struct ibuf_read r;
|
||||
struct msgbuf w;
|
||||
int fd;
|
||||
struct msgbuf *w;
|
||||
pid_t pid;
|
||||
uint32_t maxsize;
|
||||
int fd;
|
||||
int flags;
|
||||
};
|
||||
|
||||
#define IMSGF_HASFD 1
|
||||
|
||||
struct imsg_hdr {
|
||||
uint32_t type;
|
||||
uint16_t len;
|
||||
uint16_t flags;
|
||||
uint32_t len;
|
||||
uint32_t peerid;
|
||||
uint32_t pid;
|
||||
};
|
||||
|
||||
struct imsg {
|
||||
struct imsg_hdr hdr;
|
||||
int fd;
|
||||
void *data;
|
||||
struct ibuf *buf;
|
||||
};
|
||||
|
||||
struct iovec;
|
||||
|
||||
/* buffer.c */
|
||||
/* imsg-buffer.c */
|
||||
struct ibuf *ibuf_open(size_t);
|
||||
struct ibuf *ibuf_dynamic(size_t, size_t);
|
||||
int ibuf_add(struct ibuf *, const void *, size_t);
|
||||
int ibuf_add_ibuf(struct ibuf *, const struct ibuf *);
|
||||
int ibuf_add_zero(struct ibuf *, size_t);
|
||||
int ibuf_add_n8(struct ibuf *, uint64_t);
|
||||
int ibuf_add_n16(struct ibuf *, uint64_t);
|
||||
int ibuf_add_n32(struct ibuf *, uint64_t);
|
||||
int ibuf_add_n64(struct ibuf *, uint64_t);
|
||||
int ibuf_add_h16(struct ibuf *, uint64_t);
|
||||
int ibuf_add_h32(struct ibuf *, uint64_t);
|
||||
int ibuf_add_h64(struct ibuf *, uint64_t);
|
||||
void *ibuf_reserve(struct ibuf *, size_t);
|
||||
void *ibuf_seek(struct ibuf *, size_t, size_t);
|
||||
size_t ibuf_size(struct ibuf *);
|
||||
size_t ibuf_left(struct ibuf *);
|
||||
int ibuf_set(struct ibuf *, size_t, const void *, size_t);
|
||||
int ibuf_set_n8(struct ibuf *, size_t, uint64_t);
|
||||
int ibuf_set_n16(struct ibuf *, size_t, uint64_t);
|
||||
int ibuf_set_n32(struct ibuf *, size_t, uint64_t);
|
||||
int ibuf_set_n64(struct ibuf *, size_t, uint64_t);
|
||||
int ibuf_set_h16(struct ibuf *, size_t, uint64_t);
|
||||
int ibuf_set_h32(struct ibuf *, size_t, uint64_t);
|
||||
int ibuf_set_h64(struct ibuf *, size_t, uint64_t);
|
||||
void *ibuf_data(const struct ibuf *);
|
||||
size_t ibuf_size(const struct ibuf *);
|
||||
size_t ibuf_left(const struct ibuf *);
|
||||
int ibuf_truncate(struct ibuf *, size_t);
|
||||
void ibuf_rewind(struct ibuf *);
|
||||
void ibuf_close(struct msgbuf *, struct ibuf *);
|
||||
int ibuf_write(struct msgbuf *);
|
||||
void ibuf_from_buffer(struct ibuf *, void *, size_t);
|
||||
void ibuf_from_ibuf(struct ibuf *, const struct ibuf *);
|
||||
int ibuf_get(struct ibuf *, void *, size_t);
|
||||
int ibuf_get_ibuf(struct ibuf *, size_t, struct ibuf *);
|
||||
int ibuf_get_n8(struct ibuf *, uint8_t *);
|
||||
int ibuf_get_n16(struct ibuf *, uint16_t *);
|
||||
int ibuf_get_n32(struct ibuf *, uint32_t *);
|
||||
int ibuf_get_n64(struct ibuf *, uint64_t *);
|
||||
int ibuf_get_h16(struct ibuf *, uint16_t *);
|
||||
int ibuf_get_h32(struct ibuf *, uint32_t *);
|
||||
int ibuf_get_h64(struct ibuf *, uint64_t *);
|
||||
char *ibuf_get_string(struct ibuf *, size_t);
|
||||
int ibuf_skip(struct ibuf *, size_t);
|
||||
void ibuf_free(struct ibuf *);
|
||||
void msgbuf_init(struct msgbuf *);
|
||||
int ibuf_fd_avail(struct ibuf *);
|
||||
int ibuf_fd_get(struct ibuf *);
|
||||
void ibuf_fd_set(struct ibuf *, int);
|
||||
struct msgbuf *msgbuf_new(void);
|
||||
struct msgbuf *msgbuf_new_reader(size_t,
|
||||
struct ibuf *(*)(struct ibuf *, void *, int *), void *);
|
||||
void msgbuf_free(struct msgbuf *);
|
||||
void msgbuf_clear(struct msgbuf *);
|
||||
int msgbuf_write(struct msgbuf *);
|
||||
void msgbuf_drain(struct msgbuf *, size_t);
|
||||
uint32_t msgbuf_queuelen(struct msgbuf *);
|
||||
int ibuf_write(int, struct msgbuf *);
|
||||
int msgbuf_write(int, struct msgbuf *);
|
||||
int ibuf_read(int, struct msgbuf *);
|
||||
int msgbuf_read(int, struct msgbuf *);
|
||||
struct ibuf *msgbuf_get(struct msgbuf *);
|
||||
|
||||
/* imsg.c */
|
||||
void imsg_init(struct imsgbuf *, int);
|
||||
ssize_t imsg_read(struct imsgbuf *);
|
||||
int imsgbuf_init(struct imsgbuf *, int);
|
||||
void imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf);
|
||||
int imsgbuf_set_maxsize(struct imsgbuf *, uint32_t);
|
||||
int imsgbuf_read(struct imsgbuf *);
|
||||
int imsgbuf_write(struct imsgbuf *);
|
||||
int imsgbuf_flush(struct imsgbuf *);
|
||||
void imsgbuf_clear(struct imsgbuf *);
|
||||
uint32_t imsgbuf_queuelen(struct imsgbuf *);
|
||||
ssize_t imsg_get(struct imsgbuf *, struct imsg *);
|
||||
int imsg_get_ibuf(struct imsg *, struct ibuf *);
|
||||
int imsg_get_data(struct imsg *, void *, size_t);
|
||||
int imsg_get_fd(struct imsg *);
|
||||
uint32_t imsg_get_id(struct imsg *);
|
||||
size_t imsg_get_len(struct imsg *);
|
||||
pid_t imsg_get_pid(struct imsg *);
|
||||
uint32_t imsg_get_type(struct imsg *);
|
||||
int imsg_forward(struct imsgbuf *, struct imsg *);
|
||||
int imsg_compose(struct imsgbuf *, uint32_t, uint32_t, pid_t, int,
|
||||
const void *, uint16_t);
|
||||
const void *, size_t);
|
||||
int imsg_composev(struct imsgbuf *, uint32_t, uint32_t, pid_t, int,
|
||||
const struct iovec *, int);
|
||||
struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, uint16_t);
|
||||
int imsg_add(struct ibuf *, const void *, uint16_t);
|
||||
int imsg_compose_ibuf(struct imsgbuf *, uint32_t, uint32_t, pid_t,
|
||||
struct ibuf *);
|
||||
struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, size_t);
|
||||
int imsg_add(struct ibuf *, const void *, size_t);
|
||||
void imsg_close(struct imsgbuf *, struct ibuf *);
|
||||
void imsg_free(struct imsg *);
|
||||
int imsg_flush(struct imsgbuf *);
|
||||
void imsg_clear(struct imsgbuf *);
|
||||
|
||||
#endif
|
||||
|
31
compat/ntohll.c
Normal file
31
compat/ntohll.c
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
uint64_t
|
||||
ntohll(uint64_t v)
|
||||
{
|
||||
uint32_t b;
|
||||
uint32_t t;
|
||||
|
||||
b = ntohl (v & 0xffffffff);
|
||||
t = ntohl (v >> 32);
|
||||
return ((uint64_t)b << 32 | t);
|
||||
}
|
@ -32,8 +32,8 @@
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H_
|
||||
#define _SYS_QUEUE_H_
|
||||
#ifndef _COMPAT_QUEUE_H_
|
||||
#define _COMPAT_QUEUE_H_
|
||||
|
||||
/*
|
||||
* This file defines five types of data structures: singly-linked lists,
|
||||
@ -530,4 +530,4 @@ struct { \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif /* !_SYS_QUEUE_H_ */
|
||||
#endif /* !_COMPAT_QUEUE_H_ */
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
270
compat/systemd.c
270
compat/systemd.c
@ -19,17 +19,35 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <systemd/sd-daemon.h>
|
||||
#include <systemd/sd-login.h>
|
||||
#include <systemd/sd-id128.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
#ifndef SD_ID128_UUID_FORMAT_STR
|
||||
#define SD_ID128_UUID_FORMAT_STR \
|
||||
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x"
|
||||
#endif
|
||||
|
||||
int
|
||||
systemd_activated(void)
|
||||
{
|
||||
return (sd_listen_fds(0) >= 1);
|
||||
}
|
||||
|
||||
int
|
||||
systemd_create_socket(int flags, char **cause)
|
||||
{
|
||||
int fds;
|
||||
int fd;
|
||||
struct sockaddr_un sa;
|
||||
int addrlen = sizeof sa;
|
||||
socklen_t addrlen = sizeof sa;
|
||||
|
||||
fds = sd_listen_fds(0);
|
||||
if (fds > 1) { /* too many file descriptors */
|
||||
@ -56,3 +74,253 @@ fail:
|
||||
xasprintf(cause, "systemd socket error (%s)", strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
struct systemd_job_watch {
|
||||
const char *path;
|
||||
int done;
|
||||
};
|
||||
|
||||
static int
|
||||
job_removed_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
|
||||
{
|
||||
struct systemd_job_watch *watch = userdata;
|
||||
const char *path = NULL;
|
||||
uint32_t id;
|
||||
int r;
|
||||
|
||||
/* This handler could be called during the sd_bus_call. */
|
||||
if (watch->path == NULL)
|
||||
return 0;
|
||||
|
||||
r = sd_bus_message_read(m, "uo", &id, &path);
|
||||
if (r < 0)
|
||||
return (r);
|
||||
|
||||
if (strcmp(path, watch->path) == 0)
|
||||
watch->done = 1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
systemd_move_to_new_cgroup(char **cause)
|
||||
{
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
sd_bus_message *m = NULL, *reply = NULL;
|
||||
sd_bus *bus = NULL;
|
||||
sd_bus_slot *slot = NULL;
|
||||
char *name, *desc, *slice;
|
||||
sd_id128_t uuid;
|
||||
int r;
|
||||
uint64_t elapsed_usec;
|
||||
pid_t pid, parent_pid;
|
||||
struct timeval start, now;
|
||||
struct systemd_job_watch watch = {};
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
|
||||
/* Connect to the session bus. */
|
||||
r = sd_bus_default_user(&bus);
|
||||
if (r < 0) {
|
||||
xasprintf(cause, "failed to connect to session bus: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Start watching for JobRemoved events */
|
||||
r = sd_bus_match_signal(bus, &slot,
|
||||
"org.freedesktop.systemd1",
|
||||
"/org/freedesktop/systemd1",
|
||||
"org.freedesktop.systemd1.Manager",
|
||||
"JobRemoved",
|
||||
job_removed_handler,
|
||||
&watch);
|
||||
if (r < 0) {
|
||||
xasprintf(cause, "failed to create match signal: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Start building the method call. */
|
||||
r = sd_bus_message_new_method_call(bus, &m,
|
||||
"org.freedesktop.systemd1",
|
||||
"/org/freedesktop/systemd1",
|
||||
"org.freedesktop.systemd1.Manager",
|
||||
"StartTransientUnit");
|
||||
if (r < 0) {
|
||||
xasprintf(cause, "failed to create bus message: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Generate a unique name for the new scope, to avoid collisions. */
|
||||
r = sd_id128_randomize(&uuid);
|
||||
if (r < 0) {
|
||||
xasprintf(cause, "failed to generate uuid: %s", strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
xasprintf(&name, "tmux-spawn-" SD_ID128_UUID_FORMAT_STR ".scope",
|
||||
SD_ID128_FORMAT_VAL(uuid));
|
||||
r = sd_bus_message_append(m, "s", name);
|
||||
free(name);
|
||||
if (r < 0) {
|
||||
xasprintf(cause, "failed to append to bus message: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Mode: fail if there's a queued unit with the same name. */
|
||||
r = sd_bus_message_append(m, "s", "fail");
|
||||
if (r < 0) {
|
||||
xasprintf(cause, "failed to append to bus message: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Start properties array. */
|
||||
r = sd_bus_message_open_container(m, 'a', "(sv)");
|
||||
if (r < 0) {
|
||||
xasprintf(cause, "failed to start properties array: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
pid = getpid();
|
||||
parent_pid = getppid();
|
||||
xasprintf(&desc, "tmux child pane %ld launched by process %ld",
|
||||
(long)pid, (long)parent_pid);
|
||||
r = sd_bus_message_append(m, "(sv)", "Description", "s", desc);
|
||||
free(desc);
|
||||
if (r < 0) {
|
||||
xasprintf(cause, "failed to append to properties: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure that the session shells are terminated with SIGHUP since
|
||||
* bash and friends tend to ignore SIGTERM.
|
||||
*/
|
||||
r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", 1);
|
||||
if (r < 0) {
|
||||
xasprintf(cause, "failed to append to properties: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/*
|
||||
* Inherit the slice from the parent process, or default to
|
||||
* "app-tmux.slice" if that fails.
|
||||
*/
|
||||
r = sd_pid_get_user_slice(parent_pid, &slice);
|
||||
if (r < 0) {
|
||||
slice = xstrdup("app-tmux.slice");
|
||||
}
|
||||
r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
|
||||
free(slice);
|
||||
if (r < 0) {
|
||||
xasprintf(cause, "failed to append to properties: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* PIDs to add to the scope: length - 1 array of uint32_t. */
|
||||
r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
|
||||
if (r < 0) {
|
||||
xasprintf(cause, "failed to append to properties: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Clean up the scope even if it fails. */
|
||||
r = sd_bus_message_append(m, "(sv)", "CollectMode", "s",
|
||||
"inactive-or-failed");
|
||||
if (r < 0) {
|
||||
xasprintf(cause, "failed to append to properties: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* End properties array. */
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0) {
|
||||
xasprintf(cause, "failed to end properties array: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* aux is currently unused and should be passed an empty array. */
|
||||
r = sd_bus_message_append(m, "a(sa(sv))", 0);
|
||||
if (r < 0) {
|
||||
xasprintf(cause, "failed to append to bus message: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Call the method with a timeout of 1 second = 1e6 us. */
|
||||
r = sd_bus_call(bus, m, 1000000, &error, &reply);
|
||||
if (r < 0) {
|
||||
if (error.message != NULL) {
|
||||
/* We have a specific error message from sd-bus. */
|
||||
xasprintf(cause, "StartTransientUnit call failed: %s",
|
||||
error.message);
|
||||
} else {
|
||||
xasprintf(cause, "StartTransientUnit call failed: %s",
|
||||
strerror(-r));
|
||||
}
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Get the job (object path) from the reply */
|
||||
r = sd_bus_message_read(reply, "o", &watch.path);
|
||||
if (r < 0) {
|
||||
xasprintf(cause, "failed to parse method reply: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
while (!watch.done) {
|
||||
/* Process events including callbacks. */
|
||||
r = sd_bus_process(bus, NULL);
|
||||
if (r < 0) {
|
||||
xasprintf(cause,
|
||||
"failed waiting for cgroup allocation: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/*
|
||||
* A positive return means we handled an event and should keep
|
||||
* processing; zero indicates no events available, so wait.
|
||||
*/
|
||||
if (r > 0)
|
||||
continue;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
elapsed_usec = (now.tv_sec - start.tv_sec) * 1000000 +
|
||||
now.tv_usec - start.tv_usec;
|
||||
|
||||
if (elapsed_usec >= 1000000) {
|
||||
xasprintf(cause,
|
||||
"timeout waiting for cgroup allocation");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = sd_bus_wait(bus, 1000000 - elapsed_usec);
|
||||
if (r < 0) {
|
||||
xasprintf(cause,
|
||||
"failed waiting for cgroup allocation: %s",
|
||||
strerror(-r));
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
sd_bus_error_free(&error);
|
||||
sd_bus_message_unref(m);
|
||||
sd_bus_message_unref(reply);
|
||||
sd_bus_slot_unref(slot);
|
||||
sd_bus_unref(bus);
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
108
configure.ac
108
configure.ac
@ -1,6 +1,6 @@
|
||||
# configure.ac
|
||||
|
||||
AC_INIT([tmux], 3.3a)
|
||||
AC_INIT([tmux], next-3.6)
|
||||
AC_PREREQ([2.60])
|
||||
|
||||
AC_CONFIG_AUX_DIR(etc)
|
||||
@ -44,7 +44,7 @@ fi
|
||||
# Set up the compiler in two different ways and say yes we may want to install.
|
||||
AC_PROG_CC
|
||||
AM_PROG_CC_C_O
|
||||
AC_PROG_CC_C99
|
||||
m4_version_prereq(2.70, [AC_PROG_CC], [AC_PROG_CC_C99])
|
||||
AC_PROG_CPP
|
||||
AC_PROG_EGREP
|
||||
AC_PROG_INSTALL
|
||||
@ -56,10 +56,11 @@ AC_USE_SYSTEM_EXTENSIONS
|
||||
test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc
|
||||
|
||||
# Is this --enable-debug?
|
||||
case "x$VERSION" in xnext*) enable_debug=yes;; esac
|
||||
AC_ARG_ENABLE(
|
||||
debug,
|
||||
AS_HELP_STRING(--enable-debug, enable debug build flags),
|
||||
,
|
||||
[case "x$VERSION" in xnext*) enable_debug=yes;; esac]
|
||||
)
|
||||
AM_CONDITIONAL(IS_DEBUG, test "x$enable_debug" = xyes)
|
||||
|
||||
@ -148,7 +149,7 @@ AC_CHECK_FUNCS([ \
|
||||
prctl \
|
||||
proc_pidinfo \
|
||||
getpeerucred \
|
||||
sysconf \
|
||||
sysconf
|
||||
])
|
||||
|
||||
# Check for functions with a compatibility implementation.
|
||||
@ -165,7 +166,9 @@ AC_REPLACE_FUNCS([ \
|
||||
getpeereid \
|
||||
getline \
|
||||
getprogname \
|
||||
htonll \
|
||||
memmem \
|
||||
ntohll \
|
||||
setenv \
|
||||
setproctitle \
|
||||
strcasestr \
|
||||
@ -216,7 +219,7 @@ AC_SEARCH_LIBS(clock_gettime, rt)
|
||||
# musl does not set optarg to NULL for flags without arguments (although it is
|
||||
# not required to, but it is helpful) 3) there are probably other weird
|
||||
# implementations.
|
||||
AC_LIBOBJ(getopt)
|
||||
AC_LIBOBJ(getopt_long)
|
||||
|
||||
# Look for libevent. Try libevent_core or libevent with pkg-config first then
|
||||
# look for the library.
|
||||
@ -267,6 +270,12 @@ if test "x$found_libevent" = xno; then
|
||||
AC_MSG_ERROR("libevent not found")
|
||||
fi
|
||||
|
||||
# Look for yacc.
|
||||
AC_CHECK_PROG(found_yacc, $YACC, yes, no)
|
||||
if test "x$found_yacc" = xno; then
|
||||
AC_MSG_ERROR("yacc not found")
|
||||
fi
|
||||
|
||||
# Look for ncurses or curses. Try pkg-config first then directly for the
|
||||
# library.
|
||||
PKG_CHECK_MODULES(
|
||||
@ -309,7 +318,7 @@ fi
|
||||
if test "x$found_ncurses" = xno; then
|
||||
AC_SEARCH_LIBS(
|
||||
setupterm,
|
||||
[tinfo ncurses ncursesw],
|
||||
[tinfo terminfo ncurses ncursesw],
|
||||
found_ncurses=yes,
|
||||
found_ncurses=no
|
||||
)
|
||||
@ -344,6 +353,10 @@ else
|
||||
AC_MSG_ERROR("curses not found")
|
||||
fi
|
||||
fi
|
||||
AC_CHECK_FUNCS([ \
|
||||
tiparm \
|
||||
tiparm_s \
|
||||
])
|
||||
|
||||
# Look for utempter.
|
||||
AC_ARG_ENABLE(
|
||||
@ -373,6 +386,15 @@ AC_ARG_ENABLE(
|
||||
AS_HELP_STRING(--enable-utf8proc, use utf8proc if it is installed)
|
||||
)
|
||||
if test "x$enable_utf8proc" = xyes; then
|
||||
PKG_CHECK_MODULES(
|
||||
LIBUTF8PROC,
|
||||
libutf8proc,
|
||||
[
|
||||
AM_CPPFLAGS="$LIBUTF8PROC_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$LIBUTF8PROC_CFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBUTF8PROC_LIBS $LIBS"
|
||||
]
|
||||
)
|
||||
AC_CHECK_HEADER(utf8proc.h, enable_utf8proc=yes, enable_utf8proc=no)
|
||||
if test "x$enable_utf8proc" = xyes; then
|
||||
AC_SEARCH_LIBS(
|
||||
@ -414,10 +436,35 @@ if test x"$enable_systemd" = xyes; then
|
||||
fi
|
||||
fi
|
||||
AM_CONDITIONAL(HAVE_SYSTEMD, [test "x$found_systemd" = xyes])
|
||||
AC_ARG_ENABLE(
|
||||
cgroups,
|
||||
AS_HELP_STRING(--disable-cgroups, disable adding panes to new cgroups with systemd)
|
||||
)
|
||||
if test "x$enable_cgroups" = x; then
|
||||
# Default to the same as $enable_systemd.
|
||||
enable_cgroups=$enable_systemd
|
||||
fi
|
||||
if test "x$enable_cgroups" = xyes; then
|
||||
if test "x$found_systemd" = xyes; then
|
||||
AC_DEFINE(ENABLE_CGROUPS)
|
||||
else
|
||||
AC_MSG_ERROR("cgroups requires systemd to be enabled")
|
||||
fi
|
||||
fi
|
||||
|
||||
# Enable sixel support.
|
||||
AC_ARG_ENABLE(
|
||||
sixel,
|
||||
AS_HELP_STRING(--enable-sixel, enable sixel images)
|
||||
)
|
||||
if test "x$enable_sixel" = xyes; then
|
||||
AC_DEFINE(ENABLE_SIXEL)
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_SIXEL, [test "x$enable_sixel" = xyes])
|
||||
|
||||
# Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well.
|
||||
AC_MSG_CHECKING(for b64_ntop)
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM(
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM(
|
||||
[
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
@ -502,6 +549,24 @@ if test "x$found_malloc_trim" = xyes; then
|
||||
AC_DEFINE(HAVE_MALLOC_TRIM)
|
||||
fi
|
||||
|
||||
# Build against jemalloc if requested.
|
||||
AC_ARG_ENABLE(
|
||||
jemalloc,
|
||||
AS_HELP_STRING(--enable-jemalloc, use jemalloc if it is installed)
|
||||
)
|
||||
if test "x$enable_jemalloc" = xyes; then
|
||||
PKG_CHECK_MODULES(
|
||||
JEMALLOC,
|
||||
jemalloc,
|
||||
[
|
||||
AM_CPPFLAGS="$JEMALLOC_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBS $JEMALLOC_LIBS"
|
||||
],
|
||||
AC_MSG_ERROR("jemalloc not found")
|
||||
)
|
||||
fi
|
||||
|
||||
# Check for CMSG_DATA. On some platforms like HP-UX this requires UNIX 95
|
||||
# (_XOPEN_SOURCE and _XOPEN_SOURCE_EXTENDED) (see xopen_networking(7)). On
|
||||
# others, UNIX 03 (_XOPEN_SOURCE 600, see standards(7) on Solaris).
|
||||
@ -573,9 +638,9 @@ else
|
||||
AC_LIBOBJ(err)
|
||||
fi
|
||||
|
||||
# Look for imsg_init in libutil.
|
||||
AC_SEARCH_LIBS(imsg_init, util, found_imsg_init=yes, found_imsg_init=no)
|
||||
if test "x$found_imsg_init" = xyes; then
|
||||
# Look for imsg_add in libutil.
|
||||
AC_SEARCH_LIBS(imsg_add, util, found_imsg_add=yes, found_imsg_add=no)
|
||||
if test "x$found_imsg_add" = xyes; then
|
||||
AC_DEFINE(HAVE_IMSG)
|
||||
else
|
||||
AC_LIBOBJ(imsg)
|
||||
@ -880,8 +945,13 @@ case "$host_os" in
|
||||
MANFORMAT=mdoc
|
||||
;;
|
||||
*)
|
||||
# Solaris 2.0 to 11.3 use AT&T nroff.
|
||||
MANFORMAT=man
|
||||
if test `uname -o 2>/dev/null` = illumos; then
|
||||
# Illumos uses mandoc.
|
||||
MANFORMAT=mdoc
|
||||
else
|
||||
# Solaris 2.0 to 11.3 use AT&T nroff.
|
||||
MANFORMAT=man
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
@ -912,9 +982,23 @@ AM_CONDITIONAL(IS_NETBSD, test "x$PLATFORM" = xnetbsd)
|
||||
AM_CONDITIONAL(IS_OPENBSD, test "x$PLATFORM" = xopenbsd)
|
||||
AM_CONDITIONAL(IS_SUNOS, test "x$PLATFORM" = xsunos)
|
||||
AM_CONDITIONAL(IS_HPUX, test "x$PLATFORM" = xhpux)
|
||||
AM_CONDITIONAL(IS_CYGWIN, test "x$PLATFORM" = xcygwin)
|
||||
AM_CONDITIONAL(IS_HAIKU, test "x$PLATFORM" = xhaiku)
|
||||
AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown)
|
||||
|
||||
# Set the default lock command
|
||||
DEFAULT_LOCK_CMD="lock -np"
|
||||
AC_MSG_CHECKING(lock-command)
|
||||
if test "x$PLATFORM" = xlinux; then
|
||||
AC_CHECK_PROG(found_vlock, vlock, yes, no)
|
||||
if test "x$found_vlock" = xyes; then
|
||||
DEFAULT_LOCK_CMD="vlock"
|
||||
fi
|
||||
fi
|
||||
AC_MSG_RESULT($DEFAULT_LOCK_CMD)
|
||||
AC_SUBST(DEFAULT_LOCK_CMD)
|
||||
|
||||
|
||||
# Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user
|
||||
# variables.
|
||||
AC_SUBST(AM_CPPFLAGS)
|
||||
|
@ -234,3 +234,29 @@ control_notify_session_window_changed(struct session *s)
|
||||
s->curw->window->id);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
control_notify_paste_buffer_changed(const char *name)
|
||||
{
|
||||
struct client *c;
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
|
||||
control_write(c, "%%paste-buffer-changed %s", name);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
control_notify_paste_buffer_deleted(const char *name)
|
||||
{
|
||||
struct client *c;
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
|
||||
control_write(c, "%%paste-buffer-deleted %s", name);
|
||||
}
|
||||
}
|
||||
|
12
control.c
12
control.c
@ -775,13 +775,16 @@ control_start(struct client *c)
|
||||
|
||||
cs->read_event = bufferevent_new(c->fd, control_read_callback,
|
||||
control_write_callback, control_error_callback, c);
|
||||
bufferevent_enable(cs->read_event, EV_READ);
|
||||
if (cs->read_event == NULL)
|
||||
fatalx("out of memory");
|
||||
|
||||
if (c->flags & CLIENT_CONTROLCONTROL)
|
||||
cs->write_event = cs->read_event;
|
||||
else {
|
||||
cs->write_event = bufferevent_new(c->out_fd, NULL,
|
||||
control_write_callback, control_error_callback, c);
|
||||
if (cs->write_event == NULL)
|
||||
fatalx("out of memory");
|
||||
}
|
||||
bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
|
||||
0);
|
||||
@ -792,6 +795,13 @@ control_start(struct client *c)
|
||||
}
|
||||
}
|
||||
|
||||
/* Control client ready. */
|
||||
void
|
||||
control_ready(struct client *c)
|
||||
{
|
||||
bufferevent_enable(c->control_state->read_event, EV_READ);
|
||||
}
|
||||
|
||||
/* Discard all output for a client. */
|
||||
void
|
||||
control_discard(struct client *c)
|
||||
|
21
environ.c
21
environ.c
@ -182,9 +182,11 @@ void
|
||||
environ_update(struct options *oo, struct environ *src, struct environ *dst)
|
||||
{
|
||||
struct environ_entry *envent;
|
||||
struct environ_entry *envent1;
|
||||
struct options_entry *o;
|
||||
struct options_array_item *a;
|
||||
union options_value *ov;
|
||||
int found;
|
||||
|
||||
o = options_get(oo, "update-environment");
|
||||
if (o == NULL)
|
||||
@ -192,14 +194,15 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst)
|
||||
a = options_array_first(o);
|
||||
while (a != NULL) {
|
||||
ov = options_array_item_value(a);
|
||||
RB_FOREACH(envent, environ, src) {
|
||||
if (fnmatch(ov->string, envent->name, 0) == 0)
|
||||
break;
|
||||
found = 0;
|
||||
RB_FOREACH_SAFE(envent, environ, src, envent1) {
|
||||
if (fnmatch(ov->string, envent->name, 0) == 0) {
|
||||
environ_set(dst, envent->name, 0, "%s", envent->value);
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
if (envent == NULL)
|
||||
if (!found)
|
||||
environ_clear(dst, ov->string);
|
||||
else
|
||||
environ_set(dst, envent->name, 0, "%s", envent->value);
|
||||
a = options_array_next(a);
|
||||
}
|
||||
}
|
||||
@ -261,6 +264,12 @@ environ_for_session(struct session *s, int no_TERM)
|
||||
environ_set(env, "TERM_PROGRAM_VERSION", 0, "%s", getversion());
|
||||
}
|
||||
|
||||
#ifdef HAVE_SYSTEMD
|
||||
environ_clear(env, "LISTEN_PID");
|
||||
environ_clear(env, "LISTEN_FDS");
|
||||
environ_clear(env, "LISTEN_FDNAMES");
|
||||
#endif
|
||||
|
||||
if (s != NULL)
|
||||
idx = s->id;
|
||||
else
|
||||
|
@ -14,7 +14,7 @@ set -g status-bg red
|
||||
%endif
|
||||
|
||||
# Enable RGB colour if running in xterm(1)
|
||||
set-option -sa terminal-overrides ",xterm*:Tc"
|
||||
set-option -sa terminal-features ",xterm*:RGB"
|
||||
|
||||
# Change the default $TERM to tmux-256color
|
||||
set -g default-terminal "tmux-256color"
|
||||
|
54
file.c
54
file.c
@ -149,7 +149,8 @@ file_fire_done_cb(__unused int fd, __unused short events, void *arg)
|
||||
struct client_file *cf = arg;
|
||||
struct client *c = cf->c;
|
||||
|
||||
if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD)))
|
||||
if (cf->cb != NULL &&
|
||||
(cf->closed || c == NULL || (~c->flags & CLIENT_DEAD)))
|
||||
cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
|
||||
file_free(cf);
|
||||
}
|
||||
@ -173,9 +174,9 @@ file_fire_read(struct client_file *cf)
|
||||
int
|
||||
file_can_print(struct client *c)
|
||||
{
|
||||
if (c == NULL)
|
||||
return (0);
|
||||
if (c->session != NULL && (~c->flags & CLIENT_CONTROL))
|
||||
if (c == NULL ||
|
||||
(c->flags & CLIENT_ATTACHED) ||
|
||||
(c->flags & CLIENT_CONTROL))
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
@ -352,7 +353,7 @@ done:
|
||||
}
|
||||
|
||||
/* Read a file. */
|
||||
void
|
||||
struct client_file *
|
||||
file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
|
||||
{
|
||||
struct client_file *cf;
|
||||
@ -420,10 +421,27 @@ skip:
|
||||
goto done;
|
||||
}
|
||||
free(msg);
|
||||
return;
|
||||
return cf;
|
||||
|
||||
done:
|
||||
file_fire_done(cf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Cancel a file read. */
|
||||
void
|
||||
file_cancel(struct client_file *cf)
|
||||
{
|
||||
struct msg_read_cancel msg;
|
||||
|
||||
log_debug("read cancel file %d", cf->stream);
|
||||
|
||||
if (cf->closed)
|
||||
return;
|
||||
cf->closed = 1;
|
||||
|
||||
msg.stream = cf->stream;
|
||||
proc_send(cf->peer, MSG_READ_CANCEL, -1, &msg, sizeof msg);
|
||||
}
|
||||
|
||||
/* Push event, fired if there is more writing to be done. */
|
||||
@ -585,6 +603,8 @@ file_write_open(struct client_files *files, struct tmuxpeer *peer,
|
||||
|
||||
cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
|
||||
file_write_error_callback, cf);
|
||||
if (cf->event == NULL)
|
||||
fatalx("out of memory");
|
||||
bufferevent_enable(cf->event, EV_WRITE);
|
||||
goto reply;
|
||||
|
||||
@ -744,6 +764,8 @@ file_read_open(struct client_files *files, struct tmuxpeer *peer,
|
||||
|
||||
cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
|
||||
file_read_error_callback, cf);
|
||||
if (cf->event == NULL)
|
||||
fatalx("out of memory");
|
||||
bufferevent_enable(cf->event, EV_READ);
|
||||
return;
|
||||
|
||||
@ -753,6 +775,24 @@ reply:
|
||||
proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
|
||||
}
|
||||
|
||||
/* Handle a read cancel message (client). */
|
||||
void
|
||||
file_read_cancel(struct client_files *files, struct imsg *imsg)
|
||||
{
|
||||
struct msg_read_cancel *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
struct client_file find, *cf;
|
||||
|
||||
if (msglen != sizeof *msg)
|
||||
fatalx("bad MSG_READ_CANCEL size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||
fatalx("unknown stream number");
|
||||
log_debug("cancel file %d", cf->stream);
|
||||
|
||||
file_read_error_callback(NULL, 0, cf);
|
||||
}
|
||||
|
||||
/* Handle a write ready message (server). */
|
||||
void
|
||||
file_write_ready(struct client_files *files, struct imsg *imsg)
|
||||
@ -790,7 +830,7 @@ file_read_data(struct client_files *files, struct imsg *imsg)
|
||||
return;
|
||||
|
||||
log_debug("file %d read %zu bytes", cf->stream, bsize);
|
||||
if (cf->error == 0) {
|
||||
if (cf->error == 0 && !cf->closed) {
|
||||
if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
|
||||
cf->error = ENOMEM;
|
||||
file_fire_done(cf);
|
||||
|
@ -33,6 +33,7 @@ struct format_range {
|
||||
|
||||
enum style_range_type type;
|
||||
u_int argument;
|
||||
char string[16];
|
||||
|
||||
TAILQ_ENTRY(format_range) entry;
|
||||
};
|
||||
@ -44,9 +45,18 @@ format_is_type(struct format_range *fr, struct style *sy)
|
||||
{
|
||||
if (fr->type != sy->range_type)
|
||||
return (0);
|
||||
if (fr->type == STYLE_RANGE_WINDOW &&
|
||||
fr->argument != sy->range_argument)
|
||||
return (0);
|
||||
switch (fr->type) {
|
||||
case STYLE_RANGE_NONE:
|
||||
case STYLE_RANGE_LEFT:
|
||||
case STYLE_RANGE_RIGHT:
|
||||
return (1);
|
||||
case STYLE_RANGE_PANE:
|
||||
case STYLE_RANGE_WINDOW:
|
||||
case STYLE_RANGE_SESSION:
|
||||
return (fr->argument == sy->range_argument);
|
||||
case STYLE_RANGE_USER:
|
||||
return (strcmp(fr->string, sy->range_string) == 0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
@ -709,7 +719,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
||||
int focus_start = -1, focus_end = -1;
|
||||
int list_state = -1, fill = -1, even;
|
||||
enum style_align list_align = STYLE_ALIGN_DEFAULT;
|
||||
struct grid_cell gc, current_default;
|
||||
struct grid_cell gc, current_default, base_default;
|
||||
struct style sy, saved_sy;
|
||||
struct utf8_data *ud = &sy.gc.data;
|
||||
const char *cp, *end;
|
||||
@ -719,7 +729,9 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
||||
struct format_ranges frs;
|
||||
struct style_range *sr;
|
||||
|
||||
memcpy(&base_default, base, sizeof base_default);
|
||||
memcpy(¤t_default, base, sizeof current_default);
|
||||
base = &base_default;
|
||||
style_set(&sy, ¤t_default);
|
||||
TAILQ_INIT(&frs);
|
||||
log_debug("%s: %s", __func__, expanded);
|
||||
@ -837,6 +849,12 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
||||
} else if (sy.default_type == STYLE_DEFAULT_POP) {
|
||||
memcpy(¤t_default, base, sizeof current_default);
|
||||
sy.default_type = STYLE_DEFAULT_BASE;
|
||||
} else if (sy.default_type == STYLE_DEFAULT_SET) {
|
||||
memcpy(&base_default, &saved_sy.gc,
|
||||
sizeof base_default);
|
||||
memcpy(¤t_default, &saved_sy.gc,
|
||||
sizeof current_default);
|
||||
sy.default_type = STYLE_DEFAULT_BASE;
|
||||
}
|
||||
|
||||
/* Check the list state. */
|
||||
@ -942,6 +960,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
||||
|
||||
fr->type = sy.range_type;
|
||||
fr->argument = sy.range_argument;
|
||||
strlcpy(fr->string, sy.range_string,
|
||||
sizeof fr->string);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1013,13 +1033,39 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
||||
sr = xcalloc(1, sizeof *sr);
|
||||
sr->type = fr->type;
|
||||
sr->argument = fr->argument;
|
||||
strlcpy(sr->string, fr->string, sizeof sr->string);
|
||||
sr->start = fr->start;
|
||||
sr->end = fr->end;
|
||||
TAILQ_INSERT_TAIL(srs, sr, entry);
|
||||
|
||||
log_debug("%s: range %d|%u at %u-%u", __func__, sr->type,
|
||||
sr->argument, sr->start, sr->end);
|
||||
|
||||
switch (sr->type) {
|
||||
case STYLE_RANGE_NONE:
|
||||
break;
|
||||
case STYLE_RANGE_LEFT:
|
||||
log_debug("%s: range left at %u-%u", __func__,
|
||||
sr->start, sr->end);
|
||||
break;
|
||||
case STYLE_RANGE_RIGHT:
|
||||
log_debug("%s: range right at %u-%u", __func__,
|
||||
sr->start, sr->end);
|
||||
break;
|
||||
case STYLE_RANGE_PANE:
|
||||
log_debug("%s: range pane|%%%u at %u-%u", __func__,
|
||||
sr->argument, sr->start, sr->end);
|
||||
break;
|
||||
case STYLE_RANGE_WINDOW:
|
||||
log_debug("%s: range window|%u at %u-%u", __func__,
|
||||
sr->argument, sr->start, sr->end);
|
||||
break;
|
||||
case STYLE_RANGE_SESSION:
|
||||
log_debug("%s: range session|$%u at %u-%u", __func__,
|
||||
sr->argument, sr->start, sr->end);
|
||||
break;
|
||||
case STYLE_RANGE_USER:
|
||||
log_debug("%s: range user|%u at %u-%u", __func__,
|
||||
sr->argument, sr->start, sr->end);
|
||||
break;
|
||||
}
|
||||
format_free_range(&frs, fr);
|
||||
}
|
||||
|
||||
@ -1083,7 +1129,7 @@ format_trim_left(const char *expanded, u_int limit)
|
||||
struct utf8_data ud;
|
||||
enum utf8_state more;
|
||||
|
||||
out = copy = xcalloc(1, strlen(expanded) + 1);
|
||||
out = copy = xcalloc(2, strlen(expanded) + 1);
|
||||
while (*cp != '\0') {
|
||||
if (width >= limit)
|
||||
break;
|
||||
@ -1150,7 +1196,7 @@ format_trim_right(const char *expanded, u_int limit)
|
||||
return (xstrdup(expanded));
|
||||
skip = total_width - limit;
|
||||
|
||||
out = copy = xcalloc(1, strlen(expanded) + 1);
|
||||
out = copy = xcalloc(2, strlen(expanded) + 1);
|
||||
while (*cp != '\0') {
|
||||
if (*cp == '#') {
|
||||
end = format_leading_hashes(cp, &n, &leading_width);
|
||||
|
@ -35,7 +35,7 @@ LLVMFuzzerTestOneInput(const u_char *data, size_t size)
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Since AFL doesn't support -max_len paramenter we have to
|
||||
* Since AFL doesn't support -max_len parameter we have to
|
||||
* discard long inputs manually.
|
||||
*/
|
||||
if (size > FUZZER_MAXLEN)
|
||||
|
@ -180,19 +180,14 @@ grid_reader_handle_wrap(struct grid_reader *gr, u_int *xx, u_int *yy)
|
||||
int
|
||||
grid_reader_in_set(struct grid_reader *gr, const char *set)
|
||||
{
|
||||
struct grid_cell gc;
|
||||
|
||||
grid_get_cell(gr->gd, gr->cx, gr->cy, &gc);
|
||||
if (gc.flags & GRID_FLAG_PADDING)
|
||||
return (0);
|
||||
return (utf8_cstrhas(set, &gc.data));
|
||||
return (grid_in_set(gr->gd, gr->cx, gr->cy, set));
|
||||
}
|
||||
|
||||
/* Move cursor to the start of the next word. */
|
||||
void
|
||||
grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators)
|
||||
{
|
||||
u_int xx, yy;
|
||||
u_int xx, yy, width;
|
||||
|
||||
/* Do not break up wrapped words. */
|
||||
if (grid_get_line(gr->gd, gr->cy)->flags & GRID_LINE_WRAPPED)
|
||||
@ -229,8 +224,8 @@ grid_reader_cursor_next_word(struct grid_reader *gr, const char *separators)
|
||||
}
|
||||
}
|
||||
while (grid_reader_handle_wrap(gr, &xx, &yy) &&
|
||||
grid_reader_in_set(gr, WHITESPACE))
|
||||
gr->cx++;
|
||||
(width = grid_reader_in_set(gr, WHITESPACE)))
|
||||
gr->cx += width;
|
||||
}
|
||||
|
||||
/* Move cursor to the end of the next word. */
|
||||
@ -338,6 +333,20 @@ grid_reader_cursor_previous_word(struct grid_reader *gr, const char *separators,
|
||||
gr->cy = oldy;
|
||||
}
|
||||
|
||||
/* Compare grid cell to UTF-8 data. Return 1 if equal, 0 if not. */
|
||||
static int
|
||||
grid_reader_cell_equals_data(const struct grid_cell *gc,
|
||||
const struct utf8_data *ud)
|
||||
{
|
||||
if (gc->flags & GRID_FLAG_PADDING)
|
||||
return (0);
|
||||
if (gc->flags & GRID_FLAG_TAB && ud->size == 1 && *ud->data == '\t')
|
||||
return (1);
|
||||
if (gc->data.size != ud->size)
|
||||
return (0);
|
||||
return (memcmp(gc->data.data, ud->data, gc->data.size) == 0);
|
||||
}
|
||||
|
||||
/* Jump forward to character. */
|
||||
int
|
||||
grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc)
|
||||
@ -352,9 +361,7 @@ grid_reader_cursor_jump(struct grid_reader *gr, const struct utf8_data *jc)
|
||||
xx = grid_line_length(gr->gd, py);
|
||||
while (px < xx) {
|
||||
grid_get_cell(gr->gd, px, py, &gc);
|
||||
if (!(gc.flags & GRID_FLAG_PADDING) &&
|
||||
gc.data.size == jc->size &&
|
||||
memcmp(gc.data.data, jc->data, gc.data.size) == 0) {
|
||||
if (grid_reader_cell_equals_data(&gc, jc)) {
|
||||
gr->cx = px;
|
||||
gr->cy = py;
|
||||
return (1);
|
||||
@ -382,9 +389,7 @@ grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc)
|
||||
for (py = gr->cy + 1; py > 0; py--) {
|
||||
for (px = xx; px > 0; px--) {
|
||||
grid_get_cell(gr->gd, px - 1, py - 1, &gc);
|
||||
if (!(gc.flags & GRID_FLAG_PADDING) &&
|
||||
gc.data.size == jc->size &&
|
||||
memcmp(gc.data.data, jc->data, gc.data.size) == 0) {
|
||||
if (grid_reader_cell_equals_data(&gc, jc)) {
|
||||
gr->cx = px - 1;
|
||||
gr->cy = py - 1;
|
||||
return (1);
|
||||
@ -415,7 +420,9 @@ grid_reader_cursor_back_to_indentation(struct grid_reader *gr)
|
||||
xx = grid_line_length(gr->gd, py);
|
||||
for (px = 0; px < xx; px++) {
|
||||
grid_get_cell(gr->gd, px, py, &gc);
|
||||
if (gc.data.size != 1 || *gc.data.data != ' ') {
|
||||
if ((gc.data.size != 1 || *gc.data.data != ' ') &&
|
||||
~gc.flags & GRID_FLAG_TAB &&
|
||||
~gc.flags & GRID_FLAG_PADDING) {
|
||||
gr->cx = px;
|
||||
gr->cy = py;
|
||||
return;
|
||||
|
@ -231,5 +231,5 @@ grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx)
|
||||
px = grid_view_x(gd, px);
|
||||
py = grid_view_y(gd, py);
|
||||
|
||||
return (grid_string_cells(gd, px, py, nx, NULL, 0, 0, 0));
|
||||
return (grid_string_cells(gd, px, py, nx, NULL, 0, NULL));
|
||||
}
|
||||
|
228
grid.c
228
grid.c
@ -37,7 +37,7 @@
|
||||
|
||||
/* Default grid cell data. */
|
||||
const struct grid_cell grid_default_cell = {
|
||||
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0
|
||||
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 8, 0
|
||||
};
|
||||
|
||||
/*
|
||||
@ -45,15 +45,15 @@ const struct grid_cell grid_default_cell = {
|
||||
* appears in the grid - because of this, they are always extended cells.
|
||||
*/
|
||||
static const struct grid_cell grid_padding_cell = {
|
||||
{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0
|
||||
{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0
|
||||
};
|
||||
|
||||
/* Cleared grid cell data. */
|
||||
static const struct grid_cell grid_cleared_cell = {
|
||||
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0
|
||||
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0
|
||||
};
|
||||
static const struct grid_cell_entry grid_cleared_entry = {
|
||||
GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } }
|
||||
{ .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
|
||||
};
|
||||
|
||||
/* Store cell in entry. */
|
||||
@ -84,11 +84,15 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
|
||||
return (1);
|
||||
if (gc->attr > 0xff)
|
||||
return (1);
|
||||
if (gc->data.size != 1 || gc->data.width != 1)
|
||||
if (gc->data.size > 1 || gc->data.width > 1)
|
||||
return (1);
|
||||
if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB))
|
||||
return (1);
|
||||
if (gc->us != 0) /* only supports 256 or RGB */
|
||||
if (gc->us != 8) /* only supports 256 or RGB */
|
||||
return (1);
|
||||
if (gc->link != 0)
|
||||
return (1);
|
||||
if (gc->flags & GRID_FLAG_TAB)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
@ -122,7 +126,10 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
|
||||
fatalx("offset too big");
|
||||
gl->flags |= GRID_LINE_EXTENDED;
|
||||
|
||||
utf8_from_data(&gc->data, &uc);
|
||||
if (gc->flags & GRID_FLAG_TAB)
|
||||
uc = gc->data.width;
|
||||
else
|
||||
utf8_from_data(&gc->data, &uc);
|
||||
|
||||
gee = &gl->extddata[gce->offset];
|
||||
gee->data = uc;
|
||||
@ -131,6 +138,7 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
|
||||
gee->fg = gc->fg;
|
||||
gee->bg = gc->bg;
|
||||
gee->us = gc->us;
|
||||
gee->link = gc->link;
|
||||
return (gee);
|
||||
}
|
||||
|
||||
@ -227,9 +235,15 @@ grid_check_y(struct grid *gd, const char *from, u_int py)
|
||||
int
|
||||
grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
|
||||
{
|
||||
int flags1 = gc1->flags, flags2 = gc2->flags;;
|
||||
|
||||
if (gc1->fg != gc2->fg || gc1->bg != gc2->bg)
|
||||
return (0);
|
||||
if (gc1->attr != gc2->attr || gc1->flags != gc2->flags)
|
||||
if (gc1->attr != gc2->attr)
|
||||
return (0);
|
||||
if ((flags1 & ~GRID_FLAG_CLEARED) != (flags2 & ~GRID_FLAG_CLEARED))
|
||||
return (0);
|
||||
if (gc1->link != gc2->link)
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
@ -247,6 +261,16 @@ grid_cells_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
|
||||
return (memcmp(gc1->data.data, gc2->data.data, gc1->data.size) == 0);
|
||||
}
|
||||
|
||||
/* Set grid cell to a tab. */
|
||||
void
|
||||
grid_set_tab(struct grid_cell *gc, u_int width)
|
||||
{
|
||||
memset(gc->data.data, 0, sizeof gc->data.data);
|
||||
gc->flags |= GRID_FLAG_TAB;
|
||||
gc->data.width = gc->data.size = gc->data.have = width;
|
||||
memset(gc->data.data, ' ', gc->data.size);
|
||||
}
|
||||
|
||||
/* Free one line. */
|
||||
static void
|
||||
grid_free_line(struct grid *gd, u_int py)
|
||||
@ -399,6 +423,7 @@ grid_scroll_history(struct grid *gd, u_int bg)
|
||||
|
||||
gd->hscrolled++;
|
||||
grid_compact_line(&gd->linedata[gd->hsize]);
|
||||
gd->linedata[gd->hsize].time = current_time;
|
||||
gd->hsize++;
|
||||
}
|
||||
|
||||
@ -438,6 +463,7 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
|
||||
|
||||
/* Move the line into the history. */
|
||||
memcpy(gl_history, gl_upper, sizeof *gl_history);
|
||||
gl_history->time = current_time;
|
||||
|
||||
/* Then move the region up and clear the bottom line. */
|
||||
memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
|
||||
@ -507,7 +533,12 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
|
||||
gc->fg = gee->fg;
|
||||
gc->bg = gee->bg;
|
||||
gc->us = gee->us;
|
||||
utf8_to_data(gee->data, &gc->data);
|
||||
gc->link = gee->link;
|
||||
|
||||
if (gc->flags & GRID_FLAG_TAB)
|
||||
grid_set_tab(gc, gee->data);
|
||||
else
|
||||
utf8_to_data(gee->data, &gc->data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -520,8 +551,9 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
|
||||
gc->bg = gce->data.bg;
|
||||
if (gce->flags & GRID_FLAG_BG256)
|
||||
gc->bg |= COLOUR_FLAG_256;
|
||||
gc->us = 0;
|
||||
gc->us = 8;
|
||||
utf8_set(&gc->data, gce->data.data);
|
||||
gc->link = 0;
|
||||
}
|
||||
|
||||
/* Get cell for reading. */
|
||||
@ -852,28 +884,60 @@ grid_string_cells_us(const struct grid_cell *gc, int *values)
|
||||
/* Add on SGR code. */
|
||||
static void
|
||||
grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
|
||||
int *oldc, size_t nnewc, size_t noldc, int escape_c0)
|
||||
int *oldc, size_t nnewc, size_t noldc, int flags)
|
||||
{
|
||||
u_int i;
|
||||
char tmp[64];
|
||||
int reset = (n != 0 && s[0] == 0);
|
||||
|
||||
if (nnewc != 0 &&
|
||||
(nnewc != noldc ||
|
||||
memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 ||
|
||||
(n != 0 && s[0] == 0))) {
|
||||
if (escape_c0)
|
||||
strlcat(buf, "\\033[", len);
|
||||
if (nnewc == 0)
|
||||
return; /* no code to add */
|
||||
if (!reset &&
|
||||
nnewc == noldc &&
|
||||
memcmp(newc, oldc, nnewc * sizeof newc[0]) == 0)
|
||||
return; /* no reset and colour unchanged */
|
||||
if (reset && (newc[0] == 49 || newc[0] == 39))
|
||||
return; /* reset and colour default */
|
||||
|
||||
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
|
||||
strlcat(buf, "\\033[", len);
|
||||
else
|
||||
strlcat(buf, "\033[", len);
|
||||
for (i = 0; i < nnewc; i++) {
|
||||
if (i + 1 < nnewc)
|
||||
xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
|
||||
else
|
||||
strlcat(buf, "\033[", len);
|
||||
for (i = 0; i < nnewc; i++) {
|
||||
if (i + 1 < nnewc)
|
||||
xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
|
||||
else
|
||||
xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
|
||||
strlcat(buf, tmp, len);
|
||||
}
|
||||
strlcat(buf, "m", len);
|
||||
xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
|
||||
strlcat(buf, tmp, len);
|
||||
}
|
||||
strlcat(buf, "m", len);
|
||||
}
|
||||
|
||||
static int
|
||||
grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id,
|
||||
const char *uri, int flags)
|
||||
{
|
||||
char *tmp;
|
||||
|
||||
if (strlen(uri) + strlen(id) + 17 >= len)
|
||||
return (0);
|
||||
|
||||
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
|
||||
strlcat(buf, "\\033]8;", len);
|
||||
else
|
||||
strlcat(buf, "\033]8;", len);
|
||||
if (*id != '\0') {
|
||||
xasprintf(&tmp, "id=%s;", id);
|
||||
strlcat(buf, tmp, len);
|
||||
free(tmp);
|
||||
} else
|
||||
strlcat(buf, ";", len);
|
||||
strlcat(buf, uri, len);
|
||||
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
|
||||
strlcat(buf, "\\033\\\\", len);
|
||||
else
|
||||
strlcat(buf, "\033\\", len);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -882,14 +946,16 @@ grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
|
||||
*/
|
||||
static void
|
||||
grid_string_cells_code(const struct grid_cell *lastgc,
|
||||
const struct grid_cell *gc, char *buf, size_t len, int escape_c0)
|
||||
const struct grid_cell *gc, char *buf, size_t len, int flags,
|
||||
struct screen *sc, int *has_link)
|
||||
{
|
||||
int oldc[64], newc[64], s[128];
|
||||
size_t noldc, nnewc, n, i;
|
||||
u_int attr = gc->attr, lastattr = lastgc->attr;
|
||||
char tmp[64];
|
||||
int oldc[64], newc[64], s[128];
|
||||
size_t noldc, nnewc, n, i;
|
||||
u_int attr = gc->attr, lastattr = lastgc->attr;
|
||||
char tmp[64];
|
||||
const char *uri, *id;
|
||||
|
||||
struct {
|
||||
static const struct {
|
||||
u_int mask;
|
||||
u_int code;
|
||||
} attrs[] = {
|
||||
@ -913,7 +979,7 @@ grid_string_cells_code(const struct grid_cell *lastgc,
|
||||
for (i = 0; i < nitems(attrs); i++) {
|
||||
if (((~attr & attrs[i].mask) &&
|
||||
(lastattr & attrs[i].mask)) ||
|
||||
(lastgc->us != 0 && gc->us == 0)) {
|
||||
(lastgc->us != 8 && gc->us == 8)) {
|
||||
s[n++] = 0;
|
||||
lastattr &= GRID_ATTR_CHARSET;
|
||||
break;
|
||||
@ -928,7 +994,7 @@ grid_string_cells_code(const struct grid_cell *lastgc,
|
||||
/* Write the attributes. */
|
||||
*buf = '\0';
|
||||
if (n > 0) {
|
||||
if (escape_c0)
|
||||
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
|
||||
strlcat(buf, "\\033[", len);
|
||||
else
|
||||
strlcat(buf, "\033[", len);
|
||||
@ -950,46 +1016,59 @@ grid_string_cells_code(const struct grid_cell *lastgc,
|
||||
nnewc = grid_string_cells_fg(gc, newc);
|
||||
noldc = grid_string_cells_fg(lastgc, oldc);
|
||||
grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
|
||||
escape_c0);
|
||||
flags);
|
||||
|
||||
/* If the background colour changed, append its parameters. */
|
||||
nnewc = grid_string_cells_bg(gc, newc);
|
||||
noldc = grid_string_cells_bg(lastgc, oldc);
|
||||
grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
|
||||
escape_c0);
|
||||
flags);
|
||||
|
||||
/* If the underscore colour changed, append its parameters. */
|
||||
nnewc = grid_string_cells_us(gc, newc);
|
||||
noldc = grid_string_cells_us(lastgc, oldc);
|
||||
grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
|
||||
escape_c0);
|
||||
flags);
|
||||
|
||||
/* Append shift in/shift out if needed. */
|
||||
if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) {
|
||||
if (escape_c0)
|
||||
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
|
||||
strlcat(buf, "\\016", len); /* SO */
|
||||
else
|
||||
strlcat(buf, "\016", len); /* SO */
|
||||
}
|
||||
if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) {
|
||||
if (escape_c0)
|
||||
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
|
||||
strlcat(buf, "\\017", len); /* SI */
|
||||
else
|
||||
strlcat(buf, "\017", len); /* SI */
|
||||
}
|
||||
|
||||
/* Add hyperlink if changed. */
|
||||
if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) {
|
||||
if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) {
|
||||
*has_link = grid_string_cells_add_hyperlink(buf, len,
|
||||
id, uri, flags);
|
||||
} else if (*has_link) {
|
||||
grid_string_cells_add_hyperlink(buf, len, "", "",
|
||||
flags);
|
||||
*has_link = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert cells into a string. */
|
||||
char *
|
||||
grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
|
||||
struct grid_cell **lastgc, int with_codes, int escape_c0, int trim)
|
||||
struct grid_cell **lastgc, int flags, struct screen *s)
|
||||
{
|
||||
struct grid_cell gc;
|
||||
static struct grid_cell lastgc1;
|
||||
const char *data;
|
||||
char *buf, code[128];
|
||||
char *buf, code[8192];
|
||||
size_t len, off, size, codelen;
|
||||
u_int xx;
|
||||
u_int xx, end;
|
||||
int has_link = 0;
|
||||
const struct grid_line *gl;
|
||||
|
||||
if (lastgc != NULL && *lastgc == NULL) {
|
||||
@ -1002,26 +1081,37 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
|
||||
off = 0;
|
||||
|
||||
gl = grid_peek_line(gd, py);
|
||||
if (flags & GRID_STRING_EMPTY_CELLS)
|
||||
end = gl->cellsize;
|
||||
else
|
||||
end = gl->cellused;
|
||||
for (xx = px; xx < px + nx; xx++) {
|
||||
if (gl == NULL || xx >= gl->cellused)
|
||||
if (gl == NULL || xx >= end)
|
||||
break;
|
||||
grid_get_cell(gd, xx, py, &gc);
|
||||
if (gc.flags & GRID_FLAG_PADDING)
|
||||
continue;
|
||||
|
||||
if (with_codes) {
|
||||
if (flags & GRID_STRING_WITH_SEQUENCES) {
|
||||
grid_string_cells_code(*lastgc, &gc, code, sizeof code,
|
||||
escape_c0);
|
||||
flags, s, &has_link);
|
||||
codelen = strlen(code);
|
||||
memcpy(*lastgc, &gc, sizeof **lastgc);
|
||||
} else
|
||||
codelen = 0;
|
||||
|
||||
data = gc.data.data;
|
||||
size = gc.data.size;
|
||||
if (escape_c0 && size == 1 && *data == '\\') {
|
||||
data = "\\\\";
|
||||
size = 2;
|
||||
if (gc.flags & GRID_FLAG_TAB) {
|
||||
data = "\t";
|
||||
size = 1;
|
||||
} else {
|
||||
data = gc.data.data;
|
||||
size = gc.data.size;
|
||||
if ((flags & GRID_STRING_ESCAPE_SEQUENCES) &&
|
||||
size == 1 &&
|
||||
*data == '\\') {
|
||||
data = "\\\\";
|
||||
size = 2;
|
||||
}
|
||||
}
|
||||
|
||||
while (len < off + size + codelen + 1) {
|
||||
@ -1037,7 +1127,19 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
|
||||
off += size;
|
||||
}
|
||||
|
||||
if (trim) {
|
||||
if (has_link) {
|
||||
grid_string_cells_add_hyperlink(code, sizeof code, "", "",
|
||||
flags);
|
||||
codelen = strlen(code);
|
||||
while (len < off + size + codelen + 1) {
|
||||
buf = xreallocarray(buf, 2, len);
|
||||
len *= 2;
|
||||
}
|
||||
memcpy(buf + off, code, codelen);
|
||||
off += codelen;
|
||||
}
|
||||
|
||||
if (flags & GRID_STRING_TRIM_SPACES) {
|
||||
while (off > 0 && buf[off - 1] == ' ')
|
||||
off--;
|
||||
}
|
||||
@ -1459,3 +1561,27 @@ grid_line_length(struct grid *gd, u_int py)
|
||||
}
|
||||
return (px);
|
||||
}
|
||||
|
||||
/* Check if character is in set. */
|
||||
int
|
||||
grid_in_set(struct grid *gd, u_int px, u_int py, const char *set)
|
||||
{
|
||||
struct grid_cell gc, tmp_gc;
|
||||
u_int pxx;
|
||||
|
||||
grid_get_cell(gd, px, py, &gc);
|
||||
if (strchr(set, '\t')) {
|
||||
if (gc.flags & GRID_FLAG_PADDING) {
|
||||
pxx = px;
|
||||
do
|
||||
grid_get_cell(gd, --pxx, py, &tmp_gc);
|
||||
while (pxx > 0 && tmp_gc.flags & GRID_FLAG_PADDING);
|
||||
if (tmp_gc.flags & GRID_FLAG_TAB)
|
||||
return (tmp_gc.data.width - (px - pxx));
|
||||
} else if (gc.flags & GRID_FLAG_TAB)
|
||||
return (gc.data.width);
|
||||
}
|
||||
if (gc.flags & GRID_FLAG_PADDING)
|
||||
return (0);
|
||||
return (utf8_cstrhas(set, &gc.data));
|
||||
}
|
||||
|
239
hyperlinks.c
Normal file
239
hyperlinks.c
Normal file
@ -0,0 +1,239 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2021 Will <author@will.party>
|
||||
* Copyright (c) 2022 Jeff Chiang <pobomp@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
/*
|
||||
* OSC 8 hyperlinks, described at:
|
||||
*
|
||||
* https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
|
||||
*
|
||||
* Each hyperlink and ID combination is assigned a number ("inner" in this
|
||||
* file) which is stored in an extended grid cell and maps into a tree here.
|
||||
*
|
||||
* Each URI has one inner number and one external ID (which tmux uses to send
|
||||
* the hyperlink to the terminal) and one internal ID (which is received from
|
||||
* the sending application inside tmux).
|
||||
*
|
||||
* Anonymous hyperlinks are each unique and are not reused even if they have
|
||||
* the same URI (terminals will not want to tie them together).
|
||||
*/
|
||||
|
||||
#define MAX_HYPERLINKS 5000
|
||||
|
||||
static long long hyperlinks_next_external_id = 1;
|
||||
static u_int global_hyperlinks_count;
|
||||
|
||||
struct hyperlinks_uri {
|
||||
struct hyperlinks *tree;
|
||||
|
||||
u_int inner;
|
||||
const char *internal_id;
|
||||
const char *external_id;
|
||||
const char *uri;
|
||||
|
||||
TAILQ_ENTRY(hyperlinks_uri) list_entry;
|
||||
RB_ENTRY(hyperlinks_uri) by_inner_entry;
|
||||
RB_ENTRY(hyperlinks_uri) by_uri_entry; /* by internal ID and URI */
|
||||
};
|
||||
RB_HEAD(hyperlinks_by_uri_tree, hyperlinks_uri);
|
||||
RB_HEAD(hyperlinks_by_inner_tree, hyperlinks_uri);
|
||||
|
||||
TAILQ_HEAD(hyperlinks_list, hyperlinks_uri);
|
||||
static struct hyperlinks_list global_hyperlinks =
|
||||
TAILQ_HEAD_INITIALIZER(global_hyperlinks);
|
||||
|
||||
struct hyperlinks {
|
||||
u_int next_inner;
|
||||
struct hyperlinks_by_inner_tree by_inner;
|
||||
struct hyperlinks_by_uri_tree by_uri;
|
||||
u_int references;
|
||||
};
|
||||
|
||||
static int
|
||||
hyperlinks_by_uri_cmp(struct hyperlinks_uri *left, struct hyperlinks_uri *right)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (*left->internal_id == '\0' || *right->internal_id == '\0') {
|
||||
/*
|
||||
* If both URIs are anonymous, use the inner for comparison so
|
||||
* that they do not match even if the URI is the same - each
|
||||
* anonymous URI should be unique.
|
||||
*/
|
||||
if (*left->internal_id != '\0')
|
||||
return (-1);
|
||||
if (*right->internal_id != '\0')
|
||||
return (1);
|
||||
return (left->inner - right->inner);
|
||||
}
|
||||
|
||||
r = strcmp(left->internal_id, right->internal_id);
|
||||
if (r != 0)
|
||||
return (r);
|
||||
return (strcmp(left->uri, right->uri));
|
||||
}
|
||||
RB_PROTOTYPE_STATIC(hyperlinks_by_uri_tree, hyperlinks_uri, by_uri_entry,
|
||||
hyperlinks_by_uri_cmp);
|
||||
RB_GENERATE_STATIC(hyperlinks_by_uri_tree, hyperlinks_uri, by_uri_entry,
|
||||
hyperlinks_by_uri_cmp);
|
||||
|
||||
static int
|
||||
hyperlinks_by_inner_cmp(struct hyperlinks_uri *left,
|
||||
struct hyperlinks_uri *right)
|
||||
{
|
||||
return (left->inner - right->inner);
|
||||
}
|
||||
RB_PROTOTYPE_STATIC(hyperlinks_by_inner_tree, hyperlinks_uri, by_inner_entry,
|
||||
hyperlinks_by_inner_cmp);
|
||||
RB_GENERATE_STATIC(hyperlinks_by_inner_tree, hyperlinks_uri, by_inner_entry,
|
||||
hyperlinks_by_inner_cmp);
|
||||
|
||||
/* Remove a hyperlink. */
|
||||
static void
|
||||
hyperlinks_remove(struct hyperlinks_uri *hlu)
|
||||
{
|
||||
struct hyperlinks *hl = hlu->tree;
|
||||
|
||||
TAILQ_REMOVE(&global_hyperlinks, hlu, list_entry);
|
||||
global_hyperlinks_count--;
|
||||
|
||||
RB_REMOVE(hyperlinks_by_inner_tree, &hl->by_inner, hlu);
|
||||
RB_REMOVE(hyperlinks_by_uri_tree, &hl->by_uri, hlu);
|
||||
|
||||
free((void *)hlu->internal_id);
|
||||
free((void *)hlu->external_id);
|
||||
free((void *)hlu->uri);
|
||||
free(hlu);
|
||||
}
|
||||
|
||||
/* Store a new hyperlink or return if it already exists. */
|
||||
u_int
|
||||
hyperlinks_put(struct hyperlinks *hl, const char *uri_in,
|
||||
const char *internal_id_in)
|
||||
{
|
||||
struct hyperlinks_uri find, *hlu;
|
||||
char *uri, *internal_id, *external_id;
|
||||
|
||||
/*
|
||||
* Anonymous URI are stored with an empty internal ID and the tree
|
||||
* comparator will make sure they never match each other (so each
|
||||
* anonymous URI is unique).
|
||||
*/
|
||||
if (internal_id_in == NULL)
|
||||
internal_id_in = "";
|
||||
|
||||
utf8_stravis(&uri, uri_in, VIS_OCTAL|VIS_CSTYLE);
|
||||
utf8_stravis(&internal_id, internal_id_in, VIS_OCTAL|VIS_CSTYLE);
|
||||
|
||||
if (*internal_id_in != '\0') {
|
||||
find.uri = uri;
|
||||
find.internal_id = internal_id;
|
||||
|
||||
hlu = RB_FIND(hyperlinks_by_uri_tree, &hl->by_uri, &find);
|
||||
if (hlu != NULL) {
|
||||
free (uri);
|
||||
free (internal_id);
|
||||
return (hlu->inner);
|
||||
}
|
||||
}
|
||||
xasprintf(&external_id, "tmux%llX", hyperlinks_next_external_id++);
|
||||
|
||||
hlu = xcalloc(1, sizeof *hlu);
|
||||
hlu->inner = hl->next_inner++;
|
||||
hlu->internal_id = internal_id;
|
||||
hlu->external_id = external_id;
|
||||
hlu->uri = uri;
|
||||
hlu->tree = hl;
|
||||
RB_INSERT(hyperlinks_by_uri_tree, &hl->by_uri, hlu);
|
||||
RB_INSERT(hyperlinks_by_inner_tree, &hl->by_inner, hlu);
|
||||
|
||||
TAILQ_INSERT_TAIL(&global_hyperlinks, hlu, list_entry);
|
||||
if (++global_hyperlinks_count == MAX_HYPERLINKS)
|
||||
hyperlinks_remove(TAILQ_FIRST(&global_hyperlinks));
|
||||
|
||||
return (hlu->inner);
|
||||
}
|
||||
|
||||
/* Get hyperlink by inner number. */
|
||||
int
|
||||
hyperlinks_get(struct hyperlinks *hl, u_int inner, const char **uri_out,
|
||||
const char **internal_id_out, const char **external_id_out)
|
||||
{
|
||||
struct hyperlinks_uri find, *hlu;
|
||||
|
||||
find.inner = inner;
|
||||
|
||||
hlu = RB_FIND(hyperlinks_by_inner_tree, &hl->by_inner, &find);
|
||||
if (hlu == NULL)
|
||||
return (0);
|
||||
if (internal_id_out != NULL)
|
||||
*internal_id_out = hlu->internal_id;
|
||||
if (external_id_out != NULL)
|
||||
*external_id_out = hlu->external_id;
|
||||
*uri_out = hlu->uri;
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Initialize hyperlink set. */
|
||||
struct hyperlinks *
|
||||
hyperlinks_init(void)
|
||||
{
|
||||
struct hyperlinks *hl;
|
||||
|
||||
hl = xcalloc(1, sizeof *hl);
|
||||
hl->next_inner = 1;
|
||||
RB_INIT(&hl->by_uri);
|
||||
RB_INIT(&hl->by_inner);
|
||||
hl->references = 1;
|
||||
return (hl);
|
||||
}
|
||||
|
||||
/* Copy hyperlink set. */
|
||||
struct hyperlinks *
|
||||
hyperlinks_copy(struct hyperlinks *hl)
|
||||
{
|
||||
hl->references++;
|
||||
return (hl);
|
||||
}
|
||||
|
||||
/* Free all hyperlinks but not the set itself. */
|
||||
void
|
||||
hyperlinks_reset(struct hyperlinks *hl)
|
||||
{
|
||||
struct hyperlinks_uri *hlu, *hlu1;
|
||||
|
||||
RB_FOREACH_SAFE(hlu, hyperlinks_by_inner_tree, &hl->by_inner, hlu1)
|
||||
hyperlinks_remove(hlu);
|
||||
}
|
||||
|
||||
/* Free hyperlink set. */
|
||||
void
|
||||
hyperlinks_free(struct hyperlinks *hl)
|
||||
{
|
||||
if (--hl->references == 0) {
|
||||
hyperlinks_reset(hl);
|
||||
free(hl);
|
||||
}
|
||||
}
|
679
image-sixel.c
Normal file
679
image-sixel.c
Normal file
@ -0,0 +1,679 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
#define SIXEL_WIDTH_LIMIT 10000
|
||||
#define SIXEL_HEIGHT_LIMIT 10000
|
||||
|
||||
struct sixel_line {
|
||||
u_int x;
|
||||
uint16_t *data;
|
||||
};
|
||||
|
||||
struct sixel_image {
|
||||
u_int x;
|
||||
u_int y;
|
||||
u_int xpixel;
|
||||
u_int ypixel;
|
||||
|
||||
u_int set_ra;
|
||||
u_int ra_x;
|
||||
u_int ra_y;
|
||||
|
||||
u_int *colours;
|
||||
u_int ncolours;
|
||||
u_int p2;
|
||||
|
||||
u_int dx;
|
||||
u_int dy;
|
||||
u_int dc;
|
||||
|
||||
struct sixel_line *lines;
|
||||
};
|
||||
|
||||
struct sixel_chunk {
|
||||
u_int next_x;
|
||||
u_int next_y;
|
||||
|
||||
u_int count;
|
||||
char pattern;
|
||||
char next_pattern;
|
||||
|
||||
size_t len;
|
||||
size_t used;
|
||||
char *data;
|
||||
};
|
||||
|
||||
static int
|
||||
sixel_parse_expand_lines(struct sixel_image *si, u_int y)
|
||||
{
|
||||
if (y <= si->y)
|
||||
return (0);
|
||||
if (y > SIXEL_HEIGHT_LIMIT)
|
||||
return (1);
|
||||
si->lines = xrecallocarray(si->lines, si->y, y, sizeof *si->lines);
|
||||
si->y = y;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
sixel_parse_expand_line(struct sixel_image *si, struct sixel_line *sl, u_int x)
|
||||
{
|
||||
if (x <= sl->x)
|
||||
return (0);
|
||||
if (x > SIXEL_WIDTH_LIMIT)
|
||||
return (1);
|
||||
if (x > si->x)
|
||||
si->x = x;
|
||||
sl->data = xrecallocarray(sl->data, sl->x, si->x, sizeof *sl->data);
|
||||
sl->x = si->x;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static u_int
|
||||
sixel_get_pixel(struct sixel_image *si, u_int x, u_int y)
|
||||
{
|
||||
struct sixel_line *sl;
|
||||
|
||||
if (y >= si->y)
|
||||
return (0);
|
||||
sl = &si->lines[y];
|
||||
if (x >= sl->x)
|
||||
return (0);
|
||||
return (sl->data[x]);
|
||||
}
|
||||
|
||||
static int
|
||||
sixel_set_pixel(struct sixel_image *si, u_int x, u_int y, u_int c)
|
||||
{
|
||||
struct sixel_line *sl;
|
||||
|
||||
if (sixel_parse_expand_lines(si, y + 1) != 0)
|
||||
return (1);
|
||||
sl = &si->lines[y];
|
||||
if (sixel_parse_expand_line(si, sl, x + 1) != 0)
|
||||
return (1);
|
||||
sl->data[x] = c;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
sixel_parse_write(struct sixel_image *si, u_int ch)
|
||||
{
|
||||
struct sixel_line *sl;
|
||||
u_int i;
|
||||
|
||||
if (sixel_parse_expand_lines(si, si->dy + 6) != 0)
|
||||
return (1);
|
||||
sl = &si->lines[si->dy];
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (sixel_parse_expand_line(si, sl, si->dx + 1) != 0)
|
||||
return (1);
|
||||
if (ch & (1 << i))
|
||||
sl->data[si->dx] = si->dc;
|
||||
sl++;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static const char *
|
||||
sixel_parse_attributes(struct sixel_image *si, const char *cp, const char *end)
|
||||
{
|
||||
const char *last;
|
||||
char *endptr;
|
||||
u_int x, y;
|
||||
|
||||
last = cp;
|
||||
while (last != end) {
|
||||
if (*last != ';' && (*last < '0' || *last > '9'))
|
||||
break;
|
||||
last++;
|
||||
}
|
||||
strtoul(cp, &endptr, 10);
|
||||
if (endptr == last || *endptr != ';')
|
||||
return (last);
|
||||
strtoul(endptr + 1, &endptr, 10);
|
||||
if (endptr == last)
|
||||
return (last);
|
||||
if (*endptr != ';') {
|
||||
log_debug("%s: missing ;", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
x = strtoul(endptr + 1, &endptr, 10);
|
||||
if (endptr == last || *endptr != ';') {
|
||||
log_debug("%s: missing ;", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
if (x > SIXEL_WIDTH_LIMIT) {
|
||||
log_debug("%s: image is too wide", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
y = strtoul(endptr + 1, &endptr, 10);
|
||||
if (endptr != last) {
|
||||
log_debug("%s: extra ;", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
if (y > SIXEL_HEIGHT_LIMIT) {
|
||||
log_debug("%s: image is too tall", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
si->x = x;
|
||||
sixel_parse_expand_lines(si, y);
|
||||
|
||||
si->set_ra = 1;
|
||||
si->ra_x = x;
|
||||
si->ra_y = y;
|
||||
|
||||
return (last);
|
||||
}
|
||||
|
||||
static const char *
|
||||
sixel_parse_colour(struct sixel_image *si, const char *cp, const char *end)
|
||||
{
|
||||
const char *last;
|
||||
char *endptr;
|
||||
u_int c, type, r, g, b;
|
||||
|
||||
last = cp;
|
||||
while (last != end) {
|
||||
if (*last != ';' && (*last < '0' || *last > '9'))
|
||||
break;
|
||||
last++;
|
||||
}
|
||||
|
||||
c = strtoul(cp, &endptr, 10);
|
||||
if (c > SIXEL_COLOUR_REGISTERS) {
|
||||
log_debug("%s: too many colours", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
si->dc = c + 1;
|
||||
if (endptr == last || *endptr != ';')
|
||||
return (last);
|
||||
|
||||
type = strtoul(endptr + 1, &endptr, 10);
|
||||
if (endptr == last || *endptr != ';') {
|
||||
log_debug("%s: missing ;", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
r = strtoul(endptr + 1, &endptr, 10);
|
||||
if (endptr == last || *endptr != ';') {
|
||||
log_debug("%s: missing ;", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
g = strtoul(endptr + 1, &endptr, 10);
|
||||
if (endptr == last || *endptr != ';') {
|
||||
log_debug("%s: missing ;", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
b = strtoul(endptr + 1, &endptr, 10);
|
||||
if (endptr != last) {
|
||||
log_debug("%s: missing ;", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (type != 1 && type != 2) {
|
||||
log_debug("%s: invalid type %d", __func__, type);
|
||||
return (NULL);
|
||||
}
|
||||
if (c + 1 > si->ncolours) {
|
||||
si->colours = xrecallocarray(si->colours, si->ncolours, c + 1,
|
||||
sizeof *si->colours);
|
||||
si->ncolours = c + 1;
|
||||
}
|
||||
si->colours[c] = (type << 24) | (r << 16) | (g << 8) | b;
|
||||
return (last);
|
||||
}
|
||||
|
||||
static const char *
|
||||
sixel_parse_repeat(struct sixel_image *si, const char *cp, const char *end)
|
||||
{
|
||||
const char *last;
|
||||
char tmp[32], ch;
|
||||
u_int n = 0, i;
|
||||
const char *errstr = NULL;
|
||||
|
||||
last = cp;
|
||||
while (last != end) {
|
||||
if (*last < '0' || *last > '9')
|
||||
break;
|
||||
tmp[n++] = *last++;
|
||||
if (n == (sizeof tmp) - 1) {
|
||||
log_debug("%s: repeat not terminated", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
if (n == 0 || last == end) {
|
||||
log_debug("%s: repeat not terminated", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
tmp[n] = '\0';
|
||||
|
||||
n = strtonum(tmp, 1, SIXEL_WIDTH_LIMIT, &errstr);
|
||||
if (n == 0 || errstr != NULL) {
|
||||
log_debug("%s: repeat too wide", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
ch = (*last++) - 0x3f;
|
||||
for (i = 0; i < n; i++) {
|
||||
if (sixel_parse_write(si, ch) != 0) {
|
||||
log_debug("%s: width limit reached", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
si->dx++;
|
||||
}
|
||||
return (last);
|
||||
}
|
||||
|
||||
struct sixel_image *
|
||||
sixel_parse(const char *buf, size_t len, u_int p2, u_int xpixel, u_int ypixel)
|
||||
{
|
||||
struct sixel_image *si;
|
||||
const char *cp = buf, *end = buf + len;
|
||||
char ch;
|
||||
|
||||
if (len == 0 || len == 1 || *cp++ != 'q') {
|
||||
log_debug("%s: empty image", __func__);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
si = xcalloc (1, sizeof *si);
|
||||
si->xpixel = xpixel;
|
||||
si->ypixel = ypixel;
|
||||
si->p2 = p2;
|
||||
|
||||
while (cp != end) {
|
||||
ch = *cp++;
|
||||
switch (ch) {
|
||||
case '"':
|
||||
cp = sixel_parse_attributes(si, cp, end);
|
||||
if (cp == NULL)
|
||||
goto bad;
|
||||
break;
|
||||
case '#':
|
||||
cp = sixel_parse_colour(si, cp, end);
|
||||
if (cp == NULL)
|
||||
goto bad;
|
||||
break;
|
||||
case '!':
|
||||
cp = sixel_parse_repeat(si, cp, end);
|
||||
if (cp == NULL)
|
||||
goto bad;
|
||||
break;
|
||||
case '-':
|
||||
si->dx = 0;
|
||||
si->dy += 6;
|
||||
break;
|
||||
case '$':
|
||||
si->dx = 0;
|
||||
break;
|
||||
default:
|
||||
if (ch < 0x20)
|
||||
break;
|
||||
if (ch < 0x3f || ch > 0x7e)
|
||||
goto bad;
|
||||
if (sixel_parse_write(si, ch - 0x3f) != 0) {
|
||||
log_debug("%s: width limit reached", __func__);
|
||||
goto bad;
|
||||
}
|
||||
si->dx++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (si->x == 0 || si->y == 0)
|
||||
goto bad;
|
||||
return (si);
|
||||
|
||||
bad:
|
||||
free(si);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void
|
||||
sixel_free(struct sixel_image *si)
|
||||
{
|
||||
u_int y;
|
||||
|
||||
for (y = 0; y < si->y; y++)
|
||||
free(si->lines[y].data);
|
||||
free(si->lines);
|
||||
|
||||
free(si->colours);
|
||||
free(si);
|
||||
}
|
||||
|
||||
void
|
||||
sixel_log(struct sixel_image *si)
|
||||
{
|
||||
struct sixel_line *sl;
|
||||
char s[SIXEL_WIDTH_LIMIT + 1];
|
||||
u_int i, x, y, cx, cy;
|
||||
|
||||
sixel_size_in_cells(si, &cx, &cy);
|
||||
log_debug("%s: image %ux%u (%ux%u)", __func__, si->x, si->y, cx, cy);
|
||||
for (i = 0; i < si->ncolours; i++)
|
||||
log_debug("%s: colour %u is %07x", __func__, i, si->colours[i]);
|
||||
for (y = 0; y < si->y; y++) {
|
||||
sl = &si->lines[y];
|
||||
for (x = 0; x < si->x; x++) {
|
||||
if (x >= sl->x)
|
||||
s[x] = '_';
|
||||
else if (sl->data[x] != 0)
|
||||
s[x] = '0' + (sl->data[x] - 1) % 10;
|
||||
else
|
||||
s[x] = '.';
|
||||
}
|
||||
s[x] = '\0';
|
||||
log_debug("%s: %4u: %s", __func__, y, s);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sixel_size_in_cells(struct sixel_image *si, u_int *x, u_int *y)
|
||||
{
|
||||
if ((si->x % si->xpixel) == 0)
|
||||
*x = (si->x / si->xpixel);
|
||||
else
|
||||
*x = 1 + (si->x / si->xpixel);
|
||||
if ((si->y % si->ypixel) == 0)
|
||||
*y = (si->y / si->ypixel);
|
||||
else
|
||||
*y = 1 + (si->y / si->ypixel);
|
||||
}
|
||||
|
||||
struct sixel_image *
|
||||
sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
|
||||
u_int oy, u_int sx, u_int sy, int colours)
|
||||
{
|
||||
struct sixel_image *new;
|
||||
u_int cx, cy, pox, poy, psx, psy, tsx, tsy, px, py;
|
||||
u_int x, y, i;
|
||||
|
||||
/*
|
||||
* We want to get the section of the image at ox,oy in image cells and
|
||||
* map it onto the same size in terminal cells, remembering that we
|
||||
* can only draw vertical sections of six pixels.
|
||||
*/
|
||||
|
||||
sixel_size_in_cells(si, &cx, &cy);
|
||||
if (ox >= cx)
|
||||
return (NULL);
|
||||
if (oy >= cy)
|
||||
return (NULL);
|
||||
if (ox + sx >= cx)
|
||||
sx = cx - ox;
|
||||
if (oy + sy >= cy)
|
||||
sy = cy - oy;
|
||||
|
||||
if (xpixel == 0)
|
||||
xpixel = si->xpixel;
|
||||
if (ypixel == 0)
|
||||
ypixel = si->ypixel;
|
||||
|
||||
pox = ox * si->xpixel;
|
||||
poy = oy * si->ypixel;
|
||||
psx = sx * si->xpixel;
|
||||
psy = sy * si->ypixel;
|
||||
|
||||
tsx = sx * xpixel;
|
||||
tsy = ((sy * ypixel) / 6) * 6;
|
||||
|
||||
new = xcalloc (1, sizeof *si);
|
||||
new->xpixel = xpixel;
|
||||
new->ypixel = ypixel;
|
||||
new->p2 = si->p2;
|
||||
|
||||
new->set_ra = si->set_ra;
|
||||
/* clamp to slice end */
|
||||
new->ra_x = si->ra_x < psx ? si->ra_x : psx;
|
||||
new->ra_y = si->ra_y < psy ? si->ra_y : psy;
|
||||
/* subtract slice origin */
|
||||
new->ra_x = new->ra_x > pox ? new->ra_x - pox : 0;
|
||||
new->ra_y = new->ra_y > poy ? new->ra_y - poy : 0;
|
||||
/* resize */
|
||||
new->ra_x = new->ra_x * xpixel / si->xpixel;
|
||||
new->ra_y = new->ra_y * ypixel / si->ypixel;
|
||||
|
||||
for (y = 0; y < tsy; y++) {
|
||||
py = poy + ((double)y * psy / tsy);
|
||||
for (x = 0; x < tsx; x++) {
|
||||
px = pox + ((double)x * psx / tsx);
|
||||
sixel_set_pixel(new, x, y, sixel_get_pixel(si, px, py));
|
||||
}
|
||||
}
|
||||
|
||||
if (colours) {
|
||||
new->colours = xmalloc(si->ncolours * sizeof *new->colours);
|
||||
for (i = 0; i < si->ncolours; i++)
|
||||
new->colours[i] = si->colours[i];
|
||||
new->ncolours = si->ncolours;
|
||||
}
|
||||
return (new);
|
||||
}
|
||||
|
||||
static void
|
||||
sixel_print_add(char **buf, size_t *len, size_t *used, const char *s,
|
||||
size_t slen)
|
||||
{
|
||||
if (*used + slen >= *len + 1) {
|
||||
(*len) *= 2;
|
||||
*buf = xrealloc(*buf, *len);
|
||||
}
|
||||
memcpy(*buf + *used, s, slen);
|
||||
(*used) += slen;
|
||||
}
|
||||
|
||||
static void
|
||||
sixel_print_repeat(char **buf, size_t *len, size_t *used, u_int count, char ch)
|
||||
{
|
||||
char tmp[16];
|
||||
size_t tmplen;
|
||||
|
||||
if (count == 1)
|
||||
sixel_print_add(buf, len, used, &ch, 1);
|
||||
else if (count == 2) {
|
||||
sixel_print_add(buf, len, used, &ch, 1);
|
||||
sixel_print_add(buf, len, used, &ch, 1);
|
||||
} else if (count == 3) {
|
||||
sixel_print_add(buf, len, used, &ch, 1);
|
||||
sixel_print_add(buf, len, used, &ch, 1);
|
||||
sixel_print_add(buf, len, used, &ch, 1);
|
||||
} else if (count != 0) {
|
||||
tmplen = xsnprintf(tmp, sizeof tmp, "!%u%c", count, ch);
|
||||
sixel_print_add(buf, len, used, tmp, tmplen);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sixel_print_compress_colors(struct sixel_image *si, struct sixel_chunk *chunks,
|
||||
u_int y, u_int *active, u_int *nactive)
|
||||
{
|
||||
u_int i, x, c, dx, colors[6];
|
||||
struct sixel_chunk *chunk = NULL;
|
||||
struct sixel_line *sl;
|
||||
|
||||
for (x = 0; x < si->x; x++) {
|
||||
for (i = 0; i < 6; i++) {
|
||||
colors[i] = 0;
|
||||
if (y + i < si->y) {
|
||||
sl = &si->lines[y + i];
|
||||
if (x < sl->x && sl->data[x] != 0) {
|
||||
colors[i] = sl->data[x];
|
||||
c = sl->data[x] - 1;
|
||||
chunks[c].next_pattern |= 1 << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (colors[i] == 0)
|
||||
continue;
|
||||
|
||||
c = colors[i] - 1;
|
||||
chunk = &chunks[c];
|
||||
if (chunk->next_x == x + 1)
|
||||
continue;
|
||||
|
||||
if (chunk->next_y < y + 1) {
|
||||
chunk->next_y = y + 1;
|
||||
active[(*nactive)++] = c;
|
||||
}
|
||||
|
||||
dx = x - chunk->next_x;
|
||||
if (chunk->pattern != chunk->next_pattern || dx != 0) {
|
||||
sixel_print_repeat(&chunk->data, &chunk->len,
|
||||
&chunk->used, chunk->count,
|
||||
chunk->pattern + 0x3f);
|
||||
sixel_print_repeat(&chunk->data, &chunk->len,
|
||||
&chunk->used, dx, '?');
|
||||
chunk->pattern = chunk->next_pattern;
|
||||
chunk->count = 0;
|
||||
}
|
||||
chunk->count++;
|
||||
chunk->next_pattern = 0;
|
||||
chunk->next_x = x + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
|
||||
{
|
||||
char *buf, tmp[64];
|
||||
size_t len, used = 0, tmplen;
|
||||
u_int *colours, ncolours, i, c, y, *active, nactive;
|
||||
struct sixel_chunk *chunks, *chunk;
|
||||
|
||||
if (map != NULL) {
|
||||
colours = map->colours;
|
||||
ncolours = map->ncolours;
|
||||
} else {
|
||||
colours = si->colours;
|
||||
ncolours = si->ncolours;
|
||||
}
|
||||
|
||||
if (ncolours == 0)
|
||||
return (NULL);
|
||||
|
||||
len = 8192;
|
||||
buf = xmalloc(len);
|
||||
|
||||
tmplen = xsnprintf(tmp, sizeof tmp, "\033P0;%uq", si->p2);
|
||||
sixel_print_add(&buf, &len, &used, tmp, tmplen);
|
||||
|
||||
if (si->set_ra) {
|
||||
tmplen = xsnprintf(tmp, sizeof tmp, "\"1;1;%u;%u", si->ra_x,
|
||||
si->ra_y);
|
||||
sixel_print_add(&buf, &len, &used, tmp, tmplen);
|
||||
}
|
||||
|
||||
chunks = xcalloc(ncolours, sizeof *chunks);
|
||||
active = xcalloc(ncolours, sizeof *active);
|
||||
|
||||
for (i = 0; i < ncolours; i++) {
|
||||
c = colours[i];
|
||||
tmplen = xsnprintf(tmp, sizeof tmp, "#%u;%u;%u;%u;%u",
|
||||
i, c >> 24, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff);
|
||||
sixel_print_add(&buf, &len, &used, tmp, tmplen);
|
||||
|
||||
chunk = &chunks[i];
|
||||
chunk->len = 8;
|
||||
chunk->data = xmalloc(chunk->len);
|
||||
}
|
||||
|
||||
for (y = 0; y < si->y; y += 6) {
|
||||
nactive = 0;
|
||||
sixel_print_compress_colors(si, chunks, y, active, &nactive);
|
||||
|
||||
for (i = 0; i < nactive; i++) {
|
||||
c = active[i];
|
||||
chunk = &chunks[c];
|
||||
tmplen = xsnprintf(tmp, sizeof tmp, "#%u", c);
|
||||
sixel_print_add(&buf, &len, &used, tmp, tmplen);
|
||||
sixel_print_add(&buf, &len, &used, chunk->data,
|
||||
chunk->used);
|
||||
sixel_print_repeat(&buf, &len, &used, chunk->count,
|
||||
chunk->pattern + 0x3f);
|
||||
sixel_print_add(&buf, &len, &used, "$", 1);
|
||||
chunk->used = chunk->next_x = chunk->count = 0;
|
||||
}
|
||||
|
||||
if (buf[used - 1] == '$')
|
||||
used--;
|
||||
sixel_print_add(&buf, &len, &used, "-", 1);
|
||||
}
|
||||
if (buf[used - 1] == '-')
|
||||
used--;
|
||||
|
||||
sixel_print_add(&buf, &len, &used, "\033\\", 2);
|
||||
|
||||
buf[used] = '\0';
|
||||
if (size != NULL)
|
||||
*size = used;
|
||||
|
||||
for (i = 0; i < ncolours; i++)
|
||||
free(chunks[i].data);
|
||||
free(active);
|
||||
free(chunks);
|
||||
|
||||
return (buf);
|
||||
}
|
||||
|
||||
struct screen *
|
||||
sixel_to_screen(struct sixel_image *si)
|
||||
{
|
||||
struct screen *s;
|
||||
struct screen_write_ctx ctx;
|
||||
struct grid_cell gc;
|
||||
u_int x, y, sx, sy;
|
||||
|
||||
sixel_size_in_cells(si, &sx, &sy);
|
||||
|
||||
s = xmalloc(sizeof *s);
|
||||
screen_init(s, sx, sy, 0);
|
||||
|
||||
memcpy(&gc, &grid_default_cell, sizeof gc);
|
||||
gc.attr |= (GRID_ATTR_CHARSET|GRID_ATTR_DIM);
|
||||
utf8_set(&gc.data, '~');
|
||||
|
||||
screen_write_start(&ctx, s);
|
||||
if (sx == 1 || sy == 1) {
|
||||
for (y = 0; y < sy; y++) {
|
||||
for (x = 0; x < sx; x++)
|
||||
grid_view_set_cell(s->grid, x, y, &gc);
|
||||
}
|
||||
} else {
|
||||
screen_write_box(&ctx, sx, sy, BOX_LINES_DEFAULT, NULL, NULL);
|
||||
for (y = 1; y < sy - 1; y++) {
|
||||
for (x = 1; x < sx - 1; x++)
|
||||
grid_view_set_cell(s->grid, x, y, &gc);
|
||||
}
|
||||
}
|
||||
screen_write_stop(&ctx);
|
||||
return (s);
|
||||
}
|
186
image.c
Normal file
186
image.c
Normal file
@ -0,0 +1,186 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
static struct images all_images = TAILQ_HEAD_INITIALIZER(all_images);
|
||||
static u_int all_images_count;
|
||||
|
||||
static void
|
||||
image_free(struct image *im)
|
||||
{
|
||||
struct screen *s = im->s;
|
||||
|
||||
TAILQ_REMOVE(&all_images, im, all_entry);
|
||||
all_images_count--;
|
||||
|
||||
TAILQ_REMOVE(&s->images, im, entry);
|
||||
sixel_free(im->data);
|
||||
free(im->fallback);
|
||||
free(im);
|
||||
}
|
||||
|
||||
int
|
||||
image_free_all(struct screen *s)
|
||||
{
|
||||
struct image *im, *im1;
|
||||
int redraw = !TAILQ_EMPTY(&s->images);
|
||||
|
||||
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1)
|
||||
image_free(im);
|
||||
return (redraw);
|
||||
}
|
||||
|
||||
/* Create text placeholder for an image. */
|
||||
static void
|
||||
image_fallback(char **ret, u_int sx, u_int sy)
|
||||
{
|
||||
char *buf, *label;
|
||||
u_int py, size, lsize;
|
||||
|
||||
/* Allocate first line. */
|
||||
lsize = xasprintf(&label, "SIXEL IMAGE (%ux%u)\r\n", sx, sy) + 1;
|
||||
if (sx < lsize - 3)
|
||||
size = lsize - 1;
|
||||
else
|
||||
size = sx + 2;
|
||||
|
||||
/* Remaining lines. Every placeholder line has \r\n at the end. */
|
||||
size += (sx + 2) * (sy - 1) + 1;
|
||||
*ret = buf = xmalloc(size);
|
||||
|
||||
/* Render first line. */
|
||||
if (sx < lsize - 3) {
|
||||
memcpy(buf, label, lsize);
|
||||
buf += lsize - 1;
|
||||
} else {
|
||||
memcpy(buf, label, lsize - 3);
|
||||
buf += lsize - 3;
|
||||
memset(buf, '+', sx - lsize + 3);
|
||||
buf += sx - lsize + 3;
|
||||
snprintf(buf, 3, "\r\n");
|
||||
buf += 2;
|
||||
}
|
||||
|
||||
/* Remaining lines. */
|
||||
for (py = 1; py < sy; py++) {
|
||||
memset(buf, '+', sx);
|
||||
buf += sx;
|
||||
snprintf(buf, 3, "\r\n");
|
||||
buf += 2;
|
||||
}
|
||||
|
||||
free(label);
|
||||
}
|
||||
|
||||
struct image*
|
||||
image_store(struct screen *s, struct sixel_image *si)
|
||||
{
|
||||
struct image *im;
|
||||
|
||||
im = xcalloc(1, sizeof *im);
|
||||
im->s = s;
|
||||
im->data = si;
|
||||
|
||||
im->px = s->cx;
|
||||
im->py = s->cy;
|
||||
sixel_size_in_cells(si, &im->sx, &im->sy);
|
||||
|
||||
image_fallback(&im->fallback, im->sx, im->sy);
|
||||
|
||||
TAILQ_INSERT_TAIL(&s->images, im, entry);
|
||||
|
||||
TAILQ_INSERT_TAIL(&all_images, im, all_entry);
|
||||
if (++all_images_count == 10/*XXX*/)
|
||||
image_free(TAILQ_FIRST(&all_images));
|
||||
|
||||
return (im);
|
||||
}
|
||||
|
||||
int
|
||||
image_check_line(struct screen *s, u_int py, u_int ny)
|
||||
{
|
||||
struct image *im, *im1;
|
||||
int redraw = 0;
|
||||
|
||||
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
|
||||
if (py + ny > im->py && py < im->py + im->sy) {
|
||||
image_free(im);
|
||||
redraw = 1;
|
||||
}
|
||||
}
|
||||
return (redraw);
|
||||
}
|
||||
|
||||
int
|
||||
image_check_area(struct screen *s, u_int px, u_int py, u_int nx, u_int ny)
|
||||
{
|
||||
struct image *im, *im1;
|
||||
int redraw = 0;
|
||||
|
||||
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
|
||||
if (py + ny <= im->py || py >= im->py + im->sy)
|
||||
continue;
|
||||
if (px + nx <= im->px || px >= im->px + im->sx)
|
||||
continue;
|
||||
image_free(im);
|
||||
redraw = 1;
|
||||
}
|
||||
return (redraw);
|
||||
}
|
||||
|
||||
int
|
||||
image_scroll_up(struct screen *s, u_int lines)
|
||||
{
|
||||
struct image *im, *im1;
|
||||
int redraw = 0;
|
||||
u_int sx, sy;
|
||||
struct sixel_image *new;
|
||||
|
||||
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
|
||||
if (im->py >= lines) {
|
||||
im->py -= lines;
|
||||
redraw = 1;
|
||||
continue;
|
||||
}
|
||||
if (im->py + im->sy <= lines) {
|
||||
image_free(im);
|
||||
redraw = 1;
|
||||
continue;
|
||||
}
|
||||
sx = im->sx;
|
||||
sy = (im->py + im->sy) - lines;
|
||||
|
||||
new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1);
|
||||
sixel_free(im->data);
|
||||
im->data = new;
|
||||
|
||||
im->py = 0;
|
||||
sixel_size_in_cells(im->data, &im->sx, &im->sy);
|
||||
|
||||
free(im->fallback);
|
||||
image_fallback(&im->fallback, im->sx, im->sy);
|
||||
redraw = 1;
|
||||
}
|
||||
return (redraw);
|
||||
}
|
386
input-keys.c
386
input-keys.c
@ -53,9 +53,15 @@ static struct input_key_entry input_key_defaults[] = {
|
||||
{ .key = KEYC_PASTE_START,
|
||||
.data = "\033[200~"
|
||||
},
|
||||
{ .key = KEYC_PASTE_START|KEYC_IMPLIED_META,
|
||||
.data = "\033[200~"
|
||||
},
|
||||
{ .key = KEYC_PASTE_END,
|
||||
.data = "\033[201~"
|
||||
},
|
||||
{ .key = KEYC_PASTE_END|KEYC_IMPLIED_META,
|
||||
.data = "\033[201~"
|
||||
},
|
||||
|
||||
/* Function keys. */
|
||||
{ .key = KEYC_F1,
|
||||
@ -306,7 +312,13 @@ static struct input_key_entry input_key_defaults[] = {
|
||||
},
|
||||
{ .key = KEYC_DC|KEYC_BUILD_MODIFIERS,
|
||||
.data = "\033[3;_~"
|
||||
}
|
||||
},
|
||||
{ .key = KEYC_REPORT_DARK_THEME,
|
||||
.data = "\033[?997;1n"
|
||||
},
|
||||
{ .key = KEYC_REPORT_LIGHT_THEME,
|
||||
.data = "\033[?997;2n"
|
||||
},
|
||||
};
|
||||
static const key_code input_key_modifiers[] = {
|
||||
0,
|
||||
@ -412,118 +424,18 @@ input_key_write(const char *from, struct bufferevent *bev, const char *data,
|
||||
bufferevent_write(bev, data, size);
|
||||
}
|
||||
|
||||
/* Translate a key code into an output key sequence. */
|
||||
int
|
||||
input_key(struct screen *s, struct bufferevent *bev, key_code key)
|
||||
/*
|
||||
* Encode and write an extended key escape sequence in one of the two
|
||||
* possible formats, depending on the configured output mode.
|
||||
*/
|
||||
static int
|
||||
input_key_extended(struct bufferevent *bev, key_code key)
|
||||
{
|
||||
struct input_key_entry *ike;
|
||||
key_code justkey, newkey, outkey, modifiers;
|
||||
struct utf8_data ud;
|
||||
char tmp[64], modifier;
|
||||
char tmp[64], modifier;
|
||||
struct utf8_data ud;
|
||||
wchar_t wc;
|
||||
|
||||
/* Mouse keys need a pane. */
|
||||
if (KEYC_IS_MOUSE(key))
|
||||
return (0);
|
||||
|
||||
/* Literal keys go as themselves (can't be more than eight bits). */
|
||||
if (key & KEYC_LITERAL) {
|
||||
ud.data[0] = (u_char)key;
|
||||
input_key_write(__func__, bev, &ud.data[0], 1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Is this backspace? */
|
||||
if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
|
||||
newkey = options_get_number(global_options, "backspace");
|
||||
if (newkey >= 0x7f)
|
||||
newkey = '\177';
|
||||
key = newkey|(key & (KEYC_MASK_MODIFIERS|KEYC_MASK_FLAGS));
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is a normal 7-bit key, just send it, with a leading escape
|
||||
* if necessary. If it is a UTF-8 key, split it and send it.
|
||||
*/
|
||||
justkey = (key & ~(KEYC_META|KEYC_IMPLIED_META));
|
||||
if (justkey <= 0x7f) {
|
||||
if (key & KEYC_META)
|
||||
input_key_write(__func__, bev, "\033", 1);
|
||||
ud.data[0] = justkey;
|
||||
input_key_write(__func__, bev, &ud.data[0], 1);
|
||||
return (0);
|
||||
}
|
||||
if (KEYC_IS_UNICODE(justkey)) {
|
||||
if (key & KEYC_META)
|
||||
input_key_write(__func__, bev, "\033", 1);
|
||||
utf8_to_data(justkey, &ud);
|
||||
input_key_write(__func__, bev, ud.data, ud.size);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up in the tree. If not in application keypad or cursor mode,
|
||||
* remove the flags from the key.
|
||||
*/
|
||||
if (~s->mode & MODE_KKEYPAD)
|
||||
key &= ~KEYC_KEYPAD;
|
||||
if (~s->mode & MODE_KCURSOR)
|
||||
key &= ~KEYC_CURSOR;
|
||||
ike = input_key_get(key);
|
||||
if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META))
|
||||
ike = input_key_get(key & ~KEYC_META);
|
||||
if (ike == NULL && (key & KEYC_CURSOR))
|
||||
ike = input_key_get(key & ~KEYC_CURSOR);
|
||||
if (ike == NULL && (key & KEYC_KEYPAD))
|
||||
ike = input_key_get(key & ~KEYC_KEYPAD);
|
||||
if (ike != NULL) {
|
||||
log_debug("found key 0x%llx: \"%s\"", key, ike->data);
|
||||
if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META))
|
||||
input_key_write(__func__, bev, "\033", 1);
|
||||
input_key_write(__func__, bev, ike->data, strlen(ike->data));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* No builtin key sequence; construct an extended key sequence. */
|
||||
if (~s->mode & MODE_KEXTENDED) {
|
||||
if ((key & KEYC_MASK_MODIFIERS) != KEYC_CTRL)
|
||||
goto missing;
|
||||
justkey = (key & KEYC_MASK_KEY);
|
||||
switch (justkey) {
|
||||
case ' ':
|
||||
case '2':
|
||||
key = 0|(key & ~KEYC_MASK_KEY);
|
||||
break;
|
||||
case '|':
|
||||
key = 28|(key & ~KEYC_MASK_KEY);
|
||||
break;
|
||||
case '6':
|
||||
key = 30|(key & ~KEYC_MASK_KEY);
|
||||
break;
|
||||
case '-':
|
||||
case '/':
|
||||
key = 31|(key & ~KEYC_MASK_KEY);
|
||||
break;
|
||||
case '?':
|
||||
key = 127|(key & ~KEYC_MASK_KEY);
|
||||
break;
|
||||
default:
|
||||
if (justkey >= 'A' && justkey <= '_')
|
||||
key = (justkey - 'A')|(key & ~KEYC_MASK_KEY);
|
||||
else if (justkey >= 'a' && justkey <= '~')
|
||||
key = (justkey - 96)|(key & ~KEYC_MASK_KEY);
|
||||
else
|
||||
return (0);
|
||||
break;
|
||||
}
|
||||
return (input_key(s, bev, key & ~KEYC_CTRL));
|
||||
}
|
||||
outkey = (key & KEYC_MASK_KEY);
|
||||
modifiers = (key & KEYC_MASK_MODIFIERS);
|
||||
if (outkey < 32 && outkey != 9 && outkey != 13 && outkey != 27) {
|
||||
outkey = 64 + outkey;
|
||||
modifiers |= KEYC_CTRL;
|
||||
}
|
||||
switch (modifiers) {
|
||||
switch (key & KEYC_MASK_MODIFIERS) {
|
||||
case KEYC_SHIFT:
|
||||
modifier = '2';
|
||||
break;
|
||||
@ -546,17 +458,261 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
|
||||
modifier = '8';
|
||||
break;
|
||||
default:
|
||||
goto missing;
|
||||
return (-1);
|
||||
}
|
||||
xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier);
|
||||
|
||||
if (KEYC_IS_UNICODE(key)) {
|
||||
utf8_to_data(key & KEYC_MASK_KEY, &ud);
|
||||
if (utf8_towc(&ud, &wc) == UTF8_DONE)
|
||||
key = wc;
|
||||
else
|
||||
return (-1);
|
||||
} else
|
||||
key &= KEYC_MASK_KEY;
|
||||
|
||||
if (options_get_number(global_options, "extended-keys-format") == 1)
|
||||
xsnprintf(tmp, sizeof tmp, "\033[27;%c;%llu~", modifier, key);
|
||||
else
|
||||
xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", key, modifier);
|
||||
|
||||
input_key_write(__func__, bev, tmp, strlen(tmp));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Outputs the key in the "standard" mode. This is by far the most
|
||||
* complicated output mode, with a lot of remapping in order to
|
||||
* emulate quirks of terminals that today can be only found in museums.
|
||||
*/
|
||||
static int
|
||||
input_key_vt10x(struct bufferevent *bev, key_code key)
|
||||
{
|
||||
struct utf8_data ud;
|
||||
key_code onlykey;
|
||||
char *p;
|
||||
static const char *standard_map[2] = {
|
||||
"1!9(0)=+;:'\",<.>/-8? 2",
|
||||
"119900=+;;'',,..\x1f\x1f\x7f\x7f\0\0",
|
||||
};
|
||||
|
||||
log_debug("%s: key in %llx", __func__, key);
|
||||
|
||||
if (key & KEYC_META)
|
||||
input_key_write(__func__, bev, "\033", 1);
|
||||
|
||||
/*
|
||||
* There's no way to report modifiers for unicode keys in standard mode
|
||||
* so lose the modifiers.
|
||||
*/
|
||||
if (KEYC_IS_UNICODE(key)) {
|
||||
utf8_to_data(key, &ud);
|
||||
input_key_write(__func__, bev, ud.data, ud.size);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prevent TAB, CR and LF from being swallowed by the C0 remapping
|
||||
* logic.
|
||||
*/
|
||||
onlykey = key & KEYC_MASK_KEY;
|
||||
if (onlykey == '\r' || onlykey == '\n' || onlykey == '\t')
|
||||
key &= ~KEYC_CTRL;
|
||||
|
||||
/*
|
||||
* Convert keys with Ctrl modifier into corresponding C0 control codes,
|
||||
* with the exception of *some* keys, which are remapped into printable
|
||||
* ASCII characters.
|
||||
*
|
||||
* There is no special handling for Shift modifier, which is pretty
|
||||
* much redundant anyway, as no terminal will send <base key>|SHIFT,
|
||||
* but only <shifted key>|SHIFT.
|
||||
*/
|
||||
if (key & KEYC_CTRL) {
|
||||
p = strchr(standard_map[0], onlykey);
|
||||
if (p != NULL)
|
||||
key = standard_map[1][p - standard_map[0]];
|
||||
else if (onlykey >= '3' && onlykey <= '7')
|
||||
key = onlykey - '\030';
|
||||
else if (onlykey >= '@' && onlykey <= '~')
|
||||
key = onlykey & 0x1f;
|
||||
else
|
||||
return (-1);
|
||||
}
|
||||
|
||||
log_debug("%s: key out %llx", __func__, key);
|
||||
|
||||
ud.data[0] = key & 0x7f;
|
||||
input_key_write(__func__, bev, &ud.data[0], 1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Pick keys that are reported as vt10x keys in modifyOtherKeys=1 mode. */
|
||||
static int
|
||||
input_key_mode1(struct bufferevent *bev, key_code key)
|
||||
{
|
||||
key_code onlykey;
|
||||
|
||||
log_debug("%s: key in %llx", __func__, key);
|
||||
|
||||
/* A regular or shifted key + Meta. */
|
||||
if ((key & (KEYC_CTRL | KEYC_META)) == KEYC_META)
|
||||
return (input_key_vt10x(bev, key));
|
||||
|
||||
/*
|
||||
* As per
|
||||
* https://invisible-island.net/xterm/modified-keys-us-pc105.html.
|
||||
*/
|
||||
onlykey = key & KEYC_MASK_KEY;
|
||||
if ((key & KEYC_CTRL) &&
|
||||
(onlykey == ' ' ||
|
||||
onlykey == '/' ||
|
||||
onlykey == '@' ||
|
||||
onlykey == '^' ||
|
||||
(onlykey >= '2' && onlykey <= '8') ||
|
||||
(onlykey >= '@' && onlykey <= '~')))
|
||||
return (input_key_vt10x(bev, key));
|
||||
|
||||
missing:
|
||||
log_debug("key 0x%llx missing", key);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Translate a key code into an output key sequence. */
|
||||
int
|
||||
input_key(struct screen *s, struct bufferevent *bev, key_code key)
|
||||
{
|
||||
struct input_key_entry *ike = NULL;
|
||||
key_code newkey;
|
||||
struct utf8_data ud;
|
||||
|
||||
/* Mouse keys need a pane. */
|
||||
if (KEYC_IS_MOUSE(key))
|
||||
return (0);
|
||||
|
||||
/* Literal keys go as themselves (can't be more than eight bits). */
|
||||
if (key & KEYC_LITERAL) {
|
||||
ud.data[0] = (u_char)key;
|
||||
input_key_write(__func__, bev, &ud.data[0], 1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Is this backspace? */
|
||||
if ((key & KEYC_MASK_KEY) == KEYC_BSPACE) {
|
||||
newkey = options_get_number(global_options, "backspace");
|
||||
log_debug("%s: key 0x%llx is backspace -> 0x%llx", __func__,
|
||||
key, newkey);
|
||||
if ((key & KEYC_MASK_MODIFIERS) == 0) {
|
||||
ud.data[0] = 255;
|
||||
if ((newkey & KEYC_MASK_MODIFIERS) == 0)
|
||||
ud.data[0] = newkey;
|
||||
else if ((newkey & KEYC_MASK_MODIFIERS) == KEYC_CTRL) {
|
||||
newkey &= KEYC_MASK_KEY;
|
||||
if (newkey >= 'A' && newkey <= 'Z')
|
||||
ud.data[0] = newkey - 0x40;
|
||||
else if (newkey >= 'a' && newkey <= 'z')
|
||||
ud.data[0] = newkey - 0x60;
|
||||
}
|
||||
if (ud.data[0] != 255)
|
||||
input_key_write(__func__, bev, &ud.data[0], 1);
|
||||
return (0);
|
||||
}
|
||||
key = newkey|(key & (KEYC_MASK_FLAGS|KEYC_MASK_MODIFIERS));
|
||||
}
|
||||
|
||||
/* Is this backtab? */
|
||||
if ((key & KEYC_MASK_KEY) == KEYC_BTAB) {
|
||||
if (s->mode & MODE_KEYS_EXTENDED_2) {
|
||||
/* When in xterm extended mode, remap into S-Tab. */
|
||||
key = '\011' | (key & ~KEYC_MASK_KEY) | KEYC_SHIFT;
|
||||
} else {
|
||||
/* Otherwise clear modifiers. */
|
||||
key &= ~KEYC_MASK_MODIFIERS;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A trivial case, that is a 7-bit key, excluding C0 control characters
|
||||
* that can't be entered from the keyboard, and no modifiers; or a UTF-8
|
||||
* key and no modifiers.
|
||||
*/
|
||||
if (!(key & ~KEYC_MASK_KEY)) {
|
||||
if (key == C0_HT ||
|
||||
key == C0_CR ||
|
||||
key == C0_ESC ||
|
||||
(key >= 0x20 && key <= 0x7f)) {
|
||||
ud.data[0] = key;
|
||||
input_key_write(__func__, bev, &ud.data[0], 1);
|
||||
return (0);
|
||||
}
|
||||
if (KEYC_IS_UNICODE(key)) {
|
||||
utf8_to_data(key, &ud);
|
||||
input_key_write(__func__, bev, ud.data, ud.size);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up the standard VT10x keys in the tree. If not in application
|
||||
* keypad or cursor mode, remove the respective flags from the key.
|
||||
*/
|
||||
if (~s->mode & MODE_KKEYPAD)
|
||||
key &= ~KEYC_KEYPAD;
|
||||
if (~s->mode & MODE_KCURSOR)
|
||||
key &= ~KEYC_CURSOR;
|
||||
if (ike == NULL)
|
||||
ike = input_key_get(key);
|
||||
if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META))
|
||||
ike = input_key_get(key & ~KEYC_META);
|
||||
if (ike == NULL && (key & KEYC_CURSOR))
|
||||
ike = input_key_get(key & ~KEYC_CURSOR);
|
||||
if (ike == NULL && (key & KEYC_KEYPAD))
|
||||
ike = input_key_get(key & ~KEYC_KEYPAD);
|
||||
if (ike != NULL) {
|
||||
log_debug("%s: found key 0x%llx: \"%s\"", __func__, key,
|
||||
ike->data);
|
||||
if (KEYC_IS_PASTE(key) && (~s->mode & MODE_BRACKETPASTE))
|
||||
return (0);
|
||||
if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META))
|
||||
input_key_write(__func__, bev, "\033", 1);
|
||||
input_key_write(__func__, bev, ike->data, strlen(ike->data));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Ignore internal function key codes. */
|
||||
if ((key >= KEYC_BASE && key < KEYC_BASE_END) ||
|
||||
(key >= KEYC_USER && key < KEYC_USER_END)) {
|
||||
log_debug("%s: ignoring key 0x%llx", __func__, key);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* No builtin key sequence; construct an extended key sequence
|
||||
* depending on the client mode.
|
||||
*
|
||||
* If something invalid reaches here, an invalid output may be
|
||||
* produced. For example Ctrl-Shift-2 is invalid (as there's
|
||||
* no way to enter it). The correct form is Ctrl-Shift-@, at
|
||||
* least in US English keyboard layout.
|
||||
*/
|
||||
switch (s->mode & EXTENDED_KEY_MODES) {
|
||||
case MODE_KEYS_EXTENDED_2:
|
||||
/*
|
||||
* The simplest mode to handle - *all* modified keys are
|
||||
* reported in the extended form.
|
||||
*/
|
||||
return (input_key_extended(bev, key));
|
||||
case MODE_KEYS_EXTENDED:
|
||||
/*
|
||||
* Some keys are still reported in standard mode, to maintain
|
||||
* compatibility with applications unaware of extended keys.
|
||||
*/
|
||||
if (input_key_mode1(bev, key) == -1)
|
||||
return (input_key_extended(bev, key));
|
||||
return (0);
|
||||
default:
|
||||
/* The standard mode. */
|
||||
return (input_key_vt10x(bev, key));
|
||||
}
|
||||
}
|
||||
|
||||
/* Get mouse event string. */
|
||||
int
|
||||
input_key_get_mouse(struct screen *s, struct mouse_event *m, u_int x, u_int y,
|
||||
|
457
input.c
457
input.c
@ -93,7 +93,6 @@ struct input_ctx {
|
||||
size_t param_len;
|
||||
|
||||
#define INPUT_BUF_START 32
|
||||
#define INPUT_BUF_LIMIT 1048576
|
||||
u_char *input_buf;
|
||||
size_t input_len;
|
||||
size_t input_space;
|
||||
@ -109,10 +108,11 @@ struct input_ctx {
|
||||
int utf8started;
|
||||
|
||||
int ch;
|
||||
int last;
|
||||
struct utf8_data last;
|
||||
|
||||
int flags;
|
||||
#define INPUT_DISCARD 0x1
|
||||
#define INPUT_LAST 0x2
|
||||
|
||||
const struct input_state *state;
|
||||
|
||||
@ -133,8 +133,9 @@ static void printflike(2, 3) input_reply(struct input_ctx *, const char *, ...);
|
||||
static void input_set_state(struct input_ctx *,
|
||||
const struct input_transition *);
|
||||
static void input_reset_cell(struct input_ctx *);
|
||||
|
||||
static void input_report_current_theme(struct input_ctx *);
|
||||
static void input_osc_4(struct input_ctx *, const char *);
|
||||
static void input_osc_8(struct input_ctx *, const char *);
|
||||
static void input_osc_10(struct input_ctx *, const char *);
|
||||
static void input_osc_11(struct input_ctx *, const char *);
|
||||
static void input_osc_12(struct input_ctx *, const char *);
|
||||
@ -143,6 +144,7 @@ static void input_osc_104(struct input_ctx *, const char *);
|
||||
static void input_osc_110(struct input_ctx *, const char *);
|
||||
static void input_osc_111(struct input_ctx *, const char *);
|
||||
static void input_osc_112(struct input_ctx *, const char *);
|
||||
static void input_osc_133(struct input_ctx *, const char *);
|
||||
|
||||
/* Transition entry/exit handlers. */
|
||||
static void input_clear(struct input_ctx *);
|
||||
@ -167,6 +169,7 @@ static void input_csi_dispatch_rm(struct input_ctx *);
|
||||
static void input_csi_dispatch_rm_private(struct input_ctx *);
|
||||
static void input_csi_dispatch_sm(struct input_ctx *);
|
||||
static void input_csi_dispatch_sm_private(struct input_ctx *);
|
||||
static void input_csi_dispatch_sm_graphics(struct input_ctx *);
|
||||
static void input_csi_dispatch_winops(struct input_ctx *);
|
||||
static void input_csi_dispatch_sgr_256(struct input_ctx *, int, u_int *);
|
||||
static void input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *);
|
||||
@ -201,7 +204,7 @@ enum input_esc_type {
|
||||
INPUT_ESC_SCSG0_ON,
|
||||
INPUT_ESC_SCSG1_OFF,
|
||||
INPUT_ESC_SCSG1_ON,
|
||||
INPUT_ESC_ST,
|
||||
INPUT_ESC_ST
|
||||
};
|
||||
|
||||
/* Escape command table. */
|
||||
@ -240,6 +243,7 @@ enum input_csi_type {
|
||||
INPUT_CSI_DECSTBM,
|
||||
INPUT_CSI_DL,
|
||||
INPUT_CSI_DSR,
|
||||
INPUT_CSI_DSR_PRIVATE,
|
||||
INPUT_CSI_ECH,
|
||||
INPUT_CSI_ED,
|
||||
INPUT_CSI_EL,
|
||||
@ -248,6 +252,7 @@ enum input_csi_type {
|
||||
INPUT_CSI_IL,
|
||||
INPUT_CSI_MODOFF,
|
||||
INPUT_CSI_MODSET,
|
||||
INPUT_CSI_QUERY_PRIVATE,
|
||||
INPUT_CSI_RCP,
|
||||
INPUT_CSI_REP,
|
||||
INPUT_CSI_RM,
|
||||
@ -256,12 +261,13 @@ enum input_csi_type {
|
||||
INPUT_CSI_SD,
|
||||
INPUT_CSI_SGR,
|
||||
INPUT_CSI_SM,
|
||||
INPUT_CSI_SM_GRAPHICS,
|
||||
INPUT_CSI_SM_PRIVATE,
|
||||
INPUT_CSI_SU,
|
||||
INPUT_CSI_TBC,
|
||||
INPUT_CSI_VPA,
|
||||
INPUT_CSI_WINOPS,
|
||||
INPUT_CSI_XDA,
|
||||
INPUT_CSI_XDA
|
||||
};
|
||||
|
||||
/* Control (CSI) command table. */
|
||||
@ -281,6 +287,7 @@ static const struct input_table_entry input_csi_table[] = {
|
||||
{ 'M', "", INPUT_CSI_DL },
|
||||
{ 'P', "", INPUT_CSI_DCH },
|
||||
{ 'S', "", INPUT_CSI_SU },
|
||||
{ 'S', "?", INPUT_CSI_SM_GRAPHICS },
|
||||
{ 'T', "", INPUT_CSI_SD },
|
||||
{ 'X', "", INPUT_CSI_ECH },
|
||||
{ 'Z', "", INPUT_CSI_CBT },
|
||||
@ -299,12 +306,14 @@ static const struct input_table_entry input_csi_table[] = {
|
||||
{ 'm', ">", INPUT_CSI_MODSET },
|
||||
{ 'n', "", INPUT_CSI_DSR },
|
||||
{ 'n', ">", INPUT_CSI_MODOFF },
|
||||
{ 'n', "?", INPUT_CSI_DSR_PRIVATE },
|
||||
{ 'p', "?$", INPUT_CSI_QUERY_PRIVATE },
|
||||
{ 'q', " ", INPUT_CSI_DECSCUSR },
|
||||
{ 'q', ">", INPUT_CSI_XDA },
|
||||
{ 'r', "", INPUT_CSI_DECSTBM },
|
||||
{ 's', "", INPUT_CSI_SCP },
|
||||
{ 't', "", INPUT_CSI_WINOPS },
|
||||
{ 'u', "", INPUT_CSI_RCP },
|
||||
{ 'u', "", INPUT_CSI_RCP }
|
||||
};
|
||||
|
||||
/* Input transition. */
|
||||
@ -723,6 +732,9 @@ static const struct input_transition input_state_consume_st_table[] = {
|
||||
{ -1, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
/* Maximum of bytes allowed to read in a single input. */
|
||||
static size_t input_buffer_size = INPUT_BUF_DEFAULT_SIZE;
|
||||
|
||||
/* Input table compare. */
|
||||
static int
|
||||
input_table_compare(const void *key, const void *value)
|
||||
@ -862,8 +874,6 @@ input_reset(struct input_ctx *ictx, int clear)
|
||||
|
||||
input_clear(ictx);
|
||||
|
||||
ictx->last = -1;
|
||||
|
||||
ictx->state = &input_state_ground;
|
||||
ictx->flags = 0;
|
||||
}
|
||||
@ -970,6 +980,10 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len)
|
||||
window_update_activity(wp->window);
|
||||
wp->flags |= PANE_CHANGED;
|
||||
|
||||
/* Flag new input while in a mode. */
|
||||
if (!TAILQ_EMPTY(&wp->modes))
|
||||
wp->flags |= PANE_UNSEENCHANGES;
|
||||
|
||||
/* NULL wp if there is a mode set as don't want to update the tty. */
|
||||
if (TAILQ_EMPTY(&wp->modes))
|
||||
screen_write_start_pane(sctx, wp, &wp->base);
|
||||
@ -1085,6 +1099,7 @@ input_reply(struct input_ctx *ictx, const char *fmt, ...)
|
||||
xvasprintf(&reply, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
log_debug("%s: %s", __func__, reply);
|
||||
bufferevent_write(bev, reply, strlen(reply));
|
||||
free(reply);
|
||||
}
|
||||
@ -1136,10 +1151,11 @@ input_print(struct input_ctx *ictx)
|
||||
ictx->cell.cell.attr |= GRID_ATTR_CHARSET;
|
||||
else
|
||||
ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET;
|
||||
|
||||
utf8_set(&ictx->cell.cell.data, ictx->ch);
|
||||
screen_write_collect_add(sctx, &ictx->cell.cell);
|
||||
ictx->last = ictx->ch;
|
||||
|
||||
utf8_copy(&ictx->last, &ictx->cell.cell.data);
|
||||
ictx->flags |= INPUT_LAST;
|
||||
|
||||
ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET;
|
||||
|
||||
@ -1183,7 +1199,7 @@ input_input(struct input_ctx *ictx)
|
||||
available = ictx->input_space;
|
||||
while (ictx->input_len + 1 >= available) {
|
||||
available *= 2;
|
||||
if (available > INPUT_BUF_LIMIT) {
|
||||
if (available > input_buffer_size) {
|
||||
ictx->flags |= INPUT_DISCARD;
|
||||
return (0);
|
||||
}
|
||||
@ -1203,6 +1219,10 @@ input_c0_dispatch(struct input_ctx *ictx)
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
struct window_pane *wp = ictx->wp;
|
||||
struct screen *s = sctx->s;
|
||||
struct grid_cell gc, first_gc;
|
||||
u_int cx = s->cx, line = s->cy + s->grid->hsize;
|
||||
u_int width;
|
||||
int has_content = 0;
|
||||
|
||||
ictx->utf8started = 0; /* can't be valid UTF-8 */
|
||||
|
||||
@ -1224,11 +1244,28 @@ input_c0_dispatch(struct input_ctx *ictx)
|
||||
break;
|
||||
|
||||
/* Find the next tab point, or use the last column if none. */
|
||||
grid_get_cell(s->grid, s->cx, line, &first_gc);
|
||||
do {
|
||||
s->cx++;
|
||||
if (bit_test(s->tabs, s->cx))
|
||||
if (!has_content) {
|
||||
grid_get_cell(s->grid, cx, line, &gc);
|
||||
if (gc.data.size != 1 ||
|
||||
*gc.data.data != ' ' ||
|
||||
!grid_cells_look_equal(&gc, &first_gc))
|
||||
has_content = 1;
|
||||
}
|
||||
cx++;
|
||||
if (bit_test(s->tabs, cx))
|
||||
break;
|
||||
} while (s->cx < screen_size_x(s) - 1);
|
||||
} while (cx < screen_size_x(s) - 1);
|
||||
|
||||
width = cx - s->cx;
|
||||
if (has_content || width > sizeof gc.data.data)
|
||||
s->cx = cx;
|
||||
else {
|
||||
grid_get_cell(s->grid, s->cx, line, &gc);
|
||||
grid_set_tab(&gc, width);
|
||||
screen_write_collect_add(sctx, &gc);
|
||||
}
|
||||
break;
|
||||
case '\012': /* LF */
|
||||
case '\013': /* VT */
|
||||
@ -1251,7 +1288,7 @@ input_c0_dispatch(struct input_ctx *ictx)
|
||||
break;
|
||||
}
|
||||
|
||||
ictx->last = -1;
|
||||
ictx->flags &= ~INPUT_LAST;
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -1327,7 +1364,7 @@ input_esc_dispatch(struct input_ctx *ictx)
|
||||
break;
|
||||
}
|
||||
|
||||
ictx->last = -1;
|
||||
ictx->flags &= ~INPUT_LAST;
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -1338,14 +1375,14 @@ input_csi_dispatch(struct input_ctx *ictx)
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
struct screen *s = sctx->s;
|
||||
struct input_table_entry *entry;
|
||||
int i, n, m;
|
||||
int i, n, m, ek, set;
|
||||
u_int cx, bg = ictx->cell.cell.bg;
|
||||
|
||||
if (ictx->flags & INPUT_DISCARD)
|
||||
return (0);
|
||||
|
||||
log_debug("%s: '%c' \"%s\" \"%s\"",
|
||||
__func__, ictx->ch, ictx->interm_buf, ictx->param_buf);
|
||||
log_debug("%s: '%c' \"%s\" \"%s\"", __func__, ictx->ch,
|
||||
ictx->interm_buf, ictx->param_buf);
|
||||
|
||||
if (input_split(ictx) != 0)
|
||||
return (0);
|
||||
@ -1396,18 +1433,36 @@ input_csi_dispatch(struct input_ctx *ictx)
|
||||
break;
|
||||
case INPUT_CSI_MODSET:
|
||||
n = input_get(ictx, 0, 0, 0);
|
||||
m = input_get(ictx, 1, 0, 0);
|
||||
if (options_get_number(global_options, "extended-keys") == 2)
|
||||
if (n != 4)
|
||||
break;
|
||||
if (n == 0 || (n == 4 && m == 0))
|
||||
screen_write_mode_clear(sctx, MODE_KEXTENDED);
|
||||
else if (n == 4 && (m == 1 || m == 2))
|
||||
screen_write_mode_set(sctx, MODE_KEXTENDED);
|
||||
m = input_get(ictx, 1, 0, 0);
|
||||
|
||||
/*
|
||||
* Set the extended key reporting mode as per the client
|
||||
* request, unless "extended-keys" is set to "off".
|
||||
*/
|
||||
ek = options_get_number(global_options, "extended-keys");
|
||||
if (ek == 0)
|
||||
break;
|
||||
screen_write_mode_clear(sctx, EXTENDED_KEY_MODES);
|
||||
if (m == 2)
|
||||
screen_write_mode_set(sctx, MODE_KEYS_EXTENDED_2);
|
||||
else if (m == 1 || ek == 2)
|
||||
screen_write_mode_set(sctx, MODE_KEYS_EXTENDED);
|
||||
break;
|
||||
case INPUT_CSI_MODOFF:
|
||||
n = input_get(ictx, 0, 0, 0);
|
||||
if (n == 4)
|
||||
screen_write_mode_clear(sctx, MODE_KEXTENDED);
|
||||
if (n != 4)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Clear the extended key reporting mode as per the client
|
||||
* request, unless "extended-keys always" forces into mode 1.
|
||||
*/
|
||||
screen_write_mode_clear(sctx,
|
||||
MODE_KEYS_EXTENDED|MODE_KEYS_EXTENDED_2);
|
||||
if (options_get_number(global_options, "extended-keys") == 2)
|
||||
screen_write_mode_set(sctx, MODE_KEYS_EXTENDED);
|
||||
break;
|
||||
case INPUT_CSI_WINOPS:
|
||||
input_csi_dispatch_winops(ictx);
|
||||
@ -1436,7 +1491,11 @@ input_csi_dispatch(struct input_ctx *ictx)
|
||||
case -1:
|
||||
break;
|
||||
case 0:
|
||||
#ifdef ENABLE_SIXEL
|
||||
input_reply(ictx, "\033[?1;2;4c");
|
||||
#else
|
||||
input_reply(ictx, "\033[?1;2c");
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
log_debug("%s: unknown '%c'", __func__, ictx->ch);
|
||||
@ -1476,6 +1535,20 @@ input_csi_dispatch(struct input_ctx *ictx)
|
||||
if (n != -1)
|
||||
screen_write_deleteline(sctx, n, bg);
|
||||
break;
|
||||
case INPUT_CSI_DSR_PRIVATE:
|
||||
switch (input_get(ictx, 0, 0, 0)) {
|
||||
case 996:
|
||||
input_report_current_theme(ictx);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case INPUT_CSI_QUERY_PRIVATE:
|
||||
switch (input_get(ictx, 0, 0, 0)) {
|
||||
case 2031:
|
||||
input_reply(ictx, "\033[?2031;2$y");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case INPUT_CSI_DSR:
|
||||
switch (input_get(ictx, 0, 0, 0)) {
|
||||
case -1:
|
||||
@ -1560,12 +1633,17 @@ input_csi_dispatch(struct input_ctx *ictx)
|
||||
if (n > m)
|
||||
n = m;
|
||||
|
||||
if (ictx->last == -1)
|
||||
if (~ictx->flags & INPUT_LAST)
|
||||
break;
|
||||
ictx->ch = ictx->last;
|
||||
|
||||
set = ictx->cell.set == 0 ? ictx->cell.g0set : ictx->cell.g1set;
|
||||
if (set == 1)
|
||||
ictx->cell.cell.attr |= GRID_ATTR_CHARSET;
|
||||
else
|
||||
ictx->cell.cell.attr &= ~GRID_ATTR_CHARSET;
|
||||
utf8_copy(&ictx->cell.cell.data, &ictx->last);
|
||||
for (i = 0; i < n; i++)
|
||||
input_print(ictx);
|
||||
screen_write_collect_add(sctx, &ictx->cell.cell);
|
||||
break;
|
||||
case INPUT_CSI_RCP:
|
||||
input_restore_state(ictx);
|
||||
@ -1588,6 +1666,9 @@ input_csi_dispatch(struct input_ctx *ictx)
|
||||
case INPUT_CSI_SM_PRIVATE:
|
||||
input_csi_dispatch_sm_private(ictx);
|
||||
break;
|
||||
case INPUT_CSI_SM_GRAPHICS:
|
||||
input_csi_dispatch_sm_graphics(ictx);
|
||||
break;
|
||||
case INPUT_CSI_SU:
|
||||
n = input_get(ictx, 0, 1, 1);
|
||||
if (n != -1)
|
||||
@ -1632,7 +1713,7 @@ input_csi_dispatch(struct input_ctx *ictx)
|
||||
|
||||
}
|
||||
|
||||
ictx->last = -1;
|
||||
ictx->flags &= ~INPUT_LAST;
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -1718,6 +1799,9 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
|
||||
case 2004:
|
||||
screen_write_mode_clear(sctx, MODE_BRACKETPASTE);
|
||||
break;
|
||||
case 2031:
|
||||
screen_write_mode_clear(sctx, MODE_THEME_UPDATES);
|
||||
break;
|
||||
default:
|
||||
log_debug("%s: unknown '%c'", __func__, ictx->ch);
|
||||
break;
|
||||
@ -1754,7 +1838,6 @@ static void
|
||||
input_csi_dispatch_sm_private(struct input_ctx *ictx)
|
||||
{
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
struct window_pane *wp = ictx->wp;
|
||||
struct grid_cell *gc = &ictx->cell.cell;
|
||||
u_int i;
|
||||
|
||||
@ -1796,17 +1879,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
|
||||
screen_write_mode_set(sctx, MODE_MOUSE_ALL);
|
||||
break;
|
||||
case 1004:
|
||||
if (sctx->s->mode & MODE_FOCUSON)
|
||||
break;
|
||||
screen_write_mode_set(sctx, MODE_FOCUSON);
|
||||
if (wp == NULL)
|
||||
break;
|
||||
if (!options_get_number(global_options, "focus-events"))
|
||||
break;
|
||||
if (wp->flags & PANE_FOCUSED)
|
||||
bufferevent_write(wp->event, "\033[I", 3);
|
||||
else
|
||||
bufferevent_write(wp->event, "\033[O", 3);
|
||||
break;
|
||||
case 1005:
|
||||
screen_write_mode_set(sctx, MODE_MOUSE_UTF8);
|
||||
@ -1824,6 +1897,9 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
|
||||
case 2004:
|
||||
screen_write_mode_set(sctx, MODE_BRACKETPASTE);
|
||||
break;
|
||||
case 2031:
|
||||
screen_write_mode_set(sctx, MODE_THEME_UPDATES);
|
||||
break;
|
||||
default:
|
||||
log_debug("%s: unknown '%c'", __func__, ictx->ch);
|
||||
break;
|
||||
@ -1831,6 +1907,26 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle CSI graphics SM. */
|
||||
static void
|
||||
input_csi_dispatch_sm_graphics(__unused struct input_ctx *ictx)
|
||||
{
|
||||
#ifdef ENABLE_SIXEL
|
||||
int n, m, o;
|
||||
|
||||
if (ictx->param_list_len > 3)
|
||||
return;
|
||||
n = input_get(ictx, 0, 0, 0);
|
||||
m = input_get(ictx, 1, 0, 0);
|
||||
o = input_get(ictx, 2, 0, 0);
|
||||
|
||||
if (n == 1 && (m == 1 || m == 2 || m == 4))
|
||||
input_reply(ictx, "\033[?%d;0;%uS", n, SIXEL_COLOUR_REGISTERS);
|
||||
else
|
||||
input_reply(ictx, "\033[?%d;3;%dS", n, o);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Handle CSI window operations. */
|
||||
static void
|
||||
input_csi_dispatch_winops(struct input_ctx *ictx)
|
||||
@ -1838,9 +1934,13 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
struct screen *s = sctx->s;
|
||||
struct window_pane *wp = ictx->wp;
|
||||
struct window *w = NULL;
|
||||
u_int x = screen_size_x(s), y = screen_size_y(s);
|
||||
int n, m;
|
||||
|
||||
if (wp != NULL)
|
||||
w = wp->window;
|
||||
|
||||
m = 0;
|
||||
while ((n = input_get(ictx, m, 0, -1)) != -1) {
|
||||
switch (n) {
|
||||
@ -1851,8 +1951,6 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
|
||||
case 7:
|
||||
case 11:
|
||||
case 13:
|
||||
case 14:
|
||||
case 19:
|
||||
case 20:
|
||||
case 21:
|
||||
case 24:
|
||||
@ -1870,6 +1968,30 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
|
||||
if (input_get(ictx, m, 0, -1) == -1)
|
||||
return;
|
||||
break;
|
||||
case 14:
|
||||
if (w == NULL)
|
||||
break;
|
||||
input_reply(ictx, "\033[4;%u;%ut", y * w->ypixel,
|
||||
x * w->xpixel);
|
||||
break;
|
||||
case 15:
|
||||
if (w == NULL)
|
||||
break;
|
||||
input_reply(ictx, "\033[5;%u;%ut", y * w->ypixel,
|
||||
x * w->xpixel);
|
||||
break;
|
||||
case 16:
|
||||
if (w == NULL)
|
||||
break;
|
||||
input_reply(ictx, "\033[6;%u;%ut", w->ypixel,
|
||||
w->xpixel);
|
||||
break;
|
||||
case 18:
|
||||
input_reply(ictx, "\033[8;%u;%ut", y, x);
|
||||
break;
|
||||
case 19:
|
||||
input_reply(ictx, "\033[9;%u;%ut", y, x);
|
||||
break;
|
||||
case 22:
|
||||
m++;
|
||||
switch (input_get(ictx, m, 0, -1)) {
|
||||
@ -1892,14 +2014,11 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
|
||||
if (wp == NULL)
|
||||
break;
|
||||
notify_pane("pane-title-changed", wp);
|
||||
server_redraw_window_borders(wp->window);
|
||||
server_status_window(wp->window);
|
||||
server_redraw_window_borders(w);
|
||||
server_status_window(w);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 18:
|
||||
input_reply(ictx, "\033[8;%u;%ut", x, y);
|
||||
break;
|
||||
default:
|
||||
log_debug("%s: unknown '%c'", __func__, ictx->ch);
|
||||
break;
|
||||
@ -2070,7 +2189,7 @@ static void
|
||||
input_csi_dispatch_sgr(struct input_ctx *ictx)
|
||||
{
|
||||
struct grid_cell *gc = &ictx->cell.cell;
|
||||
u_int i;
|
||||
u_int i, link;
|
||||
int n;
|
||||
|
||||
if (ictx->param_list_len == 0) {
|
||||
@ -2102,7 +2221,9 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
|
||||
|
||||
switch (n) {
|
||||
case 0:
|
||||
link = gc->link;
|
||||
memcpy(gc, &grid_default_cell, sizeof *gc);
|
||||
gc->link = link;
|
||||
break;
|
||||
case 1:
|
||||
gc->attr |= GRID_ATTR_BRIGHT;
|
||||
@ -2188,7 +2309,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
|
||||
gc->attr &= ~GRID_ATTR_OVERLINE;
|
||||
break;
|
||||
case 59:
|
||||
gc->us = 0;
|
||||
gc->us = 8;
|
||||
break;
|
||||
case 90:
|
||||
case 91:
|
||||
@ -2233,7 +2354,7 @@ input_enter_dcs(struct input_ctx *ictx)
|
||||
|
||||
input_clear(ictx);
|
||||
input_start_timer(ictx);
|
||||
ictx->last = -1;
|
||||
ictx->flags &= ~INPUT_LAST;
|
||||
}
|
||||
|
||||
/* DCS terminator (ST) received. */
|
||||
@ -2246,17 +2367,44 @@ input_dcs_dispatch(struct input_ctx *ictx)
|
||||
size_t len = ictx->input_len;
|
||||
const char prefix[] = "tmux;";
|
||||
const u_int prefixlen = (sizeof prefix) - 1;
|
||||
long long allow_passthrough = 0;
|
||||
#ifdef ENABLE_SIXEL
|
||||
struct window *w;
|
||||
struct sixel_image *si;
|
||||
int p2;
|
||||
#endif
|
||||
|
||||
if (wp == NULL)
|
||||
return (0);
|
||||
if (ictx->flags & INPUT_DISCARD)
|
||||
|
||||
if (ictx->flags & INPUT_DISCARD) {
|
||||
log_debug("%s: %zu bytes (discard)", __func__, len);
|
||||
return (0);
|
||||
if (!options_get_number(ictx->wp->options, "allow-passthrough"))
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
w = wp->window;
|
||||
if (buf[0] == 'q' && ictx->interm_len == 0) {
|
||||
if (input_split(ictx) != 0)
|
||||
return (0);
|
||||
p2 = input_get(ictx, 1, 0, 0);
|
||||
if (p2 == -1)
|
||||
p2 = 0;
|
||||
si = sixel_parse(buf, len, p2, w->xpixel, w->ypixel);
|
||||
if (si != NULL)
|
||||
screen_write_sixelimage(sctx, si, ictx->cell.cell.bg);
|
||||
}
|
||||
#endif
|
||||
|
||||
allow_passthrough = options_get_number(wp->options, "allow-passthrough");
|
||||
if (!allow_passthrough)
|
||||
return (0);
|
||||
log_debug("%s: \"%s\"", __func__, buf);
|
||||
|
||||
if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0)
|
||||
screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen);
|
||||
if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0) {
|
||||
screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen,
|
||||
allow_passthrough == 2);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
@ -2269,7 +2417,7 @@ input_enter_osc(struct input_ctx *ictx)
|
||||
|
||||
input_clear(ictx);
|
||||
input_start_timer(ictx);
|
||||
ictx->last = -1;
|
||||
ictx->flags &= ~INPUT_LAST;
|
||||
}
|
||||
|
||||
/* OSC terminator (ST) received. */
|
||||
@ -2292,13 +2440,17 @@ input_exit_osc(struct input_ctx *ictx)
|
||||
option = 0;
|
||||
while (*p >= '0' && *p <= '9')
|
||||
option = option * 10 + *p++ - '0';
|
||||
if (*p != ';' && *p != '\0')
|
||||
return;
|
||||
if (*p == ';')
|
||||
p++;
|
||||
|
||||
switch (option) {
|
||||
case 0:
|
||||
case 2:
|
||||
if (screen_set_title(sctx->s, p) && wp != NULL) {
|
||||
if (wp != NULL &&
|
||||
options_get_number(wp->options, "allow-set-title") &&
|
||||
screen_set_title(sctx->s, p)) {
|
||||
notify_pane("pane-title-changed", wp);
|
||||
server_redraw_window_borders(wp->window);
|
||||
server_status_window(wp->window);
|
||||
@ -2316,6 +2468,9 @@ input_exit_osc(struct input_ctx *ictx)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
input_osc_8(ictx, p);
|
||||
break;
|
||||
case 10:
|
||||
input_osc_10(ictx, p);
|
||||
break;
|
||||
@ -2340,6 +2495,9 @@ input_exit_osc(struct input_ctx *ictx)
|
||||
case 112:
|
||||
input_osc_112(ictx, p);
|
||||
break;
|
||||
case 133:
|
||||
input_osc_133(ictx, p);
|
||||
break;
|
||||
default:
|
||||
log_debug("%s: unknown '%u'", __func__, option);
|
||||
break;
|
||||
@ -2354,7 +2512,7 @@ input_enter_apc(struct input_ctx *ictx)
|
||||
|
||||
input_clear(ictx);
|
||||
input_start_timer(ictx);
|
||||
ictx->last = -1;
|
||||
ictx->flags &= ~INPUT_LAST;
|
||||
}
|
||||
|
||||
/* APC terminator (ST) received. */
|
||||
@ -2383,7 +2541,7 @@ input_enter_rename(struct input_ctx *ictx)
|
||||
|
||||
input_clear(ictx);
|
||||
input_start_timer(ictx);
|
||||
ictx->last = -1;
|
||||
ictx->flags &= ~INPUT_LAST;
|
||||
}
|
||||
|
||||
/* Rename terminator (ST) received. */
|
||||
@ -2427,7 +2585,7 @@ input_top_bit_set(struct input_ctx *ictx)
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
struct utf8_data *ud = &ictx->utf8data;
|
||||
|
||||
ictx->last = -1;
|
||||
ictx->flags &= ~INPUT_LAST;
|
||||
|
||||
if (!ictx->utf8started) {
|
||||
if (utf8_open(ud, ictx->ch) != UTF8_MORE)
|
||||
@ -2453,50 +2611,12 @@ input_top_bit_set(struct input_ctx *ictx)
|
||||
utf8_copy(&ictx->cell.cell.data, ud);
|
||||
screen_write_collect_add(sctx, &ictx->cell.cell);
|
||||
|
||||
utf8_copy(&ictx->last, &ictx->cell.cell.data);
|
||||
ictx->flags |= INPUT_LAST;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Parse colour from OSC. */
|
||||
static int
|
||||
input_osc_parse_colour(const char *p)
|
||||
{
|
||||
double c, m, y, k = 0;
|
||||
u_int r, g, b;
|
||||
size_t len = strlen(p);
|
||||
int colour = -1;
|
||||
char *copy;
|
||||
|
||||
if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) ||
|
||||
(len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) ||
|
||||
sscanf(p, "%d,%d,%d", &r, &g, &b) == 3)
|
||||
colour = colour_join_rgb(r, g, b);
|
||||
else if ((len == 18 &&
|
||||
sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) ||
|
||||
(len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3))
|
||||
colour = colour_join_rgb(r >> 8, g >> 8, b >> 8);
|
||||
else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 ||
|
||||
sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) &&
|
||||
c >= 0 && c <= 1 && m >= 0 && m <= 1 &&
|
||||
y >= 0 && y <= 1 && k >= 0 && k <= 1) {
|
||||
colour = colour_join_rgb(
|
||||
(1 - c) * (1 - k) * 255,
|
||||
(1 - m) * (1 - k) * 255,
|
||||
(1 - y) * (1 - k) * 255);
|
||||
} else {
|
||||
while (len != 0 && *p == ' ') {
|
||||
p++;
|
||||
len--;
|
||||
}
|
||||
while (len != 0 && p[len - 1] == ' ')
|
||||
len--;
|
||||
copy = xstrndup(p, len);
|
||||
colour = colour_byname(copy);
|
||||
free(copy);
|
||||
}
|
||||
log_debug("%s: %s = %s", __func__, p, colour_tostring(colour));
|
||||
return (colour);
|
||||
}
|
||||
|
||||
/* Reply to a colour request. */
|
||||
static void
|
||||
input_osc_colour_reply(struct input_ctx *ictx, u_int n, int c)
|
||||
@ -2545,7 +2665,7 @@ input_osc_4(struct input_ctx *ictx, const char *p)
|
||||
input_osc_colour_reply(ictx, 4, c);
|
||||
continue;
|
||||
}
|
||||
if ((c = input_osc_parse_colour(s)) == -1) {
|
||||
if ((c = colour_parseX11(s)) == -1) {
|
||||
s = next;
|
||||
continue;
|
||||
}
|
||||
@ -2560,6 +2680,48 @@ input_osc_4(struct input_ctx *ictx, const char *p)
|
||||
free(copy);
|
||||
}
|
||||
|
||||
/* Handle the OSC 8 sequence for embedding hyperlinks. */
|
||||
static void
|
||||
input_osc_8(struct input_ctx *ictx, const char *p)
|
||||
{
|
||||
struct hyperlinks *hl = ictx->ctx.s->hyperlinks;
|
||||
struct grid_cell *gc = &ictx->cell.cell;
|
||||
const char *start, *end, *uri;
|
||||
char *id = NULL;
|
||||
|
||||
for (start = p; (end = strpbrk(start, ":;")) != NULL; start = end + 1) {
|
||||
if (end - start >= 4 && strncmp(start, "id=", 3) == 0) {
|
||||
if (id != NULL)
|
||||
goto bad;
|
||||
id = xstrndup(start + 3, end - start - 3);
|
||||
}
|
||||
|
||||
/* The first ; is the end of parameters and start of the URI. */
|
||||
if (*end == ';')
|
||||
break;
|
||||
}
|
||||
if (end == NULL || *end != ';')
|
||||
goto bad;
|
||||
uri = end + 1;
|
||||
if (*uri == '\0') {
|
||||
gc->link = 0;
|
||||
free(id);
|
||||
return;
|
||||
}
|
||||
gc->link = hyperlinks_put(hl, uri, id);
|
||||
if (id == NULL)
|
||||
log_debug("hyperlink (anonymous) %s = %u", uri, gc->link);
|
||||
else
|
||||
log_debug("hyperlink (id=%s) %s = %u", id, uri, gc->link);
|
||||
free(id);
|
||||
return;
|
||||
|
||||
bad:
|
||||
log_debug("bad OSC 8 %s", p);
|
||||
free(id);
|
||||
}
|
||||
|
||||
|
||||
/* Handle the OSC 10 sequence for setting and querying foreground colour. */
|
||||
static void
|
||||
input_osc_10(struct input_ctx *ictx, const char *p)
|
||||
@ -2569,14 +2731,21 @@ input_osc_10(struct input_ctx *ictx, const char *p)
|
||||
int c;
|
||||
|
||||
if (strcmp(p, "?") == 0) {
|
||||
if (wp != NULL) {
|
||||
if (wp == NULL)
|
||||
return;
|
||||
c = window_pane_get_fg_control_client(wp);
|
||||
if (c == -1) {
|
||||
tty_default_colours(&defaults, wp);
|
||||
input_osc_colour_reply(ictx, 10, defaults.fg);
|
||||
if (COLOUR_DEFAULT(defaults.fg))
|
||||
c = window_pane_get_fg(wp);
|
||||
else
|
||||
c = defaults.fg;
|
||||
}
|
||||
input_osc_colour_reply(ictx, 10, c);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((c = input_osc_parse_colour(p)) == -1) {
|
||||
if ((c = colour_parseX11(p)) == -1) {
|
||||
log_debug("bad OSC 10: %s", p);
|
||||
return;
|
||||
}
|
||||
@ -2609,25 +2778,24 @@ static void
|
||||
input_osc_11(struct input_ctx *ictx, const char *p)
|
||||
{
|
||||
struct window_pane *wp = ictx->wp;
|
||||
struct grid_cell defaults;
|
||||
int c;
|
||||
|
||||
if (strcmp(p, "?") == 0) {
|
||||
if (wp != NULL) {
|
||||
tty_default_colours(&defaults, wp);
|
||||
input_osc_colour_reply(ictx, 11, defaults.bg);
|
||||
}
|
||||
if (wp == NULL)
|
||||
return;
|
||||
c = window_pane_get_bg(wp);
|
||||
input_osc_colour_reply(ictx, 11, c);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((c = input_osc_parse_colour(p)) == -1) {
|
||||
if ((c = colour_parseX11(p)) == -1) {
|
||||
log_debug("bad OSC 11: %s", p);
|
||||
return;
|
||||
}
|
||||
if (ictx->palette != NULL) {
|
||||
ictx->palette->bg = c;
|
||||
if (wp != NULL)
|
||||
wp->flags |= PANE_STYLECHANGED;
|
||||
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
|
||||
screen_write_fullredraw(&ictx->ctx);
|
||||
}
|
||||
}
|
||||
@ -2643,7 +2811,7 @@ input_osc_111(struct input_ctx *ictx, const char *p)
|
||||
if (ictx->palette != NULL) {
|
||||
ictx->palette->bg = 8;
|
||||
if (wp != NULL)
|
||||
wp->flags |= PANE_STYLECHANGED;
|
||||
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
|
||||
screen_write_fullredraw(&ictx->ctx);
|
||||
}
|
||||
}
|
||||
@ -2665,7 +2833,7 @@ input_osc_12(struct input_ctx *ictx, const char *p)
|
||||
return;
|
||||
}
|
||||
|
||||
if ((c = input_osc_parse_colour(p)) == -1) {
|
||||
if ((c = colour_parseX11(p)) == -1) {
|
||||
log_debug("bad OSC 12: %s", p);
|
||||
return;
|
||||
}
|
||||
@ -2680,6 +2848,27 @@ input_osc_112(struct input_ctx *ictx, const char *p)
|
||||
screen_set_cursor_colour(ictx->ctx.s, -1);
|
||||
}
|
||||
|
||||
/* Handle the OSC 133 sequence. */
|
||||
static void
|
||||
input_osc_133(struct input_ctx *ictx, const char *p)
|
||||
{
|
||||
struct grid *gd = ictx->ctx.s->grid;
|
||||
u_int line = ictx->ctx.s->cy + gd->hsize;
|
||||
struct grid_line *gl;
|
||||
|
||||
if (line > gd->hsize + gd->sy - 1)
|
||||
return;
|
||||
gl = grid_get_line(gd, line);
|
||||
|
||||
switch (*p) {
|
||||
case 'A':
|
||||
gl->flags |= GRID_LINE_START_PROMPT;
|
||||
break;
|
||||
case 'C':
|
||||
gl->flags |= GRID_LINE_START_OUTPUT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle the OSC 52 sequence for setting the clipboard. */
|
||||
static void
|
||||
@ -2693,6 +2882,9 @@ input_osc_52(struct input_ctx *ictx, const char *p)
|
||||
int outlen, state;
|
||||
struct screen_write_ctx ctx;
|
||||
struct paste_buffer *pb;
|
||||
const char* allow = "cpqs01234567";
|
||||
char flags[sizeof "cpqs01234567"] = "";
|
||||
u_int i, j = 0;
|
||||
|
||||
if (wp == NULL)
|
||||
return;
|
||||
@ -2707,6 +2899,12 @@ input_osc_52(struct input_ctx *ictx, const char *p)
|
||||
return;
|
||||
log_debug("%s: %s", __func__, end);
|
||||
|
||||
for (i = 0; p + i != end; i++) {
|
||||
if (strchr(allow, p[i]) != NULL && strchr(flags, p[i]) == NULL)
|
||||
flags[j++] = p[i];
|
||||
}
|
||||
log_debug("%s: %.*s %s", __func__, (int)(end - p - 1), p, flags);
|
||||
|
||||
if (strcmp(end, "?") == 0) {
|
||||
if ((pb = paste_get_top(NULL)) != NULL)
|
||||
buf = paste_buffer_data(pb, &len);
|
||||
@ -2728,7 +2926,7 @@ input_osc_52(struct input_ctx *ictx, const char *p)
|
||||
}
|
||||
|
||||
screen_write_start_pane(&ctx, wp, NULL);
|
||||
screen_write_setselection(&ctx, out, outlen);
|
||||
screen_write_setselection(&ctx, flags, out, outlen);
|
||||
screen_write_stop(&ctx);
|
||||
notify_pane("pane-set-clipboard", wp);
|
||||
|
||||
@ -2777,9 +2975,11 @@ input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len,
|
||||
const char *end)
|
||||
{
|
||||
char *out = NULL;
|
||||
size_t outlen = 0;
|
||||
int outlen = 0;
|
||||
|
||||
if (buf != NULL && len != 0) {
|
||||
if (len >= ((size_t)INT_MAX * 3 / 4) - 1)
|
||||
return;
|
||||
outlen = 4 * ((len + 2) / 3) + 1;
|
||||
out = xmalloc(outlen);
|
||||
if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) {
|
||||
@ -2794,3 +2994,26 @@ input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len,
|
||||
bufferevent_write(bev, end, strlen(end));
|
||||
free(out);
|
||||
}
|
||||
|
||||
/* Set input buffer size. */
|
||||
void
|
||||
input_set_buffer_size(size_t buffer_size)
|
||||
{
|
||||
log_debug("%s: %lu -> %lu", __func__, input_buffer_size, buffer_size);
|
||||
input_buffer_size = buffer_size;
|
||||
}
|
||||
|
||||
static void
|
||||
input_report_current_theme(struct input_ctx *ictx)
|
||||
{
|
||||
switch (window_pane_get_theme(ictx->wp)) {
|
||||
case THEME_DARK:
|
||||
input_reply(ictx, "\033[?997;1n");
|
||||
break;
|
||||
case THEME_LIGHT:
|
||||
input_reply(ictx, "\033[?997;2n");
|
||||
break;
|
||||
case THEME_UNKNOWN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
77
job.c
77
job.c
@ -69,27 +69,43 @@ static LIST_HEAD(joblist, job) all_jobs = LIST_HEAD_INITIALIZER(all_jobs);
|
||||
|
||||
/* Start a job running. */
|
||||
struct job *
|
||||
job_run(const char *cmd, int argc, char **argv, struct environ *e, struct session *s,
|
||||
const char *cwd, job_update_cb updatecb, job_complete_cb completecb,
|
||||
job_free_cb freecb, void *data, int flags, int sx, int sy)
|
||||
job_run(const char *cmd, int argc, char **argv, struct environ *e,
|
||||
struct session *s, const char *cwd, job_update_cb updatecb,
|
||||
job_complete_cb completecb, job_free_cb freecb, void *data, int flags,
|
||||
int sx, int sy)
|
||||
{
|
||||
struct job *job;
|
||||
struct environ *env;
|
||||
pid_t pid;
|
||||
int nullfd, out[2], master;
|
||||
const char *home;
|
||||
int nullfd, out[2], master, do_close = 1;
|
||||
const char *home, *shell;
|
||||
sigset_t set, oldset;
|
||||
struct winsize ws;
|
||||
char **argvp, tty[TTY_NAME_MAX];
|
||||
char **argvp, tty[TTY_NAME_MAX], *argv0;
|
||||
struct options *oo;
|
||||
|
||||
/*
|
||||
* Do not set TERM during .tmux.conf, it is nice to be able to use
|
||||
* if-shell to decide on default-terminal based on outside TERM.
|
||||
* Do not set TERM during .tmux.conf (second argument here), it is nice
|
||||
* to be able to use if-shell to decide on default-terminal based on
|
||||
* outside TERM.
|
||||
*/
|
||||
env = environ_for_session(s, !cfg_finished);
|
||||
if (e != NULL)
|
||||
environ_copy(e, env);
|
||||
|
||||
if (~flags & JOB_DEFAULTSHELL)
|
||||
shell = _PATH_BSHELL;
|
||||
else {
|
||||
if (s != NULL)
|
||||
oo = s->options;
|
||||
else
|
||||
oo = global_s_options;
|
||||
shell = options_get_string(oo, "default-shell");
|
||||
if (!checkshell(shell))
|
||||
shell = _PATH_BSHELL;
|
||||
}
|
||||
argv0 = shell_argv0(shell, 0);
|
||||
|
||||
sigfillset(&set);
|
||||
sigprocmask(SIG_BLOCK, &set, &oldset);
|
||||
|
||||
@ -105,10 +121,11 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
|
||||
}
|
||||
if (cmd == NULL) {
|
||||
cmd_log_argv(argc, argv, "%s:", __func__);
|
||||
log_debug("%s: cwd=%s", __func__, cwd == NULL ? "" : cwd);
|
||||
log_debug("%s: cwd=%s, shell=%s", __func__,
|
||||
cwd == NULL ? "" : cwd, shell);
|
||||
} else {
|
||||
log_debug("%s: cmd=%s, cwd=%s", __func__, cmd,
|
||||
cwd == NULL ? "" : cwd);
|
||||
log_debug("%s: cmd=%s, cwd=%s, shell=%s", __func__, cmd,
|
||||
cwd == NULL ? "" : cwd, shell);
|
||||
}
|
||||
|
||||
switch (pid) {
|
||||
@ -133,24 +150,33 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
|
||||
if (~flags & JOB_PTY) {
|
||||
if (dup2(out[1], STDIN_FILENO) == -1)
|
||||
fatal("dup2 failed");
|
||||
do_close = do_close && out[1] != STDIN_FILENO;
|
||||
if (dup2(out[1], STDOUT_FILENO) == -1)
|
||||
fatal("dup2 failed");
|
||||
if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO)
|
||||
do_close = do_close && out[1] != STDOUT_FILENO;
|
||||
if (flags & JOB_SHOWSTDERR) {
|
||||
if (dup2(out[1], STDERR_FILENO) == -1)
|
||||
fatal("dup2 failed");
|
||||
do_close = do_close && out[1] != STDERR_FILENO;
|
||||
} else {
|
||||
nullfd = open(_PATH_DEVNULL, O_RDWR);
|
||||
if (nullfd == -1)
|
||||
fatal("open failed");
|
||||
if (dup2(nullfd, STDERR_FILENO) == -1)
|
||||
fatal("dup2 failed");
|
||||
if (nullfd != STDERR_FILENO)
|
||||
close(nullfd);
|
||||
}
|
||||
if (do_close)
|
||||
close(out[1]);
|
||||
close(out[0]);
|
||||
|
||||
nullfd = open(_PATH_DEVNULL, O_RDWR);
|
||||
if (nullfd == -1)
|
||||
fatal("open failed");
|
||||
if (dup2(nullfd, STDERR_FILENO) == -1)
|
||||
fatal("dup2 failed");
|
||||
if (nullfd != STDERR_FILENO)
|
||||
close(nullfd);
|
||||
}
|
||||
closefrom(STDERR_FILENO + 1);
|
||||
|
||||
if (cmd != NULL) {
|
||||
execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
|
||||
if (flags & JOB_DEFAULTSHELL)
|
||||
setenv("SHELL", shell, 1);
|
||||
execl(shell, argv0, "-c", cmd, (char *)NULL);
|
||||
fatal("execl failed");
|
||||
} else {
|
||||
argvp = cmd_copy_argv(argc, argv);
|
||||
@ -161,8 +187,9 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
|
||||
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
environ_free(env);
|
||||
free(argv0);
|
||||
|
||||
job = xmalloc(sizeof *job);
|
||||
job = xcalloc(1, sizeof *job);
|
||||
job->state = JOB_RUNNING;
|
||||
job->flags = flags;
|
||||
|
||||
@ -171,7 +198,8 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
|
||||
else
|
||||
job->cmd = cmd_stringify_argv(argc, argv);
|
||||
job->pid = pid;
|
||||
strlcpy(job->tty, tty, sizeof job->tty);
|
||||
if (flags & JOB_PTY)
|
||||
strlcpy(job->tty, tty, sizeof job->tty);
|
||||
job->status = 0;
|
||||
|
||||
LIST_INSERT_HEAD(&all_jobs, job, entry);
|
||||
@ -194,12 +222,13 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
|
||||
fatalx("out of memory");
|
||||
bufferevent_enable(job->event, EV_READ|EV_WRITE);
|
||||
|
||||
log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid);
|
||||
log_debug("run job %p: %s, pid %ld", job, job->cmd, (long)job->pid);
|
||||
return (job);
|
||||
|
||||
fail:
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
environ_free(env);
|
||||
free(argv0);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
@ -49,11 +49,14 @@
|
||||
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Top,}' '<' {send -X history-top}" \
|
||||
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}' '>' {send -X history-bottom}" \
|
||||
" ''" \
|
||||
" '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {if -F '#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}' 'copy-mode -t='; send -Xt= search-backward \"#{q:mouse_word}\"}" \
|
||||
" '#{?mouse_word,Search For #[underscore]#{=/9/...:mouse_word},}' 'C-r' {if -F '#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}' 'copy-mode -t='; send -Xt= search-backward -- \"#{q:mouse_word}\"}" \
|
||||
" '#{?mouse_word,Type #[underscore]#{=/9/...:mouse_word},}' 'C-y' {copy-mode -q; send-keys -l -- \"#{q:mouse_word}\"}" \
|
||||
" '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {copy-mode -q; set-buffer -- \"#{q:mouse_word}\"}" \
|
||||
" '#{?mouse_line,Copy Line,}' 'l' {copy-mode -q; set-buffer -- \"#{q:mouse_line}\"}" \
|
||||
" ''" \
|
||||
" '#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}' 'C-h' {copy-mode -q; send-keys -l -- \"#{q:mouse_hyperlink}\"}" \
|
||||
" '#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}' 'h' {copy-mode -q; set-buffer -- \"#{q:mouse_hyperlink}\"}" \
|
||||
" ''" \
|
||||
" 'Horizontal Split' 'h' {split-window -h}" \
|
||||
" 'Vertical Split' 'v' {split-window -v}" \
|
||||
" ''" \
|
||||
@ -194,11 +197,12 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat,
|
||||
bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS);
|
||||
if (cmdlist == NULL) {
|
||||
if (bd != NULL) {
|
||||
free((void *)bd->note);
|
||||
if (note != NULL)
|
||||
if (note != NULL) {
|
||||
free((void *)bd->note);
|
||||
bd->note = xstrdup(note);
|
||||
else
|
||||
bd->note = NULL;
|
||||
}
|
||||
if (repeat)
|
||||
bd->flags |= KEY_BINDING_REPEAT;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -341,7 +345,7 @@ key_bindings_init_done(__unused struct cmdq_item *item, __unused void *data)
|
||||
void
|
||||
key_bindings_init(void)
|
||||
{
|
||||
static const char *defaults[] = {
|
||||
static const char *const defaults[] = {
|
||||
/* Prefix keys. */
|
||||
"bind -N 'Send the prefix key' C-b { send-prefix }",
|
||||
"bind -N 'Rotate through the panes' C-o { rotate-window }",
|
||||
@ -374,7 +378,7 @@ key_bindings_init(void)
|
||||
"bind -N 'Move to the previously active pane' \\; { last-pane }",
|
||||
"bind -N 'Choose a paste buffer from a list' = { choose-buffer -Z }",
|
||||
"bind -N 'List key bindings' ? { list-keys -N }",
|
||||
"bind -N 'Choose a client from a list' D { choose-client -Z }",
|
||||
"bind -N 'Choose and detach a client from a list' D { choose-client -Z }",
|
||||
"bind -N 'Spread panes out evenly' E { select-layout -E }",
|
||||
"bind -N 'Switch to the last client' L { switch-client -l }",
|
||||
"bind -N 'Clear the marked pane' M { select-pane -M }",
|
||||
@ -410,6 +414,8 @@ key_bindings_init(void)
|
||||
"bind -N 'Set the main-horizontal layout' M-3 { select-layout main-horizontal }",
|
||||
"bind -N 'Set the main-vertical layout' M-4 { select-layout main-vertical }",
|
||||
"bind -N 'Select the tiled layout' M-5 { select-layout tiled }",
|
||||
"bind -N 'Set the main-horizontal-mirrored layout' M-6 { select-layout main-horizontal-mirrored }",
|
||||
"bind -N 'Set the main-vertical-mirrored layout' M-7 { select-layout main-vertical-mirrored }",
|
||||
"bind -N 'Select the next window with an alert' M-n { next-window -a }",
|
||||
"bind -N 'Rotate through the panes in reverse' M-o { rotate-window -D }",
|
||||
"bind -N 'Select the previous window with an alert' M-p { previous-window -a }",
|
||||
@ -428,8 +434,9 @@ key_bindings_init(void)
|
||||
"bind -N 'Resize the pane right' -r C-Right { resize-pane -R }",
|
||||
|
||||
/* Menu keys */
|
||||
"bind < { display-menu -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU " }",
|
||||
"bind > { display-menu -xP -yP -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }",
|
||||
"bind -N 'Display window menu' < { display-menu -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU " }",
|
||||
"bind -N 'Display pane menu' > { display-menu -xP -yP -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }",
|
||||
|
||||
|
||||
/* Mouse button 1 down on pane. */
|
||||
"bind -n MouseDown1Pane { select-pane -t=; send -M }",
|
||||
@ -463,14 +470,21 @@ key_bindings_init(void)
|
||||
|
||||
/* Mouse button 3 down on status left. */
|
||||
"bind -n MouseDown3StatusLeft { display-menu -t= -xM -yW -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU " }",
|
||||
"bind -n M-MouseDown3StatusLeft { display-menu -t= -xM -yW -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU " }",
|
||||
|
||||
/* Mouse button 3 down on status line. */
|
||||
"bind -n MouseDown3Status { display-menu -t= -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU "}",
|
||||
"bind -n M-MouseDown3Status { display-menu -t= -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU "}",
|
||||
|
||||
/* Mouse button 3 down on pane. */
|
||||
"bind -n MouseDown3Pane { if -Ft= '#{||:#{mouse_any_flag},#{&&:#{pane_in_mode},#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}}}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " } }",
|
||||
"bind -n M-MouseDown3Pane { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " }",
|
||||
|
||||
/* Mouse on scrollbar. */
|
||||
"bind -n MouseDown1ScrollbarUp { copy-mode -u }",
|
||||
"bind -n MouseDown1ScrollbarDown { copy-mode -d }",
|
||||
"bind -n MouseDrag1ScrollbarSlider { copy-mode -S }",
|
||||
|
||||
/* Copy mode (emacs) keys. */
|
||||
"bind -Tcopy-mode C-Space { send -X begin-selection }",
|
||||
"bind -Tcopy-mode C-a { send -X start-of-line }",
|
||||
@ -482,26 +496,26 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode C-k { send -X copy-pipe-end-of-line-and-cancel }",
|
||||
"bind -Tcopy-mode C-n { send -X cursor-down }",
|
||||
"bind -Tcopy-mode C-p { send -X cursor-up }",
|
||||
"bind -Tcopy-mode C-r { command-prompt -T search -ip'(search up)' -I'#{pane_search_string}' { send -X search-backward-incremental '%%' } }",
|
||||
"bind -Tcopy-mode C-s { command-prompt -T search -ip'(search down)' -I'#{pane_search_string}' { send -X search-forward-incremental '%%' } }",
|
||||
"bind -Tcopy-mode C-r { command-prompt -T search -ip'(search up)' -I'#{pane_search_string}' { send -X search-backward-incremental -- '%%' } }",
|
||||
"bind -Tcopy-mode C-s { command-prompt -T search -ip'(search down)' -I'#{pane_search_string}' { send -X search-forward-incremental -- '%%' } }",
|
||||
"bind -Tcopy-mode C-v { send -X page-down }",
|
||||
"bind -Tcopy-mode C-w { send -X copy-pipe-and-cancel }",
|
||||
"bind -Tcopy-mode Escape { send -X cancel }",
|
||||
"bind -Tcopy-mode Space { send -X page-down }",
|
||||
"bind -Tcopy-mode , { send -X jump-reverse }",
|
||||
"bind -Tcopy-mode \\; { send -X jump-again }",
|
||||
"bind -Tcopy-mode F { command-prompt -1p'(jump backward)' { send -X jump-backward '%%' } }",
|
||||
"bind -Tcopy-mode F { command-prompt -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
|
||||
"bind -Tcopy-mode N { send -X search-reverse }",
|
||||
"bind -Tcopy-mode P { send -X toggle-position }",
|
||||
"bind -Tcopy-mode R { send -X rectangle-toggle }",
|
||||
"bind -Tcopy-mode T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward '%%' } }",
|
||||
"bind -Tcopy-mode T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
|
||||
"bind -Tcopy-mode X { send -X set-mark }",
|
||||
"bind -Tcopy-mode f { command-prompt -1p'(jump forward)' { send -X jump-forward '%%' } }",
|
||||
"bind -Tcopy-mode g { command-prompt -p'(goto line)' { send -X goto-line '%%' } }",
|
||||
"bind -Tcopy-mode f { command-prompt -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
|
||||
"bind -Tcopy-mode g { command-prompt -p'(goto line)' { send -X goto-line -- '%%' } }",
|
||||
"bind -Tcopy-mode n { send -X search-again }",
|
||||
"bind -Tcopy-mode q { send -X cancel }",
|
||||
"bind -Tcopy-mode r { send -X refresh-from-pane }",
|
||||
"bind -Tcopy-mode t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward '%%' } }",
|
||||
"bind -Tcopy-mode t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
|
||||
"bind -Tcopy-mode Home { send -X start-of-line }",
|
||||
"bind -Tcopy-mode End { send -X end-of-line }",
|
||||
"bind -Tcopy-mode MouseDown1Pane select-pane",
|
||||
@ -546,8 +560,8 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode C-Down { send -X scroll-down }",
|
||||
|
||||
/* Copy mode (vi) keys. */
|
||||
"bind -Tcopy-mode-vi '#' { send -FX search-backward '#{copy_cursor_word}' }",
|
||||
"bind -Tcopy-mode-vi * { send -FX search-forward '#{copy_cursor_word}' }",
|
||||
"bind -Tcopy-mode-vi '#' { send -FX search-backward -- '#{copy_cursor_word}' }",
|
||||
"bind -Tcopy-mode-vi * { send -FX search-forward -- '#{copy_cursor_word}' }",
|
||||
"bind -Tcopy-mode-vi C-c { send -X cancel }",
|
||||
"bind -Tcopy-mode-vi C-d { send -X halfpage-down }",
|
||||
"bind -Tcopy-mode-vi C-e { send -X scroll-down }",
|
||||
@ -563,7 +577,7 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode-vi Space { send -X begin-selection }",
|
||||
"bind -Tcopy-mode-vi '$' { send -X end-of-line }",
|
||||
"bind -Tcopy-mode-vi , { send -X jump-reverse }",
|
||||
"bind -Tcopy-mode-vi / { command-prompt -T search -p'(search down)' { send -X search-forward '%%' } }",
|
||||
"bind -Tcopy-mode-vi / { command-prompt -T search -p'(search down)' { send -X search-forward -- '%%' } }",
|
||||
"bind -Tcopy-mode-vi 0 { send -X start-of-line }",
|
||||
"bind -Tcopy-mode-vi 1 { command-prompt -Np'(repeat)' -I1 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi 2 { command-prompt -Np'(repeat)' -I2 { send -N '%%' } }",
|
||||
@ -574,14 +588,14 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode-vi 7 { command-prompt -Np'(repeat)' -I7 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi 8 { command-prompt -Np'(repeat)' -I8 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi 9 { command-prompt -Np'(repeat)' -I9 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi : { command-prompt -p'(goto line)' { send -X goto-line '%%' } }",
|
||||
"bind -Tcopy-mode-vi : { command-prompt -p'(goto line)' { send -X goto-line -- '%%' } }",
|
||||
"bind -Tcopy-mode-vi \\; { send -X jump-again }",
|
||||
"bind -Tcopy-mode-vi ? { command-prompt -T search -p'(search up)' { send -X search-backward '%%' } }",
|
||||
"bind -Tcopy-mode-vi ? { command-prompt -T search -p'(search up)' { send -X search-backward -- '%%' } }",
|
||||
"bind -Tcopy-mode-vi A { send -X append-selection-and-cancel }",
|
||||
"bind -Tcopy-mode-vi B { send -X previous-space }",
|
||||
"bind -Tcopy-mode-vi D { send -X copy-pipe-end-of-line-and-cancel }",
|
||||
"bind -Tcopy-mode-vi E { send -X next-space-end }",
|
||||
"bind -Tcopy-mode-vi F { command-prompt -1p'(jump backward)' { send -X jump-backward '%%' } }",
|
||||
"bind -Tcopy-mode-vi F { command-prompt -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
|
||||
"bind -Tcopy-mode-vi G { send -X history-bottom }",
|
||||
"bind -Tcopy-mode-vi H { send -X top-line }",
|
||||
"bind -Tcopy-mode-vi J { send -X scroll-down }",
|
||||
@ -590,29 +604,32 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode-vi M { send -X middle-line }",
|
||||
"bind -Tcopy-mode-vi N { send -X search-reverse }",
|
||||
"bind -Tcopy-mode-vi P { send -X toggle-position }",
|
||||
"bind -Tcopy-mode-vi T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward '%%' } }",
|
||||
"bind -Tcopy-mode-vi T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
|
||||
"bind -Tcopy-mode-vi V { send -X select-line }",
|
||||
"bind -Tcopy-mode-vi W { send -X next-space }",
|
||||
"bind -Tcopy-mode-vi X { send -X set-mark }",
|
||||
"bind -Tcopy-mode-vi ^ { send -X back-to-indentation }",
|
||||
"bind -Tcopy-mode-vi b { send -X previous-word }",
|
||||
"bind -Tcopy-mode-vi e { send -X next-word-end }",
|
||||
"bind -Tcopy-mode-vi f { command-prompt -1p'(jump forward)' { send -X jump-forward '%%' } }",
|
||||
"bind -Tcopy-mode-vi f { command-prompt -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
|
||||
"bind -Tcopy-mode-vi g { send -X history-top }",
|
||||
"bind -Tcopy-mode-vi h { send -X cursor-left }",
|
||||
"bind -Tcopy-mode-vi j { send -X cursor-down }",
|
||||
"bind -Tcopy-mode-vi k { send -X cursor-up }",
|
||||
"bind -Tcopy-mode-vi z { send -X scroll-middle }",
|
||||
"bind -Tcopy-mode-vi l { send -X cursor-right }",
|
||||
"bind -Tcopy-mode-vi n { send -X search-again }",
|
||||
"bind -Tcopy-mode-vi o { send -X other-end }",
|
||||
"bind -Tcopy-mode-vi q { send -X cancel }",
|
||||
"bind -Tcopy-mode-vi r { send -X refresh-from-pane }",
|
||||
"bind -Tcopy-mode-vi t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward '%%' } }",
|
||||
"bind -Tcopy-mode-vi t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
|
||||
"bind -Tcopy-mode-vi v { send -X rectangle-toggle }",
|
||||
"bind -Tcopy-mode-vi w { send -X next-word }",
|
||||
"bind -Tcopy-mode-vi '{' { send -X previous-paragraph }",
|
||||
"bind -Tcopy-mode-vi '}' { send -X next-paragraph }",
|
||||
"bind -Tcopy-mode-vi % { send -X next-matching-bracket }",
|
||||
"bind -Tcopy-mode-vi Home { send -X start-of-line }",
|
||||
"bind -Tcopy-mode-vi End { send -X end-of-line }",
|
||||
"bind -Tcopy-mode-vi MouseDown1Pane { select-pane }",
|
||||
"bind -Tcopy-mode-vi MouseDrag1Pane { select-pane; send -X begin-selection }",
|
||||
"bind -Tcopy-mode-vi MouseDragEnd1Pane { send -X copy-pipe-and-cancel }",
|
||||
|
89
key-string.c
89
key-string.c
@ -18,6 +18,7 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
@ -56,12 +57,47 @@ static const struct {
|
||||
{ "PPage", KEYC_PPAGE|KEYC_IMPLIED_META },
|
||||
{ "PageUp", KEYC_PPAGE|KEYC_IMPLIED_META },
|
||||
{ "PgUp", KEYC_PPAGE|KEYC_IMPLIED_META },
|
||||
{ "Tab", '\011' },
|
||||
{ "BTab", KEYC_BTAB },
|
||||
{ "Space", ' ' },
|
||||
{ "BSpace", KEYC_BSPACE },
|
||||
{ "Enter", '\r' },
|
||||
{ "Escape", '\033' },
|
||||
|
||||
/*
|
||||
* C0 control characters, with the exception of Tab, Enter,
|
||||
* and Esc, should never appear as keys. We still render them,
|
||||
* so to be able to spot them in logs in case of an abnormality.
|
||||
*/
|
||||
{ "[NUL]", C0_NUL },
|
||||
{ "[SOH]", C0_SOH },
|
||||
{ "[STX]", C0_STX },
|
||||
{ "[ETX]", C0_ETX },
|
||||
{ "[EOT]", C0_EOT },
|
||||
{ "[ENQ]", C0_ENQ },
|
||||
{ "[ASC]", C0_ASC },
|
||||
{ "[BEL]", C0_BEL },
|
||||
{ "[BS]", C0_BS },
|
||||
{ "Tab", C0_HT },
|
||||
{ "[LF]", C0_LF },
|
||||
{ "[VT]", C0_VT },
|
||||
{ "[FF]", C0_FF },
|
||||
{ "Enter", C0_CR },
|
||||
{ "[SO]", C0_SO },
|
||||
{ "[SI]", C0_SI },
|
||||
{ "[DLE]", C0_DLE },
|
||||
{ "[DC1]", C0_DC1 },
|
||||
{ "[DC2]", C0_DC2 },
|
||||
{ "[DC3]", C0_DC3 },
|
||||
{ "[DC4]", C0_DC4 },
|
||||
{ "[NAK]", C0_NAK },
|
||||
{ "[SYN]", C0_SYN },
|
||||
{ "[ETB]", C0_ETB },
|
||||
{ "[CAN]", C0_CAN },
|
||||
{ "[EM]", C0_EM },
|
||||
{ "[SUB]", C0_SUB },
|
||||
{ "Escape", C0_ESC },
|
||||
{ "[FS]", C0_FS },
|
||||
{ "[GS]", C0_GS },
|
||||
{ "[RS]", C0_RS },
|
||||
{ "[US]", C0_US },
|
||||
|
||||
/* Arrow keys. */
|
||||
{ "Up", KEYC_UP|KEYC_CURSOR|KEYC_IMPLIED_META },
|
||||
@ -206,8 +242,7 @@ key_string_get_modifiers(const char **string)
|
||||
key_code
|
||||
key_string_lookup_string(const char *string)
|
||||
{
|
||||
static const char *other = "!#()+,-.0123456789:;<=>'\r\t\177`/";
|
||||
key_code key, modifiers;
|
||||
key_code key, modifiers = 0;
|
||||
u_int u, i;
|
||||
struct utf8_data ud, *udp;
|
||||
enum utf8_state more;
|
||||
@ -244,12 +279,15 @@ key_string_lookup_string(const char *string)
|
||||
return (uc);
|
||||
}
|
||||
|
||||
/* Check for modifiers. */
|
||||
modifiers = 0;
|
||||
/* Check for short Ctrl key. */
|
||||
if (string[0] == '^' && string[1] != '\0') {
|
||||
if (string[2] == '\0')
|
||||
return (tolower((u_char)string[1])|KEYC_CTRL);
|
||||
modifiers |= KEYC_CTRL;
|
||||
string++;
|
||||
}
|
||||
|
||||
/* Check for modifiers. */
|
||||
modifiers |= key_string_get_modifiers(&string);
|
||||
if (string == NULL || string[0] == '\0')
|
||||
return (KEYC_UNKNOWN);
|
||||
@ -281,26 +319,6 @@ key_string_lookup_string(const char *string)
|
||||
key &= ~KEYC_IMPLIED_META;
|
||||
}
|
||||
|
||||
/* Convert the standard control keys. */
|
||||
if (key <= 127 &&
|
||||
(modifiers & KEYC_CTRL) &&
|
||||
strchr(other, key) == NULL &&
|
||||
key != 9 &&
|
||||
key != 13 &&
|
||||
key != 27) {
|
||||
if (key >= 97 && key <= 122)
|
||||
key -= 96;
|
||||
else if (key >= 64 && key <= 95)
|
||||
key -= 64;
|
||||
else if (key == 32)
|
||||
key = 0;
|
||||
else if (key == 63)
|
||||
key = 127;
|
||||
else
|
||||
return (KEYC_UNKNOWN);
|
||||
modifiers &= ~KEYC_CTRL;
|
||||
}
|
||||
|
||||
return (key|modifiers);
|
||||
}
|
||||
|
||||
@ -324,10 +342,6 @@ key_string_lookup_key(key_code key, int with_flags)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Display C-@ as C-Space. */
|
||||
if ((key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS)) == 0)
|
||||
key = ' '|KEYC_CTRL;
|
||||
|
||||
/* Fill in the modifiers. */
|
||||
if (key & KEYC_CTRL)
|
||||
strlcat(out, "C-", sizeof out);
|
||||
@ -396,7 +410,7 @@ key_string_lookup_key(key_code key, int with_flags)
|
||||
s = "MouseMoveBorder";
|
||||
goto append;
|
||||
}
|
||||
if (key >= KEYC_USER && key < KEYC_USER + KEYC_NUSER) {
|
||||
if (key >= KEYC_USER && key < KEYC_USER_END) {
|
||||
snprintf(tmp, sizeof tmp, "User%u", (u_int)(key - KEYC_USER));
|
||||
strlcat(out, tmp, sizeof out);
|
||||
goto out;
|
||||
@ -427,13 +441,8 @@ key_string_lookup_key(key_code key, int with_flags)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check for standard or control key. */
|
||||
if (key <= 32) {
|
||||
if (key == 0 || key > 26)
|
||||
xsnprintf(tmp, sizeof tmp, "C-%c", (int)(64 + key));
|
||||
else
|
||||
xsnprintf(tmp, sizeof tmp, "C-%c", (int)(96 + key));
|
||||
} else if (key >= 32 && key <= 126) {
|
||||
/* Printable ASCII keys. */
|
||||
if (key > 32 && key <= 126) {
|
||||
tmp[0] = key;
|
||||
tmp[1] = '\0';
|
||||
} else if (key == 127)
|
||||
@ -460,6 +469,8 @@ out:
|
||||
strlcat(out, "I", sizeof out);
|
||||
if (saved & KEYC_BUILD_MODIFIERS)
|
||||
strlcat(out, "B", sizeof out);
|
||||
if (saved & KEYC_SENT)
|
||||
strlcat(out, "S", sizeof out);
|
||||
strlcat(out, "]", sizeof out);
|
||||
}
|
||||
return (out);
|
||||
|
@ -162,8 +162,10 @@ layout_parse(struct window *w, const char *layout, char **cause)
|
||||
u_short csum;
|
||||
|
||||
/* Check validity. */
|
||||
if (sscanf(layout, "%hx,", &csum) != 1)
|
||||
if (sscanf(layout, "%hx,", &csum) != 1) {
|
||||
*cause = xstrdup("invalid layout");
|
||||
return (-1);
|
||||
}
|
||||
layout += 5;
|
||||
if (csum != layout_checksum(layout)) {
|
||||
*cause = xstrdup("invalid layout");
|
||||
@ -228,7 +230,7 @@ layout_parse(struct window *w, const char *layout, char **cause)
|
||||
/* Check the new layout. */
|
||||
if (!layout_check(lc)) {
|
||||
*cause = xstrdup("size mismatch after applying layout");
|
||||
return (-1);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Resize to the layout size. */
|
||||
|
204
layout-set.c
204
layout-set.c
@ -31,7 +31,9 @@
|
||||
static void layout_set_even_h(struct window *);
|
||||
static void layout_set_even_v(struct window *);
|
||||
static void layout_set_main_h(struct window *);
|
||||
static void layout_set_main_h_mirrored(struct window *);
|
||||
static void layout_set_main_v(struct window *);
|
||||
static void layout_set_main_v_mirrored(struct window *);
|
||||
static void layout_set_tiled(struct window *);
|
||||
|
||||
static const struct {
|
||||
@ -41,7 +43,9 @@ static const struct {
|
||||
{ "even-horizontal", layout_set_even_h },
|
||||
{ "even-vertical", layout_set_even_v },
|
||||
{ "main-horizontal", layout_set_main_h },
|
||||
{ "main-horizontal-mirrored", layout_set_main_h_mirrored },
|
||||
{ "main-vertical", layout_set_main_v },
|
||||
{ "main-vertical-mirrored", layout_set_main_v_mirrored },
|
||||
{ "tiled", layout_set_tiled },
|
||||
};
|
||||
|
||||
@ -51,6 +55,10 @@ layout_set_lookup(const char *name)
|
||||
u_int i;
|
||||
int matched = -1;
|
||||
|
||||
for (i = 0; i < nitems(layout_sets); i++) {
|
||||
if (strcmp(layout_sets[i].name, name) == 0)
|
||||
return (i);
|
||||
}
|
||||
for (i = 0; i < nitems(layout_sets); i++) {
|
||||
if (strncmp(layout_sets[i].name, name, strlen(name)) == 0) {
|
||||
if (matched != -1) /* ambiguous */
|
||||
@ -279,6 +287,104 @@ layout_set_main_h(struct window *w)
|
||||
server_redraw_window(w);
|
||||
}
|
||||
|
||||
static void
|
||||
layout_set_main_h_mirrored(struct window *w)
|
||||
{
|
||||
struct window_pane *wp;
|
||||
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
|
||||
u_int n, mainh, otherh, sx, sy;
|
||||
char *cause;
|
||||
const char *s;
|
||||
|
||||
layout_print_cell(w->layout_root, __func__, 1);
|
||||
|
||||
/* Get number of panes. */
|
||||
n = window_count_panes(w);
|
||||
if (n <= 1)
|
||||
return;
|
||||
n--; /* take off main pane */
|
||||
|
||||
/* Find available height - take off one line for the border. */
|
||||
sy = w->sy - 1;
|
||||
|
||||
/* Get the main pane height. */
|
||||
s = options_get_string(w->options, "main-pane-height");
|
||||
mainh = args_string_percentage(s, 0, sy, sy, &cause);
|
||||
if (cause != NULL) {
|
||||
mainh = 24;
|
||||
free(cause);
|
||||
}
|
||||
|
||||
/* Work out the other pane height. */
|
||||
if (mainh + PANE_MINIMUM >= sy) {
|
||||
if (sy <= PANE_MINIMUM + PANE_MINIMUM)
|
||||
mainh = PANE_MINIMUM;
|
||||
else
|
||||
mainh = sy - PANE_MINIMUM;
|
||||
otherh = PANE_MINIMUM;
|
||||
} else {
|
||||
s = options_get_string(w->options, "other-pane-height");
|
||||
otherh = args_string_percentage(s, 0, sy, sy, &cause);
|
||||
if (cause != NULL || otherh == 0) {
|
||||
otherh = sy - mainh;
|
||||
free(cause);
|
||||
} else if (otherh > sy || sy - otherh < mainh)
|
||||
otherh = sy - mainh;
|
||||
else
|
||||
mainh = sy - otherh;
|
||||
}
|
||||
|
||||
/* Work out what width is needed. */
|
||||
sx = (n * (PANE_MINIMUM + 1)) - 1;
|
||||
if (sx < w->sx)
|
||||
sx = w->sx;
|
||||
|
||||
/* Free old tree and create a new root. */
|
||||
layout_free(w);
|
||||
lc = w->layout_root = layout_create_cell(NULL);
|
||||
layout_set_size(lc, sx, mainh + otherh + 1, 0, 0);
|
||||
layout_make_node(lc, LAYOUT_TOPBOTTOM);
|
||||
|
||||
/* Create the other pane. */
|
||||
lcother = layout_create_cell(lc);
|
||||
layout_set_size(lcother, sx, otherh, 0, 0);
|
||||
if (n == 1) {
|
||||
wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
|
||||
layout_make_leaf(lcother, wp);
|
||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
||||
} else {
|
||||
layout_make_node(lcother, LAYOUT_LEFTRIGHT);
|
||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
||||
|
||||
/* Add the remaining panes as children. */
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
if (wp == TAILQ_FIRST(&w->panes))
|
||||
continue;
|
||||
lcchild = layout_create_cell(lcother);
|
||||
layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
|
||||
layout_make_leaf(lcchild, wp);
|
||||
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
|
||||
}
|
||||
layout_spread_cell(w, lcother);
|
||||
}
|
||||
|
||||
/* Create the main pane. */
|
||||
lcmain = layout_create_cell(lc);
|
||||
layout_set_size(lcmain, sx, mainh, 0, 0);
|
||||
layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
|
||||
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
|
||||
|
||||
/* Fix cell offsets. */
|
||||
layout_fix_offsets(w);
|
||||
layout_fix_panes(w, NULL);
|
||||
|
||||
layout_print_cell(w->layout_root, __func__, 1);
|
||||
|
||||
window_resize(w, lc->sx, lc->sy, -1, -1);
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
}
|
||||
|
||||
static void
|
||||
layout_set_main_v(struct window *w)
|
||||
{
|
||||
@ -377,6 +483,104 @@ layout_set_main_v(struct window *w)
|
||||
server_redraw_window(w);
|
||||
}
|
||||
|
||||
static void
|
||||
layout_set_main_v_mirrored(struct window *w)
|
||||
{
|
||||
struct window_pane *wp;
|
||||
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
|
||||
u_int n, mainw, otherw, sx, sy;
|
||||
char *cause;
|
||||
const char *s;
|
||||
|
||||
layout_print_cell(w->layout_root, __func__, 1);
|
||||
|
||||
/* Get number of panes. */
|
||||
n = window_count_panes(w);
|
||||
if (n <= 1)
|
||||
return;
|
||||
n--; /* take off main pane */
|
||||
|
||||
/* Find available width - take off one line for the border. */
|
||||
sx = w->sx - 1;
|
||||
|
||||
/* Get the main pane width. */
|
||||
s = options_get_string(w->options, "main-pane-width");
|
||||
mainw = args_string_percentage(s, 0, sx, sx, &cause);
|
||||
if (cause != NULL) {
|
||||
mainw = 80;
|
||||
free(cause);
|
||||
}
|
||||
|
||||
/* Work out the other pane width. */
|
||||
if (mainw + PANE_MINIMUM >= sx) {
|
||||
if (sx <= PANE_MINIMUM + PANE_MINIMUM)
|
||||
mainw = PANE_MINIMUM;
|
||||
else
|
||||
mainw = sx - PANE_MINIMUM;
|
||||
otherw = PANE_MINIMUM;
|
||||
} else {
|
||||
s = options_get_string(w->options, "other-pane-width");
|
||||
otherw = args_string_percentage(s, 0, sx, sx, &cause);
|
||||
if (cause != NULL || otherw == 0) {
|
||||
otherw = sx - mainw;
|
||||
free(cause);
|
||||
} else if (otherw > sx || sx - otherw < mainw)
|
||||
otherw = sx - mainw;
|
||||
else
|
||||
mainw = sx - otherw;
|
||||
}
|
||||
|
||||
/* Work out what height is needed. */
|
||||
sy = (n * (PANE_MINIMUM + 1)) - 1;
|
||||
if (sy < w->sy)
|
||||
sy = w->sy;
|
||||
|
||||
/* Free old tree and create a new root. */
|
||||
layout_free(w);
|
||||
lc = w->layout_root = layout_create_cell(NULL);
|
||||
layout_set_size(lc, mainw + otherw + 1, sy, 0, 0);
|
||||
layout_make_node(lc, LAYOUT_LEFTRIGHT);
|
||||
|
||||
/* Create the other pane. */
|
||||
lcother = layout_create_cell(lc);
|
||||
layout_set_size(lcother, otherw, sy, 0, 0);
|
||||
if (n == 1) {
|
||||
wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
|
||||
layout_make_leaf(lcother, wp);
|
||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
||||
} else {
|
||||
layout_make_node(lcother, LAYOUT_TOPBOTTOM);
|
||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
||||
|
||||
/* Add the remaining panes as children. */
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
if (wp == TAILQ_FIRST(&w->panes))
|
||||
continue;
|
||||
lcchild = layout_create_cell(lcother);
|
||||
layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
|
||||
layout_make_leaf(lcchild, wp);
|
||||
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
|
||||
}
|
||||
layout_spread_cell(w, lcother);
|
||||
}
|
||||
|
||||
/* Create the main pane. */
|
||||
lcmain = layout_create_cell(lc);
|
||||
layout_set_size(lcmain, mainw, sy, 0, 0);
|
||||
layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
|
||||
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
|
||||
|
||||
/* Fix cell offsets. */
|
||||
layout_fix_offsets(w);
|
||||
layout_fix_panes(w, NULL);
|
||||
|
||||
layout_print_cell(w->layout_root, __func__, 1);
|
||||
|
||||
window_resize(w, lc->sx, lc->sy, -1, -1);
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
}
|
||||
|
||||
void
|
||||
layout_set_tiled(struct window *w)
|
||||
{
|
||||
|
110
layout.c
110
layout.c
@ -275,7 +275,8 @@ layout_cell_is_bottom(struct window *w, struct layout_cell *lc)
|
||||
* the case for the most upper or lower panes only.
|
||||
*/
|
||||
static int
|
||||
layout_add_border(struct window *w, struct layout_cell *lc, int status)
|
||||
layout_add_horizontal_border(struct window *w, struct layout_cell *lc,
|
||||
int status)
|
||||
{
|
||||
if (status == PANE_STATUS_TOP)
|
||||
return (layout_cell_is_top(w, lc));
|
||||
@ -290,22 +291,53 @@ layout_fix_panes(struct window *w, struct window_pane *skip)
|
||||
{
|
||||
struct window_pane *wp;
|
||||
struct layout_cell *lc;
|
||||
int status;
|
||||
int status, scrollbars, sb_pos, sb_w, sb_pad;
|
||||
u_int sx, sy;
|
||||
|
||||
status = options_get_number(w->options, "pane-border-status");
|
||||
scrollbars = options_get_number(w->options, "pane-scrollbars");
|
||||
sb_pos = options_get_number(w->options, "pane-scrollbars-position");
|
||||
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
if ((lc = wp->layout_cell) == NULL || wp == skip)
|
||||
continue;
|
||||
|
||||
wp->xoff = lc->xoff;
|
||||
wp->yoff = lc->yoff;
|
||||
sx = lc->sx;
|
||||
sy = lc->sy;
|
||||
|
||||
if (layout_add_border(w, lc, status)) {
|
||||
if (layout_add_horizontal_border(w, lc, status)) {
|
||||
if (status == PANE_STATUS_TOP)
|
||||
wp->yoff++;
|
||||
window_pane_resize(wp, lc->sx, lc->sy - 1);
|
||||
} else
|
||||
window_pane_resize(wp, lc->sx, lc->sy);
|
||||
sy--;
|
||||
}
|
||||
|
||||
if (window_pane_show_scrollbar(wp, scrollbars)) {
|
||||
sb_w = wp->scrollbar_style.width;
|
||||
sb_pad = wp->scrollbar_style.pad;
|
||||
if (sb_w < 1)
|
||||
sb_w = 1;
|
||||
if (sb_pad < 0)
|
||||
sb_pad = 0;
|
||||
if (sb_pos == PANE_SCROLLBARS_LEFT) {
|
||||
if ((int)sx - sb_w < PANE_MINIMUM) {
|
||||
wp->xoff = wp->xoff +
|
||||
(int)sx - PANE_MINIMUM;
|
||||
sx = PANE_MINIMUM;
|
||||
} else {
|
||||
sx = sx - sb_w - sb_pad;
|
||||
wp->xoff = wp->xoff + sb_w + sb_pad;
|
||||
}
|
||||
} else /* sb_pos == PANE_SCROLLBARS_RIGHT */
|
||||
if ((int)sx - sb_w - sb_pad < PANE_MINIMUM)
|
||||
sx = PANE_MINIMUM;
|
||||
else
|
||||
sx = sx - sb_w - sb_pad;
|
||||
wp->flags |= PANE_REDRAWSCROLLBAR;
|
||||
}
|
||||
|
||||
window_pane_resize(wp, sx, sy);
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,18 +368,25 @@ layout_resize_check(struct window *w, struct layout_cell *lc,
|
||||
enum layout_type type)
|
||||
{
|
||||
struct layout_cell *lcchild;
|
||||
struct style *sb_style = &w->active->scrollbar_style;
|
||||
u_int available, minimum;
|
||||
int status;
|
||||
int status, scrollbars;
|
||||
|
||||
status = options_get_number(w->options, "pane-border-status");
|
||||
scrollbars = options_get_number(w->options, "pane-scrollbars");
|
||||
|
||||
if (lc->type == LAYOUT_WINDOWPANE) {
|
||||
/* Space available in this cell only. */
|
||||
if (type == LAYOUT_LEFTRIGHT) {
|
||||
available = lc->sx;
|
||||
minimum = PANE_MINIMUM;
|
||||
if (scrollbars)
|
||||
minimum = PANE_MINIMUM + sb_style->width +
|
||||
sb_style->pad;
|
||||
else
|
||||
minimum = PANE_MINIMUM;
|
||||
} else {
|
||||
available = lc->sy;
|
||||
if (layout_add_border(w, lc, status))
|
||||
if (layout_add_horizontal_border(w, lc, status))
|
||||
minimum = PANE_MINIMUM + 1;
|
||||
else
|
||||
minimum = PANE_MINIMUM;
|
||||
@ -869,10 +908,12 @@ struct layout_cell *
|
||||
layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
|
||||
int flags)
|
||||
{
|
||||
struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
|
||||
u_int sx, sy, xoff, yoff, size1, size2, minimum;
|
||||
u_int new_size, saved_size, resize_first = 0;
|
||||
int full_size = (flags & SPAWN_FULLSIZE), status;
|
||||
struct layout_cell *lc, *lcparent, *lcnew, *lc1, *lc2;
|
||||
struct style *sb_style = &wp->scrollbar_style;
|
||||
u_int sx, sy, xoff, yoff, size1, size2, minimum;
|
||||
u_int new_size, saved_size, resize_first = 0;
|
||||
int full_size = (flags & SPAWN_FULLSIZE), status;
|
||||
int scrollbars;
|
||||
|
||||
/*
|
||||
* If full_size is specified, add a new cell at the top of the window
|
||||
@ -883,6 +924,7 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
|
||||
else
|
||||
lc = wp->layout_cell;
|
||||
status = options_get_number(wp->window->options, "pane-border-status");
|
||||
scrollbars = options_get_number(wp->window->options, "pane-scrollbars");
|
||||
|
||||
/* Copy the old cell size. */
|
||||
sx = lc->sx;
|
||||
@ -893,11 +935,16 @@ layout_split_pane(struct window_pane *wp, enum layout_type type, int size,
|
||||
/* Check there is enough space for the two new panes. */
|
||||
switch (type) {
|
||||
case LAYOUT_LEFTRIGHT:
|
||||
if (sx < PANE_MINIMUM * 2 + 1)
|
||||
if (scrollbars) {
|
||||
minimum = PANE_MINIMUM * 2 + sb_style->width +
|
||||
sb_style->pad;
|
||||
} else
|
||||
minimum = PANE_MINIMUM * 2 + 1;
|
||||
if (sx < minimum)
|
||||
return (NULL);
|
||||
break;
|
||||
case LAYOUT_TOPBOTTOM:
|
||||
if (layout_add_border(wp->window, lc, status))
|
||||
if (layout_add_horizontal_border(wp->window, lc, status))
|
||||
minimum = PANE_MINIMUM * 2 + 2;
|
||||
else
|
||||
minimum = PANE_MINIMUM * 2 + 1;
|
||||
@ -1053,8 +1100,9 @@ int
|
||||
layout_spread_cell(struct window *w, struct layout_cell *parent)
|
||||
{
|
||||
struct layout_cell *lc;
|
||||
u_int number, each, size, this;
|
||||
int change, changed, status;
|
||||
struct style *sb_style = &w->active->scrollbar_style;
|
||||
u_int number, each, size, this, remainder;
|
||||
int change, changed, status, scrollbars;
|
||||
|
||||
number = 0;
|
||||
TAILQ_FOREACH (lc, &parent->cells, entry)
|
||||
@ -1062,11 +1110,16 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
|
||||
if (number <= 1)
|
||||
return (0);
|
||||
status = options_get_number(w->options, "pane-border-status");
|
||||
scrollbars = options_get_number(w->options, "pane-scrollbars");
|
||||
|
||||
if (parent->type == LAYOUT_LEFTRIGHT)
|
||||
size = parent->sx;
|
||||
if (parent->type == LAYOUT_LEFTRIGHT) {
|
||||
if (scrollbars)
|
||||
size = parent->sx - sb_style->width + sb_style->pad;
|
||||
else
|
||||
size = parent->sx;
|
||||
}
|
||||
else if (parent->type == LAYOUT_TOPBOTTOM) {
|
||||
if (layout_add_border(w, parent, status))
|
||||
if (layout_add_horizontal_border(w, parent, status))
|
||||
size = parent->sy - 1;
|
||||
else
|
||||
size = parent->sy;
|
||||
@ -1077,20 +1130,31 @@ layout_spread_cell(struct window *w, struct layout_cell *parent)
|
||||
each = (size - (number - 1)) / number;
|
||||
if (each == 0)
|
||||
return (0);
|
||||
/*
|
||||
* Remaining space after assigning that which can be evenly
|
||||
* distributed.
|
||||
*/
|
||||
remainder = size - (number * (each + 1)) + 1;
|
||||
|
||||
changed = 0;
|
||||
TAILQ_FOREACH (lc, &parent->cells, entry) {
|
||||
if (TAILQ_NEXT(lc, entry) == NULL)
|
||||
each = size - ((each + 1) * (number - 1));
|
||||
change = 0;
|
||||
if (parent->type == LAYOUT_LEFTRIGHT) {
|
||||
change = each - (int)lc->sx;
|
||||
if (remainder > 0) {
|
||||
change++;
|
||||
remainder--;
|
||||
}
|
||||
layout_resize_adjust(w, lc, LAYOUT_LEFTRIGHT, change);
|
||||
} else if (parent->type == LAYOUT_TOPBOTTOM) {
|
||||
if (layout_add_border(w, lc, status))
|
||||
if (layout_add_horizontal_border(w, lc, status))
|
||||
this = each + 1;
|
||||
else
|
||||
this = each;
|
||||
if (remainder > 0) {
|
||||
this++;
|
||||
remainder--;
|
||||
}
|
||||
change = this - (int)lc->sy;
|
||||
layout_resize_adjust(w, lc, LAYOUT_TOPBOTTOM, change);
|
||||
}
|
||||
|
184
menu.c
184
menu.c
@ -27,6 +27,11 @@ struct menu_data {
|
||||
struct cmdq_item *item;
|
||||
int flags;
|
||||
|
||||
struct grid_cell style;
|
||||
struct grid_cell border_style;
|
||||
struct grid_cell selected_style;
|
||||
enum box_lines border_lines;
|
||||
|
||||
struct cmd_find_state fs;
|
||||
struct screen s;
|
||||
|
||||
@ -64,6 +69,8 @@ menu_add_item(struct menu *menu, const struct menu_item *item,
|
||||
line = (item == NULL || item->name == NULL || *item->name == '\0');
|
||||
if (line && menu->count == 0)
|
||||
return;
|
||||
if (line && menu->items[menu->count - 1].name == NULL)
|
||||
return;
|
||||
|
||||
menu->items = xreallocarray(menu->items, menu->count + 1,
|
||||
sizeof *menu->items);
|
||||
@ -160,11 +167,16 @@ menu_free(struct menu *menu)
|
||||
}
|
||||
|
||||
struct screen *
|
||||
menu_mode_cb(__unused struct client *c, void *data, __unused u_int *cx,
|
||||
__unused u_int *cy)
|
||||
menu_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
|
||||
{
|
||||
struct menu_data *md = data;
|
||||
|
||||
*cx = md->px + 2;
|
||||
if (md->choice == -1)
|
||||
*cy = md->py;
|
||||
else
|
||||
*cy = md->py + 1 + md->choice;
|
||||
|
||||
return (&md->s);
|
||||
}
|
||||
|
||||
@ -190,13 +202,17 @@ menu_draw_cb(struct client *c, void *data,
|
||||
struct menu *menu = md->menu;
|
||||
struct screen_write_ctx ctx;
|
||||
u_int i, px = md->px, py = md->py;
|
||||
struct grid_cell gc;
|
||||
|
||||
style_apply(&gc, c->session->curw->window->options, "mode-style", NULL);
|
||||
|
||||
screen_write_start(&ctx, s);
|
||||
screen_write_clearscreen(&ctx, 8);
|
||||
screen_write_menu(&ctx, menu, md->choice, &gc);
|
||||
|
||||
if (md->border_lines != BOX_LINES_NONE) {
|
||||
screen_write_box(&ctx, menu->width + 4, menu->count + 2,
|
||||
md->border_lines, &md->border_style, menu->title);
|
||||
}
|
||||
|
||||
screen_write_menu(&ctx, menu, md->choice, md->border_lines,
|
||||
&md->style, &md->border_style, &md->selected_style);
|
||||
screen_write_stop(&ctx);
|
||||
|
||||
for (i = 0; i < screen_size_y(&md->s); i++) {
|
||||
@ -318,36 +334,73 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
|
||||
} while ((name == NULL || *name == '-') && md->choice != old);
|
||||
c->flags |= CLIENT_REDRAWOVERLAY;
|
||||
return (0);
|
||||
case 'g':
|
||||
case KEYC_PPAGE:
|
||||
case '\002': /* C-b */
|
||||
if (md->choice > 5)
|
||||
md->choice -= 5;
|
||||
else
|
||||
case 'b'|KEYC_CTRL:
|
||||
if (md->choice < 6)
|
||||
md->choice = 0;
|
||||
while (md->choice != count && (name == NULL || *name == '-'))
|
||||
else {
|
||||
i = 5;
|
||||
while (i > 0) {
|
||||
md->choice--;
|
||||
name = menu->items[md->choice].name;
|
||||
if (md->choice != 0 &&
|
||||
(name != NULL && *name != '-'))
|
||||
i--;
|
||||
else if (md->choice == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
c->flags |= CLIENT_REDRAWOVERLAY;
|
||||
break;
|
||||
case KEYC_NPAGE:
|
||||
if (md->choice > count - 6) {
|
||||
md->choice = count - 1;
|
||||
name = menu->items[md->choice].name;
|
||||
} else {
|
||||
i = 5;
|
||||
while (i > 0) {
|
||||
md->choice++;
|
||||
name = menu->items[md->choice].name;
|
||||
if (md->choice != count - 1 &&
|
||||
(name != NULL && *name != '-'))
|
||||
i++;
|
||||
else if (md->choice == count - 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (name == NULL || *name == '-') {
|
||||
md->choice--;
|
||||
name = menu->items[md->choice].name;
|
||||
}
|
||||
c->flags |= CLIENT_REDRAWOVERLAY;
|
||||
break;
|
||||
case 'g':
|
||||
case KEYC_HOME:
|
||||
md->choice = 0;
|
||||
name = menu->items[md->choice].name;
|
||||
while (name == NULL || *name == '-') {
|
||||
md->choice++;
|
||||
if (md->choice == count)
|
||||
md->choice = -1;
|
||||
name = menu->items[md->choice].name;
|
||||
}
|
||||
c->flags |= CLIENT_REDRAWOVERLAY;
|
||||
break;
|
||||
case 'G':
|
||||
case KEYC_NPAGE:
|
||||
if (md->choice > count - 6)
|
||||
md->choice = count - 1;
|
||||
else
|
||||
md->choice += 5;
|
||||
while (md->choice != -1 && (name == NULL || *name == '-'))
|
||||
case KEYC_END:
|
||||
md->choice = count - 1;
|
||||
name = menu->items[md->choice].name;
|
||||
while (name == NULL || *name == '-') {
|
||||
md->choice--;
|
||||
name = menu->items[md->choice].name;
|
||||
}
|
||||
c->flags |= CLIENT_REDRAWOVERLAY;
|
||||
break;
|
||||
case '\006': /* C-f */
|
||||
case 'f'|KEYC_CTRL:
|
||||
break;
|
||||
case '\r':
|
||||
goto chosen;
|
||||
case '\033': /* Escape */
|
||||
case '\003': /* C-c */
|
||||
case '\007': /* C-g */
|
||||
case 'c'|KEYC_CTRL:
|
||||
case 'g'|KEYC_CTRL:
|
||||
case 'q':
|
||||
return (1);
|
||||
}
|
||||
@ -384,14 +437,35 @@ chosen:
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
menu_set_style(struct client *c, struct grid_cell *gc, const char *style,
|
||||
const char *option)
|
||||
{
|
||||
struct style sytmp;
|
||||
struct options *o = c->session->curw->window->options;
|
||||
|
||||
memcpy(gc, &grid_default_cell, sizeof *gc);
|
||||
style_apply(gc, o, option, NULL);
|
||||
if (style != NULL) {
|
||||
style_set(&sytmp, &grid_default_cell);
|
||||
if (style_parse(&sytmp, gc, style) == 0) {
|
||||
gc->fg = sytmp.gc.fg;
|
||||
gc->bg = sytmp.gc.bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct menu_data *
|
||||
menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
|
||||
u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb,
|
||||
menu_prepare(struct menu *menu, int flags, int starting_choice,
|
||||
struct cmdq_item *item, u_int px, u_int py, struct client *c,
|
||||
enum box_lines lines, const char *style, const char *selected_style,
|
||||
const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb,
|
||||
void *data)
|
||||
{
|
||||
struct menu_data *md;
|
||||
u_int i;
|
||||
int choice;
|
||||
const char *name;
|
||||
struct options *o = c->session->curw->window->options;
|
||||
|
||||
if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2)
|
||||
return (NULL);
|
||||
@ -400,9 +474,18 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
|
||||
if (py + menu->count + 2 > c->tty.sy)
|
||||
py = c->tty.sy - menu->count - 2;
|
||||
|
||||
if (lines == BOX_LINES_DEFAULT)
|
||||
lines = options_get_number(o, "menu-border-lines");
|
||||
|
||||
md = xcalloc(1, sizeof *md);
|
||||
md->item = item;
|
||||
md->flags = flags;
|
||||
md->border_lines = lines;
|
||||
|
||||
menu_set_style(c, &md->style, style, "menu-style");
|
||||
menu_set_style(c, &md->selected_style, selected_style,
|
||||
"menu-selected-style");
|
||||
menu_set_style(c, &md->border_style, border_style, "menu-border-style");
|
||||
|
||||
if (fs != NULL)
|
||||
cmd_find_copy_state(&md->fs, fs);
|
||||
@ -415,18 +498,38 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
|
||||
md->py = py;
|
||||
|
||||
md->menu = menu;
|
||||
md->choice = -1;
|
||||
|
||||
if (md->flags & MENU_NOMOUSE) {
|
||||
for (i = 0; i < menu->count; i++) {
|
||||
name = menu->items[i].name;
|
||||
if (name != NULL && *name != '-')
|
||||
break;
|
||||
if (starting_choice >= (int)menu->count) {
|
||||
starting_choice = menu->count - 1;
|
||||
choice = starting_choice + 1;
|
||||
for (;;) {
|
||||
name = menu->items[choice - 1].name;
|
||||
if (name != NULL && *name != '-') {
|
||||
md->choice = choice - 1;
|
||||
break;
|
||||
}
|
||||
if (--choice == 0)
|
||||
choice = menu->count;
|
||||
if (choice == starting_choice + 1)
|
||||
break;
|
||||
}
|
||||
} else if (starting_choice >= 0) {
|
||||
choice = starting_choice;
|
||||
for (;;) {
|
||||
name = menu->items[choice].name;
|
||||
if (name != NULL && *name != '-') {
|
||||
md->choice = choice;
|
||||
break;
|
||||
}
|
||||
if (++choice == (int)menu->count)
|
||||
choice = 0;
|
||||
if (choice == starting_choice)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i != menu->count)
|
||||
md->choice = i;
|
||||
else
|
||||
md->choice = -1;
|
||||
} else
|
||||
md->choice = -1;
|
||||
}
|
||||
|
||||
md->cb = cb;
|
||||
md->data = data;
|
||||
@ -434,13 +537,16 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
|
||||
}
|
||||
|
||||
int
|
||||
menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
|
||||
u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb,
|
||||
menu_display(struct menu *menu, int flags, int starting_choice,
|
||||
struct cmdq_item *item, u_int px, u_int py, struct client *c,
|
||||
enum box_lines lines, const char *style, const char *selected_style,
|
||||
const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb,
|
||||
void *data)
|
||||
{
|
||||
struct menu_data *md;
|
||||
|
||||
md = menu_prepare(menu, flags, item, px, py, c, fs, cb, data);
|
||||
md = menu_prepare(menu, flags, starting_choice, item, px, py, c, lines,
|
||||
style, selected_style, border_style, fs, cb, data);
|
||||
if (md == NULL)
|
||||
return (-1);
|
||||
server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb,
|
||||
|
232
mode-tree.c
232
mode-tree.c
@ -25,6 +25,17 @@
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
enum mode_tree_search_dir {
|
||||
MODE_TREE_SEARCH_FORWARD,
|
||||
MODE_TREE_SEARCH_BACKWARD
|
||||
};
|
||||
|
||||
enum mode_tree_preview {
|
||||
MODE_TREE_PREVIEW_OFF,
|
||||
MODE_TREE_PREVIEW_NORMAL,
|
||||
MODE_TREE_PREVIEW_BIG
|
||||
};
|
||||
|
||||
struct mode_tree_item;
|
||||
TAILQ_HEAD(mode_tree_list, mode_tree_item);
|
||||
|
||||
@ -47,6 +58,7 @@ struct mode_tree_data {
|
||||
mode_tree_menu_cb menucb;
|
||||
mode_tree_height_cb heightcb;
|
||||
mode_tree_key_cb keycb;
|
||||
mode_tree_swap_cb swapcb;
|
||||
|
||||
struct mode_tree_list children;
|
||||
struct mode_tree_list saved;
|
||||
@ -55,6 +67,7 @@ struct mode_tree_data {
|
||||
u_int line_size;
|
||||
|
||||
u_int depth;
|
||||
u_int maxdepth;
|
||||
|
||||
u_int width;
|
||||
u_int height;
|
||||
@ -68,6 +81,7 @@ struct mode_tree_data {
|
||||
char *search;
|
||||
char *filter;
|
||||
int no_matches;
|
||||
enum mode_tree_search_dir search_dir;
|
||||
};
|
||||
|
||||
struct mode_tree_item {
|
||||
@ -88,6 +102,7 @@ struct mode_tree_item {
|
||||
|
||||
int draw_as_parent;
|
||||
int no_tag;
|
||||
int align;
|
||||
|
||||
struct mode_tree_list children;
|
||||
TAILQ_ENTRY(mode_tree_item) entry;
|
||||
@ -184,6 +199,8 @@ mode_tree_build_lines(struct mode_tree_data *mtd,
|
||||
int flat = 1;
|
||||
|
||||
mtd->depth = depth;
|
||||
if (depth > mtd->maxdepth)
|
||||
mtd->maxdepth = depth;
|
||||
TAILQ_FOREACH(mti, mtl, entry) {
|
||||
mtd->line_list = xreallocarray(mtd->line_list,
|
||||
mtd->line_size + 1, sizeof *mtd->line_list);
|
||||
@ -255,19 +272,50 @@ mode_tree_up(struct mode_tree_data *mtd, int wrap)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
mode_tree_down(struct mode_tree_data *mtd, int wrap)
|
||||
{
|
||||
if (mtd->current == mtd->line_size - 1) {
|
||||
if (wrap) {
|
||||
mtd->current = 0;
|
||||
mtd->offset = 0;
|
||||
}
|
||||
} else
|
||||
return (0);
|
||||
} else {
|
||||
mtd->current++;
|
||||
if (mtd->current > mtd->offset + mtd->height - 1)
|
||||
mtd->offset++;
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
static void
|
||||
mode_tree_swap(struct mode_tree_data *mtd, int direction)
|
||||
{
|
||||
u_int current_depth = mtd->line_list[mtd->current].depth;
|
||||
u_int swap_with, swap_with_depth;
|
||||
|
||||
if (mtd->swapcb == NULL)
|
||||
return;
|
||||
|
||||
/* Find the next line at the same depth with the same parent . */
|
||||
swap_with = mtd->current;
|
||||
do {
|
||||
if (direction < 0 && swap_with < (u_int)-direction)
|
||||
return;
|
||||
if (direction > 0 && swap_with + direction >= mtd->line_size)
|
||||
return;
|
||||
swap_with += direction;
|
||||
swap_with_depth = mtd->line_list[swap_with].depth;
|
||||
} while (swap_with_depth > current_depth);
|
||||
if (swap_with_depth != current_depth)
|
||||
return;
|
||||
|
||||
if (mtd->swapcb(mtd->line_list[mtd->current].item->itemdata,
|
||||
mtd->line_list[swap_with].item->itemdata)) {
|
||||
mtd->current = swap_with;
|
||||
mode_tree_build(mtd);
|
||||
}
|
||||
}
|
||||
|
||||
void *
|
||||
@ -342,8 +390,13 @@ mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag)
|
||||
mtd->offset = 0;
|
||||
return (1);
|
||||
}
|
||||
mtd->current = 0;
|
||||
mtd->offset = 0;
|
||||
if (mtd->current >= mtd->line_size) {
|
||||
mtd->current = mtd->line_size - 1;
|
||||
if (mtd->current > mtd->height - 1)
|
||||
mtd->offset = mtd->current - mtd->height + 1;
|
||||
else
|
||||
mtd->offset = 0;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -388,9 +441,9 @@ struct mode_tree_data *
|
||||
mode_tree_start(struct window_pane *wp, struct args *args,
|
||||
mode_tree_build_cb buildcb, mode_tree_draw_cb drawcb,
|
||||
mode_tree_search_cb searchcb, mode_tree_menu_cb menucb,
|
||||
mode_tree_height_cb heightcb, mode_tree_key_cb keycb, void *modedata,
|
||||
const struct menu_item *menu, const char **sort_list, u_int sort_size,
|
||||
struct screen **s)
|
||||
mode_tree_height_cb heightcb, mode_tree_key_cb keycb,
|
||||
mode_tree_swap_cb swapcb, void *modedata, const struct menu_item *menu,
|
||||
const char **sort_list, u_int sort_size, struct screen **s)
|
||||
{
|
||||
struct mode_tree_data *mtd;
|
||||
const char *sort;
|
||||
@ -406,7 +459,12 @@ mode_tree_start(struct window_pane *wp, struct args *args,
|
||||
mtd->sort_list = sort_list;
|
||||
mtd->sort_size = sort_size;
|
||||
|
||||
mtd->preview = !args_has(args, 'N');
|
||||
if (args_has(args, 'N') > 1)
|
||||
mtd->preview = MODE_TREE_PREVIEW_BIG;
|
||||
else if (args_has(args, 'N'))
|
||||
mtd->preview = MODE_TREE_PREVIEW_OFF;
|
||||
else
|
||||
mtd->preview = MODE_TREE_PREVIEW_NORMAL;
|
||||
|
||||
sort = args_get(args, 'O');
|
||||
if (sort != NULL) {
|
||||
@ -428,6 +486,7 @@ mode_tree_start(struct window_pane *wp, struct args *args,
|
||||
mtd->menucb = menucb;
|
||||
mtd->heightcb = heightcb;
|
||||
mtd->keycb = keycb;
|
||||
mtd->swapcb = swapcb;
|
||||
|
||||
TAILQ_INIT(&mtd->children);
|
||||
|
||||
@ -462,12 +521,21 @@ mode_tree_set_height(struct mode_tree_data *mtd)
|
||||
if (height < screen_size_y(s))
|
||||
mtd->height = screen_size_y(s) - height;
|
||||
} else {
|
||||
mtd->height = (screen_size_y(s) / 3) * 2;
|
||||
if (mtd->height > mtd->line_size)
|
||||
mtd->height = screen_size_y(s) / 2;
|
||||
if (mtd->preview == MODE_TREE_PREVIEW_NORMAL) {
|
||||
mtd->height = (screen_size_y(s) / 3) * 2;
|
||||
if (mtd->height > mtd->line_size)
|
||||
mtd->height = screen_size_y(s) / 2;
|
||||
if (mtd->height < 10)
|
||||
mtd->height = screen_size_y(s);
|
||||
} else if (mtd->preview == MODE_TREE_PREVIEW_BIG) {
|
||||
mtd->height = screen_size_y(s) / 4;
|
||||
if (mtd->height > mtd->line_size)
|
||||
mtd->height = mtd->line_size;
|
||||
if (mtd->height < 2)
|
||||
mtd->height = 2;
|
||||
} else
|
||||
mtd->height = screen_size_y(s);
|
||||
}
|
||||
if (mtd->height < 10)
|
||||
mtd->height = screen_size_y(s);
|
||||
if (screen_size_y(s) - mtd->height < 2)
|
||||
mtd->height = screen_size_y(s);
|
||||
}
|
||||
@ -495,14 +563,15 @@ mode_tree_build(struct mode_tree_data *mtd)
|
||||
TAILQ_INIT(&mtd->saved);
|
||||
|
||||
mode_tree_clear_lines(mtd);
|
||||
mtd->maxdepth = 0;
|
||||
mode_tree_build_lines(mtd, &mtd->children, 0);
|
||||
|
||||
if (tag == UINT64_MAX)
|
||||
if (mtd->line_list != NULL && tag == UINT64_MAX)
|
||||
tag = mtd->line_list[mtd->current].item->tag;
|
||||
mode_tree_set_current(mtd, tag);
|
||||
|
||||
mtd->width = screen_size_x(s);
|
||||
if (mtd->preview)
|
||||
if (mtd->preview != MODE_TREE_PREVIEW_OFF)
|
||||
mode_tree_set_height(mtd);
|
||||
else
|
||||
mtd->height = screen_size_y(s);
|
||||
@ -599,6 +668,16 @@ mode_tree_no_tag(struct mode_tree_item *mti)
|
||||
mti->no_tag = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the alignment of mti->name: -1 to align left, 0 (default) to not align,
|
||||
* or 1 to align right.
|
||||
*/
|
||||
void
|
||||
mode_tree_align(struct mode_tree_item *mti, int align)
|
||||
{
|
||||
mti->align = align;
|
||||
}
|
||||
|
||||
void
|
||||
mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
|
||||
{
|
||||
@ -625,7 +704,7 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
||||
char *text, *start, *key;
|
||||
const char *tag, *symbol;
|
||||
size_t size, n;
|
||||
int keylen, pad;
|
||||
int keylen, pad, alignlen[mtd->maxdepth + 1];
|
||||
|
||||
if (mtd->line_size == 0)
|
||||
return;
|
||||
@ -649,6 +728,16 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
||||
keylen = mti->keylen + 3;
|
||||
}
|
||||
|
||||
for (i = 0; i < mtd->maxdepth + 1; i++)
|
||||
alignlen[i] = 0;
|
||||
for (i = 0; i < mtd->line_size; i++) {
|
||||
line = &mtd->line_list[i];
|
||||
mti = line->item;
|
||||
if (mti->align &&
|
||||
(int)strlen(mti->name) > alignlen[line->depth])
|
||||
alignlen[line->depth] = strlen(mti->name);
|
||||
}
|
||||
|
||||
for (i = 0; i < mtd->line_size; i++) {
|
||||
if (i < mtd->offset)
|
||||
continue;
|
||||
@ -698,8 +787,9 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
||||
tag = "*";
|
||||
else
|
||||
tag = "";
|
||||
xasprintf(&text, "%-*s%s%s%s%s", keylen, key, start, mti->name,
|
||||
tag, (mti->text != NULL) ? ": " : "" );
|
||||
xasprintf(&text, "%-*s%s%*s%s%s", keylen, key, start,
|
||||
mti->align * alignlen[line->depth], mti->name, tag,
|
||||
(mti->text != NULL) ? ": " : "" );
|
||||
width = utf8_cstrwidth(text);
|
||||
if (width > w)
|
||||
width = w;
|
||||
@ -734,8 +824,11 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
||||
}
|
||||
}
|
||||
|
||||
if (mtd->preview == MODE_TREE_PREVIEW_OFF)
|
||||
goto done;
|
||||
|
||||
sy = screen_size_y(s);
|
||||
if (!mtd->preview || sy <= 4 || h <= 4 || sy - h <= 4 || w <= 4)
|
||||
if (sy <= 4 || h < 2 || sy - h <= 4 || w <= 4)
|
||||
goto done;
|
||||
|
||||
line = &mtd->line_list[mtd->current];
|
||||
@ -786,7 +879,49 @@ done:
|
||||
}
|
||||
|
||||
static struct mode_tree_item *
|
||||
mode_tree_search_for(struct mode_tree_data *mtd)
|
||||
mode_tree_search_backward(struct mode_tree_data *mtd)
|
||||
{
|
||||
struct mode_tree_item *mti, *last, *prev;
|
||||
|
||||
if (mtd->search == NULL)
|
||||
return (NULL);
|
||||
|
||||
mti = last = mtd->line_list[mtd->current].item;
|
||||
for (;;) {
|
||||
if ((prev = TAILQ_PREV(mti, mode_tree_list, entry)) != NULL) {
|
||||
/* Point to the last child in the previous subtree. */
|
||||
while (!TAILQ_EMPTY(&prev->children))
|
||||
prev = TAILQ_LAST(&prev->children, mode_tree_list);
|
||||
mti = prev;
|
||||
} else {
|
||||
/* If prev is NULL, jump to the parent. */
|
||||
mti = mti->parent;
|
||||
}
|
||||
|
||||
if (mti == NULL) {
|
||||
/* Point to the last child in the last root subtree. */
|
||||
prev = TAILQ_LAST(&mtd->children, mode_tree_list);
|
||||
while (!TAILQ_EMPTY(&prev->children))
|
||||
prev = TAILQ_LAST(&prev->children, mode_tree_list);
|
||||
mti = prev;
|
||||
}
|
||||
if (mti == last)
|
||||
break;
|
||||
|
||||
if (mtd->searchcb == NULL) {
|
||||
if (strstr(mti->name, mtd->search) != NULL)
|
||||
return (mti);
|
||||
continue;
|
||||
}
|
||||
if (mtd->searchcb(mtd->modedata, mti->itemdata, mtd->search))
|
||||
return (mti);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
static struct mode_tree_item *
|
||||
mode_tree_search_forward(struct mode_tree_data *mtd)
|
||||
{
|
||||
struct mode_tree_item *mti, *last, *next;
|
||||
|
||||
@ -832,7 +967,10 @@ mode_tree_search_set(struct mode_tree_data *mtd)
|
||||
struct mode_tree_item *mti, *loop;
|
||||
uint64_t tag;
|
||||
|
||||
mti = mode_tree_search_for(mtd);
|
||||
if (mtd->search_dir == MODE_TREE_SEARCH_FORWARD)
|
||||
mti = mode_tree_search_forward(mtd);
|
||||
else
|
||||
mti = mode_tree_search_backward(mtd);
|
||||
if (mti == NULL)
|
||||
return;
|
||||
tag = mti->tag;
|
||||
@ -962,9 +1100,12 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
|
||||
x -= (menu->width + 4) / 2;
|
||||
else
|
||||
x = 0;
|
||||
if (menu_display(menu, 0, NULL, x, y, c, NULL, mode_tree_menu_callback,
|
||||
mtm) != 0)
|
||||
if (menu_display(menu, 0, 0, NULL, x, y, c, BOX_LINES_DEFAULT, NULL,
|
||||
NULL, NULL, NULL, mode_tree_menu_callback, mtm) != 0) {
|
||||
mode_tree_remove_ref(mtd);
|
||||
free(mtm);
|
||||
menu_free(menu);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
@ -988,7 +1129,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
|
||||
if (x > mtd->width || y > mtd->height) {
|
||||
if (*key == KEYC_MOUSEDOWN3_PANE)
|
||||
mode_tree_display_menu(mtd, c, x, y, 1);
|
||||
if (!mtd->preview)
|
||||
if (mtd->preview == MODE_TREE_PREVIEW_OFF)
|
||||
*key = KEYC_NONE;
|
||||
return (0);
|
||||
}
|
||||
@ -1035,22 +1176,30 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
|
||||
switch (*key) {
|
||||
case 'q':
|
||||
case '\033': /* Escape */
|
||||
case '\007': /* C-g */
|
||||
case 'g'|KEYC_CTRL:
|
||||
return (1);
|
||||
case KEYC_UP:
|
||||
case 'k':
|
||||
case KEYC_WHEELUP_PANE:
|
||||
case '\020': /* C-p */
|
||||
case 'p'|KEYC_CTRL:
|
||||
mode_tree_up(mtd, 1);
|
||||
break;
|
||||
case KEYC_DOWN:
|
||||
case 'j':
|
||||
case KEYC_WHEELDOWN_PANE:
|
||||
case '\016': /* C-n */
|
||||
case 'n'|KEYC_CTRL:
|
||||
mode_tree_down(mtd, 1);
|
||||
break;
|
||||
case KEYC_UP|KEYC_SHIFT:
|
||||
case 'K':
|
||||
mode_tree_swap(mtd, -1);
|
||||
break;
|
||||
case KEYC_DOWN|KEYC_SHIFT:
|
||||
case 'J':
|
||||
mode_tree_swap(mtd, 1);
|
||||
break;
|
||||
case KEYC_PPAGE:
|
||||
case '\002': /* C-b */
|
||||
case 'b'|KEYC_CTRL:
|
||||
for (i = 0; i < mtd->height; i++) {
|
||||
if (mtd->current == 0)
|
||||
break;
|
||||
@ -1058,7 +1207,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
|
||||
}
|
||||
break;
|
||||
case KEYC_NPAGE:
|
||||
case '\006': /* C-f */
|
||||
case 'f'|KEYC_CTRL:
|
||||
for (i = 0; i < mtd->height; i++) {
|
||||
if (mtd->current == mtd->line_size - 1)
|
||||
break;
|
||||
@ -1102,7 +1251,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
|
||||
for (i = 0; i < mtd->line_size; i++)
|
||||
mtd->line_list[i].item->tagged = 0;
|
||||
break;
|
||||
case '\024': /* C-t */
|
||||
case 't'|KEYC_CTRL:
|
||||
for (i = 0; i < mtd->line_size; i++) {
|
||||
if ((mtd->line_list[i].item->parent == NULL &&
|
||||
!mtd->line_list[i].item->no_tag) ||
|
||||
@ -1158,13 +1307,18 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
|
||||
break;
|
||||
case '?':
|
||||
case '/':
|
||||
case '\023': /* C-s */
|
||||
case 's'|KEYC_CTRL:
|
||||
mtd->references++;
|
||||
status_prompt_set(c, NULL, "(search) ", "",
|
||||
mode_tree_search_callback, mode_tree_search_free, mtd,
|
||||
PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH);
|
||||
break;
|
||||
case 'n':
|
||||
mtd->search_dir = MODE_TREE_SEARCH_FORWARD;
|
||||
mode_tree_search_set(mtd);
|
||||
break;
|
||||
case 'N':
|
||||
mtd->search_dir = MODE_TREE_SEARCH_BACKWARD;
|
||||
mode_tree_search_set(mtd);
|
||||
break;
|
||||
case 'f':
|
||||
@ -1174,9 +1328,19 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
|
||||
PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH);
|
||||
break;
|
||||
case 'v':
|
||||
mtd->preview = !mtd->preview;
|
||||
switch (mtd->preview) {
|
||||
case MODE_TREE_PREVIEW_OFF:
|
||||
mtd->preview = MODE_TREE_PREVIEW_BIG;
|
||||
break;
|
||||
case MODE_TREE_PREVIEW_NORMAL:
|
||||
mtd->preview = MODE_TREE_PREVIEW_OFF;
|
||||
break;
|
||||
case MODE_TREE_PREVIEW_BIG:
|
||||
mtd->preview = MODE_TREE_PREVIEW_NORMAL;
|
||||
break;
|
||||
}
|
||||
mode_tree_build(mtd);
|
||||
if (mtd->preview)
|
||||
if (mtd->preview != MODE_TREE_PREVIEW_OFF)
|
||||
mode_tree_check_selected(mtd);
|
||||
break;
|
||||
}
|
||||
@ -1198,7 +1362,7 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs,
|
||||
if (status == CMD_PARSE_ERROR) {
|
||||
if (c != NULL) {
|
||||
*error = toupper((u_char)*error);
|
||||
status_message_set(c, -1, 1, 0, "%s", error);
|
||||
status_message_set(c, -1, 1, 0, 0, "%s", error);
|
||||
}
|
||||
free(error);
|
||||
}
|
||||
|
41
notify.c
41
notify.c
@ -32,6 +32,7 @@ struct notify_entry {
|
||||
struct session *session;
|
||||
struct window *window;
|
||||
int pane;
|
||||
const char *pbname;
|
||||
};
|
||||
|
||||
static struct cmdq_item *
|
||||
@ -149,6 +150,10 @@ notify_callback(struct cmdq_item *item, void *data)
|
||||
control_notify_session_closed(ne->session);
|
||||
if (strcmp(ne->name, "session-window-changed") == 0)
|
||||
control_notify_session_window_changed(ne->session);
|
||||
if (strcmp(ne->name, "paste-buffer-changed") == 0)
|
||||
control_notify_paste_buffer_changed(ne->pbname);
|
||||
if (strcmp(ne->name, "paste-buffer-deleted") == 0)
|
||||
control_notify_paste_buffer_deleted(ne->pbname);
|
||||
|
||||
notify_insert_hook(item, ne);
|
||||
|
||||
@ -164,6 +169,7 @@ notify_callback(struct cmdq_item *item, void *data)
|
||||
|
||||
format_free(ne->formats);
|
||||
free((void *)ne->name);
|
||||
free((void *)ne->pbname);
|
||||
free(ne);
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
@ -171,7 +177,8 @@ notify_callback(struct cmdq_item *item, void *data)
|
||||
|
||||
static void
|
||||
notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
|
||||
struct session *s, struct window *w, struct window_pane *wp)
|
||||
struct session *s, struct window *w, struct window_pane *wp,
|
||||
const char *pbname)
|
||||
{
|
||||
struct notify_entry *ne;
|
||||
struct cmdq_item *item;
|
||||
@ -186,7 +193,8 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
|
||||
ne->client = c;
|
||||
ne->session = s;
|
||||
ne->window = w;
|
||||
ne->pane = (wp != NULL ? wp->id : -1);
|
||||
ne->pane = (wp != NULL ? (int)wp->id : -1);
|
||||
ne->pbname = (pbname != NULL ? xstrdup(pbname) : NULL);
|
||||
|
||||
ne->formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
|
||||
format_add(ne->formats, "hook", "%s", name);
|
||||
@ -232,7 +240,7 @@ notify_hook(struct cmdq_item *item, const char *name)
|
||||
ne.client = cmdq_get_client(item);
|
||||
ne.session = target->s;
|
||||
ne.window = target->w;
|
||||
ne.pane = (target->wp != NULL ? target->wp->id : -1);
|
||||
ne.pane = (target->wp != NULL ? (int)target->wp->id : -1);
|
||||
|
||||
ne.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
|
||||
format_add(ne.formats, "hook", "%s", name);
|
||||
@ -248,7 +256,7 @@ notify_client(const char *name, struct client *c)
|
||||
struct cmd_find_state fs;
|
||||
|
||||
cmd_find_from_client(&fs, c, 0);
|
||||
notify_add(name, &fs, c, NULL, NULL, NULL);
|
||||
notify_add(name, &fs, c, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
@ -260,7 +268,7 @@ notify_session(const char *name, struct session *s)
|
||||
cmd_find_from_session(&fs, s, 0);
|
||||
else
|
||||
cmd_find_from_nothing(&fs, 0);
|
||||
notify_add(name, &fs, NULL, s, NULL, NULL);
|
||||
notify_add(name, &fs, NULL, s, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
@ -269,7 +277,7 @@ notify_winlink(const char *name, struct winlink *wl)
|
||||
struct cmd_find_state fs;
|
||||
|
||||
cmd_find_from_winlink(&fs, wl, 0);
|
||||
notify_add(name, &fs, NULL, wl->session, wl->window, NULL);
|
||||
notify_add(name, &fs, NULL, wl->session, wl->window, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
@ -278,7 +286,7 @@ notify_session_window(const char *name, struct session *s, struct window *w)
|
||||
struct cmd_find_state fs;
|
||||
|
||||
cmd_find_from_session_window(&fs, s, w, 0);
|
||||
notify_add(name, &fs, NULL, s, w, NULL);
|
||||
notify_add(name, &fs, NULL, s, w, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
@ -287,7 +295,7 @@ notify_window(const char *name, struct window *w)
|
||||
struct cmd_find_state fs;
|
||||
|
||||
cmd_find_from_window(&fs, w, 0);
|
||||
notify_add(name, &fs, NULL, NULL, w, NULL);
|
||||
notify_add(name, &fs, NULL, NULL, w, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
@ -296,5 +304,20 @@ notify_pane(const char *name, struct window_pane *wp)
|
||||
struct cmd_find_state fs;
|
||||
|
||||
cmd_find_from_pane(&fs, wp, 0);
|
||||
notify_add(name, &fs, NULL, NULL, NULL, wp);
|
||||
notify_add(name, &fs, NULL, NULL, NULL, wp, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
notify_paste_buffer(const char *pbname, int deleted)
|
||||
{
|
||||
struct cmd_find_state fs;
|
||||
|
||||
cmd_find_clear_state(&fs, 0);
|
||||
if (deleted) {
|
||||
notify_add("paste-buffer-deleted", &fs, NULL, NULL, NULL, NULL,
|
||||
pbname);
|
||||
} else {
|
||||
notify_add("paste-buffer-changed", &fs, NULL, NULL, NULL, NULL,
|
||||
pbname);
|
||||
}
|
||||
}
|
||||
|
247
options-table.c
247
options-table.c
@ -41,6 +41,9 @@ static const char *options_table_clock_mode_style_list[] = {
|
||||
static const char *options_table_status_list[] = {
|
||||
"off", "on", "2", "3", "4", "5", NULL
|
||||
};
|
||||
static const char *options_table_message_line_list[] = {
|
||||
"0", "1", "2", "3", "4", NULL
|
||||
};
|
||||
static const char *options_table_status_keys_list[] = {
|
||||
"emacs", "vi", NULL
|
||||
};
|
||||
@ -60,6 +63,12 @@ static const char *options_table_cursor_style_list[] = {
|
||||
"default", "blinking-block", "block", "blinking-underline", "underline",
|
||||
"blinking-bar", "bar", NULL
|
||||
};
|
||||
static const char *options_table_pane_scrollbars_list[] = {
|
||||
"off", "modal", "on", NULL
|
||||
};
|
||||
static const char *options_table_pane_scrollbars_position_list[] = {
|
||||
"right", "left", NULL
|
||||
};
|
||||
static const char *options_table_pane_status_list[] = {
|
||||
"off", "top", "bottom", NULL
|
||||
};
|
||||
@ -81,12 +90,21 @@ static const char *options_table_window_size_list[] = {
|
||||
static const char *options_table_remain_on_exit_list[] = {
|
||||
"off", "on", "failed", NULL
|
||||
};
|
||||
static const char *options_table_destroy_unattached_list[] = {
|
||||
"off", "on", "keep-last", "keep-group", NULL
|
||||
};
|
||||
static const char *options_table_detach_on_destroy_list[] = {
|
||||
"off", "on", "no-detached", NULL
|
||||
"off", "on", "no-detached", "previous", "next", NULL
|
||||
};
|
||||
static const char *options_table_extended_keys_list[] = {
|
||||
"off", "on", "always", NULL
|
||||
};
|
||||
static const char *options_table_extended_keys_format_list[] = {
|
||||
"csi-u", "xterm", NULL
|
||||
};
|
||||
static const char *options_table_allow_passthrough_list[] = {
|
||||
"off", "on", "all", NULL
|
||||
};
|
||||
|
||||
/* Status line format. */
|
||||
#define OPTIONS_TABLE_STATUS_FORMAT1 \
|
||||
@ -195,6 +213,7 @@ const struct options_name_map options_other_names[] = {
|
||||
{ "display-panes-active-color", "display-panes-active-colour" },
|
||||
{ "clock-mode-color", "clock-mode-colour" },
|
||||
{ "cursor-color", "cursor-colour" },
|
||||
{ "prompt-cursor-color", "prompt-cursor-colour" },
|
||||
{ "pane-colors", "pane-colours" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
@ -234,6 +253,15 @@ const struct options_table_entry options_table[] = {
|
||||
"Each entry is an alias and a command separated by '='."
|
||||
},
|
||||
|
||||
{ .name = "codepoint-widths",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_ARRAY,
|
||||
.default_str = "",
|
||||
.separator = ",",
|
||||
.text = "Array of override widths for Unicode codepoints."
|
||||
},
|
||||
|
||||
{ .name = "copy-command",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
@ -257,6 +285,13 @@ const struct options_table_entry options_table[] = {
|
||||
.text = "Style of the cursor."
|
||||
},
|
||||
|
||||
{ .name = "default-client-command",
|
||||
.type = OPTIONS_TABLE_COMMAND,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.default_str = "new-session",
|
||||
.text = "Default command to run when tmux is run without a command."
|
||||
},
|
||||
|
||||
{ .name = "default-terminal",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
@ -276,7 +311,7 @@ const struct options_table_entry options_table[] = {
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.minimum = 0,
|
||||
.maximum = INT_MAX,
|
||||
.default_num = 500,
|
||||
.default_num = 10,
|
||||
.unit = "milliseconds",
|
||||
.text = "Time to wait before assuming a key is Escape."
|
||||
},
|
||||
@ -305,6 +340,14 @@ const struct options_table_entry options_table[] = {
|
||||
"that support it."
|
||||
},
|
||||
|
||||
{ .name = "extended-keys-format",
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.choices = options_table_extended_keys_format_list,
|
||||
.default_num = 1,
|
||||
.text = "The format of emitted extended key sequences."
|
||||
},
|
||||
|
||||
{ .name = "focus-events",
|
||||
.type = OPTIONS_TABLE_FLAG,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
@ -320,6 +363,51 @@ const struct options_table_entry options_table[] = {
|
||||
"Empty does not write a history file."
|
||||
},
|
||||
|
||||
{ .name = "input-buffer-size",
|
||||
.type = OPTIONS_TABLE_NUMBER,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.minimum = INPUT_BUF_DEFAULT_SIZE,
|
||||
.maximum = UINT_MAX,
|
||||
.default_num = INPUT_BUF_DEFAULT_SIZE,
|
||||
.text = "Number of bytes accepted in a single input before dropping."
|
||||
},
|
||||
|
||||
{ .name = "menu-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.default_str = "default",
|
||||
.separator = ",",
|
||||
.text = "Default style of menu."
|
||||
},
|
||||
|
||||
{ .name = "menu-selected-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.default_str = "bg=yellow,fg=black",
|
||||
.separator = ",",
|
||||
.text = "Default style of selected menu item."
|
||||
},
|
||||
|
||||
{ .name = "menu-border-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "default",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Default style of menu borders."
|
||||
},
|
||||
|
||||
{ .name = "menu-border-lines",
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.choices = options_table_popup_border_lines_list,
|
||||
.default_num = BOX_LINES_SINGLE,
|
||||
.text = "Type of characters used to draw menu border lines. Some of "
|
||||
"these are only supported on terminals with UTF-8 support."
|
||||
},
|
||||
|
||||
{ .name = "message-limit",
|
||||
.type = OPTIONS_TABLE_NUMBER,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
@ -329,6 +417,17 @@ const struct options_table_entry options_table[] = {
|
||||
.text = "Maximum number of server messages to keep."
|
||||
},
|
||||
|
||||
{ .name = "prefix-timeout",
|
||||
.type = OPTIONS_TABLE_NUMBER,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.minimum = 0,
|
||||
.maximum = INT_MAX,
|
||||
.default_num = 0,
|
||||
.unit = "milliseconds",
|
||||
.text = "The timeout for the prefix key if no subsequent key is "
|
||||
"pressed. Zero means disabled."
|
||||
},
|
||||
|
||||
{ .name = "prompt-history-limit",
|
||||
.type = OPTIONS_TABLE_NUMBER,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
@ -352,7 +451,7 @@ const struct options_table_entry options_table[] = {
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_ARRAY,
|
||||
.default_str = "",
|
||||
.default_str = "linux*:AX@",
|
||||
.separator = ",",
|
||||
.text = "List of terminal capabilities overrides."
|
||||
},
|
||||
@ -362,7 +461,8 @@ const struct options_table_entry options_table[] = {
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_ARRAY,
|
||||
.default_str = "xterm*:clipboard:ccolour:cstyle:focus:title,"
|
||||
"screen*:title",
|
||||
"screen*:title,"
|
||||
"rxvt*:ignorefkeys",
|
||||
.separator = ",",
|
||||
.text = "List of terminal features, used if they cannot be "
|
||||
"automatically detected."
|
||||
@ -379,6 +479,14 @@ const struct options_table_entry options_table[] = {
|
||||
"'User0', 'User1' and so on."
|
||||
},
|
||||
|
||||
{ .name = "variation-selector-always-wide",
|
||||
.type = OPTIONS_TABLE_FLAG,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.default_num = 1,
|
||||
.text = "If the Unicode VS16 codepoint should always be treated as a "
|
||||
"wide character."
|
||||
},
|
||||
|
||||
/* Session options. */
|
||||
{ .name = "activity-action",
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
@ -440,11 +548,12 @@ const struct options_table_entry options_table[] = {
|
||||
},
|
||||
|
||||
{ .name = "destroy-unattached",
|
||||
.type = OPTIONS_TABLE_FLAG,
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.choices = options_table_destroy_unattached_list,
|
||||
.default_num = 0,
|
||||
.text = "Whether to destroy sessions when they have no attached "
|
||||
"clients."
|
||||
"clients, or keep the last session whether in the group."
|
||||
},
|
||||
|
||||
{ .name = "detach-on-destroy",
|
||||
@ -502,6 +611,18 @@ const struct options_table_entry options_table[] = {
|
||||
"If changed, the new value applies only to new panes."
|
||||
},
|
||||
|
||||
{ .name = "initial-repeat-time",
|
||||
.type = OPTIONS_TABLE_NUMBER,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.minimum = 0,
|
||||
.maximum = 2000000,
|
||||
.default_num = 0,
|
||||
.unit = "milliseconds",
|
||||
.text = "Time to wait for a key binding to repeat the first time the "
|
||||
"key is pressed, if it is bound with the '-r' flag. "
|
||||
"Subsequent presses use the 'repeat-time' option."
|
||||
},
|
||||
|
||||
{ .name = "key-table",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
@ -523,7 +644,7 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "lock-command",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.default_str = "lock -np",
|
||||
.default_str = TMUX_LOCK_CMD,
|
||||
.text = "Shell command to run to lock a client."
|
||||
},
|
||||
|
||||
@ -537,13 +658,21 @@ const struct options_table_entry options_table[] = {
|
||||
"'mode-keys' is set to 'vi'."
|
||||
},
|
||||
|
||||
{ .name = "message-line",
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.choices = options_table_message_line_list,
|
||||
.default_num = 0,
|
||||
.text = "Position (line) of messages and the command prompt."
|
||||
},
|
||||
|
||||
{ .name = "message-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.default_str = "bg=yellow,fg=black",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of the command prompt."
|
||||
.text = "Style of messages and the command prompt."
|
||||
},
|
||||
|
||||
{ .name = "mouse",
|
||||
@ -558,7 +687,7 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "prefix",
|
||||
.type = OPTIONS_TABLE_KEY,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.default_num = '\002',
|
||||
.default_num = 'b'|KEYC_CTRL,
|
||||
.text = "The prefix key."
|
||||
},
|
||||
|
||||
@ -581,7 +710,7 @@ const struct options_table_entry options_table[] = {
|
||||
.type = OPTIONS_TABLE_NUMBER,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.minimum = 0,
|
||||
.maximum = SHRT_MAX,
|
||||
.maximum = 2000000,
|
||||
.default_num = 500,
|
||||
.unit = "milliseconds",
|
||||
.text = "Time to wait for a key binding to repeat, if it is bound "
|
||||
@ -742,11 +871,26 @@ const struct options_table_entry options_table[] = {
|
||||
.text = "Style of the status line."
|
||||
},
|
||||
|
||||
{ .name = "prompt-cursor-colour",
|
||||
.type = OPTIONS_TABLE_COLOUR,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.default_num = 6,
|
||||
.text = "Colour of the cursor when in the command prompt."
|
||||
},
|
||||
|
||||
{ .name = "prompt-cursor-style",
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.choices = options_table_cursor_style_list,
|
||||
.default_num = 0,
|
||||
.text = "Style of the cursor when in the command prompt."
|
||||
},
|
||||
|
||||
{ .name = "update-environment",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.flags = OPTIONS_TABLE_IS_ARRAY,
|
||||
.default_str = "DISPLAY KRB5CCNAME SSH_ASKPASS SSH_AUTH_SOCK "
|
||||
.default_str = "DISPLAY KRB5CCNAME MSYSTEM SSH_ASKPASS SSH_AUTH_SOCK "
|
||||
"SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY",
|
||||
.text = "List of environment variables to update in the session "
|
||||
"environment when a client is attached."
|
||||
@ -802,11 +946,14 @@ const struct options_table_entry options_table[] = {
|
||||
},
|
||||
|
||||
{ .name = "allow-passthrough",
|
||||
.type = OPTIONS_TABLE_FLAG,
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.choices = options_table_allow_passthrough_list,
|
||||
.default_num = 0,
|
||||
.text = "Whether applications are allowed to use the escape sequence "
|
||||
"to bypass tmux."
|
||||
"to bypass tmux. Can be 'off' (disallowed), 'on' (allowed "
|
||||
"if the pane is visible), or 'all' (allowed even if the pane "
|
||||
"is invisible)."
|
||||
},
|
||||
|
||||
{ .name = "allow-rename",
|
||||
@ -817,6 +964,14 @@ const struct options_table_entry options_table[] = {
|
||||
"to rename windows."
|
||||
},
|
||||
|
||||
{ .name = "allow-set-title",
|
||||
.type = OPTIONS_TABLE_FLAG,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.default_num = 1,
|
||||
.text = "Whether applications are allowed to use the escape sequence "
|
||||
"to set the pane title."
|
||||
},
|
||||
|
||||
{ .name = "alternate-screen",
|
||||
.type = OPTIONS_TABLE_FLAG,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
@ -882,6 +1037,36 @@ const struct options_table_entry options_table[] = {
|
||||
.text = "Style of the marked line in copy mode."
|
||||
},
|
||||
|
||||
{ .name = "copy-mode-position-format",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.default_str = "#[align=right]"
|
||||
"#{t/p:top_line_time}#{?#{e|>:#{top_line_time},0}, ,}"
|
||||
"[#{scroll_position}/#{history_size}]"
|
||||
"#{?search_timed_out, (timed out),"
|
||||
"#{?search_count, (#{search_count}"
|
||||
"#{?search_count_partial,+,} results),}}",
|
||||
.text = "Format of the position indicator in copy mode."
|
||||
},
|
||||
|
||||
{ .name = "copy-mode-position-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "#{E:mode-style}",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of position indicator in copy mode."
|
||||
},
|
||||
|
||||
{ .name = "copy-mode-selection-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "#{E:mode-style}",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of selection in copy mode."
|
||||
},
|
||||
|
||||
{ .name = "fill-character",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
@ -916,8 +1101,8 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "mode-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "bg=yellow,fg=black",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.default_str = "noattr,bg=yellow,fg=black",
|
||||
.separator = ",",
|
||||
.text = "Style of indicators and highlighting in modes."
|
||||
},
|
||||
@ -1032,6 +1217,31 @@ const struct options_table_entry options_table[] = {
|
||||
.text = "The default colour palette for colours zero to 255."
|
||||
},
|
||||
|
||||
{ .name = "pane-scrollbars",
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.choices = options_table_pane_scrollbars_list,
|
||||
.default_num = PANE_SCROLLBARS_OFF,
|
||||
.text = "Pane scrollbar state."
|
||||
},
|
||||
|
||||
{ .name = "pane-scrollbars-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.default_str = "bg=black,fg=white,width=1,pad=0",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of the pane scrollbar."
|
||||
},
|
||||
|
||||
{ .name = "pane-scrollbars-position",
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.choices = options_table_pane_scrollbars_position_list,
|
||||
.default_num = PANE_SCROLLBARS_RIGHT,
|
||||
.text = "Pane scrollbar position."
|
||||
},
|
||||
|
||||
{ .name = "popup-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
@ -1073,12 +1283,12 @@ const struct options_table_entry options_table[] = {
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.default_str = "Pane is dead ("
|
||||
"#{?#{!=:#{pane_dead_status},},"
|
||||
"status #{pane_dead_status},}"
|
||||
"status #{pane_dead_status},}"
|
||||
"#{?#{!=:#{pane_dead_signal},},"
|
||||
"signal #{pane_dead_signal},}, "
|
||||
"signal #{pane_dead_signal},}, "
|
||||
"#{t:pane_dead_time})",
|
||||
.text = "Message shown after the program in a pane has exited, if "
|
||||
"remain-on-exit is enabled."
|
||||
"remain-on-exit is enabled."
|
||||
},
|
||||
|
||||
{ .name = "scroll-on-clear",
|
||||
@ -1259,6 +1469,9 @@ const struct options_table_entry options_table[] = {
|
||||
OPTIONS_TABLE_HOOK("client-focus-out", ""),
|
||||
OPTIONS_TABLE_HOOK("client-resized", ""),
|
||||
OPTIONS_TABLE_HOOK("client-session-changed", ""),
|
||||
OPTIONS_TABLE_HOOK("client-light-theme", ""),
|
||||
OPTIONS_TABLE_HOOK("client-dark-theme", ""),
|
||||
OPTIONS_TABLE_HOOK("command-error", ""),
|
||||
OPTIONS_TABLE_PANE_HOOK("pane-died", ""),
|
||||
OPTIONS_TABLE_PANE_HOOK("pane-exited", ""),
|
||||
OPTIONS_TABLE_PANE_HOOK("pane-focus-in", ""),
|
||||
|
114
options.c
114
options.c
@ -260,6 +260,7 @@ options_default(struct options *oo, const struct options_table_entry *oe)
|
||||
struct options_entry *o;
|
||||
union options_value *ov;
|
||||
u_int i;
|
||||
struct cmd_parse_result *pr;
|
||||
|
||||
o = options_empty(oo, oe);
|
||||
ov = &o->value;
|
||||
@ -278,6 +279,17 @@ options_default(struct options *oo, const struct options_table_entry *oe)
|
||||
case OPTIONS_TABLE_STRING:
|
||||
ov->string = xstrdup(oe->default_str);
|
||||
break;
|
||||
case OPTIONS_TABLE_COMMAND:
|
||||
pr = cmd_parse_from_string(oe->default_str, NULL);
|
||||
switch (pr->status) {
|
||||
case CMD_PARSE_ERROR:
|
||||
free(pr->error);
|
||||
break;
|
||||
case CMD_PARSE_SUCCESS:
|
||||
ov->cmdlist = pr->cmdlist;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ov->number = oe->default_num;
|
||||
break;
|
||||
@ -578,10 +590,28 @@ char *
|
||||
options_to_string(struct options_entry *o, int idx, int numeric)
|
||||
{
|
||||
struct options_array_item *a;
|
||||
char *result = NULL;
|
||||
char *last = NULL;
|
||||
char *next;
|
||||
|
||||
if (OPTIONS_IS_ARRAY(o)) {
|
||||
if (idx == -1)
|
||||
return (xstrdup(""));
|
||||
if (idx == -1) {
|
||||
RB_FOREACH(a, options_array, &o->value.array) {
|
||||
next = options_value_to_string(o, &a->value,
|
||||
numeric);
|
||||
if (last == NULL)
|
||||
result = next;
|
||||
else {
|
||||
xasprintf(&result, "%s %s", last, next);
|
||||
free(last);
|
||||
free(next);
|
||||
}
|
||||
last = result;
|
||||
}
|
||||
if (result == NULL)
|
||||
return (xstrdup(""));
|
||||
return (result);
|
||||
}
|
||||
a = options_array_item(o, idx);
|
||||
if (a == NULL)
|
||||
return (xstrdup(""));
|
||||
@ -719,6 +749,19 @@ options_get_number(struct options *oo, const char *name)
|
||||
return (o->value.number);
|
||||
}
|
||||
|
||||
const struct cmd_list *
|
||||
options_get_command(struct options *oo, const char *name)
|
||||
{
|
||||
struct options_entry *o;
|
||||
|
||||
o = options_get(oo, name);
|
||||
if (o == NULL)
|
||||
fatalx("missing option %s", name);
|
||||
if (!OPTIONS_IS_COMMAND(o))
|
||||
fatalx("option %s is not a command", name);
|
||||
return (o->value.cmdlist);
|
||||
}
|
||||
|
||||
struct options_entry *
|
||||
options_set_string(struct options *oo, const char *name, int append,
|
||||
const char *fmt, ...)
|
||||
@ -780,6 +823,30 @@ options_set_number(struct options *oo, const char *name, long long value)
|
||||
return (o);
|
||||
}
|
||||
|
||||
struct options_entry *
|
||||
options_set_command(struct options *oo, const char *name,
|
||||
struct cmd_list *value)
|
||||
{
|
||||
struct options_entry *o;
|
||||
|
||||
if (*name == '@')
|
||||
fatalx("user option %s must be a string", name);
|
||||
|
||||
o = options_get_only(oo, name);
|
||||
if (o == NULL) {
|
||||
o = options_default(oo, options_parent_table_entry(oo, name));
|
||||
if (o == NULL)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (!OPTIONS_IS_COMMAND(o))
|
||||
fatalx("option %s is not a command", name);
|
||||
if (o->value.cmdlist != NULL)
|
||||
cmd_list_free(o->value.cmdlist);
|
||||
o->value.cmdlist = value;
|
||||
return (o);
|
||||
}
|
||||
|
||||
int
|
||||
options_scope_from_name(struct args *args, int window,
|
||||
const char *name, struct cmd_find_state *fs, struct options **oo,
|
||||
@ -1036,6 +1103,7 @@ options_from_string(struct options *oo, const struct options_table_entry *oe,
|
||||
const char *errstr, *new;
|
||||
char *old;
|
||||
key_code key;
|
||||
struct cmd_parse_result *pr;
|
||||
|
||||
if (oe != NULL) {
|
||||
if (value == NULL &&
|
||||
@ -1094,6 +1162,15 @@ options_from_string(struct options *oo, const struct options_table_entry *oe,
|
||||
case OPTIONS_TABLE_CHOICE:
|
||||
return (options_from_string_choice(oe, oo, name, value, cause));
|
||||
case OPTIONS_TABLE_COMMAND:
|
||||
pr = cmd_parse_from_string(value, NULL);
|
||||
switch (pr->status) {
|
||||
case CMD_PARSE_ERROR:
|
||||
*cause = pr->error;
|
||||
return (-1);
|
||||
case CMD_PARSE_SUCCESS:
|
||||
options_set_command(oo, name, pr->cmdlist);
|
||||
return (0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return (-1);
|
||||
@ -1106,7 +1183,6 @@ options_push_changes(const char *name)
|
||||
struct session *s;
|
||||
struct window *w;
|
||||
struct window_pane *wp;
|
||||
int c;
|
||||
|
||||
log_debug("%s: %s", __func__, name);
|
||||
|
||||
@ -1119,18 +1195,12 @@ options_push_changes(const char *name)
|
||||
}
|
||||
}
|
||||
if (strcmp(name, "cursor-colour") == 0) {
|
||||
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
|
||||
c = options_get_number(wp->options, name);
|
||||
wp->screen->default_ccolour = c;
|
||||
}
|
||||
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
|
||||
window_pane_default_cursor(wp);
|
||||
}
|
||||
if (strcmp(name, "cursor-style") == 0) {
|
||||
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
|
||||
wp->screen->default_mode = 0;
|
||||
screen_set_cursor_style(options_get_number(wp->options,
|
||||
name), &wp->screen->default_cstyle,
|
||||
&wp->screen->default_mode);
|
||||
}
|
||||
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
|
||||
window_pane_default_cursor(wp);
|
||||
}
|
||||
if (strcmp(name, "fill-character") == 0) {
|
||||
RB_FOREACH(w, windows, &windows)
|
||||
@ -1154,16 +1224,30 @@ options_push_changes(const char *name)
|
||||
if (strcmp(name, "window-style") == 0 ||
|
||||
strcmp(name, "window-active-style") == 0) {
|
||||
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
|
||||
wp->flags |= PANE_STYLECHANGED;
|
||||
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
|
||||
}
|
||||
if (strcmp(name, "pane-colours") == 0) {
|
||||
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
|
||||
colour_palette_from_option(&wp->palette, wp->options);
|
||||
}
|
||||
if (strcmp(name, "pane-border-status") == 0) {
|
||||
if (strcmp(name, "pane-border-status") == 0 ||
|
||||
strcmp(name, "pane-scrollbars") == 0 ||
|
||||
strcmp(name, "pane-scrollbars-position") == 0) {
|
||||
RB_FOREACH(w, windows, &windows)
|
||||
layout_fix_panes(w, NULL);
|
||||
}
|
||||
if (strcmp(name, "pane-scrollbars-style") == 0) {
|
||||
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
|
||||
style_set_scrollbar_style_from_option(
|
||||
&wp->scrollbar_style, wp->options);
|
||||
}
|
||||
RB_FOREACH(w, windows, &windows)
|
||||
layout_fix_panes(w, NULL);
|
||||
}
|
||||
if (strcmp(name, "codepoint-widths") == 0)
|
||||
utf8_update_width_cache();
|
||||
if (strcmp(name, "input-buffer-size") == 0)
|
||||
input_set_buffer_size(options_get_number(global_options, name));
|
||||
RB_FOREACH(s, sessions, &sessions)
|
||||
status_update_cache(s);
|
||||
|
||||
|
28
paste.c
28
paste.c
@ -111,6 +111,12 @@ paste_walk(struct paste_buffer *pb)
|
||||
return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
|
||||
}
|
||||
|
||||
int
|
||||
paste_is_empty(void)
|
||||
{
|
||||
return RB_ROOT(&paste_by_time) == NULL;
|
||||
}
|
||||
|
||||
/* Get the most recent automatic buffer. */
|
||||
struct paste_buffer *
|
||||
paste_get_top(const char **name)
|
||||
@ -118,6 +124,8 @@ paste_get_top(const char **name)
|
||||
struct paste_buffer *pb;
|
||||
|
||||
pb = RB_MIN(paste_time_tree, &paste_by_time);
|
||||
while (pb != NULL && !pb->automatic)
|
||||
pb = RB_NEXT(paste_time_tree, &paste_by_time, pb);
|
||||
if (pb == NULL)
|
||||
return (NULL);
|
||||
if (name != NULL)
|
||||
@ -142,6 +150,8 @@ paste_get_name(const char *name)
|
||||
void
|
||||
paste_free(struct paste_buffer *pb)
|
||||
{
|
||||
notify_paste_buffer(pb->name, 1);
|
||||
|
||||
RB_REMOVE(paste_name_tree, &paste_by_name, pb);
|
||||
RB_REMOVE(paste_time_tree, &paste_by_time, pb);
|
||||
if (pb->automatic)
|
||||
@ -198,6 +208,8 @@ paste_add(const char *prefix, char *data, size_t size)
|
||||
pb->order = paste_next_order++;
|
||||
RB_INSERT(paste_name_tree, &paste_by_name, pb);
|
||||
RB_INSERT(paste_time_tree, &paste_by_time, pb);
|
||||
|
||||
notify_paste_buffer(pb->name, 0);
|
||||
}
|
||||
|
||||
/* Rename a paste buffer. */
|
||||
@ -228,11 +240,10 @@ paste_rename(const char *oldname, const char *newname, char **cause)
|
||||
}
|
||||
|
||||
pb_new = paste_get_name(newname);
|
||||
if (pb_new != NULL) {
|
||||
if (cause != NULL)
|
||||
xasprintf(cause, "buffer %s already exists", newname);
|
||||
return (-1);
|
||||
}
|
||||
if (pb_new == pb)
|
||||
return (0);
|
||||
if (pb_new != NULL)
|
||||
paste_free(pb_new);
|
||||
|
||||
RB_REMOVE(paste_name_tree, &paste_by_name, pb);
|
||||
|
||||
@ -245,6 +256,9 @@ paste_rename(const char *oldname, const char *newname, char **cause)
|
||||
|
||||
RB_INSERT(paste_name_tree, &paste_by_name, pb);
|
||||
|
||||
notify_paste_buffer(oldname, 1);
|
||||
notify_paste_buffer(newname, 0);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -293,6 +307,8 @@ paste_set(char *data, size_t size, const char *name, char **cause)
|
||||
RB_INSERT(paste_name_tree, &paste_by_name, pb);
|
||||
RB_INSERT(paste_time_tree, &paste_by_time, pb);
|
||||
|
||||
notify_paste_buffer(name, 0);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -303,6 +319,8 @@ paste_replace(struct paste_buffer *pb, char *data, size_t size)
|
||||
free(pb->data);
|
||||
pb->data = data;
|
||||
pb->size = size;
|
||||
|
||||
notify_paste_buffer(pb->name, 0);
|
||||
}
|
||||
|
||||
/* Convert start of buffer into a nice string. */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user