mirror of
https://github.com/tmux/tmux.git
synced 2026-09-16 18:21:58 +00:00
Compare commits
991 Commits
ta/kitty-i
...
floating_p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a91d5601e | ||
|
|
63a8af0e15 | ||
|
|
6b9b6c0b0b | ||
|
|
390805d8b9 | ||
|
|
70f37ef815 | ||
|
|
b6fca55447 | ||
|
|
e22c56f02f | ||
|
|
01c86e64b7 | ||
|
|
ca81c36383 | ||
|
|
a9a04c7bc0 | ||
|
|
0a81f4d742 | ||
|
|
0ad5035457 | ||
|
|
d626cef6de | ||
|
|
7713cbebd2 | ||
|
|
baf16f0419 | ||
|
|
9dcc2da348 | ||
|
|
7de5eb1254 | ||
|
|
f50a38de23 | ||
|
|
3ef51a469c | ||
|
|
d5688bf165 | ||
|
|
8dc9e24c74 | ||
|
|
735bbf9da7 | ||
|
|
6ad86ebdf1 | ||
|
|
3d58e04c93 | ||
|
|
9fb67a2cdd | ||
|
|
d9d12c5c43 | ||
|
|
728ad24d4a | ||
|
|
5b37471db7 | ||
|
|
c41e98be3c | ||
|
|
35f429cb6d | ||
|
|
85fc0ea57c | ||
|
|
6f588402c3 | ||
|
|
fada35478b | ||
|
|
bd5d7a8a7a | ||
|
|
8108356978 | ||
|
|
5e32eddb8c | ||
|
|
1818b6ac16 | ||
|
|
e802909de0 | ||
|
|
824ef37f5f | ||
|
|
565db46a19 | ||
|
|
87aa44f4bc | ||
|
|
e9396e59d9 | ||
|
|
002ac977f8 | ||
|
|
724f85d275 | ||
|
|
b3fa1c0b9c | ||
|
|
e69950a9ca | ||
|
|
41b4743eba | ||
|
|
1e9a16a748 | ||
|
|
74a5069c0a | ||
|
|
8d470017bc | ||
|
|
928c7a9771 | ||
|
|
eda7e563d0 | ||
|
|
90fc51542f | ||
|
|
f8674cc993 | ||
|
|
843adbf454 | ||
|
|
abefc3f705 | ||
|
|
dddd7b44c8 | ||
|
|
25c874c489 | ||
|
|
0e418b62d2 | ||
|
|
a97f643bd3 | ||
|
|
78a2145a47 | ||
|
|
dbe50934b1 | ||
|
|
9e0ca948bf | ||
|
|
8e070f1a34 | ||
|
|
543d104f85 | ||
|
|
e06207c914 | ||
|
|
166267c87a | ||
|
|
91e0737b8a | ||
|
|
65a032b205 | ||
|
|
80c7dcdbd7 | ||
|
|
bcd54d12d8 | ||
|
|
726e19a886 | ||
|
|
95719e342d | ||
|
|
132a63d1da | ||
|
|
822f700b15 | ||
|
|
5df51a06f5 | ||
|
|
ad74c5ed7f | ||
|
|
d5284be91d | ||
|
|
c29b41e527 | ||
|
|
4e612612dc | ||
|
|
705fbf92ee | ||
|
|
2dc704abb6 | ||
|
|
6c2ef75681 | ||
|
|
96899dc527 | ||
|
|
b0db912bde | ||
|
|
7cd33ce0dd | ||
|
|
e0dd40f812 | ||
|
|
79f0f97495 | ||
|
|
5e0b0a2aa3 | ||
|
|
e24969b422 | ||
|
|
dd02989e88 | ||
|
|
7383c04205 | ||
|
|
84291b021f | ||
|
|
91e30f4f22 | ||
|
|
5be44a6c03 | ||
|
|
8cc20a5618 | ||
|
|
81f88f8517 | ||
|
|
4eafb27b89 | ||
|
|
4fc4644652 | ||
|
|
c0f213b056 | ||
|
|
a2915cfdef | ||
|
|
4b921f3821 | ||
|
|
575f84ce0f | ||
|
|
1048796003 | ||
|
|
973c4ab569 | ||
|
|
445677dc00 | ||
|
|
da27529d91 | ||
|
|
f59921ce18 | ||
|
|
ec3c65122f | ||
|
|
9fa5e81836 | ||
|
|
4869a5165a | ||
|
|
30e84ff687 | ||
|
|
73fd01c986 | ||
|
|
3729af7438 | ||
|
|
1e5aef279a | ||
|
|
19db019e0c | ||
|
|
666464d7a9 | ||
|
|
391f880d07 | ||
|
|
d0bf3bf06d | ||
|
|
edb027e910 | ||
|
|
dd2fef6673 | ||
|
|
8e79c4a03d | ||
|
|
19d70a85fc | ||
|
|
f5e9bcc1fc | ||
|
|
3631f81236 | ||
|
|
1e6f64d5c9 | ||
|
|
569e9d932f | ||
|
|
6a7f4a1894 | ||
|
|
8c55a388cd | ||
|
|
d176f1f89c | ||
|
|
2d0845860a | ||
|
|
5ae701aab5 | ||
|
|
0d016195ac | ||
|
|
fc2de240d2 | ||
|
|
7c1ef6a387 | ||
|
|
0b538b6f2a | ||
|
|
3d26aa0dfb | ||
|
|
51d037e881 | ||
|
|
e5fb88fb85 | ||
|
|
787234eff4 | ||
|
|
6b1219b370 | ||
|
|
ed48555bdd | ||
|
|
ba9faae8b6 | ||
|
|
6935dee976 | ||
|
|
12473bb6d3 | ||
|
|
280a962328 | ||
|
|
37633ff957 | ||
|
|
1c2fc8c3c4 | ||
|
|
d3adecd819 | ||
|
|
0a71af41be | ||
|
|
8fb484da37 | ||
|
|
6e6a434a2d | ||
|
|
f23ea9e977 | ||
|
|
55cfdbaa3f | ||
|
|
1b5a07a740 | ||
|
|
00b0af7021 | ||
|
|
89027bd22c | ||
|
|
ff8de1a106 | ||
|
|
0c5e6770df | ||
|
|
c2ad33a04c | ||
|
|
c0a5e10313 | ||
|
|
1c394b8606 | ||
|
|
505d557fbb | ||
|
|
a63e429d22 | ||
|
|
de086f9848 | ||
|
|
6643acb375 | ||
|
|
c25cb107bb | ||
|
|
9dba08ac8b | ||
|
|
3ec115115d | ||
|
|
09c91b8931 | ||
|
|
de1940065a | ||
|
|
748ef8b2ae | ||
|
|
2ecf0a9053 | ||
|
|
b82048eb18 | ||
|
|
9d58ff97fe | ||
|
|
51b787e449 | ||
|
|
7654b518f1 | ||
|
|
e7f414416f | ||
|
|
52877c9af1 | ||
|
|
265f22be49 | ||
|
|
2a26c738bd | ||
|
|
6ec0afe5f7 | ||
|
|
02b37b3c69 | ||
|
|
d8370864f3 | ||
|
|
ffe48f00e3 | ||
|
|
49dff83a00 | ||
|
|
95afd7549c | ||
|
|
06ee890db3 | ||
|
|
e680420c89 | ||
|
|
41e731fc51 | ||
|
|
19c8da04d0 | ||
|
|
357ee692bd | ||
|
|
347baa6f3e | ||
|
|
8925000c42 | ||
|
|
568c99be4e | ||
|
|
69fc42afc9 | ||
|
|
19843cd826 | ||
|
|
4eb96fc144 | ||
|
|
cc922de758 | ||
|
|
dbca906ef5 | ||
|
|
1fc4dcb76f | ||
|
|
dcd7d1c8e6 | ||
|
|
72e768ef14 | ||
|
|
047f61aad6 | ||
|
|
d04b1ffca5 | ||
|
|
3d396f13ed | ||
|
|
7d8401d707 | ||
|
|
c3aac57dda | ||
|
|
5602571c01 | ||
|
|
c04236052c | ||
|
|
c93e2f2332 | ||
|
|
e7c29d0ceb | ||
|
|
6c46358be8 | ||
|
|
b0ff8af9ac | ||
|
|
624db256c6 | ||
|
|
cfb15d158e | ||
|
|
34c7881ddd | ||
|
|
f06d930445 | ||
|
|
b29f5688bd | ||
|
|
333e2f266a | ||
|
|
af94399473 | ||
|
|
3d35f26031 | ||
|
|
b1c72ec48c | ||
|
|
8dee34459b | ||
|
|
e7f649e40a | ||
|
|
5843fbc6ee | ||
|
|
c8eca3a69d | ||
|
|
12fdcea8b8 | ||
|
|
035447b08f | ||
|
|
584cafa284 | ||
|
|
8a4a150a1a | ||
|
|
de41b93b73 | ||
|
|
55b2e748e7 | ||
|
|
3246331876 | ||
|
|
6d9fbb0e97 | ||
|
|
14fc4a06a6 | ||
|
|
cba4ba9cdc | ||
|
|
937d99150c | ||
|
|
0a6c846091 | ||
|
|
c6b8ad6e7e | ||
|
|
d92a479d2f | ||
|
|
f242182840 | ||
|
|
f5871369cc | ||
|
|
b1b184cdb8 | ||
|
|
1a6de01210 | ||
|
|
4aa1dd3ae8 | ||
|
|
be33d01663 | ||
|
|
fdcb93a467 | ||
|
|
1dd2589a42 | ||
|
|
3485e1c089 | ||
|
|
6f7965e4f1 | ||
|
|
1ba8fa6f04 | ||
|
|
6eb67fcc5f | ||
|
|
3b8e25fef4 | ||
|
|
64e83caf04 | ||
|
|
a8abea8247 | ||
|
|
d4defd943a | ||
|
|
eafd2bed5d | ||
|
|
c54f7ec95e | ||
|
|
f8448764e2 | ||
|
|
6549765930 | ||
|
|
6cb2233cd2 | ||
|
|
9ad75e1dc3 | ||
|
|
762e503978 | ||
|
|
bec0f7d975 | ||
|
|
4b772fd783 | ||
|
|
c809aea278 | ||
|
|
1596f9c9f5 | ||
|
|
2f28d9a168 | ||
|
|
baac29debc | ||
|
|
2c4df765c5 | ||
|
|
1d026b921c | ||
|
|
fcbb6a7384 | ||
|
|
bdc01f44de | ||
|
|
d2c23773ff | ||
|
|
38dc3ec0da | ||
|
|
c03acc9572 | ||
|
|
30e133d36a | ||
|
|
0890df5891 | ||
|
|
d223a49d18 | ||
|
|
26285e183e | ||
|
|
f71e81c483 | ||
|
|
9bd53b8565 | ||
|
|
dde9dda1a4 | ||
|
|
408e9f1481 | ||
|
|
7c8344930b | ||
|
|
482e254a49 | ||
|
|
eb65331403 | ||
|
|
55c8b0c2a7 | ||
|
|
9031f59f7b | ||
|
|
2d9afa6348 | ||
|
|
bae5c14941 | ||
|
|
9cf8de099c | ||
|
|
b86bd1fcd0 | ||
|
|
c156e597dc | ||
|
|
bb750b07bc | ||
|
|
0e6fe5a097 | ||
|
|
13de7cafb2 | ||
|
|
057b688fc7 | ||
|
|
45bb3e7ffc | ||
|
|
8ced9c332c | ||
|
|
9fcd5ce03d | ||
|
|
a41e6e2ed2 | ||
|
|
e54855ef88 | ||
|
|
7a18fa281d | ||
|
|
f98aaf544d | ||
|
|
78afc24736 | ||
|
|
83e4534387 | ||
|
|
aed9018807 | ||
|
|
fd1fca51fa | ||
|
|
97b5f22a70 | ||
|
|
00620b57eb | ||
|
|
bba9f38636 | ||
|
|
9c37acd002 | ||
|
|
195a9b357a | ||
|
|
cd4c4bf723 | ||
|
|
e244be2540 | ||
|
|
5dad31b06e | ||
|
|
64ded18960 | ||
|
|
4dd496790a | ||
|
|
748dab2f38 | ||
|
|
3c11129b04 | ||
|
|
6e0e343f23 | ||
|
|
cc27470cb6 | ||
|
|
fb527fa7db | ||
|
|
87976aa48e | ||
|
|
863923472a | ||
|
|
778a15b64a | ||
|
|
5eaf5570c4 | ||
|
|
cbe0f1f1e9 | ||
|
|
97472e374f | ||
|
|
7a28aa0dd7 | ||
|
|
b1054ac227 | ||
|
|
1618b98533 | ||
|
|
5b6ed54817 | ||
|
|
d23a2b7e97 | ||
|
|
ab92b27226 | ||
|
|
eb6efdf17a | ||
|
|
b182791052 | ||
|
|
97a7f174ce | ||
|
|
654758e622 | ||
|
|
c8887a5846 | ||
|
|
56779f9542 | ||
|
|
f474838e22 | ||
|
|
19393e13db | ||
|
|
301b10de8d | ||
|
|
aa2fd3a151 | ||
|
|
2dd604949f | ||
|
|
ec481ac82a | ||
|
|
7afee45635 | ||
|
|
385d18590a | ||
|
|
bc9252f6e3 | ||
|
|
6c76f2a7c7 | ||
|
|
bf187170b1 | ||
|
|
14837e8e0f | ||
|
|
b44cdf1006 | ||
|
|
5d73f90133 | ||
|
|
34a6a9d3a1 | ||
|
|
db9e782d50 | ||
|
|
f92a4e611b | ||
|
|
b3deb9ec86 | ||
|
|
dabaae1534 | ||
|
|
a92653f2b2 | ||
|
|
29cdab650b | ||
|
|
574a4eccb5 | ||
|
|
d4869ebdc5 | ||
|
|
d1fe0d449b | ||
|
|
e2b52abd47 | ||
|
|
f58eb4dc0e | ||
|
|
42c0641daf | ||
|
|
11b6e7844a | ||
|
|
f1b67152af | ||
|
|
b6be437bab | ||
|
|
8497b588a2 | ||
|
|
3d7f421809 | ||
|
|
fa6d33f742 | ||
|
|
dc22196394 | ||
|
|
3ea458b9dc | ||
|
|
86128a7f06 | ||
|
|
de60750506 | ||
|
|
57641f67dd | ||
|
|
b675c2149c | ||
|
|
4fbc30267c | ||
|
|
2a830aa567 | ||
|
|
f07ac3068b | ||
|
|
7ec94fda08 | ||
|
|
9231c80cd8 | ||
|
|
4702e58ee5 | ||
|
|
2719549cf2 | ||
|
|
cd26f29fb8 | ||
|
|
9c350af582 | ||
|
|
c019b3dad9 | ||
|
|
8cb4aabb8b | ||
|
|
7ad8b3366d | ||
|
|
c6c8f77bc0 | ||
|
|
f415a70c0b | ||
|
|
da31d94c2a | ||
|
|
84229d4735 | ||
|
|
e370ce5a00 | ||
|
|
2a1ad05671 | ||
|
|
5c0db5a293 | ||
|
|
d533d7c97c | ||
|
|
7568bff8e6 | ||
|
|
6766c8ec1d | ||
|
|
2955c1fdc7 | ||
|
|
45e9e541a4 | ||
|
|
490802099d | ||
|
|
34dd42dc7f | ||
|
|
b374868750 | ||
|
|
538688486e | ||
|
|
27111bd76d | ||
|
|
4a3f380fd2 | ||
|
|
cc87db74c7 | ||
|
|
4ec7e66e90 | ||
|
|
a42e425d44 | ||
|
|
2179c3e420 | ||
|
|
d5c9196348 | ||
|
|
aa1f0653e9 | ||
|
|
97156f3382 | ||
|
|
8771b6051f | ||
|
|
27c58c764d | ||
|
|
1564584ba9 | ||
|
|
fd10db5a62 | ||
|
|
b2ac4475cc | ||
|
|
a7952492c1 | ||
|
|
6f683da2e9 | ||
|
|
a6a06c5aa6 | ||
|
|
4d1ab1ba2a | ||
|
|
d7a6a8e35b | ||
|
|
bf2e078ecf | ||
|
|
a25ce36925 | ||
|
|
a0f4038df2 | ||
|
|
7d8c1ae295 | ||
|
|
df7c2e605b | ||
|
|
ea51cdb3f2 | ||
|
|
fe986a52d6 | ||
|
|
34fd261a4f | ||
|
|
bbd4768bb6 | ||
|
|
cb237b5193 | ||
|
|
85d72b9952 | ||
|
|
a04c33c42d | ||
|
|
529afada82 | ||
|
|
a532f4868f | ||
|
|
e1d37c37b5 | ||
|
|
ae5e0e8c05 | ||
|
|
bbea0ef439 | ||
|
|
a134c43f4d | ||
|
|
e0ebae4481 | ||
|
|
1fbc51b69f | ||
|
|
42f3e7f7ce | ||
|
|
e163cbacec | ||
|
|
0ec6870902 | ||
|
|
55421ede70 | ||
|
|
3acd247f5d | ||
|
|
59151704b4 | ||
|
|
343ee5ece1 | ||
|
|
0256ee77c2 | ||
|
|
72f5415fc8 | ||
|
|
ef8e9df02f | ||
|
|
306ee0eddb | ||
|
|
3d9e6bf454 | ||
|
|
0fc6cb4099 | ||
|
|
5744021ac9 | ||
|
|
54be05e5e4 | ||
|
|
c9e22ab67b | ||
|
|
4063f968c6 | ||
|
|
8388c53a47 | ||
|
|
3a72f3beb8 | ||
|
|
35709a30ee | ||
|
|
f9a683cf39 | ||
|
|
d7a2b52da8 | ||
|
|
0b1089e105 | ||
|
|
f43adc364a | ||
|
|
6beebbe074 | ||
|
|
6b3bdaff97 | ||
|
|
7c908bca7a | ||
|
|
019f8eb278 | ||
|
|
580f9ced08 | ||
|
|
642cc15b99 | ||
|
|
cfeade6055 | ||
|
|
90ede52728 | ||
|
|
390207cdf1 | ||
|
|
c97b2b95e6 | ||
|
|
a3bf632276 | ||
|
|
5368ee32dd | ||
|
|
e89f02bd8f | ||
|
|
ae6a749d3a | ||
|
|
0759030ee8 | ||
|
|
35f4c8b333 | ||
|
|
17974ea7a0 | ||
|
|
25852623f0 | ||
|
|
97ac32f536 | ||
|
|
2f3ef88dbf | ||
|
|
9725a0b9bf | ||
|
|
b368e89bea | ||
|
|
3269833caa | ||
|
|
922f7d1645 | ||
|
|
a1873fef61 | ||
|
|
446aa08286 | ||
|
|
12b3cc3750 | ||
|
|
6dd288d459 | ||
|
|
d8547fc721 | ||
|
|
0eff3b6b97 | ||
|
|
f6557d1ea2 | ||
|
|
718eb2b4cc | ||
|
|
cd6c01e42b | ||
|
|
68fdc4fcbe | ||
|
|
0b773dd792 | ||
|
|
b68ab3a4b4 | ||
|
|
d8c9f88fe7 | ||
|
|
f6398ba51c | ||
|
|
67dbf01eeb | ||
|
|
398cb6fb4a | ||
|
|
7c71064b64 | ||
|
|
4d91ff8d8d | ||
|
|
e778f7f805 | ||
|
|
3bff7a9e62 | ||
|
|
7302549903 | ||
|
|
705c2ac55a | ||
|
|
3f3d65e452 | ||
|
|
a9721ea268 | ||
|
|
d876bb8cf3 | ||
|
|
b17dcc6773 | ||
|
|
e3ee2b327b | ||
|
|
74b91d6dec | ||
|
|
7a3b7ae812 | ||
|
|
e971bc79d6 | ||
|
|
35df67c893 | ||
|
|
290303e64f | ||
|
|
f8ab7a5158 | ||
|
|
650ad28f12 | ||
|
|
decaaf4b55 | ||
|
|
b9d228c72f | ||
|
|
11e48c3fa9 | ||
|
|
c40302a7ff | ||
|
|
2637477eea | ||
|
|
964e22cfef | ||
|
|
55db69e3b2 | ||
|
|
e5c69afa98 | ||
|
|
99b08ecde5 | ||
|
|
4ecf93487e | ||
|
|
f066933418 | ||
|
|
ec901e9e31 | ||
|
|
481d8299ca | ||
|
|
3b19536df9 | ||
|
|
d814529285 | ||
|
|
2212157b61 | ||
|
|
7ebeed1ae7 | ||
|
|
7769097915 | ||
|
|
82dc3d57f2 | ||
|
|
595b0cb471 | ||
|
|
c50c3629e6 | ||
|
|
3f77cb95c5 | ||
|
|
68e9ff797c | ||
|
|
887b6f4da7 | ||
|
|
9c6cfcd2e9 | ||
|
|
658c7d1dbf | ||
|
|
97ee6e2587 | ||
|
|
ecfe6e4e7a | ||
|
|
e0215480cd | ||
|
|
bd33d9c47d | ||
|
|
f53c4e7525 | ||
|
|
3690c64e88 | ||
|
|
c489f786b6 | ||
|
|
5c40cc43b2 | ||
|
|
54ae850322 | ||
|
|
3a025d39db | ||
|
|
1ed454d102 | ||
|
|
52250d518d | ||
|
|
a9b9d2f6d3 | ||
|
|
f00f92e7e3 | ||
|
|
bbe26df83d | ||
|
|
bfa2ff2bdf | ||
|
|
c62b27f014 | ||
|
|
20cf0d3ea9 | ||
|
|
daf9988b3a | ||
|
|
ee67452772 | ||
|
|
02caf7ce4a | ||
|
|
e1c7367a2e | ||
|
|
781364f7f9 | ||
|
|
84d016ddcf | ||
|
|
fc4641c5a5 | ||
|
|
0241fdac1a | ||
|
|
d62bae442b | ||
|
|
9cc8947a43 | ||
|
|
8f9e2c7db2 | ||
|
|
bff68fa079 | ||
|
|
00c59041ea | ||
|
|
a77b81f0e5 | ||
|
|
f839588c69 | ||
|
|
27d719cd4d | ||
|
|
46be03d88e | ||
|
|
f9bd5eb79e | ||
|
|
d89a4ab3df | ||
|
|
57b18d8a18 | ||
|
|
95465ca03a | ||
|
|
7a15dc6772 | ||
|
|
bfd7f468ce | ||
|
|
6513808720 | ||
|
|
6c1773542d | ||
|
|
c60422241b | ||
|
|
cca1e77f95 | ||
|
|
cc81287665 | ||
|
|
ca95153b45 | ||
|
|
92dcb22e92 | ||
|
|
f75b1e5f07 | ||
|
|
fb9e47d8c9 | ||
|
|
43ae6e0a6b | ||
|
|
724d6d7c95 | ||
|
|
95c20aef20 | ||
|
|
791f77b879 | ||
|
|
a6986864db | ||
|
|
eb330a6df6 | ||
|
|
b498979d4d | ||
|
|
30866d06d5 | ||
|
|
f047b3f8e7 | ||
|
|
c67787536b | ||
|
|
3cfc96d4de | ||
|
|
91a8c66537 | ||
|
|
5c3a4746b7 | ||
|
|
8b474429b7 | ||
|
|
91b4e02805 | ||
|
|
e65e653ea5 | ||
|
|
b8e0004ff2 | ||
|
|
29a1a1f8b0 | ||
|
|
71de737ee8 | ||
|
|
4e0aabd8a7 | ||
|
|
143a177055 | ||
|
|
55fbacb469 | ||
|
|
d45a9cad8c | ||
|
|
2a6aac4c14 | ||
|
|
285a3b7522 | ||
|
|
4fd8431ad0 | ||
|
|
d4255818c4 | ||
|
|
d3e8dd3623 | ||
|
|
1fa6ebacf4 | ||
|
|
f714f3ee78 | ||
|
|
36bc351557 | ||
|
|
d5ee330d20 | ||
|
|
8fa822207d | ||
|
|
52ad3c8425 | ||
|
|
e5abcd217c | ||
|
|
4a0118aea1 | ||
|
|
634e9d6213 | ||
|
|
9aa9a92f72 | ||
|
|
90a748f7fb | ||
|
|
2434653867 | ||
|
|
1def587884 | ||
|
|
c56bc9ed05 | ||
|
|
8c51c0fdbd | ||
|
|
24e4a545a2 | ||
|
|
c5fb5e8bb0 | ||
|
|
b8434182c9 | ||
|
|
e1f942f26a | ||
|
|
2a9ba51907 | ||
|
|
91b85efabb | ||
|
|
0057905ca0 | ||
|
|
572e26deb8 | ||
|
|
406ae3d8a6 | ||
|
|
8a674265f3 | ||
|
|
caff9a135f | ||
|
|
ed43033c39 | ||
|
|
0a7b008b21 | ||
|
|
ce24b92758 | ||
|
|
f8e908b89c | ||
|
|
2695c5305e | ||
|
|
55fdfef9d1 | ||
|
|
f5a086ac98 | ||
|
|
2f6da393e2 | ||
|
|
cb1025e0e8 | ||
|
|
a46cdb8bbc | ||
|
|
57b17bbb70 | ||
|
|
34e2035bad | ||
|
|
7c23da598d | ||
|
|
b7cc218a03 | ||
|
|
0ae8019e7f | ||
|
|
c3fc0229e5 | ||
|
|
76196b8ffa | ||
|
|
fd4e683151 | ||
|
|
a9ba7b8ecb | ||
|
|
962d647993 | ||
|
|
a8520ba59e | ||
|
|
2b05d8531d | ||
|
|
8e966688a7 | ||
|
|
f12d7b4e67 | ||
|
|
bbea6e6375 | ||
|
|
b24b8424b1 | ||
|
|
35bd1a4c02 | ||
|
|
818352c3cd | ||
|
|
742efe989f | ||
|
|
4cea1c7189 | ||
|
|
18ddda48d2 | ||
|
|
5651519f5d | ||
|
|
c4ec9234e9 | ||
|
|
2e0eabbf0a | ||
|
|
1b2435fab6 | ||
|
|
1ff3dafe4e | ||
|
|
281e8ff766 | ||
|
|
dbc0710bc9 | ||
|
|
8f28f31b78 | ||
|
|
3f651d9fa9 | ||
|
|
18fced7e7f | ||
|
|
6b16e93f1b | ||
|
|
f1f0158741 | ||
|
|
3d6c542e34 | ||
|
|
c3036116de | ||
|
|
9e3653a612 | ||
|
|
27a00d1bfd | ||
|
|
b7467a3110 | ||
|
|
800837ff3f | ||
|
|
bfbfb6dbf3 | ||
|
|
8fb1b1d412 | ||
|
|
af98467d0d | ||
|
|
f9d50d580c | ||
|
|
496805476e | ||
|
|
962e565591 | ||
|
|
862625a4bf | ||
|
|
2d8aade74b | ||
|
|
2aad2cfc53 | ||
|
|
ad94405dac | ||
|
|
518fcf7e03 | ||
|
|
1fbd00e1e8 | ||
|
|
cae229cadc | ||
|
|
9d3dcdbc38 | ||
|
|
d52fabce72 | ||
|
|
637d4c3083 | ||
|
|
1e96d9a87f | ||
|
|
49172b256c | ||
|
|
9a94f48663 | ||
|
|
2e9c6c2586 | ||
|
|
4224ecd2ab | ||
|
|
92522473b0 | ||
|
|
56200ca044 | ||
|
|
524f283cc6 | ||
|
|
a2acf0f7cf | ||
|
|
d85f1b942d | ||
|
|
8da41bdc2b | ||
|
|
4a926e489b | ||
|
|
746dd91e86 | ||
|
|
763dec881a | ||
|
|
2a5715fad5 | ||
|
|
ec58272b95 | ||
|
|
f90d3af403 | ||
|
|
e18c10d340 | ||
|
|
c2659401b0 | ||
|
|
9840d4a4a3 | ||
|
|
fc6d94a9f8 | ||
|
|
61a7be0eab | ||
|
|
e4a439aa0c | ||
|
|
0719dcf2e7 | ||
|
|
3400a312dd | ||
|
|
b2f9144e10 | ||
|
|
b844cafb9c | ||
|
|
a74f173902 | ||
|
|
a1cd68e3f8 | ||
|
|
eec2c19ad1 | ||
|
|
05fdd04262 | ||
|
|
1d9522a7a6 | ||
|
|
ffe80579df | ||
|
|
bcd17cf99a | ||
|
|
3e36ddbceb | ||
|
|
bc15723f7f | ||
|
|
7a0cc03532 | ||
|
|
d36f6783c7 | ||
|
|
d339ab51eb | ||
|
|
fee70031f6 | ||
|
|
303edb71bd | ||
|
|
314f0ae964 | ||
|
|
0835980ef1 | ||
|
|
baf55e4616 | ||
|
|
0a55e5ca54 | ||
|
|
a7ad0c4434 | ||
|
|
271bac5347 | ||
|
|
914ffc8887 | ||
|
|
dd62c2f946 | ||
|
|
7a4e35e317 | ||
|
|
1bc85cb59e | ||
|
|
638cecf43c | ||
|
|
a97cfe513d | ||
|
|
9de3c0495f | ||
|
|
67e6f8245b | ||
|
|
31c93c483a | ||
|
|
86d4d5689c | ||
|
|
663e82bc19 | ||
|
|
51109e3f72 | ||
|
|
a11363e9e7 | ||
|
|
cc57913d7a | ||
|
|
f992c68fd8 | ||
|
|
4bb7a86753 | ||
|
|
057d169492 | ||
|
|
e5a2a25faf | ||
|
|
147521b757 | ||
|
|
950194fb63 | ||
|
|
b528fc438c | ||
|
|
811604a663 | ||
|
|
31d77e29b6 | ||
|
|
4b0ff07bcb | ||
|
|
0ee18d4421 | ||
|
|
21da45e5c3 | ||
|
|
d5ed967f58 | ||
|
|
42dafcc62a | ||
|
|
cad282ebb7 | ||
|
|
7f2ac9c871 | ||
|
|
87aaff5fae | ||
|
|
b411010237 | ||
|
|
d2d86ac360 | ||
|
|
a827725a8d | ||
|
|
3badbc50e0 | ||
|
|
cd60de443e | ||
|
|
95e40115a2 | ||
|
|
66c324d7ef | ||
|
|
6b056eb53f | ||
|
|
8cfdcf9a6f | ||
|
|
0917cd7010 | ||
|
|
bdd78ce38e | ||
|
|
7497db6e37 | ||
|
|
43e2942fa0 | ||
|
|
51bc2f3b76 | ||
|
|
483683c197 | ||
|
|
c95d341220 | ||
|
|
be2c6f3b5e | ||
|
|
2d5736f297 | ||
|
|
1a51193899 | ||
|
|
8b51abef08 | ||
|
|
cbb49e8c84 | ||
|
|
dc12da4573 | ||
|
|
7af58f74b0 | ||
|
|
1081876810 | ||
|
|
2ff0dd3fef | ||
|
|
4232bf9e2f | ||
|
|
8a3620e438 | ||
|
|
a30fc69f86 | ||
|
|
022b5cf193 | ||
|
|
7620c03b72 | ||
|
|
1408861511 | ||
|
|
44229e8af6 | ||
|
|
6324dae114 | ||
|
|
e0237c6b8c | ||
|
|
1cf3e02bfb | ||
|
|
d002323de2 | ||
|
|
999c7246c3 | ||
|
|
6ca5405db9 | ||
|
|
fa31f2a3eb | ||
|
|
d03f9454b9 | ||
|
|
616fb1cbcf | ||
|
|
b7d1e6257c | ||
|
|
c10a597bf0 | ||
|
|
75ed7b27c6 | ||
|
|
ef01e9daf8 | ||
|
|
866cb2d16f | ||
|
|
6a62d49720 | ||
|
|
f201d246fd | ||
|
|
b101ffaed6 | ||
|
|
f7dad4f38f | ||
|
|
b88c08f860 | ||
|
|
cc47f4d1c2 | ||
|
|
3909d7e92f | ||
|
|
d70edfa0a0 | ||
|
|
d22ab85b84 | ||
|
|
0328fe44e4 | ||
|
|
bb85983a29 | ||
|
|
4341d6a095 | ||
|
|
10a9ce1ed2 | ||
|
|
943490cfa1 | ||
|
|
7e6bbc63ab | ||
|
|
5c30b145df | ||
|
|
0d195698f8 | ||
|
|
e446cba6a9 | ||
|
|
8a0b4e0d8c | ||
|
|
02a9cdff57 | ||
|
|
f516f8a1d4 | ||
|
|
08fd890a89 | ||
|
|
ab0081294c | ||
|
|
c42a939e98 | ||
|
|
6c9e2f7e6c | ||
|
|
992f04f328 | ||
|
|
bef4865d91 | ||
|
|
fd62f419ac | ||
|
|
7fa6f320ed | ||
|
|
19f3fb131b | ||
|
|
551e8fcd24 | ||
|
|
8a90fce476 | ||
|
|
04b4952f0e | ||
|
|
e928e80a42 | ||
|
|
0d77555c97 | ||
|
|
67141fb4bb | ||
|
|
2efa762c2b | ||
|
|
6c2dd193cf | ||
|
|
881bec958e | ||
|
|
8899c751e5 | ||
|
|
28c04b21f6 | ||
|
|
46427f1dd7 | ||
|
|
528e856566 | ||
|
|
14a934d412 | ||
|
|
d0caf0a322 | ||
|
|
91b5108eae | ||
|
|
2c7f73f9c4 | ||
|
|
5310592967 | ||
|
|
41bddae907 | ||
|
|
0faed7aeb5 | ||
|
|
8e6450c8fc | ||
|
|
6898e26a7a | ||
|
|
e2cf40ef99 | ||
|
|
96515c9f47 | ||
|
|
96e18faa93 | ||
|
|
476c6e89ad | ||
|
|
b46a96d454 | ||
|
|
f67cf7d053 | ||
|
|
2ad0f4a9e7 | ||
|
|
1ec9ca497b | ||
|
|
dd5d0f4f0c | ||
|
|
d1a6ce8e7f | ||
|
|
b22537e8a4 | ||
|
|
a25c14d472 | ||
|
|
7730d38339 | ||
|
|
58e498c9d3 | ||
|
|
35485f2b5e | ||
|
|
b108653f02 | ||
|
|
25f72cf240 | ||
|
|
d1df8dcccc | ||
|
|
a50d00aa47 | ||
|
|
34add944f2 | ||
|
|
95f85efc49 | ||
|
|
f2f6a05e2c | ||
|
|
bd442a27ee | ||
|
|
73c0eb96c3 | ||
|
|
6a4a4a432b | ||
|
|
263529e886 | ||
|
|
818797ff08 | ||
|
|
d263dbe9dd | ||
|
|
67319ad9d9 | ||
|
|
2591df66cc | ||
|
|
2ac78bccb5 | ||
|
|
47de44001e | ||
|
|
eaa467618b | ||
|
|
ac01f15bfe | ||
|
|
5df5217e2e | ||
|
|
a6ec2b76c2 | ||
|
|
bc01b300c4 | ||
|
|
c8d4d8cb66 | ||
|
|
315202b007 | ||
|
|
7194fdca38 | ||
|
|
31d0945059 | ||
|
|
466e79d572 | ||
|
|
3bb4f72a4b | ||
|
|
329e9d54ab | ||
|
|
9c45dd693b | ||
|
|
cc671e4495 | ||
|
|
61ca158de1 | ||
|
|
7e0038c691 | ||
|
|
04792d0656 | ||
|
|
6c5cc40039 | ||
|
|
6b462474f0 | ||
|
|
9a061a2fee | ||
|
|
6dd552d689 | ||
|
|
34e858ea05 | ||
|
|
39d2839e37 | ||
|
|
7980d00e8c | ||
|
|
25ce5b4281 | ||
|
|
0cd3ab6299 | ||
|
|
28d038fb16 | ||
|
|
10dc308e6b | ||
|
|
6344bab6cc | ||
|
|
984fbacccf | ||
|
|
cc83ca6628 | ||
|
|
04fc6ea11b | ||
|
|
8a9e2fccbd | ||
|
|
cc1324e2d9 | ||
|
|
b2226d1608 | ||
|
|
8ce0af3fc9 | ||
|
|
b315a6c3d1 | ||
|
|
baf642b7d2 | ||
|
|
8db76e9057 | ||
|
|
e3b7bf9b31 | ||
|
|
38724f2e86 | ||
|
|
65fc09fac9 | ||
|
|
3197b715b3 | ||
|
|
26362dfc72 | ||
|
|
275781ce45 | ||
|
|
72dbbfedce | ||
|
|
a6c6eabb5a | ||
|
|
379e4d976c | ||
|
|
a6514e2656 | ||
|
|
032af23e18 | ||
|
|
b5f9293014 | ||
|
|
a37db55689 | ||
|
|
1b959d0eff | ||
|
|
ce03f1abea | ||
|
|
5faf41b695 | ||
|
|
7634daa834 | ||
|
|
968f439672 |
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@@ -63,7 +63,7 @@ tmux -vv -Ltest -f/dev/null new
|
||||
|
||||
Or if you need your configuration:
|
||||
|
||||
~~~base
|
||||
~~~bash
|
||||
tmux kill-server
|
||||
tmux -vv new
|
||||
~~~
|
||||
|
||||
11
.github/copilot-instructions.md
vendored
Normal file
11
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
For pull request code reviews in this repository, do not leave any review comments under any circumstances.
|
||||
|
||||
If asked to review a pull request, produce no comments.
|
||||
|
||||
Do not comment on pull requests in this repository.
|
||||
|
||||
For pull request code reviews in this repository, do not leave a pull request overview.
|
||||
|
||||
Do not leave inline review comments.
|
||||
|
||||
If asked to review a pull request, provide no summary, no overview, and no code comments.
|
||||
2
.github/workflows/lock.yml
vendored
2
.github/workflows/lock.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
action:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v5
|
||||
- uses: dessant/lock-threads@v6
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
issue-inactive-days: '30'
|
||||
|
||||
75
.github/workflows/regress.yml
vendored
Normal file
75
.github/workflows/regress.yml
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
name: 'Run Tests'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '33 3 * * *'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: tmux-tests
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
regress:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 45
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: ubuntu-24.04-x64
|
||||
runner: ubuntu-24.04
|
||||
make: make
|
||||
# - name: ubuntu-24.04-arm64
|
||||
# runner: ubuntu-24.04-arm
|
||||
# make: make
|
||||
# - name: macos-26-arm64
|
||||
# runner: macos-26
|
||||
# make: gmake
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: dependencies
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
autoconf \
|
||||
automake \
|
||||
bison \
|
||||
build-essential \
|
||||
libevent-dev \
|
||||
libncurses-dev \
|
||||
libutf8proc-dev \
|
||||
pkg-config
|
||||
|
||||
- name: dependencies
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew install \
|
||||
autoconf \
|
||||
automake \
|
||||
bison \
|
||||
libevent \
|
||||
make \
|
||||
ncurses \
|
||||
utf8proc \
|
||||
pkg-config
|
||||
|
||||
- name: build
|
||||
run: |
|
||||
sh autogen.sh
|
||||
./configure --enable-utf8proc
|
||||
${{ matrix.make }} -j"$(getconf _NPROCESSORS_ONLN)"
|
||||
|
||||
- name: test
|
||||
run: |
|
||||
cd regress
|
||||
${{ matrix.make }}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,6 +7,8 @@
|
||||
*~
|
||||
.deps/
|
||||
.dirstamp
|
||||
.serena/
|
||||
AGENTS.md
|
||||
Makefile
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
|
||||
348
CHANGES
348
CHANGES
@@ -1,3 +1,351 @@
|
||||
CHANGES FROM 3.7a TO 3.7b
|
||||
|
||||
* Fix so that the end of a synchronized update again triggers a redraw.
|
||||
|
||||
CHANGES FROM 3.7 TO 3.7a
|
||||
|
||||
* Fix crash in break-pane when no name is provided.
|
||||
|
||||
* Scrollbar options are now cached rather than being looked up for every redraw
|
||||
(issue 5298).
|
||||
|
||||
* Only forbid #( in names, allow #[, empty names, : and .
|
||||
|
||||
CHANGES FROM 3.6b TO 3.7
|
||||
|
||||
* Add floating panes. These are panes which sit above the layout ("tiled
|
||||
panes") like popups but unlike popups are not modal and behave like panes (so
|
||||
the same escape sequence support). Floating panes are created with the
|
||||
new-pane command, bound to * by default.
|
||||
|
||||
This is an early release of this feature and they are relatively limited.
|
||||
Currently floating panes can only be moved and resized using the mouse. The
|
||||
default second status line (if status-format is set to 2) has changed to show
|
||||
a list of panes. Many obvious features are not yet available for floating
|
||||
panes (notably the ability to swap floating panes, resize them using
|
||||
resize-pane, change them between floating and tiles, and restore custom
|
||||
layouts with floating panes).
|
||||
|
||||
Mostly written by Michael Grant with help from Dane Jensen; testing and fixes
|
||||
from others.
|
||||
|
||||
* Allow run-shell arguments after a shell command to be expanded as #{1}, #{2}
|
||||
and so on (from Rasmus Thystrup Karstensen in issue 5121).
|
||||
|
||||
* Add -g to kill-session to kill all sessions in a session group (issue 5157
|
||||
from github at jiku dot jp).
|
||||
|
||||
* Tighten up read-only checks on attach-session, detach-client and
|
||||
switch-client so that a user should be able to only detach their own client
|
||||
(reported by John Walker).
|
||||
|
||||
* Increase escape delay if the buffer contains a partial paste end, fixes
|
||||
issues with at least Windows Terminal (from jing dot empty at gmail.com issue
|
||||
5088).
|
||||
|
||||
* When mode-keys is set to vi, do not allow the cursor to go into the invisible
|
||||
extra cell to the right of the visible text; this is closer to what vi(1)
|
||||
does (from Max Vim in issue 5070).
|
||||
|
||||
* Add a five second limit on pasting for terminals which mysteriously lose the
|
||||
end sequence if the paste is too big (that is, Terminal.app) (reported by
|
||||
Garri Djavadyan in issue 4527).
|
||||
|
||||
* Show file open errors more sensibly (reported by Meriel Luna Mittelbach in
|
||||
issue 5081).
|
||||
|
||||
* Update supported features list for Foot terminal (from Meriel Luna Mittelbach
|
||||
in issue 5079).
|
||||
|
||||
* Turn off the "is this a paste" guessing if the terminal supports bracket
|
||||
pasting instead (issue 5031).
|
||||
|
||||
* Check FIONREAD for all panes not just piped panes.
|
||||
|
||||
* Add emacs-style recentre-top-bottom command to copy mode (issue 5053 from
|
||||
sinyax75 at gmail dot com).
|
||||
|
||||
* Allow the indicator in tree mode to be customized by two new options:
|
||||
tree-mode-preview-format and tree-mode-preview-style.
|
||||
|
||||
* Fix control client hang on exit after toggling no-output (issue 5049 from
|
||||
Aaron Campbell). Also various other control mode fixes.
|
||||
|
||||
* Add support for line numbers in copy mode. There is a new
|
||||
copy-mode-line-numbers option which may be set to off, default (tmux's normal
|
||||
line numbering where 0 is the top visible line), absolute (first line in
|
||||
history is 1), relative (relative to the cursor) and hybrid (current line is
|
||||
absolute, others relative). Also adds copy-mode-line-number-style and
|
||||
copy-mode-current-line-number-style to set the style of the line numbers.
|
||||
When copy mode is entered with the mouse, line numbers stay off. From Leo
|
||||
Henon in issue 5025.
|
||||
|
||||
* Make C-[ have the same bindings as Escape for terminals with extended
|
||||
keys where they are different (issue 5035 from Eric Nicolas).
|
||||
|
||||
* Sanitize paste buffer names in paste_set and paste_rename (issue 5032 from
|
||||
Barrett Ruth).
|
||||
|
||||
* Do not hang in run-shell when job_run fails (from Barrett Ruth in issue
|
||||
5037).
|
||||
|
||||
* Add ability to forward progress bar to outside terminal (issue 4972
|
||||
from Eric Dorland).
|
||||
|
||||
* Translate keypad keys to text in prompt input (from Barrett Ruth in issue
|
||||
4996).
|
||||
|
||||
* Sanitize pane titles and window and session names more consistently and
|
||||
strictly, prevents C0 characters and other invisible characters causing
|
||||
problems (reported by Chris Monardo in issue 4999).
|
||||
|
||||
* Make clock visible on terminals without colours (from Manuel Einfalt in issue
|
||||
5001).
|
||||
|
||||
* Add detach to default session menu (suggested by Przemyslaw Sztoch).
|
||||
|
||||
* Include window format variables for pane notifications (issue 5007 from Saul
|
||||
Nogueras).
|
||||
|
||||
* Limit precision to 100 for formats to stop silly formats from running out of
|
||||
memory, reported by z1281552865 at gmail dot com. Also various other similar
|
||||
changes, mostly found by OSS-Fuzz.
|
||||
|
||||
* Add WAYLAND_DISPLAY to default update-environment (issue 4965). Also some
|
||||
additional XDG_* variables (issue 5169).
|
||||
|
||||
* Add -C flag to command-prompt to match display-message -C (do not freeze
|
||||
panes) (from Barrett Ruth in issue 4978).
|
||||
|
||||
* Cache user from getpwuid because it can be very expensive on some
|
||||
platforms (from Ben Maurer in issue 4973).
|
||||
|
||||
* Add remain-on-exit key to keep pane around until a key is pressed (from
|
||||
Michael Grant).
|
||||
|
||||
* Add some new mouse ranges called "control0" to "control9" and use to add some
|
||||
mouse controls to the pane state line (from Dane Jensen with some bits from
|
||||
Michael Grant).
|
||||
|
||||
* Handle OSC 9;4 progress bar sequence and store in format variables (from Eric
|
||||
Dorland in issue 4954).
|
||||
|
||||
* Correctly size buffer used for parsing clipboard sequences (from Michal
|
||||
Majchrowicz).
|
||||
|
||||
* Limit MSG_COMMAND argument to between 0 and 1000 to prevent a misbehaving
|
||||
client from crashing the server (from Michal Majchrowicz).
|
||||
|
||||
* Reorganize host keys are represented internally so they can be built more
|
||||
easily (from Dane Jensen in issue 4953).
|
||||
|
||||
* Add new fuzzers for command parsing, formats and styles (from David
|
||||
Korczynski in issue 4957). Also fix various issues shown from these.
|
||||
|
||||
* Add bracket_paste_flag format flag (from George Nachman in issue 4951).
|
||||
|
||||
* Use \- for hyphens in tmux.1 to cause newer groff versions to render them
|
||||
correctly (from Keith Thompson in issue 4948).
|
||||
|
||||
* Various minor code improvements and fixes from Pavel Lavrukhin (issue 4936
|
||||
and others).
|
||||
|
||||
* Use window options for cursor-style to avoid crash when no pane (from Arden
|
||||
Packeer in issue 4942).
|
||||
|
||||
* Fix issue where popup window gets overwritten by background updates (from
|
||||
Conor Taylor in issue 4920).
|
||||
|
||||
* Protect against overflow when scrollbar is off screen (from san65384 at gmail
|
||||
dot com in issue 4933).
|
||||
|
||||
* Copy hyperlinks when redrawing popup so they do not vanish (from Antoine
|
||||
Gaudreau Simard in issue 4925).
|
||||
|
||||
* Work around systemd killing panes early during system shutdown by creating
|
||||
dependencies from the panes to the service which started tmux (issue 4926
|
||||
from Dmitry Torokhov).
|
||||
|
||||
* Allow codepoint-widths to accept ranges (from san65384 at gmail dot com in
|
||||
issue 4930).
|
||||
|
||||
* Add a short builtin help text for each mode accessible with C-h (based on
|
||||
code from Patrick Motard in issue 4751).
|
||||
|
||||
* Draw message as one format, allowing prompts and messages to occupy only a
|
||||
portion of the status bar, overlaying the normal status content rather than
|
||||
replacing the entire line. A new message-format option now controls the
|
||||
entire message (like status-format). The message-style option now need to
|
||||
include "fill" in order to cover the whole width (the default has
|
||||
"fill=yellow"). From Conor Taylor in issue 4861.
|
||||
|
||||
* Add next/previous variables for windows in W: loop (from Conor Taylor in
|
||||
issue 4856).
|
||||
|
||||
* Various bug and memory leak fixes from Renaud Allard (issue 4916).
|
||||
|
||||
* Add pane_pipe_pid with pipe file descriptor.
|
||||
|
||||
* Make -c work with new-session -A (from Jody Frankowski in issue 4906).
|
||||
|
||||
* Allow copy mode to work for readonly clients, except for copy commands (from
|
||||
Dane Jensen).
|
||||
|
||||
* Pass paste buffer through vis(3) when pasting to prevent buffers containing
|
||||
for example the bracket end sequence causing issues, a new -S flag disables
|
||||
(reported by Mason Davis).
|
||||
|
||||
* Add sorting (-O flag) and a custom format (-F) to list-keys (from Dane Jensen
|
||||
in issue 4845).
|
||||
|
||||
* Add scroll-exit-on, scroll-exit-off, scroll-exit-toggle commands to copy mode
|
||||
(from xcdnlgd at hotmail dot com in issue 4884).
|
||||
|
||||
* Respond to DECRQM 2026 (from David Turnbull in issue 4887) and various others
|
||||
(from Ayman Bagabas in issue 5118).
|
||||
|
||||
* Fix various memory leaks reported by Huihui Huang (issue 4872).
|
||||
|
||||
* Pass which clipboard is set through to the terminal (from Axel Lindskog in
|
||||
issue 4858).
|
||||
|
||||
* Reuse extended entry when clearing RGB cell, to prevent memory growth when
|
||||
cells are repeatedly cleared (from Michael K Darling in issue 4862).
|
||||
|
||||
* Do not write before buffer when parsing empty clipboard or palette replies,
|
||||
or try to allocate zero bytes with an empty clipboard sequence (reported by
|
||||
DongHan Kim).
|
||||
|
||||
* Various bug fixes and code improvements from Conor Taylor (issue 4848).
|
||||
|
||||
* Clear search counts when clearing marks in case of repeated search (reported
|
||||
by Daniel Pereira in issue 4817).
|
||||
|
||||
* Make OSC 52 work in popups (from gogongxt at 163 dot com in issue 4797).
|
||||
|
||||
* Refresh copy mode when style changes (from Josh Cooper in issue 4830).
|
||||
|
||||
* Make sorting code common and add -O for sorting to the list commands (from
|
||||
Dane Jensen in issue 4813).
|
||||
|
||||
* Do not treat cells as empty unless the background colour stays the same,
|
||||
fixes invisible clock in clock mode (reported by Theo Buehler).
|
||||
|
||||
* When history-limit is changed, apply to existing panes, not just new
|
||||
ones (issue 4705).
|
||||
|
||||
* Reevaluate menu and popup styles on each draw to allow them to change when
|
||||
options change (from Josh Cooper in issues 4828 and 4829).
|
||||
|
||||
* Handle theme keys earlier so they are processed even if a popup is open (from
|
||||
Josh Cooper in issue 4827).
|
||||
|
||||
* Fix window-size=latest not resizing on switch-client in session groups (from
|
||||
Ilya Grigoriev in issue 4818).
|
||||
|
||||
* Add -e flag to command-prompt to close if empty (from Dane Jensen in issue
|
||||
4812).
|
||||
|
||||
* Correctly draw indicators when pane-border-indicators is set to both
|
||||
(reported by Ilya Grigoriev in issue 4780).
|
||||
|
||||
* Remember last pane or type of location for double and triple clicks and
|
||||
correctly handle it changes between first and second or second and third
|
||||
(issue 4795 from Shaobo Song).
|
||||
|
||||
* Add paste to the default pane menu (issue 4763).
|
||||
|
||||
* Reduce request timeout to 500 milliseconds to match the extended escape-time,
|
||||
and discard palette requests if receiving a reply for a different index.
|
||||
|
||||
* Extend escape timeout if there are active forwarded requests not just
|
||||
tmux's own requests (issue 4793).
|
||||
|
||||
* Correct redrawing of wide characters when overwritten (reported by Jake
|
||||
Stewart in issue 4737).
|
||||
|
||||
* If cannot find a terminator for palette responses, treat as a partial key not
|
||||
complete (issue 4749).
|
||||
|
||||
* Do not send theme unless it has changed, and do not send immediately when
|
||||
updates are enabled (issue 5787).
|
||||
|
||||
* Do not use ;;s in list-keys output as it is confusing and cannot be
|
||||
parsed on input (from Patrick Motard in issue 4750).
|
||||
|
||||
* Redraw pane borders when entering or leaving alternate screen (from Mike
|
||||
Jonkmans in issue 4788).
|
||||
|
||||
* Add focus-follows-mouse option (from Barry Wasdell in issue 4771).
|
||||
|
||||
* Add selection_mode format variable for copy mode (from Mike Jonkmans in issue
|
||||
4773).
|
||||
|
||||
* Add prompt-command-cursor-style (from Joshua Cooper in issue 4765).
|
||||
|
||||
* With status-keys vi, move the cursor left by one when pressing Escape to
|
||||
enter command mode, like vi (issue 4767 from Joshua Cooper).
|
||||
|
||||
* Add {current}/{active} for -t for current window or active pane (from Manuel
|
||||
Einfalt, issue 4766).
|
||||
|
||||
* Add support for applications to use synchronized output mode (DECSET 2026) to
|
||||
prevent screen tearing during rapid updates (from Chris Lloyd in issue 4744).
|
||||
|
||||
* Do not set a default prompt cursor colour because some terminals (urxvt, st)
|
||||
do not support the reset sequence (issue 4759).
|
||||
|
||||
* Add a scroll-to-mouse command for copy mode to scroll to the mouse position
|
||||
and bind to the scrollbar, brings the scrollbar keys into line with the other
|
||||
mouse keys (from Michael Grant in issue 4731).
|
||||
|
||||
* Bump the maximum number of SIXEL images to 20.
|
||||
|
||||
* Fix key code for M-BSpace (issue 4717).
|
||||
|
||||
* Fix calculation of scaled SIXEL size (from nincsnevem662 at gmail dot com in
|
||||
issue 4739). Also various other fixes and improvements for SIXEL.
|
||||
|
||||
* Fix a race between fork and pane_current_path, most noticeable on systems
|
||||
where starting processes is slow (issue 4719).
|
||||
|
||||
* Fix mouse position calculation on scrollbar with pane status line at the top
|
||||
(issue 4738 from Michael Grant).
|
||||
|
||||
* Do not read outside buffer if format is a single #, and do not loop forever
|
||||
if UTF-8 is unfinished in a format (reported by Giorgi Kobakhia in issue
|
||||
4735).
|
||||
|
||||
* Add a missing skin tone character (from Jake Stewart in issue 4736).
|
||||
|
||||
* Allow UTF-8 characters to be combined in either order (reported by Jake
|
||||
Stewart in issue 4726).
|
||||
|
||||
* Do not show scrollbar when entering copy mode from a pane in the alternate
|
||||
screen (issue 4728 from Michael Grant).
|
||||
|
||||
* Fix the size calculation for left-right windows used to spread out cells
|
||||
horizontally evenly (from Michael Grant in issue 4724).
|
||||
|
||||
* Add a get-clipboard option which when enabled (the default is off) and a
|
||||
clipboard is requested from a pane, requests it from the terminal and
|
||||
forwards to the requesting pane; also remove the now-redundant
|
||||
forward-to-pane ability from "refresh-client -l" (issue 4275).
|
||||
|
||||
* Fix the noattr attribute in styles, used by the default mode-style (issue
|
||||
4713).
|
||||
|
||||
* Do not remove TERM for commands run from config file (regression reported by
|
||||
Dennis Eriksen).
|
||||
|
||||
* Add seconds options for clock mode (from augustus7613 dot mail at pm dot me,
|
||||
issue 4697; later improved by Joao Pedro in issue 4760).
|
||||
|
||||
CHANGES FROM 3.6a TO 3.6b
|
||||
|
||||
* Remove images from the correct list when they are removed while in the
|
||||
alternate screen (reported by xlabai at tencent dot com).
|
||||
|
||||
CHANGES FROM 3.6 TO 3.6a
|
||||
|
||||
* Fix a buffer overread and an infinite loop in format processing (reported by
|
||||
|
||||
7
COPYING
7
COPYING
@@ -1,9 +1,4 @@
|
||||
THIS IS FOR INFORMATION ONLY, CODE IS UNDER THE LICENCE AT THE TOP OF ITS FILE.
|
||||
|
||||
The README, CHANGES, FAQ and TODO files are licensed under the ISC license. All
|
||||
other files have a license and copyright notice at their start, typically:
|
||||
|
||||
Copyright (c) <author>
|
||||
Copyright (c) Various Authors
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
|
||||
42
Makefile.am
42
Makefile.am
@@ -27,7 +27,7 @@ else
|
||||
AM_CFLAGS += -O0
|
||||
endif
|
||||
if IS_DEBUG
|
||||
AM_CFLAGS += -g
|
||||
AM_CFLAGS += -g3 -ggdb
|
||||
AM_CFLAGS += -Wno-long-long -Wall -W -Wformat=2 -Wno-use-after-free
|
||||
AM_CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations
|
||||
AM_CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare
|
||||
@@ -41,6 +41,10 @@ endif
|
||||
AM_CPPFLAGS += -DDEBUG
|
||||
endif
|
||||
AM_CPPFLAGS += -iquote.
|
||||
if IS_ASAN
|
||||
AM_CFLAGS += -fsanitize=address
|
||||
AM_LDFLAGS += -fsanitize=address
|
||||
endif
|
||||
endif
|
||||
|
||||
# Set flags for Solaris.
|
||||
@@ -98,6 +102,7 @@ dist_tmux_SOURCES = \
|
||||
cmd-display-panes.c \
|
||||
cmd-find-window.c \
|
||||
cmd-find.c \
|
||||
cmd-hide-pane.c \
|
||||
cmd-if-shell.c \
|
||||
cmd-join-pane.c \
|
||||
cmd-kill-pane.c \
|
||||
@@ -158,6 +163,7 @@ dist_tmux_SOURCES = \
|
||||
file.c \
|
||||
format.c \
|
||||
format-draw.c \
|
||||
fuzzy.c \
|
||||
grid-reader.c \
|
||||
grid-view.c \
|
||||
grid.c \
|
||||
@@ -180,6 +186,8 @@ dist_tmux_SOURCES = \
|
||||
paste.c \
|
||||
popup.c \
|
||||
proc.c \
|
||||
prompt.c \
|
||||
prompt-history.c \
|
||||
regsub.c \
|
||||
resize.c \
|
||||
screen-redraw.c \
|
||||
@@ -205,12 +213,15 @@ dist_tmux_SOURCES = \
|
||||
tty.c \
|
||||
utf8-combined.c \
|
||||
utf8.c \
|
||||
window-border.c \
|
||||
window-buffer.c \
|
||||
window-client.c \
|
||||
window-clock.c \
|
||||
window-copy.c \
|
||||
window-customize.c \
|
||||
window-switch.c \
|
||||
window-tree.c \
|
||||
window-visible.c \
|
||||
window.c \
|
||||
xmalloc.c \
|
||||
xmalloc.h
|
||||
@@ -232,24 +243,25 @@ nodist_tmux_SOURCES += compat/utf8proc.c
|
||||
endif
|
||||
|
||||
# Enable sixel support.
|
||||
if ENABLE_SIXEL_IMAGES
|
||||
if ENABLE_SIXEL
|
||||
dist_tmux_SOURCES += image.c image-sixel.c
|
||||
else
|
||||
# If not sixel, still need image.c for kitty.
|
||||
if ENABLE_KITTY_IMAGES
|
||||
dist_tmux_SOURCES += image.c
|
||||
endif
|
||||
endif
|
||||
|
||||
# Enable kitty graphics protocol support.
|
||||
if ENABLE_KITTY_IMAGES
|
||||
dist_tmux_SOURCES += image-kitty.c
|
||||
endif
|
||||
|
||||
# Add bits for fuzzing if enabled.
|
||||
if NEED_FUZZING
|
||||
check_PROGRAMS = fuzz/input-fuzzer
|
||||
check_PROGRAMS = \
|
||||
fuzz/input-fuzzer \
|
||||
fuzz/cmd-parse-fuzzer \
|
||||
fuzz/format-fuzzer \
|
||||
fuzz/style-fuzzer
|
||||
fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
||||
fuzz_input_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
||||
fuzz_cmd_parse_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
||||
fuzz_cmd_parse_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
||||
fuzz_format_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
||||
fuzz_format_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
||||
fuzz_style_fuzzer_LDFLAGS = $(FUZZING_LIBS)
|
||||
fuzz_style_fuzzer_LDADD = $(LDADD) $(tmux_OBJECTS)
|
||||
endif
|
||||
|
||||
# Install tmux.1 in the right format.
|
||||
@@ -264,3 +276,7 @@ install-exec-hook:
|
||||
$(mkdir_p) $(DESTDIR)$(mandir)/man1
|
||||
$(INSTALL_DATA) $(srcdir)/tmux.1.@MANFORMAT@ \
|
||||
$(DESTDIR)$(mandir)/man1/tmux.1
|
||||
|
||||
# And uninstall it.
|
||||
uninstall-hook:
|
||||
rm -f $(DESTDIR)$(mandir)/man1/tmux.1
|
||||
|
||||
18
arguments.c
18
arguments.c
@@ -149,7 +149,7 @@ args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
|
||||
int optional_argument)
|
||||
{
|
||||
struct args_value *argument, *new;
|
||||
const char *s;
|
||||
const char *s, *as;
|
||||
|
||||
new = xcalloc(1, sizeof *new);
|
||||
if (*string != '\0') {
|
||||
@@ -180,12 +180,24 @@ args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
|
||||
xasprintf(cause, "-%c expects an argument", flag);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (optional_argument && argument->type == ARGS_STRING) {
|
||||
as = argument->string;
|
||||
if (as[0] == '-' && (as[1] == '-' || isalpha((u_char)as[1]))) {
|
||||
args_free_value(new);
|
||||
free(new);
|
||||
log_debug("%s: -%c (optional)", __func__, flag);
|
||||
args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -998,7 +1010,7 @@ args_string_percentage(const char *value, long long minval, long long maxval,
|
||||
copy = xstrdup(value);
|
||||
copy[valuelen - 1] = '\0';
|
||||
|
||||
ll = strtonum(copy, 0, 100, &errstr);
|
||||
ll = strtonum(copy, 0, 1000, &errstr);
|
||||
free(copy);
|
||||
if (errstr != NULL) {
|
||||
*cause = xstrdup(errstr);
|
||||
@@ -1066,7 +1078,7 @@ args_string_percentage_and_expand(const char *value, long long minval,
|
||||
copy[valuelen - 1] = '\0';
|
||||
|
||||
f = format_single_from_target(item, copy);
|
||||
ll = strtonum(f, 0, 100, &errstr);
|
||||
ll = strtonum(f, 0, 1000, &errstr);
|
||||
free(f);
|
||||
free(copy);
|
||||
if (errstr != NULL) {
|
||||
|
||||
@@ -45,7 +45,7 @@ attributes_tostring(int attr)
|
||||
(attr & GRID_ATTR_UNDERSCORE_3) ? "curly-underscore," : "",
|
||||
(attr & GRID_ATTR_UNDERSCORE_4) ? "dotted-underscore," : "",
|
||||
(attr & GRID_ATTR_UNDERSCORE_5) ? "dashed-underscore," : "",
|
||||
(attr & GRID_ATTR_OVERLINE) ? "overline," : "",
|
||||
(attr & GRID_ATTR_OVERLINE) ? "overline," : "",
|
||||
(attr & GRID_ATTR_NOATTR) ? "noattr," : "");
|
||||
if (len > 0)
|
||||
buf[len - 1] = '\0';
|
||||
|
||||
2
cfg.c
2
cfg.c
@@ -56,7 +56,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
|
||||
if (cfg_item != NULL)
|
||||
cmdq_continue(cfg_item);
|
||||
|
||||
status_prompt_load_history();
|
||||
prompt_load_history();
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
|
||||
struct window_pane *wp;
|
||||
char *cwd, *cause;
|
||||
enum msgtype msgtype;
|
||||
uid_t uid;
|
||||
|
||||
if (RB_EMPTY(&sessions)) {
|
||||
cmdq_error(item, "no sessions");
|
||||
@@ -106,8 +107,16 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
|
||||
}
|
||||
if (fflag)
|
||||
server_client_set_flags(c, fflag);
|
||||
if (rflag)
|
||||
if (rflag) {
|
||||
if (c->flags & CLIENT_READONLY) {
|
||||
uid = proc_get_peer_uid(c->peer);
|
||||
if (uid != getuid()) {
|
||||
cmdq_error(item, "client is read-only");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
c->flags |= (CLIENT_READONLY|CLIENT_IGNORESIZE);
|
||||
}
|
||||
|
||||
c->last_session = c->session;
|
||||
if (c->session != NULL) {
|
||||
|
||||
@@ -34,9 +34,10 @@ const struct cmd_entry cmd_break_pane_entry = {
|
||||
.name = "break-pane",
|
||||
.alias = "breakp",
|
||||
|
||||
.args = { "abdPF:n:s:t:", 0, 0, NULL },
|
||||
.usage = "[-abdP] [-F format] [-n window-name] [-s src-pane] "
|
||||
"[-t dst-window]",
|
||||
.args = { "abdPF:n:s:t:Wx:X:y:Y:", 0, 0, NULL },
|
||||
.usage = "[-abdPW] [-F format] [-n window-name] [-s src-pane] "
|
||||
"[-t dst-window] [-x width] [-y height] [-X x-position] "
|
||||
"[-Y y-position]",
|
||||
|
||||
.source = { 's', CMD_FIND_PANE, 0 },
|
||||
.target = { 't', CMD_FIND_WINDOW, CMD_FIND_WINDOW_INDEX },
|
||||
@@ -45,6 +46,52 @@ const struct cmd_entry cmd_break_pane_entry = {
|
||||
.exec = cmd_break_pane_exec
|
||||
};
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_break_pane_float(struct cmdq_item *item, struct args *args,
|
||||
struct window *w, struct window_pane *wp)
|
||||
{
|
||||
struct layout_cell *lc = wp->layout_cell;
|
||||
u_int sx = lc->saved_sx, sy = lc->saved_sy;
|
||||
int ox = lc->saved_xoff, oy = lc->saved_yoff;
|
||||
char *cause = NULL;
|
||||
enum pane_lines lines = window_get_pane_lines(w);
|
||||
|
||||
if (window_pane_is_floating(wp)) {
|
||||
cmdq_error(item, "pane is already floating");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (window_pane_is_hidden(wp)) {
|
||||
cmdq_error(item, "can't float a hidden pane");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (w->flags & WINDOW_ZOOMED) {
|
||||
cmdq_error(item, "can't float a pane while window is zoomed");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (layout_floating_args_parse(item, args, lines, w, &sx, &sy, &ox, &oy,
|
||||
&cause) != 0) {
|
||||
cmdq_error(item, "failed to float pane: %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
layout_remove_tile(w, lc);
|
||||
layout_set_size(lc, sx, sy, ox, oy);
|
||||
|
||||
lc->flags |= LAYOUT_CELL_FLOATING;
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
|
||||
|
||||
if (!args_has(args, 'd'))
|
||||
window_set_active_pane(w, wp, 1);
|
||||
layout_fix_offsets(w);
|
||||
layout_fix_panes(w, NULL);
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
@@ -58,9 +105,17 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct session *dst_s = target->s;
|
||||
struct window_pane *wp = source->wp;
|
||||
struct window *w = wl->window;
|
||||
char *name, *cause, *cp;
|
||||
char *newname, *cause, *cp;
|
||||
int idx = target->idx, before;
|
||||
const char *template;
|
||||
const char *template, *name = args_get(args, 'n');
|
||||
|
||||
if (args_has(args, 'W'))
|
||||
return (cmd_break_pane_float(item, args, w, wp));
|
||||
|
||||
if (name != NULL && !check_name(name)) {
|
||||
cmdq_error(item, "invalid window name: %s", name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
before = args_has(args, 'b');
|
||||
if (args_has(args, 'a') || before) {
|
||||
@@ -73,15 +128,15 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
server_unzoom_window(w);
|
||||
|
||||
if (window_count_panes(w) == 1) {
|
||||
if (window_count_panes(w, 1) == 1) {
|
||||
if (server_link_window(src_s, wl, dst_s, idx, 0,
|
||||
!args_has(args, 'd'), &cause) != 0) {
|
||||
cmdq_error(item, "%s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (args_has(args, 'n')) {
|
||||
window_set_name(w, args_get(args, 'n'));
|
||||
if (name != NULL) {
|
||||
window_set_name(w, name, 0);
|
||||
options_set_number(w->options, "automatic-rename", 0);
|
||||
}
|
||||
server_unlink_window(src_s, wl);
|
||||
@@ -96,6 +151,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
|
||||
TAILQ_REMOVE(&w->panes, wp, entry);
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
server_client_remove_pane(wp);
|
||||
window_lost_pane(w, wp);
|
||||
layout_close_pane(wp);
|
||||
@@ -104,15 +160,16 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
options_set_parent(wp->options, w->options);
|
||||
wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
|
||||
TAILQ_INSERT_HEAD(&w->panes, wp, entry);
|
||||
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
|
||||
w->active = wp;
|
||||
w->latest = tc;
|
||||
|
||||
if (!args_has(args, 'n')) {
|
||||
name = default_window_name(w);
|
||||
window_set_name(w, name);
|
||||
free(name);
|
||||
if (name == NULL) {
|
||||
newname = default_window_name(w);
|
||||
window_set_name(w, newname, 0);
|
||||
free(newname);
|
||||
} else {
|
||||
window_set_name(w, args_get(args, 'n'));
|
||||
window_set_name(w, name, 0);
|
||||
options_set_number(w->options, "automatic-rename", 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,18 +29,21 @@
|
||||
|
||||
static enum cmd_retval cmd_capture_pane_exec(struct cmd *, struct cmdq_item *);
|
||||
|
||||
static char *cmd_capture_pane_append(char *, size_t *, char *, size_t);
|
||||
static char *cmd_capture_pane_append(char *, size_t *, const char *,
|
||||
size_t);
|
||||
static char *cmd_capture_pane_pending(struct args *, struct window_pane *,
|
||||
size_t *);
|
||||
static char *cmd_capture_pane_history(struct args *, struct cmdq_item *,
|
||||
struct window_pane *, size_t *);
|
||||
static char *cmd_capture_pane_hyperlinks(struct grid *, struct screen *,
|
||||
u_int, u_int *, u_int *, size_t *);
|
||||
|
||||
const struct cmd_entry cmd_capture_pane_entry = {
|
||||
.name = "capture-pane",
|
||||
.alias = "capturep",
|
||||
|
||||
.args = { "ab:CeE:JMNpPqS:Tt:", 0, 0, NULL },
|
||||
.usage = "[-aCeJMNpPqT] " CMD_BUFFER_USAGE " [-E end-line] "
|
||||
.args = { "ab:CeE:FHJLMNpPqRS:Tt:", 0, 0, NULL },
|
||||
.usage = "[-aCeFHJLMNpPqRT] " CMD_BUFFER_USAGE " [-E end-line] "
|
||||
"[-S start-line] " CMD_TARGET_PANE_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
@@ -63,7 +66,8 @@ const struct cmd_entry cmd_clear_history_entry = {
|
||||
};
|
||||
|
||||
static char *
|
||||
cmd_capture_pane_append(char *buf, size_t *len, char *line, size_t linelen)
|
||||
cmd_capture_pane_append(char *buf, size_t *len, const char *line,
|
||||
size_t linelen)
|
||||
{
|
||||
buf = xrealloc(buf, *len + linelen + 1);
|
||||
memcpy(buf + *len, line, linelen);
|
||||
@@ -71,6 +75,96 @@ cmd_capture_pane_append(char *buf, size_t *len, char *line, size_t linelen)
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static char *
|
||||
cmd_capture_pane_cell(struct screen *s, u_int xx, u_int yy)
|
||||
{
|
||||
struct grid *gd = s->grid;
|
||||
struct hyperlinks *hl = s->hyperlinks;
|
||||
struct grid_cell gc;
|
||||
char *line, *data, *link, *linkid, *f, *b, *u;
|
||||
char c[UTF8_SIZE + 1];
|
||||
const char *uri, *iid;
|
||||
u_int flags;
|
||||
|
||||
grid_get_cell(gd, xx, yy, &gc);
|
||||
|
||||
memcpy(c, gc.data.data, gc.data.size);
|
||||
c[gc.data.size] = '\0';
|
||||
utf8_stravis(&data, c, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL);
|
||||
|
||||
if (gc.link != 0 && hyperlinks_get(hl, gc.link, &uri, &iid, NULL)) {
|
||||
xasprintf(&link, "%s", uri);
|
||||
if (iid != NULL && *iid != '\0')
|
||||
xasprintf(&linkid, "%s", iid);
|
||||
else
|
||||
xasprintf(&linkid, "NONE");
|
||||
} else {
|
||||
xasprintf(&link, "NONE");
|
||||
xasprintf(&linkid, "NONE");
|
||||
}
|
||||
|
||||
flags = gc.flags;
|
||||
if (gc.fg & COLOUR_FLAG_256)
|
||||
flags |= GRID_FLAG_FG256;
|
||||
if (gc.bg & COLOUR_FLAG_256)
|
||||
flags |= GRID_FLAG_BG256;
|
||||
|
||||
xasprintf(&f, "%s[%x]", colour_tostring(gc.fg), gc.fg);
|
||||
xasprintf(&b, "%s[%x]", colour_tostring(gc.bg), gc.bg);
|
||||
xasprintf(&u, "%s[%x]", colour_tostring(gc.us), gc.us);
|
||||
|
||||
xasprintf(&line, "\t\tC %u,%u data=(%u,%u,%s) flags=%s[%x] "
|
||||
"attr=%s[%x] fg=%s bg=%s us=%s link=%s linkid=%s\n",
|
||||
yy, xx, gc.data.width, gc.data.size, data,
|
||||
grid_cell_flags_string(flags), flags,
|
||||
grid_cell_attr_string(gc.attr), gc.attr, f, b, u, link, linkid);
|
||||
|
||||
free(f);
|
||||
free(b);
|
||||
free(u);
|
||||
free(link);
|
||||
free(linkid);
|
||||
free(data);
|
||||
return (line);
|
||||
}
|
||||
|
||||
static char *
|
||||
cmd_capture_pane_grid(struct window_pane *wp, size_t *len)
|
||||
{
|
||||
struct screen *s = &wp->base;
|
||||
struct grid *gd = s->grid;
|
||||
struct grid_line *gl;
|
||||
char *buf = xstrdup(""), *line;
|
||||
char p[11];
|
||||
u_int yy, xx, total = gd->hsize + gd->sy;
|
||||
|
||||
xasprintf(&line, "G %ux%u (%u/%u)\n", gd->sx, gd->sy, gd->hsize,
|
||||
gd->hlimit);
|
||||
buf = cmd_capture_pane_append(buf, len, line, strlen(line));
|
||||
free(line);
|
||||
|
||||
for (yy = 0; yy < total; yy++) {
|
||||
gl = grid_get_line(gd, yy);
|
||||
if (yy < gd->hsize)
|
||||
snprintf(p, sizeof p, "-");
|
||||
else
|
||||
snprintf(p, sizeof p, "%u", yy - gd->hsize);
|
||||
xasprintf(&line, "\tL %u (%s) flags=%s[%x] %u/%u\n", yy,
|
||||
p, grid_line_flags_string(gl->flags), gl->flags,
|
||||
gl->cellused, gl->cellsize);
|
||||
buf = cmd_capture_pane_append(buf, len, line, strlen(line));
|
||||
free(line);
|
||||
|
||||
for (xx = 0; xx < gd->sx; xx++) {
|
||||
line = cmd_capture_pane_cell(s, xx, yy);
|
||||
buf = cmd_capture_pane_append(buf, len, line,
|
||||
strlen(line));
|
||||
free(line);
|
||||
}
|
||||
}
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static char *
|
||||
cmd_capture_pane_pending(struct args *args, struct window_pane *wp,
|
||||
size_t *len)
|
||||
@@ -103,6 +197,46 @@ cmd_capture_pane_pending(struct args *args, struct window_pane *wp,
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static char *
|
||||
cmd_capture_pane_hyperlinks(struct grid *gd, struct screen *s, u_int py,
|
||||
u_int *links, u_int *nlinks, size_t *len)
|
||||
{
|
||||
const struct grid_line *gl = grid_peek_line(gd, py);
|
||||
struct grid_cell gc;
|
||||
const char *uri;
|
||||
char *line = xstrdup("");
|
||||
u_int i, j;
|
||||
|
||||
*len = 0;
|
||||
|
||||
if (s->hyperlinks == NULL || (~gl->flags & GRID_LINE_HYPERLINK))
|
||||
return (line);
|
||||
|
||||
for (i = 0; i < gl->cellused; i++) {
|
||||
grid_get_cell(gd, i, py, &gc);
|
||||
if (gc.link == 0)
|
||||
continue;
|
||||
for (j = 0; j < *nlinks; j++) {
|
||||
if (links[j] == gc.link)
|
||||
break;
|
||||
}
|
||||
if (j != *nlinks)
|
||||
continue;
|
||||
|
||||
if (!hyperlinks_get(s->hyperlinks, gc.link, &uri, NULL, NULL))
|
||||
continue;
|
||||
|
||||
if (*nlinks == gd->sx)
|
||||
break;
|
||||
links[(*nlinks)++] = gc.link;
|
||||
|
||||
if (*len != 0)
|
||||
line = cmd_capture_pane_append(line, len, " ", 1);
|
||||
line = cmd_capture_pane_append(line, len, uri, strlen(uri));
|
||||
}
|
||||
return (line);
|
||||
}
|
||||
|
||||
static char *
|
||||
cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
|
||||
struct window_pane *wp, size_t *len)
|
||||
@@ -112,9 +246,11 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
|
||||
struct screen *s;
|
||||
struct grid_cell *gc = NULL;
|
||||
struct window_mode_entry *wme;
|
||||
int n, join_lines, flags = 0;
|
||||
int n, join_lines, number_lines, flags = 0;
|
||||
int show_flags, hyperlinks;
|
||||
u_int *links = NULL, nlinks = 0;
|
||||
u_int i, sx, top, bottom, tmp;
|
||||
char *cause, *buf, *line;
|
||||
char *cause, *buf = NULL, *line, b[64], *cp;
|
||||
const char *Sflag, *Eflag;
|
||||
size_t linelen;
|
||||
|
||||
@@ -152,7 +288,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
|
||||
if (cause != NULL) {
|
||||
top = gd->hsize;
|
||||
free(cause);
|
||||
} else if (n < 0 && (u_int) -n > gd->hsize)
|
||||
} else if (n < 0 && (u_int)-n > gd->hsize)
|
||||
top = 0;
|
||||
else
|
||||
top = gd->hsize + n;
|
||||
@@ -169,7 +305,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
|
||||
if (cause != NULL) {
|
||||
bottom = gd->hsize + gd->sy - 1;
|
||||
free(cause);
|
||||
} else if (n < 0 && (u_int) -n > gd->hsize)
|
||||
} else if (n < 0 && (u_int)-n > gd->hsize)
|
||||
bottom = 0;
|
||||
else
|
||||
bottom = gd->hsize + n;
|
||||
@@ -192,12 +328,57 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
|
||||
flags |= GRID_STRING_EMPTY_CELLS;
|
||||
if (!join_lines && !args_has(args, 'N'))
|
||||
flags |= GRID_STRING_TRIM_SPACES;
|
||||
number_lines = args_has(args, 'L');
|
||||
show_flags = args_has(args, 'F');
|
||||
hyperlinks = args_has(args, 'H');
|
||||
if (hyperlinks)
|
||||
links = xreallocarray(NULL, gd->sx, sizeof *links);
|
||||
|
||||
buf = NULL;
|
||||
for (i = top; i <= bottom; i++) {
|
||||
line = grid_string_cells(gd, 0, i, sx, &gc, flags, s);
|
||||
linelen = strlen(line);
|
||||
if (hyperlinks) {
|
||||
line = cmd_capture_pane_hyperlinks(gd, s, i, links,
|
||||
&nlinks, &linelen);
|
||||
} else {
|
||||
line = grid_string_cells(gd, 0, i, sx, &gc, flags, s);
|
||||
linelen = strlen(line);
|
||||
}
|
||||
if (hyperlinks && linelen == 0) {
|
||||
free(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (number_lines) {
|
||||
if (i >= gd->hsize)
|
||||
n = i - gd->hsize;
|
||||
else
|
||||
n = (int)i - (int)gd->hsize;
|
||||
n = snprintf(b, sizeof b, "%d ", n);
|
||||
if (n >= 0)
|
||||
buf = cmd_capture_pane_append(buf, len, b, n);
|
||||
}
|
||||
if (show_flags) {
|
||||
cp = b;
|
||||
*cp = '\0';
|
||||
|
||||
gl = grid_peek_line(gd, i);
|
||||
if (gl->flags & GRID_LINE_DEAD)
|
||||
*cp++ = 'D';
|
||||
if (gl->flags & GRID_LINE_HYPERLINK)
|
||||
*cp++ = 'H';
|
||||
if (gl->flags & GRID_LINE_START_OUTPUT)
|
||||
*cp++ = 'O';
|
||||
if (gl->flags & GRID_LINE_START_PROMPT)
|
||||
*cp++ = 'P';
|
||||
if (gl->flags & GRID_LINE_WRAPPED)
|
||||
*cp++ = 'W';
|
||||
if (gl->flags & GRID_LINE_EXTENDED)
|
||||
*cp++ = 'X';
|
||||
if (b == cp)
|
||||
*cp++ = '-';
|
||||
*cp++ = ' ';
|
||||
*cp = '\0';
|
||||
buf = cmd_capture_pane_append(buf, len, b, strlen (b));
|
||||
}
|
||||
buf = cmd_capture_pane_append(buf, len, line, linelen);
|
||||
|
||||
gl = grid_peek_line(gd, i);
|
||||
@@ -206,6 +387,9 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
|
||||
|
||||
free(line);
|
||||
}
|
||||
free(links);
|
||||
if (buf == NULL)
|
||||
buf = xstrdup("");
|
||||
return (buf);
|
||||
}
|
||||
|
||||
@@ -224,11 +408,14 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
grid_clear_history(wp->base.grid);
|
||||
if (args_has(args, 'H'))
|
||||
screen_reset_hyperlinks(wp->screen);
|
||||
server_redraw_window(wp->window);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
len = 0;
|
||||
if (args_has(args, 'P'))
|
||||
if (args_has(args, 'R'))
|
||||
buf = cmd_capture_pane_grid(wp, &len);
|
||||
else if (args_has(args, 'P') && !args_has(args, 'H'))
|
||||
buf = cmd_capture_pane_pending(args, wp, &len);
|
||||
else
|
||||
buf = cmd_capture_pane_history(args, item, wp, &len);
|
||||
|
||||
@@ -33,8 +33,8 @@ const struct cmd_entry cmd_choose_tree_entry = {
|
||||
.name = "choose-tree",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "F:f:GK:NO:rst:wyZ", 0, 1, cmd_choose_tree_args_parse },
|
||||
.usage = "[-GNrswZ] [-F format] [-f filter] [-K key-format] "
|
||||
.args = { "F:f:GhK:kNO:rst:wyZ", 0, 1, cmd_choose_tree_args_parse },
|
||||
.usage = "[-GhkNrswZ] [-F format] [-f filter] [-K key-format] "
|
||||
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
@@ -47,8 +47,8 @@ const struct cmd_entry cmd_choose_client_entry = {
|
||||
.name = "choose-client",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "F:f:K:NO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
|
||||
.usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
|
||||
.args = { "F:f:hiK:kNO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
|
||||
.usage = "[-hikNrZ] [-F format] [-f filter] [-K key-format] "
|
||||
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
@@ -61,8 +61,8 @@ const struct cmd_entry cmd_choose_buffer_entry = {
|
||||
.name = "choose-buffer",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "F:f:K:NO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
|
||||
.usage = "[-NrZ] [-F format] [-f filter] [-K key-format] "
|
||||
.args = { "F:f:K:kNO:rt:yZ", 0, 1, cmd_choose_tree_args_parse },
|
||||
.usage = "[-kNrZ] [-F format] [-f filter] [-K key-format] "
|
||||
"[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
@@ -75,8 +75,21 @@ const struct cmd_entry cmd_customize_mode_entry = {
|
||||
.name = "customize-mode",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "F:f:Nt:yZ", 0, 0, NULL },
|
||||
.usage = "[-NZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE,
|
||||
.args = { "F:f:kNt:yZ", 0, 0, NULL },
|
||||
.usage = "[-kNZ] [-F format] [-f filter] " CMD_TARGET_PANE_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
.flags = 0,
|
||||
.exec = cmd_choose_tree_exec
|
||||
};
|
||||
|
||||
const struct cmd_entry cmd_switch_mode_entry = {
|
||||
.name = "switch-mode",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "F:kst:wZ", 0, 1, cmd_choose_tree_args_parse },
|
||||
.usage = "[-kswZ] [-F format] " CMD_TARGET_PANE_USAGE " [command]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
@@ -116,6 +129,8 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
|
||||
mode = &window_client_mode;
|
||||
} else if (cmd_get_entry(self) == &cmd_customize_mode_entry)
|
||||
mode = &window_customize_mode;
|
||||
else if (cmd_get_entry(self) == &cmd_switch_mode_entry)
|
||||
mode = &window_switch_mode;
|
||||
else
|
||||
mode = &window_tree_mode;
|
||||
|
||||
|
||||
@@ -34,16 +34,16 @@ static enum args_parse_type cmd_command_prompt_args_parse(struct args *,
|
||||
static enum cmd_retval cmd_command_prompt_exec(struct cmd *,
|
||||
struct cmdq_item *);
|
||||
|
||||
static int cmd_command_prompt_callback(struct client *, void *,
|
||||
const char *, int);
|
||||
static enum prompt_result cmd_command_prompt_callback(struct client *, void *,
|
||||
const char *, enum prompt_key_result);
|
||||
static void cmd_command_prompt_free(void *);
|
||||
|
||||
const struct cmd_entry cmd_command_prompt_entry = {
|
||||
.name = "command-prompt",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "1beFiklI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse },
|
||||
.usage = "[-1beFiklN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
|
||||
.args = { "1CbeFiklI:NPp:t:T:", 0, 1, cmd_command_prompt_args_parse },
|
||||
.usage = "[-1CbeFiklNP] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
|
||||
" [-T prompt-type] [template]",
|
||||
|
||||
.flags = CMD_CLIENT_TFLAG,
|
||||
@@ -62,6 +62,8 @@ struct cmd_command_prompt_cdata {
|
||||
int flags;
|
||||
enum prompt_type prompt_type;
|
||||
|
||||
struct window_pane *wp;
|
||||
|
||||
struct cmd_command_prompt_prompt *prompts;
|
||||
u_int count;
|
||||
u_int current;
|
||||
@@ -87,10 +89,15 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct cmd_command_prompt_cdata *cdata;
|
||||
char *tmp, *prompts, *prompt, *next_prompt;
|
||||
char *inputs = NULL, *next_input;
|
||||
struct window_pane *wp = target->wp;
|
||||
u_int count = args_count(args);
|
||||
int wait = !args_has(args, 'b'), space = 1;
|
||||
int pane = args_has(args, 'P');
|
||||
|
||||
if (tc->prompt_string != NULL)
|
||||
if (pane) {
|
||||
if (wp == NULL || window_pane_has_prompt(wp))
|
||||
return (CMD_RETURN_NORMAL);
|
||||
} else if (tc->prompt != NULL)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
if (args_has(args, 'i'))
|
||||
wait = 0;
|
||||
@@ -98,6 +105,8 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
|
||||
cdata = xcalloc(1, sizeof *cdata);
|
||||
if (wait)
|
||||
cdata->item = item;
|
||||
if (pane)
|
||||
cdata->wp = wp;
|
||||
cdata->state = args_make_commands_prepare(self, item, 0, "%1", wait,
|
||||
args_has(args, 'F'));
|
||||
|
||||
@@ -146,7 +155,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
|
||||
if ((type = args_get(args, 'T')) != NULL) {
|
||||
cdata->prompt_type = status_prompt_type(type);
|
||||
cdata->prompt_type = prompt_type(type);
|
||||
if (cdata->prompt_type == PROMPT_TYPE_INVALID) {
|
||||
cmdq_error(item, "unknown type: %s", type);
|
||||
cmd_command_prompt_free(cdata);
|
||||
@@ -165,18 +174,29 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
|
||||
cdata->flags |= PROMPT_KEY;
|
||||
else if (args_has(args, 'e'))
|
||||
cdata->flags |= PROMPT_BSPACE_EXIT;
|
||||
status_prompt_set(tc, target, cdata->prompts[0].prompt,
|
||||
cdata->prompts[0].input, cmd_command_prompt_callback,
|
||||
cmd_command_prompt_free, cdata, cdata->flags, cdata->prompt_type);
|
||||
if (args_has(args, 'C'))
|
||||
cdata->flags |= PROMPT_NOFREEZE;
|
||||
if (pane) {
|
||||
cdata->flags |= PROMPT_ISPANE;
|
||||
window_pane_set_prompt(wp, tc, target, cdata->prompts[0].prompt,
|
||||
cdata->prompts[0].input, cmd_command_prompt_callback,
|
||||
cmd_command_prompt_free, cdata, cdata->flags,
|
||||
cdata->prompt_type);
|
||||
} else {
|
||||
status_prompt_set(tc, target, cdata->prompts[0].prompt,
|
||||
cdata->prompts[0].input, cmd_command_prompt_callback,
|
||||
cmd_command_prompt_free, cdata, cdata->flags,
|
||||
cdata->prompt_type);
|
||||
}
|
||||
|
||||
if (!wait)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
return (CMD_RETURN_WAIT);
|
||||
}
|
||||
|
||||
static int
|
||||
static enum prompt_result
|
||||
cmd_command_prompt_callback(struct client *c, void *data, const char *s,
|
||||
int done)
|
||||
enum prompt_key_result key)
|
||||
{
|
||||
struct cmd_command_prompt_cdata *cdata = data;
|
||||
char *error;
|
||||
@@ -186,26 +206,30 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
|
||||
int argc = 0;
|
||||
char **argv = NULL;
|
||||
|
||||
if (s == NULL)
|
||||
if (s == NULL || key == PROMPT_KEY_MOVE)
|
||||
goto out;
|
||||
|
||||
if (done) {
|
||||
if (key == PROMPT_KEY_CLOSE) {
|
||||
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];
|
||||
status_prompt_update(c, prompt->prompt, prompt->input);
|
||||
return (1);
|
||||
if (cdata->wp != NULL) {
|
||||
window_pane_update_prompt(cdata->wp,
|
||||
prompt->prompt, prompt->input);
|
||||
} else
|
||||
status_prompt_update(c, prompt->prompt,
|
||||
prompt->input);
|
||||
return (PROMPT_CONTINUE);
|
||||
}
|
||||
}
|
||||
|
||||
argc = cdata->argc;
|
||||
argv = cmd_copy_argv(cdata->argc, cdata->argv);
|
||||
if (!done)
|
||||
if (key != PROMPT_KEY_CLOSE)
|
||||
cmd_append_argv(&argc, &argv, s);
|
||||
|
||||
if (done) {
|
||||
else {
|
||||
cmd_free_argv(cdata->argc, cdata->argv);
|
||||
cdata->argc = argc;
|
||||
cdata->argv = cmd_copy_argv(argc, argv);
|
||||
@@ -224,13 +248,20 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
|
||||
}
|
||||
cmd_free_argv(argc, argv);
|
||||
|
||||
if (c->prompt_inputcb != cmd_command_prompt_callback)
|
||||
return (1);
|
||||
/*
|
||||
* An incremental prompt fires its callback on every edit but must stay
|
||||
* open for further typing; only an explicit close (handled above) ends
|
||||
* it.
|
||||
*/
|
||||
if (cdata->flags & PROMPT_INCREMENTAL)
|
||||
return (PROMPT_CONTINUE);
|
||||
|
||||
out:
|
||||
if (item != NULL)
|
||||
if (item != NULL) {
|
||||
cdata->item = NULL;
|
||||
cmdq_continue(item);
|
||||
return (0);
|
||||
}
|
||||
return (PROMPT_CLOSE);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -239,6 +270,11 @@ cmd_command_prompt_free(void *data)
|
||||
struct cmd_command_prompt_cdata *cdata = data;
|
||||
u_int i;
|
||||
|
||||
if (cdata->item != NULL) {
|
||||
cmdq_continue(cdata->item);
|
||||
cdata->item = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < cdata->count; i++) {
|
||||
free(cdata->prompts[i].prompt);
|
||||
free(cdata->prompts[i].input);
|
||||
|
||||
@@ -33,8 +33,8 @@ static enum args_parse_type cmd_confirm_before_args_parse(struct args *,
|
||||
static enum cmd_retval cmd_confirm_before_exec(struct cmd *,
|
||||
struct cmdq_item *);
|
||||
|
||||
static int cmd_confirm_before_callback(struct client *, void *,
|
||||
const char *, int);
|
||||
static enum prompt_result cmd_confirm_before_callback(struct client *, void *,
|
||||
const char *, enum prompt_key_result);
|
||||
static void cmd_confirm_before_free(void *);
|
||||
|
||||
const struct cmd_entry cmd_confirm_before_entry = {
|
||||
@@ -118,9 +118,9 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
|
||||
return (CMD_RETURN_WAIT);
|
||||
}
|
||||
|
||||
static int
|
||||
static enum prompt_result
|
||||
cmd_confirm_before_callback(struct client *c, void *data, const char *s,
|
||||
__unused int done)
|
||||
__unused enum prompt_key_result key)
|
||||
{
|
||||
struct cmd_confirm_before_data *cdata = data;
|
||||
struct cmdq_item *item = cdata->item, *new_item;
|
||||
@@ -151,7 +151,7 @@ out:
|
||||
cmdq_get_client(item)->retval = retcode;
|
||||
cmdq_continue(item);
|
||||
}
|
||||
return (0);
|
||||
return (PROMPT_CLOSE);
|
||||
}
|
||||
|
||||
static void
|
||||
|
||||
@@ -63,6 +63,8 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct client *c = cmdq_get_client(item);
|
||||
struct session *s;
|
||||
struct window_pane *wp = target->wp, *swp;
|
||||
u_int tty_ox, tty_oy, tty_sx, tty_sy;
|
||||
int line_numbers;
|
||||
|
||||
if (args_has(args, 'q')) {
|
||||
window_pane_reset_mode_all(wp);
|
||||
@@ -85,17 +87,23 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
|
||||
swp = source->wp;
|
||||
else
|
||||
swp = wp;
|
||||
line_numbers = 1;
|
||||
if (event != NULL && KEYC_IS_MOUSE(event->key))
|
||||
line_numbers = 0;
|
||||
if (!window_pane_set_mode(wp, swp, &window_copy_mode, NULL, args)) {
|
||||
window_copy_set_line_numbers(wp, line_numbers);
|
||||
if (args_has(args, 'M'))
|
||||
window_copy_start_drag(c, &event->m);
|
||||
}
|
||||
} else
|
||||
window_copy_set_line_numbers(wp, line_numbers);
|
||||
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')) {
|
||||
tty_window_offset(&c->tty, &tty_ox, &tty_oy, &tty_sx, &tty_sy);
|
||||
window_copy_scroll(wp, c->tty.mouse_slider_mpos, event->m.y,
|
||||
args_has(args, 'e'));
|
||||
tty_oy, args_has(args, 'e'));
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
struct args *args = cmd_get_args(self);
|
||||
struct cmd_find_state *source = cmdq_get_source(item);
|
||||
struct client *c = cmdq_get_client(item);
|
||||
struct client *tc = cmdq_get_target_client(item), *loop;
|
||||
struct session *s;
|
||||
enum msgtype msgtype;
|
||||
@@ -69,6 +70,13 @@ cmd_detach_client_exec(struct cmd *self, struct cmdq_item *item)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
if (c->flags & CLIENT_READONLY) {
|
||||
if (args_has(args, 's') || args_has(args, 'a') || c != tc) {
|
||||
cmdq_error(item, "client is read-only");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
if (args_has(args, 'P'))
|
||||
msgtype = MSG_DETACHKILL;
|
||||
else
|
||||
|
||||
@@ -370,7 +370,7 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
|
||||
flags |= MENU_NOMOUSE;
|
||||
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);
|
||||
goto out;
|
||||
return (CMD_RETURN_WAIT);
|
||||
|
||||
out:
|
||||
|
||||
@@ -48,6 +48,16 @@ struct cmd_display_panes_data {
|
||||
struct args_command_state *state;
|
||||
};
|
||||
|
||||
struct cmd_display_panes_ctx {
|
||||
struct client *c;
|
||||
int ox;
|
||||
int oy;
|
||||
u_int sx;
|
||||
u_int sy;
|
||||
u_int statuslines;
|
||||
int statustop;
|
||||
};
|
||||
|
||||
static enum args_parse_type
|
||||
cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx,
|
||||
__unused char **cause)
|
||||
@@ -56,7 +66,63 @@ cmd_display_panes_args_parse(__unused struct args *args, __unused u_int idx,
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
|
||||
cmd_display_panes_put(struct cmd_display_panes_ctx *ctx,
|
||||
struct window_pane *wp, u_int cx, u_int cy, const char *buf, size_t len)
|
||||
{
|
||||
struct client *c = ctx->c;
|
||||
struct tty *tty = &c->tty;
|
||||
struct visible_ranges *r;
|
||||
struct visible_range *ri;
|
||||
u_int i, j;
|
||||
|
||||
r = window_visible_ranges(wp, ctx->ox + cx, ctx->oy + cy, len, NULL);
|
||||
for (i = 0; i < r->used; i++) {
|
||||
ri = &r->ranges[i];
|
||||
for (j = ri->px; j < ri->px + ri->nx; j++) {
|
||||
tty_cursor(tty, j - ctx->ox, cy);
|
||||
tty_putn(tty, &buf[j - ri->px], 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_display_panes_draw_format(struct cmd_display_panes_ctx *ctx,
|
||||
struct window_pane *wp, u_int xoff, u_int yoff, u_int sx,
|
||||
const struct grid_cell *gc)
|
||||
{
|
||||
struct client *c = ctx->c;
|
||||
struct tty *tty = &c->tty;
|
||||
struct session *s = c->session;
|
||||
struct screen *sc = wp->screen, screen;
|
||||
struct screen_write_ctx sctx;
|
||||
struct visible_ranges *r;
|
||||
struct visible_range *ri;
|
||||
const char *format;
|
||||
char *expanded;
|
||||
u_int i, px = ctx->ox + xoff;
|
||||
|
||||
format = options_get_string(s->options, "display-panes-format");
|
||||
expanded = format_single(NULL, format, c, s, s->curw, wp);
|
||||
|
||||
screen_init(&screen, sx, 1, 0);
|
||||
screen_write_start(&sctx, &screen);
|
||||
screen_write_fast_copy(&sctx, sc, 0, sc->grid->hsize, sx, 1);
|
||||
screen_write_cursormove(&sctx, 0, 0, 0);
|
||||
format_draw(&sctx, gc, sx, expanded, NULL, 0);
|
||||
screen_write_stop(&sctx);
|
||||
free(expanded);
|
||||
|
||||
r = window_visible_ranges(wp, px, wp->yoff, sx, NULL);
|
||||
for (i = 0; i < r->used; i++) {
|
||||
ri = &r->ranges[i];
|
||||
tty_draw_line(tty, &screen, ri->px - px, 0, ri->nx,
|
||||
ri->px - ctx->ox, yoff, NULL);
|
||||
}
|
||||
screen_free(&screen);
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_display_panes_draw_pane(struct cmd_display_panes_ctx *ctx,
|
||||
struct window_pane *wp)
|
||||
{
|
||||
struct client *c = ctx->c;
|
||||
@@ -66,14 +132,16 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
|
||||
struct window *w = wp->window;
|
||||
struct grid_cell fgc, bgc;
|
||||
u_int pane, idx, px, py, i, j, xoff, yoff, sx, sy;
|
||||
int colour, active_colour;
|
||||
char buf[16], lbuf[16], rbuf[16], *ptr;
|
||||
size_t len, llen, rlen;
|
||||
u_int cx, cy;
|
||||
const char *name;
|
||||
struct format_tree *ft;
|
||||
char buf[16], lbuf[16], *ptr;
|
||||
size_t len, llen;
|
||||
|
||||
if (wp->xoff + wp->sx <= ctx->ox ||
|
||||
wp->xoff >= ctx->ox + ctx->sx ||
|
||||
wp->yoff + wp->sy <= ctx->oy ||
|
||||
wp->yoff >= ctx->oy + ctx->sy)
|
||||
if (wp->xoff + (int)wp->sx <= ctx->ox ||
|
||||
wp->xoff >= ctx->ox + (int)ctx->sx ||
|
||||
wp->yoff + (int)wp->sy <= ctx->oy ||
|
||||
wp->yoff >= ctx->oy + (int)ctx->sy)
|
||||
return;
|
||||
|
||||
if (wp->xoff >= ctx->ox && wp->xoff + wp->sx <= ctx->ox + ctx->sx) {
|
||||
@@ -124,36 +192,37 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
|
||||
|
||||
if (sx < len)
|
||||
return;
|
||||
colour = options_get_number(oo, "display-panes-colour");
|
||||
active_colour = options_get_number(oo, "display-panes-active-colour");
|
||||
if (w->active == wp)
|
||||
name = "display-panes-active-colour";
|
||||
else
|
||||
name = "display-panes-colour";
|
||||
ft = format_create_defaults(NULL, c, s, NULL, wp);
|
||||
style_apply(&fgc, oo, name, ft);
|
||||
format_free(ft);
|
||||
|
||||
memcpy(&fgc, &grid_default_cell, sizeof fgc);
|
||||
memcpy(&bgc, &grid_default_cell, sizeof bgc);
|
||||
if (w->active == wp) {
|
||||
fgc.fg = active_colour;
|
||||
bgc.bg = active_colour;
|
||||
} else {
|
||||
fgc.fg = colour;
|
||||
bgc.bg = colour;
|
||||
}
|
||||
bgc.bg = fgc.fg;
|
||||
|
||||
rlen = xsnprintf(rbuf, sizeof rbuf, "%ux%u", wp->sx, wp->sy);
|
||||
if (pane > 9 && pane < 35)
|
||||
llen = xsnprintf(lbuf, sizeof lbuf, "%c", 'a' + (pane - 10));
|
||||
else
|
||||
llen = 0;
|
||||
|
||||
if (sx < len * 6 || sy < 5) {
|
||||
tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
|
||||
tty_attributes(tty, &fgc, NULL);
|
||||
if (sx >= len + llen + 1) {
|
||||
len += llen + 1;
|
||||
tty_cursor(tty, xoff + px - len / 2, yoff + py);
|
||||
tty_putn(tty, buf, len, len);
|
||||
tty_putn(tty, " ", 1, 1);
|
||||
tty_putn(tty, lbuf, llen, llen);
|
||||
cx = xoff + px - len / 2;
|
||||
cy = yoff + py;
|
||||
cmd_display_panes_put(ctx, wp, cx, cy, buf, len);
|
||||
cx += len;
|
||||
cmd_display_panes_put(ctx, wp, cx, cy, " ", 1);
|
||||
cx++;
|
||||
cmd_display_panes_put(ctx, wp, cx, cy, lbuf, llen);
|
||||
} else {
|
||||
tty_cursor(tty, xoff + px - len / 2, yoff + py);
|
||||
tty_putn(tty, buf, len, len);
|
||||
cx = xoff + px - len / 2;
|
||||
cy = yoff + py;
|
||||
cmd_display_panes_put(ctx, wp, cx, cy, buf, len);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
@@ -161,7 +230,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
|
||||
px -= len * 3;
|
||||
py -= 2;
|
||||
|
||||
tty_attributes(tty, &bgc, &grid_default_cell, NULL, NULL);
|
||||
tty_attributes(tty, &bgc, NULL);
|
||||
for (ptr = buf; *ptr != '\0'; ptr++) {
|
||||
if (*ptr < '0' || *ptr > '9')
|
||||
continue;
|
||||
@@ -169,9 +238,11 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
|
||||
|
||||
for (j = 0; j < 5; j++) {
|
||||
for (i = px; i < px + 5; i++) {
|
||||
tty_cursor(tty, xoff + i, yoff + py + j);
|
||||
if (window_clock_table[idx][j][i - px])
|
||||
tty_putc(tty, ' ');
|
||||
if (!window_clock_table[idx][j][i - px])
|
||||
continue;
|
||||
cx = xoff + i;
|
||||
cy = yoff + py + j;
|
||||
cmd_display_panes_put(ctx, wp, cx, cy, " ", 1);
|
||||
}
|
||||
}
|
||||
px += 6;
|
||||
@@ -179,15 +250,12 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
|
||||
|
||||
if (sy <= 6)
|
||||
goto out;
|
||||
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);
|
||||
}
|
||||
cmd_display_panes_draw_format(ctx, wp, xoff, yoff, sx, &fgc);
|
||||
if (llen != 0) {
|
||||
tty_cursor(tty, xoff + sx / 2 + len * 3 - llen - 1,
|
||||
yoff + py + 5);
|
||||
tty_putn(tty, lbuf, llen, llen);
|
||||
tty_attributes(tty, &fgc, NULL);
|
||||
cx = xoff + sx / 2 + len * 3 - llen - 1;
|
||||
cy = yoff + py + 5;
|
||||
cmd_display_panes_put(ctx, wp, cx, cy, lbuf, llen);
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -195,17 +263,30 @@ out:
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_display_panes_draw(struct client *c, __unused void *data,
|
||||
struct screen_redraw_ctx *ctx)
|
||||
cmd_display_panes_draw(struct client *c, __unused void *data)
|
||||
{
|
||||
struct window *w = c->session->curw->window;
|
||||
struct window_pane *wp;
|
||||
struct session *s = c->session;
|
||||
struct window *w = s->curw->window;
|
||||
struct window_pane *wp;
|
||||
struct cmd_display_panes_ctx ctx;
|
||||
u_int lines;
|
||||
|
||||
log_debug("%s: %s @%u", __func__, c->name, w->id);
|
||||
|
||||
memset(&ctx, 0, sizeof ctx);
|
||||
ctx.c = c;
|
||||
tty_window_offset(&c->tty, &ctx.ox, &ctx.oy, &ctx.sx, &ctx.sy);
|
||||
if (options_get_number(s->options, "status-position") == 0) {
|
||||
lines = status_line_size(c);
|
||||
if (c->message_string != NULL || c->prompt != NULL)
|
||||
lines = (lines == 0 ? 1 : lines);
|
||||
ctx.statuslines = lines;
|
||||
ctx.statustop = 1;
|
||||
}
|
||||
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
if (window_pane_visible(wp))
|
||||
cmd_display_panes_draw_pane(ctx, wp);
|
||||
if (window_pane_is_visible(wp))
|
||||
cmd_display_panes_draw_pane(&ctx, wp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
27
cmd-find.c
27
cmd-find.c
@@ -147,6 +147,18 @@ cmd_find_session_better(struct session *s, struct session *than, int flags)
|
||||
return (timercmp(&s->activity_time, &than->activity_time, >));
|
||||
}
|
||||
|
||||
/* Can this session be usefully targeted? */
|
||||
static int
|
||||
cmd_find_session_valid(struct session *s)
|
||||
{
|
||||
if (!session_alive(s) ||
|
||||
s->curw == NULL ||
|
||||
s->curw->window == NULL ||
|
||||
s->curw->window->active == NULL)
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Find best session from a list, or all if list is NULL. */
|
||||
static struct session *
|
||||
cmd_find_best_session(struct session **slist, u_int ssize, int flags)
|
||||
@@ -159,11 +171,15 @@ cmd_find_best_session(struct session **slist, u_int ssize, int flags)
|
||||
s = NULL;
|
||||
if (slist != NULL) {
|
||||
for (i = 0; i < ssize; i++) {
|
||||
if (!cmd_find_session_valid(slist[i]))
|
||||
continue;
|
||||
if (cmd_find_session_better(slist[i], s, flags))
|
||||
s = slist[i];
|
||||
}
|
||||
} else {
|
||||
RB_FOREACH(s_loop, sessions, &sessions) {
|
||||
if (!cmd_find_session_valid(s_loop))
|
||||
continue;
|
||||
if (cmd_find_session_better(s_loop, s, flags))
|
||||
s = s_loop;
|
||||
}
|
||||
@@ -978,15 +994,20 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
|
||||
} else if (cmd_find_from_client(¤t, cmdq_get_client(item),
|
||||
flags) == 0) {
|
||||
fs->current = ¤t;
|
||||
/* No active pane, window empty, return the window instead. */
|
||||
if (current.wp == NULL) {
|
||||
type = CMD_FIND_WINDOW;
|
||||
}
|
||||
log_debug("%s: current is from client", __func__);
|
||||
} else {
|
||||
if (~flags & CMD_FIND_QUIET)
|
||||
cmdq_error(item, "no current target");
|
||||
goto error;
|
||||
}
|
||||
/*
|
||||
if (!cmd_find_valid_state(fs->current))
|
||||
fatalx("invalid current find state");
|
||||
|
||||
*/
|
||||
/* An empty or NULL target is the current. */
|
||||
if (target == NULL || *target == '\0')
|
||||
goto current;
|
||||
@@ -995,7 +1016,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
|
||||
strcmp(target, "{active}") == 0 ||
|
||||
strcmp(target, "{current}") == 0) {
|
||||
c = cmdq_get_client(item);
|
||||
if (c == NULL) {
|
||||
if (c == NULL || c->session == NULL) {
|
||||
cmdq_error(item, "no current client");
|
||||
goto error;
|
||||
}
|
||||
@@ -1025,7 +1046,7 @@ cmd_find_target(struct cmd_find_state *fs, struct cmdq_item *item,
|
||||
fs->w = fs->wl->window;
|
||||
fs->wp = fs->w->active;
|
||||
}
|
||||
break;
|
||||
goto found;
|
||||
}
|
||||
if (fs->wp == NULL) {
|
||||
if (~flags & CMD_FIND_QUIET)
|
||||
|
||||
213
cmd-hide-pane.c
Normal file
213
cmd-hide-pane.c
Normal file
@@ -0,0 +1,213 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Michael Grant <mgrant@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"
|
||||
|
||||
/*
|
||||
* Hide or show panes.
|
||||
*/
|
||||
|
||||
static enum cmd_retval cmd_hide_pane_exec(struct cmd *, struct cmdq_item *);
|
||||
static enum cmd_retval cmd_show_pane_exec(struct cmd *, struct cmdq_item *);
|
||||
|
||||
const struct cmd_entry cmd_hide_pane_entry = {
|
||||
.name = "hide-pane",
|
||||
.alias = "hidep",
|
||||
|
||||
.args = { "at:", 0, 1, NULL },
|
||||
.usage = "[-a] " CMD_TARGET_PANE_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
.flags = CMD_AFTERHOOK,
|
||||
.exec = cmd_hide_pane_exec
|
||||
};
|
||||
|
||||
const struct cmd_entry cmd_show_pane_entry = {
|
||||
.name = "show-pane",
|
||||
.alias = "showp",
|
||||
|
||||
.args = { "at:", 0, 1, NULL },
|
||||
.usage = "[-a] " CMD_TARGET_PANE_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
.flags = CMD_AFTERHOOK,
|
||||
.exec = cmd_show_pane_exec
|
||||
};
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_hide_pane_hide(struct window *w, struct window_pane *wp)
|
||||
{
|
||||
struct window_pane *wpp = NULL;
|
||||
struct layout_cell *lc = wp->layout_cell;
|
||||
|
||||
if (window_pane_is_hidden(wp))
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
if (wp == w->active) {
|
||||
/* Find previous active pane. */
|
||||
TAILQ_FOREACH(wpp, &w->last_panes, sentry) {
|
||||
if (wpp != wp && window_pane_is_visible(wpp))
|
||||
break;
|
||||
}
|
||||
if (wpp == NULL) {
|
||||
TAILQ_FOREACH(wpp, &w->z_index, zentry) {
|
||||
if (wpp != wp &&
|
||||
window_pane_is_visible(wpp))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lc->flags |= LAYOUT_CELL_HIDDEN;
|
||||
|
||||
layout_remove_tile(w, lc);
|
||||
layout_fix_offsets(w);
|
||||
layout_fix_panes(w, NULL);
|
||||
|
||||
window_pane_stack_remove(&w->last_panes, wp);
|
||||
if (wpp != NULL) {
|
||||
window_set_active_pane(w, wpp, 1);
|
||||
} else if (wp == w->active) {
|
||||
/* No visible previous active pane; null active pane
|
||||
* to show dots background. */
|
||||
w->active = NULL;
|
||||
if (options_get_number(global_options, "focus-events"))
|
||||
window_pane_update_focus(wp);
|
||||
notify_window("window-pane-changed", w);
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
} else {
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
}
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_hide_pane_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 winlink *wl = target->wl;
|
||||
struct window *w = wl->window;
|
||||
struct window_pane *wp, *active_pane = w->active;
|
||||
u_int id;
|
||||
char *cause = NULL;
|
||||
enum cmd_retval rv;
|
||||
|
||||
window_unzoom(w, 1);
|
||||
|
||||
if (args_has(args, 'a')) {
|
||||
TAILQ_FOREACH(wp, &w->z_index, zentry) {
|
||||
if (!window_pane_is_visible(wp) || wp == active_pane)
|
||||
continue;
|
||||
rv = cmd_hide_pane_hide(w, wp);
|
||||
if (rv != CMD_RETURN_NORMAL)
|
||||
return (rv);
|
||||
}
|
||||
return (CMD_RETURN_NORMAL);
|
||||
} else {
|
||||
wp = target->wp;
|
||||
if (wp == NULL) {
|
||||
id = args_strtonum_and_expand(args, 't', 0, INT_MAX,
|
||||
item, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(item, "%s target pane", cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
wp = window_pane_find_by_id(id);
|
||||
}
|
||||
if (wp == NULL) {
|
||||
cmdq_error(item, "No target pane to hide.");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
return (cmd_hide_pane_hide(w, wp));
|
||||
}
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_hide_pane_show(struct window *w, struct window_pane *wp)
|
||||
{
|
||||
struct layout_cell *lc = wp->layout_cell;
|
||||
|
||||
if (!window_pane_is_hidden(wp))
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
lc->flags &= ~LAYOUT_CELL_HIDDEN;
|
||||
|
||||
layout_insert_tile(w, lc);
|
||||
layout_fix_offsets(w);
|
||||
layout_fix_panes(w, NULL);
|
||||
|
||||
window_set_active_pane(w, wp, 1);
|
||||
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_show_pane_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 winlink *wl = target->wl;
|
||||
struct window *w = wl->window;
|
||||
struct window_pane *wp;
|
||||
u_int id;
|
||||
char *cause = NULL;
|
||||
enum cmd_retval rv;
|
||||
|
||||
window_unzoom(w, 1);
|
||||
|
||||
if (args_has(args, 'a')) {
|
||||
TAILQ_FOREACH(wp, &w->z_index, zentry) {
|
||||
if (!window_pane_is_visible(wp))
|
||||
continue;
|
||||
rv = cmd_hide_pane_show(w, wp);
|
||||
if (rv != CMD_RETURN_NORMAL)
|
||||
return (rv);
|
||||
}
|
||||
return (CMD_RETURN_NORMAL);
|
||||
} else {
|
||||
wp = target->wp;
|
||||
if (wp == NULL) {
|
||||
id = args_strtonum_and_expand(args, 't', 0, INT_MAX,
|
||||
item, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(item, "%s target pane", cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
wp = window_pane_find_by_id(id);
|
||||
}
|
||||
if (wp == NULL) {
|
||||
cmdq_error(item, "No target pane to show.");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
return (cmd_hide_pane_show(w, wp));
|
||||
}
|
||||
}
|
||||
437
cmd-join-pane.c
437
cmd-join-pane.c
@@ -30,6 +30,9 @@
|
||||
*/
|
||||
|
||||
static enum cmd_retval cmd_join_pane_exec(struct cmd *, struct cmdq_item *);
|
||||
static enum cmd_retval cmd_join_pane_mouse_update(struct cmdq_item *);
|
||||
static void cmd_join_pane_mouse_move(struct client *,
|
||||
struct mouse_event *);
|
||||
|
||||
const struct cmd_entry cmd_join_pane_entry = {
|
||||
.name = "join-pane",
|
||||
@@ -49,8 +52,10 @@ const struct cmd_entry cmd_move_pane_entry = {
|
||||
.name = "move-pane",
|
||||
.alias = "movep",
|
||||
|
||||
.args = { "bdfhvp:l:s:t:", 0, 0, NULL },
|
||||
.usage = "[-bdfhv] [-l size] " CMD_SRCDST_PANE_USAGE,
|
||||
.args = { "bdD::fhMvl:L::P:R::s:t:U::X:Y:z:", 0, 0, NULL },
|
||||
.usage = "[-bdfhMv] [-D lines] [-l size] [-L columns] [-P position] "
|
||||
"[-R columns] " CMD_SRCDST_PANE_USAGE " [-U lines] "
|
||||
"[-X x-position] [-Y y-position] [-z z-index]",
|
||||
|
||||
.source = { 's', CMD_FIND_PANE, CMD_FIND_DEFAULT_MARKED },
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
@@ -59,6 +64,344 @@ const struct cmd_entry cmd_move_pane_entry = {
|
||||
.exec = cmd_join_pane_exec
|
||||
};
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl,
|
||||
struct window_pane *wp, const char *position)
|
||||
{
|
||||
struct window *w = wl->window;
|
||||
struct layout_cell *lc = wp->layout_cell;
|
||||
struct window_pane *owp;
|
||||
int wx = w->sx, wy = w->sy, px = lc->sx;
|
||||
int py = lc->sy, xoff = lc->xoff, yoff = lc->yoff;
|
||||
int border = 1;
|
||||
|
||||
if (window_pane_get_pane_lines(wp) == PANE_LINES_NONE)
|
||||
border = 0;
|
||||
|
||||
if (strcmp(position, "top-left") == 0) {
|
||||
xoff = border;
|
||||
yoff = border;
|
||||
} else if (strcmp(position, "top-centre") == 0 ||
|
||||
strcmp(position, "top-center") == 0) {
|
||||
xoff = (wx - px) / 2;
|
||||
yoff = border;
|
||||
} else if (strcmp(position, "top-right") == 0) {
|
||||
xoff = wx - px - border;
|
||||
yoff = border;
|
||||
} else if (strcmp(position, "centre-left") == 0 ||
|
||||
strcmp(position, "center-left") == 0) {
|
||||
xoff = border;
|
||||
yoff = (wy - py) / 2;
|
||||
} else if (strcmp(position, "centre") == 0 ||
|
||||
strcmp(position, "center") == 0) {
|
||||
xoff = (wx - px) / 2;
|
||||
yoff = (wy - py) / 2;
|
||||
} else if (strcmp(position, "centre-right") == 0 ||
|
||||
strcmp(position, "center-right") == 0) {
|
||||
xoff = wx - px - border;
|
||||
yoff = (wy - py) / 2;
|
||||
} else if (strcmp(position, "bottom-left") == 0) {
|
||||
xoff = border;
|
||||
yoff = wy - py - border;
|
||||
} else if (strcmp(position, "bottom-centre") == 0 ||
|
||||
strcmp(position, "bottom-center") == 0) {
|
||||
xoff = (wx - px) / 2;
|
||||
yoff = wy - py - border;
|
||||
} else if (strcmp(position, "bottom-right") == 0) {
|
||||
xoff = wx - px - border;
|
||||
yoff = wy - py - border;
|
||||
} else if (strcmp(position, "top-left-centre") == 0 ||
|
||||
strcmp(position, "top-left-center") == 0) {
|
||||
xoff = wx / 4 - px / 2;
|
||||
yoff = wy / 4 - py / 2;
|
||||
} else if (strcmp(position, "top-right-centre") == 0 ||
|
||||
strcmp(position, "top-right-center") == 0) {
|
||||
xoff = (3 * wx) / 4 - px / 2;
|
||||
yoff = wy / 4 - py / 2;
|
||||
} else if (strcmp(position, "bottom-left-centre") == 0 ||
|
||||
strcmp(position, "bottom-left-center") == 0) {
|
||||
xoff = wx / 4 - px / 2;
|
||||
yoff = (3 * wy) / 4 - py / 2;
|
||||
} else if (strcmp(position, "bottom-right-centre") == 0 ||
|
||||
strcmp(position, "bottom-right-center") == 0) {
|
||||
xoff = (3 * wx) / 4 - px / 2;
|
||||
yoff = (3 * wy) / 4 - py / 2;
|
||||
} else if (strcmp(position, "front") == 0) {
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
|
||||
} else if (strcmp(position, "back") == 0) {
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_FOREACH(owp, &w->z_index, zentry) {
|
||||
if (!window_pane_is_floating(owp))
|
||||
break;
|
||||
}
|
||||
if (owp != NULL)
|
||||
TAILQ_INSERT_BEFORE(owp, wp, zentry);
|
||||
else
|
||||
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
|
||||
} else if (strcmp(position, "forward") == 0) {
|
||||
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
|
||||
if (owp != NULL) {
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_INSERT_BEFORE(owp, wp, zentry);
|
||||
}
|
||||
} else if (strcmp(position, "backward") == 0) {
|
||||
owp = TAILQ_NEXT(wp, zentry);
|
||||
if (owp != NULL && window_pane_is_floating(owp)) {
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
|
||||
}
|
||||
} else if (strcmp(position, "forward-loop") == 0) {
|
||||
owp = TAILQ_PREV(wp, window_panes_zindex, zentry);
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
if (owp != NULL)
|
||||
TAILQ_INSERT_BEFORE(owp, wp, zentry);
|
||||
else {
|
||||
TAILQ_FOREACH(owp, &w->z_index, zentry) {
|
||||
if (!window_pane_is_floating(owp))
|
||||
break;
|
||||
}
|
||||
if (owp != NULL)
|
||||
TAILQ_INSERT_BEFORE(owp, wp, zentry);
|
||||
else
|
||||
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
|
||||
}
|
||||
} else if (strcmp(position, "backward-loop") == 0) {
|
||||
owp = TAILQ_NEXT(wp, zentry);
|
||||
if (owp != NULL && window_pane_is_floating(owp)) {
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_INSERT_AFTER(&w->z_index, owp, wp, zentry);
|
||||
} else {
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_INSERT_HEAD(&w->z_index, wp, zentry);
|
||||
}
|
||||
} else {
|
||||
cmdq_error(item, "unknown position: %s", position);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (xoff != lc->xoff || yoff != lc->yoff) {
|
||||
lc->xoff = xoff;
|
||||
lc->yoff = yoff;
|
||||
layout_fix_panes(w, NULL);
|
||||
}
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_join_pane_move(struct cmdq_item *item, struct args *args,
|
||||
struct winlink *wl, struct window_pane *wp)
|
||||
{
|
||||
struct window *w = wl->window;
|
||||
struct layout_cell *lc = wp->layout_cell;
|
||||
const char *errstr, *argval;
|
||||
const char flags[] = { 'U', 'D', 'L', 'R' };
|
||||
char *cause = NULL, flag;
|
||||
int xoff = lc->xoff, yoff = lc->yoff, adjust;
|
||||
u_int i;
|
||||
enum pane_lines lines = window_pane_get_pane_lines(wp);
|
||||
|
||||
if (args_has(args, 'X')) {
|
||||
xoff = args_percentage_and_expand(args, 'X', -(int)w->sx,
|
||||
w->sx, w->sx, item, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(item, "position %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (lines != PANE_LINES_NONE)
|
||||
xoff += 1;
|
||||
}
|
||||
if (args_has(args, 'Y')) {
|
||||
yoff = args_percentage_and_expand(args, 'Y', -(int)w->sy,
|
||||
w->sy, w->sy, item, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(item, "position %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (lines != PANE_LINES_NONE)
|
||||
yoff += 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < nitems(flags); i++) {
|
||||
flag = flags[i];
|
||||
if (!args_has(args, flag))
|
||||
continue;
|
||||
|
||||
argval = args_get(args, flag);
|
||||
if (argval == NULL)
|
||||
argval = "1";
|
||||
adjust = strtonum(argval, INT_MIN, INT_MAX, &errstr);
|
||||
if (errstr != NULL) {
|
||||
cmdq_error(item, "offset %s", errstr);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (flag == 'U')
|
||||
yoff -= adjust;
|
||||
else if (flag == 'D')
|
||||
yoff += adjust;
|
||||
else if (flag == 'L')
|
||||
xoff -= adjust;
|
||||
else
|
||||
xoff += adjust;
|
||||
}
|
||||
|
||||
if (xoff != lc->xoff || yoff != lc->yoff) {
|
||||
lc->xoff = xoff;
|
||||
lc->yoff = yoff;
|
||||
layout_fix_panes(w, NULL);
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
}
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_join_pane_mouse_update(struct cmdq_item *item)
|
||||
{
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct key_event *event = cmdq_get_event(item);
|
||||
struct client *c = cmdq_get_client(item);
|
||||
struct session *s = target->s;
|
||||
struct winlink *wl;
|
||||
struct window *w;
|
||||
struct window_pane *wp;
|
||||
|
||||
if (!event->m.valid)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
wp = cmd_mouse_pane(&event->m, &s, &wl);
|
||||
if (wp == NULL || c == NULL || c->session != s)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
if (!window_pane_is_floating(wp))
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
w = wl->window;
|
||||
window_redraw_active_switch(w, wp);
|
||||
window_set_active_pane(w, wp, 1);
|
||||
|
||||
c->tty.mouse_drag_update = cmd_join_pane_mouse_move;
|
||||
cmd_join_pane_mouse_move(c, &event->m);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_join_pane_mouse_move(struct client *c, struct mouse_event *m)
|
||||
{
|
||||
struct winlink *wl;
|
||||
struct window *w;
|
||||
struct window_pane *wp;
|
||||
struct layout_cell *lc;
|
||||
int y, ly, x, lx;
|
||||
|
||||
wp = cmd_mouse_pane(m, NULL, &wl);
|
||||
if (wp == NULL) {
|
||||
c->tty.mouse_drag_update = NULL;
|
||||
return;
|
||||
}
|
||||
w = wl->window;
|
||||
lc = wp->layout_cell;
|
||||
|
||||
y = m->y + m->oy; x = m->x + m->ox;
|
||||
if (m->statusat == 0 && y >= (int)m->statuslines)
|
||||
y -= m->statuslines;
|
||||
else if (m->statusat > 0 && y >= m->statusat)
|
||||
y = m->statusat - 1;
|
||||
ly = m->ly + m->oy; lx = m->lx + m->ox;
|
||||
if (m->statusat == 0 && ly >= (int)m->statuslines)
|
||||
ly -= m->statuslines;
|
||||
else if (m->statusat > 0 && ly >= m->statusat)
|
||||
ly = m->statusat - 1;
|
||||
|
||||
if (x != lx || y != ly) {
|
||||
lc->xoff += x - lx;
|
||||
lc->yoff += y - ly;
|
||||
layout_fix_panes(w, NULL);
|
||||
server_redraw_window(w);
|
||||
server_redraw_window_borders(w);
|
||||
}
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_join_pane_zindex(struct cmdq_item *item, struct winlink *wl,
|
||||
struct window_pane *wp, const char *s)
|
||||
{
|
||||
struct window *w = wl->window;
|
||||
struct window_pane *owp;
|
||||
const char *errstr;
|
||||
u_int n, z;
|
||||
|
||||
z = strtonum(s, 0, UINT_MAX, &errstr);
|
||||
if (errstr != NULL) {
|
||||
cmdq_error(item, "z-index %s", errstr);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
|
||||
n = 0;
|
||||
TAILQ_FOREACH(owp, &w->z_index, zentry) {
|
||||
if (!window_pane_is_floating(owp))
|
||||
break;
|
||||
if (n >= z)
|
||||
break;
|
||||
n++;
|
||||
}
|
||||
|
||||
if (owp != NULL)
|
||||
TAILQ_INSERT_BEFORE(owp, wp, zentry);
|
||||
else
|
||||
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
|
||||
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_join_pane_tile(struct cmdq_item *item, struct args *args, struct window *w,
|
||||
struct window_pane *wp)
|
||||
{
|
||||
struct layout_cell *lc = wp->layout_cell;
|
||||
|
||||
if (!window_pane_is_floating(wp)) {
|
||||
cmdq_error(item, "pane is not floating");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (window_pane_is_hidden(wp)) {
|
||||
cmdq_error(item, "can't tile a hidden pane");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (w->flags & WINDOW_ZOOMED) {
|
||||
cmdq_error(item, "can't tile a pane while window is zoomed");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
layout_save_size(lc);
|
||||
lc->flags &= ~LAYOUT_CELL_FLOATING;
|
||||
if (layout_insert_tile(w, lc) == 0) {
|
||||
cmdq_error(item, "no space for a new pane");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
TAILQ_INSERT_TAIL(&w->z_index, wp, zentry);
|
||||
|
||||
if (!args_has(args, 'd'))
|
||||
window_set_active_pane(w, wp, 1);
|
||||
layout_fix_offsets(w);
|
||||
layout_fix_panes(w, NULL);
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
@@ -70,12 +413,10 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct winlink *src_wl, *dst_wl;
|
||||
struct window *src_w, *dst_w;
|
||||
struct window_pane *src_wp, *dst_wp;
|
||||
const char *s;
|
||||
char *cause = NULL;
|
||||
int size, dst_idx;
|
||||
int flags;
|
||||
enum layout_type type;
|
||||
struct layout_cell *lc;
|
||||
u_int curval = 0;
|
||||
int flags = 0, dst_idx;
|
||||
struct layout_cell *lc, *src_lc;
|
||||
|
||||
dst_s = target->s;
|
||||
dst_wl = target->wl;
|
||||
@@ -84,61 +425,51 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
dst_idx = dst_wl->idx;
|
||||
server_unzoom_window(dst_w);
|
||||
|
||||
if (cmd_get_entry(self) == &cmd_move_pane_entry) {
|
||||
if (args_has(args, 'M'))
|
||||
return (cmd_join_pane_mouse_update(item));
|
||||
if (!window_pane_is_floating(dst_wp)) {
|
||||
cmdq_error(item, "pane is not floating");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if ((s = args_get(args, 'P')) != NULL)
|
||||
return (cmd_join_pane_place(item, dst_wl, dst_wp, s));
|
||||
if ((s = args_get(args, 'z')) != NULL)
|
||||
return (cmd_join_pane_zindex(item, dst_wl, dst_wp, s));
|
||||
if (args_has(args, 'X') ||
|
||||
args_has(args, 'Y') ||
|
||||
args_has(args, 'U') ||
|
||||
args_has(args, 'D') ||
|
||||
args_has(args, 'L') ||
|
||||
args_has(args, 'R'))
|
||||
return (cmd_join_pane_move(item, args, dst_wl, dst_wp));
|
||||
}
|
||||
|
||||
src_wl = source->wl;
|
||||
src_wp = source->wp;
|
||||
src_lc = src_wp->layout_cell;
|
||||
src_w = src_wl->window;
|
||||
server_unzoom_window(src_w);
|
||||
|
||||
if (!args_has(args, 't') && window_pane_is_floating(src_wp))
|
||||
return (cmd_join_pane_tile(item, args, src_w, src_wp));
|
||||
|
||||
if (src_wp == dst_wp) {
|
||||
cmdq_error(item, "source and target panes must be different");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
type = LAYOUT_TOPBOTTOM;
|
||||
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')) {
|
||||
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, 'l', 0, 100, item,
|
||||
&cause);
|
||||
if (cause == NULL)
|
||||
size = curval * size / 100;
|
||||
}
|
||||
lc = layout_get_tiled_cell(item, args, dst_w, dst_wp, flags, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(item, "size %s", cause);
|
||||
cmdq_error(item, "size or position %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
flags = 0;
|
||||
if (args_has(args, 'b'))
|
||||
flags |= SPAWN_BEFORE;
|
||||
if (args_has(args, 'f'))
|
||||
flags |= SPAWN_FULLSIZE;
|
||||
|
||||
lc = layout_split_pane(dst_wp, type, size, flags);
|
||||
if (lc == NULL) {
|
||||
cmdq_error(item, "create pane failed: pane too small");
|
||||
return (CMD_RETURN_ERROR);
|
||||
if (window_pane_is_floating(src_wp)) {
|
||||
lc->saved_sx = src_lc->sx;
|
||||
lc->saved_sy = src_lc->sy;
|
||||
lc->saved_xoff = src_lc->xoff;
|
||||
lc->saved_yoff = src_lc->yoff;
|
||||
}
|
||||
|
||||
layout_close_pane(src_wp);
|
||||
@@ -146,14 +477,18 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
server_client_remove_pane(src_wp);
|
||||
window_lost_pane(src_w, src_wp);
|
||||
TAILQ_REMOVE(&src_w->panes, src_wp, entry);
|
||||
TAILQ_REMOVE(&src_w->z_index, src_wp, zentry);
|
||||
|
||||
src_wp->window = dst_w;
|
||||
options_set_parent(src_wp->options, dst_w->options);
|
||||
src_wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
|
||||
if (flags & SPAWN_BEFORE)
|
||||
if (flags & SPAWN_BEFORE) {
|
||||
TAILQ_INSERT_BEFORE(dst_wp, src_wp, entry);
|
||||
else
|
||||
TAILQ_INSERT_BEFORE(dst_wp, src_wp, zentry);
|
||||
} else {
|
||||
TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
|
||||
TAILQ_INSERT_AFTER(&dst_w->z_index, dst_wp, src_wp, zentry);
|
||||
}
|
||||
layout_assign_pane(lc, src_wp, 0);
|
||||
colour_palette_from_option(&src_wp->palette, src_wp->options);
|
||||
|
||||
@@ -170,7 +505,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
} else
|
||||
server_status_session(dst_s);
|
||||
|
||||
if (window_count_panes(src_w) == 0)
|
||||
if (window_count_panes(src_w, 1) == 0)
|
||||
server_kill_window(src_w, 1);
|
||||
else
|
||||
notify_window("window-layout-changed", src_w);
|
||||
|
||||
@@ -27,13 +27,17 @@
|
||||
*/
|
||||
|
||||
static enum cmd_retval cmd_kill_pane_exec(struct cmd *, struct cmdq_item *);
|
||||
static enum cmd_retval cmd_kill_pane_all(struct cmdq_item *, const char *);
|
||||
static int cmd_kill_pane_filter(struct cmdq_item *,
|
||||
struct session *, struct winlink *,
|
||||
struct window_pane *, const char *);
|
||||
|
||||
const struct cmd_entry cmd_kill_pane_entry = {
|
||||
.name = "kill-pane",
|
||||
.alias = "killp",
|
||||
|
||||
.args = { "at:", 0, 0, NULL },
|
||||
.usage = "[-a] " CMD_TARGET_PANE_USAGE,
|
||||
.args = { "af:t:", 0, 0, NULL },
|
||||
.usage = "[-a] [-f filter] " CMD_TARGET_PANE_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
@@ -46,22 +50,66 @@ cmd_kill_pane_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 winlink *wl = target->wl;
|
||||
struct window_pane *loopwp, *tmpwp, *wp = target->wp;
|
||||
struct window_pane *wp = target->wp;
|
||||
const char *filter = args_get(args, 'f');
|
||||
|
||||
if (args_has(args, 'a')) {
|
||||
server_unzoom_window(wl->window);
|
||||
TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) {
|
||||
if (loopwp == wp)
|
||||
continue;
|
||||
server_client_remove_pane(loopwp);
|
||||
layout_close_pane(loopwp);
|
||||
window_remove_pane(wl->window, loopwp);
|
||||
}
|
||||
server_redraw_window(wl->window);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
if (filter != NULL && !args_has(args, 'a')) {
|
||||
cmdq_error(item, "-f only valid with -a");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (args_has(args, 'a'))
|
||||
return (cmd_kill_pane_all(item, filter));
|
||||
|
||||
if (wp == NULL) {
|
||||
cmdq_error(item, "no active pane to kill");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
server_kill_pane(wp);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_kill_pane_all(struct cmdq_item *item, const char *filter)
|
||||
{
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct session *s = target->s;
|
||||
struct winlink *wl = target->wl;
|
||||
struct window_pane *wp = target->wp;
|
||||
struct window_pane *loopwp, *tmpwp;
|
||||
|
||||
server_unzoom_window(wl->window);
|
||||
TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) {
|
||||
if (loopwp == wp)
|
||||
continue;
|
||||
if (!cmd_kill_pane_filter(item, s, wl, loopwp, filter))
|
||||
continue;
|
||||
server_client_remove_pane(loopwp);
|
||||
layout_close_pane(loopwp);
|
||||
window_remove_pane(wl->window, loopwp);
|
||||
}
|
||||
server_redraw_window(wl->window);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static int
|
||||
cmd_kill_pane_filter(struct cmdq_item *item, struct session *s,
|
||||
struct winlink *wl, struct window_pane *wp, const char *filter)
|
||||
{
|
||||
struct format_tree *ft;
|
||||
char *expanded;
|
||||
int flag;
|
||||
|
||||
if (filter == NULL)
|
||||
return (1);
|
||||
|
||||
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
||||
format_defaults(ft, NULL, s, wl, wp);
|
||||
|
||||
expanded = format_expand(ft, filter);
|
||||
flag = format_true(expanded);
|
||||
free(expanded);
|
||||
|
||||
format_free(ft);
|
||||
return (flag);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
/*
|
||||
@@ -28,13 +30,16 @@
|
||||
*/
|
||||
|
||||
static enum cmd_retval cmd_kill_session_exec(struct cmd *, struct cmdq_item *);
|
||||
static enum cmd_retval cmd_kill_session_all(struct cmdq_item *, const char *);
|
||||
static int cmd_kill_session_filter(struct cmdq_item *,
|
||||
struct session *, const char *);
|
||||
|
||||
const struct cmd_entry cmd_kill_session_entry = {
|
||||
.name = "kill-session",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "aCt:", 0, 0, NULL },
|
||||
.usage = "[-aC] " CMD_TARGET_SESSION_USAGE,
|
||||
.args = { "aCgf:t:", 0, 0, NULL },
|
||||
.usage = "[-aCg] [-f filter] " CMD_TARGET_SESSION_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_SESSION, 0 },
|
||||
|
||||
@@ -48,7 +53,14 @@ cmd_kill_session_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 session *s = target->s, *sloop, *stmp;
|
||||
struct session_group *sg;
|
||||
struct winlink *wl;
|
||||
const char *filter = args_get(args, 'f');
|
||||
|
||||
if (filter != NULL && (!args_has(args, 'a') || args_has(args, 'C'))) {
|
||||
cmdq_error(item, "-f only valid with -a");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (args_has(args, 'C')) {
|
||||
RB_FOREACH(wl, winlinks, &s->windows) {
|
||||
@@ -56,12 +68,13 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
|
||||
wl->flags &= ~WINLINK_ALERTFLAGS;
|
||||
}
|
||||
server_redraw_session(s);
|
||||
} else if (args_has(args, 'a')) {
|
||||
RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) {
|
||||
if (sloop != s) {
|
||||
server_destroy_session(sloop);
|
||||
session_destroy(sloop, 1, __func__);
|
||||
}
|
||||
} else if (args_has(args, 'a'))
|
||||
return (cmd_kill_session_all(item, filter));
|
||||
else if (args_has(args, 'g') &&
|
||||
(sg = session_group_contains(s)) != NULL) {
|
||||
TAILQ_FOREACH_SAFE(sloop, &sg->sessions, gentry, stmp) {
|
||||
server_destroy_session(sloop);
|
||||
session_destroy(sloop, 1, __func__);
|
||||
}
|
||||
} else {
|
||||
server_destroy_session(s);
|
||||
@@ -69,3 +82,42 @@ cmd_kill_session_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_kill_session_all(struct cmdq_item *item, const char *filter)
|
||||
{
|
||||
struct session *s = cmdq_get_target(item)->s;
|
||||
struct session *sloop, *stmp;
|
||||
|
||||
RB_FOREACH_SAFE(sloop, sessions, &sessions, stmp) {
|
||||
if (sloop == s)
|
||||
continue;
|
||||
if (!cmd_kill_session_filter(item, sloop, filter))
|
||||
continue;
|
||||
server_destroy_session(sloop);
|
||||
session_destroy(sloop, 1, __func__);
|
||||
}
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static int
|
||||
cmd_kill_session_filter(struct cmdq_item *item, struct session *s,
|
||||
const char *filter)
|
||||
{
|
||||
struct format_tree *ft;
|
||||
char *expanded;
|
||||
int flag;
|
||||
|
||||
if (filter == NULL)
|
||||
return (1);
|
||||
|
||||
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
||||
format_defaults(ft, NULL, s, NULL, NULL);
|
||||
|
||||
expanded = format_expand(ft, filter);
|
||||
flag = format_true(expanded);
|
||||
free(expanded);
|
||||
|
||||
format_free(ft);
|
||||
return (flag);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
/*
|
||||
@@ -25,13 +27,16 @@
|
||||
*/
|
||||
|
||||
static enum cmd_retval cmd_kill_window_exec(struct cmd *, struct cmdq_item *);
|
||||
static enum cmd_retval cmd_kill_window_all(struct cmdq_item *, const char *);
|
||||
static int cmd_kill_window_filter(struct cmdq_item *,
|
||||
struct session *, struct winlink *, const char *);
|
||||
|
||||
const struct cmd_entry cmd_kill_window_entry = {
|
||||
.name = "kill-window",
|
||||
.alias = "killw",
|
||||
|
||||
.args = { "at:", 0, 0, NULL },
|
||||
.usage = "[-a] " CMD_TARGET_WINDOW_USAGE,
|
||||
.args = { "af:t:", 0, 0, NULL },
|
||||
.usage = "[-a] [-f filter] " CMD_TARGET_WINDOW_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_WINDOW, 0 },
|
||||
|
||||
@@ -57,10 +62,15 @@ cmd_kill_window_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 winlink *wl = target->wl, *loop;
|
||||
struct winlink *wl = target->wl;
|
||||
struct window *w = wl->window;
|
||||
struct session *s = target->s;
|
||||
u_int found;
|
||||
const char *filter = args_get(args, 'f');
|
||||
|
||||
if (filter != NULL && !args_has(args, 'a')) {
|
||||
cmdq_error(item, "-f only valid with -a");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (cmd_get_entry(self) == &cmd_unlink_window_entry) {
|
||||
if (!args_has(args, 'k') && !session_is_linked(s, w)) {
|
||||
@@ -72,39 +82,76 @@ cmd_kill_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
if (args_has(args, 'a')) {
|
||||
if (RB_PREV(winlinks, &s->windows, wl) == NULL &&
|
||||
RB_NEXT(winlinks, &s->windows, wl) == NULL)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
/* Kill all windows except the current one. */
|
||||
do {
|
||||
found = 0;
|
||||
RB_FOREACH(loop, winlinks, &s->windows) {
|
||||
if (loop->window != wl->window) {
|
||||
server_kill_window(loop->window, 0);
|
||||
found++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (found != 0);
|
||||
|
||||
/*
|
||||
* If the current window appears in the session more than once,
|
||||
* kill it as well.
|
||||
*/
|
||||
found = 0;
|
||||
RB_FOREACH(loop, winlinks, &s->windows) {
|
||||
if (loop->window == wl->window)
|
||||
found++;
|
||||
}
|
||||
if (found > 1)
|
||||
server_kill_window(wl->window, 0);
|
||||
|
||||
server_renumber_all();
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
if (args_has(args, 'a'))
|
||||
return (cmd_kill_window_all(item, filter));
|
||||
|
||||
server_kill_window(wl->window, 1);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_kill_window_all(struct cmdq_item *item, const char *filter)
|
||||
{
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct session *s = target->s;
|
||||
struct winlink *wl = target->wl;
|
||||
struct winlink *loop;
|
||||
u_int found, kill_current;
|
||||
|
||||
if (RB_PREV(winlinks, &s->windows, wl) == NULL &&
|
||||
RB_NEXT(winlinks, &s->windows, wl) == NULL)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
/* Kill all windows except the current one. */
|
||||
do {
|
||||
found = 0;
|
||||
RB_FOREACH(loop, winlinks, &s->windows) {
|
||||
if (loop->window != wl->window &&
|
||||
cmd_kill_window_filter(item, s, loop, filter)) {
|
||||
server_kill_window(loop->window, 0);
|
||||
found++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (found != 0);
|
||||
|
||||
/*
|
||||
* If the current window appears in the session more than once, kill it
|
||||
* as well if it matches the filter.
|
||||
*/
|
||||
found = kill_current = 0;
|
||||
RB_FOREACH(loop, winlinks, &s->windows) {
|
||||
if (loop->window == wl->window) {
|
||||
found++;
|
||||
if (cmd_kill_window_filter(item, s, loop, filter))
|
||||
kill_current = 1;
|
||||
}
|
||||
}
|
||||
if (kill_current && found > 1)
|
||||
server_kill_window(wl->window, 0);
|
||||
|
||||
server_renumber_all();
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static int
|
||||
cmd_kill_window_filter(struct cmdq_item *item, struct session *s,
|
||||
struct winlink *wl, const char *filter)
|
||||
{
|
||||
struct format_tree *ft;
|
||||
char *expanded;
|
||||
int flag;
|
||||
|
||||
if (filter == NULL)
|
||||
return (1);
|
||||
|
||||
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
||||
format_defaults(ft, NULL, s, wl, NULL);
|
||||
|
||||
expanded = format_expand(ft, filter);
|
||||
flag = format_true(expanded);
|
||||
free(expanded);
|
||||
|
||||
format_free(ft);
|
||||
return (flag);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"," \
|
||||
"bind-key #{?key_has_repeat,#{?key_repeat,-r, },} " \
|
||||
"-T #{p|#{key_table_width}:key_table} " \
|
||||
"#{p|#{key_string_width}:key_string} " \
|
||||
"#{p|#{key_string_width}:#{q|a:key_string}} " \
|
||||
"#{key_command}}"
|
||||
|
||||
static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *);
|
||||
@@ -93,7 +93,7 @@ cmd_list_keys_get_table_width(struct key_binding **l, u_int n)
|
||||
}
|
||||
|
||||
static struct key_binding **
|
||||
cmd_get_root_and_prefix(u_int *n, struct sort_criteria *sort_crit)
|
||||
cmd_list_keys_get_root_and_prefix(u_int *n, struct sort_criteria *sort_crit)
|
||||
{
|
||||
const char *tables[] = { "prefix", "root" };
|
||||
struct key_table *t;
|
||||
@@ -119,7 +119,7 @@ cmd_get_root_and_prefix(u_int *n, struct sort_criteria *sort_crit)
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_filter_key_list(int filter_notes, int filter_key, key_code only,
|
||||
cmd_list_keys_filter_key_list(int filter_notes, int filter_key, key_code only,
|
||||
struct key_binding **l, u_int *n)
|
||||
{
|
||||
key_code key;
|
||||
@@ -137,10 +137,10 @@ cmd_filter_key_list(int filter_notes, int filter_key, key_code only,
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_format_add_key_binding(struct format_tree *ft,
|
||||
cmd_list_keys_format_add_key_binding(struct format_tree *ft,
|
||||
const struct key_binding *bd, const char *prefix)
|
||||
{
|
||||
const char *s;
|
||||
char *s;
|
||||
|
||||
if (bd->flags & KEY_BINDING_REPEAT)
|
||||
format_add(ft, "key_repeat", "1");
|
||||
@@ -155,12 +155,12 @@ cmd_format_add_key_binding(struct format_tree *ft,
|
||||
format_add(ft, "key_prefix", "%s", prefix);
|
||||
format_add(ft, "key_table", "%s", bd->tablename);
|
||||
|
||||
s = key_string_lookup_key(bd->key, 0);
|
||||
format_add(ft, "key_string", "%s", s);
|
||||
format_add(ft, "key_string", "%s", key_string_lookup_key(bd->key, 0));
|
||||
|
||||
s = cmd_list_print(bd->cmdlist,
|
||||
CMD_LIST_PRINT_ESCAPED|CMD_LIST_PRINT_NO_GROUPS);
|
||||
s = cmd_list_print(bd->cmdlist, CMD_LIST_PRINT_ESCAPED|
|
||||
CMD_LIST_PRINT_NO_GROUPS);
|
||||
format_add(ft, "key_command", "%s", s);
|
||||
free(s);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
@@ -195,10 +195,6 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
sort_crit.reversed = args_has(args, 'r');
|
||||
|
||||
prefix = cmd_list_keys_get_prefix(args);
|
||||
single = args_has(args, '1');
|
||||
notes_only = args_has(args, 'N');
|
||||
|
||||
if ((tablename = args_get(args, 'T')) != NULL) {
|
||||
table = key_bindings_get_table(tablename, 0);
|
||||
if (table == NULL) {
|
||||
@@ -207,35 +203,46 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
}
|
||||
|
||||
prefix = cmd_list_keys_get_prefix(args);
|
||||
single = args_has(args, '1');
|
||||
notes_only = args_has(args, 'N');
|
||||
|
||||
if ((template = args_get(args, 'F')) == NULL)
|
||||
template = LIST_KEYS_TEMPLATE;
|
||||
|
||||
if (table)
|
||||
l = sort_get_key_bindings_table(table, &n, &sort_crit);
|
||||
else if (notes_only)
|
||||
l = cmd_get_root_and_prefix(&n, &sort_crit);
|
||||
l = cmd_list_keys_get_root_and_prefix(&n, &sort_crit);
|
||||
else
|
||||
l = sort_get_key_bindings(&n, &sort_crit);
|
||||
|
||||
filter_notes = notes_only && !args_has(args, 'a');
|
||||
filter_key = only != KEYC_UNKNOWN;
|
||||
if (filter_notes || filter_key)
|
||||
cmd_filter_key_list(filter_notes, filter_key, only, l, &n);
|
||||
if (single)
|
||||
if (filter_notes || filter_key) {
|
||||
cmd_list_keys_filter_key_list(filter_notes, filter_key, only, l,
|
||||
&n);
|
||||
}
|
||||
if (filter_key && n == 0) {
|
||||
cmdq_error(item, "unknown key: %s", keystr);
|
||||
free(prefix);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (single && n > 1)
|
||||
n = 1;
|
||||
|
||||
ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0);
|
||||
format_defaults(ft, NULL, NULL, NULL, NULL);
|
||||
format_defaults(ft, tc, NULL, NULL, NULL);
|
||||
format_add(ft, "notes_only", "%d", notes_only);
|
||||
format_add(ft, "key_has_repeat", "%d", key_bindings_has_repeat(l, n));
|
||||
format_add(ft, "key_string_width", "%u", cmd_list_keys_get_width(l, n));
|
||||
format_add(ft, "key_table_width", "%u",
|
||||
cmd_list_keys_get_table_width(l, n));
|
||||
for (i = 0; i < n; i++) {
|
||||
cmd_format_add_key_binding(ft, l[i], prefix);
|
||||
cmd_list_keys_format_add_key_binding(ft, l[i], prefix);
|
||||
|
||||
line = format_expand(ft, template);
|
||||
if ((single && tc != NULL) || n == 1)
|
||||
if (single && tc != NULL && (~tc->flags & CLIENT_CONTROL))
|
||||
status_message_set(tc, -1, 1, 0, 0, "%s", line);
|
||||
else if (*line != '\0')
|
||||
cmdq_print(item, "%s", line);
|
||||
|
||||
@@ -110,22 +110,28 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl,
|
||||
switch (type) {
|
||||
case 0:
|
||||
template = "#{pane_index}: "
|
||||
"[#{pane_width}x#{pane_height}] [history "
|
||||
"[#{pane_width}x#{pane_height}"
|
||||
"#{?pane_floating_flag, "
|
||||
"#{pane_x}#,#{pane_y}#,#{pane_z}}] [history "
|
||||
"#{history_size}/#{history_limit}, "
|
||||
"#{history_bytes} bytes] #{pane_id}"
|
||||
"#{?pane_active, (active),}#{?pane_dead, (dead),}";
|
||||
break;
|
||||
case 1:
|
||||
template = "#{window_index}.#{pane_index}: "
|
||||
"[#{pane_width}x#{pane_height}] [history "
|
||||
"[#{pane_width}x#{pane_height}"
|
||||
"#{?pane_floating_flag, "
|
||||
"#{pane_x}#,#{pane_y}#,#{pane_z}}] [history "
|
||||
"#{history_size}/#{history_limit}, "
|
||||
"#{history_bytes} bytes] #{pane_id}"
|
||||
"#{?pane_active, (active),}#{?pane_dead, (dead),}";
|
||||
break;
|
||||
case 2:
|
||||
template = "#{session_name}:#{window_index}."
|
||||
"#{pane_index}: [#{pane_width}x#{pane_height}] "
|
||||
"[history #{history_size}/#{history_limit}, "
|
||||
"#{pane_index}: [#{pane_width}x#{pane_height}"
|
||||
"#{?pane_floating_flag, "
|
||||
"#{pane_x}#,#{pane_y}#,#{pane_z}}] [history "
|
||||
"#{history_size}/#{history_limit}, "
|
||||
"#{history_bytes} bytes] #{pane_id}"
|
||||
"#{?pane_active, (active),}#{?pane_dead, (dead),}";
|
||||
break;
|
||||
|
||||
@@ -77,8 +77,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct termios tio, *tiop;
|
||||
struct session_group *sg = NULL;
|
||||
const char *errstr, *template, *group, *tmp;
|
||||
char *cause, *cwd = NULL, *cp, *newname = NULL;
|
||||
char *name, *prefix = NULL;
|
||||
char *cause, *cwd = NULL, *cp, *ename;
|
||||
char *wname = NULL, *sname = NULL, *prefix = NULL;
|
||||
int detached, already_attached, is_control = 0;
|
||||
u_int sx, sy, dsx, dsy, count = args_count(args);
|
||||
struct spawn_context sc = { 0 };
|
||||
@@ -99,20 +99,29 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
tmp = args_get(args, 's');
|
||||
if (tmp != NULL) {
|
||||
name = format_single(item, tmp, c, NULL, NULL, NULL);
|
||||
newname = session_check_name(name);
|
||||
if (newname == NULL) {
|
||||
cmdq_error(item, "invalid session: %s", name);
|
||||
free(name);
|
||||
if ((tmp = args_get(args, 'n')) != NULL) {
|
||||
ename = format_single(item, tmp, c, NULL, NULL, NULL);
|
||||
if (!check_name(ename)) {
|
||||
cmdq_error(item, "invalid window name: %s", ename);
|
||||
free(ename);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
free(name);
|
||||
wname = clean_name(ename, 0);
|
||||
free(ename);
|
||||
}
|
||||
if ((tmp = args_get(args, 's')) != NULL) {
|
||||
ename = format_single(item, tmp, c, NULL, NULL, NULL);
|
||||
if (!check_name(ename)) {
|
||||
cmdq_error(item, "invalid session name: %s", ename);
|
||||
free(ename);
|
||||
goto fail;
|
||||
}
|
||||
sname = clean_name(ename, 0);
|
||||
free(ename);
|
||||
}
|
||||
if (args_has(args, 'A')) {
|
||||
if (newname != NULL)
|
||||
as = session_find(newname);
|
||||
if (sname != NULL)
|
||||
as = session_find(sname);
|
||||
else
|
||||
as = target->s;
|
||||
if (as != NULL) {
|
||||
@@ -120,12 +129,13 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
|
||||
args_has(args, 'D'), args_has(args, 'X'), 0,
|
||||
args_get(args, 'c'), args_has(args, 'E'),
|
||||
args_get(args, 'f'));
|
||||
free(newname);
|
||||
free(wname);
|
||||
free(sname);
|
||||
return (retval);
|
||||
}
|
||||
}
|
||||
if (newname != NULL && session_find(newname) != NULL) {
|
||||
cmdq_error(item, "duplicate session: %s", newname);
|
||||
if (sname != NULL && session_find(sname) != NULL) {
|
||||
cmdq_error(item, "duplicate session: %s", sname);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -142,12 +152,12 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
|
||||
else if (groupwith != NULL)
|
||||
prefix = xstrdup(groupwith->name);
|
||||
else {
|
||||
prefix = session_check_name(group);
|
||||
if (prefix == NULL) {
|
||||
cmdq_error(item, "invalid session group: %s",
|
||||
group);
|
||||
if (!check_name(group)) {
|
||||
cmdq_error(item,
|
||||
"invalid session group name: %s", group);
|
||||
goto fail;
|
||||
}
|
||||
prefix = clean_name(group, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +286,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
|
||||
environ_put(env, av->string, 0);
|
||||
av = args_next_value(av);
|
||||
}
|
||||
s = session_create(prefix, newname, cwd, env, oo, tiop);
|
||||
s = session_create(prefix, sname, cwd, env, oo, tiop);
|
||||
|
||||
/* Spawn the initial window. */
|
||||
sc.item = item;
|
||||
@@ -284,7 +294,7 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if (!detached)
|
||||
sc.tc = c;
|
||||
|
||||
sc.name = args_get(args, 'n');
|
||||
sc.name = wname;
|
||||
args_to_vector(args, &sc.argc, &sc.argv);
|
||||
|
||||
sc.idx = -1;
|
||||
@@ -357,7 +367,8 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if (sc.argv != NULL)
|
||||
cmd_free_argv(sc.argc, sc.argv);
|
||||
free(cwd);
|
||||
free(newname);
|
||||
free(wname);
|
||||
free(sname);
|
||||
free(prefix);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
@@ -365,7 +376,8 @@ fail:
|
||||
if (sc.argv != NULL)
|
||||
cmd_free_argv(sc.argc, sc.argv);
|
||||
free(cwd);
|
||||
free(newname);
|
||||
free(wname);
|
||||
free(sname);
|
||||
free(prefix);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
@@ -61,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, *expanded;
|
||||
char *cause = NULL, *cp, *expanded, *wname = NULL;
|
||||
const char *template, *name;
|
||||
struct cmd_find_state fs;
|
||||
struct args_value *av;
|
||||
@@ -71,8 +71,18 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
* name already exists, select it.
|
||||
*/
|
||||
name = args_get(args, 'n');
|
||||
if (args_has(args, 'S') && name != NULL && target->idx == -1) {
|
||||
if (name != NULL) {
|
||||
expanded = format_single(item, name, c, s, NULL, NULL);
|
||||
if (!check_name(expanded)) {
|
||||
cmdq_error(item, "invalid window name: %s", expanded);
|
||||
free(expanded);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
wname = clean_name(expanded, 0);
|
||||
free(expanded);
|
||||
}
|
||||
if (args_has(args, 'S') && wname != NULL && target->idx == -1) {
|
||||
expanded = format_single(item, wname, c, s, NULL, NULL);
|
||||
RB_FOREACH(wl, winlinks, &s->windows) {
|
||||
if (strcmp(wl->window->name, expanded) != 0)
|
||||
continue;
|
||||
@@ -80,12 +90,14 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
new_wl = wl;
|
||||
continue;
|
||||
}
|
||||
cmdq_error(item, "multiple windows named %s", name);
|
||||
cmdq_error(item, "multiple windows named %s", wname);
|
||||
free(wname);
|
||||
free(expanded);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
free(expanded);
|
||||
if (new_wl != NULL) {
|
||||
free(wname);
|
||||
if (args_has(args, 'd'))
|
||||
return (CMD_RETURN_NORMAL);
|
||||
if (session_set_current(s, new_wl) == 0)
|
||||
@@ -108,7 +120,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
sc.s = s;
|
||||
sc.tc = tc;
|
||||
|
||||
sc.name = args_get(args, 'n');
|
||||
sc.name = wname;
|
||||
args_to_vector(args, &sc.argc, &sc.argv);
|
||||
sc.environ = environ_create();
|
||||
|
||||
@@ -130,10 +142,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if ((new_wl = spawn_window(&sc, &cause)) == NULL) {
|
||||
cmdq_error(item, "create window failed: %s", cause);
|
||||
free(cause);
|
||||
if (sc.argv != NULL)
|
||||
cmd_free_argv(sc.argc, sc.argv);
|
||||
environ_free(sc.environ);
|
||||
return (CMD_RETURN_ERROR);
|
||||
goto fail;
|
||||
}
|
||||
if (!args_has(args, 'd') || new_wl == s->curw) {
|
||||
cmd_find_from_winlink(current, new_wl, 0);
|
||||
@@ -156,5 +165,13 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if (sc.argv != NULL)
|
||||
cmd_free_argv(sc.argc, sc.argv);
|
||||
environ_free(sc.environ);
|
||||
free(wname);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
fail:
|
||||
if (sc.argv != NULL)
|
||||
cmd_free_argv(sc.argc, sc.argv);
|
||||
environ_free(sc.environ);
|
||||
free(wname);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
14
cmd-parse.y
14
cmd-parse.y
@@ -37,6 +37,8 @@ static void printflike(1,2) yyerror(const char *, ...);
|
||||
static char *yylex_token(int);
|
||||
static char *yylex_format(void);
|
||||
|
||||
#define CMD_PARSE_MAX_ENVIRON_LEN 16384
|
||||
|
||||
struct cmd_parse_scope {
|
||||
int flag;
|
||||
TAILQ_ENTRY (cmd_parse_scope) entry;
|
||||
@@ -232,6 +234,10 @@ assignment : EQUALS
|
||||
flag = flag && scope->flag;
|
||||
}
|
||||
|
||||
if (strlen($1) > CMD_PARSE_MAX_ENVIRON_LEN) {
|
||||
yyerror("environment variable is too long");
|
||||
YYABORT;
|
||||
}
|
||||
if ((~flags & CMD_PARSE_PARSEONLY) && flag)
|
||||
environ_put(global_environ, $1, 0);
|
||||
free($1);
|
||||
@@ -250,6 +256,10 @@ hidden_assignment : HIDDEN EQUALS
|
||||
flag = flag && scope->flag;
|
||||
}
|
||||
|
||||
if (strlen($2) > CMD_PARSE_MAX_ENVIRON_LEN) {
|
||||
yyerror("environment variable is too long");
|
||||
YYABORT;
|
||||
}
|
||||
if ((~flags & CMD_PARSE_PARSEONLY) && flag)
|
||||
environ_put(global_environ, $2, ENVIRON_HIDDEN);
|
||||
free($2);
|
||||
@@ -1604,7 +1614,9 @@ yylex_token_tilde(char **buf, size_t *len)
|
||||
|
||||
if (*name == '\0') {
|
||||
envent = environ_find(global_environ, "HOME");
|
||||
if (envent != NULL && *envent->value != '\0')
|
||||
if (envent != NULL &&
|
||||
envent->value != NULL &&
|
||||
*envent->value != '\0')
|
||||
home = envent->value;
|
||||
else if ((pw = getpwuid(getuid())) != NULL)
|
||||
home = pw->pw_dir;
|
||||
|
||||
@@ -128,6 +128,8 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
||||
cmdq_error(item, "fork error: %s", strerror(errno));
|
||||
|
||||
close(pipe_fd[0]);
|
||||
close(pipe_fd[1]);
|
||||
free(cmd);
|
||||
return (CMD_RETURN_ERROR);
|
||||
case 0:
|
||||
|
||||
@@ -168,6 +168,7 @@ cmd_refresh_report(struct tty *tty, const char *value)
|
||||
{
|
||||
struct window_pane *wp;
|
||||
u_int pane;
|
||||
int fg, bg;
|
||||
size_t size = 0;
|
||||
char *copy, *split;
|
||||
|
||||
@@ -184,8 +185,14 @@ cmd_refresh_report(struct tty *tty, const char *value)
|
||||
if (wp == NULL)
|
||||
goto out;
|
||||
|
||||
tty_keys_colours(tty, split, strlen(split), &size, &wp->control_fg,
|
||||
&wp->control_bg);
|
||||
fg = wp->control_fg;
|
||||
bg = wp->control_bg;
|
||||
if (tty_keys_colours(tty, split, strlen(split), &size, &fg, &bg) == 0) {
|
||||
if (bg != wp->control_bg)
|
||||
wp->flags |= PANE_THEMECHANGED;
|
||||
wp->control_fg = fg;
|
||||
wp->control_bg = bg;
|
||||
}
|
||||
|
||||
out:
|
||||
free(copy);
|
||||
|
||||
@@ -52,12 +52,12 @@ cmd_rename_session_exec(struct cmd *self, struct cmdq_item *item)
|
||||
char *newname, *tmp;
|
||||
|
||||
tmp = format_single_from_target(item, args_string(args, 0));
|
||||
newname = session_check_name(tmp);
|
||||
if (newname == NULL) {
|
||||
cmdq_error(item, "invalid session: %s", tmp);
|
||||
if (!check_name(tmp)) {
|
||||
cmdq_error(item, "invalid session name: %s", tmp);
|
||||
free(tmp);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
newname = clean_name(tmp, 0);
|
||||
free(tmp);
|
||||
if (strcmp(newname, s->name) == 0) {
|
||||
free(newname);
|
||||
|
||||
@@ -48,15 +48,21 @@ cmd_rename_window_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 winlink *wl = target->wl;
|
||||
char *newname;
|
||||
char *name;
|
||||
|
||||
newname = format_single_from_target(item, args_string(args, 0));
|
||||
window_set_name(wl->window, newname);
|
||||
name = format_single_from_target(item, args_string(args, 0));
|
||||
if (!check_name(name)) {
|
||||
cmdq_error(item, "invalid window name: %s", name);
|
||||
free(name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
window_set_name(wl->window, name, 0);
|
||||
options_set_number(wl->window->options, "automatic-rename", 0);
|
||||
free(name);
|
||||
|
||||
server_redraw_window_borders(wl->window);
|
||||
server_status_window(wl->window);
|
||||
free(newname);
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -29,16 +30,20 @@
|
||||
|
||||
static enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmdq_item *);
|
||||
|
||||
static void cmd_resize_pane_mouse_update(struct client *,
|
||||
struct mouse_event *);
|
||||
static enum cmd_retval cmd_resize_pane_mouse_update(struct cmd *,
|
||||
struct cmdq_item *);
|
||||
static void cmd_resize_pane_mouse_resize_move_floating(
|
||||
struct client *, struct mouse_event *);
|
||||
static void cmd_resize_pane_mouse_resize_tiled(struct client *,
|
||||
struct mouse_event *);
|
||||
|
||||
const struct cmd_entry cmd_resize_pane_entry = {
|
||||
.name = "resize-pane",
|
||||
.alias = "resizep",
|
||||
|
||||
.args = { "DLMRTt:Ux:y:Z", 0, 1, NULL },
|
||||
.usage = "[-DLMRTUZ] [-x width] [-y height] " CMD_TARGET_PANE_USAGE " "
|
||||
"[adjustment]",
|
||||
.args = { "D::L::MR::Tt:U::x:y:Z", 0, 1, NULL },
|
||||
.usage = "[-MTZ] [-D lines] [-L columns] [-R columns] [-U lines] "
|
||||
"[-x width] [-y height] " CMD_TARGET_PANE_USAGE,
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
@@ -51,23 +56,23 @@ cmd_resize_pane_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 key_event *event = cmdq_get_event(item);
|
||||
struct window_pane *wp = target->wp;
|
||||
struct winlink *wl = target->wl;
|
||||
struct window *w = wl->window;
|
||||
struct client *c = cmdq_get_client(item);
|
||||
struct session *s = target->s;
|
||||
const char *errstr;
|
||||
char *cause;
|
||||
u_int adjust;
|
||||
int x, y, status;
|
||||
struct layout_cell *lc = wp->layout_cell;
|
||||
enum layout_type type;
|
||||
const char *errstr, *argval;
|
||||
const char flags[4] = { 'U', 'D', 'L', 'R' };
|
||||
char *cause = NULL, flag;
|
||||
int adjust, x, y, status, opposite = 0;
|
||||
long unsigned i;
|
||||
struct grid *gd = wp->base.grid;
|
||||
|
||||
if (args_has(args, 'T')) {
|
||||
if (!TAILQ_EMPTY(&wp->modes))
|
||||
return (CMD_RETURN_NORMAL);
|
||||
adjust = screen_size_y(&wp->base) - 1 - wp->base.cy;
|
||||
if (adjust > gd->hsize)
|
||||
if (adjust > (int)gd->hsize)
|
||||
adjust = gd->hsize;
|
||||
grid_remove_history(gd, adjust);
|
||||
wp->base.cy += adjust;
|
||||
@@ -75,15 +80,8 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
if (args_has(args, 'M')) {
|
||||
if (!event->m.valid || cmd_mouse_window(&event->m, &s) == NULL)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
if (c == NULL || c->session != s)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
c->tty.mouse_drag_update = cmd_resize_pane_mouse_update;
|
||||
cmd_resize_pane_mouse_update(c, &event->m);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
if (args_has(args, 'M'))
|
||||
return (cmd_resize_pane_mouse_update(self, item));
|
||||
|
||||
if (args_has(args, 'Z')) {
|
||||
if (w->flags & WINDOW_ZOOMED)
|
||||
@@ -95,33 +93,31 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
server_unzoom_window(w);
|
||||
|
||||
if (args_count(args) == 0)
|
||||
adjust = 1;
|
||||
else {
|
||||
adjust = strtonum(args_string(args, 0), 1, INT_MAX, &errstr);
|
||||
if (errstr != NULL) {
|
||||
cmdq_error(item, "adjustment %s", errstr);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
if (args_has(args, 'x')) {
|
||||
x = args_percentage(args, 'x', 0, INT_MAX, w->sx, &cause);
|
||||
x = args_percentage(args, 'x', 0, PANE_MAXIMUM, w->sx, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(item, "width %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x);
|
||||
if (window_pane_is_floating(wp)) {
|
||||
if (layout_resize_floating_pane_to(wp, LAYOUT_LEFTRIGHT,
|
||||
x, &cause) != 0) {
|
||||
cmdq_error(item, "size %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
} else
|
||||
layout_resize_pane_to(wp, LAYOUT_LEFTRIGHT, x);
|
||||
}
|
||||
if (args_has(args, 'y')) {
|
||||
y = args_percentage(args, 'y', 0, INT_MAX, w->sy, &cause);
|
||||
y = args_percentage(args, 'y', 0, PANE_MAXIMUM, w->sy, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(item, "height %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
status = options_get_number(w->options, "pane-border-status");
|
||||
status = window_get_pane_status(w);
|
||||
switch (status) {
|
||||
case PANE_STATUS_TOP:
|
||||
if (y != INT_MAX && wp->yoff == 1)
|
||||
@@ -132,29 +128,236 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
y++;
|
||||
break;
|
||||
}
|
||||
layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y);
|
||||
if (window_pane_is_floating(wp)) {
|
||||
if (layout_resize_floating_pane_to(wp, LAYOUT_TOPBOTTOM,
|
||||
y, &cause) != 0) {
|
||||
cmdq_error(item, "size %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
} else
|
||||
layout_resize_pane_to(wp, LAYOUT_TOPBOTTOM, y);
|
||||
}
|
||||
|
||||
if (args_has(args, 'L'))
|
||||
layout_resize_pane(wp, LAYOUT_LEFTRIGHT, -adjust, 1);
|
||||
else if (args_has(args, 'R'))
|
||||
layout_resize_pane(wp, LAYOUT_LEFTRIGHT, adjust, 1);
|
||||
else if (args_has(args, 'U'))
|
||||
layout_resize_pane(wp, LAYOUT_TOPBOTTOM, -adjust, 1);
|
||||
else if (args_has(args, 'D'))
|
||||
layout_resize_pane(wp, LAYOUT_TOPBOTTOM, adjust, 1);
|
||||
server_redraw_window(wl->window);
|
||||
for (i = 0; i < nitems(flags); i++) {
|
||||
flag = flags[i];
|
||||
if (!args_has(args, flag))
|
||||
continue;
|
||||
|
||||
argval = args_get(args, flag);
|
||||
if (argval == NULL) {
|
||||
if (args_count(args) == 0)
|
||||
argval = "1";
|
||||
else
|
||||
argval = args_string(args, 0);
|
||||
}
|
||||
|
||||
adjust = strtonum(argval, INT_MIN, INT_MAX, &errstr);
|
||||
if (errstr != NULL) {
|
||||
cmdq_error(item, "adjustment %s", errstr);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
type = LAYOUT_TOPBOTTOM;
|
||||
if (flag == 'L' || flag == 'R')
|
||||
type = LAYOUT_LEFTRIGHT;
|
||||
|
||||
if (window_pane_is_floating(wp)) {
|
||||
if (flag == 'L' || flag == 'U')
|
||||
opposite = 1;
|
||||
|
||||
if (layout_resize_floating_pane(wp, type, adjust,
|
||||
opposite, &cause) != 0) {
|
||||
cmdq_error(item, "adjustment %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
} else {
|
||||
if (flag == 'L' || flag == 'U')
|
||||
adjust = -adjust;
|
||||
layout_resize_pane(wp, type, adjust, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (lc->parent != NULL)
|
||||
layout_fix_offsets(w);
|
||||
layout_fix_panes(w, NULL);
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_resize_pane_mouse_update(__unused struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct key_event *event = cmdq_get_event(item);
|
||||
struct window_pane *wp = target->wp;
|
||||
struct winlink *wl = target->wl;
|
||||
struct window *w = wl->window;
|
||||
struct client *c = cmdq_get_client(item);
|
||||
struct session *s = target->s;
|
||||
|
||||
if (!event->m.valid)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
wp = cmd_mouse_pane(&event->m, &s, NULL);
|
||||
if (wp == NULL || c == NULL || c->session != s)
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
if (!window_pane_is_floating(wp)) {
|
||||
c->tty.mouse_drag_update = cmd_resize_pane_mouse_resize_tiled;
|
||||
cmd_resize_pane_mouse_resize_tiled(c, &event->m);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
window_redraw_active_switch(w, wp);
|
||||
window_set_active_pane(w, wp, 1);
|
||||
|
||||
c->tty.mouse_drag_update = cmd_resize_pane_mouse_resize_move_floating;
|
||||
cmd_resize_pane_mouse_resize_move_floating(c, &event->m);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Resizes or moves the pane by dragging. Resize a floating pane by dragging
|
||||
* the borders or corners. Grabbing an edge only resizes that axis (special
|
||||
* case). Moves the pane if dragging the top border. Since characters are
|
||||
* generally rectangular, to make it easier to grab the corner, the character
|
||||
* next to the corner is also considered the corner.
|
||||
*/
|
||||
static void
|
||||
cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m)
|
||||
cmd_resize_pane_mouse_resize_move_floating(struct client *c,
|
||||
struct mouse_event *m)
|
||||
{
|
||||
struct winlink *wl;
|
||||
struct window *w;
|
||||
struct window_pane *wp;
|
||||
struct layout_cell *lc;
|
||||
int y, ly, x, lx, sx, sy, new_sx, new_sy;
|
||||
int left, right;
|
||||
int new_xoff, new_yoff, resizes = 0;
|
||||
|
||||
wp = cmd_mouse_pane(m, NULL, &wl);
|
||||
if (wp == NULL) {
|
||||
c->tty.mouse_drag_update = NULL;
|
||||
return;
|
||||
}
|
||||
w = wl->window;
|
||||
lc = wp->layout_cell;
|
||||
sx = wp->sx;
|
||||
sy = wp->sy;
|
||||
left = wp->xoff - 1;
|
||||
right = wp->xoff + sx;
|
||||
if (window_pane_scrollbar_reserve(wp) &&
|
||||
w->sb_pos == PANE_SCROLLBARS_LEFT) {
|
||||
left -= wp->scrollbar_style.width + wp->scrollbar_style.pad;
|
||||
} else if (window_pane_scrollbar_reserve(wp) &&
|
||||
w->sb_pos == PANE_SCROLLBARS_RIGHT) {
|
||||
right += wp->scrollbar_style.width + wp->scrollbar_style.pad;
|
||||
}
|
||||
|
||||
y = m->y + m->oy; x = m->x + m->ox;
|
||||
if (m->statusat == 0 && y >= (int)m->statuslines)
|
||||
y -= m->statuslines;
|
||||
else if (m->statusat > 0 && y >= m->statusat)
|
||||
y = m->statusat - 1;
|
||||
ly = m->ly + m->oy; lx = m->lx + m->ox;
|
||||
if (m->statusat == 0 && ly >= (int)m->statuslines)
|
||||
ly -= m->statuslines;
|
||||
else if (m->statusat > 0 && ly >= m->statusat)
|
||||
ly = m->statusat - 1;
|
||||
|
||||
if ((lx == left || lx == left + 1) && ly == wp->yoff - 1) {
|
||||
/* Top left corner. */
|
||||
new_sx = lc->sx + (lx - x);
|
||||
if (new_sx < PANE_MINIMUM)
|
||||
new_sx = PANE_MINIMUM;
|
||||
new_sy = lc->sy + (ly - y);
|
||||
if (new_sy < PANE_MINIMUM)
|
||||
new_sy = PANE_MINIMUM;
|
||||
new_xoff = x + 1; /* because mouse is on border at xoff - 1 */
|
||||
new_yoff = y + 1;
|
||||
layout_set_size(lc, new_sx, new_sy, new_xoff, new_yoff);
|
||||
resizes++;
|
||||
} else if ((lx == right + 1 || lx == right) &&
|
||||
ly == wp->yoff - 1) {
|
||||
/* Top right corner. */
|
||||
new_sx = x - lc->xoff;
|
||||
if (new_sx < PANE_MINIMUM)
|
||||
new_sx = PANE_MINIMUM;
|
||||
new_sy = lc->sy + (ly - y);
|
||||
if (new_sy < PANE_MINIMUM)
|
||||
new_sy = PANE_MINIMUM;
|
||||
new_yoff = y + 1;
|
||||
layout_set_size(lc, new_sx, new_sy, lc->xoff, new_yoff);
|
||||
resizes++;
|
||||
} else if ((lx == left || lx == left + 1) &&
|
||||
ly == wp->yoff + sy) {
|
||||
/* Bottom left corner. */
|
||||
new_sx = lc->sx + (lx - x);
|
||||
if (new_sx < PANE_MINIMUM)
|
||||
new_sx = PANE_MINIMUM;
|
||||
new_sy = y - lc->yoff;
|
||||
if (new_sy < PANE_MINIMUM)
|
||||
return;
|
||||
new_xoff = x + 1;
|
||||
layout_set_size(lc, new_sx, new_sy, new_xoff, lc->yoff);
|
||||
resizes++;
|
||||
} else if ((lx == right + 1 || lx == right) &&
|
||||
ly == wp->yoff + sy) {
|
||||
/* Bottom right corner. */
|
||||
new_sx = x - lc->xoff;
|
||||
if (new_sx < PANE_MINIMUM)
|
||||
new_sx = PANE_MINIMUM;
|
||||
new_sy = y - lc->yoff;
|
||||
if (new_sy < PANE_MINIMUM)
|
||||
new_sy = PANE_MINIMUM;
|
||||
layout_set_size(lc, new_sx, new_sy, lc->xoff, lc->yoff);
|
||||
resizes++;
|
||||
} else if (lx == right) {
|
||||
/* Right border. */
|
||||
new_sx = x - lc->xoff;
|
||||
if (new_sx < PANE_MINIMUM)
|
||||
return;
|
||||
layout_set_size(lc, new_sx, lc->sy, lc->xoff, lc->yoff);
|
||||
resizes++;
|
||||
} else if (lx == left) {
|
||||
/* Left border. */
|
||||
new_sx = lc->sx + (lx - x);
|
||||
if (new_sx < PANE_MINIMUM)
|
||||
return;
|
||||
new_xoff = x + 1;
|
||||
layout_set_size(lc, new_sx, lc->sy, new_xoff, lc->yoff);
|
||||
resizes++;
|
||||
} else if (ly == wp->yoff + sy) {
|
||||
/* Bottom border. */
|
||||
new_sy = y - lc->yoff;
|
||||
if (new_sy < PANE_MINIMUM)
|
||||
return;
|
||||
layout_set_size(lc, lc->sx, new_sy, lc->xoff, lc->yoff);
|
||||
resizes++;
|
||||
} else if (ly == wp->yoff - 1) {
|
||||
/* Top border (move instead of resize). */
|
||||
new_xoff = lc->xoff + (x - lx);
|
||||
new_yoff = y + 1;
|
||||
layout_set_size(lc, lc->sx, lc->sy, new_xoff, new_yoff);
|
||||
resizes++;
|
||||
}
|
||||
if (resizes != 0) {
|
||||
layout_fix_panes(w, NULL);
|
||||
server_redraw_window(w);
|
||||
server_redraw_window_borders(w);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cmd_resize_pane_mouse_resize_tiled(struct client *c, struct mouse_event *m)
|
||||
{
|
||||
struct winlink *wl;
|
||||
struct window *w;
|
||||
u_int y, ly, x, lx;
|
||||
static const int offsets[][2] = {
|
||||
static const int offsets[][2] = {
|
||||
{ 0, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 },
|
||||
};
|
||||
struct layout_cell *cells[nitems(offsets)], *lc;
|
||||
|
||||
@@ -109,6 +109,7 @@ cmd_rotate_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
window_set_active_pane(w, wp, 1);
|
||||
cmd_find_from_winlink_pane(current, wl, wp, 0);
|
||||
window_pop_zoom(w);
|
||||
redraw_invalidate_scene(w);
|
||||
server_redraw_window(w);
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
@@ -44,9 +44,9 @@ const struct cmd_entry cmd_run_shell_entry = {
|
||||
.name = "run-shell",
|
||||
.alias = "run",
|
||||
|
||||
.args = { "bd:Ct:Es:c:", 0, 1, cmd_run_shell_args_parse },
|
||||
.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]",
|
||||
" [shell-command [argument ...]]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
|
||||
|
||||
@@ -115,9 +115,11 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct session *s = target->s;
|
||||
struct window_pane *wp = target->wp;
|
||||
const char *delay, *cmd;
|
||||
struct format_tree *ft;
|
||||
double d;
|
||||
struct timeval tv;
|
||||
char *end;
|
||||
char *end, key[16];
|
||||
u_int i;
|
||||
int wait = !args_has(args, 'b');
|
||||
|
||||
if ((delay = args_get(args, 'd')) != NULL) {
|
||||
@@ -132,8 +134,15 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
|
||||
cdata = xcalloc(1, sizeof *cdata);
|
||||
if (!args_has(args, 'C')) {
|
||||
cmd = args_string(args, 0);
|
||||
if (cmd != NULL)
|
||||
cdata->cmd = format_single_from_target(item, cmd);
|
||||
if (cmd != NULL) {
|
||||
ft = format_create_from_target(item);
|
||||
for (i = 1; i < args_count(args); i++) {
|
||||
xsnprintf(key, sizeof key, "%u", i);
|
||||
format_add(ft, key, "%s", args_string(args, i));
|
||||
}
|
||||
cdata->cmd = format_expand(ft, cmd);
|
||||
format_free(ft);
|
||||
}
|
||||
} else {
|
||||
cdata->state = args_make_commands_prepare(self, item, 0, NULL,
|
||||
wait, 1);
|
||||
@@ -198,8 +207,17 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg)
|
||||
}
|
||||
if (job_run(cmd, 0, NULL, NULL, cdata->s, cdata->cwd, NULL,
|
||||
cmd_run_shell_callback, cmd_run_shell_free, cdata,
|
||||
cdata->flags, -1, -1) == NULL)
|
||||
cdata->flags, -1, -1) == NULL) {
|
||||
if (cdata->item == NULL)
|
||||
status_message_set(c, -1, 1, 0, 0,
|
||||
"failed to run command: %s", cmd);
|
||||
else {
|
||||
cmdq_error(cdata->item,
|
||||
"failed to run command: %s", cmd);
|
||||
cmdq_continue(cdata->item);
|
||||
}
|
||||
cmd_run_shell_free(cdata);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ cmd_select_layout_exec(struct cmd *self, struct cmdq_item *item)
|
||||
previous = 1;
|
||||
|
||||
oldlayout = w->old_layout;
|
||||
w->old_layout = layout_dump(w->layout_root);
|
||||
w->old_layout = layout_dump(w, w->layout_root);
|
||||
|
||||
if (next || previous) {
|
||||
if (next)
|
||||
|
||||
@@ -103,7 +103,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
* spawned without being visited (for example split-window -d).
|
||||
*/
|
||||
lastwp = TAILQ_FIRST(&w->last_panes);
|
||||
if (lastwp == NULL && window_count_panes(w) == 2) {
|
||||
if (lastwp == NULL && window_count_panes(w, 1) == 2) {
|
||||
lastwp = TAILQ_PREV(w->active, window_panes, entry);
|
||||
if (lastwp == NULL)
|
||||
lastwp = TAILQ_NEXT(w->active, entry);
|
||||
@@ -135,7 +135,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
|
||||
if (args_has(args, 'm') || args_has(args, 'M')) {
|
||||
if (args_has(args, 'm') && !window_pane_visible(wp))
|
||||
if (args_has(args, 'm') && !window_pane_is_visible(wp))
|
||||
return (CMD_RETURN_NORMAL);
|
||||
if (server_check_marked())
|
||||
lastwp = marked_pane.wp;
|
||||
@@ -160,6 +160,10 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
server_redraw_window_borders(markedwp->window);
|
||||
server_status_window(markedwp->window);
|
||||
}
|
||||
if (window_pane_is_floating(wp)) {
|
||||
window_redraw_active_switch(w, wp);
|
||||
window_set_active_pane(w, wp, 1);
|
||||
}
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
@@ -213,7 +217,7 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
|
||||
if (args_has(args, 'T')) {
|
||||
title = format_single_from_target(item, args_get(args, 'T'));
|
||||
if (screen_set_title(&wp->base, title)) {
|
||||
if (screen_set_title(&wp->base, title, 0)) {
|
||||
notify_pane("pane-title-changed", wp);
|
||||
server_redraw_window_borders(wp->window);
|
||||
server_status_window(wp->window);
|
||||
|
||||
@@ -70,6 +70,7 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
|
||||
struct key_table *table = NULL;
|
||||
struct key_binding *bd;
|
||||
struct key_event *event;
|
||||
struct cmdq_item *new_after = after;
|
||||
|
||||
if (args_has(args, 'K')) {
|
||||
if (tc == NULL)
|
||||
@@ -77,10 +78,16 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
|
||||
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);
|
||||
if (after == NULL) {
|
||||
if (server_client_handle_key(tc, event) != 0)
|
||||
return (item);
|
||||
} else {
|
||||
if (server_client_handle_key_after(tc, event, after,
|
||||
&new_after) != 0)
|
||||
return (new_after);
|
||||
}
|
||||
free(event->buf);
|
||||
free(event);
|
||||
return (item);
|
||||
}
|
||||
|
||||
@@ -229,8 +236,10 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if (count == 0) {
|
||||
if (args_has(args, 'N') || args_has(args, 'R'))
|
||||
return (CMD_RETURN_NORMAL);
|
||||
after = args_has(args, 'K') ? item : NULL;
|
||||
for (; np != 0; np--)
|
||||
cmd_send_keys_inject_key(item, NULL, args, event->key);
|
||||
after = cmd_send_keys_inject_key(item, after, args,
|
||||
event->key);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -37,67 +38,71 @@ const struct cmd_entry cmd_server_access_entry = {
|
||||
.name = "server-access",
|
||||
.alias = NULL,
|
||||
|
||||
.args = { "adlrw", 0, 1, NULL },
|
||||
.usage = "[-adlrw] " CMD_TARGET_PANE_USAGE " [user]",
|
||||
.args = { "adglrw", 0, 1, NULL },
|
||||
.usage = "[-adglrw] " CMD_TARGET_PANE_USAGE " [user|group]",
|
||||
|
||||
.flags = CMD_CLIENT_CANFAIL,
|
||||
.exec = cmd_server_access_exec
|
||||
};
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_server_access_deny(struct cmdq_item *item, struct passwd *pw)
|
||||
cmd_server_access_deny(struct cmdq_item *item, id_t id, int flags,
|
||||
const char *type, const char *name)
|
||||
{
|
||||
struct client *loop;
|
||||
struct server_acl_user *user;
|
||||
uid_t uid;
|
||||
|
||||
if ((user = server_acl_user_find(pw->pw_uid)) == NULL) {
|
||||
cmdq_error(item, "user %s not found", pw->pw_name);
|
||||
if (!server_acl_find(id, flags)) {
|
||||
cmdq_error(item, "%s %s not found", type, name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
TAILQ_FOREACH(loop, &clients, entry) {
|
||||
uid = proc_get_peer_uid(loop->peer);
|
||||
if (uid == server_acl_get_uid(user)) {
|
||||
loop->exit_message = xstrdup("access not allowed");
|
||||
loop->flags |= CLIENT_EXIT;
|
||||
}
|
||||
}
|
||||
server_acl_user_deny(pw->pw_uid);
|
||||
|
||||
server_acl_deny(id, flags);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
cmd_server_access_exec(struct cmd *self, struct cmdq_item *item)
|
||||
{
|
||||
|
||||
struct args *args = cmd_get_args(self);
|
||||
struct client *c = cmdq_get_target_client(item);
|
||||
char *name;
|
||||
struct passwd *pw = NULL;
|
||||
char *arg;
|
||||
const char *name = NULL, *type;
|
||||
struct passwd *pw;
|
||||
struct group *gr;
|
||||
id_t id;
|
||||
int flags = 0;
|
||||
|
||||
if (args_has(args, 'l')) {
|
||||
server_acl_display(item);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
if (args_count(args) == 0) {
|
||||
cmdq_error(item, "missing user argument");
|
||||
cmdq_error(item, "missing user or group argument");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
name = format_single(item, args_string(args, 0), c, NULL, NULL, NULL);
|
||||
if (*name != '\0')
|
||||
pw = getpwnam(name);
|
||||
if (pw == NULL) {
|
||||
cmdq_error(item, "unknown user: %s", name);
|
||||
free(name);
|
||||
arg = format_single(item, args_string(args, 0), c, NULL, NULL, NULL);
|
||||
if (args_has(args, 'g')) {
|
||||
type = "group";
|
||||
if ((gr = getgrnam(arg)) != NULL) {
|
||||
id = gr->gr_gid;
|
||||
name = gr->gr_name;
|
||||
flags |= SERVER_ACL_IS_GROUP;
|
||||
}
|
||||
} else {
|
||||
type = "user";
|
||||
if ((pw = getpwnam(arg)) != NULL) {
|
||||
id = pw->pw_uid;
|
||||
name = pw->pw_name;
|
||||
}
|
||||
}
|
||||
if (name == NULL) {
|
||||
cmdq_error(item, "unknown %s: %s", type, arg);
|
||||
free(arg);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
free(name);
|
||||
free(arg);
|
||||
|
||||
if (pw->pw_uid == 0 || pw->pw_uid == getuid()) {
|
||||
if ((~flags & SERVER_ACL_IS_GROUP) && (id == 0 || id == getuid())) {
|
||||
cmdq_error(item, "%s owns the server, can't change access",
|
||||
pw->pw_name);
|
||||
name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
@@ -111,36 +116,35 @@ cmd_server_access_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
|
||||
if (args_has(args, 'd'))
|
||||
return (cmd_server_access_deny(item, pw));
|
||||
return (cmd_server_access_deny(item, id, flags, type, name));
|
||||
if (args_has(args, 'a')) {
|
||||
if (server_acl_user_find(pw->pw_uid) != NULL) {
|
||||
cmdq_error(item, "user %s is already added",
|
||||
pw->pw_name);
|
||||
if (server_acl_find(id, flags)) {
|
||||
cmdq_error(item, "%s %s is already added", type, name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
server_acl_user_allow(pw->pw_uid);
|
||||
server_acl_allow(id, flags);
|
||||
/* Do not return - allow -r or -w with -a. */
|
||||
} else if (args_has(args, 'r') || args_has(args, 'w')) {
|
||||
/* -r or -w implies -a if user does not exist. */
|
||||
if (server_acl_user_find(pw->pw_uid) == NULL)
|
||||
server_acl_user_allow(pw->pw_uid);
|
||||
/* -r or -w implies -a if the entry does not exist. */
|
||||
if (!server_acl_find(id, flags))
|
||||
server_acl_allow(id, flags);
|
||||
}
|
||||
|
||||
if (args_has(args, 'w')) {
|
||||
if (server_acl_user_find(pw->pw_uid) == NULL) {
|
||||
cmdq_error(item, "user %s not found", pw->pw_name);
|
||||
if (!server_acl_find(id, flags)) {
|
||||
cmdq_error(item, "%s %s not found", type, name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
server_acl_user_allow_write(pw->pw_uid);
|
||||
server_acl_allow_write(id, flags);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
if (args_has(args, 'r')) {
|
||||
if (server_acl_user_find(pw->pw_uid) == NULL) {
|
||||
cmdq_error(item, "user %s not found", pw->pw_name);
|
||||
if (!server_acl_find(id, flags)) {
|
||||
cmdq_error(item, "%s %s not found", type, name);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
server_acl_user_deny_write(pw->pw_uid);
|
||||
server_acl_deny_write(id, flags);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,56 +55,44 @@ cmd_show_prompt_history_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct args *args = cmd_get_args(self);
|
||||
const char *typestr = args_get(args, 'T');
|
||||
enum prompt_type type;
|
||||
u_int tidx, hidx;
|
||||
u_int t, h;
|
||||
const char *v;
|
||||
|
||||
if (cmd_get_entry(self) == &cmd_clear_prompt_history_entry) {
|
||||
if (typestr == NULL) {
|
||||
for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) {
|
||||
for (hidx = 0; hidx < status_prompt_hsize[tidx];
|
||||
hidx++)
|
||||
free(status_prompt_hlist[tidx][hidx]);
|
||||
free(status_prompt_hlist[tidx]);
|
||||
status_prompt_hlist[tidx] = NULL;
|
||||
status_prompt_hsize[tidx] = 0;
|
||||
}
|
||||
for (t = 0; t < PROMPT_NTYPES; t++)
|
||||
prompt_history_clear(t);
|
||||
} else {
|
||||
type = status_prompt_type(typestr);
|
||||
type = prompt_type(typestr);
|
||||
if (type == PROMPT_TYPE_INVALID) {
|
||||
cmdq_error(item, "invalid type: %s", typestr);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
for (hidx = 0; hidx < status_prompt_hsize[type]; hidx++)
|
||||
free(status_prompt_hlist[type][hidx]);
|
||||
free(status_prompt_hlist[type]);
|
||||
status_prompt_hlist[type] = NULL;
|
||||
status_prompt_hsize[type] = 0;
|
||||
prompt_history_clear(type);
|
||||
}
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
if (typestr == NULL) {
|
||||
for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) {
|
||||
cmdq_print(item, "History for %s:\n",
|
||||
status_prompt_type_string(tidx));
|
||||
for (hidx = 0; hidx < status_prompt_hsize[tidx];
|
||||
hidx++) {
|
||||
cmdq_print(item, "%d: %s", hidx + 1,
|
||||
status_prompt_hlist[tidx][hidx]);
|
||||
for (t = 0; t < PROMPT_NTYPES; t++) {
|
||||
typestr = prompt_type_string(t);
|
||||
cmdq_print(item, "History for %s:\n", typestr);
|
||||
for (h = 0; h < prompt_history_size(t); h++) {
|
||||
v = prompt_history_get(t, h);
|
||||
cmdq_print(item, "%d: %s", h + 1, v);
|
||||
}
|
||||
cmdq_print(item, "%s", "");
|
||||
}
|
||||
} else {
|
||||
type = status_prompt_type(typestr);
|
||||
type = prompt_type(typestr);
|
||||
if (type == PROMPT_TYPE_INVALID) {
|
||||
cmdq_error(item, "invalid type: %s", typestr);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
cmdq_print(item, "History for %s:\n",
|
||||
status_prompt_type_string(type));
|
||||
for (hidx = 0; hidx < status_prompt_hsize[type]; hidx++) {
|
||||
cmdq_print(item, "%d: %s", hidx + 1,
|
||||
status_prompt_hlist[type][hidx]);
|
||||
cmdq_print(item, "History for %s:\n", prompt_type_string(type));
|
||||
for (h = 0; h < prompt_history_size(type); h++) {
|
||||
v = prompt_history_get(type, h);
|
||||
cmdq_print(item, "%d: %s", h + 1, v);
|
||||
}
|
||||
cmdq_print(item, "%s", "");
|
||||
}
|
||||
|
||||
@@ -27,22 +27,42 @@
|
||||
#include "tmux.h"
|
||||
|
||||
/*
|
||||
* Split a window (add a new pane).
|
||||
* Create a new pane.
|
||||
*/
|
||||
|
||||
#define SPLIT_WINDOW_TEMPLATE "#{session_name}:#{window_index}.#{pane_index}"
|
||||
|
||||
static enum cmd_retval cmd_split_window_exec(struct cmd *,
|
||||
struct cmdq_item *);
|
||||
static enum cmd_retval cmd_split_window_exec(struct cmd *, struct cmdq_item *);
|
||||
|
||||
const struct cmd_entry cmd_new_pane_entry = {
|
||||
.name = "new-pane",
|
||||
.alias = "newp",
|
||||
|
||||
.args = { "bB:c:de:EfF:hIkl:Lm:p:PR:s:S:t:T:vWx:X:y:Y:Z", 0, -1, NULL },
|
||||
.usage = "[-bdefhIklPvWZ] [-B border-lines] "
|
||||
"[-c start-directory] [-e environment] "
|
||||
"[-F format] [-l size] [-m message] [-p percentage] "
|
||||
"[-s style] [-S active-border-style] "
|
||||
"[-R inactive-border-style] [-T title] [-x width] [-y height] "
|
||||
"[-X x-position] [-Y y-position] " CMD_TARGET_PANE_USAGE " "
|
||||
"[shell-command [argument ...]]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
.flags = 0,
|
||||
.exec = cmd_split_window_exec
|
||||
};
|
||||
|
||||
const struct cmd_entry cmd_split_window_entry = {
|
||||
.name = "split-window",
|
||||
.alias = "splitw",
|
||||
|
||||
.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 [argument ...]]",
|
||||
.args = { "bc:de:EfF:hIkl:m:p:PR:s:S:t:T:vWZ", 0, -1, NULL },
|
||||
.usage = "[-bdefhIklPvWZ] [-c start-directory] [-e environment] "
|
||||
"[-F format] [-l size] [-m message] [-p percentage] "
|
||||
"[-s style] [-S active-border-style] "
|
||||
"[-R inactive-border-style] [-T title] " CMD_TARGET_PANE_USAGE " "
|
||||
"[shell-command [argument ...]]",
|
||||
|
||||
.target = { 't', CMD_FIND_PANE, 0 },
|
||||
|
||||
@@ -62,64 +82,60 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
struct winlink *wl = target->wl;
|
||||
struct window *w = wl->window;
|
||||
struct window_pane *wp = target->wp, *new_wp;
|
||||
enum layout_type type;
|
||||
struct layout_cell *lc;
|
||||
struct layout_cell *lc = NULL;
|
||||
struct cmd_find_state fs;
|
||||
int size, flags, input;
|
||||
const char *template;
|
||||
char *cause = NULL, *cp;
|
||||
int input, empty, is_floating, flags = 0;
|
||||
const char *template, *style, *value;
|
||||
char *cause = NULL, *cp, *title;
|
||||
const struct options_table_entry *oe;
|
||||
struct args_value *av;
|
||||
u_int count = args_count(args), curval = 0;
|
||||
enum pane_lines lines;
|
||||
u_int count = args_count(args);
|
||||
|
||||
type = LAYOUT_TOPBOTTOM;
|
||||
if (args_has(args, 'h'))
|
||||
type = LAYOUT_LEFTRIGHT;
|
||||
if (cmd_get_entry(self) == &cmd_new_pane_entry)
|
||||
is_floating = !args_has(args, 'L');
|
||||
else
|
||||
is_floating = window_pane_is_floating(wp);
|
||||
|
||||
/* 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 = w->sy;
|
||||
else
|
||||
curval = w->sx;
|
||||
} else {
|
||||
if (type == LAYOUT_TOPBOTTOM)
|
||||
curval = wp->sy;
|
||||
else
|
||||
curval = wp->sx;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
flags = 0;
|
||||
flags = is_floating ? SPAWN_FLOATING : 0;
|
||||
if (args_has(args, 'b'))
|
||||
flags |= SPAWN_BEFORE;
|
||||
if (args_has(args, 'f'))
|
||||
flags |= SPAWN_FULLSIZE;
|
||||
if (input || (count == 1 && *args_string(args, 0) == '\0'))
|
||||
|
||||
input = args_has(args, 'I');
|
||||
if (input)
|
||||
empty = 1;
|
||||
else
|
||||
empty = args_has(args, 'E');
|
||||
if (empty &&
|
||||
count != 0 &&
|
||||
(count != 1 || *args_string(args, 0) != '\0')) {
|
||||
cmdq_error(item, "command cannot be given for empty pane");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (empty)
|
||||
flags |= SPAWN_EMPTY;
|
||||
|
||||
lc = layout_split_pane(wp, type, size, flags);
|
||||
if (lc == NULL) {
|
||||
cmdq_error(item, "no space for new pane");
|
||||
if ((value = args_get(args, 'B')) == NULL)
|
||||
lines = window_get_pane_lines(w);
|
||||
else {
|
||||
oe = options_search("pane-border-lines");
|
||||
lines = options_find_choice(oe, value, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(item, "pane-border-lines %s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_floating)
|
||||
lc = layout_get_floating_cell(item, args, lines, w, wp, &cause);
|
||||
else
|
||||
lc = layout_get_tiled_cell(item, args, w, wp, flags, &cause);
|
||||
if (cause != NULL) {
|
||||
cmdq_error(item, "%s", cause);
|
||||
free(cause);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
@@ -151,23 +167,65 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
if ((new_wp = spawn_pane(&sc, &cause)) == NULL) {
|
||||
cmdq_error(item, "create pane failed: %s", cause);
|
||||
free(cause);
|
||||
if (sc.argv != NULL)
|
||||
cmd_free_argv(sc.argc, sc.argv);
|
||||
environ_free(sc.environ);
|
||||
return (CMD_RETURN_ERROR);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
style = args_get(args, 's');
|
||||
if (style != NULL) {
|
||||
if (options_set_string(new_wp->options, "window-style", 0,
|
||||
"%s", style) == NULL) {
|
||||
cmdq_error(item, "bad style: %s", style);
|
||||
goto fail;
|
||||
}
|
||||
options_set_string(new_wp->options, "window-active-style", 0,
|
||||
"%s", style);
|
||||
new_wp->flags |= (PANE_REDRAW|PANE_STYLECHANGED|
|
||||
PANE_THEMECHANGED);
|
||||
}
|
||||
style = args_get(args, 'S');
|
||||
if (style != NULL) {
|
||||
if (options_set_string(new_wp->options,
|
||||
"pane-active-border-style", 0, "%s", style) == NULL) {
|
||||
cmdq_error(item, "bad active border style: %s", style);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
style = args_get(args, 'R');
|
||||
if (style != NULL) {
|
||||
if (options_set_string(new_wp->options, "pane-border-style", 0,
|
||||
"%s", style) == NULL) {
|
||||
cmdq_error(item, "bad inactive border style: %s",
|
||||
style);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (args_has(args, 'B'))
|
||||
options_set_number(new_wp->options, "pane-border-lines", lines);
|
||||
if (args_has(args, 'k') || args_has(args, 'm')) {
|
||||
options_set_number(new_wp->options, "remain-on-exit", 3);
|
||||
if (args_has(args, 'm')) {
|
||||
options_set_string(new_wp->options,
|
||||
"remain-on-exit-format", 0, "%s",
|
||||
args_get(args, 'm'));
|
||||
}
|
||||
}
|
||||
if (args_has(args, 'T')) {
|
||||
title = format_single_from_target(item, args_get(args, 'T'));
|
||||
screen_set_title(&new_wp->base, title, 0);
|
||||
notify_pane("pane-title-changed", new_wp);
|
||||
free(title);
|
||||
}
|
||||
|
||||
if (input) {
|
||||
switch (window_pane_start_input(new_wp, item, &cause)) {
|
||||
case -1:
|
||||
server_client_remove_pane(new_wp);
|
||||
layout_close_pane(new_wp);
|
||||
if (!is_floating)
|
||||
layout_close_pane(new_wp);
|
||||
window_remove_pane(wp->window, new_wp);
|
||||
cmdq_error(item, "%s", cause);
|
||||
free(cause);
|
||||
if (sc.argv != NULL)
|
||||
cmd_free_argv(sc.argc, sc.argv);
|
||||
environ_free(sc.environ);
|
||||
return (CMD_RETURN_ERROR);
|
||||
goto fail;
|
||||
case 1:
|
||||
input = 0;
|
||||
break;
|
||||
@@ -175,9 +233,12 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
}
|
||||
if (!args_has(args, 'd'))
|
||||
cmd_find_from_winlink_pane(current, wl, new_wp, 0);
|
||||
window_pop_zoom(wp->window);
|
||||
server_redraw_window(wp->window);
|
||||
server_status_session(s);
|
||||
|
||||
if (!is_floating) {
|
||||
window_pop_zoom(wp->window);
|
||||
server_redraw_window(wp->window);
|
||||
}
|
||||
server_redraw_session(s);
|
||||
|
||||
if (args_has(args, 'P')) {
|
||||
if ((template = args_get(args, 'F')) == NULL)
|
||||
@@ -195,5 +256,24 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
|
||||
environ_free(sc.environ);
|
||||
if (input)
|
||||
return (CMD_RETURN_WAIT);
|
||||
|
||||
if (args_has(args, 'W')) {
|
||||
/*
|
||||
* With -W, block this command queue item until the pane's
|
||||
* command exits; window_pane_wait_finish will be called to
|
||||
* continue it.
|
||||
*/
|
||||
new_wp->wait_item = item;
|
||||
return (CMD_RETURN_WAIT);
|
||||
}
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
||||
fail:
|
||||
if (sc.argv != NULL)
|
||||
cmd_free_argv(sc.argc, sc.argv);
|
||||
environ_free(sc.environ);
|
||||
layout_destroy_cell(w, lc, &w->layout_root);
|
||||
|
||||
return (CMD_RETURN_ERROR);
|
||||
|
||||
}
|
||||
|
||||
@@ -92,6 +92,16 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
else
|
||||
TAILQ_INSERT_AFTER(&dst_w->panes, tmp_wp, src_wp, entry);
|
||||
|
||||
tmp_wp = TAILQ_PREV(dst_wp, window_panes, zentry);
|
||||
TAILQ_REMOVE(&dst_w->z_index, dst_wp, zentry);
|
||||
TAILQ_REPLACE(&src_w->z_index, src_wp, dst_wp, zentry);
|
||||
if (tmp_wp == src_wp)
|
||||
tmp_wp = dst_wp;
|
||||
if (tmp_wp == NULL)
|
||||
TAILQ_INSERT_HEAD(&dst_w->z_index, src_wp, zentry);
|
||||
else
|
||||
TAILQ_INSERT_AFTER(&dst_w->z_index, tmp_wp, src_wp, zentry);
|
||||
|
||||
src_lc = src_wp->layout_cell;
|
||||
dst_lc = dst_wp->layout_cell;
|
||||
src_lc->wp = dst_wp;
|
||||
@@ -133,9 +143,11 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
|
||||
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);
|
||||
redraw_invalidate_scene(src_w);
|
||||
server_redraw_window(src_w);
|
||||
}
|
||||
layout_fix_panes(dst_w, NULL);
|
||||
redraw_invalidate_scene(dst_w);
|
||||
server_redraw_window(dst_w);
|
||||
|
||||
notify_window("window-layout-changed", src_w);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
@@ -53,6 +54,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
|
||||
const char *tflag = args_get(args, 't');
|
||||
enum cmd_find_type type;
|
||||
int flags;
|
||||
struct client *c = cmdq_get_client(item);
|
||||
struct client *tc = cmdq_get_target_client(item);
|
||||
struct session *s;
|
||||
struct winlink *wl;
|
||||
@@ -61,6 +63,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
|
||||
const char *tablename;
|
||||
struct key_table *table;
|
||||
struct sort_criteria sort_crit;
|
||||
uid_t uid;
|
||||
|
||||
if (tflag != NULL &&
|
||||
(tflag[strcspn(tflag, ":.%")] != '\0' || strcmp(tflag, "=") == 0)) {
|
||||
@@ -77,6 +80,13 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item)
|
||||
wp = target.wp;
|
||||
|
||||
if (args_has(args, 'r')) {
|
||||
if (tc->flags & CLIENT_READONLY) {
|
||||
uid = proc_get_peer_uid(c->peer);
|
||||
if (uid != getuid()) {
|
||||
cmdq_error(item, "client is read-only");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
if (tc->flags & CLIENT_READONLY)
|
||||
tc->flags &= ~(CLIENT_READONLY|CLIENT_IGNORESIZE);
|
||||
else
|
||||
|
||||
14
cmd.c
14
cmd.c
@@ -69,8 +69,10 @@ extern const struct cmd_entry cmd_load_buffer_entry;
|
||||
extern const struct cmd_entry cmd_lock_client_entry;
|
||||
extern const struct cmd_entry cmd_lock_server_entry;
|
||||
extern const struct cmd_entry cmd_lock_session_entry;
|
||||
extern const struct cmd_entry cmd_hide_pane_entry;
|
||||
extern const struct cmd_entry cmd_move_pane_entry;
|
||||
extern const struct cmd_entry cmd_move_window_entry;
|
||||
extern const struct cmd_entry cmd_new_pane_entry;
|
||||
extern const struct cmd_entry cmd_new_session_entry;
|
||||
extern const struct cmd_entry cmd_new_window_entry;
|
||||
extern const struct cmd_entry cmd_next_layout_entry;
|
||||
@@ -105,6 +107,7 @@ extern const struct cmd_entry cmd_show_environment_entry;
|
||||
extern const struct cmd_entry cmd_show_hooks_entry;
|
||||
extern const struct cmd_entry cmd_show_messages_entry;
|
||||
extern const struct cmd_entry cmd_show_options_entry;
|
||||
extern const struct cmd_entry cmd_show_pane_entry;
|
||||
extern const struct cmd_entry cmd_show_prompt_history_entry;
|
||||
extern const struct cmd_entry cmd_show_window_options_entry;
|
||||
extern const struct cmd_entry cmd_source_file_entry;
|
||||
@@ -114,6 +117,7 @@ extern const struct cmd_entry cmd_suspend_client_entry;
|
||||
extern const struct cmd_entry cmd_swap_pane_entry;
|
||||
extern const struct cmd_entry cmd_swap_window_entry;
|
||||
extern const struct cmd_entry cmd_switch_client_entry;
|
||||
extern const struct cmd_entry cmd_switch_mode_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_wait_for_entry;
|
||||
@@ -161,8 +165,10 @@ const struct cmd_entry *cmd_table[] = {
|
||||
&cmd_lock_client_entry,
|
||||
&cmd_lock_server_entry,
|
||||
&cmd_lock_session_entry,
|
||||
&cmd_hide_pane_entry,
|
||||
&cmd_move_pane_entry,
|
||||
&cmd_move_window_entry,
|
||||
&cmd_new_pane_entry,
|
||||
&cmd_new_session_entry,
|
||||
&cmd_new_window_entry,
|
||||
&cmd_next_layout_entry,
|
||||
@@ -197,6 +203,7 @@ const struct cmd_entry *cmd_table[] = {
|
||||
&cmd_show_hooks_entry,
|
||||
&cmd_show_messages_entry,
|
||||
&cmd_show_options_entry,
|
||||
&cmd_show_pane_entry,
|
||||
&cmd_show_prompt_history_entry,
|
||||
&cmd_show_window_options_entry,
|
||||
&cmd_source_file_entry,
|
||||
@@ -206,6 +213,7 @@ const struct cmd_entry *cmd_table[] = {
|
||||
&cmd_swap_pane_entry,
|
||||
&cmd_swap_window_entry,
|
||||
&cmd_switch_client_entry,
|
||||
&cmd_switch_mode_entry,
|
||||
&cmd_unbind_key_entry,
|
||||
&cmd_unlink_window_entry,
|
||||
&cmd_wait_for_entry,
|
||||
@@ -303,6 +311,8 @@ cmd_unpack_argv(char *buf, size_t len, int argc, char ***argv)
|
||||
|
||||
if (argc == 0)
|
||||
return (0);
|
||||
if (argc < 0 || argc > 1000)
|
||||
return (-1);
|
||||
*argv = xcalloc(argc, sizeof **argv);
|
||||
|
||||
buf[len - 1] = '\0';
|
||||
@@ -772,9 +782,9 @@ cmd_mouse_at(struct window_pane *wp, struct mouse_event *m, u_int *xp,
|
||||
if (m->statusat == 0 && y >= m->statuslines)
|
||||
y -= m->statuslines;
|
||||
|
||||
if (x < wp->xoff || x >= wp->xoff + wp->sx)
|
||||
if ((int)x < wp->xoff || (int)x >= wp->xoff + (int)wp->sx)
|
||||
return (-1);
|
||||
if (y < wp->yoff || y >= wp->yoff + wp->sy)
|
||||
if ((int)y < wp->yoff || (int)y >= wp->yoff + (int)wp->sy)
|
||||
return (-1);
|
||||
|
||||
if (xp != NULL)
|
||||
|
||||
166
colour.c
166
colour.c
@@ -26,6 +26,85 @@
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
/* Theme colour slots and their server options. */
|
||||
static const struct {
|
||||
const char *name;
|
||||
const char *dark_option;
|
||||
const char *light_option;
|
||||
int terminal_colour;
|
||||
} colour_theme_table[] = {
|
||||
{ "themeblack",
|
||||
"dark-theme-black",
|
||||
"light-theme-black",
|
||||
0
|
||||
},
|
||||
{ "themewhite",
|
||||
"dark-theme-white",
|
||||
"light-theme-white",
|
||||
7
|
||||
},
|
||||
{ "themelightgrey",
|
||||
"dark-theme-light-grey",
|
||||
"light-theme-light-grey",
|
||||
7
|
||||
},
|
||||
{ "themedarkgrey",
|
||||
"dark-theme-dark-grey",
|
||||
"light-theme-dark-grey",
|
||||
0
|
||||
},
|
||||
{ "themegreen",
|
||||
"dark-theme-green",
|
||||
"light-theme-green",
|
||||
2
|
||||
},
|
||||
{ "themeyellow",
|
||||
"dark-theme-yellow",
|
||||
"light-theme-yellow",
|
||||
3
|
||||
},
|
||||
{ "themered",
|
||||
"dark-theme-red",
|
||||
"light-theme-red",
|
||||
1
|
||||
},
|
||||
{ "themeblue",
|
||||
"dark-theme-blue",
|
||||
"light-theme-blue",
|
||||
4
|
||||
},
|
||||
{ "themecyan",
|
||||
"dark-theme-cyan",
|
||||
"light-theme-cyan",
|
||||
6
|
||||
},
|
||||
{ "thememagenta",
|
||||
"dark-theme-magenta",
|
||||
"light-theme-magenta",
|
||||
5
|
||||
}
|
||||
};
|
||||
|
||||
/* Get theme colour option. */
|
||||
const char *
|
||||
colour_theme_option(u_int n, enum client_theme theme)
|
||||
{
|
||||
if (n >= nitems(colour_theme_table))
|
||||
return (NULL);
|
||||
if (theme == THEME_LIGHT)
|
||||
return (colour_theme_table[n].light_option);
|
||||
return (colour_theme_table[n].dark_option);
|
||||
}
|
||||
|
||||
/* Get theme terminal colour. */
|
||||
int
|
||||
colour_theme_terminal_colour(u_int n)
|
||||
{
|
||||
if (n >= nitems(colour_theme_table))
|
||||
return (8);
|
||||
return (colour_theme_table[n].terminal_colour);
|
||||
}
|
||||
|
||||
static int
|
||||
colour_dist_sq(int R, int G, int B, int r, int g, int b)
|
||||
{
|
||||
@@ -120,6 +199,28 @@ colour_force_rgb(int c)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Dim colour by a percentage. */
|
||||
int
|
||||
colour_dim(int c, u_int dim)
|
||||
{
|
||||
u_char r, g, b;
|
||||
|
||||
if (dim == 0 || COLOUR_DEFAULT(c) || (c & COLOUR_FLAG_THEME))
|
||||
return (c);
|
||||
if (dim >= 100)
|
||||
return (colour_join_rgb(0, 0, 0));
|
||||
|
||||
c = colour_force_rgb(c);
|
||||
if (c == -1)
|
||||
return (-1);
|
||||
colour_split_rgb(c, &r, &g, &b);
|
||||
|
||||
r = (r * (100 - dim)) / 100;
|
||||
g = (g * (100 - dim)) / 100;
|
||||
b = (b * (100 - dim)) / 100;
|
||||
return (colour_join_rgb(r, g, b));
|
||||
}
|
||||
|
||||
/* Convert colour to a string. */
|
||||
const char *
|
||||
colour_tostring(int c)
|
||||
@@ -130,6 +231,13 @@ colour_tostring(int c)
|
||||
if (c == -1)
|
||||
return ("none");
|
||||
|
||||
if (c & COLOUR_FLAG_THEME) {
|
||||
c &= 0xff;
|
||||
if (c >= 0 && (u_int)c < nitems(colour_theme_table))
|
||||
return (colour_theme_table[c].name);
|
||||
return ("invalid");
|
||||
}
|
||||
|
||||
if (c & COLOUR_FLAG_RGB) {
|
||||
colour_split_rgb(c, &r, &g, &b);
|
||||
xsnprintf(s, sizeof s, "#%02x%02x%02x", r, g, b);
|
||||
@@ -182,6 +290,58 @@ colour_tostring(int c)
|
||||
return ("invalid");
|
||||
}
|
||||
|
||||
/* Convert colour to an SGR escape sequence. */
|
||||
const char *
|
||||
colour_toescape(struct client *c, int colour, int bg)
|
||||
{
|
||||
static char s[32];
|
||||
u_char r, g, b;
|
||||
int n, flags = (TERM_256COLOURS|TERM_RGBCOLOURS);
|
||||
u_int o = (bg ? 40 : 30);
|
||||
|
||||
if (c != NULL && (c->tty.flags & TTY_OPENED) && c->tty.term != NULL)
|
||||
flags = c->tty.term->flags;
|
||||
|
||||
if (colour & COLOUR_FLAG_THEME) {
|
||||
n = colour & 0xff;
|
||||
if (c != NULL && (u_int)n < COLOUR_THEME_COUNT)
|
||||
colour = c->theme_colours[n];
|
||||
else
|
||||
colour = colour_theme_terminal_colour(n);
|
||||
}
|
||||
|
||||
if (colour == 8 || colour == 9) {
|
||||
xsnprintf(s, sizeof s, "\033[%dm", o + 9);
|
||||
return (s);
|
||||
}
|
||||
|
||||
if ((~flags & TERM_RGBCOLOURS) & (colour & COLOUR_FLAG_RGB)) {
|
||||
colour_split_rgb(colour, &r, &g, &b);
|
||||
colour = colour_find_rgb(r, g, b);
|
||||
}
|
||||
if ((~flags & TERM_256COLOURS) & (colour & COLOUR_FLAG_256))
|
||||
colour = colour_256to16(colour);
|
||||
|
||||
if (colour & COLOUR_FLAG_RGB) {
|
||||
colour_split_rgb(colour, &r, &g, &b);
|
||||
xsnprintf(s, sizeof s, "\033[%d;2;%u;%u;%um", o + 8, r, g, b);
|
||||
return (s);
|
||||
}
|
||||
if (colour & COLOUR_FLAG_256) {
|
||||
xsnprintf(s, sizeof s, "\033[%d;5;%um", o + 8, colour & 0xff);
|
||||
return (s);
|
||||
}
|
||||
if (colour >= 0 && colour <= 7) {
|
||||
xsnprintf(s, sizeof s, "\033[%dm", colour + o);
|
||||
return (s);
|
||||
}
|
||||
if (colour >= 90 && colour <= 97) {
|
||||
xsnprintf(s, sizeof s, "\033[%dm", colour + o - 30);
|
||||
return (s);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Convert background colour to theme. */
|
||||
enum client_theme
|
||||
colour_totheme(int c)
|
||||
@@ -230,6 +390,7 @@ colour_fromstring(const char *s)
|
||||
const char *cp;
|
||||
int n;
|
||||
u_char r, g, b;
|
||||
u_int i;
|
||||
|
||||
if (*s == '#' && strlen(s) == 7) {
|
||||
for (cp = s + 1; isxdigit((u_char) *cp); cp++)
|
||||
@@ -260,6 +421,11 @@ colour_fromstring(const char *s)
|
||||
if (strcasecmp(s, "terminal") == 0)
|
||||
return (9);
|
||||
|
||||
for (i = 0; i < nitems(colour_theme_table); i++) {
|
||||
if (strcasecmp(s, colour_theme_table[i].name) == 0)
|
||||
return (i|COLOUR_FLAG_THEME);
|
||||
}
|
||||
|
||||
if (strcasecmp(s, "black") == 0 || strcmp(s, "0") == 0)
|
||||
return (0);
|
||||
if (strcasecmp(s, "red") == 0 || strcmp(s, "1") == 0)
|
||||
|
||||
3
compat.h
3
compat.h
@@ -463,6 +463,9 @@ int utf8proc_wctomb(char *, wchar_t);
|
||||
#ifdef NEED_FUZZING
|
||||
/* tmux.c */
|
||||
#define main __weak main
|
||||
#define regcomp(preg, pattern, cflags) (0)
|
||||
#define regexec(preg, string, nmatch, pmatch, eflags) (REG_NOMATCH)
|
||||
#define regfree(preg) ((void)0)
|
||||
#endif
|
||||
|
||||
/* getopt.c */
|
||||
|
||||
@@ -339,7 +339,7 @@ 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 */
|
||||
const char *oli; /* option letter list index */
|
||||
int optchar, short_too;
|
||||
static int posixly_correct = -1;
|
||||
|
||||
|
||||
@@ -81,7 +81,8 @@ struct systemd_job_watch {
|
||||
};
|
||||
|
||||
static int
|
||||
job_removed_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
|
||||
job_removed_handler(sd_bus_message *m, void *userdata,
|
||||
__unused sd_bus_error *ret_error)
|
||||
{
|
||||
struct systemd_job_watch *watch = userdata;
|
||||
const char *path = NULL;
|
||||
@@ -109,7 +110,7 @@ systemd_move_to_new_cgroup(char **cause)
|
||||
sd_bus_message *m = NULL, *reply = NULL;
|
||||
sd_bus *bus = NULL;
|
||||
sd_bus_slot *slot = NULL;
|
||||
char *name, *desc, *slice;
|
||||
char *name, *desc, *slice, *unit;
|
||||
sd_id128_t uuid;
|
||||
int r;
|
||||
uint64_t elapsed_usec;
|
||||
@@ -241,6 +242,26 @@ systemd_move_to_new_cgroup(char **cause)
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try locating systemd unit that started the server, and mark pane units
|
||||
* as dependent on it. Use "Before" to make sure systemd will not try to
|
||||
* kill them first.
|
||||
*/
|
||||
if (sd_pid_get_user_unit(parent_pid, &unit) == 0 ||
|
||||
sd_pid_get_unit(parent_pid, &unit) == 0) {
|
||||
r = sd_bus_message_append(m, "(sv)", "Before", "as", 1, unit);
|
||||
if (r >= 0) {
|
||||
r = sd_bus_message_append(m, "(sv)", "PartOf", "as", 1,
|
||||
unit);
|
||||
}
|
||||
free(unit);
|
||||
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) {
|
||||
|
||||
71
configure.ac
71
configure.ac
@@ -73,6 +73,15 @@ AC_ARG_ENABLE(
|
||||
)
|
||||
AM_CONDITIONAL(IS_OPTIMIZED, test "x$enable_optimizations" = xyes)
|
||||
|
||||
# Is this --enable-asan?
|
||||
AC_ARG_ENABLE(
|
||||
asan,
|
||||
AS_HELP_STRING(--enable-asan, enable ASAN build flags),
|
||||
,
|
||||
enable_asan=no
|
||||
)
|
||||
AM_CONDITIONAL(IS_ASAN, test "x$enable_asan" = xyes)
|
||||
|
||||
# Is this a static build?
|
||||
AC_ARG_ENABLE(
|
||||
static,
|
||||
@@ -288,24 +297,24 @@ fi
|
||||
# Look for ncurses or curses. Try pkg-config first then directly for the
|
||||
# library.
|
||||
PKG_CHECK_MODULES(
|
||||
LIBTINFO,
|
||||
tinfo,
|
||||
LIBTINFOW,
|
||||
tinfow,
|
||||
[
|
||||
AM_CPPFLAGS="$LIBTINFO_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBTINFO_LIBS $LIBS"
|
||||
AM_CPPFLAGS="$LIBTINFOW_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$LIBTINFOW_CFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBTINFOW_LIBS $LIBS"
|
||||
found_ncurses=yes
|
||||
],
|
||||
found_ncurses=no
|
||||
)
|
||||
if test "x$found_ncurses" = xno; then
|
||||
PKG_CHECK_MODULES(
|
||||
LIBNCURSES,
|
||||
ncurses,
|
||||
LIBTINFO,
|
||||
tinfo,
|
||||
[
|
||||
AM_CPPFLAGS="$LIBNCURSES_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBNCURSES_LIBS $LIBS"
|
||||
AM_CPPFLAGS="$LIBTINFO_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$LIBTINFO_CFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBTINFO_LIBS $LIBS"
|
||||
found_ncurses=yes
|
||||
],
|
||||
found_ncurses=no
|
||||
@@ -324,10 +333,23 @@ if test "x$found_ncurses" = xno; then
|
||||
found_ncurses=no
|
||||
)
|
||||
fi
|
||||
if test "x$found_ncurses" = xno; then
|
||||
PKG_CHECK_MODULES(
|
||||
LIBNCURSES,
|
||||
ncurses,
|
||||
[
|
||||
AM_CPPFLAGS="$LIBNCURSES_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$LIBNCURSES_CFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$LIBNCURSES_LIBS $LIBS"
|
||||
found_ncurses=yes
|
||||
],
|
||||
found_ncurses=no
|
||||
)
|
||||
fi
|
||||
if test "x$found_ncurses" = xno; then
|
||||
AC_SEARCH_LIBS(
|
||||
setupterm,
|
||||
[tinfo terminfo ncurses ncursesw],
|
||||
[tinfow tinfo terminfo ncursesw ncurses],
|
||||
found_ncurses=yes,
|
||||
found_ncurses=no
|
||||
)
|
||||
@@ -462,33 +484,14 @@ if test "x$enable_cgroups" = xyes; then
|
||||
fi
|
||||
|
||||
# Enable sixel support.
|
||||
AC_ARG_ENABLE(
|
||||
sixel-images,
|
||||
AS_HELP_STRING(--enable-sixel-images, enable sixel images)
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
sixel,
|
||||
AS_HELP_STRING(--enable-sixel support),
|
||||
[
|
||||
enable_sixel_images="$enableval"
|
||||
]
|
||||
AS_HELP_STRING(--enable-sixel, enable sixel images)
|
||||
)
|
||||
|
||||
if test "x$enable_sixel_images" = xyes; then
|
||||
AC_DEFINE(ENABLE_SIXEL_IMAGES)
|
||||
if test "x$enable_sixel" = xyes; then
|
||||
AC_DEFINE(ENABLE_SIXEL)
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_SIXEL_IMAGES, [test "x$enable_sixel_images" = xyes])
|
||||
|
||||
# Enable kitty graphics protocol support.
|
||||
AC_ARG_ENABLE(
|
||||
kitty-images,
|
||||
AS_HELP_STRING(--enable-kitty-images, enable kitty terminal graphics)
|
||||
)
|
||||
if test "x$enable_kitty_images" = xyes; then
|
||||
AC_DEFINE(ENABLE_KITTY_IMAGES)
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_KITTY_IMAGES, [test "x$enable_kitty_images" = xyes])
|
||||
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)
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
#include "tmux.h"
|
||||
|
||||
#define CONTROL_SHOULD_NOTIFY_CLIENT(c) \
|
||||
((c) != NULL && ((c)->flags & CLIENT_CONTROL))
|
||||
((c) != NULL && ((c)->flags & CLIENT_CONTROL) && \
|
||||
(c)->control_state != NULL)
|
||||
|
||||
void
|
||||
control_notify_pane_mode_changed(int pane)
|
||||
@@ -76,6 +77,8 @@ control_notify_window_pane_changed(struct window *w)
|
||||
{
|
||||
struct client *c;
|
||||
|
||||
if (w->active == NULL)
|
||||
return;
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
@@ -221,6 +224,14 @@ control_notify_session_window_changed(struct session *s)
|
||||
{
|
||||
struct client *c;
|
||||
|
||||
/*
|
||||
* A deferred session-window-changed notification can fire after the
|
||||
* session has been destroyed (which sets curw to NULL) but is kept
|
||||
* alive by the notification's reference. Skip the notification.
|
||||
*/
|
||||
if (s->curw == NULL)
|
||||
return;
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
|
||||
15
control.c
15
control.c
@@ -207,10 +207,12 @@ control_free_sub(struct control_state *cs, struct control_sub *csub)
|
||||
|
||||
RB_FOREACH_SAFE(csp, control_sub_panes, &csub->panes, csp1) {
|
||||
RB_REMOVE(control_sub_panes, &csub->panes, csp);
|
||||
free(csp->last);
|
||||
free(csp);
|
||||
}
|
||||
RB_FOREACH_SAFE(csw, control_sub_windows, &csub->windows, csw1) {
|
||||
RB_REMOVE(control_sub_windows, &csub->windows, csw);
|
||||
free(csw->last);
|
||||
free(csw);
|
||||
}
|
||||
free(csub->last);
|
||||
@@ -298,6 +300,7 @@ control_reset_offsets(struct client *c)
|
||||
struct control_pane *cp, *cp1;
|
||||
|
||||
RB_FOREACH_SAFE(cp, control_panes, &cs->panes, cp1) {
|
||||
control_discard_pane(c, cp);
|
||||
RB_REMOVE(control_panes, &cs->panes, cp);
|
||||
free(cp);
|
||||
}
|
||||
@@ -352,6 +355,9 @@ control_set_pane_off(struct client *c, struct window_pane *wp)
|
||||
struct control_pane *cp;
|
||||
|
||||
cp = control_add_pane(c, wp);
|
||||
control_discard_pane(c, cp);
|
||||
memcpy(&cp->offset, &wp->offset, sizeof cp->offset);
|
||||
memcpy(&cp->queued, &wp->offset, sizeof cp->queued);
|
||||
cp->flags |= CONTROL_PANE_OFF;
|
||||
}
|
||||
|
||||
@@ -828,6 +834,9 @@ control_stop(struct client *c)
|
||||
struct control_block *cb, *cb1;
|
||||
struct control_sub *csub, *csub1;
|
||||
|
||||
if (cs == NULL)
|
||||
return;
|
||||
|
||||
if (~c->flags & CLIENT_CONTROLCONTROL)
|
||||
bufferevent_free(cs->write_event);
|
||||
bufferevent_free(cs->read_event);
|
||||
@@ -837,10 +846,11 @@ control_stop(struct client *c)
|
||||
if (evtimer_initialized(&cs->subs_timer))
|
||||
evtimer_del(&cs->subs_timer);
|
||||
|
||||
control_reset_offsets(c);
|
||||
TAILQ_FOREACH_SAFE(cb, &cs->all_blocks, all_entry, cb1)
|
||||
control_free_block(cs, cb);
|
||||
control_reset_offsets(c);
|
||||
|
||||
c->control_state = NULL;
|
||||
free(cs);
|
||||
}
|
||||
|
||||
@@ -1045,6 +1055,9 @@ control_check_subs_timer(__unused int fd, __unused short events, void *data)
|
||||
log_debug("%s: timer fired", __func__);
|
||||
evtimer_add(&cs->subs_timer, &tv);
|
||||
|
||||
if (s == NULL)
|
||||
return;
|
||||
|
||||
/* Find which subscription types are present. */
|
||||
RB_FOREACH(csub, control_subs, &cs->subs) {
|
||||
switch (csub->type) {
|
||||
|
||||
@@ -152,7 +152,8 @@ environ_clear(struct environ *env, const char *name)
|
||||
void
|
||||
environ_put(struct environ *env, const char *var, int flags)
|
||||
{
|
||||
char *name, *value;
|
||||
char *name;
|
||||
const char *value;
|
||||
|
||||
value = strchr(var, '=');
|
||||
if (value == NULL)
|
||||
|
||||
29
file.c
29
file.c
@@ -399,6 +399,10 @@ file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
|
||||
}
|
||||
for (;;) {
|
||||
size = fread(buffer, 1, sizeof buffer, f);
|
||||
if (ferror(f)) {
|
||||
cf->error = errno;
|
||||
goto done;
|
||||
}
|
||||
if (evbuffer_add(cf->buffer, buffer, size) != 0) {
|
||||
cf->error = ENOMEM;
|
||||
goto done;
|
||||
@@ -671,7 +675,7 @@ file_write_close(struct client_files *files, struct imsg *imsg)
|
||||
|
||||
/* Client file read error callback. */
|
||||
static void
|
||||
file_read_error_callback(__unused struct bufferevent *bev, __unused short what,
|
||||
file_read_error_callback(__unused struct bufferevent *bev, short what,
|
||||
void *arg)
|
||||
{
|
||||
struct client_file *cf = arg;
|
||||
@@ -680,7 +684,7 @@ file_read_error_callback(__unused struct bufferevent *bev, __unused short what,
|
||||
log_debug("read error file %d", cf->stream);
|
||||
|
||||
msg.stream = cf->stream;
|
||||
msg.error = 0;
|
||||
msg.error = (what & EVBUFFER_ERROR) ? EIO : 0;
|
||||
proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg);
|
||||
|
||||
bufferevent_free(cf->event);
|
||||
@@ -804,7 +808,7 @@ file_read_cancel(struct client_files *files, struct imsg *imsg)
|
||||
}
|
||||
|
||||
/* Handle a write ready message (server). */
|
||||
void
|
||||
int
|
||||
file_write_ready(struct client_files *files, struct imsg *imsg)
|
||||
{
|
||||
struct msg_write_ready *msg = imsg->data;
|
||||
@@ -812,19 +816,20 @@ file_write_ready(struct client_files *files, struct imsg *imsg)
|
||||
struct client_file find, *cf;
|
||||
|
||||
if (msglen != sizeof *msg)
|
||||
fatalx("bad MSG_WRITE_READY size");
|
||||
return (-1);
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||
return;
|
||||
return (0);
|
||||
if (msg->error != 0) {
|
||||
cf->error = msg->error;
|
||||
file_fire_done(cf);
|
||||
} else
|
||||
file_push(cf);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Handle read data message (server). */
|
||||
void
|
||||
int
|
||||
file_read_data(struct client_files *files, struct imsg *imsg)
|
||||
{
|
||||
struct msg_read_data *msg = imsg->data;
|
||||
@@ -834,10 +839,10 @@ file_read_data(struct client_files *files, struct imsg *imsg)
|
||||
size_t bsize = msglen - sizeof *msg;
|
||||
|
||||
if (msglen < sizeof *msg)
|
||||
fatalx("bad MSG_READ_DATA size");
|
||||
return (-1);
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||
return;
|
||||
return (0);
|
||||
|
||||
log_debug("file %d read %zu bytes", cf->stream, bsize);
|
||||
if (cf->error == 0 && !cf->closed) {
|
||||
@@ -847,10 +852,11 @@ file_read_data(struct client_files *files, struct imsg *imsg)
|
||||
} else
|
||||
file_fire_read(cf);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Handle a read done message (server). */
|
||||
void
|
||||
int
|
||||
file_read_done(struct client_files *files, struct imsg *imsg)
|
||||
{
|
||||
struct msg_read_done *msg = imsg->data;
|
||||
@@ -858,12 +864,13 @@ file_read_done(struct client_files *files, struct imsg *imsg)
|
||||
struct client_file find, *cf;
|
||||
|
||||
if (msglen != sizeof *msg)
|
||||
fatalx("bad MSG_READ_DONE size");
|
||||
return (-1);
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||
return;
|
||||
return (0);
|
||||
|
||||
log_debug("file %d read done", cf->stream);
|
||||
cf->error = msg->error;
|
||||
file_fire_done(cf);
|
||||
return (0);
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ format_is_type(struct format_range *fr, struct style *sy)
|
||||
case STYLE_RANGE_NONE:
|
||||
case STYLE_RANGE_LEFT:
|
||||
case STYLE_RANGE_RIGHT:
|
||||
case STYLE_RANGE_CONTROL:
|
||||
return (1);
|
||||
case STYLE_RANGE_PANE:
|
||||
case STYLE_RANGE_WINDOW:
|
||||
@@ -709,6 +710,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
||||
"AFTER" };
|
||||
size_t size = strlen(expanded);
|
||||
struct screen *os = octx->s, s[TOTAL];
|
||||
struct hyperlinks *hl = os->hyperlinks;
|
||||
struct screen_write_ctx ctx[TOTAL];
|
||||
u_int ocx = os->cx, ocy = os->cy, n, i, width[TOTAL];
|
||||
u_int map[] = { LEFT,
|
||||
@@ -722,7 +724,7 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
||||
struct grid_cell gc, current_default, base_default;
|
||||
struct style sy, saved_sy;
|
||||
struct utf8_data *ud = &sy.gc.data;
|
||||
const char *cp, *end;
|
||||
const char *cp, *end, *link_uri;
|
||||
enum utf8_state more;
|
||||
char *tmp;
|
||||
struct format_range *fr = NULL, *fr1;
|
||||
@@ -837,6 +839,17 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
||||
sy.gc.fg = base->fg;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resolve any hyperlink and store it in the cell. The URI
|
||||
* doubles as the internal ID so repeated links share one entry
|
||||
* and the ID stays stable across redraws.
|
||||
*/
|
||||
link_uri = style_link(&sy);
|
||||
if (link_uri != NULL && hl != NULL)
|
||||
sy.gc.link = hyperlinks_put(hl, link_uri, link_uri);
|
||||
else
|
||||
sy.gc.link = 0;
|
||||
|
||||
/* If this style has a fill colour, store it for later. */
|
||||
if (sy.fill != 8)
|
||||
fill = sy.fill;
|
||||
@@ -1065,6 +1078,10 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
|
||||
log_debug("%s: range user|%u at %u-%u", __func__,
|
||||
sr->argument, sr->start, sr->end);
|
||||
break;
|
||||
case STYLE_RANGE_CONTROL:
|
||||
log_debug("%s: range control|%u at %u-%u", __func__,
|
||||
sr->argument, sr->start, sr->end);
|
||||
break;
|
||||
}
|
||||
format_free_range(&frs, fr);
|
||||
}
|
||||
@@ -1116,7 +1133,7 @@ format_width(const char *expanded)
|
||||
/*
|
||||
* Trim on the left, taking #[] into account. Note, we copy the whole set of
|
||||
* unescaped #s, but only add their escaped size to width. This is because the
|
||||
* format_draw function will actually do the escaping when it runs
|
||||
* format_draw function will actually do the escaping.
|
||||
*/
|
||||
char *
|
||||
format_trim_left(const char *expanded, u_int limit)
|
||||
|
||||
83
fuzz/cmd-parse-fuzzer.c
Normal file
83
fuzz/cmd-parse-fuzzer.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2026 David Korczynski <david@adalogics.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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Fuzz the tmux command parser (cmd_parse_from_buffer).
|
||||
*
|
||||
* This exercises:
|
||||
* - cmd-parse.y (yacc grammar, lexer, command building)
|
||||
* - cmd.c (command lookup and validation)
|
||||
* - arguments.c (argument parsing and flag handling)
|
||||
* - cmd-find.c (target resolution)
|
||||
* - options.c (option name lookups during parsing)
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
struct event_base *libevent;
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const u_char *data, size_t size)
|
||||
{
|
||||
struct cmd_parse_input pi;
|
||||
struct cmd_parse_result *pr;
|
||||
|
||||
if (size > 2048 || size == 0)
|
||||
return 0;
|
||||
|
||||
memset(&pi, 0, sizeof pi);
|
||||
pi.flags = CMD_PARSE_QUIET;
|
||||
|
||||
pr = cmd_parse_from_buffer(data, size, &pi);
|
||||
switch (pr->status) {
|
||||
case CMD_PARSE_SUCCESS:
|
||||
cmd_list_free(pr->cmdlist);
|
||||
break;
|
||||
case CMD_PARSE_ERROR:
|
||||
free(pr->error);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv)
|
||||
{
|
||||
const struct options_table_entry *oe;
|
||||
|
||||
global_environ = environ_create();
|
||||
global_options = options_create(NULL);
|
||||
global_s_options = options_create(NULL);
|
||||
global_w_options = options_create(NULL);
|
||||
for (oe = options_table; oe->name != NULL; oe++) {
|
||||
if (oe->scope & OPTIONS_TABLE_SERVER)
|
||||
options_default(global_options, oe);
|
||||
if (oe->scope & OPTIONS_TABLE_SESSION)
|
||||
options_default(global_s_options, oe);
|
||||
if (oe->scope & OPTIONS_TABLE_WINDOW)
|
||||
options_default(global_w_options, oe);
|
||||
}
|
||||
libevent = osdep_event_init();
|
||||
socket_path = xstrdup("dummy");
|
||||
|
||||
return 0;
|
||||
}
|
||||
133
fuzz/cmd-parse-fuzzer.dict
Normal file
133
fuzz/cmd-parse-fuzzer.dict
Normal file
@@ -0,0 +1,133 @@
|
||||
# tmux command names
|
||||
"set-option"
|
||||
"bind-key"
|
||||
"unbind-key"
|
||||
"send-keys"
|
||||
"new-session"
|
||||
"new-window"
|
||||
"split-window"
|
||||
"select-window"
|
||||
"select-pane"
|
||||
"kill-pane"
|
||||
"kill-window"
|
||||
"kill-session"
|
||||
"kill-server"
|
||||
"list-sessions"
|
||||
"list-windows"
|
||||
"list-panes"
|
||||
"list-keys"
|
||||
"list-buffers"
|
||||
"list-clients"
|
||||
"list-commands"
|
||||
"attach-session"
|
||||
"detach-client"
|
||||
"switch-client"
|
||||
"rename-session"
|
||||
"rename-window"
|
||||
"resize-pane"
|
||||
"resize-window"
|
||||
"display-message"
|
||||
"display-menu"
|
||||
"display-popup"
|
||||
"display-panes"
|
||||
"copy-mode"
|
||||
"paste-buffer"
|
||||
"capture-pane"
|
||||
"save-buffer"
|
||||
"load-buffer"
|
||||
"set-buffer"
|
||||
"delete-buffer"
|
||||
"show-buffer"
|
||||
"choose-buffer"
|
||||
"choose-tree"
|
||||
"choose-client"
|
||||
"if-shell"
|
||||
"run-shell"
|
||||
"source-file"
|
||||
"command-prompt"
|
||||
"confirm-before"
|
||||
"pipe-pane"
|
||||
"wait-for"
|
||||
"set-environment"
|
||||
"show-environment"
|
||||
"set-hook"
|
||||
"show-hooks"
|
||||
"show-messages"
|
||||
"show-options"
|
||||
"set-window-option"
|
||||
"show-window-options"
|
||||
"clear-history"
|
||||
"clock-mode"
|
||||
"find-window"
|
||||
"join-pane"
|
||||
"move-pane"
|
||||
"break-pane"
|
||||
"swap-pane"
|
||||
"swap-window"
|
||||
"move-window"
|
||||
"link-window"
|
||||
"unlink-window"
|
||||
"rotate-window"
|
||||
"next-window"
|
||||
"previous-window"
|
||||
"last-window"
|
||||
"last-pane"
|
||||
"next-layout"
|
||||
"previous-layout"
|
||||
"select-layout"
|
||||
"customize-mode"
|
||||
"refresh-client"
|
||||
"suspend-client"
|
||||
"lock-client"
|
||||
"lock-server"
|
||||
"lock-session"
|
||||
"respawn-pane"
|
||||
"respawn-window"
|
||||
"start-server"
|
||||
"server-access"
|
||||
"send-prefix"
|
||||
"clear-prompt-history"
|
||||
"show-prompt-history"
|
||||
|
||||
# Common flags and syntax
|
||||
"-t"
|
||||
"-s"
|
||||
"-g"
|
||||
"-w"
|
||||
"-p"
|
||||
"-a"
|
||||
"-o"
|
||||
"-q"
|
||||
"-u"
|
||||
"-F"
|
||||
"-f"
|
||||
"-b"
|
||||
"-d"
|
||||
"-e"
|
||||
"-n"
|
||||
"-r"
|
||||
|
||||
# Targets and option names
|
||||
"status"
|
||||
"default"
|
||||
"mouse"
|
||||
"on"
|
||||
"off"
|
||||
"vi"
|
||||
"emacs"
|
||||
"set -g"
|
||||
"set -s"
|
||||
"setw"
|
||||
"bind"
|
||||
"unbind"
|
||||
|
||||
# Syntax elements
|
||||
"{"
|
||||
"}"
|
||||
";"
|
||||
"#{"
|
||||
"%if"
|
||||
"%elif"
|
||||
"%else"
|
||||
"%endif"
|
||||
"%hidden"
|
||||
2
fuzz/cmd-parse-fuzzer.options
Normal file
2
fuzz/cmd-parse-fuzzer.options
Normal file
@@ -0,0 +1,2 @@
|
||||
[libfuzzer]
|
||||
max_len = 2048
|
||||
88
fuzz/format-fuzzer.c
Normal file
88
fuzz/format-fuzzer.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2026 David Korczynski <david@adalogics.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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Fuzz the tmux format string expander (format_expand).
|
||||
*
|
||||
* This exercises:
|
||||
* - format.c (format parsing, modifier chains, conditionals, math, regex)
|
||||
* - colour.c (colour name and RGB parsing within formats)
|
||||
* - utf8.c (UTF-8 width calculations in format padding)
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
struct event_base *libevent;
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const u_char *data, size_t size)
|
||||
{
|
||||
struct format_tree *ft;
|
||||
char *buf, *expanded;
|
||||
|
||||
if (size > 2048 || size == 0)
|
||||
return 0;
|
||||
|
||||
/* Null-terminate the input for format_expand. */
|
||||
buf = malloc(size + 1);
|
||||
if (buf == NULL)
|
||||
return 0;
|
||||
memcpy(buf, data, size);
|
||||
buf[size] = '\0';
|
||||
|
||||
ft = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
|
||||
format_add(ft, "session_name", "%s", "fuzz-session");
|
||||
format_add(ft, "window_index", "%d", 0);
|
||||
format_add(ft, "window_name", "%s", "fuzz-window");
|
||||
format_add(ft, "pane_index", "%d", 0);
|
||||
format_add(ft, "pane_id", "%s", "%%0");
|
||||
format_add(ft, "host", "%s", "fuzzhost");
|
||||
format_add(ft, "pane_width", "%d", 80);
|
||||
format_add(ft, "pane_height", "%d", 25);
|
||||
|
||||
expanded = format_expand(ft, buf);
|
||||
free(expanded);
|
||||
format_free(ft);
|
||||
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv)
|
||||
{
|
||||
const struct options_table_entry *oe;
|
||||
|
||||
global_environ = environ_create();
|
||||
global_options = options_create(NULL);
|
||||
global_s_options = options_create(NULL);
|
||||
global_w_options = options_create(NULL);
|
||||
for (oe = options_table; oe->name != NULL; oe++) {
|
||||
if (oe->scope & OPTIONS_TABLE_SERVER)
|
||||
options_default(global_options, oe);
|
||||
if (oe->scope & OPTIONS_TABLE_SESSION)
|
||||
options_default(global_s_options, oe);
|
||||
if (oe->scope & OPTIONS_TABLE_WINDOW)
|
||||
options_default(global_w_options, oe);
|
||||
}
|
||||
libevent = osdep_event_init();
|
||||
socket_path = xstrdup("dummy");
|
||||
|
||||
return 0;
|
||||
}
|
||||
71
fuzz/format-fuzzer.dict
Normal file
71
fuzz/format-fuzzer.dict
Normal file
@@ -0,0 +1,71 @@
|
||||
# Format expansion syntax
|
||||
"#{"
|
||||
"}"
|
||||
"#{?"
|
||||
"#{=="
|
||||
"#{!="
|
||||
"#{<"
|
||||
"#{>"
|
||||
"#{m:"
|
||||
"#{C:"
|
||||
"#{s/"
|
||||
"#{t:"
|
||||
"#{T:"
|
||||
"#{E:"
|
||||
"#{S:"
|
||||
"#{W:"
|
||||
"#{P:"
|
||||
"##"
|
||||
|
||||
# Common format modifiers
|
||||
"b:"
|
||||
"d:"
|
||||
"q:"
|
||||
"l:"
|
||||
"e:"
|
||||
"t:"
|
||||
"p:"
|
||||
"w:"
|
||||
"n:"
|
||||
"a:"
|
||||
"=:"
|
||||
"||"
|
||||
"&&"
|
||||
","
|
||||
"#{e|"
|
||||
"#{p-1:"
|
||||
"#{=/10/...:"
|
||||
|
||||
# Format variables
|
||||
"session_name"
|
||||
"window_index"
|
||||
"window_name"
|
||||
"pane_index"
|
||||
"pane_id"
|
||||
"host"
|
||||
"pane_width"
|
||||
"pane_height"
|
||||
"pane_current_command"
|
||||
"pane_pid"
|
||||
"session_id"
|
||||
"window_id"
|
||||
"client_name"
|
||||
"version"
|
||||
"line"
|
||||
|
||||
# Conditionals
|
||||
"#{?pane_active,"
|
||||
",}"
|
||||
"#{?#{==:"
|
||||
|
||||
# Math and comparison
|
||||
"#{e|+:"
|
||||
"#{e|-:"
|
||||
"#{e|*:"
|
||||
"#{e|/:"
|
||||
"#{e|%:"
|
||||
|
||||
# Nested
|
||||
"#{="
|
||||
"#{"
|
||||
"#{l:"
|
||||
2
fuzz/format-fuzzer.options
Normal file
2
fuzz/format-fuzzer.options
Normal file
@@ -0,0 +1,2 @@
|
||||
[libfuzzer]
|
||||
max_len = 2048
|
||||
79
fuzz/style-fuzzer.c
Normal file
79
fuzz/style-fuzzer.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2026 David Korczynski <david@adalogics.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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Fuzz the tmux style parser (style_parse).
|
||||
*
|
||||
* This exercises:
|
||||
* - style.c (style string parsing, alignment, ranges)
|
||||
* - colour.c (colour name, RGB, and indexed colour parsing)
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
struct event_base *libevent;
|
||||
|
||||
int
|
||||
LLVMFuzzerTestOneInput(const u_char *data, size_t size)
|
||||
{
|
||||
struct style sy;
|
||||
struct grid_cell gc;
|
||||
char *buf;
|
||||
|
||||
if (size > 512 || size == 0)
|
||||
return 0;
|
||||
|
||||
/* Null-terminate the input for style_parse. */
|
||||
buf = malloc(size + 1);
|
||||
if (buf == NULL)
|
||||
return 0;
|
||||
memcpy(buf, data, size);
|
||||
buf[size] = '\0';
|
||||
|
||||
memset(&gc, 0, sizeof gc);
|
||||
style_set(&sy, &gc);
|
||||
|
||||
style_parse(&sy, &gc, buf);
|
||||
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
LLVMFuzzerInitialize(__unused int *argc, __unused char ***argv)
|
||||
{
|
||||
const struct options_table_entry *oe;
|
||||
|
||||
global_environ = environ_create();
|
||||
global_options = options_create(NULL);
|
||||
global_s_options = options_create(NULL);
|
||||
global_w_options = options_create(NULL);
|
||||
for (oe = options_table; oe->name != NULL; oe++) {
|
||||
if (oe->scope & OPTIONS_TABLE_SERVER)
|
||||
options_default(global_options, oe);
|
||||
if (oe->scope & OPTIONS_TABLE_SESSION)
|
||||
options_default(global_s_options, oe);
|
||||
if (oe->scope & OPTIONS_TABLE_WINDOW)
|
||||
options_default(global_w_options, oe);
|
||||
}
|
||||
libevent = osdep_event_init();
|
||||
socket_path = xstrdup("dummy");
|
||||
|
||||
return 0;
|
||||
}
|
||||
73
fuzz/style-fuzzer.dict
Normal file
73
fuzz/style-fuzzer.dict
Normal file
@@ -0,0 +1,73 @@
|
||||
# Style attributes
|
||||
"default"
|
||||
"ignore"
|
||||
"nodefaults"
|
||||
"bright"
|
||||
"bold"
|
||||
"dim"
|
||||
"underscore"
|
||||
"blink"
|
||||
"reverse"
|
||||
"hidden"
|
||||
"italics"
|
||||
"overline"
|
||||
"strikethrough"
|
||||
"double-underscore"
|
||||
"curly-underscore"
|
||||
"dotted-underscore"
|
||||
"dashed-underscore"
|
||||
"nobright"
|
||||
"nobold"
|
||||
"nodim"
|
||||
"nounderscore"
|
||||
"noblink"
|
||||
"noreverse"
|
||||
"nohidden"
|
||||
"noitalics"
|
||||
"nooverline"
|
||||
"nostrikethrough"
|
||||
|
||||
# Colours
|
||||
"fg="
|
||||
"bg="
|
||||
"us="
|
||||
"fill="
|
||||
"colour0"
|
||||
"colour255"
|
||||
"red"
|
||||
"green"
|
||||
"blue"
|
||||
"yellow"
|
||||
"cyan"
|
||||
"magenta"
|
||||
"white"
|
||||
"black"
|
||||
|
||||
# RGB and hex
|
||||
"#ff0000"
|
||||
"#00ff00"
|
||||
"#0000ff"
|
||||
|
||||
# Alignment and ranges
|
||||
"align=left"
|
||||
"align=centre"
|
||||
"align=right"
|
||||
"align=absolute-centre"
|
||||
"list=on"
|
||||
"list=focus"
|
||||
"list=left-marker"
|
||||
"list=right-marker"
|
||||
"range=left"
|
||||
"range=right"
|
||||
"range=pane"
|
||||
"range=window"
|
||||
"range=session"
|
||||
"range=user"
|
||||
|
||||
# Width and padding
|
||||
"width="
|
||||
"pad="
|
||||
|
||||
# Delimiters
|
||||
","
|
||||
" "
|
||||
2
fuzz/style-fuzzer.options
Normal file
2
fuzz/style-fuzzer.options
Normal file
@@ -0,0 +1,2 @@
|
||||
[libfuzzer]
|
||||
max_len = 512
|
||||
655
fuzzy.c
Normal file
655
fuzzy.c
Normal file
@@ -0,0 +1,655 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 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 <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
/*
|
||||
* Fuzzy matching in the style of fzf. The pattern is split into groups by |
|
||||
* and each group is split on spaces into terms. A row matches if any group
|
||||
* matches; within a group all positive terms must match and all inverse terms
|
||||
* must not match.
|
||||
*
|
||||
* Plain positive terms are fuzzy subsequences. A leading ' makes a term an
|
||||
* exact substring match, ^ anchors a term at the start and $ anchors it at
|
||||
* the end. A leading ! inverts the term. Plain inverse terms are exact
|
||||
* substring matches rather than inverse fuzzy matches, like fzf.
|
||||
*
|
||||
* Both the pattern and the text are UTF-8. The text may contain tmux style
|
||||
* directives (#[...]); these and their contents are invisible to matching and
|
||||
* occupy no columns, but align= styles do move the surrounding text and are
|
||||
* accounted for exactly as format_draw lays it out (the no-list layout, see
|
||||
* format_draw_none). Matching is smart-case: case is ignored unless the pattern
|
||||
* contains an uppercase character (ASCII case folding only; other characters
|
||||
* are compared exactly by their UTF-8 data).
|
||||
*
|
||||
* On a match a bitstr_t of the requested display width is returned with a bit
|
||||
* set for every column occupied by a matched character, so the caller can
|
||||
* highlight them; NULL is returned if there is no match. A cheap fzf-style
|
||||
* score (matches at the start, after word boundaries and in contiguous runs
|
||||
* score higher) is also produced so callers can rank best-match-first.
|
||||
*/
|
||||
|
||||
#define FUZZY_BONUS_EXACT 1000
|
||||
#define FUZZY_BONUS_PREFIX 200
|
||||
#define FUZZY_BONUS_SUFFIX 100
|
||||
#define FUZZY_BONUS_START 12
|
||||
#define FUZZY_BONUS_BOUNDARY 8
|
||||
#define FUZZY_BONUS_CONSECUTIVE 6
|
||||
#define FUZZY_PENALTY_LEADING 1
|
||||
#define FUZZY_PENALTY_LEADING_MAX 10
|
||||
#define FUZZY_PENALTY_GAP 1
|
||||
|
||||
/* A single visible character of the text. */
|
||||
struct fuzzy_char {
|
||||
enum style_align align;
|
||||
struct utf8_data ud; /* original UTF-8 data */
|
||||
u_int width; /* display width */
|
||||
u_int offset; /* within its alignment */
|
||||
};
|
||||
|
||||
/* One parsed query term. */
|
||||
struct fuzzy_term {
|
||||
int inverse;
|
||||
int exact;
|
||||
int prefix;
|
||||
int suffix;
|
||||
const char *text;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
/* Is this character a word boundary, so a match after it scores higher? */
|
||||
static int
|
||||
fuzzy_is_boundary(const struct utf8_data *ud)
|
||||
{
|
||||
static const char *boundary = " -_/.:";
|
||||
|
||||
if (ud->size != 1)
|
||||
return (0);
|
||||
return (strchr(boundary, ud->data[0]) != NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare two characters, folding ASCII case if wanted. UTF-8 is compared
|
||||
* directly without case folding.
|
||||
*/
|
||||
static int
|
||||
fuzzy_char_equal(const struct utf8_data *a, const struct utf8_data *b, int fold)
|
||||
{
|
||||
if (fold &&
|
||||
a->size == 1 &&
|
||||
b->size == 1 &&
|
||||
a->data[0] < 0x80 &&
|
||||
b->data[0] < 0x80)
|
||||
return (tolower(a->data[0]) == tolower(b->data[0]));
|
||||
return (a->size == b->size && memcmp(a->data, b->data, a->size) == 0);
|
||||
}
|
||||
|
||||
/* Map a style alignment onto one of the four layout columns. */
|
||||
static enum style_align
|
||||
fuzzy_align(enum style_align align)
|
||||
{
|
||||
if (align == STYLE_ALIGN_DEFAULT)
|
||||
return (STYLE_ALIGN_LEFT);
|
||||
return (align);
|
||||
}
|
||||
|
||||
/* Add a visible character to the array, updating the alignment width. */
|
||||
static void
|
||||
fuzzy_add(struct fuzzy_char **cs, u_int *ncs, u_int *alloc, enum style_align a,
|
||||
const struct utf8_data *ud, u_int *widths)
|
||||
{
|
||||
struct fuzzy_char *fc;
|
||||
|
||||
if (*ncs == *alloc) {
|
||||
*alloc = (*alloc == 0) ? 64 : *alloc * 2;
|
||||
*cs = xreallocarray(*cs, *alloc, sizeof **cs);
|
||||
}
|
||||
fc = &(*cs)[(*ncs)++];
|
||||
fc->align = a;
|
||||
memcpy(&fc->ud, ud, sizeof fc->ud);
|
||||
fc->width = ud->width;
|
||||
fc->offset = widths[a];
|
||||
widths[a] += ud->width;
|
||||
}
|
||||
|
||||
/* Decode a character as UTF-8. */
|
||||
static const char *
|
||||
fuzzy_decode_one(const char *cp, const char *end, struct utf8_data *ud)
|
||||
{
|
||||
enum utf8_state more;
|
||||
const char *start = cp;
|
||||
|
||||
if ((more = utf8_open(ud, (u_char)*cp)) == UTF8_MORE) {
|
||||
while (++cp != end && more == UTF8_MORE)
|
||||
more = utf8_append(ud, (u_char)*cp);
|
||||
if (more == UTF8_DONE)
|
||||
return (cp);
|
||||
cp = start;
|
||||
}
|
||||
utf8_set(ud, (u_char)*cp);
|
||||
return (cp + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the text into an array of visible characters, skipping styles and
|
||||
* recording the alignment and intra-alignment offset of each. Returns the
|
||||
* array and its length and fills in the per-alignment widths.
|
||||
*/
|
||||
static struct fuzzy_char *
|
||||
fuzzy_scan(const char *text, u_int *ncs, u_int *widths)
|
||||
{
|
||||
struct fuzzy_char *cs = NULL;
|
||||
u_int alloc = 0, n, leading, i;
|
||||
enum style_align current = STYLE_ALIGN_LEFT;
|
||||
struct style sy;
|
||||
const char *cp = text, *textend = text + strlen(text);
|
||||
const char *end;
|
||||
struct utf8_data ud, hash, bracket;
|
||||
char *tmp;
|
||||
|
||||
*ncs = 0;
|
||||
memset(widths, 0, sizeof *widths * (STYLE_ALIGN_ABSOLUTE_CENTRE + 1));
|
||||
style_set(&sy, &grid_default_cell);
|
||||
utf8_set(&hash, '#');
|
||||
utf8_set(&bracket, '[');
|
||||
|
||||
while (*cp != '\0') {
|
||||
/* Handle a run of #s, which may introduce a style. */
|
||||
if (*cp == '#') {
|
||||
for (n = 0; cp[n] == '#'; n++)
|
||||
/* nothing */;
|
||||
if (cp[n] != '[') {
|
||||
/* Escaped #s: ##->#, so half (rounded up). */
|
||||
leading = (n % 2 == 0) ? n / 2 : n / 2 + 1;
|
||||
for (i = 0; i < leading; i++) {
|
||||
fuzzy_add(&cs, ncs, &alloc, current,
|
||||
&hash, widths);
|
||||
}
|
||||
cp += n;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Even count: all #s escaped, the [ is literal. */
|
||||
for (i = 0; i < n / 2; i++)
|
||||
fuzzy_add(&cs, ncs, &alloc, current, &hash,
|
||||
widths);
|
||||
if (n % 2 == 0) {
|
||||
fuzzy_add(&cs, ncs, &alloc, current, &bracket,
|
||||
widths);
|
||||
cp += n + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Odd count: this is a style, find and parse it. */
|
||||
end = format_skip(cp + n + 1, "]");
|
||||
if (end == NULL)
|
||||
break;
|
||||
tmp = xstrndup(cp + n + 1, end - (cp + n + 1));
|
||||
if (style_parse(&sy, &grid_default_cell, tmp) == 0)
|
||||
current = fuzzy_align(sy.align);
|
||||
free(tmp);
|
||||
cp = end + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Decode one character, multibyte or single byte. */
|
||||
cp = fuzzy_decode_one(cp, textend, &ud);
|
||||
|
||||
/*
|
||||
* Skip non-printable single bytes (control characters and raw
|
||||
* bytes left over from a failed decode); keep printable ASCII
|
||||
* and any decoded UTF-8.
|
||||
*/
|
||||
if (ud.size == 1 && (ud.data[0] <= 0x1f || ud.data[0] >= 0x7f))
|
||||
continue;
|
||||
fuzzy_add(&cs, ncs, &alloc, current, &ud, widths);
|
||||
}
|
||||
return (cs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Work out the display column of a visible character given the trimmed widths
|
||||
* and start columns of each alignment. Returns 0 and sets the column if the
|
||||
* character is visible, otherwise returns -1.
|
||||
*/
|
||||
static int
|
||||
fuzzy_column(const struct fuzzy_char *fc, const u_int *start, const u_int *src,
|
||||
const u_int *vis, u_int *column)
|
||||
{
|
||||
enum style_align a = fc->align;
|
||||
|
||||
if (fc->offset < src[a] || fc->offset >= src[a] + vis[a])
|
||||
return (-1);
|
||||
*column = start[a] + (fc->offset - src[a]);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Decode a UTF-8 term into an array of characters. */
|
||||
static u_int
|
||||
fuzzy_decode(const char *tok, size_t len, struct utf8_data *out)
|
||||
{
|
||||
const char *cp = tok, *end = tok + len;
|
||||
u_int n = 0;
|
||||
|
||||
while (cp != end)
|
||||
cp = fuzzy_decode_one(cp, end, &out[n++]);
|
||||
return (n);
|
||||
}
|
||||
|
||||
/* Add the score for a fuzzy token matched at the given positions. */
|
||||
static int
|
||||
fuzzy_score_positions(const u_int *pos, u_int npos, const struct fuzzy_char *cs)
|
||||
{
|
||||
u_int i, gap, span;
|
||||
int score = 0;
|
||||
|
||||
if (npos == 0)
|
||||
return (0);
|
||||
if (pos[0] == 0)
|
||||
score += FUZZY_BONUS_START;
|
||||
else {
|
||||
if (fuzzy_is_boundary(&cs[pos[0] - 1].ud))
|
||||
score += FUZZY_BONUS_BOUNDARY;
|
||||
if (pos[0] < FUZZY_PENALTY_LEADING_MAX)
|
||||
score -= pos[0] * FUZZY_PENALTY_LEADING;
|
||||
else {
|
||||
score -= FUZZY_PENALTY_LEADING_MAX *
|
||||
FUZZY_PENALTY_LEADING;
|
||||
}
|
||||
}
|
||||
for (i = 1; i < npos; i++) {
|
||||
if (pos[i] == pos[i - 1] + 1)
|
||||
score += FUZZY_BONUS_CONSECUTIVE;
|
||||
else if (fuzzy_is_boundary(&cs[pos[i] - 1].ud))
|
||||
score += FUZZY_BONUS_BOUNDARY;
|
||||
}
|
||||
span = pos[npos - 1] - pos[0] + 1;
|
||||
gap = span - npos;
|
||||
score -= gap * FUZZY_PENALTY_GAP;
|
||||
return (score);
|
||||
}
|
||||
|
||||
/*
|
||||
* Match a token as a subsequence of the visible characters. Returns if the
|
||||
* token matches.
|
||||
*/
|
||||
static int
|
||||
fuzzy_match_fuzzy(const struct utf8_data *tok, u_int toklen,
|
||||
struct fuzzy_char *cs, u_int ncs, int fold, int *score, char *matched)
|
||||
{
|
||||
u_int pi, ci, *pos;
|
||||
int found, value;
|
||||
|
||||
if (toklen == 0 || ncs == 0)
|
||||
return (0);
|
||||
pos = xcalloc(toklen, sizeof *pos);
|
||||
|
||||
/* First find a subsequence from the start. */
|
||||
ci = 0;
|
||||
for (pi = 0; pi < toklen; pi++) {
|
||||
while (ci != ncs &&
|
||||
!fuzzy_char_equal(&tok[pi], &cs[ci].ud, fold))
|
||||
ci++;
|
||||
if (ci == ncs) {
|
||||
free(pos);
|
||||
return (0);
|
||||
}
|
||||
pos[pi] = ci++;
|
||||
}
|
||||
|
||||
/* Then compact it backwards to prefer a shorter span. */
|
||||
ci = pos[toklen - 1];
|
||||
for (pi = toklen; pi > 0; pi--) {
|
||||
found = 0;
|
||||
for (;;) {
|
||||
if (fuzzy_char_equal(&tok[pi - 1], &cs[ci].ud, fold)) {
|
||||
pos[pi - 1] = ci;
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
if (ci == 0)
|
||||
break;
|
||||
ci--;
|
||||
}
|
||||
if (!found) {
|
||||
free(pos);
|
||||
return (0);
|
||||
}
|
||||
if (pi != 1)
|
||||
ci--;
|
||||
}
|
||||
|
||||
value = fuzzy_score_positions(pos, toklen, cs);
|
||||
*score += value;
|
||||
for (pi = 0; pi < toklen; pi++)
|
||||
matched[pos[pi]] = 1;
|
||||
free(pos);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Score an exact, prefix or suffix match. */
|
||||
static int
|
||||
fuzzy_score_exact(u_int start, u_int toklen, u_int ncs,
|
||||
const struct fuzzy_char *cs, int prefix, int suffix)
|
||||
{
|
||||
int score;
|
||||
|
||||
score = FUZZY_BONUS_EXACT + toklen * FUZZY_BONUS_CONSECUTIVE;
|
||||
if (prefix)
|
||||
score += FUZZY_BONUS_PREFIX;
|
||||
if (suffix)
|
||||
score += FUZZY_BONUS_SUFFIX;
|
||||
if (start == 0)
|
||||
score += FUZZY_BONUS_START;
|
||||
else if (fuzzy_is_boundary(&cs[start - 1].ud))
|
||||
score += FUZZY_BONUS_BOUNDARY;
|
||||
if (start < FUZZY_PENALTY_LEADING_MAX)
|
||||
score -= start * FUZZY_PENALTY_LEADING;
|
||||
else
|
||||
score -= FUZZY_PENALTY_LEADING_MAX * FUZZY_PENALTY_LEADING;
|
||||
if (!prefix && !suffix)
|
||||
score -= ncs - (start + toklen);
|
||||
return (score);
|
||||
}
|
||||
|
||||
/* Match an exact, prefix or suffix term against the visible characters. */
|
||||
static int
|
||||
fuzzy_match_exact(const struct utf8_data *tok, u_int toklen,
|
||||
struct fuzzy_char *cs, u_int ncs, int fold, int prefix, int suffix,
|
||||
int *score, char *matched)
|
||||
{
|
||||
u_int start, end, i, j, best = 0;
|
||||
int ok, found = 0, value, bestscore = 0;
|
||||
|
||||
if (toklen == 0 || toklen > ncs)
|
||||
return (0);
|
||||
|
||||
if (prefix && suffix) {
|
||||
if (toklen != ncs)
|
||||
return (0);
|
||||
start = 0;
|
||||
end = 1;
|
||||
} else if (prefix) {
|
||||
start = 0;
|
||||
end = 1;
|
||||
} else if (suffix) {
|
||||
start = ncs - toklen;
|
||||
end = start + 1;
|
||||
} else {
|
||||
start = 0;
|
||||
end = ncs - toklen + 1;
|
||||
}
|
||||
|
||||
for (i = start; i < end; i++) {
|
||||
ok = 1;
|
||||
for (j = 0; j < toklen; j++) {
|
||||
if (!fuzzy_char_equal(&tok[j], &cs[i + j].ud, fold)) {
|
||||
ok = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ok)
|
||||
continue;
|
||||
value = fuzzy_score_exact(i, toklen, ncs, cs, prefix, suffix);
|
||||
if (!found || value > bestscore) {
|
||||
found = 1;
|
||||
best = i;
|
||||
bestscore = value;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
return (0);
|
||||
*score += bestscore;
|
||||
if (matched != NULL) {
|
||||
for (i = 0; i < toklen; i++)
|
||||
matched[best + i] = 1;
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Parse one term. */
|
||||
static int
|
||||
fuzzy_parse_term(const char *start, const char *end, struct fuzzy_term *term)
|
||||
{
|
||||
memset(term, 0, sizeof *term);
|
||||
if (start == end)
|
||||
return (0);
|
||||
if (*start == '!') {
|
||||
term->inverse = 1;
|
||||
start++;
|
||||
}
|
||||
if (start == end)
|
||||
return (0);
|
||||
if (*start == '\'') {
|
||||
term->exact = 1;
|
||||
start++;
|
||||
} else if (*start == '^') {
|
||||
term->exact = 1;
|
||||
term->prefix = 1;
|
||||
start++;
|
||||
}
|
||||
if (start == end)
|
||||
return (0);
|
||||
if (end[-1] == '$') {
|
||||
term->exact = 1;
|
||||
term->suffix = 1;
|
||||
end--;
|
||||
}
|
||||
if (start == end)
|
||||
return (0);
|
||||
|
||||
if (term->inverse)
|
||||
term->exact = 1;
|
||||
term->text = start;
|
||||
term->len = end - start;
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Match one parsed term. */
|
||||
static int
|
||||
fuzzy_match_term(const struct fuzzy_term *term, struct utf8_data *tok,
|
||||
struct fuzzy_char *cs, u_int ncs, int fold, int *score, char *matched)
|
||||
{
|
||||
u_int toklen;
|
||||
int value = 0, matched_term;
|
||||
|
||||
toklen = fuzzy_decode(term->text, term->len, tok);
|
||||
if (term->exact) {
|
||||
matched_term = fuzzy_match_exact(tok, toklen, cs, ncs, fold,
|
||||
term->prefix, term->suffix, &value,
|
||||
term->inverse ? NULL : matched);
|
||||
} else {
|
||||
matched_term = fuzzy_match_fuzzy(tok, toklen, cs, ncs, fold,
|
||||
&value, term->inverse ? NULL : matched);
|
||||
}
|
||||
|
||||
if (term->inverse)
|
||||
return (!matched_term);
|
||||
if (!matched_term)
|
||||
return (0);
|
||||
*score += value;
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Match one AND group of terms. */
|
||||
static int
|
||||
fuzzy_match_group(const char *start, const char *end, struct utf8_data *tok,
|
||||
struct fuzzy_char *cs, u_int ncs, int fold, int *score, char *matched)
|
||||
{
|
||||
const char *cp = start, *sp;
|
||||
struct fuzzy_term term;
|
||||
int any = 0;
|
||||
|
||||
*score = 0;
|
||||
while (cp != end) {
|
||||
while (cp != end && *cp == ' ')
|
||||
cp++;
|
||||
if (cp == end)
|
||||
break;
|
||||
sp = cp;
|
||||
while (cp != end && *cp != ' ')
|
||||
cp++;
|
||||
if (!fuzzy_parse_term(sp, cp, &term))
|
||||
return (0);
|
||||
any = 1;
|
||||
if (!fuzzy_match_term(&term, tok, cs, ncs, fold, score,
|
||||
matched))
|
||||
return (0);
|
||||
}
|
||||
return (any);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fuzzy match pattern against text, which is drawn into a region of the given
|
||||
* display width. Returns a bitstr_t of width bits with a bit set for each
|
||||
* column occupied by a matched character, or NULL if there is no match. A
|
||||
* higher returned score is better.
|
||||
*/
|
||||
bitstr_t *
|
||||
fuzzy_match(const char *pattern, const char *text, u_int width, u_int *score)
|
||||
{
|
||||
struct fuzzy_char *cs;
|
||||
char *matched = NULL, *best = NULL, *groupmatched;
|
||||
struct utf8_data *tok;
|
||||
bitstr_t *mask;
|
||||
u_int ncs, i, j, column;
|
||||
u_int widths[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
|
||||
u_int start[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
|
||||
u_int src[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
|
||||
u_int vis[STYLE_ALIGN_ABSOLUTE_CENTRE + 1];
|
||||
u_int wl, wc, wr, wa;
|
||||
const char *cp, *sp;
|
||||
int bestscore = 0, groupscore, found = 0, fold;
|
||||
|
||||
if (width == 0)
|
||||
return (NULL);
|
||||
|
||||
/* An empty query matches everything, with nothing highlighted. */
|
||||
for (cp = pattern; *cp == ' ' || *cp == '|'; cp++)
|
||||
/* nothing */;
|
||||
if (*cp == '\0') {
|
||||
if (score != NULL)
|
||||
*score = 0;
|
||||
return (bit_alloc(width));
|
||||
}
|
||||
|
||||
/* Smart-case: fold unless the pattern has an uppercase character. */
|
||||
fold = 1;
|
||||
for (cp = pattern; *cp != '\0'; cp++) {
|
||||
if (*cp >= 'A' && *cp <= 'Z') {
|
||||
fold = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Scan the text into visible characters. */
|
||||
cs = fuzzy_scan(text, &ncs, widths);
|
||||
matched = xcalloc(ncs == 0 ? 1 : ncs, sizeof *matched);
|
||||
best = xcalloc(ncs == 0 ? 1 : ncs, sizeof *best);
|
||||
tok = xreallocarray(NULL, strlen(pattern) + 1, sizeof *tok);
|
||||
|
||||
/* Match each |-separated group and keep the best-scoring one. */
|
||||
cp = pattern;
|
||||
while (*cp != '\0') {
|
||||
while (*cp == ' ' || *cp == '|')
|
||||
cp++;
|
||||
if (*cp == '\0')
|
||||
break;
|
||||
sp = cp;
|
||||
while (*cp != '\0' && *cp != '|')
|
||||
cp++;
|
||||
memset(matched, 0, ncs == 0 ? 1 : ncs);
|
||||
groupmatched = matched;
|
||||
if (fuzzy_match_group(sp, cp, tok, cs, ncs, fold,
|
||||
&groupscore, groupmatched)) {
|
||||
if (!found || groupscore > bestscore) {
|
||||
found = 1;
|
||||
bestscore = groupscore;
|
||||
memcpy(best, matched, ncs == 0 ? 1 : ncs);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(tok);
|
||||
if (!found) {
|
||||
free(best);
|
||||
free(matched);
|
||||
free(cs);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Work out the trimmed widths and start columns of each alignment,
|
||||
* mirroring format_draw_none.
|
||||
*/
|
||||
wl = widths[STYLE_ALIGN_LEFT];
|
||||
wc = widths[STYLE_ALIGN_CENTRE];
|
||||
wr = widths[STYLE_ALIGN_RIGHT];
|
||||
wa = widths[STYLE_ALIGN_ABSOLUTE_CENTRE];
|
||||
while (wl + wc + wr > width) {
|
||||
if (wc > 0)
|
||||
wc--;
|
||||
else if (wr > 0)
|
||||
wr--;
|
||||
else
|
||||
wl--;
|
||||
}
|
||||
if (wa > width)
|
||||
wa = width;
|
||||
|
||||
start[STYLE_ALIGN_LEFT] = 0;
|
||||
src[STYLE_ALIGN_LEFT] = 0;
|
||||
vis[STYLE_ALIGN_LEFT] = wl;
|
||||
|
||||
start[STYLE_ALIGN_RIGHT] = width - wr;
|
||||
src[STYLE_ALIGN_RIGHT] = widths[STYLE_ALIGN_RIGHT] - wr;
|
||||
vis[STYLE_ALIGN_RIGHT] = wr;
|
||||
|
||||
start[STYLE_ALIGN_CENTRE] =
|
||||
wl + ((width - wr) - wl) / 2 - wc / 2;
|
||||
src[STYLE_ALIGN_CENTRE] = widths[STYLE_ALIGN_CENTRE] / 2 - wc / 2;
|
||||
vis[STYLE_ALIGN_CENTRE] = wc;
|
||||
|
||||
start[STYLE_ALIGN_ABSOLUTE_CENTRE] = (width - wa) / 2;
|
||||
src[STYLE_ALIGN_ABSOLUTE_CENTRE] = 0;
|
||||
vis[STYLE_ALIGN_ABSOLUTE_CENTRE] = wa;
|
||||
|
||||
/* Set a bit for each column of each matched character. */
|
||||
mask = bit_alloc(width);
|
||||
for (i = 0; i < ncs; i++) {
|
||||
if (!best[i])
|
||||
continue;
|
||||
if (fuzzy_column(&cs[i], start, src, vis, &column) != 0)
|
||||
continue;
|
||||
for (j = 0; j < cs[i].width && column + j < width; j++)
|
||||
bit_set(mask, column + j);
|
||||
}
|
||||
|
||||
free(best);
|
||||
free(matched);
|
||||
free(cs);
|
||||
|
||||
if (score != NULL)
|
||||
*score = (bestscore < 0) ? 0 : (u_int)bestscore;
|
||||
return (mask);
|
||||
}
|
||||
@@ -45,15 +45,20 @@ grid_reader_line_length(struct grid_reader *gr)
|
||||
|
||||
/* Move cursor forward one position. */
|
||||
void
|
||||
grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all)
|
||||
grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all, int onemore)
|
||||
{
|
||||
u_int px;
|
||||
struct grid_cell gc;
|
||||
|
||||
if (all)
|
||||
px = gr->gd->sx;
|
||||
else
|
||||
else if (onemore)
|
||||
px = grid_reader_line_length(gr);
|
||||
else {
|
||||
px = grid_reader_line_length(gr);
|
||||
if (px != 0)
|
||||
px--;
|
||||
}
|
||||
|
||||
if (wrap && gr->cx >= px && gr->cy < gr->gd->hsize + gr->gd->sy - 1) {
|
||||
grid_reader_cursor_start_of_line(gr, 0);
|
||||
|
||||
264
grid.c
264
grid.c
@@ -18,6 +18,9 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <assert.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -56,6 +59,62 @@ static const struct grid_cell_entry grid_cleared_entry = {
|
||||
{ .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
|
||||
};
|
||||
|
||||
#ifdef __APPLE__
|
||||
static void
|
||||
grid_check_lines(struct grid *gd)
|
||||
{
|
||||
u_int i, j;
|
||||
|
||||
for (i = 0; i < gd->hsize + gd->sy; i++) {
|
||||
for (j = i + 1; j < gd->hsize + gd->sy; j++) {
|
||||
if (gd->linedata[i].celldata != NULL)
|
||||
assert(gd->linedata[i].celldata != gd->linedata[j].celldata);
|
||||
if (gd->linedata[i].extddata != NULL)
|
||||
assert(gd->linedata[i].extddata != gd->linedata[j].extddata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
grid_check_is_clear(struct grid *gd)
|
||||
{
|
||||
struct grid_line *gl;
|
||||
u_int yy, ny;
|
||||
|
||||
assert(gd != NULL);
|
||||
|
||||
if (gd->sy == 0) {
|
||||
assert(gd->linedata == NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(gd->linedata != NULL);
|
||||
|
||||
ny = gd->hsize + gd->sy;
|
||||
for (yy = 0; yy < ny; yy++) {
|
||||
gl = &gd->linedata[yy];
|
||||
|
||||
assert(gl->celldata == NULL);
|
||||
assert(gl->cellused == 0);
|
||||
assert(gl->cellsize == 0);
|
||||
assert(gl->extddata == NULL);
|
||||
assert(gl->extdsize == 0);
|
||||
assert(gl->flags == 0);
|
||||
assert(gl->time == 0);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void
|
||||
grid_check_lines(__unused struct grid *gd)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
grid_check_is_clear(__unused struct grid *gd)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Store cell in entry. */
|
||||
static void
|
||||
grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc,
|
||||
@@ -86,7 +145,8 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
|
||||
return (1);
|
||||
if (gc->data.size > 1 || gc->data.width > 1)
|
||||
return (1);
|
||||
if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB))
|
||||
if ((gc->fg & (COLOUR_FLAG_RGB|COLOUR_FLAG_THEME)) ||
|
||||
(gc->bg & (COLOUR_FLAG_RGB|COLOUR_FLAG_THEME)))
|
||||
return (1);
|
||||
if (gc->us != 8) /* only supports 256 or RGB */
|
||||
return (1);
|
||||
@@ -125,6 +185,8 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
|
||||
else if (gce->offset >= gl->extdsize)
|
||||
fatalx("offset too big");
|
||||
gl->flags |= GRID_LINE_EXTENDED;
|
||||
if (gc->link != 0)
|
||||
gl->flags |= GRID_LINE_HYPERLINK;
|
||||
|
||||
if (gc->flags & GRID_FLAG_TAB)
|
||||
uc = gc->data.width;
|
||||
@@ -200,7 +262,7 @@ grid_adjust_lines(struct grid *gd, u_int lines)
|
||||
|
||||
/* Copy default into a cell. */
|
||||
static void
|
||||
grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
|
||||
grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg, int moved)
|
||||
{
|
||||
struct grid_line *gl = &gd->linedata[py];
|
||||
struct grid_cell_entry *gce = &gl->celldata[px];
|
||||
@@ -209,14 +271,14 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
|
||||
int had_extd = (gce->flags & GRID_FLAG_EXTENDED);
|
||||
|
||||
memcpy(gce, &grid_cleared_entry, sizeof *gce);
|
||||
if (had_extd && old_offset < gl->extdsize) {
|
||||
if (!moved && had_extd && old_offset < gl->extdsize) {
|
||||
gce->flags |= GRID_FLAG_EXTENDED;
|
||||
gce->offset = old_offset;
|
||||
gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
|
||||
if (bg != 8)
|
||||
gee->bg = bg;
|
||||
} else if (bg != 8) {
|
||||
if (bg & COLOUR_FLAG_RGB) {
|
||||
if (bg & (COLOUR_FLAG_RGB|COLOUR_FLAG_THEME)) {
|
||||
grid_get_extended_cell(gl, gce, gce->flags);
|
||||
gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
|
||||
gee->bg = bg;
|
||||
@@ -284,14 +346,21 @@ grid_set_tab(struct grid_cell *gc, u_int width)
|
||||
static void
|
||||
grid_free_line(struct grid *gd, u_int py)
|
||||
{
|
||||
free(gd->linedata[py].celldata);
|
||||
gd->linedata[py].celldata = NULL;
|
||||
free(gd->linedata[py].extddata);
|
||||
gd->linedata[py].extddata = NULL;
|
||||
struct grid_line *gl = &gd->linedata[py];
|
||||
|
||||
#ifdef __APPLE__
|
||||
assert(gl->cellused <= gl->cellsize);
|
||||
assert(gl->extdsize == 0 || gl->extddata != NULL);
|
||||
assert(gl->cellsize == 0 || gl->celldata != NULL);
|
||||
#endif
|
||||
|
||||
free(gl->celldata);
|
||||
free(gl->extddata);
|
||||
memset(gl, 0, sizeof *gl);
|
||||
}
|
||||
|
||||
/* Free several lines. */
|
||||
static void
|
||||
void
|
||||
grid_free_lines(struct grid *gd, u_int py, u_int ny)
|
||||
{
|
||||
u_int yy;
|
||||
@@ -306,24 +375,18 @@ grid_create(u_int sx, u_int sy, u_int hlimit)
|
||||
{
|
||||
struct grid *gd;
|
||||
|
||||
gd = xmalloc(sizeof *gd);
|
||||
gd = xcalloc(1, sizeof *gd);
|
||||
gd->sx = sx;
|
||||
gd->sy = sy;
|
||||
|
||||
if (hlimit != 0)
|
||||
gd->flags = GRID_HISTORY;
|
||||
else
|
||||
gd->flags = 0;
|
||||
|
||||
gd->hscrolled = 0;
|
||||
gd->hsize = 0;
|
||||
gd->hlimit = hlimit;
|
||||
|
||||
if (gd->sy != 0)
|
||||
gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
|
||||
else
|
||||
gd->linedata = NULL;
|
||||
|
||||
grid_check_is_clear(gd);
|
||||
return (gd);
|
||||
}
|
||||
|
||||
@@ -332,9 +395,7 @@ void
|
||||
grid_destroy(struct grid *gd)
|
||||
{
|
||||
grid_free_lines(gd, 0, gd->hsize + gd->sy);
|
||||
|
||||
free(gd->linedata);
|
||||
|
||||
free(gd);
|
||||
}
|
||||
|
||||
@@ -406,6 +467,7 @@ grid_collect_history(struct grid *gd, int all)
|
||||
grid_trim_history(gd, ny);
|
||||
|
||||
gd->hsize -= ny;
|
||||
gd->scroll_collected += ny;
|
||||
if (gd->hscrolled > gd->hsize)
|
||||
gd->hscrolled = gd->hsize;
|
||||
}
|
||||
@@ -414,12 +476,14 @@ grid_collect_history(struct grid *gd, int all)
|
||||
void
|
||||
grid_remove_history(struct grid *gd, u_int ny)
|
||||
{
|
||||
u_int yy;
|
||||
u_int yy, start;
|
||||
|
||||
if (ny > gd->hsize)
|
||||
return;
|
||||
start = gd->hsize + gd->sy - ny;
|
||||
for (yy = 0; yy < ny; yy++)
|
||||
grid_free_line(gd, gd->hsize + gd->sy - 1 - yy);
|
||||
grid_free_line(gd, start + yy);
|
||||
memset(&gd->linedata[start], 0, ny * sizeof *gd->linedata);
|
||||
gd->hsize -= ny;
|
||||
}
|
||||
|
||||
@@ -441,6 +505,9 @@ grid_scroll_history(struct grid *gd, u_int bg)
|
||||
grid_compact_line(&gd->linedata[gd->hsize]);
|
||||
gd->linedata[gd->hsize].time = current_time;
|
||||
gd->hsize++;
|
||||
gd->scroll_added++;
|
||||
|
||||
grid_check_lines(gd);
|
||||
}
|
||||
|
||||
/* Clear the history. */
|
||||
@@ -451,6 +518,7 @@ grid_clear_history(struct grid *gd)
|
||||
|
||||
gd->hscrolled = 0;
|
||||
gd->hsize = 0;
|
||||
gd->scroll_generation++;
|
||||
|
||||
gd->linedata = xreallocarray(gd->linedata, gd->sy,
|
||||
sizeof *gd->linedata);
|
||||
@@ -488,6 +556,9 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
|
||||
/* Move the history offset down over the line. */
|
||||
gd->hscrolled++;
|
||||
gd->hsize++;
|
||||
gd->scroll_added++;
|
||||
|
||||
grid_check_lines(gd);
|
||||
}
|
||||
|
||||
/* Expand line to fit to cell. */
|
||||
@@ -495,7 +566,7 @@ static void
|
||||
grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
|
||||
{
|
||||
struct grid_line *gl;
|
||||
u_int xx, old_cellsize;
|
||||
u_int xx;
|
||||
|
||||
gl = &gd->linedata[py];
|
||||
if (sx <= gl->cellsize)
|
||||
@@ -508,11 +579,14 @@ grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
|
||||
else if (gd->sx > sx)
|
||||
sx = gd->sx;
|
||||
|
||||
old_cellsize = gl->cellsize;
|
||||
gl->celldata = xrecallocarray(gl->celldata, old_cellsize, sx,
|
||||
gl->celldata = xreallocarray(gl->celldata, sx,
|
||||
sizeof *gl->celldata);
|
||||
for (xx = old_cellsize; xx < sx; xx++)
|
||||
grid_clear_cell(gd, xx, py, bg);
|
||||
if (gl->cellsize < sx) {
|
||||
memset(gl->celldata + gl->cellsize, 0,
|
||||
(sx - gl->cellsize) * sizeof *gl->celldata);
|
||||
}
|
||||
for (xx = gl->cellsize; xx < sx; xx++)
|
||||
grid_clear_cell(gd, xx, py, bg, 0);
|
||||
gl->cellsize = sx;
|
||||
}
|
||||
|
||||
@@ -680,7 +754,7 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
|
||||
|
||||
grid_expand_line(gd, yy, px + ox, 8); /* default bg first */
|
||||
for (xx = px; xx < px + ox; xx++)
|
||||
grid_clear_cell(gd, xx, yy, bg);
|
||||
grid_clear_cell(gd, xx, yy, bg, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -746,6 +820,8 @@ grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
|
||||
}
|
||||
if (py != 0 && (py < dy || py >= dy + ny))
|
||||
gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
|
||||
|
||||
grid_check_lines(gd);
|
||||
}
|
||||
|
||||
/* Move a group of cells. */
|
||||
@@ -774,7 +850,7 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
|
||||
for (xx = px; xx < px + nx; xx++) {
|
||||
if (xx >= dx && xx < dx + nx)
|
||||
continue;
|
||||
grid_clear_cell(gd, xx, py, bg);
|
||||
grid_clear_cell(gd, xx, py, bg, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -784,9 +860,16 @@ grid_string_cells_fg(const struct grid_cell *gc, int *values)
|
||||
{
|
||||
size_t n;
|
||||
u_char r, g, b;
|
||||
int c;
|
||||
|
||||
n = 0;
|
||||
if (gc->fg & COLOUR_FLAG_256) {
|
||||
if (gc->fg & COLOUR_FLAG_THEME) {
|
||||
c = colour_theme_terminal_colour(gc->fg & 0xff);
|
||||
if (c == 8)
|
||||
values[n++] = 39;
|
||||
else
|
||||
values[n++] = c + 30;
|
||||
} else if (gc->fg & COLOUR_FLAG_256) {
|
||||
values[n++] = 38;
|
||||
values[n++] = 5;
|
||||
values[n++] = gc->fg & 0xff;
|
||||
@@ -833,9 +916,16 @@ grid_string_cells_bg(const struct grid_cell *gc, int *values)
|
||||
{
|
||||
size_t n;
|
||||
u_char r, g, b;
|
||||
int c;
|
||||
|
||||
n = 0;
|
||||
if (gc->bg & COLOUR_FLAG_256) {
|
||||
if (gc->bg & COLOUR_FLAG_THEME) {
|
||||
c = colour_theme_terminal_colour(gc->bg & 0xff);
|
||||
if (c == 8)
|
||||
values[n++] = 49;
|
||||
else
|
||||
values[n++] = c + 40;
|
||||
} else if (gc->bg & COLOUR_FLAG_256) {
|
||||
values[n++] = 48;
|
||||
values[n++] = 5;
|
||||
values[n++] = gc->bg & 0xff;
|
||||
@@ -882,9 +972,19 @@ grid_string_cells_us(const struct grid_cell *gc, int *values)
|
||||
{
|
||||
size_t n;
|
||||
u_char r, g, b;
|
||||
int c;
|
||||
|
||||
n = 0;
|
||||
if (gc->us & COLOUR_FLAG_256) {
|
||||
if (gc->us & COLOUR_FLAG_THEME) {
|
||||
c = colour_theme_terminal_colour(gc->us & 0xff);
|
||||
if (c == 8)
|
||||
values[n++] = 59;
|
||||
else {
|
||||
values[n++] = 58;
|
||||
values[n++] = 5;
|
||||
values[n++] = c;
|
||||
}
|
||||
} else if (gc->us & COLOUR_FLAG_256) {
|
||||
values[n++] = 58;
|
||||
values[n++] = 5;
|
||||
values[n++] = gc->us & 0xff;
|
||||
@@ -1114,7 +1214,7 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
|
||||
if (gc.flags & GRID_FLAG_PADDING)
|
||||
continue;
|
||||
|
||||
if (flags & GRID_STRING_WITH_SEQUENCES) {
|
||||
if (lastgc != NULL && (flags & GRID_STRING_WITH_SEQUENCES)) {
|
||||
grid_string_cells_code(*lastgc, &gc, code, sizeof code,
|
||||
flags, s, &has_link);
|
||||
codelen = strlen(code);
|
||||
@@ -1211,6 +1311,8 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
|
||||
sy++;
|
||||
dy++;
|
||||
}
|
||||
|
||||
grid_check_lines(dst);
|
||||
}
|
||||
|
||||
/* Mark line as dead. */
|
||||
@@ -1506,6 +1608,9 @@ grid_reflow(struct grid *gd, u_int sx)
|
||||
free(gd->linedata);
|
||||
gd->linedata = target->linedata;
|
||||
free(target);
|
||||
gd->scroll_generation++;
|
||||
|
||||
grid_check_lines(gd);
|
||||
}
|
||||
|
||||
/* Convert to position based on wrapped lines. */
|
||||
@@ -1607,3 +1712,98 @@ grid_in_set(struct grid *gd, u_int px, u_int py, const char *set)
|
||||
return (0);
|
||||
return (utf8_cstrhas(set, &gc.data));
|
||||
}
|
||||
|
||||
/* Line flags to string. */
|
||||
const char *
|
||||
grid_line_flags_string(int flags)
|
||||
{
|
||||
static char s[128];
|
||||
|
||||
*s = '\0';
|
||||
if (flags & GRID_LINE_WRAPPED)
|
||||
strlcat(s, "WRAPPED,", sizeof s);
|
||||
if (flags & GRID_LINE_EXTENDED)
|
||||
strlcat(s, "EXTENDED,", sizeof s);
|
||||
if (flags & GRID_LINE_DEAD)
|
||||
strlcat(s, "DEAD,", sizeof s);
|
||||
if (flags & GRID_LINE_START_PROMPT)
|
||||
strlcat(s, "START_PROMPT,", sizeof s);
|
||||
if (flags & GRID_LINE_START_OUTPUT)
|
||||
strlcat(s, "START_OUTPUT,", sizeof s);
|
||||
if (flags & GRID_LINE_HYPERLINK)
|
||||
strlcat(s, "HYPERLINK,", sizeof s);
|
||||
if (*s == '\0')
|
||||
return ("NONE");
|
||||
s[strlen(s) - 1] = '\0';
|
||||
return (s);
|
||||
}
|
||||
|
||||
/* Cell flags to string. */
|
||||
const char *
|
||||
grid_cell_flags_string(int flags)
|
||||
{
|
||||
static char s[128];
|
||||
|
||||
*s = '\0';
|
||||
if (flags & GRID_FLAG_FG256)
|
||||
strlcat(s, "FG256,", sizeof s);
|
||||
if (flags & GRID_FLAG_BG256)
|
||||
strlcat(s, "BG256,", sizeof s);
|
||||
if (flags & GRID_FLAG_PADDING)
|
||||
strlcat(s, "PADDING,", sizeof s);
|
||||
if (flags & GRID_FLAG_EXTENDED)
|
||||
strlcat(s, "EXTENDED,", sizeof s);
|
||||
if (flags & GRID_FLAG_SELECTED)
|
||||
strlcat(s, "SELECTED,", sizeof s);
|
||||
if (flags & GRID_FLAG_CLEARED)
|
||||
strlcat(s, "CLEARED,", sizeof s);
|
||||
if (flags & GRID_FLAG_TAB)
|
||||
strlcat(s, "TAB,", sizeof s);
|
||||
if (flags & GRID_FLAG_NOPALETTE)
|
||||
strlcat(s, "NOPALETTE,", sizeof s);
|
||||
if (*s == '\0')
|
||||
return ("NONE");
|
||||
s[strlen(s) - 1] = '\0';
|
||||
return (s);
|
||||
}
|
||||
|
||||
/* Cell attributes to string. */
|
||||
const char *
|
||||
grid_cell_attr_string(int attr)
|
||||
{
|
||||
static char s[256];
|
||||
|
||||
*s = '\0';
|
||||
if (attr & GRID_ATTR_CHARSET)
|
||||
strlcat(s, "CHARSET,", sizeof s);
|
||||
if (attr & GRID_ATTR_BRIGHT)
|
||||
strlcat(s, "BRIGHT,", sizeof s);
|
||||
if (attr & GRID_ATTR_DIM)
|
||||
strlcat(s, "DIM,", sizeof s);
|
||||
if (attr & GRID_ATTR_UNDERSCORE)
|
||||
strlcat(s, "UNDERSCORE,", sizeof s);
|
||||
if (attr & GRID_ATTR_BLINK)
|
||||
strlcat(s, "BLINK,", sizeof s);
|
||||
if (attr & GRID_ATTR_REVERSE)
|
||||
strlcat(s, "REVERSE,", sizeof s);
|
||||
if (attr & GRID_ATTR_HIDDEN)
|
||||
strlcat(s, "HIDDEN,", sizeof s);
|
||||
if (attr & GRID_ATTR_ITALICS)
|
||||
strlcat(s, "ITALICS,", sizeof s);
|
||||
if (attr & GRID_ATTR_STRIKETHROUGH)
|
||||
strlcat(s, "STRIKETHROUGH,", sizeof s);
|
||||
if (attr & GRID_ATTR_UNDERSCORE_2)
|
||||
strlcat(s, "UNDERSCORE_2,", sizeof s);
|
||||
if (attr & GRID_ATTR_UNDERSCORE_3)
|
||||
strlcat(s, "UNDERSCORE_3,", sizeof s);
|
||||
if (attr & GRID_ATTR_UNDERSCORE_4)
|
||||
strlcat(s, "UNDERSCORE_4,", sizeof s);
|
||||
if (attr & GRID_ATTR_UNDERSCORE_5)
|
||||
strlcat(s, "UNDERSCORE_5,", sizeof s);
|
||||
if (attr & GRID_ATTR_OVERLINE)
|
||||
strlcat(s, "OVERLINE,", sizeof s);
|
||||
if (*s == '\0')
|
||||
return ("NONE");
|
||||
s[strlen(s) - 1] = '\0';
|
||||
return (s);
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
*/
|
||||
|
||||
#define MAX_HYPERLINKS 5000
|
||||
#define MAX_HYPERLINK_URI 1024
|
||||
|
||||
static long long hyperlinks_next_external_id = 1;
|
||||
static u_int global_hyperlinks_count;
|
||||
@@ -145,9 +146,13 @@ hyperlinks_put(struct hyperlinks *hl, const char *uri_in,
|
||||
internal_id_in = "";
|
||||
|
||||
utf8_stravis(&uri, uri_in, VIS_OCTAL|VIS_CSTYLE);
|
||||
utf8_stravis(&internal_id, internal_id_in, VIS_OCTAL|VIS_CSTYLE);
|
||||
if (strlen(uri) > MAX_HYPERLINK_URI) {
|
||||
free(uri);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (*internal_id_in != '\0') {
|
||||
utf8_stravis(&internal_id, internal_id_in, VIS_OCTAL|VIS_CSTYLE);
|
||||
if (*internal_id != '\0') {
|
||||
find.uri = uri;
|
||||
find.internal_id = internal_id;
|
||||
|
||||
|
||||
325
image-kitty.c
325
image-kitty.c
@@ -1,325 +0,0 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 Thomas Adam <thomas@xteddy.org>
|
||||
*
|
||||
* 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"
|
||||
|
||||
/*
|
||||
* kitty_image stores the raw decoded pixel data and metadata from a kitty
|
||||
* graphics protocol APC sequence. It is used to re-emit the sequence to the
|
||||
* outer terminal on redraw.
|
||||
*/
|
||||
struct kitty_image {
|
||||
/* Control-data fields parsed from the APC sequence. */
|
||||
char action; /* a=: 'T'=transmit+display, 't', 'p', 'd' */
|
||||
u_int format; /* f=: 32=RGBA, 24=RGB, 100=PNG */
|
||||
char medium; /* t=: 'd'=direct, 'f'=file, 't'=tmp, 's'=shm */
|
||||
u_int pixel_w; /* s=: source image pixel width */
|
||||
u_int pixel_h; /* v=: source image pixel height */
|
||||
u_int cols; /* c=: display columns (0=auto) */
|
||||
u_int rows; /* r=: display rows (0=auto) */
|
||||
u_int image_id; /* i=: image id (0=unassigned) */
|
||||
u_int image_num; /* I=: image number */
|
||||
u_int placement_id; /* p=: placement id */
|
||||
u_int more; /* m=: 1=more chunks coming, 0=last */
|
||||
u_int quiet; /* q=: suppress responses */
|
||||
int z_index; /* z=: z-index */
|
||||
char compression; /* o=: 'z'=zlib, 0=none */
|
||||
char delete_what; /* d=: delete target (used with a=d) */
|
||||
|
||||
/* Cell size at the time of parsing (from the owning window). */
|
||||
u_int xpixel;
|
||||
u_int ypixel;
|
||||
|
||||
/* Original base64-encoded payload (concatenated across all chunks). */
|
||||
char *encoded;
|
||||
size_t encodedlen;
|
||||
|
||||
char *ctrl;
|
||||
size_t ctrllen;
|
||||
};
|
||||
|
||||
/*
|
||||
* Parse control-data key=value pairs from a kitty APC sequence.
|
||||
* Format: key=value,key=value,...
|
||||
*/
|
||||
static int
|
||||
kitty_parse_control(const char *ctrl, size_t ctrllen, struct kitty_image *ki)
|
||||
{
|
||||
const char *p = ctrl, *end = ctrl + ctrllen, *errstr;
|
||||
char key[4], val[32];
|
||||
size_t klen, vlen;
|
||||
|
||||
while (p < end) {
|
||||
klen = 0;
|
||||
while (p < end && *p != '=' && klen < sizeof(key) - 1)
|
||||
key[klen++] = *p++;
|
||||
key[klen] = '\0';
|
||||
if (p >= end || *p != '=')
|
||||
return (-1);
|
||||
p++;
|
||||
|
||||
vlen = 0;
|
||||
while (p < end && *p != ',' && vlen < sizeof(val) - 1)
|
||||
val[vlen++] = *p++;
|
||||
val[vlen] = '\0';
|
||||
if (p < end && *p == ',')
|
||||
p++;
|
||||
|
||||
if (klen != 1)
|
||||
continue;
|
||||
|
||||
switch (key[0]) {
|
||||
case 'a':
|
||||
ki->action = val[0];
|
||||
break;
|
||||
case 'f':
|
||||
ki->format = strtonum(val, 0, UINT_MAX, &errstr);
|
||||
if (errstr != NULL)
|
||||
return (-1);
|
||||
break;
|
||||
case 't':
|
||||
ki->medium = val[0];
|
||||
break;
|
||||
case 's':
|
||||
ki->pixel_w = strtonum(val, 0, UINT_MAX, &errstr);
|
||||
if (errstr != NULL)
|
||||
return (-1);
|
||||
break;
|
||||
case 'v':
|
||||
ki->pixel_h = strtonum(val, 0, UINT_MAX, &errstr);
|
||||
if (errstr != NULL)
|
||||
return (-1);
|
||||
break;
|
||||
case 'c':
|
||||
ki->cols = strtonum(val, 0, UINT_MAX, &errstr);
|
||||
if (errstr != NULL)
|
||||
return (-1);
|
||||
break;
|
||||
case 'r':
|
||||
ki->rows = strtonum(val, 0, UINT_MAX, &errstr);
|
||||
if (errstr != NULL)
|
||||
return (-1);
|
||||
break;
|
||||
case 'i':
|
||||
ki->image_id = strtonum(val, 0, UINT_MAX, &errstr);
|
||||
if (errstr != NULL)
|
||||
return (-1);
|
||||
break;
|
||||
case 'I':
|
||||
ki->image_num = strtonum(val, 0, UINT_MAX, &errstr);
|
||||
if (errstr != NULL)
|
||||
return (-1);
|
||||
break;
|
||||
case 'p':
|
||||
ki->placement_id = strtonum(val, 0, UINT_MAX, &errstr);
|
||||
if (errstr != NULL)
|
||||
return (-1);
|
||||
break;
|
||||
case 'm':
|
||||
ki->more = strtonum(val, 0, UINT_MAX, &errstr);
|
||||
if (errstr != NULL)
|
||||
return (-1);
|
||||
break;
|
||||
case 'q':
|
||||
ki->quiet = strtonum(val, 0, UINT_MAX, &errstr);
|
||||
if (errstr != NULL)
|
||||
return (-1);
|
||||
break;
|
||||
case 'z':
|
||||
ki->z_index = strtonum(val, INT_MIN, INT_MAX, &errstr);
|
||||
if (errstr != NULL)
|
||||
return (-1);
|
||||
break;
|
||||
case 'o':
|
||||
ki->compression = val[0];
|
||||
break;
|
||||
case 'd':
|
||||
ki->delete_what = val[0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a kitty APC body (after the leading 'G').
|
||||
* Stores the original control string and base64 payload verbatim for
|
||||
* pass-through re-emission to the outer terminal.
|
||||
*/
|
||||
struct kitty_image *
|
||||
kitty_parse(const u_char *buf, size_t len, u_int xpixel, u_int ypixel)
|
||||
{
|
||||
struct kitty_image *ki;
|
||||
const u_char *semi;
|
||||
const char *ctrl;
|
||||
size_t ctrllen, paylen;
|
||||
|
||||
if (len == 0)
|
||||
return (NULL);
|
||||
|
||||
semi = memchr(buf, ';', len);
|
||||
if (semi != NULL) {
|
||||
ctrl = (const char *)buf;
|
||||
ctrllen = semi - buf;
|
||||
paylen = len - ctrllen - 1;
|
||||
} else {
|
||||
ctrl = (const char *)buf;
|
||||
ctrllen = len;
|
||||
paylen = 0;
|
||||
}
|
||||
|
||||
ki = xcalloc(1, sizeof *ki);
|
||||
ki->xpixel = xpixel;
|
||||
ki->ypixel = ypixel;
|
||||
ki->action = 'T';
|
||||
ki->format = 32;
|
||||
ki->medium = 'd';
|
||||
|
||||
if (kitty_parse_control(ctrl, ctrllen, ki) != 0) {
|
||||
free(ki);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (paylen > 0) {
|
||||
ki->encoded = xmalloc(paylen + 1);
|
||||
memcpy(ki->encoded, semi + 1, paylen);
|
||||
ki->encoded[paylen] = '\0';
|
||||
ki->encodedlen = paylen;
|
||||
}
|
||||
|
||||
ki->ctrl = xmalloc(ctrllen + 1);
|
||||
memcpy(ki->ctrl, ctrl, ctrllen);
|
||||
ki->ctrl[ctrllen] = '\0';
|
||||
ki->ctrllen = ctrllen;
|
||||
|
||||
return (ki);
|
||||
}
|
||||
|
||||
void
|
||||
kitty_free(struct kitty_image *ki)
|
||||
{
|
||||
if (ki == NULL)
|
||||
return;
|
||||
free(ki->encoded);
|
||||
free(ki->ctrl);
|
||||
free(ki);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the size in cells of a kitty image. If cols/rows are 0 (auto),
|
||||
* calculate from pixel dimensions. Returns size via sx/sy pointers.
|
||||
*/
|
||||
void
|
||||
kitty_size_in_cells(struct kitty_image *ki, u_int *sx, u_int *sy)
|
||||
{
|
||||
*sx = ki->cols;
|
||||
*sy = ki->rows;
|
||||
|
||||
/*
|
||||
* If cols/rows are 0, they mean "auto" - calculate from
|
||||
* pixel dimensions.
|
||||
*/
|
||||
if (*sx == 0 && ki->pixel_w > 0 && ki->xpixel > 0) {
|
||||
*sx = (ki->pixel_w + ki->xpixel - 1) / ki->xpixel;
|
||||
}
|
||||
if (*sy == 0 && ki->pixel_h > 0 && ki->ypixel > 0) {
|
||||
*sy = (ki->pixel_h + ki->ypixel - 1) / ki->ypixel;
|
||||
}
|
||||
|
||||
/* If still 0, use a reasonable default */
|
||||
if (*sx == 0)
|
||||
*sx = 10;
|
||||
if (*sy == 0)
|
||||
*sy = 10;
|
||||
}
|
||||
|
||||
char
|
||||
kitty_get_action(struct kitty_image *ki)
|
||||
{
|
||||
return (ki->action);
|
||||
}
|
||||
|
||||
u_int
|
||||
kitty_get_image_id(struct kitty_image *ki)
|
||||
{
|
||||
return (ki->image_id);
|
||||
}
|
||||
|
||||
u_int
|
||||
kitty_get_rows(struct kitty_image *ki)
|
||||
{
|
||||
return (ki->rows);
|
||||
}
|
||||
|
||||
/*
|
||||
* Serialize a kitty_image back into an APC escape sequence for transmission
|
||||
* to the terminal. This recreates the original command that was parsed.
|
||||
*/
|
||||
char *
|
||||
kitty_print(struct kitty_image *ki, size_t *outlen)
|
||||
{
|
||||
char *out;
|
||||
size_t total, pos;
|
||||
|
||||
if (ki == NULL || ki->ctrl == NULL)
|
||||
return (NULL);
|
||||
|
||||
/* Calculate total length: ESC _ G + ctrl + ; + encoded + ESC \ */
|
||||
total = 3 + ki->ctrllen; /* \033_G + ctrl */
|
||||
if (ki->encoded != NULL && ki->encodedlen > 0) {
|
||||
total += 1 + ki->encodedlen; /* ; + encoded */
|
||||
}
|
||||
total += 2; /* \033\\ */
|
||||
|
||||
out = xmalloc(total + 1);
|
||||
*outlen = total;
|
||||
|
||||
/* Build the sequence */
|
||||
pos = 0;
|
||||
memcpy(out + pos, "\033_G", 3);
|
||||
pos += 3;
|
||||
memcpy(out + pos, ki->ctrl, ki->ctrllen);
|
||||
pos += ki->ctrllen;
|
||||
|
||||
if (ki->encoded != NULL && ki->encodedlen > 0) {
|
||||
out[pos++] = ';';
|
||||
memcpy(out + pos, ki->encoded, ki->encodedlen);
|
||||
pos += ki->encodedlen;
|
||||
}
|
||||
|
||||
memcpy(out + pos, "\033\\", 2);
|
||||
pos += 2;
|
||||
out[pos] = '\0';
|
||||
|
||||
return (out);
|
||||
}
|
||||
|
||||
char *
|
||||
kitty_delete_all(size_t *outlen)
|
||||
{
|
||||
char *out;
|
||||
|
||||
out = xstrdup("\033_Ga=d,d=a\033\\");
|
||||
*outlen = strlen(out);
|
||||
return (out);
|
||||
}
|
||||
@@ -122,19 +122,13 @@ sixel_set_pixel(struct sixel_image *si, u_int x, u_int y, u_int c)
|
||||
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++;
|
||||
if (ch & (1 << i)) {
|
||||
if (sixel_set_pixel(si, si->dx, si->dy + i, si->dc))
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
@@ -357,7 +351,7 @@ sixel_parse(const char *buf, size_t len, u_int p2, u_int xpixel, u_int ypixel)
|
||||
return (si);
|
||||
|
||||
bad:
|
||||
free(si);
|
||||
sixel_free(si);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
@@ -456,11 +450,11 @@ sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
|
||||
|
||||
new->set_ra = si->set_ra;
|
||||
/* subtract offset */
|
||||
new->ra_x = new->ra_x > pox ? new->ra_x - pox : 0;
|
||||
new->ra_y = new->ra_y > poy ? new->ra_y - poy : 0;
|
||||
new->ra_x = si->ra_x > pox ? si->ra_x - pox : 0;
|
||||
new->ra_y = si->ra_y > poy ? si->ra_y - poy : 0;
|
||||
/* clamp to size */
|
||||
new->ra_x = si->ra_x < psx ? si->ra_x : psx;
|
||||
new->ra_y = si->ra_y < psy ? si->ra_y : psy;
|
||||
new->ra_x = new->ra_x < psx ? new->ra_x : psx;
|
||||
new->ra_y = new->ra_y < psy ? new->ra_y : psy;
|
||||
/* resize */
|
||||
new->ra_x = new->ra_x * xpixel / si->xpixel;
|
||||
new->ra_y = new->ra_y * ypixel / si->ypixel;
|
||||
@@ -487,9 +481,9 @@ static void
|
||||
sixel_print_add(char **buf, size_t *len, size_t *used, const char *s,
|
||||
size_t slen)
|
||||
{
|
||||
if (*used + slen >= *len + 1) {
|
||||
while (*used + slen >= *len + 1) {
|
||||
*buf = xreallocarray(*buf, 2, *len);
|
||||
(*len) *= 2;
|
||||
*buf = xrealloc(*buf, *len);
|
||||
}
|
||||
memcpy(*buf + *used, s, slen);
|
||||
(*used) += slen;
|
||||
@@ -592,7 +586,7 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
|
||||
len = 8192;
|
||||
buf = xmalloc(len);
|
||||
|
||||
tmplen = xsnprintf(tmp, sizeof tmp, "\033P0;%uq", si->p2);
|
||||
tmplen = xsnprintf(tmp, sizeof tmp, "\033P9;%uq", si->p2);
|
||||
sixel_print_add(&buf, &len, &used, tmp, tmplen);
|
||||
|
||||
if (si->set_ra) {
|
||||
|
||||
119
image.c
119
image.c
@@ -53,30 +53,13 @@ image_log(struct image *im, const char* from, const char* fmt, ...)
|
||||
static void
|
||||
image_free(struct image *im)
|
||||
{
|
||||
struct screen *s = im->s;
|
||||
|
||||
image_log(im, __func__, NULL);
|
||||
|
||||
TAILQ_REMOVE(&all_images, im, all_entry);
|
||||
all_images_count--;
|
||||
|
||||
TAILQ_REMOVE(&s->images, im, entry);
|
||||
|
||||
switch (im->type) {
|
||||
#ifdef ENABLE_SIXEL_IMAGES
|
||||
case IMAGE_SIXEL:
|
||||
sixel_free(im->data.sixel);
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_KITTY_IMAGES
|
||||
case IMAGE_KITTY:
|
||||
kitty_free(im->data.kitty);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
TAILQ_REMOVE(im->list, im, entry);
|
||||
sixel_free(im->data);
|
||||
free(im->fallback);
|
||||
free(im);
|
||||
}
|
||||
@@ -96,30 +79,13 @@ image_free_all(struct screen *s)
|
||||
|
||||
/* Create text placeholder for an image. */
|
||||
static void
|
||||
image_fallback(char **ret, enum image_type type, u_int sx, u_int sy)
|
||||
image_fallback(char **ret, u_int sx, u_int sy)
|
||||
{
|
||||
char *buf, *label;
|
||||
u_int py, size, lsize;
|
||||
const char *type_name;
|
||||
|
||||
switch (type) {
|
||||
#ifdef ENABLE_SIXEL_IMAGES
|
||||
case IMAGE_SIXEL:
|
||||
type_name = "SIXEL";
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_KITTY_IMAGES
|
||||
case IMAGE_KITTY:
|
||||
type_name = "KITTY";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
type_name = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
|
||||
/* Allocate first line. */
|
||||
lsize = xasprintf(&label, "%s IMAGE (%ux%u)\r\n", type_name, sx, sy) + 1;
|
||||
lsize = xasprintf(&label, "SIXEL IMAGE (%ux%u)\r\n", sx, sy) + 1;
|
||||
if (sx < lsize - 3)
|
||||
size = lsize - 1;
|
||||
else
|
||||
@@ -154,39 +120,23 @@ image_fallback(char **ret, enum image_type type, u_int sx, u_int sy)
|
||||
}
|
||||
|
||||
struct image*
|
||||
image_store(struct screen *s, enum image_type type, void *data)
|
||||
image_store(struct screen *s, struct sixel_image *si)
|
||||
{
|
||||
struct image *im;
|
||||
|
||||
im = xcalloc(1, sizeof *im);
|
||||
|
||||
im->type = type;
|
||||
im->s = s;
|
||||
im->data = si;
|
||||
|
||||
im->px = s->cx;
|
||||
im->py = s->cy;
|
||||
sixel_size_in_cells(si, &im->sx, &im->sy);
|
||||
|
||||
switch (type) {
|
||||
#ifdef ENABLE_SIXEL_IMAGES
|
||||
case IMAGE_SIXEL:
|
||||
im->data.sixel = data;
|
||||
sixel_size_in_cells(im->data.sixel, &im->sx, &im->sy);
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_KITTY_IMAGES
|
||||
case IMAGE_KITTY:
|
||||
im->data.kitty = data;
|
||||
kitty_size_in_cells(im->data.kitty, &im->sx, &im->sy);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
image_fallback(&im->fallback, type, im->sx, im->sy);
|
||||
image_fallback(&im->fallback, im->sx, im->sy);
|
||||
|
||||
image_log(im, __func__, NULL);
|
||||
TAILQ_INSERT_TAIL(&s->images, im, entry);
|
||||
im->list = &s->images;
|
||||
TAILQ_INSERT_TAIL(im->list, im, entry);
|
||||
|
||||
TAILQ_INSERT_TAIL(&all_images, im, all_entry);
|
||||
if (++all_images_count == MAX_IMAGE_COUNT)
|
||||
@@ -237,10 +187,8 @@ image_scroll_up(struct screen *s, u_int lines)
|
||||
{
|
||||
struct image *im, *im1;
|
||||
int redraw = 0;
|
||||
#ifdef ENABLE_SIXEL_IMAGES
|
||||
struct sixel_image *new;
|
||||
u_int sx, sy;
|
||||
#endif
|
||||
struct sixel_image *new;
|
||||
|
||||
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
|
||||
if (im->py >= lines) {
|
||||
@@ -255,43 +203,20 @@ image_scroll_up(struct screen *s, u_int lines)
|
||||
redraw = 1;
|
||||
continue;
|
||||
}
|
||||
sx = im->sx;
|
||||
sy = (im->py + im->sy) - lines;
|
||||
image_log(im, __func__, "3, lines=%u, sy=%u", lines, sy);
|
||||
|
||||
/* Image is partially scrolled off - need to crop it */
|
||||
switch (im->type) {
|
||||
#ifdef ENABLE_SIXEL_IMAGES
|
||||
case IMAGE_SIXEL:
|
||||
sx = im->sx;
|
||||
sy = (im->py + im->sy) - lines;
|
||||
image_log(im, __func__, "sixel, lines=%u, sy=%u",
|
||||
lines, sy);
|
||||
new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1);
|
||||
sixel_free(im->data);
|
||||
im->data = new;
|
||||
|
||||
new = sixel_scale(im->data.sixel, 0, 0, 0, im->sy - sy,
|
||||
sx, sy, 1);
|
||||
sixel_free(im->data.sixel);
|
||||
im->data.sixel = new;
|
||||
im->py = 0;
|
||||
sixel_size_in_cells(im->data, &im->sx, &im->sy);
|
||||
|
||||
im->py = 0;
|
||||
sixel_size_in_cells(im->data.sixel, &im->sx, &im->sy);
|
||||
|
||||
free(im->fallback);
|
||||
image_fallback(&im->fallback, im->type, im->sx, im->sy);
|
||||
redraw = 1;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_KITTY_IMAGES
|
||||
case IMAGE_KITTY:
|
||||
/*
|
||||
* For kitty images, we can't rescale - the terminal
|
||||
* owns the placement. Just adjust position and let
|
||||
* the terminal handle clipping.
|
||||
*/
|
||||
im->py = 0;
|
||||
redraw = 1;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
free(im->fallback);
|
||||
image_fallback(&im->fallback, im->sx, im->sy);
|
||||
redraw = 1;
|
||||
}
|
||||
return (redraw);
|
||||
}
|
||||
|
||||
@@ -483,7 +483,7 @@ input_key_vt10x(struct bufferevent *bev, key_code key)
|
||||
{
|
||||
struct utf8_data ud;
|
||||
key_code onlykey;
|
||||
char *p;
|
||||
const char *p;
|
||||
static const char *standard_map[2] = {
|
||||
"1!9(0)=+;:'\",<.>/-8? 2",
|
||||
"119900=+;;'',,..\x1f\x1f\x7f\x7f\0\0",
|
||||
@@ -673,8 +673,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
|
||||
}
|
||||
|
||||
/* Ignore internal function key codes. */
|
||||
if ((key >= KEYC_BASE && key < KEYC_BASE_END) ||
|
||||
(key >= KEYC_USER && key < KEYC_USER_END)) {
|
||||
if (KEYC_IS_USER(key) || KEYC_IS_SPECIAL(key) || KEYC_IS_MOUSE(key)) {
|
||||
log_debug("%s: ignoring key 0x%llx", __func__, key);
|
||||
return (0);
|
||||
}
|
||||
@@ -807,7 +806,7 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m)
|
||||
return;
|
||||
if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
|
||||
return;
|
||||
if (!window_pane_visible(wp))
|
||||
if (!window_pane_is_visible(wp))
|
||||
return;
|
||||
if (!input_key_get_mouse(s, m, x, y, &buf, &len))
|
||||
return;
|
||||
|
||||
302
input.c
302
input.c
@@ -64,7 +64,7 @@ struct input_request {
|
||||
|
||||
enum input_request_type type;
|
||||
uint64_t t;
|
||||
enum input_end_type end;
|
||||
enum input_end_type end;
|
||||
|
||||
int idx;
|
||||
void *data;
|
||||
@@ -145,12 +145,11 @@ struct input_ctx {
|
||||
*/
|
||||
struct evbuffer *since_ground;
|
||||
struct event ground_timer;
|
||||
|
||||
};
|
||||
|
||||
/* Helper functions. */
|
||||
struct input_transition;
|
||||
static void input_request_timer_callback(int, short, void *);
|
||||
static void input_request_timer_callback(int, short, void *);
|
||||
static void input_start_request_timer(struct input_ctx *);
|
||||
static struct input_request *input_make_request(struct input_ctx *,
|
||||
enum input_request_type);
|
||||
@@ -165,6 +164,7 @@ 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_9(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 *);
|
||||
@@ -181,7 +181,6 @@ static void input_ground(struct input_ctx *);
|
||||
static void input_enter_dcs(struct input_ctx *);
|
||||
static void input_enter_osc(struct input_ctx *);
|
||||
static void input_exit_osc(struct input_ctx *);
|
||||
static int input_da1_has_sixel(struct input_ctx *);
|
||||
static void input_enter_apc(struct input_ctx *);
|
||||
static void input_exit_apc(struct input_ctx *);
|
||||
static void input_enter_rename(struct input_ctx *);
|
||||
@@ -282,6 +281,7 @@ enum input_csi_type {
|
||||
INPUT_CSI_IL,
|
||||
INPUT_CSI_MODOFF,
|
||||
INPUT_CSI_MODSET,
|
||||
INPUT_CSI_QUERY,
|
||||
INPUT_CSI_QUERY_PRIVATE,
|
||||
INPUT_CSI_RCP,
|
||||
INPUT_CSI_REP,
|
||||
@@ -337,6 +337,7 @@ static const struct input_table_entry input_csi_table[] = {
|
||||
{ 'n', "", INPUT_CSI_DSR },
|
||||
{ 'n', ">", INPUT_CSI_MODOFF },
|
||||
{ 'n', "?", INPUT_CSI_DSR_PRIVATE },
|
||||
{ 'p', "$", INPUT_CSI_QUERY },
|
||||
{ 'p', "?$", INPUT_CSI_QUERY_PRIVATE },
|
||||
{ 'q', " ", INPUT_CSI_DECSCUSR },
|
||||
{ 'q', ">", INPUT_CSI_XDA },
|
||||
@@ -951,7 +952,7 @@ input_set_state(struct input_ctx *ictx, const struct input_transition *itr)
|
||||
|
||||
/* Parse data. */
|
||||
static void
|
||||
input_parse(struct input_ctx *ictx, u_char *buf, size_t len)
|
||||
input_parse(struct input_ctx *ictx, const u_char *buf, size_t len)
|
||||
{
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
const struct input_state *state = NULL;
|
||||
@@ -1022,7 +1023,7 @@ input_parse_pane(struct window_pane *wp)
|
||||
|
||||
/* Parse given input. */
|
||||
void
|
||||
input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len)
|
||||
input_parse_buffer(struct window_pane *wp, const u_char *buf, size_t len)
|
||||
{
|
||||
struct input_ctx *ictx = wp->ictx;
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
@@ -1053,7 +1054,7 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len)
|
||||
/* Parse given input for screen. */
|
||||
void
|
||||
input_parse_screen(struct input_ctx *ictx, struct screen *s,
|
||||
screen_write_init_ctx_cb cb, void *arg, u_char *buf, size_t len)
|
||||
screen_write_init_ctx_cb cb, void *arg, const u_char *buf, size_t len)
|
||||
{
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
|
||||
@@ -1364,7 +1365,7 @@ input_esc_dispatch(struct input_ctx *ictx)
|
||||
{
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
struct screen *s = sctx->s;
|
||||
struct input_table_entry *entry;
|
||||
const struct input_table_entry *entry;
|
||||
|
||||
if (ictx->flags & INPUT_DISCARD)
|
||||
return (0);
|
||||
@@ -1383,10 +1384,6 @@ input_esc_dispatch(struct input_ctx *ictx)
|
||||
input_reset_cell(ictx);
|
||||
screen_write_reset(sctx);
|
||||
screen_write_fullredraw(sctx);
|
||||
#ifdef ENABLE_KITTY_IMAGES
|
||||
if (ictx->wp != NULL)
|
||||
tty_kitty_delete_all_pane(ictx->wp);
|
||||
#endif
|
||||
break;
|
||||
case INPUT_ESC_IND:
|
||||
screen_write_linefeed(sctx, 0, ictx->cell.cell.bg);
|
||||
@@ -1442,12 +1439,12 @@ input_esc_dispatch(struct input_ctx *ictx)
|
||||
static int
|
||||
input_csi_dispatch(struct input_ctx *ictx)
|
||||
{
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
struct screen *s = sctx->s;
|
||||
struct input_table_entry *entry;
|
||||
struct options *oo;
|
||||
int i, n, m, ek, set, p;
|
||||
u_int cx, bg = ictx->cell.cell.bg;
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
struct screen *s = sctx->s;
|
||||
const struct input_table_entry *entry;
|
||||
struct options *oo;
|
||||
int i, n, m, ek, set, p;
|
||||
u_int cx, bg = ictx->cell.cell.bg;
|
||||
|
||||
if (ictx->flags & INPUT_DISCARD)
|
||||
return (0);
|
||||
@@ -1562,10 +1559,11 @@ input_csi_dispatch(struct input_ctx *ictx)
|
||||
case -1:
|
||||
break;
|
||||
case 0:
|
||||
if (input_da1_has_sixel(ictx))
|
||||
input_reply(ictx, 1, "\033[?1;2;4c");
|
||||
else
|
||||
input_reply(ictx, 1, "\033[?1;2c");
|
||||
#ifdef ENABLE_SIXEL
|
||||
input_reply(ictx, 1, "\033[?1;2;4c");
|
||||
#else
|
||||
input_reply(ictx, 1, "\033[?1;2c");
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
log_debug("%s: unknown '%c'", __func__, ictx->ch);
|
||||
@@ -1612,8 +1610,34 @@ input_csi_dispatch(struct input_ctx *ictx)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case INPUT_CSI_QUERY:
|
||||
m = input_get(ictx, 0, 0, 0);
|
||||
switch (m) {
|
||||
case 4: /* IRM */
|
||||
n = (s->mode & MODE_INSERT) ? 1 : 2;
|
||||
break;
|
||||
default:
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
if (m > 0)
|
||||
input_reply(ictx, 1, "\033[%d;%d$y", m, n);
|
||||
break;
|
||||
case INPUT_CSI_QUERY_PRIVATE:
|
||||
switch (input_get(ictx, 0, 0, 0)) {
|
||||
m = input_get(ictx, 0, 0, 0);
|
||||
switch (m) {
|
||||
case 1: /* DECCKM */
|
||||
n = (s->mode & MODE_KCURSOR) ? 1 : 2;
|
||||
break;
|
||||
case 3: /* DECCOLM: always reset */
|
||||
n = 4;
|
||||
break;
|
||||
case 6: /* DECOM */
|
||||
n = (s->mode & MODE_ORIGIN) ? 1 : 2;
|
||||
break;
|
||||
case 7: /* DECAWM */
|
||||
n = (s->mode & MODE_WRAP) ? 1 : 2;
|
||||
break;
|
||||
case 12: /* cursor blink: 1 = blink, 2 = steady */
|
||||
if (s->cstyle != SCREEN_CURSOR_DEFAULT ||
|
||||
s->mode & MODE_CURSOR_BLINKING_SET)
|
||||
@@ -1622,34 +1646,54 @@ input_csi_dispatch(struct input_ctx *ictx)
|
||||
if (ictx->wp != NULL)
|
||||
oo = ictx->wp->options;
|
||||
else
|
||||
oo = global_options;
|
||||
oo = global_w_options;
|
||||
p = options_get_number(oo, "cursor-style");
|
||||
|
||||
/* blink for 1,3,5; steady for 0,2,4,6 */
|
||||
n = (p == 1 || p == 3 || p == 5) ? 1 : 2;
|
||||
n = (p == 1 || p == 3 || p == 5) ? 1 : 2;
|
||||
}
|
||||
input_reply(ictx, 1, "\033[?12;%d$y", n);
|
||||
break;
|
||||
case 1004: /* focus reporting */
|
||||
case 25: /* DECTCEM */
|
||||
n = (s->mode & MODE_CURSOR) ? 1 : 2;
|
||||
break;
|
||||
case 47:
|
||||
case 1047:
|
||||
case 1049: /* alternate screen */
|
||||
n = SCREEN_IS_ALTERNATE(s) ? 1 : 2;
|
||||
break;
|
||||
case 1000: /* mouse: normal tracking */
|
||||
n = (s->mode & MODE_MOUSE_STANDARD) ? 1 : 2;
|
||||
break;
|
||||
case 1002: /* mouse: button-event tracking */
|
||||
n = (s->mode & MODE_MOUSE_BUTTON) ? 1 : 2;
|
||||
break;
|
||||
case 1003: /* mouse: any-event tracking */
|
||||
n = (s->mode & MODE_MOUSE_ALL) ? 1 : 2;
|
||||
break;
|
||||
case 1004: /* focus reporting */
|
||||
n = (s->mode & MODE_FOCUSON) ? 1 : 2;
|
||||
input_reply(ictx, 1, "\033[?1004;%d$y", n);
|
||||
break;
|
||||
case 1006: /* SGR mouse */
|
||||
case 1005: /* mouse: UTF-8 */
|
||||
n = (s->mode & MODE_MOUSE_UTF8) ? 1 : 2;
|
||||
break;
|
||||
case 1006: /* mouse: SGR */
|
||||
n = (s->mode & MODE_MOUSE_SGR) ? 1 : 2;
|
||||
input_reply(ictx, 1, "\033[?1006;%d$y", n);
|
||||
break;
|
||||
case 2004: /* bracketed paste */
|
||||
case 2004: /* bracketed paste */
|
||||
n = (s->mode & MODE_BRACKETPASTE) ? 1 : 2;
|
||||
input_reply(ictx, 1, "\033[?2004;%d$y", n);
|
||||
break;
|
||||
case 2026: /* synchronized output */
|
||||
case 2026: /* synchronized output */
|
||||
n = (s->mode & MODE_SYNC) ? 1 : 2;
|
||||
input_reply(ictx, 1, "\033[?2026;%d$y", n);
|
||||
break;
|
||||
case 2031:
|
||||
input_reply(ictx, 1, "\033[?2031;2$y");
|
||||
case 2031: /* theme update notifications */
|
||||
n = (s->mode & MODE_THEME_UPDATES) ? 1 : 2;
|
||||
break;
|
||||
default:
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
if (m > 0)
|
||||
input_reply(ictx, 1, "\033[?%d;%d$y", m, n);
|
||||
break;
|
||||
case INPUT_CSI_DSR:
|
||||
switch (input_get(ictx, 0, 0, 0)) {
|
||||
@@ -1909,16 +1953,14 @@ input_csi_dispatch_rm_private(struct input_ctx *ictx)
|
||||
case 2004:
|
||||
screen_write_mode_clear(sctx, MODE_BRACKETPASTE);
|
||||
break;
|
||||
case 2026:
|
||||
screen_write_stop_sync(ictx->wp);
|
||||
break;
|
||||
case 2031:
|
||||
screen_write_mode_clear(sctx, MODE_THEME_UPDATES);
|
||||
if (ictx->wp != NULL)
|
||||
ictx->wp->flags &= ~PANE_THEMECHANGED;
|
||||
break;
|
||||
case 2026: /* synchronized output */
|
||||
screen_write_stop_sync(ictx->wp);
|
||||
if (ictx->wp != NULL)
|
||||
ictx->wp->flags |= PANE_REDRAW;
|
||||
break;
|
||||
default:
|
||||
log_debug("%s: unknown '%c'", __func__, ictx->ch);
|
||||
break;
|
||||
@@ -2021,7 +2063,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
|
||||
ictx->wp->flags &= ~PANE_THEMECHANGED;
|
||||
}
|
||||
break;
|
||||
case 2026: /* synchronized output */
|
||||
case 2026:
|
||||
screen_write_start_sync(ictx->wp);
|
||||
break;
|
||||
default:
|
||||
@@ -2035,7 +2077,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
|
||||
static void
|
||||
input_csi_dispatch_sm_graphics(__unused struct input_ctx *ictx)
|
||||
{
|
||||
#ifdef ENABLE_SIXEL_IMAGES
|
||||
#ifdef ENABLE_SIXEL
|
||||
int n, m, o;
|
||||
|
||||
if (ictx->param_list_len > 3)
|
||||
@@ -2527,7 +2569,7 @@ input_handle_decrqss(struct input_ctx *ictx)
|
||||
if (wp != NULL)
|
||||
oo = wp->options;
|
||||
else
|
||||
oo = global_options;
|
||||
oo = global_w_options;
|
||||
opt_ps = options_get_number(oo, "cursor-style");
|
||||
|
||||
/* Sanity clamp: valid Ps are 0..6 per DECSCUSR. */
|
||||
@@ -2560,24 +2602,25 @@ input_dcs_dispatch(struct input_ctx *ictx)
|
||||
const char prefix[] = "tmux;";
|
||||
const u_int prefixlen = (sizeof prefix) - 1;
|
||||
long long allow_passthrough = 0;
|
||||
#ifdef ENABLE_SIXEL_IMAGES
|
||||
#ifdef ENABLE_SIXEL
|
||||
struct window *w;
|
||||
struct sixel_image *si;
|
||||
int p2;
|
||||
#endif
|
||||
|
||||
if (wp == NULL)
|
||||
return (0);
|
||||
oo = wp->options;
|
||||
oo = global_w_options;
|
||||
else
|
||||
oo = wp->options;
|
||||
|
||||
if (ictx->flags & INPUT_DISCARD) {
|
||||
log_debug("%s: %zu bytes (discard)", __func__, len);
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL_IMAGES
|
||||
w = wp->window;
|
||||
if (buf[0] == 'q' && ictx->interm_len == 0) {
|
||||
#ifdef ENABLE_SIXEL
|
||||
if (wp != NULL && buf[0] == 'q' && ictx->interm_len == 0) {
|
||||
w = wp->window;
|
||||
if (input_split(ictx) != 0)
|
||||
return (0);
|
||||
p2 = input_get(ictx, 1, 0, 0);
|
||||
@@ -2656,7 +2699,7 @@ input_exit_osc(struct input_ctx *ictx)
|
||||
case 2:
|
||||
if (wp != NULL &&
|
||||
options_get_number(wp->options, "allow-set-title") &&
|
||||
screen_set_title(sctx->s, p)) {
|
||||
screen_set_title(sctx->s, p, 1)) {
|
||||
notify_pane("pane-title-changed", wp);
|
||||
server_redraw_window_borders(wp->window);
|
||||
server_status_window(wp->window);
|
||||
@@ -2666,17 +2709,17 @@ input_exit_osc(struct input_ctx *ictx)
|
||||
input_osc_4(ictx, p);
|
||||
break;
|
||||
case 7:
|
||||
if (utf8_isvalid(p)) {
|
||||
screen_set_path(sctx->s, p);
|
||||
if (wp != NULL) {
|
||||
server_redraw_window_borders(wp->window);
|
||||
server_status_window(wp->window);
|
||||
}
|
||||
if (wp != NULL && screen_set_path(sctx->s, p, 1)) {
|
||||
server_redraw_window_borders(wp->window);
|
||||
server_status_window(wp->window);
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
input_osc_8(ictx, p);
|
||||
break;
|
||||
case 9:
|
||||
input_osc_9(ictx, p);
|
||||
break;
|
||||
case 10:
|
||||
input_osc_10(ictx, p);
|
||||
break;
|
||||
@@ -2721,79 +2764,6 @@ input_enter_apc(struct input_ctx *ictx)
|
||||
ictx->flags &= ~INPUT_LAST;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if any client viewing this pane has an outer terminal that supports
|
||||
* sixel, so we can report it in the DA1 response.
|
||||
*/
|
||||
static int
|
||||
input_da1_has_sixel(__unused struct input_ctx *ictx)
|
||||
{
|
||||
#ifdef ENABLE_SIXEL_IMAGES
|
||||
struct window_pane *wp = ictx->wp;
|
||||
struct client *c;
|
||||
|
||||
if (wp == NULL)
|
||||
return (0);
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (c->session == NULL)
|
||||
continue;
|
||||
if (c->session->curw->window != wp->window)
|
||||
continue;
|
||||
if (c->tty.term->flags & TERM_SIXEL)
|
||||
return (1);
|
||||
}
|
||||
#endif
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_KITTY_IMAGES
|
||||
/* Handle a kitty graphics APC sequence. */
|
||||
static void
|
||||
input_apc_kitty_image(struct input_ctx *ictx)
|
||||
{
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
struct window_pane *wp = ictx->wp;
|
||||
struct window *w;
|
||||
struct kitty_image *ki;
|
||||
|
||||
if (wp == NULL)
|
||||
return;
|
||||
|
||||
w = wp->window;
|
||||
ki = kitty_parse(ictx->input_buf + 1, ictx->input_len - 1,
|
||||
w->xpixel, w->ypixel);
|
||||
if (ki == NULL)
|
||||
return;
|
||||
|
||||
/* Handle query commands. */
|
||||
if (kitty_get_action(ki) == 'q') {
|
||||
if (kitty_get_image_id(ki) != 0)
|
||||
input_reply(ictx, 0, "\033_Gi=%u;OK\033\\",
|
||||
kitty_get_image_id(ki));
|
||||
else
|
||||
input_reply(ictx, 0, "\033_Ga=q;OK\033\\");
|
||||
kitty_free(ki);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store image placements and trigger a redraw. */
|
||||
if (kitty_get_action(ki) == 'T' || kitty_get_action(ki) == 't' ||
|
||||
kitty_get_action(ki) == 'p') {
|
||||
screen_write_kittyimage(sctx, ki);
|
||||
} else {
|
||||
/* For other actions (delete, etc.), pass through. */
|
||||
char *apc;
|
||||
size_t apclen;
|
||||
|
||||
apclen = xasprintf(&apc, "\033_%s\033\\", ictx->input_buf);
|
||||
tty_kitty_passthrough(wp, apc, apclen, sctx->s->cx,
|
||||
sctx->s->cy);
|
||||
free(apc);
|
||||
kitty_free(ki);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* APC terminator (ST) received. */
|
||||
static void
|
||||
input_exit_apc(struct input_ctx *ictx)
|
||||
@@ -2805,16 +2775,9 @@ input_exit_apc(struct input_ctx *ictx)
|
||||
return;
|
||||
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
|
||||
|
||||
#ifdef ENABLE_KITTY_IMAGES
|
||||
if (ictx->input_len >= 1 && ictx->input_buf[0] == 'G') {
|
||||
input_apc_kitty_image(ictx);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (wp != NULL &&
|
||||
options_get_number(wp->options, "allow-set-title") &&
|
||||
screen_set_title(sctx->s, ictx->input_buf)) {
|
||||
screen_set_title(sctx->s, ictx->input_buf, 1)) {
|
||||
notify_pane("pane-title-changed", wp);
|
||||
server_redraw_window_borders(wp->window);
|
||||
server_status_window(wp->window);
|
||||
@@ -2857,10 +2820,10 @@ input_exit_rename(struct input_ctx *ictx)
|
||||
if (o != NULL)
|
||||
options_remove_or_default(o, -1, NULL);
|
||||
if (!options_get_number(w->options, "automatic-rename"))
|
||||
window_set_name(w, "");
|
||||
window_set_name(w, "", 1);
|
||||
} else {
|
||||
options_set_number(w->options, "automatic-rename", 0);
|
||||
window_set_name(w, ictx->input_buf);
|
||||
window_set_name(w, ictx->input_buf, 1);
|
||||
}
|
||||
server_redraw_window_borders(w);
|
||||
server_status_window(w);
|
||||
@@ -3025,6 +2988,57 @@ bad:
|
||||
free(id);
|
||||
}
|
||||
|
||||
/* Helper to handle setting the progress bar and redrawing. */
|
||||
static void
|
||||
input_set_progress_bar(struct input_ctx *ictx, enum progress_bar_state state,
|
||||
int p)
|
||||
{
|
||||
screen_set_progress_bar(ictx->ctx.s, state, p);
|
||||
if (ictx->wp != NULL) {
|
||||
server_redraw_window_borders(ictx->wp->window);
|
||||
server_status_window(ictx->wp->window);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle the OSC 9;4 sequence for progress bars. */
|
||||
static void
|
||||
input_osc_9(struct input_ctx *ictx, const char *p)
|
||||
{
|
||||
const char *pb = p;
|
||||
enum progress_bar_state state;
|
||||
int progress = 0;
|
||||
|
||||
if (*pb++ != '4')
|
||||
return;
|
||||
if (*pb == '\0' || (*pb == ';' && pb[1] == '\0'))
|
||||
return;
|
||||
|
||||
if (*pb++ != ';')
|
||||
return;
|
||||
if (*pb < '0' || *pb > '4')
|
||||
goto bad;
|
||||
state = *pb++ - '0';
|
||||
|
||||
if (*pb == '\0' || (*pb == ';' && pb[1] == '\0')) {
|
||||
input_set_progress_bar(ictx, state, -1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*pb++ != ';')
|
||||
goto bad;
|
||||
while (*pb >= '0' && *pb <= '9') {
|
||||
if (progress > 100)
|
||||
goto bad;
|
||||
progress = progress * 10 + *pb++ - '0';
|
||||
}
|
||||
if (*pb != '\0' || progress < 0 || progress > 100)
|
||||
goto bad;
|
||||
input_set_progress_bar(ictx, state, progress);
|
||||
return;
|
||||
|
||||
bad:
|
||||
log_debug("bad OSC 9;4 %s", p);
|
||||
}
|
||||
|
||||
/* Handle the OSC 10 sequence for setting and querying foreground colour. */
|
||||
static void
|
||||
@@ -3039,7 +3053,7 @@ input_osc_10(struct input_ctx *ictx, const char *p)
|
||||
return;
|
||||
c = window_pane_get_fg_control_client(wp);
|
||||
if (c == -1) {
|
||||
tty_default_colours(&defaults, wp);
|
||||
tty_default_colours(&defaults, wp, NULL);
|
||||
if (COLOUR_DEFAULT(defaults.fg))
|
||||
c = window_pane_get_fg(wp);
|
||||
else
|
||||
@@ -3209,7 +3223,7 @@ static int
|
||||
input_osc_52_parse(struct input_ctx *ictx, const char *p, u_char **out,
|
||||
int *outlen, char *clip)
|
||||
{
|
||||
char *end;
|
||||
const char *end;
|
||||
size_t len;
|
||||
const char *allow = "cpqs01234567";
|
||||
u_int i, j = 0;
|
||||
@@ -3235,7 +3249,7 @@ input_osc_52_parse(struct input_ctx *ictx, const char *p, u_char **out,
|
||||
return (0);
|
||||
}
|
||||
|
||||
len = (strlen(end) / 4) * 3;
|
||||
len = ((strlen(end) + 3) / 4) * 3;
|
||||
if (len == 0)
|
||||
return (0);
|
||||
|
||||
@@ -3254,7 +3268,7 @@ static void
|
||||
input_osc_52(struct input_ctx *ictx, const char *p)
|
||||
{
|
||||
struct window_pane *wp = ictx->wp;
|
||||
struct screen_write_ctx ctx;
|
||||
struct screen_write_ctx ctx;
|
||||
u_char *out;
|
||||
int outlen;
|
||||
char clip[sizeof "cpqs01234567"] = "";
|
||||
|
||||
125
key-bindings.c
125
key-bindings.c
@@ -25,11 +25,11 @@
|
||||
#include "tmux.h"
|
||||
|
||||
#define DEFAULT_SESSION_MENU \
|
||||
" 'Next' 'n' {switch-client -n}" \
|
||||
" 'Previous' 'p' {switch-client -p}" \
|
||||
" #{S/t:#{?#{&&:#{<:#{loop_index},6},#{!:#{session_active}}},'Switch To #[underscore]#{session_name}' '' {switch-client -t=#{session_id}#} ,}}" \
|
||||
" ''" \
|
||||
" 'Renumber' 'N' {move-window -r}" \
|
||||
" 'Rename' 'n' {command-prompt -I \"#S\" {rename-session -- '%%'}}" \
|
||||
" 'Rename' 'r' {command-prompt -I '#S' {rename-session -- '%%'}}" \
|
||||
" 'Detach' 'd' {detach-client}" \
|
||||
" ''" \
|
||||
" 'New Session' 's' {new-session}" \
|
||||
" 'New Window' 'w' {new-window}"
|
||||
@@ -59,11 +59,11 @@
|
||||
" '#{?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}" \
|
||||
" '#{?#{!:#{pane_floating_flag}},Horizontal Split,}' 'h' {split-window -h}" \
|
||||
" '#{?#{!:#{pane_floating_flag}},Vertical Split,}' 'v' {split-window -v}" \
|
||||
" ''" \
|
||||
" '#{?#{>:#{window_panes},1},,-}Swap Up' 'u' {swap-pane -U}" \
|
||||
" '#{?#{>:#{window_panes},1},,-}Swap Down' 'd' {swap-pane -D}" \
|
||||
" '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Up,}' 'u' {swap-pane -U}" \
|
||||
" '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Down,}' 'd' {swap-pane -D}" \
|
||||
" '#{?pane_marked_set,,-}Swap Marked' 's' {swap-pane}" \
|
||||
" ''" \
|
||||
" 'Kill' 'X' {kill-pane}" \
|
||||
@@ -350,6 +350,8 @@ key_bindings_init(void)
|
||||
{
|
||||
static const char *const defaults[] = {
|
||||
/* Prefix keys. */
|
||||
"bind -N 'Hide pane' _ { hide-pane }",
|
||||
|
||||
"bind -N 'Send the prefix key' C-b { send-prefix }",
|
||||
"bind -N 'Rotate through the panes' C-o { rotate-window }",
|
||||
"bind -N 'Suspend the current client' C-z { suspend-client }",
|
||||
@@ -361,6 +363,8 @@ key_bindings_init(void)
|
||||
"bind -N 'Split window horizontally' % { split-window -h }",
|
||||
"bind -N 'Kill current window' & { confirm-before -p\"kill-window #W? (y/n)\" kill-window }",
|
||||
"bind -N 'Prompt for window index to select' \"'\" { command-prompt -T window-target -pindex { select-window -t ':%%' } }",
|
||||
"bind -N 'New floating pane' * { new-pane }",
|
||||
"bind -N 'Toggle pane between floating and tiled' @ { if -F '#{pane_floating_flag}' { join-pane } { break-pane -W } }",
|
||||
"bind -N 'Switch to previous client' ( { switch-client -p }",
|
||||
"bind -N 'Switch to next client' ) { switch-client -n }",
|
||||
"bind -N 'Rename current window' , { command-prompt -I'#W' { rename-window -- '%%' } }",
|
||||
@@ -401,11 +405,15 @@ key_bindings_init(void)
|
||||
"bind -N 'Redraw the current client' r { refresh-client }",
|
||||
"bind -N 'Choose a session from a list' s { choose-tree -Zs }",
|
||||
"bind -N 'Show a clock' t { clock-mode }",
|
||||
"bind -N 'Switch to a window' Tab { new-pane -E -x75% -y30% -X0 -Y0; move-pane -P bottom-centre; switch-mode -wk }",
|
||||
"bind -N 'Switch to a session' BTab { new-pane -E -x75% -y30% -X0 -Y0; move-pane -P bottom-centre; switch-mode -sk }",
|
||||
"bind -N 'Choose a window from a list' w { choose-tree -Zw }",
|
||||
"bind -N 'Kill the active pane' x { confirm-before -p\"kill-pane #P? (y/n)\" kill-pane }",
|
||||
"bind -N 'Zoom the active pane' z { resize-pane -Z }",
|
||||
"bind -N 'Swap the active pane with the pane above' '{' { swap-pane -U }",
|
||||
"bind -N 'Swap the active pane with the pane below' '}' { swap-pane -D }",
|
||||
"bind -N 'Move pane to top-left corner' '{' { resize-pane -x50% -y50%; move-pane -P top-left }",
|
||||
"bind -N 'Move pane to top-right corner' '}' { resize-pane -x50% -y50%; move-pane -P top-right }",
|
||||
"bind -N 'Move pane to bottom-left corner' 'M-{' { resize-pane -x50% -y50%; move-pane -P bottom-left }",
|
||||
"bind -N 'Move pane to bottom-right corner' 'M-}' { resize-pane -x50% -y50%; move-pane -P bottom-right }",
|
||||
"bind -N 'Show messages' '~' { show-messages }",
|
||||
"bind -N 'Enter copy mode and scroll up' PPage { copy-mode -u }",
|
||||
"bind -N 'Select the pane above the active pane' -r Up { select-pane -U }",
|
||||
@@ -427,13 +435,13 @@ key_bindings_init(void)
|
||||
"bind -N 'Move the visible part of the window left' -r S-Left { refresh-client -L 10 }",
|
||||
"bind -N 'Move the visible part of the window right' -r S-Right { refresh-client -R 10 }",
|
||||
"bind -N 'Reset so the visible part of the window follows the cursor' -r DC { refresh-client -c }",
|
||||
"bind -N 'Resize the pane up by 5' -r M-Up { resize-pane -U 5 }",
|
||||
"bind -N 'Resize the pane up by 5' -r M-Up if -F '#{?floating_pane_flag}' { resizep -D-5 } { resize-pane -U 5 }",
|
||||
"bind -N 'Resize the pane down by 5' -r M-Down { resize-pane -D 5 }",
|
||||
"bind -N 'Resize the pane left by 5' -r M-Left { resize-pane -L 5 }",
|
||||
"bind -N 'Resize the pane right by 5' -r M-Right { resize-pane -R 5 }",
|
||||
"bind -N 'Resize the pane up' -r C-Up { resize-pane -U }",
|
||||
"bind -N 'Resize the pane left by 5' -r M-Left if -F '#{?floating_pane_flag}' { resizep -R-5 } { resize-pane -L 5 }",
|
||||
"bind -N 'Resize the pane right by 5' -r M-Right resize-pane -R 5",
|
||||
"bind -N 'Resize the pane up' -r C-Up if -F '#{?floating_pane_flag}' { resizep -D-1 } { resize-pane -U }",
|
||||
"bind -N 'Resize the pane down' -r C-Down { resize-pane -D }",
|
||||
"bind -N 'Resize the pane left' -r C-Left { resize-pane -L }",
|
||||
"bind -N 'Resize the pane left' -r C-Left if -F '#{?floating_pane_flag}' { resizep -R-1 } { resize-pane -L }",
|
||||
"bind -N 'Resize the pane right' -r C-Right { resize-pane -R }",
|
||||
|
||||
/* Menu keys */
|
||||
@@ -446,6 +454,7 @@ key_bindings_init(void)
|
||||
|
||||
/* Mouse button 1 drag on pane. */
|
||||
"bind -n MouseDrag1Pane { if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }",
|
||||
"bind -n M-MouseDrag1Pane { move-pane -M }",
|
||||
|
||||
/* Mouse wheel up on pane. */
|
||||
"bind -n WheelUpPane { if -F '#{||:#{alternate_on},#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -e } }",
|
||||
@@ -459,13 +468,21 @@ key_bindings_init(void)
|
||||
/* Mouse button 1 triple click on pane. */
|
||||
"bind -n TripleClick1Pane { select-pane -t=; if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -H; send -X select-line; run -d0.3; send -X copy-pipe-and-cancel } }",
|
||||
|
||||
/* Mouse button 1 on border. */
|
||||
"bind -n MouseDown1Border { select-pane -M }",
|
||||
|
||||
/* Mouse button 1 drag on border. */
|
||||
"bind -n MouseDrag1Border { resize-pane -M }",
|
||||
"bind -n M-MouseDrag1Border { move-pane -M }",
|
||||
|
||||
/* Mouse button 1 down on status line. */
|
||||
"bind -n MouseDown1Status { switch-client -t= }",
|
||||
"bind -n MouseDown1Status { if -F '#{&&:#{pane_active},#{!#{pane_hidden_flag}}}' { hide-pane -t= } { switch-client -t= } }",
|
||||
"bind -n C-MouseDown1Status { swap-window -t@ }",
|
||||
|
||||
/* Mouse button 1 down on default pane-border-format */
|
||||
"bind -n MouseDown1Control9 { display-menu -t= -xM -yM -O -T 'Kill pane #{pane_index}?' 'Yes' 'y' { kill-pane -t= } 'No' 'n' {}}",
|
||||
"bind -n MouseDown1Control8 { resize-pane -Z }",
|
||||
|
||||
/* Mouse wheel down on status line. */
|
||||
"bind -n WheelDownStatus { next-window }",
|
||||
|
||||
@@ -473,8 +490,8 @@ key_bindings_init(void)
|
||||
"bind -n WheelUpStatus { previous-window }",
|
||||
|
||||
/* 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 " }",
|
||||
"bind -n MouseDown3StatusLeft { run -C \"display-menu -t= -xM -yW -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU "\" }",
|
||||
"bind -n M-MouseDown3StatusLeft { run -C \"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 "}",
|
||||
@@ -498,30 +515,31 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode C-b { send -X cursor-left }",
|
||||
"bind -Tcopy-mode C-g { send -X clear-selection }",
|
||||
"bind -Tcopy-mode C-k { send -X copy-pipe-end-of-line-and-cancel }",
|
||||
"bind -Tcopy-mode C-l { send -X cursor-centre-vertical }",
|
||||
"bind -Tcopy-mode C-l { send -X recentre-top-bottom }",
|
||||
"bind -Tcopy-mode M-l { send -X cursor-centre-horizontal }",
|
||||
"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 -P -T search -ip'(search up)' -I'#{pane_search_string}' { send -X search-backward-incremental -- '%%' } }",
|
||||
"bind -Tcopy-mode C-s { command-prompt -P -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 C-[ { 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 -P -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 -P -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 -P -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
|
||||
"bind -Tcopy-mode g { command-prompt -P -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 r { send -X refresh-toggle }",
|
||||
"bind -Tcopy-mode t { command-prompt -P -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",
|
||||
@@ -537,15 +555,15 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode Down { send -X cursor-down }",
|
||||
"bind -Tcopy-mode Left { send -X cursor-left }",
|
||||
"bind -Tcopy-mode Right { send -X cursor-right }",
|
||||
"bind -Tcopy-mode M-1 { command-prompt -Np'(repeat)' -I1 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-2 { command-prompt -Np'(repeat)' -I2 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-3 { command-prompt -Np'(repeat)' -I3 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-4 { command-prompt -Np'(repeat)' -I4 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-5 { command-prompt -Np'(repeat)' -I5 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-6 { command-prompt -Np'(repeat)' -I6 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-7 { command-prompt -Np'(repeat)' -I7 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-8 { command-prompt -Np'(repeat)' -I8 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-9 { command-prompt -Np'(repeat)' -I9 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-1 { command-prompt -P -Np'(repeat)' -I1 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-2 { command-prompt -P -Np'(repeat)' -I2 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-3 { command-prompt -P -Np'(repeat)' -I3 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-4 { command-prompt -P -Np'(repeat)' -I4 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-5 { command-prompt -P -Np'(repeat)' -I5 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-6 { command-prompt -P -Np'(repeat)' -I6 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-7 { command-prompt -P -Np'(repeat)' -I7 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-8 { command-prompt -P -Np'(repeat)' -I8 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-9 { command-prompt -P -Np'(repeat)' -I9 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode M-< { send -X history-top }",
|
||||
"bind -Tcopy-mode M-> { send -X history-bottom }",
|
||||
"bind -Tcopy-mode M-R { send -X top-line }",
|
||||
@@ -564,6 +582,8 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode M-Down { send -X halfpage-down }",
|
||||
"bind -Tcopy-mode C-Up { send -X scroll-up }",
|
||||
"bind -Tcopy-mode C-Down { send -X scroll-down }",
|
||||
"bind -Tcopy-mode M-C-Up { send -X previous-prompt }",
|
||||
"bind -Tcopy-mode M-C-Down { send -X next-prompt }",
|
||||
|
||||
/* Copy mode (vi) keys. */
|
||||
"bind -Tcopy-mode-vi '#' { send -FX search-backward -- '#{copy_cursor_word}' }",
|
||||
@@ -580,28 +600,29 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode-vi C-v { send -X rectangle-toggle }",
|
||||
"bind -Tcopy-mode-vi C-y { send -X scroll-up }",
|
||||
"bind -Tcopy-mode-vi Escape { send -X clear-selection }",
|
||||
"bind -Tcopy-mode-vi C-[ { send -X clear-selection }",
|
||||
"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 -P -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 '%%' } }",
|
||||
"bind -Tcopy-mode-vi 3 { command-prompt -Np'(repeat)' -I3 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi 4 { command-prompt -Np'(repeat)' -I4 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi 5 { command-prompt -Np'(repeat)' -I5 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi 6 { command-prompt -Np'(repeat)' -I6 { send -N '%%' } }",
|
||||
"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 1 { command-prompt -P -Np'(repeat)' -I1 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi 2 { command-prompt -P -Np'(repeat)' -I2 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi 3 { command-prompt -P -Np'(repeat)' -I3 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi 4 { command-prompt -P -Np'(repeat)' -I4 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi 5 { command-prompt -P -Np'(repeat)' -I5 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi 6 { command-prompt -P -Np'(repeat)' -I6 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi 7 { command-prompt -P -Np'(repeat)' -I7 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi 8 { command-prompt -P -Np'(repeat)' -I8 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi 9 { command-prompt -P -Np'(repeat)' -I9 { send -N '%%' } }",
|
||||
"bind -Tcopy-mode-vi : { command-prompt -P -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 -P -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 -P -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 }",
|
||||
@@ -610,14 +631,14 @@ 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 -P -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 -P -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 }",
|
||||
@@ -627,8 +648,8 @@ key_bindings_init(void)
|
||||
"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 r { send -X refresh-toggle }",
|
||||
"bind -Tcopy-mode-vi t { command-prompt -P -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 }",
|
||||
|
||||
@@ -202,7 +202,7 @@ key_string_search_table(const char *string)
|
||||
return (key_string_table[i].key);
|
||||
}
|
||||
|
||||
if (sscanf(string, "User%u", &user) == 1 && user < KEYC_NUSER)
|
||||
if (sscanf(string, "User%u", &user) == 1 && user <= KEYC_NUSER)
|
||||
return (KEYC_USER + user);
|
||||
|
||||
return (KEYC_UNKNOWN);
|
||||
@@ -418,7 +418,7 @@ key_string_lookup_key(key_code key, int with_flags)
|
||||
s = "MouseMoveBorder";
|
||||
goto append;
|
||||
}
|
||||
if (key >= KEYC_USER && key < KEYC_USER_END) {
|
||||
if (KEYC_IS_USER(key)) {
|
||||
snprintf(tmp, sizeof tmp, "User%u", (u_int)(key - KEYC_USER));
|
||||
strlcat(out, tmp, sizeof out);
|
||||
goto out;
|
||||
|
||||
160
layout-custom.c
160
layout-custom.c
@@ -27,10 +27,10 @@ static struct layout_cell *layout_find_bottomright(struct layout_cell *);
|
||||
static u_short layout_checksum(const char *);
|
||||
static int layout_append(struct layout_cell *, char *,
|
||||
size_t);
|
||||
static struct layout_cell *layout_construct(struct layout_cell *,
|
||||
const char **);
|
||||
static int layout_construct(struct layout_cell *,
|
||||
const char **, struct layout_cell **);
|
||||
static void layout_assign(struct window_pane **,
|
||||
struct layout_cell *);
|
||||
struct layout_cell *, int);
|
||||
|
||||
/* Find the bottom-right cell. */
|
||||
static struct layout_cell *
|
||||
@@ -58,14 +58,30 @@ layout_checksum(const char *layout)
|
||||
|
||||
/* Dump layout as a string. */
|
||||
char *
|
||||
layout_dump(struct layout_cell *root)
|
||||
layout_dump(struct window *w, struct layout_cell *root)
|
||||
{
|
||||
char layout[8192], *out;
|
||||
char layout[8192], *out;
|
||||
int bracket = 0;
|
||||
struct window_pane *wp;
|
||||
|
||||
*layout = '\0';
|
||||
if (layout_append(root, layout, sizeof layout) != 0)
|
||||
return (NULL);
|
||||
|
||||
TAILQ_FOREACH(wp, &w->z_index, zentry) {
|
||||
if (!window_pane_is_floating(wp))
|
||||
break;
|
||||
if (!bracket) {
|
||||
strlcat(layout, "<", sizeof layout);
|
||||
bracket = 1;
|
||||
}
|
||||
if (layout_append(wp->layout_cell, layout, sizeof layout) != 0)
|
||||
return (NULL);
|
||||
strlcat(layout, ",", sizeof layout);
|
||||
}
|
||||
if (bracket)
|
||||
layout[strlen(layout) - 1] = '>';
|
||||
|
||||
xasprintf(&out, "%04hx,%s", layout_checksum(layout), layout);
|
||||
return (out);
|
||||
}
|
||||
@@ -81,12 +97,13 @@ layout_append(struct layout_cell *lc, char *buf, size_t len)
|
||||
|
||||
if (len == 0)
|
||||
return (-1);
|
||||
|
||||
if (lc == NULL)
|
||||
return (0);
|
||||
if (lc->wp != NULL) {
|
||||
tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u,%u",
|
||||
tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%d,%d,%u",
|
||||
lc->sx, lc->sy, lc->xoff, lc->yoff, lc->wp->id);
|
||||
} else {
|
||||
tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%u,%u",
|
||||
tmplen = xsnprintf(tmp, sizeof tmp, "%ux%u,%d,%d",
|
||||
lc->sx, lc->sy, lc->xoff, lc->yoff);
|
||||
}
|
||||
if (tmplen > (sizeof tmp) - 1)
|
||||
@@ -156,37 +173,43 @@ layout_check(struct layout_cell *lc)
|
||||
int
|
||||
layout_parse(struct window *w, const char *layout, char **cause)
|
||||
{
|
||||
struct layout_cell *lc, *lcchild;
|
||||
struct layout_cell *lcchild, *tiled_lc = NULL;
|
||||
struct window_pane *wp;
|
||||
u_int npanes, ncells, sx = 0, sy = 0;
|
||||
u_short csum;
|
||||
int n;
|
||||
|
||||
/* Check validity. */
|
||||
if (sscanf(layout, "%hx,", &csum) != 1) {
|
||||
if (sscanf(layout, "%hx,%n", &csum, &n) != 1 || n != 5) {
|
||||
*cause = xstrdup("invalid layout");
|
||||
return (-1);
|
||||
}
|
||||
layout += 5;
|
||||
layout += n;
|
||||
if (csum != layout_checksum(layout)) {
|
||||
*cause = xstrdup("invalid layout");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Build the layout. */
|
||||
lc = layout_construct(NULL, &layout);
|
||||
if (lc == NULL) {
|
||||
if (layout_construct(NULL, &layout, &tiled_lc) != 0) {
|
||||
*cause = xstrdup("invalid layout");
|
||||
return (-1);
|
||||
}
|
||||
if (tiled_lc == NULL) {
|
||||
/* A stub layout cell for an empty window. */
|
||||
tiled_lc = layout_create_cell(NULL);
|
||||
tiled_lc->type = LAYOUT_LEFTRIGHT;
|
||||
layout_set_size(tiled_lc, w->sx, w->sy, 0, 0);
|
||||
}
|
||||
if (*layout != '\0') {
|
||||
*cause = xstrdup("invalid layout");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Check this window will fit into the layout. */
|
||||
npanes = window_count_panes(w, 1);
|
||||
for (;;) {
|
||||
npanes = window_count_panes(w);
|
||||
ncells = layout_count_cells(lc);
|
||||
ncells = layout_count_cells(tiled_lc);
|
||||
if (npanes > ncells) {
|
||||
xasprintf(cause, "have %u panes but need %u", npanes,
|
||||
ncells);
|
||||
@@ -195,9 +218,12 @@ layout_parse(struct window *w, const char *layout, char **cause)
|
||||
if (npanes == ncells)
|
||||
break;
|
||||
|
||||
/* Fewer panes than cells - close the bottom right. */
|
||||
lcchild = layout_find_bottomright(lc);
|
||||
layout_destroy_cell(w, lcchild, &lc);
|
||||
/*
|
||||
* Fewer panes than cells, close the bottom right until none
|
||||
* remain.
|
||||
*/
|
||||
lcchild = layout_find_bottomright(tiled_lc);
|
||||
layout_destroy_cell(w, lcchild, &tiled_lc);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -205,91 +231,104 @@ layout_parse(struct window *w, const char *layout, char **cause)
|
||||
* an incorrect top cell size - if it is larger than the top child then
|
||||
* correct that (if this is still wrong the check code will catch it).
|
||||
*/
|
||||
switch (lc->type) {
|
||||
|
||||
switch (tiled_lc->type) {
|
||||
case LAYOUT_WINDOWPANE:
|
||||
break;
|
||||
case LAYOUT_LEFTRIGHT:
|
||||
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
|
||||
TAILQ_FOREACH(lcchild, &tiled_lc->cells, entry) {
|
||||
sy = lcchild->sy + 1;
|
||||
sx += lcchild->sx + 1;
|
||||
}
|
||||
break;
|
||||
case LAYOUT_TOPBOTTOM:
|
||||
TAILQ_FOREACH(lcchild, &lc->cells, entry) {
|
||||
TAILQ_FOREACH(lcchild, &tiled_lc->cells, entry) {
|
||||
sx = lcchild->sx + 1;
|
||||
sy += lcchild->sy + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (lc->type != LAYOUT_WINDOWPANE && (lc->sx != sx || lc->sy != sy)) {
|
||||
log_debug("fix layout %u,%u to %u,%u", lc->sx, lc->sy, sx,sy);
|
||||
layout_print_cell(lc, __func__, 0);
|
||||
lc->sx = sx - 1; lc->sy = sy - 1;
|
||||
if (tiled_lc->type != LAYOUT_WINDOWPANE &&
|
||||
(tiled_lc->sx != sx || tiled_lc->sy != sy)) {
|
||||
layout_print_cell(tiled_lc, __func__, 0);
|
||||
tiled_lc->sx = sx - 1; tiled_lc->sy = sy - 1;
|
||||
}
|
||||
|
||||
/* Check the new layout. */
|
||||
if (!layout_check(lc)) {
|
||||
if (!layout_check(tiled_lc)) {
|
||||
*cause = xstrdup("size mismatch after applying layout");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Resize to the layout size. */
|
||||
window_resize(w, lc->sx, lc->sy, -1, -1);
|
||||
/* Resize window to the layout size. */
|
||||
if (sx != 0 && sy != 0)
|
||||
window_resize(w, tiled_lc->sx, tiled_lc->sy, -1, -1);
|
||||
|
||||
/* Destroy the old layout and swap to the new. */
|
||||
layout_free_cell(w->layout_root);
|
||||
w->layout_root = lc;
|
||||
layout_free_cell(w->layout_root, 0);
|
||||
w->layout_root = tiled_lc;
|
||||
|
||||
/* Assign the panes into the cells. */
|
||||
wp = TAILQ_FIRST(&w->panes);
|
||||
layout_assign(&wp, lc);
|
||||
if (tiled_lc != NULL)
|
||||
layout_assign(&wp, tiled_lc, 0);
|
||||
|
||||
/* Fix pane z-indexes. */
|
||||
while (!TAILQ_EMPTY(&w->z_index)) {
|
||||
wp = TAILQ_FIRST(&w->z_index);
|
||||
TAILQ_REMOVE(&w->z_index, wp, zentry);
|
||||
}
|
||||
layout_fix_zindexes(w, tiled_lc);
|
||||
|
||||
/* Update pane offsets and sizes. */
|
||||
layout_fix_offsets(w);
|
||||
layout_fix_panes(w, NULL);
|
||||
recalculate_sizes();
|
||||
|
||||
layout_print_cell(lc, __func__, 0);
|
||||
layout_print_cell(tiled_lc, __func__, 0);
|
||||
|
||||
notify_window("window-layout-changed", w);
|
||||
|
||||
return (0);
|
||||
|
||||
fail:
|
||||
layout_free_cell(lc);
|
||||
layout_free_cell(tiled_lc, 0);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Assign panes into cells. */
|
||||
static void
|
||||
layout_assign(struct window_pane **wp, struct layout_cell *lc)
|
||||
layout_assign(struct window_pane **wp, struct layout_cell *lc, int flags)
|
||||
{
|
||||
struct layout_cell *lcchild;
|
||||
|
||||
if (lc == NULL)
|
||||
return;
|
||||
|
||||
switch (lc->type) {
|
||||
case LAYOUT_WINDOWPANE:
|
||||
layout_make_leaf(lc, *wp);
|
||||
lc->flags |= flags;
|
||||
*wp = TAILQ_NEXT(*wp, entry);
|
||||
return;
|
||||
case LAYOUT_LEFTRIGHT:
|
||||
case LAYOUT_TOPBOTTOM:
|
||||
TAILQ_FOREACH(lcchild, &lc->cells, entry)
|
||||
layout_assign(wp, lcchild);
|
||||
layout_assign(wp, lcchild, flags);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Construct a cell from all or part of a layout tree. */
|
||||
static struct layout_cell *
|
||||
layout_construct(struct layout_cell *lcparent, const char **layout)
|
||||
layout_construct_cell(struct layout_cell *lcparent, const char **layout)
|
||||
{
|
||||
struct layout_cell *lc, *lcchild;
|
||||
u_int sx, sy, xoff, yoff;
|
||||
struct layout_cell *lc;
|
||||
u_int sx, sy;
|
||||
int xoff, yoff;
|
||||
const char *saved;
|
||||
|
||||
if (!isdigit((u_char) **layout))
|
||||
return (NULL);
|
||||
if (sscanf(*layout, "%ux%u,%u,%u", &sx, &sy, &xoff, &yoff) != 4)
|
||||
if (sscanf(*layout, "%ux%u,%d,%d", &sx, &sy, &xoff, &yoff) != 4)
|
||||
return (NULL);
|
||||
|
||||
while (isdigit((u_char) **layout))
|
||||
@@ -324,17 +363,37 @@ layout_construct(struct layout_cell *lcparent, const char **layout)
|
||||
lc->xoff = xoff;
|
||||
lc->yoff = yoff;
|
||||
|
||||
return (lc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a character string layout, recursively construct cells.
|
||||
* Possible return values:
|
||||
* lc LAYOUT_WINDOWPANE, no children
|
||||
* lc LAYOUT_LEFTRIGHT or LAYOUT_TOPBOTTOM, with children
|
||||
*/
|
||||
static int
|
||||
layout_construct(struct layout_cell *lcparent, const char **layout,
|
||||
struct layout_cell **lc)
|
||||
{
|
||||
struct layout_cell *lcchild;
|
||||
|
||||
*lc = layout_construct_cell(lcparent, layout);
|
||||
if (*lc == NULL)
|
||||
return (-1);
|
||||
|
||||
switch (**layout) {
|
||||
case ',':
|
||||
case '}':
|
||||
case ']':
|
||||
case '>':
|
||||
case '\0':
|
||||
return (lc);
|
||||
return (0);
|
||||
case '{':
|
||||
lc->type = LAYOUT_LEFTRIGHT;
|
||||
(*lc)->type = LAYOUT_LEFTRIGHT;
|
||||
break;
|
||||
case '[':
|
||||
lc->type = LAYOUT_TOPBOTTOM;
|
||||
(*lc)->type = LAYOUT_TOPBOTTOM;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
@@ -342,13 +401,12 @@ layout_construct(struct layout_cell *lcparent, const char **layout)
|
||||
|
||||
do {
|
||||
(*layout)++;
|
||||
lcchild = layout_construct(lc, layout);
|
||||
if (lcchild == NULL)
|
||||
if (layout_construct(*lc, layout, &lcchild) != 0)
|
||||
goto fail;
|
||||
TAILQ_INSERT_TAIL(&lc->cells, lcchild, entry);
|
||||
TAILQ_INSERT_TAIL(&(*lc)->cells, lcchild, entry);
|
||||
} while (**layout == ',');
|
||||
|
||||
switch (lc->type) {
|
||||
switch ((*lc)->type) {
|
||||
case LAYOUT_LEFTRIGHT:
|
||||
if (**layout != '}')
|
||||
goto fail;
|
||||
@@ -362,9 +420,9 @@ layout_construct(struct layout_cell *lcparent, const char **layout)
|
||||
}
|
||||
(*layout)++;
|
||||
|
||||
return (lc);
|
||||
return (0);
|
||||
|
||||
fail:
|
||||
layout_free_cell(lc);
|
||||
return (NULL);
|
||||
layout_free_cell(*lc, 0);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
325
layout-set.c
325
layout-set.c
@@ -123,23 +123,31 @@ layout_set_previous(struct window *w)
|
||||
return (layout);
|
||||
}
|
||||
|
||||
static struct window_pane *
|
||||
layout_set_first_tiled(struct window *w)
|
||||
{
|
||||
struct window_pane *wp;
|
||||
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
if (wp->layout_cell && layout_cell_is_tiled(wp->layout_cell))
|
||||
return (wp);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
layout_set_even(struct window *w, enum layout_type type)
|
||||
{
|
||||
struct window_pane *wp;
|
||||
struct layout_cell *lc, *lcnew;
|
||||
struct layout_cell *lcroot, *lcchild;
|
||||
u_int n, sx, sy;
|
||||
|
||||
layout_print_cell(w->layout_root, __func__, 1);
|
||||
|
||||
/* Get number of panes. */
|
||||
n = window_count_panes(w);
|
||||
n = window_count_panes(w, 0);
|
||||
if (n <= 1)
|
||||
return;
|
||||
|
||||
/* Free the old root and construct a new. */
|
||||
layout_free(w);
|
||||
lc = w->layout_root = layout_create_cell(NULL);
|
||||
if (type == LAYOUT_LEFTRIGHT) {
|
||||
sx = (n * (PANE_MINIMUM + 1)) - 1;
|
||||
if (sx < w->sx)
|
||||
@@ -151,28 +159,30 @@ layout_set_even(struct window *w, enum layout_type type)
|
||||
sy = w->sy;
|
||||
sx = w->sx;
|
||||
}
|
||||
layout_set_size(lc, sx, sy, 0, 0);
|
||||
layout_make_node(lc, type);
|
||||
|
||||
/* Build new leaf cells. */
|
||||
layout_free(w, 1);
|
||||
lcroot = w->layout_root = layout_create_cell(NULL);
|
||||
layout_set_size(lcroot, sx, sy, 0, 0);
|
||||
layout_make_node(lcroot, type);
|
||||
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
lcnew = layout_create_cell(lc);
|
||||
layout_make_leaf(lcnew, wp);
|
||||
lcnew->sx = w->sx;
|
||||
lcnew->sy = w->sy;
|
||||
TAILQ_INSERT_TAIL(&lc->cells, lcnew, entry);
|
||||
lcchild = wp->layout_cell;
|
||||
TAILQ_INSERT_TAIL(&lcroot->cells, lcchild, entry);
|
||||
lcchild->parent = lcroot;
|
||||
if (layout_cell_is_tiled(lcchild)) {
|
||||
lcchild->sx = w->sx;
|
||||
lcchild->sy = w->sy;
|
||||
}
|
||||
}
|
||||
|
||||
/* Spread out cells. */
|
||||
layout_spread_cell(w, lc);
|
||||
layout_spread_cell(w, lcroot);
|
||||
|
||||
/* 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);
|
||||
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
}
|
||||
@@ -192,16 +202,15 @@ layout_set_even_v(struct window *w)
|
||||
static void
|
||||
layout_set_main_h(struct window *w)
|
||||
{
|
||||
struct window_pane *wp;
|
||||
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
|
||||
struct window_pane *wp, *wpmain;
|
||||
struct layout_cell *lcroot, *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);
|
||||
n = window_count_panes(w, 0);
|
||||
if (n <= 1)
|
||||
return;
|
||||
n--; /* take off main pane */
|
||||
@@ -241,48 +250,48 @@ layout_set_main_h(struct window *w)
|
||||
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);
|
||||
layout_free(w, 1);
|
||||
lcroot = w->layout_root = layout_create_cell(NULL);
|
||||
layout_set_size(lcroot, sx, mainh + otherh + 1, 0, 0);
|
||||
layout_make_node(lcroot, LAYOUT_TOPBOTTOM);
|
||||
|
||||
/* Create the main pane. */
|
||||
lcmain = layout_create_cell(lc);
|
||||
wpmain = layout_set_first_tiled(w);
|
||||
lcmain = wpmain->layout_cell;
|
||||
lcmain->parent = lcroot;
|
||||
layout_set_size(lcmain, sx, mainh, 0, 0);
|
||||
layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
|
||||
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
|
||||
TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry);
|
||||
|
||||
/* 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);
|
||||
wp = TAILQ_NEXT(wpmain, entry);
|
||||
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
|
||||
wp = TAILQ_NEXT(wp, entry);
|
||||
TAILQ_INSERT_TAIL(&lcroot->cells, wp->layout_cell, entry);
|
||||
wp->layout_cell->parent = lcroot;
|
||||
} else {
|
||||
lcother = layout_create_cell(lcroot);
|
||||
layout_set_size(lcother, sx, otherh, 0, 0);
|
||||
layout_make_node(lcother, LAYOUT_LEFTRIGHT);
|
||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
||||
TAILQ_INSERT_TAIL(&lcroot->cells, lcother, entry);
|
||||
|
||||
/* Add the remaining panes as children. */
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
if (wp == TAILQ_FIRST(&w->panes))
|
||||
if (wp == wpmain)
|
||||
continue;
|
||||
lcchild = layout_create_cell(lcother);
|
||||
layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
|
||||
layout_make_leaf(lcchild, wp);
|
||||
lcchild = wp->layout_cell;
|
||||
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
|
||||
lcchild->parent = lcother;
|
||||
if (layout_cell_is_tiled(lcchild))
|
||||
layout_set_size(lcchild, PANE_MINIMUM, otherh,
|
||||
0, 0);
|
||||
}
|
||||
layout_spread_cell(w, lcother);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
}
|
||||
@@ -290,16 +299,15 @@ layout_set_main_h(struct window *w)
|
||||
static void
|
||||
layout_set_main_h_mirrored(struct window *w)
|
||||
{
|
||||
struct window_pane *wp;
|
||||
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
|
||||
struct window_pane *wp, *wpmain;
|
||||
struct layout_cell *lcroot, *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);
|
||||
n = window_count_panes(w, 0);
|
||||
if (n <= 1)
|
||||
return;
|
||||
n--; /* take off main pane */
|
||||
@@ -339,48 +347,48 @@ layout_set_main_h_mirrored(struct window *w)
|
||||
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);
|
||||
layout_free(w, 1);
|
||||
lcroot = w->layout_root = layout_create_cell(NULL);
|
||||
layout_set_size(lcroot, sx, mainh + otherh + 1, 0, 0);
|
||||
layout_make_node(lcroot, LAYOUT_TOPBOTTOM);
|
||||
|
||||
wpmain = layout_set_first_tiled(w);
|
||||
lcmain = wpmain->layout_cell;
|
||||
lcmain->parent = lcroot;
|
||||
layout_set_size(lcmain, sx, mainh, 0, 0);
|
||||
TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry);
|
||||
|
||||
/* 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);
|
||||
wp = TAILQ_NEXT(wpmain, entry);
|
||||
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
|
||||
wp = TAILQ_NEXT(wp, entry);
|
||||
TAILQ_INSERT_HEAD(&lcroot->cells, wp->layout_cell, entry);
|
||||
wp->layout_cell->parent = lcroot;
|
||||
} else {
|
||||
lcother = layout_create_cell(lcroot);
|
||||
layout_set_size(lcother, sx, otherh, 0, 0);
|
||||
layout_make_node(lcother, LAYOUT_LEFTRIGHT);
|
||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
||||
TAILQ_INSERT_HEAD(&lcroot->cells, lcother, entry);
|
||||
|
||||
/* Add the remaining panes as children. */
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
if (wp == TAILQ_FIRST(&w->panes))
|
||||
if (wp == wpmain)
|
||||
continue;
|
||||
lcchild = layout_create_cell(lcother);
|
||||
layout_set_size(lcchild, PANE_MINIMUM, otherh, 0, 0);
|
||||
layout_make_leaf(lcchild, wp);
|
||||
lcchild = wp->layout_cell;
|
||||
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
|
||||
lcchild->parent = lcother;
|
||||
if (layout_cell_is_tiled(lcchild))
|
||||
layout_set_size(lcchild, PANE_MINIMUM, otherh,
|
||||
0, 0);
|
||||
}
|
||||
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);
|
||||
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
}
|
||||
@@ -388,21 +396,20 @@ layout_set_main_h_mirrored(struct window *w)
|
||||
static void
|
||||
layout_set_main_v(struct window *w)
|
||||
{
|
||||
struct window_pane *wp;
|
||||
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
|
||||
struct window_pane *wp, *wpmain;
|
||||
struct layout_cell *lcroot, *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);
|
||||
n = window_count_panes(w, 0);
|
||||
if (n <= 1)
|
||||
return;
|
||||
n--; /* take off main pane */
|
||||
|
||||
/* Find available width - take off one line for the border. */
|
||||
/* Find available width - take off one column for the border. */
|
||||
sx = w->sx - 1;
|
||||
|
||||
/* Get the main pane width. */
|
||||
@@ -437,48 +444,48 @@ layout_set_main_v(struct window *w)
|
||||
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);
|
||||
layout_free(w, 1);
|
||||
lcroot = w->layout_root = layout_create_cell(NULL);
|
||||
layout_set_size(lcroot, mainw + otherw + 1, sy, 0, 0);
|
||||
layout_make_node(lcroot, LAYOUT_LEFTRIGHT);
|
||||
|
||||
/* Create the main pane. */
|
||||
lcmain = layout_create_cell(lc);
|
||||
wpmain = layout_set_first_tiled(w);
|
||||
lcmain = wpmain->layout_cell;
|
||||
lcmain->parent = lcroot;
|
||||
layout_set_size(lcmain, mainw, sy, 0, 0);
|
||||
layout_make_leaf(lcmain, TAILQ_FIRST(&w->panes));
|
||||
TAILQ_INSERT_TAIL(&lc->cells, lcmain, entry);
|
||||
TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry);
|
||||
|
||||
/* 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);
|
||||
wp = TAILQ_NEXT(wpmain, entry);
|
||||
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
|
||||
wp = TAILQ_NEXT(wp, entry);
|
||||
TAILQ_INSERT_TAIL(&lcroot->cells, wp->layout_cell, entry);
|
||||
wp->layout_cell->parent = lcroot;
|
||||
} else {
|
||||
lcother = layout_create_cell(lcroot);
|
||||
layout_make_node(lcother, LAYOUT_TOPBOTTOM);
|
||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
||||
layout_set_size(lcother, otherw, sy, 0, 0);
|
||||
TAILQ_INSERT_TAIL(&lcroot->cells, lcother, entry);
|
||||
|
||||
/* Add the remaining panes as children. */
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
if (wp == TAILQ_FIRST(&w->panes))
|
||||
if (wp == wpmain)
|
||||
continue;
|
||||
lcchild = layout_create_cell(lcother);
|
||||
layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
|
||||
layout_make_leaf(lcchild, wp);
|
||||
lcchild = wp->layout_cell;
|
||||
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
|
||||
lcchild->parent = lcother;
|
||||
if (layout_cell_is_tiled(lcchild))
|
||||
layout_set_size(lcchild, otherw, PANE_MINIMUM,
|
||||
0, 0);
|
||||
}
|
||||
layout_spread_cell(w, lcother);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
}
|
||||
@@ -486,8 +493,8 @@ layout_set_main_v(struct window *w)
|
||||
static void
|
||||
layout_set_main_v_mirrored(struct window *w)
|
||||
{
|
||||
struct window_pane *wp;
|
||||
struct layout_cell *lc, *lcmain, *lcother, *lcchild;
|
||||
struct window_pane *wp, *wpmain;
|
||||
struct layout_cell *lcroot, *lcmain, *lcother, *lcchild;
|
||||
u_int n, mainw, otherw, sx, sy;
|
||||
char *cause;
|
||||
const char *s;
|
||||
@@ -495,12 +502,12 @@ layout_set_main_v_mirrored(struct window *w)
|
||||
layout_print_cell(w->layout_root, __func__, 1);
|
||||
|
||||
/* Get number of panes. */
|
||||
n = window_count_panes(w);
|
||||
n = window_count_panes(w, 0);
|
||||
if (n <= 1)
|
||||
return;
|
||||
n--; /* take off main pane */
|
||||
|
||||
/* Find available width - take off one line for the border. */
|
||||
/* Find available width - take off one column for the border. */
|
||||
sx = w->sx - 1;
|
||||
|
||||
/* Get the main pane width. */
|
||||
@@ -535,65 +542,65 @@ layout_set_main_v_mirrored(struct window *w)
|
||||
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);
|
||||
layout_free(w, 1);
|
||||
lcroot = w->layout_root = layout_create_cell(NULL);
|
||||
layout_set_size(lcroot, mainw + otherw + 1, sy, 0, 0);
|
||||
layout_make_node(lcroot, LAYOUT_LEFTRIGHT);
|
||||
|
||||
wpmain = layout_set_first_tiled(w);
|
||||
lcmain = wpmain->layout_cell;
|
||||
lcmain->parent = lcroot;
|
||||
layout_set_size(lcmain, mainw, sy, 0, 0);
|
||||
TAILQ_INSERT_TAIL(&lcroot->cells, lcmain, entry);
|
||||
|
||||
/* 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);
|
||||
wp = TAILQ_NEXT(wpmain, entry);
|
||||
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
|
||||
wp = TAILQ_NEXT(wp, entry);
|
||||
TAILQ_INSERT_HEAD(&lcroot->cells, wp->layout_cell, entry);
|
||||
wp->layout_cell->parent = lcroot;
|
||||
} else {
|
||||
lcother = layout_create_cell(lcroot);
|
||||
layout_make_node(lcother, LAYOUT_TOPBOTTOM);
|
||||
TAILQ_INSERT_TAIL(&lc->cells, lcother, entry);
|
||||
layout_set_size(lcother, otherw, sy, 0, 0);
|
||||
TAILQ_INSERT_HEAD(&lcroot->cells, lcother, entry);
|
||||
|
||||
/* Add the remaining panes as children. */
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
if (wp == TAILQ_FIRST(&w->panes))
|
||||
if (wp == wpmain)
|
||||
continue;
|
||||
lcchild = layout_create_cell(lcother);
|
||||
layout_set_size(lcchild, otherw, PANE_MINIMUM, 0, 0);
|
||||
layout_make_leaf(lcchild, wp);
|
||||
lcchild = wp->layout_cell;
|
||||
TAILQ_INSERT_TAIL(&lcother->cells, lcchild, entry);
|
||||
lcchild->parent = lcother;
|
||||
if (layout_cell_is_tiled(lcchild))
|
||||
layout_set_size(lcchild, otherw, PANE_MINIMUM,
|
||||
0, 0);
|
||||
}
|
||||
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);
|
||||
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
layout_set_tiled(struct window *w)
|
||||
{
|
||||
struct options *oo = w->options;
|
||||
struct window_pane *wp;
|
||||
struct layout_cell *lc, *lcrow, *lcchild;
|
||||
struct layout_cell *lcroot, *lcrow, *lcchild;
|
||||
u_int n, width, height, used, sx, sy;
|
||||
u_int i, j, columns, rows, max_columns;
|
||||
|
||||
layout_print_cell(w->layout_root, __func__, 1);
|
||||
|
||||
/* Get number of panes. */
|
||||
n = window_count_panes(w);
|
||||
n = window_count_panes(w, 0);
|
||||
if (n <= 1)
|
||||
return;
|
||||
|
||||
@@ -617,49 +624,59 @@ layout_set_tiled(struct window *w)
|
||||
if (height < PANE_MINIMUM)
|
||||
height = PANE_MINIMUM;
|
||||
|
||||
/* Free old tree and create a new root. */
|
||||
layout_free(w);
|
||||
lc = w->layout_root = layout_create_cell(NULL);
|
||||
sx = ((width + 1) * columns) - 1;
|
||||
if (sx < w->sx)
|
||||
sx = w->sx;
|
||||
sy = ((height + 1) * rows) - 1;
|
||||
if (sy < w->sy)
|
||||
sy = w->sy;
|
||||
layout_set_size(lc, sx, sy, 0, 0);
|
||||
layout_make_node(lc, LAYOUT_TOPBOTTOM);
|
||||
|
||||
/* Create a grid of the cells. */
|
||||
layout_free(w, 1);
|
||||
lcroot = w->layout_root = layout_create_cell(NULL);
|
||||
layout_set_size(lcroot, sx, sy, 0, 0);
|
||||
layout_make_node(lcroot, LAYOUT_TOPBOTTOM);
|
||||
|
||||
/* Create a grid of the tiled cells. */
|
||||
wp = TAILQ_FIRST(&w->panes);
|
||||
for (j = 0; j < rows; j++) {
|
||||
while (wp != NULL && !layout_cell_is_tiled(wp->layout_cell))
|
||||
wp = TAILQ_NEXT(wp, entry);
|
||||
/* If this is the last cell, all done. */
|
||||
if (wp == NULL)
|
||||
break;
|
||||
|
||||
/* Create the new row. */
|
||||
lcrow = layout_create_cell(lc);
|
||||
layout_set_size(lcrow, w->sx, height, 0, 0);
|
||||
TAILQ_INSERT_TAIL(&lc->cells, lcrow, entry);
|
||||
lcchild = wp->layout_cell;
|
||||
|
||||
/* If only one column, just use the row directly. */
|
||||
if (n - (j * columns) == 1 || columns == 1) {
|
||||
layout_make_leaf(lcrow, wp);
|
||||
lcchild->parent = lcroot;
|
||||
TAILQ_INSERT_TAIL(&lcroot->cells, lcchild, entry);
|
||||
layout_set_size(lcchild, w->sx, height, 0, 0);
|
||||
wp = TAILQ_NEXT(wp, entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Add in the columns. */
|
||||
/* Create the new row. */
|
||||
lcrow = layout_create_cell(lcroot);
|
||||
layout_make_node(lcrow, LAYOUT_LEFTRIGHT);
|
||||
layout_set_size(lcrow, w->sx, height, 0, 0);
|
||||
TAILQ_INSERT_TAIL(&lcroot->cells, lcrow, entry);
|
||||
|
||||
/* Add in the columns. */
|
||||
for (i = 0; i < columns; i++) {
|
||||
/* Create and add a pane cell. */
|
||||
lcchild = layout_create_cell(lcrow);
|
||||
layout_set_size(lcchild, width, height, 0, 0);
|
||||
layout_make_leaf(lcchild, wp);
|
||||
lcchild->parent = lcrow;
|
||||
TAILQ_INSERT_TAIL(&lcrow->cells, lcchild, entry);
|
||||
layout_set_size(lcchild, width, height, 0, 0);
|
||||
|
||||
/* Move to the next cell. */
|
||||
if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
|
||||
/* Move to the next non-floating cell. */
|
||||
wp = TAILQ_NEXT(wp, entry);
|
||||
while (wp != NULL &&
|
||||
!layout_cell_is_tiled(wp->layout_cell))
|
||||
wp = TAILQ_NEXT(wp, entry);
|
||||
if (wp == NULL)
|
||||
break;
|
||||
lcchild = wp->layout_cell;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -676,21 +693,19 @@ layout_set_tiled(struct window *w)
|
||||
w->sx - used);
|
||||
}
|
||||
|
||||
/* Adjust the last row height to fit if necessary. */
|
||||
used = (rows * height) + rows - 1;
|
||||
if (w->sy > used) {
|
||||
lcrow = TAILQ_LAST(&lc->cells, layout_cells);
|
||||
lcrow = TAILQ_LAST(&lcroot->cells, layout_cells);
|
||||
layout_resize_adjust(w, lcrow, LAYOUT_TOPBOTTOM,
|
||||
w->sy - used);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
window_resize(w, lcroot->sx, lcroot->sy, -1, -1);
|
||||
notify_window("window-layout-changed", w);
|
||||
server_redraw_window(w);
|
||||
}
|
||||
|
||||
22
menu.c
22
menu.c
@@ -257,8 +257,7 @@ menu_reapply_styles(struct menu_data *md, struct client *c)
|
||||
}
|
||||
|
||||
void
|
||||
menu_draw_cb(struct client *c, void *data,
|
||||
__unused struct screen_redraw_ctx *rctx)
|
||||
menu_draw_cb(struct client *c, void *data)
|
||||
{
|
||||
struct menu_data *md = data;
|
||||
struct tty *tty = &c->tty;
|
||||
@@ -281,10 +280,8 @@ menu_draw_cb(struct client *c, void *data,
|
||||
&md->style_gc, &md->border_style_gc, &md->selected_style_gc);
|
||||
screen_write_stop(&ctx);
|
||||
|
||||
for (i = 0; i < screen_size_y(&md->s); i++) {
|
||||
tty_draw_line(tty, s, 0, i, menu->width + 4, px, py + i,
|
||||
&grid_default_cell, NULL);
|
||||
}
|
||||
for (i = 0; i < screen_size_y(&md->s); i++)
|
||||
tty_draw_line(tty, s, 0, i, menu->width + 4, px, py + i, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -363,7 +360,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
|
||||
name = menu->items[i].name;
|
||||
if (name == NULL || *name == '-')
|
||||
continue;
|
||||
if (event->key == menu->items[i].key) {
|
||||
if ((event->key & ~KEYC_MASK_FLAGS) == menu->items[i].key) {
|
||||
md->choice = i;
|
||||
goto chosen;
|
||||
}
|
||||
@@ -440,7 +437,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (name == NULL || *name == '-') {
|
||||
while ((name == NULL || *name == '-') && md->choice != 0) {
|
||||
md->choice--;
|
||||
name = menu->items[md->choice].name;
|
||||
}
|
||||
@@ -450,7 +447,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
|
||||
case KEYC_HOME:
|
||||
md->choice = 0;
|
||||
name = menu->items[md->choice].name;
|
||||
while (name == NULL || *name == '-') {
|
||||
while ((name == NULL || *name == '-') && md->choice != count - 1) {
|
||||
md->choice++;
|
||||
name = menu->items[md->choice].name;
|
||||
}
|
||||
@@ -460,7 +457,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
|
||||
case KEYC_END:
|
||||
md->choice = count - 1;
|
||||
name = menu->items[md->choice].name;
|
||||
while (name == NULL || *name == '-') {
|
||||
while ((name == NULL || *name == '-') && md->choice != 0) {
|
||||
md->choice--;
|
||||
name = menu->items[md->choice].name;
|
||||
}
|
||||
@@ -471,6 +468,7 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
|
||||
case '\r':
|
||||
goto chosen;
|
||||
case '\033': /* Escape */
|
||||
case '['|KEYC_CTRL:
|
||||
case 'c'|KEYC_CTRL:
|
||||
case 'g'|KEYC_CTRL:
|
||||
case 'q':
|
||||
@@ -637,7 +635,7 @@ menu_display(struct menu *menu, int flags, int starting_choice,
|
||||
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,
|
||||
menu_key_cb, menu_free_cb, menu_resize_cb, md);
|
||||
server_client_set_overlay(c, 0, menu_check_cb, menu_mode_cb,
|
||||
menu_draw_cb, menu_key_cb, menu_free_cb, menu_resize_cb, md);
|
||||
return (0);
|
||||
}
|
||||
|
||||
667
mode-tree.c
667
mode-tree.c
@@ -36,7 +36,26 @@ enum mode_tree_preview {
|
||||
MODE_TREE_PREVIEW_BIG
|
||||
};
|
||||
|
||||
#define MODE_TREE_PREFIX_STYLE \
|
||||
"#{?mode_tree_selected,#[default]#[noacs]," \
|
||||
"#[fg=themelightgrey]#[bg=default]#[noacs]}"
|
||||
|
||||
#define MODE_TREE_PREFIX_FORMAT \
|
||||
MODE_TREE_PREFIX_STYLE \
|
||||
"#{p/#{mode_tree_key_width}:" \
|
||||
"#{?#{!=:#{mode_tree_key},},(#{mode_tree_key}),}}" \
|
||||
"#{R:#{?mode_tree_parent_last, ," \
|
||||
"#[acs]x" MODE_TREE_PREFIX_STYLE " }," \
|
||||
"#{mode_tree_repeat}}" \
|
||||
"#{?mode_tree_branch," \
|
||||
"#[acs]#{?mode_tree_last,mq,tq}" MODE_TREE_PREFIX_STYLE "> ,}" \
|
||||
"#{?mode_tree_has_children," \
|
||||
"#{?mode_tree_expanded,#[fg=themered]-" MODE_TREE_PREFIX_STYLE " ," \
|
||||
"#[fg=themegreen]+" MODE_TREE_PREFIX_STYLE " }," \
|
||||
"#{?mode_tree_flat,, }}"
|
||||
|
||||
struct mode_tree_item;
|
||||
struct mode_tree_prompt;
|
||||
TAILQ_HEAD(mode_tree_list, mode_tree_item);
|
||||
|
||||
struct mode_tree_data {
|
||||
@@ -49,15 +68,17 @@ struct mode_tree_data {
|
||||
const struct menu_item *menu;
|
||||
|
||||
struct sort_criteria sort_crit;
|
||||
const char *view_name;
|
||||
|
||||
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_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;
|
||||
mode_tree_swap_cb swapcb;
|
||||
mode_tree_sort_cb sortcb;
|
||||
mode_tree_help_cb helpcb;
|
||||
|
||||
struct mode_tree_list children;
|
||||
struct mode_tree_list saved;
|
||||
@@ -75,6 +96,10 @@ struct mode_tree_data {
|
||||
u_int current;
|
||||
|
||||
struct screen screen;
|
||||
struct prompt *prompt;
|
||||
struct mode_tree_prompt *prompt_data;
|
||||
u_int prompt_cx;
|
||||
int prompt_top;
|
||||
|
||||
int preview;
|
||||
char *search;
|
||||
@@ -82,6 +107,7 @@ struct mode_tree_data {
|
||||
int no_matches;
|
||||
enum mode_tree_search_dir search_dir;
|
||||
int search_icase;
|
||||
int help;
|
||||
};
|
||||
|
||||
struct mode_tree_item {
|
||||
@@ -121,7 +147,25 @@ struct mode_tree_menu {
|
||||
u_int line;
|
||||
};
|
||||
|
||||
static void mode_tree_free_items(struct mode_tree_list *);
|
||||
/*
|
||||
* Wrapper around a prompt owned by a mode tree. The mode tree holds a reference
|
||||
* while the prompt is alive; the wrapper callbacks forward to the caller's
|
||||
* callbacks and drop that reference when the prompt is freed.
|
||||
*/
|
||||
struct mode_tree_prompt {
|
||||
struct mode_tree_data *mtd;
|
||||
struct client *c;
|
||||
mode_tree_prompt_input_cb inputcb;
|
||||
prompt_free_cb freecb;
|
||||
void *data;
|
||||
};
|
||||
|
||||
static void mode_tree_free_items(struct mode_tree_list *);
|
||||
static void mode_tree_draw_help(struct mode_tree_data *,
|
||||
struct screen_write_ctx *);
|
||||
static void mode_tree_draw_prompt(struct mode_tree_data *,
|
||||
struct screen_write_ctx *);
|
||||
static enum cmd_retval mode_tree_prompt_accept(struct cmdq_item *, void *);
|
||||
|
||||
static const struct menu_item mode_tree_menu_items[] = {
|
||||
{ "Scroll Left", '<', NULL },
|
||||
@@ -132,6 +176,56 @@ static const struct menu_item mode_tree_menu_items[] = {
|
||||
{ NULL, KEYC_NONE, NULL }
|
||||
};
|
||||
|
||||
static const char* mode_tree_help_start[] = {
|
||||
"#[fg=themelightgrey]"
|
||||
" Up, k #[#{E:tree-mode-border-style},acs]x#[default] Move cursor up",
|
||||
"#[fg=themelightgrey]"
|
||||
" Down, j #[#{E:tree-mode-border-style},acs]x#[default] Move cursor down",
|
||||
"#[fg=themelightgrey]"
|
||||
" g #[#{E:tree-mode-border-style},acs]x#[default] Go to top",
|
||||
"#[fg=themelightgrey]"
|
||||
" G #[#{E:tree-mode-border-style},acs]x#[default] Go to bottom",
|
||||
"#[fg=themelightgrey]"
|
||||
" PPage, C-b #[#{E:tree-mode-border-style},acs]x#[default] Page up",
|
||||
"#[fg=themelightgrey]"
|
||||
" NPage, C-f #[#{E:tree-mode-border-style},acs]x#[default] Page down",
|
||||
"#[fg=themelightgrey]"
|
||||
" Left, h #[#{E:tree-mode-border-style},acs]x#[default] Collapse %1",
|
||||
"#[fg=themelightgrey]"
|
||||
" Right, l #[#{E:tree-mode-border-style},acs]x#[default] Expand %1",
|
||||
"#[fg=themelightgrey]"
|
||||
" M-- #[#{E:tree-mode-border-style},acs]x#[default] Collapse all %1s",
|
||||
"#[fg=themelightgrey]"
|
||||
" M-+ #[#{E:tree-mode-border-style},acs]x#[default] Expand all %1s",
|
||||
"#[fg=themelightgrey]"
|
||||
" t #[#{E:tree-mode-border-style},acs]x#[default] Toggle %1 tag",
|
||||
"#[fg=themelightgrey]"
|
||||
" T #[#{E:tree-mode-border-style},acs]x#[default] Untag all %1s",
|
||||
"#[fg=themelightgrey]"
|
||||
" C-t #[#{E:tree-mode-border-style},acs]x#[default] Tag all %1s",
|
||||
"#[fg=themelightgrey]"
|
||||
" C-s #[#{E:tree-mode-border-style},acs]x#[default] Search forward",
|
||||
"#[fg=themelightgrey]"
|
||||
" n #[#{E:tree-mode-border-style},acs]x#[default] Repeat search forward",
|
||||
"#[fg=themelightgrey]"
|
||||
" N #[#{E:tree-mode-border-style},acs]x#[default] Repeat search backward",
|
||||
"#[fg=themelightgrey]"
|
||||
" f #[#{E:tree-mode-border-style},acs]x#[default] Filter %1s",
|
||||
"#[fg=themelightgrey]"
|
||||
" O #[#{E:tree-mode-border-style},acs]x#[default] Change sort order",
|
||||
"#[fg=themelightgrey]"
|
||||
" r #[#{E:tree-mode-border-style},acs]x#[default] Reverse sort order",
|
||||
"#[fg=themelightgrey]"
|
||||
" v #[#{E:tree-mode-border-style},acs]x#[default] Toggle preview",
|
||||
NULL
|
||||
};
|
||||
static const char* mode_tree_help_end[] = {
|
||||
"#[fg=themelightgrey]"
|
||||
" q, Escape #[#{E:tree-mode-border-style},acs]x#[default] Exit mode",
|
||||
NULL
|
||||
};
|
||||
#define MODE_TREE_HELP_DEFAULT_WIDTH 39
|
||||
|
||||
static int
|
||||
mode_tree_is_lowercase(const char *ptr)
|
||||
{
|
||||
@@ -270,6 +364,8 @@ mode_tree_clear_tagged(struct mode_tree_list *mtl)
|
||||
void
|
||||
mode_tree_up(struct mode_tree_data *mtd, int wrap)
|
||||
{
|
||||
if (mtd->line_size == 0)
|
||||
return;
|
||||
if (mtd->current == 0) {
|
||||
if (wrap) {
|
||||
mtd->current = mtd->line_size - 1;
|
||||
@@ -286,6 +382,8 @@ mode_tree_up(struct mode_tree_data *mtd, int wrap)
|
||||
int
|
||||
mode_tree_down(struct mode_tree_data *mtd, int wrap)
|
||||
{
|
||||
if (mtd->line_size == 0)
|
||||
return (0);
|
||||
if (mtd->current == mtd->line_size - 1) {
|
||||
if (wrap) {
|
||||
mtd->current = 0;
|
||||
@@ -332,6 +430,8 @@ mode_tree_swap(struct mode_tree_data *mtd, int direction)
|
||||
void *
|
||||
mode_tree_get_current(struct mode_tree_data *mtd)
|
||||
{
|
||||
if (mtd->line_size == 0)
|
||||
return (NULL);
|
||||
return (mtd->line_list[mtd->current].item->itemdata);
|
||||
}
|
||||
|
||||
@@ -341,6 +441,13 @@ mode_tree_get_current_name(struct mode_tree_data *mtd)
|
||||
return (mtd->line_list[mtd->current].item->name);
|
||||
}
|
||||
|
||||
void
|
||||
mode_tree_select_top(struct mode_tree_data *mtd)
|
||||
{
|
||||
mtd->current = 0;
|
||||
mtd->offset = 0;
|
||||
}
|
||||
|
||||
void
|
||||
mode_tree_expand_current(struct mode_tree_data *mtd)
|
||||
{
|
||||
@@ -402,6 +509,8 @@ mode_tree_set_current(struct mode_tree_data *mtd, uint64_t tag)
|
||||
return (1);
|
||||
}
|
||||
if (mtd->current >= mtd->line_size) {
|
||||
if (mtd->line_size == 0)
|
||||
return (0);
|
||||
mtd->current = mtd->line_size - 1;
|
||||
if (mtd->current > mtd->height - 1)
|
||||
mtd->offset = mtd->current - mtd->height + 1;
|
||||
@@ -453,8 +562,9 @@ 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,
|
||||
mode_tree_swap_cb swapcb, mode_tree_sort_cb sortcb, void *modedata,
|
||||
const struct menu_item *menu, struct screen **s)
|
||||
mode_tree_swap_cb swapcb, mode_tree_sort_cb sortcb,
|
||||
mode_tree_help_cb helpcb, void *modedata, const struct menu_item *menu,
|
||||
struct screen **s)
|
||||
{
|
||||
struct mode_tree_data *mtd;
|
||||
|
||||
@@ -465,7 +575,9 @@ mode_tree_start(struct window_pane *wp, struct args *args,
|
||||
mtd->modedata = modedata;
|
||||
mtd->menu = menu;
|
||||
|
||||
if (args_has(args, 'N') > 1)
|
||||
if (drawcb == NULL)
|
||||
mtd->preview = MODE_TREE_PREVIEW_OFF;
|
||||
else if (args_has(args, 'N') > 1)
|
||||
mtd->preview = MODE_TREE_PREVIEW_BIG;
|
||||
else if (args_has(args, 'N'))
|
||||
mtd->preview = MODE_TREE_PREVIEW_OFF;
|
||||
@@ -488,6 +600,7 @@ mode_tree_start(struct window_pane *wp, struct args *args,
|
||||
mtd->keycb = keycb;
|
||||
mtd->swapcb = swapcb;
|
||||
mtd->sortcb = sortcb;
|
||||
mtd->helpcb = helpcb;
|
||||
|
||||
TAILQ_INIT(&mtd->children);
|
||||
|
||||
@@ -596,6 +709,7 @@ mode_tree_free(struct mode_tree_data *mtd)
|
||||
if (mtd->zoomed == 0)
|
||||
server_unzoom_window(wp->window);
|
||||
|
||||
mode_tree_clear_prompt(mtd);
|
||||
mode_tree_free_items(&mtd->children);
|
||||
mode_tree_clear_lines(mtd);
|
||||
screen_free(&mtd->screen);
|
||||
@@ -659,6 +773,12 @@ mode_tree_add(struct mode_tree_data *mtd, struct mode_tree_item *parent,
|
||||
return (mti);
|
||||
}
|
||||
|
||||
void
|
||||
mode_tree_view_name(struct mode_tree_data *mtd, const char *name)
|
||||
{
|
||||
mtd->view_name = name;
|
||||
}
|
||||
|
||||
void
|
||||
mode_tree_draw_as_parent(struct mode_tree_item *mti)
|
||||
{
|
||||
@@ -672,8 +792,8 @@ mode_tree_no_tag(struct mode_tree_item *mti)
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the alignment of mti->name: -1 to align left, 0 (default) to not align,
|
||||
* or 1 to align right.
|
||||
* Set the alignment of the item 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)
|
||||
@@ -702,25 +822,34 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
||||
struct mode_tree_item *mti;
|
||||
struct options *oo = wp->window->options;
|
||||
struct screen_write_ctx ctx;
|
||||
struct grid_cell gc0, gc;
|
||||
u_int w, h, i, j, sy, box_x, box_y, width;
|
||||
char *text, *start, *key;
|
||||
const char *tag, *symbol;
|
||||
size_t size, n;
|
||||
int keylen, pad, alignlen[mtd->maxdepth + 1];
|
||||
struct format_tree *ft;
|
||||
struct grid_cell gc0, gc, box_gc;
|
||||
u_int w, h, i, sy, box_x, box_y;
|
||||
u_int width, text_width, prefix_width, left;
|
||||
char *text, *prefix;
|
||||
const char *tag, *separator;
|
||||
size_t n;
|
||||
int keylen, alignlen[mtd->maxdepth + 1];
|
||||
int dfg, dfg0;
|
||||
|
||||
if (mtd->line_size == 0)
|
||||
return;
|
||||
|
||||
memcpy(&gc0, &grid_default_cell, sizeof gc0);
|
||||
memcpy(&gc, &grid_default_cell, sizeof gc);
|
||||
style_apply(&gc, oo, "mode-style", NULL);
|
||||
style_apply(&gc, oo, "tree-mode-selection-style", NULL);
|
||||
memcpy(&box_gc, &grid_default_cell, sizeof box_gc);
|
||||
style_apply(&box_gc, oo, "tree-mode-border-style", NULL);
|
||||
|
||||
dfg = gc.fg;
|
||||
dfg0 = gc0.fg;
|
||||
|
||||
w = mtd->width;
|
||||
h = mtd->height;
|
||||
|
||||
screen_write_start(&ctx, s);
|
||||
screen_write_clearscreen(&ctx, 8);
|
||||
ft = format_create_defaults(NULL, NULL, NULL, NULL, wp);
|
||||
|
||||
keylen = 0;
|
||||
for (i = 0; i < mtd->line_size; i++) {
|
||||
@@ -751,81 +880,98 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
||||
|
||||
screen_write_cursormove(&ctx, 0, i - mtd->offset, 0);
|
||||
|
||||
pad = keylen - 2 - mti->keylen;
|
||||
if (mti->key != KEYC_NONE)
|
||||
xasprintf(&key, "(%s)%*s", mti->keystr, pad, "");
|
||||
format_add(ft, "mode_tree_key", "%s", mti->keystr);
|
||||
else
|
||||
key = xstrdup("");
|
||||
|
||||
if (line->flat)
|
||||
symbol = "";
|
||||
else if (TAILQ_EMPTY(&mti->children))
|
||||
symbol = " ";
|
||||
else if (mti->expanded)
|
||||
symbol = "- ";
|
||||
else
|
||||
symbol = "+ ";
|
||||
|
||||
if (line->depth == 0)
|
||||
start = xstrdup(symbol);
|
||||
else {
|
||||
size = (4 * line->depth) + 32;
|
||||
|
||||
start = xcalloc(1, size);
|
||||
for (j = 1; j < line->depth; j++) {
|
||||
if (mti->parent != NULL &&
|
||||
mtd->line_list[mti->parent->line].last)
|
||||
strlcat(start, " ", size);
|
||||
else
|
||||
strlcat(start, "\001x\001 ", size);
|
||||
}
|
||||
if (line->last)
|
||||
strlcat(start, "\001mq\001> ", size);
|
||||
format_add(ft, "mode_tree_key", "%s", "");
|
||||
format_add(ft, "mode_tree_key_width", "%d", keylen);
|
||||
format_add(ft, "mode_tree_selected", "%d", i == mtd->current);
|
||||
if (line->depth == 0) {
|
||||
format_add(ft, "mode_tree_repeat", "%u", 0);
|
||||
format_add(ft, "mode_tree_branch", "0");
|
||||
format_add(ft, "mode_tree_parent_last", "0");
|
||||
} else {
|
||||
format_add(ft, "mode_tree_repeat", "%u",
|
||||
line->depth - 1);
|
||||
format_add(ft, "mode_tree_branch", "1");
|
||||
if (mti->parent != NULL &&
|
||||
mtd->line_list[mti->parent->line].last)
|
||||
format_add(ft, "mode_tree_parent_last", "1");
|
||||
else
|
||||
strlcat(start, "\001tq\001> ", size);
|
||||
strlcat(start, symbol, size);
|
||||
format_add(ft, "mode_tree_parent_last", "0");
|
||||
}
|
||||
if (TAILQ_EMPTY(&mti->children))
|
||||
format_add(ft, "mode_tree_has_children", "0");
|
||||
else
|
||||
format_add(ft, "mode_tree_has_children", "1");
|
||||
format_add(ft, "mode_tree_last", "%d", line->last);
|
||||
format_add(ft, "mode_tree_expanded", "%d", mti->expanded);
|
||||
format_add(ft, "mode_tree_flat", "%d", line->flat);
|
||||
prefix = format_expand(ft, MODE_TREE_PREFIX_FORMAT);
|
||||
prefix_width = format_width(prefix);
|
||||
if (prefix_width > w)
|
||||
prefix_width = w;
|
||||
|
||||
if (mti->tagged)
|
||||
tag = "*";
|
||||
else
|
||||
tag = "";
|
||||
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;
|
||||
free(start);
|
||||
if (mti->text != NULL)
|
||||
separator = "#[fg=themelightgrey]: #[default]";
|
||||
else
|
||||
separator = "";
|
||||
xasprintf(&text, "%*s%s%s",
|
||||
mti->align * alignlen[line->depth], mti->name, tag, separator);
|
||||
text_width = format_width(text);
|
||||
left = (prefix_width < w) ? (w - prefix_width) : 0;
|
||||
if (text_width > left)
|
||||
text_width = left;
|
||||
width = prefix_width + text_width;
|
||||
|
||||
if (mti->tagged) {
|
||||
gc.attr ^= GRID_ATTR_BRIGHT;
|
||||
gc0.attr ^= GRID_ATTR_BRIGHT;
|
||||
gc.fg = COLOUR_THEME_CYAN|COLOUR_FLAG_THEME;
|
||||
gc0.fg = COLOUR_THEME_CYAN|COLOUR_FLAG_THEME;
|
||||
}
|
||||
|
||||
if (i != mtd->current) {
|
||||
screen_write_clearendofline(&ctx, 8);
|
||||
screen_write_nputs(&ctx, w, &gc0, "%s", text);
|
||||
if (mti->text != NULL) {
|
||||
format_draw(&ctx, &gc0, w - width, mti->text,
|
||||
NULL, 0);
|
||||
format_draw(&ctx, &grid_default_cell, prefix_width,
|
||||
prefix, NULL, 0);
|
||||
if (left != 0) {
|
||||
screen_write_cursormove(&ctx, prefix_width,
|
||||
i - mtd->offset, 0);
|
||||
format_draw(&ctx, &gc0, left, text, NULL, 0);
|
||||
if (mti->text != NULL && width < w) {
|
||||
screen_write_cursormove(&ctx, width,
|
||||
i - mtd->offset, 0);
|
||||
format_draw(&ctx, &gc0, w - width,
|
||||
mti->text, NULL, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
screen_write_clearendofline(&ctx, gc.bg);
|
||||
screen_write_nputs(&ctx, w, &gc, "%s", text);
|
||||
if (mti->text != NULL) {
|
||||
format_draw(&ctx, &gc, w - width, mti->text,
|
||||
NULL, 0);
|
||||
format_draw(&ctx, &gc, prefix_width, prefix, NULL, 0);
|
||||
if (left != 0) {
|
||||
screen_write_cursormove(&ctx, prefix_width,
|
||||
i - mtd->offset, 0);
|
||||
format_draw(&ctx, &gc, left, text, NULL, 0);
|
||||
if (mti->text != NULL && width < w) {
|
||||
screen_write_cursormove(&ctx, width,
|
||||
i - mtd->offset, 0);
|
||||
format_draw(&ctx, &gc, w - width,
|
||||
mti->text, NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(text);
|
||||
free(key);
|
||||
free(prefix);
|
||||
|
||||
if (mti->tagged) {
|
||||
gc.attr ^= GRID_ATTR_BRIGHT;
|
||||
gc0.attr ^= GRID_ATTR_BRIGHT;
|
||||
gc.fg = dfg;
|
||||
gc0.fg = dfg0;
|
||||
}
|
||||
}
|
||||
format_free(ft);
|
||||
|
||||
if (mtd->preview == MODE_TREE_PREVIEW_OFF)
|
||||
goto done;
|
||||
@@ -840,31 +986,34 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
||||
mti = mti->parent;
|
||||
|
||||
screen_write_cursormove(&ctx, 0, h, 0);
|
||||
screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, NULL, NULL);
|
||||
screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, &box_gc, NULL);
|
||||
|
||||
if (mtd->sort_crit.order_seq != NULL) {
|
||||
xasprintf(&text, " %s (sort: %s%s)", mti->name,
|
||||
xasprintf(&text, " %s (sort: %s%s)%s%s%s", mti->name,
|
||||
sort_order_to_string(mtd->sort_crit.order),
|
||||
mtd->sort_crit.reversed ? ", reversed" : "");
|
||||
mtd->sort_crit.reversed ? ", reversed" : "",
|
||||
mtd->view_name == NULL ? "" : " (view: ",
|
||||
mtd->view_name == NULL ? "" : mtd->view_name,
|
||||
mtd->view_name == NULL ? "" : ")");
|
||||
} else
|
||||
xasprintf(&text, " %s", mti->name);
|
||||
if (w - 2 >= strlen(text)) {
|
||||
screen_write_cursormove(&ctx, 1, h, 0);
|
||||
screen_write_puts(&ctx, &gc0, "%s", text);
|
||||
screen_write_puts(&ctx, &box_gc, "%s", text);
|
||||
|
||||
if (mtd->no_matches)
|
||||
n = (sizeof "no matches") - 1;
|
||||
else
|
||||
n = (sizeof "active") - 1;
|
||||
if (mtd->filter != NULL && w - 2 >= strlen(text) + 10 + n + 2) {
|
||||
screen_write_puts(&ctx, &gc0, " (filter: ");
|
||||
screen_write_puts(&ctx, &box_gc, " (filter: ");
|
||||
if (mtd->no_matches)
|
||||
screen_write_puts(&ctx, &gc, "no matches");
|
||||
screen_write_puts(&ctx, &box_gc, "no matches");
|
||||
else
|
||||
screen_write_puts(&ctx, &gc0, "active");
|
||||
screen_write_puts(&ctx, &gc0, ") ");
|
||||
screen_write_puts(&ctx, &box_gc, "active");
|
||||
screen_write_puts(&ctx, &box_gc, ") ");
|
||||
} else
|
||||
screen_write_puts(&ctx, &gc0, " ");
|
||||
screen_write_puts(&ctx, &box_gc, " ");
|
||||
}
|
||||
free(text);
|
||||
|
||||
@@ -877,10 +1026,151 @@ mode_tree_draw(struct mode_tree_data *mtd)
|
||||
}
|
||||
|
||||
done:
|
||||
screen_write_cursormove(&ctx, 0, mtd->current - mtd->offset, 0);
|
||||
if (mtd->help)
|
||||
mode_tree_draw_help(mtd, &ctx);
|
||||
if (mtd->prompt != NULL)
|
||||
mode_tree_draw_prompt(mtd, &ctx);
|
||||
else {
|
||||
s->mode &= ~MODE_CURSOR;
|
||||
screen_write_cursormove(&ctx, 0, mtd->current - mtd->offset, 0);
|
||||
}
|
||||
screen_write_stop(&ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
mode_tree_draw_prompt(struct mode_tree_data *mtd, struct screen_write_ctx *ctx)
|
||||
{
|
||||
struct screen *s = &mtd->screen;
|
||||
struct prompt_draw_data pdd;
|
||||
u_int sx = screen_size_x(s), sy = screen_size_y(s);
|
||||
u_int py;
|
||||
|
||||
if (sx == 0 || sy == 0)
|
||||
return;
|
||||
|
||||
if (mtd->prompt_top)
|
||||
py = 0;
|
||||
else
|
||||
py = sy - 1;
|
||||
|
||||
pdd.ctx = ctx;
|
||||
pdd.cursor_x = &mtd->prompt_cx;
|
||||
pdd.area_x = 0;
|
||||
pdd.area_width = sx;
|
||||
pdd.prompt_line = py;
|
||||
|
||||
s->mode |= MODE_CURSOR;
|
||||
prompt_draw(mtd->prompt, &pdd);
|
||||
screen_write_cursormove(ctx, mtd->prompt_cx, py, 0);
|
||||
}
|
||||
|
||||
void
|
||||
mode_tree_clear_prompt(struct mode_tree_data *mtd)
|
||||
{
|
||||
struct prompt *prompt = mtd->prompt;
|
||||
|
||||
if (mtd->prompt != NULL) {
|
||||
mtd->prompt = NULL;
|
||||
prompt_free(prompt);
|
||||
mtd->screen.mode &= ~MODE_CURSOR;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
mode_tree_has_prompt(struct mode_tree_data *mtd)
|
||||
{
|
||||
return (mtd->prompt != NULL);
|
||||
}
|
||||
|
||||
static enum cmd_retval
|
||||
mode_tree_prompt_accept(struct cmdq_item *item, void *data)
|
||||
{
|
||||
struct mode_tree_data *mtd = data;
|
||||
struct client *c = cmdq_get_client(item);
|
||||
key_code key = 'y';
|
||||
|
||||
if (mtd->prompt != NULL && c != NULL)
|
||||
mode_tree_key(mtd, c, &key, NULL, NULL, NULL);
|
||||
|
||||
mode_tree_remove_ref(mtd);
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
||||
static enum prompt_result
|
||||
mode_tree_prompt_input_callback(void *data, const char *s,
|
||||
enum prompt_key_result key)
|
||||
{
|
||||
struct mode_tree_prompt *mtp = data;
|
||||
|
||||
if (mtp->inputcb != NULL)
|
||||
return (mtp->inputcb(mtp->c, mtp->data, s, key));
|
||||
return (PROMPT_CLOSE);
|
||||
}
|
||||
|
||||
static void
|
||||
mode_tree_prompt_free_callback(void *data)
|
||||
{
|
||||
struct mode_tree_prompt *mtp = data;
|
||||
|
||||
if (mtp->mtd->prompt_data == mtp)
|
||||
mtp->mtd->prompt_data = NULL;
|
||||
if (mtp->freecb != NULL)
|
||||
mtp->freecb(mtp->data);
|
||||
mode_tree_remove_ref(mtp->mtd);
|
||||
free(mtp);
|
||||
}
|
||||
|
||||
void
|
||||
mode_tree_set_prompt(struct mode_tree_data *mtd, struct client *c,
|
||||
const char *prompt, const char *input, enum prompt_type type, int flags,
|
||||
mode_tree_prompt_input_cb inputcb, prompt_free_cb freecb, void *data)
|
||||
{
|
||||
struct session *s;
|
||||
struct options *oo;
|
||||
struct prompt_create_data pd;
|
||||
struct mode_tree_prompt *mtp;
|
||||
|
||||
if (c != NULL && c->session != NULL) {
|
||||
s = c->session;
|
||||
oo = s->options;
|
||||
} else {
|
||||
s = NULL;
|
||||
oo = global_s_options;
|
||||
}
|
||||
|
||||
mode_tree_clear_prompt(mtd);
|
||||
|
||||
mtp = xcalloc(1, sizeof *mtp);
|
||||
mtp->mtd = mtd;
|
||||
mtp->c = c;
|
||||
mtp->inputcb = inputcb;
|
||||
mtp->freecb = freecb;
|
||||
mtp->data = data;
|
||||
|
||||
mtd->references++;
|
||||
mtd->prompt_top = options_get_number(oo, "status-position") == 0;
|
||||
|
||||
memset(&pd, 0, sizeof pd);
|
||||
prompt_set_options(&pd, s);
|
||||
pd.prompt = prompt;
|
||||
pd.input = input;
|
||||
pd.type = type;
|
||||
pd.flags = flags|PROMPT_ISMODE;
|
||||
pd.inputcb = mode_tree_prompt_input_callback;
|
||||
pd.freecb = mode_tree_prompt_free_callback;
|
||||
pd.data = mtp;
|
||||
mtd->prompt = prompt_create(&pd);
|
||||
mtd->prompt_data = mtp;
|
||||
|
||||
mode_tree_draw(mtd);
|
||||
mtd->wp->flags |= PANE_REDRAW;
|
||||
|
||||
if ((flags & PROMPT_SINGLE) && (flags & PROMPT_ACCEPT) && c != NULL) {
|
||||
mtd->references++;
|
||||
cmdq_append(c, cmdq_get_callback(mode_tree_prompt_accept, mtd));
|
||||
}
|
||||
}
|
||||
|
||||
static struct mode_tree_item *
|
||||
mode_tree_search_backward(struct mode_tree_data *mtd)
|
||||
{
|
||||
@@ -1001,41 +1291,37 @@ mode_tree_search_set(struct mode_tree_data *mtd)
|
||||
mtd->wp->flags |= PANE_REDRAW;
|
||||
}
|
||||
|
||||
static int
|
||||
static enum prompt_result
|
||||
mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
|
||||
__unused int done)
|
||||
enum prompt_key_result key)
|
||||
{
|
||||
struct mode_tree_data *mtd = data;
|
||||
|
||||
if (mtd->dead)
|
||||
return (0);
|
||||
return (PROMPT_CLOSE);
|
||||
|
||||
free(mtd->search);
|
||||
if (s == NULL || *s == '\0') {
|
||||
if (s == NULL || *s == '\0')
|
||||
mtd->search = NULL;
|
||||
return (0);
|
||||
else {
|
||||
mtd->search = xstrdup(s);
|
||||
mtd->search_icase = mode_tree_is_lowercase(s);
|
||||
mode_tree_search_set(mtd);
|
||||
}
|
||||
mtd->search = xstrdup(s);
|
||||
mtd->search_icase = mode_tree_is_lowercase(s);
|
||||
mode_tree_search_set(mtd);
|
||||
|
||||
return (0);
|
||||
if (key == PROMPT_KEY_HANDLED)
|
||||
return (PROMPT_CONTINUE);
|
||||
return (PROMPT_CLOSE);
|
||||
}
|
||||
|
||||
static void
|
||||
mode_tree_search_free(void *data)
|
||||
{
|
||||
mode_tree_remove_ref(data);
|
||||
}
|
||||
|
||||
static int
|
||||
static enum prompt_result
|
||||
mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
|
||||
__unused int done)
|
||||
enum prompt_key_result key)
|
||||
{
|
||||
struct mode_tree_data *mtd = data;
|
||||
|
||||
if (mtd->dead)
|
||||
return (0);
|
||||
return (PROMPT_CLOSE);
|
||||
|
||||
if (mtd->filter != NULL)
|
||||
free(mtd->filter);
|
||||
@@ -1048,13 +1334,20 @@ mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
|
||||
mode_tree_draw(mtd);
|
||||
mtd->wp->flags |= PANE_REDRAW;
|
||||
|
||||
return (0);
|
||||
if (key == PROMPT_KEY_HANDLED)
|
||||
return (PROMPT_CONTINUE);
|
||||
return (PROMPT_CLOSE);
|
||||
}
|
||||
|
||||
static void
|
||||
mode_tree_filter_free(void *data)
|
||||
mode_tree_clear_filter(struct mode_tree_data *mtd)
|
||||
{
|
||||
mode_tree_remove_ref(data);
|
||||
free(mtd->filter);
|
||||
mtd->filter = NULL;
|
||||
|
||||
mode_tree_build(mtd);
|
||||
mode_tree_draw(mtd);
|
||||
mtd->wp->flags |= PANE_REDRAW;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1123,14 +1416,160 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mode_tree_draw_help_line(struct screen_write_ctx *ctx,
|
||||
const struct grid_cell *gc, struct format_tree *ft, const char *line,
|
||||
const char *item, u_int x, u_int y, u_int w)
|
||||
{
|
||||
char *expanded, *replaced;
|
||||
|
||||
replaced = cmd_template_replace(line, item, 1);
|
||||
expanded = format_expand(ft, replaced);
|
||||
free(replaced);
|
||||
screen_write_cursormove(ctx, x, y, 0);
|
||||
screen_write_clearcharacter(ctx, w, gc->bg);
|
||||
screen_write_cursormove(ctx, x, y, 0);
|
||||
format_draw(ctx, gc, w, expanded, NULL, 0);
|
||||
free(expanded);
|
||||
}
|
||||
|
||||
static void
|
||||
mode_tree_draw_help(struct mode_tree_data *mtd, struct screen_write_ctx *ctx)
|
||||
{
|
||||
struct screen *s = &mtd->screen;
|
||||
struct options *oo = mtd->wp->window->options;
|
||||
struct grid_cell box_gc, gc;
|
||||
struct format_tree *ft;
|
||||
const char **line, **lines = NULL, *item = "item";
|
||||
u_int sx = screen_size_x(s), sy = screen_size_y(s);
|
||||
u_int x, y, w, h = 0, box_w, box_h;
|
||||
|
||||
if (mtd->helpcb == NULL)
|
||||
w = MODE_TREE_HELP_DEFAULT_WIDTH;
|
||||
else {
|
||||
lines = mtd->helpcb(&w, &item);
|
||||
if (w < MODE_TREE_HELP_DEFAULT_WIDTH)
|
||||
w = MODE_TREE_HELP_DEFAULT_WIDTH;
|
||||
}
|
||||
for (line = mode_tree_help_start; *line != NULL; line++)
|
||||
h++;
|
||||
for (line = lines; line != NULL && *line != NULL; line++)
|
||||
h++;
|
||||
for (line = mode_tree_help_end; *line != NULL; line++)
|
||||
h++;
|
||||
|
||||
box_w = w + 2;
|
||||
box_h = h + 2;
|
||||
if (sx < box_w || sy < box_h)
|
||||
return;
|
||||
x = (sx - box_w) / 2;
|
||||
y = (sy - box_h) / 2;
|
||||
|
||||
memcpy(&box_gc, &grid_default_cell, sizeof box_gc);
|
||||
style_apply(&box_gc, oo, "tree-mode-border-style", NULL);
|
||||
memcpy(&gc, &grid_default_cell, sizeof gc);
|
||||
ft = format_create_defaults(NULL, NULL, NULL, NULL, mtd->wp);
|
||||
screen_write_cursormove(ctx, x, y, 0);
|
||||
screen_write_box(ctx, box_w, box_h, BOX_LINES_DEFAULT, &box_gc, NULL);
|
||||
|
||||
y++;
|
||||
x++;
|
||||
for (line = mode_tree_help_start; *line != NULL; line++, y++)
|
||||
mode_tree_draw_help_line(ctx, &gc, ft, *line, item, x, y, w);
|
||||
for (line = lines; line != NULL && *line != NULL; line++, y++)
|
||||
mode_tree_draw_help_line(ctx, &gc, ft, *line, item, x, y, w);
|
||||
for (line = mode_tree_help_end; *line != NULL; line++, y++)
|
||||
mode_tree_draw_help_line(ctx, &gc, ft, *line, item, x, y, w);
|
||||
format_free(ft);
|
||||
}
|
||||
|
||||
static void
|
||||
mode_tree_display_help(struct mode_tree_data *mtd)
|
||||
{
|
||||
mtd->help = 1;
|
||||
mode_tree_draw(mtd);
|
||||
}
|
||||
|
||||
int
|
||||
mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
|
||||
struct mouse_event *m, u_int *xp, u_int *yp)
|
||||
{
|
||||
struct mode_tree_line *line;
|
||||
struct mode_tree_item *current, *parent, *mti;
|
||||
u_int i, x, y;
|
||||
int choice;
|
||||
u_int i, x, y, py, sx;
|
||||
int choice, preview;
|
||||
enum prompt_key_result result;
|
||||
int redraw;
|
||||
struct prompt *prompt;
|
||||
struct mode_tree_prompt *mtp;
|
||||
|
||||
if (mtd->line_size == 0) {
|
||||
*key = KEYC_NONE;
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (mtd->prompt != NULL) {
|
||||
redraw = 0;
|
||||
prompt = mtd->prompt;
|
||||
|
||||
mtp = mtd->prompt_data;
|
||||
if (mtp != NULL)
|
||||
mtp->c = c;
|
||||
if (KEYC_IS_MOUSE(*key)) {
|
||||
if (m == NULL ||
|
||||
MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1 ||
|
||||
MOUSE_DRAG(m->b) || MOUSE_RELEASE(m->b) ||
|
||||
cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0)
|
||||
result = PROMPT_KEY_NOT_HANDLED;
|
||||
else {
|
||||
sx = screen_size_x(&mtd->screen);
|
||||
if (mtd->prompt_top)
|
||||
py = 0;
|
||||
else
|
||||
py = screen_size_y(&mtd->screen) - 1;
|
||||
if (y == py) {
|
||||
result = prompt_mouse(prompt, x, 0, sx,
|
||||
&redraw);
|
||||
} else
|
||||
result = PROMPT_KEY_NOT_HANDLED;
|
||||
}
|
||||
} else
|
||||
result = prompt_key(prompt, *key, &redraw);
|
||||
if (mtd->prompt_data == mtp && mtp != NULL)
|
||||
mtp->c = NULL;
|
||||
|
||||
/*
|
||||
* Only an explicit close or the prompt marking itself closed
|
||||
* ends it; cursor movement and editing keep it open.
|
||||
*/
|
||||
if (mtd->prompt == prompt &&
|
||||
(result == PROMPT_KEY_CLOSE || prompt_closed(prompt)))
|
||||
mode_tree_clear_prompt(mtd);
|
||||
|
||||
if (redraw || mtd->prompt != prompt) {
|
||||
mode_tree_draw(mtd);
|
||||
mtd->wp->flags |= PANE_REDRAW;
|
||||
}
|
||||
if (result != PROMPT_KEY_NOT_HANDLED) {
|
||||
*key = KEYC_NONE;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
if (mtd->help) {
|
||||
if (KEYC_IS_MOUSE(*key)) {
|
||||
*key = KEYC_NONE;
|
||||
return (0);
|
||||
}
|
||||
if (*key == KEYC_FOCUS_IN || *key == KEYC_FOCUS_OUT) {
|
||||
*key = KEYC_NONE;
|
||||
return (0);
|
||||
}
|
||||
mtd->help = 0;
|
||||
mode_tree_draw(mtd);
|
||||
*key = KEYC_NONE;
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (KEYC_IS_MOUSE(*key) && m != NULL) {
|
||||
if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) {
|
||||
@@ -1142,9 +1581,10 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
|
||||
if (yp != NULL)
|
||||
*yp = y;
|
||||
if (x > mtd->width || y > mtd->height) {
|
||||
preview = mtd->preview;
|
||||
if (*key == KEYC_MOUSEDOWN3_PANE)
|
||||
mode_tree_display_menu(mtd, c, x, y, 1);
|
||||
if (mtd->preview == MODE_TREE_PREVIEW_OFF)
|
||||
if (preview == MODE_TREE_PREVIEW_OFF)
|
||||
*key = KEYC_NONE;
|
||||
return (0);
|
||||
}
|
||||
@@ -1191,8 +1631,13 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
|
||||
switch (*key) {
|
||||
case 'q':
|
||||
case '\033': /* Escape */
|
||||
case '['|KEYC_CTRL:
|
||||
case 'g'|KEYC_CTRL:
|
||||
return (1);
|
||||
case KEYC_F1:
|
||||
case 'h'|KEYC_CTRL:
|
||||
mode_tree_display_help(mtd);
|
||||
break;
|
||||
case KEYC_UP:
|
||||
case 'k':
|
||||
case KEYC_WHEELUP_PANE:
|
||||
@@ -1321,11 +1766,10 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
|
||||
case '?':
|
||||
case '/':
|
||||
case 's'|KEYC_CTRL:
|
||||
mtd->references++;
|
||||
mtd->search_dir = MODE_TREE_SEARCH_FORWARD;
|
||||
status_prompt_set(c, NULL, "(search) ", "",
|
||||
mode_tree_search_callback, mode_tree_search_free, mtd,
|
||||
PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH);
|
||||
mode_tree_set_prompt(mtd, c, "(search) ", "",
|
||||
PROMPT_TYPE_SEARCH, PROMPT_NOFORMAT,
|
||||
mode_tree_search_callback, NULL, mtd);
|
||||
break;
|
||||
case 'n':
|
||||
mtd->search_dir = MODE_TREE_SEARCH_FORWARD;
|
||||
@@ -1336,10 +1780,13 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
|
||||
mode_tree_search_set(mtd);
|
||||
break;
|
||||
case 'f':
|
||||
mtd->references++;
|
||||
status_prompt_set(c, NULL, "(filter) ", mtd->filter,
|
||||
mode_tree_filter_callback, mode_tree_filter_free, mtd,
|
||||
PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH);
|
||||
mode_tree_set_prompt(mtd, c, "(filter) ", mtd->filter,
|
||||
PROMPT_TYPE_SEARCH, PROMPT_NOFORMAT,
|
||||
mode_tree_filter_callback, NULL, mtd);
|
||||
break;
|
||||
case 'c':
|
||||
mode_tree_clear_prompt(mtd);
|
||||
mode_tree_clear_filter(mtd);
|
||||
break;
|
||||
case 'v':
|
||||
switch (mtd->preview) {
|
||||
|
||||
6
names.c
6
names.c
@@ -95,7 +95,7 @@ check_window_name(struct window *w)
|
||||
name = format_window_name(w);
|
||||
if (strcmp(name, w->name) != 0) {
|
||||
log_debug("@%u new name %s (was %s)", w->id, name, w->name);
|
||||
window_set_name(w, name);
|
||||
window_set_name(w, name, 1);
|
||||
server_redraw_window_borders(w);
|
||||
server_status_window(w);
|
||||
} else
|
||||
@@ -166,7 +166,9 @@ parse_window_name(const char *in)
|
||||
|
||||
if (*name == '/')
|
||||
name = basename(name);
|
||||
name = xstrdup(name);
|
||||
name = clean_name(name, 0);
|
||||
free(copy);
|
||||
if (name == NULL)
|
||||
return (xstrdup(""));
|
||||
return (name);
|
||||
}
|
||||
|
||||
6
notify.c
6
notify.c
@@ -208,8 +208,12 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
|
||||
format_add(ne->formats, "hook_window", "@%u", w->id);
|
||||
format_add(ne->formats, "hook_window_name", "%s", w->name);
|
||||
}
|
||||
if (wp != NULL)
|
||||
if (wp != NULL) {
|
||||
format_add(ne->formats, "hook_pane", "%%%d", wp->id);
|
||||
format_add(ne->formats, "hook_window", "@%u", wp->window->id);
|
||||
format_add(ne->formats, "hook_window_name", "%s",
|
||||
wp->window->name);
|
||||
}
|
||||
format_log_debug(ne->formats, __func__);
|
||||
|
||||
if (c != NULL)
|
||||
|
||||
454
options-table.c
454
options-table.c
@@ -64,19 +64,19 @@ static const char *options_table_cursor_style_list[] = {
|
||||
"blinking-bar", "bar", NULL
|
||||
};
|
||||
static const char *options_table_pane_scrollbars_list[] = {
|
||||
"off", "modal", "on", NULL
|
||||
"off", "modal", "on", "auto-hide", 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
|
||||
"off", "top", "bottom", "top-floating", "bottom-floating", NULL
|
||||
};
|
||||
static const char *options_table_pane_border_indicators_list[] = {
|
||||
"off", "colour", "arrows", "both", NULL
|
||||
};
|
||||
static const char *options_table_pane_border_lines_list[] = {
|
||||
"single", "double", "heavy", "simple", "number", "spaces", NULL
|
||||
"single", "double", "heavy", "simple", "number", "spaces", "none", NULL
|
||||
};
|
||||
static const char *options_table_popup_border_lines_list[] = {
|
||||
"single", "double", "heavy", "simple", "rounded", "padded", "none", NULL
|
||||
@@ -91,7 +91,7 @@ static const char *options_table_window_size_list[] = {
|
||||
"largest", "smallest", "manual", "latest", NULL
|
||||
};
|
||||
static const char *options_table_remain_on_exit_list[] = {
|
||||
"off", "on", "failed", NULL
|
||||
"off", "on", "failed", "key", NULL
|
||||
};
|
||||
static const char *options_table_destroy_unattached_list[] = {
|
||||
"off", "on", "keep-last", "keep-group", NULL
|
||||
@@ -108,6 +108,12 @@ static const char *options_table_extended_keys_format_list[] = {
|
||||
static const char *options_table_allow_passthrough_list[] = {
|
||||
"off", "on", "all", NULL
|
||||
};
|
||||
static const char *options_table_theme_list[] = {
|
||||
"detect", "terminal", "light", "dark", NULL
|
||||
};
|
||||
static const char *options_table_copy_mode_line_numbers_list[] = {
|
||||
"off", "default", "absolute", "relative", "hybrid", NULL
|
||||
};
|
||||
|
||||
/* Status line format. */
|
||||
#define OPTIONS_TABLE_STATUS_FORMAT1 \
|
||||
@@ -141,7 +147,7 @@ static const char *options_table_allow_passthrough_list[] = {
|
||||
"#{T:window-status-format}" \
|
||||
"#[pop-default]" \
|
||||
"#[norange default]" \
|
||||
"#{?loop_last_flag,,#{window-status-separator}}" \
|
||||
"#{?loop_last_flag,,#{E:window-status-separator}}" \
|
||||
"," \
|
||||
"#[range=window|#{window_index} list=focus " \
|
||||
"#{?#{!=:#{E:window-status-current-style},default}," \
|
||||
@@ -168,7 +174,7 @@ static const char *options_table_allow_passthrough_list[] = {
|
||||
"#{T:window-status-current-format}" \
|
||||
"#[pop-default]" \
|
||||
"#[norange list=on default]" \
|
||||
"#{?loop_last_flag,,#{window-status-separator}}" \
|
||||
"#{?loop_last_flag,,#{E:window-status-separator}}" \
|
||||
"}" \
|
||||
"#[nolist align=right range=right #{E:status-right-style}]" \
|
||||
"#[push-default]" \
|
||||
@@ -185,7 +191,7 @@ static const char *options_table_allow_passthrough_list[] = {
|
||||
"#{E:pane-status-style}" \
|
||||
"]" \
|
||||
"#[push-default]" \
|
||||
"#P[#{pane_width}x#{pane_height}]" \
|
||||
"#{T:window-pane-status-format}" \
|
||||
"#[pop-default]" \
|
||||
"#[norange list=on default] " \
|
||||
"," \
|
||||
@@ -196,7 +202,7 @@ static const char *options_table_allow_passthrough_list[] = {
|
||||
"}" \
|
||||
"]" \
|
||||
"#[push-default]" \
|
||||
"#P[#{pane_width}x#{pane_height}]*" \
|
||||
"#{T:window-pane-current-status-format}" \
|
||||
"#[pop-default]" \
|
||||
"#[norange list=on default] " \
|
||||
"}"
|
||||
@@ -267,6 +273,7 @@ const struct options_name_map options_other_names[] = {
|
||||
{ "clock-mode-color", "clock-mode-colour" },
|
||||
{ "cursor-color", "cursor-colour" },
|
||||
{ "prompt-cursor-color", "prompt-cursor-colour" },
|
||||
{ "prompt-command-cursor-color", "prompt-command-cursor-colour" },
|
||||
{ "pane-colors", "pane-colours" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
@@ -324,9 +331,10 @@ const struct options_table_entry options_table[] = {
|
||||
},
|
||||
|
||||
{ .name = "cursor-colour",
|
||||
.type = OPTIONS_TABLE_COLOUR,
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.default_num = -1,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "",
|
||||
.text = "Colour of the cursor."
|
||||
},
|
||||
|
||||
@@ -414,10 +422,10 @@ const struct options_table_entry options_table[] = {
|
||||
.choices = options_table_get_clipboard_list,
|
||||
.default_num = 1,
|
||||
.text = "When an application requests the clipboard, whether to "
|
||||
"ignore the request ('off'); respond with the newest buffer "
|
||||
"('buffer'); request the clipboard from the most recently "
|
||||
"used terminal ('request'); or to request the clipboard, "
|
||||
"create a buffer, and send it to the application ('both')."
|
||||
"ignore the request ('off'); respond with the newest buffer "
|
||||
"('buffer'); request the clipboard from the most recently "
|
||||
"used terminal ('request'); or to request the clipboard, "
|
||||
"create a buffer, and send it to the application ('both')."
|
||||
},
|
||||
|
||||
{ .name = "history-file",
|
||||
@@ -441,7 +449,7 @@ const struct options_table_entry options_table[] = {
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.default_str = "default",
|
||||
.default_str = "bg=themedarkgrey,fg=themewhite",
|
||||
.separator = ",",
|
||||
.text = "Default style of menu."
|
||||
},
|
||||
@@ -450,7 +458,7 @@ const struct options_table_entry options_table[] = {
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.default_str = "bg=yellow,fg=black",
|
||||
.default_str = "bg=themeyellow,fg=themeblack",
|
||||
.separator = ",",
|
||||
.text = "Default style of selected menu item."
|
||||
},
|
||||
@@ -458,7 +466,7 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "menu-border-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "default",
|
||||
.default_str = "bg=themedarkgrey,fg=themelightgrey",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Default style of menu borders."
|
||||
@@ -533,6 +541,175 @@ const struct options_table_entry options_table[] = {
|
||||
"automatically detected."
|
||||
},
|
||||
|
||||
{ .name = "theme",
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.choices = options_table_theme_list,
|
||||
.default_num = 0,
|
||||
.text = "Whether tmux should detect the terminal theme, use terminal "
|
||||
"ANSI colours, or force the light or dark theme."
|
||||
},
|
||||
|
||||
{ .name = "dark-theme-black",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},gray5,black}",
|
||||
.text = "Dark theme colour for black."
|
||||
},
|
||||
|
||||
{ .name = "dark-theme-white",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},gray90,white}",
|
||||
.text = "Dark theme colour for white."
|
||||
},
|
||||
|
||||
{ .name = "dark-theme-light-grey",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},gray70,white}",
|
||||
.text = "Dark theme colour for light grey."
|
||||
},
|
||||
|
||||
{ .name = "dark-theme-dark-grey",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},gray15,black}",
|
||||
.text = "Dark theme colour for dark grey."
|
||||
},
|
||||
|
||||
{ .name = "dark-theme-green",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},yellowgreen,green}",
|
||||
.text = "Dark theme colour for green."
|
||||
},
|
||||
|
||||
{ .name = "dark-theme-yellow",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},darkgoldenrod,yellow}",
|
||||
.text = "Dark theme colour for yellow."
|
||||
},
|
||||
|
||||
{ .name = "dark-theme-red",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},indianred,red}",
|
||||
.text = "Dark theme colour for red."
|
||||
},
|
||||
|
||||
{ .name = "dark-theme-blue",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},skyblue3,blue}",
|
||||
.text = "Dark theme colour for blue."
|
||||
},
|
||||
|
||||
{ .name = "dark-theme-cyan",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},cadetblue,cyan}",
|
||||
.text = "Dark theme colour for cyan."
|
||||
},
|
||||
|
||||
{ .name = "dark-theme-magenta",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},mediumpurple,magenta}",
|
||||
.text = "Dark theme colour for magenta."
|
||||
},
|
||||
|
||||
{ .name = "light-theme-black",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},gray10,black}",
|
||||
.text = "Light theme colour for black."
|
||||
},
|
||||
|
||||
{ .name = "light-theme-white",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},gray95,white}",
|
||||
.text = "Light theme colour for white."
|
||||
},
|
||||
|
||||
{ .name = "light-theme-light-grey",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},gray80,white}",
|
||||
.text = "Light theme colour for light grey."
|
||||
},
|
||||
|
||||
{ .name = "light-theme-dark-grey",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},gray45,black}",
|
||||
.text = "Light theme colour for dark grey."
|
||||
},
|
||||
|
||||
{ .name = "light-theme-green",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},seagreen,green}",
|
||||
.text = "Light theme colour for green."
|
||||
},
|
||||
|
||||
{ .name = "light-theme-yellow",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},darkgoldenrod,yellow}",
|
||||
.text = "Light theme colour for yellow."
|
||||
},
|
||||
|
||||
{ .name = "light-theme-red",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},indianred4,red}",
|
||||
.text = "Light theme colour for red."
|
||||
},
|
||||
|
||||
{ .name = "light-theme-blue",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},steelblue,blue}",
|
||||
.text = "Light theme colour for blue."
|
||||
},
|
||||
|
||||
{ .name = "light-theme-cyan",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},darkcyan,cyan}",
|
||||
.text = "Light theme colour for cyan."
|
||||
},
|
||||
|
||||
{ .name = "light-theme-magenta",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "#{?#{e|>=:#{client_colours},256},purple4,magenta}",
|
||||
.text = "Light theme colour for magenta."
|
||||
},
|
||||
|
||||
{ .name = "user-keys",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
@@ -631,19 +808,29 @@ const struct options_table_entry options_table[] = {
|
||||
},
|
||||
|
||||
{ .name = "display-panes-active-colour",
|
||||
.type = OPTIONS_TABLE_COLOUR,
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.default_num = 1,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "themered",
|
||||
.text = "Colour of the active pane for 'display-panes'."
|
||||
},
|
||||
|
||||
{ .name = "display-panes-colour",
|
||||
.type = OPTIONS_TABLE_COLOUR,
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.default_num = 4,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "themeblue",
|
||||
.text = "Colour of not active panes for 'display-panes'."
|
||||
},
|
||||
|
||||
{ .name = "display-panes-format",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.default_str = "#[align=right]#{pane_width}x#{pane_height}",
|
||||
.text = "Format of text shown by 'display-panes', expanded for each "
|
||||
"pane."
|
||||
},
|
||||
|
||||
{ .name = "display-panes-time",
|
||||
.type = OPTIONS_TABLE_NUMBER,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
@@ -723,11 +910,24 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "message-command-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.default_str = "bg=black,fg=yellow",
|
||||
.default_str = "bg=themeblack,fg=themeyellow,"
|
||||
"#{?#{m/r:(^|#,)IS(PANE|MODE)($|#,),#{prompt_flags}},,"
|
||||
"fill=themeblack}",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of the command prompt when in command mode, if "
|
||||
"'mode-keys' is set to 'vi'."
|
||||
"'status-keys' is set to 'vi'."
|
||||
},
|
||||
|
||||
{ .name = "message-format",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.default_str = "#[#{?#{command_prompt},"
|
||||
"#{E:message-command-style},"
|
||||
"#{E:message-style}}]"
|
||||
"#{message}",
|
||||
.text = "Format string for the prompt and message area. "
|
||||
"The '#{message}' placeholder is replaced with the content."
|
||||
},
|
||||
|
||||
{ .name = "message-line",
|
||||
@@ -741,10 +941,15 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "message-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.default_str = "bg=yellow,fg=black",
|
||||
.default_str = "bg=themeyellow,fg=themeblack,"
|
||||
"#{?#{m/r:(^|#,)IS(PANE|MODE)($|#,),#{prompt_flags}},,"
|
||||
"fill=themeyellow}",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of messages and the command prompt."
|
||||
.text = "Style of messages and the command prompt. "
|
||||
"A 'fill' attribute controls background clearing and "
|
||||
"a 'width' attribute (fixed or percentage) constrains "
|
||||
"the prompt area width."
|
||||
},
|
||||
|
||||
{ .name = "mouse",
|
||||
@@ -937,7 +1142,7 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "status-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.default_str = "bg=green,fg=black",
|
||||
.default_str = "bg=themegreen,fg=themeblack",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of the status line."
|
||||
@@ -946,7 +1151,7 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "pane-status-current-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "default",
|
||||
.default_str = "underscore",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of the current pane in the status line."
|
||||
@@ -963,12 +1168,22 @@ const struct options_table_entry options_table[] = {
|
||||
},
|
||||
|
||||
{ .name = "prompt-cursor-colour",
|
||||
.type = OPTIONS_TABLE_COLOUR,
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.default_num = -1,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "",
|
||||
.text = "Colour of the cursor when in the command prompt."
|
||||
},
|
||||
|
||||
{ .name = "prompt-command-cursor-colour",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "",
|
||||
.text = "Colour of the cursor in the command prompt when in command "
|
||||
"mode, if 'status-keys' is set to 'vi'."
|
||||
},
|
||||
|
||||
{ .name = "prompt-cursor-style",
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
@@ -989,7 +1204,7 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "session-status-current-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "default",
|
||||
.default_str = "underscore",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of the current session in the status line."
|
||||
@@ -1010,7 +1225,9 @@ const struct options_table_entry options_table[] = {
|
||||
.scope = OPTIONS_TABLE_SESSION,
|
||||
.flags = OPTIONS_TABLE_IS_ARRAY,
|
||||
.default_str = "DISPLAY KRB5CCNAME MSYSTEM SSH_ASKPASS SSH_AUTH_SOCK "
|
||||
"SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY",
|
||||
"SSH_AGENT_PID SSH_CONNECTION WAYLAND_DISPLAY "
|
||||
"WINDOWID XAUTHORITY XDG_CURRENT_DESKTOP "
|
||||
"XDG_SESSION_DESKTOP XDG_SESSION_TYPE",
|
||||
.text = "List of environment variables to update in the session "
|
||||
"environment when a client is attached."
|
||||
},
|
||||
@@ -1115,9 +1332,10 @@ const struct options_table_entry options_table[] = {
|
||||
},
|
||||
|
||||
{ .name = "clock-mode-colour",
|
||||
.type = OPTIONS_TABLE_COLOUR,
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_num = 4,
|
||||
.flags = OPTIONS_TABLE_IS_COLOUR,
|
||||
.default_str = "themeblue",
|
||||
.text = "Colour of the clock in clock mode."
|
||||
},
|
||||
|
||||
@@ -1132,7 +1350,7 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "copy-mode-match-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "bg=cyan,fg=black",
|
||||
.default_str = "bg=themecyan,fg=themeblack",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of search matches in copy mode."
|
||||
@@ -1141,7 +1359,7 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "copy-mode-current-match-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "bg=magenta,fg=black",
|
||||
.default_str = "bg=thememagenta,fg=themeblack",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of the current search match in copy mode."
|
||||
@@ -1150,7 +1368,7 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "copy-mode-mark-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "bg=red,fg=black",
|
||||
.default_str = "bg=themeyellow,fg=themeblack",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of the marked line in copy mode."
|
||||
@@ -1161,7 +1379,7 @@ const struct options_table_entry options_table[] = {
|
||||
.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}]"
|
||||
"[#{copy_position}/#{copy_position_limit}]"
|
||||
"#{?search_timed_out, (timed out),"
|
||||
"#{?search_count, (#{search_count}"
|
||||
"#{?search_count_partial,+,} results),}}",
|
||||
@@ -1186,6 +1404,32 @@ const struct options_table_entry options_table[] = {
|
||||
.text = "Style of selection in copy mode."
|
||||
},
|
||||
|
||||
{ .name = "copy-mode-current-line-number-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "fg=themeyellow",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of current line number in copy mode."
|
||||
},
|
||||
|
||||
{ .name = "copy-mode-line-number-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "fg=themelightgrey,dim",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of line numbers in copy mode."
|
||||
},
|
||||
|
||||
{ .name = "copy-mode-line-numbers",
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.choices = options_table_copy_mode_line_numbers_list,
|
||||
.default_num = 0,
|
||||
.text = "Line number mode in copy mode."
|
||||
},
|
||||
|
||||
{ .name = "fill-character",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
@@ -1193,6 +1437,28 @@ const struct options_table_entry options_table[] = {
|
||||
.text = "Character used to fill unused parts of window."
|
||||
},
|
||||
|
||||
{ .name = "floating-pane-border-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.default_str = "default",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Default border style for floating panes. "
|
||||
"Overrides pane-border-style for floating panes unless "
|
||||
"a per-pane style is set."
|
||||
},
|
||||
|
||||
{ .name = "floating-pane-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.default_str = "default",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Default content style for floating panes. "
|
||||
"Overrides window-style for floating panes unless "
|
||||
"a per-pane style is set."
|
||||
},
|
||||
|
||||
{ .name = "main-pane-height",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
@@ -1221,7 +1487,7 @@ const struct options_table_entry options_table[] = {
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.default_str = "noattr,bg=yellow,fg=black",
|
||||
.default_str = "noattr,bg=themeyellow,fg=themeblack",
|
||||
.separator = ",",
|
||||
.text = "Style of indicators and highlighting in modes."
|
||||
},
|
||||
@@ -1269,8 +1535,10 @@ const struct options_table_entry options_table[] = {
|
||||
|
||||
{ .name = "pane-active-border-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "#{?pane_in_mode,fg=yellow,#{?synchronize-panes,fg=red,fg=green}}",
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.default_str = "fg=#{?pane_marked,thememagenta,"
|
||||
"#{?synchronize-panes,themered,"
|
||||
"#{?pane_in_mode,themeyellow,themegreen}}}",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of the active pane border."
|
||||
@@ -1289,7 +1557,14 @@ const struct options_table_entry options_table[] = {
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.default_str = "#{?pane_active,#[reverse],}#{pane_index}#[default] "
|
||||
"\"#{pane_title}\"",
|
||||
"\"#{pane_title}\""
|
||||
"#{?#{mouse},"
|
||||
"#[align=right]"
|
||||
"#[range=control|8]["
|
||||
"#{?#{window_zoomed_flag},u,z}"
|
||||
"]#[norange]"
|
||||
"#[range=control|9][x]#[norange]"
|
||||
",}",
|
||||
.text = "Format of text in the pane status lines."
|
||||
},
|
||||
|
||||
@@ -1304,7 +1579,7 @@ const struct options_table_entry options_table[] = {
|
||||
|
||||
{ .name = "pane-border-lines",
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.choices = options_table_pane_border_lines_list,
|
||||
.default_num = PANE_LINES_SINGLE,
|
||||
.text = "Type of characters used to draw pane border lines. Some of "
|
||||
@@ -1313,7 +1588,7 @@ const struct options_table_entry options_table[] = {
|
||||
|
||||
{ .name = "pane-border-status",
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.choices = options_table_pane_status_list,
|
||||
.default_num = PANE_STATUS_OFF,
|
||||
.text = "Position of the pane status lines."
|
||||
@@ -1321,8 +1596,8 @@ const struct options_table_entry options_table[] = {
|
||||
|
||||
{ .name = "pane-border-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "default",
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.default_str = "fg=themelightgrey",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of the pane status lines."
|
||||
@@ -1341,13 +1616,23 @@ const struct options_table_entry options_table[] = {
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.choices = options_table_pane_scrollbars_list,
|
||||
.default_num = PANE_SCROLLBARS_OFF,
|
||||
.text = "Pane scrollbar state."
|
||||
.text = "Pane scrollbar state: off, on, modal, or auto-hide."
|
||||
},
|
||||
|
||||
{ .name = "pane-scrollbars-timeout",
|
||||
.type = OPTIONS_TABLE_NUMBER,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.minimum = 0,
|
||||
.maximum = INT_MAX,
|
||||
.default_num = 500,
|
||||
.unit = "milliseconds",
|
||||
.text = "Time before modal and auto-hide pane scrollbars disappear."
|
||||
},
|
||||
|
||||
{ .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",
|
||||
.default_str = "bg=themedarkgrey,fg=themelightgrey,width=1,pad=0",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of the pane scrollbar."
|
||||
@@ -1364,7 +1649,7 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "popup-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "default",
|
||||
.default_str = "bg=themedarkgrey,fg=themewhite",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Default style of popups."
|
||||
@@ -1373,7 +1658,7 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "popup-border-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "default",
|
||||
.default_str = "bg=themedarkgrey,fg=themelightgrey",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Default style of popup borders."
|
||||
@@ -1393,8 +1678,9 @@ const struct options_table_entry options_table[] = {
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.choices = options_table_remain_on_exit_list,
|
||||
.default_num = 0,
|
||||
.text = "Whether panes should remain ('on') or be automatically "
|
||||
"killed ('off' or 'failed') when the program inside exits."
|
||||
.text = "Whether panes should remain ('on'), remain until a key is "
|
||||
"pressed ('key') or be automatically killed ('off' or "
|
||||
"'failed') when the program inside exits."
|
||||
},
|
||||
|
||||
{ .name = "remain-on-exit-format",
|
||||
@@ -1418,6 +1704,15 @@ const struct options_table_entry options_table[] = {
|
||||
"history when clearing the whole screen."
|
||||
},
|
||||
|
||||
{ .name = "switch-mode-match-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.default_str = "bg=cyan fg=black",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of matched characters in switch mode."
|
||||
},
|
||||
|
||||
{ .name = "synchronize-panes",
|
||||
.type = OPTIONS_TABLE_FLAG,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
@@ -1435,6 +1730,46 @@ const struct options_table_entry options_table[] = {
|
||||
"A value of 0 means no limit."
|
||||
},
|
||||
|
||||
{ .name = "tree-mode-border-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "bg=themedarkgrey,fg=themelightgrey",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of borders in tree mode."
|
||||
},
|
||||
|
||||
{ .name = "tree-mode-preview-format",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
.default_str = "#{?pane_format,"
|
||||
"#{pane_index}:#{pane_title},"
|
||||
"#{window_index}:#{window_name}}",
|
||||
.text = "Format of the preview indicator in tree mode."
|
||||
},
|
||||
|
||||
{ .name = "tree-mode-preview-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "fg=#{?#{||:"
|
||||
"#{&&:#{pane_format},#{pane_active}},"
|
||||
"#{&&:#{window_format},#{window_active}}},"
|
||||
"themered,"
|
||||
"themeblue}",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of preview indicator in tree mode."
|
||||
},
|
||||
|
||||
{ .name = "tree-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 the selected line in tree mode."
|
||||
},
|
||||
|
||||
{ .name = "window-active-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
|
||||
@@ -1444,6 +1779,21 @@ const struct options_table_entry options_table[] = {
|
||||
.text = "Default style of the active pane."
|
||||
},
|
||||
|
||||
{ .name = "window-pane-current-status-format",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "#P:[#T]#{?pane_flags,#{pane_flags}, }",
|
||||
.text = "Format of the current window pane in the status line."
|
||||
},
|
||||
|
||||
{ .name = "window-pane-status-format",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "#P:[#T]#{?pane_flags,#{pane_flags}, }",
|
||||
.text = "Format of window panes in the status line, except the "
|
||||
"current pane."
|
||||
},
|
||||
|
||||
{ .name = "window-size",
|
||||
.type = OPTIONS_TABLE_CHOICE,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
@@ -1493,7 +1843,7 @@ const struct options_table_entry options_table[] = {
|
||||
{ .name = "window-status-current-style",
|
||||
.type = OPTIONS_TABLE_STRING,
|
||||
.scope = OPTIONS_TABLE_WINDOW,
|
||||
.default_str = "default",
|
||||
.default_str = "underscore",
|
||||
.flags = OPTIONS_TABLE_IS_STYLE,
|
||||
.separator = ",",
|
||||
.text = "Style of the current window in the status line."
|
||||
|
||||
78
options.c
78
options.c
@@ -662,6 +662,18 @@ options_parse_get(struct options *oo, const char *s, int *idx, int only)
|
||||
return (o);
|
||||
}
|
||||
|
||||
const struct options_table_entry *
|
||||
options_search(const char *name)
|
||||
{
|
||||
const struct options_table_entry *oe;
|
||||
|
||||
for (oe = options_table; oe->name != NULL; oe++) {
|
||||
if (strcmp(oe->name, name) == 0)
|
||||
return (oe);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
char *
|
||||
options_match(const char *s, int *idx, int *ambiguous)
|
||||
{
|
||||
@@ -977,9 +989,12 @@ struct style *
|
||||
options_string_to_style(struct options *oo, const char *name,
|
||||
struct format_tree *ft)
|
||||
{
|
||||
struct options_entry *o;
|
||||
const char *s;
|
||||
char *expanded;
|
||||
struct options_entry *o;
|
||||
const struct options_table_entry *oe;
|
||||
const struct grid_cell *dgc = &grid_default_cell;
|
||||
const char *s;
|
||||
char *expanded;
|
||||
int failed;
|
||||
|
||||
o = options_get(oo, name);
|
||||
if (o == NULL || !OPTIONS_IS_STRING(o))
|
||||
@@ -988,20 +1003,27 @@ options_string_to_style(struct options *oo, const char *name,
|
||||
if (o->cached)
|
||||
return (&o->style);
|
||||
s = o->value.string;
|
||||
oe = o->tableentry;
|
||||
log_debug("%s: %s is '%s'", __func__, name, s);
|
||||
|
||||
style_set(&o->style, &grid_default_cell);
|
||||
style_set(&o->style, dgc);
|
||||
o->cached = (strstr(s, "#{") == NULL);
|
||||
|
||||
if (ft != NULL && !o->cached) {
|
||||
expanded = format_expand(ft, s);
|
||||
if (style_parse(&o->style, &grid_default_cell, expanded) != 0) {
|
||||
free(expanded);
|
||||
return (NULL);
|
||||
}
|
||||
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_COLOUR))
|
||||
failed = style_parse_colour(&o->style, dgc, expanded);
|
||||
else
|
||||
failed = style_parse(&o->style, dgc, expanded);
|
||||
free(expanded);
|
||||
if (failed != 0)
|
||||
return (NULL);
|
||||
} else {
|
||||
if (style_parse(&o->style, &grid_default_cell, s) != 0)
|
||||
if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_COLOUR))
|
||||
failed = style_parse_colour(&o->style, dgc, s);
|
||||
else
|
||||
failed = style_parse(&o->style, dgc, s);
|
||||
if (failed != 0)
|
||||
return (NULL);
|
||||
}
|
||||
return (&o->style);
|
||||
@@ -1029,6 +1051,12 @@ options_from_string_check(const struct options_table_entry *oe,
|
||||
xasprintf(cause, "invalid style: %s", value);
|
||||
return (-1);
|
||||
}
|
||||
if ((oe->flags & OPTIONS_TABLE_IS_COLOUR) &&
|
||||
strstr(value, "#{") == NULL &&
|
||||
style_parse_colour(&sy, &grid_default_cell, value) != 0) {
|
||||
xasprintf(cause, "invalid colour: %s", value);
|
||||
return (-1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
@@ -1186,6 +1214,17 @@ options_push_changes(const char *name)
|
||||
|
||||
log_debug("%s: %s", __func__, name);
|
||||
|
||||
if (strcmp(name, "theme") == 0 ||
|
||||
strncmp(name, "dark-theme-", 11) == 0 ||
|
||||
strncmp(name, "light-theme-", 12) == 0) {
|
||||
TAILQ_FOREACH(loop, &clients, entry) {
|
||||
server_client_update_theme_colours(loop);
|
||||
if (loop->tty.flags & TTY_OPENED)
|
||||
tty_invalidate(&loop->tty);
|
||||
server_redraw_client(loop);
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(name, "automatic-rename") == 0) {
|
||||
RB_FOREACH(w, windows, &windows) {
|
||||
if (w->active == NULL)
|
||||
@@ -1219,6 +1258,16 @@ options_push_changes(const char *name)
|
||||
if (strcmp(name, "status") == 0 ||
|
||||
strcmp(name, "status-interval") == 0)
|
||||
status_timer_start_all();
|
||||
if (strcmp(name, "status") == 0 ||
|
||||
strcmp(name, "status-position") == 0 ||
|
||||
strcmp(name, "pane-border-indicators") == 0 ||
|
||||
strcmp(name, "pane-border-lines") == 0 ||
|
||||
strcmp(name, "pane-border-status") == 0 ||
|
||||
strcmp(name, "pane-scrollbars") == 0 ||
|
||||
strcmp(name, "pane-scrollbars-timeout") == 0 ||
|
||||
strcmp(name, "pane-scrollbars-position") == 0 ||
|
||||
strcmp(name, "pane-scrollbars-style") == 0)
|
||||
redraw_invalidate_all_scenes();
|
||||
if (strcmp(name, "monitor-silence") == 0)
|
||||
alerts_reset_all();
|
||||
if (strcmp(name, "window-style") == 0 ||
|
||||
@@ -1237,8 +1286,17 @@ options_push_changes(const char *name)
|
||||
if (strcmp(name, "pane-border-status") == 0 ||
|
||||
strcmp(name, "pane-scrollbars") == 0 ||
|
||||
strcmp(name, "pane-scrollbars-position") == 0) {
|
||||
RB_FOREACH(w, windows, &windows)
|
||||
RB_FOREACH(w, windows, &windows) {
|
||||
w->sb = options_get_number(w->options,
|
||||
"pane-scrollbars");
|
||||
w->sb_pos = options_get_number(w->options,
|
||||
"pane-scrollbars-position");
|
||||
layout_fix_panes(w, NULL);
|
||||
}
|
||||
}
|
||||
if (strcmp(name, "pane-scrollbars") == 0) {
|
||||
RB_FOREACH(wp, window_pane_tree, &all_window_panes)
|
||||
window_pane_scrollbar_hide(wp);
|
||||
}
|
||||
if (strcmp(name, "pane-scrollbars-style") == 0) {
|
||||
RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
|
||||
|
||||
35
paste.c
35
paste.c
@@ -176,7 +176,6 @@ paste_add(const char *prefix, char *data, size_t size)
|
||||
}
|
||||
|
||||
pb = xmalloc(sizeof *pb);
|
||||
|
||||
pb->name = NULL;
|
||||
do {
|
||||
free(pb->name);
|
||||
@@ -204,6 +203,7 @@ int
|
||||
paste_rename(const char *oldname, const char *newname, char **cause)
|
||||
{
|
||||
struct paste_buffer *pb, *pb_new;
|
||||
char *name;
|
||||
|
||||
if (cause != NULL)
|
||||
*cause = NULL;
|
||||
@@ -219,23 +219,33 @@ paste_rename(const char *oldname, const char *newname, char **cause)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
name = clean_name(newname, 0);
|
||||
if (name == NULL) {
|
||||
if (cause != NULL)
|
||||
xasprintf(cause, "invalid buffer name: %s", newname);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
pb = paste_get_name(oldname);
|
||||
if (pb == NULL) {
|
||||
if (cause != NULL)
|
||||
xasprintf(cause, "no buffer %s", oldname);
|
||||
free(name);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
pb_new = paste_get_name(newname);
|
||||
if (pb_new == pb)
|
||||
pb_new = paste_get_name(name);
|
||||
if (pb_new == pb) {
|
||||
free(name);
|
||||
return (0);
|
||||
}
|
||||
if (pb_new != NULL)
|
||||
paste_free(pb_new);
|
||||
|
||||
RB_REMOVE(paste_name_tree, &paste_by_name, pb);
|
||||
|
||||
free(pb->name);
|
||||
pb->name = xstrdup(newname);
|
||||
pb->name = name;
|
||||
|
||||
if (pb->automatic)
|
||||
paste_num_automatic--;
|
||||
@@ -244,7 +254,7 @@ 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);
|
||||
notify_paste_buffer(pb->name, 0);
|
||||
|
||||
return (0);
|
||||
}
|
||||
@@ -257,6 +267,7 @@ int
|
||||
paste_set(char *data, size_t size, const char *name, char **cause)
|
||||
{
|
||||
struct paste_buffer *pb, *old;
|
||||
char *newname;
|
||||
|
||||
if (cause != NULL)
|
||||
*cause = NULL;
|
||||
@@ -276,9 +287,15 @@ paste_set(char *data, size_t size, const char *name, char **cause)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
pb = xmalloc(sizeof *pb);
|
||||
newname = clean_name(name, 0);
|
||||
if (newname == NULL) {
|
||||
if (cause != NULL)
|
||||
xasprintf(cause, "invalid buffer name: %s", name);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
pb->name = xstrdup(name);
|
||||
pb = xmalloc(sizeof *pb);
|
||||
pb->name = newname;
|
||||
|
||||
pb->data = data;
|
||||
pb->size = size;
|
||||
@@ -288,13 +305,13 @@ paste_set(char *data, size_t size, const char *name, char **cause)
|
||||
|
||||
pb->created = time(NULL);
|
||||
|
||||
if ((old = paste_get_name(name)) != NULL)
|
||||
if ((old = paste_get_name(pb->name)) != NULL)
|
||||
paste_free(old);
|
||||
|
||||
RB_INSERT(paste_name_tree, &paste_by_name, pb);
|
||||
RB_INSERT(paste_time_tree, &paste_by_time, pb);
|
||||
|
||||
notify_paste_buffer(name, 0);
|
||||
notify_paste_buffer(pb->name, 0);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
181
popup.c
181
popup.c
@@ -75,12 +75,6 @@ struct popup_data {
|
||||
u_int lb;
|
||||
};
|
||||
|
||||
struct popup_editor {
|
||||
char *path;
|
||||
popup_finish_edit_cb cb;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
static const struct menu_item popup_menu_items[] = {
|
||||
{ "Close", 'q', NULL },
|
||||
{ "#{?buffer_name,Paste #[underscore]#{buffer_name},}", 'p', NULL },
|
||||
@@ -94,14 +88,26 @@ static const struct menu_item popup_menu_items[] = {
|
||||
{ NULL, KEYC_NONE, NULL }
|
||||
};
|
||||
|
||||
static const struct menu_item popup_internal_menu_items[] = {
|
||||
{ "Close", 'q', NULL },
|
||||
{ "", KEYC_NONE, NULL },
|
||||
{ "Fill Space", 'F', NULL },
|
||||
{ "Centre", 'C', NULL },
|
||||
static void
|
||||
popup_free(struct popup_data *pd)
|
||||
{
|
||||
server_client_unref(pd->c);
|
||||
|
||||
{ NULL, KEYC_NONE, NULL }
|
||||
};
|
||||
if (pd->job != NULL)
|
||||
job_free(pd->job);
|
||||
input_free(pd->ictx);
|
||||
|
||||
free(pd->or[0].ranges);
|
||||
free(pd->or[1].ranges);
|
||||
free(pd->r.ranges);
|
||||
screen_free(&pd->s);
|
||||
colour_palette_free(&pd->palette);
|
||||
|
||||
free(pd->title);
|
||||
free(pd->style);
|
||||
free(pd->border_style);
|
||||
free(pd);
|
||||
}
|
||||
|
||||
static void
|
||||
popup_reapply_styles(struct popup_data *pd)
|
||||
@@ -164,7 +170,6 @@ popup_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
||||
if (pd->c->flags & CLIENT_REDRAWOVERLAY)
|
||||
return (0);
|
||||
|
||||
ttyctx->bigger = 0;
|
||||
ttyctx->wox = 0;
|
||||
ttyctx->woy = 0;
|
||||
ttyctx->wsx = c->tty.sx;
|
||||
@@ -187,7 +192,9 @@ popup_init_ctx_cb(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx)
|
||||
struct popup_data *pd = ctx->arg;
|
||||
|
||||
memcpy(&ttyctx->defaults, &pd->defaults, sizeof ttyctx->defaults);
|
||||
ttyctx->palette = &pd->palette;
|
||||
ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER;
|
||||
ttyctx->style_ctx.defaults = &ttyctx->defaults;
|
||||
ttyctx->style_ctx.palette = &pd->palette;
|
||||
ttyctx->redraw_cb = popup_redraw_cb;
|
||||
ttyctx->set_client_cb = popup_set_client_cb;
|
||||
ttyctx->arg = pd;
|
||||
@@ -266,19 +273,23 @@ popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx)
|
||||
}
|
||||
|
||||
static void
|
||||
popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
|
||||
popup_draw_cb(struct client *c, void *data)
|
||||
{
|
||||
struct popup_data *pd = data;
|
||||
struct tty *tty = &c->tty;
|
||||
struct screen s;
|
||||
struct screen_write_ctx ctx;
|
||||
u_int i, px = pd->px, py = pd->py;
|
||||
struct colour_palette *palette = &pd->palette;
|
||||
struct grid_cell defaults;
|
||||
struct tty_style_ctx style_ctx;
|
||||
|
||||
popup_reapply_styles(pd);
|
||||
|
||||
screen_init(&s, pd->sx, pd->sy, 0);
|
||||
if (pd->s.hyperlinks != NULL) {
|
||||
hyperlinks_free(s.hyperlinks);
|
||||
s.hyperlinks = hyperlinks_copy(pd->s.hyperlinks);
|
||||
}
|
||||
screen_write_start(&ctx, &s);
|
||||
screen_write_clearscreen(&ctx, 8);
|
||||
|
||||
@@ -296,9 +307,13 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
|
||||
|
||||
memcpy(&defaults, &pd->defaults, sizeof defaults);
|
||||
if (defaults.fg == 8)
|
||||
defaults.fg = palette->fg;
|
||||
defaults.fg = pd->palette.fg;
|
||||
if (defaults.bg == 8)
|
||||
defaults.bg = palette->bg;
|
||||
defaults.bg = pd->palette.bg;
|
||||
style_ctx.defaults = &defaults;
|
||||
style_ctx.palette = &pd->palette;
|
||||
style_ctx.dim = 0;
|
||||
style_ctx.hyperlinks = s.hyperlinks;
|
||||
|
||||
if (pd->md != NULL) {
|
||||
c->overlay_check = menu_check_cb;
|
||||
@@ -307,15 +322,13 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
|
||||
c->overlay_check = NULL;
|
||||
c->overlay_data = NULL;
|
||||
}
|
||||
for (i = 0; i < pd->sy; i++) {
|
||||
tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &defaults,
|
||||
palette);
|
||||
}
|
||||
for (i = 0; i < pd->sy; i++)
|
||||
tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &style_ctx);
|
||||
screen_free(&s);
|
||||
if (pd->md != NULL) {
|
||||
c->overlay_check = NULL;
|
||||
c->overlay_data = NULL;
|
||||
menu_draw_cb(c, pd->md, rctx);
|
||||
menu_draw_cb(c, pd->md);
|
||||
}
|
||||
c->overlay_check = popup_check_cb;
|
||||
c->overlay_data = pd;
|
||||
@@ -339,22 +352,8 @@ popup_free_cb(struct client *c, void *data)
|
||||
cmdq_get_client(item)->retval = pd->status;
|
||||
cmdq_continue(item);
|
||||
}
|
||||
server_client_unref(pd->c);
|
||||
|
||||
if (pd->job != NULL)
|
||||
job_free(pd->job);
|
||||
input_free(pd->ictx);
|
||||
|
||||
free(pd->or[0].ranges);
|
||||
free(pd->or[1].ranges);
|
||||
free(pd->r.ranges);
|
||||
screen_free(&pd->s);
|
||||
colour_palette_free(&pd->palette);
|
||||
|
||||
free(pd->title);
|
||||
free(pd->style);
|
||||
free(pd->border_style);
|
||||
free(pd);
|
||||
popup_free(pd);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -424,7 +423,7 @@ popup_make_pane(struct popup_data *pd, enum layout_type type)
|
||||
pd->job = NULL;
|
||||
}
|
||||
|
||||
screen_set_title(&pd->s, new_wp->base.title);
|
||||
screen_set_title(&pd->s, new_wp->base.title, 0);
|
||||
screen_free(&new_wp->base);
|
||||
memcpy(&new_wp->base, &pd->s, sizeof wp->base);
|
||||
screen_resize(&new_wp->base, new_wp->sx, new_wp->sy, 1);
|
||||
@@ -636,11 +635,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event)
|
||||
|
||||
menu:
|
||||
pd->menu = menu_create("");
|
||||
if (pd->flags & POPUP_INTERNAL) {
|
||||
menu_add_items(pd->menu, popup_internal_menu_items, NULL, c,
|
||||
NULL);
|
||||
} else
|
||||
menu_add_items(pd->menu, popup_menu_items, NULL, c, NULL);
|
||||
menu_add_items(pd->menu, popup_menu_items, NULL, c, NULL);
|
||||
if (m->x >= (pd->menu->width + 4) / 2)
|
||||
x = m->x - (pd->menu->width + 4) / 2;
|
||||
else
|
||||
@@ -850,101 +845,13 @@ popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
|
||||
pd->job = job_run(shellcmd, argc, argv, env, s, cwd,
|
||||
popup_job_update_cb, popup_job_complete_cb, NULL, pd,
|
||||
JOB_NOWAIT|JOB_PTY|JOB_KEEPWRITE|JOB_DEFAULTSHELL, jx, jy);
|
||||
if (pd->job == NULL) {
|
||||
popup_free(pd);
|
||||
return (-1);
|
||||
}
|
||||
pd->ictx = input_init(NULL, job_get_event(pd->job), &pd->palette, c);
|
||||
|
||||
server_client_set_overlay(c, 0, popup_check_cb, popup_mode_cb,
|
||||
popup_draw_cb, popup_key_cb, popup_free_cb, popup_resize_cb, pd);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
popup_editor_free(struct popup_editor *pe)
|
||||
{
|
||||
unlink(pe->path);
|
||||
free(pe->path);
|
||||
free(pe);
|
||||
}
|
||||
|
||||
static void
|
||||
popup_editor_close_cb(int status, void *arg)
|
||||
{
|
||||
struct popup_editor *pe = arg;
|
||||
FILE *f;
|
||||
char *buf = NULL;
|
||||
off_t len = 0;
|
||||
|
||||
if (status != 0) {
|
||||
pe->cb(NULL, 0, pe->arg);
|
||||
popup_editor_free(pe);
|
||||
return;
|
||||
}
|
||||
|
||||
f = fopen(pe->path, "r");
|
||||
if (f != NULL) {
|
||||
fseeko(f, 0, SEEK_END);
|
||||
len = ftello(f);
|
||||
fseeko(f, 0, SEEK_SET);
|
||||
|
||||
if (len == 0 ||
|
||||
(uintmax_t)len > (uintmax_t)SIZE_MAX ||
|
||||
(buf = malloc(len)) == NULL ||
|
||||
fread(buf, len, 1, f) != 1) {
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
len = 0;
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
pe->cb(buf, len, pe->arg); /* callback now owns buffer */
|
||||
popup_editor_free(pe);
|
||||
}
|
||||
|
||||
int
|
||||
popup_editor(struct client *c, const char *buf, size_t len,
|
||||
popup_finish_edit_cb cb, void *arg)
|
||||
{
|
||||
struct popup_editor *pe;
|
||||
int fd;
|
||||
FILE *f;
|
||||
char *cmd;
|
||||
char path[] = _PATH_TMP "tmux.XXXXXXXX";
|
||||
const char *editor;
|
||||
u_int px, py, sx, sy;
|
||||
|
||||
editor = options_get_string(global_options, "editor");
|
||||
if (*editor == '\0')
|
||||
return (-1);
|
||||
|
||||
fd = mkstemp(path);
|
||||
if (fd == -1)
|
||||
return (-1);
|
||||
f = fdopen(fd, "w");
|
||||
if (f == NULL)
|
||||
return (-1);
|
||||
if (fwrite(buf, len, 1, f) != 1) {
|
||||
fclose(f);
|
||||
return (-1);
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
pe = xcalloc(1, sizeof *pe);
|
||||
pe->path = xstrdup(path);
|
||||
pe->cb = cb;
|
||||
pe->arg = arg;
|
||||
|
||||
sx = c->tty.sx * 9 / 10;
|
||||
sy = c->tty.sy * 9 / 10;
|
||||
px = (c->tty.sx / 2) - (sx / 2);
|
||||
py = (c->tty.sy / 2) - (sy / 2);
|
||||
|
||||
xasprintf(&cmd, "%s %s", editor, path);
|
||||
if (popup_display(POPUP_INTERNAL|POPUP_CLOSEEXIT, BOX_LINES_DEFAULT,
|
||||
NULL, px, py, sx, sy, NULL, cmd, 0, NULL, _PATH_TMP, NULL, c, NULL,
|
||||
NULL, NULL, popup_editor_close_cb, pe) != 0) {
|
||||
popup_editor_free(pe);
|
||||
free(cmd);
|
||||
return (-1);
|
||||
}
|
||||
free(cmd);
|
||||
return (0);
|
||||
}
|
||||
|
||||
12
proc.c
12
proc.c
@@ -57,6 +57,7 @@ struct tmuxpeer {
|
||||
struct imsgbuf ibuf;
|
||||
struct event event;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
||||
int flags;
|
||||
#define PEER_BAD 0x1
|
||||
@@ -301,7 +302,6 @@ proc_add_peer(struct tmuxproc *tp, int fd,
|
||||
void (*dispatchcb)(struct imsg *, void *), void *arg)
|
||||
{
|
||||
struct tmuxpeer *peer;
|
||||
gid_t gid;
|
||||
|
||||
peer = xcalloc(1, sizeof *peer);
|
||||
peer->parent = tp;
|
||||
@@ -314,8 +314,10 @@ proc_add_peer(struct tmuxproc *tp, int fd,
|
||||
imsgbuf_allow_fdpass(&peer->ibuf);
|
||||
event_set(&peer->event, fd, EV_READ, proc_event_cb, peer);
|
||||
|
||||
if (getpeereid(fd, &peer->uid, &gid) != 0)
|
||||
if (getpeereid(fd, &peer->uid, &peer->gid) != 0) {
|
||||
peer->uid = (uid_t)-1;
|
||||
peer->gid = (gid_t)-1;
|
||||
}
|
||||
|
||||
log_debug("add peer %p: %d (%p)", peer, fd, arg);
|
||||
TAILQ_INSERT_TAIL(&tp->peers, peer, entry);
|
||||
@@ -384,3 +386,9 @@ proc_get_peer_uid(struct tmuxpeer *peer)
|
||||
{
|
||||
return (peer->uid);
|
||||
}
|
||||
|
||||
gid_t
|
||||
proc_get_peer_gid(struct tmuxpeer *peer)
|
||||
{
|
||||
return (peer->gid);
|
||||
}
|
||||
|
||||
264
prompt-history.c
Normal file
264
prompt-history.c
Normal file
@@ -0,0 +1,264 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026 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 <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
static char *prompt_find_history_file(void);
|
||||
static void prompt_add_typed_history(char *);
|
||||
|
||||
/* Prompt history. */
|
||||
static char **prompt_hlist[PROMPT_NTYPES];
|
||||
static u_int prompt_hsize[PROMPT_NTYPES];
|
||||
|
||||
/* Find the history file to load/save from/to. */
|
||||
static char *
|
||||
prompt_find_history_file(void)
|
||||
{
|
||||
const char *home, *history_file;
|
||||
char *path;
|
||||
|
||||
history_file = options_get_string(global_options, "history-file");
|
||||
if (*history_file == '\0')
|
||||
return (NULL);
|
||||
if (*history_file == '/')
|
||||
return (xstrdup(history_file));
|
||||
|
||||
if (history_file[0] != '~' || history_file[1] != '/')
|
||||
return (NULL);
|
||||
if ((home = find_home()) == NULL)
|
||||
return (NULL);
|
||||
xasprintf(&path, "%s%s", home, history_file + 1);
|
||||
return (path);
|
||||
}
|
||||
|
||||
/* Add loaded history item to the appropriate list. */
|
||||
static void
|
||||
prompt_add_typed_history(char *line)
|
||||
{
|
||||
char *typestr;
|
||||
enum prompt_type type = PROMPT_TYPE_INVALID;
|
||||
|
||||
typestr = strsep(&line, ":");
|
||||
if (line != NULL)
|
||||
type = prompt_type(typestr);
|
||||
if (type == PROMPT_TYPE_INVALID) {
|
||||
/*
|
||||
* Invalid types are not expected, but this provides backward
|
||||
* compatibility with old history files.
|
||||
*/
|
||||
if (line != NULL)
|
||||
*(--line) = ':';
|
||||
prompt_add_history(typestr, PROMPT_TYPE_COMMAND);
|
||||
} else
|
||||
prompt_add_history(line, type);
|
||||
}
|
||||
|
||||
/* Load prompt history from file. */
|
||||
void
|
||||
prompt_load_history(void)
|
||||
{
|
||||
FILE *f;
|
||||
char *history_file, *line, *tmp;
|
||||
size_t length;
|
||||
|
||||
if ((history_file = prompt_find_history_file()) == NULL)
|
||||
return;
|
||||
log_debug("loading history from %s", history_file);
|
||||
|
||||
f = fopen(history_file, "r");
|
||||
if (f == NULL) {
|
||||
log_debug("%s: %s", history_file, strerror(errno));
|
||||
free(history_file);
|
||||
return;
|
||||
}
|
||||
free(history_file);
|
||||
|
||||
for (;;) {
|
||||
if ((line = fgetln(f, &length)) == NULL)
|
||||
break;
|
||||
|
||||
if (length > 0) {
|
||||
if (line[length - 1] == '\n') {
|
||||
line[length - 1] = '\0';
|
||||
prompt_add_typed_history(line);
|
||||
} else {
|
||||
tmp = xmalloc(length + 1);
|
||||
memcpy(tmp, line, length);
|
||||
tmp[length] = '\0';
|
||||
prompt_add_typed_history(tmp);
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
/* Save prompt history to file. */
|
||||
void
|
||||
prompt_save_history(void)
|
||||
{
|
||||
FILE *f;
|
||||
u_int i, type;
|
||||
char *history_file;
|
||||
|
||||
if ((history_file = prompt_find_history_file()) == NULL)
|
||||
return;
|
||||
log_debug("saving history to %s", history_file);
|
||||
|
||||
f = fopen(history_file, "w");
|
||||
if (f == NULL) {
|
||||
log_debug("%s: %s", history_file, strerror(errno));
|
||||
free(history_file);
|
||||
return;
|
||||
}
|
||||
free(history_file);
|
||||
|
||||
for (type = 0; type < PROMPT_NTYPES; type++) {
|
||||
for (i = 0; i < prompt_hsize[type]; i++) {
|
||||
fputs(prompt_type_string(type), f);
|
||||
fputc(':', f);
|
||||
fputs(prompt_hlist[type][i], f);
|
||||
fputc('\n', f);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
}
|
||||
|
||||
/* Get previous line from the history. */
|
||||
const char *
|
||||
prompt_up_history(u_int *idx, u_int type)
|
||||
{
|
||||
/*
|
||||
* History runs from 0 to size - 1. Index is from 0 to size. Zero is
|
||||
* empty.
|
||||
*/
|
||||
|
||||
if (type >= PROMPT_NTYPES)
|
||||
return (NULL);
|
||||
if (prompt_hsize[type] == 0 || idx[type] == prompt_hsize[type])
|
||||
return (NULL);
|
||||
idx[type]++;
|
||||
return (prompt_hlist[type][prompt_hsize[type] - idx[type]]);
|
||||
}
|
||||
|
||||
/* Get next line from the history. */
|
||||
const char *
|
||||
prompt_down_history(u_int *idx, u_int type)
|
||||
{
|
||||
if (type >= PROMPT_NTYPES)
|
||||
return ("");
|
||||
if (prompt_hsize[type] == 0 || idx[type] == 0)
|
||||
return ("");
|
||||
idx[type]--;
|
||||
if (idx[type] == 0)
|
||||
return ("");
|
||||
return (prompt_hlist[type][prompt_hsize[type] - idx[type]]);
|
||||
}
|
||||
|
||||
/* Add line to the history. */
|
||||
void
|
||||
prompt_add_history(const char *line, u_int type)
|
||||
{
|
||||
u_int i, oldsize, newsize, freecount, hlimit, new = 1;
|
||||
size_t movesize;
|
||||
|
||||
if (type >= PROMPT_NTYPES)
|
||||
return;
|
||||
|
||||
oldsize = prompt_hsize[type];
|
||||
if (oldsize > 0 &&
|
||||
strcmp(prompt_hlist[type][oldsize - 1], line) == 0)
|
||||
new = 0;
|
||||
|
||||
hlimit = options_get_number(global_options, "prompt-history-limit");
|
||||
if (hlimit > oldsize) {
|
||||
if (new == 0)
|
||||
return;
|
||||
newsize = oldsize + new;
|
||||
} else {
|
||||
newsize = hlimit;
|
||||
freecount = oldsize + new - newsize;
|
||||
if (freecount > oldsize)
|
||||
freecount = oldsize;
|
||||
if (freecount == 0)
|
||||
return;
|
||||
for (i = 0; i < freecount; i++)
|
||||
free(prompt_hlist[type][i]);
|
||||
movesize = (oldsize - freecount) *
|
||||
sizeof *prompt_hlist[type];
|
||||
if (movesize > 0) {
|
||||
memmove(&prompt_hlist[type][0],
|
||||
&prompt_hlist[type][freecount], movesize);
|
||||
}
|
||||
}
|
||||
|
||||
if (newsize == 0) {
|
||||
free(prompt_hlist[type]);
|
||||
prompt_hlist[type] = NULL;
|
||||
} else if (newsize != oldsize) {
|
||||
prompt_hlist[type] =
|
||||
xreallocarray(prompt_hlist[type], newsize,
|
||||
sizeof *prompt_hlist[type]);
|
||||
}
|
||||
|
||||
if (new == 1 && newsize > 0)
|
||||
prompt_hlist[type][newsize - 1] = xstrdup(line);
|
||||
prompt_hsize[type] = newsize;
|
||||
}
|
||||
|
||||
/* Get history size. */
|
||||
u_int
|
||||
prompt_history_size(enum prompt_type type)
|
||||
{
|
||||
if (type >= PROMPT_NTYPES)
|
||||
return (0);
|
||||
return (prompt_hsize[type]);
|
||||
}
|
||||
|
||||
/* Get history entry. */
|
||||
const char *
|
||||
prompt_history_get(enum prompt_type type, u_int idx)
|
||||
{
|
||||
if (type >= PROMPT_NTYPES)
|
||||
return (NULL);
|
||||
if (idx >= prompt_hsize[type])
|
||||
return (NULL);
|
||||
return (prompt_hlist[type][idx]);
|
||||
}
|
||||
|
||||
/* Clear prompt history. */
|
||||
void
|
||||
prompt_history_clear(enum prompt_type type)
|
||||
{
|
||||
u_int idx;
|
||||
|
||||
if (type >= PROMPT_NTYPES)
|
||||
return;
|
||||
for (idx = 0; idx < prompt_hsize[type]; idx++)
|
||||
free(prompt_hlist[type][idx]);
|
||||
free(prompt_hlist[type]);
|
||||
prompt_hlist[type] = NULL;
|
||||
prompt_hsize[type] = 0;
|
||||
}
|
||||
@@ -3,8 +3,31 @@ TESTS!= echo *.sh
|
||||
.PHONY: all $(TESTS)
|
||||
.NOTPARALLEL: all $(TESTS)
|
||||
|
||||
all: $(TESTS)
|
||||
all:
|
||||
@failed=0; failures=; \
|
||||
for test in $(TESTS); do \
|
||||
printf '%-40s ' "$$test"; \
|
||||
start=$$(date +%s); \
|
||||
if sh "$$test" >/dev/null 2>&1; then \
|
||||
end=$$(date +%s); \
|
||||
echo "PASS ($$((end - start))s)"; \
|
||||
else \
|
||||
end=$$(date +%s); \
|
||||
echo "FAIL ($$((end - start))s)"; \
|
||||
failed=1; \
|
||||
failures="$$failures $$test"; \
|
||||
fi; \
|
||||
sleep 1; \
|
||||
done; \
|
||||
if [ "$$failed" -ne 0 ]; then \
|
||||
echo; \
|
||||
echo "failures:"; \
|
||||
for test in $$failures; do \
|
||||
echo " $$test"; \
|
||||
done; \
|
||||
fi; \
|
||||
exit $$failed
|
||||
|
||||
$(TESTS):
|
||||
sh $*.sh
|
||||
sh $@
|
||||
sleep 1
|
||||
|
||||
Binary file not shown.
@@ -4,9 +4,9 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
TMUX2="$TEST_TMUX -Ltest2"
|
||||
TMUX2="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
@@ -11,9 +11,9 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
TMUX_OUTER="$TEST_TMUX -Ltest2"
|
||||
TMUX_OUTER="$TEST_TMUX -Ltest2 -f/dev/null"
|
||||
$TMUX_OUTER kill-server 2>/dev/null
|
||||
|
||||
trap "$TMUX kill-server 2>/dev/null; $TMUX_OUTER kill-server 2>/dev/null" 0 1 15
|
||||
|
||||
@@ -6,7 +6,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
TMP=$(mktemp)
|
||||
TMP2=$(mktemp)
|
||||
trap "rm -f $TMP $TMP2" 0 1 15
|
||||
|
||||
@@ -7,7 +7,7 @@ PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest"
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
TMP=$(mktemp)
|
||||
|
||||
137
regress/check-names.sh
Normal file
137
regress/check-names.sh
Normal file
@@ -0,0 +1,137 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Name validation policy.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
|
||||
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
|
||||
TMUX="$TEST_TMUX -Ltest -f/dev/null"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
|
||||
fail()
|
||||
{
|
||||
echo "$*"
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 1
|
||||
}
|
||||
|
||||
must_fail()
|
||||
{
|
||||
"$@" >/dev/null 2>&1 && fail "unexpected success: $*"
|
||||
return 0
|
||||
}
|
||||
|
||||
must_equal()
|
||||
{
|
||||
got=$1
|
||||
want=$2
|
||||
|
||||
[ "$got" = "$want" ] || fail "got '$got', expected '$want'"
|
||||
}
|
||||
|
||||
$TMUX new-session -d -x 80 -y 24 || exit 1
|
||||
$TMUX set-option -qg allow-set-title on || exit 1
|
||||
$TMUX set-option -qg allow-rename on || exit 1
|
||||
$TMUX set-option -qg automatic-rename off || exit 1
|
||||
|
||||
# Commands allow empty names, ':', '.', '#' and '#('.
|
||||
$TMUX rename-session '' || fail "empty session name rejected"
|
||||
must_equal "$($TMUX display-message -p '#{session_name}')" ''
|
||||
$TMUX rename-session 'session:.##(ok)' || \
|
||||
fail "session name with : . or #( rejected"
|
||||
must_equal "$($TMUX display-message -p '#{session_name}')" 'session:.#(ok)'
|
||||
|
||||
$TMUX rename-window '' || fail "empty window name rejected"
|
||||
must_equal "$($TMUX display-message -p '#{window_name}')" ''
|
||||
$TMUX rename-window 'window:.##(ok)' || \
|
||||
fail "window name with : . or #( rejected"
|
||||
must_equal "$($TMUX display-message -p '#{window_name}')" 'window:.#(ok)'
|
||||
|
||||
$TMUX set-option -q @name 'format:.#(ok)' || exit 1
|
||||
$TMUX rename-session '#{@name}' || fail "format in session name not expanded"
|
||||
must_equal "$($TMUX display-message -p '#{session_name}')" 'format:.#(ok)'
|
||||
$TMUX rename-window '#{@name}' || fail "format in window name not expanded"
|
||||
must_equal "$($TMUX display-message -p '#{window_name}')" 'format:.#(ok)'
|
||||
$TMUX set-option -q @name 'format:.#(ok)' || exit 1
|
||||
pid=$($TMUX display-message -p '#{pid}')
|
||||
|
||||
created=$($TMUX new-session -dP -F '#{session_id}:#{window_id}' \
|
||||
-s 'new-session:.##(ok)' -n 'new-window:.##(ok)') || \
|
||||
fail "new-session name with : . or #( rejected"
|
||||
created_session=${created%:*}
|
||||
created_window=${created#*:}
|
||||
must_equal "$($TMUX display-message -pt "$created_session" '#{session_name}')" \
|
||||
'new-session:.#(ok)'
|
||||
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
|
||||
'new-window:.#(ok)'
|
||||
$TMUX kill-session -t "$created_session"
|
||||
|
||||
created_window=$($TMUX new-window -dP -F '#{window_id}' \
|
||||
-n 'created-window:.##(ok)') || \
|
||||
fail "new-window name with : . or #( rejected"
|
||||
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
|
||||
'created-window:.#(ok)'
|
||||
|
||||
created=$($TMUX new-session -dP -F '#{session_id}:#{window_id}' \
|
||||
-s 'new-session-#{pid}:.##(ok)' -n 'new-window-#{pid}:.##(ok)') || \
|
||||
fail "format in new-session name not expanded"
|
||||
created_session=${created%:*}
|
||||
created_window=${created#*:}
|
||||
must_equal "$($TMUX display-message -pt "$created_session" '#{session_name}')" \
|
||||
"new-session-$pid:.#(ok)"
|
||||
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
|
||||
"new-window-$pid:.#(ok)"
|
||||
$TMUX kill-session -t "$created_session"
|
||||
|
||||
created_window=$($TMUX new-window -dP -F '#{window_id}' -n '#{@name}') || \
|
||||
fail "format in new-window name not expanded"
|
||||
must_equal "$($TMUX display-message -pt "$created_window" '#{window_name}')" \
|
||||
'format:.#(ok)'
|
||||
|
||||
# Invalid UTF-8 is never allowed for command names.
|
||||
invalid=$(printf '\302')
|
||||
must_fail $TMUX rename-session "bad${invalid}name"
|
||||
must_fail $TMUX rename-window "bad${invalid}name"
|
||||
must_fail $TMUX new-session -d -s "bad${invalid}name"
|
||||
must_fail $TMUX new-session -d -n "bad${invalid}name"
|
||||
must_fail $TMUX new-window -d -n "bad${invalid}name"
|
||||
must_fail $TMUX set-buffer -b "bad${invalid}name" data
|
||||
|
||||
# Titles set by commands allow '#', ':' and '.'.
|
||||
$TMUX select-pane -T 'title#:.ok' || fail "command title rejected"
|
||||
must_equal "$($TMUX display-message -p '#{pane_title}')" 'title#:.ok'
|
||||
$TMUX send-keys "printf '\\033]2;title#[fg=red]ok\\007'" Enter || exit 1
|
||||
sleep 1
|
||||
must_equal "$($TMUX display-message -p '#{pane_title}')" 'title#[fg=red]ok'
|
||||
$TMUX send-keys "printf '\\033]2;title#(bad)\\007'" Enter || exit 1
|
||||
sleep 1
|
||||
must_equal "$($TMUX display-message -p '#{pane_title}')" 'title_(bad)'
|
||||
|
||||
# Buffer names allow '#', ':' and '.'.
|
||||
$TMUX set-buffer -b 'buffer#:.ok' data || fail "buffer name rejected"
|
||||
must_equal "$($TMUX list-buffers -F '#{buffer_name}')" 'buffer#:.ok'
|
||||
|
||||
# Window names from escape sequences allow '#' except in '#('.
|
||||
$TMUX send-keys "printf '\\033kescape#:.ok\\033\\\\'" Enter || exit 1
|
||||
sleep 1
|
||||
must_equal "$($TMUX display-message -p '#{window_name}')" 'escape#:.ok'
|
||||
|
||||
# Titles from escape sequences allow '#' except in '#('.
|
||||
$TMUX send-keys "printf '\\033]2;escape#:.ok\\007'" Enter || exit 1
|
||||
sleep 1
|
||||
must_equal "$($TMUX display-message -p '#{pane_title}')" 'escape#:.ok'
|
||||
|
||||
# Invalid UTF-8 from escape sequences is ignored.
|
||||
$TMUX rename-window 'before-invalid' || exit 1
|
||||
$TMUX send-keys "printf '\\033kbad\\302name\\033\\\\'" Enter || exit 1
|
||||
sleep 1
|
||||
must_equal "$($TMUX display-message -p '#{window_name}')" 'before-invalid'
|
||||
|
||||
$TMUX select-pane -T 'before-invalid-title' || exit 1
|
||||
$TMUX send-keys "printf '\\033]2;bad\\302title\\007'" Enter || exit 1
|
||||
sleep 1
|
||||
must_equal "$($TMUX display-message -p '#{pane_title}')" 'before-invalid-title'
|
||||
|
||||
$TMUX kill-server 2>/dev/null
|
||||
exit 0
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user