335 Commits
3.3a ... 3.4

Author SHA1 Message Date
Nicholas Marriott
9ae69c3795 3.4. 2024-02-13 10:17:07 +00:00
Thomas Adam
0960862950 Merge branch 'obsd-master' 2024-02-13 10:01:10 +00:00
Nicholas Marriott
44ad25b367 Update CHANGES. 2024-02-13 09:12:08 +00:00
nicm
40b97b1715 Add two new values for the destroy-unattached option to destroy sessions
only if they are not members of sessions groups, from Mark Huang, GitHub
issue 3806.
2024-02-13 08:10:23 +00:00
nicm
4bdb855020 Do not allow paste into panes which have exited, from Romain Francoise
in GitHub issue 3830.
2024-02-13 08:03:50 +00:00
Thomas Adam
ea7136fb83 Merge branch 'obsd-master' 2024-01-22 18:01:09 +00:00
nicm
428f8a9b28 Increase buffer size to avoid truncating styles, GitHub issue 3809 from
Ricardo Bittencourt.
2024-01-22 16:34:46 +00:00
Nicholas Marriott
84faada25b Remove existing defines. 2024-01-17 10:59:07 +00:00
Nicholas Marriott
55d0abad89 Need htonll and ntohll. 2024-01-17 10:57:32 +00:00
Nicholas Marriott
7d91b4b90b htobe is not portable. 2024-01-17 09:47:35 +00:00
Nicholas Marriott
66369416fc Update imsg. 2024-01-17 09:41:53 +00:00
Thomas Adam
001e26d0bb Merge branch 'obsd-master' 2024-01-16 14:01:09 +00:00
claudio
2e39d698d2 Use imsg_get_fd() instead of direct access to imsg.fd
The change in proc.c can be further simplified once imsg_free() takes
care of unclaimed file descriptors.

OK nicm@
2024-01-16 13:09:11 +00:00
Thomas Adam
e809c2ec35 Merge branch 'obsd-master' 2024-01-03 19:17:10 +00:00
Nicholas Marriott
4266d3efc8 Assignment should be inside SIXEL. 2023-12-28 03:12:27 +00:00
nicm
40a20bc8ae Only wrap pattern in *s if using a regular expression. 2023-12-27 20:42:01 +00:00
nicm
f7bf7e9671 Remove flags from the prefix before comparing with the received key so
that modifier keys with flags work correctly, GitHub issue 3764.
2023-12-27 20:23:59 +00:00
nicm
008ecd4592 groff apparently generates broken output for some common characters in
mdoc, so escaped versions have to be used instead. From Alexis
Hildebrandt in GitHub issue 3762.
2023-12-27 20:20:50 +00:00
nicm
73a2b12088 Always initialize screen mode, GitHub issue 3750 from Ding Fei. 2023-12-27 20:17:13 +00:00
Nicholas Marriott
605bf21ff2 Do not use NULL window, GitHub issue 3747. 2023-12-27 20:15:57 +00:00
nicm
f028445407 Correctly handle window ops with no pane, GitHub issue 3747. 2023-12-27 20:13:35 +00:00
Thomas Adam
bdf8e614af Merge branch 'obsd-master' 2023-11-14 22:01:09 +00:00
nicm
4dea352dee Don't strdup NULL filename. 2023-11-14 20:01:11 +00:00
Thomas Adam
151875d144 Merge branch 'obsd-master' 2023-11-14 18:01:10 +00:00
nicm
88fd1f00b8 Handle NULL client (in config file) when showing a status message; also
copy the file when processing if-shell since it may be freed. GitHub
issue 3746.
2023-11-14 15:59:49 +00:00
nicm
1a14d6d2e1 Use SM 2026 for Sync which is more widely supported now. 2023-11-14 15:38:33 +00:00
Thomas Adam
381c00a74e Merge branch 'obsd-master' 2023-11-02 22:58:45 +00:00
nicm
5aadee6df4 next-prompt can have 1 argument. 2023-11-02 10:38:14 +00:00
Nicholas Marriott
a5545dbc9f Allow attributes to have only two parameters, from Tim Culverhouse. 2023-11-01 10:37:41 +00:00
nicm
fdf465925e Do not allow combined UTF-8 characters that are too long, GitHub issue
3729.
2023-10-30 16:05:30 +00:00
nicm
36e1ac6556 Unzoom window at start of destroy so it doesn't happen later (when
destroying panes) after the layout has been freed, GitHub issue 3717.
2023-10-23 08:12:00 +00:00
nicm
ffa376edf7 Switch to tiparm_s (added in ncurses 6.4-20230424) instead of tparm,
which allows ncurses to validate the capabilities correctly.
2023-10-17 09:55:32 +00:00
Thomas Adam
b777780720 Merge branch 'obsd-master' 2023-09-19 12:01:11 +01:00
nicm
347cd0b5f8 Fix a couple of mouse mode flag names. 2023-09-19 08:35:44 +00:00
Nicholas Marriott
0ca28b362e Add combined character test. 2023-09-19 09:29:20 +01:00
Nicholas Marriott
51b80b985e Restore utf8proc code. 2023-09-19 09:29:04 +01:00
Nicholas Marriott
789a98982e Reply to SMGRAPHICS. 2023-09-19 09:27:59 +01:00
Thomas Adam
b202a2f1b5 Merge branch 'obsd-master' 2023-09-17 21:03:06 +01:00
nicm
7e79108f8a Remove next- and previous-prompt added in error. GitHub issue 3696. 2023-09-16 16:18:29 +00:00
nicm
f09cde2542 Change UTF-8 combining to inspect the previous character at the cursor
position rather than keeping the last character from the input stream,
this is how most terminals work and fixes problems with displaying these
characters in vim. GitHub issue 3600.
2023-09-15 15:49:05 +00:00
Thomas Adam
9f9156c030 Merge branch 'obsd-master' 2023-09-15 10:01:11 +01:00
nicm
d394293ba5 Add -t to source-file, GitHub issue 3473. 2023-09-15 06:31:49 +00:00
Thomas Adam
c57a09269b Merge branch 'obsd-master' 2023-09-14 16:01:10 +01:00
nicm
8191c58737 Reset combine flag only if text is actually processed. 2023-09-14 13:01:35 +00:00
Thomas Adam
f68d35c529 Merge branch 'obsd-master' 2023-09-08 10:01:10 +01:00
nicm
c02bc4dbe9 On second thoughts, do check DA2 for DECFRA and DECSLRM since that will
catch terminals that say they are VT520 even if we can't use DA1
(because of VTE).
2023-09-08 07:05:06 +00:00
nicm
4872811ba7 Use DECSLRM and DECFRA only at level 4 rather than checking the terminal
id.
2023-09-08 06:52:31 +00:00
Nicholas Marriott
1a1290f30b Only remove images if reverse index actually scrolls. 2023-09-07 22:02:11 +01:00
Nicholas Marriott
7be7ca7195 Shut autoconf up. 2023-09-07 18:24:28 +01:00
Thomas Adam
32197fa52d Merge branch 'obsd-master' 2023-09-07 14:01:11 +01:00
nicm
9653a52a6a Use DECSLRM and DECFRA on terminals pretending to be VT520 or VT525 as
well as VT420.
2023-09-07 10:21:46 +00:00
Thomas Adam
d60c8942ce Merge branch 'obsd-master' 2023-09-07 10:01:10 +01:00
nicm
c99f9e92e0 Accept 65 for VT525 as well. 2023-09-07 07:19:21 +00:00
Thomas Adam
e26356607e Merge branch 'obsd-master' 2023-09-04 10:01:10 +01:00
nicm
43e5e80343 Skip wrapped lines in top level search loop because they will be
combined in the inner loop (in window_copy_search_rl_regex and the
others), avoids searching the same text multiple times. Also add a line
length limit for regex searches. GitHub issue 3675.
2023-09-04 08:01:43 +00:00
Thomas Adam
1742138f05 Merge branch 'obsd-master' 2023-09-02 22:01:09 +01:00
nicm
c767d62329 Request terminal colours again on SIGWINCH but at most once every 30
seconds, GitHub issue 3582.
2023-09-02 20:03:10 +00:00
Thomas Adam
7ad29b9831 Merge branch 'obsd-master' 2023-09-02 12:01:09 +01:00
nicm
d209fe9b1e Setulc only does RGB colour so add Setulc1 to do non-RGB colours, GitHub
issue 3627.
2023-09-02 09:17:23 +00:00
nicm
c5542637d7 Set visited flag on last windows when linking session. 2023-09-02 08:38:37 +00:00
Thomas Adam
e7c829fc67 Merge branch 'obsd-master' 2023-09-01 20:01:10 +01:00
nicm
579829eef2 Only compare the actual size of the UTF-8 character, not all of it. 2023-09-01 18:43:54 +00:00
Nicholas Marriott
3aa20f6e75 Use %05X not %08X. 2023-09-01 19:37:27 +01:00
nicm
f78279bb2e Add missing -T to getopt string. 2023-09-01 16:40:38 +00:00
Thomas Adam
a99d7c6314 makefile: fixup bad merge 2023-09-01 17:13:55 +01:00
Thomas Adam
cf1ed67fcc Merge branch 'obsd-master' 2023-09-01 17:09:41 +01:00
Thomas Adam
1aec420465 Merge branch 'obsd-master' 2023-09-01 17:06:27 +01:00
nicm
16e4b39359 Clear combine flag when a non-UTF-8 set of characters is encountered. 2023-09-01 16:01:54 +00:00
nicm
9456258ccc Rewrite combined character handling to be more consistent and to support
newer Unicode combined characters (which we have to "know" are combined
since they are not width zero). GitHub issue 3600.
2023-09-01 14:29:11 +00:00
nicm
c41d59f232 Expand name before looking for window with -S, GitHub issue 3670. 2023-09-01 14:24:46 +00:00
Nicholas Marriott
d682ef88e6 Bump width and height to 10000. 2023-09-01 14:54:27 +01:00
nicm
c1e6e54e6e Add detach-on-destroy previous and next, mostly from Alexis Hildebrandt. 2023-09-01 13:48:54 +00:00
Nicholas Marriott
62f657845e Fix merge error, from Jakub Łukasiewicz. 2023-08-26 20:57:44 +01:00
Thomas Adam
a9841a6d1e portable: fixup merge 2023-08-23 20:55:23 +01:00
Thomas Adam
70ecf17f85 Merge branch 'obsd-master' 2023-08-23 20:37:42 +01:00
nicm
71d453f169 Add -c to run-shell to set working directory, from someone in GitHub
issue 3661.
2023-08-23 08:40:25 +00:00
Nicholas Marriott
071849f82f Improve logging of SIXEL parsing errors. 2023-08-23 09:30:20 +01:00
nicm
52084b2948 Log what input_dcs_dispatch does with the input data. 2023-08-23 08:30:07 +00:00
Nicholas Marriott
dfbc6b1888 Merge SIXEL branch.
Squashed commit of the following:

commit 6ebc3feb4671d9b25b3db99d3c16b2323b8e3d02
Author: topcat001 <anindya49@hotmail.com>
Date:   Sun Aug 20 16:09:51 2023 -0700

    Remove redundant {}.

commit 6f013fce39602c259a5be2d690d548c73e51cccc
Author: topcat001 <anindya49@hotmail.com>
Date:   Sun Aug 20 16:02:15 2023 -0700

    Revert "Do not defer redraw if it is just the status line (will need to do more here I"

    This reverts commit 0a15bbf3f1.

commit e6322b4196d73c975ba2e73633e6de9c46779059
Author: topcat001 <anindya49@hotmail.com>
Date:   Sun Aug 20 15:46:59 2023 -0700

    Fix placeholder label and clean up.

commit 5896ac52a1f72056a75480b3e1ada328f239df9b
Merge: ad982330 e3a8b843
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Fri Aug 18 17:00:03 2023 +0100

    Merge branch 'master' into sixel

commit ad98233066b72547aee7fa0c87838847ee7f1ece
Author: topcat001 <anindya49@hotmail.com>
Date:   Tue Aug 15 13:57:08 2023 -0700

    Better text placeholder.

commit 312d83252c27fc4d09d09d121bf7573336e3cdca
Merge: 14b8b524 3d93b0c5
Author: topcat001 <anindya49@hotmail.com>
Date:   Tue Aug 15 13:39:22 2023 -0700

    Merge remote-tracking branch 'origin/master' into sixel

commit 14b8b524523a7d5a4e42f7dfa346905c604c91e2
Merge: 4baf7642 fda39377
Author: topcat001 <anindya49@hotmail.com>
Date:   Sat Jul 22 17:29:10 2023 -0700

    Merge branch 'master' into sixel

commit 4baf76422fadb216bf27b47645b52da3379e7dea
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Wed Jun 21 07:43:53 2023 +0100

    Both files can go on one line.

commit 4c92acf6ff24dde37ad41cd168ea2d3bcefb8567
Author: topcat001 <anindya49@hotmail.com>
Date:   Sat Jun 17 17:53:01 2023 -0700

    Merge topcat001/tmux/sixel.

commit 6794facc82e98f8448c192913cf62fe6e10fde63
Merge: 7b85f5ad f41c536f
Author: topcat001 <anindya49@hotmail.com>
Date:   Sat Jun 17 17:21:02 2023 -0700

    Merge remote-tracking branch 'origin/master' into sixel

commit 7b85f5adf9a5094db580ca98e4d2231d8d5b5a4f
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Jun 8 12:55:03 2023 +0100

    Do not require passthrough for SIXEL.

commit a6ee55e0925cac35d011c188db2da0421fc09be1
Merge: 6da391f4 fe385b18
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Jun 8 12:19:55 2023 +0100

    Merge branch 'master' into sixel

commit 6da391f460414ed3dde23e5ab6ca3fe8e988ce51
Merge: 0d71e585 0eb5d254
Author: topcat001 <anindya49@hotmail.com>
Date:   Sat May 20 17:05:55 2023 -0700

    Merge branch 'master' into sixel

commit 0d71e5853ffe797f90b815ac3af25ac0ad92ab07
Merge: 64368a1a fbe6fe7f
Author: topcat001 <anindya49@hotmail.com>
Date:   Sat Apr 29 17:32:07 2023 -0700

    Merge branch 'master' into sixel

commit 64368a1a63f04fb877b57e4286c9a2e1efe966c9
Merge: c630a56a 22eb0334
Author: topcat001 <anindya49@hotmail.com>
Date:   Thu Mar 30 14:21:09 2023 -0700

    Merge branch 'master' into sixel

commit c630a56a621b9761eed558cbd566a36cb09adf8f
Merge: 34c96c4c aaa043a2
Author: topcat001 <anindya49@hotmail.com>
Date:   Thu Nov 10 18:53:01 2022 -0800

    Merge branch 'master' into sixel

commit 34c96c4c4a33f86b49c8a53dc48b2b817db24e95
Merge: 2a1e16a2 50f4e0fa
Author: topcat001 <anindya49@hotmail.com>
Date:   Sat Nov 5 18:05:36 2022 -0700

    Merge branch 'master' into sixel

commit 2a1e16a24dc75741c66f5d72fa5bf26b73507993
Merge: a82f14c7 d001a94d
Author: topcat001 <anindya49@hotmail.com>
Date:   Thu Oct 27 16:01:35 2022 -0700

    Merge branch 'master' into sixel

commit a82f14c7b2
Merge: 742c0634 f7b30ed3
Author: topcat001 <anindya49@hotmail.com>
Date:   Sun Aug 28 13:43:07 2022 -0700

    Merge branch 'master' into sixel

commit 742c063473
Merge: 906c92a5 87b248f3
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Fri Apr 1 10:14:15 2022 +0100

    Merge branch 'master' into sixel

commit 906c92a5f4
Merge: 6680a024 138ffc7c
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Wed Dec 8 10:37:33 2021 +0000

    Merge branch 'master' into sixel

commit 6680a024be
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Oct 7 13:59:08 2021 +0100

    Fix build.

commit ebd2c58593
Merge: 90dc0519 fed7b29c
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Oct 7 13:19:48 2021 +0100

    Merge branch 'master' into sixel

commit 90dc05191c
Merge: a282439f 4694afbe
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Feb 20 20:37:32 2020 +0000

    Merge branch 'master' into sixel

commit a282439fcb
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Jan 30 09:12:53 2020 +0000

    Add missing declarations.

commit 3a741aacd1
Merge: 40ad0107 339832b9
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Jan 30 09:11:01 2020 +0000

    Merge branch 'sixel-passthrough' into sixel

commit 339832b92c
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Jan 30 09:04:51 2020 +0000

    Bad merge.

commit 92ed9fc0b2
Merge: 5bb07548 32be954b
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Jan 30 09:03:38 2020 +0000

    Merge branch 'master' into sixel-passthrough

commit 40ad01073d
Merge: dd3c72f1 61b075a2
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Sun Jan 12 20:03:41 2020 +0000

    Merge branch 'master' into sixel

commit 5bb075487f
Merge: 7c033a74 54efe337
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Wed Dec 18 20:24:42 2019 +0000

    Merge branch 'master' into sixel-passthrough

commit dd3c72f132
Merge: 1a0e5fe9 54efe337
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Wed Dec 18 20:24:26 2019 +0000

    Merge branch 'master' into sixel

commit 1a0e5fe933
Merge: cf071ffe 15d7e564
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Tue Dec 10 16:34:11 2019 +0000

    Merge branch 'master' into sixel

commit cf071ffecd
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Mon Dec 9 15:41:56 2019 +0000

    Remove images when reflow happens.

commit 2006b7a563
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Dec 5 09:27:15 2019 +0000

    More invalidation of images.

commit b642eac450
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Dec 5 09:11:24 2019 +0000

    Redraw and scroll images and part of invalidating them.

commit 7566e37a46
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Dec 5 08:51:24 2019 +0000

    Call sixel_scale with the right number of arguments.

commit 62c0280b23
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Dec 5 08:48:58 2019 +0000

    Correctly remove when not visible.

commit 86c5098a88
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Dec 5 08:32:25 2019 +0000

    Add helpers to scroll image up and a flag to copy the colours.

commit 49f2f0a8f1
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Dec 5 00:02:55 2019 +0000

    Store images, currently at most 10.

commit 3aebcc6709
Merge: 146ee3f6 92ecd611
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Wed Dec 4 19:27:16 2019 +0000

    Merge branch 'master' into sixel

commit 7c033a74e2
Merge: 0a15bbf3 92ecd611
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Wed Dec 4 12:41:09 2019 +0000

    Merge branch 'master' into sixel-passthrough

commit 146ee3f6f8
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Sat Nov 30 09:47:53 2019 +0000

    Don't write image as text yet.

commit 0a15bbf3f1
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Sat Nov 30 09:15:35 2019 +0000

    Do not defer redraw if it is just the status line (will need to do more here I
    think).

commit a5b1e20941
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Nov 28 14:20:22 2019 +0000

    Add a flag to disable blocking while sending a SIXEL image (turned off when the
    buffer hits 0 size).

commit 968382aa6a
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Nov 28 12:35:18 2019 +0000

    Pass through SIXEL DCS sequences (treat similarly to the passthrough escape
    sequence) if it appears the terminal outside supports them.

commit b1904c9b8d
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Sat Nov 30 09:17:18 2019 +0000

    Store SIXELs as a box for the moment.

commit 5d8dbcdf3d
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Sat Nov 30 09:15:35 2019 +0000

    Do not defer redraw if it is just the status line (will need to do more here I
    think).

commit 0c999a402e
Merge: 28961dd5 866b053f
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Fri Nov 29 18:54:09 2019 +0000

    Merge branch 'master' into sixel

commit 28961dd5a3
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Nov 28 14:24:57 2019 +0000

    Add an image.

commit d2e3f3c1cc
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Nov 28 14:20:22 2019 +0000

    Add a flag to disable blocking while sending a SIXEL image (turned off when the
    buffer hits 0 size).

commit e01df67ca1
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Nov 28 13:21:40 2019 +0000

    Crop and scale images as needed when drawing them.

commit e24acc0b5c
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Nov 28 12:38:02 2019 +0000

    Simple SIXEL parse and modify API.

commit b34111b3da
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Nov 28 12:35:18 2019 +0000

    Pass through SIXEL DCS sequences (treat similarly to the passthrough escape
    sequence) if it appears the terminal outside supports them.
2023-08-22 08:43:35 +01:00
Thomas Adam
e3a8b8434c Merge branch 'obsd-master' 2023-08-17 18:01:09 +01:00
nicm
8636848e63 Add a session, pane and user mouse range types for the status line and
add format variables for mouse_status_line and mouse_status_range so
they can be associated with different commands in the key bindings.
GitHub issue 3652.
2023-08-17 14:10:28 +00:00
Thomas Adam
3d93b0c52e Merge branch 'obsd-master' 2023-08-15 12:01:09 +01:00
nicm
d9942c769e Add meta bindings for status line menus as well as the existing pane one
for terminals which steal the mouse menu button.
2023-08-15 09:51:48 +00:00
Thomas Adam
6a45e6c256 Merge branch 'obsd-master' 2023-08-15 10:01:09 +01:00
nicm
b770a429c6 Add an option menu-selected-style to configure the currently selected
menu item, from Alexis Hildebrandt.
2023-08-15 07:01:47 +00:00
Thomas Adam
11e69f6025 Merge branch 'obsd-master' 2023-08-11 20:01:09 +01:00
nicm
57837bbf67 Do not crash if in buffer mode and the last buffer is deleted using the
command.
2023-08-11 17:09:00 +00:00
Thomas Adam
4c60afde78 Merge branch 'obsd-master' 2023-08-08 12:01:10 +01:00
Thomas Adam
ed689cd54e Merge branch 'obsd-master' 2023-08-08 10:01:11 +01:00
nicm
7a44984069 Add flag to next-prompt/previous-prompt to go to command output instead,
from Magnus Gross.
2023-08-08 08:21:29 +00:00
nicm
dee72ed41f Add options and flags for menu styles similar to those existing for
popups, from Alexis Hildebrandt. GitHub issue 3650.
2023-08-08 08:08:47 +00:00
nicm
1071ef8fc5 Extend the menu drawing function to support custom characters and
styles, from Alexis Hildebrandt.
2023-08-08 07:41:04 +00:00
nicm
2b535bc173 Fix a couple of rounded border characters, from Alexis Hildebrandt. 2023-08-08 07:19:48 +00:00
Thomas Adam
b791f00bf1 Merge branch 'obsd-master' 2023-08-07 14:01:12 +01:00
Thomas Adam
3e82ad5340 Merge branch 'obsd-master' 2023-08-07 12:01:10 +01:00
nicm
7b1030293c Free title earlier, from Alexis Hildebrandt. 2023-08-07 10:52:00 +00:00
nicm
a5fd80bbc3 Trim can generate strings longer than the original if there are many #s,
so create a bigger buffer. Reported by Robert Morris.
2023-08-07 10:04:29 +00:00
Thomas Adam
fda3937734 Merge branch 'obsd-master' 2023-07-19 16:01:09 +01:00
nicm
b13c230749 Correct visited flag when the last window list is rebuilt by renumbering
windows, appears to fix hang reported by Mark Kelly.
2023-07-19 13:03:36 +00:00
Thomas Adam
715f39a53a Merge branch 'obsd-master' 2023-07-14 22:01:10 +01:00
nicm
2f74e811f1 Set extended keys flag again after reset, from Eric T Johnson. 2023-07-14 19:32:59 +00:00
Thomas Adam
828efc7bcf Merge branch 'obsd-master' 2023-07-13 18:01:10 +01:00
nicm
84936b832f Use 8 for underscore colour defaults instead of 0 which is less
confusing, and fix writing tge default colour. GitHub issue 3627.
2023-07-13 06:03:48 +00:00
Thomas Adam
c8494dff7b Merge branch 'obsd-master' 2023-07-11 20:01:10 +01:00
nicm
8fcc212e7a Remove Ns and Li and change Nm to Ic, suggested by jmc. 2023-07-11 16:09:09 +00:00
Thomas Adam
0e281530cb Merge branch 'obsd-master' 2023-07-11 10:01:10 +01:00
nicm
efded95ed7 Add descriptions of copy mode commands, from Michael Bianco. 2023-07-11 07:34:23 +00:00
Thomas Adam
18870913c5 Merge branch 'obsd-master' 2023-07-10 14:01:12 +01:00
nicm
63b7282377 It should no longer be necessary to ignore SIGCHLD because it is now
blocked around daemon(), and doing so causes trouble with newer libevent
(it cannot restore the original handler). Reported by Azat Khuzhin in
GitHub issue 3626.
2023-07-10 12:00:08 +00:00
Thomas Adam
269dab4b3e Merge branch 'obsd-master' 2023-07-10 12:01:12 +01:00
nicm
4ece43a029 Loop around waitpid in client, from Azat Khuzhin. 2023-07-10 09:35:46 +00:00
nicm
8b3e2eab5a Use a stack for last panes line windows, from Thomas Bertschinger in
GitHub issue 3588.
2023-07-10 09:24:53 +00:00
Thomas Adam
e4c4ceb286 Merge branch 'obsd-master' 2023-07-10 02:01:11 +01:00
nicm
b7e22d00b4 Call closefrom after removing signals because newer libevent doesn't
like its signal fd being closed Azat Khuzhin.
2023-07-09 22:54:52 +00:00
Thomas Adam
1a11c972ae Merge branch 'obsd-master' 2023-07-03 20:01:12 +01:00
nicm
43b841f188 Add support for marking lines with a shell prompt based on the OSC 133
extension, from Munif Tanjim in GitHub issue 3596.
2023-07-03 16:47:43 +00:00
Thomas Adam
f9b0460840 Merge branch 'obsd-master' 2023-07-03 14:01:10 +01:00
Thomas Adam
659d876cd5 Merge branch 'obsd-master' 2023-07-03 12:01:09 +01:00
nicm
ac43186dff Do not risk writing over the end of the buffer when it ends in #
(because strchr \0 will be non-NULL), reported by Robert Morris in
GitHub issue 3610.
2023-07-03 10:48:26 +00:00
nicm
e79fb214f8 Another warning fix for GCC from Thomas Klausner. 2023-07-03 08:37:14 +00:00
Thomas Adam
9cf58d1a52 Merge branch 'obsd-master' 2023-07-01 02:01:10 +01:00
nicm
a2a02fd7d7 Change a few types to fix warnings, from Thomas Klausner. 2023-06-30 21:55:08 +00:00
Thomas Adam
237ee6f231 Merge branch 'obsd-master' 2023-06-30 16:01:10 +01:00
nicm
4e57894e85 Get rid of some warnings with GCC 10, from Thomas Klausner. 2023-06-30 13:19:32 +00:00
Nicholas Marriott
8c9fbbf4f3 Add libterminfo for NetBSD, from Thomas Klausner. 2023-06-29 15:31:32 +01:00
Thomas Adam
3f3d61bd58 Merge branch 'obsd-master' 2023-06-26 12:01:09 +01:00
Thomas Adam
80d4f4afc6 Merge branch 'obsd-master' 2023-06-26 10:01:10 +01:00
nicm
2546216019 When exiting alternate screen, there is no need to reflow when going
back to old size since the contents will be overwritten. GitHub issue
3510.
2023-06-26 08:14:19 +00:00
nicm
ff8882a24f Add "us" to styles for underscore colour, GitHub issue 3589. 2023-06-26 07:17:40 +00:00
Thomas Adam
ffe2410639 Merge branch 'obsd-master' 2023-06-25 20:01:09 +01:00
nicm
9e14c1f88d SGR 0 should not end hyperlink, reported by Lucas Trzesniewski. 2023-06-25 15:53:07 +00:00
Thomas Adam
29a5dfc7c0 Merge branch 'obsd-master' 2023-06-21 08:01:08 +01:00
nicm
645bf8b3ab Check fdopen return value, from Christian Menges. 2023-06-21 06:28:18 +00:00
Thomas Adam
f41c536ff3 Merge branch 'obsd-master' 2023-06-08 14:01:09 +01:00
nicm
bdd05bdbd3 Fix mismatch between function prototype and definition, from Anindya
Mukherjee.
2023-06-08 11:17:28 +00:00
Nicholas Marriott
fe385b180f Try utf8proc with pkg-config, from Alex Wu. 2023-06-08 09:10:43 +01:00
Thomas Adam
0eb5d25453 Merge branch 'obsd-master' 2023-05-19 10:01:09 +01:00
nicm
1d98394b41 Add format for server_sessions, from Magnus Gross. 2023-05-19 07:46:34 +00:00
Thomas Adam
149d9cc851 Merge branch 'obsd-master' 2023-05-08 12:01:09 +01:00
tb
204d8f31d7 Reorder struct grid_cell_entry
On aarch64 with llvm 15, the new -Wunaligned-access emits noise on every
one of tmux's source files. This avoids this warning by moving a u_char
to the end of the struct. This does not change the size of the struct on
any architecture.

ok nicm
2023-05-08 10:03:39 +00:00
Thomas Adam
fbe6fe7f55 Merge branch 'obsd-master' 2023-04-28 12:52:21 +01:00
Nicholas Marriott
168eab11a7 Cast both strings for tparm. 2023-04-28 07:23:53 +01:00
nicm
41b318ac7c Add options to change the confirm key and default behaviour of
confirm-before. From Elias Assaf in GitHub issue 3548; prompted by an
earlier change from Yutaro Yoshii in GitHub issue 3496.
2023-04-28 06:12:27 +00:00
nicm
bf636d9575 Do not fatal if tparm fails, instead just log it (not working sequences
are better than exiting).
2023-04-28 05:59:35 +00:00
Nicholas Marriott
39d41d0810 Use ncurses' new tparm_s function (added in 6.4-20230424) instead of tparm so
it does not object to string arguments in capabilities it doesn't already know.
2023-04-28 06:44:40 +01:00
Thomas Adam
9d8131c190 Merge branch 'obsd-master' 2023-04-27 19:16:08 +01:00
Nicholas Marriott
0ff991b25f Set default lock command to vlock on Linux if present at build time, from Josh
Boyer in GitHub issue 3527.
2023-04-25 17:23:32 +01:00
Nicholas Marriott
15c70e562c Include NCURSES_VERSION_PATCH in the log. 2023-04-25 16:48:49 +01:00
nicm
8f34504736 Tidy tparm wrapper functions to have more obvious names and check tparm
return value.
2023-04-25 09:31:50 +00:00
nicm
551e0c36d9 Invalidate cached tty state after changing features since they may
change what the terminal can do and need mouse sequences or similar to
be sent again, GitHub issue 3513.
2023-04-25 09:24:44 +00:00
nicm
48eba4c195 Ignore the user keys range when checking if a key is Unicode. 2023-04-17 18:22:24 +00:00
nicm
9f605178c3 It seems silly to use progname for version, just always say tmux. 2023-04-17 18:00:19 +00:00
nicm
bcafe51378 Make the check if printing is allowed the same as writing which is less
confusing.
2023-04-17 17:58:35 +00:00
nicm
280fe77edd Discard mouse sequences that have the right form but actually are
invalid (for example have column zero rather than one).
2023-04-17 17:57:35 +00:00
Nicholas Marriott
b9524f5b72 Add support for spawning panes in separate cgroups with systemd and a configure
flag to disable. From Eric T Johnson yut23 AT gvljohnsons DOT com in GitHub
issue 3514.
2023-04-03 08:54:06 +01:00
nicm
a2018b2c3f Clarify text for new -A slightly, GitHub issue 3508. 2023-04-03 07:39:37 +00:00
nicm
c21af7e446 Add a format to show if there are unseen changes while in a mode, from
Dan Aloni in GitHub issue 3498.
2023-03-27 08:47:57 +00:00
nicm
d73078838d For passthrough, don't write to clients attached to different sessions,
based on a fix from Sergei Grechanik.
2023-03-27 08:31:32 +00:00
Thomas Adam
22eb0334c3 Merge branch 'obsd-master' 2023-03-15 22:01:09 +00:00
nicm
a9ac614691 Do not leak screen in popups, GitHub issue 3492. 2023-03-15 19:23:22 +00:00
Thomas Adam
1d0f68dee9 Merge branch 'obsd-master' 2023-03-15 10:01:10 +00:00
nicm
ac4bb89d43 Fix command prompt not to always append argument but only if there has
actually been expansion. GitHub issue 3493.
2023-03-15 08:15:39 +00:00
Thomas Adam
b55f34029a Merge branch 'obsd-master' 2023-02-10 16:01:10 +00:00
nicm
907f58cc3c Fix cursor position after zero width space, GitHub issue 3469. 2023-02-10 14:01:43 +00:00
Thomas Adam
023b0f76c3 Merge branch 'obsd-master' 2023-02-07 14:01:11 +00:00
Thomas Adam
ddaeebc213 Merge branch 'obsd-master' 2023-02-07 12:01:13 +00:00
nicm
1262e685b8 Remove old buffer when renaming rather than complaining, GitHub issue
3467 from Jean-Philippe Paradis.
2023-02-07 10:56:04 +00:00
nicm
0bd78b42c0 Add an L modifier like P, W, S to loop over clients. Also fix some long
lines in tmux(1).
2023-02-07 10:21:01 +00:00
Thomas Adam
4a0126f7fb Merge branch 'obsd-master' 2023-02-06 12:01:12 +00:00
nicm
7acc8d703d Add -f to list-clients like the other list commands, from Andy Walker in
GitHub issue 3449.
2023-02-06 09:20:30 +00:00
Thomas Adam
77118f3a9f portable: remove vis.h
This is included portably across different systems.
2023-02-06 01:55:02 +00:00
Thomas Adam
e25926d3c4 Merge branch 'obsd-master' 2023-02-06 00:01:11 +00:00
nicm
0cb75f1332 Do not allow multiple line separators in a row. 2023-02-05 21:26:48 +00:00
nicm
93b1b78150 Extend display-message to work for control clients. GitHub issue 3449. 2023-02-05 21:15:32 +00:00
Thomas Adam
493922dc4b Merge branch 'obsd-master' 2023-02-02 12:01:11 +00:00
nicm
f10854cfc5 Add a missing error message which causes an invalid layout name to crash. 2023-02-02 09:24:59 +00:00
nicm
993e7a937f Tweak note for D key binding, from Clark Wang. 2023-02-02 09:06:44 +00:00
Thomas Adam
f5af3cfb21 Merge branch 'obsd-master' 2023-01-23 12:01:11 +00:00
nicm
e7e112fbd0 Too many \s in example, GitHub issue 3445. 2023-01-23 09:33:51 +00:00
Thomas Adam
c42087c789 Merge branch 'obsd-master' 2023-01-21 00:01:11 +00:00
nicm
3aa458ea63 Add a flag to display-menu to select the manu item chosen first, GitHub
issue 3442.
2023-01-20 21:36:00 +00:00
Thomas Adam
c4a6f403bb Merge branch 'obsd-master' 2023-01-17 12:01:09 +00:00
nicm
9789ea3fb4 Support -1 without -N for list-keys. 2023-01-17 10:40:51 +00:00
Thomas Adam
f416ae1c12 Merge branch 'obsd-master' 2023-01-17 10:01:09 +00:00
nicm
d578cf8d3f Update palette when moving a pane, GitHub issue 3437. 2023-01-17 06:50:55 +00:00
Thomas Adam
789cb91f31 Merge branch 'obsd-master' 2023-01-16 14:01:10 +00:00
nicm
eb1f8d70a7 Mark keys sent by command and skip paste handling for them. 2023-01-16 11:26:14 +00:00
Thomas Adam
42895efac3 Merge branch 'obsd-master' 2023-01-12 22:01:09 +00:00
nicm
483cc77c1c Have tmux recognise pasted texts wrapped in bracket paste sequences,
rather than only forwarding them to the program inside. From Andrew
Onyshchuk in GitHub issue 3431.
2023-01-12 18:49:11 +00:00
Thomas Adam
9b1ea8b16d Merge branch 'obsd-master' 2023-01-09 16:01:11 +00:00
nicm
b41892622d Fix behaviour with \007 (used the wrong tree for last change). 2023-01-09 14:12:41 +00:00
Thomas Adam
9051220243 Merge branch 'obsd-master' 2023-01-09 10:01:09 +00:00
nicm
c0031f8b85 Accept \007 as terminator to OSC 10 or 11. 2023-01-09 07:57:14 +00:00
Thomas Adam
c1a30ed995 Merge branch 'obsd-master' 2023-01-09 02:01:10 +00:00
nicm
565de3f54b Fix parsing of optional arguments so that and accept a - starting an
argument.
2023-01-08 23:34:46 +00:00
Thomas Adam
153ae758c9 portable: fixup merge with utf8.c 2023-01-08 23:27:54 +00:00
Thomas Adam
5086377f30 Merge branch 'obsd-master' 2023-01-08 23:26:09 +00:00
nicm
7c0789d2d2 Have client return 1 if process is interrupted to an input pane. 2023-01-08 22:17:04 +00:00
Nicholas Marriott
2a32565e0c Restore code to handle wcwidth failure so that unknown codepoints still
do the most likely right thing. GitHub issue 3427, patch based on an
diff from Jesse Luehrs in GitHub issue 3003.
2023-01-08 22:15:38 +00:00
nicm
7ced0a03d2 Restore code to handle wcwidth failure so that unknown codepoints still
do the most likely right thing. GitHub issue 3427, patch based on an
diff from Jesse Luehrs in GitHub issue 3003.
2023-01-08 22:15:30 +00:00
nicm
cb51942669 Quotes are now required in select-layout example. 2023-01-08 21:00:01 +00:00
Nicholas Marriott
093fb53773 Missing #endif. 2023-01-06 11:38:41 +00:00
Thomas Adam
21e00e4635 Merge branch 'obsd-master' 2023-01-06 09:02:00 +00:00
nicm
09afc6c8ee If a pane is killed, cancel reading from the file. GitHub issue 3422. 2023-01-06 07:09:27 +00:00
nicm
a41a927441 Query the client terminal for foreground and background colours and if
OSC 10 or 11 is received but no colour has been set inside tmux, return
the colour from the first attached client (probably most people will
have all light or or all dark terminals).
2023-01-03 11:43:24 +00:00
jmc
3fe01ff09c spelling fixes; from paul tagliamonte
amendments to his diff are noted on tech
2022-12-26 19:16:03 +00:00
kn
b5ab4d2c13 Denote multiple arguments with 'arg ...' not 'args'
A few programs used the plural in their synopsis which doesn't read as
clear as the obvious triple-dot notation.

mdoc(7) .Ar defaults to "file ..." if no arguments are given and consistent
use of 'arg ...' matches that behaviour.

Cleanup a few markups of the same argument so the text keeps reading
naturally;  omit unhelpful parts like 'if optional arguments are given,
they are passed along' for tools like time(1) and timeout(1) that obviously
execute commands with whatever arguments where given -- just like doas(1)
which doesn't mention arguments in its DESCRIPTION in the first place.

For expr(1) the difference between 'expressions' and 'expression ...' is
crucial, as arguments must be passed as individual words.

Feedback millert jmc schwarze deraadt
OK jmc
2022-12-22 19:53:23 +00:00
nicm
4d79d463ef Allow send-keys without a client again, reported by Stefan Hagen. 2022-12-19 07:30:10 +00:00
nicm
7cb48fc40b Do not escape tabs in output (iTerm2 needs them). GitHub issue 3414. 2022-12-16 08:22:05 +00:00
nicm
8bd17bff49 Make U+FE0F VARIATION SELECTOR-16 change the width from 1 to 2. GitHub
issue 3409.
2022-12-16 08:19:58 +00:00
nicm
3b3f42053a Add send-keys -K to handle keys directly as if typed (so look up in key
table). GitHub issue 3361.
2022-12-16 08:13:40 +00:00
Nicholas Marriott
70ff8cfe1e No vis.h in portable. 2022-12-07 12:30:36 +00:00
Thomas Adam
6249a4b866 Merge branch 'obsd-master' 2022-12-07 12:01:09 +00:00
nicm
7e497c7f23 Process escape sequences in show-buffer, GitHub issue 3401. 2022-12-07 09:44:44 +00:00
Thomas Adam
1536b7e206 Merge branch 'obsd-master' 2022-11-11 10:01:09 +00:00
Nicholas Marriott
e46d0632a5 Add key regression tests from Aaron Jensen. 2022-11-11 08:47:55 +00:00
nicm
20da167377 Tweak previous to set and log the feature instead of just setting the
flag.
2022-11-11 08:44:11 +00:00
nicm
fe475bd856 Parse primary device attributes as well as secondary and add a SIXEL
flag (not used yet), from Anindya Mukherjee.
2022-11-11 08:37:55 +00:00
nicm
079f48e8a6 Document alternative delimiters for substitution, from Jim Wisniewski. 2022-11-11 08:27:17 +00:00
Thomas Adam
aaa043a20f Merge branch 'obsd-master' 2022-11-11 02:01:10 +00:00
jmc
48f41e4a41 - sort options; from josiah frentsos
ok nicm

- add -N to SYNOPSIS

- sort usage()
2022-11-10 22:58:39 +00:00
Thomas Adam
6fb80527f3 Merge branch 'obsd-master' 2022-11-08 12:01:11 +00:00
nicm
f86eba2129 Fix C-S-Tab without extended keys, from Aaron Jensen. 2022-11-08 10:04:31 +00:00
Thomas Adam
50f4e0fac9 Merge branch 'obsd-master' 2022-11-04 10:01:10 +00:00
nicm
77c135349a Unescape the string for the literal operator (l:) so special characters
work.
2022-11-04 08:03:23 +00:00
Thomas Adam
c449512be4 Merge branch 'obsd-master' 2022-11-03 10:01:11 +00:00
nicm
17290b9121 If there are no buffers, reset mode as soon as any key pressed. Fixes
crash reported by Gaoyang Zhang in GitHub issue 3373.
2022-11-03 08:41:53 +00:00
nicm
3be369522b Add a -l flag to display-message to disable format expansion, from Aaron
Jensen. GitHub issue 3372.
2022-11-03 08:33:57 +00:00
Thomas Adam
dbfbd8a195 Merge branch 'obsd-master' 2022-11-02 08:01:11 +00:00
nicm
9614f51560 Instead of always setting the extended flag, set it only when searching.
Allows send-keys to work. From Aaron Jensen.
2022-11-02 07:36:07 +00:00
Thomas Adam
36896f6dd0 Merge branch 'obsd-master' 2022-11-01 12:01:10 +00:00
nicm
2d08235987 Add modified Tab key sequences, from Aaron Jensen, GitHub issue 3368. 2022-11-01 09:54:13 +00:00
nicm
2291045116 Use active pane in target window not current window for +/-. GitHub
issue 3370.
2022-11-01 09:46:14 +00:00
Thomas Adam
9dd1f442c5 Merge branch 'obsd-master' 2022-10-28 16:01:10 +01:00
nicm
8edece2cdb Add paste-buffer-deleted notification and fix name of paste-buffer-changed. 2022-10-28 13:00:02 +00:00
Thomas Adam
d001a94d7b Merge branch 'obsd-master' 2022-10-25 22:01:10 +01:00
nicm
c2580cfe24 Initialize context before testing it. 2022-10-25 17:53:31 +00:00
Thomas Adam
dafd6f462f Merge branch 'obsd-master' 2022-10-25 12:01:10 +01:00
nicm
2111142cf1 Fix a memory leak, from Japin Li in GitHub issue 3358. 2022-10-25 09:12:05 +00:00
nicm
0fc961b22e Do not fire redraw callback if NULL. 2022-10-25 09:04:49 +00:00
Nicholas Marriott
5ce34add77 Do not attempt to connect to the socket as a client if systemd is active, from
Julien Moutinho in GitHub issue 3345.
2022-10-18 15:58:06 +01:00
Thomas Adam
934f357149 Merge branch 'obsd-master' 2022-10-17 14:01:10 +01:00
nicm
ff2766b024 Preserve marked pane when renumbering windows. 2022-10-17 10:59:42 +00:00
Thomas Adam
9ef854f5a9 Merge branch 'obsd-master' 2022-09-28 12:01:09 +01:00
nicm
a10452be2d Add scroll-top and scroll-bottom commands to scroll so cursor is at top
or bottom. From Anindya Mukherjee, GitHub issue 3334.
2022-09-28 07:59:50 +00:00
nicm
9cc8e40aa0 Add a -T flag to capture-pane to stop at the last used cell instead of
the full width. Restore the previous behaviour by making it default to
off unless -J is used (the only time it matters). Fixes mosh unit tests;
GitHub issue 3339.
2022-09-28 07:55:29 +00:00
Thomas Adam
f49f92737f Merge branch 'obsd-master' 2022-09-22 17:10:38 +01:00
Nicholas Marriott
19344ec890 Add headers and fix type, from Marvin Schmidt. GitHub issue 3332. 2022-09-19 07:03:17 +01:00
nicm
a2cc601c3d Don't use options from pane if pane is NULL. 2022-09-12 12:02:17 +00:00
nicm
9ab1ba36cd Use correct option name. 2022-09-10 17:01:33 +00:00
nicm
f03c3ca6c3 Add message-line option to control where message and prompt go, from
Varun Kumar E in GitHub issue 3324.
2022-09-09 11:02:23 +00:00
Nicholas Marriott
0a0ded3268 Regress typos. 2022-09-07 07:28:26 +01:00
Thomas Adam
6da520c5a1 Merge branch 'obsd-master' 2022-08-31 12:01:10 +01:00
nicm
68dc9af9ac Fix window size report, from Vincent Bernat. 2022-08-31 08:07:05 +00:00
Thomas Adam
f7b30ed3d4 Merge branch 'obsd-master' 2022-08-24 10:01:13 +01:00
nicm
e867528209 Check for NULL returns from bufferevent_new. 2022-08-24 07:22:30 +00:00
Thomas Adam
038dfb27a8 Merge branch 'obsd-master' 2022-08-23 12:01:10 +01:00
nicm
416c27c995 Add scroll-middle copy mode command to make cursor line in the middle,
from Varun Kumar E in GitHub issue 3307.
2022-08-23 08:14:19 +00:00
Nicholas Marriott
19344efa78 Fix fallback implementaion of getpeereid, from Pino Toscano. 2022-08-22 08:21:42 +01:00
Thomas Adam
9c34aad21c Merge branch 'obsd-master' 2022-08-15 13:54:47 +01:00
nicm
7c2dcd7238 Notify when a paste buffer is deleted, GitHub issue 3302 from George
Nachman.
2022-08-15 09:10:34 +00:00
nicm
03149bf7f6 Add a Nobr terminfo capability to tell tmux the terminal does not use
bright colours for bold (makes a difference to how tmux applies palette
differences). From Damien Tardy-Panis in GitHub issue 3301.
2022-08-15 08:54:03 +00:00
nicm
497021d0db Add some const, from Markus F X J Oberhumer. 2022-08-15 08:41:13 +00:00
nicm
cfdc5b62ad Don't stop at first match when updating environment. 2022-08-15 08:37:03 +00:00
Thomas Adam
9b08e5139b Merge branch 'obsd-master' 2022-08-11 12:01:10 +01:00
nicm
e139f977b1 vi(1) Home/End bindings, from Markus F X J Oberhumer. 2022-08-11 09:11:26 +00:00
Thomas Adam
9abf5d9fe5 Merge branch 'obsd-master' 2022-08-10 18:01:11 +01:00
nicm
273577ba0a Fix check of home directory (&& not ||), from Markus F X J Oberhumer,
GitHub issue 3297.
2022-08-10 14:03:59 +00:00
Thomas Adam
e15058e60f Merge branch 'obsd-master' 2022-08-04 16:01:09 +01:00
nicm
de5cd54124 Change g and G to go to top and bottom of menu, GitHub issue 3286. 2022-08-04 12:06:09 +00:00
Thomas Adam
c6cf09450a Merge branch 'obsd-master' 2022-08-03 16:01:11 +01:00
nicm
c6e7568471 Do not crash when searching for .* with extremely long lines. Reported
by Torbjorn Lonnemark, GitHub issue 3272.
2022-08-03 13:27:48 +00:00
Thomas Adam
7b8ececd8d Merge branch 'obsd-master' 2022-08-02 14:01:09 +01:00
nicm
42ba6c1b22 Add a third state "all" to allow-passthrough to work even in invisible
panes, from Sergei Grechanik in GitHub issue 3274.
2022-08-02 11:09:26 +00:00
Thomas Adam
9a2fdf8fd4 Merge branch 'obsd-master' 2022-08-02 12:01:09 +01:00
Nicholas Marriott
00812c9053 Check for $YACC, from Wei Shih in GitHub issue 3267. 2022-08-02 11:52:09 +01:00
nicm
33c59100ae Fix validation of missing percentage arguments. 2022-08-02 09:23:34 +00:00
nicm
36d904011a -u is no longer equivalent to -TUTF-8 so don't say it is. 2022-08-02 08:57:01 +00:00
Thomas Adam
9d9445a48e Merge branch 'obsd-master' 2022-07-22 10:01:10 +01:00
nicm
a8da24771c Clear marks when the search string changes. From Anindya Mukherjee,
GitHub issue 3255.
2022-07-22 07:14:07 +00:00
Thomas Adam
ab1d18d00f Merge branch 'obsd-master' 2022-07-19 10:01:08 +01:00
nicm
ee431d482a Do not ignore the "off" flag when checking if a pane should be stopped,
GitHub issue 3250.
2022-07-19 07:10:13 +00:00
Nicholas Marriott
e06c09889c Add permissions for workflow, GitHub issue 3202. 2022-07-19 07:54:11 +01:00
nicm
86dfbda0e4 Process modifiers as bits rather than using a switch, from Koichi Murase. 2022-07-19 06:51:31 +00:00
Nicholas Marriott
697cebb4c1 Include curses properly for hyperlinks ifdef, from chrysn at fsfe dot org. 2022-07-19 07:48:48 +01:00
nicm
3c65475561 Fix memory leak, from Gabriel Souza Franco. 2022-07-19 06:46:57 +00:00
Thomas Adam
dc6bc0e95a Merge branch 'obsd-master' 2022-07-06 12:01:09 +01:00
Thomas Adam
b130e951cc Merge branch 'obsd-master' 2022-07-06 10:01:10 +01:00
Nicholas Marriott
9e19f132f2 Errors are now displayed on attach so use control mode to test
instead.
2022-07-06 09:54:53 +01:00
nicm
1afe22086f Show config errors on attach if they were not shown when the session
was created.
2022-07-06 08:40:52 +00:00
Nicholas Marriott
8e8b9865d1 Add hyperlink test, from Jeff Chiang. 2022-07-06 09:33:30 +01:00
nicm
a39827a85c Remove debugging code. 2022-07-06 08:32:28 +00:00
nicm
9e03df5500 Defer reading from control client until the command line command has
completed.
2022-07-06 08:31:59 +00:00
nicm
dd602eaa61 Mention whether time is creation/activity for sort orders. 2022-07-06 07:51:37 +00:00
nicm
d0d2c39dec Support hyperlinks with capture-pane -e and add a mouse_hyperlink
format, GitHub issue 3247 from Jeff Chiang.
2022-07-06 07:36:36 +00:00
Thomas Adam
57fec74966 Merge branch 'obsd-master' 2022-07-04 12:01:10 +01:00
nicm
9360e0ef32 Sort panes by index not by ID, GitHub issue 3249. 2022-07-04 08:39:45 +00:00
Nicholas Marriott
f08c019d41 Do not set Hls for hyperlinks on ncurses older than 5.9 (for example macOS). 2022-06-30 16:46:26 +01:00
Thomas Adam
c3af8f6b16 hyperlinks: remove vis.h
Not used on Linux.
2022-06-30 16:44:43 +01:00
Thomas Adam
01c4919f5f Merge branch 'obsd-master' 2022-06-30 16:37:18 +01:00
nicm
cdacc12ce3 Add support for OSC 8 hyperlinks (a VTE extension now supported by other
terminals such as iTerm2). Originally written by me then extended and
completed by first Will Noble and later Jeff Chiang. GitHub issues 911,
2621, 2890, 3240.
2022-06-30 09:55:53 +00:00
Thomas Adam
d8c527a5f9 Merge branch 'obsd-master' 2022-06-27 12:01:09 +01:00
nicm
b22edcf3a5 Tweak previous - find end of style correctly. 2022-06-27 09:16:54 +00:00
nicm
786cff8db9 Do not expand single character format aliases inside #[] since they
interfere with colours. GitHub issue 3239 from Magnus Gross.
2022-06-27 09:14:49 +00:00
Thomas Adam
b63afaea61 Merge branch 'obsd-master' 2022-06-21 12:01:09 +01:00
nicm
9c89f7c2af Store time lines are scrolled into history and display in copy mode. 2022-06-21 09:30:01 +00:00
Thomas Adam
d46870ede5 Merge branch 'obsd-master' 2022-06-20 12:01:09 +01:00
nicm
a888ce9963 Do not display configuration file errors in a pane when in control mode,
instead report them with a %config-error notification. GitHub issue 3193.
2022-06-20 07:59:37 +00:00
Thomas Adam
8ff3091d16 Merge branch 'obsd-master' 2022-06-17 10:01:10 +01:00
nicm
d9f84854ac Check cursor options when a pane is created, not just when they are changed. 2022-06-17 07:28:05 +00:00
Thomas Adam
89fe2680a9 Merge branch 'obsd-master' 2022-06-16 16:01:08 +01:00
nicm
7cee982f90 Keep cursor on selected item on menu (useful for blind people), GitHub
issue 3225.
2022-06-16 13:27:39 +00:00
Nicholas Marriott
42358cc521 Typos from Bastian Venthur. 2022-06-15 08:01:50 +01:00
Thomas Adam
06869ff22f Merge branch 'obsd-master' 2022-06-14 10:01:08 +01:00
nicm
616bde08ac kf* terminfo capabilities are poorly defined and rxvt uses them in a
different way from xterm, so add a feature flag for rxvt to make tmux
ignore the capabilities and instead rely on its builtin definitions.
2022-06-14 07:29:00 +00:00
Thomas Adam
6d0828b81c Merge branch 'obsd-master' 2022-06-11 20:01:09 +01:00
nicm
42ddf02ffc Fix size of flags output buffer. 2022-06-11 16:59:33 +00:00
Thomas Adam
56390e0a39 Merge branch 'obsd-master' 2022-06-10 16:01:11 +01:00
nicm
18a5835aff Ignore OSC if the first argument is not properly terminated. 2022-06-10 11:55:30 +00:00
Nicholas Marriott
67960dcc9a Merge tag '3.3a'
3.3a
2022-06-09 13:07:18 +01:00
Thomas Adam
810daefdd1 Merge branch 'obsd-master' 2022-06-09 12:01:09 +01:00
nicm
ccc9dc3bb4 If an application gives the first parameter to OSC 52, validate and pass
on to outside terminal. GitHub issue 3192.
2022-06-09 09:12:55 +00:00
Thomas Adam
be2eb57d62 Merge branch 'obsd-master' 2022-06-07 14:01:09 +01:00
nicm
c07d582e24 Expand arguments to some commands where it makes sense, GitHub issue
3204 from Anindya Mukherjee.
2022-06-07 10:02:19 +00:00
Thomas Adam
afb3a5fe71 Merge branch 'obsd-master' 2022-06-04 10:01:09 +01:00
nicm
020c403dff When picking a buffer because one isn't specified by the user, ignore
named buffers. GitHub issue 3212 from David le Blanc.
2022-06-04 07:42:07 +00:00
Thomas Adam
e77e11ec6b Merge branch 'obsd-master' 2022-06-03 12:01:09 +01:00
nicm
3edda3c5e7 Do not unintentionally turn off all mouse mode when button is also present. 2022-06-03 08:09:16 +00:00
Thomas Adam
1184dc08d4 Merge branch 'obsd-master' 2022-06-03 00:01:08 +01:00
nicm
18838fbc87 Do not attempt to use client in config file (it will be NULL), GitHub
issue 3206.
2022-06-02 21:19:32 +00:00
Nicholas Marriott
be2617036f Remove extra definition of getpeereid. From Eric N Vander Weele in GitHub issue
3209.
2022-06-02 21:45:53 +01:00
nicm
0f6227f46b When deleting or renaming a buffer and a buffer name is specified,
complain if the buffer doesn't exist instead of silently deleting or
renaming the most recent buffer. GitHub issue 3205.
2022-06-02 20:41:21 +00:00
Thomas Adam
c1ac007576 Merge branch 'obsd-master' 2022-06-01 18:01:08 +01:00
nicm
201a8d8e7e If escape-time is 0, force to 1 instead - not waiting at all is asking
for problems on some platforms.
2022-06-01 15:43:22 +00:00
Nicholas Marriott
b566cd57bf Now back to 3.4. 2022-06-01 08:50:54 +01:00
113 changed files with 7820 additions and 1631 deletions

View File

@@ -4,8 +4,14 @@ on:
schedule: schedule:
- cron: '0 0 * * *' - cron: '0 0 * * *'
permissions:
contents: read
jobs: jobs:
lock: lock:
permissions:
issues: write # for dessant/lock-threads to lock issues
pull-requests: write # for dessant/lock-threads to lock PRs
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: dessant/lock-threads@v2 - uses: dessant/lock-threads@v2

115
CHANGES
View File

@@ -1,3 +1,116 @@
CHANGES FROM 3.3a to 3.4
* Add options keep-last and keep-group to destroy-unattached to keep the last
session whether in a group.
* Don't allow paste-buffer into dead panes.
* Add -t to source-file.
* Rewrite combined character handling to be more consistent and to support
newer Unicode combined characters.
* Add basic support for SIXEL if built with --enable-sixel.
* Add a session, pane and user mouse range types for the status line and add
format variables for mouse_status_line and mouse_status_range so they can be
associated with different commands in the key bindings.
* Add flag (-o) to next-prompt/previous-prompt to go to OSC 133 command output.
* Add options and flags for menu styles (menu-style, menu-border-style) similar
to those existing for popups.
* Add support for marking lines with a shell prompt based on the OSC 133 extension.
* Check for libterminfo for NetBSD.
* Add "us" to styles for underscore colour.
* Add flags (-c and -y) to change the confirm key and default behaviour of
confirm-before.
* Use ncurses' new tparm_s function (added in 6.4-20230424) instead of tparm so
it does not object to string arguments in c apabilities it doesn't already
know. Also ignore errors from tparm if using previous ncurses versions.
* Set default lock command to vlock on Linux if present at build time.
* Discard mouse sequences that have the right form but actually are invalid.
* Add support for spawning panes in separate cgroups with systemd and a
configure flag (--disable-cgroups) to turn off.
* Add a format (pane_unseen_changes) to show if there are unseen changes while
in a mode.
* Remove old buffer when renaming rather than complaining.
* Add an L modifier like P, W, S to loop over clients.
* Add -f to list-clients like the other list commands.
* Extend display-message to work for control clients.
* Add a flag to display-menu to select the manu item selected when the menu is
open.
* Have tmux recognise pasted text wrapped in bracket paste sequences, rather
than only forwarding them to the program inside.
* Have client return 1 if process is interrupted to an input pane.
* Query the client terminal for foreground and background colours and if OSC 10
or 11 is received but no colour has been set inside tmux, return the colour
from the first attached client.
* Add send-keys -K to handle keys directly as if typed (so look up in key
table).
* Process escape sequences in show-buffer.
* Add a -l flag to display-message to disable format expansion.
* Add paste-buffer-deleted notification and fix name of paste-buffer-changed.
* Do not attempt to connect to the socket as a client if systemd is active.
* Add scroll-top and scroll-bottom commands to scroll so cursor is at top or
bottom.
* Add a -T flag to capture-pane to stop at the last used cell instead of the
full width. Restore the previous behaviour by making it default to off unless
-J is used.
* Add message-line option to control where message and prompt go.
* Notification when a when a paste buffer is deleted.
* Add a Nobr terminfo(5) capability to tell tmux the terminal does not use bright
colours for bold.
* Change g and G to go to top and bottom in menus.
* Add a third state "all" to allow-passthrough to work even in invisible panes.
* Add support for OSC 8 hyperlinks.
* Store the time lines are scrolled into history and display in copy mode.
* Add a %config-error reply to control mode for configuration file errors since
reporting them in view mode is useless.
* A new feature flag (ignorefkeys) to ignore terminfo(5) function key
definitions for rxvt.
* Pass through first argument to OSC 52 (which clipboards to set) if the
application provides it.
* Expand arguments to send-keys, capture-pane, split-window, join-pane where it
makes sense to do so.
* Ignore named buffers when choosing a buffer if one is not specified by the user.
CHANGES FROM 3.3 TO 3.3a CHANGES FROM 3.3 TO 3.3a
* Do not crash when run-shell produces output from a config file. * Do not crash when run-shell produces output from a config file.
@@ -1302,7 +1415,7 @@ Incompatible Changes
bind -Tcopy-mode C-r command-prompt -i -p'search up' "send -X search-backward-incremental '%%'" bind -Tcopy-mode C-r command-prompt -i -p'search up' "send -X search-backward-incremental '%%'"
There are also some new commmands available with send -X, such as There are also some new commands available with send -X, such as
copy-pipe-and-cancel. copy-pipe-and-cancel.
* set-remain-on-exit has gone -- can be achieved with hooks instead. * set-remain-on-exit has gone -- can be achieved with hooks instead.
* Hooks: before hooks have been removed and only a selection of commands now * Hooks: before hooks have been removed and only a selection of commands now

View File

@@ -1,5 +1,3 @@
# Makefile.am
# Obvious program stuff. # Obvious program stuff.
bin_PROGRAMS = tmux bin_PROGRAMS = tmux
CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c
@@ -14,6 +12,7 @@ dist_EXTRA_tmux_SOURCES = compat/*.[ch]
AM_CPPFLAGS += @XOPEN_DEFINES@ \ AM_CPPFLAGS += @XOPEN_DEFINES@ \
-DTMUX_VERSION='"@VERSION@"' \ -DTMUX_VERSION='"@VERSION@"' \
-DTMUX_CONF='"$(sysconfdir)/tmux.conf:~/.tmux.conf:$$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"' \ -DTMUX_CONF='"$(sysconfdir)/tmux.conf:~/.tmux.conf:$$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"' \
-DTMUX_LOCK_CMD='"@DEFAULT_LOCK_CMD@"' \
-DTMUX_TERM='"@DEFAULT_TERM@"' -DTMUX_TERM='"@DEFAULT_TERM@"'
# Additional object files. # Additional object files.
@@ -150,6 +149,7 @@ dist_tmux_SOURCES = \
grid-reader.c \ grid-reader.c \
grid-view.c \ grid-view.c \
grid.c \ grid.c \
hyperlinks.c \
input-keys.c \ input-keys.c \
input.c \ input.c \
job.c \ job.c \
@@ -189,6 +189,7 @@ dist_tmux_SOURCES = \
tty-keys.c \ tty-keys.c \
tty-term.c \ tty-term.c \
tty.c \ tty.c \
utf8-combined.c \
utf8.c \ utf8.c \
window-buffer.c \ window-buffer.c \
window-client.c \ window-client.c \
@@ -216,6 +217,11 @@ if HAVE_UTF8PROC
nodist_tmux_SOURCES += compat/utf8proc.c nodist_tmux_SOURCES += compat/utf8proc.c
endif endif
# Enable sixel support.
if ENABLE_SIXEL
dist_tmux_SOURCES += image.c image-sixel.c
endif
if NEED_FUZZING if NEED_FUZZING
check_PROGRAMS = fuzz/input-fuzzer check_PROGRAMS = fuzz/input-fuzzer
fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS) fuzz_input_fuzzer_LDFLAGS = $(FUZZING_LIBS)

View File

@@ -37,6 +37,10 @@ struct args_entry {
u_char flag; u_char flag;
struct args_values values; struct args_values values;
u_int count; u_int count;
int flags;
#define ARGS_ENTRY_OPTIONAL_VALUE 0x1
RB_ENTRY(args_entry) entry; RB_ENTRY(args_entry) entry;
}; };
@@ -94,6 +98,22 @@ args_copy_value(struct args_value *to, struct args_value *from)
} }
} }
/* Type to string. */
static const char *
args_type_to_string (enum args_type type)
{
switch (type)
{
case ARGS_NONE:
return "NONE";
case ARGS_STRING:
return "STRING";
case ARGS_COMMANDS:
return "COMMANDS";
}
return "INVALID";
}
/* Get value as string. */ /* Get value as string. */
static const char * static const char *
args_value_as_string(struct args_value *value) args_value_as_string(struct args_value *value)
@@ -122,6 +142,99 @@ args_create(void)
return (args); return (args);
} }
/* Parse a single flag. */
static int
args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
struct args *args, u_int *i, const char *string, int flag,
int optional_argument)
{
struct args_value *argument, *new;
const char *s;
new = xcalloc(1, sizeof *new);
if (*string != '\0') {
new->type = ARGS_STRING;
new->string = xstrdup(string);
goto out;
}
if (*i == count)
argument = NULL;
else {
argument = &values[*i];
if (argument->type != ARGS_STRING) {
xasprintf(cause, "-%c argument must be a string", flag);
return (-1);
}
}
if (argument == NULL) {
if (optional_argument) {
log_debug("%s: -%c (optional)", __func__, flag);
args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
return (0); /* either - or end */
}
xasprintf(cause, "-%c expects an argument", flag);
return (-1);
}
args_copy_value(new, argument);
(*i)++;
out:
s = args_value_as_string(new);
log_debug("%s: -%c = %s", __func__, flag, s);
args_set(args, flag, new, 0);
return (0);
}
/* Parse flags argument. */
static int
args_parse_flags(const struct args_parse *parse, struct args_value *values,
u_int count, char **cause, struct args *args, u_int *i)
{
struct args_value *value;
u_char flag;
const char *found, *string;
int optional_argument;
value = &values[*i];
if (value->type != ARGS_STRING)
return (1);
string = value->string;
log_debug("%s: next %s", __func__, string);
if (*string++ != '-' || *string == '\0')
return (1);
(*i)++;
if (string[0] == '-' && string[1] == '\0')
return (1);
for (;;) {
flag = *string++;
if (flag == '\0')
return (0);
if (flag == '?')
return (-1);
if (!isalnum(flag)) {
xasprintf(cause, "invalid flag -%c", flag);
return (-1);
}
found = strchr(parse->template, flag);
if (found == NULL) {
xasprintf(cause, "unknown flag -%c", flag);
return (-1);
}
if (found[1] != ':') {
log_debug("%s: -%c", __func__, flag);
args_set(args, flag, NULL, 0);
continue;
}
optional_argument = (found[2] == ':');
return (args_parse_flag_argument(values, count, cause, args, i,
string, flag, optional_argument));
}
}
/* Parse arguments into a new argument set. */ /* Parse arguments into a new argument set. */
struct args * struct args *
args_parse(const struct args_parse *parse, struct args_value *values, args_parse(const struct args_parse *parse, struct args_value *values,
@@ -131,86 +244,21 @@ args_parse(const struct args_parse *parse, struct args_value *values,
u_int i; u_int i;
enum args_parse_type type; enum args_parse_type type;
struct args_value *value, *new; struct args_value *value, *new;
u_char flag; const char *s;
const char *found, *string, *s; int stop;
int optional_argument;
if (count == 0) if (count == 0)
return (args_create()); return (args_create());
args = args_create(); args = args_create();
for (i = 1; i < count; /* nothing */) { for (i = 1; i < count; /* nothing */) {
value = &values[i]; stop = args_parse_flags(parse, values, count, cause, args, &i);
if (value->type != ARGS_STRING) if (stop == -1) {
break; args_free(args);
return (NULL);
string = value->string;
if (*string++ != '-' || *string == '\0')
break;
i++;
if (string[0] == '-' && string[1] == '\0')
break;
for (;;) {
flag = *string++;
if (flag == '\0')
break;
if (flag == '?') {
args_free(args);
return (NULL);
}
if (!isalnum(flag)) {
xasprintf(cause, "invalid flag -%c", flag);
args_free(args);
return (NULL);
}
found = strchr(parse->template, flag);
if (found == NULL) {
xasprintf(cause, "unknown flag -%c", flag);
args_free(args);
return (NULL);
}
if (*++found != ':') {
log_debug("%s: -%c", __func__, flag);
args_set(args, flag, NULL);
continue;
}
if (*found == ':') {
optional_argument = 1;
found++;
}
new = xcalloc(1, sizeof *new);
if (*string != '\0') {
new->type = ARGS_STRING;
new->string = xstrdup(string);
} else {
if (i == count) {
if (optional_argument) {
log_debug("%s: -%c", __func__,
flag);
args_set(args, flag, NULL);
continue;
}
xasprintf(cause,
"-%c expects an argument",
flag);
args_free(args);
return (NULL);
}
if (values[i].type != ARGS_STRING) {
xasprintf(cause,
"-%c argument must be a string",
flag);
args_free(args);
return (NULL);
}
args_copy_value(new, &values[i++]);
}
s = args_value_as_string(new);
log_debug("%s: -%c = %s", __func__, flag, s);
args_set(args, flag, new);
break;
} }
if (stop == 1)
break;
} }
log_debug("%s: flags end at %u of %u", __func__, i, count); log_debug("%s: flags end at %u of %u", __func__, i, count);
if (i != count) { if (i != count) {
@@ -218,8 +266,8 @@ args_parse(const struct args_parse *parse, struct args_value *values,
value = &values[i]; value = &values[i];
s = args_value_as_string(value); s = args_value_as_string(value);
log_debug("%s: %u = %s (type %d)", __func__, i, s, log_debug("%s: %u = %s (type %s)", __func__, i, s,
value->type); args_type_to_string (value->type));
if (parse->cb != NULL) { if (parse->cb != NULL) {
type = parse->cb(args, args->count, cause); type = parse->cb(args, args->count, cause);
@@ -323,13 +371,13 @@ args_copy(struct args *args, int argc, char **argv)
RB_FOREACH(entry, args_tree, &args->tree) { RB_FOREACH(entry, args_tree, &args->tree) {
if (TAILQ_EMPTY(&entry->values)) { if (TAILQ_EMPTY(&entry->values)) {
for (i = 0; i < entry->count; i++) for (i = 0; i < entry->count; i++)
args_set(new_args, entry->flag, NULL); args_set(new_args, entry->flag, NULL, 0);
continue; continue;
} }
TAILQ_FOREACH(value, &entry->values, entry) { TAILQ_FOREACH(value, &entry->values, entry) {
new_value = xcalloc(1, sizeof *new_value); new_value = xcalloc(1, sizeof *new_value);
args_copy_copy_value(new_value, value, argc, argv); args_copy_copy_value(new_value, value, argc, argv);
args_set(new_args, entry->flag, new_value); args_set(new_args, entry->flag, new_value, 0);
} }
} }
if (args->count == 0) if (args->count == 0)
@@ -487,6 +535,7 @@ args_print(struct args *args)
char *buf; char *buf;
u_int i, j; u_int i, j;
struct args_entry *entry; struct args_entry *entry;
struct args_entry *last = NULL;
struct args_value *value; struct args_value *value;
len = 1; len = 1;
@@ -494,6 +543,8 @@ args_print(struct args *args)
/* Process the flags first. */ /* Process the flags first. */
RB_FOREACH(entry, args_tree, &args->tree) { RB_FOREACH(entry, args_tree, &args->tree) {
if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE)
continue;
if (!TAILQ_EMPTY(&entry->values)) if (!TAILQ_EMPTY(&entry->values))
continue; continue;
@@ -505,6 +556,16 @@ args_print(struct args *args)
/* Then the flags with arguments. */ /* Then the flags with arguments. */
RB_FOREACH(entry, args_tree, &args->tree) { RB_FOREACH(entry, args_tree, &args->tree) {
if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) {
if (*buf != '\0')
args_print_add(&buf, &len, " -%c", entry->flag);
else
args_print_add(&buf, &len, "-%c", entry->flag);
last = entry;
continue;
}
if (TAILQ_EMPTY(&entry->values))
continue;
TAILQ_FOREACH(value, &entry->values, entry) { TAILQ_FOREACH(value, &entry->values, entry) {
if (*buf != '\0') if (*buf != '\0')
args_print_add(&buf, &len, " -%c", entry->flag); args_print_add(&buf, &len, " -%c", entry->flag);
@@ -512,7 +573,10 @@ args_print(struct args *args)
args_print_add(&buf, &len, "-%c", entry->flag); args_print_add(&buf, &len, "-%c", entry->flag);
args_print_add_value(&buf, &len, value); args_print_add_value(&buf, &len, value);
} }
last = entry;
} }
if (last && (last->flags & ARGS_ENTRY_OPTIONAL_VALUE))
args_print_add(&buf, &len, " --");
/* And finally the argument vector. */ /* And finally the argument vector. */
for (i = 0; i < args->count; i++) for (i = 0; i < args->count; i++)
@@ -582,7 +646,7 @@ args_has(struct args *args, u_char flag)
/* Set argument value in the arguments tree. */ /* Set argument value in the arguments tree. */
void void
args_set(struct args *args, u_char flag, struct args_value *value) args_set(struct args *args, u_char flag, struct args_value *value, int flags)
{ {
struct args_entry *entry; struct args_entry *entry;
@@ -591,6 +655,7 @@ args_set(struct args *args, u_char flag, struct args_value *value)
entry = xcalloc(1, sizeof *entry); entry = xcalloc(1, sizeof *entry);
entry->flag = flag; entry->flag = flag;
entry->count = 1; entry->count = 1;
entry->flags = flags;
TAILQ_INIT(&entry->values); TAILQ_INIT(&entry->values);
RB_INSERT(args_tree, &args->tree, entry); RB_INSERT(args_tree, &args->tree, entry);
} else } else
@@ -696,6 +761,7 @@ args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
struct args_value *value; struct args_value *value;
struct args_command_state *state; struct args_command_state *state;
const char *cmd; const char *cmd;
const char *file;
state = xcalloc(1, sizeof *state); state = xcalloc(1, sizeof *state);
@@ -722,7 +788,9 @@ args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
if (wait) if (wait)
state->pi.item = item; state->pi.item = item;
cmd_get_source(self, &state->pi.file, &state->pi.line); cmd_get_source(self, &file, &state->pi.line);
if (file != NULL)
state->pi.file = xstrdup(file);
state->pi.c = tc; state->pi.c = tc;
if (state->pi.c != NULL) if (state->pi.c != NULL)
state->pi.c->references++; state->pi.c->references++;
@@ -747,6 +815,8 @@ args_make_commands(struct args_command_state *state, int argc, char **argv,
} }
cmd = xstrdup(state->cmd); cmd = xstrdup(state->cmd);
log_debug("%s: %s", __func__, cmd);
cmd_log_argv(argc, argv, __func__);
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
new_cmd = cmd_template_replace(cmd, argv[i], i + 1); new_cmd = cmd_template_replace(cmd, argv[i], i + 1);
log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd); log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd);
@@ -775,6 +845,7 @@ args_make_commands_free(struct args_command_state *state)
cmd_list_free(state->cmdlist); cmd_list_free(state->cmdlist);
if (state->pi.c != NULL) if (state->pi.c != NULL)
server_client_unref(state->pi.c); server_client_unref(state->pi.c);
free((void *)state->pi.file);
free(state->cmd); free(state->cmd);
free(state); free(state);
} }
@@ -848,6 +919,41 @@ args_strtonum(struct args *args, u_char flag, long long minval,
return (ll); return (ll);
} }
/* Convert an argument value to a number, and expand formats. */
long long
args_strtonum_and_expand(struct args *args, u_char flag, long long minval,
long long maxval, struct cmdq_item *item, char **cause)
{
const char *errstr;
char *formatted;
long long ll;
struct args_entry *entry;
struct args_value *value;
if ((entry = args_find(args, flag)) == NULL) {
*cause = xstrdup("missing");
return (0);
}
value = TAILQ_LAST(&entry->values, args_values);
if (value == NULL ||
value->type != ARGS_STRING ||
value->string == NULL) {
*cause = xstrdup("missing");
return (0);
}
formatted = format_single_from_target(item, value->string);
ll = strtonum(formatted, minval, maxval, &errstr);
free(formatted);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);
}
*cause = NULL;
return (ll);
}
/* Convert an argument to a number which may be a percentage. */ /* Convert an argument to a number which may be a percentage. */
long long long long
args_percentage(struct args *args, u_char flag, long long minval, args_percentage(struct args *args, u_char flag, long long minval,
@@ -860,6 +966,10 @@ args_percentage(struct args *args, u_char flag, long long minval,
*cause = xstrdup("missing"); *cause = xstrdup("missing");
return (0); return (0);
} }
if (TAILQ_EMPTY(&entry->values)) {
*cause = xstrdup("empty");
return (0);
}
value = TAILQ_LAST(&entry->values, args_values)->string; value = TAILQ_LAST(&entry->values, args_values)->string;
return (args_string_percentage(value, minval, maxval, curval, cause)); return (args_string_percentage(value, minval, maxval, curval, cause));
} }
@@ -874,6 +984,10 @@ args_string_percentage(const char *value, long long minval, long long maxval,
size_t valuelen = strlen(value); size_t valuelen = strlen(value);
char *copy; char *copy;
if (valuelen == 0) {
*cause = xstrdup("empty");
return (0);
}
if (value[valuelen - 1] == '%') { if (value[valuelen - 1] == '%') {
copy = xstrdup(value); copy = xstrdup(value);
copy[valuelen - 1] = '\0'; copy[valuelen - 1] = '\0';
@@ -904,3 +1018,74 @@ args_string_percentage(const char *value, long long minval, long long maxval,
*cause = NULL; *cause = NULL;
return (ll); return (ll);
} }
/*
* Convert an argument to a number which may be a percentage, and expand
* formats.
*/
long long
args_percentage_and_expand(struct args *args, u_char flag, long long minval,
long long maxval, long long curval, struct cmdq_item *item, char **cause)
{
const char *value;
struct args_entry *entry;
if ((entry = args_find(args, flag)) == NULL) {
*cause = xstrdup("missing");
return (0);
}
if (TAILQ_EMPTY(&entry->values)) {
*cause = xstrdup("empty");
return (0);
}
value = TAILQ_LAST(&entry->values, args_values)->string;
return (args_string_percentage_and_expand(value, minval, maxval, curval,
item, cause));
}
/*
* Convert a string to a number which may be a percentage, and expand formats.
*/
long long
args_string_percentage_and_expand(const char *value, long long minval,
long long maxval, long long curval, struct cmdq_item *item, char **cause)
{
const char *errstr;
long long ll;
size_t valuelen = strlen(value);
char *copy, *f;
if (value[valuelen - 1] == '%') {
copy = xstrdup(value);
copy[valuelen - 1] = '\0';
f = format_single_from_target(item, copy);
ll = strtonum(f, 0, 100, &errstr);
free(f);
free(copy);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);
}
ll = (curval * ll) / 100;
if (ll < minval) {
*cause = xstrdup("too small");
return (0);
}
if (ll > maxval) {
*cause = xstrdup("too large");
return (0);
}
} else {
f = format_single_from_target(item, value);
ll = strtonum(f, minval, maxval, &errstr);
free(f);
if (errstr != NULL) {
*cause = xstrdup(errstr);
return (0);
}
}
*cause = NULL;
return (ll);
}

47
cfg.c
View File

@@ -51,8 +51,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
cfg_finished = 1; cfg_finished = 1;
if (!RB_EMPTY(&sessions)) cfg_show_causes(NULL);
cfg_show_causes(RB_MIN(sessions, &sessions));
if (cfg_item != NULL) if (cfg_item != NULL)
cmdq_continue(cfg_item); cmdq_continue(cfg_item);
@@ -67,6 +66,7 @@ start_cfg(void)
{ {
struct client *c; struct client *c;
u_int i; u_int i;
int flags = 0;
/* /*
* Configuration files are loaded without a client, so commands are run * Configuration files are loaded without a client, so commands are run
@@ -84,19 +84,17 @@ start_cfg(void)
cmdq_append(c, cfg_item); cmdq_append(c, cfg_item);
} }
for (i = 0; i < cfg_nfiles; i++) { if (cfg_quiet)
if (cfg_quiet) flags = CMD_PARSE_QUIET;
load_cfg(cfg_files[i], c, NULL, CMD_PARSE_QUIET, NULL); for (i = 0; i < cfg_nfiles; i++)
else load_cfg(cfg_files[i], c, NULL, NULL, flags, NULL);
load_cfg(cfg_files[i], c, NULL, 0, NULL);
}
cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL)); cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
} }
int int
load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, load_cfg(const char *path, struct client *c, struct cmdq_item *item,
struct cmdq_item **new_item) struct cmd_find_state *current, int flags, struct cmdq_item **new_item)
{ {
FILE *f; FILE *f;
struct cmd_parse_input pi; struct cmd_parse_input pi;
@@ -135,7 +133,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
} }
if (item != NULL) if (item != NULL)
state = cmdq_copy_state(cmdq_get_state(item)); state = cmdq_copy_state(cmdq_get_state(item), current);
else else
state = cmdq_new_state(NULL, NULL, 0); state = cmdq_new_state(NULL, NULL, 0);
cmdq_add_format(state, "current_file", "%s", pi.file); cmdq_add_format(state, "current_file", "%s", pi.file);
@@ -155,8 +153,8 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
int int
load_cfg_from_buffer(const void *buf, size_t len, const char *path, load_cfg_from_buffer(const void *buf, size_t len, const char *path,
struct client *c, struct cmdq_item *item, int flags, struct client *c, struct cmdq_item *item, struct cmd_find_state *current,
struct cmdq_item **new_item) int flags, struct cmdq_item **new_item)
{ {
struct cmd_parse_input pi; struct cmd_parse_input pi;
struct cmd_parse_result *pr; struct cmd_parse_result *pr;
@@ -187,7 +185,7 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path,
} }
if (item != NULL) if (item != NULL)
state = cmdq_copy_state(cmdq_get_state(item)); state = cmdq_copy_state(cmdq_get_state(item), current);
else else
state = cmdq_new_state(NULL, NULL, 0); state = cmdq_new_state(NULL, NULL, 0);
cmdq_add_format(state, "current_file", "%s", pi.file); cmdq_add_format(state, "current_file", "%s", pi.file);
@@ -238,11 +236,29 @@ cfg_print_causes(struct cmdq_item *item)
void void
cfg_show_causes(struct session *s) cfg_show_causes(struct session *s)
{ {
struct client *c = TAILQ_FIRST(&clients);
struct window_pane *wp; struct window_pane *wp;
struct window_mode_entry *wme; struct window_mode_entry *wme;
u_int i; u_int i;
if (s == NULL || cfg_ncauses == 0) if (cfg_ncauses == 0)
return;
if (c != NULL && (c->flags & CLIENT_CONTROL)) {
for (i = 0; i < cfg_ncauses; i++) {
control_write(c, "%%config-error %s", cfg_causes[i]);
free(cfg_causes[i]);
}
goto out;
}
if (s == NULL) {
if (c != NULL && c->session != NULL)
s = c->session;
else
s = RB_MIN(sessions, &sessions);
}
if (s == NULL || s->attached == 0) /* wait for an attached session */
return; return;
wp = s->curw->window->active; wp = s->curw->window->active;
@@ -254,6 +270,7 @@ cfg_show_causes(struct session *s)
free(cfg_causes[i]); free(cfg_causes[i]);
} }
out:
free(cfg_causes); free(cfg_causes);
cfg_causes = NULL; cfg_causes = NULL;
cfg_ncauses = 0; cfg_ncauses = 0;

View File

@@ -245,9 +245,6 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
u_int ncaps = 0; u_int ncaps = 0;
struct args_value *values; struct args_value *values;
/* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */
signal(SIGCHLD, SIG_IGN);
/* Set up the initial command. */ /* Set up the initial command. */
if (shell_command != NULL) { if (shell_command != NULL) {
msg = MSG_SHELL; msg = MSG_SHELL;
@@ -284,6 +281,12 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
log_debug("flags are %#llx", (unsigned long long)client_flags); log_debug("flags are %#llx", (unsigned long long)client_flags);
/* Initialize the client socket and start the server. */ /* Initialize the client socket and start the server. */
#ifdef HAVE_SYSTEMD
if (systemd_activated()) {
/* socket-based activation, do not even try to be a client. */
fd = server_start(client_proc, flags, base, 0, NULL);
} else
#endif
fd = client_connect(base, socket_path, client_flags); fd = client_connect(base, socket_path, client_flags);
if (fd == -1) { if (fd == -1) {
if (errno == ECONNREFUSED) { if (errno == ECONNREFUSED) {
@@ -527,11 +530,22 @@ client_signal(int sig)
{ {
struct sigaction sigact; struct sigaction sigact;
int status; int status;
pid_t pid;
log_debug("%s: %s", __func__, strsignal(sig)); log_debug("%s: %s", __func__, strsignal(sig));
if (sig == SIGCHLD) if (sig == SIGCHLD) {
waitpid(WAIT_ANY, &status, WNOHANG); for (;;) {
else if (!client_attached) { pid = waitpid(WAIT_ANY, &status, WNOHANG);
if (pid == 0)
break;
if (pid == -1) {
if (errno == ECHILD)
break;
log_debug("waitpid failed: %s",
strerror(errno));
}
}
} else if (!client_attached) {
if (sig == SIGTERM || sig == SIGHUP) if (sig == SIGTERM || sig == SIGHUP)
proc_exit(client_proc); proc_exit(client_proc);
} else { } else {
@@ -694,6 +708,9 @@ client_dispatch_wait(struct imsg *imsg)
!(client_flags & CLIENT_CONTROL), client_file_check_cb, !(client_flags & CLIENT_CONTROL), client_file_check_cb,
NULL); NULL);
break; break;
case MSG_READ_CANCEL:
file_read_cancel(&client_files, imsg);
break;
case MSG_WRITE_OPEN: case MSG_WRITE_OPEN:
file_write_open(&client_files, client_peer, imsg, 1, file_write_open(&client_files, client_peer, imsg, 1,
!(client_flags & CLIENT_CONTROL), client_file_check_cb, !(client_flags & CLIENT_CONTROL), client_file_check_cb,

View File

@@ -158,6 +158,9 @@ cmd_attach_session(struct cmdq_item *item, const char *tflag, int dflag,
c->flags |= CLIENT_ATTACHED; c->flags |= CLIENT_ATTACHED;
} }
if (cfg_finished)
cfg_show_causes(s);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -115,6 +115,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
layout_init(w, wp); layout_init(w, wp);
wp->flags |= PANE_CHANGED; wp->flags |= PANE_CHANGED;
colour_palette_from_option(&wp->palette, wp->options);
if (idx == -1) if (idx == -1)
idx = -1 - options_get_number(dst_s->options, "base-index"); idx = -1 - options_get_number(dst_s->options, "base-index");

View File

@@ -39,8 +39,8 @@ const struct cmd_entry cmd_capture_pane_entry = {
.name = "capture-pane", .name = "capture-pane",
.alias = "capturep", .alias = "capturep",
.args = { "ab:CeE:JNpPqS:t:", 0, 0, NULL }, .args = { "ab:CeE:JNpPqS:Tt:", 0, 0, NULL },
.usage = "[-aCeJNpPq] " CMD_BUFFER_USAGE " [-E end-line] " .usage = "[-aCeJNpPqT] " CMD_BUFFER_USAGE " [-E end-line] "
"[-S start-line] " CMD_TARGET_PANE_USAGE, "[-S start-line] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -53,8 +53,8 @@ const struct cmd_entry cmd_clear_history_entry = {
.name = "clear-history", .name = "clear-history",
.alias = "clearhist", .alias = "clearhist",
.args = { "t:", 0, 0, NULL }, .args = { "Ht:", 0, 0, NULL },
.usage = CMD_TARGET_PANE_USAGE, .usage = "[-H] " CMD_TARGET_PANE_USAGE,
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -110,7 +110,7 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
struct grid *gd; struct grid *gd;
const struct grid_line *gl; const struct grid_line *gl;
struct grid_cell *gc = NULL; struct grid_cell *gc = NULL;
int n, with_codes, escape_c0, join_lines, no_trim; int n, join_lines, flags = 0;
u_int i, sx, top, bottom, tmp; u_int i, sx, top, bottom, tmp;
char *cause, *buf, *line; char *cause, *buf, *line;
const char *Sflag, *Eflag; const char *Sflag, *Eflag;
@@ -133,7 +133,8 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
if (Sflag != NULL && strcmp(Sflag, "-") == 0) if (Sflag != NULL && strcmp(Sflag, "-") == 0)
top = 0; top = 0;
else { else {
n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause); n = args_strtonum_and_expand(args, 'S', INT_MIN, SHRT_MAX,
item, &cause);
if (cause != NULL) { if (cause != NULL) {
top = gd->hsize; top = gd->hsize;
free(cause); free(cause);
@@ -149,7 +150,8 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
if (Eflag != NULL && strcmp(Eflag, "-") == 0) if (Eflag != NULL && strcmp(Eflag, "-") == 0)
bottom = gd->hsize + gd->sy - 1; bottom = gd->hsize + gd->sy - 1;
else { else {
n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause); n = args_strtonum_and_expand(args, 'E', INT_MIN, SHRT_MAX,
item, &cause);
if (cause != NULL) { if (cause != NULL) {
bottom = gd->hsize + gd->sy - 1; bottom = gd->hsize + gd->sy - 1;
free(cause); free(cause);
@@ -167,15 +169,19 @@ cmd_capture_pane_history(struct args *args, struct cmdq_item *item,
top = tmp; top = tmp;
} }
with_codes = args_has(args, 'e');
escape_c0 = args_has(args, 'C');
join_lines = args_has(args, 'J'); join_lines = args_has(args, 'J');
no_trim = args_has(args, 'N'); if (args_has(args, 'e'))
flags |= GRID_STRING_WITH_SEQUENCES;
if (args_has(args, 'C'))
flags |= GRID_STRING_ESCAPE_SEQUENCES;
if (!join_lines && !args_has(args, 'T'))
flags |= GRID_STRING_EMPTY_CELLS;
if (!join_lines && !args_has(args, 'N'))
flags |= GRID_STRING_TRIM_SPACES;
buf = NULL; buf = NULL;
for (i = top; i <= bottom; i++) { for (i = top; i <= bottom; i++) {
line = grid_string_cells(gd, 0, i, sx, &gc, with_codes, line = grid_string_cells(gd, 0, i, sx, &gc, flags, wp->screen);
escape_c0, !join_lines && !no_trim);
linelen = strlen(line); linelen = strlen(line);
buf = cmd_capture_pane_append(buf, len, line, linelen); buf = cmd_capture_pane_append(buf, len, line, linelen);
@@ -202,6 +208,8 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
if (cmd_get_entry(self) == &cmd_clear_history_entry) { if (cmd_get_entry(self) == &cmd_clear_history_entry) {
window_pane_reset_mode_all(wp); window_pane_reset_mode_all(wp);
grid_clear_history(wp->base.grid); grid_clear_history(wp->base.grid);
if (args_has(args, 'H'))
screen_reset_hyperlinks(wp->screen);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -100,7 +100,7 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
const struct window_mode *mode; const struct window_mode *mode;
if (cmd_get_entry(self) == &cmd_choose_buffer_entry) { if (cmd_get_entry(self) == &cmd_choose_buffer_entry) {
if (paste_get_top(NULL) == NULL) if (paste_is_empty())
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
mode = &window_buffer_mode; mode = &window_buffer_mode;
} else if (cmd_get_entry(self) == &cmd_choose_client_entry) { } else if (cmd_get_entry(self) == &cmd_choose_client_entry) {

View File

@@ -179,10 +179,10 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
if (s == NULL) if (s == NULL)
goto out; goto out;
if (done) { if (done) {
if (cdata->flags & PROMPT_INCREMENTAL) if (cdata->flags & PROMPT_INCREMENTAL)
goto out; goto out;
cmd_append_argv(&cdata->argc, &cdata->argv, s); cmd_append_argv(&cdata->argc, &cdata->argv, s);
if (++cdata->current != cdata->count) { if (++cdata->current != cdata->count) {
prompt = &cdata->prompts[cdata->current]; prompt = &cdata->prompts[cdata->current];
@@ -193,8 +193,11 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
argc = cdata->argc; argc = cdata->argc;
argv = cmd_copy_argv(cdata->argc, cdata->argv); argv = cmd_copy_argv(cdata->argc, cdata->argv);
cmd_append_argv(&argc, &argv, s); if (!done)
cmd_append_argv(&argc, &argv, s);
if (done) { if (done) {
cmd_free_argv(cdata->argc, cdata->argv);
cdata->argc = argc; cdata->argc = argc;
cdata->argv = cmd_copy_argv(argc, argv); cdata->argv = cmd_copy_argv(argc, argv);
} }

View File

@@ -41,8 +41,9 @@ const struct cmd_entry cmd_confirm_before_entry = {
.name = "confirm-before", .name = "confirm-before",
.alias = "confirm", .alias = "confirm",
.args = { "bp:t:", 1, 1, cmd_confirm_before_args_parse }, .args = { "bc:p:t:y", 1, 1, cmd_confirm_before_args_parse },
.usage = "[-b] [-p prompt] " CMD_TARGET_CLIENT_USAGE " command", .usage = "[-by] [-c confirm_key] [-p prompt] " CMD_TARGET_CLIENT_USAGE
" command",
.flags = CMD_CLIENT_TFLAG, .flags = CMD_CLIENT_TFLAG,
.exec = cmd_confirm_before_exec .exec = cmd_confirm_before_exec
@@ -51,6 +52,8 @@ const struct cmd_entry cmd_confirm_before_entry = {
struct cmd_confirm_before_data { struct cmd_confirm_before_data {
struct cmdq_item *item; struct cmdq_item *item;
struct cmd_list *cmdlist; struct cmd_list *cmdlist;
u_char confirm_key;
int default_yes;
}; };
static enum args_parse_type static enum args_parse_type
@@ -68,7 +71,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
char *new_prompt; char *new_prompt;
const char *prompt, *cmd; const char *confirm_key, *prompt, *cmd;
int wait = !args_has(args, 'b'); int wait = !args_has(args, 'b');
cdata = xcalloc(1, sizeof *cdata); cdata = xcalloc(1, sizeof *cdata);
@@ -79,11 +82,26 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
if (wait) if (wait)
cdata->item = item; cdata->item = item;
cdata->default_yes = args_has(args, 'y');
if ((confirm_key = args_get(args, 'c')) != NULL) {
if (confirm_key[1] == '\0' &&
confirm_key[0] > 31 &&
confirm_key[0] < 127)
cdata->confirm_key = confirm_key[0];
else {
cmdq_error(item, "invalid confirm key");
return (CMD_RETURN_ERROR);
}
}
else
cdata->confirm_key = 'y';
if ((prompt = args_get(args, 'p')) != NULL) if ((prompt = args_get(args, 'p')) != NULL)
xasprintf(&new_prompt, "%s ", prompt); xasprintf(&new_prompt, "%s ", prompt);
else { else {
cmd = cmd_get_entry(cmd_list_first(cdata->cmdlist))->name; cmd = cmd_get_entry(cmd_list_first(cdata->cmdlist))->name;
xasprintf(&new_prompt, "Confirm '%s'? (y/n) ", cmd); xasprintf(&new_prompt, "Confirm '%s'? (%c/n) ",
cmd, cdata->confirm_key);
} }
status_prompt_set(tc, target, new_prompt, NULL, status_prompt_set(tc, target, new_prompt, NULL,
@@ -107,9 +125,9 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s,
if (c->flags & CLIENT_DEAD) if (c->flags & CLIENT_DEAD)
goto out; goto out;
if (s == NULL || *s == '\0') if (s == NULL)
goto out; goto out;
if (tolower((u_char)s[0]) != 'y' || s[1] != '\0') if (s[0] != cdata->confirm_key && (s[0] != '\0' || !cdata->default_yes))
goto out; goto out;
retcode = 0; retcode = 0;
@@ -123,12 +141,12 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s,
} }
out: out:
if (item != NULL) { if (item != NULL) {
if (cmdq_get_client(item) != NULL && if (cmdq_get_client(item) != NULL &&
cmdq_get_client(item)->session == NULL) cmdq_get_client(item)->session == NULL)
cmdq_get_client(item)->retval = retcode; cmdq_get_client(item)->retval = retcode;
cmdq_continue(item); cmdq_continue(item);
} }
return (0); return (0);
} }

View File

@@ -38,8 +38,10 @@ const struct cmd_entry cmd_display_menu_entry = {
.name = "display-menu", .name = "display-menu",
.alias = "menu", .alias = "menu",
.args = { "c:t:OT:x:y:", 1, -1, cmd_display_menu_args_parse }, .args = { "b:c:C:H:s:S:Ot:T:x:y:", 1, -1, cmd_display_menu_args_parse },
.usage = "[-O] [-c target-client] " CMD_TARGET_PANE_USAGE " [-T title] " .usage = "[-O] [-b border-lines] [-c target-client] "
"[-C starting-choice] [-H selected-style] [-s style] "
"[-S border-style] " CMD_TARGET_PANE_USAGE "[-T title] "
"[-x position] [-y position] name key command ...", "[-x position] [-y position] name key command ...",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -274,6 +276,7 @@ cmd_display_menu_get_position(struct client *tc, struct cmdq_item *item,
log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h); log_debug("%s: -y: %s = %s = %u (-h %u)", __func__, yp, p, *py, h);
free(p); free(p);
format_free(ft);
return (1); return (1);
} }
@@ -286,19 +289,41 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
struct menu *menu = NULL; struct menu *menu = NULL;
struct menu_item menu_item; struct menu_item menu_item;
const char *key, *name; const char *key, *name, *value;
char *title; const char *style = args_get(args, 's');
int flags = 0; const char *border_style = args_get(args, 'S');
const char *selected_style = args_get(args, 'H');
enum box_lines lines = BOX_LINES_DEFAULT;
char *title, *cause;
int flags = 0, starting_choice = 0;
u_int px, py, i, count = args_count(args); u_int px, py, i, count = args_count(args);
struct options *o = target->s->curw->window->options;
struct options_entry *oe;
if (tc->overlay_draw != NULL) if (tc->overlay_draw != NULL)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
if (args_has(args, 'C')) {
if (strcmp(args_get(args, 'C'), "-") == 0)
starting_choice = -1;
else {
starting_choice = args_strtonum(args, 'C', 0, UINT_MAX,
&cause);
if (cause != NULL) {
cmdq_error(item, "starting choice %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
}
if (args_has(args, 'T')) if (args_has(args, 'T'))
title = format_single_from_target(item, args_get(args, 'T')); title = format_single_from_target(item, args_get(args, 'T'));
else else
title = xstrdup(""); title = xstrdup("");
menu = menu_create(title); menu = menu_create(title);
free(title);
for (i = 0; i != count; /* nothing */) { for (i = 0; i != count; /* nothing */) {
name = args_string(args, i++); name = args_string(args, i++);
@@ -309,7 +334,6 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
if (count - i < 2) { if (count - i < 2) {
cmdq_error(item, "not enough arguments"); cmdq_error(item, "not enough arguments");
free(title);
menu_free(menu); menu_free(menu);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
@@ -321,7 +345,6 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
menu_add_item(menu, &menu_item, item, tc, target); menu_add_item(menu, &menu_item, item, tc, target);
} }
free(title);
if (menu == NULL) { if (menu == NULL) {
cmdq_error(item, "invalid menu arguments"); cmdq_error(item, "invalid menu arguments");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
@@ -336,12 +359,24 @@ cmd_display_menu_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
value = args_get(args, 'b');
if (value != NULL) {
oe = options_get(o, "menu-border-lines");
lines = options_find_choice(options_table_entry(oe), value,
&cause);
if (lines == -1) {
cmdq_error(item, "menu-border-lines %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
if (args_has(args, 'O')) if (args_has(args, 'O'))
flags |= MENU_STAYOPEN; flags |= MENU_STAYOPEN;
if (!event->m.valid) if (!event->m.valid)
flags |= MENU_NOMOUSE; flags |= MENU_NOMOUSE;
if (menu_display(menu, flags, item, px, py, tc, target, NULL, if (menu_display(menu, flags, starting_choice, item, px, py, tc, lines,
NULL) != 0) style, selected_style, border_style, target, NULL, NULL) != 0)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
return (CMD_RETURN_WAIT); return (CMD_RETURN_WAIT);
} }
@@ -454,11 +489,13 @@ cmd_display_popup_exec(struct cmd *self, struct cmdq_item *item)
cmd_free_argv(argc, argv); cmd_free_argv(argc, argv);
if (env != NULL) if (env != NULL)
environ_free(env); environ_free(env);
free(cwd);
free(title); free(title);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (env != NULL) if (env != NULL)
environ_free(env); environ_free(env);
free(cwd);
free(title); free(title);
cmd_free_argv(argc, argv); cmd_free_argv(argc, argv);
return (CMD_RETURN_WAIT); return (CMD_RETURN_WAIT);

View File

@@ -39,8 +39,8 @@ const struct cmd_entry cmd_display_message_entry = {
.name = "display-message", .name = "display-message",
.alias = "display", .alias = "display",
.args = { "ac:d:INpt:F:v", 0, 1, NULL }, .args = { "ac:d:lINpt:F:v", 0, 1, NULL },
.usage = "[-aINpv] [-c target-client] [-d delay] [-F format] " .usage = "[-aIlNpv] [-c target-client] [-d delay] [-F format] "
CMD_TARGET_PANE_USAGE " [message]", CMD_TARGET_PANE_USAGE " [message]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -68,9 +68,10 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
struct window_pane *wp = target->wp; struct window_pane *wp = target->wp;
const char *template; const char *template;
char *msg, *cause; char *msg, *cause;
int delay = -1, flags; int delay = -1, flags, Nflag = args_has(args, 'N');
struct format_tree *ft; struct format_tree *ft;
u_int count = args_count(args); u_int count = args_count(args);
struct evbuffer *evb;
if (args_has(args, 'I')) { if (args_has(args, 'I')) {
if (wp == NULL) if (wp == NULL)
@@ -132,15 +133,24 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
msg = format_expand_time(ft, template); if (args_has(args, 'l'))
msg = xstrdup(template);
else
msg = format_expand_time(ft, template);
if (cmdq_get_client(item) == NULL) if (cmdq_get_client(item) == NULL)
cmdq_error(item, "%s", msg); cmdq_error(item, "%s", msg);
else if (args_has(args, 'p')) else if (args_has(args, 'p'))
cmdq_print(item, "%s", msg); cmdq_print(item, "%s", msg);
else if (tc != NULL) { else if (tc != NULL && (tc->flags & CLIENT_CONTROL)) {
status_message_set(tc, delay, 0, args_has(args, 'N'), "%s", evb = evbuffer_new();
msg); if (evb == NULL)
} fatalx("out of memory");
evbuffer_add_printf(evb, "%%message %s", msg);
server_client_print(tc, 0, evb);
evbuffer_free(evb);
} else if (tc != NULL)
status_message_set(tc, delay, 0, Nflag, "%s", msg);
free(msg); free(msg);
format_free(ft); format_free(ft);

View File

@@ -144,7 +144,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
llen = 0; llen = 0;
if (sx < len * 6 || sy < 5) { if (sx < len * 6 || sy < 5) {
tty_attributes(tty, &fgc, &grid_default_cell, NULL); tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
if (sx >= len + llen + 1) { if (sx >= len + llen + 1) {
len += llen + 1; len += llen + 1;
tty_cursor(tty, xoff + px - len / 2, yoff + py); tty_cursor(tty, xoff + px - len / 2, yoff + py);
@@ -161,7 +161,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
px -= len * 3; px -= len * 3;
py -= 2; py -= 2;
tty_attributes(tty, &bgc, &grid_default_cell, NULL); tty_attributes(tty, &bgc, &grid_default_cell, NULL, NULL);
for (ptr = buf; *ptr != '\0'; ptr++) { for (ptr = buf; *ptr != '\0'; ptr++) {
if (*ptr < '0' || *ptr > '9') if (*ptr < '0' || *ptr > '9')
continue; continue;
@@ -179,7 +179,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
if (sy <= 6) if (sy <= 6)
goto out; goto out;
tty_attributes(tty, &fgc, &grid_default_cell, NULL); tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
if (rlen != 0 && sx >= rlen) { if (rlen != 0 && sx >= rlen) {
tty_cursor(tty, xoff + sx - rlen, yoff); tty_cursor(tty, xoff + sx - rlen, yoff);
tty_putn(tty, rbuf, rlen, rlen); tty_putn(tty, rbuf, rlen, rlen);

View File

@@ -48,6 +48,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct window_pane *wp = target->wp; struct window_pane *wp = target->wp;
const char *s = args_string(args, 0), *suffix = ""; const char *s = args_string(args, 0), *suffix = "";
const char *star = "*";
struct args_value *filter; struct args_value *filter;
int C, N, T; int C, N, T;
@@ -55,6 +56,8 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
N = args_has(args, 'N'); N = args_has(args, 'N');
T = args_has(args, 'T'); T = args_has(args, 'T');
if (args_has(args, 'r'))
star = "";
if (args_has(args, 'r') && args_has(args, 'i')) if (args_has(args, 'r') && args_has(args, 'i'))
suffix = "/ri"; suffix = "/ri";
else if (args_has(args, 'r')) else if (args_has(args, 'r'))
@@ -71,40 +74,40 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
if (C && N && T) { if (C && N && T) {
xasprintf(&filter->string, xasprintf(&filter->string,
"#{||:" "#{||:"
"#{C%s:%s},#{||:#{m%s:*%s*,#{window_name}}," "#{C%s:%s},#{||:#{m%s:%s%s%s,#{window_name}},"
"#{m%s:*%s*,#{pane_title}}}}", "#{m%s:%s%s%s,#{pane_title}}}}",
suffix, s, suffix, s, suffix, s); suffix, s, suffix, star, s, star, suffix, star, s, star);
} else if (C && N) { } else if (C && N) {
xasprintf(&filter->string, xasprintf(&filter->string,
"#{||:#{C%s:%s},#{m%s:*%s*,#{window_name}}}", "#{||:#{C%s:%s},#{m%s:%s%s%s,#{window_name}}}",
suffix, s, suffix, s); suffix, s, suffix, star, s, star);
} else if (C && T) { } else if (C && T) {
xasprintf(&filter->string, xasprintf(&filter->string,
"#{||:#{C%s:%s},#{m%s:*%s*,#{pane_title}}}", "#{||:#{C%s:%s},#{m%s:%s%s%s,#{pane_title}}}",
suffix, s, suffix, s); suffix, s, suffix, star, s, star);
} else if (N && T) { } else if (N && T) {
xasprintf(&filter->string, xasprintf(&filter->string,
"#{||:#{m%s:*%s*,#{window_name}}," "#{||:#{m%s:%s%s%s,#{window_name}},"
"#{m%s:*%s*,#{pane_title}}}", "#{m%s:%s%s%s,#{pane_title}}}",
suffix, s, suffix, s); suffix, star, s, star, suffix, star, s, star);
} else if (C) { } else if (C) {
xasprintf(&filter->string, xasprintf(&filter->string,
"#{C%s:%s}", "#{C%s:%s}",
suffix, s); suffix, s);
} else if (N) { } else if (N) {
xasprintf(&filter->string, xasprintf(&filter->string,
"#{m%s:*%s*,#{window_name}}", "#{m%s:%s%s%s,#{window_name}}",
suffix, s); suffix, star, s, star);
} else { } else {
xasprintf(&filter->string, xasprintf(&filter->string,
"#{m%s:*%s*,#{pane_title}}", "#{m%s:%s%s%s,#{pane_title}}",
suffix, s); suffix, star, s, star);
} }
new_args = args_create(); new_args = args_create();
if (args_has(args, 'Z')) if (args_has(args, 'Z'))
args_set(new_args, 'Z', NULL); args_set(new_args, 'Z', NULL, 0);
args_set(new_args, 'f', filter); args_set(new_args, 'f', filter, 0);
window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args); window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args);
args_free(new_args); args_free(new_args);

View File

@@ -582,27 +582,27 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
/* Try special characters. */ /* Try special characters. */
if (strcmp(pane, "!") == 0) { if (strcmp(pane, "!") == 0) {
fs->wp = fs->w->last; fs->wp = TAILQ_FIRST(&fs->w->last_panes);
if (fs->wp == NULL) if (fs->wp == NULL)
return (-1); return (-1);
return (0); return (0);
} else if (strcmp(pane, "{up-of}") == 0) { } else if (strcmp(pane, "{up-of}") == 0) {
fs->wp = window_pane_find_up(fs->current->wp); fs->wp = window_pane_find_up(fs->w->active);
if (fs->wp == NULL) if (fs->wp == NULL)
return (-1); return (-1);
return (0); return (0);
} else if (strcmp(pane, "{down-of}") == 0) { } else if (strcmp(pane, "{down-of}") == 0) {
fs->wp = window_pane_find_down(fs->current->wp); fs->wp = window_pane_find_down(fs->w->active);
if (fs->wp == NULL) if (fs->wp == NULL)
return (-1); return (-1);
return (0); return (0);
} else if (strcmp(pane, "{left-of}") == 0) { } else if (strcmp(pane, "{left-of}") == 0) {
fs->wp = window_pane_find_left(fs->current->wp); fs->wp = window_pane_find_left(fs->w->active);
if (fs->wp == NULL) if (fs->wp == NULL)
return (-1); return (-1);
return (0); return (0);
} else if (strcmp(pane, "{right-of}") == 0) { } else if (strcmp(pane, "{right-of}") == 0) {
fs->wp = window_pane_find_right(fs->current->wp); fs->wp = window_pane_find_right(fs->w->active);
if (fs->wp == NULL) if (fs->wp == NULL)
return (-1); return (-1);
return (0); return (0);
@@ -614,7 +614,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
n = strtonum(pane + 1, 1, INT_MAX, NULL); n = strtonum(pane + 1, 1, INT_MAX, NULL);
else else
n = 1; n = 1;
wp = fs->current->wp; wp = fs->w->active;
if (pane[0] == '+') if (pane[0] == '+')
fs->wp = window_pane_next_by_number(fs->w, wp, n); fs->wp = window_pane_next_by_number(fs->w, wp, n);
else else

View File

@@ -71,10 +71,11 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
struct window *src_w, *dst_w; struct window *src_w, *dst_w;
struct window_pane *src_wp, *dst_wp; struct window_pane *src_wp, *dst_wp;
char *cause = NULL; char *cause = NULL;
int size, percentage, dst_idx; int size, dst_idx;
int flags; int flags;
enum layout_type type; enum layout_type type;
struct layout_cell *lc; struct layout_cell *lc;
u_int curval = 0;
dst_s = target->s; dst_s = target->s;
dst_wl = target->wl; dst_wl = target->wl;
@@ -97,23 +98,30 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'h')) if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT; 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; size = -1;
if (args_has(args, 'l')) { if (args_has(args, 'l')) {
if (type == LAYOUT_TOPBOTTOM) { size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
size = args_percentage(args, 'l', 0, INT_MAX, item, &cause);
dst_wp->sy, &cause);
} else {
size = args_percentage(args, 'l', 0, INT_MAX,
dst_wp->sx, &cause);
}
} else if (args_has(args, 'p')) { } else if (args_has(args, 'p')) {
percentage = args_strtonum(args, 'p', 0, 100, &cause); size = args_strtonum_and_expand(args, 'l', 0, 100, item,
if (cause == NULL) { &cause);
if (type == LAYOUT_TOPBOTTOM) if (cause == NULL)
size = (dst_wp->sy * percentage) / 100; size = curval * size / 100;
else
size = (dst_wp->sx * percentage) / 100;
}
} }
if (cause != NULL) { if (cause != NULL) {
cmdq_error(item, "size %s", cause); cmdq_error(item, "size %s", cause);
@@ -147,6 +155,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item)
else else
TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry); TAILQ_INSERT_AFTER(&dst_w->panes, dst_wp, src_wp, entry);
layout_assign_pane(lc, src_wp, 0); layout_assign_pane(lc, src_wp, 0);
colour_palette_from_option(&src_wp->palette, src_wp->options);
recalculate_sizes(); recalculate_sizes();

View File

@@ -41,8 +41,8 @@ const struct cmd_entry cmd_list_clients_entry = {
.name = "list-clients", .name = "list-clients",
.alias = "lsc", .alias = "lsc",
.args = { "F:t:", 0, 0, NULL }, .args = { "F:f:t:", 0, 0, NULL },
.usage = "[-F format] " CMD_TARGET_SESSION_USAGE, .usage = "[-F format] [-f filter] " CMD_TARGET_SESSION_USAGE,
.target = { 't', CMD_FIND_SESSION, 0 }, .target = { 't', CMD_FIND_SESSION, 0 },
@@ -58,9 +58,10 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
struct client *c; struct client *c;
struct session *s; struct session *s;
struct format_tree *ft; struct format_tree *ft;
const char *template; const char *template, *filter;
u_int idx; u_int idx;
char *line; char *line, *expanded;
int flag;
if (args_has(args, 't')) if (args_has(args, 't'))
s = target->s; s = target->s;
@@ -69,6 +70,7 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
if ((template = args_get(args, 'F')) == NULL) if ((template = args_get(args, 'F')) == NULL)
template = LIST_CLIENTS_TEMPLATE; template = LIST_CLIENTS_TEMPLATE;
filter = args_get(args, 'f');
idx = 0; idx = 0;
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
@@ -79,9 +81,17 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item)
format_add(ft, "line", "%u", idx); format_add(ft, "line", "%u", idx);
format_defaults(ft, c, NULL, NULL, NULL); format_defaults(ft, c, NULL, NULL, NULL);
line = format_expand(ft, template); if (filter != NULL) {
cmdq_print(item, "%s", line); expanded = format_expand(ft, filter);
free(line); flag = format_true(expanded);
free(expanded);
} else
flag = 1;
if (flag) {
line = format_expand(ft, template);
cmdq_print(item, "%s", line);
free(line);
}
format_free(ft); format_free(ft);

View File

@@ -148,6 +148,7 @@ static enum cmd_retval
cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item);
struct key_table *table; struct key_table *table;
struct key_binding *bd; struct key_binding *bd;
const char *tablename, *r, *keystr; const char *tablename, *r, *keystr;
@@ -296,9 +297,15 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item)
strlcat(tmp, cp, tmpsize); strlcat(tmp, cp, tmpsize);
free(cp); free(cp);
cmdq_print(item, "bind-key %s", tmp); if (args_has(args, '1') && tc != NULL) {
status_message_set(tc, -1, 1, 0, "bind-key %s",
tmp);
} else
cmdq_print(item, "bind-key %s", tmp);
free(key); free(key);
if (args_has(args, '1'))
break;
bd = key_bindings_next(table, bd); bd = key_bindings_next(table, bd);
} }
table = key_bindings_next_table(table); table = key_bindings_next_table(table);

View File

@@ -77,7 +77,7 @@ cmd_load_buffer_done(__unused struct client *c, const char *path, int error,
} else if (tc != NULL && } else if (tc != NULL &&
tc->session != NULL && tc->session != NULL &&
(~tc->flags & CLIENT_DEAD)) (~tc->flags & CLIENT_DEAD))
tty_set_selection(&tc->tty, copy, bsize); tty_set_selection(&tc->tty, "", copy, bsize);
if (tc != NULL) if (tc != NULL)
server_client_unref(tc); server_client_unref(tc);
} }

View File

@@ -333,13 +333,6 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
server_client_set_key_table(c, NULL); server_client_set_key_table(c, NULL);
} }
/*
* If there are still configuration file errors to display, put the new
* session's current window into more mode and display them now.
*/
if (cfg_finished)
cfg_show_causes(s);
/* Print if requested. */ /* Print if requested. */
if (args_has(args, 'P')) { if (args_has(args, 'P')) {
if ((template = args_get(args, 'F')) == NULL) if ((template = args_get(args, 'F')) == NULL)
@@ -357,6 +350,9 @@ cmd_new_session_exec(struct cmd *self, struct cmdq_item *item)
cmd_find_from_session(&fs, s, 0); cmd_find_from_session(&fs, s, 0);
cmdq_insert_hook(s, item, &fs, "after-new-session"); cmdq_insert_hook(s, item, &fs, "after-new-session");
if (cfg_finished)
cfg_show_causes(s);
if (sc.argv != NULL) if (sc.argv != NULL)
cmd_free_argv(sc.argc, sc.argv); cmd_free_argv(sc.argc, sc.argv);
free(cwd); free(cwd);

View File

@@ -60,7 +60,7 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = target->s; struct session *s = target->s;
struct winlink *wl = target->wl, *new_wl = NULL; struct winlink *wl = target->wl, *new_wl = NULL;
int idx = target->idx, before; int idx = target->idx, before;
char *cause = NULL, *cp; char *cause = NULL, *cp, *expanded;
const char *template, *name; const char *template, *name;
struct cmd_find_state fs; struct cmd_find_state fs;
struct args_value *av; struct args_value *av;
@@ -71,16 +71,19 @@ cmd_new_window_exec(struct cmd *self, struct cmdq_item *item)
*/ */
name = args_get(args, 'n'); name = args_get(args, 'n');
if (args_has(args, 'S') && name != NULL && target->idx == -1) { if (args_has(args, 'S') && name != NULL && target->idx == -1) {
expanded = format_single(item, name, c, s, NULL, NULL);
RB_FOREACH(wl, winlinks, &s->windows) { RB_FOREACH(wl, winlinks, &s->windows) {
if (strcmp(wl->window->name, name) != 0) if (strcmp(wl->window->name, expanded) != 0)
continue; continue;
if (new_wl == NULL) { if (new_wl == NULL) {
new_wl = wl; new_wl = wl;
continue; continue;
} }
cmdq_error(item, "multiple windows named %s", name); cmdq_error(item, "multiple windows named %s", name);
free(expanded);
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
free(expanded);
if (new_wl != NULL) { if (new_wl != NULL) {
if (args_has(args, 'd')) if (args_has(args, 'd'))
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

View File

@@ -1086,7 +1086,8 @@ cmd_parse_from_arguments(struct args_value *values, u_int count,
arg->type = CMD_PARSE_STRING; arg->type = CMD_PARSE_STRING;
arg->string = copy; arg->string = copy;
TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry); TAILQ_INSERT_TAIL(&cmd->arguments, arg, entry);
} } else
free(copy);
} else if (values[i].type == ARGS_COMMANDS) { } else if (values[i].type == ARGS_COMMANDS) {
arg = xcalloc(1, sizeof *arg); arg = xcalloc(1, sizeof *arg);
arg->type = CMD_PARSE_PARSED_COMMANDS; arg->type = CMD_PARSE_PARSED_COMMANDS;
@@ -1614,13 +1615,24 @@ yylex_token(int ch)
for (;;) { for (;;) {
/* EOF or \n are always the end of the token. */ /* EOF or \n are always the end of the token. */
if (ch == EOF || (state == NONE && ch == '\n')) if (ch == EOF) {
log_debug("%s: end at EOF", __func__);
break; break;
}
if (state == NONE && ch == '\n') {
log_debug("%s: end at EOL", __func__);
break;
}
/* Whitespace or ; or } ends a token unless inside quotes. */ /* Whitespace or ; or } ends a token unless inside quotes. */
if ((ch == ' ' || ch == '\t' || ch == ';' || ch == '}') && if (state == NONE && (ch == ' ' || ch == '\t')) {
state == NONE) log_debug("%s: end at WS", __func__);
break; break;
}
if (state == NONE && (ch == ';' || ch == '}')) {
log_debug("%s: end at %c", __func__, ch);
break;
}
/* /*
* Spaces and comments inside quotes after \n are removed but * Spaces and comments inside quotes after \n are removed but

View File

@@ -54,6 +54,11 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
size_t seplen, bufsize; size_t seplen, bufsize;
int bracket = args_has(args, 'p'); int bracket = args_has(args, 'p');
if (window_pane_exited(wp)) {
cmdq_error(item, "target pane has exited");
return (CMD_RETURN_ERROR);
}
bufname = NULL; bufname = NULL;
if (args_has(args, 'b')) if (args_has(args, 'b'))
bufname = args_get(args, 'b'); bufname = args_get(args, 'b');

View File

@@ -68,7 +68,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
sigset_t set, oldset; sigset_t set, oldset;
/* Do nothing if pane is dead. */ /* Do nothing if pane is dead. */
if (wp->fd == -1 || (wp->flags & PANE_EXITED)) { if (window_pane_exited(wp)) {
cmdq_error(item, "target pane has exited"); cmdq_error(item, "target pane has exited");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }

View File

@@ -236,8 +236,10 @@ cmdq_link_state(struct cmdq_state *state)
/* Make a copy of a state. */ /* Make a copy of a state. */
struct cmdq_state * struct cmdq_state *
cmdq_copy_state(struct cmdq_state *state) cmdq_copy_state(struct cmdq_state *state, struct cmd_find_state *current)
{ {
if (current != NULL)
return (cmdq_new_state(current, &state->event, state->flags));
return (cmdq_new_state(&state->current, &state->event, state->flags)); return (cmdq_new_state(&state->current, &state->event, state->flags));
} }
@@ -821,45 +823,30 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
control_write(c, "%%%s %ld %u %d", guard, t, number, flags); control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
} }
/* Show message from command. */
void
cmdq_print_data(struct cmdq_item *item, int parse, struct evbuffer *evb)
{
server_client_print(item->client, parse, evb);
}
/* Show message from command. */ /* Show message from command. */
void void
cmdq_print(struct cmdq_item *item, const char *fmt, ...) cmdq_print(struct cmdq_item *item, const char *fmt, ...)
{ {
struct client *c = item->client; va_list ap;
struct window_pane *wp; struct evbuffer *evb;
struct window_mode_entry *wme;
va_list ap; evb = evbuffer_new();
char *tmp, *msg; if (evb == NULL)
fatalx("out of memory");
va_start(ap, fmt); va_start(ap, fmt);
xvasprintf(&msg, fmt, ap); evbuffer_add_vprintf(evb, fmt, ap);
va_end(ap); va_end(ap);
log_debug("%s: %s", __func__, msg); cmdq_print_data(item, 0, evb);
evbuffer_free(evb);
if (c == NULL)
/* nothing */;
else if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
if (~c->flags & CLIENT_UTF8) {
tmp = msg;
msg = utf8_sanitize(tmp);
free(tmp);
}
if (c->flags & CLIENT_CONTROL)
control_write(c, "%s", msg);
else
file_print(c, "%s\n", msg);
} else {
wp = server_client_get_pane(c);
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode) {
window_pane_set_mode(wp, NULL, &window_view_mode, NULL,
NULL);
}
window_copy_add(wp, 0, "%s", msg);
}
free(msg);
} }
/* Show error from command. */ /* Show error from command. */

View File

@@ -53,8 +53,7 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = target->s; struct session *s = target->s;
const char *errstr; const char *errstr;
char *cause; char *cause;
u_int adjust, sx, sy; u_int adjust, sx, sy, xpixel = 0, ypixel = 0;
int xpixel = -1, ypixel = -1;
if (args_count(args) == 0) if (args_count(args) == 0)
adjust = 1; adjust = 1;

View File

@@ -44,8 +44,9 @@ const struct cmd_entry cmd_run_shell_entry = {
.name = "run-shell", .name = "run-shell",
.alias = "run", .alias = "run",
.args = { "bd:Ct:", 0, 1, cmd_run_shell_args_parse }, .args = { "bd:Ct:c:", 0, 2, cmd_run_shell_args_parse },
.usage = "[-bC] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]", .usage = "[-bC] [-c start-directory] [-d delay] " CMD_TARGET_PANE_USAGE
" [shell-command]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@@ -103,6 +104,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct cmd_run_shell_data *cdata; struct cmd_run_shell_data *cdata;
struct client *c = cmdq_get_client(item);
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s; struct session *s = target->s;
struct window_pane *wp = target->wp; struct window_pane *wp = target->wp;
@@ -137,7 +139,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->wp_id = -1; cdata->wp_id = -1;
if (wait) { if (wait) {
cdata->client = cmdq_get_client(item); cdata->client = c;
cdata->item = item; cdata->item = item;
} else { } else {
cdata->client = tc; cdata->client = tc;
@@ -145,8 +147,10 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
} }
if (cdata->client != NULL) if (cdata->client != NULL)
cdata->client->references++; cdata->client->references++;
if (args_has(args, 'c'))
cdata->cwd = xstrdup(server_client_get_cwd(cmdq_get_client(item), s)); cdata->cwd = xstrdup(args_get(args, 'c'));
else
cdata->cwd = xstrdup(server_client_get_cwd(c, s));
cdata->s = s; cdata->s = s;
if (s != NULL) if (s != NULL)

View File

@@ -78,7 +78,8 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
int flags; int flags;
const char *bufname = args_get(args, 'b'), *bufdata; const char *bufname = args_get(args, 'b'), *bufdata;
size_t bufsize; size_t bufsize;
char *path, *tmp; char *path;
struct evbuffer *evb;
if (bufname == NULL) { if (bufname == NULL) {
if ((pb = paste_get_top(NULL)) == NULL) { if ((pb = paste_get_top(NULL)) == NULL) {
@@ -96,10 +97,12 @@ cmd_save_buffer_exec(struct cmd *self, struct cmdq_item *item)
if (cmd_get_entry(self) == &cmd_show_buffer_entry) { if (cmd_get_entry(self) == &cmd_show_buffer_entry) {
if (c->session != NULL || (c->flags & CLIENT_CONTROL)) { if (c->session != NULL || (c->flags & CLIENT_CONTROL)) {
utf8_stravisx(&tmp, bufdata, bufsize, evb = evbuffer_new();
VIS_OCTAL|VIS_CSTYLE|VIS_TAB); if (evb == NULL)
cmdq_print(item, "%s", tmp); fatalx("out of memory");
free(tmp); evbuffer_add(evb, bufdata, bufsize);
cmdq_print_data(item, 1, evb);
evbuffer_free(evb);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
path = xstrdup("-"); path = xstrdup("-");

View File

@@ -98,7 +98,11 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item)
struct options_entry *o; struct options_entry *o;
if (entry == &cmd_last_pane_entry || args_has(args, 'l')) { if (entry == &cmd_last_pane_entry || args_has(args, 'l')) {
lastwp = w->last; /*
* Check for no last pane found in case the other pane was
* spawned without being visited (for example split-window -d).
*/
lastwp = TAILQ_FIRST(&w->last_panes);
if (lastwp == NULL && window_count_panes(w) == 2) { if (lastwp == NULL && window_count_panes(w) == 2) {
lastwp = TAILQ_PREV(w->active, window_panes, entry); lastwp = TAILQ_PREV(w->active, window_panes, entry);
if (lastwp == NULL) if (lastwp == NULL)

View File

@@ -33,13 +33,13 @@ const struct cmd_entry cmd_send_keys_entry = {
.name = "send-keys", .name = "send-keys",
.alias = "send", .alias = "send",
.args = { "FHlMN:Rt:X", 0, -1, NULL }, .args = { "c:FHKlMN:Rt:X", 0, -1, NULL },
.usage = "[-FHlMRX] [-N repeat-count] " CMD_TARGET_PANE_USAGE .usage = "[-FHKlMRX] [-c target-client] [-N repeat-count] "
" key ...", CMD_TARGET_PANE_USAGE " key ...",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
.flags = CMD_AFTERHOOK, .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL,
.exec = cmd_send_keys_exec .exec = cmd_send_keys_exec
}; };
@@ -58,7 +58,7 @@ const struct cmd_entry cmd_send_prefix_entry = {
static struct cmdq_item * static struct cmdq_item *
cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after, cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
key_code key) struct args *args, key_code key)
{ {
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
@@ -66,8 +66,20 @@ cmd_send_keys_inject_key(struct cmdq_item *item, struct cmdq_item *after,
struct winlink *wl = target->wl; struct winlink *wl = target->wl;
struct window_pane *wp = target->wp; struct window_pane *wp = target->wp;
struct window_mode_entry *wme; struct window_mode_entry *wme;
struct key_table *table; struct key_table *table = NULL;
struct key_binding *bd; struct key_binding *bd;
struct key_event *event;
if (args_has(args, 'K')) {
if (tc == NULL)
return (item);
event = xmalloc(sizeof *event);
event->key = key|KEYC_SENT;
memset(&event->m, 0, sizeof event->m);
if (server_client_handle_key(tc, event) == 0)
free(event);
return (item);
}
wme = TAILQ_FIRST(&wp->modes); wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode->key_table == NULL) { if (wme == NULL || wme->mode->key_table == NULL) {
@@ -102,14 +114,16 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after,
n = strtol(s, &endptr, 16); n = strtol(s, &endptr, 16);
if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0') if (*s =='\0' || n < 0 || n > 0xff || *endptr != '\0')
return (item); return (item);
return (cmd_send_keys_inject_key(item, after, KEYC_LITERAL|n)); return (cmd_send_keys_inject_key(item, after, args,
KEYC_LITERAL|n));
} }
literal = args_has(args, 'l'); literal = args_has(args, 'l');
if (!literal) { if (!literal) {
key = key_string_lookup_string(s); key = key_string_lookup_string(s);
if (key != KEYC_NONE && key != KEYC_UNKNOWN) { if (key != KEYC_NONE && key != KEYC_UNKNOWN) {
after = cmd_send_keys_inject_key(item, after, key); after = cmd_send_keys_inject_key(item, after, args,
key);
if (after != NULL) if (after != NULL)
return (after); return (after);
} }
@@ -125,7 +139,8 @@ cmd_send_keys_inject_string(struct cmdq_item *item, struct cmdq_item *after,
continue; continue;
key = uc; key = uc;
} }
after = cmd_send_keys_inject_key(item, after, key); after = cmd_send_keys_inject_key(item, after, args,
key);
} }
free(ud); free(ud);
} }
@@ -151,7 +166,8 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
char *cause = NULL; char *cause = NULL;
if (args_has(args, 'N')) { if (args_has(args, 'N')) {
np = args_strtonum(args, 'N', 1, UINT_MAX, &cause); np = args_strtonum_and_expand(args, 'N', 1, UINT_MAX, item,
&cause);
if (cause != NULL) { if (cause != NULL) {
cmdq_error(item, "repeat count %s", cause); cmdq_error(item, "repeat count %s", cause);
free(cause); free(cause);
@@ -192,7 +208,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
key = options_get_number(s->options, "prefix2"); key = options_get_number(s->options, "prefix2");
else else
key = options_get_number(s->options, "prefix"); key = options_get_number(s->options, "prefix");
cmd_send_keys_inject_key(item, item, key); cmd_send_keys_inject_key(item, item, args, key);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -206,7 +222,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'N') || args_has(args, 'R')) if (args_has(args, 'N') || args_has(args, 'R'))
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
for (; np != 0; np--) for (; np != 0; np--)
cmd_send_keys_inject_key(item, NULL, event->key); cmd_send_keys_inject_key(item, NULL, args, event->key);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -69,8 +69,13 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
pb = paste_get_name(bufname); pb = paste_get_name(bufname);
if (cmd_get_entry(self) == &cmd_delete_buffer_entry) { if (cmd_get_entry(self) == &cmd_delete_buffer_entry) {
if (pb == NULL) if (pb == NULL) {
if (bufname != NULL) {
cmdq_error(item, "unknown buffer: %s", bufname);
return (CMD_RETURN_ERROR);
}
pb = paste_get_top(&bufname); pb = paste_get_top(&bufname);
}
if (pb == NULL) { if (pb == NULL) {
cmdq_error(item, "no buffer"); cmdq_error(item, "no buffer");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
@@ -80,8 +85,13 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
} }
if (args_has(args, 'n')) { if (args_has(args, 'n')) {
if (pb == NULL) if (pb == NULL) {
if (bufname != NULL) {
cmdq_error(item, "unknown buffer: %s", bufname);
return (CMD_RETURN_ERROR);
}
pb = paste_get_top(&bufname); pb = paste_get_top(&bufname);
}
if (pb == NULL) { if (pb == NULL) {
cmdq_error(item, "no buffer"); cmdq_error(item, "no buffer");
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
@@ -121,7 +131,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR); return (CMD_RETURN_ERROR);
} }
if (args_has(args, 'w') && tc != NULL) if (args_has(args, 'w') && tc != NULL)
tty_set_selection(&tc->tty, bufdata, bufsize); tty_set_selection(&tc->tty, "", bufdata, bufsize);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }

View File

@@ -35,8 +35,10 @@ const struct cmd_entry cmd_source_file_entry = {
.name = "source-file", .name = "source-file",
.alias = "source", .alias = "source",
.args = { "Fnqv", 1, -1, NULL }, .args = { "t:Fnqv", 1, -1, NULL },
.usage = "[-Fnqv] path ...", .usage = "[-Fnqv] " CMD_TARGET_PANE_USAGE " path ...",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
.flags = 0, .flags = 0,
.exec = cmd_source_file_exec .exec = cmd_source_file_exec
@@ -92,6 +94,7 @@ cmd_source_file_done(struct client *c, const char *path, int error,
size_t bsize = EVBUFFER_LENGTH(buffer); size_t bsize = EVBUFFER_LENGTH(buffer);
u_int n; u_int n;
struct cmdq_item *new_item; struct cmdq_item *new_item;
struct cmd_find_state *target = cmdq_get_target(item);
if (!closed) if (!closed)
return; return;
@@ -100,7 +103,7 @@ cmd_source_file_done(struct client *c, const char *path, int error,
cmdq_error(item, "%s: %s", path, strerror(error)); cmdq_error(item, "%s: %s", path, strerror(error));
else if (bsize != 0) { else if (bsize != 0) {
if (load_cfg_from_buffer(bdata, bsize, path, c, cdata->after, if (load_cfg_from_buffer(bdata, bsize, path, c, cdata->after,
cdata->flags, &new_item) < 0) target, cdata->flags, &new_item) < 0)
cdata->retval = CMD_RETURN_ERROR; cdata->retval = CMD_RETURN_ERROR;
else if (new_item != NULL) else if (new_item != NULL)
cdata->after = new_item; cdata->after = new_item;

View File

@@ -65,67 +65,46 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item)
enum layout_type type; enum layout_type type;
struct layout_cell *lc; struct layout_cell *lc;
struct cmd_find_state fs; struct cmd_find_state fs;
int size, percentage, flags, input; int size, flags, input;
const char *template, *errstr, *p; const char *template;
char *cause, *cp, *copy; char *cause = NULL, *cp;
size_t plen;
struct args_value *av; struct args_value *av;
u_int count = args_count(args); u_int count = args_count(args), curval = 0;
type = LAYOUT_TOPBOTTOM;
if (args_has(args, 'h')) if (args_has(args, 'h'))
type = LAYOUT_LEFTRIGHT; type = LAYOUT_LEFTRIGHT;
else
type = LAYOUT_TOPBOTTOM; /* If the 'p' flag is dropped then this bit can be moved into 'l'. */
if ((p = args_get(args, 'l')) != NULL) { if (args_has(args, 'l') || args_has(args, 'p')) {
plen = strlen(p);
if (p[plen - 1] == '%') {
copy = xstrdup(p);
copy[plen - 1] = '\0';
percentage = strtonum(copy, 0, INT_MAX, &errstr);
free(copy);
if (errstr != NULL) {
cmdq_error(item, "percentage %s", errstr);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'f')) {
if (type == LAYOUT_TOPBOTTOM)
size = (w->sy * percentage) / 100;
else
size = (w->sx * percentage) / 100;
} else {
if (type == LAYOUT_TOPBOTTOM)
size = (wp->sy * percentage) / 100;
else
size = (wp->sx * percentage) / 100;
}
} else {
size = args_strtonum(args, 'l', 0, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "lines %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
}
} else if (args_has(args, 'p')) {
percentage = args_strtonum(args, 'p', 0, INT_MAX, &cause);
if (cause != NULL) {
cmdq_error(item, "create pane failed: -p %s", cause);
free(cause);
return (CMD_RETURN_ERROR);
}
if (args_has(args, 'f')) { if (args_has(args, 'f')) {
if (type == LAYOUT_TOPBOTTOM) if (type == LAYOUT_TOPBOTTOM)
size = (w->sy * percentage) / 100; curval = w->sy;
else else
size = (w->sx * percentage) / 100; curval = w->sx;
} else { } else {
if (type == LAYOUT_TOPBOTTOM) if (type == LAYOUT_TOPBOTTOM)
size = (wp->sy * percentage) / 100; curval = wp->sy;
else else
size = (wp->sx * percentage) / 100; curval = wp->sx;
} }
} else }
size = -1;
size = -1;
if (args_has(args, 'l')) {
size = args_percentage_and_expand(args, 'l', 0, INT_MAX, curval,
item, &cause);
} else if (args_has(args, 'p')) {
size = args_strtonum_and_expand(args, 'l', 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')); window_push_zoom(wp->window, 1, args_has(args, 'Z'));
input = (args_has(args, 'I') && count == 0); input = (args_has(args, 'I') && count == 0);

View File

@@ -128,10 +128,10 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item)
window_set_active_pane(dst_w, src_wp, 1); window_set_active_pane(dst_w, src_wp, 1);
} }
if (src_w != dst_w) { if (src_w != dst_w) {
if (src_w->last == src_wp) window_pane_stack_remove(&src_w->last_panes, src_wp);
src_w->last = NULL; window_pane_stack_remove(&dst_w->last_panes, dst_wp);
if (dst_w->last == dst_wp) colour_palette_from_option(&src_wp->palette, src_wp->options);
dst_w->last = NULL; colour_palette_from_option(&dst_wp->palette, dst_wp->options);
} }
server_redraw_window(src_w); server_redraw_window(src_w);
server_redraw_window(dst_w); server_redraw_window(dst_w);

12
cmd.c
View File

@@ -812,10 +812,14 @@ cmd_mouse_pane(struct mouse_event *m, struct session **sp,
if ((wl = cmd_mouse_window(m, sp)) == NULL) if ((wl = cmd_mouse_window(m, sp)) == NULL)
return (NULL); return (NULL);
if ((wp = window_pane_find_by_id(m->wp)) == NULL) if (m->wp == -1)
return (NULL); wp = wl->window->active;
if (!window_has_pane(wl->window, wp)) else {
return (NULL); if ((wp = window_pane_find_by_id(m->wp)) == NULL)
return (NULL);
if (!window_has_pane(wl->window, wp))
return (NULL);
}
if (wlp != NULL) if (wlp != NULL)
*wlp = wl; *wlp = wl;

View File

@@ -960,6 +960,47 @@ colour_byname(const char *name)
return (-1); return (-1);
} }
/* Parse colour from an X11 string. */
int
colour_parseX11(const char *p)
{
double c, m, y, k = 0;
u_int r, g, b;
size_t len = strlen(p);
int colour = -1;
char *copy;
if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) ||
(len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) ||
sscanf(p, "%d,%d,%d", &r, &g, &b) == 3)
colour = colour_join_rgb(r, g, b);
else if ((len == 18 &&
sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) ||
(len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3))
colour = colour_join_rgb(r >> 8, g >> 8, b >> 8);
else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 ||
sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) &&
c >= 0 && c <= 1 && m >= 0 && m <= 1 &&
y >= 0 && y <= 1 && k >= 0 && k <= 1) {
colour = colour_join_rgb(
(1 - c) * (1 - k) * 255,
(1 - m) * (1 - k) * 255,
(1 - y) * (1 - k) * 255);
} else {
while (len != 0 && *p == ' ') {
p++;
len--;
}
while (len != 0 && p[len - 1] == ' ')
len--;
copy = xstrndup(p, len);
colour = colour_byname(copy);
free(copy);
}
log_debug("%s: %s = %s", __func__, p, colour_tostring(colour));
return (colour);
}
/* Initialize palette. */ /* Initialize palette. */
void void
colour_palette_init(struct colour_palette *p) colour_palette_init(struct colour_palette *p)
@@ -1069,5 +1110,4 @@ colour_palette_from_option(struct colour_palette *p, struct options *oo)
} }
a = options_array_next(a); a = options_array_next(a);
} }
} }

View File

@@ -334,6 +334,18 @@ char *strndup(const char *, size_t);
void *memmem(const void *, size_t, const void *, size_t); void *memmem(const void *, size_t, const void *, size_t);
#endif #endif
#ifndef HAVE_HTONLL
/* htonll.c */
#undef htonll
uint64_t htonll(uint64_t);
#endif
#ifndef HAVE_NTOHLL
/* ntohll.c */
#undef ntohll
uint64_t ntohll(uint64_t);
#endif
#ifndef HAVE_GETPEEREID #ifndef HAVE_GETPEEREID
/* getpeereid.c */ /* getpeereid.c */
int getpeereid(int, uid_t *, gid_t *); int getpeereid(int, uid_t *, gid_t *);
@@ -423,7 +435,9 @@ void *recallocarray(void *, size_t, size_t, size_t);
#ifdef HAVE_SYSTEMD #ifdef HAVE_SYSTEMD
/* systemd.c */ /* systemd.c */
int systemd_activated(void);
int systemd_create_socket(int, char **); int systemd_create_socket(int, char **);
int systemd_move_pid_to_new_cgroup(pid_t, char **);
#endif #endif
#ifdef HAVE_UTF8PROC #ifdef HAVE_UTF8PROC

View File

@@ -18,6 +18,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#ifdef HAVE_UCRED_H #ifdef HAVE_UCRED_H
#include <ucred.h> #include <ucred.h>
@@ -49,6 +50,8 @@ getpeereid(int s, uid_t *uid, gid_t *gid)
ucred_free(ucred); ucred_free(ucred);
return (0); return (0);
#else #else
return (getuid()); *uid = geteuid();
*gid = getegid();
return (0);
#endif #endif
} }

30
compat/htonll.c Normal file
View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2024 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include "compat.h"
uint64_t
htonll(uint64_t v)
{
uint32_t b;
uint32_t t;
b = htonl (v & 0xffffffff);
t = htonl (v >> 32);
return ((uint64_t)b << 32 | t);
}

View File

@@ -1,6 +1,7 @@
/* $OpenBSD: imsg-buffer.c,v 1.12 2019/01/20 02:50:03 bcook Exp $ */ /* $OpenBSD: imsg-buffer.c,v 1.18 2023/12/12 15:47:41 claudio Exp $ */
/* /*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
@@ -19,9 +20,11 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <arpa/inet.h>
#include <limits.h> #include <limits.h>
#include <errno.h> #include <errno.h>
#include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
@@ -29,18 +32,36 @@
#include "compat.h" #include "compat.h"
#include "imsg.h" #include "imsg.h"
#undef htobe16
#define htobe16 htons
#undef htobe32
#define htobe32 htonl
#undef htobe64
#define htobe64 htonll
#undef be16toh
#define be16toh ntohs
#undef be32toh
#define be32toh ntohl
#undef be64toh
#define be64toh ntohll
static int ibuf_realloc(struct ibuf *, size_t); static int ibuf_realloc(struct ibuf *, size_t);
static void ibuf_enqueue(struct msgbuf *, struct ibuf *); static void ibuf_enqueue(struct msgbuf *, struct ibuf *);
static void ibuf_dequeue(struct msgbuf *, struct ibuf *); static void ibuf_dequeue(struct msgbuf *, struct ibuf *);
static void msgbuf_drain(struct msgbuf *, size_t);
struct ibuf * struct ibuf *
ibuf_open(size_t len) ibuf_open(size_t len)
{ {
struct ibuf *buf; struct ibuf *buf;
if (len == 0) {
errno = EINVAL;
return (NULL);
}
if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
return (NULL); return (NULL);
if ((buf->buf = malloc(len)) == NULL) { if ((buf->buf = calloc(len, 1)) == NULL) {
free(buf); free(buf);
return (NULL); return (NULL);
} }
@@ -55,14 +76,22 @@ ibuf_dynamic(size_t len, size_t max)
{ {
struct ibuf *buf; struct ibuf *buf;
if (max < len) if (max == 0 || max < len) {
errno = EINVAL;
return (NULL); return (NULL);
}
if ((buf = ibuf_open(len)) == NULL) if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
return (NULL); return (NULL);
if (len > 0) {
if (max > 0) if ((buf->buf = calloc(len, 1)) == NULL) {
buf->max = max; free(buf);
return (NULL);
}
}
buf->size = len;
buf->max = max;
buf->fd = -1;
return (buf); return (buf);
} }
@@ -73,7 +102,7 @@ ibuf_realloc(struct ibuf *buf, size_t len)
unsigned char *b; unsigned char *b;
/* on static buffers max is eq size and so the following fails */ /* on static buffers max is eq size and so the following fails */
if (buf->wpos + len > buf->max) { if (len > SIZE_MAX - buf->wpos || buf->wpos + len > buf->max) {
errno = ERANGE; errno = ERANGE;
return (-1); return (-1);
} }
@@ -87,23 +116,16 @@ ibuf_realloc(struct ibuf *buf, size_t len)
return (0); return (0);
} }
int
ibuf_add(struct ibuf *buf, const void *data, size_t len)
{
if (buf->wpos + len > buf->size)
if (ibuf_realloc(buf, len) == -1)
return (-1);
memcpy(buf->buf + buf->wpos, data, len);
buf->wpos += len;
return (0);
}
void * void *
ibuf_reserve(struct ibuf *buf, size_t len) ibuf_reserve(struct ibuf *buf, size_t len)
{ {
void *b; void *b;
if (len > SIZE_MAX - buf->wpos || buf->max == 0) {
errno = ERANGE;
return (NULL);
}
if (buf->wpos + len > buf->size) if (buf->wpos + len > buf->size)
if (ibuf_realloc(buf, len) == -1) if (ibuf_realloc(buf, len) == -1)
return (NULL); return (NULL);
@@ -113,34 +135,416 @@ ibuf_reserve(struct ibuf *buf, size_t len)
return (b); return (b);
} }
int
ibuf_add(struct ibuf *buf, const void *data, size_t len)
{
void *b;
if ((b = ibuf_reserve(buf, len)) == NULL)
return (-1);
memcpy(b, data, len);
return (0);
}
int
ibuf_add_ibuf(struct ibuf *buf, const struct ibuf *from)
{
return ibuf_add(buf, ibuf_data(from), ibuf_size(from));
}
/* remove after tree is converted */
int
ibuf_add_buf(struct ibuf *buf, const struct ibuf *from)
{
return ibuf_add_ibuf(buf, from);
}
int
ibuf_add_n8(struct ibuf *buf, uint64_t value)
{
uint8_t v;
if (value > UINT8_MAX) {
errno = EINVAL;
return (-1);
}
v = value;
return ibuf_add(buf, &v, sizeof(v));
}
int
ibuf_add_n16(struct ibuf *buf, uint64_t value)
{
uint16_t v;
if (value > UINT16_MAX) {
errno = EINVAL;
return (-1);
}
v = htobe16(value);
return ibuf_add(buf, &v, sizeof(v));
}
int
ibuf_add_n32(struct ibuf *buf, uint64_t value)
{
uint32_t v;
if (value > UINT32_MAX) {
errno = EINVAL;
return (-1);
}
v = htobe32(value);
return ibuf_add(buf, &v, sizeof(v));
}
int
ibuf_add_n64(struct ibuf *buf, uint64_t value)
{
value = htobe64(value);
return ibuf_add(buf, &value, sizeof(value));
}
int
ibuf_add_h16(struct ibuf *buf, uint64_t value)
{
uint16_t v;
if (value > UINT16_MAX) {
errno = EINVAL;
return (-1);
}
v = value;
return ibuf_add(buf, &v, sizeof(v));
}
int
ibuf_add_h32(struct ibuf *buf, uint64_t value)
{
uint32_t v;
if (value > UINT32_MAX) {
errno = EINVAL;
return (-1);
}
v = value;
return ibuf_add(buf, &v, sizeof(v));
}
int
ibuf_add_h64(struct ibuf *buf, uint64_t value)
{
return ibuf_add(buf, &value, sizeof(value));
}
int
ibuf_add_zero(struct ibuf *buf, size_t len)
{
void *b;
if ((b = ibuf_reserve(buf, len)) == NULL)
return (-1);
memset(b, 0, len);
return (0);
}
void * void *
ibuf_seek(struct ibuf *buf, size_t pos, size_t len) ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
{ {
/* only allowed to seek in already written parts */ /* only allow seeking between rpos and wpos */
if (pos + len > buf->wpos) if (ibuf_size(buf) < pos || SIZE_MAX - pos < len ||
ibuf_size(buf) < pos + len) {
errno = ERANGE;
return (NULL); return (NULL);
}
return (buf->buf + pos); return (buf->buf + buf->rpos + pos);
}
int
ibuf_set(struct ibuf *buf, size_t pos, const void *data, size_t len)
{
void *b;
if ((b = ibuf_seek(buf, pos, len)) == NULL)
return (-1);
memcpy(b, data, len);
return (0);
}
int
ibuf_set_n8(struct ibuf *buf, size_t pos, uint64_t value)
{
uint8_t v;
if (value > UINT8_MAX) {
errno = EINVAL;
return (-1);
}
v = value;
return (ibuf_set(buf, pos, &v, sizeof(v)));
}
int
ibuf_set_n16(struct ibuf *buf, size_t pos, uint64_t value)
{
uint16_t v;
if (value > UINT16_MAX) {
errno = EINVAL;
return (-1);
}
v = htobe16(value);
return (ibuf_set(buf, pos, &v, sizeof(v)));
}
int
ibuf_set_n32(struct ibuf *buf, size_t pos, uint64_t value)
{
uint32_t v;
if (value > UINT32_MAX) {
errno = EINVAL;
return (-1);
}
v = htobe32(value);
return (ibuf_set(buf, pos, &v, sizeof(v)));
}
int
ibuf_set_n64(struct ibuf *buf, size_t pos, uint64_t value)
{
value = htobe64(value);
return (ibuf_set(buf, pos, &value, sizeof(value)));
}
int
ibuf_set_h16(struct ibuf *buf, size_t pos, uint64_t value)
{
uint16_t v;
if (value > UINT16_MAX) {
errno = EINVAL;
return (-1);
}
v = value;
return (ibuf_set(buf, pos, &v, sizeof(v)));
}
int
ibuf_set_h32(struct ibuf *buf, size_t pos, uint64_t value)
{
uint32_t v;
if (value > UINT32_MAX) {
errno = EINVAL;
return (-1);
}
v = value;
return (ibuf_set(buf, pos, &v, sizeof(v)));
}
int
ibuf_set_h64(struct ibuf *buf, size_t pos, uint64_t value)
{
return (ibuf_set(buf, pos, &value, sizeof(value)));
}
void *
ibuf_data(const struct ibuf *buf)
{
return (buf->buf + buf->rpos);
} }
size_t size_t
ibuf_size(struct ibuf *buf) ibuf_size(const struct ibuf *buf)
{ {
return (buf->wpos); return (buf->wpos - buf->rpos);
} }
size_t size_t
ibuf_left(struct ibuf *buf) ibuf_left(const struct ibuf *buf)
{ {
if (buf->max == 0)
return (0);
return (buf->max - buf->wpos); return (buf->max - buf->wpos);
} }
int
ibuf_truncate(struct ibuf *buf, size_t len)
{
if (ibuf_size(buf) >= len) {
buf->wpos = buf->rpos + len;
return (0);
}
if (buf->max == 0) {
/* only allow to truncate down */
errno = ERANGE;
return (-1);
}
return ibuf_add_zero(buf, len - ibuf_size(buf));
}
void
ibuf_rewind(struct ibuf *buf)
{
buf->rpos = 0;
}
void void
ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf) ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
{ {
ibuf_enqueue(msgbuf, buf); ibuf_enqueue(msgbuf, buf);
} }
void
ibuf_from_buffer(struct ibuf *buf, void *data, size_t len)
{
memset(buf, 0, sizeof(*buf));
buf->buf = data;
buf->size = buf->wpos = len;
buf->fd = -1;
}
void
ibuf_from_ibuf(struct ibuf *buf, const struct ibuf *from)
{
ibuf_from_buffer(buf, ibuf_data(from), ibuf_size(from));
}
int
ibuf_get(struct ibuf *buf, void *data, size_t len)
{
if (ibuf_size(buf) < len) {
errno = EBADMSG;
return (-1);
}
memcpy(data, ibuf_data(buf), len);
buf->rpos += len;
return (0);
}
int
ibuf_get_ibuf(struct ibuf *buf, size_t len, struct ibuf *new)
{
if (ibuf_size(buf) < len) {
errno = EBADMSG;
return (-1);
}
ibuf_from_buffer(new, ibuf_data(buf), len);
buf->rpos += len;
return (0);
}
int
ibuf_get_n8(struct ibuf *buf, uint8_t *value)
{
return ibuf_get(buf, value, sizeof(*value));
}
int
ibuf_get_n16(struct ibuf *buf, uint16_t *value)
{
int rv;
rv = ibuf_get(buf, value, sizeof(*value));
*value = be16toh(*value);
return (rv);
}
int
ibuf_get_n32(struct ibuf *buf, uint32_t *value)
{
int rv;
rv = ibuf_get(buf, value, sizeof(*value));
*value = be32toh(*value);
return (rv);
}
int
ibuf_get_n64(struct ibuf *buf, uint64_t *value)
{
int rv;
rv = ibuf_get(buf, value, sizeof(*value));
*value = be64toh(*value);
return (rv);
}
int
ibuf_get_h16(struct ibuf *buf, uint16_t *value)
{
return ibuf_get(buf, value, sizeof(*value));
}
int
ibuf_get_h32(struct ibuf *buf, uint32_t *value)
{
return ibuf_get(buf, value, sizeof(*value));
}
int
ibuf_get_h64(struct ibuf *buf, uint64_t *value)
{
return ibuf_get(buf, value, sizeof(*value));
}
int
ibuf_skip(struct ibuf *buf, size_t len)
{
if (ibuf_size(buf) < len) {
errno = EBADMSG;
return (-1);
}
buf->rpos += len;
return (0);
}
void
ibuf_free(struct ibuf *buf)
{
if (buf == NULL)
return;
if (buf->max == 0) /* if buf lives on the stack */
abort(); /* abort before causing more harm */
if (buf->fd != -1)
close(buf->fd);
freezero(buf->buf, buf->size);
free(buf);
}
int
ibuf_fd_avail(struct ibuf *buf)
{
return (buf->fd != -1);
}
int
ibuf_fd_get(struct ibuf *buf)
{
int fd;
fd = buf->fd;
buf->fd = -1;
return (fd);
}
void
ibuf_fd_set(struct ibuf *buf, int fd)
{
if (buf->max == 0) /* if buf lives on the stack */
abort(); /* abort before causing more harm */
if (buf->fd != -1)
close(buf->fd);
buf->fd = fd;
}
int int
ibuf_write(struct msgbuf *msgbuf) ibuf_write(struct msgbuf *msgbuf)
{ {
@@ -153,8 +557,8 @@ ibuf_write(struct msgbuf *msgbuf)
TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
if (i >= IOV_MAX) if (i >= IOV_MAX)
break; break;
iov[i].iov_base = buf->buf + buf->rpos; iov[i].iov_base = ibuf_data(buf);
iov[i].iov_len = buf->wpos - buf->rpos; iov[i].iov_len = ibuf_size(buf);
i++; i++;
} }
@@ -177,15 +581,6 @@ again:
return (1); return (1);
} }
void
ibuf_free(struct ibuf *buf)
{
if (buf == NULL)
return;
freezero(buf->buf, buf->size);
free(buf);
}
void void
msgbuf_init(struct msgbuf *msgbuf) msgbuf_init(struct msgbuf *msgbuf)
{ {
@@ -194,7 +589,7 @@ msgbuf_init(struct msgbuf *msgbuf)
TAILQ_INIT(&msgbuf->bufs); TAILQ_INIT(&msgbuf->bufs);
} }
void static void
msgbuf_drain(struct msgbuf *msgbuf, size_t n) msgbuf_drain(struct msgbuf *msgbuf, size_t n)
{ {
struct ibuf *buf, *next; struct ibuf *buf, *next;
@@ -202,8 +597,8 @@ msgbuf_drain(struct msgbuf *msgbuf, size_t n)
for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
buf = next) { buf = next) {
next = TAILQ_NEXT(buf, entry); next = TAILQ_NEXT(buf, entry);
if (buf->rpos + n >= buf->wpos) { if (n >= ibuf_size(buf)) {
n -= buf->wpos - buf->rpos; n -= ibuf_size(buf);
ibuf_dequeue(msgbuf, buf); ibuf_dequeue(msgbuf, buf);
} else { } else {
buf->rpos += n; buf->rpos += n;
@@ -225,7 +620,7 @@ int
msgbuf_write(struct msgbuf *msgbuf) msgbuf_write(struct msgbuf *msgbuf)
{ {
struct iovec iov[IOV_MAX]; struct iovec iov[IOV_MAX];
struct ibuf *buf; struct ibuf *buf, *buf0 = NULL;
unsigned int i = 0; unsigned int i = 0;
ssize_t n; ssize_t n;
struct msghdr msg; struct msghdr msg;
@@ -241,24 +636,26 @@ msgbuf_write(struct msgbuf *msgbuf)
TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
if (i >= IOV_MAX) if (i >= IOV_MAX)
break; break;
iov[i].iov_base = buf->buf + buf->rpos; if (i > 0 && buf->fd != -1)
iov[i].iov_len = buf->wpos - buf->rpos; break;
iov[i].iov_base = ibuf_data(buf);
iov[i].iov_len = ibuf_size(buf);
i++; i++;
if (buf->fd != -1) if (buf->fd != -1)
break; buf0 = buf;
} }
msg.msg_iov = iov; msg.msg_iov = iov;
msg.msg_iovlen = i; msg.msg_iovlen = i;
if (buf != NULL && buf->fd != -1) { if (buf0 != NULL) {
msg.msg_control = (caddr_t)&cmsgbuf.buf; msg.msg_control = (caddr_t)&cmsgbuf.buf;
msg.msg_controllen = sizeof(cmsgbuf.buf); msg.msg_controllen = sizeof(cmsgbuf.buf);
cmsg = CMSG_FIRSTHDR(&msg); cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_type = SCM_RIGHTS;
*(int *)CMSG_DATA(cmsg) = buf->fd; *(int *)CMSG_DATA(cmsg) = buf0->fd;
} }
again: again:
@@ -279,9 +676,9 @@ again:
* assumption: fd got sent if sendmsg sent anything * assumption: fd got sent if sendmsg sent anything
* this works because fds are passed one at a time * this works because fds are passed one at a time
*/ */
if (buf != NULL && buf->fd != -1) { if (buf0 != NULL) {
close(buf->fd); close(buf0->fd);
buf->fd = -1; buf0->fd = -1;
} }
msgbuf_drain(msgbuf, n); msgbuf_drain(msgbuf, n);
@@ -289,9 +686,17 @@ again:
return (1); return (1);
} }
uint32_t
msgbuf_queuelen(struct msgbuf *msgbuf)
{
return (msgbuf->queued);
}
static void static void
ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
{ {
if (buf->max == 0) /* if buf lives on the stack */
abort(); /* abort before causing more harm */
TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
msgbuf->queued++; msgbuf->queued++;
} }
@@ -300,10 +705,6 @@ static void
ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
{ {
TAILQ_REMOVE(&msgbuf->bufs, buf, entry); TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
if (buf->fd != -1)
close(buf->fd);
msgbuf->queued--; msgbuf->queued--;
ibuf_free(buf); ibuf_free(buf);
} }

View File

@@ -1,6 +1,7 @@
/* $OpenBSD: imsg.c,v 1.16 2017/12/14 09:27:44 kettenis Exp $ */ /* $OpenBSD: imsg.c,v 1.23 2023/12/12 15:47:41 claudio Exp $ */
/* /*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
@@ -28,23 +29,28 @@
#include "compat.h" #include "compat.h"
#include "imsg.h" #include "imsg.h"
struct imsg_fd {
TAILQ_ENTRY(imsg_fd) entry;
int fd;
};
int imsg_fd_overhead = 0; int imsg_fd_overhead = 0;
static int imsg_get_fd(struct imsgbuf *); static int imsg_dequeue_fd(struct imsgbuf *);
void void
imsg_init(struct imsgbuf *ibuf, int fd) imsg_init(struct imsgbuf *imsgbuf, int fd)
{ {
msgbuf_init(&ibuf->w); msgbuf_init(&imsgbuf->w);
memset(&ibuf->r, 0, sizeof(ibuf->r)); memset(&imsgbuf->r, 0, sizeof(imsgbuf->r));
ibuf->fd = fd; imsgbuf->fd = fd;
ibuf->w.fd = fd; imsgbuf->w.fd = fd;
ibuf->pid = getpid(); imsgbuf->pid = getpid();
TAILQ_INIT(&ibuf->fds); TAILQ_INIT(&imsgbuf->fds);
} }
ssize_t ssize_t
imsg_read(struct imsgbuf *ibuf) imsg_read(struct imsgbuf *imsgbuf)
{ {
struct msghdr msg; struct msghdr msg;
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
@@ -60,8 +66,8 @@ imsg_read(struct imsgbuf *ibuf)
memset(&msg, 0, sizeof(msg)); memset(&msg, 0, sizeof(msg));
memset(&cmsgbuf, 0, sizeof(cmsgbuf)); memset(&cmsgbuf, 0, sizeof(cmsgbuf));
iov.iov_base = ibuf->r.buf + ibuf->r.wpos; iov.iov_base = imsgbuf->r.buf + imsgbuf->r.wpos;
iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; iov.iov_len = sizeof(imsgbuf->r.buf) - imsgbuf->r.wpos;
msg.msg_iov = &iov; msg.msg_iov = &iov;
msg.msg_iovlen = 1; msg.msg_iovlen = 1;
msg.msg_control = &cmsgbuf.buf; msg.msg_control = &cmsgbuf.buf;
@@ -79,13 +85,13 @@ again:
return (-1); return (-1);
} }
if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { if ((n = recvmsg(imsgbuf->fd, &msg, 0)) == -1) {
if (errno == EINTR) if (errno == EINTR)
goto again; goto again;
goto fail; goto fail;
} }
ibuf->r.wpos += n; imsgbuf->r.wpos += n;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) { cmsg = CMSG_NXTHDR(&msg, cmsg)) {
@@ -105,7 +111,7 @@ again:
fd = ((int *)CMSG_DATA(cmsg))[i]; fd = ((int *)CMSG_DATA(cmsg))[i];
if (ifd != NULL) { if (ifd != NULL) {
ifd->fd = fd; ifd->fd = fd;
TAILQ_INSERT_TAIL(&ibuf->fds, ifd, TAILQ_INSERT_TAIL(&imsgbuf->fds, ifd,
entry); entry);
ifd = NULL; ifd = NULL;
} else } else
@@ -121,94 +127,235 @@ fail:
} }
ssize_t ssize_t
imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
{ {
struct imsg m;
size_t av, left, datalen; size_t av, left, datalen;
av = ibuf->r.wpos; av = imsgbuf->r.wpos;
if (IMSG_HEADER_SIZE > av) if (IMSG_HEADER_SIZE > av)
return (0); return (0);
memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr)); memcpy(&m.hdr, imsgbuf->r.buf, sizeof(m.hdr));
if (imsg->hdr.len < IMSG_HEADER_SIZE || if (m.hdr.len < IMSG_HEADER_SIZE ||
imsg->hdr.len > MAX_IMSGSIZE) { m.hdr.len > MAX_IMSGSIZE) {
errno = ERANGE; errno = ERANGE;
return (-1); return (-1);
} }
if (imsg->hdr.len > av) if (m.hdr.len > av)
return (0); return (0);
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
if (datalen == 0)
imsg->data = NULL;
else if ((imsg->data = malloc(datalen)) == NULL)
return (-1);
if (imsg->hdr.flags & IMSGF_HASFD) m.fd = -1;
imsg->fd = imsg_get_fd(ibuf); m.buf = NULL;
else m.data = NULL;
imsg->fd = -1;
memcpy(imsg->data, ibuf->r.rptr, datalen); datalen = m.hdr.len - IMSG_HEADER_SIZE;
imsgbuf->r.rptr = imsgbuf->r.buf + IMSG_HEADER_SIZE;
if (datalen != 0) {
if ((m.buf = ibuf_open(datalen)) == NULL)
return (-1);
if (ibuf_add(m.buf, imsgbuf->r.rptr, datalen) == -1) {
/* this should never fail */
ibuf_free(m.buf);
return (-1);
}
m.data = ibuf_data(m.buf);
}
if (imsg->hdr.len < av) { if (m.hdr.flags & IMSGF_HASFD)
left = av - imsg->hdr.len; m.fd = imsg_dequeue_fd(imsgbuf);
memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
ibuf->r.wpos = left; if (m.hdr.len < av) {
left = av - m.hdr.len;
memmove(&imsgbuf->r.buf, imsgbuf->r.buf + m.hdr.len, left);
imsgbuf->r.wpos = left;
} else } else
ibuf->r.wpos = 0; imsgbuf->r.wpos = 0;
*imsg = m;
return (datalen + IMSG_HEADER_SIZE); return (datalen + IMSG_HEADER_SIZE);
} }
int int
imsg_compose(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid, imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf)
int fd, const void *data, uint16_t datalen) {
if (imsg->buf == NULL) {
errno = EBADMSG;
return (-1);
}
return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf);
}
int
imsg_get_data(struct imsg *imsg, void *data, size_t len)
{
if (len == 0) {
errno = EINVAL;
return (-1);
}
if (imsg->buf == NULL || ibuf_size(imsg->buf) != len) {
errno = EBADMSG;
return (-1);
}
return ibuf_get(imsg->buf, data, len);
}
int
imsg_get_fd(struct imsg *imsg)
{
int fd = imsg->fd;
imsg->fd = -1;
return fd;
}
uint32_t
imsg_get_id(struct imsg *imsg)
{
return (imsg->hdr.peerid);
}
size_t
imsg_get_len(struct imsg *imsg)
{
if (imsg->buf == NULL)
return 0;
return ibuf_size(imsg->buf);
}
pid_t
imsg_get_pid(struct imsg *imsg)
{
return (imsg->hdr.pid);
}
uint32_t
imsg_get_type(struct imsg *imsg)
{
return (imsg->hdr.type);
}
int
imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
int fd, const void *data, size_t datalen)
{ {
struct ibuf *wbuf; struct ibuf *wbuf;
if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
return (-1); return (-1);
if (imsg_add(wbuf, data, datalen) == -1) if (imsg_add(wbuf, data, datalen) == -1)
return (-1); return (-1);
wbuf->fd = fd; ibuf_fd_set(wbuf, fd);
imsg_close(imsgbuf, wbuf);
imsg_close(ibuf, wbuf);
return (1); return (1);
} }
int int
imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid, imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
int fd, const struct iovec *iov, int iovcnt) int fd, const struct iovec *iov, int iovcnt)
{ {
struct ibuf *wbuf; struct ibuf *wbuf;
int i, datalen = 0; int i;
size_t datalen = 0;
for (i = 0; i < iovcnt; i++) for (i = 0; i < iovcnt; i++)
datalen += iov[i].iov_len; datalen += iov[i].iov_len;
if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
return (-1); return (-1);
for (i = 0; i < iovcnt; i++) for (i = 0; i < iovcnt; i++)
if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
return (-1); return (-1);
wbuf->fd = fd; ibuf_fd_set(wbuf, fd);
imsg_close(imsgbuf, wbuf);
imsg_close(ibuf, wbuf);
return (1); return (1);
} }
/* ARGSUSED */ /*
* Enqueue imsg with payload from ibuf buf. fd passing is not possible
* with this function.
*/
int
imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
pid_t pid, struct ibuf *buf)
{
struct ibuf *hdrbuf = NULL;
struct imsg_hdr hdr;
int save_errno;
if (ibuf_size(buf) + IMSG_HEADER_SIZE > MAX_IMSGSIZE) {
errno = ERANGE;
goto fail;
}
hdr.type = type;
hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE;
hdr.flags = 0;
hdr.peerid = id;
if ((hdr.pid = pid) == 0)
hdr.pid = imsgbuf->pid;
if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL)
goto fail;
if (imsg_add(hdrbuf, &hdr, sizeof(hdr)) == -1)
goto fail;
ibuf_close(&imsgbuf->w, hdrbuf);
ibuf_close(&imsgbuf->w, buf);
return (1);
fail:
save_errno = errno;
ibuf_free(buf);
ibuf_free(hdrbuf);
errno = save_errno;
return (-1);
}
/*
* Forward imsg to another channel. Any attached fd is closed.
*/
int
imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg)
{
struct ibuf *wbuf;
size_t len = 0;
if (msg->fd != -1) {
close(msg->fd);
msg->fd = -1;
}
if (msg->buf != NULL) {
ibuf_rewind(msg->buf);
len = ibuf_size(msg->buf);
}
if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid,
msg->hdr.pid, len)) == NULL)
return (-1);
if (msg->buf != NULL) {
if (ibuf_add_buf(wbuf, msg->buf) == -1) {
ibuf_free(wbuf);
return (-1);
}
}
imsg_close(imsgbuf, wbuf);
return (1);
}
struct ibuf * struct ibuf *
imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid, imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
uint16_t datalen) size_t datalen)
{ {
struct ibuf *wbuf; struct ibuf *wbuf;
struct imsg_hdr hdr; struct imsg_hdr hdr;
@@ -221,9 +368,9 @@ imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
hdr.type = type; hdr.type = type;
hdr.flags = 0; hdr.flags = 0;
hdr.peerid = peerid; hdr.peerid = id;
if ((hdr.pid = pid) == 0) if ((hdr.pid = pid) == 0)
hdr.pid = ibuf->pid; hdr.pid = imsgbuf->pid;
if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) { if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
return (NULL); return (NULL);
} }
@@ -234,7 +381,7 @@ imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
} }
int int
imsg_add(struct ibuf *msg, const void *data, uint16_t datalen) imsg_add(struct ibuf *msg, const void *data, size_t datalen)
{ {
if (datalen) if (datalen)
if (ibuf_add(msg, data, datalen) == -1) { if (ibuf_add(msg, data, datalen) == -1) {
@@ -245,58 +392,57 @@ imsg_add(struct ibuf *msg, const void *data, uint16_t datalen)
} }
void void
imsg_close(struct imsgbuf *ibuf, struct ibuf *msg) imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg)
{ {
struct imsg_hdr *hdr; struct imsg_hdr *hdr;
hdr = (struct imsg_hdr *)msg->buf; hdr = (struct imsg_hdr *)msg->buf;
hdr->flags &= ~IMSGF_HASFD; hdr->flags &= ~IMSGF_HASFD;
if (msg->fd != -1) if (ibuf_fd_avail(msg))
hdr->flags |= IMSGF_HASFD; hdr->flags |= IMSGF_HASFD;
hdr->len = ibuf_size(msg);
hdr->len = (uint16_t)msg->wpos; ibuf_close(&imsgbuf->w, msg);
ibuf_close(&ibuf->w, msg);
} }
void void
imsg_free(struct imsg *imsg) imsg_free(struct imsg *imsg)
{ {
freezero(imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE); ibuf_free(imsg->buf);
} }
static int static int
imsg_get_fd(struct imsgbuf *ibuf) imsg_dequeue_fd(struct imsgbuf *imsgbuf)
{ {
int fd; int fd;
struct imsg_fd *ifd; struct imsg_fd *ifd;
if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL) if ((ifd = TAILQ_FIRST(&imsgbuf->fds)) == NULL)
return (-1); return (-1);
fd = ifd->fd; fd = ifd->fd;
TAILQ_REMOVE(&ibuf->fds, ifd, entry); TAILQ_REMOVE(&imsgbuf->fds, ifd, entry);
free(ifd); free(ifd);
return (fd); return (fd);
} }
int int
imsg_flush(struct imsgbuf *ibuf) imsg_flush(struct imsgbuf *imsgbuf)
{ {
while (ibuf->w.queued) while (imsgbuf->w.queued)
if (msgbuf_write(&ibuf->w) <= 0) if (msgbuf_write(&imsgbuf->w) <= 0)
return (-1); return (-1);
return (0); return (0);
} }
void void
imsg_clear(struct imsgbuf *ibuf) imsg_clear(struct imsgbuf *imsgbuf)
{ {
int fd; int fd;
msgbuf_clear(&ibuf->w); msgbuf_clear(&imsgbuf->w);
while ((fd = imsg_get_fd(ibuf)) != -1) while ((fd = imsg_dequeue_fd(imsgbuf)) != -1)
close(fd); close(fd);
} }

View File

@@ -1,6 +1,7 @@
/* $OpenBSD: imsg.h,v 1.5 2019/01/20 02:50:03 bcook Exp $ */ /* $OpenBSD: imsg.h,v 1.8 2023/12/12 15:47:41 claudio Exp $ */
/* /*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org> * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
* Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org> * Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -21,7 +22,7 @@
#ifndef _IMSG_H_ #ifndef _IMSG_H_
#define _IMSG_H_ #define _IMSG_H_
#include <stdint.h> #include <sys/types.h>
#define IBUF_READ_SIZE 65535 #define IBUF_READ_SIZE 65535
#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) #define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
@@ -49,11 +50,7 @@ struct ibuf_read {
size_t wpos; size_t wpos;
}; };
struct imsg_fd { struct imsg_fd;
TAILQ_ENTRY(imsg_fd) entry;
int fd;
};
struct imsgbuf { struct imsgbuf {
TAILQ_HEAD(, imsg_fd) fds; TAILQ_HEAD(, imsg_fd) fds;
struct ibuf_read r; struct ibuf_read r;
@@ -76,35 +73,83 @@ struct imsg {
struct imsg_hdr hdr; struct imsg_hdr hdr;
int fd; int fd;
void *data; void *data;
struct ibuf *buf;
}; };
struct iovec;
/* buffer.c */ /* imsg-buffer.c */
struct ibuf *ibuf_open(size_t); struct ibuf *ibuf_open(size_t);
struct ibuf *ibuf_dynamic(size_t, size_t); struct ibuf *ibuf_dynamic(size_t, size_t);
int ibuf_add(struct ibuf *, const void *, size_t); int ibuf_add(struct ibuf *, const void *, size_t);
int ibuf_add_buf(struct ibuf *, const struct ibuf *);
int ibuf_add_ibuf(struct ibuf *, const struct ibuf *);
int ibuf_add_zero(struct ibuf *, size_t);
int ibuf_add_n8(struct ibuf *, uint64_t);
int ibuf_add_n16(struct ibuf *, uint64_t);
int ibuf_add_n32(struct ibuf *, uint64_t);
int ibuf_add_n64(struct ibuf *, uint64_t);
int ibuf_add_h16(struct ibuf *, uint64_t);
int ibuf_add_h32(struct ibuf *, uint64_t);
int ibuf_add_h64(struct ibuf *, uint64_t);
void *ibuf_reserve(struct ibuf *, size_t); void *ibuf_reserve(struct ibuf *, size_t);
void *ibuf_seek(struct ibuf *, size_t, size_t); void *ibuf_seek(struct ibuf *, size_t, size_t);
size_t ibuf_size(struct ibuf *); int ibuf_set(struct ibuf *, size_t, const void *, size_t);
size_t ibuf_left(struct ibuf *); int ibuf_set_n8(struct ibuf *, size_t, uint64_t);
int ibuf_set_n16(struct ibuf *, size_t, uint64_t);
int ibuf_set_n32(struct ibuf *, size_t, uint64_t);
int ibuf_set_n64(struct ibuf *, size_t, uint64_t);
int ibuf_set_h16(struct ibuf *, size_t, uint64_t);
int ibuf_set_h32(struct ibuf *, size_t, uint64_t);
int ibuf_set_h64(struct ibuf *, size_t, uint64_t);
void *ibuf_data(const struct ibuf *);
size_t ibuf_size(const struct ibuf *);
size_t ibuf_left(const struct ibuf *);
int ibuf_truncate(struct ibuf *, size_t);
void ibuf_rewind(struct ibuf *);
void ibuf_close(struct msgbuf *, struct ibuf *); void ibuf_close(struct msgbuf *, struct ibuf *);
int ibuf_write(struct msgbuf *); void ibuf_from_buffer(struct ibuf *, void *, size_t);
void ibuf_from_ibuf(struct ibuf *, const struct ibuf *);
int ibuf_get(struct ibuf *, void *, size_t);
int ibuf_get_ibuf(struct ibuf *, size_t, struct ibuf *);
int ibuf_get_n8(struct ibuf *, uint8_t *);
int ibuf_get_n16(struct ibuf *, uint16_t *);
int ibuf_get_n32(struct ibuf *, uint32_t *);
int ibuf_get_n64(struct ibuf *, uint64_t *);
int ibuf_get_h16(struct ibuf *, uint16_t *);
int ibuf_get_h32(struct ibuf *, uint32_t *);
int ibuf_get_h64(struct ibuf *, uint64_t *);
int ibuf_skip(struct ibuf *, size_t);
void ibuf_free(struct ibuf *); void ibuf_free(struct ibuf *);
int ibuf_fd_avail(struct ibuf *);
int ibuf_fd_get(struct ibuf *);
void ibuf_fd_set(struct ibuf *, int);
int ibuf_write(struct msgbuf *);
void msgbuf_init(struct msgbuf *); void msgbuf_init(struct msgbuf *);
void msgbuf_clear(struct msgbuf *); void msgbuf_clear(struct msgbuf *);
uint32_t msgbuf_queuelen(struct msgbuf *);
int msgbuf_write(struct msgbuf *); int msgbuf_write(struct msgbuf *);
void msgbuf_drain(struct msgbuf *, size_t);
/* imsg.c */ /* imsg.c */
void imsg_init(struct imsgbuf *, int); void imsg_init(struct imsgbuf *, int);
ssize_t imsg_read(struct imsgbuf *); ssize_t imsg_read(struct imsgbuf *);
ssize_t imsg_get(struct imsgbuf *, struct imsg *); ssize_t imsg_get(struct imsgbuf *, struct imsg *);
int imsg_get_ibuf(struct imsg *, struct ibuf *);
int imsg_get_data(struct imsg *, void *, size_t);
int imsg_get_fd(struct imsg *);
uint32_t imsg_get_id(struct imsg *);
size_t imsg_get_len(struct imsg *);
pid_t imsg_get_pid(struct imsg *);
uint32_t imsg_get_type(struct imsg *);
int imsg_forward(struct imsgbuf *, struct imsg *);
int imsg_compose(struct imsgbuf *, uint32_t, uint32_t, pid_t, int, int imsg_compose(struct imsgbuf *, uint32_t, uint32_t, pid_t, int,
const void *, uint16_t); const void *, size_t);
int imsg_composev(struct imsgbuf *, uint32_t, uint32_t, pid_t, int, int imsg_composev(struct imsgbuf *, uint32_t, uint32_t, pid_t, int,
const struct iovec *, int); const struct iovec *, int);
struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, uint16_t); int imsg_compose_ibuf(struct imsgbuf *, uint32_t, uint32_t, pid_t,
int imsg_add(struct ibuf *, const void *, uint16_t); struct ibuf *);
struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, size_t);
int imsg_add(struct ibuf *, const void *, size_t);
void imsg_close(struct imsgbuf *, struct ibuf *); void imsg_close(struct imsgbuf *, struct ibuf *);
void imsg_free(struct imsg *); void imsg_free(struct imsg *);
int imsg_flush(struct imsgbuf *); int imsg_flush(struct imsgbuf *);

30
compat/ntohll.c Normal file
View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2024 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include "compat.h"
uint64_t
ntohll(uint64_t v)
{
uint32_t b;
uint32_t t;
b = ntohl (v & 0xffffffff);
t = ntohl (v >> 32);
return ((uint64_t)b << 32 | t);
}

View File

@@ -19,17 +19,28 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/un.h> #include <sys/un.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-daemon.h> #include <systemd/sd-daemon.h>
#include <systemd/sd-login.h>
#include <systemd/sd-id128.h>
#include <string.h>
#include "tmux.h" #include "tmux.h"
int
systemd_activated(void)
{
return (sd_listen_fds(0) >= 1);
}
int int
systemd_create_socket(int flags, char **cause) systemd_create_socket(int flags, char **cause)
{ {
int fds; int fds;
int fd; int fd;
struct sockaddr_un sa; struct sockaddr_un sa;
int addrlen = sizeof sa; socklen_t addrlen = sizeof sa;
fds = sd_listen_fds(0); fds = sd_listen_fds(0);
if (fds > 1) { /* too many file descriptors */ if (fds > 1) { /* too many file descriptors */
@@ -56,3 +67,149 @@ fail:
xasprintf(cause, "systemd socket error (%s)", strerror(errno)); xasprintf(cause, "systemd socket error (%s)", strerror(errno));
return (-1); return (-1);
} }
int
systemd_move_pid_to_new_cgroup(pid_t pid, char **cause)
{
sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus_message *m = NULL, *reply = NULL;
sd_bus *bus = NULL;
char *name, *desc, *slice;
sd_id128_t uuid;
int r;
pid_t parent_pid;
/* Connect to the session bus. */
r = sd_bus_default_user(&bus);
if (r < 0) {
xasprintf(cause, "failed to connect to session bus: %s",
strerror(-r));
goto finish;
}
/* Start building the method call. */
r = sd_bus_message_new_method_call(bus, &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartTransientUnit");
if (r < 0) {
xasprintf(cause, "failed to create bus message: %s",
strerror(-r));
goto finish;
}
/* Generate a unique name for the new scope, to avoid collisions. */
r = sd_id128_randomize(&uuid);
if (r < 0) {
xasprintf(cause, "failed to generate uuid: %s", strerror(-r));
goto finish;
}
xasprintf(&name, "tmux-spawn-" SD_ID128_UUID_FORMAT_STR ".scope",
SD_ID128_FORMAT_VAL(uuid));
r = sd_bus_message_append(m, "s", name);
free(name);
if (r < 0) {
xasprintf(cause, "failed to append to bus message: %s",
strerror(-r));
goto finish;
}
/* Mode: fail if there's a queued unit with the same name. */
r = sd_bus_message_append(m, "s", "fail");
if (r < 0) {
xasprintf(cause, "failed to append to bus message: %s",
strerror(-r));
goto finish;
}
/* Start properties array. */
r = sd_bus_message_open_container(m, 'a', "(sv)");
if (r < 0) {
xasprintf(cause, "failed to start properties array: %s",
strerror(-r));
goto finish;
}
parent_pid = getpid();
xasprintf(&desc, "tmux child pane %ld launched by process %ld",
(long)pid, (long)parent_pid);
r = sd_bus_message_append(m, "(sv)", "Description", "s", desc);
free(desc);
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
/*
* Inherit the slice from the parent process, or default to
* "app-tmux.slice" if that fails.
*/
r = sd_pid_get_user_slice(parent_pid, &slice);
if (r < 0) {
slice = xstrdup("app-tmux.slice");
}
r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
free(slice);
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
/* PIDs to add to the scope: length - 1 array of uint32_t. */
r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
/* Clean up the scope even if it fails. */
r = sd_bus_message_append(m, "(sv)", "CollectMode", "s",
"inactive-or-failed");
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
/* End properties array. */
r = sd_bus_message_close_container(m);
if (r < 0) {
xasprintf(cause, "failed to end properties array: %s",
strerror(-r));
goto finish;
}
/* aux is currently unused and should be passed an empty array. */
r = sd_bus_message_append(m, "a(sa(sv))", 0);
if (r < 0) {
xasprintf(cause, "failed to append to bus message: %s",
strerror(-r));
goto finish;
}
/* Call the method with a timeout of 1 second = 1e6 us. */
r = sd_bus_call(bus, m, 1000000, &error, &reply);
if (r < 0) {
if (error.message != NULL) {
/* We have a specific error message from sd-bus. */
xasprintf(cause, "StartTransientUnit call failed: %s",
error.message);
} else {
xasprintf(cause, "StartTransientUnit call failed: %s",
strerror(-r));
}
goto finish;
}
finish:
sd_bus_error_free(&error);
sd_bus_message_unref(m);
sd_bus_message_unref(reply);
sd_bus_unref(bus);
return (r);
}

View File

@@ -1,6 +1,6 @@
# configure.ac # configure.ac
AC_INIT([tmux], 3.3a) AC_INIT([tmux], 3.4)
AC_PREREQ([2.60]) AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc) AC_CONFIG_AUX_DIR(etc)
@@ -44,7 +44,7 @@ fi
# Set up the compiler in two different ways and say yes we may want to install. # Set up the compiler in two different ways and say yes we may want to install.
AC_PROG_CC AC_PROG_CC
AM_PROG_CC_C_O AM_PROG_CC_C_O
AC_PROG_CC_C99 m4_version_prereq(2.70, [AC_PROG_CC], [AC_PROG_CC_C99])
AC_PROG_CPP AC_PROG_CPP
AC_PROG_EGREP AC_PROG_EGREP
AC_PROG_INSTALL AC_PROG_INSTALL
@@ -148,7 +148,7 @@ AC_CHECK_FUNCS([ \
prctl \ prctl \
proc_pidinfo \ proc_pidinfo \
getpeerucred \ getpeerucred \
sysconf \ sysconf
]) ])
# Check for functions with a compatibility implementation. # Check for functions with a compatibility implementation.
@@ -165,7 +165,9 @@ AC_REPLACE_FUNCS([ \
getpeereid \ getpeereid \
getline \ getline \
getprogname \ getprogname \
htonll \
memmem \ memmem \
ntohll \
setenv \ setenv \
setproctitle \ setproctitle \
strcasestr \ strcasestr \
@@ -267,6 +269,12 @@ if test "x$found_libevent" = xno; then
AC_MSG_ERROR("libevent not found") AC_MSG_ERROR("libevent not found")
fi fi
# Look for yacc.
AC_CHECK_PROG(found_yacc, $YACC, yes, no)
if test "x$found_yacc" = xno; then
AC_MSG_ERROR("yacc not found")
fi
# Look for ncurses or curses. Try pkg-config first then directly for the # Look for ncurses or curses. Try pkg-config first then directly for the
# library. # library.
PKG_CHECK_MODULES( PKG_CHECK_MODULES(
@@ -309,7 +317,7 @@ fi
if test "x$found_ncurses" = xno; then if test "x$found_ncurses" = xno; then
AC_SEARCH_LIBS( AC_SEARCH_LIBS(
setupterm, setupterm,
[tinfo ncurses ncursesw], [tinfo terminfo ncurses ncursesw],
found_ncurses=yes, found_ncurses=yes,
found_ncurses=no found_ncurses=no
) )
@@ -344,6 +352,10 @@ else
AC_MSG_ERROR("curses not found") AC_MSG_ERROR("curses not found")
fi fi
fi fi
AC_CHECK_FUNCS([ \
tiparm \
tiparm_s \
])
# Look for utempter. # Look for utempter.
AC_ARG_ENABLE( AC_ARG_ENABLE(
@@ -373,6 +385,15 @@ AC_ARG_ENABLE(
AS_HELP_STRING(--enable-utf8proc, use utf8proc if it is installed) AS_HELP_STRING(--enable-utf8proc, use utf8proc if it is installed)
) )
if test "x$enable_utf8proc" = xyes; then if test "x$enable_utf8proc" = xyes; then
PKG_CHECK_MODULES(
LIBUTF8PROC,
libutf8proc,
[
AM_CPPFLAGS="$LIBUTF8PROC_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBUTF8PROC_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBUTF8PROC_LIBS $LIBS"
]
)
AC_CHECK_HEADER(utf8proc.h, enable_utf8proc=yes, enable_utf8proc=no) AC_CHECK_HEADER(utf8proc.h, enable_utf8proc=yes, enable_utf8proc=no)
if test "x$enable_utf8proc" = xyes; then if test "x$enable_utf8proc" = xyes; then
AC_SEARCH_LIBS( AC_SEARCH_LIBS(
@@ -414,10 +435,35 @@ if test x"$enable_systemd" = xyes; then
fi fi
fi fi
AM_CONDITIONAL(HAVE_SYSTEMD, [test "x$found_systemd" = xyes]) AM_CONDITIONAL(HAVE_SYSTEMD, [test "x$found_systemd" = xyes])
AC_ARG_ENABLE(
cgroups,
AS_HELP_STRING(--disable-cgroups, disable adding panes to new cgroups with systemd)
)
if test "x$enable_cgroups" = x; then
# Default to the same as $enable_systemd.
enable_cgroups=$enable_systemd
fi
if test "x$enable_cgroups" = xyes; then
if test "x$found_systemd" = xyes; then
AC_DEFINE(ENABLE_CGROUPS)
else
AC_MSG_ERROR("cgroups requires systemd to be enabled")
fi
fi
# Enable sixel support.
AC_ARG_ENABLE(
sixel,
AS_HELP_STRING(--enable-sixel, enable sixel images)
)
if test "x$enable_sixel" = xyes; then
AC_DEFINE(ENABLE_SIXEL)
fi
AM_CONDITIONAL(ENABLE_SIXEL, [test "x$enable_sixel" = xyes])
# Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well. # Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well.
AC_MSG_CHECKING(for b64_ntop) AC_MSG_CHECKING(for b64_ntop)
AC_LINK_IFELSE([AC_LANG_PROGRAM( AC_LINK_IFELSE([AC_LANG_PROGRAM(
[ [
#include <sys/types.h> #include <sys/types.h>
#include <netinet/in.h> #include <netinet/in.h>
@@ -915,6 +961,19 @@ AM_CONDITIONAL(IS_HPUX, test "x$PLATFORM" = xhpux)
AM_CONDITIONAL(IS_HAIKU, test "x$PLATFORM" = xhaiku) AM_CONDITIONAL(IS_HAIKU, test "x$PLATFORM" = xhaiku)
AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown) AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown)
# Set the default lock command
DEFAULT_LOCK_CMD="lock -np"
AC_MSG_CHECKING(lock-command)
if test "x$PLATFORM" = xlinux; then
AC_CHECK_PROG(found_vlock, vlock, yes, no)
if test "x$found_vlock" = xyes; then
DEFAULT_LOCK_CMD="vlock"
fi
fi
AC_MSG_RESULT($DEFAULT_LOCK_CMD)
AC_SUBST(DEFAULT_LOCK_CMD)
# Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user # Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user
# variables. # variables.
AC_SUBST(AM_CPPFLAGS) AC_SUBST(AM_CPPFLAGS)

View File

@@ -234,3 +234,29 @@ control_notify_session_window_changed(struct session *s)
s->curw->window->id); s->curw->window->id);
} }
} }
void
control_notify_paste_buffer_changed(const char *name)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%paste-buffer-changed %s", name);
}
}
void
control_notify_paste_buffer_deleted(const char *name)
{
struct client *c;
TAILQ_FOREACH(c, &clients, entry) {
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
continue;
control_write(c, "%%paste-buffer-deleted %s", name);
}
}

View File

@@ -775,13 +775,16 @@ control_start(struct client *c)
cs->read_event = bufferevent_new(c->fd, control_read_callback, cs->read_event = bufferevent_new(c->fd, control_read_callback,
control_write_callback, control_error_callback, c); control_write_callback, control_error_callback, c);
bufferevent_enable(cs->read_event, EV_READ); if (cs->read_event == NULL)
fatalx("out of memory");
if (c->flags & CLIENT_CONTROLCONTROL) if (c->flags & CLIENT_CONTROLCONTROL)
cs->write_event = cs->read_event; cs->write_event = cs->read_event;
else { else {
cs->write_event = bufferevent_new(c->out_fd, NULL, cs->write_event = bufferevent_new(c->out_fd, NULL,
control_write_callback, control_error_callback, c); control_write_callback, control_error_callback, c);
if (cs->write_event == NULL)
fatalx("out of memory");
} }
bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW, bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
0); 0);
@@ -792,6 +795,13 @@ control_start(struct client *c)
} }
} }
/* Control client ready. */
void
control_ready(struct client *c)
{
bufferevent_enable(c->control_state->read_event, EV_READ);
}
/* Discard all output for a client. */ /* Discard all output for a client. */
void void
control_discard(struct client *c) control_discard(struct client *c)

View File

@@ -182,9 +182,11 @@ void
environ_update(struct options *oo, struct environ *src, struct environ *dst) environ_update(struct options *oo, struct environ *src, struct environ *dst)
{ {
struct environ_entry *envent; struct environ_entry *envent;
struct environ_entry *envent1;
struct options_entry *o; struct options_entry *o;
struct options_array_item *a; struct options_array_item *a;
union options_value *ov; union options_value *ov;
int found;
o = options_get(oo, "update-environment"); o = options_get(oo, "update-environment");
if (o == NULL) if (o == NULL)
@@ -192,14 +194,15 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst)
a = options_array_first(o); a = options_array_first(o);
while (a != NULL) { while (a != NULL) {
ov = options_array_item_value(a); ov = options_array_item_value(a);
RB_FOREACH(envent, environ, src) { found = 0;
if (fnmatch(ov->string, envent->name, 0) == 0) RB_FOREACH_SAFE(envent, environ, src, envent1) {
break; if (fnmatch(ov->string, envent->name, 0) == 0) {
environ_set(dst, envent->name, 0, "%s", envent->value);
found = 1;
}
} }
if (envent == NULL) if (!found)
environ_clear(dst, ov->string); environ_clear(dst, ov->string);
else
environ_set(dst, envent->name, 0, "%s", envent->value);
a = options_array_next(a); a = options_array_next(a);
} }
} }

54
file.c
View File

@@ -149,7 +149,8 @@ file_fire_done_cb(__unused int fd, __unused short events, void *arg)
struct client_file *cf = arg; struct client_file *cf = arg;
struct client *c = cf->c; struct client *c = cf->c;
if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD))) if (cf->cb != NULL &&
(cf->closed || c == NULL || (~c->flags & CLIENT_DEAD)))
cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data); cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
file_free(cf); file_free(cf);
} }
@@ -173,9 +174,9 @@ file_fire_read(struct client_file *cf)
int int
file_can_print(struct client *c) file_can_print(struct client *c)
{ {
if (c == NULL) if (c == NULL ||
return (0); (c->flags & CLIENT_ATTACHED) ||
if (c->session != NULL && (~c->flags & CLIENT_CONTROL)) (c->flags & CLIENT_CONTROL))
return (0); return (0);
return (1); return (1);
} }
@@ -352,7 +353,7 @@ done:
} }
/* Read a file. */ /* Read a file. */
void struct client_file *
file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata) file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
{ {
struct client_file *cf; struct client_file *cf;
@@ -420,10 +421,27 @@ skip:
goto done; goto done;
} }
free(msg); free(msg);
return; return cf;
done: done:
file_fire_done(cf); file_fire_done(cf);
return NULL;
}
/* Cancel a file read. */
void
file_cancel(struct client_file *cf)
{
struct msg_read_cancel msg;
log_debug("read cancel file %d", cf->stream);
if (cf->closed)
return;
cf->closed = 1;
msg.stream = cf->stream;
proc_send(cf->peer, MSG_READ_CANCEL, -1, &msg, sizeof msg);
} }
/* Push event, fired if there is more writing to be done. */ /* Push event, fired if there is more writing to be done. */
@@ -585,6 +603,8 @@ file_write_open(struct client_files *files, struct tmuxpeer *peer,
cf->event = bufferevent_new(cf->fd, NULL, file_write_callback, cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
file_write_error_callback, cf); file_write_error_callback, cf);
if (cf->event == NULL)
fatalx("out of memory");
bufferevent_enable(cf->event, EV_WRITE); bufferevent_enable(cf->event, EV_WRITE);
goto reply; goto reply;
@@ -744,6 +764,8 @@ file_read_open(struct client_files *files, struct tmuxpeer *peer,
cf->event = bufferevent_new(cf->fd, file_read_callback, NULL, cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
file_read_error_callback, cf); file_read_error_callback, cf);
if (cf->event == NULL)
fatalx("out of memory");
bufferevent_enable(cf->event, EV_READ); bufferevent_enable(cf->event, EV_READ);
return; return;
@@ -753,6 +775,24 @@ reply:
proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply); proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
} }
/* Handle a read cancel message (client). */
void
file_read_cancel(struct client_files *files, struct imsg *imsg)
{
struct msg_read_cancel *msg = imsg->data;
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
struct client_file find, *cf;
if (msglen != sizeof *msg)
fatalx("bad MSG_READ_CANCEL size");
find.stream = msg->stream;
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
fatalx("unknown stream number");
log_debug("cancel file %d", cf->stream);
file_read_error_callback(NULL, 0, cf);
}
/* Handle a write ready message (server). */ /* Handle a write ready message (server). */
void void
file_write_ready(struct client_files *files, struct imsg *imsg) file_write_ready(struct client_files *files, struct imsg *imsg)
@@ -790,7 +830,7 @@ file_read_data(struct client_files *files, struct imsg *imsg)
return; return;
log_debug("file %d read %zu bytes", cf->stream, bsize); log_debug("file %d read %zu bytes", cf->stream, bsize);
if (cf->error == 0) { if (cf->error == 0 && !cf->closed) {
if (evbuffer_add(cf->buffer, bdata, bsize) != 0) { if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
cf->error = ENOMEM; cf->error = ENOMEM;
file_fire_done(cf); file_fire_done(cf);

View File

@@ -33,6 +33,7 @@ struct format_range {
enum style_range_type type; enum style_range_type type;
u_int argument; u_int argument;
char string[16];
TAILQ_ENTRY(format_range) entry; TAILQ_ENTRY(format_range) entry;
}; };
@@ -44,9 +45,18 @@ format_is_type(struct format_range *fr, struct style *sy)
{ {
if (fr->type != sy->range_type) if (fr->type != sy->range_type)
return (0); return (0);
if (fr->type == STYLE_RANGE_WINDOW && switch (fr->type) {
fr->argument != sy->range_argument) case STYLE_RANGE_NONE:
return (0); case STYLE_RANGE_LEFT:
case STYLE_RANGE_RIGHT:
return (1);
case STYLE_RANGE_PANE:
case STYLE_RANGE_WINDOW:
case STYLE_RANGE_SESSION:
return (fr->argument == sy->range_argument);
case STYLE_RANGE_USER:
return (strcmp(fr->string, sy->range_string) == 0);
}
return (1); return (1);
} }
@@ -942,6 +952,8 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
fr->type = sy.range_type; fr->type = sy.range_type;
fr->argument = sy.range_argument; fr->argument = sy.range_argument;
strlcpy(fr->string, sy.range_string,
sizeof fr->string);
} }
} }
@@ -1013,13 +1025,39 @@ format_draw(struct screen_write_ctx *octx, const struct grid_cell *base,
sr = xcalloc(1, sizeof *sr); sr = xcalloc(1, sizeof *sr);
sr->type = fr->type; sr->type = fr->type;
sr->argument = fr->argument; sr->argument = fr->argument;
strlcpy(sr->string, fr->string, sizeof sr->string);
sr->start = fr->start; sr->start = fr->start;
sr->end = fr->end; sr->end = fr->end;
TAILQ_INSERT_TAIL(srs, sr, entry); TAILQ_INSERT_TAIL(srs, sr, entry);
log_debug("%s: range %d|%u at %u-%u", __func__, sr->type, switch (sr->type) {
sr->argument, sr->start, sr->end); case STYLE_RANGE_NONE:
break;
case STYLE_RANGE_LEFT:
log_debug("%s: range left at %u-%u", __func__,
sr->start, sr->end);
break;
case STYLE_RANGE_RIGHT:
log_debug("%s: range right at %u-%u", __func__,
sr->start, sr->end);
break;
case STYLE_RANGE_PANE:
log_debug("%s: range pane|%%%u at %u-%u", __func__,
sr->argument, sr->start, sr->end);
break;
case STYLE_RANGE_WINDOW:
log_debug("%s: range window|%u at %u-%u", __func__,
sr->argument, sr->start, sr->end);
break;
case STYLE_RANGE_SESSION:
log_debug("%s: range session|$%u at %u-%u", __func__,
sr->argument, sr->start, sr->end);
break;
case STYLE_RANGE_USER:
log_debug("%s: range user|%u at %u-%u", __func__,
sr->argument, sr->start, sr->end);
break;
}
format_free_range(&frs, fr); format_free_range(&frs, fr);
} }
@@ -1083,7 +1121,7 @@ format_trim_left(const char *expanded, u_int limit)
struct utf8_data ud; struct utf8_data ud;
enum utf8_state more; enum utf8_state more;
out = copy = xcalloc(1, strlen(expanded) + 1); out = copy = xcalloc(2, strlen(expanded) + 1);
while (*cp != '\0') { while (*cp != '\0') {
if (width >= limit) if (width >= limit)
break; break;
@@ -1150,7 +1188,7 @@ format_trim_right(const char *expanded, u_int limit)
return (xstrdup(expanded)); return (xstrdup(expanded));
skip = total_width - limit; skip = total_width - limit;
out = copy = xcalloc(1, strlen(expanded) + 1); out = copy = xcalloc(2, strlen(expanded) + 1);
while (*cp != '\0') { while (*cp != '\0') {
if (*cp == '#') { if (*cp == '#') {
end = format_leading_hashes(cp, &n, &leading_width); end = format_leading_hashes(cp, &n, &leading_width);

261
format.c
View File

@@ -103,6 +103,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2)
#define FORMAT_SESSION_NAME 0x8000 #define FORMAT_SESSION_NAME 0x8000
#define FORMAT_CHARACTER 0x10000 #define FORMAT_CHARACTER 0x10000
#define FORMAT_COLOUR 0x20000 #define FORMAT_COLOUR 0x20000
#define FORMAT_CLIENTS 0x40000
/* Limit on recursion. */ /* Limit on recursion. */
#define FORMAT_LOOP_LIMIT 100 #define FORMAT_LOOP_LIMIT 100
@@ -1125,7 +1126,6 @@ format_cb_mouse_word(struct format_tree *ft)
struct window_pane *wp; struct window_pane *wp;
struct grid *gd; struct grid *gd;
u_int x, y; u_int x, y;
char *s;
if (!ft->m.valid) if (!ft->m.valid)
return (NULL); return (NULL);
@@ -1138,13 +1138,32 @@ format_cb_mouse_word(struct format_tree *ft)
if (!TAILQ_EMPTY(&wp->modes)) { if (!TAILQ_EMPTY(&wp->modes)) {
if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode || if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_mode ||
TAILQ_FIRST(&wp->modes)->mode == &window_view_mode) TAILQ_FIRST(&wp->modes)->mode == &window_view_mode)
return (s = window_copy_get_word(wp, x, y)); return (window_copy_get_word(wp, x, y));
return (NULL); return (NULL);
} }
gd = wp->base.grid; gd = wp->base.grid;
return (format_grid_word(gd, x, gd->hsize + y)); return (format_grid_word(gd, x, gd->hsize + y));
} }
/* Callback for mouse_hyperlink. */
static void *
format_cb_mouse_hyperlink(struct format_tree *ft)
{
struct window_pane *wp;
struct grid *gd;
u_int x, y;
if (!ft->m.valid)
return (NULL);
wp = cmd_mouse_pane(&ft->m, NULL, NULL);
if (wp == NULL)
return (NULL);
if (cmd_mouse_at(wp, &ft->m, &x, &y, 0) != 0)
return (NULL);
gd = wp->base.grid;
return (format_grid_hyperlink(gd, x, gd->hsize + y, wp->screen));
}
/* Callback for mouse_line. */ /* Callback for mouse_line. */
static void * static void *
format_cb_mouse_line(struct format_tree *ft) format_cb_mouse_line(struct format_tree *ft)
@@ -1171,6 +1190,72 @@ format_cb_mouse_line(struct format_tree *ft)
return (format_grid_line(gd, gd->hsize + y)); return (format_grid_line(gd, gd->hsize + y));
} }
/* Callback for mouse_status_line. */
static void *
format_cb_mouse_status_line(struct format_tree *ft)
{
char *value;
u_int y;
if (!ft->m.valid)
return (NULL);
if (ft->c == NULL || (~ft->c->tty.flags & TTY_STARTED))
return (NULL);
if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines) {
y = ft->m.y;
} else if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat) {
y = ft->m.y - ft->m.statusat;
} else
return (NULL);
xasprintf(&value, "%u", y);
return (value);
}
/* Callback for mouse_status_range. */
static void *
format_cb_mouse_status_range(struct format_tree *ft)
{
struct style_range *sr;
u_int x, y;
if (!ft->m.valid)
return (NULL);
if (ft->c == NULL || (~ft->c->tty.flags & TTY_STARTED))
return (NULL);
if (ft->m.statusat == 0 && ft->m.y < ft->m.statuslines) {
x = ft->m.x;
y = ft->m.y;
} else if (ft->m.statusat > 0 && ft->m.y >= (u_int)ft->m.statusat) {
x = ft->m.x;
y = ft->m.y - ft->m.statusat;
} else
return (NULL);
sr = status_get_range(ft->c, x, y);
if (sr == NULL)
return (NULL);
switch (sr->type) {
case STYLE_RANGE_NONE:
return (NULL);
case STYLE_RANGE_LEFT:
return (xstrdup("left"));
case STYLE_RANGE_RIGHT:
return (xstrdup("right"));
case STYLE_RANGE_PANE:
return (xstrdup("pane"));
case STYLE_RANGE_WINDOW:
return (xstrdup("window"));
case STYLE_RANGE_SESSION:
return (xstrdup("session"));
case STYLE_RANGE_USER:
return (xstrdup(sr->string));
}
return (NULL);
}
/* Callback for alternate_on. */ /* Callback for alternate_on. */
static void * static void *
format_cb_alternate_on(struct format_tree *ft) format_cb_alternate_on(struct format_tree *ft)
@@ -1865,12 +1950,24 @@ format_cb_pane_input_off(struct format_tree *ft)
return (NULL); return (NULL);
} }
/* Callback for pane_unseen_changes. */
static void *
format_cb_pane_unseen_changes(struct format_tree *ft)
{
if (ft->wp != NULL) {
if (ft->wp->flags & PANE_UNSEENCHANGES)
return (xstrdup("1"));
return (xstrdup("0"));
}
return (NULL);
}
/* Callback for pane_last. */ /* Callback for pane_last. */
static void * static void *
format_cb_pane_last(struct format_tree *ft) format_cb_pane_last(struct format_tree *ft)
{ {
if (ft->wp != NULL) { if (ft->wp != NULL) {
if (ft->wp == ft->wp->window->last) if (ft->wp == TAILQ_FIRST(&ft->wp->window->last_panes))
return (xstrdup("1")); return (xstrdup("1"));
return (xstrdup("0")); return (xstrdup("0"));
} }
@@ -2045,6 +2142,18 @@ format_cb_scroll_region_upper(struct format_tree *ft)
return (NULL); return (NULL);
} }
/* Callback for server_sessions. */
static void *
format_cb_server_sessions(__unused struct format_tree *ft)
{
struct session *s;
u_int n = 0;
RB_FOREACH(s, sessions, &sessions)
n++;
return (format_printf("%u", n));
}
/* Callback for session_attached. */ /* Callback for session_attached. */
static void * static void *
format_cb_session_attached(struct format_tree *ft) format_cb_session_attached(struct format_tree *ft)
@@ -2789,6 +2898,9 @@ static const struct format_table_entry format_table[] = {
{ "mouse_button_flag", FORMAT_TABLE_STRING, { "mouse_button_flag", FORMAT_TABLE_STRING,
format_cb_mouse_button_flag format_cb_mouse_button_flag
}, },
{ "mouse_hyperlink", FORMAT_TABLE_STRING,
format_cb_mouse_hyperlink
},
{ "mouse_line", FORMAT_TABLE_STRING, { "mouse_line", FORMAT_TABLE_STRING,
format_cb_mouse_line format_cb_mouse_line
}, },
@@ -2801,6 +2913,12 @@ static const struct format_table_entry format_table[] = {
{ "mouse_standard_flag", FORMAT_TABLE_STRING, { "mouse_standard_flag", FORMAT_TABLE_STRING,
format_cb_mouse_standard_flag format_cb_mouse_standard_flag
}, },
{ "mouse_status_line", FORMAT_TABLE_STRING,
format_cb_mouse_status_line
},
{ "mouse_status_range", FORMAT_TABLE_STRING,
format_cb_mouse_status_range
},
{ "mouse_utf8_flag", FORMAT_TABLE_STRING, { "mouse_utf8_flag", FORMAT_TABLE_STRING,
format_cb_mouse_utf8_flag format_cb_mouse_utf8_flag
}, },
@@ -2930,6 +3048,9 @@ static const struct format_table_entry format_table[] = {
{ "pane_tty", FORMAT_TABLE_STRING, { "pane_tty", FORMAT_TABLE_STRING,
format_cb_pane_tty format_cb_pane_tty
}, },
{ "pane_unseen_changes", FORMAT_TABLE_STRING,
format_cb_pane_unseen_changes
},
{ "pane_width", FORMAT_TABLE_STRING, { "pane_width", FORMAT_TABLE_STRING,
format_cb_pane_width format_cb_pane_width
}, },
@@ -2942,6 +3063,9 @@ static const struct format_table_entry format_table[] = {
{ "scroll_region_upper", FORMAT_TABLE_STRING, { "scroll_region_upper", FORMAT_TABLE_STRING,
format_cb_scroll_region_upper format_cb_scroll_region_upper
}, },
{ "server_sessions", FORMAT_TABLE_STRING,
format_cb_server_sessions
},
{ "session_activity", FORMAT_TABLE_TIME, { "session_activity", FORMAT_TABLE_TIME,
format_cb_session_activity format_cb_session_activity
}, },
@@ -3387,12 +3511,12 @@ format_quote_style(const char *s)
} }
/* Make a prettier time. */ /* Make a prettier time. */
static char * char *
format_pretty_time(time_t t) format_pretty_time(time_t t, int seconds)
{ {
struct tm now_tm, tm; struct tm now_tm, tm;
time_t now, age; time_t now, age;
char s[6]; char s[9];
time(&now); time(&now);
if (now < t) if (now < t)
@@ -3404,7 +3528,10 @@ format_pretty_time(time_t t)
/* Last 24 hours. */ /* Last 24 hours. */
if (age < 24 * 3600) { if (age < 24 * 3600) {
strftime(s, sizeof s, "%H:%M", &tm); if (seconds)
strftime(s, sizeof s, "%H:%M:%S", &tm);
else
strftime(s, sizeof s, "%H:%M", &tm);
return (xstrdup(s)); return (xstrdup(s));
} }
@@ -3509,7 +3636,7 @@ found:
if (t == 0) if (t == 0)
return (NULL); return (NULL);
if (modifiers & FORMAT_PRETTY) if (modifiers & FORMAT_PRETTY)
found = format_pretty_time(t); found = format_pretty_time(t, 0);
else { else {
if (time_format != NULL) { if (time_format != NULL) {
localtime_r(&t, &tm); localtime_r(&t, &tm);
@@ -3539,18 +3666,43 @@ found:
} }
if (modifiers & FORMAT_QUOTE_SHELL) { if (modifiers & FORMAT_QUOTE_SHELL) {
saved = found; saved = found;
found = xstrdup(format_quote_shell(saved)); found = format_quote_shell(saved);
free(saved); free(saved);
} }
if (modifiers & FORMAT_QUOTE_STYLE) { if (modifiers & FORMAT_QUOTE_STYLE) {
saved = found; saved = found;
found = xstrdup(format_quote_style(saved)); found = format_quote_style(saved);
free(saved); free(saved);
} }
return (found); return (found);
} }
/* Remove escaped characters from string. */ /* Unescape escaped characters. */
static char *
format_unescape(const char *s)
{
char *out, *cp;
int brackets = 0;
cp = out = xmalloc(strlen(s) + 1);
for (; *s != '\0'; s++) {
if (*s == '#' && s[1] == '{')
brackets++;
if (brackets == 0 &&
*s == '#' &&
strchr(",#{}:", s[1]) != NULL) {
*cp++ = *++s;
continue;
}
if (*s == '}')
brackets--;
*cp++ = *s;
}
*cp = '\0';
return (out);
}
/* Remove escaped characters. */
static char * static char *
format_strip(const char *s) format_strip(const char *s)
{ {
@@ -3583,7 +3735,9 @@ format_skip(const char *s, const char *end)
for (; *s != '\0'; s++) { for (; *s != '\0'; s++) {
if (*s == '#' && s[1] == '{') if (*s == '#' && s[1] == '{')
brackets++; brackets++;
if (*s == '#' && strchr(",#{}:", s[1]) != NULL) { if (*s == '#' &&
s[1] != '\0' &&
strchr(",#{}:", s[1]) != NULL) {
s++; s++;
continue; continue;
} }
@@ -3697,7 +3851,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
cp++; cp++;
/* Check single character modifiers with no arguments. */ /* Check single character modifiers with no arguments. */
if (strchr("labcdnwETSWP<>", cp[0]) != NULL && if (strchr("labcdnwETSWPL<>", cp[0]) != NULL &&
format_is_end(cp[1])) { format_is_end(cp[1])) {
format_add_modifier(&list, count, cp, 1, NULL, 0); format_add_modifier(&list, count, cp, 1, NULL, 0);
cp++; cp++;
@@ -3732,7 +3886,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
argc = 0; argc = 0;
/* Single argument with no wrapper character. */ /* Single argument with no wrapper character. */
if (!ispunct(cp[1]) || cp[1] == '-') { if (!ispunct((u_char)cp[1]) || cp[1] == '-') {
end = format_skip(cp + 1, ":;"); end = format_skip(cp + 1, ":;");
if (end == NULL) if (end == NULL)
break; break;
@@ -4025,6 +4179,40 @@ format_loop_panes(struct format_expand_state *es, const char *fmt)
return (value); return (value);
} }
/* Loop over clients. */
static char *
format_loop_clients(struct format_expand_state *es, const char *fmt)
{
struct format_tree *ft = es->ft;
struct client *c;
struct cmdq_item *item = ft->item;
struct format_tree *nft;
struct format_expand_state next;
char *expanded, *value;
size_t valuelen;
value = xcalloc(1, 1);
valuelen = 1;
TAILQ_FOREACH(c, &clients, entry) {
format_log(es, "client loop: %s", c->name);
nft = format_create(c, item, 0, ft->flags);
format_defaults(nft, c, ft->s, ft->wl, ft->wp);
format_copy_state(&next, es, 0);
next.ft = nft;
expanded = format_expand1(&next, fmt);
format_free(nft);
valuelen += strlen(expanded);
value = xrealloc(value, valuelen);
strlcat(value, expanded, valuelen);
free(expanded);
}
return (value);
}
static char * static char *
format_replace_expression(struct format_modifier *mexp, format_replace_expression(struct format_modifier *mexp,
struct format_expand_state *es, const char *copy) struct format_expand_state *es, const char *copy)
@@ -4299,6 +4487,9 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
case 'P': case 'P':
modifiers |= FORMAT_PANES; modifiers |= FORMAT_PANES;
break; break;
case 'L':
modifiers |= FORMAT_CLIENTS;
break;
} }
} else if (fm->size == 2) { } else if (fm->size == 2) {
if (strcmp(fm->modifier, "||") == 0 || if (strcmp(fm->modifier, "||") == 0 ||
@@ -4313,7 +4504,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
/* Is this a literal string? */ /* Is this a literal string? */
if (modifiers & FORMAT_LITERAL) { if (modifiers & FORMAT_LITERAL) {
value = xstrdup(copy); format_log(es, "literal string is '%s'", copy);
value = format_unescape(copy);
goto done; goto done;
} }
@@ -4354,6 +4546,10 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen,
value = format_loop_panes(es, copy); value = format_loop_panes(es, copy);
if (value == NULL) if (value == NULL)
goto fail; goto fail;
} else if (modifiers & FORMAT_CLIENTS) {
value = format_loop_clients(es, copy);
if (value == NULL)
goto fail;
} else if (modifiers & FORMAT_WINDOW_NAME) { } else if (modifiers & FORMAT_WINDOW_NAME) {
value = format_window_name(es, copy); value = format_window_name(es, copy);
if (value == NULL) if (value == NULL)
@@ -4603,7 +4799,7 @@ format_expand1(struct format_expand_state *es, const char *fmt)
{ {
struct format_tree *ft = es->ft; struct format_tree *ft = es->ft;
char *buf, *out, *name; char *buf, *out, *name;
const char *ptr, *s; const char *ptr, *s, *style_end = NULL;
size_t off, len, n, outlen; size_t off, len, n, outlen;
int ch, brackets; int ch, brackets;
char expanded[8192]; char expanded[8192];
@@ -4698,18 +4894,20 @@ format_expand1(struct format_expand_state *es, const char *fmt)
break; break;
fmt += n + 1; fmt += n + 1;
continue; continue;
case '[':
case '#': case '#':
/* /*
* If ##[ (with two or more #s), then it is a style and * If ##[ (with two or more #s), then it is a style and
* can be left for format_draw to handle. * can be left for format_draw to handle.
*/ */
ptr = fmt; ptr = fmt - (ch == '[');
n = 2; n = 2 - (ch == '[');
while (*ptr == '#') { while (*ptr == '#') {
ptr++; ptr++;
n++; n++;
} }
if (*ptr == '[') { if (*ptr == '[') {
style_end = format_skip(fmt - 2, "]");
format_log(es, "found #*%zu[", n); format_log(es, "found #*%zu[", n);
while (len - off < n + 2) { while (len - off < n + 2) {
buf = xreallocarray(buf, 2, len); buf = xreallocarray(buf, 2, len);
@@ -4732,10 +4930,12 @@ format_expand1(struct format_expand_state *es, const char *fmt)
continue; continue;
default: default:
s = NULL; s = NULL;
if (ch >= 'A' && ch <= 'Z') if (fmt > style_end) { /* skip inside #[] */
s = format_upper[ch - 'A']; if (ch >= 'A' && ch <= 'Z')
else if (ch >= 'a' && ch <= 'z') s = format_upper[ch - 'A'];
s = format_lower[ch - 'a']; else if (ch >= 'a' && ch <= 'z')
s = format_lower[ch - 'a'];
}
if (s == NULL) { if (s == NULL) {
while (len - off < 3) { while (len - off < 3) {
buf = xreallocarray(buf, 2, len); buf = xreallocarray(buf, 2, len);
@@ -5057,3 +5257,20 @@ format_grid_line(struct grid *gd, u_int y)
} }
return (s); return (s);
} }
/* Return hyperlink at given coordinates. Caller frees. */
char *
format_grid_hyperlink(struct grid *gd, u_int x, u_int y, struct screen* s)
{
const char *uri;
struct grid_cell gc;
grid_get_cell(gd, x, y, &gc);
if (gc.flags & GRID_FLAG_PADDING)
return (NULL);
if (s->hyperlinks == NULL || gc.link == 0)
return (NULL);
if (!hyperlinks_get(s->hyperlinks, gc.link, &uri, NULL, NULL))
return (NULL);
return (xstrdup(uri));
}

View File

@@ -231,5 +231,5 @@ grid_view_string_cells(struct grid *gd, u_int px, u_int py, u_int nx)
px = grid_view_x(gd, px); px = grid_view_x(gd, px);
py = grid_view_y(gd, py); py = grid_view_y(gd, py);
return (grid_string_cells(gd, px, py, nx, NULL, 0, 0, 0)); return (grid_string_cells(gd, px, py, nx, NULL, 0, NULL));
} }

158
grid.c
View File

@@ -37,7 +37,7 @@
/* Default grid cell data. */ /* Default grid cell data. */
const struct grid_cell grid_default_cell = { const struct grid_cell grid_default_cell = {
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 8, 0
}; };
/* /*
@@ -45,15 +45,15 @@ const struct grid_cell grid_default_cell = {
* appears in the grid - because of this, they are always extended cells. * appears in the grid - because of this, they are always extended cells.
*/ */
static const struct grid_cell grid_padding_cell = { static const struct grid_cell grid_padding_cell = {
{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0 { { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0
}; };
/* Cleared grid cell data. */ /* Cleared grid cell data. */
static const struct grid_cell grid_cleared_cell = { static const struct grid_cell grid_cleared_cell = {
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0 { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0
}; };
static const struct grid_cell_entry grid_cleared_entry = { static const struct grid_cell_entry grid_cleared_entry = {
GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } } { .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
}; };
/* Store cell in entry. */ /* Store cell in entry. */
@@ -90,6 +90,8 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
return (1); return (1);
if (gc->us != 0) /* only supports 256 or RGB */ if (gc->us != 0) /* only supports 256 or RGB */
return (1); return (1);
if (gc->link != 0)
return (1);
return (0); return (0);
} }
@@ -131,6 +133,7 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
gee->fg = gc->fg; gee->fg = gc->fg;
gee->bg = gc->bg; gee->bg = gc->bg;
gee->us = gc->us; gee->us = gc->us;
gee->link = gc->link;
return (gee); return (gee);
} }
@@ -231,6 +234,8 @@ grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
return (0); return (0);
if (gc1->attr != gc2->attr || gc1->flags != gc2->flags) if (gc1->attr != gc2->attr || gc1->flags != gc2->flags)
return (0); return (0);
if (gc1->link != gc2->link)
return (0);
return (1); return (1);
} }
@@ -399,6 +404,7 @@ grid_scroll_history(struct grid *gd, u_int bg)
gd->hscrolled++; gd->hscrolled++;
grid_compact_line(&gd->linedata[gd->hsize]); grid_compact_line(&gd->linedata[gd->hsize]);
gd->linedata[gd->hsize].time = current_time;
gd->hsize++; gd->hsize++;
} }
@@ -438,6 +444,7 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
/* Move the line into the history. */ /* Move the line into the history. */
memcpy(gl_history, gl_upper, sizeof *gl_history); memcpy(gl_history, gl_upper, sizeof *gl_history);
gl_history->time = current_time;
/* Then move the region up and clear the bottom line. */ /* Then move the region up and clear the bottom line. */
memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper); memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
@@ -507,6 +514,7 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
gc->fg = gee->fg; gc->fg = gee->fg;
gc->bg = gee->bg; gc->bg = gee->bg;
gc->us = gee->us; gc->us = gee->us;
gc->link = gee->link;
utf8_to_data(gee->data, &gc->data); utf8_to_data(gee->data, &gc->data);
} }
return; return;
@@ -520,8 +528,9 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
gc->bg = gce->data.bg; gc->bg = gce->data.bg;
if (gce->flags & GRID_FLAG_BG256) if (gce->flags & GRID_FLAG_BG256)
gc->bg |= COLOUR_FLAG_256; gc->bg |= COLOUR_FLAG_256;
gc->us = 0; gc->us = 8;
utf8_set(&gc->data, gce->data.data); utf8_set(&gc->data, gce->data.data);
gc->link = 0;
} }
/* Get cell for reading. */ /* Get cell for reading. */
@@ -852,28 +861,60 @@ grid_string_cells_us(const struct grid_cell *gc, int *values)
/* Add on SGR code. */ /* Add on SGR code. */
static void static void
grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc, grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
int *oldc, size_t nnewc, size_t noldc, int escape_c0) int *oldc, size_t nnewc, size_t noldc, int flags)
{ {
u_int i; u_int i;
char tmp[64]; char tmp[64];
int reset = (n != 0 && s[0] == 0);
if (nnewc != 0 && if (nnewc == 0)
(nnewc != noldc || return; /* no code to add */
memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 || if (!reset &&
(n != 0 && s[0] == 0))) { nnewc == noldc &&
if (escape_c0) memcmp(newc, oldc, nnewc * sizeof newc[0]) == 0)
strlcat(buf, "\\033[", len); return; /* no reset and colour unchanged */
if (reset && (newc[0] == 49 || newc[0] == 39))
return; /* reset and colour default */
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\033[", len);
else
strlcat(buf, "\033[", len);
for (i = 0; i < nnewc; i++) {
if (i + 1 < nnewc)
xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
else else
strlcat(buf, "\033[", len); xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
for (i = 0; i < nnewc; i++) { strlcat(buf, tmp, len);
if (i + 1 < nnewc)
xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
else
xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
strlcat(buf, tmp, len);
}
strlcat(buf, "m", len);
} }
strlcat(buf, "m", len);
}
static int
grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id,
const char *uri, int flags)
{
char *tmp;
if (strlen(uri) + strlen(id) + 17 >= len)
return (0);
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\033]8;", len);
else
strlcat(buf, "\033]8;", len);
if (*id != '\0') {
xasprintf(&tmp, "id=%s;", id);
strlcat(buf, tmp, len);
free(tmp);
} else
strlcat(buf, ";", len);
strlcat(buf, uri, len);
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\033\\\\", len);
else
strlcat(buf, "\033\\", len);
return (1);
} }
/* /*
@@ -882,14 +923,16 @@ grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
*/ */
static void static void
grid_string_cells_code(const struct grid_cell *lastgc, grid_string_cells_code(const struct grid_cell *lastgc,
const struct grid_cell *gc, char *buf, size_t len, int escape_c0) const struct grid_cell *gc, char *buf, size_t len, int flags,
struct screen *sc, int *has_link)
{ {
int oldc[64], newc[64], s[128]; int oldc[64], newc[64], s[128];
size_t noldc, nnewc, n, i; size_t noldc, nnewc, n, i;
u_int attr = gc->attr, lastattr = lastgc->attr; u_int attr = gc->attr, lastattr = lastgc->attr;
char tmp[64]; char tmp[64];
const char *uri, *id;
struct { static const struct {
u_int mask; u_int mask;
u_int code; u_int code;
} attrs[] = { } attrs[] = {
@@ -913,7 +956,7 @@ grid_string_cells_code(const struct grid_cell *lastgc,
for (i = 0; i < nitems(attrs); i++) { for (i = 0; i < nitems(attrs); i++) {
if (((~attr & attrs[i].mask) && if (((~attr & attrs[i].mask) &&
(lastattr & attrs[i].mask)) || (lastattr & attrs[i].mask)) ||
(lastgc->us != 0 && gc->us == 0)) { (lastgc->us != 8 && gc->us == 8)) {
s[n++] = 0; s[n++] = 0;
lastattr &= GRID_ATTR_CHARSET; lastattr &= GRID_ATTR_CHARSET;
break; break;
@@ -928,7 +971,7 @@ grid_string_cells_code(const struct grid_cell *lastgc,
/* Write the attributes. */ /* Write the attributes. */
*buf = '\0'; *buf = '\0';
if (n > 0) { if (n > 0) {
if (escape_c0) if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\033[", len); strlcat(buf, "\\033[", len);
else else
strlcat(buf, "\033[", len); strlcat(buf, "\033[", len);
@@ -950,46 +993,59 @@ grid_string_cells_code(const struct grid_cell *lastgc,
nnewc = grid_string_cells_fg(gc, newc); nnewc = grid_string_cells_fg(gc, newc);
noldc = grid_string_cells_fg(lastgc, oldc); noldc = grid_string_cells_fg(lastgc, oldc);
grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
escape_c0); flags);
/* If the background colour changed, append its parameters. */ /* If the background colour changed, append its parameters. */
nnewc = grid_string_cells_bg(gc, newc); nnewc = grid_string_cells_bg(gc, newc);
noldc = grid_string_cells_bg(lastgc, oldc); noldc = grid_string_cells_bg(lastgc, oldc);
grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
escape_c0); flags);
/* If the underscore colour changed, append its parameters. */ /* If the underscore colour changed, append its parameters. */
nnewc = grid_string_cells_us(gc, newc); nnewc = grid_string_cells_us(gc, newc);
noldc = grid_string_cells_us(lastgc, oldc); noldc = grid_string_cells_us(lastgc, oldc);
grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
escape_c0); flags);
/* Append shift in/shift out if needed. */ /* Append shift in/shift out if needed. */
if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) { if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) {
if (escape_c0) if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\016", len); /* SO */ strlcat(buf, "\\016", len); /* SO */
else else
strlcat(buf, "\016", len); /* SO */ strlcat(buf, "\016", len); /* SO */
} }
if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) { if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) {
if (escape_c0) if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\017", len); /* SI */ strlcat(buf, "\\017", len); /* SI */
else else
strlcat(buf, "\017", len); /* SI */ strlcat(buf, "\017", len); /* SI */
} }
/* Add hyperlink if changed. */
if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) {
if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) {
*has_link = grid_string_cells_add_hyperlink(buf, len,
id, uri, flags);
} else if (*has_link) {
grid_string_cells_add_hyperlink(buf, len, "", "",
flags);
*has_link = 0;
}
}
} }
/* Convert cells into a string. */ /* Convert cells into a string. */
char * char *
grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
struct grid_cell **lastgc, int with_codes, int escape_c0, int trim) struct grid_cell **lastgc, int flags, struct screen *s)
{ {
struct grid_cell gc; struct grid_cell gc;
static struct grid_cell lastgc1; static struct grid_cell lastgc1;
const char *data; const char *data;
char *buf, code[128]; char *buf, code[8192];
size_t len, off, size, codelen; size_t len, off, size, codelen;
u_int xx; u_int xx, end;
int has_link = 0;
const struct grid_line *gl; const struct grid_line *gl;
if (lastgc != NULL && *lastgc == NULL) { if (lastgc != NULL && *lastgc == NULL) {
@@ -1002,16 +1058,20 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
off = 0; off = 0;
gl = grid_peek_line(gd, py); gl = grid_peek_line(gd, py);
if (flags & GRID_STRING_EMPTY_CELLS)
end = gl->cellsize;
else
end = gl->cellused;
for (xx = px; xx < px + nx; xx++) { for (xx = px; xx < px + nx; xx++) {
if (gl == NULL || xx >= gl->cellused) if (gl == NULL || xx >= end)
break; break;
grid_get_cell(gd, xx, py, &gc); grid_get_cell(gd, xx, py, &gc);
if (gc.flags & GRID_FLAG_PADDING) if (gc.flags & GRID_FLAG_PADDING)
continue; continue;
if (with_codes) { if (flags & GRID_STRING_WITH_SEQUENCES) {
grid_string_cells_code(*lastgc, &gc, code, sizeof code, grid_string_cells_code(*lastgc, &gc, code, sizeof code,
escape_c0); flags, s, &has_link);
codelen = strlen(code); codelen = strlen(code);
memcpy(*lastgc, &gc, sizeof **lastgc); memcpy(*lastgc, &gc, sizeof **lastgc);
} else } else
@@ -1019,7 +1079,9 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
data = gc.data.data; data = gc.data.data;
size = gc.data.size; size = gc.data.size;
if (escape_c0 && size == 1 && *data == '\\') { if ((flags & GRID_STRING_ESCAPE_SEQUENCES) &&
size == 1 &&
*data == '\\') {
data = "\\\\"; data = "\\\\";
size = 2; size = 2;
} }
@@ -1037,7 +1099,19 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
off += size; off += size;
} }
if (trim) { if (has_link) {
grid_string_cells_add_hyperlink(code, sizeof code, "", "",
flags);
codelen = strlen(code);
while (len < off + size + codelen + 1) {
buf = xreallocarray(buf, 2, len);
len *= 2;
}
memcpy(buf + off, code, codelen);
off += codelen;
}
if (flags & GRID_STRING_TRIM_SPACES) {
while (off > 0 && buf[off - 1] == ' ') while (off > 0 && buf[off - 1] == ' ')
off--; off--;
} }

227
hyperlinks.c Normal file
View File

@@ -0,0 +1,227 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2021 Will <author@will.party>
* Copyright (c) 2022 Jeff Chiang <pobomp@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
/*
* OSC 8 hyperlinks, described at:
*
* https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
*
* Each hyperlink and ID combination is assigned a number ("inner" in this
* file) which is stored in an extended grid cell and maps into a tree here.
*
* Each URI has one inner number and one external ID (which tmux uses to send
* the hyperlink to the terminal) and one internal ID (which is received from
* the sending application inside tmux).
*
* Anonymous hyperlinks are each unique and are not reused even if they have
* the same URI (terminals will not want to tie them together).
*/
#define MAX_HYPERLINKS 5000
static long long hyperlinks_next_external_id = 1;
static u_int global_hyperlinks_count;
struct hyperlinks_uri {
struct hyperlinks *tree;
u_int inner;
const char *internal_id;
const char *external_id;
const char *uri;
TAILQ_ENTRY(hyperlinks_uri) list_entry;
RB_ENTRY(hyperlinks_uri) by_inner_entry;
RB_ENTRY(hyperlinks_uri) by_uri_entry; /* by internal ID and URI */
};
RB_HEAD(hyperlinks_by_uri_tree, hyperlinks_uri);
RB_HEAD(hyperlinks_by_inner_tree, hyperlinks_uri);
TAILQ_HEAD(hyperlinks_list, hyperlinks_uri);
static struct hyperlinks_list global_hyperlinks =
TAILQ_HEAD_INITIALIZER(global_hyperlinks);
struct hyperlinks {
u_int next_inner;
struct hyperlinks_by_inner_tree by_inner;
struct hyperlinks_by_uri_tree by_uri;
};
static int
hyperlinks_by_uri_cmp(struct hyperlinks_uri *left, struct hyperlinks_uri *right)
{
int r;
if (*left->internal_id == '\0' || *right->internal_id == '\0') {
/*
* If both URIs are anonymous, use the inner for comparison so
* that they do not match even if the URI is the same - each
* anonymous URI should be unique.
*/
if (*left->internal_id != '\0')
return (-1);
if (*right->internal_id != '\0')
return (1);
return (left->inner - right->inner);
}
r = strcmp(left->internal_id, right->internal_id);
if (r != 0)
return (r);
return (strcmp(left->uri, right->uri));
}
RB_PROTOTYPE_STATIC(hyperlinks_by_uri_tree, hyperlinks_uri, by_uri_entry,
hyperlinks_by_uri_cmp);
RB_GENERATE_STATIC(hyperlinks_by_uri_tree, hyperlinks_uri, by_uri_entry,
hyperlinks_by_uri_cmp);
static int
hyperlinks_by_inner_cmp(struct hyperlinks_uri *left,
struct hyperlinks_uri *right)
{
return (left->inner - right->inner);
}
RB_PROTOTYPE_STATIC(hyperlinks_by_inner_tree, hyperlinks_uri, by_inner_entry,
hyperlinks_by_inner_cmp);
RB_GENERATE_STATIC(hyperlinks_by_inner_tree, hyperlinks_uri, by_inner_entry,
hyperlinks_by_inner_cmp);
/* Remove a hyperlink. */
static void
hyperlinks_remove(struct hyperlinks_uri *hlu)
{
struct hyperlinks *hl = hlu->tree;
TAILQ_REMOVE(&global_hyperlinks, hlu, list_entry);
global_hyperlinks_count--;
RB_REMOVE(hyperlinks_by_inner_tree, &hl->by_inner, hlu);
RB_REMOVE(hyperlinks_by_uri_tree, &hl->by_uri, hlu);
free((void *)hlu->internal_id);
free((void *)hlu->external_id);
free((void *)hlu->uri);
free(hlu);
}
/* Store a new hyperlink or return if it already exists. */
u_int
hyperlinks_put(struct hyperlinks *hl, const char *uri_in,
const char *internal_id_in)
{
struct hyperlinks_uri find, *hlu;
char *uri, *internal_id, *external_id;
/*
* Anonymous URI are stored with an empty internal ID and the tree
* comparator will make sure they never match each other (so each
* anonymous URI is unique).
*/
if (internal_id_in == NULL)
internal_id_in = "";
utf8_stravis(&uri, uri_in, VIS_OCTAL|VIS_CSTYLE);
utf8_stravis(&internal_id, internal_id_in, VIS_OCTAL|VIS_CSTYLE);
if (*internal_id_in != '\0') {
find.uri = uri;
find.internal_id = internal_id;
hlu = RB_FIND(hyperlinks_by_uri_tree, &hl->by_uri, &find);
if (hlu != NULL) {
free (uri);
free (internal_id);
return (hlu->inner);
}
}
xasprintf(&external_id, "tmux%llX", hyperlinks_next_external_id++);
hlu = xcalloc(1, sizeof *hlu);
hlu->inner = hl->next_inner++;
hlu->internal_id = internal_id;
hlu->external_id = external_id;
hlu->uri = uri;
hlu->tree = hl;
RB_INSERT(hyperlinks_by_uri_tree, &hl->by_uri, hlu);
RB_INSERT(hyperlinks_by_inner_tree, &hl->by_inner, hlu);
TAILQ_INSERT_TAIL(&global_hyperlinks, hlu, list_entry);
if (++global_hyperlinks_count == MAX_HYPERLINKS)
hyperlinks_remove(TAILQ_FIRST(&global_hyperlinks));
return (hlu->inner);
}
/* Get hyperlink by inner number. */
int
hyperlinks_get(struct hyperlinks *hl, u_int inner, const char **uri_out,
const char **internal_id_out, const char **external_id_out)
{
struct hyperlinks_uri find, *hlu;
find.inner = inner;
hlu = RB_FIND(hyperlinks_by_inner_tree, &hl->by_inner, &find);
if (hlu == NULL)
return (0);
if (internal_id_out != NULL)
*internal_id_out = hlu->internal_id;
if (external_id_out != NULL)
*external_id_out = hlu->external_id;
*uri_out = hlu->uri;
return (1);
}
/* Initialize hyperlink set. */
struct hyperlinks *
hyperlinks_init(void)
{
struct hyperlinks *hl;
hl = xcalloc(1, sizeof *hl);
hl->next_inner = 1;
RB_INIT(&hl->by_uri);
RB_INIT(&hl->by_inner);
return (hl);
}
/* Free all hyperlinks but not the set itself. */
void
hyperlinks_reset(struct hyperlinks *hl)
{
struct hyperlinks_uri *hlu, *hlu1;
RB_FOREACH_SAFE(hlu, hyperlinks_by_inner_tree, &hl->by_inner, hlu1)
hyperlinks_remove(hlu);
}
/* Free hyperlink set. */
void
hyperlinks_free(struct hyperlinks *hl)
{
hyperlinks_reset(hl);
free(hl);
}

600
image-sixel.c Normal file
View File

@@ -0,0 +1,600 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
#define SIXEL_WIDTH_LIMIT 10000
#define SIXEL_HEIGHT_LIMIT 10000
struct sixel_line {
u_int x;
uint16_t *data;
};
struct sixel_image {
u_int x;
u_int y;
u_int xpixel;
u_int ypixel;
u_int *colours;
u_int ncolours;
u_int dx;
u_int dy;
u_int dc;
struct sixel_line *lines;
};
static int
sixel_parse_expand_lines(struct sixel_image *si, u_int y)
{
if (y <= si->y)
return (0);
if (y > SIXEL_HEIGHT_LIMIT)
return (1);
si->lines = xrecallocarray(si->lines, si->y, y, sizeof *si->lines);
si->y = y;
return (0);
}
static int
sixel_parse_expand_line(struct sixel_image *si, struct sixel_line *sl, u_int x)
{
if (x <= sl->x)
return (0);
if (x > SIXEL_WIDTH_LIMIT)
return (1);
if (x > si->x)
si->x = x;
sl->data = xrecallocarray(sl->data, sl->x, si->x, sizeof *sl->data);
sl->x = si->x;
return (0);
}
static u_int
sixel_get_pixel(struct sixel_image *si, u_int x, u_int y)
{
struct sixel_line *sl;
if (y >= si->y)
return (0);
sl = &si->lines[y];
if (x >= sl->x)
return (0);
return (sl->data[x]);
}
static int
sixel_set_pixel(struct sixel_image *si, u_int x, u_int y, u_int c)
{
struct sixel_line *sl;
if (sixel_parse_expand_lines(si, y + 1) != 0)
return (1);
sl = &si->lines[y];
if (sixel_parse_expand_line(si, sl, x + 1) != 0)
return (1);
sl->data[x] = c;
return (0);
}
static int
sixel_parse_write(struct sixel_image *si, u_int ch)
{
struct sixel_line *sl;
u_int i;
if (sixel_parse_expand_lines(si, si->dy + 6) != 0)
return (1);
sl = &si->lines[si->dy];
for (i = 0; i < 6; i++) {
if (sixel_parse_expand_line(si, sl, si->dx + 1) != 0)
return (1);
if (ch & (1 << i))
sl->data[si->dx] = si->dc;
sl++;
}
return (0);
}
static const char *
sixel_parse_attributes(struct sixel_image *si, const char *cp, const char *end)
{
const char *last;
char *endptr;
u_int x, y;
last = cp;
while (last != end) {
if (*last != ';' && (*last < '0' || *last > '9'))
break;
last++;
}
strtoul(cp, &endptr, 10);
if (endptr == last || *endptr != ';')
return (last);
strtoul(endptr + 1, &endptr, 10);
if (endptr == last)
return (last);
if (*endptr != ';') {
log_debug("%s: missing ;", __func__);
return (NULL);
}
x = strtoul(endptr + 1, &endptr, 10);
if (endptr == last || *endptr != ';') {
log_debug("%s: missing ;", __func__);
return (NULL);
}
if (x > SIXEL_WIDTH_LIMIT) {
log_debug("%s: image is too wide", __func__);
return (NULL);
}
y = strtoul(endptr + 1, &endptr, 10);
if (endptr != last) {
log_debug("%s: extra ;", __func__);
return (NULL);
}
if (y > SIXEL_HEIGHT_LIMIT) {
log_debug("%s: image is too tall", __func__);
return (NULL);
}
si->x = x;
sixel_parse_expand_lines(si, y);
return (last);
}
static const char *
sixel_parse_colour(struct sixel_image *si, const char *cp, const char *end)
{
const char *last;
char *endptr;
u_int c, type, r, g, b;
last = cp;
while (last != end) {
if (*last != ';' && (*last < '0' || *last > '9'))
break;
last++;
}
c = strtoul(cp, &endptr, 10);
if (c > SIXEL_COLOUR_REGISTERS) {
log_debug("%s: too many colours", __func__);
return (NULL);
}
si->dc = c + 1;
if (endptr == last || *endptr != ';')
return (last);
type = strtoul(endptr + 1, &endptr, 10);
if (endptr == last || *endptr != ';') {
log_debug("%s: missing ;", __func__);
return (NULL);
}
r = strtoul(endptr + 1, &endptr, 10);
if (endptr == last || *endptr != ';') {
log_debug("%s: missing ;", __func__);
return (NULL);
}
g = strtoul(endptr + 1, &endptr, 10);
if (endptr == last || *endptr != ';') {
log_debug("%s: missing ;", __func__);
return (NULL);
}
b = strtoul(endptr + 1, &endptr, 10);
if (endptr != last) {
log_debug("%s: missing ;", __func__);
return (NULL);
}
if (type != 1 && type != 2) {
log_debug("%s: invalid type %d", __func__, type);
return (NULL);
}
if (c + 1 > si->ncolours) {
si->colours = xrecallocarray(si->colours, si->ncolours, c + 1,
sizeof *si->colours);
si->ncolours = c + 1;
}
si->colours[c] = (type << 24) | (r << 16) | (g << 8) | b;
return (last);
}
static const char *
sixel_parse_repeat(struct sixel_image *si, const char *cp, const char *end)
{
const char *last;
char tmp[32], ch;
u_int n = 0, i;
const char *errstr = NULL;
last = cp;
while (last != end) {
if (*last < '0' || *last > '9')
break;
tmp[n++] = *last++;
if (n == (sizeof tmp) - 1) {
log_debug("%s: repeat not terminated", __func__);
return (NULL);
}
}
if (n == 0 || last == end) {
log_debug("%s: repeat not terminated", __func__);
return (NULL);
}
tmp[n] = '\0';
n = strtonum(tmp, 1, SIXEL_WIDTH_LIMIT, &errstr);
if (n == 0 || errstr != NULL) {
log_debug("%s: repeat too wide", __func__);
return (NULL);
}
ch = (*last++) - 0x3f;
for (i = 0; i < n; i++) {
if (sixel_parse_write(si, ch) != 0) {
log_debug("%s: width limit reached", __func__);
return (NULL);
}
si->dx++;
}
return (last);
}
struct sixel_image *
sixel_parse(const char *buf, size_t len, u_int xpixel, u_int ypixel)
{
struct sixel_image *si;
const char *cp = buf, *end = buf + len;
char ch;
if (len == 0 || len == 1 || *cp++ != 'q') {
log_debug("%s: empty image", __func__);
return (NULL);
}
si = xcalloc (1, sizeof *si);
si->xpixel = xpixel;
si->ypixel = ypixel;
while (cp != end) {
ch = *cp++;
switch (ch) {
case '"':
cp = sixel_parse_attributes(si, cp, end);
if (cp == NULL)
goto bad;
break;
case '#':
cp = sixel_parse_colour(si, cp, end);
if (cp == NULL)
goto bad;
break;
case '!':
cp = sixel_parse_repeat(si, cp, end);
if (cp == NULL)
goto bad;
break;
case '-':
si->dx = 0;
si->dy += 6;
break;
case '$':
si->dx = 0;
break;
default:
if (ch < 0x20)
break;
if (ch < 0x3f || ch > 0x7e)
goto bad;
if (sixel_parse_write(si, ch - 0x3f) != 0) {
log_debug("%s: width limit reached", __func__);
goto bad;
}
si->dx++;
break;
}
}
if (si->x == 0 || si->y == 0)
goto bad;
return (si);
bad:
free(si);
return (NULL);
}
void
sixel_free(struct sixel_image *si)
{
u_int y;
for (y = 0; y < si->y; y++)
free(si->lines[y].data);
free(si->lines);
free(si->colours);
free(si);
}
void
sixel_log(struct sixel_image *si)
{
struct sixel_line *sl;
char s[SIXEL_WIDTH_LIMIT + 1];
u_int i, x, y, cx, cy;
sixel_size_in_cells(si, &cx, &cy);
log_debug("%s: image %ux%u (%ux%u)", __func__, si->x, si->y, cx, cy);
for (i = 0; i < si->ncolours; i++)
log_debug("%s: colour %u is %07x", __func__, i, si->colours[i]);
for (y = 0; y < si->y; y++) {
sl = &si->lines[y];
for (x = 0; x < si->x; x++) {
if (x >= sl->x)
s[x] = '_';
else if (sl->data[x] != 0)
s[x] = '0' + (sl->data[x] - 1) % 10;
else
s[x] = '.';
}
s[x] = '\0';
log_debug("%s: %4u: %s", __func__, y, s);
}
}
void
sixel_size_in_cells(struct sixel_image *si, u_int *x, u_int *y)
{
if ((si->x % si->xpixel) == 0)
*x = (si->x / si->xpixel);
else
*x = 1 + (si->x / si->xpixel);
if ((si->y % si->ypixel) == 0)
*y = (si->y / si->ypixel);
else
*y = 1 + (si->y / si->ypixel);
}
struct sixel_image *
sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
u_int oy, u_int sx, u_int sy, int colours)
{
struct sixel_image *new;
u_int cx, cy, pox, poy, psx, psy, tsx, tsy, px, py;
u_int x, y, i;
/*
* We want to get the section of the image at ox,oy in image cells and
* map it onto the same size in terminal cells, remembering that we
* can only draw vertical sections of six pixels.
*/
sixel_size_in_cells(si, &cx, &cy);
if (ox >= cx)
return (NULL);
if (oy >= cy)
return (NULL);
if (ox + sx >= cx)
sx = cx - ox;
if (oy + sy >= cy)
sy = cy - oy;
if (xpixel == 0)
xpixel = si->xpixel;
if (ypixel == 0)
ypixel = si->ypixel;
pox = ox * si->xpixel;
poy = oy * si->ypixel;
psx = sx * si->xpixel;
psy = sy * si->ypixel;
tsx = sx * xpixel;
tsy = ((sy * ypixel) / 6) * 6;
new = xcalloc (1, sizeof *si);
new->xpixel = xpixel;
new->ypixel = ypixel;
for (y = 0; y < tsy; y++) {
py = poy + ((double)y * psy / tsy);
for (x = 0; x < tsx; x++) {
px = pox + ((double)x * psx / tsx);
sixel_set_pixel(new, x, y, sixel_get_pixel(si, px, py));
}
}
if (colours) {
new->colours = xmalloc(si->ncolours * sizeof *new->colours);
for (i = 0; i < si->ncolours; i++)
new->colours[i] = si->colours[i];
new->ncolours = si->ncolours;
}
return (new);
}
static void
sixel_print_add(char **buf, size_t *len, size_t *used, const char *s,
size_t slen)
{
if (*used + slen >= *len + 1) {
(*len) *= 2;
*buf = xrealloc(*buf, *len);
}
memcpy(*buf + *used, s, slen);
(*used) += slen;
}
static void
sixel_print_repeat(char **buf, size_t *len, size_t *used, u_int count, char ch)
{
char tmp[16];
size_t tmplen;
if (count == 1)
sixel_print_add(buf, len, used, &ch, 1);
else if (count == 2) {
sixel_print_add(buf, len, used, &ch, 1);
sixel_print_add(buf, len, used, &ch, 1);
} else if (count == 3) {
sixel_print_add(buf, len, used, &ch, 1);
sixel_print_add(buf, len, used, &ch, 1);
sixel_print_add(buf, len, used, &ch, 1);
} else if (count != 0) {
tmplen = xsnprintf(tmp, sizeof tmp, "!%u%c", count, ch);
sixel_print_add(buf, len, used, tmp, tmplen);
}
}
char *
sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
{
char *buf, tmp[64], *contains, data, last = 0;
size_t len, used = 0, tmplen;
u_int *colours, ncolours, i, c, x, y, count;
struct sixel_line *sl;
if (map != NULL) {
colours = map->colours;
ncolours = map->ncolours;
} else {
colours = si->colours;
ncolours = si->ncolours;
}
contains = xcalloc(1, ncolours);
len = 8192;
buf = xmalloc(len);
sixel_print_add(&buf, &len, &used, "\033Pq", 3);
tmplen = xsnprintf(tmp, sizeof tmp, "\"1;1;%u;%u", si->x, si->y);
sixel_print_add(&buf, &len, &used, tmp, tmplen);
for (i = 0; i < ncolours; i++) {
c = colours[i];
tmplen = xsnprintf(tmp, sizeof tmp, "#%u;%u;%u;%u;%u",
i, c >> 24, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff);
sixel_print_add(&buf, &len, &used, tmp, tmplen);
}
for (y = 0; y < si->y; y += 6) {
memset(contains, 0, ncolours);
for (x = 0; x < si->x; x++) {
for (i = 0; i < 6; i++) {
if (y + i >= si->y)
break;
sl = &si->lines[y + i];
if (x < sl->x && sl->data[x] != 0)
contains[sl->data[x] - 1] = 1;
}
}
for (c = 0; c < ncolours; c++) {
if (!contains[c])
continue;
tmplen = xsnprintf(tmp, sizeof tmp, "#%u", c);
sixel_print_add(&buf, &len, &used, tmp, tmplen);
count = 0;
for (x = 0; x < si->x; x++) {
data = 0;
for (i = 0; i < 6; i++) {
if (y + i >= si->y)
break;
sl = &si->lines[y + i];
if (x < sl->x && sl->data[x] == c + 1)
data |= (1 << i);
}
data += 0x3f;
if (data != last) {
sixel_print_repeat(&buf, &len, &used,
count, last);
last = data;
count = 1;
} else
count++;
}
sixel_print_repeat(&buf, &len, &used, count, data);
sixel_print_add(&buf, &len, &used, "$", 1);
}
if (buf[used - 1] == '$')
used--;
if (buf[used - 1] != '-')
sixel_print_add(&buf, &len, &used, "-", 1);
}
if (buf[used - 1] == '$' || buf[used - 1] == '-')
used--;
sixel_print_add(&buf, &len, &used, "\033\\", 2);
buf[used] = '\0';
if (size != NULL)
*size = used;
free(contains);
return (buf);
}
struct screen *
sixel_to_screen(struct sixel_image *si)
{
struct screen *s;
struct screen_write_ctx ctx;
struct grid_cell gc;
u_int x, y, sx, sy;
sixel_size_in_cells(si, &sx, &sy);
s = xmalloc(sizeof *s);
screen_init(s, sx, sy, 0);
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.attr |= (GRID_ATTR_CHARSET|GRID_ATTR_DIM);
utf8_set(&gc.data, '~');
screen_write_start(&ctx, s);
if (sx == 1 || sy == 1) {
for (y = 0; y < sy; y++) {
for (x = 0; x < sx; x++)
grid_view_set_cell(s->grid, x, y, &gc);
}
} else {
screen_write_box(&ctx, sx, sy, BOX_LINES_DEFAULT, NULL, NULL);
for (y = 1; y < sy - 1; y++) {
for (x = 1; x < sx - 1; x++)
grid_view_set_cell(s->grid, x, y, &gc);
}
}
screen_write_stop(&ctx);
return (s);
}

186
image.c Normal file
View File

@@ -0,0 +1,186 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
static struct images all_images = TAILQ_HEAD_INITIALIZER(all_images);
static u_int all_images_count;
static void
image_free(struct image *im)
{
struct screen *s = im->s;
TAILQ_REMOVE(&all_images, im, all_entry);
all_images_count--;
TAILQ_REMOVE(&s->images, im, entry);
sixel_free(im->data);
free(im->fallback);
free(im);
}
int
image_free_all(struct screen *s)
{
struct image *im, *im1;
int redraw = !TAILQ_EMPTY(&s->images);
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1)
image_free(im);
return (redraw);
}
/* Create text placeholder for an image. */
static void
image_fallback(char **ret, u_int sx, u_int sy)
{
char *buf, *label;
u_int py, size, lsize;
/* Allocate first line. */
lsize = xasprintf(&label, "SIXEL IMAGE (%ux%u)\r\n", sx, sy) + 1;
if (sx < lsize - 3)
size = lsize - 1;
else
size = sx + 2;
/* Remaining lines. Every placeholder line has \r\n at the end. */
size += (sx + 2) * (sy - 1) + 1;
*ret = buf = xmalloc(size);
/* Render first line. */
if (sx < lsize - 3) {
memcpy(buf, label, lsize);
buf += lsize - 1;
} else {
memcpy(buf, label, lsize - 3);
buf += lsize - 3;
memset(buf, '+', sx - lsize + 3);
buf += sx - lsize + 3;
snprintf(buf, 3, "\r\n");
buf += 2;
}
/* Remaining lines. */
for (py = 1; py < sy; py++) {
memset(buf, '+', sx);
buf += sx;
snprintf(buf, 3, "\r\n");
buf += 2;
}
free(label);
}
struct image*
image_store(struct screen *s, struct sixel_image *si)
{
struct image *im;
im = xcalloc(1, sizeof *im);
im->s = s;
im->data = si;
im->px = s->cx;
im->py = s->cy;
sixel_size_in_cells(si, &im->sx, &im->sy);
image_fallback(&im->fallback, im->sx, im->sy);
TAILQ_INSERT_TAIL(&s->images, im, entry);
TAILQ_INSERT_TAIL(&all_images, im, all_entry);
if (++all_images_count == 10/*XXX*/)
image_free(TAILQ_FIRST(&all_images));
return (im);
}
int
image_check_line(struct screen *s, u_int py, u_int ny)
{
struct image *im, *im1;
int redraw = 0;
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (py + ny > im->py && py < im->py + im->sy) {
image_free(im);
redraw = 1;
}
}
return (redraw);
}
int
image_check_area(struct screen *s, u_int px, u_int py, u_int nx, u_int ny)
{
struct image *im, *im1;
int redraw = 0;
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (py + ny <= im->py || py >= im->py + im->sy)
continue;
if (px + nx <= im->px || px >= im->px + im->sx)
continue;
image_free(im);
redraw = 1;
}
return (redraw);
}
int
image_scroll_up(struct screen *s, u_int lines)
{
struct image *im, *im1;
int redraw = 0;
u_int sx, sy;
struct sixel_image *new;
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
if (im->py >= lines) {
im->py -= lines;
redraw = 1;
continue;
}
if (im->py + im->sy <= lines) {
image_free(im);
redraw = 1;
continue;
}
sx = im->sx;
sy = (im->py + im->sy) - lines;
new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1);
sixel_free(im->data);
im->data = new;
im->py = 0;
sixel_size_in_cells(im->data, &im->sx, &im->sy);
free(im->fallback);
image_fallback(&im->fallback, im->sx, im->sy);
redraw = 1;
}
return (redraw);
}

View File

@@ -306,6 +306,20 @@ static struct input_key_entry input_key_defaults[] = {
}, },
{ .key = KEYC_DC|KEYC_BUILD_MODIFIERS, { .key = KEYC_DC|KEYC_BUILD_MODIFIERS,
.data = "\033[3;_~" .data = "\033[3;_~"
},
/* Tab and modifiers. */
{ .key = '\011'|KEYC_CTRL,
.data = "\011"
},
{ .key = '\011'|KEYC_CTRL|KEYC_EXTENDED,
.data = "\033[9;5u"
},
{ .key = '\011'|KEYC_CTRL|KEYC_SHIFT,
.data = "\033[Z"
},
{ .key = '\011'|KEYC_CTRL|KEYC_SHIFT|KEYC_EXTENDED,
.data = "\033[1;5Z"
} }
}; };
static const key_code input_key_modifiers[] = { static const key_code input_key_modifiers[] = {
@@ -416,7 +430,7 @@ input_key_write(const char *from, struct bufferevent *bev, const char *data,
int int
input_key(struct screen *s, struct bufferevent *bev, key_code key) input_key(struct screen *s, struct bufferevent *bev, key_code key)
{ {
struct input_key_entry *ike; struct input_key_entry *ike = NULL;
key_code justkey, newkey, outkey, modifiers; key_code justkey, newkey, outkey, modifiers;
struct utf8_data ud; struct utf8_data ud;
char tmp[64], modifier; char tmp[64], modifier;
@@ -468,15 +482,23 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
key &= ~KEYC_KEYPAD; key &= ~KEYC_KEYPAD;
if (~s->mode & MODE_KCURSOR) if (~s->mode & MODE_KCURSOR)
key &= ~KEYC_CURSOR; key &= ~KEYC_CURSOR;
ike = input_key_get(key); if (s->mode & MODE_KEXTENDED)
ike = input_key_get(key|KEYC_EXTENDED);
if (ike == NULL)
ike = input_key_get(key);
if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META)) if (ike == NULL && (key & KEYC_META) && (~key & KEYC_IMPLIED_META))
ike = input_key_get(key & ~KEYC_META); ike = input_key_get(key & ~KEYC_META);
if (ike == NULL && (key & KEYC_CURSOR)) if (ike == NULL && (key & KEYC_CURSOR))
ike = input_key_get(key & ~KEYC_CURSOR); ike = input_key_get(key & ~KEYC_CURSOR);
if (ike == NULL && (key & KEYC_KEYPAD)) if (ike == NULL && (key & KEYC_KEYPAD))
ike = input_key_get(key & ~KEYC_KEYPAD); ike = input_key_get(key & ~KEYC_KEYPAD);
if (ike == NULL && (key & KEYC_EXTENDED))
ike = input_key_get(key & ~KEYC_EXTENDED);
if (ike != NULL) { if (ike != NULL) {
log_debug("found key 0x%llx: \"%s\"", key, ike->data); log_debug("found key 0x%llx: \"%s\"", key, ike->data);
if ((key == KEYC_PASTE_START || key == KEYC_PASTE_END) &&
(~s->mode & MODE_BRACKETPASTE))
return (0);
if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META)) if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META))
input_key_write(__func__, bev, "\033", 1); input_key_write(__func__, bev, "\033", 1);
input_key_write(__func__, bev, ike->data, strlen(ike->data)); input_key_write(__func__, bev, ike->data, strlen(ike->data));

329
input.c
View File

@@ -135,6 +135,7 @@ static void input_set_state(struct input_ctx *,
static void input_reset_cell(struct input_ctx *); static void input_reset_cell(struct input_ctx *);
static void input_osc_4(struct input_ctx *, const char *); static void input_osc_4(struct input_ctx *, const char *);
static void input_osc_8(struct input_ctx *, const char *);
static void input_osc_10(struct input_ctx *, const char *); static void input_osc_10(struct input_ctx *, const char *);
static void input_osc_11(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 *); static void input_osc_12(struct input_ctx *, const char *);
@@ -143,6 +144,7 @@ static void input_osc_104(struct input_ctx *, const char *);
static void input_osc_110(struct input_ctx *, const char *); static void input_osc_110(struct input_ctx *, const char *);
static void input_osc_111(struct input_ctx *, const char *); static void input_osc_111(struct input_ctx *, const char *);
static void input_osc_112(struct input_ctx *, const char *); static void input_osc_112(struct input_ctx *, const char *);
static void input_osc_133(struct input_ctx *, const char *);
/* Transition entry/exit handlers. */ /* Transition entry/exit handlers. */
static void input_clear(struct input_ctx *); static void input_clear(struct input_ctx *);
@@ -167,6 +169,7 @@ static void input_csi_dispatch_rm(struct input_ctx *);
static void input_csi_dispatch_rm_private(struct input_ctx *); static void input_csi_dispatch_rm_private(struct input_ctx *);
static void input_csi_dispatch_sm(struct input_ctx *); static void input_csi_dispatch_sm(struct input_ctx *);
static void input_csi_dispatch_sm_private(struct input_ctx *); static void input_csi_dispatch_sm_private(struct input_ctx *);
static void input_csi_dispatch_sm_graphics(struct input_ctx *);
static void input_csi_dispatch_winops(struct input_ctx *); static void input_csi_dispatch_winops(struct input_ctx *);
static void input_csi_dispatch_sgr_256(struct input_ctx *, int, u_int *); static void input_csi_dispatch_sgr_256(struct input_ctx *, int, u_int *);
static void input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *); static void input_csi_dispatch_sgr_rgb(struct input_ctx *, int, u_int *);
@@ -201,7 +204,7 @@ enum input_esc_type {
INPUT_ESC_SCSG0_ON, INPUT_ESC_SCSG0_ON,
INPUT_ESC_SCSG1_OFF, INPUT_ESC_SCSG1_OFF,
INPUT_ESC_SCSG1_ON, INPUT_ESC_SCSG1_ON,
INPUT_ESC_ST, INPUT_ESC_ST
}; };
/* Escape command table. */ /* Escape command table. */
@@ -257,11 +260,12 @@ enum input_csi_type {
INPUT_CSI_SGR, INPUT_CSI_SGR,
INPUT_CSI_SM, INPUT_CSI_SM,
INPUT_CSI_SM_PRIVATE, INPUT_CSI_SM_PRIVATE,
INPUT_CSI_SM_GRAPHICS,
INPUT_CSI_SU, INPUT_CSI_SU,
INPUT_CSI_TBC, INPUT_CSI_TBC,
INPUT_CSI_VPA, INPUT_CSI_VPA,
INPUT_CSI_WINOPS, INPUT_CSI_WINOPS,
INPUT_CSI_XDA, INPUT_CSI_XDA
}; };
/* Control (CSI) command table. */ /* Control (CSI) command table. */
@@ -281,6 +285,7 @@ static const struct input_table_entry input_csi_table[] = {
{ 'M', "", INPUT_CSI_DL }, { 'M', "", INPUT_CSI_DL },
{ 'P', "", INPUT_CSI_DCH }, { 'P', "", INPUT_CSI_DCH },
{ 'S', "", INPUT_CSI_SU }, { 'S', "", INPUT_CSI_SU },
{ 'S', "?", INPUT_CSI_SM_GRAPHICS },
{ 'T', "", INPUT_CSI_SD }, { 'T', "", INPUT_CSI_SD },
{ 'X', "", INPUT_CSI_ECH }, { 'X', "", INPUT_CSI_ECH },
{ 'Z', "", INPUT_CSI_CBT }, { 'Z', "", INPUT_CSI_CBT },
@@ -304,7 +309,7 @@ static const struct input_table_entry input_csi_table[] = {
{ 'r', "", INPUT_CSI_DECSTBM }, { 'r', "", INPUT_CSI_DECSTBM },
{ 's', "", INPUT_CSI_SCP }, { 's', "", INPUT_CSI_SCP },
{ 't', "", INPUT_CSI_WINOPS }, { 't', "", INPUT_CSI_WINOPS },
{ 'u', "", INPUT_CSI_RCP }, { 'u', "", INPUT_CSI_RCP }
}; };
/* Input transition. */ /* Input transition. */
@@ -970,6 +975,10 @@ input_parse_buffer(struct window_pane *wp, u_char *buf, size_t len)
window_update_activity(wp->window); window_update_activity(wp->window);
wp->flags |= PANE_CHANGED; wp->flags |= PANE_CHANGED;
/* Flag new input while in a mode. */
if (!TAILQ_EMPTY(&wp->modes))
wp->flags |= PANE_UNSEENCHANGES;
/* NULL wp if there is a mode set as don't want to update the tty. */ /* NULL wp if there is a mode set as don't want to update the tty. */
if (TAILQ_EMPTY(&wp->modes)) if (TAILQ_EMPTY(&wp->modes))
screen_write_start_pane(sctx, wp, &wp->base); screen_write_start_pane(sctx, wp, &wp->base);
@@ -1085,6 +1094,7 @@ input_reply(struct input_ctx *ictx, const char *fmt, ...)
xvasprintf(&reply, fmt, ap); xvasprintf(&reply, fmt, ap);
va_end(ap); va_end(ap);
log_debug("%s: %s", __func__, reply);
bufferevent_write(bev, reply, strlen(reply)); bufferevent_write(bev, reply, strlen(reply));
free(reply); free(reply);
} }
@@ -1344,8 +1354,8 @@ input_csi_dispatch(struct input_ctx *ictx)
if (ictx->flags & INPUT_DISCARD) if (ictx->flags & INPUT_DISCARD)
return (0); return (0);
log_debug("%s: '%c' \"%s\" \"%s\"", log_debug("%s: '%c' \"%s\" \"%s\"", __func__, ictx->ch,
__func__, ictx->ch, ictx->interm_buf, ictx->param_buf); ictx->interm_buf, ictx->param_buf);
if (input_split(ictx) != 0) if (input_split(ictx) != 0)
return (0); return (0);
@@ -1436,7 +1446,11 @@ input_csi_dispatch(struct input_ctx *ictx)
case -1: case -1:
break; break;
case 0: case 0:
#ifdef ENABLE_SIXEL
input_reply(ictx, "\033[?1;2;4c");
#else
input_reply(ictx, "\033[?1;2c"); input_reply(ictx, "\033[?1;2c");
#endif
break; break;
default: default:
log_debug("%s: unknown '%c'", __func__, ictx->ch); log_debug("%s: unknown '%c'", __func__, ictx->ch);
@@ -1588,6 +1602,9 @@ input_csi_dispatch(struct input_ctx *ictx)
case INPUT_CSI_SM_PRIVATE: case INPUT_CSI_SM_PRIVATE:
input_csi_dispatch_sm_private(ictx); input_csi_dispatch_sm_private(ictx);
break; break;
case INPUT_CSI_SM_GRAPHICS:
input_csi_dispatch_sm_graphics(ictx);
break;
case INPUT_CSI_SU: case INPUT_CSI_SU:
n = input_get(ictx, 0, 1, 1); n = input_get(ictx, 0, 1, 1);
if (n != -1) if (n != -1)
@@ -1754,7 +1771,6 @@ static void
input_csi_dispatch_sm_private(struct input_ctx *ictx) input_csi_dispatch_sm_private(struct input_ctx *ictx)
{ {
struct screen_write_ctx *sctx = &ictx->ctx; struct screen_write_ctx *sctx = &ictx->ctx;
struct window_pane *wp = ictx->wp;
struct grid_cell *gc = &ictx->cell.cell; struct grid_cell *gc = &ictx->cell.cell;
u_int i; u_int i;
@@ -1796,17 +1812,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
screen_write_mode_set(sctx, MODE_MOUSE_ALL); screen_write_mode_set(sctx, MODE_MOUSE_ALL);
break; break;
case 1004: case 1004:
if (sctx->s->mode & MODE_FOCUSON)
break;
screen_write_mode_set(sctx, MODE_FOCUSON); screen_write_mode_set(sctx, MODE_FOCUSON);
if (wp == NULL)
break;
if (!options_get_number(global_options, "focus-events"))
break;
if (wp->flags & PANE_FOCUSED)
bufferevent_write(wp->event, "\033[I", 3);
else
bufferevent_write(wp->event, "\033[O", 3);
break; break;
case 1005: case 1005:
screen_write_mode_set(sctx, MODE_MOUSE_UTF8); screen_write_mode_set(sctx, MODE_MOUSE_UTF8);
@@ -1831,6 +1837,26 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
} }
} }
/* Handle CSI graphics SM. */
static void
input_csi_dispatch_sm_graphics(struct input_ctx *ictx)
{
#ifdef ENABLE_SIXEL
int n, m, o;
if (ictx->param_list_len > 3)
return;
n = input_get(ictx, 0, 0, 0);
m = input_get(ictx, 1, 0, 0);
o = input_get(ictx, 2, 0, 0);
if (n == 1 && (m == 1 || m == 2 || m == 4))
input_reply(ictx, "\033[?%d;0;%uS", n, SIXEL_COLOUR_REGISTERS);
else
input_reply(ictx, "\033[?%d;3;%dS", n, o);
#endif
}
/* Handle CSI window operations. */ /* Handle CSI window operations. */
static void static void
input_csi_dispatch_winops(struct input_ctx *ictx) input_csi_dispatch_winops(struct input_ctx *ictx)
@@ -1838,9 +1864,13 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
struct screen_write_ctx *sctx = &ictx->ctx; struct screen_write_ctx *sctx = &ictx->ctx;
struct screen *s = sctx->s; struct screen *s = sctx->s;
struct window_pane *wp = ictx->wp; struct window_pane *wp = ictx->wp;
struct window *w = NULL;
u_int x = screen_size_x(s), y = screen_size_y(s); u_int x = screen_size_x(s), y = screen_size_y(s);
int n, m; int n, m;
if (wp != NULL)
w = wp->window;
m = 0; m = 0;
while ((n = input_get(ictx, m, 0, -1)) != -1) { while ((n = input_get(ictx, m, 0, -1)) != -1) {
switch (n) { switch (n) {
@@ -1851,8 +1881,6 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
case 7: case 7:
case 11: case 11:
case 13: case 13:
case 14:
case 19:
case 20: case 20:
case 21: case 21:
case 24: case 24:
@@ -1870,6 +1898,30 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
if (input_get(ictx, m, 0, -1) == -1) if (input_get(ictx, m, 0, -1) == -1)
return; return;
break; break;
case 14:
if (w == NULL)
break;
input_reply(ictx, "\033[4;%u;%ut", y * w->ypixel,
x * w->xpixel);
break;
case 15:
if (w == NULL)
break;
input_reply(ictx, "\033[5;%u;%ut", y * w->ypixel,
x * w->xpixel);
break;
case 16:
if (w == NULL)
break;
input_reply(ictx, "\033[6;%u;%ut", w->ypixel,
w->xpixel);
break;
case 18:
input_reply(ictx, "\033[8;%u;%ut", y, x);
break;
case 19:
input_reply(ictx, "\033[9;%u;%ut", y, x);
break;
case 22: case 22:
m++; m++;
switch (input_get(ictx, m, 0, -1)) { switch (input_get(ictx, m, 0, -1)) {
@@ -1892,14 +1944,11 @@ input_csi_dispatch_winops(struct input_ctx *ictx)
if (wp == NULL) if (wp == NULL)
break; break;
notify_pane("pane-title-changed", wp); notify_pane("pane-title-changed", wp);
server_redraw_window_borders(wp->window); server_redraw_window_borders(w);
server_status_window(wp->window); server_status_window(w);
break; break;
} }
break; break;
case 18:
input_reply(ictx, "\033[8;%u;%ut", x, y);
break;
default: default:
log_debug("%s: unknown '%c'", __func__, ictx->ch); log_debug("%s: unknown '%c'", __func__, ictx->ch);
break; break;
@@ -2070,7 +2119,7 @@ static void
input_csi_dispatch_sgr(struct input_ctx *ictx) input_csi_dispatch_sgr(struct input_ctx *ictx)
{ {
struct grid_cell *gc = &ictx->cell.cell; struct grid_cell *gc = &ictx->cell.cell;
u_int i; u_int i, link;
int n; int n;
if (ictx->param_list_len == 0) { if (ictx->param_list_len == 0) {
@@ -2102,7 +2151,9 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
switch (n) { switch (n) {
case 0: case 0:
link = gc->link;
memcpy(gc, &grid_default_cell, sizeof *gc); memcpy(gc, &grid_default_cell, sizeof *gc);
gc->link = link;
break; break;
case 1: case 1:
gc->attr |= GRID_ATTR_BRIGHT; gc->attr |= GRID_ATTR_BRIGHT;
@@ -2188,7 +2239,7 @@ input_csi_dispatch_sgr(struct input_ctx *ictx)
gc->attr &= ~GRID_ATTR_OVERLINE; gc->attr &= ~GRID_ATTR_OVERLINE;
break; break;
case 59: case 59:
gc->us = 0; gc->us = 8;
break; break;
case 90: case 90:
case 91: case 91:
@@ -2246,17 +2297,38 @@ input_dcs_dispatch(struct input_ctx *ictx)
size_t len = ictx->input_len; size_t len = ictx->input_len;
const char prefix[] = "tmux;"; const char prefix[] = "tmux;";
const u_int prefixlen = (sizeof prefix) - 1; const u_int prefixlen = (sizeof prefix) - 1;
long long allow_passthrough = 0;
#ifdef ENABLE_SIXEL
struct window *w;
struct sixel_image *si;
#endif
if (wp == NULL) if (wp == NULL)
return (0); return (0);
if (ictx->flags & INPUT_DISCARD)
if (ictx->flags & INPUT_DISCARD) {
log_debug("%s: %zu bytes (discard)", __func__, len);
return (0); return (0);
if (!options_get_number(ictx->wp->options, "allow-passthrough")) }
#ifdef ENABLE_SIXEL
w = wp->window;
if (buf[0] == 'q') {
si = sixel_parse(buf, len, w->xpixel, w->ypixel);
if (si != NULL)
screen_write_sixelimage(sctx, si, ictx->cell.cell.bg);
}
#endif
allow_passthrough = options_get_number(wp->options, "allow-passthrough");
if (!allow_passthrough)
return (0); return (0);
log_debug("%s: \"%s\"", __func__, buf); log_debug("%s: \"%s\"", __func__, buf);
if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0) if (len >= prefixlen && strncmp(buf, prefix, prefixlen) == 0) {
screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen); screen_write_rawstring(sctx, buf + prefixlen, len - prefixlen,
allow_passthrough == 2);
}
return (0); return (0);
} }
@@ -2292,6 +2364,8 @@ input_exit_osc(struct input_ctx *ictx)
option = 0; option = 0;
while (*p >= '0' && *p <= '9') while (*p >= '0' && *p <= '9')
option = option * 10 + *p++ - '0'; option = option * 10 + *p++ - '0';
if (*p != ';' && *p != '\0')
return;
if (*p == ';') if (*p == ';')
p++; p++;
@@ -2316,6 +2390,9 @@ input_exit_osc(struct input_ctx *ictx)
} }
} }
break; break;
case 8:
input_osc_8(ictx, p);
break;
case 10: case 10:
input_osc_10(ictx, p); input_osc_10(ictx, p);
break; break;
@@ -2340,6 +2417,9 @@ input_exit_osc(struct input_ctx *ictx)
case 112: case 112:
input_osc_112(ictx, p); input_osc_112(ictx, p);
break; break;
case 133:
input_osc_133(ictx, p);
break;
default: default:
log_debug("%s: unknown '%u'", __func__, option); log_debug("%s: unknown '%u'", __func__, option);
break; break;
@@ -2456,47 +2536,6 @@ input_top_bit_set(struct input_ctx *ictx)
return (0); return (0);
} }
/* Parse colour from OSC. */
static int
input_osc_parse_colour(const char *p)
{
double c, m, y, k = 0;
u_int r, g, b;
size_t len = strlen(p);
int colour = -1;
char *copy;
if ((len == 12 && sscanf(p, "rgb:%02x/%02x/%02x", &r, &g, &b) == 3) ||
(len == 7 && sscanf(p, "#%02x%02x%02x", &r, &g, &b) == 3) ||
sscanf(p, "%d,%d,%d", &r, &g, &b) == 3)
colour = colour_join_rgb(r, g, b);
else if ((len == 18 &&
sscanf(p, "rgb:%04x/%04x/%04x", &r, &g, &b) == 3) ||
(len == 13 && sscanf(p, "#%04x%04x%04x", &r, &g, &b) == 3))
colour = colour_join_rgb(r >> 8, g >> 8, b >> 8);
else if ((sscanf(p, "cmyk:%lf/%lf/%lf/%lf", &c, &m, &y, &k) == 4 ||
sscanf(p, "cmy:%lf/%lf/%lf", &c, &m, &y) == 3) &&
c >= 0 && c <= 1 && m >= 0 && m <= 1 &&
y >= 0 && y <= 1 && k >= 0 && k <= 1) {
colour = colour_join_rgb(
(1 - c) * (1 - k) * 255,
(1 - m) * (1 - k) * 255,
(1 - y) * (1 - k) * 255);
} else {
while (len != 0 && *p == ' ') {
p++;
len--;
}
while (len != 0 && p[len - 1] == ' ')
len--;
copy = xstrndup(p, len);
colour = colour_byname(copy);
free(copy);
}
log_debug("%s: %s = %s", __func__, p, colour_tostring(colour));
return (colour);
}
/* Reply to a colour request. */ /* Reply to a colour request. */
static void static void
input_osc_colour_reply(struct input_ctx *ictx, u_int n, int c) input_osc_colour_reply(struct input_ctx *ictx, u_int n, int c)
@@ -2545,7 +2584,7 @@ input_osc_4(struct input_ctx *ictx, const char *p)
input_osc_colour_reply(ictx, 4, c); input_osc_colour_reply(ictx, 4, c);
continue; continue;
} }
if ((c = input_osc_parse_colour(s)) == -1) { if ((c = colour_parseX11(s)) == -1) {
s = next; s = next;
continue; continue;
} }
@@ -2560,6 +2599,88 @@ input_osc_4(struct input_ctx *ictx, const char *p)
free(copy); free(copy);
} }
/* Handle the OSC 8 sequence for embedding hyperlinks. */
static void
input_osc_8(struct input_ctx *ictx, const char *p)
{
struct hyperlinks *hl = ictx->ctx.s->hyperlinks;
struct grid_cell *gc = &ictx->cell.cell;
const char *start, *end, *uri;
char *id = NULL;
for (start = p; (end = strpbrk(start, ":;")) != NULL; start = end + 1) {
if (end - start >= 4 && strncmp(start, "id=", 3) == 0) {
if (id != NULL)
goto bad;
id = xstrndup(start + 3, end - start - 3);
}
/* The first ; is the end of parameters and start of the URI. */
if (*end == ';')
break;
}
if (end == NULL || *end != ';')
goto bad;
uri = end + 1;
if (*uri == '\0') {
gc->link = 0;
free(id);
return;
}
gc->link = hyperlinks_put(hl, uri, id);
if (id == NULL)
log_debug("hyperlink (anonymous) %s = %u", uri, gc->link);
else
log_debug("hyperlink (id=%s) %s = %u", id, uri, gc->link);
free(id);
return;
bad:
log_debug("bad OSC 8 %s", p);
free(id);
}
/*
* Get a client with a foreground for the pane. There isn't much to choose
* between them so just use the first.
*/
static int
input_get_fg_client(struct window_pane *wp)
{
struct window *w = wp->window;
struct client *loop;
TAILQ_FOREACH(loop, &clients, entry) {
if (loop->flags & CLIENT_UNATTACHEDFLAGS)
continue;
if (loop->session == NULL || !session_has(loop->session, w))
continue;
if (loop->tty.fg == -1)
continue;
return (loop->tty.fg);
}
return (-1);
}
/* Get a client with a background for the pane. */
static int
input_get_bg_client(struct window_pane *wp)
{
struct window *w = wp->window;
struct client *loop;
TAILQ_FOREACH(loop, &clients, entry) {
if (loop->flags & CLIENT_UNATTACHEDFLAGS)
continue;
if (loop->session == NULL || !session_has(loop->session, w))
continue;
if (loop->tty.bg == -1)
continue;
return (loop->tty.bg);
}
return (-1);
}
/* Handle the OSC 10 sequence for setting and querying foreground colour. */ /* Handle the OSC 10 sequence for setting and querying foreground colour. */
static void static void
input_osc_10(struct input_ctx *ictx, const char *p) input_osc_10(struct input_ctx *ictx, const char *p)
@@ -2569,14 +2690,18 @@ input_osc_10(struct input_ctx *ictx, const char *p)
int c; int c;
if (strcmp(p, "?") == 0) { if (strcmp(p, "?") == 0) {
if (wp != NULL) { if (wp == NULL)
tty_default_colours(&defaults, wp); return;
input_osc_colour_reply(ictx, 10, defaults.fg); tty_default_colours(&defaults, wp);
} if (COLOUR_DEFAULT(defaults.fg))
c = input_get_fg_client(wp);
else
c = defaults.fg;
input_osc_colour_reply(ictx, 10, c);
return; return;
} }
if ((c = input_osc_parse_colour(p)) == -1) { if ((c = colour_parseX11(p)) == -1) {
log_debug("bad OSC 10: %s", p); log_debug("bad OSC 10: %s", p);
return; return;
} }
@@ -2613,14 +2738,18 @@ input_osc_11(struct input_ctx *ictx, const char *p)
int c; int c;
if (strcmp(p, "?") == 0) { if (strcmp(p, "?") == 0) {
if (wp != NULL) { if (wp == NULL)
tty_default_colours(&defaults, wp); return;
input_osc_colour_reply(ictx, 11, defaults.bg); tty_default_colours(&defaults, wp);
} if (COLOUR_DEFAULT(defaults.bg))
c = input_get_bg_client(wp);
else
c = defaults.bg;
input_osc_colour_reply(ictx, 11, c);
return; return;
} }
if ((c = input_osc_parse_colour(p)) == -1) { if ((c = colour_parseX11(p)) == -1) {
log_debug("bad OSC 11: %s", p); log_debug("bad OSC 11: %s", p);
return; return;
} }
@@ -2665,7 +2794,7 @@ input_osc_12(struct input_ctx *ictx, const char *p)
return; return;
} }
if ((c = input_osc_parse_colour(p)) == -1) { if ((c = colour_parseX11(p)) == -1) {
log_debug("bad OSC 12: %s", p); log_debug("bad OSC 12: %s", p);
return; return;
} }
@@ -2680,6 +2809,27 @@ input_osc_112(struct input_ctx *ictx, const char *p)
screen_set_cursor_colour(ictx->ctx.s, -1); screen_set_cursor_colour(ictx->ctx.s, -1);
} }
/* Handle the OSC 133 sequence. */
static void
input_osc_133(struct input_ctx *ictx, const char *p)
{
struct grid *gd = ictx->ctx.s->grid;
u_int line = ictx->ctx.s->cy + gd->hsize;
struct grid_line *gl;
if (line > gd->hsize + gd->sy - 1)
return;
gl = grid_get_line(gd, line);
switch (*p) {
case 'A':
gl->flags |= GRID_LINE_START_PROMPT;
break;
case 'C':
gl->flags |= GRID_LINE_START_OUTPUT;
break;
}
}
/* Handle the OSC 52 sequence for setting the clipboard. */ /* Handle the OSC 52 sequence for setting the clipboard. */
static void static void
@@ -2693,6 +2843,9 @@ input_osc_52(struct input_ctx *ictx, const char *p)
int outlen, state; int outlen, state;
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
struct paste_buffer *pb; struct paste_buffer *pb;
const char* allow = "cpqs01234567";
char flags[sizeof "cpqs01234567"] = "";
u_int i, j = 0;
if (wp == NULL) if (wp == NULL)
return; return;
@@ -2707,6 +2860,12 @@ input_osc_52(struct input_ctx *ictx, const char *p)
return; return;
log_debug("%s: %s", __func__, end); log_debug("%s: %s", __func__, end);
for (i = 0; p + i != end; i++) {
if (strchr(allow, p[i]) != NULL && strchr(flags, p[i]) == NULL)
flags[j++] = p[i];
}
log_debug("%s: %.*s %s", __func__, (int)(end - p - 1), p, flags);
if (strcmp(end, "?") == 0) { if (strcmp(end, "?") == 0) {
if ((pb = paste_get_top(NULL)) != NULL) if ((pb = paste_get_top(NULL)) != NULL)
buf = paste_buffer_data(pb, &len); buf = paste_buffer_data(pb, &len);
@@ -2728,7 +2887,7 @@ input_osc_52(struct input_ctx *ictx, const char *p)
} }
screen_write_start_pane(&ctx, wp, NULL); screen_write_start_pane(&ctx, wp, NULL);
screen_write_setselection(&ctx, out, outlen); screen_write_setselection(&ctx, flags, out, outlen);
screen_write_stop(&ctx); screen_write_stop(&ctx);
notify_pane("pane-set-clipboard", wp); notify_pane("pane-set-clipboard", wp);
@@ -2777,9 +2936,11 @@ input_reply_clipboard(struct bufferevent *bev, const char *buf, size_t len,
const char *end) const char *end)
{ {
char *out = NULL; char *out = NULL;
size_t outlen = 0; int outlen = 0;
if (buf != NULL && len != 0) { if (buf != NULL && len != 0) {
if (len >= ((size_t)INT_MAX * 3 / 4) - 1)
return;
outlen = 4 * ((len + 2) / 3) + 1; outlen = 4 * ((len + 2) / 3) + 1;
out = xmalloc(outlen); out = xmalloc(outlen);
if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) { if ((outlen = b64_ntop(buf, len, out, outlen)) == -1) {

View File

@@ -54,6 +54,9 @@
" '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {copy-mode -q; set-buffer -- \"#{q:mouse_word}\"}" \ " '#{?mouse_word,Copy #[underscore]#{=/9/...:mouse_word},}' 'c' {copy-mode -q; set-buffer -- \"#{q:mouse_word}\"}" \
" '#{?mouse_line,Copy Line,}' 'l' {copy-mode -q; set-buffer -- \"#{q:mouse_line}\"}" \ " '#{?mouse_line,Copy Line,}' 'l' {copy-mode -q; set-buffer -- \"#{q:mouse_line}\"}" \
" ''" \ " ''" \
" '#{?mouse_hyperlink,Type #[underscore]#{=/9/...:mouse_hyperlink},}' 'C-h' {copy-mode -q; send-keys -l -- \"#{q:mouse_hyperlink}\"}" \
" '#{?mouse_hyperlink,Copy #[underscore]#{=/9/...:mouse_hyperlink},}' 'h' {copy-mode -q; set-buffer -- \"#{q:mouse_hyperlink}\"}" \
" ''" \
" 'Horizontal Split' 'h' {split-window -h}" \ " 'Horizontal Split' 'h' {split-window -h}" \
" 'Vertical Split' 'v' {split-window -v}" \ " 'Vertical Split' 'v' {split-window -v}" \
" ''" \ " ''" \
@@ -341,7 +344,7 @@ key_bindings_init_done(__unused struct cmdq_item *item, __unused void *data)
void void
key_bindings_init(void) key_bindings_init(void)
{ {
static const char *defaults[] = { static const char *const defaults[] = {
/* Prefix keys. */ /* Prefix keys. */
"bind -N 'Send the prefix key' C-b { send-prefix }", "bind -N 'Send the prefix key' C-b { send-prefix }",
"bind -N 'Rotate through the panes' C-o { rotate-window }", "bind -N 'Rotate through the panes' C-o { rotate-window }",
@@ -374,7 +377,7 @@ key_bindings_init(void)
"bind -N 'Move to the previously active pane' \\; { last-pane }", "bind -N 'Move to the previously active pane' \\; { last-pane }",
"bind -N 'Choose a paste buffer from a list' = { choose-buffer -Z }", "bind -N 'Choose a paste buffer from a list' = { choose-buffer -Z }",
"bind -N 'List key bindings' ? { list-keys -N }", "bind -N 'List key bindings' ? { list-keys -N }",
"bind -N 'Choose a client from a list' D { choose-client -Z }", "bind -N 'Choose and detach a client from a list' D { choose-client -Z }",
"bind -N 'Spread panes out evenly' E { select-layout -E }", "bind -N 'Spread panes out evenly' E { select-layout -E }",
"bind -N 'Switch to the last client' L { switch-client -l }", "bind -N 'Switch to the last client' L { switch-client -l }",
"bind -N 'Clear the marked pane' M { select-pane -M }", "bind -N 'Clear the marked pane' M { select-pane -M }",
@@ -463,9 +466,11 @@ key_bindings_init(void)
/* Mouse button 3 down on status left. */ /* 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 MouseDown3StatusLeft { display-menu -t= -xM -yW -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU " }",
"bind -n M-MouseDown3StatusLeft { display-menu -t= -xM -yW -T '#[align=centre]#{session_name}' " DEFAULT_SESSION_MENU " }",
/* Mouse button 3 down on status line. */ /* Mouse button 3 down on status line. */
"bind -n MouseDown3Status { display-menu -t= -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU "}", "bind -n MouseDown3Status { display-menu -t= -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU "}",
"bind -n M-MouseDown3Status { display-menu -t= -xW -yW -T '#[align=centre]#{window_index}:#{window_name}' " DEFAULT_WINDOW_MENU "}",
/* Mouse button 3 down on pane. */ /* Mouse button 3 down on pane. */
"bind -n MouseDown3Pane { if -Ft= '#{||:#{mouse_any_flag},#{&&:#{pane_in_mode},#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}}}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " } }", "bind -n MouseDown3Pane { if -Ft= '#{||:#{mouse_any_flag},#{&&:#{pane_in_mode},#{?#{m/r:(copy|view)-mode,#{pane_mode}},0,1}}}' { select-pane -t=; send -M } { display-menu -t= -xM -yM -T '#[align=centre]#{pane_index} (#{pane_id})' " DEFAULT_PANE_MENU " } }",
@@ -602,6 +607,7 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi h { send -X cursor-left }", "bind -Tcopy-mode-vi h { send -X cursor-left }",
"bind -Tcopy-mode-vi j { send -X cursor-down }", "bind -Tcopy-mode-vi j { send -X cursor-down }",
"bind -Tcopy-mode-vi k { send -X cursor-up }", "bind -Tcopy-mode-vi k { send -X cursor-up }",
"bind -Tcopy-mode-vi z { send -X scroll-middle }",
"bind -Tcopy-mode-vi l { send -X cursor-right }", "bind -Tcopy-mode-vi l { send -X cursor-right }",
"bind -Tcopy-mode-vi n { send -X search-again }", "bind -Tcopy-mode-vi n { send -X search-again }",
"bind -Tcopy-mode-vi o { send -X other-end }", "bind -Tcopy-mode-vi o { send -X other-end }",
@@ -613,6 +619,8 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi '{' { send -X previous-paragraph }", "bind -Tcopy-mode-vi '{' { send -X previous-paragraph }",
"bind -Tcopy-mode-vi '}' { send -X next-paragraph }", "bind -Tcopy-mode-vi '}' { send -X next-paragraph }",
"bind -Tcopy-mode-vi % { send -X next-matching-bracket }", "bind -Tcopy-mode-vi % { send -X next-matching-bracket }",
"bind -Tcopy-mode-vi Home { send -X start-of-line }",
"bind -Tcopy-mode-vi End { send -X end-of-line }",
"bind -Tcopy-mode-vi MouseDown1Pane { select-pane }", "bind -Tcopy-mode-vi MouseDown1Pane { select-pane }",
"bind -Tcopy-mode-vi MouseDrag1Pane { select-pane; send -X begin-selection }", "bind -Tcopy-mode-vi MouseDrag1Pane { select-pane; send -X begin-selection }",
"bind -Tcopy-mode-vi MouseDragEnd1Pane { send -X copy-pipe-and-cancel }", "bind -Tcopy-mode-vi MouseDragEnd1Pane { send -X copy-pipe-and-cancel }",

View File

@@ -460,6 +460,10 @@ out:
strlcat(out, "I", sizeof out); strlcat(out, "I", sizeof out);
if (saved & KEYC_BUILD_MODIFIERS) if (saved & KEYC_BUILD_MODIFIERS)
strlcat(out, "B", sizeof out); strlcat(out, "B", sizeof out);
if (saved & KEYC_EXTENDED)
strlcat(out, "E", sizeof out);
if (saved & KEYC_SENT)
strlcat(out, "S", sizeof out);
strlcat(out, "]", sizeof out); strlcat(out, "]", sizeof out);
} }
return (out); return (out);

View File

@@ -162,8 +162,10 @@ layout_parse(struct window *w, const char *layout, char **cause)
u_short csum; u_short csum;
/* Check validity. */ /* Check validity. */
if (sscanf(layout, "%hx,", &csum) != 1) if (sscanf(layout, "%hx,", &csum) != 1) {
*cause = xstrdup("invalid layout");
return (-1); return (-1);
}
layout += 5; layout += 5;
if (csum != layout_checksum(layout)) { if (csum != layout_checksum(layout)) {
*cause = xstrdup("invalid layout"); *cause = xstrdup("invalid layout");

177
menu.c
View File

@@ -27,6 +27,11 @@ struct menu_data {
struct cmdq_item *item; struct cmdq_item *item;
int flags; int flags;
struct grid_cell style;
struct grid_cell border_style;
struct grid_cell selected_style;
enum box_lines border_lines;
struct cmd_find_state fs; struct cmd_find_state fs;
struct screen s; struct screen s;
@@ -64,6 +69,8 @@ menu_add_item(struct menu *menu, const struct menu_item *item,
line = (item == NULL || item->name == NULL || *item->name == '\0'); line = (item == NULL || item->name == NULL || *item->name == '\0');
if (line && menu->count == 0) if (line && menu->count == 0)
return; return;
if (line && menu->items[menu->count - 1].name == NULL)
return;
menu->items = xreallocarray(menu->items, menu->count + 1, menu->items = xreallocarray(menu->items, menu->count + 1,
sizeof *menu->items); sizeof *menu->items);
@@ -160,11 +167,16 @@ menu_free(struct menu *menu)
} }
struct screen * struct screen *
menu_mode_cb(__unused struct client *c, void *data, __unused u_int *cx, menu_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
__unused u_int *cy)
{ {
struct menu_data *md = data; struct menu_data *md = data;
*cx = md->px + 2;
if (md->choice == -1)
*cy = md->py;
else
*cy = md->py + 1 + md->choice;
return (&md->s); return (&md->s);
} }
@@ -190,13 +202,17 @@ menu_draw_cb(struct client *c, void *data,
struct menu *menu = md->menu; struct menu *menu = md->menu;
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
u_int i, px = md->px, py = md->py; u_int i, px = md->px, py = md->py;
struct grid_cell gc;
style_apply(&gc, c->session->curw->window->options, "mode-style", NULL);
screen_write_start(&ctx, s); screen_write_start(&ctx, s);
screen_write_clearscreen(&ctx, 8); screen_write_clearscreen(&ctx, 8);
screen_write_menu(&ctx, menu, md->choice, &gc);
if (md->border_lines != BOX_LINES_NONE) {
screen_write_box(&ctx, menu->width + 4, menu->count + 2,
md->border_lines, &md->border_style, menu->title);
}
screen_write_menu(&ctx, menu, md->choice, md->border_lines,
&md->style, &md->border_style, &md->selected_style);
screen_write_stop(&ctx); screen_write_stop(&ctx);
for (i = 0; i < screen_size_y(&md->s); i++) { for (i = 0; i < screen_size_y(&md->s); i++) {
@@ -318,27 +334,64 @@ menu_key_cb(struct client *c, void *data, struct key_event *event)
} while ((name == NULL || *name == '-') && md->choice != old); } while ((name == NULL || *name == '-') && md->choice != old);
c->flags |= CLIENT_REDRAWOVERLAY; c->flags |= CLIENT_REDRAWOVERLAY;
return (0); return (0);
case 'g':
case KEYC_PPAGE: case KEYC_PPAGE:
case '\002': /* C-b */ case '\002': /* C-b */
if (md->choice > 5) if (md->choice < 6)
md->choice -= 5;
else
md->choice = 0; md->choice = 0;
while (md->choice != count && (name == NULL || *name == '-')) else {
i = 5;
while (i > 0) {
md->choice--;
name = menu->items[md->choice].name;
if (md->choice != 0 &&
(name != NULL && *name != '-'))
i--;
else if (md->choice == 0)
break;
}
}
c->flags |= CLIENT_REDRAWOVERLAY;
break;
case KEYC_NPAGE:
if (md->choice > count - 6) {
md->choice = count - 1;
name = menu->items[md->choice].name;
} else {
i = 5;
while (i > 0) {
md->choice++;
name = menu->items[md->choice].name;
if (md->choice != count - 1 &&
(name != NULL && *name != '-'))
i++;
else if (md->choice == count - 1)
break;
}
}
while (name == NULL || *name == '-') {
md->choice--;
name = menu->items[md->choice].name;
}
c->flags |= CLIENT_REDRAWOVERLAY;
break;
case 'g':
case KEYC_HOME:
md->choice = 0;
name = menu->items[md->choice].name;
while (name == NULL || *name == '-') {
md->choice++; md->choice++;
if (md->choice == count) name = menu->items[md->choice].name;
md->choice = -1; }
c->flags |= CLIENT_REDRAWOVERLAY; c->flags |= CLIENT_REDRAWOVERLAY;
break; break;
case 'G': case 'G':
case KEYC_NPAGE: case KEYC_END:
if (md->choice > count - 6) md->choice = count - 1;
md->choice = count - 1; name = menu->items[md->choice].name;
else while (name == NULL || *name == '-') {
md->choice += 5;
while (md->choice != -1 && (name == NULL || *name == '-'))
md->choice--; md->choice--;
name = menu->items[md->choice].name;
}
c->flags |= CLIENT_REDRAWOVERLAY; c->flags |= CLIENT_REDRAWOVERLAY;
break; break;
case '\006': /* C-f */ case '\006': /* C-f */
@@ -384,14 +437,36 @@ chosen:
return (1); return (1);
} }
static void
menu_set_style(struct client *c, struct grid_cell *gc, const char *style,
const char *option)
{
struct style sytmp;
struct options *o = c->session->curw->window->options;
memcpy(gc, &grid_default_cell, sizeof *gc);
style_apply(gc, o, option, NULL);
if (style != NULL) {
style_set(&sytmp, &grid_default_cell);
if (style_parse(&sytmp, gc, style) == 0) {
gc->fg = sytmp.gc.fg;
gc->bg = sytmp.gc.bg;
}
}
gc->attr = 0;
}
struct menu_data * struct menu_data *
menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px, menu_prepare(struct menu *menu, int flags, int starting_choice,
u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb, struct cmdq_item *item, u_int px, u_int py, struct client *c,
enum box_lines lines, const char *style, const char *selected_style,
const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb,
void *data) void *data)
{ {
struct menu_data *md; struct menu_data *md;
u_int i; int choice;
const char *name; const char *name;
struct options *o = c->session->curw->window->options;
if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2) if (c->tty.sx < menu->width + 4 || c->tty.sy < menu->count + 2)
return (NULL); return (NULL);
@@ -400,9 +475,18 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
if (py + menu->count + 2 > c->tty.sy) if (py + menu->count + 2 > c->tty.sy)
py = c->tty.sy - menu->count - 2; py = c->tty.sy - menu->count - 2;
if (lines == BOX_LINES_DEFAULT)
lines = options_get_number(o, "menu-border-lines");
md = xcalloc(1, sizeof *md); md = xcalloc(1, sizeof *md);
md->item = item; md->item = item;
md->flags = flags; md->flags = flags;
md->border_lines = lines;
menu_set_style(c, &md->style, style, "menu-style");
menu_set_style(c, &md->selected_style, selected_style,
"menu-selected-style");
menu_set_style(c, &md->border_style, border_style, "menu-border-style");
if (fs != NULL) if (fs != NULL)
cmd_find_copy_state(&md->fs, fs); cmd_find_copy_state(&md->fs, fs);
@@ -415,18 +499,38 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
md->py = py; md->py = py;
md->menu = menu; md->menu = menu;
md->choice = -1;
if (md->flags & MENU_NOMOUSE) { if (md->flags & MENU_NOMOUSE) {
for (i = 0; i < menu->count; i++) { if (starting_choice >= (int)menu->count) {
name = menu->items[i].name; starting_choice = menu->count - 1;
if (name != NULL && *name != '-') choice = starting_choice + 1;
break; for (;;) {
name = menu->items[choice - 1].name;
if (name != NULL && *name != '-') {
md->choice = choice - 1;
break;
}
if (--choice == 0)
choice = menu->count;
if (choice == starting_choice + 1)
break;
}
} else if (starting_choice >= 0) {
choice = starting_choice;
for (;;) {
name = menu->items[choice].name;
if (name != NULL && *name != '-') {
md->choice = choice;
break;
}
if (++choice == (int)menu->count)
choice = 0;
if (choice == starting_choice)
break;
}
} }
if (i != menu->count) }
md->choice = i;
else
md->choice = -1;
} else
md->choice = -1;
md->cb = cb; md->cb = cb;
md->data = data; md->data = data;
@@ -434,13 +538,16 @@ menu_prepare(struct menu *menu, int flags, struct cmdq_item *item, u_int px,
} }
int int
menu_display(struct menu *menu, int flags, struct cmdq_item *item, u_int px, menu_display(struct menu *menu, int flags, int starting_choice,
u_int py, struct client *c, struct cmd_find_state *fs, menu_choice_cb cb, struct cmdq_item *item, u_int px, u_int py, struct client *c,
enum box_lines lines, const char *style, const char *selected_style,
const char *border_style, struct cmd_find_state *fs, menu_choice_cb cb,
void *data) void *data)
{ {
struct menu_data *md; struct menu_data *md;
md = menu_prepare(menu, flags, item, px, py, c, fs, cb, data); md = menu_prepare(menu, flags, starting_choice, item, px, py, c, lines,
style, selected_style, border_style, fs, cb, data);
if (md == NULL) if (md == NULL)
return (-1); return (-1);
server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb, server_client_set_overlay(c, 0, NULL, menu_mode_cb, menu_draw_cb,

View File

@@ -497,7 +497,7 @@ mode_tree_build(struct mode_tree_data *mtd)
mode_tree_clear_lines(mtd); mode_tree_clear_lines(mtd);
mode_tree_build_lines(mtd, &mtd->children, 0); mode_tree_build_lines(mtd, &mtd->children, 0);
if (tag == UINT64_MAX) if (mtd->line_list != NULL && tag == UINT64_MAX)
tag = mtd->line_list[mtd->current].item->tag; tag = mtd->line_list[mtd->current].item->tag;
mode_tree_set_current(mtd, tag); mode_tree_set_current(mtd, tag);
@@ -962,8 +962,8 @@ mode_tree_display_menu(struct mode_tree_data *mtd, struct client *c, u_int x,
x -= (menu->width + 4) / 2; x -= (menu->width + 4) / 2;
else else
x = 0; x = 0;
if (menu_display(menu, 0, NULL, x, y, c, NULL, mode_tree_menu_callback, if (menu_display(menu, 0, 0, NULL, x, y, c, BOX_LINES_DEFAULT, NULL,
mtm) != 0) NULL, NULL, NULL, mode_tree_menu_callback, mtm) != 0)
menu_free(menu); menu_free(menu);
} }

View File

@@ -32,6 +32,7 @@ struct notify_entry {
struct session *session; struct session *session;
struct window *window; struct window *window;
int pane; int pane;
const char *pbname;
}; };
static struct cmdq_item * static struct cmdq_item *
@@ -149,6 +150,10 @@ notify_callback(struct cmdq_item *item, void *data)
control_notify_session_closed(ne->session); control_notify_session_closed(ne->session);
if (strcmp(ne->name, "session-window-changed") == 0) if (strcmp(ne->name, "session-window-changed") == 0)
control_notify_session_window_changed(ne->session); control_notify_session_window_changed(ne->session);
if (strcmp(ne->name, "paste-buffer-changed") == 0)
control_notify_paste_buffer_changed(ne->pbname);
if (strcmp(ne->name, "paste-buffer-deleted") == 0)
control_notify_paste_buffer_deleted(ne->pbname);
notify_insert_hook(item, ne); notify_insert_hook(item, ne);
@@ -164,6 +169,7 @@ notify_callback(struct cmdq_item *item, void *data)
format_free(ne->formats); format_free(ne->formats);
free((void *)ne->name); free((void *)ne->name);
free((void *)ne->pbname);
free(ne); free(ne);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
@@ -171,7 +177,8 @@ notify_callback(struct cmdq_item *item, void *data)
static void static void
notify_add(const char *name, struct cmd_find_state *fs, struct client *c, notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
struct session *s, struct window *w, struct window_pane *wp) struct session *s, struct window *w, struct window_pane *wp,
const char *pbname)
{ {
struct notify_entry *ne; struct notify_entry *ne;
struct cmdq_item *item; struct cmdq_item *item;
@@ -186,7 +193,8 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
ne->client = c; ne->client = c;
ne->session = s; ne->session = s;
ne->window = w; ne->window = w;
ne->pane = (wp != NULL ? wp->id : -1); ne->pane = (wp != NULL ? (int)wp->id : -1);
ne->pbname = (pbname != NULL ? xstrdup(pbname) : NULL);
ne->formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS); ne->formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
format_add(ne->formats, "hook", "%s", name); format_add(ne->formats, "hook", "%s", name);
@@ -232,7 +240,7 @@ notify_hook(struct cmdq_item *item, const char *name)
ne.client = cmdq_get_client(item); ne.client = cmdq_get_client(item);
ne.session = target->s; ne.session = target->s;
ne.window = target->w; ne.window = target->w;
ne.pane = (target->wp != NULL ? target->wp->id : -1); ne.pane = (target->wp != NULL ? (int)target->wp->id : -1);
ne.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS); ne.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
format_add(ne.formats, "hook", "%s", name); format_add(ne.formats, "hook", "%s", name);
@@ -248,7 +256,7 @@ notify_client(const char *name, struct client *c)
struct cmd_find_state fs; struct cmd_find_state fs;
cmd_find_from_client(&fs, c, 0); cmd_find_from_client(&fs, c, 0);
notify_add(name, &fs, c, NULL, NULL, NULL); notify_add(name, &fs, c, NULL, NULL, NULL, NULL);
} }
void void
@@ -260,7 +268,7 @@ notify_session(const char *name, struct session *s)
cmd_find_from_session(&fs, s, 0); cmd_find_from_session(&fs, s, 0);
else else
cmd_find_from_nothing(&fs, 0); cmd_find_from_nothing(&fs, 0);
notify_add(name, &fs, NULL, s, NULL, NULL); notify_add(name, &fs, NULL, s, NULL, NULL, NULL);
} }
void void
@@ -269,7 +277,7 @@ notify_winlink(const char *name, struct winlink *wl)
struct cmd_find_state fs; struct cmd_find_state fs;
cmd_find_from_winlink(&fs, wl, 0); cmd_find_from_winlink(&fs, wl, 0);
notify_add(name, &fs, NULL, wl->session, wl->window, NULL); notify_add(name, &fs, NULL, wl->session, wl->window, NULL, NULL);
} }
void void
@@ -278,7 +286,7 @@ notify_session_window(const char *name, struct session *s, struct window *w)
struct cmd_find_state fs; struct cmd_find_state fs;
cmd_find_from_session_window(&fs, s, w, 0); cmd_find_from_session_window(&fs, s, w, 0);
notify_add(name, &fs, NULL, s, w, NULL); notify_add(name, &fs, NULL, s, w, NULL, NULL);
} }
void void
@@ -287,7 +295,7 @@ notify_window(const char *name, struct window *w)
struct cmd_find_state fs; struct cmd_find_state fs;
cmd_find_from_window(&fs, w, 0); cmd_find_from_window(&fs, w, 0);
notify_add(name, &fs, NULL, NULL, w, NULL); notify_add(name, &fs, NULL, NULL, w, NULL, NULL);
} }
void void
@@ -296,5 +304,20 @@ notify_pane(const char *name, struct window_pane *wp)
struct cmd_find_state fs; struct cmd_find_state fs;
cmd_find_from_pane(&fs, wp, 0); cmd_find_from_pane(&fs, wp, 0);
notify_add(name, &fs, NULL, NULL, NULL, wp); notify_add(name, &fs, NULL, NULL, NULL, wp, NULL);
}
void
notify_paste_buffer(const char *pbname, int deleted)
{
struct cmd_find_state fs;
cmd_find_clear_state(&fs, 0);
if (deleted) {
notify_add("paste-buffer-deleted", &fs, NULL, NULL, NULL, NULL,
pbname);
} else {
notify_add("paste-buffer-changed", &fs, NULL, NULL, NULL, NULL,
pbname);
}
} }

View File

@@ -41,6 +41,9 @@ static const char *options_table_clock_mode_style_list[] = {
static const char *options_table_status_list[] = { static const char *options_table_status_list[] = {
"off", "on", "2", "3", "4", "5", NULL "off", "on", "2", "3", "4", "5", NULL
}; };
static const char *options_table_message_line_list[] = {
"0", "1", "2", "3", "4", NULL
};
static const char *options_table_status_keys_list[] = { static const char *options_table_status_keys_list[] = {
"emacs", "vi", NULL "emacs", "vi", NULL
}; };
@@ -81,12 +84,18 @@ static const char *options_table_window_size_list[] = {
static const char *options_table_remain_on_exit_list[] = { static const char *options_table_remain_on_exit_list[] = {
"off", "on", "failed", NULL "off", "on", "failed", NULL
}; };
static const char *options_table_destroy_unattached_list[] = {
"off", "on", "keep-last", "keep-group", NULL
};
static const char *options_table_detach_on_destroy_list[] = { static const char *options_table_detach_on_destroy_list[] = {
"off", "on", "no-detached", NULL "off", "on", "no-detached", "previous", "next", NULL
}; };
static const char *options_table_extended_keys_list[] = { static const char *options_table_extended_keys_list[] = {
"off", "on", "always", NULL "off", "on", "always", NULL
}; };
static const char *options_table_allow_passthrough_list[] = {
"off", "on", "all", NULL
};
/* Status line format. */ /* Status line format. */
#define OPTIONS_TABLE_STATUS_FORMAT1 \ #define OPTIONS_TABLE_STATUS_FORMAT1 \
@@ -320,6 +329,42 @@ const struct options_table_entry options_table[] = {
"Empty does not write a history file." "Empty does not write a history file."
}, },
{ .name = "menu-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.flags = OPTIONS_TABLE_IS_STYLE,
.default_str = "default",
.separator = ",",
.text = "Default style of menu."
},
{ .name = "menu-selected-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.flags = OPTIONS_TABLE_IS_STYLE,
.default_str = "bg=yellow,fg=black",
.separator = ",",
.text = "Default style of selected menu item."
},
{ .name = "menu-border-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "default",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Default style of menu borders."
},
{ .name = "menu-border-lines",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW,
.choices = options_table_popup_border_lines_list,
.default_num = BOX_LINES_SINGLE,
.text = "Type of characters used to draw menu border lines. Some of "
"these are only supported on terminals with UTF-8 support."
},
{ .name = "message-limit", { .name = "message-limit",
.type = OPTIONS_TABLE_NUMBER, .type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SERVER, .scope = OPTIONS_TABLE_SERVER,
@@ -362,7 +407,8 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SERVER, .scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY, .flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "xterm*:clipboard:ccolour:cstyle:focus:title," .default_str = "xterm*:clipboard:ccolour:cstyle:focus:title,"
"screen*:title", "screen*:title,"
"rxvt*:ignorefkeys",
.separator = ",", .separator = ",",
.text = "List of terminal features, used if they cannot be " .text = "List of terminal features, used if they cannot be "
"automatically detected." "automatically detected."
@@ -440,11 +486,12 @@ const struct options_table_entry options_table[] = {
}, },
{ .name = "destroy-unattached", { .name = "destroy-unattached",
.type = OPTIONS_TABLE_FLAG, .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.choices = options_table_destroy_unattached_list,
.default_num = 0, .default_num = 0,
.text = "Whether to destroy sessions when they have no attached " .text = "Whether to destroy sessions when they have no attached "
"clients." "clients, or keep the last session whether in the group."
}, },
{ .name = "detach-on-destroy", { .name = "detach-on-destroy",
@@ -523,7 +570,7 @@ const struct options_table_entry options_table[] = {
{ .name = "lock-command", { .name = "lock-command",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.default_str = "lock -np", .default_str = TMUX_LOCK_CMD,
.text = "Shell command to run to lock a client." .text = "Shell command to run to lock a client."
}, },
@@ -537,13 +584,21 @@ const struct options_table_entry options_table[] = {
"'mode-keys' is set to 'vi'." "'mode-keys' is set to 'vi'."
}, },
{ .name = "message-line",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_message_line_list,
.default_num = 0,
.text = "Position (line) of messages and the command prompt."
},
{ .name = "message-style", { .name = "message-style",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION, .scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=yellow,fg=black", .default_str = "bg=yellow,fg=black",
.flags = OPTIONS_TABLE_IS_STYLE, .flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",", .separator = ",",
.text = "Style of the command prompt." .text = "Style of messages and the command prompt."
}, },
{ .name = "mouse", { .name = "mouse",
@@ -802,11 +857,14 @@ const struct options_table_entry options_table[] = {
}, },
{ .name = "allow-passthrough", { .name = "allow-passthrough",
.type = OPTIONS_TABLE_FLAG, .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.choices = options_table_allow_passthrough_list,
.default_num = 0, .default_num = 0,
.text = "Whether applications are allowed to use the escape sequence " .text = "Whether applications are allowed to use the escape sequence "
"to bypass tmux." "to bypass tmux. Can be 'off' (disallowed), 'on' (allowed "
"if the pane is visible), or 'all' (allowed even if the pane "
"is invisible)."
}, },
{ .name = "allow-rename", { .name = "allow-rename",
@@ -916,8 +974,8 @@ const struct options_table_entry options_table[] = {
{ .name = "mode-style", { .name = "mode-style",
.type = OPTIONS_TABLE_STRING, .type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW, .scope = OPTIONS_TABLE_WINDOW,
.default_str = "bg=yellow,fg=black",
.flags = OPTIONS_TABLE_IS_STYLE, .flags = OPTIONS_TABLE_IS_STYLE,
.default_str = "bg=yellow,fg=black",
.separator = ",", .separator = ",",
.text = "Style of indicators and highlighting in modes." .text = "Style of indicators and highlighting in modes."
}, },

View File

@@ -1106,7 +1106,6 @@ options_push_changes(const char *name)
struct session *s; struct session *s;
struct window *w; struct window *w;
struct window_pane *wp; struct window_pane *wp;
int c;
log_debug("%s: %s", __func__, name); log_debug("%s: %s", __func__, name);
@@ -1119,18 +1118,12 @@ options_push_changes(const char *name)
} }
} }
if (strcmp(name, "cursor-colour") == 0) { if (strcmp(name, "cursor-colour") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes) { RB_FOREACH(wp, window_pane_tree, &all_window_panes)
c = options_get_number(wp->options, name); window_pane_default_cursor(wp);
wp->screen->default_ccolour = c;
}
} }
if (strcmp(name, "cursor-style") == 0) { if (strcmp(name, "cursor-style") == 0) {
RB_FOREACH(wp, window_pane_tree, &all_window_panes) { RB_FOREACH(wp, window_pane_tree, &all_window_panes)
wp->screen->default_mode = 0; window_pane_default_cursor(wp);
screen_set_cursor_style(options_get_number(wp->options,
name), &wp->screen->default_cstyle,
&wp->screen->default_mode);
}
} }
if (strcmp(name, "fill-character") == 0) { if (strcmp(name, "fill-character") == 0) {
RB_FOREACH(w, windows, &windows) RB_FOREACH(w, windows, &windows)

26
paste.c
View File

@@ -111,6 +111,12 @@ paste_walk(struct paste_buffer *pb)
return (RB_NEXT(paste_time_tree, &paste_by_time, pb)); return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
} }
int
paste_is_empty(void)
{
return RB_ROOT(&paste_by_time) == NULL;
}
/* Get the most recent automatic buffer. */ /* Get the most recent automatic buffer. */
struct paste_buffer * struct paste_buffer *
paste_get_top(const char **name) paste_get_top(const char **name)
@@ -118,6 +124,8 @@ paste_get_top(const char **name)
struct paste_buffer *pb; struct paste_buffer *pb;
pb = RB_MIN(paste_time_tree, &paste_by_time); pb = RB_MIN(paste_time_tree, &paste_by_time);
while (pb != NULL && !pb->automatic)
pb = RB_NEXT(paste_time_tree, &paste_by_time, pb);
if (pb == NULL) if (pb == NULL)
return (NULL); return (NULL);
if (name != NULL) if (name != NULL)
@@ -142,6 +150,8 @@ paste_get_name(const char *name)
void void
paste_free(struct paste_buffer *pb) paste_free(struct paste_buffer *pb)
{ {
notify_paste_buffer(pb->name, 1);
RB_REMOVE(paste_name_tree, &paste_by_name, pb); RB_REMOVE(paste_name_tree, &paste_by_name, pb);
RB_REMOVE(paste_time_tree, &paste_by_time, pb); RB_REMOVE(paste_time_tree, &paste_by_time, pb);
if (pb->automatic) if (pb->automatic)
@@ -198,6 +208,8 @@ paste_add(const char *prefix, char *data, size_t size)
pb->order = paste_next_order++; pb->order = paste_next_order++;
RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_name_tree, &paste_by_name, pb);
RB_INSERT(paste_time_tree, &paste_by_time, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb);
notify_paste_buffer(pb->name, 0);
} }
/* Rename a paste buffer. */ /* Rename a paste buffer. */
@@ -228,11 +240,8 @@ paste_rename(const char *oldname, const char *newname, char **cause)
} }
pb_new = paste_get_name(newname); pb_new = paste_get_name(newname);
if (pb_new != NULL) { if (pb_new != NULL)
if (cause != NULL) paste_free(pb_new);
xasprintf(cause, "buffer %s already exists", newname);
return (-1);
}
RB_REMOVE(paste_name_tree, &paste_by_name, pb); RB_REMOVE(paste_name_tree, &paste_by_name, pb);
@@ -245,6 +254,9 @@ paste_rename(const char *oldname, const char *newname, char **cause)
RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_name_tree, &paste_by_name, pb);
notify_paste_buffer(oldname, 1);
notify_paste_buffer(newname, 0);
return (0); return (0);
} }
@@ -293,6 +305,8 @@ paste_set(char *data, size_t size, const char *name, char **cause)
RB_INSERT(paste_name_tree, &paste_by_name, pb); RB_INSERT(paste_name_tree, &paste_by_name, pb);
RB_INSERT(paste_time_tree, &paste_by_time, pb); RB_INSERT(paste_time_tree, &paste_by_time, pb);
notify_paste_buffer(name, 0);
return (0); return (0);
} }
@@ -303,6 +317,8 @@ paste_replace(struct paste_buffer *pb, char *data, size_t size)
free(pb->data); free(pb->data);
pb->data = data; pb->data = data;
pb->size = size; pb->size = size;
notify_paste_buffer(pb->name, 0);
} }
/* Convert start of buffer into a nice string. */ /* Convert start of buffer into a nice string. */

View File

@@ -252,6 +252,7 @@ popup_draw_cb(struct client *c, void *data, struct screen_redraw_ctx *rctx)
tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &defaults, tty_draw_line(tty, &s, 0, i, pd->sx, px, py + i, &defaults,
palette); palette);
} }
screen_free(&s);
if (pd->md != NULL) { if (pd->md != NULL) {
c->overlay_check = NULL; c->overlay_check = NULL;
c->overlay_data = NULL; c->overlay_data = NULL;
@@ -573,8 +574,8 @@ menu:
x = m->x - (pd->menu->width + 4) / 2; x = m->x - (pd->menu->width + 4) / 2;
else else
x = 0; x = 0;
pd->md = menu_prepare(pd->menu, 0, NULL, x, m->y, c, NULL, pd->md = menu_prepare(pd->menu, 0, 0, NULL, x, m->y, c,
popup_menu_done, pd); BOX_LINES_DEFAULT, NULL, NULL, NULL, NULL, popup_menu_done, pd);
c->flags |= CLIENT_REDRAWOVERLAY; c->flags |= CLIENT_REDRAWOVERLAY;
out: out:
@@ -635,7 +636,7 @@ int
popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px, popup_display(int flags, enum box_lines lines, struct cmdq_item *item, u_int px,
u_int py, u_int sx, u_int sy, struct environ *env, const char *shellcmd, u_int py, u_int sx, u_int sy, struct environ *env, const char *shellcmd,
int argc, char **argv, const char *cwd, const char *title, struct client *c, int argc, char **argv, const char *cwd, const char *title, struct client *c,
struct session *s, const char* style, const char* border_style, struct session *s, const char *style, const char *border_style,
popup_close_cb cb, void *arg) popup_close_cb cb, void *arg)
{ {
struct popup_data *pd; struct popup_data *pd;
@@ -786,6 +787,8 @@ popup_editor(struct client *c, const char *buf, size_t len,
if (fd == -1) if (fd == -1)
return (-1); return (-1);
f = fdopen(fd, "w"); f = fdopen(fd, "w");
if (f == NULL)
return (-1);
if (fwrite(buf, len, 1, f) != 1) { if (fwrite(buf, len, 1, f) != 1) {
fclose(f); fclose(f);
return (-1); return (-1);

16
proc.c
View File

@@ -93,8 +93,9 @@ proc_event_cb(__unused int fd, short events, void *arg)
log_debug("peer %p message %d", peer, imsg.hdr.type); log_debug("peer %p message %d", peer, imsg.hdr.type);
if (peer_check_version(peer, &imsg) != 0) { if (peer_check_version(peer, &imsg) != 0) {
if (imsg.fd != -1) fd = imsg_get_fd(&imsg);
close(imsg.fd); if (fd != -1)
close(fd);
imsg_free(&imsg); imsg_free(&imsg);
break; break;
} }
@@ -193,18 +194,13 @@ proc_start(const char *name)
log_debug("%s started (%ld): version %s, socket %s, protocol %d", name, log_debug("%s started (%ld): version %s, socket %s, protocol %d", name,
(long)getpid(), getversion(), socket_path, PROTOCOL_VERSION); (long)getpid(), getversion(), socket_path, PROTOCOL_VERSION);
log_debug("on %s %s %s", u.sysname, u.release, u.version); log_debug("on %s %s %s", u.sysname, u.release, u.version);
log_debug("using libevent %s (%s)" log_debug("using libevent %s %s", event_get_version(), event_get_method());
#ifdef HAVE_UTF8PROC #ifdef HAVE_UTF8PROC
"; utf8proc %s" log_debug("using utf8proc %s", utf8proc_version());
#endif #endif
#ifdef NCURSES_VERSION #ifdef NCURSES_VERSION
"; ncurses " NCURSES_VERSION log_debug("using ncurses %s %06u", NCURSES_VERSION, NCURSES_VERSION_PATCH);
#endif #endif
, event_get_version(), event_get_method()
#ifdef HAVE_UTF8PROC
, utf8proc_version()
#endif
);
tp = xcalloc(1, sizeof *tp); tp = xcalloc(1, sizeof *tp);
tp->name = xstrdup(name); tp->name = xstrdup(name);

View File

@@ -0,0 +1,30 @@
#!/bin/sh
# capture-pane -e for OSC 8 hyperlink
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
TMP=$(mktemp)
TMP2=$(mktemp)
trap "rm -f $TMP $TMP2" 0 1 15
$TMUX kill-server 2>/dev/null
do_test() {
$TMUX -f/dev/null new -d "
printf '$1'
$TMUX capturep -peS0 -E1 >$TMP"
echo $2 > $TMP2
sleep 1
cmp $TMP $TMP2 || exit 1
return 0
}
do_test '\033]8;id=1;https://github.com\033\\test1\033]8;;\033\\\n' '\033]8;id=1;https://github.com\033\\test1\033]8;;\033\\\n' || exit 1
do_test '\033]8;;https://github.com/tmux/tmux\033\\test1\033]8;;\033\\\n' '\033]8;;https://github.com/tmux/tmux\033\\test1\033]8;;\033\\\n' || exit 1
$TMUX has 2>/dev/null && exit 1
exit 0

View File

@@ -22,8 +22,8 @@ $TMUX -f/dev/null new -d "
sleep 1 sleep 1
( (
printf '\033[1m\033[31m\033[42mabc\033[0m\033[31m\033[49mdef\033[39m\n' printf '\033[1m\033[31m\033[42mabc\033[0m\033[31m\033[49mdef\n'
printf '\033[100m bright bg \033[49m\n' printf '\033[39m\033[100m bright bg\n'
) | cmp - $TMP || exit 1 ) | cmp - $TMP || exit 1
$TMUX has 2>/dev/null && exit 1 $TMUX has 2>/dev/null && exit 1

View File

@@ -0,0 +1,24 @@
0
Λ̊1
🏻2
👍🏻3
👍🏻 👍🏻4
🤷5
♂️ 7
🤷8
🤷9
🤷10
🇪11
🇸🇪12
🇸🇪13

37
regress/combine-test.sh Normal file
View File

@@ -0,0 +1,37 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15
$TMUX -f/dev/null new -d "
printf '\e[H\e[J'
printf '\e[3;1H\316\233\e[3;1H\314\2120\n'
printf '\e[4;1H\316\233\e[4;2H\314\2121\n'
printf '\e[5;1H👍\e[5;1H🏻2\n'
printf '\e[6;1H👍\e[6;3H🏻3\n'
printf '\e[7;1H👍\e[7;10H👍\e[7;3H🏻\e[7;12H🏻4\n'
printf '\e[8;1H\360\237\244\267\342\200\215\342\231\202\357\270\2175\n'
printf '\e[9;1H\360\237\244\267\e[9;1H\342\200\215\342\231\202\357\270\2176\n'
printf '\e[9;1H\360\237\244\267\e[9;1H\342\200\215\342\231\202\357\270\2177\n'
printf '\e[10;1H\360\237\244\267\e[10;3H\342\200\215\342\231\202\357\270\2178\n'
printf '\e[11;1H\360\237\244\267\e[11;3H\342\200\215\e[11;3H\342\231\202\357\270\2179\n'
printf '\e[12;1H\360\237\244\267\e[12;3H\342\200\215\342\231\202\357\270\21710\n'
printf '\e[13;1H\360\237\207\25211\n'
printf '\e[14;1H\360\237\207\270\360\237\207\25212\n'
printf '\e[15;1H\360\237\207\270 \010\010\360\237\207\25213\n'
$TMUX capturep -pe >>$TMP"
sleep 1
cmp $TMP combine-test.result || exit 1
$TMUX has 2>/dev/null && exit 1
exit 0

View File

@@ -12,7 +12,7 @@ set-window-option -g pane-base-index 1
unbind ^B unbind ^B
bind ^B select-pane -t :.+ bind ^B select-pane -t :.+
# Reload config wtih a key # Reload config with a key
bind-key r source-file ~/.tmux.conf \; display "Config reloaded!" bind-key r source-file ~/.tmux.conf \; display "Config reloaded!"
# Mouse works as expected # Mouse works as expected

View File

@@ -24,7 +24,7 @@ set-option -g default-terminal 'screen-256color'
# allow Vim to receive focus events from terminal window # allow Vim to receive focus events from terminal window
set-option -g focus-events on set-option -g focus-events on
# allow Vim to recieve modifier keys: Shift, Control, Alt # allow Vim to receive modifier keys: Shift, Control, Alt
set-window-option -g xterm-keys on set-window-option -g xterm-keys on
# prevent tmux from catching modifier keys meant for Vim # prevent tmux from catching modifier keys meant for Vim

View File

@@ -552,7 +552,7 @@ setw -g status-keys emacs
# Changelog: https://github.com/tmux/tmux/blob/master/CHANGES # Changelog: https://github.com/tmux/tmux/blob/master/CHANGES
# style colors: default, black, red, green, yellow, blue, magenta, cyan, white, # style colors: default, black, red, green, yellow, blue, magenta, cyan, white,
# colour0-colour255, hexdecimal RGB string '#ffffff' # colour0-colour255, hexadecimal RGB string '#ffffff'
# Use $SCRIPTS/bash/256-colors.sh to figure out the color number you want # Use $SCRIPTS/bash/256-colors.sh to figure out the color number you want
# style attributes: none, bold/bright, dim, underscore, blink, reverse, hidden, # style attributes: none, bold/bright, dim, underscore, blink, reverse, hidden,
# or italics # or italics

View File

@@ -1,6 +1,6 @@
# none of these attempts worked, to bind keys, except sometimes during the sesssion. Oh well. # none of these attempts worked, to bind keys, except sometimes during the session. Oh well.
# I thought maybe that was because F1 is handled differently in a console than in X, but # I thought maybe that was because F1 is handled differently in a console than in X, but
# even just C-1 didnt work. Using just "a" or "x" as the key did, but not yet sure why not "C-". # even just C-1 didn't work. Using just "a" or "x" as the key did, but not yet sure why not "C-".
#bind-key -T root C-1 attach-session -t$0 #bind-key -T root C-1 attach-session -t$0
#But this one works now, only picks the wrong one? Mbe need2understand what "$1" or $0 mean, better, #But this one works now, only picks the wrong one? Mbe need2understand what "$1" or $0 mean, better,
#but with the stub maybe this doesn't matter: #but with the stub maybe this doesn't matter:
@@ -47,7 +47,7 @@ select-window -t :=3
#$3 for email (mutt) #$3 for email (mutt)
new-session sula new-session sula
new-window sula ; send-keys mutt Enter new-window sula ; send-keys mutt Enter
#nah, probly betr not?: #nah, probably better not?:
#send-keys -l z #send-keys -l z
#send-keys -l "thepassifdecide" #send-keys -l "thepassifdecide"
#send-keys Enter #send-keys Enter

View File

@@ -63,7 +63,7 @@ bind N command-prompt -p hosts: 'run-shell -b "bash -c \"~/lbin/nw %% >/dev/null
#05:59 < Celti> annihilannic: I believe the #{pane_in_mode} format does what you want #05:59 < Celti> annihilannic: I believe the #{pane_in_mode} format does what you want
#05:59 < Celti> put it in your statusline #05:59 < Celti> put it in your statusline
#05:59 < Celti> annihilannic: No, my mistake, I should have read farther down, you want #{pane_synchronized} #05:59 < Celti> annihilannic: No, my mistake, I should have read farther down, you want #{pane_synchronized}
# only works in tmux 2.0?, higher than 1.6.3 anyawy # only works in tmux 2.0?, higher than 1.6.3 anyway
set-option -g window-status-format ' #I:#W#F#{?pane_synchronized,S,}' set-option -g window-status-format ' #I:#W#F#{?pane_synchronized,S,}'
#set-option -g window-status-current-format ' #I:#W#{?pane_synchronized,[sync],}#F' #set-option -g window-status-current-format ' #I:#W#{?pane_synchronized,[sync],}#F'
# to highlight in red when sync is on... not sure why I did this with set-window-option instead of set-option, perhaps # to highlight in red when sync is on... not sure why I did this with set-window-option instead of set-option, perhaps

View File

@@ -11,16 +11,13 @@ TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null $TMUX kill-server 2>/dev/null
TMP=$(mktemp) TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15 OUT=$(mktemp)
trap "rm -f $TMP $OUT" 0 1 15
cat <<EOF >$TMP cat <<EOF >$TMP
if 'true' 'wibble wobble' if 'true' 'wibble wobble'
EOF EOF
$TMUX -f$TMP new -d || exit 1 $TMUX -f$TMP -C new <<EOF >$OUT
sleep 1 EOF
E=$($TMUX display -p '#{pane_in_mode}') grep -q "^%config-error $TMP:1: $TMP:1: unknown command: wibble$" $OUT
$TMUX kill-server 2>/dev/null
[ "$E" = "1" ] || exit 1
exit 0

299
regress/input-keys.sh Normal file
View File

@@ -0,0 +1,299 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
$TMUX -f/dev/null new -x20 -y2 -d || exit 1
sleep 0.1
exit_status=0
assert_key () {
key=$1
expected_code=$2
$TMUX new-window -- sh -c 'stty raw -echo && cat -tv'
$TMUX send-keys "$key" $
actual_code=$($TMUX capturep -p | head -1 | sed -e 's/\$$//')
$TMUX kill-window
if [ "$actual_code" = "$expected_code" ]; then
if [ -n "$VERBOSE" ]; then
echo "[PASS] $key -> $actual_code"
fi
else
echo "[FAIL] $key -> $expected_code (Got: $actual_code)"
exit_status=1
fi
shift
shift
if [ "$1" = "--" ]; then
shift
assert_key "$@"
fi
}
assert_key 'C-Space' '^@'
assert_key 'C-a' '^A' -- 'M-C-a' '^[^A'
assert_key 'C-b' '^B' -- 'M-C-b' '^[^B'
assert_key 'C-c' '^C' -- 'M-C-c' '^[^C'
assert_key 'C-d' '^D' -- 'M-C-d' '^[^D'
assert_key 'C-e' '^E' -- 'M-C-e' '^[^E'
assert_key 'C-f' '^F' -- 'M-C-f' '^[^F'
assert_key 'C-g' '^G' -- 'M-C-g' '^[^G'
assert_key 'C-h' '^H' -- 'M-C-h' '^[^H'
assert_key 'C-i' '^I' -- 'M-C-i' '^[^I'
assert_key 'C-j' '' -- 'M-C-j' '^[' # NL
assert_key 'C-k' '^K' -- 'M-C-k' '^[^K'
assert_key 'C-l' '^L' -- 'M-C-l' '^[^L'
assert_key 'C-m' '^M' -- 'M-C-m' '^[^M'
assert_key 'C-n' '^N' -- 'M-C-n' '^[^N'
assert_key 'C-o' '^O' -- 'M-C-o' '^[^O'
assert_key 'C-p' '^P' -- 'M-C-p' '^[^P'
assert_key 'C-q' '^Q' -- 'M-C-q' '^[^Q'
assert_key 'C-r' '^R' -- 'M-C-r' '^[^R'
assert_key 'C-s' '^S' -- 'M-C-s' '^[^S'
assert_key 'C-t' '^T' -- 'M-C-t' '^[^T'
assert_key 'C-u' '^U' -- 'M-C-u' '^[^U'
assert_key 'C-v' '^V' -- 'M-C-v' '^[^V'
assert_key 'C-w' '^W' -- 'M-C-w' '^[^W'
assert_key 'C-x' '^X' -- 'M-C-x' '^[^X'
assert_key 'C-y' '^Y' -- 'M-C-y' '^[^Y'
assert_key 'C-z' '^Z' -- 'M-C-z' '^[^Z'
assert_key 'Escape' '^[' -- 'M-Escape' '^[^['
assert_key "C-\\" "^\\" -- "M-C-\\" "^[^\\"
assert_key 'C-]' '^]' -- 'M-C-]' '^[^]'
assert_key 'C-^' '^^' -- 'M-C-^' '^[^^'
assert_key 'C-_' '^_' -- 'M-C-_' '^[^_'
assert_key 'Space' ' ' -- 'M-Space' '^[ '
assert_key '!' '!' -- 'M-!' '^[!'
assert_key '"' '"' -- 'M-"' '^["'
assert_key '#' '#' -- 'M-#' '^[#'
assert_key '$' '$' -- 'M-$' '^[$'
assert_key '%' '%' -- 'M-%' '^[%'
assert_key '&' '&' -- 'M-&' '^[&'
assert_key "'" "'" -- "M-'" "^['"
assert_key '(' '(' -- 'M-(' '^[('
assert_key ')' ')' -- 'M-)' '^[)'
assert_key '*' '*' -- 'M-*' '^[*'
assert_key '+' '+' -- 'M-+' '^[+'
assert_key ',' ',' -- 'M-,' '^[,'
assert_key '-' '-' -- 'M--' '^[-'
assert_key '.' '.' -- 'M-.' '^[.'
assert_key '/' '/' -- 'M-/' '^[/'
assert_key '0' '0' -- 'M-0' '^[0'
assert_key '1' '1' -- 'M-1' '^[1'
assert_key '2' '2' -- 'M-2' '^[2'
assert_key '3' '3' -- 'M-3' '^[3'
assert_key '4' '4' -- 'M-4' '^[4'
assert_key '5' '5' -- 'M-5' '^[5'
assert_key '6' '6' -- 'M-6' '^[6'
assert_key '7' '7' -- 'M-7' '^[7'
assert_key '8' '8' -- 'M-8' '^[8'
assert_key '9' '9' -- 'M-9' '^[9'
assert_key ':' ':' -- 'M-:' '^[:'
assert_key '\;' ';' -- 'M-\;' '^[;'
assert_key '<' '<' -- 'M-<' '^[<'
assert_key '=' '=' -- 'M-=' '^[='
assert_key '>' '>' -- 'M->' '^[>'
assert_key '?' '?' -- 'M-?' '^[?'
assert_key '@' '@' -- 'M-@' '^[@'
assert_key 'A' 'A' -- 'M-A' '^[A'
assert_key 'B' 'B' -- 'M-B' '^[B'
assert_key 'C' 'C' -- 'M-C' '^[C'
assert_key 'D' 'D' -- 'M-D' '^[D'
assert_key 'E' 'E' -- 'M-E' '^[E'
assert_key 'F' 'F' -- 'M-F' '^[F'
assert_key 'G' 'G' -- 'M-G' '^[G'
assert_key 'H' 'H' -- 'M-H' '^[H'
assert_key 'I' 'I' -- 'M-I' '^[I'
assert_key 'J' 'J' -- 'M-J' '^[J'
assert_key 'K' 'K' -- 'M-K' '^[K'
assert_key 'L' 'L' -- 'M-L' '^[L'
assert_key 'M' 'M' -- 'M-M' '^[M'
assert_key 'N' 'N' -- 'M-N' '^[N'
assert_key 'O' 'O' -- 'M-O' '^[O'
assert_key 'P' 'P' -- 'M-P' '^[P'
assert_key 'Q' 'Q' -- 'M-Q' '^[Q'
assert_key 'R' 'R' -- 'M-R' '^[R'
assert_key 'S' 'S' -- 'M-S' '^[S'
assert_key 'T' 'T' -- 'M-T' '^[T'
assert_key 'U' 'U' -- 'M-U' '^[U'
assert_key 'V' 'V' -- 'M-V' '^[V'
assert_key 'W' 'W' -- 'M-W' '^[W'
assert_key 'X' 'X' -- 'M-X' '^[X'
assert_key 'Y' 'Y' -- 'M-Y' '^[Y'
assert_key 'Z' 'Z' -- 'M-Z' '^[Z'
assert_key '[' '[' -- 'M-[' '^[['
assert_key "\\" "\\" -- "M-\\" "^[\\"
assert_key ']' ']' -- 'M-]' '^[]'
assert_key '^' '^' -- 'M-^' '^[^'
assert_key '_' '_' -- 'M-_' '^[_'
assert_key '`' '`' -- 'M-`' '^[`'
assert_key 'a' 'a' -- 'M-a' '^[a'
assert_key 'b' 'b' -- 'M-b' '^[b'
assert_key 'c' 'c' -- 'M-c' '^[c'
assert_key 'd' 'd' -- 'M-d' '^[d'
assert_key 'e' 'e' -- 'M-e' '^[e'
assert_key 'f' 'f' -- 'M-f' '^[f'
assert_key 'g' 'g' -- 'M-g' '^[g'
assert_key 'h' 'h' -- 'M-h' '^[h'
assert_key 'i' 'i' -- 'M-i' '^[i'
assert_key 'j' 'j' -- 'M-j' '^[j'
assert_key 'k' 'k' -- 'M-k' '^[k'
assert_key 'l' 'l' -- 'M-l' '^[l'
assert_key 'm' 'm' -- 'M-m' '^[m'
assert_key 'n' 'n' -- 'M-n' '^[n'
assert_key 'o' 'o' -- 'M-o' '^[o'
assert_key 'p' 'p' -- 'M-p' '^[p'
assert_key 'q' 'q' -- 'M-q' '^[q'
assert_key 'r' 'r' -- 'M-r' '^[r'
assert_key 's' 's' -- 'M-s' '^[s'
assert_key 't' 't' -- 'M-t' '^[t'
assert_key 'u' 'u' -- 'M-u' '^[u'
assert_key 'v' 'v' -- 'M-v' '^[v'
assert_key 'w' 'w' -- 'M-w' '^[w'
assert_key 'x' 'x' -- 'M-x' '^[x'
assert_key 'y' 'y' -- 'M-y' '^[y'
assert_key 'z' 'z' -- 'M-z' '^[z'
assert_key '{' '{' -- 'M-{' '^[{'
assert_key '|' '|' -- 'M-|' '^[|'
assert_key '}' '}' -- 'M-}' '^[}'
assert_key '~' '~' -- 'M-~' '^[~'
assert_key 'Tab' '^I' -- 'M-Tab' '^[^I'
assert_key 'BSpace' '^?' -- 'M-BSpace' '^[^?'
## These cannot be sent, is that intentional?
## assert_key 'PasteStart' "^[[200~"
## assert_key 'PasteEnd' "^[[201~"
assert_key 'F1' "^[OP"
assert_key 'F2' "^[OQ"
assert_key 'F3' "^[OR"
assert_key 'F4' "^[OS"
assert_key 'F5' "^[[15~"
assert_key 'F6' "^[[17~"
assert_key 'F8' "^[[19~"
assert_key 'F9' "^[[20~"
assert_key 'F10' "^[[21~"
assert_key 'F11' "^[[23~"
assert_key 'F12' "^[[24~"
assert_key 'IC' '^[[2~'
assert_key 'Insert' '^[[2~'
assert_key 'DC' '^[[3~'
assert_key 'Delete' '^[[3~'
## Why do these differ from tty-keys?
assert_key 'Home' '^[[1~'
assert_key 'End' '^[[4~'
assert_key 'NPage' '^[[6~'
assert_key 'PageDown' '^[[6~'
assert_key 'PgDn' '^[[6~'
assert_key 'PPage' '^[[5~'
assert_key 'PageUp' '^[[5~'
assert_key 'PgUp' '^[[5~'
assert_key 'BTab' '^[[Z'
assert_key 'C-S-Tab' '^[[Z'
assert_key 'Up' '^[[A'
assert_key 'Down' '^[[B'
assert_key 'Right' '^[[C'
assert_key 'Left' '^[[D'
# assert_key 'KPEnter'
assert_key 'KP*' '*' -- 'M-KP*' '^[*'
assert_key 'KP+' '+' -- 'M-KP+' '^[+'
assert_key 'KP-' '-' -- 'M-KP-' '^[-'
assert_key 'KP.' '.' -- 'M-KP.' '^[.'
assert_key 'KP/' '/' -- 'M-KP/' '^[/'
assert_key 'KP0' '0' -- 'M-KP0' '^[0'
assert_key 'KP1' '1' -- 'M-KP1' '^[1'
assert_key 'KP2' '2' -- 'M-KP2' '^[2'
assert_key 'KP3' '3' -- 'M-KP3' '^[3'
assert_key 'KP4' '4' -- 'M-KP4' '^[4'
assert_key 'KP5' '5' -- 'M-KP5' '^[5'
assert_key 'KP6' '6' -- 'M-KP6' '^[6'
assert_key 'KP7' '7' -- 'M-KP7' '^[7'
assert_key 'KP8' '8' -- 'M-KP8' '^[8'
assert_key 'KP9' '9' -- 'M-KP9' '^[9'
# Extended keys
$TMUX set -g extended-keys always
assert_extended_key () {
extended_key=$1
expected_code_pattern=$2
expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;2/')
assert_key "S-$extended_key" "$expected_code"
expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;3/')
assert_key "M-$extended_key" "$expected_code"
expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;4/')
assert_key "S-M-$extended_key" "$expected_code"
expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;5/')
assert_key "C-$extended_key" "$expected_code"
expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;6/')
assert_key "S-C-$extended_key" "$expected_code"
expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;7/')
assert_key "C-M-$extended_key" "$expected_code"
expected_code=$(printf '%s' "$expected_code_pattern" | sed -e 's/;_/;8/')
assert_key "S-C-M-$extended_key" "$expected_code"
}
## Many of these pass without extended keys enabled -- are they extended keys?
assert_extended_key 'F1' '^[[1;_P'
assert_extended_key 'F2' "^[[1;_Q"
assert_extended_key 'F3' "^[[1;_R"
assert_extended_key 'F4' "^[[1;_S"
assert_extended_key 'F5' "^[[15;_~"
assert_extended_key 'F6' "^[[17;_~"
assert_extended_key 'F8' "^[[19;_~"
assert_extended_key 'F9' "^[[20;_~"
assert_extended_key 'F10' "^[[21;_~"
assert_extended_key 'F11' "^[[23;_~"
assert_extended_key 'F12' "^[[24;_~"
assert_extended_key 'Up' '^[[1;_A'
assert_extended_key 'Down' '^[[1;_B'
assert_extended_key 'Right' '^[[1;_C'
assert_extended_key 'Left' '^[[1;_D'
assert_extended_key 'Home' '^[[1;_H'
assert_extended_key 'End' '^[[1;_F'
assert_extended_key 'PPage' '^[[5;_~'
assert_extended_key 'PageUp' '^[[5;_~'
assert_extended_key 'PgUp' '^[[5;_~'
assert_extended_key 'NPage' '^[[6;_~'
assert_extended_key 'PageDown' '^[[6;_~'
assert_extended_key 'PgDn' '^[[6;_~'
assert_extended_key 'IC' '^[[2;_~'
assert_extended_key 'Insert' '^[[2;_~'
assert_extended_key 'DC' '^[[3;_~'
assert_extended_key 'Delete' '^[[3;_~'
assert_key 'C-Tab' "^[[9;5u"
assert_key 'C-S-Tab' "^[[1;5Z"
$TMUX kill-server 2>/dev/null
exit $exit_status

View File

@@ -8,13 +8,14 @@ TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) [ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest" TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null $TMUX kill-server 2>/dev/null
sleep 1
$TMUX -f/dev/null new -d 'sleep 1000' || exit 1 $TMUX -f/dev/null new -d 'sleep 1000' || exit 1
P=$($TMUX display -pt0:0.0 '#{pane_pid}') P=$($TMUX display -pt0:0.0 '#{pane_pid}')
$TMUX -f/dev/null new -d || exit 1 $TMUX -f/dev/null new -d || exit 1
sleep 1 sleep 1
$TMUX kill-session -t0: $TMUX kill-session -t0:
sleep 1 sleep 3
kill -0 $P 2>/dev/null && exit 1 kill -0 $P 2>/dev/null && exit 1
$TMUX kill-server 2>/dev/null $TMUX kill-server 2>/dev/null

361
regress/tty-keys.sh Normal file
View File

@@ -0,0 +1,361 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
TMUX2="$TEST_TMUX -Ltest2"
$TMUX2 kill-server 2>/dev/null
TMP=$(mktemp)
trap "rm -f $TMP" 0 1 15
$TMUX2 -f/dev/null new -d || exit 1
$TMUX -f/dev/null new -d "$TMUX2 attach" || exit 1
sleep 0.1
exit_status=0
format_string () {
case $1 in
*\')
printf '"%%%%"'
;;
*)
printf "'%%%%'"
;;
esac
}
assert_key () {
keys=$1
expected_name=$2
format_string=$(format_string "$expected_name")
$TMUX2 command-prompt -k 'display-message -pl '"$format_string" > "$TMP" &
sleep 0.05
$TMUX send-keys $keys
wait
keys=$(printf '%s' "$keys" | sed -e 's/Escape/\\\\033/g' | tr -d '[:space:]')
actual_name=$(tr -d '[:space:]' < "$TMP")
if [ "$actual_name" = "$expected_name" ]; then
if [ -n "$VERBOSE" ]; then
echo "[PASS] $keys -> $actual_name"
fi
else
echo "[FAIL] $keys -> $expected_name (Got: '$actual_name')"
exit_status=1
fi
if [ "$3" = "--" ]; then
shift; shift; shift
assert_key "$@"
fi
}
assert_key 0x00 'C-Space' # -- 'Escape 0x00' 'M-C-Space'
assert_key 0x01 'C-a' -- 'Escape 0x01' 'M-C-a'
assert_key 0x02 'C-b' -- 'Escape 0x02' 'M-C-b'
assert_key 0x03 'C-c' -- 'Escape 0x03' 'M-C-c'
assert_key 0x04 'C-d' -- 'Escape 0x04' 'M-C-d'
assert_key 0x05 'C-e' -- 'Escape 0x05' 'M-C-e'
assert_key 0x06 'C-f' -- 'Escape 0x06' 'M-C-f'
assert_key 0x07 'C-g' -- 'Escape 0x07' 'M-C-g'
assert_key 0x08 'C-h' -- 'Escape 0x08' 'M-C-h'
assert_key 0x09 'Tab' -- 'Escape 0x09' 'M-Tab'
assert_key 0x0A 'C-j' -- 'Escape 0x0A' 'M-C-j'
assert_key 0x0B 'C-k' -- 'Escape 0x0B' 'M-C-k'
assert_key 0x0C 'C-l' -- 'Escape 0x0C' 'M-C-l'
assert_key 0x0D 'Enter' -- 'Escape 0x0D' 'M-Enter'
assert_key 0x0E 'C-n' -- 'Escape 0x0E' 'M-C-n'
assert_key 0x0F 'C-o' -- 'Escape 0x0F' 'M-C-o'
assert_key 0x10 'C-p' -- 'Escape 0x10' 'M-C-p'
assert_key 0x11 'C-q' -- 'Escape 0x11' 'M-C-q'
assert_key 0x12 'C-r' -- 'Escape 0x12' 'M-C-r'
assert_key 0x13 'C-s' -- 'Escape 0x13' 'M-C-s'
assert_key 0x14 'C-t' -- 'Escape 0x14' 'M-C-t'
assert_key 0x15 'C-u' -- 'Escape 0x15' 'M-C-u'
assert_key 0x16 'C-v' -- 'Escape 0x16' 'M-C-v'
assert_key 0x17 'C-w' -- 'Escape 0x17' 'M-C-w'
assert_key 0x18 'C-x' -- 'Escape 0x18' 'M-C-x'
assert_key 0x19 'C-y' -- 'Escape 0x19' 'M-C-y'
assert_key 0x1A 'C-z' -- 'Escape 0x1A' 'M-C-z'
assert_key 0x1B 'Escape' -- 'Escape 0x1B' 'M-Escape'
assert_key 0x1C "C-\\" -- 'Escape 0x1C' "M-C-\\"
assert_key 0x1D 'C-]' -- 'Escape 0x1D' 'M-C-]'
assert_key 0x1E 'C-^' -- 'Escape 0x1E' 'M-C-^'
assert_key 0x1F 'C-_' -- 'Escape 0x1F' 'M-C-_'
assert_key 0x20 'Space' -- 'Escape 0x20' 'M-Space'
assert_key 0x21 '!' -- 'Escape 0x21' 'M-!'
assert_key 0x22 '"' -- 'Escape 0x22' 'M-"'
assert_key 0x23 '#' -- 'Escape 0x23'= 'M-#'
assert_key 0x24 '$' -- 'Escape 0x24'= 'M-$'
assert_key 0x25 '%' -- 'Escape 0x25'= 'M-%'
assert_key 0x26 '&' -- 'Escape 0x26'= 'M-&'
assert_key 0x27 "'" -- 'Escape 0x27' "M-'"
assert_key 0x28 '(' -- 'Escape 0x28' 'M-('
assert_key 0x29 ')' -- 'Escape 0x29' 'M-)'
assert_key 0x2A '*' -- 'Escape 0x2A' 'M-*'
assert_key 0x2B '+' -- 'Escape 0x2B' 'M-+'
assert_key 0x2C ',' -- 'Escape 0x2C' 'M-,'
assert_key 0x2D '-' -- 'Escape 0x2D' 'M--'
assert_key 0x2E '.' -- 'Escape 0x2E' 'M-.'
assert_key 0x2F '/' -- 'Escape 0x2F' 'M-/'
assert_key 0x30 '0' -- 'Escape 0x30' 'M-0'
assert_key 0x31 '1' -- 'Escape 0x31' 'M-1'
assert_key 0x32 '2' -- 'Escape 0x32' 'M-2'
assert_key 0x33 '3' -- 'Escape 0x33' 'M-3'
assert_key 0x34 '4' -- 'Escape 0x34' 'M-4'
assert_key 0x35 '5' -- 'Escape 0x35' 'M-5'
assert_key 0x36 '6' -- 'Escape 0x36' 'M-6'
assert_key 0x37 '7' -- 'Escape 0x37' 'M-7'
assert_key 0x38 '8' -- 'Escape 0x38' 'M-8'
assert_key 0x39 '9' -- 'Escape 0x39' 'M-9'
assert_key 0x3A ':' -- 'Escape 0x3A' 'M-:'
assert_key 0x3B ';' -- 'Escape 0x3B' 'M-;'
assert_key 0x3C '<' -- 'Escape 0x3C' 'M-<'
assert_key 0x3D '=' -- 'Escape 0x3D' 'M-='
assert_key 0x3E '>' -- 'Escape 0x3E' 'M->'
assert_key 0x3F '?' -- 'Escape 0x3F' 'M-?'
assert_key 0x40 '@' -- 'Escape 0x40' 'M-@'
assert_key 0x41 'A' -- 'Escape 0x41' 'M-A'
assert_key 0x42 'B' -- 'Escape 0x42' 'M-B'
assert_key 0x43 'C' -- 'Escape 0x43' 'M-C'
assert_key 0x44 'D' -- 'Escape 0x44' 'M-D'
assert_key 0x45 'E' -- 'Escape 0x45' 'M-E'
assert_key 0x46 'F' -- 'Escape 0x46' 'M-F'
assert_key 0x47 'G' -- 'Escape 0x47' 'M-G'
assert_key 0x48 'H' -- 'Escape 0x48' 'M-H'
assert_key 0x49 'I' -- 'Escape 0x49' 'M-I'
assert_key 0x4A 'J' -- 'Escape 0x4A' 'M-J'
assert_key 0x4B 'K' -- 'Escape 0x4B' 'M-K'
assert_key 0x4C 'L' -- 'Escape 0x4C' 'M-L'
assert_key 0x4D 'M' -- 'Escape 0x4D' 'M-M'
assert_key 0x4E 'N' -- 'Escape 0x4E' 'M-N'
assert_key 0x4F 'O' -- 'Escape 0x4F' 'M-O'
assert_key 0x50 'P' -- 'Escape 0x50' 'M-P'
assert_key 0x51 'Q' -- 'Escape 0x51' 'M-Q'
assert_key 0x52 'R' -- 'Escape 0x52' 'M-R'
assert_key 0x53 'S' -- 'Escape 0x53' 'M-S'
assert_key 0x54 'T' -- 'Escape 0x54' 'M-T'
assert_key 0x55 'U' -- 'Escape 0x55' 'M-U'
assert_key 0x56 'V' -- 'Escape 0x56' 'M-V'
assert_key 0x57 'W' -- 'Escape 0x57' 'M-W'
assert_key 0x58 'X' -- 'Escape 0x58' 'M-X'
assert_key 0x59 'Y' -- 'Escape 0x59' 'M-Y'
assert_key 0x5A 'Z' -- 'Escape 0x5A' 'M-Z'
assert_key 0x5B '[' -- 'Escape 0x5B' 'M-['
assert_key 0x5C "\\" -- 'Escape 0x5C' "M-\\"
assert_key 0x5D ']' -- 'Escape 0x5D' 'M-]'
assert_key 0x5E '^' -- 'Escape 0x5E' 'M-^'
assert_key 0x5F '_' -- 'Escape 0x5F' 'M-_'
assert_key 0x60 '`' -- 'Escape 0x60' 'M-`'
assert_key 0x61 'a' -- 'Escape 0x61' 'M-a'
assert_key 0x62 'b' -- 'Escape 0x62' 'M-b'
assert_key 0x63 'c' -- 'Escape 0x63' 'M-c'
assert_key 0x64 'd' -- 'Escape 0x64' 'M-d'
assert_key 0x65 'e' -- 'Escape 0x65' 'M-e'
assert_key 0x66 'f' -- 'Escape 0x66' 'M-f'
assert_key 0x67 'g' -- 'Escape 0x67' 'M-g'
assert_key 0x68 'h' -- 'Escape 0x68' 'M-h'
assert_key 0x69 'i' -- 'Escape 0x69' 'M-i'
assert_key 0x6A 'j' -- 'Escape 0x6A' 'M-j'
assert_key 0x6B 'k' -- 'Escape 0x6B' 'M-k'
assert_key 0x6C 'l' -- 'Escape 0x6C' 'M-l'
assert_key 0x6D 'm' -- 'Escape 0x6D' 'M-m'
assert_key 0x6E 'n' -- 'Escape 0x6E' 'M-n'
assert_key 0x6F 'o' -- 'Escape 0x6F' 'M-o'
assert_key 0x70 'p' -- 'Escape 0x70' 'M-p'
assert_key 0x71 'q' -- 'Escape 0x71' 'M-q'
assert_key 0x72 'r' -- 'Escape 0x72' 'M-r'
assert_key 0x73 's' -- 'Escape 0x73' 'M-s'
assert_key 0x74 't' -- 'Escape 0x74' 'M-t'
assert_key 0x75 'u' -- 'Escape 0x75' 'M-u'
assert_key 0x76 'v' -- 'Escape 0x76' 'M-v'
assert_key 0x77 'w' -- 'Escape 0x77' 'M-w'
assert_key 0x78 'x' -- 'Escape 0x78' 'M-x'
assert_key 0x79 'y' -- 'Escape 0x79' 'M-y'
assert_key 0x7A 'z' -- 'Escape 0x7A' 'M-z'
assert_key 0x7B '{' -- 'Escape 0x7B' 'M-{'
assert_key 0x7C '|' -- 'Escape 0x7C' 'M-|'
assert_key 0x7D '}' -- 'Escape 0x7D' 'M-}'
assert_key 0x7E '~' -- 'Escape 0x7E' 'M-~'
assert_key 0x7F 'BSpace' -- 'Escape 0x7F' 'M-BSpace'
# Numeric keypad
assert_key 'Escape OM' 'KPEnter' -- 'Escape Escape OM' 'M-KPEnter'
assert_key 'Escape Oj' 'KP*' -- 'Escape Escape Oj' 'M-KP*'
assert_key 'Escape Ok' 'KP+' -- 'Escape Escape Ok' 'M-KP+'
assert_key 'Escape Om' 'KP-' -- 'Escape Escape Om' 'M-KP-'
assert_key 'Escape On' 'KP.' -- 'Escape Escape On' 'M-KP.'
assert_key 'Escape Oo' 'KP/' -- 'Escape Escape Oo' 'M-KP/'
assert_key 'Escape Op' 'KP0' -- 'Escape Escape Op' 'M-KP0'
assert_key 'Escape Oq' 'KP1' -- 'Escape Escape Oq' 'M-KP1'
assert_key 'Escape Or' 'KP2' -- 'Escape Escape Or' 'M-KP2'
assert_key 'Escape Os' 'KP3' -- 'Escape Escape Os' 'M-KP3'
assert_key 'Escape Ot' 'KP4' -- 'Escape Escape Ot' 'M-KP4'
assert_key 'Escape Ou' 'KP5' -- 'Escape Escape Ou' 'M-KP5'
assert_key 'Escape Ov' 'KP6' -- 'Escape Escape Ov' 'M-KP6'
assert_key 'Escape Ow' 'KP7' -- 'Escape Escape Ow' 'M-KP7'
assert_key 'Escape Ox' 'KP8' -- 'Escape Escape Ox' 'M-KP8'
assert_key 'Escape Oy' 'KP9' -- 'Escape Escape Oy' 'M-KP9'
# Arrow keys
assert_key 'Escape OA' 'Up' -- 'Escape Escape OA' 'M-Up'
assert_key 'Escape OB' 'Down' -- 'Escape Escape OB' 'M-Down'
assert_key 'Escape OC' 'Right' -- 'Escape Escape OC' 'M-Right'
assert_key 'Escape OD' 'Left' -- 'Escape Escape OD' 'M-Left'
assert_key 'Escape [A' 'Up' -- 'Escape Escape [A' 'M-Up'
assert_key 'Escape [B' 'Down' -- 'Escape Escape [B' 'M-Down'
assert_key 'Escape [C' 'Right' -- 'Escape Escape [C' 'M-Right'
assert_key 'Escape [D' 'Left' -- 'Escape Escape [D' 'M-Left'
# Other xterm keys
assert_key 'Escape OH' 'Home' -- 'Escape Escape OH' 'M-Home'
assert_key 'Escape OF' 'End' -- 'Escape Escape OF' 'M-End'
assert_key 'Escape [H' 'Home' -- 'Escape Escape [H' 'M-Home'
assert_key 'Escape [F' 'End' -- 'Escape Escape [F' 'M-End'
# rxvt arrow keys
assert_key 'Escape Oa' 'C-Up'
assert_key 'Escape Ob' 'C-Down'
assert_key 'Escape Oc' 'C-Right'
assert_key 'Escape Od' 'C-Left'
assert_key 'Escape [a' 'S-Up'
assert_key 'Escape [b' 'S-Down'
assert_key 'Escape [c' 'S-Right'
assert_key 'Escape [d' 'S-Left'
# rxvt function keys
assert_key 'Escape [11~' 'F1'
assert_key 'Escape [12~' 'F2'
assert_key 'Escape [13~' 'F3'
assert_key 'Escape [14~' 'F4'
assert_key 'Escape [15~' 'F5'
assert_key 'Escape [17~' 'F6'
assert_key 'Escape [18~' 'F7'
assert_key 'Escape [19~' 'F8'
assert_key 'Escape [20~' 'F9'
assert_key 'Escape [21~' 'F10'
assert_key 'Escape [23~' 'F11'
assert_key 'Escape [24~' 'F12'
# With TERM=screen, these will be seen as F11 and F12
# assert_key 'Escape [23~' 'S-F1'
# assert_key 'Escape [24~' 'S-F2'
assert_key 'Escape [25~' 'S-F3'
assert_key 'Escape [26~' 'S-F4'
assert_key 'Escape [28~' 'S-F5'
assert_key 'Escape [29~' 'S-F6'
assert_key 'Escape [31~' 'S-F7'
assert_key 'Escape [32~' 'S-F8'
assert_key 'Escape [33~' 'S-F9'
assert_key 'Escape [34~' 'S-F10'
assert_key 'Escape [23$' 'S-F11'
assert_key 'Escape [24$' 'S-F12'
assert_key 'Escape [11^' 'C-F1'
assert_key 'Escape [12^' 'C-F2'
assert_key 'Escape [13^' 'C-F3'
assert_key 'Escape [14^' 'C-F4'
assert_key 'Escape [15^' 'C-F5'
assert_key 'Escape [17^' 'C-F6'
assert_key 'Escape [18^' 'C-F7'
assert_key 'Escape [19^' 'C-F8'
assert_key 'Escape [20^' 'C-F9'
assert_key 'Escape [21^' 'C-F10'
assert_key 'Escape [23^' 'C-F11'
assert_key 'Escape [24^' 'C-F12'
assert_key 'Escape [11@' 'C-S-F1'
assert_key 'Escape [12@' 'C-S-F2'
assert_key 'Escape [13@' 'C-S-F3'
assert_key 'Escape [14@' 'C-S-F4'
assert_key 'Escape [15@' 'C-S-F5'
assert_key 'Escape [17@' 'C-S-F6'
assert_key 'Escape [18@' 'C-S-F7'
assert_key 'Escape [19@' 'C-S-F8'
assert_key 'Escape [20@' 'C-S-F9'
assert_key 'Escape [21@' 'C-S-F10'
assert_key 'Escape [23@' 'C-S-F11'
assert_key 'Escape [24@' 'C-S-F12'
# Focus tracking
assert_key 'Escape [I' 'FocusIn'
assert_key 'Escape [O' 'FocusOut'
# Paste keys
assert_key 'Escape [200~' 'PasteStart'
assert_key 'Escape [201~' 'PasteEnd'
assert_key 'Escape [Z' 'BTab'
assert_extended_key () {
code=$1
key_name=$2
assert_key "Escape [${code};5u" "C-$key_name"
assert_key "Escape [${code};7u" "M-C-$key_name"
}
# Extended keys
# assert_extended_key 65 'A'
# assert_extended_key 66 'B'
# assert_extended_key 67 'C'
# assert_extended_key 68 'D'
# assert_extended_key 69 'E'
# assert_extended_key 70 'F'
# assert_extended_key 71 'G'
# assert_extended_key 72 'H'
# assert_extended_key 73 'I'
# assert_extended_key 74 'J'
# assert_extended_key 75 'K'
# assert_extended_key 76 'L'
# assert_extended_key 77 'M'
# assert_extended_key 78 'N'
# assert_extended_key 79 'O'
# assert_extended_key 80 'P'
# assert_extended_key 81 'Q'
# assert_extended_key 82 'R'
# assert_extended_key 83 'S'
# assert_extended_key 84 'T'
# assert_extended_key 85 'U'
# assert_extended_key 86 'V'
# assert_extended_key 87 'W'
# assert_extended_key 88 'X'
# assert_extended_key 89 'Y'
# assert_extended_key 90 'Z'
# assert_extended_key 123 '{'
# assert_extended_key 124 '|'
# assert_extended_key 125 '}'
# assert_key 'Escape [105;5u' 'C-i'
# assert_key 'Escape [73;5u' 'C-I'
# assert_key 'Escape [109;5u' 'C-m'
# assert_key 'Escape [77;5u' 'C-M'
# assert_key 'Escape [91;5u' 'C-['
assert_key 'Escape [123;5u' 'C-{'
# assert_key 'Escape [64;5u' 'C-@'
assert_key 'Escape [32;2u' 'S-Space'
# assert_key 'Escape [32;6u' 'C-S-Space'
assert_key 'Escape [9;5u' 'C-Tab'
assert_key 'Escape [1;5Z' 'C-S-Tab'
$TMUX kill-server 2>/dev/null
$TMUX2 kill-server 2>/dev/null
exit $exit_status

View File

@@ -24,7 +24,7 @@
#include "tmux.h" #include "tmux.h"
static void static void
regsub_copy(char **buf, size_t *len, const char *text, size_t start, size_t end) regsub_copy(char **buf, ssize_t *len, const char *text, size_t start, size_t end)
{ {
size_t add = end - start; size_t add = end - start;
@@ -34,7 +34,7 @@ regsub_copy(char **buf, size_t *len, const char *text, size_t start, size_t end)
} }
static void static void
regsub_expand(char **buf, size_t *len, const char *with, const char *text, regsub_expand(char **buf, ssize_t *len, const char *with, const char *text,
regmatch_t *m, u_int n) regmatch_t *m, u_int n)
{ {
const char *cp; const char *cp;

View File

@@ -738,7 +738,7 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
} }
} }
tty_cell(tty, &gc, &grid_default_cell, NULL); tty_cell(tty, &gc, &grid_default_cell, NULL, NULL);
if (isolates) if (isolates)
tty_puts(tty, START_ISOLATE); tty_puts(tty, START_ISOLATE);
} }
@@ -856,4 +856,8 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
tty_default_colours(&defaults, wp); tty_default_colours(&defaults, wp);
tty_draw_line(tty, s, i, j, width, x, y, &defaults, palette); tty_draw_line(tty, s, i, j, width, x, y, &defaults, palette);
} }
#ifdef ENABLE_SIXEL
tty_draw_images(c, wp, s);
#endif
} }

View File

@@ -30,11 +30,10 @@ static void screen_write_collect_clear(struct screen_write_ctx *, u_int,
static void screen_write_collect_scroll(struct screen_write_ctx *, u_int); static void screen_write_collect_scroll(struct screen_write_ctx *, u_int);
static void screen_write_collect_flush(struct screen_write_ctx *, int, static void screen_write_collect_flush(struct screen_write_ctx *, int,
const char *); const char *);
static int screen_write_overwrite(struct screen_write_ctx *, static int screen_write_overwrite(struct screen_write_ctx *,
struct grid_cell *, u_int); struct grid_cell *, u_int);
static const struct grid_cell *screen_write_combine(struct screen_write_ctx *, static int screen_write_combine(struct screen_write_ctx *,
const struct utf8_data *, u_int *); const struct grid_cell *);
struct screen_write_citem { struct screen_write_citem {
u_int x; u_int x;
@@ -132,6 +131,12 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
{ {
struct window_pane *wp = ttyctx->arg; struct window_pane *wp = ttyctx->arg;
if (ttyctx->allow_invisible_panes) {
if (session_has(c->session, wp->window))
return (1);
return (0);
}
if (c->session->curw->window != wp->window) if (c->session->curw->window != wp->window)
return (0); return (0);
if (wp->layout_cell == NULL) if (wp->layout_cell == NULL)
@@ -320,7 +325,9 @@ screen_write_reset(struct screen_write_ctx *ctx)
screen_reset_tabs(s); screen_reset_tabs(s);
screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1);
s->mode = MODE_CURSOR | MODE_WRAP; s->mode = MODE_CURSOR|MODE_WRAP;
if (options_get_number(global_options, "extended-keys") == 2)
s->mode |= MODE_KEXTENDED;
screen_write_clearscreen(ctx, 8); screen_write_clearscreen(ctx, 8);
screen_write_set_cursor(ctx, 0, 0); screen_write_set_cursor(ctx, 0, 0);
@@ -584,9 +591,46 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
} }
} }
/* Select character set for drawing border lines. */
static void
screen_write_box_border_set(enum box_lines lines, int cell_type,
struct grid_cell *gc)
{
switch (lines) {
case BOX_LINES_NONE:
break;
case BOX_LINES_DOUBLE:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_copy(&gc->data, tty_acs_double_borders(cell_type));
break;
case BOX_LINES_HEAVY:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
break;
case BOX_LINES_ROUNDED:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_copy(&gc->data, tty_acs_rounded_borders(cell_type));
break;
case BOX_LINES_SIMPLE:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
break;
case BOX_LINES_PADDED:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_set(&gc->data, PADDED_BORDERS[cell_type]);
break;
case BOX_LINES_SINGLE:
case BOX_LINES_DEFAULT:
gc->attr |= GRID_ATTR_CHARSET;
utf8_set(&gc->data, CELL_BORDERS[cell_type]);
break;
}
}
/* Draw a horizontal line on screen. */ /* Draw a horizontal line on screen. */
void void
screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right) screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right,
enum box_lines lines, const struct grid_cell *border_gc)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct grid_cell gc; struct grid_cell gc;
@@ -595,13 +639,27 @@ screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right)
cx = s->cx; cx = s->cx;
cy = s->cy; cy = s->cy;
memcpy(&gc, &grid_default_cell, sizeof gc); if (border_gc != NULL)
memcpy(&gc, border_gc, sizeof gc);
else
memcpy(&gc, &grid_default_cell, sizeof gc);
gc.attr |= GRID_ATTR_CHARSET; gc.attr |= GRID_ATTR_CHARSET;
screen_write_putc(ctx, &gc, left ? 't' : 'q'); if (left)
screen_write_box_border_set(lines, CELL_LEFTJOIN, &gc);
else
screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
screen_write_cell(ctx, &gc);
screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
for (i = 1; i < nx - 1; i++) for (i = 1; i < nx - 1; i++)
screen_write_putc(ctx, &gc, 'q'); screen_write_cell(ctx, &gc);
screen_write_putc(ctx, &gc, right ? 'u' : 'q');
if (right)
screen_write_box_border_set(lines, CELL_RIGHTJOIN, &gc);
else
screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
screen_write_cell(ctx, &gc);
screen_write_set_cursor(ctx, cx, cy); screen_write_set_cursor(ctx, cx, cy);
} }
@@ -633,86 +691,55 @@ screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
/* Draw a menu on screen. */ /* Draw a menu on screen. */
void void
screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice,
int choice, const struct grid_cell *choice_gc) enum box_lines lines, const struct grid_cell *menu_gc,
const struct grid_cell *border_gc, const struct grid_cell *choice_gc)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct grid_cell default_gc; struct grid_cell default_gc;
const struct grid_cell *gc = &default_gc; const struct grid_cell *gc = &default_gc;
u_int cx, cy, i, j; u_int cx, cy, i, j, width = menu->width;
const char *name; const char *name;
cx = s->cx; cx = s->cx;
cy = s->cy; cy = s->cy;
memcpy(&default_gc, &grid_default_cell, sizeof default_gc); memcpy(&default_gc, menu_gc, sizeof default_gc);
screen_write_box(ctx, menu->width + 4, menu->count + 2, screen_write_box(ctx, menu->width + 4, menu->count + 2, lines,
BOX_LINES_DEFAULT, &default_gc, menu->title); border_gc, menu->title);
for (i = 0; i < menu->count; i++) { for (i = 0; i < menu->count; i++) {
name = menu->items[i].name; name = menu->items[i].name;
if (name == NULL) { if (name == NULL) {
screen_write_cursormove(ctx, cx, cy + 1 + i, 0); screen_write_cursormove(ctx, cx, cy + 1 + i, 0);
screen_write_hline(ctx, menu->width + 4, 1, 1); screen_write_hline(ctx, width + 4, 1, 1, lines,
} else { border_gc);
if (choice >= 0 && i == (u_int)choice && *name != '-') continue;
gc = choice_gc;
screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
for (j = 0; j < menu->width; j++)
screen_write_putc(ctx, gc, ' ');
screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
if (*name == '-') {
name++;
default_gc.attr |= GRID_ATTR_DIM;
format_draw(ctx, gc, menu->width, name, NULL,
0);
default_gc.attr &= ~GRID_ATTR_DIM;
} else
format_draw(ctx, gc, menu->width, name, NULL,
gc == choice_gc);
gc = &default_gc;
} }
if (choice >= 0 && i == (u_int)choice && *name != '-')
gc = choice_gc;
screen_write_cursormove(ctx, cx + 1, cy + 1 + i, 0);
for (j = 0; j < width + 2; j++)
screen_write_putc(ctx, gc, ' ');
screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
if (*name == '-') {
default_gc.attr |= GRID_ATTR_DIM;
format_draw(ctx, gc, width, name + 1, NULL, 0);
default_gc.attr &= ~GRID_ATTR_DIM;
continue;
}
format_draw(ctx, gc, width, name, NULL, 0);
gc = &default_gc;
} }
screen_write_set_cursor(ctx, cx, cy); screen_write_set_cursor(ctx, cx, cy);
} }
static void
screen_write_box_border_set(enum box_lines box_lines, int cell_type,
struct grid_cell *gc)
{
switch (box_lines) {
case BOX_LINES_NONE:
break;
case BOX_LINES_DOUBLE:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_copy(&gc->data, tty_acs_double_borders(cell_type));
break;
case BOX_LINES_HEAVY:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
break;
case BOX_LINES_ROUNDED:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_copy(&gc->data, tty_acs_rounded_borders(cell_type));
break;
case BOX_LINES_SIMPLE:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
break;
case BOX_LINES_PADDED:
gc->attr &= ~GRID_ATTR_CHARSET;
utf8_set(&gc->data, PADDED_BORDERS[cell_type]);
break;
case BOX_LINES_SINGLE:
case BOX_LINES_DEFAULT:
gc->attr |= GRID_ATTR_CHARSET;
utf8_set(&gc->data, CELL_BORDERS[cell_type]);
break;
}
}
/* Draw a box on screen. */ /* Draw a box on screen. */
void void
screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny, screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny,
@@ -984,6 +1011,11 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
memcpy(&gc, &grid_default_cell, sizeof gc); memcpy(&gc, &grid_default_cell, sizeof gc);
utf8_set(&gc.data, 'E'); utf8_set(&gc.data, 'E');
#ifdef ENABLE_SIXEL
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
for (yy = 0; yy < screen_size_y(s); yy++) { for (yy = 0; yy < screen_size_y(s); yy++) {
for (xx = 0; xx < screen_size_x(s); xx++) for (xx = 0; xx < screen_size_x(s); xx++)
grid_view_set_cell(s->grid, xx, yy, &gc); grid_view_set_cell(s->grid, xx, yy, &gc);
@@ -1018,6 +1050,11 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
if (s->cx > screen_size_x(s) - 1) if (s->cx > screen_size_x(s) - 1)
return; return;
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
screen_write_initctx(ctx, &ttyctx, 0); screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.bg = bg; ttyctx.bg = bg;
@@ -1046,6 +1083,11 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
if (s->cx > screen_size_x(s) - 1) if (s->cx > screen_size_x(s) - 1)
return; return;
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
screen_write_initctx(ctx, &ttyctx, 0); screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.bg = bg; ttyctx.bg = bg;
@@ -1074,6 +1116,11 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
if (s->cx > screen_size_x(s) - 1) if (s->cx > screen_size_x(s) - 1)
return; return;
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
screen_write_initctx(ctx, &ttyctx, 0); screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.bg = bg; ttyctx.bg = bg;
@@ -1092,9 +1139,18 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
struct grid *gd = s->grid; struct grid *gd = s->grid;
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
#ifdef ENABLE_SIXEL
u_int sy = screen_size_y(s);
#endif
if (ny == 0) if (ny == 0)
ny = 1; ny = 1;
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
if (s->cy < s->rupper || s->cy > s->rlower) { if (s->cy < s->rupper || s->cy > s->rlower) {
if (ny > screen_size_y(s) - s->cy) if (ny > screen_size_y(s) - s->cy)
ny = screen_size_y(s) - s->cy; ny = screen_size_y(s) - s->cy;
@@ -1138,13 +1194,19 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct grid *gd = s->grid; struct grid *gd = s->grid;
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
u_int sy = screen_size_y(s);
if (ny == 0) if (ny == 0)
ny = 1; ny = 1;
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
if (s->cy < s->rupper || s->cy > s->rlower) { if (s->cy < s->rupper || s->cy > s->rlower) {
if (ny > screen_size_y(s) - s->cy) if (ny > sy - s->cy)
ny = screen_size_y(s) - s->cy; ny = sy - s->cy;
if (ny == 0) if (ny == 0)
return; return;
@@ -1190,6 +1252,11 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
return; return;
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
screen_write_collect_clear(ctx, s->cy, 1); screen_write_collect_clear(ctx, s->cy, 1);
@@ -1219,6 +1286,11 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg))) if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
return; return;
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL); before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL);
@@ -1246,6 +1318,11 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
return; return;
} }
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
if (s->cx > sx - 1) if (s->cx > sx - 1)
grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
else else
@@ -1294,6 +1371,11 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
if (s->cy == s->rupper) { if (s->cy == s->rupper) {
#ifdef ENABLE_SIXEL
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg); grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
screen_write_collect_flush(ctx, 0, __func__); screen_write_collect_flush(ctx, 0, __func__);
@@ -1336,13 +1418,17 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct grid *gd = s->grid; struct grid *gd = s->grid;
struct grid_line *gl; struct grid_line *gl;
#ifdef ENABLE_SIXEL
int redraw = 0;
#endif
u_int rupper = s->rupper, rlower = s->rlower;
gl = grid_get_line(gd, gd->hsize + s->cy); gl = grid_get_line(gd, gd->hsize + s->cy);
if (wrapped) if (wrapped)
gl->flags |= GRID_LINE_WRAPPED; gl->flags |= GRID_LINE_WRAPPED;
log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
s->rupper, s->rlower); rupper, rlower);
if (bg != ctx->bg) { if (bg != ctx->bg) {
screen_write_collect_flush(ctx, 1, __func__); screen_write_collect_flush(ctx, 1, __func__);
@@ -1350,6 +1436,14 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
} }
if (s->cy == s->rlower) { if (s->cy == s->rlower) {
#ifdef ENABLE_SIXEL
if (rlower == screen_size_y(s) - 1)
redraw = image_scroll_up(s, 1);
else
redraw = image_check_line(s, rupper, rlower - rupper);
if (redraw && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
screen_write_collect_scroll(ctx, bg); screen_write_collect_scroll(ctx, bg);
ctx->scrolled++; ctx->scrolled++;
@@ -1375,6 +1469,11 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
ctx->bg = bg; ctx->bg = bg;
} }
#ifdef ENABLE_SIXEL
if (image_scroll_up(s, lines) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
for (i = 0; i < lines; i++) { for (i = 0; i < lines; i++) {
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
screen_write_collect_scroll(ctx, bg); screen_write_collect_scroll(ctx, bg);
@@ -1399,6 +1498,11 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
else if (lines > s->rlower - s->rupper + 1) else if (lines > s->rlower - s->rupper + 1)
lines = s->rlower - s->rupper + 1; lines = s->rlower - s->rupper + 1;
#ifdef ENABLE_SIXEL
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
for (i = 0; i < lines; i++) for (i = 0; i < lines; i++)
grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg); grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg);
@@ -1423,6 +1527,11 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
u_int sx = screen_size_x(s), sy = screen_size_y(s); u_int sx = screen_size_x(s), sy = screen_size_y(s);
#ifdef ENABLE_SIXEL
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
screen_write_initctx(ctx, &ttyctx, 1); screen_write_initctx(ctx, &ttyctx, 1);
ttyctx.bg = bg; ttyctx.bg = bg;
@@ -1452,6 +1561,11 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
u_int sx = screen_size_x(s); u_int sx = screen_size_x(s);
#ifdef ENABLE_SIXEL
if (image_check_line(s, 0, s->cy - 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
screen_write_initctx(ctx, &ttyctx, 1); screen_write_initctx(ctx, &ttyctx, 1);
ttyctx.bg = bg; ttyctx.bg = bg;
@@ -1475,6 +1589,11 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
u_int sx = screen_size_x(s), sy = screen_size_y(s); u_int sx = screen_size_x(s), sy = screen_size_y(s);
#ifdef ENABLE_SIXEL
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
screen_write_initctx(ctx, &ttyctx, 1); screen_write_initctx(ctx, &ttyctx, 1);
ttyctx.bg = bg; ttyctx.bg = bg;
@@ -1506,7 +1625,8 @@ screen_write_fullredraw(struct screen_write_ctx *ctx)
screen_write_collect_flush(ctx, 0, __func__); screen_write_collect_flush(ctx, 0, __func__);
screen_write_initctx(ctx, &ttyctx, 1); screen_write_initctx(ctx, &ttyctx, 1);
ttyctx.redraw_cb(&ttyctx); if (ttyctx.redraw_cb != NULL)
ttyctx.redraw_cb(&ttyctx);
} }
/* Trim collected items. */ /* Trim collected items. */
@@ -1742,6 +1862,11 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
} }
} }
#ifdef ENABLE_SIXEL
if (image_check_area(s, s->cx, s->cy, ci->used, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x, grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
ci->used); ci->used);
screen_write_set_cursor(ctx, s->cx + ci->used, -1); screen_write_set_cursor(ctx, s->cx + ci->used, -1);
@@ -1813,54 +1938,21 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct grid *gd = s->grid; struct grid *gd = s->grid;
const struct utf8_data *ud = &gc->data; const struct utf8_data *ud = &gc->data;
const struct utf8_data zwj = { "\342\200\215", 0, 3, 0 };
struct grid_line *gl; struct grid_line *gl;
struct grid_cell_entry *gce; struct grid_cell_entry *gce;
struct grid_cell tmp_gc, now_gc; struct grid_cell tmp_gc, now_gc;
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
u_int sx = screen_size_x(s), sy = screen_size_y(s); u_int sx = screen_size_x(s), sy = screen_size_y(s);
u_int width = gc->data.width, xx, last, cx, cy; u_int width = ud->width, xx, not_wrap;
int selected, skip = 1; int selected, skip = 1;
/* Ignore padding cells. */ /* Ignore padding cells. */
if (gc->flags & GRID_FLAG_PADDING) if (gc->flags & GRID_FLAG_PADDING)
return; return;
/* /* Get the previous cell to check for combining. */
* If this is a zero width joiner, set the flag so the next character if (screen_write_combine(ctx, gc) != 0)
* will be treated as zero width and appended. Note that we assume a
* ZWJ will not change the width - the width of the first character is
* used.
*/
if (ud->size == 3 && memcmp(ud->data, "\342\200\215", 3) == 0) {
log_debug("zero width joiner at %u,%u", s->cx, s->cy);
ctx->flags |= SCREEN_WRITE_ZWJ;
return; return;
}
/*
* If the width is zero, combine onto the previous character. We always
* combine with the cell to the left of the cursor position. In theory,
* the application could have moved the cursor somewhere else, but if
* they are silly enough to do that, who cares?
*/
if (ctx->flags & SCREEN_WRITE_ZWJ) {
screen_write_collect_flush(ctx, 0, __func__);
screen_write_combine(ctx, &zwj, &xx);
}
if (width == 0 || (ctx->flags & SCREEN_WRITE_ZWJ)) {
ctx->flags &= ~SCREEN_WRITE_ZWJ;
screen_write_collect_flush(ctx, 0, __func__);
if ((gc = screen_write_combine(ctx, ud, &xx)) != NULL) {
cx = s->cx; cy = s->cy;
screen_write_set_cursor(ctx, xx, s->cy);
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.cell = gc;
tty_write(tty_cmd_cell, &ttyctx);
s->cx = cx; s->cy = cy;
}
return;
}
/* Flush any existing scrolling. */ /* Flush any existing scrolling. */
screen_write_collect_flush(ctx, 1, __func__); screen_write_collect_flush(ctx, 1, __func__);
@@ -1952,11 +2044,11 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
* Move the cursor. If not wrapping, stick at the last character and * Move the cursor. If not wrapping, stick at the last character and
* replace it. * replace it.
*/ */
last = !(s->mode & MODE_WRAP); not_wrap = !(s->mode & MODE_WRAP);
if (s->cx <= sx - last - width) if (s->cx <= sx - not_wrap - width)
screen_write_set_cursor(ctx, s->cx + width, -1); screen_write_set_cursor(ctx, s->cx + width, -1);
else else
screen_write_set_cursor(ctx, sx - last, -1); screen_write_set_cursor(ctx, sx - not_wrap, -1);
/* Create space for character in insert mode. */ /* Create space for character in insert mode. */
if (s->mode & MODE_INSERT) { if (s->mode & MODE_INSERT) {
@@ -1976,49 +2068,102 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
} }
} }
/* Combine a UTF-8 zero-width character onto the previous. */ /* Combine a UTF-8 zero-width character onto the previous if necessary. */
static const struct grid_cell * static int
screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud, screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
u_int *xx)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct grid *gd = s->grid; struct grid *gd = s->grid;
static struct grid_cell gc; const struct utf8_data *ud = &gc->data;
u_int n; u_int n, cx = s->cx, cy = s->cy;
struct grid_cell last;
struct tty_ctx ttyctx;
int force_wide = 0, zero_width = 0;
/* Can't combine if at 0. */ /*
if (s->cx == 0) * Is this character which makes no sense without being combined? If
return (NULL); * this is true then flag it here and discard the character (return 1)
* if we cannot combine it.
*/
if (utf8_is_zwj(ud))
zero_width = 1;
else if (utf8_is_vs(ud))
zero_width = force_wide = 1;
else if (ud->width == 0)
zero_width = 1;
/* Empty data is out. */ /* Cannot combine empty character or at left. */
if (ud->size == 0) if (ud->size < 2 || cx == 0)
fatalx("UTF-8 data empty"); return (zero_width);
log_debug("%s: character %.*s at %u,%u (width %u)", __func__,
(int)ud->size, ud->data, cx, cy, ud->width);
/* Retrieve the previous cell. */ /* Find the cell to combine with. */
for (n = 1; n <= s->cx; n++) { n = 1;
grid_view_get_cell(gd, s->cx - n, s->cy, &gc); grid_view_get_cell(gd, cx - n, cy, &last);
if (~gc.flags & GRID_FLAG_PADDING) if (cx != 1 && (last.flags & GRID_FLAG_PADDING)) {
break; n = 2;
grid_view_get_cell(gd, cx - n, cy, &last);
} }
if (n > s->cx) if (n != last.data.width || (last.flags & GRID_FLAG_PADDING))
return (NULL); return (zero_width);
*xx = s->cx - n;
/* Check there is enough space. */ /*
if (gc.data.size + ud->size > sizeof gc.data.data) * Check if we need to combine characters. This could be zero width
return (NULL); * (set above), a modifier character (with an existing Unicode
* character) or a previous ZWJ.
*/
if (!zero_width) {
if (utf8_is_modifier(ud)) {
if (last.data.size < 2)
return (0);
force_wide = 1;
} else if (!utf8_has_zwj(&last.data))
return (0);
}
log_debug("%s: %.*s onto %.*s at %u,%u", __func__, (int)ud->size, /* Check if this combined character would be too long. */
ud->data, (int)gc.data.size, gc.data.data, *xx, s->cy); if (last.data.size + ud->size > sizeof last.data.data)
return (0);
/* Combining; flush any pending output. */
screen_write_collect_flush(ctx, 0, __func__);
log_debug("%s: %.*s -> %.*s at %u,%u (offset %u, width %u)", __func__,
(int)ud->size, ud->data, (int)last.data.size, last.data.data,
cx - n, cy, n, last.data.width);
/* Append the data. */ /* Append the data. */
memcpy(gc.data.data + gc.data.size, ud->data, ud->size); memcpy(last.data.data + last.data.size, ud->data, ud->size);
gc.data.size += ud->size; last.data.size += ud->size;
/* Force the width to 2 for modifiers and variation selector. */
if (last.data.width == 1 && force_wide) {
last.data.width = 2;
n = 2;
cx++;
} else
force_wide = 0;
/* Set the new cell. */ /* Set the new cell. */
grid_view_set_cell(gd, *xx, s->cy, &gc); grid_view_set_cell(gd, cx - n, cy, &last);
if (force_wide)
grid_view_set_padding(gd, cx, cy);
return (&gc); /*
* Redraw the combined cell. If forcing the cell to width 2, reset the
* cached cursor position in the tty, since we don't really know
* whether the terminal thought the character was width 1 or width 2
* and what it is going to do now.
*/
screen_write_set_cursor(ctx, cx - n, cy);
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.cell = &last;
ttyctx.num = force_wide; /* reset cached cursor position */
tty_write(tty_cmd_cell, &ttyctx);
screen_write_set_cursor(ctx, cx, cy);
return (1);
} }
/* /*
@@ -2085,12 +2230,14 @@ screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
/* Set external clipboard. */ /* Set external clipboard. */
void void
screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) screen_write_setselection(struct screen_write_ctx *ctx, const char *flags,
u_char *str, u_int len)
{ {
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
screen_write_initctx(ctx, &ttyctx, 0); screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.ptr = str; ttyctx.ptr = str;
ttyctx.ptr2 = (void *)flags;
ttyctx.num = len; ttyctx.num = len;
tty_write(tty_cmd_setselection, &ttyctx); tty_write(tty_cmd_setselection, &ttyctx);
@@ -2098,17 +2245,74 @@ screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len)
/* Write unmodified string. */ /* Write unmodified string. */
void void
screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
int allow_invisible_panes)
{ {
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
screen_write_initctx(ctx, &ttyctx, 0); screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.ptr = str; ttyctx.ptr = str;
ttyctx.num = len; ttyctx.num = len;
ttyctx.allow_invisible_panes = allow_invisible_panes;
tty_write(tty_cmd_rawstring, &ttyctx); tty_write(tty_cmd_rawstring, &ttyctx);
} }
#ifdef ENABLE_SIXEL
/* Write a SIXEL image. */
void
screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
u_int bg)
{
struct screen *s = ctx->s;
struct grid *gd = s->grid;
struct tty_ctx ttyctx;
u_int x, y, sx, sy, cx = s->cx, cy = s->cy, i, lines;
struct sixel_image *new;
sixel_size_in_cells(si, &x, &y);
if (x > screen_size_x(s) || y > screen_size_y(s)) {
if (x > screen_size_x(s) - cx)
sx = screen_size_x(s) - cx;
else
sx = x;
if (y > screen_size_y(s) - 1)
sy = screen_size_y(s) - 1;
else
sy = y;
new = sixel_scale(si, 0, 0, 0, y - sy, sx, sy, 1);
sixel_free(si);
si = new;
sixel_size_in_cells(si, &x, &y);
}
sy = screen_size_y(s) - cy;
if (sy < y) {
lines = y - sy + 1;
if (image_scroll_up(s, lines) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
for (i = 0; i < lines; i++) {
grid_view_scroll_region_up(gd, 0, screen_size_y(s) - 1,
bg);
screen_write_collect_scroll(ctx, bg);
}
ctx->scrolled += lines;
if (lines > cy)
screen_write_cursormove(ctx, -1, 0, 0);
else
screen_write_cursormove(ctx, -1, cy - lines, 0);
}
screen_write_collect_flush(ctx, 0, __func__);
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.ptr = image_store(s, si);
tty_write(tty_cmd_sixelimage, &ttyctx);
screen_write_cursormove(ctx, 0, cy + y, 0);
}
#endif
/* Turn alternate screen on. */ /* Turn alternate screen on. */
void void
screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc, screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
@@ -2124,7 +2328,8 @@ screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
screen_alternate_on(ctx->s, gc, cursor); screen_alternate_on(ctx->s, gc, cursor);
screen_write_initctx(ctx, &ttyctx, 1); screen_write_initctx(ctx, &ttyctx, 1);
ttyctx.redraw_cb(&ttyctx); if (ttyctx.redraw_cb != NULL)
ttyctx.redraw_cb(&ttyctx);
} }
/* Turn alternate screen off. */ /* Turn alternate screen off. */
@@ -2142,5 +2347,6 @@ screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
screen_alternate_off(ctx->s, gc, cursor); screen_alternate_off(ctx->s, gc, cursor);
screen_write_initctx(ctx, &ttyctx, 1); screen_write_initctx(ctx, &ttyctx, 1);
ttyctx.redraw_cb(&ttyctx); if (ttyctx.redraw_cb != NULL)
ttyctx.redraw_cb(&ttyctx);
} }

View File

@@ -82,13 +82,19 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
s->cstyle = SCREEN_CURSOR_DEFAULT; s->cstyle = SCREEN_CURSOR_DEFAULT;
s->default_cstyle = SCREEN_CURSOR_DEFAULT; s->default_cstyle = SCREEN_CURSOR_DEFAULT;
s->mode = MODE_CURSOR;
s->default_mode = 0; s->default_mode = 0;
s->ccolour = -1; s->ccolour = -1;
s->default_ccolour = -1; s->default_ccolour = -1;
s->tabs = NULL; s->tabs = NULL;
s->sel = NULL; s->sel = NULL;
#ifdef ENABLE_SIXEL
TAILQ_INIT(&s->images);
#endif
s->write_list = NULL; s->write_list = NULL;
s->hyperlinks = NULL;
screen_reinit(s); screen_reinit(s);
} }
@@ -118,6 +124,22 @@ screen_reinit(struct screen *s)
screen_clear_selection(s); screen_clear_selection(s);
screen_free_titles(s); screen_free_titles(s);
#ifdef ENABLE_SIXEL
image_free_all(s);
#endif
screen_reset_hyperlinks(s);
}
/* Reset hyperlinks of a screen. */
void
screen_reset_hyperlinks(struct screen *s)
{
if (s->hyperlinks == NULL)
s->hyperlinks = hyperlinks_init();
else
hyperlinks_reset(s->hyperlinks);
} }
/* Destroy a screen. */ /* Destroy a screen. */
@@ -136,7 +158,13 @@ screen_free(struct screen *s)
grid_destroy(s->saved_grid); grid_destroy(s->saved_grid);
grid_destroy(s->grid); grid_destroy(s->grid);
if (s->hyperlinks != NULL)
hyperlinks_free(s->hyperlinks);
screen_free_titles(s); screen_free_titles(s);
#ifdef ENABLE_SIXEL
image_free_all(s);
#endif
} }
/* Reset tabs to default, eight spaces apart. */ /* Reset tabs to default, eight spaces apart. */
@@ -280,8 +308,12 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow,
if (sy != screen_size_y(s)) if (sy != screen_size_y(s))
screen_resize_y(s, sy, eat_empty, &cy); screen_resize_y(s, sy, eat_empty, &cy);
if (reflow) if (reflow) {
#ifdef ENABLE_SIXEL
image_free_all(s);
#endif
screen_reflow(s, sx, &cx, &cy, cursor); screen_reflow(s, sx, &cx, &cy, cursor);
}
if (cy >= s->grid->hsize) { if (cy >= s->grid->hsize) {
s->cx = cx; s->cx = cx;
@@ -611,7 +643,7 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
* before copying back. * before copying back.
*/ */
if (s->saved_grid != NULL) if (s->saved_grid != NULL)
screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 1); screen_resize(s, s->saved_grid->sx, s->saved_grid->sy, 0);
/* /*
* Restore the cursor position and cell. This happens even if not * Restore the cursor position and cell. This happens even if not
@@ -685,9 +717,9 @@ screen_mode_to_string(int mode)
if (mode & MODE_CURSOR_VERY_VISIBLE) if (mode & MODE_CURSOR_VERY_VISIBLE)
strlcat(tmp, "CURSOR_VERY_VISIBLE,", sizeof tmp); strlcat(tmp, "CURSOR_VERY_VISIBLE,", sizeof tmp);
if (mode & MODE_MOUSE_UTF8) if (mode & MODE_MOUSE_UTF8)
strlcat(tmp, "UTF8,", sizeof tmp); strlcat(tmp, "MOUSE_UTF8,", sizeof tmp);
if (mode & MODE_MOUSE_SGR) if (mode & MODE_MOUSE_SGR)
strlcat(tmp, "SGR,", sizeof tmp); strlcat(tmp, "MOUSE_SGR,", sizeof tmp);
if (mode & MODE_BRACKETPASTE) if (mode & MODE_BRACKETPASTE)
strlcat(tmp, "BRACKETPASTE,", sizeof tmp); strlcat(tmp, "BRACKETPASTE,", sizeof tmp);
if (mode & MODE_FOCUSON) if (mode & MODE_FOCUSON)

View File

@@ -42,6 +42,7 @@ static void server_client_check_modes(struct client *);
static void server_client_set_title(struct client *); static void server_client_set_title(struct client *);
static void server_client_set_path(struct client *); static void server_client_set_path(struct client *);
static void server_client_reset_state(struct client *); static void server_client_reset_state(struct client *);
static int server_client_is_bracket_pasting(struct client *, key_code);
static int server_client_assume_paste(struct session *); static int server_client_assume_paste(struct session *);
static void server_client_update_latest(struct client *); static void server_client_update_latest(struct client *);
@@ -559,9 +560,9 @@ static key_code
server_client_check_mouse(struct client *c, struct key_event *event) server_client_check_mouse(struct client *c, struct key_event *event)
{ {
struct mouse_event *m = &event->m; struct mouse_event *m = &event->m;
struct session *s = c->session; struct session *s = c->session, *fs;
struct winlink *wl; struct winlink *fwl;
struct window_pane *wp; struct window_pane *wp, *fwp;
u_int x, y, b, sx, sy, px, py; u_int x, y, b, sx, sy, px, py;
int ignore = 0; int ignore = 0;
key_code key; key_code key;
@@ -667,6 +668,7 @@ have_event:
/* Save the session. */ /* Save the session. */
m->s = s->id; m->s = s->id;
m->w = -1; m->w = -1;
m->wp = -1;
m->ignore = ignore; m->ignore = ignore;
/* Is this on the status line? */ /* Is this on the status line? */
@@ -683,18 +685,42 @@ have_event:
case STYLE_RANGE_NONE: case STYLE_RANGE_NONE:
return (KEYC_UNKNOWN); return (KEYC_UNKNOWN);
case STYLE_RANGE_LEFT: case STYLE_RANGE_LEFT:
log_debug("mouse range: left");
where = STATUS_LEFT; where = STATUS_LEFT;
break; break;
case STYLE_RANGE_RIGHT: case STYLE_RANGE_RIGHT:
log_debug("mouse range: right");
where = STATUS_RIGHT; where = STATUS_RIGHT;
break; break;
case STYLE_RANGE_WINDOW: case STYLE_RANGE_PANE:
wl = winlink_find_by_index(&s->windows, fwp = window_pane_find_by_id(sr->argument);
sr->argument); if (fwp == NULL)
if (wl == NULL)
return (KEYC_UNKNOWN); return (KEYC_UNKNOWN);
m->w = wl->window->id; m->wp = sr->argument;
log_debug("mouse range: pane %%%u", m->wp);
where = STATUS;
break;
case STYLE_RANGE_WINDOW:
fwl = winlink_find_by_index(&s->windows,
sr->argument);
if (fwl == NULL)
return (KEYC_UNKNOWN);
m->w = fwl->window->id;
log_debug("mouse range: window @%u", m->w);
where = STATUS;
break;
case STYLE_RANGE_SESSION:
fs = session_find_by_id(sr->argument);
if (fs == NULL)
return (KEYC_UNKNOWN);
m->s = sr->argument;
log_debug("mouse range: session $%u", m->s);
where = STATUS;
break;
case STYLE_RANGE_USER:
where = STATUS; where = STATUS;
break; break;
} }
@@ -1754,6 +1780,25 @@ out:
return (key); return (key);
} }
/* Is this a bracket paste key? */
static int
server_client_is_bracket_pasting(struct client *c, key_code key)
{
if (key == KEYC_PASTE_START) {
c->flags |= CLIENT_BRACKETPASTING;
log_debug("%s: bracket paste on", c->name);
return (1);
}
if (key == KEYC_PASTE_END) {
c->flags &= ~CLIENT_BRACKETPASTING;
log_debug("%s: bracket paste off", c->name);
return (1);
}
return !!(c->flags & CLIENT_BRACKETPASTING);
}
/* Is this fast enough to probably be a paste? */ /* Is this fast enough to probably be a paste? */
static int static int
server_client_assume_paste(struct session *s) server_client_assume_paste(struct session *s)
@@ -1818,7 +1863,7 @@ server_client_key_callback(struct cmdq_item *item, void *data)
struct key_binding *bd; struct key_binding *bd;
int xtimeout, flags; int xtimeout, flags;
struct cmd_find_state fs; struct cmd_find_state fs;
key_code key0; key_code key0, prefix, prefix2;
/* Check the client is good to accept input. */ /* Check the client is good to accept input. */
if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
@@ -1862,8 +1907,14 @@ server_client_key_callback(struct cmdq_item *item, void *data)
if (KEYC_IS_MOUSE(key) && !options_get_number(s->options, "mouse")) if (KEYC_IS_MOUSE(key) && !options_get_number(s->options, "mouse"))
goto forward_key; goto forward_key;
/* Forward if bracket pasting. */
if (server_client_is_bracket_pasting(c, key))
goto forward_key;
/* Treat everything as a regular key when pasting is detected. */ /* Treat everything as a regular key when pasting is detected. */
if (!KEYC_IS_MOUSE(key) && server_client_assume_paste(s)) if (!KEYC_IS_MOUSE(key) &&
(~key & KEYC_SENT) &&
server_client_assume_paste(s))
goto forward_key; goto forward_key;
/* /*
@@ -1884,9 +1935,11 @@ table_changed:
* The prefix always takes precedence and forces a switch to the prefix * The prefix always takes precedence and forces a switch to the prefix
* table, unless we are already there. * table, unless we are already there.
*/ */
prefix = (key_code)options_get_number(s->options, "prefix");
prefix2 = (key_code)options_get_number(s->options, "prefix2");
key0 = (key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS)); key0 = (key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS));
if ((key0 == (key_code)options_get_number(s->options, "prefix") || if ((key0 == (prefix & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS)) ||
key0 == (key_code)options_get_number(s->options, "prefix2")) && key0 == (prefix2 & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS))) &&
strcmp(table->name, "prefix") != 0) { strcmp(table->name, "prefix") != 0) {
server_client_set_key_table(c, "prefix"); server_client_set_key_table(c, "prefix");
server_status_client(c); server_status_client(c);
@@ -2218,7 +2271,8 @@ server_client_check_pane_buffer(struct window_pane *wp)
} }
wpo = control_pane_offset(c, wp, &flag); wpo = control_pane_offset(c, wp, &flag);
if (wpo == NULL) { if (wpo == NULL) {
off = 0; if (!flag)
off = 0;
continue; continue;
} }
if (!flag) if (!flag)
@@ -2714,6 +2768,7 @@ server_client_dispatch(struct imsg *imsg, void *arg)
break; break;
server_client_update_latest(c); server_client_update_latest(c);
tty_resize(&c->tty); tty_resize(&c->tty);
tty_repeat_requests(&c->tty);
recalculate_sizes(); recalculate_sizes();
if (c->overlay_resize == NULL) if (c->overlay_resize == NULL)
server_client_clear_overlay(c); server_client_clear_overlay(c);
@@ -2788,8 +2843,11 @@ server_client_command_done(struct cmdq_item *item, __unused void *data)
if (~c->flags & CLIENT_ATTACHED) if (~c->flags & CLIENT_ATTACHED)
c->flags |= CLIENT_EXIT; c->flags |= CLIENT_EXIT;
else if (~c->flags & CLIENT_EXIT) else if (~c->flags & CLIENT_EXIT) {
if (c->flags & CLIENT_CONTROL)
control_ready(c);
tty_send_requests(&c->tty); tty_send_requests(&c->tty);
}
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
@@ -2940,14 +2998,14 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
case MSG_IDENTIFY_STDIN: case MSG_IDENTIFY_STDIN:
if (datalen != 0) if (datalen != 0)
fatalx("bad MSG_IDENTIFY_STDIN size"); fatalx("bad MSG_IDENTIFY_STDIN size");
c->fd = imsg->fd; c->fd = imsg_get_fd(imsg);
log_debug("client %p IDENTIFY_STDIN %d", c, imsg->fd); log_debug("client %p IDENTIFY_STDIN %d", c, c->fd);
break; break;
case MSG_IDENTIFY_STDOUT: case MSG_IDENTIFY_STDOUT:
if (datalen != 0) if (datalen != 0)
fatalx("bad MSG_IDENTIFY_STDOUT size"); fatalx("bad MSG_IDENTIFY_STDOUT size");
c->out_fd = imsg->fd; c->out_fd = imsg_get_fd(imsg);
log_debug("client %p IDENTIFY_STDOUT %d", c, imsg->fd); log_debug("client %p IDENTIFY_STDOUT %d", c, c->out_fd);
break; break;
case MSG_IDENTIFY_ENVIRON: case MSG_IDENTIFY_ENVIRON:
if (datalen == 0 || data[datalen - 1] != '\0') if (datalen == 0 || data[datalen - 1] != '\0')
@@ -3210,3 +3268,69 @@ server_client_remove_pane(struct window_pane *wp)
} }
} }
} }
/* Print to a client. */
void
server_client_print(struct client *c, int parse, struct evbuffer *evb)
{
void *data = EVBUFFER_DATA(evb);
size_t size = EVBUFFER_LENGTH(evb);
struct window_pane *wp;
struct window_mode_entry *wme;
char *sanitized, *msg, *line;
if (!parse) {
utf8_stravisx(&msg, data, size,
VIS_OCTAL|VIS_CSTYLE|VIS_NOSLASH);
log_debug("%s: %s", __func__, msg);
} else {
msg = EVBUFFER_DATA(evb);
if (msg[size - 1] != '\0')
evbuffer_add(evb, "", 1);
}
if (c == NULL)
goto out;
if (c->session == NULL || (c->flags & CLIENT_CONTROL)) {
if (~c->flags & CLIENT_UTF8) {
sanitized = utf8_sanitize(msg);
if (c->flags & CLIENT_CONTROL)
control_write(c, "%s", sanitized);
else
file_print(c, "%s\n", sanitized);
free(sanitized);
} else {
if (c->flags & CLIENT_CONTROL)
control_write(c, "%s", msg);
else
file_print(c, "%s\n", msg);
}
goto out;
}
wp = server_client_get_pane(c);
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL);
if (parse) {
do {
line = evbuffer_readln(evb, NULL, EVBUFFER_EOL_LF);
if (line != NULL) {
window_copy_add(wp, 1, "%s", line);
free(line);
}
} while (line != NULL);
size = EVBUFFER_LENGTH(evb);
if (size != 0) {
line = EVBUFFER_DATA(evb);
window_copy_add(wp, 1, "%.*s", (int)size, line);
}
} else
window_copy_add(wp, 0, "%s", msg);
out:
if (!parse)
free(msg);
}

View File

@@ -27,8 +27,7 @@
#include "tmux.h" #include "tmux.h"
static struct session *server_next_session(struct session *); static void server_destroy_session_group(struct session *);
static void server_destroy_session_group(struct session *);
void void
server_redraw_client(struct client *c) server_redraw_client(struct client *c)
@@ -207,8 +206,8 @@ server_kill_window(struct window *w, int renumber)
if (session_detach(s, wl)) { if (session_detach(s, wl)) {
server_destroy_session_group(s); server_destroy_session_group(s);
break; break;
} else }
server_redraw_session_group(s); server_redraw_session_group(s);
} }
if (renumber) if (renumber)
@@ -385,9 +384,10 @@ server_destroy_session_group(struct session *s)
struct session_group *sg; struct session_group *sg;
struct session *s1; struct session *s1;
if ((sg = session_group_contains(s)) == NULL) if ((sg = session_group_contains(s)) == NULL) {
server_destroy_session(s); server_destroy_session(s);
else { session_destroy(s, 1, __func__);
} else {
TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) { TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
server_destroy_session(s); server_destroy_session(s);
session_destroy(s, 1, __func__); session_destroy(s, 1, __func__);
@@ -396,52 +396,55 @@ server_destroy_session_group(struct session *s)
} }
static struct session * static struct session *
server_next_session(struct session *s) server_find_session(struct session *s,
int (*f)(struct session *, struct session *))
{ {
struct session *s_loop, *s_out = NULL; struct session *s_loop, *s_out = NULL;
RB_FOREACH(s_loop, sessions, &sessions) { RB_FOREACH(s_loop, sessions, &sessions) {
if (s_loop == s) if (s_loop != s && (s_out == NULL || f(s_loop, s_out)))
continue;
if (s_out == NULL ||
timercmp(&s_loop->activity_time, &s_out->activity_time, <))
s_out = s_loop; s_out = s_loop;
} }
return (s_out); return (s_out);
} }
static struct session * static int
server_next_detached_session(struct session *s) server_newer_session(struct session *s_loop, struct session *s_out)
{ {
struct session *s_loop, *s_out = NULL; return (timercmp(&s_loop->activity_time, &s_out->activity_time, <));
}
RB_FOREACH(s_loop, sessions, &sessions) { static int
if (s_loop == s || s_loop->attached) server_newer_detached_session(struct session *s_loop, struct session *s_out)
continue; {
if (s_out == NULL || if (s_loop->attached)
timercmp(&s_loop->activity_time, &s_out->activity_time, <)) return (0);
s_out = s_loop; return (server_newer_session(s_loop, s_out));
}
return (s_out);
} }
void void
server_destroy_session(struct session *s) server_destroy_session(struct session *s)
{ {
struct client *c; struct client *c;
struct session *s_new; struct session *s_new = NULL;
int detach_on_destroy; int detach_on_destroy;
detach_on_destroy = options_get_number(s->options, "detach-on-destroy"); detach_on_destroy = options_get_number(s->options, "detach-on-destroy");
if (detach_on_destroy == 0) if (detach_on_destroy == 0)
s_new = server_next_session(s); s_new = server_find_session(s, server_newer_session);
else if (detach_on_destroy == 2) else if (detach_on_destroy == 2)
s_new = server_next_detached_session(s); s_new = server_find_session(s, server_newer_detached_session);
else else if (detach_on_destroy == 3)
s_new = session_previous_session(s);
else if (detach_on_destroy == 4)
s_new = session_next_session(s);
if (s_new == s)
s_new = NULL; s_new = NULL;
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {
if (c->session != s) if (c->session != s)
continue; continue;
c->session = NULL;
c->last_session = NULL;
server_client_set_session(c, s_new); server_client_set_session(c, s_new);
if (s_new == NULL) if (s_new == NULL)
c->flags |= CLIENT_EXIT; c->flags |= CLIENT_EXIT;
@@ -452,7 +455,8 @@ server_destroy_session(struct session *s)
void void
server_check_unattached(void) server_check_unattached(void)
{ {
struct session *s; struct session *s;
struct session_group *sg;
/* /*
* If any sessions are no longer attached and have destroy-unattached * If any sessions are no longer attached and have destroy-unattached
@@ -461,8 +465,23 @@ server_check_unattached(void)
RB_FOREACH(s, sessions, &sessions) { RB_FOREACH(s, sessions, &sessions) {
if (s->attached != 0) if (s->attached != 0)
continue; continue;
if (options_get_number (s->options, "destroy-unattached")) switch (options_get_number(s->options, "destroy-unattached")) {
session_destroy(s, 1, __func__); case 0: /* off */
continue;
case 1: /* on */
break;
case 2: /* keep-last */
sg = session_group_contains(s);
if (sg == NULL || session_group_count(sg) <= 1)
continue;
break;
case 3: /* keep-group */
sg = session_group_contains(s);
if (sg != NULL && session_group_count(sg) == 1)
continue;
break;
}
session_destroy(s, 1, __func__);
} }
} }

View File

@@ -53,6 +53,8 @@ struct cmd_find_state marked_pane;
static u_int message_next; static u_int message_next;
struct message_list message_log; struct message_list message_log;
time_t current_time;
static int server_loop(void); static int server_loop(void);
static void server_send_exit(void); static void server_send_exit(void);
static void server_accept(int, short, void *); static void server_accept(int, short, void *);
@@ -211,7 +213,6 @@ server_start(struct tmuxproc *client, int flags, struct event_base *base,
RB_INIT(&sessions); RB_INIT(&sessions);
key_bindings_init(); key_bindings_init();
TAILQ_INIT(&message_log); TAILQ_INIT(&message_log);
gettimeofday(&start_time, NULL); gettimeofday(&start_time, NULL);
#ifdef HAVE_SYSTEMD #ifdef HAVE_SYSTEMD
@@ -263,6 +264,8 @@ server_loop(void)
struct client *c; struct client *c;
u_int items; u_int items;
current_time = time (NULL);
do { do {
items = cmdq_next(NULL); items = cmdq_next(NULL);
TAILQ_FOREACH(c, &clients, entry) { TAILQ_FOREACH(c, &clients, entry) {

View File

@@ -365,11 +365,9 @@ session_detach(struct session *s, struct winlink *wl)
session_group_synchronize_from(s); session_group_synchronize_from(s);
if (RB_EMPTY(&s->windows)) { if (RB_EMPTY(&s->windows))
session_destroy(s, 1, __func__);
return (1); return (1);
} return (0);
return (0);
} }
/* Return if session has window. */ /* Return if session has window. */
@@ -687,8 +685,10 @@ session_group_synchronize1(struct session *target, struct session *s)
TAILQ_INIT(&s->lastw); TAILQ_INIT(&s->lastw);
TAILQ_FOREACH(wl, &old_lastw, sentry) { TAILQ_FOREACH(wl, &old_lastw, sentry) {
wl2 = winlink_find_by_index(&s->windows, wl->idx); wl2 = winlink_find_by_index(&s->windows, wl->idx);
if (wl2 != NULL) if (wl2 != NULL) {
TAILQ_INSERT_TAIL(&s->lastw, wl2, sentry); TAILQ_INSERT_TAIL(&s->lastw, wl2, sentry);
wl2->flags |= WINLINK_VISITED;
}
} }
/* Then free the old winlinks list. */ /* Then free the old winlinks list. */
@@ -708,7 +708,7 @@ session_renumber_windows(struct session *s)
struct winlink *wl, *wl1, *wl_new; struct winlink *wl, *wl1, *wl_new;
struct winlinks old_wins; struct winlinks old_wins;
struct winlink_stack old_lastw; struct winlink_stack old_lastw;
int new_idx, new_curw_idx; int new_idx, new_curw_idx, marked_idx = -1;
/* Save and replace old window list. */ /* Save and replace old window list. */
memcpy(&old_wins, &s->windows, sizeof old_wins); memcpy(&old_wins, &s->windows, sizeof old_wins);
@@ -725,6 +725,8 @@ session_renumber_windows(struct session *s)
winlink_set_window(wl_new, wl->window); winlink_set_window(wl_new, wl->window);
wl_new->flags |= wl->flags & WINLINK_ALERTFLAGS; wl_new->flags |= wl->flags & WINLINK_ALERTFLAGS;
if (wl == marked_pane.wl)
marked_idx = wl_new->idx;
if (wl == s->curw) if (wl == s->curw)
new_curw_idx = wl_new->idx; new_curw_idx = wl_new->idx;
@@ -735,12 +737,20 @@ session_renumber_windows(struct session *s)
memcpy(&old_lastw, &s->lastw, sizeof old_lastw); memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
TAILQ_INIT(&s->lastw); TAILQ_INIT(&s->lastw);
TAILQ_FOREACH(wl, &old_lastw, sentry) { TAILQ_FOREACH(wl, &old_lastw, sentry) {
wl->flags &= ~WINLINK_VISITED;
wl_new = winlink_find_by_window(&s->windows, wl->window); wl_new = winlink_find_by_window(&s->windows, wl->window);
if (wl_new != NULL) if (wl_new != NULL) {
TAILQ_INSERT_TAIL(&s->lastw, wl_new, sentry); TAILQ_INSERT_TAIL(&s->lastw, wl_new, sentry);
wl_new->flags |= WINLINK_VISITED;
}
} }
/* Set the current window. */ /* Set the current window. */
if (marked_idx != -1) {
marked_pane.wl = winlink_find_by_index(&s->windows, marked_idx);
if (marked_pane.wl == NULL)
server_clear_marked();
}
s->curw = winlink_find_by_index(&s->windows, new_curw_idx); s->curw = winlink_find_by_index(&s->windows, new_curw_idx);
/* Free the old winlinks (reducing window references too). */ /* Free the old winlinks (reducing window references too). */

19
spawn.c
View File

@@ -113,6 +113,7 @@ spawn_window(struct spawn_context *sc, char **cause)
window_pane_resize(sc->wp0, w->sx, w->sy); window_pane_resize(sc->wp0, w->sx, w->sy);
layout_init(w, sc->wp0); layout_init(w, sc->wp0);
w->active = NULL;
window_set_active_pane(w, sc->wp0, 0); window_set_active_pane(w, sc->wp0, 0);
} }
@@ -380,8 +381,20 @@ spawn_pane(struct spawn_context *sc, char **cause)
} }
/* In the parent process, everything is done now. */ /* In the parent process, everything is done now. */
if (new_wp->pid != 0) if (new_wp->pid != 0) {
#if defined(HAVE_SYSTEMD) && defined(ENABLE_CGROUPS)
/*
* Move the child process into a new cgroup for systemd-oomd
* isolation.
*/
if (systemd_move_pid_to_new_cgroup(new_wp->pid, cause) < 0) {
log_debug("%s: moving pane to new cgroup failed: %s",
__func__, *cause);
free (*cause);
}
#endif
goto complete; goto complete;
}
/* /*
* Child process. Change to the working directory or home if that * Child process. Change to the working directory or home if that
@@ -389,7 +402,7 @@ spawn_pane(struct spawn_context *sc, char **cause)
*/ */
if (chdir(new_wp->cwd) == 0) if (chdir(new_wp->cwd) == 0)
environ_set(child, "PWD", 0, "%s", new_wp->cwd); environ_set(child, "PWD", 0, "%s", new_wp->cwd);
else if ((tmp = find_home()) != NULL || chdir(tmp) == 0) else if ((tmp = find_home()) != NULL && chdir(tmp) == 0)
environ_set(child, "PWD", 0, "%s", tmp); environ_set(child, "PWD", 0, "%s", tmp);
else if (chdir("/") == 0) else if (chdir("/") == 0)
environ_set(child, "PWD", 0, "/"); environ_set(child, "PWD", 0, "/");
@@ -416,8 +429,8 @@ spawn_pane(struct spawn_context *sc, char **cause)
_exit(1); _exit(1);
/* Clean up file descriptors and signals and update the environment. */ /* Clean up file descriptors and signals and update the environment. */
closefrom(STDERR_FILENO + 1);
proc_clear_signals(server_proc, 1); proc_clear_signals(server_proc, 1);
closefrom(STDERR_FILENO + 1);
sigprocmask(SIG_SETMASK, &oldset, NULL); sigprocmask(SIG_SETMASK, &oldset, NULL);
log_close(); log_close();
environ_push(child); environ_push(child);

View File

@@ -263,6 +263,17 @@ status_line_size(struct client *c)
return (s->statuslines); return (s->statuslines);
} }
/* Get the prompt line number for client's session. 1 means at the bottom. */
static u_int
status_prompt_line_at(struct client *c)
{
struct session *s = c->session;
if (c->flags & (CLIENT_STATUSOFF|CLIENT_CONTROL))
return (1);
return (options_get_number(s->options, "message-line"));
}
/* Get window at window list position. */ /* Get window at window list position. */
struct style_range * struct style_range *
status_get_range(struct client *c, u_int x, u_int y) status_get_range(struct client *c, u_int x, u_int y)
@@ -461,17 +472,26 @@ void
status_message_set(struct client *c, int delay, int ignore_styles, status_message_set(struct client *c, int delay, int ignore_styles,
int ignore_keys, const char *fmt, ...) int ignore_keys, const char *fmt, ...)
{ {
struct timeval tv; struct timeval tv;
va_list ap; va_list ap;
char *s;
va_start(ap, fmt);
xvasprintf(&s, fmt, ap);
va_end(ap);
log_debug("%s: %s", __func__, s);
if (c == NULL) {
server_add_message("message: %s", s);
free(s);
return;
}
status_message_clear(c); status_message_clear(c);
status_push_screen(c); status_push_screen(c);
c->message_string = s;
va_start(ap, fmt); server_add_message("%s message: %s", c->name, s);
xvasprintf(&c->message_string, fmt, ap);
va_end(ap);
server_add_message("%s message: %s", c->name, c->message_string);
/* /*
* With delay -1, the display-time option is used; zero means wait for * With delay -1, the display-time option is used; zero means wait for
@@ -533,7 +553,7 @@ status_message_redraw(struct client *c)
struct session *s = c->session; struct session *s = c->session;
struct screen old_screen; struct screen old_screen;
size_t len; size_t len;
u_int lines, offset; u_int lines, offset, messageline;
struct grid_cell gc; struct grid_cell gc;
struct format_tree *ft; struct format_tree *ft;
@@ -546,6 +566,10 @@ status_message_redraw(struct client *c)
lines = 1; lines = 1;
screen_init(sl->active, c->tty.sx, lines, 0); screen_init(sl->active, c->tty.sx, lines, 0);
messageline = status_prompt_line_at(c);
if (messageline > lines - 1)
messageline = lines - 1;
len = screen_write_strlen("%s", c->message_string); len = screen_write_strlen("%s", c->message_string);
if (len > c->tty.sx) if (len > c->tty.sx)
len = c->tty.sx; len = c->tty.sx;
@@ -555,11 +579,11 @@ status_message_redraw(struct client *c)
format_free(ft); format_free(ft);
screen_write_start(&ctx, sl->active); screen_write_start(&ctx, sl->active);
screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1); screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines);
screen_write_cursormove(&ctx, 0, lines - 1, 0); screen_write_cursormove(&ctx, 0, messageline, 0);
for (offset = 0; offset < c->tty.sx; offset++) for (offset = 0; offset < c->tty.sx; offset++)
screen_write_putc(&ctx, &gc, ' '); screen_write_putc(&ctx, &gc, ' ');
screen_write_cursormove(&ctx, 0, lines - 1, 0); screen_write_cursormove(&ctx, 0, messageline, 0);
if (c->message_ignore_styles) if (c->message_ignore_styles)
screen_write_nputs(&ctx, len, &gc, "%s", c->message_string); screen_write_nputs(&ctx, len, &gc, "%s", c->message_string);
else else
@@ -695,7 +719,7 @@ status_prompt_redraw(struct client *c)
struct session *s = c->session; struct session *s = c->session;
struct screen old_screen; struct screen old_screen;
u_int i, lines, offset, left, start, width; u_int i, lines, offset, left, start, width;
u_int pcursor, pwidth; u_int pcursor, pwidth, promptline;
struct grid_cell gc, cursorgc; struct grid_cell gc, cursorgc;
struct format_tree *ft; struct format_tree *ft;
@@ -708,6 +732,10 @@ status_prompt_redraw(struct client *c)
lines = 1; lines = 1;
screen_init(sl->active, c->tty.sx, lines, 0); screen_init(sl->active, c->tty.sx, lines, 0);
promptline = status_prompt_line_at(c);
if (promptline > lines - 1)
promptline = lines - 1;
ft = format_create_defaults(NULL, c, NULL, NULL, NULL); ft = format_create_defaults(NULL, c, NULL, NULL, NULL);
if (c->prompt_mode == PROMPT_COMMAND) if (c->prompt_mode == PROMPT_COMMAND)
style_apply(&gc, s->options, "message-command-style", ft); style_apply(&gc, s->options, "message-command-style", ft);
@@ -723,13 +751,13 @@ status_prompt_redraw(struct client *c)
start = c->tty.sx; start = c->tty.sx;
screen_write_start(&ctx, sl->active); screen_write_start(&ctx, sl->active);
screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1); screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines);
screen_write_cursormove(&ctx, 0, lines - 1, 0); screen_write_cursormove(&ctx, 0, promptline, 0);
for (offset = 0; offset < c->tty.sx; offset++) for (offset = 0; offset < c->tty.sx; offset++)
screen_write_putc(&ctx, &gc, ' '); screen_write_putc(&ctx, &gc, ' ');
screen_write_cursormove(&ctx, 0, lines - 1, 0); screen_write_cursormove(&ctx, 0, promptline, 0);
format_draw(&ctx, &gc, start, c->prompt_string, NULL, 0); format_draw(&ctx, &gc, start, c->prompt_string, NULL, 0);
screen_write_cursormove(&ctx, start, lines - 1, 0); screen_write_cursormove(&ctx, start, promptline, 0);
left = c->tty.sx - start; left = c->tty.sx - start;
if (left == 0) if (left == 0)
@@ -1452,8 +1480,6 @@ process_key:
return (0); return (0);
append_key: append_key:
if (key <= 0x1f || (key >= KEYC_BASE && key < KEYC_BASE_END))
return (0);
if (key <= 0x7f) if (key <= 0x7f)
utf8_set(&tmp, key); utf8_set(&tmp, key);
else if (KEYC_IS_UNICODE(key)) else if (KEYC_IS_UNICODE(key))
@@ -1747,8 +1773,9 @@ status_prompt_complete_list_menu(struct client *c, char **list, u_int size,
else else
offset = 0; offset = 0;
if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset, if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, 0, NULL, offset, py, c,
py, c, NULL, status_prompt_menu_callback, spm) != 0) { BOX_LINES_DEFAULT, NULL, NULL, NULL, NULL,
status_prompt_menu_callback, spm) != 0) {
menu_free(menu); menu_free(menu);
free(spm); free(spm);
return (0); return (0);
@@ -1840,8 +1867,9 @@ status_prompt_complete_window_menu(struct client *c, struct session *s,
else else
offset = 0; offset = 0;
if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset, if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, 0, NULL, offset, py, c,
py, c, NULL, status_prompt_menu_callback, spm) != 0) { BOX_LINES_DEFAULT, NULL, NULL, NULL, NULL,
status_prompt_menu_callback, spm) != 0) {
menu_free(menu); menu_free(menu);
free(spm); free(spm);
return (NULL); return (NULL);

83
style.c
View File

@@ -30,18 +30,25 @@
/* Default style. */ /* Default style. */
static struct style style_default = { static struct style style_default = {
{ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0 }, { { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0 },
0, 0,
8, 8,
STYLE_ALIGN_DEFAULT, STYLE_ALIGN_DEFAULT,
STYLE_LIST_OFF, STYLE_LIST_OFF,
STYLE_RANGE_NONE, 0, STYLE_RANGE_NONE, 0, "",
STYLE_DEFAULT_BASE STYLE_DEFAULT_BASE
}; };
/* Set range string. */
static void
style_set_range_string(struct style *sy, const char *s)
{
strlcpy(sy->range_string, s, sizeof sy->range_string);
}
/* /*
* Parse an embedded style of the form "fg=colour,bg=colour,bright,...". Note * Parse an embedded style of the form "fg=colour,bg=colour,bright,...". Note
* that this adds onto the given style, so it must have been initialized * that this adds onto the given style, so it must have been initialized
@@ -77,6 +84,7 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
if (strcasecmp(tmp, "default") == 0) { if (strcasecmp(tmp, "default") == 0) {
sy->gc.fg = base->fg; sy->gc.fg = base->fg;
sy->gc.bg = base->bg; sy->gc.bg = base->bg;
sy->gc.us = base->us;
sy->gc.attr = base->attr; sy->gc.attr = base->attr;
sy->gc.flags = base->flags; sy->gc.flags = base->flags;
} else if (strcasecmp(tmp, "ignore") == 0) } else if (strcasecmp(tmp, "ignore") == 0)
@@ -103,32 +111,67 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
} else if (strcasecmp(tmp, "norange") == 0) { } else if (strcasecmp(tmp, "norange") == 0) {
sy->range_type = style_default.range_type; sy->range_type = style_default.range_type;
sy->range_argument = style_default.range_type; sy->range_argument = style_default.range_type;
strlcpy(sy->range_string, style_default.range_string,
sizeof sy->range_string);
} else if (end > 6 && strncasecmp(tmp, "range=", 6) == 0) { } else if (end > 6 && strncasecmp(tmp, "range=", 6) == 0) {
found = strchr(tmp + 6, '|'); found = strchr(tmp + 6, '|');
if (found != NULL) { if (found != NULL) {
*found++ = '\0'; *found++ = '\0';
if (*found == '\0') if (*found == '\0')
goto error; goto error;
for (cp = found; *cp != '\0'; cp++) {
if (!isdigit((u_char)*cp))
goto error;
}
} }
if (strcasecmp(tmp + 6, "left") == 0) { if (strcasecmp(tmp + 6, "left") == 0) {
if (found != NULL) if (found != NULL)
goto error; goto error;
sy->range_type = STYLE_RANGE_LEFT; sy->range_type = STYLE_RANGE_LEFT;
sy->range_argument = 0; sy->range_argument = 0;
style_set_range_string(sy, "");
} else if (strcasecmp(tmp + 6, "right") == 0) { } else if (strcasecmp(tmp + 6, "right") == 0) {
if (found != NULL) if (found != NULL)
goto error; goto error;
sy->range_type = STYLE_RANGE_RIGHT; sy->range_type = STYLE_RANGE_RIGHT;
sy->range_argument = 0; sy->range_argument = 0;
style_set_range_string(sy, "");
} else if (strcasecmp(tmp + 6, "pane") == 0) {
if (found == NULL)
goto error;
if (*found != '%' || found[1] == '\0')
goto error;
for (cp = found + 1; *cp != '\0'; cp++) {
if (!isdigit((u_char)*cp))
goto error;
}
sy->range_type = STYLE_RANGE_PANE;
sy->range_argument = atoi(found + 1);
style_set_range_string(sy, "");
} else if (strcasecmp(tmp + 6, "window") == 0) { } else if (strcasecmp(tmp + 6, "window") == 0) {
if (found == NULL) if (found == NULL)
goto error; goto error;
for (cp = found; *cp != '\0'; cp++) {
if (!isdigit((u_char)*cp))
goto error;
}
sy->range_type = STYLE_RANGE_WINDOW; sy->range_type = STYLE_RANGE_WINDOW;
sy->range_argument = atoi(found); sy->range_argument = atoi(found);
style_set_range_string(sy, "");
} else if (strcasecmp(tmp + 6, "session") == 0) {
if (found == NULL)
goto error;
if (*found != '$' || found[1] == '\0')
goto error;
for (cp = found + 1; *cp != '\0'; cp++) {
if (!isdigit((u_char)*cp))
goto error;
}
sy->range_type = STYLE_RANGE_SESSION;
sy->range_argument = atoi(found + 1);
style_set_range_string(sy, "");
} else if (strcasecmp(tmp + 6, "user") == 0) {
if (found == NULL)
goto error;
sy->range_type = STYLE_RANGE_USER;
sy->range_argument = 0;
style_set_range_string(sy, found);
} }
} else if (strcasecmp(tmp, "noalign") == 0) } else if (strcasecmp(tmp, "noalign") == 0)
sy->align = style_default.align; sy->align = style_default.align;
@@ -162,6 +205,13 @@ style_parse(struct style *sy, const struct grid_cell *base, const char *in)
sy->gc.bg = base->bg; sy->gc.bg = base->bg;
} else } else
goto error; goto error;
} else if (end > 3 && strncasecmp(tmp, "us=", 3) == 0) {
if ((value = colour_fromstring(tmp + 3)) == -1)
goto error;
if (value != 8)
sy->gc.us = value;
else
sy->gc.us = base->us;
} else if (strcasecmp(tmp, "none") == 0) } else if (strcasecmp(tmp, "none") == 0)
sy->gc.attr = 0; sy->gc.attr = 0;
else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) { else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) {
@@ -192,7 +242,7 @@ style_tostring(struct style *sy)
int off = 0; int off = 0;
const char *comma = "", *tmp = ""; const char *comma = "", *tmp = "";
static char s[256]; static char s[256];
char b[16]; char b[21];
*s = '\0'; *s = '\0';
@@ -214,9 +264,19 @@ style_tostring(struct style *sy)
tmp = "left"; tmp = "left";
else if (sy->range_type == STYLE_RANGE_RIGHT) else if (sy->range_type == STYLE_RANGE_RIGHT)
tmp = "right"; tmp = "right";
else if (sy->range_type == STYLE_RANGE_WINDOW) { else if (sy->range_type == STYLE_RANGE_PANE) {
snprintf(b, sizeof b, "pane|%%%u", sy->range_argument);
tmp = b;
} else if (sy->range_type == STYLE_RANGE_WINDOW) {
snprintf(b, sizeof b, "window|%u", sy->range_argument); snprintf(b, sizeof b, "window|%u", sy->range_argument);
tmp = b; tmp = b;
} else if (sy->range_type == STYLE_RANGE_SESSION) {
snprintf(b, sizeof b, "session|$%u",
sy->range_argument);
tmp = b;
} else if (sy->range_type == STYLE_RANGE_USER) {
snprintf(b, sizeof b, "user|%s", sy->range_string);
tmp = b;
} }
off += xsnprintf(s + off, sizeof s - off, "%srange=%s", comma, off += xsnprintf(s + off, sizeof s - off, "%srange=%s", comma,
tmp); tmp);
@@ -258,6 +318,11 @@ style_tostring(struct style *sy)
colour_tostring(gc->bg)); colour_tostring(gc->bg));
comma = ","; comma = ",";
} }
if (gc->us != 8) {
off += xsnprintf(s + off, sizeof s - off, "%sus=%s", comma,
colour_tostring(gc->us));
comma = ",";
}
if (gc->attr != 0) { if (gc->attr != 0) {
xsnprintf(s + off, sizeof s - off, "%s%s", comma, xsnprintf(s + off, sizeof s - off, "%s%s", comma,
attributes_tostring(gc->attr)); attributes_tostring(gc->attr));
@@ -287,6 +352,8 @@ style_add(struct grid_cell *gc, struct options *oo, const char *name,
gc->fg = sy->gc.fg; gc->fg = sy->gc.fg;
if (sy->gc.bg != 8) if (sy->gc.bg != 8)
gc->bg = sy->gc.bg; gc->bg = sy->gc.bg;
if (sy->gc.us != 8)
gc->us = sy->gc.us;
gc->attr |= sy->gc.attr; gc->attr |= sy->gc.attr;
if (ft0 != NULL) if (ft0 != NULL)

View File

@@ -66,7 +66,8 @@ enum msgtype {
MSG_WRITE_OPEN, MSG_WRITE_OPEN,
MSG_WRITE, MSG_WRITE,
MSG_WRITE_READY, MSG_WRITE_READY,
MSG_WRITE_CLOSE MSG_WRITE_CLOSE,
MSG_READ_CANCEL
}; };
/* /*
@@ -92,6 +93,10 @@ struct msg_read_done {
int error; int error;
}; };
struct msg_read_cancel {
int stream;
};
struct msg_write_open { struct msg_write_open {
int stream; int stream;
int fd; int fd;

858
tmux.1

File diff suppressed because it is too large Load Diff

4
tmux.c
View File

@@ -53,7 +53,7 @@ static __dead void
usage(void) usage(void)
{ {
fprintf(stderr, fprintf(stderr,
"usage: %s [-2CDlNuvV] [-c shell-command] [-f file] [-L socket-name]\n" "usage: %s [-2CDlNuVv] [-c shell-command] [-f file] [-L socket-name]\n"
" [-S socket-path] [-T features] [command [flags]]\n", " [-S socket-path] [-T features] [command [flags]]\n",
getprogname()); getprogname());
exit(1); exit(1);
@@ -391,7 +391,7 @@ main(int argc, char **argv)
cfg_quiet = 0; cfg_quiet = 0;
break; break;
case 'V': case 'V':
printf("%s %s\n", getprogname(), getversion()); printf("tmux %s\n", getversion());
exit(0); exit(0);
case 'l': case 'l':
flags |= CLIENT_LOGIN; flags |= CLIENT_LOGIN;

312
tmux.h
View File

@@ -26,6 +26,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <termios.h> #include <termios.h>
#include <wchar.h>
#ifdef HAVE_UTEMPTER #ifdef HAVE_UTEMPTER
#include <utempter.h> #include <utempter.h>
@@ -50,6 +51,8 @@ struct control_state;
struct environ; struct environ;
struct format_job_tree; struct format_job_tree;
struct format_tree; struct format_tree;
struct hyperlinks_uri;
struct hyperlinks;
struct input_ctx; struct input_ctx;
struct job; struct job;
struct menu_data; struct menu_data;
@@ -62,6 +65,11 @@ struct screen_write_citem;
struct screen_write_cline; struct screen_write_cline;
struct screen_write_ctx; struct screen_write_ctx;
struct session; struct session;
#ifdef ENABLE_SIXEL
struct sixel_image;
#endif
struct tty_ctx; struct tty_ctx;
struct tty_code; struct tty_code;
struct tty_key; struct tty_key;
@@ -79,6 +87,9 @@ struct winlink;
#ifndef TMUX_TERM #ifndef TMUX_TERM
#define TMUX_TERM "screen" #define TMUX_TERM "screen"
#endif #endif
#ifndef TMUX_LOCK_CMD
#define TMUX_LOCK_CMD "lock -np"
#endif
/* Minimum layout cell size, NOT including border lines. */ /* Minimum layout cell size, NOT including border lines. */
#define PANE_MINIMUM 1 #define PANE_MINIMUM 1
@@ -130,12 +141,14 @@ struct winlink;
#define KEYC_SHIFT 0x00400000000000ULL #define KEYC_SHIFT 0x00400000000000ULL
/* Key flag bits. */ /* Key flag bits. */
#define KEYC_LITERAL 0x01000000000000ULL #define KEYC_LITERAL 0x01000000000000ULL
#define KEYC_KEYPAD 0x02000000000000ULL #define KEYC_KEYPAD 0x02000000000000ULL
#define KEYC_CURSOR 0x04000000000000ULL #define KEYC_CURSOR 0x04000000000000ULL
#define KEYC_IMPLIED_META 0x08000000000000ULL #define KEYC_IMPLIED_META 0x08000000000000ULL
#define KEYC_BUILD_MODIFIERS 0x10000000000000ULL #define KEYC_BUILD_MODIFIERS 0x10000000000000ULL
#define KEYC_VI 0x20000000000000ULL #define KEYC_VI 0x20000000000000ULL
#define KEYC_EXTENDED 0x40000000000000ULL
#define KEYC_SENT 0x80000000000000ULL
/* Masks for key bits. */ /* Masks for key bits. */
#define KEYC_MASK_MODIFIERS 0x00f00000000000ULL #define KEYC_MASK_MODIFIERS 0x00f00000000000ULL
@@ -154,7 +167,9 @@ struct winlink;
#define KEYC_IS_UNICODE(key) \ #define KEYC_IS_UNICODE(key) \
(((key) & KEYC_MASK_KEY) > 0x7f && \ (((key) & KEYC_MASK_KEY) > 0x7f && \
(((key) & KEYC_MASK_KEY) < KEYC_BASE || \ (((key) & KEYC_MASK_KEY) < KEYC_BASE || \
((key) & KEYC_MASK_KEY) >= KEYC_BASE_END)) ((key) & KEYC_MASK_KEY) >= KEYC_BASE_END) && \
(((key) & KEYC_MASK_KEY) < KEYC_USER || \
((key) & KEYC_MASK_KEY) >= KEYC_USER + KEYC_NUSER))
/* Multiple click timeout. */ /* Multiple click timeout. */
#define KEYC_CLICK_TIMEOUT 300 #define KEYC_CLICK_TIMEOUT 300
@@ -366,6 +381,7 @@ enum tty_code_code {
TTYC_ENFCS, TTYC_ENFCS,
TTYC_ENMG, TTYC_ENMG,
TTYC_FSL, TTYC_FSL,
TTYC_HLS,
TTYC_HOME, TTYC_HOME,
TTYC_HPA, TTYC_HPA,
TTYC_ICH, TTYC_ICH,
@@ -512,6 +528,7 @@ enum tty_code_code {
TTYC_KUP6, TTYC_KUP6,
TTYC_KUP7, TTYC_KUP7,
TTYC_MS, TTYC_MS,
TTYC_NOBR,
TTYC_OL, TTYC_OL,
TTYC_OP, TTYC_OP,
TTYC_RECT, TTYC_RECT,
@@ -529,6 +546,7 @@ enum tty_code_code {
TTYC_SETRGBB, TTYC_SETRGBB,
TTYC_SETRGBF, TTYC_SETRGBF,
TTYC_SETULC, TTYC_SETULC,
TTYC_SETULC1,
TTYC_SGR0, TTYC_SGR0,
TTYC_SITM, TTYC_SITM,
TTYC_SMACS, TTYC_SMACS,
@@ -539,6 +557,7 @@ enum tty_code_code {
TTYC_SMUL, TTYC_SMUL,
TTYC_SMULX, TTYC_SMULX,
TTYC_SMXX, TTYC_SMXX,
TTYC_SXL,
TTYC_SS, TTYC_SS,
TTYC_SWD, TTYC_SWD,
TTYC_SYNC, TTYC_SYNC,
@@ -663,7 +682,17 @@ struct colour_palette {
#define GRID_LINE_WRAPPED 0x1 #define GRID_LINE_WRAPPED 0x1
#define GRID_LINE_EXTENDED 0x2 #define GRID_LINE_EXTENDED 0x2
#define GRID_LINE_DEAD 0x4 #define GRID_LINE_DEAD 0x4
#define GRID_LINE_START_PROMPT 0x8
#define GRID_LINE_START_OUTPUT 0x10
/* Grid string flags. */
#define GRID_STRING_WITH_SEQUENCES 0x1
#define GRID_STRING_ESCAPE_SEQUENCES 0x2
#define GRID_STRING_TRIM_SPACES 0x4
#define GRID_STRING_USED_ONLY 0x8
#define GRID_STRING_EMPTY_CELLS 0x10
/* Cell positions. */
#define CELL_INSIDE 0 #define CELL_INSIDE 0
#define CELL_TOPBOTTOM 1 #define CELL_TOPBOTTOM 1
#define CELL_LEFTRIGHT 2 #define CELL_LEFTRIGHT 2
@@ -678,6 +707,7 @@ struct colour_palette {
#define CELL_JOIN 11 #define CELL_JOIN 11
#define CELL_OUTSIDE 12 #define CELL_OUTSIDE 12
/* Cell borders. */
#define CELL_BORDERS " xqlkmjwvtun~" #define CELL_BORDERS " xqlkmjwvtun~"
#define SIMPLE_BORDERS " |-+++++++++." #define SIMPLE_BORDERS " |-+++++++++."
#define PADDED_BORDERS " " #define PADDED_BORDERS " "
@@ -690,6 +720,7 @@ struct grid_cell {
int fg; int fg;
int bg; int bg;
int us; int us;
u_int link;
}; };
/* Grid extended cell entry. */ /* Grid extended cell entry. */
@@ -700,11 +731,11 @@ struct grid_extd_entry {
int fg; int fg;
int bg; int bg;
int us; int us;
u_int link;
} __packed; } __packed;
/* Grid cell entry. */ /* Grid cell entry. */
struct grid_cell_entry { struct grid_cell_entry {
u_char flags;
union { union {
u_int offset; u_int offset;
struct { struct {
@@ -714,6 +745,7 @@ struct grid_cell_entry {
u_char data; u_char data;
} data; } data;
}; };
u_char flags;
} __packed; } __packed;
/* Grid line. */ /* Grid line. */
@@ -726,6 +758,7 @@ struct grid_line {
u_int extdsize; u_int extdsize;
int flags; int flags;
time_t time;
}; };
/* Entire grid of cells. */ /* Entire grid of cells. */
@@ -773,11 +806,15 @@ enum style_range_type {
STYLE_RANGE_NONE, STYLE_RANGE_NONE,
STYLE_RANGE_LEFT, STYLE_RANGE_LEFT,
STYLE_RANGE_RIGHT, STYLE_RANGE_RIGHT,
STYLE_RANGE_WINDOW STYLE_RANGE_PANE,
STYLE_RANGE_WINDOW,
STYLE_RANGE_SESSION,
STYLE_RANGE_USER
}; };
struct style_range { struct style_range {
enum style_range_type type; enum style_range_type type;
u_int argument; u_int argument;
char string[16];
u_int start; u_int start;
u_int end; /* not included */ u_int end; /* not included */
@@ -804,10 +841,29 @@ struct style {
enum style_range_type range_type; enum style_range_type range_type;
u_int range_argument; u_int range_argument;
char range_string[16];
enum style_default_type default_type; enum style_default_type default_type;
}; };
#ifdef ENABLE_SIXEL
/* Image. */
struct image {
struct screen *s;
struct sixel_image *data;
char *fallback;
u_int px;
u_int py;
u_int sx;
u_int sy;
TAILQ_ENTRY (image) all_entry;
TAILQ_ENTRY (image) entry;
};
TAILQ_HEAD(images, image);
#endif
/* Cursor style. */ /* Cursor style. */
enum screen_cursor_style { enum screen_cursor_style {
SCREEN_CURSOR_DEFAULT, SCREEN_CURSOR_DEFAULT,
@@ -849,7 +905,13 @@ struct screen {
bitstr_t *tabs; bitstr_t *tabs;
struct screen_sel *sel; struct screen_sel *sel;
#ifdef ENABLE_SIXEL
struct images images;
#endif
struct screen_write_cline *write_list; struct screen_write_cline *write_list;
struct hyperlinks *hyperlinks;
}; };
/* Screen write context. */ /* Screen write context. */
@@ -861,7 +923,6 @@ struct screen_write_ctx {
int flags; int flags;
#define SCREEN_WRITE_SYNC 0x1 #define SCREEN_WRITE_SYNC 0x1
#define SCREEN_WRITE_ZWJ 0x2
screen_write_init_ctx_cb init_ctx_cb; screen_write_init_ctx_cb init_ctx_cb;
void *arg; void *arg;
@@ -1015,7 +1076,7 @@ struct window_pane {
#define PANE_REDRAW 0x1 #define PANE_REDRAW 0x1
#define PANE_DROP 0x2 #define PANE_DROP 0x2
#define PANE_FOCUSED 0x4 #define PANE_FOCUSED 0x4
/* 0x8 unused */ #define PANE_VISITED 0x8
/* 0x10 unused */ /* 0x10 unused */
/* 0x20 unused */ /* 0x20 unused */
#define PANE_INPUTOFF 0x40 #define PANE_INPUTOFF 0x40
@@ -1025,6 +1086,7 @@ struct window_pane {
#define PANE_STATUSDRAWN 0x400 #define PANE_STATUSDRAWN 0x400
#define PANE_EMPTY 0x800 #define PANE_EMPTY 0x800
#define PANE_STYLECHANGED 0x1000 #define PANE_STYLECHANGED 0x1000
#define PANE_UNSEENCHANGES 0x2000
int argc; int argc;
char **argv; char **argv;
@@ -1069,7 +1131,8 @@ struct window_pane {
int border_gc_set; int border_gc_set;
struct grid_cell border_gc; struct grid_cell border_gc;
TAILQ_ENTRY(window_pane) entry; TAILQ_ENTRY(window_pane) entry; /* link in list of all panes */
TAILQ_ENTRY(window_pane) sentry; /* link in list of last visited */
RB_ENTRY(window_pane) tree_entry; RB_ENTRY(window_pane) tree_entry;
}; };
TAILQ_HEAD(window_panes, window_pane); TAILQ_HEAD(window_panes, window_pane);
@@ -1090,7 +1153,7 @@ struct window {
struct timeval activity_time; struct timeval activity_time;
struct window_pane *active; struct window_pane *active;
struct window_pane *last; struct window_panes last_panes;
struct window_panes panes; struct window_panes panes;
int lastlayout; int lastlayout;
@@ -1143,6 +1206,7 @@ struct winlink {
#define WINLINK_ACTIVITY 0x2 #define WINLINK_ACTIVITY 0x2
#define WINLINK_SILENCE 0x4 #define WINLINK_SILENCE 0x4
#define WINLINK_ALERTFLAGS (WINLINK_BELL|WINLINK_ACTIVITY|WINLINK_SILENCE) #define WINLINK_ALERTFLAGS (WINLINK_BELL|WINLINK_ACTIVITY|WINLINK_SILENCE)
#define WINLINK_VISITED 0x8
RB_ENTRY(winlink) entry; RB_ENTRY(winlink) entry;
TAILQ_ENTRY(winlink) wentry; TAILQ_ENTRY(winlink) wentry;
@@ -1331,6 +1395,7 @@ struct tty_term {
#define TERM_DECFRA 0x8 #define TERM_DECFRA 0x8
#define TERM_RGBCOLOURS 0x10 #define TERM_RGBCOLOURS 0x10
#define TERM_VT100LIKE 0x20 #define TERM_VT100LIKE 0x20
#define TERM_SIXEL 0x40
int flags; int flags;
LIST_ENTRY(tty_term) entry; LIST_ENTRY(tty_term) entry;
@@ -1342,9 +1407,11 @@ struct tty {
struct client *client; struct client *client;
struct event start_timer; struct event start_timer;
struct event clipboard_timer; struct event clipboard_timer;
time_t last_requests;
u_int sx; u_int sx;
u_int sy; u_int sy;
/* Cell size in pixels. */
u_int xpixel; u_int xpixel;
u_int ypixel; u_int ypixel;
@@ -1353,6 +1420,8 @@ struct tty {
enum screen_cursor_style cstyle; enum screen_cursor_style cstyle;
int ccolour; int ccolour;
/* Properties of the area being drawn on. */
/* When true, the drawing area is bigger than the terminal. */
int oflag; int oflag;
u_int oox; u_int oox;
u_int ooy; u_int ooy;
@@ -1360,6 +1429,8 @@ struct tty {
u_int osy; u_int osy;
int mode; int mode;
int fg;
int bg;
u_int rlower; u_int rlower;
u_int rupper; u_int rupper;
@@ -1387,9 +1458,12 @@ struct tty {
#define TTY_OPENED 0x20 #define TTY_OPENED 0x20
#define TTY_OSC52QUERY 0x40 #define TTY_OSC52QUERY 0x40
#define TTY_BLOCK 0x80 #define TTY_BLOCK 0x80
#define TTY_HAVEDA 0x100 #define TTY_HAVEDA 0x100 /* Primary DA. */
#define TTY_HAVEXDA 0x200 #define TTY_HAVEXDA 0x200
#define TTY_SYNCING 0x400 #define TTY_SYNCING 0x400
#define TTY_HAVEDA2 0x800 /* Secondary DA. */
#define TTY_ALL_REQUEST_FLAGS \
(TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)
int flags; int flags;
struct tty_term *term; struct tty_term *term;
@@ -1422,39 +1496,47 @@ struct tty_ctx {
u_int num; u_int num;
void *ptr; void *ptr;
void *ptr2;
/*
* Whether this command should be sent even when the pane is not
* visible (used for a passthrough sequence when allow-passthrough is
* "all").
*/
int allow_invisible_panes;
/* /*
* Cursor and region position before the screen was updated - this is * Cursor and region position before the screen was updated - this is
* where the command should be applied; the values in the screen have * where the command should be applied; the values in the screen have
* already been updated. * already been updated.
*/ */
u_int ocx; u_int ocx;
u_int ocy; u_int ocy;
u_int orupper; u_int orupper;
u_int orlower; u_int orlower;
/* Target region (usually pane) offset and size. */ /* Target region (usually pane) offset and size. */
u_int xoff; u_int xoff;
u_int yoff; u_int yoff;
u_int rxoff; u_int rxoff;
u_int ryoff; u_int ryoff;
u_int sx; u_int sx;
u_int sy; u_int sy;
/* The background colour used for clearing (erasing). */ /* The background colour used for clearing (erasing). */
u_int bg; u_int bg;
/* The default colours and palette. */ /* The default colours and palette. */
struct grid_cell defaults; struct grid_cell defaults;
struct colour_palette *palette; struct colour_palette *palette;
/* Containing region (usually window) offset and size. */ /* Containing region (usually window) offset and size. */
int bigger; int bigger;
u_int wox; u_int wox;
u_int woy; u_int woy;
u_int wsx; u_int wsx;
u_int wsy; u_int wsy;
}; };
/* Saved message entry. */ /* Saved message entry. */
@@ -1776,6 +1858,7 @@ struct client {
#define CLIENT_CONTROL_WAITEXIT 0x200000000ULL #define CLIENT_CONTROL_WAITEXIT 0x200000000ULL
#define CLIENT_WINDOWSIZECHANGED 0x400000000ULL #define CLIENT_WINDOWSIZECHANGED 0x400000000ULL
#define CLIENT_CLIPBOARDBUFFER 0x800000000ULL #define CLIENT_CLIPBOARDBUFFER 0x800000000ULL
#define CLIENT_BRACKETPASTING 0x1000000000ULL
#define CLIENT_ALLREDRAWFLAGS \ #define CLIENT_ALLREDRAWFLAGS \
(CLIENT_REDRAWWINDOW| \ (CLIENT_REDRAWWINDOW| \
CLIENT_REDRAWSTATUS| \ CLIENT_REDRAWSTATUS| \
@@ -2043,10 +2126,11 @@ extern char **cfg_files;
extern u_int cfg_nfiles; extern u_int cfg_nfiles;
extern int cfg_quiet; extern int cfg_quiet;
void start_cfg(void); void start_cfg(void);
int load_cfg(const char *, struct client *, struct cmdq_item *, int, int load_cfg(const char *, struct client *, struct cmdq_item *,
struct cmdq_item **); struct cmd_find_state *, int, struct cmdq_item **);
int load_cfg_from_buffer(const void *, size_t, const char *, int load_cfg_from_buffer(const void *, size_t, const char *,
struct client *, struct cmdq_item *, int, struct cmdq_item **); struct client *, struct cmdq_item *, struct cmd_find_state *,
int, struct cmdq_item **);
void printflike(1, 2) cfg_add_cause(const char *, ...); void printflike(1, 2) cfg_add_cause(const char *, ...);
void cfg_print_causes(struct cmdq_item *); void cfg_print_causes(struct cmdq_item *);
void cfg_show_causes(struct session *); void cfg_show_causes(struct session *);
@@ -2058,6 +2142,7 @@ u_int paste_buffer_order(struct paste_buffer *);
time_t paste_buffer_created(struct paste_buffer *); time_t paste_buffer_created(struct paste_buffer *);
const char *paste_buffer_data(struct paste_buffer *, size_t *); const char *paste_buffer_data(struct paste_buffer *, size_t *);
struct paste_buffer *paste_walk(struct paste_buffer *); struct paste_buffer *paste_walk(struct paste_buffer *);
int paste_is_empty(void);
struct paste_buffer *paste_get_top(const char **); struct paste_buffer *paste_get_top(const char **);
struct paste_buffer *paste_get_name(const char *); struct paste_buffer *paste_get_name(const char *);
void paste_free(struct paste_buffer *); void paste_free(struct paste_buffer *);
@@ -2094,6 +2179,7 @@ void format_add_cb(struct format_tree *, const char *, format_cb);
void format_log_debug(struct format_tree *, const char *); void format_log_debug(struct format_tree *, const char *);
void format_each(struct format_tree *, void (*)(const char *, void format_each(struct format_tree *, void (*)(const char *,
const char *, void *), void *); const char *, void *), void *);
char *format_pretty_time(time_t, int);
char *format_expand_time(struct format_tree *, const char *); char *format_expand_time(struct format_tree *, const char *);
char *format_expand(struct format_tree *, const char *); char *format_expand(struct format_tree *, const char *);
char *format_single(struct cmdq_item *, const char *, char *format_single(struct cmdq_item *, const char *,
@@ -2116,6 +2202,8 @@ void format_defaults_paste_buffer(struct format_tree *,
struct paste_buffer *); struct paste_buffer *);
void format_lost_client(struct client *); void format_lost_client(struct client *);
char *format_grid_word(struct grid *, u_int, u_int); char *format_grid_word(struct grid *, u_int, u_int);
char *format_grid_hyperlink(struct grid *, u_int, u_int,
struct screen *);
char *format_grid_line(struct grid *, u_int); char *format_grid_line(struct grid *, u_int);
/* format-draw.c */ /* format-draw.c */
@@ -2134,6 +2222,7 @@ void notify_winlink(const char *, struct winlink *);
void notify_session_window(const char *, struct session *, struct window *); void notify_session_window(const char *, struct session *, struct window *);
void notify_window(const char *, struct window *); void notify_window(const char *, struct window *);
void notify_pane(const char *, struct window_pane *); void notify_pane(const char *, struct window_pane *);
void notify_paste_buffer(const char *, int);
/* options.c */ /* options.c */
struct options *options_create(struct options *); struct options *options_create(struct options *);
@@ -2243,42 +2332,50 @@ void tty_update_window_offset(struct window *);
void tty_update_client_offset(struct client *); void tty_update_client_offset(struct client *);
void tty_raw(struct tty *, const char *); void tty_raw(struct tty *, const char *);
void tty_attributes(struct tty *, const struct grid_cell *, void tty_attributes(struct tty *, const struct grid_cell *,
const struct grid_cell *, struct colour_palette *); const struct grid_cell *, struct colour_palette *,
struct hyperlinks *);
void tty_reset(struct tty *); void tty_reset(struct tty *);
void tty_region_off(struct tty *); void tty_region_off(struct tty *);
void tty_margin_off(struct tty *); void tty_margin_off(struct tty *);
void tty_cursor(struct tty *, u_int, u_int); void tty_cursor(struct tty *, u_int, u_int);
void tty_clipboard_query(struct tty *); void tty_clipboard_query(struct tty *);
void tty_putcode(struct tty *, enum tty_code_code); void tty_putcode(struct tty *, enum tty_code_code);
void tty_putcode1(struct tty *, enum tty_code_code, int); void tty_putcode_i(struct tty *, enum tty_code_code, int);
void tty_putcode2(struct tty *, enum tty_code_code, int, int); void tty_putcode_ii(struct tty *, enum tty_code_code, int, int);
void tty_putcode3(struct tty *, enum tty_code_code, int, int, int); void tty_putcode_iii(struct tty *, enum tty_code_code, int, int, int);
void tty_putcode_ptr1(struct tty *, enum tty_code_code, const void *); void tty_putcode_s(struct tty *, enum tty_code_code, const char *);
void tty_putcode_ptr2(struct tty *, enum tty_code_code, const void *, void tty_putcode_ss(struct tty *, enum tty_code_code, const char *,
const void *); const char *);
void tty_puts(struct tty *, const char *); void tty_puts(struct tty *, const char *);
void tty_putc(struct tty *, u_char); void tty_putc(struct tty *, u_char);
void tty_putn(struct tty *, const void *, size_t, u_int); void tty_putn(struct tty *, const void *, size_t, u_int);
void tty_cell(struct tty *, const struct grid_cell *, void tty_cell(struct tty *, const struct grid_cell *,
const struct grid_cell *, struct colour_palette *); const struct grid_cell *, struct colour_palette *,
struct hyperlinks *);
int tty_init(struct tty *, struct client *); int tty_init(struct tty *, struct client *);
void tty_resize(struct tty *); void tty_resize(struct tty *);
void tty_set_size(struct tty *, u_int, u_int, u_int, u_int); void tty_set_size(struct tty *, u_int, u_int, u_int, u_int);
void tty_start_tty(struct tty *); void tty_start_tty(struct tty *);
void tty_send_requests(struct tty *); void tty_send_requests(struct tty *);
void tty_repeat_requests(struct tty *);
void tty_stop_tty(struct tty *); void tty_stop_tty(struct tty *);
void tty_set_title(struct tty *, const char *); void tty_set_title(struct tty *, const char *);
void tty_set_path(struct tty *, const char *); void tty_set_path(struct tty *, const char *);
void tty_update_mode(struct tty *, int, struct screen *); void tty_update_mode(struct tty *, int, struct screen *);
void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int, void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int,
u_int, u_int, const struct grid_cell *, struct colour_palette *); u_int, u_int, const struct grid_cell *, struct colour_palette *);
#ifdef ENABLE_SIXEL
void tty_draw_images(struct client *, struct window_pane *, struct screen *);
#endif
void tty_sync_start(struct tty *); void tty_sync_start(struct tty *);
void tty_sync_end(struct tty *); void tty_sync_end(struct tty *);
int tty_open(struct tty *, char **); int tty_open(struct tty *, char **);
void tty_close(struct tty *); void tty_close(struct tty *);
void tty_free(struct tty *); void tty_free(struct tty *);
void tty_update_features(struct tty *); void tty_update_features(struct tty *);
void tty_set_selection(struct tty *, const char *, size_t); void tty_set_selection(struct tty *, const char *, const char *, size_t);
void tty_write(void (*)(struct tty *, const struct tty_ctx *), void tty_write(void (*)(struct tty *, const struct tty_ctx *),
struct tty_ctx *); struct tty_ctx *);
void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *);
@@ -2302,6 +2399,11 @@ void tty_cmd_scrolldown(struct tty *, const struct tty_ctx *);
void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *);
void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_setselection(struct tty *, const struct tty_ctx *);
void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *);
#ifdef ENABLE_SIXEL
void tty_cmd_sixelimage(struct tty *, const struct tty_ctx *);
#endif
void tty_cmd_syncstart(struct tty *, const struct tty_ctx *); void tty_cmd_syncstart(struct tty *, const struct tty_ctx *);
void tty_default_colours(struct grid_cell *, struct window_pane *); void tty_default_colours(struct grid_cell *, struct window_pane *);
@@ -2318,15 +2420,15 @@ int tty_term_read_list(const char *, int, char ***, u_int *,
void tty_term_free_list(char **, u_int); void tty_term_free_list(char **, u_int);
int tty_term_has(struct tty_term *, enum tty_code_code); int tty_term_has(struct tty_term *, enum tty_code_code);
const char *tty_term_string(struct tty_term *, enum tty_code_code); const char *tty_term_string(struct tty_term *, enum tty_code_code);
const char *tty_term_string1(struct tty_term *, enum tty_code_code, int); const char *tty_term_string_i(struct tty_term *, enum tty_code_code, int);
const char *tty_term_string2(struct tty_term *, enum tty_code_code, int, const char *tty_term_string_ii(struct tty_term *, enum tty_code_code, int,
int); int);
const char *tty_term_string3(struct tty_term *, enum tty_code_code, int, const char *tty_term_string_iii(struct tty_term *, enum tty_code_code, int,
int, int); int, int);
const char *tty_term_ptr1(struct tty_term *, enum tty_code_code, const char *tty_term_string_s(struct tty_term *, enum tty_code_code,
const void *); const char *);
const char *tty_term_ptr2(struct tty_term *, enum tty_code_code, const char *tty_term_string_ss(struct tty_term *, enum tty_code_code,
const void *, const void *); const char *, const char *);
int tty_term_number(struct tty_term *, enum tty_code_code); int tty_term_number(struct tty_term *, enum tty_code_code);
int tty_term_flag(struct tty_term *, enum tty_code_code); int tty_term_flag(struct tty_term *, enum tty_code_code);
const char *tty_term_describe(struct tty_term *, enum tty_code_code); const char *tty_term_describe(struct tty_term *, enum tty_code_code);
@@ -2351,7 +2453,7 @@ void tty_keys_free(struct tty *);
int tty_keys_next(struct tty *); int tty_keys_next(struct tty *);
/* arguments.c */ /* arguments.c */
void args_set(struct args *, u_char, struct args_value *); void args_set(struct args *, u_char, struct args_value *, int);
struct args *args_create(void); struct args *args_create(void);
struct args *args_parse(const struct args_parse *, struct args_value *, struct args *args_parse(const struct args_parse *, struct args_value *,
u_int, char **); u_int, char **);
@@ -2383,10 +2485,16 @@ struct args_value *args_first_value(struct args *, u_char);
struct args_value *args_next_value(struct args_value *); struct args_value *args_next_value(struct args_value *);
long long args_strtonum(struct args *, u_char, long long, long long, long long args_strtonum(struct args *, u_char, long long, long long,
char **); char **);
long long args_strtonum_and_expand(struct args *, u_char, long long,
long long, struct cmdq_item *, char **);
long long args_percentage(struct args *, u_char, long long, long long args_percentage(struct args *, u_char, long long,
long long, long long, char **); long long, long long, char **);
long long args_string_percentage(const char *, long long, long long, long long args_string_percentage(const char *, long long, long long,
long long, char **); long long, char **);
long long args_percentage_and_expand(struct args *, u_char, long long,
long long, long long, struct cmdq_item *, char **);
long long args_string_percentage_and_expand(const char *, long long,
long long, long long, struct cmdq_item *, char **);
/* cmd-find.c */ /* cmd-find.c */
int cmd_find_target(struct cmd_find_state *, struct cmdq_item *, int cmd_find_target(struct cmd_find_state *, struct cmdq_item *,
@@ -2478,7 +2586,8 @@ struct cmd_parse_result *cmd_parse_from_arguments(struct args_value *, u_int,
struct cmdq_state *cmdq_new_state(struct cmd_find_state *, struct key_event *, struct cmdq_state *cmdq_new_state(struct cmd_find_state *, struct key_event *,
int); int);
struct cmdq_state *cmdq_link_state(struct cmdq_state *); struct cmdq_state *cmdq_link_state(struct cmdq_state *);
struct cmdq_state *cmdq_copy_state(struct cmdq_state *); struct cmdq_state *cmdq_copy_state(struct cmdq_state *,
struct cmd_find_state *);
void cmdq_free_state(struct cmdq_state *); void cmdq_free_state(struct cmdq_state *);
void printflike(3, 4) cmdq_add_format(struct cmdq_state *, const char *, void printflike(3, 4) cmdq_add_format(struct cmdq_state *, const char *,
const char *, ...); const char *, ...);
@@ -2508,6 +2617,7 @@ u_int cmdq_next(struct client *);
struct cmdq_item *cmdq_running(struct client *); struct cmdq_item *cmdq_running(struct client *);
void cmdq_guard(struct cmdq_item *, const char *, int); void cmdq_guard(struct cmdq_item *, const char *, int);
void printflike(2, 3) cmdq_print(struct cmdq_item *, const char *, ...); void printflike(2, 3) cmdq_print(struct cmdq_item *, const char *, ...);
void cmdq_print_data(struct cmdq_item *, int, struct evbuffer *);
void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...); void printflike(2, 3) cmdq_error(struct cmdq_item *, const char *, ...);
/* cmd-wait-for.c */ /* cmd-wait-for.c */
@@ -2562,7 +2672,9 @@ void file_print_buffer(struct client *, void *, size_t);
void printflike(2, 3) file_error(struct client *, const char *, ...); void printflike(2, 3) file_error(struct client *, const char *, ...);
void file_write(struct client *, const char *, int, const void *, size_t, void file_write(struct client *, const char *, int, const void *, size_t,
client_file_cb, void *); client_file_cb, void *);
void file_read(struct client *, const char *, client_file_cb, void *); struct client_file *file_read(struct client *, const char *, client_file_cb,
void *);
void file_cancel(struct client_file *);
void file_push(struct client_file *); void file_push(struct client_file *);
int file_write_left(struct client_files *); int file_write_left(struct client_files *);
void file_write_open(struct client_files *, struct tmuxpeer *, void file_write_open(struct client_files *, struct tmuxpeer *,
@@ -2574,12 +2686,14 @@ void file_read_open(struct client_files *, struct tmuxpeer *, struct imsg *,
void file_write_ready(struct client_files *, struct imsg *); void file_write_ready(struct client_files *, struct imsg *);
void file_read_data(struct client_files *, struct imsg *); void file_read_data(struct client_files *, struct imsg *);
void file_read_done(struct client_files *, struct imsg *); void file_read_done(struct client_files *, struct imsg *);
void file_read_cancel(struct client_files *, struct imsg *);
/* server.c */ /* server.c */
extern struct tmuxproc *server_proc; extern struct tmuxproc *server_proc;
extern struct clients clients; extern struct clients clients;
extern struct cmd_find_state marked_pane; extern struct cmd_find_state marked_pane;
extern struct message_list message_log; extern struct message_list message_log;
extern time_t current_time;
void server_set_marked(struct session *, struct winlink *, void server_set_marked(struct session *, struct winlink *,
struct window_pane *); struct window_pane *);
void server_clear_marked(void); void server_clear_marked(void);
@@ -2624,6 +2738,7 @@ struct client_window *server_client_add_client_window(struct client *, u_int);
struct window_pane *server_client_get_pane(struct client *); struct window_pane *server_client_get_pane(struct client *);
void server_client_set_pane(struct client *, struct window_pane *); void server_client_set_pane(struct client *, struct window_pane *);
void server_client_remove_pane(struct window_pane *); void server_client_remove_pane(struct window_pane *);
void server_client_print(struct client *, int, struct evbuffer *);
/* server-fn.c */ /* server-fn.c */
void server_redraw_client(struct client *); void server_redraw_client(struct client *);
@@ -2716,6 +2831,7 @@ int colour_fromstring(const char *s);
int colour_256toRGB(int); int colour_256toRGB(int);
int colour_256to16(int); int colour_256to16(int);
int colour_byname(const char *); int colour_byname(const char *);
int colour_parseX11(const char *);
void colour_palette_init(struct colour_palette *); void colour_palette_init(struct colour_palette *);
void colour_palette_clear(struct colour_palette *); void colour_palette_clear(struct colour_palette *);
void colour_palette_free(struct colour_palette *); void colour_palette_free(struct colour_palette *);
@@ -2754,7 +2870,7 @@ void grid_clear_lines(struct grid *, u_int, u_int, u_int);
void grid_move_lines(struct grid *, u_int, u_int, u_int, u_int); void grid_move_lines(struct grid *, u_int, u_int, u_int, u_int);
void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int, u_int); void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int, u_int);
char *grid_string_cells(struct grid *, u_int, u_int, u_int, char *grid_string_cells(struct grid *, u_int, u_int, u_int,
struct grid_cell **, int, int, int); struct grid_cell **, int, struct screen *);
void grid_duplicate_lines(struct grid *, u_int, struct grid *, u_int, void grid_duplicate_lines(struct grid *, u_int, struct grid *, u_int,
u_int); u_int);
void grid_reflow(struct grid *, u_int); void grid_reflow(struct grid *, u_int);
@@ -2827,12 +2943,14 @@ void screen_write_putc(struct screen_write_ctx *, const struct grid_cell *,
u_char); u_char);
void screen_write_fast_copy(struct screen_write_ctx *, struct screen *, void screen_write_fast_copy(struct screen_write_ctx *, struct screen *,
u_int, u_int, u_int, u_int); u_int, u_int, u_int, u_int);
void screen_write_hline(struct screen_write_ctx *, u_int, int, int); void screen_write_hline(struct screen_write_ctx *, u_int, int, int,
enum box_lines, const struct grid_cell *);
void screen_write_vline(struct screen_write_ctx *, u_int, int, int); void screen_write_vline(struct screen_write_ctx *, u_int, int, int);
void screen_write_menu(struct screen_write_ctx *, struct menu *, int, void screen_write_menu(struct screen_write_ctx *, struct menu *, int,
enum box_lines, const struct grid_cell *, const struct grid_cell *,
const struct grid_cell *); const struct grid_cell *);
void screen_write_box(struct screen_write_ctx *, u_int, u_int, int, void screen_write_box(struct screen_write_ctx *, u_int, u_int,
const struct grid_cell *, const char *); enum box_lines, const struct grid_cell *, const char *);
void screen_write_preview(struct screen_write_ctx *, struct screen *, u_int, void screen_write_preview(struct screen_write_ctx *, struct screen *, u_int,
u_int); u_int);
void screen_write_backspace(struct screen_write_ctx *); void screen_write_backspace(struct screen_write_ctx *);
@@ -2867,8 +2985,14 @@ void screen_write_collect_end(struct screen_write_ctx *);
void screen_write_collect_add(struct screen_write_ctx *, void screen_write_collect_add(struct screen_write_ctx *,
const struct grid_cell *); const struct grid_cell *);
void screen_write_cell(struct screen_write_ctx *, const struct grid_cell *); void screen_write_cell(struct screen_write_ctx *, const struct grid_cell *);
void screen_write_setselection(struct screen_write_ctx *, u_char *, u_int); void screen_write_setselection(struct screen_write_ctx *, const char *,
void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int); u_char *, u_int);
void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int,
int);
#ifdef ENABLE_SIXEL
void screen_write_sixelimage(struct screen_write_ctx *,
struct sixel_image *, u_int);
#endif
void screen_write_alternateon(struct screen_write_ctx *, void screen_write_alternateon(struct screen_write_ctx *,
struct grid_cell *, int); struct grid_cell *, int);
void screen_write_alternateoff(struct screen_write_ctx *, void screen_write_alternateoff(struct screen_write_ctx *,
@@ -2883,6 +3007,7 @@ void screen_init(struct screen *, u_int, u_int, u_int);
void screen_reinit(struct screen *); void screen_reinit(struct screen *);
void screen_free(struct screen *); void screen_free(struct screen *);
void screen_reset_tabs(struct screen *); void screen_reset_tabs(struct screen *);
void screen_reset_hyperlinks(struct screen *);
void screen_set_cursor_style(u_int, enum screen_cursor_style *, int *); void screen_set_cursor_style(u_int, enum screen_cursor_style *, int *);
void screen_set_cursor_colour(struct screen *, int); void screen_set_cursor_colour(struct screen *, int);
int screen_set_title(struct screen *, const char *); int screen_set_title(struct screen *, const char *);
@@ -2971,6 +3096,7 @@ int window_pane_key(struct window_pane *, struct client *,
struct session *, struct winlink *, key_code, struct session *, struct winlink *, key_code,
struct mouse_event *); struct mouse_event *);
int window_pane_visible(struct window_pane *); int window_pane_visible(struct window_pane *);
int window_pane_exited(struct window_pane *);
u_int window_pane_search(struct window_pane *, const char *, int, u_int window_pane_search(struct window_pane *, const char *, int,
int); int);
const char *window_printable_flags(struct winlink *, int); const char *window_printable_flags(struct winlink *, int);
@@ -2978,6 +3104,10 @@ struct window_pane *window_pane_find_up(struct window_pane *);
struct window_pane *window_pane_find_down(struct window_pane *); struct window_pane *window_pane_find_down(struct window_pane *);
struct window_pane *window_pane_find_left(struct window_pane *); struct window_pane *window_pane_find_left(struct window_pane *);
struct window_pane *window_pane_find_right(struct window_pane *); struct window_pane *window_pane_find_right(struct window_pane *);
void window_pane_stack_push(struct window_panes *,
struct window_pane *);
void window_pane_stack_remove(struct window_panes *,
struct window_pane *);
void window_set_name(struct window *, const char *); void window_set_name(struct window *, const char *);
void window_add_ref(struct window *, const char *); void window_add_ref(struct window *, const char *);
void window_remove_ref(struct window *, const char *); void window_remove_ref(struct window *, const char *);
@@ -2990,6 +3120,7 @@ void *window_pane_get_new_data(struct window_pane *,
void window_pane_update_used_data(struct window_pane *, void window_pane_update_used_data(struct window_pane *,
struct window_pane_offset *, size_t); struct window_pane_offset *, size_t);
void window_set_fill_character(struct window *); void window_set_fill_character(struct window *);
void window_pane_default_cursor(struct window_pane *);
/* layout.c */ /* layout.c */
u_int layout_count_cells(struct layout_cell *); u_int layout_count_cells(struct layout_cell *);
@@ -3111,6 +3242,7 @@ char *parse_window_name(const char *);
/* control.c */ /* control.c */
void control_discard(struct client *); void control_discard(struct client *);
void control_start(struct client *); void control_start(struct client *);
void control_ready(struct client *);
void control_stop(struct client *); void control_stop(struct client *);
void control_set_pane_on(struct client *, struct window_pane *); void control_set_pane_on(struct client *, struct window_pane *);
void control_set_pane_off(struct client *, struct window_pane *); void control_set_pane_off(struct client *, struct window_pane *);
@@ -3141,6 +3273,8 @@ void control_notify_session_renamed(struct session *);
void control_notify_session_created(struct session *); void control_notify_session_created(struct session *);
void control_notify_session_closed(struct session *); void control_notify_session_closed(struct session *);
void control_notify_session_window_changed(struct session *); void control_notify_session_window_changed(struct session *);
void control_notify_paste_buffer_changed(const char *);
void control_notify_paste_buffer_deleted(const char *);
/* session.c */ /* session.c */
extern struct sessions sessions; extern struct sessions sessions;
@@ -3183,6 +3317,8 @@ u_int session_group_attached_count(struct session_group *);
void session_renumber_windows(struct session *); void session_renumber_windows(struct session *);
/* utf8.c */ /* utf8.c */
enum utf8_state utf8_towc (const struct utf8_data *, wchar_t *);
int utf8_in_table(wchar_t, const wchar_t *, u_int);
utf8_char utf8_build_one(u_char); utf8_char utf8_build_one(u_char);
enum utf8_state utf8_from_data(const struct utf8_data *, utf8_char *); enum utf8_state utf8_from_data(const struct utf8_data *, utf8_char *);
void utf8_to_data(utf8_char, struct utf8_data *); void utf8_to_data(utf8_char, struct utf8_data *);
@@ -3209,6 +3345,16 @@ char *osdep_get_name(int, char *);
char *osdep_get_cwd(int); char *osdep_get_cwd(int);
struct event_base *osdep_event_init(void); struct event_base *osdep_event_init(void);
/* utf8-combined.c */
int utf8_has_zwj(const struct utf8_data *);
int utf8_is_zwj(const struct utf8_data *);
int utf8_is_vs(const struct utf8_data *);
int utf8_is_modifier(const struct utf8_data *);
/* procname.c */
char *get_proc_name(int, char *);
char *get_proc_cwd(int);
/* log.c */ /* log.c */
void log_add_level(void); void log_add_level(void);
int log_get_level(void); int log_get_level(void);
@@ -3231,11 +3377,13 @@ void menu_add_item(struct menu *, const struct menu_item *,
struct cmdq_item *, struct client *, struct cmdq_item *, struct client *,
struct cmd_find_state *); struct cmd_find_state *);
void menu_free(struct menu *); void menu_free(struct menu *);
struct menu_data *menu_prepare(struct menu *, int, struct cmdq_item *, u_int, struct menu_data *menu_prepare(struct menu *, int, int, struct cmdq_item *,
u_int, struct client *, struct cmd_find_state *, u_int, u_int, struct client *, enum box_lines, const char *,
const char *, const char *, struct cmd_find_state *,
menu_choice_cb, void *); menu_choice_cb, void *);
int menu_display(struct menu *, int, struct cmdq_item *, u_int, int menu_display(struct menu *, int, int, struct cmdq_item *,
u_int, struct client *, struct cmd_find_state *, u_int, u_int, struct client *, enum box_lines, const char *,
const char *, const char *, struct cmd_find_state *,
menu_choice_cb, void *); menu_choice_cb, void *);
struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *); struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *);
void menu_check_cb(struct client *, void *, u_int, u_int, u_int, void menu_check_cb(struct client *, void *, u_int, u_int, u_int,
@@ -3251,11 +3399,11 @@ int menu_key_cb(struct client *, void *, struct key_event *);
#define POPUP_INTERNAL 0x4 #define POPUP_INTERNAL 0x4
typedef void (*popup_close_cb)(int, void *); typedef void (*popup_close_cb)(int, void *);
typedef void (*popup_finish_edit_cb)(char *, size_t, void *); typedef void (*popup_finish_edit_cb)(char *, size_t, void *);
int popup_display(int, int, struct cmdq_item *, u_int, u_int, int popup_display(int, enum box_lines, struct cmdq_item *, u_int,
u_int, u_int, struct environ *, const char *, int, char **, u_int, u_int, u_int, struct environ *, const char *, int,
const char *, const char *, struct client *, char **, const char *, const char *, struct client *,
struct session *, const char *, const char *, struct session *, const char *, const char *,
popup_close_cb, void *); popup_close_cb, void *);
int popup_editor(struct client *, const char *, size_t, int popup_editor(struct client *, const char *, size_t,
popup_finish_edit_cb, void *); popup_finish_edit_cb, void *);
@@ -3277,6 +3425,27 @@ struct window_pane *spawn_pane(struct spawn_context *, char **);
/* regsub.c */ /* regsub.c */
char *regsub(const char *, const char *, const char *, int); char *regsub(const char *, const char *, const char *, int);
#ifdef ENABLE_SIXEL
/* image.c */
int image_free_all(struct screen *);
struct image *image_store(struct screen *, struct sixel_image *);
int image_check_line(struct screen *, u_int, u_int);
int image_check_area(struct screen *, u_int, u_int, u_int, u_int);
int image_scroll_up(struct screen *, u_int);
/* image-sixel.c */
#define SIXEL_COLOUR_REGISTERS 1024
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int);
void sixel_free(struct sixel_image *);
void sixel_log(struct sixel_image *);
void sixel_size_in_cells(struct sixel_image *, u_int *, u_int *);
struct sixel_image *sixel_scale(struct sixel_image *, u_int, u_int, u_int,
u_int, u_int, u_int, int);
char *sixel_print(struct sixel_image *, struct sixel_image *,
size_t *);
struct screen *sixel_to_screen(struct sixel_image *);
#endif
/* server-acl.c */ /* server-acl.c */
void server_acl_init(void); void server_acl_init(void);
struct server_acl_user *server_acl_user_find(uid_t); struct server_acl_user *server_acl_user_find(uid_t);
@@ -3288,4 +3457,13 @@ void server_acl_user_deny_write(uid_t);
int server_acl_join(struct client *); int server_acl_join(struct client *);
uid_t server_acl_get_uid(struct server_acl_user *); uid_t server_acl_get_uid(struct server_acl_user *);
/* hyperlink.c */
u_int hyperlinks_put(struct hyperlinks *, const char *,
const char *);
int hyperlinks_get(struct hyperlinks *, u_int,
const char **, const char **, const char **);
struct hyperlinks *hyperlinks_init(void);
void hyperlinks_reset(struct hyperlinks *);
void hyperlinks_free(struct hyperlinks *);
#endif /* TMUX_H */ #endif /* TMUX_H */

View File

@@ -268,7 +268,7 @@ Oct Hex * (* marks function used in DEC VT series or LA series terminals)
073 3B ; 073 3B ;
074 3C < * DECANSI - Switch from VT52 mode to VT100 mode 074 3C < * DECANSI - Switch from VT52 mode to VT100 mode
075 3D = * DECKPAM - Set keypad to applications mode (ESCape instead of digits) 075 3D = * DECKPAM - Set keypad to applications mode (ESCape instead of digits)
076 3E > * DECKPNM - Set keypad to numeric mode (digits intead of ESCape seq) 076 3E > * DECKPNM - Set keypad to numeric mode (digits instead of ESCape seq)
077 3F ? 077 3F ?
DCS Device Control Strings used by DEC terminals (ends with ST) DCS Device Control Strings used by DEC terminals (ends with ST)
@@ -449,7 +449,7 @@ Oct Hex * (* marks function used in DEC VT series or LA series terminals)
* [16h = TTM - Transmit Termination Mode, send scrolling region * [16h = TTM - Transmit Termination Mode, send scrolling region
[17h = SATM - Send Area Transmit Mode, send entire buffer [17h = SATM - Send Area Transmit Mode, send entire buffer
[18h = TSM - Tabulation Stop Mode, lines are independent [18h = TSM - Tabulation Stop Mode, lines are independent
[19h = EBM - Editing Boundry Mode, all of memory affected [19h = EBM - Editing Boundary Mode, all of memory affected
* [20h = LNM - Linefeed Newline Mode, LF interpreted as CR LF * [20h = LNM - Linefeed Newline Mode, LF interpreted as CR LF
* [?1h = DECCKM - Cursor Keys Mode, send ESC O A for cursor up * [?1h = DECCKM - Cursor Keys Mode, send ESC O A for cursor up
* [?2h = DECANM - ANSI Mode, use ESC < to switch VT52 to ANSI * [?2h = DECANM - ANSI Mode, use ESC < to switch VT52 to ANSI

Some files were not shown because too many files have changed in this diff Show More