Compare commits

...

392 Commits
3.3 ... master

Author SHA1 Message Date
Thomas Adam 4c2eedca5a Merge branch 'obsd-master' 2024-05-19 06:01:09 +01:00
jsg ac6c1e9589 remove prototype with no matching function 2024-05-19 03:27:58 +00:00
Thomas Adam 0903790b00 Merge branch 'obsd-master' 2024-05-18 12:01:09 +01:00
jsg 03de52653e remove prototypes with no matching function; ok nicm@ 2024-05-18 08:51:26 +00:00
jsg da06719309 remove externs with no matching var; ok nicm@ 2024-05-18 08:50:11 +00:00
Thomas Adam fc84097379 Merge branch 'obsd-master' 2024-05-15 14:01:09 +01:00
Thomas Adam 4fd725c6e1 Merge branch 'obsd-master' 2024-05-15 12:01:10 +01:00
nicm d39dcea30a Use default-shell for command prompt #() and popups as well 2024-05-15 09:59:12 +00:00
nicm bfd65398a9 Fix memory leaks reported by Lu Ming Yin. 2024-05-15 08:39:30 +00:00
Thomas Adam 452d987e0e Merge branch 'obsd-master' 2024-05-14 14:01:10 +01:00
Thomas Adam 8ef899f315 Merge branch 'obsd-master' 2024-05-14 12:01:09 +01:00
nicm a18d1146aa Add missing time.h to tty.c (from Ismail Donmez), also remove some stray
spaces.
2024-05-14 10:11:09 +00:00
nicm 5b5004e5ac Revert part of the change for GitHub issue 3675 because it does not work
correctly, it was intended to skip lines that are already being searched
as part of a previous wrapped line but in fact is skipping all lines
except the last in wrapped lines.

Also revert the search-wrapped-lines option (I didn't realize it was
intended to work around this).
2024-05-14 09:32:37 +00:00
Thomas Adam 6ff8f8fbf9 Merge branch 'obsd-master' 2024-05-14 10:01:10 +01:00
nicm c9616700ca Add a command-error hook when a command fails, from Hugh Davenport in
GitHub issue 3973.
2024-05-14 07:52:19 +00:00
nicm 4c928dce74 Add an option to disable unwrapping lines for searching, from
meanderingprogrammer at gmail dot com, GitHub issue 3975.
2024-05-14 07:40:39 +00:00
nicm fb37d52dde Restore previous behaviour or writing to stdout if available. 2024-05-14 07:33:01 +00:00
Thomas Adam 363d9c401e Merge branch 'obsd-master' 2024-05-13 14:01:10 +01:00
nicm 8643ece345 Fix memory leak, from Fadi Afani. 2024-05-13 11:45:05 +00:00
Nicholas Marriott 9ba433e521 Use printf not echo -e, from Joyce Lin. 2024-05-13 12:42:14 +01:00
Nicholas Marriott 3823fa2c57 Send SIGHUP since some programs ignore SIGTERM, from Eduardo Grajeda in GitHub
issue 3958.
2024-04-30 12:38:58 +01:00
Thomas Adam 0a8571b6fe Merge branch 'obsd-master' 2024-04-23 16:09:50 +01:00
jsg ea9f416c99 correct indentation; no functional change
ok tb@
2024-04-23 13:34:51 +00:00
Thomas Adam 036d8993e6 Merge branch 'obsd-master' 2024-04-15 12:01:11 +01:00
nicm e8530c9fee Fixes for memory leaks reported by Lu Ming Yin, fixes from Howard Chu. 2024-04-15 08:19:55 +00:00
Nicholas Marriott dd4c0109a9 Missing headers for Android, from Biswapriyo Nath. 2024-04-15 09:07:41 +01:00
Thomas Adam 43530d4397 Merge branch 'obsd-master' 2024-04-10 10:01:13 +01:00
nicm 553d4cba79 Add an option allow-set-title to forbid applications from changing the
pane title, from someone in GitHub issue 3930.
2024-04-10 07:36:25 +00:00
nicm c62a9ca16b Correct handling of mouse up events (don't ignore all but the last
released button), and always process down event for double click. From
Rudy Dellomas III in GitHub issue 3919.
2024-04-10 07:29:15 +00:00
nicm 424f13fe13 Do not get muddled and crash if focusing a pane that is exiting,
reported by Saul Nogueras in GitHub issue 3776.
2024-04-10 07:15:21 +00:00
Thomas Adam 4bb6da75ba Merge branch 'obsd-master' 2024-04-05 02:01:09 +01:00
nicm a28175dbfd Pick newest session as documented, not oldest, from Magnus Gross. 2024-04-04 22:44:40 +00:00
Thomas Adam fc204bb5e5 Merge branch 'obsd-master' 2024-03-26 12:01:11 +00:00
nicm 6207a45139 Fix selection present check, reported by M Kelly. 2024-03-26 10:20:20 +00:00
Thomas Adam 3c3643f580 Merge branch 'obsd-master' 2024-03-21 14:01:10 +00:00
nicm 89c1c43ef9 Write padding character into the right position. 2024-03-21 12:10:57 +00:00
nicm 2e9d7ebf15 Reduce escape-time default to 10 milliseconds, 500 is far too long for
modern terminals and networks. Case made by Kurtis Rader in GitHub issue
3844.
2024-03-21 11:53:11 +00:00
nicm d8ddeec7db Add -M to always turn mouse on in a menu, GitHub issue 3779. 2024-03-21 11:51:32 +00:00
nicm 6f0254e6a8 Look for feature code 21 for DECSLRM and 28 for DECFRA in the device
attributes and also accept level 1 (there is no hardware with this but
some emulators may use it). Pointed out by James Holderness.
2024-03-21 11:47:55 +00:00
Nicholas Marriott aa17f0e0c1 Fix crash if SIXEL colour register is invalid and remove SIXEL images before
reflow to avoid a different crash, from Anindya Mukherjee.
2024-03-21 11:37:09 +00:00
nicm 0ae8b681b2 Use -p for default paste-buffer command in buffer mode, it will only do
anything if the application asked for it. From Gregory Anders.
2024-03-21 11:32:49 +00:00
nicm 6c0067c103 Do not notify window-layout-changed if the window is about to be
destroyed (since it may have been freed by the time the notify happens),
from Romain Francoise in GitHub issue 3860.
2024-03-21 11:30:42 +00:00
nicm 5458cb2850 Revert detach-client part of last, did not intend this to go in. 2024-03-21 11:27:18 +00:00
nicm 0c374868ca Do not consider a selection present if it is empty, from Michael Grant
(GitHub issue 3869). Also a typo fix from GitHub issue 3877.
2024-03-21 11:26:28 +00:00
Nicholas Marriott bf5d3f2e26 Typo, GitHub issue 3877. 2024-03-21 11:19:59 +00:00
Nicholas Marriott d5ef837f63 Remove duplicate .tmux.conf mention, from Valentin Rylenko. 2024-03-21 11:18:49 +00:00
Thomas Adam b79e28b2c3 Merge branch 'obsd-master' 2024-03-13 14:01:09 +00:00
nicm 8ffd5458ff Make the attach-session description clearer - do not mention creating a
client which is not important, explicitly say the session must exist,
and mention new-session and new-session -A. Prompted by Theo.
2024-03-13 11:25:50 +00:00
Thomas Adam b54e1fc4f7 Merge branch 'obsd-master' 2024-03-07 00:01:10 +00:00
Nicholas Marriott bdb6321229 Update lock.yml. 2024-03-06 21:45:26 +00:00
nicm bd29a48b56 Check for the right flag to fix split-window -p, from Bryan Childs. 2024-03-06 21:32:39 +00:00
Nicholas Marriott f3f1c3db58 Add missing headers, from Marvin Schmidt. 2024-03-06 21:29:28 +00:00
Nicholas Marriott 608d113486 next-3.5 2024-02-13 10:20:18 +00:00
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 6ebc3feb46
Author: topcat001 <anindya49@hotmail.com>
Date:   Sun Aug 20 16:09:51 2023 -0700

    Remove redundant {}.

commit 6f013fce39
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 e6322b4196
Author: topcat001 <anindya49@hotmail.com>
Date:   Sun Aug 20 15:46:59 2023 -0700

    Fix placeholder label and clean up.

commit 5896ac52a1
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 ad98233066
Author: topcat001 <anindya49@hotmail.com>
Date:   Tue Aug 15 13:57:08 2023 -0700

    Better text placeholder.

commit 312d83252c
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 14b8b52452
Merge: 4baf7642 fda39377
Author: topcat001 <anindya49@hotmail.com>
Date:   Sat Jul 22 17:29:10 2023 -0700

    Merge branch 'master' into sixel

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

    Both files can go on one line.

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

    Merge topcat001/tmux/sixel.

commit 6794facc82
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 7b85f5adf9
Author: Nicholas Marriott <nicholas.marriott@gmail.com>
Date:   Thu Jun 8 12:55:03 2023 +0100

    Do not require passthrough for SIXEL.

commit a6ee55e092
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 6da391f460
Merge: 0d71e585 0eb5d254
Author: topcat001 <anindya49@hotmail.com>
Date:   Sat May 20 17:05:55 2023 -0700

    Merge branch 'master' into sixel

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

    Merge branch 'master' into sixel

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

    Merge branch 'master' into sixel

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

    Merge branch 'master' into sixel

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

    Merge branch 'master' into sixel

commit 2a1e16a24d
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
Nicholas Marriott 0b355ae811 3.3a. 2022-06-09 12:30:50 +01:00
Nicholas Marriott 6a9bb2a622 Remove extra definition of getpeereid. From Eric N Vander Weele in GitHub issue
3209.
2022-06-09 12:28:38 +01:00
nicm 988e59cf3e Do not attempt to use client in config file (it will be NULL), GitHub
issue 3206.
2022-06-09 12:28:34 +01:00
nicm 30e06e9d85 Do not unintentionally turn off all mouse mode when button is also present. 2022-06-09 12:28:22 +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
119 changed files with 8042 additions and 1742 deletions

View File

@ -3,21 +3,32 @@ name: 'Lock Threads'
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
discussions: write
concurrency:
group: lock-threads
jobs:
lock:
action:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v2
- uses: dessant/lock-threads@v5
with:
github-token: ${{ github.token }}
issue-lock-inactive-days: '30'
pr-lock-inactive-days: '60'
issue-lock-comment: >
issue-inactive-days: '30'
issue-comment: >
This issue has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new issue for related bugs.
pr-lock-comment: >
pr-inactive-days: '60'
pr-comment: >
This pull request has been automatically locked since there
has not been any recent activity after it was closed.
Please open a new issue for related bugs.
discussion-inactive-days: '60'
discussion-comment: >
This discussion has been automatically locked since there
has not been any recent activity after it was closed.

122
CHANGES
View File

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

View File

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

View File

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

47
cfg.c
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -143,6 +143,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata->prompt_type = status_prompt_type(type);
if (cdata->prompt_type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "unknown type: %s", type);
cmd_command_prompt_free(cdata);
return (CMD_RETURN_ERROR);
}
} else
@ -179,10 +180,10 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
if (s == NULL)
goto out;
if (done) {
if (cdata->flags & PROMPT_INCREMENTAL)
goto out;
cmd_append_argv(&cdata->argc, &cdata->argv, s);
if (++cdata->current != cdata->count) {
prompt = &cdata->prompts[cdata->current];
@ -193,8 +194,11 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
argc = cdata->argc;
argv = cmd_copy_argv(cdata->argc, cdata->argv);
cmd_append_argv(&argc, &argv, s);
if (!done)
cmd_append_argv(&argc, &argv, s);
if (done) {
cmd_free_argv(cdata->argc, cdata->argv);
cdata->argc = argc;
cdata->argv = cmd_copy_argv(argc, argv);
}

View File

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

View File

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

View File

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

View File

@ -144,7 +144,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
llen = 0;
if (sx < len * 6 || sy < 5) {
tty_attributes(tty, &fgc, &grid_default_cell, NULL);
tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
if (sx >= len + llen + 1) {
len += llen + 1;
tty_cursor(tty, xoff + px - len / 2, yoff + py);
@ -161,7 +161,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
px -= len * 3;
py -= 2;
tty_attributes(tty, &bgc, &grid_default_cell, NULL);
tty_attributes(tty, &bgc, &grid_default_cell, NULL, NULL);
for (ptr = buf; *ptr != '\0'; ptr++) {
if (*ptr < '0' || *ptr > '9')
continue;
@ -179,7 +179,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
if (sy <= 6)
goto out;
tty_attributes(tty, &fgc, &grid_default_cell, NULL);
tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
if (rlen != 0 && sx >= rlen) {
tty_cursor(tty, xoff + sx - rlen, yoff);
tty_putn(tty, rbuf, rlen, rlen);
@ -246,7 +246,7 @@ cmd_display_panes_key(struct client *c, void *data, struct key_event *event)
wp = window_pane_at_index(w, index);
if (wp == NULL)
return (1);
window_unzoom(w);
window_unzoom(w, 1);
xasprintf(&expanded, "%%%u", wp->id);

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -87,7 +87,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
if (args_has(args, 'Z')) {
if (w->flags & WINDOW_ZOOMED)
window_unzoom(w);
window_unzoom(w, 1);
else
window_zoom(wp);
server_redraw_window(w);

View File

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

View File

@ -44,8 +44,9 @@ const struct cmd_entry cmd_run_shell_entry = {
.name = "run-shell",
.alias = "run",
.args = { "bd:Ct:", 0, 1, cmd_run_shell_args_parse },
.usage = "[-bC] [-d delay] " CMD_TARGET_PANE_USAGE " [shell-command]",
.args = { "bd:Ct:c:", 0, 2, cmd_run_shell_args_parse },
.usage = "[-bC] [-c start-directory] [-d delay] " CMD_TARGET_PANE_USAGE
" [shell-command]",
.target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL },
@ -84,12 +85,18 @@ cmd_run_shell_print(struct job *job, const char *msg)
if (cdata->wp_id != -1)
wp = window_pane_find_by_id(cdata->wp_id);
if (wp == NULL && cdata->item != NULL)
wp = server_client_get_pane(cdata->client);
if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0)
wp = fs.wp;
if (wp == NULL)
return;
if (wp == NULL) {
if (cdata->item != NULL) {
cmdq_print(cdata->item, "%s", msg);
return;
}
if (cdata->item != NULL && cdata->client != NULL)
wp = server_client_get_pane(cdata->client);
if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0)
wp = fs.wp;
if (wp == NULL)
return;
}
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
@ -103,6 +110,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item);
struct cmd_run_shell_data *cdata;
struct client *c = cmdq_get_client(item);
struct client *tc = cmdq_get_target_client(item);
struct session *s = target->s;
struct window_pane *wp = target->wp;
@ -137,7 +145,7 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
cdata->wp_id = -1;
if (wait) {
cdata->client = cmdq_get_client(item);
cdata->client = c;
cdata->item = item;
} else {
cdata->client = tc;
@ -145,8 +153,10 @@ cmd_run_shell_exec(struct cmd *self, struct cmdq_item *item)
}
if (cdata->client != NULL)
cdata->client->references++;
cdata->cwd = xstrdup(server_client_get_cwd(cmdq_get_client(item), s));
if (args_has(args, 'c'))
cdata->cwd = xstrdup(args_get(args, 'c'));
else
cdata->cwd = xstrdup(server_client_get_cwd(c, s));
cdata->s = s;
if (s != NULL)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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);
}
if (src_w != dst_w) {
if (src_w->last == src_wp)
src_w->last = NULL;
if (dst_w->last == dst_wp)
dst_w->last = NULL;
window_pane_stack_remove(&src_w->last_panes, src_wp);
window_pane_stack_remove(&dst_w->last_panes, dst_wp);
colour_palette_from_option(&src_wp->palette, src_wp->options);
colour_palette_from_option(&dst_wp->palette, dst_wp->options);
}
server_redraw_window(src_w);
server_redraw_window(dst_w);

14
cmd.c
View File

@ -47,7 +47,6 @@ extern const struct cmd_entry cmd_display_menu_entry;
extern const struct cmd_entry cmd_display_message_entry;
extern const struct cmd_entry cmd_display_popup_entry;
extern const struct cmd_entry cmd_display_panes_entry;
extern const struct cmd_entry cmd_down_pane_entry;
extern const struct cmd_entry cmd_find_window_entry;
extern const struct cmd_entry cmd_has_session_entry;
extern const struct cmd_entry cmd_if_shell_entry;
@ -117,7 +116,6 @@ extern const struct cmd_entry cmd_swap_window_entry;
extern const struct cmd_entry cmd_switch_client_entry;
extern const struct cmd_entry cmd_unbind_key_entry;
extern const struct cmd_entry cmd_unlink_window_entry;
extern const struct cmd_entry cmd_up_pane_entry;
extern const struct cmd_entry cmd_wait_for_entry;
const struct cmd_entry *cmd_table[] = {
@ -812,10 +810,14 @@ cmd_mouse_pane(struct mouse_event *m, struct session **sp,
if ((wl = cmd_mouse_window(m, sp)) == NULL)
return (NULL);
if ((wp = window_pane_find_by_id(m->wp)) == NULL)
return (NULL);
if (!window_has_pane(wl->window, wp))
return (NULL);
if (m->wp == -1)
wp = wl->window->active;
else {
if ((wp = window_pane_find_by_id(m->wp)) == NULL)
return (NULL);
if (!window_has_pane(wl->window, wp))
return (NULL);
}
if (wlp != NULL)
*wlp = wl;

View File

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

View File

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

View File

@ -18,6 +18,7 @@
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#ifdef HAVE_UCRED_H
#include <ucred.h>
@ -38,9 +39,6 @@ getpeereid(int s, uid_t *uid, gid_t *gid)
*gid = uc.gid;
return (0);
#elif defined(HAVE_GETPEERUCRED)
int
getpeereid(int s, uid_t *uid, gid_t *gid)
{
ucred_t *ucred = NULL;
if (getpeerucred(s, &ucred) == -1)
@ -51,8 +49,9 @@ getpeereid(int s, uid_t *uid, gid_t *gid)
return (-1);
ucred_free(ucred);
return (0);
}
#else
return (getuid());
*uid = geteuid();
*gid = getegid();
return (0);
#endif
}

31
compat/htonll.c Normal file
View File

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

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>
*
* Permission to use, copy, modify, and distribute this software for any
@ -19,9 +20,11 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <arpa/inet.h>
#include <limits.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -29,18 +32,36 @@
#include "compat.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 void ibuf_enqueue(struct msgbuf *, struct ibuf *);
static void ibuf_dequeue(struct msgbuf *, struct ibuf *);
static void msgbuf_drain(struct msgbuf *, size_t);
struct ibuf *
ibuf_open(size_t len)
{
struct ibuf *buf;
if (len == 0) {
errno = EINVAL;
return (NULL);
}
if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
return (NULL);
if ((buf->buf = malloc(len)) == NULL) {
if ((buf->buf = calloc(len, 1)) == NULL) {
free(buf);
return (NULL);
}
@ -55,14 +76,22 @@ ibuf_dynamic(size_t len, size_t max)
{
struct ibuf *buf;
if (max < len)
if (max == 0 || max < len) {
errno = EINVAL;
return (NULL);
}
if ((buf = ibuf_open(len)) == NULL)
if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
return (NULL);
if (max > 0)
buf->max = max;
if (len > 0) {
if ((buf->buf = calloc(len, 1)) == NULL) {
free(buf);
return (NULL);
}
}
buf->size = len;
buf->max = max;
buf->fd = -1;
return (buf);
}
@ -73,7 +102,7 @@ ibuf_realloc(struct ibuf *buf, size_t len)
unsigned char *b;
/* 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;
return (-1);
}
@ -87,23 +116,16 @@ ibuf_realloc(struct ibuf *buf, size_t len)
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 *
ibuf_reserve(struct ibuf *buf, size_t len)
{
void *b;
if (len > SIZE_MAX - buf->wpos || buf->max == 0) {
errno = ERANGE;
return (NULL);
}
if (buf->wpos + len > buf->size)
if (ibuf_realloc(buf, len) == -1)
return (NULL);
@ -113,34 +135,416 @@ ibuf_reserve(struct ibuf *buf, size_t len)
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 *
ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
{
/* only allowed to seek in already written parts */
if (pos + len > buf->wpos)
/* only allow seeking between rpos and wpos */
if (ibuf_size(buf) < pos || SIZE_MAX - pos < len ||
ibuf_size(buf) < pos + len) {
errno = ERANGE;
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
ibuf_size(struct ibuf *buf)
ibuf_size(const struct ibuf *buf)
{
return (buf->wpos);
return (buf->wpos - buf->rpos);
}
size_t
ibuf_left(struct ibuf *buf)
ibuf_left(const struct ibuf *buf)
{
if (buf->max == 0)
return (0);
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
ibuf_close(struct msgbuf *msgbuf, struct ibuf *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
ibuf_write(struct msgbuf *msgbuf)
{
@ -153,8 +557,8 @@ ibuf_write(struct msgbuf *msgbuf)
TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
if (i >= IOV_MAX)
break;
iov[i].iov_base = buf->buf + buf->rpos;
iov[i].iov_len = buf->wpos - buf->rpos;
iov[i].iov_base = ibuf_data(buf);
iov[i].iov_len = ibuf_size(buf);
i++;
}
@ -177,15 +581,6 @@ again:
return (1);
}
void
ibuf_free(struct ibuf *buf)
{
if (buf == NULL)
return;
freezero(buf->buf, buf->size);
free(buf);
}
void
msgbuf_init(struct msgbuf *msgbuf)
{
@ -194,7 +589,7 @@ msgbuf_init(struct msgbuf *msgbuf)
TAILQ_INIT(&msgbuf->bufs);
}
void
static void
msgbuf_drain(struct msgbuf *msgbuf, size_t n)
{
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;
buf = next) {
next = TAILQ_NEXT(buf, entry);
if (buf->rpos + n >= buf->wpos) {
n -= buf->wpos - buf->rpos;
if (n >= ibuf_size(buf)) {
n -= ibuf_size(buf);
ibuf_dequeue(msgbuf, buf);
} else {
buf->rpos += n;
@ -225,7 +620,7 @@ int
msgbuf_write(struct msgbuf *msgbuf)
{
struct iovec iov[IOV_MAX];
struct ibuf *buf;
struct ibuf *buf, *buf0 = NULL;
unsigned int i = 0;
ssize_t n;
struct msghdr msg;
@ -241,24 +636,26 @@ msgbuf_write(struct msgbuf *msgbuf)
TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
if (i >= IOV_MAX)
break;
iov[i].iov_base = buf->buf + buf->rpos;
iov[i].iov_len = buf->wpos - buf->rpos;
if (i > 0 && buf->fd != -1)
break;
iov[i].iov_base = ibuf_data(buf);
iov[i].iov_len = ibuf_size(buf);
i++;
if (buf->fd != -1)
break;
buf0 = buf;
}
msg.msg_iov = iov;
msg.msg_iovlen = i;
if (buf != NULL && buf->fd != -1) {
if (buf0 != NULL) {
msg.msg_control = (caddr_t)&cmsgbuf.buf;
msg.msg_controllen = sizeof(cmsgbuf.buf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
*(int *)CMSG_DATA(cmsg) = buf->fd;
*(int *)CMSG_DATA(cmsg) = buf0->fd;
}
again:
@ -279,9 +676,9 @@ again:
* assumption: fd got sent if sendmsg sent anything
* this works because fds are passed one at a time
*/
if (buf != NULL && buf->fd != -1) {
close(buf->fd);
buf->fd = -1;
if (buf0 != NULL) {
close(buf0->fd);
buf0->fd = -1;
}
msgbuf_drain(msgbuf, n);
@ -289,9 +686,17 @@ again:
return (1);
}
uint32_t
msgbuf_queuelen(struct msgbuf *msgbuf)
{
return (msgbuf->queued);
}
static void
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);
msgbuf->queued++;
}
@ -300,10 +705,6 @@ static void
ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
{
TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
if (buf->fd != -1)
close(buf->fd);
msgbuf->queued--;
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>
*
* Permission to use, copy, modify, and distribute this software for any
@ -28,23 +29,28 @@
#include "compat.h"
#include "imsg.h"
struct imsg_fd {
TAILQ_ENTRY(imsg_fd) entry;
int fd;
};
int imsg_fd_overhead = 0;
static int imsg_get_fd(struct imsgbuf *);
static int imsg_dequeue_fd(struct imsgbuf *);
void
imsg_init(struct imsgbuf *ibuf, int fd)
imsg_init(struct imsgbuf *imsgbuf, int fd)
{
msgbuf_init(&ibuf->w);
memset(&ibuf->r, 0, sizeof(ibuf->r));
ibuf->fd = fd;
ibuf->w.fd = fd;
ibuf->pid = getpid();
TAILQ_INIT(&ibuf->fds);
msgbuf_init(&imsgbuf->w);
memset(&imsgbuf->r, 0, sizeof(imsgbuf->r));
imsgbuf->fd = fd;
imsgbuf->w.fd = fd;
imsgbuf->pid = getpid();
TAILQ_INIT(&imsgbuf->fds);
}
ssize_t
imsg_read(struct imsgbuf *ibuf)
imsg_read(struct imsgbuf *imsgbuf)
{
struct msghdr msg;
struct cmsghdr *cmsg;
@ -60,8 +66,8 @@ imsg_read(struct imsgbuf *ibuf)
memset(&msg, 0, sizeof(msg));
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
iov.iov_base = imsgbuf->r.buf + imsgbuf->r.wpos;
iov.iov_len = sizeof(imsgbuf->r.buf) - imsgbuf->r.wpos;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = &cmsgbuf.buf;
@ -79,13 +85,13 @@ again:
return (-1);
}
if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
if ((n = recvmsg(imsgbuf->fd, &msg, 0)) == -1) {
if (errno == EINTR)
goto again;
goto fail;
}
ibuf->r.wpos += n;
imsgbuf->r.wpos += n;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
@ -105,7 +111,7 @@ again:
fd = ((int *)CMSG_DATA(cmsg))[i];
if (ifd != NULL) {
ifd->fd = fd;
TAILQ_INSERT_TAIL(&ibuf->fds, ifd,
TAILQ_INSERT_TAIL(&imsgbuf->fds, ifd,
entry);
ifd = NULL;
} else
@ -121,94 +127,235 @@ fail:
}
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;
av = ibuf->r.wpos;
av = imsgbuf->r.wpos;
if (IMSG_HEADER_SIZE > av)
return (0);
memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
if (imsg->hdr.len < IMSG_HEADER_SIZE ||
imsg->hdr.len > MAX_IMSGSIZE) {
memcpy(&m.hdr, imsgbuf->r.buf, sizeof(m.hdr));
if (m.hdr.len < IMSG_HEADER_SIZE ||
m.hdr.len > MAX_IMSGSIZE) {
errno = ERANGE;
return (-1);
}
if (imsg->hdr.len > av)
if (m.hdr.len > av)
return (0);
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
if (datalen == 0)
imsg->data = NULL;
else if ((imsg->data = malloc(datalen)) == NULL)
return (-1);
if (imsg->hdr.flags & IMSGF_HASFD)
imsg->fd = imsg_get_fd(ibuf);
else
imsg->fd = -1;
m.fd = -1;
m.buf = NULL;
m.data = NULL;
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) {
left = av - imsg->hdr.len;
memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
ibuf->r.wpos = left;
if (m.hdr.flags & IMSGF_HASFD)
m.fd = imsg_dequeue_fd(imsgbuf);
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
ibuf->r.wpos = 0;
imsgbuf->r.wpos = 0;
*imsg = m;
return (datalen + IMSG_HEADER_SIZE);
}
int
imsg_compose(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
int fd, const void *data, uint16_t datalen)
imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf)
{
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;
if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
return (-1);
if (imsg_add(wbuf, data, datalen) == -1)
return (-1);
wbuf->fd = fd;
imsg_close(ibuf, wbuf);
ibuf_fd_set(wbuf, fd);
imsg_close(imsgbuf, wbuf);
return (1);
}
int
imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
int fd, const struct iovec *iov, int iovcnt)
{
struct ibuf *wbuf;
int i, datalen = 0;
int i;
size_t datalen = 0;
for (i = 0; i < iovcnt; i++)
datalen += iov[i].iov_len;
if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
return (-1);
for (i = 0; i < iovcnt; i++)
if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
return (-1);
wbuf->fd = fd;
imsg_close(ibuf, wbuf);
ibuf_fd_set(wbuf, fd);
imsg_close(imsgbuf, wbuf);
return (1);
}
/* ARGSUSED */
/*
* Enqueue imsg with payload from ibuf buf. fd passing is not possible
* with this function.
*/
int
imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
pid_t pid, struct ibuf *buf)
{
struct ibuf *hdrbuf = NULL;
struct imsg_hdr hdr;
int save_errno;
if (ibuf_size(buf) + IMSG_HEADER_SIZE > 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 *
imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
uint16_t datalen)
imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
size_t datalen)
{
struct ibuf *wbuf;
struct imsg_hdr hdr;
@ -221,9 +368,9 @@ imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
hdr.type = type;
hdr.flags = 0;
hdr.peerid = peerid;
hdr.peerid = id;
if ((hdr.pid = pid) == 0)
hdr.pid = ibuf->pid;
hdr.pid = imsgbuf->pid;
if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
return (NULL);
}
@ -234,7 +381,7 @@ imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
}
int
imsg_add(struct ibuf *msg, const void *data, uint16_t datalen)
imsg_add(struct ibuf *msg, const void *data, size_t datalen)
{
if (datalen)
if (ibuf_add(msg, data, datalen) == -1) {
@ -245,58 +392,57 @@ imsg_add(struct ibuf *msg, const void *data, uint16_t datalen)
}
void
imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg)
{
struct imsg_hdr *hdr;
hdr = (struct imsg_hdr *)msg->buf;
hdr->flags &= ~IMSGF_HASFD;
if (msg->fd != -1)
if (ibuf_fd_avail(msg))
hdr->flags |= IMSGF_HASFD;
hdr->len = ibuf_size(msg);
hdr->len = (uint16_t)msg->wpos;
ibuf_close(&ibuf->w, msg);
ibuf_close(&imsgbuf->w, msg);
}
void
imsg_free(struct imsg *imsg)
{
freezero(imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE);
ibuf_free(imsg->buf);
}
static int
imsg_get_fd(struct imsgbuf *ibuf)
imsg_dequeue_fd(struct imsgbuf *imsgbuf)
{
int fd;
struct imsg_fd *ifd;
if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
if ((ifd = TAILQ_FIRST(&imsgbuf->fds)) == NULL)
return (-1);
fd = ifd->fd;
TAILQ_REMOVE(&ibuf->fds, ifd, entry);
TAILQ_REMOVE(&imsgbuf->fds, ifd, entry);
free(ifd);
return (fd);
}
int
imsg_flush(struct imsgbuf *ibuf)
imsg_flush(struct imsgbuf *imsgbuf)
{
while (ibuf->w.queued)
if (msgbuf_write(&ibuf->w) <= 0)
while (imsgbuf->w.queued)
if (msgbuf_write(&imsgbuf->w) <= 0)
return (-1);
return (0);
}
void
imsg_clear(struct imsgbuf *ibuf)
imsg_clear(struct imsgbuf *imsgbuf)
{
int fd;
msgbuf_clear(&ibuf->w);
while ((fd = imsg_get_fd(ibuf)) != -1)
msgbuf_clear(&imsgbuf->w);
while ((fd = imsg_dequeue_fd(imsgbuf)) != -1)
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, 2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@ -21,7 +22,7 @@
#ifndef _IMSG_H_
#define _IMSG_H_
#include <stdint.h>
#include <sys/types.h>
#define IBUF_READ_SIZE 65535
#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
@ -49,11 +50,7 @@ struct ibuf_read {
size_t wpos;
};
struct imsg_fd {
TAILQ_ENTRY(imsg_fd) entry;
int fd;
};
struct imsg_fd;
struct imsgbuf {
TAILQ_HEAD(, imsg_fd) fds;
struct ibuf_read r;
@ -76,35 +73,83 @@ struct imsg {
struct imsg_hdr hdr;
int fd;
void *data;
struct ibuf *buf;
};
struct iovec;
/* buffer.c */
/* imsg-buffer.c */
struct ibuf *ibuf_open(size_t);
struct ibuf *ibuf_dynamic(size_t, size_t);
int ibuf_add(struct ibuf *, const void *, size_t);
int ibuf_add_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_seek(struct ibuf *, size_t, size_t);
size_t ibuf_size(struct ibuf *);
size_t ibuf_left(struct ibuf *);
int ibuf_set(struct ibuf *, size_t, const void *, size_t);
int ibuf_set_n8(struct ibuf *, size_t, uint64_t);
int ibuf_set_n16(struct ibuf *, size_t, uint64_t);
int ibuf_set_n32(struct ibuf *, size_t, uint64_t);
int ibuf_set_n64(struct ibuf *, size_t, uint64_t);
int ibuf_set_h16(struct ibuf *, size_t, uint64_t);
int ibuf_set_h32(struct ibuf *, size_t, uint64_t);
int ibuf_set_h64(struct ibuf *, size_t, uint64_t);
void *ibuf_data(const struct ibuf *);
size_t ibuf_size(const struct ibuf *);
size_t ibuf_left(const struct ibuf *);
int ibuf_truncate(struct ibuf *, size_t);
void ibuf_rewind(struct ibuf *);
void ibuf_close(struct msgbuf *, struct ibuf *);
int ibuf_write(struct msgbuf *);
void ibuf_from_buffer(struct ibuf *, void *, size_t);
void ibuf_from_ibuf(struct ibuf *, const struct ibuf *);
int ibuf_get(struct ibuf *, void *, size_t);
int ibuf_get_ibuf(struct ibuf *, size_t, struct ibuf *);
int ibuf_get_n8(struct ibuf *, uint8_t *);
int ibuf_get_n16(struct ibuf *, uint16_t *);
int ibuf_get_n32(struct ibuf *, uint32_t *);
int ibuf_get_n64(struct ibuf *, uint64_t *);
int ibuf_get_h16(struct ibuf *, uint16_t *);
int ibuf_get_h32(struct ibuf *, uint32_t *);
int ibuf_get_h64(struct ibuf *, uint64_t *);
int ibuf_skip(struct ibuf *, size_t);
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_clear(struct msgbuf *);
uint32_t msgbuf_queuelen(struct msgbuf *);
int msgbuf_write(struct msgbuf *);
void msgbuf_drain(struct msgbuf *, size_t);
/* imsg.c */
void imsg_init(struct imsgbuf *, int);
ssize_t imsg_read(struct imsgbuf *);
ssize_t imsg_get(struct imsgbuf *, struct imsg *);
int imsg_get_ibuf(struct imsg *, struct ibuf *);
int imsg_get_data(struct imsg *, void *, size_t);
int imsg_get_fd(struct imsg *);
uint32_t imsg_get_id(struct imsg *);
size_t imsg_get_len(struct imsg *);
pid_t imsg_get_pid(struct imsg *);
uint32_t imsg_get_type(struct imsg *);
int imsg_forward(struct imsgbuf *, struct imsg *);
int imsg_compose(struct imsgbuf *, uint32_t, uint32_t, pid_t, int,
const void *, uint16_t);
const void *, size_t);
int imsg_composev(struct imsgbuf *, uint32_t, uint32_t, pid_t, int,
const struct iovec *, int);
struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, uint16_t);
int imsg_add(struct ibuf *, const void *, uint16_t);
int imsg_compose_ibuf(struct imsgbuf *, uint32_t, uint32_t, pid_t,
struct ibuf *);
struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, size_t);
int imsg_add(struct ibuf *, const void *, size_t);
void imsg_close(struct imsgbuf *, struct ibuf *);
void imsg_free(struct imsg *);
int imsg_flush(struct imsgbuf *);

31
compat/ntohll.c Normal file
View File

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

View File

@ -17,6 +17,7 @@
#include <sys/types.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "compat.h"

View File

@ -19,17 +19,30 @@
#include <sys/types.h>
#include <sys/un.h>
#include <systemd/sd-bus.h>
#include <systemd/sd-daemon.h>
#include <systemd/sd-login.h>
#include <systemd/sd-id128.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tmux.h"
int
systemd_activated(void)
{
return (sd_listen_fds(0) >= 1);
}
int
systemd_create_socket(int flags, char **cause)
{
int fds;
int fd;
struct sockaddr_un sa;
int addrlen = sizeof sa;
socklen_t addrlen = sizeof sa;
fds = sd_listen_fds(0);
if (fds > 1) { /* too many file descriptors */
@ -56,3 +69,160 @@ fail:
xasprintf(cause, "systemd socket error (%s)", strerror(errno));
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;
}
/*
* Make sure that the session shells are terminated with SIGHUP since
* bash and friends tend to ignore SIGTERM.
*/
r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", 1);
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
/*
* Inherit the slice from the parent process, or default to
* "app-tmux.slice" if that fails.
*/
r = sd_pid_get_user_slice(parent_pid, &slice);
if (r < 0) {
slice = xstrdup("app-tmux.slice");
}
r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
free(slice);
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
/* PIDs to add to the scope: length - 1 array of uint32_t. */
r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
/* Clean up the scope even if it fails. */
r = sd_bus_message_append(m, "(sv)", "CollectMode", "s",
"inactive-or-failed");
if (r < 0) {
xasprintf(cause, "failed to append to properties: %s",
strerror(-r));
goto finish;
}
/* End properties array. */
r = sd_bus_message_close_container(m);
if (r < 0) {
xasprintf(cause, "failed to end properties array: %s",
strerror(-r));
goto finish;
}
/* aux is currently unused and should be passed an empty array. */
r = sd_bus_message_append(m, "a(sa(sv))", 0);
if (r < 0) {
xasprintf(cause, "failed to append to bus message: %s",
strerror(-r));
goto finish;
}
/* Call the method with a timeout of 1 second = 1e6 us. */
r = sd_bus_call(bus, m, 1000000, &error, &reply);
if (r < 0) {
if (error.message != NULL) {
/* We have a specific error message from sd-bus. */
xasprintf(cause, "StartTransientUnit call failed: %s",
error.message);
} else {
xasprintf(cause, "StartTransientUnit call failed: %s",
strerror(-r));
}
goto finish;
}
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
AC_INIT([tmux], 3.3)
AC_INIT([tmux], next-3.5)
AC_PREREQ([2.60])
AC_CONFIG_AUX_DIR(etc)
@ -44,7 +44,7 @@ fi
# Set up the compiler in two different ways and say yes we may want to install.
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_CC_C99
m4_version_prereq(2.70, [AC_PROG_CC], [AC_PROG_CC_C99])
AC_PROG_CPP
AC_PROG_EGREP
AC_PROG_INSTALL
@ -148,7 +148,7 @@ AC_CHECK_FUNCS([ \
prctl \
proc_pidinfo \
getpeerucred \
sysconf \
sysconf
])
# Check for functions with a compatibility implementation.
@ -165,7 +165,9 @@ AC_REPLACE_FUNCS([ \
getpeereid \
getline \
getprogname \
htonll \
memmem \
ntohll \
setenv \
setproctitle \
strcasestr \
@ -267,6 +269,12 @@ if test "x$found_libevent" = xno; then
AC_MSG_ERROR("libevent not found")
fi
# Look for yacc.
AC_CHECK_PROG(found_yacc, $YACC, yes, no)
if test "x$found_yacc" = xno; then
AC_MSG_ERROR("yacc not found")
fi
# Look for ncurses or curses. Try pkg-config first then directly for the
# library.
PKG_CHECK_MODULES(
@ -309,7 +317,7 @@ fi
if test "x$found_ncurses" = xno; then
AC_SEARCH_LIBS(
setupterm,
[tinfo ncurses ncursesw],
[tinfo terminfo ncurses ncursesw],
found_ncurses=yes,
found_ncurses=no
)
@ -344,6 +352,10 @@ else
AC_MSG_ERROR("curses not found")
fi
fi
AC_CHECK_FUNCS([ \
tiparm \
tiparm_s \
])
# Look for utempter.
AC_ARG_ENABLE(
@ -373,6 +385,15 @@ AC_ARG_ENABLE(
AS_HELP_STRING(--enable-utf8proc, use utf8proc if it is installed)
)
if test "x$enable_utf8proc" = xyes; then
PKG_CHECK_MODULES(
LIBUTF8PROC,
libutf8proc,
[
AM_CPPFLAGS="$LIBUTF8PROC_CFLAGS $AM_CPPFLAGS"
CPPFLAGS="$LIBUTF8PROC_CFLAGS $SAVED_CPPFLAGS"
LIBS="$LIBUTF8PROC_LIBS $LIBS"
]
)
AC_CHECK_HEADER(utf8proc.h, enable_utf8proc=yes, enable_utf8proc=no)
if test "x$enable_utf8proc" = xyes; then
AC_SEARCH_LIBS(
@ -414,10 +435,35 @@ if test x"$enable_systemd" = xyes; then
fi
fi
AM_CONDITIONAL(HAVE_SYSTEMD, [test "x$found_systemd" = xyes])
AC_ARG_ENABLE(
cgroups,
AS_HELP_STRING(--disable-cgroups, disable adding panes to new cgroups with systemd)
)
if test "x$enable_cgroups" = x; then
# Default to the same as $enable_systemd.
enable_cgroups=$enable_systemd
fi
if test "x$enable_cgroups" = xyes; then
if test "x$found_systemd" = xyes; then
AC_DEFINE(ENABLE_CGROUPS)
else
AC_MSG_ERROR("cgroups requires systemd to be enabled")
fi
fi
# Enable sixel support.
AC_ARG_ENABLE(
sixel,
AS_HELP_STRING(--enable-sixel, enable sixel images)
)
if test "x$enable_sixel" = xyes; then
AC_DEFINE(ENABLE_SIXEL)
fi
AM_CONDITIONAL(ENABLE_SIXEL, [test "x$enable_sixel" = xyes])
# Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well.
AC_MSG_CHECKING(for b64_ntop)
AC_LINK_IFELSE([AC_LANG_PROGRAM(
AC_LINK_IFELSE([AC_LANG_PROGRAM(
[
#include <sys/types.h>
#include <netinet/in.h>
@ -915,6 +961,19 @@ AM_CONDITIONAL(IS_HPUX, test "x$PLATFORM" = xhpux)
AM_CONDITIONAL(IS_HAIKU, test "x$PLATFORM" = xhaiku)
AM_CONDITIONAL(IS_UNKNOWN, test "x$PLATFORM" = xunknown)
# Set the default lock command
DEFAULT_LOCK_CMD="lock -np"
AC_MSG_CHECKING(lock-command)
if test "x$PLATFORM" = xlinux; then
AC_CHECK_PROG(found_vlock, vlock, yes, no)
if test "x$found_vlock" = xyes; then
DEFAULT_LOCK_CMD="vlock"
fi
fi
AC_MSG_RESULT($DEFAULT_LOCK_CMD)
AC_SUBST(DEFAULT_LOCK_CMD)
# Save our CFLAGS/CPPFLAGS/LDFLAGS for the Makefile and restore the old user
# variables.
AC_SUBST(AM_CPPFLAGS)

View File

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

View File

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

View File

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

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

View File

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

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_CHARACTER 0x10000
#define FORMAT_COLOUR 0x20000
#define FORMAT_CLIENTS 0x40000
/* Limit on recursion. */
#define FORMAT_LOOP_LIMIT 100
@ -1125,7 +1126,6 @@ format_cb_mouse_word(struct format_tree *ft)
struct window_pane *wp;
struct grid *gd;
u_int x, y;
char *s;
if (!ft->m.valid)
return (NULL);
@ -1138,13 +1138,32 @@ format_cb_mouse_word(struct format_tree *ft)
if (!TAILQ_EMPTY(&wp->modes)) {
if (TAILQ_FIRST(&wp->modes)->mode == &window_copy_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);
}
gd = wp->base.grid;
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. */
static void *
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));
}
/* 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. */
static void *
format_cb_alternate_on(struct format_tree *ft)
@ -1865,12 +1950,24 @@ format_cb_pane_input_off(struct format_tree *ft)
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. */
static void *
format_cb_pane_last(struct format_tree *ft)
{
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("0"));
}
@ -2045,6 +2142,18 @@ format_cb_scroll_region_upper(struct format_tree *ft)
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. */
static void *
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,
format_cb_mouse_button_flag
},
{ "mouse_hyperlink", FORMAT_TABLE_STRING,
format_cb_mouse_hyperlink
},
{ "mouse_line", FORMAT_TABLE_STRING,
format_cb_mouse_line
},
@ -2801,6 +2913,12 @@ static const struct format_table_entry format_table[] = {
{ "mouse_standard_flag", FORMAT_TABLE_STRING,
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,
format_cb_mouse_utf8_flag
},
@ -2930,6 +3048,9 @@ static const struct format_table_entry format_table[] = {
{ "pane_tty", FORMAT_TABLE_STRING,
format_cb_pane_tty
},
{ "pane_unseen_changes", FORMAT_TABLE_STRING,
format_cb_pane_unseen_changes
},
{ "pane_width", FORMAT_TABLE_STRING,
format_cb_pane_width
},
@ -2942,6 +3063,9 @@ static const struct format_table_entry format_table[] = {
{ "scroll_region_upper", FORMAT_TABLE_STRING,
format_cb_scroll_region_upper
},
{ "server_sessions", FORMAT_TABLE_STRING,
format_cb_server_sessions
},
{ "session_activity", FORMAT_TABLE_TIME,
format_cb_session_activity
},
@ -3387,12 +3511,12 @@ format_quote_style(const char *s)
}
/* Make a prettier time. */
static char *
format_pretty_time(time_t t)
char *
format_pretty_time(time_t t, int seconds)
{
struct tm now_tm, tm;
time_t now, age;
char s[6];
char s[9];
time(&now);
if (now < t)
@ -3404,7 +3528,10 @@ format_pretty_time(time_t t)
/* Last 24 hours. */
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));
}
@ -3509,7 +3636,7 @@ found:
if (t == 0)
return (NULL);
if (modifiers & FORMAT_PRETTY)
found = format_pretty_time(t);
found = format_pretty_time(t, 0);
else {
if (time_format != NULL) {
localtime_r(&t, &tm);
@ -3539,18 +3666,43 @@ found:
}
if (modifiers & FORMAT_QUOTE_SHELL) {
saved = found;
found = xstrdup(format_quote_shell(saved));
found = format_quote_shell(saved);
free(saved);
}
if (modifiers & FORMAT_QUOTE_STYLE) {
saved = found;
found = xstrdup(format_quote_style(saved));
found = format_quote_style(saved);
free(saved);
}
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 *
format_strip(const char *s)
{
@ -3583,7 +3735,9 @@ format_skip(const char *s, const char *end)
for (; *s != '\0'; s++) {
if (*s == '#' && s[1] == '{')
brackets++;
if (*s == '#' && strchr(",#{}:", s[1]) != NULL) {
if (*s == '#' &&
s[1] != '\0' &&
strchr(",#{}:", s[1]) != NULL) {
s++;
continue;
}
@ -3697,7 +3851,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
cp++;
/* 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_add_modifier(&list, count, cp, 1, NULL, 0);
cp++;
@ -3732,7 +3886,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s,
argc = 0;
/* 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, ":;");
if (end == NULL)
break;
@ -4025,6 +4179,40 @@ format_loop_panes(struct format_expand_state *es, const char *fmt)
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 *
format_replace_expression(struct format_modifier *mexp,
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':
modifiers |= FORMAT_PANES;
break;
case 'L':
modifiers |= FORMAT_CLIENTS;
break;
}
} else if (fm->size == 2) {
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? */
if (modifiers & FORMAT_LITERAL) {
value = xstrdup(copy);
format_log(es, "literal string is '%s'", copy);
value = format_unescape(copy);
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);
if (value == NULL)
goto fail;
} else if (modifiers & FORMAT_CLIENTS) {
value = format_loop_clients(es, copy);
if (value == NULL)
goto fail;
} else if (modifiers & FORMAT_WINDOW_NAME) {
value = format_window_name(es, copy);
if (value == NULL)
@ -4603,7 +4799,7 @@ format_expand1(struct format_expand_state *es, const char *fmt)
{
struct format_tree *ft = es->ft;
char *buf, *out, *name;
const char *ptr, *s;
const char *ptr, *s, *style_end = NULL;
size_t off, len, n, outlen;
int ch, brackets;
char expanded[8192];
@ -4698,18 +4894,20 @@ format_expand1(struct format_expand_state *es, const char *fmt)
break;
fmt += n + 1;
continue;
case '[':
case '#':
/*
* If ##[ (with two or more #s), then it is a style and
* can be left for format_draw to handle.
*/
ptr = fmt;
n = 2;
ptr = fmt - (ch == '[');
n = 2 - (ch == '[');
while (*ptr == '#') {
ptr++;
n++;
}
if (*ptr == '[') {
style_end = format_skip(fmt - 2, "]");
format_log(es, "found #*%zu[", n);
while (len - off < n + 2) {
buf = xreallocarray(buf, 2, len);
@ -4732,10 +4930,12 @@ format_expand1(struct format_expand_state *es, const char *fmt)
continue;
default:
s = NULL;
if (ch >= 'A' && ch <= 'Z')
s = format_upper[ch - 'A'];
else if (ch >= 'a' && ch <= 'z')
s = format_lower[ch - 'a'];
if (fmt > style_end) { /* skip inside #[] */
if (ch >= 'A' && ch <= 'Z')
s = format_upper[ch - 'A'];
else if (ch >= 'a' && ch <= 'z')
s = format_lower[ch - 'a'];
}
if (s == NULL) {
while (len - off < 3) {
buf = xreallocarray(buf, 2, len);
@ -5057,3 +5257,20 @@ format_grid_line(struct grid *gd, u_int y)
}
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);
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. */
const struct grid_cell grid_default_cell = {
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 8, 0
};
/*
@ -45,15 +45,15 @@ const struct grid_cell grid_default_cell = {
* appears in the grid - because of this, they are always extended cells.
*/
static const struct grid_cell grid_padding_cell = {
{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0
{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0
};
/* Cleared grid cell data. */
static const struct grid_cell grid_cleared_cell = {
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0
};
static const struct grid_cell_entry grid_cleared_entry = {
GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } }
{ .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
};
/* Store cell in entry. */
@ -90,6 +90,8 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
return (1);
if (gc->us != 0) /* only supports 256 or RGB */
return (1);
if (gc->link != 0)
return (1);
return (0);
}
@ -131,6 +133,7 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
gee->fg = gc->fg;
gee->bg = gc->bg;
gee->us = gc->us;
gee->link = gc->link;
return (gee);
}
@ -231,6 +234,8 @@ grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
return (0);
if (gc1->attr != gc2->attr || gc1->flags != gc2->flags)
return (0);
if (gc1->link != gc2->link)
return (0);
return (1);
}
@ -399,6 +404,7 @@ grid_scroll_history(struct grid *gd, u_int bg)
gd->hscrolled++;
grid_compact_line(&gd->linedata[gd->hsize]);
gd->linedata[gd->hsize].time = current_time;
gd->hsize++;
}
@ -438,6 +444,7 @@ grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
/* Move the line into the history. */
memcpy(gl_history, gl_upper, sizeof *gl_history);
gl_history->time = current_time;
/* Then move the region up and clear the bottom line. */
memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
@ -507,6 +514,7 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
gc->fg = gee->fg;
gc->bg = gee->bg;
gc->us = gee->us;
gc->link = gee->link;
utf8_to_data(gee->data, &gc->data);
}
return;
@ -520,8 +528,9 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
gc->bg = gce->data.bg;
if (gce->flags & GRID_FLAG_BG256)
gc->bg |= COLOUR_FLAG_256;
gc->us = 0;
gc->us = 8;
utf8_set(&gc->data, gce->data.data);
gc->link = 0;
}
/* Get cell for reading. */
@ -852,28 +861,60 @@ grid_string_cells_us(const struct grid_cell *gc, int *values)
/* Add on SGR code. */
static void
grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
int *oldc, size_t nnewc, size_t noldc, int escape_c0)
int *oldc, size_t nnewc, size_t noldc, int flags)
{
u_int i;
char tmp[64];
int reset = (n != 0 && s[0] == 0);
if (nnewc != 0 &&
(nnewc != noldc ||
memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0 ||
(n != 0 && s[0] == 0))) {
if (escape_c0)
strlcat(buf, "\\033[", len);
if (nnewc == 0)
return; /* no code to add */
if (!reset &&
nnewc == noldc &&
memcmp(newc, oldc, nnewc * sizeof newc[0]) == 0)
return; /* no reset and colour unchanged */
if (reset && (newc[0] == 49 || newc[0] == 39))
return; /* reset and colour default */
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\033[", len);
else
strlcat(buf, "\033[", len);
for (i = 0; i < nnewc; i++) {
if (i + 1 < nnewc)
xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
else
strlcat(buf, "\033[", len);
for (i = 0; i < nnewc; i++) {
if (i + 1 < nnewc)
xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
else
xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
strlcat(buf, tmp, len);
}
strlcat(buf, "m", len);
xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
strlcat(buf, tmp, len);
}
strlcat(buf, "m", len);
}
static int
grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id,
const char *uri, int flags)
{
char *tmp;
if (strlen(uri) + strlen(id) + 17 >= len)
return (0);
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\033]8;", len);
else
strlcat(buf, "\033]8;", len);
if (*id != '\0') {
xasprintf(&tmp, "id=%s;", id);
strlcat(buf, tmp, len);
free(tmp);
} else
strlcat(buf, ";", len);
strlcat(buf, uri, len);
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\033\\\\", len);
else
strlcat(buf, "\033\\", len);
return (1);
}
/*
@ -882,14 +923,16 @@ grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
*/
static void
grid_string_cells_code(const struct grid_cell *lastgc,
const struct grid_cell *gc, char *buf, size_t len, int escape_c0)
const struct grid_cell *gc, char *buf, size_t len, int flags,
struct screen *sc, int *has_link)
{
int oldc[64], newc[64], s[128];
size_t noldc, nnewc, n, i;
u_int attr = gc->attr, lastattr = lastgc->attr;
char tmp[64];
int oldc[64], newc[64], s[128];
size_t noldc, nnewc, n, i;
u_int attr = gc->attr, lastattr = lastgc->attr;
char tmp[64];
const char *uri, *id;
struct {
static const struct {
u_int mask;
u_int code;
} attrs[] = {
@ -913,7 +956,7 @@ grid_string_cells_code(const struct grid_cell *lastgc,
for (i = 0; i < nitems(attrs); i++) {
if (((~attr & attrs[i].mask) &&
(lastattr & attrs[i].mask)) ||
(lastgc->us != 0 && gc->us == 0)) {
(lastgc->us != 8 && gc->us == 8)) {
s[n++] = 0;
lastattr &= GRID_ATTR_CHARSET;
break;
@ -928,7 +971,7 @@ grid_string_cells_code(const struct grid_cell *lastgc,
/* Write the attributes. */
*buf = '\0';
if (n > 0) {
if (escape_c0)
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\033[", len);
else
strlcat(buf, "\033[", len);
@ -950,46 +993,59 @@ grid_string_cells_code(const struct grid_cell *lastgc,
nnewc = grid_string_cells_fg(gc, newc);
noldc = grid_string_cells_fg(lastgc, oldc);
grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
escape_c0);
flags);
/* If the background colour changed, append its parameters. */
nnewc = grid_string_cells_bg(gc, newc);
noldc = grid_string_cells_bg(lastgc, oldc);
grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
escape_c0);
flags);
/* If the underscore colour changed, append its parameters. */
nnewc = grid_string_cells_us(gc, newc);
noldc = grid_string_cells_us(lastgc, oldc);
grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
escape_c0);
flags);
/* Append shift in/shift out if needed. */
if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) {
if (escape_c0)
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\016", len); /* SO */
else
strlcat(buf, "\016", len); /* SO */
}
if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) {
if (escape_c0)
if (flags & GRID_STRING_ESCAPE_SEQUENCES)
strlcat(buf, "\\017", len); /* SI */
else
strlcat(buf, "\017", len); /* SI */
}
/* Add hyperlink if changed. */
if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) {
if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) {
*has_link = grid_string_cells_add_hyperlink(buf, len,
id, uri, flags);
} else if (*has_link) {
grid_string_cells_add_hyperlink(buf, len, "", "",
flags);
*has_link = 0;
}
}
}
/* Convert cells into a string. */
char *
grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
struct grid_cell **lastgc, int with_codes, int escape_c0, int trim)
struct grid_cell **lastgc, int flags, struct screen *s)
{
struct grid_cell gc;
static struct grid_cell lastgc1;
const char *data;
char *buf, code[128];
char *buf, code[8192];
size_t len, off, size, codelen;
u_int xx;
u_int xx, end;
int has_link = 0;
const struct grid_line *gl;
if (lastgc != NULL && *lastgc == NULL) {
@ -1002,16 +1058,20 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
off = 0;
gl = grid_peek_line(gd, py);
if (flags & GRID_STRING_EMPTY_CELLS)
end = gl->cellsize;
else
end = gl->cellused;
for (xx = px; xx < px + nx; xx++) {
if (gl == NULL || xx >= gl->cellused)
if (gl == NULL || xx >= end)
break;
grid_get_cell(gd, xx, py, &gc);
if (gc.flags & GRID_FLAG_PADDING)
continue;
if (with_codes) {
if (flags & GRID_STRING_WITH_SEQUENCES) {
grid_string_cells_code(*lastgc, &gc, code, sizeof code,
escape_c0);
flags, s, &has_link);
codelen = strlen(code);
memcpy(*lastgc, &gc, sizeof **lastgc);
} else
@ -1019,7 +1079,9 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
data = gc.data.data;
size = gc.data.size;
if (escape_c0 && size == 1 && *data == '\\') {
if ((flags & GRID_STRING_ESCAPE_SEQUENCES) &&
size == 1 &&
*data == '\\') {
data = "\\\\";
size = 2;
}
@ -1037,7 +1099,19 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
off += size;
}
if (trim) {
if (has_link) {
grid_string_cells_add_hyperlink(code, sizeof code, "", "",
flags);
codelen = strlen(code);
while (len < off + size + codelen + 1) {
buf = xreallocarray(buf, 2, len);
len *= 2;
}
memcpy(buf + off, code, codelen);
off += codelen;
}
if (flags & GRID_STRING_TRIM_SPACES) {
while (off > 0 && buf[off - 1] == ' ')
off--;
}

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);
}

603
image-sixel.c Normal file
View File

@ -0,0 +1,603 @@
/* $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;
}
if (ncolours == 0)
return (NULL);
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,
.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[] = {
@ -416,7 +430,7 @@ input_key_write(const char *from, struct bufferevent *bev, const char *data,
int
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;
struct utf8_data ud;
char tmp[64], modifier;
@ -468,15 +482,23 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key)
key &= ~KEYC_KEYPAD;
if (~s->mode & MODE_KCURSOR)
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))
ike = input_key_get(key & ~KEYC_META);
if (ike == NULL && (key & KEYC_CURSOR))
ike = input_key_get(key & ~KEYC_CURSOR);
if (ike == NULL && (key & KEYC_KEYPAD))
ike = input_key_get(key & ~KEYC_KEYPAD);
if (ike == NULL && (key & KEYC_EXTENDED))
ike = input_key_get(key & ~KEYC_EXTENDED);
if (ike != NULL) {
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))
input_key_write(__func__, bev, "\033", 1);
input_key_write(__func__, bev, ike->data, strlen(ike->data));

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

31
job.c
View File

@ -77,19 +77,28 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
struct environ *env;
pid_t pid;
int nullfd, out[2], master;
const char *home;
const char *home, *shell;
sigset_t set, oldset;
struct winsize ws;
char **argvp, tty[TTY_NAME_MAX];
char **argvp, tty[TTY_NAME_MAX], *argv0;
/*
* Do not set TERM during .tmux.conf, it is nice to be able to use
* if-shell to decide on default-terminal based on outside TERM.
* Do not set TERM during .tmux.conf (second argument here), it is nice
* to be able to use if-shell to decide on default-terminal based on
* outside TERM.
*/
env = environ_for_session(s, !cfg_finished);
if (e != NULL)
environ_copy(e, env);
if (s != NULL)
shell = options_get_string(s->options, "default-shell");
else
shell = options_get_string(global_s_options, "default-shell");
if (!checkshell(shell))
shell = _PATH_BSHELL;
argv0 = shell_argv0(shell, 0);
sigfillset(&set);
sigprocmask(SIG_BLOCK, &set, &oldset);
@ -105,10 +114,11 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
}
if (cmd == NULL) {
cmd_log_argv(argc, argv, "%s:", __func__);
log_debug("%s: cwd=%s", __func__, cwd == NULL ? "" : cwd);
log_debug("%s: cwd=%s, shell=%s", __func__,
cwd == NULL ? "" : cwd, shell);
} else {
log_debug("%s: cmd=%s, cwd=%s", __func__, cmd,
cwd == NULL ? "" : cwd);
log_debug("%s: cmd=%s, cwd=%s, shell=%s", __func__, cmd,
cwd == NULL ? "" : cwd, shell);
}
switch (pid) {
@ -150,7 +160,8 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
closefrom(STDERR_FILENO + 1);
if (cmd != NULL) {
execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
setenv("SHELL", shell, 1);
execl(shell, argv0, "-c", cmd, (char *)NULL);
fatal("execl failed");
} else {
argvp = cmd_copy_argv(argc, argv);
@ -161,6 +172,7 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
sigprocmask(SIG_SETMASK, &oldset, NULL);
environ_free(env);
free(argv0);
job = xmalloc(sizeof *job);
job->state = JOB_RUNNING;
@ -194,12 +206,13 @@ job_run(const char *cmd, int argc, char **argv, struct environ *e, struct sessio
fatalx("out of memory");
bufferevent_enable(job->event, EV_READ|EV_WRITE);
log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid);
log_debug("run job %p: %s, pid %ld", job, job->cmd, (long)job->pid);
return (job);
fail:
sigprocmask(SIG_SETMASK, &oldset, NULL);
environ_free(env);
free(argv0);
return (NULL);
}

View File

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

View File

@ -460,6 +460,10 @@ out:
strlcat(out, "I", sizeof out);
if (saved & KEYC_BUILD_MODIFIERS)
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);
}
return (out);

View File

@ -162,8 +162,10 @@ layout_parse(struct window *w, const char *layout, char **cause)
u_short csum;
/* Check validity. */
if (sscanf(layout, "%hx,", &csum) != 1)
if (sscanf(layout, "%hx,", &csum) != 1) {
*cause = xstrdup("invalid layout");
return (-1);
}
layout += 5;
if (csum != layout_checksum(layout)) {
*cause = xstrdup("invalid layout");
@ -228,7 +230,7 @@ layout_parse(struct window *w, const char *layout, char **cause)
/* Check the new layout. */
if (!layout_check(lc)) {
*cause = xstrdup("size mismatch after applying layout");
return (-1);
goto fail;
}
/* Resize to the layout size. */

177
menu.c
View File

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

View File

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

View File

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

View File

@ -41,6 +41,9 @@ static const char *options_table_clock_mode_style_list[] = {
static const char *options_table_status_list[] = {
"off", "on", "2", "3", "4", "5", NULL
};
static const char *options_table_message_line_list[] = {
"0", "1", "2", "3", "4", NULL
};
static const char *options_table_status_keys_list[] = {
"emacs", "vi", NULL
};
@ -81,12 +84,18 @@ static const char *options_table_window_size_list[] = {
static const char *options_table_remain_on_exit_list[] = {
"off", "on", "failed", NULL
};
static const char *options_table_destroy_unattached_list[] = {
"off", "on", "keep-last", "keep-group", NULL
};
static const char *options_table_detach_on_destroy_list[] = {
"off", "on", "no-detached", NULL
"off", "on", "no-detached", "previous", "next", NULL
};
static const char *options_table_extended_keys_list[] = {
"off", "on", "always", NULL
};
static const char *options_table_allow_passthrough_list[] = {
"off", "on", "all", NULL
};
/* Status line format. */
#define OPTIONS_TABLE_STATUS_FORMAT1 \
@ -276,7 +285,7 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SERVER,
.minimum = 0,
.maximum = INT_MAX,
.default_num = 500,
.default_num = 10,
.unit = "milliseconds",
.text = "Time to wait before assuming a key is Escape."
},
@ -320,6 +329,42 @@ const struct options_table_entry options_table[] = {
"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",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SERVER,
@ -362,7 +407,8 @@ const struct options_table_entry options_table[] = {
.scope = OPTIONS_TABLE_SERVER,
.flags = OPTIONS_TABLE_IS_ARRAY,
.default_str = "xterm*:clipboard:ccolour:cstyle:focus:title,"
"screen*:title",
"screen*:title,"
"rxvt*:ignorefkeys",
.separator = ",",
.text = "List of terminal features, used if they cannot be "
"automatically detected."
@ -440,11 +486,12 @@ const struct options_table_entry options_table[] = {
},
{ .name = "destroy-unattached",
.type = OPTIONS_TABLE_FLAG,
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_destroy_unattached_list,
.default_num = 0,
.text = "Whether to destroy sessions when they have no attached "
"clients."
"clients, or keep the last session whether in the group."
},
{ .name = "detach-on-destroy",
@ -523,7 +570,7 @@ const struct options_table_entry options_table[] = {
{ .name = "lock-command",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "lock -np",
.default_str = TMUX_LOCK_CMD,
.text = "Shell command to run to lock a client."
},
@ -537,13 +584,21 @@ const struct options_table_entry options_table[] = {
"'mode-keys' is set to 'vi'."
},
{ .name = "message-line",
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SESSION,
.choices = options_table_message_line_list,
.default_num = 0,
.text = "Position (line) of messages and the command prompt."
},
{ .name = "message-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=yellow,fg=black",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of the command prompt."
.text = "Style of messages and the command prompt."
},
{ .name = "mouse",
@ -802,11 +857,14 @@ const struct options_table_entry options_table[] = {
},
{ .name = "allow-passthrough",
.type = OPTIONS_TABLE_FLAG,
.type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.choices = options_table_allow_passthrough_list,
.default_num = 0,
.text = "Whether applications are allowed to use the escape sequence "
"to bypass tmux."
"to bypass tmux. Can be 'off' (disallowed), 'on' (allowed "
"if the pane is visible), or 'all' (allowed even if the pane "
"is invisible)."
},
{ .name = "allow-rename",
@ -817,6 +875,14 @@ const struct options_table_entry options_table[] = {
"to rename windows."
},
{ .name = "allow-set-title",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
.default_num = 1,
.text = "Whether applications are allowed to use the escape sequence "
"to set the pane title."
},
{ .name = "alternate-screen",
.type = OPTIONS_TABLE_FLAG,
.scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE,
@ -916,8 +982,8 @@ const struct options_table_entry options_table[] = {
{ .name = "mode-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_WINDOW,
.default_str = "bg=yellow,fg=black",
.flags = OPTIONS_TABLE_IS_STYLE,
.default_str = "bg=yellow,fg=black",
.separator = ",",
.text = "Style of indicators and highlighting in modes."
},
@ -1259,6 +1325,7 @@ const struct options_table_entry options_table[] = {
OPTIONS_TABLE_HOOK("client-focus-out", ""),
OPTIONS_TABLE_HOOK("client-resized", ""),
OPTIONS_TABLE_HOOK("client-session-changed", ""),
OPTIONS_TABLE_HOOK("command-error", ""),
OPTIONS_TABLE_PANE_HOOK("pane-died", ""),
OPTIONS_TABLE_PANE_HOOK("pane-exited", ""),
OPTIONS_TABLE_PANE_HOOK("pane-focus-in", ""),

View File

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

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

11
popup.c
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,
palette);
}
screen_free(&s);
if (pd->md != NULL) {
c->overlay_check = NULL;
c->overlay_data = NULL;
@ -344,7 +345,7 @@ popup_make_pane(struct popup_data *pd, enum layout_type type)
u_int hlimit;
const char *shell;
window_unzoom(w);
window_unzoom(w, 1);
lc = layout_split_pane(wp, type, -1, 0);
hlimit = options_get_number(s->options, "history-limit");
@ -573,8 +574,8 @@ menu:
x = m->x - (pd->menu->width + 4) / 2;
else
x = 0;
pd->md = menu_prepare(pd->menu, 0, NULL, x, m->y, c, NULL,
popup_menu_done, pd);
pd->md = menu_prepare(pd->menu, 0, 0, NULL, x, m->y, c,
BOX_LINES_DEFAULT, NULL, NULL, NULL, NULL, popup_menu_done, pd);
c->flags |= CLIENT_REDRAWOVERLAY;
out:
@ -635,7 +636,7 @@ int
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,
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)
{
struct popup_data *pd;
@ -786,6 +787,8 @@ popup_editor(struct client *c, const char *buf, size_t len,
if (fd == -1)
return (-1);
f = fdopen(fd, "w");
if (f == NULL)
return (-1);
if (fwrite(buf, len, 1, f) != 1) {
fclose(f);
return (-1);

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);
if (peer_check_version(peer, &imsg) != 0) {
if (imsg.fd != -1)
close(imsg.fd);
fd = imsg_get_fd(&imsg);
if (fd != -1)
close(fd);
imsg_free(&imsg);
break;
}
@ -193,18 +194,13 @@ proc_start(const char *name)
log_debug("%s started (%ld): version %s, socket %s, protocol %d", name,
(long)getpid(), getversion(), socket_path, PROTOCOL_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
"; utf8proc %s"
log_debug("using utf8proc %s", utf8proc_version());
#endif
#ifdef NCURSES_VERSION
"; ncurses " NCURSES_VERSION
log_debug("using ncurses %s %06u", NCURSES_VERSION, NCURSES_VERSION_PATCH);
#endif
, event_get_version(), event_get_method()
#ifdef HAVE_UTF8PROC
, utf8proc_version()
#endif
);
tp = xcalloc(1, sizeof *tp);
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
(
printf '\033[1m\033[31m\033[42mabc\033[0m\033[31m\033[49mdef\033[39m\n'
printf '\033[100m bright bg \033[49m\n'
printf '\033[1m\033[31m\033[42mabc\033[0m\033[31m\033[49mdef\n'
printf '\033[39m\033[100m bright bg\n'
) | cmp - $TMP || 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
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!"
# 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
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
# 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
# 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
# style attributes: none, bold/bright, dim, underscore, blink, reverse, hidden,
# 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
# 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
#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:
@ -47,7 +47,7 @@ select-window -t :=3
#$3 for email (mutt)
new-session sula
new-window sula ; send-keys mutt Enter
#nah, probly betr not?:
#nah, probably better not?:
#send-keys -l z
#send-keys -l "thepassifdecide"
#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> put it in your statusline
#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-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

View File

@ -42,14 +42,14 @@ $TMUX send-keys -X begin-selection
$TMUX send-keys -X next-word-end
$TMUX send-keys -X next-word-end
$TMUX send-keys -X copy-selection
[ "$($TMUX show-buffer)" = "$(echo -e "words\n Indented")" ] || exit 1
[ "$($TMUX show-buffer)" = "$(printf "words\n Indented")" ] || exit 1
# Test that `next-word` wraps around un-indented line breaks.
$TMUX send-keys -X next-word
$TMUX send-keys -X begin-selection
$TMUX send-keys -X next-word
$TMUX send-keys -X copy-selection
[ "$($TMUX show-buffer)" = "$(echo -e "line\n")" ] || exit 1
[ "$($TMUX show-buffer)" = "$(printf "line\n")" ] || exit 1
# Test that `next-word-end` treats periods as letters.
$TMUX send-keys -X next-word
@ -63,14 +63,14 @@ $TMUX send-keys -X previous-word
$TMUX send-keys -X begin-selection
$TMUX send-keys -X next-word
$TMUX send-keys -X copy-selection
[ "$($TMUX show-buffer)" = "$(echo -e "line...\n")" ] || exit 1
[ "$($TMUX show-buffer)" = "$(printf "line...\n")" ] || exit 1
# Test that `previous-space` and `next-space` treat periods as letters.
$TMUX send-keys -X previous-space
$TMUX send-keys -X begin-selection
$TMUX send-keys -X next-space
$TMUX send-keys -X copy-selection
[ "$($TMUX show-buffer)" = "$(echo -e "line...\n")" ] || exit 1
[ "$($TMUX show-buffer)" = "$(printf "line...\n")" ] || exit 1
# Test that `next-word` and `next-word-end` treat other symbols as letters.
$TMUX send-keys -X begin-selection
@ -87,7 +87,7 @@ $TMUX send-keys -X previous-word
$TMUX send-keys -X begin-selection
$TMUX send-keys -X next-word
$TMUX send-keys -X copy-selection
[ "$($TMUX show-buffer)" = "$(echo -e "\$ym_bols[]{}\n ")" ] || exit 1
[ "$($TMUX show-buffer)" = "$(printf "\$ym_bols[]{}\n ")" ] || exit 1
# Test that `next-word-end` treats digits as letters
$TMUX send-keys -X next-word-end

View File

@ -41,14 +41,14 @@ $TMUX send-keys -X begin-selection
$TMUX send-keys -X next-word-end
$TMUX send-keys -X next-word-end
$TMUX send-keys -X copy-selection
[ "$($TMUX show-buffer)" = "$(echo -e "words\n Indented")" ] || exit 1
[ "$($TMUX show-buffer)" = "$(printf "words\n Indented")" ] || exit 1
# Test that `next-word` wraps around un-indented line breaks.
$TMUX send-keys -X next-word
$TMUX send-keys -X begin-selection
$TMUX send-keys -X next-word
$TMUX send-keys -X copy-selection
[ "$($TMUX show-buffer)" = "$(echo -e "line\nA")" ] || exit 1
[ "$($TMUX show-buffer)" = "$(printf "line\nA")" ] || exit 1
# Test that `next-word-end` does not treat periods as letters.
$TMUX send-keys -X next-word
@ -69,7 +69,7 @@ $TMUX send-keys -X previous-space
$TMUX send-keys -X begin-selection
$TMUX send-keys -X next-space
$TMUX send-keys -X copy-selection
[ "$($TMUX show-buffer)" = "$(echo -e "line...\n.")" ] || exit 1
[ "$($TMUX show-buffer)" = "$(printf "line...\n.")" ] || exit 1
# Test that `next-word` and `next-word-end` do not treat other symbols as letters.
$TMUX send-keys -X begin-selection
@ -85,7 +85,7 @@ $TMUX send-keys -X next-space
$TMUX send-keys -X begin-selection
$TMUX send-keys -X next-space
$TMUX send-keys -X copy-selection
[ "$($TMUX show-buffer)" = "$(echo -e "\$ym_bols[]{}\n ?")" ] || exit 1
[ "$($TMUX show-buffer)" = "$(printf "\$ym_bols[]{}\n ?")" ] || exit 1
# Test that `next-word-end` treats digits as letters
$TMUX send-keys -X next-word-end

View File

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

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)
TMUX="$TEST_TMUX -Ltest"
$TMUX kill-server 2>/dev/null
sleep 1
$TMUX -f/dev/null new -d 'sleep 1000' || exit 1
P=$($TMUX display -pt0:0.0 '#{pane_pid}')
$TMUX -f/dev/null new -d || exit 1
sleep 1
$TMUX kill-session -t0:
sleep 1
sleep 3
kill -0 $P 2>/dev/null && exit 1
$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"
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;
@ -34,7 +34,7 @@ regsub_copy(char **buf, size_t *len, const char *text, size_t start, size_t end)
}
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)
{
const char *cp;

View File

@ -40,7 +40,7 @@ resize_window(struct window *w, u_int sx, u_int sy, int xpixel, int ypixel)
/* If the window is zoomed, unzoom. */
zoomed = w->flags & WINDOW_ZOOMED;
if (zoomed)
window_unzoom(w);
window_unzoom(w, 1);
/* Resize the layout first. */
layout_resize(w, sx, sy);

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)
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_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_flush(struct screen_write_ctx *, int,
const char *);
static int screen_write_overwrite(struct screen_write_ctx *,
struct grid_cell *, u_int);
static const struct grid_cell *screen_write_combine(struct screen_write_ctx *,
const struct utf8_data *, u_int *);
static int screen_write_combine(struct screen_write_ctx *,
const struct grid_cell *);
struct screen_write_citem {
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;
if (ttyctx->allow_invisible_panes) {
if (session_has(c->session, wp->window))
return (1);
return (0);
}
if (c->session->curw->window != wp->window)
return (0);
if (wp->layout_cell == NULL)
@ -320,7 +325,9 @@ screen_write_reset(struct screen_write_ctx *ctx)
screen_reset_tabs(s);
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_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. */
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 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;
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;
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++)
screen_write_putc(ctx, &gc, 'q');
screen_write_putc(ctx, &gc, right ? 'u' : 'q');
screen_write_cell(ctx, &gc);
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);
}
@ -633,86 +691,55 @@ screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
/* Draw a menu on screen. */
void
screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu,
int choice, const struct grid_cell *choice_gc)
screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice,
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 grid_cell 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;
cx = s->cx;
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,
BOX_LINES_DEFAULT, &default_gc, menu->title);
screen_write_box(ctx, menu->width + 4, menu->count + 2, lines,
border_gc, menu->title);
for (i = 0; i < menu->count; i++) {
name = menu->items[i].name;
if (name == NULL) {
screen_write_cursormove(ctx, cx, cy + 1 + i, 0);
screen_write_hline(ctx, menu->width + 4, 1, 1);
} else {
if (choice >= 0 && i == (u_int)choice && *name != '-')
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;
screen_write_hline(ctx, width + 4, 1, 1, lines,
border_gc);
continue;
}
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);
}
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. */
void
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);
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 (xx = 0; xx < screen_size_x(s); xx++)
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)
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);
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)
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);
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)
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);
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 tty_ctx ttyctx;
#ifdef ENABLE_SIXEL
u_int sy = screen_size_y(s);
#endif
if (ny == 0)
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 (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 grid *gd = s->grid;
struct tty_ctx ttyctx;
u_int sy = screen_size_y(s);
if (ny == 0)
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 (ny > screen_size_y(s) - s->cy)
ny = screen_size_y(s) - s->cy;
if (ny > sy - s->cy)
ny = sy - s->cy;
if (ny == 0)
return;
@ -1190,6 +1252,11 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
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);
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)))
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);
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;
}
#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)
grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
else
@ -1294,6 +1371,11 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
struct tty_ctx ttyctx;
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);
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 grid *gd = s->grid;
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);
if (wrapped)
gl->flags |= GRID_LINE_WRAPPED;
log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
s->rupper, s->rlower);
rupper, rlower);
if (bg != ctx->bg) {
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) {
#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);
screen_write_collect_scroll(ctx, bg);
ctx->scrolled++;
@ -1375,6 +1469,11 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int 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++) {
grid_view_scroll_region_up(gd, s->rupper, s->rlower, 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)
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++)
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;
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);
ttyctx.bg = bg;
@ -1452,6 +1561,11 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
struct tty_ctx ttyctx;
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);
ttyctx.bg = bg;
@ -1475,6 +1589,11 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
struct tty_ctx ttyctx;
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);
ttyctx.bg = bg;
@ -1506,7 +1625,8 @@ screen_write_fullredraw(struct screen_write_ctx *ctx)
screen_write_collect_flush(ctx, 0, __func__);
screen_write_initctx(ctx, &ttyctx, 1);
ttyctx.redraw_cb(&ttyctx);
if (ttyctx.redraw_cb != NULL)
ttyctx.redraw_cb(&ttyctx);
}
/* 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,
ci->used);
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 grid *gd = s->grid;
const struct utf8_data *ud = &gc->data;
const struct utf8_data zwj = { "\342\200\215", 0, 3, 0 };
struct grid_line *gl;
struct grid_cell_entry *gce;
struct grid_cell tmp_gc, now_gc;
struct tty_ctx ttyctx;
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;
/* Ignore padding cells. */
if (gc->flags & GRID_FLAG_PADDING)
return;
/*
* If this is a zero width joiner, set the flag so the next character
* 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;
/* Get the previous cell to check for combining. */
if (screen_write_combine(ctx, gc) != 0)
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. */
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
* replace it.
*/
last = !(s->mode & MODE_WRAP);
if (s->cx <= sx - last - width)
not_wrap = !(s->mode & MODE_WRAP);
if (s->cx <= sx - not_wrap - width)
screen_write_set_cursor(ctx, s->cx + width, -1);
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. */
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. */
static const struct grid_cell *
screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud,
u_int *xx)
/* Combine a UTF-8 zero-width character onto the previous if necessary. */
static int
screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
{
struct screen *s = ctx->s;
struct grid *gd = s->grid;
static struct grid_cell gc;
u_int n;
const struct utf8_data *ud = &gc->data;
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)
return (NULL);
/*
* Is this character which makes no sense without being combined? If
* 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. */
if (ud->size == 0)
fatalx("UTF-8 data empty");
/* Cannot combine empty character or at left. */
if (ud->size < 2 || cx == 0)
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. */
for (n = 1; n <= s->cx; n++) {
grid_view_get_cell(gd, s->cx - n, s->cy, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
/* Find the cell to combine with. */
n = 1;
grid_view_get_cell(gd, cx - n, cy, &last);
if (cx != 1 && (last.flags & GRID_FLAG_PADDING)) {
n = 2;
grid_view_get_cell(gd, cx - n, cy, &last);
}
if (n > s->cx)
return (NULL);
*xx = s->cx - n;
if (n != last.data.width || (last.flags & GRID_FLAG_PADDING))
return (zero_width);
/* Check there is enough space. */
if (gc.data.size + ud->size > sizeof gc.data.data)
return (NULL);
/*
* Check if we need to combine characters. This could be zero width
* (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,
ud->data, (int)gc.data.size, gc.data.data, *xx, s->cy);
/* Check if this combined character would be too long. */
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. */
memcpy(gc.data.data + gc.data.size, ud->data, ud->size);
gc.data.size += ud->size;
memcpy(last.data.data + last.data.size, ud->data, 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. */
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 - 1, 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. */
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;
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.ptr = str;
ttyctx.ptr2 = (void *)flags;
ttyctx.num = len;
tty_write(tty_cmd_setselection, &ttyctx);
@ -2098,17 +2245,78 @@ screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len)
/* Write unmodified string. */
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;
screen_write_initctx(ctx, &ttyctx, 0);
ttyctx.ptr = str;
ttyctx.num = len;
ttyctx.allow_invisible_panes = allow_invisible_panes;
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;
/* Bail out if the image cannot be scaled. */
if (si == NULL)
return;
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. */
void
screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
@ -2124,7 +2332,8 @@ screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
screen_alternate_on(ctx->s, gc, cursor);
screen_write_initctx(ctx, &ttyctx, 1);
ttyctx.redraw_cb(&ttyctx);
if (ttyctx.redraw_cb != NULL)
ttyctx.redraw_cb(&ttyctx);
}
/* Turn alternate screen off. */
@ -2142,5 +2351,6 @@ screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
screen_alternate_off(ctx->s, gc, cursor);
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->default_cstyle = SCREEN_CURSOR_DEFAULT;
s->mode = MODE_CURSOR;
s->default_mode = 0;
s->ccolour = -1;
s->default_ccolour = -1;
s->tabs = NULL;
s->sel = NULL;
#ifdef ENABLE_SIXEL
TAILQ_INIT(&s->images);
#endif
s->write_list = NULL;
s->hyperlinks = NULL;
screen_reinit(s);
}
@ -118,6 +124,22 @@ screen_reinit(struct screen *s)
screen_clear_selection(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. */
@ -136,7 +158,13 @@ screen_free(struct screen *s)
grid_destroy(s->saved_grid);
grid_destroy(s->grid);
if (s->hyperlinks != NULL)
hyperlinks_free(s->hyperlinks);
screen_free_titles(s);
#ifdef ENABLE_SIXEL
image_free_all(s);
#endif
}
/* Reset tabs to default, eight spaces apart. */
@ -280,6 +308,10 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow,
if (sy != screen_size_y(s))
screen_resize_y(s, sy, eat_empty, &cy);
#ifdef ENABLE_SIXEL
image_free_all(s);
#endif
if (reflow)
screen_reflow(s, sx, &cx, &cy, cursor);
@ -368,7 +400,7 @@ screen_resize_y(struct screen *s, u_int sy, int eat_empty, u_int *cy)
/*
* Try to pull as much as possible out of scrolled history, if
* is is enabled.
* it is enabled.
*/
available = gd->hscrolled;
if (gd->flags & GRID_HISTORY && available > 0) {
@ -611,7 +643,7 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
* before copying back.
*/
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
@ -685,9 +717,9 @@ screen_mode_to_string(int mode)
if (mode & MODE_CURSOR_VERY_VISIBLE)
strlcat(tmp, "CURSOR_VERY_VISIBLE,", sizeof tmp);
if (mode & MODE_MOUSE_UTF8)
strlcat(tmp, "UTF8,", sizeof tmp);
strlcat(tmp, "MOUSE_UTF8,", sizeof tmp);
if (mode & MODE_MOUSE_SGR)
strlcat(tmp, "SGR,", sizeof tmp);
strlcat(tmp, "MOUSE_SGR,", sizeof tmp);
if (mode & MODE_BRACKETPASTE)
strlcat(tmp, "BRACKETPASTE,", sizeof tmp);
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_path(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 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)
{
struct mouse_event *m = &event->m;
struct session *s = c->session;
struct winlink *wl;
struct window_pane *wp;
struct session *s = c->session, *fs;
struct winlink *fwl;
struct window_pane *wp, *fwp;
u_int x, y, b, sx, sy, px, py;
int ignore = 0;
key_code key;
@ -621,6 +622,8 @@ server_client_check_mouse(struct client *c, struct key_event *event)
} else if (MOUSE_RELEASE(m->b)) {
type = UP;
x = m->x, y = m->y, b = m->lb;
if (m->sgr_type == 'm')
b = m->sgr_b;
log_debug("up at %u,%u", x, y);
} else {
if (c->flags & CLIENT_DOUBLECLICK) {
@ -641,7 +644,10 @@ server_client_check_mouse(struct client *c, struct key_event *event)
log_debug("triple-click at %u,%u", x, y);
goto have_event;
}
} else {
}
/* DOWN is the only remaining event type. */
if (type == NOTYPE) {
type = DOWN;
x = m->x, y = m->y, b = m->b;
log_debug("down at %u,%u", x, y);
@ -667,6 +673,7 @@ have_event:
/* Save the session. */
m->s = s->id;
m->w = -1;
m->wp = -1;
m->ignore = ignore;
/* Is this on the status line? */
@ -683,18 +690,42 @@ have_event:
case STYLE_RANGE_NONE:
return (KEYC_UNKNOWN);
case STYLE_RANGE_LEFT:
log_debug("mouse range: left");
where = STATUS_LEFT;
break;
case STYLE_RANGE_RIGHT:
log_debug("mouse range: right");
where = STATUS_RIGHT;
break;
case STYLE_RANGE_WINDOW:
wl = winlink_find_by_index(&s->windows,
sr->argument);
if (wl == NULL)
case STYLE_RANGE_PANE:
fwp = window_pane_find_by_id(sr->argument);
if (fwp == NULL)
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;
break;
}
@ -1754,6 +1785,25 @@ out:
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? */
static int
server_client_assume_paste(struct session *s)
@ -1818,7 +1868,7 @@ server_client_key_callback(struct cmdq_item *item, void *data)
struct key_binding *bd;
int xtimeout, flags;
struct cmd_find_state fs;
key_code key0;
key_code key0, prefix, prefix2;
/* Check the client is good to accept input. */
if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
@ -1862,8 +1912,14 @@ server_client_key_callback(struct cmdq_item *item, void *data)
if (KEYC_IS_MOUSE(key) && !options_get_number(s->options, "mouse"))
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. */
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;
/*
@ -1884,9 +1940,11 @@ table_changed:
* The prefix always takes precedence and forces a switch to the prefix
* 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));
if ((key0 == (key_code)options_get_number(s->options, "prefix") ||
key0 == (key_code)options_get_number(s->options, "prefix2")) &&
if ((key0 == (prefix & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS)) ||
key0 == (prefix2 & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS))) &&
strcmp(table->name, "prefix") != 0) {
server_client_set_key_table(c, "prefix");
server_status_client(c);
@ -2218,7 +2276,8 @@ server_client_check_pane_buffer(struct window_pane *wp)
}
wpo = control_pane_offset(c, wp, &flag);
if (wpo == NULL) {
off = 0;
if (!flag)
off = 0;
continue;
}
if (!flag)
@ -2714,6 +2773,7 @@ server_client_dispatch(struct imsg *imsg, void *arg)
break;
server_client_update_latest(c);
tty_resize(&c->tty);
tty_repeat_requests(&c->tty);
recalculate_sizes();
if (c->overlay_resize == NULL)
server_client_clear_overlay(c);
@ -2788,8 +2848,11 @@ server_client_command_done(struct cmdq_item *item, __unused void *data)
if (~c->flags & CLIENT_ATTACHED)
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);
}
return (CMD_RETURN_NORMAL);
}
@ -2940,14 +3003,14 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
case MSG_IDENTIFY_STDIN:
if (datalen != 0)
fatalx("bad MSG_IDENTIFY_STDIN size");
c->fd = imsg->fd;
log_debug("client %p IDENTIFY_STDIN %d", c, imsg->fd);
c->fd = imsg_get_fd(imsg);
log_debug("client %p IDENTIFY_STDIN %d", c, c->fd);
break;
case MSG_IDENTIFY_STDOUT:
if (datalen != 0)
fatalx("bad MSG_IDENTIFY_STDOUT size");
c->out_fd = imsg->fd;
log_debug("client %p IDENTIFY_STDOUT %d", c, imsg->fd);
c->out_fd = imsg_get_fd(imsg);
log_debug("client %p IDENTIFY_STDOUT %d", c, c->out_fd);
break;
case MSG_IDENTIFY_ENVIRON:
if (datalen == 0 || data[datalen - 1] != '\0')
@ -3210,3 +3273,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"
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
server_redraw_client(struct client *c)
@ -207,8 +206,8 @@ server_kill_window(struct window *w, int renumber)
if (session_detach(s, wl)) {
server_destroy_session_group(s);
break;
} else
server_redraw_session_group(s);
}
server_redraw_session_group(s);
}
if (renumber)
@ -385,9 +384,10 @@ server_destroy_session_group(struct session *s)
struct session_group *sg;
struct session *s1;
if ((sg = session_group_contains(s)) == NULL)
if ((sg = session_group_contains(s)) == NULL) {
server_destroy_session(s);
else {
session_destroy(s, 1, __func__);
} else {
TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) {
server_destroy_session(s);
session_destroy(s, 1, __func__);
@ -396,52 +396,55 @@ server_destroy_session_group(struct session *s)
}
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;
RB_FOREACH(s_loop, sessions, &sessions) {
if (s_loop == s)
continue;
if (s_out == NULL ||
timercmp(&s_loop->activity_time, &s_out->activity_time, <))
if (s_loop != s && (s_out == NULL || f(s_loop, s_out)))
s_out = s_loop;
}
return (s_out);
}
static struct session *
server_next_detached_session(struct session *s)
static int
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) {
if (s_loop == s || s_loop->attached)
continue;
if (s_out == NULL ||
timercmp(&s_loop->activity_time, &s_out->activity_time, <))
s_out = s_loop;
}
return (s_out);
static int
server_newer_detached_session(struct session *s_loop, struct session *s_out)
{
if (s_loop->attached)
return (0);
return (server_newer_session(s_loop, s_out));
}
void
server_destroy_session(struct session *s)
{
struct client *c;
struct session *s_new;
struct session *s_new = NULL;
int detach_on_destroy;
detach_on_destroy = options_get_number(s->options, "detach-on-destroy");
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)
s_new = server_next_detached_session(s);
else
s_new = server_find_session(s, server_newer_detached_session);
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;
TAILQ_FOREACH(c, &clients, entry) {
if (c->session != s)
continue;
c->session = NULL;
c->last_session = NULL;
server_client_set_session(c, s_new);
if (s_new == NULL)
c->flags |= CLIENT_EXIT;
@ -452,7 +455,8 @@ server_destroy_session(struct session *s)
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
@ -461,14 +465,29 @@ server_check_unattached(void)
RB_FOREACH(s, sessions, &sessions) {
if (s->attached != 0)
continue;
if (options_get_number (s->options, "destroy-unattached"))
session_destroy(s, 1, __func__);
switch (options_get_number(s->options, "destroy-unattached")) {
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__);
}
}
void
server_unzoom_window(struct window *w)
{
if (window_unzoom(w) == 0)
if (window_unzoom(w, 1) == 0)
server_redraw_window(w);
}

View File

@ -53,6 +53,8 @@ struct cmd_find_state marked_pane;
static u_int message_next;
struct message_list message_log;
time_t current_time;
static int server_loop(void);
static void server_send_exit(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);
key_bindings_init();
TAILQ_INIT(&message_log);
gettimeofday(&start_time, NULL);
#ifdef HAVE_SYSTEMD
@ -263,6 +264,8 @@ server_loop(void)
struct client *c;
u_int items;
current_time = time(NULL);
do {
items = cmdq_next(NULL);
TAILQ_FOREACH(c, &clients, entry) {

View File

@ -365,11 +365,9 @@ session_detach(struct session *s, struct winlink *wl)
session_group_synchronize_from(s);
if (RB_EMPTY(&s->windows)) {
session_destroy(s, 1, __func__);
if (RB_EMPTY(&s->windows))
return (1);
}
return (0);
return (0);
}
/* Return if session has window. */
@ -687,8 +685,10 @@ session_group_synchronize1(struct session *target, struct session *s)
TAILQ_INIT(&s->lastw);
TAILQ_FOREACH(wl, &old_lastw, sentry) {
wl2 = winlink_find_by_index(&s->windows, wl->idx);
if (wl2 != NULL)
if (wl2 != NULL) {
TAILQ_INSERT_TAIL(&s->lastw, wl2, sentry);
wl2->flags |= WINLINK_VISITED;
}
}
/* Then free the old winlinks list. */
@ -708,7 +708,7 @@ session_renumber_windows(struct session *s)
struct winlink *wl, *wl1, *wl_new;
struct winlinks old_wins;
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. */
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);
wl_new->flags |= wl->flags & WINLINK_ALERTFLAGS;
if (wl == marked_pane.wl)
marked_idx = wl_new->idx;
if (wl == s->curw)
new_curw_idx = wl_new->idx;
@ -735,12 +737,20 @@ session_renumber_windows(struct session *s)
memcpy(&old_lastw, &s->lastw, sizeof old_lastw);
TAILQ_INIT(&s->lastw);
TAILQ_FOREACH(wl, &old_lastw, sentry) {
wl->flags &= ~WINLINK_VISITED;
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);
wl_new->flags |= WINLINK_VISITED;
}
}
/* 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);
/* 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);
layout_init(w, sc->wp0);
w->active = NULL;
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. */
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;
}
/*
* 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)
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);
else if (chdir("/") == 0)
environ_set(child, "PWD", 0, "/");
@ -416,8 +429,8 @@ spawn_pane(struct spawn_context *sc, char **cause)
_exit(1);
/* Clean up file descriptors and signals and update the environment. */
closefrom(STDERR_FILENO + 1);
proc_clear_signals(server_proc, 1);
closefrom(STDERR_FILENO + 1);
sigprocmask(SIG_SETMASK, &oldset, NULL);
log_close();
environ_push(child);

114
status.c
View File

@ -263,6 +263,17 @@ status_line_size(struct client *c)
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. */
struct style_range *
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,
int ignore_keys, const char *fmt, ...)
{
struct timeval tv;
va_list ap;
struct timeval tv;
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_push_screen(c);
va_start(ap, fmt);
xvasprintf(&c->message_string, fmt, ap);
va_end(ap);
server_add_message("%s message: %s", c->name, c->message_string);
c->message_string = s;
server_add_message("%s message: %s", c->name, s);
/*
* 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 screen old_screen;
size_t len;
u_int lines, offset;
u_int lines, offset, messageline;
struct grid_cell gc;
struct format_tree *ft;
@ -546,6 +566,10 @@ status_message_redraw(struct client *c)
lines = 1;
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);
if (len > c->tty.sx)
len = c->tty.sx;
@ -555,11 +579,11 @@ status_message_redraw(struct client *c)
format_free(ft);
screen_write_start(&ctx, sl->active);
screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1);
screen_write_cursormove(&ctx, 0, lines - 1, 0);
screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines);
screen_write_cursormove(&ctx, 0, messageline, 0);
for (offset = 0; offset < c->tty.sx; offset++)
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)
screen_write_nputs(&ctx, len, &gc, "%s", c->message_string);
else
@ -695,7 +719,7 @@ status_prompt_redraw(struct client *c)
struct session *s = c->session;
struct screen old_screen;
u_int i, lines, offset, left, start, width;
u_int pcursor, pwidth;
u_int pcursor, pwidth, promptline;
struct grid_cell gc, cursorgc;
struct format_tree *ft;
@ -708,6 +732,10 @@ status_prompt_redraw(struct client *c)
lines = 1;
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);
if (c->prompt_mode == PROMPT_COMMAND)
style_apply(&gc, s->options, "message-command-style", ft);
@ -723,13 +751,13 @@ status_prompt_redraw(struct client *c)
start = c->tty.sx;
screen_write_start(&ctx, sl->active);
screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines - 1);
screen_write_cursormove(&ctx, 0, lines - 1, 0);
screen_write_fast_copy(&ctx, &sl->screen, 0, 0, c->tty.sx, lines);
screen_write_cursormove(&ctx, 0, promptline, 0);
for (offset = 0; offset < c->tty.sx; offset++)
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);
screen_write_cursormove(&ctx, start, lines - 1, 0);
screen_write_cursormove(&ctx, start, promptline, 0);
left = c->tty.sx - start;
if (left == 0)
@ -966,8 +994,7 @@ status_prompt_paste(struct client *c)
if ((pb = paste_get_top(NULL)) == NULL)
return (0);
bufdata = paste_buffer_data(pb, &bufsize);
ud = xreallocarray(NULL, bufsize + 1, sizeof *ud);
udp = ud;
ud = udp = xreallocarray(NULL, bufsize + 1, sizeof *ud);
for (i = 0; i != bufsize; /* nothing */) {
more = utf8_open(udp, bufdata[i]);
if (more == UTF8_MORE) {
@ -988,25 +1015,24 @@ status_prompt_paste(struct client *c)
udp->size = 0;
n = udp - ud;
}
if (n == 0)
return (0);
c->prompt_buffer = xreallocarray(c->prompt_buffer, size + n + 1,
sizeof *c->prompt_buffer);
if (c->prompt_index == size) {
memcpy(c->prompt_buffer + c->prompt_index, ud,
n * sizeof *c->prompt_buffer);
c->prompt_index += n;
c->prompt_buffer[c->prompt_index].size = 0;
} else {
memmove(c->prompt_buffer + c->prompt_index + n,
c->prompt_buffer + c->prompt_index,
(size + 1 - c->prompt_index) * sizeof *c->prompt_buffer);
memcpy(c->prompt_buffer + c->prompt_index, ud,
n * sizeof *c->prompt_buffer);
c->prompt_index += n;
if (n != 0) {
c->prompt_buffer = xreallocarray(c->prompt_buffer, size + n + 1,
sizeof *c->prompt_buffer);
if (c->prompt_index == size) {
memcpy(c->prompt_buffer + c->prompt_index, ud,
n * sizeof *c->prompt_buffer);
c->prompt_index += n;
c->prompt_buffer[c->prompt_index].size = 0;
} else {
memmove(c->prompt_buffer + c->prompt_index + n,
c->prompt_buffer + c->prompt_index,
(size + 1 - c->prompt_index) *
sizeof *c->prompt_buffer);
memcpy(c->prompt_buffer + c->prompt_index, ud,
n * sizeof *c->prompt_buffer);
c->prompt_index += n;
}
}
if (ud != c->prompt_saved)
free(ud);
return (1);
@ -1452,8 +1478,6 @@ process_key:
return (0);
append_key:
if (key <= 0x1f || (key >= KEYC_BASE && key < KEYC_BASE_END))
return (0);
if (key <= 0x7f)
utf8_set(&tmp, key);
else if (KEYC_IS_UNICODE(key))
@ -1747,8 +1771,9 @@ status_prompt_complete_list_menu(struct client *c, char **list, u_int size,
else
offset = 0;
if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset,
py, c, NULL, status_prompt_menu_callback, spm) != 0) {
if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, 0, NULL, offset, py, c,
BOX_LINES_DEFAULT, NULL, NULL, NULL, NULL,
status_prompt_menu_callback, spm) != 0) {
menu_free(menu);
free(spm);
return (0);
@ -1812,6 +1837,7 @@ status_prompt_complete_window_menu(struct client *c, struct session *s,
}
if (size == 0) {
menu_free(menu);
free(spm);
return (NULL);
}
if (size == 1) {
@ -1822,6 +1848,7 @@ status_prompt_complete_window_menu(struct client *c, struct session *s,
} else
tmp = list[0];
free(list);
free(spm);
return (tmp);
}
if (height > size)
@ -1840,8 +1867,9 @@ status_prompt_complete_window_menu(struct client *c, struct session *s,
else
offset = 0;
if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, NULL, offset,
py, c, NULL, status_prompt_menu_callback, spm) != 0) {
if (menu_display(menu, MENU_NOMOUSE|MENU_TAB, 0, NULL, offset, py, c,
BOX_LINES_DEFAULT, NULL, NULL, NULL, NULL,
status_prompt_menu_callback, spm) != 0) {
menu_free(menu);
free(spm);
return (NULL);

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