mirror of https://github.com/tmux/tmux.git
Compare commits
392 Commits
Author | SHA1 | Date |
---|---|---|
Thomas Adam | 4c2eedca5a | |
jsg | ac6c1e9589 | |
Thomas Adam | 0903790b00 | |
jsg | 03de52653e | |
jsg | da06719309 | |
Thomas Adam | fc84097379 | |
Thomas Adam | 4fd725c6e1 | |
nicm | d39dcea30a | |
nicm | bfd65398a9 | |
Thomas Adam | 452d987e0e | |
Thomas Adam | 8ef899f315 | |
nicm | a18d1146aa | |
nicm | 5b5004e5ac | |
Thomas Adam | 6ff8f8fbf9 | |
nicm | c9616700ca | |
nicm | 4c928dce74 | |
nicm | fb37d52dde | |
Thomas Adam | 363d9c401e | |
nicm | 8643ece345 | |
Nicholas Marriott | 9ba433e521 | |
Nicholas Marriott | 3823fa2c57 | |
Thomas Adam | 0a8571b6fe | |
jsg | ea9f416c99 | |
Thomas Adam | 036d8993e6 | |
nicm | e8530c9fee | |
Nicholas Marriott | dd4c0109a9 | |
Thomas Adam | 43530d4397 | |
nicm | 553d4cba79 | |
nicm | c62a9ca16b | |
nicm | 424f13fe13 | |
Thomas Adam | 4bb6da75ba | |
nicm | a28175dbfd | |
Thomas Adam | fc204bb5e5 | |
nicm | 6207a45139 | |
Thomas Adam | 3c3643f580 | |
nicm | 89c1c43ef9 | |
nicm | 2e9d7ebf15 | |
nicm | d8ddeec7db | |
nicm | 6f0254e6a8 | |
Nicholas Marriott | aa17f0e0c1 | |
nicm | 0ae8b681b2 | |
nicm | 6c0067c103 | |
nicm | 5458cb2850 | |
nicm | 0c374868ca | |
Nicholas Marriott | bf5d3f2e26 | |
Nicholas Marriott | d5ef837f63 | |
Thomas Adam | b79e28b2c3 | |
nicm | 8ffd5458ff | |
Thomas Adam | b54e1fc4f7 | |
Nicholas Marriott | bdb6321229 | |
nicm | bd29a48b56 | |
Nicholas Marriott | f3f1c3db58 | |
Nicholas Marriott | 608d113486 | |
Nicholas Marriott | 9ae69c3795 | |
Thomas Adam | 0960862950 | |
Nicholas Marriott | 44ad25b367 | |
nicm | 40b97b1715 | |
nicm | 4bdb855020 | |
Thomas Adam | ea7136fb83 | |
nicm | 428f8a9b28 | |
Nicholas Marriott | 84faada25b | |
Nicholas Marriott | 55d0abad89 | |
Nicholas Marriott | 7d91b4b90b | |
Nicholas Marriott | 66369416fc | |
Thomas Adam | 001e26d0bb | |
claudio | 2e39d698d2 | |
Thomas Adam | e809c2ec35 | |
Nicholas Marriott | 4266d3efc8 | |
nicm | 40a20bc8ae | |
nicm | f7bf7e9671 | |
nicm | 008ecd4592 | |
nicm | 73a2b12088 | |
Nicholas Marriott | 605bf21ff2 | |
nicm | f028445407 | |
Thomas Adam | bdf8e614af | |
nicm | 4dea352dee | |
Thomas Adam | 151875d144 | |
nicm | 88fd1f00b8 | |
nicm | 1a14d6d2e1 | |
Thomas Adam | 381c00a74e | |
nicm | 5aadee6df4 | |
Nicholas Marriott | a5545dbc9f | |
nicm | fdf465925e | |
nicm | 36e1ac6556 | |
nicm | ffa376edf7 | |
Thomas Adam | b777780720 | |
nicm | 347cd0b5f8 | |
Nicholas Marriott | 0ca28b362e | |
Nicholas Marriott | 51b80b985e | |
Nicholas Marriott | 789a98982e | |
Thomas Adam | b202a2f1b5 | |
nicm | 7e79108f8a | |
nicm | f09cde2542 | |
Thomas Adam | 9f9156c030 | |
nicm | d394293ba5 | |
Thomas Adam | c57a09269b | |
nicm | 8191c58737 | |
Thomas Adam | f68d35c529 | |
nicm | c02bc4dbe9 | |
nicm | 4872811ba7 | |
Nicholas Marriott | 1a1290f30b | |
Nicholas Marriott | 7be7ca7195 | |
Thomas Adam | 32197fa52d | |
nicm | 9653a52a6a | |
Thomas Adam | d60c8942ce | |
nicm | c99f9e92e0 | |
Thomas Adam | e26356607e | |
nicm | 43e5e80343 | |
Thomas Adam | 1742138f05 | |
nicm | c767d62329 | |
Thomas Adam | 7ad29b9831 | |
nicm | d209fe9b1e | |
nicm | c5542637d7 | |
Thomas Adam | e7c829fc67 | |
nicm | 579829eef2 | |
Nicholas Marriott | 3aa20f6e75 | |
nicm | f78279bb2e | |
Thomas Adam | a99d7c6314 | |
Thomas Adam | cf1ed67fcc | |
Thomas Adam | 1aec420465 | |
nicm | 16e4b39359 | |
nicm | 9456258ccc | |
nicm | c41d59f232 | |
Nicholas Marriott | d682ef88e6 | |
nicm | c1e6e54e6e | |
Nicholas Marriott | 62f657845e | |
Thomas Adam | a9841a6d1e | |
Thomas Adam | 70ecf17f85 | |
nicm | 71d453f169 | |
Nicholas Marriott | 071849f82f | |
nicm | 52084b2948 | |
Nicholas Marriott | dfbc6b1888 | |
Thomas Adam | e3a8b8434c | |
nicm | 8636848e63 | |
Thomas Adam | 3d93b0c52e | |
nicm | d9942c769e | |
Thomas Adam | 6a45e6c256 | |
nicm | b770a429c6 | |
Thomas Adam | 11e69f6025 | |
nicm | 57837bbf67 | |
Thomas Adam | 4c60afde78 | |
Thomas Adam | ed689cd54e | |
nicm | 7a44984069 | |
nicm | dee72ed41f | |
nicm | 1071ef8fc5 | |
nicm | 2b535bc173 | |
Thomas Adam | b791f00bf1 | |
Thomas Adam | 3e82ad5340 | |
nicm | 7b1030293c | |
nicm | a5fd80bbc3 | |
Thomas Adam | fda3937734 | |
nicm | b13c230749 | |
Thomas Adam | 715f39a53a | |
nicm | 2f74e811f1 | |
Thomas Adam | 828efc7bcf | |
nicm | 84936b832f | |
Thomas Adam | c8494dff7b | |
nicm | 8fcc212e7a | |
Thomas Adam | 0e281530cb | |
nicm | efded95ed7 | |
Thomas Adam | 18870913c5 | |
nicm | 63b7282377 | |
Thomas Adam | 269dab4b3e | |
nicm | 4ece43a029 | |
nicm | 8b3e2eab5a | |
Thomas Adam | e4c4ceb286 | |
nicm | b7e22d00b4 | |
Thomas Adam | 1a11c972ae | |
nicm | 43b841f188 | |
Thomas Adam | f9b0460840 | |
Thomas Adam | 659d876cd5 | |
nicm | ac43186dff | |
nicm | e79fb214f8 | |
Thomas Adam | 9cf58d1a52 | |
nicm | a2a02fd7d7 | |
Thomas Adam | 237ee6f231 | |
nicm | 4e57894e85 | |
Nicholas Marriott | 8c9fbbf4f3 | |
Thomas Adam | 3f3d61bd58 | |
Thomas Adam | 80d4f4afc6 | |
nicm | 2546216019 | |
nicm | ff8882a24f | |
Thomas Adam | ffe2410639 | |
nicm | 9e14c1f88d | |
Thomas Adam | 29a5dfc7c0 | |
nicm | 645bf8b3ab | |
Thomas Adam | f41c536ff3 | |
nicm | bdd05bdbd3 | |
Nicholas Marriott | fe385b180f | |
Thomas Adam | 0eb5d25453 | |
nicm | 1d98394b41 | |
Thomas Adam | 149d9cc851 | |
tb | 204d8f31d7 | |
Thomas Adam | fbe6fe7f55 | |
Nicholas Marriott | 168eab11a7 | |
nicm | 41b318ac7c | |
nicm | bf636d9575 | |
Nicholas Marriott | 39d41d0810 | |
Thomas Adam | 9d8131c190 | |
Nicholas Marriott | 0ff991b25f | |
Nicholas Marriott | 15c70e562c | |
nicm | 8f34504736 | |
nicm | 551e0c36d9 | |
nicm | 48eba4c195 | |
nicm | 9f605178c3 | |
nicm | bcafe51378 | |
nicm | 280fe77edd | |
Nicholas Marriott | b9524f5b72 | |
nicm | a2018b2c3f | |
nicm | c21af7e446 | |
nicm | d73078838d | |
Thomas Adam | 22eb0334c3 | |
nicm | a9ac614691 | |
Thomas Adam | 1d0f68dee9 | |
nicm | ac4bb89d43 | |
Thomas Adam | b55f34029a | |
nicm | 907f58cc3c | |
Thomas Adam | 023b0f76c3 | |
Thomas Adam | ddaeebc213 | |
nicm | 1262e685b8 | |
nicm | 0bd78b42c0 | |
Thomas Adam | 4a0126f7fb | |
nicm | 7acc8d703d | |
Thomas Adam | 77118f3a9f | |
Thomas Adam | e25926d3c4 | |
nicm | 0cb75f1332 | |
nicm | 93b1b78150 | |
Thomas Adam | 493922dc4b | |
nicm | f10854cfc5 | |
nicm | 993e7a937f | |
Thomas Adam | f5af3cfb21 | |
nicm | e7e112fbd0 | |
Thomas Adam | c42087c789 | |
nicm | 3aa458ea63 | |
Thomas Adam | c4a6f403bb | |
nicm | 9789ea3fb4 | |
Thomas Adam | f416ae1c12 | |
nicm | d578cf8d3f | |
Thomas Adam | 789cb91f31 | |
nicm | eb1f8d70a7 | |
Thomas Adam | 42895efac3 | |
nicm | 483cc77c1c | |
Thomas Adam | 9b1ea8b16d | |
nicm | b41892622d | |
Thomas Adam | 9051220243 | |
nicm | c0031f8b85 | |
Thomas Adam | c1a30ed995 | |
nicm | 565de3f54b | |
Thomas Adam | 153ae758c9 | |
Thomas Adam | 5086377f30 | |
nicm | 7c0789d2d2 | |
Nicholas Marriott | 2a32565e0c | |
nicm | 7ced0a03d2 | |
nicm | cb51942669 | |
Nicholas Marriott | 093fb53773 | |
Thomas Adam | 21e00e4635 | |
nicm | 09afc6c8ee | |
nicm | a41a927441 | |
jmc | 3fe01ff09c | |
kn | b5ab4d2c13 | |
nicm | 4d79d463ef | |
nicm | 7cb48fc40b | |
nicm | 8bd17bff49 | |
nicm | 3b3f42053a | |
Nicholas Marriott | 70ff8cfe1e | |
Thomas Adam | 6249a4b866 | |
nicm | 7e497c7f23 | |
Thomas Adam | 1536b7e206 | |
Nicholas Marriott | e46d0632a5 | |
nicm | 20da167377 | |
nicm | fe475bd856 | |
nicm | 079f48e8a6 | |
Thomas Adam | aaa043a20f | |
jmc | 48f41e4a41 | |
Thomas Adam | 6fb80527f3 | |
nicm | f86eba2129 | |
Thomas Adam | 50f4e0fac9 | |
nicm | 77c135349a | |
Thomas Adam | c449512be4 | |
nicm | 17290b9121 | |
nicm | 3be369522b | |
Thomas Adam | dbfbd8a195 | |
nicm | 9614f51560 | |
Thomas Adam | 36896f6dd0 | |
nicm | 2d08235987 | |
nicm | 2291045116 | |
Thomas Adam | 9dd1f442c5 | |
nicm | 8edece2cdb | |
Thomas Adam | d001a94d7b | |
nicm | c2580cfe24 | |
Thomas Adam | dafd6f462f | |
nicm | 2111142cf1 | |
nicm | 0fc961b22e | |
Nicholas Marriott | 5ce34add77 | |
Thomas Adam | 934f357149 | |
nicm | ff2766b024 | |
Thomas Adam | 9ef854f5a9 | |
nicm | a10452be2d | |
nicm | 9cc8e40aa0 | |
Thomas Adam | f49f92737f | |
Nicholas Marriott | 19344ec890 | |
nicm | a2cc601c3d | |
nicm | 9ab1ba36cd | |
nicm | f03c3ca6c3 | |
Nicholas Marriott | 0a0ded3268 | |
Thomas Adam | 6da520c5a1 | |
nicm | 68dc9af9ac | |
Thomas Adam | f7b30ed3d4 | |
nicm | e867528209 | |
Thomas Adam | 038dfb27a8 | |
nicm | 416c27c995 | |
Nicholas Marriott | 19344efa78 | |
Thomas Adam | 9c34aad21c | |
nicm | 7c2dcd7238 | |
nicm | 03149bf7f6 | |
nicm | 497021d0db | |
nicm | cfdc5b62ad | |
Thomas Adam | 9b08e5139b | |
nicm | e139f977b1 | |
Thomas Adam | 9abf5d9fe5 | |
nicm | 273577ba0a | |
Thomas Adam | e15058e60f | |
nicm | de5cd54124 | |
Thomas Adam | c6cf09450a | |
nicm | c6e7568471 | |
Thomas Adam | 7b8ececd8d | |
nicm | 42ba6c1b22 | |
Thomas Adam | 9a2fdf8fd4 | |
Nicholas Marriott | 00812c9053 | |
nicm | 33c59100ae | |
nicm | 36d904011a | |
Thomas Adam | 9d9445a48e | |
nicm | a8da24771c | |
Thomas Adam | ab1d18d00f | |
nicm | ee431d482a | |
Nicholas Marriott | e06c09889c | |
nicm | 86dfbda0e4 | |
Nicholas Marriott | 697cebb4c1 | |
nicm | 3c65475561 | |
Thomas Adam | dc6bc0e95a | |
Thomas Adam | b130e951cc | |
Nicholas Marriott | 9e19f132f2 | |
nicm | 1afe22086f | |
Nicholas Marriott | 8e8b9865d1 | |
nicm | a39827a85c | |
nicm | 9e03df5500 | |
nicm | dd602eaa61 | |
nicm | d0d2c39dec | |
Thomas Adam | 57fec74966 | |
nicm | 9360e0ef32 | |
Nicholas Marriott | f08c019d41 | |
Thomas Adam | c3af8f6b16 | |
Thomas Adam | 01c4919f5f | |
nicm | cdacc12ce3 | |
Thomas Adam | d8c527a5f9 | |
nicm | b22edcf3a5 | |
nicm | 786cff8db9 | |
Thomas Adam | b63afaea61 | |
nicm | 9c89f7c2af | |
Thomas Adam | d46870ede5 | |
nicm | a888ce9963 | |
Thomas Adam | 8ff3091d16 | |
nicm | d9f84854ac | |
Thomas Adam | 89fe2680a9 | |
nicm | 7cee982f90 | |
Nicholas Marriott | 42358cc521 | |
Thomas Adam | 06869ff22f | |
nicm | 616bde08ac | |
Thomas Adam | 6d0828b81c | |
nicm | 42ddf02ffc | |
Thomas Adam | 56390e0a39 | |
nicm | 18a5835aff | |
Nicholas Marriott | 67960dcc9a | |
Nicholas Marriott | 0b355ae811 | |
Nicholas Marriott | 6a9bb2a622 | |
nicm | 988e59cf3e | |
nicm | 30e06e9d85 | |
Thomas Adam | 810daefdd1 | |
nicm | ccc9dc3bb4 | |
Thomas Adam | be2eb57d62 | |
nicm | c07d582e24 | |
Thomas Adam | afb3a5fe71 | |
nicm | 020c403dff | |
Thomas Adam | e77e11ec6b | |
nicm | 3edda3c5e7 | |
Thomas Adam | 1184dc08d4 | |
nicm | 18838fbc87 | |
Nicholas Marriott | be2617036f | |
nicm | 0f6227f46b | |
Thomas Adam | c1ac007576 | |
nicm | 201a8d8e7e | |
Nicholas Marriott | b566cd57bf |
|
@ -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
122
CHANGES
|
@ -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
|
||||
|
|
10
Makefile.am
10
Makefile.am
|
@ -1,5 +1,3 @@
|
|||
# Makefile.am
|
||||
|
||||
# Obvious program stuff.
|
||||
bin_PROGRAMS = tmux
|
||||
CLEANFILES = tmux.1.mdoc tmux.1.man cmd-parse.c
|
||||
|
@ -14,6 +12,7 @@ dist_EXTRA_tmux_SOURCES = compat/*.[ch]
|
|||
AM_CPPFLAGS += @XOPEN_DEFINES@ \
|
||||
-DTMUX_VERSION='"@VERSION@"' \
|
||||
-DTMUX_CONF='"$(sysconfdir)/tmux.conf:~/.tmux.conf:$$XDG_CONFIG_HOME/tmux/tmux.conf:~/.config/tmux/tmux.conf"' \
|
||||
-DTMUX_LOCK_CMD='"@DEFAULT_LOCK_CMD@"' \
|
||||
-DTMUX_TERM='"@DEFAULT_TERM@"'
|
||||
|
||||
# Additional object files.
|
||||
|
@ -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)
|
||||
|
|
349
arguments.c
349
arguments.c
|
@ -37,6 +37,10 @@ struct args_entry {
|
|||
u_char flag;
|
||||
struct args_values values;
|
||||
u_int count;
|
||||
|
||||
int flags;
|
||||
#define ARGS_ENTRY_OPTIONAL_VALUE 0x1
|
||||
|
||||
RB_ENTRY(args_entry) entry;
|
||||
};
|
||||
|
||||
|
@ -94,6 +98,22 @@ args_copy_value(struct args_value *to, struct args_value *from)
|
|||
}
|
||||
}
|
||||
|
||||
/* Type to string. */
|
||||
static const char *
|
||||
args_type_to_string (enum args_type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ARGS_NONE:
|
||||
return "NONE";
|
||||
case ARGS_STRING:
|
||||
return "STRING";
|
||||
case ARGS_COMMANDS:
|
||||
return "COMMANDS";
|
||||
}
|
||||
return "INVALID";
|
||||
}
|
||||
|
||||
/* Get value as string. */
|
||||
static const char *
|
||||
args_value_as_string(struct args_value *value)
|
||||
|
@ -122,6 +142,103 @@ args_create(void)
|
|||
return (args);
|
||||
}
|
||||
|
||||
/* Parse a single flag. */
|
||||
static int
|
||||
args_parse_flag_argument(struct args_value *values, u_int count, char **cause,
|
||||
struct args *args, u_int *i, const char *string, int flag,
|
||||
int optional_argument)
|
||||
{
|
||||
struct args_value *argument, *new;
|
||||
const char *s;
|
||||
|
||||
new = xcalloc(1, sizeof *new);
|
||||
if (*string != '\0') {
|
||||
new->type = ARGS_STRING;
|
||||
new->string = xstrdup(string);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (*i == count)
|
||||
argument = NULL;
|
||||
else {
|
||||
argument = &values[*i];
|
||||
if (argument->type != ARGS_STRING) {
|
||||
xasprintf(cause, "-%c argument must be a string", flag);
|
||||
args_free_value(new);
|
||||
free(new);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
if (argument == NULL) {
|
||||
args_free_value(new);
|
||||
free(new);
|
||||
if (optional_argument) {
|
||||
log_debug("%s: -%c (optional)", __func__, flag);
|
||||
args_set(args, flag, NULL, ARGS_ENTRY_OPTIONAL_VALUE);
|
||||
return (0); /* either - or end */
|
||||
}
|
||||
xasprintf(cause, "-%c expects an argument", flag);
|
||||
return (-1);
|
||||
}
|
||||
args_copy_value(new, argument);
|
||||
(*i)++;
|
||||
|
||||
out:
|
||||
s = args_value_as_string(new);
|
||||
log_debug("%s: -%c = %s", __func__, flag, s);
|
||||
args_set(args, flag, new, 0);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Parse flags argument. */
|
||||
static int
|
||||
args_parse_flags(const struct args_parse *parse, struct args_value *values,
|
||||
u_int count, char **cause, struct args *args, u_int *i)
|
||||
{
|
||||
struct args_value *value;
|
||||
u_char flag;
|
||||
const char *found, *string;
|
||||
int optional_argument;
|
||||
|
||||
value = &values[*i];
|
||||
if (value->type != ARGS_STRING)
|
||||
return (1);
|
||||
|
||||
string = value->string;
|
||||
log_debug("%s: next %s", __func__, string);
|
||||
if (*string++ != '-' || *string == '\0')
|
||||
return (1);
|
||||
(*i)++;
|
||||
if (string[0] == '-' && string[1] == '\0')
|
||||
return (1);
|
||||
|
||||
for (;;) {
|
||||
flag = *string++;
|
||||
if (flag == '\0')
|
||||
return (0);
|
||||
if (flag == '?')
|
||||
return (-1);
|
||||
if (!isalnum(flag)) {
|
||||
xasprintf(cause, "invalid flag -%c", flag);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
found = strchr(parse->template, flag);
|
||||
if (found == NULL) {
|
||||
xasprintf(cause, "unknown flag -%c", flag);
|
||||
return (-1);
|
||||
}
|
||||
if (found[1] != ':') {
|
||||
log_debug("%s: -%c", __func__, flag);
|
||||
args_set(args, flag, NULL, 0);
|
||||
continue;
|
||||
}
|
||||
optional_argument = (found[2] == ':');
|
||||
return (args_parse_flag_argument(values, count, cause, args, i,
|
||||
string, flag, optional_argument));
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse arguments into a new argument set. */
|
||||
struct args *
|
||||
args_parse(const struct args_parse *parse, struct args_value *values,
|
||||
|
@ -131,86 +248,21 @@ args_parse(const struct args_parse *parse, struct args_value *values,
|
|||
u_int i;
|
||||
enum args_parse_type type;
|
||||
struct args_value *value, *new;
|
||||
u_char flag;
|
||||
const char *found, *string, *s;
|
||||
int optional_argument;
|
||||
const char *s;
|
||||
int stop;
|
||||
|
||||
if (count == 0)
|
||||
return (args_create());
|
||||
|
||||
args = args_create();
|
||||
for (i = 1; i < count; /* nothing */) {
|
||||
value = &values[i];
|
||||
if (value->type != ARGS_STRING)
|
||||
break;
|
||||
|
||||
string = value->string;
|
||||
if (*string++ != '-' || *string == '\0')
|
||||
break;
|
||||
i++;
|
||||
if (string[0] == '-' && string[1] == '\0')
|
||||
break;
|
||||
|
||||
for (;;) {
|
||||
flag = *string++;
|
||||
if (flag == '\0')
|
||||
break;
|
||||
if (flag == '?') {
|
||||
args_free(args);
|
||||
return (NULL);
|
||||
}
|
||||
if (!isalnum(flag)) {
|
||||
xasprintf(cause, "invalid flag -%c", flag);
|
||||
args_free(args);
|
||||
return (NULL);
|
||||
}
|
||||
found = strchr(parse->template, flag);
|
||||
if (found == NULL) {
|
||||
xasprintf(cause, "unknown flag -%c", flag);
|
||||
args_free(args);
|
||||
return (NULL);
|
||||
}
|
||||
if (*++found != ':') {
|
||||
log_debug("%s: -%c", __func__, flag);
|
||||
args_set(args, flag, NULL);
|
||||
continue;
|
||||
}
|
||||
if (*found == ':') {
|
||||
optional_argument = 1;
|
||||
found++;
|
||||
}
|
||||
new = xcalloc(1, sizeof *new);
|
||||
if (*string != '\0') {
|
||||
new->type = ARGS_STRING;
|
||||
new->string = xstrdup(string);
|
||||
} else {
|
||||
if (i == count) {
|
||||
if (optional_argument) {
|
||||
log_debug("%s: -%c", __func__,
|
||||
flag);
|
||||
args_set(args, flag, NULL);
|
||||
continue;
|
||||
}
|
||||
xasprintf(cause,
|
||||
"-%c expects an argument",
|
||||
flag);
|
||||
args_free(args);
|
||||
return (NULL);
|
||||
}
|
||||
if (values[i].type != ARGS_STRING) {
|
||||
xasprintf(cause,
|
||||
"-%c argument must be a string",
|
||||
flag);
|
||||
args_free(args);
|
||||
return (NULL);
|
||||
}
|
||||
args_copy_value(new, &values[i++]);
|
||||
}
|
||||
s = args_value_as_string(new);
|
||||
log_debug("%s: -%c = %s", __func__, flag, s);
|
||||
args_set(args, flag, new);
|
||||
break;
|
||||
stop = args_parse_flags(parse, values, count, cause, args, &i);
|
||||
if (stop == -1) {
|
||||
args_free(args);
|
||||
return (NULL);
|
||||
}
|
||||
if (stop == 1)
|
||||
break;
|
||||
}
|
||||
log_debug("%s: flags end at %u of %u", __func__, i, count);
|
||||
if (i != count) {
|
||||
|
@ -218,8 +270,8 @@ args_parse(const struct args_parse *parse, struct args_value *values,
|
|||
value = &values[i];
|
||||
|
||||
s = args_value_as_string(value);
|
||||
log_debug("%s: %u = %s (type %d)", __func__, i, s,
|
||||
value->type);
|
||||
log_debug("%s: %u = %s (type %s)", __func__, i, s,
|
||||
args_type_to_string (value->type));
|
||||
|
||||
if (parse->cb != NULL) {
|
||||
type = parse->cb(args, args->count, cause);
|
||||
|
@ -323,13 +375,13 @@ args_copy(struct args *args, int argc, char **argv)
|
|||
RB_FOREACH(entry, args_tree, &args->tree) {
|
||||
if (TAILQ_EMPTY(&entry->values)) {
|
||||
for (i = 0; i < entry->count; i++)
|
||||
args_set(new_args, entry->flag, NULL);
|
||||
args_set(new_args, entry->flag, NULL, 0);
|
||||
continue;
|
||||
}
|
||||
TAILQ_FOREACH(value, &entry->values, entry) {
|
||||
new_value = xcalloc(1, sizeof *new_value);
|
||||
args_copy_copy_value(new_value, value, argc, argv);
|
||||
args_set(new_args, entry->flag, new_value);
|
||||
args_set(new_args, entry->flag, new_value, 0);
|
||||
}
|
||||
}
|
||||
if (args->count == 0)
|
||||
|
@ -487,6 +539,7 @@ args_print(struct args *args)
|
|||
char *buf;
|
||||
u_int i, j;
|
||||
struct args_entry *entry;
|
||||
struct args_entry *last = NULL;
|
||||
struct args_value *value;
|
||||
|
||||
len = 1;
|
||||
|
@ -494,6 +547,8 @@ args_print(struct args *args)
|
|||
|
||||
/* Process the flags first. */
|
||||
RB_FOREACH(entry, args_tree, &args->tree) {
|
||||
if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE)
|
||||
continue;
|
||||
if (!TAILQ_EMPTY(&entry->values))
|
||||
continue;
|
||||
|
||||
|
@ -505,6 +560,16 @@ args_print(struct args *args)
|
|||
|
||||
/* Then the flags with arguments. */
|
||||
RB_FOREACH(entry, args_tree, &args->tree) {
|
||||
if (entry->flags & ARGS_ENTRY_OPTIONAL_VALUE) {
|
||||
if (*buf != '\0')
|
||||
args_print_add(&buf, &len, " -%c", entry->flag);
|
||||
else
|
||||
args_print_add(&buf, &len, "-%c", entry->flag);
|
||||
last = entry;
|
||||
continue;
|
||||
}
|
||||
if (TAILQ_EMPTY(&entry->values))
|
||||
continue;
|
||||
TAILQ_FOREACH(value, &entry->values, entry) {
|
||||
if (*buf != '\0')
|
||||
args_print_add(&buf, &len, " -%c", entry->flag);
|
||||
|
@ -512,7 +577,10 @@ args_print(struct args *args)
|
|||
args_print_add(&buf, &len, "-%c", entry->flag);
|
||||
args_print_add_value(&buf, &len, value);
|
||||
}
|
||||
last = entry;
|
||||
}
|
||||
if (last && (last->flags & ARGS_ENTRY_OPTIONAL_VALUE))
|
||||
args_print_add(&buf, &len, " --");
|
||||
|
||||
/* And finally the argument vector. */
|
||||
for (i = 0; i < args->count; i++)
|
||||
|
@ -582,7 +650,7 @@ args_has(struct args *args, u_char flag)
|
|||
|
||||
/* Set argument value in the arguments tree. */
|
||||
void
|
||||
args_set(struct args *args, u_char flag, struct args_value *value)
|
||||
args_set(struct args *args, u_char flag, struct args_value *value, int flags)
|
||||
{
|
||||
struct args_entry *entry;
|
||||
|
||||
|
@ -591,12 +659,15 @@ args_set(struct args *args, u_char flag, struct args_value *value)
|
|||
entry = xcalloc(1, sizeof *entry);
|
||||
entry->flag = flag;
|
||||
entry->count = 1;
|
||||
entry->flags = flags;
|
||||
TAILQ_INIT(&entry->values);
|
||||
RB_INSERT(args_tree, &args->tree, entry);
|
||||
} else
|
||||
entry->count++;
|
||||
if (value != NULL && value->type != ARGS_NONE)
|
||||
TAILQ_INSERT_TAIL(&entry->values, value, entry);
|
||||
else
|
||||
free(value);
|
||||
}
|
||||
|
||||
/* Get argument value. Will be NULL if it isn't present. */
|
||||
|
@ -696,6 +767,7 @@ args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
|
|||
struct args_value *value;
|
||||
struct args_command_state *state;
|
||||
const char *cmd;
|
||||
const char *file;
|
||||
|
||||
state = xcalloc(1, sizeof *state);
|
||||
|
||||
|
@ -722,7 +794,9 @@ args_make_commands_prepare(struct cmd *self, struct cmdq_item *item, u_int idx,
|
|||
|
||||
if (wait)
|
||||
state->pi.item = item;
|
||||
cmd_get_source(self, &state->pi.file, &state->pi.line);
|
||||
cmd_get_source(self, &file, &state->pi.line);
|
||||
if (file != NULL)
|
||||
state->pi.file = xstrdup(file);
|
||||
state->pi.c = tc;
|
||||
if (state->pi.c != NULL)
|
||||
state->pi.c->references++;
|
||||
|
@ -747,6 +821,8 @@ args_make_commands(struct args_command_state *state, int argc, char **argv,
|
|||
}
|
||||
|
||||
cmd = xstrdup(state->cmd);
|
||||
log_debug("%s: %s", __func__, cmd);
|
||||
cmd_log_argv(argc, argv, __func__);
|
||||
for (i = 0; i < argc; i++) {
|
||||
new_cmd = cmd_template_replace(cmd, argv[i], i + 1);
|
||||
log_debug("%s: %%%u %s: %s", __func__, i + 1, argv[i], new_cmd);
|
||||
|
@ -775,6 +851,7 @@ args_make_commands_free(struct args_command_state *state)
|
|||
cmd_list_free(state->cmdlist);
|
||||
if (state->pi.c != NULL)
|
||||
server_client_unref(state->pi.c);
|
||||
free((void *)state->pi.file);
|
||||
free(state->cmd);
|
||||
free(state);
|
||||
}
|
||||
|
@ -848,6 +925,41 @@ args_strtonum(struct args *args, u_char flag, long long minval,
|
|||
return (ll);
|
||||
}
|
||||
|
||||
/* Convert an argument value to a number, and expand formats. */
|
||||
long long
|
||||
args_strtonum_and_expand(struct args *args, u_char flag, long long minval,
|
||||
long long maxval, struct cmdq_item *item, char **cause)
|
||||
{
|
||||
const char *errstr;
|
||||
char *formatted;
|
||||
long long ll;
|
||||
struct args_entry *entry;
|
||||
struct args_value *value;
|
||||
|
||||
if ((entry = args_find(args, flag)) == NULL) {
|
||||
*cause = xstrdup("missing");
|
||||
return (0);
|
||||
}
|
||||
value = TAILQ_LAST(&entry->values, args_values);
|
||||
if (value == NULL ||
|
||||
value->type != ARGS_STRING ||
|
||||
value->string == NULL) {
|
||||
*cause = xstrdup("missing");
|
||||
return (0);
|
||||
}
|
||||
|
||||
formatted = format_single_from_target(item, value->string);
|
||||
ll = strtonum(formatted, minval, maxval, &errstr);
|
||||
free(formatted);
|
||||
if (errstr != NULL) {
|
||||
*cause = xstrdup(errstr);
|
||||
return (0);
|
||||
}
|
||||
|
||||
*cause = NULL;
|
||||
return (ll);
|
||||
}
|
||||
|
||||
/* Convert an argument to a number which may be a percentage. */
|
||||
long long
|
||||
args_percentage(struct args *args, u_char flag, long long minval,
|
||||
|
@ -860,6 +972,10 @@ args_percentage(struct args *args, u_char flag, long long minval,
|
|||
*cause = xstrdup("missing");
|
||||
return (0);
|
||||
}
|
||||
if (TAILQ_EMPTY(&entry->values)) {
|
||||
*cause = xstrdup("empty");
|
||||
return (0);
|
||||
}
|
||||
value = TAILQ_LAST(&entry->values, args_values)->string;
|
||||
return (args_string_percentage(value, minval, maxval, curval, cause));
|
||||
}
|
||||
|
@ -874,6 +990,10 @@ args_string_percentage(const char *value, long long minval, long long maxval,
|
|||
size_t valuelen = strlen(value);
|
||||
char *copy;
|
||||
|
||||
if (valuelen == 0) {
|
||||
*cause = xstrdup("empty");
|
||||
return (0);
|
||||
}
|
||||
if (value[valuelen - 1] == '%') {
|
||||
copy = xstrdup(value);
|
||||
copy[valuelen - 1] = '\0';
|
||||
|
@ -904,3 +1024,74 @@ args_string_percentage(const char *value, long long minval, long long maxval,
|
|||
*cause = NULL;
|
||||
return (ll);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an argument to a number which may be a percentage, and expand
|
||||
* formats.
|
||||
*/
|
||||
long long
|
||||
args_percentage_and_expand(struct args *args, u_char flag, long long minval,
|
||||
long long maxval, long long curval, struct cmdq_item *item, char **cause)
|
||||
{
|
||||
const char *value;
|
||||
struct args_entry *entry;
|
||||
|
||||
if ((entry = args_find(args, flag)) == NULL) {
|
||||
*cause = xstrdup("missing");
|
||||
return (0);
|
||||
}
|
||||
if (TAILQ_EMPTY(&entry->values)) {
|
||||
*cause = xstrdup("empty");
|
||||
return (0);
|
||||
}
|
||||
value = TAILQ_LAST(&entry->values, args_values)->string;
|
||||
return (args_string_percentage_and_expand(value, minval, maxval, curval,
|
||||
item, cause));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a string to a number which may be a percentage, and expand formats.
|
||||
*/
|
||||
long long
|
||||
args_string_percentage_and_expand(const char *value, long long minval,
|
||||
long long maxval, long long curval, struct cmdq_item *item, char **cause)
|
||||
{
|
||||
const char *errstr;
|
||||
long long ll;
|
||||
size_t valuelen = strlen(value);
|
||||
char *copy, *f;
|
||||
|
||||
if (value[valuelen - 1] == '%') {
|
||||
copy = xstrdup(value);
|
||||
copy[valuelen - 1] = '\0';
|
||||
|
||||
f = format_single_from_target(item, copy);
|
||||
ll = strtonum(f, 0, 100, &errstr);
|
||||
free(f);
|
||||
free(copy);
|
||||
if (errstr != NULL) {
|
||||
*cause = xstrdup(errstr);
|
||||
return (0);
|
||||
}
|
||||
ll = (curval * ll) / 100;
|
||||
if (ll < minval) {
|
||||
*cause = xstrdup("too small");
|
||||
return (0);
|
||||
}
|
||||
if (ll > maxval) {
|
||||
*cause = xstrdup("too large");
|
||||
return (0);
|
||||
}
|
||||
} else {
|
||||
f = format_single_from_target(item, value);
|
||||
ll = strtonum(f, minval, maxval, &errstr);
|
||||
free(f);
|
||||
if (errstr != NULL) {
|
||||
*cause = xstrdup(errstr);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
*cause = NULL;
|
||||
return (ll);
|
||||
}
|
||||
|
|
47
cfg.c
47
cfg.c
|
@ -51,8 +51,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
|
|||
return (CMD_RETURN_NORMAL);
|
||||
cfg_finished = 1;
|
||||
|
||||
if (!RB_EMPTY(&sessions))
|
||||
cfg_show_causes(RB_MIN(sessions, &sessions));
|
||||
cfg_show_causes(NULL);
|
||||
|
||||
if (cfg_item != NULL)
|
||||
cmdq_continue(cfg_item);
|
||||
|
@ -67,6 +66,7 @@ start_cfg(void)
|
|||
{
|
||||
struct client *c;
|
||||
u_int i;
|
||||
int flags = 0;
|
||||
|
||||
/*
|
||||
* Configuration files are loaded without a client, so commands are run
|
||||
|
@ -84,19 +84,17 @@ start_cfg(void)
|
|||
cmdq_append(c, cfg_item);
|
||||
}
|
||||
|
||||
for (i = 0; i < cfg_nfiles; i++) {
|
||||
if (cfg_quiet)
|
||||
load_cfg(cfg_files[i], c, NULL, CMD_PARSE_QUIET, NULL);
|
||||
else
|
||||
load_cfg(cfg_files[i], c, NULL, 0, NULL);
|
||||
}
|
||||
if (cfg_quiet)
|
||||
flags = CMD_PARSE_QUIET;
|
||||
for (i = 0; i < cfg_nfiles; i++)
|
||||
load_cfg(cfg_files[i], c, NULL, NULL, flags, NULL);
|
||||
|
||||
cmdq_append(NULL, cmdq_get_callback(cfg_done, NULL));
|
||||
}
|
||||
|
||||
int
|
||||
load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
|
||||
struct cmdq_item **new_item)
|
||||
load_cfg(const char *path, struct client *c, struct cmdq_item *item,
|
||||
struct cmd_find_state *current, int flags, struct cmdq_item **new_item)
|
||||
{
|
||||
FILE *f;
|
||||
struct cmd_parse_input pi;
|
||||
|
@ -135,7 +133,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
|
|||
}
|
||||
|
||||
if (item != NULL)
|
||||
state = cmdq_copy_state(cmdq_get_state(item));
|
||||
state = cmdq_copy_state(cmdq_get_state(item), current);
|
||||
else
|
||||
state = cmdq_new_state(NULL, NULL, 0);
|
||||
cmdq_add_format(state, "current_file", "%s", pi.file);
|
||||
|
@ -155,8 +153,8 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags,
|
|||
|
||||
int
|
||||
load_cfg_from_buffer(const void *buf, size_t len, const char *path,
|
||||
struct client *c, struct cmdq_item *item, int flags,
|
||||
struct cmdq_item **new_item)
|
||||
struct client *c, struct cmdq_item *item, struct cmd_find_state *current,
|
||||
int flags, struct cmdq_item **new_item)
|
||||
{
|
||||
struct cmd_parse_input pi;
|
||||
struct cmd_parse_result *pr;
|
||||
|
@ -187,7 +185,7 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path,
|
|||
}
|
||||
|
||||
if (item != NULL)
|
||||
state = cmdq_copy_state(cmdq_get_state(item));
|
||||
state = cmdq_copy_state(cmdq_get_state(item), current);
|
||||
else
|
||||
state = cmdq_new_state(NULL, NULL, 0);
|
||||
cmdq_add_format(state, "current_file", "%s", pi.file);
|
||||
|
@ -238,11 +236,29 @@ cfg_print_causes(struct cmdq_item *item)
|
|||
void
|
||||
cfg_show_causes(struct session *s)
|
||||
{
|
||||
struct client *c = TAILQ_FIRST(&clients);
|
||||
struct window_pane *wp;
|
||||
struct window_mode_entry *wme;
|
||||
u_int i;
|
||||
|
||||
if (s == NULL || cfg_ncauses == 0)
|
||||
if (cfg_ncauses == 0)
|
||||
return;
|
||||
|
||||
if (c != NULL && (c->flags & CLIENT_CONTROL)) {
|
||||
for (i = 0; i < cfg_ncauses; i++) {
|
||||
control_write(c, "%%config-error %s", cfg_causes[i]);
|
||||
free(cfg_causes[i]);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (s == NULL) {
|
||||
if (c != NULL && c->session != NULL)
|
||||
s = c->session;
|
||||
else
|
||||
s = RB_MIN(sessions, &sessions);
|
||||
}
|
||||
if (s == NULL || s->attached == 0) /* wait for an attached session */
|
||||
return;
|
||||
wp = s->curw->window->active;
|
||||
|
||||
|
@ -254,6 +270,7 @@ cfg_show_causes(struct session *s)
|
|||
free(cfg_causes[i]);
|
||||
}
|
||||
|
||||
out:
|
||||
free(cfg_causes);
|
||||
cfg_causes = NULL;
|
||||
cfg_ncauses = 0;
|
||||
|
|
43
client.c
43
client.c
|
@ -245,9 +245,6 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
|
|||
u_int ncaps = 0;
|
||||
struct args_value *values;
|
||||
|
||||
/* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
|
||||
/* Set up the initial command. */
|
||||
if (shell_command != NULL) {
|
||||
msg = MSG_SHELL;
|
||||
|
@ -284,6 +281,12 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags,
|
|||
log_debug("flags are %#llx", (unsigned long long)client_flags);
|
||||
|
||||
/* Initialize the client socket and start the server. */
|
||||
#ifdef HAVE_SYSTEMD
|
||||
if (systemd_activated()) {
|
||||
/* socket-based activation, do not even try to be a client. */
|
||||
fd = server_start(client_proc, flags, base, 0, NULL);
|
||||
} else
|
||||
#endif
|
||||
fd = client_connect(base, socket_path, client_flags);
|
||||
if (fd == -1) {
|
||||
if (errno == ECONNREFUSED) {
|
||||
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -115,6 +115,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||
|
||||
layout_init(w, wp);
|
||||
wp->flags |= PANE_CHANGED;
|
||||
colour_palette_from_option(&wp->palette, wp->options);
|
||||
|
||||
if (idx == -1)
|
||||
idx = -1 - options_get_number(dst_s->options, "base-index");
|
||||
|
|
|
@ -39,8 +39,8 @@ const struct cmd_entry cmd_capture_pane_entry = {
|
|||
.name = "capture-pane",
|
||||
.alias = "capturep",
|
||||
|
||||
.args = { "ab:CeE:JNpPqS:t:", 0, 0, NULL },
|
||||
.usage = "[-aCeJNpPq] " CMD_BUFFER_USAGE " [-E end-line] "
|
||||
.args = { "ab:CeE: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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -143,6 +143,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
|
|||
cdata->prompt_type = status_prompt_type(type);
|
||||
if (cdata->prompt_type == PROMPT_TYPE_INVALID) {
|
||||
cmdq_error(item, "unknown type: %s", type);
|
||||
cmd_command_prompt_free(cdata);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
} else
|
||||
|
@ -179,10 +180,10 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
|
|||
|
||||
if (s == NULL)
|
||||
goto out;
|
||||
|
||||
if (done) {
|
||||
if (cdata->flags & PROMPT_INCREMENTAL)
|
||||
goto out;
|
||||
|
||||
cmd_append_argv(&cdata->argc, &cdata->argv, s);
|
||||
if (++cdata->current != cdata->count) {
|
||||
prompt = &cdata->prompts[cdata->current];
|
||||
|
@ -193,8 +194,11 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
|
|||
|
||||
argc = cdata->argc;
|
||||
argv = cmd_copy_argv(cdata->argc, cdata->argv);
|
||||
cmd_append_argv(&argc, &argv, s);
|
||||
if (!done)
|
||||
cmd_append_argv(&argc, &argv, s);
|
||||
|
||||
if (done) {
|
||||
cmd_free_argv(cdata->argc, cdata->argv);
|
||||
cdata->argc = argc;
|
||||
cdata->argv = cmd_copy_argv(argc, argv);
|
||||
}
|
||||
|
|
|
@ -41,8 +41,9 @@ const struct cmd_entry cmd_confirm_before_entry = {
|
|||
.name = "confirm-before",
|
||||
.alias = "confirm",
|
||||
|
||||
.args = { "bp:t:", 1, 1, cmd_confirm_before_args_parse },
|
||||
.usage = "[-b] [-p prompt] " CMD_TARGET_CLIENT_USAGE " command",
|
||||
.args = { "bc:p:t:y", 1, 1, cmd_confirm_before_args_parse },
|
||||
.usage = "[-by] [-c confirm_key] [-p prompt] " CMD_TARGET_CLIENT_USAGE
|
||||
" command",
|
||||
|
||||
.flags = CMD_CLIENT_TFLAG,
|
||||
.exec = cmd_confirm_before_exec
|
||||
|
@ -51,6 +52,8 @@ const struct cmd_entry cmd_confirm_before_entry = {
|
|||
struct cmd_confirm_before_data {
|
||||
struct cmdq_item *item;
|
||||
struct cmd_list *cmdlist;
|
||||
u_char confirm_key;
|
||||
int default_yes;
|
||||
};
|
||||
|
||||
static enum args_parse_type
|
||||
|
@ -68,22 +71,40 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
|
|||
struct client *tc = cmdq_get_target_client(item);
|
||||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
char *new_prompt;
|
||||
const char *prompt, *cmd;
|
||||
const char *confirm_key, *prompt, *cmd;
|
||||
int wait = !args_has(args, 'b');
|
||||
|
||||
cdata = xcalloc(1, sizeof *cdata);
|
||||
cdata->cmdlist = args_make_commands_now(self, item, 0, 1);
|
||||
if (cdata->cmdlist == NULL)
|
||||
if (cdata->cmdlist == NULL) {
|
||||
free(cdata);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
if (wait)
|
||||
cdata->item = item;
|
||||
|
||||
cdata->default_yes = args_has(args, 'y');
|
||||
if ((confirm_key = args_get(args, 'c')) != NULL) {
|
||||
if (confirm_key[1] == '\0' &&
|
||||
confirm_key[0] > 31 &&
|
||||
confirm_key[0] < 127)
|
||||
cdata->confirm_key = confirm_key[0];
|
||||
else {
|
||||
cmdq_error(item, "invalid confirm key");
|
||||
free(cdata);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
}
|
||||
else
|
||||
cdata->confirm_key = 'y';
|
||||
|
||||
if ((prompt = args_get(args, 'p')) != NULL)
|
||||
xasprintf(&new_prompt, "%s ", prompt);
|
||||
else {
|
||||
cmd = cmd_get_entry(cmd_list_first(cdata->cmdlist))->name;
|
||||
xasprintf(&new_prompt, "Confirm '%s'? (y/n) ", cmd);
|
||||
xasprintf(&new_prompt, "Confirm '%s'? (%c/n) ", cmd,
|
||||
cdata->confirm_key);
|
||||
}
|
||||
|
||||
status_prompt_set(tc, target, new_prompt, NULL,
|
||||
|
@ -107,9 +128,9 @@ cmd_confirm_before_callback(struct client *c, void *data, const char *s,
|
|||
if (c->flags & CLIENT_DEAD)
|
||||
goto out;
|
||||
|
||||
if (s == NULL || *s == '\0')
|
||||
if (s == NULL)
|
||||
goto out;
|
||||
if (tolower((u_char)s[0]) != 'y' || s[1] != '\0')
|
||||
if (s[0] != cdata->confirm_key && (s[0] != '\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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -144,7 +144,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
|
|||
llen = 0;
|
||||
|
||||
if (sx < len * 6 || sy < 5) {
|
||||
tty_attributes(tty, &fgc, &grid_default_cell, NULL);
|
||||
tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
|
||||
if (sx >= len + llen + 1) {
|
||||
len += llen + 1;
|
||||
tty_cursor(tty, xoff + px - len / 2, yoff + py);
|
||||
|
@ -161,7 +161,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
|
|||
px -= len * 3;
|
||||
py -= 2;
|
||||
|
||||
tty_attributes(tty, &bgc, &grid_default_cell, NULL);
|
||||
tty_attributes(tty, &bgc, &grid_default_cell, NULL, NULL);
|
||||
for (ptr = buf; *ptr != '\0'; ptr++) {
|
||||
if (*ptr < '0' || *ptr > '9')
|
||||
continue;
|
||||
|
@ -179,7 +179,7 @@ cmd_display_panes_draw_pane(struct screen_redraw_ctx *ctx,
|
|||
|
||||
if (sy <= 6)
|
||||
goto out;
|
||||
tty_attributes(tty, &fgc, &grid_default_cell, NULL);
|
||||
tty_attributes(tty, &fgc, &grid_default_cell, NULL, NULL);
|
||||
if (rlen != 0 && sx >= rlen) {
|
||||
tty_cursor(tty, xoff + sx - rlen, yoff);
|
||||
tty_putn(tty, rbuf, rlen, rlen);
|
||||
|
@ -246,7 +246,7 @@ cmd_display_panes_key(struct client *c, void *data, struct key_event *event)
|
|||
wp = window_pane_at_index(w, index);
|
||||
if (wp == NULL)
|
||||
return (1);
|
||||
window_unzoom(w);
|
||||
window_unzoom(w, 1);
|
||||
|
||||
xasprintf(&expanded, "%%%u", wp->id);
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||
struct cmd_find_state *target = cmdq_get_target(item);
|
||||
struct window_pane *wp = target->wp;
|
||||
const char *s = args_string(args, 0), *suffix = "";
|
||||
const char *star = "*";
|
||||
struct args_value *filter;
|
||||
int C, N, T;
|
||||
|
||||
|
@ -55,6 +56,8 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||
N = args_has(args, 'N');
|
||||
T = args_has(args, 'T');
|
||||
|
||||
if (args_has(args, 'r'))
|
||||
star = "";
|
||||
if (args_has(args, 'r') && args_has(args, 'i'))
|
||||
suffix = "/ri";
|
||||
else if (args_has(args, 'r'))
|
||||
|
@ -71,40 +74,40 @@ cmd_find_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||
if (C && N && T) {
|
||||
xasprintf(&filter->string,
|
||||
"#{||:"
|
||||
"#{C%s:%s},#{||:#{m%s:*%s*,#{window_name}},"
|
||||
"#{m%s:*%s*,#{pane_title}}}}",
|
||||
suffix, s, suffix, s, suffix, s);
|
||||
"#{C%s:%s},#{||:#{m%s:%s%s%s,#{window_name}},"
|
||||
"#{m%s:%s%s%s,#{pane_title}}}}",
|
||||
suffix, s, suffix, star, s, star, suffix, star, s, star);
|
||||
} else if (C && N) {
|
||||
xasprintf(&filter->string,
|
||||
"#{||:#{C%s:%s},#{m%s:*%s*,#{window_name}}}",
|
||||
suffix, s, suffix, s);
|
||||
"#{||:#{C%s:%s},#{m%s:%s%s%s,#{window_name}}}",
|
||||
suffix, s, suffix, star, s, star);
|
||||
} else if (C && T) {
|
||||
xasprintf(&filter->string,
|
||||
"#{||:#{C%s:%s},#{m%s:*%s*,#{pane_title}}}",
|
||||
suffix, s, suffix, s);
|
||||
"#{||:#{C%s:%s},#{m%s:%s%s%s,#{pane_title}}}",
|
||||
suffix, s, suffix, star, s, star);
|
||||
} else if (N && T) {
|
||||
xasprintf(&filter->string,
|
||||
"#{||:#{m%s:*%s*,#{window_name}},"
|
||||
"#{m%s:*%s*,#{pane_title}}}",
|
||||
suffix, s, suffix, s);
|
||||
"#{||:#{m%s:%s%s%s,#{window_name}},"
|
||||
"#{m%s:%s%s%s,#{pane_title}}}",
|
||||
suffix, star, s, star, suffix, star, s, star);
|
||||
} else if (C) {
|
||||
xasprintf(&filter->string,
|
||||
"#{C%s:%s}",
|
||||
suffix, s);
|
||||
} else if (N) {
|
||||
xasprintf(&filter->string,
|
||||
"#{m%s:*%s*,#{window_name}}",
|
||||
suffix, s);
|
||||
"#{m%s:%s%s%s,#{window_name}}",
|
||||
suffix, star, s, star);
|
||||
} else {
|
||||
xasprintf(&filter->string,
|
||||
"#{m%s:*%s*,#{pane_title}}",
|
||||
suffix, s);
|
||||
"#{m%s:%s%s%s,#{pane_title}}",
|
||||
suffix, star, s, star);
|
||||
}
|
||||
|
||||
new_args = args_create();
|
||||
if (args_has(args, 'Z'))
|
||||
args_set(new_args, 'Z', NULL);
|
||||
args_set(new_args, 'f', filter);
|
||||
args_set(new_args, 'Z', NULL, 0);
|
||||
args_set(new_args, 'f', filter, 0);
|
||||
|
||||
window_pane_set_mode(wp, NULL, &window_tree_mode, target, new_args);
|
||||
args_free(new_args);
|
||||
|
|
12
cmd-find.c
12
cmd-find.c
|
@ -582,27 +582,27 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
|
|||
|
||||
/* Try special characters. */
|
||||
if (strcmp(pane, "!") == 0) {
|
||||
fs->wp = fs->w->last;
|
||||
fs->wp = TAILQ_FIRST(&fs->w->last_panes);
|
||||
if (fs->wp == NULL)
|
||||
return (-1);
|
||||
return (0);
|
||||
} else if (strcmp(pane, "{up-of}") == 0) {
|
||||
fs->wp = window_pane_find_up(fs->current->wp);
|
||||
fs->wp = window_pane_find_up(fs->w->active);
|
||||
if (fs->wp == NULL)
|
||||
return (-1);
|
||||
return (0);
|
||||
} else if (strcmp(pane, "{down-of}") == 0) {
|
||||
fs->wp = window_pane_find_down(fs->current->wp);
|
||||
fs->wp = window_pane_find_down(fs->w->active);
|
||||
if (fs->wp == NULL)
|
||||
return (-1);
|
||||
return (0);
|
||||
} else if (strcmp(pane, "{left-of}") == 0) {
|
||||
fs->wp = window_pane_find_left(fs->current->wp);
|
||||
fs->wp = window_pane_find_left(fs->w->active);
|
||||
if (fs->wp == NULL)
|
||||
return (-1);
|
||||
return (0);
|
||||
} else if (strcmp(pane, "{right-of}") == 0) {
|
||||
fs->wp = window_pane_find_right(fs->current->wp);
|
||||
fs->wp = window_pane_find_right(fs->w->active);
|
||||
if (fs->wp == NULL)
|
||||
return (-1);
|
||||
return (0);
|
||||
|
@ -614,7 +614,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane)
|
|||
n = strtonum(pane + 1, 1, INT_MAX, NULL);
|
||||
else
|
||||
n = 1;
|
||||
wp = fs->current->wp;
|
||||
wp = fs->w->active;
|
||||
if (pane[0] == '+')
|
||||
fs->wp = window_pane_next_by_number(fs->w, wp, n);
|
||||
else
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
20
cmd-parse.y
20
cmd-parse.y
|
@ -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
|
||||
|
|
|
@ -54,6 +54,11 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
|||
size_t seplen, bufsize;
|
||||
int bracket = args_has(args, 'p');
|
||||
|
||||
if (window_pane_exited(wp)) {
|
||||
cmdq_error(item, "target pane has exited");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
||||
bufname = NULL;
|
||||
if (args_has(args, 'b'))
|
||||
bufname = args_get(args, 'b');
|
||||
|
|
|
@ -68,7 +68,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||
sigset_t set, oldset;
|
||||
|
||||
/* Do nothing if pane is dead. */
|
||||
if (wp->fd == -1 || (wp->flags & PANE_EXITED)) {
|
||||
if (window_pane_exited(wp)) {
|
||||
cmdq_error(item, "target pane has exited");
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
|
|
72
cmd-queue.c
72
cmd-queue.c
|
@ -236,8 +236,10 @@ cmdq_link_state(struct cmdq_state *state)
|
|||
|
||||
/* Make a copy of a state. */
|
||||
struct cmdq_state *
|
||||
cmdq_copy_state(struct cmdq_state *state)
|
||||
cmdq_copy_state(struct cmdq_state *state, struct cmd_find_state *current)
|
||||
{
|
||||
if (current != NULL)
|
||||
return (cmdq_new_state(current, &state->event, state->flags));
|
||||
return (cmdq_new_state(&state->current, &state->event, state->flags));
|
||||
}
|
||||
|
||||
|
@ -662,9 +664,18 @@ cmdq_fire_command(struct cmdq_item *item)
|
|||
|
||||
out:
|
||||
item->client = saved;
|
||||
if (retval == CMD_RETURN_ERROR)
|
||||
if (retval == CMD_RETURN_ERROR) {
|
||||
fsp = NULL;
|
||||
if (cmd_find_valid_state(&item->target))
|
||||
fsp = &item->target;
|
||||
else if (cmd_find_valid_state(&item->state->current))
|
||||
fsp = &item->state->current;
|
||||
else if (cmd_find_from_client(&fs, item->client, 0) == 0)
|
||||
fsp = &fs;
|
||||
cmdq_insert_hook(fsp != NULL ? fsp->s : NULL, item, fsp,
|
||||
"command-error");
|
||||
cmdq_guard(item, "error", flags);
|
||||
else
|
||||
} else
|
||||
cmdq_guard(item, "end", flags);
|
||||
return (retval);
|
||||
}
|
||||
|
@ -803,10 +814,10 @@ cmdq_running(struct client *c)
|
|||
struct cmdq_list *queue = cmdq_get(c);
|
||||
|
||||
if (queue->item == NULL)
|
||||
return (NULL);
|
||||
if (queue->item->flags & CMDQ_WAITING)
|
||||
return (NULL);
|
||||
return (queue->item);
|
||||
return (NULL);
|
||||
if (queue->item->flags & CMDQ_WAITING)
|
||||
return (NULL);
|
||||
return (queue->item);
|
||||
}
|
||||
|
||||
/* Print a guard line. */
|
||||
|
@ -821,45 +832,30 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
|
|||
control_write(c, "%%%s %ld %u %d", guard, t, number, flags);
|
||||
}
|
||||
|
||||
/* Show message from command. */
|
||||
void
|
||||
cmdq_print_data(struct cmdq_item *item, 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. */
|
||||
|
|
|
@ -87,7 +87,7 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
|
|||
|
||||
if (args_has(args, 'Z')) {
|
||||
if (w->flags & WINDOW_ZOOMED)
|
||||
window_unzoom(w);
|
||||
window_unzoom(w, 1);
|
||||
else
|
||||
window_zoom(wp);
|
||||
server_redraw_window(w);
|
||||
|
|
|
@ -53,8 +53,7 @@ cmd_resize_window_exec(struct cmd *self, struct cmdq_item *item)
|
|||
struct session *s = target->s;
|
||||
const char *errstr;
|
||||
char *cause;
|
||||
u_int adjust, sx, sy;
|
||||
int xpixel = -1, ypixel = -1;
|
||||
u_int adjust, sx, sy, xpixel = 0, ypixel = 0;
|
||||
|
||||
if (args_count(args) == 0)
|
||||
adjust = 1;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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("-");
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -69,8 +69,13 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
|||
pb = paste_get_name(bufname);
|
||||
|
||||
if (cmd_get_entry(self) == &cmd_delete_buffer_entry) {
|
||||
if (pb == NULL)
|
||||
if (pb == NULL) {
|
||||
if (bufname != NULL) {
|
||||
cmdq_error(item, "unknown buffer: %s", bufname);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
pb = paste_get_top(&bufname);
|
||||
}
|
||||
if (pb == NULL) {
|
||||
cmdq_error(item, "no buffer");
|
||||
return (CMD_RETURN_ERROR);
|
||||
|
@ -80,8 +85,13 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
|||
}
|
||||
|
||||
if (args_has(args, 'n')) {
|
||||
if (pb == NULL)
|
||||
if (pb == NULL) {
|
||||
if (bufname != NULL) {
|
||||
cmdq_error(item, "unknown buffer: %s", bufname);
|
||||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
pb = paste_get_top(&bufname);
|
||||
}
|
||||
if (pb == NULL) {
|
||||
cmdq_error(item, "no buffer");
|
||||
return (CMD_RETURN_ERROR);
|
||||
|
@ -121,7 +131,7 @@ cmd_set_buffer_exec(struct cmd *self, struct cmdq_item *item)
|
|||
return (CMD_RETURN_ERROR);
|
||||
}
|
||||
if (args_has(args, 'w') && tc != NULL)
|
||||
tty_set_selection(&tc->tty, bufdata, bufsize);
|
||||
tty_set_selection(&tc->tty, "", bufdata, bufsize);
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
}
|
||||
|
|
|
@ -35,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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
14
cmd.c
|
@ -47,7 +47,6 @@ extern const struct cmd_entry cmd_display_menu_entry;
|
|||
extern const struct cmd_entry cmd_display_message_entry;
|
||||
extern const struct cmd_entry cmd_display_popup_entry;
|
||||
extern const struct cmd_entry cmd_display_panes_entry;
|
||||
extern const struct cmd_entry cmd_down_pane_entry;
|
||||
extern const struct cmd_entry cmd_find_window_entry;
|
||||
extern const struct cmd_entry cmd_has_session_entry;
|
||||
extern const struct cmd_entry cmd_if_shell_entry;
|
||||
|
@ -117,7 +116,6 @@ extern const struct cmd_entry cmd_swap_window_entry;
|
|||
extern const struct cmd_entry cmd_switch_client_entry;
|
||||
extern const struct cmd_entry cmd_unbind_key_entry;
|
||||
extern const struct cmd_entry cmd_unlink_window_entry;
|
||||
extern const struct cmd_entry cmd_up_pane_entry;
|
||||
extern const struct cmd_entry cmd_wait_for_entry;
|
||||
|
||||
const struct cmd_entry *cmd_table[] = {
|
||||
|
@ -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;
|
||||
|
|
42
colour.c
42
colour.c
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
19
compat.h
19
compat.h
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
286
compat/imsg.c
286
compat/imsg.c
|
@ -1,6 +1,7 @@
|
|||
/* $OpenBSD: imsg.c,v 1.16 2017/12/14 09:27:44 kettenis Exp $ */
|
||||
/* $OpenBSD: imsg.c,v 1.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);
|
||||
}
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
#include <sys/types.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
|
172
compat/systemd.c
172
compat/systemd.c
|
@ -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);
|
||||
}
|
||||
|
|
69
configure.ac
69
configure.ac
|
@ -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)
|
||||
|
|
|
@ -234,3 +234,29 @@ control_notify_session_window_changed(struct session *s)
|
|||
s->curw->window->id);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
control_notify_paste_buffer_changed(const char *name)
|
||||
{
|
||||
struct client *c;
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
|
||||
control_write(c, "%%paste-buffer-changed %s", name);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
control_notify_paste_buffer_deleted(const char *name)
|
||||
{
|
||||
struct client *c;
|
||||
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (!CONTROL_SHOULD_NOTIFY_CLIENT(c))
|
||||
continue;
|
||||
|
||||
control_write(c, "%%paste-buffer-deleted %s", name);
|
||||
}
|
||||
}
|
||||
|
|
12
control.c
12
control.c
|
@ -775,13 +775,16 @@ control_start(struct client *c)
|
|||
|
||||
cs->read_event = bufferevent_new(c->fd, control_read_callback,
|
||||
control_write_callback, control_error_callback, c);
|
||||
bufferevent_enable(cs->read_event, EV_READ);
|
||||
if (cs->read_event == NULL)
|
||||
fatalx("out of memory");
|
||||
|
||||
if (c->flags & CLIENT_CONTROLCONTROL)
|
||||
cs->write_event = cs->read_event;
|
||||
else {
|
||||
cs->write_event = bufferevent_new(c->out_fd, NULL,
|
||||
control_write_callback, control_error_callback, c);
|
||||
if (cs->write_event == NULL)
|
||||
fatalx("out of memory");
|
||||
}
|
||||
bufferevent_setwatermark(cs->write_event, EV_WRITE, CONTROL_BUFFER_LOW,
|
||||
0);
|
||||
|
@ -792,6 +795,13 @@ control_start(struct client *c)
|
|||
}
|
||||
}
|
||||
|
||||
/* Control client ready. */
|
||||
void
|
||||
control_ready(struct client *c)
|
||||
{
|
||||
bufferevent_enable(c->control_state->read_event, EV_READ);
|
||||
}
|
||||
|
||||
/* Discard all output for a client. */
|
||||
void
|
||||
control_discard(struct client *c)
|
||||
|
|
15
environ.c
15
environ.c
|
@ -182,9 +182,11 @@ void
|
|||
environ_update(struct options *oo, struct environ *src, struct environ *dst)
|
||||
{
|
||||
struct environ_entry *envent;
|
||||
struct environ_entry *envent1;
|
||||
struct options_entry *o;
|
||||
struct options_array_item *a;
|
||||
union options_value *ov;
|
||||
int found;
|
||||
|
||||
o = options_get(oo, "update-environment");
|
||||
if (o == NULL)
|
||||
|
@ -192,14 +194,15 @@ environ_update(struct options *oo, struct environ *src, struct environ *dst)
|
|||
a = options_array_first(o);
|
||||
while (a != NULL) {
|
||||
ov = options_array_item_value(a);
|
||||
RB_FOREACH(envent, environ, src) {
|
||||
if (fnmatch(ov->string, envent->name, 0) == 0)
|
||||
break;
|
||||
found = 0;
|
||||
RB_FOREACH_SAFE(envent, environ, src, envent1) {
|
||||
if (fnmatch(ov->string, envent->name, 0) == 0) {
|
||||
environ_set(dst, envent->name, 0, "%s", envent->value);
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
if (envent == NULL)
|
||||
if (!found)
|
||||
environ_clear(dst, ov->string);
|
||||
else
|
||||
environ_set(dst, envent->name, 0, "%s", envent->value);
|
||||
a = options_array_next(a);
|
||||
}
|
||||
}
|
||||
|
|
54
file.c
54
file.c
|
@ -149,7 +149,8 @@ file_fire_done_cb(__unused int fd, __unused short events, void *arg)
|
|||
struct client_file *cf = arg;
|
||||
struct client *c = cf->c;
|
||||
|
||||
if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD)))
|
||||
if (cf->cb != NULL &&
|
||||
(cf->closed || c == NULL || (~c->flags & CLIENT_DEAD)))
|
||||
cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
|
||||
file_free(cf);
|
||||
}
|
||||
|
@ -173,9 +174,9 @@ file_fire_read(struct client_file *cf)
|
|||
int
|
||||
file_can_print(struct client *c)
|
||||
{
|
||||
if (c == NULL)
|
||||
return (0);
|
||||
if (c->session != NULL && (~c->flags & CLIENT_CONTROL))
|
||||
if (c == NULL ||
|
||||
(c->flags & CLIENT_ATTACHED) ||
|
||||
(c->flags & CLIENT_CONTROL))
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
|
@ -352,7 +353,7 @@ done:
|
|||
}
|
||||
|
||||
/* Read a file. */
|
||||
void
|
||||
struct client_file *
|
||||
file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
|
||||
{
|
||||
struct client_file *cf;
|
||||
|
@ -420,10 +421,27 @@ skip:
|
|||
goto done;
|
||||
}
|
||||
free(msg);
|
||||
return;
|
||||
return cf;
|
||||
|
||||
done:
|
||||
file_fire_done(cf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Cancel a file read. */
|
||||
void
|
||||
file_cancel(struct client_file *cf)
|
||||
{
|
||||
struct msg_read_cancel msg;
|
||||
|
||||
log_debug("read cancel file %d", cf->stream);
|
||||
|
||||
if (cf->closed)
|
||||
return;
|
||||
cf->closed = 1;
|
||||
|
||||
msg.stream = cf->stream;
|
||||
proc_send(cf->peer, MSG_READ_CANCEL, -1, &msg, sizeof msg);
|
||||
}
|
||||
|
||||
/* Push event, fired if there is more writing to be done. */
|
||||
|
@ -585,6 +603,8 @@ file_write_open(struct client_files *files, struct tmuxpeer *peer,
|
|||
|
||||
cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
|
||||
file_write_error_callback, cf);
|
||||
if (cf->event == NULL)
|
||||
fatalx("out of memory");
|
||||
bufferevent_enable(cf->event, EV_WRITE);
|
||||
goto reply;
|
||||
|
||||
|
@ -744,6 +764,8 @@ file_read_open(struct client_files *files, struct tmuxpeer *peer,
|
|||
|
||||
cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
|
||||
file_read_error_callback, cf);
|
||||
if (cf->event == NULL)
|
||||
fatalx("out of memory");
|
||||
bufferevent_enable(cf->event, EV_READ);
|
||||
return;
|
||||
|
||||
|
@ -753,6 +775,24 @@ reply:
|
|||
proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
|
||||
}
|
||||
|
||||
/* Handle a read cancel message (client). */
|
||||
void
|
||||
file_read_cancel(struct client_files *files, struct imsg *imsg)
|
||||
{
|
||||
struct msg_read_cancel *msg = imsg->data;
|
||||
size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
struct client_file find, *cf;
|
||||
|
||||
if (msglen != sizeof *msg)
|
||||
fatalx("bad MSG_READ_CANCEL size");
|
||||
find.stream = msg->stream;
|
||||
if ((cf = RB_FIND(client_files, files, &find)) == NULL)
|
||||
fatalx("unknown stream number");
|
||||
log_debug("cancel file %d", cf->stream);
|
||||
|
||||
file_read_error_callback(NULL, 0, cf);
|
||||
}
|
||||
|
||||
/* Handle a write ready message (server). */
|
||||
void
|
||||
file_write_ready(struct client_files *files, struct imsg *imsg)
|
||||
|
@ -790,7 +830,7 @@ file_read_data(struct client_files *files, struct imsg *imsg)
|
|||
return;
|
||||
|
||||
log_debug("file %d read %zu bytes", cf->stream, bsize);
|
||||
if (cf->error == 0) {
|
||||
if (cf->error == 0 && !cf->closed) {
|
||||
if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
|
||||
cf->error = ENOMEM;
|
||||
file_fire_done(cf);
|
||||
|
|
|
@ -33,6 +33,7 @@ struct format_range {
|
|||
|
||||
enum style_range_type type;
|
||||
u_int argument;
|
||||
char string[16];
|
||||
|
||||
TAILQ_ENTRY(format_range) entry;
|
||||
};
|
||||
|
@ -44,9 +45,18 @@ format_is_type(struct format_range *fr, struct style *sy)
|
|||
{
|
||||
if (fr->type != sy->range_type)
|
||||
return (0);
|
||||
if (fr->type == STYLE_RANGE_WINDOW &&
|
||||
fr->argument != sy->range_argument)
|
||||
return (0);
|
||||
switch (fr->type) {
|
||||
case STYLE_RANGE_NONE:
|
||||
case STYLE_RANGE_LEFT:
|
||||
case STYLE_RANGE_RIGHT:
|
||||
return (1);
|
||||
case STYLE_RANGE_PANE:
|
||||
case STYLE_RANGE_WINDOW:
|
||||
case STYLE_RANGE_SESSION:
|
||||
return (fr->argument == sy->range_argument);
|
||||
case STYLE_RANGE_USER:
|
||||
return (strcmp(fr->string, sy->range_string) == 0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
|
@ -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
261
format.c
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
158
grid.c
|
@ -37,7 +37,7 @@
|
|||
|
||||
/* Default grid cell data. */
|
||||
const struct grid_cell grid_default_cell = {
|
||||
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0
|
||||
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 8, 0
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -45,15 +45,15 @@ const struct grid_cell grid_default_cell = {
|
|||
* appears in the grid - because of this, they are always extended cells.
|
||||
*/
|
||||
static const struct grid_cell grid_padding_cell = {
|
||||
{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 0
|
||||
{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0
|
||||
};
|
||||
|
||||
/* Cleared grid cell data. */
|
||||
static const struct grid_cell grid_cleared_cell = {
|
||||
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 0
|
||||
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0
|
||||
};
|
||||
static const struct grid_cell_entry grid_cleared_entry = {
|
||||
GRID_FLAG_CLEARED, { .data = { 0, 8, 8, ' ' } }
|
||||
{ .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
|
||||
};
|
||||
|
||||
/* Store cell in entry. */
|
||||
|
@ -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--;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
26
input-keys.c
26
input-keys.c
|
@ -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
333
input.c
|
@ -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
31
job.c
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
177
menu.c
|
@ -27,6 +27,11 @@ struct menu_data {
|
|||
struct cmdq_item *item;
|
||||
int flags;
|
||||
|
||||
struct grid_cell style;
|
||||
struct grid_cell border_style;
|
||||
struct grid_cell selected_style;
|
||||
enum box_lines border_lines;
|
||||
|
||||
struct cmd_find_state fs;
|
||||
struct screen s;
|
||||
|
||||
|
@ -64,6 +69,8 @@ menu_add_item(struct menu *menu, const struct menu_item *item,
|
|||
line = (item == NULL || item->name == NULL || *item->name == '\0');
|
||||
if (line && menu->count == 0)
|
||||
return;
|
||||
if (line && menu->items[menu->count - 1].name == NULL)
|
||||
return;
|
||||
|
||||
menu->items = xreallocarray(menu->items, menu->count + 1,
|
||||
sizeof *menu->items);
|
||||
|
@ -160,11 +167,16 @@ menu_free(struct menu *menu)
|
|||
}
|
||||
|
||||
struct screen *
|
||||
menu_mode_cb(__unused struct client *c, void *data, __unused u_int *cx,
|
||||
__unused u_int *cy)
|
||||
menu_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
|
||||
{
|
||||
struct menu_data *md = data;
|
||||
|
||||
*cx = md->px + 2;
|
||||
if (md->choice == -1)
|
||||
*cy = md->py;
|
||||
else
|
||||
*cy = md->py + 1 + md->choice;
|
||||
|
||||
return (&md->s);
|
||||
}
|
||||
|
||||
|
@ -190,13 +202,17 @@ menu_draw_cb(struct client *c, void *data,
|
|||
struct menu *menu = md->menu;
|
||||
struct screen_write_ctx ctx;
|
||||
u_int i, px = md->px, py = md->py;
|
||||
struct grid_cell gc;
|
||||
|
||||
style_apply(&gc, c->session->curw->window->options, "mode-style", NULL);
|
||||
|
||||
screen_write_start(&ctx, s);
|
||||
screen_write_clearscreen(&ctx, 8);
|
||||
screen_write_menu(&ctx, menu, md->choice, &gc);
|
||||
|
||||
if (md->border_lines != BOX_LINES_NONE) {
|
||||
screen_write_box(&ctx, menu->width + 4, menu->count + 2,
|
||||
md->border_lines, &md->border_style, menu->title);
|
||||
}
|
||||
|
||||
screen_write_menu(&ctx, menu, md->choice, md->border_lines,
|
||||
&md->style, &md->border_style, &md->selected_style);
|
||||
screen_write_stop(&ctx);
|
||||
|
||||
for (i = 0; i < screen_size_y(&md->s); i++) {
|
||||
|
@ -318,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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
41
notify.c
41
notify.c
|
@ -32,6 +32,7 @@ struct notify_entry {
|
|||
struct session *session;
|
||||
struct window *window;
|
||||
int pane;
|
||||
const char *pbname;
|
||||
};
|
||||
|
||||
static struct cmdq_item *
|
||||
|
@ -149,6 +150,10 @@ notify_callback(struct cmdq_item *item, void *data)
|
|||
control_notify_session_closed(ne->session);
|
||||
if (strcmp(ne->name, "session-window-changed") == 0)
|
||||
control_notify_session_window_changed(ne->session);
|
||||
if (strcmp(ne->name, "paste-buffer-changed") == 0)
|
||||
control_notify_paste_buffer_changed(ne->pbname);
|
||||
if (strcmp(ne->name, "paste-buffer-deleted") == 0)
|
||||
control_notify_paste_buffer_deleted(ne->pbname);
|
||||
|
||||
notify_insert_hook(item, ne);
|
||||
|
||||
|
@ -164,6 +169,7 @@ notify_callback(struct cmdq_item *item, void *data)
|
|||
|
||||
format_free(ne->formats);
|
||||
free((void *)ne->name);
|
||||
free((void *)ne->pbname);
|
||||
free(ne);
|
||||
|
||||
return (CMD_RETURN_NORMAL);
|
||||
|
@ -171,7 +177,8 @@ notify_callback(struct cmdq_item *item, void *data)
|
|||
|
||||
static void
|
||||
notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
|
||||
struct session *s, struct window *w, struct window_pane *wp)
|
||||
struct session *s, struct window *w, struct window_pane *wp,
|
||||
const char *pbname)
|
||||
{
|
||||
struct notify_entry *ne;
|
||||
struct cmdq_item *item;
|
||||
|
@ -186,7 +193,8 @@ notify_add(const char *name, struct cmd_find_state *fs, struct client *c,
|
|||
ne->client = c;
|
||||
ne->session = s;
|
||||
ne->window = w;
|
||||
ne->pane = (wp != NULL ? wp->id : -1);
|
||||
ne->pane = (wp != NULL ? (int)wp->id : -1);
|
||||
ne->pbname = (pbname != NULL ? xstrdup(pbname) : NULL);
|
||||
|
||||
ne->formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
|
||||
format_add(ne->formats, "hook", "%s", name);
|
||||
|
@ -232,7 +240,7 @@ notify_hook(struct cmdq_item *item, const char *name)
|
|||
ne.client = cmdq_get_client(item);
|
||||
ne.session = target->s;
|
||||
ne.window = target->w;
|
||||
ne.pane = (target->wp != NULL ? target->wp->id : -1);
|
||||
ne.pane = (target->wp != NULL ? (int)target->wp->id : -1);
|
||||
|
||||
ne.formats = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
|
||||
format_add(ne.formats, "hook", "%s", name);
|
||||
|
@ -248,7 +256,7 @@ notify_client(const char *name, struct client *c)
|
|||
struct cmd_find_state fs;
|
||||
|
||||
cmd_find_from_client(&fs, c, 0);
|
||||
notify_add(name, &fs, c, NULL, NULL, NULL);
|
||||
notify_add(name, &fs, c, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -260,7 +268,7 @@ notify_session(const char *name, struct session *s)
|
|||
cmd_find_from_session(&fs, s, 0);
|
||||
else
|
||||
cmd_find_from_nothing(&fs, 0);
|
||||
notify_add(name, &fs, NULL, s, NULL, NULL);
|
||||
notify_add(name, &fs, NULL, s, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -269,7 +277,7 @@ notify_winlink(const char *name, struct winlink *wl)
|
|||
struct cmd_find_state fs;
|
||||
|
||||
cmd_find_from_winlink(&fs, wl, 0);
|
||||
notify_add(name, &fs, NULL, wl->session, wl->window, NULL);
|
||||
notify_add(name, &fs, NULL, wl->session, wl->window, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -278,7 +286,7 @@ notify_session_window(const char *name, struct session *s, struct window *w)
|
|||
struct cmd_find_state fs;
|
||||
|
||||
cmd_find_from_session_window(&fs, s, w, 0);
|
||||
notify_add(name, &fs, NULL, s, w, NULL);
|
||||
notify_add(name, &fs, NULL, s, w, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -287,7 +295,7 @@ notify_window(const char *name, struct window *w)
|
|||
struct cmd_find_state fs;
|
||||
|
||||
cmd_find_from_window(&fs, w, 0);
|
||||
notify_add(name, &fs, NULL, NULL, w, NULL);
|
||||
notify_add(name, &fs, NULL, NULL, w, NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -296,5 +304,20 @@ notify_pane(const char *name, struct window_pane *wp)
|
|||
struct cmd_find_state fs;
|
||||
|
||||
cmd_find_from_pane(&fs, wp, 0);
|
||||
notify_add(name, &fs, NULL, NULL, NULL, wp);
|
||||
notify_add(name, &fs, NULL, NULL, NULL, wp, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
notify_paste_buffer(const char *pbname, int deleted)
|
||||
{
|
||||
struct cmd_find_state fs;
|
||||
|
||||
cmd_find_clear_state(&fs, 0);
|
||||
if (deleted) {
|
||||
notify_add("paste-buffer-deleted", &fs, NULL, NULL, NULL, NULL,
|
||||
pbname);
|
||||
} else {
|
||||
notify_add("paste-buffer-changed", &fs, NULL, NULL, NULL, NULL,
|
||||
pbname);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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", ""),
|
||||
|
|
15
options.c
15
options.c
|
@ -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
26
paste.c
|
@ -111,6 +111,12 @@ paste_walk(struct paste_buffer *pb)
|
|||
return (RB_NEXT(paste_time_tree, &paste_by_time, pb));
|
||||
}
|
||||
|
||||
int
|
||||
paste_is_empty(void)
|
||||
{
|
||||
return RB_ROOT(&paste_by_time) == NULL;
|
||||
}
|
||||
|
||||
/* Get the most recent automatic buffer. */
|
||||
struct paste_buffer *
|
||||
paste_get_top(const char **name)
|
||||
|
@ -118,6 +124,8 @@ paste_get_top(const char **name)
|
|||
struct paste_buffer *pb;
|
||||
|
||||
pb = RB_MIN(paste_time_tree, &paste_by_time);
|
||||
while (pb != NULL && !pb->automatic)
|
||||
pb = RB_NEXT(paste_time_tree, &paste_by_time, pb);
|
||||
if (pb == NULL)
|
||||
return (NULL);
|
||||
if (name != NULL)
|
||||
|
@ -142,6 +150,8 @@ paste_get_name(const char *name)
|
|||
void
|
||||
paste_free(struct paste_buffer *pb)
|
||||
{
|
||||
notify_paste_buffer(pb->name, 1);
|
||||
|
||||
RB_REMOVE(paste_name_tree, &paste_by_name, pb);
|
||||
RB_REMOVE(paste_time_tree, &paste_by_time, pb);
|
||||
if (pb->automatic)
|
||||
|
@ -198,6 +208,8 @@ paste_add(const char *prefix, char *data, size_t size)
|
|||
pb->order = paste_next_order++;
|
||||
RB_INSERT(paste_name_tree, &paste_by_name, pb);
|
||||
RB_INSERT(paste_time_tree, &paste_by_time, pb);
|
||||
|
||||
notify_paste_buffer(pb->name, 0);
|
||||
}
|
||||
|
||||
/* Rename a paste buffer. */
|
||||
|
@ -228,11 +240,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
11
popup.c
|
@ -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
16
proc.c
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
|
||||
|
||||
0
|
||||
Λ̊1
|
||||
🏻2
|
||||
👍🏻3
|
||||
👍🏻 👍🏻4
|
||||
🤷♂️5
|
||||
♂️ 7
|
||||
🤷♂️8
|
||||
🤷♂️9
|
||||
🤷♂️10
|
||||
🇪11
|
||||
🇸🇪12
|
||||
🇸🇪13
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
4
regsub.c
4
regsub.c
|
@ -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;
|
||||
|
|
2
resize.c
2
resize.c
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
498
screen-write.c
498
screen-write.c
|
@ -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);
|
||||
}
|
||||
|
|
40
screen.c
40
screen.c
|
@ -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)
|
||||
|
|
167
server-client.c
167
server-client.c
|
@ -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);
|
||||
}
|
||||
|
|
79
server-fn.c
79
server-fn.c
|
@ -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);
|
||||
}
|
||||
|
|
5
server.c
5
server.c
|
@ -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) {
|
||||
|
|
24
session.c
24
session.c
|
@ -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
19
spawn.c
|
@ -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
114
status.c
|
@ -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
Loading…
Reference in New Issue